about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorSven Wischnowsky <wischnow@users.sourceforge.net>2000-04-17 11:17:10 +0000
committerSven Wischnowsky <wischnow@users.sourceforge.net>2000-04-17 11:17:10 +0000
commit30a98cc4d6e54f2a5c356579aa60c2af8d344101 (patch)
tree6cc8874b7712a0be1f5a506dee62ca984d53b130 /Src
parentd85d34d8183a8a45bfb09c4242ef9368860be6d9 (diff)
downloadzsh-30a98cc4d6e54f2a5c356579aa60c2af8d344101.tar.gz
zsh-30a98cc4d6e54f2a5c356579aa60c2af8d344101.tar.xz
zsh-30a98cc4d6e54f2a5c356579aa60c2af8d344101.zip
scrolling in completion lists and menu-selection, version1
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/compcore.c3
-rw-r--r--Src/Zle/complete.c11
-rw-r--r--Src/Zle/complist.c1809
-rw-r--r--Src/Zle/compresult.c19
-rw-r--r--Src/Zle/zle_tricky.c2
-rw-r--r--Src/Zle/zle_utils.c33
-rw-r--r--Src/params.c2
7 files changed, 1526 insertions, 353 deletions
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 6e2cbf537..d5dee0dca 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -296,7 +296,8 @@ do_completion(Hookdef dummy, Compldat dat)
     comppatinsert = ztrdup("menu");
     forcelist = 0;
     haspattern = 0;
