about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:17:36 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:17:36 +0000
commit904b939cbd81a542303da2c58288b95b153106f5 (patch)
tree84b3751ed1deacc51eb186023101360ae92ef221 /Src
parentb4a5b9db8b528f9c9b6a9cbb00db381c95659380 (diff)
downloadzsh-904b939cbd81a542303da2c58288b95b153106f5.tar.gz
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.xz
zsh-904b939cbd81a542303da2c58288b95b153106f5.zip
zsh-3.1.5-pws-10 zsh-3.1.5-pws-10
Diffstat (limited to 'Src')
-rw-r--r--Src/.cvsignore1
-rw-r--r--Src/Builtins/rlimits.c12
-rw-r--r--Src/Zle/comp.h7
-rw-r--r--Src/Zle/comp1.c5
-rw-r--r--Src/Zle/compctl.c12
-rw-r--r--Src/Zle/zle_thingy.c11
-rw-r--r--Src/Zle/zle_tricky.c953
-rw-r--r--Src/exec.c2
-rw-r--r--Src/glob.c441
-rw-r--r--Src/hashtable.c20
-rw-r--r--Src/module.c49
-rw-r--r--Src/params.c13
-rw-r--r--Src/subst.c5
-rw-r--r--Src/system.h11
-rw-r--r--Src/zsh.export1
15 files changed, 1054 insertions, 489 deletions
diff --git a/Src/.cvsignore b/Src/.cvsignore
index edec5401b..fcef4da67 100644
--- a/Src/.cvsignore
+++ b/Src/.cvsignore
@@ -19,6 +19,7 @@ zsh
 libzsh.so*
 sigcount.h
 signames.c
+version.h
 zshpaths.h
 zshxmods.h
 bltinmods.list
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 2af8dc6ac..7825a900d 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -37,7 +37,7 @@
 
 # include "rlimits.h"
 
-# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED)
+# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
 static rlim_t
 zstrtorlimt(const char *s, char **t, int base)
 {
@@ -62,9 +62,9 @@ zstrtorlimt(const char *s, char **t, int base)
 	*t = (char *)s;
     return ret;
 }
-# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
 #  define zstrtorlimt(a, b, c)	zstrtol((a), (b), (c))
-# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
 
 /* Display resource limits.  hard indicates whether `hard' or `soft'  *
  * limits should be displayed.  lim specifies the limit, or may be -1 *
@@ -107,9 +107,15 @@ showlimits(int hard, int lim)
 	    else
 		printf("%qdkB\n", val / 1024L);
 # else
+#  ifdef RLIM_T_IS_LONG_LONG
+		printf("%lldMB\n", val / (1024L * 1024L));
+            else
+		printf("%lldkB\n", val / 1024L);
+#  else
 		printf("%ldMB\n", val / (1024L * 1024L));
             else
 		printf("%ldkB\n", val / 1024L);
+#  endif /* RLIM_T_IS_LONG_LONG */
 # endif /* RLIM_T_IS_QUAD_T */
 	}
 }
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 0d5419817..76ac67114 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -271,9 +271,10 @@ struct cline {
 #define CLF_MISS   4
 #define CLF_DIFF   8
 #define CLF_SUF   16
-#define CLF_NEW   32
-#define CLF_VAR   64
-#define CLF_JOIN 128
+#define CLF_PNEW  32
+#define CLF_SNEW  64
+#define CLF_VAR  128
+#define CLF_JOIN 256
 
 /* Flags for makecomplist*(). Things not to do. */
 
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 77fe0a1fe..72f3cea53 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -49,10 +49,10 @@ void (*makecompparamsptr) _((void));
 /* pointers to functions required by compctl and defined by zle */
 
 /**/
-void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char **));
+void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
 
 /**/
-char *(*comp_strptr) _((int*,int*));
+char *(*comp_strptr) _((int*, int*, int));
 
 /**/
 int (*getcpatptr) _((char *, int, char *, int));
@@ -410,6 +410,7 @@ setup_comp1(Module m)
     cc_first.mask2 = CC_CCCONT;
     compcontext = compcommand = compprefix = compsuffix =
 	compiprefix = NULL;
+    makecompparamsptr = NULL;
     return 0;
 }
 
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index e3b778508..f71d67510 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1676,7 +1676,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     char *p, **sp, *e;
     char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
     char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
-    char *ign = NULL, *rf = NULL;
+    char *ign = NULL, *rf = NULL, *expl = NULL;
     int f = 0, a = 0, dm;
     Cmatcher match = NULL;
 
@@ -1757,6 +1757,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		e = "matching specification expected after -%c";
 		dm = 1;
 		break;
+	    case 'X':
+		sp = &expl;
+		e = "string expected after -%c";
+		break;
 	    case 'r':
 		f |= CMF_REMOVE;
 		sp = &rs;
@@ -1802,7 +1806,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 
     match = cpcmatcher(match);
     addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
-		  rs, rf, ign, f, a, match, argv);
+		  rs, rf, ign, f, a, match, expl, argv);
     freecmatcher(match);
 
     return 0;
@@ -2049,8 +2053,8 @@ cond_strcl(char **a, int id)
 	    zerr("zle not loaded, zle condition not available", NULL, 0);
 	    return 1;
 	}
