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