about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/comp.h12
-rw-r--r--Src/Zle/comp1.c18
-rw-r--r--Src/Zle/comp1.export5
-rw-r--r--Src/Zle/compctl.c30
-rw-r--r--Src/Zle/zle_main.c3
-rw-r--r--Src/Zle/zle_params.c12
-rw-r--r--Src/Zle/zle_refresh.c29
-rw-r--r--Src/Zle/zle_tricky.c441
-rw-r--r--Src/glob.c51
-rw-r--r--Src/subst.c24
-rw-r--r--Src/utils.c5
11 files changed, 494 insertions, 136 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 42a647199..c9d6aa859 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -190,6 +190,7 @@ struct cmgroup {
     LinkList lmatches;		/* list of matches */
     LinkList lfmatches;		/* list of matches without fignore */
     LinkList lallccs;		/* list of used compctls */
+    int num;			/* number of this group */
 };
 
 
@@ -213,6 +214,8 @@ struct cmatch {
     int brsl;			/* ...and the suffix */
     char *rems;			/* when to remove the suffix */
     char *remf;			/* shell function to call for suffix-removal */
+    int rnum;			/* group relative number */
+    int gnum;			/* global number */
 };
 
 #define CMF_FILE     1		/* this is a file */
@@ -298,7 +301,12 @@ struct cpattern {
 #define CP_PATINSERT  (1 << 23)
 #define CP_UNAMBIG    (1 << 24)
 #define CP_UNAMBIGC   (1 << 25)
+#define CP_LISTMAX    (1 << 26)
+#define CP_LASTPROMPT (1 << 27)
+#define CP_TOEND      (1 << 28)
+#define CP_OLDLIST    (1 << 29)
+#define CP_OLDINS     (1 << 30)
 
-#define CP_NUM              26
+#define CP_NUM              31
 
-#define CP_ALLMASK    ((1 << CP_NUM) - 1)
+#define CP_ALLMASK    ((int) ((((unsigned int) 1) << CP_NUM) - 1))
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 904f66305..fe21b1dfc 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -52,7 +52,7 @@ void (*comp_setunsetptr) _((int, int));
 /* pointers to functions required by compctl and defined by zle */
 
 /**/
-int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
+int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char *, char *, char **));
 
 /**/
 char *(*comp_strptr) _((int *, int *, int));
@@ -103,7 +103,8 @@ int incompfunc;
 long compcurrent,
      compnmatches,
      compmatcher,
-     compmatchertot;
+     compmatchertot,
+     complistmax;
 
 /**/
 char **compwords,
@@ -124,7 +125,11 @@ char **compwords,
      *compexact,
      *compexactstr,
      *comppatmatch,
-     *comppatinsert;
+     *comppatinsert,
+     *complastprompt,
+     *comptoend,
+     *compoldlist,
+     *compoldins;
 
 /**/
 Param *comppms;
@@ -439,7 +444,8 @@ setup_comp1(Module m)
 	compcontext = compparameter = compredirect = compquote =
 	compquoting = comprestore = complist = compinsert =
 	compexact = compexactstr = comppatmatch = comppatinsert =
-	compforcelist = NULL;
+	compforcelist = complastprompt = comptoend = 
+	compoldlist = compoldins = NULL;
     makecompparamsptr = NULL;
     comp_setunsetptr = NULL;
     return 0;
@@ -487,6 +493,10 @@ finish_comp1(Module m)
     zsfree(compexactstr);
     zsfree(comppatmatch);
     zsfree(comppatinsert);
+    zsfree(complastprompt);
+    zsfree(comptoend);
+    zsfree(compoldlist);
+    zsfree(compoldins);
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 1be9b6492..4f9fb143d 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -19,11 +19,15 @@ compforcelist
 compinsert
 compiprefix
 compisuffix
+complastprompt
 complist
+complistmax
 compmatcher
 compmatcherstr
 compmatchertot
 compnmatches
+compoldlist
+compoldins
 compparameter
 comppatinsert
 comppatmatch
@@ -36,6 +40,7 @@ comprestore
 comp_setunsetptr
 comp_strptr
 compsuffix
+comptoend
 compwords
 freecmatcher
 freecmlist
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 2ebe2d4a4..28c7cb7b7 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1694,7 +1694,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     char *p, **sp, *e;
     char *ipre = NULL, *isuf = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
     char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
-    char *ign = NULL, *rf = NULL, *expl = NULL;
+    char *ign = NULL, *rf = NULL, *expl = NULL, *apar = NULL, *opar = NULL;
     int f = 0, a = CAF_MATCH, dm;
     Cmatcher match = NULL;
 
@@ -1791,6 +1791,14 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &rf;
 		e = "function name expected after -%c";
 		break;
+	    case 'A':
+		sp = &apar;
+		e = "parameter name expected after -%c";
+		break;
+	    case 'O':
+		sp = &opar;
+		e = "parameter name expected after -%c";
+		break;
 	    case '-':
 		argv++;
 		goto ca_args;
