1 /* 2 * sh.exp.c: Expression evaluations 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "tw.h" 34 35 /* 36 * C shell 37 */ 38 39 #define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */ 40 #define TEXP_NOGLOB 2 /* in ignore, it means not to globone */ 41 42 #define ADDOP 1 43 #define MULOP 2 44 #define EQOP 4 45 #define RELOP 8 46 #define RESTOP 16 47 #define ANYOP 31 48 49 #define EQEQ 1 50 #define GTR 2 51 #define LSS 4 52 #define NOTEQ 6 53 #define EQMATCH 7 54 #define NOTEQMATCH 8 55 56 static int sh_access (const Char *, int); 57 static tcsh_number_t exp1 (Char ***, int); 58 static tcsh_number_t exp2x (Char ***, int); 59 static tcsh_number_t exp2a (Char ***, int); 60 static tcsh_number_t exp2b (Char ***, int); 61 static tcsh_number_t exp2c (Char ***, int); 62 static Char *exp3 (Char ***, int); 63 static Char *exp3a (Char ***, int); 64 static Char *exp4 (Char ***, int); 65 static Char *exp5 (Char ***, int); 66 static Char *exp6 (Char ***, int); 67 static void evalav (Char **); 68 static int isa (Char *, int); 69 static tcsh_number_t egetn (const Char *); 70 71 #ifdef EDEBUG 72 static void etracc (const char *, const Char *, Char ***); 73 static void etraci (const char *, tcsh_number_t, Char ***); 74 #else /* !EDEBUG */ 75 #define etracc(A, B, C) ((void)0) 76 #define etraci(A, B, C) ((void)0) 77 #endif /* !EDEBUG */ 78 79 /* 80 * shell access function according to POSIX and non POSIX 81 * From Beto Appleton (beto@aixwiz.aix.ibm.com) 82 */ 83 static int 84 sh_access(const Char *fname, int mode) 85 { 86 #if defined(POSIX) && !defined(USE_ACCESS) 87 struct stat statb; 88 #endif /* POSIX */ 89 char *name = short2str(fname); 90 91 if (*name == '\0') 92 return 1; 93 94 #if !defined(POSIX) || defined(USE_ACCESS) 95 return access(name, mode); 96 #else /* POSIX */ 97 98 /* 99 * POSIX 1003.2-d11.2 100 * -r file True if file exists and is readable. 101 * -w file True if file exists and is writable. 102 * True shall indicate only that the write flag is on. 103 * The file shall not be writable on a read-only file 104 * system even if this test indicates true. 105 * -x file True if file exists and is executable. 106 * True shall indicate only that the execute flag is on. 107 * If file is a directory, true indicates that the file 108 * can be searched. 109 */ 110 if (mode != W_OK && mode != X_OK) 111 return access(name, mode); 112 113 if (stat(name, &statb) == -1) 114 return 1; 115 116 if (access(name, mode) == 0) { 117 #ifdef S_ISDIR 118 if (S_ISDIR(statb.st_mode) && mode == X_OK) 119 return 0; 120 #endif /* S_ISDIR */ 121 122 /* root needs permission for someone */ 123 switch (mode) { 124 case W_OK: 125 mode = S_IWUSR | S_IWGRP | S_IWOTH; 126 break; 127 case X_OK: 128 mode = S_IXUSR | S_IXGRP | S_IXOTH; 129 break; 130 default: 131 abort(); 132 break; 133 } 134 135 } 136 137 else if (euid == statb.st_uid) 138 mode <<= 6; 139 140 else if (egid == statb.st_gid) 141 mode <<= 3; 142 143 # ifdef NGROUPS_MAX 144 else { 145 /* you can be in several groups */ 146 long n; 147 GETGROUPS_T *groups; 148 149 /* 150 * Try these things to find a positive maximum groups value: 151 * 1) sysconf(_SC_NGROUPS_MAX) 152 * 2) NGROUPS_MAX 153 * 3) getgroups(0, unused) 154 * Then allocate and scan the groups array if one of these worked. 155 */ 156 # if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX) 157 if ((n = sysconf(_SC_NGROUPS_MAX)) == -1) 158 # endif /* _SC_NGROUPS_MAX */ 159 n = NGROUPS_MAX; 160 if (n <= 0) 161 n = getgroups(0, (GETGROUPS_T *) NULL); 162 163 if (n > 0) { 164 groups = xmalloc(n * sizeof(*groups)); 165 n = getgroups((int) n, groups); 166 while (--n >= 0) 167 if (groups[n] == statb.st_gid) { 168 mode <<= 3; 169 break; 170 } 171 xfree(groups); 172 } 173 } 174 # endif /* NGROUPS_MAX */ 175 176 if (statb.st_mode & mode) 177 return 0; 178 else 179 return 1; 180 #endif /* !POSIX */ 181 } 182 183 tcsh_number_t 184 expr(Char ***vp) 185 { 186 return (exp0(vp, 0)); 187 } 188 189 tcsh_number_t 190 exp0(Char ***vp, int ignore) 191 { 192 tcsh_number_t p1 = exp1(vp, ignore); 193 194 etraci("exp0 p1", p1, vp); 195 while (**vp && eq(**vp, STRor2)) { 196 int p2; 197 198 (*vp)++; 199 200 p2 = compat_expr ? 201 exp0(vp, (ignore & TEXP_IGNORE) || p1) : 202 exp1(vp, (ignore & TEXP_IGNORE) || p1); 203 if (compat_expr || !(ignore & TEXP_IGNORE)) 204 p1 = (p1 || p2); 205 etraci("exp0 p1", p1, vp); 206 if (compat_expr) 207 break; 208 } 209 return (p1); 210 } 211 212 static tcsh_number_t 213 exp1(Char ***vp, int ignore) 214 { 215 tcsh_number_t p1 = exp2x(vp, ignore); 216 217 etraci("exp1 p1", p1, vp); 218 while (**vp && eq(**vp, STRand2)) { 219 tcsh_number_t p2; 220 221 (*vp)++; 222 p2 = compat_expr ? 223 exp1(vp, (ignore & TEXP_IGNORE) || !p1) : 224 exp2x(vp, (ignore & TEXP_IGNORE) || !p1); 225 226 etraci("exp1 p2", p2, vp); 227 if (compat_expr || !(ignore & TEXP_IGNORE)) 228 p1 = (p1 && p2); 229 etraci("exp1 p1", p1, vp); 230 if (compat_expr) 231 break; 232 } 233 return (p1); 234 } 235 236 static tcsh_number_t 237 exp2x(Char ***vp, int ignore) 238 { 239 tcsh_number_t p1 = exp2a(vp, ignore); 240 241 etraci("exp2x p1", p1, vp); 242 while (**vp && eq(**vp, STRor)) { 243 tcsh_number_t p2; 244 245 (*vp)++; 246 p2 = compat_expr ? 247 exp2x(vp, ignore) : 248 exp2a(vp, ignore); 249 etraci("exp2x p2", p2, vp); 250 if (compat_expr || !(ignore & TEXP_IGNORE)) 251 p1 = (p1 | p2); 252 etraci("exp2x p1", p1, vp); 253 if (compat_expr) 254 break; 255 } 256 return (p1); 257 } 258 259 static tcsh_number_t 260 exp2a(Char ***vp, int ignore) 261 { 262 tcsh_number_t p1 = exp2b(vp, ignore); 263 264 etraci("exp2a p1", p1, vp); 265 while (**vp && eq(**vp, STRcaret)) { 266 tcsh_number_t p2; 267 268 (*vp)++; 269 p2 = compat_expr ? 270 exp2a(vp, ignore) : 271 exp2b(vp, ignore); 272 etraci("exp2a p2", p2, vp); 273 if (compat_expr || !(ignore & TEXP_IGNORE)) 274 p1 = (p1 ^ p2); 275 etraci("exp2a p1", p1, vp); 276 if (compat_expr) 277 break; 278 } 279 return (p1); 280 } 281 282 static tcsh_number_t 283 exp2b(Char ***vp, int ignore) 284 { 285 tcsh_number_t p1 = exp2c(vp, ignore); 286 287 etraci("exp2b p1", p1, vp); 288 while (**vp && eq(**vp, STRand)) { 289 tcsh_number_t p2; 290 291 (*vp)++; 292 p2 = compat_expr ? 293 exp2b(vp, ignore) : 294 exp2c(vp, ignore); 295 etraci("exp2b p2", p2, vp); 296 if (compat_expr || !(ignore & TEXP_IGNORE)) 297 p1 = (p1 & p2); 298 etraci("exp2b p1", p1, vp); 299 if (compat_expr) 300 break; 301 } 302 return (p1); 303 } 304 305 static tcsh_number_t 306 exp2c(Char ***vp, int ignore) 307 { 308 Char *p1 = exp3(vp, ignore); 309 Char *p2; 310 tcsh_number_t i; 311 312 cleanup_push(p1, xfree); 313 etracc("exp2c p1", p1, vp); 314 if ((i = isa(**vp, EQOP)) != 0) { 315 (*vp)++; 316 if (i == EQMATCH || i == NOTEQMATCH) 317 ignore |= TEXP_NOGLOB; 318 p2 = exp3(vp, ignore); 319 cleanup_push(p2, xfree); 320 etracc("exp2c p2", p2, vp); 321 if (!(ignore & TEXP_IGNORE)) 322 switch ((int)i) { 323 324 case EQEQ: 325 i = eq(p1, p2); 326 break; 327 328 case NOTEQ: 329 i = !eq(p1, p2); 330 break; 331 332 case EQMATCH: 333 i = Gmatch(p1, p2); 334 break; 335 336 case NOTEQMATCH: 337 i = !Gmatch(p1, p2); 338 break; 339 } 340 cleanup_until(p1); 341 return (i); 342 } 343 i = egetn(p1); 344 cleanup_until(p1); 345 return (i); 346 } 347 348 static Char * 349 exp3(Char ***vp, int ignore) 350 { 351 Char *p1, *p2; 352 tcsh_number_t i; 353 354 p1 = exp3a(vp, ignore); 355 etracc("exp3 p1", p1, vp); 356 while ((i = isa(**vp, RELOP)) != 0) { 357 (*vp)++; 358 if (**vp && eq(**vp, STRequal)) 359 i |= 1, (*vp)++; 360 cleanup_push(p1, xfree); 361 p2 = compat_expr ? 362 exp3(vp, ignore) : 363 exp3a(vp, ignore); 364 cleanup_push(p2, xfree); 365 etracc("exp3 p2", p2, vp); 366 if (!(ignore & TEXP_IGNORE)) 367 switch ((int)i) { 368 369 case GTR: 370 i = egetn(p1) > egetn(p2); 371 break; 372 373 case GTR | 1: 374 i = egetn(p1) >= egetn(p2); 375 break; 376 377 case LSS: 378 i = egetn(p1) < egetn(p2); 379 break; 380 381 case LSS | 1: 382 i = egetn(p1) <= egetn(p2); 383 break; 384 } 385 cleanup_until(p1); 386 p1 = putn(i); 387 etracc("exp3 p1", p1, vp); 388 if (compat_expr) 389 break; 390 } 391 return (p1); 392 } 393 394 static Char * 395 exp3a(Char ***vp, int ignore) 396 { 397 Char *p1, *p2; 398 const Char *op; 399 tcsh_number_t i; 400 401 p1 = exp4(vp, ignore); 402 etracc("exp3a p1", p1, vp); 403 op = **vp; 404 if (op && any("<>", op[0]) && op[0] == op[1]) { 405 (*vp)++; 406 cleanup_push(p1, xfree); 407 p2 = compat_expr ? 408 exp3a(vp, ignore) : 409 exp4(vp, ignore); 410 cleanup_push(p2, xfree); 411 etracc("exp3a p2", p2, vp); 412 if (op[0] == '<') 413 i = egetn(p1) << egetn(p2); 414 else 415 i = egetn(p1) >> egetn(p2); 416 cleanup_until(p1); 417 p1 = putn(i); 418 etracc("exp3a p1", p1, vp); 419 } 420 return (p1); 421 } 422 423 static Char * 424 exp4(Char ***vp, int ignore) 425 { 426 Char *p1, *p2; 427 tcsh_number_t i = 0; 428 429 p1 = exp5(vp, ignore); 430 etracc("exp4 p1", p1, vp); 431 while (isa(**vp, ADDOP)) { 432 const Char *op = *(*vp)++; 433 434 cleanup_push(p1, xfree); 435 p2 = compat_expr ? 436 exp4(vp, ignore) : 437 exp5(vp, ignore); 438 cleanup_push(p2, xfree); 439 etracc("exp4 p2", p2, vp); 440 if (!(ignore & TEXP_IGNORE)) 441 switch (op[0]) { 442 443 case '+': 444 i = egetn(p1) + egetn(p2); 445 break; 446 447 case '-': 448 i = egetn(p1) - egetn(p2); 449 break; 450 } 451 cleanup_until(p1); 452 p1 = putn(i); 453 etracc("exp4 p1", p1, vp); 454 if (compat_expr) 455 break; 456 } 457 return (p1); 458 } 459 460 static Char * 461 exp5(Char ***vp, int ignore) 462 { 463 Char *p1, *p2; 464 tcsh_number_t i = 0; 465 466 p1 = exp6(vp, ignore); 467 etracc("exp5 p1", p1, vp); 468 469 while (isa(**vp, MULOP)) { 470 const Char *op = *(*vp)++; 471 if ((ignore & TEXP_NOGLOB) != 0) { 472 /* 473 * We are just trying to get the right side of 474 * a =~ or !~ operator 475 */ 476 xfree(p1); 477 return Strsave(op); 478 } 479 480 cleanup_push(p1, xfree); 481 p2 = compat_expr ? 482 exp5(vp, ignore) : 483 exp6(vp, ignore); 484 cleanup_push(p2, xfree); 485 etracc("exp5 p2", p2, vp); 486 if (!(ignore & TEXP_IGNORE)) 487 switch (op[0]) { 488 489 case '*': 490 i = egetn(p1) * egetn(p2); 491 break; 492 493 case '/': 494 i = egetn(p2); 495 if (i == 0) 496 stderror(ERR_DIV0); 497 i = egetn(p1) / i; 498 break; 499 500 case '%': 501 i = egetn(p2); 502 if (i == 0) 503 stderror(ERR_MOD0); 504 i = egetn(p1) % i; 505 break; 506 } 507 cleanup_until(p1); 508 p1 = putn(i); 509 etracc("exp5 p1", p1, vp); 510 if (compat_expr) 511 break; 512 } 513 return (p1); 514 } 515 516 static Char * 517 exp6(Char ***vp, int ignore) 518 { 519 tcsh_number_t ccode; 520 tcsh_number_t i = 0; 521 Char *cp; 522 523 if (**vp == 0) 524 stderror(ERR_NAME | ERR_EXPRESSION); 525 if (eq(**vp, STRbang)) { 526 (*vp)++; 527 cp = exp6(vp, ignore); 528 cleanup_push(cp, xfree); 529 etracc("exp6 ! cp", cp, vp); 530 i = egetn(cp); 531 cleanup_until(cp); 532 return (putn(!i)); 533 } 534 if (eq(**vp, STRtilde)) { 535 (*vp)++; 536 cp = exp6(vp, ignore); 537 cleanup_push(cp, xfree); 538 etracc("exp6 ~ cp", cp, vp); 539 i = egetn(cp); 540 cleanup_until(cp); 541 return (putn(~i)); 542 } 543 if (eq(**vp, STRLparen)) { 544 (*vp)++; 545 ccode = exp0(vp, ignore); 546 etraci("exp6 () ccode", ccode, vp); 547 if (**vp == 0 || ***vp != ')') 548 stderror(ERR_NAME | ERR_EXPRESSION); 549 (*vp)++; 550 return (putn(ccode)); 551 } 552 if (eq(**vp, STRLbrace)) { 553 Char **v; 554 struct command faket; 555 Char *fakecom[2]; 556 557 faket.t_dtyp = NODE_COMMAND; 558 faket.t_dflg = F_BACKQ; 559 faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL; 560 faket.t_dcom = fakecom; 561 fakecom[0] = STRfakecom; 562 fakecom[1] = NULL; 563 (*vp)++; 564 v = *vp; 565 for (;;) { 566 if (!**vp) 567 stderror(ERR_NAME | ERR_MISSING, '}'); 568 if (eq(*(*vp)++, STRRbrace)) 569 break; 570 } 571 if (ignore & TEXP_IGNORE) 572 return (Strsave(STRNULL)); 573 psavejob(); 574 cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */ 575 if (pfork(&faket, -1) == 0) { 576 *--(*vp) = 0; 577 evalav(v); 578 exitstat(); 579 } 580 pwait(); 581 cleanup_until(&faket); 582 etraci("exp6 {} status", egetn(varval(STRstatus)), vp); 583 return (putn(egetn(varval(STRstatus)) == 0)); 584 } 585 if (isa(**vp, ANYOP)) 586 return (Strsave(STRNULL)); 587 cp = *(*vp)++; 588 #ifdef convex 589 # define FILETESTS "erwxfdzoplstSXLbcugkmKR" 590 #else 591 # define FILETESTS "erwxfdzoplstSXLbcugkmK" 592 #endif /* convex */ 593 #define FILEVALS "ZAMCDIUGNFPL" 594 if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1]))) 595 return(filetest(cp, vp, ignore)); 596 etracc("exp6 default", cp, vp); 597 return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND)); 598 } 599 600 601 /* 602 * Extended file tests 603 * From: John Rowe <rowe@excc.exeter.ac.uk> 604 */ 605 Char * 606 filetest(Char *cp, Char ***vp, int ignore) 607 { 608 #ifdef convex 609 struct cvxstat stb, *st = NULL; 610 # define TCSH_STAT stat64 611 #else 612 # define TCSH_STAT stat 613 struct stat stb, *st = NULL; 614 #endif /* convex */ 615 616 #ifdef S_IFLNK 617 # ifdef convex 618 struct cvxstat lstb, *lst = NULL; 619 # define TCSH_LSTAT lstat64 620 # else 621 # define TCSH_LSTAT lstat 622 struct stat lstb, *lst = NULL; 623 # endif /* convex */ 624 char *filnam; 625 #endif /* S_IFLNK */ 626 627 tcsh_number_t i = 0; 628 unsigned pmask = 0xffff; 629 int altout = 0; 630 Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0', 631 *errval = STR0; 632 char *string, string0[22 + MB_LEN_MAX + 1]; /* space for 64 bit octal */ 633 time_t footime; 634 struct passwd *pw; 635 struct group *gr; 636 637 while (any(FILETESTS, *++ft)) 638 continue; 639 640 if (!*ft && *(ft - 1) == 'L') 641 --ft; 642 643 if (any(FILEVALS, *ft)) { 644 valtest = *ft++; 645 /* 646 * Value tests return '-1' on failure as 0 is 647 * a legitimate value for many of them. 648 * 'F' returns ':' for compatibility. 649 */ 650 errval = valtest == 'F' ? STRcolon : STRminus1; 651 652 if (valtest == 'P' && *ft >= '0' && *ft <= '7') { 653 pmask = (char) *ft - '0'; 654 while ( *++ft >= '0' && *ft <= '7' ) 655 pmask = 8 * pmask + ((char) *ft - '0'); 656 } 657 if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) { 658 altout = 1; 659 ++ft; 660 } 661 } 662 663 if (*ft || ft == cp + 1) 664 stderror(ERR_NAME | ERR_FILEINQ); 665 666 /* 667 * Detect missing file names by checking for operator in the file name 668 * position. However, if an operator name appears there, we must make 669 * sure that there's no file by that name (e.g., "/") before announcing 670 * an error. Even this check isn't quite right, since it doesn't take 671 * globbing into account. 672 */ 673 674 if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb)) 675 stderror(ERR_NAME | ERR_FILENAME); 676 677 dp = *(*vp)++; 678 if (ignore & TEXP_IGNORE) 679 return (Strsave(STRNULL)); 680 ep = globone(dp, G_APPEND); 681 cleanup_push(ep, xfree); 682 ft = &cp[1]; 683 do 684 switch (*ft) { 685 686 case 'r': 687 i = !sh_access(ep, R_OK); 688 break; 689 690 case 'w': 691 i = !sh_access(ep, W_OK); 692 break; 693 694 case 'x': 695 i = !sh_access(ep, X_OK); 696 break; 697 698 case 'X': /* tcsh extension, name is an executable in the path 699 * or a tcsh builtin command 700 */ 701 i = find_cmd(ep, 0); 702 break; 703 704 case 't': /* SGI extension, true when file is a tty */ 705 i = isatty(atoi(short2str(ep))); 706 break; 707 708 default: 709 710 #ifdef S_IFLNK 711 if (tolower(*ft) == 'l') { 712 /* 713 * avoid convex compiler bug. 714 */ 715 if (!lst) { 716 lst = &lstb; 717 if (TCSH_LSTAT(short2str(ep), lst) == -1) { 718 cleanup_until(ep); 719 return (Strsave(errval)); 720 } 721 } 722 if (*ft == 'L') 723 st = lst; 724 } 725 else 726 #endif /* S_IFLNK */ 727 /* 728 * avoid convex compiler bug. 729 */ 730 if (!st) { 731 st = &stb; 732 if (TCSH_STAT(short2str(ep), st) == -1) { 733 cleanup_until(ep); 734 return (Strsave(errval)); 735 } 736 } 737 738 switch (*ft) { 739 740 case 'f': 741 #ifdef S_ISREG 742 i = S_ISREG(st->st_mode); 743 #else /* !S_ISREG */ 744 i = 0; 745 #endif /* S_ISREG */ 746 break; 747 748 case 'd': 749 #ifdef S_ISDIR 750 i = S_ISDIR(st->st_mode); 751 #else /* !S_ISDIR */ 752 i = 0; 753 #endif /* S_ISDIR */ 754 break; 755 756 case 'p': 757 #ifdef S_ISFIFO 758 i = S_ISFIFO(st->st_mode); 759 #else /* !S_ISFIFO */ 760 i = 0; 761 #endif /* S_ISFIFO */ 762 break; 763 764 case 'm' : 765 #ifdef S_ISOFL 766 i = S_ISOFL(st->st_dm_mode); 767 #else /* !S_ISOFL */ 768 i = 0; 769 #endif /* S_ISOFL */ 770 break ; 771 772 case 'K' : 773 #ifdef S_ISOFL 774 i = stb.st_dm_key; 775 #else /* !S_ISOFL */ 776 i = 0; 777 #endif /* S_ISOFL */ 778 break ; 779 780 781 case 'l': 782 #ifdef S_ISLNK 783 i = S_ISLNK(lst->st_mode); 784 #else /* !S_ISLNK */ 785 i = 0; 786 #endif /* S_ISLNK */ 787 break; 788 789 case 'S': 790 # ifdef S_ISSOCK 791 i = S_ISSOCK(st->st_mode); 792 # else /* !S_ISSOCK */ 793 i = 0; 794 # endif /* S_ISSOCK */ 795 break; 796 797 case 'b': 798 #ifdef S_ISBLK 799 i = S_ISBLK(st->st_mode); 800 #else /* !S_ISBLK */ 801 i = 0; 802 #endif /* S_ISBLK */ 803 break; 804 805 case 'c': 806 #ifdef S_ISCHR 807 i = S_ISCHR(st->st_mode); 808 #else /* !S_ISCHR */ 809 i = 0; 810 #endif /* S_ISCHR */ 811 break; 812 813 case 'u': 814 i = (S_ISUID & st->st_mode) != 0; 815 break; 816 817 case 'g': 818 i = (S_ISGID & st->st_mode) != 0; 819 break; 820 821 case 'k': 822 i = (S_ISVTX & st->st_mode) != 0; 823 break; 824 825 case 'z': 826 i = st->st_size == 0; 827 break; 828 829 #ifdef convex 830 case 'R': 831 i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED; 832 break; 833 #endif /* convex */ 834 835 case 's': 836 i = stb.st_size != 0; 837 break; 838 839 case 'e': 840 i = 1; 841 break; 842 843 case 'o': 844 i = st->st_uid == uid; 845 break; 846 847 /* 848 * Value operators are a tcsh extension. 849 */ 850 851 case 'D': 852 i = (tcsh_number_t) st->st_dev; 853 break; 854 855 case 'I': 856 i = (tcsh_number_t) st->st_ino; 857 break; 858 859 case 'F': 860 strdev = putn( (int) st->st_dev); 861 strino = putn( (int) st->st_ino); 862 strF = xmalloc((2 + Strlen(strdev) + Strlen(strino)) 863 * sizeof(Char)); 864 (void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino); 865 xfree(strdev); 866 xfree(strino); 867 cleanup_until(ep); 868 return(strF); 869 870 case 'L': 871 if ( *(ft + 1) ) { 872 i = 1; 873 break; 874 } 875 #ifdef S_ISLNK 876 filnam = short2str(ep); 877 string = areadlink(filnam); 878 strF = string == NULL ? errval : str2short(string); 879 xfree(string); 880 cleanup_until(ep); 881 return(Strsave(strF)); 882 883 #else /* !S_ISLNK */ 884 i = 0; 885 break; 886 #endif /* S_ISLNK */ 887 888 889 case 'N': 890 i = (tcsh_number_t) st->st_nlink; 891 break; 892 893 case 'P': 894 string = string0 + 1; 895 (void) xsnprintf(string, sizeof(string0) - 1, "%o", 896 pmask & (unsigned int) 897 ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode)); 898 if (altout && *string != '0') 899 *--string = '0'; 900 cleanup_until(ep); 901 return(Strsave(str2short(string))); 902 903 case 'U': 904 if (altout && (pw = xgetpwuid(st->st_uid))) { 905 cleanup_until(ep); 906 return(Strsave(str2short(pw->pw_name))); 907 } 908 i = (tcsh_number_t) st->st_uid; 909 break; 910 911 case 'G': 912 if (altout && (gr = xgetgrgid(st->st_gid))) { 913 cleanup_until(ep); 914 return(Strsave(str2short(gr->gr_name))); 915 } 916 i = (tcsh_number_t) st->st_gid; 917 break; 918 919 case 'Z': 920 i = (tcsh_number_t) st->st_size; 921 break; 922 923 case 'A': case 'M': case 'C': 924 footime = *ft == 'A' ? st->st_atime : 925 *ft == 'M' ? st->st_mtime : st->st_ctime; 926 if (altout) { 927 strF = str2short(ctime(&footime)); 928 if ((str = Strchr(strF, '\n')) != NULL) 929 *str = (Char) '\0'; 930 cleanup_until(ep); 931 return(Strsave(strF)); 932 } 933 i = (tcsh_number_t) footime; 934 break; 935 936 } 937 } 938 while (*++ft && i); 939 etraci("exp6 -? i", i, vp); 940 cleanup_until(ep); 941 return (putn(i)); 942 } 943 944 945 static void 946 evalav(Char **v) 947 { 948 struct wordent paraml1; 949 struct wordent *hp = ¶ml1; 950 struct command *t; 951 struct wordent *wdp = hp; 952 953 setcopy(STRstatus, STR0, VAR_READWRITE); 954 initlex(hp); 955 while (*v) { 956 struct wordent *new = xcalloc(1, sizeof *wdp); 957 958 new->prev = wdp; 959 new->next = hp; 960 wdp->next = new; 961 wdp = new; 962 wdp->word = Strsave(*v++); 963 } 964 hp->prev = wdp; 965 cleanup_push(¶ml1, lex_cleanup); 966 alias(¶ml1); 967 t = syntax(paraml1.next, ¶ml1, 0); 968 cleanup_push(t, syntax_cleanup); 969 if (seterr) 970 stderror(ERR_OLD); 971 execute(t, -1, NULL, NULL, TRUE); 972 cleanup_until(¶ml1); 973 } 974 975 static int 976 isa(Char *cp, int what) 977 { 978 if (cp == 0) 979 return ((what & RESTOP) != 0); 980 if (*cp == '\0') 981 return 0; 982 if (cp[1] == 0) { 983 if (what & ADDOP && (*cp == '+' || *cp == '-')) 984 return (1); 985 if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) 986 return (1); 987 if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || 988 *cp == '~' || *cp == '^' || *cp == '"')) 989 return (1); 990 } 991 else if (cp[2] == 0) { 992 if (what & RESTOP) { 993 if (cp[0] == '|' && cp[1] == '&') 994 return (1); 995 if (cp[0] == '<' && cp[1] == '<') 996 return (1); 997 if (cp[0] == '>' && cp[1] == '>') 998 return (1); 999 } 1000 if (what & EQOP) { 1001 if (cp[0] == '=') { 1002 if (cp[1] == '=') 1003 return (EQEQ); 1004 if (cp[1] == '~') 1005 return (EQMATCH); 1006 } 1007 else if (cp[0] == '!') { 1008 if (cp[1] == '=') 1009 return (NOTEQ); 1010 if (cp[1] == '~') 1011 return (NOTEQMATCH); 1012 } 1013 } 1014 } 1015 if (what & RELOP) { 1016 if (*cp == '<') 1017 return (LSS); 1018 if (*cp == '>') 1019 return (GTR); 1020 } 1021 return (0); 1022 } 1023 1024 static tcsh_number_t 1025 egetn(const Char *cp) 1026 { 1027 if (*cp && *cp != '-' && !Isdigit(*cp)) 1028 stderror(ERR_NAME | ERR_EXPRESSION); 1029 return (getn(cp)); 1030 } 1031 1032 /* Phew! */ 1033 1034 #ifdef EDEBUG 1035 static void 1036 etraci(const char *str, tcsh_number_t i, Char ***vp) 1037 { 1038 #ifdef HAVE_LONG_LONG 1039 xprintf("%s=%lld\t", str, i); 1040 #else 1041 xprintf("%s=%ld\t", str, i); 1042 #endif 1043 blkpr(*vp); 1044 xputchar('\n'); 1045 } 1046 static void 1047 etracc(const char *str, const Char *cp, Char ***vp) 1048 { 1049 xprintf("%s=%S\t", str, cp); 1050 blkpr(*vp); 1051 xputchar('\n'); 1052 } 1053 #endif /* EDEBUG */ 1054