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