blob: a8286525a87155a4dfcbd7cfb20cea062f711b47 [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"
Paul Fox6ab03782006-06-08 21:37:26 +000079#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000080
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000081/* This is a NOFORK applet. Be very careful! */
82
83/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010084 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000085 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000086
Erik Andersen13456d12000-03-16 08:09:57 +000087/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020088 oexpr ::= aexpr | aexpr "-o" oexpr ;
89 aexpr ::= nexpr | nexpr "-a" aexpr ;
90 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000091 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000092 | operand binary-operator operand
93 | operand
94 | "(" oexpr ")"
95 ;
96 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
97 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
98
Mike Frysinger75ac42b2005-04-14 02:49:22 +000099 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +0000100 "-nt"|"-ot"|"-ef";
101 operand ::= <any legal UNIX file name>
102*/
103
Denys Vlasenko82a6fb32009-06-14 19:42:12 +0200104/* TODO: handle [[ expr ]] bashism bash-compatibly.
105 * [[ ]] is meant to be a "better [ ]", with less weird syntax
106 * and without the risk of variables and quoted strings misinterpreted
107 * as operators.
108 * This will require support from shells - we need to know quote status
109 * of each parameter (see below).
110 *
111 * Word splitting and pathname expansion should NOT be performed:
112 * # a="a b"; [[ $a = "a b" ]] && echo YES
113 * YES
114 * # [[ /bin/m* ]] && echo YES
115 * YES
116 *
117 * =~ should do regexp match
118 * = and == should do pattern match against right side:
119 * # [[ *a* == bab ]] && echo YES
120 * # [[ bab == *a* ]] && echo YES
121 * YES
122 * != does the negated == (i.e., also with pattern matching).
123 * Pattern matching is quotation-sensitive:
124 * # [[ bab == "b"a* ]] && echo YES
125 * YES
126 * # [[ bab == b"a*" ]] && echo YES
127 *
128 * Conditional operators such as -f must be unquoted literals to be recognized:
129 * # [[ -e /bin ]] && echo YES
130 * YES
131 * # [[ '-e' /bin ]] && echo YES
132 * bash: conditional binary operator expected...
133 * # A='-e'; [[ $A /bin ]] && echo YES
134 * bash: conditional binary operator expected...
135 *
136 * || and && should work as -o and -a work in [ ]
137 * -a and -o aren't recognized (&& and || are to be used instead)
138 * ( and ) do not need to be quoted unlike in [ ]:
139 * # [[ ( abc ) && '' ]] && echo YES
140 * # [[ ( abc ) || '' ]] && echo YES
141 * YES
142 * # [[ ( abc ) -o '' ]] && echo YES
143 * bash: syntax error in conditional expression...
144 *
145 * Apart from the above, [[ expr ]] should work as [ expr ]
146 */
147
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000148#define TEST_DEBUG 0
149
Erik Andersen13456d12000-03-16 08:09:57 +0000150enum token {
151 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200152
153 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000154 FILWR,
155 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200156
Erik Andersen13456d12000-03-16 08:09:57 +0000157 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200158
159 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000160 FILDIR,
161 FILCDEV,
162 FILBDEV,
163 FILFIFO,
164 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200165
Erik Andersen13456d12000-03-16 08:09:57 +0000166 FILSYM,
167 FILGZ,
168 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200169
170 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000171 FILSGID,
172 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200173
174 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000175 FILOT,
176 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200177
Erik Andersen13456d12000-03-16 08:09:57 +0000178 FILUID,
179 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200180
181 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000182 STRNZ,
183 STREQ,
184 STRNE,
185 STRLT,
186 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200187
188 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000189 INTNE,
190 INTGE,
191 INTGT,
192 INTLE,
193 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200194
Erik Andersen13456d12000-03-16 08:09:57 +0000195 UNOT,
196 BAND,
197 BOR,
198 LPAREN,
199 RPAREN,
200 OPERAND
201};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000202#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
203#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
204#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000205#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000206#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
207#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000208
209#if TEST_DEBUG
210int depth;
211#define nest_msg(...) do { \
212 depth++; \
213 fprintf(stderr, "%*s", depth*2, ""); \
214 fprintf(stderr, __VA_ARGS__); \
215} while (0)
216#define unnest_msg(...) do { \
217 fprintf(stderr, "%*s", depth*2, ""); \
218 fprintf(stderr, __VA_ARGS__); \
219 depth--; \
220} while (0)
221#define dbg_msg(...) do { \
222 fprintf(stderr, "%*s", depth*2, ""); \
223 fprintf(stderr, __VA_ARGS__); \
224} while (0)
225#define unnest_msg_and_return(expr, ...) do { \
226 number_t __res = (expr); \
227 fprintf(stderr, "%*s", depth*2, ""); \
228 fprintf(stderr, __VA_ARGS__, res); \
229 depth--; \
230 return __res; \
231} while (0)
232static const char *const TOKSTR[] = {
233 "EOI",
234 "FILRD",
235 "FILWR",
236 "FILEX",
237 "FILEXIST",
238 "FILREG",
239 "FILDIR",
240 "FILCDEV",
241 "FILBDEV",
242 "FILFIFO",
243 "FILSOCK",
244 "FILSYM",
245 "FILGZ",
246 "FILTT",
247 "FILSUID",
248 "FILSGID",
249 "FILSTCK",
250 "FILNT",
251 "FILOT",
252 "FILEQ",
253 "FILUID",
254 "FILGID",
255 "STREZ",
256 "STRNZ",
257 "STREQ",
258 "STRNE",
259 "STRLT",
260 "STRGT",
261 "INTEQ",
262 "INTNE",
263 "INTGE",
264 "INTGT",
265 "INTLE",
266 "INTLT",
267 "UNOT",
268 "BAND",
269 "BOR",
270 "LPAREN",
271 "RPAREN",
272 "OPERAND"
273};
274#else
275#define nest_msg(...) ((void)0)
276#define unnest_msg(...) ((void)0)
277#define dbg_msg(...) ((void)0)
278#define unnest_msg_and_return(expr, ...) return expr
279#endif
280
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200281enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000282 UNOP,
283 BINOP,
284 BUNOP,
285 BBINOP,
286 PAREN
287};
288
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000289struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000290 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000291};
292
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200293static const struct operator_t ops_table[] = {
294 { /* "-r" */ FILRD , UNOP },
295 { /* "-w" */ FILWR , UNOP },
296 { /* "-x" */ FILEX , UNOP },
297 { /* "-e" */ FILEXIST, UNOP },
298 { /* "-f" */ FILREG , UNOP },
299 { /* "-d" */ FILDIR , UNOP },
300 { /* "-c" */ FILCDEV , UNOP },
301 { /* "-b" */ FILBDEV , UNOP },
302 { /* "-p" */ FILFIFO , UNOP },
303 { /* "-u" */ FILSUID , UNOP },
304 { /* "-g" */ FILSGID , UNOP },
305 { /* "-k" */ FILSTCK , UNOP },
306 { /* "-s" */ FILGZ , UNOP },
307 { /* "-t" */ FILTT , UNOP },
308 { /* "-z" */ STREZ , UNOP },
309 { /* "-n" */ STRNZ , UNOP },
310 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000311
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200312 { /* "-O" */ FILUID , UNOP },
313 { /* "-G" */ FILGID , UNOP },
314 { /* "-L" */ FILSYM , UNOP },
315 { /* "-S" */ FILSOCK , UNOP },
316 { /* "=" */ STREQ , BINOP },
317 { /* "==" */ STREQ , BINOP },
318 { /* "!=" */ STRNE , BINOP },
319 { /* "<" */ STRLT , BINOP },
320 { /* ">" */ STRGT , BINOP },
321 { /* "-eq"*/ INTEQ , BINOP },
322 { /* "-ne"*/ INTNE , BINOP },
323 { /* "-ge"*/ INTGE , BINOP },
324 { /* "-gt"*/ INTGT , BINOP },
325 { /* "-le"*/ INTLE , BINOP },
326 { /* "-lt"*/ INTLT , BINOP },
327 { /* "-nt"*/ FILNT , BINOP },
328 { /* "-ot"*/ FILOT , BINOP },
329 { /* "-ef"*/ FILEQ , BINOP },
330 { /* "!" */ UNOT , BUNOP },
331 { /* "-a" */ BAND , BBINOP },
332 { /* "-o" */ BOR , BBINOP },
333 { /* "(" */ LPAREN , PAREN },
334 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000335};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200336/* Please keep these two tables in sync */
337static const char ops_texts[] ALIGN1 =
338 "-r" "\0"
339 "-w" "\0"
340 "-x" "\0"
341 "-e" "\0"
342 "-f" "\0"
343 "-d" "\0"
344 "-c" "\0"
345 "-b" "\0"
346 "-p" "\0"
347 "-u" "\0"
348 "-g" "\0"
349 "-k" "\0"
350 "-s" "\0"
351 "-t" "\0"
352 "-z" "\0"
353 "-n" "\0"
354 "-h" "\0"
355
356 "-O" "\0"
357 "-G" "\0"
358 "-L" "\0"
359 "-S" "\0"
360 "=" "\0"
361 "==" "\0"
362 "!=" "\0"
363 "<" "\0"
364 ">" "\0"
365 "-eq" "\0"
366 "-ne" "\0"
367 "-ge" "\0"
368 "-gt" "\0"
369 "-le" "\0"
370 "-lt" "\0"
371 "-nt" "\0"
372 "-ot" "\0"
373 "-ef" "\0"
374 "!" "\0"
375 "-a" "\0"
376 "-o" "\0"
377 "(" "\0"
378 ")" "\0"
379;
Erik Andersen13456d12000-03-16 08:09:57 +0000380
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000381
382#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000383typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000384#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000385typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000386#endif
387
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000388
389/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000390struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000391 char **args;
392 /* set only by check_operator(), either to bogus struct
393 * or points to matching operator_t struct. Never NULL. */
394 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000395 gid_t *group_array;
396 int ngroups;
397 jmp_buf leaving;
398};
399
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000400/* See test_ptr_hack.c */
401extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000402
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000403#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000404#define args (S.args )
405#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000406#define group_array (S.group_array )
407#define ngroups (S.ngroups )
408#define leaving (S.leaving )
409
410#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000411 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000412 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000413} while (0)
414#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200415 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000416 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000417} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000418
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000419static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000420
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000421static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000422static void syntax(const char *op, const char *msg)
423{
424 if (op && *op) {
425 bb_error_msg("%s: %s", op, msg);
426 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000427 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000428 }
429 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000430}
431
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000432/* atoi with error detection */
433//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000434static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000435{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000436 char *p;
437#if ENABLE_FEATURE_TEST_64
438 long long r;
439#else
440 long r;
441#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000442
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000443 errno = 0;
444#if ENABLE_FEATURE_TEST_64
445 r = strtoll(s, &p, 10);
446#else
447 r = strtol(s, &p, 10);
448#endif
449
450 if (errno != 0)
451 syntax(s, "out of range");
452
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200453 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000454 syntax(s, "bad number");
455
456 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000457}
458
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000459/* UNUSED
460static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000461{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000462 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000463
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000464 return (stat(f1, &b1) == 0 &&
465 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000466}
467
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000468static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000469{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000470 struct stat b1, b2;
471
472 return (stat(f1, &b1) == 0 &&
473 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000474}
475
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000476static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000477{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000478 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000479
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000480 return (stat(f1, &b1) == 0 &&
481 stat(f2, &b2) == 0 &&
482 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000483}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000484*/
485
486
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200487static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000488{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000489 static const struct operator_t no_op = {
490 .op_num = -1,
491 .op_type = -1
492 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200493 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000494
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000495 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200496 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000497 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200498 n = index_in_strings(ops_texts, s);
499 if (n < 0)
500 return OPERAND;
501 last_operator = &ops_table[n];
502 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000503}
504
Erik Andersen13456d12000-03-16 08:09:57 +0000505
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000506static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000507{
508 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000509 const struct operator_t *op;
510 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000511
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000512 opnd1 = *args;
513 check_operator(*++args);
514 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000515
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000516 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000517 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200518 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000519
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000520 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000521 val1 = getn(opnd1);
522 val2 = getn(opnd2);
523 if (op->op_num == INTEQ)
524 return val1 == val2;
525 if (op->op_num == INTNE)
526 return val1 != val2;
527 if (op->op_num == INTGE)
528 return val1 >= val2;
529 if (op->op_num == INTGT)
530 return val1 > val2;
531 if (op->op_num == INTLE)
532 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200533 /*if (op->op_num == INTLT)*/
534 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000535 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000536 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000537 val1 = strcmp(opnd1, opnd2);
538 if (op->op_num == STREQ)
539 return val1 == 0;
540 if (op->op_num == STRNE)
541 return val1 != 0;
542 if (op->op_num == STRLT)
543 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200544 /*if (op->op_num == STRGT)*/
545 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000546 }
547 /* We are sure that these three are by now the only binops we didn't check
548 * yet, so we do not check if the class is correct:
549 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000550/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000551 {
552 struct stat b1, b2;
553
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000554 if (stat(opnd1, &b1) || stat(opnd2, &b2))
555 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000556 if (op->op_num == FILNT)
557 return b1.st_mtime > b2.st_mtime;
558 if (op->op_num == FILOT)
559 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200560 /*if (op->op_num == FILEQ)*/
561 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000562 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200563 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000564}
565
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000566static void initialize_group_array(void)
567{
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200568 group_array = bb_getgroups(&ngroups, NULL);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000569}
570
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000571/* Return non-zero if GID is one that we have in our groups list. */
572//XXX: FIXME: duplicate of existing libbb function?
573// see toplevel TODO file:
574// possible code duplication ingroup() and is_a_group_member()
575static int is_a_group_member(gid_t gid)
576{
577 int i;
578
579 /* Short-circuit if possible, maybe saving a call to getgroups(). */
580 if (gid == getgid() || gid == getegid())
581 return 1;
582
583 if (ngroups == 0)
584 initialize_group_array();
585
586 /* Search through the list looking for GID. */
587 for (i = 0; i < ngroups; i++)
588 if (gid == group_array[i])
589 return 1;
590
591 return 0;
592}
593
594
595/* Do the same thing access(2) does, but use the effective uid and gid,
596 and don't make the mistake of telling root that any file is
597 executable. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200598static int test_eaccess(struct stat *st, int mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000599{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000600 unsigned int euid = geteuid();
601
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000602 if (euid == 0) {
603 /* Root can read or write any file. */
604 if (mode != X_OK)
605 return 0;
606
607 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100608 * bits set. */
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200609 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000610 return 0;
611 }
612
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200613 if (st->st_uid == euid) /* owner */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000614 mode <<= 6;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200615 else if (is_a_group_member(st->st_gid))
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000616 mode <<= 3;
617
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200618 if (st->st_mode & mode)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000619 return 0;
620
621 return -1;
622}
623
624
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000625static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000626{
627 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000628 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000629
630 if (mode == FILSYM) {
631#ifdef S_IFLNK
632 if (lstat(nm, &s) == 0) {
633 i = S_IFLNK;
634 goto filetype;
635 }
636#endif
637 return 0;
638 }
639
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000640 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000641 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000642 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000643 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000644 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000645 if (mode == FILRD)
646 i = R_OK;
647 if (mode == FILWR)
648 i = W_OK;
649 if (mode == FILEX)
650 i = X_OK;
Denys Vlasenkoa8cf9c52017-07-04 18:49:24 +0200651 return test_eaccess(&s, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000652 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000653 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000654 if (mode == FILREG)
655 i = S_IFREG;
656 if (mode == FILDIR)
657 i = S_IFDIR;
658 if (mode == FILCDEV)
659 i = S_IFCHR;
660 if (mode == FILBDEV)
661 i = S_IFBLK;
662 if (mode == FILFIFO) {
663#ifdef S_IFIFO
664 i = S_IFIFO;
665#else
666 return 0;
667#endif
668 }
669 if (mode == FILSOCK) {
670#ifdef S_IFSOCK
671 i = S_IFSOCK;
672#else
673 return 0;
674#endif
675 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000676 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000677 return ((s.st_mode & S_IFMT) == i);
678 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000679 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000680 if (mode == FILSUID)
681 i = S_ISUID;
682 if (mode == FILSGID)
683 i = S_ISGID;
684 if (mode == FILSTCK)
685 i = S_ISVTX;
686 return ((s.st_mode & i) != 0);
687 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000688 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000689 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000690 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000691 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000692 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000693 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000694 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000695}
696
Erik Andersen13456d12000-03-16 08:09:57 +0000697
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000698static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000699{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000700 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000701
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000702 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
703 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200704 n = check_operator(*++args);
705 if (n == EOI) {
706 /* special case: [ ! ], [ a -a ! ] are valid */
707 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100708 args--;
709 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200710 return 1;
711 }
712 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000713 unnest_msg("<nexpr:%lld\n", res);
714 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000715 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000716 res = primary(n);
717 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000718 return res;
719}
720
721
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000722static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000723{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000724 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000725
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000726 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
727 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100728 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000729 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100730 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000731 res = aexpr(check_operator(*++args)) && res;
732 unnest_msg("<aexpr:%lld\n", res);
733 return res;
734 }
735 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100736 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000737 return res;
738}
739
740
741static number_t oexpr(enum token n)
742{
743 number_t res;
744
745 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
746 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100747 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000748 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100749 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000750 res = oexpr(check_operator(*++args)) || res;
751 unnest_msg("<oexpr:%lld\n", res);
752 return res;
753 }
754 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100755 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000756 return res;
757}
758
759
760static number_t primary(enum token n)
761{
762#if TEST_DEBUG
763 number_t res = res; /* for compiler */
764#else
765 number_t res;
766#endif
767 const struct operator_t *args0_op;
768
769 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000770 if (n == EOI) {
771 syntax(NULL, "argument expected");
772 }
773 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000774 res = oexpr(check_operator(*++args));
775 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000776 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000777 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000778 return res;
779 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000780
781 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
782 * do the same */
783 args0_op = last_operator;
784 /* last_operator = operator at args[1] */
785 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
786 if (args[2]) {
787 // coreutils also does this:
788 // if (args[3] && args[0]="-l" && args[2] is BINOP)
789 // return binop(1 /* prepended by -l */);
790 if (last_operator->op_type == BINOP)
791 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
792 }
793 }
794 /* check "is args[0] unop?" second */
795 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000796 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000797 if (args[1] == NULL)
798// syntax(args0_op->op_text, "argument expected");
799 goto check_emptiness;
800 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000801 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000802 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000803 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000804 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000805 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000806 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
807 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000808 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000809
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000810 /*check_operator(args[1]); - already done */
811 if (last_operator->op_type == BINOP) {
812 /* args[2] is known to be NULL, isn't it bound to fail? */
813 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000814 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000815 check_emptiness:
816 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000817}
818
819
820int test_main(int argc, char **argv)
821{
822 int res;
823 const char *arg0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000824
825 arg0 = bb_basename(argv[0]);
Denys Vlasenko265062d2017-01-10 15:13:30 +0100826 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100827 && (arg0[0] == '[')
828 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000829 --argc;
830 if (!arg0[1]) { /* "[" ? */
831 if (NOT_LONE_CHAR(argv[argc], ']')) {
832 bb_error_msg("missing ]");
833 return 2;
834 }
835 } else { /* assuming "[[" */
836 if (strcmp(argv[argc], "]]") != 0) {
837 bb_error_msg("missing ]]");
838 return 2;
839 }
Erik Andersen13456d12000-03-16 08:09:57 +0000840 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000841 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000842 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200843 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000844
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000845 /* We must do DEINIT_S() prior to returning */
846 INIT_S();
847
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000848 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000849 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000850 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000851
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000852 /* resetting ngroups is probably unnecessary. it will
853 * force a new call to getgroups(), which prevents using
854 * group data fetched during a previous call. but the
855 * only way the group data could be stale is if there's
856 * been an intervening call to setgroups(), and this
857 * isn't likely in the case of a shell. paranoia
858 * prevails...
859 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200860 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000861
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000862 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200863 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000864
Denys Vlasenko98654b92014-07-01 14:16:28 +0200865 /* Implement special cases from POSIX.2, section 4.62.4.
866 * Testcase: "test '(' = '('"
867 * The general parser would misinterpret '(' as group start.
868 */
869 if (1) {
870 int negate = 0;
871 again:
872 if (!argv[0]) {
873 /* "test" */
874 res = 1;
875 goto ret_special;
876 }
877 if (!argv[1]) {
878 /* "test [!] arg" */
879 res = (argv[0][0] == '\0');
880 goto ret_special;
881 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200882 if (argv[2]) {
883 if (!argv[3]) {
884 /*
885 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
886 * """ 3 arguments:
887 * If $2 is a binary primary, perform the binary test of $1 and $3.
888 * """
889 */
890 check_operator(argv[1]);
891 if (last_operator->op_type == BINOP) {
892 /* "test [!] arg1 <binary_op> arg2" */
893 args = argv;
894 res = (binop() == 0);
Denys Vlasenko98654b92014-07-01 14:16:28 +0200895 ret_special:
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200896 /* If there was leading "!" op... */
897 res ^= negate;
898 goto ret;
899 }
900 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
901 * Looks like this works without additional coding.
902 */
903 goto check_negate;
904 }
905 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
906 if (!argv[4]) {
907 /*
908 * """ 4 arguments:
909 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
910 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
911 * """
912 * Example why code below is necessary: test '(' ! -e ')'
913 */
914 if (LONE_CHAR(argv[0], '(')
915 && LONE_CHAR(argv[3], ')')
916 ) {
917 /* "test [!] ( x y )" */
918 argv[3] = NULL;
919 argv++;
920 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200921 }
922 }
Denys Vlasenko78b1b1b2017-07-31 19:20:43 +0200923 check_negate:
Denys Vlasenko98654b92014-07-01 14:16:28 +0200924 if (LONE_CHAR(argv[0], '!')) {
925 argv++;
926 negate ^= 1;
927 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000928 }
929 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000930
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000931 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000932
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000933 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100934 /* Examples:
935 * test 3 -lt 5 6
936 * test -t 1 2
937 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000938 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000939 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000940 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000941 ret:
942 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000943 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000944}