blob: 73048d311de8d23297b1b9aa569c14c29629af8f [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-Fischer6c4dade2008-09-25 12:13:34 +000015 * modified by Bernhard Reutner-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 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000022#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000023#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000024
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000025/* 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. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000030
Erik Andersen13456d12000-03-16 08:09:57 +000031/* test(1) accepts the following grammar:
32 oexpr ::= aexpr | aexpr "-o" oexpr ;
33 aexpr ::= nexpr | nexpr "-a" aexpr ;
34 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000035 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000036 | 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
Mike Frysinger75ac42b2005-04-14 02:49:22 +000043 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000044 "-nt"|"-ot"|"-ef";
45 operand ::= <any legal UNIX file name>
46*/
47
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020048/* 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
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +000092#define TEST_DEBUG 0
93
Erik Andersen13456d12000-03-16 08:09:57 +000094enum token {
95 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +020096
97 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +000098 FILWR,
99 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200100
Erik Andersen13456d12000-03-16 08:09:57 +0000101 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200102
103 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000104 FILDIR,
105 FILCDEV,
106 FILBDEV,
107 FILFIFO,
108 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200109
Erik Andersen13456d12000-03-16 08:09:57 +0000110 FILSYM,
111 FILGZ,
112 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200113
114 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000115 FILSGID,
116 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200117
118 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000119 FILOT,
120 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200121
Erik Andersen13456d12000-03-16 08:09:57 +0000122 FILUID,
123 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200124
125 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000126 STRNZ,
127 STREQ,
128 STRNE,
129 STRLT,
130 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200131
132 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000133 INTNE,
134 INTGE,
135 INTGT,
136 INTLE,
137 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200138
Erik Andersen13456d12000-03-16 08:09:57 +0000139 UNOT,
140 BAND,
141 BOR,
142 LPAREN,
143 RPAREN,
144 OPERAND
145};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000146#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)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000149#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000150#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
151#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000152
153#if TEST_DEBUG
154int 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)
176static 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
Erik Andersen13456d12000-03-16 08:09:57 +0000225enum token_types {
226 UNOP,
227 BINOP,
228 BUNOP,
229 BBINOP,
230 PAREN
231};
232
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000233struct operator_t {
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000234 char op_text[4];
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000235 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000236};
237
238static const struct operator_t ops[] = {
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000239 { "-r", FILRD , UNOP },
240 { "-w", FILWR , UNOP },
241 { "-x", FILEX , UNOP },
242 { "-e", FILEXIST, UNOP },
243 { "-f", FILREG , UNOP },
244 { "-d", FILDIR , UNOP },
245 { "-c", FILCDEV , UNOP },
246 { "-b", FILBDEV , UNOP },
247 { "-p", FILFIFO , UNOP },
248 { "-u", FILSUID , UNOP },
249 { "-g", FILSGID , UNOP },
250 { "-k", FILSTCK , UNOP },
251 { "-s", FILGZ , UNOP },
252 { "-t", FILTT , UNOP },
253 { "-z", STREZ , UNOP },
254 { "-n", STRNZ , UNOP },
255 { "-h", FILSYM , UNOP }, /* for backwards compat */
256
257 { "-O" , FILUID , UNOP },
258 { "-G" , FILGID , UNOP },
259 { "-L" , FILSYM , UNOP },
260 { "-S" , FILSOCK, UNOP },
261 { "=" , STREQ , BINOP },
262 { "==" , STREQ , BINOP },
263 { "!=" , STRNE , BINOP },
264 { "<" , STRLT , BINOP },
265 { ">" , STRGT , BINOP },
266 { "-eq", INTEQ , BINOP },
267 { "-ne", INTNE , BINOP },
268 { "-ge", INTGE , BINOP },
269 { "-gt", INTGT , BINOP },
270 { "-le", INTLE , BINOP },
271 { "-lt", INTLT , BINOP },
272 { "-nt", FILNT , BINOP },
273 { "-ot", FILOT , BINOP },
274 { "-ef", FILEQ , BINOP },
275 { "!" , UNOT , BUNOP },
276 { "-a" , BAND , BBINOP },
277 { "-o" , BOR , BBINOP },
278 { "(" , LPAREN , PAREN },
279 { ")" , RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000280};
281
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000282
283#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000284typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000285#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000286typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000287#endif
288
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000289
290/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000291struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000292 char **args;
293 /* set only by check_operator(), either to bogus struct
294 * or points to matching operator_t struct. Never NULL. */
295 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000296 gid_t *group_array;
297 int ngroups;
298 jmp_buf leaving;
299};
300
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000301/* See test_ptr_hack.c */
302extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000303
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000304#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000305#define args (S.args )
306#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000307#define group_array (S.group_array )
308#define ngroups (S.ngroups )
309#define leaving (S.leaving )
310
311#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000312 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000313 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000314} while (0)
315#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000316 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000317} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000318
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000319static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000320
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000321static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000322static void syntax(const char *op, const char *msg)
323{
324 if (op && *op) {
325 bb_error_msg("%s: %s", op, msg);
326 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000327 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000328 }
329 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000330}
331
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000332/* atoi with error detection */
333//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000334static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000335{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000336 char *p;
337#if ENABLE_FEATURE_TEST_64
338 long long r;
339#else
340 long r;
341#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000342
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000343 errno = 0;
344#if ENABLE_FEATURE_TEST_64
345 r = strtoll(s, &p, 10);
346#else
347 r = strtol(s, &p, 10);
348#endif
349
350 if (errno != 0)
351 syntax(s, "out of range");
352
353 if (*(skip_whitespace(p)))
354 syntax(s, "bad number");
355
356 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000357}
358
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000359/* UNUSED
360static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000361{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000362 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000363
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000364 return (stat(f1, &b1) == 0 &&
365 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000366}
367
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000368static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000369{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000370 struct stat b1, b2;
371
372 return (stat(f1, &b1) == 0 &&
373 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000374}
375
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000376static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000377{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000378 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000379
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000380 return (stat(f1, &b1) == 0 &&
381 stat(f2, &b2) == 0 &&
382 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000383}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000384*/
385
386
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000387static enum token check_operator(char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000388{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000389 static const struct operator_t no_op = {
390 .op_num = -1,
391 .op_type = -1
392 };
393 const struct operator_t *op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000394
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000395 last_operator = &no_op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000396 if (s == NULL) {
397 return EOI;
398 }
399
400 op = ops;
401 do {
402 if (strcmp(s, op->op_text) == 0) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000403 last_operator = op;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000404 return op->op_num;
405 }
406 op++;
407 } while (op < ops + ARRAY_SIZE(ops));
408
409 return OPERAND;
410}
411
Erik Andersen13456d12000-03-16 08:09:57 +0000412
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000413static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000414{
415 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000416 const struct operator_t *op;
417 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000418
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000419 opnd1 = *args;
420 check_operator(*++args);
421 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000422
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000423 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000424 if (opnd2 == NULL)
Paul Fox6ab03782006-06-08 21:37:26 +0000425 syntax(op->op_text, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000426
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000427 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000428 val1 = getn(opnd1);
429 val2 = getn(opnd2);
430 if (op->op_num == INTEQ)
431 return val1 == val2;
432 if (op->op_num == INTNE)
433 return val1 != val2;
434 if (op->op_num == INTGE)
435 return val1 >= val2;
436 if (op->op_num == INTGT)
437 return val1 > val2;
438 if (op->op_num == INTLE)
439 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200440 /*if (op->op_num == INTLT)*/
441 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000442 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000443 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000444 val1 = strcmp(opnd1, opnd2);
445 if (op->op_num == STREQ)
446 return val1 == 0;
447 if (op->op_num == STRNE)
448 return val1 != 0;
449 if (op->op_num == STRLT)
450 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200451 /*if (op->op_num == STRGT)*/
452 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000453 }
454 /* We are sure that these three are by now the only binops we didn't check
455 * yet, so we do not check if the class is correct:
456 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000457/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000458 {
459 struct stat b1, b2;
460
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000461 if (stat(opnd1, &b1) || stat(opnd2, &b2))
462 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000463 if (op->op_num == FILNT)
464 return b1.st_mtime > b2.st_mtime;
465 if (op->op_num == FILOT)
466 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200467 /*if (op->op_num == FILEQ)*/
468 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000469 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200470 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000471}
472
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000473
474static void initialize_group_array(void)
475{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200476 int n;
477
478 /* getgroups may be expensive, try to use it only once */
479 ngroups = 32;
480 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000481 /* FIXME: ash tries so hard to not die on OOM,
482 * and we spoil it with just one xrealloc here */
483 /* We realloc, because test_main can be entered repeatedly by shell.
484 * Testcase (ash): 'while true; do test -x some_file; done'
485 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200486 n = ngroups;
487 group_array = xrealloc(group_array, n * sizeof(gid_t));
488 ngroups = getgroups(n, group_array);
489 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000490}
491
492
493/* Return non-zero if GID is one that we have in our groups list. */
494//XXX: FIXME: duplicate of existing libbb function?
495// see toplevel TODO file:
496// possible code duplication ingroup() and is_a_group_member()
497static int is_a_group_member(gid_t gid)
498{
499 int i;
500
501 /* Short-circuit if possible, maybe saving a call to getgroups(). */
502 if (gid == getgid() || gid == getegid())
503 return 1;
504
505 if (ngroups == 0)
506 initialize_group_array();
507
508 /* Search through the list looking for GID. */
509 for (i = 0; i < ngroups; i++)
510 if (gid == group_array[i])
511 return 1;
512
513 return 0;
514}
515
516
517/* Do the same thing access(2) does, but use the effective uid and gid,
518 and don't make the mistake of telling root that any file is
519 executable. */
520static int test_eaccess(char *path, int mode)
521{
522 struct stat st;
523 unsigned int euid = geteuid();
524
525 if (stat(path, &st) < 0)
526 return -1;
527
528 if (euid == 0) {
529 /* Root can read or write any file. */
530 if (mode != X_OK)
531 return 0;
532
533 /* Root can execute any file that has any one of the execute
534 bits set. */
535 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
536 return 0;
537 }
538
539 if (st.st_uid == euid) /* owner */
540 mode <<= 6;
541 else if (is_a_group_member(st.st_gid))
542 mode <<= 3;
543
544 if (st.st_mode & mode)
545 return 0;
546
547 return -1;
548}
549
550
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000551static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000552{
553 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000554 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000555
556 if (mode == FILSYM) {
557#ifdef S_IFLNK
558 if (lstat(nm, &s) == 0) {
559 i = S_IFLNK;
560 goto filetype;
561 }
562#endif
563 return 0;
564 }
565
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000566 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000567 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000568 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000569 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000570 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000571 if (mode == FILRD)
572 i = R_OK;
573 if (mode == FILWR)
574 i = W_OK;
575 if (mode == FILEX)
576 i = X_OK;
577 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000578 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000579 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000580 if (mode == FILREG)
581 i = S_IFREG;
582 if (mode == FILDIR)
583 i = S_IFDIR;
584 if (mode == FILCDEV)
585 i = S_IFCHR;
586 if (mode == FILBDEV)
587 i = S_IFBLK;
588 if (mode == FILFIFO) {
589#ifdef S_IFIFO
590 i = S_IFIFO;
591#else
592 return 0;
593#endif
594 }
595 if (mode == FILSOCK) {
596#ifdef S_IFSOCK
597 i = S_IFSOCK;
598#else
599 return 0;
600#endif
601 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000602 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000603 return ((s.st_mode & S_IFMT) == i);
604 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000605 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000606 if (mode == FILSUID)
607 i = S_ISUID;
608 if (mode == FILSGID)
609 i = S_ISGID;
610 if (mode == FILSTCK)
611 i = S_ISVTX;
612 return ((s.st_mode & i) != 0);
613 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000614 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000615 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000616 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000617 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000618 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000619 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000620 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000621}
622
Erik Andersen13456d12000-03-16 08:09:57 +0000623
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000624static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000625{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000626 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000627
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000628 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
629 if (n == UNOT) {
630 res = !nexpr(check_operator(*++args));
631 unnest_msg("<nexpr:%lld\n", res);
632 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000633 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000634 res = primary(n);
635 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000636 return res;
637}
638
639
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000640static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000641{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000642 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000643
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000644 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
645 res = nexpr(n);
646 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
647 if (check_operator(*++args) == BAND) {
648 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
649 res = aexpr(check_operator(*++args)) && res;
650 unnest_msg("<aexpr:%lld\n", res);
651 return res;
652 }
653 args--;
654 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
655 return res;
656}
657
658
659static number_t oexpr(enum token n)
660{
661 number_t res;
662
663 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
664 res = aexpr(n);
665 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
666 if (check_operator(*++args) == BOR) {
667 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
668 res = oexpr(check_operator(*++args)) || res;
669 unnest_msg("<oexpr:%lld\n", res);
670 return res;
671 }
672 args--;
673 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
674 return res;
675}
676
677
678static number_t primary(enum token n)
679{
680#if TEST_DEBUG
681 number_t res = res; /* for compiler */
682#else
683 number_t res;
684#endif
685 const struct operator_t *args0_op;
686
687 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000688 if (n == EOI) {
689 syntax(NULL, "argument expected");
690 }
691 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000692 res = oexpr(check_operator(*++args));
693 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000694 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000695 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000696 return res;
697 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000698
699 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
700 * do the same */
701 args0_op = last_operator;
702 /* last_operator = operator at args[1] */
703 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
704 if (args[2]) {
705 // coreutils also does this:
706 // if (args[3] && args[0]="-l" && args[2] is BINOP)
707 // return binop(1 /* prepended by -l */);
708 if (last_operator->op_type == BINOP)
709 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
710 }
711 }
712 /* check "is args[0] unop?" second */
713 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000714 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000715 if (args[1] == NULL)
716// syntax(args0_op->op_text, "argument expected");
717 goto check_emptiness;
718 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000719 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000720 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000721 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000722 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000723 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000724 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
725 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000726 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000727
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000728 /*check_operator(args[1]); - already done */
729 if (last_operator->op_type == BINOP) {
730 /* args[2] is known to be NULL, isn't it bound to fail? */
731 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000732 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000733 check_emptiness:
734 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000735}
736
737
738int test_main(int argc, char **argv)
739{
740 int res;
741 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000742// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000743
744 arg0 = bb_basename(argv[0]);
745 if (arg0[0] == '[') {
746 --argc;
747 if (!arg0[1]) { /* "[" ? */
748 if (NOT_LONE_CHAR(argv[argc], ']')) {
749 bb_error_msg("missing ]");
750 return 2;
751 }
752 } else { /* assuming "[[" */
753 if (strcmp(argv[argc], "]]") != 0) {
754 bb_error_msg("missing ]]");
755 return 2;
756 }
Erik Andersen13456d12000-03-16 08:09:57 +0000757 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000758 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000759 }
760
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000761 /* We must do DEINIT_S() prior to returning */
762 INIT_S();
763
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000764 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000765 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000766 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000767
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000768 /* resetting ngroups is probably unnecessary. it will
769 * force a new call to getgroups(), which prevents using
770 * group data fetched during a previous call. but the
771 * only way the group data could be stale is if there's
772 * been an intervening call to setgroups(), and this
773 * isn't likely in the case of a shell. paranoia
774 * prevails...
775 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200776 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000777
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000778 //argc--;
779 argv++;
780
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000781 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000782 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000783 res = 1;
784 goto ret;
785 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000786#if 0
787// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000788 if (LONE_CHAR(argv[0], '!') && argv[1]) {
789 negate = 1;
790 //argc--;
791 argv++;
792 }
793 if (!argv[1]) { /* "test [!] arg" */
794 res = (*argv[0] == '\0');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000795 goto ret;
796 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000797 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000798 check_operator(argv[1]);
799 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000800 /* "test [!] arg1 <binary_op> arg2" */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000801 args = &argv[0];
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000802 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000803 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000804 }
805 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000806
807 /* Some complex expression. Undo '!' removal */
808 if (negate) {
809 negate = 0;
810 //argc++;
811 argv--;
812 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000813#endif
814 args = &argv[0];
815 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000816
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000817 if (*args != NULL && *++args != NULL) {
818 /* TODO: example when this happens? */
819 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000820 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000821 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000822 ret:
823 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000824// return negate ? !res : res;
825 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000826}