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 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1992, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94"; 45 #endif /* not lint */ 46 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <limits.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "operators.h" 60 61 #define STACKSIZE 12 62 #define NESTINCR 16 63 64 /* data types */ 65 #define STRING 0 66 #define INTEGER 1 67 #define BOOLEAN 2 68 69 #define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 70 71 /* 72 * This structure hold a value. The type keyword specifies the type of 73 * the value, and the union u holds the value. The value of a boolean 74 * is stored in u.num (1 = TRUE, 0 = FALSE). 75 */ 76 struct value { 77 int type; 78 union { 79 char *string; 80 long num; 81 } u; 82 }; 83 84 struct operator { 85 short op; /* Which operator. */ 86 short pri; /* Priority of operator. */ 87 }; 88 89 struct filestat { 90 char *name; /* Name of file. */ 91 int rcode; /* Return code from stat. */ 92 struct stat stat; /* Status info on file. */ 93 }; 94 95 static int expr_is_false __P((struct value *)); 96 static void expr_operator __P((int, struct value *, struct filestat *)); 97 static void get_int __P((char *, long *)); 98 static int lookup_op __P((char *, const char *const *)); 99 static void overflow __P((void)); 100 static int posix_binary_op __P((char **)); 101 static int posix_unary_op __P((char **)); 102 static void syntax __P((void)); 103 104 int 105 main(argc, argv) 106 int argc; 107 char *argv[]; 108 { 109 struct operator opstack[STACKSIZE]; 110 struct operator *opsp; 111 struct value valstack[STACKSIZE + 1]; 112 struct value *valsp; 113 struct filestat fs; 114 char c, **ap, *opname, *p; 115 int binary, nest, op, pri, ret_val, skipping; 116 117 if ((p = argv[0]) == NULL) 118 errx(2, "test: argc is zero"); 119 120 if (*p != '\0' && p[strlen(p) - 1] == '[') { 121 if (strcmp(argv[--argc], "]")) 122 errx(2, "missing ]"); 123 argv[argc] = NULL; 124 } 125 ap = argv + 1; 126 fs.name = NULL; 127 128 /* 129 * Test(1) implements an inherently ambiguous grammer. In order to 130 * assure some degree of consistency, we special case the POSIX 1003.2 131 * requirements to assure correct evaluation for POSIX scripts. The 132 * following special cases comply with POSIX P1003.2/D11.2 Section 133 * 4.62.4. 134 */ 135 switch(argc - 1) { 136 case 0: /* % test */ 137 return (1); 138 break; 139 case 1: /* % test arg */ 140 return (argv[1] == NULL || *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 { 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])) { 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 get_int(valsp->u.string, 236 &valsp->u.num); 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(2, NULL); 243 #ifdef SHELL 244 fmtstr(p, 32, "%d", 245 valsp->u.num); 246 #else 247 (void)sprintf(p, 248 "%d", valsp->u.num); 249 #endif 250 valsp->u.string = p; 251 } else if (valsp->type == BOOLEAN) { 252 if (valsp->u.num) 253 valsp->u.string = 254 "true"; 255 else 256 valsp->u.string = ""; 257 } 258 valsp->type = STRING; 259 if (c == OP_FILE && (fs.name == NULL || 260 strcmp(fs.name, valsp->u.string))) { 261 fs.name = valsp->u.string; 262 fs.rcode = 263 stat(valsp->u.string, 264 &fs.stat); 265 } 266 } 267 if (binary < FIRST_BINARY_OP) 268 break; 269 binary = 0; 270 } 271 if (!skipping) 272 expr_operator(opsp->op, valsp, &fs); 273 else if (opsp->op == AND1 || opsp->op == OR1) 274 skipping--; 275 valsp++; /* push value */ 276 opsp++; /* pop operator */ 277 } 278 if (opname == NULL) 279 break; 280 if (opsp == &opstack[0]) 281 overflow(); 282 if (op == AND1 || op == AND2) { 283 op = AND1; 284 if (skipping || expr_is_false(valsp - 1)) 285 skipping++; 286 } 287 if (op == OR1 || op == OR2) { 288 op = OR1; 289 if (skipping || !expr_is_false(valsp - 1)) 290 skipping++; 291 } 292 opsp--; 293 opsp->op = op; 294 opsp->pri = pri; 295 } 296 done: return (expr_is_false(&valstack[0])); 297 } 298 299 static int 300 expr_is_false(val) 301 struct value *val; 302 { 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 ISSYMLINK: 367 i = S_IFLNK; 368 (void)lstat(sp->u.string, &fs->stat); 369 goto filetype; 370 case ISFIFO: 371 i = S_IFIFO; 372 goto filetype; 373 filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 374 true: sp->u.num = 1; 375 else 376 false: sp->u.num = 0; 377 sp->type = BOOLEAN; 378 break; 379 case ISSETUID: 380 i = S_ISUID; 381 goto filebit; 382 case ISSETGID: 383 i = S_ISGID; 384 goto filebit; 385 case ISSTICKY: 386 i = S_ISVTX; 387 filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 388 goto true; 389 goto false; 390 case ISSIZE: 391 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 392 sp->type = INTEGER; 393 break; 394 case ISTTY: 395 sp->u.num = isatty(sp->u.num); 396 sp->type = BOOLEAN; 397 break; 398 case NULSTR: 399 if (sp->u.string[0] == '\0') 400 goto true; 401 goto false; 402 case STRLEN: 403 sp->u.num = strlen(sp->u.string); 404 sp->type = INTEGER; 405 break; 406 case OR1: 407 case AND1: 408 /* 409 * These operators are mostly handled by the parser. If we 410 * get here it means that both operands were evaluated, so 411 * the value is the value of the second operand. 412 */ 413 *sp = *(sp + 1); 414 break; 415 case STREQ: 416 case STRNE: 417 i = 0; 418 if (!strcmp(sp->u.string, (sp + 1)->u.string)) 419 i++; 420 if (op == STRNE) 421 i = 1 - i; 422 sp->u.num = i; 423 sp->type = BOOLEAN; 424 break; 425 case EQ: 426 if (sp->u.num == (sp + 1)->u.num) 427 goto true; 428 goto false; 429 case NE: 430 if (sp->u.num != (sp + 1)->u.num) 431 goto true; 432 goto false; 433 case GT: 434 if (sp->u.num > (sp + 1)->u.num) 435 goto true; 436 goto false; 437 case LT: 438 if (sp->u.num < (sp + 1)->u.num) 439 goto true; 440 goto false; 441 case LE: 442 if (sp->u.num <= (sp + 1)->u.num) 443 goto true; 444 goto false; 445 case GE: 446 if (sp->u.num >= (sp + 1)->u.num) 447 goto true; 448 goto false; 449 450 } 451 } 452 453 static int 454 lookup_op(name, table) 455 char *name; 456 const char *const * table; 457 { 458 const char *const * tp; 459 const char *p; 460 char c; 461 462 c = name[1]; 463 for (tp = table; (p = *tp) != NULL; tp++) 464 if (p[1] == c && !strcmp(p, name)) 465 return (tp - table); 466 return (-1); 467 } 468 469 static int 470 posix_unary_op(argv) 471 char **argv; 472 { 473 struct filestat fs; 474 struct value valp; 475 int op, c; 476 char *opname; 477 478 opname = *argv; 479 if ((op = lookup_op(opname, unary_op)) < 0) 480 return (-1); 481 c = op_argflag[op]; 482 opname = argv[1]; 483 valp.u.string = opname; 484 if (c == OP_FILE) { 485 fs.name = opname; 486 fs.rcode = stat(opname, &fs.stat); 487 } else if (c != OP_STRING) 488 return (-1); 489 490 expr_operator(op, &valp, &fs); 491 return (valp.u.num == 0); 492 } 493 494 static int 495 posix_binary_op(argv) 496 char **argv; 497 { 498 struct value v[2]; 499 int op, c; 500 char *opname; 501 502 opname = argv[1]; 503 if ((op = lookup_op(opname, binary_op)) < 0) 504 return (-1); 505 op += FIRST_BINARY_OP; 506 c = op_argflag[op]; 507 508 if (c == OP_INT) { 509 get_int(argv[0], &v[0].u.num); 510 get_int(argv[2], &v[1].u.num); 511 } else { 512 v[0].u.string = argv[0]; 513 v[1].u.string = argv[2]; 514 } 515 expr_operator(op, v, NULL); 516 return (v[0].u.num == 0); 517 } 518 519 /* 520 * Integer type checking. 521 */ 522 static void 523 get_int(v, lp) 524 char *v; 525 long *lp; 526 { 527 long val; 528 char *ep; 529 530 for (; *v && isspace(*v); ++v); 531 if (isdigit(*v)) { 532 errno = 0; 533 val = strtol(v, &ep, 10); 534 if (*ep != '\0') 535 errx(2, "%s: trailing non-numeric characters", v); 536 if (errno == ERANGE) { 537 if (val == LONG_MIN) 538 errx(2, "%s: underflow", v); 539 if (val == LONG_MAX) 540 errx(2, "%s: overflow", v); 541 } 542 *lp = val; 543 return; 544 } 545 errx(2, "%s: expected integer", v); 546 } 547 548 static void 549 syntax() 550 { 551 552 err(2, "syntax error"); 553 } 554 555 static void 556 overflow() 557 { 558 559 err(2, "expression is too complex"); 560 } 561