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