-    complistmax = getiparam("LISTMAX");
+    zsfree(complistmax);
+    complistmax = ztrdup(getsparam("LISTMAX"));
     zsfree(complastprompt);
     complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
 			     (unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index ef961eeba..58cd8a216 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -35,8 +35,7 @@
 /**/
 mod_export zlong compcurrent;
 /**/
-zlong complistmax,
-      complistlines,
+zlong complistlines,
       compignored;
 
 /**/
@@ -50,7 +49,8 @@ char **compwords,
      *compquote,
      *compqstack,
      *comppatmatch,
-     *complastprompt;
+     *complastprompt,
+     *complistmax;
 /**/
 char *compiprefix,
      *compcontext,
@@ -904,7 +904,7 @@ static struct compparam compkparams[] = {
     { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
     { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
       VAL(get_unambig_curs) },
-    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+    { "list_max", PM_SCALAR, 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 },
@@ -1292,7 +1292,7 @@ setup_(Module m)
     comprpms = compkpms = NULL;
     compwords = NULL;
     compprefix = compsuffix = compiprefix = compisuffix = 
-	compqiprefix = compqisuffix =
+	compqiprefix = compqisuffix = complistmax = 
 	compcontext = compparameter = compredirect = compquote =
 	compquoting = comprestore = complist = compinsert =
 	compexact = compexactstr = comppatmatch = comppatinsert =
@@ -1347,6 +1347,7 @@ finish_(Module m)
 {
     if (compwords)
 	freearray(compwords);
+    zsfree(complistmax);
     zsfree(compprefix);
     zsfree(compsuffix);
     zsfree(compiprefix);
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 63bc4c6e3..f3363cc97 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -54,22 +54,52 @@ static Keymap mskeymap;
 #define COL_LC 10
 #define COL_RC 11
 #define COL_EC 12
-#define COL_MA 13
+#define COL_TC 13
+#define COL_SP 14
+#define COL_MA 15
+#define COL_HI 16
+#define COL_DU 17
+#define COL_ST 18
 
-#define NUM_COLS 14
+#define NUM_COLS 19
+
+/* Maximum number of in-string colours supported. */
+
+#define MAX_POS 11
 
 /* Names of the terminal strings. */
 
 static char *colnames[] = {
     "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "ex", "mi",
-    "lc", "rc", "ec", "ma", NULL
+    "lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "st", NULL
 };
 
 /* Default values. */
 
 static char *defcols[] = {
     "0", "0", "1;34", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL,
-    "\033[", "m", NULL, "7"
+    "\033[", "m", NULL, "0", "0", "7", "0", "0", "7"
+};
+
+/* This describes a terminal string for a file type. */
+
+typedef struct filecol *Filecol;
+
+struct filecol {
+    Patprog prog;		/* group pattern */
+    char *col;			/* color string */
+    Filecol next;		/* next one */
+};
+
+/* This describes a terminal string for a pattern. */
+
+typedef struct patcol *Patcol;
+
+struct patcol {
+    Patprog prog;
+    Patprog pat;		/* pattern for match */
+    char *cols[MAX_POS + 1];
+    Patcol next;
 };
 
 /* This describes a terminal string for a filename extension. */
@@ -77,6 +107,7 @@ static char *defcols[] = {
 typedef struct extcol *Extcol;
 
 struct extcol {
+    Patprog prog;		/* group pattern or NULL */
     char *ext;			/* the extension */
     char *col;			/* the terminal color string */
     Extcol next;		/* the next one in the list */
@@ -87,7 +118,8 @@ struct extcol {
 typedef struct listcols *Listcols;
 
 struct listcols {
-    char *cols[NUM_COLS];	/* strings for file types */
+    Filecol files[NUM_COLS];	/* strings for file types */
+    Patcol pats;		/* strings for patterns */
     Extcol exts;		/* strings for extensions */
 };
 
@@ -95,11 +127,11 @@ struct listcols {
  * The return value is a pointer to the character after it. */
 
 static char *
-getcolval(char *s)
+getcolval(char *s, int multi)
 {
     char *p;
 
-    for (p = s; *s && *s != ':'; p++, s++) {
+    for (p = s; *s && *s != ':' && (!multi || *s != '='); p++, s++) {
 	if (*s == '\\' && s[1]) {
 	    switch (*++s) {
 	    case 'a': *p = '\007'; break;
@@ -150,8 +182,33 @@ getcolval(char *s)
 static char *
 getcoldef(Listcols c, char *s)
 {
+    Patprog gprog = NULL;
+
+    if (*s == '(') {
+	char *p;
+	int l = 0;
+
+	for (p = s + 1, l = 0; *p && (*p != ')' || l); p++)
+	    if (*p == '\\' && p[1])
+		p++;
+	    else if (*p == '(')
+		l++;
+	    else if (*p == ')')
+		l--;
+
+	if (*p == ')') {
+	    char sav = p[1];
+
+	    p[1] = '\0';
+	    tokenize(s);
+	    gprog = patcompile(s, 0, NULL);
+	    p[1]  =sav;
+
+	    s = p + 1;
+	}
+    }
     if (*s == '*') {
-	Extcol ec;
+	Extcol ec, eo;
 	char *n, *p;
 
 	/* This is for an extension. */
@@ -159,20 +216,67 @@ getcoldef(Listcols c, char *s)
 	n = ++s;
 	while (*s && *s != '=')
 	    s++;
-	if (!*s )
+	if (!*s)
 	    return s;
 	*s++ = '\0';
-	p = getcolval(s);
-	if (*n) {
-	    ec = (Extcol) zhalloc(sizeof(*ec));
-	    ec->ext = n;
-	    ec->col = s;
-	    ec->next = c->exts;
+	p = getcolval(s, 0);
+	ec = (Extcol) zhalloc(sizeof(*ec));
+	ec->prog = gprog;
+	ec->ext = n;
+	ec->col = s;
+	ec->next = NULL;
+	if ((eo = c->exts)) {
+	    while (eo->next)
+		eo = eo->next;
+	    eo->next = ec;
+	} else
 	    c->exts = ec;
-	}
 	if (*p)
 	    *p++ = '\0';
 	return p;
+    } else if (*s == '=') {
+	char *p = ++s, *t, *cols[MAX_POS];
+	int ncols = 0;
+	Patprog prog;
+
+	/* This is for a pattern. */
+
+	while (*s && *s != '=')
+	    s++;
+	if (!*s)
+	    return s;
+	*s++ = '\0';
+	while (1) {
+	    t = getcolval(s, 1);
+	    if (ncols < MAX_POS)
+		cols[ncols++] = s;
+	    s = t;
+	    if (*s != '=')
+		break;
+	    *s++ = '\0';
+	}
+	tokenize(p);
+	if ((prog = patcompile(p, 0, NULL))) {
+	    Patcol pc, po;
+	    int i;
+
+	    pc = (Patcol) zhalloc(sizeof(*pc));
+	    pc->prog = gprog;
+	    pc->pat = prog;
+	    for (i = 0; i < ncols; i++)
+		pc->cols[i] = cols[i];
+	    pc->cols[i] = NULL;
+	    pc->next = NULL;
+	    if ((po = c->pats)) {
+		while (po->next)
+		    po = po->next;
+		po->next = pc;
+	    } else
+		c->pats = pc;
+	}
+	if (*t)
+	    *t++ = '\0';
+	return t;
     } else {
 	char *n = s, *p, **nn;
 	int i;
@@ -185,37 +289,76 @@ getcoldef(Listcols c, char *s)
 	    return s;
 	*s++ = '\0';
 	for (i = 0, nn = colnames; *nn; i++, nn++)
-	    if (!strcmp(n ,*nn))
+	    if (!strcmp(n, *nn))
 		break;
-	p = getcolval(s);
-	if (*nn)
-	    c->cols[i] = s;
+	p = getcolval(s, 0);
+	if (*nn) {
+	    Filecol fc, fo;
+
+	    fc = (Filecol) zhalloc(sizeof(*fc));
+	    fc->prog = (i == COL_EC || i == COL_LC || i == COL_RC ?
+			NULL : gprog);
+	    fc->col = s;
+	    fc->next = NULL;
+	    if ((fo = c->files[i])) {
+		while (fo->next)
+		    fo = fo->next;
+		fo->next = fc;
+	    } else
+		c->files[i] = fc;
+	}
 	if (*p)
 	    *p++ = '\0';
 	return p;
     }
 }
 
+static Filecol
+filecol(char *col)
+{
+    Filecol fc;
+
+    fc = (Filecol) zhalloc(sizeof(*fc));
+    fc->prog = NULL;
+    fc->col = col;
+    fc->next = NULL;
+
+    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 int
+static void
 getcols(Listcols c)
 {
     char *s;
-    int i;
+    int i, l;
 
     if (!(s = getsparam("ZLS_COLORS")) &&
 	!(s = getsparam("ZLS_COLOURS"))) {
-	if (!c)
-	    return 1;
 	for (i = 0; i < NUM_COLS; i++)
-	    c->cols[i] = "";
-	
+	    c->files[i] = filecol("");
+	c->pats = NULL;
 	c->exts = NULL;
-	return 1;
+	
+	if ((s = tcstr[TCSTANDOUTBEG]) && s[0]) {
+	    c->files[COL_MA] = filecol(s);
+	    c->files[COL_ST] = filecol(s);
+	    c->files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]);
+	} else {
+	    c->files[COL_MA] = filecol(defcols[COL_MA]);
+	    c->files[COL_ST] = filecol(defcols[COL_ST]);
+	}
+	lr_caplen = 0;
+	if ((max_caplen = strlen(c->files[COL_MA]->col)) <
+	    (l = strlen(c->files[COL_EC]->col)))
+	    max_caplen = l;
+	return;
     }
-    if (!c)
-	return 0;
     /* We have one of the parameters, use it. */
     memset(c, 0, sizeof(*c));
     s = dupstring(s);
@@ -223,312 +366,796 @@ getcols(Listcols c)
 	s = getcoldef(c, s);
 
     /* Use default values for those that aren't set explicitly. */
-    for (i = 0; i < NUM_COLS; i++)
-	if (!c->cols[i])
-	    c->cols[i] = defcols[i];
+    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]);
+	if (c->files[i] && c->files[i]->col &&
+	    (l = strlen(c->files[i]->col)) > max_caplen)
+	    max_caplen = l;
+    }
+    lr_caplen = strlen(c->files[COL_LC]->col) + strlen(c->files[COL_RC]->col);
+
     /* Default for missing files. */
-    if (!c->cols[COL_MI])
-	c->cols[COL_MI] = c->cols[COL_FI];
-
-    if (!c->cols[COL_EC]) {
-	char *e = (char *) zhalloc(strlen(c->cols[COL_LC]) +
-				   strlen(c->cols[COL_NO]) +
-				   strlen(c->cols[COL_RC]) + 1);
-
-	/* If no `ec' was given, we is `<lc><no><rc>' as the default. */
-	strcpy(e, c->cols[COL_LC]);
-	strcat(e, c->cols[COL_NO]);
-	strcat(e, c->cols[COL_RC]);
-	c->cols[COL_EC] = e;
+    if (!c->files[COL_MI] || !c->files[COL_MI]->col)
+	c->files[COL_MI] = c->files[COL_FI];
+
+    return;
+}
+
+/* Information about the list shown. */
+
+static int noselect, mselect, inselect, mcol, mline, mcols, mlines, mmlen;
+static int selected, mlbeg = -1, mlend = 9999999, mscroll, mrestlines;
+static int mnew, mlastcols, mlastlines, mhasstat;
+static char *mstatus;
+static Cmatch **mtab, **mmtabp;
+static Cmgroup *mgtab, *mgtabp;
+static struct listcols mcolors;
+
+/* Information for in-string colours. */
+
+static int nrefs;
+static int begpos[MAX_POS], curisbeg;
+static int endpos[MAX_POS];
+static int sendpos[MAX_POS], curissend; /* sorted end positions */
+static char **patcols, *curiscols[MAX_POS];
+static int curiscol;
+
+/* The last color used. */
+
+static char *last_cap;
+
+static void
+zlrputs(Listcols c, char *cap)
+{
+    if (!*last_cap || strcmp(last_cap, cap)) {
+	VARARR(char, buf, lr_caplen + max_caplen + 1);
+
+	strcpy(buf, c->files[COL_LC]->col);
+	strcat(buf, cap);
+	strcat(buf, c->files[COL_RC]->col);
+
+	tputs(buf, 1, putshout);
+
+	strcpy(last_cap, cap);
     }
+}
+
+static void
+zcputs(Listcols c, char *group, int colour)
+{
+    Filecol fc;
+
+    for (fc = c->files[colour]; fc; fc = fc->next)
+	if (fc->col &&
+	    (!fc->prog || !group || pattry(fc->prog, group))) {
+	    zlrputs(c, fc->col);
+
+	    return;
+	}
+}
+
+/* Turn off colouring. */
+
+static void
+zcoff(void)
+{
+    if (mcolors.files[COL_EC] && mcolors.files[COL_EC]->col) {
+	tputs(mcolors.files[COL_EC]->col, 1, putshout);
+	*last_cap = '\0';
+    } else
+	zcputs(&mcolors, NULL, COL_NO);
+}
+
+
+static void
+initiscol(Listcols c)
+{
+    int i;
+
+    zlrputs(c, patcols[0]);
+
+    curiscols[curiscol = 0] = *patcols++;
+
+    curisbeg = curissend = 0;
+
+    for (i = 0; i < nrefs; i++)
+	sendpos[i] = 0xfffffff;
+    for (; i < MAX_POS; i++)
+	begpos[i] = endpos[i] = sendpos[i] = 0xfffffff;
+}
+
+static void
+doiscol(Listcols c, int pos)
+{
+    int fi;
+
+    while (pos > sendpos[curissend]) {
+	curissend++;
+	if (curiscol) {
+	    zcputs(c, NULL, COL_NO);
+	    zlrputs(c, curiscols[--curiscol]);
+	}
+    }
+    while (((fi = (endpos[curisbeg] < begpos[curisbeg] || 
+		  begpos[curisbeg] == -1)) ||
+	    pos == begpos[curisbeg]) && *patcols) {
+	if (!fi) {
+	    int i, j, e = endpos[curisbeg];
+	    
+	    /* insert e in sendpos */
+	    for (i = curissend; sendpos[i] <= e; ++i)
+		;
+	    for (j = i + 1; j < MAX_POS; ++j)
+		sendpos[j] = sendpos[j-1];
+	    sendpos[i] = e;
+	    
+	    zcputs(c, NULL, COL_NO);
+	    zlrputs(c, *patcols);
+	    curiscols[++curiscol] = *patcols;
+	}
+	++patcols;
+	++curisbeg;
+    }
+}
+
+/* Stripped-down version of printfmt(). But can do in-string colouring. */
+
+static int
+clprintfmt(Listcols c, char *p, int ml)
+{
+    int cc = 0, i = 0, ask, beg;
+
+    initiscol(c);
+
+    for (; *p; p++) {
+	doiscol(c, i++);
+	cc++;
+	if (*p == '\n') {
+	    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+		tcout(TCCLEAREOL);
+	    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 (mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+    return 0;
+}
+
+/* Local version of nicezputs() with in-string colouring. */
+
+static int
+clnicezputs(Listcols c, char *s, int ml)
+{
+    int cc, i = 0, col = 0, ask;
+    char *t;
+
+    initiscol(c);
+
+    while ((cc = *s++)) {
+	doiscol(c, i++);
+	if (itok(cc)) {
+	    if (cc <= Comma)
+		cc = ztokens[cc - Pound];
+	    else 
+		continue;
+	}
+	if (cc == Meta)
+	    cc = *s++ ^ 32;
+
+	for (t = nicechar(cc); *t; t++) {
+	    if (ml == mlend - 1 && col == columns - 1)
+		return 0;
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
+		    return ask;
+
+		col = 0;
+	    }
+	}
+    }
+    return 0;
+}
+
+/* Get the terminal color string for the given match. */
+
+static int
+putmatchcol(Listcols c, char *group, char *n)
+{
+    Patcol pc;
+
+    nrefs = MAX_POS - 1;
+
+    for (pc = c->pats; pc; pc = pc->next)
+	if ((!pc->prog || !group || pattry(pc->prog, group)) &&
+	    pattryrefs(pc->pat, n, &nrefs, begpos, endpos)) {
+	    if (pc->cols[1]) {
+		patcols = pc->cols;
+
+		return 1;
+	    }
+	    zlrputs(c, pc->cols[0]);
+
+	    return 0;
+	}
+
+    zcputs(c, group, COL_NO);
+
     return 0;
 }
 
 /* Get the terminal color string for the file with the given name and
  * file modes. */
 
-static char *
-getcolstr(Listcols c, char *n, mode_t m)
+static int
+putfilecol(Listcols c, char *group, char *n, mode_t m)
 {
-    Extcol e;
+    int colour;
+    Extcol ec;
+    Patcol pc;
+
+    for (ec = c->exts; ec; ec = ec->next)
+	if (strsfx(ec->ext, n) &&
+	    (!ec->prog || !group || pattry(ec->prog, group))) {
+	    zlrputs(c, ec->col);
+
+	    return 0;
+	}
 
-    for (e = c->exts; e; e = e->next)
-	if (strsfx(e->ext, n))
-	    return e->col;
+    nrefs = MAX_POS - 1;
+
+    for (pc = c->pats; pc; pc = pc->next)
+	if ((!pc->prog || !group || pattry(pc->prog, group)) &&
+	    pattryrefs(pc->pat, n, &nrefs, begpos, endpos)) {
+	    if (pc->cols[1]) {
+		patcols = pc->cols;
+
+		return 1;
+	    }
+	    zlrputs(c, pc->cols[0]);
+
+	    return 0;
+	}
 
     if (S_ISDIR(m))
-	return c->cols[COL_DI];
+	colour = COL_DI;
     else if (S_ISLNK(m))
-	return c->cols[COL_LN];
+	colour = COL_LN;
     else if (S_ISFIFO(m))
-	return c->cols[COL_PI];
+	colour = COL_PI;
     else if (S_ISSOCK(m))
-	return c->cols[COL_SO];
+	colour = COL_SO;
     else if (S_ISBLK(m))
-	return c->cols[COL_BD];
+	colour = COL_BD;
     else if (S_ISCHR(m))
-	return c->cols[COL_CD];
+	colour = COL_CD;
     else if (S_ISREG(m) && (m & S_IXUGO))
-	return c->cols[COL_EX];
+	colour = COL_EX;
+    else
+	colour = COL_FI;
+
+    zcputs(c, group, colour);
 
-    return c->cols[COL_FI];
+    return 0;
 }
 
-/* Information about the list shown. */
+static Cmgroup last_group;
+
+/**/
+static int
+asklistscroll(int ml)
+{
+    int v, i;
+
+    compprintfmt(NULL, -1, 1, 1, ml, NULL);
 
-static int noselect, mselect, inselect, mcol, mline, mcols, mlines;
-static Cmatch *mmatch, **mtab;
-static Cmgroup mgroup, *mgtab;
+    fflush(shout);
+    zsetterm();
+    v = getzlequery(0);
+    settyinfo(&shttyinfo);
+    putc('\r', shout);
+    for (i = columns - 1; i--; )
+	putc(' ', shout);
 
-/* List the matches. Most of this is just taken from ilistmatches(),
- * of course. */
+    putc('\r', shout);
 
+    if (v == '\n' || v == '\r') {
+	mrestlines = 1;
+	return 0;
+    }
+    mrestlines = lines - 1;
+
+    if (v == ' ' || v == '\t')
+	return 0;
+    if (v != 'q')
+	ungetkey(v);
+
+    return 1;
+}
+
+#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
-complistmatches(Hookdef dummy, Chdata dat)
+compprintnl(int ml)
 {
-    Cmgroup amatches = dat->matches, g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0;
-    int of = isset(LISTTYPES);
-    int mc, ml = 0, cc, hasm = 0;
-    struct listcols col;
+    int ask;
 
-    if (minfo.asked == 2) {
-	showinglist = 0;
-	return (noselect = 1);
-    }
-    getcols(&col);
+    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+    putc('\n', shout);
 
-    /* Set the cursor below the prompt. */
-    if (inselect)
-	clearflag = 0;
-    trashzle();
-    showinglist = listshown = 0;
+    if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
+	return ask;
 
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
+    return 0;
+}
 
-    for (g = amatches; g; g = g->next) {
-	char **pp = g->ylist;
-	int nl = 0, l;
-
-	if (pp) {
-	    /* We have an ylist, lets see, if it contains newlines. */
-	    while (!nl && *pp)
-		nl = !!strchr(*pp++, '\n');
-
-	    pp = g->ylist;
-	    if (nl) {
-		/* Yup, there are newlines, count lines. */
-		char *nlptr, *sptr;
-
-		g->flags |= CGF_LINES;
-		noselect = 1;
-		while ((sptr = *pp)) {
-		    while (sptr && *sptr) {
-			nlines += (nlptr = strchr(sptr, '\n'))
-			    ? 1 + (nlptr-sptr)/columns
-			    : strlen(sptr)/columns;
-			sptr = nlptr ? nlptr+1 : NULL;
+/* 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))
+		return 0;
+	    cc = -1;
+	} else if (!(fmt = getsparam("LISTSTATUS")))
+	    fmt = "continue? ";
+    }
+    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 && n >= 0) {
+			sprintf(nbuf, "%d/%d", mselect, listdat.nlist);
+			sprintf(nc, "%-9s", nbuf);
+			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 && n >= 0) {
+			if (ml == listdat.nlines - 1)
+			    strcpy(nc, "Bottom");
+			else if (mlbeg || ml != n)
+			    sprintf(nc, "%d%%",
+				    ((ml + 1) * 100) / listdat.nlines);
+			else
+			    strcpy(nc, "Top");
+			m = 2;
 		    }
-		    nlines++;
-		    pp++;
+		    break;
 		}
-		nlines--;
-	    } else {
-		while (*pp) {
-		    if ((l = strlen(*pp)) > longest)
-			longest = l;
-		    nlist++;
-		    pp++;
+		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 {
-	    for (p = g->matches; (m = *p); p++) {
-		if (!(m->flags & CMF_NOLIST)) {
-		    if ((l = niceztrlen(m->str)) > longest)
-			longest = l;
-		    nlist++;
-		} else
-		    noselect = 1;
+	    if ((++cc == columns - 2 || *p == '\n') && stat)
+		dopr = 2;
+	    if (*p == '\n') {
+		if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL))
+		    tcout(TCCLEAREOL);
+		l += 1 + (cc / columns);
+		cc = 0;
 	    }
-	}
-	if ((e = g->expls)) {
-	    while (*e) {
-		if ((*e)->count)
-		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0);
-		e++;
+	    if (dopr == 1) {
+		if (ml == mlend - 1 && (cc % columns) == columns - 1) {
+		    dopr = 0;
+		    continue;
+		}
+		putc(*p, shout);
+		if ((beg = !(cc % columns)) && !stat)
+		    ml++;
+		if (mscroll && beg && !--mrestlines && (ask = asklistscroll(ml))) {
+		    *stop = 1;
+		    return l + (cc / columns);
+		}
 	    }
 	}
     }
-    longest += 2 + of;
-    if ((ncols = (columns + 1) / longest)) {
-	for (g = amatches; g; g = g->next)
-	    nlines += (g->lcount + ncols - 1) / ncols;
-    } else {
-	ncols = 1;
-	opl = 1;
-	for (g = amatches; g; g = g->next) {
-	    char **pp = g->ylist;
-
-	    if (pp) {
-		if (!(g->flags & CGF_LINES)) {
-		    while (*pp) {
-			nlines += 1 + (strlen(*pp) / columns);
-			pp++;
-		    }
-		}
-	    } else
-		for (p = g->matches; (m = *p); p++)
-		    if (!(m->flags & CMF_NOLIST))
-			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
+    if (dopr && mlbeg >= 0 && tccan(TCCLEAREOL))
+	tcout(TCCLEAREOL);
+
+    return 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;
+}
 
-    /* Maybe we have to ask if the user wants to see the list. */
-    if ((!minfo.cur || !minfo.asked) &&
-	((complistmax && nlist > complistmax) ||
-	 (!complistmax && nlines >= lines))) {
-	int qup;
-	zsetterm();
-	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
-	fflush(shout);
-	if (getzlequery() != 'y') {
-	    if (clearflag) {
-		putc('\r', shout);
-		tcmultout(TCUP, TCMULTUP, qup);
-		if (tccan(TCCLEAREOD))
-		    tcout(TCCLEAREOD);
-		tcmultout(TCUP, TCMULTUP, nlnct);
-	    } else
-		putc('\n', shout);
-	    noselect = 1;
-	    if (minfo.cur)
-		minfo.asked = 2;
-	    return 1;
+/* This is like nicezputs(), but allows scrolling. */
+
+/**/
+static int
+compnicezputs(char *s, int ml)
+{
+    int c, col = 0, ask;
+    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)
+		return 0;
+	    putc(*t, shout);
+	    if (++col == columns) {
+		ml++;
+		if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
+		    return ask;
+
+		col = 0;
+	    }
 	}
-	if (clearflag) {
-	    putc('\r', shout);
-	    tcmultout(TCUP, TCMULTUP, qup);
-	    if (tccan(TCCLEAREOD))
-		tcout(TCCLEAREOD);
-	} else
-	    putc('\n', shout);
-	settyinfo(&shttyinfo);
-	if (minfo.cur)
-	    minfo.asked = 1;
     }
-    if (mselect >= 0) {
-	int i;
+    return 0;
+}
 
-	i = ncols * nlines;
-	free(mtab);
-	mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
-	memset(mtab, 0, i * sizeof(Cmatch **));
-	free(mgtab);
-	mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
-	memset(mgtab, 0, i * sizeof(Cmgroup));
-	mcols = ncols;
-	mlines = nlines;
+/**/
+static int
+compprintlist(int showall)
+{
+    static int lasttype = 0, lastbeg = 0, lastml = 0;
+    static lastn = 0, lastnl = 0;
+    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, fl = -1;
+
+    if (mnew || lastbeg != mlbeg || mlbeg < 0) {
+	lasttype = 0;
+	lastg = NULL;
+	lastexpl = NULL;
+	lastml = 0;
     }
-    /* Now print the matches. */
-    g = amatches;
+    cl = (listdat.nlines > lines - nlnct - mhasstat ?
+	  lines - nlnct - mhasstat : listdat.nlines);
+    mrestlines = lines - 1;
+
+    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) {
 		    if (pnl) {
-			putc('\n', shout);
+			if (dolistnl(ml) && compprintnl(ml))
+			    goto end;
 			pnl = 0;
 			ml++;
+			if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    l = compprintfmt((*e)->str, (*e)->count, dolist(ml), 1,
+				     ml, &stop);
+		    if (stop)
+			goto end;
+		    if (!lasttype && ml >= mlbeg) {
+			lasttype = 1;
+			lastg = g;
+			lastbeg = mlbeg;
+			lastml = ml;
+			lastexpl = e;
+			lastp = NULL;
+			lastused = 1;
+		    }
+		    ml += l;
+		    if (dolistcl(ml) && cl >= 0 && (cl -= l) <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
 		    }
-		    ml += printfmt((*e)->str, (*e)->count, 1);
 		    pnl = 1;
 		}
 		e++;
+		if (!mnew && ml > mlend)
+		    goto end;
 	    }
 	}
-	if (pp && *pp) {
+	if (!listdat.onlyexpl && mlbeg < 0 && pp && *pp) {
 	    if (pnl) {
-		putc('\n', shout);
+		if (dolistnl(ml) && compprintnl(ml))
+		    goto end;
 		pnl = 0;
 		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
 	    }
 	    if (g->flags & CGF_LINES) {
 		while (*pp) {
-		    zputs(*pp, shout);
-		    if (*++pp)
-			putc('\n', shout);
+		    if (compzputs(*pp, ml))
+			goto end;
+		    if (*++pp && compprintnl(ml))
+			goto end;
 		}
 	    } else {
-		int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a;
+		int n = g->lcount, nl, nc, i, a;
 		char **pq;
 
+		nl = nc = g->lins;
+
 		while (n && nl--) {
-		    i = ncols;
+		    i = g->cols;
 		    mc = 0;
 		    pq = pp;
 		    while (n && i--) {
 			if (pq - g->ylist >= g->lcount)
 			    break;
-			zputs(*pq, shout);
+			if (compzputs(*pq, mscroll))
+			    goto end;
 			if (i) {
-			    a = longest - strlen(*pq);
+			    a = (g->widths ? g->widths[mc] : g->width) -
+				strlen(*pq);
 			    while (a--)
 				putc(' ', shout);
 			}
-			pq += nc;
+			pq += ((g->flags & CGF_ROWS) ? 1 : nc);
+			mc++;
 			n--;
 		    }
 		    if (n) {
-			putc('\n', shout);
+			if (compprintnl(ml))
+			    goto end;
 			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
 		    }
-		    pp++;
+		    pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
 		}
 	    }
-	} else if (g->lcount) {
-	    int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a = 0;
-	    int zt;
+	} 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;
+		} else
+		    p = g->matches;
+
+		for (; (m = *p); p++) {
+		    if (m->disp && (m->flags & CMF_DISPLINE)) {
+			if (!lasttype && ml >= mlbeg) {
+			    lasttype = 2;
+			    lastg = g;
+			    lastbeg = mlbeg;
+			    lastml = ml;
+			    lastp = p;
+			    lastn = n;
+			    lastnl = nl;
+			    lastused = 1;
+			}
+			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 (fl < 0)
+			    fl = ml;
+			if (dolist(ml))
+			    printed++;
+			if (clprintm(g, p, 0, ml, 1, 0, NULL, NULL))
+			    goto end;
+			pnl = 1;
+		    }
+		    if (!mnew && ml > mlend)
+			goto end;
+		}
+	    }
 	    if (n && pnl) {
-		putc('\n', shout);
+		if (dolistnl(ml) && compprintnl(ml))
+		    goto end;
 		pnl = 0;
 		ml++;
+		if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
 	    }
-	    for (p = skipnolist(g->matches); n && nl--;) {
-		i = ncols;
+	    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--) {
-		    fputs(col.cols[COL_LC], shout);
+		    wid = (g->widths ? g->widths[mc] : g->width);
 		    if (!(m = *q)) {
-			fputs(col.cols[COL_MI], shout);
-			fputs(col.cols[COL_RC], shout);
-			a = longest - 2;
-			while (a--)
-			    putc(' ', shout);
-			fputs(col.cols[COL_EC], shout);
+			if (clprintm(g, NULL, mc, ml, (!i), wid, NULL, NULL))
+			    goto end;
 			break;
 		    }
-		    hasm = 1;
-		    if (mselect >= 0) {
-			mtab[mc + (ncols * ml)] = q;
-			mgtab[mc + (ncols * ml)] = g;
-		    }
-		    if (m->gnum == mselect) {
-			mcol = mc;
-			mline = ml;
-			mmatch = q;
-			mgroup = g;
-			cc = COL_MA;
-		    } else
-			cc = -1;
-		    if (m->flags & CMF_FILE) {
+		    if (!m->disp && (m->flags & CMF_FILE)) {
 			struct stat buf;
 			char *pb;
 
@@ -537,112 +1164,419 @@ complistmatches(Hookdef dummy, Chdata dat)
 			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
 				m->str);
 
-			zt = ztat(pb, &buf, 1);
-			if (cc >= 0)
-			    fputs(col.cols[cc], shout);
-			else if (zt)
-			    fputs(col.cols[COL_NO], shout);
-			else
-			    fputs(getcolstr(&col, pb, buf.st_mode), shout);
-			fputs(col.cols[COL_RC], shout);
-			nicezputs(m->str, shout);
-			if (zt)
-			    putc(' ', shout);
-			else
-			    putc(file_type(buf.st_mode), shout);
-		    } else {
-			fputs(col.cols[cc >= 0 ? cc : COL_NO], shout);
-			fputs(col.cols[COL_RC], shout);
-			nicezputs(m->str, shout);
-			if (of)
-			    putc(' ', shout);
-		    }
-		    a = longest - niceztrlen(m->str) - 2 - of;
-		    while (a--)
-			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
-		    if (i) {
-			fputs(col.cols[COL_LC], shout);
-			fputs(col.cols[COL_NO], shout);
-			fputs(col.cols[COL_RC], shout);
-			fputs("  ", shout);
-			fputs(col.cols[COL_EC], shout);
-		    }
+			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++;
+		    if (fl < 0)
+			fl = ml;
+
 		    if (--n)
-			for (j = nc; j && *q; j--)
-			    q = skipnolist(q + 1);
+			for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
+			     j && *q; j--)
+			    q = skipnolist(q + 1, showall);
 		    mc++;
 		}
-		if (i > 0) {
-		    fputs(col.cols[COL_LC], shout);
-		    fputs(col.cols[COL_MI], shout);
-		    fputs(col.cols[COL_RC], shout);
-		    a = longest - 2;
-		    while (a--)
-			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
+		while (i-- > 0) {
+		    if (clprintm(g, NULL, mc, ml, (!i),
+				 (g->widths ? g->widths[mc] : g->width),
+				 NULL, NULL))
+			goto end;
+		    mc++;
 		}
 		if (n) {
-		    putc('\n', shout);
+		    if (dolistnl(ml) && compprintnl(ml))
+			goto end;
 		    ml++;
-		    if (n && nl)
-			p = skipnolist(p + 1);
+		    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)
+	if (g->lcount || (showall && g->mcount))
 	    pnl = 1;
 	g = g->next;
     }
-
+    asked = 0;
+ end:
+    lastlistlen = 0;
+    if (nlnct <= 1)
+	mscroll = 0;
     if (clearflag) {
 	/* Move the cursor up to the prompt, if always_last_prompt *
 	 * is set and all that...                                  */
-	if ((nlines += nlnct - 1) < lines) {
-	    tcmultout(TCUP, TCMULTUP, nlines);
+	if (mlbeg >= 0) {
+	    if ((ml = listdat.nlines + nlnct) >= lines) {
+		if (mhasstat) {
+		    putc('\n', shout);
+		    compprintfmt(NULL, fl, 1, 1, mline, NULL);
+		}
+		ml = lines - 1;
+	    } else
+		ml--;
+	    tcmultout(TCUP, TCMULTUP, ml);
+	    showinglist = -1;
+
+	    lastlistlen = listdat.nlines;
+	} else if ((ml = listdat.nlines + nlnct - 1) < lines) {
+	    if (mlbeg >= 0 && tccan(TCCLEAREOL))
+		tcout(TCCLEAREOL);
+	    tcmultout(TCUP, TCMULTUP, ml);
 	    showinglist = -1;
-	    listshown = 1;
+
+	    lastlistlen = listdat.nlines;
+	} else {
+	    clearflag = 0;
+	    if (!asked)
+		compprintnl(ml);
+	}
+    } else if (!asked)
+	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, stop = 0, ret = 0;
+
+    if (g != last_group)
+        *last_cap = '\0';
+
+    last_group = g;
+
+    if (!mp) {
+	if (dolist(ml)) {
+	    zcputs(&mcolors, g->name, COL_SP);
+	    len = width - 2;
+	    while (len-- > 0)
+		putc(' ', shout);
+	    zcoff();
+	}
+	return 0;
+    }
+    m = *mp;
+    if (m->disp && (m->flags & CMF_DISPLINE)) {
+	if (mselect >= 0) {
+	    int mm = (mcols * ml), i;
+
+	    for (i = mcols; i--; ) {
+		mtab[mm + i] = mp;
+		mgtab[mm + i] = g;
+	    }
+	}
+	if (!dolist(ml))
+	    return 0;
+	if (m->gnum == mselect) {
+	    int mm = (mcols * ml);
+	    mline = ml;
+	    mcol = 0;
+	    mmtabp = mtab + mm;
+	    mgtabp = mgtab + mm;
+	    mmlen = mcols;
+	    zcputs(&mcolors, g->name, COL_MA);
+	} else if (m->flags & CMF_NOLIST)
+	    zcputs(&mcolors, g->name, COL_HI);
+	else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)))
+	    zcputs(&mcolors, g->name, COL_DU);
+	else
+	    subcols = putmatchcol(&mcolors, g->name, m->disp);
+	if (subcols)
+	    ret = clprintfmt(&mcolors, m->disp, ml);
+	else {
+	    compprintfmt(m->disp, 0, 1, 0, ml, &stop);
+	    if (stop)
+		ret = 1;
+	}
+	zcoff();
+    } else {
+	int mx;
+
+	if (g->widths) {
+	    int i;
+
+	    for (i = mx = 0; i < mc; i++)
+		mx += g->widths[i];
 	} else
-	    clearflag = 0, putc('\n', shout);
+	    mx = mc * g->width;
+
+	if (mselect >= 0) {
+	    int mm = mcols * ml, i;
+
+	    for (i = (width ? width : mcols); i--; ) {
+		mtab[mx + mm + i] = mp;
+		mgtab[mx + mm + i] = g;
+	    }
+	}
+	if (!dolist(ml))
+	    return 0;
+	if (m->gnum == mselect) {
+	    int mm = mcols * ml;
+
+	    mcol = mx;
+	    mline = ml;
+	    mmtabp = mtab + mx + mm;
+	    mgtabp = mgtab + mx + mm;
+	    mmlen = width;
+	    zcputs(&mcolors, g->name, COL_MA);
+	} else if (m->flags & CMF_NOLIST)
+	    zcputs(&mcolors, g->name, COL_HI);
+	else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)))
+	    zcputs(&mcolors, g->name, COL_DU);
+	else if (buf)
+	    subcols = putfilecol(&mcolors, g->name, m->str, buf->st_mode);
+	else
+	    subcols = putmatchcol(&mcolors, g->name, (m->disp ? m->disp : m->str));
+
+	if (subcols)
+	    ret = clnicezputs(&mcolors, (m->disp ? m->disp : m->str), ml);
+	else
+	    ret = compnicezputs((m->disp ? m->disp : m->str), ml);
+	if (ret) {
+	    zcoff();
+	    return 1;
+	}
+	len = niceztrlen(m->disp ? m->disp : m->str);
+
+	 if (isset(LISTTYPES) && buf) {
+	    if (m->gnum != mselect) {
+		zcoff();
+		zcputs(&mcolors, g->name, COL_TC);
+	    }
+	    putc(file_type(buf->st_mode), shout);
+	    len++;
+        }
+	if ((len = width - len - 2) > 0) {
+	    if (m->gnum != mselect) {
+		zcoff();
+		zcputs(&mcolors, g->name, COL_SP);
+	    }
+	    while (len-- > 0)
+		putc(' ', shout);
+	}
+	zcoff();
+	if (!lastc) {
+	    zcputs(&mcolors, g->name, COL_SP);
+	    fputs("  ", shout);
+	    zcoff();
+	}
+    }
+    return ret;
+}
+
+static int
+complistmatches(Hookdef dummy, Chdata dat)
+{
+    Cmgroup oamatches = amatches;
+    char *p = NULL;
+
+    amatches = dat->matches;
+
+    if ((minfo.asked == 2 && mselect < 0) || nlnct >= lines) {
+	showinglist = 0;
+	amatches = oamatches;
+	return (noselect = 1);
+    }
+    getcols(&mcolors);
+
+    mnew = ((calclist(mselect >= 0) || mlastcols != columns ||
+	     mlastlines != listdat.nlines) && mselect >= 0);
+
+    if (!listdat.nlines || (mselect >= 0 &&
+			    !(isset(USEZLE) && !termflags &&
+			      complastprompt && *complastprompt))) {
+	showinglist = listshown = 0;
+	noselect = 1;
+	amatches = oamatches;
+	return 1;
+    }
+    if (inselect)
+	clearflag = 0;
+
+    mscroll = 0;
+
+    if (mselect >= 0 || mlbeg >= 0 ||
+	((p = getsparam("LISTMAX")) && !strcmp(p, "scroll"))) {
+	trashzle();
+	showinglist = listshown = 0;
+
+	lastlistlen = 0;
+
+	if (p) {
+	    clearflag = (isset(USEZLE) && !termflags && dolastprompt);
+	    mscroll = 1;
+	} else {
+	    clearflag = 1;
+	    minfo.asked = (listdat.nlines + nlnct <= lines);
+	}
+    } else if (asklist()) {
+	amatches = oamatches;
+	return (noselect = 1);
+    }
+    if (mlbeg >= 0) {
+	mlend = mlbeg + lines - nlnct - mhasstat;
+	while (mline >= mlend)
+	    mlbeg++, mlend++;
     } else
-	putc('\n', shout);
-    if (!hasm || nlines >= lines)
+	mlend = 9999999;
+
+    if (mnew) {
+	int i;
+
+	i = columns * listdat.nlines;
+	free(mtab);
+	mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
+	memset(mtab, 0, i * sizeof(Cmatch **));
+	free(mgtab);
+	mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
+	memset(mgtab, 0, i * sizeof(Cmgroup));
+	mlastcols = mcols = columns;
+	mlastlines = mlines = listdat.nlines;
+    }
+    last_cap = (char *) zhalloc(max_caplen + 1);
+    *last_cap = '\0';
+
+    if (!compprintlist(mselect >= 0) || !clearflag)
 	noselect = 1;
+
+    amatches = oamatches;
+
     return noselect;
 }
 
+static int
+adjust_mcol(int wish, Cmatch ***tabp, Cmgroup **grp)
+{
+    Cmatch **tab = *tabp;
+    int p, n, c;
+
+    tab -= mcol;
+
+    for (p = wish; p >= 0 && !tab[p]; p--);
+    for (n = wish; n < mcols && !tab[n]; n++);
+    if (n == mcols)
+	n = -1;
+
+    if (p < 0) {
+	if (n < 0)
+	    return 1;
+	c = n;
+    } else if (n < 0)
+	c = p;
+    else
+	c = ((mcol - p) < (n - mcol) ? p : n);
+
+    *tabp = tab + c;
+    if (grp)
+	*grp = *grp + c - mcol;
+
+    mcol = c;
+    
+    return 0;
+}
+
 typedef struct menustack *Menustack;
 
 struct menustack {
     Menustack prev;
     char *line;
-    int cs;
+    Brinfo brbeg;
+    Brinfo brend;
+    int nbrbeg, nbrend;
+    int cs, acc, nmatches, mline, mlbeg;
     struct menuinfo info;
+    Cmgroup amatches, pmatches, lastmatches, lastlmatches;
 };
 
 static int
 domenuselect(Hookdef dummy, Chdata dat)
 {
+    static Chdata fdat = NULL;
     Cmatch **p;
     Cmgroup *pg;
     Thingy cmd;
     Menustack u = NULL;
-    int i = 0;
+    int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0;
+    int space, lbeg = 0, step = 1;
     char *s;
 
-    if (getcols(NULL) || (dummy && (!(s = getsparam("SELECTMIN")) ||
-				    (dat && dat->num < atoi(s)))))
-	return 1;
-
+    if (fdat || (dummy && (!(s = getsparam("SELECTMIN")) ||
+			   (dat && dat->num < atoi(s))))) {
+	if (fdat) {
+	    fdat->matches = dat->matches;
+	    fdat->num = dat->num;
+	}
+	return 0;
+    }
+    if ((s = getsparam("SELECTSCROLL"))) {
+	if (!(step = mathevali(s)))
+	    step = (lines - nlnct) >> 1;
+	else if (step < 0)
+	    if ((step += lines - nlnct) < 0)
+		step = 1;
+    }
+    mstatus = getsparam("SELECTSTATUS");
+    mhasstat = !!mstatus;
+    fdat = dat;
     selectlocalmap(mskeymap);
     noselect = 0;
     mselect = (*(minfo.cur))->gnum;
+    mline = 0;
+    mlines = 999999;
+    mlbeg = 0;
     for (;;) {
+	space = lines - nlnct - mhasstat;
+	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)
+			break;
+		if (c)
+		    break;
+		p -= columns;
+		mlbeg--;
+	    }
+	}
+	while (mline >= mlbeg + space)
+	    if ((mlbeg += step) + space > mlines)
+		mlbeg = mlines - space;
+
+	lbeg = mlbeg;
+	onlyexpl = 0;
 	showinglist = -2;
 	zrefresh();
 	inselect = 1;
 	if (noselect)
 	    break;
+	selected = 1;
 	if (!i) {
 	    i = mcols * mlines;
 	    while (i--)
@@ -652,28 +1586,107 @@ domenuselect(Hookdef dummy, Chdata dat)
 		break;
 	    i = 1;
 	}
-	p = mtab + mcol + (mline * mcols);
-	pg = mgtab + mcol + (mline * mcols);
+	p = mmtabp;
+	pg = mgtabp;
 	minfo.cur = *p;
 	minfo.group = *pg;
+	if (setwish)
+	    wishcol = mcol;
+	else if (mcol > wishcol) {
+	    while (mcol > 0 && p[-1] == minfo.cur)
+		mcol--, p--, pg--;
+	} else if (mcol < wishcol) {
+	    while (mcol < mcols - 1 && p[1] == minfo.cur)
+		mcol++, p++, pg++;
+	}
+	setwish = wasnext = 0;
 
     getk:
 
-	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak) ||
-	    cmd == Th(z_acceptline))
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
+	    break;
+	else if (cmd == Th(z_acceptline)) {
+	    acc = 1;
 	    break;
-	else if (cmd == Th(z_acceptandhold) ||
-		 cmd == Th(z_acceptandmenucomplete)) {
+	} else if (cmd == Th(z_acceptandinfernexthistory)) {
 	    Menustack s = (Menustack) zhalloc(sizeof(*s));
 
 	    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));
-	    acceptlast();
+	    s->amatches = amatches;
+	    s->pmatches = pmatches;
+	    s->lastmatches = lastmatches;
+	    s->lastlmatches = lastlmatches;
+	    s->acc = menuacc;
+	    s->brbeg = dupbrinfo(brbeg, NULL, 1);
+	    s->brend = dupbrinfo(brend, NULL, 1);
+	    s->nbrbeg = nbrbeg;
+	    s->nbrend = nbrend;
+	    s->nmatches = nmatches;
+	    menucmp = menuacc = hasoldlist = 0;
+	    fixsuffix();
+	    validlist = 0;
+	    amatches = pmatches = lastmatches = NULL;
+	    invalidate_list();
+	    menucomplete(zlenoargs);
+	    if (dat->num < 2 || !minfo.cur || !*(minfo.cur)) {
+		noselect = clearlist = listshown = 1;
+		onlyexpl = 0;
+		zrefresh();
+		break;
+	    }
+	    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;
+	    s->acc = menuacc;
+	    s->brbeg = dupbrinfo(brbeg, NULL, 1);
+	    s->brend = dupbrinfo(brend, NULL, 1);
+	    s->nbrbeg = nbrbeg;
+	    s->nbrend = nbrend;
+	    s->nmatches = nmatches;
+	    accept_last();
 	    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;
+		mline++;
+	    } 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;
@@ -686,9 +1699,32 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    spaceinline(l = strlen(u->line));
 	    strncpy((char *) line, u->line, l);
 	    cs = u->cs;
+	    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);
+		amatches = u->amatches;
+		pmatches = u->pmatches;
+		lastmatches = u->lastmatches;
+		lastlmatches = u->lastlmatches;
+		nmatches = u->nmatches;
+		hasoldlist = 1;
+	    }
+	    freebrinfo(brbeg);
+	    freebrinfo(brend);
+	    brbeg = dupbrinfo(u->brbeg, &lastbrbeg, 0);
+	    brend = dupbrinfo(u->brend, &lastbrend, 0);
+	    nbrbeg = u->nbrbeg;
+	    nbrend = u->nbrend;
+
 	    u = u->prev;
+	    clearlist = 1;
+	    setwish = 1;
+	    listdat.valid = 0;
 	} else if (cmd == Th(z_redisplay)) {
 	    redisplay(zlenoargs);
 	    continue;
@@ -707,6 +1743,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    mline++;
 		    p += mcols;
 		}
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
 	    } while (!*p);
 	} else if (cmd == Th(z_uphistory) ||
 		   cmd == Th(z_uplineorhistory) ||
@@ -720,8 +1758,104 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    mline--;
 		    p -= mcols;
 		}
