about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
authorSven Wischnowsky <wischnow@users.sourceforge.net>2001-06-13 14:05:30 +0000
committerSven Wischnowsky <wischnow@users.sourceforge.net>2001-06-13 14:05:30 +0000
commitd6629f38e2f71b0d5a265af32843a64998af9715 (patch)
treea89bcbd8e11dfe2897cbc027cc68598c46e57a6f /Src/Zle
parent4f17e281bade8df8c35aa5ff3307f37e476c4bf0 (diff)
downloadzsh-d6629f38e2f71b0d5a265af32843a64998af9715.tar.gz
zsh-d6629f38e2f71b0d5a265af32843a64998af9715.tar.xz
zsh-d6629f38e2f71b0d5a265af32843a64998af9715.zip
*** empty log message ***
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/complist.c1336
-rw-r--r--Src/Zle/zle_tricky.c234
2 files changed, 1403 insertions, 167 deletions
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index a7cab9807..f76e54116 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -37,7 +37,7 @@
 
 
 static Widget w_menuselect;
-static Keymap mskeymap;
+static Keymap mskeymap, lskeymap;
 
 /* Indixes into the terminal string arrays. */
 
@@ -76,7 +76,7 @@ static char *colnames[] = {
 /* Default values. */
 
 static char *defcols[] = {
-    "0", "0", "1;34", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL,
+    "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL,
     "\033[", "m", NULL, "0", "0", "7", "0", "0"
 };
 
@@ -122,13 +122,17 @@ struct listcols {
     Extcol exts;		/* strings for extensions */
 };
 
+/* Combined length of LC and RC, maximum length of capability strings. */
+
+static int lr_caplen, max_caplen;
+
 /* This parses the value of a definition (the part after the `=').
  * The return value is a pointer to the character after it. */
 
 static char *
 getcolval(char *s, int multi)
 {
-    char *p;
+    char *p, *o = s;
 
     for (p = s; *s && *s != ':' && (!multi || *s != '='); p++, s++) {
 	if (*s == '\\' && s[1]) {
@@ -172,6 +176,8 @@ getcolval(char *s, int multi)
     }
     if (p != s)
 	*p = '\0';
+    if ((s - o) > max_caplen)
+	max_caplen = s - o;
     return s;
 }
 
@@ -325,10 +331,6 @@ filecol(char *col)
     return fc;
 }
 
-/* Combined length of LC and RC, maximum length of capability strings. */
-
-static int lr_caplen, max_caplen;
-
 /* This initializes the given terminal color structure. */
 
 static void
@@ -337,6 +339,8 @@ getcols(Listcols c)
     char *s;
     int i, l;
 
+    max_caplen = lr_caplen = 0;
+    queue_signals();
     if (!(s = getsparam("ZLS_COLORS")) &&
 	!(s = getsparam("ZLS_COLOURS"))) {
 	for (i = 0; i < NUM_COLS; i++)
@@ -348,21 +352,25 @@ getcols(Listcols c)
 	    c->files[COL_MA] = filecol(s);
 	    c->files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]);
 	} else
-	    c->files[COL_MA] = filecol("");
+	    c->files[COL_MA] = filecol(defcols[COL_MA]);
 	lr_caplen = 0;
 	if ((max_caplen = strlen(c->files[COL_MA]->col)) <
 	    (l = strlen(c->files[COL_EC]->col)))
 	    max_caplen = l;
+	unqueue_signals();
 	return;
     }
     /* We have one of the parameters, use it. */
     memset(c, 0, sizeof(*c));
     s = dupstring(s);
     while (*s)
-	s = getcoldef(c, s);
+	if (*s == ':')
+	    s++;
+	else
+	    s = getcoldef(c, s);
+    unqueue_signals();
 
     /* Use default values for those that aren't set explicitly. */
-    max_caplen = lr_caplen = 0;
     for (i = 0; i < NUM_COLS; i++) {
 	if (!c->files[i] || !c->files[i]->col)
 	    c->files[i] = filecol(defcols[i]);
@@ -382,11 +390,19 @@ getcols(Listcols c)
 /* Information about the list shown. */
 
 static int noselect, mselect, inselect, mcol, mline, mcols, mlines, mmlen;
-static int selected;
+static int selected, mlbeg = -1, mlend = 9999999, mscroll, mrestlines;
+static int mnew, mlastcols, mlastlines, mhasstat, mfirstl, mlastm;
+static int mlprinted;
+static char *mstatus, *mlistp;
 static Cmatch **mtab, **mmtabp;
 static Cmgroup *mgtab, *mgtabp;
 static struct listcols mcolors;
 
+/* Used in mtab/mgtab, for explanations. */
+
+#define mtexpl ((Cmatch *) 1)
+#define mgexpl ((Cmgroup) 1)
+
 /* Information for in-string colours. */
 
 static int nrefs;
@@ -481,7 +497,7 @@ doiscol(Listcols c, int pos)
 	    /* insert e in sendpos */
 	    for (i = curissend; sendpos[i] <= e; ++i)
 		;
-	    for (j = i + 1; j < MAX_POS; ++j)
+	    for (j = MAX_POS - 1; j > i; --j)
 		sendpos[j] = sendpos[j-1];
 	    sendpos[i] = e;
 	    
@@ -496,10 +512,10 @@ doiscol(Listcols c, int pos)
 
 /* Stripped-down version of printfmt(). But can do in-string colouring. */
 
-static void
-clprintfmt(Listcols c, char *p)
+static int
+clprintfmt(Listcols c, char *p, int ml)
 {
-    int cc = 0, i = 0;
+    int cc = 0, i = 0, ask, beg;
 
     initiscol(c);
 
@@ -507,34 +523,32 @@ clprintfmt(Listcols c, char *p)
 	doiscol(c, i++);
 	cc++;
 	if (*p == '\n') {
-	    if (tccan(TCCLEAREOL))
+	    if (mlbeg >= 0 && tccan(TCCLEAREOL))
 		tcout(TCCLEAREOL);
-	    else {
-		int s = columns - 1 - (cc % columns);
-
-		while (s-- > 0)
-		    putc(' ', shout);
-	    }
 	    cc = 0;
 	}
+	if (ml == mlend - 1 && (cc % columns) == columns - 1)
+	    return 0;
+
 	putc(*p, shout);
+	if ((beg = !(cc % columns)))
+	    ml++;
+	if (mscroll && !(cc % columns) &&
+	    !--mrestlines && (ask = asklistscroll(ml)))
+	    return ask;
     }
-    if (tccan(TCCLEAREOL))
+    if (mlbeg >= 0 && tccan(TCCLEAREOL))
 	tcout(TCCLEAREOL);
-    else {
-	int s = columns - 1 - (cc % columns);
-
-	while (s-- > 0)
-	    putc(' ', shout);
-    }
+    return 0;
 }
 
 /* Local version of nicezputs() with in-string colouring. */
 
-static void
-clnicezputs(Listcols c, char *s)
+static int
+clnicezputs(Listcols c, char *s, int ml)
 {
-    int cc, i = 0;
+    int cc, i = 0, col = 0, ask, oml = ml;
+    char *t;
 
     initiscol(c);
 
@@ -548,8 +562,26 @@ clnicezputs(Listcols c, char *s)
 	}
 	if (cc == Meta)
 	    cc = *s++ ^ 32;
-	fputs(nicechar(cc), shout);
+
+	for (t = nicechar(cc); *t; t++) {
+	    if (ml == mlend - 1 && col == columns - 1) {
+		mlprinted = ml - oml;
+		return 0;
+	    }
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
+		    mlprinted = ml - oml;
+		    return ask;
+		}
+		col = 0;
+                fputs(" \010", shout);
+	    }
+	}
     }
+    mlprinted = ml - oml;
+    return 0;
 }
 
 /* Get the terminal color string for the given match. */
@@ -636,12 +668,706 @@ putfilecol(Listcols c, char *group, char *n, mode_t m)
 
 static Cmgroup last_group;
 
