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