test.c revision 4171
164562Sgshapiro/*- 264562Sgshapiro * Copyright (c) 1992, 1993, 1994 364562Sgshapiro * The Regents of the University of California. All rights reserved. 464562Sgshapiro * 564562Sgshapiro * This code is derived from software contributed to Berkeley by 664562Sgshapiro * Kenneth Almquist. 764562Sgshapiro * 864562Sgshapiro * Redistribution and use in source and binary forms, with or without 964562Sgshapiro * modification, are permitted provided that the following conditions 1064562Sgshapiro * are met: 1164562Sgshapiro * 1. Redistributions of source code must retain the above copyright 1271345Sgshapiro * notice, this list of conditions and the following disclaimer. 1364562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1464562Sgshapiro * notice, this list of conditions and the following disclaimer in the 1564562Sgshapiro * documentation and/or other materials provided with the distribution. 1664562Sgshapiro * 3. All advertising materials mentioning features or use of this software 1764562Sgshapiro * must display the following acknowledgement: 1864562Sgshapiro * This product includes software developed by the University of 1964562Sgshapiro * California, Berkeley and its contributors. 2064562Sgshapiro * 4. Neither the name of the University nor the names of its contributors 2164562Sgshapiro * may be used to endorse or promote products derived from this software 2264562Sgshapiro * without specific prior written permission. 2364562Sgshapiro * 2464562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2564562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2664562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2764562Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2864562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2964562Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3071345Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3171345Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3271345Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3364562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3464562Sgshapiro * SUCH DAMAGE. 3564562Sgshapiro * 3664562Sgshapiro * $Id: test.c,v 1.8 1994/11/05 20:24:49 ache Exp $ 3764562Sgshapiro */ 3864562Sgshapiro 3964562Sgshapiro#ifndef lint 4064562Sgshapirostatic char copyright[] = 4171345Sgshapiro"@(#) Copyright (c) 1992, 1993, 1994\n\ 4271345Sgshapiro The Regents of the University of California. All rights reserved.\n"; 4371345Sgshapiro#endif /* not lint */ 4464562Sgshapiro 4564562Sgshapiro#ifndef lint 4671345Sgshapirostatic char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94"; 4771345Sgshapiro#endif /* not lint */ 4871345Sgshapiro 4971345Sgshapiro#include <sys/types.h> 5071345Sgshapiro#include <sys/stat.h> 5171345Sgshapiro#include <sys/param.h> 5271345Sgshapiro 5371345Sgshapiro#include <ctype.h> 5464562Sgshapiro#include <err.h> 5571345Sgshapiro#include <errno.h> 5664562Sgshapiro#include <limits.h> 5771345Sgshapiro#include <stdio.h> 5871345Sgshapiro#include <stdlib.h> 5971345Sgshapiro#include <string.h> 6071345Sgshapiro#include <unistd.h> 6171345Sgshapiro 6271345Sgshapiro#include "operators.h" 6371345Sgshapiro 6471345Sgshapiro#define STACKSIZE 12 6571345Sgshapiro#define NESTINCR 16 6671345Sgshapiro 6771345Sgshapiro/* data types */ 6864562Sgshapiro#define STRING 0 6964562Sgshapiro#define INTEGER 1 7064562Sgshapiro#define BOOLEAN 2 7164562Sgshapiro 7271345Sgshapiro#define IS_BANG(s) (s[0] == '!' && s[1] == '\0') 7371345Sgshapiro 7471345Sgshapiro/* 7571345Sgshapiro * This structure hold a value. The type keyword specifies the type of 7671345Sgshapiro * the value, and the union u holds the value. The value of a boolean 7771345Sgshapiro * is stored in u.num (1 = TRUE, 0 = FALSE). 7871345Sgshapiro */ 7971345Sgshapirostruct value { 8071345Sgshapiro int type; 8171345Sgshapiro union { 8271345Sgshapiro char *string; 8371345Sgshapiro long num; 8471345Sgshapiro } u; 8571345Sgshapiro}; 8671345Sgshapiro 8771345Sgshapirostruct operator { 8871345Sgshapiro short op; /* Which operator. */ 8964562Sgshapiro short pri; /* Priority of operator. */ 9071345Sgshapiro}; 9171345Sgshapiro 9271345Sgshapirostruct filestat { 9371345Sgshapiro char *name; /* Name of file. */ 9471345Sgshapiro int rcode; /* Return code from stat. */ 9571345Sgshapiro struct stat stat; /* Status info on file. */ 9664562Sgshapiro}; 9764562Sgshapiro 9864562Sgshapirostatic int expr_is_false __P((struct value *)); 9964562Sgshapirostatic void expr_operator __P((int, struct value *, struct filestat *)); 10064562Sgshapirostatic void get_int __P((char *, long *)); 10164562Sgshapirostatic int lookup_op __P((char *, const char *const *)); 10264562Sgshapirostatic void overflow __P((void)); 10364562Sgshapirostatic int posix_binary_op __P((char **)); 10464562Sgshapirostatic int posix_unary_op __P((char **)); 10564562Sgshapirostatic void syntax __P((void)); 10664562Sgshapiro 10764562Sgshapiroint 10864562Sgshapiromain(argc, argv) 10964562Sgshapiro int argc; 11064562Sgshapiro char *argv[]; 11164562Sgshapiro{ 11264562Sgshapiro struct operator opstack[STACKSIZE]; 11364562Sgshapiro struct operator *opsp; 11464562Sgshapiro struct value valstack[STACKSIZE + 1]; 11564562Sgshapiro struct value *valsp; 11664562Sgshapiro struct filestat fs; 11764562Sgshapiro char c, **ap, *opname, *p; 11871345Sgshapiro int binary, nest, op, pri, ret_val, skipping; 11964562Sgshapiro 12064562Sgshapiro if ((p = argv[0]) == NULL) 12164562Sgshapiro errx(2, "test: argc is zero"); 12264562Sgshapiro 12364562Sgshapiro if (*p != '\0' && p[strlen(p) - 1] == '[') { 12464562Sgshapiro if (strcmp(argv[--argc], "]")) 12564562Sgshapiro errx(2, "missing ]"); 12664562Sgshapiro argv[argc] = NULL; 12764562Sgshapiro } 12864562Sgshapiro ap = argv + 1; 12964562Sgshapiro fs.name = NULL; 13064562Sgshapiro 13164562Sgshapiro /* 13264562Sgshapiro * Test(1) implements an inherently ambiguous grammer. In order to 13364562Sgshapiro * assure some degree of consistency, we special case the POSIX 1003.2 13464562Sgshapiro * requirements to assure correct evaluation for POSIX scripts. The 13564562Sgshapiro * following special cases comply with POSIX P1003.2/D11.2 Section 13664562Sgshapiro * 4.62.4. 13764562Sgshapiro */ 13864562Sgshapiro switch(argc - 1) { 13964562Sgshapiro case 0: /* % test */ 14064562Sgshapiro return (1); 14164562Sgshapiro break; 14264562Sgshapiro case 1: /* % test arg */ 14364562Sgshapiro return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; 14464562Sgshapiro break; 14564562Sgshapiro case 2: /* % test op arg */ 14664562Sgshapiro opname = argv[1]; 14764562Sgshapiro if (IS_BANG(opname)) 14864562Sgshapiro return (*argv[2] == '\0') ? 0 : 1; 14964562Sgshapiro else { 15064562Sgshapiro ret_val = posix_unary_op(&argv[1]); 15164562Sgshapiro if (ret_val >= 0) 15264562Sgshapiro return (ret_val); 15364562Sgshapiro } 15464562Sgshapiro break; 15564562Sgshapiro case 3: /* % test arg1 op arg2 */ 15664562Sgshapiro if (IS_BANG(argv[1])) { 15764562Sgshapiro ret_val = posix_unary_op(&argv[1]); 15864562Sgshapiro if (ret_val >= 0) 15964562Sgshapiro return (!ret_val); 16064562Sgshapiro } else { 16164562Sgshapiro ret_val = posix_binary_op(&argv[1]); 16264562Sgshapiro if (ret_val >= 0) 16364562Sgshapiro return (ret_val); 16464562Sgshapiro } 16564562Sgshapiro break; 16664562Sgshapiro case 4: /* % test ! arg1 op arg2 */ 16764562Sgshapiro if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) { 16864562Sgshapiro ret_val = posix_binary_op(&argv[2]); 16964562Sgshapiro if (ret_val >= 0) 17064562Sgshapiro return (!ret_val); 17164562Sgshapiro } 17264562Sgshapiro break; 17364562Sgshapiro default: 17464562Sgshapiro break; 17564562Sgshapiro } 17664562Sgshapiro 17764562Sgshapiro /* 17864562Sgshapiro * We use operator precedence parsing, evaluating the expression as 17964562Sgshapiro * we parse it. Parentheses are handled by bumping up the priority 18064562Sgshapiro * of operators using the variable "nest." We use the variable 18164562Sgshapiro * "skipping" to turn off evaluation temporarily for the short 18264562Sgshapiro * circuit boolean operators. (It is important do the short circuit 18364562Sgshapiro * evaluation because under NFS a stat operation can take infinitely 18464562Sgshapiro * long.) 18564562Sgshapiro */ 18664562Sgshapiro opsp = opstack + STACKSIZE; 18764562Sgshapiro valsp = valstack; 18864562Sgshapiro nest = skipping = 0; 18964562Sgshapiro if (*ap == NULL) { 19064562Sgshapiro valstack[0].type = BOOLEAN; 19164562Sgshapiro valstack[0].u.num = 0; 19264562Sgshapiro goto done; 19364562Sgshapiro } 19464562Sgshapiro for (;;) { 19564562Sgshapiro opname = *ap++; 19664562Sgshapiro if (opname == NULL) 19764562Sgshapiro syntax(); 19864562Sgshapiro if (opname[0] == '(' && opname[1] == '\0') { 19964562Sgshapiro nest += NESTINCR; 20064562Sgshapiro continue; 20164562Sgshapiro } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { 20264562Sgshapiro if (opsp == &opstack[0]) 20364562Sgshapiro overflow(); 20464562Sgshapiro --opsp; 20564562Sgshapiro opsp->op = op; 20664562Sgshapiro opsp->pri = op_priority[op] + nest; 20764562Sgshapiro continue; 20864562Sgshapiro } else { 20964562Sgshapiro valsp->type = STRING; 21064562Sgshapiro valsp->u.string = opname; 21164562Sgshapiro valsp++; 21264562Sgshapiro } 21364562Sgshapiro for (;;) { 21464562Sgshapiro opname = *ap++; 21564562Sgshapiro if (opname == NULL) { 21664562Sgshapiro if (nest != 0) 21764562Sgshapiro syntax(); 21864562Sgshapiro pri = 0; 21964562Sgshapiro break; 22064562Sgshapiro } 22164562Sgshapiro if (opname[0] != ')' || opname[1] != '\0') { 22264562Sgshapiro if ((op = lookup_op(opname, binary_op)) < 0) 22364562Sgshapiro syntax(); 22464562Sgshapiro op += FIRST_BINARY_OP; 22564562Sgshapiro pri = op_priority[op] + nest; 22664562Sgshapiro break; 22764562Sgshapiro } 22864562Sgshapiro if ((nest -= NESTINCR) < 0) 22964562Sgshapiro syntax(); 23064562Sgshapiro } 23164562Sgshapiro while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { 23264562Sgshapiro binary = opsp->op; 23364562Sgshapiro for (;;) { 23464562Sgshapiro valsp--; 23564562Sgshapiro c = op_argflag[opsp->op]; 23664562Sgshapiro if (c == OP_INT) { 23764562Sgshapiro if (valsp->type == STRING) 23864562Sgshapiro get_int(valsp->u.string, 23964562Sgshapiro &valsp->u.num); 24064562Sgshapiro valsp->type = INTEGER; 24164562Sgshapiro } else if (c >= OP_STRING) { 24264562Sgshapiro /* OP_STRING or OP_FILE */ 24364562Sgshapiro if (valsp->type == INTEGER) { 24464562Sgshapiro if ((p = malloc(32)) == NULL) 24564562Sgshapiro err(2, NULL); 24664562Sgshapiro#ifdef SHELL 24764562Sgshapiro fmtstr(p, 32, "%d", 24864562Sgshapiro valsp->u.num); 24964562Sgshapiro#else 25064562Sgshapiro (void)sprintf(p, 25164562Sgshapiro "%d", valsp->u.num); 25264562Sgshapiro#endif 25364562Sgshapiro valsp->u.string = p; 25464562Sgshapiro } else if (valsp->type == BOOLEAN) { 25564562Sgshapiro if (valsp->u.num) 25664562Sgshapiro valsp->u.string = 25764562Sgshapiro "true"; 25864562Sgshapiro else 25964562Sgshapiro valsp->u.string = ""; 26064562Sgshapiro } 26164562Sgshapiro valsp->type = STRING; 26264562Sgshapiro if (c == OP_FILE && (fs.name == NULL || 26364562Sgshapiro strcmp(fs.name, valsp->u.string))) { 26464562Sgshapiro fs.name = valsp->u.string; 26564562Sgshapiro fs.rcode = 26664562Sgshapiro stat(valsp->u.string, 26764562Sgshapiro &fs.stat); 26864562Sgshapiro } 26964562Sgshapiro } 27064562Sgshapiro if (binary < FIRST_BINARY_OP) 27164562Sgshapiro break; 27264562Sgshapiro binary = 0; 27364562Sgshapiro } 27464562Sgshapiro if (!skipping) 27564562Sgshapiro expr_operator(opsp->op, valsp, &fs); 27664562Sgshapiro else if (opsp->op == AND1 || opsp->op == OR1) 27764562Sgshapiro skipping--; 27864562Sgshapiro valsp++; /* push value */ 27964562Sgshapiro opsp++; /* pop operator */ 28064562Sgshapiro } 28164562Sgshapiro if (opname == NULL) 28264562Sgshapiro break; 28364562Sgshapiro if (opsp == &opstack[0]) 28464562Sgshapiro overflow(); 28564562Sgshapiro if (op == AND1 || op == AND2) { 28664562Sgshapiro op = AND1; 28764562Sgshapiro if (skipping || expr_is_false(valsp - 1)) 28864562Sgshapiro skipping++; 28964562Sgshapiro } 29064562Sgshapiro if (op == OR1 || op == OR2) { 29164562Sgshapiro op = OR1; 29264562Sgshapiro if (skipping || !expr_is_false(valsp - 1)) 29364562Sgshapiro skipping++; 29464562Sgshapiro } 29564562Sgshapiro opsp--; 29664562Sgshapiro opsp->op = op; 29764562Sgshapiro opsp->pri = pri; 29864562Sgshapiro } 29964562Sgshapirodone: return (expr_is_false(&valstack[0])); 30064562Sgshapiro} 30164562Sgshapiro 30264562Sgshapirostatic int 30364562Sgshapiroexpr_is_false(val) 30464562Sgshapiro struct value *val; 30564562Sgshapiro{ 30664562Sgshapiro 30764562Sgshapiro if (val->type == STRING) { 30864562Sgshapiro if (val->u.string[0] == '\0') 30964562Sgshapiro return (1); 31064562Sgshapiro } else { /* INTEGER or BOOLEAN */ 31164562Sgshapiro if (val->u.num == 0) 31264562Sgshapiro return (1); 31364562Sgshapiro } 31464562Sgshapiro return (0); 31564562Sgshapiro} 31664562Sgshapiro 31764562Sgshapiro 31864562Sgshapiro/* 31964562Sgshapiro * Execute an operator. Op is the operator. Sp is the stack pointer; 32064562Sgshapiro * sp[0] refers to the first operand, sp[1] refers to the second operand 32164562Sgshapiro * (if any), and the result is placed in sp[0]. The operands are converted 32264562Sgshapiro * to the type expected by the operator before expr_operator is called. 32364562Sgshapiro * Fs is a pointer to a structure which holds the value of the last call 32464562Sgshapiro * to stat, to avoid repeated stat calls on the same file. 32564562Sgshapiro */ 32664562Sgshapirostatic void 32764562Sgshapiroexpr_operator(op, sp, fs) 32864562Sgshapiro int op; 32964562Sgshapiro struct value *sp; 33064562Sgshapiro struct filestat *fs; 33164562Sgshapiro{ 33266494Sgshapiro int i; 33366494Sgshapiro 33464562Sgshapiro switch (op) { 33564562Sgshapiro case NOT: 33664562Sgshapiro sp->u.num = expr_is_false(sp); 33764562Sgshapiro sp->type = BOOLEAN; 33864562Sgshapiro break; 33964562Sgshapiro case ISEXIST: 34064562Sgshapiroexist: 34164562Sgshapiro if (fs == NULL || fs->rcode == -1) 34264562Sgshapiro goto false; 34364562Sgshapiro else 34464562Sgshapiro goto true; 34564562Sgshapiro case ISREAD: 34664562Sgshapiro if (geteuid() == 0) 34764562Sgshapiro goto exist; 34864562Sgshapiro i = S_IROTH; 34964562Sgshapiro goto permission; 35064562Sgshapiro case ISWRITE: 35164562Sgshapiro if (geteuid() != 0) 35264562Sgshapiro i = S_IWOTH; 35364562Sgshapiro else { 35464562Sgshapiro i = S_IWOTH|S_IWGRP|S_IWUSR; 35564562Sgshapiro goto filebit; 35664562Sgshapiro } 35764562Sgshapiro goto permission; 35864562Sgshapiro case ISEXEC: 35964562Sgshapiro if (geteuid() != 0) { 36066494Sgshapiro i = S_IXOTH; 36166494Sgshapiropermission: if (fs->stat.st_uid == geteuid()) 36266494Sgshapiro i <<= 6; 36366494Sgshapiro else { 36466494Sgshapiro gid_t grlist[NGROUPS]; 36566494Sgshapiro int ngroups, j; 36666494Sgshapiro 36766494Sgshapiro ngroups = getgroups(NGROUPS, grlist); 36866494Sgshapiro for (j = 0; j < ngroups; j++) 36964562Sgshapiro if (fs->stat.st_gid == grlist[j]) { 37064562Sgshapiro i <<= 3; 37164562Sgshapiro goto filebit; 37264562Sgshapiro } 37364562Sgshapiro } 37464562Sgshapiro } else 37564562Sgshapiro i = S_IXOTH|S_IXGRP|S_IXUSR; 37664562Sgshapiro goto filebit; /* true if (stat.st_mode & i) != 0 */ 37764562Sgshapiro case ISFILE: 37864562Sgshapiro i = S_IFREG; 37964562Sgshapiro goto filetype; 38064562Sgshapiro case ISDIR: 38164562Sgshapiro i = S_IFDIR; 38264562Sgshapiro goto filetype; 38364562Sgshapiro case ISCHAR: 38464562Sgshapiro i = S_IFCHR; 38564562Sgshapiro goto filetype; 38664562Sgshapiro case ISBLOCK: 38764562Sgshapiro i = S_IFBLK; 38864562Sgshapiro goto filetype; 38964562Sgshapiro case ISSYMLINK: 39064562Sgshapiro i = S_IFLNK; 39164562Sgshapiro (void)lstat(sp->u.string, &fs->stat); 39264562Sgshapiro goto filetype; 39364562Sgshapiro case ISFIFO: 39464562Sgshapiro i = S_IFIFO; 39564562Sgshapiro goto filetype; 39664562Sgshapirofiletype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) 39764562Sgshapirotrue: sp->u.num = 1; 39864562Sgshapiro else 39964562Sgshapirofalse: sp->u.num = 0; 40064562Sgshapiro sp->type = BOOLEAN; 40164562Sgshapiro break; 40264562Sgshapiro case ISSETUID: 40364562Sgshapiro i = S_ISUID; 40464562Sgshapiro goto filebit; 40564562Sgshapiro case ISSETGID: 40664562Sgshapiro i = S_ISGID; 40764562Sgshapiro goto filebit; 40864562Sgshapiro case ISSTICKY: 409 i = S_ISVTX; 410filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) 411 goto true; 412 goto false; 413 case ISSIZE: 414 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; 415 sp->type = INTEGER; 416 break; 417 case ISTTY: 418 sp->u.num = isatty(sp->u.num); 419 sp->type = BOOLEAN; 420 break; 421 case NULSTR: 422 if (sp->u.string[0] == '\0') 423 goto true; 424 goto false; 425 case STRLEN: 426 sp->u.num = strlen(sp->u.string); 427 sp->type = INTEGER; 428 break; 429 case OR1: 430 case AND1: 431 /* 432 * These operators are mostly handled by the parser. If we 433 * get here it means that both operands were evaluated, so 434 * the value is the value of the second operand. 435 */ 436 *sp = *(sp + 1); 437 break; 438 case STREQ: 439 case STRNE: 440 i = 0; 441 if (!strcmp(sp->u.string, (sp + 1)->u.string)) 442 i++; 443 if (op == STRNE) 444 i = 1 - i; 445 sp->u.num = i; 446 sp->type = BOOLEAN; 447 break; 448 case EQ: 449 if (sp->u.num == (sp + 1)->u.num) 450 goto true; 451 goto false; 452 case NE: 453 if (sp->u.num != (sp + 1)->u.num) 454 goto true; 455 goto false; 456 case GT: 457 if (sp->u.num > (sp + 1)->u.num) 458 goto true; 459 goto false; 460 case LT: 461 if (sp->u.num < (sp + 1)->u.num) 462 goto true; 463 goto false; 464 case LE: 465 if (sp->u.num <= (sp + 1)->u.num) 466 goto true; 467 goto false; 468 case GE: 469 if (sp->u.num >= (sp + 1)->u.num) 470 goto true; 471 goto false; 472 473 } 474} 475 476static int 477lookup_op(name, table) 478 char *name; 479 const char *const * table; 480{ 481 const char *const * tp; 482 const char *p; 483 char c; 484 485 c = name[1]; 486 for (tp = table; (p = *tp) != NULL; tp++) 487 if (p[1] == c && !strcmp(p, name)) 488 return (tp - table); 489 return (-1); 490} 491 492static int 493posix_unary_op(argv) 494 char **argv; 495{ 496 struct filestat fs; 497 struct value valp; 498 int op, c; 499 char *opname; 500 501 opname = *argv; 502 if ((op = lookup_op(opname, unary_op)) < 0) 503 return (-1); 504 c = op_argflag[op]; 505 opname = argv[1]; 506 valp.u.string = opname; 507 if (c == OP_FILE) { 508 fs.name = opname; 509 fs.rcode = stat(opname, &fs.stat); 510 } else if (c != OP_STRING) 511 return (-1); 512 513 expr_operator(op, &valp, &fs); 514 return (valp.u.num == 0); 515} 516 517static int 518posix_binary_op(argv) 519 char **argv; 520{ 521 struct value v[2]; 522 int op, c; 523 char *opname; 524 525 opname = argv[1]; 526 if ((op = lookup_op(opname, binary_op)) < 0) 527 return (-1); 528 op += FIRST_BINARY_OP; 529 c = op_argflag[op]; 530 531 if (c == OP_INT) { 532 get_int(argv[0], &v[0].u.num); 533 get_int(argv[2], &v[1].u.num); 534 } else { 535 v[0].u.string = argv[0]; 536 v[1].u.string = argv[2]; 537 } 538 expr_operator(op, v, NULL); 539 return (v[0].u.num == 0); 540} 541 542/* 543 * Integer type checking. 544 */ 545static void 546get_int(v, lp) 547 char *v; 548 long *lp; 549{ 550 long val; 551 char *ep; 552 553 for (; *v && isspace(*v); ++v); 554 555 if(!*v) { 556 *lp = 0; 557 return; 558 } 559 560 if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) { 561 errno = 0; 562 val = strtol(v, &ep, 10); 563 if (*ep != '\0') 564 errx(2, "%s: trailing non-numeric characters", v); 565 if (errno == ERANGE) { 566 if (val == LONG_MIN) 567 errx(2, "%s: underflow", v); 568 if (val == LONG_MAX) 569 errx(2, "%s: overflow", v); 570 } 571 *lp = val; 572 return; 573 } 574 errx(2, "%s: expected integer", v); 575} 576 577static void 578syntax() 579{ 580 581 err(2, "syntax error"); 582} 583 584static void 585overflow() 586{ 587 588 err(2, "expression is too complex"); 589} 590