1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/sh.exp.c,v 3.63 2015/12/09 17:17:43 christos Exp $ */ 259243Sobrien/* 359243Sobrien * sh.exp.c: Expression evaluations 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 3459243Sobrien 35316958SdchaginRCSID("$tcsh: sh.exp.c,v 3.63 2015/12/09 17:17:43 christos Exp $") 3659243Sobrien 37100616Smp#include "tw.h" 38100616Smp 3959243Sobrien/* 4059243Sobrien * C shell 4159243Sobrien */ 4259243Sobrien 4359243Sobrien#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */ 4459243Sobrien#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */ 4559243Sobrien 4659243Sobrien#define ADDOP 1 4759243Sobrien#define MULOP 2 4859243Sobrien#define EQOP 4 4959243Sobrien#define RELOP 8 5059243Sobrien#define RESTOP 16 5159243Sobrien#define ANYOP 31 5259243Sobrien 5359243Sobrien#define EQEQ 1 5459243Sobrien#define GTR 2 5559243Sobrien#define LSS 4 5659243Sobrien#define NOTEQ 6 5759243Sobrien#define EQMATCH 7 5859243Sobrien#define NOTEQMATCH 8 5959243Sobrien 60231990Smpstatic int sh_access (const Char *, int); 61231990Smpstatic tcsh_number_t exp1 (Char ***, int); 62231990Smpstatic tcsh_number_t exp2x (Char ***, int); 63231990Smpstatic tcsh_number_t exp2a (Char ***, int); 64231990Smpstatic tcsh_number_t exp2b (Char ***, int); 65231990Smpstatic tcsh_number_t exp2c (Char ***, int); 66231990Smpstatic Char *exp3 (Char ***, int); 67231990Smpstatic Char *exp3a (Char ***, int); 68231990Smpstatic Char *exp4 (Char ***, int); 69231990Smpstatic Char *exp5 (Char ***, int); 70231990Smpstatic Char *exp6 (Char ***, int); 71231990Smpstatic void evalav (Char **); 72231990Smpstatic int isa (Char *, int); 73231990Smpstatic tcsh_number_t egetn (const Char *); 7459243Sobrien 7559243Sobrien#ifdef EDEBUG 76231990Smpstatic void etracc (const char *, const Char *, Char ***); 77231990Smpstatic void etraci (const char *, tcsh_number_t, Char ***); 78167465Smp#else /* !EDEBUG */ 79167465Smp#define etracc(A, B, C) ((void)0) 80167465Smp#define etraci(A, B, C) ((void)0) 81167465Smp#endif /* !EDEBUG */ 8259243Sobrien 8359243Sobrien/* 8459243Sobrien * shell access function according to POSIX and non POSIX 8559243Sobrien * From Beto Appleton (beto@aixwiz.aix.ibm.com) 8659243Sobrien */ 8759243Sobrienstatic int 88167465Smpsh_access(const Char *fname, int mode) 8959243Sobrien{ 9059243Sobrien#if defined(POSIX) && !defined(USE_ACCESS) 9159243Sobrien struct stat statb; 9259243Sobrien#endif /* POSIX */ 9359243Sobrien char *name = short2str(fname); 9459243Sobrien 9559243Sobrien if (*name == '\0') 9659243Sobrien return 1; 9759243Sobrien 9859243Sobrien#if !defined(POSIX) || defined(USE_ACCESS) 9959243Sobrien return access(name, mode); 10059243Sobrien#else /* POSIX */ 10159243Sobrien 10259243Sobrien /* 10359243Sobrien * POSIX 1003.2-d11.2 10459243Sobrien * -r file True if file exists and is readable. 10559243Sobrien * -w file True if file exists and is writable. 10659243Sobrien * True shall indicate only that the write flag is on. 10759243Sobrien * The file shall not be writable on a read-only file 10859243Sobrien * system even if this test indicates true. 10959243Sobrien * -x file True if file exists and is executable. 11059243Sobrien * True shall indicate only that the execute flag is on. 11159243Sobrien * If file is a directory, true indicates that the file 11259243Sobrien * can be searched. 11359243Sobrien */ 11459243Sobrien if (mode != W_OK && mode != X_OK) 11559243Sobrien return access(name, mode); 11659243Sobrien 11759243Sobrien if (stat(name, &statb) == -1) 11859243Sobrien return 1; 11959243Sobrien 12059243Sobrien if (access(name, mode) == 0) { 12159243Sobrien#ifdef S_ISDIR 12259243Sobrien if (S_ISDIR(statb.st_mode) && mode == X_OK) 12359243Sobrien return 0; 12459243Sobrien#endif /* S_ISDIR */ 12559243Sobrien 12659243Sobrien /* root needs permission for someone */ 12759243Sobrien switch (mode) { 12859243Sobrien case W_OK: 12959243Sobrien mode = S_IWUSR | S_IWGRP | S_IWOTH; 13059243Sobrien break; 13159243Sobrien case X_OK: 13259243Sobrien mode = S_IXUSR | S_IXGRP | S_IXOTH; 13359243Sobrien break; 13459243Sobrien default: 13559243Sobrien abort(); 13659243Sobrien break; 13759243Sobrien } 13859243Sobrien 13959243Sobrien } 14059243Sobrien 14159243Sobrien else if (euid == statb.st_uid) 14259243Sobrien mode <<= 6; 14359243Sobrien 14459243Sobrien else if (egid == statb.st_gid) 14559243Sobrien mode <<= 3; 14659243Sobrien 14759243Sobrien# ifdef NGROUPS_MAX 14859243Sobrien else { 14959243Sobrien /* you can be in several groups */ 15059243Sobrien long n; 151145479Smp GETGROUPS_T *groups; 15259243Sobrien 15359243Sobrien /* 15459243Sobrien * Try these things to find a positive maximum groups value: 15559243Sobrien * 1) sysconf(_SC_NGROUPS_MAX) 15659243Sobrien * 2) NGROUPS_MAX 15759243Sobrien * 3) getgroups(0, unused) 15859243Sobrien * Then allocate and scan the groups array if one of these worked. 15959243Sobrien */ 160145479Smp# if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX) 16159243Sobrien if ((n = sysconf(_SC_NGROUPS_MAX)) == -1) 16259243Sobrien# endif /* _SC_NGROUPS_MAX */ 16359243Sobrien n = NGROUPS_MAX; 16459243Sobrien if (n <= 0) 165145479Smp n = getgroups(0, (GETGROUPS_T *) NULL); 16659243Sobrien 16759243Sobrien if (n > 0) { 168167465Smp groups = xmalloc(n * sizeof(*groups)); 16959243Sobrien n = getgroups((int) n, groups); 17059243Sobrien while (--n >= 0) 17159243Sobrien if (groups[n] == statb.st_gid) { 17259243Sobrien mode <<= 3; 17359243Sobrien break; 17459243Sobrien } 175316958Sdchagin xfree(groups); 17659243Sobrien } 17759243Sobrien } 17859243Sobrien# endif /* NGROUPS_MAX */ 17959243Sobrien 18059243Sobrien if (statb.st_mode & mode) 18159243Sobrien return 0; 18259243Sobrien else 18359243Sobrien return 1; 18459243Sobrien#endif /* !POSIX */ 18559243Sobrien} 18659243Sobrien 187231990Smptcsh_number_t 188167465Smpexpr(Char ***vp) 18959243Sobrien{ 19059243Sobrien return (exp0(vp, 0)); 19159243Sobrien} 19259243Sobrien 193231990Smptcsh_number_t 194167465Smpexp0(Char ***vp, int ignore) 19559243Sobrien{ 196231990Smp tcsh_number_t p1 = exp1(vp, ignore); 19759243Sobrien 19859243Sobrien etraci("exp0 p1", p1, vp); 199195609Smp while (**vp && eq(**vp, STRor2)) { 200145479Smp int p2; 20159243Sobrien 20259243Sobrien (*vp)++; 203195609Smp 204195609Smp p2 = compat_expr ? 205195609Smp exp0(vp, (ignore & TEXP_IGNORE) || p1) : 206195609Smp exp1(vp, (ignore & TEXP_IGNORE) || p1); 207195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 208195609Smp p1 = (p1 || p2); 209195609Smp etraci("exp0 p1", p1, vp); 210195609Smp if (compat_expr) 211195609Smp break; 21259243Sobrien } 21359243Sobrien return (p1); 21459243Sobrien} 21559243Sobrien 216231990Smpstatic tcsh_number_t 217167465Smpexp1(Char ***vp, int ignore) 21859243Sobrien{ 219231990Smp tcsh_number_t p1 = exp2x(vp, ignore); 22059243Sobrien 22159243Sobrien etraci("exp1 p1", p1, vp); 222195609Smp while (**vp && eq(**vp, STRand2)) { 223231990Smp tcsh_number_t p2; 22459243Sobrien 22559243Sobrien (*vp)++; 226195609Smp p2 = compat_expr ? 227195609Smp exp1(vp, (ignore & TEXP_IGNORE) || !p1) : 228195609Smp exp2x(vp, (ignore & TEXP_IGNORE) || !p1); 229195609Smp 23059243Sobrien etraci("exp1 p2", p2, vp); 231195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 232195609Smp p1 = (p1 && p2); 233195609Smp etraci("exp1 p1", p1, vp); 234195609Smp if (compat_expr) 235195609Smp break; 23659243Sobrien } 23759243Sobrien return (p1); 23859243Sobrien} 23959243Sobrien 240231990Smpstatic tcsh_number_t 241167465Smpexp2x(Char ***vp, int ignore) 24259243Sobrien{ 243231990Smp tcsh_number_t p1 = exp2a(vp, ignore); 24459243Sobrien 245195609Smp etraci("exp2x p1", p1, vp); 246195609Smp while (**vp && eq(**vp, STRor)) { 247231990Smp tcsh_number_t p2; 24859243Sobrien 24959243Sobrien (*vp)++; 250195609Smp p2 = compat_expr ? 251195609Smp exp2x(vp, ignore) : 252195609Smp exp2a(vp, ignore); 253195609Smp etraci("exp2x p2", p2, vp); 254195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 255195609Smp p1 = (p1 | p2); 256195609Smp etraci("exp2x p1", p1, vp); 257195609Smp if (compat_expr) 258195609Smp break; 25959243Sobrien } 26059243Sobrien return (p1); 26159243Sobrien} 26259243Sobrien 263231990Smpstatic tcsh_number_t 264167465Smpexp2a(Char ***vp, int ignore) 26559243Sobrien{ 266231990Smp tcsh_number_t p1 = exp2b(vp, ignore); 26759243Sobrien 26859243Sobrien etraci("exp2a p1", p1, vp); 269195609Smp while (**vp && eq(**vp, STRcaret)) { 270231990Smp tcsh_number_t p2; 27159243Sobrien 27259243Sobrien (*vp)++; 273195609Smp p2 = compat_expr ? 274195609Smp exp2a(vp, ignore) : 275195609Smp exp2b(vp, ignore); 27659243Sobrien etraci("exp2a p2", p2, vp); 277195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 278195609Smp p1 = (p1 ^ p2); 279195609Smp etraci("exp2a p1", p1, vp); 280195609Smp if (compat_expr) 281195609Smp break; 28259243Sobrien } 28359243Sobrien return (p1); 28459243Sobrien} 28559243Sobrien 286231990Smpstatic tcsh_number_t 287167465Smpexp2b(Char ***vp, int ignore) 28859243Sobrien{ 289231990Smp tcsh_number_t p1 = exp2c(vp, ignore); 29059243Sobrien 29159243Sobrien etraci("exp2b p1", p1, vp); 292195609Smp while (**vp && eq(**vp, STRand)) { 293231990Smp tcsh_number_t p2; 29459243Sobrien 29559243Sobrien (*vp)++; 296195609Smp p2 = compat_expr ? 297195609Smp exp2b(vp, ignore) : 298195609Smp exp2c(vp, ignore); 29959243Sobrien etraci("exp2b p2", p2, vp); 300195609Smp if (compat_expr || !(ignore & TEXP_IGNORE)) 301195609Smp p1 = (p1 & p2); 302195609Smp etraci("exp2b p1", p1, vp); 303195609Smp if (compat_expr) 304195609Smp break; 30559243Sobrien } 30659243Sobrien return (p1); 30759243Sobrien} 30859243Sobrien 309231990Smpstatic tcsh_number_t 310167465Smpexp2c(Char ***vp, int ignore) 31159243Sobrien{ 312145479Smp Char *p1 = exp3(vp, ignore); 313145479Smp Char *p2; 314231990Smp tcsh_number_t i; 31559243Sobrien 316167465Smp cleanup_push(p1, xfree); 31759243Sobrien etracc("exp2c p1", p1, vp); 31859243Sobrien if ((i = isa(**vp, EQOP)) != 0) { 31959243Sobrien (*vp)++; 32059243Sobrien if (i == EQMATCH || i == NOTEQMATCH) 32159243Sobrien ignore |= TEXP_NOGLOB; 32259243Sobrien p2 = exp3(vp, ignore); 323167465Smp cleanup_push(p2, xfree); 32459243Sobrien etracc("exp2c p2", p2, vp); 32559243Sobrien if (!(ignore & TEXP_IGNORE)) 326316958Sdchagin switch ((int)i) { 32759243Sobrien 32859243Sobrien case EQEQ: 32959243Sobrien i = eq(p1, p2); 33059243Sobrien break; 33159243Sobrien 33259243Sobrien case NOTEQ: 33359243Sobrien i = !eq(p1, p2); 33459243Sobrien break; 33559243Sobrien 33659243Sobrien case EQMATCH: 33759243Sobrien i = Gmatch(p1, p2); 33859243Sobrien break; 33959243Sobrien 34059243Sobrien case NOTEQMATCH: 34159243Sobrien i = !Gmatch(p1, p2); 34259243Sobrien break; 34359243Sobrien } 344167465Smp cleanup_until(p1); 34559243Sobrien return (i); 34659243Sobrien } 34759243Sobrien i = egetn(p1); 348167465Smp cleanup_until(p1); 34959243Sobrien return (i); 35059243Sobrien} 35159243Sobrien 35259243Sobrienstatic Char * 353167465Smpexp3(Char ***vp, int ignore) 35459243Sobrien{ 355145479Smp Char *p1, *p2; 356231990Smp tcsh_number_t i; 35759243Sobrien 35859243Sobrien p1 = exp3a(vp, ignore); 35959243Sobrien etracc("exp3 p1", p1, vp); 360195609Smp while ((i = isa(**vp, RELOP)) != 0) { 36159243Sobrien (*vp)++; 36259243Sobrien if (**vp && eq(**vp, STRequal)) 36359243Sobrien i |= 1, (*vp)++; 364167465Smp cleanup_push(p1, xfree); 365195609Smp p2 = compat_expr ? 366195609Smp exp3(vp, ignore) : 367195609Smp exp3a(vp, ignore); 368167465Smp cleanup_push(p2, xfree); 36959243Sobrien etracc("exp3 p2", p2, vp); 37059243Sobrien if (!(ignore & TEXP_IGNORE)) 371316958Sdchagin switch ((int)i) { 37259243Sobrien 37359243Sobrien case GTR: 37459243Sobrien i = egetn(p1) > egetn(p2); 37559243Sobrien break; 37659243Sobrien 37759243Sobrien case GTR | 1: 37859243Sobrien i = egetn(p1) >= egetn(p2); 37959243Sobrien break; 38059243Sobrien 38159243Sobrien case LSS: 38259243Sobrien i = egetn(p1) < egetn(p2); 38359243Sobrien break; 38459243Sobrien 38559243Sobrien case LSS | 1: 38659243Sobrien i = egetn(p1) <= egetn(p2); 38759243Sobrien break; 38859243Sobrien } 389167465Smp cleanup_until(p1); 390195609Smp p1 = putn(i); 391195609Smp etracc("exp3 p1", p1, vp); 392195609Smp if (compat_expr) 393195609Smp break; 39459243Sobrien } 39559243Sobrien return (p1); 39659243Sobrien} 39759243Sobrien 39859243Sobrienstatic Char * 399167465Smpexp3a(Char ***vp, int ignore) 40059243Sobrien{ 401167465Smp Char *p1, *p2; 402167465Smp const Char *op; 403231990Smp tcsh_number_t i; 40459243Sobrien 40559243Sobrien p1 = exp4(vp, ignore); 40659243Sobrien etracc("exp3a p1", p1, vp); 40759243Sobrien op = **vp; 40859243Sobrien if (op && any("<>", op[0]) && op[0] == op[1]) { 40959243Sobrien (*vp)++; 410167465Smp cleanup_push(p1, xfree); 411195609Smp p2 = compat_expr ? 412195609Smp exp3a(vp, ignore) : 413195609Smp exp4(vp, ignore); 414167465Smp cleanup_push(p2, xfree); 41559243Sobrien etracc("exp3a p2", p2, vp); 41659243Sobrien if (op[0] == '<') 41759243Sobrien i = egetn(p1) << egetn(p2); 41859243Sobrien else 41959243Sobrien i = egetn(p1) >> egetn(p2); 420167465Smp cleanup_until(p1); 421195609Smp p1 = putn(i); 422195609Smp etracc("exp3a p1", p1, vp); 42359243Sobrien } 42459243Sobrien return (p1); 42559243Sobrien} 42659243Sobrien 42759243Sobrienstatic Char * 428167465Smpexp4(Char ***vp, int ignore) 42959243Sobrien{ 430145479Smp Char *p1, *p2; 431231990Smp tcsh_number_t i = 0; 43259243Sobrien 43359243Sobrien p1 = exp5(vp, ignore); 43459243Sobrien etracc("exp4 p1", p1, vp); 435195609Smp while (isa(**vp, ADDOP)) { 436167465Smp const Char *op = *(*vp)++; 43759243Sobrien 438167465Smp cleanup_push(p1, xfree); 439195609Smp p2 = compat_expr ? 440195609Smp exp4(vp, ignore) : 441195609Smp exp5(vp, ignore); 442167465Smp cleanup_push(p2, xfree); 44359243Sobrien etracc("exp4 p2", p2, vp); 44459243Sobrien if (!(ignore & TEXP_IGNORE)) 44559243Sobrien switch (op[0]) { 44659243Sobrien 44759243Sobrien case '+': 44859243Sobrien i = egetn(p1) + egetn(p2); 44959243Sobrien break; 45059243Sobrien 45159243Sobrien case '-': 45259243Sobrien i = egetn(p1) - egetn(p2); 45359243Sobrien break; 45459243Sobrien } 455167465Smp cleanup_until(p1); 456195609Smp p1 = putn(i); 457195609Smp etracc("exp4 p1", p1, vp); 458195609Smp if (compat_expr) 459195609Smp break; 46059243Sobrien } 46159243Sobrien return (p1); 46259243Sobrien} 46359243Sobrien 46459243Sobrienstatic Char * 465167465Smpexp5(Char ***vp, int ignore) 46659243Sobrien{ 467145479Smp Char *p1, *p2; 468231990Smp tcsh_number_t i = 0; 46959243Sobrien 47059243Sobrien p1 = exp6(vp, ignore); 47159243Sobrien etracc("exp5 p1", p1, vp); 47259243Sobrien 473195609Smp while (isa(**vp, MULOP)) { 474167465Smp const Char *op = *(*vp)++; 475167465Smp if ((ignore & TEXP_NOGLOB) != 0) { 476167465Smp /* 47759243Sobrien * We are just trying to get the right side of 478167465Smp * a =~ or !~ operator 47959243Sobrien */ 480167465Smp xfree(p1); 48159243Sobrien return Strsave(op); 482167465Smp } 48359243Sobrien 484167465Smp cleanup_push(p1, xfree); 485195609Smp p2 = compat_expr ? 486195609Smp exp5(vp, ignore) : 487195609Smp exp6(vp, ignore); 488167465Smp cleanup_push(p2, xfree); 48959243Sobrien etracc("exp5 p2", p2, vp); 49059243Sobrien if (!(ignore & TEXP_IGNORE)) 49159243Sobrien switch (op[0]) { 49259243Sobrien 49359243Sobrien case '*': 49459243Sobrien i = egetn(p1) * egetn(p2); 49559243Sobrien break; 49659243Sobrien 49759243Sobrien case '/': 49859243Sobrien i = egetn(p2); 49959243Sobrien if (i == 0) 50059243Sobrien stderror(ERR_DIV0); 50159243Sobrien i = egetn(p1) / i; 50259243Sobrien break; 50359243Sobrien 50459243Sobrien case '%': 50559243Sobrien i = egetn(p2); 50659243Sobrien if (i == 0) 50759243Sobrien stderror(ERR_MOD0); 50859243Sobrien i = egetn(p1) % i; 50959243Sobrien break; 51059243Sobrien } 511167465Smp cleanup_until(p1); 512195609Smp p1 = putn(i); 513195609Smp etracc("exp5 p1", p1, vp); 514195609Smp if (compat_expr) 515195609Smp break; 51659243Sobrien } 51759243Sobrien return (p1); 51859243Sobrien} 51959243Sobrien 52059243Sobrienstatic Char * 521167465Smpexp6(Char ***vp, int ignore) 52259243Sobrien{ 523231990Smp tcsh_number_t ccode; 524231990Smp tcsh_number_t i = 0; 525145479Smp Char *cp; 52659243Sobrien 52759243Sobrien if (**vp == 0) 52859243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 52959243Sobrien if (eq(**vp, STRbang)) { 53059243Sobrien (*vp)++; 53159243Sobrien cp = exp6(vp, ignore); 532167465Smp cleanup_push(cp, xfree); 53359243Sobrien etracc("exp6 ! cp", cp, vp); 53459243Sobrien i = egetn(cp); 535167465Smp cleanup_until(cp); 53659243Sobrien return (putn(!i)); 53759243Sobrien } 53859243Sobrien if (eq(**vp, STRtilde)) { 53959243Sobrien (*vp)++; 54059243Sobrien cp = exp6(vp, ignore); 541167465Smp cleanup_push(cp, xfree); 54259243Sobrien etracc("exp6 ~ cp", cp, vp); 54359243Sobrien i = egetn(cp); 544167465Smp cleanup_until(cp); 54559243Sobrien return (putn(~i)); 54659243Sobrien } 54759243Sobrien if (eq(**vp, STRLparen)) { 54859243Sobrien (*vp)++; 54959243Sobrien ccode = exp0(vp, ignore); 55059243Sobrien etraci("exp6 () ccode", ccode, vp); 551167465Smp if (**vp == 0 || ***vp != ')') 55259243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 55359243Sobrien (*vp)++; 55459243Sobrien return (putn(ccode)); 55559243Sobrien } 55659243Sobrien if (eq(**vp, STRLbrace)) { 557145479Smp Char **v; 55859243Sobrien struct command faket; 55959243Sobrien Char *fakecom[2]; 56059243Sobrien 56159243Sobrien faket.t_dtyp = NODE_COMMAND; 56259243Sobrien faket.t_dflg = F_BACKQ; 56359243Sobrien faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL; 56459243Sobrien faket.t_dcom = fakecom; 56559243Sobrien fakecom[0] = STRfakecom; 56659243Sobrien fakecom[1] = NULL; 56759243Sobrien (*vp)++; 56859243Sobrien v = *vp; 56959243Sobrien for (;;) { 57059243Sobrien if (!**vp) 57159243Sobrien stderror(ERR_NAME | ERR_MISSING, '}'); 57259243Sobrien if (eq(*(*vp)++, STRRbrace)) 57359243Sobrien break; 57459243Sobrien } 57559243Sobrien if (ignore & TEXP_IGNORE) 57659243Sobrien return (Strsave(STRNULL)); 57759243Sobrien psavejob(); 578167465Smp cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */ 57959243Sobrien if (pfork(&faket, -1) == 0) { 58059243Sobrien *--(*vp) = 0; 58159243Sobrien evalav(v); 58259243Sobrien exitstat(); 58359243Sobrien } 58459243Sobrien pwait(); 585167465Smp cleanup_until(&faket); 58659243Sobrien etraci("exp6 {} status", egetn(varval(STRstatus)), vp); 58759243Sobrien return (putn(egetn(varval(STRstatus)) == 0)); 58859243Sobrien } 58959243Sobrien if (isa(**vp, ANYOP)) 59059243Sobrien return (Strsave(STRNULL)); 59159243Sobrien cp = *(*vp)++; 59259243Sobrien#ifdef convex 59359243Sobrien# define FILETESTS "erwxfdzoplstSXLbcugkmKR" 59459243Sobrien#else 59559243Sobrien# define FILETESTS "erwxfdzoplstSXLbcugkmK" 59659243Sobrien#endif /* convex */ 59759243Sobrien#define FILEVALS "ZAMCDIUGNFPL" 59859243Sobrien if (*cp == '-' && (any(FILETESTS, cp[1]) || any(FILEVALS, cp[1]))) 59959243Sobrien return(filetest(cp, vp, ignore)); 60059243Sobrien etracc("exp6 default", cp, vp); 60159243Sobrien return (ignore & TEXP_NOGLOB ? Strsave(cp) : globone(cp, G_APPEND)); 60259243Sobrien} 60359243Sobrien 60459243Sobrien 60559243Sobrien/* 60659243Sobrien * Extended file tests 60759243Sobrien * From: John Rowe <rowe@excc.exeter.ac.uk> 60859243Sobrien */ 60959243SobrienChar * 610167465Smpfiletest(Char *cp, Char ***vp, int ignore) 61159243Sobrien{ 61259243Sobrien#ifdef convex 61359243Sobrien struct cvxstat stb, *st = NULL; 61459243Sobrien# define TCSH_STAT stat64 61559243Sobrien#else 61659243Sobrien# define TCSH_STAT stat 61759243Sobrien struct stat stb, *st = NULL; 61859243Sobrien#endif /* convex */ 61959243Sobrien 62059243Sobrien#ifdef S_IFLNK 62159243Sobrien# ifdef convex 62259243Sobrien struct cvxstat lstb, *lst = NULL; 62359243Sobrien# define TCSH_LSTAT lstat64 62459243Sobrien# else 62559243Sobrien# define TCSH_LSTAT lstat 62659243Sobrien struct stat lstb, *lst = NULL; 62759243Sobrien# endif /* convex */ 62859243Sobrien char *filnam; 62959243Sobrien#endif /* S_IFLNK */ 63059243Sobrien 631231990Smp tcsh_number_t i = 0; 63259243Sobrien unsigned pmask = 0xffff; 633145479Smp int altout = 0; 63459243Sobrien Char *ft = cp, *dp, *ep, *strdev, *strino, *strF, *str, valtest = '\0', 63559243Sobrien *errval = STR0; 636316958Sdchagin char *string, string0[22 + MB_LEN_MAX + 1]; /* space for 64 bit octal */ 63759243Sobrien time_t footime; 63859243Sobrien struct passwd *pw; 63959243Sobrien struct group *gr; 64059243Sobrien 64159243Sobrien while(any(FILETESTS, *++ft)) 64259243Sobrien continue; 64359243Sobrien 64459243Sobrien if (!*ft && *(ft - 1) == 'L') 64559243Sobrien --ft; 64659243Sobrien 64759243Sobrien if (any(FILEVALS, *ft)) { 64859243Sobrien valtest = *ft++; 64959243Sobrien /* 65059243Sobrien * Value tests return '-1' on failure as 0 is 65159243Sobrien * a legitimate value for many of them. 65259243Sobrien * 'F' returns ':' for compatibility. 65359243Sobrien */ 65459243Sobrien errval = valtest == 'F' ? STRcolon : STRminus1; 65559243Sobrien 65659243Sobrien if (valtest == 'P' && *ft >= '0' && *ft <= '7') { 65759243Sobrien pmask = (char) *ft - '0'; 65859243Sobrien while ( *++ft >= '0' && *ft <= '7' ) 65959243Sobrien pmask = 8 * pmask + ((char) *ft - '0'); 66059243Sobrien } 66159243Sobrien if (Strcmp(ft, STRcolon) == 0 && any("AMCUGP", valtest)) { 66259243Sobrien altout = 1; 66359243Sobrien ++ft; 66459243Sobrien } 66559243Sobrien } 66659243Sobrien 66759243Sobrien if (*ft || ft == cp + 1) 66859243Sobrien stderror(ERR_NAME | ERR_FILEINQ); 66959243Sobrien 67059243Sobrien /* 67159243Sobrien * Detect missing file names by checking for operator in the file name 67259243Sobrien * position. However, if an operator name appears there, we must make 67359243Sobrien * sure that there's no file by that name (e.g., "/") before announcing 67459243Sobrien * an error. Even this check isn't quite right, since it doesn't take 67559243Sobrien * globbing into account. 67659243Sobrien */ 67759243Sobrien 67859243Sobrien if (isa(**vp, ANYOP) && TCSH_STAT(short2str(**vp), &stb)) 67959243Sobrien stderror(ERR_NAME | ERR_FILENAME); 68059243Sobrien 68159243Sobrien dp = *(*vp)++; 68259243Sobrien if (ignore & TEXP_IGNORE) 68359243Sobrien return (Strsave(STRNULL)); 68459243Sobrien ep = globone(dp, G_APPEND); 685167465Smp cleanup_push(ep, xfree); 68659243Sobrien ft = &cp[1]; 68759243Sobrien do 68859243Sobrien switch (*ft) { 68959243Sobrien 69059243Sobrien case 'r': 69159243Sobrien i = !sh_access(ep, R_OK); 69259243Sobrien break; 69359243Sobrien 69459243Sobrien case 'w': 69559243Sobrien i = !sh_access(ep, W_OK); 69659243Sobrien break; 69759243Sobrien 69859243Sobrien case 'x': 69959243Sobrien i = !sh_access(ep, X_OK); 70059243Sobrien break; 70159243Sobrien 70259243Sobrien case 'X': /* tcsh extension, name is an executable in the path 70359243Sobrien * or a tcsh builtin command 70459243Sobrien */ 70559243Sobrien i = find_cmd(ep, 0); 70659243Sobrien break; 70759243Sobrien 70859243Sobrien case 't': /* SGI extension, true when file is a tty */ 70959243Sobrien i = isatty(atoi(short2str(ep))); 71059243Sobrien break; 71159243Sobrien 71259243Sobrien default: 71359243Sobrien 71459243Sobrien#ifdef S_IFLNK 71559243Sobrien if (tolower(*ft) == 'l') { 71659243Sobrien /* 71759243Sobrien * avoid convex compiler bug. 71859243Sobrien */ 71959243Sobrien if (!lst) { 72059243Sobrien lst = &lstb; 72159243Sobrien if (TCSH_LSTAT(short2str(ep), lst) == -1) { 722167465Smp cleanup_until(ep); 72359243Sobrien return (Strsave(errval)); 72459243Sobrien } 72559243Sobrien } 72659243Sobrien if (*ft == 'L') 72759243Sobrien st = lst; 72859243Sobrien } 72959243Sobrien else 73059243Sobrien#endif /* S_IFLNK */ 73159243Sobrien /* 73259243Sobrien * avoid convex compiler bug. 73359243Sobrien */ 73459243Sobrien if (!st) { 73559243Sobrien st = &stb; 73659243Sobrien if (TCSH_STAT(short2str(ep), st) == -1) { 737167465Smp cleanup_until(ep); 73859243Sobrien return (Strsave(errval)); 73959243Sobrien } 74059243Sobrien } 74159243Sobrien 74259243Sobrien switch (*ft) { 74359243Sobrien 74459243Sobrien case 'f': 74559243Sobrien#ifdef S_ISREG 74659243Sobrien i = S_ISREG(st->st_mode); 74759243Sobrien#else /* !S_ISREG */ 74859243Sobrien i = 0; 74959243Sobrien#endif /* S_ISREG */ 75059243Sobrien break; 75159243Sobrien 75259243Sobrien case 'd': 75359243Sobrien#ifdef S_ISDIR 75459243Sobrien i = S_ISDIR(st->st_mode); 75559243Sobrien#else /* !S_ISDIR */ 75659243Sobrien i = 0; 75759243Sobrien#endif /* S_ISDIR */ 75859243Sobrien break; 75959243Sobrien 76059243Sobrien case 'p': 76159243Sobrien#ifdef S_ISFIFO 76259243Sobrien i = S_ISFIFO(st->st_mode); 76359243Sobrien#else /* !S_ISFIFO */ 76459243Sobrien i = 0; 76559243Sobrien#endif /* S_ISFIFO */ 76659243Sobrien break; 76759243Sobrien 76859243Sobrien case 'm' : 76959243Sobrien#ifdef S_ISOFL 77059243Sobrien i = S_ISOFL(st->st_dm_mode); 77159243Sobrien#else /* !S_ISOFL */ 77259243Sobrien i = 0; 77359243Sobrien#endif /* S_ISOFL */ 77459243Sobrien break ; 77559243Sobrien 77659243Sobrien case 'K' : 77759243Sobrien#ifdef S_ISOFL 77859243Sobrien i = stb.st_dm_key; 77959243Sobrien#else /* !S_ISOFL */ 78059243Sobrien i = 0; 78159243Sobrien#endif /* S_ISOFL */ 78259243Sobrien break ; 78359243Sobrien 78459243Sobrien 78559243Sobrien case 'l': 78659243Sobrien#ifdef S_ISLNK 78759243Sobrien i = S_ISLNK(lst->st_mode); 78859243Sobrien#else /* !S_ISLNK */ 78959243Sobrien i = 0; 79059243Sobrien#endif /* S_ISLNK */ 79159243Sobrien break; 79259243Sobrien 79359243Sobrien case 'S': 79459243Sobrien# ifdef S_ISSOCK 79559243Sobrien i = S_ISSOCK(st->st_mode); 79659243Sobrien# else /* !S_ISSOCK */ 79759243Sobrien i = 0; 79859243Sobrien# endif /* S_ISSOCK */ 79959243Sobrien break; 80059243Sobrien 80159243Sobrien case 'b': 80259243Sobrien#ifdef S_ISBLK 80359243Sobrien i = S_ISBLK(st->st_mode); 80459243Sobrien#else /* !S_ISBLK */ 80559243Sobrien i = 0; 80659243Sobrien#endif /* S_ISBLK */ 80759243Sobrien break; 80859243Sobrien 80959243Sobrien case 'c': 81059243Sobrien#ifdef S_ISCHR 81159243Sobrien i = S_ISCHR(st->st_mode); 81259243Sobrien#else /* !S_ISCHR */ 81359243Sobrien i = 0; 81459243Sobrien#endif /* S_ISCHR */ 81559243Sobrien break; 81659243Sobrien 81759243Sobrien case 'u': 81859243Sobrien i = (S_ISUID & st->st_mode) != 0; 81959243Sobrien break; 82059243Sobrien 82159243Sobrien case 'g': 82259243Sobrien i = (S_ISGID & st->st_mode) != 0; 82359243Sobrien break; 82459243Sobrien 82559243Sobrien case 'k': 82659243Sobrien i = (S_ISVTX & st->st_mode) != 0; 82759243Sobrien break; 82859243Sobrien 82959243Sobrien case 'z': 83059243Sobrien i = st->st_size == 0; 83159243Sobrien break; 83259243Sobrien 83359243Sobrien#ifdef convex 83459243Sobrien case 'R': 83559243Sobrien i = (stb.st_dmonflags & IMIGRATED) == IMIGRATED; 83659243Sobrien break; 83759243Sobrien#endif /* convex */ 83859243Sobrien 83959243Sobrien case 's': 84059243Sobrien i = stb.st_size != 0; 84159243Sobrien break; 84259243Sobrien 84359243Sobrien case 'e': 84459243Sobrien i = 1; 84559243Sobrien break; 84659243Sobrien 84759243Sobrien case 'o': 84859243Sobrien i = st->st_uid == uid; 84959243Sobrien break; 85059243Sobrien 85159243Sobrien /* 85259243Sobrien * Value operators are a tcsh extension. 85359243Sobrien */ 85459243Sobrien 85559243Sobrien case 'D': 856231990Smp i = (tcsh_number_t) st->st_dev; 85759243Sobrien break; 85859243Sobrien 85959243Sobrien case 'I': 860231990Smp i = (tcsh_number_t) st->st_ino; 86159243Sobrien break; 86259243Sobrien 86359243Sobrien case 'F': 86459243Sobrien strdev = putn( (int) st->st_dev); 86559243Sobrien strino = putn( (int) st->st_ino); 866167465Smp strF = xmalloc((2 + Strlen(strdev) + Strlen(strino)) 867167465Smp * sizeof(Char)); 86859243Sobrien (void) Strcat(Strcat(Strcpy(strF, strdev), STRcolon), strino); 869167465Smp xfree(strdev); 870167465Smp xfree(strino); 871167465Smp cleanup_until(ep); 87259243Sobrien return(strF); 87359243Sobrien 87459243Sobrien case 'L': 87559243Sobrien if ( *(ft + 1) ) { 87659243Sobrien i = 1; 87759243Sobrien break; 87859243Sobrien } 87959243Sobrien#ifdef S_ISLNK 88059243Sobrien filnam = short2str(ep); 881167465Smp string = areadlink(filnam); 882167465Smp strF = string == NULL ? errval : str2short(string); 883167465Smp xfree(string); 884167465Smp cleanup_until(ep); 88559243Sobrien return(Strsave(strF)); 88659243Sobrien 88759243Sobrien#else /* !S_ISLNK */ 88859243Sobrien i = 0; 88959243Sobrien break; 89059243Sobrien#endif /* S_ISLNK */ 89159243Sobrien 89259243Sobrien 89359243Sobrien case 'N': 894231990Smp i = (tcsh_number_t) st->st_nlink; 89559243Sobrien break; 89659243Sobrien 89759243Sobrien case 'P': 89859243Sobrien string = string0 + 1; 89959243Sobrien (void) xsnprintf(string, sizeof(string0) - 1, "%o", 90059243Sobrien pmask & (unsigned int) 90159243Sobrien ((S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID) & st->st_mode)); 90259243Sobrien if (altout && *string != '0') 90359243Sobrien *--string = '0'; 904167465Smp cleanup_until(ep); 90559243Sobrien return(Strsave(str2short(string))); 90659243Sobrien 90759243Sobrien case 'U': 908167465Smp if (altout && (pw = xgetpwuid(st->st_uid))) { 909167465Smp cleanup_until(ep); 91059243Sobrien return(Strsave(str2short(pw->pw_name))); 91159243Sobrien } 912231990Smp i = (tcsh_number_t) st->st_uid; 91359243Sobrien break; 91459243Sobrien 91559243Sobrien case 'G': 916167465Smp if (altout && (gr = xgetgrgid(st->st_gid))) { 917167465Smp cleanup_until(ep); 91859243Sobrien return(Strsave(str2short(gr->gr_name))); 91959243Sobrien } 920231990Smp i = (tcsh_number_t) st->st_gid; 92159243Sobrien break; 92259243Sobrien 92359243Sobrien case 'Z': 924231990Smp i = (tcsh_number_t) st->st_size; 92559243Sobrien break; 92659243Sobrien 92759243Sobrien case 'A': case 'M': case 'C': 92859243Sobrien footime = *ft == 'A' ? st->st_atime : 92959243Sobrien *ft == 'M' ? st->st_mtime : st->st_ctime; 93059243Sobrien if (altout) { 93159243Sobrien strF = str2short(ctime(&footime)); 93259243Sobrien if ((str = Strchr(strF, '\n')) != NULL) 93359243Sobrien *str = (Char) '\0'; 934167465Smp cleanup_until(ep); 93559243Sobrien return(Strsave(strF)); 93659243Sobrien } 937231990Smp i = (tcsh_number_t) footime; 93859243Sobrien break; 93959243Sobrien 94059243Sobrien } 94159243Sobrien } 94259243Sobrien while (*++ft && i); 94359243Sobrien etraci("exp6 -? i", i, vp); 944167465Smp cleanup_until(ep); 94559243Sobrien return (putn(i)); 94659243Sobrien} 94759243Sobrien 94859243Sobrien 94959243Sobrienstatic void 950167465Smpevalav(Char **v) 95159243Sobrien{ 95259243Sobrien struct wordent paraml1; 953145479Smp struct wordent *hp = ¶ml1; 95459243Sobrien struct command *t; 955145479Smp struct wordent *wdp = hp; 95659243Sobrien 957167465Smp setcopy(STRstatus, STR0, VAR_READWRITE); 958316958Sdchagin initlex(hp); 95959243Sobrien while (*v) { 960167465Smp struct wordent *new = xcalloc(1, sizeof *wdp); 96159243Sobrien 96259243Sobrien new->prev = wdp; 96359243Sobrien new->next = hp; 96459243Sobrien wdp->next = new; 96559243Sobrien wdp = new; 96659243Sobrien wdp->word = Strsave(*v++); 96759243Sobrien } 96859243Sobrien hp->prev = wdp; 969167465Smp cleanup_push(¶ml1, lex_cleanup); 97059243Sobrien alias(¶ml1); 97159243Sobrien t = syntax(paraml1.next, ¶ml1, 0); 972167465Smp cleanup_push(t, syntax_cleanup); 97359243Sobrien if (seterr) 97459243Sobrien stderror(ERR_OLD); 975100616Smp execute(t, -1, NULL, NULL, TRUE); 976167465Smp cleanup_until(¶ml1); 97759243Sobrien} 97859243Sobrien 97959243Sobrienstatic int 980167465Smpisa(Char *cp, int what) 98159243Sobrien{ 98259243Sobrien if (cp == 0) 98359243Sobrien return ((what & RESTOP) != 0); 98459243Sobrien if (*cp == '\0') 98559243Sobrien return 0; 98659243Sobrien if (cp[1] == 0) { 98759243Sobrien if (what & ADDOP && (*cp == '+' || *cp == '-')) 98859243Sobrien return (1); 98959243Sobrien if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) 99059243Sobrien return (1); 99159243Sobrien if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || 99259243Sobrien *cp == '~' || *cp == '^' || *cp == '"')) 99359243Sobrien return (1); 99459243Sobrien } 99559243Sobrien else if (cp[2] == 0) { 99659243Sobrien if (what & RESTOP) { 99759243Sobrien if (cp[0] == '|' && cp[1] == '&') 99859243Sobrien return (1); 99959243Sobrien if (cp[0] == '<' && cp[1] == '<') 100059243Sobrien return (1); 100159243Sobrien if (cp[0] == '>' && cp[1] == '>') 100259243Sobrien return (1); 100359243Sobrien } 100459243Sobrien if (what & EQOP) { 100559243Sobrien if (cp[0] == '=') { 100659243Sobrien if (cp[1] == '=') 100759243Sobrien return (EQEQ); 100859243Sobrien if (cp[1] == '~') 100959243Sobrien return (EQMATCH); 101059243Sobrien } 101159243Sobrien else if (cp[0] == '!') { 101259243Sobrien if (cp[1] == '=') 101359243Sobrien return (NOTEQ); 101459243Sobrien if (cp[1] == '~') 101559243Sobrien return (NOTEQMATCH); 101659243Sobrien } 101759243Sobrien } 101859243Sobrien } 101959243Sobrien if (what & RELOP) { 102059243Sobrien if (*cp == '<') 102159243Sobrien return (LSS); 102259243Sobrien if (*cp == '>') 102359243Sobrien return (GTR); 102459243Sobrien } 102559243Sobrien return (0); 102659243Sobrien} 102759243Sobrien 1028231990Smpstatic tcsh_number_t 1029231990Smpegetn(const Char *cp) 103059243Sobrien{ 103159243Sobrien if (*cp && *cp != '-' && !Isdigit(*cp)) 103259243Sobrien stderror(ERR_NAME | ERR_EXPRESSION); 103359243Sobrien return (getn(cp)); 103459243Sobrien} 103559243Sobrien 103659243Sobrien/* Phew! */ 103759243Sobrien 103859243Sobrien#ifdef EDEBUG 103959243Sobrienstatic void 1040231990Smpetraci(const char *str, tcsh_number_t i, Char ***vp) 104159243Sobrien{ 1042231990Smp#ifdef HAVE_LONG_LONG 1043231990Smp xprintf("%s=%lld\t", str, i); 1044231990Smp#else 1045231990Smp xprintf("%s=%ld\t", str, i); 1046231990Smp#endif 104759243Sobrien blkpr(*vp); 104859243Sobrien xputchar('\n'); 104959243Sobrien} 105059243Sobrienstatic void 1051195609Smpetracc(const char *str, const Char *cp, Char ***vp) 105259243Sobrien{ 1053195609Smp xprintf("%s=%S\t", str, cp); 105459243Sobrien blkpr(*vp); 105559243Sobrien xputchar('\n'); 105659243Sobrien} 105759243Sobrien#endif /* EDEBUG */ 1058