147116Sbostic /*- 2*60698Sbostic * Copyright (c) 1991, 1993 3*60698Sbostic * The Regents of the University of California. 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*60698Sbostic static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 05/31/93"; 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 *); 7155787Smarc 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(); 8755787Smarc 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, 11255787Smarc * 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 /* 17055787Smarc * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 17155787Smarc * 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 { 17955787Smarc register char c; 18055787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 18155799Smarc int firsteq = 1; 18247116Sbostic 18353299Smarc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 18455787Smarc p = exptilde(p, flag); 18547116Sbostic for (;;) { 18647116Sbostic switch (c = *p++) { 18747116Sbostic case '\0': 18853276Smarc case CTLENDVAR: /* ??? */ 18947116Sbostic goto breakloop; 19047116Sbostic case CTLESC: 19155787Smarc if (quotes) 19247116Sbostic STPUTC(c, expdest); 19347116Sbostic c = *p++; 19447116Sbostic STPUTC(c, expdest); 19547116Sbostic break; 19647116Sbostic case CTLVAR: 19753299Smarc p = evalvar(p, flag); 19847116Sbostic break; 19947116Sbostic case CTLBACKQ: 20047116Sbostic case CTLBACKQ|CTLQUOTE: 20155787Smarc expbackq(argbackq->n, c & CTLQUOTE, flag); 20247116Sbostic argbackq = argbackq->next; 20347116Sbostic break; 20453276Smarc case CTLENDARI: 20555787Smarc expari(flag); 20653276Smarc break; 20753299Smarc case ':': 20855799Smarc case '=': 20955799Smarc /* 21055799Smarc * sort of a hack - expand tildes in variable 21155799Smarc * assignments (after the first '=' and after ':'s). 21255799Smarc */ 21354315Smarc STPUTC(c, expdest); 21455799Smarc if (flag & EXP_VARTILDE && *p == '~') { 21555799Smarc if (c == '=') { 21655799Smarc if (firsteq) 21755799Smarc firsteq = 0; 21855799Smarc else 21955799Smarc break; 22055799Smarc } 22155787Smarc p = exptilde(p, flag); 22255799Smarc } 22354315Smarc break; 22447116Sbostic default: 22547116Sbostic STPUTC(c, expdest); 22647116Sbostic } 22747116Sbostic } 22847116Sbostic breakloop:; 22947116Sbostic } 23047116Sbostic 23153299Smarc STATIC char * 23255787Smarc exptilde(p, flag) 23353299Smarc char *p; 23453299Smarc { 23553299Smarc char c, *startp = p; 23653299Smarc struct passwd *pw; 23753299Smarc char *home; 23855787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 23953299Smarc 24053299Smarc while (c = *p) { 24153299Smarc switch(c) { 24253299Smarc case CTLESC: 24353299Smarc return (startp); 24453299Smarc case ':': 24555787Smarc if (flag & EXP_VARTILDE) 24653299Smarc goto done; 24753299Smarc break; 24853299Smarc case '/': 24953299Smarc goto done; 25053299Smarc } 25153299Smarc p++; 25253299Smarc } 25353299Smarc done: 25453299Smarc *p = '\0'; 25553299Smarc if (*(startp+1) == '\0') { 25653299Smarc if ((home = lookupvar("HOME")) == NULL) 25753299Smarc goto lose; 25853299Smarc } else { 25953299Smarc if ((pw = getpwnam(startp+1)) == NULL) 26053299Smarc goto lose; 26153299Smarc home = pw->pw_dir; 26253299Smarc } 26353299Smarc if (*home == '\0') 26453299Smarc goto lose; 26553299Smarc *p = c; 26653299Smarc while (c = *home++) { 26755787Smarc if (quotes && SQSYNTAX[c] == CCTL) 26853299Smarc STPUTC(CTLESC, expdest); 26953299Smarc STPUTC(c, expdest); 27053299Smarc } 27153299Smarc return (p); 27253299Smarc lose: 27353299Smarc *p = c; 27453299Smarc return (startp); 27553299Smarc } 27653299Smarc 27753299Smarc 27853276Smarc /* 27953276Smarc * Expand arithmetic expression. Backup to start of expression, 28053276Smarc * evaluate, place result in (backed up) result, adjust string position. 28153276Smarc */ 28255787Smarc void 28355787Smarc expari(flag) 28453299Smarc { 28555243Smarc char *p, *start; 28653276Smarc int result; 28755787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 28847116Sbostic 28953276Smarc /* 29053276Smarc * This routine is slightly over-compilcated for 29153276Smarc * efficiency. First we make sure there is 29253276Smarc * enough space for the result, which may be bigger 29353276Smarc * than the expression if we add exponentation. Next we 29453276Smarc * scan backwards looking for the start of arithmetic. If the 29553276Smarc * next previous character is a CTLESC character, then we 29653276Smarc * have to rescan starting from the beginning since CTLESC 29753276Smarc * characters have to be processed left to right. 29853276Smarc */ 29953276Smarc CHECKSTRSPACE(8, expdest); 30055243Smarc USTPUTC('\0', expdest); 30155243Smarc start = stackblock(); 30253276Smarc p = expdest; 30353276Smarc while (*p != CTLARI && p >= start) 30453276Smarc --p; 30553276Smarc if (*p != CTLARI) 30653276Smarc error("missing CTLARI (shouldn't happen)"); 30753276Smarc if (p > start && *(p-1) == CTLESC) 30853276Smarc for (p = start; *p != CTLARI; p++) 30953276Smarc if (*p == CTLESC) 31053276Smarc p++; 31155787Smarc if (quotes) 31255787Smarc rmescapes(p+1); 31353276Smarc result = arith(p+1); 31453276Smarc fmtstr(p, 10, "%d", result); 31553276Smarc while (*p++) 31653276Smarc ; 31753276Smarc result = expdest - p + 1; 31853276Smarc STADJUST(-result, expdest); 31953276Smarc } 32053276Smarc 32153276Smarc 32247116Sbostic /* 32347116Sbostic * Expand stuff in backwards quotes. 32447116Sbostic */ 32547116Sbostic 32647116Sbostic STATIC void 32755787Smarc expbackq(cmd, quoted, flag) 32847116Sbostic union node *cmd; 32947116Sbostic { 33047116Sbostic struct backcmd in; 33147116Sbostic int i; 33247116Sbostic char buf[128]; 33347116Sbostic char *p; 33447116Sbostic char *dest = expdest; 33547116Sbostic struct ifsregion saveifs, *savelastp; 33647116Sbostic struct nodelist *saveargbackq; 33747116Sbostic char lastc; 33847116Sbostic int startloc = dest - stackblock(); 33947116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 34047116Sbostic int saveherefd; 34155787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 34247116Sbostic 34347116Sbostic INTOFF; 34447116Sbostic saveifs = ifsfirst; 34547116Sbostic savelastp = ifslastp; 34647116Sbostic saveargbackq = argbackq; 34747116Sbostic saveherefd = herefd; 34847116Sbostic herefd = -1; 34947116Sbostic p = grabstackstr(dest); 35047116Sbostic evalbackcmd(cmd, &in); 35147116Sbostic ungrabstackstr(p, dest); 35247116Sbostic ifsfirst = saveifs; 35347116Sbostic ifslastp = savelastp; 35447116Sbostic argbackq = saveargbackq; 35547116Sbostic herefd = saveherefd; 35647116Sbostic 35747116Sbostic p = in.buf; 35847116Sbostic lastc = '\0'; 35947116Sbostic for (;;) { 36047116Sbostic if (--in.nleft < 0) { 36147116Sbostic if (in.fd < 0) 36247116Sbostic break; 36347116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 36447116Sbostic TRACE(("expbackq: read returns %d\n", i)); 36547116Sbostic if (i <= 0) 36647116Sbostic break; 36747116Sbostic p = buf; 36847116Sbostic in.nleft = i - 1; 36947116Sbostic } 37047116Sbostic lastc = *p++; 37147116Sbostic if (lastc != '\0') { 37255787Smarc if (quotes && syntax[lastc] == CCTL) 37347116Sbostic STPUTC(CTLESC, dest); 37447116Sbostic STPUTC(lastc, dest); 37547116Sbostic } 37647116Sbostic } 37747116Sbostic if (lastc == '\n') { 37847116Sbostic STUNPUTC(dest); 37947116Sbostic } 38047116Sbostic if (in.fd >= 0) 38147116Sbostic close(in.fd); 38247116Sbostic if (in.buf) 38347116Sbostic ckfree(in.buf); 38447116Sbostic if (in.jp) 38559176Storek exitstatus = waitforjob(in.jp); 38647116Sbostic if (quoted == 0) 38747116Sbostic recordregion(startloc, dest - stackblock(), 0); 38847116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 38947116Sbostic (dest - stackblock()) - startloc, 39047116Sbostic (dest - stackblock()) - startloc, 39147116Sbostic stackblock() + startloc)); 39247116Sbostic expdest = dest; 39347116Sbostic INTON; 39447116Sbostic } 39547116Sbostic 39647116Sbostic 39747116Sbostic 39847116Sbostic /* 39947116Sbostic * Expand a variable, and return a pointer to the next character in the 40047116Sbostic * input string. 40147116Sbostic */ 40247116Sbostic 40347116Sbostic STATIC char * 40453299Smarc evalvar(p, flag) 40547116Sbostic char *p; 40647116Sbostic { 40747116Sbostic int subtype; 40853299Smarc int varflags; 40947116Sbostic char *var; 41047116Sbostic char *val; 41147116Sbostic int c; 41247116Sbostic int set; 41347116Sbostic int special; 41447116Sbostic int startloc; 41555787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 41647116Sbostic 41753299Smarc varflags = *p++; 41853299Smarc subtype = varflags & VSTYPE; 41947116Sbostic var = p; 42047116Sbostic special = 0; 42147116Sbostic if (! is_name(*p)) 42247116Sbostic special = 1; 42347116Sbostic p = strchr(p, '=') + 1; 42447116Sbostic again: /* jump here after setting a variable with ${var=text} */ 42547116Sbostic if (special) { 42647116Sbostic set = varisset(*var); 42747116Sbostic val = NULL; 42847116Sbostic } else { 42947116Sbostic val = lookupvar(var); 43053299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 43147116Sbostic val = NULL; 43247116Sbostic set = 0; 43347116Sbostic } else 43447116Sbostic set = 1; 43547116Sbostic } 43647116Sbostic startloc = expdest - stackblock(); 43747116Sbostic if (set && subtype != VSPLUS) { 43847116Sbostic /* insert the value of the variable */ 43947116Sbostic if (special) { 44053299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 44147116Sbostic } else { 44253299Smarc char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 44347116Sbostic 44447116Sbostic while (*val) { 44555787Smarc if (quotes && syntax[*val] == CCTL) 44647116Sbostic STPUTC(CTLESC, expdest); 44747116Sbostic STPUTC(*val++, expdest); 44847116Sbostic } 44947116Sbostic } 45047116Sbostic } 45147116Sbostic if (subtype == VSPLUS) 45247116Sbostic set = ! set; 45353299Smarc if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 45447116Sbostic && (set || subtype == VSNORMAL)) 45553299Smarc recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 45647116Sbostic if (! set && subtype != VSNORMAL) { 45747116Sbostic if (subtype == VSPLUS || subtype == VSMINUS) { 45853299Smarc argstr(p, flag); 45947116Sbostic } else { 46047116Sbostic char *startp; 46147116Sbostic int saveherefd = herefd; 46247116Sbostic herefd = -1; 46347116Sbostic argstr(p, 0); 46447116Sbostic STACKSTRNUL(expdest); 46547116Sbostic herefd = saveherefd; 46647116Sbostic startp = stackblock() + startloc; 46747116Sbostic if (subtype == VSASSIGN) { 46847116Sbostic setvar(var, startp, 0); 46947116Sbostic STADJUST(startp - expdest, expdest); 47053299Smarc varflags &=~ VSNUL; 47147116Sbostic goto again; 47247116Sbostic } 47347116Sbostic /* subtype == VSQUESTION */ 47447116Sbostic if (*p != CTLENDVAR) { 47547116Sbostic outfmt(&errout, "%s\n", startp); 47647116Sbostic error((char *)NULL); 47747116Sbostic } 47847116Sbostic error("%.*s: parameter %snot set", p - var - 1, 47953299Smarc var, (varflags & VSNUL)? "null or " : nullstr); 48047116Sbostic } 48147116Sbostic } 48247116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 48347116Sbostic int nesting = 1; 48447116Sbostic for (;;) { 48547116Sbostic if ((c = *p++) == CTLESC) 48647116Sbostic p++; 48747116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 48847116Sbostic if (set) 48947116Sbostic argbackq = argbackq->next; 49047116Sbostic } else if (c == CTLVAR) { 49147116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 49247116Sbostic nesting++; 49347116Sbostic } else if (c == CTLENDVAR) { 49447116Sbostic if (--nesting == 0) 49547116Sbostic break; 49647116Sbostic } 49747116Sbostic } 49847116Sbostic } 49947116Sbostic return p; 50047116Sbostic } 50147116Sbostic 50247116Sbostic 50347116Sbostic 50447116Sbostic /* 50547116Sbostic * Test whether a specialized variable is set. 50647116Sbostic */ 50747116Sbostic 50847116Sbostic STATIC int 50947116Sbostic varisset(name) 51047116Sbostic char name; 51147116Sbostic { 51247116Sbostic char **ap; 51347116Sbostic 51447116Sbostic if (name == '!') { 51547116Sbostic if (backgndpid == -1) 51647116Sbostic return 0; 51747116Sbostic } else if (name == '@' || name == '*') { 51847116Sbostic if (*shellparam.p == NULL) 51947116Sbostic return 0; 52047116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 52147116Sbostic ap = shellparam.p; 52247116Sbostic do { 52347116Sbostic if (*ap++ == NULL) 52447116Sbostic return 0; 52547116Sbostic } while (--name >= 0); 52647116Sbostic } 52747116Sbostic return 1; 52847116Sbostic } 52947116Sbostic 53047116Sbostic 53147116Sbostic 53247116Sbostic /* 53347116Sbostic * Add the value of a specialized variable to the stack string. 53447116Sbostic */ 53547116Sbostic 53647116Sbostic STATIC void 53747116Sbostic varvalue(name, quoted, allow_split) 53847116Sbostic char name; 53947116Sbostic { 54047116Sbostic int num; 54147116Sbostic char temp[32]; 54247116Sbostic char *p; 54347116Sbostic int i; 54447116Sbostic extern int exitstatus; 54547116Sbostic char sep; 54647116Sbostic char **ap; 54747116Sbostic char const *syntax; 54847116Sbostic 54954315Smarc #define STRTODEST(p) \ 55054315Smarc do {\ 55154315Smarc if (allow_split) { \ 55254315Smarc syntax = quoted? DQSYNTAX : BASESYNTAX; \ 55354315Smarc while (*p) { \ 55454315Smarc if (syntax[*p] == CCTL) \ 55554315Smarc STPUTC(CTLESC, expdest); \ 55654315Smarc STPUTC(*p++, expdest); \ 55754315Smarc } \ 55854315Smarc } else \ 55954315Smarc while (*p) \ 56054315Smarc STPUTC(*p++, expdest); \ 56154315Smarc } while (0) 56254315Smarc 56354315Smarc 56447116Sbostic switch (name) { 56547116Sbostic case '$': 56647116Sbostic num = rootpid; 56747116Sbostic goto numvar; 56847116Sbostic case '?': 56947116Sbostic num = exitstatus; 57047116Sbostic goto numvar; 57147116Sbostic case '#': 57247116Sbostic num = shellparam.nparam; 57347116Sbostic goto numvar; 57447116Sbostic case '!': 57547116Sbostic num = backgndpid; 57647116Sbostic numvar: 57747116Sbostic p = temp + 31; 57847116Sbostic temp[31] = '\0'; 57947116Sbostic do { 58047116Sbostic *--p = num % 10 + '0'; 58147116Sbostic } while ((num /= 10) != 0); 58247116Sbostic while (*p) 58347116Sbostic STPUTC(*p++, expdest); 58447116Sbostic break; 58547116Sbostic case '-': 58655243Smarc for (i = 0 ; i < NOPTS ; i++) { 58755243Smarc if (optlist[i].val) 58855243Smarc STPUTC(optlist[i].letter, expdest); 58947116Sbostic } 59047116Sbostic break; 59147116Sbostic case '@': 59247116Sbostic if (allow_split) { 59347116Sbostic sep = '\0'; 59447116Sbostic goto allargs; 59547116Sbostic } 59647116Sbostic /* fall through */ 59747116Sbostic case '*': 59847116Sbostic sep = ' '; 59947116Sbostic allargs: 60047116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 60154315Smarc STRTODEST(p); 60247116Sbostic if (*ap) 60347116Sbostic STPUTC(sep, expdest); 60447116Sbostic } 60547116Sbostic break; 60647116Sbostic case '0': 60747116Sbostic p = arg0; 60854315Smarc STRTODEST(p); 60947116Sbostic break; 61047116Sbostic default: 61147116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 61247116Sbostic p = shellparam.p[name]; 61354315Smarc STRTODEST(p); 61447116Sbostic } 61547116Sbostic break; 61647116Sbostic } 61747116Sbostic } 61847116Sbostic 61947116Sbostic 62047116Sbostic 62147116Sbostic /* 62247116Sbostic * Record the the fact that we have to scan this region of the 62347116Sbostic * string for IFS characters. 62447116Sbostic */ 62547116Sbostic 62647116Sbostic STATIC void 62747116Sbostic recordregion(start, end, nulonly) { 62847116Sbostic register struct ifsregion *ifsp; 62947116Sbostic 63047116Sbostic if (ifslastp == NULL) { 63147116Sbostic ifsp = &ifsfirst; 63247116Sbostic } else { 63347116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 63447116Sbostic ifslastp->next = ifsp; 63547116Sbostic } 63647116Sbostic ifslastp = ifsp; 63747116Sbostic ifslastp->next = NULL; 63847116Sbostic ifslastp->begoff = start; 63947116Sbostic ifslastp->endoff = end; 64047116Sbostic ifslastp->nulonly = nulonly; 64147116Sbostic } 64247116Sbostic 64347116Sbostic 64447116Sbostic 64547116Sbostic /* 64647116Sbostic * Break the argument string into pieces based upon IFS and add the 64747116Sbostic * strings to the argument list. The regions of the string to be 64847116Sbostic * searched for IFS characters have been stored by recordregion. 64947116Sbostic */ 65047116Sbostic 65147116Sbostic STATIC void 65247116Sbostic ifsbreakup(string, arglist) 65347116Sbostic char *string; 65447116Sbostic struct arglist *arglist; 65547116Sbostic { 65647116Sbostic struct ifsregion *ifsp; 65747116Sbostic struct strlist *sp; 65847116Sbostic char *start; 65947116Sbostic register char *p; 66047116Sbostic char *q; 66147116Sbostic char *ifs; 66247116Sbostic 66347116Sbostic start = string; 66447116Sbostic if (ifslastp != NULL) { 66547116Sbostic ifsp = &ifsfirst; 66647116Sbostic do { 66747116Sbostic p = string + ifsp->begoff; 66847116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 66947116Sbostic while (p < string + ifsp->endoff) { 67047116Sbostic q = p; 67147116Sbostic if (*p == CTLESC) 67247116Sbostic p++; 67347116Sbostic if (strchr(ifs, *p++)) { 67447116Sbostic if (q > start || *ifs != ' ') { 67547116Sbostic *q = '\0'; 67647116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 67747116Sbostic sp->text = start; 67847116Sbostic *arglist->lastp = sp; 67947116Sbostic arglist->lastp = &sp->next; 68047116Sbostic } 68147116Sbostic if (*ifs == ' ') { 68247116Sbostic for (;;) { 68347116Sbostic if (p >= string + ifsp->endoff) 68447116Sbostic break; 68547116Sbostic q = p; 68647116Sbostic if (*p == CTLESC) 68747116Sbostic p++; 68847116Sbostic if (strchr(ifs, *p++) == NULL) { 68947116Sbostic p = q; 69047116Sbostic break; 69147116Sbostic } 69247116Sbostic } 69347116Sbostic } 69447116Sbostic start = p; 69547116Sbostic } 69647116Sbostic } 69747116Sbostic } while ((ifsp = ifsp->next) != NULL); 69847116Sbostic if (*start || (*ifs != ' ' && start > string)) { 69947116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 70047116Sbostic sp->text = start; 70147116Sbostic *arglist->lastp = sp; 70247116Sbostic arglist->lastp = &sp->next; 70347116Sbostic } 70447116Sbostic } else { 70547116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 70647116Sbostic sp->text = start; 70747116Sbostic *arglist->lastp = sp; 70847116Sbostic arglist->lastp = &sp->next; 70947116Sbostic } 71047116Sbostic } 71147116Sbostic 71247116Sbostic 71347116Sbostic 71447116Sbostic /* 71547116Sbostic * Expand shell metacharacters. At this point, the only control characters 71647116Sbostic * should be escapes. The results are stored in the list exparg. 71747116Sbostic */ 71847116Sbostic 71947116Sbostic char *expdir; 72047116Sbostic 72147116Sbostic 72247116Sbostic STATIC void 72353299Smarc expandmeta(str, flag) 72447116Sbostic struct strlist *str; 72547116Sbostic { 72647116Sbostic char *p; 72747116Sbostic struct strlist **savelastp; 72847116Sbostic struct strlist *sp; 72947116Sbostic char c; 73053299Smarc /* TODO - EXP_REDIR */ 73147116Sbostic 73247116Sbostic while (str) { 73347116Sbostic if (fflag) 73447116Sbostic goto nometa; 73547116Sbostic p = str->text; 73647116Sbostic for (;;) { /* fast check for meta chars */ 73747116Sbostic if ((c = *p++) == '\0') 73847116Sbostic goto nometa; 73947116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 74047116Sbostic break; 74147116Sbostic } 74247116Sbostic savelastp = exparg.lastp; 74347116Sbostic INTOFF; 74455272Smarc if (expdir == NULL) { 74555272Smarc int i = strlen(str->text); 74655272Smarc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 74755272Smarc } 74855272Smarc 74947116Sbostic expmeta(expdir, str->text); 75047116Sbostic ckfree(expdir); 75147116Sbostic expdir = NULL; 75247116Sbostic INTON; 75347116Sbostic if (exparg.lastp == savelastp) { 75453299Smarc /* 75553299Smarc * no matches 75653299Smarc */ 75747116Sbostic nometa: 75855243Smarc *exparg.lastp = str; 75955243Smarc rmescapes(str->text); 76055243Smarc exparg.lastp = &str->next; 76147116Sbostic } else { 76247116Sbostic *exparg.lastp = NULL; 76347116Sbostic *savelastp = sp = expsort(*savelastp); 76447116Sbostic while (sp->next != NULL) 76547116Sbostic sp = sp->next; 76647116Sbostic exparg.lastp = &sp->next; 76747116Sbostic } 76847116Sbostic str = str->next; 76947116Sbostic } 77047116Sbostic } 77147116Sbostic 77247116Sbostic 77347116Sbostic /* 77447116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 77547116Sbostic */ 77647116Sbostic 77747116Sbostic STATIC void 77847116Sbostic expmeta(enddir, name) 77947116Sbostic char *enddir; 78047116Sbostic char *name; 78147116Sbostic { 78247116Sbostic register char *p; 78347116Sbostic char *q; 78447116Sbostic char *start; 78547116Sbostic char *endname; 78647116Sbostic int metaflag; 78747116Sbostic struct stat statb; 78847116Sbostic DIR *dirp; 78947116Sbostic struct dirent *dp; 79047116Sbostic int atend; 79147116Sbostic int matchdot; 79247116Sbostic 79347116Sbostic metaflag = 0; 79447116Sbostic start = name; 79547116Sbostic for (p = name ; ; p++) { 79647116Sbostic if (*p == '*' || *p == '?') 79747116Sbostic metaflag = 1; 79847116Sbostic else if (*p == '[') { 79947116Sbostic q = p + 1; 80047116Sbostic if (*q == '!') 80147116Sbostic q++; 80247116Sbostic for (;;) { 80347116Sbostic if (*q == CTLESC) 80447116Sbostic q++; 80547116Sbostic if (*q == '/' || *q == '\0') 80647116Sbostic break; 80747116Sbostic if (*++q == ']') { 80847116Sbostic metaflag = 1; 80947116Sbostic break; 81047116Sbostic } 81147116Sbostic } 81247116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 81347116Sbostic metaflag = 1; 81447116Sbostic } else if (*p == '\0') 81547116Sbostic break; 81647116Sbostic else if (*p == CTLESC) 81747116Sbostic p++; 81847116Sbostic if (*p == '/') { 81947116Sbostic if (metaflag) 82047116Sbostic break; 82147116Sbostic start = p + 1; 82247116Sbostic } 82347116Sbostic } 82447116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 82547116Sbostic if (enddir != expdir) 82647116Sbostic metaflag++; 82747116Sbostic for (p = name ; ; p++) { 82847116Sbostic if (*p == CTLESC) 82947116Sbostic p++; 83047116Sbostic *enddir++ = *p; 83147116Sbostic if (*p == '\0') 83247116Sbostic break; 83347116Sbostic } 83447116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 83547116Sbostic addfname(expdir); 83647116Sbostic return; 83747116Sbostic } 83847116Sbostic endname = p; 83947116Sbostic if (start != name) { 84047116Sbostic p = name; 84147116Sbostic while (p < start) { 84247116Sbostic if (*p == CTLESC) 84347116Sbostic p++; 84447116Sbostic *enddir++ = *p++; 84547116Sbostic } 84647116Sbostic } 84747116Sbostic if (enddir == expdir) { 84847116Sbostic p = "."; 84947116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 85047116Sbostic p = "/"; 85147116Sbostic } else { 85247116Sbostic p = expdir; 85347116Sbostic enddir[-1] = '\0'; 85447116Sbostic } 85547116Sbostic if ((dirp = opendir(p)) == NULL) 85647116Sbostic return; 85747116Sbostic if (enddir != expdir) 85847116Sbostic enddir[-1] = '/'; 85947116Sbostic if (*endname == 0) { 86047116Sbostic atend = 1; 86147116Sbostic } else { 86247116Sbostic atend = 0; 86347116Sbostic *endname++ = '\0'; 86447116Sbostic } 86547116Sbostic matchdot = 0; 86647116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 86747116Sbostic matchdot++; 86847116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 86947116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 87047116Sbostic continue; 87147116Sbostic if (patmatch(start, dp->d_name)) { 87247116Sbostic if (atend) { 87347116Sbostic scopy(dp->d_name, enddir); 87447116Sbostic addfname(expdir); 87547116Sbostic } else { 87647116Sbostic char *q; 87747116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 87847116Sbostic p[-1] = '/'; 87947116Sbostic expmeta(p, endname); 88047116Sbostic } 88147116Sbostic } 88247116Sbostic } 88347116Sbostic closedir(dirp); 88447116Sbostic if (! atend) 88547116Sbostic endname[-1] = '/'; 88647116Sbostic } 88747116Sbostic 88847116Sbostic 88947116Sbostic /* 89047116Sbostic * Add a file name to the list. 89147116Sbostic */ 89247116Sbostic 89347116Sbostic STATIC void 89447116Sbostic addfname(name) 89547116Sbostic char *name; 89647116Sbostic { 89747116Sbostic char *p; 89847116Sbostic struct strlist *sp; 89947116Sbostic 90047116Sbostic p = stalloc(strlen(name) + 1); 90147116Sbostic scopy(name, p); 90247116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 90347116Sbostic sp->text = p; 90447116Sbostic *exparg.lastp = sp; 90547116Sbostic exparg.lastp = &sp->next; 90647116Sbostic } 90747116Sbostic 90847116Sbostic 90947116Sbostic /* 91047116Sbostic * Sort the results of file name expansion. It calculates the number of 91147116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 91247116Sbostic * work. 91347116Sbostic */ 91447116Sbostic 91547116Sbostic STATIC struct strlist * 91647116Sbostic expsort(str) 91747116Sbostic struct strlist *str; 91847116Sbostic { 91947116Sbostic int len; 92047116Sbostic struct strlist *sp; 92147116Sbostic 92247116Sbostic len = 0; 92347116Sbostic for (sp = str ; sp ; sp = sp->next) 92447116Sbostic len++; 92547116Sbostic return msort(str, len); 92647116Sbostic } 92747116Sbostic 92847116Sbostic 92947116Sbostic STATIC struct strlist * 93047116Sbostic msort(list, len) 93147116Sbostic struct strlist *list; 93247116Sbostic { 93347116Sbostic struct strlist *p, *q; 93447116Sbostic struct strlist **lpp; 93547116Sbostic int half; 93647116Sbostic int n; 93747116Sbostic 93847116Sbostic if (len <= 1) 93947116Sbostic return list; 94047116Sbostic half = len >> 1; 94147116Sbostic p = list; 94247116Sbostic for (n = half ; --n >= 0 ; ) { 94347116Sbostic q = p; 94447116Sbostic p = p->next; 94547116Sbostic } 94647116Sbostic q->next = NULL; /* terminate first half of list */ 94747116Sbostic q = msort(list, half); /* sort first half of list */ 94847116Sbostic p = msort(p, len - half); /* sort second half */ 94947116Sbostic lpp = &list; 95047116Sbostic for (;;) { 95147116Sbostic if (strcmp(p->text, q->text) < 0) { 95247116Sbostic *lpp = p; 95347116Sbostic lpp = &p->next; 95447116Sbostic if ((p = *lpp) == NULL) { 95547116Sbostic *lpp = q; 95647116Sbostic break; 95747116Sbostic } 95847116Sbostic } else { 95947116Sbostic *lpp = q; 96047116Sbostic lpp = &q->next; 96147116Sbostic if ((q = *lpp) == NULL) { 96247116Sbostic *lpp = p; 96347116Sbostic break; 96447116Sbostic } 96547116Sbostic } 96647116Sbostic } 96747116Sbostic return list; 96847116Sbostic } 96947116Sbostic 97047116Sbostic 97147116Sbostic 97247116Sbostic /* 97347116Sbostic * Returns true if the pattern matches the string. 97447116Sbostic */ 97547116Sbostic 97647116Sbostic int 97747116Sbostic patmatch(pattern, string) 97847116Sbostic char *pattern; 97947116Sbostic char *string; 98047116Sbostic { 98155787Smarc #ifdef notdef 98247116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 98347116Sbostic return 1 - pmatch(pattern + 2, string); 98447116Sbostic else 98555787Smarc #endif 98647116Sbostic return pmatch(pattern, string); 98747116Sbostic } 98847116Sbostic 98947116Sbostic 99047116Sbostic STATIC int 99147116Sbostic pmatch(pattern, string) 99247116Sbostic char *pattern; 99347116Sbostic char *string; 99447116Sbostic { 99547116Sbostic register char *p, *q; 99647116Sbostic register char c; 99747116Sbostic 99847116Sbostic p = pattern; 99947116Sbostic q = string; 100047116Sbostic for (;;) { 100147116Sbostic switch (c = *p++) { 100247116Sbostic case '\0': 100347116Sbostic goto breakloop; 100447116Sbostic case CTLESC: 100547116Sbostic if (*q++ != *p++) 100647116Sbostic return 0; 100747116Sbostic break; 100847116Sbostic case '?': 100947116Sbostic if (*q++ == '\0') 101047116Sbostic return 0; 101147116Sbostic break; 101247116Sbostic case '*': 101347116Sbostic c = *p; 101447116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 101547116Sbostic while (*q != c) { 101647116Sbostic if (*q == '\0') 101747116Sbostic return 0; 101847116Sbostic q++; 101947116Sbostic } 102047116Sbostic } 102147116Sbostic do { 102247116Sbostic if (pmatch(p, q)) 102347116Sbostic return 1; 102447116Sbostic } while (*q++ != '\0'); 102547116Sbostic return 0; 102647116Sbostic case '[': { 102747116Sbostic char *endp; 102847116Sbostic int invert, found; 102947116Sbostic char chr; 103047116Sbostic 103147116Sbostic endp = p; 103247116Sbostic if (*endp == '!') 103347116Sbostic endp++; 103447116Sbostic for (;;) { 103547116Sbostic if (*endp == '\0') 103647116Sbostic goto dft; /* no matching ] */ 103747116Sbostic if (*endp == CTLESC) 103847116Sbostic endp++; 103947116Sbostic if (*++endp == ']') 104047116Sbostic break; 104147116Sbostic } 104247116Sbostic invert = 0; 104347116Sbostic if (*p == '!') { 104447116Sbostic invert++; 104547116Sbostic p++; 104647116Sbostic } 104747116Sbostic found = 0; 104847116Sbostic chr = *q++; 104947116Sbostic c = *p++; 105047116Sbostic do { 105147116Sbostic if (c == CTLESC) 105247116Sbostic c = *p++; 105347116Sbostic if (*p == '-' && p[1] != ']') { 105447116Sbostic p++; 105547116Sbostic if (*p == CTLESC) 105647116Sbostic p++; 105747116Sbostic if (chr >= c && chr <= *p) 105847116Sbostic found = 1; 105947116Sbostic p++; 106047116Sbostic } else { 106147116Sbostic if (chr == c) 106247116Sbostic found = 1; 106347116Sbostic } 106447116Sbostic } while ((c = *p++) != ']'); 106547116Sbostic if (found == invert) 106647116Sbostic return 0; 106747116Sbostic break; 106847116Sbostic } 106955787Smarc dft: default: 107047116Sbostic if (*q++ != c) 107147116Sbostic return 0; 107247116Sbostic break; 107347116Sbostic } 107447116Sbostic } 107547116Sbostic breakloop: 107647116Sbostic if (*q != '\0') 107747116Sbostic return 0; 107847116Sbostic return 1; 107947116Sbostic } 108047116Sbostic 108147116Sbostic 108247116Sbostic 108347116Sbostic /* 108447116Sbostic * Remove any CTLESC characters from a string. 108547116Sbostic */ 108647116Sbostic 108747116Sbostic void 108847116Sbostic rmescapes(str) 108947116Sbostic char *str; 109047116Sbostic { 109147116Sbostic register char *p, *q; 109247116Sbostic 109347116Sbostic p = str; 109447116Sbostic while (*p != CTLESC) { 109547116Sbostic if (*p++ == '\0') 109647116Sbostic return; 109747116Sbostic } 109847116Sbostic q = p; 109947116Sbostic while (*p) { 110047116Sbostic if (*p == CTLESC) 110147116Sbostic p++; 110247116Sbostic *q++ = *p++; 110347116Sbostic } 110447116Sbostic *q = '\0'; 110547116Sbostic } 110647116Sbostic 110747116Sbostic 110847116Sbostic 110947116Sbostic /* 111047116Sbostic * See if a pattern matches in a case statement. 111147116Sbostic */ 111247116Sbostic 111347116Sbostic int 111447116Sbostic casematch(pattern, val) 111547116Sbostic union node *pattern; 111647116Sbostic char *val; 111747116Sbostic { 111847116Sbostic struct stackmark smark; 111947116Sbostic int result; 112047116Sbostic char *p; 112147116Sbostic 112247116Sbostic setstackmark(&smark); 112347116Sbostic argbackq = pattern->narg.backquote; 112447116Sbostic STARTSTACKSTR(expdest); 112547116Sbostic ifslastp = NULL; 112655787Smarc argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 112747116Sbostic STPUTC('\0', expdest); 112847116Sbostic p = grabstackstr(expdest); 112947116Sbostic result = patmatch(p, val); 113047116Sbostic popstackmark(&smark); 113147116Sbostic return result; 113247116Sbostic } 1133