153958Sbostic /*- 253958Sbostic * 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 1253958Sbostic char copyright[] = 1353958Sbostic "@(#) Copyright (c) 1992 The Regents of the University of California.\n\ 1453852Selan All rights reserved.\n"; 1553958Sbostic #endif /* not lint */ 1653835Selan 1753852Selan #ifndef lint 18*59571Sbostic static char sccsid[] = "@(#)test.c 5.5 (Berkeley) 04/30/93"; 1953958Sbostic #endif /* not lint */ 2053835Selan 2153835Selan #include <sys/types.h> 2253835Selan #include <sys/stat.h> 23*59571Sbostic 24*59571Sbostic #include <err.h> 2553958Sbostic #include <errno.h> 26*59571Sbostic #include <limits.h> 27*59571Sbostic #include <stdio.h> 2853958Sbostic #include <stdlib.h> 2953918Selan #include <string.h> 3053958Sbostic 3153835Selan #include "operators.h" 3253835Selan 3353958Sbostic #define STACKSIZE 12 3453958Sbostic #define NESTINCR 16 3553835Selan 3653835Selan /* data types */ 3753958Sbostic #define STRING 0 3853958Sbostic #define INTEGER 1 3953958Sbostic #define BOOLEAN 2 4053835Selan 4153958Sbostic #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 4253835Selan 4353835Selan /* 4453835Selan * This structure hold a value. The type keyword specifies the type of 4553835Selan * the value, and the union u holds the value. The value of a boolean 4653835Selan * is stored in u.num (1 = TRUE, 0 = FALSE). 4753835Selan */ 4853835Selan struct value { 4953958Sbostic int type; 5053852Selan union { 5153958Sbostic char *string; 5253958Sbostic long num; 5353958Sbostic } u; 5453835Selan }; 5553835Selan 5653835Selan struct operator { 5753958Sbostic short op; /* Which operator. */ 5853958Sbostic short pri; /* Priority of operator. */ 5953835Selan }; 6053835Selan 6153835Selan struct filestat { 6253958Sbostic char *name; /* Name of file. */ 6353958Sbostic int rcode; /* Return code from stat. */ 6453958Sbostic struct stat stat; /* Status info on file. */ 6553835Selan }; 6653835Selan 6753958Sbostic static int expr_is_false __P((struct value *)); 6853958Sbostic static void expr_operator __P((int, struct value *, struct filestat *)); 69*59571Sbostic static void get_int __P((char *, long *)); 7053958Sbostic static int lookup_op __P((char *, char *const *)); 7153958Sbostic static void overflow __P((void)); 7253958Sbostic static int posix_binary_op __P((char **)); 7353958Sbostic static int posix_unary_op __P((char **)); 7453958Sbostic static void syntax __P((void)); 7553835Selan 7653835Selan int 7753852Selan main(argc, argv) 7853958Sbostic int argc; 7953958Sbostic char *argv[]; 8053835Selan { 8153852Selan struct operator opstack[STACKSIZE]; 8253852Selan struct operator *opsp; 8353852Selan struct value valstack[STACKSIZE + 1]; 8453852Selan struct value *valsp; 8553852Selan struct filestat fs; 8653958Sbostic char c, **ap, *opname, *p; 8753958Sbostic int binary, nest, op, pri, ret_val, skipping; 8853835Selan 89*59571Sbostic if ((p = argv[0]) == NULL) 90*59571Sbostic errx(2, "test: argc is zero"); 9153958Sbostic 9257920Storek if (*p != '\0' && p[strlen(p) - 1] == '[') { 9357920Storek if (strcmp(argv[--argc], "]")) 94*59571Sbostic errx(2, "missing ]"); 9557920Storek argv[argc] = NULL; 9653852Selan } 9753852Selan ap = argv + 1; 9853852Selan fs.name = NULL; 9953835Selan 10053958Sbostic /* 10153958Sbostic * Test(1) implements an inherently ambiguous grammer. In order to 10253958Sbostic * assure some degree of consistency, we special case the POSIX 1003.2 10353958Sbostic * requirements to assure correct evaluation for POSIX scripts. The 10453958Sbostic * following special cases comply with POSIX P1003.2/D11.2 Section 10553958Sbostic * 4.62.4. 10653958Sbostic */ 10753958Sbostic switch(argc - 1) { 10853958Sbostic case 0: /* % test */ 10953958Sbostic return (1); 11053852Selan break; 11153958Sbostic case 1: /* % test arg */ 112*59571Sbostic return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; 11353852Selan break; 11453958Sbostic case 2: /* % test op arg */ 11553852Selan opname = argv[1]; 11653852Selan if (IS_BANG(opname)) 11753852Selan return (*argv[2] == '\0') ? 1 : 0; 11853852Selan else { 11953852Selan ret_val = posix_unary_op(&argv[1]); 12053852Selan if (ret_val >= 0) 12153958Sbostic return (ret_val); 12253852Selan } 12353852Selan break; 12453958Sbostic case 3: /* % test arg1 op arg2 */ 12553852Selan if (IS_BANG(argv[1])) { 12653852Selan ret_val = posix_unary_op(&argv[1]); 12753852Selan if (ret_val >= 0) 12853958Sbostic return (!ret_val); 12953852Selan } else { 13053852Selan ret_val = posix_binary_op(&argv[1]); 13153852Selan if (ret_val >= 0) 13253958Sbostic return (ret_val); 13353852Selan } 13453852Selan break; 13553958Sbostic case 4: /* % test ! arg1 op arg2 */ 13653852Selan if (IS_BANG(argv[1])) { 13753852Selan ret_val = posix_binary_op(&argv[2]); 13853852Selan if (ret_val >= 0) 13953958Sbostic return (!ret_val); 14053852Selan } 14153852Selan break; 14253852Selan default: 14353852Selan break; 14453852Selan } 14553835Selan 14653958Sbostic /* 14753958Sbostic * We use operator precedence parsing, evaluating the expression as 14853852Selan * we parse it. Parentheses are handled by bumping up the priority 14953852Selan * of operators using the variable "nest." We use the variable 15053852Selan * "skipping" to turn off evaluation temporarily for the short 15153852Selan * circuit boolean operators. (It is important do the short circuit 15253852Selan * evaluation because under NFS a stat operation can take infinitely 15353958Sbostic * long.) 15453958Sbostic */ 15553852Selan opsp = opstack + STACKSIZE; 15653852Selan valsp = valstack; 15753958Sbostic nest = skipping = 0; 15853852Selan if (*ap == NULL) { 15953852Selan valstack[0].type = BOOLEAN; 16053852Selan valstack[0].u.num = 0; 16153852Selan goto done; 16253852Selan } 16353852Selan for (;;) { 16453852Selan opname = *ap++; 16553852Selan if (opname == NULL) 16653958Sbostic syntax(); 16753852Selan if (opname[0] == '(' && opname[1] == '\0') { 16853852Selan nest += NESTINCR; 16953852Selan continue; 17053852Selan } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 17153852Selan if (opsp == &opstack[0]) 17253958Sbostic overflow(); 17353852Selan --opsp; 17453852Selan opsp->op = op; 17553852Selan opsp->pri = op_priority[op] + nest; 17653852Selan continue; 17753852Selan } else { 17853852Selan valsp->type = STRING; 17953852Selan valsp->u.string = opname; 18053852Selan valsp++; 18153852Selan } 18253852Selan for (;;) { 18353852Selan opname = *ap++; 18453852Selan if (opname == NULL) { 18553852Selan if (nest != 0) 18653958Sbostic syntax(); 18753852Selan pri = 0; 18853852Selan break; 18953852Selan } 19053852Selan if (opname[0] != ')' || opname[1] != '\0') { 19153852Selan if ((op = lookup_op(opname, binary_op)) < 0) 19253958Sbostic syntax(); 19353852Selan op += FIRST_BINARY_OP; 19453852Selan pri = op_priority[op] + nest; 19553852Selan break; 19653852Selan } 19753852Selan if ((nest -= NESTINCR) < 0) 19853958Sbostic syntax(); 19953852Selan } 20053852Selan while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 20153852Selan binary = opsp->op; 20253852Selan for (;;) { 20353852Selan valsp--; 20453852Selan c = op_argflag[opsp->op]; 20553852Selan if (c == OP_INT) { 206*59571Sbostic if (valsp->type == STRING) 207*59571Sbostic get_int(valsp->u.string, 208*59571Sbostic &valsp->u.num); 20953852Selan valsp->type = INTEGER; 21053852Selan } else if (c >= OP_STRING) { 21153852Selan /* OP_STRING or OP_FILE */ 21253852Selan if (valsp->type == INTEGER) { 21353958Sbostic if ((p = malloc(32)) == NULL) 214*59571Sbostic err(2, NULL); 21553835Selan #ifdef SHELL 21653852Selan fmtstr(p, 32, "%d", 21753852Selan valsp->u.num); 21853835Selan #else 21953958Sbostic (void)sprintf(p, 22053958Sbostic "%d", valsp->u.num); 22153835Selan #endif 22253852Selan valsp->u.string = p; 22353852Selan } else if (valsp->type == BOOLEAN) { 22453852Selan if (valsp->u.num) 22553852Selan valsp->u.string = 22653852Selan "true"; 22753852Selan else 22853852Selan valsp->u.string = ""; 22953852Selan } 23053852Selan valsp->type = STRING; 23153958Sbostic if (c == OP_FILE && (fs.name == NULL || 23253958Sbostic strcmp(fs.name, valsp->u.string))) { 23353852Selan fs.name = valsp->u.string; 23453852Selan fs.rcode = 23553852Selan stat(valsp->u.string, 23653852Selan &fs.stat); 23753852Selan } 23853852Selan } 23953852Selan if (binary < FIRST_BINARY_OP) 24053852Selan break; 24153852Selan binary = 0; 24253835Selan } 24353852Selan if (!skipping) 24453852Selan expr_operator(opsp->op, valsp, &fs); 24553852Selan else if (opsp->op == AND1 || opsp->op == OR1) 24653852Selan skipping--; 24753958Sbostic valsp++; /* push value */ 24853958Sbostic opsp++; /* pop operator */ 24953852Selan } 25053852Selan if (opname == NULL) 25153852Selan break; 25253852Selan if (opsp == &opstack[0]) 25353958Sbostic overflow(); 25453852Selan if (op == AND1 || op == AND2) { 25553852Selan op = AND1; 25653852Selan if (skipping || expr_is_false(valsp - 1)) 25753852Selan skipping++; 25853852Selan } 25953852Selan if (op == OR1 || op == OR2) { 26053852Selan op = OR1; 26153852Selan if (skipping || !expr_is_false(valsp - 1)) 26253852Selan skipping++; 26353852Selan } 26453852Selan opsp--; 26553852Selan opsp->op = op; 26653852Selan opsp->pri = pri; 26753852Selan } 26853958Sbostic done: return (expr_is_false(&valstack[0])); 26953835Selan } 27053835Selan 27153835Selan static int 27253835Selan expr_is_false(val) 27353852Selan struct value *val; 27453835Selan { 27553852Selan if (val->type == STRING) { 27653852Selan if (val->u.string[0] == '\0') 27753958Sbostic return (1); 27853852Selan } else { /* INTEGER or BOOLEAN */ 27953852Selan if (val->u.num == 0) 28053958Sbostic return (1); 28153852Selan } 28253958Sbostic return (0); 28353835Selan } 28453835Selan 28553835Selan 28653835Selan /* 28753835Selan * Execute an operator. Op is the operator. Sp is the stack pointer; 28853835Selan * sp[0] refers to the first operand, sp[1] refers to the second operand 28953835Selan * (if any), and the result is placed in sp[0]. The operands are converted 29053835Selan * to the type expected by the operator before expr_operator is called. 29153835Selan * Fs is a pointer to a structure which holds the value of the last call 29253835Selan * to stat, to avoid repeated stat calls on the same file. 29353835Selan */ 29453835Selan static void 29553835Selan expr_operator(op, sp, fs) 29653958Sbostic int op; 29753835Selan struct value *sp; 29853835Selan struct filestat *fs; 29953835Selan { 30053958Sbostic int i; 30153835Selan 30253835Selan switch (op) { 30353835Selan case NOT: 30453852Selan sp->u.num = expr_is_false(sp); 30553852Selan sp->type = BOOLEAN; 30653852Selan break; 30753852Selan case ISEXIST: 30853852Selan if (fs == NULL || fs->rcode == -1) 30953852Selan goto false; 31053852Selan else 31153852Selan goto true; 31253852Selan case ISREAD: 31353958Sbostic i = S_IROTH; 31453852Selan goto permission; 31553852Selan case ISWRITE: 31653958Sbostic i = S_IWOTH; 31753852Selan goto permission; 31853852Selan case ISEXEC: 31953958Sbostic i = S_IXOTH; 32053958Sbostic permission: if (fs->stat.st_uid == geteuid()) 32153852Selan i <<= 6; 32253852Selan else if (fs->stat.st_gid == getegid()) 32353852Selan i <<= 3; 32453852Selan goto filebit; /* true if (stat.st_mode & i) != 0 */ 32553852Selan case ISFILE: 32653852Selan i = S_IFREG; 32753852Selan goto filetype; 32853852Selan case ISDIR: 32953852Selan i = S_IFDIR; 33053852Selan goto filetype; 33153852Selan case ISCHAR: 33253852Selan i = S_IFCHR; 33353852Selan goto filetype; 33453852Selan case ISBLOCK: 33553852Selan i = S_IFBLK; 33653852Selan goto filetype; 33753852Selan case ISFIFO: 33853852Selan i = S_IFIFO; 33953852Selan goto filetype; 34053958Sbostic filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 34153958Sbostic true: sp->u.num = 1; 34253958Sbostic else 34353958Sbostic false: sp->u.num = 0; 34453852Selan sp->type = BOOLEAN; 34553852Selan break; 34653852Selan case ISSETUID: 34753852Selan i = S_ISUID; 34853852Selan goto filebit; 34953852Selan case ISSETGID: 35053852Selan i = S_ISGID; 35153852Selan goto filebit; 35253852Selan case ISSTICKY: 35353852Selan i = S_ISVTX; 35453958Sbostic filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 35553852Selan goto true; 35653852Selan goto false; 35753852Selan case ISSIZE: 35853852Selan sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 35953852Selan sp->type = INTEGER; 36053852Selan break; 36153852Selan case ISTTY: 36253852Selan sp->u.num = isatty(sp->u.num); 36353852Selan sp->type = BOOLEAN; 36453852Selan break; 36553852Selan case NULSTR: 36653852Selan if (sp->u.string[0] == '\0') 36753852Selan goto true; 36853852Selan goto false; 36953852Selan case STRLEN: 37053852Selan sp->u.num = strlen(sp->u.string); 37153852Selan sp->type = INTEGER; 37253852Selan break; 37353852Selan case OR1: 37453852Selan case AND1: 37553958Sbostic /* 37653958Sbostic * These operators are mostly handled by the parser. If we 37753852Selan * get here it means that both operands were evaluated, so 37853958Sbostic * the value is the value of the second operand. 37953958Sbostic */ 38053852Selan *sp = *(sp + 1); 38153852Selan break; 38253852Selan case STREQ: 38353852Selan case STRNE: 38453852Selan i = 0; 38553958Sbostic if (!strcmp(sp->u.string, (sp + 1)->u.string)) 38653852Selan i++; 38753852Selan if (op == STRNE) 38853852Selan i = 1 - i; 38953852Selan sp->u.num = i; 39053852Selan sp->type = BOOLEAN; 39153852Selan break; 39253852Selan case EQ: 39353852Selan if (sp->u.num == (sp + 1)->u.num) 39453852Selan goto true; 39553852Selan goto false; 39653852Selan case NE: 39753852Selan if (sp->u.num != (sp + 1)->u.num) 39853852Selan goto true; 39953852Selan goto false; 40053852Selan case GT: 40153852Selan if (sp->u.num > (sp + 1)->u.num) 40253852Selan goto true; 40353852Selan goto false; 40453852Selan case LT: 40553852Selan if (sp->u.num < (sp + 1)->u.num) 40653852Selan goto true; 40753852Selan goto false; 40853852Selan case LE: 40953852Selan if (sp->u.num <= (sp + 1)->u.num) 41053852Selan goto true; 41153852Selan goto false; 41253852Selan case GE: 41353852Selan if (sp->u.num >= (sp + 1)->u.num) 41453852Selan goto true; 41553852Selan goto false; 41653835Selan 41753852Selan } 41853835Selan } 41953835Selan 42053835Selan static int 42153835Selan lookup_op(name, table) 42253852Selan char *name; 42353852Selan char *const * table; 42453835Selan { 42553852Selan register char *const * tp; 42653852Selan register char const *p; 42753958Sbostic char c; 42853835Selan 42953958Sbostic c = name[1]; 43053958Sbostic for (tp = table; (p = *tp) != NULL; tp++) 43153958Sbostic if (p[1] == c && !strcmp(p, name)) 43253958Sbostic return (tp - table); 43353958Sbostic return (-1); 43453835Selan } 43553835Selan 43653835Selan static int 43753835Selan posix_unary_op(argv) 43853958Sbostic char **argv; 43953835Selan { 44053835Selan struct filestat fs; 44153835Selan struct value valp; 44253958Sbostic int op, c; 44353958Sbostic char *opname; 44453835Selan 44553835Selan opname = *argv; 44653852Selan if ((op = lookup_op(opname, unary_op)) < 0) 44753958Sbostic return (-1); 44853852Selan c = op_argflag[op]; 44953852Selan opname = argv[1]; 45053852Selan valp.u.string = opname; 45153852Selan if (c == OP_FILE) { 45253835Selan fs.name = opname; 45353835Selan fs.rcode = stat(opname, &fs.stat); 45453835Selan } else if (c != OP_STRING) 45553958Sbostic return (-1); 45653835Selan 45753835Selan expr_operator(op, &valp, &fs); 45853835Selan return (valp.u.num == 0); 45953835Selan } 46053835Selan 46153835Selan static int 46253835Selan posix_binary_op(argv) 46353852Selan char **argv; 46453835Selan { 46553835Selan struct value v[2]; 46653958Sbostic int op, c; 46753958Sbostic char *opname; 46853852Selan 46953835Selan opname = argv[1]; 47053835Selan if ((op = lookup_op(opname, binary_op)) < 0) 47153958Sbostic return (-1); 47253835Selan op += FIRST_BINARY_OP; 47353835Selan c = op_argflag[op]; 47453835Selan 475*59571Sbostic if (c == OP_INT) { 476*59571Sbostic get_int(argv[0], &v[0].u.num); 477*59571Sbostic get_int(argv[2], &v[1].u.num); 47853918Selan } else { 47953918Selan v[0].u.string = argv[0]; 48053918Selan v[1].u.string = argv[2]; 48153835Selan } 48253835Selan expr_operator(op, v, NULL); 48353835Selan return (v[0].u.num == 0); 48453835Selan } 48553835Selan 48653835Selan /* 48753835Selan * Integer type checking. 48853835Selan */ 489*59571Sbostic static void 490*59571Sbostic get_int(v, lp) 49153958Sbostic char *v; 492*59571Sbostic long *lp; 49353835Selan { 494*59571Sbostic long val; 495*59571Sbostic char *ep; 49653835Selan 497*59571Sbostic for (; *v && isspace(*v); ++v); 498*59571Sbostic if (isdigit(*v)) { 499*59571Sbostic errno = 0; 500*59571Sbostic val = strtol(v, &ep, 10); 501*59571Sbostic if (*ep != '\0') 502*59571Sbostic errx(2, "%s: trailing non-numeric characters", v); 503*59571Sbostic if (errno == ERANGE) { 504*59571Sbostic if (val == LONG_MIN) 505*59571Sbostic errx(2, "%s: underflow", v); 506*59571Sbostic if (val == LONG_MAX) 507*59571Sbostic errx(2, "%s: overflow", v); 508*59571Sbostic } 509*59571Sbostic *lp = val; 510*59571Sbostic return; 511*59571Sbostic } 512*59571Sbostic errx(2, "%s: expected integer", v); 51353835Selan } 51453958Sbostic 51553958Sbostic static void 51653958Sbostic syntax() 51753958Sbostic { 518*59571Sbostic err(2, "syntax error"); 51953958Sbostic } 52053958Sbostic 52153958Sbostic static void 52253958Sbostic overflow() 52353958Sbostic { 524*59571Sbostic err(2, "expression is too complex"); 52553958Sbostic } 526