mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 21:44:11 +01:00
2187 lines
53 KiB
C
2187 lines
53 KiB
C
/*
|
|
* 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"
|
|
#include "compresult.pro"
|
|
|
|
/* The number of columns to leave empty between rows of matches. */
|
|
|
|
#define CM_SPACE 2
|
|
|
|
/* This counts how often the list of completions was invalidated.
|
|
* Can be used to detect if we have a new list. */
|
|
|
|
/**/
|
|
mod_export int invcount;
|
|
|
|
#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, miss = 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->prefix && !q->suffix)) &&
|
|
(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->min;
|
|
|
|
if (len > ((minmlen << 1) / 3))
|
|
goto end;
|
|
}
|
|
e->line = e->word = NULL;
|
|
e->llen = e->wlen = e->olen = 0;
|
|
e->next = NULL;
|
|
}
|
|
}
|
|
end:
|
|
|
|
/* Sanity check. If there are no parts with missing characters but
|
|
* parts with joined substrings, remove those. */
|
|
|
|
for (p = l, e = 0, tmp = 0; p; p = p->next) {
|
|
if (p->flags & (CLF_MISS|CLF_DIFF))
|
|
miss = 1;
|
|
for (q = p->prefix; q; q = q->next)
|
|
if (q->flags & CLF_JOIN) {
|
|
e = p;
|
|
tmp = 0;
|
|
break;
|
|
}
|
|
for (q = p->suffix; q; q = q->next)
|
|
if (q->flags & CLF_JOIN) {
|
|
e = p;
|
|
tmp = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (e && (!miss || cline_sublen(e) == e->min)) {
|
|
for (p = (tmp ? e->suffix : e->prefix);
|
|
p && p->next && !(p->next->flags & CLF_JOIN); p = p->next);
|
|
if (p)
|
|
p->next = NULL;
|
|
}
|
|
if (!ls)
|
|
cline_setlens(l, 0);
|
|
|
|
return l;
|
|
}
|
|
|
|
/* This builds the unambiguous string. If ins is one, it is immediately
|
|
* inserted into the line. Otherwise csp is used to return the relative
|
|
* cursor position in the string returned and posl contains all
|
|
* positions with missing or ambiguous characters. If ins is two, csp
|
|
* and posl contain real command line positions (including braces). */
|
|
|
|
/**/
|
|
static char *
|
|
cline_str(Cline l, int ins, int *csp, LinkList posl)
|
|
{
|
|
Cline s;
|
|
int ocs = cs, ncs, pcs, scs, opos = -1, npos;
|
|
int pm, pmax, pmm, pma, sm, smax, smm, sma, d, dm, mid;
|
|
int i, j, li = 0, cbr, padd = (ins ? wb - ocs : -ocs);
|
|
Brinfo brp, brs;
|
|
|
|
l = cut_cline(l);
|
|
|
|
pmm = pma = smm = sma = dm = pcs = scs = 0;
|
|
pm = pmax = sm = smax = d = mid = cbr = -1;
|
|
brp = brs = NULL;
|
|
|
|
/* 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;
|
|
if (posl && (npos = cs + padd) != opos) {
|
|
opos = npos;
|
|
addlinknode(posl, (void *) ((long) npos));
|
|
}
|
|
}
|
|
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)) {
|
|
if (posl && (npos = cs + padd) != opos) {
|
|
opos = npos;
|
|
addlinknode(posl, (void *) ((long) npos));
|
|
}
|
|
if (((pmax <= (l->max - l->min) || (pma && l->max != l->min)) &&
|
|
(!pmm || (l->flags & CLF_MATCHED))) ||
|
|
((l->flags & CLF_MATCHED) && !pmm)) {
|
|
pm = cs; pmax = l->max - l->min; pmm = l->flags & CLF_MATCHED;
|
|
pma = ((l->prefix || l->suffix) && l->min == cline_sublen(l));
|
|
}
|
|
}
|
|
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) {
|
|
if (posl && (npos = cs + padd) != opos) {
|
|
opos = npos;
|
|
addlinknode(posl, (void *) ((long) npos));
|
|
}
|
|
if (((smax <= (l->min - l->max) || (sma && l->max != l->min)) &&
|
|
(!smm || (l->flags & CLF_MATCHED))) ||
|
|
((l->flags & CLF_MATCHED) && !smm)) {
|
|
sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
|
|
sma = ((l->prefix || l->suffix) && l->min == cline_sublen(l));
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
if (posl && (npos = cs - j + padd) != opos) {
|
|
opos = npos;
|
|
addlinknode(posl, (void *) ((long) npos));
|
|
}
|
|
}
|
|
}
|
|
l = l->next;
|
|
}
|
|
if (posl && (npos = cs + padd) != opos)
|
|
#if 0
|
|
/* This could be used to put an extra colon before the end-of-word
|
|
* position if there is nothing missing. */
|
|
addlinknode(posl, (void *) ((long) -npos));
|
|
#endif
|
|
addlinknode(posl, (void *) ((long) npos));
|
|
|
|
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;
|
|
|
|
if (posl) {
|
|
LinkNode node;
|
|
long p;
|
|
|
|
for (node = firstnode(posl); node; incnode(node)) {
|
|
p = (long) getdata(node);
|
|
if (p >= ocs)
|
|
setdata(node, (void *) (p + 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 = (mid >= 0 ? mid :
|
|
(cbr >= 0 ? cbr :
|
|
(pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
|
|
|
|
if (ins != 1) {
|
|
/* 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);
|
|
|
|
if (csp)
|
|
*csp = ncs - ocs;
|
|
|
|
return r;
|
|
}
|
|
lastend = cs;
|
|
cs = ncs;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Small utility function turning a list of positions into a colon
|
|
* separated string. */
|
|
|
|
static char *
|
|
build_pos_string(LinkList list)
|
|
{
|
|
LinkNode node;
|
|
int l;
|
|
char buf[40], *s;
|
|
long p;
|
|
|
|
for (node = firstnode(list), l = 0; node; incnode(node)) {
|
|
p = (long) getdata(node);
|
|
#if 0
|
|
/* This could be used to put an extra colon before the end-of-word
|
|
* position if there is nothing missing. */
|
|
if (p < 0)
|
|
sprintf(buf, ":%ld", -p);
|
|
else
|
|
#endif
|
|
sprintf(buf, "%ld", p);
|
|
setdata(node, dupstring(buf));
|
|
l += 1 + strlen(buf);
|
|
}
|
|
s = (char *) zalloc(l * sizeof(char));
|
|
*s = 0;
|
|
for (node = firstnode(list); node;) {
|
|
strcat(s, (char *) getdata(node));
|
|
incnode(node);
|
|
if (node)
|
|
strcat(s, ":");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* 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, char **pp, char **ip)
|
|
{
|
|
static char *scache = NULL, *pcache = NULL, *icache = NULL;
|
|
static int ccache;
|
|
|
|
if (mnum && ainfo) {
|
|
if (mnum != unambig_mnum) {
|
|
LinkList list = newlinklist();
|
|
|
|
zsfree(scache);
|
|
scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
|
|
0, &ccache, list);
|
|
zsfree(pcache);
|
|
if (empty(list))
|
|
pcache = ztrdup("");
|
|
else
|
|
pcache = build_pos_string(list);
|
|
|
|
zsfree(icache);
|
|
|
|
list = newlinklist();
|
|
zsfree(cline_str((ainfo->count ? ainfo->line : fainfo->line),
|
|
2, NULL, list));
|
|
if (empty(list))
|
|
icache = ztrdup("");
|
|
else
|
|
icache = build_pos_string(list);
|
|
}
|
|
} else if (mnum != unambig_mnum || !ainfo || !scache) {
|
|
zsfree(scache);
|
|
scache = ztrdup("");
|
|
zsfree(pcache);
|
|
pcache = ztrdup("");
|
|
zsfree(icache);
|
|
icache = ztrdup("");
|
|
ccache = 0;
|
|
}
|
|
unambig_mnum = mnum;
|
|
if (cp)
|
|
*cp = ccache + 1;
|
|
if (pp)
|
|
*pp = pcache;
|
|
if (ip)
|
|
*ip = icache;
|
|
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. */
|
|
|
|
/**/
|
|
mod_export int
|
|
hasbrpsfx(Cmatch m, char *pre, char *suf)
|
|
{
|
|
if (m->flags & CMF_ALL)
|
|
return 1;
|
|
else {
|
|
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 && !(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;
|
|
VARARR(char, old, we - wb);
|
|
|
|
minfo.cur = NULL;
|
|
minfo.asked = 0;
|
|
|
|
fixsuffix();
|
|
|
|
/* First remove the old string from the line. */
|
|
tcs = cs;
|
|
cs = wb;
|
|
memcpy(old, (char *) line + wb, we - wb);
|
|
foredel(we - wb);
|
|
|
|
/* Now get the unambiguous string and insert it into the line. */
|
|
cline_str(ainfo->line, 1, NULL, NULL);
|
|
|
|
/* Sometimes the different match specs used may result in a cline
|
|
* that gives an empty string. If that happened, we re-insert the
|
|
* old string. Unless there were matches added with -U, that is. */
|
|
|
|
if (lastend < we && !lenchanged && !hasunmatched) {
|
|
cs = wb;
|
|
foredel(lastend - wb);
|
|
inststrlen(old, 0, we - wb);
|
|
lastend = we;
|
|
cs = tcs;
|
|
}
|
|
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 ||
|
|
(!uselist && isset(BASHAUTOLIST) && isset(LISTAMBIGUOUS))) &&
|
|
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) && !oldlist)
|
|
ret = 1;
|
|
|
|
if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
|
|
((!showinglist && (!listshown || !oldlist)) ||
|
|
(usemenu == 3 && !oldlist)) &&
|
|
(smatches >= 2 || forcelist))
|
|
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(). */
|
|
|
|
/**/
|
|
mod_export int
|
|
ztat(char *nam, struct stat *buf, int ls)
|
|
{
|
|
if (!(ls ? lstat(nam, buf) : stat(nam, buf)))
|
|
return 0;
|
|
else {
|
|
char *p;
|
|
VARARR(char, b, strlen(nam) + 1);
|
|
|
|
for (p = b; *nam; nam++)
|
|
if (*nam == '\\' && nam[1])
|
|
*p++ = *++nam;
|
|
else
|
|
*p++ = *nam;
|
|
*p = '\0';
|
|
|
|
return ls ? lstat(b, buf) : stat(b, buf);
|
|
}
|
|
}
|
|
|
|
/* Insert all matches in the command line. */
|
|
|
|
/**/
|
|
void
|
|
do_allmatches(int end)
|
|
{
|
|
int first = 1, nm = nmatches - 1, omc = menucmp, oma = menuacc, e;
|
|
Cmatch *mc;
|
|
struct menuinfo mi;
|
|
char *p = (brbeg ? ztrdup(lastbrbeg->str) : NULL);
|
|
|
|
memcpy(&mi, &minfo, sizeof(struct menuinfo));
|
|
menucmp = 1;
|
|
menuacc = 0;
|
|
|
|
for (minfo.group = amatches;
|
|
minfo.group && !(minfo.group)->mcount;
|
|
minfo.group = (minfo.group)->next);
|
|
|
|
mc = (minfo.group)->matches;
|
|
|
|
while (1) {
|
|
if (!((*mc)->flags & CMF_ALL)) {
|
|
if (!first)
|
|
accept_last();
|
|
first = 0;
|
|
|
|
if (!omc && !--nm)
|
|
menucmp = 0;
|
|
|
|
do_single(*mc);
|
|
}
|
|
minfo.cur = mc;
|
|
|
|
if (!*++(minfo.cur)) {
|
|
do {
|
|
if (!(minfo.group = (minfo.group)->next))
|
|
break;
|
|
} while (!(minfo.group)->mcount);
|
|
if (!minfo.group)
|
|
break;
|
|
minfo.cur = minfo.group->matches;
|
|
}
|
|
mc = minfo.cur;
|
|
}
|
|
menucmp = omc;
|
|
menuacc = oma;
|
|
|
|
e = minfo.end;
|
|
memcpy(&minfo, &mi, sizeof(struct menuinfo));
|
|
minfo.end = e;
|
|
minfo.len = e - minfo.pos;
|
|
|
|
if (p) {
|
|
zsfree(lastbrbeg->str);
|
|
lastbrbeg->str = p;
|
|
}
|
|
}
|
|
|
|
/* Insert a single match in the command line. */
|
|
|
|
/**/
|
|
mod_export 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->orig, *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) ||
|
|
(!movetoend && cs == we));
|
|
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);
|
|
|
|
if (m->flags & CMF_ALL)
|
|
do_allmatches(0);
|
|
else {
|
|
|
|
/* 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, tryit = 1;
|
|
|
|
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) ? '}' : '\0'));
|
|
if (*p == '$') {
|
|
char *n;
|
|
Param pm;
|
|
|
|
if (p[1] == '{') {
|
|
char *e;
|
|
|
|
n = dupstring(p + 2);
|
|
e = n + strlen(n) - 1;
|
|
|
|
if (*e == '}')
|
|
*e = '\0';
|
|
} else
|
|
n = p + 1;
|
|
|
|
if ((pm = (Param) paramtab->getnode(paramtab, n)) &&
|
|
PM_TYPE(pm->flags) != PM_SCALAR)
|
|
tryit = 0;
|
|
}
|
|
if (tryit) {
|
|
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 || !strpfx(m->autoq, m->isuf))) {
|
|
int al = strlen(m->autoq);
|
|
inststrlen(m->autoq, 1, al);
|
|
minfo.insc += al;
|
|
}
|
|
if (!menucmp && !(m->flags & CMF_NOSPACE) &&
|
|
(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. */
|
|
|
|
/**/
|
|
mod_export 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... */
|
|
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)) ||
|
|
((*minfo.cur)->flags & CMF_DUMMY) ||
|
|
(((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
|
|
(!(*minfo.cur)->str || !*(*minfo.cur)->str)));
|
|
/* ... and insert it into the command line. */
|
|
metafy_line();
|
|
do_single(*(minfo.cur));
|
|
unmetafy_line();
|
|
}
|
|
|
|
/**/
|
|
int
|
|
reverse_menu(Hookdef dummy, void *dummy2)
|
|
{
|
|
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)) ||
|
|
((*minfo.cur)->flags & CMF_DUMMY) ||
|
|
(((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
|
|
(!(*minfo.cur)->str || !*(*minfo.cur)->str)));
|
|
metafy_line();
|
|
do_single(*(minfo.cur));
|
|
unmetafy_line();
|
|
|
|
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. */
|
|
|
|
/**/
|
|
mod_export 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);
|
|
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. */
|
|
|
|
/**/
|
|
void
|
|
do_ambig_menu(void)
|
|
{
|
|
Cmatch *mc;
|
|
|
|
if (usemenu != 3) {
|
|
menucmp = 1;
|
|
menuacc = 0;
|
|
minfo.cur = NULL;
|
|
} else {
|
|
if (oldlist) {
|
|
if (oldins && minfo.cur)
|
|
accept_last();
|
|
} else
|
|
minfo.cur = NULL;
|
|
}
|
|
#if 0
|
|
/* group-numbers in compstate[insert] */
|
|
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 {
|
|
#endif
|
|
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;
|
|
}
|
|
#if 0
|
|
/* group-numbers in compstate[insert] */
|
|
}
|
|
#endif
|
|
mc = (minfo.group)->matches + insmnum;
|
|
do_single(*mc);
|
|
minfo.cur = mc;
|
|
}
|
|
|
|
/* Return the number of screen lines needed for the list. */
|
|
|
|
/**/
|
|
zlong
|
|
list_lines(void)
|
|
{
|
|
Cmgroup oam;
|
|
|
|
permmatches(0);
|
|
|
|
oam = amatches;
|
|
amatches = pmatches;
|
|
listdat.valid = 0;
|
|
calclist(0);
|
|
listdat.valid = 0;
|
|
amatches = oam;
|
|
|
|
return listdat.nlines;
|
|
}
|
|
|
|
/**/
|
|
void
|
|
comp_list(char *v)
|
|
{
|
|
zsfree(complist);
|
|
complist = v;
|
|
|
|
onlyexpl = (v ? ((strstr(v, "expl") ? 1 : 0) |
|
|
(strstr(v, "messages") ? 2 : 0)) : 0);
|
|
}
|
|
|
|
/* This skips over matches that are not to be listed. */
|
|
|
|
/**/
|
|
mod_export Cmatch *
|
|
skipnolist(Cmatch *p, int showall)
|
|
{
|
|
int mask = (showall ? 0 : (CMF_NOLIST | CMF_MULT)) | CMF_HIDE;
|
|
|
|
while (*p && (((*p)->flags & mask) ||
|
|
((*p)->disp &&
|
|
((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
|
|
p++;
|
|
|
|
return p;
|
|
}
|
|
|
|
/**/
|
|
mod_export int
|
|
calclist(int showall)
|
|
{
|
|
static int lastinvcount = -1;
|
|
|
|
Cmgroup g;
|
|
Cmatch *p, m;
|
|
Cexpl *e;
|
|
int hidden = 0, nlist = 0, nlines = 0;
|
|
int max = 0, i;
|
|
VARARR(int, mlens, nmatches + 1);
|
|
|
|
if (lastinvcount == invcount &&
|
|
listdat.valid && onlyexpl == listdat.onlyexpl &&
|
|
menuacc == listdat.menuacc && showall == listdat.showall &&
|
|
lines == listdat.lines && columns == listdat.columns)
|
|
return 0;
|
|
lastinvcount = invcount;
|
|
|
|
for (g = amatches; g; g = g->next) {
|
|
char **pp = g->ylist;
|
|
int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
|
|
int hasf = 0;
|
|
|
|
g->flags |= CGF_PACKED | CGF_ROWS;
|
|
|
|
if (!onlyexpl && pp) {
|
|
/* We have an ylist, lets see, if it contains newlines. */
|
|
hidden = 1;
|
|
while (!nl && *pp) {
|
|
if (ztrlen(*pp) >= columns)
|
|
nl = 1;
|
|
else
|
|
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 - 1) / columns
|
|
: (ztrlen(sptr) - 1) / columns;
|
|
sptr = nlptr ? nlptr+1 : NULL;
|
|
}
|
|
nlines++;
|
|
pp++;
|
|
}
|
|
/*** nlines--; */
|
|
} else {
|
|
while (*pp) {
|
|
l = ztrlen(*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 (m->flags & CMF_FILE)
|
|
hasf = 1;
|
|
if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
|
|
m->flags |= CMF_HIDE;
|
|
continue;
|
|
}
|
|
m->flags &= ~CMF_HIDE;
|
|
|
|
if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) {
|
|
if ((m->flags & (CMF_NOLIST | CMF_MULT)) &&
|
|
(!m->str || !*m->str)) {
|
|
m->flags |= CMF_HIDE;
|
|
continue;
|
|
}
|
|
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++;
|
|
if (!(m->flags & CMF_PACKED))
|
|
g->flags &= ~CGF_PACKED;
|
|
if (!(m->flags & CMF_ROWS))
|
|
g->flags &= ~CGF_ROWS;
|
|
} else {
|
|
l = niceztrlen(m->str) + !!m->modec;
|
|
ndisp++;
|
|
if (l > glong)
|
|
glong = l;
|
|
if (l < gshort)
|
|
gshort = l;
|
|
totl += l;
|
|
mlens[m->gnum] = l;
|
|
nlist++;
|
|
if (!(m->flags & CMF_PACKED))
|
|
g->flags &= ~CGF_PACKED;
|
|
if (!(m->flags & CMF_ROWS))
|
|
g->flags &= ~CGF_ROWS;
|
|
}
|
|
} else
|
|
hidden = 1;
|
|
}
|
|
}
|
|
if ((e = g->expls)) {
|
|
while (*e) {
|
|
if (((*e)->count || (*e)->always) &&
|
|
(!onlyexpl ||
|
|
(onlyexpl & ((*e)->always > 0 ? 2 : 1))))
|
|
nlines += 1 + printfmt((*e)->str,
|
|
((*e)->always ? -1 : (*e)->count),
|
|
0, 1);
|
|
e++;
|
|
}
|
|
}
|
|
if (isset(LISTTYPES) && hasf)
|
|
g->flags |= CGF_FILES;
|
|
g->totl = totl + (ndisp * CM_SPACE);
|
|
g->dcount = ndisp;
|
|
g->width = glong + CM_SPACE;
|
|
g->shortest = gshort + CM_SPACE;
|
|
if ((g->cols = columns / g->width) > g->dcount)
|
|
g->cols = g->dcount;
|
|
if (g->cols) {
|
|
i = g->cols * g->width - CM_SPACE;
|
|
if (i > max)
|
|
max = i;
|
|
}
|
|
}
|
|
if (!onlyexpl) {
|
|
char **pp;
|
|
int *ws, tlines, tcols, width, glines;
|
|
|
|
for (g = amatches; g; g = g->next) {
|
|
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 -
|
|
CM_SPACE)) /
|
|
g->cols);
|
|
} else {
|
|
g->cols = 1;
|
|
g->width = 1;
|
|
|
|
while (*pp)
|
|
glines += 1 + (ztrlen(*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 - CM_SPACE)) /
|
|
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 (showall ||
|
|
!(m->flags & (CMF_NOLIST | CMF_MULT)))
|
|
glines += 1 + ((mlens[m->gnum]) / columns);
|
|
}
|
|
}
|
|
}
|
|
g->lins = glines;
|
|
nlines += glines;
|
|
}
|
|
for (g = amatches; g; g = g->next) {
|
|
if (!(g->flags & CGF_PACKED))
|
|
continue;
|
|
|
|
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] = ztrlen(*pp) + CM_SPACE;
|
|
|
|
if (g->flags & CGF_ROWS) {
|
|
int nth, tcol, len;
|
|
|
|
for (tcols = columns / (g->shortest + CM_SPACE);
|
|
tcols > g->cols;
|
|
tcols--) {
|
|
|
|
memset(ws, 0, tcols * sizeof(int));
|
|
|
|
for (width = nth = tcol = 0, tlines = 1;
|
|
width < columns && nth < g->dcount;
|
|
nth++, tcol++) {
|
|
|
|
m = *p;
|
|
|
|
if (tcol == tcols) {
|
|
tcol = 0;
|
|
tlines++;
|
|
}
|
|
len = ylens[nth];
|
|
|
|
if (len > ws[tcol]) {
|
|
width += len - ws[tcol];
|
|
ws[tcol] = len;
|
|
}
|
|
}
|
|
if (width < columns)
|
|
break;
|
|
}
|
|
} else {
|
|
int nth, tcol, tline, len;
|
|
|
|
for (tcols = columns / (g->shortest + CM_SPACE);
|
|
tcols > g->cols;
|
|
tcols--) {
|
|
|
|
if ((tlines = (g->dcount + tcols - 1) / tcols) <= 0)
|
|
tlines = 1;
|
|
|
|
memset(ws, 0, tcols * sizeof(int));
|
|
|
|
for (width = nth = tcol = tline = 0;
|
|
width < columns && nth < g->dcount;
|
|
nth++, tline++) {
|
|
|
|
m = *p;
|
|
|
|
if (tline == tlines) {
|
|
tcol++;
|
|
tline = 0;
|
|
}
|
|
if (tcol == tcols) {
|
|
tcol = 0;
|
|
tlines++;
|
|
}
|
|
len = ylens[nth];
|
|
|
|
if (len > ws[tcol]) {
|
|
width += len - ws[tcol];
|
|
ws[tcol] = len;
|
|
}
|
|
}
|
|
if (width < columns)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (g->width) {
|
|
if (g->flags & CGF_ROWS) {
|
|
int nth, tcol, len;
|
|
|
|
for (tcols = columns / (g->shortest + CM_SPACE);
|
|
tcols > g->cols;
|
|
tcols--) {
|
|
|
|
memset(ws, 0, tcols * sizeof(int));
|
|
|
|
for (width = nth = tcol = 0, tlines = 1,
|
|
p = skipnolist(g->matches, showall);
|
|
*p && width < columns && nth < g->dcount;
|
|
nth++, p = skipnolist(p + 1, showall), tcol++) {
|
|
|
|
m = *p;
|
|
|
|
if (tcol == tcols) {
|
|
tcol = 0;
|
|
tlines++;
|
|
}
|
|
len = (mlens[m->gnum] +
|
|
(tcol == tcols - 1 ? 0 : CM_SPACE));
|
|
|
|
if (len > ws[tcol]) {
|
|
width += len - ws[tcol];
|
|
ws[tcol] = len;
|
|
}
|
|
}
|
|
if (width < columns)
|
|
break;
|
|
}
|
|
} else {
|
|
int nth, tcol, tline, len;
|
|
|
|
for (tcols = columns / (g->shortest + CM_SPACE);
|
|
tcols > g->cols;
|
|
tcols--) {
|
|
|
|
if ((tlines = (g->dcount + tcols - 1) / tcols) <= 0)
|
|
tlines = 1;
|
|
|
|
memset(ws, 0, tcols * sizeof(int));
|
|
|
|
for (width = nth = tcol = tline = 0,
|
|
p = skipnolist(g->matches, showall);
|
|
*p && width < columns && nth < g->dcount;
|
|
nth++, p = skipnolist(p + 1, showall), tline++) {
|
|
|
|
m = *p;
|
|
|
|
if (tline == tlines) {
|
|
tcol++;
|
|
tline = 0;
|
|
}
|
|
if (tcol == tcols) {
|
|
tcol = 0;
|
|
tlines++;
|
|
}
|
|
len = (mlens[m->gnum] +
|
|
(tcol == tcols - 1 ? 0 : CM_SPACE));
|
|
|
|
if (len > ws[tcol]) {
|
|
width += len - ws[tcol];
|
|
ws[tcol] = len;
|
|
}
|
|
}
|
|
if (width < columns) {
|
|
if (++tcol < tcols)
|
|
tcols = tcol;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (tcols <= g->cols)
|
|
tlines = g->lins;
|
|
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 -= CM_SPACE;
|
|
if (width > max)
|
|
max = width;
|
|
}
|
|
}
|
|
for (g = amatches; g; g = g->next) {
|
|
if (g->widths) {
|
|
int *p, a = (max - g->totl + CM_SPACE) / 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 - CM_SPACE)) / 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;
|
|
listdat.showall = showall;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**/
|
|
mod_export int
|
|
asklist(void)
|
|
{
|
|
/* Set the cursor below the prompt. */
|
|
trashzle();
|
|
showinglist = listshown = 0;
|
|
|
|
clearflag = (isset(USEZLE) && !termflags && dolastprompt);
|
|
lastlistlen = 0;
|
|
|
|
/* 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))) {
|
|
int qup, l;
|
|
|
|
zsetterm();
|
|
l = (listdat.nlist > 0 ?
|
|
fprintf(shout, "zsh: do you wish to see all %d possibilities (%d lines)? ",
|
|
listdat.nlist, listdat.nlines) :
|
|
fprintf(shout, "zsh: do you wish to see all %d lines? ",
|
|
listdat.nlines));
|
|
qup = ((l + columns - 1) / columns) - 1;
|
|
fflush(shout);
|
|
if (getzlequery(1) != 'y') {
|
|
if (clearflag) {
|
|
putc('\r', shout);
|
|
tcmultout(TCUP, TCMULTUP, qup);
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
tcmultout(TCUP, TCMULTUP, nlnct);
|
|
} else
|
|
putc('\n', shout);
|
|
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);
|
|
minfo.asked = 1;
|
|
} else if (minfo.asked == 2)
|
|
tcmultout(TCUP, TCMULTUP, nlnct);
|
|
|
|
return (minfo.asked ? minfo.asked - 1 : 0);
|
|
}
|
|
|
|
/**/
|
|
mod_export int
|
|
printlist(int over, CLPrintFunc printm, int showall)
|
|
{
|
|
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 || (*e)->always) &&
|
|
(!listdat.onlyexpl ||
|
|
(listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
|
|
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)->always ? -1 : (*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) {
|
|
char *p;
|
|
|
|
while ((p = *pp++)) {
|
|
zputs(p, shout);
|
|
if (*pp) {
|
|
if (ztrlen(p) % columns)
|
|
putc('\n', shout);
|
|
else
|
|
fputs(" \010", 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 += ((g->flags & CGF_ROWS) ? 1 : nc);
|
|
mc++;
|
|
n--;
|
|
}
|
|
if (n) {
|
|
putc('\n', shout);
|
|
ml++;
|
|
if (cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
|
|
}
|
|
}
|
|
} else if (!listdat.onlyexpl &&
|
|
(g->lcount || (showall && g->mcount))) {
|
|
int n = g->dcount, nl, nc, i, j, wid;
|
|
Cmatch *q;
|
|
|
|
nl = nc = g->lins;
|
|
|
|
if (g->flags & CGF_HASDL) {
|
|
for (p = g->matches; (m = *p); p++)
|
|
if (m->disp && (m->flags & CMF_DISPLINE) &&
|
|
(showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) {
|
|
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);
|
|
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, showall); 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);
|
|
break;
|
|
}
|
|
printm(g, q, mc, ml, (!i), wid);
|
|
|
|
printed++;
|
|
|
|
if (--n)
|
|
for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
|
|
j && *q; j--)
|
|
q = skipnolist(q + 1, showall);
|
|
mc++;
|
|
}
|
|
while (i-- > 0) {
|
|
printm(g, NULL, mc, ml, (!i),
|
|
(g->widths ? g->widths[mc] : g->width));
|
|
mc++;
|
|
}
|
|
if (n) {
|
|
putc('\n', shout);
|
|
ml++;
|
|
if (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 (g->lcount || (showall && g->mcount))
|
|
pnl = 1;
|
|
g = g->next;
|
|
}
|
|
lastlistlen = 0;
|
|
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;
|
|
|
|
lastlistlen = listdat.nlines;
|
|
} else
|
|
clearflag = 0, putc('\n', shout);
|
|
} else
|
|
putc('\n', shout);
|
|
|
|
listshown = (clearflag ? 1 : -1);
|
|
|
|
return printed;
|
|
}
|
|
|
|
/**/
|
|
mod_export void
|
|
bld_all_str(Cmatch all)
|
|
{
|
|
Cmgroup g;
|
|
Cmatch *mp, m;
|
|
int len = columns - 5, t, add = 0;
|
|
VARARR(char, buf, columns + 1);
|
|
|
|
buf[0] = '\0';
|
|
|
|
for (g = amatches; g && !g->mcount; g = g->next);
|
|
|
|
mp = g->matches;
|
|
while (1) {
|
|
m = *mp;
|
|
if (!(m->flags & (CMF_ALL | CMF_HIDE)) && m->str) {
|
|
t = strlen(m->str) + add;
|
|
if (len >= t) {
|
|
if (add)
|
|
strcat(buf, " ");
|
|
strcat(buf, m->str);
|
|
len -= t;
|
|
add = 1;
|
|
} else {
|
|
if (len > add + 2) {
|
|
if (add)
|
|
strcat(buf, " ");
|
|
strncat(buf, m->str, len);
|
|
}
|
|
strcat(buf, " ...");
|
|
break;
|
|
}
|
|
}
|
|
if (!*++mp) {
|
|
do {
|
|
if (!(g = g->next))
|
|
break;
|
|
} while (!g->mcount);
|
|
if (!g)
|
|
break;
|
|
mp = g->matches;
|
|
}
|
|
}
|
|
zsfree(all->disp);
|
|
all->disp = ztrdup(buf);
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width)
|
|
{
|
|
Cmatch m;
|
|
int len = 0;
|
|
|
|
if (!mp)
|
|
return;
|
|
|
|
m = *mp;
|
|
if ((m->flags & CMF_ALL) && (!m->disp || !m->disp[0]))
|
|
bld_all_str(m);
|
|
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 ((g->flags & CGF_FILES) && m->modec) {
|
|
putc(m->modec, shout);
|
|
len++;
|
|
}
|
|
}
|
|
if (!lastc) {
|
|
len = width - len;
|
|
|
|
while (len-- > 0)
|
|
putc(' ', shout);
|
|
}
|
|
}
|
|
|
|
/**/
|
|
int
|
|
ilistmatches(Hookdef dummy, Chdata dat)
|
|
{
|
|
calclist(0);
|
|
|
|
if (!listdat.nlines) {
|
|
showinglist = listshown = 0;
|
|
return 1;
|
|
}
|
|
if (asklist())
|
|
return 0;
|
|
|
|
printlist(0, iprintm, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* List the matches. Note that the list entries are metafied. */
|
|
|
|
/**/
|
|
int
|
|
list_matches(Hookdef dummy, void *dummy2)
|
|
{
|
|
struct chdata dat;
|
|
int ret;
|
|
|
|
#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;
|
|
ret = runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Invalidate the completion list. */
|
|
|
|
/**/
|
|
mod_export int
|
|
invalidate_list(void)
|
|
{
|
|
invcount++;
|
|
if (validlist) {
|
|
if (showinglist == -2)
|
|
zrefresh();
|
|
freematches(lastmatches, 1);
|
|
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;
|
|
}
|