-static void
+/**/
+static int
+asklistscroll(int ml)
+{
+    Thingy cmd;
+    int i, ret = 0;
+
+    compprintfmt(NULL, 1, 1, 1, ml, NULL);
+
+    fflush(shout);
+    zsetterm();
+    selectlocalmap(lskeymap);
+    if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
+	ret = 1;
+    else if (cmd == Th(z_acceptline) ||
+	     cmd == Th(z_downhistory) ||
+	     cmd == Th(z_downlineorhistory) ||
+	     cmd == Th(z_downlineorsearch) ||
+	     cmd == Th(z_vidownlineorhistory))
+	mrestlines = 1;
+    else if (cmd == Th(z_completeword) ||
+		   cmd == Th(z_expandorcomplete) ||
+		   cmd == Th(z_expandorcompleteprefix) ||
+		   cmd == Th(z_menucomplete) ||
+		   cmd == Th(z_menuexpandorcomplete) ||
+		   !strcmp(cmd->nam, "menu-select") ||
+		   !strcmp(cmd->nam, "complete-word") ||
+		   !strcmp(cmd->nam, "expand-or-complete") ||
+		   !strcmp(cmd->nam, "expand-or-complete-prefix") ||
+		   !strcmp(cmd->nam, "menu-complete") ||
+	     !strcmp(cmd->nam, "menu-expand-or-complete"))
+	mrestlines = lines - 1;
+    else {
+	ungetkeycmd();
+	ret = 1;
+    }
+    selectlocalmap(NULL);
+    settyinfo(&shttyinfo);
+    putc('\r', shout);
+    for (i = columns - 1; i--; )
+	putc(' ', shout);
+    putc('\r', shout);
+
+    return ret;
+}
+
+#define dolist(X)   ((X) >= mlbeg && (X) < mlend)
+#define dolistcl(X) ((X) >= mlbeg && (X) < mlend + 1)
+#define dolistnl(X) ((X) >= mlbeg && (X) < mlend - 1)
+
+/**/
+static int
+compprintnl(int ml)
+{
+    int ask;
+
+    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+    putc('\n', shout);
+
+    if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
+	return ask;
+
+    return 0;
+}
+
+/* This is used to print the strings (e.g. explanations). *
+ * It returns the number of lines printed.       */
+
+/**/
+static int
+compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
+{
+    char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12];
+    int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat;
+
+    if ((stat = !fmt)) {
+	if (mlbeg >= 0) {
+	    if (!(fmt = mstatus)) {
+		mlprinted = 0;
+		return 0;
+	    }
+	    cc = -1;
+	} else
+	    fmt = mlistp;
+    }
+    for (p = fmt; *p; p++) {
+	if (doesc && *p == '%') {
+	    if (*++p) {
+		m = 0;
+		switch (*p) {
+		case '%':
+		    if (dopr == 1)
+			putc('%', shout);
+		    cc++;
+		    break;
+		case 'n':
+		    if (!stat) {
+			sprintf(nc, "%d", n);
+			if (dopr == 1)
+			    fputs(nc, shout);
+			cc += strlen(nc);
+		    }
+		    break;
+		case 'B':
+		    b = 1;
+		    if (dopr)
+			tcout(TCBOLDFACEBEG);
+		    break;
+		case 'b':
+		    b = 0; m = 1;
+		    if (dopr)
+			tcout(TCALLATTRSOFF);
+		    break;
+		case 'S':
+		    s = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTBEG);
+		    break;
+		case 's':
+		    s = 0; m = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTEND);
+		    break;
+		case 'U':
+		    u = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEBEG);
+		    break;
+		case 'u':
+		    u = 0; m = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEEND);
+		    break;
+		case '{':
+		    for (p++; *p && (*p != '%' || p[1] != '}'); p++)
+			if (dopr)
+			    putc(*p, shout);
+		    if (*p)
+			p++;
+		    else
+			p--;
+		    break;
+		case 'm':
+		    if (stat) {
+			sprintf(nc, "%d/%d", (n ? mlastm : mselect),
+				listdat.nlist);
+			m = 2;
+		    }
+		    break;
+		case 'M':
+		    if (stat) {
+			sprintf(nbuf, "%d/%d", (n ? mlastm : mselect),
+				listdat.nlist);
+			sprintf(nc, "%-9s", nbuf);
+			m = 2;
+		    }
+		    break;
+		case 'l':
+		    if (stat) {
+			sprintf(nc, "%d/%d", ml + 1, listdat.nlines);
+			m = 2;
+		    }
+		    break;
+		case 'L':
+		    if (stat) {
+			sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines);
+			sprintf(nc, "%-9s", nbuf);
+			m = 2;
+		    }
+		    break;
+		case 'p':
+		    if (stat) {
+			if (ml == listdat.nlines - 1)
+			    strcpy(nc, "Bottom");
+			else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
+			    sprintf(nc, "%d%%",
+				    ((ml + 1) * 100) / listdat.nlines);
+			else
+			    strcpy(nc, "Top");
+			m = 2;
+		    }
+		    break;
+		case 'P':
+		    if (stat) {
+			if (ml == listdat.nlines - 1)
+			    strcpy(nc, "Bottom");
+			else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
+			    sprintf(nc, "%2d%%   ",
+				    ((ml + 1) * 100) / listdat.nlines);
+			else
+			    strcpy(nc, "Top   ");
+			m = 2;
+		    }
+		    break;
+		}
+		if (m == 2 && dopr == 1) {
+		    int l = strlen(nc);
+
+		    if (l + cc > columns - 2)
+			nc[l -= l + cc - (columns - 2)] = '\0';
+		    fputs(nc, shout);
+		    cc += l;
+		} else if (dopr && m == 1) {
+		    if (b)
+			tcout(TCBOLDFACEBEG);
+		    if (s)
+			tcout(TCSTANDOUTBEG);
+		    if (u)
+			tcout(TCUNDERLINEBEG);
+		}
+	    } else
+		break;
+	} else {
+	    if ((++cc == columns - 2 || *p == '\n') && stat)
+		dopr = 2;
+	    if (*p == '\n') {
+		if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL))
+		    tcout(TCCLEAREOL);
+		l += 1 + ((cc - 1) / columns);
+		cc = 0;
+	    }
+	    if (dopr == 1) {
+		if (ml == mlend - 1 && (cc % columns) == columns - 1) {
+		    dopr = 0;
+		    continue;
+		}
+		putc(*p, shout);
+		if ((beg = !(cc % columns)) && !stat) {
+		    ml++;
+                    fputs(" \010", shout);
+                }
+		if (mscroll && beg && !--mrestlines && (ask = asklistscroll(ml))) {
+		    *stop = 1;
+		    if (stat && n)
+			mfirstl = -1;
+		    return (mlprinted = l + (cc / columns));
+		}
+	    }
+	}
+    }
+    if (dopr) {
+        if (!(cc % columns))
+            fputs(" \010", shout);
+        if (mlbeg >= 0 && tccan(TCCLEAREOL))
+            tcout(TCCLEAREOL);
+    }
+    if (stat && n)
+	mfirstl = -1;
+
+    return (mlprinted = l + (cc / columns));
+}
+
+/* This is like zputs(), but allows scrolling. */
+
+/**/
+static int
+compzputs(char const *s, int ml)
+{
+    int c, col = 0, ask;
+
+    while (*s) {
+	if (*s == Meta)
+	    c = *++s ^ 32;
+	else if(itok(*s)) {
+	    s++;
+	    continue;
+	} else
+	    c = *s;
+	s++;
+	putc(c, shout);
+	if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL))
+	    tcout(TCCLEAREOL);
+	if (mscroll && (++col == columns || c == '\n')) {
+	    ml++;
+	    if (!--mrestlines && (ask = asklistscroll(ml)))
+		return ask;
+
+	    col = 0;
+	}
+    }
+    return 0;
+}
+
+/* This is like nicezputs(), but allows scrolling. */
+
+/**/
+static int
+compnicezputs(char *s, int ml)
+{
+    int c, col = 0, ask, oml = ml;
+    char *t;
+
+    while ((c = *s++)) {
+	if (itok(c)) {
+	    if (c <= Comma)
+		c = ztokens[c - Pound];
+	    else 
+		continue;
+	}
+	if (c == Meta)
+	    c = *s++ ^ 32;
+
+	for (t = nicechar(c); *t; t++) {
+	    if (ml == mlend - 1 && col == columns - 1) {
+		mlprinted = ml - oml;
+		return 0;
+	    }
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
+		    mlprinted = ml - oml;
+		    return ask;
+		}
+		col = 0;
+	    }
+	}
+    }
+    mlprinted = ml - oml;
+    return 0;
+}
+
+/**/
+static int
+compprintlist(int showall)
+{
+    static int lasttype = 0, lastbeg = 0, lastml = 0, lastinvcount = -1;
+    static int lastn = 0, lastnl = 0, lastnlnct = -1;
+    static Cmgroup lastg = NULL;
+    static Cmatch *lastp = NULL;
+    static Cexpl *lastexpl = NULL;
+
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int pnl = 0, cl, mc = 0, ml = 0, printed = 0, stop = 0, asked = 1;
+    int lastused = 0;
+
+    mfirstl = -1;
+    if (mnew || lastinvcount != invcount || lastbeg != mlbeg || mlbeg < 0) {
+	lasttype = 0;
+	lastg = NULL;
+	lastexpl = NULL;
+	lastml = 0;
+	lastnlnct = -1;
+    }
+    cl = (listdat.nlines > lines - nlnct - mhasstat ?
+	  lines - nlnct - mhasstat : listdat.nlines) - (lastnlnct > nlnct);
+    lastnlnct = nlnct;
+    mrestlines = lines - 1;
+    lastinvcount = invcount;
+
+    if (cl < 2) {
+	cl = -1;
+	if (tccan(TCCLEAREOD))
+	    tcout(TCCLEAREOD);
+    } else if (mlbeg >= 0 && !tccan(TCCLEAREOL) && tccan(TCCLEAREOD))
+	tcout(TCCLEAREOD);
+
+    g = ((lasttype && lastg) ? lastg : amatches);
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    int l;
+
+	    if (!lastused && lasttype == 1) {
+		e = lastexpl;
+		ml = lastml;
+		lastused = 1;
+	    }
+	    while (*e) {
+		if ((*e)->count &&
+		    (!listdat.onlyexpl ||
+		     (listdat.onlyexpl & ((*e)->count > 0 ? 1 : 2)))) {
+		    if (pnl) {
+			if (dolistnl(ml) && compprintnl(ml))
+			    goto end;
+			pnl = 0;
+			ml++;
+			if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    if (mlbeg < 0 && mfirstl < 0)
+			mfirstl = ml;
+		    l = compprintfmt((*e)->str, (*e)->count, dolist(ml), 1,
+				     ml, &stop);
+		    if (mselect >= 0) {
+			int mm = (mcols * ml), i;
+
+			for (i = mcols; i--; ) {
+			    mtab[mm + i] = mtexpl;
+			    mgtab[mm + i] = mgexpl;
+			}
+		    }
+		    if (stop)
+			goto end;
+		    if (!lasttype && ml >= mlbeg) {
+			lasttype = 1;
+			lastg = g;
+			lastbeg = mlbeg;
+			lastml = ml;
+			lastexpl = e;
+			lastp = NULL;
+			lastused = 1;
+		    }
+		    ml += mlprinted;
+		    if (dolistcl(ml) && cl >= 0 && (cl -= mlprinted) <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    pnl = 1;
+		}
+		e++;
+		if (!mnew && ml > mlend)
+		    goto end;
+	    }
+	}
+	if (!listdat.onlyexpl && mlbeg < 0 && pp && *pp) {
+	    if (pnl) {
+		if (dolistnl(ml) && compprintnl(ml))
+		    goto end;
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    if (mlbeg < 0 && mfirstl < 0)
+		mfirstl = ml;
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    if (compzputs(*pp, ml))
+			goto end;
+		    if (*++pp && compprintnl(ml))
+			goto end;
+		}
+	    } else {
+		int n = g->lcount, nl, nc, i, a;
+		char **pq;
+
+		nl = nc = g->lins;
+
+		while (n && nl--) {
+		    i = g->cols;
+		    mc = 0;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			if (compzputs(*pq, mscroll))
+			    goto end;
+			if (i) {
+			    a = (g->widths ? g->widths[mc] : g->width) -
+				strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += ((g->flags & CGF_ROWS) ? 1 : nc);
+			mc++;
+			n--;
+		    }
+		    if (n) {
+			if (compprintnl(ml))
+			    goto end;
+			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
+		}
+	    }
+	} else if (!listdat.onlyexpl &&
+		   (g->lcount || (showall && g->mcount))) {
+	    int n = g->dcount, nl, nc, i, j, wid;
+	    Cmatch *q;
+
+	    nl = nc = g->lins;
+
+	    if ((g->flags & CGF_HASDL) &&
+		(lastused || !lasttype || lasttype == 2)) {
+		if (!lastused && lasttype == 2) {
+		    p = lastp;
+		    ml = lastml;
+		    n = lastn;
+		    nl = lastnl;
+		    lastused = 1;
+		    pnl = 0;
+		} else
+		    p = g->matches;
+
+		for (; (m = *p); p++) {
+		    if (m->disp && (m->flags & CMF_DISPLINE)) {
+			if (pnl) {
+			    if (dolistnl(ml) && compprintnl(ml))
+				goto end;
+			    pnl = 0;
+			    ml++;
+			    if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+				cl = -1;
+				if (tccan(TCCLEAREOD))
+				    tcout(TCCLEAREOD);
+			    }
+			}
+			if (!lasttype && ml >= mlbeg) {
+			    lasttype = 2;
+			    lastg = g;
+			    lastbeg = mlbeg;
+			    lastml = ml;
+			    lastp = p;
+			    lastn = n;
+			    lastnl = nl;
+			    lastused = 1;
+			}
+			if (mfirstl < 0)
+			    mfirstl = ml;
+			if (dolist(ml))
+			    printed++;
+			if (clprintm(g, p, 0, ml, 1, 0, NULL, NULL))
+			    goto end;
+			ml += mlprinted;
+			if (dolistcl(ml) && (cl -= mlprinted) <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+			pnl = 1;
+		    }
+		    if (!mnew && ml > mlend)
+			goto end;
+		}
+	    }
+	    if (n && pnl) {
+		if (dolistnl(ml) && compprintnl(ml))
+		    goto end;
+		pnl = 0;
+		ml++;
+		if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    if (!lastused && lasttype == 3) {
+		p = lastp;
+		n = lastn;
+		nl = lastnl;
+		ml = lastml;
+		lastused = 1;
+	    } else
+		p = skipnolist(g->matches, showall);
+
+	    while (n && nl--) {
+		if (!lasttype && ml >= mlbeg) {
+		    lasttype = 3;
+		    lastg = g;
+		    lastbeg = mlbeg;
+		    lastml = ml;
+		    lastp = p;
+		    lastn = n;
+		    lastnl = nl + 1;
+		    lastused = 1;
+		}
+		i = g->cols;
+		mc = 0;
+		q = p;
+		while (n && i--) {
+		    wid = (g->widths ? g->widths[mc] : g->width);
+		    if (!(m = *q)) {
+			if (clprintm(g, NULL, mc, ml, (!i), wid, NULL, NULL))
+			    goto end;
+			break;
+		    }
+		    if (!m->disp && (m->flags & CMF_FILE) &&
+			m->str[0] && m->str[strlen(m->str) - 1] != '/') {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
+					     3 + strlen(m->str));
+			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
+				m->str);
+
+			if (ztat(pb, &buf, 1) ?
+			    clprintm(g, q, mc, ml, (!i), wid, NULL, NULL) :
+			    clprintm(g, q, mc, ml, (!i), wid, pb, &buf))
+			    goto end;
+		    } else if (clprintm(g, q, mc, ml, (!i), wid, NULL, NULL))
+			goto end;
+
+		    if (dolist(ml))
+			printed++;
+		    ml += mlprinted;
+		    if (dolistcl(ml) && (cl -= mlprinted) < 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    if (mfirstl < 0)
+			mfirstl = ml;
+
+		    if (--n)
+			for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
+			     j && *q; j--)
+			    q = skipnolist(q + 1, showall);
+		    mc++;
+		}
+		while (i-- > 0) {
+		    if (clprintm(g, NULL, mc, ml, (!i),
+				 (g->widths ? g->widths[mc] : g->width),
+				 NULL, NULL))
+			goto end;
+		    mc++;
+		}
+		if (n) {
+		    if (dolistnl(ml) && compprintnl(ml))
+			goto end;
+		    ml++;
+		    if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    if (nl)
+			for (j = ((g->flags & CGF_ROWS) ? g->cols : 1);
+			     j && *p; j--)
+			    p = skipnolist(p + 1, showall);
+		}
+		if (!mnew && ml > mlend)
+		    goto end;
+	    }
+	}
+	if (g->lcount || (showall && g->mcount))
+	    pnl = 1;
+	g = g->next;
+    }
+    asked = 0;
+ end:
+    lastlistlen = 0;
+    if (nlnct <= 1)
+	mscroll = 0;
+    if (clearflag) {
+	int nl;
+
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if (mlbeg >= 0) {
+	    if ((nl = listdat.nlines + nlnct) >= lines) {
+		if (mhasstat) {
+		    putc('\n', shout);
+		    compprintfmt(NULL, 0, 1, 1, mline, NULL);
+		}
+		nl = lines - 1;
+	    } else
+		nl--;
+	    tcmultout(TCUP, TCMULTUP, nl);
+	    showinglist = -1;
+
+	    lastlistlen = listdat.nlines;
+	} else if ((nl = listdat.nlines + nlnct - 1) < lines) {
+	    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+		tcout(TCCLEAREOL);
+	    tcmultout(TCUP, TCMULTUP, nl);
+	    showinglist = -1;
+
+	    lastlistlen = listdat.nlines;
+	} else {
+	    clearflag = 0;
+	    if (!asked) {
+		mrestlines = (ml + nlnct > lines);
+		compprintnl(ml);
+	    }
+	}
+    } else if (!asked) {
+	mrestlines = (ml + nlnct > lines);
+	compprintnl(ml);
+    }
+    listshown = (clearflag ? 1 : -1);
+    mnew = 0;
+
+    return printed;
+}
+
+/**/
+static int
 clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	 char *path, struct stat *buf)
 {
     Cmatch m;
-    int len, subcols = 0;
+    int len, subcols = 0, stop = 0, ret = 0;
 
     if (g != last_group)
         *last_cap = '\0';
@@ -649,14 +1375,22 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
     last_group = g;
 
     if (!mp) {
-	zcputs(&mcolors, g->name, COL_SP);
-	len = width - 2;
-	while (len-- > 0)
-	    putc(' ', shout);
-	zcoff();
-	return;
+	if (dolist(ml)) {
+	    zcputs(&mcolors, g->name, COL_SP);
+	    len = width - 2;
+	    while (len-- > 0)
+		putc(' ', shout);
+	    zcoff();
+	}
+	mlprinted = 0;
+	return 0;
     }
     m = *mp;
+
+    if ((m->flags & CMF_ALL) && (!m->disp || !m->disp[0]))
+	bld_all_str(m);
+
+    mlastm = m->gnum;
     if (m->disp && (m->flags & CMF_DISPLINE)) {
 	if (mselect >= 0) {
 	    int mm = (mcols * ml), i;
@@ -666,6 +1400,10 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 		mgtab[mm + i] = g;
 	    }
 	}
+	if (!dolist(ml)) {
+	    mlprinted = printfmt(m->disp, 0, 0, 0) / columns;
+	    return 0;
+	}
 	if (m->gnum == mselect) {
 	    int mm = (mcols * ml);
 	    mline = ml;
@@ -681,9 +1419,12 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	else
 	    subcols = putmatchcol(&mcolors, g->name, m->disp);
 	if (subcols)
-	    clprintfmt(&mcolors, m->disp);
-	else
-	    printfmt(m->disp, 0, 1, 0);
+	    ret = clprintfmt(&mcolors, m->disp, ml);
+	else {
+	    compprintfmt(m->disp, 0, 1, 0, ml, &stop);
+	    if (stop)
+		ret = 1;
+	}
 	zcoff();
     } else {
 	int mx;
@@ -704,6 +1445,10 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 		mgtab[mx + mm + i] = g;
 	    }
 	}
