blob: 7b8b10cd9736eb58ee676c5647835df805c18e82 [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
Denys Vlasenkode5edad2015-04-21 16:00:41 +020042/* "test --help" is special-cased to ignore --help */
43//usage:#define test_trivial_usage NOUSAGE_STR
44//usage:#define test_full_usage ""
Pere Orga34425382011-03-31 14:43:25 +020045//usage:
46//usage:#define test_example_usage
47//usage: "$ test 1 -eq 2\n"
48//usage: "$ echo $?\n"
49//usage: "1\n"
50//usage: "$ test 1 -eq 1\n"
51//usage: "$ echo $?\n"
52//usage: "0\n"
53//usage: "$ [ -d /etc ]\n"
54//usage: "$ echo $?\n"
55//usage: "0\n"
56//usage: "$ [ -d /junk ]\n"
57//usage: "$ echo $?\n"
58//usage: "1\n"
59
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000060#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000061#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000062
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000063/* This is a NOFORK applet. Be very careful! */
64
65/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010066 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000067 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000068
Erik Andersen13456d12000-03-16 08:09:57 +000069/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020070 oexpr ::= aexpr | aexpr "-o" oexpr ;
71 aexpr ::= nexpr | nexpr "-a" aexpr ;
72 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000073 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000074 | operand binary-operator operand
75 | operand
76 | "(" oexpr ")"
77 ;
78 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
79 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
80
Mike Frysinger75ac42b2005-04-14 02:49:22 +000081 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000082 "-nt"|"-ot"|"-ef";
83 operand ::= <any legal UNIX file name>
84*/
85
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020086/* TODO: handle [[ expr ]] bashism bash-compatibly.
87 * [[ ]] is meant to be a "better [ ]", with less weird syntax
88 * and without the risk of variables and quoted strings misinterpreted
89 * as operators.
90 * This will require support from shells - we need to know quote status
91 * of each parameter (see below).
92 *
93 * Word splitting and pathname expansion should NOT be performed:
94 * # a="a b"; [[ $a = "a b" ]] && echo YES
95 * YES
96 * # [[ /bin/m* ]] && echo YES
97 * YES
98 *
99 * =~ should do regexp match
100 * = and == should do pattern match against right side:
101 * # [[ *a* == bab ]] && echo YES
102 * # [[ bab == *a* ]] && echo YES
103 * YES
104 * != does the negated == (i.e., also with pattern matching).
105 * Pattern matching is quotation-sensitive:
106 * # [[ bab == "b"a* ]] && echo YES
107 * YES
108 * # [[ bab == b"a*" ]] && echo YES
109 *
110 * Conditional operators such as -f must be unquoted literals to be recognized:
111 * # [[ -e /bin ]] && echo YES
112 * YES
113 * # [[ '-e' /bin ]] && echo YES
114 * bash: conditional binary operator expected...
115 * # A='-e'; [[ $A /bin ]] && echo YES
116 * bash: conditional binary operator expected...
117 *
118 * || and && should work as -o and -a work in [ ]
119 * -a and -o aren't recognized (&& and || are to be used instead)
120 * ( and ) do not need to be quoted unlike in [ ]:
121 * # [[ ( abc ) && '' ]] && echo YES
122 * # [[ ( abc ) || '' ]] && echo YES
123 * YES
124 * # [[ ( abc ) -o '' ]] && echo YES
125 * bash: syntax error in conditional expression...
126 *
127 * Apart from the above, [[ expr ]] should work as [ expr ]
128 */
129
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000130#define TEST_DEBUG 0
131
Erik Andersen13456d12000-03-16 08:09:57 +0000132enum token {
133 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200134
135 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000136 FILWR,
137 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200138
Erik Andersen13456d12000-03-16 08:09:57 +0000139 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200140
141 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000142 FILDIR,
143 FILCDEV,
144 FILBDEV,
145 FILFIFO,
146 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200147
Erik Andersen13456d12000-03-16 08:09:57 +0000148 FILSYM,
149 FILGZ,
150 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200151
152 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000153 FILSGID,
154 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200155
156 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000157 FILOT,
158 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200159
Erik Andersen13456d12000-03-16 08:09:57 +0000160 FILUID,
161 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200162
163 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000164 STRNZ,
165 STREQ,
166 STRNE,
167 STRLT,
168 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200169
170 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000171 INTNE,
172 INTGE,
173 INTGT,
174 INTLE,
175 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200176
Erik Andersen13456d12000-03-16 08:09:57 +0000177 UNOT,
178 BAND,
179 BOR,
180 LPAREN,
181 RPAREN,
182 OPERAND
183};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000184#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
185#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
186#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000187#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000188#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
189#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000190
191#if TEST_DEBUG
192int depth;
193#define nest_msg(...) do { \
194 depth++; \
195 fprintf(stderr, "%*s", depth*2, ""); \
196 fprintf(stderr, __VA_ARGS__); \
197} while (0)
198#define unnest_msg(...) do { \
199 fprintf(stderr, "%*s", depth*2, ""); \
200 fprintf(stderr, __VA_ARGS__); \
201 depth--; \
202} while (0)
203#define dbg_msg(...) do { \
204 fprintf(stderr, "%*s", depth*2, ""); \
205 fprintf(stderr, __VA_ARGS__); \
206} while (0)
207#define unnest_msg_and_return(expr, ...) do { \
208 number_t __res = (expr); \
209 fprintf(stderr, "%*s", depth*2, ""); \
210 fprintf(stderr, __VA_ARGS__, res); \
211 depth--; \
212 return __res; \
213} while (0)
214static const char *const TOKSTR[] = {
215 "EOI",
216 "FILRD",
217 "FILWR",
218 "FILEX",
219 "FILEXIST",
220 "FILREG",
221 "FILDIR",
222 "FILCDEV",
223 "FILBDEV",
224 "FILFIFO",
225 "FILSOCK",
226 "FILSYM",
227 "FILGZ",
228 "FILTT",
229 "FILSUID",
230 "FILSGID",
231 "FILSTCK",
232 "FILNT",
233 "FILOT",
234 "FILEQ",
235 "FILUID",
236 "FILGID",
237 "STREZ",
238 "STRNZ",
239 "STREQ",
240 "STRNE",
241 "STRLT",
242 "STRGT",
243 "INTEQ",
244 "INTNE",
245 "INTGE",
246 "INTGT",
247 "INTLE",
248 "INTLT",
249 "UNOT",
250 "BAND",
251 "BOR",
252 "LPAREN",
253 "RPAREN",
254 "OPERAND"
255};
256#else
257#define nest_msg(...) ((void)0)
258#define unnest_msg(...) ((void)0)
259#define dbg_msg(...) ((void)0)
260#define unnest_msg_and_return(expr, ...) return expr
261#endif
262
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200263enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000264 UNOP,
265 BINOP,
266 BUNOP,
267 BBINOP,
268 PAREN
269};
270
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000271struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000272 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000273};
274
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200275static const struct operator_t ops_table[] = {
276 { /* "-r" */ FILRD , UNOP },
277 { /* "-w" */ FILWR , UNOP },
278 { /* "-x" */ FILEX , UNOP },
279 { /* "-e" */ FILEXIST, UNOP },
280 { /* "-f" */ FILREG , UNOP },
281 { /* "-d" */ FILDIR , UNOP },
282 { /* "-c" */ FILCDEV , UNOP },
283 { /* "-b" */ FILBDEV , UNOP },
284 { /* "-p" */ FILFIFO , UNOP },
285 { /* "-u" */ FILSUID , UNOP },
286 { /* "-g" */ FILSGID , UNOP },
287 { /* "-k" */ FILSTCK , UNOP },
288 { /* "-s" */ FILGZ , UNOP },
289 { /* "-t" */ FILTT , UNOP },
290 { /* "-z" */ STREZ , UNOP },
291 { /* "-n" */ STRNZ , UNOP },
292 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000293
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200294 { /* "-O" */ FILUID , UNOP },
295 { /* "-G" */ FILGID , UNOP },
296 { /* "-L" */ FILSYM , UNOP },
297 { /* "-S" */ FILSOCK , UNOP },
298 { /* "=" */ STREQ , BINOP },
299 { /* "==" */ STREQ , BINOP },
300 { /* "!=" */ STRNE , BINOP },
301 { /* "<" */ STRLT , BINOP },
302 { /* ">" */ STRGT , BINOP },
303 { /* "-eq"*/ INTEQ , BINOP },
304 { /* "-ne"*/ INTNE , BINOP },
305 { /* "-ge"*/ INTGE , BINOP },
306 { /* "-gt"*/ INTGT , BINOP },
307 { /* "-le"*/ INTLE , BINOP },
308 { /* "-lt"*/ INTLT , BINOP },
309 { /* "-nt"*/ FILNT , BINOP },
310 { /* "-ot"*/ FILOT , BINOP },
311 { /* "-ef"*/ FILEQ , BINOP },
312 { /* "!" */ UNOT , BUNOP },
313 { /* "-a" */ BAND , BBINOP },
314 { /* "-o" */ BOR , BBINOP },
315 { /* "(" */ LPAREN , PAREN },
316 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000317};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200318/* Please keep these two tables in sync */
319static const char ops_texts[] ALIGN1 =
320 "-r" "\0"
321 "-w" "\0"
322 "-x" "\0"
323 "-e" "\0"
324 "-f" "\0"
325 "-d" "\0"
326 "-c" "\0"
327 "-b" "\0"
328 "-p" "\0"
329 "-u" "\0"
330 "-g" "\0"
331 "-k" "\0"
332 "-s" "\0"
333 "-t" "\0"
334 "-z" "\0"
335 "-n" "\0"
336 "-h" "\0"
337
338 "-O" "\0"
339 "-G" "\0"
340 "-L" "\0"
341 "-S" "\0"
342 "=" "\0"
343 "==" "\0"
344 "!=" "\0"
345 "<" "\0"
346 ">" "\0"
347 "-eq" "\0"
348 "-ne" "\0"
349 "-ge" "\0"
350 "-gt" "\0"
351 "-le" "\0"
352 "-lt" "\0"
353 "-nt" "\0"
354 "-ot" "\0"
355 "-ef" "\0"
356 "!" "\0"
357 "-a" "\0"
358 "-o" "\0"
359 "(" "\0"
360 ")" "\0"
361;
Erik Andersen13456d12000-03-16 08:09:57 +0000362
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000363
364#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000365typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000366#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000367typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000368#endif
369
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000370
371/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000372struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000373 char **args;
374 /* set only by check_operator(), either to bogus struct
375 * or points to matching operator_t struct. Never NULL. */
376 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000377 gid_t *group_array;
378 int ngroups;
379 jmp_buf leaving;
380};
381
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000382/* See test_ptr_hack.c */
383extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000384
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000385#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000386#define args (S.args )
387#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000388#define group_array (S.group_array )
389#define ngroups (S.ngroups )
390#define leaving (S.leaving )
391
392#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000393 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000394 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000395} while (0)
396#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000397 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000398} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000399
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000400static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000401
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000402static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000403static void syntax(const char *op, const char *msg)
404{
405 if (op && *op) {
406 bb_error_msg("%s: %s", op, msg);
407 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000408 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000409 }
410 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000411}
412
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000413/* atoi with error detection */
414//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000415static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000416{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000417 char *p;
418#if ENABLE_FEATURE_TEST_64
419 long long r;
420#else
421 long r;
422#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000423
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000424 errno = 0;
425#if ENABLE_FEATURE_TEST_64
426 r = strtoll(s, &p, 10);
427#else
428 r = strtol(s, &p, 10);
429#endif
430
431 if (errno != 0)
432 syntax(s, "out of range");
433
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200434 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000435 syntax(s, "bad number");
436
437 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000438}
439
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000440/* UNUSED
441static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000442{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000443 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000444
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000445 return (stat(f1, &b1) == 0 &&
446 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000447}
448
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000449static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000450{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000451 struct stat b1, b2;
452
453 return (stat(f1, &b1) == 0 &&
454 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000455}
456
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000457static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000458{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000459 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000460
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000461 return (stat(f1, &b1) == 0 &&
462 stat(f2, &b2) == 0 &&
463 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000464}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000465*/
466
467
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200468static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000469{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000470 static const struct operator_t no_op = {
471 .op_num = -1,
472 .op_type = -1
473 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200474 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000475
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000476 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200477 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000478 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200479 n = index_in_strings(ops_texts, s);
480 if (n < 0)
481 return OPERAND;
482 last_operator = &ops_table[n];
483 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000484}
485
Erik Andersen13456d12000-03-16 08:09:57 +0000486
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000487static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000488{
489 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000490 const struct operator_t *op;
491 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000492
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000493 opnd1 = *args;
494 check_operator(*++args);
495 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000496
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000497 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000498 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200499 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000500
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000501 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000502 val1 = getn(opnd1);
503 val2 = getn(opnd2);
504 if (op->op_num == INTEQ)
505 return val1 == val2;
506 if (op->op_num == INTNE)
507 return val1 != val2;
508 if (op->op_num == INTGE)
509 return val1 >= val2;
510 if (op->op_num == INTGT)
511 return val1 > val2;
512 if (op->op_num == INTLE)
513 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200514 /*if (op->op_num == INTLT)*/
515 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000516 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000517 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000518 val1 = strcmp(opnd1, opnd2);
519 if (op->op_num == STREQ)
520 return val1 == 0;
521 if (op->op_num == STRNE)
522 return val1 != 0;
523 if (op->op_num == STRLT)
524 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200525 /*if (op->op_num == STRGT)*/
526 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000527 }
528 /* We are sure that these three are by now the only binops we didn't check
529 * yet, so we do not check if the class is correct:
530 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000531/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000532 {
533 struct stat b1, b2;
534
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000535 if (stat(opnd1, &b1) || stat(opnd2, &b2))
536 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000537 if (op->op_num == FILNT)
538 return b1.st_mtime > b2.st_mtime;
539 if (op->op_num == FILOT)
540 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200541 /*if (op->op_num == FILEQ)*/
542 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000543 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200544 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000545}
546
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000547
548static void initialize_group_array(void)
549{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200550 int n;
551
552 /* getgroups may be expensive, try to use it only once */
553 ngroups = 32;
554 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000555 /* FIXME: ash tries so hard to not die on OOM,
556 * and we spoil it with just one xrealloc here */
557 /* We realloc, because test_main can be entered repeatedly by shell.
558 * Testcase (ash): 'while true; do test -x some_file; done'
559 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200560 n = ngroups;
561 group_array = xrealloc(group_array, n * sizeof(gid_t));
562 ngroups = getgroups(n, group_array);
563 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000564}
565
566
567/* Return non-zero if GID is one that we have in our groups list. */
568//XXX: FIXME: duplicate of existing libbb function?
569// see toplevel TODO file:
570// possible code duplication ingroup() and is_a_group_member()
571static int is_a_group_member(gid_t gid)
572{
573 int i;
574
575 /* Short-circuit if possible, maybe saving a call to getgroups(). */
576 if (gid == getgid() || gid == getegid())
577 return 1;
578
579 if (ngroups == 0)
580 initialize_group_array();
581
582 /* Search through the list looking for GID. */
583 for (i = 0; i < ngroups; i++)
584 if (gid == group_array[i])
585 return 1;
586
587 return 0;
588}
589
590
591/* Do the same thing access(2) does, but use the effective uid and gid,
592 and don't make the mistake of telling root that any file is
593 executable. */
594static int test_eaccess(char *path, int mode)
595{
596 struct stat st;
597 unsigned int euid = geteuid();
598
599 if (stat(path, &st) < 0)
600 return -1;
601
602 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. */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000609 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
610 return 0;
611 }
612
613 if (st.st_uid == euid) /* owner */
614 mode <<= 6;
615 else if (is_a_group_member(st.st_gid))
616 mode <<= 3;
617
618 if (st.st_mode & mode)
619 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;
651 return test_eaccess(nm, 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]);
826 if (arg0[0] == '[') {
827 --argc;
828 if (!arg0[1]) { /* "[" ? */
829 if (NOT_LONE_CHAR(argv[argc], ']')) {
830 bb_error_msg("missing ]");
831 return 2;
832 }
833 } else { /* assuming "[[" */
834 if (strcmp(argv[argc], "]]") != 0) {
835 bb_error_msg("missing ]]");
836 return 2;
837 }
Erik Andersen13456d12000-03-16 08:09:57 +0000838 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000839 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000840 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200841 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000842
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000843 /* We must do DEINIT_S() prior to returning */
844 INIT_S();
845
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000846 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000847 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000848 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000849
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000850 /* resetting ngroups is probably unnecessary. it will
851 * force a new call to getgroups(), which prevents using
852 * group data fetched during a previous call. but the
853 * only way the group data could be stale is if there's
854 * been an intervening call to setgroups(), and this
855 * isn't likely in the case of a shell. paranoia
856 * prevails...
857 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200858 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000859
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000860 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200861 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000862
Denys Vlasenko98654b92014-07-01 14:16:28 +0200863 /* Implement special cases from POSIX.2, section 4.62.4.
864 * Testcase: "test '(' = '('"
865 * The general parser would misinterpret '(' as group start.
866 */
867 if (1) {
868 int negate = 0;
869 again:
870 if (!argv[0]) {
871 /* "test" */
872 res = 1;
873 goto ret_special;
874 }
875 if (!argv[1]) {
876 /* "test [!] arg" */
877 res = (argv[0][0] == '\0');
878 goto ret_special;
879 }
880 if (argv[2] && !argv[3]) {
881 check_operator(argv[1]);
882 if (last_operator->op_type == BINOP) {
883 /* "test [!] arg1 <binary_op> arg2" */
884 args = argv;
885 res = (binop() == 0);
886 ret_special:
887 /* If there was leading "!" op... */
888 res ^= negate;
889 goto ret;
890 }
891 }
892 if (LONE_CHAR(argv[0], '!')) {
893 argv++;
894 negate ^= 1;
895 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000896 }
897 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000898
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000899 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000900
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000901 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100902 /* Examples:
903 * test 3 -lt 5 6
904 * test -t 1 2
905 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000906 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000907 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000908 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000909 ret:
910 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000911 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000912}