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: }

Defined functions

expr_is_false defined in line 296; used 4 times
expr_operator defined in line 320; used 3 times
get_int defined in line 541; used 3 times
lookup_op defined in line 470; used 5 times
main defined in line 101; never used
overflow defined in line 568; used 2 times
posix_binary_op defined in line 513; used 2 times
posix_unary_op defined in line 488; used 2 times
syntax defined in line 561; used 4 times

Defined variables

copyright defined in line 40; never used
sccsid defined in line 46; never used

Defined struct's

filestat defined in line 88; used 6 times
operator defined in line 83; used 4 times
value defined in line 75; used 12 times

Defined macros

BOOLEAN defined in line 66; used 6 times
INTEGER defined in line 65; used 4 times
IS_BANG defined in line 68; used 3 times
NESTINCR defined in line 61; used 2 times
STACKSIZE defined in line 60; used 4 times
STRING defined in line 64; used 4 times
Last modified: 1995-03-14
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4365
Valid CSS Valid XHTML 1.0 Strict