-	i = getcpatptr(comp_strptr(&ipl, NULL), i, s, id);
-	if (i != -1) {
+	i = getcpatptr(comp_strptr(&ipl, NULL, 1), i, s, id);
+	if (i != -1 && i >= ipl) {
 	    ignore_prefix(i - ipl);
 	    return 1;
 	}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index d055c45c1..28781e7f0 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -478,6 +478,17 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
     Thingy t;
     Widget w, cw;
 
+#ifdef DYNAMIC
+    if (!require_module(name, "compctl", 0, 0)) {
+	zerrnam(name, "can't load compctl module", NULL, 0);
+	return 1;
+    }
+#else
+    if (!makecompparamsptr) {
+	zerrnam(name, "compctl module not available", NULL, 0);
+	return 1;
+    }
+#endif
     t = rthingy(args[1]);
     cw = t->widget;
     unrefthingy(t);
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index af78e1c06..4b42640e1 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -214,11 +214,9 @@ static Cmlist mstack;
 
 /* The completion matchers used when building new stuff for the line. */
 
-static struct cmlist bmstack0, bmstack1;
-static Cmlist bmstack;
-static int had_lm;
+static Cmlist bmatchers;
 
-/* A list with references to all matcher we used. */
+/* A list with references to all matchers we used. */
 
 static LinkList matchers;
 
@@ -293,12 +291,24 @@ enum { COMP_COMPLETE,
 
 static int lastambig;
 
-/* Non-zero if the string on the line came from a previous completion. *
- * This is used to detect if the string should be taken as an exact    *
- * match (see do_ambiguous()).                                         */
+/* This says what of the state the line is in when completion is started *
+ * came from a previous completion. If the FC_LINE bit is set, the       *
+ * string was inserted. If FC_INWORD is set, the last completion moved   *
+ * the cursor into the word although it was at the end of it when the    *
+ * last completion was invoked.                                          *
+ * This is used to detect if the string should be taken as an exact      *
+ * match (see do_ambiguous()) and if the cursor has to be moved to the   *
+ * end of the word before generating the completions.                    */
 
 static int fromcomp;
 
+/* This holds the end-position of the last string inserted into the line. */
+
+static int lastend;
+
+#define FC_LINE   1
+#define FC_INWORD 2
+
 /**/
 void
 completecall(void)
@@ -607,6 +617,12 @@ docomplete(int lst)
 	return;
     }
 
+    /* We may have to reset the cursor to its position after the   *
+     * string inserted by the last completion. */
+
+    if (fromcomp & FC_INWORD)
+	cs = lastend;
+
     /* Check if we have to start a menu-completion (via automenu). */
 
     if ((amenu = (isset(AUTOMENU) && lastambig &&
@@ -1567,6 +1583,83 @@ addtocline(Cline *retp, Cline *lrp,
     *lrp = ln;
 }
 
+/* This compares two cpattern lists and returns non-zero if they are
+ * equal. */
+
+static int
+cmp_cpatterns(Cpattern a, Cpattern b)
+{
+    while (a) {
+	if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
+	    return 0;
+	a = a->next;
+	b = b->next;
+    }
+    return 1;
+}
+
+/* This compares two cmatchers and returns non-zero if they are equal. */
+
+static int
+cmp_cmatchers(Cmatcher a, Cmatcher b)
+{
+    return (a == b ||
+	    (a->flags == b->flags &&
+	     a->llen == b->llen && a->wlen == b->wlen &&
+	     (!a->llen || cmp_cpatterns(a->line, b->line)) &&
+	     (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
+	     (!(a->flags & CMF_LEFT) ||
+	      (a->lalen == b->lalen &&
+	       (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
+	     (!(a->flags & CMF_RIGHT) ||
+	      (a->ralen == b->ralen &&
+	       (!a->ralen || cmp_cpatterns(a->right, b->right))))));
+}
+
+/* Add the given matchers to the bmatcher list. */
+
+static void
+add_bmatchers(Cmatcher m)
+{
+    Cmlist old = bmatchers, *q = &bmatchers, n;
+
+    for (; m; m = m->next) {
+	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
+	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
+	    *q = n = (Cmlist) halloc(sizeof(struct cmlist));
+	    n->matcher = m;
+	    q = &(n->next);
+	}
+    }
+    *q = old;
+}
+
+/* This is called when the matchers in the mstack have changed to
+ * ensure that the bmatchers list contains no matchers not in mstack. */
+
+static void
+update_bmatchers(void)
+{
+    Cmlist p = bmatchers, q = NULL, ms;
+    Cmatcher mp;
+    int t;
+
+    while (p) {
+	t = 0;
+	for (ms = mstack; ms && !t; ms = ms->next)
+	    for (mp = ms->matcher; mp && !t; mp = mp->next)
+		t = cmp_cmatchers(mp, p->matcher);
+
+	p = p->next;
+	if (!t) {
+	    if (q)
+		q->next = p;
+	    else
+		bmatchers = p;
+	}
+    }
+}
+
 /* When building up cline lists that are to be inserted at the end of the
  * string or on the left hand side in the middle, we do this separately for
  * multiple runs of characters separated by the anchors of `*' match patterns.
@@ -1582,21 +1675,20 @@ end_list(int len, char *str)
     char *p = str;
 
     while (len) {
-	for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-	    for (mp = ms->matcher; mp && !t; mp = mp->next) {
-		if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
-		    !mp->llen && len >= mp->ralen && mp->ralen &&
-		    pattern_match(mp->right, str, NULL, NULL)) {
-		    /* This is one of those patterns, so add a cline struct.
-		     * We store the anchor string in the line and the contents
-		     * (i.e. the strings between the anchors) in the word field. */
-		    *q = getcline(str, mp->ralen, p, str - p, 0);
-		    q = &((*q)->next);
-		    str += mp->ralen;
-		    len -= mp->ralen;
-		    p = str;
-		    t = 1;
-		}
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+		!mp->llen && len >= mp->ralen && mp->ralen &&
+		pattern_match(mp->right, str, NULL, NULL)) {
+		/* This is one of those patterns, so add a cline struct.
+		 * We store the anchor string in the line and the contents
+		 * (i.e. the strings between the anchors) in the word field. */
+		*q = getcline(str, mp->ralen, p, str - p, 0);
+		q = &((*q)->next);
+		str += mp->ralen;
+		len -= mp->ralen;
+		p = str;
+		t = 1;
 	    }
 	}
 	if (!t) {
@@ -1616,7 +1708,7 @@ end_list(int len, char *str)
 
 /* This builds a string that may be put on the line that fully matches the
  * given strings. The return value is NULL if no such string could be built
- * or that string, allocated on the heap.
+ * or that string in local static memory, dup it.
  * All this is a lot like the procedure in bld_new_pfx(), only slightly
  * simpler, see that function for more comments. */
 
@@ -1644,60 +1736,59 @@ join_strs(int la, char *sa, int lb, char *sb)
     }
     while (la && lb) {
 	if (*sa != *sb) {
-	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-		for (mp = ms->matcher; mp && !t; mp = mp->next) {
-		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-			mp->wlen <= la && mp->wlen <= lb) {
-			if (pattern_match(mp->word, sa, NULL, ea)) {
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
-			    }
-			    if ((bl = bld_line_pfx(mp->line, line, line,
-						   lb, sb, ea))) {
-				line[mp->llen] = '\0';
-				if (rr <= mp->llen) {
-				    char *or = rs;
-
-				    rs = realloc(rs, (rl += 20));
-				    rr += 20;
-				    rp += rs - or;
-				}
-				memcpy(rp, line, mp->llen);
-				rp += mp->llen;
-				rr -= mp->llen;
-				sa += mp->wlen;
-				sb += bl;
-				la -= mp->wlen;
-				lb -= bl;
-				t = 1;
-			    }
-			} else if (pattern_match(mp->word, sb, NULL, ea)) {
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
+	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+		mp = ms->matcher;
+		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+		    mp->wlen <= la && mp->wlen <= lb) {
+		    if (pattern_match(mp->word, sa, NULL, ea)) {
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			if ((bl = bld_line_pfx(mp->line, line, line,
+					       lb, sb, ea))) {
+			    line[mp->llen] = '\0';
+			    if (rr <= mp->llen) {
+				char *or = rs;
+
+				rs = realloc(rs, (rl += 20));
+				rr += 20;
+				rp += rs - or;
 			    }
-			    if ((bl = bld_line_pfx(mp->line, line, line,
-						   la, sa, ea))) {
-				line[mp->llen] = '\0';
-				if (rr <= mp->llen) {
-				    char *or = rs;
-
-				    rs = realloc(rs, (rl += 20));
-				    rr += 20;
-				    rp += rs - or;
-				}
-				memcpy(rp, line, mp->llen);
-				rp += mp->llen;
-				rr -= mp->llen;
-				sa += bl;
-				sb += mp->wlen;
-				la -= bl;
-				lb -= mp->wlen;
-				t = 1;
+			    memcpy(rp, line, mp->llen);
+			    rp += mp->llen;
+			    rr -= mp->llen;
+			    sa += mp->wlen;
+			    sb += bl;
+			    la -= mp->wlen;
+			    lb -= bl;
+			    t = 1;
+			}
+		    } else if (pattern_match(mp->word, sb, NULL, ea)) {
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			if ((bl = bld_line_pfx(mp->line, line, line,
+					       la, sa, ea))) {
+			    line[mp->llen] = '\0';
+			    if (rr <= mp->llen) {
+				char *or = rs;
+
+				rs = realloc(rs, (rl += 20));
+				rr += 20;
+				rp += rs - or;
 			    }
+			    memcpy(rp, line, mp->llen);
+			    rp += mp->llen;
+			    rr -= mp->llen;
+			    sa += bl;
+			    sb += mp->wlen;
+			    la -= bl;
+			    lb -= mp->wlen;
+			    t = 1;
 			}
 		    }
 		}
@@ -1725,7 +1816,7 @@ join_strs(int la, char *sa, int lb, char *sb)
 
     *rp = '\0';
 
-    return dupstring(rs);
+    return rs;
 }
 
 /* This gets two cline lists with separated runs and joins the corresponding
@@ -1752,26 +1843,55 @@ join_ends(Cline o, Cline n, int *olp, int *nlp)
 	     * and mark the cline so that we don't try to join it again. */
 	    o->flags |= CLF_JOIN;
 	    o->llen = strlen(j);
-	    o->line = j;
+	    o->line = dupstring(j);
 	    bld_pfx(o, n, &mol, &mnl);
 	    smol += mol + o->llen;
 	    smnl += mnl + n->llen;
 	} else {
-	    /* Different anchor strings, so shorten the list. */
+	    /* Different anchors, see if we can find matching anchors
+	     * further down the lists. */
+	    Cline to, tn;
+	    int t = 0;
+
+	    /* But first build the common prefix. */
 	    bld_pfx(o, n, &mol, &mnl);
 	    smol += mol;
 	    smnl += mnl;
-	    o->flags |= CLF_MISS;
-	    o->next = NULL;
-	    o->llen = 0;
-	    if (olp)
-		*olp = smol;
-	    if (nlp)
-		*nlp = smnl;
-	    return ret;
+
+	    for (to = o; to && !t; to = to->next) {
+		for (tn = n; tn && !t; tn = tn->next) {
+		    if ((t = ((to->llen == tn->llen &&
+			       !strncmp(to->line, tn->line, to->llen)) ||
+			      (!(to->flags & CLF_JOIN) &&
+			       join_strs(to->llen, to->line,
+					 tn->llen, tn->line)))))
+			break;
+		}
+		if (t)
+		    break;
+	    }
+	    if (t) {
+		/* Found matching anchors, continue with them. */
+		o->line = to->line;
+		o->llen = to->llen;
+		o->next = to->next;
+		o->flags |= CLF_MISS;
+		n = tn;
+	    } else {
+		/* No matching anchors found, shorten the list. */
+		o->flags |= CLF_MISS;
+		o->next = NULL;
+		o->llen = 0;
+		if (olp)
+		    *olp = smol;
+		if (nlp)
+		    *nlp = smnl;
+		return ret;
+	    }
 	}
 	/* If we reached the end of the new list but not the end of the old
-	 * list, we mark the old list (saying that characters are missing here). */
+	 * list, we mark the old list (saying that characters are missing 
+	 * here). */
 	if (!(n = n->next) && o->next)
 	    o->flags |= CLF_MISS;
 	o = o->next;
@@ -1859,20 +1979,19 @@ bld_line_pfx(Cpattern pat, char *line, char *lp,
 		rl++;
 	    } else {
 		t = 0;
-		for (ms = bmstack; ms && !t; ms = ms->next) {
-		    for (mp = ms->matcher; mp && !t; mp = mp->next) {
-			if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			    pattern_match(mp->line, line, NULL, ea) &&
-			    pattern_match(mp->word, word, ea, NULL)) {
-			    /* Both the line and the word pattern matched,
-			     * now skip over the matched portions. */
-			    line += mp->llen;
-			    word += mp->wlen;
-			    l -= mp->llen;
-			    wlen -= mp->wlen;
-			    rl += mp->wlen;
-			    t = 1;
-			}
+		for (ms = bmatchers; ms && !t; ms = ms->next) {
+		    mp = ms->matcher;
+		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			pattern_match(mp->line, line, NULL, ea) &&
+			pattern_match(mp->word, word, ea, NULL)) {
+			/* Both the line and the word pattern matched,
+			 * now skip over the matched portions. */
+			line += mp->llen;
+			word += mp->wlen;
+			l -= mp->llen;
+			wlen -= mp->wlen;
+			rl += mp->wlen;
+			t = 1;
 		    }
 		}
 		if (!t)
@@ -1923,100 +2042,99 @@ bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
     while (ol && nl) {
 	if (*ow != *nw) {
 	    /* Not the same character, use the patterns. */
-	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-		for (mp = ms->matcher; mp && !t; mp = mp->next) {
-		    /* We use only those patterns that match a non-empty
-		     * string in both the line and the word, that match
-		     * strings no longer than the string we still have
-		     * to compare, that don't have anchors, and that don't
-		     * use the line strings for insertion. */
-		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-			mp->wlen <= ol && mp->wlen <= nl) {
-			/* Try the pattern only if the word-pattern matches
-			 * one of the strings. */
-			if (pattern_match(mp->word, ow, NULL, ea)) {
-			    /* Make the buffer where we build the possible
-			     * line patterns big enough. */
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
-			    }
-			    /* Then build all the possible lines and see
-			     * if one of them matches the othe string. */
-			    if ((bl = bld_line_pfx(mp->line, line, line,
-						   nl, nw, ea))) {
-				/* Yep, one of the lines matched the other
-				 * string. */
-				if (p != ow) {
-				    /* There is still a substring that is
-				     * the same in both strings, so add
-				     * a cline for it. */
-				    char sav = *ow;
-
-				    *ow = '\0';
-				    n = getcline(NULL, 0, dupstring(p),
-						 ow - p, 0);
-				    *ow = sav;
-				    if (l)
-					l->next = n;
-				    else
-					ret = n;
-				    l = n;
-				}
-				/* Then we add the line string built. */
-				line[mp->llen] = '\0';
-				n = getcline(dupstring(line), mp->llen,
-					     NULL, 0, CLF_DIFF);
+	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+		mp = ms->matcher;
+		/* We use only those patterns that match a non-empty
+		 * string in both the line and the word, that match
+		 * strings no longer than the string we still have
+		 * to compare, that don't have anchors, and that don't
+		 * use the line strings for insertion. */
+		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+		    mp->wlen <= ol && mp->wlen <= nl) {
+		    /* Try the pattern only if the word-pattern matches
+		     * one of the strings. */
+		    if (pattern_match(mp->word, ow, NULL, ea)) {
+			/* Make the buffer where we build the possible
+			 * line patterns big enough. */
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			/* Then build all the possible lines and see
+			 * if one of them matches the othe string. */
+			if ((bl = bld_line_pfx(mp->line, line, line,
+					       nl, nw, ea))) {
+			    /* Yep, one of the lines matched the other
+			     * string. */
+			    if (p != ow) {
+				/* There is still a substring that is
+				 * the same in both strings, so add
+				 * a cline for it. */
+				char sav = *ow;
+
+				*ow = '\0';
+				n = getcline(NULL, 0, dupstring(p),
+					     ow - p, 0);
+				*ow = sav;
 				if (l)
 				    l->next = n;
 				else
 				    ret = n;
 				l = n;
-				ow += mp->wlen;
-				nw += bl;
-				ol -= mp->wlen;
-				nl -= bl;
-				p = ow;
-				t = 1;
-			    }
-			} else if (pattern_match(mp->word, nw, NULL, ea)) {
-			    /* Now do the same for the other string. */
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
 			    }
-			    if ((bl = bld_line_pfx(mp->line, line, line,
-						   ol, ow, ea))) {
-				if (p != ow) {
-				    char sav = *ow;
-
-				    *ow = '\0';
-				    n = getcline(NULL, 0, dupstring(p),
-						 ow - p, 0);
-				    *ow = sav;
-				    if (l)
-					l->next = n;
-				    else
-					ret = n;
-				    l = n;
-				}
-				line[mp->llen] = '\0';
-				n = getcline(dupstring(line), mp->llen,
-					     NULL, 0, CLF_DIFF);
+			    /* Then we add the line string built. */
+			    line[mp->llen] = '\0';
+			    n = getcline(dupstring(line), mp->llen,
+					 NULL, 0, CLF_DIFF);
+			    if (l)
+				l->next = n;
+			    else
+				ret = n;
+			    l = n;
+			    ow += mp->wlen;
+			    nw += bl;
+			    ol -= mp->wlen;
+			    nl -= bl;
+			    p = ow;
+			    t = 1;
+			}
+		    } else if (pattern_match(mp->word, nw, NULL, ea)) {
+			/* Now do the same for the other string. */
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			if ((bl = bld_line_pfx(mp->line, line, line,
+					       ol, ow, ea))) {
+			    if (p != ow) {
+				char sav = *ow;
+
+				*ow = '\0';
+				n = getcline(NULL, 0, dupstring(p),
+					     ow - p, 0);
+				*ow = sav;
 				if (l)
 				    l->next = n;
 				else
 				    ret = n;
 				l = n;
-				ow += bl;
-				nw += mp->wlen;
-				ol -= bl;
-				nl -= mp->wlen;
-				p = ow;
-				t = 1;
 			    }
+			    line[mp->llen] = '\0';
+			    n = getcline(dupstring(line), mp->llen,
+					 NULL, 0, CLF_DIFF);
+			    if (l)
+				l->next = n;
+			    else
+				ret = n;
+			    l = n;
+			    ow += bl;
+			    nw += mp->wlen;
+			    ol -= bl;
+			    nl -= mp->wlen;
+			    p = ow;
+			    t = 1;
 			}
 		    }
 		}
@@ -2107,19 +2225,18 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 		    ll--;
 		    len--;
 		} else {
-		    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-			for (mp = ms->matcher; mp && !t; mp = mp->next) {
-			    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-				mp->wlen <= len && mp->llen <= len &&
-				pattern_match(mp->word, word, NULL, ea) &&
-				pattern_match(mp->line, p, ea, NULL)) {
-				/* We have a matched substring, skip over. */
-				p += mp->llen;
-				word += mp->wlen;
-				ll -= mp->llen;
-				len -= mp->wlen;
-				t = 1;
-			    }
+		    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+			mp = ms->matcher;
+			if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+			    mp->wlen <= len && mp->llen <= len &&
+			    pattern_match(mp->word, word, NULL, ea) &&
+			    pattern_match(mp->line, p, ea, NULL)) {
+			    /* We have a matched substring, skip over. */
+			    p += mp->llen;
+			    word += mp->wlen;
+			    ll -= mp->llen;
+			    len -= mp->wlen;
+			    t = 1;
 			}
 		    }
 		    if (!t)
@@ -2148,6 +2265,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 		    l->next = line;
 		else
 		    ret = line;
+		l = line;
 	    }
 	} else {
 	    /* The cline doesn't have a string built by reverse matching,
@@ -2163,6 +2281,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 		    l->next = line;
 		else
 		    ret = line;
+		l = line;
 		len = 0;
 	    } else {
 		char sav = word[len];
@@ -2180,6 +2299,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 			l->next = line;
 		    else
 			ret = line;
+		    l = line;
 		    len = 0;
 		} else if (strpfx(line->word, word)) {
 		    word[len] = sav;
@@ -2190,6 +2310,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 			l->next = line;
 		    else
 			ret = line;
+		    l = line;
 		    len = 0;
 		} else {
 		    /* Not the same and no prefix, so we try to build a
@@ -2208,6 +2329,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 			    l->next = sl;
 			else
 			    ret = sl;
+			l = sl;
 			if (!mol) {
 			    send->next = next;
 			    word += len - mnl;
@@ -2238,7 +2360,11 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
 static void
 bld_pfx(Cline o, Cline n, int *olp, int *nlp)
 {
-    if (o->flags & CLF_NEW) {
+    if (olp)
+	*olp = 0;
+    if (nlp)
+	*nlp = 0;
+    if (o->flags & CLF_PNEW) {
 	if (o->flags & (CLF_END | CLF_MID))
 	    /* We split the suffix in the middle and at the end into
 	     * separate runs. */
@@ -2255,7 +2381,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
 		o->flags |= CLF_MISS;
 	}
     } else if (o->flags & (CLF_END | CLF_MID)) {
-	o->flags |= CLF_NEW;
+	o->flags |= CLF_PNEW;
 	o->prefix = join_ends(end_list(o->wlen, o->word),
 			      end_list(n->wlen, n->word), olp, nlp);
     } else if (o->wlen && n->wlen) {
@@ -2280,7 +2406,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
 	     * matches both strings from the original cline structs
 	     * and thus can be put in the command line to represent
 	     * them. This cline list is stored in o. */
-	    o->flags |= CLF_NEW;
+	    o->flags |= CLF_PNEW;
 	    o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word,
 				    &mol, &mnl, NULL);
 	    newl = 0;
@@ -2300,7 +2426,8 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
 
 	if (!o->prefix && n->wlen != o->wlen)
 	    o->flags |= CLF_MISS;
-    }
+    } else
+	o->wlen = 0;
 }
 
 /* The following function are like their counterparts above, only for
@@ -2353,20 +2480,19 @@ bld_line_sfx(Cpattern pat, char *line, char *lp,
 		rl++;
 	    } else {
 		t = 0;
-		for (ms = bmstack; ms && !t; ms = ms->next) {
-		    for (mp = ms->matcher; mp && !t; mp = mp->next) {
-			if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			    pattern_match(mp->line, line - mp->llen,
-					  NULL, ea) &&
-			    pattern_match(mp->word, word - mp->wlen,
-					  ea, NULL)) {
-			    line -= mp->llen;
-			    word -= mp->wlen;
-			    l -= mp->llen;
-			    wlen -= mp->wlen;
-			    rl += mp->wlen;
-			    t = 1;
-			}
+		for (ms = bmatchers; ms && !t; ms = ms->next) {
+		    mp = ms->matcher;
+		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			pattern_match(mp->line, line - mp->llen,
+				      NULL, ea) &&
+			pattern_match(mp->word, word - mp->wlen,
+				      ea, NULL)) {
+			line -= mp->llen;
+			word -= mp->wlen;
+			l -= mp->llen;
+			wlen -= mp->wlen;
+			rl += mp->wlen;
+			t = 1;
 		    }
 		}
 		if (!t)
@@ -2404,84 +2530,83 @@ bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
     }
     while (ol && nl) {
 	if (ow[-1] != nw[-1]) {
-	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-		for (mp = ms->matcher; mp && !t; mp = mp->next) {
-		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-			mp->wlen <= ol && mp->wlen <= nl) {
-			if (pattern_match(mp->word, ow - mp->wlen,
-					  NULL, ea)) {
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
-			    }
-			    if ((bl = bld_line_sfx(mp->line, line, line,
-						   nl, nw, ea))) {
-				if (p != ow) {
-				    char sav = *p;
-
-				    *p = '\0';
-				    n = getcline(NULL, 0, dupstring(ow),
-						 p - ow, 0);
-				    *p = sav;
-				    if (l)
-					l->next = n;
-				    else
-					ret = n;
-				    l = n;
-				}
-				line[mp->llen] = '\0';
-				n = getcline(dupstring(line), mp->llen,
-					     NULL, 0, CLF_DIFF);
+	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+		mp = ms->matcher;
+		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+		    mp->wlen <= ol && mp->wlen <= nl) {
+		    if (pattern_match(mp->word, ow - mp->wlen,
+				      NULL, ea)) {
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			if ((bl = bld_line_sfx(mp->line, line, line,
+					       nl, nw, ea))) {
+			    if (p != ow) {
+				char sav = *p;
+
+				*p = '\0';
+				n = getcline(NULL, 0, dupstring(ow),
+					     p - ow, 0);
+				*p = sav;
 				if (l)
 				    l->next = n;
 				else
 				    ret = n;
 				l = n;
-				ow -= mp->wlen;
-				nw -= bl;
-				ol -= mp->wlen;
-				nl -= bl;
-				p = ow;
-				t = 1;
 			    }
-			} else if (pattern_match(mp->word, nw - mp->wlen,
-						 NULL, ea)) {
-			    if (mp->llen + 1 > llen) {
-				if (llen)
-				    zfree(line, llen);
-				line = (char *) zalloc(llen = mp->llen + 20);
-			    }
-			    if ((bl = bld_line_sfx(mp->line, line, line,
-						   ol, ow, ea))) {
-				if (p != ow) {
-				    char sav = *p;
-
-				    *p = '\0';
-				    n = getcline(NULL, 0, dupstring(ow),
-						 p - ow, 0);
-				    *p = sav;
-				    if (l)
-					l->next = n;
-				    else
-					ret = n;
-				    l = n;
-				}
-				line[mp->llen] = '\0';
-				n = getcline(dupstring(line), mp->llen,
-					     NULL, 0, CLF_DIFF);
+			    line[mp->llen] = '\0';
+			    n = getcline(dupstring(line), mp->llen,
+					 NULL, 0, CLF_DIFF);
+			    if (l)
+				l->next = n;
+			    else
+				ret = n;
+			    l = n;
+			    ow -= mp->wlen;
+			    nw -= bl;
+			    ol -= mp->wlen;
+			    nl -= bl;
+			    p = ow;
+			    t = 1;
+			}
+		    } else if (pattern_match(mp->word, nw - mp->wlen,
+					     NULL, ea)) {
+			if (mp->llen + 1 > llen) {
+			    if (llen)
+				zfree(line, llen);
+			    line = (char *) zalloc(llen = mp->llen + 20);
+			}
+			if ((bl = bld_line_sfx(mp->line, line, line,
+					       ol, ow, ea))) {
+			    if (p != ow) {
+				char sav = *p;
+
+				*p = '\0';
+				n = getcline(NULL, 0, dupstring(ow),
+					     p - ow, 0);
+				*p = sav;
 				if (l)
 				    l->next = n;
 				else
 				    ret = n;
 				l = n;
-				ow -= bl;
-				nw -= mp->wlen;
-				ol -= bl;
-				nl -= mp->wlen;
-				p = ow;
-				t = 1;
 			    }
+			    line[mp->llen] = '\0';
+			    n = getcline(dupstring(line), mp->llen,
+					 NULL, 0, CLF_DIFF);
+			    if (l)
+				l->next = n;
+			    else
+				ret = n;
+			    l = n;
+			    ow -= bl;
+			    nw -= mp->wlen;
+			    ol -= bl;
+			    nl -= mp->wlen;
+			    p = ow;
+			    t = 1;
 			}
 		    }
 		}
@@ -2554,21 +2679,20 @@ join_new_sfx(Cline line, int len, char *word, int *missp)
 		    len--;
 		    ind++;
 		} else {
-		    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
-			for (mp = ms->matcher; mp && !t; mp = mp->next) {
-			    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-				mp->wlen <= len && mp->llen <= len &&
-				pattern_match(mp->word, word - mp->wlen,
-					      NULL, ea) &&
-				pattern_match(mp->line, p - mp->llen,
-					      ea, NULL)) {
-				p -= mp->llen;
-				word -= mp->wlen;
-				ll -= mp->llen;
-				len -= mp->wlen;
-				ind += mp->wlen;
-				t = 1;
-			    }
+		    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+			mp = ms->matcher;
+			if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+			    mp->wlen <= len && mp->llen <= len &&
+			    pattern_match(mp->word, word - mp->wlen,
+					  NULL, ea) &&
+			    pattern_match(mp->line, p - mp->llen,
+					  ea, NULL)) {
+			    p -= mp->llen;
+			    word -= mp->wlen;
+			    ll -= mp->llen;
+			    len -= mp->wlen;
+			    ind += mp->wlen;
+			    t = 1;
 			}
 		    }
 		    if (!t)
@@ -2663,7 +2787,7 @@ join_new_sfx(Cline line, int len, char *word, int *missp)
 static void
 bld_sfx(Cline o, Cline n)
 {
-    if (o->flags & CLF_NEW) {
+    if (o->flags & CLF_SNEW) {
 	int miss;
 
 	o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss);
@@ -2679,7 +2803,7 @@ bld_sfx(Cline o, Cline n)
 	    new = dupstring(n->word);
 	    newl = n->wlen;
 	} else if (!strpfx(o->word, n->word)) {
-	    o->flags |= CLF_NEW;
+	    o->flags |= CLF_SNEW;
 	    o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word,
 				    &mol, &mnl, NULL);
 	    newl = 0;
@@ -2732,7 +2856,7 @@ join_clines(Cline o, Cline n)
 		    n = q;
 		}
 	    }
-	    if (n->flags & CLF_MID) {
+	    if (n && n->flags & CLF_MID) {
 		while (o && !(o->flags & CLF_MID)) {
 		    o->word = NULL;
 		    o->flags |= CLF_DIFF;
@@ -3128,6 +3252,7 @@ inst_cline(Cline l, int pl, int sl)
 	}
 	l = l->next;
     }
+    lastend = cs;
     /* Now place the cursor. Preferably in a position where something
      * is missing, otherwise in a place where the string differs from
      * any of the matches, or just leave it at the end. */
@@ -3281,6 +3406,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 				     * to continue after the matched portion. */
 				    w = q;
 				    iw = j;
+				    lw = k;
 				    l = p;
 				    il = jj;
 				    ll = kk;
@@ -3501,6 +3627,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 				    }
 				    w = q + 1;
 				    iw = j;
