blob: a1d164574e23ede49bdde50bc9735ddaff5b7c12 [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
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200225enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000226 UNOP,
227 BINOP,
228 BUNOP,
229 BBINOP,
230 PAREN
231};
232
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000233struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000234 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000235};
236
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200237static const struct operator_t ops_table[] = {
238 { /* "-r" */ FILRD , UNOP },
239 { /* "-w" */ FILWR , UNOP },
240 { /* "-x" */ FILEX , UNOP },
241 { /* "-e" */ FILEXIST, UNOP },
242 { /* "-f" */ FILREG , UNOP },
243 { /* "-d" */ FILDIR , UNOP },
244 { /* "-c" */ FILCDEV , UNOP },
245 { /* "-b" */ FILBDEV , UNOP },
246 { /* "-p" */ FILFIFO , UNOP },
247 { /* "-u" */ FILSUID , UNOP },
248 { /* "-g" */ FILSGID , UNOP },
249 { /* "-k" */ FILSTCK , UNOP },
250 { /* "-s" */ FILGZ , UNOP },
251 { /* "-t" */ FILTT , UNOP },
252 { /* "-z" */ STREZ , UNOP },
253 { /* "-n" */ STRNZ , UNOP },
254 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000255
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200256 { /* "-O" */ FILUID , UNOP },
257 { /* "-G" */ FILGID , UNOP },
258 { /* "-L" */ FILSYM , UNOP },
259 { /* "-S" */ FILSOCK , UNOP },
260 { /* "=" */ STREQ , BINOP },
261 { /* "==" */ STREQ , BINOP },
262 { /* "!=" */ STRNE , BINOP },
263 { /* "<" */ STRLT , BINOP },
264 { /* ">" */ STRGT , BINOP },
265 { /* "-eq"*/ INTEQ , BINOP },
266 { /* "-ne"*/ INTNE , BINOP },
267 { /* "-ge"*/ INTGE , BINOP },
268 { /* "-gt"*/ INTGT , BINOP },
269 { /* "-le"*/ INTLE , BINOP },
270 { /* "-lt"*/ INTLT , BINOP },
271 { /* "-nt"*/ FILNT , BINOP },
272 { /* "-ot"*/ FILOT , BINOP },
273 { /* "-ef"*/ FILEQ , BINOP },
274 { /* "!" */ UNOT , BUNOP },
275 { /* "-a" */ BAND , BBINOP },
276 { /* "-o" */ BOR , BBINOP },
277 { /* "(" */ LPAREN , PAREN },
278 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000279};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200280/* Please keep these two tables in sync */
281static const char ops_texts[] ALIGN1 =
282 "-r" "\0"
283 "-w" "\0"
284 "-x" "\0"
285 "-e" "\0"
286 "-f" "\0"
287 "-d" "\0"
288 "-c" "\0"
289 "-b" "\0"
290 "-p" "\0"
291 "-u" "\0"
292 "-g" "\0"
293 "-k" "\0"
294 "-s" "\0"
295 "-t" "\0"
296 "-z" "\0"
297 "-n" "\0"
298 "-h" "\0"
299
300 "-O" "\0"
301 "-G" "\0"
302 "-L" "\0"
303 "-S" "\0"
304 "=" "\0"
305 "==" "\0"
306 "!=" "\0"
307 "<" "\0"
308 ">" "\0"
309 "-eq" "\0"
310 "-ne" "\0"
311 "-ge" "\0"
312 "-gt" "\0"
313 "-le" "\0"
314 "-lt" "\0"
315 "-nt" "\0"
316 "-ot" "\0"
317 "-ef" "\0"
318 "!" "\0"
319 "-a" "\0"
320 "-o" "\0"
321 "(" "\0"
322 ")" "\0"
323;
Erik Andersen13456d12000-03-16 08:09:57 +0000324
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000325
326#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000327typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000328#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000329typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000330#endif
331
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000332
333/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000334struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000335 char **args;
336 /* set only by check_operator(), either to bogus struct
337 * or points to matching operator_t struct. Never NULL. */
338 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000339 gid_t *group_array;
340 int ngroups;
341 jmp_buf leaving;
342};
343
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000344/* See test_ptr_hack.c */
345extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000346
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000347#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000348#define args (S.args )
349#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000350#define group_array (S.group_array )
351#define ngroups (S.ngroups )
352#define leaving (S.leaving )
353
354#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000355 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000356 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000357} while (0)
358#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000359 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000360} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000361
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000362static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000363
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000364static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000365static void syntax(const char *op, const char *msg)
366{
367 if (op && *op) {
368 bb_error_msg("%s: %s", op, msg);
369 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000370 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000371 }
372 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000373}
374
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000375/* atoi with error detection */
376//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000377static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000378{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000379 char *p;
380#if ENABLE_FEATURE_TEST_64
381 long long r;
382#else
383 long r;
384#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000385
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000386 errno = 0;
387#if ENABLE_FEATURE_TEST_64
388 r = strtoll(s, &p, 10);
389#else
390 r = strtol(s, &p, 10);
391#endif
392
393 if (errno != 0)
394 syntax(s, "out of range");
395
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200396 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000397 syntax(s, "bad number");
398
399 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000400}
401
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000402/* UNUSED
403static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000404{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000405 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000406
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000407 return (stat(f1, &b1) == 0 &&
408 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000409}
410
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000411static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000412{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000413 struct stat b1, b2;
414
415 return (stat(f1, &b1) == 0 &&
416 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000417}
418
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000419static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000420{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000421 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000422
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000423 return (stat(f1, &b1) == 0 &&
424 stat(f2, &b2) == 0 &&
425 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000426}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000427*/
428
429
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200430static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000431{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000432 static const struct operator_t no_op = {
433 .op_num = -1,
434 .op_type = -1
435 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200436 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000437
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000438 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200439 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000440 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200441 n = index_in_strings(ops_texts, s);
442 if (n < 0)
443 return OPERAND;
444 last_operator = &ops_table[n];
445 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000446}
447
Erik Andersen13456d12000-03-16 08:09:57 +0000448
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000449static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000450{
451 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000452 const struct operator_t *op;
453 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000454
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000455 opnd1 = *args;
456 check_operator(*++args);
457 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000458
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000459 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000460 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200461 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000462
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000463 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000464 val1 = getn(opnd1);
465 val2 = getn(opnd2);
466 if (op->op_num == INTEQ)
467 return val1 == val2;
468 if (op->op_num == INTNE)
469 return val1 != val2;
470 if (op->op_num == INTGE)
471 return val1 >= val2;
472 if (op->op_num == INTGT)
473 return val1 > val2;
474 if (op->op_num == INTLE)
475 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200476 /*if (op->op_num == INTLT)*/
477 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000478 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000479 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000480 val1 = strcmp(opnd1, opnd2);
481 if (op->op_num == STREQ)
482 return val1 == 0;
483 if (op->op_num == STRNE)
484 return val1 != 0;
485 if (op->op_num == STRLT)
486 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200487 /*if (op->op_num == STRGT)*/
488 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000489 }
490 /* We are sure that these three are by now the only binops we didn't check
491 * yet, so we do not check if the class is correct:
492 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000493/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000494 {
495 struct stat b1, b2;
496
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000497 if (stat(opnd1, &b1) || stat(opnd2, &b2))
498 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000499 if (op->op_num == FILNT)
500 return b1.st_mtime > b2.st_mtime;
501 if (op->op_num == FILOT)
502 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200503 /*if (op->op_num == FILEQ)*/
504 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000505 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200506 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000507}
508
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000509
510static void initialize_group_array(void)
511{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200512 int n;
513
514 /* getgroups may be expensive, try to use it only once */
515 ngroups = 32;
516 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000517 /* FIXME: ash tries so hard to not die on OOM,
518 * and we spoil it with just one xrealloc here */
519 /* We realloc, because test_main can be entered repeatedly by shell.
520 * Testcase (ash): 'while true; do test -x some_file; done'
521 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200522 n = ngroups;
523 group_array = xrealloc(group_array, n * sizeof(gid_t));
524 ngroups = getgroups(n, group_array);
525 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000526}
527
528
529/* Return non-zero if GID is one that we have in our groups list. */
530//XXX: FIXME: duplicate of existing libbb function?
531// see toplevel TODO file:
532// possible code duplication ingroup() and is_a_group_member()
533static int is_a_group_member(gid_t gid)
534{
535 int i;
536
537 /* Short-circuit if possible, maybe saving a call to getgroups(). */
538 if (gid == getgid() || gid == getegid())
539 return 1;
540
541 if (ngroups == 0)
542 initialize_group_array();
543
544 /* Search through the list looking for GID. */
545 for (i = 0; i < ngroups; i++)
546 if (gid == group_array[i])
547 return 1;
548
549 return 0;
550}
551
552
553/* Do the same thing access(2) does, but use the effective uid and gid,
554 and don't make the mistake of telling root that any file is
555 executable. */
556static int test_eaccess(char *path, int mode)
557{
558 struct stat st;
559 unsigned int euid = geteuid();
560
561 if (stat(path, &st) < 0)
562 return -1;
563
564 if (euid == 0) {
565 /* Root can read or write any file. */
566 if (mode != X_OK)
567 return 0;
568
569 /* Root can execute any file that has any one of the execute
570 bits set. */
571 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
572 return 0;
573 }
574
575 if (st.st_uid == euid) /* owner */
576 mode <<= 6;
577 else if (is_a_group_member(st.st_gid))
578 mode <<= 3;
579
580 if (st.st_mode & mode)
581 return 0;
582
583 return -1;
584}
585
586
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000587static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000588{
589 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000590 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000591
592 if (mode == FILSYM) {
593#ifdef S_IFLNK
594 if (lstat(nm, &s) == 0) {
595 i = S_IFLNK;
596 goto filetype;
597 }
598#endif
599 return 0;
600 }
601
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000602 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000603 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000604 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000605 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000606 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000607 if (mode == FILRD)
608 i = R_OK;
609 if (mode == FILWR)
610 i = W_OK;
611 if (mode == FILEX)
612 i = X_OK;
613 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000614 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000615 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000616 if (mode == FILREG)
617 i = S_IFREG;
618 if (mode == FILDIR)
619 i = S_IFDIR;
620 if (mode == FILCDEV)
621 i = S_IFCHR;
622 if (mode == FILBDEV)
623 i = S_IFBLK;
624 if (mode == FILFIFO) {
625#ifdef S_IFIFO
626 i = S_IFIFO;
627#else
628 return 0;
629#endif
630 }
631 if (mode == FILSOCK) {
632#ifdef S_IFSOCK
633 i = S_IFSOCK;
634#else
635 return 0;
636#endif
637 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000638 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000639 return ((s.st_mode & S_IFMT) == i);
640 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000641 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000642 if (mode == FILSUID)
643 i = S_ISUID;
644 if (mode == FILSGID)
645 i = S_ISGID;
646 if (mode == FILSTCK)
647 i = S_ISVTX;
648 return ((s.st_mode & i) != 0);
649 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000650 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000651 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000652 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000653 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000654 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000655 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000656 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000657}
658
Erik Andersen13456d12000-03-16 08:09:57 +0000659
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000660static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000661{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000662 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000663
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000664 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
665 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200666 n = check_operator(*++args);
667 if (n == EOI) {
668 /* special case: [ ! ], [ a -a ! ] are valid */
669 /* IOW, "! ARG" may miss ARG */
670 unnest_msg("<nexpr:1 (!EOI)\n");
671 return 1;
672 }
673 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000674 unnest_msg("<nexpr:%lld\n", res);
675 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000676 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000677 res = primary(n);
678 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000679 return res;
680}
681
682
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000683static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000684{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000685 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000686
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000687 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
688 res = nexpr(n);
689 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
690 if (check_operator(*++args) == BAND) {
691 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
692 res = aexpr(check_operator(*++args)) && res;
693 unnest_msg("<aexpr:%lld\n", res);
694 return res;
695 }
696 args--;
697 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
698 return res;
699}
700
701
702static number_t oexpr(enum token n)
703{
704 number_t res;
705
706 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
707 res = aexpr(n);
708 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
709 if (check_operator(*++args) == BOR) {
710 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
711 res = oexpr(check_operator(*++args)) || res;
712 unnest_msg("<oexpr:%lld\n", res);
713 return res;
714 }
715 args--;
716 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
717 return res;
718}
719
720
721static number_t primary(enum token n)
722{
723#if TEST_DEBUG
724 number_t res = res; /* for compiler */
725#else
726 number_t res;
727#endif
728 const struct operator_t *args0_op;
729
730 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000731 if (n == EOI) {
732 syntax(NULL, "argument expected");
733 }
734 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000735 res = oexpr(check_operator(*++args));
736 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000737 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000738 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000739 return res;
740 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000741
742 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
743 * do the same */
744 args0_op = last_operator;
745 /* last_operator = operator at args[1] */
746 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
747 if (args[2]) {
748 // coreutils also does this:
749 // if (args[3] && args[0]="-l" && args[2] is BINOP)
750 // return binop(1 /* prepended by -l */);
751 if (last_operator->op_type == BINOP)
752 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
753 }
754 }
755 /* check "is args[0] unop?" second */
756 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000757 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000758 if (args[1] == NULL)
759// syntax(args0_op->op_text, "argument expected");
760 goto check_emptiness;
761 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000762 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000763 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000764 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000765 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000766 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000767 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
768 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000769 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000770
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000771 /*check_operator(args[1]); - already done */
772 if (last_operator->op_type == BINOP) {
773 /* args[2] is known to be NULL, isn't it bound to fail? */
774 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000775 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000776 check_emptiness:
777 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000778}
779
780
781int test_main(int argc, char **argv)
782{
783 int res;
784 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000785// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000786
787 arg0 = bb_basename(argv[0]);
788 if (arg0[0] == '[') {
789 --argc;
790 if (!arg0[1]) { /* "[" ? */
791 if (NOT_LONE_CHAR(argv[argc], ']')) {
792 bb_error_msg("missing ]");
793 return 2;
794 }
795 } else { /* assuming "[[" */
796 if (strcmp(argv[argc], "]]") != 0) {
797 bb_error_msg("missing ]]");
798 return 2;
799 }
Erik Andersen13456d12000-03-16 08:09:57 +0000800 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000801 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000802 }
803
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000804 /* We must do DEINIT_S() prior to returning */
805 INIT_S();
806
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000807 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000808 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000809 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000810
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000811 /* resetting ngroups is probably unnecessary. it will
812 * force a new call to getgroups(), which prevents using
813 * group data fetched during a previous call. but the
814 * only way the group data could be stale is if there's
815 * been an intervening call to setgroups(), and this
816 * isn't likely in the case of a shell. paranoia
817 * prevails...
818 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200819 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000820
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000821 //argc--;
822 argv++;
823
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000824 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000825 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000826 res = 1;
827 goto ret;
828 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000829#if 0
830// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000831 if (LONE_CHAR(argv[0], '!') && argv[1]) {
832 negate = 1;
833 //argc--;
834 argv++;
835 }
836 if (!argv[1]) { /* "test [!] arg" */
837 res = (*argv[0] == '\0');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000838 goto ret;
839 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000840 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000841 check_operator(argv[1]);
842 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000843 /* "test [!] arg1 <binary_op> arg2" */
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200844 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000845 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000846 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000847 }
848 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000849
850 /* Some complex expression. Undo '!' removal */
851 if (negate) {
852 negate = 0;
853 //argc++;
854 argv--;
855 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000856#endif
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200857 args = argv;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000858 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000859
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000860 if (*args != NULL && *++args != NULL) {
861 /* TODO: example when this happens? */
862 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000863 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000864 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000865 ret:
866 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000867// return negate ? !res : res;
868 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000869}