test: fix mishandling of "test '(' = '('" and similar
[oweals/busybox.git] / coreutils / test.c
index 4df505a0507b4bb290b66b786ecfe76b61436ffb..88cc550506e3306513176c3902e6b572b773ea3c 100644 (file)
@@ -826,7 +826,6 @@ int test_main(int argc, char **argv)
 {
        int res;
        const char *arg0;
-//     bool negate = 0;
 
        arg0 = bb_basename(argv[0]);
        if (arg0[0] == '[') {
@@ -844,6 +843,7 @@ int test_main(int argc, char **argv)
                }
                argv[argc] = NULL;
        }
+       /* argc is unused after this point */
 
        /* We must do DEINIT_S() prior to returning */
        INIT_S();
@@ -862,43 +862,45 @@ int test_main(int argc, char **argv)
         */
        /*ngroups = 0; - done by INIT_S() */
 
-       //argc--;
        argv++;
+       args = argv;
 
-       /* Implement special cases from POSIX.2, section 4.62.4 */
-       if (!argv[0]) { /* "test" */
-               res = 1;
-               goto ret;
-       }
-#if 0
-// Now it's fixed in the parser and should not be needed
-       if (LONE_CHAR(argv[0], '!') && argv[1]) {
-               negate = 1;
-               //argc--;
-               argv++;
-       }
-       if (!argv[1]) { /* "test [!] arg" */
-               res = (*argv[0] == '\0');
-               goto ret;
-       }
-       if (argv[2] && !argv[3]) {
-               check_operator(argv[1]);
-               if (last_operator->op_type == BINOP) {
-                       /* "test [!] arg1 <binary_op> arg2" */
-                       args = argv;
-                       res = (binop() == 0);
-                       goto ret;
+       /* Implement special cases from POSIX.2, section 4.62.4.
+        * Testcase: "test '(' = '('"
+        * The general parser would misinterpret '(' as group start.
+        */
+       if (1) {
+               int negate = 0;
+ again:
+               if (!argv[0]) {
+                       /* "test" */
+                       res = 1;
+                       goto ret_special;
+               }
+               if (!argv[1]) {
+                       /* "test [!] arg" */
+                       res = (argv[0][0] == '\0');
+                       goto ret_special;
+               }
+               if (argv[2] && !argv[3]) {
+                       check_operator(argv[1]);
+                       if (last_operator->op_type == BINOP) {
+                               /* "test [!] arg1 <binary_op> arg2" */
+                               args = argv;
+                               res = (binop() == 0);
+ ret_special:
+                               /* If there was leading "!" op... */
+                               res ^= negate;
+                               goto ret;
+                       }
+               }
+               if (LONE_CHAR(argv[0], '!')) {
+                       argv++;
+                       negate ^= 1;
+                       goto again;
                }
        }
 
-       /* Some complex expression. Undo '!' removal */
-       if (negate) {
-               negate = 0;
-               //argc++;
-               argv--;
-       }
-#endif
-       args = argv;
        res = !oexpr(check_operator(*args));
 
        if (*args != NULL && *++args != NULL) {
@@ -911,6 +913,5 @@ int test_main(int argc, char **argv)
        }
  ret:
        DEINIT_S();
-//     return negate ? !res : res;
        return res;
 }