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.6 (Berkeley) 04/30/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') ? 1 : 0; 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 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) { 478 get_int(argv[0], &v[0].u.num); 479 get_int(argv[2], &v[1].u.num); 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 void 492 get_int(v, lp) 493 char *v; 494 long *lp; 495 { 496 long val; 497 char *ep; 498 499 for (; *v && isspace(*v); ++v); 500 if (isdigit(*v)) { 501 errno = 0; 502 val = strtol(v, &ep, 10); 503 if (*ep != '\0') 504 errx(2, "%s: trailing non-numeric characters", v); 505 if (errno == ERANGE) { 506 if (val == LONG_MIN) 507 errx(2, "%s: underflow", v); 508 if (val == LONG_MAX) 509 errx(2, "%s: overflow", v); 510 } 511 *lp = val; 512 return; 513 } 514 errx(2, "%s: expected integer", v); 515 } 516 517 static void 518 syntax() 519 { 520 err(2, "syntax error"); 521 } 522 523 static void 524 overflow() 525 { 526 err(2, "expression is too complex"); 527 } 528