+				    lw = k;
 				    l = p + 1;
 				    il = jj;
 				    ll = kk;
@@ -3765,6 +3892,7 @@ instmatch(Cmatch m)
 	inststrlen(m->suf, 1, (l = strlen(m->suf)));
 	r += l;
     }
+    lastend = cs;
     cs = ocs;
     return r;
 }
@@ -3777,10 +3905,10 @@ instmatch(Cmatch m)
 void
 addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	   char *suf, char *group, char *rems, char *remf, char *ign,
-	   int flags, int aflags, Cmatcher match, char **argv)
+	   int flags, int aflags, Cmatcher match, char *exp, char **argv)
 {
-    char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
-    int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl;
+    char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
+    int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl, llsl;
     Aminfo ai;
     Cline lc = NULL;
     LinkList l;
@@ -3788,43 +3916,39 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
     struct cmlist mst;
     Cmlist oms = mstack;
 
-    /* Select the set of matches. */
-    if (aflags & CAF_ALT) {
-	l = fmatches;
-	ai = fainfo;
-    } else {
-	l = matches;
-	ai = ainfo;
-    }
-    /* Store the matcher in our stack of matchers. */
-    if (match) {
-	mst.next = mstack;
-	mst.matcher = match;
-	mstack = &mst;
-	if (had_lm && mnum)
-	    bmstack1.matcher = NULL;
-	else {
-	    bmstack1.matcher = match;
-	    had_lm = 1;
-	}
-	addlinknode(matchers, match);
-	match->refc++;
-    } else {
-	bmstack1.matcher = NULL;
-	if (mnum)
-	    had_lm = 1;
-    }
     /* Use menu-completion (-U)? */
     if ((aflags & CAF_MENU) && isset(AUTOMENU))
 	usemenu = 1;
-    /* Get the suffixes to ignore. */
-    if (ign)
-	aign = get_user_var(ign);
 
     /* Switch back to the heap that was used when the completion widget
      * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
+	    if (exp) {
+		expl = (Cexpl) halloc(sizeof(struct cexpl));
+		expl->count = expl->fcount = 0;
+		expl->str = dupstring(exp);
+	    } else
+		expl = NULL;
+
+	    /* Store the matcher in our stack of matchers. */
+	    if (match) {
+		mst.next = mstack;
+		mst.matcher = match;
+		mstack = &mst;
+
+		if (!mnum)
+		    add_bmatchers(match);
+
+		addlinknode(matchers, match);
+		match->refc++;
+	    }
+	    if (mnum && (mstack || bmatchers))
+		update_bmatchers();
+
+	    /* Get the suffixes to ignore. */
+	    if (ign)
+		aign = get_user_var(ign);
 	    /* Get the contents of the completion variables if we have
 	     * to perform matching. */
 	    if (aflags & CAF_MATCH) {
@@ -3832,6 +3956,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		lpre = dupstring(compprefix);
 		llpl = strlen(lpre);
 		lsuf = dupstring(compsuffix);
+		llsl = strlen(lsuf);
 	    }
 	    /* Now duplicate the strings we have from the command line. */
 	    if (ipre)
@@ -3848,8 +3973,6 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		lsl = strlen(psuf);
 	    } else
 		lsl = 0;