@@ -1799,15 +1807,13 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		return 1;
 	    }
 	    if (sp) {
-		if (*sp) {
-		    zerrnam(name, "doubled option: -%c", NULL, *p);
-		    return 1;
-		}
 		if (p[1]) {
-		    *sp = p + 1;
+		    if (!*sp)
+			*sp = p + 1;
 		    p = "" - 1;
 		} else if (argv[1]) {
-		    *sp = *++argv;
+		    if (!*sp)
+			*sp = *++argv;
 		    p = "" - 1;
 		} else {
 		    zerrnam(name, e, NULL, *p);
@@ -1826,7 +1832,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 
     match = cpcmatcher(match);
     a = addmatchesptr(ipre, isuf, ppre, psuf, prpre, pre, suf, group,
-		      rs, rf, ign, f, a, match, expl, argv);
+		      rs, rf, ign, f, a, match, expl, apar, opar, argv);
     freecmatcher(match);
 
     return a;
@@ -2152,7 +2158,13 @@ static struct compparam {
     { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
     { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
     { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
-    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_unambig_curs) },
+    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
+      VAL(get_unambig_curs) },
+    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
+    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
+    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
+    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
     { NULL, 0, NULL, NULL, NULL }
 };
 
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 479994249..7a5d0d7db 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -883,7 +883,7 @@ trashzle(void)
 	moveto(nlnct, 0);
 	if (clearflag && tccan(TCCLEAREOD)) {
 	    tcout(TCCLEAREOD);
-	    clearflag = 0;
+	    clearflag = listshown = 0;
 	}
 	if (postedit)
 	    fprintf(shout, "%s", postedit);
@@ -927,6 +927,7 @@ setup_zle(Module m)
     /* miscellaneous initialisations */
     stackhist = stackcs = -1;
     kungetbuf = (char *) zalloc(kungetsz = 32);
+    hasperm = 0;
 
     /* initialise the keymap system */
     init_keymaps();
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index adde116f2..d9fefe659 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -61,6 +61,8 @@ static struct zleparam {
 	zleunsetfn, NULL },
     { "RBUFFER", PM_SCALAR,  FN(set_rbuffer), FN(get_rbuffer),
 	zleunsetfn, NULL },
+    { "PREBUFFER",  PM_SCALAR | PM_READONLY,  NULL,  FN(get_prebuffer),
+	zleunsetfn, NULL },
     { "WIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_widget),
         zleunsetfn, NULL },
     { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget),
@@ -212,6 +214,16 @@ get_rbuffer(Param pm)
 
 /**/
 static char *
+get_prebuffer(Param pm)
+{
+    if (chline)
+	return dupstrpfx(chline, hptr - chline);
+    else
+	return dupstring("");
+}
+
+/**/
+static char *
 get_widget(Param pm)
 {
     return bindk->nam;
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index b9f39b70b..1f1063b8d 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -53,6 +53,11 @@ int nlnct;
 /**/
 int showinglist;
 
+/* Non-zero if a completion list was displayed. */
+
+/**/
+int listshown;
+
 /* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the *
  * screen below the buffer display should not be cleared by       *
  * zrefresh(), but should be by trashzle().                       */
@@ -253,13 +258,23 @@ zrefresh(void)
     if (inlist)
 	return;
 
-    if (clearlist) {
-	invalidatelist();
-	moveto(0, 0);
-	clearflag = 0;
-	resetneeded = 1;
-	clearlist = 0;
+    if (clearlist && listshown) {
+	if (tccan(TCCLEAREOD)) {
+	    int ovln = vln, ovcs = vcs;
+
+	    moveto(nlnct, 0);
+	    tcout(TCCLEAREOD);
+	    moveto(ovln, ovcs);
+	} else {
+	    invalidatelist();
+	    moveto(0, 0);
+	    clearflag = 0;
+	    resetneeded = 1;
+	}
+	listshown = 0;
     }
+    clearlist = 0;
+
 #ifdef HAVE_SELECT
     cost = 0;			/* reset */
 #endif
@@ -287,6 +302,7 @@ zrefresh(void)
 	    moveto(0, 0);
 	    t0 = olnct;		/* this is to clear extra lines even when */
 	    winchanged = 0;	/* the terminal cannot TCCLEAREOD	  */
+	    listshown = 0;
 	}
 #endif
 	resetvideo();
@@ -303,6 +319,7 @@ zrefresh(void)
                 tcout(TCCLEAREOD);
             else
                 cleareol = 1;   /* request: clear to end of line */
+	    listshown = 0;
 	}
         if (t0 > -1)
             olnct = t0;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 4ae7495c3..1f13d55b4 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -72,12 +72,27 @@ static int wb, we;
 
 static int offs;
 
-/* These control the type of completion that will be done.  They are    *
- * affected by the choice of ZLE command and by relevant shell options. *
- * usemenu is set to 2 if we have to start automenu.                    */
+/* These control the type of completion that will be done.  They are      *
+ * affected by the choice of ZLE command and by relevant shell options.   *
+ * usemenu is set to 2 if we have to start automenu and 3 if we have to   *
+ * insert a match as if for menucompletion but without really stating it. */
 
 static int usemenu, useglob, useexact, useline, uselist;
 
+/* Non-zero if we should keep an old list. */
+
+static int oldlist, oldins;
+
+/* The match and group number to insert when starting menucompletion.   */
+
+static int insmnum, insgnum, insgroup;
+
+/* This is used to decide when the cursor should be moved to the end of    *
+ * the inserted word: 0 - never, 1 - only when a single match is inserted, *
+ * 2 - when a full match is inserted (single or menu), 3 - always.         */
+
+static int movetoend;
+
 /* != 0 if we are in the middle of a menu completion */
 
 static int menucmp;
@@ -99,12 +114,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc;
 
 /* This is for completion inside a brace expansion. brbeg and brend hold  *
  * strings that were temporarily removed from the string to complete.     *
- * brpl and brsl, brbsl hold the offset of these strings.                 *
+ * brpl and brsl, hold the offset of these strings.                 *
  * brpcs and brscs hold the positions of the re-inserted string in the    *
  * line.                                                                  */
 
 static char *brbeg = NULL, *brend = NULL;
-static int brpl, brsl, brbsl, brpcs, brscs;
+static int brpl, brsl, brpcs, brscs, qbrpl, qbrsl, hasunqu;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
@@ -114,7 +129,16 @@ static LinkList matches, fmatches;
 /* This holds the list of matches-groups. lmatches is a pointer to the  *
  * last element in this list. */
 
-static Cmgroup amatches, lmatches;
+static Cmgroup pmatches, amatches, lmatches;
+
+/* Non-zero if we have permanently allocated matches. */
+
+/**/
+int hasperm;
+
+/* Number of permanently allocated matches and groups. */
+
+static int permmnum, permgnum;
 
 /* The total number of matches and the number of matches to be listed. */
 
@@ -155,6 +179,7 @@ static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
 static char *fpre, *fsuf;
 static char *ipre, *ripre;
 static char *isuf;
+static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
 static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 static int noreal;
 
@@ -485,14 +510,9 @@ reversemenucomplete(void)
  * with the next completions. This gives you a way to   *
  * accept several selections from the list of matches.  */
 
-/**/
-void
-acceptandmenucomplete(void)
+static void
+acceptlast(void)
 {
-    if (!menucmp) {
-	feep();
-	return;
-    }
     if (brbeg && *brbeg) {
 	int l;
 
@@ -514,6 +534,17 @@ acceptandmenucomplete(void)
 	menupos = cs;
 	menuwe = 1;
     }
+}
+
+/**/
+void
+acceptandmenucomplete(void)
+{
+    if (!menucmp) {
+	feep();
+	return;
+    }
+    acceptlast();
     menucomplete();
 }
 
@@ -650,7 +681,13 @@ check_param(char *s, int set)
 	int n = 0, br = 1;
 
 	if (*b == Inbrace) {
-	    /* If this is a ${...}, ignore the possible (...) flags. */
+	    char *tb = b;
+
+	    /* If this is a ${...}, see if we are before the '}'. */
+	    if (!skipparens(Inbrace, Outbrace, &tb))
+		return NULL;
+
+	    /* Ignore the possible (...) flags. */
 	    b++, br++;
 	    n = skipparens(Inpar, Outpar, &b);
 	}
@@ -1494,11 +1531,13 @@ get_comp_string(void)
 
 	    if (tt && tt < s + myoffs) {
 		/* Braces are go:  delete opening brace */
-		char *com = NULL;
+		char *com = NULL, *tmp;
 		int pl, sl;
 
 		brbeg = dupstring(tt);
 		brpl = tt - s;
+		tmp = dupstrpfx(s, tt - s);
+		qbrpl = strlen(quotename(tmp, NULL));
 		pl = 1;
 		sl = 0;
 		chuck(tt);
@@ -1531,8 +1570,8 @@ get_comp_string(void)
 		    if (*p == Outbrace)
 			chuck(p);
 		    brsl = strlen(s) - (p - s);
-		    brbsl = p - s;
 		    brend[sl] = '\0';
+		    qbrsl = strlen(quotename(p, NULL));
 		}
 		/* we are still waiting for an outbrace and maybe commas */
 		if (brbeg)
@@ -2040,7 +2079,7 @@ static int
 match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 {
     int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
-    int il = 0, iw = 0, t, ind, add, bc;
+    int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0);
     VARARR(unsigned char, ea, ll + 1);
     char *ow;
     Cmlist ms;
@@ -2054,9 +2093,9 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 
     if (sfx) {
 	l += ll; w += lw;
-	ind = -1; add = -1; bc = brsl;
+	ind = -1; add = -1;
     } else {
-	ind = 0; add = 1; bc = brpl;
+	ind = 0; add = 1;
     }
     /* ow will always point to the beginning (or end) of that sub-string
      * in w that wasn't put in the match-variables yet. */
@@ -2406,17 +2445,19 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp,
 	wl = strlen(w);
 	*clp = bld_parts(w, wl, wl, NULL);
 	*exact = 0;
+	*bpl = (qu ? qbrpl : brpl);
+	*bsl = (qu ? qbrsl : brsl);
     } else {
 	Cline pli, plil;
 	int mpl, rpl, wl;
 
-	if (qu)
-	    w = quotename(w, NULL);
+	w = (qu ? quotename(w, NULL) : dupstring(w));
 
 	wl = strlen(w);
 
 	/* Always try to match the prefix. */
 
+	*bpl = (qu ? qbrpl : brpl);
 	if ((mpl = match_str(pfx, w, bpl, &rpl, 0, 0)) < 0)
 	    return NULL;
 
@@ -2441,6 +2482,7 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp,
 	    plil = matchlastpart;
 
 	    /* The try to match the suffix. */
+	    *bsl = (qu ? qbrsl : brsl);
 	    if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0)) < 0) {
 		free_cline(pli);
 
@@ -2479,6 +2521,7 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp,
 	    pli = matchparts;
 	}
 	r = dupstring(matchbuf);
