test.c revision 4167
1271947Sdes/*- 2271947Sdes * Copyright (c) 1992, 1993, 1994 3271947Sdes * The Regents of the University of California. All rights reserved. 4271947Sdes * 5271947Sdes * This code is derived from software contributed to Berkeley by 6271947Sdes * Kenneth Almquist. 7271947Sdes * 8271947Sdes * Redistribution and use in source and binary forms, with or without 9271947Sdes * modification, are permitted provided that the following conditions 10271947Sdes * are met: 11271947Sdes * 1. Redistributions of source code must retain the above copyright 12271947Sdes * notice, this list of conditions and the following disclaimer. 13271947Sdes * 2. Redistributions in binary form must reproduce the above copyright 14271947Sdes * notice, this list of conditions and the following disclaimer in the 15271947Sdes * documentation and/or other materials provided with the distribution. 16271947Sdes * 3. All advertising materials mentioning features or use of this software 17271947Sdes * must display the following acknowledgement: 18271947Sdes * This product includes software developed by the University of 19271947Sdes * California, Berkeley and its contributors. 20271947Sdes * 4. Neither the name of the University nor the names of its contributors 21271947Sdes * may be used to endorse or promote products derived from this software 22255376Sdes * without specific prior written permission. 23255376Sdes * 24255376Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25255376Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26255376Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27255376Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28255376Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29255376Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30255376Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31255376Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32255376Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33255376Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34255376Sdes * SUCH DAMAGE. 35255376Sdes * 36255376Sdes * $Id: test.c,v 1.5 1994/11/05 17:07:14 ache Exp $ 37255376Sdes */ 38255376Sdes 39255376Sdes#ifndef lint 40255376Sdesstatic char copyright[] = 41255376Sdes"@(#) Copyright (c) 1992, 1993, 1994\n\ 42255376Sdes The Regents of the University of California. All rights reserved.\n"; 43255376Sdes#endif /* not lint */ 44255376Sdes 45255376Sdes#ifndef lint 46255376Sdesstatic char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94"; 47255376Sdes#endif /* not lint */ 48255376Sdes 49236109Sdes#include <sys/types.h> 50236109Sdes#include <sys/stat.h> 51236109Sdes 52236109Sdes#include <ctype.h> 53236109Sdes#include <err.h> 54236109Sdes#include <errno.h> 55236109Sdes#include <limits.h> 56236109Sdes#include <stdio.h> 57236109Sdes#include <stdlib.h> 58236109Sdes#include <string.h> 59236109Sdes#include <unistd.h> 60236109Sdes 61236109Sdes#include "operators.h" 62236109Sdes 63236109Sdes#define STACKSIZE 12 64236109Sdes#define NESTINCR 16 65236109Sdes 66236109Sdes/* data types */ 67236109Sdes#define STRING 0 68236109Sdes#define INTEGER 1 69236109Sdes#define BOOLEAN 2 70236109Sdes 71236109Sdes#define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 72236109Sdes 73236109Sdes/* 74236109Sdes * This structure hold a value. The type keyword specifies the type of 75236109Sdes * the value, and the union u holds the value. The value of a boolean 76236109Sdes * is stored in u.num (1 = TRUE, 0 = FALSE). 77236109Sdes */ 78236109Sdesstruct value { 79236109Sdes int type; 80236109Sdes union { 81236109Sdes char *string; 82236109Sdes long num; 83236109Sdes } u; 84236109Sdes}; 85236109Sdes 86236109Sdesstruct operator { 87236109Sdes short op; /* Which operator. */ 88236109Sdes short pri; /* Priority of operator. */ 89236109Sdes}; 90236109Sdes 91236109Sdesstruct filestat { 92236109Sdes char *name; /* Name of file. */ 93236109Sdes int rcode; /* Return code from stat. */ 94236109Sdes struct stat stat; /* Status info on file. */ 95236109Sdes}; 96236109Sdes 97228692Sdesstatic int expr_is_false __P((struct value *)); 98228692Sdesstatic void expr_operator __P((int, struct value *, struct filestat *)); 99228692Sdesstatic void get_int __P((char *, long *)); 100228692Sdesstatic int lookup_op __P((char *, const char *const *)); 101228692Sdesstatic void overflow __P((void)); 102228692Sdesstatic int posix_binary_op __P((char **)); 103228692Sdesstatic int posix_unary_op __P((char **)); 104228692Sdesstatic void syntax __P((void)); 105228692Sdes 106228692Sdesint 107228692Sdesmain(argc, argv) 108228692Sdes int argc; 109228692Sdes char *argv[]; 110228692Sdes{ 111228692Sdes struct operator opstack[STACKSIZE]; 112228692Sdes struct operator *opsp; 113228692Sdes struct value valstack[STACKSIZE + 1]; 114228692Sdes struct value *valsp; 115228692Sdes struct filestat fs; 116228692Sdes char c, **ap, *opname, *p; 117228692Sdes int binary, nest, op, pri, ret_val, skipping; 118228692Sdes 119228692Sdes if ((p = argv[0]) == NULL) 120228692Sdes errx(2, "test: argc is zero"); 121271947Sdes 122228692Sdes if (*p != '\0' && p[strlen(p) - 1] == '[') { 123174832Sdes if (strcmp(argv[--argc], "]")) 124147455Sdes errx(2, "missing ]"); 125174832Sdes argv[argc] = NULL; 126174832Sdes } 127174832Sdes ap = argv + 1; 128174832Sdes fs.name = NULL; 129174832Sdes 130174832Sdes /* 131174832Sdes * Test(1) implements an inherently ambiguous grammer. In order to 132174832Sdes * assure some degree of consistency, we special case the POSIX 1003.2 133174832Sdes * requirements to assure correct evaluation for POSIX scripts. The 134174832Sdes * following special cases comply with POSIX P1003.2/D11.2 Section 135174832Sdes * 4.62.4. 136174832Sdes */ 137174832Sdes switch(argc - 1) { 138174832Sdes case 0: /* % test */ 139174832Sdes return (1); 140174832Sdes break; 141174832Sdes case 1: /* % test arg */ 142174832Sdes return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; 143174832Sdes break; 144174832Sdes case 2: /* % test op arg */ 145174832Sdes opname = argv[1]; 146228692Sdes if (IS_BANG(opname)) 147174832Sdes return (*argv[2] == '\0') ? 0 : 1; 148147455Sdes else { 149147455Sdes ret_val = posix_unary_op(&argv[1]); 150147455Sdes if (ret_val >= 0) 151147455Sdes return (ret_val); 152147455Sdes } 153147455Sdes break; 154147455Sdes case 3: /* % test arg1 op arg2 */ 155147455Sdes if (IS_BANG(argv[1])) { 156147455Sdes ret_val = posix_unary_op(&argv[1]); 157147455Sdes if (ret_val >= 0) 158147455Sdes return (!ret_val); 159141098Sdes } else { 160141098Sdes ret_val = posix_binary_op(&argv[1]); 161141098Sdes if (ret_val >= 0) 162141098Sdes return (ret_val); 163141098Sdes } 164141098Sdes break; 165141098Sdes case 4: /* % test ! arg1 op arg2 */ 166141098Sdes if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) { 167141098Sdes ret_val = posix_binary_op(&argv[2]); 168141098Sdes if (ret_val >= 0) 169141098Sdes return (!ret_val); 170141098Sdes } 171141098Sdes break; 172141098Sdes default: 173141098Sdes break; 174141098Sdes } 175141098Sdes 176141098Sdes /* 177141098Sdes * We use operator precedence parsing, evaluating the expression as 178125647Sdes * we parse it. Parentheses are handled by bumping up the priority 179125647Sdes * of operators using the variable "nest." We use the variable 180125647Sdes * "skipping" to turn off evaluation temporarily for the short 181125647Sdes * circuit boolean operators. (It is important do the short circuit 182125647Sdes * evaluation because under NFS a stat operation can take infinitely 183125647Sdes * long.) 184125647Sdes */ 185125647Sdes opsp = opstack + STACKSIZE; 186125647Sdes valsp = valstack; 187117610Sdes nest = skipping = 0; 188117610Sdes if (*ap == NULL) { 189117610Sdes valstack[0].type = BOOLEAN; 190117610Sdes valstack[0].u.num = 0; 191117610Sdes goto done; 192117610Sdes } 193117610Sdes for (;;) { 194117610Sdes opname = *ap++; 195117610Sdes if (opname == NULL) 196117610Sdes syntax(); 197117610Sdes if (opname[0] == '(' && opname[1] == '\0') { 198117610Sdes nest += NESTINCR; 199117610Sdes continue; 200117610Sdes } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 201117610Sdes if (opsp == &opstack[0]) 202117610Sdes overflow(); 203117610Sdes --opsp; 204117610Sdes opsp->op = op; 205117610Sdes opsp->pri = op_priority[op] + nest; 206117610Sdes continue; 207117610Sdes } else { 208115619Sdes valsp->type = STRING; 209115619Sdes valsp->u.string = opname; 210115619Sdes valsp++; 211115619Sdes } 212115619Sdes for (;;) { 213115619Sdes opname = *ap++; 214115619Sdes if (opname == NULL) { 215115619Sdes if (nest != 0) 216115619Sdes syntax(); 217115619Sdes pri = 0; 218115619Sdes break; 219115619Sdes } 220115619Sdes if (opname[0] != ')' || opname[1] != '\0') { 221115619Sdes if ((op = lookup_op(opname, binary_op)) < 0) 222115619Sdes syntax(); 223115619Sdes op += FIRST_BINARY_OP; 224115619Sdes pri = op_priority[op] + nest; 225115619Sdes break; 226115619Sdes } 227114536Sdes if ((nest -= NESTINCR) < 0) 228114536Sdes syntax(); 229114536Sdes } 230114536Sdes while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 231114536Sdes binary = opsp->op; 232114536Sdes for (;;) { 233114536Sdes valsp--; 234114536Sdes c = op_argflag[opsp->op]; 235114536Sdes if (c == OP_INT) { 236114536Sdes if (valsp->type == STRING) 237114536Sdes get_int(valsp->u.string, 238114536Sdes &valsp->u.num); 239114536Sdes valsp->type = INTEGER; 240114536Sdes } else if (c >= OP_STRING) { 241114536Sdes /* OP_STRING or OP_FILE */ 242114536Sdes if (valsp->type == INTEGER) { 243114536Sdes if ((p = malloc(32)) == NULL) 244108794Sdes err(2, NULL); 245108794Sdes#ifdef SHELL 246108794Sdes fmtstr(p, 32, "%d", 247108794Sdes valsp->u.num); 248108794Sdes#else 249108794Sdes (void)sprintf(p, 250108794Sdes "%d", valsp->u.num); 251108794Sdes#endif 252107937Sdes valsp->u.string = p; 253107937Sdes } else if (valsp->type == BOOLEAN) { 254107937Sdes if (valsp->u.num) 255107937Sdes valsp->u.string = 256107937Sdes "true"; 257107937Sdes else 258107937Sdes valsp->u.string = ""; 259107937Sdes } 260107937Sdes valsp->type = STRING; 261107937Sdes if (c == OP_FILE && (fs.name == NULL || 262107937Sdes strcmp(fs.name, valsp->u.string))) { 263107937Sdes fs.name = valsp->u.string; 264107937Sdes fs.rcode = 265107937Sdes stat(valsp->u.string, 266107937Sdes &fs.stat); 26791094Sdes } 26899158Sdes } 26999158Sdes if (binary < FIRST_BINARY_OP) 27099158Sdes break; 27199158Sdes binary = 0; 27299158Sdes } 27399158Sdes if (!skipping) 27499158Sdes expr_operator(opsp->op, valsp, &fs); 275107937Sdes else if (opsp->op == AND1 || opsp->op == OR1) 27699158Sdes skipping--; 27799158Sdes valsp++; /* push value */ 27899158Sdes opsp++; /* pop operator */ 27999158Sdes } 28099158Sdes if (opname == NULL) 28199158Sdes break; 28299158Sdes if (opsp == &opstack[0]) 28399158Sdes overflow(); 28499158Sdes if (op == AND1 || op == AND2) { 28599158Sdes op = AND1; 28699158Sdes if (skipping || expr_is_false(valsp - 1)) 28799158Sdes skipping++; 28899158Sdes } 28997241Sdes if (op == OR1 || op == OR2) { 29097241Sdes op = OR1; 29197241Sdes if (skipping || !expr_is_false(valsp - 1)) 29297241Sdes skipping++; 29397241Sdes } 29497241Sdes opsp--; 29597241Sdes opsp->op = op; 29697241Sdes opsp->pri = pri; 29797241Sdes } 29897241Sdesdone: return (expr_is_false(&valstack[0])); 29995908Sdes} 30095908Sdes 30195908Sdesstatic int 30295908Sdesexpr_is_false(val) 30395908Sdes struct value *val; 30495908Sdes{ 30595908Sdes 30695908Sdes if (val->type == STRING) { 30795908Sdes if (val->u.string[0] == '\0') 30895908Sdes return (1); 30995908Sdes } else { /* INTEGER or BOOLEAN */ 31095908Sdes if (val->u.num == 0) 31195908Sdes return (1); 31295908Sdes } 31395908Sdes return (0); 31495908Sdes} 31595908Sdes 31695908Sdes 31795908Sdes/* 31895908Sdes * Execute an operator. Op is the operator. Sp is the stack pointer; 31995908Sdes * sp[0] refers to the first operand, sp[1] refers to the second operand 32094670Sdes * (if any), and the result is placed in sp[0]. The operands are converted 32194670Sdes * to the type expected by the operator before expr_operator is called. 32295908Sdes * Fs is a pointer to a structure which holds the value of the last call 32395908Sdes * to stat, to avoid repeated stat calls on the same file. 32495908Sdes */ 32594670Sdesstatic void 32694670Sdesexpr_operator(op, sp, fs) 32794670Sdes int op; 32894670Sdes struct value *sp; 32994670Sdes struct filestat *fs; 33094670Sdes{ 33195908Sdes int i; 33294670Sdes 33394670Sdes switch (op) { 33494670Sdes case NOT: 33594670Sdes sp->u.num = expr_is_false(sp); 33694670Sdes sp->type = BOOLEAN; 33794209Sdes break; 33894209Sdes case ISEXIST: 33994209Sdesexist: 34094209Sdes if (fs == NULL || fs->rcode == -1) 34194209Sdes goto false; 34294209Sdes else 34394209Sdes goto true; 34494209Sdes case ISREAD: 34594209Sdes if (geteuid() == 0) 34694209Sdes goto exist; 34794209Sdes i = S_IROTH; 34894209Sdes goto permission; 34994209Sdes case ISWRITE: 35094209Sdes if (geteuid() == 0) 35194209Sdes goto exist; 35294209Sdes i = S_IWOTH; 35394209Sdes goto permission; 35494209Sdes case ISEXEC: 35594209Sdes if (geteuid() != 0) { 35694209Sdes i = S_IXOTH; 35794209Sdespermission: if (fs->stat.st_uid == geteuid()) 35894209Sdes i <<= 6; 35994209Sdes else if (fs->stat.st_gid == getegid()) 36094209Sdes i <<= 3; 36194209Sdes } else 36294209Sdes i = S_IXOTH|S_IXGRP|S_IXUSR; 36394209Sdes goto filebit; /* true if (stat.st_mode & i) != 0 */ 36494209Sdes case ISFILE: 36594209Sdes i = S_IFREG; 36694209Sdes goto filetype; 36794209Sdes case ISDIR: 368236109Sdes i = S_IFDIR; 36991684Sdes goto filetype; 37092289Sdes case ISCHAR: 37192289Sdes i = S_IFCHR; 37292289Sdes goto filetype; 37392289Sdes case ISBLOCK: 37492289Sdes i = S_IFBLK; 37592289Sdes goto filetype; 37692289Sdes case ISSYMLINK: 37792289Sdes i = S_IFLNK; 37892289Sdes (void)lstat(sp->u.string, &fs->stat); 37992289Sdes goto filetype; 38092289Sdes case ISFIFO: 38192289Sdes i = S_IFIFO; 38292289Sdes goto filetype; 38392289Sdesfiletype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 38492289Sdestrue: sp->u.num = 1; 38594209Sdes else 38692289Sdesfalse: sp->u.num = 0; 38791684Sdes sp->type = BOOLEAN; 38891684Sdes break; 38991684Sdes case ISSETUID: 39091684Sdes i = S_ISUID; 39191684Sdes goto filebit; 39291684Sdes case ISSETGID: 39391684Sdes i = S_ISGID; 39491684Sdes goto filebit; 39591684Sdes case ISSTICKY: 39691684Sdes i = S_ISVTX; 39791684Sdesfilebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 39891684Sdes goto true; 39991684Sdes goto false; 40091684Sdes case ISSIZE: 40191684Sdes sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 40291684Sdes sp->type = INTEGER; 40391684Sdes break; 40491684Sdes case ISTTY: 40591684Sdes sp->u.num = isatty(sp->u.num); 40691684Sdes sp->type = BOOLEAN; 407236109Sdes break; 40891100Sdes case NULSTR: 40991100Sdes if (sp->u.string[0] == '\0') 41091100Sdes goto true; 41191100Sdes goto false; 41291100Sdes case STRLEN: 41391100Sdes sp->u.num = strlen(sp->u.string); 41491100Sdes sp->type = INTEGER; 41591100Sdes break; 41691100Sdes case OR1: 41791100Sdes case AND1: 41891100Sdes /* 41991100Sdes * These operators are mostly handled by the parser. If we 42091100Sdes * get here it means that both operands were evaluated, so 42191100Sdes * the value is the value of the second operand. 42291100Sdes */ 42391100Sdes *sp = *(sp + 1); 42491100Sdes break; 42591100Sdes case STREQ: 42691100Sdes case STRNE: 42791100Sdes i = 0; 42891100Sdes if (!strcmp(sp->u.string, (sp + 1)->u.string)) 42991100Sdes i++; 43091100Sdes if (op == STRNE) 43191100Sdes i = 1 - i; 43291100Sdes sp->u.num = i; 43391100Sdes sp->type = BOOLEAN; 43491100Sdes break; 43591100Sdes case EQ: 43691100Sdes if (sp->u.num == (sp + 1)->u.num) 437236109Sdes goto true; 43891097Sdes goto false; 43991097Sdes case NE: 44091097Sdes if (sp->u.num != (sp + 1)->u.num) 44191097Sdes goto true; 44291097Sdes goto false; 44391097Sdes case GT: 44491097Sdes if (sp->u.num > (sp + 1)->u.num) 44591097Sdes goto true; 44691097Sdes goto false; 44791097Sdes case LT: 448236109Sdes if (sp->u.num < (sp + 1)->u.num) 44991094Sdes goto true; 45091094Sdes goto false; 451 case LE: 452 if (sp->u.num <= (sp + 1)->u.num) 453 goto true; 454 goto false; 455 case GE: 456 if (sp->u.num >= (sp + 1)->u.num) 457 goto true; 458 goto false; 459 460 } 461} 462 463static int 464lookup_op(name, table) 465 char *name; 466 const char *const * table; 467{ 468 const char *const * tp; 469 const char *p; 470 char c; 471 472 c = name[1]; 473 for (tp = table; (p = *tp) != NULL; tp++) 474 if (p[1] == c && !strcmp(p, name)) 475 return (tp - table); 476 return (-1); 477} 478 479static int 480posix_unary_op(argv) 481 char **argv; 482{ 483 struct filestat fs; 484 struct value valp; 485 int op, c; 486 char *opname; 487 488 opname = *argv; 489 if ((op = lookup_op(opname, unary_op)) < 0) 490 return (-1); 491 c = op_argflag[op]; 492 opname = argv[1]; 493 valp.u.string = opname; 494 if (c == OP_FILE) { 495 fs.name = opname; 496 fs.rcode = stat(opname, &fs.stat); 497 } else if (c != OP_STRING) 498 return (-1); 499 500 expr_operator(op, &valp, &fs); 501 return (valp.u.num == 0); 502} 503 504static int 505posix_binary_op(argv) 506 char **argv; 507{ 508 struct value v[2]; 509 int op, c; 510 char *opname; 511 512 opname = argv[1]; 513 if ((op = lookup_op(opname, binary_op)) < 0) 514 return (-1); 515 op += FIRST_BINARY_OP; 516 c = op_argflag[op]; 517 518 if (c == OP_INT) { 519 get_int(argv[0], &v[0].u.num); 520 get_int(argv[2], &v[1].u.num); 521 } else { 522 v[0].u.string = argv[0]; 523 v[1].u.string = argv[2]; 524 } 525 expr_operator(op, v, NULL); 526 return (v[0].u.num == 0); 527} 528 529/* 530 * Integer type checking. 531 */ 532static void 533get_int(v, lp) 534 char *v; 535 long *lp; 536{ 537 long val; 538 char *ep; 539 540 for (; *v && isspace(*v); ++v); 541 542 if(!*v) { 543 *lp = 0; 544 return; 545 } 546 547 if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) { 548 errno = 0; 549 val = strtol(v, &ep, 10); 550 if (*ep != '\0') 551 errx(2, "%s: trailing non-numeric characters", v); 552 if (errno == ERANGE) { 553 if (val == LONG_MIN) 554 errx(2, "%s: underflow", v); 555 if (val == LONG_MAX) 556 errx(2, "%s: overflow", v); 557 } 558 *lp = val; 559 return; 560 } 561 errx(2, "%s: expected integer", v); 562} 563 564static void 565syntax() 566{ 567 568 err(2, "syntax error"); 569} 570 571static void 572overflow() 573{ 574 575 err(2, "expression is too complex"); 576} 577