+	if (!dolist(ml)) {
+	    mlprinted = niceztrlen(m->disp ? m->disp : m->str) / columns;
+	    return 0;
+	}
 	if (m->gnum == mselect) {
 	    int mm = mcols * ml;
 
@@ -723,12 +1468,17 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	    subcols = putmatchcol(&mcolors, g->name, (m->disp ? m->disp : m->str));
 
 	if (subcols)
-	    clnicezputs(&mcolors, (m->disp ? m->disp : m->str));
+	    ret = clnicezputs(&mcolors, (m->disp ? m->disp : m->str), ml);
 	else
-	    nicezputs((m->disp ? m->disp : m->str), shout);
+	    ret = compnicezputs((m->disp ? m->disp : m->str), ml);
+	if (ret) {
+	    zcoff();
+	    return 1;
+	}
 	len = niceztrlen(m->disp ? m->disp : m->str);
+	mlprinted = len / columns;
 
-	 if (isset(LISTTYPES) && buf) {
+	if (isset(LISTTYPES) && buf) {
 	    if (m->gnum != mselect) {
 		zcoff();
 		zcputs(&mcolors, g->name, COL_TC);
@@ -751,6 +1501,7 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	    zcoff();
 	}
     }
+    return ret;
 }
 
 static int
@@ -760,32 +1511,66 @@ complistmatches(Hookdef dummy, Chdata dat)
 
     amatches = dat->matches;
 
-    if (minfo.asked == 2) {
+    noselect = 0;
+
+    if ((minfo.asked == 2 && mselect < 0) || nlnct >= lines) {
 	showinglist = 0;
 	amatches = oamatches;
 	return (noselect = 1);
     }
     getcols(&mcolors);
 
-    calclist(mselect >= 0);
+    mnew = ((calclist(mselect >= 0) || mlastcols != columns ||
+	     mlastlines != listdat.nlines) && mselect >= 0);
 
     if (!listdat.nlines || (mselect >= 0 &&
-			    (!(isset(USEZLE) && !termflags &&
-			       complastprompt && *complastprompt) ||
-			     (listdat.nlines + nlnct - 1) >= lines))) {
+			    !(isset(USEZLE) && !termflags &&
+			      complastprompt && *complastprompt))) {
 	showinglist = listshown = 0;
 	noselect = 1;
 	amatches = oamatches;
 	return 1;
     }
-    if (inselect)
+    if (inselect || mlbeg >= 0)
 	clearflag = 0;
 
-    if (asklist()) {
-	amatches = oamatches;
-	return (noselect = 1);
+    mscroll = 0;
+    mlistp = NULL;
+
+    queue_signals();
+    if (mselect >= 0 || mlbeg >= 0 ||
+	(mlistp = dupstring(getsparam("LISTPROMPT")))) {
+	unqueue_signals();
+	if (mlistp && !*mlistp)
+	    mlistp = "%SAt %p: Hit TAB for more, or the character to insert%s";
+	trashzle();
+	showinglist = listshown = 0;
+
+	lastlistlen = 0;
+
+	if (mlistp) {
+	    clearflag = (isset(USEZLE) && !termflags && dolastprompt);
+	    mscroll = 1;
+	} else {
+	    clearflag = 1;
+	    minfo.asked = (listdat.nlines + nlnct <= lines);
+	}
+    } else {
+	unqueue_signals();
+	mlistp = NULL;
+	if (asklist()) {
+	    amatches = oamatches;
+	    return (noselect = 1);
+	}
     }
-    if (mselect >= 0) {
+    if (mlbeg >= 0) {
+	mlend = mlbeg + lines - nlnct - mhasstat;
+	while (mline >= mlend)
+	    mlbeg++, mlend++;
+    } else
+	mlend = 9999999;
+
+    if (mnew) {
 	int i;
 
 	i = columns * listdat.nlines;
@@ -795,14 +1580,13 @@ complistmatches(Hookdef dummy, Chdata dat)
 	free(mgtab);
 	mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
 	memset(mgtab, 0, i * sizeof(Cmgroup));
-	mcols = columns;
-	mlines = listdat.nlines;
+	mlastcols = mcols = columns;
+	mlastlines = mlines = listdat.nlines;
     }
     last_cap = (char *) zhalloc(max_caplen + 1);
     *last_cap = '\0';
 
-    if (!printlist(1, clprintm, (mselect >= 0)) || listdat.nlines >= lines ||
-	!clearflag)
+    if (!compprintlist(mselect >= 0) || !clearflag)
 	noselect = 1;
 
     amatches = oamatches;
@@ -818,8 +1602,8 @@ adjust_mcol(int wish, Cmatch ***tabp, Cmgroup **grp)
 
     tab -= mcol;
 
-    for (p = wish; p >= 0 && !tab[p]; p--);
-    for (n = wish; n < mcols && !tab[n]; n++);
+    for (p = wish; p >= 0 && (!tab[p] || tab[p] == mtexpl); p--);
+    for (n = wish; n < mcols && (!tab[n] || tab[n] == mtexpl); n++);
     if (n == mcols)
 	n = -1;
 
@@ -849,9 +1633,11 @@ struct menustack {
     Brinfo brbeg;
     Brinfo brend;
     int nbrbeg, nbrend;
-    int cs, acc, nmatches;
+    int cs, acc, nmatches, mline, mlbeg;
     struct menuinfo info;
     Cmgroup amatches, pmatches, lastmatches, lastlmatches;
+    char *origline;
+    int origcs, origll;
 };
 
 static int
@@ -863,27 +1649,108 @@ domenuselect(Hookdef dummy, Chdata dat)
     Thingy cmd;
     Menustack u = NULL;
     int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0;
+    int space, lbeg = 0, step = 1, wrap, pl = nlnct, broken = 0, first = 1;
+    int nolist = 0;
     char *s;
 
-    if (fdat || (dummy && (!(s = getsparam("SELECTMIN")) ||
+    queue_signals();
+    if (fdat || (dummy && (!(s = getsparam("MENUSELECT")) ||
 			   (dat && dat->num < atoi(s))))) {
 	if (fdat) {
 	    fdat->matches = dat->matches;
 	    fdat->num = dat->num;
+	    fdat->nmesg = dat->nmesg;
 	}
+	unqueue_signals();
 	return 0;
     }
+    if ((s = getsparam("MENUSCROLL"))) {
+	if (!(step = mathevali(s)))
+	    step = (lines - nlnct) >> 1;
+	else if (step < 0)
+	    if ((step += lines - nlnct) < 0)
+		step = 1;
+    }
+    if ((mstatus = dupstring(getsparam("MENUPROMPT"))) && !*mstatus)
+	mstatus = "%SScrolling active: current selection at %p%s";
+    unqueue_signals();
+    mhasstat = (mstatus && *mstatus);
     fdat = dat;
     selectlocalmap(mskeymap);
-    noselect = 0;
+    noselect = 1;
+    while ((menuacc &&
+	    !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
+	   (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
+	    (!(*minfo.cur)->str || !*(*minfo.cur)->str)))
+	do_menucmp(0);
+
     mselect = (*(minfo.cur))->gnum;
+    mline = 0;
+    mlines = 999999;
+    mlbeg = 0;
     for (;;) {
+	if (mline < 0) {
+	    int x, y;
+	    Cmatch **p = mtab;
+
+	    for (y = 0; y < mlines; y++) {
+		for (x = mcols; x; x--, p++)
+		    if (*p && *p != mtexpl && **p && mselect == (**p)->gnum)
+			break;
+		if (x)
+		    break;
+	    }
+	    if (y < mlines)
+		mline = y;
+	}
+	while (mline < mlbeg)
+	    if ((mlbeg -= step) < 0)
+		mlbeg = 0;
+
+	if (mlbeg && lbeg != mlbeg) {
+	    Cmatch **p = mtab + ((mlbeg - 1) * columns), **q;
+	    int c;
+
+	    while (mlbeg) {
+		for (q = p, c = columns; c; q++, c--)
+		    if (*q && *q != mtexpl)
+			break;
+		if (c)
+		    break;
+		p -= columns;
+		mlbeg--;
+	    }
+	}
+	if ((space = lines - pl - mhasstat))
+	    while (mline >= mlbeg + space)
+		if ((mlbeg += step) + space > mlines)
+		    mlbeg = mlines - space;
+	if (lbeg != mlbeg) {
+	    Cmatch **p = mtab + (mlbeg * columns), **q;
+	    int c;
+
+	    while (mlbeg < mlines) {
+		for (q = p, c = columns; c; q++, c--)
+		    if (*q)
+			break;
+		if (c)
+		    break;
+		p += columns;
+		mlbeg++;
+	    }
+	}
+	lbeg = mlbeg;
 	onlyexpl = 0;
 	showinglist = -2;
+	if (first && !listshown && isset(LISTBEEP))
+	    zbeep();
+	first = 0;
 	zrefresh();
 	inselect = 1;
-	if (noselect)
+	if (noselect) {
+	    broken = 1;
 	    break;
+	}
 	selected = 1;
 	if (!i) {
 	    i = mcols * mlines;
@@ -911,9 +1778,13 @@ domenuselect(Hookdef dummy, Chdata dat)
 
     getk:
 
-	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
+	    zbeep();
 	    break;
-	else if (cmd == Th(z_acceptline)) {
+	} else if (nolist && cmd != Th(z_undo)) {
+	    ungetkeycmd();
+	    break;
+	} else if (cmd == Th(z_acceptline)) {
 	    acc = 1;
 	    break;
 	} else if (cmd == Th(z_acceptandinfernexthistory)) {
@@ -923,6 +1794,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    u = s;
 	    s->line = dupstring((char *) line);
 	    s->cs = cs;
+	    s->mline = mline;
+	    s->mlbeg = mlbeg;
 	    memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
 	    s->amatches = amatches;
 	    s->pmatches = pmatches;
@@ -934,30 +1807,57 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    s->nbrbeg = nbrbeg;
 	    s->nbrend = nbrend;
 	    s->nmatches = nmatches;
+	    s->origline = origline;
+	    s->origcs = origcs;
+	    s->origll = origll;
 	    menucmp = menuacc = hasoldlist = 0;
+	    minfo.cur = NULL;
 	    fixsuffix();
+	    handleundo();
 	    validlist = 0;
 	    amatches = pmatches = lastmatches = NULL;
 	    invalidate_list();
+	    iforcemenu = 1;
+	    comprecursive = 1;
 	    menucomplete(zlenoargs);
-	    if (dat->num < 2 || !minfo.cur || !*(minfo.cur)) {
-		noselect = clearlist = listshown = 1;
-		onlyexpl = 0;
-		zrefresh();
-		break;
+	    iforcemenu = 0;
+
+	    if (dat->num < 1 || !minfo.cur || !*(minfo.cur)) {
+		nolist = 1;
+		if (dat->nmesg || nmessages) {
+		    showinglist = -2;
+		    zrefresh();
+		} else {
+		    trashzle();
+		    zsetterm();
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		    fputs("no matches\r", shout);
+		    fflush(shout);
+		    tcmultout(TCUP, TCMULTUP, nlnct);
+		    showinglist = clearlist = 0;
+		    clearflag = 1;
+		    zrefresh();
+		    showinglist = clearlist = 0;
+		}
+		goto getk;
 	    }
 	    clearlist = listshown = 1;
 	    mselect = (*(minfo.cur))->gnum;
 	    setwish = wasnext = 1;
+	    mline = 0;
 	    continue;
 	} else if (cmd == Th(z_acceptandhold) ||
 		   cmd == Th(z_acceptandmenucomplete)) {
 	    Menustack s = (Menustack) zhalloc(sizeof(*s));
+	    int ol;
 
 	    s->prev = u;
 	    u = s;
 	    s->line = dupstring((char *) line);
 	    s->cs = cs;
+	    s->mline = mline;
+	    s->mlbeg = mlbeg;
 	    memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
 	    s->amatches = s->pmatches =
 		s->lastmatches = s->lastlmatches = NULL;
@@ -967,18 +1867,45 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    s->nbrbeg = nbrbeg;
 	    s->nbrend = nbrend;
 	    s->nmatches = nmatches;
+	    s->origline = origline;
+	    s->origcs = origcs;
+	    s->origll = origll;
 	    accept_last();
+	    handleundo();
+	    comprecursive = 1;
 	    do_menucmp(0);
 	    mselect = (*(minfo.cur))->gnum;
+
+	    p -= mcol;
+	    mcol = 0;
+	    ol = mline;
+	    do {
+		for (mcol = 0; mcol < mcols; mcol++, p++)
+		    if (*p == minfo.cur)
+			break;
+		if (mcol != mcols)
+		    break;
+		if (++mline == mlines) {
+		    mline = 0;
+		    p -= mlines * mcols;
+		}
+	    } while (mline != ol);
+	    if (*p != minfo.cur) {
+		noselect = clearlist = listshown = 1;
+		onlyexpl = 0;
+		zrefresh();
+		break;
+	    }
 	    setwish = 1;
 	    continue;
 	} else if (cmd == Th(z_undo)) {
 	    int l;
 
 	    if (!u)
-		goto getk;
+		break;
 
-	    cs = 0;
+	    handleundo();
+	    cs = nolist = 0;
 	    foredel(ll);
 	    spaceinline(l = strlen(u->line));
 	    strncpy((char *) line, u->line, l);
@@ -986,15 +1913,17 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    menuacc = u->acc;
 	    memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
 	    p = &(minfo.cur);
+	    mline = u->mline;
+	    mlbeg = u->mlbeg;
 	    if (u->lastmatches && lastmatches != u->lastmatches) {
 		if (lastmatches)
-		    freematches(lastmatches);
+		    freematches(lastmatches, 0);
 		amatches = u->amatches;
 		pmatches = u->pmatches;
 		lastmatches = u->lastmatches;
 		lastlmatches = u->lastlmatches;
 		nmatches = u->nmatches;
-		hasoldlist = 1;
+		hasoldlist = validlist = 1;
 	    }
 	    freebrinfo(brbeg);
 	    freebrinfo(brend);
@@ -1002,6 +1931,9 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    brend = dupbrinfo(u->brend, &lastbrend, 0);
 	    nbrbeg = u->nbrbeg;
 	    nbrend = u->nbrend;
+	    origline = u->origline;
+	    origcs = u->origcs;
+	    origll = u->origll;
 
 	    u = u->prev;
 	    clearlist = 1;
@@ -1017,67 +1949,240 @@ domenuselect(Hookdef dummy, Chdata dat)
 		   cmd == Th(z_downlineorhistory) ||
 		   cmd == Th(z_downlineorsearch) ||
 		   cmd == Th(z_vidownlineorhistory)) {
+	    int omline;
+	    Cmatch **op;
+
+	    wrap = 0;
+
+	down:
+
+	    omline = mline;
+	    op = p;
+
 	    do {
 		if (mline == mlines - 1) {
+		    if (wrap & 2) {
+			mline = omline; 
+			p = op;
+			break;
+		    }
 		    p -= mline * mcols;
 		    mline = 0;
+		    wrap |= 1;
 		} else {
 		    mline++;
 		    p += mcols;
 		}
 		if (adjust_mcol(wishcol, &p, NULL))
 		    continue;
-	    } while (!*p);
+	    } while (!*p || *p == mtexpl);
+
+	    if (wrap == 1)
+		goto right;
 	} else if (cmd == Th(z_uphistory) ||
 		   cmd == Th(z_uplineorhistory) ||
 		   cmd == Th(z_uplineorsearch) ||
 		   cmd == Th(z_viuplineorhistory)) {
+	    int omline;
+	    Cmatch **op;
+
+	    wrap = 0;
+
+	up:
+
+	    omline = mline;
+	    op = p;
+
 	    do {
 		if (!mline) {
+		    if (wrap & 2) {
+			mline = omline; 
+			p = op;
+			break;
+		    }
 		    mline = mlines - 1;
 		    p += mline * mcols;
+		    wrap |= 1;
+		} else {
+		    mline--;
+		    p -= mcols;
+		}
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
+	    } while (!*p || *p == mtexpl);
+
+	    if (wrap == 1) {
+		if (mcol == wishcol)
+		    goto left;
+
+		wishcol = mcol;
+	    }
+	} else if (cmd == Th(z_emacsforwardword) ||
+		   cmd == Th(z_viforwardword) ||
+		   cmd == Th(z_viforwardwordend) ||
+		   cmd == Th(z_forwardword)) {
+	    int i = lines - pl - 1, oi = i, ll = 0;
+	    Cmatch **lp = NULL;
+
+	    if (mline == mlines - 1)
+		goto top;
+	    while (i > 0) {
+		if (mline == mlines - 1) {
+		    if (i != oi && lp)
+			break;
+		    goto top;
+		} else {
+		    mline++;
+		    p += mcols;
+		}
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
+		if (*p && *p != mtexpl) {
+		    i--;
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    p = lp;
+	    mline = ll;
+	} else if (cmd == Th(z_emacsbackwardword) ||
+		   cmd == Th(z_vibackwardword) ||
+		   cmd == Th(z_backwardword)) {
+	    int i = lines - pl - 1, oi = i, ll = 0;
+	    Cmatch **lp = NULL;
+
+	    if (!mline)
+		goto bottom;
+	    while (i > 0) {
+		if (!mline) {
+		    if (i != oi && lp)
+			break;
+		    goto bottom;
 		} else {
 		    mline--;
 		    p -= mcols;
 		}
 		if (adjust_mcol(wishcol, &p, NULL))
 		    continue;
-	    } while (!*p);
+		if (*p || *p != mtexpl) {
+		    i--;
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    p = lp;
+	    mline = ll;
+	} else if (cmd == Th(z_beginningofhistory)) {
+	    int ll;
+	    Cmatch **lp;
+
+	top:
+
+	    ll = mline;
+	    lp = p;
+	    while (mline) {
+		mline--;
+		p -= mcols;
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
+		if (*p && *p != mtexpl) {
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    mline = ll;
+	    p = lp;
+	} else if (cmd == Th(z_endofhistory)) {
+	    int ll;
+	    Cmatch **lp;
+
+	bottom:
+
+	    ll = mline;
+	    lp = p;
+	    while (mline < mlines - 1) {
+		mline++;
+		p += mcols;
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
+		if (*p && *p != mtexpl) {
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    mline = ll;
+	    p = lp;
 	} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
-	    int omcol = mcol;
-	    Cmatch *op = *p;
+	    int omcol;
+	    Cmatch **op;
+
+	    wrap = 0;
+
+	right:
+
+	    omcol = mcol;
+	    op = p;
 
 	    do {
 		if (mcol == mcols - 1) {
+		    if (wrap & 1) {
+			p = op;
+			mcol = omcol;
+			break;
+		    }
 		    p -= mcol;
 		    mcol = 0;
+		    wrap |= 2;
 		} else {
 		    mcol++;
 		    p++;
 		}
-	    } while (!*p || (mcol != omcol && *p == op));
+	    } while (!*p || *p == mtexpl || (mcol != omcol && *p == *op));
 	    wishcol = mcol;
+
+	    if (wrap == 2)
+		goto down;
 	} else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
-	    int omcol = mcol;
-	    Cmatch *op = *p;
+	    int omcol;
+	    Cmatch **op;
+
+	    wrap = 0;
+
+	left:
+
+	    omcol = mcol;
+	    op = p;
 
 	    do {
 		if (!mcol) {
+		    if (wrap & 1) {
+			p = op;
+			mcol = omcol;
+			break;
+		    }
 		    mcol = mcols - 1;
 		    p += mcol;
+		    wrap |= 2;
 		} else {
 		    mcol--;
 		    p--;
 		}
-	    } while (!*p || (mcol != omcol && *p == op));
+	    } while (!*p || *p == mtexpl || (mcol != omcol && *p == *op));
 	    wishcol = mcol;
+
+	    if (wrap == 2) {
+		p += mcols - 1 - mcol;
+		wishcol = mcol = mcols - 1;
+		adjust_mcol(wishcol, &p, NULL);
+		goto up;
+	    }
 	} else if (cmd == Th(z_beginningofbufferorhistory) ||
 		   cmd == Th(z_beginningofline) ||
 		   cmd == Th(z_beginningoflinehist) ||
 		   cmd == Th(z_vibeginningofline)) {
 	    p -= mcol;
 	    mcol = 0;
-	    while (!*p) {
+	    while (!*p || *p == mtexpl) {
 		mcol++;
 		p++;
 	    }
@@ -1088,15 +2193,13 @@ domenuselect(Hookdef dummy, Chdata dat)
 		   cmd == Th(z_viendofline)) {
 	    p += mcols - mcol - 1;
 	    mcol = mcols - 1;
-	    while (!*p) {
+	    while (!*p || *p == mtexpl) {
 		mcol--;
 		p--;
 	    }
 	    wishcol = mcols - 1;
-	} else if (cmd == Th(z_forwardword) ||
-		   cmd == Th(z_emacsforwardword) ||
-		   cmd == Th(z_viforwardword) ||
-		   cmd == Th(z_viforwardwordend)) {
+	} else if (cmd == Th(z_viforwardblankword) ||
+		   cmd == Th(z_viforwardblankwordend)) {
 	    Cmgroup g = *pg;
 	    int ol = mline;
 
@@ -1112,10 +2215,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 		}
 		if (adjust_mcol(wishcol, &p, &pg))
 		    continue;
-	    } while (ol != mline && (*pg == g || !*pg));
-	} else if (cmd == Th(z_backwardword) ||
-		   cmd == Th(z_emacsbackwardword) ||
-		   cmd == Th(z_vibackwardword)) {
+	    } while (ol != mline && (*pg == g || !*pg || *pg == mgexpl));
+	} else if (cmd == Th(z_vibackwardblankword)) {
 	    Cmgroup g = *pg;
 	    int ol = mline;
 
@@ -1131,7 +2232,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		}
 		if (adjust_mcol(wishcol, &p, &pg))
 		    continue;
-	    } while (ol != mline && (*pg == g || !*pg));
+	    } while (ol != mline && (*pg == g || !*pg || *pg == mgexpl));
 	} else if (cmd == Th(z_completeword) ||
 		   cmd == Th(z_expandorcomplete) ||
 		   cmd == Th(z_expandorcompleteprefix) ||
@@ -1143,18 +2244,29 @@ domenuselect(Hookdef dummy, Chdata dat)
 		   !strcmp(cmd->nam, "expand-or-complete-prefix") ||
 		   !strcmp(cmd->nam, "menu-complete") ||
 		   !strcmp(cmd->nam, "menu-expand-or-complete")) {
+	    comprecursive = 1;
 	    do_menucmp(0);
 	    mselect = (*(minfo.cur))->gnum;
 	    setwish = 1;
+	    mline = -1;
 	    continue;
 	} else if (cmd == Th(z_reversemenucomplete) ||
 		   !strcmp(cmd->nam, "reverse-menu-complete")) {
+	    comprecursive = 1;
 	    reversemenucomplete(zlenoargs);
 	    mselect = (*(minfo.cur))->gnum;
 	    setwish = 1;
+	    mline = -1;
+	    continue;
+	} else if (cmd == Th(z_undefinedkey)) {
 	    continue;
 	} else {
 	    ungetkeycmd();
+	    if (cmd->widget && (cmd->widget->flags & WIDGET_NCOMP)) {
+		acc = 0;
+		broken = 2;
+	    } else
+		acc = 1;
 	    break;
 	}
 	do_single(**p);
@@ -1163,30 +2275,39 @@ domenuselect(Hookdef dummy, Chdata dat)
     if (u)
 	for (; u; u = u->prev)
 	    if (u->lastmatches != lastmatches)
-		freematches(u->lastmatches);
+		freematches(u->lastmatches, 0);
 
     selectlocalmap(NULL);
-    mselect = -1;
-    inselect = 0;
+    mselect = mlastcols = mlastlines = -1;
+    mstatus = NULL;
+    inselect = mhasstat = 0;
     if (acc) {
 	menucmp = lastambig = hasoldlist = 0;
 	do_single(*(minfo.cur));
     }
-    if (wasnext) {
+    if (wasnext || broken) {
 	menucmp = 2;
 	showinglist = -2;
 	minfo.asked = 0;
+	if (!noselect) {
+	    int nos = noselect;
+
+	    zrefresh();
+	    noselect = nos;
+	}
     }
-    if (!noselect) {
+    if (!noselect && (!dat || acc)) {
 	showinglist = -2;
 	onlyexpl = oe;
 	if (!smatches)
 	    clearlist = 1;
 	zrefresh();
     }
+    mlbeg = -1;
     fdat = NULL;
 
-    return (!noselect ^ acc);
+    return (broken == 2 ? 3 :
+	    ((dat && !broken) ? (acc ? 1 : 2) : (!noselect ^ acc)));
 }
 
 /* The widget function. */
@@ -1247,6 +2368,14 @@ boot_(Module m)
     bindkey(mskeymap, "\33OB",  refthingy(t_downlineorhistory), NULL);
     bindkey(mskeymap, "\33OC",  refthingy(t_forwardchar), NULL);
     bindkey(mskeymap, "\33OD",  refthingy(t_backwardchar), NULL);
+    lskeymap = newkeymap(NULL, "listscroll");
+    linkkeymap(lskeymap, "listscroll", 1);
+    bindkey(lskeymap, "\t", refthingy(t_completeword), NULL);
+    bindkey(lskeymap, " ", refthingy(t_completeword), NULL);
+    bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL);
+    bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL);
+    bindkey(lskeymap, "\33[B",  refthingy(t_downlineorhistory), NULL);
+    bindkey(lskeymap, "\33OB",  refthingy(t_downlineorhistory), NULL);
     return 0;
 }
 
@@ -1261,6 +2390,7 @@ cleanup_(Module m)
     deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
     deletehookfunc("menu_start", (Hookfn) domenuselect);
     unlinkkeymap("menuselect", 1);
+    unlinkkeymap("listscroll", 1);
     return 0;
 }
 
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 00bc0382a..5886b4114 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -58,11 +58,6 @@ mod_export int clwsize, clwnum, clwpos;
 /**/
 mod_export char **clwords;
 
-/* wb and we hold the beginning/end position of the word we are completing. */
-
-/**/
-mod_export int wb, we;
-
 /* offs is the cursor position within the tokenized *
  * current word after removing nulargs.             */
 
@@ -77,6 +72,11 @@ mod_export int offs;
 /**/
 mod_export int usemenu, useglob;
 
+/* != 0 if we would insert a TAB if we weren't calling a completion widget. */
+
+/**/
+mod_export int wouldinstab;
+
 /* != 0 if we are in the middle of a menu completion. May be == 2 to force *
  * menu completion even if using different widgets.                        */
 
@@ -136,7 +136,7 @@ mod_export char *compfunc = NULL;
  * lastambig == 2.                                                    */
 
 /**/
-mod_export int lastambig;
+mod_export int lastambig, bashlistfirst;
 
 /* Arguments for and return value of completion widget. */
 
@@ -145,6 +145,11 @@ mod_export char **cfargs;
 /**/
 mod_export int cfret;
 
+/* != 0 if recursive calls to completion are (temporarily) allowed */
+
+/**/
+mod_export int comprecursive;
+
 /* Find out if we have to insert a tab (instead of trying to complete). */
 
 /**/
@@ -153,9 +158,16 @@ usetab(void)
 {
     unsigned char *s = line + cs - 1;
 
+    if (keybuf[0] != '\t' || keybuf[1])
+	return 0;
     for (; s >= line && *s != '\n'; s--)
 	if (*s != '\t' && *s != ' ')
 	    return 0;
+    if (compfunc) {
+	wouldinstab = 1;
+
+	return 0;
+    }
     return 1;
 }
 
@@ -179,12 +191,15 @@ completeword(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     if (c == '\t' && usetab())
 	return selfinsert(args);
     else {
 	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
+	    bashlistfirst = 1;
 	    ret = docomplete(COMP_LIST_COMPLETE);
+	    bashlistfirst = 0;
 	    lastambig = 2;
 	} else
 	    ret = docomplete(COMP_COMPLETE);
@@ -198,6 +213,7 @@ menucomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     if (c == '\t' && usetab())
 	return selfinsert(args);
     else
@@ -210,6 +226,7 @@ listchoices(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     return docomplete(COMP_LIST_COMPLETE);
 }
 
@@ -218,6 +235,7 @@ int
 spellword(char **args)
 {
     usemenu = useglob = 0;
+    wouldinstab = 0;
     return docomplete(COMP_SPELL);
 }
 
@@ -227,6 +245,7 @@ deletecharorlist(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
 
     if (cs != ll) {
 	fixsuffix();
@@ -241,6 +260,7 @@ int
 expandword(char **args)
 {
     usemenu = useglob = 0;
+    wouldinstab = 0;
     if (c == '\t' && usetab())
 	return selfinsert(args);
     else
@@ -253,12 +273,15 @@ expandorcomplete(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     if (c == '\t' && usetab())
 	return selfinsert(args);
     else {
 	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
+	    bashlistfirst = 1;
 	    ret = docomplete(COMP_LIST_COMPLETE);
+	    bashlistfirst = 0;
 	    lastambig = 2;
 	} else
 	    ret = docomplete(COMP_EXPAND_COMPLETE);
@@ -272,6 +295,7 @@ menuexpandorcomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     if (c == '\t' && usetab())
 	return selfinsert(args);
     else
@@ -284,6 +308,7 @@ listexpand(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+    wouldinstab = 0;
     return docomplete(COMP_LIST_EXPAND);
 }
 
@@ -291,6 +316,7 @@ listexpand(char **args)
 mod_export int
 reversemenucomplete(char **args)
 {
+    wouldinstab = 0;
     if (!menucmp)
 	return menucomplete(args);
 
@@ -302,6 +328,7 @@ reversemenucomplete(char **args)
 int
 acceptandmenucomplete(char **args)
 {
+    wouldinstab = 0;
     if (!menucmp)
 	return 1;
     runhookdef(ACCEPTCOMPHOOK, NULL);
@@ -513,21 +540,34 @@ parambeg(char *s)
 static int
 docomplete(int lst)
 {
+    static int active = 0;
+
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, dat[2];
 
+    if (active && !comprecursive) {
+	zwarn("completion cannot be used recursively (yet)", NULL, 0);
+	return 1;
+    }
+    active = 1;
+    comprecursive = 0;
     if (undoing)
 	setlastline();
 
-    if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst))
-	return 0;
+    if (!module_loaded("zsh/complete"))
+	load_module("zsh/compctl");
 
+    if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst)) {
+	active = 0;
+	return 0;
+    }
     /* Expand history references before starting completion.  If anything *
      * changed, do no more.                                               */
 
-    if (doexpandhist())
+    if (doexpandhist()) {
+	active = 0;
 	return 0;
-
+    }
     metafy_line();
 
     ocs = cs;
@@ -556,7 +596,8 @@ docomplete(int lst)
     qisuf = ztrdup("");
     zsfree(autoq);
     autoq = NULL;
-    /* Get the word to complete. */
+    /* Get the word to complete.
+     * NOTE: get_comp_string() calls pushheap(), but not popheap(). */
     noerrs = 1;
     s = get_comp_string();
     DPUTS(wb < 0 || cs < wb || cs > we,
@@ -578,7 +619,11 @@ docomplete(int lst)
 	    strcpy((char *) line, ol);
 	    ll = strlen((char *) line);
 	    cs = ocs;
+	    popheap();
 	    unmetafy_line();
+	    zsfree(s);
+	    zsfree(qword);
+	    active = 0;
 	    return 1;
 	}
 	ocs = cs;
@@ -738,8 +783,20 @@ docomplete(int lst)
 			}
 		}
 		ret = docompletion(s, lst, lincmd);
-	    } else if (ret)
-		clearlist = 1;
+            } else {
+                if (ret)
+                    clearlist = 1;
+                if (!strcmp(ol, (char *)line)) {
+                    /* We may have removed some quotes. For completion, other
+                     * parts of the code re-install them, but for expansion
+                     * we have to do it here. */
+                    cs = 0;
+                    foredel(ll);
+                    spaceinline(origll);
+                    memcpy(line, origline, origll);
+                    cs = origcs;
+                }
+            }
 	} else
 	    /* Just do completion. */
 	    ret = docompletion(s, lst, lincmd);
@@ -752,9 +809,12 @@ docomplete(int lst)
     zsfree(qword);
     unmetafy_line();
 
-    runhookdef(AFTERCOMPLETEHOOK, (void *) &lst);
+    dat[0] = lst;
+    dat[1] = ret;
+    runhookdef(AFTERCOMPLETEHOOK, (void *) dat);
 
-    return ret;
+    active = 0;
+    return dat[1];
 }
 
 /* 1 if we are completing the prefix */
@@ -786,7 +846,8 @@ addx(char **ptmp)
 
     if (!line[cs] || line[cs] == '\n' ||
 	(iblank(line[cs]) && (!cs || line[cs-1] != '\\')) ||
-	line[cs] == ')' || line[cs] == '`' ||
+	line[cs] == ')' || line[cs] == '`' || line[cs] == '}' ||
+	line[cs] == ';' || line[cs] == '|' || line[cs] == '&' ||
 	(instring && (line[cs] == '"' || line[cs] == '\'')) ||
 	(addspace = (comppref && !iblank(line[cs])))) {
 	*ptmp = (char *)line;
@@ -915,6 +976,7 @@ static char *
 get_comp_string(void)
 {
     int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0;
+    int ona = noaliases;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     freebrinfo(brbeg);
@@ -970,13 +1032,10 @@ get_comp_string(void)
      * the previously massaged command line using the lexer.  It stores *
      * each token in each command (commands being regarded, roughly, as *
      * being separated by tokens | & &! |& || &&).  The loop stops when *
-     * the end of the command containing the cursor is reached.  It's a *
-     * simple way to do things, but suffers from an inability to        *
-     * distinguish actual command arguments from, for example,          *
-     * filenames in redirections.  (But note that code elsewhere checks *
-     * if we are completing *in* a redirection.)  The only way to fix   *
-     * this would be to pass the command line through the parser too,   *
-     * and get the arguments that way.  Maybe in 3.1...                 */
+     * the end of the command containing the cursor is reached.  What   *
+     * makes this messy is checking for things like redirections, loops *
+    * and whatnot. */
+
     do {
 	lincmd = ((incmdpos && !ins && !incond) || (oins == 2 && i == 2) ||
 		  (ins == 3 && i == 1));
@@ -1126,6 +1185,7 @@ get_comp_string(void)
 		line[ll + addedx] = '\0';
 	    }
 	    lexrestore();
+	    tt = NULL;
 	    goto start;
 	}
     }
@@ -1186,12 +1246,12 @@ get_comp_string(void)
 	    addedx = 0;
 	    goto start;
 	}
-	noaliases = 0;
+	noaliases = ona;
 	lexrestore();
 	return NULL;
     }
 
-    noaliases = 0;
+    noaliases = ona;
 
     /* Check if we are in an array subscript.  We simply assume that  *
      * we are in a subscript if we are in brackets.  Correct solution *
@@ -1281,6 +1341,7 @@ get_comp_string(void)
 	    } else
 		insubscr = 1;
 	}
+	parse_subst_string(s);
     }
     /* This variable will hold the current word in quoted form. */
     qword = ztrdup(s);
@@ -1291,6 +1352,19 @@ get_comp_string(void)
 		*p = '"';
 	    else if (*p == Snull)
 		*p = '\'';
+    } else {
+        int level = 0;
+
+        for (p = s; *p; p++) {
+            if (level && *p == Snull)
+                *p = '\'';
+            else if (level && *p == Dnull)
+                *p = '"';
+            else if ((*p == String || *p == Qstring) && p[1] == Inbrace)
+                level++;
+            else if (*p == Outbrace)
+                level--;
+        }
     }
     if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) {
 	char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, "");
