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*54315Smarc static char sccsid[] = "@(#)expand.c 5.4 (Berkeley) 06/23/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); 128*54315Smarc if (arglist == NULL) { 12947116Sbostic return; /* here document expanded */ 130*54315Smarc } 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 { 143*54315Smarc if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 144*54315Smarc 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 ':': 207*54315Smarc STPUTC(c, expdest); 208*54315Smarc if (vartilde && *p == '~') 20953299Smarc p = exptilde(p, vartilde); 210*54315Smarc 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 { 27053276Smarc char *p, *start = stackblock(); 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); 28453276Smarc USTPUTC('\0', expdest); 28553276Smarc p = expdest; 28653276Smarc while (*p != CTLARI && p >= start) 28753276Smarc --p; 28853276Smarc if (*p != CTLARI) 28953276Smarc error("missing CTLARI (shouldn't happen)"); 29053276Smarc if (p > start && *(p-1) == CTLESC) 29153276Smarc for (p = start; *p != CTLARI; p++) 29253276Smarc if (*p == CTLESC) 29353276Smarc p++; 29453276Smarc rmescapes(p+1); 29553276Smarc result = arith(p+1); 29653276Smarc fmtstr(p, 10, "%d", result); 29753276Smarc while (*p++) 29853276Smarc ; 29953276Smarc result = expdest - p + 1; 30053276Smarc STADJUST(-result, expdest); 30153276Smarc } 30253276Smarc 30353276Smarc 30447116Sbostic /* 30547116Sbostic * Expand stuff in backwards quotes. 30647116Sbostic */ 30747116Sbostic 30847116Sbostic STATIC void 30947116Sbostic expbackq(cmd, quoted, full) 31047116Sbostic union node *cmd; 31147116Sbostic { 31247116Sbostic struct backcmd in; 31347116Sbostic int i; 31447116Sbostic char buf[128]; 31547116Sbostic char *p; 31647116Sbostic char *dest = expdest; 31747116Sbostic struct ifsregion saveifs, *savelastp; 31847116Sbostic struct nodelist *saveargbackq; 31947116Sbostic char lastc; 32047116Sbostic int startloc = dest - stackblock(); 32147116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 32247116Sbostic int saveherefd; 32347116Sbostic 32447116Sbostic INTOFF; 32547116Sbostic saveifs = ifsfirst; 32647116Sbostic savelastp = ifslastp; 32747116Sbostic saveargbackq = argbackq; 32847116Sbostic saveherefd = herefd; 32947116Sbostic herefd = -1; 33047116Sbostic p = grabstackstr(dest); 33147116Sbostic evalbackcmd(cmd, &in); 33247116Sbostic ungrabstackstr(p, dest); 33347116Sbostic ifsfirst = saveifs; 33447116Sbostic ifslastp = savelastp; 33547116Sbostic argbackq = saveargbackq; 33647116Sbostic herefd = saveherefd; 33747116Sbostic 33847116Sbostic p = in.buf; 33947116Sbostic lastc = '\0'; 34047116Sbostic for (;;) { 34147116Sbostic if (--in.nleft < 0) { 34247116Sbostic if (in.fd < 0) 34347116Sbostic break; 34447116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 34547116Sbostic TRACE(("expbackq: read returns %d\n", i)); 34647116Sbostic if (i <= 0) 34747116Sbostic break; 34847116Sbostic p = buf; 34947116Sbostic in.nleft = i - 1; 35047116Sbostic } 35147116Sbostic lastc = *p++; 35247116Sbostic if (lastc != '\0') { 35347116Sbostic if (full && syntax[lastc] == CCTL) 35447116Sbostic STPUTC(CTLESC, dest); 35547116Sbostic STPUTC(lastc, dest); 35647116Sbostic } 35747116Sbostic } 35847116Sbostic if (lastc == '\n') { 35947116Sbostic STUNPUTC(dest); 36047116Sbostic } 36147116Sbostic if (in.fd >= 0) 36247116Sbostic close(in.fd); 36347116Sbostic if (in.buf) 36447116Sbostic ckfree(in.buf); 36547116Sbostic if (in.jp) 36647116Sbostic waitforjob(in.jp); 36747116Sbostic if (quoted == 0) 36847116Sbostic recordregion(startloc, dest - stackblock(), 0); 36947116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 37047116Sbostic (dest - stackblock()) - startloc, 37147116Sbostic (dest - stackblock()) - startloc, 37247116Sbostic stackblock() + startloc)); 37347116Sbostic expdest = dest; 37447116Sbostic INTON; 37547116Sbostic } 37647116Sbostic 37747116Sbostic 37847116Sbostic 37947116Sbostic /* 38047116Sbostic * Expand a variable, and return a pointer to the next character in the 38147116Sbostic * input string. 38247116Sbostic */ 38347116Sbostic 38447116Sbostic STATIC char * 38553299Smarc evalvar(p, flag) 38647116Sbostic char *p; 38747116Sbostic { 38847116Sbostic int subtype; 38953299Smarc int varflags; 39047116Sbostic char *var; 39147116Sbostic char *val; 39247116Sbostic int c; 39347116Sbostic int set; 39447116Sbostic int special; 39547116Sbostic int startloc; 39647116Sbostic 39753299Smarc varflags = *p++; 39853299Smarc subtype = varflags & VSTYPE; 39947116Sbostic var = p; 40047116Sbostic special = 0; 40147116Sbostic if (! is_name(*p)) 40247116Sbostic special = 1; 40347116Sbostic p = strchr(p, '=') + 1; 40447116Sbostic again: /* jump here after setting a variable with ${var=text} */ 40547116Sbostic if (special) { 40647116Sbostic set = varisset(*var); 40747116Sbostic val = NULL; 40847116Sbostic } else { 40947116Sbostic val = lookupvar(var); 41053299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 41147116Sbostic val = NULL; 41247116Sbostic set = 0; 41347116Sbostic } else 41447116Sbostic set = 1; 41547116Sbostic } 41647116Sbostic startloc = expdest - stackblock(); 41747116Sbostic if (set && subtype != VSPLUS) { 41847116Sbostic /* insert the value of the variable */ 41947116Sbostic if (special) { 42053299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 42147116Sbostic } else { 42253299Smarc char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; 42347116Sbostic 42447116Sbostic while (*val) { 42553299Smarc if ((flag & EXP_FULL) && syntax[*val] == CCTL) 42647116Sbostic STPUTC(CTLESC, expdest); 42747116Sbostic STPUTC(*val++, expdest); 42847116Sbostic } 42947116Sbostic } 43047116Sbostic } 43147116Sbostic if (subtype == VSPLUS) 43247116Sbostic set = ! set; 43353299Smarc if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) 43447116Sbostic && (set || subtype == VSNORMAL)) 43553299Smarc recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); 43647116Sbostic if (! set && subtype != VSNORMAL) { 43747116Sbostic if (subtype == VSPLUS || subtype == VSMINUS) { 43853299Smarc argstr(p, flag); 43947116Sbostic } else { 44047116Sbostic char *startp; 44147116Sbostic int saveherefd = herefd; 44247116Sbostic herefd = -1; 44347116Sbostic argstr(p, 0); 44447116Sbostic STACKSTRNUL(expdest); 44547116Sbostic herefd = saveherefd; 44647116Sbostic startp = stackblock() + startloc; 44747116Sbostic if (subtype == VSASSIGN) { 44847116Sbostic setvar(var, startp, 0); 44947116Sbostic STADJUST(startp - expdest, expdest); 45053299Smarc varflags &=~ VSNUL; 45147116Sbostic goto again; 45247116Sbostic } 45347116Sbostic /* subtype == VSQUESTION */ 45447116Sbostic if (*p != CTLENDVAR) { 45547116Sbostic outfmt(&errout, "%s\n", startp); 45647116Sbostic error((char *)NULL); 45747116Sbostic } 45847116Sbostic error("%.*s: parameter %snot set", p - var - 1, 45953299Smarc var, (varflags & VSNUL)? "null or " : nullstr); 46047116Sbostic } 46147116Sbostic } 46247116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 46347116Sbostic int nesting = 1; 46447116Sbostic for (;;) { 46547116Sbostic if ((c = *p++) == CTLESC) 46647116Sbostic p++; 46747116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 46847116Sbostic if (set) 46947116Sbostic argbackq = argbackq->next; 47047116Sbostic } else if (c == CTLVAR) { 47147116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 47247116Sbostic nesting++; 47347116Sbostic } else if (c == CTLENDVAR) { 47447116Sbostic if (--nesting == 0) 47547116Sbostic break; 47647116Sbostic } 47747116Sbostic } 47847116Sbostic } 47947116Sbostic return p; 48047116Sbostic } 48147116Sbostic 48247116Sbostic 48347116Sbostic 48447116Sbostic /* 48547116Sbostic * Test whether a specialized variable is set. 48647116Sbostic */ 48747116Sbostic 48847116Sbostic STATIC int 48947116Sbostic varisset(name) 49047116Sbostic char name; 49147116Sbostic { 49247116Sbostic char **ap; 49347116Sbostic 49447116Sbostic if (name == '!') { 49547116Sbostic if (backgndpid == -1) 49647116Sbostic return 0; 49747116Sbostic } else if (name == '@' || name == '*') { 49847116Sbostic if (*shellparam.p == NULL) 49947116Sbostic return 0; 50047116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 50147116Sbostic ap = shellparam.p; 50247116Sbostic do { 50347116Sbostic if (*ap++ == NULL) 50447116Sbostic return 0; 50547116Sbostic } while (--name >= 0); 50647116Sbostic } 50747116Sbostic return 1; 50847116Sbostic } 50947116Sbostic 51047116Sbostic 51147116Sbostic 51247116Sbostic /* 51347116Sbostic * Add the value of a specialized variable to the stack string. 51447116Sbostic */ 51547116Sbostic 51647116Sbostic STATIC void 51747116Sbostic varvalue(name, quoted, allow_split) 51847116Sbostic char name; 51947116Sbostic { 52047116Sbostic int num; 52147116Sbostic char temp[32]; 52247116Sbostic char *p; 52347116Sbostic int i; 52447116Sbostic extern int exitstatus; 52547116Sbostic char sep; 52647116Sbostic char **ap; 52747116Sbostic char const *syntax; 52847116Sbostic 529*54315Smarc #define STRTODEST(p) \ 530*54315Smarc do {\ 531*54315Smarc if (allow_split) { \ 532*54315Smarc syntax = quoted? DQSYNTAX : BASESYNTAX; \ 533*54315Smarc while (*p) { \ 534*54315Smarc if (syntax[*p] == CCTL) \ 535*54315Smarc STPUTC(CTLESC, expdest); \ 536*54315Smarc STPUTC(*p++, expdest); \ 537*54315Smarc } \ 538*54315Smarc } else \ 539*54315Smarc while (*p) \ 540*54315Smarc STPUTC(*p++, expdest); \ 541*54315Smarc } while (0) 542*54315Smarc 543*54315Smarc 54447116Sbostic switch (name) { 54547116Sbostic case '$': 54647116Sbostic num = rootpid; 54747116Sbostic goto numvar; 54847116Sbostic case '?': 54947116Sbostic num = exitstatus; 55047116Sbostic goto numvar; 55147116Sbostic case '#': 55247116Sbostic num = shellparam.nparam; 55347116Sbostic goto numvar; 55447116Sbostic case '!': 55547116Sbostic num = backgndpid; 55647116Sbostic numvar: 55747116Sbostic p = temp + 31; 55847116Sbostic temp[31] = '\0'; 55947116Sbostic do { 56047116Sbostic *--p = num % 10 + '0'; 56147116Sbostic } while ((num /= 10) != 0); 56247116Sbostic while (*p) 56347116Sbostic STPUTC(*p++, expdest); 56447116Sbostic break; 56547116Sbostic case '-': 56647116Sbostic for (i = 0 ; optchar[i] ; i++) { 56747116Sbostic if (optval[i]) 56847116Sbostic STPUTC(optchar[i], expdest); 56947116Sbostic } 57047116Sbostic break; 57147116Sbostic case '@': 57247116Sbostic if (allow_split) { 57347116Sbostic sep = '\0'; 57447116Sbostic goto allargs; 57547116Sbostic } 57647116Sbostic /* fall through */ 57747116Sbostic case '*': 57847116Sbostic sep = ' '; 57947116Sbostic allargs: 58047116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 581*54315Smarc STRTODEST(p); 58247116Sbostic if (*ap) 58347116Sbostic STPUTC(sep, expdest); 58447116Sbostic } 58547116Sbostic break; 58647116Sbostic case '0': 58747116Sbostic p = arg0; 588*54315Smarc STRTODEST(p); 58947116Sbostic break; 59047116Sbostic default: 59147116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 59247116Sbostic p = shellparam.p[name]; 593*54315Smarc STRTODEST(p); 59447116Sbostic } 59547116Sbostic break; 59647116Sbostic } 59747116Sbostic } 59847116Sbostic 59947116Sbostic 60047116Sbostic 60147116Sbostic /* 60247116Sbostic * Record the the fact that we have to scan this region of the 60347116Sbostic * string for IFS characters. 60447116Sbostic */ 60547116Sbostic 60647116Sbostic STATIC void 60747116Sbostic recordregion(start, end, nulonly) { 60847116Sbostic register struct ifsregion *ifsp; 60947116Sbostic 61047116Sbostic if (ifslastp == NULL) { 61147116Sbostic ifsp = &ifsfirst; 61247116Sbostic } else { 61347116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 61447116Sbostic ifslastp->next = ifsp; 61547116Sbostic } 61647116Sbostic ifslastp = ifsp; 61747116Sbostic ifslastp->next = NULL; 61847116Sbostic ifslastp->begoff = start; 61947116Sbostic ifslastp->endoff = end; 62047116Sbostic ifslastp->nulonly = nulonly; 62147116Sbostic } 62247116Sbostic 62347116Sbostic 62447116Sbostic 62547116Sbostic /* 62647116Sbostic * Break the argument string into pieces based upon IFS and add the 62747116Sbostic * strings to the argument list. The regions of the string to be 62847116Sbostic * searched for IFS characters have been stored by recordregion. 62947116Sbostic */ 63047116Sbostic 63147116Sbostic STATIC void 63247116Sbostic ifsbreakup(string, arglist) 63347116Sbostic char *string; 63447116Sbostic struct arglist *arglist; 63547116Sbostic { 63647116Sbostic struct ifsregion *ifsp; 63747116Sbostic struct strlist *sp; 63847116Sbostic char *start; 63947116Sbostic register char *p; 64047116Sbostic char *q; 64147116Sbostic char *ifs; 64247116Sbostic 64347116Sbostic start = string; 64447116Sbostic if (ifslastp != NULL) { 64547116Sbostic ifsp = &ifsfirst; 64647116Sbostic do { 64747116Sbostic p = string + ifsp->begoff; 64847116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 64947116Sbostic while (p < string + ifsp->endoff) { 65047116Sbostic q = p; 65147116Sbostic if (*p == CTLESC) 65247116Sbostic p++; 65347116Sbostic if (strchr(ifs, *p++)) { 65447116Sbostic if (q > start || *ifs != ' ') { 65547116Sbostic *q = '\0'; 65647116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 65747116Sbostic sp->text = start; 65847116Sbostic *arglist->lastp = sp; 65947116Sbostic arglist->lastp = &sp->next; 66047116Sbostic } 66147116Sbostic if (*ifs == ' ') { 66247116Sbostic for (;;) { 66347116Sbostic if (p >= string + ifsp->endoff) 66447116Sbostic break; 66547116Sbostic q = p; 66647116Sbostic if (*p == CTLESC) 66747116Sbostic p++; 66847116Sbostic if (strchr(ifs, *p++) == NULL) { 66947116Sbostic p = q; 67047116Sbostic break; 67147116Sbostic } 67247116Sbostic } 67347116Sbostic } 67447116Sbostic start = p; 67547116Sbostic } 67647116Sbostic } 67747116Sbostic } while ((ifsp = ifsp->next) != NULL); 67847116Sbostic if (*start || (*ifs != ' ' && start > string)) { 67947116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 68047116Sbostic sp->text = start; 68147116Sbostic *arglist->lastp = sp; 68247116Sbostic arglist->lastp = &sp->next; 68347116Sbostic } 68447116Sbostic } else { 68547116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 68647116Sbostic sp->text = start; 68747116Sbostic *arglist->lastp = sp; 68847116Sbostic arglist->lastp = &sp->next; 68947116Sbostic } 69047116Sbostic } 69147116Sbostic 69247116Sbostic 69347116Sbostic 69447116Sbostic /* 69547116Sbostic * Expand shell metacharacters. At this point, the only control characters 69647116Sbostic * should be escapes. The results are stored in the list exparg. 69747116Sbostic */ 69847116Sbostic 69947116Sbostic char *expdir; 70047116Sbostic 70147116Sbostic 70247116Sbostic STATIC void 70353299Smarc expandmeta(str, flag) 70447116Sbostic struct strlist *str; 70547116Sbostic { 70647116Sbostic char *p; 70747116Sbostic struct strlist **savelastp; 70847116Sbostic struct strlist *sp; 70947116Sbostic char c; 71053299Smarc /* TODO - EXP_REDIR */ 71147116Sbostic 71247116Sbostic while (str) { 71347116Sbostic if (fflag) 71447116Sbostic goto nometa; 71547116Sbostic p = str->text; 71647116Sbostic for (;;) { /* fast check for meta chars */ 71747116Sbostic if ((c = *p++) == '\0') 71847116Sbostic goto nometa; 71947116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 72047116Sbostic break; 72147116Sbostic } 72247116Sbostic savelastp = exparg.lastp; 72347116Sbostic INTOFF; 72447116Sbostic if (expdir == NULL) 72547116Sbostic expdir = ckmalloc(1024); /* I hope this is big enough */ 72647116Sbostic expmeta(expdir, str->text); 72747116Sbostic ckfree(expdir); 72847116Sbostic expdir = NULL; 72947116Sbostic INTON; 73047116Sbostic if (exparg.lastp == savelastp) { 73153299Smarc /* 73253299Smarc * no matches 73353299Smarc */ 73447116Sbostic if (! zflag) { 73547116Sbostic nometa: 73647116Sbostic *exparg.lastp = str; 73747116Sbostic rmescapes(str->text); 73847116Sbostic exparg.lastp = &str->next; 73947116Sbostic } 74047116Sbostic } else { 74147116Sbostic *exparg.lastp = NULL; 74247116Sbostic *savelastp = sp = expsort(*savelastp); 74347116Sbostic while (sp->next != NULL) 74447116Sbostic sp = sp->next; 74547116Sbostic exparg.lastp = &sp->next; 74647116Sbostic } 74747116Sbostic str = str->next; 74847116Sbostic } 74947116Sbostic } 75047116Sbostic 75147116Sbostic 75247116Sbostic /* 75347116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 75447116Sbostic */ 75547116Sbostic 75647116Sbostic STATIC void 75747116Sbostic expmeta(enddir, name) 75847116Sbostic char *enddir; 75947116Sbostic char *name; 76047116Sbostic { 76147116Sbostic register char *p; 76247116Sbostic char *q; 76347116Sbostic char *start; 76447116Sbostic char *endname; 76547116Sbostic int metaflag; 76647116Sbostic struct stat statb; 76747116Sbostic DIR *dirp; 76847116Sbostic struct dirent *dp; 76947116Sbostic int atend; 77047116Sbostic int matchdot; 77147116Sbostic 77247116Sbostic metaflag = 0; 77347116Sbostic start = name; 77447116Sbostic for (p = name ; ; p++) { 77547116Sbostic if (*p == '*' || *p == '?') 77647116Sbostic metaflag = 1; 77747116Sbostic else if (*p == '[') { 77847116Sbostic q = p + 1; 77947116Sbostic if (*q == '!') 78047116Sbostic q++; 78147116Sbostic for (;;) { 78247116Sbostic if (*q == CTLESC) 78347116Sbostic q++; 78447116Sbostic if (*q == '/' || *q == '\0') 78547116Sbostic break; 78647116Sbostic if (*++q == ']') { 78747116Sbostic metaflag = 1; 78847116Sbostic break; 78947116Sbostic } 79047116Sbostic } 79147116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 79247116Sbostic metaflag = 1; 79347116Sbostic } else if (*p == '\0') 79447116Sbostic break; 79547116Sbostic else if (*p == CTLESC) 79647116Sbostic p++; 79747116Sbostic if (*p == '/') { 79847116Sbostic if (metaflag) 79947116Sbostic break; 80047116Sbostic start = p + 1; 80147116Sbostic } 80247116Sbostic } 80347116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 80447116Sbostic if (enddir != expdir) 80547116Sbostic metaflag++; 80647116Sbostic for (p = name ; ; p++) { 80747116Sbostic if (*p == CTLESC) 80847116Sbostic p++; 80947116Sbostic *enddir++ = *p; 81047116Sbostic if (*p == '\0') 81147116Sbostic break; 81247116Sbostic } 81347116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 81447116Sbostic addfname(expdir); 81547116Sbostic return; 81647116Sbostic } 81747116Sbostic endname = p; 81847116Sbostic if (start != name) { 81947116Sbostic p = name; 82047116Sbostic while (p < start) { 82147116Sbostic if (*p == CTLESC) 82247116Sbostic p++; 82347116Sbostic *enddir++ = *p++; 82447116Sbostic } 82547116Sbostic } 82647116Sbostic if (enddir == expdir) { 82747116Sbostic p = "."; 82847116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 82947116Sbostic p = "/"; 83047116Sbostic } else { 83147116Sbostic p = expdir; 83247116Sbostic enddir[-1] = '\0'; 83347116Sbostic } 83447116Sbostic if ((dirp = opendir(p)) == NULL) 83547116Sbostic return; 83647116Sbostic if (enddir != expdir) 83747116Sbostic enddir[-1] = '/'; 83847116Sbostic if (*endname == 0) { 83947116Sbostic atend = 1; 84047116Sbostic } else { 84147116Sbostic atend = 0; 84247116Sbostic *endname++ = '\0'; 84347116Sbostic } 84447116Sbostic matchdot = 0; 84547116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 84647116Sbostic matchdot++; 84747116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 84847116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 84947116Sbostic continue; 85047116Sbostic if (patmatch(start, dp->d_name)) { 85147116Sbostic if (atend) { 85247116Sbostic scopy(dp->d_name, enddir); 85347116Sbostic addfname(expdir); 85447116Sbostic } else { 85547116Sbostic char *q; 85647116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 85747116Sbostic p[-1] = '/'; 85847116Sbostic expmeta(p, endname); 85947116Sbostic } 86047116Sbostic } 86147116Sbostic } 86247116Sbostic closedir(dirp); 86347116Sbostic if (! atend) 86447116Sbostic endname[-1] = '/'; 86547116Sbostic } 86647116Sbostic 86747116Sbostic 86847116Sbostic /* 86947116Sbostic * Add a file name to the list. 87047116Sbostic */ 87147116Sbostic 87247116Sbostic STATIC void 87347116Sbostic addfname(name) 87447116Sbostic char *name; 87547116Sbostic { 87647116Sbostic char *p; 87747116Sbostic struct strlist *sp; 87847116Sbostic 87947116Sbostic p = stalloc(strlen(name) + 1); 88047116Sbostic scopy(name, p); 88147116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 88247116Sbostic sp->text = p; 88347116Sbostic *exparg.lastp = sp; 88447116Sbostic exparg.lastp = &sp->next; 88547116Sbostic } 88647116Sbostic 88747116Sbostic 88847116Sbostic /* 88947116Sbostic * Sort the results of file name expansion. It calculates the number of 89047116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 89147116Sbostic * work. 89247116Sbostic */ 89347116Sbostic 89447116Sbostic STATIC struct strlist * 89547116Sbostic expsort(str) 89647116Sbostic struct strlist *str; 89747116Sbostic { 89847116Sbostic int len; 89947116Sbostic struct strlist *sp; 90047116Sbostic 90147116Sbostic len = 0; 90247116Sbostic for (sp = str ; sp ; sp = sp->next) 90347116Sbostic len++; 90447116Sbostic return msort(str, len); 90547116Sbostic } 90647116Sbostic 90747116Sbostic 90847116Sbostic STATIC struct strlist * 90947116Sbostic msort(list, len) 91047116Sbostic struct strlist *list; 91147116Sbostic { 91247116Sbostic struct strlist *p, *q; 91347116Sbostic struct strlist **lpp; 91447116Sbostic int half; 91547116Sbostic int n; 91647116Sbostic 91747116Sbostic if (len <= 1) 91847116Sbostic return list; 91947116Sbostic half = len >> 1; 92047116Sbostic p = list; 92147116Sbostic for (n = half ; --n >= 0 ; ) { 92247116Sbostic q = p; 92347116Sbostic p = p->next; 92447116Sbostic } 92547116Sbostic q->next = NULL; /* terminate first half of list */ 92647116Sbostic q = msort(list, half); /* sort first half of list */ 92747116Sbostic p = msort(p, len - half); /* sort second half */ 92847116Sbostic lpp = &list; 92947116Sbostic for (;;) { 93047116Sbostic if (strcmp(p->text, q->text) < 0) { 93147116Sbostic *lpp = p; 93247116Sbostic lpp = &p->next; 93347116Sbostic if ((p = *lpp) == NULL) { 93447116Sbostic *lpp = q; 93547116Sbostic break; 93647116Sbostic } 93747116Sbostic } else { 93847116Sbostic *lpp = q; 93947116Sbostic lpp = &q->next; 94047116Sbostic if ((q = *lpp) == NULL) { 94147116Sbostic *lpp = p; 94247116Sbostic break; 94347116Sbostic } 94447116Sbostic } 94547116Sbostic } 94647116Sbostic return list; 94747116Sbostic } 94847116Sbostic 94947116Sbostic 95047116Sbostic 95147116Sbostic /* 95247116Sbostic * Returns true if the pattern matches the string. 95347116Sbostic */ 95447116Sbostic 95547116Sbostic int 95647116Sbostic patmatch(pattern, string) 95747116Sbostic char *pattern; 95847116Sbostic char *string; 95947116Sbostic { 96047116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 96147116Sbostic return 1 - pmatch(pattern + 2, string); 96247116Sbostic else 96347116Sbostic return pmatch(pattern, string); 96447116Sbostic } 96547116Sbostic 96647116Sbostic 96747116Sbostic STATIC int 96847116Sbostic pmatch(pattern, string) 96947116Sbostic char *pattern; 97047116Sbostic char *string; 97147116Sbostic { 97247116Sbostic register char *p, *q; 97347116Sbostic register char c; 97447116Sbostic 97547116Sbostic p = pattern; 97647116Sbostic q = string; 97747116Sbostic for (;;) { 97847116Sbostic switch (c = *p++) { 97947116Sbostic case '\0': 98047116Sbostic goto breakloop; 98147116Sbostic case CTLESC: 98247116Sbostic if (*q++ != *p++) 98347116Sbostic return 0; 98447116Sbostic break; 98547116Sbostic case '?': 98647116Sbostic if (*q++ == '\0') 98747116Sbostic return 0; 98847116Sbostic break; 98947116Sbostic case '*': 99047116Sbostic c = *p; 99147116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 99247116Sbostic while (*q != c) { 99347116Sbostic if (*q == '\0') 99447116Sbostic return 0; 99547116Sbostic q++; 99647116Sbostic } 99747116Sbostic } 99847116Sbostic do { 99947116Sbostic if (pmatch(p, q)) 100047116Sbostic return 1; 100147116Sbostic } while (*q++ != '\0'); 100247116Sbostic return 0; 100347116Sbostic case '[': { 100447116Sbostic char *endp; 100547116Sbostic int invert, found; 100647116Sbostic char chr; 100747116Sbostic 100847116Sbostic endp = p; 100947116Sbostic if (*endp == '!') 101047116Sbostic endp++; 101147116Sbostic for (;;) { 101247116Sbostic if (*endp == '\0') 101347116Sbostic goto dft; /* no matching ] */ 101447116Sbostic if (*endp == CTLESC) 101547116Sbostic endp++; 101647116Sbostic if (*++endp == ']') 101747116Sbostic break; 101847116Sbostic } 101947116Sbostic invert = 0; 102047116Sbostic if (*p == '!') { 102147116Sbostic invert++; 102247116Sbostic p++; 102347116Sbostic } 102447116Sbostic found = 0; 102547116Sbostic chr = *q++; 102647116Sbostic c = *p++; 102747116Sbostic do { 102847116Sbostic if (c == CTLESC) 102947116Sbostic c = *p++; 103047116Sbostic if (*p == '-' && p[1] != ']') { 103147116Sbostic p++; 103247116Sbostic if (*p == CTLESC) 103347116Sbostic p++; 103447116Sbostic if (chr >= c && chr <= *p) 103547116Sbostic found = 1; 103647116Sbostic p++; 103747116Sbostic } else { 103847116Sbostic if (chr == c) 103947116Sbostic found = 1; 104047116Sbostic } 104147116Sbostic } while ((c = *p++) != ']'); 104247116Sbostic if (found == invert) 104347116Sbostic return 0; 104447116Sbostic break; 104547116Sbostic } 104647116Sbostic dft: default: 104747116Sbostic if (*q++ != c) 104847116Sbostic return 0; 104947116Sbostic break; 105047116Sbostic } 105147116Sbostic } 105247116Sbostic breakloop: 105347116Sbostic if (*q != '\0') 105447116Sbostic return 0; 105547116Sbostic return 1; 105647116Sbostic } 105747116Sbostic 105847116Sbostic 105947116Sbostic 106047116Sbostic /* 106147116Sbostic * Remove any CTLESC characters from a string. 106247116Sbostic */ 106347116Sbostic 106447116Sbostic void 106547116Sbostic rmescapes(str) 106647116Sbostic char *str; 106747116Sbostic { 106847116Sbostic register char *p, *q; 106947116Sbostic 107047116Sbostic p = str; 107147116Sbostic while (*p != CTLESC) { 107247116Sbostic if (*p++ == '\0') 107347116Sbostic return; 107447116Sbostic } 107547116Sbostic q = p; 107647116Sbostic while (*p) { 107747116Sbostic if (*p == CTLESC) 107847116Sbostic p++; 107947116Sbostic *q++ = *p++; 108047116Sbostic } 108147116Sbostic *q = '\0'; 108247116Sbostic } 108347116Sbostic 108447116Sbostic 108547116Sbostic 108647116Sbostic /* 108747116Sbostic * See if a pattern matches in a case statement. 108847116Sbostic */ 108947116Sbostic 109047116Sbostic int 109147116Sbostic casematch(pattern, val) 109247116Sbostic union node *pattern; 109347116Sbostic char *val; 109447116Sbostic { 109547116Sbostic struct stackmark smark; 109647116Sbostic int result; 109747116Sbostic char *p; 109847116Sbostic 109947116Sbostic setstackmark(&smark); 110047116Sbostic argbackq = pattern->narg.backquote; 110147116Sbostic STARTSTACKSTR(expdest); 110247116Sbostic ifslastp = NULL; 110347116Sbostic argstr(pattern->narg.text, 0); 110447116Sbostic STPUTC('\0', expdest); 110547116Sbostic p = grabstackstr(expdest); 110647116Sbostic result = patmatch(p, val); 110747116Sbostic popstackmark(&smark); 110847116Sbostic return result; 110947116Sbostic } 1110