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*55787Smarc static char sccsid[] = "@(#)expand.c 5.8 (Berkeley) 07/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> 4053299Smarc #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 *); 6953299Smarc STATIC void expandmeta(struct strlist *, int); 7047116Sbostic STATIC void expmeta(char *, char *); 71*55787Smarc STATIC void expari(int); 7247116Sbostic STATIC void addfname(char *); 7347116Sbostic STATIC struct strlist *expsort(struct strlist *); 7447116Sbostic STATIC struct strlist *msort(struct strlist *, int); 7547116Sbostic STATIC int pmatch(char *, char *); 7653299Smarc STATIC char *exptilde(char *, int); 7747116Sbostic #else 7847116Sbostic STATIC void argstr(); 7947116Sbostic STATIC void expbackq(); 8047116Sbostic STATIC char *evalvar(); 8147116Sbostic STATIC int varisset(); 8247116Sbostic STATIC void varvalue(); 8347116Sbostic STATIC void recordregion(); 8447116Sbostic STATIC void ifsbreakup(); 8547116Sbostic STATIC void expandmeta(); 8647116Sbostic STATIC void expmeta(); 87*55787Smarc STATIC void expari(); 8847116Sbostic STATIC void addfname(); 8947116Sbostic STATIC struct strlist *expsort(); 9047116Sbostic STATIC struct strlist *msort(); 9147116Sbostic STATIC int pmatch(); 9253299Smarc STATIC char *exptilde(); 9347116Sbostic #endif 9447116Sbostic 9547116Sbostic /* 9647116Sbostic * Expand shell variables and backquotes inside a here document. 9747116Sbostic */ 9847116Sbostic 9947116Sbostic void 10047116Sbostic expandhere(arg, fd) 10147116Sbostic union node *arg; /* the document */ 10247116Sbostic int fd; /* where to write the expanded version */ 10347116Sbostic { 10447116Sbostic herefd = fd; 10547116Sbostic expandarg(arg, (struct arglist *)NULL, 0); 10647116Sbostic xwrite(fd, stackblock(), expdest - stackblock()); 10747116Sbostic } 10847116Sbostic 10947116Sbostic 11047116Sbostic /* 11147116Sbostic * Perform variable substitution and command substitution on an argument, 112*55787Smarc * placing the resulting list of arguments in arglist. If EXP_FULL is true, 11347116Sbostic * perform splitting and file name expansion. When arglist is NULL, perform 11447116Sbostic * here document expansion. 11547116Sbostic */ 11647116Sbostic 11747116Sbostic void 11853299Smarc expandarg(arg, arglist, flag) 11947116Sbostic union node *arg; 12047116Sbostic struct arglist *arglist; 12147116Sbostic { 12247116Sbostic struct strlist *sp; 12347116Sbostic char *p; 12447116Sbostic 12547116Sbostic argbackq = arg->narg.backquote; 12647116Sbostic STARTSTACKSTR(expdest); 12747116Sbostic ifsfirst.next = NULL; 12847116Sbostic ifslastp = NULL; 12953299Smarc argstr(arg->narg.text, flag); 13054315Smarc if (arglist == NULL) { 13147116Sbostic return; /* here document expanded */ 13254315Smarc } 13347116Sbostic STPUTC('\0', expdest); 13447116Sbostic p = grabstackstr(expdest); 13547116Sbostic exparg.lastp = &exparg.list; 13653299Smarc /* 13753299Smarc * TODO - EXP_REDIR 13853299Smarc */ 13953299Smarc if (flag & EXP_FULL) { 14047116Sbostic ifsbreakup(p, &exparg); 14147116Sbostic *exparg.lastp = NULL; 14247116Sbostic exparg.lastp = &exparg.list; 14353299Smarc expandmeta(exparg.list, flag); 14447116Sbostic } else { 14554315Smarc if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 14654315Smarc rmescapes(p); 14747116Sbostic sp = (struct strlist *)stalloc(sizeof (struct strlist)); 14847116Sbostic sp->text = p; 14947116Sbostic *exparg.lastp = sp; 15047116Sbostic exparg.lastp = &sp->next; 15147116Sbostic } 15247116Sbostic while (ifsfirst.next != NULL) { 15347116Sbostic struct ifsregion *ifsp; 15447116Sbostic INTOFF; 15547116Sbostic ifsp = ifsfirst.next->next; 15647116Sbostic ckfree(ifsfirst.next); 15747116Sbostic ifsfirst.next = ifsp; 15847116Sbostic INTON; 15947116Sbostic } 16047116Sbostic *exparg.lastp = NULL; 16147116Sbostic if (exparg.list) { 16247116Sbostic *arglist->lastp = exparg.list; 16347116Sbostic arglist->lastp = exparg.lastp; 16447116Sbostic } 16547116Sbostic } 16647116Sbostic 16747116Sbostic 16847116Sbostic 16947116Sbostic /* 170*55787Smarc * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 171*55787Smarc * characters to allow for further processing. Otherwise treat 17247116Sbostic * $@ like $* since no splitting will be performed. 17347116Sbostic */ 17447116Sbostic 17547116Sbostic STATIC void 17653299Smarc argstr(p, flag) 17747116Sbostic register char *p; 17847116Sbostic { 179*55787Smarc register char c; 180*55787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 18147116Sbostic 18253299Smarc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 183*55787Smarc p = exptilde(p, flag); 18447116Sbostic for (;;) { 18547116Sbostic switch (c = *p++) { 18647116Sbostic case '\0': 18753276Smarc case CTLENDVAR: /* ??? */ 18847116Sbostic goto breakloop; 18947116Sbostic case CTLESC: 190*55787Smarc if (quotes) 19147116Sbostic STPUTC(c, expdest); 19247116Sbostic c = *p++; 19347116Sbostic STPUTC(c, expdest); 19447116Sbostic break; 19547116Sbostic case CTLVAR: 19653299Smarc p = evalvar(p, flag); 19747116Sbostic break; 19847116Sbostic case CTLBACKQ: 19947116Sbostic case CTLBACKQ|CTLQUOTE: 200*55787Smarc expbackq(argbackq->n, c & CTLQUOTE, flag); 20147116Sbostic argbackq = argbackq->next; 20247116Sbostic break; 20353276Smarc case CTLENDARI: 204*55787Smarc expari(flag); 20553276Smarc break; 20653299Smarc case ':': 20754315Smarc STPUTC(c, expdest); 208*55787Smarc if (flag & EXP_VARTILDE && *p == '~') 209*55787Smarc p = exptilde(p, flag); 21054315Smarc break; 21147116Sbostic default: 21247116Sbostic STPUTC(c, expdest); 21347116Sbostic } 21447116Sbostic } 21547116Sbostic breakloop:; 21647116Sbostic } 21747116Sbostic 21853299Smarc STATIC char * 219*55787Smarc exptilde(p, flag) 22053299Smarc char *p; 22153299Smarc { 22253299Smarc char c, *startp = p; 22353299Smarc struct passwd *pw; 22453299Smarc char *home; 225*55787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 22653299Smarc 22753299Smarc while (c = *p) { 22853299Smarc switch(c) { 22953299Smarc case CTLESC: 23053299Smarc return (startp); 23153299Smarc case ':': 232*55787Smarc if (flag & EXP_VARTILDE) 23353299Smarc goto done; 23453299Smarc break; 23553299Smarc case '/': 23653299Smarc goto done; 23753299Smarc } 23853299Smarc p++; 23953299Smarc } 24053299Smarc done: 24153299Smarc *p = '\0'; 24253299Smarc if (*(startp+1) == '\0') { 24353299Smarc if ((home = lookupvar("HOME")) == NULL) 24453299Smarc goto lose; 24553299Smarc } else { 24653299Smarc if ((pw = getpwnam(startp+1)) == NULL) 24753299Smarc goto lose; 24853299Smarc home = pw->pw_dir; 24953299Smarc } 25053299Smarc if (*home == '\0') 25153299Smarc goto lose; 25253299Smarc *p = c; 25353299Smarc while (c = *home++) { 254*55787Smarc if (quotes && SQSYNTAX[c] == CCTL) 25553299Smarc STPUTC(CTLESC, expdest); 25653299Smarc STPUTC(c, expdest); 25753299Smarc } 25853299Smarc return (p); 25953299Smarc lose: 26053299Smarc *p = c; 26153299Smarc return (startp); 26253299Smarc } 26353299Smarc 26453299Smarc 26553276Smarc /* 26653276Smarc * Expand arithmetic expression. Backup to start of expression, 26753276Smarc * evaluate, place result in (backed up) result, adjust string position. 26853276Smarc */ 269*55787Smarc void 270*55787Smarc expari(flag) 27153299Smarc { 27255243Smarc char *p, *start; 27353276Smarc int result; 274*55787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 27547116Sbostic 27653276Smarc /* 27753276Smarc * This routine is slightly over-compilcated for 27853276Smarc * efficiency. First we make sure there is 27953276Smarc * enough space for the result, which may be bigger 28053276Smarc * than the expression if we add exponentation. Next we 28153276Smarc * scan backwards looking for the start of arithmetic. If the 28253276Smarc * next previous character is a CTLESC character, then we 28353276Smarc * have to rescan starting from the beginning since CTLESC 28453276Smarc * characters have to be processed left to right. 28553276Smarc */ 28653276Smarc CHECKSTRSPACE(8, expdest); 28755243Smarc USTPUTC('\0', expdest); 28855243Smarc start = stackblock(); 28953276Smarc p = expdest; 29053276Smarc while (*p != CTLARI && p >= start) 29153276Smarc --p; 29253276Smarc if (*p != CTLARI) 29353276Smarc error("missing CTLARI (shouldn't happen)"); 29453276Smarc if (p > start && *(p-1) == CTLESC) 29553276Smarc for (p = start; *p != CTLARI; p++) 29653276Smarc if (*p == CTLESC) 29753276Smarc p++; 298*55787Smarc if (quotes) 299*55787Smarc rmescapes(p+1); 30053276Smarc result = arith(p+1); 30153276Smarc fmtstr(p, 10, "%d", result); 30253276Smarc while (*p++) 30353276Smarc ; 30453276Smarc result = expdest - p + 1; 30553276Smarc STADJUST(-result, expdest); 30653276Smarc } 30753276Smarc 30853276Smarc 30947116Sbostic /* 31047116Sbostic * Expand stuff in backwards quotes. 31147116Sbostic */ 31247116Sbostic 31347116Sbostic STATIC void 314*55787Smarc expbackq(cmd, quoted, flag) 31547116Sbostic union node *cmd; 31647116Sbostic { 31747116Sbostic struct backcmd in; 31847116Sbostic int i; 31947116Sbostic char buf[128]; 32047116Sbostic char *p; 32147116Sbostic char *dest = expdest; 32247116Sbostic struct ifsregion saveifs, *savelastp; 32347116Sbostic struct nodelist *saveargbackq; 32447116Sbostic char lastc; 32547116Sbostic int startloc = dest - stackblock(); 32647116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 32747116Sbostic int saveherefd; 328*55787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 32947116Sbostic 33047116Sbostic INTOFF; 33147116Sbostic saveifs = ifsfirst; 33247116Sbostic savelastp = ifslastp; 33347116Sbostic saveargbackq = argbackq; 33447116Sbostic saveherefd = herefd; 33547116Sbostic herefd = -1; 33647116Sbostic p = grabstackstr(dest); 33747116Sbostic evalbackcmd(cmd, &in); 33847116Sbostic ungrabstackstr(p, dest); 33947116Sbostic ifsfirst = saveifs; 34047116Sbostic ifslastp = savelastp; 34147116Sbostic argbackq = saveargbackq; 34247116Sbostic herefd = saveherefd; 34347116Sbostic 34447116Sbostic p = in.buf; 34547116Sbostic lastc = '\0'; 34647116Sbostic for (;;) { 34747116Sbostic if (--in.nleft < 0) { 34847116Sbostic if (in.fd < 0) 34947116Sbostic break; 35047116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 35147116Sbostic TRACE(("expbackq: read returns %d\n", i)); 35247116Sbostic if (i <= 0) 35347116Sbostic break; 35447116Sbostic p = buf; 35547116Sbostic in.nleft = i - 1; 35647116Sbostic } 35747116Sbostic lastc = *p++; 35847116Sbostic if (lastc != '\0') { 359*55787Smarc if (quotes && syntax[lastc] == CCTL) 36047116Sbostic STPUTC(CTLESC, dest); 36147116Sbostic STPUTC(lastc, dest); 36247116Sbostic } 36347116Sbostic } 36447116Sbostic if (lastc == '\n') { 36547116Sbostic STUNPUTC(dest); 36647116Sbostic } 36747116Sbostic if (in.fd >= 0) 36847116Sbostic close(in.fd); 36947116Sbostic if (in.buf) 37047116Sbostic ckfree(in.buf); 37147116Sbostic if (in.jp) 37247116Sbostic waitforjob(in.jp); 37347116Sbostic if (quoted == 0) 37447116Sbostic recordregion(startloc, dest - stackblock(), 0); 37547116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 37647116Sbostic (dest - stackblock()) - startloc, 37747116Sbostic (dest - stackblock()) - startloc, 37847116Sbostic stackblock() + startloc)); 37947116Sbostic expdest = dest; 38047116Sbostic INTON; 38147116Sbostic } 38247116Sbostic 38347116Sbostic 38447116Sbostic 38547116Sbostic /* 38647116Sbostic * Expand a variable, and return a pointer to the next character in the 38747116Sbostic * input string. 38847116Sbostic */ 38947116Sbostic 39047116Sbostic STATIC char * 39153299Smarc evalvar(p, flag) 39247116Sbostic char *p; 39347116Sbostic { 39447116Sbostic int subtype; 39553299Smarc int varflags; 39647116Sbostic char *var; 39747116Sbostic char *val; 39847116Sbostic int c; 39947116Sbostic int set; 40047116Sbostic int special; 40147116Sbostic int startloc; 402*55787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 40347116Sbostic 40453299Smarc varflags = *p++; 40553299Smarc subtype = varflags & VSTYPE; 40647116Sbostic var = p; 40747116Sbostic special = 0; 40847116Sbostic if (! is_name(*p)) 40947116Sbostic special = 1; 41047116Sbostic p = strchr(p, '=') + 1; 41147116Sbostic again: /* jump here after setting a variable with ${var=text} */ 41247116Sbostic if (special) { 41347116Sbostic set = varisset(*var); 41447116Sbostic val = NULL; 41547116Sbostic } else { 41647116Sbostic val = lookupvar(var); 41753299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 41847116Sbostic val = NULL; 41947116Sbostic set = 0; 42047116Sbostic } else 42147116Sbostic set = 1; 42247116Sbostic } 42347116Sbostic startloc = expdest - stackblock(); 42447116Sbostic if (set && subtype != VSPLUS) { 42547116Sbostic /* insert the value of the variable */ 42647116Sbostic if (special) { 42753299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 42847116Sbostic } else { 42953299Smarc char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 43047116Sbostic 43147116Sbostic while (*val) { 432*55787Smarc if (quotes && syntax[*val] == CCTL) 43347116Sbostic STPUTC(CTLESC, expdest); 43447116Sbostic STPUTC(*val++, expdest); 43547116Sbostic } 43647116Sbostic } 43747116Sbostic } 43847116Sbostic if (subtype == VSPLUS) 43947116Sbostic set = ! set; 44053299Smarc if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 44147116Sbostic && (set || subtype == VSNORMAL)) 44253299Smarc recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 44347116Sbostic if (! set && subtype != VSNORMAL) { 44447116Sbostic if (subtype == VSPLUS || subtype == VSMINUS) { 44553299Smarc argstr(p, flag); 44647116Sbostic } else { 44747116Sbostic char *startp; 44847116Sbostic int saveherefd = herefd; 44947116Sbostic herefd = -1; 45047116Sbostic argstr(p, 0); 45147116Sbostic STACKSTRNUL(expdest); 45247116Sbostic herefd = saveherefd; 45347116Sbostic startp = stackblock() + startloc; 45447116Sbostic if (subtype == VSASSIGN) { 45547116Sbostic setvar(var, startp, 0); 45647116Sbostic STADJUST(startp - expdest, expdest); 45753299Smarc varflags &=~ VSNUL; 45847116Sbostic goto again; 45947116Sbostic } 46047116Sbostic /* subtype == VSQUESTION */ 46147116Sbostic if (*p != CTLENDVAR) { 46247116Sbostic outfmt(&errout, "%s\n", startp); 46347116Sbostic error((char *)NULL); 46447116Sbostic } 46547116Sbostic error("%.*s: parameter %snot set", p - var - 1, 46653299Smarc var, (varflags & VSNUL)? "null or " : nullstr); 46747116Sbostic } 46847116Sbostic } 46947116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 47047116Sbostic int nesting = 1; 47147116Sbostic for (;;) { 47247116Sbostic if ((c = *p++) == CTLESC) 47347116Sbostic p++; 47447116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 47547116Sbostic if (set) 47647116Sbostic argbackq = argbackq->next; 47747116Sbostic } else if (c == CTLVAR) { 47847116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 47947116Sbostic nesting++; 48047116Sbostic } else if (c == CTLENDVAR) { 48147116Sbostic if (--nesting == 0) 48247116Sbostic break; 48347116Sbostic } 48447116Sbostic } 48547116Sbostic } 48647116Sbostic return p; 48747116Sbostic } 48847116Sbostic 48947116Sbostic 49047116Sbostic 49147116Sbostic /* 49247116Sbostic * Test whether a specialized variable is set. 49347116Sbostic */ 49447116Sbostic 49547116Sbostic STATIC int 49647116Sbostic varisset(name) 49747116Sbostic char name; 49847116Sbostic { 49947116Sbostic char **ap; 50047116Sbostic 50147116Sbostic if (name == '!') { 50247116Sbostic if (backgndpid == -1) 50347116Sbostic return 0; 50447116Sbostic } else if (name == '@' || name == '*') { 50547116Sbostic if (*shellparam.p == NULL) 50647116Sbostic return 0; 50747116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 50847116Sbostic ap = shellparam.p; 50947116Sbostic do { 51047116Sbostic if (*ap++ == NULL) 51147116Sbostic return 0; 51247116Sbostic } while (--name >= 0); 51347116Sbostic } 51447116Sbostic return 1; 51547116Sbostic } 51647116Sbostic 51747116Sbostic 51847116Sbostic 51947116Sbostic /* 52047116Sbostic * Add the value of a specialized variable to the stack string. 52147116Sbostic */ 52247116Sbostic 52347116Sbostic STATIC void 52447116Sbostic varvalue(name, quoted, allow_split) 52547116Sbostic char name; 52647116Sbostic { 52747116Sbostic int num; 52847116Sbostic char temp[32]; 52947116Sbostic char *p; 53047116Sbostic int i; 53147116Sbostic extern int exitstatus; 53247116Sbostic char sep; 53347116Sbostic char **ap; 53447116Sbostic char const *syntax; 53547116Sbostic 53654315Smarc #define STRTODEST(p) \ 53754315Smarc do {\ 53854315Smarc if (allow_split) { \ 53954315Smarc syntax = quoted? DQSYNTAX : BASESYNTAX; \ 54054315Smarc while (*p) { \ 54154315Smarc if (syntax[*p] == CCTL) \ 54254315Smarc STPUTC(CTLESC, expdest); \ 54354315Smarc STPUTC(*p++, expdest); \ 54454315Smarc } \ 54554315Smarc } else \ 54654315Smarc while (*p) \ 54754315Smarc STPUTC(*p++, expdest); \ 54854315Smarc } while (0) 54954315Smarc 55054315Smarc 55147116Sbostic switch (name) { 55247116Sbostic case '$': 55347116Sbostic num = rootpid; 55447116Sbostic goto numvar; 55547116Sbostic case '?': 55647116Sbostic num = exitstatus; 55747116Sbostic goto numvar; 55847116Sbostic case '#': 55947116Sbostic num = shellparam.nparam; 56047116Sbostic goto numvar; 56147116Sbostic case '!': 56247116Sbostic num = backgndpid; 56347116Sbostic numvar: 56447116Sbostic p = temp + 31; 56547116Sbostic temp[31] = '\0'; 56647116Sbostic do { 56747116Sbostic *--p = num % 10 + '0'; 56847116Sbostic } while ((num /= 10) != 0); 56947116Sbostic while (*p) 57047116Sbostic STPUTC(*p++, expdest); 57147116Sbostic break; 57247116Sbostic case '-': 57355243Smarc for (i = 0 ; i < NOPTS ; i++) { 57455243Smarc if (optlist[i].val) 57555243Smarc STPUTC(optlist[i].letter, expdest); 57647116Sbostic } 57747116Sbostic break; 57847116Sbostic case '@': 57947116Sbostic if (allow_split) { 58047116Sbostic sep = '\0'; 58147116Sbostic goto allargs; 58247116Sbostic } 58347116Sbostic /* fall through */ 58447116Sbostic case '*': 58547116Sbostic sep = ' '; 58647116Sbostic allargs: 58747116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 58854315Smarc STRTODEST(p); 58947116Sbostic if (*ap) 59047116Sbostic STPUTC(sep, expdest); 59147116Sbostic } 59247116Sbostic break; 59347116Sbostic case '0': 59447116Sbostic p = arg0; 59554315Smarc STRTODEST(p); 59647116Sbostic break; 59747116Sbostic default: 59847116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 59947116Sbostic p = shellparam.p[name]; 60054315Smarc STRTODEST(p); 60147116Sbostic } 60247116Sbostic break; 60347116Sbostic } 60447116Sbostic } 60547116Sbostic 60647116Sbostic 60747116Sbostic 60847116Sbostic /* 60947116Sbostic * Record the the fact that we have to scan this region of the 61047116Sbostic * string for IFS characters. 61147116Sbostic */ 61247116Sbostic 61347116Sbostic STATIC void 61447116Sbostic recordregion(start, end, nulonly) { 61547116Sbostic register struct ifsregion *ifsp; 61647116Sbostic 61747116Sbostic if (ifslastp == NULL) { 61847116Sbostic ifsp = &ifsfirst; 61947116Sbostic } else { 62047116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 62147116Sbostic ifslastp->next = ifsp; 62247116Sbostic } 62347116Sbostic ifslastp = ifsp; 62447116Sbostic ifslastp->next = NULL; 62547116Sbostic ifslastp->begoff = start; 62647116Sbostic ifslastp->endoff = end; 62747116Sbostic ifslastp->nulonly = nulonly; 62847116Sbostic } 62947116Sbostic 63047116Sbostic 63147116Sbostic 63247116Sbostic /* 63347116Sbostic * Break the argument string into pieces based upon IFS and add the 63447116Sbostic * strings to the argument list. The regions of the string to be 63547116Sbostic * searched for IFS characters have been stored by recordregion. 63647116Sbostic */ 63747116Sbostic 63847116Sbostic STATIC void 63947116Sbostic ifsbreakup(string, arglist) 64047116Sbostic char *string; 64147116Sbostic struct arglist *arglist; 64247116Sbostic { 64347116Sbostic struct ifsregion *ifsp; 64447116Sbostic struct strlist *sp; 64547116Sbostic char *start; 64647116Sbostic register char *p; 64747116Sbostic char *q; 64847116Sbostic char *ifs; 64947116Sbostic 65047116Sbostic start = string; 65147116Sbostic if (ifslastp != NULL) { 65247116Sbostic ifsp = &ifsfirst; 65347116Sbostic do { 65447116Sbostic p = string + ifsp->begoff; 65547116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 65647116Sbostic while (p < string + ifsp->endoff) { 65747116Sbostic q = p; 65847116Sbostic if (*p == CTLESC) 65947116Sbostic p++; 66047116Sbostic if (strchr(ifs, *p++)) { 66147116Sbostic if (q > start || *ifs != ' ') { 66247116Sbostic *q = '\0'; 66347116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 66447116Sbostic sp->text = start; 66547116Sbostic *arglist->lastp = sp; 66647116Sbostic arglist->lastp = &sp->next; 66747116Sbostic } 66847116Sbostic if (*ifs == ' ') { 66947116Sbostic for (;;) { 67047116Sbostic if (p >= string + ifsp->endoff) 67147116Sbostic break; 67247116Sbostic q = p; 67347116Sbostic if (*p == CTLESC) 67447116Sbostic p++; 67547116Sbostic if (strchr(ifs, *p++) == NULL) { 67647116Sbostic p = q; 67747116Sbostic break; 67847116Sbostic } 67947116Sbostic } 68047116Sbostic } 68147116Sbostic start = p; 68247116Sbostic } 68347116Sbostic } 68447116Sbostic } while ((ifsp = ifsp->next) != NULL); 68547116Sbostic if (*start || (*ifs != ' ' && start > string)) { 68647116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 68747116Sbostic sp->text = start; 68847116Sbostic *arglist->lastp = sp; 68947116Sbostic arglist->lastp = &sp->next; 69047116Sbostic } 69147116Sbostic } else { 69247116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 69347116Sbostic sp->text = start; 69447116Sbostic *arglist->lastp = sp; 69547116Sbostic arglist->lastp = &sp->next; 69647116Sbostic } 69747116Sbostic } 69847116Sbostic 69947116Sbostic 70047116Sbostic 70147116Sbostic /* 70247116Sbostic * Expand shell metacharacters. At this point, the only control characters 70347116Sbostic * should be escapes. The results are stored in the list exparg. 70447116Sbostic */ 70547116Sbostic 70647116Sbostic char *expdir; 70747116Sbostic 70847116Sbostic 70947116Sbostic STATIC void 71053299Smarc expandmeta(str, flag) 71147116Sbostic struct strlist *str; 71247116Sbostic { 71347116Sbostic char *p; 71447116Sbostic struct strlist **savelastp; 71547116Sbostic struct strlist *sp; 71647116Sbostic char c; 71753299Smarc /* TODO - EXP_REDIR */ 71847116Sbostic 71947116Sbostic while (str) { 72047116Sbostic if (fflag) 72147116Sbostic goto nometa; 72247116Sbostic p = str->text; 72347116Sbostic for (;;) { /* fast check for meta chars */ 72447116Sbostic if ((c = *p++) == '\0') 72547116Sbostic goto nometa; 72647116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 72747116Sbostic break; 72847116Sbostic } 72947116Sbostic savelastp = exparg.lastp; 73047116Sbostic INTOFF; 73155272Smarc if (expdir == NULL) { 73255272Smarc int i = strlen(str->text); 73355272Smarc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 73455272Smarc } 73555272Smarc 73647116Sbostic expmeta(expdir, str->text); 73747116Sbostic ckfree(expdir); 73847116Sbostic expdir = NULL; 73947116Sbostic INTON; 74047116Sbostic if (exparg.lastp == savelastp) { 74153299Smarc /* 74253299Smarc * no matches 74353299Smarc */ 74447116Sbostic nometa: 74555243Smarc *exparg.lastp = str; 74655243Smarc rmescapes(str->text); 74755243Smarc exparg.lastp = &str->next; 74847116Sbostic } else { 74947116Sbostic *exparg.lastp = NULL; 75047116Sbostic *savelastp = sp = expsort(*savelastp); 75147116Sbostic while (sp->next != NULL) 75247116Sbostic sp = sp->next; 75347116Sbostic exparg.lastp = &sp->next; 75447116Sbostic } 75547116Sbostic str = str->next; 75647116Sbostic } 75747116Sbostic } 75847116Sbostic 75947116Sbostic 76047116Sbostic /* 76147116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 76247116Sbostic */ 76347116Sbostic 76447116Sbostic STATIC void 76547116Sbostic expmeta(enddir, name) 76647116Sbostic char *enddir; 76747116Sbostic char *name; 76847116Sbostic { 76947116Sbostic register char *p; 77047116Sbostic char *q; 77147116Sbostic char *start; 77247116Sbostic char *endname; 77347116Sbostic int metaflag; 77447116Sbostic struct stat statb; 77547116Sbostic DIR *dirp; 77647116Sbostic struct dirent *dp; 77747116Sbostic int atend; 77847116Sbostic int matchdot; 77947116Sbostic 78047116Sbostic metaflag = 0; 78147116Sbostic start = name; 78247116Sbostic for (p = name ; ; p++) { 78347116Sbostic if (*p == '*' || *p == '?') 78447116Sbostic metaflag = 1; 78547116Sbostic else if (*p == '[') { 78647116Sbostic q = p + 1; 78747116Sbostic if (*q == '!') 78847116Sbostic q++; 78947116Sbostic for (;;) { 79047116Sbostic if (*q == CTLESC) 79147116Sbostic q++; 79247116Sbostic if (*q == '/' || *q == '\0') 79347116Sbostic break; 79447116Sbostic if (*++q == ']') { 79547116Sbostic metaflag = 1; 79647116Sbostic break; 79747116Sbostic } 79847116Sbostic } 79947116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 80047116Sbostic metaflag = 1; 80147116Sbostic } else if (*p == '\0') 80247116Sbostic break; 80347116Sbostic else if (*p == CTLESC) 80447116Sbostic p++; 80547116Sbostic if (*p == '/') { 80647116Sbostic if (metaflag) 80747116Sbostic break; 80847116Sbostic start = p + 1; 80947116Sbostic } 81047116Sbostic } 81147116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 81247116Sbostic if (enddir != expdir) 81347116Sbostic metaflag++; 81447116Sbostic for (p = name ; ; p++) { 81547116Sbostic if (*p == CTLESC) 81647116Sbostic p++; 81747116Sbostic *enddir++ = *p; 81847116Sbostic if (*p == '\0') 81947116Sbostic break; 82047116Sbostic } 82147116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 82247116Sbostic addfname(expdir); 82347116Sbostic return; 82447116Sbostic } 82547116Sbostic endname = p; 82647116Sbostic if (start != name) { 82747116Sbostic p = name; 82847116Sbostic while (p < start) { 82947116Sbostic if (*p == CTLESC) 83047116Sbostic p++; 83147116Sbostic *enddir++ = *p++; 83247116Sbostic } 83347116Sbostic } 83447116Sbostic if (enddir == expdir) { 83547116Sbostic p = "."; 83647116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 83747116Sbostic p = "/"; 83847116Sbostic } else { 83947116Sbostic p = expdir; 84047116Sbostic enddir[-1] = '\0'; 84147116Sbostic } 84247116Sbostic if ((dirp = opendir(p)) == NULL) 84347116Sbostic return; 84447116Sbostic if (enddir != expdir) 84547116Sbostic enddir[-1] = '/'; 84647116Sbostic if (*endname == 0) { 84747116Sbostic atend = 1; 84847116Sbostic } else { 84947116Sbostic atend = 0; 85047116Sbostic *endname++ = '\0'; 85147116Sbostic } 85247116Sbostic matchdot = 0; 85347116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 85447116Sbostic matchdot++; 85547116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 85647116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 85747116Sbostic continue; 85847116Sbostic if (patmatch(start, dp->d_name)) { 85947116Sbostic if (atend) { 86047116Sbostic scopy(dp->d_name, enddir); 86147116Sbostic addfname(expdir); 86247116Sbostic } else { 86347116Sbostic char *q; 86447116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 86547116Sbostic p[-1] = '/'; 86647116Sbostic expmeta(p, endname); 86747116Sbostic } 86847116Sbostic } 86947116Sbostic } 87047116Sbostic closedir(dirp); 87147116Sbostic if (! atend) 87247116Sbostic endname[-1] = '/'; 87347116Sbostic } 87447116Sbostic 87547116Sbostic 87647116Sbostic /* 87747116Sbostic * Add a file name to the list. 87847116Sbostic */ 87947116Sbostic 88047116Sbostic STATIC void 88147116Sbostic addfname(name) 88247116Sbostic char *name; 88347116Sbostic { 88447116Sbostic char *p; 88547116Sbostic struct strlist *sp; 88647116Sbostic 88747116Sbostic p = stalloc(strlen(name) + 1); 88847116Sbostic scopy(name, p); 88947116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 89047116Sbostic sp->text = p; 89147116Sbostic *exparg.lastp = sp; 89247116Sbostic exparg.lastp = &sp->next; 89347116Sbostic } 89447116Sbostic 89547116Sbostic 89647116Sbostic /* 89747116Sbostic * Sort the results of file name expansion. It calculates the number of 89847116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 89947116Sbostic * work. 90047116Sbostic */ 90147116Sbostic 90247116Sbostic STATIC struct strlist * 90347116Sbostic expsort(str) 90447116Sbostic struct strlist *str; 90547116Sbostic { 90647116Sbostic int len; 90747116Sbostic struct strlist *sp; 90847116Sbostic 90947116Sbostic len = 0; 91047116Sbostic for (sp = str ; sp ; sp = sp->next) 91147116Sbostic len++; 91247116Sbostic return msort(str, len); 91347116Sbostic } 91447116Sbostic 91547116Sbostic 91647116Sbostic STATIC struct strlist * 91747116Sbostic msort(list, len) 91847116Sbostic struct strlist *list; 91947116Sbostic { 92047116Sbostic struct strlist *p, *q; 92147116Sbostic struct strlist **lpp; 92247116Sbostic int half; 92347116Sbostic int n; 92447116Sbostic 92547116Sbostic if (len <= 1) 92647116Sbostic return list; 92747116Sbostic half = len >> 1; 92847116Sbostic p = list; 92947116Sbostic for (n = half ; --n >= 0 ; ) { 93047116Sbostic q = p; 93147116Sbostic p = p->next; 93247116Sbostic } 93347116Sbostic q->next = NULL; /* terminate first half of list */ 93447116Sbostic q = msort(list, half); /* sort first half of list */ 93547116Sbostic p = msort(p, len - half); /* sort second half */ 93647116Sbostic lpp = &list; 93747116Sbostic for (;;) { 93847116Sbostic if (strcmp(p->text, q->text) < 0) { 93947116Sbostic *lpp = p; 94047116Sbostic lpp = &p->next; 94147116Sbostic if ((p = *lpp) == NULL) { 94247116Sbostic *lpp = q; 94347116Sbostic break; 94447116Sbostic } 94547116Sbostic } else { 94647116Sbostic *lpp = q; 94747116Sbostic lpp = &q->next; 94847116Sbostic if ((q = *lpp) == NULL) { 94947116Sbostic *lpp = p; 95047116Sbostic break; 95147116Sbostic } 95247116Sbostic } 95347116Sbostic } 95447116Sbostic return list; 95547116Sbostic } 95647116Sbostic 95747116Sbostic 95847116Sbostic 95947116Sbostic /* 96047116Sbostic * Returns true if the pattern matches the string. 96147116Sbostic */ 96247116Sbostic 96347116Sbostic int 96447116Sbostic patmatch(pattern, string) 96547116Sbostic char *pattern; 96647116Sbostic char *string; 96747116Sbostic { 968*55787Smarc #ifdef notdef 96947116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 97047116Sbostic return 1 - pmatch(pattern + 2, string); 97147116Sbostic else 972*55787Smarc #endif 97347116Sbostic return pmatch(pattern, string); 97447116Sbostic } 97547116Sbostic 97647116Sbostic 97747116Sbostic STATIC int 97847116Sbostic pmatch(pattern, string) 97947116Sbostic char *pattern; 98047116Sbostic char *string; 98147116Sbostic { 98247116Sbostic register char *p, *q; 98347116Sbostic register char c; 98447116Sbostic 98547116Sbostic p = pattern; 98647116Sbostic q = string; 98747116Sbostic for (;;) { 98847116Sbostic switch (c = *p++) { 98947116Sbostic case '\0': 99047116Sbostic goto breakloop; 99147116Sbostic case CTLESC: 99247116Sbostic if (*q++ != *p++) 99347116Sbostic return 0; 99447116Sbostic break; 99547116Sbostic case '?': 99647116Sbostic if (*q++ == '\0') 99747116Sbostic return 0; 99847116Sbostic break; 99947116Sbostic case '*': 100047116Sbostic c = *p; 100147116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 100247116Sbostic while (*q != c) { 100347116Sbostic if (*q == '\0') 100447116Sbostic return 0; 100547116Sbostic q++; 100647116Sbostic } 100747116Sbostic } 100847116Sbostic do { 100947116Sbostic if (pmatch(p, q)) 101047116Sbostic return 1; 101147116Sbostic } while (*q++ != '\0'); 101247116Sbostic return 0; 101347116Sbostic case '[': { 101447116Sbostic char *endp; 101547116Sbostic int invert, found; 101647116Sbostic char chr; 101747116Sbostic 101847116Sbostic endp = p; 101947116Sbostic if (*endp == '!') 102047116Sbostic endp++; 102147116Sbostic for (;;) { 102247116Sbostic if (*endp == '\0') 102347116Sbostic goto dft; /* no matching ] */ 102447116Sbostic if (*endp == CTLESC) 102547116Sbostic endp++; 102647116Sbostic if (*++endp == ']') 102747116Sbostic break; 102847116Sbostic } 102947116Sbostic invert = 0; 103047116Sbostic if (*p == '!') { 103147116Sbostic invert++; 103247116Sbostic p++; 103347116Sbostic } 103447116Sbostic found = 0; 103547116Sbostic chr = *q++; 103647116Sbostic c = *p++; 103747116Sbostic do { 103847116Sbostic if (c == CTLESC) 103947116Sbostic c = *p++; 104047116Sbostic if (*p == '-' && p[1] != ']') { 104147116Sbostic p++; 104247116Sbostic if (*p == CTLESC) 104347116Sbostic p++; 104447116Sbostic if (chr >= c && chr <= *p) 104547116Sbostic found = 1; 104647116Sbostic p++; 104747116Sbostic } else { 104847116Sbostic if (chr == c) 104947116Sbostic found = 1; 105047116Sbostic } 105147116Sbostic } while ((c = *p++) != ']'); 105247116Sbostic if (found == invert) 105347116Sbostic return 0; 105447116Sbostic break; 105547116Sbostic } 1056*55787Smarc dft: default: 105747116Sbostic if (*q++ != c) 105847116Sbostic return 0; 105947116Sbostic break; 106047116Sbostic } 106147116Sbostic } 106247116Sbostic breakloop: 106347116Sbostic if (*q != '\0') 106447116Sbostic return 0; 106547116Sbostic return 1; 106647116Sbostic } 106747116Sbostic 106847116Sbostic 106947116Sbostic 107047116Sbostic /* 107147116Sbostic * Remove any CTLESC characters from a string. 107247116Sbostic */ 107347116Sbostic 107447116Sbostic void 107547116Sbostic rmescapes(str) 107647116Sbostic char *str; 107747116Sbostic { 107847116Sbostic register char *p, *q; 107947116Sbostic 108047116Sbostic p = str; 108147116Sbostic while (*p != CTLESC) { 108247116Sbostic if (*p++ == '\0') 108347116Sbostic return; 108447116Sbostic } 108547116Sbostic q = p; 108647116Sbostic while (*p) { 108747116Sbostic if (*p == CTLESC) 108847116Sbostic p++; 108947116Sbostic *q++ = *p++; 109047116Sbostic } 109147116Sbostic *q = '\0'; 109247116Sbostic } 109347116Sbostic 109447116Sbostic 109547116Sbostic 109647116Sbostic /* 109747116Sbostic * See if a pattern matches in a case statement. 109847116Sbostic */ 109947116Sbostic 110047116Sbostic int 110147116Sbostic casematch(pattern, val) 110247116Sbostic union node *pattern; 110347116Sbostic char *val; 110447116Sbostic { 110547116Sbostic struct stackmark smark; 110647116Sbostic int result; 110747116Sbostic char *p; 110847116Sbostic 110947116Sbostic setstackmark(&smark); 111047116Sbostic argbackq = pattern->narg.backquote; 111147116Sbostic STARTSTACKSTR(expdest); 111247116Sbostic ifslastp = NULL; 1113*55787Smarc argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 111447116Sbostic STPUTC('\0', expdest); 111547116Sbostic p = grabstackstr(expdest); 111647116Sbostic result = patmatch(p, val); 111747116Sbostic popstackmark(&smark); 111847116Sbostic return result; 111947116Sbostic } 1120