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