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