blob: f18e3ae5a5e9f128eea9905e442567ffe828bd6b [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020017 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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 */
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020022
23//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
24//kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o
25//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
26
27//config:config TEST
28//config: bool "test"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020029//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020030//config: help
31//config: test is used to check file types and compare values,
32//config: returning an appropriate exit code. The bash shell
33//config: has test built in, ash can build it in optionally.
34//config:
35//config:config FEATURE_TEST_64
36//config: bool "Extend test to 64 bit"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020037//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020038//config: depends on TEST || ASH_BUILTIN_TEST || HUSH
39//config: help
40//config: Enable 64-bit support in test.
41
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000042#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000043#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000044
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000045/* This is a NOFORK applet. Be very careful! */
46
47/* test_main() is called from shells, and we need to be extra careful here.
48 * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL
49 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000050
Erik Andersen13456d12000-03-16 08:09:57 +000051/* test(1) accepts the following grammar:
52 oexpr ::= aexpr | aexpr "-o" oexpr ;
53 aexpr ::= nexpr | nexpr "-a" aexpr ;
54 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000055 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000056 | operand binary-operator operand
57 | operand
58 | "(" oexpr ")"
59 ;
60 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
61 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
62
Mike Frysinger75ac42b2005-04-14 02:49:22 +000063 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000064 "-nt"|"-ot"|"-ef";
65 operand ::= <any legal UNIX file name>
66*/
67
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020068/* TODO: handle [[ expr ]] bashism bash-compatibly.
69 * [[ ]] is meant to be a "better [ ]", with less weird syntax
70 * and without the risk of variables and quoted strings misinterpreted
71 * as operators.
72 * This will require support from shells - we need to know quote status
73 * of each parameter (see below).
74 *
75 * Word splitting and pathname expansion should NOT be performed:
76 * # a="a b"; [[ $a = "a b" ]] && echo YES
77 * YES
78 * # [[ /bin/m* ]] && echo YES
79 * YES
80 *
81 * =~ should do regexp match
82 * = and == should do pattern match against right side:
83 * # [[ *a* == bab ]] && echo YES
84 * # [[ bab == *a* ]] && echo YES
85 * YES
86 * != does the negated == (i.e., also with pattern matching).
87 * Pattern matching is quotation-sensitive:
88 * # [[ bab == "b"a* ]] && echo YES
89 * YES
90 * # [[ bab == b"a*" ]] && echo YES
91 *
92 * Conditional operators such as -f must be unquoted literals to be recognized:
93 * # [[ -e /bin ]] && echo YES
94 * YES
95 * # [[ '-e' /bin ]] && echo YES
96 * bash: conditional binary operator expected...
97 * # A='-e'; [[ $A /bin ]] && echo YES
98 * bash: conditional binary operator expected...
99 *
100 * || and && should work as -o and -a work in [ ]
101 * -a and -o aren't recognized (&& and || are to be used instead)
102 * ( and ) do not need to be quoted unlike in [ ]:
103 * # [[ ( abc ) && '' ]] && echo YES
104 * # [[ ( abc ) || '' ]] && echo YES
105 * YES
106 * # [[ ( abc ) -o '' ]] && echo YES
107 * bash: syntax error in conditional expression...
108 *
109 * Apart from the above, [[ expr ]] should work as [ expr ]
110 */
111
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000112#define TEST_DEBUG 0
113
Erik Andersen13456d12000-03-16 08:09:57 +0000114enum token {
115 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200116
117 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000118 FILWR,
119 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200120
Erik Andersen13456d12000-03-16 08:09:57 +0000121 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200122
123 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000124 FILDIR,
125 FILCDEV,
126 FILBDEV,
127 FILFIFO,
128 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200129
Erik Andersen13456d12000-03-16 08:09:57 +0000130 FILSYM,
131 FILGZ,
132 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200133
134 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000135 FILSGID,
136 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200137
138 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000139 FILOT,
140 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200141
Erik Andersen13456d12000-03-16 08:09:57 +0000142 FILUID,
143 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200144
145 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000146 STRNZ,
147 STREQ,
148 STRNE,
149 STRLT,
150 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200151
152 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000153 INTNE,
154 INTGE,
155 INTGT,
156 INTLE,
157 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200158
Erik Andersen13456d12000-03-16 08:09:57 +0000159 UNOT,
160 BAND,
161 BOR,
162 LPAREN,
163 RPAREN,
164 OPERAND
165};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000166#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
167#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
168#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000169#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000170#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
171#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000172
173#if TEST_DEBUG
174int depth;
175#define nest_msg(...) do { \
176 depth++; \
177 fprintf(stderr, "%*s", depth*2, ""); \
178 fprintf(stderr, __VA_ARGS__); \
179} while (0)
180#define unnest_msg(...) do { \
181 fprintf(stderr, "%*s", depth*2, ""); \
182 fprintf(stderr, __VA_ARGS__); \
183 depth--; \
184} while (0)
185#define dbg_msg(...) do { \
186 fprintf(stderr, "%*s", depth*2, ""); \
187 fprintf(stderr, __VA_ARGS__); \
188} while (0)
189#define unnest_msg_and_return(expr, ...) do { \
190 number_t __res = (expr); \
191 fprintf(stderr, "%*s", depth*2, ""); \
192 fprintf(stderr, __VA_ARGS__, res); \
193 depth--; \
194 return __res; \
195} while (0)
196static const char *const TOKSTR[] = {
197 "EOI",
198 "FILRD",
199 "FILWR",
200 "FILEX",
201 "FILEXIST",
202 "FILREG",
203 "FILDIR",
204 "FILCDEV",
205 "FILBDEV",
206 "FILFIFO",
207 "FILSOCK",
208 "FILSYM",
209 "FILGZ",
210 "FILTT",
211 "FILSUID",
212 "FILSGID",
213 "FILSTCK",
214 "FILNT",
215 "FILOT",
216 "FILEQ",
217 "FILUID",
218 "FILGID",
219 "STREZ",
220 "STRNZ",
221 "STREQ",
222 "STRNE",
223 "STRLT",
224 "STRGT",
225 "INTEQ",
226 "INTNE",
227 "INTGE",
228 "INTGT",
229 "INTLE",
230 "INTLT",
231 "UNOT",
232 "BAND",
233 "BOR",
234 "LPAREN",
235 "RPAREN",
236 "OPERAND"
237};
238#else
239#define nest_msg(...) ((void)0)
240#define unnest_msg(...) ((void)0)
241#define dbg_msg(...) ((void)0)
242#define unnest_msg_and_return(expr, ...) return expr
243#endif
244
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200245enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000246 UNOP,
247 BINOP,
248 BUNOP,
249 BBINOP,
250 PAREN
251};
252
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000253struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000254 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000255};
256
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200257static const struct operator_t ops_table[] = {
258 { /* "-r" */ FILRD , UNOP },
259 { /* "-w" */ FILWR , UNOP },
260 { /* "-x" */ FILEX , UNOP },
261 { /* "-e" */ FILEXIST, UNOP },
262 { /* "-f" */ FILREG , UNOP },
263 { /* "-d" */ FILDIR , UNOP },
264 { /* "-c" */ FILCDEV , UNOP },
265 { /* "-b" */ FILBDEV , UNOP },
266 { /* "-p" */ FILFIFO , UNOP },
267 { /* "-u" */ FILSUID , UNOP },
268 { /* "-g" */ FILSGID , UNOP },
269 { /* "-k" */ FILSTCK , UNOP },
270 { /* "-s" */ FILGZ , UNOP },
271 { /* "-t" */ FILTT , UNOP },
272 { /* "-z" */ STREZ , UNOP },
273 { /* "-n" */ STRNZ , UNOP },
274 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000275
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200276 { /* "-O" */ FILUID , UNOP },
277 { /* "-G" */ FILGID , UNOP },
278 { /* "-L" */ FILSYM , UNOP },
279 { /* "-S" */ FILSOCK , UNOP },
280 { /* "=" */ STREQ , BINOP },
281 { /* "==" */ STREQ , BINOP },
282 { /* "!=" */ STRNE , BINOP },
283 { /* "<" */ STRLT , BINOP },
284 { /* ">" */ STRGT , BINOP },
285 { /* "-eq"*/ INTEQ , BINOP },
286 { /* "-ne"*/ INTNE , BINOP },
287 { /* "-ge"*/ INTGE , BINOP },
288 { /* "-gt"*/ INTGT , BINOP },
289 { /* "-le"*/ INTLE , BINOP },
290 { /* "-lt"*/ INTLT , BINOP },
291 { /* "-nt"*/ FILNT , BINOP },
292 { /* "-ot"*/ FILOT , BINOP },
293 { /* "-ef"*/ FILEQ , BINOP },
294 { /* "!" */ UNOT , BUNOP },
295 { /* "-a" */ BAND , BBINOP },
296 { /* "-o" */ BOR , BBINOP },
297 { /* "(" */ LPAREN , PAREN },
298 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000299};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200300/* Please keep these two tables in sync */
301static const char ops_texts[] ALIGN1 =
302 "-r" "\0"
303 "-w" "\0"
304 "-x" "\0"
305 "-e" "\0"
306 "-f" "\0"
307 "-d" "\0"
308 "-c" "\0"
309 "-b" "\0"
310 "-p" "\0"
311 "-u" "\0"
312 "-g" "\0"
313 "-k" "\0"
314 "-s" "\0"
315 "-t" "\0"
316 "-z" "\0"
317 "-n" "\0"
318 "-h" "\0"
319
320 "-O" "\0"
321 "-G" "\0"
322 "-L" "\0"
323 "-S" "\0"
324 "=" "\0"
325 "==" "\0"
326 "!=" "\0"
327 "<" "\0"
328 ">" "\0"
329 "-eq" "\0"
330 "-ne" "\0"
331 "-ge" "\0"
332 "-gt" "\0"
333 "-le" "\0"
334 "-lt" "\0"
335 "-nt" "\0"
336 "-ot" "\0"
337 "-ef" "\0"
338 "!" "\0"
339 "-a" "\0"
340 "-o" "\0"
341 "(" "\0"
342 ")" "\0"
343;
Erik Andersen13456d12000-03-16 08:09:57 +0000344
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000345
346#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000347typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000348#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000349typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000350#endif
351
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000352
353/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000354struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000355 char **args;
356 /* set only by check_operator(), either to bogus struct
357 * or points to matching operator_t struct. Never NULL. */
358 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000359 gid_t *group_array;
360 int ngroups;
361 jmp_buf leaving;
362};
363
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000364/* See test_ptr_hack.c */
365extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000366
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000367#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000368#define args (S.args )
369#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000370#define group_array (S.group_array )
371#define ngroups (S.ngroups )
372#define leaving (S.leaving )
373
374#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000375 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000376 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000377} while (0)
378#define DEINIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000379 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000380} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000381
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000382static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000383
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000384static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000385static void syntax(const char *op, const char *msg)
386{
387 if (op && *op) {
388 bb_error_msg("%s: %s", op, msg);
389 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000390 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000391 }
392 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000393}
394
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000395/* atoi with error detection */
396//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000397static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000398{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000399 char *p;
400#if ENABLE_FEATURE_TEST_64
401 long long r;
402#else
403 long r;
404#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000405
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000406 errno = 0;
407#if ENABLE_FEATURE_TEST_64
408 r = strtoll(s, &p, 10);
409#else
410 r = strtol(s, &p, 10);
411#endif
412
413 if (errno != 0)
414 syntax(s, "out of range");
415
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200416 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000417 syntax(s, "bad number");
418
419 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000420}
421
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000422/* UNUSED
423static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000424{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000425 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000426
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000427 return (stat(f1, &b1) == 0 &&
428 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000429}
430
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000431static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000432{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000433 struct stat b1, b2;
434
435 return (stat(f1, &b1) == 0 &&
436 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000437}
438
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000439static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000440{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000441 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000442
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000443 return (stat(f1, &b1) == 0 &&
444 stat(f2, &b2) == 0 &&
445 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000446}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000447*/
448
449
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200450static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000451{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000452 static const struct operator_t no_op = {
453 .op_num = -1,
454 .op_type = -1
455 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200456 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000457
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000458 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200459 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000460 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200461 n = index_in_strings(ops_texts, s);
462 if (n < 0)
463 return OPERAND;
464 last_operator = &ops_table[n];
465 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000466}
467
Erik Andersen13456d12000-03-16 08:09:57 +0000468
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000469static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000470{
471 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000472 const struct operator_t *op;
473 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000474
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000475 opnd1 = *args;
476 check_operator(*++args);
477 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000478
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000479 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000480 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200481 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000482
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000483 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000484 val1 = getn(opnd1);
485 val2 = getn(opnd2);
486 if (op->op_num == INTEQ)
487 return val1 == val2;
488 if (op->op_num == INTNE)
489 return val1 != val2;
490 if (op->op_num == INTGE)
491 return val1 >= val2;
492 if (op->op_num == INTGT)
493 return val1 > val2;
494 if (op->op_num == INTLE)
495 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200496 /*if (op->op_num == INTLT)*/
497 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000498 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000499 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000500 val1 = strcmp(opnd1, opnd2);
501 if (op->op_num == STREQ)
502 return val1 == 0;
503 if (op->op_num == STRNE)
504 return val1 != 0;
505 if (op->op_num == STRLT)
506 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200507 /*if (op->op_num == STRGT)*/
508 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000509 }
510 /* We are sure that these three are by now the only binops we didn't check
511 * yet, so we do not check if the class is correct:
512 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000513/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000514 {
515 struct stat b1, b2;
516
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000517 if (stat(opnd1, &b1) || stat(opnd2, &b2))
518 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000519 if (op->op_num == FILNT)
520 return b1.st_mtime > b2.st_mtime;
521 if (op->op_num == FILOT)
522 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200523 /*if (op->op_num == FILEQ)*/
524 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000525 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200526 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000527}
528
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000529
530static void initialize_group_array(void)
531{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200532 int n;
533
534 /* getgroups may be expensive, try to use it only once */
535 ngroups = 32;
536 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000537 /* FIXME: ash tries so hard to not die on OOM,
538 * and we spoil it with just one xrealloc here */
539 /* We realloc, because test_main can be entered repeatedly by shell.
540 * Testcase (ash): 'while true; do test -x some_file; done'
541 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200542 n = ngroups;
543 group_array = xrealloc(group_array, n * sizeof(gid_t));
544 ngroups = getgroups(n, group_array);
545 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000546}
547
548
549/* Return non-zero if GID is one that we have in our groups list. */
550//XXX: FIXME: duplicate of existing libbb function?
551// see toplevel TODO file:
552// possible code duplication ingroup() and is_a_group_member()
553static int is_a_group_member(gid_t gid)
554{
555 int i;
556
557 /* Short-circuit if possible, maybe saving a call to getgroups(). */
558 if (gid == getgid() || gid == getegid())
559 return 1;
560
561 if (ngroups == 0)
562 initialize_group_array();
563
564 /* Search through the list looking for GID. */
565 for (i = 0; i < ngroups; i++)
566 if (gid == group_array[i])
567 return 1;
568
569 return 0;
570}
571
572
573/* Do the same thing access(2) does, but use the effective uid and gid,
574 and don't make the mistake of telling root that any file is
575 executable. */
576static int test_eaccess(char *path, int mode)
577{
578 struct stat st;
579 unsigned int euid = geteuid();
580
581 if (stat(path, &st) < 0)
582 return -1;
583
584 if (euid == 0) {
585 /* Root can read or write any file. */
586 if (mode != X_OK)
587 return 0;
588
589 /* Root can execute any file that has any one of the execute
590 bits set. */
591 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
592 return 0;
593 }
594
595 if (st.st_uid == euid) /* owner */
596 mode <<= 6;
597 else if (is_a_group_member(st.st_gid))
598 mode <<= 3;
599
600 if (st.st_mode & mode)
601 return 0;
602
603 return -1;
604}
605
606
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000607static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000608{
609 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000610 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000611
612 if (mode == FILSYM) {
613#ifdef S_IFLNK
614 if (lstat(nm, &s) == 0) {
615 i = S_IFLNK;
616 goto filetype;
617 }
618#endif
619 return 0;
620 }
621
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000622 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000623 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000624 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000625 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000626 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000627 if (mode == FILRD)
628 i = R_OK;
629 if (mode == FILWR)
630 i = W_OK;
631 if (mode == FILEX)
632 i = X_OK;
633 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000634 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000635 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000636 if (mode == FILREG)
637 i = S_IFREG;
638 if (mode == FILDIR)
639 i = S_IFDIR;
640 if (mode == FILCDEV)
641 i = S_IFCHR;
642 if (mode == FILBDEV)
643 i = S_IFBLK;
644 if (mode == FILFIFO) {
645#ifdef S_IFIFO
646 i = S_IFIFO;
647#else
648 return 0;
649#endif
650 }
651 if (mode == FILSOCK) {
652#ifdef S_IFSOCK
653 i = S_IFSOCK;
654#else
655 return 0;
656#endif
657 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000658 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000659 return ((s.st_mode & S_IFMT) == i);
660 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000661 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000662 if (mode == FILSUID)
663 i = S_ISUID;
664 if (mode == FILSGID)
665 i = S_ISGID;
666 if (mode == FILSTCK)
667 i = S_ISVTX;
668 return ((s.st_mode & i) != 0);
669 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000670 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000671 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000672 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000673 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000674 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000675 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000676 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000677}
678
Erik Andersen13456d12000-03-16 08:09:57 +0000679
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000680static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000681{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000682 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000683
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000684 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
685 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200686 n = check_operator(*++args);
687 if (n == EOI) {
688 /* special case: [ ! ], [ a -a ! ] are valid */
689 /* IOW, "! ARG" may miss ARG */
690 unnest_msg("<nexpr:1 (!EOI)\n");
691 return 1;
692 }
693 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000694 unnest_msg("<nexpr:%lld\n", res);
695 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000696 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000697 res = primary(n);
698 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000699 return res;
700}
701
702
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000703static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000704{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000705 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000706
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000707 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
708 res = nexpr(n);
709 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
710 if (check_operator(*++args) == BAND) {
711 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
712 res = aexpr(check_operator(*++args)) && res;
713 unnest_msg("<aexpr:%lld\n", res);
714 return res;
715 }
716 args--;
717 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
718 return res;
719}
720
721
722static number_t oexpr(enum token n)
723{
724 number_t res;
725
726 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
727 res = aexpr(n);
728 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
729 if (check_operator(*++args) == BOR) {
730 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
731 res = oexpr(check_operator(*++args)) || res;
732 unnest_msg("<oexpr:%lld\n", res);
733 return res;
734 }
735 args--;
736 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
737 return res;
738}
739
740
741static number_t primary(enum token n)
742{
743#if TEST_DEBUG
744 number_t res = res; /* for compiler */
745#else
746 number_t res;
747#endif
748 const struct operator_t *args0_op;
749
750 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000751 if (n == EOI) {
752 syntax(NULL, "argument expected");
753 }
754 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000755 res = oexpr(check_operator(*++args));
756 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000757 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000758 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000759 return res;
760 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000761
762 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
763 * do the same */
764 args0_op = last_operator;
765 /* last_operator = operator at args[1] */
766 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
767 if (args[2]) {
768 // coreutils also does this:
769 // if (args[3] && args[0]="-l" && args[2] is BINOP)
770 // return binop(1 /* prepended by -l */);
771 if (last_operator->op_type == BINOP)
772 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
773 }
774 }
775 /* check "is args[0] unop?" second */
776 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000777 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000778 if (args[1] == NULL)
779// syntax(args0_op->op_text, "argument expected");
780 goto check_emptiness;
781 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000782 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000783 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000784 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000785 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000786 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000787 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
788 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000789 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000790
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000791 /*check_operator(args[1]); - already done */
792 if (last_operator->op_type == BINOP) {
793 /* args[2] is known to be NULL, isn't it bound to fail? */
794 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000795 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000796 check_emptiness:
797 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000798}
799
800
801int test_main(int argc, char **argv)
802{
803 int res;
804 const char *arg0;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000805// bool negate = 0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000806
807 arg0 = bb_basename(argv[0]);
808 if (arg0[0] == '[') {
809 --argc;
810 if (!arg0[1]) { /* "[" ? */
811 if (NOT_LONE_CHAR(argv[argc], ']')) {
812 bb_error_msg("missing ]");
813 return 2;
814 }
815 } else { /* assuming "[[" */
816 if (strcmp(argv[argc], "]]") != 0) {
817 bb_error_msg("missing ]]");
818 return 2;
819 }
Erik Andersen13456d12000-03-16 08:09:57 +0000820 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000821 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000822 }
823
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000824 /* We must do DEINIT_S() prior to returning */
825 INIT_S();
826
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000827 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000828 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000829 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000830
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000831 /* resetting ngroups is probably unnecessary. it will
832 * force a new call to getgroups(), which prevents using
833 * group data fetched during a previous call. but the
834 * only way the group data could be stale is if there's
835 * been an intervening call to setgroups(), and this
836 * isn't likely in the case of a shell. paranoia
837 * prevails...
838 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200839 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000840
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000841 //argc--;
842 argv++;
843
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000844 /* Implement special cases from POSIX.2, section 4.62.4 */
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000845 if (!argv[0]) { /* "test" */
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000846 res = 1;
847 goto ret;
848 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000849#if 0
850// Now it's fixed in the parser and should not be needed
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000851 if (LONE_CHAR(argv[0], '!') && argv[1]) {
852 negate = 1;
853 //argc--;
854 argv++;
855 }
856 if (!argv[1]) { /* "test [!] arg" */
857 res = (*argv[0] == '\0');
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000858 goto ret;
859 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000860 if (argv[2] && !argv[3]) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000861 check_operator(argv[1]);
862 if (last_operator->op_type == BINOP) {
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000863 /* "test [!] arg1 <binary_op> arg2" */
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200864 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000865 res = (binop() == 0);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000866 goto ret;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000867 }
868 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000869
870 /* Some complex expression. Undo '!' removal */
871 if (negate) {
872 negate = 0;
873 //argc++;
874 argv--;
875 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000876#endif
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200877 args = argv;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000878 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000879
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000880 if (*args != NULL && *++args != NULL) {
881 /* TODO: example when this happens? */
882 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000883 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000884 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000885 ret:
886 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000887// return negate ? !res : res;
888 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000889}