1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * 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 37 #ifndef lint 38 char copyright[] = 39 "@(#) Copyright (c) 1992 The Regents of the University of California.\n\ 40 All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 /*static char sccsid[] = "@(#)test.c 5.4 (Berkeley) 2/12/93";*/ 45 static char *rcsid = "$Id: test.c,v 1.11 1994/04/10 05:37:11 cgd Exp $"; 46 #endif /* not lint */ 47 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <errno.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <stdio.h> 54 55 #include "operators.h" 56 57 #define STACKSIZE 12 58 #define NESTINCR 16 59 60 /* data types */ 61 #define STRING 0 62 #define INTEGER 1 63 #define BOOLEAN 2 64 65 #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 66 67 /* 68 * This structure hold a value. The type keyword specifies the type of 69 * the value, and the union u holds the value. The value of a boolean 70 * is stored in u.num (1 = TRUE, 0 = FALSE). 71 */ 72 struct value { 73 int type; 74 union { 75 char *string; 76 long num; 77 } u; 78 }; 79 80 struct operator { 81 short op; /* Which operator. */ 82 short pri; /* Priority of operator. */ 83 }; 84 85 struct filestat { 86 char *name; /* Name of file. */ 87 int rcode; /* Return code from stat. */ 88 struct stat stat; /* Status info on file. */ 89 }; 90 91 static void err __P((const char *, ...)); 92 static int expr_is_false __P((struct value *)); 93 static void expr_operator __P((int, struct value *, struct filestat *)); 94 static long chk_atol __P((char *)); 95 static int lookup_op __P((char *, char *const *)); 96 static void overflow __P((void)); 97 static int posix_binary_op __P((char **)); 98 static int posix_unary_op __P((char **)); 99 static void syntax __P((void)); 100 101 int 102 main(argc, argv) 103 int argc; 104 char *argv[]; 105 { 106 struct operator opstack[STACKSIZE]; 107 struct operator *opsp; 108 struct value valstack[STACKSIZE + 1]; 109 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 err("test: argc is zero.\n"); 116 exit(2); 117 } 118 119 if (*p != '\0' && p[strlen(p) - 1] == '[') { 120 if (strcmp(argv[--argc], "]")) 121 err("missing ]"); 122 argv[argc] = NULL; 123 } 124 ap = argv + 1; 125 fs.name = NULL; 126 127 /* 128 * Test(1) implements an inherently ambiguous grammer. In order to 129 * assure some degree of consistency, we special case the POSIX 1003.2 130 * requirements to assure correct evaluation for POSIX scripts. The 131 * following special cases comply with POSIX P1003.2/D11.2 Section 132 * 4.62.4. 133 */ 134 switch(argc - 1) { 135 case 0: /* % test */ 136 return (1); 137 break; 138 case 1: /* % test arg */ 139 /* make sure it's not e.g. 'test -f' */ 140 if (argv[1] != 0 && 141 (lookup_op(argv[1], unary_op) != -1 || 142 lookup_op(argv[1], binary_op) != -1 || 143 lookup_op(argv[1], andor_op) != -1)) 144 syntax(); 145 /* MIPS machine returns NULL of '[ ]' is called. */ 146 return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0; 147 break; 148 case 2: /* % test op arg */ 149 opname = argv[1]; 150 if (IS_BANG(opname)) 151 return (*argv[2] == '\0') ? 0 : 1; 152 else { 153 ret_val = posix_unary_op(&argv[1]); 154 if (ret_val >= 0) 155 return (ret_val); 156 } 157 break; 158 case 3: /* % test arg1 op arg2 */ 159 if (IS_BANG(argv[1])) { 160 ret_val = posix_unary_op(&argv[1]); 161 if (ret_val >= 0) 162 return (!ret_val); 163 } else if (lookup_op(argv[2], andor_op) < 0) { 164 ret_val = posix_binary_op(&argv[1]); 165 if (ret_val >= 0) 166 return (ret_val); 167 } 168 break; 169 case 4: /* % test ! arg1 op arg2 */ 170 if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0) { 171 ret_val = posix_binary_op(&argv[2]); 172 if (ret_val >= 0) 173 return (!ret_val); 174 } 175 break; 176 default: 177 break; 178 } 179 180 /* 181 * We use operator precedence parsing, evaluating the expression as 182 * we parse it. Parentheses are handled by bumping up the priority 183 * of operators using the variable "nest." We use the variable 184 * "skipping" to turn off evaluation temporarily for the short 185 * circuit boolean operators. (It is important do the short circuit 186 * evaluation because under NFS a stat operation can take infinitely 187 * long.) 188 */ 189 opsp = opstack + STACKSIZE; 190 valsp = valstack; 191 nest = skipping = 0; 192 if (*ap == NULL) { 193 valstack[0].type = BOOLEAN; 194 valstack[0].u.num = 0; 195 goto done; 196 } 197 for (;;) { 198 opname = *ap++; 199 if (opname == NULL) 200 syntax(); 201 if (opname[0] == '(' && opname[1] == '\0') { 202 nest += NESTINCR; 203 continue; 204 } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 205 if (opsp == &opstack[0]) 206 overflow(); 207 --opsp; 208 opsp->op = op; 209 opsp->pri = op_priority[op] + nest; 210 continue; 211 } else { 212 valsp->type = STRING; 213 valsp->u.string = opname; 214 valsp++; 215 } 216 for (;;) { 217 opname = *ap++; 218 if (opname == NULL) { 219 if (nest != 0) 220 syntax(); 221 pri = 0; 222 break; 223 } 224 if (opname[0] != ')' || opname[1] != '\0') { 225 if ((op = lookup_op(opname, binary_op)) < 0) 226 syntax(); 227 op += FIRST_BINARY_OP; 228 pri = op_priority[op] + nest; 229 break; 230 } 231 if ((nest -= NESTINCR) < 0) 232 syntax(); 233 } 234 while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 235 binary = opsp->op; 236 for (;;) { 237 valsp--; 238 c = op_argflag[opsp->op]; 239 if (c == OP_INT) { 240 if (valsp->type == STRING) 241 valsp->u.num = 242 chk_atol(valsp->u.string); 243 valsp->type = INTEGER; 244 } else if (c >= OP_STRING) { 245 /* OP_STRING or OP_FILE */ 246 if (valsp->type == INTEGER) { 247 if ((p = malloc(32)) == NULL) 248 err("%s", 249 strerror(errno)); 250 #ifdef SHELL 251 fmtstr(p, 32, "%d", 252 valsp->u.num); 253 #else 254 (void)sprintf(p, 255 "%d", valsp->u.num); 256 #endif 257 valsp->u.string = p; 258 } else if (valsp->type == BOOLEAN) { 259 if (valsp->u.num) 260 valsp->u.string = 261 "true"; 262 else 263 valsp->u.string = ""; 264 } 265 valsp->type = STRING; 266 if (c == OP_FILE && (fs.name == NULL || 267 strcmp(fs.name, valsp->u.string))) { 268 fs.name = valsp->u.string; 269 fs.rcode = 270 stat(valsp->u.string, 271 &fs.stat); 272 } 273 } 274 if (binary < FIRST_BINARY_OP) 275 break; 276 binary = 0; 277 } 278 if (!skipping) 279 expr_operator(opsp->op, valsp, &fs); 280 else if (opsp->op == AND1 || opsp->op == OR1) 281 skipping--; 282 valsp++; /* push value */ 283 opsp++; /* pop operator */ 284 } 285 if (opname == NULL) 286 break; 287 if (opsp == &opstack[0]) 288 overflow(); 289 if (op == AND1 || op == AND2) { 290 op = AND1; 291 if (skipping || expr_is_false(valsp - 1)) 292 skipping++; 293 } 294 if (op == OR1 || op == OR2) { 295 op = OR1; 296 if (skipping || !expr_is_false(valsp - 1)) 297 skipping++; 298 } 299 opsp--; 300 opsp->op = op; 301 opsp->pri = pri; 302 } 303 done: return (expr_is_false(&valstack[0])); 304 } 305 306 static int 307 expr_is_false(val) 308 struct value *val; 309 { 310 if (val->type == STRING) { 311 if (val->u.string[0] == '\0') 312 return (1); 313 } else { /* INTEGER or BOOLEAN */ 314 if (val->u.num == 0) 315 return (1); 316 } 317 return (0); 318 } 319 320 321 /* 322 * Execute an operator. Op is the operator. Sp is the stack pointer; 323 * sp[0] refers to the first operand, sp[1] refers to the second operand 324 * (if any), and the result is placed in sp[0]. The operands are converted 325 * to the type expected by the operator before expr_operator is called. 326 * Fs is a pointer to a structure which holds the value of the last call 327 * to stat, to avoid repeated stat calls on the same file. 328 */ 329 static void 330 expr_operator(op, sp, fs) 331 int op; 332 struct value *sp; 333 struct filestat *fs; 334 { 335 int i; 336 337 switch (op) { 338 case NOT: 339 sp->u.num = expr_is_false(sp); 340 sp->type = BOOLEAN; 341 break; 342 case ISEXIST: 343 if (fs == NULL || fs->rcode == -1) 344 goto false; 345 else 346 goto true; 347 case ISREAD: 348 i = S_IROTH; 349 goto permission; 350 case ISWRITE: 351 i = S_IWOTH; 352 goto permission; 353 case ISEXEC: 354 i = S_IXOTH; 355 permission: if (fs->stat.st_uid == geteuid()) 356 i <<= 6; 357 else if (fs->stat.st_gid == getegid()) 358 i <<= 3; 359 goto filebit; /* true if (stat.st_mode & i) != 0 */ 360 case ISFILE: 361 i = S_IFREG; 362 goto filetype; 363 case ISDIR: 364 i = S_IFDIR; 365 goto filetype; 366 case ISCHAR: 367 i = S_IFCHR; 368 goto filetype; 369 case ISBLOCK: 370 i = S_IFBLK; 371 goto filetype; 372 case ISFIFO: 373 i = S_IFIFO; 374 goto filetype; 375 filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 376 true: sp->u.num = 1; 377 else 378 false: sp->u.num = 0; 379 sp->type = BOOLEAN; 380 break; 381 case ISSETUID: 382 i = S_ISUID; 383 goto filebit; 384 case ISSETGID: 385 i = S_ISGID; 386 goto filebit; 387 case ISSTICKY: 388 i = S_ISVTX; 389 filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 390 goto true; 391 goto false; 392 case ISSIZE: 393 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 394 sp->type = INTEGER; 395 break; 396 case ISTTY: 397 sp->u.num = isatty(sp->u.num); 398 sp->type = BOOLEAN; 399 break; 400 case ISLNK: 401 { 402 struct stat sb; 403 int rv; 404 405 rv = lstat(fs->name, &sb); 406 if ((sb.st_mode & S_IFLNK) == S_IFLNK && rv >= 0) 407 goto true; 408 goto false; 409 } 410 case NULSTR: 411 if (sp->u.string[0] == '\0') 412 goto true; 413 goto false; 414 case STRLEN: 415 sp->u.num = strlen(sp->u.string); 416 sp->type = INTEGER; 417 break; 418 case OR1: 419 case AND1: 420 /* 421 * These operators are mostly handled by the parser. If we 422 * get here it means that both operands were evaluated, so 423 * the value is the value of the second operand. 424 */ 425 *sp = *(sp + 1); 426 break; 427 case STREQ: 428 case STRNE: 429 i = 0; 430 if (!strcmp(sp->u.string, (sp + 1)->u.string)) 431 i++; 432 if (op == STRNE) 433 i = 1 - i; 434 sp->u.num = i; 435 sp->type = BOOLEAN; 436 break; 437 case EQ: 438 if (sp->u.num == (sp + 1)->u.num) 439 goto true; 440 goto false; 441 case NE: 442 if (sp->u.num != (sp + 1)->u.num) 443 goto true; 444 goto false; 445 case GT: 446 if (sp->u.num > (sp + 1)->u.num) 447 goto true; 448 goto false; 449 case LT: 450 if (sp->u.num < (sp + 1)->u.num) 451 goto true; 452 goto false; 453 case LE: 454 if (sp->u.num <= (sp + 1)->u.num) 455 goto true; 456 goto false; 457 case GE: 458 if (sp->u.num >= (sp + 1)->u.num) 459 goto true; 460 goto false; 461 462 } 463 } 464 465 static int 466 lookup_op(name, table) 467 char *name; 468 char *const * table; 469 { 470 register char *const * tp; 471 register char const *p; 472 char c; 473 474 c = name[1]; 475 for (tp = table; (p = *tp) != NULL; tp++) 476 if (p[1] == c && !strcmp(p, name)) 477 return (tp - table); 478 return (-1); 479 } 480 481 static int 482 posix_unary_op(argv) 483 char **argv; 484 { 485 struct filestat fs; 486 struct value valp; 487 int op, c; 488 char *opname; 489 490 opname = *argv; 491 if ((op = lookup_op(opname, unary_op)) < 0) 492 return (-1); 493 c = op_argflag[op]; 494 opname = argv[1]; 495 valp.u.string = opname; 496 if (c == OP_FILE) { 497 fs.name = opname; 498 fs.rcode = stat(opname, &fs.stat); 499 } else if (c != OP_STRING) 500 return (-1); 501 502 expr_operator(op, &valp, &fs); 503 return (valp.u.num == 0); 504 } 505 506 static int 507 posix_binary_op(argv) 508 char **argv; 509 { 510 struct value v[2]; 511 int op, c; 512 char *opname; 513 514 opname = argv[1]; 515 if ((op = lookup_op(opname, binary_op)) < 0) 516 return (-1); 517 op += FIRST_BINARY_OP; 518 c = op_argflag[op]; 519 520 if (c == OP_INT) { 521 v[0].u.num = chk_atol(argv[0]); 522 v[1].u.num = chk_atol(argv[2]); 523 } else { 524 v[0].u.string = argv[0]; 525 v[1].u.string = argv[2]; 526 } 527 expr_operator(op, v, NULL); 528 return (v[0].u.num == 0); 529 } 530 531 /* 532 * Integer type checking. 533 */ 534 static long 535 chk_atol(v) 536 char *v; 537 { 538 char *p; 539 long r; 540 541 errno = 0; 542 r = strtol(v, &p, 10); 543 if (errno != 0) 544 err("\"%s\" -- out of range.", v); 545 while (isspace(*p)) 546 p++; 547 if (*p != '\0') 548 err("illegal operand \"%s\" -- expected integer.", v); 549 return (r); 550 } 551 552 static void 553 syntax() 554 { 555 err("syntax error"); 556 } 557 558 static void 559 overflow() 560 { 561 err("expression is too complex"); 562 } 563 564 #if __STDC__ 565 #include <stdarg.h> 566 #else 567 #include <varargs.h> 568 #endif 569 570 void 571 #if __STDC__ 572 err(const char *fmt, ...) 573 #else 574 err(fmt, va_alist) 575 char *fmt; 576 va_dcl 577 #endif 578 { 579 va_list ap; 580 #if __STDC__ 581 va_start(ap, fmt); 582 #else 583 va_start(ap); 584 #endif 585 (void)fprintf(stderr, "test: "); 586 (void)vfprintf(stderr, fmt, ap); 587 va_end(ap); 588 (void)fprintf(stderr, "\n"); 589 exit(2); 590 /* NOTREACHED */ 591 } 592