blob: 868ffbecbff1b0bb7da6287db6c21327069f1116 [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"
Erik Andersen13456d12000-03-16 08:09:57 +000079
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000080/* This is a NOFORK applet. Be very careful! */
81
82/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010083 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000084 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000085
Erik Andersen13456d12000-03-16 08:09:57 +000086/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020087 oexpr ::= aexpr | aexpr "-o" oexpr ;
88 aexpr ::= nexpr | nexpr "-a" aexpr ;
89 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000090 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000091 | operand binary-operator operand
92 | operand
93 | "(" oexpr ")"
94 ;
95 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
96 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
97
Mike Frysinger75ac42b2005-04-14 02:49:22 +000098 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000099 "-nt"|"-ot"|"-ef";
100 operand ::= <any legal UNIX file name>
101*/
102
Denys Vlasenko82a6fb32009-06-14 19:42:12 +0200103/* TODO: handle [[ expr ]] bashism bash-compatibly.
104 * [[ ]] is meant to be a "better [ ]", with less weird syntax
105 * and without the risk of variables and quoted strings misinterpreted
106 * as operators.
107 * This will require support from shells - we need to know quote status
108 * of each parameter (see below).
109 *
110 * Word splitting and pathname expansion should NOT be performed:
111 * # a="a b"; [[ $a = "a b" ]] && echo YES
112 * YES
113 * # [[ /bin/m* ]] && echo YES
114 * YES
115 *
116 * =~ should do regexp match
117 * = and == should do pattern match against right side:
118 * # [[ *a* == bab ]] && echo YES
119 * # [[ bab == *a* ]] && echo YES
120 * YES
121 * != does the negated == (i.e., also with pattern matching).
122 * Pattern matching is quotation-sensitive:
123 * # [[ bab == "b"a* ]] && echo YES
124 * YES
125 * # [[ bab == b"a*" ]] && echo YES
126 *
127 * Conditional operators such as -f must be unquoted literals to be recognized:
128 * # [[ -e /bin ]] && echo YES
129 * YES
130 * # [[ '-e' /bin ]] && echo YES
131 * bash: conditional binary operator expected...
132 * # A='-e'; [[ $A /bin ]] && echo YES
133 * bash: conditional binary operator expected...
134 *
135 * || and && should work as -o and -a work in [ ]
136 * -a and -o aren't recognized (&& and || are to be used instead)
137 * ( and ) do not need to be quoted unlike in [ ]:
138 * # [[ ( abc ) && '' ]] && echo YES
139 * # [[ ( abc ) || '' ]] && echo YES
140 * YES
141 * # [[ ( abc ) -o '' ]] && echo YES
142 * bash: syntax error in conditional expression...
143 *
144 * Apart from the above, [[ expr ]] should work as [ expr ]
145 */
146
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000147#define TEST_DEBUG 0
148
Erik Andersen13456d12000-03-16 08:09:57 +0000149enum token {
150 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200151
152 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000153 FILWR,
154 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200155
Erik Andersen13456d12000-03-16 08:09:57 +0000156 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200157
158 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000159 FILDIR,
160 FILCDEV,
161 FILBDEV,
162 FILFIFO,
163 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200164
Erik Andersen13456d12000-03-16 08:09:57 +0000165 FILSYM,
166 FILGZ,
167 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200168
169 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000170 FILSGID,
171 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200172
173 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000174 FILOT,
175 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200176
Erik Andersen13456d12000-03-16 08:09:57 +0000177 FILUID,
178 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200179
180 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000181 STRNZ,
182 STREQ,
183 STRNE,
184 STRLT,
185 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200186
187 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000188 INTNE,
189 INTGE,
190 INTGT,
191 INTLE,
192 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200193
Erik Andersen13456d12000-03-16 08:09:57 +0000194 UNOT,
195 BAND,
196 BOR,
197 LPAREN,
198 RPAREN,
199 OPERAND
200};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000201#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
202#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
203#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000204#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000205#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
206#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000207
208#if TEST_DEBUG
209int depth;
210#define nest_msg(...) do { \
211 depth++; \
212 fprintf(stderr, "%*s", depth*2, ""); \
213 fprintf(stderr, __VA_ARGS__); \
214} while (0)
215#define unnest_msg(...) do { \
216 fprintf(stderr, "%*s", depth*2, ""); \
217 fprintf(stderr, __VA_ARGS__); \
218 depth--; \
219} while (0)
220#define dbg_msg(...) do { \
221 fprintf(stderr, "%*s", depth*2, ""); \
222 fprintf(stderr, __VA_ARGS__); \
223} while (0)
224#define unnest_msg_and_return(expr, ...) do { \
225 number_t __res = (expr); \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__, res); \
228 depth--; \
229 return __res; \
230} while (0)
231static const char *const TOKSTR[] = {
232 "EOI",
233 "FILRD",
234 "FILWR",
235 "FILEX",
236 "FILEXIST",
237 "FILREG",
238 "FILDIR",
239 "FILCDEV",
240 "FILBDEV",
241 "FILFIFO",
242 "FILSOCK",
243 "FILSYM",
244 "FILGZ",
245 "FILTT",
246 "FILSUID",
247 "FILSGID",
248 "FILSTCK",
249 "FILNT",
250 "FILOT",
251 "FILEQ",
252 "FILUID",
253 "FILGID",
254 "STREZ",
255 "STRNZ",
256 "STREQ",
257 "STRNE",
258 "STRLT",
259 "STRGT",
260 "INTEQ",
261 "INTNE",
262 "INTGE",
263 "INTGT",
264 "INTLE",
265 "INTLT",
266 "UNOT",
267 "BAND",
268 "BOR",
269 "LPAREN",
270 "RPAREN",
271 "OPERAND"
272};
273#else
274#define nest_msg(...) ((void)0)
275#define unnest_msg(...) ((void)0)
276#define dbg_msg(...) ((void)0)
277#define unnest_msg_and_return(expr, ...) return expr
278#endif
279
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200280enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000281 UNOP,
282 BINOP,
283 BUNOP,
284 BBINOP,
285 PAREN
286};
287
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000288struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000289 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000290};
291
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200292static const struct operator_t ops_table[] = {
293 { /* "-r" */ FILRD , UNOP },
294 { /* "-w" */ FILWR , UNOP },
295 { /* "-x" */ FILEX , UNOP },
296 { /* "-e" */ FILEXIST, UNOP },
297 { /* "-f" */ FILREG , UNOP },
298 { /* "-d" */ FILDIR , UNOP },
299 { /* "-c" */ FILCDEV , UNOP },
300 { /* "-b" */ FILBDEV , UNOP },
301 { /* "-p" */ FILFIFO , UNOP },
302 { /* "-u" */ FILSUID , UNOP },
303 { /* "-g" */ FILSGID , UNOP },
304 { /* "-k" */ FILSTCK , UNOP },
305 { /* "-s" */ FILGZ , UNOP },
306 { /* "-t" */ FILTT , UNOP },
307 { /* "-z" */ STREZ , UNOP },
308 { /* "-n" */ STRNZ , UNOP },
309 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000310
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200311 { /* "-O" */ FILUID , UNOP },
312 { /* "-G" */ FILGID , UNOP },
313 { /* "-L" */ FILSYM , UNOP },
314 { /* "-S" */ FILSOCK , UNOP },
315 { /* "=" */ STREQ , BINOP },
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200316 /* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
317 * lists only "=" as comparison operator.
318 */
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200319 { /* "==" */ STREQ , BINOP },
320 { /* "!=" */ STRNE , BINOP },
321 { /* "<" */ STRLT , BINOP },
322 { /* ">" */ STRGT , BINOP },
323 { /* "-eq"*/ INTEQ , BINOP },
324 { /* "-ne"*/ INTNE , BINOP },
325 { /* "-ge"*/ INTGE , BINOP },
326 { /* "-gt"*/ INTGT , BINOP },
327 { /* "-le"*/ INTLE , BINOP },
328 { /* "-lt"*/ INTLT , BINOP },
329 { /* "-nt"*/ FILNT , BINOP },
330 { /* "-ot"*/ FILOT , BINOP },
331 { /* "-ef"*/ FILEQ , BINOP },
332 { /* "!" */ UNOT , BUNOP },
333 { /* "-a" */ BAND , BBINOP },
334 { /* "-o" */ BOR , BBINOP },
335 { /* "(" */ LPAREN , PAREN },
336 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000337};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200338/* Please keep these two tables in sync */
339static const char ops_texts[] ALIGN1 =
340 "-r" "\0"
341 "-w" "\0"
342 "-x" "\0"
343 "-e" "\0"
344 "-f" "\0"
345 "-d" "\0"
346 "-c" "\0"
347 "-b" "\0"
348 "-p" "\0"
349 "-u" "\0"
350 "-g" "\0"
351 "-k" "\0"
352 "-s" "\0"
353 "-t" "\0"
354 "-z" "\0"
355 "-n" "\0"
356 "-h" "\0"
357
358 "-O" "\0"
359 "-G" "\0"
360 "-L" "\0"
361 "-S" "\0"
362 "=" "\0"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200363 /* "==" is bashism */
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200364 "==" "\0"
365 "!=" "\0"
366 "<" "\0"
367 ">" "\0"
368 "-eq" "\0"
369 "-ne" "\0"
370 "-ge" "\0"
371 "-gt" "\0"
372 "-le" "\0"
373 "-lt" "\0"
374 "-nt" "\0"
375 "-ot" "\0"
376 "-ef" "\0"
377 "!" "\0"
378 "-a" "\0"
379 "-o" "\0"
380 "(" "\0"
381 ")" "\0"
382;
Erik Andersen13456d12000-03-16 08:09:57 +0000383
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000384
385#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000386typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000387#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000388typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000389#endif
390
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000391
392/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000393struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000394 char **args;
395 /* set only by check_operator(), either to bogus struct
396 * or points to matching operator_t struct. Never NULL. */
397 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000398 gid_t *group_array;
399 int ngroups;
400 jmp_buf leaving;
401};
402
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000403/* See test_ptr_hack.c */
404extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000405
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000406#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000407#define args (S.args )
408#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000409#define group_array (S.group_array )
410#define ngroups (S.ngroups )
411#define leaving (S.leaving )
412
413#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000414 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000415 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000416} while (0)
417#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200418 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000419 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000420} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000421
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000422static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000423
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000424static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000425static void syntax(const char *op, const char *msg)
426{
427 if (op && *op) {
428 bb_error_msg("%s: %s", op, msg);
429 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000430 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000431 }
432 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000433}
434
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000435/* atoi with error detection */
436//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000437static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000438{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000439 char *p;
440#if ENABLE_FEATURE_TEST_64
441 long long r;
442#else
443 long r;
444#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000445
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000446 errno = 0;
447#if ENABLE_FEATURE_TEST_64
448 r = strtoll(s, &p, 10);
449#else
450 r = strtol(s, &p, 10);
451#endif
452
453 if (errno != 0)
454 syntax(s, "out of range");
455
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200456 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000457 syntax(s, "bad number");
458
459 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000460}
461
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000462/* UNUSED
463static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000464{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000465 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000466
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000467 return (stat(f1, &b1) == 0 &&
468 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000469}
470
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000471static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000472{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000473 struct stat b1, b2;
474
475 return (stat(f1, &b1) == 0 &&
476 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000477}
478
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000479static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000480{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000481 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000482
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000483 return (stat(f1, &b1) == 0 &&
484 stat(f2, &b2) == 0 &&
485 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000486}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000487*/
488
489
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200490static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000491{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000492 static const struct operator_t no_op = {
493 .op_num = -1,
494 .op_type = -1
495 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200496 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000497
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000498 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200499 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000500 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200501 n = index_in_strings(ops_texts, s);
502 if (n < 0)
503 return OPERAND;
504 last_operator = &ops_table[n];
505 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000506}
507
Erik Andersen13456d12000-03-16 08:09:57 +0000508
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000509static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000510{
511 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000512 const struct operator_t *op;
513 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000514
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000515 opnd1 = *args;
516 check_operator(*++args);
517 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000518
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000519 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000520 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200521 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000522
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000523 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000524 val1 = getn(opnd1);
525 val2 = getn(opnd2);
526 if (op->op_num == INTEQ)
527 return val1 == val2;
528 if (op->op_num == INTNE)
529 return val1 != val2;
530 if (op->op_num == INTGE)
531 return val1 >= val2;
532 if (op->op_num == INTGT)
533 return val1 > val2;
534 if (op->op_num == INTLE)
535 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200536 /*if (op->op_num == INTLT)*/
537 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000538 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000539 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000540 val1 = strcmp(opnd1, opnd2);
541 if (op->op_num == STREQ)
542 return val1 == 0;
543 if (op->op_num == STRNE)
544 return val1 != 0;
545 if (op->op_num == STRLT)
546 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200547 /*if (op->op_num == STRGT)*/
548 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000549 }
550 /* We are sure that these three are by now the only binops we didn't check
551 * yet, so we do not check if the class is correct:
552 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000553/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000554 {
555 struct stat b1, b2;
556
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000557 if (stat(opnd1, &b1) || stat(opnd2, &b2))
558 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000559 if (op->op_num == FILNT)
560 return b1.st_mtime > b2.st_mtime;
561 if (op->op_num == FILOT)
562 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200563 /*if (op->op_num == FILEQ)*/
564 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000565 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200566 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000567}
568
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000569static void initialize_group_array(void)
570{
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200571 group_array = bb_getgroups(&ngroups, NULL);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000572}
573
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000574/* Return non-zero if GID is one that we have in our groups list. */
575//XXX: FIXME: duplicate of existing libbb function?
576// see toplevel TODO file:
577// possible code duplication ingroup() and is_a_group_member()
578static int is_a_group_member(gid_t gid)
579{
580 int i;
581
582 /* Short-circuit if possible, maybe saving a call to getgroups(). */
583 if (gid == getgid() || gid == getegid())
584 return 1;
585
586 if (ngroups == 0)
587 initialize_group_array();
588
589 /* Search through the list looking for GID. */
590 for (i = 0; i < ngroups; i++)
591 if (gid == group_array[i])
592 return 1;
593
594 return 0;
595}
596
597
598/* Do the same thing access(2) does, but use the effective uid and gid,
599 and don't make the mistake of telling root that any file is
600 executable. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200601static int test_eaccess(struct stat *st, int mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000602{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000603 unsigned int euid = geteuid();
604
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000605 if (euid == 0) {
606 /* Root can read or write any file. */
607 if (mode != X_OK)
608 return 0;
609
610 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100611 * bits set. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200612 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000613 return 0;
614 }
615
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200616 if (st->st_uid == euid) /* owner */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000617 mode <<= 6;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200618 else if (is_a_group_member(st->st_gid))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000619 mode <<= 3;
620
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200621 if (st->st_mode & mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000622 return 0;
623
624 return -1;
625}
626
627
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000628static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000629{
630 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000631 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000632
633 if (mode == FILSYM) {
634#ifdef S_IFLNK
635 if (lstat(nm, &s) == 0) {
636 i = S_IFLNK;
637 goto filetype;
638 }
639#endif
640 return 0;
641 }
642
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000643 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000644 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000645 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000646 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000647 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000648 if (mode == FILRD)
649 i = R_OK;
650 if (mode == FILWR)
651 i = W_OK;
652 if (mode == FILEX)
653 i = X_OK;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200654 return test_eaccess(&s, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000655 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000656 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000657 if (mode == FILREG)
658 i = S_IFREG;
659 if (mode == FILDIR)
660 i = S_IFDIR;
661 if (mode == FILCDEV)
662 i = S_IFCHR;
663 if (mode == FILBDEV)
664 i = S_IFBLK;
665 if (mode == FILFIFO) {
666#ifdef S_IFIFO
667 i = S_IFIFO;
668#else
669 return 0;
670#endif
671 }
672 if (mode == FILSOCK) {
673#ifdef S_IFSOCK
674 i = S_IFSOCK;
675#else
676 return 0;
677#endif
678 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000679 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000680 return ((s.st_mode & S_IFMT) == i);
681 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000682 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000683 if (mode == FILSUID)
684 i = S_ISUID;
685 if (mode == FILSGID)
686 i = S_ISGID;
687 if (mode == FILSTCK)
688 i = S_ISVTX;
689 return ((s.st_mode & i) != 0);
690 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000691 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000692 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000693 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000694 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000695 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000696 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000697 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000698}
699
Erik Andersen13456d12000-03-16 08:09:57 +0000700
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000701static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000702{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000703 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000704
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000705 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
706 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200707 n = check_operator(*++args);
708 if (n == EOI) {
709 /* special case: [ ! ], [ a -a ! ] are valid */
710 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100711 args--;
712 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200713 return 1;
714 }
715 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000716 unnest_msg("<nexpr:%lld\n", res);
717 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000718 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000719 res = primary(n);
720 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000721 return res;
722}
723
724
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000725static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000726{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000727 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000728
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000729 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
730 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100731 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000732 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100733 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000734 res = aexpr(check_operator(*++args)) && res;
735 unnest_msg("<aexpr:%lld\n", res);
736 return res;
737 }
738 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100739 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000740 return res;
741}
742
743
744static number_t oexpr(enum token n)
745{
746 number_t res;
747
748 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
749 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100750 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000751 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100752 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000753 res = oexpr(check_operator(*++args)) || res;
754 unnest_msg("<oexpr:%lld\n", res);
755 return res;
756 }
757 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100758 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000759 return res;
760}
761
762
763static number_t primary(enum token n)
764{
765#if TEST_DEBUG
766 number_t res = res; /* for compiler */
767#else
768 number_t res;
769#endif
770 const struct operator_t *args0_op;
771
772 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000773 if (n == EOI) {
774 syntax(NULL, "argument expected");
775 }
776 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000777 res = oexpr(check_operator(*++args));
778 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000779 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000780 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000781 return res;
782 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000783
784 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
785 * do the same */
786 args0_op = last_operator;
787 /* last_operator = operator at args[1] */
788 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
789 if (args[2]) {
790 // coreutils also does this:
791 // if (args[3] && args[0]="-l" && args[2] is BINOP)
792 // return binop(1 /* prepended by -l */);
793 if (last_operator->op_type == BINOP)
794 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
795 }
796 }
797 /* check "is args[0] unop?" second */
798 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000799 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000800 if (args[1] == NULL)
801// syntax(args0_op->op_text, "argument expected");
802 goto check_emptiness;
803 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000804 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000805 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000806 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000807 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000808 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000809 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
810 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000811 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000812
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000813 /*check_operator(args[1]); - already done */
814 if (last_operator->op_type == BINOP) {
815 /* args[2] is known to be NULL, isn't it bound to fail? */
816 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000817 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000818 check_emptiness:
819 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000820}
821
822
823int test_main(int argc, char **argv)
824{
825 int res;
826 const char *arg0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000827
828 arg0 = bb_basename(argv[0]);
Denys Vlasenko265062d2017-01-10 15:13:30 +0100829 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100830 && (arg0[0] == '[')
831 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000832 --argc;
833 if (!arg0[1]) { /* "[" ? */
834 if (NOT_LONE_CHAR(argv[argc], ']')) {
James Byrne69374872019-07-02 11:35:03 +0200835 bb_simple_error_msg("missing ]");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000836 return 2;
837 }
838 } else { /* assuming "[[" */
839 if (strcmp(argv[argc], "]]") != 0) {
James Byrne69374872019-07-02 11:35:03 +0200840 bb_simple_error_msg("missing ]]");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000841 return 2;
842 }
Erik Andersen13456d12000-03-16 08:09:57 +0000843 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000844 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000845 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200846 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000847
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000848 /* We must do DEINIT_S() prior to returning */
849 INIT_S();
850
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000851 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000852 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000853 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000854
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000855 /* resetting ngroups is probably unnecessary. it will
856 * force a new call to getgroups(), which prevents using
857 * group data fetched during a previous call. but the
858 * only way the group data could be stale is if there's
859 * been an intervening call to setgroups(), and this
860 * isn't likely in the case of a shell. paranoia
861 * prevails...
862 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200863 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000864
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000865 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200866 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000867
Denys Vlasenko98654b92014-07-01 14:16:28 +0200868 /* Implement special cases from POSIX.2, section 4.62.4.
869 * Testcase: "test '(' = '('"
870 * The general parser would misinterpret '(' as group start.
871 */
872 if (1) {
873 int negate = 0;
874 again:
875 if (!argv[0]) {
876 /* "test" */
877 res = 1;
878 goto ret_special;
879 }
880 if (!argv[1]) {
881 /* "test [!] arg" */
882 res = (argv[0][0] == '\0');
883 goto ret_special;
884 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200885 if (argv[2]) {
886 if (!argv[3]) {
887 /*
888 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
889 * """ 3 arguments:
890 * If $2 is a binary primary, perform the binary test of $1 and $3.
891 * """
892 */
893 check_operator(argv[1]);
894 if (last_operator->op_type == BINOP) {
895 /* "test [!] arg1 <binary_op> arg2" */
896 args = argv;
897 res = (binop() == 0);
Denys Vlasenko98654b92014-07-01 14:16:28 +0200898 ret_special:
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200899 /* If there was leading "!" op... */
900 res ^= negate;
901 goto ret;
902 }
903 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
904 * Looks like this works without additional coding.
905 */
906 goto check_negate;
907 }
908 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
909 if (!argv[4]) {
910 /*
911 * """ 4 arguments:
912 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
913 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
914 * """
915 * Example why code below is necessary: test '(' ! -e ')'
916 */
917 if (LONE_CHAR(argv[0], '(')
918 && LONE_CHAR(argv[3], ')')
919 ) {
920 /* "test [!] ( x y )" */
921 argv[3] = NULL;
922 argv++;
923 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200924 }
925 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200926 check_negate:
Denys Vlasenko98654b92014-07-01 14:16:28 +0200927 if (LONE_CHAR(argv[0], '!')) {
928 argv++;
929 negate ^= 1;
930 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000931 }
932 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000933
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000934 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000935
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000936 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100937 /* Examples:
938 * test 3 -lt 5 6
939 * test -t 1 2
940 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000941 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000942 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000943 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000944 ret:
945 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000946 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000947}