1 /* vi: set sw=4 ts=4: */
2 /*
3  * test implementation for busybox
4  *
5  * Copyright (c) by a whole pile of folks:
6  *
7  *     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.
15  *     modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
16  *
17  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
18  *
19  * Original copyright notice states:
20  *     "This program is in the Public Domain."
21  */
22 //config:config TEST
23 //config:	bool "test (4.1 kb)"
24 //config:	default y
25 //config:	help
26 //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.
29 //config:
30 //config:config TEST1
31 //config:	bool "test as ["
32 //config:	default y
33 //config:	help
34 //config:	Provide test command in the "[ EXPR ]" form
35 //config:
36 //config:config TEST2
37 //config:	bool "test as [["
38 //config:	default y
39 //config:	help
40 //config:	Provide test command in the "[[ EXPR ]]" form
41 //config:
42 //config:config FEATURE_TEST_64
43 //config:	bool "Extend test to 64 bit"
44 //config:	default y
45 //config:	depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
46 //config:	help
47 //config:	Enable 64-bit support in test.
48 
49 //applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
50 //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))
52 
53 //kbuild:lib-$(CONFIG_TEST)  += test.o test_ptr_hack.o
54 //kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
55 //kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
56 
57 //kbuild:lib-$(CONFIG_ASH_TEST)  += test.o test_ptr_hack.o
58 //kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
59 
60 /* "test --help" is special-cased to ignore --help */
61 //usage:#define test_trivial_usage NOUSAGE_STR
62 //usage:#define test_full_usage ""
63 //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 
78 #include "libbb.h"
79 #include <regex.h>
80 #include <fnmatch.h>
81 
82 /* This is a NOFORK applet. Be very careful! */
83 
84 /* test_main() is called from shells, and we need to be extra careful here.
85  * This is true regardless of PREFER_APPLETS and SH_STANDALONE
86  * state. */
87 
88 /* test(1) accepts the following grammar:
89 	oexpr   ::= aexpr | aexpr "-o" oexpr ;
90 	aexpr   ::= nexpr | nexpr "-a" aexpr ;
91 	nexpr   ::= primary | "!" primary
92 	primary ::= unary-operator operand
93 		| operand binary-operator operand
94 		| operand
95 		| "(" oexpr ")"
96 		;
97 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
98 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
99 
100 	binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
101 			"-nt"|"-ot"|"-ef";
102 	operand ::= <any legal UNIX file name>
103 */
104 
105 /* TODO: handle [[ expr ]] bashism bash-compatibly.
106  * [[ ]] is meant to be a "better [ ]", with less weird syntax
107  * and without the risk of variables and quoted strings misinterpreted
108  * as operators.
109  * This will require support from shells - we need to know quote status
110  * of each parameter (see below).
111  *
112  * Word splitting and pathname expansion should NOT be performed:
113  *      # a="a b"; [[ $a = "a b" ]] && echo YES
114  *      YES
115  *      # [[ /bin/m* ]] && echo YES
116  *      YES
117  *
118  * =~ should do regexp match
119  * = and == should do pattern match against right side:
120  *      # [[ *a* == bab ]] && echo YES
121  *      # [[ bab == *a* ]] && echo YES
122  *      YES
123  * != does the negated == (i.e., also with pattern matching).
124  * Pattern matching is quotation-sensitive:
125  *      # [[ bab == "b"a* ]] && echo YES
126  *      YES
127  *      # [[ bab == b"a*" ]] && echo YES
128  *
129  * Conditional operators such as -f must be unquoted literals to be recognized:
130  *      # [[ -e /bin ]] && echo YES
131  *      YES
132  *      # [[ '-e' /bin ]] && echo YES
133  *      bash: conditional binary operator expected...
134  *      # A='-e'; [[ $A /bin ]] && echo YES
135  *      bash: conditional binary operator expected...
136  *
137  * || and && should work as -o and -a work in [ ]
138  * -a and -o aren't recognized (&& and || are to be used instead)
139  * ( and ) do not need to be quoted unlike in [ ]:
140  *      # [[ ( abc ) && '' ]] && echo YES
141  *      # [[ ( abc ) || '' ]] && echo YES
142  *      YES
143  *      # [[ ( abc ) -o '' ]] && echo YES
144  *      bash: syntax error in conditional expression...
145  *
146  * Apart from the above, [[ expr ]] should work as [ expr ]
147  */
148 
149 #define TEST_DEBUG 0
150 
151 #if ENABLE_TEST2 \
152  || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153  || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154 # define BASH_TEST2 1
155 #else
156 # define BASH_TEST2 0
157 #endif
158 
159 enum token {
160 	EOI,
161 
162 	FILRD, /* file access */
163 	FILWR,
164 	FILEX,
165 
166 	FILEXIST,
167 
168 	FILREG, /* file type */
169 	FILDIR,
170 	FILCDEV,
171 	FILBDEV,
172 	FILFIFO,
173 	FILSOCK,
174 
175 	FILSYM,
176 	FILGZ,
177 	FILTT,
178 
179 	FILSUID, /* file bit */
180 	FILSGID,
181 	FILSTCK,
182 
183 	FILNT, /* file ops */
184 	FILOT,
185 	FILEQ,
186 
187 	FILUID,
188 	FILGID,
189 
190 	STREZ, /* str ops */
191 	STRNZ,
192 	STREQ,
193 	STRNE,
194 	STRLT,
195 	STRGT,
196 
197 #if BASH_TEST2
198 	REGEX,
199 #endif
200 
201 	INTEQ, /* int ops */
202 	INTNE,
203 	INTGE,
204 	INTGT,
205 	INTLE,
206 	INTLT,
207 
208 	UNOT,
209 	BAND,
210 	BOR,
211 	LPAREN,
212 	RPAREN,
213 	OPERAND
214 };
215 #define is_int_op(a)      (((unsigned char)((a) - INTEQ)) <= 5)
216 #define is_str_op(a)      (((unsigned char)((a) - STREZ)) <= 5)
217 #define is_file_op(a)     (((unsigned char)((a) - FILNT)) <= 2)
218 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
219 #define is_file_type(a)   (((unsigned char)((a) - FILREG)) <= 5)
220 #define is_file_bit(a)    (((unsigned char)((a) - FILSUID)) <= 2)
221 
222 #if TEST_DEBUG
223 int depth;
224 #define nest_msg(...) do { \
225 	depth++; \
226 	fprintf(stderr, "%*s", depth*2, ""); \
227 	fprintf(stderr, __VA_ARGS__); \
228 } while (0)
229 #define unnest_msg(...) do { \
230 	fprintf(stderr, "%*s", depth*2, ""); \
231 	fprintf(stderr, __VA_ARGS__); \
232 	depth--; \
233 } while (0)
234 #define dbg_msg(...) do { \
235 	fprintf(stderr, "%*s", depth*2, ""); \
236 	fprintf(stderr, __VA_ARGS__); \
237 } while (0)
238 #define unnest_msg_and_return(expr, ...) do { \
239 	number_t __res = (expr); \
240 	fprintf(stderr, "%*s", depth*2, ""); \
241 	fprintf(stderr, __VA_ARGS__, res); \
242 	depth--; \
243 	return __res; \
244 } while (0)
245 static const char *const TOKSTR[] = {
246 	"EOI",
247 	"FILRD",
248 	"FILWR",
249 	"FILEX",
250 	"FILEXIST",
251 	"FILREG",
252 	"FILDIR",
253 	"FILCDEV",
254 	"FILBDEV",
255 	"FILFIFO",
256 	"FILSOCK",
257 	"FILSYM",
258 	"FILGZ",
259 	"FILTT",
260 	"FILSUID",
261 	"FILSGID",
262 	"FILSTCK",
263 	"FILNT",
264 	"FILOT",
265 	"FILEQ",
266 	"FILUID",
267 	"FILGID",
268 	"STREZ",
269 	"STRNZ",
270 	"STREQ",
271 	"STRNE",
272 	"STRLT",
273 	"STRGT",
274 #if BASH_TEST2
275 	"REGEX",
276 #endif
277 	"INTEQ",
278 	"INTNE",
279 	"INTGE",
280 	"INTGT",
281 	"INTLE",
282 	"INTLT",
283 	"UNOT",
284 	"BAND",
285 	"BOR",
286 	"LPAREN",
287 	"RPAREN",
288 	"OPERAND"
289 };
290 #else
291 #define nest_msg(...)   ((void)0)
292 #define unnest_msg(...) ((void)0)
293 #define dbg_msg(...)    ((void)0)
294 #define unnest_msg_and_return(expr, ...) return expr
295 #endif
296 
297 enum {
298 	UNOP,
299 	BINOP,
300 	BUNOP,
301 	BBINOP,
302 	PAREN
303 };
304 
305 struct operator_t {
306 	unsigned char op_num, op_type;
307 };
308 
309 static const struct operator_t ops_table[] ALIGN2 = {
310 	{ /* "-r" */ FILRD   , UNOP   },
311 	{ /* "-w" */ FILWR   , UNOP   },
312 	{ /* "-x" */ FILEX   , UNOP   },
313 	{ /* "-e" */ FILEXIST, UNOP   },
314 	{ /* "-f" */ FILREG  , UNOP   },
315 	{ /* "-d" */ FILDIR  , UNOP   },
316 	{ /* "-c" */ FILCDEV , UNOP   },
317 	{ /* "-b" */ FILBDEV , UNOP   },
318 	{ /* "-p" */ FILFIFO , UNOP   },
319 	{ /* "-u" */ FILSUID , UNOP   },
320 	{ /* "-g" */ FILSGID , UNOP   },
321 	{ /* "-k" */ FILSTCK , UNOP   },
322 	{ /* "-s" */ FILGZ   , UNOP   },
323 	{ /* "-t" */ FILTT   , UNOP   },
324 	{ /* "-z" */ STREZ   , UNOP   },
325 	{ /* "-n" */ STRNZ   , UNOP   },
326 	{ /* "-h" */ FILSYM  , UNOP   },    /* for backwards compat */
327 
328 	{ /* "-O" */ FILUID  , UNOP   },
329 	{ /* "-G" */ FILGID  , UNOP   },
330 	{ /* "-L" */ FILSYM  , UNOP   },
331 	{ /* "-S" */ FILSOCK , UNOP   },
332 	{ /* "="  */ STREQ   , BINOP  },
333 	/* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
334 	 * lists only "=" as comparison operator.
335 	 */
336 	{ /* "==" */ STREQ   , BINOP  },
337 	{ /* "!=" */ STRNE   , BINOP  },
338 	{ /* "<"  */ STRLT   , BINOP  },
339 	{ /* ">"  */ STRGT   , BINOP  },
340 #if BASH_TEST2
341 	{ /* "=~" */ REGEX   , BINOP  },
342 #endif
343 	{ /* "-eq"*/ INTEQ   , BINOP  },
344 	{ /* "-ne"*/ INTNE   , BINOP  },
345 	{ /* "-ge"*/ INTGE   , BINOP  },
346 	{ /* "-gt"*/ INTGT   , BINOP  },
347 	{ /* "-le"*/ INTLE   , BINOP  },
348 	{ /* "-lt"*/ INTLT   , BINOP  },
349 	{ /* "-nt"*/ FILNT   , BINOP  },
350 	{ /* "-ot"*/ FILOT   , BINOP  },
351 	{ /* "-ef"*/ FILEQ   , BINOP  },
352 	{ /* "!"  */ UNOT    , BUNOP  },
353 	{ /* "-a" */ BAND    , BBINOP },
354 	{ /* "-o" */ BOR     , BBINOP },
355 #if BASH_TEST2
356 	{ /* "&&" */ BAND    , BBINOP },
357 	{ /* "||" */ BOR     , BBINOP },
358 #endif
359 	{ /* "("  */ LPAREN  , PAREN  },
360 	{ /* ")"  */ RPAREN  , PAREN  },
361 };
362 /* Please keep these two tables in sync */
363 static const char ops_texts[] ALIGN1 =
364 	"-r"  "\0"
365 	"-w"  "\0"
366 	"-x"  "\0"
367 	"-e"  "\0"
368 	"-f"  "\0"
369 	"-d"  "\0"
370 	"-c"  "\0"
371 	"-b"  "\0"
372 	"-p"  "\0"
373 	"-u"  "\0"
374 	"-g"  "\0"
375 	"-k"  "\0"
376 	"-s"  "\0"
377 	"-t"  "\0"
378 	"-z"  "\0"
379 	"-n"  "\0"
380 	"-h"  "\0"
381 
382 	"-O"  "\0"
383 	"-G"  "\0"
384 	"-L"  "\0"
385 	"-S"  "\0"
386 	"="   "\0"
387 	/* "==" is bashism */
388 	"=="  "\0"
389 	"!="  "\0"
390 	"<"   "\0"
391 	">"   "\0"
392 #if BASH_TEST2
393 	"=~"  "\0"
394 #endif
395 	"-eq" "\0"
396 	"-ne" "\0"
397 	"-ge" "\0"
398 	"-gt" "\0"
399 	"-le" "\0"
400 	"-lt" "\0"
401 	"-nt" "\0"
402 	"-ot" "\0"
403 	"-ef" "\0"
404 	"!"   "\0"
405 	"-a"  "\0"
406 	"-o"  "\0"
407 #if BASH_TEST2
408 	"&&"  "\0"
409 	"||"  "\0"
410 #endif
411 	"("   "\0"
412 	")"   "\0"
413 ;
414 
415 
416 #if ENABLE_FEATURE_TEST_64
417 typedef int64_t number_t;
418 #else
419 typedef int number_t;
420 #endif
421 
422 
423 /* We try to minimize both static and stack usage. */
424 struct test_statics {
425 	char **args;
426 	/* set only by check_operator(), either to bogus struct
427 	 * or points to matching operator_t struct. Never NULL. */
428 	const struct operator_t *last_operator;
429 	gid_t *group_array;
430 	int ngroups;
431 #if BASH_TEST2
432 	bool bash_test2;
433 #endif
434 	jmp_buf leaving;
435 };
436 
437 /* See test_ptr_hack.c */
438 extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
439 
440 #define S (*test_ptr_to_statics)
441 #define args            (S.args         )
442 #define last_operator   (S.last_operator)
443 #define group_array     (S.group_array  )
444 #define ngroups         (S.ngroups      )
445 #define bash_test2      (S.bash_test2   )
446 #define leaving         (S.leaving      )
447 
448 #define INIT_S() do { \
449 	XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
450 } while (0)
451 #define DEINIT_S() do { \
452 	free(group_array); \
453 	free(test_ptr_to_statics); \
454 } while (0)
455 
456 static number_t primary(enum token n);
457 
458 static void syntax(const char *op, const char *msg) NORETURN;
syntax(const char * op,const char * msg)459 static void syntax(const char *op, const char *msg)
460 {
461 	if (op && *op) {
462 		bb_error_msg("%s: %s", op, msg);
463 	} else {
464 		bb_error_msg("%s: %s"+4, msg);
465 	}
466 	longjmp(leaving, 2);
467 }
468 
469 /* atoi with error detection */
470 //XXX: FIXME: duplicate of existing libbb function?
getn(const char * s)471 static number_t getn(const char *s)
472 {
473 	char *p;
474 #if ENABLE_FEATURE_TEST_64
475 	long long r;
476 #else
477 	long r;
478 #endif
479 
480 	errno = 0;
481 #if ENABLE_FEATURE_TEST_64
482 	r = strtoll(s, &p, 10);
483 #else
484 	r = strtol(s, &p, 10);
485 #endif
486 
487 	if (errno != 0)
488 		syntax(s, "out of range");
489 
490 	if (p == s || *(skip_whitespace(p)) != '\0')
491 		syntax(s, "bad number");
492 
493 	return r;
494 }
495 
496 /* UNUSED
497 static int newerf(const char *f1, const char *f2)
498 {
499 	struct stat b1, b2;
500 
501 	return (stat(f1, &b1) == 0 &&
502 			stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
503 }
504 
505 static int olderf(const char *f1, const char *f2)
506 {
507 	struct stat b1, b2;
508 
509 	return (stat(f1, &b1) == 0 &&
510 			stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
511 }
512 
513 static int equalf(const char *f1, const char *f2)
514 {
515 	struct stat b1, b2;
516 
517 	return (stat(f1, &b1) == 0 &&
518 			stat(f2, &b2) == 0 &&
519 			b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
520 }
521 */
522 
523 
check_operator(const char * s)524 static enum token check_operator(const char *s)
525 {
526 	static const struct operator_t no_op = {
527 		.op_num = -1,
528 		.op_type = -1
529 	};
530 	int n;
531 
532 	last_operator = &no_op;
533 	if (s == NULL)
534 		return EOI;
535 	n = index_in_strings(ops_texts, s);
536 	if (n < 0)
537 		return OPERAND;
538 
539 #if BASH_TEST2
540 	if (ops_table[n].op_num == REGEX && !bash_test2) {
541 		/* =~ is only for [[ ]] */
542 		return OPERAND;
543 	}
544 	if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
545 		/* [ ]   accepts -a and -o but not && and || */
546 		/* [[ ]] accepts && and || but not -a and -o */
547 		if (bash_test2 == (s[0] == '-'))
548 			return OPERAND;
549 	}
550 #endif
551 
552 	last_operator = &ops_table[n];
553 	return ops_table[n].op_num;
554 }
555 
556 
binop(void)557 static int binop(void)
558 {
559 	const char *opnd1, *opnd2;
560 	const struct operator_t *op;
561 	number_t val1, val2;
562 
563 	opnd1 = *args;
564 	check_operator(*++args);
565 	op = last_operator;
566 
567 	opnd2 = *++args;
568 	if (opnd2 == NULL)
569 		syntax(args[-1], "argument expected");
570 
571 	if (is_int_op(op->op_num)) {
572 		val1 = getn(opnd1);
573 		val2 = getn(opnd2);
574 		if (op->op_num == INTEQ)
575 			return val1 == val2;
576 		if (op->op_num == INTNE)
577 			return val1 != val2;
578 		if (op->op_num == INTGE)
579 			return val1 >= val2;
580 		if (op->op_num == INTGT)
581 			return val1 >  val2;
582 		if (op->op_num == INTLE)
583 			return val1 <= val2;
584 		/*if (op->op_num == INTLT)*/
585 		return val1 <  val2;
586 	}
587 #if BASH_TEST2
588 	if (bash_test2) {
589 		if (op->op_num == STREQ) {
590 			val1 = fnmatch(opnd2, opnd1, 0);
591 			return val1 == 0;
592 		}
593 		if (op->op_num == STRNE) {
594 			val1 = fnmatch(opnd2, opnd1, 0);
595 			return val1 != 0;
596 		}
597 		if (op->op_num == REGEX) {
598 			regex_t re_buffer;
599 			memset(&re_buffer, 0, sizeof(re_buffer));
600 			if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
601 				/* Bad regex */
602 				longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
603 			}
604 			val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
605 			regfree(&re_buffer);
606 			return val1 == 0;
607 		}
608 	}
609 #endif
610 	if (is_str_op(op->op_num)) {
611 		val1 = strcmp(opnd1, opnd2);
612 		if (op->op_num == STREQ)
613 			return val1 == 0;
614 		if (op->op_num == STRNE)
615 			return val1 != 0;
616 		if (op->op_num == STRLT)
617 			return val1 < 0;
618 		/*if (op->op_num == STRGT)*/
619 		return val1 > 0;
620 	}
621 	/* We are sure that these three are by now the only binops we didn't check
622 	 * yet, so we do not check if the class is correct:
623 	 */
624 /*	if (is_file_op(op->op_num)) */
625 	{
626 		struct stat b1, b2;
627 
628 		if (stat(opnd1, &b1) || stat(opnd2, &b2))
629 			return 0; /* false, since at least one stat failed */
630 		if (op->op_num == FILNT)
631 			return b1.st_mtime > b2.st_mtime;
632 		if (op->op_num == FILOT)
633 			return b1.st_mtime < b2.st_mtime;
634 		/*if (op->op_num == FILEQ)*/
635 		return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
636 	}
637 	/*return 1; - NOTREACHED */
638 }
639 
initialize_group_array(void)640 static void initialize_group_array(void)
641 {
642 	group_array = bb_getgroups(&ngroups, NULL);
643 }
644 
645 /* Return non-zero if GID is one that we have in our groups list. */
646 //XXX: FIXME: duplicate of existing libbb function?
647 // see toplevel TODO file:
648 // possible code duplication ingroup() and is_a_group_member()
is_a_group_member(gid_t gid)649 static int is_a_group_member(gid_t gid)
650 {
651 	int i;
652 
653 	/* Short-circuit if possible, maybe saving a call to getgroups(). */
654 	if (gid == getgid() || gid == getegid())
655 		return 1;
656 
657 	if (ngroups == 0)
658 		initialize_group_array();
659 
660 	/* Search through the list looking for GID. */
661 	for (i = 0; i < ngroups; i++)
662 		if (gid == group_array[i])
663 			return 1;
664 
665 	return 0;
666 }
667 
668 
669 /* Do the same thing access(2) does, but use the effective uid and gid,
670    and don't make the mistake of telling root that any file is
671    executable. */
test_eaccess(struct stat * st,int mode)672 static int test_eaccess(struct stat *st, int mode)
673 {
674 	unsigned int euid = geteuid();
675 
676 	if (euid == 0) {
677 		/* Root can read or write any file. */
678 		if (mode != X_OK)
679 			return 0;
680 
681 		/* Root can execute any file that has any one of the execute
682 		 * bits set. */
683 		if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
684 			return 0;
685 	}
686 
687 	if (st->st_uid == euid)  /* owner */
688 		mode <<= 6;
689 	else if (is_a_group_member(st->st_gid))
690 		mode <<= 3;
691 
692 	if (st->st_mode & mode)
693 		return 0;
694 
695 	return -1;
696 }
697 
698 
filstat(char * nm,enum token mode)699 static int filstat(char *nm, enum token mode)
700 {
701 	struct stat s;
702 	unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
703 
704 	if (mode == FILSYM) {
705 #ifdef S_IFLNK
706 		if (lstat(nm, &s) == 0) {
707 			i = S_IFLNK;
708 			goto filetype;
709 		}
710 #endif
711 		return 0;
712 	}
713 
714 	if (stat(nm, &s) != 0)
715 		return 0;
716 	if (mode == FILEXIST)
717 		return 1;
718 	if (is_file_access(mode)) {
719 		if (mode == FILRD)
720 			i = R_OK;
721 		if (mode == FILWR)
722 			i = W_OK;
723 		if (mode == FILEX)
724 			i = X_OK;
725 		return test_eaccess(&s, i) == 0;
726 	}
727 	if (is_file_type(mode)) {
728 		if (mode == FILREG)
729 			i = S_IFREG;
730 		if (mode == FILDIR)
731 			i = S_IFDIR;
732 		if (mode == FILCDEV)
733 			i = S_IFCHR;
734 		if (mode == FILBDEV)
735 			i = S_IFBLK;
736 		if (mode == FILFIFO) {
737 #ifdef S_IFIFO
738 			i = S_IFIFO;
739 #else
740 			return 0;
741 #endif
742 		}
743 		if (mode == FILSOCK) {
744 #ifdef S_IFSOCK
745 			i = S_IFSOCK;
746 #else
747 			return 0;
748 #endif
749 		}
750  filetype:
751 		return ((s.st_mode & S_IFMT) == i);
752 	}
753 	if (is_file_bit(mode)) {
754 		if (mode == FILSUID)
755 			i = S_ISUID;
756 		if (mode == FILSGID)
757 			i = S_ISGID;
758 		if (mode == FILSTCK)
759 			i = S_ISVTX;
760 		return ((s.st_mode & i) != 0);
761 	}
762 	if (mode == FILGZ)
763 		return s.st_size > 0L;
764 	if (mode == FILUID)
765 		return s.st_uid == geteuid();
766 	if (mode == FILGID)
767 		return s.st_gid == getegid();
768 	return 1; /* NOTREACHED */
769 }
770 
771 
nexpr(enum token n)772 static number_t nexpr(enum token n)
773 {
774 	number_t res;
775 
776 	nest_msg(">nexpr(%s)\n", TOKSTR[n]);
777 	if (n == UNOT) {
778 		n = check_operator(*++args);
779 		if (n == EOI) {
780 			/* special case: [ ! ], [ a -a ! ] are valid */
781 			/* IOW, "! ARG" may miss ARG */
782 			args--;
783 			unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
784 			return 1;
785 		}
786 		res = !nexpr(n);
787 		unnest_msg("<nexpr:%lld\n", res);
788 		return res;
789 	}
790 	res = primary(n);
791 	unnest_msg("<nexpr:%lld\n", res);
792 	return res;
793 }
794 
795 
aexpr(enum token n)796 static number_t aexpr(enum token n)
797 {
798 	number_t res;
799 
800 	nest_msg(">aexpr(%s)\n", TOKSTR[n]);
801 	res = nexpr(n);
802 	dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
803 	if (check_operator(*++args) == BAND) {
804 		dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
805 		res = aexpr(check_operator(*++args)) && res;
806 		unnest_msg("<aexpr:%lld\n", res);
807 		return res;
808 	}
809 	args--;
810 	unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
811 	return res;
812 }
813 
814 
oexpr(enum token n)815 static number_t oexpr(enum token n)
816 {
817 	number_t res;
818 
819 	nest_msg(">oexpr(%s)\n", TOKSTR[n]);
820 	res = aexpr(n);
821 	dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
822 	if (check_operator(*++args) == BOR) {
823 		dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
824 		res = oexpr(check_operator(*++args)) || res;
825 		unnest_msg("<oexpr:%lld\n", res);
826 		return res;
827 	}
828 	args--;
829 	unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
830 	return res;
831 }
832 
833 
primary(enum token n)834 static number_t primary(enum token n)
835 {
836 #if TEST_DEBUG
837 	number_t res = res; /* for compiler */
838 #else
839 	number_t res;
840 #endif
841 	const struct operator_t *args0_op;
842 
843 	nest_msg(">primary(%s)\n", TOKSTR[n]);
844 	if (n == EOI) {
845 		syntax(NULL, "argument expected");
846 	}
847 	if (n == LPAREN) {
848 		res = oexpr(check_operator(*++args));
849 		if (check_operator(*++args) != RPAREN)
850 			syntax(NULL, "closing paren expected");
851 		unnest_msg("<primary:%lld\n", res);
852 		return res;
853 	}
854 
855 	/* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
856 	 * do the same */
857 	args0_op = last_operator;
858 	/* last_operator = operator at args[1] */
859 	if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
860 		if (args[2]) {
861 			// coreutils also does this:
862 			// if (args[3] && args[0]="-l" && args[2] is BINOP)
863 			//	return binop(1 /* prepended by -l */);
864 			if (last_operator->op_type == BINOP)
865 				unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
866 		}
867 	}
868 	/* check "is args[0] unop?" second */
869 	if (args0_op->op_type == UNOP) {
870 		/* unary expression */
871 		if (args[1] == NULL)
872 //			syntax(args0_op->op_text, "argument expected");
873 			goto check_emptiness;
874 		args++;
875 		if (n == STREZ)
876 			unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
877 		if (n == STRNZ)
878 			unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
879 		if (n == FILTT)
880 			unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
881 		unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
882 	}
883 
884 	/*check_operator(args[1]); - already done */
885 	if (last_operator->op_type == BINOP) {
886 		/* args[2] is known to be NULL, isn't it bound to fail? */
887 		unnest_msg_and_return(binop(), "<primary:%lld\n");
888 	}
889  check_emptiness:
890 	unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
891 }
892 
893 
test_main(int argc,char ** argv)894 int test_main(int argc, char **argv)
895 {
896 	int res;
897 	const char *arg0;
898 #if BASH_TEST2
899 	bool bt2 = 0;
900 #endif
901 
902 	arg0 = bb_basename(argv[0]);
903 	if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
904 	 && (arg0[0] == '[')
905 	) {
906 		--argc;
907 		if (!arg0[1]) { /* "[" ? */
908 			if (NOT_LONE_CHAR(argv[argc], ']')) {
909 				bb_simple_error_msg("missing ]");
910 				return 2;
911 			}
912 		} else { /* assuming "[[" */
913 			if (strcmp(argv[argc], "]]") != 0) {
914 				bb_simple_error_msg("missing ]]");
915 				return 2;
916 			}
917 #if BASH_TEST2
918 			bt2 = 1;
919 #endif
920 		}
921 		argv[argc] = NULL;
922 	}
923 	/* argc is unused after this point */
924 
925 	/* We must do DEINIT_S() prior to returning */
926 	INIT_S();
927 
928 #if BASH_TEST2
929 	bash_test2 = bt2;
930 #endif
931 
932 	res = setjmp(leaving);
933 	if (res)
934 		goto ret;
935 
936 	/* resetting ngroups is probably unnecessary.  it will
937 	 * force a new call to getgroups(), which prevents using
938 	 * group data fetched during a previous call.  but the
939 	 * only way the group data could be stale is if there's
940 	 * been an intervening call to setgroups(), and this
941 	 * isn't likely in the case of a shell.  paranoia
942 	 * prevails...
943 	 */
944 	/*ngroups = 0; - done by INIT_S() */
945 
946 	argv++;
947 	args = argv;
948 
949 	/* Implement special cases from POSIX.2, section 4.62.4.
950 	 * Testcase: "test '(' = '('"
951 	 * The general parser would misinterpret '(' as group start.
952 	 */
953 	if (1) {
954 		int negate = 0;
955  again:
956 		if (!argv[0]) {
957 			/* "test" */
958 			res = 1;
959 			goto ret_special;
960 		}
961 		if (!argv[1]) {
962 			/* "test [!] arg" */
963 			res = (argv[0][0] == '\0');
964 			goto ret_special;
965 		}
966 		if (argv[2]) {
967 			if (!argv[3]) {
968 				/*
969 				 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
970 				 * """ 3 arguments:
971 				 * If $2 is a binary primary, perform the binary test of $1 and $3.
972 				 * """
973 				 */
974 				check_operator(argv[1]);
975 				if (last_operator->op_type == BINOP) {
976 					/* "test [!] arg1 <binary_op> arg2" */
977 					args = argv;
978 					res = (binop() == 0);
979  ret_special:
980 					/* If there was leading "!" op... */
981 					res ^= negate;
982 					goto ret;
983 				}
984 				/* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
985 				 * Looks like this works without additional coding.
986 				 */
987 				goto check_negate;
988 			}
989 			/* argv[3] exists (at least 4 args), is it exactly 4 args? */
990 			if (!argv[4]) {
991 				/*
992 				 * """ 4 arguments:
993 				 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
994 				 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
995 				 * """
996 				 * Example why code below is necessary: test '(' ! -e ')'
997 				 */
998 				if (LONE_CHAR(argv[0], '(')
999 				 && LONE_CHAR(argv[3], ')')
1000 				) {
1001 					/* "test [!] ( x y )" */
1002 					argv[3] = NULL;
1003 					argv++;
1004 				}
1005 			}
1006 		}
1007  check_negate:
1008 		if (LONE_CHAR(argv[0], '!')) {
1009 			argv++;
1010 			negate ^= 1;
1011 			goto again;
1012 		}
1013 	}
1014 
1015 	res = !oexpr(check_operator(*args));
1016 
1017 	if (*args != NULL && *++args != NULL) {
1018 		/* Examples:
1019 		 * test 3 -lt 5 6
1020 		 * test -t 1 2
1021 		 */
1022 		bb_error_msg("%s: unknown operand", *args);
1023 		res = 2;
1024 	}
1025  ret:
1026 	DEINIT_S();
1027 	return res;
1028 }
1029