-	    if (aflags & CAF_MATCH)
-		lsm = (psuf ? !strcmp(psuf, lsuf) : (!lsuf || !*lsuf));
 	    if (pre)
 		pre = dupstring(pre);
 	    if (suf)
@@ -3865,6 +3988,17 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		begcmgroup(group, (aflags & CAF_NOSORT));
 		if (aflags & CAF_NOSORT)
 		    mgroup->flags |= CGF_NOSORT;
+	    } else {
+		endcmgroup(NULL);
+		begcmgroup("default", 0);
+	    }
+	    /* Select the set of matches. */
+	    if (aflags & CAF_ALT) {
+		l = fmatches;
+		ai = fainfo;
+	    } else {
+		l = matches;
+		ai = ainfo;
 	    }
 	    if (remf) {
 		remf = dupstring(remf);
@@ -3908,19 +4042,19 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		}
 		if (aflags & CAF_MATCH) {
 		    /* Do the matching. */
-		    t = (ppre ? dyncat(ppre, s) : s);
-		    pl = sl + lpl;
-		    if ((test = (llpl <= pl && !strncmp(t, lpre, pl))))
-			test = lsm;
+		    test = (sl >= llpl + llsl &&
+			    strpfx(lpre, s) && strsfx(lsuf, s));
 		    if (!test && mstack &&
-			(ms = comp_match(lpre, lsuf,
-					 (psuf ? dyncat(t, psuf) : t),
+			(ms = comp_match(lpre, lsuf, s,
 					 &lc, (aflags & CAF_QUOTE),
 					 &bpl, &bsl)))
 			test = 1;
+
 		    if (!test)
 			continue;
-		    me = e = s + sl;
+		    pl = sl - llsl;
+		    me = s + sl - llsl;
+		    e = s + llpl;
 		} else {
 		    e = s;
 		    me = s + sl;
@@ -3928,8 +4062,10 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		}
 		/* Quoting? */
 		if (!(aflags & CAF_QUOTE)) {
-		    te = s + pl;
-		    s = quotename(s, &e, te, &pl);
+		    int tmp = me - s;
+
+		    s = quotename(s, &e, me, &tmp);
+		    me = s + tmp;
 		    sl = strlen(s);
 		}
 		/* The rest is almost the same as in addmatch(). */
@@ -3944,31 +4080,48 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (ppre)
 		    t = dyncat(ppre, t);
 		if (!ms && mstack) {
+		    int bl = ((aflags & CAF_MATCH) ? llpl : 0);
 		    Cline *clp = &lc, tlc;
 		    char *ss = dupstring(s), *ee = me + (ss - s);
 
 		    if (ppre && *ppre) {
-			*clp = str_cline(ppre, strlen(ppre), &tlc);
+			*clp = tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
 			clp = &(tlc->next);
 		    }
-		    if (ee != ss + sl || (lpsuf && *lpsuf)) {
-			*clp = tlc = getcline(ss, ee - ss,
+		    if (bl) {
+			*clp = str_cline(ss, bl, &tlc);
+			clp = &(tlc->next);
+		    }
+		    if (ee != ss + sl) {
+			*clp = tlc = getcline(ss + bl, ee - ss - bl,
 					      NULL, 0, CLF_MID);
 			clp = &(tlc->next);
-			if (ee != ss + sl) {
-			    *clp = str_cline(ee, (ss + sl) - ee, &tlc);
-			    clp = &(tlc->next);
-			}
-			if (lpsuf && *lpsuf) {
-			    *clp = str_cline(lpsuf, strlen(lpsuf), &tlc);
-			    clp = &(tlc->next);
-			}
+			*clp = str_cline(ee, (ss + sl) - ee, &tlc);
+			clp = &(tlc->next);
 		    } else {
-			*clp = tlc = getcline(NULL, 0, ss, sl,
+			*clp = tlc = getcline(NULL, 0, ss + bl, sl - bl,
+					      CLF_END | CLF_VAR);
+			clp = &(tlc->next);
+		    }
+		    if (psuf && *psuf) {
+			*clp = tlc = getcline(NULL, 0, psuf, lsl,
 					      CLF_END | CLF_VAR);
 			clp = &(tlc->next);
 		    }
 		    *clp = NULL;
+		} else if (mstack) {
+		    Cline tlc;
+
+		    if (ppre && *ppre) {
+			tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
+			tlc->next = lc;
+			lc = tlc;
+		    }
+		    if (psuf && *psuf) {
+			for (tlc = lc; tlc->next; tlc = tlc->next);
+			tlc->next = getcline(NULL, 0, psuf, lsl,
+					     CLF_END | CLF_VAR);
+		    }
 		}
 		if (ipre && *ipre) {
 		    Cline tlc = prepend_cline(ipre, lc);
@@ -4047,6 +4200,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		}
 	    }
 	    compnmatches = mnum;
+	    if (exp)
+		addexpl();
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
@@ -4129,8 +4284,10 @@ addmatch(char *s, char *t)
 	    mpl = fpl; msl = fsl;
 	} else {
 	    if ((cp = filecomp)) {
-		if ((test = domatch(s, filecomp, 0)))
+		if ((test = domatch(s, filecomp, 0))) {
+		    e = s + sl;
 		    cc = 1;
+		}
 	    } else {
 		e = s + sl - fsl;
 		if ((test = !strncmp(s, fpre, fpl)))
@@ -4168,6 +4325,7 @@ addmatch(char *s, char *t)
 		strcat(tt, s);
 		if (lpsuf)
 		    strcat(tt, lpsuf);
+		e += (tt - s);
 		untokenize(s = tt);
 		sl = strlen(s);
 	    }
@@ -4196,9 +4354,10 @@ addmatch(char *s, char *t)
 		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
 	if (sl >= rpl + rsl || mstack) {
-	    if (cp)
+	    if (cp) {
 		test = domatch(s, patcomp, 0);
-	    else {
+		e = s + sl;
+	    } else {
 		e = s + sl - rsl;
 		if ((test = !strncmp(s, rpre, rpl)))
 		    if ((test = !strcmp(e, rsuf))) {
@@ -4888,9 +5047,11 @@ makecomplist(char *s, int incmd, int lst)
 {
     struct cmlist ms;
     Cmlist m;
+    char *os = s;
 
-    /* If we already have a list from a previous execution of this *
-     * function, skip the list building code.                      */
+    /* We build a copy of the list of matchers to use to make sure that this
+     * works even if a shell function called from the completion code changes
+     * the global matchers. */
 
     if ((m = cmatcher)) {
 	Cmlist mm, *mp = &mm;
@@ -4906,16 +5067,19 @@ makecomplist(char *s, int incmd, int lst)
 	m = mm;
     }
     compmatcher = 1;
-    bmstack = &bmstack1;
-    bmstack1.next = &bmstack0;
-    bmstack0.next = NULL;
-    bmstack0.matcher = bmstack1.matcher = NULL;
+
+    /* Walk through the global matchers. */
     for (;;) {
+	bmatchers = NULL;
 	if (m) {
 	    ms.next = NULL;
 	    ms.matcher = m->matcher;
 	    mstack = &ms;
-	    bmstack0.matcher = m->matcher;
+
+	    /* Store the matchers used in the bmatchers list which is used
+	     * when building new parts for the string to insert into the 
+	     * line. */
+	    add_bmatchers(m->matcher);
 	} else
 	    mstack = NULL;
 
@@ -4932,12 +5096,12 @@ makecomplist(char *s, int incmd, int lst)
 	    lastambig = 0;
 	amatches = 0;
 	mnum = 0;
-	had_lm = 0;
 	begcmgroup("default", 0);
 
 	ccused = newlinklist();
 	ccstack = newlinklist();
 
+	s = dupstring(os);
 	if (compfunc)
 	    callcompfunc(s, compfunc);
 	else
@@ -5004,7 +5168,7 @@ ctokenize(char *p)
 
 /**/
 char *
-comp_str(int *ipl, int *pl)
+comp_str(int *ipl, int *pl, int untok)
 {
     char *p = dupstring(compprefix);
     char *s = dupstring(compsuffix);
@@ -5012,12 +5176,14 @@ comp_str(int *ipl, int *pl)
     char *str;
     int lp, ls, lip;
 
-    ctokenize(p);
-    remnulargs(p);
-    ctokenize(s);
-    remnulargs(s);
-    ctokenize(ip);
-    remnulargs(ip);
+    if (!untok) {
+	ctokenize(p);
+	remnulargs(p);
+	ctokenize(s);
+	remnulargs(s);
+	ctokenize(ip);
+	remnulargs(ip);
+    }
     ls = strlen(s);
     lip = strlen(ip);
     lp = strlen(p);
@@ -5041,7 +5207,7 @@ makecomplistcall(Compctl cc)
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
 	    int ooffs = offs, lip, lp;
-	    char *str = comp_str(&lip, &lp);
+	    char *str = comp_str(&lip, &lp, 0);
 
 	    offs = lip + lp;
 	    cc->refc++;
@@ -5073,7 +5239,7 @@ makecomplistctl(int flags)
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
 	    int ooffs = offs, lip, lp;
-	    char *str = comp_str(&lip, &lp), *t;
+	    char *str = comp_str(&lip, &lp, 0), *t;
 	    char *os = cmdstr, **ow = clwords, **p, **q;
 	    int on = clwnum, op = clwpos;
 
@@ -5571,19 +5737,16 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	ms.next = mstack;
 	ms.matcher = cc->matcher;
 	mstack = &ms;
-	if (had_lm && mnum)
-	    bmstack1.matcher = NULL;
-	else {
-	    bmstack1.matcher = cc->matcher;
-	    had_lm = 1;
-	}
+
+	if (!mnum)
+	    add_bmatchers(cc->matcher);
+
 	addlinknode(matchers, cc->matcher);
 	cc->matcher->refc++;
-    } else {
-	bmstack1.matcher = NULL;
-	if (mnum)
-	    had_lm = 1;
     }
+    if (mnum && (mstack || bmatchers))
+	update_bmatchers();
+
     /* Insert the prefix (compctl -P), if any. */
     if (cc->prefix) {
 	int pl = 0;
@@ -6344,7 +6507,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	}
 	
 	if ((tt = cc->explain)) {
-	    if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+	    tt = dupstring(tt);
+	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
 		singsub(&tt);
 		untokenize(tt);
 	    }
@@ -6358,7 +6522,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	begcmgroup("default", 0);
     }
     else if ((tt = cc->explain)) {
-	if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+	tt = dupstring(tt);
+	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
 	    singsub(&tt);
 	    untokenize(tt);
 	}
@@ -6631,7 +6796,7 @@ begcmgroup(char *n, int nu)
 	}
     }
     mgroup = (Cmgroup) halloc(sizeof(struct cmgroup));
-    mgroup->name = n;
+    mgroup->name = dupstring(n);
     mgroup->flags = mgroup->lcount = mgroup->mcount = 0;
     mgroup->matches = NULL;
     mgroup->ylist = NULL;
@@ -6872,7 +7037,7 @@ do_ambiguous(void)
     /* If we have to insert the first match, call do_single().  This is *
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
      * completion into an unambiguous one.                              */
-    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !fromcomp &&
+    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !(fromcomp & FC_LINE) &&
 	(usemenu == 0 || unset(AUTOMENU))) {
 	do_single(ainfo->exactm);
 	invalidatelist();
@@ -6945,7 +7110,6 @@ do_ambiguous(void)
 		    merge_cline(lc, ps, lp, NULL, 0, 0);
 	    }
 	    inst_cline(lc, pl, sl);
-
 	} else {
 	    inststrlen(ps, 1, -1);
 	    ocs = cs;
@@ -6966,6 +7130,7 @@ do_ambiguous(void)
 		cs -= brsl;
 		inststrlen(brend, 1, -1);
 	    }
+	    lastend = cs;
 	    cs = ocs;
 	}
 	/* la is non-zero if listambiguous may be used. Copying and
@@ -6973,11 +7138,13 @@ do_ambiguous(void)
 	 * solution. Really. */
 	la = (ll != oll || strncmp(oline, (char *) line, ll));
 
-	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an *
-	 * exact match, we want menu completion the next time round      *
-	 * so we set fromcomp,to ensure that the word on the line is not *
-	 * taken as an exact match.                                      */
-	fromcomp = isset(AUTOMENU);
+	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an  *
+	 * exact match, we want menu completion the next time round       *
+	 * so we set fromcomp,to ensure that the word on the line is not  *
+	 * taken as an exact match. Also we remember if we just moved the *
+	 * cursor into the word.                                          */
+	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
+		    ((atend && cs != lastend) ? FC_INWORD : 0));
 
 	/*
 	 * If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
diff --git a/Src/exec.c b/Src/exec.c
index 59061af3f..764b7140c 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2558,7 +2558,7 @@ execarith(Cmd cmd)
 	val = matheval(e);
     }
     if (isset(XTRACE)) {
-	fprintf(stderr, " ))\n", stderr);
+	fprintf(stderr, " ))\n");
 	fflush(stderr);
     }
     errflag = 0;
diff --git a/Src/glob.c b/Src/glob.c
index 516f75618..47fa63567 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -71,6 +71,7 @@ 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                  */
 static int matchsz;		/* size of matchbuf                     */
 static int matchct;		/* number of matches found              */
@@ -134,7 +135,7 @@ struct complist {
 struct comp {
     Comp left, right, next, exclude;
     char *str;
-    int stat;
+    int stat, errsmax;
 };
 
 /* Type of Comp:  a closure with one or two #'s, the end of a *
@@ -162,6 +163,18 @@ struct comp {
 #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)
+/*
+ * 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))
+
 static char *pptr;		/* current place in string being matched */
 static Comp tail;
 static int first;		/* are leading dots special? */
@@ -352,6 +365,25 @@ 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.                                */
@@ -363,6 +395,7 @@ scanner(Complist q)
     Comp c;
     int closure;
     int pbcwdsav = pathbufcwd;
+    int errssofar = errsfound;
     struct dirsav ds;
 
     ds.ino = ds.dev = 0;
@@ -381,7 +414,7 @@ scanner(Complist q)
     c = q->comp;
     /* Now the actual matching for the current path section. */
     if (!(c->next || c->left) && !haswilds(c->str)
-	&& (!(c->stat & (C_LCMATCHUC|C_IGNCASE))
+	&& (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax)
 	    || !strcmp(".", c->str) || !strcmp("..", c->str))) {
 	/*
 	 * We always need to match . and .. explicitly, even if we're
@@ -433,6 +466,7 @@ scanner(Complist q)
 		((glob_pre && !strpfx(glob_pre, fn))
 		 || (glob_suf && !strsfx(glob_suf, fn))))
 		continue;
+	    errsfound = errssofar;
 	    if (domatch(fn, c, gf_noglobdots)) {
 		/* if this name matchs the pattern... */
 		if (pbcwdsav == pathbufcwd &&
@@ -453,7 +487,34 @@ scanner(Complist q)
 		if (dirs) {
 		    int l;
 
-		    /* if not the last component in the path */
+		    /*
+		     * 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
+		     * match with none.  This will stop later parts of the
+		     * path matching, so we need to check by reducing the
+		     * maximum number of errors and seeing if the directory
+		     * still matches.  Luckily, this is not a terribly
+		     * common case, since complex patterns typically occur
+		     * 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) {
+			    errsfound = errssofar;
+			    if (!domatch(fn, c, gf_noglobdots))
+				break;
+			    errsmax = errsfound - 1;
+			}
+			errsfound = errsmax + 1;
+			errsmax = -1;
+		    }
 		    if (closure) {
 			/* if matching multiple directories */
 			struct stat buf;
@@ -470,9 +531,14 @@ scanner(Complist q)
 			    continue;
 		    }
 		    l = strlen(fn) + 1;
-		    subdirs = hrealloc(subdirs, subdirlen, subdirlen + l);
+		    subdirs = hrealloc(subdirs, subdirlen, subdirlen + l
+				       + sizeof(int));
 		    strcpy(subdirs + subdirlen, fn);
 		    subdirlen += l;
+		    /* store the count of errors made so far, too */
+		    memcpy(subdirs + subdirlen, (char *)&errsfound,
+			   sizeof(int));
+		    subdirlen += sizeof(int);
 		} else
 		    /* if the last filename component, just add it */
 		    insert(fn, 1);
@@ -482,8 +548,11 @@ scanner(Complist q)
 	if (subdirs) {
 	    int oppos = pathpos;
 
-	    for (fn = subdirs; fn < subdirs+subdirlen; fn += strlen(fn) + 1) {
+	    for (fn = subdirs; fn < subdirs+subdirlen; ) {
 		addpath(fn);
+		fn += strlen(fn) + 1;
+		memcpy((char *)&errsfound, fn, sizeof(int));
+		fn += sizeof(int);
 		scanner((q->closure) ? q : q->next);  /* scan next level */
 		pathbuf[pathpos = oppos] = '\0';
 	    }
@@ -502,16 +571,13 @@ scanner(Complist q)
 
 /* Parse a series of path components pointed to by pptr */
 
-/* Flags to apply to current level of grourping */
-
-static int addflags;
-
 /**/
 static Comp
 compalloc(void)
 {
     Comp c = (Comp) alloc(sizeof *c);
     c->stat |= addflags;
+    c->errsmax = errsmax;
     return c;
 }
 
@@ -519,10 +585,19 @@ compalloc(void)
 static int
 getglobflags()
 {
+    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;
@@ -635,6 +710,7 @@ parsecomp(int gflag)
 	    if (eptr == cstr) {
 		/* if no string yet, carry on and get one. */
 		c->stat = addflags;
+		c->errsmax = errsmax;
 		cstr = pptr;
 		continue;
 	    }
@@ -817,6 +893,7 @@ parsecompsw(int gflag)
 {
     Comp c1, c2, c3, excl = NULL, stail = tail;
     int oaddflags = addflags;
+    int oerrsmax = errsmax;
     char *sptr;
 
     /*
@@ -845,12 +922,14 @@ parsecompsw(int gflag)
 	return NULL;
     if (isset(EXTENDEDGLOB) && *pptr == Tilde) {
 	/* Matching remainder of pattern excludes the pattern from matching */
-	int oldmode = mode;
+	int oldmode = mode, olderrsmax = errsmax;
 
 	mode = 1;
+	errsmax = 0;
 	pptr++;
 	excl = parsecomp(gflag);
 	mode = oldmode;
+	errsmax = olderrsmax;
 	if (!excl)
 	    return NULL;
     }
@@ -878,8 +957,10 @@ parsecompsw(int gflag)
 	    c2->stat |= C_PATHADD;
 	c1 = c2;
     }
-    if (!(gflag & GF_TOPLEV))
+    if (!(gflag & GF_TOPLEV)) {
 	addflags = oaddflags;
+	errsmax = oerrsmax;
+    }
     return c1;
 }
 
@@ -965,6 +1046,7 @@ parsepat(char *str)
 {
     mode = 0;			/* path components present */
     addflags = 0;
+    errsmax = 0;
     pptr = str;
     tail = NULL;
     /*
@@ -1102,7 +1184,7 @@ static int
 gmatchcmp(Gmatch a, Gmatch b)
 {
     int i, *s;
-    long r;
+    long r = 0L;
 
     for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
 	switch (*s & ~GS_DESC) {
@@ -1594,10 +1676,14 @@ glob(LinkList list, LinkNode np)
     matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
 					 sizeof(struct gmatch));
     matchct = 0;
+    errsfound = 0;
+    errsmax = -1;
 
     /* 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)
@@ -2427,6 +2513,14 @@ 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 {
@@ -2444,15 +2538,17 @@ static int
 excluded(Comp c, char *eptr)
 {
     char *saves = pptr;
-    int savei = first, savel = longest, ret;
+    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);
 
@@ -2470,13 +2566,18 @@ excluded(Comp c, char *eptr)
     pptr = saves;
     first = savei;
     longest = savel;
+    errsfound = savee;
 
     return ret;
 }
 
+/*
+ * Structure for storing instances of a pattern in a closured.
+ */
 struct gclose {
-    char *start;
-    char *end;
+    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;
 
@@ -2488,34 +2589,48 @@ typedef struct gclose *Gclose;
  * 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, char *trystring)
+addclosures(Comp c, LinkList closlist, int *pdone, unsigned char *trystring)
 {
     Gclose gcnode;
     char *opptr = pptr;
+    unsigned char *tpos;
 
     while (*pptr) {
 	if (STARP(c)) {
-	    if (trystring[(pptr+1)-opptr])
-		break;
+	    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 = ++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 ||
-		trystring[pptr-opptr]) {
+		(*(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)++;
@@ -2523,13 +2638,29 @@ addclosures(Comp c, LinkList closlist, int *pdone, char *trystring)
 }
 
 /*
- * Match characters with case-insensitivity.
- * Note CHARMATCH(x,y) != CHARMATCH(y,x)
+ * 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.
  */
-#define CHARMATCH(x, y) \
-(x == y || (((c->stat & C_IGNCASE) ? (tulower(x) == tulower(y)) : \
-	     (c->stat & C_LCMATCHUC) ? (islower(y) && tuupper(y) == x) : 0)))
 
+/**/
+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);
+    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 */
 
@@ -2539,14 +2670,16 @@ doesmatch(Comp c)
 {
     if (CLOSUREP(c)) {
 	int done, retflag = 0;
-	char *saves, *trystring, *opptr;
+	char *saves, *opptr;
+	unsigned char *trystring, *tpos;
+	int savee;
 	LinkList closlist;
 	Gclose gcnode;
 
 	if (first && *pptr == '.')
 	    return 0;
 
-	if (!inclosure && !c->left) {
+	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
@@ -2561,26 +2694,30 @@ doesmatch(Comp c)
 		!itok(looka)) {
 		/* Another simple optimisation for a very common case:
 		 * we are processing a * and there is
-		 * an ordinary character match next.  We look ahead for
-		 * that character, taking care of Meta bytes.
+		 * 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(STOUC(*pptr), STOUC(looka)))
+			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))))
@@ -2588,6 +2725,7 @@ doesmatch(Comp c)
 		    if (done && OPTIONALP(c))
 			return 0;
 		    pptr = saves;
+		    errsfound = savee;
 		    first = 0;
 		    if (STARP(c)) {
 			if (!*pptr)
@@ -2602,7 +2740,7 @@ doesmatch(Comp c)
 	/* The full, gory backtracking code is now necessary. */
 	inclosure++;
 	closlist = newlinklist();
-	trystring = zcalloc(strlen(pptr)+1);
+	trystring = (unsigned char *)zcalloc(strlen(pptr)+1);
 	opptr = pptr;
 
 	/* Start by making a list where each match is as long
@@ -2621,7 +2759,7 @@ doesmatch(Comp c)
 		retflag = 1;
 		break;
 	    }
-	    trystring[saves-opptr] = 1;
+	    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.
@@ -2633,14 +2771,18 @@ doesmatch(Comp c)
 		char savec = *gcnode->end;
 		*gcnode->end = '\0';
 		pptr = gcnode->start;
+		errsfound = gcnode->errsfound;
 		if (matchonce(c) && pptr != gcnode->start
-		    && !trystring[pptr-opptr]) {
+		    && (!*(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, trystring+(pptr-opptr));
+		    addclosures(c, closlist, &done, tpos);
 		    continue;
 		}
 		*gcnode->end = savec;
@@ -2650,6 +2792,7 @@ doesmatch(Comp c)
 	     */
 	    if ((gcnode = (Gclose)getlinknode(closlist))) {
 		pptr = gcnode->start;
+		errsfound = gcnode->errsfound;
 		zfree(gcnode, sizeof(struct gclose));
 		done--;
 	    } else
@@ -2723,8 +2866,7 @@ rangematch(char **patptr, int ch, int rchar)
 #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;
-	 *pat == Meta ? pat += 2 : pat++) {
+    for (pat++; *pat != Outbrack && *pat; METAINC(pat)) {
 	if (*pat == Inbrack) {
 	    /* Inbrack can only occur inside a range if we found [:...:]. */
 	    pat += 2;
@@ -2748,6 +2890,22 @@ rangematch(char **patptr, int ch, int rchar)
     *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)
@@ -2758,7 +2916,7 @@ matchonce(Comp c)
 	if (!pat || !*pat) {
 	    /* No current pattern (c->str). */
 	    char *saves;
-	    int savei;
+	    int savee, savei;
 
 	    if (errflag)
 		return 0;
@@ -2766,6 +2924,7 @@ matchonce(Comp c)
 	     * 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) {
@@ -2812,6 +2971,7 @@ matchonce(Comp c)
 			 */
 			exclend--;
 			pptr = saves;
+			errsfound = savee;
 		    }
 		    inclosure--;
 		    if (ret)
@@ -2822,19 +2982,43 @@ matchonce(Comp c)
 		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))
+		    if (ret && (!ret2 || pptr < newpptr)) {
 			pptr = newpptr;
+			errsfound = newerrsfound;
+		    }
+		}
+		if (!ret && !ret2) {
+		    pptr = saves;
+		    first = savei;
+		    errsfound = savee;
+		    break;
 		}
-		if (!ret && !ret2)
-		    return 0;
 	    }
 	    if (CLOSUREP(c))
 		return 1;
-	    if (!c->next)	/* no more patterns left */
-		return (!LASTP(c) || !*pptr || longest);
+	    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;
@@ -2843,7 +3027,10 @@ matchonce(Comp c)
 	    }
 	    return doesmatch(c->next);
 	}
-	/* Don't match leading dot if first is set */
+	/*
+	 * 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 */
@@ -2852,39 +3039,47 @@ matchonce(Comp c)
 	    return 1;
 	}
 	first = 0;		/* finished checking start of pattern */
-	if (*pat == Quest && *pptr) {
+	if (*pat == Quest) {
 	    /* match exactly one character */
-	    if (*pptr == Meta)
-		pptr++;
-	    pptr++;
+	    if (!*pptr)
+		break;
+	    METAINC(pptr);
 	    pat++;
 	    continue;
 	}
 	if (*pat == Inbrack) {
 	    /* Match groups of characters */
 	    char ch;
+	    char *saves, *savep;
 
 	    if (!*pptr)
 		break;
-	    ch = *pptr == Meta ? pptr[1] ^ 32 : *pptr;
+	    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)
+		if (*pat != Outbrack) {
+		    pptr = saves;
+		    pat = savep;
 		    break;
+		}
 		pat++;
-		*pptr == Meta ? pptr += 2 : pptr++;
+		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)
+		if (*pat == Outbrack) {
+		    pptr = saves;
+		    pat = savep;
 		    break;
-		for (*pptr == Meta ? pptr += 2 : pptr++;
-		     *pat != Outbrack; pat++);
+		}
+		for (METAINC(pptr); *pat != Outbrack; pat++);
 		pat++;
 		continue;
 	    }
@@ -2892,7 +3087,7 @@ matchonce(Comp c)
 	if (*pat == Inang) {
 	    /* Numeric globbing. */
 	    unsigned long t1, t2, t3;
-	    char *ptr;
+	    char *ptr, *saves = pptr, *savep = pat;
 
 	    if (!idigit(*pptr))
 		break;
@@ -2933,19 +3128,146 @@ matchonce(Comp c)
 		  pptr--;
 		  t1 /= 10;
 		}
-		if (t1 < t2 || (!not3 && t1 > t3))
+		if (t1 < t2 || (!not3 && t1 > t3)) {
+		    pptr = saves;
+		    pat = savep;
 		    break;
+		}
 	    }
 	    continue;
 	}
-	if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) {
-	    /* just plain old characters */
-	    pptr++;
-	    pat++;
+	/* 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;
 }
 
@@ -2958,6 +3280,7 @@ parsereg(char *str)
     remnulargs(str);
     mode = 1;			/* no path components */
     addflags = 0;
+    errsmax = 0;
     pptr = str;
     tail = NULL;
     return parsecompsw(GF_TOPLEV);
diff --git a/Src/hashtable.c b/Src/hashtable.c
index b816f1892..72e4db21b 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -607,6 +607,9 @@ hashdir(char **dirp)
     Cmdnam cn;
     DIR *dir;
     char *fn;
+#ifdef _WIN32
+    char *exe;
+#endif
 
     if (isrelative(*dirp) || !(dir = opendir(unmeta(*dirp))))
 	return;
@@ -618,6 +621,23 @@ hashdir(char **dirp)
 	    cn->u.name = dirp;
 	    cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn);
 	}
+#ifdef _WIN32
+	/* Hash foo.exe as foo, since when no real foo exists, foo.exe
+	   will get executed by DOS automatically.  This quiets
+	   spurious corrections when CORRECT or CORRECT_ALL is set. */
+	if ((exe = strrchr(fn, '.')) &&
+	    (exe[1] == 'E' || exe[1] == 'e') &&
+	    (exe[2] == 'X' || exe[2] == 'x') &&
+	    (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) {
+	    *exe = 0;
+	    if (!cmdnamtab->getnode(cmdnamtab, fn)) {
+		cn = (Cmdnam) zcalloc(sizeof *cn);
+		cn->flags = 0;
+		cn->u.name = dirp;
+		cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn);
+	    }
+	}
+#endif /* _WIN32 */
     }
     closedir(dir);
 }