+		if (adjust_mcol(wishcol, &p, NULL))
+		    continue;
 	    } while (!*p);
+	} else if (cmd == Th(z_emacsforwardword) ||
+		   cmd == Th(z_viforwardword) ||
+		   cmd == Th(z_viforwardwordend) ||
+		   cmd == Th(z_forwardword)) {
+	    int i = lines - nlnct - 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) {
+		    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 - nlnct - 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;
+		if (*p) {
+		    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) {
+		    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) {
+		    lp = p;
+		    ll = mline;
+		}
+	    }
+	    mline = ll;
+	    p = lp;
 	} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
+	    int omcol = mcol;
+	    Cmatch *op = *p;
+
 	    do {
 		if (mcol == mcols - 1) {
 		    p -= mcol;
@@ -730,8 +1864,12 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    mcol++;
 		    p++;
 		}
-	    } while (!*p);
+	    } while (!*p || (mcol != omcol && *p == op));
+	    wishcol = mcol;
 	} else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
+	    int omcol = mcol;
+	    Cmatch *op = *p;
+
 	    do {
 		if (!mcol) {
 		    mcol = mcols - 1;
@@ -740,7 +1878,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    mcol--;
 		    p--;
 		}
-	    } while (!*p);
+	    } while (!*p || (mcol != omcol && *p == op));
+	    wishcol = mcol;
 	} else if (cmd == Th(z_beginningofbufferorhistory) ||
 		   cmd == Th(z_beginningofline) ||
 		   cmd == Th(z_beginningoflinehist) ||
@@ -751,6 +1890,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		mcol++;
 		p++;
 	    }
