X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/95f51977ddc18faa2e212f30c00a39540b39f325..7c6d863706f1dade8eba78648ed0007d7b73e95c:/usr/src/bin/sh/expand.c diff --git a/usr/src/bin/sh/expand.c b/usr/src/bin/sh/expand.c index 38ce8d3c5b..1c92fe4077 100644 --- a/usr/src/bin/sh/expand.c +++ b/usr/src/bin/sh/expand.c @@ -1,194 +1,1104 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + #ifndef lint -static char sccsid[] = "@(#)expand.c 4.5 8/11/83"; +static char sccsid[] = "@(#)expand.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include +#include +#include +#include + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + + +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ +#if UDIR +/* + * Set if the last argument processed had /u/logname expanded. This + * variable is read by the cd command. + */ +int didudir; #endif -# +#ifdef __STDC__ +STATIC void argstr(char *, int); +STATIC void expbackq(union node *, int, int); +STATIC char *evalvar(char *, int); +STATIC int varisset(int); +STATIC void varvalue(int, int, int); +STATIC void recordregion(int, int, int); +STATIC void ifsbreakup(char *, struct arglist *); +STATIC void expandmeta(struct strlist *); +STATIC void expmeta(char *, char *); +STATIC void addfname(char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +STATIC int pmatch(char *, char *); +#else +STATIC void argstr(); +STATIC void expbackq(); +STATIC char *evalvar(); +STATIC int varisset(); +STATIC void varvalue(); +STATIC void recordregion(); +STATIC void ifsbreakup(); +STATIC void expandmeta(); +STATIC void expmeta(); +STATIC void addfname(); +STATIC struct strlist *expsort(); +STATIC struct strlist *msort(); +STATIC int pmatch(); +#endif +#if UDIR +#ifdef __STDC__ +STATIC char *expudir(char *); +#else +STATIC char *expudir(); +#endif +#endif /* UDIR */ + + + /* - * UNIX shell - * - * S. R. Bourne - * Bell Telephone Laboratories - * + * Expand shell variables and backquotes inside a here document. */ -#include "defs.h" -#include -#include -#include +void +expandhere(arg, fd) + union node *arg; /* the document */ + int fd; /* where to write the expanded version */ + { + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If full is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ -/* globals (file name generation) - * - * "*" in params matches r.e ".*" - * "?" in params matches r.e. "." - * "[...]" in params matches character class - * "[...a-z...]" in params matches a through z. - * +void +expandarg(arg, arglist, full) + union node *arg; + struct arglist *arglist; + { + struct strlist *sp; + char *p; + +#if UDIR + didudir = 0; +#endif + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, full); + if (arglist == NULL) + return; /* here document expanded */ + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + if (full) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list); + } else { + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + + +/* + * Perform variable and command substitution. If full is set, output CTLESC + * characters to allow for further processing. If full is not set, treat + * $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(p, full) + register char *p; + { + char c; + + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: + goto breakloop; + case CTLESC: + if (full) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, full); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, full); + argbackq = argbackq->next; + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(cmd, quoted, full) + union node *cmd; + { + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + struct ifsregion saveifs, *savelastp; + struct nodelist *saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int saveherefd; + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (full && syntax[lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + if (lastc == '\n') { + STUNPUTC(dest); + } + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + waitforjob(in.jp); + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(p, full) + char *p; + { + int subtype; + int flags; + char *var; + char *val; + int c; + int set; + int special; + int startloc; + + flags = *p++; + subtype = flags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(*var); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || (flags & VSNUL) && val[0] == '\0') { + val = NULL; + set = 0; + } else + set = 1; + } + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(*var, flags & VSQUOTE, full); + } else { + char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX; + + while (*val) { + if (full && syntax[*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + } + } + if (subtype == VSPLUS) + set = ! set; + if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) + && (set || subtype == VSNORMAL)) + recordregion(startloc, expdest - stackblock(), flags & VSQUOTE); + if (! set && subtype != VSNORMAL) { + if (subtype == VSPLUS || subtype == VSMINUS) { + argstr(p, full); + } else { + char *startp; + int saveherefd = herefd; + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + startp = stackblock() + startloc; + if (subtype == VSASSIGN) { + setvar(var, startp, 0); + STADJUST(startp - expdest, expdest); + flags &=~ VSNUL; + goto again; + } + /* subtype == VSQUESTION */ + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - var - 1, + var, (flags & VSNUL)? "null or " : nullstr); + } + } + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a specialized variable is set. + */ + +STATIC int +varisset(name) + char name; + { + char **ap; + + if (name == '!') { + if (backgndpid == -1) + return 0; + } else if (name == '@' || name == '*') { + if (*shellparam.p == NULL) + return 0; + } else if ((unsigned)(name -= '1') <= '9' - '1') { + ap = shellparam.p; + do { + if (*ap++ == NULL) + return 0; + } while (--name >= 0); + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(name, quoted, allow_split) + char name; + { + int num; + char temp[32]; + char *p; + int i; + extern int exitstatus; + char sep; + char **ap; + char const *syntax; + + switch (name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + p = temp + 31; + temp[31] = '\0'; + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + while (*p) + STPUTC(*p++, expdest); + break; + case '-': + for (i = 0 ; optchar[i] ; i++) { + if (optval[i]) + STPUTC(optchar[i], expdest); + } + break; + case '@': + if (allow_split) { + sep = '\0'; + goto allargs; + } + /* fall through */ + case '*': + sep = ' '; +allargs: + syntax = quoted? DQSYNTAX : BASESYNTAX; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + /* should insert CTLESC characters */ + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + if (*ap) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; +string: + syntax = quoted? DQSYNTAX : BASESYNTAX; + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + break; + default: + if ((unsigned)(name -= '1') <= '9' - '1') { + p = shellparam.p[name]; + goto string; + } + break; + } +} + + + +/* + * Record the the fact that we have to scan this region of the + * string for IFS characters. + */ + +STATIC void +recordregion(start, end, nulonly) { + register struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifslastp->next = ifsp; + } + ifslastp = ifsp; + ifslastp->next = NULL; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ + +STATIC void +ifsbreakup(string, arglist) + char *string; + struct arglist *arglist; + { + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + register char *p; + char *q; + char *ifs; + + start = string; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + ifs = ifsp->nulonly? nullstr : ifsval(); + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++)) { + if (q > start || *ifs != ' ') { + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + if (*ifs == ' ') { + for (;;) { + if (p >= string + ifsp->endoff) + break; + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++) == NULL) { + p = q; + break; + } + } + } + start = p; + } + } + } while ((ifsp = ifsp->next) != NULL); + if (*start || (*ifs != ' ' && start > string)) { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + } else { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +char *expdir; + + +STATIC void +expandmeta(str) + struct strlist *str; + { + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + + while (str) { + if (fflag) + goto nometa; + p = str->text; +#if UDIR + if (p[0] == '/' && p[1] == 'u' && p[2] == '/') + str->text = p = expudir(p); +#endif + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) + expdir = ckmalloc(1024); /* I hope this is big enough */ + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + INTON; + if (exparg.lastp == savelastp) { + if (! zflag) { +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +#if UDIR +/* + * Expand /u/username into the home directory for the specified user. + * We could use the getpw stuff here, but then we would have to load + * in stdio and who knows what else. + */ + +#define MAXLOGNAME 32 +#define MAXPWLINE 128 + +char *pfgets(); + + +STATIC char * +expudir(path) + char *path; + { + register char *p, *q, *r; + char name[MAXLOGNAME]; + char line[MAXPWLINE]; + int i; + + r = path; /* result on failure */ + p = r + 3; /* the 3 skips "/u/" */ + q = name; + while (*p && *p != '/') { + if (q >= name + MAXLOGNAME - 1) + return r; /* fail, name too long */ + *q++ = *p++; + } + *q = '\0'; + setinputfile("/etc/passwd", 1); + q = line + strlen(name); + while (pfgets(line, MAXPWLINE) != NULL) { + if (line[0] == name[0] && prefix(name, line) && *q == ':') { + /* skip to start of home directory */ + i = 4; + do { + while (*++q && *q != ':'); + } while (--i > 0); + if (*q == '\0') + break; /* fail, corrupted /etc/passwd */ + q++; + for (r = q ; *r && *r != '\n' && *r != ':' ; r++); + *r = '\0'; /* nul terminate home directory */ + i = r - q; /* i = strlen(q) */ + r = stalloc(i + strlen(p) + 1); + scopy(q, r); + scopy(p, r + i); + TRACE(("expudir converts %s to %s\n", path, r)); + didudir = 1; + path = r; /* succeed */ + break; + } + } + popfile(); + return r; +} +#endif + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(enddir, name) + char *enddir; + char *name; + { + register char *p; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || stat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + p = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + p = "/"; + } else { + p = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(p)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + char *q; + for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + + +/* + * Add a file name to the list. */ -PROC VOID addg(); +STATIC void +addfname(name) + char *name; + { + char *p; + struct strlist *sp; + p = stalloc(strlen(name) + 1); + scopy(name, p); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} -INT expand(as,rflg) - STRING as; -{ - INT count; - DIR *dirf; - BOOL dir=0; - STRING rescan = 0; - REG STRING s, cs; - ARGPTR schain = gchain; - struct direct *dp; - STATBUF statb; - IF trapnote&SIGSET THEN return(0); FI +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(str) + struct strlist *str; + { + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} - s=cs=as; - /* check for meta chars */ - BEGIN - REG BOOL slash; slash=0; - WHILE !fngchar(*cs) - DO IF *cs++==0 - THEN IF rflg ANDF slash THEN break; ELSE return(0) FI - ELIF *cs=='/' - THEN slash++; - FI - OD - END +STATIC struct strlist * +msort(list, len) + struct strlist *list; + { + struct strlist *p, *q; + struct strlist **lpp; + int half; + int n; - LOOP IF cs==s - THEN s=nullstr; + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + + +/* + * Returns true if the pattern matches the string. + */ + +int +patmatch(pattern, string) + char *pattern; + char *string; + { + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else + return pmatch(pattern, string); +} + + +STATIC int +pmatch(pattern, string) + char *pattern; + char *string; + { + register char *p, *q; + register char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (*q++ != *p++) + return 0; break; - ELIF *--cs == '/' - THEN *cs=0; - IF s==cs THEN s="/" FI + case '?': + if (*q++ == '\0') + return 0; break; - FI - POOL - IF stat(s,&statb)>=0 - ANDF (statb.st_mode&S_IFMT)==S_IFDIR - ANDF (dirf=opendir(s)) != NULL - THEN dir++; - FI - count=0; - IF *cs==0 THEN *cs++=0200 FI - IF dir - THEN /* check for rescan */ - REG STRING rs; rs=cs; - - REP IF *rs=='/' THEN rescan=rs; *rs=0; gchain=0 FI - PER *rs++ DONE - - IF setjmp(INTbuf) == 0 THEN trapjmp[INTR] = 1; FI - WHILE (trapnote&SIGSET) == 0 ANDF (dp = readdir(dirf)) != NULL - DO IF (*dp->d_name=='.' ANDF *cs!='.') - THEN continue; - FI - IF gmatch(dp->d_name, cs) - THEN addg(s,dp->d_name,rescan); count++; - FI - OD - closedir(dirf); trapjmp[INTR] = 0; - - IF rescan - THEN REG ARGPTR rchain; - rchain=gchain; gchain=schain; - IF count - THEN count=0; - WHILE rchain - DO count += expand(rchain->argval,1); - rchain=rchain->argnxt; - OD - FI - *rescan='/'; - FI - FI - - BEGIN - REG CHAR c; - s=as; - WHILE c = *s - DO *s++=(c&STRIP?c:'/') OD - END - return(count); -} - -gmatch(s, p) - REG STRING s, p; -{ - REG INT scc; - CHAR c; - - IF scc = *s++ - THEN IF (scc &= STRIP)==0 - THEN scc=0200; - FI - FI - SWITCH c = *p++ IN - - case '[': - {BOOL ok; INT lc; - ok=0; lc=077777; - WHILE c = *p++ - DO IF c==']' - THEN return(ok?gmatch(s,p):0); - ELIF c==MINUS - THEN IF lc<=scc ANDF scc<=(*p++) THEN ok++ FI - ELSE IF scc==(lc=(c&STRIP)) THEN ok++ FI - FI - OD - return(0); - } - - default: - IF (c&STRIP)!=scc THEN return(0) FI - - case '?': - return(scc?gmatch(s,p):0); - - case '*': - IF *p==0 THEN return(1) FI - --s; - WHILE *s - DO IF gmatch(s++,p) THEN return(1) FI OD - return(0); - - case 0: - return(scc==0); - ENDSW -} - -LOCAL VOID addg(as1,as2,as3) - STRING as1, as2, as3; -{ - REG STRING s1, s2; - REG INT c; - - s2 = locstak()+BYTESPERWORD; - - s1=as1; - WHILE c = *s1++ - DO IF (c &= STRIP)==0 - THEN *s2++='/'; + case '*': + c = *p; + if (c != CTLESC && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + c = *p++; + do { + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; break; - FI - *s2++=c; - OD - s1=as2; - WHILE *s2 = *s1++ DO s2++ OD - IF s1=as3 - THEN *s2++='/'; - WHILE *s2++ = *++s1 DONE - FI - makearg(endstak(s2)); + } +dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; } -makearg(args) - REG STRING args; -{ - args->argnxt=gchain; - gchain=args; + + +/* + * Remove any CTLESC characters from a string. + */ + +void +rmescapes(str) + char *str; + { + register char *p, *q; + + p = str; + while (*p != CTLESC) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; } + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(pattern, val) + union node *pattern; + char *val; + { + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, 0); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val); + popstackmark(&smark); + return result; +}