+
 	*clp = pli;
 
 	/* Test if the string built is equal to the one from the line. */
@@ -2489,6 +2532,9 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp,
 	} else
 	    *exact = !strcmp(pfx, w);
     }
+    if (!qu)
+	hasunqu = 1;
+
     return r;
 }
 
@@ -3398,6 +3444,22 @@ add_match_data(int alt, char *str, Cline line,
     return cm;
 }
 
+/* This stores the strings from the list in an array. */
+
+static void
+set_param(char *name, LinkList l)
+{
+    char **a, **p;
+    LinkNode n;
+
+    a = (char **) zalloc((countlinknodes(l) + 1) * sizeof(char *));
+    for (p = a, n = firstnode(l); n; incnode(n))
+	*p++ = ztrdup((char *) getdata(n));
+    *p = NULL;
+
+    setaparam(name, a);
+}
+
 /* This is used by compadd to add a couple of matches. The arguments are
  * the strings given via options. The last argument is the array with
  * the matches. */
@@ -3407,22 +3469,29 @@ int
 addmatches(char *ipre, char *isuf,
 	   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 *exp, char **argv)
+	   int flags, int aflags, Cmatcher match, char *exp, 
+	   char *apar, char *opar, char **argv)
 {
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
     char **aign = NULL;
     int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
-    int oisalt = 0, isalt, isexact;
+    int oisalt = 0, isalt, isexact, doadd;
     Cline lc = NULL;
     Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
     Comp cp = NULL;
+    LinkList aparl = NULL, oparl = NULL;
 
     /* Switch back to the heap that was used when the completion widget
      * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
+	    doadd = (!apar && !opar);
+	    if (apar)
+		aparl = newlinklist();
+	    if (opar)
+		oparl = newlinklist();
 	    if (exp) {
 		expl = (Cexpl) zhalloc(sizeof(struct cexpl));
 		expl->count = expl->fcount = 0;
@@ -3548,9 +3617,17 @@ addmatches(char *ipre, char *isuf,
 		    rems = NULL;
 		} else if (rems)
 		    rems = dupstring(rems);
+
+		/* Probably quote the prefix and suffix for testing. */
+		if (!cp && (aflags & CAF_MATCH) && !(aflags & CAF_QUOTE)) {
+		    lpre = quotename(lpre, NULL);
+		    lsuf = quotename(lsuf, NULL);
+		    llpl = strlen(lpre);
+		    llsl = strlen(lsuf);
+		}
 	    }
 	    /* Walk through the matches given. */
