blob: edc625f57a4ba8986f911c661b579f4420101c44 [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//config:config TEST
23//config: bool "test"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020024//config: default y
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020025//config: help
26//config: test is used to check file types and compare values,
27//config: returning an appropriate exit code. The bash shell
28//config: has test built in, ash can build it in optionally.
29//config:
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010030//config:config TEST1
31//config: bool "test as ["
32//config: default y
33//config: help
34//config: Provide test command in the "[ EXPR ]" form
35//config:
36//config:config TEST2
37//config: bool "test as [["
38//config: default y
39//config: help
40//config: Provide test command in the "[[ EXPR ]]" form
41//config:
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020042//config:config FEATURE_TEST_64
43//config: bool "Extend test to 64 bit"
Denys Vlasenko2f32bf82010-06-06 04:14:28 +020044//config: default y
Denys Vlasenko265062d2017-01-10 15:13:30 +010045//config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
Denys Vlasenkoe4070cb2010-06-04 19:59:49 +020046//config: help
47//config: Enable 64-bit support in test.
48
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010049//applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
Denys Vlasenko265062d2017-01-10 15:13:30 +010050//applet:IF_TEST1(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
51//applet:IF_TEST2(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010052
Denys Vlasenko265062d2017-01-10 15:13:30 +010053//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
Denys Vlasenko15fb91c2016-11-23 18:31:48 +010054//kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
55//kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
Denys Vlasenko265062d2017-01-10 15:13:30 +010056
57//kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o
58//kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010059
Denys Vlasenkode5edad2015-04-21 16:00:41 +020060/* "test --help" is special-cased to ignore --help */
61//usage:#define test_trivial_usage NOUSAGE_STR
62//usage:#define test_full_usage ""
Pere Orga34425382011-03-31 14:43:25 +020063//usage:
64//usage:#define test_example_usage
65//usage: "$ test 1 -eq 2\n"
66//usage: "$ echo $?\n"
67//usage: "1\n"
68//usage: "$ test 1 -eq 1\n"
69//usage: "$ echo $?\n"
70//usage: "0\n"
71//usage: "$ [ -d /etc ]\n"
72//usage: "$ echo $?\n"
73//usage: "0\n"
74//usage: "$ [ -d /junk ]\n"
75//usage: "$ echo $?\n"
76//usage: "1\n"
77
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000078#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000079#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000080
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000081/* This is a NOFORK applet. Be very careful! */
82
83/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010084 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000085 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000086
Erik Andersen13456d12000-03-16 08:09:57 +000087/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020088 oexpr ::= aexpr | aexpr "-o" oexpr ;
89 aexpr ::= nexpr | nexpr "-a" aexpr ;
90 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000091 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000092 | operand binary-operator operand
93 | operand
94 | "(" oexpr ")"
95 ;
96 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
97 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
98
Mike Frysinger75ac42b2005-04-14 02:49:22 +000099 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +0000100 "-nt"|"-ot"|"-ef";
101 operand ::= <any legal UNIX file name>
102*/
103
Denys Vlasenko82a6fb32009-06-14 19:42:12 +0200104/* TODO: handle [[ expr ]] bashism bash-compatibly.
105 * [[ ]] is meant to be a "better [ ]", with less weird syntax
106 * and without the risk of variables and quoted strings misinterpreted
107 * as operators.
108 * This will require support from shells - we need to know quote status
109 * of each parameter (see below).
110 *
111 * Word splitting and pathname expansion should NOT be performed:
112 * # a="a b"; [[ $a = "a b" ]] && echo YES
113 * YES
114 * # [[ /bin/m* ]] && echo YES
115 * YES
116 *
117 * =~ should do regexp match
118 * = and == should do pattern match against right side:
119 * # [[ *a* == bab ]] && echo YES
120 * # [[ bab == *a* ]] && echo YES
121 * YES
122 * != does the negated == (i.e., also with pattern matching).
123 * Pattern matching is quotation-sensitive:
124 * # [[ bab == "b"a* ]] && echo YES
125 * YES
126 * # [[ bab == b"a*" ]] && echo YES
127 *
128 * Conditional operators such as -f must be unquoted literals to be recognized:
129 * # [[ -e /bin ]] && echo YES
130 * YES
131 * # [[ '-e' /bin ]] && echo YES
132 * bash: conditional binary operator expected...
133 * # A='-e'; [[ $A /bin ]] && echo YES
134 * bash: conditional binary operator expected...
135 *
136 * || and && should work as -o and -a work in [ ]
137 * -a and -o aren't recognized (&& and || are to be used instead)
138 * ( and ) do not need to be quoted unlike in [ ]:
139 * # [[ ( abc ) && '' ]] && echo YES
140 * # [[ ( abc ) || '' ]] && echo YES
141 * YES
142 * # [[ ( abc ) -o '' ]] && echo YES
143 * bash: syntax error in conditional expression...
144 *
145 * Apart from the above, [[ expr ]] should work as [ expr ]
146 */
147
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000148#define TEST_DEBUG 0
149
Erik Andersen13456d12000-03-16 08:09:57 +0000150enum token {
151 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200152
153 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000154 FILWR,
155 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200156
Erik Andersen13456d12000-03-16 08:09:57 +0000157 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200158
159 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000160 FILDIR,
161 FILCDEV,
162 FILBDEV,
163 FILFIFO,
164 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200165
Erik Andersen13456d12000-03-16 08:09:57 +0000166 FILSYM,
167 FILGZ,
168 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200169
170 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000171 FILSGID,
172 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200173
174 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000175 FILOT,
176 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200177
Erik Andersen13456d12000-03-16 08:09:57 +0000178 FILUID,
179 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200180
181 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000182 STRNZ,
183 STREQ,
184 STRNE,
185 STRLT,
186 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200187
188 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000189 INTNE,
190 INTGE,
191 INTGT,
192 INTLE,
193 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200194
Erik Andersen13456d12000-03-16 08:09:57 +0000195 UNOT,
196 BAND,
197 BOR,
198 LPAREN,
199 RPAREN,
200 OPERAND
201};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000202#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
203#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
204#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000205#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000206#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
207#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000208
209#if TEST_DEBUG
210int depth;
211#define nest_msg(...) do { \
212 depth++; \
213 fprintf(stderr, "%*s", depth*2, ""); \
214 fprintf(stderr, __VA_ARGS__); \
215} while (0)
216#define unnest_msg(...) do { \
217 fprintf(stderr, "%*s", depth*2, ""); \
218 fprintf(stderr, __VA_ARGS__); \
219 depth--; \
220} while (0)
221#define dbg_msg(...) do { \
222 fprintf(stderr, "%*s", depth*2, ""); \
223 fprintf(stderr, __VA_ARGS__); \
224} while (0)
225#define unnest_msg_and_return(expr, ...) do { \
226 number_t __res = (expr); \
227 fprintf(stderr, "%*s", depth*2, ""); \
228 fprintf(stderr, __VA_ARGS__, res); \
229 depth--; \
230 return __res; \
231} while (0)
232static const char *const TOKSTR[] = {
233 "EOI",
234 "FILRD",
235 "FILWR",
236 "FILEX",
237 "FILEXIST",
238 "FILREG",
239 "FILDIR",
240 "FILCDEV",
241 "FILBDEV",
242 "FILFIFO",
243 "FILSOCK",
244 "FILSYM",
245 "FILGZ",
246 "FILTT",
247 "FILSUID",
248 "FILSGID",
249 "FILSTCK",
250 "FILNT",
251 "FILOT",
252 "FILEQ",
253 "FILUID",
254 "FILGID",
255 "STREZ",
256 "STRNZ",
257 "STREQ",
258 "STRNE",
259 "STRLT",
260 "STRGT",
261 "INTEQ",
262 "INTNE",
263 "INTGE",
264 "INTGT",
265 "INTLE",
266 "INTLT",
267 "UNOT",
268 "BAND",
269 "BOR",
270 "LPAREN",
271 "RPAREN",
272 "OPERAND"
273};
274#else
275#define nest_msg(...) ((void)0)
276#define unnest_msg(...) ((void)0)
277#define dbg_msg(...) ((void)0)
278#define unnest_msg_and_return(expr, ...) return expr
279#endif
280
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200281enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000282 UNOP,
283 BINOP,
284 BUNOP,
285 BBINOP,
286 PAREN
287};
288
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000289struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000290 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000291};
292
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200293static const struct operator_t ops_table[] = {
294 { /* "-r" */ FILRD , UNOP },
295 { /* "-w" */ FILWR , UNOP },
296 { /* "-x" */ FILEX , UNOP },
297 { /* "-e" */ FILEXIST, UNOP },
298 { /* "-f" */ FILREG , UNOP },
299 { /* "-d" */ FILDIR , UNOP },
300 { /* "-c" */ FILCDEV , UNOP },
301 { /* "-b" */ FILBDEV , UNOP },
302 { /* "-p" */ FILFIFO , UNOP },
303 { /* "-u" */ FILSUID , UNOP },
304 { /* "-g" */ FILSGID , UNOP },
305 { /* "-k" */ FILSTCK , UNOP },
306 { /* "-s" */ FILGZ , UNOP },
307 { /* "-t" */ FILTT , UNOP },
308 { /* "-z" */ STREZ , UNOP },
309 { /* "-n" */ STRNZ , UNOP },
310 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000311
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200312 { /* "-O" */ FILUID , UNOP },
313 { /* "-G" */ FILGID , UNOP },
314 { /* "-L" */ FILSYM , UNOP },
315 { /* "-S" */ FILSOCK , UNOP },
316 { /* "=" */ STREQ , BINOP },
317 { /* "==" */ STREQ , BINOP },
318 { /* "!=" */ STRNE , BINOP },
319 { /* "<" */ STRLT , BINOP },
320 { /* ">" */ STRGT , BINOP },
321 { /* "-eq"*/ INTEQ , BINOP },
322 { /* "-ne"*/ INTNE , BINOP },
323 { /* "-ge"*/ INTGE , BINOP },
324 { /* "-gt"*/ INTGT , BINOP },
325 { /* "-le"*/ INTLE , BINOP },
326 { /* "-lt"*/ INTLT , BINOP },
327 { /* "-nt"*/ FILNT , BINOP },
328 { /* "-ot"*/ FILOT , BINOP },
329 { /* "-ef"*/ FILEQ , BINOP },
330 { /* "!" */ UNOT , BUNOP },
331 { /* "-a" */ BAND , BBINOP },
332 { /* "-o" */ BOR , BBINOP },
333 { /* "(" */ LPAREN , PAREN },
334 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000335};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200336/* Please keep these two tables in sync */
337static const char ops_texts[] ALIGN1 =
338 "-r" "\0"
339 "-w" "\0"
340 "-x" "\0"
341 "-e" "\0"
342 "-f" "\0"
343 "-d" "\0"
344 "-c" "\0"
345 "-b" "\0"
346 "-p" "\0"
347 "-u" "\0"
348 "-g" "\0"
349 "-k" "\0"
350 "-s" "\0"
351 "-t" "\0"
352 "-z" "\0"
353 "-n" "\0"
354 "-h" "\0"
355
356 "-O" "\0"
357 "-G" "\0"
358 "-L" "\0"
359 "-S" "\0"
360 "=" "\0"
361 "==" "\0"
362 "!=" "\0"
363 "<" "\0"
364 ">" "\0"
365 "-eq" "\0"
366 "-ne" "\0"
367 "-ge" "\0"
368 "-gt" "\0"
369 "-le" "\0"
370 "-lt" "\0"
371 "-nt" "\0"
372 "-ot" "\0"
373 "-ef" "\0"
374 "!" "\0"
375 "-a" "\0"
376 "-o" "\0"
377 "(" "\0"
378 ")" "\0"
379;
Erik Andersen13456d12000-03-16 08:09:57 +0000380
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000381
382#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000383typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000384#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000385typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000386#endif
387
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000388
389/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000390struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000391 char **args;
392 /* set only by check_operator(), either to bogus struct
393 * or points to matching operator_t struct. Never NULL. */
394 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000395 gid_t *group_array;
396 int ngroups;
397 jmp_buf leaving;
398};
399
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000400/* See test_ptr_hack.c */
401extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000402
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000403#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000404#define args (S.args )
405#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000406#define group_array (S.group_array )
407#define ngroups (S.ngroups )
408#define leaving (S.leaving )
409
410#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000411 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000412 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000413} while (0)
414#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200415 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000416 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000417} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000418
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000419static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000420
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000421static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000422static void syntax(const char *op, const char *msg)
423{
424 if (op && *op) {
425 bb_error_msg("%s: %s", op, msg);
426 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000427 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000428 }
429 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000430}
431
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000432/* atoi with error detection */
433//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000434static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000435{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000436 char *p;
437#if ENABLE_FEATURE_TEST_64
438 long long r;
439#else
440 long r;
441#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000442
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000443 errno = 0;
444#if ENABLE_FEATURE_TEST_64
445 r = strtoll(s, &p, 10);
446#else
447 r = strtol(s, &p, 10);
448#endif
449
450 if (errno != 0)
451 syntax(s, "out of range");
452
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200453 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000454 syntax(s, "bad number");
455
456 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000457}
458
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000459/* UNUSED
460static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000461{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000462 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000463
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000464 return (stat(f1, &b1) == 0 &&
465 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000466}
467
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000468static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000469{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000470 struct stat b1, b2;
471
472 return (stat(f1, &b1) == 0 &&
473 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000474}
475
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000476static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000477{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000478 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000479
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000480 return (stat(f1, &b1) == 0 &&
481 stat(f2, &b2) == 0 &&
482 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000483}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000484*/
485
486
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200487static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000488{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000489 static const struct operator_t no_op = {
490 .op_num = -1,
491 .op_type = -1
492 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200493 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000494
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000495 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200496 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000497 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200498 n = index_in_strings(ops_texts, s);
499 if (n < 0)
500 return OPERAND;
501 last_operator = &ops_table[n];
502 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000503}
504
Erik Andersen13456d12000-03-16 08:09:57 +0000505
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000506static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000507{
508 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000509 const struct operator_t *op;
510 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000511
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000512 opnd1 = *args;
513 check_operator(*++args);
514 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000515
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000516 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000517 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200518 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000519
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000520 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000521 val1 = getn(opnd1);
522 val2 = getn(opnd2);
523 if (op->op_num == INTEQ)
524 return val1 == val2;
525 if (op->op_num == INTNE)
526 return val1 != val2;
527 if (op->op_num == INTGE)
528 return val1 >= val2;
529 if (op->op_num == INTGT)
530 return val1 > val2;
531 if (op->op_num == INTLE)
532 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200533 /*if (op->op_num == INTLT)*/
534 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000535 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000536 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000537 val1 = strcmp(opnd1, opnd2);
538 if (op->op_num == STREQ)
539 return val1 == 0;
540 if (op->op_num == STRNE)
541 return val1 != 0;
542 if (op->op_num == STRLT)
543 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200544 /*if (op->op_num == STRGT)*/
545 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000546 }
547 /* We are sure that these three are by now the only binops we didn't check
548 * yet, so we do not check if the class is correct:
549 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000550/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000551 {
552 struct stat b1, b2;
553
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000554 if (stat(opnd1, &b1) || stat(opnd2, &b2))
555 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000556 if (op->op_num == FILNT)
557 return b1.st_mtime > b2.st_mtime;
558 if (op->op_num == FILOT)
559 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200560 /*if (op->op_num == FILEQ)*/
561 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000562 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200563 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000564}
565
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000566
567static void initialize_group_array(void)
568{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200569 int n;
570
571 /* getgroups may be expensive, try to use it only once */
572 ngroups = 32;
573 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000574 /* FIXME: ash tries so hard to not die on OOM,
575 * and we spoil it with just one xrealloc here */
576 /* We realloc, because test_main can be entered repeatedly by shell.
577 * Testcase (ash): 'while true; do test -x some_file; done'
578 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200579 n = ngroups;
580 group_array = xrealloc(group_array, n * sizeof(gid_t));
581 ngroups = getgroups(n, group_array);
582 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000583}
584
585
586/* Return non-zero if GID is one that we have in our groups list. */
587//XXX: FIXME: duplicate of existing libbb function?
588// see toplevel TODO file:
589// possible code duplication ingroup() and is_a_group_member()
590static int is_a_group_member(gid_t gid)
591{
592 int i;
593
594 /* Short-circuit if possible, maybe saving a call to getgroups(). */
595 if (gid == getgid() || gid == getegid())
596 return 1;
597
598 if (ngroups == 0)
599 initialize_group_array();
600
601 /* Search through the list looking for GID. */
602 for (i = 0; i < ngroups; i++)
603 if (gid == group_array[i])
604 return 1;
605
606 return 0;
607}
608
609
610/* Do the same thing access(2) does, but use the effective uid and gid,
611 and don't make the mistake of telling root that any file is
612 executable. */
613static int test_eaccess(char *path, int mode)
614{
615 struct stat st;
616 unsigned int euid = geteuid();
617
618 if (stat(path, &st) < 0)
619 return -1;
620
621 if (euid == 0) {
622 /* Root can read or write any file. */
623 if (mode != X_OK)
624 return 0;
625
626 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100627 * bits set. */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000628 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
629 return 0;
630 }
631
632 if (st.st_uid == euid) /* owner */
633 mode <<= 6;
634 else if (is_a_group_member(st.st_gid))
635 mode <<= 3;
636
637 if (st.st_mode & mode)
638 return 0;
639
640 return -1;
641}
642
643
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000644static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000645{
646 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000647 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000648
649 if (mode == FILSYM) {
650#ifdef S_IFLNK
651 if (lstat(nm, &s) == 0) {
652 i = S_IFLNK;
653 goto filetype;
654 }
655#endif
656 return 0;
657 }
658
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000659 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000660 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000661 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000662 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000663 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000664 if (mode == FILRD)
665 i = R_OK;
666 if (mode == FILWR)
667 i = W_OK;
668 if (mode == FILEX)
669 i = X_OK;
670 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000671 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000672 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000673 if (mode == FILREG)
674 i = S_IFREG;
675 if (mode == FILDIR)
676 i = S_IFDIR;
677 if (mode == FILCDEV)
678 i = S_IFCHR;
679 if (mode == FILBDEV)
680 i = S_IFBLK;
681 if (mode == FILFIFO) {
682#ifdef S_IFIFO
683 i = S_IFIFO;
684#else
685 return 0;
686#endif
687 }
688 if (mode == FILSOCK) {
689#ifdef S_IFSOCK
690 i = S_IFSOCK;
691#else
692 return 0;
693#endif
694 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000695 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000696 return ((s.st_mode & S_IFMT) == i);
697 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000698 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000699 if (mode == FILSUID)
700 i = S_ISUID;
701 if (mode == FILSGID)
702 i = S_ISGID;
703 if (mode == FILSTCK)
704 i = S_ISVTX;
705 return ((s.st_mode & i) != 0);
706 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000707 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000708 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000709 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000710 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000711 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000712 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000713 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000714}
715
Erik Andersen13456d12000-03-16 08:09:57 +0000716
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000717static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000718{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000719 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000720
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000721 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
722 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200723 n = check_operator(*++args);
724 if (n == EOI) {
725 /* special case: [ ! ], [ a -a ! ] are valid */
726 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100727 args--;
728 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200729 return 1;
730 }
731 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000732 unnest_msg("<nexpr:%lld\n", res);
733 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000734 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000735 res = primary(n);
736 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000737 return res;
738}
739
740
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000741static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000742{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000743 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000744
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000745 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
746 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100747 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000748 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100749 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000750 res = aexpr(check_operator(*++args)) && res;
751 unnest_msg("<aexpr:%lld\n", res);
752 return res;
753 }
754 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100755 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000756 return res;
757}
758
759
760static number_t oexpr(enum token n)
761{
762 number_t res;
763
764 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
765 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100766 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000767 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100768 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000769 res = oexpr(check_operator(*++args)) || res;
770 unnest_msg("<oexpr:%lld\n", res);
771 return res;
772 }
773 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100774 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000775 return res;
776}
777
778
779static number_t primary(enum token n)
780{
781#if TEST_DEBUG
782 number_t res = res; /* for compiler */
783#else
784 number_t res;
785#endif
786 const struct operator_t *args0_op;
787
788 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000789 if (n == EOI) {
790 syntax(NULL, "argument expected");
791 }
792 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000793 res = oexpr(check_operator(*++args));
794 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000795 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000796 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000797 return res;
798 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000799
800 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
801 * do the same */
802 args0_op = last_operator;
803 /* last_operator = operator at args[1] */
804 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
805 if (args[2]) {
806 // coreutils also does this:
807 // if (args[3] && args[0]="-l" && args[2] is BINOP)
808 // return binop(1 /* prepended by -l */);
809 if (last_operator->op_type == BINOP)
810 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
811 }
812 }
813 /* check "is args[0] unop?" second */
814 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000815 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000816 if (args[1] == NULL)
817// syntax(args0_op->op_text, "argument expected");
818 goto check_emptiness;
819 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000820 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000821 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000822 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000823 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000824 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000825 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
826 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000827 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000828
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000829 /*check_operator(args[1]); - already done */
830 if (last_operator->op_type == BINOP) {
831 /* args[2] is known to be NULL, isn't it bound to fail? */
832 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000833 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000834 check_emptiness:
835 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000836}
837
838
839int test_main(int argc, char **argv)
840{
841 int res;
842 const char *arg0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000843
844 arg0 = bb_basename(argv[0]);
Denys Vlasenko265062d2017-01-10 15:13:30 +0100845 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100846 && (arg0[0] == '[')
847 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000848 --argc;
849 if (!arg0[1]) { /* "[" ? */
850 if (NOT_LONE_CHAR(argv[argc], ']')) {
851 bb_error_msg("missing ]");
852 return 2;
853 }
854 } else { /* assuming "[[" */
855 if (strcmp(argv[argc], "]]") != 0) {
856 bb_error_msg("missing ]]");
857 return 2;
858 }
Erik Andersen13456d12000-03-16 08:09:57 +0000859 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000860 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000861 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200862 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000863
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000864 /* We must do DEINIT_S() prior to returning */
865 INIT_S();
866
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000867 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000868 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000869 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000870
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000871 /* resetting ngroups is probably unnecessary. it will
872 * force a new call to getgroups(), which prevents using
873 * group data fetched during a previous call. but the
874 * only way the group data could be stale is if there's
875 * been an intervening call to setgroups(), and this
876 * isn't likely in the case of a shell. paranoia
877 * prevails...
878 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200879 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000880
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000881 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200882 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000883
Denys Vlasenko98654b92014-07-01 14:16:28 +0200884 /* Implement special cases from POSIX.2, section 4.62.4.
885 * Testcase: "test '(' = '('"
886 * The general parser would misinterpret '(' as group start.
887 */
888 if (1) {
889 int negate = 0;
890 again:
891 if (!argv[0]) {
892 /* "test" */
893 res = 1;
894 goto ret_special;
895 }
896 if (!argv[1]) {
897 /* "test [!] arg" */
898 res = (argv[0][0] == '\0');
899 goto ret_special;
900 }
901 if (argv[2] && !argv[3]) {
902 check_operator(argv[1]);
903 if (last_operator->op_type == BINOP) {
904 /* "test [!] arg1 <binary_op> arg2" */
905 args = argv;
906 res = (binop() == 0);
907 ret_special:
908 /* If there was leading "!" op... */
909 res ^= negate;
910 goto ret;
911 }
912 }
913 if (LONE_CHAR(argv[0], '!')) {
914 argv++;
915 negate ^= 1;
916 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000917 }
918 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000919
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000920 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000921
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000922 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100923 /* Examples:
924 * test 3 -lt 5 6
925 * test -t 1 2
926 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000927 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000928 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000929 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000930 ret:
931 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000932 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000933}