@@ -1307,10 +1381,12 @@ get_comp_string(void)
 	autoq = ztrdup(q);
     }
     /* While building the quoted form, we also clean up the command line. */
-    for (p = s, tt = qword, i = wb; *p; p++, tt++, i++)
+    for (p = s, tt = qword, i = wb, j = 0; *p; p++, tt++, i++)
 	if (INULL(*p)) {
 	    if (i < cs)
 		offs--;
+	    if (*p == Snull && isset(RCQUOTES))
+		j = 1-j;
 	    if (p[1] || *p != Bnull) {
 		if (*p == Bnull) {
 		    *tt = '\\';
@@ -1337,7 +1413,8 @@ get_comp_string(void)
 		we--;
 	    }
 	    chuck(p--);
-	}
+	} else if (j && *p == '\'' && i < cs)
+	    offs--;
 
     zsfree(origword);
     origword = ztrdup(s);
@@ -1408,6 +1485,14 @@ get_comp_string(void)
 		    break;
 		}
 		if (*p == Inbrace) {
+		    char *tp = p;
+
+		    if (!skipparens(Inbrace, Outbrace, &tp)) {
+			i += tp - p - 1;
+			dp += tp - p - 1;
+			p = tp - 1;
+			continue;
+		    }
 		    if (bbeg) {
 			Brinfo new;
 			int len = bend - bbeg;
@@ -1423,11 +1508,12 @@ get_comp_string(void)
 			lastbrbeg = new;
 
 			new->next = NULL;
-			new->str = ztrduppfx(bbeg, len);
+			new->str = dupstrpfx(bbeg, len);
+			new->str = ztrdup(bslashquote(new->str, NULL, instring));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(quotename(predup, NULL));
+			new->qpos = strlen(bslashquote(predup, NULL, instring));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -1444,6 +1530,14 @@ get_comp_string(void)
 		}
 	    } else {
 		if (*p == Inbrace) {
+		    char *tp = p;
+
+		    if (!skipparens(Inbrace, Outbrace, &tp)) {
+			i += tp - p - 1;
+			dp += tp - p - 1;
+			p = tp - 1;
+			continue;
+		    }
 		    cant = 1;
 		    break;
 		}
@@ -1462,11 +1556,12 @@ get_comp_string(void)
 			    brbeg = new;
 			lastbrbeg = new;
 
-			new->str = ztrduppfx(bbeg, len);
+			new->str = dupstrpfx(bbeg, len);
+			new->str = ztrdup(bslashquote(new->str, NULL, instring));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(quotename(predup, NULL));
+			new->qpos = strlen(bslashquote(predup, NULL, instring));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -1478,7 +1573,7 @@ get_comp_string(void)
 		if (*p == Comma) {
 		    if (!bbeg)
 			bbeg = p;
-		    hascom = 1;
+		    hascom = 2;
 		} else if (*p == Outbrace) {
 		    Brinfo new;
 		    int len;
@@ -1498,7 +1593,8 @@ get_comp_string(void)
 		    new->next = brend;
 		    brend = new;
 
-		    new->str = ztrduppfx(bbeg, len);
+		    new->str = dupstrpfx(bbeg, len);
+		    new->str = ztrdup(bslashquote(new->str, NULL, instring));
 		    untokenize(new->str);
 		    new->pos = dp - predup - len + 1;
 		    new->qpos = len;
@@ -1526,11 +1622,12 @@ get_comp_string(void)
 		    brbeg = new;
 		lastbrbeg = new;
 
-		new->str = ztrduppfx(bbeg, len);
+		new->str = dupstrpfx(bbeg, len);
+		new->str = ztrdup(bslashquote(new->str, NULL, instring));
 		untokenize(new->str);
 		new->pos = begi;
 		*dbeg = '\0';
-		new->qpos = strlen(quotename(predup, NULL));
+		new->qpos = strlen(bslashquote(predup, NULL, instring));
 		*dbeg = '{';
 		boffs -= len;
 		strcpy(dbeg, dbeg + len);
@@ -1545,7 +1642,7 @@ get_comp_string(void)
 		    p = bp->pos;
 		    l = bp->qpos;
 		    bp->pos = strlen(predup + p + l);
-		    bp->qpos = strlen(quotename(predup + p + l, NULL));
+		    bp->qpos = strlen(bslashquote(predup + p + l, NULL, instring));
 		    strcpy(predup + p, predup + p + l);
 		}
 	    }
