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