153958Sbostic /*- 2*60726Sbostic * Copyright (c) 1992, 1993 3*60726Sbostic * 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 12*60726Sbostic static char copyright[] = 13*60726Sbostic "@(#) Copyright (c) 1992, 1993\n\ 14*60726Sbostic The Regents of the University of California. All rights reserved.\n"; 1553958Sbostic #endif /* not lint */ 1653835Selan 1753852Selan #ifndef lint 18*60726Sbostic static char sccsid[] = "@(#)test.c 8.1 (Berkeley) 05/31/93"; 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 *)); 7253958Sbostic static int lookup_op __P((char *, 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 { 27753852Selan if (val->type == STRING) { 27853852Selan if (val->u.string[0] == '\0') 27953958Sbostic return (1); 28053852Selan } else { /* INTEGER or BOOLEAN */ 28153852Selan if (val->u.num == 0) 28253958Sbostic return (1); 28353852Selan } 28453958Sbostic return (0); 28553835Selan } 28653835Selan 28753835Selan 28853835Selan /* 28953835Selan * Execute an operator. Op is the operator. Sp is the stack pointer; 29053835Selan * sp[0] refers to the first operand, sp[1] refers to the second operand 29153835Selan * (if any), and the result is placed in sp[0]. The operands are converted 29253835Selan * to the type expected by the operator before expr_operator is called. 29353835Selan * Fs is a pointer to a structure which holds the value of the last call 29453835Selan * to stat, to avoid repeated stat calls on the same file. 29553835Selan */ 29653835Selan static void 29753835Selan expr_operator(op, sp, fs) 29853958Sbostic int op; 29953835Selan struct value *sp; 30053835Selan struct filestat *fs; 30153835Selan { 30253958Sbostic int i; 30353835Selan 30453835Selan switch (op) { 30553835Selan case NOT: 30653852Selan sp->u.num = expr_is_false(sp); 30753852Selan sp->type = BOOLEAN; 30853852Selan break; 30953852Selan case ISEXIST: 31053852Selan if (fs == NULL || fs->rcode == -1) 31153852Selan goto false; 31253852Selan else 31353852Selan goto true; 31453852Selan case ISREAD: 31553958Sbostic i = S_IROTH; 31653852Selan goto permission; 31753852Selan case ISWRITE: 31853958Sbostic i = S_IWOTH; 31953852Selan goto permission; 32053852Selan case ISEXEC: 32153958Sbostic i = S_IXOTH; 32253958Sbostic permission: if (fs->stat.st_uid == geteuid()) 32353852Selan i <<= 6; 32453852Selan else if (fs->stat.st_gid == getegid()) 32553852Selan i <<= 3; 32653852Selan goto filebit; /* true if (stat.st_mode & i) != 0 */ 32753852Selan case ISFILE: 32853852Selan i = S_IFREG; 32953852Selan goto filetype; 33053852Selan case ISDIR: 33153852Selan i = S_IFDIR; 33253852Selan goto filetype; 33353852Selan case ISCHAR: 33453852Selan i = S_IFCHR; 33553852Selan goto filetype; 33653852Selan case ISBLOCK: 33753852Selan i = S_IFBLK; 33853852Selan goto filetype; 33960597Sbostic case ISSYMLINK: 34060597Sbostic i = S_IFLNK; 34160597Sbostic (void)lstat(sp->u.string, &fs->stat); 34260597Sbostic goto filetype; 34353852Selan case ISFIFO: 34453852Selan i = S_IFIFO; 34553852Selan goto filetype; 34653958Sbostic filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 34753958Sbostic true: sp->u.num = 1; 34853958Sbostic else 34953958Sbostic false: sp->u.num = 0; 35053852Selan sp->type = BOOLEAN; 35153852Selan break; 35253852Selan case ISSETUID: 35353852Selan i = S_ISUID; 35453852Selan goto filebit; 35553852Selan case ISSETGID: 35653852Selan i = S_ISGID; 35753852Selan goto filebit; 35853852Selan case ISSTICKY: 35953852Selan i = S_ISVTX; 36053958Sbostic filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 36153852Selan goto true; 36253852Selan goto false; 36353852Selan case ISSIZE: 36453852Selan sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 36553852Selan sp->type = INTEGER; 36653852Selan break; 36753852Selan case ISTTY: 36853852Selan sp->u.num = isatty(sp->u.num); 36953852Selan sp->type = BOOLEAN; 37053852Selan break; 37153852Selan case NULSTR: 37253852Selan if (sp->u.string[0] == '\0') 37353852Selan goto true; 37453852Selan goto false; 37553852Selan case STRLEN: 37653852Selan sp->u.num = strlen(sp->u.string); 37753852Selan sp->type = INTEGER; 37853852Selan break; 37953852Selan case OR1: 38053852Selan case AND1: 38153958Sbostic /* 38253958Sbostic * These operators are mostly handled by the parser. If we 38353852Selan * get here it means that both operands were evaluated, so 38453958Sbostic * the value is the value of the second operand. 38553958Sbostic */ 38653852Selan *sp = *(sp + 1); 38753852Selan break; 38853852Selan case STREQ: 38953852Selan case STRNE: 39053852Selan i = 0; 39153958Sbostic if (!strcmp(sp->u.string, (sp + 1)->u.string)) 39253852Selan i++; 39353852Selan if (op == STRNE) 39453852Selan i = 1 - i; 39553852Selan sp->u.num = i; 39653852Selan sp->type = BOOLEAN; 39753852Selan break; 39853852Selan case EQ: 39953852Selan if (sp->u.num == (sp + 1)->u.num) 40053852Selan goto true; 40153852Selan goto false; 40253852Selan case NE: 40353852Selan if (sp->u.num != (sp + 1)->u.num) 40453852Selan goto true; 40553852Selan goto false; 40653852Selan case GT: 40753852Selan if (sp->u.num > (sp + 1)->u.num) 40853852Selan goto true; 40953852Selan goto false; 41053852Selan case LT: 41153852Selan if (sp->u.num < (sp + 1)->u.num) 41253852Selan goto true; 41353852Selan goto false; 41453852Selan case LE: 41553852Selan if (sp->u.num <= (sp + 1)->u.num) 41653852Selan goto true; 41753852Selan goto false; 41853852Selan case GE: 41953852Selan if (sp->u.num >= (sp + 1)->u.num) 42053852Selan goto true; 42153852Selan goto false; 42253835Selan 42353852Selan } 42453835Selan } 42553835Selan 42653835Selan static int 42753835Selan lookup_op(name, table) 42853852Selan char *name; 42953852Selan char *const * table; 43053835Selan { 43153852Selan register char *const * tp; 43253852Selan register char const *p; 43353958Sbostic char c; 43453835Selan 43553958Sbostic c = name[1]; 43653958Sbostic for (tp = table; (p = *tp) != NULL; tp++) 43753958Sbostic if (p[1] == c && !strcmp(p, name)) 43853958Sbostic return (tp - table); 43953958Sbostic return (-1); 44053835Selan } 44153835Selan 44253835Selan static int 44353835Selan posix_unary_op(argv) 44453958Sbostic char **argv; 44553835Selan { 44653835Selan struct filestat fs; 44753835Selan struct value valp; 44853958Sbostic int op, c; 44953958Sbostic char *opname; 45053835Selan 45153835Selan opname = *argv; 45253852Selan if ((op = lookup_op(opname, unary_op)) < 0) 45353958Sbostic return (-1); 45453852Selan c = op_argflag[op]; 45553852Selan opname = argv[1]; 45653852Selan valp.u.string = opname; 45753852Selan if (c == OP_FILE) { 45853835Selan fs.name = opname; 45953835Selan fs.rcode = stat(opname, &fs.stat); 46053835Selan } else if (c != OP_STRING) 46153958Sbostic return (-1); 46253835Selan 46353835Selan expr_operator(op, &valp, &fs); 46453835Selan return (valp.u.num == 0); 46553835Selan } 46653835Selan 46753835Selan static int 46853835Selan posix_binary_op(argv) 46953852Selan char **argv; 47053835Selan { 47153835Selan struct value v[2]; 47253958Sbostic int op, c; 47353958Sbostic char *opname; 47453852Selan 47553835Selan opname = argv[1]; 47653835Selan if ((op = lookup_op(opname, binary_op)) < 0) 47753958Sbostic return (-1); 47853835Selan op += FIRST_BINARY_OP; 47953835Selan c = op_argflag[op]; 48053835Selan 48159571Sbostic if (c == OP_INT) { 48259571Sbostic get_int(argv[0], &v[0].u.num); 48359571Sbostic get_int(argv[2], &v[1].u.num); 48453918Selan } else { 48553918Selan v[0].u.string = argv[0]; 48653918Selan v[1].u.string = argv[2]; 48753835Selan } 48853835Selan expr_operator(op, v, NULL); 48953835Selan return (v[0].u.num == 0); 49053835Selan } 49153835Selan 49253835Selan /* 49353835Selan * Integer type checking. 49453835Selan */ 49559571Sbostic static void 49659571Sbostic get_int(v, lp) 49753958Sbostic char *v; 49859571Sbostic long *lp; 49953835Selan { 50059571Sbostic long val; 50159571Sbostic char *ep; 50253835Selan 50359571Sbostic for (; *v && isspace(*v); ++v); 50459571Sbostic if (isdigit(*v)) { 50559571Sbostic errno = 0; 50659571Sbostic val = strtol(v, &ep, 10); 50759571Sbostic if (*ep != '\0') 50859571Sbostic errx(2, "%s: trailing non-numeric characters", v); 50959571Sbostic if (errno == ERANGE) { 51059571Sbostic if (val == LONG_MIN) 51159571Sbostic errx(2, "%s: underflow", v); 51259571Sbostic if (val == LONG_MAX) 51359571Sbostic errx(2, "%s: overflow", v); 51459571Sbostic } 51559571Sbostic *lp = val; 51659571Sbostic return; 51759571Sbostic } 51859571Sbostic errx(2, "%s: expected integer", v); 51953835Selan } 52053958Sbostic 52153958Sbostic static void 52253958Sbostic syntax() 52353958Sbostic { 52459571Sbostic err(2, "syntax error"); 52553958Sbostic } 52653958Sbostic 52753958Sbostic static void 52853958Sbostic overflow() 52953958Sbostic { 53059571Sbostic err(2, "expression is too complex"); 53153958Sbostic } 532