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