-	    for (; (s = dupstring(*argv)); argv++) {
+	    for (; (s = *argv); argv++) {
 		sl = strlen(s);
 		bpl = brpl;
 		bsl = brsl;
@@ -3565,25 +3642,40 @@ addmatches(char *ipre, char *isuf,
 			if ((filell = strlen(*pt)) < sl
 			    && !strcmp(*pt, s + sl - filell))
 			    isalt = 1;
+
+		    if (isalt && !doadd)
+			continue;
 		}
 		if (!(aflags & CAF_MATCH)) {
-		    ms = s;
-		    lc = bld_parts(s, sl, -1, NULL);
+		    ms = dupstring(s);
+		    lc = bld_parts(ms, sl, -1, NULL);
 		    isexact = 0;
 		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
-					     (aflags & CAF_QUOTE),
+					     !(aflags & CAF_QUOTE),
 					     &bpl, &bsl, &isexact)))
 		    continue;
 
-		cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre, prpre,
-				    ppre, psuf, suf, bpl, bsl,
-				    flags, isexact);
-		cm->rems = rems;
-		cm->remf = remf;
+		if (doadd) {
+		    cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre, 
+					prpre, ppre, psuf, suf, bpl, bsl,
+					flags, isexact);
+		    cm->rems = rems;
+		    cm->remf = remf;
+		} else {
+		    if (apar)
+			addlinknode(aparl, ms);
+		    if (opar)
+			addlinknode(oparl, s);
+		    free_cline(lc);
+		}
 	    }
 	    compnmatches = mnum;
 	    if (exp)
 		addexpl();
+	    if (apar)
+		set_param(apar, aparl);
+	    if (opar)
+		set_param(opar, oparl);
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
@@ -3629,9 +3721,6 @@ addmatch(char *s, char *t)
     hn = (HashNode) t;
     pm = (Param) t;
 
-    if (incompfunc)
-	s = dupstring(s);
-
     if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
 	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
 	if ((addwhat == CC_FILES ||
@@ -3645,10 +3734,13 @@ addmatch(char *s, char *t)
 		    !strcmp(*pt, s + sl - filell))
 		    isalt = 1;
 	}
-	if (!(ms = comp_match(fpre, fsuf, s, filecomp, &lc,
-			      (addwhat == CC_FILES || addwhat == -6 ||
-			       addwhat == -5 || addwhat == -8),
-			      &bpl, &bsl, &isexact)))
+	ms = ((addwhat == CC_FILES || addwhat == -6 ||
+	       addwhat == -5 || addwhat == -8) ? 
+	      comp_match(qfpre, qfsuf, s, filecomp, &lc, 1,
+			 &bpl, &bsl, &isexact) :
+	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
+			 &bpl, &bsl, &isexact));
+	if (!ms)
 	    return;
 
 	if (addwhat == -7 && !findcmd(s, 0))