+	    wishcol = 0;
 	} else if (cmd == Th(z_endofbufferorhistory) ||
 		   cmd == Th(z_endofline) ||
 		   cmd == Th(z_endoflinehist) ||
@@ -761,10 +1901,9 @@ domenuselect(Hookdef dummy, Chdata dat)
 		mcol--;
 		p--;
 	    }
-	} else if (cmd == Th(z_forwardword) ||
-		   cmd == Th(z_emacsforwardword) ||
-		   cmd == Th(z_viforwardword) ||
-		   cmd == Th(z_viforwardwordend)) {
+	    wishcol = mcols - 1;
+	} else if (cmd == Th(z_viforwardblankword) ||
+		   cmd == Th(z_viforwardblankwordend)) {
 	    Cmgroup g = *pg;
 	    int ol = mline;
 
@@ -778,10 +1917,10 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    p += mcols;
 		    pg += mcols;
 		}
+		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)) {
+	} else if (cmd == Th(z_vibackwardblankword)) {
 	    Cmgroup g = *pg;
 	    int ol = mline;
 
@@ -795,6 +1934,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    p -= mcols;
 		    pg -= mcols;
 		}
+		if (adjust_mcol(wishcol, &p, &pg))
+		    continue;
 	    } while (ol != mline && (*pg == g || !*pg));
 	} else if (cmd == Th(z_completeword) ||
 		   cmd == Th(z_expandorcomplete) ||
@@ -809,11 +1950,13 @@ domenuselect(Hookdef dummy, Chdata dat)
 		   !strcmp(cmd->nam, "menu-expand-or-complete")) {
 	    do_menucmp(0);
 	    mselect = (*(minfo.cur))->gnum;
+	    setwish = 1;
 	    continue;
 	} else if (cmd == Th(z_reversemenucomplete) ||
 		   !strcmp(cmd->nam, "reverse-menu-complete")) {
 	    reversemenucomplete(zlenoargs);
 	    mselect = (*(minfo.cur))->gnum;
+	    setwish = 1;
 	    continue;
 	} else {
 	    ungetkeycmd();
@@ -822,14 +1965,35 @@ domenuselect(Hookdef dummy, Chdata dat)
 	do_single(**p);
 	mselect = (**p)->gnum;
     }
