test.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * test implementation for busybox
  4. *
  5. * Copyright (c) by a whole pile of folks:
  6. *
  7. * 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.
  15. * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty).
  16. *
  17. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  18. *
  19. * Original copyright notice states:
  20. * "This program is in the Public Domain."
  21. */
  22. #include "libbb.h"
  23. #include <setjmp.h>
  24. /* This is a NOFORK applet. Be very careful! */
  25. /* test_main() is called from shells, and we need to be extra careful here.
  26. * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL
  27. * state. */
  28. /* test(1) accepts the following grammar:
  29. oexpr ::= aexpr | aexpr "-o" oexpr ;
  30. aexpr ::= nexpr | nexpr "-a" aexpr ;
  31. nexpr ::= primary | "!" primary
  32. primary ::= unary-operator operand
  33. | operand binary-operator operand
  34. | operand
  35. | "(" oexpr ")"
  36. ;
  37. unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
  38. "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
  39. binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
  40. "-nt"|"-ot"|"-ef";
  41. operand ::= <any legal UNIX file name>
  42. */
  43. #define TEST_DEBUG 0
  44. enum token {
  45. EOI,
  46. FILRD,
  47. FILWR,
  48. FILEX,
  49. FILEXIST,
  50. FILREG,
  51. FILDIR,
  52. FILCDEV,
  53. FILBDEV,
  54. FILFIFO,
  55. FILSOCK,
  56. FILSYM,
  57. FILGZ,
  58. FILTT,
  59. FILSUID,
  60. FILSGID,
  61. FILSTCK,
  62. FILNT,
  63. FILOT,
  64. FILEQ,
  65. FILUID,
  66. FILGID,
  67. STREZ,
  68. STRNZ,
  69. STREQ,
  70. STRNE,
  71. STRLT,
  72. STRGT,
  73. INTEQ,
  74. INTNE,
  75. INTGE,
  76. INTGT,
  77. INTLE,
  78. INTLT,
  79. UNOT,
  80. BAND,
  81. BOR,
  82. LPAREN,
  83. RPAREN,
  84. OPERAND
  85. };
  86. #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
  87. #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
  88. #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
  89. #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
  90. #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
  91. #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
  92. #if TEST_DEBUG
  93. int depth;
  94. #define nest_msg(...) do { \
  95. depth++; \
  96. fprintf(stderr, "%*s", depth*2, ""); \
  97. fprintf(stderr, __VA_ARGS__); \
  98. } while (0)
  99. #define unnest_msg(...) do { \
  100. fprintf(stderr, "%*s", depth*2, ""); \
  101. fprintf(stderr, __VA_ARGS__); \
  102. depth--; \
  103. } while (0)
  104. #define dbg_msg(...) do { \
  105. fprintf(stderr, "%*s", depth*2, ""); \
  106. fprintf(stderr, __VA_ARGS__); \
  107. } while (0)
  108. #define unnest_msg_and_return(expr, ...) do { \
  109. number_t __res = (expr); \
  110. fprintf(stderr, "%*s", depth*2, ""); \
  111. fprintf(stderr, __VA_ARGS__, res); \
  112. depth--; \
  113. return __res; \
  114. } while (0)
  115. static const char *const TOKSTR[] = {
  116. "EOI",
  117. "FILRD",
  118. "FILWR",
  119. "FILEX",
  120. "FILEXIST",
  121. "FILREG",
  122. "FILDIR",
  123. "FILCDEV",
  124. "FILBDEV",
  125. "FILFIFO",
  126. "FILSOCK",
  127. "FILSYM",
  128. "FILGZ",
  129. "FILTT",
  130. "FILSUID",
  131. "FILSGID",
  132. "FILSTCK",
  133. "FILNT",
  134. "FILOT",
  135. "FILEQ",
  136. "FILUID",
  137. "FILGID",
  138. "STREZ",
  139. "STRNZ",
  140. "STREQ",
  141. "STRNE",
  142. "STRLT",
  143. "STRGT",
  144. "INTEQ",
  145. "INTNE",
  146. "INTGE",
  147. "INTGT",
  148. "INTLE",
  149. "INTLT",
  150. "UNOT",
  151. "BAND",
  152. "BOR",
  153. "LPAREN",
  154. "RPAREN",
  155. "OPERAND"
  156. };
  157. #else
  158. #define nest_msg(...) ((void)0)
  159. #define unnest_msg(...) ((void)0)
  160. #define dbg_msg(...) ((void)0)
  161. #define unnest_msg_and_return(expr, ...) return expr
  162. #endif
  163. enum token_types {
  164. UNOP,
  165. BINOP,
  166. BUNOP,
  167. BBINOP,
  168. PAREN
  169. };
  170. struct operator_t {
  171. char op_text[4];
  172. unsigned char op_num, op_type;
  173. };
  174. static const struct operator_t ops[] = {
  175. { "-r", FILRD , UNOP },
  176. { "-w", FILWR , UNOP },
  177. { "-x", FILEX , UNOP },
  178. { "-e", FILEXIST, UNOP },
  179. { "-f", FILREG , UNOP },
  180. { "-d", FILDIR , UNOP },
  181. { "-c", FILCDEV , UNOP },
  182. { "-b", FILBDEV , UNOP },
  183. { "-p", FILFIFO , UNOP },
  184. { "-u", FILSUID , UNOP },
  185. { "-g", FILSGID , UNOP },
  186. { "-k", FILSTCK , UNOP },
  187. { "-s", FILGZ , UNOP },
  188. { "-t", FILTT , UNOP },
  189. { "-z", STREZ , UNOP },
  190. { "-n", STRNZ , UNOP },
  191. { "-h", FILSYM , UNOP }, /* for backwards compat */
  192. { "-O" , FILUID , UNOP },
  193. { "-G" , FILGID , UNOP },
  194. { "-L" , FILSYM , UNOP },
  195. { "-S" , FILSOCK, UNOP },
  196. { "=" , STREQ , BINOP },
  197. { "==" , STREQ , BINOP },
  198. { "!=" , STRNE , BINOP },
  199. { "<" , STRLT , BINOP },
  200. { ">" , STRGT , BINOP },
  201. { "-eq", INTEQ , BINOP },
  202. { "-ne", INTNE , BINOP },
  203. { "-ge", INTGE , BINOP },
  204. { "-gt", INTGT , BINOP },
  205. { "-le", INTLE , BINOP },
  206. { "-lt", INTLT , BINOP },
  207. { "-nt", FILNT , BINOP },
  208. { "-ot", FILOT , BINOP },
  209. { "-ef", FILEQ , BINOP },
  210. { "!" , UNOT , BUNOP },
  211. { "-a" , BAND , BBINOP },
  212. { "-o" , BOR , BBINOP },
  213. { "(" , LPAREN , PAREN },
  214. { ")" , RPAREN , PAREN },
  215. };
  216. #if ENABLE_FEATURE_TEST_64
  217. typedef int64_t number_t;
  218. #else
  219. typedef int number_t;
  220. #endif
  221. /* We try to minimize both static and stack usage. */
  222. struct test_statics {
  223. char **args;
  224. /* set only by check_operator(), either to bogus struct
  225. * or points to matching operator_t struct. Never NULL. */
  226. const struct operator_t *last_operator;
  227. gid_t *group_array;
  228. int ngroups;
  229. jmp_buf leaving;
  230. };
  231. /* See test_ptr_hack.c */
  232. extern struct test_statics *const test_ptr_to_statics;
  233. #define S (*test_ptr_to_statics)
  234. #define args (S.args )
  235. #define last_operator (S.last_operator)
  236. #define group_array (S.group_array )
  237. #define ngroups (S.ngroups )
  238. #define leaving (S.leaving )
  239. #define INIT_S() do { \
  240. (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
  241. barrier(); \
  242. } while (0)
  243. #define DEINIT_S() do { \
  244. free(test_ptr_to_statics); \
  245. } while (0)
  246. static number_t primary(enum token n);
  247. static void syntax(const char *op, const char *msg) NORETURN;
  248. static void syntax(const char *op, const char *msg)
  249. {
  250. if (op && *op) {
  251. bb_error_msg("%s: %s", op, msg);
  252. } else {
  253. bb_error_msg("%s: %s"+4, msg);
  254. }
  255. longjmp(leaving, 2);
  256. }
  257. /* atoi with error detection */
  258. //XXX: FIXME: duplicate of existing libbb function?
  259. static number_t getn(const char *s)
  260. {
  261. char *p;
  262. #if ENABLE_FEATURE_TEST_64
  263. long long r;
  264. #else
  265. long r;
  266. #endif
  267. errno = 0;
  268. #if ENABLE_FEATURE_TEST_64
  269. r = strtoll(s, &p, 10);
  270. #else
  271. r = strtol(s, &p, 10);
  272. #endif
  273. if (errno != 0)
  274. syntax(s, "out of range");
  275. if (*(skip_whitespace(p)))
  276. syntax(s, "bad number");
  277. return r;
  278. }
  279. /* UNUSED
  280. static int newerf(const char *f1, const char *f2)
  281. {
  282. struct stat b1, b2;
  283. return (stat(f1, &b1) == 0 &&
  284. stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
  285. }
  286. static int olderf(const char *f1, const char *f2)
  287. {
  288. struct stat b1, b2;
  289. return (stat(f1, &b1) == 0 &&
  290. stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
  291. }
  292. static int equalf(const char *f1, const char *f2)
  293. {
  294. struct stat b1, b2;
  295. return (stat(f1, &b1) == 0 &&
  296. stat(f2, &b2) == 0 &&
  297. b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
  298. }
  299. */
  300. static enum token check_operator(char *s)
  301. {
  302. static const struct operator_t no_op = {
  303. .op_num = -1,
  304. .op_type = -1
  305. };
  306. const struct operator_t *op;
  307. last_operator = &no_op;
  308. if (s == NULL) {
  309. return EOI;
  310. }
  311. op = ops;
  312. do {
  313. if (strcmp(s, op->op_text) == 0) {
  314. last_operator = op;
  315. return op->op_num;
  316. }
  317. op++;
  318. } while (op < ops + ARRAY_SIZE(ops));
  319. return OPERAND;
  320. }
  321. static int binop(void)
  322. {
  323. const char *opnd1, *opnd2;
  324. const struct operator_t *op;
  325. number_t val1, val2;
  326. opnd1 = *args;
  327. check_operator(*++args);
  328. op = last_operator;
  329. opnd2 = *++args;
  330. if (opnd2 == NULL)
  331. syntax(op->op_text, "argument expected");
  332. if (is_int_op(op->op_num)) {
  333. val1 = getn(opnd1);
  334. val2 = getn(opnd2);
  335. if (op->op_num == INTEQ)
  336. return val1 == val2;
  337. if (op->op_num == INTNE)
  338. return val1 != val2;
  339. if (op->op_num == INTGE)
  340. return val1 >= val2;
  341. if (op->op_num == INTGT)
  342. return val1 > val2;
  343. if (op->op_num == INTLE)
  344. return val1 <= val2;
  345. if (op->op_num == INTLT)
  346. return val1 < val2;
  347. }
  348. if (is_str_op(op->op_num)) {
  349. val1 = strcmp(opnd1, opnd2);
  350. if (op->op_num == STREQ)
  351. return val1 == 0;
  352. if (op->op_num == STRNE)
  353. return val1 != 0;
  354. if (op->op_num == STRLT)
  355. return val1 < 0;
  356. if (op->op_num == STRGT)
  357. return val1 > 0;
  358. }
  359. /* We are sure that these three are by now the only binops we didn't check
  360. * yet, so we do not check if the class is correct:
  361. */
  362. /* if (is_file_op(op->op_num)) */
  363. {
  364. struct stat b1, b2;
  365. if (stat(opnd1, &b1) || stat(opnd2, &b2))
  366. return 0; /* false, since at least one stat failed */
  367. if (op->op_num == FILNT)
  368. return b1.st_mtime > b2.st_mtime;
  369. if (op->op_num == FILOT)
  370. return b1.st_mtime < b2.st_mtime;
  371. if (op->op_num == FILEQ)
  372. return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
  373. }
  374. return 1; /* NOTREACHED */
  375. }
  376. static void initialize_group_array(void)
  377. {
  378. ngroups = getgroups(0, NULL);
  379. if (ngroups > 0) {
  380. /* FIXME: ash tries so hard to not die on OOM,
  381. * and we spoil it with just one xrealloc here */
  382. /* We realloc, because test_main can be entered repeatedly by shell.
  383. * Testcase (ash): 'while true; do test -x some_file; done'
  384. * and watch top. (some_file must have owner != you) */
  385. group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
  386. getgroups(ngroups, group_array);
  387. }
  388. }
  389. /* Return non-zero if GID is one that we have in our groups list. */
  390. //XXX: FIXME: duplicate of existing libbb function?
  391. // see toplevel TODO file:
  392. // possible code duplication ingroup() and is_a_group_member()
  393. static int is_a_group_member(gid_t gid)
  394. {
  395. int i;
  396. /* Short-circuit if possible, maybe saving a call to getgroups(). */
  397. if (gid == getgid() || gid == getegid())
  398. return 1;
  399. if (ngroups == 0)
  400. initialize_group_array();
  401. /* Search through the list looking for GID. */
  402. for (i = 0; i < ngroups; i++)
  403. if (gid == group_array[i])
  404. return 1;
  405. return 0;
  406. }
  407. /* Do the same thing access(2) does, but use the effective uid and gid,
  408. and don't make the mistake of telling root that any file is
  409. executable. */
  410. static int test_eaccess(char *path, int mode)
  411. {
  412. struct stat st;
  413. unsigned int euid = geteuid();
  414. if (stat(path, &st) < 0)
  415. return -1;
  416. if (euid == 0) {
  417. /* Root can read or write any file. */
  418. if (mode != X_OK)
  419. return 0;
  420. /* Root can execute any file that has any one of the execute
  421. bits set. */
  422. if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
  423. return 0;
  424. }
  425. if (st.st_uid == euid) /* owner */
  426. mode <<= 6;
  427. else if (is_a_group_member(st.st_gid))
  428. mode <<= 3;
  429. if (st.st_mode & mode)
  430. return 0;
  431. return -1;
  432. }
  433. static int filstat(char *nm, enum token mode)
  434. {
  435. struct stat s;
  436. unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
  437. if (mode == FILSYM) {
  438. #ifdef S_IFLNK
  439. if (lstat(nm, &s) == 0) {
  440. i = S_IFLNK;
  441. goto filetype;
  442. }
  443. #endif
  444. return 0;
  445. }
  446. if (stat(nm, &s) != 0)
  447. return 0;
  448. if (mode == FILEXIST)
  449. return 1;
  450. if (is_file_access(mode)) {
  451. if (mode == FILRD)
  452. i = R_OK;
  453. if (mode == FILWR)
  454. i = W_OK;
  455. if (mode == FILEX)
  456. i = X_OK;
  457. return test_eaccess(nm, i) == 0;
  458. }
  459. if (is_file_type(mode)) {
  460. if (mode == FILREG)
  461. i = S_IFREG;
  462. if (mode == FILDIR)
  463. i = S_IFDIR;
  464. if (mode == FILCDEV)
  465. i = S_IFCHR;
  466. if (mode == FILBDEV)
  467. i = S_IFBLK;
  468. if (mode == FILFIFO) {
  469. #ifdef S_IFIFO
  470. i = S_IFIFO;
  471. #else
  472. return 0;
  473. #endif
  474. }
  475. if (mode == FILSOCK) {
  476. #ifdef S_IFSOCK
  477. i = S_IFSOCK;
  478. #else
  479. return 0;
  480. #endif
  481. }
  482. filetype:
  483. return ((s.st_mode & S_IFMT) == i);
  484. }
  485. if (is_file_bit(mode)) {
  486. if (mode == FILSUID)
  487. i = S_ISUID;
  488. if (mode == FILSGID)
  489. i = S_ISGID;
  490. if (mode == FILSTCK)
  491. i = S_ISVTX;
  492. return ((s.st_mode & i) != 0);
  493. }
  494. if (mode == FILGZ)
  495. return s.st_size > 0L;
  496. if (mode == FILUID)
  497. return s.st_uid == geteuid();
  498. if (mode == FILGID)
  499. return s.st_gid == getegid();
  500. return 1; /* NOTREACHED */
  501. }
  502. static number_t nexpr(enum token n)
  503. {
  504. number_t res;
  505. nest_msg(">nexpr(%s)\n", TOKSTR[n]);
  506. if (n == UNOT) {
  507. res = !nexpr(check_operator(*++args));
  508. unnest_msg("<nexpr:%lld\n", res);
  509. return res;
  510. }
  511. res = primary(n);
  512. unnest_msg("<nexpr:%lld\n", res);
  513. return res;
  514. }
  515. static number_t aexpr(enum token n)
  516. {
  517. number_t res;
  518. nest_msg(">aexpr(%s)\n", TOKSTR[n]);
  519. res = nexpr(n);
  520. dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
  521. if (check_operator(*++args) == BAND) {
  522. dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
  523. res = aexpr(check_operator(*++args)) && res;
  524. unnest_msg("<aexpr:%lld\n", res);
  525. return res;
  526. }
  527. args--;
  528. unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
  529. return res;
  530. }
  531. static number_t oexpr(enum token n)
  532. {
  533. number_t res;
  534. nest_msg(">oexpr(%s)\n", TOKSTR[n]);
  535. res = aexpr(n);
  536. dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
  537. if (check_operator(*++args) == BOR) {
  538. dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
  539. res = oexpr(check_operator(*++args)) || res;
  540. unnest_msg("<oexpr:%lld\n", res);
  541. return res;
  542. }
  543. args--;
  544. unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
  545. return res;
  546. }
  547. static number_t primary(enum token n)
  548. {
  549. #if TEST_DEBUG
  550. number_t res = res; /* for compiler */
  551. #else
  552. number_t res;
  553. #endif
  554. const struct operator_t *args0_op;
  555. nest_msg(">primary(%s)\n", TOKSTR[n]);
  556. if (n == EOI) {
  557. syntax(NULL, "argument expected");
  558. }
  559. if (n == LPAREN) {
  560. res = oexpr(check_operator(*++args));
  561. if (check_operator(*++args) != RPAREN)
  562. syntax(NULL, "closing paren expected");
  563. unnest_msg("<primary:%lld\n", res);
  564. return res;
  565. }
  566. /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
  567. * do the same */
  568. args0_op = last_operator;
  569. /* last_operator = operator at args[1] */
  570. if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
  571. if (args[2]) {
  572. // coreutils also does this:
  573. // if (args[3] && args[0]="-l" && args[2] is BINOP)
  574. // return binop(1 /* prepended by -l */);
  575. if (last_operator->op_type == BINOP)
  576. unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
  577. }
  578. }
  579. /* check "is args[0] unop?" second */
  580. if (args0_op->op_type == UNOP) {
  581. /* unary expression */
  582. if (args[1] == NULL)
  583. // syntax(args0_op->op_text, "argument expected");
  584. goto check_emptiness;
  585. args++;
  586. if (n == STREZ)
  587. unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
  588. if (n == STRNZ)
  589. unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
  590. if (n == FILTT)
  591. unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
  592. unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
  593. }
  594. /*check_operator(args[1]); - already done */
  595. if (last_operator->op_type == BINOP) {
  596. /* args[2] is known to be NULL, isn't it bound to fail? */
  597. unnest_msg_and_return(binop(), "<primary:%lld\n");
  598. }
  599. check_emptiness:
  600. unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
  601. }
  602. int test_main(int argc, char **argv)
  603. {
  604. int res;
  605. const char *arg0;
  606. // bool negate = 0;
  607. arg0 = bb_basename(argv[0]);
  608. if (arg0[0] == '[') {
  609. --argc;
  610. if (!arg0[1]) { /* "[" ? */
  611. if (NOT_LONE_CHAR(argv[argc], ']')) {
  612. bb_error_msg("missing ]");
  613. return 2;
  614. }
  615. } else { /* assuming "[[" */
  616. if (strcmp(argv[argc], "]]") != 0) {
  617. bb_error_msg("missing ]]");
  618. return 2;
  619. }
  620. }
  621. argv[argc] = NULL;
  622. }
  623. /* We must do DEINIT_S() prior to returning */
  624. INIT_S();
  625. res = setjmp(leaving);
  626. if (res)
  627. goto ret;
  628. /* resetting ngroups is probably unnecessary. it will
  629. * force a new call to getgroups(), which prevents using
  630. * group data fetched during a previous call. but the
  631. * only way the group data could be stale is if there's
  632. * been an intervening call to setgroups(), and this
  633. * isn't likely in the case of a shell. paranoia
  634. * prevails...
  635. */
  636. ngroups = 0;
  637. //argc--;
  638. argv++;
  639. /* Implement special cases from POSIX.2, section 4.62.4 */
  640. if (!argv[0]) { /* "test" */
  641. res = 1;
  642. goto ret;
  643. }
  644. #if 0
  645. // Now it's fixed in the parser and should not be needed
  646. if (LONE_CHAR(argv[0], '!') && argv[1]) {
  647. negate = 1;
  648. //argc--;
  649. argv++;
  650. }
  651. if (!argv[1]) { /* "test [!] arg" */
  652. res = (*argv[0] == '\0');
  653. goto ret;
  654. }
  655. if (argv[2] && !argv[3]) {
  656. check_operator(argv[1]);
  657. if (last_operator->op_type == BINOP) {
  658. /* "test [!] arg1 <binary_op> arg2" */
  659. args = &argv[0];
  660. res = (binop() == 0);
  661. goto ret;
  662. }
  663. }
  664. /* Some complex expression. Undo '!' removal */
  665. if (negate) {
  666. negate = 0;
  667. //argc++;
  668. argv--;
  669. }
  670. #endif
  671. args = &argv[0];
  672. res = !oexpr(check_operator(*args));
  673. if (*args != NULL && *++args != NULL) {
  674. /* TODO: example when this happens? */
  675. bb_error_msg("%s: unknown operand", *args);
  676. res = 2;
  677. }
  678. ret:
  679. DEINIT_S();
  680. // return negate ? !res : res;
  681. return res;
  682. }