1/* 2 * cond.c - evaluate conditional expressions 3 * 4 * This file is part of zsh, the Z shell. 5 * 6 * Copyright (c) 1992-1997 Paul Falstad 7 * All rights reserved. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and to distribute modified versions of this software for any 12 * purpose, provided that the above copyright notice and the following 13 * two paragraphs appear in all copies of this software. 14 * 15 * In no event shall Paul Falstad or the Zsh Development Group be liable 16 * to any party for direct, indirect, special, incidental, or consequential 17 * damages arising out of the use of this software and its documentation, 18 * even if Paul Falstad and the Zsh Development Group have been advised of 19 * the possibility of such damage. 20 * 21 * Paul Falstad and the Zsh Development Group specifically disclaim any 22 * warranties, including, but not limited to, the implied warranties of 23 * merchantability and fitness for a particular purpose. The software 24 * provided hereunder is on an "as is" basis, and Paul Falstad and the 25 * Zsh Development Group have no obligation to provide maintenance, 26 * support, updates, enhancements, or modifications. 27 * 28 */ 29 30#include "zsh.mdh" 31#include "cond.pro" 32 33int tracingcond; 34 35static char *condstr[COND_MOD] = { 36 "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", 37 "-ne", "-lt", "-gt", "-le", "-ge", "=~" 38}; 39 40/* 41 * Evaluate a conditional expression given the arguments. 42 * If fromtest is set, the caller is the test or [ builtin; 43 * with the pointer giving the name of the command. 44 * for POSIX conformance this supports a more limited range 45 * of functionality. 46 * 47 * Return status is the final shell status, i.e. 0 for true, 48 * 1 for false and 2 for error. 49 */ 50 51/**/ 52int 53evalcond(Estate state, char *fromtest) 54{ 55 struct stat *st; 56 char *left, *right, *overridename, overridebuf[13]; 57 Wordcode pcode; 58 wordcode code; 59 int ctype, htok = 0, ret; 60 61 rec: 62 63 left = right = overridename = NULL; 64 pcode = state->pc++; 65 code = *pcode; 66 ctype = WC_COND_TYPE(code); 67 68 switch (ctype) { 69 case COND_NOT: 70 if (tracingcond) 71 fprintf(xtrerr, " %s", condstr[ctype]); 72 ret = evalcond(state, fromtest); 73 if (ret == 2) 74 return ret; 75 else 76 return !ret; 77 case COND_AND: 78 if (!(ret = evalcond(state, fromtest))) { 79 if (tracingcond) 80 fprintf(xtrerr, " %s", condstr[ctype]); 81 goto rec; 82 } else { 83 state->pc = pcode + (WC_COND_SKIP(code) + 1); 84 return ret; 85 } 86 case COND_OR: 87 if ((ret = evalcond(state, fromtest)) == 1) { 88 if (tracingcond) 89 fprintf(xtrerr, " %s", condstr[ctype]); 90 goto rec; 91 } else { 92 state->pc = pcode + (WC_COND_SKIP(code) + 1); 93 return ret; 94 } 95 case COND_REGEX: 96 { 97 char *modname = isset(REMATCHPCRE) ? "zsh/pcre" : "zsh/regex"; 98 sprintf(overridename = overridebuf, "-%s-match", modname+4); 99 (void)ensurefeature(modname, "C:", overridename+1); 100 ctype = COND_MODI; 101 } 102 /*FALLTHROUGH*/ 103 case COND_MOD: 104 case COND_MODI: 105 { 106 Conddef cd; 107 char *name = overridename, *errname; 108 char **strs; 109 int l = WC_COND_SKIP(code); 110 111 if (name == NULL) 112 name = ecgetstr(state, EC_NODUP, NULL); 113 if (ctype == COND_MOD) 114 strs = ecgetarr(state, l, EC_DUP, NULL); 115 else { 116 char *sbuf[3]; 117 118 sbuf[0] = ecgetstr(state, EC_NODUP, NULL); 119 sbuf[1] = ecgetstr(state, EC_NODUP, NULL); 120 sbuf[2] = NULL; 121 122 strs = arrdup(sbuf); 123 l = 2; 124 } 125 if (name && name[0] == '-') 126 errname = name; 127 else if (strs[0] && *strs[0] == '-') 128 errname = strs[0]; 129 else 130 errname = "<null>"; 131 if (name && name[0] == '-' && 132 (cd = getconddef((ctype == COND_MODI), name + 1, 1))) { 133 if (ctype == COND_MOD && 134 (l < cd->min || (cd->max >= 0 && l > cd->max))) { 135 zwarnnam(fromtest, "unknown condition: %s", name); 136 return 2; 137 } 138 if (tracingcond) 139 tracemodcond(name, strs, ctype == COND_MODI); 140 return !cd->handler(strs, cd->condid); 141 } 142 else { 143 char *s = strs[0]; 144 145 if (overridename) { 146 /* 147 * Standard regex function not available: this 148 * is a hard error. 149 */ 150 zerrnam(fromtest, "%s not available for regex", 151 overridename); 152 return 2; 153 } 154 155 strs[0] = dupstring(name); 156 name = s; 157 158 if (name && name[0] == '-' && 159 (cd = getconddef(0, name + 1, 1))) { 160 if (l < cd->min || (cd->max >= 0 && l > cd->max)) { 161 zwarnnam(fromtest, "unknown condition: %s", 162 errname); 163 return 2; 164 } 165 if (tracingcond) 166 tracemodcond(name, strs, ctype == COND_MODI); 167 return !cd->handler(strs, cd->condid); 168 } else { 169 zwarnnam(fromtest, 170 "unknown condition: %s", 171 errname); 172 } 173 } 174 /* module not found, error */ 175 return 2; 176 } 177 } 178 left = ecgetstr(state, EC_DUPTOK, &htok); 179 if (htok) { 180 singsub(&left); 181 untokenize(left); 182 } 183 if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) { 184 right = ecgetstr(state, EC_DUPTOK, &htok); 185 if (htok) { 186 singsub(&right); 187 untokenize(right); 188 } 189 } 190 if (tracingcond) { 191 if (ctype < COND_MOD) { 192 fputc(' ',xtrerr); 193 quotedzputs(left, xtrerr); 194 fprintf(xtrerr, " %s ", condstr[ctype]); 195 if (ctype == COND_STREQ || ctype == COND_STRNEQ) { 196 char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL)); 197 singsub(&rt); 198 quote_tokenized_output(rt, xtrerr); 199 } 200 else 201 quotedzputs((char *)right, xtrerr); 202 } else { 203 fprintf(xtrerr, " -%c ", ctype); 204 quotedzputs(left, xtrerr); 205 } 206 } 207 208 if (ctype >= COND_EQ && ctype <= COND_GE) { 209 mnumber mn1, mn2; 210 if (fromtest) { 211 /* 212 * For test and [, the expressions must be base 10 integers, 213 * not integer expressions. 214 */ 215 char *eptr, *err; 216 217 mn1.u.l = zstrtol(left, &eptr, 10); 218 if (!*eptr) 219 { 220 mn2.u.l = zstrtol(right, &eptr, 10); 221 err = right; 222 } 223 else 224 err = left; 225 226 if (*eptr) 227 { 228 zwarnnam(fromtest, "integer expression expected: %s", err); 229 return 2; 230 } 231 232 mn1.type = mn2.type = MN_INTEGER; 233 } else { 234 mn1 = matheval(left); 235 mn2 = matheval(right); 236 } 237 238 if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) == 239 (MN_INTEGER|MN_FLOAT)) { 240 /* promote to float */ 241 if (mn1.type & MN_INTEGER) { 242 mn1.type = MN_FLOAT; 243 mn1.u.d = (double)mn1.u.l; 244 } 245 if (mn2.type & MN_INTEGER) { 246 mn2.type = MN_FLOAT; 247 mn2.u.d = (double)mn2.u.l; 248 } 249 } 250 switch(ctype) { 251 case COND_EQ: 252 return !((mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) : 253 (mn1.u.l == mn2.u.l)); 254 case COND_NE: 255 return !((mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) : 256 (mn1.u.l != mn2.u.l)); 257 case COND_LT: 258 return !((mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) : 259 (mn1.u.l < mn2.u.l)); 260 case COND_GT: 261 return !((mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) : 262 (mn1.u.l > mn2.u.l)); 263 case COND_LE: 264 return !((mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) : 265 (mn1.u.l <= mn2.u.l)); 266 case COND_GE: 267 return !((mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) : 268 (mn1.u.l >= mn2.u.l)); 269 } 270 } 271 272 switch (ctype) { 273 case COND_STREQ: 274 case COND_STRNEQ: 275 { 276 int test, npat = state->pc[1]; 277 Patprog pprog = state->prog->pats[npat]; 278 279 if (pprog == dummy_patprog1 || pprog == dummy_patprog2) { 280 char *opat; 281 int save; 282 283 right = dupstring(opat = ecrawstr(state->prog, state->pc, 284 &htok)); 285 if (htok) 286 singsub(&right); 287 save = (!(state->prog->flags & EF_HEAP) && 288 !strcmp(opat, right) && pprog != dummy_patprog2); 289 290 if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), 291 NULL))) { 292 zwarnnam(fromtest, "bad pattern: %s", right); 293 return 2; 294 } 295 else if (save) 296 state->prog->pats[npat] = pprog; 297 } 298 state->pc += 2; 299 test = (pprog && pattry(pprog, left)); 300 301 return !(ctype == COND_STREQ ? test : !test); 302 } 303 case COND_STRLT: 304 return !(strcmp(left, right) < 0); 305 case COND_STRGTR: 306 return !(strcmp(left, right) > 0); 307 case 'e': 308 case 'a': 309 return (!doaccess(left, F_OK)); 310 case 'b': 311 return (!S_ISBLK(dostat(left))); 312 case 'c': 313 return (!S_ISCHR(dostat(left))); 314 case 'd': 315 return (!S_ISDIR(dostat(left))); 316 case 'f': 317 return (!S_ISREG(dostat(left))); 318 case 'g': 319 return (!(dostat(left) & S_ISGID)); 320 case 'k': 321 return (!(dostat(left) & S_ISVTX)); 322 case 'n': 323 return (!strlen(left)); 324 case 'o': 325 return (optison(fromtest, left)); 326 case 'p': 327 return (!S_ISFIFO(dostat(left))); 328 case 'r': 329 return (!doaccess(left, R_OK)); 330 case 's': 331 return !((st = getstat(left)) && !!(st->st_size)); 332 case 'S': 333 return (!S_ISSOCK(dostat(left))); 334 case 'u': 335 return (!(dostat(left) & S_ISUID)); 336 case 'w': 337 return (!doaccess(left, W_OK)); 338 case 'x': 339 if (privasserted()) { 340 mode_t mode = dostat(left); 341 return !((mode & S_IXUGO) || S_ISDIR(mode)); 342 } 343 return !doaccess(left, X_OK); 344 case 'z': 345 return !!(strlen(left)); 346 case 'h': 347 case 'L': 348 return (!S_ISLNK(dolstat(left))); 349 case 'O': 350 return !((st = getstat(left)) && st->st_uid == geteuid()); 351 case 'G': 352 return !((st = getstat(left)) && st->st_gid == getegid()); 353 case 'N': 354#if defined(GET_ST_MTIME_NSEC) && defined(GET_ST_ATIME_NSEC) 355 if (!(st = getstat(left))) 356 return 1; 357 return (st->st_atime == st->st_mtime) ? 358 GET_ST_ATIME_NSEC(*st) > GET_ST_MTIME_NSEC(*st) : 359 st->st_atime > st->st_mtime; 360#else 361 return !((st = getstat(left)) && st->st_atime <= st->st_mtime); 362#endif 363 case 't': 364 return !isatty(mathevali(left)); 365 case COND_NT: 366 case COND_OT: 367 { 368 time_t a; 369#ifdef GET_ST_MTIME_NSEC 370 long nsecs; 371#endif 372 373 if (!(st = getstat(left))) 374 return 1; 375 a = st->st_mtime; 376#ifdef GET_ST_MTIME_NSEC 377 nsecs = GET_ST_MTIME_NSEC(*st); 378#endif 379 if (!(st = getstat(right))) 380 return 1; 381#ifdef GET_ST_MTIME_NSEC 382 if (a == st->st_mtime) { 383 return !((ctype == COND_NT) ? nsecs > GET_ST_MTIME_NSEC(*st) : 384 nsecs < GET_ST_MTIME_NSEC(*st)); 385 } 386#endif 387 return !((ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime); 388 } 389 case COND_EF: 390 { 391 dev_t d; 392 ino_t i; 393 394 if (!(st = getstat(left))) 395 return 1; 396 d = st->st_dev; 397 i = st->st_ino; 398 if (!(st = getstat(right))) 399 return 1; 400 return !(d == st->st_dev && i == st->st_ino); 401 } 402 default: 403 zwarnnam(fromtest, "bad cond code"); 404 return 2; 405 } 406} 407 408 409/**/ 410static int 411doaccess(char *s, int c) 412{ 413#ifdef HAVE_FACCESSX 414 if (!strncmp(s, "/dev/fd/", 8)) 415 return !faccessx(atoi(s + 8), c, ACC_SELF); 416#endif 417 return !access(unmeta(s), c); 418} 419 420 421static struct stat st; 422 423/**/ 424static struct stat * 425getstat(char *s) 426{ 427 char *us; 428 429/* /dev/fd/n refers to the open file descriptor n. We always use fstat * 430 * in this case since on Solaris /dev/fd/n is a device special file */ 431 if (!strncmp(s, "/dev/fd/", 8)) { 432 if (fstat(atoi(s + 8), &st)) 433 return NULL; 434 return &st; 435 } 436 437 if (!(us = unmeta(s))) 438 return NULL; 439 if (stat(us, &st)) 440 return NULL; 441 return &st; 442} 443 444 445/**/ 446static mode_t 447dostat(char *s) 448{ 449 struct stat *statp; 450 451 if (!(statp = getstat(s))) 452 return 0; 453 return statp->st_mode; 454} 455 456 457/* pem@aaii.oz; needed since dostat now uses "stat" */ 458 459/**/ 460static mode_t 461dolstat(char *s) 462{ 463 if (lstat(unmeta(s), &st) < 0) 464 return 0; 465 return st.st_mode; 466} 467 468 469/* 470 * optison returns evalcond-friendly statuses (true, false, error). 471 */ 472 473/**/ 474static int 475optison(char *name, char *s) 476{ 477 int i; 478 479 if (strlen(s) == 1) 480 i = optlookupc(*s); 481 else 482 i = optlookup(s); 483 if (!i) { 484 zwarnnam(name, "no such option: %s", s); 485 return 2; 486 } else if(i < 0) 487 return !unset(-i); 488 else 489 return !isset(i); 490} 491 492/**/ 493mod_export char * 494cond_str(char **args, int num, int raw) 495{ 496 char *s = args[num]; 497 498 if (has_token(s)) { 499 singsub(&s); 500 if (!raw) 501 untokenize(s); 502 } 503 return s; 504} 505 506/**/ 507mod_export zlong 508cond_val(char **args, int num) 509{ 510 char *s = args[num]; 511 512 if (has_token(s)) { 513 singsub(&s); 514 untokenize(s); 515 } 516 return mathevali(s); 517} 518 519/**/ 520mod_export int 521cond_match(char **args, int num, char *str) 522{ 523 char *s = args[num]; 524 525 singsub(&s); 526 527 return matchpat(str, s); 528} 529 530/**/ 531static void 532tracemodcond(char *name, char **args, int inf) 533{ 534 char **aptr; 535 536 args = arrdup(args); 537 for (aptr = args; *aptr; aptr++) 538 untokenize(*aptr); 539 if (inf) { 540 fprintf(xtrerr, " %s %s %s", args[0], name, args[1]); 541 } else { 542 fprintf(xtrerr, " %s", name); 543 while (*args) 544 fprintf(xtrerr, " %s", *args++); 545 } 546} 547