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