- char **ap;
- char *opname;
- char c;
- char *p;
- int nest; /* parenthises nesting */
- int op;
- int pri;
- int skipping;
- int binary;
- struct operator opstack[STACKSIZE];
- struct operator *opsp;
- struct value valstack[STACKSIZE + 1];
- struct value *valsp;
- struct filestat fs;
- int ret_val;
-
- INITARGS(argv);
- if (**argv == '[') {
- if (! equal(argv[argc - 1], "]"))
- error("missing ]");
- argv[argc - 1] = NULL;
- }
- ap = argv + 1;
- fs.name = NULL;
-
- /*
- * Test(1) implements an inherently ambiguous grammer. In order to
- * assure some degree of consistency, we special case the POSIX
- * requirements to assure correct evaluation for POSIX following
- * scripts. The following special cases comply with POSIX
- * P1003.2/D11.2 Section 4.62.4.
- */
- switch(argc - 1) {
- case 0: /* % test */
-#ifdef PRINT
- printf("1\n");
-#endif
- return 1;
- break;
- case 1: /* % test arg */
-#ifdef PRINT
- printf("%d\n", (*argv[1] == '\0')? 1 : 0);
-#endif
- return (*argv[1] == '\0')? 1 : 0;
- break;
- case 2: /* % test op arg */
- opname = argv[1];
- if (IS_BANG(opname)) {
-#ifdef PRINT
- printf("%d\n", (*argv[2] == '\0')? 1 : 0);
-#endif
- return (*argv[2] == '\0')? 1 : 0;
- } else {
- ret_val = posix_unary_op(&argv[1]);
- if (ret_val >= 0) {
-#ifdef PRINT
- printf("%d\n", ret_val);
-#endif
- return ret_val;
- }
- }
- break;
- case 3: /* % test arg1 op arg2 */
- if (IS_BANG(argv[1])) {
- ret_val = posix_unary_op(&argv[1]);
- if (ret_val >= 0) {
-#ifdef PRINT
- printf("%d\n", !ret_val);
-#endif
- return !ret_val;
- }
- } else {
- ret_val = posix_binary_op(&argv[1]);
- if (ret_val >= 0) {
-#ifdef PRINT
- printf("%d\n", ret_val);
-#endif
- return ret_val;
- }
- }
- break;
- case 4: /* % test ! arg1 op arg2 */
- if (IS_BANG(argv[1])) {
- ret_val = posix_binary_op(&argv[2]);
- if (ret_val >= 0) {
-#ifdef PRINT
- printf("%d\n", !ret_val);
-#endif
- return !ret_val;
- }
- }
- break;
- default:
- break;
- }
-
- /*
- * We use operator precedence parsing, evaluating the expression
- * as we parse it. Parentheses are handled by bumping up the
- * priority of operators using the variable "nest." We use the
- * variable "skipping" to turn off evaluation temporarily for the
- * short circuit boolean operators. (It is important do the short
- * circuit evaluation because under NFS a stat operation can take
- * infinitely long.)
- */
-
- opsp = opstack + STACKSIZE;
- valsp = valstack;
- nest = 0;
- skipping = 0;
- if (*ap == NULL) {
- valstack[0].type = BOOLEAN;
- valstack[0].u.num = 0;
- goto done;
- }
- for (;;) {
- opname = *ap++;
- if (opname == NULL)
- goto syntax;
- if (opname[0] == '(' && opname[1] == '\0') {
- nest += NESTINCR;
- continue;
- } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
- if (opsp == &opstack[0])
- goto overflow;
- --opsp;
- opsp->op = op;
- opsp->pri = op_priority[op] + nest;
- continue;
-
- } else {
- valsp->type = STRING;
- valsp->u.string = opname;
- valsp++;
- }
- for (;;) {
- opname = *ap++;
- if (opname == NULL) {
- if (nest != 0)
- goto syntax;
- pri = 0;
- break;
- }
- if (opname[0] != ')' || opname[1] != '\0') {
- if ((op = lookup_op(opname, binary_op)) < 0)
- goto syntax;
- op += FIRST_BINARY_OP;
- pri = op_priority[op] + nest;
- break;
- }
- if ((nest -= NESTINCR) < 0)
- goto syntax;
- }
- while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
- binary = opsp->op;
- for (;;) {
- valsp--;
- c = op_argflag[opsp->op];
- if (c == OP_INT) {
- if (valsp->type == STRING &&
- int_tcheck(valsp->u.string))
- valsp->u.num =
- atol(valsp->u.string);
- valsp->type = INTEGER;
- } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */
- if (valsp->type == INTEGER) {
- p = stalloc(32);
+ struct operator opstack[STACKSIZE];
+ struct operator *opsp;
+ struct value valstack[STACKSIZE + 1];
+ struct value *valsp;
+ struct filestat fs;
+ char c, **ap, *opname, *p;
+ int binary, nest, op, pri, ret_val, skipping;
+
+ if ((p = argv[0]) == NULL)
+ errx(2, "test: argc is zero");
+
+ if (*p != '\0' && p[strlen(p) - 1] == '[') {
+ if (strcmp(argv[--argc], "]"))
+ errx(2, "missing ]");
+ argv[argc] = NULL;
+ }
+ ap = argv + 1;
+ fs.name = NULL;
+
+ /*
+ * Test(1) implements an inherently ambiguous grammer. In order to
+ * assure some degree of consistency, we special case the POSIX 1003.2
+ * requirements to assure correct evaluation for POSIX scripts. The
+ * following special cases comply with POSIX P1003.2/D11.2 Section
+ * 4.62.4.
+ */
+ switch(argc - 1) {
+ case 0: /* % test */
+ return (1);
+ break;
+ case 1: /* % test arg */
+ return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
+ break;
+ case 2: /* % test op arg */
+ opname = argv[1];
+ if (IS_BANG(opname))
+ return (*argv[2] == '\0') ? 0 : 1;
+ else {
+ ret_val = posix_unary_op(&argv[1]);
+ if (ret_val >= 0)
+ return (ret_val);
+ }
+ break;
+ case 3: /* % test arg1 op arg2 */
+ if (IS_BANG(argv[1])) {
+ ret_val = posix_unary_op(&argv[1]);
+ if (ret_val >= 0)
+ return (!ret_val);
+ } else {
+ ret_val = posix_binary_op(&argv[1]);
+ if (ret_val >= 0)
+ return (ret_val);
+ }
+ break;
+ case 4: /* % test ! arg1 op arg2 */
+ if (IS_BANG(argv[1])) {
+ ret_val = posix_binary_op(&argv[2]);
+ if (ret_val >= 0)
+ return (!ret_val);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * We use operator precedence parsing, evaluating the expression as
+ * we parse it. Parentheses are handled by bumping up the priority
+ * of operators using the variable "nest." We use the variable
+ * "skipping" to turn off evaluation temporarily for the short
+ * circuit boolean operators. (It is important do the short circuit
+ * evaluation because under NFS a stat operation can take infinitely
+ * long.)
+ */
+ opsp = opstack + STACKSIZE;
+ valsp = valstack;
+ nest = skipping = 0;
+ if (*ap == NULL) {
+ valstack[0].type = BOOLEAN;
+ valstack[0].u.num = 0;
+ goto done;
+ }
+ for (;;) {
+ opname = *ap++;
+ if (opname == NULL)
+ syntax();
+ if (opname[0] == '(' && opname[1] == '\0') {
+ nest += NESTINCR;
+ continue;
+ } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
+ if (opsp == &opstack[0])
+ overflow();
+ --opsp;
+ opsp->op = op;
+ opsp->pri = op_priority[op] + nest;
+ continue;
+ } else {
+ valsp->type = STRING;
+ valsp->u.string = opname;
+ valsp++;
+ }
+ for (;;) {
+ opname = *ap++;
+ if (opname == NULL) {
+ if (nest != 0)
+ syntax();
+ pri = 0;
+ break;
+ }
+ if (opname[0] != ')' || opname[1] != '\0') {
+ if ((op = lookup_op(opname, binary_op)) < 0)
+ syntax();
+ op += FIRST_BINARY_OP;
+ pri = op_priority[op] + nest;
+ break;
+ }
+ if ((nest -= NESTINCR) < 0)
+ syntax();
+ }
+ while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
+ binary = opsp->op;
+ for (;;) {
+ valsp--;
+ c = op_argflag[opsp->op];
+ if (c == OP_INT) {
+ if (valsp->type == STRING)
+ get_int(valsp->u.string,
+ &valsp->u.num);
+ valsp->type = INTEGER;
+ } else if (c >= OP_STRING) {
+ /* OP_STRING or OP_FILE */
+ if (valsp->type == INTEGER) {
+ if ((p = malloc(32)) == NULL)
+ err(2, NULL);