blob: 9e18ee986aa1947dca18a49092e4fff247207e87 [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 Vlasenko15fb91c2016-11-23 18:31:48 +010045//config: depends on TEST || TEST1 || TEST2 || ASH_BUILTIN_TEST || HUSH
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 Vlasenko15fb91c2016-11-23 18:31:48 +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
53//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
56//kbuild:lib-$(CONFIG_ASH_BUILTIN_TEST) += test.o test_ptr_hack.o
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010057//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
58
Denys Vlasenkode5edad2015-04-21 16:00:41 +020059/* "test --help" is special-cased to ignore --help */
60//usage:#define test_trivial_usage NOUSAGE_STR
61//usage:#define test_full_usage ""
Pere Orga34425382011-03-31 14:43:25 +020062//usage:
63//usage:#define test_example_usage
64//usage: "$ test 1 -eq 2\n"
65//usage: "$ echo $?\n"
66//usage: "1\n"
67//usage: "$ test 1 -eq 1\n"
68//usage: "$ echo $?\n"
69//usage: "0\n"
70//usage: "$ [ -d /etc ]\n"
71//usage: "$ echo $?\n"
72//usage: "0\n"
73//usage: "$ [ -d /junk ]\n"
74//usage: "$ echo $?\n"
75//usage: "1\n"
76
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000077#include "libbb.h"
Paul Fox6ab03782006-06-08 21:37:26 +000078#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000079
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000080/* This is a NOFORK applet. Be very careful! */
81
82/* test_main() is called from shells, and we need to be extra careful here.
Denys Vlasenko86cf0362011-03-08 12:44:02 +010083 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
Denis Vlasenko6672c8e2007-11-30 07:29:05 +000084 * state. */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000085
Erik Andersen13456d12000-03-16 08:09:57 +000086/* test(1) accepts the following grammar:
Denys Vlasenkofb132e42010-10-29 11:46:52 +020087 oexpr ::= aexpr | aexpr "-o" oexpr ;
88 aexpr ::= nexpr | nexpr "-a" aexpr ;
89 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000090 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000091 | operand binary-operator operand
92 | operand
93 | "(" oexpr ")"
94 ;
95 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
96 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
97
Mike Frysinger75ac42b2005-04-14 02:49:22 +000098 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000099 "-nt"|"-ot"|"-ef";
100 operand ::= <any legal UNIX file name>
101*/
102
Denys Vlasenko82a6fb32009-06-14 19:42:12 +0200103/* TODO: handle [[ expr ]] bashism bash-compatibly.
104 * [[ ]] is meant to be a "better [ ]", with less weird syntax
105 * and without the risk of variables and quoted strings misinterpreted
106 * as operators.
107 * This will require support from shells - we need to know quote status
108 * of each parameter (see below).
109 *
110 * Word splitting and pathname expansion should NOT be performed:
111 * # a="a b"; [[ $a = "a b" ]] && echo YES
112 * YES
113 * # [[ /bin/m* ]] && echo YES
114 * YES
115 *
116 * =~ should do regexp match
117 * = and == should do pattern match against right side:
118 * # [[ *a* == bab ]] && echo YES
119 * # [[ bab == *a* ]] && echo YES
120 * YES
121 * != does the negated == (i.e., also with pattern matching).
122 * Pattern matching is quotation-sensitive:
123 * # [[ bab == "b"a* ]] && echo YES
124 * YES
125 * # [[ bab == b"a*" ]] && echo YES
126 *
127 * Conditional operators such as -f must be unquoted literals to be recognized:
128 * # [[ -e /bin ]] && echo YES
129 * YES
130 * # [[ '-e' /bin ]] && echo YES
131 * bash: conditional binary operator expected...
132 * # A='-e'; [[ $A /bin ]] && echo YES
133 * bash: conditional binary operator expected...
134 *
135 * || and && should work as -o and -a work in [ ]
136 * -a and -o aren't recognized (&& and || are to be used instead)
137 * ( and ) do not need to be quoted unlike in [ ]:
138 * # [[ ( abc ) && '' ]] && echo YES
139 * # [[ ( abc ) || '' ]] && echo YES
140 * YES
141 * # [[ ( abc ) -o '' ]] && echo YES
142 * bash: syntax error in conditional expression...
143 *
144 * Apart from the above, [[ expr ]] should work as [ expr ]
145 */
146
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000147#define TEST_DEBUG 0
148
Erik Andersen13456d12000-03-16 08:09:57 +0000149enum token {
150 EOI,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200151
152 FILRD, /* file access */
Erik Andersen13456d12000-03-16 08:09:57 +0000153 FILWR,
154 FILEX,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200155
Erik Andersen13456d12000-03-16 08:09:57 +0000156 FILEXIST,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200157
158 FILREG, /* file type */
Erik Andersen13456d12000-03-16 08:09:57 +0000159 FILDIR,
160 FILCDEV,
161 FILBDEV,
162 FILFIFO,
163 FILSOCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200164
Erik Andersen13456d12000-03-16 08:09:57 +0000165 FILSYM,
166 FILGZ,
167 FILTT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200168
169 FILSUID, /* file bit */
Erik Andersen13456d12000-03-16 08:09:57 +0000170 FILSGID,
171 FILSTCK,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200172
173 FILNT, /* file ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000174 FILOT,
175 FILEQ,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200176
Erik Andersen13456d12000-03-16 08:09:57 +0000177 FILUID,
178 FILGID,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200179
180 STREZ, /* str ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000181 STRNZ,
182 STREQ,
183 STRNE,
184 STRLT,
185 STRGT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200186
187 INTEQ, /* int ops */
Erik Andersen13456d12000-03-16 08:09:57 +0000188 INTNE,
189 INTGE,
190 INTGT,
191 INTLE,
192 INTLT,
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200193
Erik Andersen13456d12000-03-16 08:09:57 +0000194 UNOT,
195 BAND,
196 BOR,
197 LPAREN,
198 RPAREN,
199 OPERAND
200};
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000201#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
202#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
203#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000204#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000205#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
206#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000207
208#if TEST_DEBUG
209int depth;
210#define nest_msg(...) do { \
211 depth++; \
212 fprintf(stderr, "%*s", depth*2, ""); \
213 fprintf(stderr, __VA_ARGS__); \
214} while (0)
215#define unnest_msg(...) do { \
216 fprintf(stderr, "%*s", depth*2, ""); \
217 fprintf(stderr, __VA_ARGS__); \
218 depth--; \
219} while (0)
220#define dbg_msg(...) do { \
221 fprintf(stderr, "%*s", depth*2, ""); \
222 fprintf(stderr, __VA_ARGS__); \
223} while (0)
224#define unnest_msg_and_return(expr, ...) do { \
225 number_t __res = (expr); \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__, res); \
228 depth--; \
229 return __res; \
230} while (0)
231static const char *const TOKSTR[] = {
232 "EOI",
233 "FILRD",
234 "FILWR",
235 "FILEX",
236 "FILEXIST",
237 "FILREG",
238 "FILDIR",
239 "FILCDEV",
240 "FILBDEV",
241 "FILFIFO",
242 "FILSOCK",
243 "FILSYM",
244 "FILGZ",
245 "FILTT",
246 "FILSUID",
247 "FILSGID",
248 "FILSTCK",
249 "FILNT",
250 "FILOT",
251 "FILEQ",
252 "FILUID",
253 "FILGID",
254 "STREZ",
255 "STRNZ",
256 "STREQ",
257 "STRNE",
258 "STRLT",
259 "STRGT",
260 "INTEQ",
261 "INTNE",
262 "INTGE",
263 "INTGT",
264 "INTLE",
265 "INTLT",
266 "UNOT",
267 "BAND",
268 "BOR",
269 "LPAREN",
270 "RPAREN",
271 "OPERAND"
272};
273#else
274#define nest_msg(...) ((void)0)
275#define unnest_msg(...) ((void)0)
276#define dbg_msg(...) ((void)0)
277#define unnest_msg_and_return(expr, ...) return expr
278#endif
279
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200280enum {
Erik Andersen13456d12000-03-16 08:09:57 +0000281 UNOP,
282 BINOP,
283 BUNOP,
284 BBINOP,
285 PAREN
286};
287
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000288struct operator_t {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000289 unsigned char op_num, op_type;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000290};
291
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200292static const struct operator_t ops_table[] = {
293 { /* "-r" */ FILRD , UNOP },
294 { /* "-w" */ FILWR , UNOP },
295 { /* "-x" */ FILEX , UNOP },
296 { /* "-e" */ FILEXIST, UNOP },
297 { /* "-f" */ FILREG , UNOP },
298 { /* "-d" */ FILDIR , UNOP },
299 { /* "-c" */ FILCDEV , UNOP },
300 { /* "-b" */ FILBDEV , UNOP },
301 { /* "-p" */ FILFIFO , UNOP },
302 { /* "-u" */ FILSUID , UNOP },
303 { /* "-g" */ FILSGID , UNOP },
304 { /* "-k" */ FILSTCK , UNOP },
305 { /* "-s" */ FILGZ , UNOP },
306 { /* "-t" */ FILTT , UNOP },
307 { /* "-z" */ STREZ , UNOP },
308 { /* "-n" */ STRNZ , UNOP },
309 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000310
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200311 { /* "-O" */ FILUID , UNOP },
312 { /* "-G" */ FILGID , UNOP },
313 { /* "-L" */ FILSYM , UNOP },
314 { /* "-S" */ FILSOCK , UNOP },
315 { /* "=" */ STREQ , BINOP },
316 { /* "==" */ STREQ , BINOP },
317 { /* "!=" */ STRNE , BINOP },
318 { /* "<" */ STRLT , BINOP },
319 { /* ">" */ STRGT , BINOP },
320 { /* "-eq"*/ INTEQ , BINOP },
321 { /* "-ne"*/ INTNE , BINOP },
322 { /* "-ge"*/ INTGE , BINOP },
323 { /* "-gt"*/ INTGT , BINOP },
324 { /* "-le"*/ INTLE , BINOP },
325 { /* "-lt"*/ INTLT , BINOP },
326 { /* "-nt"*/ FILNT , BINOP },
327 { /* "-ot"*/ FILOT , BINOP },
328 { /* "-ef"*/ FILEQ , BINOP },
329 { /* "!" */ UNOT , BUNOP },
330 { /* "-a" */ BAND , BBINOP },
331 { /* "-o" */ BOR , BBINOP },
332 { /* "(" */ LPAREN , PAREN },
333 { /* ")" */ RPAREN , PAREN },
Erik Andersen13456d12000-03-16 08:09:57 +0000334};
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200335/* Please keep these two tables in sync */
336static const char ops_texts[] ALIGN1 =
337 "-r" "\0"
338 "-w" "\0"
339 "-x" "\0"
340 "-e" "\0"
341 "-f" "\0"
342 "-d" "\0"
343 "-c" "\0"
344 "-b" "\0"
345 "-p" "\0"
346 "-u" "\0"
347 "-g" "\0"
348 "-k" "\0"
349 "-s" "\0"
350 "-t" "\0"
351 "-z" "\0"
352 "-n" "\0"
353 "-h" "\0"
354
355 "-O" "\0"
356 "-G" "\0"
357 "-L" "\0"
358 "-S" "\0"
359 "=" "\0"
360 "==" "\0"
361 "!=" "\0"
362 "<" "\0"
363 ">" "\0"
364 "-eq" "\0"
365 "-ne" "\0"
366 "-ge" "\0"
367 "-gt" "\0"
368 "-le" "\0"
369 "-lt" "\0"
370 "-nt" "\0"
371 "-ot" "\0"
372 "-ef" "\0"
373 "!" "\0"
374 "-a" "\0"
375 "-o" "\0"
376 "(" "\0"
377 ")" "\0"
378;
Erik Andersen13456d12000-03-16 08:09:57 +0000379
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000380
381#if ENABLE_FEATURE_TEST_64
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000382typedef int64_t number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000383#else
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000384typedef int number_t;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000385#endif
386
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000387
388/* We try to minimize both static and stack usage. */
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000389struct test_statics {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000390 char **args;
391 /* set only by check_operator(), either to bogus struct
392 * or points to matching operator_t struct. Never NULL. */
393 const struct operator_t *last_operator;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000394 gid_t *group_array;
395 int ngroups;
396 jmp_buf leaving;
397};
398
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000399/* See test_ptr_hack.c */
400extern struct test_statics *const test_ptr_to_statics;
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000401
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000402#define S (*test_ptr_to_statics)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000403#define args (S.args )
404#define last_operator (S.last_operator)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000405#define group_array (S.group_array )
406#define ngroups (S.ngroups )
407#define leaving (S.leaving )
408
409#define INIT_S() do { \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000410 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000411 barrier(); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000412} while (0)
413#define DEINIT_S() do { \
Denys Vlasenko42eeb252016-10-02 02:35:13 +0200414 free(group_array); \
Denis Vlasenko5d89fba2008-04-22 00:08:27 +0000415 free(test_ptr_to_statics); \
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000416} while (0)
Erik Andersen13456d12000-03-16 08:09:57 +0000417
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000418static number_t primary(enum token n);
Erik Andersen13456d12000-03-16 08:09:57 +0000419
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000420static void syntax(const char *op, const char *msg) NORETURN;
Paul Fox6ab03782006-06-08 21:37:26 +0000421static void syntax(const char *op, const char *msg)
422{
423 if (op && *op) {
424 bb_error_msg("%s: %s", op, msg);
425 } else {
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000426 bb_error_msg("%s: %s"+4, msg);
Paul Fox6ab03782006-06-08 21:37:26 +0000427 }
428 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000429}
430
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000431/* atoi with error detection */
432//XXX: FIXME: duplicate of existing libbb function?
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000433static number_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000434{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000435 char *p;
436#if ENABLE_FEATURE_TEST_64
437 long long r;
438#else
439 long r;
440#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000441
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000442 errno = 0;
443#if ENABLE_FEATURE_TEST_64
444 r = strtoll(s, &p, 10);
445#else
446 r = strtol(s, &p, 10);
447#endif
448
449 if (errno != 0)
450 syntax(s, "out of range");
451
Denys Vlasenko3e47cfe2010-06-03 01:47:04 +0200452 if (p == s || *(skip_whitespace(p)) != '\0')
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000453 syntax(s, "bad number");
454
455 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000456}
457
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000458/* UNUSED
459static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000460{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000461 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000462
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000463 return (stat(f1, &b1) == 0 &&
464 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000465}
466
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000467static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000468{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000469 struct stat b1, b2;
470
471 return (stat(f1, &b1) == 0 &&
472 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000473}
474
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000475static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000476{
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000477 struct stat b1, b2;
Erik Andersen13456d12000-03-16 08:09:57 +0000478
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000479 return (stat(f1, &b1) == 0 &&
480 stat(f2, &b2) == 0 &&
481 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000482}
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000483*/
484
485
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200486static enum token check_operator(const char *s)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000487{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000488 static const struct operator_t no_op = {
489 .op_num = -1,
490 .op_type = -1
491 };
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200492 int n;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000493
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000494 last_operator = &no_op;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200495 if (s == NULL)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000496 return EOI;
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200497 n = index_in_strings(ops_texts, s);
498 if (n < 0)
499 return OPERAND;
500 last_operator = &ops_table[n];
501 return ops_table[n].op_num;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000502}
503
Erik Andersen13456d12000-03-16 08:09:57 +0000504
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000505static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000506{
507 const char *opnd1, *opnd2;
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000508 const struct operator_t *op;
509 number_t val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000510
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000511 opnd1 = *args;
512 check_operator(*++args);
513 op = last_operator;
Erik Andersen13456d12000-03-16 08:09:57 +0000514
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000515 opnd2 = *++args;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000516 if (opnd2 == NULL)
Denys Vlasenkoc541a892009-09-12 22:41:57 +0200517 syntax(args[-1], "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000518
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000519 if (is_int_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000520 val1 = getn(opnd1);
521 val2 = getn(opnd2);
522 if (op->op_num == INTEQ)
523 return val1 == val2;
524 if (op->op_num == INTNE)
525 return val1 != val2;
526 if (op->op_num == INTGE)
527 return val1 >= val2;
528 if (op->op_num == INTGT)
529 return val1 > val2;
530 if (op->op_num == INTLE)
531 return val1 <= val2;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200532 /*if (op->op_num == INTLT)*/
533 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000534 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000535 if (is_str_op(op->op_num)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000536 val1 = strcmp(opnd1, opnd2);
537 if (op->op_num == STREQ)
538 return val1 == 0;
539 if (op->op_num == STRNE)
540 return val1 != 0;
541 if (op->op_num == STRLT)
542 return val1 < 0;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200543 /*if (op->op_num == STRGT)*/
544 return val1 > 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000545 }
546 /* We are sure that these three are by now the only binops we didn't check
547 * yet, so we do not check if the class is correct:
548 */
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000549/* if (is_file_op(op->op_num)) */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000550 {
551 struct stat b1, b2;
552
Denis Vlasenkofe3e1772007-05-27 03:39:50 +0000553 if (stat(opnd1, &b1) || stat(opnd2, &b2))
554 return 0; /* false, since at least one stat failed */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000555 if (op->op_num == FILNT)
556 return b1.st_mtime > b2.st_mtime;
557 if (op->op_num == FILOT)
558 return b1.st_mtime < b2.st_mtime;
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200559 /*if (op->op_num == FILEQ)*/
560 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000561 }
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200562 /*return 1; - NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000563}
564
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000565
566static void initialize_group_array(void)
567{
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200568 int n;
569
570 /* getgroups may be expensive, try to use it only once */
571 ngroups = 32;
572 do {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000573 /* FIXME: ash tries so hard to not die on OOM,
574 * and we spoil it with just one xrealloc here */
575 /* We realloc, because test_main can be entered repeatedly by shell.
576 * Testcase (ash): 'while true; do test -x some_file; done'
577 * and watch top. (some_file must have owner != you) */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200578 n = ngroups;
579 group_array = xrealloc(group_array, n * sizeof(gid_t));
580 ngroups = getgroups(n, group_array);
581 } while (ngroups > n);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000582}
583
584
585/* Return non-zero if GID is one that we have in our groups list. */
586//XXX: FIXME: duplicate of existing libbb function?
587// see toplevel TODO file:
588// possible code duplication ingroup() and is_a_group_member()
589static int is_a_group_member(gid_t gid)
590{
591 int i;
592
593 /* Short-circuit if possible, maybe saving a call to getgroups(). */
594 if (gid == getgid() || gid == getegid())
595 return 1;
596
597 if (ngroups == 0)
598 initialize_group_array();
599
600 /* Search through the list looking for GID. */
601 for (i = 0; i < ngroups; i++)
602 if (gid == group_array[i])
603 return 1;
604
605 return 0;
606}
607
608
609/* Do the same thing access(2) does, but use the effective uid and gid,
610 and don't make the mistake of telling root that any file is
611 executable. */
612static int test_eaccess(char *path, int mode)
613{
614 struct stat st;
615 unsigned int euid = geteuid();
616
617 if (stat(path, &st) < 0)
618 return -1;
619
620 if (euid == 0) {
621 /* Root can read or write any file. */
622 if (mode != X_OK)
623 return 0;
624
625 /* Root can execute any file that has any one of the execute
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100626 * bits set. */
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000627 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
628 return 0;
629 }
630
631 if (st.st_uid == euid) /* owner */
632 mode <<= 6;
633 else if (is_a_group_member(st.st_gid))
634 mode <<= 3;
635
636 if (st.st_mode & mode)
637 return 0;
638
639 return -1;
640}
641
642
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000643static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000644{
645 struct stat s;
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000646 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
Erik Andersen13456d12000-03-16 08:09:57 +0000647
648 if (mode == FILSYM) {
649#ifdef S_IFLNK
650 if (lstat(nm, &s) == 0) {
651 i = S_IFLNK;
652 goto filetype;
653 }
654#endif
655 return 0;
656 }
657
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000658 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000659 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000660 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000661 return 1;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000662 if (is_file_access(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000663 if (mode == FILRD)
664 i = R_OK;
665 if (mode == FILWR)
666 i = W_OK;
667 if (mode == FILEX)
668 i = X_OK;
669 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000670 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000671 if (is_file_type(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000672 if (mode == FILREG)
673 i = S_IFREG;
674 if (mode == FILDIR)
675 i = S_IFDIR;
676 if (mode == FILCDEV)
677 i = S_IFCHR;
678 if (mode == FILBDEV)
679 i = S_IFBLK;
680 if (mode == FILFIFO) {
681#ifdef S_IFIFO
682 i = S_IFIFO;
683#else
684 return 0;
685#endif
686 }
687 if (mode == FILSOCK) {
688#ifdef S_IFSOCK
689 i = S_IFSOCK;
690#else
691 return 0;
692#endif
693 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000694 filetype:
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000695 return ((s.st_mode & S_IFMT) == i);
696 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000697 if (is_file_bit(mode)) {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000698 if (mode == FILSUID)
699 i = S_ISUID;
700 if (mode == FILSGID)
701 i = S_ISGID;
702 if (mode == FILSTCK)
703 i = S_ISVTX;
704 return ((s.st_mode & i) != 0);
705 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000706 if (mode == FILGZ)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000707 return s.st_size > 0L;
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000708 if (mode == FILUID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000709 return s.st_uid == geteuid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000710 if (mode == FILGID)
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000711 return s.st_gid == getegid();
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000712 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000713}
714
Erik Andersen13456d12000-03-16 08:09:57 +0000715
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000716static number_t nexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000717{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000718 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000719
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000720 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
721 if (n == UNOT) {
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200722 n = check_operator(*++args);
723 if (n == EOI) {
724 /* special case: [ ! ], [ a -a ! ] are valid */
725 /* IOW, "! ARG" may miss ARG */
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100726 args--;
727 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
Denys Vlasenkod23f64e2009-07-17 00:59:26 +0200728 return 1;
729 }
730 res = !nexpr(n);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000731 unnest_msg("<nexpr:%lld\n", res);
732 return res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000733 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000734 res = primary(n);
735 unnest_msg("<nexpr:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000736 return res;
737}
738
739
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000740static number_t aexpr(enum token n)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000741{
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000742 number_t res;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000743
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000744 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
745 res = nexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100746 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000747 if (check_operator(*++args) == BAND) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100748 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000749 res = aexpr(check_operator(*++args)) && res;
750 unnest_msg("<aexpr:%lld\n", res);
751 return res;
752 }
753 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100754 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000755 return res;
756}
757
758
759static number_t oexpr(enum token n)
760{
761 number_t res;
762
763 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
764 res = aexpr(n);
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100765 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000766 if (check_operator(*++args) == BOR) {
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100767 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000768 res = oexpr(check_operator(*++args)) || res;
769 unnest_msg("<oexpr:%lld\n", res);
770 return res;
771 }
772 args--;
Denys Vlasenko07fcaab2012-03-08 03:50:01 +0100773 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000774 return res;
775}
776
777
778static number_t primary(enum token n)
779{
780#if TEST_DEBUG
781 number_t res = res; /* for compiler */
782#else
783 number_t res;
784#endif
785 const struct operator_t *args0_op;
786
787 nest_msg(">primary(%s)\n", TOKSTR[n]);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000788 if (n == EOI) {
789 syntax(NULL, "argument expected");
790 }
791 if (n == LPAREN) {
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000792 res = oexpr(check_operator(*++args));
793 if (check_operator(*++args) != RPAREN)
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000794 syntax(NULL, "closing paren expected");
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000795 unnest_msg("<primary:%lld\n", res);
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000796 return res;
797 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000798
799 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
800 * do the same */
801 args0_op = last_operator;
802 /* last_operator = operator at args[1] */
803 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
804 if (args[2]) {
805 // coreutils also does this:
806 // if (args[3] && args[0]="-l" && args[2] is BINOP)
807 // return binop(1 /* prepended by -l */);
808 if (last_operator->op_type == BINOP)
809 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
810 }
811 }
812 /* check "is args[0] unop?" second */
813 if (args0_op->op_type == UNOP) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000814 /* unary expression */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000815 if (args[1] == NULL)
816// syntax(args0_op->op_text, "argument expected");
817 goto check_emptiness;
818 args++;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000819 if (n == STREZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000820 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000821 if (n == STRNZ)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000822 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000823 if (n == FILTT)
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000824 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
825 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
Erik Andersen13456d12000-03-16 08:09:57 +0000826 }
Denis Vlasenkodcf4de22007-05-01 20:07:29 +0000827
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000828 /*check_operator(args[1]); - already done */
829 if (last_operator->op_type == BINOP) {
830 /* args[2] is known to be NULL, isn't it bound to fail? */
831 unnest_msg_and_return(binop(), "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000832 }
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000833 check_emptiness:
834 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000835}
836
837
838int test_main(int argc, char **argv)
839{
840 int res;
841 const char *arg0;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000842
843 arg0 = bb_basename(argv[0]);
Denys Vlasenko15fb91c2016-11-23 18:31:48 +0100844 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_BUILTIN_TEST || ENABLE_HUSH)
845 && (arg0[0] == '[')
846 ) {
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000847 --argc;
848 if (!arg0[1]) { /* "[" ? */
849 if (NOT_LONE_CHAR(argv[argc], ']')) {
850 bb_error_msg("missing ]");
851 return 2;
852 }
853 } else { /* assuming "[[" */
854 if (strcmp(argv[argc], "]]") != 0) {
855 bb_error_msg("missing ]]");
856 return 2;
857 }
Erik Andersen13456d12000-03-16 08:09:57 +0000858 }
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000859 argv[argc] = NULL;
Erik Andersen13456d12000-03-16 08:09:57 +0000860 }
Denys Vlasenko98654b92014-07-01 14:16:28 +0200861 /* argc is unused after this point */
Erik Andersen13456d12000-03-16 08:09:57 +0000862
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000863 /* We must do DEINIT_S() prior to returning */
864 INIT_S();
865
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000866 res = setjmp(leaving);
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000867 if (res)
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000868 goto ret;
Erik Andersen13456d12000-03-16 08:09:57 +0000869
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000870 /* resetting ngroups is probably unnecessary. it will
871 * force a new call to getgroups(), which prevents using
872 * group data fetched during a previous call. but the
873 * only way the group data could be stale is if there's
874 * been an intervening call to setgroups(), and this
875 * isn't likely in the case of a shell. paranoia
876 * prevails...
877 */
Denys Vlasenko16635cc2009-06-13 22:49:08 +0200878 /*ngroups = 0; - done by INIT_S() */
Erik Andersen13456d12000-03-16 08:09:57 +0000879
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000880 argv++;
Denys Vlasenko98654b92014-07-01 14:16:28 +0200881 args = argv;
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000882
Denys Vlasenko98654b92014-07-01 14:16:28 +0200883 /* Implement special cases from POSIX.2, section 4.62.4.
884 * Testcase: "test '(' = '('"
885 * The general parser would misinterpret '(' as group start.
886 */
887 if (1) {
888 int negate = 0;
889 again:
890 if (!argv[0]) {
891 /* "test" */
892 res = 1;
893 goto ret_special;
894 }
895 if (!argv[1]) {
896 /* "test [!] arg" */
897 res = (argv[0][0] == '\0');
898 goto ret_special;
899 }
900 if (argv[2] && !argv[3]) {
901 check_operator(argv[1]);
902 if (last_operator->op_type == BINOP) {
903 /* "test [!] arg1 <binary_op> arg2" */
904 args = argv;
905 res = (binop() == 0);
906 ret_special:
907 /* If there was leading "!" op... */
908 res ^= negate;
909 goto ret;
910 }
911 }
912 if (LONE_CHAR(argv[0], '!')) {
913 argv++;
914 negate ^= 1;
915 goto again;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000916 }
917 }
Denis Vlasenko1e2a7e42008-02-09 05:48:42 +0000918
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000919 res = !oexpr(check_operator(*args));
Erik Andersen13456d12000-03-16 08:09:57 +0000920
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000921 if (*args != NULL && *++args != NULL) {
Denys Vlasenko27c6c002010-12-20 03:43:20 +0100922 /* Examples:
923 * test 3 -lt 5 6
924 * test -t 1 2
925 */
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000926 bb_error_msg("%s: unknown operand", *args);
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000927 res = 2;
Bernhard Reutner-Fischera7024572007-11-16 12:20:30 +0000928 }
Denis Vlasenko6672c8e2007-11-30 07:29:05 +0000929 ret:
930 DEINIT_S();
Denis Vlasenkobd28f6b2008-07-19 08:15:13 +0000931 return res;
Erik Andersen13456d12000-03-16 08:09:57 +0000932}