source: MondoRescue/branches/3.3/mindi-busybox/coreutils/test.c

Last change on this file was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 20.3 KB
Line 
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
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"
29//config: default y
30//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"
37//config: default y
38//config: depends on TEST || ASH_BUILTIN_TEST || HUSH
39//config: help
40//config: Enable 64-bit support in test.
41
42/* "test --help" is special-cased to ignore --help */
43//usage:#define test_trivial_usage NOUSAGE_STR
44//usage:#define test_full_usage ""
45//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
60#include "libbb.h"
61#include <setjmp.h>
62
63/* This is a NOFORK applet. Be very careful! */
64
65/* test_main() is called from shells, and we need to be extra careful here.
66 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
67 * state. */
68
69/* test(1) accepts the following grammar:
70 oexpr ::= aexpr | aexpr "-o" oexpr ;
71 aexpr ::= nexpr | nexpr "-a" aexpr ;
72 nexpr ::= primary | "!" primary
73 primary ::= unary-operator operand
74 | 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
81 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
82 "-nt"|"-ot"|"-ef";
83 operand ::= <any legal UNIX file name>
84*/
85
86/* 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
130#define TEST_DEBUG 0
131
132enum token {
133 EOI,
134
135 FILRD, /* file access */
136 FILWR,
137 FILEX,
138
139 FILEXIST,
140
141 FILREG, /* file type */
142 FILDIR,
143 FILCDEV,
144 FILBDEV,
145 FILFIFO,
146 FILSOCK,
147
148 FILSYM,
149 FILGZ,
150 FILTT,
151
152 FILSUID, /* file bit */
153 FILSGID,
154 FILSTCK,
155
156 FILNT, /* file ops */
157 FILOT,
158 FILEQ,
159
160 FILUID,
161 FILGID,
162
163 STREZ, /* str ops */
164 STRNZ,
165 STREQ,
166 STRNE,
167 STRLT,
168 STRGT,
169
170 INTEQ, /* int ops */
171 INTNE,
172 INTGE,
173 INTGT,
174 INTLE,
175 INTLT,
176
177 UNOT,
178 BAND,
179 BOR,
180 LPAREN,
181 RPAREN,
182 OPERAND
183};
184#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)
187#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
188#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
189#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
190
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
263enum {
264 UNOP,
265 BINOP,
266 BUNOP,
267 BBINOP,
268 PAREN
269};
270
271struct operator_t {
272 unsigned char op_num, op_type;
273};
274
275static 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 */
293
294 { /* "-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 },
317};
318/* 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;
362
363
364#if ENABLE_FEATURE_TEST_64
365typedef int64_t number_t;
366#else
367typedef int number_t;
368#endif
369
370
371/* We try to minimize both static and stack usage. */
372struct test_statics {
373 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;
377 gid_t *group_array;
378 int ngroups;
379 jmp_buf leaving;
380};
381
382/* See test_ptr_hack.c */
383extern struct test_statics *const test_ptr_to_statics;
384
385#define S (*test_ptr_to_statics)
386#define args (S.args )
387#define last_operator (S.last_operator)
388#define group_array (S.group_array )
389#define ngroups (S.ngroups )
390#define leaving (S.leaving )
391
392#define INIT_S() do { \
393 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
394 barrier(); \
395} while (0)
396#define DEINIT_S() do { \
397 free(test_ptr_to_statics); \
398} while (0)
399
400static number_t primary(enum token n);
401
402static void syntax(const char *op, const char *msg) NORETURN;
403static void syntax(const char *op, const char *msg)
404{
405 if (op && *op) {
406 bb_error_msg("%s: %s", op, msg);
407 } else {
408 bb_error_msg("%s: %s"+4, msg);
409 }
410 longjmp(leaving, 2);
411}
412
413/* atoi with error detection */
414//XXX: FIXME: duplicate of existing libbb function?
415static number_t getn(const char *s)
416{
417 char *p;
418#if ENABLE_FEATURE_TEST_64
419 long long r;
420#else
421 long r;
422#endif
423
424 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
434 if (p == s || *(skip_whitespace(p)) != '\0')
435 syntax(s, "bad number");
436
437 return r;
438}
439
440/* UNUSED
441static int newerf(const char *f1, const char *f2)
442{
443 struct stat b1, b2;
444
445 return (stat(f1, &b1) == 0 &&
446 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
447}
448
449static int olderf(const char *f1, const char *f2)
450{
451 struct stat b1, b2;
452
453 return (stat(f1, &b1) == 0 &&
454 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
455}
456
457static int equalf(const char *f1, const char *f2)
458{
459 struct stat b1, b2;
460
461 return (stat(f1, &b1) == 0 &&
462 stat(f2, &b2) == 0 &&
463 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
464}
465*/
466
467
468static enum token check_operator(const char *s)
469{
470 static const struct operator_t no_op = {
471 .op_num = -1,
472 .op_type = -1
473 };
474 int n;
475
476 last_operator = &no_op;
477 if (s == NULL)
478 return EOI;
479 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;
484}
485
486
487static int binop(void)
488{
489 const char *opnd1, *opnd2;
490 const struct operator_t *op;
491 number_t val1, val2;
492
493 opnd1 = *args;
494 check_operator(*++args);
495 op = last_operator;
496
497 opnd2 = *++args;
498 if (opnd2 == NULL)
499 syntax(args[-1], "argument expected");
500
501 if (is_int_op(op->op_num)) {
502 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;
514 /*if (op->op_num == INTLT)*/
515 return val1 < val2;
516 }
517 if (is_str_op(op->op_num)) {
518 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;
525 /*if (op->op_num == STRGT)*/
526 return val1 > 0;
527 }
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 */
531/* if (is_file_op(op->op_num)) */
532 {
533 struct stat b1, b2;
534
535 if (stat(opnd1, &b1) || stat(opnd2, &b2))
536 return 0; /* false, since at least one stat failed */
537 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;
541 /*if (op->op_num == FILEQ)*/
542 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
543 }
544 /*return 1; - NOTREACHED */
545}
546
547
548static void initialize_group_array(void)
549{
550 int n;
551
552 /* getgroups may be expensive, try to use it only once */
553 ngroups = 32;
554 do {
555 /* 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) */
560 n = ngroups;
561 group_array = xrealloc(group_array, n * sizeof(gid_t));
562 ngroups = getgroups(n, group_array);
563 } while (ngroups > n);
564}
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
608 * bits set. */
609 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
625static int filstat(char *nm, enum token mode)
626{
627 struct stat s;
628 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
629
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
640 if (stat(nm, &s) != 0)
641 return 0;
642 if (mode == FILEXIST)
643 return 1;
644 if (is_file_access(mode)) {
645 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;
652 }
653 if (is_file_type(mode)) {
654 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 }
676 filetype:
677 return ((s.st_mode & S_IFMT) == i);
678 }
679 if (is_file_bit(mode)) {
680 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 }
688 if (mode == FILGZ)
689 return s.st_size > 0L;
690 if (mode == FILUID)
691 return s.st_uid == geteuid();
692 if (mode == FILGID)
693 return s.st_gid == getegid();
694 return 1; /* NOTREACHED */
695}
696
697
698static number_t nexpr(enum token n)
699{
700 number_t res;
701
702 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
703 if (n == UNOT) {
704 n = check_operator(*++args);
705 if (n == EOI) {
706 /* special case: [ ! ], [ a -a ! ] are valid */
707 /* IOW, "! ARG" may miss ARG */
708 args--;
709 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
710 return 1;
711 }
712 res = !nexpr(n);
713 unnest_msg("<nexpr:%lld\n", res);
714 return res;
715 }
716 res = primary(n);
717 unnest_msg("<nexpr:%lld\n", res);
718 return res;
719}
720
721
722static number_t aexpr(enum token n)
723{
724 number_t res;
725
726 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
727 res = nexpr(n);
728 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
729 if (check_operator(*++args) == BAND) {
730 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
731 res = aexpr(check_operator(*++args)) && res;
732 unnest_msg("<aexpr:%lld\n", res);
733 return res;
734 }
735 args--;
736 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
737 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);
747 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
748 if (check_operator(*++args) == BOR) {
749 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
750 res = oexpr(check_operator(*++args)) || res;
751 unnest_msg("<oexpr:%lld\n", res);
752 return res;
753 }
754 args--;
755 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
756 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]);
770 if (n == EOI) {
771 syntax(NULL, "argument expected");
772 }
773 if (n == LPAREN) {
774 res = oexpr(check_operator(*++args));
775 if (check_operator(*++args) != RPAREN)
776 syntax(NULL, "closing paren expected");
777 unnest_msg("<primary:%lld\n", res);
778 return res;
779 }
780
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) {
796 /* unary expression */
797 if (args[1] == NULL)
798// syntax(args0_op->op_text, "argument expected");
799 goto check_emptiness;
800 args++;
801 if (n == STREZ)
802 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
803 if (n == STRNZ)
804 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
805 if (n == FILTT)
806 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);
808 }
809
810 /*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");
814 }
815 check_emptiness:
816 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
817}
818
819
820int test_main(int argc, char **argv)
821{
822 int res;
823 const char *arg0;
824
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 }
838 }
839 argv[argc] = NULL;
840 }
841 /* argc is unused after this point */
842
843 /* We must do DEINIT_S() prior to returning */
844 INIT_S();
845
846 res = setjmp(leaving);
847 if (res)
848 goto ret;
849
850 /* 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 */
858 /*ngroups = 0; - done by INIT_S() */
859
860 argv++;
861 args = argv;
862
863 /* 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;
896 }
897 }
898
899 res = !oexpr(check_operator(*args));
900
901 if (*args != NULL && *++args != NULL) {
902 /* Examples:
903 * test 3 -lt 5 6
904 * test -t 1 2
905 */
906 bb_error_msg("%s: unknown operand", *args);
907 res = 2;
908 }
909 ret:
910 DEINIT_S();
911 return res;
912}
Note: See TracBrowser for help on using the repository browser.