blob: 4df505a0507b4bb290b66b786ecfe76b61436ffb [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
23//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
24//kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o
25//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
26
27//config:config TEST
28//config: bool "test"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020029//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020030//config: help
31//config: test is used to check file types and compare values,
32//config: returning an appropriate exit code. The bash shell
33//config: has test built in, ash can build it in optionally.
34//config:
35//config:config FEATURE_TEST_64
36//config: bool "Extend test to 64 bit"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020037//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020038//config: depends on TEST || ASH_BUILTIN_TEST || HUSH
39//config: help
40//config: Enable 64-bit support in test.
41
Pere Orga34425382011-03-31 14:43:25 +020042/* "test --help" does not print help (POSIX compat), only "[ --help" does.
43 * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION")
44 * Unfortunately, it screws up generated BusyBox.html. TODO. */
45//usage:#define test_trivial_usage
46//usage: "EXPRESSION ]"
47//usage:#define test_full_usage "\n\n"
48//usage: "Check file types, compare values etc. Return a 0/1 exit code\n"
49//usage: "depending on logical value of EXPRESSION"
50//usage:
51//usage:#define test_example_usage
52//usage: "$ test 1 -eq 2\n"
53//usage: "$ echo $?\n"
54//usage: "1\n"
55//usage: "$ test 1 -eq 1\n"
56//usage: "$ echo $?\n"
57//usage: "0\n"
58//usage: "$ [ -d /etc ]\n"
59//usage: "$ echo $?\n"
60//usage: "0\n"
61//usage: "$ [ -d /junk ]\n"
62//usage: "$ echo $?\n"
63//usage: "1\n"
64
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000065#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000066#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000067
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000068/* This is a NOFORK applet. Be very careful! */
69
70/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010071 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000072 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000073
Erik Andersen13456d12000-03-16 08:09:57 +000074/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020075 oexpr ::= aexpr | aexpr "-o" oexpr ;
76 aexpr ::= nexpr | nexpr "-a" aexpr ;
77 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000078 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000079 | operand binary-operator operand
80 | operand
81 | "(" oexpr ")"
82 ;
83 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
84 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
85
Mike Frysinger75ac42b2005-04-14 02:49:22 +000086 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000087 "-nt"|"-ot"|"-ef";
88 operand ::= <any legal UNIX file name>
89*/
90
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020091/* TODO: handle [[ expr ]] bashism bash-compatibly.
92 * [[ ]] is meant to be a "better [ ]", with less weird syntax
93 * and without the risk of variables and quoted strings misinterpreted
94 * as operators.
95 * This will require support from shells - we need to know quote status
96 * of each parameter (see below).
97 *
98 * Word splitting and pathname expansion should NOT be performed:
99 * # a="a b"; [[ $a = "a b" ]] && echo YES
100 * YES
101 * # [[ /bin/m* ]] && echo YES
102 * YES
103 *
104 * =~ should do regexp match
105 * = and == should do pattern match against right side:
106 * # [[ *a* == bab ]] && echo YES
107 * # [[ bab == *a* ]] && echo YES
108 * YES
109 * != does the negated == (i.e., also with pattern matching).
110 * Pattern matching is quotation-sensitive:
111 * # [[ bab == "b"a* ]] && echo YES
112 * YES
113 * # [[ bab == b"a*" ]] && echo YES
114 *
115 * Conditional operators such as -f must be unquoted literals to be recognized:
116 * # [[ -e /bin ]] && echo YES
117 * YES
118 * # [[ '-e' /bin ]] && echo YES
119 * bash: conditional binary operator expected...
120 * # A='-e'; [[ $A /bin ]] && echo YES
121 * bash: conditional binary operator expected...
122 *
123 * || and && should work as -o and -a work in [ ]
124 * -a and -o aren't recognized (&& and || are to be used instead)
125 * ( and ) do not need to be quoted unlike in [ ]:
126 * # [[ ( abc ) && '' ]] && echo YES
127 * # [[ ( abc ) || '' ]] && echo YES
128 * YES
129 * # [[ ( abc ) -o '' ]] && echo YES
130 * bash: syntax error in conditional expression...
131 *
132 * Apart from the above, [[ expr ]] should work as [ expr ]
133 */
134
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000135#define TEST_DEBUG 0
136
Erik Andersen13456d12000-03-16 08:09:57 +0000137enum token {
138 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200139
140 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000141 FILWR,
142 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200143
Erik Andersen13456d12000-03-16 08:09:57 +0000144 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200145
146 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000147 FILDIR,
148 FILCDEV,
149 FILBDEV,
150 FILFIFO,
151 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200152
Erik Andersen13456d12000-03-16 08:09:57 +0000153 FILSYM,
154 FILGZ,
155 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200156
157 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000158 FILSGID,
159 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200160
161 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000162 FILOT,
163 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200164
Erik Andersen13456d12000-03-16 08:09:57 +0000165 FILUID,
166 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200167
168 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000169 STRNZ,
170 STREQ,
171 STRNE,
172 STRLT,
173 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200174
175 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000176 INTNE,
177 INTGE,
178 INTGT,
179 INTLE,
180 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200181
Erik Andersen13456d12000-03-16 08:09:57 +0000182 UNOT,
183 BAND,
184 BOR,
185 LPAREN,
186 RPAREN,
187 OPERAND
188};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000189#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
190#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
191#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000192#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000193#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
194#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000195
196#if TEST_DEBUG
197int depth;
198#define nest_msg(...) do { \
199 depth++; \
200 fprintf(stderr, "%*s", depth*2, ""); \
201 fprintf(stderr, __VA_ARGS__); \
202} while (0)
203#define unnest_msg(...) do { \
204 fprintf(stderr, "%*s", depth*2, ""); \
205 fprintf(stderr, __VA_ARGS__); \
206 depth--; \
207} while (0)
208#define dbg_msg(...) do { \
209 fprintf(stderr, "%*s", depth*2, ""); \
210 fprintf(stderr, __VA_ARGS__); \
211} while (0)
212#define unnest_msg_and_return(expr, ...) do { \
213 number_t __res = (expr); \
214 fprintf(stderr, "%*s", depth*2, ""); \
215 fprintf(stderr, __VA_ARGS__, res); \
216 depth--; \
217 return __res; \
218} while (0)
219static const char *const TOKSTR[] = {
220 "EOI",
221 "FILRD",
222 "FILWR",
223 "FILEX",
224 "FILEXIST",
225 "FILREG",
226 "FILDIR",
227 "FILCDEV",
228 "FILBDEV",
229 "FILFIFO",
230 "FILSOCK",
231 "FILSYM",
232 "FILGZ",
233 "FILTT",
234 "FILSUID",
235 "FILSGID",
236 "FILSTCK",
237 "FILNT",
238 "FILOT",
239 "FILEQ",
240 "FILUID",
241 "FILGID",
242 "STREZ",
243 "STRNZ",
244 "STREQ",
245 "STRNE",
246 "STRLT",
247 "STRGT",
248 "INTEQ",
249 "INTNE",
250 "INTGE",
251 "INTGT",
252 "INTLE",
253 "INTLT",
254 "UNOT",
255 "BAND",
256 "BOR",
257 "LPAREN",
258 "RPAREN",
259 "OPERAND"
260};
261#else
262#define nest_msg(...) ((void)0)
263#define unnest_msg(...) ((void)0)
264#define dbg_msg(...) ((void)0)
265#define unnest_msg_and_return(expr, ...) return expr
266#endif
267
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200268enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000269 UNOP,
270 BINOP,
271 BUNOP,
272 BBINOP,
273 PAREN
274};
275
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000276struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000277 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000278};
279
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200280static const struct operator_t ops_table[] = {
281 { /* "-r" */ FILRD , UNOP },
282 { /* "-w" */ FILWR , UNOP },
283 { /* "-x" */ FILEX , UNOP },
284 { /* "-e" */ FILEXIST, UNOP },
285 { /* "-f" */ FILREG , UNOP },
286 { /* "-d" */ FILDIR , UNOP },
287 { /* "-c" */ FILCDEV , UNOP },
288 { /* "-b" */ FILBDEV , UNOP },
289 { /* "-p" */ FILFIFO , UNOP },
290 { /* "-u" */ FILSUID , UNOP },
291 { /* "-g" */ FILSGID , UNOP },
292 { /* "-k" */ FILSTCK , UNOP },
293 { /* "-s" */ FILGZ , UNOP },
294 { /* "-t" */ FILTT , UNOP },
295 { /* "-z" */ STREZ , UNOP },
296 { /* "-n" */ STRNZ , UNOP },
297 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000298
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200299 { /* "-O" */ FILUID , UNOP },
300 { /* "-G" */ FILGID , UNOP },
301 { /* "-L" */ FILSYM , UNOP },
302 { /* "-S" */ FILSOCK , UNOP },
303 { /* "=" */ STREQ , BINOP },
304 { /* "==" */ STREQ , BINOP },
305 { /* "!=" */ STRNE , BINOP },
306 { /* "<" */ STRLT , BINOP },
307 { /* ">" */ STRGT , BINOP },
308 { /* "-eq"*/ INTEQ , BINOP },
309 { /* "-ne"*/ INTNE , BINOP },
310 { /* "-ge"*/ INTGE , BINOP },
311 { /* "-gt"*/ INTGT , BINOP },
312 { /* "-le"*/ INTLE , BINOP },
313 { /* "-lt"*/ INTLT , BINOP },
314 { /* "-nt"*/ FILNT , BINOP },
315 { /* "-ot"*/ FILOT , BINOP },
316 { /* "-ef"*/ FILEQ , BINOP },
317 { /* "!" */ UNOT , BUNOP },
318 { /* "-a" */ BAND , BBINOP },
319 { /* "-o" */ BOR , BBINOP },
320 { /* "(" */ LPAREN , PAREN },
321 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000322};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200323/* Please keep these two tables in sync */
324static const char ops_texts[] ALIGN1 =
325 "-r" "\0"
326 "-w" "\0"
327 "-x" "\0"
328 "-e" "\0"
329 "-f" "\0"
330 "-d" "\0"
331 "-c" "\0"
332 "-b" "\0"
333 "-p" "\0"
334 "-u" "\0"
335 "-g" "\0"
336 "-k" "\0"
337 "-s" "\0"
338 "-t" "\0"
339 "-z" "\0"
340 "-n" "\0"
341 "-h" "\0"
342
343 "-O" "\0"
344 "-G" "\0"
345 "-L" "\0"
346 "-S" "\0"
347 "=" "\0"
348 "==" "\0"
349 "!=" "\0"
350 "<" "\0"
351 ">" "\0"
352 "-eq" "\0"
353 "-ne" "\0"
354 "-ge" "\0"
355 "-gt" "\0"
356 "-le" "\0"
357 "-lt" "\0"
358 "-nt" "\0"
359 "-ot" "\0"
360 "-ef" "\0"
361 "!" "\0"
362 "-a" "\0"
363 "-o" "\0"
364 "(" "\0"
365 ")" "\0"
366;
Erik Andersen13456d12000-03-16 08:09:57 +0000367
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000368
369#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000370typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000371#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000372typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000373#endif
374
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000375
376/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000377struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000378 char **args;
379 /* set only by check_operator(), either to bogus struct
380 * or points to matching operator_t struct. Never NULL. */
381 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000382 gid_t *group_array;
383 int ngroups;
384 jmp_buf leaving;
385};
386
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000387/* See test_ptr_hack.c */
388extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000389
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000390#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000391#define args (S.args )
392#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000393#define group_array (S.group_array )
394#define ngroups (S.ngroups )
395#define leaving (S.leaving )
396
397#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000398 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000399 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000400} while (0)
401#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000402 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000403} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000404
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000405static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000406
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000407static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000408static void syntax(const char *op, const char *msg)
409{
410 if (op && *op) {
411 bb_error_msg("%s: %s", op, msg);
412 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000413 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000414 }
415 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000416}
417
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000418/* atoi with error detection */
419//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000420static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000421{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000422 char *p;
423#if ENABLE_FEATURE_TEST_64
424 long long r;
425#else
426 long r;
427#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000428
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000429 errno = 0;
430#if ENABLE_FEATURE_TEST_64
431 r = strtoll(s, &p, 10);
432#else
433 r = strtol(s, &p, 10);
434#endif
435
436 if (errno != 0)
437 syntax(s, "out of range");
438
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200439 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000440 syntax(s, "bad number");
441
442 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000443}
444
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000445/* UNUSED
446static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000447{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000448 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000449
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000450 return (stat(f1, &b1) == 0 &&
451 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000452}
453
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000454static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000455{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000456 struct stat b1, b2;
457
458 return (stat(f1, &b1) == 0 &&
459 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000460}
461
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000462static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000463{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000464 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000465
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000466 return (stat(f1, &b1) == 0 &&
467 stat(f2, &b2) == 0 &&
468 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000469}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000470*/
471
472
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200473static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000474{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000475 static const struct operator_t no_op = {
476 .op_num = -1,
477 .op_type = -1
478 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200479 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000480
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000481 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200482 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000483 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200484 n = index_in_strings(ops_texts, s);
485 if (n < 0)
486 return OPERAND;
487 last_operator = &ops_table[n];
488 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000489}
490
Erik Andersen13456d12000-03-16 08:09:57 +0000491
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000492static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000493{
494 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000495 const struct operator_t *op;
496 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000497
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000498 opnd1 = *args;
499 check_operator(*++args);
500 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000501
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000502 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000503 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200504 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000505
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000506 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000507 val1 = getn(opnd1);
508 val2 = getn(opnd2);
509 if (op->op_num == INTEQ)
510 return val1 == val2;
511 if (op->op_num == INTNE)
512 return val1 != val2;
513 if (op->op_num == INTGE)
514 return val1 >= val2;
515 if (op->op_num == INTGT)
516 return val1 > val2;
517 if (op->op_num == INTLE)
518 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200519 /*if (op->op_num == INTLT)*/
520 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000521 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000522 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000523 val1 = strcmp(opnd1, opnd2);
524 if (op->op_num == STREQ)
525 return val1 == 0;
526 if (op->op_num == STRNE)
527 return val1 != 0;
528 if (op->op_num == STRLT)
529 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200530 /*if (op->op_num == STRGT)*/
531 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000532 }
533 /* We are sure that these three are by now the only binops we didn't check
534 * yet, so we do not check if the class is correct:
535 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000536/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000537 {
538 struct stat b1, b2;
539
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000540 if (stat(opnd1, &b1) || stat(opnd2, &b2))
541 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000542 if (op->op_num == FILNT)
543 return b1.st_mtime > b2.st_mtime;
544 if (op->op_num == FILOT)
545 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200546 /*if (op->op_num == FILEQ)*/
547 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000548 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200549 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000550}
551
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000552
553static void initialize_group_array(void)
554{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200555 int n;
556
557 /* getgroups may be expensive, try to use it only once */
558 ngroups = 32;
559 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000560 /* FIXME: ash tries so hard to not die on OOM,
561 * and we spoil it with just one xrealloc here */
562 /* We realloc, because test_main can be entered repeatedly by shell.
563 * Testcase (ash): 'while true; do test -x some_file; done'
564 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200565 n = ngroups;
566 group_array = xrealloc(group_array, n * sizeof(gid_t));
567 ngroups = getgroups(n, group_array);
568 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000569}
570
571
572/* Return non-zero if GID is one that we have in our groups list. */
573//XXX: FIXME: duplicate of existing libbb function?
574// see toplevel TODO file:
575// possible code duplication ingroup() and is_a_group_member()
576static int is_a_group_member(gid_t gid)
577{
578 int i;
579
580 /* Short-circuit if possible, maybe saving a call to getgroups(). */
581 if (gid == getgid() || gid == getegid())
582 return 1;
583
584 if (ngroups == 0)
585 initialize_group_array();
586
587 /* Search through the list looking for GID. */
588 for (i = 0; i < ngroups; i++)
589 if (gid == group_array[i])
590 return 1;
591
592 return 0;
593}
594
595
596/* Do the same thing access(2) does, but use the effective uid and gid,
597 and don't make the mistake of telling root that any file is
598 executable. */
599static int test_eaccess(char *path, int mode)
600{
601 struct stat st;
602 unsigned int euid = geteuid();
603
604 if (stat(path, &st) < 0)
605 return -1;
606
607 if (euid == 0) {
608 /* Root can read or write any file. */
609 if (mode != X_OK)
610 return 0;
611
612 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100613 * bits set. */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000614 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
615 return 0;
616 }
617
618 if (st.st_uid == euid) /* owner */
619 mode <<= 6;
620 else if (is_a_group_member(st.st_gid))
621 mode <<= 3;
622
623 if (st.st_mode & mode)
624 return 0;
625
626 return -1;
627}
628
629
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000630static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000631{
632 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000633 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000634
635 if (mode == FILSYM) {
636#ifdef S_IFLNK
637 if (lstat(nm, &s) == 0) {
638 i = S_IFLNK;
639 goto filetype;
640 }
641#endif
642 return 0;
643 }
644
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000645 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000646 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000647 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000648 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000649 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000650 if (mode == FILRD)
651 i = R_OK;
652 if (mode == FILWR)
653 i = W_OK;
654 if (mode == FILEX)
655 i = X_OK;
656 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000657 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000658 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000659 if (mode == FILREG)
660 i = S_IFREG;
661 if (mode == FILDIR)
662 i = S_IFDIR;
663 if (mode == FILCDEV)
664 i = S_IFCHR;
665 if (mode == FILBDEV)
666 i = S_IFBLK;
667 if (mode == FILFIFO) {
668#ifdef S_IFIFO
669 i = S_IFIFO;
670#else
671 return 0;
672#endif
673 }
674 if (mode == FILSOCK) {
675#ifdef S_IFSOCK
676 i = S_IFSOCK;
677#else
678 return 0;
679#endif
680 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000681 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000682 return ((s.st_mode & S_IFMT) == i);
683 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000684 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000685 if (mode == FILSUID)
686 i = S_ISUID;
687 if (mode == FILSGID)
688 i = S_ISGID;
689 if (mode == FILSTCK)
690 i = S_ISVTX;
691 return ((s.st_mode & i) != 0);
692 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000693 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000694 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000695 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000696 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000697 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000698 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000699 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000700}
701
Erik Andersen13456d12000-03-16 08:09:57 +0000702
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000703static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000704{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000705 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000706
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000707 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
708 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200709 n = check_operator(*++args);
710 if (n == EOI) {
711 /* special case: [ ! ], [ a -a ! ] are valid */
712 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100713 args--;
714 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200715 return 1;
716 }
717 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000718 unnest_msg("<nexpr:%lld\n", res);
719 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000720 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000721 res = primary(n);
722 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000723 return res;
724}
725
726
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000727static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000728{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000729 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000730
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000731 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
732 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100733 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000734 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100735 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000736 res = aexpr(check_operator(*++args)) && res;
737 unnest_msg("<aexpr:%lld\n", res);
738 return res;
739 }
740 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100741 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000742 return res;
743}
744
745
746static number_t oexpr(enum token n)
747{
748 number_t res;
749
750 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
751 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100752 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000753 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100754 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000755 res = oexpr(check_operator(*++args)) || res;
756 unnest_msg("<oexpr:%lld\n", res);
757 return res;
758 }
759 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100760 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000761 return res;
762}
763
764
765static number_t primary(enum token n)
766{
767#if TEST_DEBUG
768 number_t res = res; /* for compiler */
769#else
770 number_t res;
771#endif
772 const struct operator_t *args0_op;
773
774 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000775 if (n == EOI) {
776 syntax(NULL, "argument expected");
777 }
778 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000779 res = oexpr(check_operator(*++args));
780 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000781 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000782 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000783 return res;
784 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000785
786 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
787 * do the same */
788 args0_op = last_operator;
789 /* last_operator = operator at args[1] */
790 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
791 if (args[2]) {
792 // coreutils also does this:
793 // if (args[3] && args[0]="-l" && args[2] is BINOP)
794 // return binop(1 /* prepended by -l */);
795 if (last_operator->op_type == BINOP)
796 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
797 }
798 }
799 /* check "is args[0] unop?" second */
800 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000801 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000802 if (args[1] == NULL)
803// syntax(args0_op->op_text, "argument expected");
804 goto check_emptiness;
805 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000806 if (n == STREZ)
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 == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000809 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000810 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000811 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
812 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000813 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000814
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000815 /*check_operator(args[1]); - already done */
816 if (last_operator->op_type == BINOP) {
817 /* args[2] is known to be NULL, isn't it bound to fail? */
818 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000819 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000820 check_emptiness:
821 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000822}
823
824
825int test_main(int argc, char **argv)
826{
827 int res;
828 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000829// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000830
831 arg0 = bb_basename(argv[0]);
832 if (arg0[0] == '[') {
833 --argc;
834 if (!arg0[1]) { /* "[" ? */
835 if (NOT_LONE_CHAR(argv[argc], ']')) {
836 bb_error_msg("missing ]");
837 return 2;
838 }
839 } else { /* assuming "[[" */
840 if (strcmp(argv[argc], "]]") != 0) {
841 bb_error_msg("missing ]]");
842 return 2;
843 }
Erik Andersen13456d12000-03-16 08:09:57 +0000844 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000845 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000846 }
847
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 //argc--;
866 argv++;
867
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000868 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000869 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000870 res = 1;
871 goto ret;
872 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000873#if 0
874// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000875 if (LONE_CHAR(argv[0], '!') && argv[1]) {
876 negate = 1;
877 //argc--;
878 argv++;
879 }
880 if (!argv[1]) { /* "test [!] arg" */
881 res = (*argv[0] == '\0');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000882 goto ret;
883 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000884 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000885 check_operator(argv[1]);
886 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000887 /* "test [!] arg1 <binary_op> arg2" */
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200888 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000889 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000890 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000891 }
892 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000893
894 /* Some complex expression. Undo '!' removal */
895 if (negate) {
896 negate = 0;
897 //argc++;
898 argv--;
899 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000900#endif
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200901 args = argv;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000902 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000903
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000904 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100905 /* Examples:
906 * test 3 -lt 5 6
907 * test -t 1 2
908 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000909 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000910 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000911 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000912 ret:
913 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000914// return negate ? !res : res;
915 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000916}