blob: ac7b546a37b9189f0874ec6d7bf74f91472d1367 [file] [log] [blame]
Erik Andersen13456d12000-03-16 08:09:57 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen5e1189e2000-04-15 16:34:54 +00003 * test implementation for busybox
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (c) by a whole pile of folks:
Erik Andersen13456d12000-03-16 08:09:57 +00006 *
Paul Fox6ab03782006-06-08 21:37:26 +00007 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersen@codepoet.org> to be used
14 * in busybox.
Bernhard Reutner-Fischer6c4dade2008-09-25 12:13:34 +000015 * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
Erik Andersen13456d12000-03-16 08:09:57 +000016 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020017 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Erik Andersen13456d12000-03-16 08:09:57 +000018 *
19 * Original copyright notice states:
Paul Fox6ab03782006-06-08 21:37:26 +000020 * "This program is in the Public Domain."
Erik Andersen13456d12000-03-16 08:09:57 +000021 */
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020022//config:config TEST
Denys Vlasenkob097a842018-12-28 03:20:17 +010023//config: bool "test (4.1 kb)"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020024//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020025//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020026//config: test is used to check file types and compare values,
27//config: returning an appropriate exit code. The bash shell
28//config: has test built in, ash can build it in optionally.
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020029//config:
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010030//config:config TEST1
31//config: bool "test as ["
32//config: default y
33//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020034//config: Provide test command in the "[ EXPR ]" form
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010035//config:
36//config:config TEST2
37//config: bool "test as [["
38//config: default y
39//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020040//config: Provide test command in the "[[ EXPR ]]" form
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010041//config:
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020042//config:config FEATURE_TEST_64
43//config: bool "Extend test to 64 bit"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020044//config: default y
Denys Vlasenko265062d2017-01-10 15:13:30 +010045//config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020046//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020047//config: Enable 64-bit support in test.
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020048
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010049//applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
Denys Vlasenko265062d2017-01-10 15:13:30 +010050//applet:IF_TEST1(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
51//applet:IF_TEST2(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010052
Denys Vlasenko265062d2017-01-10 15:13:30 +010053//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010054//kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
55//kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
Denys Vlasenko265062d2017-01-10 15:13:30 +010056
57//kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o
58//kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010059
Denys Vlasenkode5edad2015-04-21 16:00:41 +020060/* "test --help" is special-cased to ignore --help */
61//usage:#define test_trivial_usage NOUSAGE_STR
62//usage:#define test_full_usage ""
Pere Orga34425382011-03-31 14:43:25 +020063//usage:
64//usage:#define test_example_usage
65//usage: "$ test 1 -eq 2\n"
66//usage: "$ echo $?\n"
67//usage: "1\n"
68//usage: "$ test 1 -eq 1\n"
69//usage: "$ echo $?\n"
70//usage: "0\n"
71//usage: "$ [ -d /etc ]\n"
72//usage: "$ echo $?\n"
73//usage: "0\n"
74//usage: "$ [ -d /junk ]\n"
75//usage: "$ echo $?\n"
76//usage: "1\n"
77
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000078#include "libbb.h"
Denys Vlasenkod2241f52020-10-31 03:34:07 +010079#include <regex.h>
80#include <fnmatch.h>
Erik Andersen13456d12000-03-16 08:09:57 +000081
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000082/* This is a NOFORK applet. Be very careful! */
83
84/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010085 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000086 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000087
Erik Andersen13456d12000-03-16 08:09:57 +000088/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020089 oexpr ::= aexpr | aexpr "-o" oexpr ;
90 aexpr ::= nexpr | nexpr "-a" aexpr ;
91 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000092 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000093 | operand binary-operator operand
94 | operand
95 | "(" oexpr ")"
96 ;
97 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
98 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
99
Mike Frysinger75ac42b2005-04-14 02:49:22 +0000100 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +0000101 "-nt"|"-ot"|"-ef";
102 operand ::= <any legal UNIX file name>
103*/
104
Denys Vlasenko82a6fb32009-06-14 19:42:12 +0200105/* TODO: handle [[ expr ]] bashism bash-compatibly.
106 * [[ ]] is meant to be a "better [ ]", with less weird syntax
107 * and without the risk of variables and quoted strings misinterpreted
108 * as operators.
109 * This will require support from shells - we need to know quote status
110 * of each parameter (see below).
111 *
112 * Word splitting and pathname expansion should NOT be performed:
113 * # a="a b"; [[ $a = "a b" ]] && echo YES
114 * YES
115 * # [[ /bin/m* ]] && echo YES
116 * YES
117 *
118 * =~ should do regexp match
119 * = and == should do pattern match against right side:
120 * # [[ *a* == bab ]] && echo YES
121 * # [[ bab == *a* ]] && echo YES
122 * YES
123 * != does the negated == (i.e., also with pattern matching).
124 * Pattern matching is quotation-sensitive:
125 * # [[ bab == "b"a* ]] && echo YES
126 * YES
127 * # [[ bab == b"a*" ]] && echo YES
128 *
129 * Conditional operators such as -f must be unquoted literals to be recognized:
130 * # [[ -e /bin ]] && echo YES
131 * YES
132 * # [[ '-e' /bin ]] && echo YES
133 * bash: conditional binary operator expected...
134 * # A='-e'; [[ $A /bin ]] && echo YES
135 * bash: conditional binary operator expected...
136 *
137 * || and && should work as -o and -a work in [ ]
138 * -a and -o aren't recognized (&& and || are to be used instead)
139 * ( and ) do not need to be quoted unlike in [ ]:
140 * # [[ ( abc ) && '' ]] && echo YES
141 * # [[ ( abc ) || '' ]] && echo YES
142 * YES
143 * # [[ ( abc ) -o '' ]] && echo YES
144 * bash: syntax error in conditional expression...
145 *
146 * Apart from the above, [[ expr ]] should work as [ expr ]
147 */
148
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000149#define TEST_DEBUG 0
150
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100151#if ENABLE_TEST2 \
152 || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153 || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154# define BASH_TEST2 1
155#else
156# define BASH_TEST2 0
157#endif
158
Erik Andersen13456d12000-03-16 08:09:57 +0000159enum token {
160 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200161
162 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000163 FILWR,
164 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200165
Erik Andersen13456d12000-03-16 08:09:57 +0000166 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200167
168 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000169 FILDIR,
170 FILCDEV,
171 FILBDEV,
172 FILFIFO,
173 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200174
Erik Andersen13456d12000-03-16 08:09:57 +0000175 FILSYM,
176 FILGZ,
177 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200178
179 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000180 FILSGID,
181 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200182
183 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000184 FILOT,
185 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200186
Erik Andersen13456d12000-03-16 08:09:57 +0000187 FILUID,
188 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200189
190 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000191 STRNZ,
192 STREQ,
193 STRNE,
194 STRLT,
195 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200196
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100197#if BASH_TEST2
198 REGEX,
199#endif
200
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200201 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000202 INTNE,
203 INTGE,
204 INTGT,
205 INTLE,
206 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200207
Erik Andersen13456d12000-03-16 08:09:57 +0000208 UNOT,
209 BAND,
210 BOR,
211 LPAREN,
212 RPAREN,
213 OPERAND
214};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000215#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
216#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
217#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000218#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000219#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
220#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000221
222#if TEST_DEBUG
223int depth;
224#define nest_msg(...) do { \
225 depth++; \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__); \
228} while (0)
229#define unnest_msg(...) do { \
230 fprintf(stderr, "%*s", depth*2, ""); \
231 fprintf(stderr, __VA_ARGS__); \
232 depth--; \
233} while (0)
234#define dbg_msg(...) do { \
235 fprintf(stderr, "%*s", depth*2, ""); \
236 fprintf(stderr, __VA_ARGS__); \
237} while (0)
238#define unnest_msg_and_return(expr, ...) do { \
239 number_t __res = (expr); \
240 fprintf(stderr, "%*s", depth*2, ""); \
241 fprintf(stderr, __VA_ARGS__, res); \
242 depth--; \
243 return __res; \
244} while (0)
245static const char *const TOKSTR[] = {
246 "EOI",
247 "FILRD",
248 "FILWR",
249 "FILEX",
250 "FILEXIST",
251 "FILREG",
252 "FILDIR",
253 "FILCDEV",
254 "FILBDEV",
255 "FILFIFO",
256 "FILSOCK",
257 "FILSYM",
258 "FILGZ",
259 "FILTT",
260 "FILSUID",
261 "FILSGID",
262 "FILSTCK",
263 "FILNT",
264 "FILOT",
265 "FILEQ",
266 "FILUID",
267 "FILGID",
268 "STREZ",
269 "STRNZ",
270 "STREQ",
271 "STRNE",
272 "STRLT",
273 "STRGT",
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100274#if BASH_TEST2
275 "REGEX",
276#endif
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000277 "INTEQ",
278 "INTNE",
279 "INTGE",
280 "INTGT",
281 "INTLE",
282 "INTLT",
283 "UNOT",
284 "BAND",
285 "BOR",
286 "LPAREN",
287 "RPAREN",
288 "OPERAND"
289};
290#else
291#define nest_msg(...) ((void)0)
292#define unnest_msg(...) ((void)0)
293#define dbg_msg(...) ((void)0)
294#define unnest_msg_and_return(expr, ...) return expr
295#endif
296
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200297enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000298 UNOP,
299 BINOP,
300 BUNOP,
301 BBINOP,
302 PAREN
303};
304
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000305struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000306 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000307};
308
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200309static const struct operator_t ops_table[] = {
310 { /* "-r" */ FILRD , UNOP },
311 { /* "-w" */ FILWR , UNOP },
312 { /* "-x" */ FILEX , UNOP },
313 { /* "-e" */ FILEXIST, UNOP },
314 { /* "-f" */ FILREG , UNOP },
315 { /* "-d" */ FILDIR , UNOP },
316 { /* "-c" */ FILCDEV , UNOP },
317 { /* "-b" */ FILBDEV , UNOP },
318 { /* "-p" */ FILFIFO , UNOP },
319 { /* "-u" */ FILSUID , UNOP },
320 { /* "-g" */ FILSGID , UNOP },
321 { /* "-k" */ FILSTCK , UNOP },
322 { /* "-s" */ FILGZ , UNOP },
323 { /* "-t" */ FILTT , UNOP },
324 { /* "-z" */ STREZ , UNOP },
325 { /* "-n" */ STRNZ , UNOP },
326 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000327
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200328 { /* "-O" */ FILUID , UNOP },
329 { /* "-G" */ FILGID , UNOP },
330 { /* "-L" */ FILSYM , UNOP },
331 { /* "-S" */ FILSOCK , UNOP },
332 { /* "=" */ STREQ , BINOP },
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200333 /* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
334 * lists only "=" as comparison operator.
335 */
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200336 { /* "==" */ STREQ , BINOP },
337 { /* "!=" */ STRNE , BINOP },
338 { /* "<" */ STRLT , BINOP },
339 { /* ">" */ STRGT , BINOP },
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100340#if BASH_TEST2
341 { /* "=~" */ REGEX , BINOP },
342#endif
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200343 { /* "-eq"*/ INTEQ , BINOP },
344 { /* "-ne"*/ INTNE , BINOP },
345 { /* "-ge"*/ INTGE , BINOP },
346 { /* "-gt"*/ INTGT , BINOP },
347 { /* "-le"*/ INTLE , BINOP },
348 { /* "-lt"*/ INTLT , BINOP },
349 { /* "-nt"*/ FILNT , BINOP },
350 { /* "-ot"*/ FILOT , BINOP },
351 { /* "-ef"*/ FILEQ , BINOP },
352 { /* "!" */ UNOT , BUNOP },
353 { /* "-a" */ BAND , BBINOP },
354 { /* "-o" */ BOR , BBINOP },
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100355#if BASH_TEST2
356 { /* "&&" */ BAND , BBINOP },
357 { /* "||" */ BOR , BBINOP },
358#endif
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200359 { /* "(" */ LPAREN , PAREN },
360 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000361};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200362/* Please keep these two tables in sync */
363static const char ops_texts[] ALIGN1 =
364 "-r" "\0"
365 "-w" "\0"
366 "-x" "\0"
367 "-e" "\0"
368 "-f" "\0"
369 "-d" "\0"
370 "-c" "\0"
371 "-b" "\0"
372 "-p" "\0"
373 "-u" "\0"
374 "-g" "\0"
375 "-k" "\0"
376 "-s" "\0"
377 "-t" "\0"
378 "-z" "\0"
379 "-n" "\0"
380 "-h" "\0"
381
382 "-O" "\0"
383 "-G" "\0"
384 "-L" "\0"
385 "-S" "\0"
386 "=" "\0"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200387 /* "==" is bashism */
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200388 "==" "\0"
389 "!=" "\0"
390 "<" "\0"
391 ">" "\0"
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100392#if BASH_TEST2
393 "=~" "\0"
394#endif
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200395 "-eq" "\0"
396 "-ne" "\0"
397 "-ge" "\0"
398 "-gt" "\0"
399 "-le" "\0"
400 "-lt" "\0"
401 "-nt" "\0"
402 "-ot" "\0"
403 "-ef" "\0"
404 "!" "\0"
405 "-a" "\0"
406 "-o" "\0"
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100407#if BASH_TEST2
408 "&&" "\0"
409 "||" "\0"
410#endif
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200411 "(" "\0"
412 ")" "\0"
413;
Erik Andersen13456d12000-03-16 08:09:57 +0000414
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000415
416#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000417typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000418#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000419typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000420#endif
421
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000422
423/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000424struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000425 char **args;
426 /* set only by check_operator(), either to bogus struct
427 * or points to matching operator_t struct. Never NULL. */
428 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000429 gid_t *group_array;
430 int ngroups;
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100431#if BASH_TEST2
432 bool bash_test2;
433#endif
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000434 jmp_buf leaving;
435};
436
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000437/* See test_ptr_hack.c */
438extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000439
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000440#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000441#define args (S.args )
442#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000443#define group_array (S.group_array )
444#define ngroups (S.ngroups )
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100445#define bash_test2 (S.bash_test2 )
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000446#define leaving (S.leaving )
447
448#define INIT_S() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +0200449 (*(struct test_statics**)not_const_pp(&test_ptr_to_statics)) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000450 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000451} while (0)
452#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200453 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000454 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000455} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000456
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000457static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000458
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000459static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000460static void syntax(const char *op, const char *msg)
461{
462 if (op && *op) {
463 bb_error_msg("%s: %s", op, msg);
464 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000465 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000466 }
467 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000468}
469
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000470/* atoi with error detection */
471//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000472static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000473{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000474 char *p;
475#if ENABLE_FEATURE_TEST_64
476 long long r;
477#else
478 long r;
479#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000480
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000481 errno = 0;
482#if ENABLE_FEATURE_TEST_64
483 r = strtoll(s, &p, 10);
484#else
485 r = strtol(s, &p, 10);
486#endif
487
488 if (errno != 0)
489 syntax(s, "out of range");
490
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200491 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000492 syntax(s, "bad number");
493
494 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000495}
496
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000497/* UNUSED
498static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000499{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000500 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000501
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000502 return (stat(f1, &b1) == 0 &&
503 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000504}
505
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000506static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000507{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000508 struct stat b1, b2;
509
510 return (stat(f1, &b1) == 0 &&
511 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000512}
513
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000514static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000515{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000516 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000517
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000518 return (stat(f1, &b1) == 0 &&
519 stat(f2, &b2) == 0 &&
520 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000521}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000522*/
523
524
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200525static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000526{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000527 static const struct operator_t no_op = {
528 .op_num = -1,
529 .op_type = -1
530 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200531 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000532
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000533 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200534 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000535 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200536 n = index_in_strings(ops_texts, s);
537 if (n < 0)
538 return OPERAND;
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100539
540#if BASH_TEST2
541 if (ops_table[n].op_num == REGEX && !bash_test2) {
542 /* =~ is only for [[ ]] */
543 return OPERAND;
544 }
545 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
546 /* [ ] accepts -a and -o but not && and || */
547 /* [[ ]] accepts && and || but not -a and -o */
548 if (bash_test2 == (s[0] == '-'))
549 return OPERAND;
550 }
551#endif
552
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200553 last_operator = &ops_table[n];
554 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000555}
556
Erik Andersen13456d12000-03-16 08:09:57 +0000557
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000558static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000559{
560 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000561 const struct operator_t *op;
562 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000563
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000564 opnd1 = *args;
565 check_operator(*++args);
566 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000567
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000568 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000569 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200570 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000571
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000572 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000573 val1 = getn(opnd1);
574 val2 = getn(opnd2);
575 if (op->op_num == INTEQ)
576 return val1 == val2;
577 if (op->op_num == INTNE)
578 return val1 != val2;
579 if (op->op_num == INTGE)
580 return val1 >= val2;
581 if (op->op_num == INTGT)
582 return val1 > val2;
583 if (op->op_num == INTLE)
584 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200585 /*if (op->op_num == INTLT)*/
586 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000587 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100588#if BASH_TEST2
589 if (bash_test2) {
590 if (op->op_num == STREQ) {
591 val1 = fnmatch(opnd2, opnd1, 0);
592 return val1 == 0;
593 }
594 if (op->op_num == STRNE) {
595 val1 = fnmatch(opnd2, opnd1, 0);
596 return val1 != 0;
597 }
598 if (op->op_num == REGEX) {
599 regex_t re_buffer;
600 memset(&re_buffer, 0, sizeof(re_buffer));
601 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
602 /* Bad regex */
603 longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
604 }
605 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
606 regfree(&re_buffer);
607 return val1 == 0;
608 }
609 }
610#endif
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000611 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000612 val1 = strcmp(opnd1, opnd2);
613 if (op->op_num == STREQ)
614 return val1 == 0;
615 if (op->op_num == STRNE)
616 return val1 != 0;
617 if (op->op_num == STRLT)
618 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200619 /*if (op->op_num == STRGT)*/
620 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000621 }
622 /* We are sure that these three are by now the only binops we didn't check
623 * yet, so we do not check if the class is correct:
624 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000625/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000626 {
627 struct stat b1, b2;
628
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000629 if (stat(opnd1, &b1) || stat(opnd2, &b2))
630 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000631 if (op->op_num == FILNT)
632 return b1.st_mtime > b2.st_mtime;
633 if (op->op_num == FILOT)
634 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200635 /*if (op->op_num == FILEQ)*/
636 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000637 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200638 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000639}
640
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000641static void initialize_group_array(void)
642{
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200643 group_array = bb_getgroups(&ngroups, NULL);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000644}
645
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000646/* Return non-zero if GID is one that we have in our groups list. */
647//XXX: FIXME: duplicate of existing libbb function?
648// see toplevel TODO file:
649// possible code duplication ingroup() and is_a_group_member()
650static int is_a_group_member(gid_t gid)
651{
652 int i;
653
654 /* Short-circuit if possible, maybe saving a call to getgroups(). */
655 if (gid == getgid() || gid == getegid())
656 return 1;
657
658 if (ngroups == 0)
659 initialize_group_array();
660
661 /* Search through the list looking for GID. */
662 for (i = 0; i < ngroups; i++)
663 if (gid == group_array[i])
664 return 1;
665
666 return 0;
667}
668
669
670/* Do the same thing access(2) does, but use the effective uid and gid,
671 and don't make the mistake of telling root that any file is
672 executable. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200673static int test_eaccess(struct stat *st, int mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000674{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000675 unsigned int euid = geteuid();
676
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000677 if (euid == 0) {
678 /* Root can read or write any file. */
679 if (mode != X_OK)
680 return 0;
681
682 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100683 * bits set. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200684 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000685 return 0;
686 }
687
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200688 if (st->st_uid == euid) /* owner */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000689 mode <<= 6;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200690 else if (is_a_group_member(st->st_gid))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000691 mode <<= 3;
692
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200693 if (st->st_mode & mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000694 return 0;
695
696 return -1;
697}
698
699
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000700static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000701{
702 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000703 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000704
705 if (mode == FILSYM) {
706#ifdef S_IFLNK
707 if (lstat(nm, &s) == 0) {
708 i = S_IFLNK;
709 goto filetype;
710 }
711#endif
712 return 0;
713 }
714
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000715 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000716 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000717 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000718 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000719 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000720 if (mode == FILRD)
721 i = R_OK;
722 if (mode == FILWR)
723 i = W_OK;
724 if (mode == FILEX)
725 i = X_OK;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200726 return test_eaccess(&s, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000727 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000728 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000729 if (mode == FILREG)
730 i = S_IFREG;
731 if (mode == FILDIR)
732 i = S_IFDIR;
733 if (mode == FILCDEV)
734 i = S_IFCHR;
735 if (mode == FILBDEV)
736 i = S_IFBLK;
737 if (mode == FILFIFO) {
738#ifdef S_IFIFO
739 i = S_IFIFO;
740#else
741 return 0;
742#endif
743 }
744 if (mode == FILSOCK) {
745#ifdef S_IFSOCK
746 i = S_IFSOCK;
747#else
748 return 0;
749#endif
750 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000751 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000752 return ((s.st_mode & S_IFMT) == i);
753 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000754 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000755 if (mode == FILSUID)
756 i = S_ISUID;
757 if (mode == FILSGID)
758 i = S_ISGID;
759 if (mode == FILSTCK)
760 i = S_ISVTX;
761 return ((s.st_mode & i) != 0);
762 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000763 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000764 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000765 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000766 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000767 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000768 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000769 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000770}
771
Erik Andersen13456d12000-03-16 08:09:57 +0000772
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000773static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000774{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000775 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000776
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000777 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
778 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200779 n = check_operator(*++args);
780 if (n == EOI) {
781 /* special case: [ ! ], [ a -a ! ] are valid */
782 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100783 args--;
784 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200785 return 1;
786 }
787 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000788 unnest_msg("<nexpr:%lld\n", res);
789 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000790 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000791 res = primary(n);
792 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000793 return res;
794}
795
796
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000797static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000798{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000799 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000800
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000801 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
802 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100803 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000804 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100805 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000806 res = aexpr(check_operator(*++args)) && res;
807 unnest_msg("<aexpr:%lld\n", res);
808 return res;
809 }
810 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100811 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000812 return res;
813}
814
815
816static number_t oexpr(enum token n)
817{
818 number_t res;
819
820 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
821 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100822 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000823 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100824 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000825 res = oexpr(check_operator(*++args)) || res;
826 unnest_msg("<oexpr:%lld\n", res);
827 return res;
828 }
829 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100830 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000831 return res;
832}
833
834
835static number_t primary(enum token n)
836{
837#if TEST_DEBUG
838 number_t res = res; /* for compiler */
839#else
840 number_t res;
841#endif
842 const struct operator_t *args0_op;
843
844 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000845 if (n == EOI) {
846 syntax(NULL, "argument expected");
847 }
848 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000849 res = oexpr(check_operator(*++args));
850 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000851 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000852 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000853 return res;
854 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000855
856 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
857 * do the same */
858 args0_op = last_operator;
859 /* last_operator = operator at args[1] */
860 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
861 if (args[2]) {
862 // coreutils also does this:
863 // if (args[3] && args[0]="-l" && args[2] is BINOP)
864 // return binop(1 /* prepended by -l */);
865 if (last_operator->op_type == BINOP)
866 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
867 }
868 }
869 /* check "is args[0] unop?" second */
870 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000871 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000872 if (args[1] == NULL)
873// syntax(args0_op->op_text, "argument expected");
874 goto check_emptiness;
875 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000876 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000877 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000878 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000879 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000880 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000881 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
882 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000883 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000884
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000885 /*check_operator(args[1]); - already done */
886 if (last_operator->op_type == BINOP) {
887 /* args[2] is known to be NULL, isn't it bound to fail? */
888 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000889 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000890 check_emptiness:
891 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000892}
893
894
895int test_main(int argc, char **argv)
896{
897 int res;
898 const char *arg0;
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100899#if BASH_TEST2
900 bool bt2 = 0;
901#endif
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000902
903 arg0 = bb_basename(argv[0]);
Denys Vlasenko265062d2017-01-10 15:13:30 +0100904 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100905 && (arg0[0] == '[')
906 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000907 --argc;
908 if (!arg0[1]) { /* "[" ? */
909 if (NOT_LONE_CHAR(argv[argc], ']')) {
James Byrne69374872019-07-02 11:35:03 +0200910 bb_simple_error_msg("missing ]");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000911 return 2;
912 }
913 } else { /* assuming "[[" */
914 if (strcmp(argv[argc], "]]") != 0) {
James Byrne69374872019-07-02 11:35:03 +0200915 bb_simple_error_msg("missing ]]");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000916 return 2;
917 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100918#if BASH_TEST2
919 bt2 = 1;
920#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000921 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000922 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000923 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200924 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000925
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000926 /* We must do DEINIT_S() prior to returning */
927 INIT_S();
928
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100929#if BASH_TEST2
930 bash_test2 = bt2;
931#endif
932
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000933 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000934 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000935 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000936
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000937 /* resetting ngroups is probably unnecessary. it will
938 * force a new call to getgroups(), which prevents using
939 * group data fetched during a previous call. but the
940 * only way the group data could be stale is if there's
941 * been an intervening call to setgroups(), and this
942 * isn't likely in the case of a shell. paranoia
943 * prevails...
944 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200945 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000946
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000947 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200948 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000949
Denys Vlasenko98654b92014-07-01 14:16:28 +0200950 /* Implement special cases from POSIX.2, section 4.62.4.
951 * Testcase: "test '(' = '('"
952 * The general parser would misinterpret '(' as group start.
953 */
954 if (1) {
955 int negate = 0;
956 again:
957 if (!argv[0]) {
958 /* "test" */
959 res = 1;
960 goto ret_special;
961 }
962 if (!argv[1]) {
963 /* "test [!] arg" */
964 res = (argv[0][0] == '\0');
965 goto ret_special;
966 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200967 if (argv[2]) {
968 if (!argv[3]) {
969 /*
970 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
971 * """ 3 arguments:
972 * If $2 is a binary primary, perform the binary test of $1 and $3.
973 * """
974 */
975 check_operator(argv[1]);
976 if (last_operator->op_type == BINOP) {
977 /* "test [!] arg1 <binary_op> arg2" */
978 args = argv;
979 res = (binop() == 0);
Denys Vlasenko98654b92014-07-01 14:16:28 +0200980 ret_special:
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200981 /* If there was leading "!" op... */
982 res ^= negate;
983 goto ret;
984 }
985 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
986 * Looks like this works without additional coding.
987 */
988 goto check_negate;
989 }
990 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
991 if (!argv[4]) {
992 /*
993 * """ 4 arguments:
994 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
995 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
996 * """
997 * Example why code below is necessary: test '(' ! -e ')'
998 */
999 if (LONE_CHAR(argv[0], '(')
1000 && LONE_CHAR(argv[3], ')')
1001 ) {
1002 /* "test [!] ( x y )" */
1003 argv[3] = NULL;
1004 argv++;
1005 }
Denys Vlasenko98654b92014-07-01 14:16:28 +02001006 }
1007 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +02001008 check_negate:
Denys Vlasenko98654b92014-07-01 14:16:28 +02001009 if (LONE_CHAR(argv[0], '!')) {
1010 argv++;
1011 negate ^= 1;
1012 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +00001013 }
1014 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +00001015
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +00001016 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +00001017
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +00001018 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +01001019 /* Examples:
1020 * test 3 -lt 5 6
1021 * test -t 1 2
1022 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +00001023 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +00001024 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +00001025 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +00001026 ret:
1027 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +00001028 return res;
Erik Andersen13456d12000-03-16 08:09:57 +00001029}