mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1935 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1935 lines
		
	
	
	
		
			44 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"
 | |
| 
 | |
| #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->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->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 = 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;
 | |
| 		}
 | |
| 		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 && !(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);
 | |
| 
 | |
| 	/* 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 - wb) && !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))
 | |
| 	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)
 | |
| {
 | |
|     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. */
 | |
| 
 | |
| /**/
 | |
| 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->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) ||
 | |
| 		    (!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);
 | |
| 
 | |
|     /* 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));
 | |
|     /* ... 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));
 | |
|     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);
 | |
| 	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)
 | |
| 		accept_last();
 | |
| 	} else
 | |
| 	    minfo.cur = NULL;
 | |
|     }
 | |
| #if 0
 | |
|     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
 | |
|     }
 | |
| #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 = ztrdup(v);
 | |
| 
 | |
|     onlyexpl = (v && strstr(v, "expl"));
 | |
| }
 | |
| 
 | |
| /* This skips over matches that are not to be listed. */
 | |
| 
 | |
| /**/
 | |
| 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 void
 | |
| calclist(int showall)
 | |
| {
 | |
|     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 && showall == listdat.showall &&
 | |
| 	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;
 | |
| 
 | |
| 	g->flags |= CGF_PACKED | CGF_ROWS;
 | |
| 
 | |
| 	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++;
 | |
| 		    if (!(m->flags & CMF_PACKED))
 | |
| 			g->flags &= ~CGF_PACKED;
 | |
| 		    if (!(m->flags & CMF_ROWS))
 | |
| 			g->flags &= ~CGF_ROWS;
 | |
| 		} else if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) {
 | |
| 		    l = niceztrlen(m->str);
 | |
| 		    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)
 | |
| 		    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) {
 | |
| 	char **pp;
 | |
| 	int *ws, tlines, tline, tcols, maxlen, nth, 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 - 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 (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] = strlen(*pp) + add;
 | |
| 
 | |
| 		    if (g->flags & CGF_ROWS) {
 | |
| 			int count, tcol, first, maxlines = 0, llines;
 | |
| 			int beg = columns / g->shortest, end = g->cols;
 | |
| 
 | |
| 			while (1) {
 | |
| 			    tcols = (beg + end) >> 1;
 | |
| 
 | |
| 			    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 &&
 | |
| 				(tcols <= 0 || beg == end))
 | |
| 				break;
 | |
| 
 | |
| 			    if (beg == end) {
 | |
| 				beg--;
 | |
| 				end--;
 | |
| 			    } else if (width < columns) {
 | |
| 				if ((end = tcols) == beg - 1)
 | |
| 				    end++;
 | |
| 			    } else {
 | |
| 				if ((beg = tcols) - 1 == end)
 | |
| 				    end++;
 | |
| 			    }
 | |
| 			}
 | |
| 			if (tcols > g->cols)
 | |
| 			    tlines = maxlines;
 | |
| 		    } else {
 | |
| 			int beg = ((g->totl + columns) / columns);
 | |
| 			int end = g->lins;
 | |
| 
 | |
| 			while (1) {
 | |
| 			    tlines = (beg + end) >> 1;
 | |
| 
 | |
| 			    for (pp = g->ylist, nth = tline = width =
 | |
| 				     maxlen = tcols = 0;
 | |
| 				 *pp; pp++) {
 | |
| 				if (ylens[nth] > maxlen)
 | |
| 				    maxlen = ylens[nth];
 | |
| 				if (++tline == tlines) {
 | |
| 				    if ((width += maxlen) >= columns)
 | |
| 					break;
 | |
| 				    ws[tcols++] = maxlen;
 | |
| 				    maxlen = tline = 0;
 | |
| 				}
 | |
| 				nth++;
 | |
| 			    }
 | |
| 			    if (tline) {
 | |
| 				ws[tcols++] = maxlen;
 | |
| 				width += maxlen;
 | |
| 			    }
 | |
| 			    if (nth == yl && width < columns &&
 | |
| 				(beg == end || tlines >= g->lins))
 | |
| 				break;
 | |
| 
 | |
| 			    if (beg == end) {
 | |
| 				beg++;
 | |
| 				end++;
 | |
| 			    } else if (width < columns) {
 | |
| 				if ((end = tlines) == beg + 1)
 | |
| 				    end--;
 | |
| 			    } else {
 | |
| 				if ((beg = tlines) + 1 == end)
 | |
| 				    end--;
 | |
| 			    }
 | |
| 			}
 | |
| 			if (tlines > g->lins)
 | |
| 			    tlines = g->lins;
 | |
| 		    }
 | |
