blob: c430f221015004417cce4878077431b845ae28a2 [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) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200630 n = check_operator(*++args);
631 if (n == EOI) {
632 /* special case: [ ! ], [ a -a ! ] are valid */
633 /* IOW, "! ARG" may miss ARG */
634 unnest_msg("<nexpr:1 (!EOI)\n");
635 return 1;
636 }
637 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000638 unnest_msg("<nexpr:%lld\n", res);
639 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000640 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000641 res = primary(n);
642 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000643 return res;
644}
645
646
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000647static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000648{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000649 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000650
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000651 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
652 res = nexpr(n);
653 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
654 if (check_operator(*++args) == BAND) {
655 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
656 res = aexpr(check_operator(*++args)) && res;
657 unnest_msg("<aexpr:%lld\n", res);
658 return res;
659 }
660 args--;
661 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
662 return res;
663}
664
665
666static number_t oexpr(enum token n)
667{
668 number_t res;
669
670 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
671 res = aexpr(n);
672 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
673 if (check_operator(*++args) == BOR) {
674 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
675 res = oexpr(check_operator(*++args)) || res;
676 unnest_msg("<oexpr:%lld\n", res);
677 return res;
678 }
679 args--;
680 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
681 return res;
682}
683
684
685static number_t primary(enum token n)
686{
687#if TEST_DEBUG
688 number_t res = res; /* for compiler */
689#else
690 number_t res;
691#endif
692 const struct operator_t *args0_op;
693
694 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000695 if (n == EOI) {
696 syntax(NULL, "argument expected");
697 }
698 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000699 res = oexpr(check_operator(*++args));
700 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000701 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000702 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000703 return res;
704 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000705
706 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
707 * do the same */
708 args0_op = last_operator;
709 /* last_operator = operator at args[1] */
710 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
711 if (args[2]) {
712 // coreutils also does this:
713 // if (args[3] && args[0]="-l" && args[2] is BINOP)
714 // return binop(1 /* prepended by -l */);
715 if (last_operator->op_type == BINOP)
716 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
717 }
718 }
719 /* check "is args[0] unop?" second */
720 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000721 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000722 if (args[1] == NULL)
723// syntax(args0_op->op_text, "argument expected");
724 goto check_emptiness;
725 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000726 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000727 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000728 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000729 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000730 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000731 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
732 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000733 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000734
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000735 /*check_operator(args[1]); - already done */
736 if (last_operator->op_type == BINOP) {
737 /* args[2] is known to be NULL, isn't it bound to fail? */
738 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000739 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000740 check_emptiness:
741 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000742}
743
744
745int test_main(int argc, char **argv)
746{
747 int res;
748 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000749// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000750
751 arg0 = bb_basename(argv[0]);
752 if (arg0[0] == '[') {
753 --argc;
754 if (!arg0[1]) { /* "[" ? */
755 if (NOT_LONE_CHAR(argv[argc], ']')) {
756 bb_error_msg("missing ]");
757 return 2;
758 }
759 } else { /* assuming "[[" */
760 if (strcmp(argv[argc], "]]") != 0) {
761 bb_error_msg("missing ]]");
762 return 2;
763 }
Erik Andersen13456d12000-03-16 08:09:57 +0000764 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000765 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000766 }
767
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000768 /* We must do DEINIT_S() prior to returning */
769 INIT_S();
770
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000771 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000772 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000773 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000774
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000775 /* resetting ngroups is probably unnecessary. it will
776 * force a new call to getgroups(), which prevents using
777 * group data fetched during a previous call. but the
778 * only way the group data could be stale is if there's
779 * been an intervening call to setgroups(), and this
780 * isn't likely in the case of a shell. paranoia
781 * prevails...
782 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200783 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000784
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000785 //argc--;
786 argv++;
787
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000788 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000789 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000790 res = 1;
791 goto ret;
792 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000793#if 0
794// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000795 if (LONE_CHAR(argv[0], '!') && argv[1]) {
796 negate = 1;
797 //argc--;
798 argv++;
799 }
800 if (!argv[1]) { /* "test [!] arg" */
801 res = (*argv[0] == '\0');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000802 goto ret;
803 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000804 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000805 check_operator(argv[1]);
806 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000807 /* "test [!] arg1 <binary_op> arg2" */
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200808 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000809 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000810 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000811 }
812 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000813
814 /* Some complex expression. Undo '!' removal */
815 if (negate) {
816 negate = 0;
817 //argc++;
818 argv--;
819 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000820#endif
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200821 args = argv;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000822 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000823
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000824 if (*args != NULL && *++args != NULL) {
825 /* TODO: example when this happens? */
826 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000827 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000828 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000829 ret:
830 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000831// return negate ? !res : res;
832 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000833}