+    if (u)
+	for (; u; u = u->prev)
+	    if (u->lastmatches != lastmatches)
+		freematches(u->lastmatches);
+
     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) {
+	menucmp = 2;
+	showinglist = -2;
+	minfo.asked = 0;
+    }
     if (!noselect) {
 	showinglist = -2;
+	onlyexpl = oe;
+	if (!smatches)
+	    clearlist = 1;
 	zrefresh();
     }
-    return noselect;
+    mlbeg = -1;
+    fdat = NULL;
+
+    return (!noselect ^ acc);
 }
 
 /* The widget function. */
@@ -840,8 +2004,9 @@ menuselect(char **args)
     int d = 0;
 
     if (!minfo.cur) {
+	selected = 0;
 	menucomplete(args);
-	if ((minfo.cur && minfo.asked == 2) || getsparam("ZLS_SELECT"))
+	if ((minfo.cur && minfo.asked == 2) || selected)
 	    return 0;
 	d = 1;
     }
@@ -853,14 +2018,14 @@ menuselect(char **args)
 
 /**/
 int
-setup_complist(Module m)
+setup_(Module m)
 {
     return 0;
 }
 
 /**/
 int
-boot_complist(Module m)
+boot_(Module m)
 {
     mtab = NULL;
     mgtab = NULL;
@@ -874,7 +2039,7 @@ boot_complist(Module m)
 		 NULL, 0);
 	return -1;
     }
