147116Sbostic /*- 247116Sbostic * Copyright (c) 1991 The Regents of the University of California. 347116Sbostic * All rights reserved. 447116Sbostic * 547116Sbostic * This code is derived from software contributed to Berkeley by 647116Sbostic * Kenneth Almquist. 747116Sbostic * 847116Sbostic * %sccs.include.redist.c% 947116Sbostic */ 1047116Sbostic 1147116Sbostic #ifndef lint 12*53299Smarc static char sccsid[] = "@(#)expand.c 5.3 (Berkeley) 04/30/92"; 1347116Sbostic #endif /* not lint */ 1447116Sbostic 1547116Sbostic /* 1647116Sbostic * Routines to expand arguments to commands. We have to deal with 1747116Sbostic * backquotes, shell variables, and file metacharacters. 1847116Sbostic */ 1947116Sbostic 2047116Sbostic #include "shell.h" 2147116Sbostic #include "main.h" 2247116Sbostic #include "nodes.h" 2347116Sbostic #include "eval.h" 2447116Sbostic #include "expand.h" 2547116Sbostic #include "syntax.h" 2647116Sbostic #include "parser.h" 2747116Sbostic #include "jobs.h" 2847116Sbostic #include "options.h" 2947116Sbostic #include "var.h" 3047116Sbostic #include "input.h" 3147116Sbostic #include "output.h" 3247116Sbostic #include "memalloc.h" 3347116Sbostic #include "error.h" 3447116Sbostic #include "mystring.h" 3547116Sbostic #include <sys/types.h> 3653276Smarc #include <sys/time.h> 3747116Sbostic #include <sys/stat.h> 3847116Sbostic #include <errno.h> 3947116Sbostic #include <dirent.h> 40*53299Smarc #include <pwd.h> 4147116Sbostic 4247116Sbostic /* 4347116Sbostic * Structure specifying which parts of the string should be searched 4447116Sbostic * for IFS characters. 4547116Sbostic */ 4647116Sbostic 4747116Sbostic struct ifsregion { 4847116Sbostic struct ifsregion *next; /* next region in list */ 4947116Sbostic int begoff; /* offset of start of region */ 5047116Sbostic int endoff; /* offset of end of region */ 5147116Sbostic int nulonly; /* search for nul bytes only */ 5247116Sbostic }; 5347116Sbostic 5447116Sbostic 5547116Sbostic char *expdest; /* output of current string */ 5647116Sbostic struct nodelist *argbackq; /* list of back quote expressions */ 5747116Sbostic struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 5847116Sbostic struct ifsregion *ifslastp; /* last struct in list */ 5947116Sbostic struct arglist exparg; /* holds expanded arg list */ 6047116Sbostic 6147116Sbostic #ifdef __STDC__ 6247116Sbostic STATIC void argstr(char *, int); 6347116Sbostic STATIC void expbackq(union node *, int, int); 6447116Sbostic STATIC char *evalvar(char *, int); 6547116Sbostic STATIC int varisset(int); 6647116Sbostic STATIC void varvalue(int, int, int); 6747116Sbostic STATIC void recordregion(int, int, int); 6847116Sbostic STATIC void ifsbreakup(char *, struct arglist *); 69*53299Smarc STATIC void expandmeta(struct strlist *, int); 7047116Sbostic STATIC void expmeta(char *, char *); 7147116Sbostic STATIC void addfname(char *); 7247116Sbostic STATIC struct strlist *expsort(struct strlist *); 7347116Sbostic STATIC struct strlist *msort(struct strlist *, int); 7447116Sbostic STATIC int pmatch(char *, char *); 75*53299Smarc STATIC char *exptilde(char *, int); 7647116Sbostic #else 7747116Sbostic STATIC void argstr(); 7847116Sbostic STATIC void expbackq(); 7947116Sbostic STATIC char *evalvar(); 8047116Sbostic STATIC int varisset(); 8147116Sbostic STATIC void varvalue(); 8247116Sbostic STATIC void recordregion(); 8347116Sbostic STATIC void ifsbreakup(); 8447116Sbostic STATIC void expandmeta(); 8547116Sbostic STATIC void expmeta(); 8647116Sbostic STATIC void addfname(); 8747116Sbostic STATIC struct strlist *expsort(); 8847116Sbostic STATIC struct strlist *msort(); 8947116Sbostic STATIC int pmatch(); 90*53299Smarc STATIC char *exptilde(); 9147116Sbostic #endif 9247116Sbostic 9347116Sbostic /* 9447116Sbostic * Expand shell variables and backquotes inside a here document. 9547116Sbostic */ 9647116Sbostic 9747116Sbostic void 9847116Sbostic expandhere(arg, fd) 9947116Sbostic union node *arg; /* the document */ 10047116Sbostic int fd; /* where to write the expanded version */ 10147116Sbostic { 10247116Sbostic herefd = fd; 10347116Sbostic expandarg(arg, (struct arglist *)NULL, 0); 10447116Sbostic xwrite(fd, stackblock(), expdest - stackblock()); 10547116Sbostic } 10647116Sbostic 10747116Sbostic 10847116Sbostic /* 10947116Sbostic * Perform variable substitution and command substitution on an argument, 11047116Sbostic * placing the resulting list of arguments in arglist. If full is true, 11147116Sbostic * perform splitting and file name expansion. When arglist is NULL, perform 11247116Sbostic * here document expansion. 11347116Sbostic */ 11447116Sbostic 11547116Sbostic void 116*53299Smarc expandarg(arg, arglist, flag) 11747116Sbostic union node *arg; 11847116Sbostic struct arglist *arglist; 11947116Sbostic { 12047116Sbostic struct strlist *sp; 12147116Sbostic char *p; 12247116Sbostic 12347116Sbostic argbackq = arg->narg.backquote; 12447116Sbostic STARTSTACKSTR(expdest); 12547116Sbostic ifsfirst.next = NULL; 12647116Sbostic ifslastp = NULL; 127*53299Smarc argstr(arg->narg.text, flag); 12847116Sbostic if (arglist == NULL) 12947116Sbostic return; /* here document expanded */ 13047116Sbostic STPUTC('\0', expdest); 13147116Sbostic p = grabstackstr(expdest); 13247116Sbostic exparg.lastp = &exparg.list; 133*53299Smarc /* 134*53299Smarc * TODO - EXP_REDIR 135*53299Smarc */ 136*53299Smarc if (flag & EXP_FULL) { 13747116Sbostic ifsbreakup(p, &exparg); 13847116Sbostic *exparg.lastp = NULL; 13947116Sbostic exparg.lastp = &exparg.list; 140*53299Smarc expandmeta(exparg.list, flag); 14147116Sbostic } else { 14247116Sbostic sp = (struct strlist *)stalloc(sizeof (struct strlist)); 14347116Sbostic sp->text = p; 14447116Sbostic *exparg.lastp = sp; 14547116Sbostic exparg.lastp = &sp->next; 14647116Sbostic } 14747116Sbostic while (ifsfirst.next != NULL) { 14847116Sbostic struct ifsregion *ifsp; 14947116Sbostic INTOFF; 15047116Sbostic ifsp = ifsfirst.next->next; 15147116Sbostic ckfree(ifsfirst.next); 15247116Sbostic ifsfirst.next = ifsp; 15347116Sbostic INTON; 15447116Sbostic } 15547116Sbostic *exparg.lastp = NULL; 15647116Sbostic if (exparg.list) { 15747116Sbostic *arglist->lastp = exparg.list; 15847116Sbostic arglist->lastp = exparg.lastp; 15947116Sbostic } 16047116Sbostic } 16147116Sbostic 16247116Sbostic 16347116Sbostic 16447116Sbostic /* 16547116Sbostic * Perform variable and command substitution. If full is set, output CTLESC 16647116Sbostic * characters to allow for further processing. If full is not set, treat 16747116Sbostic * $@ like $* since no splitting will be performed. 16847116Sbostic */ 16947116Sbostic 17047116Sbostic STATIC void 171*53299Smarc argstr(p, flag) 17247116Sbostic register char *p; 17347116Sbostic { 17447116Sbostic char c; 17553276Smarc int sawari = 0; 176*53299Smarc int full = flag & EXP_FULL; /* is there a later stage? */ 177*53299Smarc int vartilde = flag & EXP_VARTILDE; 17847116Sbostic 179*53299Smarc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 180*53299Smarc p = exptilde(p, vartilde); 18147116Sbostic for (;;) { 18247116Sbostic switch (c = *p++) { 18347116Sbostic case '\0': 18453276Smarc case CTLENDVAR: /* ??? */ 18547116Sbostic goto breakloop; 18647116Sbostic case CTLESC: 18747116Sbostic if (full) 18847116Sbostic STPUTC(c, expdest); 18947116Sbostic c = *p++; 19047116Sbostic STPUTC(c, expdest); 19147116Sbostic break; 19247116Sbostic case CTLVAR: 193*53299Smarc p = evalvar(p, flag); 19447116Sbostic break; 19547116Sbostic case CTLBACKQ: 19647116Sbostic case CTLBACKQ|CTLQUOTE: 19747116Sbostic expbackq(argbackq->n, c & CTLQUOTE, full); 19847116Sbostic argbackq = argbackq->next; 19947116Sbostic break; 20053276Smarc case CTLENDARI: 20153276Smarc expari(); 20253276Smarc break; 203*53299Smarc case ':': 204*53299Smarc if (vartilde) 205*53299Smarc p = exptilde(p, vartilde); 20647116Sbostic default: 20747116Sbostic STPUTC(c, expdest); 20847116Sbostic } 20947116Sbostic } 21047116Sbostic breakloop:; 21147116Sbostic } 21247116Sbostic 213*53299Smarc STATIC char * 214*53299Smarc exptilde(p, vartilde) 215*53299Smarc char *p; 216*53299Smarc { 217*53299Smarc char c, *startp = p; 218*53299Smarc struct passwd *pw; 219*53299Smarc char *home; 220*53299Smarc 221*53299Smarc while (c = *p) { 222*53299Smarc switch(c) { 223*53299Smarc case CTLESC: 224*53299Smarc return (startp); 225*53299Smarc case ':': 226*53299Smarc if (vartilde) 227*53299Smarc goto done; 228*53299Smarc break; 229*53299Smarc case '/': 230*53299Smarc goto done; 231*53299Smarc } 232*53299Smarc p++; 233*53299Smarc } 234*53299Smarc done: 235*53299Smarc *p = '\0'; 236*53299Smarc if (*(startp+1) == '\0') { 237*53299Smarc if ((home = lookupvar("HOME")) == NULL) 238*53299Smarc goto lose; 239*53299Smarc } else { 240*53299Smarc if ((pw = getpwnam(startp+1)) == NULL) 241*53299Smarc goto lose; 242*53299Smarc home = pw->pw_dir; 243*53299Smarc } 244*53299Smarc if (*home == '\0') 245*53299Smarc goto lose; 246*53299Smarc *p = c; 247*53299Smarc while (c = *home++) { 248*53299Smarc if (SQSYNTAX[c] == CCTL) 249*53299Smarc STPUTC(CTLESC, expdest); 250*53299Smarc STPUTC(c, expdest); 251*53299Smarc } 252*53299Smarc return (p); 253*53299Smarc lose: 254*53299Smarc *p = c; 255*53299Smarc return (startp); 256*53299Smarc } 257*53299Smarc 258*53299Smarc 25953276Smarc /* 26053276Smarc * Expand arithmetic expression. Backup to start of expression, 26153276Smarc * evaluate, place result in (backed up) result, adjust string position. 26253276Smarc */ 26353276Smarc expari() 264*53299Smarc { 26553276Smarc char *p, *start = stackblock(); 26653276Smarc int result; 26747116Sbostic 26853276Smarc /* 26953276Smarc * This routine is slightly over-compilcated for 27053276Smarc * efficiency. First we make sure there is 27153276Smarc * enough space for the result, which may be bigger 27253276Smarc * than the expression if we add exponentation. Next we 27353276Smarc * scan backwards looking for the start of arithmetic. If the 27453276Smarc * next previous character is a CTLESC character, then we 27553276Smarc * have to rescan starting from the beginning since CTLESC 27653276Smarc * characters have to be processed left to right. 27753276Smarc */ 27853276Smarc CHECKSTRSPACE(8, expdest); 27953276Smarc USTPUTC('\0', expdest); 28053276Smarc p = expdest; 28153276Smarc while (*p != CTLARI && p >= start) 28253276Smarc --p; 28353276Smarc if (*p != CTLARI) 28453276Smarc error("missing CTLARI (shouldn't happen)"); 28553276Smarc if (p > start && *(p-1) == CTLESC) 28653276Smarc for (p = start; *p != CTLARI; p++) 28753276Smarc if (*p == CTLESC) 28853276Smarc p++; 28953276Smarc rmescapes(p+1); 29053276Smarc result = arith(p+1); 29153276Smarc fmtstr(p, 10, "%d", result); 29253276Smarc while (*p++) 29353276Smarc ; 29453276Smarc result = expdest - p + 1; 29553276Smarc STADJUST(-result, expdest); 29653276Smarc } 29753276Smarc 29853276Smarc 29947116Sbostic /* 30047116Sbostic * Expand stuff in backwards quotes. 30147116Sbostic */ 30247116Sbostic 30347116Sbostic STATIC void 30447116Sbostic expbackq(cmd, quoted, full) 30547116Sbostic union node *cmd; 30647116Sbostic { 30747116Sbostic struct backcmd in; 30847116Sbostic int i; 30947116Sbostic char buf[128]; 31047116Sbostic char *p; 31147116Sbostic char *dest = expdest; 31247116Sbostic struct ifsregion saveifs, *savelastp; 31347116Sbostic struct nodelist *saveargbackq; 31447116Sbostic char lastc; 31547116Sbostic int startloc = dest - stackblock(); 31647116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 31747116Sbostic int saveherefd; 31847116Sbostic 31947116Sbostic INTOFF; 32047116Sbostic saveifs = ifsfirst; 32147116Sbostic savelastp = ifslastp; 32247116Sbostic saveargbackq = argbackq; 32347116Sbostic saveherefd = herefd; 32447116Sbostic herefd = -1; 32547116Sbostic p = grabstackstr(dest); 32647116Sbostic evalbackcmd(cmd, &in); 32747116Sbostic ungrabstackstr(p, dest); 32847116Sbostic ifsfirst = saveifs; 32947116Sbostic ifslastp = savelastp; 33047116Sbostic argbackq = saveargbackq; 33147116Sbostic herefd = saveherefd; 33247116Sbostic 33347116Sbostic p = in.buf; 33447116Sbostic lastc = '\0'; 33547116Sbostic for (;;) { 33647116Sbostic if (--in.nleft < 0) { 33747116Sbostic if (in.fd < 0) 33847116Sbostic break; 33947116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 34047116Sbostic TRACE(("expbackq: read returns %d\n", i)); 34147116Sbostic if (i <= 0) 34247116Sbostic break; 34347116Sbostic p = buf; 34447116Sbostic in.nleft = i - 1; 34547116Sbostic } 34647116Sbostic lastc = *p++; 34747116Sbostic if (lastc != '\0') { 34847116Sbostic if (full && syntax[lastc] == CCTL) 34947116Sbostic STPUTC(CTLESC, dest); 35047116Sbostic STPUTC(lastc, dest); 35147116Sbostic } 35247116Sbostic } 35347116Sbostic if (lastc == '\n') { 35447116Sbostic STUNPUTC(dest); 35547116Sbostic } 35647116Sbostic if (in.fd >= 0) 35747116Sbostic close(in.fd); 35847116Sbostic if (in.buf) 35947116Sbostic ckfree(in.buf); 36047116Sbostic if (in.jp) 36147116Sbostic waitforjob(in.jp); 36247116Sbostic if (quoted == 0) 36347116Sbostic recordregion(startloc, dest - stackblock(), 0); 36447116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 36547116Sbostic (dest - stackblock()) - startloc, 36647116Sbostic (dest - stackblock()) - startloc, 36747116Sbostic stackblock() + startloc)); 36847116Sbostic expdest = dest; 36947116Sbostic INTON; 37047116Sbostic } 37147116Sbostic 37247116Sbostic 37347116Sbostic 37447116Sbostic /* 37547116Sbostic * Expand a variable, and return a pointer to the next character in the 37647116Sbostic * input string. 37747116Sbostic */ 37847116Sbostic 37947116Sbostic STATIC char * 380*53299Smarc evalvar(p, flag) 38147116Sbostic char *p; 38247116Sbostic { 38347116Sbostic int subtype; 384*53299Smarc int varflags; 38547116Sbostic char *var; 38647116Sbostic char *val; 38747116Sbostic int c; 38847116Sbostic int set; 38947116Sbostic int special; 39047116Sbostic int startloc; 39147116Sbostic 392*53299Smarc varflags = *p++; 393*53299Smarc subtype = varflags & VSTYPE; 39447116Sbostic var = p; 39547116Sbostic special = 0; 39647116Sbostic if (! is_name(*p)) 39747116Sbostic special = 1; 39847116Sbostic p = strchr(p, '=') + 1; 39947116Sbostic again: /* jump here after setting a variable with ${var=text} */ 40047116Sbostic if (special) { 40147116Sbostic set = varisset(*var); 40247116Sbostic val = NULL; 40347116Sbostic } else { 40447116Sbostic val = lookupvar(var); 405*53299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 40647116Sbostic val = NULL; 40747116Sbostic set = 0; 40847116Sbostic } else 40947116Sbostic set = 1; 41047116Sbostic } 41147116Sbostic startloc = expdest - stackblock(); 41247116Sbostic if (set && subtype != VSPLUS) { 41347116Sbostic /* insert the value of the variable */ 41447116Sbostic if (special) { 415*53299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 41647116Sbostic } else { 417*53299Smarc char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 41847116Sbostic 41947116Sbostic while (*val) { 420*53299Smarc if ((flag & EXP_FULL) && syntax[*val] == CCTL) 42147116Sbostic STPUTC(CTLESC, expdest); 42247116Sbostic STPUTC(*val++, expdest); 42347116Sbostic } 42447116Sbostic } 42547116Sbostic } 42647116Sbostic if (subtype == VSPLUS) 42747116Sbostic set = ! set; 428*53299Smarc if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 42947116Sbostic && (set || subtype == VSNORMAL)) 430*53299Smarc recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 43147116Sbostic if (! set && subtype != VSNORMAL) { 43247116Sbostic if (subtype == VSPLUS || subtype == VSMINUS) { 433*53299Smarc argstr(p, flag); 43447116Sbostic } else { 43547116Sbostic char *startp; 43647116Sbostic int saveherefd = herefd; 43747116Sbostic herefd = -1; 43847116Sbostic argstr(p, 0); 43947116Sbostic STACKSTRNUL(expdest); 44047116Sbostic herefd = saveherefd; 44147116Sbostic startp = stackblock() + startloc; 44247116Sbostic if (subtype == VSASSIGN) { 44347116Sbostic setvar(var, startp, 0); 44447116Sbostic STADJUST(startp - expdest, expdest); 445*53299Smarc varflags &=~ VSNUL; 44647116Sbostic goto again; 44747116Sbostic } 44847116Sbostic /* subtype == VSQUESTION */ 44947116Sbostic if (*p != CTLENDVAR) { 45047116Sbostic outfmt(&errout, "%s\n", startp); 45147116Sbostic error((char *)NULL); 45247116Sbostic } 45347116Sbostic error("%.*s: parameter %snot set", p - var - 1, 454*53299Smarc var, (varflags & VSNUL)? "null or " : nullstr); 45547116Sbostic } 45647116Sbostic } 45747116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 45847116Sbostic int nesting = 1; 45947116Sbostic for (;;) { 46047116Sbostic if ((c = *p++) == CTLESC) 46147116Sbostic p++; 46247116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 46347116Sbostic if (set) 46447116Sbostic argbackq = argbackq->next; 46547116Sbostic } else if (c == CTLVAR) { 46647116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 46747116Sbostic nesting++; 46847116Sbostic } else if (c == CTLENDVAR) { 46947116Sbostic if (--nesting == 0) 47047116Sbostic break; 47147116Sbostic } 47247116Sbostic } 47347116Sbostic } 47447116Sbostic return p; 47547116Sbostic } 47647116Sbostic 47747116Sbostic 47847116Sbostic 47947116Sbostic /* 48047116Sbostic * Test whether a specialized variable is set. 48147116Sbostic */ 48247116Sbostic 48347116Sbostic STATIC int 48447116Sbostic varisset(name) 48547116Sbostic char name; 48647116Sbostic { 48747116Sbostic char **ap; 48847116Sbostic 48947116Sbostic if (name == '!') { 49047116Sbostic if (backgndpid == -1) 49147116Sbostic return 0; 49247116Sbostic } else if (name == '@' || name == '*') { 49347116Sbostic if (*shellparam.p == NULL) 49447116Sbostic return 0; 49547116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 49647116Sbostic ap = shellparam.p; 49747116Sbostic do { 49847116Sbostic if (*ap++ == NULL) 49947116Sbostic return 0; 50047116Sbostic } while (--name >= 0); 50147116Sbostic } 50247116Sbostic return 1; 50347116Sbostic } 50447116Sbostic 50547116Sbostic 50647116Sbostic 50747116Sbostic /* 50847116Sbostic * Add the value of a specialized variable to the stack string. 50947116Sbostic */ 51047116Sbostic 51147116Sbostic STATIC void 51247116Sbostic varvalue(name, quoted, allow_split) 51347116Sbostic char name; 51447116Sbostic { 51547116Sbostic int num; 51647116Sbostic char temp[32]; 51747116Sbostic char *p; 51847116Sbostic int i; 51947116Sbostic extern int exitstatus; 52047116Sbostic char sep; 52147116Sbostic char **ap; 52247116Sbostic char const *syntax; 52347116Sbostic 52447116Sbostic switch (name) { 52547116Sbostic case '$': 52647116Sbostic num = rootpid; 52747116Sbostic goto numvar; 52847116Sbostic case '?': 52947116Sbostic num = exitstatus; 53047116Sbostic goto numvar; 53147116Sbostic case '#': 53247116Sbostic num = shellparam.nparam; 53347116Sbostic goto numvar; 53447116Sbostic case '!': 53547116Sbostic num = backgndpid; 53647116Sbostic numvar: 53747116Sbostic p = temp + 31; 53847116Sbostic temp[31] = '\0'; 53947116Sbostic do { 54047116Sbostic *--p = num % 10 + '0'; 54147116Sbostic } while ((num /= 10) != 0); 54247116Sbostic while (*p) 54347116Sbostic STPUTC(*p++, expdest); 54447116Sbostic break; 54547116Sbostic case '-': 54647116Sbostic for (i = 0 ; optchar[i] ; i++) { 54747116Sbostic if (optval[i]) 54847116Sbostic STPUTC(optchar[i], expdest); 54947116Sbostic } 55047116Sbostic break; 55147116Sbostic case '@': 55247116Sbostic if (allow_split) { 55347116Sbostic sep = '\0'; 55447116Sbostic goto allargs; 55547116Sbostic } 55647116Sbostic /* fall through */ 55747116Sbostic case '*': 55847116Sbostic sep = ' '; 55947116Sbostic allargs: 56047116Sbostic syntax = quoted? DQSYNTAX : BASESYNTAX; 56147116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 56247116Sbostic /* should insert CTLESC characters */ 56347116Sbostic while (*p) { 56447116Sbostic if (syntax[*p] == CCTL) 56547116Sbostic STPUTC(CTLESC, expdest); 56647116Sbostic STPUTC(*p++, expdest); 56747116Sbostic } 56847116Sbostic if (*ap) 56947116Sbostic STPUTC(sep, expdest); 57047116Sbostic } 57147116Sbostic break; 57247116Sbostic case '0': 57347116Sbostic p = arg0; 57447116Sbostic string: 57547116Sbostic syntax = quoted? DQSYNTAX : BASESYNTAX; 57647116Sbostic while (*p) { 57747116Sbostic if (syntax[*p] == CCTL) 57847116Sbostic STPUTC(CTLESC, expdest); 57947116Sbostic STPUTC(*p++, expdest); 58047116Sbostic } 58147116Sbostic break; 58247116Sbostic default: 58347116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 58447116Sbostic p = shellparam.p[name]; 58547116Sbostic goto string; 58647116Sbostic } 58747116Sbostic break; 58847116Sbostic } 58947116Sbostic } 59047116Sbostic 59147116Sbostic 59247116Sbostic 59347116Sbostic /* 59447116Sbostic * Record the the fact that we have to scan this region of the 59547116Sbostic * string for IFS characters. 59647116Sbostic */ 59747116Sbostic 59847116Sbostic STATIC void 59947116Sbostic recordregion(start, end, nulonly) { 60047116Sbostic register struct ifsregion *ifsp; 60147116Sbostic 60247116Sbostic if (ifslastp == NULL) { 60347116Sbostic ifsp = &ifsfirst; 60447116Sbostic } else { 60547116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 60647116Sbostic ifslastp->next = ifsp; 60747116Sbostic } 60847116Sbostic ifslastp = ifsp; 60947116Sbostic ifslastp->next = NULL; 61047116Sbostic ifslastp->begoff = start; 61147116Sbostic ifslastp->endoff = end; 61247116Sbostic ifslastp->nulonly = nulonly; 61347116Sbostic } 61447116Sbostic 61547116Sbostic 61647116Sbostic 61747116Sbostic /* 61847116Sbostic * Break the argument string into pieces based upon IFS and add the 61947116Sbostic * strings to the argument list. The regions of the string to be 62047116Sbostic * searched for IFS characters have been stored by recordregion. 62147116Sbostic */ 62247116Sbostic 62347116Sbostic STATIC void 62447116Sbostic ifsbreakup(string, arglist) 62547116Sbostic char *string; 62647116Sbostic struct arglist *arglist; 62747116Sbostic { 62847116Sbostic struct ifsregion *ifsp; 62947116Sbostic struct strlist *sp; 63047116Sbostic char *start; 63147116Sbostic register char *p; 63247116Sbostic char *q; 63347116Sbostic char *ifs; 63447116Sbostic 63547116Sbostic start = string; 63647116Sbostic if (ifslastp != NULL) { 63747116Sbostic ifsp = &ifsfirst; 63847116Sbostic do { 63947116Sbostic p = string + ifsp->begoff; 64047116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 64147116Sbostic while (p < string + ifsp->endoff) { 64247116Sbostic q = p; 64347116Sbostic if (*p == CTLESC) 64447116Sbostic p++; 64547116Sbostic if (strchr(ifs, *p++)) { 64647116Sbostic if (q > start || *ifs != ' ') { 64747116Sbostic *q = '\0'; 64847116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 64947116Sbostic sp->text = start; 65047116Sbostic *arglist->lastp = sp; 65147116Sbostic arglist->lastp = &sp->next; 65247116Sbostic } 65347116Sbostic if (*ifs == ' ') { 65447116Sbostic for (;;) { 65547116Sbostic if (p >= string + ifsp->endoff) 65647116Sbostic break; 65747116Sbostic q = p; 65847116Sbostic if (*p == CTLESC) 65947116Sbostic p++; 66047116Sbostic if (strchr(ifs, *p++) == NULL) { 66147116Sbostic p = q; 66247116Sbostic break; 66347116Sbostic } 66447116Sbostic } 66547116Sbostic } 66647116Sbostic start = p; 66747116Sbostic } 66847116Sbostic } 66947116Sbostic } while ((ifsp = ifsp->next) != NULL); 67047116Sbostic if (*start || (*ifs != ' ' && start > string)) { 67147116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 67247116Sbostic sp->text = start; 67347116Sbostic *arglist->lastp = sp; 67447116Sbostic arglist->lastp = &sp->next; 67547116Sbostic } 67647116Sbostic } else { 67747116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 67847116Sbostic sp->text = start; 67947116Sbostic *arglist->lastp = sp; 68047116Sbostic arglist->lastp = &sp->next; 68147116Sbostic } 68247116Sbostic } 68347116Sbostic 68447116Sbostic 68547116Sbostic 68647116Sbostic /* 68747116Sbostic * Expand shell metacharacters. At this point, the only control characters 68847116Sbostic * should be escapes. The results are stored in the list exparg. 68947116Sbostic */ 69047116Sbostic 69147116Sbostic char *expdir; 69247116Sbostic 69347116Sbostic 69447116Sbostic STATIC void 695*53299Smarc expandmeta(str, flag) 69647116Sbostic struct strlist *str; 69747116Sbostic { 69847116Sbostic char *p; 69947116Sbostic struct strlist **savelastp; 70047116Sbostic struct strlist *sp; 70147116Sbostic char c; 702*53299Smarc /* TODO - EXP_REDIR */ 70347116Sbostic 70447116Sbostic while (str) { 70547116Sbostic if (fflag) 70647116Sbostic goto nometa; 70747116Sbostic p = str->text; 70847116Sbostic for (;;) { /* fast check for meta chars */ 70947116Sbostic if ((c = *p++) == '\0') 71047116Sbostic goto nometa; 71147116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 71247116Sbostic break; 71347116Sbostic } 71447116Sbostic savelastp = exparg.lastp; 71547116Sbostic INTOFF; 71647116Sbostic if (expdir == NULL) 71747116Sbostic expdir = ckmalloc(1024); /* I hope this is big enough */ 71847116Sbostic expmeta(expdir, str->text); 71947116Sbostic ckfree(expdir); 72047116Sbostic expdir = NULL; 72147116Sbostic INTON; 72247116Sbostic if (exparg.lastp == savelastp) { 723*53299Smarc /* 724*53299Smarc * no matches 725*53299Smarc */ 72647116Sbostic if (! zflag) { 72747116Sbostic nometa: 72847116Sbostic *exparg.lastp = str; 72947116Sbostic rmescapes(str->text); 73047116Sbostic exparg.lastp = &str->next; 73147116Sbostic } 73247116Sbostic } else { 73347116Sbostic *exparg.lastp = NULL; 73447116Sbostic *savelastp = sp = expsort(*savelastp); 73547116Sbostic while (sp->next != NULL) 73647116Sbostic sp = sp->next; 73747116Sbostic exparg.lastp = &sp->next; 73847116Sbostic } 73947116Sbostic str = str->next; 74047116Sbostic } 74147116Sbostic } 74247116Sbostic 74347116Sbostic 74447116Sbostic /* 74547116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 74647116Sbostic */ 74747116Sbostic 74847116Sbostic STATIC void 74947116Sbostic expmeta(enddir, name) 75047116Sbostic char *enddir; 75147116Sbostic char *name; 75247116Sbostic { 75347116Sbostic register char *p; 75447116Sbostic char *q; 75547116Sbostic char *start; 75647116Sbostic char *endname; 75747116Sbostic int metaflag; 75847116Sbostic struct stat statb; 75947116Sbostic DIR *dirp; 76047116Sbostic struct dirent *dp; 76147116Sbostic int atend; 76247116Sbostic int matchdot; 76347116Sbostic 76447116Sbostic metaflag = 0; 76547116Sbostic start = name; 76647116Sbostic for (p = name ; ; p++) { 76747116Sbostic if (*p == '*' || *p == '?') 76847116Sbostic metaflag = 1; 76947116Sbostic else if (*p == '[') { 77047116Sbostic q = p + 1; 77147116Sbostic if (*q == '!') 77247116Sbostic q++; 77347116Sbostic for (;;) { 77447116Sbostic if (*q == CTLESC) 77547116Sbostic q++; 77647116Sbostic if (*q == '/' || *q == '\0') 77747116Sbostic break; 77847116Sbostic if (*++q == ']') { 77947116Sbostic metaflag = 1; 78047116Sbostic break; 78147116Sbostic } 78247116Sbostic } 78347116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 78447116Sbostic metaflag = 1; 78547116Sbostic } else if (*p == '\0') 78647116Sbostic break; 78747116Sbostic else if (*p == CTLESC) 78847116Sbostic p++; 78947116Sbostic if (*p == '/') { 79047116Sbostic if (metaflag) 79147116Sbostic break; 79247116Sbostic start = p + 1; 79347116Sbostic } 79447116Sbostic } 79547116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 79647116Sbostic if (enddir != expdir) 79747116Sbostic metaflag++; 79847116Sbostic for (p = name ; ; p++) { 79947116Sbostic if (*p == CTLESC) 80047116Sbostic p++; 80147116Sbostic *enddir++ = *p; 80247116Sbostic if (*p == '\0') 80347116Sbostic break; 80447116Sbostic } 80547116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 80647116Sbostic addfname(expdir); 80747116Sbostic return; 80847116Sbostic } 80947116Sbostic endname = p; 81047116Sbostic if (start != name) { 81147116Sbostic p = name; 81247116Sbostic while (p < start) { 81347116Sbostic if (*p == CTLESC) 81447116Sbostic p++; 81547116Sbostic *enddir++ = *p++; 81647116Sbostic } 81747116Sbostic } 81847116Sbostic if (enddir == expdir) { 81947116Sbostic p = "."; 82047116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 82147116Sbostic p = "/"; 82247116Sbostic } else { 82347116Sbostic p = expdir; 82447116Sbostic enddir[-1] = '\0'; 82547116Sbostic } 82647116Sbostic if ((dirp = opendir(p)) == NULL) 82747116Sbostic return; 82847116Sbostic if (enddir != expdir) 82947116Sbostic enddir[-1] = '/'; 83047116Sbostic if (*endname == 0) { 83147116Sbostic atend = 1; 83247116Sbostic } else { 83347116Sbostic atend = 0; 83447116Sbostic *endname++ = '\0'; 83547116Sbostic } 83647116Sbostic matchdot = 0; 83747116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 83847116Sbostic matchdot++; 83947116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 84047116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 84147116Sbostic continue; 84247116Sbostic if (patmatch(start, dp->d_name)) { 84347116Sbostic if (atend) { 84447116Sbostic scopy(dp->d_name, enddir); 84547116Sbostic addfname(expdir); 84647116Sbostic } else { 84747116Sbostic char *q; 84847116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 84947116Sbostic p[-1] = '/'; 85047116Sbostic expmeta(p, endname); 85147116Sbostic } 85247116Sbostic } 85347116Sbostic } 85447116Sbostic closedir(dirp); 85547116Sbostic if (! atend) 85647116Sbostic endname[-1] = '/'; 85747116Sbostic } 85847116Sbostic 85947116Sbostic 86047116Sbostic /* 86147116Sbostic * Add a file name to the list. 86247116Sbostic */ 86347116Sbostic 86447116Sbostic STATIC void 86547116Sbostic addfname(name) 86647116Sbostic char *name; 86747116Sbostic { 86847116Sbostic char *p; 86947116Sbostic struct strlist *sp; 87047116Sbostic 87147116Sbostic p = stalloc(strlen(name) + 1); 87247116Sbostic scopy(name, p); 87347116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 87447116Sbostic sp->text = p; 87547116Sbostic *exparg.lastp = sp; 87647116Sbostic exparg.lastp = &sp->next; 87747116Sbostic } 87847116Sbostic 87947116Sbostic 88047116Sbostic /* 88147116Sbostic * Sort the results of file name expansion. It calculates the number of 88247116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 88347116Sbostic * work. 88447116Sbostic */ 88547116Sbostic 88647116Sbostic STATIC struct strlist * 88747116Sbostic expsort(str) 88847116Sbostic struct strlist *str; 88947116Sbostic { 89047116Sbostic int len; 89147116Sbostic struct strlist *sp; 89247116Sbostic 89347116Sbostic len = 0; 89447116Sbostic for (sp = str ; sp ; sp = sp->next) 89547116Sbostic len++; 89647116Sbostic return msort(str, len); 89747116Sbostic } 89847116Sbostic 89947116Sbostic 90047116Sbostic STATIC struct strlist * 90147116Sbostic msort(list, len) 90247116Sbostic struct strlist *list; 90347116Sbostic { 90447116Sbostic struct strlist *p, *q; 90547116Sbostic struct strlist **lpp; 90647116Sbostic int half; 90747116Sbostic int n; 90847116Sbostic 90947116Sbostic if (len <= 1) 91047116Sbostic return list; 91147116Sbostic half = len >> 1; 91247116Sbostic p = list; 91347116Sbostic for (n = half ; --n >= 0 ; ) { 91447116Sbostic q = p; 91547116Sbostic p = p->next; 91647116Sbostic } 91747116Sbostic q->next = NULL; /* terminate first half of list */ 91847116Sbostic q = msort(list, half); /* sort first half of list */ 91947116Sbostic p = msort(p, len - half); /* sort second half */ 92047116Sbostic lpp = &list; 92147116Sbostic for (;;) { 92247116Sbostic if (strcmp(p->text, q->text) < 0) { 92347116Sbostic *lpp = p; 92447116Sbostic lpp = &p->next; 92547116Sbostic if ((p = *lpp) == NULL) { 92647116Sbostic *lpp = q; 92747116Sbostic break; 92847116Sbostic } 92947116Sbostic } else { 93047116Sbostic *lpp = q; 93147116Sbostic lpp = &q->next; 93247116Sbostic if ((q = *lpp) == NULL) { 93347116Sbostic *lpp = p; 93447116Sbostic break; 93547116Sbostic } 93647116Sbostic } 93747116Sbostic } 93847116Sbostic return list; 93947116Sbostic } 94047116Sbostic 94147116Sbostic 94247116Sbostic 94347116Sbostic /* 94447116Sbostic * Returns true if the pattern matches the string. 94547116Sbostic */ 94647116Sbostic 94747116Sbostic int 94847116Sbostic patmatch(pattern, string) 94947116Sbostic char *pattern; 95047116Sbostic char *string; 95147116Sbostic { 95247116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 95347116Sbostic return 1 - pmatch(pattern + 2, string); 95447116Sbostic else 95547116Sbostic return pmatch(pattern, string); 95647116Sbostic } 95747116Sbostic 95847116Sbostic 95947116Sbostic STATIC int 96047116Sbostic pmatch(pattern, string) 96147116Sbostic char *pattern; 96247116Sbostic char *string; 96347116Sbostic { 96447116Sbostic register char *p, *q; 96547116Sbostic register char c; 96647116Sbostic 96747116Sbostic p = pattern; 96847116Sbostic q = string; 96947116Sbostic for (;;) { 97047116Sbostic switch (c = *p++) { 97147116Sbostic case '\0': 97247116Sbostic goto breakloop; 97347116Sbostic case CTLESC: 97447116Sbostic if (*q++ != *p++) 97547116Sbostic return 0; 97647116Sbostic break; 97747116Sbostic case '?': 97847116Sbostic if (*q++ == '\0') 97947116Sbostic return 0; 98047116Sbostic break; 98147116Sbostic case '*': 98247116Sbostic c = *p; 98347116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 98447116Sbostic while (*q != c) { 98547116Sbostic if (*q == '\0') 98647116Sbostic return 0; 98747116Sbostic q++; 98847116Sbostic } 98947116Sbostic } 99047116Sbostic do { 99147116Sbostic if (pmatch(p, q)) 99247116Sbostic return 1; 99347116Sbostic } while (*q++ != '\0'); 99447116Sbostic return 0; 99547116Sbostic case '[': { 99647116Sbostic char *endp; 99747116Sbostic int invert, found; 99847116Sbostic char chr; 99947116Sbostic 100047116Sbostic endp = p; 100147116Sbostic if (*endp == '!') 100247116Sbostic endp++; 100347116Sbostic for (;;) { 100447116Sbostic if (*endp == '\0') 100547116Sbostic goto dft; /* no matching ] */ 100647116Sbostic if (*endp == CTLESC) 100747116Sbostic endp++; 100847116Sbostic if (*++endp == ']') 100947116Sbostic break; 101047116Sbostic } 101147116Sbostic invert = 0; 101247116Sbostic if (*p == '!') { 101347116Sbostic invert++; 101447116Sbostic p++; 101547116Sbostic } 101647116Sbostic found = 0; 101747116Sbostic chr = *q++; 101847116Sbostic c = *p++; 101947116Sbostic do { 102047116Sbostic if (c == CTLESC) 102147116Sbostic c = *p++; 102247116Sbostic if (*p == '-' && p[1] != ']') { 102347116Sbostic p++; 102447116Sbostic if (*p == CTLESC) 102547116Sbostic p++; 102647116Sbostic if (chr >= c && chr <= *p) 102747116Sbostic found = 1; 102847116Sbostic p++; 102947116Sbostic } else { 103047116Sbostic if (chr == c) 103147116Sbostic found = 1; 103247116Sbostic } 103347116Sbostic } while ((c = *p++) != ']'); 103447116Sbostic if (found == invert) 103547116Sbostic return 0; 103647116Sbostic break; 103747116Sbostic } 103847116Sbostic dft: default: 103947116Sbostic if (*q++ != c) 104047116Sbostic return 0; 104147116Sbostic break; 104247116Sbostic } 104347116Sbostic } 104447116Sbostic breakloop: 104547116Sbostic if (*q != '\0') 104647116Sbostic return 0; 104747116Sbostic return 1; 104847116Sbostic } 104947116Sbostic 105047116Sbostic 105147116Sbostic 105247116Sbostic /* 105347116Sbostic * Remove any CTLESC characters from a string. 105447116Sbostic */ 105547116Sbostic 105647116Sbostic void 105747116Sbostic rmescapes(str) 105847116Sbostic char *str; 105947116Sbostic { 106047116Sbostic register char *p, *q; 106147116Sbostic 106247116Sbostic p = str; 106347116Sbostic while (*p != CTLESC) { 106447116Sbostic if (*p++ == '\0') 106547116Sbostic return; 106647116Sbostic } 106747116Sbostic q = p; 106847116Sbostic while (*p) { 106947116Sbostic if (*p == CTLESC) 107047116Sbostic p++; 107147116Sbostic *q++ = *p++; 107247116Sbostic } 107347116Sbostic *q = '\0'; 107447116Sbostic } 107547116Sbostic 107647116Sbostic 107747116Sbostic 107847116Sbostic /* 107947116Sbostic * See if a pattern matches in a case statement. 108047116Sbostic */ 108147116Sbostic 108247116Sbostic int 108347116Sbostic casematch(pattern, val) 108447116Sbostic union node *pattern; 108547116Sbostic char *val; 108647116Sbostic { 108747116Sbostic struct stackmark smark; 108847116Sbostic int result; 108947116Sbostic char *p; 109047116Sbostic 109147116Sbostic setstackmark(&smark); 109247116Sbostic argbackq = pattern->narg.backquote; 109347116Sbostic STARTSTACKSTR(expdest); 109447116Sbostic ifslastp = NULL; 109547116Sbostic argstr(pattern->narg.text, 0); 109647116Sbostic STPUTC('\0', expdest); 109747116Sbostic p = grabstackstr(expdest); 109847116Sbostic result = patmatch(p, val); 109947116Sbostic popstackmark(&smark); 110047116Sbostic return result; 110147116Sbostic } 1102