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