blob: 824ce3b5a06c56cf0527e83696c7b69d587d12b2 [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 Vlasenko4eed2c62017-07-18 22:01:24 +020023//config: bool "test (3.6 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 },
316 { /* "==" */ STREQ , BINOP },
317 { /* "!=" */ STRNE , BINOP },
318 { /* "<" */ STRLT , BINOP },
319 { /* ">" */ STRGT , BINOP },
320 { /* "-eq"*/ INTEQ , BINOP },
321 { /* "-ne"*/ INTNE , BINOP },
322 { /* "-ge"*/ INTGE , BINOP },
323 { /* "-gt"*/ INTGT , BINOP },
324 { /* "-le"*/ INTLE , BINOP },
325 { /* "-lt"*/ INTLT , BINOP },
326 { /* "-nt"*/ FILNT , BINOP },
327 { /* "-ot"*/ FILOT , BINOP },
328 { /* "-ef"*/ FILEQ , BINOP },
329 { /* "!" */ UNOT , BUNOP },
330 { /* "-a" */ BAND , BBINOP },
331 { /* "-o" */ BOR , BBINOP },
332 { /* "(" */ LPAREN , PAREN },
333 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000334};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200335/* Please keep these two tables in sync */
336static const char ops_texts[] ALIGN1 =
337 "-r" "\0"
338 "-w" "\0"
339 "-x" "\0"
340 "-e" "\0"
341 "-f" "\0"
342 "-d" "\0"
343 "-c" "\0"
344 "-b" "\0"
345 "-p" "\0"
346 "-u" "\0"
347 "-g" "\0"
348 "-k" "\0"
349 "-s" "\0"
350 "-t" "\0"
351 "-z" "\0"
352 "-n" "\0"
353 "-h" "\0"
354
355 "-O" "\0"
356 "-G" "\0"
357 "-L" "\0"
358 "-S" "\0"
359 "=" "\0"
360 "==" "\0"
361 "!=" "\0"
362 "<" "\0"
363 ">" "\0"
364 "-eq" "\0"
365 "-ne" "\0"
366 "-ge" "\0"
367 "-gt" "\0"
368 "-le" "\0"
369 "-lt" "\0"
370 "-nt" "\0"
371 "-ot" "\0"
372 "-ef" "\0"
373 "!" "\0"
374 "-a" "\0"
375 "-o" "\0"
376 "(" "\0"
377 ")" "\0"
378;
Erik Andersen13456d12000-03-16 08:09:57 +0000379
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000380
381#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000382typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000383#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000384typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000385#endif
386
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000387
388/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000389struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000390 char **args;
391 /* set only by check_operator(), either to bogus struct
392 * or points to matching operator_t struct. Never NULL. */
393 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000394 gid_t *group_array;
395 int ngroups;
396 jmp_buf leaving;
397};
398
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000399/* See test_ptr_hack.c */
400extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000401
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000402#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000403#define args (S.args )
404#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000405#define group_array (S.group_array )
406#define ngroups (S.ngroups )
407#define leaving (S.leaving )
408
409#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000410 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000411 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000412} while (0)
413#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200414 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000415 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000416} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000417
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000418static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000419
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000420static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000421static void syntax(const char *op, const char *msg)
422{
423 if (op && *op) {
424 bb_error_msg("%s: %s", op, msg);
425 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000426 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000427 }
428 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000429}
430
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000431/* atoi with error detection */
432//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000433static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000434{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000435 char *p;
436#if ENABLE_FEATURE_TEST_64
437 long long r;
438#else
439 long r;
440#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000441
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000442 errno = 0;
443#if ENABLE_FEATURE_TEST_64
444 r = strtoll(s, &p, 10);
445#else
446 r = strtol(s, &p, 10);
447#endif
448
449 if (errno != 0)
450 syntax(s, "out of range");
451
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200452 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000453 syntax(s, "bad number");
454
455 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000456}
457
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000458/* UNUSED
459static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000460{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000461 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000462
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000463 return (stat(f1, &b1) == 0 &&
464 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000465}
466
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000467static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000468{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000469 struct stat b1, b2;
470
471 return (stat(f1, &b1) == 0 &&
472 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000473}
474
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000475static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000476{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000477 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000478
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000479 return (stat(f1, &b1) == 0 &&
480 stat(f2, &b2) == 0 &&
481 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000482}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000483*/
484
485
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200486static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000487{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000488 static const struct operator_t no_op = {
489 .op_num = -1,
490 .op_type = -1
491 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200492 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000493
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000494 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200495 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000496 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200497 n = index_in_strings(ops_texts, s);
498 if (n < 0)
499 return OPERAND;
500 last_operator = &ops_table[n];
501 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000502}
503
Erik Andersen13456d12000-03-16 08:09:57 +0000504
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000505static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000506{
507 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000508 const struct operator_t *op;
509 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000510
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000511 opnd1 = *args;
512 check_operator(*++args);
513 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000514
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000515 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000516 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200517 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000518
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000519 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000520 val1 = getn(opnd1);
521 val2 = getn(opnd2);
522 if (op->op_num == INTEQ)
523 return val1 == val2;
524 if (op->op_num == INTNE)
525 return val1 != val2;
526 if (op->op_num == INTGE)
527 return val1 >= val2;
528 if (op->op_num == INTGT)
529 return val1 > val2;
530 if (op->op_num == INTLE)
531 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200532 /*if (op->op_num == INTLT)*/
533 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000534 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000535 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000536 val1 = strcmp(opnd1, opnd2);
537 if (op->op_num == STREQ)
538 return val1 == 0;
539 if (op->op_num == STRNE)
540 return val1 != 0;
541 if (op->op_num == STRLT)
542 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200543 /*if (op->op_num == STRGT)*/
544 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000545 }
546 /* We are sure that these three are by now the only binops we didn't check
547 * yet, so we do not check if the class is correct:
548 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000549/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000550 {
551 struct stat b1, b2;
552
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000553 if (stat(opnd1, &b1) || stat(opnd2, &b2))
554 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000555 if (op->op_num == FILNT)
556 return b1.st_mtime > b2.st_mtime;
557 if (op->op_num == FILOT)
558 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200559 /*if (op->op_num == FILEQ)*/
560 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000561 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200562 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000563}
564
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000565static void initialize_group_array(void)
566{
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200567 group_array = bb_getgroups(&ngroups, NULL);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000568}
569
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000570/* Return non-zero if GID is one that we have in our groups list. */
571//XXX: FIXME: duplicate of existing libbb function?
572// see toplevel TODO file:
573// possible code duplication ingroup() and is_a_group_member()
574static int is_a_group_member(gid_t gid)
575{
576 int i;
577
578 /* Short-circuit if possible, maybe saving a call to getgroups(). */
579 if (gid == getgid() || gid == getegid())
580 return 1;
581
582 if (ngroups == 0)
583 initialize_group_array();
584
585 /* Search through the list looking for GID. */
586 for (i = 0; i < ngroups; i++)
587 if (gid == group_array[i])
588 return 1;
589
590 return 0;
591}
592
593
594/* Do the same thing access(2) does, but use the effective uid and gid,
595 and don't make the mistake of telling root that any file is
596 executable. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200597static int test_eaccess(struct stat *st, int mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000598{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000599 unsigned int euid = geteuid();
600
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000601 if (euid == 0) {
602 /* Root can read or write any file. */
603 if (mode != X_OK)
604 return 0;
605
606 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100607 * bits set. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200608 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000609 return 0;
610 }
611
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200612 if (st->st_uid == euid) /* owner */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000613 mode <<= 6;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200614 else if (is_a_group_member(st->st_gid))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000615 mode <<= 3;
616
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200617 if (st->st_mode & mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000618 return 0;
619
620 return -1;
621}
622
623
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000624static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000625{
626 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000627 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000628
629 if (mode == FILSYM) {
630#ifdef S_IFLNK
631 if (lstat(nm, &s) == 0) {
632 i = S_IFLNK;
633 goto filetype;
634 }
635#endif
636 return 0;
637 }
638
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000639 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000640 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000641 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000642 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000643 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000644 if (mode == FILRD)
645 i = R_OK;
646 if (mode == FILWR)
647 i = W_OK;
648 if (mode == FILEX)
649 i = X_OK;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200650 return test_eaccess(&s, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000651 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000652 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000653 if (mode == FILREG)
654 i = S_IFREG;
655 if (mode == FILDIR)
656 i = S_IFDIR;
657 if (mode == FILCDEV)
658 i = S_IFCHR;
659 if (mode == FILBDEV)
660 i = S_IFBLK;
661 if (mode == FILFIFO) {
662#ifdef S_IFIFO
663 i = S_IFIFO;
664#else
665 return 0;
666#endif
667 }
668 if (mode == FILSOCK) {
669#ifdef S_IFSOCK
670 i = S_IFSOCK;
671#else
672 return 0;
673#endif
674 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000675 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000676 return ((s.st_mode & S_IFMT) == i);
677 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000678 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000679 if (mode == FILSUID)
680 i = S_ISUID;
681 if (mode == FILSGID)
682 i = S_ISGID;
683 if (mode == FILSTCK)
684 i = S_ISVTX;
685 return ((s.st_mode & i) != 0);
686 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000687 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000688 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000689 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000690 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000691 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000692 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000693 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000694}
695
Erik Andersen13456d12000-03-16 08:09:57 +0000696
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000697static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000698{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000699 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000700
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000701 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
702 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200703 n = check_operator(*++args);
704 if (n == EOI) {
705 /* special case: [ ! ], [ a -a ! ] are valid */
706 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100707 args--;
708 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200709 return 1;
710 }
711 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000712 unnest_msg("<nexpr:%lld\n", res);
713 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000714 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000715 res = primary(n);
716 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000717 return res;
718}
719
720
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000721static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000722{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000723 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000724
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000725 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
726 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100727 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000728 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100729 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000730 res = aexpr(check_operator(*++args)) && res;
731 unnest_msg("<aexpr:%lld\n", res);
732 return res;
733 }
734 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100735 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000736 return res;
737}
738
739
740static number_t oexpr(enum token n)
741{
742 number_t res;
743
744 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
745 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100746 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000747 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100748 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000749 res = oexpr(check_operator(*++args)) || res;
750 unnest_msg("<oexpr:%lld\n", res);
751 return res;
752 }
753 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100754 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000755 return res;
756}
757
758
759static number_t primary(enum token n)
760{
761#if TEST_DEBUG
762 number_t res = res; /* for compiler */
763#else
764 number_t res;
765#endif
766 const struct operator_t *args0_op;
767
768 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000769 if (n == EOI) {
770 syntax(NULL, "argument expected");
771 }
772 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000773 res = oexpr(check_operator(*++args));
774 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000775 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000776 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000777 return res;
778 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000779
780 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
781 * do the same */
782 args0_op = last_operator;
783 /* last_operator = operator at args[1] */
784 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
785 if (args[2]) {
786 // coreutils also does this:
787 // if (args[3] && args[0]="-l" && args[2] is BINOP)
788 // return binop(1 /* prepended by -l */);
789 if (last_operator->op_type == BINOP)
790 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
791 }
792 }
793 /* check "is args[0] unop?" second */
794 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000795 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000796 if (args[1] == NULL)
797// syntax(args0_op->op_text, "argument expected");
798 goto check_emptiness;
799 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000800 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000801 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000802 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000803 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000804 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000805 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
806 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000807 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000808
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000809 /*check_operator(args[1]); - already done */
810 if (last_operator->op_type == BINOP) {
811 /* args[2] is known to be NULL, isn't it bound to fail? */
812 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000813 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000814 check_emptiness:
815 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000816}
817
818
819int test_main(int argc, char **argv)
820{
821 int res;
822 const char *arg0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000823
824 arg0 = bb_basename(argv[0]);
Denys Vlasenko265062d2017-01-10 15:13:30 +0100825 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100826 && (arg0[0] == '[')
827 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000828 --argc;
829 if (!arg0[1]) { /* "[" ? */
830 if (NOT_LONE_CHAR(argv[argc], ']')) {
831 bb_error_msg("missing ]");
832 return 2;
833 }
834 } else { /* assuming "[[" */
835 if (strcmp(argv[argc], "]]") != 0) {
836 bb_error_msg("missing ]]");
837 return 2;
838 }
Erik Andersen13456d12000-03-16 08:09:57 +0000839 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000840 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000841 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200842 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000843
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000844 /* We must do DEINIT_S() prior to returning */
845 INIT_S();
846
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000847 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000848 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000849 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000850
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000851 /* resetting ngroups is probably unnecessary. it will
852 * force a new call to getgroups(), which prevents using
853 * group data fetched during a previous call. but the
854 * only way the group data could be stale is if there's
855 * been an intervening call to setgroups(), and this
856 * isn't likely in the case of a shell. paranoia
857 * prevails...
858 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200859 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000860
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000861 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200862 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000863
Denys Vlasenko98654b92014-07-01 14:16:28 +0200864 /* Implement special cases from POSIX.2, section 4.62.4.
865 * Testcase: "test '(' = '('"
866 * The general parser would misinterpret '(' as group start.
867 */
868 if (1) {
869 int negate = 0;
870 again:
871 if (!argv[0]) {
872 /* "test" */
873 res = 1;
874 goto ret_special;
875 }
876 if (!argv[1]) {
877 /* "test [!] arg" */
878 res = (argv[0][0] == '\0');
879 goto ret_special;
880 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200881 if (argv[2]) {
882 if (!argv[3]) {
883 /*
884 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
885 * """ 3 arguments:
886 * If $2 is a binary primary, perform the binary test of $1 and $3.
887 * """
888 */
889 check_operator(argv[1]);
890 if (last_operator->op_type == BINOP) {
891 /* "test [!] arg1 <binary_op> arg2" */
892 args = argv;
893 res = (binop() == 0);
Denys Vlasenko98654b92014-07-01 14:16:28 +0200894 ret_special:
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200895 /* If there was leading "!" op... */
896 res ^= negate;
897 goto ret;
898 }
899 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
900 * Looks like this works without additional coding.
901 */
902 goto check_negate;
903 }
904 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
905 if (!argv[4]) {
906 /*
907 * """ 4 arguments:
908 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
909 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
910 * """
911 * Example why code below is necessary: test '(' ! -e ')'
912 */
913 if (LONE_CHAR(argv[0], '(')
914 && LONE_CHAR(argv[3], ')')
915 ) {
916 /* "test [!] ( x y )" */
917 argv[3] = NULL;
918 argv++;
919 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200920 }
921 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200922 check_negate:
Denys Vlasenko98654b92014-07-01 14:16:28 +0200923 if (LONE_CHAR(argv[0], '!')) {
924 argv++;
925 negate ^= 1;
926 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000927 }
928 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000929
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000930 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000931
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000932 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100933 /* Examples:
934 * test 3 -lt 5 6
935 * test -t 1 2
936 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000937 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000938 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000939 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000940 ret:
941 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000942 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000943}