[9] | 1 | /*
|
---|
| 2 | * The expr and test commands.
|
---|
| 3 | *
|
---|
| 4 | * Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
|
---|
| 5 | * This file is part of ash, which is distributed under the terms specified
|
---|
| 6 | * by the Ash General Public License. See the file named LICENSE.
|
---|
| 7 | */
|
---|
| 8 |
|
---|
| 9 |
|
---|
| 10 | #define main exprcmd
|
---|
| 11 |
|
---|
| 12 | #include "bltin.h"
|
---|
| 13 | #include "operators.h"
|
---|
| 14 | #include <sys/types.h>
|
---|
| 15 | #include <sys/stat.h>
|
---|
| 16 |
|
---|
| 17 | #ifndef S_ISLNK
|
---|
| 18 | #define lstat stat
|
---|
| 19 | #define S_ISLNK(mode) (0)
|
---|
| 20 | #endif
|
---|
| 21 |
|
---|
| 22 | #define STACKSIZE 12
|
---|
| 23 | #define NESTINCR 16
|
---|
| 24 |
|
---|
| 25 | /* data types */
|
---|
| 26 | #define STRING 0
|
---|
| 27 | #define INTEGER 1
|
---|
| 28 | #define BOOLEAN 2
|
---|
| 29 |
|
---|
| 30 |
|
---|
| 31 | /*
|
---|
| 32 | * This structure hold a value. The type keyword specifies the type of
|
---|
| 33 | * the value, and the union u holds the value. The value of a boolean
|
---|
| 34 | * is stored in u.num (1 = TRUE, 0 = FALSE).
|
---|
| 35 | */
|
---|
| 36 |
|
---|
| 37 | struct value {
|
---|
| 38 | int type;
|
---|
| 39 | union {
|
---|
| 40 | char *string;
|
---|
| 41 | long num;
|
---|
| 42 | } u;
|
---|
| 43 | };
|
---|
| 44 |
|
---|
| 45 |
|
---|
| 46 | struct operator {
|
---|
| 47 | short op; /* which operator */
|
---|
| 48 | short pri; /* priority of operator */
|
---|
| 49 | };
|
---|
| 50 |
|
---|
| 51 |
|
---|
| 52 | struct filestat {
|
---|
| 53 | int op; /* OP_FILE or OP_LFILE */
|
---|
| 54 | char *name; /* name of file */
|
---|
| 55 | int rcode; /* return code from stat */
|
---|
| 56 | struct stat stat; /* status info on file */
|
---|
| 57 | };
|
---|
| 58 |
|
---|
| 59 |
|
---|
| 60 | extern char *match_begin[10]; /* matched string */
|
---|
| 61 | extern short match_length[10]; /* defined in regexp.c */
|
---|
| 62 | extern short number_parens; /* number of \( \) pairs */
|
---|
| 63 |
|
---|
| 64 |
|
---|
| 65 | #ifdef __STDC__
|
---|
| 66 | int expr_is_false(struct value *);
|
---|
| 67 | void expr_operator(int, struct value *, struct filestat *);
|
---|
| 68 | int lookup_op(char *, char *const*);
|
---|
| 69 | char *re_compile(char *); /* defined in regexp.c */
|
---|
| 70 | int re_match(char *, char *); /* defined in regexp.c */
|
---|
| 71 | long atol(const char *);
|
---|
| 72 | #else
|
---|
| 73 | int expr_is_false();
|
---|
| 74 | void expr_operator();
|
---|
| 75 | int lookup_op();
|
---|
| 76 | char *re_compile(); /* defined in regexp.c */
|
---|
| 77 | int re_match(); /* defined in regexp.c */
|
---|
| 78 | long atol();
|
---|
| 79 | #endif
|
---|
| 80 |
|
---|
| 81 |
|
---|
| 82 |
|
---|
| 83 | main(argc, argv) char **argv; {
|
---|
| 84 | char **ap;
|
---|
| 85 | char *opname;
|
---|
| 86 | char c;
|
---|
| 87 | char *p;
|
---|
| 88 | int print;
|
---|
| 89 | int nest; /* parenthises nesting */
|
---|
| 90 | int op;
|
---|
| 91 | int pri;
|
---|
| 92 | int skipping;
|
---|
| 93 | int binary;
|
---|
| 94 | struct operator opstack[STACKSIZE];
|
---|
| 95 | struct operator *opsp;
|
---|
| 96 | struct value valstack[STACKSIZE + 1];
|
---|
| 97 | struct value *valsp;
|
---|
| 98 | struct filestat fs;
|
---|
| 99 |
|
---|
| 100 | INITARGS(argv);
|
---|
| 101 | c = **argv;
|
---|
| 102 | print = 1;
|
---|
| 103 | if (c == 't')
|
---|
| 104 | print = 0;
|
---|
| 105 | else if (c == '[') {
|
---|
| 106 | if (! equal(argv[argc - 1], "]"))
|
---|
| 107 | error("missing ]");
|
---|
| 108 | argv[argc - 1] = NULL;
|
---|
| 109 | print = 0;
|
---|
| 110 | }
|
---|
| 111 | ap = argv + 1;
|
---|
| 112 | fs.name = NULL;
|
---|
| 113 |
|
---|
| 114 | /*
|
---|
| 115 | * We use operator precedence parsing, evaluating the expression
|
---|
| 116 | * as we parse it. Parentheses are handled by bumping up the
|
---|
| 117 | * priority of operators using the variable "nest." We use the
|
---|
| 118 | * variable "skipping" to turn off evaluation temporarily for the
|
---|
| 119 | * short circuit boolean operators. (It is important do the short
|
---|
| 120 | * circuit evaluation because under NFS a stat operation can take
|
---|
| 121 | * infinitely long.)
|
---|
| 122 | */
|
---|
| 123 |
|
---|
| 124 | nest = 0;
|
---|
| 125 | skipping = 0;
|
---|
| 126 | opsp = opstack + STACKSIZE;
|
---|
| 127 | valsp = valstack;
|
---|
| 128 | if (*ap == NULL) {
|
---|
| 129 | valstack[0].type = BOOLEAN;
|
---|
| 130 | valstack[0].u.num = 0;
|
---|
| 131 | goto done;
|
---|
| 132 | }
|
---|
| 133 | for (;;) {
|
---|
| 134 | opname = *ap++;
|
---|
| 135 | if (opname == NULL)
|
---|
| 136 | syntax: error("syntax error");
|
---|
| 137 | if (opname[0] == '(' && opname[1] == '\0') {
|
---|
| 138 | nest += NESTINCR;
|
---|
| 139 | continue;
|
---|
| 140 | } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
|
---|
| 141 | if (opsp == &opstack[0])
|
---|
| 142 | overflow: error("Expression too complex");
|
---|
| 143 | --opsp;
|
---|
| 144 | opsp->op = op;
|
---|
| 145 | opsp->pri = op_priority[op] + nest;
|
---|
| 146 | continue;
|
---|
| 147 |
|
---|
| 148 | } else {
|
---|
| 149 | valsp->type = STRING;
|
---|
| 150 | valsp->u.string = opname;
|
---|
| 151 | valsp++;
|
---|
| 152 | }
|
---|
| 153 | for (;;) {
|
---|
| 154 | opname = *ap++;
|
---|
| 155 | if (opname == NULL) {
|
---|
| 156 | if (nest != 0)
|
---|
| 157 | goto syntax;
|
---|
| 158 | pri = 0;
|
---|
| 159 | break;
|
---|
| 160 | }
|
---|
| 161 | if (opname[0] != ')' || opname[1] != '\0') {
|
---|
| 162 | if ((op = lookup_op(opname, binary_op)) < 0)
|
---|
| 163 | goto syntax;
|
---|
| 164 | op += FIRST_BINARY_OP;
|
---|
| 165 | pri = op_priority[op] + nest;
|
---|
| 166 | break;
|
---|
| 167 | }
|
---|
| 168 | if ((nest -= NESTINCR) < 0)
|
---|
| 169 | goto syntax;
|
---|
| 170 | }
|
---|
| 171 | while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
|
---|
| 172 | binary = opsp->op;
|
---|
| 173 | for (;;) {
|
---|
| 174 | valsp--;
|
---|
| 175 | c = op_argflag[opsp->op];
|
---|
| 176 | if (c == OP_INT) {
|
---|
| 177 | if (valsp->type == STRING)
|
---|
| 178 | valsp->u.num = atol(valsp->u.string);
|
---|
| 179 | valsp->type = INTEGER;
|
---|
| 180 | } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */
|
---|
| 181 | if (valsp->type == INTEGER) {
|
---|
| 182 | p = stalloc(32);
|
---|
| 183 | #ifdef SHELL
|
---|
| 184 | fmtstr(p, 32, "%d", valsp->u.num);
|
---|
| 185 | #else
|
---|
| 186 | sprintf(p, "%d", valsp->u.num);
|
---|
| 187 | #endif
|
---|
| 188 | valsp->u.string = p;
|
---|
| 189 | } else if (valsp->type == BOOLEAN) {
|
---|
| 190 | if (valsp->u.num)
|
---|
| 191 | valsp->u.string = "true";
|
---|
| 192 | else
|
---|
| 193 | valsp->u.string = "";
|
---|
| 194 | }
|
---|
| 195 | valsp->type = STRING;
|
---|
| 196 | if (c >= OP_FILE
|
---|
| 197 | && (fs.op != c
|
---|
| 198 | || fs.name == NULL
|
---|
| 199 | || ! equal(fs.name, valsp->u.string))) {
|
---|
| 200 | fs.op = c;
|
---|
| 201 | fs.name = valsp->u.string;
|
---|
| 202 | if (c == OP_FILE) {
|
---|
| 203 | fs.rcode = stat(valsp->u.string,
|
---|
| 204 | &fs.stat);
|
---|
| 205 | } else {
|
---|
| 206 | fs.rcode = lstat(valsp->u.string,
|
---|
| 207 | &fs.stat);
|
---|
| 208 | }
|
---|
| 209 | }
|
---|
| 210 | }
|
---|
| 211 | if (binary < FIRST_BINARY_OP)
|
---|
| 212 | break;
|
---|
| 213 | binary = 0;
|
---|
| 214 | }
|
---|
| 215 | if (! skipping)
|
---|
| 216 | expr_operator(opsp->op, valsp, &fs);
|
---|
| 217 | else if (opsp->op == AND1 || opsp->op == OR1)
|
---|
| 218 | skipping--;
|
---|
| 219 | valsp++; /* push value */
|
---|
| 220 | opsp++; /* pop operator */
|
---|
| 221 | }
|
---|
| 222 | if (opname == NULL)
|
---|
| 223 | break;
|
---|
| 224 | if (opsp == &opstack[0])
|
---|
| 225 | goto overflow;
|
---|
| 226 | if (op == AND1 || op == AND2) {
|
---|
| 227 | op = AND1;
|
---|
| 228 | if (skipping || expr_is_false(valsp - 1))
|
---|
| 229 | skipping++;
|
---|
| 230 | }
|
---|
| 231 | if (op == OR1 || op == OR2) {
|
---|
| 232 | op = OR1;
|
---|
| 233 | if (skipping || ! expr_is_false(valsp - 1))
|
---|
| 234 | skipping++;
|
---|
| 235 | }
|
---|
| 236 | opsp--;
|
---|
| 237 | opsp->op = op;
|
---|
| 238 | opsp->pri = pri;
|
---|
| 239 | }
|
---|
| 240 | done:
|
---|
| 241 | if (print) {
|
---|
| 242 | if (valstack[0].type == STRING)
|
---|
| 243 | printf("%s\n", valstack[0].u.string);
|
---|
| 244 | else if (valstack[0].type == INTEGER)
|
---|
| 245 | printf("%ld\n", valstack[0].u.num);
|
---|
| 246 | else if (valstack[0].u.num != 0)
|
---|
| 247 | printf("true\n");
|
---|
| 248 | }
|
---|
| 249 | return expr_is_false(&valstack[0]);
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 |
|
---|
| 253 | int
|
---|
| 254 | expr_is_false(val)
|
---|
| 255 | struct value *val;
|
---|
| 256 | {
|
---|
| 257 | if (val->type == STRING) {
|
---|
| 258 | if (val->u.string[0] == '\0')
|
---|
| 259 | return 1;
|
---|
| 260 | } else { /* INTEGER or BOOLEAN */
|
---|
| 261 | if (val->u.num == 0)
|
---|
| 262 | return 1;
|
---|
| 263 | }
|
---|
| 264 | return 0;
|
---|
| 265 | }
|
---|
| 266 |
|
---|
| 267 |
|
---|
| 268 | /*
|
---|
| 269 | * Execute an operator. Op is the operator. Sp is the stack pointer;
|
---|
| 270 | * sp[0] refers to the first operand, sp[1] refers to the second operand
|
---|
| 271 | * (if any), and the result is placed in sp[0]. The operands are converted
|
---|
| 272 | * to the type expected by the operator before expr_operator is called.
|
---|
| 273 | * Fs is a pointer to a structure which holds the value of the last call
|
---|
| 274 | * to stat, to avoid repeated stat calls on the same file.
|
---|
| 275 | */
|
---|
| 276 |
|
---|
| 277 | void
|
---|
| 278 | expr_operator(op, sp, fs)
|
---|
| 279 | int op;
|
---|
| 280 | struct value *sp;
|
---|
| 281 | struct filestat *fs;
|
---|
| 282 | {
|
---|
| 283 | int i;
|
---|
| 284 | struct stat st1, st2;
|
---|
| 285 |
|
---|
| 286 | switch (op) {
|
---|
| 287 | case NOT:
|
---|
| 288 | sp->u.num = expr_is_false(sp);
|
---|
| 289 | sp->type = BOOLEAN;
|
---|
| 290 | break;
|
---|
| 291 | case ISREAD:
|
---|
| 292 | i = 04;
|
---|
| 293 | goto permission;
|
---|
| 294 | case ISWRITE:
|
---|
| 295 | i = 02;
|
---|
| 296 | goto permission;
|
---|
| 297 | case ISEXEC:
|
---|
| 298 | i = 01;
|
---|
| 299 | permission:
|
---|
| 300 | if (fs->stat.st_uid == geteuid())
|
---|
| 301 | i <<= 6;
|
---|
| 302 | else if (fs->stat.st_gid == getegid())
|
---|
| 303 | i <<= 3;
|
---|
| 304 | goto filebit; /* true if (stat.st_mode & i) != 0 */
|
---|
| 305 | case ISFILE:
|
---|
| 306 | i = S_IFREG;
|
---|
| 307 | goto filetype;
|
---|
| 308 | case ISDIR:
|
---|
| 309 | i = S_IFDIR;
|
---|
| 310 | goto filetype;
|
---|
| 311 | case ISCHAR:
|
---|
| 312 | i = S_IFCHR;
|
---|
| 313 | goto filetype;
|
---|
| 314 | case ISBLOCK:
|
---|
| 315 | i = S_IFBLK;
|
---|
| 316 | goto filetype;
|
---|
| 317 | case ISFIFO:
|
---|
| 318 | #ifdef S_IFIFO
|
---|
| 319 | i = S_IFIFO;
|
---|
| 320 | goto filetype;
|
---|
| 321 | #else
|
---|
| 322 | goto false;
|
---|
| 323 | #endif
|
---|
| 324 | filetype:
|
---|
| 325 | if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) {
|
---|
| 326 | true:
|
---|
| 327 | sp->u.num = 1;
|
---|
| 328 | } else {
|
---|
| 329 | false:
|
---|
| 330 | sp->u.num = 0;
|
---|
| 331 | }
|
---|
| 332 | sp->type = BOOLEAN;
|
---|
| 333 | break;
|
---|
| 334 | case ISSETUID:
|
---|
| 335 | i = S_ISUID;
|
---|
| 336 | goto filebit;
|
---|
| 337 | case ISSETGID:
|
---|
| 338 | i = S_ISGID;
|
---|
| 339 | goto filebit;
|
---|
| 340 | case ISSTICKY:
|
---|
| 341 | i = S_ISVTX;
|
---|
| 342 | filebit:
|
---|
| 343 | if (fs->stat.st_mode & i && fs->rcode >= 0)
|
---|
| 344 | goto true;
|
---|
| 345 | goto false;
|
---|
| 346 | case ISSIZE:
|
---|
| 347 | sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L;
|
---|
| 348 | sp->type = INTEGER;
|
---|
| 349 | break;
|
---|
| 350 | case ISLINK1:
|
---|
| 351 | case ISLINK2:
|
---|
| 352 | if (S_ISLNK(fs->stat.st_mode) && fs->rcode >= 0)
|
---|
| 353 | goto true;
|
---|
| 354 | fs->op = OP_FILE; /* not a symlink, so expect a -d or so next */
|
---|
| 355 | goto false;
|
---|
| 356 | case NEWER:
|
---|
| 357 | if (stat(sp->u.string, &st1) != 0) {
|
---|
| 358 | sp->u.num = 0;
|
---|
| 359 | } else if (stat((sp + 1)->u.string, &st2) != 0) {
|
---|
| 360 | sp->u.num = 1;
|
---|
| 361 | } else {
|
---|
| 362 | sp->u.num = st1.st_mtime >= st2.st_mtime;
|
---|
| 363 | }
|
---|
| 364 | sp->type = INTEGER;
|
---|
| 365 | break;
|
---|
| 366 | case ISTTY:
|
---|
| 367 | sp->u.num = isatty(sp->u.num);
|
---|
| 368 | sp->type = BOOLEAN;
|
---|
| 369 | break;
|
---|
| 370 | case NULSTR:
|
---|
| 371 | if (sp->u.string[0] == '\0')
|
---|
| 372 | goto true;
|
---|
| 373 | goto false;
|
---|
| 374 | case STRLEN:
|
---|
| 375 | sp->u.num = strlen(sp->u.string);
|
---|
| 376 | sp->type = INTEGER;
|
---|
| 377 | break;
|
---|
| 378 | case OR1:
|
---|
| 379 | case AND1:
|
---|
| 380 | /*
|
---|
| 381 | * These operators are mostly handled by the parser. If we
|
---|
| 382 | * get here it means that both operands were evaluated, so
|
---|
| 383 | * the value is the value of the second operand.
|
---|
| 384 | */
|
---|
| 385 | *sp = *(sp + 1);
|
---|
| 386 | break;
|
---|
| 387 | case STREQ:
|
---|
| 388 | case STRNE:
|
---|
| 389 | i = 0;
|
---|
| 390 | if (equal(sp->u.string, (sp + 1)->u.string))
|
---|
| 391 | i++;
|
---|
| 392 | if (op == STRNE)
|
---|
| 393 | i = 1 - i;
|
---|
| 394 | sp->u.num = i;
|
---|
| 395 | sp->type = BOOLEAN;
|
---|
| 396 | break;
|
---|
| 397 | case EQ:
|
---|
| 398 | if (sp->u.num == (sp + 1)->u.num)
|
---|
| 399 | goto true;
|
---|
| 400 | goto false;
|
---|
| 401 | case NE:
|
---|
| 402 | if (sp->u.num != (sp + 1)->u.num)
|
---|
| 403 | goto true;
|
---|
| 404 | goto false;
|
---|
| 405 | case GT:
|
---|
| 406 | if (sp->u.num > (sp + 1)->u.num)
|
---|
| 407 | goto true;
|
---|
| 408 | goto false;
|
---|
| 409 | case LT:
|
---|
| 410 | if (sp->u.num < (sp + 1)->u.num)
|
---|
| 411 | goto true;
|
---|
| 412 | goto false;
|
---|
| 413 | case LE:
|
---|
| 414 | if (sp->u.num <= (sp + 1)->u.num)
|
---|
| 415 | goto true;
|
---|
| 416 | goto false;
|
---|
| 417 | case GE:
|
---|
| 418 | if (sp->u.num >= (sp + 1)->u.num)
|
---|
| 419 | goto true;
|
---|
| 420 | goto false;
|
---|
| 421 | case PLUS:
|
---|
| 422 | sp->u.num += (sp + 1)->u.num;
|
---|
| 423 | break;
|
---|
| 424 | case MINUS:
|
---|
| 425 | sp->u.num -= (sp + 1)->u.num;
|
---|
| 426 | break;
|
---|
| 427 | case TIMES:
|
---|
| 428 | sp->u.num *= (sp + 1)->u.num;
|
---|
| 429 | break;
|
---|
| 430 | case DIVIDE:
|
---|
| 431 | if ((sp + 1)->u.num == 0)
|
---|
| 432 | error("Division by zero");
|
---|
| 433 | sp->u.num /= (sp + 1)->u.num;
|
---|
| 434 | break;
|
---|
| 435 | case REM:
|
---|
| 436 | if ((sp + 1)->u.num == 0)
|
---|
| 437 | error("Division by zero");
|
---|
| 438 | sp->u.num %= (sp + 1)->u.num;
|
---|
| 439 | break;
|
---|
| 440 | case MATCHPAT:
|
---|
| 441 | {
|
---|
| 442 | char *pat;
|
---|
| 443 |
|
---|
| 444 | pat = re_compile((sp + 1)->u.string);
|
---|
| 445 | if (re_match(pat, sp->u.string)) {
|
---|
| 446 | if (number_parens > 0) {
|
---|
| 447 | sp->u.string = match_begin[1];
|
---|
| 448 | sp->u.string[match_length[1]] = '\0';
|
---|
| 449 | } else {
|
---|
| 450 | sp->u.num = match_length[0];
|
---|
| 451 | sp->type = INTEGER;
|
---|
| 452 | }
|
---|
| 453 | } else {
|
---|
| 454 | if (number_parens > 0) {
|
---|
| 455 | sp->u.string[0] = '\0';
|
---|
| 456 | } else {
|
---|
| 457 | sp->u.num = 0;
|
---|
| 458 | sp->type = INTEGER;
|
---|
| 459 | }
|
---|
| 460 | }
|
---|
| 461 | }
|
---|
| 462 | break;
|
---|
| 463 | }
|
---|
| 464 | }
|
---|
| 465 |
|
---|
| 466 |
|
---|
| 467 | int
|
---|
| 468 | lookup_op(name, table)
|
---|
| 469 | char *name;
|
---|
| 470 | char *const*table;
|
---|
| 471 | {
|
---|
| 472 | register char *const*tp;
|
---|
| 473 | register char const *p;
|
---|
| 474 | char c = name[1];
|
---|
| 475 |
|
---|
| 476 | for (tp = table ; (p = *tp) != NULL ; tp++) {
|
---|
| 477 | if (p[1] == c && equal(p, name))
|
---|
| 478 | return tp - table;
|
---|
| 479 | }
|
---|
| 480 | return -1;
|
---|
| 481 | }
|
---|