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