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.9 1993/10/26 01:11:28 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 /* MIPS machine returns NULL of '[ ]' is called. */ 140 return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0; 141 break; 142 case 2: /* % test op arg */ 143 opname = argv[1]; 144 if (IS_BANG(opname)) 145 return (*argv[2] == '\0') ? 0 : 1; 146 else { 147 ret_val = posix_unary_op(&argv[1]); 148 if (ret_val >= 0) 149 return (ret_val); 150 } 151 break; 152 case 3: /* % test arg1 op arg2 */ 153 if (IS_BANG(argv[1])) { 154 ret_val = posix_unary_op(&argv[1]); 155 if (ret_val >= 0) 156 return (!ret_val); 157 } else if (lookup_op(argv[2], andor_op) < 0) { 158 ret_val = posix_binary_op(&argv[1]); 159 if (ret_val >= 0) 160 return (ret_val); 161 } 162 break; 163 case 4: /* % test ! arg1 op arg2 */ 164 if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0) { 165 ret_val = posix_binary_op(&argv[2]); 166 if (ret_val >= 0) 167 return (!ret_val); 168 } 169 break; 170 default: 171 break; 172 } 173 174 /* 175 * We use operator precedence parsing, evaluating the expression as 176 * we parse it. Parentheses are handled by bumping up the priority 177 * of operators using the variable "nest." We use the variable 178 * "skipping" to turn off evaluation temporarily for the short 179 * circuit boolean operators. (It is important do the short circuit 180 * evaluation because under NFS a stat operation can take infinitely 181 * long.) 182 */ 183 opsp = opstack + STACKSIZE; 184 valsp = valstack; 185 nest = skipping = 0; 186 if (*ap == NULL) { 187 valstack[0].type = BOOLEAN; 188 valstack[0].u.num = 0; 189 goto done; 190 } 191 for (;;) { 192 opname = *ap++; 193 if (opname == NULL) 194 syntax(); 195 if (opname[0] == '(' && opname[1] == '\0') { 196 nest += NESTINCR; 197 continue; 198 } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 199 if (opsp == &opstack[0]) 200 overflow(); 201 --opsp; 202 opsp->op = op; 203 opsp->pri = op_priority[op] + nest; 204 continue; 205 } else { 206 valsp->type = STRING; 207 valsp->u.string = opname; 208 valsp++; 209 } 210 for (;;) { 211 opname = *ap++; 212 if (opname == NULL) { 213 if (nest != 0) 214 syntax(); 215 pri = 0; 216 break; 217 } 218 if (opname[0] != ')' || opname[1] != '\0') { 219 if ((op = lookup_op(opname, binary_op)) < 0) 220 syntax(); 221 op += FIRST_BINARY_OP; 222 pri = op_priority[op] + nest; 223 break; 224 } 225 if ((nest -= NESTINCR) < 0) 226 syntax(); 227 } 228 while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 229 binary = opsp->op; 230 for (;;) { 231 valsp--; 232 c = op_argflag[opsp->op]; 233 if (c == OP_INT) { 234 if (valsp->type == STRING) 235 valsp->u.num = 236 chk_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) { 505 v[0].u.num = chk_atol(argv[0]); 506 v[1].u.num = chk_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 long 519 chk_atol(v) 520 char *v; 521 { 522 char *p; 523 long r; 524 525 errno = 0; 526 r = strtol(v, &p, 10); 527 if (errno != 0) 528 err("\"%s\" -- out of range.", v); 529 while (isspace(*p)) 530 p++; 531 if (*p != '\0') 532 err("illegal operand \"%s\" -- expected integer.", v); 533 return (r); 534 } 535 536 static void 537 syntax() 538 { 539 err("syntax error"); 540 } 541 542 static void 543 overflow() 544 { 545 err("expression is too complex"); 546 } 547 548 #if __STDC__ 549 #include <stdarg.h> 550 #else 551 #include <varargs.h> 552 #endif 553 554 void 555 #if __STDC__ 556 err(const char *fmt, ...) 557 #else 558 err(fmt, va_alist) 559 char *fmt; 560 va_dcl 561 #endif 562 { 563 va_list ap; 564 #if __STDC__ 565 va_start(ap, fmt); 566 #else 567 va_start(ap); 568 #endif 569 (void)fprintf(stderr, "test: "); 570 (void)vfprintf(stderr, fmt, ap); 571 va_end(ap); 572 (void)fprintf(stderr, "\n"); 573 exit(2); 574 /* NOTREACHED */ 575 } 576