diff options
author | Tanaka Akira <akr@users.sourceforge.net> | 1999-08-06 18:01:35 +0000 |
---|---|---|
committer | Tanaka Akira <akr@users.sourceforge.net> | 1999-08-06 18:01:35 +0000 |
commit | 784c413690c71212ad9e08bb093414abd1cacc08 (patch) | |
tree | 450cc9242047dd50255af3b1ef940dae5bb3ab39 /Src | |
parent | 61e68d70da5af5afe943f92cd94a8c96e78348d9 (diff) | |
download | zsh-784c413690c71212ad9e08bb093414abd1cacc08.tar.gz zsh-784c413690c71212ad9e08bb093414abd1cacc08.tar.xz zsh-784c413690c71212ad9e08bb093414abd1cacc08.zip |
zsh-3.1.6-pws-1 zsh-3.1.6-pws-1
Diffstat (limited to 'Src')
-rw-r--r-- | Src/.distfiles | 2 | ||||
-rw-r--r-- | Src/Makefile.in | 5 | ||||
-rw-r--r-- | Src/Modules/example.c | 4 | ||||
-rw-r--r-- | Src/Zle/comp.h | 3 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 43 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 128 | ||||
-rw-r--r-- | Src/builtin.c | 91 | ||||
-rw-r--r-- | Src/cond.c | 5 | ||||
-rw-r--r-- | Src/glob.c | 1789 | ||||
-rw-r--r-- | Src/hashtable.c | 4 | ||||
-rw-r--r-- | Src/hist.c | 28 | ||||
-rw-r--r-- | Src/init.c | 2 | ||||
-rw-r--r-- | Src/jobs.c | 4 | ||||
-rw-r--r-- | Src/loop.c | 5 | ||||
-rw-r--r-- | Src/options.c | 14 | ||||
-rw-r--r-- | Src/params.c | 33 | ||||
-rw-r--r-- | Src/pattern.c | 2284 | ||||
-rw-r--r-- | Src/signals.c | 20 | ||||
-rw-r--r-- | Src/subst.c | 104 | ||||
-rw-r--r-- | Src/system.h | 7 | ||||
-rw-r--r-- | Src/utils.c | 2 | ||||
-rw-r--r-- | Src/zsh.export | 4 | ||||
-rw-r--r-- | Src/zsh.h | 53 | ||||
-rw-r--r-- | Src/zsh.mdd | 2 |
24 files changed, 2943 insertions, 1693 deletions
diff --git a/Src/.distfiles b/Src/.distfiles index ce135a00e..3815b31c8 100644 --- a/Src/.distfiles +++ b/Src/.distfiles @@ -6,7 +6,7 @@ DISTFILES_SRC=' builtin.c compat.c cond.c exec.c glob.c hashtable.c hashtable.h hist.c init.c input.c jobs.c lex.c linklist.c loop.c main.c makepro.awk math.c mem.c mkbltnmlst.sh mkmakemod.sh mkmodindex.sh - module.c options.c params.c parse.c prompt.c prototypes.h + module.c options.c params.c parse.c pattern.c prompt.c prototypes.h signals.c signals.h subst.c system.h text.c utils.c watch.c xmods.conf zsh.h zsh.mdd ztype.h zsh.export ' diff --git a/Src/Makefile.in b/Src/Makefile.in index 0dcfbdee1..e17633f45 100644 --- a/Src/Makefile.in +++ b/Src/Makefile.in @@ -35,6 +35,7 @@ VPATH = @srcdir@ sdir = @srcdir@ sdir_top = @top_srcdir@ INSTALL = @INSTALL@ +LN = @LN@ @DEFS_MK@ @@ -163,10 +164,10 @@ install.bin-here: zsh install.bin-@L@ $(INSTALL_PROGRAM) $(STRIPFLAGS) zsh $(bindir)/zsh-$(VERSION) if test -f $(bindir)/zsh; then \ rm -f $(bindir)/zsh.old; \ - ln $(bindir)/zsh $(bindir)/zsh.old; \ + $(LN) $(bindir)/zsh $(bindir)/zsh.old; \ else :; fi rm -f $(bindir)/zsh.new - ln $(bindir)/zsh-$(VERSION) $(bindir)/zsh.new + $(LN) $(bindir)/zsh-$(VERSION) $(bindir)/zsh.new mv $(bindir)/zsh.new $(bindir)/zsh install.bin-N: diff --git a/Src/Modules/example.c b/Src/Modules/example.c index 1b24f336c..b0bbee967 100644 --- a/Src/Modules/example.c +++ b/Src/Modules/example.c @@ -80,7 +80,7 @@ bin_example(char *nam, char **args, char *ops, int func) static int cond_p_len(char **a, int id) { - char *s1 = cond_str(a, 0); + char *s1 = cond_str(a, 0, 0); if (a[1]) { zlong v = cond_val(a, 1); @@ -95,7 +95,7 @@ cond_p_len(char **a, int id) static int cond_i_ex(char **a, int id) { - char *s1 = cond_str(a, 0), *s2 = cond_str(a, 1); + char *s1 = cond_str(a, 0, 0), *s2 = cond_str(a, 1, 0); return !strcmp("example", dyncat(s1, s2)); } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index b0fbf3ac6..ab8d62c8a 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -227,7 +227,8 @@ struct cmatch { #define CMF_FILE 1 /* this is a file */ #define CMF_REMOVE 2 /* remove the suffix */ #define CMF_PARBR 4 /* paramter expansion with a brace */ -#define CMF_NOLIST 8 /* should not be listed */ +#define CMF_PARNEST 8 /* nested paramter expansion */ +#define CMF_NOLIST 16 /* should not be listed */ /* Stuff for completion matcher control. */ diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index e9ff83387..31daff24c 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1947,25 +1947,25 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) if (compcurrent - 1 < na || compcurrent - 1 > nb) return 0; - - restrict_range(na, nb); + if (mod) + restrict_range(na, nb); return 1; } case CVT_RANGEPAT: { char **p; int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1; - Comp c; + Patprog pp; i = compcurrent - 1; if (i < 0 || i >= l) return 0; singsub(&sa); - c = parsereg(sa); + pp = patcompile(sa, PAT_STATIC, NULL); for (i--, p = compwords + i; i >= 0; p--, i--) { - if (domatch(*p, c, 0)) { + if (pattry(pp, *p)) { b = i + 1; t = 1; break; @@ -1975,10 +1975,10 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) int tt = 0; singsub(&sb); - c = parsereg(sb); + pp = patcompile(sb, PAT_STATIC, NULL); for (i++, p = compwords + i; i < l; p++, i++) { - if (domatch(*p, c, 0)) { + if (pattry(pp, *p)) { e = i - 1; tt = 1; break; @@ -1989,7 +1989,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) } if (e < b) t = 0; - if (t) + if (t && mod) restrict_range(b, e); return t; } @@ -2011,12 +2011,12 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) case CVT_PREPAT: case CVT_SUFPAT: { - Comp c; + Patprog pp; if (!na) return 0; - if (!(c = parsereg(sa))) + if (!(pp = patcompile(sa, PAT_STATIC, 0))) return 0; if (test == CVT_PREPAT) { @@ -2036,15 +2036,15 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) for (; l; l--, p += add) { sav = *p; *p = '\0'; - test = domatch(compprefix, c, 0); + test = pattry(pp, compprefix); *p = sav; if (test && !--na) break; } if (!l) return 0; - - ignore_prefix(p - compprefix); + if (mod) + ignore_prefix(p - compprefix); } else { int l, ol, add; char *p; @@ -2060,13 +2060,13 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) add = -1; } for (; l; l--, p += add) - if (domatch(p, c, 0) && !--na) + if (pattry(pp, p) && !--na) break; if (!l) return 0; - - ignore_suffix(ol - (p - compsuffix)); + if (mod) + ignore_suffix(ol - (p - compsuffix)); } return 1; } @@ -2126,9 +2126,11 @@ bin_compset(char *name, char **argv, char *ops, int func) case CVT_RANGEPAT: tokenize(sa); sa = rembslash(sa); + remnulargs(sa); if (sb) { tokenize(sb); sb = rembslash(sb); + remnulargs(sb); } break; case CVT_PRENUM: @@ -2144,6 +2146,7 @@ bin_compset(char *name, char **argv, char *ops, int func) na = -1; tokenize(sa); sa = rembslash(sa); + remnulargs(sa); break; } return !do_comp_vars(test, na, sa, nb, sb, 1); @@ -2469,10 +2472,10 @@ cond_psfix(char **a, int id) { if (comp_check()) { if (a[1]) - return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1), + return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1), 0, NULL, 0); else - return do_comp_vars(id, -1, cond_str(a, 0), 0, NULL, 0); + return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0); } return 0; } @@ -2481,8 +2484,8 @@ cond_psfix(char **a, int id) static int cond_range(char **a, int id) { - return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0), 0, - (id ? cond_str(a, 1) : NULL), 0); + return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0, + (id ? cond_str(a, 1, 1) : NULL), 0); } static struct builtin bintab[] = { diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 9f4fa0c93..347eb9ffa 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -163,7 +163,7 @@ static int ispattern, haspattern; * from the whole word we are completing and the second one from that * * part of the word that was identified as a possible filename. */ -static Comp patcomp, filecomp; +static Patprog patcomp, filecomp; /* We store the following prefixes/suffixes: * * lpre/lsuf -- what's on the line * @@ -728,7 +728,7 @@ check_param(char *s, int set, int test) if ((*p == String || *p == Qstring) && p[1] != Inpar && p[1] != Inbrack) { /* This is really a parameter expression (not $(...) or $[...]). */ char *b = p + 1, *e = b; - int n = 0, br = 1; + int n = 0, br = 1, nest = 0; if (*b == Inbrace) { char *tb = b; @@ -740,6 +740,10 @@ check_param(char *s, int set, int test) /* Ignore the possible (...) flags. */ b++, br++; n = skipparens(Inpar, Outpar, &b); + + for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--); + if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring)) + nest = 1; } /* Ignore the stuff before the parameter name. */ @@ -788,9 +792,11 @@ check_param(char *s, int set, int test) * global variables. */ if (set) { - if (br >= 2) + if (br >= 2) { mflags |= CMF_PARBR; - + if (nest) + mflags |= CMF_PARNEST; + } /* Get the prefix (anything up to the character before the name). */ isuf = dupstring(e); untokenize(isuf); @@ -2542,9 +2548,12 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test) lm = NULL; } else { /* No matcher and different characters: l does not match w. */ + if (test) + return 0; + abort_match(); - return (test ? 0 : -1); + return -1; } } /* If this is a recursive call, we just return if l matched w or not. */ @@ -2600,7 +2609,7 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test) * and the suffix don't match the word w. */ static char * -comp_match(char *pfx, char *sfx, char *w, Comp cp, +comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, int *bpl, int *bsl, int *exact) { char *r = NULL; @@ -2610,7 +2619,7 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp, int wl; r = w; - if (!domatch(r, cp, 0)) + if (!pattry(cp, r)) return NULL; r = (qu ? quotename(r, NULL) : dupstring(r)); @@ -2868,7 +2877,8 @@ join_strs(int la, char *sa, int lb, char *sb) *ap += mp->wlen; *alp -= mp->wlen; *bp += bl; *blp -= bl; t = 1; - } + } else + t = 0; } } } @@ -2907,7 +2917,7 @@ cmp_anchors(Cline o, Cline n, int join) /* First try the exact strings. */ if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen && (!o->word || !strncmp(o->word, n->word, o->wlen))) || - (line = ((!o->line && !n->line) || + (line = ((!o->line && !n->line && !o->wlen && !n->wlen) || (o->llen == n->llen && o->line && n->line && !strncmp(o->line, n->line, o->llen))))) { if (line) { @@ -3419,6 +3429,11 @@ join_clines(Cline o, Cline n) } /* Now see if they have matching anchors. If not, cut the list. */ if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) { +#if 0 + /* This used to search forward for matching anchors. + * Unfortunately this does the wrong thing if the prefixes + * before the differing anchors match nicely. */ + Cline t, tn; for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn); @@ -3427,6 +3442,7 @@ join_clines(Cline o, Cline n) n = tn; continue; } else { +#endif if (o->flags & CLF_SUF) break; @@ -3434,7 +3450,10 @@ join_clines(Cline o, Cline n) o->wlen = 0; free_cline(o->next); o->next = NULL; + o->flags |= CLF_MISS; +#if 0 } +#endif } /* Ok, they are equal, now join the sub-lists. */ if (o->flags & CLF_MID) @@ -3726,7 +3745,7 @@ int addmatches(Cadata dat, char **argv) { char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; - char **aign = NULL, **dparr = NULL, oaq = autoq; + char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre; char *oqp = qipre, *oqs = qisuf, qc; int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum; int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt; @@ -3734,7 +3753,7 @@ addmatches(Cadata dat, char **argv) Cmatch cm; struct cmlist mst; Cmlist oms = mstack; - Comp cp = NULL; + Patprog cp = NULL; LinkList aparl = NULL, oparl = NULL, dparl = NULL; if (compquote && (qc = *compquote)) { @@ -3798,13 +3817,8 @@ addmatches(Cadata dat, char **argv) /* Get the contents of the completion variables if we have * to perform matching. */ if (dat->aflags & CAF_MATCH) { - if (dat->aflags & CAF_QUOTE) { - lipre = dupstring(compiprefix); - lisuf = dupstring(compisuffix); - } else { - lipre = quotename(compiprefix, NULL); - lisuf = quotename(compisuffix, NULL); - } + lipre = dupstring(compiprefix); + lisuf = dupstring(compisuffix); lpre = dupstring(compprefix); lsuf = dupstring(compsuffix); llpl = strlen(lpre); @@ -3830,7 +3844,7 @@ addmatches(Cadata dat, char **argv) if (haswilds(tmp)) { if (is) tmp[llpl] = Star; - if ((cp = parsereg(tmp))) + if ((cp = patcompile(tmp, 0, NULL))) haspattern = 1; } } @@ -3847,12 +3861,21 @@ addmatches(Cadata dat, char **argv) else if (lisuf) dat->isuf = lisuf; if (dat->ppre) { - dat->ppre = dupstring(dat->ppre); + if (!(dat->aflags & CAF_QUOTE)) { + dat->ppre = quotename(dat->ppre, NULL); + if ((dat->flags & CMF_FILE) && + dat->ppre[0] == '\\' && dat->ppre[1] == '~') + chuck(dat->ppre); + } else + dat->ppre = dupstring(dat->ppre); lpl = strlen(dat->ppre); } else lpl = 0; if (dat->psuf) { - dat->psuf = dupstring(dat->psuf); + if (!(dat->aflags & CAF_QUOTE)) + dat->psuf = quotename(dat->psuf, NULL); + else + dat->psuf = dupstring(dat->psuf); lsl = strlen(dat->psuf); } else lsl = 0; @@ -3877,7 +3900,7 @@ addmatches(Cadata dat, char **argv) dat->pre = dupstring(dat->pre); if (dat->suf) dat->suf = dupstring(dat->suf); - if (!dat->prpre && (dat->prpre = dat->ppre)) { + if (!dat->prpre && (dat->prpre = oppre)) { singsub(&(dat->prpre)); untokenize(dat->prpre); } else @@ -3901,22 +3924,6 @@ addmatches(Cadata dat, char **argv) dat->rems = NULL; } else if (dat->rems) dat->rems = dupstring(dat->rems); - - /* Probably quote the prefix and suffix for testing. */ - if (!(dat->aflags & CAF_QUOTE)) { - if (!cp && (dat->aflags & CAF_MATCH)) { - lpre = quotename(lpre, NULL); - lsuf = quotename(lsuf, NULL); - } - if (dat->ppre) { - dat->ppre = quotename(dat->ppre, NULL); - if ((dat->flags & CMF_FILE) && - dat->ppre[0] == '\\' && dat->ppre[1] == '~') - chuck(dat->ppre); - } - if (dat->psuf) - dat->psuf = quotename(dat->psuf, NULL); - } } /* Walk through the matches given. */ for (; (s = *argv); argv++) { @@ -4340,7 +4347,7 @@ gen_matches_files(int dirs, int execs, int all) addwhat = execs ? -8 : -5; if (filecomp) /* If we have a pattern for the filename check, use it. */ - test = domatch(n, filecomp, 0); + test = pattry(filecomp, n); else { /* Otherwise use the prefix and suffix strings directly. */ e = n + strlen(n) - fsl; @@ -5024,8 +5031,6 @@ comp_str(int *ipl, int *pl, int untok) remnulargs(p); ctokenize(s); remnulargs(s); - ctokenize(ip); - remnulargs(ip); } lp = strlen(p); ls = strlen(s); @@ -5563,14 +5568,14 @@ static int makecomplistpc(char *os, int incmd) { Patcomp pc; - Comp pat; + Patprog pat; char *s = findcmd(cmdstr, 1); int ret = 0; for (pc = patcomps; pc; pc = pc->next) { - if ((pat = parsereg(pc->pat)) && - (domatch(cmdstr, pat, 0) || - (s && domatch(s, pat, 0)))) { + if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) && + (pattry(pat, cmdstr) || + (s && pattry(pat, s)))) { makecomplistcc(pc->cc, os, incmd); ret |= 2; if (!(ccont & CC_CCCONT)) @@ -5666,7 +5671,7 @@ makecomplistext(Compctl occ, char *os, int incmd) { Compctl compc; Compcond or, cc; - Comp comp; + Patprog pprog; int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins; char *sc = NULL, *s, *ss; @@ -5752,8 +5757,8 @@ makecomplistext(Compctl occ, char *os, int incmd) if (cc->type == CCT_CURPAT || cc->type == CCT_WORDPAT) { tokenize(ss = dupstring(cc->u.s.s[i])); - t = ((comp = parsereg(ss)) && - domatch(s, comp, 0)); + t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) && + pattry(pprog, s)); } else t = (!strcmp(s, rembslash(cc->u.s.s[i]))); break; @@ -5767,8 +5772,8 @@ makecomplistext(Compctl occ, char *os, int incmd) sc = rembslash(cc->u.l.a[i]); if (cc->type == CCT_RANGESTR ? !strncmp(s, sc, strlen(sc)) : - ((comp = parsereg(sc)) && - domatch(s, comp, 0))) { + ((pprog = patcompile(sc, PAT_STATIC, 0)) && + pattry(pprog, s))) { zsfree(s); brange = j + 1; t = 1; @@ -5785,8 +5790,8 @@ makecomplistext(Compctl occ, char *os, int incmd) sc = rembslash(cc->u.l.b[i]); if (cc->type == CCT_RANGESTR ? !strncmp(s, sc, strlen(sc)) : - ((comp = parsereg(sc)) && - domatch(s, comp, 0))) { + ((pprog = patcompile(sc, PAT_STATIC, 0)) && + pattry(pprog, s))) { zsfree(s); erange = j - 1; t = clwpos <= erange; @@ -6050,7 +6055,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) strcpy(p + rpl + 1, rsuf); } else strcpy(p + rpl, rsuf); - patcomp = parsereg(p); + patcomp = patcompile(p, 0, NULL); haspattern = 1; } if (!patcomp) { @@ -6148,7 +6153,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) (!comppatmatch || *comppatmatch == '*')) p[t2++] = Star; strcpy(p + t2, fsuf); - filecomp = parsereg(p); + filecomp = patcompile(p, 0, NULL); } if (!filecomp) { untokenize(fpre); @@ -6567,7 +6572,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } if (cc->hpat) { /* We have a pattern to take things from the history. */ - Comp compc = NULL; + Patprog pprogc = NULL; char *e, *h, hpatsav; Histent he; int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum; @@ -6577,7 +6582,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) char *thpat = dupstring(cc->hpat); tokenize(thpat); - compc = parsereg(thpat); + pprogc = patcompile(thpat, 0, NULL); } /* n holds the number of history line we have to search. */ if (!n) @@ -6594,7 +6599,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* We now have a word from the history, ignore it * * if it begins with a quote or `$'. */ if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' && - (!compc || domatch(h, compc, 0))) + (!pprogc || pattry(pprogc, h))) /* Otherwise add it if it was matched. */ addmatch(dupstring(h), NULL); if (hpatsav) @@ -7729,6 +7734,8 @@ do_single(Cmatch m) minfo.insc++; if (minfo.we) minfo.end += minfo.insc; + if (m->flags & CMF_PARNEST) + havesuff = 1; } if ((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) { /* If we have a filename or we completed a parameter name * @@ -7742,11 +7749,12 @@ do_single(Cmatch m) t = 1; else { /* Build the path name. */ - if (m->ripre && !*psuf) { + if (m->ripre && !*psuf && !(m->flags & CMF_PARNEST)) { int ne = noerrs; - p = (char *) zhalloc(strlen(m->ripre) + strlen(str) + 1); - sprintf(p, "%s%s", m->ripre, str); + p = (char *) zhalloc(strlen(m->ripre) + strlen(str) + 2); + sprintf(p, "%s%s%c", m->ripre, str, + ((m->flags & CMF_PARBR) ? Outbrace : '\0')); noerrs = 1; parsestr(p); singsub(&p); diff --git a/Src/builtin.c b/Src/builtin.c index 5c6b24601..6a2ec8335 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -86,6 +86,10 @@ static struct builtin builtins[] = BUILTIN("mem", 0, bin_mem, 0, 0, 0, "v", NULL), #endif +#if defined(ZSH_PAT_DEBUG) + BUILTIN("patdebug", 0, bin_patdebug, 1, -1, 0, "p", NULL), +#endif + BUILTIN("popd", 0, bin_cd, 0, 2, BIN_POPD, NULL, NULL), BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrslzNu0123456789pioOcm-", NULL), BUILTIN("pushd", 0, bin_cd, 0, 2, BIN_PUSHD, NULL, NULL), @@ -377,7 +381,7 @@ bin_enable(char *name, char **argv, char *ops, int func) HashTable ht; HashNode hn; ScanFunc scanfunc; - Comp com; + Patprog pprog; int flags1 = 0, flags2 = 0; int match = 0, returnval = 0; @@ -414,8 +418,8 @@ bin_enable(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* parse pattern */ tokenize(*argv); - if ((com = parsereg(*argv))) - match += scanmatchtable(ht, com, 0, 0, scanfunc, 0); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) + match += scanmatchtable(ht, pprog, 0, 0, scanfunc, 0); else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -1174,7 +1178,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) int first = -1, last = -1, retval, minflag = 0; char *s; struct asgment *asgf = NULL, *asgl = NULL; - Comp com = NULL; + Patprog pprog = NULL; /* fc is only permitted in interactive shells */ if (!interact) { @@ -1185,7 +1189,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) * as a pattern that history lines have to match */ if (*argv && ops['m']) { tokenize(*argv); - if (!(com = parsereg(*argv++))) { + if (!(pprog = patcompile(*argv++, 0, NULL))) { zwarnnam(nam, "invalid match pattern", NULL, 0); return 1; } @@ -1257,7 +1261,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) /* list the required part of the history */ retval = fclist(stdout, !ops['n'], ops['r'], ops['D'], ops['d'] + ops['f'] * 2 + ops['E'] * 4 + ops['i'] * 8, - first, last, asgf, com); + first, last, asgf, pprog); else { /* edit history file, and (if successful) use the result as a new command */ int tempfd; @@ -1271,7 +1275,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) ((out = fdopen(tempfd, "w")) == NULL)) { zwarnnam("fc", "can't open temp file: %e", NULL, errno); } else { - if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, com)) { + if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, pprog)) { char *editor; editor = auxdata ? auxdata : getsparam("FCEDIT"); @@ -1372,7 +1376,7 @@ fcsubs(char **sp, struct asgment *sub) /**/ static int -fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Comp com) +fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Patprog pprog) { int fclistdone = 0; char *s; @@ -1400,7 +1404,7 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment for (;;) { s = dupstring(ent->text); /* this if does the pattern matching, if required */ - if (!com || domatch(s, com, 0)) { + if (!pprog || pattry(pprog, s)) { /* perform substitution */ fclistdone |= fcsubs(&s, subs); @@ -1775,7 +1779,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) { Param pm; Asgment asg; - Comp com; + Patprog pprog; char *optstr = "aiALRZlurtxUT"; int on = 0, off = 0, roff, bit = PM_ARRAY; int i; @@ -1914,7 +1918,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) LinkNode pmnode; tokenize(asg->name); /* expand argument */ - if (!(com = parsereg(asg->name))) { + if (!(pprog = patcompile(asg->name, 0, NULL))) { untokenize(asg->name); zwarnnam(name, "bad pattern : %s", argv[-1], 0); returnval = 1; @@ -1934,7 +1938,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) if (((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) || (pm->flags & PM_UNSET)) continue; - if (domatch(pm->nam, com, 0)) + if (pattry(pprog, pm->nam)) addlinknode(pmlist, pm); } } @@ -1974,7 +1978,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) int bin_functions(char *name, char **argv, char *ops, int func) { - Comp com; + Patprog pprog; Shfunc shf; int i, returnval = 0; int on = 0, off = 0, pflags = 0; @@ -2018,16 +2022,18 @@ bin_functions(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* expand argument */ tokenize(*argv); - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { /* with no options, just print all functions matching the glob pattern */ if (!(on|off)) { - scanmatchtable(shfunctab, com, 0, DISABLED, + scanmatchtable(shfunctab, pprog, 0, DISABLED, shfunctab->printnode, pflags); } else { /* apply the options to all functions matching the glob pattern */ for (i = 0; i < shfunctab->hsize; i++) { - for (shf = (Shfunc) shfunctab->nodes[i]; shf; shf = (Shfunc) shf->next) - if (domatch(shf->nam, com, 0) && !(shf->flags & DISABLED)) + for (shf = (Shfunc) shfunctab->nodes[i]; shf; + shf = (Shfunc) shf->next) + if (pattry(pprog, shf->nam) && + !(shf->flags & DISABLED)) shf->flags = (shf->flags | on) & (~off); } } @@ -2097,7 +2103,7 @@ int bin_unset(char *name, char **argv, char *ops, int func) { Param pm, next; - Comp com; + Patprog pprog; char *s; int match = 0, returnval = 0; int i; @@ -2111,14 +2117,15 @@ bin_unset(char *name, char **argv, char *ops, int func) while ((s = *argv++)) { /* expand */ tokenize(s); - if ((com = parsereg(s))) { + if ((pprog = patcompile(s, PAT_STATIC, NULL))) { /* Go through the parameter table, and unset any matches */ for (i = 0; i < paramtab->hsize; i++) { for (pm = (Param) paramtab->nodes[i]; pm; pm = next) { /* record pointer to next, since we may free this one */ next = (Param) pm->next; if ((!(pm->flags & PM_RESTRICTED) || - unset(RESTRICTED)) && domatch(pm->nam, com, 0)) { + unset(RESTRICTED)) && + pattry(pprog, pm->nam)) { unsetparam_pm(pm, 0, 1); match++; } @@ -2184,7 +2191,7 @@ int bin_whence(char *nam, char **argv, char *ops, int func) { HashNode hn; - Comp com; + Patprog pprog; int returnval = 0; int printflags = 0; int csh, all, v, wd; @@ -2213,7 +2220,7 @@ bin_whence(char *nam, char **argv, char *ops, int func) for (; *argv; argv++) { /* parse the pattern */ tokenize(*argv); - if (!(com = parsereg(*argv))) { + if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) { untokenize(*argv); zwarnnam(nam, "bad pattern : %s", *argv, 0); returnval = 1; @@ -2224,21 +2231,26 @@ bin_whence(char *nam, char **argv, char *ops, int func) * We're not using it, so search for ... */ /* aliases ... */ - scanmatchtable(aliastab, com, 0, DISABLED, aliastab->printnode, printflags); + scanmatchtable(aliastab, pprog, 0, DISABLED, + aliastab->printnode, printflags); /* and reserved words ... */ - scanmatchtable(reswdtab, com, 0, DISABLED, reswdtab->printnode, printflags); + scanmatchtable(reswdtab, pprog, 0, DISABLED, + reswdtab->printnode, printflags); /* and shell functions... */ - scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, printflags); + scanmatchtable(shfunctab, pprog, 0, DISABLED, + shfunctab->printnode, printflags); /* and builtins. */ - scanmatchtable(builtintab, com, 0, DISABLED, builtintab->printnode, printflags); + scanmatchtable(builtintab, pprog, 0, DISABLED, + builtintab->printnode, printflags); } /* Done search for `internal' commands, if the -p option * * was not used. Now search the path. */ cmdnamtab->filltable(cmdnamtab); - scanmatchtable(cmdnamtab, com, 0, 0, cmdnamtab->printnode, printflags); + scanmatchtable(cmdnamtab, pprog, 0, 0, + cmdnamtab->printnode, printflags); } return returnval; @@ -2367,7 +2379,7 @@ int bin_hash(char *name, char **argv, char *ops, int func) { HashTable ht; - Comp com; + Patprog pprog; Asgment asg; int returnval = 0; @@ -2405,9 +2417,9 @@ bin_hash(char *name, char **argv, char *ops, int func) if (ops['m']) { /* with the -m option, treat the argument as a glob pattern */ tokenize(*argv); /* expand */ - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display matching hash table elements */ - scanmatchtable(ht, com, 0, 0, ht->printnode, 0); + scanmatchtable(ht, pprog, 0, 0, ht->printnode, 0); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -2464,7 +2476,7 @@ bin_unhash(char *name, char **argv, char *ops, int func) { HashTable ht; HashNode hn, nhn; - Comp com; + Patprog pprog; int match = 0, returnval = 0; int i; @@ -2484,13 +2496,13 @@ bin_unhash(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* expand argument */ tokenize(*argv); - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* remove all nodes matching glob pattern */ for (i = 0; i < ht->hsize; i++) { for (hn = ht->nodes[i]; hn; hn = nhn) { /* record pointer to next, since we may free this one */ nhn = hn->next; - if (domatch(hn->nam, com, 0)) { + if (pattry(pprog, hn->nam)) { ht->freenode(ht->removenode(ht, hn->nam)); match++; } @@ -2529,7 +2541,7 @@ int bin_alias(char *name, char **argv, char *ops, int func) { Alias a; - Comp com; + Patprog pprog; Asgment asg; int haveflags = 0, returnval = 0; int flags1 = 0, flags2 = DISABLED; @@ -2565,9 +2577,10 @@ bin_alias(char *name, char **argv, char *ops, int func) if (ops['m']) { for (; *argv; argv++) { tokenize(*argv); /* expand argument */ - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display the matching aliases */ - scanmatchtable(aliastab, com, flags1, flags2, aliastab->printnode, printflags); + scanmatchtable(aliastab, pprog, flags1, flags2, + aliastab->printnode, printflags); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -2636,17 +2649,17 @@ bin_print(char *name, char **args, char *ops, int func) /* -m option -- treat the first argument as a pattern and remove * arguments not matching */ if (ops['m']) { - Comp com; + Patprog pprog; char **t, **p; tokenize(*args); - if (!(com = parsereg(*args))) { + if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) { untokenize(*args); zwarnnam(name, "bad pattern : %s", *args, 0); return 1; } for (p = ++args; *p; p++) - if (!domatch(*p, com, 0)) + if (!pattry(pprog, *p)) for (t = p--; (*t = t[1]); t++); } /* compute lengths, and interpret according to -P, -D, -e, etc. */ diff --git a/Src/cond.c b/Src/cond.c index 98deea2bf..a4b652fee 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -303,12 +303,13 @@ optison(char *s) /**/ char * -cond_str(char **args, int num) +cond_str(char **args, int num, int raw) { char *s = args[num]; singsub(&s); - untokenize(s); + if (!raw) + untokenize(s); return s; } diff --git a/Src/glob.c b/Src/glob.c index 20ca30b2e..b8c535a6f 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -76,12 +76,14 @@ struct gmatch { /**/ int badcshglob; -static int mode; /* != 0 if we are parsing glob patterns */ -static int scanning; /* != 0 if we are scanning file paths */ -static int pathpos; /* position in pathbuf */ +/**/ +int pathpos; /* position in pathbuf (needed by pattern code) */ + +/**/ +char *pathbuf; /* pathname buffer (needed by pattern code) */ + static int matchsz; /* size of matchbuf */ static int matchct; /* number of matches found */ -static char *pathbuf; /* pathname buffer */ static int pathbufsz; /* size of pathbuf */ static int pathbufcwd; /* where did we chdir()'ed */ static Gmatch matchbuf; /* array of matches */ @@ -135,40 +137,10 @@ char *glob_pre, *glob_suf; struct complist { Complist next; - Comp comp; + Patprog pat; int closure; /* 1 if this is a (foo/)# */ int follow; /* 1 to go thru symlinks */ }; -struct comp { - Comp left, right, next, exclude; - char *str; - int stat, errsmax; -}; - -/* Type of Comp: a closure with one or two #'s, the end of a * - * pattern or path component, a piece of path to be added. */ -#define C_ONEHASH 1 -#define C_TWOHASH 2 -#define C_OPTIONAL 4 -#define C_STAR 8 -#define C_CLOSURE (C_ONEHASH|C_TWOHASH|C_OPTIONAL|C_STAR) -#define C_LAST 16 -#define C_PATHADD 32 -#define C_LCMATCHUC 64 -#define C_IGNCASE 128 - -/* Test macros for the above */ -#define CLOSUREP(c) (c->stat & C_CLOSURE) -#define ONEHASHP(c) (c->stat & (C_ONEHASH|C_STAR)) -#define TWOHASHP(c) (c->stat & C_TWOHASH) -#define OPTIONALP(c) (c->stat & C_OPTIONAL) -#define STARP(c) (c->stat & C_STAR) -#define LASTP(c) (c->stat & C_LAST) -#define PATHADDP(c) (c->stat & C_PATHADD) - -/* Flags passed down to guts when compiling */ -#define GF_PATHADD 1 /* file glob, adding path components */ -#define GF_TOPLEV 2 /* outside (), so ~ ends main match */ /* Next character after one which may be a Meta (x is any char *) */ #define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) @@ -182,12 +154,6 @@ struct comp { */ #define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) -static char *pptr; /* current place in string being matched */ -static Comp tail; -static int first; /* are leading dots special? */ -static int longest; /* always match longest piece of path. */ -static int inclosure; /* see comment in doesmatch() */ - /* Add a component to pathbuf: This keeps track of how * * far we are into a file name, since each path component * * must be matched separately. */ @@ -377,25 +343,6 @@ haswilds(char *str) return 0; } -/* Flags to apply to current level of grouping */ - -static int addflags; - -/* - * Approximate matching. - * - * errsmax is used while parsing to store the current number - * of errors allowed. While executing it is usually set to -1, - * but can be set to something else to fix a different maximum - * no. of errors which acts as a limit on the local value: see - * scanner() for why this is necessary. - * - * errsfound is used while executing a pattern to record the - * number of errors found so far. - */ - -static int errsmax, errsfound; - /* Do the globbing: scanner is called recursively * * with successive bits of the path until we've * * tried all of it. */ @@ -404,12 +351,11 @@ static int errsmax, errsfound; static void scanner(Complist q) { - Comp c; + Patprog p; int closure; int pbcwdsav = pathbufcwd; int errssofar = errsfound; struct dirsav ds; - char *str; ds.ino = ds.dev = 0; ds.dirname = NULL; @@ -424,19 +370,14 @@ scanner(Complist q) else scanner(q->next); } - c = q->comp; - str = c->str; + p = q->pat; /* Now the actual matching for the current path section. */ - if (!(c->next || c->left) && !haswilds(str) - && (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax) - || !*str || !strcmp(".", str) || !strcmp("..", str))) { + if (p->flags & PAT_PURES) { /* - * We always need to match . and .. explicitly, even if we're - * checking other strings for case-insensitive matches. - * * It's a straight string to the end of the path section. */ - int l = strlen(str); + char *str = (char *)p + p->startoff; + int l = p->patmlen; if (l + !l + pathpos - pathbufcwd >= PATH_MAX) { int err; @@ -481,7 +422,7 @@ scanner(Complist q) || (glob_suf && !strsfx(glob_suf, fn)))) continue; errsfound = errssofar; - if (domatch(fn, c, gf_noglobdots)) { + if (pattry(p, fn)) { /* if this name matchs the pattern... */ if (pbcwdsav == pathbufcwd && strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) { @@ -505,8 +446,6 @@ scanner(Complist q) * If not the last component in the path: * * If we made an approximation in the new path segment, - * and the pattern includes closures other than a - * trailing * (we only test if it is not a string), * then it is possible we made too many errors. For * example, (ab)#(cb)# will match the directory abcb * with one error if allowed to, even though it can @@ -518,16 +457,16 @@ scanner(Complist q) * in the last part of the path which is not affected * by this problem. */ - if (errsfound > errssofar && (c->next || c->left)) { - errsmax = errsfound - 1; - while (errsmax >= errssofar) { + if (errsfound > errssofar) { + forceerrs = errsfound - 1; + while (forceerrs >= errssofar) { errsfound = errssofar; - if (!domatch(fn, c, gf_noglobdots)) + if (!pattry(p, fn)) break; - errsmax = errsfound - 1; + forceerrs = errsfound - 1; } - errsfound = errsmax + 1; - errsmax = -1; + errsfound = forceerrs + 1; + forceerrs = -1; } if (closure) { /* if matching multiple directories */ @@ -583,495 +522,72 @@ scanner(Complist q) } } -/* Parse a series of path components pointed to by pptr */ - -/**/ -static Comp -compalloc(void) -{ - Comp c = (Comp) alloc(sizeof *c); - c->stat |= addflags; - c->errsmax = errsmax; - return c; -} - -/**/ -static int -getglobflags(void) -{ - char *nptr; - /* (#X): assumes we are still positioned on the initial '(' */ - pptr++; - while (*++pptr && *pptr != Outpar) { - switch (*pptr) { - case 'a': - /* Approximate matching, max no. of errors follows */ - errsmax = zstrtol(++pptr, &nptr, 10); - if (pptr == nptr) - return 1; - pptr = nptr-1; - break; - - case 'l': - /* Lowercase in pattern matches lower or upper in target */ - addflags |= C_LCMATCHUC; - break; - - case 'i': - /* Fully case insensitive */ - addflags |= C_IGNCASE; - break; - - case 'I': - /* Restore case sensitivity */ - addflags &= ~(C_LCMATCHUC|C_IGNCASE); - break; - - default: - return 1; - } - } - if (*pptr != Outpar) - return 1; - pptr++; - return 0; -} - -/**/ -static void -parse_charset(void) -{ - /* Character set: brackets had better match */ - if (pptr[1] == Outbrack) - *++pptr = ']'; - else if ((pptr[1] == Hat || pptr[1] == '^' || pptr[1] == '!') && - pptr[2] == Outbrack) - *(pptr += 2) = ']'; - while (*++pptr && *pptr != Outbrack) { - if (itok(*pptr)) { - /* POSIX classes: make sure it's a real one, - * leave the Inbrack tokenised if so. - * We need to untokenize the Outbrack since otherwise - * it might look like we got to the end of the range without - * matching; we also need to accept ']' instead of - * Outbrack in case this has already happened. - */ - char *nptr; - if (*pptr == Inbrack && pptr[1] == ':' - && (nptr = strchr(pptr+2, ':')) && - (*++nptr == Outbrack || *nptr == ']')) - *(pptr = nptr) = ']'; - else - *pptr = ztokens[*pptr - Pound]; - } - } -} - -/* enum used with ksh-like patterns, @(...) etc. */ - -enum { KF_NONE, KF_AT, KF_QUEST, KF_STAR, KF_PLUS, KF_NOT }; - -/* parse lowest level pattern */ - -/**/ -static Comp -parsecomp(int gflag) -{ - int kshfunc; - Comp c = compalloc(), c1, c2; - char *cstr, *ls = NULL; - - /* In case of alternatives, code coming up is stored in tail. */ - c->next = tail; - cstr = pptr; - - while (*pptr && (mode || *pptr != '/') && *pptr != Bar && - (unset(EXTENDEDGLOB) || *pptr != Tilde || - !pptr[1] || pptr[1] == Outpar || pptr[1] == Bar) && - *pptr != Outpar) { - /* Go through code until we find something separating alternatives, - * or path components if relevant. - */ - if (*pptr == Hat && isset(EXTENDEDGLOB)) { - /* negate remaining pattern */ - Comp stail = tail; - tail = NULL; - c->str = dupstrpfx(cstr, pptr - cstr); - pptr++; - - c1 = compalloc(); - c1->stat |= C_STAR; - - c2 = compalloc(); - if (!(c2->exclude = parsecomp(gflag))) - return NULL; - if (!*pptr || *pptr == '/') - c2->stat |= C_LAST; - c2->left = c1; - c2->next = stail; - c->next = c2; - tail = stail; - return c; - } - - /* Ksh-type globs */ - kshfunc = KF_NONE; - if (isset(KSHGLOB) && *pptr && pptr[1] == Inpar) { - switch (*pptr) { - case '@': /* just do paren as usual */ - kshfunc = KF_AT; - break; - - case Quest: - case '?': /* matched optionally, treat as (...|) */ - kshfunc = KF_QUEST; - break; - - case Star: - case '*': /* treat as (...)# */ - kshfunc = KF_STAR; - break; - - case '+': /* treat as (...)## */ - kshfunc = KF_PLUS; - break; - - case '!': /* treat as (*~...) */ - kshfunc = KF_NOT; - break; - } - if (kshfunc != KF_NONE) - pptr++; - } - - if (*pptr == Inpar && pptr[1] == Pound && isset(EXTENDEDGLOB)) { - /* Found some globbing flags */ - char *eptr = pptr; - if (kshfunc != KF_NONE) - eptr--; - if (getglobflags()) - return NULL; - if (eptr == cstr) { - /* if no string yet, carry on and get one. */ - c->stat = addflags; - c->errsmax = errsmax; - cstr = pptr; - continue; - } - c->str = dupstrpfx(cstr, eptr - cstr); - /* - * The next bit simply handles the case where . or .. - * is followed by a set of flags, but we need to force - * them to be handled as a string. Hardly worth it. - */ - if (!*pptr || (!mode && *pptr == '/') || *pptr == Bar || - (isset(EXTENDEDGLOB) && *pptr == Tilde && - pptr[1] && pptr[1] != Outpar && pptr[1] != Bar) || - *pptr == Outpar) { - if (*pptr == '/' || !*pptr || - ((*pptr == Bar || - (isset(EXTENDEDGLOB) && *pptr == Tilde)) && - (gflag & GF_TOPLEV))) - c->stat |= C_LAST; - return c; - } - if (!(c->next = parsecomp(gflag))) - return NULL; - return c; - } - if (*pptr == Inpar) { - /* Found a group (...) */ - char *startp = pptr, *endp; - Comp stail = tail; - int dpnd = 0; - - /* Need matching close parenthesis */ - if (skipparens(Inpar, Outpar, &pptr)) { - errflag = 1; - return NULL; - } - if (kshfunc == KF_STAR) - dpnd = 1; - else if (kshfunc == KF_PLUS) - dpnd = 2; - else if (kshfunc == KF_QUEST) - dpnd = 3; - if (*pptr == Pound && isset(EXTENDEDGLOB)) { - /* Zero (or one) or more repetitions of group */ - pptr++; - if(*pptr == Pound) { - pptr++; - if(dpnd == 0) - dpnd = 2; - else if(dpnd == 3) - dpnd = 1; - } else - dpnd = 1; - } - /* Parse the remaining pattern following the group... */ - if (!(c1 = parsecomp(gflag))) - return NULL; - /* ...remembering what comes after it... */ - tail = (dpnd || kshfunc == KF_NOT) ? NULL : c1; - /* ...before going back and parsing inside the group. */ - endp = pptr; - pptr = startp; - c->str = dupstrpfx(cstr, (pptr - cstr) - (kshfunc != KF_NONE)); - pptr++; - c2 = compalloc(); - c->next = c2; - c2->next = (dpnd || kshfunc == KF_NOT) ? - c1 : compalloc(); - if (!(c2->left = parsecompsw(0))) - return NULL; - if (kshfunc == KF_NOT) { - /* we'd actually rather it didn't match. Instead, match * - * a star and put the parsed pattern into exclude. */ - Comp c3 = compalloc(); - c3->stat |= C_STAR; - - c2->exclude = c2->left; - c2->left = c3; - } - /* Remember closures for group. */ - if (dpnd) - c2->stat |= (dpnd == 3) ? C_OPTIONAL - : (dpnd == 2) ? C_TWOHASH : C_ONEHASH; - pptr = endp; - tail = stail; - return c; - } - if (*pptr == Star && pptr[1] && - (unset(EXTENDEDGLOB) || !(gflag & GF_TOPLEV) || - pptr[1] != Tilde || !pptr[2] || pptr[2] == Bar || - pptr[2] == Outpar) && (mode || pptr[1] != '/')) { - /* Star followed by other patterns is now treated as a special - * type of closure in doesmatch(). - */ - c->str = dupstrpfx(cstr, pptr - cstr); - pptr++; - c1 = compalloc(); - c1->stat |= C_STAR; - if (!(c2 = parsecomp(gflag))) - return NULL; - c1->next = c2; - c->next = c1; - return c; - } - if (*pptr == Pound && isset(EXTENDEDGLOB)) { - /* repeat whatever we've just had (ls) zero or more times */ - if (!ls) - return NULL; - c2 = compalloc(); - c2->str = dupstrpfx(ls, pptr - ls); - pptr++; - if (*pptr == Pound) { - /* need one or more matches: cheat by copying previous char */ - pptr++; - c->next = c1 = compalloc(); - c1->str = c2->str; - } else - c1 = c; - c1->next = c2; - c2->stat |= C_ONEHASH; - /* parse the rest of the pattern and return. */ - c2->next = parsecomp(gflag); - if (!c2->next) - return NULL; - c->str = dupstrpfx(cstr, ls - cstr); - return c; - } - ls = pptr; /* whatever we just parsed */ - if (*pptr == Inang) { - /* Numeric glob */ - int dshct; - - dshct = (pptr[1] == Outang); - while (*++pptr && *pptr != Outang) - if (*pptr == '-' && !dshct) - dshct = 1; - else if (!idigit(*pptr)) - break; - if (*pptr != Outang) - return NULL; - } else if (*pptr == Inbrack) { - parse_charset(); - if (*pptr != Outbrack) - return NULL; - } else if (itok(*pptr) && *pptr != Star && *pptr != Quest) - /* something that can be tokenised which isn't otherwise special */ - *pptr = ztokens[*pptr - Pound]; - pptr++; - } - /* mark if last pattern component in path component or pattern */ - if (*pptr == '/' || !*pptr || - ((*pptr == Bar || - (isset(EXTENDEDGLOB) && *pptr == Tilde)) && (gflag & GF_TOPLEV))) - c->stat |= C_LAST; - c->str = dupstrpfx(cstr, pptr - cstr); - return c; -} - -/* Parse pattern possibly with different alternatives (|) */ - -/**/ -static Comp -parsecompsw(int gflag) -{ - Comp c1, c2, c3, excl = NULL, stail = tail; - int oaddflags = addflags; - int oerrsmax = errsmax; - char *sptr; - - /* - * Check for a tilde in the expression. We need to know this in - * advance so as to be able to treat the whole a~b expression by - * backtracking: see exclusion code in doesmatch(). - */ - if (isset(EXTENDEDGLOB)) { - int pct = 0; - for (sptr = pptr; *sptr; sptr++) { - if (*sptr == Inpar) - pct++; - else if (*sptr == Outpar && --pct < 0) - break; - else if (*sptr == Bar && !pct) - break; - else if (*sptr == Inbrack) { - /* - * Character classes can have tokenized characters in, - * so we have to parse them properly. - */ - char *bstart = pptr; - - pptr = sptr; - parse_charset(); - sptr = pptr; - pptr = bstart; - if (*sptr != Outbrack) - break; - } else if (*sptr == Tilde && !pct) { - tail = NULL; - break; - } - } - } - - c1 = parsecomp(gflag); - if (!c1) - return NULL; - if (isset(EXTENDEDGLOB) && *pptr == Tilde) { - /* Matching remainder of pattern excludes the pattern from matching */ - int oldmode = mode, olderrsmax = errsmax; - - mode = 1; - errsmax = 0; - pptr++; - excl = parsecomp(gflag); - mode = oldmode; - errsmax = olderrsmax; - if (!excl) - return NULL; - } - tail = stail; - if (*pptr == Bar || excl) { - /* found an alternative or something to exclude */ - c2 = compalloc(); - if (*pptr == Bar) { - /* get the next alternative after the | */ - pptr++; - c3 = parsecompsw(gflag); - if (!c3) - return NULL; - } else - c3 = NULL; - /* mark if end of pattern or path component */ - if (!*pptr || *pptr == '/') - c1->stat |= c2->stat = C_LAST; - c2->str = dupstring(""); - c2->left = c1; - c2->right = c3; - if ((c2->exclude = excl)) - c2->next = stail; - if (gflag & GF_PATHADD) - c2->stat |= C_PATHADD; - c1 = c2; - } - if (!(gflag & GF_TOPLEV)) { - addflags = oaddflags; - errsmax = oerrsmax; - } - return c1; -} - /* This function tokenizes a zsh glob pattern */ /**/ static Complist -parsecomplist(void) +parsecomplist(char *instr) { - Comp c1; - Complist p1; + Patprog p1; + Complist l1; char *str; + int compflags = gf_noglobdots ? (PAT_FILE|PAT_NOGLD) : PAT_FILE; - if (pptr[0] == Star && pptr[1] == Star && - (pptr[2] == '/' || (pptr[2] == Star && pptr[3] == '/'))) { + if (instr[0] == Star && instr[1] == Star && + (instr[2] == '/' || (instr[2] == Star && instr[3] == '/'))) { /* Match any number of directories. */ int follow; /* with three stars, follow symbolic links */ - follow = (pptr[2] == Star); - pptr += (3 + follow); + follow = (instr[2] == Star); + instr += (3 + follow); /* Now get the next path component if there is one. */ - p1 = (Complist) alloc(sizeof *p1); - if ((p1->next = parsecomplist()) == NULL) { + l1 = (Complist) alloc(sizeof *l1); + if ((l1->next = parsecomplist(instr)) == NULL) { errflag = 1; return NULL; } - p1->comp = compalloc(); - p1->comp->stat |= C_LAST; /* end of path component */ - p1->comp->str = dupstring("*"); - *p1->comp->str = Star; /* match anything... */ - p1->closure = 1; /* ...zero or more times. */ - p1->follow = follow; - return p1; + l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); + l1->closure = 1; /* ...zero or more times. */ + l1->follow = follow; + return l1; } /* Parse repeated directories such as (dir/)# and (dir/)## */ - if (*(str = pptr) == Inpar && !skipparens(Inpar, Outpar, &str) && + if (*(str = instr) == Inpar && !skipparens(Inpar, Outpar, (char **)&str) && *str == Pound && isset(EXTENDEDGLOB) && str[-2] == '/') { - pptr++; - if (!(c1 = parsecompsw(0))) + instr++; + if (!(p1 = patcompile(instr, compflags, &instr))) return NULL; - if (pptr[0] == '/' && pptr[1] == Outpar && pptr[2] == Pound) { + if (instr[0] == '/' && instr[1] == Outpar && instr[2] == Pound) { int pdflag = 0; - pptr += 3; - if (*pptr == Pound) { + instr += 3; + if (*instr == Pound) { pdflag = 1; - pptr++; + instr++; } - p1 = (Complist) alloc(sizeof *p1); - p1->comp = c1; - p1->closure = 1 + pdflag; - p1->follow = 0; - p1->next = parsecomplist(); - return (p1->comp) ? p1 : NULL; + l1 = (Complist) alloc(sizeof *l1); + l1->pat = p1; + l1->closure = 1 + pdflag; + l1->follow = 0; + l1->next = parsecomplist(instr); + return (l1->pat) ? l1 : NULL; } } else { /* parse single path component */ - if (!(c1 = parsecompsw(GF_PATHADD|GF_TOPLEV))) + if (!(p1 = patcompile(instr, compflags|PAT_FILET, &instr))) return NULL; /* then do the remaining path compoents */ - if (*pptr == '/' || !*pptr) { - int ef = *pptr == '/'; - - p1 = (Complist) alloc(sizeof *p1); - p1->comp = c1; - p1->closure = 0; - p1->next = ef ? (pptr++, parsecomplist()) : NULL; - return (ef && !p1->next) ? NULL : p1; + if (*instr == '/' || !*instr) { + int ef = *instr == '/'; + + l1 = (Complist) alloc(sizeof *l1); + l1->pat = p1; + l1->closure = 0; + l1->next = ef ? parsecomplist(instr+1) : NULL; + return (ef && !l1->next) ? NULL : l1; } } errflag = 1; @@ -1084,31 +600,31 @@ parsecomplist(void) static Complist parsepat(char *str) { - mode = 0; /* path components present */ - addflags = 0; - errsmax = 0; - pptr = str; - tail = NULL; + patcompstart(); /* * Check for initial globbing flags, so that they don't form * a bogus path component. */ - if (*pptr == Inpar && pptr[1] == Pound && isset(EXTENDEDGLOB) && - getglobflags()) - return NULL; + if ((*str == Inpar && str[1] == Pound && isset(EXTENDEDGLOB)) || + (isset(KSHGLOB) && *str == '@' && str[1] == Inpar && + str[2] == Pound)) { + str += (*str == Inpar) ? 2 : 3; + if (!patgetglobflags(&str)) + return NULL; + } /* Now there is no (#X) in front, we can check the path. */ if (!pathbuf) pathbuf = zalloc(pathbufsz = PATH_MAX); DPUTS(pathbufcwd, "BUG: glob changed directory"); - if (*pptr == '/') { /* pattern has absolute path */ - pptr++; + if (*str == '/') { /* pattern has absolute path */ + str++; pathbuf[0] = '/'; pathbuf[pathpos = 1] = '\0'; } else /* pattern is relative to pwd */ pathbuf[pathpos = 0] = '\0'; - return parsecomplist(); + return parsecomplist(str); } /* get number after qualifier */ @@ -1718,14 +1234,11 @@ glob(LinkList list, LinkNode np) matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * sizeof(struct gmatch)); matchct = 0; - errsfound = 0; - errsmax = -1; + pattrystart(); /* The actual processing takes place here: matches go into * * matchbuf. This is the only top-level call to scanner(). */ - scanning = 1; scanner(q); - scanning = 0; /* Deal with failures to match depending on options */ if (matchct) @@ -2174,13 +1687,13 @@ xpandbraces(LinkList list, LinkNode *np) int matchpat(char *a, char *b) { - Comp c = parsereg(b); + Patprog p = patcompile(b, PAT_STATIC, NULL); - if (!c) { + if (!p) { zerr("bad pattern: %s", b, 0); return 0; } - return domatch(a, c, 0); + return pattry(p, a); } /* do the ${foo%%bar}, ${foo#bar} stuff */ @@ -2284,29 +1797,6 @@ get_match_ret(char *s, int b, int e, int fl, char *replstr) } /* - * Run the pattern so that we always get the longest possible match. - * This eliminates a loop where we gradually shorten the target string - * to find same. We also need to check pptr (the point successfully - * reached along the target string) explicitly. - * - * For this to work, we need the full hairy closure code, so - * set inclosure. - */ - -/**/ -static int -dolongestmatch(char *str, Comp c, int fist) -{ - int ret; - longest = 1; - inclosure++; - ret = domatch(str, c, fist); - inclosure--; - longest = 0; - return ret; -} - -/* * This is called from paramsubst to get the match for ${foo#bar} etc. * fl is a set of the SUB_* flags defined in zsh.h * *sp points to the string we have to modify. The n'th match will be @@ -2323,15 +1813,19 @@ dolongestmatch(char *str, Comp c, int fist) int getmatch(char **sp, char *pat, int fl, int n, char *replstr) { - Comp c; + Patprog p; + int patflags = PAT_STATIC|PAT_SCAN|PAT_NOANCH; MUSTUSEHEAP("getmatch"); /* presumably covered by prefork() test */ - c = parsereg(pat); - if (!c) { + + if ((fl & SUB_ALL) || ((fl & SUB_END) && !(fl & SUB_SUBSTR))) + patflags &= ~PAT_NOANCH; + p = patcompile(pat, patflags, NULL); + if (!p) { zerr("bad pattern: %s", pat, 0); return 1; } - return igetmatch(sp, c, fl, n, replstr); + return igetmatch(sp, p, fl, n, replstr); } /**/ @@ -2339,200 +1833,219 @@ void getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr) { char **arr = *ap, **pp; - Comp c; + Patprog p; + /* + * Flags to pattern compiler: use static buffer since we only + * have one pattern at a time; we will try the must-match test ourselves, + * so tell the pattern compiler we are scanning. + */ + int patflags = PAT_STATIC|PAT_SCAN|PAT_NOANCH; MUSTUSEHEAP("getmatch"); /* presumably covered by prefork() test */ - c = parsereg(pat); - if (!c) { + /* + * Search is anchored to the end of the string if we want to match + * it all, or if we are matching at the end of the string and not + * using substrings. + */ + if ((fl & SUB_ALL) || ((fl & SUB_END) && !(fl & SUB_SUBSTR))) + patflags &= ~PAT_NOANCH; + p = patcompile(pat, patflags, NULL); + if (!p) { zerr("bad pattern: %s", pat, 0); return; } *ap = pp = ncalloc(sizeof(char *) * (arrlen(arr) + 1)); while ((*pp = *arr++)) - if (igetmatch(pp, c, fl, n, replstr)) + if (igetmatch(pp, p, fl, n, replstr)) pp++; } /**/ static int -igetmatch(char **sp, Comp c, int fl, int n, char *replstr) +igetmatch(char **sp, Patprog p, int fl, int n, char *replstr) { char *s = *sp, *t, *start, sav; - int i, l = strlen(*sp), matched; + int i, l = strlen(*sp), matched = 1; repllist = NULL; + /* perform must-match test for complex closures */ + if (p->mustoff && !strstr((char *)s, (char *)p + p->mustoff)) + matched = 0; + if (fl & SUB_ALL) { - i = domatch(s, c, 0); + i = matched && pattry(p, s); *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0); if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) return 0; return 1; } - switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { - case 0: - case SUB_LONG: - /* - * Largest/smallest possible match at head of string. - * First get the longest match... - */ - if (dolongestmatch(s, c, 0)) { - char *mpos = pptr; - if (!(fl & SUB_LONG)) { - /* - * ... now we know whether it's worth looking for the - * shortest, which we do by brute force. - */ - for (t = s; t < mpos; METAINC(t)) { - sav = *t; - *t = '\0'; - if (dolongestmatch(s, c, 0)) { - mpos = pptr; - *t = sav; - break; + if (matched) { + switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { + case 0: + case SUB_LONG: + /* + * Largest/smallest possible match at head of string. + * First get the longest match... + */ + if (pattry(p, s)) { + char *mpos = patinput; + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + /* + * ... now we know whether it's worth looking for the + * shortest, which we do by brute force. + */ + for (t = s; t < mpos; METAINC(t)) { + sav = *t; + *t = '\0'; + if (pattry(p, s)) { + mpos = patinput; + *t = sav; + break; + } + *t = sav; + } } - *t = sav; - } + *sp = get_match_ret(*sp, 0, mpos-s, fl, replstr); + return 1; } - *sp = get_match_ret(*sp, 0, mpos-s, fl, replstr); - return 1; - } - break; + break; - case SUB_END: - /* Smallest possible match at tail of string: * - * move back down string until we get a match. * - * There's no optimization here. */ - for (t = s + l; t >= s; t--) { - if (domatch(t, c, 0)) { - *sp = get_match_ret(*sp, t - s, l, fl, replstr); - return 1; + case SUB_END: + /* Smallest possible match at tail of string: * + * move back down string until we get a match. * + * There's no optimization here. */ + for (t = s + l; t >= s; t--) { + if (pattry(p, t)) { + *sp = get_match_ret(*sp, t - s, l, fl, replstr); + return 1; + } + if (t > s+1 && t[-2] == Meta) + t--; } - if (t > s+1 && t[-2] == Meta) - t--; - } - break; + break; - case (SUB_END|SUB_LONG): - /* Largest possible match at tail of string: * - * move forward along string until we get a match. * - * Again there's no optimisation. */ - for (i = 0, t = s; i < l; i++, t++) { - if (domatch(t, c, 0)) { - *sp = get_match_ret(*sp, i, l, fl, replstr); - return 1; + case (SUB_END|SUB_LONG): + /* Largest possible match at tail of string: * + * move forward along string until we get a match. * + * Again there's no optimisation. */ + for (i = 0, t = s; i < l; i++, t++) { + if (pattry(p, t)) { + *sp = get_match_ret(*sp, i, l, fl, replstr); + return 1; + } + if (*t == Meta) + i++, t++; } - if (*t == Meta) - i++, t++; - } - break; + break; - case SUB_SUBSTR: - /* Smallest at start, but matching substrings. */ - if (!(fl & SUB_GLOBAL) && domatch(s + l, c, 0) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr); - return 1; - } /* fall through */ - case (SUB_SUBSTR|SUB_LONG): - /* longest or smallest at start with substrings */ - start = s; - if (fl & SUB_GLOBAL) - repllist = newlinklist(); - do { - /* loop over all matches for global substitution */ - matched = 0; - for (t = start; t < s + l; t++) { - /* Find the longest match from this position. */ - if (dolongestmatch(t, c, 0) && pptr > t) { - char *mpos = pptr; - while (!(fl & SUB_LONG) && pptr > t) { - /* Now reduce to find the smallest match */ - char *p = (pptr > t + 1 && pptr[-2] == Meta) ? - pptr - 2 : pptr - 1; - sav = *p; - *p = '\0'; - if (!dolongestmatch(t, c, 0)) { - *p = sav; - break; + case SUB_SUBSTR: + /* Smallest at start, but matching substrings. */ + if (!(fl & SUB_GLOBAL) && pattry(p, s + l) && !--n) { + *sp = get_match_ret(*sp, 0, 0, fl, replstr); + return 1; + } /* fall through */ + case (SUB_SUBSTR|SUB_LONG): + /* longest or smallest at start with substrings */ + start = s; + if (fl & SUB_GLOBAL) + repllist = newlinklist(); + do { + /* loop over all matches for global substitution */ + matched = 0; + for (t = start; t < s + l; t++) { + /* Find the longest match from this position. */ + if (pattry(p, t) && patinput > t) { + char *mpos = patinput; + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + char *ptr; + for (ptr = t; ptr < mpos; METAINC(ptr)) { + sav = *ptr; + *ptr = '\0'; + if (pattry(p, t)) { + mpos = patinput; + *ptr = sav; + break; + } + *ptr = sav; + } } - mpos = pptr; - *p = sav; - } - if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) - *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); - if (!(fl & SUB_GLOBAL)) { - if (n) { - /* - * Looking for a later match: in this case, - * we can continue looking for matches from - * the next character, even if it overlaps - * with what we just found. - */ - continue; - } else - return 1; + if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) + *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); + if (!(fl & SUB_GLOBAL)) { + if (n) { + /* + * Looking for a later match: in this case, + * we can continue looking for matches from + * the next character, even if it overlaps + * with what we just found. + */ + continue; + } else + return 1; + } + /* + * For a global match, we need to skip the stuff + * which is already marked for replacement. + */ + matched = 1; + start = mpos; + break; } - /* - * For a global match, we need to skip the stuff - * which is already marked for replacement. - */ - matched = 1; - start = mpos; - break; + if (*t == Meta) + t++; } - if (*t == Meta) - t++; + } while (matched); + /* + * check if we can match a blank string, if so do it + * at the start. Goodness knows if this is a good idea + * with global substitution, so it doesn't happen. + */ + if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && + pattry(p, s + l) && !--n) { + *sp = get_match_ret(*sp, 0, 0, fl, replstr); + return 1; } - } while (matched); - /* - * check if we can match a blank string, if so do it - * at the start. Goodness knows if this is a good idea - * with global substitution, so it doesn't happen. - */ - if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && - domatch(s + l, c, 0) && !--n) { - *sp = get_match_ret(*sp, 0, 0, fl, replstr); - return 1; - } - break; + break; - case (SUB_END|SUB_SUBSTR): - /* Shortest at end with substrings */ - if (domatch(s + l, c, 0) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr); - return 1; - } /* fall through */ - case (SUB_END|SUB_LONG|SUB_SUBSTR): - /* Longest/shortest at end, matching substrings. */ - for (t = s + l - 1; t >= s; t--) { - if (t > s && t[-1] == Meta) - t--; - if (dolongestmatch(t, c, 0) && pptr > t && !--n) { - /* Found the longest match */ - char *mpos = pptr; - while (!(fl & SUB_LONG) && pptr > t) { - /* Look for the shortest match */ - char *p = (pptr > t+1 && pptr[-2] == Meta) ? - pptr-2 : pptr-1; - sav = *p; - *p = '\0'; - if (!dolongestmatch(t, c, 0) || pptr == t) { - *p = sav; - break; + case (SUB_END|SUB_SUBSTR): + /* Shortest at end with substrings */ + if (pattry(p, s + l) && !--n) { + *sp = get_match_ret(*sp, l, l, fl, replstr); + return 1; + } /* fall through */ + case (SUB_END|SUB_LONG|SUB_SUBSTR): + /* Longest/shortest at end, matching substrings. */ + for (t = s + l - 1; t >= s; t--) { + if (t > s && t[-1] == Meta) + t--; + if (pattry(p, t) && patinput > t && !--n) { + /* Found the longest match */ + char *mpos = patinput; + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + char *ptr; + for (ptr = t; ptr < mpos; METAINC(ptr)) { + sav = *ptr; + *ptr = '\0'; + if (pattry(p, t)) { + mpos = patinput; + *ptr = sav; + break; + } + *ptr = sav; + } } - *p = sav; - mpos = pptr; + *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); + return 1; } - *sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr); + } + if ((fl & SUB_LONG) && pattry(p, s + l) && !--n) { + *sp = get_match_ret(*sp, l, l, fl, replstr); return 1; } + break; } - if ((fl & SUB_LONG) && domatch(s + l, c, 0) && !--n) { - *sp = get_match_ret(*sp, l, l, fl, replstr); - return 1; - } - break; } if (repllist && nonempty(repllist)) { @@ -2563,7 +2076,7 @@ igetmatch(char **sp, Comp c, int fl, int n, char *replstr) } memcpy(t, s + i, l - i); start[lleft] = '\0'; - *sp = start; + *sp = (char *)start; return 1; } @@ -2572,804 +2085,6 @@ igetmatch(char **sp, Comp c, int fl, int n, char *replstr) return 1; } -/* The main entry point for matching a string str against * - * a compiled pattern c. `fist' indicates whether leading * - * dots are special. */ - -/**/ -int -domatch(char *str, Comp c, int fist) -{ - int ret; - pptr = str; - first = fist; - /* - * If scanning paths, keep the error count over the whole - * filename - */ - if (!scanning) { - errsfound = 0; - errsmax = -1; - } - if (*pptr == Nularg) - pptr++; - PERMALLOC { - ret = doesmatch(c); - } LASTALLOC; - return ret; -} - -#define untok(C) (itok(C) ? ztokens[(C) - Pound] : (C)) - -/* See if pattern has a matching exclusion (~...) part */ - -/**/ -static int -excluded(Comp c, char *eptr) -{ - char *saves = pptr; - int savei = first, savel = longest, savee = errsfound, ret; - - first = 0; - /* - * Even if we've been told always to match the longest string, - * i.e. not anchored to the end, we want to match the full string - * we've already matched when we're trying to exclude it. - * Likewise, approximate matching here is treated separately. - */ - longest = 0; - errsfound = 0; - if (PATHADDP(c) && pathpos) { - VARARR(char, buf, pathpos + strlen(eptr) + 1); - - strcpy(buf, pathbuf); - strcpy(buf + pathpos, eptr); - pptr = buf; - ret = doesmatch(c->exclude); - } else { - pptr = eptr; - ret = doesmatch(c->exclude); - } - if (*pptr) - ret = 0; - - pptr = saves; - first = savei; - longest = savel; - errsfound = savee; - - return ret; -} - -/* - * Structure for storing instances of a pattern in a closured. - */ -struct gclose { - char *start; /* Start of this instance */ - char *end; /* End of this instance */ - int errsfound; /* Errors found up to start of instance */ -}; -typedef struct gclose *Gclose; - -/* Add a list of matches that fit the closure. trystring is a string of - * the same length as the target string; a non-zero in that string - * indicates that we have already tried to match the patterns following - * the closure (c->next) at that point and failed. This means that not - * only should we not bother using the corresponding match, we should - * also not bother going any further, since the first time we got to - * that position (when it was marked), we must already have failed on - * and backtracked over any further closure matches beyond that point. - * If we are using approximation, the number in the string is incremented - * by the current error count; if we come back to that point with a - * lower error count, it is worth trying from that point again, but - * we must now mark that point in trystring with the new error. - */ - -/**/ -static void -addclosures(Comp c, LinkList closlist, int *pdone, unsigned char *trystring) -{ - Gclose gcnode; - char *opptr = pptr; - unsigned char *tpos; - - while (*pptr) { - if (STARP(c)) { - if (*(tpos = trystring + ((pptr+1) - opptr))) { - if ((unsigned)(errsfound+1) >= *tpos) - break; - *tpos = (unsigned)(errsfound+1); - } - gcnode = (Gclose)zalloc(sizeof(struct gclose)); - gcnode->start = pptr; - gcnode->end = METAINC(pptr); - gcnode->errsfound = errsfound; - } else { - char *saves = pptr; - int savee = errsfound; - if (OPTIONALP(c) && *pdone >= 1) - return; - if (!matchonce(c) || saves == pptr || - (*(tpos = trystring + (pptr-opptr)) && - (unsigned)(errsfound+1) >= *tpos)) { - pptr = saves; - break; - } - if (*tpos) - *tpos = (unsigned)(errsfound+1); - gcnode = (Gclose)zalloc(sizeof(struct gclose)); - gcnode->start = saves; - gcnode->end = pptr; - gcnode->errsfound = savee; - } - pushnode(closlist, gcnode); - (*pdone)++; - } -} - -/* - * Match characters with case-insensitivity. Note charmatch(x,y) != - * charmatch(y,x): the first argument comes from the target string, the - * second from the pattern. This used to be a macro, and in principle - * could be turned back into one. - */ - -/**/ -static int -charmatch(Comp c, char *x, char *y) -{ - /* - * This takes care of matching two characters which may be a - * two byte sequence, Meta followed by something else. - * Here we bypass tulower() and tuupper() for speed. - */ - int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff); - /* A NULL is a real null, since a \000 would be metafied. */ - if (!*x || !*y) - return 0; - return xi == yi || - (((c->stat & C_IGNCASE) ? - ((isupper(xi) ? tolower(xi) : xi) == - (isupper(yi) ? tolower(yi) : yi)) : - (c->stat & C_LCMATCHUC) ? - (islower(yi) && toupper(yi) == xi) : 0)); -} - -/* see if current string in pptr matches c */ - -/**/ -static int -doesmatch(Comp c) -{ - if (CLOSUREP(c)) { - int done, retflag = 0; - char *saves, *opptr; - unsigned char *trystring, *tpos; - int savee; - LinkList closlist; - Gclose gcnode; - - if (first && *pptr == '.') - return 0; - - if (!inclosure && !c->left && !c->errsmax) { - /* We are not inside another closure, and the current - * pattern is a simple string. We handle this very common - * case specially: otherwise, matches like *foo* are - * extremely slow. Here, instead of backtracking, we track - * forward until we get a match. At top level, we are bound - * to get there eventually, so this is OK. - */ - char looka; - - if (STARP(c) && c->next && - !c->next->left && (looka = *c->next->str) && - !itok(looka)) { - /* Another simple optimisation for a very common case: - * we are processing a * and there is - * an ordinary character match (which may not be a Metafied - * character, just to make it easier) next. We look ahead - * for that character, taking care of Meta bytes. - */ - while (*pptr) { - for (; *pptr; pptr++) { - if (*pptr == Meta) - pptr++; - else if (charmatch(c, pptr, &looka)) - break; - } - if (!*(saves = pptr)) - break; - savee = errsfound; - if (doesmatch(c->next)) - return 1; - pptr = saves+1; - errsfound = savee; - } - } else { - /* Standard track-forward code */ - for (done = 0; ; done++) { - saves = pptr; - savee = errsfound; - if ((done || ONEHASHP(c) || OPTIONALP(c)) && - ((!c->next && (!LASTP(c) || !*pptr || longest)) || - (c->next && doesmatch(c->next)))) - return 1; - if (done && OPTIONALP(c)) - return 0; - pptr = saves; - errsfound = savee; - first = 0; - if (STARP(c)) { - if (!*pptr) - return 0; - pptr++; - } else if (!matchonce(c) || pptr == saves) - return 0; - } - } - return 0; - } - /* The full, gory backtracking code is now necessary. */ - inclosure++; - closlist = newlinklist(); - trystring = (unsigned char *)zcalloc(strlen(pptr)+1); - opptr = pptr; - - /* Start by making a list where each match is as long - * as possible. We later have to take account of the - * fact that earlier matches may be too long. - */ - done = 0; - addclosures(c, closlist, &done, trystring); - for (;;) { - if (TWOHASHP(c) && !done) - break; - saves = pptr; - /* do we really want this LASTP here?? */ - if ((!c->next && (!LASTP(c) || !*pptr || longest)) || - (c->next && doesmatch(c->next))) { - retflag = 1; - break; - } - trystring[saves-opptr] = (unsigned)(errsfound + 1); - /* - * If we failed, the first thing to try is whether we can - * shorten the match using the last pattern in the closure. - */ - gcnode = firstnode(closlist) ? peekfirst(closlist) : NULL; - if (gcnode && --gcnode->end > gcnode->start - && (gcnode->end[-1] != Meta || - --gcnode->end > gcnode->start)) { - char savec = *gcnode->end; - *gcnode->end = '\0'; - pptr = gcnode->start; - errsfound = gcnode->errsfound; - if (matchonce(c) && pptr != gcnode->start - && (!*(tpos = trystring + (pptr-opptr)) || - *tpos > (unsigned)(errsfound+1))) { - if (*tpos) - *tpos = (unsigned)(errsfound+1); - *gcnode->end = savec; - gcnode->end = pptr; - /* Try again to construct a list based on - * this new position - */ - addclosures(c, closlist, &done, tpos); - continue; - } - *gcnode->end = savec; - } - /* We've now exhausted the possibilities with that match, - * backtrack to the previous. - */ - if ((gcnode = (Gclose)getlinknode(closlist))) { - pptr = gcnode->start; - errsfound = gcnode->errsfound; - zfree(gcnode, sizeof(struct gclose)); - done--; - } else - break; - } - freelinklist(closlist, free); - zfree(trystring, strlen(opptr)+1); - inclosure--; - - return retflag; - } else - return matchonce(c); -} - -/**/ -static int -posix_range(char **patptr, int ch) -{ - /* Match POSIX ranges, which correspond to ctype macros, * - * e.g. [:alpha:] -> isalpha. It just doesn't seem worth * - * the palaver of creating a hash table for this. */ - char *start = *patptr; - int len; - - /* we made sure in parsecomp() there was a ':' to search for */ - *patptr = strchr(start, ':'); - len = (*patptr)++ - start; - - if (!strncmp(start, "alpha", len)) - return isalpha(ch); - if (!strncmp(start, "alnum", len)) - return isalnum(ch); - if (!strncmp(start, "blank", len)) - return ch == ' ' || ch == '\t'; - if (!strncmp(start, "cntrl", len)) - return iscntrl(ch); - if (!strncmp(start, "digit", len)) - return isdigit(ch); - if (!strncmp(start, "graph", len)) - return isgraph(ch); - if (!strncmp(start, "lower", len)) - return islower(ch); - if (!strncmp(start, "print", len)) - return isprint(ch); - if (!strncmp(start, "punct", len)) - return ispunct(ch); - if (!strncmp(start, "space", len)) - return isspace(ch); - if (!strncmp(start, "upper", len)) - return isupper(ch); - if (!strncmp(start, "xdigit", len)) - return isxdigit(ch); - return 0; -} - -/**/ -static void -rangematch(char **patptr, int ch, int rchar) -{ - /* Check for a character in a [...] or [^...]. The [ * - * and optional ^ have already been skipped. */ - - char *pat = *patptr; - /* We don't use strcoll() for ranges, since it can have side - * effects. It's less necessary now we have [:posix:] ranges. - */ -#if 0 - char l_buf[2], r_buf[2], ch_buf[2]; - - ch_buf[0] = ch; - l_buf[1] = r_buf[1] = ch_buf[1] = '\0'; -#endif - -#define PAT(X) (pat[X] == Meta ? pat[(X)+1] ^ 32 : untok(pat[X])) -#define PPAT(X) (pat[(X)-1] == Meta ? pat[X] ^ 32 : untok(pat[X])) - - for (pat++; *pat != Outbrack && *pat; METAINC(pat)) { - if (*pat == Inbrack) { - /* Inbrack can only occur inside a range if we found [:...:]. */ - pat += 2; - if (posix_range(&pat, ch)) - break; - } else if (*pat == '-' && pat[-1] != rchar && - pat[1] != Outbrack) { -#if 0 - l_buf[0] = PPAT(-1); - r_buf[0] = PAT(1); - if (strcoll(l_buf, ch_buf) <= 0 && - strcoll(ch_buf, r_buf) <= 0) -#else - if (PPAT(-1) <= ch && PAT(1) >= ch) -#endif - break; - } else if (ch == PAT(0)) - break; - } - - *patptr = pat; -} - -/* - * matchonce() is the core of the pattern matching, handling individual - * strings and instances of a pattern in a closure. - * - * Note on approximate matching: The rule is supposed to be - * (1) Take the longest possible match without approximation. - * (2) At any failure, make the single approximation that results - * in the longest match for the remaining part (which may - * include further approximations). - * (3) If we match the same distance, take the one with fewer - * approximations. - * If this is wrong, I haven't yet discovered a counterexample. Email - * lines are now open. - * pws 1999/02/23 - */ - -/**/ -static int -matchonce(Comp c) -{ - char *pat = c->str; - for (;;) { - /* loop until success or failure of pattern */ - if (!pat || !*pat) { - /* No current pattern (c->str). */ - char *saves; - int savee, savei; - - if (errflag) - return 0; - /* Remember state in case we need to go back and * - * check for exclusion of pattern or alternatives. */ - saves = pptr; - savei = first; - savee = errsfound; - /* Loop over alternatives with exclusions: (foo~bar|...). * - * Exclusions apply to the pattern in c->left. */ - if (c->left || c->right) { - int ret = 0, ret2 = 0; - if (c->exclude) { - char *exclend = 0; - - /* We may need to back up on things like `(*~foo)' - * if the `*' matched `foo' but needs to match `fo'. - * exclend marks the end of the shortened text. We - * need to restore it to match the tail. - * We set `inclosure' because we need the more - * sophisticated code in doesmatch() for any nested - * closures. - */ - inclosure++; - - while (!exclend || exclend >= pptr) { - char exclsav = 0; - if (exclend) { - exclsav = *exclend; - *exclend = '\0'; - } - if ((ret = doesmatch(c->left))) { - if (exclend) - *exclend = exclsav; - exclsav = *(exclend = pptr); - *exclend = '\0'; - ret2 = !excluded(c, saves); - } - if (exclend) - *exclend = exclsav; - - if (!ret) - break; - if ((ret = ret2 && - ((!c->next && (!LASTP(c) || !*pptr || longest)) - || (c->next && doesmatch(c->next)))) || - (!c->next && LASTP(c))) - break; - /* Back up if necessary: exclend gives the position - * of the end of the match we are excluding, - * so only try to match to there. - */ - exclend--; - pptr = saves; - errsfound = savee; - } - inclosure--; - if (ret) - return 1; - } else - ret = doesmatch(c->left); - ret2 = 0; - if (c->right && (!ret || inclosure)) { - /* If in a closure, we always want the longest match. */ - char *newpptr = pptr; - int newerrsfound = errsfound; - pptr = saves; - first = savei; - errsfound = savee; - ret2 = doesmatch(c->right); - if (ret && (!ret2 || pptr < newpptr)) { - pptr = newpptr; - errsfound = newerrsfound; - } - } - if (!ret && !ret2) { - pptr = saves; - first = savei; - errsfound = savee; - break; - } - } - if (CLOSUREP(c)) - return 1; - if (!c->next) { - /* - * No more patterns left, but we may still be in the middle - * of a match, in which case alles in Ordnung... - */ - if (!LASTP(c)) - return 1; - /* - * ...else we're at the last pattern, so this is our last - * ditch attempt at an approximate match: try to omit the - * last few characters. - */ - for (; *pptr && errsfound < c->errsmax && - (errsmax == -1 || errsfound < errsmax); - METAINC(pptr)) - errsfound++; - return !*pptr || longest; - } - /* optimisation when next pattern is not a closure */ - if (!CLOSUREP(c->next)) { - c = c->next; - pat = c->str; - continue; - } - return doesmatch(c->next); - } - /* - * Don't match leading dot if first is set - * (don't even try for an approximate match) - */ - if (first && *pptr == '.' && *pat != '.') - return 0; - if (*pat == Star) { /* final * is not expanded to ?#; returns success */ - while (*pptr) - pptr++; - return 1; - } - first = 0; /* finished checking start of pattern */ - if (*pat == Quest) { - /* match exactly one character */ - if (!*pptr) - break; - METAINC(pptr); - pat++; - continue; - } - if (*pat == Inbrack) { - /* Match groups of characters */ - char ch; - char *saves, *savep; - - if (!*pptr) - break; - saves = pptr; - savep = pat; - ch = UNMETA(pptr); - if (pat[1] == Hat || pat[1] == '^' || pat[1] == '!') { - /* group is negated */ - *++pat = Hat; - rangematch(&pat, ch, Hat); - DPUTS(!*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat != Outbrack) { - pptr = saves; - pat = savep; - break; - } - pat++; - METAINC(pptr); - continue; - } else { - /* pattern is not negated (affirmed? asserted?) */ - rangematch(&pat, ch, Inbrack); - DPUTS(!pat || !*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat == Outbrack) { - pptr = saves; - pat = savep; - break; - } - for (METAINC(pptr); *pat != Outbrack; pat++); - pat++; - continue; - } - } - if (*pat == Inang) { - /* Numeric globbing. */ -#ifdef ZSH_64_BIT_TYPE -/* zstrtol returns zlong anyway */ -# define RANGE_CAST() - zlong t1, t2, t3; -#else -# define RANGE_CAST() (unsigned long) - unsigned long t1, t2, t3; -#endif - char *ptr, *saves = pptr, *savep = pat; - - if (!idigit(*pptr)) - break; - if (*++pat == Outang || - (*pat == '-' && pat[1] == Outang && ++pat)) { - /* <> or <->: any number matches */ - while (idigit(*++pptr)); - pat++; - } else { - /* Flag that there is no upper limit */ - int not3 = 0; - char *opptr = pptr; - /* - * Form is <a-b>, where a or b are numbers or blank. - * t1 = number supplied: must be positive, so use - * unsigned arithmetic. - */ - t1 = RANGE_CAST() zstrtol(pptr, &ptr, 10); - pptr = ptr; - /* t2 = lower limit */ - if (idigit(*pat)) - t2 = RANGE_CAST() zstrtol(pat, &ptr, 10); - else - t2 = 0, ptr = pat; - if (*ptr != '-' || (not3 = (ptr[1] == Outang))) - /* exact match or no upper limit */ - t3 = t2, pat = ptr + not3; - else /* t3 = upper limit */ - t3 = RANGE_CAST() zstrtol(ptr + 1, &pat, 10); - DPUTS(*pat != Outang, "BUG: wrong internal range pattern"); - pat++; - /* - * If the number found is too large for the pattern, - * try matching just the first part. This way - * we always get the longest possible match. - */ - while (!not3 && t1 > t3 && pptr > opptr+1) { - pptr--; - t1 /= 10; - } - if (t1 < t2 || (!not3 && t1 > t3)) { - pptr = saves; - pat = savep; - break; - } - } - continue; -#undef RANGE_CAST - } - /* itok(Meta) is zero */ - DPUTS(itok(*pat), "BUG: matching tokenized character"); - if (charmatch(c, pptr, pat)) { - /* just plain old characters (or maybe unplain new characters) */ - METAINC(pptr); - METAINC(pat); - continue; - } - if (errsfound < c->errsmax && - (errsmax == -1 || errsfound < errsmax)) { - /* - * We tried to match literal characters and failed. Now it's - * time to match approximately. Note this doesn't handle the - * case where we *didn't* try to match literal characters, - * including the case where we were already at the end of the - * pattern, because then we never get here. In that case the - * pattern has to be matched exactly, but we could maybe - * advance up the target string before trying it, so we have to - * handle that case elsewhere. - */ - char *saves = pptr, *savep = c->str; - char *maxpptr = pptr, *patnext = METANEXT(pat); - int savee, maxerrs = -1; - - /* First try to advance up the pattern. */ - c->str = patnext; - savee = ++errsfound; - if (matchonce(c)) { - maxpptr = pptr; - maxerrs = errsfound; - } - errsfound = savee; - - if (*saves) { - /* - * If we have characters on both strings, we have more - * choice. - * - * Try to edge up the target string. - */ - char *strnext = METANEXT(saves); - pptr = strnext; - c->str = pat; - if (matchonce(c) && - (maxerrs == -1 || pptr > maxpptr || - (pptr == maxpptr && errsfound <= maxerrs))) { - maxpptr = pptr; - maxerrs = errsfound; - } - errsfound = savee; - - /* - * Try edging up both of them at once. - * Note this takes precedence in the case of equal - * length as we get further up the pattern. - */ - c->str = patnext; - pptr = strnext; - if (matchonce(c) && - (maxerrs == -1 || pptr > maxpptr || - (pptr == maxpptr && errsfound <= maxerrs))) { - maxpptr = pptr; - maxerrs = errsfound; - } - errsfound = savee; - - /* - * See if we can transpose: that counts as just one error. - * - * Note, however, `abanana' and `banana': transposing - * is wrong here, as it gets us two errors, - * [ab][a]nana vs [ba][]nana, instead of the correct - * [a]banana vs []banana, so we still need to try - * the other possibilities. - */ - if (strnext && patnext && !itok(*patnext) && - charmatch(c, strnext, pat) && - charmatch(c, saves, patnext)) { - c->str = patnext; - METAINC(c->str); - - pptr = strnext; - METAINC(pptr); - - if (matchonce(c) && - (maxerrs == -1 || pptr > maxpptr || - (pptr == maxpptr && errsfound <= maxerrs))) { - maxpptr = pptr; - maxerrs = errsfound; - } - } - } - /* - * We don't usually restore state on failure, but we need - * to fix up the Comp structure which we altered to - * look at the tail of the pattern. - */ - c->str = savep; - /* - * If that failed, game over: we don't want to break - * and try the other approximate test, because we just did - * that. - */ - if (maxerrs == -1) - return 0; - pptr = maxpptr; - errsfound = maxerrs; - return 1; - } - break; - } - if (*pptr && errsfound < c->errsmax && - (errsmax == -1 || errsfound < errsmax)) { - /* - * The pattern failed, but we can try edging up the target string - * and rematching with an error. Note we do this from wherever we - * got to in the pattern string c->str, not the start. hence the - * need to modify c->str. - * - * At this point, we don't have a literal character in the pattern - * (handled above), so we don't try any funny business on the - * pattern itself. - */ - int ret; - char *savep = c->str; - errsfound++; - METAINC(pptr); - c->str = pat; - ret = matchonce(c); - c->str = savep; - return ret; - } - return 0; -} - -/* turn a string into a Comp struct: this doesn't treat / specially */ - -/**/ -Comp -parsereg(char *str) -{ - remnulargs(str); - mode = 1; /* no path components */ - addflags = 0; - errsmax = 0; - pptr = str; - tail = NULL; - return parsecompsw(GF_TOPLEV); -} - /* blindly turn a string into a tokenised expression without lexing */ /**/ diff --git a/Src/hashtable.c b/Src/hashtable.c index c4c0b00ac..7a533d98a 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -418,7 +418,7 @@ scanhashtable(HashTable ht, int sorted, int flags1, int flags2, ScanFunc scanfun /**/ int -scanmatchtable(HashTable ht, Comp com, int flags1, int flags2, ScanFunc scanfunc, int scanflags) +scanmatchtable(HashTable ht, Patprog pprog, int flags1, int flags2, ScanFunc scanfunc, int scanflags) { int i, hsize = ht->hsize; HashNode *nodes = ht->nodes; @@ -433,7 +433,7 @@ scanmatchtable(HashTable ht, Comp com, int flags1, int flags2, ScanFunc scanfunc HashNode hn = st.u.u; st.u.u = st.u.u->next; if ((hn->flags & flags1) + !flags1 && !(hn->flags & flags2) && - domatch(hn->nam, com, 0)) { + pattry(pprog, hn->nam)) { scanfunc(hn, scanflags); match++; } diff --git a/Src/hist.c b/Src/hist.c index 52e3e1394..8d8fba4af 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -577,6 +577,18 @@ histsubchar(int c) case 'q': quote(&sline); break; + case 'Q': + { + int one = noerrs, oef = errflag; + + noerrs = 1; + parse_subst_string(sline); + noerrs = one; + errflag = oef; + remnulargs(sline); + untokenize(sline); + } + break; case 'x': quotebreak(&sline); break; @@ -1947,6 +1959,7 @@ lockhistfile(char *fn, int keep_trying) int fd, len = strlen(fn); char *tmpfile, *lockfile; +#ifdef HAVE_LINK tmpfile = zalloc(len + 10 + 1); sprintf(tmpfile, "%s.%ld", fn, (long)mypid); if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_EXCL, 0644)) >= 0) { @@ -1973,6 +1986,21 @@ lockhistfile(char *fn, int keep_trying) } unlink(tmpfile); free(tmpfile); +#else /* not HAVE_LINK */ + lockfile = zalloc(len + 5 + 1); + sprintf(lockfile, "%s.LOCK", fn); + while ((fd = open(lockfile, O_CREAT|O_EXCL, 0644)) < 0) { + if (errno == EEXIST) continue; + else if (keep_trying) { + if (time(NULL) - sb.st_mtime < 10) + sleep(1); + continue; + } + lockhistct--; + break; + } + free(lockfile); +#endif /* HAVE_LINK */ } return ct != lockhistct; } diff --git a/Src/init.c b/Src/init.c index 2da409415..136c718d0 100644 --- a/Src/init.c +++ b/Src/init.c @@ -353,7 +353,7 @@ init_io(void) * Try both stdin and stdout before trying /dev/tty. -- Bart */ #if defined(HAVE_FCNTL_H) && defined(F_GETFL) -#define rdwrtty(fd) ((fcntl(fd, F_GETFL) & O_RDWR) == O_RDWR) +#define rdwrtty(fd) ((fcntl(fd, F_GETFL, 0) & O_RDWR) == O_RDWR) #else #define rdwrtty(fd) 1 #endif diff --git a/Src/jobs.c b/Src/jobs.c index fdf69a960..4b9d66986 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -835,7 +835,11 @@ waitforpid(pid_t pid) /* child_block() around this loop in case #ifndef WNOHANG */ child_block(); /* unblocked in child_suspend() */ +#ifdef BROKEN_KILL_ESRCH + while (!errflag && (kill(pid, 0) >= 0 || (errno != ESRCH && errno != EINVAL))) { +#else /* not BROKEN_KILL_ESRCH */ while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) { +#endif /* BROKEN_KILL_ESRCH */ if (first) first = 0; else diff --git a/Src/loop.c b/Src/loop.c index fd63edb5a..3432c384f 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -178,8 +178,13 @@ execselect(Cmd cmd, LinkList args, int flags) for (;;) { if (empty(bufstack)) { if (interact && SHTTY != -1 && isset(USEZLE)) { + int oef = errflag; + isfirstln = 1; str = (char *)zleread(prompt3, NULL, 0); + if (errflag) + str = NULL; + errflag = oef; } else { str = promptexpand(prompt3, 0, NULL, NULL); zputs(str, stderr); diff --git a/Src/options.c b/Src/options.c index 0207cd232..03fa89f5a 100644 --- a/Src/options.c +++ b/Src/options.c @@ -526,7 +526,7 @@ bin_setopt(char *nam, char **args, char *ops, int isun) } else { /* Globbing option (-m) set. */ while (*args) { - Comp com; + Patprog pprog; char *s, *t; t = s = dupstring(*args); @@ -540,12 +540,12 @@ bin_setopt(char *nam, char **args, char *ops, int isun) /* Expand the current arg. */ tokenize(s); - if (!(com = parsereg(s))) { + if (!(pprog = patcompile(s, PAT_STATIC, NULL))) { zwarnnam(nam, "bad pattern: %s", *args, 0); continue; } /* Loop over expansions. */ - scanmatchtable(optiontab, com, 0, OPT_ALIAS, setoption, !isun); + scanmatchtable(optiontab, pprog, 0, OPT_ALIAS, setoption, !isun); args++; } } @@ -653,6 +653,14 @@ dosetopt(int optno, int value, int force) setuid(getuid()); setgid(getgid()); #endif /* HAVE_SETUID */ +#ifndef JOB_CONTROL + } else if(optno == MONITOR && value) { + return -1; +#endif /* not JOB_CONTROL */ +#ifdef GETPWNAM_FAKED + } else if(optno == CDABLEVARS && value) { + return -1; +#endif /* GETPWNAM_FAKED */ } opts[optno] = value; if (optno == BANGHIST || optno == SHINSTDIN) diff --git a/Src/params.c b/Src/params.c index 094d7a166..8cba005d4 100644 --- a/Src/params.c +++ b/Src/params.c @@ -354,7 +354,7 @@ scancountparams(HashNode hn, int flags) ++numparamvals; } -static Comp scancomp; +static Patprog scanprog; static char **paramvals; /**/ @@ -366,7 +366,8 @@ scanparamvals(HashNode hn, int flags) !(flags & SCANPM_MATCHMANY)) return; v.pm = (Param)hn; - if ((flags & SCANPM_MATCHKEY) && !domatch(v.pm->nam, scancomp, 0)) { + if ((flags & SCANPM_MATCHKEY) && + !pattry(scanprog, v.pm->nam)) { return; } if (flags & SCANPM_WANTKEYS) { @@ -380,7 +381,7 @@ scanparamvals(HashNode hn, int flags) v.b = -1; paramvals[numparamvals] = getstrvalue(&v); if (flags & SCANPM_MATCHVAL) { - if (domatch(paramvals[numparamvals], scancomp, 0)) { + if (pattry(scanprog, paramvals[numparamvals])) { numparamvals += ((flags & SCANPM_WANTVALS) ? 1 : !(flags & SCANPM_WANTKEYS)); } else if (flags & SCANPM_WANTKEYS) @@ -724,7 +725,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash; char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt; zlong num = 1, beg = 0, r = 0; - Comp c; + Patprog pprog; ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED); @@ -923,12 +924,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) } tokenize(s); - if ((c = parsereg(s))) { + if ((pprog = patcompile(s, 0, NULL))) { int len; if (v->isarr) { if (ishash) { - scancomp = c; + scanprog = pprog; if (ind) v->isarr |= SCANPM_MATCHKEY; else @@ -952,12 +953,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (!hasbeg) beg = len - 1; for (r = 1 + beg, p = ta + beg; p >= ta; r--, p--) { - if (domatch(*p, c, 0) && !--num) + if (pattry(pprog, *p) && !--num) return r; } } else for (r = 1 + beg, p = ta + beg; *p; r++, p++) - if (domatch(*p, c, 0) && !--num) + if (pattry(pprog, *p) && !--num) return r; } } else if (word) { @@ -970,13 +971,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (!hasbeg) beg = len - 1; for (r = 1 + beg, p = ta + beg; p >= ta; p--, r--) - if (domatch(*p, c, 0) && !--num) + if (pattry(pprog, *p) && !--num) break; if (p < ta) return 0; } else { for (r = 1 + beg, p = ta + beg; *p; r++, p++) - if (domatch(*p, c, 0) && !--num) + if (pattry(pprog, *p) && !--num) break; if (!*p) return 0; @@ -1007,7 +1008,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) for (r = beg, t = d + beg; t >= d; r--, t--) { sav = *t; *t = '\0'; - if (domatch(d, c, 0) && !--num) { + if (pattry(pprog, d) + && !--num) { *t = sav; return r; } @@ -1017,7 +1019,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) for (r = beg, t = d + beg; *t; r++, t++) { sav = *t; *t = '\0'; - if (domatch(d, c, 0) && !--num) { + if (pattry(pprog, d) && + !--num) { *t = sav; return r; } @@ -1028,12 +1031,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w) if (!hasbeg) beg = len - 1; for (r = beg + 1, t = d + beg; t >= d; r--, t--) { - if (domatch(t, c, 0) && !--num) + if (pattry(pprog, t) && + !--num) return r; } } else for (r = beg + 1, t = d + beg; *t; r++, t++) - if (domatch(t, c, 0) && !--num) + if (pattry(pprog, t) && + !--num) return r; } } diff --git a/Src/pattern.c b/Src/pattern.c new file mode 100644 index 000000000..048e3d3ec --- /dev/null +++ b/Src/pattern.c @@ -0,0 +1,2284 @@ +/* + * glob.c - filename generation + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Peter Stephenson or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Peter Stephenson and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Peter Stephenson and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Peter Stephenson and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + * Pattern matching code derived from the regexp library by Henry + * Spencer, which has the following copyright. + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Eagle-eyed readers will notice this is an altered version. Incredibly + * sharp-eyed readers might even find bits that weren't altered. + */ + +#include "zsh.mdh" +#include "pattern.pro" + +/* + * Globbing flags: lower 8 bits gives approx count + */ +#define C_LCMATCHUC 0x0100 +#define C_IGNCASE 0x0200 + +/* definition number opnd? meaning */ +#define P_END 0x00 /* no End of program. */ +#define P_EXCSYNC 0x01 /* no Test if following exclude already failed */ +#define P_EXCEND 0x02 /* no Test if exclude matched orig branch */ +#define P_BACK 0x03 /* no Match "", "next" ptr points backward. */ +#define P_EXACTLY 0x04 /* str Match this string. */ +#define P_NOTHING 0x05 /* no Match empty string. */ +#define P_ONEHASH 0x06 /* node Match this (simple) thing 0 or more times. */ +#define P_TWOHASH 0x07 /* node Match this (simple) thing 1 or more times. */ +#define P_GFLAGS 0x08 /* long Match nothing and set globbing flags */ +/* numbered so we can test bit 5 for a branch */ +#define P_BRANCH 0x20 /* node Match this alternative, or the next... */ +#define P_WBRANCH 0x21 /* uc* node P_BRANCH, but match at least 1 char */ +/* excludes are also branches, but have bit 4 set, too */ +#define P_EXCLUDE 0x30 /* uc* node Exclude this from previous branch */ +#define P_EXCLUDP 0x31 /* uc* node Exclude, using full file path so far */ +/* numbered so we can test bit 6 so as not to match initial '.' */ +#define P_ANY 0x40 /* no Match any one character. */ +#define P_ANYOF 0x41 /* str Match any character in this string. */ +#define P_ANYBUT 0x42 /* str Match any character not in this string. */ +#define P_STAR 0x43 /* no Match any set of characters. */ +#define P_NUMRNG 0x44 /* zr, zr Match a numeric range. */ +#define P_NUMFROM 0x45 /* zr Match a number >= X */ +#define P_NUMTO 0x46 /* zr Match a number <= X */ +#define P_NUMANY 0x47 /* no Match any set of decimal digits */ +/* spaces left for P_OPEN+n,... for backreferences */ +#define P_OPEN 0x80 /* no Mark this point in input as start of n. */ +#define P_CLOSE 0x90 /* no Analogous to OPEN. */ +/* zl is the range type zrange_t: may be zlong or unsigned long + * char is a single char + * uc* is a pointer to unsigned char, used at run time and initialised + * to NULL. + */ + +/* + * Notes on usage: + * P_WBRANCH: This works like a branch and is used in complex closures, + * to ensure we don't succeed on a zero-length match of the pattern, + * since that would cause an infinite loop. We do this by recording + * the positions where we have already tried to match. See the + * P_WBRANCH test in patmatch(). + * + * P_ANY, P_ANYOF: the operand is a null terminated + * string. Normal characters match as expected. Characters + * in the range Meta+PP_ALPHA..Meta+PP_UNKNWN do the approprate + * Posix range tests. This relies on imeta returning true for these + * characters. We treat unknown POSIX ranges as never matching. + * PP_RANGE means the next two (possibly metafied) characters form + * the limits of a range to test; it's too much like hard work to + * expand the range. + * + * P_EXCLUDE, P_EXCSYNC, PEXCEND: P_EXCLUDE appears in the pattern like + * P_BRANCH, but applies to the immediately preceding branch. The code in + * the corresponding branch is followed by a P_EXCSYNC, which simply + * acts as a marker that a P_EXCLUDE comes next. The P_EXCLUDE + * has a pointer to char embeded in it, which works + * like P_WBRANCH: if we get to the P_EXCSYNC, and we already matched + * up to the same position, fail. Thus we are forced to backtrack + * on closures in the P_BRANCH if the first attempt was excluded. + * Corresponding to P_EXCSYNC in the original branch, there is a + * P_EXCEND in the exclusion. If we get to this point, and we did + * *not* match in the original branch, the exclusion itself fails, + * otherwise it succeeds since we know the tail already matches, + * so P_EXCEND is the end of the exclusion test. + * The whole sorry mess looks like this, where the upper lines + * show the linkage of the branches, and the lower shows the linkage + * of their pattern arguments. + * + * --------------------- ---------------------- + * ^ v ^ v + * ( <BRANCH>:apat-><EXCSYNC> <EXCLUDE>:excpat-><EXCEND> ) tail + * ^ + * | | + * -------------------------------------- + * + * P_EXCLUDP: this behaves exactly like P_EXCLUDE, with the sole exception + * that we prepend the path so far to the exclude pattern. This is + * for top level file globs, e.g. ** / *.c~*foo.c + * ^ I had to leave this space + * P_NUM*: zl is a zlong if that is 64-bit, else an unsigned long. + */ +#define PP_ALPHA 1 +#define PP_ALNUM 2 +#define PP_BLANK 3 +#define PP_CNTRL 4 +#define PP_DIGIT 5 +#define PP_GRAPH 6 +#define PP_LOWER 7 +#define PP_PRINT 8 +#define PP_PUNCT 9 +#define PP_SPACE 10 +#define PP_UPPER 11 +#define PP_XDIGIT 12 +#define PP_UNKWN 13 +#define PP_RANGE 14 + +/* Align everything to the pointer type. */ +typedef char *zalign_t; + +#define P_OP(p) (*(long *)(p) & 0xff) +#define P_NEXT(p) (*(long *)(p) >> 8) +#define P_OPERAND(p) ((p) + sizeof(zalign_t)) +#define P_ISBRANCH(p) (*(long *)(p) & 0x20) +#define P_ISEXCLUDE(p) ((*(long *)(p) & 0x30) == 0x30) +#define P_NOTDOT(p) (*(long *)(p) & 0x40) + +#define P_SIMPLE 0x01 /* Simple enough to be #/## operand. */ +#define P_HSTART 0x02 /* Starts with # or ##'d pattern. */ +#define P_PURESTR 0x04 /* Can be matched with a strcmp */ + +/* + * pointer is end string to be parsed? + * a bit dire because of extendedglob possibilities: + * we need to make sure a ~ at the end of a string isn't mistaken + * for an excluder or lots of emacs users get very cross. + */ +#define ISENDCHAR(X) (!*(X) || ((patflags & PAT_FILE) && *(X) == '/') || \ + *(X) == Bar || *(X) == Outpar || \ + (isset(EXTENDEDGLOB) && \ + (*(X) == Hat || \ + (*(X) == Tilde && \ + !(!(X)[1] || ((patflags & PAT_FILE) && \ + (X)[1] == '/') || \ + (X)[1] == Bar || (X)[1] == Outpar || \ + (X)[1] == Tilde))))) + +/* Next character after one which may be a Meta (x is any char *) */ +#define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) +/* + * Increment pointer which may be on a Meta (x is a pointer variable), + * returning the incremented value (i.e. like pre-increment). + */ +#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1) +/* + * Return unmetafied char from string (x is any char *) + */ +#define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) + +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) +typedef zlong zrange_t; +#else +typedef unsigned long zrange_t; +#endif + +/* Default size for pattern buffer */ +#define P_DEF_ALLOC 256 + +/* Flags used in compilation */ +static char *patstart, *patparse; /* input pointers */ +static int patnpar; /* () count */ +static char *patcode; /* point of code emission */ +static long patsize; /* size of code */ +static char *patout; /* start of code emission string */ +static long patalloc; /* size allocated for same */ + +/* Flags used in both compilation and execution */ +static int patflags; /* flags passed down to patcompile */ +static int patglobflags; /* globbing flags & approx */ + +/* Add n more characters, ensuring there is enough space. */ + +/**/ +static void +patadd(char *add, int ch, long n, int noalgn) +{ + /* Make sure everything gets aligned unless we get noalgn. */ + long newpatsize = patsize + n; + if (!noalgn) + newpatsize = (newpatsize + sizeof(zalign_t) - 1) & + ~(sizeof(zalign_t) - 1); + if (patalloc < newpatsize) { + long newpatalloc = + 2*(newpatsize > patalloc ? newpatsize : patalloc); + patout = (char *)zrealloc((char *)patout, newpatalloc); + patcode = patout + patsize; + patalloc = newpatalloc; + } + patsize = newpatsize; + if (add) { + while (n--) + *patcode++ = *add++; + } else + *patcode++ = ch; + patcode = patout + patsize; +} + +static long rn_offs; +/* operates on poiners, returns a pointer */ +#define PATNEXT(p) ((rn_offs = P_NEXT(p)) ? \ + (P_OP(p) == P_BACK) ? \ + ((p)-rn_offs) : ((p)+rn_offs) : NULL) + +/* Called before parsing a set of file matchs to initialize flags */ + +/**/ +void +patcompstart(void) +{ + patglobflags = 0; +} + +/* Top level pattern compilation subroutine */ + +/**/ +Patprog +patcompile(char *exp, int inflags, char **endexp) +{ + int flags, len; + long startoff; + char *pscan, *lng; + Patprog p; + + DPUTS(sizeof(long) > sizeof(zalign_t), "BUG: patprog alignment too small"); + +#ifdef BACKREFERENCES + startoff = (inflags & PAT_BACKR) ? sizeof(struct patprog) : + sizeof(struct patprog_short); +#else + startoff = sizeof(struct patprog); +#endif + /* Ensure alignment of start of program string */ + startoff = (startoff + sizeof(zalign_t) - 1) & ~(sizeof(zalign_t) - 1); + + /* Allocate reasonable sized chunk if none, reduce size if too big */ + if (patalloc != P_DEF_ALLOC) + patout = (char *)zrealloc(patout, patalloc = P_DEF_ALLOC); + patcode = patout + startoff; + patsize = patcode - patout; + patstart = patparse = exp; + patnpar = 1; + patflags = inflags; + + if (!(patflags & PAT_FILE)) { + remnulargs(exp); + patglobflags = 0; + } + /* + * Have to be set now, since they get updated during compilation. + */ + ((Patprog)patout)->globflags = patglobflags; + + if (patflags & PAT_ANY) + flags = 0; + else if (patcompswitch(0, &flags) == 0) + return NULL; + + /* end of compilation: safe to use pointers */ + p = (Patprog)patout; + p->startoff = startoff; + p->patstartch = '\0'; + p->globend = patglobflags; + p->flags = (patflags & ~PAT_PURES); + p->mustoff = 0; + p->size = patsize; + p->patmlen = 0; + pscan = patout + startoff; + + if (!(patflags & PAT_ANY) && P_OP(PATNEXT(pscan)) == P_END) { + /* only one top level choice */ + pscan = P_OPERAND(pscan); + + if (flags & P_PURESTR) { + /* + * The pattern can be matched with a simple strncmp/strcmp. + * Careful in case we've overwritten the node for the next ptr. + */ + char *dst = patout + startoff, *next; + p->flags |= PAT_PURES; + for (; pscan; pscan = next) { + next = PATNEXT(pscan); + if (P_OP(pscan) == P_EXACTLY) { + char *opnd = P_OPERAND(pscan); + while ((*dst = *opnd++)) + dst++; + } + } + *dst++ = '\0'; + p->size = dst - patout; + /* patmlen is reall strlen, don't include null byte */ + p->patmlen = p->size - startoff - 1; + } else { + /* starting point info */ + if (P_OP(pscan) == P_EXACTLY && !p->globflags) + p->patstartch = *P_OPERAND(pscan); + /* Find the longest literal string in something expensive. + * This is itself not all that cheap if we have case-insensitive + * matching or approximation, so don't. + */ + if ((flags & P_HSTART) && !p->globflags) { + lng = NULL; + len = 0; + for (; pscan; pscan = PATNEXT(pscan)) + if (P_OP(pscan) == P_EXACTLY && + strlen((char *)P_OPERAND(pscan)) >= len) { + lng = P_OPERAND(pscan); + len = strlen((char *)P_OPERAND(pscan)); + } + if (lng) { + p->mustoff = lng - patout; + p->patmlen = len; + } + } + } + } + + /* + * The pattern was compiled in a fixed buffer: unless told otherwise, + * we stick the compiled pattern on the heap. This is necessary + * for files where we will often be compiling multiple segments at once. + */ + if (!(patflags & PAT_STATIC)) { + Patprog newp = (Patprog)zhalloc(patsize); + memcpy((char *)newp, (char *)p, patsize); + p = newp; + } + + if (endexp) + *endexp = patparse; + return p; +} + +/* + * Main body or parenthesised subexpression in pattern + * Parenthesis (and any ksh_glob gubbins) will have been removed. + */ + +/**/ +static long +patcompswitch(int paren, int *flagp) +{ + long starter, br, ender, excsync = 0; +#ifdef BACKREFERENCES + int parno = 0; +#endif + int flags, gfchanged = 0, savflags = patflags, savglobflags = patglobflags; + char *ptr; + + *flagp = 0; + +#ifdef BACKREFERENCES + if (paren && (patflags & PAT_BACKR)) { + /* + * parenthesized: make an open node. + * We can only refer to the first nine parentheses. + * For any others, we just use P_OPEN on its own; there's + * no gain in arbitrarily limiting the number of parentheses. + */ + parno = patnpar >= NSUBEXP ? 0 : patnpar++; + starter = patnode(P_OPEN + parno); + } else +#endif + starter = 0; + + br = patnode(P_BRANCH); + if (!patcompbranch(&flags)) + return 0; + if (patglobflags != savglobflags) + gfchanged++; + if (starter) + pattail(starter, br); + else + starter = br; + + *flagp |= flags & (P_HSTART|P_PURESTR); + + while (*patparse == Bar || + (isset(EXTENDEDGLOB) && + *patparse == Tilde && patparse[1] && patparse[1] != Bar && + patparse[1] != Outpar && patparse[1] != Tilde && + !((patflags & PAT_FILE) && patparse[1] == '/'))) { + int tilde = *patparse++ == Tilde; + long gfnode = 0, newbr; + + *flagp &= ~P_PURESTR; + + if (tilde) { + unsigned char *unull = NULL; + /* excsync remembers the P_EXCSYNC node before a chain of + * exclusions: all point back to this. only the + * original (non-excluded) branch gets a trailing P_EXCSYNC. + */ + if (!excsync) { + excsync = patnode(P_EXCSYNC); + patoptail(br, excsync); + } + /* + * By default, approximations are turned off in exclusions: + * we need to do this here as otherwise the code compiling + * the exclusion doesn't know if the flags have really + * changed if the error count gets restored. + * + * At top level (paren == 0) in a file glob !(patflags &PAT_FILET) + * do the exclusion prepending the file path so far, else don't. + */ + patglobflags &= ~0xff; + br = patnode(!(patflags & PAT_FILET) || paren ? + P_EXCLUDE : P_EXCLUDP); + patadd((char *)&unull, 0, sizeof(unull), 0); + /* / is not treated as special if we are at top level */ + if (!paren) + patflags &= ~PAT_FILE; + } else { + excsync = 0; + br = patnode(P_BRANCH); + /* + * The position of the following statements means globflags + * set in the main branch carry over to the exclusion. + */ + if (!paren) { + patglobflags = 0; + if (((Patprog)patout)->globflags) { + /* + * If at top level, we need to reinitialize flags to zero, + * since (#i)foo|bar only applies to foo and we stuck + * the #i into the global flags. + * We could have done it so that they only got set in the + * first branch, but it's quite convenient having any + * global flags set in the header and not buried in the + * pattern. (Or maybe it isn't and we should + * forget this bit and always stick in an explicit GFLAGS + * statement instead of using the header.) + * Also, this can't happen for file globs where there are + * no top-level |'s. + * + * No gfchanged, as nothing to follow branch at top + * level. + */ + gfnode = patnode(P_GFLAGS); + patadd((char *)&patglobflags, 0, sizeof(long), + 0); + } + } else { + patglobflags = savglobflags; + } + } + newbr = patcompbranch(&flags); + patflags = savflags; + if (!newbr) + return 0; + if (gfnode) + pattail(gfnode, newbr); + if (!tilde && patglobflags != savglobflags) + gfchanged++; + pattail(starter, br); + if (excsync) + patoptail(br, patnode(P_EXCEND)); + *flagp |= flags & P_HSTART; + } + + /* + * Make a closing node, hooking it to the end. + * Note that we can't optimize P_NOTHING out here, since another + * branch at that point would indicate the current choices continue, + * which they don't. + */ +#ifdef BACKREFERENCES + ender = patnode(paren ? (patflags & PAT_BACKR) ? P_CLOSE+parno + : P_NOTHING : P_END); +#else + ender = patnode(paren ? P_NOTHING : P_END); +#endif + pattail(starter, ender); + + /* + * Hook the tails of the branches to the closing node, + * except for exclusions which terminate where they are. + */ + for (ptr = patout + starter; ptr; ptr = PATNEXT(ptr)) + if (!P_ISEXCLUDE(ptr)) + patoptail(ptr-patout, ender); + + /* check for proper termination */ + if ((paren && *patparse++ != Outpar) || + (!paren && *patparse && + !((patflags & PAT_FILE) && *patparse == '/'))) + return 0; + + if (paren && gfchanged) { + /* + * Restore old values of flags when leaving parentheses. + * gfchanged detects a change in any branch (except exclusions + * which are separate), since we need to emit this even if + * a later branch happened to put the flags back. + */ + pattail(ender, patnode(P_GFLAGS)); + patglobflags = savglobflags; + patadd((char *)&savglobflags, 0, sizeof(long), 0); + } + + return starter; +} + +/* + * Compile something ended by Bar, Outpar, Tilde, or end of string. + * Note the BRANCH or EXCLUDE tag must already have been omitted: + * this returns the position of the operand of that. + */ + +/**/ +static long +patcompbranch(int *flagp) +{ + long chain, latest, starter; + int flags; + + *flagp = P_PURESTR; + + starter = chain = 0; + while (*patparse && !((patflags & PAT_FILE) && *patparse == '/') && + *patparse != Bar && *patparse != Outpar && + (!isset(EXTENDEDGLOB) || *patparse != Tilde || + !patparse[1] || patparse[1] == Bar || patparse[1] == Outpar + || patparse[1] == Tilde || + ((patflags & PAT_FILE) && patparse[1] == '/'))) { + if (isset(EXTENDEDGLOB) && + ((!isset(SHGLOB) && + (*patparse == Inpar && patparse[1] == Pound)) || + (isset(KSHGLOB) && *patparse == '@' && patparse[1] == Inpar && + patparse[2] == Pound))) { + /* Globbing flags. */ + char *pp1 = patparse; + int oldglobflags = patglobflags; + patparse += (*patparse == '@') ? 3 : 2; + if (!patgetglobflags(&patparse)) + return 0; + if (pp1 == patstart) { + /* Right at start of pattern, the simplest case. + * Put them into the flags and don't emit anything. + */ + ((Patprog)patout)->globflags = patglobflags; + continue; + } else if (!*patparse) { + /* Right at the end, so just leave the flags for + * the next Patprog in the chain to pick up. + */ + break; + } + /* + * Otherwise, we have to stick them in as a pattern + * matching nothing. + */ + if (oldglobflags != patglobflags) { + /* Flags changed */ + latest = patnode(P_GFLAGS); + patadd((char *)&patglobflags, 0, sizeof(int), 0); + } else { + /* No effect. */ + continue; + } + } else if (isset(EXTENDEDGLOB) && *patparse == Hat) { + /* + * ^pat: anything but pat. For proper backtracking, + * etc., we turn this into (*~pat), except without the + * parentheses. + */ + patparse++; + latest = patcompnot(0, &flags); + } else + latest = patcomppiece(&flags); + if (!latest) + return 0; + if (!starter) + starter = latest; + if (!(flags & P_PURESTR)) + *flagp &= ~P_PURESTR; + if (!chain) + *flagp |= flags & P_HSTART; + else + pattail(chain, latest); + chain = latest; + } + /* check if there was nothing in the loop, i.e. () */ + if (!chain) + starter = patnode(P_NOTHING); + + return starter; +} + +/* get glob flags, return 1 for success, 0 for failure */ + +/**/ +int +patgetglobflags(char **strp) +{ + char *nptr, *ptr = *strp; + zlong ret; + /* (#X): assumes we are still positioned on the first X */ + for (; *ptr && *ptr != Outpar; ptr++) { + switch (*ptr) { + case 'a': + /* Approximate matching, max no. of errors follows */ + ret = zstrtol(++ptr, &nptr, 10); + /* + * We can't have more than 254, because we need 255 to + * mark 254 errors in wbranch and exclude sync strings + * (hypothetically --- hope no-one tries it). + */ + if (ret < 0 || ret > 254 || ptr == nptr) + return 0; + patglobflags = (patglobflags & ~0xff) | (ret & 0xff); + ptr = nptr-1; + break; + + case 'l': + /* Lowercase in pattern matches lower or upper in target */ + patglobflags = (patglobflags & ~C_IGNCASE) | C_LCMATCHUC; + break; + + case 'i': + /* Fully case insensitive */ + patglobflags = (patglobflags & ~C_LCMATCHUC) | C_IGNCASE; + break; + + case 'I': + /* Restore case sensitivity */ + patglobflags &= ~(C_LCMATCHUC|C_IGNCASE); + break; + + default: + return 0; + } + } + if (*ptr != Outpar) + return 0; + *strp = ptr + 1; + return 1; +} + +/* + * compile a chunk such as a literal string or a [...] followed + * by a possible hash operator + */ + +/**/ +static long +patcomppiece(int *flagp) +{ + long starter, next, pound, op; + int flags, kshchar; + unsigned char *ptr; + + starter = patcompatom(&flags, &kshchar); + if (!starter) + return 0; + + if (!(pound = (*patparse == Pound && isset(EXTENDEDGLOB))) && + (kshchar <= 0 || kshchar == '@' || kshchar == '!')) { + *flagp = flags; + return starter; + } + + /* too much at once doesn't currently work */ + if (kshchar && pound) + return 0; + + if (kshchar == '*') { + op = P_ONEHASH; + *flagp = P_HSTART; + } else if (kshchar == '+') { + op = P_TWOHASH; + *flagp = P_HSTART; + } else if (kshchar == '?') { + op = 0; + *flagp = 0; + } else if (*++patparse == Pound) { + op = P_TWOHASH; + patparse++; + *flagp = P_HSTART; + } else { + op = P_ONEHASH; + *flagp = P_HSTART; + } + + /* + * Note optimizations with pointers into P_NOTHING branches: some + * should logically point to next node after current piece. + * + * Backtracking is also encoded in a slightly obscure way: the + * code emitted ensures we test the non-empty branch of complex + * patterns before the empty branch on each repetition. Hence + * each time we fail on a non-empty branch, we try the empty branch, + * which is equivalent to backtracking. + */ + if ((flags & P_SIMPLE) && op == P_ONEHASH && + P_OP(patout+starter) == P_ANY) { + /* Optimize ?# to *. Silly thing to do, since who would use + * use ?# ? But it makes the later code shorter. + */ + long *lptr = (long *)(patout + starter); + *lptr = (*lptr & ~0xff) | P_STAR; + } else if ((flags & P_SIMPLE) && op && !(patglobflags & 0xff)) { + /* Don't simplify if we need to look for approximations. */ + patinsert(op, starter, NULL, 0); + } else if (op == P_ONEHASH) { + /* Emit x# as (x&|), where & means "self". */ + ptr = NULL; + patinsert(P_WBRANCH, starter, (char *)&ptr, sizeof(ptr)); + /* Either x */ + patoptail(starter, patnode(P_BACK)); /* and loop */ + patoptail(starter, starter); /* back */ + pattail(starter, patnode(P_BRANCH)); /* or */ + pattail(starter, patnode(P_NOTHING)); /* null. */ + } else if (op == P_TWOHASH) { + /* Emit x## as x(&|) where & means "self". */ + next = patnode(P_WBRANCH); /* Either */ + ptr = NULL; + patadd((char *)&ptr , 0, sizeof(ptr), 0); + pattail(starter, next); + pattail(patnode(P_BACK), starter); /* loop back */ + pattail(next, patnode(P_BRANCH)); /* or */ + pattail(starter, patnode(P_NOTHING)); /* null. */ + } else if (kshchar == '?') { + /* Emit ?(x) as (x|) */ + patinsert(P_BRANCH, starter, NULL, 0); /* Either x */ + pattail(starter, patnode(P_BRANCH)); /* or */ + next = patnode(P_NOTHING); /* null */ + pattail(starter, next); + patoptail(starter, next); + } + if (*patparse == Pound) + return 0; + + return starter; +} + +/* + * Parse lowest level pattern. If doing ordinary characters, we + * gobble a complete string as far as we can get. + * kshcharp returns a character found before an Inpar, for handling + * as a closure. + */ + +/**/ +static long +patcompatom(int *flagp, int *kshcharp) +{ + long starter; + int patch, flags, len, ch; + char *nptr, *str0, cbuf[2]; + zrange_t from, to; + + *flagp = 0; + str0 = patparse; + for (;;) { + /* check kshglob here */ + *kshcharp = '\0'; + if (isset(KSHGLOB) && *patparse && patparse[1] == Inpar) { + if (strchr("?*+!@", (char)*patparse)) + *kshcharp = STOUC(*patparse); + else if (*patparse == Star || *patparse == Quest) + *kshcharp = STOUC(ztokens[*patparse - Pound]); + } + + if (patparse > str0) { + /* + * This is up here instead of at the end to simplify the + * kshglob bracket testing. Note patparse doesn't + * get incremented till afterwards. + */ + if (ISENDCHAR(patparse) || *kshcharp || *patparse == Quest || + *patparse == Star || *patparse == Inbrack || + (*patparse == Inpar && !isset(SHGLOB)) || + *patparse == Inang || + (isset(EXTENDEDGLOB) && *patparse == Pound)) + break; + else { + METAINC(patparse); + continue; + } + } + + if (*kshcharp) + patparse++; + + patch = *patparse; + METAINC(patparse); + switch(patch) { + case Quest: + *flagp |= P_SIMPLE; + return patnode(P_ANY); + break; + case Star: + /* kshchar is used as a sign that we can't have #'s. */ + *kshcharp = -1; + return patnode(P_STAR); + break; + case Inbrack: + *flagp |= P_SIMPLE; + if (*patparse == Hat || *patparse == '^' || *patparse == '!') { + patparse++; + starter = patnode(P_ANYBUT); + } else + starter = patnode(P_ANYOF); + if (*patparse == Outbrack) { + patparse++; + patadd(NULL, ']', 1, 1); + } + while (*patparse && *patparse != Outbrack) { + /* Meta is not a token */ + if (*patparse == Inbrack && patparse[1] == ':' && + (nptr = strchr(patparse+2, ':')) && + nptr[1] == Outbrack) { + /* Posix range. */ + patparse += 2; + len = nptr - patparse; + if (!strncmp(patparse, "alpha", len)) + ch = PP_ALPHA; + else if (!strncmp(patparse, "alnum", len)) + ch = PP_ALNUM; + else if (!strncmp(patparse, "blank", len)) + ch = PP_BLANK; + else if (!strncmp(patparse, "cntrl", len)) + ch = PP_CNTRL; + else if (!strncmp(patparse, "digit", len)) + ch = PP_DIGIT; + else if (!strncmp(patparse, "graph", len)) + ch = PP_GRAPH; + else if (!strncmp(patparse, "lower", len)) + ch = PP_LOWER; + else if (!strncmp(patparse, "print", len)) + ch = PP_PRINT; + else if (!strncmp(patparse, "punct", len)) + ch = PP_PUNCT; + else if (!strncmp(patparse, "space", len)) + ch = PP_SPACE; + else if (!strncmp(patparse, "upper", len)) + ch = PP_UPPER; + else if (!strncmp(patparse, "xdigit", len)) + ch = PP_XDIGIT; + else + ch = PP_UNKWN; + patparse = nptr + 2; + if (ch != PP_UNKWN) + patadd(NULL, STOUC(Meta+ch), 1, 1); + continue; + } + if (itok(*patparse)) { + cbuf[0] = ztokens[*patparse - Pound]; + } else if (*patparse == Meta) { + cbuf[0] = Meta; + cbuf[1] = *++patparse; + } else + cbuf[0] = *patparse; + patparse++; + + if (*patparse == '-' && patparse[1] != Outbrack) { + patadd(NULL, STOUC(Meta+PP_RANGE), 1, 1); + patadd(cbuf, 0, (cbuf[0] == Meta) ? 2 : 1, 1); + if (itok(*++patparse)) { + patadd(0, STOUC(ztokens[*patparse - Pound]), 1, 1); + } else + patadd(patparse, 0, (*patparse == Meta) ? 2 : 1, 1); + METAINC(patparse); + } else + patadd(cbuf, 0, (cbuf[0] == Meta) ? 2 : 1, 1); + } + if (*patparse != Outbrack) + return 0; + patparse++; + /* terminate null string and fix alignment */ + patadd(NULL, 0, 1, 0); + return starter; + break; + case Inpar: + /* is this how to treat parentheses in SHGLOB? */ + if (isset(SHGLOB) && !*kshcharp) + return 0; + if (*kshcharp == '!') { + /* This is nasty, we should really either handle all + * kshglobbing upstairs or down here. But most of the + * others look like non-ksh patterns, while this one + * doesn't, so we handle it here and leave the rest. + * We treat it like an extendedglob ^, except that + * it goes into parentheses. + * + * If we did do kshglob here, we could support + * the old behaviour that things like !(foo)## + * work, but it makes the code more complicated at + * the expense of allowing the user to do things + * they shouldn't. + */ + if (!(starter = patcompnot(1, &flags))) + return 0; + } else if (!(starter = patcompswitch(1, &flags))) + return 0; + *flagp |= flags & P_HSTART; + return starter; + break; + case Inang: + /* Numeric glob */ + len = 0; /* beginning present 1, end present 2 */ + if (idigit(*patparse)) { + from = (zrange_t) zstrtol((char *)patparse, + (char **)&nptr, 10); + patparse = nptr; + len |= 1; + } + if (*patparse == '-') { + patparse++; + if (idigit(*patparse)) { + to = (zrange_t) zstrtol((char *)patparse, + (char **)&nptr, 10); + patparse = nptr; + len |= 2; + } + } + if (*patparse != Outang) + return 0; + patparse++; + starter = 0; /* shut compiler up */ + switch(len) { + case 3: + starter = patnode(P_NUMRNG); + patadd((char *)&from, 0, sizeof(from), 0); + patadd((char *)&to, 0, sizeof(to), 0); + break; + case 2: + starter = patnode(P_NUMTO); + patadd((char *)&to, 0, sizeof(to), 0); + break; + case 1: + starter = patnode(P_NUMFROM); + patadd((char *)&from, 0, sizeof(from), 0); + break; + case 0: + starter = patnode(P_NUMANY); + break; + } + /* This can't be simple, because it isn't. + * Mention in manual that matching digits with [...] + * is more efficient. + */ + return starter; + break; + case Pound: + if (!isset(EXTENDEDGLOB)) + break; + return 0; + break; +#ifdef DEBUG + case Bar: + case Outpar: + case '\0': + dputs("BUG: wrong character in patcompatom."); + return 0; + break; +#endif + } + } + + /* Simple string: cancel kshchar lookahead */ + *kshcharp = '\0'; + /* + * Assume it matches a simple string until we find otherwise. + */ + *flagp |= P_PURESTR; + DPUTS(patparse == str0, "BUG: matched nothing in patcompatom."); + /* more than one character matched? */ + len = str0 + (*str0 == Meta ? 2 : 1) < patparse; + /* + * Ordinary string of characters. + * If we have more than one character, a following hash only + * applies to the last, so decrement. + */ + if (isset(EXTENDEDGLOB) && *patparse == Pound && len) + patparse -= (patparse > str0 + 1 && patparse[-2] == Meta) ? 2 : 1; + /* if len is 1, we can't have an active # following, so doesn't + * matter that we don't make X in `XX#' simple. + */ + if (!len) + *flagp |= P_SIMPLE; + starter = patnode(P_EXACTLY); + while (str0 < patparse) { + if (*str0 == Meta) { + cbuf[0] = *str0++; + cbuf[1] = *str0++; + } else { + cbuf[0] = itok(*str0) ? ztokens[*str0 - Pound] : *str0; + str0++; + } + ch = UNMETA(cbuf); + /* + * HACK: this cause a string consisting of any number of + * dots in files to be matched exactly, even with approximation. + * We just want to limit it to the first two. + */ + if (((patglobflags & C_IGNCASE) && (islower(ch) || isupper(ch))) || + ((patglobflags & C_LCMATCHUC) && islower(ch)) || + ((patglobflags & 0xff) && !((patflags & PAT_FILE) && ch == '.'))) + *flagp &= ~P_PURESTR; + patadd(cbuf, 0, (cbuf[0] == Meta) ? 2 : 1, 1); + } + /* null terminate and fix alignment */ + patadd(NULL, 0, 1, 0); + return starter; +} + +/* + * Turn a ^foo (paren = 0) or !(foo) (paren = 1) into *~foo with + * parentheses if necessary. As you see, that's really quite easy. + */ + +/**/ +static long +patcompnot(int paren, int *flagsp) +{ + unsigned char *unull = NULL; + long excsync, br, excl, n, starter; + int dummy; + + /* Here, we're matching a star at the start. */ + *flagsp = P_HSTART; + + starter = patnode(P_BRANCH); + br = patnode(P_STAR); + excsync = patnode(P_EXCSYNC); + pattail(br, excsync); + pattail(starter, excl = patnode(P_EXCLUDE)); + patadd((char *)&unull, 0, sizeof(unull), 0); + if (!(br = (paren ? patcompswitch(1, &dummy) : patcompbranch(&dummy)))) + return 0; + pattail(br, patnode(P_EXCEND)); + n = patnode(P_NOTHING); /* just so much easier */ + pattail(excsync, n); + pattail(excl, n); + + return starter; +} + +/* Emit a node */ + +/**/ +static long +patnode(long op) +{ + long starter = patcode - patout; + + patadd((char *)&op, 0, sizeof(long), 0); + return starter; +} + +/* + * insert an operator in front of an already emitted operand: + * we relocate the operand. there had better be nothing else after. + */ + +/**/ +static void +patinsert(long op, int opnd, char *xtra, int sz) +{ + char *src, *dst, *opdst; + long buf, *lptr; + + buf = 0; + patadd((char *)&buf, 0, sizeof(long), 0); + if (sz) + patadd(xtra, 0, sz, 0); + src = patcode - sizeof(long) - sz; + dst = patcode; + opdst = patout + opnd; + while (src > opdst) + *--dst = *--src; + + /* A cast can't be an lvalue */ + lptr = (long *)opdst; + *lptr = op; + opdst += sizeof(long); + while (sz--) + *opdst++ = *xtra++; +} + +/* set the 'next' pointer at the end of a node chain */ + +/**/ +static void +pattail(long p, long val) +{ + char *scan, *temp; + long offset, *lptr; + + scan = patout + p; + for (;;) { + if (!(temp = PATNEXT(scan))) + break; + scan = temp; + } + + offset = (P_OP(scan) == P_BACK) + ? (scan - patout) - val : val - (scan - patout); + + lptr = (long *)scan; + *lptr |= offset << 8; +} + +/* do pattail, but on operand of first argument; nop if operandless */ + +/**/ +static void patoptail(long p, long val) +{ + char *ptr = patout + p; + int op = P_OP(ptr); + if (!p || !P_ISBRANCH(ptr)) + return; + if (op == P_BRANCH) + pattail(P_OPERAND(p), val); + else + pattail(P_OPERAND(p) + sizeof(char *), val); +} + + +/* + * Run a pattern. + */ +static char *patinstart; /* Start of input string */ + +/**/ +char *patinput; /* String input pointer */ + +/* Length of input string, plus null byte, if needed */ +static int patinlen; +#ifdef BACKREFERENCES +static char **patstartp; /* Pointer to backref starts */ +static char **patendp; /* Pointer to backref ends */ +static int parsfound; /* parentheses found */ +#endif +static int globdots; /* Glob initial dots? */ + +/* + * The following need to be accessed in the globbing scanner for + * a multi-component file path. See horror story there. + */ +/**/ +int errsfound; /* Total error count so far */ + +/**/ +int forceerrs; /* Forced maximum error count */ + +/**/ +void +pattrystart(void) +{ + forceerrs = -1; + errsfound = 0; +} + +/**/ +int +pattry(Patprog prog, char *string) +{ +#ifdef BACKREFERENCES + int i; + char **sp, **ep; +#endif + char *progstr = (char *)prog + prog->startoff; + + /* inherited from domatch, but why, exactly? */ + if (*string == Nularg) + string++; + + patinstart = patinput = string; + + if (prog->flags & (PAT_PURES|PAT_ANY)) { + if ((prog->flags & PAT_ANY) || + ((prog->flags & PAT_NOANCH) ? + !strncmp(progstr, string, prog->patmlen) + : !strcmp(progstr, string))) { + /* in case used for ${..#..} etc. */ + patinput = string + prog->patmlen; + /* if matching files, must update globbing flags */ + patglobflags = prog->globend; + return 1; + } else + return 0; + } else { + /* + * Test for a `must match' string, unless we're scanning for a match + * in which case we don't need to do this each time. + */ + if (!(prog->flags & PAT_SCAN) && prog->mustoff && + !strstr(string, (char *)prog + prog->mustoff)) + return 0; + + patinlen = 0; /* don't calculate length unless needed */ + patflags = prog->flags; + patglobflags = prog->globflags; + if (!(patflags & PAT_FILE)) { + forceerrs = -1; + errsfound = 0; + } + globdots = !(patflags & PAT_NOGLD); +#ifdef BACKREFERENCES + parsfound = 0; + if (patflags & PAT_BACKR) { + patstartp = prog->ppStartp; + patendp = prog->ppEndp; + } else { + patstartp = patendp = NULL; + } +#endif + + if (patmatch(progstr)) { +#ifdef BACKREFERENCES + if (patflags & PAT_BACKR) { + prog->ppStartp[0] = string; + prog->ppEndp[0] = patinput; + + sp = patstartp+1; + ep = patendp + 1; + for (i = 1; i < NSUBEXP; i++) { + if (!(parsfound & (1 << (i - 1)))) + *sp = 0; + if (!(parsfound & (1 << (i + 15)))) + *ep = 0; + sp++; + ep++; + } + + } +#endif + /* + * we were lazy and didn't save the globflags if an exclusion + * failed, so set it now + */ + patglobflags = prog->globend; + return 1; + } else + return 0; + } +} + +/* + * Match literal characters with case insensitivity test: the first + * comes from the input string, the second the current pattern. + */ +#define CHARMATCH(chin, chpa) (chin == chpa || \ + ((patglobflags & C_IGNCASE) ? \ + ((isupper(chin) ? tolower(chin) : chin) == \ + (isupper(chpa) ? tolower(chpa) : chpa)) : \ + (patglobflags & C_LCMATCHUC) ? \ + (islower(chpa) && toupper(chpa) == chin) : 0)) + +/* + * exactpos is used to remember how far down an exact string we have + * matched, if we are doing approximation and can therefore redo from + * the same point; we never need to otherwise. + */ +static char *exactpos; + +/* + * Main matching routine. + * + * Testing the tail end of a match is usually done by recursion, but + * we try to eliminate that in favour of looping for simple cases. + */ + +/**/ +int +patmatch(char *prog) +{ + /* Current and next nodes */ + char *scan = prog, *next, *opnd, *save, *start; + int savglobflags, op, no, min, nextch, fail = 0, saverrsfound; + zrange_t from, to, comp; + + while (scan) { + next = PATNEXT(scan); + + if (!globdots && P_NOTDOT(scan) && patinput == patinstart && + *patinput == '.') + return 0; + + switch (P_OP(scan)) { + case P_ANY: + if (!*patinput) + fail = 1; + else + METAINC(patinput); + break; + case P_EXACTLY: + /* + * acts as nothing if *opnd is null: this is used by + * approx code. + */ + opnd = exactpos ? exactpos : P_OPERAND(scan); + exactpos = NULL; + while (*opnd && *patinput) { + int chin = STOUC(UNMETA(patinput)); + int chpa = STOUC(UNMETA(opnd)); + if (!CHARMATCH(chin, chpa)) { + fail = 1; + break; + } + METAINC(opnd); + METAINC(patinput); + } + if (*opnd) { + exactpos = opnd; + fail = 1; + } + break; + case P_ANYOF: + if (!*patinput || + !patmatchrange(P_OPERAND(scan), STOUC(UNMETA(patinput)))) + fail = 1; + else + METAINC(patinput); + break; + case P_ANYBUT: + if (!*patinput || + patmatchrange(P_OPERAND(scan), STOUC(UNMETA(patinput)))) + fail = 1; + else + METAINC(patinput); + break; + case P_NUMRNG: + case P_NUMFROM: + case P_NUMTO: + /* + * To do this properly, we really have to treat numbers as + * closures: that's so things like like <1-1000>33 will + * match 633 (they didn't up to 3.1.6). To avoid making this + * too inefficient, we see if there's an exact match next: + * if there is, and it's not a digit, we return 1 after + * the first attempt. + */ + op = P_OP(scan); + start = P_OPERAND(scan); + from = to = 0; + if (op != P_NUMTO) { +#ifdef ZSH_64_BIT_TYPE + /* We can't rely on pointer alignment being good enough. */ + memcpy((char *)&from, (char *)start, sizeof(zrange_t)); +#else + from = *((zrange_t *) start); +#endif + start += sizeof(zrange_t); + } + if (op != P_NUMFROM) { +#ifdef ZSH_64_BIT_TYPE + memcpy((char *)&to, start, sizeof(zrange_t)); +#else + to = *((zrange_t *) start); +#endif + } + start = patinput; + comp = (zrange_t) zstrtol(patinput, &save, 10); + patinput = save; + no = 0; + while (patinput > start) { + /* if already too small, no power on earth can save it */ + if (comp < from) + break; + if ((op == P_NUMFROM || comp <= to) && patmatch(next)) { + return 1; + } + if (!no && P_OP(next) == P_EXACTLY && + !idigit(STOUC(*P_OPERAND(next))) && !(patglobflags & 0xff)) + return 0; + patinput = --save; + no++; + comp /= 10; + } + patinput = start; + fail = 1; + break; + case P_NUMANY: + /* This is <->: any old set of digits, don't bother comparing */ + start = patinput; + while (idigit(STOUC(*patinput))) + patinput++; + save = patinput; + no = 0; + while (patinput > start) { + if (patmatch(next)) + return 1; + if (!no && P_OP(next) == P_EXACTLY && + !idigit(STOUC(*P_OPERAND(next))) && !(patglobflags & 0xff)) + return 0; + patinput = --save; + no++; + } + patinput = start; + fail = 1; + break; + case P_NOTHING: + break; + case P_BACK: + break; + case P_GFLAGS: + patglobflags = *(int *)P_OPERAND(scan); + break; +#ifdef BACKREFERENCES + case P_OPEN: + case P_OPEN+1: + case P_OPEN+2: + case P_OPEN+3: + case P_OPEN+4: + case P_OPEN+5: + case P_OPEN+6: + case P_OPEN+7: + case P_OPEN+8: + case P_OPEN+9: + no = P_OP(scan) - P_OPEN; + save = patinput; + + if (patmatch(next)) { + DPUTS(!patstartp, "patstartp not set for backreferencing"); + /* + * Don't set ppStartp if some later invocation of + * the same parentheses already has. + */ + if (no && !(parsfound & (1 << (no - 1)))) { + patstartp[no] = save; + parsfound |= 1 << (no - 1); + } + return 1; + } else + return 0; + break; + case P_CLOSE: + case P_CLOSE+1: + case P_CLOSE+2: + case P_CLOSE+3: + case P_CLOSE+4: + case P_CLOSE+5: + case P_CLOSE+6: + case P_CLOSE+7: + case P_CLOSE+8: + case P_CLOSE+9: + no = P_OP(scan) - P_CLOSE; + save = patinput; + + if (patmatch(next)) { + DPUTS(!patendp, "patendp not set for backreferencing"); + if (no && !(parsfound & (1 << (no + 15)))) { + patendp[no] = save; + parsfound |= 1 << (no + 15); + } + return 1; + } else + return 0; + break; +#endif + case P_EXCSYNC: + /* See the P_EXCLUDE code below for where syncstrp comes from */ + { + unsigned char **syncstrp, *syncptr; + char *after; + after = P_OPERAND(scan); + DPUTS(!P_ISEXCLUDE(after), + "BUG: EXCSYNC not followed by EXCLUDE."); + syncstrp = (unsigned char **)P_OPERAND(after); + DPUTS(!*syncstrp, "BUG: EXCSYNC not handled by EXCLUDE"); + syncptr = *syncstrp + (patinput - patinstart); + /* + * If we already matched from here, this time we fail. + * See WBRANCH code for story about error count. + */ + if (*syncptr && errsfound + 1 >= *syncptr) + return 0; + /* + * Else record that we (possibly) matched this time. + * No harm if we don't: then the previous test will just + * short cut the attempted match that is bound to fail. + * We never try to exclude something that has already + * failed anyway. + */ + *syncptr = errsfound + 1; + } + break; + case P_EXCEND: + /* + * This is followed by a P_EXCSYNC, but only in the P_EXCLUDE + * branch. Actually, we don't bother following it: all we + * need to know is that we successfully matched so far up + * to the end of the asserted pattern; the endpoint + * in the target string is nulled out. + */ + if (!(fail = (*patinput != '\0'))) + return 1; + break; + case P_BRANCH: + case P_WBRANCH: + /* P_EXCLUDE shouldn't occur without a P_BRANCH */ + if (!P_ISBRANCH(next)) { + /* no choice, avoid recursion */ + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: WBRANCH with no alternative."); + next = P_OPERAND(scan); + } else { + do { + save = patinput; + savglobflags = patglobflags; + saverrsfound = errsfound; + if (P_ISEXCLUDE(next)) { + /* + * The strategy is to test the asserted pattern, + * recording via P_EXCSYNC how far the part to + * be excluded matched. We then null out that + * point and see if the exclusion as far as + * P_EXCEND also matches that string. + * We need to keep testing the asserted pattern + * by backtracking, since the first attempt + * may be excluded while a later attempt may not. + * For this we keep a pointer just after + * the P_EXCLUDE which is tested by the P_EXCSYNC + * to see if we matched there last time, in which + * case we fail. If there is nothing to backtrack + * over, that doesn't matter: we should fail anyway. + * The pointer also tells us where the asserted + * pattern matched for use by the exclusion. + */ + unsigned char **syncstrp, *oldsyncstr; + char *matchpt = NULL; + int ret, savglobdots, matchederrs = 0; +#ifdef BACKREFERENCES + int savparsfound = parsfound; +#endif + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: excluded WBRANCH"); + syncstrp = (unsigned char **)P_OPERAND(next); + /* + * Unlike WBRANCH, each test at the same exclude + * sync point (due to an external loop) is separate, + * i.e testing (foo~bar)# is no different from + * (foo~bar)(foo~bar)... from the exclusion point + * of view, so we use a different sync string. + */ + oldsyncstr = *syncstrp; + if (!patinlen) + patinlen = strlen(patinstart)+1; + *syncstrp = (unsigned char *)zcalloc(patinlen); + while ((ret = patmatch(P_OPERAND(scan)))) { + unsigned char *syncpt; + char savchar, *testptr; + int savforce = forceerrs; + forceerrs = -1; + savglobdots = globdots; + matchederrs = errsfound; + matchpt = patinput; /* may not be end */ + globdots = 1; /* OK to match . first */ + /* Find the point where the scan + * matched the part to be excluded: because + * of backtracking, the one + * most recently matched will be the first. + * (Luckily, backtracking is done after all + * possibilities for approximation have been + * checked.) + */ + for (syncpt = *syncstrp; !*syncpt; syncpt++) + ; + testptr = patinstart + (syncpt - *syncstrp); + DPUTS(testptr > matchpt, "BUG: EXCSYNC failed"); + savchar = *testptr; + *testptr = '\0'; + next = PATNEXT(scan); + while (next && P_ISEXCLUDE(next)) { + char *buf = NULL; + patinput = save; + /* + * turn off approximations in exclusions: + * note we keep remaining patglobflags + * set by asserted branch (or previous + * excluded branches, for consistency). + */ + patglobflags &= ~0xff; + errsfound = 0; + opnd = P_OPERAND(next) + sizeof(char *); + if (P_OP(next) == P_EXCLUDP && pathpos) { + /* + * top level exclusion with a file, + * applies to whole path so add the + * segments already matched + */ + DPUTS(patinput != patinstart, + "BUG: not at start excluding path"); + buf = (char *) + zalloc(pathpos + patinlen); + strcpy(buf, pathbuf); + strcpy(buf + pathpos, patinput); + patinput = buf; + } + if (patmatch(opnd)) { + ret = 0; +#ifdef BACKREFERENCES + /* + * Another subtlety: if we exclude the + * match, any parentheses just found + * become invalidated. + */ + parsfound = savparsfound; +#endif + } + if (buf) + zfree(buf, pathpos + patinlen); + if (!ret) + break; + next = PATNEXT(next); + } + *testptr = savchar; + globdots = savglobdots; + forceerrs = savforce; + if (ret) + break; + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + } + zfree((char *)*syncstrp, patinlen); + *syncstrp = oldsyncstr; + if (ret) { + patinput = matchpt; + errsfound = matchederrs; + return 1; + } + while ((scan = PATNEXT(scan)) && + P_ISEXCLUDE(scan)) + ; + } else { + int ret = 1, pfree = 0; + unsigned char **ptrp = NULL, *ptr; + if (P_OP(scan) == P_WBRANCH) { + /* + * This is where we make sure that we are not + * repeatedly matching zero-length strings in + * a closure, which would cause an infinite loop, + * and also remove exponential behaviour in + * backtracking nested closures. + * The P_WBRANCH operator leaves a space for a + * uchar *, initialized to NULL, which is + * turned into a string the same length as the + * target string. Every time we match from a + * particular point in the target string, we + * stick a 1 at the corresponding point here. + * If we come round to the same branch again, and + * there is already a 1, then the test fails. + */ + opnd = P_OPERAND(scan); + ptrp = (unsigned char **)opnd; + opnd += sizeof(unsigned char *); + if (!*ptrp) { + if (!patinlen) + patinlen = strlen((char *)patinstart)+1; + *ptrp = (unsigned char *)zcalloc(patinlen); + pfree = 1; + } + ptr = *ptrp + (patinput - patinstart); + + /* + * Without approximation, this is just a + * single bit test. With approximation, we + * need to know how many errors there were + * last time we made the test. If errsfound + * is now smaller than it was, hence we can + * maker more approximations in the remaining + * code, we continue with the test. + * (This is why the max number of errors is + * 254, not 255.) + */ + if (*ptr && errsfound + 1 >= *ptr) + ret = 0; + *ptr = errsfound + 1; + } else + opnd = P_OPERAND(scan); + if (ret) + ret = patmatch(opnd); + if (pfree) { + zfree((char *)*ptrp, patinlen); + *ptrp = NULL; + } + if (ret) + return 1; + scan = PATNEXT(scan); + } + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: WBRANCH not first choice."); + next = PATNEXT(scan); + } while (scan && P_ISBRANCH(scan)); + return 0; + } + break; + case P_STAR: + /* Handle specially for speed, although really P_ONEHASH+P_ANY */ + case P_ONEHASH: + case P_TWOHASH: + /* + * This is just simple cases, matching one character. + * With approximations, we still handle * this way, since + * no approximation is ever necessary, but other closures + * are handled by the more compicated branching method + */ + op = P_OP(scan); + /* Note that no counts possibly metafied characters */ + start = patinput; + if (op == P_STAR) { + for (no = 0; *patinput; METAINC(patinput)) + no++; + /* simple optimization for reasonably common case */ + if (P_OP(next) == P_END) + return 1; + } else { + DPUTS(patglobflags & 0xff, + "BUG: wrong backtracking with approximation."); + if (!globdots && P_NOTDOT(P_OPERAND(scan)) && + patinput == patinstart && *patinput == '.') + return 0; + no = patrepeat(P_OPERAND(scan)); + } + /* Lookahead to avoid useless matches */ + if (P_OP(next) == P_EXACTLY && !(patglobflags & 0xff)) + nextch = STOUC(UNMETA(P_OPERAND(next))); + else + nextch = -1; + min = (op == P_TWOHASH) ? 1 : 0; + save = patinput; + savglobflags = patglobflags; + saverrsfound = errsfound; + while (no >= min) { + int chin; + if ((nextch < 0 || (chin = STOUC(UNMETA(patinput)), + CHARMATCH(chin, nextch))) && + patmatch(next)) + return 1; + no--; + save--; + if (save > start && save[-1] == Meta) + save--; + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + } + /* + * As with branches, the patmatch(next) stuff for * + * handles approximation, so we don't need to try + * anything here. + */ + return 0; + case P_END: + if (!(fail = (*patinput && !(patflags & PAT_NOANCH)))) + return 1; + break; +#ifdef DEBUG + default: + dputs("BUG: bad operand in patmatch."); + return 0; + break; +#endif + } + + if (fail) { + if (errsfound < (patglobflags & 0xff) && + (forceerrs == -1 || errsfound < forceerrs)) { + /* + * Approximation code. There are four possibilities + * + * 1. omit character from input string + * 2. transpose characters in input and pattern strings + * 3. omit character in both input and pattern strings + * 4. omit character from pattern string. + * + * which we try in that order. + * + * Of these, 2, 3 and 4 require an exact match string + * (P_EXACTLY) while 1, 2 and 3 require that we not + * have reached the end of the input string. + * + * Note in each case after making the approximation we + * need to retry the *same* pattern; this is what + * requires exactpos, a slightly doleful way of + * communicating with the exact character matcher. + */ + char *savexact = exactpos; + save = patinput; + savglobflags = patglobflags; + saverrsfound = ++errsfound; + fail = 0; + + DPUTS(P_OP(scan) != P_EXACTLY && exactpos, + "BUG: non-exact match has set exactpos"); + + /* Try omitting a character from the input string */ + if (*patinput) { + METAINC(patinput); + /* If we are not on an exact match, then this is + * our last gasp effort, so we can optimize out + * the recursive call. + */ + if (P_OP(scan) != P_EXACTLY) + continue; + if (patmatch(scan)) + return 1; + } + + if (P_OP(scan) == P_EXACTLY) { + char *nextexact = savexact; + DPUTS(!savexact || !*savexact, + "BUG: exact match has not set exactpos"); + METAINC(nextexact); + + if (*save) { + char *nextin = save; + METAINC(nextin); + patglobflags = savglobflags; + errsfound = saverrsfound; + exactpos = savexact; + + /* + * Try swapping two characters in patinput and + * exactpos + */ + if (*save && *nextin && *nextexact) { + int cin0 = UNMETA(save); + int cpa0 = UNMETA(exactpos); + int cin1 = UNMETA(nextin); + int cpa1 = UNMETA(nextexact); + + if (CHARMATCH(cin0, cpa1) && + CHARMATCH(cin1, cpa0)) { + patinput = nextin; + METAINC(patinput); + exactpos = nextexact; + METAINC(exactpos); + if (patmatch(scan)) + return 1; + + patglobflags = savglobflags; + errsfound = saverrsfound; + } + } + + /* + * Try moving up both strings. + */ + patinput = nextin; + exactpos = nextexact; + if (patmatch(scan)) + return 1; + + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + exactpos = savexact; + } + + /* + * Try moving up the exact match pattern. + * This must be the last attempt, so just loop + * instead of calling recursively. + */ + METAINC(exactpos); + continue; + } + } + exactpos = NULL; + return 0; + } + + scan = next; + } + + return 0; +} + +/**/ +static int +patmatchrange(char *range, int ch) +{ + int r1, r2; + + for (; *range; range++) { + if (imeta(STOUC(*range))) { + switch (STOUC(*range)-STOUC(Meta)) { + case 0: + if (STOUC(*++range ^ 32) == ch) + return 1; + break; + case PP_ALPHA: + if (isalpha(ch)) + return 1; + break; + case PP_ALNUM: + if (isalnum(ch)) + return 1; + break; + case PP_BLANK: + if (ch == ' ' || ch == '\t') + return 1; + break; + case PP_CNTRL: + if (iscntrl(ch)) + return 1; + break; + case PP_DIGIT: + if (isdigit(ch)) + return 1; + break; + case PP_GRAPH: + if (isgraph(ch)) + return 1; + break; + case PP_LOWER: + if (islower(ch)) + return 1; + break; + case PP_PRINT: + if (isprint(ch)) + return 1; + break; + case PP_PUNCT: + if (ispunct(ch)) + return 1; + break; + case PP_SPACE: + if (isspace(ch)) + return 1; + break; + case PP_UPPER: + if (isupper(ch)) + return 1; + break; + case PP_XDIGIT: + if (isxdigit(ch)) + return 1; + case PP_RANGE: + range++; + r1 = STOUC(UNMETA(range)); + METAINC(range); + r2 = STOUC(UNMETA(range)); + if (*range == Meta) + range++; + if (r1 <= ch && ch <= r2) + return 1; + break; + case PP_UNKWN: + DPUTS(1, "BUG: unknown posix range passed through.\n"); + break; + default: + DPUTS(1, "BUG: unknown metacharacter in range."); + break; + } + } else if (*range == ch) + return 1; + } + return 0; +} + +/* repeatedly match something simple and say how many times */ + +/**/ +static int patrepeat(char *p) +{ + int count = 0, tch, inch; + char *scan, *opnd; + + scan = patinput; + opnd = P_OPERAND(p); + + switch(P_OP(p)) { +#ifdef DEBUG + case P_ANY: + dputs("BUG: ?# did not get optimized to *"); + return 0; + break; +#endif + case P_EXACTLY: + tch = STOUC(UNMETA(opnd)); + while (*scan && (inch = STOUC(UNMETA(scan)), CHARMATCH(inch, tch))) { + count++; + METAINC(scan); + } + break; + case P_ANYOF: + while (*scan && patmatchrange(opnd, STOUC(UNMETA(scan)))) { + count++; + METAINC(scan); + } + break; + case P_ANYBUT: + while (*scan && !patmatchrange(opnd, STOUC(UNMETA(scan)))) { + count++; + METAINC(scan); + } + break; +#ifdef DEBUG + default: + dputs("BUG: something very strange is happening in patrepeat"); + return 0; + break; +#endif + } + + patinput = scan; + return count; +} + +/**/ +#ifdef ZSH_PAT_DEBUG + +/* Debugging stuff: print and test a regular expression */ + +/* Dump a regexp onto stdout in vaguely comprehensible form */ + +/**/ +static void +patdump(Patprog r) +{ + char *s, *next, *codestart, *base, op = P_EXACTLY; + + base = (char *)r; + s = base + r->startoff; + + if (r->flags & PAT_PURES) { + printf("STRING:%s\n", (char *)s); + } else { + codestart = s; + while (op != P_END) { + op = P_OP(s); + printf("%2d%s", s-codestart, patprop(s)); + next = PATNEXT(s); + printf("(%d)", next ? (s-codestart)+(next-s) : 0); + s += sizeof(zalign_t); + if (op == P_ANYOF || op == P_ANYBUT || op == P_EXACTLY) { + while (*s != '\0') { + if (itok(*s)) { + if (*s == Meta + PP_RANGE) { + s++; + printf("<RANGE:%c-", UNMETA(s)); + METAINC(s); + printf("%c>", UNMETA(s)); + } else { + printf("<TYPE:%d>", *s - Meta); + s++; + continue; + } + } else + putchar(UNMETA(s)); + METAINC(s); + } + } else if (op == P_NUMRNG || op == P_NUMFROM || op == P_NUMTO) { + printf("%lu", (unsigned long)*(zrange_t *)s); + s += sizeof(zrange_t); + if (op == P_NUMRNG) { + printf("-%lu", (unsigned long)*(zrange_t *)s); + s += sizeof(zrange_t); + } + } else if (op == P_GFLAGS) { + long *lptr = (long *)s; + printf("%ld, %ld", *lptr & ~0xff, *lptr & 0xff); + s += sizeof(long); + } else if (op == P_WBRANCH || op == P_EXCLUDE || + op == P_EXCLUDP) { + s += sizeof(char *); + } + putchar('\n'); + s = base + (((s - base) + sizeof(zalign_t) - 1) & + ~(sizeof(zalign_t) - 1)); + } + } + + printf("Total size = %ld\n", r->size); + if (r->patstartch) + printf("start `%c' ", r->patstartch); + if (!(r->flags & PAT_NOANCH)) + printf("EOL-anchor "); +#ifdef BACKREFERENCES + if (r->flags & PAT_BACKR) + printf("backreferences "); +#endif + if (r->mustoff) + printf("must have \"%s\"", (char *)r + r->mustoff); + printf("\n"); + if (r->globflags) { + printf("Globbing flags: "); + if (r->globflags & C_LCMATCHUC) + printf("LC matches UC "); + if (r->globflags & C_IGNCASE) + printf("Ignore case"); + printf("\n"); + if (r->globflags & 0xff) + printf("Max errors = %d\n", r->globflags & 0xff); + } +} + +/**/ +static char * +patprop(char *op) +{ + char *p = NULL; + static char buf[50]; + + strcpy(buf, ":"); + + switch(P_OP(op)) { + case P_ANY: + p = "ANY"; + break; + case P_ANYOF: + p = "ANYOF"; + break; + case P_ANYBUT: + p = "ANYBUT"; + break; + case P_BRANCH: + p = "BRANCH"; + break; + case P_WBRANCH: + p = "WBRANCH"; + break; + case P_EXCLUDE: + p = "EXCLUDE"; + break; + case P_EXCLUDP: + p = "EXCLUDP"; + break; + case P_EXCSYNC: + p = "EXCSYNC"; + break; + case P_EXCEND: + p = "EXCEND"; + break; + case P_EXACTLY: + p = "EXACTLY"; + break; + case P_GFLAGS: + p = "GFLAGS"; + break; + case P_NOTHING: + p = "NOTHING"; + break; + case P_BACK: + p = "BACK"; + break; + case P_END: + p = "END"; + break; + case P_OPEN: + case P_OPEN+1: + case P_OPEN+2: + case P_OPEN+3: + case P_OPEN+4: + case P_OPEN+5: + case P_OPEN+6: + case P_OPEN+7: + case P_OPEN+8: + case P_OPEN+9: + sprintf(buf+strlen(buf), "OPEN%ld", P_OP(op)-P_OPEN); + p = NULL; + break; + case P_CLOSE: + case P_CLOSE+1: + case P_CLOSE+2: + case P_CLOSE+3: + case P_CLOSE+4: + case P_CLOSE+5: + case P_CLOSE+6: + case P_CLOSE+7: + case P_CLOSE+8: + case P_CLOSE+9: + sprintf(buf+strlen(buf), "CLOSE%ld", P_OP(op)-P_CLOSE); + p = NULL; + break; + case P_STAR: + p = "STAR"; + break; + case P_ONEHASH: + p = "ONEHASH"; + break; + case P_TWOHASH: + p = "TWOHASH"; + break; + case P_NUMRNG: + p = "NUMRNG"; + break; + case P_NUMFROM: + p = "NUMFROM"; + break; + case P_NUMTO: + p = "NUMTO"; + break; + case P_NUMANY: + p = "NUMANY"; + break; + default: + fprintf(stderr, "Bad opcode\n"); + p = NULL; + break; + } + if (p) + strcat(buf, p); + return buf; +} + +/**/ +int +bin_patdebug(char *name, char **args, char *ops, int func) +{ + Patprog prog; + int ret = 0, flags; + + tokenize(*args); + +#ifdef BACKREFERENCES + flags = ops['b'] ? PAT_BACKR : 0; +#else + flags = 0; +#endif + if (!(prog = patcompile((char *)*args, flags, 0))) + return 1; + if (ops['p'] || !args[1]) { + patdump(prog); + } + + while (*++args) { + if (!pattry(prog, (char *)*args)) + ret++; + } + return ret; +} + +/**/ +#endif /* ZSH_PAT_DEBUG */ diff --git a/Src/signals.c b/Src/signals.c index d29bdd4b0..4e9fed0cb 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -326,14 +326,23 @@ signal_suspend(int sig, int sig2) #ifdef POSIX_SIGNALS sigset_t set; +#ifdef BROKEN_POSIX_SIGSUSPEND + sigset_t oset; +#endif /* BROKEN_POSIX_SIGSUSPEND */ sigfillset(&set); sigdelset(&set, sig); sigdelset(&set, SIGHUP); /* still don't know why we add this? */ if (sig2) sigdelset(&set, sig2); +#ifdef BROKEN_POSIX_SIGSUSPEND + sigprocmask(SIG_SETMASK, &set, &oset); + pause(); + sigprocmask(SIG_SETMASK, &oset, NULL); +#else /* not BROKEN_POSIX_SIGSUSPEND */ ret = sigsuspend(&set); -#else +#endif /* BROKEN_POSIX_SIGSUSPEND */ +#else /* not POSIX_SIGNALS */ # ifdef BSD_SIGNALS sigset_t set; @@ -601,6 +610,9 @@ killjb(Job jn, int sig) } for (pn = jn->procs; pn; pn = pn->next) if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH) +#ifdef BROKEN_KILL_ESRCH + if(errno != EINVAL || sig != 0) +#endif /* BROKEN_KILL_ESRCH */ return -1; return err; } @@ -640,7 +652,11 @@ dosavetrap(int sig, int level) */ char func[20]; sprintf(func, "TRAP%s", sigs[sig]); - st->list = shfunctab->removenode(shfunctab, func); + /* We call removehashnode() directly because otherwise + * removeshfuncnode() would be called which in turn would + * call us again so that we would end up with a NULL pointer + * instead of the list for the trap. */ + st->list = removehashnode(shfunctab, func); } else { st->list = sigfuncs[sig]; unsettrap(sig); diff --git a/Src/subst.c b/Src/subst.c index 2a25d3e4b..6734218c7 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -721,6 +721,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) int flnum = 0; int sortit = 0, casind = 0; int casmod = 0; + int quotemod = 0, quoteerr = 0; char *sep = NULL, *spsep = NULL; char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL; char *replstr = NULL; /* replacement string for /orig/repl */ @@ -822,6 +823,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) case 'i': casind = 1; break; + + case 'q': + quotemod++; + break; + case 'Q': + quotemod--; + break; + case 'X': + quoteerr = 1; + break; + case 'e': eval = 1; break; @@ -1379,12 +1391,23 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) case '#': case Pound: case '/': - if (qt) - if (parse_subst_string(s)) { + if (qt) { + int one = noerrs, oef = errflag, haserr; + + if (!quoteerr) + noerrs = 1; + haserr = parse_subst_string(s); + noerrs = one; + if (!quoteerr) { + errflag = oef; + if (haserr) + tokenize(s); + } else if (haserr || errflag) { zerr("parse error in ${...%c...} substitution", NULL, s[-1]); return NULL; } + } { char t = s[-1]; @@ -1546,6 +1569,58 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) makecapitals(&val); } } + if (quotemod) { + if (isarr) { + char **ap; + + if (!copied) + aval = arrdup(aval), copied = 1; + ap = aval; + + if (quotemod > 0) + for (; *ap; ap++) + *ap = bslashquote(*ap, NULL, 0); + else { + int one = noerrs, oef = errflag, haserr = 0; + + if (!quoteerr) + noerrs = 1; + for (; *ap; ap++) { + haserr |= parse_subst_string(*ap); + remnulargs(*ap); + untokenize(*ap); + } + noerrs = one; + if (!quoteerr) + errflag = oef; + else if (haserr || errflag) { + zerr("parse error in parameter value", NULL, 0); + return NULL; + } + } + } else { + if (!copied) + val = dupstring(val), copied = 1; + if (quotemod > 0) + val = bslashquote(val, NULL, 0); + else { + int one = noerrs, oef = errflag, haserr; + + if (!quoteerr) + noerrs = 1; + haserr = parse_subst_string(val); + noerrs = one; + if (!quoteerr) + errflag = oef; + else if (haserr || errflag) { + zerr("parse error in parameter value", NULL, 0); + return NULL; + } + remnulargs(val); + untokenize(val); + } + } + } if (isarr) { char *x; char *y; @@ -1747,6 +1822,7 @@ modify(char **str, char **ptr) case 'l': case 'u': case 'q': + case 'Q': c = **ptr; break; @@ -1868,6 +1944,18 @@ modify(char **str, char **ptr) case 'q': copy = bslashquote(copy, NULL, 0); break; + case 'Q': + { + int one = noerrs, oef = errflag; + + noerrs = 1; + parse_subst_string(copy); + noerrs = one; + errflag = oef; + remnulargs(copy); + untokenize(copy); + } + break; } tc = *tt; *tt = '\0'; @@ -1922,6 +2010,18 @@ modify(char **str, char **ptr) case 'q': *str = bslashquote(*str, NULL, 0); break; + case 'Q': + { + int one = noerrs, oef = errflag; + + noerrs = 1; + parse_subst_string(*str); + noerrs = one; + errflag = oef; + remnulargs(*str); + untokenize(*str); + } + break; } } if (rec < 0) { diff --git a/Src/system.h b/Src/system.h index e95e2c4cc..60f0dfe05 100644 --- a/Src/system.h +++ b/Src/system.h @@ -267,6 +267,8 @@ struct timezone { # ifndef TIME_H_SELECT_H_CONFLICTS # include <sys/select.h> # endif +#elif defined(SELECT_IN_SYS_SOCKET_H) +# include <sys/socket.h> #endif #ifdef HAVE_SYS_FILIO_H @@ -613,3 +615,8 @@ extern short ospeed; #define ftell ftello #endif #endif + +/* Can't support job control without working tcsetgrp() */ +#ifdef BROKEN_TCSETPGRP +#undef JOB_CONTROL +#endif /* BROKEN_TCSETPGRP */ diff --git a/Src/utils.c b/Src/utils.c index f86c18b16..b982b4767 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1249,7 +1249,7 @@ setblock_stdin(void) long mode; if (!fstat(0, &st) && !S_ISREG(st.st_mode)) { - mode = fcntl(0, F_GETFL); + mode = fcntl(0, F_GETFL, 0); if (mode != -1 && (mode & NONBLOCK) && !fcntl(0, F_SETFL, mode & ~NONBLOCK)) return 1; diff --git a/Src/zsh.export b/Src/zsh.export index 59d75676f..68d93d161 100644 --- a/Src/zsh.export +++ b/Src/zsh.export @@ -50,7 +50,6 @@ deletehookfunc deleteparamdefs deleteparamtable deletewrapper -domatch dosetopt doshfunc down_histent @@ -177,10 +176,11 @@ paramtab parbegin parend parse_string -parsereg parsestr +patcompile path pathchecked +pattry popheap postedit ppid diff --git a/Src/zsh.h b/Src/zsh.h index d2b64b9bb..fe80a17bb 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -260,6 +260,7 @@ typedef struct builtin *Builtin; typedef struct nameddir *Nameddir; typedef struct module *Module; +typedef struct patprog *Patprog; typedef struct process *Process; typedef struct job *Job; typedef struct value *Value; @@ -270,7 +271,6 @@ typedef struct cmd *Cmd; typedef struct pline *Pline; typedef struct sublist *Sublist; typedef struct list *List; -typedef struct comp *Comp; typedef struct redir *Redir; typedef struct complist *Complist; typedef struct heap *Heap; @@ -906,6 +906,57 @@ struct hookdef { #define HOOKDEF(name, func, flags) { NULL, name, (Hookfn) func, flags, NULL } +/* + * Types used in pattern matching. Most of these longs could probably + * happily be ints. + */ + +#define NSUBEXP 10 +struct patprog { + long startoff; /* length before start of programme */ + long size; /* total size from start of struct */ + long mustoff; /* offset to string that must be present */ + int globflags; /* globbing flags to set at start */ + int globend; /* globbing flags set after finish */ + int flags; /* PAT_* flags */ + int patmlen; + char patstartch; +#ifdef BACKREFERENCES + unsigned char * ppStartp[NSUBEXP]; + unsigned char * ppEndp[NSUBEXP]; +}; + +/* Same as patprog, but without the backreference storage. + * Note the calling code must test PAT_BACKR to know which is + * which, since they are both passed back as a Patprog. + */ + +struct patprog_short { + long startoff; + long size; + long mustoff; + int globflags; + int globend; + int flags; + int patmlen; + char patstartch; +#endif +}; + +/* Flags used in pattern matchers (Patprog) and passed down to patcompile */ + +#define PAT_FILE 0x0001 /* Pattern is a file name */ +#define PAT_FILET 0x0002 /* Pattern is top level file, affects ~ */ +#define PAT_ANY 0x0004 /* Match anything (cheap "*") */ +#define PAT_NOANCH 0x0008 /* Not anchored at end */ +#define PAT_NOGLD 0x0010 /* Don't glob dots */ +#define PAT_PURES 0x0020 /* Pattern is a pure string: set internally */ +#define PAT_STATIC 0x0040 /* Don't copy pattern to heap as per default */ +#define PAT_SCAN 0x0080 /* Scanning, so don't try must-match test */ +#ifdef BACKREFERENCES +#define PAT_BACKR 0x0100 /* Parentheses make backreferences */ +#endif + /* node used in parameter hash table (paramtab) */ struct param { diff --git a/Src/zsh.mdd b/Src/zsh.mdd index ef04658b1..a92fee90c 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -5,7 +5,7 @@ alwayslink=1 objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o \ hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ -mem.o module.o options.o params.o parse.o prompt.o signals.o \ +mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ signames.o subst.o text.o utils.o watch.o" headers="../config.h system.h zsh.h sigcount.h signals.h \ |