blob: dbfe4af6933e726da68911d2292d6531fb1fc42b [file] [log] [blame]
Erik Andersen13456d12000-03-16 08:09:57 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen5e1189e2000-04-15 16:34:54 +00003 * test implementation for busybox
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (c) by a whole pile of folks:
Erik Andersen13456d12000-03-16 08:09:57 +00006 *
Paul Fox6ab03782006-06-08 21:37:26 +00007 * 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.
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +000015 * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty).
Erik Andersen13456d12000-03-16 08:09:57 +000016 *
Paul Fox6ab03782006-06-08 21:37:26 +000017 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Erik Andersen13456d12000-03-16 08:09:57 +000018 *
19 * Original copyright notice states:
Paul Fox6ab03782006-06-08 21:37:26 +000020 * "This program is in the Public Domain."
Erik Andersen13456d12000-03-16 08:09:57 +000021 */
22
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000023#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000024#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000025
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000026/* 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. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000031
32
Erik Andersen13456d12000-03-16 08:09:57 +000033/* test(1) accepts the following grammar:
34 oexpr ::= aexpr | aexpr "-o" oexpr ;
35 aexpr ::= nexpr | nexpr "-a" aexpr ;
36 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000037 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000038 | 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
Mike Frysinger75ac42b2005-04-14 02:49:22 +000045 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000046 "-nt"|"-ot"|"-ef";
47 operand ::= <any legal UNIX file name>
48*/
49
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +000050#define TEST_DEBUG 0
51
Erik Andersen13456d12000-03-16 08:09:57 +000052enum 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};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000094#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)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +000097#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000098#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
99#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000100
101#if TEST_DEBUG
102int 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)
124static 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
Erik Andersen13456d12000-03-16 08:09:57 +0000173enum token_types {
174 UNOP,
175 BINOP,
176 BUNOP,
177 BBINOP,
178 PAREN
179};
180
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000181struct operator_t {
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000182 char op_text[4];
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000183 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000184};
185
186static const struct operator_t ops[] = {
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000187 { "-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 },
Erik Andersen13456d12000-03-16 08:09:57 +0000228};
229
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000230
231#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000232typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000233#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000234typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000235#endif
236
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000237
238/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000239struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000240 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;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000244 gid_t *group_array;
245 int ngroups;
246 jmp_buf leaving;
247};
248
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000249/* See test_ptr_hack.c */
250extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000251
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000252#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000253#define args (S.args )
254#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000255#define group_array (S.group_array )
256#define ngroups (S.ngroups )
257#define leaving (S.leaving )
258
259#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000260 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000261 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000262} while (0)
263#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000264 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000265} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000266
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000267static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000268
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000269static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000270static void syntax(const char *op, const char *msg)
271{
272 if (op && *op) {
273 bb_error_msg("%s: %s", op, msg);
274 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000275 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000276 }
277 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000278}
279
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000280/* atoi with error detection */
281//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000282static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000283{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000284 char *p;
285#if ENABLE_FEATURE_TEST_64
286 long long r;
287#else
288 long r;
289#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000290
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000291 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;
Erik Andersen13456d12000-03-16 08:09:57 +0000305}
306
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000307/* UNUSED
308static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000309{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000310 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000311
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000312 return (stat(f1, &b1) == 0 &&
313 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000314}
315
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000316static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000317{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000318 struct stat b1, b2;
319
320 return (stat(f1, &b1) == 0 &&
321 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000322}
323
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000324static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000325{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000326 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000327
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000328 return (stat(f1, &b1) == 0 &&
329 stat(f2, &b2) == 0 &&
330 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000331}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000332*/
333
334
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000335static enum token check_operator(char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000336{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000337 static const struct operator_t no_op = {
338 .op_num = -1,
339 .op_type = -1
340 };
341 const struct operator_t *op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000342
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000343 last_operator = &no_op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000344 if (s == NULL) {
345 return EOI;
346 }
347
348 op = ops;
349 do {
350 if (strcmp(s, op->op_text) == 0) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000351 last_operator = op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000352 return op->op_num;
353 }
354 op++;
355 } while (op < ops + ARRAY_SIZE(ops));
356
357 return OPERAND;
358}
359
Erik Andersen13456d12000-03-16 08:09:57 +0000360
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000361static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000362{
363 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000364 const struct operator_t *op;
365 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000366
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000367 opnd1 = *args;
368 check_operator(*++args);
369 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000370
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000371 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000372 if (opnd2 == NULL)
Paul Fox6ab03782006-06-08 21:37:26 +0000373 syntax(op->op_text, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000374
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000375 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000376 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;
Erik Andersen13456d12000-03-16 08:09:57 +0000390 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000391 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000392 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 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000405/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000406 {
407 struct stat b1, b2;
408
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000409 if (stat(opnd1, &b1) || stat(opnd2, &b2))
410 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000411 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 */
Erik Andersen13456d12000-03-16 08:09:57 +0000419}
420
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000421
422static 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()
441static 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. */
464static 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
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000495static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000496{
497 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000498 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000499
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
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000510 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000511 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000512 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000513 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000514 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000515 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;
Erik Andersen13456d12000-03-16 08:09:57 +0000522 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000523 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000524 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 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000546 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000547 return ((s.st_mode & S_IFMT) == i);
548 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000549 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000550 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 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000558 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000559 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000560 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000561 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000562 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000563 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000564 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000565}
566
Erik Andersen13456d12000-03-16 08:09:57 +0000567
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000568static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000569{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000570 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000571
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000572 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;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000577 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000578 res = primary(n);
579 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000580 return res;
581}
582
583
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000584static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000585{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000586 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000587
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000588 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
603static 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
622static 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]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000632 if (n == EOI) {
633 syntax(NULL, "argument expected");
634 }
635 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000636 res = oexpr(check_operator(*++args));
637 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000638 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000639 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000640 return res;
641 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000642
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) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000658 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000659 if (args[1] == NULL)
660// syntax(args0_op->op_text, "argument expected");
661 goto check_emptiness;
662 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000663 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000664 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000665 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000666 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000667 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000668 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);
Erik Andersen13456d12000-03-16 08:09:57 +0000670 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000671
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000672 /*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");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000676 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000677 check_emptiness:
678 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000679}
680
681
682int test_main(int argc, char **argv)
683{
684 int res;
685 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000686// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000687
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 }
Erik Andersen13456d12000-03-16 08:09:57 +0000701 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000702 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000703 }
704
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000705 /* We must do DEINIT_S() prior to returning */
706 INIT_S();
707
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000708 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000709 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000710 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000711
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000712 /* 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;
Erik Andersen13456d12000-03-16 08:09:57 +0000721
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000722 //argc--;
723 argv++;
724
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000725 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000726 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000727 res = 1;
728 goto ret;
729 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000730#if 0
731// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000732 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');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000739 goto ret;
740 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000741 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000742 check_operator(argv[1]);
743 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000744 /* "test [!] arg1 <binary_op> arg2" */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000745 args = &argv[0];
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000746 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000747 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000748 }
749 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000750
751 /* Some complex expression. Undo '!' removal */
752 if (negate) {
753 negate = 0;
754 //argc++;
755 argv--;
756 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000757#endif
758 args = &argv[0];
759 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000760
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000761 if (*args != NULL && *++args != NULL) {
762 /* TODO: example when this happens? */
763 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000764 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000765 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000766 ret:
767 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000768// return negate ? !res : res;
769 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000770}