expr.c

Go to the documentation of this file.
00001 /*
00002  * The expr and test commands.
00003  *
00004  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
00005  * This file is part of ash, which is distributed under the terms specified
00006  * by the Ash General Public License.  See the file named LICENSE.
00007  */
00008 
00009 
00010 #define main exprcmd
00011 
00012 #include "bltin.h"
00013 #include "operators.h"
00014 #include <sys/types.h>
00015 #include <sys/stat.h>
00016 
00017 #ifndef S_ISLNK
00018 #define lstat           stat
00019 #define S_ISLNK(mode)   (0)
00020 #endif
00021 
00022 #define STACKSIZE 12
00023 #define NESTINCR 16
00024 
00025 /* data types */
00026 #define STRING 0
00027 #define INTEGER 1
00028 #define BOOLEAN 2
00029 
00030 
00031 /*
00032  * This structure hold a value.  The type keyword specifies the type of
00033  * the value, and the union u holds the value.  The value of a boolean
00034  * is stored in u.num (1 = TRUE, 0 = FALSE).
00035  */
00036 
00037 struct value {
00038       int type;
00039       union {
00040             char *string;
00041             long num;
00042       } u;
00043 };
00044 
00045 
00046 struct operator {
00047       short op;                 /* which operator */
00048       short pri;                /* priority of operator */
00049 };
00050 
00051 
00052 struct filestat {
00053       int op;                   /* OP_FILE or OP_LFILE */
00054       char *name;               /* name of file */
00055       int rcode;                /* return code from stat */
00056       struct stat stat;         /* status info on file */
00057 };
00058 
00059 
00060 extern char *match_begin[10];   /* matched string */
00061 extern short match_length[10];  /* defined in regexp.c */
00062 extern short number_parens;     /* number of \( \) pairs */
00063 
00064 
00065 #ifdef __STDC__
00066 int expr_is_false(struct value *);
00067 void expr_operator(int, struct value *, struct filestat *);
00068 int lookup_op(char *, char *const*);
00069 char *re_compile(char *);       /* defined in regexp.c */
00070 int re_match(char *, char *);   /* defined in regexp.c */
00071 long atol(const char *);
00072 #else
00073 int expr_is_false();
00074 void expr_operator();
00075 int lookup_op();
00076 char *re_compile();     /* defined in regexp.c */
00077 int re_match(); /* defined in regexp.c */
00078 long atol();
00079 #endif
00080 
00081 
00082 
00083 main(argc, argv)  char **argv; {
00084       char **ap;
00085       char *opname;
00086       char c;
00087       char *p;
00088       int print;
00089       int nest;         /* parenthises nesting */
00090       int op;
00091       int pri;
00092       int skipping;
00093       int binary;
00094       struct operator opstack[STACKSIZE];
00095       struct operator *opsp;
00096       struct value valstack[STACKSIZE + 1];
00097       struct value *valsp;
00098       struct filestat fs;
00099 
00100       INITARGS(argv);
00101       c = **argv;
00102       print = 1;
00103       if (c == 't')
00104             print = 0;
00105       else if (c == '[') {
00106             if (! equal(argv[argc - 1], "]"))
00107                   error("missing ]");
00108             argv[argc - 1] = NULL;
00109             print = 0;
00110       }
00111       ap = argv + 1;
00112       fs.name = NULL;
00113 
00114       /*
00115        * We use operator precedence parsing, evaluating the expression
00116        * as we parse it.  Parentheses are handled by bumping up the
00117        * priority of operators using the variable "nest."  We use the
00118        * variable "skipping" to turn off evaluation temporarily for the
00119        * short circuit boolean operators.  (It is important do the short
00120        * circuit evaluation because under NFS a stat operation can take
00121        * infinitely long.)
00122        */
00123 
00124       nest = 0;
00125       skipping = 0;
00126       opsp = opstack + STACKSIZE;
00127       valsp = valstack;
00128       if (*ap == NULL) {
00129             valstack[0].type = BOOLEAN;
00130             valstack[0].u.num = 0;
00131             goto done;
00132       }
00133       for (;;) {
00134             opname = *ap++;
00135             if (opname == NULL)
00136 syntax:           error("syntax error");
00137             if (opname[0] == '(' && opname[1] == '\0') {
00138                   nest += NESTINCR;
00139                   continue;
00140             } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
00141                   if (opsp == &opstack[0])
00142 overflow:               error("Expression too complex");
00143                   --opsp;
00144                   opsp->op = op;
00145                   opsp->pri = op_priority[op] + nest;
00146                   continue;
00147 
00148             } else {
00149                   valsp->type = STRING;
00150                   valsp->u.string = opname;
00151                   valsp++;
00152             }
00153             for (;;) {
00154                   opname = *ap++;
00155                   if (opname == NULL) {
00156                         if (nest != 0)
00157                               goto syntax;
00158                         pri = 0;
00159                         break;
00160                   }
00161                   if (opname[0] != ')' || opname[1] != '\0') {
00162                         if ((op = lookup_op(opname, binary_op)) < 0)
00163                               goto syntax;
00164                         op += FIRST_BINARY_OP;
00165                         pri = op_priority[op] + nest;
00166                         break;
00167                   }
00168                   if ((nest -= NESTINCR) < 0)
00169                         goto syntax;
00170             }
00171             while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
00172                   binary = opsp->op;
00173                   for (;;) {
00174                         valsp--;
00175                         c = op_argflag[opsp->op];
00176                         if (c == OP_INT) {
00177                               if (valsp->type == STRING)
00178                                     valsp->u.num = atol(valsp->u.string);
00179                               valsp->type = INTEGER;
00180                         } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */
00181                               if (valsp->type == INTEGER) {
00182                                     p = stalloc(32);
00183 #ifdef SHELL
00184                                     fmtstr(p, 32, "%d", valsp->u.num);
00185 #else
00186                                     sprintf(p, "%d", valsp->u.num);
00187 #endif
00188                                     valsp->u.string = p;
00189                               } else if (valsp->type == BOOLEAN) {
00190                                     if (valsp->u.num)
00191                                           valsp->u.string = "true";
00192                                     else
00193                                           valsp->u.string = "";
00194                               }
00195                               valsp->type = STRING;
00196                               if (c >= OP_FILE
00197                                && (fs.op != c
00198                                    || fs.name == NULL
00199                                    || ! equal(fs.name, valsp->u.string))) {
00200                                     fs.op = c;
00201                                     fs.name = valsp->u.string;
00202                                     if (c == OP_FILE) {
00203                                         fs.rcode = stat(valsp->u.string,
00204                                                                 &fs.stat);
00205                                     } else {
00206                                         fs.rcode = lstat(valsp->u.string,
00207                                                                 &fs.stat);
00208                                     }
00209                               }
00210                         }
00211                         if (binary < FIRST_BINARY_OP)
00212                               break;
00213                         binary = 0;
00214                   }
00215                   if (! skipping)
00216                         expr_operator(opsp->op, valsp, &fs);
00217                   else if (opsp->op == AND1 || opsp->op == OR1)
00218                         skipping--;
00219                   valsp++;              /* push value */
00220                   opsp++;               /* pop operator */
00221             }
00222             if (opname == NULL)
00223                   break;
00224             if (opsp == &opstack[0])
00225                   goto overflow;
00226             if (op == AND1 || op == AND2) {
00227                   op = AND1;
00228                   if (skipping || expr_is_false(valsp - 1))
00229                         skipping++;
00230             }
00231             if (op == OR1 || op == OR2) {
00232                   op = OR1;
00233                   if (skipping || ! expr_is_false(valsp - 1))
00234                         skipping++;
00235             }
00236             opsp--;
00237             opsp->op = op;
00238             opsp->pri = pri;
00239       }
00240 done:
00241       if (print) {
00242             if (valstack[0].type == STRING)
00243                   printf("%s\n", valstack[0].u.string);
00244             else if (valstack[0].type == INTEGER)
00245                   printf("%ld\n", valstack[0].u.num);
00246             else if (valstack[0].u.num != 0)
00247                   printf("true\n");
00248       }
00249       return expr_is_false(&valstack[0]);
00250 }
00251 
00252 
00253 int
00254 expr_is_false(val)
00255       struct value *val;
00256       {
00257       if (val->type == STRING) {
00258             if (val->u.string[0] == '\0')
00259                   return 1;
00260       } else {  /* INTEGER or BOOLEAN */
00261             if (val->u.num == 0)
00262                   return 1;
00263       }
00264       return 0;
00265 }
00266 
00267 
00268 /*
00269  * Execute an operator.  Op is the operator.  Sp is the stack pointer;
00270  * sp[0] refers to the first operand, sp[1] refers to the second operand
00271  * (if any), and the result is placed in sp[0].  The operands are converted
00272  * to the type expected by the operator before expr_operator is called.
00273  * Fs is a pointer to a structure which holds the value of the last call
00274  * to stat, to avoid repeated stat calls on the same file.
00275  */
00276 
00277 void
00278 expr_operator(op, sp, fs)
00279       int op;
00280       struct value *sp;
00281       struct filestat *fs;
00282       {
00283       int i;
00284       struct stat st1, st2;
00285 
00286       switch (op) {
00287       case NOT:
00288             sp->u.num = expr_is_false(sp);
00289             sp->type = BOOLEAN;
00290             break;
00291       case ISREAD:
00292             i = 04;
00293             goto permission;
00294       case ISWRITE:
00295             i = 02;
00296             goto permission;
00297       case ISEXEC:
00298             i = 01;
00299 permission:
00300             if (fs->stat.st_uid == geteuid())
00301                   i <<= 6;
00302             else if (fs->stat.st_gid == getegid())
00303                   i <<= 3;
00304             goto filebit;       /* true if (stat.st_mode & i) != 0 */
00305       case ISFILE:
00306             i = S_IFREG;
00307             goto filetype;
00308       case ISDIR:
00309             i = S_IFDIR;
00310             goto filetype;
00311       case ISCHAR:
00312             i = S_IFCHR;
00313             goto filetype;
00314       case ISBLOCK:
00315             i = S_IFBLK;
00316             goto filetype;
00317       case ISFIFO:
00318 #ifdef S_IFIFO
00319             i = S_IFIFO;
00320             goto filetype;
00321 #else
00322             goto false;
00323 #endif
00324 filetype:
00325             if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) {
00326 true:
00327                   sp->u.num = 1;
00328             } else {
00329 false:
00330                   sp->u.num = 0;
00331             }
00332             sp->type = BOOLEAN;
00333             break;
00334       case ISSETUID:
00335             i = S_ISUID;
00336             goto filebit;
00337       case ISSETGID:
00338             i = S_ISGID;
00339             goto filebit;
00340       case ISSTICKY:
00341             i = S_ISVTX;
00342 filebit:
00343             if (fs->stat.st_mode & i && fs->rcode >= 0)
00344                   goto true;
00345             goto false;
00346       case ISSIZE:
00347             sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L;
00348             sp->type = INTEGER;
00349             break;
00350       case ISLINK1:
00351       case ISLINK2:
00352             if (S_ISLNK(fs->stat.st_mode) && fs->rcode >= 0)
00353                   goto true;
00354             fs->op = OP_FILE;   /* not a symlink, so expect a -d or so next */
00355             goto false;
00356       case NEWER:
00357             if (stat(sp->u.string, &st1) != 0) {
00358                   sp->u.num = 0;
00359             } else if (stat((sp + 1)->u.string, &st2) != 0) {
00360                   sp->u.num = 1;
00361             } else {
00362                   sp->u.num = st1.st_mtime >= st2.st_mtime;
00363             }
00364             sp->type = INTEGER;
00365             break;
00366       case ISTTY:
00367             sp->u.num = isatty(sp->u.num);
00368             sp->type = BOOLEAN;
00369             break;
00370       case NULSTR:
00371             if (sp->u.string[0] == '\0')
00372                   goto true;
00373             goto false;
00374       case STRLEN:
00375             sp->u.num = strlen(sp->u.string);
00376             sp->type = INTEGER;
00377             break;
00378       case OR1:
00379       case AND1:
00380             /*
00381              * These operators are mostly handled by the parser.  If we
00382              * get here it means that both operands were evaluated, so
00383              * the value is the value of the second operand.
00384              */
00385             *sp = *(sp + 1);
00386             break;
00387       case STREQ:
00388       case STRNE:
00389             i = 0;
00390             if (equal(sp->u.string, (sp + 1)->u.string))
00391                   i++;
00392             if (op == STRNE)
00393                   i = 1 - i;
00394             sp->u.num = i;
00395             sp->type = BOOLEAN;
00396             break;
00397       case EQ:
00398             if (sp->u.num == (sp + 1)->u.num)
00399                   goto true;
00400             goto false;
00401       case NE:
00402             if (sp->u.num != (sp + 1)->u.num)
00403                   goto true;
00404             goto false;
00405       case GT:
00406             if (sp->u.num > (sp + 1)->u.num)
00407                   goto true;
00408             goto false;
00409       case LT:
00410             if (sp->u.num < (sp + 1)->u.num)
00411                   goto true;
00412             goto false;
00413       case LE:
00414             if (sp->u.num <= (sp + 1)->u.num)
00415                   goto true;
00416             goto false;
00417       case GE:
00418             if (sp->u.num >= (sp + 1)->u.num)
00419                   goto true;
00420             goto false;
00421       case PLUS:
00422             sp->u.num += (sp + 1)->u.num;
00423             break;
00424       case MINUS:
00425             sp->u.num -= (sp + 1)->u.num;
00426             break;
00427       case TIMES:
00428             sp->u.num *= (sp + 1)->u.num;
00429             break;
00430       case DIVIDE:
00431             if ((sp + 1)->u.num == 0)
00432                   error("Division by zero");
00433             sp->u.num /= (sp + 1)->u.num;
00434             break;
00435       case REM:
00436             if ((sp + 1)->u.num == 0)
00437                   error("Division by zero");
00438             sp->u.num %= (sp + 1)->u.num;
00439             break;
00440       case MATCHPAT:
00441             {
00442                   char *pat;
00443 
00444                   pat = re_compile((sp + 1)->u.string);
00445                   if (re_match(pat, sp->u.string)) {
00446                         if (number_parens > 0) {
00447                               sp->u.string = match_begin[1];
00448                               sp->u.string[match_length[1]] = '\0';
00449                         } else {
00450                               sp->u.num = match_length[0];
00451                               sp->type = INTEGER;
00452                         }
00453                   } else {
00454                         if (number_parens > 0) {
00455                               sp->u.string[0] = '\0';
00456                         } else {
00457                               sp->u.num = 0;
00458                               sp->type = INTEGER;
00459                         }
00460                   }
00461             }
00462             break;
00463       }
00464 }
00465 
00466 
00467 int
00468 lookup_op(name, table)
00469       char *name;
00470       char *const*table;
00471       {
00472       register char *const*tp;
00473       register char const *p;
00474       char c = name[1];
00475 
00476       for (tp = table ; (p = *tp) != NULL ; tp++) {
00477             if (p[1] == c && equal(p, name))
00478                   return tp - table;
00479       }
00480       return -1;
00481 }

Generated on Fri Apr 14 22:56:39 2006 for minix by  doxygen 1.4.6