| 		}
 | |
| 	    } else if (g->width) {
 | |
| 		if (g->flags & CGF_ROWS) {
 | |
| 		    int addlen, count, tcol, maxlines = 0, llines, i;
 | |
| 		    int beg = columns / g->shortest, end = g->cols;
 | |
| 		    Cmatch *first;
 | |
| 
 | |
| 		    while (1) {
 | |
| 			tcols = (beg + end) >> 1;
 | |
| 
 | |
| 			p = first = skipnolist(g->matches, showall);
 | |
| 			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, showall);
 | |
| 
 | |
| 			    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, showall);
 | |
| 			    }
 | |
| 			}
 | |
| 			if (tlines) {
 | |
| 			    ws[tcol++] = maxlen;
 | |
| 			    width += maxlen;
 | |
| 			}
 | |
| 			if (!count && width < columns &&
 | |
| 			    (tcols <= 0 || beg == end))
 | |
| 			    break;
 | |
| 
 | |
| 			if (beg == end) {
 | |
| 			    beg--;
 | |
| 			    end--;
 | |
| 			} else if (width < columns) {
 | |
| 			    if ((end = tcols) == beg - 1)
 | |
| 				end++;
 | |
| 			} else {
 | |
| 			    if ((beg = tcols) - 1 == end)
 | |
| 				end++;
 | |
| 			}
 | |
| 		    }
 | |
| 		    if (tcols > g->cols)
 | |
| 			tlines = maxlines;
 | |
| 		} else {
 | |
| 		    int addlen;
 | |
| 		    int smask = ((showall ? 0 : (CMF_NOLIST | CMF_MULT)) |
 | |
| 				 CMF_HIDE);
 | |
| 		    int beg = ((g->totl + columns) / columns);
 | |
| 		    int end = g->lins;
 | |
| 
 | |
| 		    while (1) {
 | |
| 			tlines = (beg + end) >> 1;
 | |
| 
 | |
| 			for (p = g->matches, nth = tline = width =
 | |
| 				 maxlen = tcols = 0;
 | |
| 			     (m = *p); p++) {
 | |
| 			    if (!(m->flags &
 | |
| 				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
 | |
| 				   smask))) {
 | |
| 				addlen = mlens[m->gnum] + add;
 | |
| 				if (addlen > maxlen)
 | |
| 				    maxlen = addlen;
 | |
| 				if (++tline == tlines) {
 | |
| 				    if ((width += maxlen) >= columns)
 | |
| 					break;
 | |
| 				    ws[tcols++] = maxlen;
 | |
| 				    maxlen = tline = 0;
 | |
| 				}
 | |
| 				nth++;
 | |
| 			    }
 | |
| 			}
 | |
| 			if (tline) {
 | |
| 			    ws[tcols++] = maxlen;
 | |
| 			    width += maxlen;
 | |
| 			}
 | |
| 			if (nth == g->dcount && width < columns &&
 | |
| 			    (beg == end || tlines >= g->lins))
 | |
| 			    break;
 | |
| 
 | |
| 			if (beg == end) {
 | |
| 			    beg++;
 | |
| 			    end++;
 | |
| 			} else if (width < columns) {
 | |
| 			    if ((end = tlines) == beg + 1)
 | |
| 				end--;
 | |
| 			} else {
 | |
| 			    if ((beg = tlines) + 1 == end)
 | |
| 				end--;
 | |
| 			}
 | |
| 		    }
 | |
| 		    if (tlines > g->lins)
 | |
| 			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 -= 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;
 | |
|     listdat.showall = showall;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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;
 | |
| 	zsetterm();
 | |
| 	qup = printfmt("zsh: do you wish to see all %n lines? ",
 | |
| 		       listdat.nlines, 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);
 | |
| 	    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;
 | |
|     }
 | |
|     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) {
 | |
| 		    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 += ((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)) {
 | |
| 			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, 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, 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 = ((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), NULL, NULL);
 | |
| 		    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;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| 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) && 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(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)
 | |
| {
 | |
|     if (validlist) {
 | |
| 	if (showinglist == -2)
 | |
| 	    zrefresh();
 | |
| 	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;
 | |
| }
 |