Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/coreutils/test.c

Parent Directory Parent Directory | Revision Log Revision Log


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