about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog51
-rw-r--r--Completion/Base/_brace_parameter4
-rw-r--r--Completion/Core/_path_files15
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Makefile.in2
-rw-r--r--Doc/Zsh/compsys.yo10
-rw-r--r--Doc/Zsh/expn.yo14
-rw-r--r--Doc/Zsh/metafaq.yo3
-rw-r--r--Etc/BUGS11
-rw-r--r--Etc/CONTRIBUTORS2
-rwxr-xr-xMisc/globtests19
-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
-rw-r--r--Util/zsh-development-guide5
-rw-r--r--acconfig.h18
-rw-r--r--aclocal.m422
-rw-r--r--configure.in165
39 files changed, 3252 insertions, 1729 deletions
diff --git a/ChangeLog b/ChangeLog
index 8db427571..a8bff14a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,54 @@
+1999-08-06  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: Config/version.mk: 3.1.6-pws-1 made available.
+
+	* Sven: 7386: Src/subst.c: Order of q and Q parameter flags matters.
+
+1999-08-05  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* Sven: 7380: Src/hist.c, Src/subst.c, Doc/Zsh/expn.yo: flag X for
+	  reporting errors de-quoting.
+
+	* Sven: 7374: Src/subst.c, Doc/Zsh/expn.yo: modifier to remove one
+	  level of quotation, corresponding flags.
+
+	* Sven: 7373: Src/Zle/comp.h, Src/Zle/zle_tricky.c,
+	  Completion/Base/_brace_parameter:  support nested parameter
+	  expansion type of deal.
+
+1999-08-04  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* Sven: 7355: Src/Zle/compctl.c, Src/Zle/zle_tricky.c: make
+	  compadd quoting consistent with compgen.	
+
+1999-08-02  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* Sven: 7349: Src/loop.c, Src/signals.c:  restore errflags in
+	  select after a zleread; fix problem restoring function-style trap
+	  after a function set it to something else.
+
+	* Sven: 7348: Completion/Core/_path_files, Doc/Zsh/compsys.yo:
+	  remove path_keepdir configuration key but don't do keepdir
+	  trick unless _files had no type arguments.
+
+	* Sven: 7345, 7346, 7347: Src/Zle/zle_tricky.c: match control
+	  problems.
+
+	* Sven: 7344, 7353: Src/cond.c, Src/Modules/example.c,
+	  Src/Zle/compctl.c, Util/zsh-development-guide:  conditions can
+	  get the tokenized string; better quoting behaviour in
+	  IPREFIX/ISUFFIX.
+
+	* Sven: 7343: Src/Zle/zle_tricky.c: quote path prefix and suffix
+	  earlier.
+
+	* pws: 7342: Doc/Makfile.in: use makeinfo ... || exit 0 to
+	  ignore result of makeinfo.
+
+	* Irving Wolfe: 7336: utils.c: same fcntl() change.
+
+	* Geoff: 7335: init.c: third argument for fcntl().
+
 1999-08-01  Peter Stephenson  <pws@ibmth.df.unipi.it>
 
 	* pws: Config/version.mk: zsh 3.1.6 released.
diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter
index 020dc81e9..9dfe46bd6 100644
--- a/Completion/Base/_brace_parameter
+++ b/Completion/Base/_brace_parameter
@@ -7,6 +7,8 @@ local lp ls n q suf=''
 if [[ "$SUFFIX" = *\}* ]]; then
   ISUFFIX="${SUFFIX#*\}}$ISUFFIX"
   SUFFIX="${SUFFIX%%\}*}"
