summaryrefslogtreecommitdiff
path: root/Src/Zle/compresult.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/compresult.c')
-rw-r--r--Src/Zle/compresult.c1940
1 files changed, 1940 insertions, 0 deletions
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
new file mode 100644
index 000000000..fe997b12b
--- /dev/null
+++ b/Src/Zle/compresult.c
@@ -0,0 +1,1940 @@
+/*
+ * compresult.c - the complete module, completion result handling
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compresult.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above. */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+#define inststr(X) inststrlen((X),1,-1)
+
+/* This cuts the cline list before the stuff that isn't worth
+ * inserting in the line. */
+
+/**/
+static Cline
+cut_cline(Cline l)
+{
+ Cline q, p, e = NULL, maxp = NULL;
+ int sum = 0, max = 0, tmp, ls = 0;
+
+ /* If no match was added with matching, we don't really know
+ * which parts of the unambiguous string are worth keeping,
+ * so for now we keep everything (in the hope that this
+ * produces a string containing at least everything that was
+ * originally on the line). */
+
+ if (!hasmatched) {
+ cline_setlens(l, 0);
+ return l;
+ }
+ e = l = cp_cline(l, 0);
+
+ /* First, search the last struct for which we have something on
+ * the line. Anything before that is kept. */
+
+ for (q = NULL, p = l; p; p = p->next) {
+ if (p->orig || p->olen || !(p->flags & CLF_NEW))
+ e = p->next;
+ if (!p->suffix && (p->wlen || p->llen || p->prefix))
+ q = p;
+ }
+ if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) &&
+ !(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) {
+ q->word = q->line = NULL;
+ q->wlen = q->llen = 0;
+ }
+ /* Then keep all structs without missing characters. */
+
+ while (e && !(e->flags & CLF_MISS))
+ e = e->next;
+
+ if (e) {
+ /* Then we see if there is another struct with missing
+ * characters. If not, we keep the whole list. */
+
+ for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
+
+ if (p) {
+ for (p = e; p; p = p->next) {
+ if (!(p->flags & CLF_MISS))
+ sum += p->max;
+ else {
+ tmp = cline_sublen(p);
+ if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
+ sum += tmp - (p->max - tmp);
+ else if (tmp < p->min)
+ sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
+ }
+ if (sum > max) {
+ max = sum;
+ maxp = p;
+ }
+ }
+ if (max)
+ e = maxp;
+ else {
+ int len = 0;
+
+ cline_setlens(l, 0);
+ ls = 1;
+
+ for (p = e; p; p = p->next)
+ len += p->max;
+
+ if (len > ((minmlen << 1) / 3))
+ return l;
+ }
+ e->line = e->word = NULL;
+ e->llen = e->wlen = e->olen = 0;
+ e->next = NULL;
+ }
+ }
+ if (!ls)
+ cline_setlens(l, 0);
+
+ return l;
+}
+
+/* This builds the unambiguous string. If ins is non-zero, it is
+ * immediatly inserted in the line. Otherwise csp is used to return
+ * the relative cursor position in the string returned. */
+
+/**/
+static char *
+cline_str(Cline l, int ins, int *csp)
+{
+ Cline s;
+ int ocs = cs, ncs, pcs, scs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
+ int i, j, li = 0, cbr;
+ Brinfo brp, brs;
+
+ l = cut_cline(l);
+
+ pmm = smm = dm = 0;
+ pm = pmax = sm = smax = d = mid = cbr = -1;
+
+ /* Get the information about the brace beginning and end we have
+ * to re-insert. */
+ if (ins) {
+ Brinfo bp;
+ int olen = we - wb;
+
+ if ((brp = brbeg)) {
+ for (bp = brbeg; bp; bp = bp->next) {
+ bp->curpos = (hasunqu ? bp->pos : bp->qpos);
+ olen -= strlen(bp->str);
+ }
+ }
+ if ((brs = lastbrend)) {
+ for (bp = brend; bp; bp = bp->next)
+ olen -= strlen(bp->str);
+
+ for (bp = brend; bp; bp = bp->next)
+ bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
+ }
+ while (brp && !brp->curpos) {
+ inststrlen(brp->str, 1, -1);
+ brp = brp->next;
+ }
+ while (brs && !brs->curpos) {
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, -1);
+ brs = brs->prev;
+ }
+ }
+ /* Walk through the top-level cline list. */
+ while (l) {
+ /* Insert the original string if no prefix. */
+ if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
+ pcs = cs + l->olen;
+ inststrlen(l->orig, 1, l->olen);
+ } else {
+ /* Otherwise insert the prefix. */
+ for (s = l->prefix; s; s = s->next) {
+ pcs = cs + s->llen;
+ if (s->flags & CLF_LINE)
+ inststrlen(s->line, 1, s->llen);
+ else
+ inststrlen(s->word, 1, s->wlen);
+ scs = cs;
+
+ if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
+ d = cs; dm = s->flags & CLF_MATCHED;
+ }
+ li += s->llen;
+ }
+ }
+ if (ins) {
+ int ocs, bl;
+
+ while (brp && li >= brp->curpos) {
+ ocs = cs;
+ bl = strlen(brp->str);
+ cs = pcs - (li - brp->curpos);
+ inststrlen(brp->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ scs += bl;
+ brp = brp->next;
+ }
+ }
+ /* Remember the position if this is the first prefix with
+ * missing characters. */
+ if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
+ ((pmax < (l->min - l->max) && (!pmm || (l->flags & CLF_MATCHED))) ||
+ ((l->flags & CLF_MATCHED) && !pmm))) {
+ pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
+ }
+ if (ins) {
+ int ocs, bl;
+
+ while (brs && li >= brs->curpos) {
+ ocs = cs;
+ bl = strlen(brs->str);
+ cs = scs - (li - brs->curpos);
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ brs = brs->prev;
+ }
+ }
+ pcs = cs;
+ /* Insert the anchor. */
+ if (l->flags & CLF_LINE)
+ inststrlen(l->line, 1, l->llen);
+ else
+ inststrlen(l->word, 1, l->wlen);
+ scs = cs;
+ if (ins) {
+ int ocs, bl;
+
+ li += l->llen;
+
+ while (brp && li >= brp->curpos) {
+ ocs = cs;
+ bl = strlen(brp->str);
+ cs = pcs + l->llen - (li - brp->curpos);
+ inststrlen(brp->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ scs += bl;
+ brp = brp->next;
+ }
+ }
+ /* Remember the cursor position for suffixes and mids. */
+ if (l->flags & CLF_MISS) {
+ if (l->flags & CLF_MID)
+ mid = cs;
+ else if ((l->flags & CLF_SUF) &&
+ ((smax < (l->min - l->max) &&
+ (!smm || (l->flags & CLF_MATCHED))) ||
+ ((l->flags & CLF_MATCHED) && !smm))) {
+ sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
+ }
+ }
+ if (ins) {
+ int ocs, bl;
+
+ while (brs && li >= brs->curpos) {
+ ocs = cs;
+ bl = strlen(brs->str);
+ cs = scs - (li - brs->curpos);
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ brs = brs->prev;
+ }
+ }
+ /* And now insert the suffix or the original string. */
+ if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
+ pcs = cs;
+ inststrlen(l->orig, 1, l->olen);
+ if (ins) {
+ int ocs, bl;
+
+ li += l->olen;
+
+ while (brp && li >= brp->curpos) {
+ ocs = cs;
+ bl = strlen(brp->str);
+ cs = pcs + l->olen - (li - brp->curpos);
+ inststrlen(brp->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ brp = brp->next;
+ }
+ while (brs && li >= brs->curpos) {
+ ocs = cs;
+ bl = strlen(brs->str);
+ cs = pcs + l->olen - (li - brs->curpos);
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ brs = brs->prev;
+ }
+ }
+ } else {
+ Cline js = NULL;
+
+ for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
+ if (j < 0 && (s->flags & CLF_DIFF))
+ j = i, js = s;
+ pcs = cs;
+ if (s->flags & CLF_LINE) {
+ inststrlen(s->line, 0, s->llen);
+ i += s->llen; scs = cs + s->llen;
+ } else {
+ inststrlen(s->word, 0, s->wlen);
+ i += s->wlen; scs = cs + s->wlen;
+ }
+ if (ins) {
+ int ocs, bl;
+
+ li += s->llen;
+
+ while (brp && li >= brp->curpos) {
+ ocs = cs;
+ bl = strlen(brp->str);
+ cs = pcs + (li - brp->curpos);
+ inststrlen(brp->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ scs += bl;
+ brp = brp->next;
+ }
+ while (brs && li >= brs->curpos) {
+ ocs = cs;
+ bl = strlen(brs->str);
+ cs = scs - (li - brs->curpos);
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, bl);
+ cs = ocs + bl;
+ pcs += bl;
+ brs = brs->prev;
+ }
+ }
+ }
+ cs += i;
+ if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
+ d = cs - j; dm = js->flags & CLF_MATCHED;
+ }
+ }
+ l = l->next;
+ }
+ if (ins) {
+ int ocs = cs;
+
+ for (; brp; brp = brp->next)
+ inststrlen(brp->str, 1, -1);
+ for (; brs; brs = brs->prev) {
+ if (cbr < 0)
+ cbr = cs;
+ inststrlen(brs->str, 1, -1);
+ }
+ if (mid >= ocs)
+ mid += cs - ocs;
+ if (pm >= ocs)
+ pm += cs - ocs;
+ if (sm >= ocs)
+ sm += cs - ocs;
+ if (d >= ocs)
+ d += cs - ocs;
+ }
+ /* This calculates the new cursor position. If we had a mid cline
+ * with missing characters, we take this, otherwise if we have a
+ * prefix with missing characters, we take that, the same for a
+ * suffix, and finally a place where the matches differ. */
+ ncs = (cbr >= 0 ? cbr :
+ (mid >= 0 ? mid :
+ (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
+
+ if (!ins) {
+ /* We always inserted the string in the line. If that was not
+ * requested, we copy it and remove from the line. */
+ char *r = zalloc((i = cs - ocs) + 1);
+
+ memcpy(r, (char *) (line + ocs), i);
+ r[i] = '\0';
+ cs = ocs;
+ foredel(i);
+
+ *csp = ncs - ocs;
+
+ return r;
+ }
+ lastend = cs;
+ cs = ncs;
+
+ return NULL;
+}
+
+/* This is a utility function using the function above to allow access
+ * to the unambiguous string and cursor position via compstate. */
+
+/**/
+char *
+unambig_data(int *cp)
+{
+ static char *scache = NULL;
+ static int ccache;
+
+ if (mnum && ainfo) {
+ if (mnum != unambig_mnum) {
+ zsfree(scache);
+ scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
+ 0, &ccache);
+ }
+ } else if (mnum != unambig_mnum || !ainfo || !scache) {
+ zsfree(scache);
+ scache = ztrdup("");
+ ccache = 0;
+ }
+ unambig_mnum = mnum;
+ if (cp)
+ *cp = ccache + 1;
+
+ return scache;
+}
+
+/* Insert the given match. This returns the number of characters inserted.
+ * scs is used to return the position where a automatically created suffix
+ * has to be inserted. */
+
+/**/
+static int
+instmatch(Cmatch m, int *scs)
+{
+ int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
+ Brinfo bp;
+
+ zsfree(lastprebr);
+ zsfree(lastpostbr);
+ lastprebr = lastpostbr = NULL;
+
+ /* Ignored prefix. */
+ if (m->ipre) {
+ char *p = m->ipre + (menuacc ? m->qipl : 0);
+
+ inststrlen(p, 1, (l = strlen(p)));
+ r += l;
+ }
+ /* -P prefix. */
+ if (m->pre) {
+ inststrlen(m->pre, 1, (l = strlen(m->pre)));
+ r += l;
+ }
+ /* Path prefix. */
+ if (m->ppre) {
+ inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
+ r += l;
+ }
+ /* The string itself. */
+ inststrlen(m->str, 1, (l = strlen(m->str)));
+ r += l;
+ ocs = cs;
+ /* Re-insert the brace beginnings, if any. */
+ if (brbeg) {
+ int pcs = cs;
+
+ l = 0;
+ for (bp = brbeg, brpos = m->brpl,
+ bradd = (m->pre ? strlen(m->pre) : 0);
+ bp; bp = bp->next, brpos++) {
+ cs = a + *brpos + bradd;
+ pcs = cs;
+ l = strlen(bp->str);
+ bradd += l;
+ brpcs = cs;
+ inststrlen(bp->str, 1, l);
+ r += l;
+ ocs += l;
+ }
+ lastprebr = (char *) zalloc(pcs - a + 1);
+ memcpy(lastprebr, (char *) line + a, pcs - a);
+ lastprebr[pcs - a] = '\0';
+ cs = ocs;
+ }
+ /* Path suffix. */
+ if (m->psuf) {
+ inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
+ r += l;
+ }
+ /* Re-insert the brace end. */
+ if (brend) {
+ a = cs;
+ for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
+ cs = a - *brpos;
+ ocs = brscs = cs;
+ l = strlen(bp->str);
+ bradd += l;
+ inststrlen(bp->str, 1, l);
+ brb = cs;
+ r += l;
+ }
+ cs = a + bradd;
+ if (scs)
+ *scs = ocs;
+ } else {
+ brscs = -1;
+
+ if (scs)
+ *scs = cs;
+ }
+ /* -S suffix */
+ if (m->suf) {
+ inststrlen(m->suf, 1, (l = strlen(m->suf)));
+ r += l;
+ }
+ /* ignored suffix */
+ if (m->isuf) {
+ inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
+ r += l;
+ }
+ if (brend) {
+ lastpostbr = (char *) zalloc(cs - brb + 1);
+ memcpy(lastpostbr, (char *) line + brb, cs - brb);
+ lastpostbr[cs - brb] = '\0';
+ }
+ lastend = cs;
+ cs = ocs;
+
+ return r;
+}
+
+/* Check if the match has the given prefix/suffix before/after the
+ * braces. */
+
+/**/
+int
+hasbrpsfx(Cmatch m, char *pre, char *suf)
+{
+ char *op = lastprebr, *os = lastpostbr;
+ VARARR(char, oline, ll);
+ int oll = ll, ocs = cs, ole = lastend, opcs = brpcs, oscs = brscs, ret;
+
+ memcpy(oline, line, ll);
+
+ lastprebr = lastpostbr = NULL;
+
+ instmatch(m, NULL);
+
+ cs = 0;
+ foredel(ll);
+ spaceinline(oll);
+ memcpy(line, oline, oll);
+ cs = ocs;
+ lastend = ole;
+ brpcs = opcs;
+ brscs = oscs;
+
+ ret = (((!pre && !lastprebr) ||
+ (pre && lastprebr && !strcmp(pre, lastprebr))) &&
+ ((!suf && !lastpostbr) ||
+ (suf && lastpostbr && !strcmp(suf, lastpostbr))));
+
+ zsfree(lastprebr);
+ zsfree(lastpostbr);
+ lastprebr = op;
+ lastpostbr = os;
+
+ return ret;
+}
+
+/* Handle the case were we found more than one match. */
+
+/**/
+int
+do_ambiguous(void)
+{
+ int ret = 0;
+
+ menucmp = menuacc = 0;
+
+ /* If we have to insert the first match, call do_single(). This is *
+ * how REC_EXACT takes effect. We effectively turn the ambiguous *
+ * completion into an unambiguous one. */
+ if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
+ minfo.cur = NULL;
+ do_single(ainfo->exactm);
+ invalidatelist();
+ return ret;
+ }
+ /* Setting lastambig here means that the completion is ambiguous and *
+ * AUTO_MENU might want to start a menu completion next time round, *
+ * but this might be overridden below if we can complete an *
+ * unambiguous prefix. */
+ lastambig = 1;
+
+ if (usemenu || (haspattern && comppatinsert &&
+ !strcmp(comppatinsert, "menu"))) {
+ /* We are in a position to start using menu completion due to one *
+ * of the menu completion options, or due to the menu-complete- *
+ * word command, or due to using GLOB_COMPLETE which does menu- *
+ * style completion regardless of the setting of the normal menu *
+ * completion options. */
+ do_ambig_menu();
+ } else if (ainfo) {
+ int atend = (cs == we), la, eq, tcs;
+
+ minfo.cur = NULL;
+ minfo.asked = 0;
+
+ fixsuffix();
+
+ /* First remove the old string from the line. */
+ cs = wb;
+ foredel(we - wb);
+
+ /* Now get the unambiguous string and insert it into the line. */
+ cline_str(ainfo->line, 1, NULL);
+ if (eparq) {
+ tcs = cs;
+ cs = lastend;
+ for (eq = eparq; eq; eq--)
+ inststrlen("\"", 0, 1);
+ cs = tcs;
+ }
+ /* la is non-zero if listambiguous may be used. Copying and
+ * comparing the line looks like BFI but it is the easiest
+ * solution. Really. */
+ la = (ll != origll || strncmp(origline, (char *) line, ll));
+
+ /* If REC_EXACT and AUTO_MENU are set and what we inserted is an *
+ * exact match, we want menu completion the next time round *
+ * so we set fromcomp, to ensure that the word on the line is not *
+ * taken as an exact match. Also we remember if we just moved the *
+ * cursor into the word. */
+ fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
+ ((atend && cs != lastend) ? FC_INWORD : 0));
+
+ /* Probably move the cursor to the end. */
+ if (movetoend == 3)
+ cs = lastend;
+
+ /* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
+ * if the completion is completely ambiguous') is set, and some *
+ * prefix was inserted, return now, bypassing the list-displaying *
+ * code. On the way, invalidate the list and note that we don't *
+ * want to enter an AUTO_MENU imediately. */
+ if (uselist == 3 && la) {
+ int fc = fromcomp;
+
+ invalidatelist();
+ fromcomp = fc;
+ lastambig = 0;
+ clearlist = 1;
+ return ret;
+ }
+ } else
+ return ret;
+
+ /* At this point, we might want a completion listing. Show the listing *
+ * if it is needed. */
+ if (isset(LISTBEEP))
+ ret = 1;
+
+ if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
+ ((!showinglist && (!listshown || !oldlist)) ||
+ (usemenu == 3 && !oldlist)) &&
+ (smatches >= 2 || (compforcelist && *compforcelist)))
+ showinglist = -2;
+
+ return ret;
+}
+
+/* This is a stat that ignores backslashes in the filename. The `ls' *
+ * parameter says if we have to do lstat() or stat(). I think this *
+ * should instead be done by use of a general function to expand a *
+ * filename (stripping backslashes), combined with the actual *
+ * (l)stat(). */
+
+/**/
+int
+ztat(char *nam, struct stat *buf, int ls)
+{
+ char b[PATH_MAX], *p;
+
+ for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
+ if (*nam == '\\' && nam[1])
+ *p++ = *++nam;
+ else
+ *p++ = *nam;
+ *p = '\0';
+
+ return ls ? lstat(b, buf) : stat(b, buf);
+}
+
+/* Insert a single match in the command line. */
+
+/**/
+void
+do_single(Cmatch m)
+{
+ int l, sr = 0, scs;
+ int havesuff = 0;
+ int partest = (m->ripre || ((m->flags & CMF_ISPAR) && parpre));
+ char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
+
+ if (!prpre) prpre = "";
+ if (!ppre) ppre = "";
+ if (!psuf) psuf = "";
+
+ fixsuffix();
+
+ if (!minfo.cur) {
+ /* We are currently not in a menu-completion, *
+ * so set the position variables. */
+ minfo.pos = wb;
+ minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+ minfo.end = we;
+ }
+ /* If we are already in a menu-completion or if we have done a *
+ * glob completion, we have to delete some of the stuff on the *
+ * command line. */
+ if (minfo.cur)
+ l = minfo.len + minfo.insc;
+ else
+ l = we - wb;
+
+ minfo.insc = 0;
+ cs = minfo.pos;
+ foredel(l);
+
+ /* And then we insert the new string. */
+ minfo.len = instmatch(m, &scs);
+ minfo.end = cs;
+ cs = minfo.pos + minfo.len;
+
+ if (m->suf) {
+ havesuff = 1;
+ minfo.insc = ztrlen(m->suf);
+ minfo.len -= minfo.insc;
+ if (minfo.we) {
+ minfo.end += minfo.insc;
+ if (m->flags & CMF_REMOVE) {
+ makesuffixstr(m->remf, m->rems, minfo.insc);
+ if (minfo.insc == 1)
+ suffixlen[STOUC(m->suf[0])] = 1;
+ }
+ }
+ } else {
+ /* There is no user-specified suffix, *
+ * so generate one automagically. */
+ cs = scs;
+ if (partest && (m->flags & CMF_PARBR)) {
+ int pq;
+
+ /*{{*/
+ /* Completing a parameter in braces. Add a removable `}' suffix. */
+ cs += eparq;
+ for (pq = parq; pq; pq--)
+ inststrlen("\"", 1, 1);
+ minfo.insc += parq;
+ inststrlen("}", 1, 1);
+ minfo.insc++;
+ if (minfo.we)
+ minfo.end += minfo.insc;
+ if (m->flags & CMF_PARNEST)
+ havesuff = 1;
+ }
+ if (((m->flags & CMF_FILE) || (partest && isset(AUTOPARAMSLASH))) &&
+ cs > 0 && line[cs - 1] != '/') {
+ /* If we have a filename or we completed a parameter name *
+ * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
+ * If it is, we append a slash. */
+ struct stat buf;
+ char *p;
+ int t = 0;
+
+ if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
+ t = 1;
+ else {
+ /* Build the path name. */
+ if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
+ int ne = noerrs;
+
+ p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ?
+ parpre : m->ripre) +
+ strlen(str) + 2);
+ sprintf(p, "%s%s%c",
+ ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str,
+ ((m->flags & CMF_PARBR) ? Outbrace : '\0'));
+ noerrs = 1;
+ parsestr(p);
+ singsub(&p);
+ errflag = 0;
+ noerrs = ne;
+ } else {
+ p = (char *) zhalloc(strlen(prpre) + strlen(str) +
+ strlen(psuf) + 3);
+ sprintf(p, "%s%s%s", ((prpre && *prpre) ?
+ prpre : "./"), str, psuf);
+ }
+ /* And do the stat. */
+ t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
+ }
+ if (t) {
+ /* It is a directory, so add the slash. */
+ havesuff = 1;
+ inststrlen("/", 1, 1);
+ minfo.insc++;
+ if (minfo.we)
+ minfo.end++;
+ if (!menucmp || minfo.we) {
+ if (m->remf || m->rems)
+ makesuffixstr(m->remf, m->rems, 1);
+ else if (isset(AUTOREMOVESLASH)) {
+ makesuffix(1);
+ suffixlen['/'] = 1;
+ }
+ }
+ }
+ }
+ if (!minfo.insc)
+ cs = minfo.pos + minfo.len - m->qisl;
+ }
+ /* If completing in a brace expansion... */
+ if (brbeg) {
+ if (havesuff) {
+ /*{{*/
+ /* If a suffix was added, and is removable, let *
+ * `,' and `}' remove it. */
+ if (isset(AUTOPARAMKEYS))
+ suffixlen[','] = suffixlen['}'] = suffixlen[256];
+ } else if (!menucmp) {
+ /*{{*/
+ /* Otherwise, add a `,' suffix, and let `}' remove it. */
+ cs = scs;
+ havesuff = 1;
+ inststrlen(",", 1, 1);
+ minfo.insc++;
+ makesuffix(1);
+ if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
+ suffixlen[','] = suffixlen['}'] = 1;
+ }
+ } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
+ /* If we didn't add a suffix, add a space, unless we are *
+ * doing menu completion or we are completing files and *
+ * the string doesn't name an existing file. */
+ if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
+ inststrlen(&(m->autoq), 1, 1);
+ minfo.insc++;
+ }
+ if (!menucmp && (usemenu != 3 || insspace)) {
+ inststrlen(" ", 1, 1);
+ minfo.insc++;
+ if (minfo.we)
+ makesuffix(1);
+ }
+ }
+ if (minfo.we && partest && isset(AUTOPARAMKEYS))
+ makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
+
+ if ((menucmp && !minfo.we) || !movetoend) {
+ cs = minfo.end;
+ if (cs + m->qisl == lastend)
+ cs += minfo.insc;
+ }
+ {
+ Cmatch *om = minfo.cur;
+ struct chdata dat;
+
+ dat.matches = amatches;
+ dat.num = nmatches;
+ dat.cur = m;
+
+ if (menucmp)
+ minfo.cur = &m;
+ runhookdef(INSERTMATCHHOOK, (void *) &dat);
+ minfo.cur = om;
+ }
+}
+
+/* Do completion, given that we are in the middle of a menu completion. We *
+ * don't need to generate a list of matches, because that's already been *
+ * done by previous commands. We will either list the completions, or *
+ * insert the next completion. */
+
+/**/
+void
+do_menucmp(int lst)
+{
+ /* Just list the matches if the list was requested. */
+ if (lst == COMP_LIST_COMPLETE) {
+ showinglist = -2;
+ return;
+ }
+ /* Otherwise go to the next match in the array... */
+ HEAPALLOC {
+ do {
+ if (!*++(minfo.cur)) {
+ do {
+ if (!(minfo.group = (minfo.group)->next))
+ minfo.group = amatches;
+ } while (!(minfo.group)->mcount);
+ minfo.cur = minfo.group->matches;
+ }
+ } while (menuacc &&
+ !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+ /* ... and insert it into the command line. */
+ metafy_line();
+ do_single(*(minfo.cur));
+ unmetafy_line();
+ } LASTALLOC;
+}
+
+/**/
+int
+reverse_menu(Hookdef dummy, void *dummy2)
+{
+ HEAPALLOC {
+ do {
+ if (minfo.cur == (minfo.group)->matches) {
+ do {
+ if (!(minfo.group = (minfo.group)->prev))
+ minfo.group = lmatches;
+ } while (!(minfo.group)->mcount);
+ minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
+ } else
+ minfo.cur--;
+ } while (menuacc &&
+ !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+ metafy_line();
+ do_single(*(minfo.cur));
+ unmetafy_line();
+ } LASTALLOC;
+
+ return 0;
+}
+
+/* Accepts the current completion and starts a new arg, *
+ * with the next completions. This gives you a way to *
+ * accept several selections from the list of matches. */
+
+/**/
+int
+accept_last(void)
+{
+ if (!menuacc) {
+ zsfree(minfo.prebr);
+ minfo.prebr = ztrdup(lastprebr);
+ zsfree(minfo.postbr);
+ minfo.postbr = ztrdup(lastpostbr);
+
+ if (listshown && (lastprebr || lastpostbr)) {
+ Cmgroup g;
+ Cmatch *m;
+
+ for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
+ for (m = g->matches; *m; m++)
+ if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
+ showinglist = -2;
+ break;
+ }
+ }
+ }
+ menuacc++;
+
+ if (brbeg) {
+ int l;
+
+ iremovesuffix(',', 1);
+
+ l = (brscs >= 0 ? brscs : cs) - brpcs;
+
+ zsfree(lastbrbeg->str);
+ lastbrbeg->str = (char *) zalloc(l + 2);
+ memcpy(lastbrbeg->str, line + brpcs, l);
+ lastbrbeg->str[l] = ',';
+ lastbrbeg->str[l + 1] = '\0';
+ } else {
+ int l;
+
+ cs = minfo.pos + minfo.len + minfo.insc;
+ iremovesuffix(' ', 1);
+ l = cs;
+ cs = minfo.pos + minfo.len + minfo.insc - (*(minfo.cur))->qisl;
+ if (cs < l)
+ foredel(l - cs);
+ else if (cs > ll)
+ cs = ll;
+ inststrlen(" ", 1, 1);
+ if (parpre)
+ inststr(parpre);
+ minfo.insc = minfo.len = 0;
+ minfo.pos = cs;
+ minfo.we = 1;
+ }
+ return 0;
+}
+
+/* This maps the value in v into the range [0,m-1], decrementing v
+ * if it is non-negative and making negative values count backwards. */
+
+/**/
+static int
+comp_mod(int v, int m)
+{
+ if (v >= 0)
+ v--;
+ if (v >= 0)
+ return v % m;
+ else {
+ while (v < 0)
+ v += m;
+ return v;
+ }
+}
+
+/* This handles the beginning of menu-completion. */
+
+/**/
+static void
+do_ambig_menu(void)
+{
+ Cmatch *mc;
+
+ if (usemenu != 3) {
+ menucmp = 1;
+ menuacc = 0;
+ minfo.cur = NULL;
+ } else {
+ if (oldlist) {
+ if (oldins && minfo.cur)
+ acceptlast();
+ } else
+ minfo.cur = NULL;
+ }
+ if (insgroup) {
+ insgnum = comp_mod(insgnum, lastpermgnum);
+ for (minfo.group = amatches;
+ minfo.group && (minfo.group)->num != insgnum + 1;
+ minfo.group = (minfo.group)->next);
+ if (!minfo.group || !(minfo.group)->mcount) {
+ minfo.cur = NULL;
+ minfo.asked = 0;
+ return;
+ }
+ insmnum = comp_mod(insmnum, (minfo.group)->mcount);
+ } else {
+ insmnum = comp_mod(insmnum, lastpermmnum);
+ for (minfo.group = amatches;
+ minfo.group && (minfo.group)->mcount <= insmnum;
+ minfo.group = (minfo.group)->next)
+ insmnum -= (minfo.group)->mcount;
+ if (!minfo.group) {
+ minfo.cur = NULL;
+ minfo.asked = 0;
+ return;
+ }
+ }
+ mc = (minfo.group)->matches + insmnum;
+ do_single(*mc);
+ minfo.cur = mc;
+}
+
+/* Return the real number of matches. */
+
+/**/
+zlong
+num_matches(int normal)
+{
+ int alt;
+
+ PERMALLOC {
+ alt = permmatches(0);
+ } LASTALLOC;
+
+ if (normal)
+ return (alt ? 0 : nmatches);
+ else
+ return (alt ? nmatches : 0);
+}
+
+/* Return the number of screen lines needed for the list. */
+
+/**/
+zlong
+list_lines(void)
+{
+ Cmgroup oam;
+
+ PERMALLOC {
+ permmatches(0);
+ } LASTALLOC;
+
+ oam = amatches;
+ amatches = pmatches;
+ listdat.valid = 0;
+ calclist();
+ listdat.valid = 0;
+ amatches = oam;
+
+ return listdat.nlines;
+}
+
+/**/
+void
+comp_list(char *v)
+{
+ zsfree(complist);
+ complist = ztrdup(v);
+
+ onlyexpl = (v && strstr(v, "expl"));
+}
+
+/* This is used to print the explanation string. *
+ * It returns the number of lines printed. */
+
+/**/
+int
+printfmt(char *fmt, int n, int dopr, int doesc)
+{
+ char *p = fmt, nc[DIGBUFSIZE];
+ int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
+
+ for (; *p; p++) {
+ /* Handle the `%' stuff (%% == %, %n == <number of matches>). */
+ if (doesc && *p == '%') {
+ if (*++p) {
+ m = 0;
+ switch (*p) {
+ case '%':
+ if (dopr)
+ putc('%', shout);
+ cc++;
+ break;
+ case 'n':
+ sprintf(nc, "%d", n);
+ if (dopr)
+ fprintf(shout, nc);
+ 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++, cc++)
+ if (dopr)
+ putc(*p, shout);
+ if (*p)
+ p++;
+ else
+ p--;
+ break;
+ }
+ if (dopr && m) {
+ if (b)
+ tcout(TCBOLDFACEBEG);
+ if (s)
+ tcout(TCSTANDOUTBEG);
+ if (u)
+ tcout(TCUNDERLINEBEG);
+ }
+ } else
+ break;
+ } else {
+ cc++;
+ if (*p == '\n') {
+ if (dopr) {
+ if (tccan(TCCLEAREOL))
+ tcout(TCCLEAREOL);
+ else {
+ int s = columns - 1 - (cc % columns);
+
+ while (s-- > 0)
+ putc(' ', shout);
+ }
+ }
+ l += 1 + (cc / columns);
+ cc = 0;
+ }
+ if (dopr)
+ putc(*p, shout);
+ }
+ }
+ if (dopr) {
+ if (tccan(TCCLEAREOL))
+ tcout(TCCLEAREOL);
+ else {
+ int s = columns - 1 - (cc % columns);
+
+ while (s-- > 0)
+ putc(' ', shout);
+ }
+ }
+ return l + (cc / columns);
+}
+
+/* This skips over matches that are not to be listed. */
+
+/**/
+Cmatch *
+skipnolist(Cmatch *p)
+{
+ while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
+ ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
+ p++;
+
+ return p;
+}
+
+/**/
+void
+calclist(void)
+{
+ Cmgroup g;
+ Cmatch *p, m;
+ Cexpl *e;
+ int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES);
+ int max = 0, i;
+ VARARR(int, mlens, nmatches + 1);
+
+ if (listdat.valid && onlyexpl == listdat.onlyexpl &&
+ menuacc == listdat.menuacc &&
+ lines == listdat.lines && columns == listdat.columns)
+ return;
+
+ for (g = amatches; g; g = g->next) {
+ char **pp = g->ylist;
+ int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
+
+ if (!onlyexpl && pp) {
+ /* We have an ylist, lets see, if it contains newlines. */
+ hidden = 1;
+ while (!nl && *pp)
+ nl = !!strchr(*pp++, '\n');
+
+ pp = g->ylist;
+ if (nl || !pp[1]) {
+ /* Yup, there are newlines, count lines. */
+ char *nlptr, *sptr;
+
+ g->flags |= CGF_LINES;
+ hidden = 1;
+ while ((sptr = *pp)) {
+ while (sptr && *sptr) {
+ nlines += (nlptr = strchr(sptr, '\n'))
+ ? 1 + (nlptr-sptr)/columns
+ : strlen(sptr)/columns;
+ sptr = nlptr ? nlptr+1 : NULL;
+ }
+ nlines++;
+ pp++;
+ }
+ nlines--;
+ } else {
+ while (*pp) {
+ l = strlen(*pp);
+ ndisp++;
+ if (l > glong)
+ glong = l;
+ if (l < gshort)
+ gshort = l;
+ totl += l;
+ nlist++;
+ pp++;
+ }
+ }
+ } else if (!onlyexpl) {
+ for (p = g->matches; (m = *p); p++) {
+ if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
+ m->flags |= CMF_HIDE;
+ continue;
+ }
+ m->flags &= ~CMF_HIDE;
+
+ if (m->disp) {
+ if (m->flags & CMF_DISPLINE) {
+ nlines += 1 + printfmt(m->disp, 0, 0, 0);
+ g->flags |= CGF_HASDL;
+ } else {
+ l = niceztrlen(m->disp);
+ ndisp++;
+ if (l > glong)
+ glong = l;
+ if (l < gshort)
+ gshort = l;
+ totl += l;
+ mlens[m->gnum] = l;
+ }
+ nlist++;
+ } else if (!(m->flags & CMF_NOLIST)) {
+ l = niceztrlen(m->str);
+ ndisp++;
+ if (l > glong)
+ glong = l;
+ if (l < gshort)
+ gshort = l;
+ totl += l;
+ mlens[m->gnum] = l;
+ nlist++;
+ } else
+ hidden = 1;
+ }
+ }
+ if ((e = g->expls)) {
+ while (*e) {
+ if ((*e)->count)
+ nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1);
+ e++;
+ }
+ }
+ g->totl = totl + (ndisp * add);
+ g->dcount = ndisp;
+ g->width = glong + add;
+ g->shortest = gshort + add;
+ if ((g->cols = columns / g->width) > g->dcount)
+ g->cols = g->dcount;
+ if (g->cols) {
+ i = g->cols * g->width - add;
+ if (i > max)
+ max = i;
+ }
+ }
+ if (!onlyexpl) {
+ for (g = amatches; g; g = g->next) {
+ char **pp;
+ int glines = 0;
+
+ zfree(g->widths, 0);
+ g->widths = NULL;
+
+ if ((pp = g->ylist)) {
+ if (!(g->flags & CGF_LINES)) {
+ if (g->cols) {
+ glines += (arrlen(pp) + g->cols - 1) / g->cols;
+ if (g->cols > 1)
+ g->width += (max - (g->width * g->cols - add)) / g->cols;
+ } else {
+ g->cols = 1;
+ g->width = 1;
+
+ while (*pp)
+ glines += 1 + (strlen(*pp++) / columns);
+ }
+ }
+ } else {
+ if (g->cols) {
+ glines += (g->dcount + g->cols - 1) / g->cols;
+ if (g->cols > 1)
+ g->width += (max - (g->width * g->cols - add)) / g->cols;
+ } else if (!(g->flags & CGF_LINES)) {
+ g->cols = 1;
+ g->width = 0;
+
+ for (p = g->matches; (m = *p); p++)
+ if (!(m->flags & CMF_HIDE)) {
+ if (m->disp) {
+ if (!(m->flags & CMF_DISPLINE))
+ glines += 1 + (mlens[m->gnum] / columns);
+ } else if (!(m->flags & CMF_NOLIST))
+ glines += 1 + ((1 + mlens[m->gnum]) / columns);
+ }
+ }
+ }
+ g->lins = glines;
+ nlines += glines;
+ }
+ }
+ if (!onlyexpl && isset(LISTPACKED)) {
+ char **pp;
+ int *ws, tlines, tline, tcols, maxlen, nth, width;
+
+ for (g = amatches; g; g = g->next) {
+ ws = g->widths = (int *) zalloc(columns * sizeof(int));
+ memset(ws, 0, columns * sizeof(int));
+ tlines = g->lins;
+ tcols = g->cols;
+ width = 0;
+
+ if ((pp = g->ylist)) {
+ if (!(g->flags & CGF_LINES)) {
+ int yl = arrlen(pp), i;
+ VARARR(int, ylens, yl);
+
+ for (i = 0; *pp; i++, pp++)
+ ylens[i] = strlen(*pp) + add;
+
+ if (isset(LISTROWSFIRST)) {
+ int count, tcol, first, maxlines = 0, llines;
+
+ for (tcols = columns / g->shortest; tcols > g->cols;
+ tcols--) {
+ for (nth = first = maxlen = width = maxlines =
+ llines = tcol = 0,
+ count = g->dcount;
+ count > 0; count--) {
+ if (ylens[nth] > maxlen)
+ maxlen = ylens[nth];
+ nth += tcols;
+ tlines++;
+ if (nth >= g->dcount) {
+ if ((width += maxlen) >= columns)
+ break;
+ ws[tcol++] = maxlen;
+ maxlen = 0;
+ nth = ++first;
+ if (llines > maxlines)
+ maxlines = llines;
+ llines = 0;
+ }
+ }
+ if (nth < yl) {
+ ws[tcol++] = maxlen;
+ width += maxlen;
+ }
+ if (!count && width < columns)
+ break;
+ }
+ if (tcols > g->cols)
+ tlines = maxlines;
+ } else {
+ for (tlines = ((g->totl + columns) / columns);
+ tlines < g->lins; tlines++) {
+ for (pp = g->ylist, nth = tline = width =
+ maxlen = tcols = 0;
+ *pp; nth++, pp++) {
+ if (ylens[nth] > maxlen)
+ maxlen = ylens[nth];
+ if (++tline == tlines) {
+ if ((width += maxlen) >= columns)
+ break;
+ ws[tcols++] = maxlen;
+ maxlen = tline = 0;
+ }
+ }
+ if (tline) {
+ ws[tcols++] = maxlen;
+ width += maxlen;
+ }
+ if (nth == yl && width < columns)
+ break;
+ }
+ }
+ }
+ } else if (g->width) {
+ if (isset(LISTROWSFIRST)) {
+ int addlen, count, tcol, maxlines = 0, llines, i;
+ Cmatch *first;
+
+ for (tcols = columns / g->shortest; tcols > g->cols;
+ tcols--) {
+ p = first = skipnolist(g->matches);
+ for (maxlen = width = maxlines = llines = tcol = 0,
+ count = g->dcount;
+ count > 0; count--) {
+ m = *p;
+ addlen = mlens[m->gnum] + add;
+ if (addlen > maxlen)
+ maxlen = addlen;
+ for (i = tcols; i && *p; i--)
+ p = skipnolist(p + 1);
+
+ llines++;
+ if (!*p) {
+ if (llines > maxlines)
+ maxlines = llines;
+ llines = 0;
+
+ if ((width += maxlen) >= columns)
+ break;
+ ws[tcol++] = maxlen;
+ maxlen = 0;
+
+ p = first = skipnolist(first + 1);
+ }
+ }
+ if (tlines) {
+ ws[tcol++] = maxlen;
+ width += maxlen;
+ }
+ if (!count && width < columns)
+ break;
+ }
+ if (tcols > g->cols)
+ tlines = maxlines;
+ } else {
+ int addlen;
+
+ for (tlines = ((g->totl + columns) / columns);
+ tlines < g->lins; tlines++) {
+ for (p = g->matches, nth = tline = width =
+ maxlen = tcols = 0;
+ (m = *p); p++, nth++) {
+ if (!(m->flags &
+ (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
+ (CMF_NOLIST | CMF_HIDE)))) {
+ addlen = mlens[m->gnum] + add;
+ if (addlen > maxlen)
+ maxlen = addlen;
+ if (++tline == tlines) {
+ if ((width += maxlen) >= columns)
+ break;
+ ws[tcols++] = maxlen;
+ maxlen = tline = 0;
+ }
+ }
+ }
+ if (tline) {
+ ws[tcols++] = maxlen;
+ width += maxlen;
+ }
+ if (nth == g->dcount && width < columns)
+ break;
+ }
+ }
+ }
+ if (tlines == g->lins) {
+ zfree(ws, columns * sizeof(int));
+ g->widths = NULL;
+ } else {
+ nlines += tlines - g->lins;
+ g->lins = tlines;
+ g->cols = tcols;
+ g->totl = width;
+ width -= add;
+ if (width > max)
+ max = width;
+ }
+ }
+ for (g = amatches; g; g = g->next) {
+ if (g->widths) {
+ int *p, a = (max - g->totl + add) / g->cols;
+
+ for (i = g->cols, p = g->widths; i; i--, p++)
+ *p += a;
+ } else if (g->width && g->cols > 1)
+ g->width += (max - (g->width * g->cols - add)) / g->cols;
+ }
+ }
+ listdat.valid = 1;
+ listdat.hidden = hidden;
+ listdat.nlist = nlist;
+ listdat.nlines = nlines;
+ listdat.menuacc = menuacc;
+ listdat.onlyexpl = onlyexpl;
+ listdat.columns = columns;
+ listdat.lines = lines;
+}
+
+/**/
+int asklist(void)
+{
+ /* Set the cursor below the prompt. */
+ trashzle();
+ showinglist = listshown = 0;
+
+ clearflag = (isset(USEZLE) && !termflags &&
+ complastprompt && *complastprompt);
+
+ /* Maybe we have to ask if the user wants to see the list. */
+ if ((!minfo.cur || !minfo.asked) &&
+ ((complistmax && listdat.nlist > complistmax) ||
+ (!complistmax && listdat.nlines >= lines))) {
+ int qup;
+ zsetterm();
+ qup = printfmt("zsh: do you wish to see all %n possibilities? ",
+ listdat.nlist, 1, 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);
+ if (minfo.cur)
+ minfo.asked = 2;
+ return 1;
+ }
+ 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;
+ }
+ return 0;
+}
+
+/**/
+int
+printlist(int over, CLPrintFunc printm)
+{
+ Cmgroup g;
+ Cmatch *p, m;
+ Cexpl *e;
+ int pnl = 0, cl = (over ? listdat.nlines : -1);
+ int mc = 0, ml = 0, printed = 0;
+
+ if (cl < 2) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ g = amatches;
+ while (g) {
+ char **pp = g->ylist;
+
+ if ((e = g->expls)) {
+ int l;
+
+ while (*e) {
+ if ((*e)->count) {
+ if (pnl) {
+ putc('\n', shout);
+ pnl = 0;
+ ml++;
+ if (cl >= 0 && --cl <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ }
+ l = printfmt((*e)->str, (*e)->count, 1, 1);
+ ml += l;
+ if (cl >= 0 && (cl -= l) <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ pnl = 1;
+ }
+ e++;
+ }
+ }
+ if (!listdat.onlyexpl && pp && *pp) {
+ if (pnl) {
+ putc('\n', shout);
+ 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);
+ }
+ } 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;
+ zputs(*pq, shout);
+ if (i) {
+ a = (g->widths ? g->widths[mc] : g->width) -
+ strlen(*pq);
+ while (a--)
+ putc(' ', shout);
+ }
+ pq += (isset(LISTROWSFIRST) ? 1 : nc);
+ mc++;
+ n--;
+ }
+ if (n) {
+ putc('\n', shout);
+ ml++;
+ if (cl >= 0 && --cl <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ }
+ pp += (isset(LISTROWSFIRST) ? g->cols : 1);
+ }
+ }
+ } else if (!listdat.onlyexpl && g->lcount) {
+ int n = g->dcount, nl, nc, i, j, wid;
+ Cmatch *q;
+
+ nl = nc = g->lins;
+
+ if (g->flags & CGF_HASDL) {
+ for (p = g->matches; (m = *p); p++)
+ if (m->disp && (m->flags & CMF_DISPLINE)) {
+ if (pnl) {
+ putc('\n', shout);
+ pnl = 0;
+ ml++;
+ if (cl >= 0 && --cl <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ }
+ printed++;
+ printm(g, p, 0, ml, 1, 0, NULL, NULL);
+ pnl = 1;
+ }
+ }
+ if (n && pnl) {
+ putc('\n', shout);
+ pnl = 0;
+ ml++;
+ if (cl >= 0 && --cl <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ }
+ for (p = skipnolist(g->matches); n && nl--;) {
+ i = g->cols;
+ mc = 0;
+ q = p;
+ while (n && i--) {
+ wid = (g->widths ? g->widths[mc] : g->width);
+ if (!(m = *q)) {
+ printm(g, NULL, mc, ml, (!i), wid, NULL, NULL);
+ break;
+ }
+ if (!m->disp && (m->flags & CMF_FILE)) {
+ 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))
+ printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+ else
+ printm(g, q, mc, ml, (!i), wid, pb, &buf);
+ } else
+ printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+
+ printed++;
+
+ if (--n)
+ for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
+ q = skipnolist(q + 1);
+ mc++;
+ }
+ while (i-- > 0)
+ printm(g, NULL, mc++, ml, (!i),
+ (g->widths ? g->widths[mc] : g->width), NULL, NULL);
+
+ if (n) {
+ putc('\n', shout);
+ ml++;
+ if (cl >= 0 && --cl <= 1) {
+ cl = -1;
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ }
+ if (nl)
+ for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
+ p = skipnolist(p + 1);
+ }
+ }
+ }
+ if (g->lcount)
+ pnl = 1;
+ g = g->next;
+ }
+ if (clearflag) {
+ /* Move the cursor up to the prompt, if always_last_prompt *
+ * is set and all that... */
+ if ((ml = listdat.nlines + nlnct - 1) < lines) {
+ tcmultout(TCUP, TCMULTUP, ml);
+ showinglist = -1;
+ } else
+ clearflag = 0, putc('\n', shout);
+ } else
+ putc('\n', shout);
+ listshown = (clearflag ? 1 : -1);
+
+ return printed;
+}
+
+/**/
+static void
+iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
+ char *path, struct stat *buf)
+{
+ Cmatch m;
+ int len = 0;
+
+ if (!mp)
+ return;
+
+ m = *mp;
+ if (m->disp) {
+ if (m->flags & CMF_DISPLINE) {
+ printfmt(m->disp, 0, 1, 0);
+ return;
+ }
+ nicezputs(m->disp, shout);
+ len = niceztrlen(m->disp);
+ } else {
+ nicezputs(m->str, shout);
+ len = niceztrlen(m->str);
+
+ if (isset(LISTTYPES)) {
+ if (buf)
+ putc(file_type(buf->st_mode), shout);
+ len++;
+ }
+ }
+ if (!lastc) {
+ len = width - len;
+
+ while (len-- > 0)
+ putc(' ', shout);
+ }
+}
+
+/**/
+int
+ilistmatches(Hookdef dummy, Chdata dat)
+{
+ calclist();
+
+ if (!listdat.nlines) {
+ showinglist = listshown = 0;
+ return 1;
+ }
+ if (asklist())
+ return 0;
+
+ printlist(0, iprintm);
+
+ return 0;
+}
+
+/* List the matches. Note that the list entries are metafied. */
+
+/**/
+int
+list_matches(Hookdef dummy, void *dummy2)
+{
+ struct chdata dat;
+
+#ifdef DEBUG
+ /* Sanity check */
+ if (!validlist) {
+ showmsg("BUG: listmatches called with bogus list");
+ return 1;
+ }
+#endif
+
+ dat.matches = amatches;
+ dat.num = nmatches;
+ dat.cur = NULL;
+ return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
+}
+
+/* Invalidate the completion list. */
+
+/**/
+int
+invalidate_list(void)
+{
+ if (showinglist == -2)
+ listmatches();
+ if (validlist) {
+ freematches(lastmatches);
+ lastmatches = NULL;
+ hasoldlist = 0;
+ }
+ lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0;
+ listdat.valid = 0;
+ if (listshown < 0)
+ listshown = 0;
+ minfo.cur = NULL;
+ minfo.asked = 0;
+ zsfree(minfo.prebr);
+ zsfree(minfo.postbr);
+ minfo.postbr = minfo.prebr = NULL;
+ compwidget = NULL;
+
+ return 0;
+}