1 /*- 2 * Copyright (c) 1992, 1993 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\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.1 (Berkeley) 05/31/93"; 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 *, 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 if (val->type == STRING) { 278 if (val->u.string[0] == '\0') 279 return (1); 280 } else { /* INTEGER or BOOLEAN */ 281 if (val->u.num == 0) 282 return (1); 283 } 284 return (0); 285 } 286 287 288 /* 289 * Execute an operator. Op is the operator. Sp is the stack pointer; 290 * sp[0] refers to the first operand, sp[1] refers to the second operand 291 * (if any), and the result is placed in sp[0]. The operands are converted 292 * to the type expected by the operator before expr_operator is called. 293 * Fs is a pointer to a structure which holds the value of the last call 294 * to stat, to avoid repeated stat calls on the same file. 295 */ 296 static void 297 expr_operator(op, sp, fs) 298 int op; 299 struct value *sp; 300 struct filestat *fs; 301 { 302 int i; 303 304 switch (op) { 305 case NOT: 306 sp->u.num = expr_is_false(sp); 307 sp->type = BOOLEAN; 308 break; 309 case ISEXIST: 310 if (fs == NULL || fs->rcode == -1) 311 goto false; 312 else 313 goto true; 314 case ISREAD: 315 i = S_IROTH; 316 goto permission; 317 case ISWRITE: 318 i = S_IWOTH; 319 goto permission; 320 case ISEXEC: 321 i = S_IXOTH; 322 permission: if (fs->stat.st_uid == geteuid()) 323 i <<= 6; 324 else if (fs->stat.st_gid == getegid()) 325 i <<= 3; 326 goto filebit; /* true if (stat.st_mode & i) != 0 */ 327 case ISFILE: 328 i = S_IFREG; 329 goto filetype; 330 case ISDIR: 331 i = S_IFDIR; 332 goto filetype; 333 case ISCHAR: 334 i = S_IFCHR; 335 goto filetype; 336 case ISBLOCK: 337 i = S_IFBLK; 338 goto filetype; 339 case ISSYMLINK: 340 i = S_IFLNK; 341 (void)lstat(sp->u.string, &fs->stat); 342 goto filetype; 343 case ISFIFO: 344 i = S_IFIFO; 345 goto filetype; 346 filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 347 true: sp->u.num = 1; 348 else 349 false: sp->u.num = 0; 350 sp->type = BOOLEAN; 351 break; 352 case ISSETUID: 353 i = S_ISUID; 354 goto filebit; 355 case ISSETGID: 356 i = S_ISGID; 357 goto filebit; 358 case ISSTICKY: 359 i = S_ISVTX; 360 filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 361 goto true; 362 goto false; 363 case ISSIZE: 364 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 365 sp->type = INTEGER; 366 break; 367 case ISTTY: 368 sp->u.num = isatty(sp->u.num); 369 sp->type = BOOLEAN; 370 break; 371 case NULSTR: 372 if (sp->u.string[0] == '\0') 373 goto true; 374 goto false; 375 case STRLEN: 376 sp->u.num = strlen(sp->u.string); 377 sp->type = INTEGER; 378 break; 379 case OR1: 380 case AND1: 381 /* 382 * These operators are mostly handled by the parser. If we 383 * get here it means that both operands were evaluated, so 384 * the value is the value of the second operand. 385 */ 386 *sp = *(sp + 1); 387 break; 388 case STREQ: 389 case STRNE: 390 i = 0; 391 if (!strcmp(sp->u.string, (sp + 1)->u.string)) 392 i++; 393 if (op == STRNE) 394 i = 1 - i; 395 sp->u.num = i; 396 sp->type = BOOLEAN; 397 break; 398 case EQ: 399 if (sp->u.num == (sp + 1)->u.num) 400 goto true; 401 goto false; 402 case NE: 403 if (sp->u.num != (sp + 1)->u.num) 404 goto true; 405 goto false; 406 case GT: 407 if (sp->u.num > (sp + 1)->u.num) 408 goto true; 409 goto false; 410 case LT: 411 if (sp->u.num < (sp + 1)->u.num) 412 goto true; 413 goto false; 414 case LE: 415 if (sp->u.num <= (sp + 1)->u.num) 416 goto true; 417 goto false; 418 case GE: 419 if (sp->u.num >= (sp + 1)->u.num) 420 goto true; 421 goto false; 422 423 } 424 } 425 426 static int 427 lookup_op(name, table) 428 char *name; 429 char *const * table; 430 { 431 register char *const * tp; 432 register char const *p; 433 char c; 434 435 c = name[1]; 436 for (tp = table; (p = *tp) != NULL; tp++) 437 if (p[1] == c && !strcmp(p, name)) 438 return (tp - table); 439 return (-1); 440 } 441 442 static int 443 posix_unary_op(argv) 444 char **argv; 445 { 446 struct filestat fs; 447 struct value valp; 448 int op, c; 449 char *opname; 450 451 opname = *argv; 452 if ((op = lookup_op(opname, unary_op)) < 0) 453 return (-1); 454 c = op_argflag[op]; 455 opname = argv[1]; 456 valp.u.string = opname; 457 if (c == OP_FILE) { 458 fs.name = opname; 459 fs.rcode = stat(opname, &fs.stat); 460 } else if (c != OP_STRING) 461 return (-1); 462 463 expr_operator(op, &valp, &fs); 464 return (valp.u.num == 0); 465 } 466 467 static int 468 posix_binary_op(argv) 469 char **argv; 470 { 471 struct value v[2]; 472 int op, c; 473 char *opname; 474 475 opname = argv[1]; 476 if ((op = lookup_op(opname, binary_op)) < 0) 477 return (-1); 478 op += FIRST_BINARY_OP; 479 c = op_argflag[op]; 480 481 if (c == OP_INT) { 482 get_int(argv[0], &v[0].u.num); 483 get_int(argv[2], &v[1].u.num); 484 } else { 485 v[0].u.string = argv[0]; 486 v[1].u.string = argv[2]; 487 } 488 expr_operator(op, v, NULL); 489 return (v[0].u.num == 0); 490 } 491 492 /* 493 * Integer type checking. 494 */ 495 static void 496 get_int(v, lp) 497 char *v; 498 long *lp; 499 { 500 long val; 501 char *ep; 502 503 for (; *v && isspace(*v); ++v); 504 if (isdigit(*v)) { 505 errno = 0; 506 val = strtol(v, &ep, 10); 507 if (*ep != '\0') 508 errx(2, "%s: trailing non-numeric characters", v); 509 if (errno == ERANGE) { 510 if (val == LONG_MIN) 511 errx(2, "%s: underflow", v); 512 if (val == LONG_MAX) 513 errx(2, "%s: overflow", v); 514 } 515 *lp = val; 516 return; 517 } 518 errx(2, "%s: expected integer", v); 519 } 520 521 static void 522 syntax() 523 { 524 err(2, "syntax error"); 525 } 526 527 static void 528 overflow() 529 { 530 err(2, "expression is too complex"); 531 } 532