about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/.distfiles2
-rw-r--r--Src/Makefile.in5
-rw-r--r--Src/Modules/example.c4
-rw-r--r--Src/Zle/comp.h3
-rw-r--r--Src/Zle/compctl.c43
-rw-r--r--Src/Zle/zle_tricky.c128
-rw-r--r--Src/builtin.c91
-rw-r--r--Src/cond.c5
-rw-r--r--Src/glob.c1789
-rw-r--r--Src/hashtable.c4
-rw-r--r--Src/hist.c28
-rw-r--r--Src/init.c2
-rw-r--r--Src/jobs.c4
-rw-r--r--Src/loop.c5
-rw-r--r--Src/options.c14
-rw-r--r--Src/params.c33
-rw-r--r--Src/pattern.c2284
-rw-r--r--Src/signals.c20
-rw-r--r--Src/subst.c104
-rw-r--r--Src/system.h7
-rw-r--r--Src/utils.c2
-rw-r--r--Src/zsh.export4
-rw-r--r--Src/zsh.h53
-rw-r--r--Src/zsh.mdd2
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 \