+elif [[ "$LBUFFER" = *\$\{[^}]#\$\{[^}]#$PREFIX ]]; then
+  suf='}'
 else
   suf='} '
 fi
@@ -18,4 +20,4 @@ q=${(M)lp%%\"#}
 
 [[ n -gt 0 ]] && suf=''
 
-_parameters -Qs "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/'
+_parameters -Qs "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/}'
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 04f4f1db3..9494d5a4a 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -25,7 +25,7 @@
 
 local linepath realpath donepath prepath testpath exppath
 local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf
-local pats ignore group expl addpfx addsfx remsfx
+local pats haspats=no ignore group expl addpfx addsfx remsfx
 local nm=$compstate[nmatches] menu
 
 typeset -U prepaths exppaths
@@ -82,9 +82,11 @@ while getopts "P:S:qr:R:W:F:J:V:X:f/g:" opt; do
 	 ;;
   /)     sopt="${sopt}/"
          pats=("$pats[@]" '*(-/)')
+	 haspats=yes
 	 ;;
   g)     gopt='-g'
          pats=("$pats[@]" ${=OPTARG})
+	 haspats=yes
 	 ;;
   esac
 done
@@ -259,14 +261,11 @@ for prepath in "$prepaths[@]"; do
       # an attempt to correct a valid directory name. So we just add the
       # original string in such a case so that the command line doesn't 
       # change but other completers still think there are matches.
+      # We do this only if we weren't given a `-g' or `-/' option because
+      # otherwise this would keep `_files' from completing all filenames
+      # if none of the patterns match.
 
-      # Problem:  this seems to stop _files from finding directory
-      # completions if there were no file completions, for
-      # example `_files *(*)' no longer completes subdirectories after
-      # a /.  For now, make this a configuration option, but
-      # probably it needs to be done better.
-
-      if [[ -n "$compconfig[path_keepdir]" && -z "$tpre$tsuf" &&
+      if [[ "$haspats" = no && -z "$tpre$tsuf" &&
 	"$pre" = */ && -z "$suf" ]]; then
         compadd -nQS '' - "$linepath$donepath$orig"
         tmp4=-
diff --git a/Config/version.mk b/Config/version.mk
index 5af4324a6..d653f1fdd 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=3.1.6
-VERSION_DATE='July 30, 1999'
+VERSION=3.1.6-pws-1
+VERSION_DATE='August 6, 1999'
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index c76083b89..13d531c29 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -74,7 +74,7 @@ zsh.dvi: zsh.texi
 	$(TEXI2DVI) $(sdir)/zsh.texi
 
 zsh.info: zsh.texi
-	$(MAKEINFO) $(sdir)/zsh.texi; exit 0
+	$(MAKEINFO) $(sdir)/zsh.texi || exit 0
 
 zsh.texi: zsh.yo
 	$(YODL) -o $(sdir)/zsh.texi -I$(sdir) -w ztexi.yo version.yo zsh.yo
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 5a52ca9ad..466e612ed 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -738,7 +738,7 @@ These functions also accept the `tt(-J)', `tt(-V)', `tt(-X)', `tt(-P)',
 `tt(-S)', `tt(-q)', `tt(-r)', and `tt(-R)' options from the
 tt(compadd) builtin.
 
-Finally, the tt(_path_files) function supports three configuration keys.
+Finally, the tt(_path_files) function supports two configuration keys.
 startitem()
 item(tt(path_expand))(
 If this is set to any non-empty string, the partially
@@ -750,14 +750,6 @@ If this is set to a non-empty string, the cursor will be left
 after the first ambiguous pathname component even when menucompletion
 is used.
 )
-item(tt(path_keepdir))(
-If this is set to a non-empty string, then if completion immediately after
-a slash fails, treat the original string as a successful completion.  This
-prevents a valid directory being treated as a candidate for correction.
-However, it has the side effect that a pattern completion, such as
-`tt(files -g *(*))', will no longer try to complete directories in this
-position if there are no file matches.
-)
 enditem()
 )
 item(tt(_parameters))(
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 7eae493e0..f7c1b5056 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -166,6 +166,9 @@ with history expansion and parameter expansion, though in the second
 case it is only useful if the resulting text is to be re-evaluated
 such as by tt(eval).
 )
+item(tt(Q))(
+Remove one level of quotes from the substituted words.
+)
 item(tt(x))(
 Like tt(q), but break into words at each blank.
 )
@@ -578,6 +581,17 @@ Capitalize the resulting words.  `Words' in this case refers to sequences
 of alphanumeric characters separated by non-alphanumerics, em(not) to words
 that result from field splitting.
 )
+item(tt(q))(
+Quote the resulting words with backslashes.
+)
+item(tt(Q))(
+Remove one level of quotes from the resulting words.
+)
+item(tt(X))(
+With this flag parsing errors occuring with the tt(Q) flag or the
+pattern matching forms such as `tt(${)var(name)tt(#)var(pattern)tt(})' 
+are reported. Without the flag they are silently ignored.
+)
 item(tt(c))(
 With tt(${#)var(name)tt(}), count the total number of characters in an array,
 as if the elements were concatenated with spaces between them.
diff --git a/Doc/Zsh/metafaq.yo b/Doc/Zsh/metafaq.yo
index a47fac515..8dc003090 100644
--- a/Doc/Zsh/metafaq.yo
+++ b/Doc/Zsh/metafaq.yo
@@ -71,7 +71,8 @@ item(Poland)(
 nofill(tt(ftp://sunsite.icm.edu.pl/pub/unix/shells/zsh/))
 )
 item(Romania)(
-nofill(tt(ftp://ftp.roedu.net/pub/mirrors/ftp.zsh.org/pub/zsh/))
+nofill(tt(ftp://ftp.roedu.net/pub/mirrors/ftp.zsh.org/pub/zsh/)
+tt(ftp://ftp.kappa.ro/pub/mirrors/ftp.zsh.org/pub/zsh/))
 )
 item(Slovenia)(
 nofill(tt(ftp://ftp.siol.net/mirrors/zsh/))
diff --git a/Etc/BUGS b/Etc/BUGS
index 51aff5428..69f9bad47 100644
--- a/Etc/BUGS
+++ b/Etc/BUGS
@@ -48,14 +48,3 @@ Numeric ranges are still too greedy with using characters; for example,
 <1-1000>33 will not match 633 because the 633 matches the range.  Some
 backtracking will be necessary.
 ------------------------------------------------------------------------
-Matching control can leave the wrong thing in the line.  For example,
-        touch Abc-Def-Ghij.txt
-        touch Abc-def.ghi.jkl_mno.pqr.txt
-        touch Abc_def_ghi_jkl_mno_pqr.txt
-        compctl -M 'm:{a-z}={A-Z} r:|[.,_-]=*'
-        ls a<TAB>
-produces
-        ls Abcdefghi
-which won't complete further.  It seems to get confused over the choice of
-possible punctuation characters, and the string won't complete further.
-------------------------------------------------------------------------
diff --git a/Etc/CONTRIBUTORS b/Etc/CONTRIBUTORS
index 842199f95..6cb64199f 100644
--- a/Etc/CONTRIBUTORS
+++ b/Etc/CONTRIBUTORS
@@ -18,7 +18,7 @@ at <coordinator@zsh.org>.
 Version 3.1.6
 -------------
 
-* Sven Wishnowsky <wischnow@informatik.hu-berlin.de>: completion code,
+* Sven Wischnowsky <wischnow@informatik.hu-berlin.de>: completion code,
   major rewrite and enhancements, including matching control, completion
   widgets and function system, complist module; zle code additions; job
   control code changes; parameters code changes; parameter module;
diff --git a/Misc/globtests b/Misc/globtests
index b1e25bb0c..b2fe8d8ee 100755
--- a/Misc/globtests
+++ b/Misc/globtests
@@ -14,6 +14,11 @@ while read res str pat; do
     (( failed++ ))
   fi
 done <<EOT
+# a few simple things certain nameless idiots have been known to mess up
+t foo~                foo~
+t foo.c               *.c~boo*
+f foo.c               *.c~boo*~foo*
+# closures
 t fofo                (fo#)#
 t ffo                 (fo#)#
 t foooofo             (fo#)#
@@ -95,6 +100,8 @@ t zoox          (^z*|*x)
 t foo           (^foo)#
 f foob          (^foo)b*
 t foobb         (^foo)b*
+f foob          (*~foo)b*
+t foobb         (*~foo)b*
 f zsh           ^z*
 t a%1X          [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
 f a%1           [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
@@ -115,6 +122,16 @@ f fooxx         ((#i)FOOX)X
 f BAR           (bar|(#i)foo)
 t FOO           (bar|(#i)foo)
 t Modules       (#i)*m*
+t fooGRUD       (#i)(bar|(#I)foo|(#i)rod)grud
+f FOOGRUD       (#i)(bar|(#I)foo|(#i)rod)grud
+t readme        (#i)readme~README|readme
+# the readme doesn't get excluded the second time...
+t readme        (#i)readme~README|readme~README
+# Ranges with backtracking
+t 633           <1-1000>33
+t 633           <-1000>33
+t 633           <1->33
+t 633           <->33
 # Approximate matching
 t READ.ME       (#ia1)readme
 f READ..ME      (#ia1)readme
@@ -143,6 +160,8 @@ t dcba          (#a3)abcd
 t aabaXaaabY    (#a1)(a#b)#Y
 t aabaXaaabY    (#a1)(a#b)(a#b)Y
 t aaXaaaaabY    (#a1)(a#b)(a#b)Y
+t aaaXaaabY     (#a1)(a##b)##Y
+t aaaXbaabY     (#a1)(a##b)##Y
 f read.me       (#ia1)README~READ.ME
 t read.me       (#ia1)README~READ_ME
 f read.me       (#ia1)README~(#a1)READ_ME
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 \
diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide
index d96cc1a04..4ec4ff079 100644
--- a/Util/zsh-development-guide
+++ b/Util/zsh-development-guide
@@ -285,11 +285,12 @@ Note that no preprocessing is done on the strings. This means that
 no substitutions are performed on them and that they will be
 tokenized. There are three helper functions available:
 
-  - char *cond_str(args, num)
+  - char *cond_str(args, num, raw)
     The first argument is the array of strings the handler function
     got as an argument and the second one is an index into this array.
     The return value is the num'th string from the array with
-    substitutions performed and untokenized.
+    substitutions performed. If the last argument is zero, the string
+    will also be untokenized.
   - long cond_val(args, num)
     The arguments are the same as for cond_str(). The return value is
     the result of the mathematical evaluation of the num'th string
diff --git a/acconfig.h b/acconfig.h
index 94e6113f8..05ab0a733 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -198,6 +198,9 @@
 /* Define to 1 if there is a prototype defined for ioctl() on your system */
 #undef HAVE_IOCTL_PROTO
 
+/* Define to 1 if select() is defined in <sys/socket.h>, ie BeOS R4.51*/
+#undef SELECT_IN_SYS_SOCKET_H
+
 /* Define to 1 if system has working FIFO's */
 #undef HAVE_FIFOS
 
@@ -216,6 +219,21 @@
 /* Define to 1 if /bin/sh does not interpret \ escape sequences */
 #undef SH_USE_BSD_ECHO
 
+/* Define to 1 if system has working link() */
+#undef HAVE_LINK
+
+/* Define to 1 if kill(pid, 0) doesn't return ESRCH, ie BeOS R4.51 */
+#undef BROKEN_KILL_ESRCH
+
+/* Define to 1 if sigsuspend() is broken, ie BeOS R4.51 */
+#undef BROKEN_POSIX_SIGSUSPEND
+
+/* Define to 1 if getpwnam() is faked, ie BeOS R4.51 */
+#undef GETPWNAM_FAKED
+
+/* Define to 1 if tcsetpgrp() doesn't work, ie BeOS R4.51 */
+#undef BROKEN_TCSETPGRP
+
 /* Define to 1 if an underscore has to be prepended to dlsym() argument */
 #undef DLSYM_NEEDS_UNDERSCORE
 
diff --git a/aclocal.m4 b/aclocal.m4
index 71718a9dd..e91be3c0d 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -52,4 +52,26 @@ case "x$fp_cv_prog_cc_stdc" in
 esac
 ])
 
+AC_DEFUN(AC_PROG_LN,
+[AC_MSG_CHECKING(whether ln works)
+AC_CACHE_VAL(ac_cv_prog_LN,
+[rm -f conftestdata conftestlink
+echo > conftestdata
+if ln conftestdata conftestlink 2>/dev/null
+then
+  rm -f conftestdata conftestlink
+  ac_cv_prog_LN="ln"
+else
+  rm -f conftestdata
+  ac_cv_prog_LN="cp"
+fi])dnl
+LN="$ac_cv_prog_LN"
+if test "$ac_cv_prog_LN" = "ln"; then
+  AC_MSG_RESULT(yes)
+else
+  AC_MSG_RESULT(no)
+fi
+AC_SUBST(LN)dnl
+])
+
 builtin(include, aczsh.m4)
diff --git a/configure.in b/configure.in
index 6f3a1f52e..67a91ee1a 100644
--- a/configure.in
+++ b/configure.in
@@ -380,6 +380,7 @@ dnl ------------------
 AC_PROG_MAKE_SET            dnl Does make define $MAKE
 AC_PROG_INSTALL             dnl Check for BSD compatible `install'
 AC_PROG_AWK                 dnl Check for mawk,gawk,nawk, then awk.
+AC_PROG_LN                  dnl Check for working ln, for "make install"
 AC_CHECK_PROGS([YODL], [yodl], [:])
 
 dnl ------------------
@@ -1046,6 +1047,20 @@ if test "$ac_cv_prog_cc_stdc" != no; then
   fi
 fi
 
+dnl -------------------
+dnl select() defined in <sys/socket.h>, ie BeOS R4.51
+dnl -------------------
+if test $ac_cv_header_sys_select_h != yes; then
+  AC_CACHE_CHECK(for select() in <sys/socket.h>,
+  zsh_cv_header_socket_h_select_proto,
+  [AC_TRY_COMPILE([#include <sys/socket.h>], [fd_set fd;],
+  zsh_cv_header_socket_h_select_proto=yes, 
+  zsh_cv_header_socket_h_select_proto=no)])
+  if test $zsh_cv_header_socket_h_select_proto = yes; then
+    AC_DEFINE(SELECT_IN_SYS_SOCKET_H)
+  fi
+fi
+
 dnl -----------
 dnl named FIFOs
 dnl -----------
@@ -1099,6 +1114,156 @@ if test $zsh_cv_prog_sh_echo_escape = no; then
   AC_DEFINE(SH_USE_BSD_ECHO)
 fi
 
+dnl -----------
+dnl test for whether link() works
+dnl for instance, BeOS R4.51 doesn't support hard links yet
+dnl -----------
+AC_CACHE_CHECK(if link() works,
+zsh_cv_sys_link,
+[AC_TRY_RUN([
+#include <unistd.h>
+#include <fcntl.h>
+main()
+{
+    int ret;
+    char *tmpfile, *newfile;
+    tmpfile="/tmp/zsh.linktest$$";
+    newfile="/tmp/zsh.linktest2$$";
+    unlink(tmpfile);
+    unlink(newfile);
+    if(creat(tmpfile, 0644) < 0)
+	exit(1);
+    ret = link(tmpfile, newfile);
+    unlink(tmpfile);
+    unlink(newfile);
+    exit(ret<0);
+}
+],
+  zsh_cv_sys_link=yes,
+  zsh_cv_sys_link=no,
+  zsh_cv_sys_link=yes)])
+if test $zsh_cv_sys_link = yes; then
+  AC_DEFINE(HAVE_LINK)
+fi
+
+dnl -----------
+dnl test for whether kill(pid, 0) where pid doesn't exit
+dnl should set errno to ESRCH, but some like BeOS R4.51 set to EINVAL
+dnl -----------
+AC_CACHE_CHECK(if kill(pid, 0) returns ESRCH correctly,
+zsh_cv_sys_killesrch,
+[AC_TRY_RUN([
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+main()
+{
+    int pid, ret;
+    pid=getpid() + 10000;
+    ret=kill(pid, 0);
+    exit(ret<0 && errno!=ESRCH);
+}
+],
+  zsh_cv_sys_killesrch=yes,
+  zsh_cv_sys_killesrch=no,
+  zsh_cv_sys_killesrch=yes)])
+if test $zsh_cv_sys_killesrch = no; then
+  AC_DEFINE(BROKEN_KILL_ESRCH)
+fi
+
+dnl -----------
+dnl if POSIX, test for working sigsuspend().
+dnl for instance, BeOS R4.51 is broken.
+dnl -----------
+if test $signals_style=POSIX_SIGNALS; then
+    AC_CACHE_CHECK(if POSIX sigsuspend() works,
+    zsh_cv_sys_sigsuspend,
+    [AC_TRY_RUN([
+#include <signal.h>
+#include <unistd.h>
+int child=0;
+void handler(sig)
+    int sig;
+{if(sig==SIGCHLD) child=1;}
+main() {
+    struct sigaction act;
+    sigset_t set;
+    int pid, ret;
+    act.sa_handler = &handler;
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    sigaction(SIGCHLD, &act, 0);
+    sigfillset(&set);
+    sigprocmask(SIG_SETMASK, &set, 0);
+    pid=fork();
+    if(pid==0) return 0;
+    if(pid>0) {
+    sigemptyset(&set);
+        ret=sigsuspend(&set);
+        exit(child==0);
+    }
+}
+],
+      zsh_cv_sys_sigsuspend=yes,
+      zsh_cv_sys_sigsuspend=no,
+      zsh_cv_sys_sigsuspend=yes)])
+    if test $zsh_cv_sys_sigsuspend = no; then
+      AC_DEFINE(BROKEN_POSIX_SIGSUSPEND)
+    fi
+fi
+
+dnl -----------
+dnl if found tcsetpgrp, test to see if it actually works
+dnl for instance, BeOS R4.51 does not support it yet
+dnl -----------
+if test $ac_cv_func_tcsetpgrp=yes; then
+    AC_CACHE_CHECK(if tcsetpgrp() actually works,
+    zsh_cv_sys_tcsetpgrp,
+    [AC_TRY_RUN([
+#include <sys/types.h>
+#include <unistd.h>
+main() {
+    int ret;
+    ret=tcsetpgrp(0, tcgetpgrp(0));
+    exit(ret<0);
+}
+],
+      zsh_cv_sys_tcsetpgrp=yes,
+      zsh_cv_sys_tcsetpgrp=no,
+      zsh_cv_sys_tcsetpgrp=yes)])
+    if test $zsh_cv_sys_tcsetpgrp = no; then
+      AC_DEFINE(BROKEN_TCSETPGRP)
+    fi
+fi
+
+dnl -----------
+dnl test for faked getpwnam() entry, ie a single entry returned for any username
+dnl for instance, BeOS R4.51 is not multiuser yet, and fakes getpwnam()
+dnl test by looking up two usernames that shouldn't succeed, and compare entry
+dnl -----------
+if test $ac_cv_func_getpwnam=yes; then
+    AC_CACHE_CHECK(if getpwnam() is faked,
+    zsh_cv_sys_getpwnam_faked,
+    [AC_TRY_RUN([
+#include <pwd.h>
+main() {
+    struct passwd *pw1, *pw2;
+    char buf[1024];
+    sprintf(buf, "%d:%d", getpid(), rand());
+    pw1=getpwnam(buf);
+    sprintf(buf, "%d:%d", rand(), getpid());
+    pw2=getpwnam(buf);
+    exit(pw1!=0 && pw2!=0 && !strcmp(pw1->pw_name, pw2->pw_name));
+}
+],
+      zsh_cv_sys_getpwnam_faked=no,
+      zsh_cv_sys_getpwnam_faked=yes,
+      zsh_cv_sys_getpwnam_faked=no)])
+    if test $zsh_cv_sys_getpwnam_faked = yes; then
+      AC_DEFINE(GETPWNAM_FAKED)
+    fi
+fi
+
 dnl ---------------
 dnl dynamic loading
 dnl ---------------