@@ -1596,13 +1693,20 @@ inststrlen(char *str, int move, int len)
 static int
 doexpansion(char *s, int lst, int olst, int explincmd)
 {
-    int ret = 1;
+    int ret = 1, first = 1;
     LinkList vl;
-    char *ss;
+    char *ss, *ts;
 
     pushheap();
     vl = newlinklist();
     ss = dupstring(s);
+    /* get_comp_string() leaves these quotes unchanged when they are
+     * inside parameter expansions. */
+    for (ts = ss; *ts; ts++)
+        if (*ts == '"')
+            *ts = Dnull;
+        else if (*ts == '\'')
+            *ts = Snull;
     addlinknode(vl, ss);
     prefork(vl, 0);
     if (errflag)
@@ -1630,9 +1734,15 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	goto end;
     }
     if (lst == COMP_LIST_EXPAND) {
-	/* Only the list of expansions was requested. */
-	ret = listlist(vl);
-	showinglist = 0;
+	/* Only the list of expansions was requested. Restore the 
+         * command line. */
+        cs = 0;
+        foredel(ll);
+        spaceinline(origll);
+        memcpy(line, origline, origll);
+        cs = origcs;
+        ret = listlist(vl);
+        showinglist = 0;
 	goto end;
     }
     /* Remove the current word and put the expansions there. */