@@ -3678,10 +3770,19 @@ addmatch(char *s, char *t)
 		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
 		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	if (!(ms = comp_match(rpre, rsuf, s, patcomp, &lc,
+	char *p1, *s1, *p2, *s2;
+
+	if (addwhat == CC_QUOTEFLAG) {
+	    p1 = qrpre; s1 = qrsuf;
+	    p2 = rpre;  s2 = rsuf;
+	} else {
+	    p1 = qlpre; s1 = qlsuf;
+	    p2 = lpre;  s2 = lsuf;
+	}
+	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
 			      (addwhat == CC_QUOTEFLAG),
 			      &bpl, &bsl, &isexact)) &&
-	    !(ms = comp_match(lpre, lsuf, s, NULL, &lc,
+	    !(ms = comp_match(p2, s2, s, NULL, &lc,
 			      (addwhat == CC_QUOTEFLAG),
 			      &bpl, &bsl, &isexact)))
 	    return;
@@ -4002,6 +4103,7 @@ docompletion(char *s, int lst, int incmd)
 	ainfo = fainfo = NULL;
 	matchers = newlinklist();
 
+	hasunqu = 0;
 	useline = (lst != COMP_LIST_COMPLETE);
 	useexact = (isset(RECEXACT) && usemenu != 1);
 	uselist = (useline ?
@@ -4014,6 +4116,12 @@ docompletion(char *s, int lst, int incmd)
 	zsfree(compforcelist);
 	compforcelist = ztrdup("");
 	haspattern = 0;
+	complistmax = getiparam("LISTMAX");
+	zsfree(complastprompt);
+	complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
+				(unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
+				"yes" : "");
+	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
 
 	/* Make sure we have the completion list and compctl. */
 	if (makecomplist(s, incmd, lst)) {
@@ -4031,7 +4139,7 @@ docompletion(char *s, int lst, int incmd)
 	    /* We have matches. */
 	    if (nmatches > 1)
 		/* There is more than one match. */
-		do_ambiguous();
+		    do_ambiguous();
 
 	    else if (nmatches == 1) {
 		/* Only one match. */
@@ -4039,6 +4147,7 @@ docompletion(char *s, int lst, int incmd)
 
 		while (!m->mcount)
 		    m = m->next;
+		menucur = NULL;
 		do_single(m->matches[0]);
 		invalidatelist();
 	    }
@@ -4069,9 +4178,8 @@ docompletion(char *s, int lst, int incmd)
 		g = g->next;
 	    }
 	    if (!tr) {
-		clearflag = ((isset(USEZLE) && !termflags &&
-			      (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
-			     (unset(ALWAYSLASTPROMPT) && zmult != 1));
+		clearflag = (isset(USEZLE) && !termflags &&
+			      complastprompt && *complastprompt);
 
 		if (clearflag && up + nlnct < lines)
 		    tcmultout(TCUP, TCMULTUP, up + nlnct);
@@ -4099,7 +4207,8 @@ callcompfunc(char *s, char *fn)
 {
     List list;
     int lv = lastval;
-    
+    char buf[20];
+
     if ((list = getshfunc(fn)) != &dummy_list) {
 	char **p, *tmp;
 	int set, aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
@@ -4108,7 +4217,10 @@ callcompfunc(char *s, char *fn)
 	comppms = (Param *) zalloc(CP_NUM * sizeof(Param));
 
 	set = -1 & ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
-		     CP_EXACTSTR | CP_FORCELIST | (useglob ? 0 : CP_PATMATCH));
+		     CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
+		     (useglob ? 0 : CP_PATMATCH));
+	if (!*complastprompt)
+	    set &= ~CP_LASTPROMPT;
 	zsfree(compcontext);
 	zsfree(compparameter);
 	zsfree(compredirect);
@@ -4205,20 +4317,17 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    if (linwhat == IN_MATH)
-		tmp = s;
-	    else
-		tmp = quotename(s, NULL);
+	    /* Maybe we'll have to do quoting here some time. */
+	    tmp = dupstring(s);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
 	} else {
-	    char *ss = s + offs, sav;
+	    char *ss, sav;
 	    
-	    if (linwhat == IN_MATH)
-		tmp = s;
-	    else
-		tmp = quotename(s, &ss);
+	    tmp = dupstring(s);
+	    ss = tmp + offs;
+
 	    sav = *ss;
 	    *ss = '\0';
 	    untokenize(tmp);
@@ -4260,6 +4369,30 @@ callcompfunc(char *s, char *fn)
 	    compexact = ztrdup("");
 	    set &= ~CP_EXACT;
 	}
+	zsfree(comptoend);
+	if (movetoend == 1)
+	    comptoend = ztrdup("single");
+	else
+	    comptoend = ztrdup("match");
+	zsfree(compoldlist);
+	zsfree(compoldins);
+	if (hasperm && permmnum) {
+	    if (listshown)
+		compoldlist = "shown";
+	    else
+		compoldlist = "yes";
+	    set |= CP_OLDLIST;
+	    if (menucur) {
+		sprintf(buf, "%d", (*menucur)->gnum);
+		compoldins = buf;
+		set |= CP_OLDINS;
+	    } else
+		compoldins = "";
+	} else
+	    compoldlist = compoldins = "";
+	compoldlist = ztrdup(compoldlist);
+	compoldins = ztrdup(compoldins);
+
 	incompfunc = 1;
 	startparamscope();
 	makecompparamsptr();
@@ -4294,10 +4427,32 @@ callcompfunc(char *s, char *fn)
 	else if (!strcmp(compinsert, "auto") ||
 		 !strcmp(compinsert, "automenu"))
 	    useline = 1, usemenu = 2;
-	else
+	else if (idigit(*compinsert)) {
+	    char *m;
+
+	    useline = 1; usemenu = 3;
+	    insmnum = atoi(compinsert);
+	    if ((m = strchr(compinsert, ':'))) {
+		insgroup = 1;
+		insgnum = atoi(m + 1);
+	    }
+	} else
 	    useline = usemenu = 0;
 	useexact = (compexact && !strcmp(compexact, "accept"));
 
+	if (!comptoend || !*comptoend)
+	    movetoend = 0;
+	else if (!strcmp(comptoend, "single"))
+	    movetoend = 1;
+	else if (!strcmp(comptoend, "always"))
+	    movetoend = 3;
+	else
+	    movetoend = 2;
+
+	oldlist = (hasperm && compoldlist && !strcmp(compoldlist, "keep"));
+	oldins = (hasperm && menucur &&
+		  compoldins && !strcmp(compoldins, "keep"));
+
 	zfree(comppms, CP_NUM * sizeof(Param));
 	comppms = ocpms;
     }
@@ -4327,6 +4482,7 @@ makecomplist(char *s, int incmd, int lst)
     struct cmlist ms;
     Cmlist m;
     char *p, *os = s;
+    int onm = nmatches;
 
     /* Inside $... ? */
     if (compfunc && (p = check_param(s, 0)))
@@ -4382,10 +4538,12 @@ makecomplist(char *s, int incmd, int lst)
 
 	if (!validlist)
 	    lastambig = 0;
-	amatches = 0;
+	amatches = NULL;
 	mnum = 0;
 	unambig_mnum = -1;
 	isuf = NULL;
+	insmnum = insgnum = 1;
+	insgroup = oldlist = oldins = 0;
 	begcmgroup("default", 0);
 
 	ccused = newlinklist();
@@ -4399,7 +4557,7 @@ makecomplist(char *s, int incmd, int lst)
 
 	endcmgroup(NULL);
 
-	if (amatches)
+	if (amatches && !oldlist)
 	    amatches->ccs = (Compctl *) makearray(ccused, 0,
 						  &(amatches->ccount), NULL);
 	else {
@@ -4408,7 +4566,13 @@ makecomplist(char *s, int incmd, int lst)
 	    for (n = firstnode(ccused); n; incnode(n))
 		freecompctl((Compctl) getdata(n));
 	}
+	if (oldlist) {
+	    nmatches = onm;
+	    validlist = 1;
+	    amatches = pmatches;
 
+	    return 0;
+	}
 	PERMALLOC {
 	    permmatches();
 	} LASTALLOC;
@@ -4996,9 +5160,9 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     delit = ispattern = 0;
     usemenu = um;
     patcomp = filecomp = NULL;
-    menucur = NULL;
-    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = prpre =
-	fpre = fsuf = ipre = ripre = prpre = NULL;
+    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
+	fpre = fsuf = ipre = ripre = prpre = 
+	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
 
     curcc = cc;
 
@@ -5106,8 +5270,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     lpre = zhalloc(lpl + 1);
     memcpy(lpre, s, lpl);
     lpre[lpl] = '\0';
+    qlpre = quotename(lpre, NULL);
     lsuf = dupstring(s + offs);
     lsl = strlen(lsuf);
+    qlsuf = quotename(lsuf, NULL);
 
     /* First check for ~.../... */
     if (ic == Tilde) {
@@ -5126,9 +5292,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
 	(noreal = 0, getreal(tt)) :
 	dupstring(tt);
+    qrpre = quotename(rpre, NULL);
 
     for (p = lsuf; *p && *p != String && *p != Tick; p++);
     rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
+    qrsuf = quotename(rsuf, NULL);
 
     /* Check if word is a pattern. */
 
@@ -5204,7 +5372,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    lppre = dupstring((char *) (line + wb));
 	    line[cs] = save;
 	    if (brbeg && *brbeg)
-		strcpy(lppre + brpl, lppre + brpl + strlen(brbeg));
+		strcpy(lppre + qbrpl, lppre + qbrpl + strlen(brbeg));
 	    if ((p = strrchr(lppre, '/'))) {
 		p[1] = '\0';
 		lppl = strlen(lppre);
@@ -5226,7 +5394,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    lpsuf = dupstring((char *) (line + cs));
 	    line[we] = save;
 	    if (brend && *brend) {
-		char *p = lpsuf + brsl - (cs - wb);
+		char *p = lpsuf + qbrsl - (cs - wb);
 
 		strcpy(p, p + strlen(brend));
 	    }
@@ -5241,8 +5409,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* And get the file prefix. */
 	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
 			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+	qfpre = quotename(fpre, NULL);
 	/* And the suffix. */
 	fsuf = dupstrpfx(rsuf, s2 - rsuf);
+	qfsuf = quotename(fsuf, NULL);
 
 	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
 	    int t2;
@@ -5506,6 +5676,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	 * that things starting with these characters will be added.   */
 	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
 	rpl++;
+	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
     }
     if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
 	/* If we have to complete commands, add alias names, *
@@ -6137,7 +6308,10 @@ permmatches(void)
     Cmatch *p, *q;
     Cexpl *ep, *eq, e, o;
     Compctl *cp, *cq;
-    int nn, nl, fi = 0;
+    int nn, nl, fi = 0, gn = 1, mn = 1, rn;
+
+    if (hasperm)
+	freematches();
 
     amatches = lmatches = NULL;
     nmatches = smatches = 0;
@@ -6179,13 +6353,17 @@ permmatches(void)
 	n->next = amatches;
 	amatches = n;
 	n->prev = 0;
+	n->num = gn++;
 
 	n->flags = g->flags;
 	n->mcount = g->mcount;
 	n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) *
 					    sizeof(Cmatch));
-	for (q = g->matches; *q; q++, p++)
+	for (rn = 1, q = g->matches; *q; q++, p++, rn) {
 	    *p = dupmatch(*q);
+	    (*p)->rnum = rn++;
+	    (*p)->gnum = mn++;
+	}
 	*p = NULL;
 
 	n->lcount = g->lcount;
@@ -6216,6 +6394,10 @@ permmatches(void)
 	    n->ccs = NULL;
 	g = g->next;
     }
+    pmatches = amatches;
+    hasperm = 1;
+    permmnum = mn - 1;
+    permgnum = gn - 1;
 }
 
 /* This frees one match. */
@@ -6247,7 +6429,7 @@ freematch(Cmatch m)
 static void
 freematches(void)
 {
-    Cmgroup g = amatches, n;
+    Cmgroup g = pmatches, n;
     Cmatch *m;
     Cexpl *e;
     Compctl *c;
@@ -6281,24 +6463,27 @@ freematches(void)
 
 	g = n;
     }
+    hasperm = 0;
 }
 
 /* Insert the given string into the command line.  If move is non-zero, *
  * the cursor position is changed and len is the length of the string   *
- * to insert (if it is -1, the length is calculated here).              */
+ * to insert (if it is -1, the length is calculated here).              *
+ * The last argument says if we should quote the string.                */
 
 /**/
-static void
+static int
 inststrlen(char *str, int move, int len)
 {
     if (!len || !str)
-	return;
+	return 0;
     if (len == -1)
 	len = strlen(str);
     spaceinline(len);
     strncpy((char *)(line + cs), str, len);
     if (move)
 	cs += len;
+    return len;
 }
 
 /* This builds the unambiguous string. If ins is non-zero, it is
@@ -6319,10 +6504,11 @@ cline_str(Cline l, int ins, int *csp)
      * to re-insert. */
     if (ins) {
 	if ((hasp = (brbeg && *brbeg))) {
-	    plen = strlen(brbeg); pl = brpl;
+	    plen = strlen(brbeg); pl = (hasunqu ? brpl : qbrpl);
 	}
 	if ((hass = (brend && *brend))) {
-	    slen = strlen(brend); sl = we - wb - brsl - plen - slen + 1;
+	    slen = strlen(brend);
+	    sl = we - wb - (hasunqu ? brsl : qbrsl) - plen - slen + 1;
 	}
 	if (!pl) {
 	    inststrlen(brbeg, 1, -1);
@@ -6606,6 +6792,7 @@ do_ambiguous(void)
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
      * completion into an unambiguous one.                              */
     if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
+	menucur = NULL;
 	do_single(ainfo->exactm);
 	invalidatelist();
 	return;
@@ -6628,6 +6815,8 @@ do_ambiguous(void)
 	int atend = (cs == we), oll = ll, la;
 	VARARR(char, oline, ll);
 
+	menucur = NULL;
+
 	/* Copy the line buffer to be able to easily test if it changed. */
 	memcpy(oline, line, ll);
 
@@ -6653,6 +6842,10 @@ do_ambiguous(void)
 	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
 		    ((atend && cs != lastend) ? FC_INWORD : 0));
 
+	/* Probably move the cursor to then end. */
+	if (movetoend == 3)
+	    cs = lastend;
+
 	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
 	 * if the completion is completely ambiguous') is set, and some    *
 	 * prefix was inserted, return now, bypassing the list-displaying  *
@@ -6674,7 +6867,8 @@ do_ambiguous(void)
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
 	feep();
-    if (uselist && usemenu != 2 && !showinglist &&
+    if (uselist && usemenu != 2 &&
+	(!showinglist || (usemenu == 3 && !oldlist)) &&
 	(smatches >= 2 || (compforcelist && *compforcelist)))
 	showinglist = -2;
 }
@@ -6721,7 +6915,7 @@ do_single(Cmatch m)
 	/* We are currently not in a menu-completion, *
 	 * so set the position variables.             */
 	menupos = wb;
-	menuwe = (cs == we) || isset(ALWAYSTOEND);
+	menuwe = (movetoend >= 2 || (movetoend == 1 && !menucmp));
 	menuend = we;
     }
     /* If we are already in a menu-completion or if we have done a *
@@ -6770,14 +6964,20 @@ do_single(Cmatch m)
 	     * If it is, we append a slash.                                */
 	    struct stat buf;
 	    char *p;
+	    int t = 0;
 
-	    /* Build the path name. */
-	    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
+	    if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
+		t = 1;
+	    else {
+		/* Build the path name. */
+		p = (char *) zhalloc(strlen(prpre) + strlen(str) +
 				 strlen(psuf) + 3);
-	    sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf);
+		sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf);
 
-	    /* And do the stat. */
-	    if (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode)) {
+		/* And do the stat. */
+		t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
+	    }
+	    if (t) {
 		/* It is a directory, so add the slash. */
 		havesuff = 1;
 		inststrlen("/", 1, 1);
@@ -6822,23 +7022,71 @@ do_single(Cmatch m)
     if (menuwe && m->ripre && isset(AUTOPARAMKEYS))
 	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc);
 
-    if (menucmp && !menuwe)
+    if ((menucmp && !menuwe) || !movetoend)
 	cs = menuend;
 }
 
+/* This maps the value in v into the range [0,m-1], decrementing v
+ * if it is non-negative and making negative values cound backwards. */
+
+static int
+comp_mod(int v, int m)
+{
+    if (v >= 0)
+	v--;
+    if (v >= 0)
+	return v % m;
+    else {
+	while (v < 0)
+	    v += m;
+	return v;
+    }
+}
+
 /* This handles the beginning of menu-completion. */
 
 /**/
 static void
 do_ambig_menu(void)
 {
-    menucmp = 1;
-    menucur = NULL;
-    menugrp = amatches;
-    while (!menugrp->mcount)
-	menugrp = menugrp->next;
-    do_single(menugrp->matches[0]);
-    menucur = menugrp->matches;
+    Cmatch *mc;
+
+    if (usemenu != 3) {
+	menucmp = 1;
+	menucur = NULL;
+    } else {
+	if (oldlist) {
+	    if (oldins)
+		acceptlast();
+	} else
+	    menucur = NULL;
+    }
+    if (insgroup) {
+	insgnum = comp_mod(insgnum, permgnum);
+	for (menugrp = amatches;
+	     menugrp && menugrp->num != insgnum + 1;
+	     menugrp = menugrp->next);
+	if (!menugrp || !menugrp->mcount) {
+	    menucur = NULL;
+	    return;
+	}
+	insmnum = comp_mod(insmnum, menugrp->mcount);
+    } else {
+	int c = 0;
+
+	insmnum = comp_mod(insmnum, permmnum);
+	for (menugrp = amatches;
+	     menugrp && (c += menugrp->mcount) <= insmnum;
+	     menugrp = menugrp->next)
+	    insmnum -= menugrp->mcount;
+	if (!menugrp) {
+	    menucur = NULL;
+	    return;
+	}
+    }
+    mc = menugrp->matches + insmnum;
+    do_single(*mc);
+    menucur = mc;
 }
 
 /* Return the length of the common prefix of s and t. */
@@ -6924,6 +7172,15 @@ printfmt(char *fmt, int n, int dopr)
 		    u = 0; m = 1;
 		    tcout(TCUNDERLINEEND);
 		    break;
+		case '{':
+		    for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++)
+			if (dopr)
+			    putc(*p, shout);
+		    if (*p)
+			p++;
+		    else
+			p--;
+		    break;
 		}
 		if (m) {
 		    if (b)
@@ -6971,7 +7228,6 @@ listmatches(void)
     Cexpl *e;
     int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
     int of = isset(LISTTYPES), opl = 0;
-    int listmax = getiparam("LISTMAX");
 
 #ifdef DEBUG
     /* Sanity check */
@@ -6983,11 +7239,10 @@ listmatches(void)
 
     /* Set the cursor below the prompt. */
     trashzle();
-    showinglist = 0;
+    showinglist = listshown = 0;
 
     clearflag = (isset(USEZLE) && !termflags &&
-		 (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
-	(unset(ALWAYSLASTPROMPT) && zmult != 1);
+		 complastprompt && *complastprompt);
 
     for (g = amatches; g; g = g->next) {
 	char **pp = g->ylist;
@@ -7066,7 +7321,8 @@ listmatches(void)
     }
 
     /* Maybe we have to ask if the user wants to see the list. */
-    if ((listmax && nlist > listmax) || (!listmax && nlines >= lines)) {
+    if ((complistmax && nlist > complistmax) ||
+	(!complistmax && nlines >= lines)) {
 	int qup;
 	zsetterm();
 	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
@@ -7204,6 +7460,7 @@ listmatches(void)
 	if ((nlines += nlnct - 1) < lines) {
 	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
+	    listshown = 1;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
diff --git a/Src/glob.c b/Src/glob.c
index f8fd2aa4a..0d6d4d778 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -397,6 +397,7 @@ scanner(Complist q)
     int pbcwdsav = pathbufcwd;
     int errssofar = errsfound;
     struct dirsav ds;
+    char *str;
 
     ds.ino = ds.dev = 0;
     ds.dirname = NULL;
@@ -412,17 +413,18 @@ scanner(Complist q)
 	    scanner(q->next);
     }
     c = q->comp;
+    str = c->str;
     /* Now the actual matching for the current path section. */
-    if (!(c->next || c->left) && !haswilds(c->str)
+    if (!(c->next || c->left) && !haswilds(str)
 	&& (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax)
-	    || !strcmp(".", c->str) || !strcmp("..", c->str))) {
+	    || !*str || !strcmp(".", str) || !strcmp("..", str))) {
 	/*
 	 * 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(c->str);
+	int l = strlen(str);
 
 	if (l + !l + pathpos - pathbufcwd >= PATH_MAX) {
 	    int err;
@@ -442,14 +444,14 @@ scanner(Complist q)
 	    /* Not the last path section. Just add it to the path. */
 	    int oppos = pathpos;
 
-	    if (!errflag && !(q->closure && !strcmp(c->str, "."))) {
-		addpath(c->str);
+	    if (!errflag && !(q->closure && !strcmp(str, "."))) {
+		addpath(str);
 		if (!closure || statfullpath("", NULL, 1))
 		    scanner((q->closure) ? q : q->next);
 		pathbuf[pathpos = oppos] = '\0';
 	    }
 	} else
-	    insert(c->str, 0);
+	    insert(str, 0);
     } else {
 	/* Do pattern matching on current path section. */
 	char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : ".";
@@ -2282,16 +2284,45 @@ int
 getmatch(char **sp, char *pat, int fl, int n, char *replstr)
 {
     Comp c;
-    char *s = *sp, *t, *start, sav;
-    int i, l = strlen(*sp), matched;
 
     MUSTUSEHEAP("getmatch");	/* presumably covered by prefork() test */
-    repllist = NULL;
+    c = parsereg(pat);
+    if (!c) {
+ 	zerr("bad pattern: %s", pat, 0);
+ 	return 1;
+    }
+    return igetmatch(sp, c, fl, n, replstr);
+}
+
+/**/
+void
+getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr)
+{
+    char **arr = *ap, **pp;
+    Comp c;
+
+    MUSTUSEHEAP("getmatch");	/* presumably covered by prefork() test */
+
     c = parsereg(pat);
     if (!c) {
 	zerr("bad pattern: %s", pat, 0);
-	return 1;
+	return;
     }
+    *ap = pp = ncalloc(sizeof(char *) * (arrlen(arr) + 1));
+    while ((*pp = *arr++))
+	if (igetmatch(pp, c, fl, n, replstr))
+	    pp++;
+}
+
+/**/
+static int
+igetmatch(char **sp, Comp c, int fl, int n, char *replstr)
+{
+    char *s = *sp, *t, *start, sav;
+    int i, l = strlen(*sp), matched;
+
+    repllist = NULL;
+
     if (fl & SUB_ALL) {
 	i = domatch(s, c, 0);
 	*sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0);
diff --git a/Src/subst.c b/Src/subst.c
index c62fcce6b..e8e22f943 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1361,16 +1361,24 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			 NULL, s[-1]);
 		    return NULL;
 		}
-	    singsub(&s);
+	    {
+		char t = s[-1];
+
+		singsub(&s);
+		if (t == '/' && (flags & SUB_SUBSTR)) {
+		    if (*s == '#' || *s == '%') {
+			flags &= ~SUB_SUBSTR;
+			if (*s == '%')
+			    flags |= SUB_END;
+			s++;
+		    } else if (*s == '\\') {
+			s++;
+		    }
+		}
+	    }
 
 	    if (!vunset && isarr) {
-		char **ap = aval;
-		char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1));
-
-		while ((*pp = *ap++)) {
-		    if (getmatch(pp, s, flags, flnum, replstr))
-			pp++;
-		}
+		getmatcharr(&aval, s, flags, flnum, replstr);
 		copied = 1;
 	    } else {
 		if (vunset)
diff --git a/Src/utils.c b/Src/utils.c
index 12b7fc7eb..86679e90f 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3177,10 +3177,7 @@ bslashquote(const char *s, char **e, int instring)
 	*v++ = *u;
     }
     *v = '\0';
-    if (strcmp(buf, s))
-	tt = dupstring(buf);
-    else
-	tt = s;
+    tt = dupstring(buf);
     v += tt - buf;
     if (e && (sf & 1))
 	*e += tt - buf;