1: /*- 2: * Copyright (c) 1992, 1993, 1994 3: * The Regents of the University of California. All rights reserved. 4: * 5: * This code is derived from software contributed to Berkeley by 6: * Kenneth Almquist. 7: * 8: * Redistribution and use in source and binary forms, with or without 9: * modification, are permitted provided that the following conditions 10: * are met: 11: * 1. Redistributions of source code must retain the above copyright 12: * notice, this list of conditions and the following disclaimer. 13: * 2. Redistributions in binary form must reproduce the above copyright 14: * notice, this list of conditions and the following disclaimer in the 15: * documentation and/or other materials provided with the distribution. 16: * 3. All advertising materials mentioning features or use of this software 17: * must display the following acknowledgement: 18: * This product includes software developed by the University of 19: * California, Berkeley and its contributors. 20: * 4. Neither the name of the University nor the names of its contributors 21: * may be used to endorse or promote products derived from this software 22: * without specific prior written permission. 23: * 24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34: * SUCH DAMAGE. 35: * 36: * test.c,v 1.9 1994/11/05 20:48:06 ache Exp 37: */ 38: 39: #if defined(DOSCCS) & !defined(lint) 40: static char copyright[] = 41: "@(#) Copyright (c) 1992, 1993, 1994\n\ 42: The Regents of the University of California. All rights reserved.\n"; 43: #endif /* not lint */ 44: 45: #if defined(DOSCCS) & !defined(lint) 46: static char sccsid[] = "@(#)test.c 8.3.1 (2.11BSD) 1995/03/13"; 47: #endif /* not lint */ 48: 49: #include <sys/types.h> 50: #include <sys/stat.h> 51: #include <sys/param.h> 52: 53: #include <ctype.h> 54: #include <errno.h> 55: #include <stdio.h> 56: #include <string.h> 57: 58: #include "operators.h" 59: 60: #define STACKSIZE 12 61: #define NESTINCR 16 62: 63: /* data types */ 64: #define STRING 0 65: #define INTEGER 1 66: #define BOOLEAN 2 67: 68: #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 69: 70: /* 71: * This structure hold a value. The type keyword specifies the type of 72: * the value, and the union u holds the value. The value of a boolean 73: * is stored in u.num (1 = TRUE, 0 = FALSE). 74: */ 75: struct value { 76: int type; 77: union { 78: char *string; 79: long num; 80: } u; 81: }; 82: 83: struct operator { 84: short op; /* Which operator. */ 85: short pri; /* Priority of operator. */ 86: }; 87: 88: struct filestat { 89: char *name; /* Name of file. */ 90: int rcode; /* Return code from stat. */ 91: struct stat stat; /* Status info on file. */ 92: }; 93: 94: extern char unary_op[][4]; 95: extern char binary_op[][4]; 96: extern char andor_op[][4]; 97: extern int op_priority[]; 98: extern int op_argflag[]; 99: extern long atol(); 100: 101: main(argc, argv) 102: int argc; 103: char *argv[]; 104: { 105: 106: struct operator opstack[STACKSIZE]; 107: register struct operator *opsp; 108: struct value valstack[STACKSIZE + 1]; 109: register struct value *valsp; 110: struct filestat fs; 111: char c, **ap, *opname, *p; 112: int binary, nest, op, pri, ret_val, skipping; 113: 114: if ((p = argv[0]) == NULL) 115: errx(2, "test: argc is zero"); 116: 117: if (*p != '\0' && p[strlen(p) - 1] == '[') { 118: if (strcmp(argv[--argc], "]")) 119: errx(2, "missing ]"); 120: argv[argc] = NULL; 121: } 122: ap = argv + 1; 123: fs.name = NULL; 124: 125: /* 126: * Test(1) implements an inherently ambiguous grammer. In order to 127: * assure some degree of consistency, we special case the POSIX 1003.2 128: * requirements to assure correct evaluation for POSIX scripts. The 129: * following special cases comply with POSIX P1003.2/D11.2 Section 130: * 4.62.4. 131: */ 132: switch(argc - 1) { 133: case 0: /* % test */ 134: return (1); 135: break; 136: case 1: /* % test arg */ 137: return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; 138: break; 139: case 2: /* % test op arg */ 140: opname = argv[1]; 141: if (IS_BANG(opname)) 142: return (*argv[2] == '\0') ? 0 : 1; 143: else { 144: ret_val = posix_unary_op(&argv[1]); 145: if (ret_val >= 0) 146: return (ret_val); 147: } 148: break; 149: case 3: /* % test arg1 op arg2 */ 150: if (IS_BANG(argv[1])) { 151: ret_val = posix_unary_op(&argv[1]); 152: if (ret_val >= 0) 153: return (!ret_val); 154: } else { 155: ret_val = posix_binary_op(&argv[1]); 156: if (ret_val >= 0) 157: return (ret_val); 158: } 159: break; 160: case 4: /* % test ! arg1 op arg2 */ 161: if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) { 162: ret_val = posix_binary_op(&argv[2]); 163: if (ret_val >= 0) 164: return (!ret_val); 165: } 166: break; 167: default: 168: break; 169: } 170: 171: /* 172: * We use operator precedence parsing, evaluating the expression as 173: * we parse it. Parentheses are handled by bumping up the priority 174: * of operators using the variable "nest." We use the variable 175: * "skipping" to turn off evaluation temporarily for the short 176: * circuit boolean operators. (It is important do the short circuit 177: * evaluation because under NFS a stat operation can take infinitely 178: * long.) 179: */ 180: opsp = opstack + STACKSIZE; 181: valsp = valstack; 182: nest = skipping = 0; 183: if (*ap == NULL) { 184: valstack[0].type = BOOLEAN; 185: valstack[0].u.num = 0; 186: goto done; 187: } 188: for (;;) { 189: opname = *ap++; 190: if (opname == NULL) 191: syntax(); 192: if (opname[0] == '(' && opname[1] == '\0') { 193: nest += NESTINCR; 194: continue; 195: } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 196: if (opsp == &opstack[0]) 197: overflow(); 198: --opsp; 199: opsp->op = op; 200: opsp->pri = op_priority[op] + nest; 201: continue; 202: } else { 203: valsp->type = STRING; 204: valsp->u.string = opname; 205: valsp++; 206: } 207: for (;;) { 208: opname = *ap++; 209: if (opname == NULL) { 210: if (nest != 0) 211: syntax(); 212: pri = 0; 213: break; 214: } 215: if (opname[0] != ')' || opname[1] != '\0') { 216: if ((op = lookup_op(opname, binary_op)) < 0) 217: syntax(); 218: op += FIRST_BINARY_OP; 219: pri = op_priority[op] + nest; 220: break; 221: } 222: if ((nest -= NESTINCR) < 0) 223: syntax(); 224: } 225: while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 226: binary = opsp->op; 227: for (;;) { 228: valsp--; 229: c = op_argflag[opsp->op]; 230: if (c == OP_INT) { 231: if (valsp->type == STRING) 232: get_int(valsp->u.string, 233: &valsp->u.num); 234: valsp->type = INTEGER; 235: } else if (c >= OP_STRING) { 236: /* OP_STRING or OP_FILE */ 237: if (valsp->type == INTEGER) { 238: if ((p = (char *)malloc(32)) == NULL) 239: err(2, NULL); 240: #ifdef SHELL 241: fmtstr(p, 32, "%d", 242: valsp->u.num); 243: #else 244: (void)sprintf(p, 245: "%d", valsp->u.num); 246: #endif 247: valsp->u.string = p; 248: } else if (valsp->type == BOOLEAN) { 249: if (valsp->u.num) 250: valsp->u.string = 251: "true"; 252: else 253: valsp->u.string = ""; 254: } 255: valsp->type = STRING; 256: if (c == OP_FILE && (fs.name == NULL || 257: strcmp(fs.name, valsp->u.string))) { 258: fs.name = valsp->u.string; 259: fs.rcode = 260: stat(valsp->u.string, 261: &fs.stat); 262: } 263: } 264: if (binary < FIRST_BINARY_OP) 265: break; 266: binary = 0; 267: } 268: if (!skipping) 269: expr_operator(opsp->op, valsp, &fs); 270: else if (opsp->op == AND1 || opsp->op == OR1) 271: skipping--; 272: valsp++; /* push value */ 273: opsp++; /* pop operator */ 274: } 275: if (opname == NULL) 276: break; 277: if (opsp == &opstack[0]) 278: overflow(); 279: if (op == AND1 || op == AND2) { 280: op = AND1; 281: if (skipping || expr_is_false(valsp - 1)) 282: skipping++; 283: } 284: if (op == OR1 || op == OR2) { 285: op = OR1; 286: if (skipping || !expr_is_false(valsp - 1)) 287: skipping++; 288: } 289: opsp--; 290: opsp->op = op; 291: opsp->pri = pri; 292: } 293: done: return (expr_is_false(&valstack[0])); 294: } 295: 296: int 297: expr_is_false(val) 298: register struct value *val; 299: { 300: 301: if (val->type == STRING) { 302: if (val->u.string[0] == '\0') 303: return (1); 304: } else { /* INTEGER or BOOLEAN */ 305: if (val->u.num == 0) 306: return (1); 307: } 308: return (0); 309: } 310: 311: 312: /* 313: * Execute an operator. Op is the operator. Sp is the stack pointer; 314: * sp[0] refers to the first operand, sp[1] refers to the second operand 315: * (if any), and the result is placed in sp[0]. The operands are converted 316: * to the type expected by the operator before expr_operator is called. 317: * Fs is a pointer to a structure which holds the value of the last call 318: * to stat, to avoid repeated stat calls on the same file. 319: */ 320: void 321: expr_operator(op, sp, fs) 322: int op; 323: register struct value *sp; 324: struct filestat *fs; 325: { 326: register int i; 327: 328: switch (op) { 329: case NOT: 330: sp->u.num = expr_is_false(sp); 331: sp->type = BOOLEAN; 332: break; 333: case ISEXIST: 334: exist: 335: if (fs == NULL || fs->rcode == -1) 336: goto false; 337: else 338: goto true; 339: case ISREAD: 340: if (geteuid() == 0) 341: goto exist; 342: i = S_IROTH; 343: goto permission; 344: case ISWRITE: 345: if (geteuid() != 0) 346: i = S_IWOTH; 347: else { 348: i = S_IWOTH|S_IWGRP|S_IWUSR; 349: goto filebit; 350: } 351: goto permission; 352: case ISEXEC: 353: if (geteuid() != 0) { 354: i = S_IXOTH; 355: permission: if (fs->stat.st_uid == geteuid()) 356: i <<= 6; 357: else { 358: gid_t grlist[NGROUPS]; 359: int ngroups, j; 360: 361: ngroups = getgroups(NGROUPS, grlist); 362: for (j = 0; j < ngroups; j++) 363: if (fs->stat.st_gid == grlist[j]) { 364: i <<= 3; 365: goto filebit; 366: } 367: } 368: } else 369: i = S_IXOTH|S_IXGRP|S_IXUSR; 370: goto filebit; /* true if (stat.st_mode & i) != 0 */ 371: case ISFILE: 372: i = S_IFREG; 373: goto filetype; 374: case ISDIR: 375: i = S_IFDIR; 376: goto filetype; 377: case ISCHAR: 378: i = S_IFCHR; 379: goto filetype; 380: case ISBLOCK: 381: i = S_IFBLK; 382: goto filetype; 383: case ISSYMLINK: 384: i = S_IFLNK; 385: (void)lstat(sp->u.string, &fs->stat); 386: goto filetype; 387: case ISFIFO: 388: i = S_IFIFO; 389: goto filetype; 390: filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 391: true: sp->u.num = 1; 392: else 393: false: sp->u.num = 0; 394: sp->type = BOOLEAN; 395: break; 396: case ISSETUID: 397: i = S_ISUID; 398: goto filebit; 399: case ISSETGID: 400: i = S_ISGID; 401: goto filebit; 402: case ISSTICKY: 403: i = S_ISVTX; 404: filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 405: goto true; 406: goto false; 407: case ISSIZE: 408: sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 409: sp->type = INTEGER; 410: break; 411: case ISTTY: 412: sp->u.num = isatty(sp->u.num); 413: sp->type = BOOLEAN; 414: break; 415: case NULSTR: 416: if (sp->u.string[0] == '\0') 417: goto true; 418: goto false; 419: case STRLEN: 420: sp->u.num = strlen(sp->u.string); 421: sp->type = INTEGER; 422: break; 423: case OR1: 424: case AND1: 425: /* 426: * These operators are mostly handled by the parser. If we 427: * get here it means that both operands were evaluated, so 428: * the value is the value of the second operand. 429: */ 430: *sp = *(sp + 1); 431: break; 432: case STREQ: 433: case STRNE: 434: i = 0; 435: if (!strcmp(sp->u.string, (sp + 1)->u.string)) 436: i++; 437: if (op == STRNE) 438: i = 1 - i; 439: sp->u.num = i; 440: sp->type = BOOLEAN; 441: break; 442: case EQ: 443: if (sp->u.num == (sp + 1)->u.num) 444: goto true; 445: goto false; 446: case NE: 447: if (sp->u.num != (sp + 1)->u.num) 448: goto true; 449: goto false; 450: case GT: 451: if (sp->u.num > (sp + 1)->u.num) 452: goto true; 453: goto false; 454: case LT: 455: if (sp->u.num < (sp + 1)->u.num) 456: goto true; 457: goto false; 458: case LE: 459: if (sp->u.num <= (sp + 1)->u.num) 460: goto true; 461: goto false; 462: case GE: 463: if (sp->u.num >= (sp + 1)->u.num) 464: goto true; 465: goto false; 466: 467: } 468: } 469: 470: int 471: lookup_op(name, table) 472: char *name; 473: char *table; 474: { 475: char *tp; 476: char c; 477: int i; 478: 479: c = name[1]; 480: tp = table; 481: for(i = 0; strcmp((char *)(tp + i),"ZZZ") ; i=i+4){ 482: if ((char)(tp + i)[1] == c && !strcmp((char *)(tp + i), name)) 483: return (i/4); 484: } 485: return (-1); 486: } 487: 488: int 489: posix_unary_op(argv) 490: char **argv; 491: { 492: struct filestat fs; 493: struct value valp; 494: int op, c; 495: char *opname; 496: 497: opname = *argv; 498: if ((op = lookup_op(opname, unary_op)) < 0) 499: return (-1); 500: c = op_argflag[op]; 501: opname = argv[1]; 502: valp.u.string = opname; 503: if (c == OP_FILE) { 504: fs.name = opname; 505: fs.rcode = stat(opname, &fs.stat); 506: } else if (c != OP_STRING) 507: return (-1); 508: 509: expr_operator(op, &valp, &fs); 510: return (valp.u.num == 0); 511: } 512: 513: int 514: posix_binary_op(argv) 515: char **argv; 516: { 517: struct value v[2]; 518: int op, c; 519: char *opname; 520: 521: opname = argv[1]; 522: if ((op = lookup_op(opname, binary_op)) < 0) 523: return (-1); 524: op += FIRST_BINARY_OP; 525: c = op_argflag[op]; 526: 527: if (c == OP_INT) { 528: get_int(argv[0], &v[0].u.num); 529: get_int(argv[2], &v[1].u.num); 530: } else { 531: v[0].u.string = argv[0]; 532: v[1].u.string = argv[2]; 533: } 534: expr_operator(op, v, NULL); 535: return (v[0].u.num == 0); 536: } 537: 538: /* 539: * Integer type checking. 540: */ 541: void 542: get_int(v, lp) 543: register char *v; 544: long *lp; 545: { 546: 547: for (; *v && isspace(*v); ++v); 548: 549: if(!*v) { 550: *lp = 0; 551: return; 552: } 553: 554: if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) { 555: *lp = atol(v); 556: return; 557: } 558: errx(2, "%s: expected integer", v); 559: } 560: 561: void 562: syntax() 563: { 564: 565: err(2, "syntax error"); 566: } 567: 568: void 569: overflow() 570: { 571: 572: err(2, "expression is too complex"); 573: }