1*53958Sbostic /*- 2*53958Sbostic * Copyright (c) 1992 The Regents of the University of California. 353852Selan * All rights reserved. 453835Selan * 553852Selan * This code is derived from software contributed to Berkeley by 653852Selan * Kenneth Almquist. 753852Selan * 853852Selan * %sccs.include.redist.c% 953835Selan */ 1053835Selan 1153852Selan #ifndef lint 12*53958Sbostic char copyright[] = 13*53958Sbostic "@(#) Copyright (c) 1992 The Regents of the University of California.\n\ 1453852Selan All rights reserved.\n"; 15*53958Sbostic #endif /* not lint */ 1653835Selan 1753852Selan #ifndef lint 18*53958Sbostic static char sccsid[] = "@(#)test.c 5.1 (Berkeley) 06/08/92"; 19*53958Sbostic #endif /* not lint */ 2053835Selan 2153835Selan #include <sys/types.h> 2253835Selan #include <sys/stat.h> 23*53958Sbostic #include <errno.h> 24*53958Sbostic #include <stdlib.h> 2553918Selan #include <string.h> 26*53958Sbostic #include <stdio.h> 27*53958Sbostic 2853835Selan #include "operators.h" 2953835Selan 30*53958Sbostic #define STACKSIZE 12 31*53958Sbostic #define NESTINCR 16 3253835Selan 3353835Selan /* data types */ 34*53958Sbostic #define STRING 0 35*53958Sbostic #define INTEGER 1 36*53958Sbostic #define BOOLEAN 2 3753835Selan 38*53958Sbostic #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 3953835Selan 4053835Selan /* 4153835Selan * This structure hold a value. The type keyword specifies the type of 4253835Selan * the value, and the union u holds the value. The value of a boolean 4353835Selan * is stored in u.num (1 = TRUE, 0 = FALSE). 4453835Selan */ 4553835Selan struct value { 46*53958Sbostic int type; 4753852Selan union { 48*53958Sbostic char *string; 49*53958Sbostic long num; 50*53958Sbostic } u; 5153835Selan }; 5253835Selan 5353835Selan struct operator { 54*53958Sbostic short op; /* Which operator. */ 55*53958Sbostic short pri; /* Priority of operator. */ 5653835Selan }; 5753835Selan 5853835Selan struct filestat { 59*53958Sbostic char *name; /* Name of file. */ 60*53958Sbostic int rcode; /* Return code from stat. */ 61*53958Sbostic struct stat stat; /* Status info on file. */ 6253835Selan }; 6353835Selan 64*53958Sbostic static long atol __P((const char *)); 65*53958Sbostic static void err __P((const char *, ...)); 66*53958Sbostic static int expr_is_false __P((struct value *)); 67*53958Sbostic static void expr_operator __P((int, struct value *, struct filestat *)); 68*53958Sbostic static int int_tcheck __P((char *)); 69*53958Sbostic static int lookup_op __P((char *, char *const *)); 70*53958Sbostic static void overflow __P((void)); 71*53958Sbostic static int posix_binary_op __P((char **)); 72*53958Sbostic static int posix_unary_op __P((char **)); 73*53958Sbostic static void syntax __P((void)); 7453835Selan 7553835Selan int 7653852Selan main(argc, argv) 77*53958Sbostic int argc; 78*53958Sbostic char *argv[]; 7953835Selan { 8053852Selan struct operator opstack[STACKSIZE]; 8153852Selan struct operator *opsp; 8253852Selan struct value valstack[STACKSIZE + 1]; 8353852Selan struct value *valsp; 8453852Selan struct filestat fs; 85*53958Sbostic char c, **ap, *opname, *p; 86*53958Sbostic int binary, nest, op, pri, ret_val, skipping; 8753835Selan 88*53958Sbostic if (argv[0] == NULL) { 89*53958Sbostic err("test: argc is zero.\n"); 90*53958Sbostic exit(2); 91*53958Sbostic } 92*53958Sbostic 9353852Selan if (**argv == '[') { 94*53958Sbostic if (strcmp(argv[argc - 1], "]")) 95*53958Sbostic err("missing ]"); 9653852Selan argv[argc - 1] = NULL; 9753852Selan } 9853852Selan ap = argv + 1; 9953852Selan fs.name = NULL; 10053835Selan 101*53958Sbostic /* 102*53958Sbostic * Test(1) implements an inherently ambiguous grammer. In order to 103*53958Sbostic * assure some degree of consistency, we special case the POSIX 1003.2 104*53958Sbostic * requirements to assure correct evaluation for POSIX scripts. The 105*53958Sbostic * following special cases comply with POSIX P1003.2/D11.2 Section 106*53958Sbostic * 4.62.4. 107*53958Sbostic */ 108*53958Sbostic switch(argc - 1) { 109*53958Sbostic case 0: /* % test */ 110*53958Sbostic return (1); 11153852Selan break; 112*53958Sbostic case 1: /* % test arg */ 11353852Selan return (*argv[1] == '\0') ? 1 : 0; 11453852Selan break; 115*53958Sbostic case 2: /* % test op arg */ 11653852Selan opname = argv[1]; 11753852Selan if (IS_BANG(opname)) 11853852Selan return (*argv[2] == '\0') ? 1 : 0; 11953852Selan else { 12053852Selan ret_val = posix_unary_op(&argv[1]); 12153852Selan if (ret_val >= 0) 122*53958Sbostic return (ret_val); 12353852Selan } 12453852Selan break; 125*53958Sbostic case 3: /* % test arg1 op arg2 */ 12653852Selan if (IS_BANG(argv[1])) { 12753852Selan ret_val = posix_unary_op(&argv[1]); 12853852Selan if (ret_val >= 0) 129*53958Sbostic return (!ret_val); 13053852Selan } else { 13153852Selan ret_val = posix_binary_op(&argv[1]); 13253852Selan if (ret_val >= 0) 133*53958Sbostic return (ret_val); 13453852Selan } 13553852Selan break; 136*53958Sbostic case 4: /* % test ! arg1 op arg2 */ 13753852Selan if (IS_BANG(argv[1])) { 13853852Selan ret_val = posix_binary_op(&argv[2]); 13953852Selan if (ret_val >= 0) 140*53958Sbostic return (!ret_val); 14153852Selan } 14253852Selan break; 14353852Selan default: 14453852Selan break; 14553852Selan } 14653835Selan 147*53958Sbostic /* 148*53958Sbostic * We use operator precedence parsing, evaluating the expression as 14953852Selan * we parse it. Parentheses are handled by bumping up the priority 15053852Selan * of operators using the variable "nest." We use the variable 15153852Selan * "skipping" to turn off evaluation temporarily for the short 15253852Selan * circuit boolean operators. (It is important do the short circuit 15353852Selan * evaluation because under NFS a stat operation can take infinitely 154*53958Sbostic * long.) 155*53958Sbostic */ 15653852Selan opsp = opstack + STACKSIZE; 15753852Selan valsp = valstack; 158*53958Sbostic nest = skipping = 0; 15953852Selan if (*ap == NULL) { 16053852Selan valstack[0].type = BOOLEAN; 16153852Selan valstack[0].u.num = 0; 16253852Selan goto done; 16353852Selan } 16453852Selan for (;;) { 16553852Selan opname = *ap++; 16653852Selan if (opname == NULL) 167*53958Sbostic syntax(); 16853852Selan if (opname[0] == '(' && opname[1] == '\0') { 16953852Selan nest += NESTINCR; 17053852Selan continue; 17153852Selan } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 17253852Selan if (opsp == &opstack[0]) 173*53958Sbostic overflow(); 17453852Selan --opsp; 17553852Selan opsp->op = op; 17653852Selan opsp->pri = op_priority[op] + nest; 17753852Selan continue; 17853852Selan } else { 17953852Selan valsp->type = STRING; 18053852Selan valsp->u.string = opname; 18153852Selan valsp++; 18253852Selan } 18353852Selan for (;;) { 18453852Selan opname = *ap++; 18553852Selan if (opname == NULL) { 18653852Selan if (nest != 0) 187*53958Sbostic syntax(); 18853852Selan pri = 0; 18953852Selan break; 19053852Selan } 19153852Selan if (opname[0] != ')' || opname[1] != '\0') { 19253852Selan if ((op = lookup_op(opname, binary_op)) < 0) 193*53958Sbostic syntax(); 19453852Selan op += FIRST_BINARY_OP; 19553852Selan pri = op_priority[op] + nest; 19653852Selan break; 19753852Selan } 19853852Selan if ((nest -= NESTINCR) < 0) 199*53958Sbostic syntax(); 20053852Selan } 20153852Selan while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 20253852Selan binary = opsp->op; 20353852Selan for (;;) { 20453852Selan valsp--; 20553852Selan c = op_argflag[opsp->op]; 20653852Selan if (c == OP_INT) { 20753852Selan if (valsp->type == STRING && 20853852Selan int_tcheck(valsp->u.string)) 20953852Selan valsp->u.num = 21053852Selan atol(valsp->u.string); 21153852Selan valsp->type = INTEGER; 21253852Selan } else if (c >= OP_STRING) { 21353852Selan /* OP_STRING or OP_FILE */ 21453852Selan if (valsp->type == INTEGER) { 215*53958Sbostic if ((p = malloc(32)) == NULL) 216*53958Sbostic err("%s", 217*53958Sbostic strerror(errno)); 21853835Selan #ifdef SHELL 21953852Selan fmtstr(p, 32, "%d", 22053852Selan valsp->u.num); 22153835Selan #else 222*53958Sbostic (void)sprintf(p, 223*53958Sbostic "%d", valsp->u.num); 22453835Selan #endif 22553852Selan valsp->u.string = p; 22653852Selan } else if (valsp->type == BOOLEAN) { 22753852Selan if (valsp->u.num) 22853852Selan valsp->u.string = 22953852Selan "true"; 23053852Selan else 23153852Selan valsp->u.string = ""; 23253852Selan } 23353852Selan valsp->type = STRING; 234*53958Sbostic if (c == OP_FILE && (fs.name == NULL || 235*53958Sbostic strcmp(fs.name, valsp->u.string))) { 23653852Selan fs.name = valsp->u.string; 23753852Selan fs.rcode = 23853852Selan stat(valsp->u.string, 23953852Selan &fs.stat); 24053852Selan } 24153852Selan } 24253852Selan if (binary < FIRST_BINARY_OP) 24353852Selan break; 24453852Selan binary = 0; 24553835Selan } 24653852Selan if (!skipping) 24753852Selan expr_operator(opsp->op, valsp, &fs); 24853852Selan else if (opsp->op == AND1 || opsp->op == OR1) 24953852Selan skipping--; 250*53958Sbostic valsp++; /* push value */ 251*53958Sbostic opsp++; /* pop operator */ 25253852Selan } 25353852Selan if (opname == NULL) 25453852Selan break; 25553852Selan if (opsp == &opstack[0]) 256*53958Sbostic overflow(); 25753852Selan if (op == AND1 || op == AND2) { 25853852Selan op = AND1; 25953852Selan if (skipping || expr_is_false(valsp - 1)) 26053852Selan skipping++; 26153852Selan } 26253852Selan if (op == OR1 || op == OR2) { 26353852Selan op = OR1; 26453852Selan if (skipping || !expr_is_false(valsp - 1)) 26553852Selan skipping++; 26653852Selan } 26753852Selan opsp--; 26853852Selan opsp->op = op; 26953852Selan opsp->pri = pri; 27053852Selan } 271*53958Sbostic done: return (expr_is_false(&valstack[0])); 27253835Selan } 27353835Selan 27453835Selan static int 27553835Selan expr_is_false(val) 27653852Selan struct value *val; 27753835Selan { 27853852Selan if (val->type == STRING) { 27953852Selan if (val->u.string[0] == '\0') 280*53958Sbostic return (1); 28153852Selan } else { /* INTEGER or BOOLEAN */ 28253852Selan if (val->u.num == 0) 283*53958Sbostic return (1); 28453852Selan } 285*53958Sbostic return (0); 28653835Selan } 28753835Selan 28853835Selan 28953835Selan /* 29053835Selan * Execute an operator. Op is the operator. Sp is the stack pointer; 29153835Selan * sp[0] refers to the first operand, sp[1] refers to the second operand 29253835Selan * (if any), and the result is placed in sp[0]. The operands are converted 29353835Selan * to the type expected by the operator before expr_operator is called. 29453835Selan * Fs is a pointer to a structure which holds the value of the last call 29553835Selan * to stat, to avoid repeated stat calls on the same file. 29653835Selan */ 29753835Selan static void 29853835Selan expr_operator(op, sp, fs) 299*53958Sbostic int op; 30053835Selan struct value *sp; 30153835Selan struct filestat *fs; 30253835Selan { 303*53958Sbostic int i; 30453835Selan 30553835Selan switch (op) { 30653835Selan case NOT: 30753852Selan sp->u.num = expr_is_false(sp); 30853852Selan sp->type = BOOLEAN; 30953852Selan break; 31053852Selan case ISEXIST: 31153852Selan if (fs == NULL || fs->rcode == -1) 31253852Selan goto false; 31353852Selan else 31453852Selan goto true; 31553852Selan case ISREAD: 316*53958Sbostic i = S_IROTH; 31753852Selan goto permission; 31853852Selan case ISWRITE: 319*53958Sbostic i = S_IWOTH; 32053852Selan goto permission; 32153852Selan case ISEXEC: 322*53958Sbostic i = S_IXOTH; 323*53958Sbostic permission: if (fs->stat.st_uid == geteuid()) 32453852Selan i <<= 6; 32553852Selan else if (fs->stat.st_gid == getegid()) 32653852Selan i <<= 3; 32753852Selan goto filebit; /* true if (stat.st_mode & i) != 0 */ 32853852Selan case ISFILE: 32953852Selan i = S_IFREG; 33053852Selan goto filetype; 33153852Selan case ISDIR: 33253852Selan i = S_IFDIR; 33353852Selan goto filetype; 33453852Selan case ISCHAR: 33553852Selan i = S_IFCHR; 33653852Selan goto filetype; 33753852Selan case ISBLOCK: 33853852Selan i = S_IFBLK; 33953852Selan goto filetype; 34053852Selan case ISFIFO: 34153852Selan i = S_IFIFO; 34253852Selan goto filetype; 343*53958Sbostic filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 344*53958Sbostic true: sp->u.num = 1; 345*53958Sbostic else 346*53958Sbostic false: sp->u.num = 0; 34753852Selan sp->type = BOOLEAN; 34853852Selan break; 34953852Selan case ISSETUID: 35053852Selan i = S_ISUID; 35153852Selan goto filebit; 35253852Selan case ISSETGID: 35353852Selan i = S_ISGID; 35453852Selan goto filebit; 35553852Selan case ISSTICKY: 35653852Selan i = S_ISVTX; 357*53958Sbostic filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 35853852Selan goto true; 35953852Selan goto false; 36053852Selan case ISSIZE: 36153852Selan sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 36253852Selan sp->type = INTEGER; 36353852Selan break; 36453852Selan case ISTTY: 36553852Selan sp->u.num = isatty(sp->u.num); 36653852Selan sp->type = BOOLEAN; 36753852Selan break; 36853852Selan case NULSTR: 36953852Selan if (sp->u.string[0] == '\0') 37053852Selan goto true; 37153852Selan goto false; 37253852Selan case STRLEN: 37353852Selan sp->u.num = strlen(sp->u.string); 37453852Selan sp->type = INTEGER; 37553852Selan break; 37653852Selan case OR1: 37753852Selan case AND1: 378*53958Sbostic /* 379*53958Sbostic * These operators are mostly handled by the parser. If we 38053852Selan * get here it means that both operands were evaluated, so 381*53958Sbostic * the value is the value of the second operand. 382*53958Sbostic */ 38353852Selan *sp = *(sp + 1); 38453852Selan break; 38553852Selan case STREQ: 38653852Selan case STRNE: 38753852Selan i = 0; 388*53958Sbostic if (!strcmp(sp->u.string, (sp + 1)->u.string)) 38953852Selan i++; 39053852Selan if (op == STRNE) 39153852Selan i = 1 - i; 39253852Selan sp->u.num = i; 39353852Selan sp->type = BOOLEAN; 39453852Selan break; 39553852Selan case EQ: 39653852Selan if (sp->u.num == (sp + 1)->u.num) 39753852Selan goto true; 39853852Selan goto false; 39953852Selan case NE: 40053852Selan if (sp->u.num != (sp + 1)->u.num) 40153852Selan goto true; 40253852Selan goto false; 40353852Selan case GT: 40453852Selan if (sp->u.num > (sp + 1)->u.num) 40553852Selan goto true; 40653852Selan goto false; 40753852Selan case LT: 40853852Selan if (sp->u.num < (sp + 1)->u.num) 40953852Selan goto true; 41053852Selan goto false; 41153852Selan case LE: 41253852Selan if (sp->u.num <= (sp + 1)->u.num) 41353852Selan goto true; 41453852Selan goto false; 41553852Selan case GE: 41653852Selan if (sp->u.num >= (sp + 1)->u.num) 41753852Selan goto true; 41853852Selan goto false; 41953835Selan 42053852Selan } 42153835Selan } 42253835Selan 42353835Selan static int 42453835Selan lookup_op(name, table) 42553852Selan char *name; 42653852Selan char *const * table; 42753835Selan { 42853852Selan register char *const * tp; 42953852Selan register char const *p; 430*53958Sbostic char c; 43153835Selan 432*53958Sbostic c = name[1]; 433*53958Sbostic for (tp = table; (p = *tp) != NULL; tp++) 434*53958Sbostic if (p[1] == c && !strcmp(p, name)) 435*53958Sbostic return (tp - table); 436*53958Sbostic return (-1); 43753835Selan } 43853835Selan 43953835Selan static int 44053835Selan posix_unary_op(argv) 441*53958Sbostic char **argv; 44253835Selan { 44353835Selan struct filestat fs; 44453835Selan struct value valp; 445*53958Sbostic int op, c; 446*53958Sbostic char *opname; 44753835Selan 44853835Selan opname = *argv; 44953852Selan if ((op = lookup_op(opname, unary_op)) < 0) 450*53958Sbostic return (-1); 45153852Selan c = op_argflag[op]; 45253852Selan opname = argv[1]; 45353852Selan valp.u.string = opname; 45453852Selan if (c == OP_FILE) { 45553835Selan fs.name = opname; 45653835Selan fs.rcode = stat(opname, &fs.stat); 45753835Selan } else if (c != OP_STRING) 458*53958Sbostic return (-1); 45953835Selan 46053835Selan expr_operator(op, &valp, &fs); 46153835Selan return (valp.u.num == 0); 46253835Selan } 46353835Selan 46453835Selan static int 46553835Selan posix_binary_op(argv) 46653852Selan char **argv; 46753835Selan { 46853835Selan struct value v[2]; 469*53958Sbostic int op, c; 470*53958Sbostic char *opname; 47153852Selan 47253835Selan opname = argv[1]; 47353835Selan if ((op = lookup_op(opname, binary_op)) < 0) 474*53958Sbostic return (-1); 47553835Selan op += FIRST_BINARY_OP; 47653835Selan c = op_argflag[op]; 47753835Selan 47853835Selan if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) { 47953835Selan v[0].u.num = atol(argv[0]); 48053835Selan v[1].u.num = atol(argv[2]); 48153918Selan } else { 48253918Selan v[0].u.string = argv[0]; 48353918Selan v[1].u.string = argv[2]; 48453835Selan } 48553835Selan expr_operator(op, v, NULL); 48653835Selan return (v[0].u.num == 0); 48753835Selan } 48853835Selan 48953835Selan /* 49053835Selan * Integer type checking. 49153835Selan */ 49253835Selan static int 49353835Selan int_tcheck(v) 494*53958Sbostic char *v; 49553835Selan { 496*53958Sbostic char *p; 49753835Selan 49853835Selan for (p = v; *p != '\0'; p++) 499*53958Sbostic if (!isdigit(*p)) 500*53958Sbostic err("illegal operand \"%s\" -- expected integer.", v); 501*53958Sbostic return (1); 50253835Selan } 503*53958Sbostic 504*53958Sbostic static void 505*53958Sbostic syntax() 506*53958Sbostic { 507*53958Sbostic err("syntax error"); 508*53958Sbostic } 509*53958Sbostic 510*53958Sbostic static void 511*53958Sbostic overflow() 512*53958Sbostic { 513*53958Sbostic err("expression is too complex"); 514*53958Sbostic } 515*53958Sbostic 516*53958Sbostic #if __STDC__ 517*53958Sbostic #include <stdarg.h> 518*53958Sbostic #else 519*53958Sbostic #include <varargs.h> 520*53958Sbostic #endif 521*53958Sbostic 522*53958Sbostic void 523*53958Sbostic #if __STDC__ 524*53958Sbostic err(const char *fmt, ...) 525*53958Sbostic #else 526*53958Sbostic err(fmt, va_alist) 527*53958Sbostic char *fmt; 528*53958Sbostic va_dcl 529*53958Sbostic #endif 530*53958Sbostic { 531*53958Sbostic va_list ap; 532*53958Sbostic #if __STDC__ 533*53958Sbostic va_start(ap, fmt); 534*53958Sbostic #else 535*53958Sbostic va_start(ap); 536*53958Sbostic #endif 537*53958Sbostic (void)fprintf(stderr, "test: "); 538*53958Sbostic (void)vfprintf(stderr, fmt, ap); 539*53958Sbostic va_end(ap); 540*53958Sbostic (void)fprintf(stderr, "\n"); 541*53958Sbostic exit(2); 542*53958Sbostic /* NOTREACHED */ 543*53958Sbostic } 544