@@ -1647,10 +1757,11 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) ||
 	    (cs && line[cs-1] != '/')) {
 #endif
-	if (nonempty(vl)) {
+	if (nonempty(vl) || !first) {
 	    spaceinline(1);
 	    line[cs++] = ' ';
 	}
+	first = 0;
     }
     end:
     popheap();
@@ -1658,19 +1769,6 @@ doexpansion(char *s, int lst, int olst, int explincmd)
     return ret;
 }
 
-/* This is called from the lexer to give us word positions. */
-
-/**/
-void
-gotword(void)
-{
-    we = ll + 1 - inbufct + (addedx == 2 ? 1 : 0);
-    if (cs <= we) {
-	wb = ll - wordbeg + addedx;
-	zleparse = 0;
-    }
-}
-
 /**/
 static int
 docompletion(char *s, int lst, int incmd)
@@ -1851,14 +1949,19 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 			    putc(' ', shout);
 		    }
 		}
-		l += 1 + (cc / columns);
+		l += 1 + ((cc - 1) / columns);
 		cc = 0;
 	    }
-	    if (dopr)
+	    if (dopr) {
 		putc(*p, shout);
+                if (!(cc % columns))
+                    fputs(" \010", shout);
+            }
 	}
     }
     if (dopr) {
+        if (!(cc % columns))
+            fputs(" \010", shout);
 	if (tccan(TCCLEAREOL))
 	    tcout(TCCLEAREOL);
 	else {
@@ -1983,13 +2086,16 @@ listlist(LinkList l)
 
     max = getiparam("LISTMAX");
     if ((max && num > max) || (!max && nlines > lines)) {
-	int qup;
+	int qup, l;
 
 	zsetterm();
-	qup = printfmt("zsh: do you wish to see all %n possibilities? ",
-		       num, 1, 1);
+	l = (num > 0 ?
+	     fprintf(shout, "zsh: do you wish to see all %d possibilities (%d lines)? ",
+		     num, nlines) :
+	     fprintf(shout, "zsh: do you wish to see all %d lines? ", nlines));
+	qup = ((l + columns - 1) / columns) - 1;
 	fflush(shout);
-	if (getzlequery() != 'y') {
+	if (getzlequery(1) != 'y') {
 	    if (clearflag) {
 		putc('\r', shout);
 		tcmultout(TCUP, TCMULTUP, qup);
@@ -2073,7 +2179,7 @@ int
 doexpandhist(void)
 {
     unsigned char *ol;
-    int oll, ocs, ne = noerrs, err;
+    int oll, ocs, ne = noerrs, err, ona = noaliases;
 
     pushheap();
     metafy_line();
@@ -2100,7 +2206,7 @@ doexpandhist(void)
      * means that the expanded string is unusable.                       */
     err = errflag;
     noerrs = ne;
-    noaliases = 0;
+    noaliases = ona;
     strinend();
     inpop();
     zleparse = 0;