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*55272Smarc static char sccsid[] = "@(#)expand.c 5.7 (Berkeley) 07/15/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 *); 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 *); 7553299Smarc 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(); 9053299Smarc 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 11653299Smarc 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; 12753299Smarc argstr(arg->narg.text, flag); 12854315Smarc if (arglist == NULL) { 12947116Sbostic return; /* here document expanded */ 13054315Smarc } 13147116Sbostic STPUTC('\0', expdest); 13247116Sbostic p = grabstackstr(expdest); 13347116Sbostic exparg.lastp = &exparg.list; 13453299Smarc /* 13553299Smarc * TODO - EXP_REDIR 13653299Smarc */ 13753299Smarc if (flag & EXP_FULL) { 13847116Sbostic ifsbreakup(p, &exparg); 13947116Sbostic *exparg.lastp = NULL; 14047116Sbostic exparg.lastp = &exparg.list; 14153299Smarc expandmeta(exparg.list, flag); 14247116Sbostic } else { 14354315Smarc if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 14454315Smarc rmescapes(p); 14547116Sbostic sp = (struct strlist *)stalloc(sizeof (struct strlist)); 14647116Sbostic sp->text = p; 14747116Sbostic *exparg.lastp = sp; 14847116Sbostic exparg.lastp = &sp->next; 14947116Sbostic } 15047116Sbostic while (ifsfirst.next != NULL) { 15147116Sbostic struct ifsregion *ifsp; 15247116Sbostic INTOFF; 15347116Sbostic ifsp = ifsfirst.next->next; 15447116Sbostic ckfree(ifsfirst.next); 15547116Sbostic ifsfirst.next = ifsp; 15647116Sbostic INTON; 15747116Sbostic } 15847116Sbostic *exparg.lastp = NULL; 15947116Sbostic if (exparg.list) { 16047116Sbostic *arglist->lastp = exparg.list; 16147116Sbostic arglist->lastp = exparg.lastp; 16247116Sbostic } 16347116Sbostic } 16447116Sbostic 16547116Sbostic 16647116Sbostic 16747116Sbostic /* 16847116Sbostic * Perform variable and command substitution. If full is set, output CTLESC 16947116Sbostic * characters to allow for further processing. If full is not set, treat 17047116Sbostic * $@ like $* since no splitting will be performed. 17147116Sbostic */ 17247116Sbostic 17347116Sbostic STATIC void 17453299Smarc argstr(p, flag) 17547116Sbostic register char *p; 17647116Sbostic { 17747116Sbostic char c; 17853276Smarc int sawari = 0; 17953299Smarc int full = flag & EXP_FULL; /* is there a later stage? */ 18053299Smarc int vartilde = flag & EXP_VARTILDE; 18147116Sbostic 18253299Smarc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 18353299Smarc p = exptilde(p, vartilde); 18447116Sbostic for (;;) { 18547116Sbostic switch (c = *p++) { 18647116Sbostic case '\0': 18753276Smarc case CTLENDVAR: /* ??? */ 18847116Sbostic goto breakloop; 18947116Sbostic case CTLESC: 19047116Sbostic if (full) 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: 20047116Sbostic expbackq(argbackq->n, c & CTLQUOTE, full); 20147116Sbostic argbackq = argbackq->next; 20247116Sbostic break; 20353276Smarc case CTLENDARI: 20453276Smarc expari(); 20553276Smarc break; 20653299Smarc case ':': 20754315Smarc STPUTC(c, expdest); 20854315Smarc if (vartilde && *p == '~') 20953299Smarc p = exptilde(p, vartilde); 21054315Smarc break; 21147116Sbostic default: 21247116Sbostic STPUTC(c, expdest); 21347116Sbostic } 21447116Sbostic } 21547116Sbostic breakloop:; 21647116Sbostic } 21747116Sbostic 21853299Smarc STATIC char * 21953299Smarc exptilde(p, vartilde) 22053299Smarc char *p; 22153299Smarc { 22253299Smarc char c, *startp = p; 22353299Smarc struct passwd *pw; 22453299Smarc char *home; 22553299Smarc 22653299Smarc while (c = *p) { 22753299Smarc switch(c) { 22853299Smarc case CTLESC: 22953299Smarc return (startp); 23053299Smarc case ':': 23153299Smarc if (vartilde) 23253299Smarc goto done; 23353299Smarc break; 23453299Smarc case '/': 23553299Smarc goto done; 23653299Smarc } 23753299Smarc p++; 23853299Smarc } 23953299Smarc done: 24053299Smarc *p = '\0'; 24153299Smarc if (*(startp+1) == '\0') { 24253299Smarc if ((home = lookupvar("HOME")) == NULL) 24353299Smarc goto lose; 24453299Smarc } else { 24553299Smarc if ((pw = getpwnam(startp+1)) == NULL) 24653299Smarc goto lose; 24753299Smarc home = pw->pw_dir; 24853299Smarc } 24953299Smarc if (*home == '\0') 25053299Smarc goto lose; 25153299Smarc *p = c; 25253299Smarc while (c = *home++) { 25353299Smarc if (SQSYNTAX[c] == CCTL) 25453299Smarc STPUTC(CTLESC, expdest); 25553299Smarc STPUTC(c, expdest); 25653299Smarc } 25753299Smarc return (p); 25853299Smarc lose: 25953299Smarc *p = c; 26053299Smarc return (startp); 26153299Smarc } 26253299Smarc 26353299Smarc 26453276Smarc /* 26553276Smarc * Expand arithmetic expression. Backup to start of expression, 26653276Smarc * evaluate, place result in (backed up) result, adjust string position. 26753276Smarc */ 26853276Smarc expari() 26953299Smarc { 27055243Smarc char *p, *start; 27153276Smarc int result; 27247116Sbostic 27353276Smarc /* 27453276Smarc * This routine is slightly over-compilcated for 27553276Smarc * efficiency. First we make sure there is 27653276Smarc * enough space for the result, which may be bigger 27753276Smarc * than the expression if we add exponentation. Next we 27853276Smarc * scan backwards looking for the start of arithmetic. If the 27953276Smarc * next previous character is a CTLESC character, then we 28053276Smarc * have to rescan starting from the beginning since CTLESC 28153276Smarc * characters have to be processed left to right. 28253276Smarc */ 28353276Smarc CHECKSTRSPACE(8, expdest); 28455243Smarc USTPUTC('\0', expdest); 28555243Smarc start = stackblock(); 28653276Smarc p = expdest; 28753276Smarc while (*p != CTLARI && p >= start) 28853276Smarc --p; 28953276Smarc if (*p != CTLARI) 29053276Smarc error("missing CTLARI (shouldn't happen)"); 29153276Smarc if (p > start && *(p-1) == CTLESC) 29253276Smarc for (p = start; *p != CTLARI; p++) 29353276Smarc if (*p == CTLESC) 29453276Smarc p++; 29553276Smarc rmescapes(p+1); 29653276Smarc result = arith(p+1); 29753276Smarc fmtstr(p, 10, "%d", result); 29853276Smarc while (*p++) 29953276Smarc ; 30053276Smarc result = expdest - p + 1; 30153276Smarc STADJUST(-result, expdest); 30253276Smarc } 30353276Smarc 30453276Smarc 30547116Sbostic /* 30647116Sbostic * Expand stuff in backwards quotes. 30747116Sbostic */ 30847116Sbostic 30947116Sbostic STATIC void 31047116Sbostic expbackq(cmd, quoted, full) 31147116Sbostic union node *cmd; 31247116Sbostic { 31347116Sbostic struct backcmd in; 31447116Sbostic int i; 31547116Sbostic char buf[128]; 31647116Sbostic char *p; 31747116Sbostic char *dest = expdest; 31847116Sbostic struct ifsregion saveifs, *savelastp; 31947116Sbostic struct nodelist *saveargbackq; 32047116Sbostic char lastc; 32147116Sbostic int startloc = dest - stackblock(); 32247116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 32347116Sbostic int saveherefd; 32447116Sbostic 32547116Sbostic INTOFF; 32647116Sbostic saveifs = ifsfirst; 32747116Sbostic savelastp = ifslastp; 32847116Sbostic saveargbackq = argbackq; 32947116Sbostic saveherefd = herefd; 33047116Sbostic herefd = -1; 33147116Sbostic p = grabstackstr(dest); 33247116Sbostic evalbackcmd(cmd, &in); 33347116Sbostic ungrabstackstr(p, dest); 33447116Sbostic ifsfirst = saveifs; 33547116Sbostic ifslastp = savelastp; 33647116Sbostic argbackq = saveargbackq; 33747116Sbostic herefd = saveherefd; 33847116Sbostic 33947116Sbostic p = in.buf; 34047116Sbostic lastc = '\0'; 34147116Sbostic for (;;) { 34247116Sbostic if (--in.nleft < 0) { 34347116Sbostic if (in.fd < 0) 34447116Sbostic break; 34547116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 34647116Sbostic TRACE(("expbackq: read returns %d\n", i)); 34747116Sbostic if (i <= 0) 34847116Sbostic break; 34947116Sbostic p = buf; 35047116Sbostic in.nleft = i - 1; 35147116Sbostic } 35247116Sbostic lastc = *p++; 35347116Sbostic if (lastc != '\0') { 35447116Sbostic if (full && syntax[lastc] == CCTL) 35547116Sbostic STPUTC(CTLESC, dest); 35647116Sbostic STPUTC(lastc, dest); 35747116Sbostic } 35847116Sbostic } 35947116Sbostic if (lastc == '\n') { 36047116Sbostic STUNPUTC(dest); 36147116Sbostic } 36247116Sbostic if (in.fd >= 0) 36347116Sbostic close(in.fd); 36447116Sbostic if (in.buf) 36547116Sbostic ckfree(in.buf); 36647116Sbostic if (in.jp) 36747116Sbostic waitforjob(in.jp); 36847116Sbostic if (quoted == 0) 36947116Sbostic recordregion(startloc, dest - stackblock(), 0); 37047116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 37147116Sbostic (dest - stackblock()) - startloc, 37247116Sbostic (dest - stackblock()) - startloc, 37347116Sbostic stackblock() + startloc)); 37447116Sbostic expdest = dest; 37547116Sbostic INTON; 37647116Sbostic } 37747116Sbostic 37847116Sbostic 37947116Sbostic 38047116Sbostic /* 38147116Sbostic * Expand a variable, and return a pointer to the next character in the 38247116Sbostic * input string. 38347116Sbostic */ 38447116Sbostic 38547116Sbostic STATIC char * 38653299Smarc evalvar(p, flag) 38747116Sbostic char *p; 38847116Sbostic { 38947116Sbostic int subtype; 39053299Smarc int varflags; 39147116Sbostic char *var; 39247116Sbostic char *val; 39347116Sbostic int c; 39447116Sbostic int set; 39547116Sbostic int special; 39647116Sbostic int startloc; 39747116Sbostic 39853299Smarc varflags = *p++; 39953299Smarc subtype = varflags & VSTYPE; 40047116Sbostic var = p; 40147116Sbostic special = 0; 40247116Sbostic if (! is_name(*p)) 40347116Sbostic special = 1; 40447116Sbostic p = strchr(p, '=') + 1; 40547116Sbostic again: /* jump here after setting a variable with ${var=text} */ 40647116Sbostic if (special) { 40747116Sbostic set = varisset(*var); 40847116Sbostic val = NULL; 40947116Sbostic } else { 41047116Sbostic val = lookupvar(var); 41153299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 41247116Sbostic val = NULL; 41347116Sbostic set = 0; 41447116Sbostic } else 41547116Sbostic set = 1; 41647116Sbostic } 41747116Sbostic startloc = expdest - stackblock(); 41847116Sbostic if (set && subtype != VSPLUS) { 41947116Sbostic /* insert the value of the variable */ 42047116Sbostic if (special) { 42153299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 42247116Sbostic } else { 42353299Smarc char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 42447116Sbostic 42547116Sbostic while (*val) { 42653299Smarc if ((flag & EXP_FULL) && syntax[*val] == CCTL) 42747116Sbostic STPUTC(CTLESC, expdest); 42847116Sbostic STPUTC(*val++, expdest); 42947116Sbostic } 43047116Sbostic } 43147116Sbostic } 43247116Sbostic if (subtype == VSPLUS) 43347116Sbostic set = ! set; 43453299Smarc if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 43547116Sbostic && (set || subtype == VSNORMAL)) 43653299Smarc recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 43747116Sbostic if (! set && subtype != VSNORMAL) { 43847116Sbostic if (subtype == VSPLUS || subtype == VSMINUS) { 43953299Smarc argstr(p, flag); 44047116Sbostic } else { 44147116Sbostic char *startp; 44247116Sbostic int saveherefd = herefd; 44347116Sbostic herefd = -1; 44447116Sbostic argstr(p, 0); 44547116Sbostic STACKSTRNUL(expdest); 44647116Sbostic herefd = saveherefd; 44747116Sbostic startp = stackblock() + startloc; 44847116Sbostic if (subtype == VSASSIGN) { 44947116Sbostic setvar(var, startp, 0); 45047116Sbostic STADJUST(startp - expdest, expdest); 45153299Smarc varflags &=~ VSNUL; 45247116Sbostic goto again; 45347116Sbostic } 45447116Sbostic /* subtype == VSQUESTION */ 45547116Sbostic if (*p != CTLENDVAR) { 45647116Sbostic outfmt(&errout, "%s\n", startp); 45747116Sbostic error((char *)NULL); 45847116Sbostic } 45947116Sbostic error("%.*s: parameter %snot set", p - var - 1, 46053299Smarc var, (varflags & VSNUL)? "null or " : nullstr); 46147116Sbostic } 46247116Sbostic } 46347116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 46447116Sbostic int nesting = 1; 46547116Sbostic for (;;) { 46647116Sbostic if ((c = *p++) == CTLESC) 46747116Sbostic p++; 46847116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 46947116Sbostic if (set) 47047116Sbostic argbackq = argbackq->next; 47147116Sbostic } else if (c == CTLVAR) { 47247116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 47347116Sbostic nesting++; 47447116Sbostic } else if (c == CTLENDVAR) { 47547116Sbostic if (--nesting == 0) 47647116Sbostic break; 47747116Sbostic } 47847116Sbostic } 47947116Sbostic } 48047116Sbostic return p; 48147116Sbostic } 48247116Sbostic 48347116Sbostic 48447116Sbostic 48547116Sbostic /* 48647116Sbostic * Test whether a specialized variable is set. 48747116Sbostic */ 48847116Sbostic 48947116Sbostic STATIC int 49047116Sbostic varisset(name) 49147116Sbostic char name; 49247116Sbostic { 49347116Sbostic char **ap; 49447116Sbostic 49547116Sbostic if (name == '!') { 49647116Sbostic if (backgndpid == -1) 49747116Sbostic return 0; 49847116Sbostic } else if (name == '@' || name == '*') { 49947116Sbostic if (*shellparam.p == NULL) 50047116Sbostic return 0; 50147116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 50247116Sbostic ap = shellparam.p; 50347116Sbostic do { 50447116Sbostic if (*ap++ == NULL) 50547116Sbostic return 0; 50647116Sbostic } while (--name >= 0); 50747116Sbostic } 50847116Sbostic return 1; 50947116Sbostic } 51047116Sbostic 51147116Sbostic 51247116Sbostic 51347116Sbostic /* 51447116Sbostic * Add the value of a specialized variable to the stack string. 51547116Sbostic */ 51647116Sbostic 51747116Sbostic STATIC void 51847116Sbostic varvalue(name, quoted, allow_split) 51947116Sbostic char name; 52047116Sbostic { 52147116Sbostic int num; 52247116Sbostic char temp[32]; 52347116Sbostic char *p; 52447116Sbostic int i; 52547116Sbostic extern int exitstatus; 52647116Sbostic char sep; 52747116Sbostic char **ap; 52847116Sbostic char const *syntax; 52947116Sbostic 53054315Smarc #define STRTODEST(p) \ 53154315Smarc do {\ 53254315Smarc if (allow_split) { \ 53354315Smarc syntax = quoted? DQSYNTAX : BASESYNTAX; \ 53454315Smarc while (*p) { \ 53554315Smarc if (syntax[*p] == CCTL) \ 53654315Smarc STPUTC(CTLESC, expdest); \ 53754315Smarc STPUTC(*p++, expdest); \ 53854315Smarc } \ 53954315Smarc } else \ 54054315Smarc while (*p) \ 54154315Smarc STPUTC(*p++, expdest); \ 54254315Smarc } while (0) 54354315Smarc 54454315Smarc 54547116Sbostic switch (name) { 54647116Sbostic case '$': 54747116Sbostic num = rootpid; 54847116Sbostic goto numvar; 54947116Sbostic case '?': 55047116Sbostic num = exitstatus; 55147116Sbostic goto numvar; 55247116Sbostic case '#': 55347116Sbostic num = shellparam.nparam; 55447116Sbostic goto numvar; 55547116Sbostic case '!': 55647116Sbostic num = backgndpid; 55747116Sbostic numvar: 55847116Sbostic p = temp + 31; 55947116Sbostic temp[31] = '\0'; 56047116Sbostic do { 56147116Sbostic *--p = num % 10 + '0'; 56247116Sbostic } while ((num /= 10) != 0); 56347116Sbostic while (*p) 56447116Sbostic STPUTC(*p++, expdest); 56547116Sbostic break; 56647116Sbostic case '-': 56755243Smarc for (i = 0 ; i < NOPTS ; i++) { 56855243Smarc if (optlist[i].val) 56955243Smarc STPUTC(optlist[i].letter, expdest); 57047116Sbostic } 57147116Sbostic break; 57247116Sbostic case '@': 57347116Sbostic if (allow_split) { 57447116Sbostic sep = '\0'; 57547116Sbostic goto allargs; 57647116Sbostic } 57747116Sbostic /* fall through */ 57847116Sbostic case '*': 57947116Sbostic sep = ' '; 58047116Sbostic allargs: 58147116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 58254315Smarc STRTODEST(p); 58347116Sbostic if (*ap) 58447116Sbostic STPUTC(sep, expdest); 58547116Sbostic } 58647116Sbostic break; 58747116Sbostic case '0': 58847116Sbostic p = arg0; 58954315Smarc STRTODEST(p); 59047116Sbostic break; 59147116Sbostic default: 59247116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 59347116Sbostic p = shellparam.p[name]; 59454315Smarc STRTODEST(p); 59547116Sbostic } 59647116Sbostic break; 59747116Sbostic } 59847116Sbostic } 59947116Sbostic 60047116Sbostic 60147116Sbostic 60247116Sbostic /* 60347116Sbostic * Record the the fact that we have to scan this region of the 60447116Sbostic * string for IFS characters. 60547116Sbostic */ 60647116Sbostic 60747116Sbostic STATIC void 60847116Sbostic recordregion(start, end, nulonly) { 60947116Sbostic register struct ifsregion *ifsp; 61047116Sbostic 61147116Sbostic if (ifslastp == NULL) { 61247116Sbostic ifsp = &ifsfirst; 61347116Sbostic } else { 61447116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 61547116Sbostic ifslastp->next = ifsp; 61647116Sbostic } 61747116Sbostic ifslastp = ifsp; 61847116Sbostic ifslastp->next = NULL; 61947116Sbostic ifslastp->begoff = start; 62047116Sbostic ifslastp->endoff = end; 62147116Sbostic ifslastp->nulonly = nulonly; 62247116Sbostic } 62347116Sbostic 62447116Sbostic 62547116Sbostic 62647116Sbostic /* 62747116Sbostic * Break the argument string into pieces based upon IFS and add the 62847116Sbostic * strings to the argument list. The regions of the string to be 62947116Sbostic * searched for IFS characters have been stored by recordregion. 63047116Sbostic */ 63147116Sbostic 63247116Sbostic STATIC void 63347116Sbostic ifsbreakup(string, arglist) 63447116Sbostic char *string; 63547116Sbostic struct arglist *arglist; 63647116Sbostic { 63747116Sbostic struct ifsregion *ifsp; 63847116Sbostic struct strlist *sp; 63947116Sbostic char *start; 64047116Sbostic register char *p; 64147116Sbostic char *q; 64247116Sbostic char *ifs; 64347116Sbostic 64447116Sbostic start = string; 64547116Sbostic if (ifslastp != NULL) { 64647116Sbostic ifsp = &ifsfirst; 64747116Sbostic do { 64847116Sbostic p = string + ifsp->begoff; 64947116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 65047116Sbostic while (p < string + ifsp->endoff) { 65147116Sbostic q = p; 65247116Sbostic if (*p == CTLESC) 65347116Sbostic p++; 65447116Sbostic if (strchr(ifs, *p++)) { 65547116Sbostic if (q > start || *ifs != ' ') { 65647116Sbostic *q = '\0'; 65747116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 65847116Sbostic sp->text = start; 65947116Sbostic *arglist->lastp = sp; 66047116Sbostic arglist->lastp = &sp->next; 66147116Sbostic } 66247116Sbostic if (*ifs == ' ') { 66347116Sbostic for (;;) { 66447116Sbostic if (p >= string + ifsp->endoff) 66547116Sbostic break; 66647116Sbostic q = p; 66747116Sbostic if (*p == CTLESC) 66847116Sbostic p++; 66947116Sbostic if (strchr(ifs, *p++) == NULL) { 67047116Sbostic p = q; 67147116Sbostic break; 67247116Sbostic } 67347116Sbostic } 67447116Sbostic } 67547116Sbostic start = p; 67647116Sbostic } 67747116Sbostic } 67847116Sbostic } while ((ifsp = ifsp->next) != NULL); 67947116Sbostic if (*start || (*ifs != ' ' && start > string)) { 68047116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 68147116Sbostic sp->text = start; 68247116Sbostic *arglist->lastp = sp; 68347116Sbostic arglist->lastp = &sp->next; 68447116Sbostic } 68547116Sbostic } else { 68647116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 68747116Sbostic sp->text = start; 68847116Sbostic *arglist->lastp = sp; 68947116Sbostic arglist->lastp = &sp->next; 69047116Sbostic } 69147116Sbostic } 69247116Sbostic 69347116Sbostic 69447116Sbostic 69547116Sbostic /* 69647116Sbostic * Expand shell metacharacters. At this point, the only control characters 69747116Sbostic * should be escapes. The results are stored in the list exparg. 69847116Sbostic */ 69947116Sbostic 70047116Sbostic char *expdir; 70147116Sbostic 70247116Sbostic 70347116Sbostic STATIC void 70453299Smarc expandmeta(str, flag) 70547116Sbostic struct strlist *str; 70647116Sbostic { 70747116Sbostic char *p; 70847116Sbostic struct strlist **savelastp; 70947116Sbostic struct strlist *sp; 71047116Sbostic char c; 71153299Smarc /* TODO - EXP_REDIR */ 71247116Sbostic 71347116Sbostic while (str) { 71447116Sbostic if (fflag) 71547116Sbostic goto nometa; 71647116Sbostic p = str->text; 71747116Sbostic for (;;) { /* fast check for meta chars */ 71847116Sbostic if ((c = *p++) == '\0') 71947116Sbostic goto nometa; 72047116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 72147116Sbostic break; 72247116Sbostic } 72347116Sbostic savelastp = exparg.lastp; 72447116Sbostic INTOFF; 725*55272Smarc if (expdir == NULL) { 726*55272Smarc int i = strlen(str->text); 727*55272Smarc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 728*55272Smarc } 729*55272Smarc 73047116Sbostic expmeta(expdir, str->text); 73147116Sbostic ckfree(expdir); 73247116Sbostic expdir = NULL; 73347116Sbostic INTON; 73447116Sbostic if (exparg.lastp == savelastp) { 73553299Smarc /* 73653299Smarc * no matches 73753299Smarc */ 73847116Sbostic nometa: 73955243Smarc *exparg.lastp = str; 74055243Smarc rmescapes(str->text); 74155243Smarc exparg.lastp = &str->next; 74247116Sbostic } else { 74347116Sbostic *exparg.lastp = NULL; 74447116Sbostic *savelastp = sp = expsort(*savelastp); 74547116Sbostic while (sp->next != NULL) 74647116Sbostic sp = sp->next; 74747116Sbostic exparg.lastp = &sp->next; 74847116Sbostic } 74947116Sbostic str = str->next; 75047116Sbostic } 75147116Sbostic } 75247116Sbostic 75347116Sbostic 75447116Sbostic /* 75547116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 75647116Sbostic */ 75747116Sbostic 75847116Sbostic STATIC void 75947116Sbostic expmeta(enddir, name) 76047116Sbostic char *enddir; 76147116Sbostic char *name; 76247116Sbostic { 76347116Sbostic register char *p; 76447116Sbostic char *q; 76547116Sbostic char *start; 76647116Sbostic char *endname; 76747116Sbostic int metaflag; 76847116Sbostic struct stat statb; 76947116Sbostic DIR *dirp; 77047116Sbostic struct dirent *dp; 77147116Sbostic int atend; 77247116Sbostic int matchdot; 77347116Sbostic 77447116Sbostic metaflag = 0; 77547116Sbostic start = name; 77647116Sbostic for (p = name ; ; p++) { 77747116Sbostic if (*p == '*' || *p == '?') 77847116Sbostic metaflag = 1; 77947116Sbostic else if (*p == '[') { 78047116Sbostic q = p + 1; 78147116Sbostic if (*q == '!') 78247116Sbostic q++; 78347116Sbostic for (;;) { 78447116Sbostic if (*q == CTLESC) 78547116Sbostic q++; 78647116Sbostic if (*q == '/' || *q == '\0') 78747116Sbostic break; 78847116Sbostic if (*++q == ']') { 78947116Sbostic metaflag = 1; 79047116Sbostic break; 79147116Sbostic } 79247116Sbostic } 79347116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 79447116Sbostic metaflag = 1; 79547116Sbostic } else if (*p == '\0') 79647116Sbostic break; 79747116Sbostic else if (*p == CTLESC) 79847116Sbostic p++; 79947116Sbostic if (*p == '/') { 80047116Sbostic if (metaflag) 80147116Sbostic break; 80247116Sbostic start = p + 1; 80347116Sbostic } 80447116Sbostic } 80547116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 80647116Sbostic if (enddir != expdir) 80747116Sbostic metaflag++; 80847116Sbostic for (p = name ; ; p++) { 80947116Sbostic if (*p == CTLESC) 81047116Sbostic p++; 81147116Sbostic *enddir++ = *p; 81247116Sbostic if (*p == '\0') 81347116Sbostic break; 81447116Sbostic } 81547116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 81647116Sbostic addfname(expdir); 81747116Sbostic return; 81847116Sbostic } 81947116Sbostic endname = p; 82047116Sbostic if (start != name) { 82147116Sbostic p = name; 82247116Sbostic while (p < start) { 82347116Sbostic if (*p == CTLESC) 82447116Sbostic p++; 82547116Sbostic *enddir++ = *p++; 82647116Sbostic } 82747116Sbostic } 82847116Sbostic if (enddir == expdir) { 82947116Sbostic p = "."; 83047116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 83147116Sbostic p = "/"; 83247116Sbostic } else { 83347116Sbostic p = expdir; 83447116Sbostic enddir[-1] = '\0'; 83547116Sbostic } 83647116Sbostic if ((dirp = opendir(p)) == NULL) 83747116Sbostic return; 83847116Sbostic if (enddir != expdir) 83947116Sbostic enddir[-1] = '/'; 84047116Sbostic if (*endname == 0) { 84147116Sbostic atend = 1; 84247116Sbostic } else { 84347116Sbostic atend = 0; 84447116Sbostic *endname++ = '\0'; 84547116Sbostic } 84647116Sbostic matchdot = 0; 84747116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 84847116Sbostic matchdot++; 84947116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 85047116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 85147116Sbostic continue; 85247116Sbostic if (patmatch(start, dp->d_name)) { 85347116Sbostic if (atend) { 85447116Sbostic scopy(dp->d_name, enddir); 85547116Sbostic addfname(expdir); 85647116Sbostic } else { 85747116Sbostic char *q; 85847116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 85947116Sbostic p[-1] = '/'; 86047116Sbostic expmeta(p, endname); 86147116Sbostic } 86247116Sbostic } 86347116Sbostic } 86447116Sbostic closedir(dirp); 86547116Sbostic if (! atend) 86647116Sbostic endname[-1] = '/'; 86747116Sbostic } 86847116Sbostic 86947116Sbostic 87047116Sbostic /* 87147116Sbostic * Add a file name to the list. 87247116Sbostic */ 87347116Sbostic 87447116Sbostic STATIC void 87547116Sbostic addfname(name) 87647116Sbostic char *name; 87747116Sbostic { 87847116Sbostic char *p; 87947116Sbostic struct strlist *sp; 88047116Sbostic 88147116Sbostic p = stalloc(strlen(name) + 1); 88247116Sbostic scopy(name, p); 88347116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 88447116Sbostic sp->text = p; 88547116Sbostic *exparg.lastp = sp; 88647116Sbostic exparg.lastp = &sp->next; 88747116Sbostic } 88847116Sbostic 88947116Sbostic 89047116Sbostic /* 89147116Sbostic * Sort the results of file name expansion. It calculates the number of 89247116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 89347116Sbostic * work. 89447116Sbostic */ 89547116Sbostic 89647116Sbostic STATIC struct strlist * 89747116Sbostic expsort(str) 89847116Sbostic struct strlist *str; 89947116Sbostic { 90047116Sbostic int len; 90147116Sbostic struct strlist *sp; 90247116Sbostic 90347116Sbostic len = 0; 90447116Sbostic for (sp = str ; sp ; sp = sp->next) 90547116Sbostic len++; 90647116Sbostic return msort(str, len); 90747116Sbostic } 90847116Sbostic 90947116Sbostic 91047116Sbostic STATIC struct strlist * 91147116Sbostic msort(list, len) 91247116Sbostic struct strlist *list; 91347116Sbostic { 91447116Sbostic struct strlist *p, *q; 91547116Sbostic struct strlist **lpp; 91647116Sbostic int half; 91747116Sbostic int n; 91847116Sbostic 91947116Sbostic if (len <= 1) 92047116Sbostic return list; 92147116Sbostic half = len >> 1; 92247116Sbostic p = list; 92347116Sbostic for (n = half ; --n >= 0 ; ) { 92447116Sbostic q = p; 92547116Sbostic p = p->next; 92647116Sbostic } 92747116Sbostic q->next = NULL; /* terminate first half of list */ 92847116Sbostic q = msort(list, half); /* sort first half of list */ 92947116Sbostic p = msort(p, len - half); /* sort second half */ 93047116Sbostic lpp = &list; 93147116Sbostic for (;;) { 93247116Sbostic if (strcmp(p->text, q->text) < 0) { 93347116Sbostic *lpp = p; 93447116Sbostic lpp = &p->next; 93547116Sbostic if ((p = *lpp) == NULL) { 93647116Sbostic *lpp = q; 93747116Sbostic break; 93847116Sbostic } 93947116Sbostic } else { 94047116Sbostic *lpp = q; 94147116Sbostic lpp = &q->next; 94247116Sbostic if ((q = *lpp) == NULL) { 94347116Sbostic *lpp = p; 94447116Sbostic break; 94547116Sbostic } 94647116Sbostic } 94747116Sbostic } 94847116Sbostic return list; 94947116Sbostic } 95047116Sbostic 95147116Sbostic 95247116Sbostic 95347116Sbostic /* 95447116Sbostic * Returns true if the pattern matches the string. 95547116Sbostic */ 95647116Sbostic 95747116Sbostic int 95847116Sbostic patmatch(pattern, string) 95947116Sbostic char *pattern; 96047116Sbostic char *string; 96147116Sbostic { 96247116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 96347116Sbostic return 1 - pmatch(pattern + 2, string); 96447116Sbostic else 96547116Sbostic return pmatch(pattern, string); 96647116Sbostic } 96747116Sbostic 96847116Sbostic 96947116Sbostic STATIC int 97047116Sbostic pmatch(pattern, string) 97147116Sbostic char *pattern; 97247116Sbostic char *string; 97347116Sbostic { 97447116Sbostic register char *p, *q; 97547116Sbostic register char c; 97647116Sbostic 97747116Sbostic p = pattern; 97847116Sbostic q = string; 97947116Sbostic for (;;) { 98047116Sbostic switch (c = *p++) { 98147116Sbostic case '\0': 98247116Sbostic goto breakloop; 98347116Sbostic case CTLESC: 98447116Sbostic if (*q++ != *p++) 98547116Sbostic return 0; 98647116Sbostic break; 98747116Sbostic case '?': 98847116Sbostic if (*q++ == '\0') 98947116Sbostic return 0; 99047116Sbostic break; 99147116Sbostic case '*': 99247116Sbostic c = *p; 99347116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 99447116Sbostic while (*q != c) { 99547116Sbostic if (*q == '\0') 99647116Sbostic return 0; 99747116Sbostic q++; 99847116Sbostic } 99947116Sbostic } 100047116Sbostic do { 100147116Sbostic if (pmatch(p, q)) 100247116Sbostic return 1; 100347116Sbostic } while (*q++ != '\0'); 100447116Sbostic return 0; 100547116Sbostic case '[': { 100647116Sbostic char *endp; 100747116Sbostic int invert, found; 100847116Sbostic char chr; 100947116Sbostic 101047116Sbostic endp = p; 101147116Sbostic if (*endp == '!') 101247116Sbostic endp++; 101347116Sbostic for (;;) { 101447116Sbostic if (*endp == '\0') 101547116Sbostic goto dft; /* no matching ] */ 101647116Sbostic if (*endp == CTLESC) 101747116Sbostic endp++; 101847116Sbostic if (*++endp == ']') 101947116Sbostic break; 102047116Sbostic } 102147116Sbostic invert = 0; 102247116Sbostic if (*p == '!') { 102347116Sbostic invert++; 102447116Sbostic p++; 102547116Sbostic } 102647116Sbostic found = 0; 102747116Sbostic chr = *q++; 102847116Sbostic c = *p++; 102947116Sbostic do { 103047116Sbostic if (c == CTLESC) 103147116Sbostic c = *p++; 103247116Sbostic if (*p == '-' && p[1] != ']') { 103347116Sbostic p++; 103447116Sbostic if (*p == CTLESC) 103547116Sbostic p++; 103647116Sbostic if (chr >= c && chr <= *p) 103747116Sbostic found = 1; 103847116Sbostic p++; 103947116Sbostic } else { 104047116Sbostic if (chr == c) 104147116Sbostic found = 1; 104247116Sbostic } 104347116Sbostic } while ((c = *p++) != ']'); 104447116Sbostic if (found == invert) 104547116Sbostic return 0; 104647116Sbostic break; 104747116Sbostic } 104847116Sbostic dft: default: 104947116Sbostic if (*q++ != c) 105047116Sbostic return 0; 105147116Sbostic break; 105247116Sbostic } 105347116Sbostic } 105447116Sbostic breakloop: 105547116Sbostic if (*q != '\0') 105647116Sbostic return 0; 105747116Sbostic return 1; 105847116Sbostic } 105947116Sbostic 106047116Sbostic 106147116Sbostic 106247116Sbostic /* 106347116Sbostic * Remove any CTLESC characters from a string. 106447116Sbostic */ 106547116Sbostic 106647116Sbostic void 106747116Sbostic rmescapes(str) 106847116Sbostic char *str; 106947116Sbostic { 107047116Sbostic register char *p, *q; 107147116Sbostic 107247116Sbostic p = str; 107347116Sbostic while (*p != CTLESC) { 107447116Sbostic if (*p++ == '\0') 107547116Sbostic return; 107647116Sbostic } 107747116Sbostic q = p; 107847116Sbostic while (*p) { 107947116Sbostic if (*p == CTLESC) 108047116Sbostic p++; 108147116Sbostic *q++ = *p++; 108247116Sbostic } 108347116Sbostic *q = '\0'; 108447116Sbostic } 108547116Sbostic 108647116Sbostic 108747116Sbostic 108847116Sbostic /* 108947116Sbostic * See if a pattern matches in a case statement. 109047116Sbostic */ 109147116Sbostic 109247116Sbostic int 109347116Sbostic casematch(pattern, val) 109447116Sbostic union node *pattern; 109547116Sbostic char *val; 109647116Sbostic { 109747116Sbostic struct stackmark smark; 109847116Sbostic int result; 109947116Sbostic char *p; 110047116Sbostic 110147116Sbostic setstackmark(&smark); 110247116Sbostic argbackq = pattern->narg.backquote; 110347116Sbostic STARTSTACKSTR(expdest); 110447116Sbostic ifslastp = NULL; 110547116Sbostic argstr(pattern->narg.text, 0); 110647116Sbostic STPUTC('\0', expdest); 110747116Sbostic p = grabstackstr(expdest); 110847116Sbostic result = patmatch(p, val); 110947116Sbostic popstackmark(&smark); 111047116Sbostic return result; 111147116Sbostic } 1112