diff --git a/Src/module.c b/Src/module.c
index f91650a7f..1117571a4 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -567,6 +567,37 @@ load_module(char const *name)
     return m;
 }
 
+/* This ensures that the module with the name given as the second argument
+ * is loaded.
+ * The third argument should be non-zero if the function should complain
+ * about trying to load a module with a full path name in restricted mode.
+ * The last argument should be non-zero if this function should signal an
+ * error if the module is already loaded.
+ * The return value is the module of NULL if the module couldn't be loaded. */
+
+/**/
+Module
+require_module(char *nam, char *module, int res, int test)
+{
+    Module m = NULL;
+    LinkNode node;
+
+    node = find_module(module);
+    if (node && (m = ((Module) getdata(node)))->handle &&
+	!(m->flags & MOD_UNLOAD)) {
+	if (test) {
+	    zwarnnam(nam, "module %s already loaded.", module, 0);
+	    return NULL;
+	}
+    } else if (res && isset(RESTRICTED) && strchr(module, '/')) {
+	zwarnnam(nam, "%s: restricted", module, 0);
+	return NULL;
+    } else
+	return load_module(module);
+
+    return m;
+}
+
 /**/
 void
 add_dep(char *name, char *from)
@@ -963,22 +994,10 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 	return 0;
     } else {
 	/* load modules */
-	for (; *args; args++) {
-	    Module m;
-
-	    node = find_module(*args);
-	    if (node && (m = ((Module) getdata(node)))->handle &&
-		!(m->flags & MOD_UNLOAD)) {
-		if (!ops['i']) {
-		    zwarnnam(nam, "module %s already loaded.", *args, 0);
-		    ret = 1;
-		}
-	    } else if (isset(RESTRICTED) && strchr(*args, '/')) {
-		zwarnnam(nam, "%s: restricted", *args, 0);
+	for (; *args; args++)
+	    if (!require_module(nam, *args, 1, (!ops['i'])))
 		ret = 1;
-	    } else if (!load_module(*args))
-		ret = 1;
-	}
+
 	return ret;
     }
 }
