153835Selan /* 2*53852Selan * Copyright (c) 1988 The Regents of the University of California. 3*53852Selan * All rights reserved. 453835Selan * 5*53852Selan * This code is derived from software contributed to Berkeley by 6*53852Selan * Kenneth Almquist. 7*53852Selan * 8*53852Selan * %sccs.include.redist.c% 953835Selan */ 1053835Selan 11*53852Selan #ifndef lint 12*53852Selan char copyright[] = 13*53852Selan "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 14*53852Selan All rights reserved.\n"; 15*53852Selan #endif /* not lint */ 1653835Selan 17*53852Selan #ifndef lint 18*53852Selan static char sccsid[] = "@(#)test.c 1.2 (Berkeley) 06/03/92"; 19*53852Selan #endif /* not lint */ 2053835Selan 2153835Selan #include <stdio.h> 2253835Selan #include <errno.h> 2353835Selan #include <sys/types.h> 2453835Selan #include <sys/stat.h> 2553835Selan #include "operators.h" 2653835Selan #include "extern.h" 2753835Selan 2853835Selan #define STACKSIZE 12 2953835Selan #define NESTINCR 16 3053835Selan 3153835Selan /* data types */ 3253835Selan #define STRING 0 3353835Selan #define INTEGER 1 3453835Selan #define BOOLEAN 2 3553835Selan 3653835Selan #define INITARGS(argv) if (argv[0] == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else 3753835Selan 3853835Selan #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 3953835Selan #define equal(s1, s2) (strcmp(s1, s2) == 0) 4053835Selan 4153835Selan /* 4253835Selan * This structure hold a value. The type keyword specifies the type of 4353835Selan * the value, and the union u holds the value. The value of a boolean 4453835Selan * is stored in u.num (1 = TRUE, 0 = FALSE). 4553835Selan */ 4653835Selan 4753835Selan struct value { 48*53852Selan int type; 49*53852Selan union { 50*53852Selan char *string; 51*53852Selan long num; 52*53852Selan } u; 5353835Selan }; 5453835Selan 5553835Selan struct operator { 56*53852Selan short op; /* which operator */ 57*53852Selan short pri; /* priority of operator */ 5853835Selan }; 5953835Selan 6053835Selan struct filestat { 61*53852Selan char *name; /* name of file */ 62*53852Selan int rcode; /* return code from stat */ 63*53852Selan struct stat stat; /* status info on file */ 6453835Selan }; 6553835Selan 6653835Selan static int expr_is_false __P((struct value *)); 6753835Selan static void expr_operator __P((int, struct value *, struct filestat *)); 68*53852Selan static int lookup_op __P((char *, char *const *)); 6953835Selan static long atol __P((const char *)); 7053835Selan static int posix_binary_op __P((char **)); 7153835Selan static int posix_unary_op __P((char **)); 7253835Selan static int int_tcheck __P((char *)); 7353835Selan 7453835Selan int 75*53852Selan main(argc, argv) 76*53852Selan int argc; 77*53852Selan char **argv; 7853835Selan { 79*53852Selan char **ap; 80*53852Selan char *opname; 81*53852Selan char c; 82*53852Selan char *p; 83*53852Selan int nest; /* parentheses nesting */ 84*53852Selan int op; 85*53852Selan int pri; 86*53852Selan int skipping; 87*53852Selan int binary; 88*53852Selan struct operator opstack[STACKSIZE]; 89*53852Selan struct operator *opsp; 90*53852Selan struct value valstack[STACKSIZE + 1]; 91*53852Selan struct value *valsp; 92*53852Selan struct filestat fs; 93*53852Selan int ret_val; 9453835Selan 95*53852Selan INITARGS(argv); 96*53852Selan if (**argv == '[') { 97*53852Selan if (!equal(argv[argc - 1], "]")) 98*53852Selan error("missing ]"); 99*53852Selan argv[argc - 1] = NULL; 100*53852Selan } 101*53852Selan ap = argv + 1; 102*53852Selan fs.name = NULL; 10353835Selan 104*53852Selan /* Test(1) implements an inherently ambiguous grammer. In order to 105*53852Selan * assure some degree of consistency, we special case the POSIX 106*53852Selan * requirements to assure correct evaluation for POSIX following 107*53852Selan * scripts. The following special cases comply with POSIX 108*53852Selan * P1003.2/D11.2 Section 4.62.4. */ 109*53852Selan switch (argc - 1) { 110*53852Selan case 0: /* % test */ 111*53852Selan return 1; 112*53852Selan break; 113*53852Selan case 1: /* % test arg */ 114*53852Selan return (*argv[1] == '\0') ? 1 : 0; 115*53852Selan break; 116*53852Selan case 2: /* % test op arg */ 117*53852Selan opname = argv[1]; 118*53852Selan if (IS_BANG(opname)) 119*53852Selan return (*argv[2] == '\0') ? 1 : 0; 120*53852Selan else { 121*53852Selan ret_val = posix_unary_op(&argv[1]); 122*53852Selan if (ret_val >= 0) 123*53852Selan return ret_val; 124*53852Selan } 125*53852Selan break; 126*53852Selan case 3: /* % test arg1 op arg2 */ 127*53852Selan if (IS_BANG(argv[1])) { 128*53852Selan ret_val = posix_unary_op(&argv[1]); 129*53852Selan if (ret_val >= 0) 130*53852Selan return !ret_val; 131*53852Selan } else { 132*53852Selan ret_val = posix_binary_op(&argv[1]); 133*53852Selan if (ret_val >= 0) 134*53852Selan return ret_val; 135*53852Selan } 136*53852Selan break; 137*53852Selan case 4: /* % test ! arg1 op arg2 */ 138*53852Selan if (IS_BANG(argv[1])) { 139*53852Selan ret_val = posix_binary_op(&argv[2]); 140*53852Selan if (ret_val >= 0) 141*53852Selan return !ret_val; 142*53852Selan } 143*53852Selan break; 144*53852Selan default: 145*53852Selan break; 146*53852Selan } 14753835Selan 148*53852Selan /* We use operator precedence parsing, evaluating the expression as 149*53852Selan * we parse it. Parentheses are handled by bumping up the priority 150*53852Selan * of operators using the variable "nest." We use the variable 151*53852Selan * "skipping" to turn off evaluation temporarily for the short 152*53852Selan * circuit boolean operators. (It is important do the short circuit 153*53852Selan * evaluation because under NFS a stat operation can take infinitely 154*53852Selan * long.) */ 15553835Selan 156*53852Selan opsp = opstack + STACKSIZE; 157*53852Selan valsp = valstack; 158*53852Selan nest = 0; 159*53852Selan skipping = 0; 160*53852Selan if (*ap == NULL) { 161*53852Selan valstack[0].type = BOOLEAN; 162*53852Selan valstack[0].u.num = 0; 163*53852Selan goto done; 164*53852Selan } 165*53852Selan for (;;) { 166*53852Selan opname = *ap++; 167*53852Selan if (opname == NULL) 168*53852Selan goto syntax; 169*53852Selan if (opname[0] == '(' && opname[1] == '\0') { 170*53852Selan nest += NESTINCR; 171*53852Selan continue; 172*53852Selan } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 173*53852Selan if (opsp == &opstack[0]) 174*53852Selan goto overflow; 175*53852Selan --opsp; 176*53852Selan opsp->op = op; 177*53852Selan opsp->pri = op_priority[op] + nest; 178*53852Selan continue; 17953835Selan 180*53852Selan } else { 181*53852Selan valsp->type = STRING; 182*53852Selan valsp->u.string = opname; 183*53852Selan valsp++; 184*53852Selan } 185*53852Selan for (;;) { 186*53852Selan opname = *ap++; 187*53852Selan if (opname == NULL) { 188*53852Selan if (nest != 0) 189*53852Selan goto syntax; 190*53852Selan pri = 0; 191*53852Selan break; 192*53852Selan } 193*53852Selan if (opname[0] != ')' || opname[1] != '\0') { 194*53852Selan if ((op = lookup_op(opname, binary_op)) < 0) 195*53852Selan goto syntax; 196*53852Selan op += FIRST_BINARY_OP; 197*53852Selan pri = op_priority[op] + nest; 198*53852Selan break; 199*53852Selan } 200*53852Selan if ((nest -= NESTINCR) < 0) 201*53852Selan goto syntax; 202*53852Selan } 203*53852Selan while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 204*53852Selan binary = opsp->op; 205*53852Selan for (;;) { 206*53852Selan valsp--; 207*53852Selan c = op_argflag[opsp->op]; 208*53852Selan if (c == OP_INT) { 209*53852Selan if (valsp->type == STRING && 210*53852Selan int_tcheck(valsp->u.string)) 211*53852Selan valsp->u.num = 212*53852Selan atol(valsp->u.string); 213*53852Selan valsp->type = INTEGER; 214*53852Selan } else if (c >= OP_STRING) { 215*53852Selan /* OP_STRING or OP_FILE */ 216*53852Selan if (valsp->type == INTEGER) { 217*53852Selan p = stalloc(32); 21853835Selan #ifdef SHELL 219*53852Selan fmtstr(p, 32, "%d", 220*53852Selan valsp->u.num); 22153835Selan #else 222*53852Selan sprintf(p, "%d", valsp->u.num); 22353835Selan #endif 224*53852Selan valsp->u.string = p; 225*53852Selan } else if (valsp->type == BOOLEAN) { 226*53852Selan if (valsp->u.num) 227*53852Selan valsp->u.string = 228*53852Selan "true"; 229*53852Selan else 230*53852Selan valsp->u.string = ""; 231*53852Selan } 232*53852Selan valsp->type = STRING; 233*53852Selan if (c == OP_FILE 234*53852Selan && (fs.name == NULL 235*53852Selan || !equal(fs.name, 236*53852Selan valsp->u.string))) { 237*53852Selan fs.name = valsp->u.string; 238*53852Selan fs.rcode = 239*53852Selan stat(valsp->u.string, 240*53852Selan &fs.stat); 241*53852Selan } 242*53852Selan } 243*53852Selan if (binary < FIRST_BINARY_OP) 244*53852Selan break; 245*53852Selan binary = 0; 24653835Selan } 247*53852Selan if (!skipping) 248*53852Selan expr_operator(opsp->op, valsp, &fs); 249*53852Selan else if (opsp->op == AND1 || opsp->op == OR1) 250*53852Selan skipping--; 251*53852Selan valsp++;/* push value */ 252*53852Selan opsp++; /* pop operator */ 253*53852Selan } 254*53852Selan if (opname == NULL) 255*53852Selan break; 256*53852Selan if (opsp == &opstack[0]) 257*53852Selan goto overflow; 258*53852Selan if (op == AND1 || op == AND2) { 259*53852Selan op = AND1; 260*53852Selan if (skipping || expr_is_false(valsp - 1)) 261*53852Selan skipping++; 262*53852Selan } 263*53852Selan if (op == OR1 || op == OR2) { 264*53852Selan op = OR1; 265*53852Selan if (skipping || !expr_is_false(valsp - 1)) 266*53852Selan skipping++; 267*53852Selan } 268*53852Selan opsp--; 269*53852Selan opsp->op = op; 270*53852Selan opsp->pri = pri; 271*53852Selan } 27253835Selan done: 273*53852Selan return expr_is_false(&valstack[0]); 27453835Selan 275*53852Selan syntax: error("syntax error"); 276*53852Selan overflow: error("Expression too complex"); 27753835Selan 27853835Selan } 27953835Selan 28053835Selan 28153835Selan static int 28253835Selan expr_is_false(val) 283*53852Selan struct value *val; 28453835Selan { 285*53852Selan if (val->type == STRING) { 286*53852Selan if (val->u.string[0] == '\0') 287*53852Selan return 1; 288*53852Selan } else { /* INTEGER or BOOLEAN */ 289*53852Selan if (val->u.num == 0) 290*53852Selan return 1; 291*53852Selan } 292*53852Selan return 0; 29353835Selan } 29453835Selan 29553835Selan 29653835Selan /* 29753835Selan * Execute an operator. Op is the operator. Sp is the stack pointer; 29853835Selan * sp[0] refers to the first operand, sp[1] refers to the second operand 29953835Selan * (if any), and the result is placed in sp[0]. The operands are converted 30053835Selan * to the type expected by the operator before expr_operator is called. 30153835Selan * Fs is a pointer to a structure which holds the value of the last call 30253835Selan * to stat, to avoid repeated stat calls on the same file. 30353835Selan */ 30453835Selan 30553835Selan static void 30653835Selan expr_operator(op, sp, fs) 307*53852Selan int op; 30853835Selan struct value *sp; 30953835Selan struct filestat *fs; 31053835Selan { 311*53852Selan int i; 31253835Selan 31353835Selan switch (op) { 31453835Selan case NOT: 315*53852Selan sp->u.num = expr_is_false(sp); 316*53852Selan sp->type = BOOLEAN; 317*53852Selan break; 318*53852Selan case ISEXIST: 319*53852Selan if (fs == NULL || fs->rcode == -1) 320*53852Selan goto false; 321*53852Selan else 322*53852Selan goto true; 323*53852Selan case ISREAD: 324*53852Selan i = 04; 325*53852Selan goto permission; 326*53852Selan case ISWRITE: 327*53852Selan i = 02; 328*53852Selan goto permission; 329*53852Selan case ISEXEC: 330*53852Selan i = 01; 33153835Selan permission: 332*53852Selan if (fs->stat.st_uid == geteuid()) 333*53852Selan i <<= 6; 334*53852Selan else if (fs->stat.st_gid == getegid()) 335*53852Selan i <<= 3; 336*53852Selan goto filebit; /* true if (stat.st_mode & i) != 0 */ 337*53852Selan case ISFILE: 338*53852Selan i = S_IFREG; 339*53852Selan goto filetype; 340*53852Selan case ISDIR: 341*53852Selan i = S_IFDIR; 342*53852Selan goto filetype; 343*53852Selan case ISCHAR: 344*53852Selan i = S_IFCHR; 345*53852Selan goto filetype; 346*53852Selan case ISBLOCK: 347*53852Selan i = S_IFBLK; 348*53852Selan goto filetype; 349*53852Selan case ISFIFO: 35053835Selan #ifdef S_IFIFO 351*53852Selan i = S_IFIFO; 352*53852Selan goto filetype; 35353835Selan #else 354*53852Selan goto false; 35553835Selan #endif 35653835Selan filetype: 357*53852Selan if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) { 358*53852Selan true: 359*53852Selan sp->u.num = 1; 360*53852Selan } else { 361*53852Selan false: 362*53852Selan sp->u.num = 0; 363*53852Selan } 364*53852Selan sp->type = BOOLEAN; 365*53852Selan break; 366*53852Selan case ISSETUID: 367*53852Selan i = S_ISUID; 368*53852Selan goto filebit; 369*53852Selan case ISSETGID: 370*53852Selan i = S_ISGID; 371*53852Selan goto filebit; 372*53852Selan case ISSTICKY: 373*53852Selan i = S_ISVTX; 37453835Selan filebit: 375*53852Selan if (fs->stat.st_mode & i && fs->rcode >= 0) 376*53852Selan goto true; 377*53852Selan goto false; 378*53852Selan case ISSIZE: 379*53852Selan sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 380*53852Selan sp->type = INTEGER; 381*53852Selan break; 382*53852Selan case ISTTY: 383*53852Selan sp->u.num = isatty(sp->u.num); 384*53852Selan sp->type = BOOLEAN; 385*53852Selan break; 386*53852Selan case NULSTR: 387*53852Selan if (sp->u.string[0] == '\0') 388*53852Selan goto true; 389*53852Selan goto false; 390*53852Selan case STRLEN: 391*53852Selan sp->u.num = strlen(sp->u.string); 392*53852Selan sp->type = INTEGER; 393*53852Selan break; 394*53852Selan case OR1: 395*53852Selan case AND1: 396*53852Selan /* These operators are mostly handled by the parser. If we 397*53852Selan * get here it means that both operands were evaluated, so 398*53852Selan * the value is the value of the second operand. */ 399*53852Selan *sp = *(sp + 1); 400*53852Selan break; 401*53852Selan case STREQ: 402*53852Selan case STRNE: 403*53852Selan i = 0; 404*53852Selan if (equal(sp->u.string, (sp + 1)->u.string)) 405*53852Selan i++; 406*53852Selan if (op == STRNE) 407*53852Selan i = 1 - i; 408*53852Selan sp->u.num = i; 409*53852Selan sp->type = BOOLEAN; 410*53852Selan break; 411*53852Selan case EQ: 412*53852Selan if (sp->u.num == (sp + 1)->u.num) 413*53852Selan goto true; 414*53852Selan goto false; 415*53852Selan case NE: 416*53852Selan if (sp->u.num != (sp + 1)->u.num) 417*53852Selan goto true; 418*53852Selan goto false; 419*53852Selan case GT: 420*53852Selan if (sp->u.num > (sp + 1)->u.num) 421*53852Selan goto true; 422*53852Selan goto false; 423*53852Selan case LT: 424*53852Selan if (sp->u.num < (sp + 1)->u.num) 425*53852Selan goto true; 426*53852Selan goto false; 427*53852Selan case LE: 428*53852Selan if (sp->u.num <= (sp + 1)->u.num) 429*53852Selan goto true; 430*53852Selan goto false; 431*53852Selan case GE: 432*53852Selan if (sp->u.num >= (sp + 1)->u.num) 433*53852Selan goto true; 434*53852Selan goto false; 43553835Selan 436*53852Selan } 43753835Selan } 43853835Selan 43953835Selan 44053835Selan static int 44153835Selan lookup_op(name, table) 442*53852Selan char *name; 443*53852Selan char *const * table; 44453835Selan { 445*53852Selan register char *const * tp; 446*53852Selan register char const *p; 447*53852Selan char c = name[1]; 44853835Selan 449*53852Selan for (tp = table; (p = *tp) != NULL; tp++) { 450*53852Selan if (p[1] == c && equal(p, name)) 451*53852Selan return tp - table; 452*53852Selan } 453*53852Selan return -1; 45453835Selan } 45553835Selan 45653835Selan static int 45753835Selan posix_unary_op(argv) 458*53852Selan char **argv; 45953835Selan { 460*53852Selan int op, c; 461*53852Selan char *opname; 46253835Selan struct filestat fs; 46353835Selan struct value valp; 46453835Selan 46553835Selan opname = *argv; 466*53852Selan if ((op = lookup_op(opname, unary_op)) < 0) 46753835Selan return -1; 468*53852Selan c = op_argflag[op]; 469*53852Selan opname = argv[1]; 470*53852Selan valp.u.string = opname; 471*53852Selan if (c == OP_FILE) { 47253835Selan fs.name = opname; 47353835Selan fs.rcode = stat(opname, &fs.stat); 47453835Selan } else if (c != OP_STRING) 47553835Selan return -1; 47653835Selan 47753835Selan expr_operator(op, &valp, &fs); 47853835Selan return (valp.u.num == 0); 47953835Selan } 48053835Selan 48153835Selan 48253835Selan static int 48353835Selan posix_binary_op(argv) 484*53852Selan char **argv; 48553835Selan { 486*53852Selan int op, c; 487*53852Selan char *opname; 48853835Selan struct value v[2]; 489*53852Selan 49053835Selan opname = argv[1]; 49153835Selan if ((op = lookup_op(opname, binary_op)) < 0) 49253835Selan return -1; 49353835Selan op += FIRST_BINARY_OP; 49453835Selan c = op_argflag[op]; 49553835Selan 49653835Selan if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) { 49753835Selan v[0].u.num = atol(argv[0]); 49853835Selan v[1].u.num = atol(argv[2]); 49953835Selan } 50053835Selan expr_operator(op, v, NULL); 50153835Selan return (v[0].u.num == 0); 50253835Selan } 50353835Selan 50453835Selan /* 50553835Selan * Integer type checking. 50653835Selan */ 50753835Selan static int 50853835Selan int_tcheck(v) 509*53852Selan char *v; 51053835Selan { 511*53852Selan char *p; 512*53852Selan char outbuf[512]; 51353835Selan 51453835Selan for (p = v; *p != '\0'; p++) 51553835Selan if (*p < '0' || *p > '9') { 516*53852Selan snprintf(outbuf, sizeof(outbuf), 517*53852Selan "Illegal operand \"%s\" -- expected integer.", v); 518*53852Selan error(outbuf); 51953835Selan } 52053835Selan return 1; 52153835Selan } 522