153958Sbostic /*- 260726Sbostic * Copyright (c) 1992, 1993 360726Sbostic * The Regents of the University of California. 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 1260726Sbostic static char copyright[] = 1360726Sbostic "@(#) Copyright (c) 1992, 1993\n\ 1460726Sbostic The Regents of the University of California. All rights reserved.\n"; 1553958Sbostic #endif /* not lint */ 1653835Selan 1753852Selan #ifndef lint 18*66565Spendry static char sccsid[] = "@(#)test.c 8.2 (Berkeley) 04/01/94"; 1953958Sbostic #endif /* not lint */ 2053835Selan 2153835Selan #include <sys/types.h> 2253835Selan #include <sys/stat.h> 2359571Sbostic 2459576Sbostic #include <ctype.h> 2559571Sbostic #include <err.h> 2653958Sbostic #include <errno.h> 2759571Sbostic #include <limits.h> 2859571Sbostic #include <stdio.h> 2953958Sbostic #include <stdlib.h> 3053918Selan #include <string.h> 3159576Sbostic #include <unistd.h> 3253958Sbostic 3353835Selan #include "operators.h" 3453835Selan 3553958Sbostic #define STACKSIZE 12 3653958Sbostic #define NESTINCR 16 3753835Selan 3853835Selan /* data types */ 3953958Sbostic #define STRING 0 4053958Sbostic #define INTEGER 1 4153958Sbostic #define BOOLEAN 2 4253835Selan 4353958Sbostic #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 4453835Selan 4553835Selan /* 4653835Selan * This structure hold a value. The type keyword specifies the type of 4753835Selan * the value, and the union u holds the value. The value of a boolean 4853835Selan * is stored in u.num (1 = TRUE, 0 = FALSE). 4953835Selan */ 5053835Selan struct value { 5153958Sbostic int type; 5253852Selan union { 5353958Sbostic char *string; 5453958Sbostic long num; 5553958Sbostic } u; 5653835Selan }; 5753835Selan 5853835Selan struct operator { 5953958Sbostic short op; /* Which operator. */ 6053958Sbostic short pri; /* Priority of operator. */ 6153835Selan }; 6253835Selan 6353835Selan struct filestat { 6453958Sbostic char *name; /* Name of file. */ 6553958Sbostic int rcode; /* Return code from stat. */ 6653958Sbostic struct stat stat; /* Status info on file. */ 6753835Selan }; 6853835Selan 6953958Sbostic static int expr_is_false __P((struct value *)); 7053958Sbostic static void expr_operator __P((int, struct value *, struct filestat *)); 7159571Sbostic static void get_int __P((char *, long *)); 72*66565Spendry static int lookup_op __P((char *, const char *const *)); 7353958Sbostic static void overflow __P((void)); 7453958Sbostic static int posix_binary_op __P((char **)); 7553958Sbostic static int posix_unary_op __P((char **)); 7653958Sbostic static void syntax __P((void)); 7753835Selan 7853835Selan int 7953852Selan main(argc, argv) 8053958Sbostic int argc; 8153958Sbostic char *argv[]; 8253835Selan { 8353852Selan struct operator opstack[STACKSIZE]; 8453852Selan struct operator *opsp; 8553852Selan struct value valstack[STACKSIZE + 1]; 8653852Selan struct value *valsp; 8753852Selan struct filestat fs; 8853958Sbostic char c, **ap, *opname, *p; 8953958Sbostic int binary, nest, op, pri, ret_val, skipping; 9053835Selan 9159571Sbostic if ((p = argv[0]) == NULL) 9259571Sbostic errx(2, "test: argc is zero"); 9353958Sbostic 9457920Storek if (*p != '\0' && p[strlen(p) - 1] == '[') { 9557920Storek if (strcmp(argv[--argc], "]")) 9659571Sbostic errx(2, "missing ]"); 9757920Storek argv[argc] = NULL; 9853852Selan } 9953852Selan ap = argv + 1; 10053852Selan fs.name = NULL; 10153835Selan 10253958Sbostic /* 10353958Sbostic * Test(1) implements an inherently ambiguous grammer. In order to 10453958Sbostic * assure some degree of consistency, we special case the POSIX 1003.2 10553958Sbostic * requirements to assure correct evaluation for POSIX scripts. The 10653958Sbostic * following special cases comply with POSIX P1003.2/D11.2 Section 10753958Sbostic * 4.62.4. 10853958Sbostic */ 10953958Sbostic switch(argc - 1) { 11053958Sbostic case 0: /* % test */ 11153958Sbostic return (1); 11253852Selan break; 11353958Sbostic case 1: /* % test arg */ 11459571Sbostic return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; 11553852Selan break; 11653958Sbostic case 2: /* % test op arg */ 11753852Selan opname = argv[1]; 11853852Selan if (IS_BANG(opname)) 11960589Sbostic return (*argv[2] == '\0') ? 0 : 1; 12053852Selan else { 12153852Selan ret_val = posix_unary_op(&argv[1]); 12253852Selan if (ret_val >= 0) 12353958Sbostic return (ret_val); 12453852Selan } 12553852Selan break; 12653958Sbostic case 3: /* % test arg1 op arg2 */ 12753852Selan if (IS_BANG(argv[1])) { 12853852Selan ret_val = posix_unary_op(&argv[1]); 12953852Selan if (ret_val >= 0) 13053958Sbostic return (!ret_val); 13153852Selan } else { 13253852Selan ret_val = posix_binary_op(&argv[1]); 13353852Selan if (ret_val >= 0) 13453958Sbostic return (ret_val); 13553852Selan } 13653852Selan break; 13753958Sbostic case 4: /* % test ! arg1 op arg2 */ 13853852Selan if (IS_BANG(argv[1])) { 13953852Selan ret_val = posix_binary_op(&argv[2]); 14053852Selan if (ret_val >= 0) 14153958Sbostic return (!ret_val); 14253852Selan } 14353852Selan break; 14453852Selan default: 14553852Selan break; 14653852Selan } 14753835Selan 14853958Sbostic /* 14953958Sbostic * We use operator precedence parsing, evaluating the expression as 15053852Selan * we parse it. Parentheses are handled by bumping up the priority 15153852Selan * of operators using the variable "nest." We use the variable 15253852Selan * "skipping" to turn off evaluation temporarily for the short 15353852Selan * circuit boolean operators. (It is important do the short circuit 15453852Selan * evaluation because under NFS a stat operation can take infinitely 15553958Sbostic * long.) 15653958Sbostic */ 15753852Selan opsp = opstack + STACKSIZE; 15853852Selan valsp = valstack; 15953958Sbostic nest = skipping = 0; 16053852Selan if (*ap == NULL) { 16153852Selan valstack[0].type = BOOLEAN; 16253852Selan valstack[0].u.num = 0; 16353852Selan goto done; 16453852Selan } 16553852Selan for (;;) { 16653852Selan opname = *ap++; 16753852Selan if (opname == NULL) 16853958Sbostic syntax(); 16953852Selan if (opname[0] == '(' && opname[1] == '\0') { 17053852Selan nest += NESTINCR; 17153852Selan continue; 17253852Selan } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 17353852Selan if (opsp == &opstack[0]) 17453958Sbostic overflow(); 17553852Selan --opsp; 17653852Selan opsp->op = op; 17753852Selan opsp->pri = op_priority[op] + nest; 17853852Selan continue; 17953852Selan } else { 18053852Selan valsp->type = STRING; 18153852Selan valsp->u.string = opname; 18253852Selan valsp++; 18353852Selan } 18453852Selan for (;;) { 18553852Selan opname = *ap++; 18653852Selan if (opname == NULL) { 18753852Selan if (nest != 0) 18853958Sbostic syntax(); 18953852Selan pri = 0; 19053852Selan break; 19153852Selan } 19253852Selan if (opname[0] != ')' || opname[1] != '\0') { 19353852Selan if ((op = lookup_op(opname, binary_op)) < 0) 19453958Sbostic syntax(); 19553852Selan op += FIRST_BINARY_OP; 19653852Selan pri = op_priority[op] + nest; 19753852Selan break; 19853852Selan } 19953852Selan if ((nest -= NESTINCR) < 0) 20053958Sbostic syntax(); 20153852Selan } 20253852Selan while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 20353852Selan binary = opsp->op; 20453852Selan for (;;) { 20553852Selan valsp--; 20653852Selan c = op_argflag[opsp->op]; 20753852Selan if (c == OP_INT) { 20859571Sbostic if (valsp->type == STRING) 20959571Sbostic get_int(valsp->u.string, 21059571Sbostic &valsp->u.num); 21153852Selan valsp->type = INTEGER; 21253852Selan } else if (c >= OP_STRING) { 21353852Selan /* OP_STRING or OP_FILE */ 21453852Selan if (valsp->type == INTEGER) { 21553958Sbostic if ((p = malloc(32)) == NULL) 21659571Sbostic err(2, NULL); 21753835Selan #ifdef SHELL 21853852Selan fmtstr(p, 32, "%d", 21953852Selan valsp->u.num); 22053835Selan #else 22153958Sbostic (void)sprintf(p, 22253958Sbostic "%d", valsp->u.num); 22353835Selan #endif 22453852Selan valsp->u.string = p; 22553852Selan } else if (valsp->type == BOOLEAN) { 22653852Selan if (valsp->u.num) 22753852Selan valsp->u.string = 22853852Selan "true"; 22953852Selan else 23053852Selan valsp->u.string = ""; 23153852Selan } 23253852Selan valsp->type = STRING; 23353958Sbostic if (c == OP_FILE && (fs.name == NULL || 23453958Sbostic strcmp(fs.name, valsp->u.string))) { 23553852Selan fs.name = valsp->u.string; 23653852Selan fs.rcode = 23753852Selan stat(valsp->u.string, 23853852Selan &fs.stat); 23953852Selan } 24053852Selan } 24153852Selan if (binary < FIRST_BINARY_OP) 24253852Selan break; 24353852Selan binary = 0; 24453835Selan } 24553852Selan if (!skipping) 24653852Selan expr_operator(opsp->op, valsp, &fs); 24753852Selan else if (opsp->op == AND1 || opsp->op == OR1) 24853852Selan skipping--; 24953958Sbostic valsp++; /* push value */ 25053958Sbostic opsp++; /* pop operator */ 25153852Selan } 25253852Selan if (opname == NULL) 25353852Selan break; 25453852Selan if (opsp == &opstack[0]) 25553958Sbostic overflow(); 25653852Selan if (op == AND1 || op == AND2) { 25753852Selan op = AND1; 25853852Selan if (skipping || expr_is_false(valsp - 1)) 25953852Selan skipping++; 26053852Selan } 26153852Selan if (op == OR1 || op == OR2) { 26253852Selan op = OR1; 26353852Selan if (skipping || !expr_is_false(valsp - 1)) 26453852Selan skipping++; 26553852Selan } 26653852Selan opsp--; 26753852Selan opsp->op = op; 26853852Selan opsp->pri = pri; 26953852Selan } 27053958Sbostic done: return (expr_is_false(&valstack[0])); 27153835Selan } 27253835Selan 27353835Selan static int 27453835Selan expr_is_false(val) 27553852Selan struct value *val; 27653835Selan { 277*66565Spendry 27853852Selan if (val->type == STRING) { 27953852Selan if (val->u.string[0] == '\0') 28053958Sbostic return (1); 28153852Selan } else { /* INTEGER or BOOLEAN */ 28253852Selan if (val->u.num == 0) 28353958Sbostic return (1); 28453852Selan } 28553958Sbostic 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) 29953958Sbostic int op; 30053835Selan struct value *sp; 30153835Selan struct filestat *fs; 30253835Selan { 30353958Sbostic 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: 31653958Sbostic i = S_IROTH; 31753852Selan goto permission; 31853852Selan case ISWRITE: 31953958Sbostic i = S_IWOTH; 32053852Selan goto permission; 32153852Selan case ISEXEC: 32253958Sbostic i = S_IXOTH; 32353958Sbostic 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; 34060597Sbostic case ISSYMLINK: 34160597Sbostic i = S_IFLNK; 34260597Sbostic (void)lstat(sp->u.string, &fs->stat); 34360597Sbostic goto filetype; 34453852Selan case ISFIFO: 34553852Selan i = S_IFIFO; 34653852Selan goto filetype; 34753958Sbostic filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 34853958Sbostic true: sp->u.num = 1; 34953958Sbostic else 35053958Sbostic false: sp->u.num = 0; 35153852Selan sp->type = BOOLEAN; 35253852Selan break; 35353852Selan case ISSETUID: 35453852Selan i = S_ISUID; 35553852Selan goto filebit; 35653852Selan case ISSETGID: 35753852Selan i = S_ISGID; 35853852Selan goto filebit; 35953852Selan case ISSTICKY: 36053852Selan i = S_ISVTX; 36153958Sbostic filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 36253852Selan goto true; 36353852Selan goto false; 36453852Selan case ISSIZE: 36553852Selan sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 36653852Selan sp->type = INTEGER; 36753852Selan break; 36853852Selan case ISTTY: 36953852Selan sp->u.num = isatty(sp->u.num); 37053852Selan sp->type = BOOLEAN; 37153852Selan break; 37253852Selan case NULSTR: 37353852Selan if (sp->u.string[0] == '\0') 37453852Selan goto true; 37553852Selan goto false; 37653852Selan case STRLEN: 37753852Selan sp->u.num = strlen(sp->u.string); 37853852Selan sp->type = INTEGER; 37953852Selan break; 38053852Selan case OR1: 38153852Selan case AND1: 38253958Sbostic /* 38353958Sbostic * These operators are mostly handled by the parser. If we 38453852Selan * get here it means that both operands were evaluated, so 38553958Sbostic * the value is the value of the second operand. 38653958Sbostic */ 38753852Selan *sp = *(sp + 1); 38853852Selan break; 38953852Selan case STREQ: 39053852Selan case STRNE: 39153852Selan i = 0; 39253958Sbostic if (!strcmp(sp->u.string, (sp + 1)->u.string)) 39353852Selan i++; 39453852Selan if (op == STRNE) 39553852Selan i = 1 - i; 39653852Selan sp->u.num = i; 39753852Selan sp->type = BOOLEAN; 39853852Selan break; 39953852Selan case EQ: 40053852Selan if (sp->u.num == (sp + 1)->u.num) 40153852Selan goto true; 40253852Selan goto false; 40353852Selan case NE: 40453852Selan if (sp->u.num != (sp + 1)->u.num) 40553852Selan goto true; 40653852Selan goto false; 40753852Selan case GT: 40853852Selan if (sp->u.num > (sp + 1)->u.num) 40953852Selan goto true; 41053852Selan goto false; 41153852Selan case LT: 41253852Selan if (sp->u.num < (sp + 1)->u.num) 41353852Selan goto true; 41453852Selan goto false; 41553852Selan case LE: 41653852Selan if (sp->u.num <= (sp + 1)->u.num) 41753852Selan goto true; 41853852Selan goto false; 41953852Selan case GE: 42053852Selan if (sp->u.num >= (sp + 1)->u.num) 42153852Selan goto true; 42253852Selan goto false; 42353835Selan 42453852Selan } 42553835Selan } 42653835Selan 42753835Selan static int 42853835Selan lookup_op(name, table) 429*66565Spendry char *name; 430*66565Spendry const char *const * table; 43153835Selan { 432*66565Spendry const char *const * tp; 433*66565Spendry const char *p; 43453958Sbostic char c; 43553835Selan 43653958Sbostic c = name[1]; 43753958Sbostic for (tp = table; (p = *tp) != NULL; tp++) 43853958Sbostic if (p[1] == c && !strcmp(p, name)) 43953958Sbostic return (tp - table); 44053958Sbostic return (-1); 44153835Selan } 44253835Selan 44353835Selan static int 44453835Selan posix_unary_op(argv) 44553958Sbostic char **argv; 44653835Selan { 44753835Selan struct filestat fs; 44853835Selan struct value valp; 44953958Sbostic int op, c; 45053958Sbostic char *opname; 45153835Selan 45253835Selan opname = *argv; 45353852Selan if ((op = lookup_op(opname, unary_op)) < 0) 45453958Sbostic return (-1); 45553852Selan c = op_argflag[op]; 45653852Selan opname = argv[1]; 45753852Selan valp.u.string = opname; 45853852Selan if (c == OP_FILE) { 45953835Selan fs.name = opname; 46053835Selan fs.rcode = stat(opname, &fs.stat); 46153835Selan } else if (c != OP_STRING) 46253958Sbostic return (-1); 46353835Selan 46453835Selan expr_operator(op, &valp, &fs); 46553835Selan return (valp.u.num == 0); 46653835Selan } 46753835Selan 46853835Selan static int 46953835Selan posix_binary_op(argv) 47053852Selan char **argv; 47153835Selan { 47253835Selan struct value v[2]; 47353958Sbostic int op, c; 47453958Sbostic char *opname; 47553852Selan 47653835Selan opname = argv[1]; 47753835Selan if ((op = lookup_op(opname, binary_op)) < 0) 47853958Sbostic return (-1); 47953835Selan op += FIRST_BINARY_OP; 48053835Selan c = op_argflag[op]; 48153835Selan 48259571Sbostic if (c == OP_INT) { 48359571Sbostic get_int(argv[0], &v[0].u.num); 48459571Sbostic get_int(argv[2], &v[1].u.num); 48553918Selan } else { 48653918Selan v[0].u.string = argv[0]; 48753918Selan v[1].u.string = argv[2]; 48853835Selan } 48953835Selan expr_operator(op, v, NULL); 49053835Selan return (v[0].u.num == 0); 49153835Selan } 49253835Selan 49353835Selan /* 49453835Selan * Integer type checking. 49553835Selan */ 49659571Sbostic static void 49759571Sbostic get_int(v, lp) 49853958Sbostic char *v; 49959571Sbostic long *lp; 50053835Selan { 50159571Sbostic long val; 50259571Sbostic char *ep; 50353835Selan 50459571Sbostic for (; *v && isspace(*v); ++v); 50559571Sbostic if (isdigit(*v)) { 50659571Sbostic errno = 0; 50759571Sbostic val = strtol(v, &ep, 10); 50859571Sbostic if (*ep != '\0') 50959571Sbostic errx(2, "%s: trailing non-numeric characters", v); 51059571Sbostic if (errno == ERANGE) { 51159571Sbostic if (val == LONG_MIN) 51259571Sbostic errx(2, "%s: underflow", v); 51359571Sbostic if (val == LONG_MAX) 51459571Sbostic errx(2, "%s: overflow", v); 51559571Sbostic } 51659571Sbostic *lp = val; 51759571Sbostic return; 51859571Sbostic } 51959571Sbostic errx(2, "%s: expected integer", v); 52053835Selan } 52153958Sbostic 52253958Sbostic static void 52353958Sbostic syntax() 52453958Sbostic { 525*66565Spendry 52659571Sbostic err(2, "syntax error"); 52753958Sbostic } 52853958Sbostic 52953958Sbostic static void 53053958Sbostic overflow() 53153958Sbostic { 532*66565Spendry 53359571Sbostic err(2, "expression is too complex"); 53453958Sbostic } 535