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.2 (Berkeley) 08/29/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 void err __P((const char *, ...)); 65 static int expr_is_false __P((struct value *)); 66 static void expr_operator __P((int, struct value *, struct filestat *)); 67 static int int_tcheck __P((char *)); 68 static int lookup_op __P((char *, char *const *)); 69 static void overflow __P((void)); 70 static int posix_binary_op __P((char **)); 71 static int posix_unary_op __P((char **)); 72 static void syntax __P((void)); 73 74 int 75 main(argc, argv) 76 int argc; 77 char *argv[]; 78 { 79 struct operator opstack[STACKSIZE]; 80 struct operator *opsp; 81 struct value valstack[STACKSIZE + 1]; 82 struct value *valsp; 83 struct filestat fs; 84 char c, **ap, *opname, *p; 85 int binary, nest, op, pri, ret_val, skipping; 86 87 if (argv[0] == NULL) { 88 err("test: argc is zero.\n"); 89 exit(2); 90 } 91 92 if (**argv == '[') { 93 if (strcmp(argv[argc - 1], "]")) 94 err("missing ]"); 95 argv[argc - 1] = NULL; 96 } 97 ap = argv + 1; 98 fs.name = NULL; 99 100 /* 101 * Test(1) implements an inherently ambiguous grammer. In order to 102 * assure some degree of consistency, we special case the POSIX 1003.2 103 * requirements to assure correct evaluation for POSIX scripts. The 104 * following special cases comply with POSIX P1003.2/D11.2 Section 105 * 4.62.4. 106 */ 107 switch(argc - 1) { 108 case 0: /* % test */ 109 return (1); 110 break; 111 case 1: /* % test arg */ 112 return (*argv[1] == '\0') ? 1 : 0; 113 break; 114 case 2: /* % test op arg */ 115 opname = argv[1]; 116 if (IS_BANG(opname)) 117 return (*argv[2] == '\0') ? 1 : 0; 118 else { 119 ret_val = posix_unary_op(&argv[1]); 120 if (ret_val >= 0) 121 return (ret_val); 122 } 123 break; 124 case 3: /* % test arg1 op arg2 */ 125 if (IS_BANG(argv[1])) { 126 ret_val = posix_unary_op(&argv[1]); 127 if (ret_val >= 0) 128 return (!ret_val); 129 } else { 130 ret_val = posix_binary_op(&argv[1]); 131 if (ret_val >= 0) 132 return (ret_val); 133 } 134 break; 135 case 4: /* % test ! arg1 op arg2 */ 136 if (IS_BANG(argv[1])) { 137 ret_val = posix_binary_op(&argv[2]); 138 if (ret_val >= 0) 139 return (!ret_val); 140 } 141 break; 142 default: 143 break; 144 } 145 146 /* 147 * We use operator precedence parsing, evaluating the expression as 148 * we parse it. Parentheses are handled by bumping up the priority 149 * of operators using the variable "nest." We use the variable 150 * "skipping" to turn off evaluation temporarily for the short 151 * circuit boolean operators. (It is important do the short circuit 152 * evaluation because under NFS a stat operation can take infinitely 153 * long.) 154 */ 155 opsp = opstack + STACKSIZE; 156 valsp = valstack; 157 nest = skipping = 0; 158 if (*ap == NULL) { 159 valstack[0].type = BOOLEAN; 160 valstack[0].u.num = 0; 161 goto done; 162 } 163 for (;;) { 164 opname = *ap++; 165 if (opname == NULL) 166 syntax(); 167 if (opname[0] == '(' && opname[1] == '\0') { 168 nest += NESTINCR; 169 continue; 170 } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 171 if (opsp == &opstack[0]) 172 overflow(); 173 --opsp; 174 opsp->op = op; 175 opsp->pri = op_priority[op] + nest; 176 continue; 177 } else { 178 valsp->type = STRING; 179 valsp->u.string = opname; 180 valsp++; 181 } 182 for (;;) { 183 opname = *ap++; 184 if (opname == NULL) { 185 if (nest != 0) 186 syntax(); 187 pri = 0; 188 break; 189 } 190 if (opname[0] != ')' || opname[1] != '\0') { 191 if ((op = lookup_op(opname, binary_op)) < 0) 192 syntax(); 193 op += FIRST_BINARY_OP; 194 pri = op_priority[op] + nest; 195 break; 196 } 197 if ((nest -= NESTINCR) < 0) 198 syntax(); 199 } 200 while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 201 binary = opsp->op; 202 for (;;) { 203 valsp--; 204 c = op_argflag[opsp->op]; 205 if (c == OP_INT) { 206 if (valsp->type == STRING && 207 int_tcheck(valsp->u.string)) 208 valsp->u.num = 209 atol(valsp->u.string); 210 valsp->type = INTEGER; 211 } else if (c >= OP_STRING) { 212 /* OP_STRING or OP_FILE */ 213 if (valsp->type == INTEGER) { 214 if ((p = malloc(32)) == NULL) 215 err("%s", 216 strerror(errno)); 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 ISFIFO: 340 i = S_IFIFO; 341 goto filetype; 342 filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 343 true: sp->u.num = 1; 344 else 345 false: sp->u.num = 0; 346 sp->type = BOOLEAN; 347 break; 348 case ISSETUID: 349 i = S_ISUID; 350 goto filebit; 351 case ISSETGID: 352 i = S_ISGID; 353 goto filebit; 354 case ISSTICKY: 355 i = S_ISVTX; 356 filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 357 goto true; 358 goto false; 359 case ISSIZE: 360 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 361 sp->type = INTEGER; 362 break; 363 case ISTTY: 364 sp->u.num = isatty(sp->u.num); 365 sp->type = BOOLEAN; 366 break; 367 case NULSTR: 368 if (sp->u.string[0] == '\0') 369 goto true; 370 goto false; 371 case STRLEN: 372 sp->u.num = strlen(sp->u.string); 373 sp->type = INTEGER; 374 break; 375 case OR1: 376 case AND1: 377 /* 378 * These operators are mostly handled by the parser. If we 379 * get here it means that both operands were evaluated, so 380 * the value is the value of the second operand. 381 */ 382 *sp = *(sp + 1); 383 break; 384 case STREQ: 385 case STRNE: 386 i = 0; 387 if (!strcmp(sp->u.string, (sp + 1)->u.string)) 388 i++; 389 if (op == STRNE) 390 i = 1 - i; 391 sp->u.num = i; 392 sp->type = BOOLEAN; 393 break; 394 case EQ: 395 if (sp->u.num == (sp + 1)->u.num) 396 goto true; 397 goto false; 398 case NE: 399 if (sp->u.num != (sp + 1)->u.num) 400 goto true; 401 goto false; 402 case GT: 403 if (sp->u.num > (sp + 1)->u.num) 404 goto true; 405 goto false; 406 case LT: 407 if (sp->u.num < (sp + 1)->u.num) 408 goto true; 409 goto false; 410 case LE: 411 if (sp->u.num <= (sp + 1)->u.num) 412 goto true; 413 goto false; 414 case GE: 415 if (sp->u.num >= (sp + 1)->u.num) 416 goto true; 417 goto false; 418 419 } 420 } 421 422 static int 423 lookup_op(name, table) 424 char *name; 425 char *const * table; 426 { 427 register char *const * tp; 428 register char const *p; 429 char c; 430 431 c = name[1]; 432 for (tp = table; (p = *tp) != NULL; tp++) 433 if (p[1] == c && !strcmp(p, name)) 434 return (tp - table); 435 return (-1); 436 } 437 438 static int 439 posix_unary_op(argv) 440 char **argv; 441 { 442 struct filestat fs; 443 struct value valp; 444 int op, c; 445 char *opname; 446 447 opname = *argv; 448 if ((op = lookup_op(opname, unary_op)) < 0) 449 return (-1); 450 c = op_argflag[op]; 451 opname = argv[1]; 452 valp.u.string = opname; 453 if (c == OP_FILE) { 454 fs.name = opname; 455 fs.rcode = stat(opname, &fs.stat); 456 } else if (c != OP_STRING) 457 return (-1); 458 459 expr_operator(op, &valp, &fs); 460 return (valp.u.num == 0); 461 } 462 463 static int 464 posix_binary_op(argv) 465 char **argv; 466 { 467 struct value v[2]; 468 int op, c; 469 char *opname; 470 471 opname = argv[1]; 472 if ((op = lookup_op(opname, binary_op)) < 0) 473 return (-1); 474 op += FIRST_BINARY_OP; 475 c = op_argflag[op]; 476 477 if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) { 478 v[0].u.num = atol(argv[0]); 479 v[1].u.num = atol(argv[2]); 480 } else { 481 v[0].u.string = argv[0]; 482 v[1].u.string = argv[2]; 483 } 484 expr_operator(op, v, NULL); 485 return (v[0].u.num == 0); 486 } 487 488 /* 489 * Integer type checking. 490 */ 491 static int 492 int_tcheck(v) 493 char *v; 494 { 495 char *p; 496 497 for (p = v; *p != '\0'; p++) 498 if (!isdigit(*p)) 499 err("illegal operand \"%s\" -- expected integer.", v); 500 return (1); 501 } 502 503 static void 504 syntax() 505 { 506 err("syntax error"); 507 } 508 509 static void 510 overflow() 511 { 512 err("expression is too complex"); 513 } 514 515 #if __STDC__ 516 #include <stdarg.h> 517 #else 518 #include <varargs.h> 519 #endif 520 521 void 522 #if __STDC__ 523 err(const char *fmt, ...) 524 #else 525 err(fmt, va_alist) 526 char *fmt; 527 va_dcl 528 #endif 529 { 530 va_list ap; 531 #if __STDC__ 532 va_start(ap, fmt); 533 #else 534 va_start(ap); 535 #endif 536 (void)fprintf(stderr, "test: "); 537 (void)vfprintf(stderr, fmt, ap); 538 va_end(ap); 539 (void)fprintf(stderr, "\n"); 540 exit(2); 541 /* NOTREACHED */ 542 } 543