diff --git a/Src/params.c b/Src/params.c
index 77166209f..e8182815e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -679,11 +679,13 @@ static char **garr;
 static long
 getarg(char **str, int *inv, Value v, int a2, long *w)
 {
-    int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i;
+    int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
     char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
     long r = 0;
     Comp c;
 
+    ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED);
+
     /* first parse any subscription flags */
     if (v->pm && (*s == '(' || *s == Inpar)) {
 	int escapes = 0;
@@ -771,12 +773,13 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	} else if (rev) {
 	    v->isarr |= SCANPM_WANTVALS;
 	}
-	if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED)
+	if (!down && ishash)
 	    v->isarr &= ~SCANPM_MATCHMANY;
 	*inv = ind;
     }
 
-    for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++)
+    for (t=s, i=0;
+	 *t && ((*t != ']' && *t != Outbrack && (ishash || *t != ',')) || i); t++)
 	if (*t == '[' || *t == Inbrack)
 	    i++;
 	else if (*t == ']' || *t == Outbrack)
@@ -791,7 +794,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     singsub(&s);
 
     if (!rev) {
-	if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) {
+	if (ishash) {
 	    HashTable ht = v->pm->gets.hfn(v->pm);
 	    if (!ht) {
 		ht = newparamtable(17, v->pm->nam);
@@ -867,7 +870,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 
 	if ((c = parsereg(s))) {
 	    if (v->isarr) {
-		if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+		if (ishash) {
 		    scancomp = c;
 		    if (ind)
 			v->isarr |= SCANPM_MATCHKEY;
diff --git a/Src/subst.c b/Src/subst.c
index 66c363145..2e9b84718 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -999,11 +999,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 
 	if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 :
 				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
-			     hkeys|hvals)))
+			     hkeys|hvals)) ||
+	    (v->pm && (v->pm->flags & PM_UNSET)))
 	    vunset = 1;
 
 	if (wantt) {
-	    if (v) {
+	    if (v && v->pm && !(v->pm->flags & PM_UNSET)) {
 		int f = v->pm->flags;
 
 		switch (PM_TYPE(f)) {
diff --git a/Src/system.h b/Src/system.h
index 292943dd9..650690b51 100644
--- a/Src/system.h
+++ b/Src/system.h
@@ -70,8 +70,15 @@ char *alloca _((size_t));
 # endif
 #endif
 
-#ifdef HAVE_LIBC_H     /* NeXT */
-# include <libc.h>
+/*
+ * libc.h in an optional package for Debian Linux is broken (it
+ * defines dup() as a synonym for dup2(), which has a different
+ * number of arguments), so just include it for next.
+ */
+#ifdef __NeXT__
+# ifdef HAVE_LIBC_H
+#  include <libc.h>
+# endif
 #endif
 
 #ifdef HAVE_SYS_TYPES_H
diff --git a/Src/zsh.export b/Src/zsh.export
index bfad7aea2..d31e7902e 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -173,6 +173,7 @@ refreshptr
 remlpaths
 remnulargs
 removehashnode
+require_module
 resetneeded
 restoredir
 reswdtab