home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / test / test.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-02-20  |  12.8 KB  |  570 lines

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