source: MondoRescue/branches/3.2/mindi-busybox/coreutils/test.c@ 3232

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