159243Sobrien/* 259243Sobrien * sh.exp.c: Expression evaluations 359243Sobrien */ 459243Sobrien/*- 559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 16100616Smp * 3. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien#include "sh.h" 33100616Smp#include "tw.h" 34100616Smp 3559243Sobrien/* 3659243Sobrien * C shell 3759243Sobrien */ 3859243Sobrien 3959243Sobrien#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */ 4059243Sobrien#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */ 4159243Sobrien 4259243Sobrien#define ADDOP 1 4359243Sobrien#define MULOP 2 4459243Sobrien#define EQOP 4 4559243Sobrien#define RELOP 8 4659243Sobrien#define RESTOP 16 4759243Sobrien#define ANYOP 31 4859243Sobrien 4959243Sobrien#define EQEQ 1 5059243Sobrien#define GTR 2 5159243Sobrien#define LSS 4 5259243Sobrien#define NOTEQ 6 5359243Sobrien#define EQMATCH 7 5459243Sobrien#define NOTEQMATCH 8 5559243Sobrien 56231990Smpstatic int sh_access (const Char *, int); 57231990Smpstatic tcsh_number_t exp1 (Char ***, int); 58231990Smpstatic tcsh_number_t exp2x (Char ***, int); 59231990Smpstatic tcsh_number_t exp2a (Char ***, int); 60231990Smpstatic tcsh_number_t exp2b (Char ***, int); 61231990Smpstatic tcsh_number_t exp2c (Char ***, int); 62231990Smpstatic Char *exp3 (Char ***, int); 63231990Smpstatic Char *exp3a (Char ***, int); 64231990Smpstatic Char *exp4 (Char ***, int); 65231990Smpstatic Char *exp5 (Char ***, int); 66231990Smpstatic Char *exp6 (Char ***, int); 67231990Smpstatic void evalav (Char **); 68231990Smpstatic int isa (Char *, int); 69231990Smpstatic tcsh_number_t egetn (const Char *); 7059243Sobrien 7159243Sobrien#ifdef EDEBUG 72231990Smpstatic void etracc (const char *, const Char *, Char ***); 73231990Smpstatic void etraci (const char *, tcsh_number_t, Char ***); 74167465Smp#else /* !EDEBUG */ 75167465Smp#define etracc(A, B, C) ((void)0) 76167465Smp#define etraci(A, B, C) ((void)0) 77167465Smp#endif /* !EDEBUG */ 7859243Sobrien 7959243Sobrien/* 8059243Sobrien * shell access function according to POSIX and non POSIX 8159243Sobrien * From Beto Appleton (beto@aixwiz.aix.ibm.com) 8259243Sobrien */ 8359243Sobrienstatic int 84167465Smpsh_access(const Char *fname, int mode) 8559243Sobrien{ 8659243Sobrien#if defined(POSIX) && !defined(USE_ACCESS) 8759243Sobrien struct stat statb; 8859243Sobrien#endif /* POSIX */ 8959243Sobrien char *name = short2str(fname); 9059243Sobrien 9159243Sobrien if (*name == '\0') 9259243Sobrien return 1; 9359243Sobrien 9459243Sobrien#if !defined(POSIX) || defined(USE_ACCESS) 9559243Sobrien return access(name, mode); 9659243Sobrien#else /* POSIX */ 9759243Sobrien 9859243Sobrien /* 9959243Sobrien * POSIX 1003.2-d11.2 10059243Sobrien * -r file True if file exists and is readable. 10159243Sobrien * -w file True if file exists and is writable. 10259243Sobrien * True shall indicate only that the write flag is on. 10359243Sobrien * The file shall not be writable on a read-only file 10459243Sobrien * system even if this test indicates true. 10559243Sobrien * -x file True if file exists and is executable. 10659243Sobrien * True shall indicate only that the execute flag is on. 10759243Sobrien * If file is a directory, true indicates that the file 10859243Sobrien * can be searched. 10959243Sobrien */ 11059243Sobrien if (mode != W_OK && mode != X_OK) 11159243Sobrien return access(name, mode); 11259243Sobrien 11359243Sobrien if (stat(name, &statb) == -1) 11459243Sobrien return 1; 11559243Sobrien 11659243Sobrien if (access(name, mode) == 0) { 11759243Sobrien#ifdef S_ISDIR 11859243Sobrien if (S_ISDIR(statb.st_mode) && mode == X_OK) 11959243Sobrien return 0; 12059243Sobrien#endif /* S_ISDIR */ 12159243Sobrien 12259243Sobrien /* root needs permission for someone */ 12359243Sobrien switch (mode) { 12459243Sobrien case W_OK: 12559243Sobrien mode = S_IWUSR | S_IWGRP | S_IWOTH; 12659243Sobrien break; 12759243Sobrien case X_OK: 12859243Sobrien mode = S_IXUSR | S_IXGRP | S_IXOTH; 12959243Sobrien break; 13059243Sobrien default: 13159243Sobrien abort(); 13259243Sobrien break; 13359243Sobrien } 13459243Sobrien 13559243Sobrien } 13659243Sobrien 13759243Sobrien else if (euid == statb.st_uid) 13859243Sobrien mode <<= 6; 13959243Sobrien 14059243Sobrien else if (egid == statb.st_gid) 14159243Sobrien mode <<= 3; 14259243Sobrien 14359243Sobrien# ifdef NGROUPS_MAX 14459243Sobrien else { 14559243Sobrien /* you can be in several groups */ 14659243Sobrien long n; 147145479Smp GETGROUPS_T *groups; 14859243Sobrien 14959243Sobrien /* 15059243Sobrien * Try these things to find a positive maximum groups value: 15159243Sobrien * 1) sysconf(_SC_NGROUPS_MAX) 15259243Sobrien * 2) NGROUPS_MAX 15359243Sobrien * 3) getgroups(0, unused) 15459243Sobrien * Then allocate and scan the groups array if one of these worked. 15559243Sobrien */ 156145479Smp# if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX) 15759243Sobrien if ((n = sysconf(_SC_NGROUPS_MAX)) == -1) 15859243Sobrien# endif /* _SC_NGROUPS_MAX */ 15959243Sobrien n = NGROUPS_MAX; 16059243Sobrien if (n <= 0) 161145479Smp n = getgroups(0, (GETGROUPS_T *) NULL); 16259243Sobrien 16359243Sobrien if (n > 0) { 164167465Smp groups = xmalloc(n * sizeof(*groups)); 16559243Sobrien n = getgroups((int) n, groups); 16659243Sobrien while (--n >= 0) 16759243Sobrien if (groups[n] == statb.st_gid) { 16859243Sobrien mode <<= 3; 16959243Sobrien break; 17059243Sobrien } 171316957Sdchagin xfree(groups); 17259243Sobrien } 17359243Sobrien } 17459243Sobrien# endif /* NGROUPS_MAX */ 17559243Sobrien 17659243Sobrien if (statb.st_mode & mode) 17759243Sobrien return 0; 17859243Sobrien else 17959243Sobrien return 1; 18059243Sobrien#endif /* !POSIX */ 18159243Sobrien} 18259243Sobrien 183231990Smptcsh_number_t 184167465Smpexpr(Char ***vp) 18559243Sobrien{ 18659243Sobrien return (exp0(vp, 0)); 18759243Sobrien} 18859243Sobrien 189231990Smptcsh_number_t 190167465Smpexp0(Char ***vp, int ignore) 19159243Sobrien{ 192231990Smp tcsh_number_t p1 = exp1(vp, ignore); 19359243Sobrien 19459243Sobrien etraci("exp0 p1", p1, vp); 195195609Smp while (**vp && eq(**vp, STRor2)) { 196145479Smp int p2; 19759243Sobrien 19859243Sobrien (*vp)++; 199195609Smp 200195609Smp p2 = compat_expr ? 201195609Smp exp0(vp, (ignore & TEXP_IGNORE) || p1) : 202195609Smp exp1(vp, (ignore & TEXP_IGNORE) || p1); 203195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 204195609Smp p1 = (p1 || p2); 205195609Smp etraci("exp0 p1", p1, vp); 206195609Smp if (compat_expr) 207195609Smp break; 20859243Sobrien } 20959243Sobrien return (p1); 21059243Sobrien} 21159243Sobrien 212231990Smpstatic tcsh_number_t 213167465Smpexp1(Char ***vp, int ignore) 21459243Sobrien{ 215231990Smp tcsh_number_t p1 = exp2x(vp, ignore); 21659243Sobrien 21759243Sobrien etraci("exp1 p1", p1, vp); 218195609Smp while (**vp && eq(**vp, STRand2)) { 219231990Smp tcsh_number_t p2; 22059243Sobrien 22159243Sobrien (*vp)++; 222195609Smp p2 = compat_expr ? 223195609Smp exp1(vp, (ignore & TEXP_IGNORE) || !p1) : 224195609Smp exp2x(vp, (ignore & TEXP_IGNORE) || !p1); 225195609Smp 22659243Sobrien etraci("exp1 p2", p2, vp); 227195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 228195609Smp p1 = (p1 && p2); 229195609Smp etraci("exp1 p1", p1, vp); 230195609Smp if (compat_expr) 231195609Smp break; 23259243Sobrien } 23359243Sobrien return (p1); 23459243Sobrien} 23559243Sobrien 236231990Smpstatic tcsh_number_t 237167465Smpexp2x(Char ***vp, int ignore) 23859243Sobrien{ 239231990Smp tcsh_number_t p1 = exp2a(vp, ignore); 24059243Sobrien 241195609Smp etraci("exp2x p1", p1, vp); 242195609Smp while (**vp && eq(**vp, STRor)) { 243231990Smp tcsh_number_t p2; 24459243Sobrien 24559243Sobrien (*vp)++; 246195609Smp p2 = compat_expr ? 247195609Smp exp2x(vp, ignore) : 248195609Smp exp2a(vp, ignore); 249195609Smp etraci("exp2x p2", p2, vp); 250195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 251195609Smp p1 = (p1 | p2); 252195609Smp etraci("exp2x p1", p1, vp); 253195609Smp if (compat_expr) 254195609Smp break; 25559243Sobrien } 25659243Sobrien return (p1); 25759243Sobrien} 25859243Sobrien 259231990Smpstatic tcsh_number_t 260167465Smpexp2a(Char ***vp, int ignore) 26159243Sobrien{ 262231990Smp tcsh_number_t p1 = exp2b(vp, ignore); 26359243Sobrien 26459243Sobrien etraci("exp2a p1", p1, vp); 265195609Smp while (**vp && eq(**vp, STRcaret)) { 266231990Smp tcsh_number_t p2; 26759243Sobrien 26859243Sobrien (*vp)++; 269195609Smp p2 = compat_expr ? 270195609Smp exp2a(vp, ignore) : 271195609Smp exp2b(vp, ignore); 27259243Sobrien etraci("exp2a p2", p2, vp); 273195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 274195609Smp p1 = (p1 ^ p2); 275195609Smp etraci("exp2a p1", p1, vp); 276195609Smp if (compat_expr) 277195609Smp break; 27859243Sobrien } 27959243Sobrien return (p1); 28059243Sobrien} 28159243Sobrien 282231990Smpstatic tcsh_number_t 283167465Smpexp2b(Char ***vp, int ignore) 28459243Sobrien{ 285231990Smp tcsh_number_t p1 = exp2c(vp, ignore); 28659243Sobrien 28759243Sobrien etraci("exp2b p1", p1, vp); 288195609Smp while (**vp && eq(**vp, STRand)) { 289231990Smp tcsh_number_t p2; 29059243Sobrien 29159243Sobrien (*vp)++; 292195609Smp p2 = compat_expr ? 293195609Smp exp2b(vp, ignore) : 294195609Smp exp2c(vp, ignore); 29559243Sobrien etraci("exp2b p2", p2, vp); 296195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 297195609Smp p1 = (p1 & p2); 298195609Smp etraci("exp2b p1", p1, vp); 299195609Smp if (compat_expr) 300195609Smp break; 30159243Sobrien } 30259243Sobrien return (p1); 30359243Sobrien} 30459243Sobrien 305231990Smpstatic tcsh_number_t 306167465Smpexp2c(Char ***vp, int ignore) 30759243Sobrien{ 308145479Smp Char *p1 = exp3(vp, ignore); 309145479Smp Char *p2; 310231990Smp tcsh_number_t i; 31159243Sobrien 312167465Smp cleanup_push(p1, xfree); 31359243Sobrien etracc("exp2c p1", p1, vp); 31459243Sobrien if ((i = isa(**vp, EQOP)) != 0) { 31559243Sobrien (*vp)++; 31659243Sobrien if (i == EQMATCH || i == NOTEQMATCH) 31759243Sobrien ignore |= TEXP_NOGLOB; 31859243Sobrien p2 = exp3(vp, ignore); 319167465Smp cleanup_push(p2, xfree); 32059243Sobrien etracc("exp2c p2", p2, vp); 32159243Sobrien if (!(ignore & TEXP_IGNORE)) 322316957Sdchagin switch ((int)i) { 32359243Sobrien 32459243Sobrien case EQEQ: 32559243Sobrien i = eq(p1, p2); 32659243Sobrien break; 32759243Sobrien 32859243Sobrien case NOTEQ: 32959243Sobrien i = !eq(p1, p2); 33059243Sobrien break; 33159243Sobrien 33259243Sobrien case EQMATCH: 33359243Sobrien i = Gmatch(p1, p2); 33459243Sobrien break; 33559243Sobrien 33659243Sobrien case NOTEQMATCH: 33759243Sobrien i = !Gmatch(p1, p2); 33859243Sobrien break; 33959243Sobrien } 340167465Smp cleanup_until(p1); 34159243Sobrien return (i); 34259243Sobrien } 34359243Sobrien i = egetn(p1); 344167465Smp cleanup_until(p1); 34559243Sobrien return (i); 34659243Sobrien} 34759243Sobrien 34859243Sobrienstatic Char * 349167465Smpexp3(Char ***vp, int ignore) 35059243Sobrien{ 351145479Smp Char *p1, *p2; 352231990Smp tcsh_number_t i; 35359243Sobrien 35459243Sobrien p1 = exp3a(vp, ignore); 35559243Sobrien etracc("exp3 p1", p1, vp); 356195609Smp while ((i = isa(**vp, RELOP)) != 0) { 35759243Sobrien (*vp)++; 35859243Sobrien if (**vp && eq(**vp, STRequal)) 35959243Sobrien i |= 1, (*vp)++; 360167465Smp cleanup_push(p1, xfree); 361195609Smp p2 = compat_expr ? 362195609Smp exp3(vp, ignore) : 363195609Smp exp3a(vp, ignore); 364167465Smp cleanup_push(p2, xfree); 36559243Sobrien etracc("exp3 p2", p2, vp); 36659243Sobrien if (!(ignore & TEXP_IGNORE)) 367316957Sdchagin switch ((int)i) { 36859243Sobrien 36959243Sobrien case GTR: 37059243Sobrien i = egetn(p1) > egetn(p2); 37159243Sobrien break; 37259243Sobrien 37359243Sobrien case GTR | 1: 37459243Sobrien i = egetn(p1) >= egetn(p2); 37559243Sobrien break; 37659243Sobrien 37759243Sobrien case LSS: 37859243Sobrien i = egetn(p1) < egetn(p2); 37959243Sobrien break; 38059243Sobrien 38159243Sobrien case LSS | 1: 38259243Sobrien i = egetn(p1) <= egetn(p2); 38359243Sobrien break; 38459243Sobrien } 385167465Smp cleanup_until(p1); 386195609Smp p1 = putn(i); 387195609Smp etracc("exp3 p1", p1, vp); 388195609Smp if (compat_expr) 389195609Smp break; 39059243Sobrien } 39159243Sobrien return (p1); 39259243Sobrien} 39359243Sobrien 39459243Sobrienstatic Char * 395167465Smpexp3a(Char ***vp, int ignore) 39659243Sobrien{ 397167465Smp Char *p1, *p2; 398167465Smp const Char *op; 399231990Smp tcsh_number_t i; 40059243Sobrien 40159243Sobrien p1 = exp4(vp, ignore); 40259243Sobrien etracc("exp3a p1", p1, vp); 40359243Sobrien op = **vp; 40459243Sobrien if (op && any("<>", op[0]) && op[0] == op[1]) { 40559243Sobrien (*vp)++; 406167465Smp cleanup_push(p1, xfree); 407195609Smp p2 = compat_expr ? 408195609Smp exp3a(vp, ignore) : 409195609Smp exp4(vp, ignore); 410167465Smp cleanup_push(p2, xfree); 41159243Sobrien etracc("exp3a p2", p2, vp); 41259243Sobrien if (op[0] == '<') 41359243Sobrien i = egetn(p1) << egetn(p2); 41459243Sobrien else 41559243Sobrien i = egetn(p1) >> egetn(p2); 416167465Smp cleanup_until(p1); 417195609Smp p1 = putn(i); 418195609Smp etracc("exp3a p1", p1, vp); 41959243Sobrien } 42059243Sobrien return (p1); 42159243Sobrien} 42259243Sobrien 42359243Sobrienstatic Char * 424167465Smpexp4(Char ***vp, int ignore) 42559243Sobrien{ 426145479Smp Char *p1, *p2; 427231990Smp tcsh_number_t i = 0; 42859243Sobrien 42959243Sobrien p1 = exp5(vp, ignore); 43059243Sobrien etracc("exp4 p1", p1, vp); 431195609Smp while (isa(**vp, ADDOP)) { 432167465Smp const Char *op = *(*vp)++; 43359243Sobrien 434167465Smp cleanup_push(p1, xfree); 435195609Smp p2 = compat_expr ? 436195609Smp exp4(vp, ignore) : 437195609Smp exp5(vp, ignore); 438167465Smp cleanup_push(p2, xfree); 43959243Sobrien etracc("exp4 p2", p2, vp); 44059243Sobrien if (!(ignore & TEXP_IGNORE)) 44159243Sobrien switch (op[0]) { 44259243Sobrien 44359243Sobrien case '+': 44459243Sobrien i = egetn(p1) + egetn(p2); 44559243Sobrien break; 44659243Sobrien 44759243Sobrien case '-': 44859243Sobrien i = egetn(p1) - egetn(p2); 44959243Sobrien break; 45059243Sobrien } 451167465Smp cleanup_until(p1); 452195609Smp p1 = putn(i); 453195609Smp etracc("exp4 p1", p1, vp); 454195609Smp if (compat_expr) 455195609Smp break; 45659243Sobrien } 45759243Sobrien return (p1); 45859243Sobrien} 45959243Sobrien 46059243Sobrienstatic Char * 461167465Smpexp5(Char ***vp, int ignore) 46259243Sobrien{ 463145479Smp Char *p1, *p2; 464231990Smp tcsh_number_t i = 0; 46559243Sobrien 46659243Sobrien p1 = exp6(vp, ignore); 46759243Sobrien etracc("exp5 p1", p1, vp); 46859243Sobrien 469195609Smp while (isa(**vp, MULOP)) { 470167465Smp const Char *op = *(*vp)++; 471167465Smp if ((ignore & TEXP_NOGLOB) != 0) { 472167465Smp /* 47359243Sobrien * We are just trying to get the right side of 474167465Smp * a =~ or !~ operator 47559243Sobrien */ 476167465Smp xfree(p1); 47759243Sobrien return Strsave(op); 478167465Smp } 47959243Sobrien 480167465Smp cleanup_push(p1, xfree); 481195609Smp p2 = compat_expr ? 482195609Smp exp5(vp, ignore) : 483195609Smp exp6(vp, ignore); 484167465Smp cleanup_push(p2, xfree); 48559243Sobrien etracc("exp5 p2", p2, vp); 48659243Sobrien if (!(ignore & TEXP_IGNORE)) 48759243Sobrien switch (op[0]) { 48859243Sobrien 48959243Sobrien case '*': 49059243Sobrien i = egetn(p1) * egetn(p2); 49159243Sobrien break; 49259243Sobrien 49359243Sobrien case '/': 49459243Sobrien i = egetn(p2); 49559243Sobrien if (i == 0) 49659243Sobrien stderror(ERR_DIV0); 49759243Sobrien i = egetn(p1) / i; 49859243Sobrien break; 49959243Sobrien 50059243Sobrien case '%': 50159243Sobrien i = egetn(p2); 50259243Sobrien if (i == 0) 50359243Sobrien stderror(ERR_MOD0); 50459243Sobrien i = egetn(p1) % i; 50559243Sobrien break; 50659243Sobrien } 507167465Smp cleanup_until(p1); 508195609Smp p1 = putn(i); 509195609Smp etracc("exp5 p1", p1, vp); 510195609Smp if (compat_expr) 511195609Smp break; 51259243Sobrien } 51359243Sobrien return (p1); 51459243Sobrien} 51559243Sobrien 51659243Sobrienstatic Char * 517167465Smpexp6(Char ***vp, int ignore) 51859243Sobrien{ 519231990Smp tcsh_number_t ccode; 520231990Smp tcsh_number_t i = 0; 521145479Smp Char *cp; 52259243Sobrien 52359243Sobrien if (**vp == 0) 52459243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 52559243Sobrien if (eq(**vp, STRbang)) { 52659243Sobrien (*vp)++; 52759243Sobrien cp = exp6(vp, ignore); 528167465Smp cleanup_push(cp, xfree); 52959243Sobrien etracc("exp6 ! cp", cp, vp); 53059243Sobrien i = egetn(cp); 531167465Smp cleanup_until(cp); 53259243Sobrien return (putn(!i)); 53359243Sobrien } 53459243Sobrien if (eq(**vp, STRtilde)) { 53559243Sobrien (*vp)++; 53659243Sobrien cp = exp6(vp, ignore); 537167465Smp cleanup_push(cp, xfree); 53859243Sobrien etracc("exp6 ~ cp", cp, vp); 53959243Sobrien i = egetn(cp); 540167465Smp cleanup_until(cp); 54159243Sobrien return (putn(~i)); 54259243Sobrien } 54359243Sobrien if (eq(**vp, STRLparen)) { 54459243Sobrien (*vp)++; 54559243Sobrien ccode = exp0(vp, ignore); 54659243Sobrien etraci("exp6 () ccode", ccode, vp); 547167465Smp if (**vp == 0 || ***vp != ')') 54859243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 54959243Sobrien (*vp)++; 55059243Sobrien return (putn(ccode)); 55159243Sobrien } 55259243Sobrien if (eq(**vp, STRLbrace)) { 553145479Smp Char **v; 55459243Sobrien struct command faket; 55559243Sobrien Char *fakecom[2]; 55659243Sobrien 55759243Sobrien faket.t_dtyp = NODE_COMMAND; 55859243Sobrien faket.t_dflg = F_BACKQ; 55959243Sobrien faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL; 56059243Sobrien faket.t_dcom = fakecom; 56159243Sobrien fakecom[0] = STRfakecom; 56259243Sobrien fakecom[1] = NULL; 56359243Sobrien (*vp)++; 56459243Sobrien v = *vp; 56559243Sobrien for (;;) { 56659243Sobrien if (!**vp) 56759243Sobrien stderror(ERR_NAME | ERR_MISSING, '}'); 56859243Sobrien if (eq(*(*vp)++, STRRbrace)) 56959243Sobrien break; 57059243Sobrien } 57159243Sobrien if (ignore & TEXP_IGNORE) 57259243Sobrien return (Strsave(STRNULL)); 57359243Sobrien psavejob(); 574167465Smp cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */ 57559243Sobrien if (pfork(&faket, -1) == 0) { 57659243Sobrien *--(*vp) = 0; 57759243Sobrien evalav(v); 57859243Sobrien exitstat(); 57959243Sobrien } 58059243Sobrien pwait(); 581167465Smp cleanup_until(&faket); 58259243Sobrien etraci("exp6 {} status", egetn(varval(STRstatus)), vp); 58359243Sobrien return (putn(egetn(varval(STRstatus)) == 0)); 58459243Sobrien } 58559243Sobrien if (isa(**vp, ANYOP)) 58659243Sobrien return (Strsave(STRNULL)); 58759243Sobrien cp = *(*vp)++; 58859243Sobrien#ifdef convex 58959243Sobrien# define FILETESTS "erwxfdzoplstSXLbcugkmKR" 59059243Sobrien#else 59159243Sobrien# define FILETESTS "erwxfdzoplstSXLbcugkmK" 59259243Sobrien#endif /* convex */ 59359243Sobrien#define FILEVALS "ZAMCDIUGNFPL" 59459243Sobrien if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1]))) 59559243Sobrien return(filetest(cp, vp, ignore)); 59659243Sobrien etracc("exp6 default", cp, vp); 59759243Sobrien return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND)); 59859243Sobrien} 59959243Sobrien 60059243Sobrien 60159243Sobrien/* 60259243Sobrien * Extended file tests 60359243Sobrien * From: John Rowe <rowe@excc.exeter.ac.uk> 60459243Sobrien */ 60559243SobrienChar * 606167465Smpfiletest(Char *cp, Char ***vp, int ignore) 60759243Sobrien{ 60859243Sobrien#ifdef convex 60959243Sobrien struct cvxstat stb, *st = NULL; 61059243Sobrien# define TCSH_STAT stat64 61159243Sobrien#else 61259243Sobrien# define TCSH_STAT stat 61359243Sobrien struct stat stb, *st = NULL; 61459243Sobrien#endif /* convex */ 61559243Sobrien 61659243Sobrien#ifdef S_IFLNK 61759243Sobrien# ifdef convex 61859243Sobrien struct cvxstat lstb, *lst = NULL; 61959243Sobrien# define TCSH_LSTAT lstat64 62059243Sobrien# else 62159243Sobrien# define TCSH_LSTAT lstat 62259243Sobrien struct stat lstb, *lst = NULL; 62359243Sobrien# endif /* convex */ 62459243Sobrien char *filnam; 62559243Sobrien#endif /* S_IFLNK */ 62659243Sobrien 627231990Smp tcsh_number_t i = 0; 62859243Sobrien unsigned pmask = 0xffff; 629145479Smp int altout = 0; 63059243Sobrien Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0', 63159243Sobrien *errval = STR0; 632316957Sdchagin char *string, string0[22 + MB_LEN_MAX + 1]; /* space for 64 bit octal */ 63359243Sobrien time_t footime; 63459243Sobrien struct passwd *pw; 63559243Sobrien struct group *gr; 63659243Sobrien 63759243Sobrien while(any(FILETESTS, *++ft)) 63859243Sobrien continue; 63959243Sobrien 64059243Sobrien if (!*ft && *(ft - 1) == 'L') 64159243Sobrien --ft; 64259243Sobrien 64359243Sobrien if (any(FILEVALS, *ft)) { 64459243Sobrien valtest = *ft++; 64559243Sobrien /* 64659243Sobrien * Value tests return '-1' on failure as 0 is 64759243Sobrien * a legitimate value for many of them. 64859243Sobrien * 'F' returns ':' for compatibility. 64959243Sobrien */ 65059243Sobrien errval = valtest == 'F' ? STRcolon : STRminus1; 65159243Sobrien 65259243Sobrien if (valtest == 'P' && *ft >= '0' && *ft <= '7') { 65359243Sobrien pmask = (char) *ft - '0'; 65459243Sobrien while ( *++ft >= '0' && *ft <= '7' ) 65559243Sobrien pmask = 8 * pmask + ((char) *ft - '0'); 65659243Sobrien } 65759243Sobrien if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) { 65859243Sobrien altout = 1; 65959243Sobrien ++ft; 66059243Sobrien } 66159243Sobrien } 66259243Sobrien 66359243Sobrien if (*ft || ft == cp + 1) 66459243Sobrien stderror(ERR_NAME | ERR_FILEINQ); 66559243Sobrien 66659243Sobrien /* 66759243Sobrien * Detect missing file names by checking for operator in the file name 66859243Sobrien * position. However, if an operator name appears there, we must make 66959243Sobrien * sure that there's no file by that name (e.g., "/") before announcing 67059243Sobrien * an error. Even this check isn't quite right, since it doesn't take 67159243Sobrien * globbing into account. 67259243Sobrien */ 67359243Sobrien 67459243Sobrien if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb)) 67559243Sobrien stderror(ERR_NAME | ERR_FILENAME); 67659243Sobrien 67759243Sobrien dp = *(*vp)++; 67859243Sobrien if (ignore & TEXP_IGNORE) 67959243Sobrien return (Strsave(STRNULL)); 68059243Sobrien ep = globone(dp, G_APPEND); 681167465Smp cleanup_push(ep, xfree); 68259243Sobrien ft = &cp[1]; 68359243Sobrien do 68459243Sobrien switch (*ft) { 68559243Sobrien 68659243Sobrien case 'r': 68759243Sobrien i = !sh_access(ep, R_OK); 68859243Sobrien break; 68959243Sobrien 69059243Sobrien case 'w': 69159243Sobrien i = !sh_access(ep, W_OK); 69259243Sobrien break; 69359243Sobrien 69459243Sobrien case 'x': 69559243Sobrien i = !sh_access(ep, X_OK); 69659243Sobrien break; 69759243Sobrien 69859243Sobrien case 'X': /* tcsh extension, name is an executable in the path 69959243Sobrien * or a tcsh builtin command 70059243Sobrien */ 70159243Sobrien i = find_cmd(ep, 0); 70259243Sobrien break; 70359243Sobrien 70459243Sobrien case 't': /* SGI extension, true when file is a tty */ 70559243Sobrien i = isatty(atoi(short2str(ep))); 70659243Sobrien break; 70759243Sobrien 70859243Sobrien default: 70959243Sobrien 71059243Sobrien#ifdef S_IFLNK 71159243Sobrien if (tolower(*ft) == 'l') { 71259243Sobrien /* 71359243Sobrien * avoid convex compiler bug. 71459243Sobrien */ 71559243Sobrien if (!lst) { 71659243Sobrien lst = &lstb; 71759243Sobrien if (TCSH_LSTAT(short2str(ep), lst) == -1) { 718167465Smp cleanup_until(ep); 71959243Sobrien return (Strsave(errval)); 72059243Sobrien } 72159243Sobrien } 72259243Sobrien if (*ft == 'L') 72359243Sobrien st = lst; 72459243Sobrien } 72559243Sobrien else 72659243Sobrien#endif /* S_IFLNK */ 72759243Sobrien /* 72859243Sobrien * avoid convex compiler bug. 72959243Sobrien */ 73059243Sobrien if (!st) { 73159243Sobrien st = &stb; 73259243Sobrien if (TCSH_STAT(short2str(ep), st) == -1) { 733167465Smp cleanup_until(ep); 73459243Sobrien return (Strsave(errval)); 73559243Sobrien } 73659243Sobrien } 73759243Sobrien 73859243Sobrien switch (*ft) { 73959243Sobrien 74059243Sobrien case 'f': 74159243Sobrien#ifdef S_ISREG 74259243Sobrien i = S_ISREG(st->st_mode); 74359243Sobrien#else /* !S_ISREG */ 74459243Sobrien i = 0; 74559243Sobrien#endif /* S_ISREG */ 74659243Sobrien break; 74759243Sobrien 74859243Sobrien case 'd': 74959243Sobrien#ifdef S_ISDIR 75059243Sobrien i = S_ISDIR(st->st_mode); 75159243Sobrien#else /* !S_ISDIR */ 75259243Sobrien i = 0; 75359243Sobrien#endif /* S_ISDIR */ 75459243Sobrien break; 75559243Sobrien 75659243Sobrien case 'p': 75759243Sobrien#ifdef S_ISFIFO 75859243Sobrien i = S_ISFIFO(st->st_mode); 75959243Sobrien#else /* !S_ISFIFO */ 76059243Sobrien i = 0; 76159243Sobrien#endif /* S_ISFIFO */ 76259243Sobrien break; 76359243Sobrien 76459243Sobrien case 'm' : 76559243Sobrien#ifdef S_ISOFL 76659243Sobrien i = S_ISOFL(st->st_dm_mode); 76759243Sobrien#else /* !S_ISOFL */ 76859243Sobrien i = 0; 76959243Sobrien#endif /* S_ISOFL */ 77059243Sobrien break ; 77159243Sobrien 77259243Sobrien case 'K' : 77359243Sobrien#ifdef S_ISOFL 77459243Sobrien i = stb.st_dm_key; 77559243Sobrien#else /* !S_ISOFL */ 77659243Sobrien i = 0; 77759243Sobrien#endif /* S_ISOFL */ 77859243Sobrien break ; 77959243Sobrien 78059243Sobrien 78159243Sobrien case 'l': 78259243Sobrien#ifdef S_ISLNK 78359243Sobrien i = S_ISLNK(lst->st_mode); 78459243Sobrien#else /* !S_ISLNK */ 78559243Sobrien i = 0; 78659243Sobrien#endif /* S_ISLNK */ 78759243Sobrien break; 78859243Sobrien 78959243Sobrien case 'S': 79059243Sobrien# ifdef S_ISSOCK 79159243Sobrien i = S_ISSOCK(st->st_mode); 79259243Sobrien# else /* !S_ISSOCK */ 79359243Sobrien i = 0; 79459243Sobrien# endif /* S_ISSOCK */ 79559243Sobrien break; 79659243Sobrien 79759243Sobrien case 'b': 79859243Sobrien#ifdef S_ISBLK 79959243Sobrien i = S_ISBLK(st->st_mode); 80059243Sobrien#else /* !S_ISBLK */ 80159243Sobrien i = 0; 80259243Sobrien#endif /* S_ISBLK */ 80359243Sobrien break; 80459243Sobrien 80559243Sobrien case 'c': 80659243Sobrien#ifdef S_ISCHR 80759243Sobrien i = S_ISCHR(st->st_mode); 80859243Sobrien#else /* !S_ISCHR */ 80959243Sobrien i = 0; 81059243Sobrien#endif /* S_ISCHR */ 81159243Sobrien break; 81259243Sobrien 81359243Sobrien case 'u': 81459243Sobrien i = (S_ISUID & st->st_mode) != 0; 81559243Sobrien break; 81659243Sobrien 81759243Sobrien case 'g': 81859243Sobrien i = (S_ISGID & st->st_mode) != 0; 81959243Sobrien break; 82059243Sobrien 82159243Sobrien case 'k': 82259243Sobrien i = (S_ISVTX & st->st_mode) != 0; 82359243Sobrien break; 82459243Sobrien 82559243Sobrien case 'z': 82659243Sobrien i = st->st_size == 0; 82759243Sobrien break; 82859243Sobrien 82959243Sobrien#ifdef convex 83059243Sobrien case 'R': 83159243Sobrien i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED; 83259243Sobrien break; 83359243Sobrien#endif /* convex */ 83459243Sobrien 83559243Sobrien case 's': 83659243Sobrien i = stb.st_size != 0; 83759243Sobrien break; 83859243Sobrien 83959243Sobrien case 'e': 84059243Sobrien i = 1; 84159243Sobrien break; 84259243Sobrien 84359243Sobrien case 'o': 84459243Sobrien i = st->st_uid == uid; 84559243Sobrien break; 84659243Sobrien 84759243Sobrien /* 84859243Sobrien * Value operators are a tcsh extension. 84959243Sobrien */ 85059243Sobrien 85159243Sobrien case 'D': 852231990Smp i = (tcsh_number_t) st->st_dev; 85359243Sobrien break; 85459243Sobrien 85559243Sobrien case 'I': 856231990Smp i = (tcsh_number_t) st->st_ino; 85759243Sobrien break; 85859243Sobrien 85959243Sobrien case 'F': 86059243Sobrien strdev = putn( (int) st->st_dev); 86159243Sobrien strino = putn( (int) st->st_ino); 862167465Smp strF = xmalloc((2 + Strlen(strdev) + Strlen(strino)) 863167465Smp * sizeof(Char)); 86459243Sobrien (void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino); 865167465Smp xfree(strdev); 866167465Smp xfree(strino); 867167465Smp cleanup_until(ep); 86859243Sobrien return(strF); 86959243Sobrien 87059243Sobrien case 'L': 87159243Sobrien if ( *(ft + 1) ) { 87259243Sobrien i = 1; 87359243Sobrien break; 87459243Sobrien } 87559243Sobrien#ifdef S_ISLNK 87659243Sobrien filnam = short2str(ep); 877167465Smp string = areadlink(filnam); 878167465Smp strF = string == NULL ? errval : str2short(string); 879167465Smp xfree(string); 880167465Smp cleanup_until(ep); 88159243Sobrien return(Strsave(strF)); 88259243Sobrien 88359243Sobrien#else /* !S_ISLNK */ 88459243Sobrien i = 0; 88559243Sobrien break; 88659243Sobrien#endif /* S_ISLNK */ 88759243Sobrien 88859243Sobrien 88959243Sobrien case 'N': 890231990Smp i = (tcsh_number_t) st->st_nlink; 89159243Sobrien break; 89259243Sobrien 89359243Sobrien case 'P': 89459243Sobrien string = string0 + 1; 89559243Sobrien (void) xsnprintf(string, sizeof(string0) - 1, "%o", 89659243Sobrien pmask & (unsigned int) 89759243Sobrien ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode)); 89859243Sobrien if (altout && *string != '0') 89959243Sobrien *--string = '0'; 900167465Smp cleanup_until(ep); 90159243Sobrien return(Strsave(str2short(string))); 90259243Sobrien 90359243Sobrien case 'U': 904167465Smp if (altout && (pw = xgetpwuid(st->st_uid))) { 905167465Smp cleanup_until(ep); 90659243Sobrien return(Strsave(str2short(pw->pw_name))); 90759243Sobrien } 908231990Smp i = (tcsh_number_t) st->st_uid; 90959243Sobrien break; 91059243Sobrien 91159243Sobrien case 'G': 912167465Smp if (altout && (gr = xgetgrgid(st->st_gid))) { 913167465Smp cleanup_until(ep); 91459243Sobrien return(Strsave(str2short(gr->gr_name))); 91559243Sobrien } 916231990Smp i = (tcsh_number_t) st->st_gid; 91759243Sobrien break; 91859243Sobrien 91959243Sobrien case 'Z': 920231990Smp i = (tcsh_number_t) st->st_size; 92159243Sobrien break; 92259243Sobrien 92359243Sobrien case 'A': case 'M': case 'C': 92459243Sobrien footime = *ft == 'A' ? st->st_atime : 92559243Sobrien *ft == 'M' ? st->st_mtime : st->st_ctime; 92659243Sobrien if (altout) { 92759243Sobrien strF = str2short(ctime(&footime)); 92859243Sobrien if ((str = Strchr(strF, '\n')) != NULL) 92959243Sobrien *str = (Char) '\0'; 930167465Smp cleanup_until(ep); 93159243Sobrien return(Strsave(strF)); 93259243Sobrien } 933231990Smp i = (tcsh_number_t) footime; 93459243Sobrien break; 93559243Sobrien 93659243Sobrien } 93759243Sobrien } 93859243Sobrien while (*++ft && i); 93959243Sobrien etraci("exp6 -? i", i, vp); 940167465Smp cleanup_until(ep); 94159243Sobrien return (putn(i)); 94259243Sobrien} 94359243Sobrien 94459243Sobrien 94559243Sobrienstatic void 946167465Smpevalav(Char **v) 94759243Sobrien{ 94859243Sobrien struct wordent paraml1; 949145479Smp struct wordent *hp = ¶ml1; 95059243Sobrien struct command *t; 951145479Smp struct wordent *wdp = hp; 95259243Sobrien 953167465Smp setcopy(STRstatus, STR0, VAR_READWRITE); 954316957Sdchagin initlex(hp); 95559243Sobrien while (*v) { 956167465Smp struct wordent *new = xcalloc(1, sizeof *wdp); 95759243Sobrien 95859243Sobrien new->prev = wdp; 95959243Sobrien new->next = hp; 96059243Sobrien wdp->next = new; 96159243Sobrien wdp = new; 96259243Sobrien wdp->word = Strsave(*v++); 96359243Sobrien } 96459243Sobrien hp->prev = wdp; 965167465Smp cleanup_push(¶ml1, lex_cleanup); 96659243Sobrien alias(¶ml1); 96759243Sobrien t = syntax(paraml1.next, ¶ml1, 0); 968167465Smp cleanup_push(t, syntax_cleanup); 96959243Sobrien if (seterr) 97059243Sobrien stderror(ERR_OLD); 971100616Smp execute(t, -1, NULL, NULL, TRUE); 972167465Smp cleanup_until(¶ml1); 97359243Sobrien} 97459243Sobrien 97559243Sobrienstatic int 976167465Smpisa(Char *cp, int what) 97759243Sobrien{ 97859243Sobrien if (cp == 0) 97959243Sobrien return ((what & RESTOP) != 0); 98059243Sobrien if (*cp == '\0') 98159243Sobrien return 0; 98259243Sobrien if (cp[1] == 0) { 98359243Sobrien if (what & ADDOP && (*cp == '+' || *cp == '-')) 98459243Sobrien return (1); 98559243Sobrien if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) 98659243Sobrien return (1); 98759243Sobrien if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || 98859243Sobrien *cp == '~' || *cp == '^' || *cp == '"')) 98959243Sobrien return (1); 99059243Sobrien } 99159243Sobrien else if (cp[2] == 0) { 99259243Sobrien if (what & RESTOP) { 99359243Sobrien if (cp[0] == '|' && cp[1] == '&') 99459243Sobrien return (1); 99559243Sobrien if (cp[0] == '<' && cp[1] == '<') 99659243Sobrien return (1); 99759243Sobrien if (cp[0] == '>' && cp[1] == '>') 99859243Sobrien return (1); 99959243Sobrien } 100059243Sobrien if (what & EQOP) { 100159243Sobrien if (cp[0] == '=') { 100259243Sobrien if (cp[1] == '=') 100359243Sobrien return (EQEQ); 100459243Sobrien if (cp[1] == '~') 100559243Sobrien return (EQMATCH); 100659243Sobrien } 100759243Sobrien else if (cp[0] == '!') { 100859243Sobrien if (cp[1] == '=') 100959243Sobrien return (NOTEQ); 101059243Sobrien if (cp[1] == '~') 101159243Sobrien return (NOTEQMATCH); 101259243Sobrien } 101359243Sobrien } 101459243Sobrien } 101559243Sobrien if (what & RELOP) { 101659243Sobrien if (*cp == '<') 101759243Sobrien return (LSS); 101859243Sobrien if (*cp == '>') 101959243Sobrien return (GTR); 102059243Sobrien } 102159243Sobrien return (0); 102259243Sobrien} 102359243Sobrien 1024231990Smpstatic tcsh_number_t 1025231990Smpegetn(const Char *cp) 102659243Sobrien{ 102759243Sobrien if (*cp && *cp != '-' && !Isdigit(*cp)) 102859243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 102959243Sobrien return (getn(cp)); 103059243Sobrien} 103159243Sobrien 103259243Sobrien/* Phew! */ 103359243Sobrien 103459243Sobrien#ifdef EDEBUG 103559243Sobrienstatic void 1036231990Smpetraci(const char *str, tcsh_number_t i, Char ***vp) 103759243Sobrien{ 1038231990Smp#ifdef HAVE_LONG_LONG 1039231990Smp xprintf("%s=%lld\t", str, i); 1040231990Smp#else 1041231990Smp xprintf("%s=%ld\t", str, i); 1042231990Smp#endif 104359243Sobrien blkpr(*vp); 104459243Sobrien xputchar('\n'); 104559243Sobrien} 104659243Sobrienstatic void 1047195609Smpetracc(const char *str, const Char *cp, Char ***vp) 104859243Sobrien{ 1049195609Smp xprintf("%s=%S\t", str, cp); 105059243Sobrien blkpr(*vp); 105159243Sobrien xputchar('\n'); 105259243Sobrien} 105359243Sobrien#endif /* EDEBUG */ 1054