-    addhookfunc("list_matches", (Hookfn) complistmatches);
+    addhookfunc("comp_list_matches", (Hookfn) complistmatches);
     addhookfunc("menu_start", (Hookfn) domenuselect);
     mskeymap = newkeymap(NULL, "menuselect");
     linkkeymap(mskeymap, "menuselect", 1);
@@ -892,17 +2057,15 @@ boot_complist(Module m)
     return 0;
 }
 
-#ifdef MODULE
-
 /**/
 int
-cleanup_complist(Module m)
+cleanup_(Module m)
 {
     free(mtab);
     free(mgtab);
 
     deletezlefunction(w_menuselect);
-    deletehookfunc("list_matches", (Hookfn) complistmatches);
+    deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
     deletehookfunc("menu_start", (Hookfn) domenuselect);
     unlinkkeymap("menuselect", 1);
     return 0;
@@ -910,9 +2073,7 @@ cleanup_complist(Module m)
 
 /**/
 int
-finish_complist(Module m)
+finish_(Module m)
 {
     return 0;
 }
-
-#endif
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index e56110a70..87e1f7278 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1157,7 +1157,7 @@ skipnolist(Cmatch *p, int showall)
 }
 
 /**/
-mod_export void
+mod_export int
 calclist(int showall)
 {
     Cmgroup g;
@@ -1170,7 +1170,7 @@ calclist(int showall)
     if (listdat.valid && onlyexpl == listdat.onlyexpl &&
 	menuacc == listdat.menuacc && showall == listdat.showall &&
 	lines == listdat.lines && columns == listdat.columns)
-	return;
+	return 0;
 
     for (g = amatches; g; g = g->next) {
 	char **pp = g->ylist;
@@ -1572,11 +1572,16 @@ calclist(int showall)
     listdat.columns = columns;
     listdat.lines = lines;
     listdat.showall = showall;
+
+    return 1;
 }
 
 /**/
-mod_export int asklist(void)
+mod_export int
+asklist(void)
 {
+    int lmax = (complistmax ? (int) mathevali(complistmax) : 0);
+
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = listshown = 0;
@@ -1586,9 +1591,9 @@ mod_export int asklist(void)
 
     /* Maybe we have to ask if the user wants to see the list. */
     if ((!minfo.cur || !minfo.asked) &&
-	((complistmax > 0 && listdat.nlist >= complistmax) ||
-	 (complistmax < 0 && listdat.nlines <= -complistmax) ||
-	 (!complistmax && listdat.nlines >= lines))) {
+	((lmax > 0 && listdat.nlist >= lmax) ||
+	 (lmax < 0 && listdat.nlines <= -lmax) ||
+	 (!lmax && listdat.nlines >= lines))) {
 	int qup, l;
 
 	zsetterm();
@@ -1599,7 +1604,7 @@ mod_export int asklist(void)
 		     listdat.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);
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 51a77b224..f18babbbd 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -1997,7 +1997,7 @@ listlist(LinkList l)
 	     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);
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 51af32e0b..ece9a23ae 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -277,6 +277,7 @@ hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens)
  * question is assumed to have been printed already, and the    *
  * cursor is left immediately after the response echoed.        *
  * (Might cause a problem if this takes it onto the next line.) *
+ * If yesno is non-zero:                                        *
  * <Tab> is interpreted as 'y'; any other control character is  *
  * interpreted as 'n'.  If there are any characters in the      *
  * buffer, this is taken as a negative response, and no         *
@@ -284,31 +285,35 @@ hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens)
 
 /**/
 mod_export int
-getzlequery(void)
+getzlequery(int yesno)
 {
     int c;
 #ifdef FIONREAD
     int val;
 
-    /* check for typeahead, which is treated as a negative response */
-    ioctl(SHTTY, FIONREAD, (char *)&val);
-    if (val) {
-	putc('n', shout);
-	return 'n';
+    if (yesno) {
+	/* check for typeahead, which is treated as a negative response */
+	ioctl(SHTTY, FIONREAD, (char *)&val);
+	if (val) {
+	    putc('n', shout);
+	    return 'n';
+	}
     }
 #endif
 
     /* get a character from the tty and interpret it */
     c = getkey(0);
-    if (c == '\t')
-	c = 'y';
-    else if (icntrl(c) || c == EOF)
-	c = 'n';
-    else
-	c = tulower(c);
-
+    if (yesno) {
+	if (c == '\t')
+	    c = 'y';
+	else if (icntrl(c) || c == EOF)
+	    c = 'n';
+	else
+	    c = tulower(c);
+    }
     /* echo response and return */
-    putc(c, shout);
+    if (c != '\n')
+	putc(c, shout);
     return c;
 }
 
diff --git a/Src/params.c b/Src/params.c
index 74c4ee1c8..79bc5e9a9 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -483,10 +483,10 @@ createparamtable(void)
     setiparam("MAILCHECK", 60);
     setiparam("LOGCHECK", 60);
     setiparam("KEYTIMEOUT", 40);
-    setiparam("LISTMAX", 100);
 #ifdef HAVE_SELECT
     setiparam("BAUD", getbaudrate(&shttyinfo));  /* get the output baudrate */
 #endif
+    setsparam("LISTMAX", ztrdup("100"));
     setsparam("FCEDIT", ztrdup(DEFAULT_FCEDIT));
     setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX));
     setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT));