mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-11-03 19:11:34 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3785 lines
		
	
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3785 lines
		
	
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * compctl.c - the compctl builtin
 | 
						|
 *
 | 
						|
 * This file is part of zsh, the Z shell.
 | 
						|
 *
 | 
						|
 * Copyright (c) 1992-1997 Paul Falstad
 | 
						|
 * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
 | 
						|
 * the possibility of such damage.
 | 
						|
 *
 | 
						|
 * Paul Falstad 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 Paul Falstad and the
 | 
						|
 * Zsh Development Group have no obligation to provide maintenance,
 | 
						|
 * support, updates, enhancements, or modifications.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include "compctl.mdh"
 | 
						|
#include "compctl.pro"
 | 
						|
 | 
						|
/* Default completion infos */
 | 
						|
 
 | 
						|
/**/
 | 
						|
struct compctl cc_compos, cc_default, cc_first, cc_dummy;
 | 
						|
 
 | 
						|
/* Hash table for completion info for commands */
 | 
						|
 
 | 
						|
/**/
 | 
						|
HashTable compctltab;
 | 
						|
 | 
						|
/* List of pattern compctls */
 | 
						|
 | 
						|
/**/
 | 
						|
Patcomp patcomps;
 | 
						|
 | 
						|
#define COMP_LIST	(1<<0)	/* -L */
 | 
						|
#define COMP_COMMAND	(1<<1)	/* -C */
 | 
						|
#define COMP_DEFAULT	(1<<2)	/* -D */
 | 
						|
#define COMP_FIRST	(1<<3)	/* -T */
 | 
						|
#define COMP_REMOVE	(1<<4)
 | 
						|
#define COMP_LISTMATCH	(1<<5)	/* -L and -M */
 | 
						|
 | 
						|
#define COMP_SPECIAL (COMP_COMMAND|COMP_DEFAULT|COMP_FIRST)
 | 
						|
 | 
						|
/* Flag for listing, command, default, or first completion */
 | 
						|
static int cclist;
 | 
						|
 | 
						|
/* Mask for determining what to print */
 | 
						|
static unsigned long showmask = 0;
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
createcompctltable(void)
 | 
						|
{
 | 
						|
    compctltab = newhashtable(23, "compctltab", NULL);
 | 
						|
 | 
						|
    compctltab->hash        = hasher;
 | 
						|
    compctltab->emptytable  = emptyhashtable;
 | 
						|
    compctltab->filltable   = NULL;
 | 
						|
    compctltab->cmpnodes    = strcmp;
 | 
						|
    compctltab->addnode     = addhashnode;
 | 
						|
    compctltab->getnode     = gethashnode2;
 | 
						|
    compctltab->getnode2    = gethashnode2;
 | 
						|
    compctltab->removenode  = removehashnode;
 | 
						|
    compctltab->disablenode = NULL;
 | 
						|
    compctltab->enablenode  = NULL;
 | 
						|
    compctltab->freenode    = freecompctlp;
 | 
						|
    compctltab->printnode   = printcompctlp;
 | 
						|
 | 
						|
    patcomps = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
freecompctlp(HashNode hn)
 | 
						|
{
 | 
						|
    Compctlp ccp = (Compctlp) hn;
 | 
						|
 | 
						|
    zsfree(ccp->nam);
 | 
						|
    freecompctl(ccp->cc);
 | 
						|
    zfree(ccp, sizeof(struct compctlp));
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
void
 | 
						|
freecompctl(Compctl cc)
 | 
						|
{
 | 
						|
    if (cc == &cc_default ||
 | 
						|
 	cc == &cc_first ||
 | 
						|
	cc == &cc_compos ||
 | 
						|
	--cc->refc > 0)
 | 
						|
	return;
 | 
						|
 | 
						|
    zsfree(cc->keyvar);
 | 
						|
    zsfree(cc->glob);
 | 
						|
    zsfree(cc->str);
 | 
						|
    zsfree(cc->func);
 | 
						|
    zsfree(cc->explain);
 | 
						|
    zsfree(cc->ylist);
 | 
						|
    zsfree(cc->prefix);
 | 
						|
    zsfree(cc->suffix);
 | 
						|
    zsfree(cc->hpat);
 | 
						|
    zsfree(cc->gname);
 | 
						|
    zsfree(cc->subcmd);
 | 
						|
    zsfree(cc->substr);
 | 
						|
    if (cc->cond)
 | 
						|
	freecompcond(cc->cond);
 | 
						|
    if (cc->ext) {
 | 
						|
	Compctl n, m;
 | 
						|
 | 
						|
	n = cc->ext;
 | 
						|
	do {
 | 
						|
	    m = (Compctl) (n->next);
 | 
						|
	    freecompctl(n);
 | 
						|
	    n = m;
 | 
						|
	}
 | 
						|
	while (n);
 | 
						|
    }
 | 
						|
    if (cc->xor && cc->xor != &cc_default)
 | 
						|
	freecompctl(cc->xor);
 | 
						|
    if (cc->matcher)
 | 
						|
	freecmatcher(cc->matcher);
 | 
						|
    zsfree(cc->mstr);
 | 
						|
    zfree(cc, sizeof(struct compctl));
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
void
 | 
						|
freecompcond(void *a)
 | 
						|
{
 | 
						|
    Compcond cc = (Compcond) a;
 | 
						|
    Compcond and, or, c;
 | 
						|
    int n;
 | 
						|
 | 
						|
    for (c = cc; c; c = or) {
 | 
						|
	or = c->or;
 | 
						|
	for (; c; c = and) {
 | 
						|
	    and = c->and;
 | 
						|
	    if (c->type == CCT_POS ||
 | 
						|
		c->type == CCT_NUMWORDS) {
 | 
						|
		free(c->u.r.a);
 | 
						|
		free(c->u.r.b);
 | 
						|
	    } else if (c->type == CCT_CURSUF ||
 | 
						|
		       c->type == CCT_CURPRE) {
 | 
						|
		for (n = 0; n < c->n; n++)
 | 
						|
		    if (c->u.s.s[n])
 | 
						|
			zsfree(c->u.s.s[n]);
 | 
						|
		free(c->u.s.s);
 | 
						|
	    } else if (c->type == CCT_RANGESTR ||
 | 
						|
		       c->type == CCT_RANGEPAT) {
 | 
						|
		for (n = 0; n < c->n; n++)
 | 
						|
		    if (c->u.l.a[n])
 | 
						|
			zsfree(c->u.l.a[n]);
 | 
						|
		free(c->u.l.a);
 | 
						|
		for (n = 0; n < c->n; n++)
 | 
						|
		    if (c->u.l.b[n])
 | 
						|
			zsfree(c->u.l.b[n]);
 | 
						|
		free(c->u.l.b);
 | 
						|
	    } else {
 | 
						|
		for (n = 0; n < c->n; n++)
 | 
						|
		    if (c->u.s.s[n])
 | 
						|
			zsfree(c->u.s.s[n]);
 | 
						|
		free(c->u.s.p);
 | 
						|
		free(c->u.s.s);
 | 
						|
	    }
 | 
						|
	    zfree(c, sizeof(struct compcond));
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
compctlread(char *name, char **args, char *ops, char *reply)
 | 
						|
{
 | 
						|
    char *buf, *bptr;
 | 
						|
 | 
						|
    /* only allowed to be called for completion */
 | 
						|
    if (!incompctlfunc) {
 | 
						|
	zwarnnam(name, "option valid only in functions called for completion",
 | 
						|
		NULL, 0);
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (ops['l']) {
 | 
						|
	/* -ln gives the index of the word the cursor is currently on, which is
 | 
						|
	available in cs (but remember that Zsh counts from one, not zero!) */
 | 
						|
	if (ops['n']) {
 | 
						|
	    char nbuf[14];
 | 
						|
 | 
						|
	    if (ops['e'] || ops['E'])
 | 
						|
		printf("%d\n", cs + 1);
 | 
						|
	    if (!ops['e']) {
 | 
						|
		sprintf(nbuf, "%d", cs + 1);
 | 
						|
		setsparam(reply, ztrdup(nbuf));
 | 
						|
	    }
 | 
						|
	    return 0;
 | 
						|
	}
 | 
						|
	/* without -n, the current line is assigned to the given parameter as a
 | 
						|
	scalar */
 | 
						|
	if (ops['e'] || ops['E']) {
 | 
						|
	    zputs((char *) line, stdout);
 | 
						|
	    putchar('\n');
 | 
						|
	}
 | 
						|
	if (!ops['e'])
 | 
						|
	    setsparam(reply, ztrdup((char *) line));
 | 
						|
    } else {
 | 
						|
	int i;
 | 
						|
 | 
						|
	/* -cn gives the current cursor position within the current word, which
 | 
						|
	is available in clwpos (but remember that Zsh counts from one, not
 | 
						|
	zero!) */
 | 
						|
	if (ops['n']) {
 | 
						|
	    char nbuf[14];
 | 
						|
 | 
						|
	    if (ops['e'] || ops['E'])
 | 
						|
		printf("%d\n", clwpos + 1);
 | 
						|
	    if (!ops['e']) {
 | 
						|
		sprintf(nbuf, "%d", clwpos + 1);
 | 
						|
		setsparam(reply, ztrdup(nbuf));
 | 
						|
	    }
 | 
						|
	    return 0;
 | 
						|
	}
 | 
						|
	/* without -n, the words of the current line are assigned to the given
 | 
						|
	parameters separately */
 | 
						|
	if (ops['A'] && !ops['e']) {
 | 
						|
	    /* the -A option means that one array is specified, instead of
 | 
						|
	    many parameters */
 | 
						|
	    char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
 | 
						|
 | 
						|
	    for (i = 0, p = b; i < clwnum; p++, i++)
 | 
						|
		*p = ztrdup(clwords[i]);
 | 
						|
 | 
						|
	    setaparam(reply, b);
 | 
						|
	    return 0;
 | 
						|
	}
 | 
						|
	if (ops['e'] || ops['E']) {
 | 
						|
	    for (i = 0; i < clwnum; i++) {
 | 
						|
		zputs(clwords[i], stdout);
 | 
						|
		putchar('\n');
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (ops['e'])
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < clwnum && *args; reply = *args++, i++)
 | 
						|
	    setsparam(reply, ztrdup(clwords[i]));
 | 
						|
 | 
						|
	if (i < clwnum) {
 | 
						|
	    int j, len;
 | 
						|
 | 
						|
	    for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
 | 
						|
	    bptr = buf = zalloc(len + j - i);
 | 
						|
	    while (i < clwnum) {
 | 
						|
		strucpy(&bptr, clwords[i++]);
 | 
						|
		*bptr++ = ' ';
 | 
						|
	    }
 | 
						|
	    bptr[-1] = '\0';
 | 
						|
	} else
 | 
						|
	    buf = ztrdup("");
 | 
						|
	setsparam(reply, buf);
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Try to get the global matcher from the given compctl. */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
get_gmatcher(char *name, char **argv)
 | 
						|
{
 | 
						|
    if (!strcmp(*argv, "-M")) {
 | 
						|
	char **p = ++argv;
 | 
						|
 | 
						|
	while (*p) {
 | 
						|
	    if (**p++ == '-')
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if (set_gmatcher(name, argv))
 | 
						|
	    return 2;
 | 
						|
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This prints the global matcher definitions. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
print_gmatcher(int ac)
 | 
						|
{
 | 
						|
    Cmlist p;
 | 
						|
 | 
						|
    if ((p = cmatcher)) {
 | 
						|
	printf((ac ? "compctl -M" : "MATCH"));
 | 
						|
 | 
						|
	while (p) {
 | 
						|
	    printf(" \'%s\'", p->str);
 | 
						|
 | 
						|
	    p = p->next;
 | 
						|
	}
 | 
						|
	putchar('\n');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Parse the basic flags for `compctl' */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 | 
						|
{
 | 
						|
    /* Parse the basic flags for completion:
 | 
						|
     * first is a flag that we are not in extended completion,
 | 
						|
     * while hx indicates or (+) completion (need to know for
 | 
						|
     * default and command completion as the initial compctl is special). 
 | 
						|
     * cct is a temporary just to hold flags; it never needs freeing.
 | 
						|
     */
 | 
						|
    struct compctl cct;
 | 
						|
    char **argv = *av;
 | 
						|
    int ready = 0, hx = 0;
 | 
						|
 | 
						|
    /* Handle `compctl + foo ...' specially:  turn it into
 | 
						|
     * a default compctl by removing it from the hash table.
 | 
						|
     */
 | 
						|
    if (first && argv[0][0] == '+' && !argv[0][1] &&
 | 
						|
	!(argv[1] && argv[1][0] == '-' && argv[1][1])) {
 | 
						|
	argv++;
 | 
						|
	if(argv[0] && argv[0][0] == '-')
 | 
						|
	    argv++;
 | 
						|
	*av = argv;
 | 
						|
	if (cl)
 | 
						|
	    return 1;
 | 
						|
	else {
 | 
						|
	    freecompctl(cc);
 | 
						|
	    cclist = COMP_REMOVE;
 | 
						|
	    return 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    memset((void *)&cct, 0, sizeof(cct));
 | 
						|
    cct.mask2 = CC_CCCONT;
 | 
						|
 | 
						|
    /* Loop through the flags until we have no more:        *
 | 
						|
     * those with arguments are not properly allocated yet, *
 | 
						|
     * we just hang on to the argument that was passed.     */
 | 
						|
    for (; !ready && argv[0] && argv[0][0] == '-' && (argv[0][1] || !first);) {
 | 
						|
	if (!argv[0][1])
 | 
						|
	    *argv = "-+";
 | 
						|
	while (!ready && *++(*argv)) {
 | 
						|
	    if(**argv == Meta)
 | 
						|
		*++*argv ^= 32;
 | 
						|
	    switch (**argv) {
 | 
						|
	    case 'f':
 | 
						|
		cct.mask |= CC_FILES;
 | 
						|
		break;
 | 
						|
	    case 'c':
 | 
						|
		cct.mask |= CC_COMMPATH;
 | 
						|
		break;
 | 
						|
	    case 'm':
 | 
						|
		cct.mask |= CC_EXTCMDS;
 | 
						|
		break;
 | 
						|
	    case 'w':
 | 
						|
		cct.mask |= CC_RESWDS;
 | 
						|
		break;
 | 
						|
	    case 'o':
 | 
						|
		cct.mask |= CC_OPTIONS;
 | 
						|
		break;
 | 
						|
	    case 'v':
 | 
						|
		cct.mask |= CC_VARS;
 | 
						|
		break;
 | 
						|
	    case 'b':
 | 
						|
		cct.mask |= CC_BINDINGS;
 | 
						|
		break;
 | 
						|
	    case 'A':
 | 
						|
		cct.mask |= CC_ARRAYS;
 | 
						|
		break;
 | 
						|
	    case 'I':
 | 
						|
		cct.mask |= CC_INTVARS;
 | 
						|
		break;
 | 
						|
	    case 'F':
 | 
						|
		cct.mask |= CC_SHFUNCS;
 | 
						|
		break;
 | 
						|
	    case 'p':
 | 
						|
		cct.mask |= CC_PARAMS;
 | 
						|
		break;
 | 
						|
	    case 'E':
 | 
						|
		cct.mask |= CC_ENVVARS;
 | 
						|
		break;
 | 
						|
	    case 'j':
 | 
						|
		cct.mask |= CC_JOBS;
 | 
						|
		break;
 | 
						|
	    case 'r':
 | 
						|
		cct.mask |= CC_RUNNING;
 | 
						|
		break;
 | 
						|
	    case 'z':
 | 
						|
		cct.mask |= CC_STOPPED;
 | 
						|
		break;
 | 
						|
	    case 'B':
 | 
						|
		cct.mask |= CC_BUILTINS;
 | 
						|
		break;
 | 
						|
	    case 'a':
 | 
						|
		cct.mask |= CC_ALREG | CC_ALGLOB;
 | 
						|
		break;
 | 
						|
	    case 'R':
 | 
						|
		cct.mask |= CC_ALREG;
 | 
						|
		break;
 | 
						|
	    case 'G':
 | 
						|
		cct.mask |= CC_ALGLOB;
 | 
						|
		break;
 | 
						|
	    case 'u':
 | 
						|
		cct.mask |= CC_USERS;
 | 
						|
		break;
 | 
						|
	    case 'd':
 | 
						|
		cct.mask |= CC_DISCMDS;
 | 
						|
		break;
 | 
						|
	    case 'e':
 | 
						|
		cct.mask |= CC_EXCMDS;
 | 
						|
		break;
 | 
						|
	    case 'N':
 | 
						|
		cct.mask |= CC_SCALARS;
 | 
						|
		break;
 | 
						|
	    case 'O':
 | 
						|
		cct.mask |= CC_READONLYS;
 | 
						|
		break;
 | 
						|
	    case 'Z':
 | 
						|
		cct.mask |= CC_SPECIALS;
 | 
						|
		break;
 | 
						|
	    case 'q':
 | 
						|
		cct.mask |= CC_REMOVE;
 | 
						|
		break;
 | 
						|
	    case 'U':
 | 
						|
		cct.mask |= CC_DELETE;
 | 
						|
		break;
 | 
						|
	    case 'n':
 | 
						|
		cct.mask |= CC_NAMED;
 | 
						|
		break;
 | 
						|
	    case 'Q':
 | 
						|
		cct.mask |= CC_QUOTEFLAG;
 | 
						|
		break;
 | 
						|
	    case '/':
 | 
						|
		cct.mask |= CC_DIRS;
 | 
						|
		break;
 | 
						|
	    case 't':
 | 
						|
		{
 | 
						|
		    char *p;
 | 
						|
 | 
						|
		    if (cl) {
 | 
						|
			zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    if ((*argv)[1]) {
 | 
						|
			p = (*argv) + 1;
 | 
						|
			*argv = "" - 1;
 | 
						|
		    } else if (!argv[1]) {
 | 
						|
			zwarnnam(name, "retry specification expected after -%c", NULL,
 | 
						|
				 **argv);
 | 
						|
			return 1;
 | 
						|
		    } else {
 | 
						|
			p = *++argv;
 | 
						|
			*argv = "" - 1;
 | 
						|
		    }
 | 
						|
		    switch (*p) {
 | 
						|
		    case '+':
 | 
						|
			cct.mask2 = CC_XORCONT;
 | 
						|
			break;
 | 
						|
		    case 'n':
 | 
						|
			cct.mask2 = 0;
 | 
						|
			break;
 | 
						|
		    case '-':
 | 
						|
			cct.mask2 = CC_PATCONT;
 | 
						|
			break;
 | 
						|
		    case 'x':
 | 
						|
			cct.mask2 = CC_DEFCONT;
 | 
						|
			break;
 | 
						|
		    default:
 | 
						|
			zwarnnam(name, "invalid retry specification character `%c'",
 | 
						|
				 NULL, *p);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    if (p[1]) {
 | 
						|
			zwarnnam(name, "too many retry specification characters: `%s'",
 | 
						|
				 p + 1, 0);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'k':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.keyvar = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "variable name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.keyvar = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'K':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.func = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "function name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.func = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'Y':
 | 
						|
		cct.mask |= CC_EXPANDEXPL;
 | 
						|
		goto expl;
 | 
						|
	    case 'X':
 | 
						|
		cct.mask &= ~CC_EXPANDEXPL;
 | 
						|
	    expl:
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.explain = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "string expected after -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.explain = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'y':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.ylist = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "function/variable expected after -%c",
 | 
						|
			     NULL, **argv);
 | 
						|
		} else {
 | 
						|
		    cct.ylist = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'P':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.prefix = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "string expected after -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.prefix = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'S':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.suffix = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "string expected after -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.suffix = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'g':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.glob = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "glob pattern expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.glob = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 's':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.str = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "command string expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.str = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'l':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		} else if ((*argv)[1]) {
 | 
						|
		    cct.subcmd = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "command name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.subcmd = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'h':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		} else if ((*argv)[1]) {
 | 
						|
		    cct.substr = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "command name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.substr = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'W':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.withd = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "path expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.withd = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'J':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.gname = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "group name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.gname = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'V':
 | 
						|
		if ((*argv)[1]) {
 | 
						|
		    cct.gname = (*argv) + 1;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "group name expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    cct.gname = *++argv;
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		cct.mask2 |= CC_NOSORT;
 | 
						|
		break;
 | 
						|
	    case '1':
 | 
						|
		cct.mask2 |= CC_UNIQALL;
 | 
						|
		cct.mask2 &= ~CC_UNIQCON;
 | 
						|
		break;
 | 
						|
	    case '2':
 | 
						|
		cct.mask2 |= CC_UNIQCON;
 | 
						|
		cct.mask2 &= ~CC_UNIQALL;
 | 
						|
		break;
 | 
						|
	    case 'M':
 | 
						|
		if (cclist & COMP_LIST) {
 | 
						|
		    cclist |= COMP_LISTMATCH;
 | 
						|
		} else if ((*argv)[1]) {
 | 
						|
		    if ((cct.matcher =
 | 
						|
			 parse_cmatcher(name, (cct.mstr = (*argv) + 1))) ==
 | 
						|
			pcm_err) {
 | 
						|
			cct.matcher = NULL;
 | 
						|
			cct.mstr = NULL;
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *argv = "" - 1;
 | 
						|
		} else if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "matching specification expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		} else {
 | 
						|
		    if ((cct.matcher =
 | 
						|
			 parse_cmatcher(name, (cct.mstr = *++argv))) ==
 | 
						|
			pcm_err) {
 | 
						|
			cct.matcher = NULL;
 | 
						|
			cct.mstr = NULL;
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *argv = "" - 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'H':
 | 
						|
		if ((*argv)[1])
 | 
						|
		    cct.hnum = atoi((*argv) + 1);
 | 
						|
		else if (argv[1])
 | 
						|
		    cct.hnum = atoi(*++argv);
 | 
						|
		else {
 | 
						|
		    zwarnnam(name, "number expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "missing pattern after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		cct.hpat = *++argv;
 | 
						|
		if (cct.hnum < 1)
 | 
						|
		    cct.hnum = 0;
 | 
						|
		if (*cct.hpat == '*' && !cct.hpat[1])
 | 
						|
		    cct.hpat = "";
 | 
						|
		*argv = "" - 1;
 | 
						|
		break;
 | 
						|
	    case 'C':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (first && !hx) {
 | 
						|
		    cclist |= COMP_COMMAND;
 | 
						|
		} else {
 | 
						|
		    zwarnnam(name, "misplaced command completion (-C) flag",
 | 
						|
			    NULL, 0);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    case 'D':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (first && !hx) {
 | 
						|
		    isdef = 1;
 | 
						|
		    cclist |= COMP_DEFAULT;
 | 
						|
		} else {
 | 
						|
		    zwarnnam(name, "misplaced default completion (-D) flag",
 | 
						|
			    NULL, 0);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 	    case 'T':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (first && !hx) {
 | 
						|
 		    cclist |= COMP_FIRST;
 | 
						|
 		} else {
 | 
						|
 		    zwarnnam(name, "misplaced first completion (-T) flag",
 | 
						|
 			    NULL, 0);
 | 
						|
 		    return 1;
 | 
						|
 		}
 | 
						|
 		break;
 | 
						|
	    case 'L':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "illegal option -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (!first || hx) {
 | 
						|
		    zwarnnam(name, "illegal use of -L flag", NULL, 0);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		cclist |= COMP_LIST;
 | 
						|
		break;
 | 
						|
	    case 'x':
 | 
						|
		if (cl) {
 | 
						|
		    zerrnam(name, "extended completion not allowed", NULL, 0);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (!argv[1]) {
 | 
						|
		    zwarnnam(name, "condition expected after -%c", NULL,
 | 
						|
			    **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		if (first) {
 | 
						|
		    argv++;
 | 
						|
		    if (get_xcompctl(name, &argv, &cct, isdef)) {
 | 
						|
			if (cct.ext)
 | 
						|
			    freecompctl(cct.ext);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    ready = 2;
 | 
						|
		} else {
 | 
						|
		    zwarnnam(name, "recursive extended completion not allowed",
 | 
						|
			    NULL, 0);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    default:
 | 
						|
		if (!first && (**argv == '-' || **argv == '+'))
 | 
						|
		    (*argv)--, argv--, ready = 1;
 | 
						|
		else {
 | 
						|
		    zwarnnam(name, "bad option: -%c", NULL, **argv);
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	if (*++argv && (!ready || ready == 2) &&
 | 
						|
	    **argv == '+' && !argv[0][1]) {
 | 
						|
	    if (cl) {
 | 
						|
		zerrnam(name, "xor'ed completion illegal", NULL, 0);
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	    /* There's an alternative (+) completion:  assign
 | 
						|
	     * what we have so far before moving on to that.
 | 
						|
	     */
 | 
						|
	    if (cc_assign(name, &cc, &cct, first && !hx))
 | 
						|
		return 1;
 | 
						|
 | 
						|
	    hx = 1;
 | 
						|
	    ready = 0;
 | 
						|
 | 
						|
	    if (!*++argv || **argv != '-' ||
 | 
						|
		(**argv == '-' && (!argv[0][1] ||
 | 
						|
				   (argv[0][1] == '-' && !argv[0][2])))) {
 | 
						|
		/* No argument to +, which means do default completion */
 | 
						|
		if (isdef)
 | 
						|
		    zwarnnam(name,
 | 
						|
			    "recursive xor'd default completions not allowed",
 | 
						|
			    NULL, 0);
 | 
						|
		else
 | 
						|
		    cc->xor = &cc_default;
 | 
						|
	    } else {
 | 
						|
		/* more flags follow:  prepare to loop again */
 | 
						|
		cc->xor = (Compctl) zcalloc(sizeof(*cc));
 | 
						|
		cc = cc->xor;
 | 
						|
		memset((void *)&cct, 0, sizeof(cct));
 | 
						|
		cct.mask2 = CC_CCCONT;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (!ready && *argv && **argv == '-')
 | 
						|
	argv++;
 | 
						|
 | 
						|
    if (! (cct.mask & (CC_EXCMDS | CC_DISCMDS)))
 | 
						|
	cct.mask |= CC_EXCMDS;
 | 
						|
 | 
						|
    /* assign the last set of flags we parsed */
 | 
						|
    if (cc_assign(name, &cc, &cct, first && !hx))
 | 
						|
	return 1;
 | 
						|
 | 
						|
    *av = argv;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Handle the -x ... -- part of compctl. */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 | 
						|
{
 | 
						|
    char **argv = *av, *t, *tt, sav;
 | 
						|
    int n, l = 0, ready = 0;
 | 
						|
    Compcond m, c, o;
 | 
						|
    Compctl *next = &(cc->ext);
 | 
						|
 | 
						|
    while (!ready) {
 | 
						|
	/* o keeps track of or's, m remembers the starting condition,
 | 
						|
	 * c is the current condition being parsed
 | 
						|
	 */
 | 
						|
	o = m = c = (Compcond) zcalloc(sizeof(*c));
 | 
						|
	/* Loop over each condition:  something like 's[...][...], p[...]' */
 | 
						|
	for (t = *argv; *t;) {
 | 
						|
	    while (*t == ' ')
 | 
						|
		t++;
 | 
						|
	    /* First get the condition code */
 | 
						|
	    switch (*t) {
 | 
						|
	    case 'q':
 | 
						|
		c->type = CCT_QUOTE;
 | 
						|
		break;
 | 
						|
	    case 's':
 | 
						|
		c->type = CCT_CURSUF;
 | 
						|
		break;
 | 
						|
	    case 'S':
 | 
						|
		c->type = CCT_CURPRE;
 | 
						|
		break;
 | 
						|
	    case 'p':
 | 
						|
		c->type = CCT_POS;
 | 
						|
		break;
 | 
						|
	    case 'c':
 | 
						|
		c->type = CCT_CURSTR;
 | 
						|
		break;
 | 
						|
	    case 'C':
 | 
						|
		c->type = CCT_CURPAT;
 | 
						|
		break;
 | 
						|
	    case 'w':
 | 
						|
		c->type = CCT_WORDSTR;
 | 
						|
		break;
 | 
						|
	    case 'W':
 | 
						|
		c->type = CCT_WORDPAT;
 | 
						|
		break;
 | 
						|
	    case 'n':
 | 
						|
		c->type = CCT_CURSUB;
 | 
						|
		break;
 | 
						|
	    case 'N':
 | 
						|
		c->type = CCT_CURSUBC;
 | 
						|
		break;
 | 
						|
	    case 'm':
 | 
						|
		c->type = CCT_NUMWORDS;
 | 
						|
		break;
 | 
						|
	    case 'r':
 | 
						|
		c->type = CCT_RANGESTR;
 | 
						|
		break;
 | 
						|
	    case 'R':
 | 
						|
		c->type = CCT_RANGEPAT;
 | 
						|
		break;
 | 
						|
	    default:
 | 
						|
		t[1] = '\0';
 | 
						|
		zwarnnam(name, "unknown condition code: %s", t, 0);
 | 
						|
		zfree(m, sizeof(struct compcond));
 | 
						|
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	    /* Now get the arguments in square brackets */
 | 
						|
	    if (t[1] != '[') {
 | 
						|
		t[1] = '\0';
 | 
						|
		zwarnnam(name, "expected condition after condition code: %s", t, 0);
 | 
						|
		zfree(m, sizeof(struct compcond));
 | 
						|
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	    t++;
 | 
						|
	    /* First count how many or'd arguments there are,
 | 
						|
	     * marking the active ]'s and ,'s with unprintable characters.
 | 
						|
	     */
 | 
						|
	    for (n = 0, tt = t; *tt == '['; n++) {
 | 
						|
		for (l = 1, tt++; *tt && l; tt++)
 | 
						|
		    if (*tt == '\\' && tt[1])
 | 
						|
			tt++;
 | 
						|
		    else if (*tt == '[')
 | 
						|
			l++;
 | 
						|
		    else if (*tt == ']')
 | 
						|
			l--;
 | 
						|
		    else if (l == 1 && *tt == ',')
 | 
						|
			*tt = '\201';
 | 
						|
		if (tt[-1] == ']')
 | 
						|
		    tt[-1] = '\200';
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (l) {
 | 
						|
		t[1] = '\0';
 | 
						|
		zwarnnam(name, "error after condition code: %s", t, 0);
 | 
						|
		zfree(m, sizeof(struct compcond));
 | 
						|
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	    c->n = n;
 | 
						|
 | 
						|
	    /* Allocate space for all the arguments of the conditions */
 | 
						|
	    if (c->type == CCT_POS ||
 | 
						|
		c->type == CCT_NUMWORDS) {
 | 
						|
		c->u.r.a = (int *)zcalloc(n * sizeof(int));
 | 
						|
		c->u.r.b = (int *)zcalloc(n * sizeof(int));
 | 
						|
	    } else if (c->type == CCT_CURSUF ||
 | 
						|
		       c->type == CCT_CURPRE ||
 | 
						|
		       c->type == CCT_QUOTE)
 | 
						|
		c->u.s.s = (char **)zcalloc(n * sizeof(char *));
 | 
						|
 | 
						|
	    else if (c->type == CCT_RANGESTR ||
 | 
						|
		     c->type == CCT_RANGEPAT) {
 | 
						|
		c->u.l.a = (char **)zcalloc(n * sizeof(char *));
 | 
						|
		c->u.l.b = (char **)zcalloc(n * sizeof(char *));
 | 
						|
	    } else {
 | 
						|
		c->u.s.p = (int *)zcalloc(n * sizeof(int));
 | 
						|
		c->u.s.s = (char **)zcalloc(n * sizeof(char *));
 | 
						|
	    }
 | 
						|
	    /* Now loop over the actual arguments */
 | 
						|
	    for (l = 0; *t == '['; l++, t++) {
 | 
						|
		for (t++; *t && *t == ' '; t++);
 | 
						|
		tt = t;
 | 
						|
		if (c->type == CCT_POS ||
 | 
						|
		    c->type == CCT_NUMWORDS) {
 | 
						|
		    /* p[...] or m[...]:  one or two numbers expected */
 | 
						|
		    for (; *t && *t != '\201' && *t != '\200'; t++);
 | 
						|
		    if (!(sav = *t)) {
 | 
						|
			zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			freecompcond(m);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *t = '\0';
 | 
						|
		    c->u.r.a[l] = atoi(tt);
 | 
						|
		    /* Second argument is optional:  see if it's there */
 | 
						|
		    if (sav == '\200')
 | 
						|
			/* no:  copy first argument */
 | 
						|
			c->u.r.b[l] = c->u.r.a[l];
 | 
						|
		    else {
 | 
						|
			tt = ++t;
 | 
						|
			for (; *t && *t != '\200'; t++);
 | 
						|
			if (!*t) {
 | 
						|
			    zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			    freecompcond(m);
 | 
						|
			    return 1;
 | 
						|
			}
 | 
						|
			*t = '\0';
 | 
						|
			c->u.r.b[l] = atoi(tt);
 | 
						|
		    }
 | 
						|
		} else if (c->type == CCT_CURSUF ||
 | 
						|
			   c->type == CCT_CURPRE ||
 | 
						|
			   c->type == CCT_QUOTE) {
 | 
						|
		    /* -s[..] or -S[..]:  single string expected */
 | 
						|
		    for (; *t && *t != '\200'; t++)
 | 
						|
			if (*t == '\201')
 | 
						|
			    *t = ',';
 | 
						|
		    if (!*t) {
 | 
						|
			zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			freecompcond(m);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *t = '\0';
 | 
						|
		    c->u.s.s[l] = ztrdup(tt);
 | 
						|
		} else if (c->type == CCT_RANGESTR ||
 | 
						|
			   c->type == CCT_RANGEPAT) {
 | 
						|
		    int hc;
 | 
						|
 | 
						|
		    /* -r[..,..] or -R[..,..]:  two strings expected */
 | 
						|
		    for (; *t && *t != '\201' && *t != '\200'; t++);
 | 
						|
		    if (!*t) {
 | 
						|
			zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			freecompcond(m);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    hc = (*t == '\201');
 | 
						|
		    *t = '\0';
 | 
						|
		    c->u.l.a[l] = ztrdup(tt);
 | 
						|
		    if (hc) {
 | 
						|
			tt = ++t;
 | 
						|
			/* any more commas are text, not active */
 | 
						|
			for (; *t && *t != '\200'; t++)
 | 
						|
			    if (*t == '\201')
 | 
						|
				*t = ',';
 | 
						|
			if (!*t) {
 | 
						|
			    zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			    freecompcond(m);
 | 
						|
			    return 1;
 | 
						|
			}
 | 
						|
			*t = '\0';
 | 
						|
			c->u.l.b[l] = ztrdup(tt);
 | 
						|
		    }
 | 
						|
		    else
 | 
						|
			c->u.l.b[l] = NULL;
 | 
						|
		} else {
 | 
						|
		    /* remaining patterns are number followed by string */
 | 
						|
		    for (; *t && *t != '\200' && *t != '\201'; t++);
 | 
						|
		    if (!*t || *t == '\200') {
 | 
						|
			zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			freecompcond(m);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *t = '\0';
 | 
						|
		    c->u.s.p[l] = atoi(tt);
 | 
						|
		    tt = ++t;
 | 
						|
		    for (; *t && *t != '\200'; t++)
 | 
						|
			if (*t == '\201')
 | 
						|
			    *t = ',';
 | 
						|
		    if (!*t) {
 | 
						|
			zwarnnam(name, "error in condition", NULL, 0);
 | 
						|
			freecompcond(m);
 | 
						|
			return 1;
 | 
						|
		    }
 | 
						|
		    *t = '\0';
 | 
						|
		    c->u.s.s[l] = ztrdup(tt);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    while (*t == ' ')
 | 
						|
		t++;
 | 
						|
	    if (*t == ',') {
 | 
						|
		/* Another condition to `or' */
 | 
						|
		o->or = c = (Compcond) zcalloc(sizeof(*c));
 | 
						|
		o = c;
 | 
						|
		t++;
 | 
						|
	    } else if (*t) {
 | 
						|
		/* Another condition to `and' */
 | 
						|
		c->and = (Compcond) zcalloc(sizeof(*c));
 | 
						|
		c = c->and;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	/* Assign condition to current compctl */
 | 
						|
	*next = (Compctl) zcalloc(sizeof(*cc));
 | 
						|
	(*next)->cond = m;
 | 
						|
	argv++;
 | 
						|
	/* End of the condition; get the flags that go with it. */
 | 
						|
	if (get_compctl(name, &argv, *next, 0, isdef, 0))
 | 
						|
	    return 1;
 | 
						|
 	if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
 | 
						|
 	    /* default, first, or command completion finished */
 | 
						|
	    ready = 1;
 | 
						|
	else {
 | 
						|
	    /* see if we are looking for more conditions or are
 | 
						|
	     * ready to return (ready = 1)
 | 
						|
	     */
 | 
						|
	    if (!argv || !*argv || **argv != '-' ||
 | 
						|
		((!argv[0][1] || argv[0][1] == '+') && !argv[1])) {
 | 
						|
		zwarnnam(name, "missing command names", NULL, 0);
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	    if (!strcmp(*argv, "--"))
 | 
						|
		ready = 1;
 | 
						|
	    else if (!strcmp(*argv, "-+") && argv[1] &&
 | 
						|
		     !strcmp(argv[1], "--")) {
 | 
						|
		ready = 1;
 | 
						|
		argv++;
 | 
						|
	    }
 | 
						|
	    argv++;
 | 
						|
	    /* prepare to put the next lot of conditions on the end */
 | 
						|
	    next = &((*next)->next);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    /* save position at end of parsing */
 | 
						|
    *av = argv - 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
 | 
						|
{
 | 
						|
    /* Copy over the details from the values in cct to those in *ccptr */
 | 
						|
    Compctl cc;
 | 
						|
 | 
						|
    /* Handle assignment of new default or command completion */
 | 
						|
    if (reass && !(cclist & COMP_LIST)) {
 | 
						|
	/* if not listing */
 | 
						|
	if (cclist == (COMP_COMMAND|COMP_DEFAULT)
 | 
						|
	    || cclist == (COMP_COMMAND|COMP_FIRST)
 | 
						|
	    || cclist == (COMP_DEFAULT|COMP_FIRST)
 | 
						|
	    || cclist == COMP_SPECIAL) {
 | 
						|
 	    zwarnnam(name, "can't set -D, -T, and -C simultaneously", NULL, 0);
 | 
						|
	    /* ... because the following code wouldn't work. */
 | 
						|
	    return 1;
 | 
						|
	}
 | 
						|
	if (cclist & COMP_COMMAND) {
 | 
						|
	    /* command */
 | 
						|
	    *ccptr = &cc_compos;
 | 
						|
	    cc_reassign(*ccptr);
 | 
						|
	} else if (cclist & COMP_DEFAULT) {
 | 
						|
	    /* default */
 | 
						|
	    *ccptr = &cc_default;
 | 
						|
	    cc_reassign(*ccptr);
 | 
						|
 	} else if (cclist & COMP_FIRST) {
 | 
						|
 	    /* first */
 | 
						|
 	    *ccptr = &cc_first;
 | 
						|
 	    cc_reassign(*ccptr);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* Free the old compctl */
 | 
						|
    cc = *ccptr;
 | 
						|
    zsfree(cc->keyvar);
 | 
						|
    zsfree(cc->glob);
 | 
						|
    zsfree(cc->str);
 | 
						|
    zsfree(cc->func);
 | 
						|
    zsfree(cc->explain);
 | 
						|
    zsfree(cc->ylist);
 | 
						|
    zsfree(cc->prefix);
 | 
						|
    zsfree(cc->suffix);
 | 
						|
    zsfree(cc->subcmd);
 | 
						|
    zsfree(cc->substr);
 | 
						|
    zsfree(cc->withd);
 | 
						|
    zsfree(cc->hpat);
 | 
						|
    zsfree(cc->gname);
 | 
						|
    zsfree(cc->mstr);
 | 
						|
    freecmatcher(cc->matcher);
 | 
						|
 | 
						|
    /* and copy over the new stuff, (permanently) allocating
 | 
						|
     * space for strings.
 | 
						|
     */
 | 
						|
    cc->mask = cct->mask;
 | 
						|
    cc->mask2 = cct->mask2;
 | 
						|
    cc->keyvar = ztrdup(cct->keyvar);
 | 
						|
    cc->glob = ztrdup(cct->glob);
 | 
						|
    cc->str = ztrdup(cct->str);
 | 
						|
    cc->func = ztrdup(cct->func);
 | 
						|
    cc->explain = ztrdup(cct->explain);
 | 
						|
    cc->ylist = ztrdup(cct->ylist);
 | 
						|
    cc->prefix = ztrdup(cct->prefix);
 | 
						|
    cc->suffix = ztrdup(cct->suffix);
 | 
						|
    cc->subcmd = ztrdup(cct->subcmd);
 | 
						|
    cc->substr = ztrdup(cct->substr);
 | 
						|
    cc->withd = ztrdup(cct->withd);
 | 
						|
    cc->gname = ztrdup(cct->gname);
 | 
						|
    cc->hpat = ztrdup(cct->hpat);
 | 
						|
    cc->hnum = cct->hnum;
 | 
						|
    PERMALLOC {
 | 
						|
	cc->matcher = cpcmatcher(cct->matcher);
 | 
						|
    } LASTALLOC;
 | 
						|
    cc->mstr = ztrdup(cct->mstr);
 | 
						|
 | 
						|
    /* careful with extended completion:  it's already allocated */
 | 
						|
    cc->ext = cct->ext;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
cc_reassign(Compctl cc)
 | 
						|
{
 | 
						|
    /* Free up a new default or command completion:
 | 
						|
     * this is a hack to free up the parts which should be deleted,
 | 
						|
     * without removing the basic variable which is statically allocated
 | 
						|
     */
 | 
						|
    Compctl c2;
 | 
						|
 | 
						|
    c2 = (Compctl) zcalloc(sizeof *cc);
 | 
						|
    c2->xor = cc->xor;
 | 
						|
    c2->ext = cc->ext;
 | 
						|
    c2->refc = 1;
 | 
						|
 | 
						|
    freecompctl(c2);
 | 
						|
 | 
						|
    cc->ext = cc->xor = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Check if the given string is a pattern. If so, return one and tokenize *
 | 
						|
 * it. If not, we just remove the backslashes. */
 | 
						|
 | 
						|
static int
 | 
						|
compctl_name_pat(char **p)
 | 
						|
{
 | 
						|
    char *s = *p;
 | 
						|
 | 
						|
    tokenize(s = dupstring(s));
 | 
						|
    remnulargs(s);
 | 
						|
 | 
						|
    if (haswilds(s)) {
 | 
						|
	*p = s;
 | 
						|
	return 1;
 | 
						|
    } else
 | 
						|
	*p = rembslash(*p);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Delete the pattern compctl with the given name. */
 | 
						|
 | 
						|
static void
 | 
						|
delpatcomp(char *n)
 | 
						|
{
 | 
						|
    Patcomp p, q;
 | 
						|
 | 
						|
    for (q = 0, p = patcomps; p; q = p, p = p->next) {
 | 
						|
	if (!strcmp(n, p->pat)) {
 | 
						|
	    if (q)
 | 
						|
		q->next = p->next;
 | 
						|
	    else
 | 
						|
		patcomps = p->next;
 | 
						|
	    zsfree(p->pat);
 | 
						|
	    freecompctl(p->cc);
 | 
						|
	    free(p);
 | 
						|
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
compctl_process_cc(char **s, Compctl cc)
 | 
						|
{
 | 
						|
    Compctlp ccp;
 | 
						|
    char *n;
 | 
						|
 | 
						|
    if (cclist & COMP_REMOVE) {
 | 
						|
	/* Delete entries for the commands listed */
 | 
						|
	for (; *s; s++) {
 | 
						|
	    n = *s;
 | 
						|
	    if (compctl_name_pat(&n))
 | 
						|
		delpatcomp(n);
 | 
						|
	    else if ((ccp = (Compctlp) compctltab->removenode(compctltab, n)))
 | 
						|
		compctltab->freenode((HashNode) ccp);
 | 
						|
	}
 | 
						|
    } else {
 | 
						|
	/* Add the compctl just read to the hash table */
 | 
						|
 | 
						|
	cc->refc = 0;
 | 
						|
	for (; *s; s++) {
 | 
						|
	    n = *s;
 | 
						|
 | 
						|
	    cc->refc++;
 | 
						|
	    if (compctl_name_pat(&n)) {
 | 
						|
		Patcomp pc;
 | 
						|
 | 
						|
		delpatcomp(n);
 | 
						|
		pc = zalloc(sizeof *pc);
 | 
						|
		pc->pat = ztrdup(n);
 | 
						|
		pc->cc = cc;
 | 
						|
		pc->next = patcomps;
 | 
						|
		patcomps = pc;
 | 
						|
	    } else {
 | 
						|
		ccp = (Compctlp) zalloc(sizeof *ccp);
 | 
						|
		ccp->cc = cc;
 | 
						|
		compctltab->addnode(compctltab, ztrdup(n), ccp);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Print a `compctl' */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
printcompctl(char *s, Compctl cc, int printflags, int ispat)
 | 
						|
{
 | 
						|
    Compctl cc2;
 | 
						|
    char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
 | 
						|
    char *mss = " pcCwWsSnNmrRq";
 | 
						|
    unsigned long t = 0x7fffffff;
 | 
						|
    unsigned long flags = cc->mask, flags2 = cc->mask2;
 | 
						|
    unsigned long oldshowmask;
 | 
						|
 | 
						|
    /* Printflags is used outside the standard compctl commands*/
 | 
						|
    if (printflags & PRINT_LIST)
 | 
						|
	cclist |= COMP_LIST;
 | 
						|
    else if (printflags & PRINT_TYPE)
 | 
						|
	cclist &= ~COMP_LIST;
 | 
						|
 | 
						|
    if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
 | 
						|
	flags &= ~CC_EXCMDS;
 | 
						|
 | 
						|
    /* If showmask is non-zero, then print only those *
 | 
						|
     * commands with that flag set.                   */
 | 
						|
    if (showmask && !(flags & showmask))
 | 
						|
	return;
 | 
						|
 | 
						|
    /* Temporarily clear showmask in case we make *
 | 
						|
     * recursive calls to printcompctl.           */
 | 
						|
    oldshowmask = showmask;
 | 
						|
    showmask = 0;
 | 
						|
 | 
						|
    /* print either command name or start of compctl command itself */
 | 
						|
    if (s) {
 | 
						|
	if (cclist & COMP_LIST) {
 | 
						|
	    printf("compctl");
 | 
						|
	    if (cc == &cc_compos)
 | 
						|
		printf(" -C");
 | 
						|
	    if (cc == &cc_default)
 | 
						|
		printf(" -D");
 | 
						|
	    if (cc == &cc_first)
 | 
						|
		printf(" -T");
 | 
						|
	} else if (ispat) {
 | 
						|
	    char *p = dupstring(s);
 | 
						|
 | 
						|
	    untokenize(p);
 | 
						|
	    quotedzputs(p, stdout);
 | 
						|
	} else
 | 
						|
	    quotedzputs(bslashquote(s, NULL, 0), stdout);
 | 
						|
    }
 | 
						|
 | 
						|
    /* loop through flags w/o args that are set, printing them if so */
 | 
						|
    if ((flags & t) || (flags2 & (CC_UNIQALL | CC_UNIQCON))) {
 | 
						|
	printf(" -");
 | 
						|
	if ((flags & (CC_ALREG | CC_ALGLOB)) == (CC_ALREG | CC_ALGLOB))
 | 
						|
	    putchar('a'), flags &= ~(CC_ALREG | CC_ALGLOB);
 | 
						|
	while (*css) {
 | 
						|
	    if (flags & t & 1)
 | 
						|
		putchar(*css);
 | 
						|
	    css++;
 | 
						|
	    flags >>= 1;
 | 
						|
	    t >>= 1;
 | 
						|
	}
 | 
						|
	if (flags2 & CC_UNIQALL)
 | 
						|
	    putchar('1');
 | 
						|
	else if (flags2 & CC_UNIQCON)
 | 
						|
	    putchar('2');
 | 
						|
    }
 | 
						|
    if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) {
 | 
						|
	printf(" -t");
 | 
						|
	if (flags2 & CC_XORCONT)
 | 
						|
	    putchar('+');
 | 
						|
	if (flags2 & CC_PATCONT)
 | 
						|
	    putchar('-');
 | 
						|
	if (flags2 & CC_DEFCONT)
 | 
						|
	    putchar('x');
 | 
						|
    } else if (!(flags2 & CC_CCCONT))
 | 
						|
	printf(" -tn");
 | 
						|
    /* now flags with arguments */
 | 
						|
    printif(cc->mstr, 'M');
 | 
						|
    if (flags2 & CC_NOSORT)
 | 
						|
	printif(cc->gname, 'V');
 | 
						|
    else
 | 
						|
	printif(cc->gname, 'J');
 | 
						|
    printif(cc->keyvar, 'k');
 | 
						|
    printif(cc->func, 'K');
 | 
						|
    printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
 | 
						|
    printif(cc->ylist, 'y');
 | 
						|
    printif(cc->prefix, 'P');
 | 
						|
    printif(cc->suffix, 'S');
 | 
						|
    printif(cc->glob, 'g');
 | 
						|
    printif(cc->str, 's');
 | 
						|
    printif(cc->subcmd, 'l');
 | 
						|
    printif(cc->substr, 'h');
 | 
						|
    printif(cc->withd, 'W');
 | 
						|
    if (cc->hpat) {
 | 
						|
	printf(" -H %d ", cc->hnum);
 | 
						|
	quotedzputs(cc->hpat, stdout);
 | 
						|
    }
 | 
						|
 | 
						|
    /* now the -x ... -- extended completion part */
 | 
						|
    if (cc->ext) {
 | 
						|
	Compcond c, o;
 | 
						|
	int i;
 | 
						|
 | 
						|
	cc2 = cc->ext;
 | 
						|
	printf(" -x");
 | 
						|
 | 
						|
	while (cc2) {
 | 
						|
	    /* loop over conditions */
 | 
						|
	    c = cc2->cond;
 | 
						|
 | 
						|
	    printf(" '");
 | 
						|
	    for (c = cc2->cond; c;) {
 | 
						|
		/* loop over or's */
 | 
						|
		o = c->or;
 | 
						|
		while (c) {
 | 
						|
		    /* loop over and's */
 | 
						|
		    putchar(mss[c->type]);
 | 
						|
 | 
						|
		    for (i = 0; i < c->n; i++) {
 | 
						|
			/* for all [...]'s of a given condition */
 | 
						|
			putchar('[');
 | 
						|
			switch (c->type) {
 | 
						|
			case CCT_POS:
 | 
						|
			case CCT_NUMWORDS:
 | 
						|
			    printf("%d,%d", c->u.r.a[i], c->u.r.b[i]);
 | 
						|
			    break;
 | 
						|
			case CCT_CURSUF:
 | 
						|
			case CCT_CURPRE:
 | 
						|
			case CCT_QUOTE:
 | 
						|
			    printqt(c->u.s.s[i]);
 | 
						|
			    break;
 | 
						|
			case CCT_RANGESTR:
 | 
						|
			case CCT_RANGEPAT:
 | 
						|
			    printqt(c->u.l.a[i]);
 | 
						|
			    putchar(',');
 | 
						|
			    printqt(c->u.l.b[i]);
 | 
						|
			    break;
 | 
						|
			default:
 | 
						|
			    printf("%d,", c->u.s.p[i]);
 | 
						|
			    printqt(c->u.s.s[i]);
 | 
						|
			}
 | 
						|
			putchar(']');
 | 
						|
		    }
 | 
						|
		    if ((c = c->and))
 | 
						|
			putchar(' ');
 | 
						|
		}
 | 
						|
		if ((c = o))
 | 
						|
		    printf(" , ");
 | 
						|
	    }
 | 
						|
	    putchar('\'');
 | 
						|
	    c = cc2->cond;
 | 
						|
	    cc2->cond = NULL;
 | 
						|
	    /* now print the flags for the current condition */
 | 
						|
	    printcompctl(NULL, cc2, 0, 0);
 | 
						|
	    cc2->cond = c;
 | 
						|
	    if ((cc2 = (Compctl) (cc2->next)))
 | 
						|
		printf(" -");
 | 
						|
	}
 | 
						|
	if (cclist & COMP_LIST)
 | 
						|
	    printf(" --");
 | 
						|
    }
 | 
						|
    if (cc && cc->xor) {
 | 
						|
	/* print xor'd (+) completions */
 | 
						|
	printf(" +");
 | 
						|
	if (cc->xor != &cc_default)
 | 
						|
	    printcompctl(NULL, cc->xor, 0, 0);
 | 
						|
    }
 | 
						|
    if (s) {
 | 
						|
	if ((cclist & COMP_LIST) && (cc != &cc_compos)
 | 
						|
	    && (cc != &cc_default) && (cc != &cc_first)) {
 | 
						|
	    if(s[0] == '-' || s[0] == '+')
 | 
						|
		printf(" -");
 | 
						|
	    putchar(' ');
 | 
						|
	    if (ispat) {
 | 
						|
		char *p = dupstring(s);
 | 
						|
 | 
						|
		untokenize(p);
 | 
						|
		quotedzputs(p, stdout);
 | 
						|
	    } else {
 | 
						|
		char *p = dupstring(s);
 | 
						|
 | 
						|
		untokenize(p);
 | 
						|
		quotedzputs(bslashquote(p, NULL, 0), stdout);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	putchar('\n');
 | 
						|
    }
 | 
						|
 | 
						|
    showmask = oldshowmask;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
printcompctlp(HashNode hn, int printflags)
 | 
						|
{
 | 
						|
    Compctlp ccp = (Compctlp) hn;
 | 
						|
 | 
						|
    /* Function needed for use by scanhashtable() */
 | 
						|
    printcompctl(ccp->nam, ccp->cc, printflags, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Main entry point for the `compctl' builtin */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
bin_compctl(char *name, char **argv, char *ops, int func)
 | 
						|
{
 | 
						|
    Compctl cc = NULL;
 | 
						|
    int ret = 0;
 | 
						|
 | 
						|
    /* clear static flags */
 | 
						|
    cclist = 0;
 | 
						|
    showmask = 0;
 | 
						|
 | 
						|
    /* Parse all the arguments */
 | 
						|
    if (*argv) {
 | 
						|
	/* Let's see if this is a global matcher definition. */
 | 
						|
	if ((ret = get_gmatcher(name, argv)))
 | 
						|
	    return ret - 1;
 | 
						|
 | 
						|
	cc = (Compctl) zcalloc(sizeof(*cc));
 | 
						|
	if (get_compctl(name, &argv, cc, 1, 0, 0)) {
 | 
						|
	    freecompctl(cc);
 | 
						|
	    return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* remember flags for printing */
 | 
						|
	showmask = cc->mask;
 | 
						|
	if ((showmask & CC_EXCMDS) && !(showmask & CC_DISCMDS))
 | 
						|
	    showmask &= ~CC_EXCMDS;
 | 
						|
 | 
						|
	/* if no command arguments or just listing, we don't want cc */
 | 
						|
	if (!*argv || (cclist & COMP_LIST))
 | 
						|
	    freecompctl(cc);
 | 
						|
    }
 | 
						|
 | 
						|
    /* If no commands and no -C, -T, or -D, print all the compctl's *
 | 
						|
     * If some flags (other than -C, -T, or -D) were given, then    *
 | 
						|
     * only print compctl containing those flags.                   */
 | 
						|
    if (!*argv && !(cclist & (COMP_SPECIAL|COMP_LISTMATCH))) {
 | 
						|
	Patcomp pc;
 | 
						|
 | 
						|
	for (pc = patcomps; pc; pc = pc->next)
 | 
						|
	    printcompctl(pc->pat, pc->cc, 0, 1);
 | 
						|
 | 
						|
	scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
 | 
						|
	printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0, 0);
 | 
						|
	printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
 | 
						|
 	printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
 | 
						|
	print_gmatcher((cclist & COMP_LIST));
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    /* If we're listing and we've made it to here, then there are arguments *
 | 
						|
     * or a COMP_SPECIAL flag (-D, -C, -T), so print only those.            */
 | 
						|
    if (cclist & COMP_LIST) {
 | 
						|
	HashNode hn;
 | 
						|
	char **ptr, *n;
 | 
						|
 | 
						|
	showmask = 0;
 | 
						|
	for (ptr = argv; *ptr; ptr++) {
 | 
						|
	    n = *ptr;
 | 
						|
	    if (compctl_name_pat(&n)) {
 | 
						|
		Patcomp pc;
 | 
						|
 | 
						|
		for (pc = patcomps; pc; pc = pc->next)
 | 
						|
		    if (!strcmp(n, pc->pat)) {
 | 
						|
			printcompctl(pc->pat, pc->cc, 0, 1);
 | 
						|
			n = NULL;
 | 
						|
			break;
 | 
						|
		    }
 | 
						|
	    } else if ((hn = compctltab->getnode(compctltab, n))) {
 | 
						|
		compctltab->printnode(hn, 0);
 | 
						|
		n = NULL;
 | 
						|
	    }
 | 
						|
	    if (n) {
 | 
						|
		zwarnnam(name, "no compctl defined for %s", n, 0);
 | 
						|
		ret = 1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (cclist & COMP_COMMAND)
 | 
						|
	    printcompctl("", &cc_compos, 0, 0);
 | 
						|
	if (cclist & COMP_DEFAULT)
 | 
						|
	    printcompctl("", &cc_default, 0, 0);
 | 
						|
	if (cclist & COMP_FIRST)
 | 
						|
	    printcompctl("", &cc_first, 0, 0);
 | 
						|
	if (cclist & COMP_LISTMATCH)
 | 
						|
	    print_gmatcher(COMP_LIST);
 | 
						|
	return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Assign the compctl to the commands given */
 | 
						|
    if (*argv) {
 | 
						|
	if(cclist & COMP_SPECIAL)
 | 
						|
	    /* Ideally we'd handle this properly, setting both the *
 | 
						|
	     * special and normal completions.  For the moment,    *
 | 
						|
	     * this is better than silently failing.               */
 | 
						|
	    zwarnnam(name, "extraneous commands ignored", NULL, 0);
 | 
						|
	else
 | 
						|
	    compctl_process_cc(argv, cc);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* Flags for makecomplist*(). Things not to do. */
 | 
						|
 | 
						|
#define CFN_FIRST   1
 | 
						|
#define CFN_DEFAULT 2
 | 
						|
 | 
						|
static int
 | 
						|
bin_compcall(char *name, char **argv, char *ops, int func)
 | 
						|
{
 | 
						|
    if (incompfunc != 1) {
 | 
						|
	zerrnam(name, "can only be called from completion function", NULL, 0);
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) |
 | 
						|
			   (ops['D'] ? 0 : CFN_DEFAULT));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Functions to generate matches.
 | 
						|
 */
 | 
						|
 | 
						|
/* A pointer to the compctl we are using. */
 | 
						|
 | 
						|
static Compctl curcc;
 | 
						|
 | 
						|
/* A list of all compctls we have already used. */
 | 
						|
 | 
						|
static LinkList ccused, lastccused;
 | 
						|
 | 
						|
/* A stack of currently used compctls. */
 | 
						|
 | 
						|
static LinkList ccstack;
 | 
						|
 | 
						|
/* The beginning and end of a word range to be used by -l. */
 | 
						|
 | 
						|
static int brange, erange;
 | 
						|
 | 
						|
/* This is used to detect when and what to continue. */
 | 
						|
 | 
						|
static unsigned long ccont;
 | 
						|
 | 
						|
/* Two patterns used when doing glob-completion.  The first one is built *
 | 
						|
 * from the whole word we are completing and the second one from that    *
 | 
						|
 * part of the word that was identified as a possible filename.          */
 | 
						|
 | 
						|
static Patprog patcomp, filecomp;
 | 
						|
 | 
						|
/* We store the following prefixes/suffixes:                               *
 | 
						|
 * lpre/lsuf -- what's on the line                                         *
 | 
						|
 * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
 | 
						|
 * ppre/psuf   -- the path prefix/suffix                                   *
 | 
						|
 * lppre/lpsuf -- the path prefix/suffix, unexpanded                       *
 | 
						|
 * fpre/fsuf   -- prefix/suffix of the pathname component the cursor is in *
 | 
						|
 * prpre       -- ppre in expanded form usable for opendir                 *
 | 
						|
 * qipre, qisuf-- ingnored quoted string                                   *
 | 
						|
 *                                                                         *
 | 
						|
 * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
 | 
						|
 * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
 | 
						|
 | 
						|
static char *lpre, *lsuf;
 | 
						|
static char *rpre, *rsuf;
 | 
						|
static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
 | 
						|
static char *fpre, *fsuf;
 | 
						|
static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
 | 
						|
static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 | 
						|
static int noreal;
 | 
						|
 | 
						|
/* This is either zero or equal to the special character the word we are *
 | 
						|
 * trying to complete starts with (e.g. Tilde or Equals).                */
 | 
						|
 | 
						|
static char ic;
 | 
						|
 | 
						|
/* This variable says what we are currently adding to the list of matches. */
 | 
						|
 | 
						|
static int addwhat;
 | 
						|
 | 
						|
/* Convenience macro for calling bslashquote() (formerly quotename()). *
 | 
						|
 * This uses the instring variable above.                              */
 | 
						|
 | 
						|
#define quotename(s, e) bslashquote(s, e, instring)
 | 
						|
 | 
						|
/* Hook functions */
 | 
						|
 | 
						|
static int
 | 
						|
ccmakehookfn(Hookdef dummy, struct ccmakedat *dat)
 | 
						|
{
 | 
						|
    makecomplistglobal(dat->str, dat->incmd, dat->lst, 0);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ccbeforehookfn(Hookdef dummy, void *zdup)
 | 
						|
{
 | 
						|
    ccused = newlinklist();
 | 
						|
    ccstack = newlinklist();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ccafterhookfn(Hookdef dummy, void *zdup)
 | 
						|
{
 | 
						|
    LinkNode n;
 | 
						|
 | 
						|
    if (zdup) {
 | 
						|
	if (lastccused)
 | 
						|
	    freelinklist(lastccused, (FreeFunc) freecompctl);
 | 
						|
 | 
						|
	PERMALLOC {
 | 
						|
	    lastccused = newlinklist();
 | 
						|
	    for (n = firstnode(ccused); n; incnode(n))
 | 
						|
		addlinknode(lastccused, getdata(n));
 | 
						|
	} LASTALLOC;
 | 
						|
    } else
 | 
						|
	for (n = firstnode(ccused); n; incnode(n))
 | 
						|
	    if (((Compctl) getdata(n)) != &cc_dummy)
 | 
						|
		freecompctl((Compctl) getdata(n));
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This adds a match to the list of matches.  The string to add is given   *
 | 
						|
 * in s, the type of match is given in the global variable addwhat and     *
 | 
						|
 * the parameter t (if not NULL) is a pointer to a hash node node which    *
 | 
						|
 * may be used to give other information to this function.                 *
 | 
						|
 *                                                                         *
 | 
						|
 * addwhat contains either one of the special values (negative, see below) *
 | 
						|
 * or the inclusive OR of some of the CC_* flags used for compctls.        */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
addmatch(char *s, char *t)
 | 
						|
{
 | 
						|
    int isfile = 0, isalt = 0, isexact;
 | 
						|
    char *ms = NULL, *tt;
 | 
						|
    HashNode hn;
 | 
						|
    Param pm;
 | 
						|
    Cline lc = NULL;
 | 
						|
    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
 | 
						|
 | 
						|
    for (bp = brbeg; bp; bp = bp->next)
 | 
						|
	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
 | 
						|
    for (bp = brend; bp; bp = bp->next)
 | 
						|
	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
 | 
						|
 | 
						|
    /*
 | 
						|
     * addwhat: -5 is for files,
 | 
						|
     *          -6 is for glob expansions,
 | 
						|
     *          -8 is for executable files (e.g. command paths),
 | 
						|
     *          -9 is for parameters
 | 
						|
     *          -7 is for command names (from cmdnamtab)
 | 
						|
     *          -4 is for a cdable parameter
 | 
						|
     *          -3 is for executable command names.
 | 
						|
     *          -2 is for anything unquoted
 | 
						|
     *          -1 is for other file specifications
 | 
						|
     *          (things with `~' or `=' at the beginning, ...).
 | 
						|
     */
 | 
						|
 | 
						|
    /* Just to make the code cleaner */
 | 
						|
    hn = (HashNode) t;
 | 
						|
    pm = (Param) t;
 | 
						|
 | 
						|
    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
 | 
						|
	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
 | 
						|
	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
 | 
						|
 | 
						|
	while (bpl && bpl->curpos < ppl)
 | 
						|
	    bpl = bpl->next;
 | 
						|
	while (bsl && bsl->curpos < psl)
 | 
						|
	    bsl = bsl->next;
 | 
						|
 | 
						|
	if ((addwhat == CC_FILES ||
 | 
						|
	     addwhat == -5) && !*psuf) {
 | 
						|
	    /* If this is a filename, do the fignore check. */
 | 
						|
	    char **pt = fignore;
 | 
						|
	    int filell, sl = strlen(s);
 | 
						|
 | 
						|
	    for (isalt = 0; !isalt && *pt; pt++)
 | 
						|
		if ((filell = strlen(*pt)) < sl &&
 | 
						|
		    !strcmp(*pt, s + sl - filell))
 | 
						|
		    isalt = 1;
 | 
						|
	}
 | 
						|
	ms = ((addwhat == CC_FILES || addwhat == -6 ||
 | 
						|
	       addwhat == -5 || addwhat == -8) ?
 | 
						|
	      comp_match(tildequote(qfpre, 1), multiquote(qfsuf, 1),
 | 
						|
			 s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
 | 
						|
			 &bpl, ppl ,&bsl, psl, &isexact) :
 | 
						|
	      comp_match(multiquote(fpre, 1), multiquote(fsuf, 1),
 | 
						|
			 s, filecomp, &lc, 0,
 | 
						|
			 &bpl, ppl, &bsl, psl, &isexact));
 | 
						|
	if (!ms)
 | 
						|
	    return;
 | 
						|
 | 
						|
	if (addwhat == -7 && !findcmd(s, 0))
 | 
						|
	    return;
 | 
						|
	isfile = CMF_FILE;
 | 
						|
    } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
 | 
						|
	      (addwhat == -3 && !(hn->flags & DISABLED)) ||
 | 
						|
	      (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
 | 
						|
	       !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/')    ||
 | 
						|
	      (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) ||
 | 
						|
	      (addwhat > 0 &&
 | 
						|
	       ((!(hn->flags & PM_UNSET) &&
 | 
						|
		 (((addwhat & CC_ARRAYS)    &&  (hn->flags & PM_ARRAY))    ||
 | 
						|
		  ((addwhat & CC_INTVARS)   &&  (hn->flags & PM_INTEGER))  ||
 | 
						|
		  ((addwhat & CC_ENVVARS)   &&  (hn->flags & PM_EXPORTED)) ||
 | 
						|
		  ((addwhat & CC_SCALARS)   &&  (hn->flags & PM_SCALAR))   ||
 | 
						|
		  ((addwhat & CC_READONLYS) &&  (hn->flags & PM_READONLY)) ||
 | 
						|
		  ((addwhat & CC_SPECIALS)  &&  (hn->flags & PM_SPECIAL))  ||
 | 
						|
		  ((addwhat & CC_PARAMS)    && !(hn->flags & PM_EXPORTED))) &&
 | 
						|
		 !pm->level) ||
 | 
						|
		((( addwhat & CC_SHFUNCS)				  ||
 | 
						|
		  ( addwhat & CC_BUILTINS)				  ||
 | 
						|
		  ( addwhat & CC_EXTCMDS)				  ||
 | 
						|
		  ( addwhat & CC_RESWDS)				  ||
 | 
						|
		  ((addwhat & CC_ALREG)   && !(hn->flags & ALIAS_GLOBAL)) ||
 | 
						|
		  ((addwhat & CC_ALGLOB)  &&  (hn->flags & ALIAS_GLOBAL))) &&
 | 
						|
		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
 | 
						|
		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 | 
						|
		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
 | 
						|
	char *p1, *s1, *p2, *s2;
 | 
						|
 | 
						|
	if (addwhat == CC_QUOTEFLAG) {
 | 
						|
	    p1 = qrpre; s1 = qrsuf;
 | 
						|
	    p2 = rpre;  s2 = rsuf;
 | 
						|
	} else {
 | 
						|
	    p1 = qlpre; s1 = qlsuf;
 | 
						|
	    p2 = lpre;  s2 = lsuf;
 | 
						|
	}
 | 
						|
	p1 = multiquote(p1, 1); s1 = multiquote(s1, 1);
 | 
						|
	p2 = multiquote(p2, 1); s2 = multiquote(s2, 1);
 | 
						|
	bpt = bpl;
 | 
						|
	bst = bsl;
 | 
						|
 | 
						|
	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
 | 
						|
			      (addwhat == CC_QUOTEFLAG),
 | 
						|
			      &bpl, strlen(p1), &bsl, strlen(s1),
 | 
						|
			      &isexact))) {
 | 
						|
	    bpl = bpt;
 | 
						|
	    bsl = bst;
 | 
						|
	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
 | 
						|
				  (addwhat == CC_QUOTEFLAG),
 | 
						|
				  &bpl, strlen(p2), &bsl, strlen(s2),
 | 
						|
				  &isexact)))
 | 
						|
		return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (!ms)
 | 
						|
	return;
 | 
						|
    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
 | 
						|
		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
 | 
						|
		   prpre, 
 | 
						|
		   (isfile ? lppre : NULL), NULL,
 | 
						|
		   (isfile ? lpsuf : NULL), NULL,
 | 
						|
		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
 | 
						|
		   (mflags | isfile), isexact);
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
maketildelist(void)
 | 
						|
{
 | 
						|
    /* add all the usernames to the named directory table */
 | 
						|
    nameddirtab->filltable(nameddirtab);
 | 
						|
 | 
						|
    scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
 | 
						|
		  addhnmatch, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* This does the check for compctl -x `n' and `N' patterns. */
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
getcpat(char *str, int cpatindex, char *cpat, int class)
 | 
						|
{
 | 
						|
    char *s, *t, *p;
 | 
						|
    int d = 0;
 | 
						|
 | 
						|
    if (!str || !*str)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    cpat = rembslash(cpat);
 | 
						|
 | 
						|
    if (!cpatindex)
 | 
						|
	cpatindex++, d = 0;
 | 
						|
    else if ((d = (cpatindex < 0)))
 | 
						|
	cpatindex = -cpatindex;
 | 
						|
 | 
						|
    for (s = d ? str + strlen(str) - 1 : str;
 | 
						|
	 d ? (s >= str) : *s;
 | 
						|
	 d ? s-- : s++) {
 | 
						|
	for (t = s, p = cpat; *t && *p; p++) {
 | 
						|
	    if (class) {
 | 
						|
		if (*p == *s && !--cpatindex)
 | 
						|
		    return (int)(s - str + 1);
 | 
						|
	    } else if (*t++ != *p)
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (!class && !*p && !--cpatindex)
 | 
						|
	    return t - str;
 | 
						|
    }
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Dump a hash table (without sorting).  For each element the addmatch  *
 | 
						|
 * function is called and at the beginning the addwhat variable is set. *
 | 
						|
 * This could be done using scanhashtable(), but this is easy and much  *
 | 
						|
 * more efficient.                                                      */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
dumphashtable(HashTable ht, int what)
 | 
						|
{
 | 
						|
    HashNode hn;
 | 
						|
    int i;
 | 
						|
 | 
						|
    addwhat = what;
 | 
						|
 | 
						|
    for (i = 0; i < ht->hsize; i++)
 | 
						|
	for (hn = ht->nodes[i]; hn; hn = hn->next)
 | 
						|
	    addmatch(hn->nam, (char *) hn);
 | 
						|
}
 | 
						|
 | 
						|
/* ScanFunc used by maketildelist() et al. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
addhnmatch(HashNode hn, int flags)
 | 
						|
{
 | 
						|
    addmatch(hn->nam, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/* Perform expansion on the given string and return the result. *
 | 
						|
 * During this errors are not reported.                         */
 | 
						|
 | 
						|
/**/
 | 
						|
static char *
 | 
						|
getreal(char *str)
 | 
						|
{
 | 
						|
    LinkList l = newlinklist();
 | 
						|
    int ne = noerrs;
 | 
						|
 | 
						|
    noerrs = 1;
 | 
						|
    addlinknode(l, dupstring(str));
 | 
						|
    prefork(l, 0);
 | 
						|
    noerrs = ne;
 | 
						|
    if (!errflag && nonempty(l) &&
 | 
						|
	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
 | 
						|
	return dupstring(peekfirst(l));
 | 
						|
    errflag = 0;
 | 
						|
 | 
						|
    return dupstring(str);
 | 
						|
}
 | 
						|
 | 
						|
/* This reads a directory and adds the files to the list of  *
 | 
						|
 * matches.  The parameters say which files should be added. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
gen_matches_files(int dirs, int execs, int all)
 | 
						|
{
 | 
						|
    DIR *d;
 | 
						|
    struct stat buf;
 | 
						|
    char *n, p[PATH_MAX], *q = NULL, *e;
 | 
						|
    LinkList l = NULL;
 | 
						|
    int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
 | 
						|
 | 
						|
    opts[NULLGLOB] = 1;
 | 
						|
 | 
						|
    if (*psuf) {
 | 
						|
	/* If there is a path suffix, check if it doesn't have a `*' or *
 | 
						|
	 * `)' at the end (this is used to determine if we should use   *
 | 
						|
	 * globbing).                                                   */
 | 
						|
	q = psuf + strlen(psuf) - 1;
 | 
						|
	ns = !(*q == Star || *q == Outpar);
 | 
						|
	l = newlinklist();
 | 
						|
	/* And generate only directory names. */
 | 
						|
	dirs = 1;
 | 
						|
	all = execs = 0;
 | 
						|
    }
 | 
						|
    /* Open directory. */
 | 
						|
    if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
 | 
						|
	/* If we search only special files, prepare a path buffer for stat. */
 | 
						|
	if (!all && prpre) {
 | 
						|
	    strcpy(p, prpre);
 | 
						|
	    q = p + strlen(prpre);
 | 
						|
	}
 | 
						|
	/* Fine, now read the directory. */
 | 
						|
	while ((n = zreaddir(d, 1)) && !errflag) {
 | 
						|
	    /* Ignore files beginning with `.' unless the thing we found on *
 | 
						|
	     * the command line also starts with a dot or GLOBDOTS is set.  */
 | 
						|
	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
 | 
						|
		addwhat = execs ? -8 : -5;
 | 
						|
		if (filecomp)
 | 
						|
		    /* If we have a pattern for the filename check, use it. */
 | 
						|
		    test = pattry(filecomp, n);
 | 
						|
		else {
 | 
						|
		    /* Otherwise use the prefix and suffix strings directly. */
 | 
						|
		    e = n + strlen(n) - fsl;
 | 
						|
		    if ((test = !strncmp(n, fpre, fpl)))
 | 
						|
			test = !strcmp(e, fsuf);
 | 
						|
		    if (!test && mstack) {
 | 
						|
			test = 1;
 | 
						|
			addwhat = CC_FILES;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		/* Filename didn't match? */
 | 
						|
		if (!test)
 | 
						|
		    continue;
 | 
						|
		if (!all) {
 | 
						|
		    /* We still have to check the file type, so prepare *
 | 
						|
		     * the path buffer by appending the filename.       */
 | 
						|
		    strcpy(q, n);
 | 
						|
		    /* And do the stat. */
 | 
						|
		    if (stat(p, &buf) < 0)
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (all ||
 | 
						|
		    (dirs && S_ISDIR(buf.st_mode)) ||
 | 
						|
		    (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
 | 
						|
		    /* If we want all files or the file has the right type... */
 | 
						|
		    if (*psuf) {
 | 
						|
			/* We have to test for a path suffix. */
 | 
						|
			int o = strlen(p), tt;
 | 
						|
 | 
						|
			/* Append it to the path buffer. */
 | 
						|
			strcpy(p + o, psuf);
 | 
						|
 | 
						|
			/* Do we have to use globbing? */
 | 
						|
			if (ispattern ||
 | 
						|
			    (ns && comppatmatch && *comppatmatch)) {
 | 
						|
			    /* Yes, so append a `*' if needed. */
 | 
						|
			    if (ns && comppatmatch && *comppatmatch == '*') {
 | 
						|
				int tl = strlen(p);
 | 
						|
 | 
						|
				p[tl] = Star;
 | 
						|
				p[tl + 1] = '\0';
 | 
						|
			    }
 | 
						|
			    /* Do the globbing... */
 | 
						|
			    remnulargs(p);
 | 
						|
			    addlinknode(l, p);
 | 
						|
			    globlist(l);
 | 
						|
			    /* And see if that produced a filename. */
 | 
						|
			    tt = nonempty(l);
 | 
						|
			    while (ugetnode(l));
 | 
						|
			} else
 | 
						|
			    /* Otherwise just check, if we have access *
 | 
						|
			     * to the file.                            */
 | 
						|
			    tt = !access(p, F_OK);
 | 
						|
 | 
						|
			p[o] = '\0';
 | 
						|
			if (tt)
 | 
						|
			    /* Ok, we can add the filename to the *
 | 
						|
			     * list of matches.                   */
 | 
						|
			    addmatch(dupstring(n), NULL);
 | 
						|
		    } else
 | 
						|
			/* We want all files, so just add the name *
 | 
						|
			 * to the matches.                         */
 | 
						|
			addmatch(dupstring(n), NULL);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	closedir(d);
 | 
						|
    }
 | 
						|
    opts[NULLGLOB] = ng;
 | 
						|
    addwhat = aw;
 | 
						|
}
 | 
						|
 | 
						|
/* This returns the node with the given data. */
 | 
						|
/* ...should probably be moved to linklist.c. */
 | 
						|
 | 
						|
static LinkNode
 | 
						|
findnode(LinkList list, void *dat)
 | 
						|
{
 | 
						|
    LinkNode tmp = list->first;
 | 
						|
 | 
						|
    while (tmp && tmp->dat != dat) tmp = tmp->next;
 | 
						|
 | 
						|
    return tmp;
 | 
						|
}
 | 
						|
 | 
						|
/* A simple counter to avoid endless recursion between old and new style *
 | 
						|
 * completion. */
 | 
						|
 | 
						|
static int cdepth = 0;
 | 
						|
 | 
						|
#define MAX_CDEPTH 16
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
makecomplistctl(int flags)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (cdepth == MAX_CDEPTH)
 | 
						|
	return 0;
 | 
						|
 | 
						|
    cdepth++;
 | 
						|
    SWITCHHEAPS(compheap) {
 | 
						|
	HEAPALLOC {
 | 
						|
	    int ooffs = offs, lip, lp;
 | 
						|
	    char *str = comp_str(&lip, &lp, 0), *t;
 | 
						|
	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
 | 
						|
	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
 | 
						|
	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, *oaq = autoq;
 | 
						|
	    char buf[2];
 | 
						|
 | 
						|
	    if (compquote && (qc = *compquote)) {
 | 
						|
		if (qc == '`') {
 | 
						|
		    instring = 0;
 | 
						|
		    inbackt = 0;
 | 
						|
		    autoq = "";
 | 
						|
		} else {
 | 
						|
		    buf[0] = qc;
 | 
						|
		    buf[1] = '\0';
 | 
						|
		    instring = (qc == '\'' ? 1 : 2);
 | 
						|
		    inbackt = 0;
 | 
						|
		    autoq = buf;
 | 
						|
		}
 | 
						|
	    } else {
 | 
						|
		instring = inbackt = 0;
 | 
						|
		autoq = "";
 | 
						|
	    }
 | 
						|
	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
 | 
						|
	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
 | 
						|
	    isuf = dupstring(compisuffix);
 | 
						|
	    ctokenize(isuf);
 | 
						|
	    remnulargs(isuf);
 | 
						|
	    clwnum = arrlen(compwords);
 | 
						|
	    clwpos = compcurrent - 1;
 | 
						|
	    cmdstr = ztrdup(compwords[0]);
 | 
						|
	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
 | 
						|
	    for (p = compwords, q = clwords; *p; p++, q++) {
 | 
						|
		t = dupstring(*p);
 | 
						|
		tokenize(t);
 | 
						|
		remnulargs(t);
 | 
						|
		*q = ztrdup(t);
 | 
						|
	    }
 | 
						|
	    *q = NULL;
 | 
						|
	    offs = lip + lp;
 | 
						|
	    incompfunc = 2;
 | 
						|
	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
 | 
						|
	    incompfunc = 1;
 | 
						|
	    isuf = oisuf;
 | 
						|
	    zsfree(qipre);
 | 
						|
	    zsfree(qisuf);
 | 
						|
	    qipre = oqp;
 | 
						|
	    qisuf = oqs;
 | 
						|
	    instring = ois;
 | 
						|
	    inbackt = oib;
 | 
						|
	    autoq = oaq;
 | 
						|
	    offs = ooffs;
 | 
						|
	    zsfree(cmdstr);
 | 
						|
	    freearray(clwords);
 | 
						|
	    cmdstr = os;
 | 
						|
	    clwords = ow;
 | 
						|
	    clwnum = on;
 | 
						|
	    clwpos = op;
 | 
						|
	} LASTALLOC;
 | 
						|
    } SWITCHBACKHEAPS;
 | 
						|
    cdepth--;
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* This function gets the compctls for the given command line and *
 | 
						|
 * adds all completions for them. */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
makecomplistglobal(char *os, int incmd, int lst, int flags)
 | 
						|
{
 | 
						|
    Compctl cc = NULL;
 | 
						|
    char *s;
 | 
						|
 | 
						|
    ccont = CC_CCCONT;
 | 
						|
    cc_dummy.suffix = NULL;
 | 
						|
 | 
						|
    if (linwhat == IN_ENV) {
 | 
						|
        /* Default completion for parameter values. */
 | 
						|
	if (!(flags & CFN_DEFAULT)) {
 | 
						|
	    cc = &cc_default;
 | 
						|
	    keypm = NULL;
 | 
						|
	}
 | 
						|
    } else if (linwhat == IN_MATH) {
 | 
						|
	if (!(flags & CFN_DEFAULT)) {
 | 
						|
	    if (insubscr >= 2) {
 | 
						|
		/* Inside subscript of assoc array, complete keys. */
 | 
						|
		cc_dummy.mask = 0;
 | 
						|
		cc_dummy.suffix = (insubscr == 2 ? "]" : "");
 | 
						|
	    } else {
 | 
						|
		/* Other math environment, complete paramete names. */
 | 
						|
		keypm = NULL;
 | 
						|
		cc_dummy.mask = CC_PARAMS;
 | 
						|
	    }
 | 
						|
	    cc = &cc_dummy;
 | 
						|
	    cc_dummy.refc = 10000;
 | 
						|
	}
 | 
						|
    } else if (linwhat == IN_COND) {
 | 
						|
	/* We try to be clever here: in conditions we complete option   *
 | 
						|
	 * names after a `-o', file names after `-nt', `-ot', and `-ef' *
 | 
						|
	 * and file names and parameter names elsewhere.                */
 | 
						|
	if (!(flags & CFN_DEFAULT)) {
 | 
						|
	    s = clwpos ? clwords[clwpos - 1] : "";
 | 
						|
	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
 | 
						|
		((*s == '-' && s[1] && !s[2]) ||
 | 
						|
		 !strcmp("-nt", s) ||
 | 
						|
		 !strcmp("-ot", s) ||
 | 
						|
		 !strcmp("-ef", s)) ? CC_FILES :
 | 
						|
		(CC_FILES | CC_PARAMS);
 | 
						|
	    cc = &cc_dummy;
 | 
						|
	    cc_dummy.refc = 10000;
 | 
						|
	    keypm = NULL;
 | 
						|
	}
 | 
						|
    } else if (linredir) {
 | 
						|
	if (!(flags & CFN_DEFAULT)) {
 | 
						|
	    /* In redirections use default completion. */
 | 
						|
	    cc = &cc_default;
 | 
						|
	    keypm = NULL;
 | 
						|
	}
 | 
						|
    } else {
 | 
						|
	/* Otherwise get the matches for the command. */
 | 
						|
	keypm = NULL;
 | 
						|
	return makecomplistcmd(os, incmd, flags);
 | 
						|
    }
 | 
						|
    if (cc) {
 | 
						|
	/* First, use the -T compctl. */
 | 
						|
	if (!(flags & CFN_FIRST)) {
 | 
						|
	    makecomplistcc(&cc_first, os, incmd);
 | 
						|
 | 
						|
	    if (!(ccont & CC_CCCONT))
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	makecomplistcc(cc, os, incmd);
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This produces the matches for a command. */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
makecomplistcmd(char *os, int incmd, int flags)
 | 
						|
{
 | 
						|
    Compctl cc;
 | 
						|
    Compctlp ccp;
 | 
						|
    char *s;
 | 
						|
    int ret = 0;
 | 
						|
 | 
						|
    /* First, use the -T compctl. */
 | 
						|
    if (!(flags & CFN_FIRST)) {
 | 
						|
	makecomplistcc(&cc_first, os, incmd);
 | 
						|
 | 
						|
	if (!(ccont & CC_CCCONT))
 | 
						|
	    return 0;
 | 
						|
    }
 | 
						|
    /* Then search the pattern compctls, with the command name and the *
 | 
						|
     * full pathname of the command. */
 | 
						|
    if (cmdstr) {
 | 
						|
	ret |= makecomplistpc(os, incmd);
 | 
						|
	if (!(ccont & CC_CCCONT))
 | 
						|
	    return ret;
 | 
						|
    }
 | 
						|
    /* If the command string starts with `=', try the path name of the *
 | 
						|
     * command. */
 | 
						|
    if (cmdstr && cmdstr[0] == Equals) {
 | 
						|
	char *c = findcmd(cmdstr + 1, 1);
 | 
						|
 | 
						|
	if (c) {
 | 
						|
	    zsfree(cmdstr);
 | 
						|
	    cmdstr = ztrdup(c);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* Find the compctl for this command, trying the full name and then *
 | 
						|
     * the trailing pathname component. If that doesn't yield anything, *
 | 
						|
     * use default completion. */
 | 
						|
    if (incmd)
 | 
						|
	cc = &cc_compos;
 | 
						|
    else if (!(cmdstr &&
 | 
						|
	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
 | 
						|
	    (cc = ccp->cc)) ||
 | 
						|
	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
 | 
						|
	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
 | 
						|
	    (cc = ccp->cc))))) {
 | 
						|
	if (flags & CFN_DEFAULT)
 | 
						|
	    return ret;
 | 
						|
	cc = &cc_default;
 | 
						|
    } else
 | 
						|
	ret|= 1;
 | 
						|
    makecomplistcc(cc, os, incmd);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* This add the matches for the pattern compctls. */
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
makecomplistpc(char *os, int incmd)
 | 
						|
{
 | 
						|
    Patcomp pc;
 | 
						|
    Patprog pat;
 | 
						|
    char *s = findcmd(cmdstr, 1);
 | 
						|
    int ret = 0;
 | 
						|
 | 
						|
    for (pc = patcomps; pc; pc = pc->next) {
 | 
						|
	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
 | 
						|
	    (pattry(pat, cmdstr) ||
 | 
						|
	     (s && pattry(pat, s)))) {
 | 
						|
	    makecomplistcc(pc->cc, os, incmd);
 | 
						|
	    ret |= 2;
 | 
						|
	    if (!(ccont & CC_CCCONT))
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* This produces the matches for one compctl. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
makecomplistcc(Compctl cc, char *s, int incmd)
 | 
						|
{
 | 
						|
    cc->refc++;
 | 
						|
    addlinknode(ccused, cc);
 | 
						|
 | 
						|
    ccont = 0;
 | 
						|
 | 
						|
    makecomplistor(cc, s, incmd, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* This adds the completions for one run of [x]or'ed completions. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
 | 
						|
{
 | 
						|
    int mn, ct, um = usemenu;
 | 
						|
 | 
						|
    /* Loop over xors. */
 | 
						|
    do {
 | 
						|
	mn = mnum;
 | 
						|
 | 
						|
	/* Loop over ors. */
 | 
						|
	do {
 | 
						|
	    /* Reset the range information if we are not in a sub-list. */
 | 
						|
	    if (!sub) {
 | 
						|
		brange = 0;
 | 
						|
		erange = clwnum - 1;
 | 
						|
	    }
 | 
						|
	    usemenu = 0;
 | 
						|
	    makecomplistlist(cc, s, incmd, compadd);
 | 
						|
	    um |= usemenu;
 | 
						|
 | 
						|
	    ct = cc->mask2 & CC_XORCONT;
 | 
						|
 | 
						|
	    cc = cc->xor;
 | 
						|
	} while (cc && ct);
 | 
						|
 | 
						|
	/* Stop if we got some matches. */
 | 
						|
	if (mn != mnum)
 | 
						|
	    break;
 | 
						|
	if (cc) {
 | 
						|
	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
 | 
						|
	    if (!sub)
 | 
						|
		ccont &= ~CC_CCCONT;
 | 
						|
	}
 | 
						|
    } while (cc);
 | 
						|
 | 
						|
    usemenu = um;
 | 
						|
}
 | 
						|
 | 
						|
/* This dispatches for simple and extended completion. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
 | 
						|
{
 | 
						|
    int oloffs = offs, owe = we, owb = wb, ocs = cs;
 | 
						|
 | 
						|
    if (cc->ext)
 | 
						|
	/* Handle extended completion. */
 | 
						|
	makecomplistext(cc, s, incmd);
 | 
						|
    else
 | 
						|
	/* Only normal flags. */
 | 
						|
	makecomplistflags(cc, s, incmd, compadd);
 | 
						|
 | 
						|
    /* Reset some information variables for the next try. */
 | 
						|
    errflag = 0;
 | 
						|
    offs = oloffs;
 | 
						|
    wb = owb;
 | 
						|
    we = owe;
 | 
						|
    cs = ocs;
 | 
						|
}
 | 
						|
 | 
						|
/* This add matches for extended completion patterns */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
makecomplistext(Compctl occ, char *os, int incmd)
 | 
						|
{
 | 
						|
    Compctl compc;
 | 
						|
    Compcond or, cc;
 | 
						|
    Patprog pprog;
 | 
						|
    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
 | 
						|
    char *sc = NULL, *s, *ss;
 | 
						|
 | 
						|
    ins = (instring ? instring : (inbackt ? 3 : 0));
 | 
						|
 | 
						|
    /* This loops over the patterns separated by `-'s. */
 | 
						|
    for (compc = occ->ext; compc; compc = compc->next) {
 | 
						|
	compadd = t = brange = 0;
 | 
						|
	erange = clwnum - 1;
 | 
						|
	/* This loops over OR'ed patterns. */
 | 
						|
	for (cc = compc->cond; cc && !t; cc = or) {
 | 
						|
	    or = cc->or;
 | 
						|
	    /* This loops over AND'ed patterns. */
 | 
						|
	    for (t = 1; cc && t; cc = cc->and) {
 | 
						|
		/* And this loops over [...] pairs. */
 | 
						|
		for (t = i = 0; i < cc->n && !t; i++) {
 | 
						|
		    s = NULL;
 | 
						|
		    brange = 0;
 | 
						|
		    erange = clwnum - 1;
 | 
						|
		    switch (cc->type) {
 | 
						|
		    case CCT_QUOTE:
 | 
						|
			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
 | 
						|
			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
 | 
						|
			     (cc->u.s.s[i][0] == 'b' && ins == 3));
 | 
						|
			break;
 | 
						|
		    case CCT_POS:
 | 
						|
			tt = clwpos;
 | 
						|
			goto cct_num;
 | 
						|
		    case CCT_NUMWORDS:
 | 
						|
			tt = clwnum;
 | 
						|
		    cct_num:
 | 
						|
			if ((a = cc->u.r.a[i]) < 0)
 | 
						|
			    a += clwnum;
 | 
						|
			if ((b = cc->u.r.b[i]) < 0)
 | 
						|
			    b += clwnum;
 | 
						|
			if (cc->type == CCT_POS)
 | 
						|
			    brange = a, erange = b;
 | 
						|
			t = (tt >= a && tt <= b);
 | 
						|
			break;
 | 
						|
		    case CCT_CURSUF:
 | 
						|
		    case CCT_CURPRE:
 | 
						|
			s = ztrdup(clwpos < clwnum ? os : "");
 | 
						|
			untokenize(s);
 | 
						|
			if (isset(COMPLETEINWORD)) s[offs] = '\0';
 | 
						|
			sc = rembslash(cc->u.s.s[i]);
 | 
						|
			a = strlen(sc);
 | 
						|
			if (!strncmp(s, sc, a)) {
 | 
						|
			    compadd = (cc->type == CCT_CURSUF ? a : 0);
 | 
						|
			    t = 1;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		    case CCT_CURSUB:
 | 
						|
		    case CCT_CURSUBC:
 | 
						|
			if (clwpos < 0 || clwpos >= clwnum)
 | 
						|
			    t = 0;
 | 
						|
			else {
 | 
						|
			    s = ztrdup(os);
 | 
						|
			    untokenize(s);
 | 
						|
			    if (isset(COMPLETEINWORD)) s[offs] = '\0';
 | 
						|
			    a = getcpat(s,
 | 
						|
					cc->u.s.p[i],
 | 
						|
					cc->u.s.s[i],
 | 
						|
					cc->type == CCT_CURSUBC);
 | 
						|
			    if (a != -1)
 | 
						|
				compadd = a, t = 1;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
			
 | 
						|
		    case CCT_CURPAT:
 | 
						|
		    case CCT_CURSTR:
 | 
						|
			tt = clwpos;
 | 
						|
			goto cct_str;
 | 
						|
		    case CCT_WORDPAT:
 | 
						|
		    case CCT_WORDSTR:
 | 
						|
			tt = 0;
 | 
						|
		    cct_str:
 | 
						|
			if ((a = tt + cc->u.s.p[i]) < 0)
 | 
						|
			    a += clwnum;
 | 
						|
			s = ztrdup((a < 0 || a >= clwnum) ? "" :
 | 
						|
				   clwords[a]);
 | 
						|
			untokenize(s);
 | 
						|
			
 | 
						|
			if (cc->type == CCT_CURPAT ||
 | 
						|
			    cc->type == CCT_WORDPAT) {
 | 
						|
			    tokenize(ss = dupstring(cc->u.s.s[i]));
 | 
						|
			    t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
 | 
						|
				 pattry(pprog, s));
 | 
						|
			} else
 | 
						|
			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
 | 
						|
			break;
 | 
						|
		    case CCT_RANGESTR:
 | 
						|
		    case CCT_RANGEPAT:
 | 
						|
			if (cc->type == CCT_RANGEPAT)
 | 
						|
			    tokenize(sc = dupstring(cc->u.l.a[i]));
 | 
						|
			for (j = clwpos - 1; j > 0; j--) {
 | 
						|
			    untokenize(s = ztrdup(clwords[j]));
 | 
						|
			    if (cc->type == CCT_RANGESTR)
 | 
						|
				sc = rembslash(cc->u.l.a[i]);
 | 
						|
			    if (cc->type == CCT_RANGESTR ?
 | 
						|
				!strncmp(s, sc, strlen(sc)) :
 | 
						|
				((pprog = patcompile(sc, PAT_STATIC, 0)) &&
 | 
						|
				 pattry(pprog, s))) {
 | 
						|
				zsfree(s);
 | 
						|
				brange = j + 1;
 | 
						|
				t = 1;
 | 
						|
				break;
 | 
						|
			    }
 | 
						|
			    zsfree(s);
 | 
						|
			}
 | 
						|
			if (t && cc->u.l.b[i]) {
 | 
						|
			    if (cc->type == CCT_RANGEPAT)
 | 
						|
				tokenize(sc = dupstring(cc->u.l.b[i]));
 | 
						|
			    for (j++; j < clwnum; j++) {
 | 
						|
				untokenize(s = ztrdup(clwords[j]));
 | 
						|
				if (cc->type == CCT_RANGESTR)
 | 
						|
				    sc = rembslash(cc->u.l.b[i]);
 | 
						|
				if (cc->type == CCT_RANGESTR ?
 | 
						|
				    !strncmp(s, sc, strlen(sc)) :
 | 
						|
				    ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
 | 
						|
				     pattry(pprog, s))) {
 | 
						|
				    zsfree(s);
 | 
						|
				    erange = j - 1;
 | 
						|
				    t = clwpos <= erange;
 | 
						|
				    break;
 | 
						|
				}
 | 
						|
				zsfree(s);
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			s = NULL;
 | 
						|
		    }
 | 
						|
		    zsfree(s);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (t) {
 | 
						|
	    /* The patterns matched, use the flags. */
 | 
						|
	    m = 1;
 | 
						|
	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
 | 
						|
	    makecomplistor(compc, os, incmd, compadd, 1);
 | 
						|
	    if (!d && (ccont & CC_DEFCONT)) {
 | 
						|
		d = 1;
 | 
						|
		compadd = 0;
 | 
						|
		brange = 0;
 | 
						|
		erange = clwnum - 1;
 | 
						|
		makecomplistflags(occ, os, incmd, 0);
 | 
						|
	    }
 | 
						|
	    if (!(ccont & CC_PATCONT))
 | 
						|
		break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    /* If no pattern matched, use the standard flags. */
 | 
						|
    if (!m) {
 | 
						|
	compadd = 0;
 | 
						|
	brange = 0;
 | 
						|
	erange = clwnum - 1;
 | 
						|
	makecomplistflags(occ, os, incmd, 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
static int
 | 
						|
sep_comp_string(char *ss, char *s, int noffs)
 | 
						|
{
 | 
						|
    LinkList foo = newlinklist();
 | 
						|
    LinkNode n;
 | 
						|
    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
 | 
						|
    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll, remq;
 | 
						|
    int ois = instring, oib = inbackt;
 | 
						|
    char *tmp, *p, *ns, *ol = (char *) line, sav, *oaq = autoq, *qp, *qs;
 | 
						|
    char *ts, qc = '\0';
 | 
						|
 | 
						|
    swb = swe = soffs = 0;
 | 
						|
    ns = NULL;
 | 
						|
 | 
						|
    /* Put the string in the lexer buffer and call the lexer to *
 | 
						|
     * get the words we have to expand.                        */
 | 
						|
    zleparse = 1;
 | 
						|
    addedx = 1;
 | 
						|
    noerrs = 1;
 | 
						|
    lexsave();
 | 
						|
    tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
 | 
						|
    strcpy(tmp, ss);
 | 
						|
    tmp[sl] = ' ';
 | 
						|
    memcpy(tmp + sl + 1, s, noffs);
 | 
						|
    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
 | 
						|
    strcpy(tmp + sl + 2 + noffs, s + noffs);
 | 
						|
    if ((remq = (*compqstack == '\\')))
 | 
						|
	tmp = rembslash(tmp);
 | 
						|
    inpush(dupstrspace(tmp), 0, NULL);
 | 
						|
    line = (unsigned char *) tmp;
 | 
						|
    ll = tl - 1;
 | 
						|
    strinbeg(0);
 | 
						|
    noaliases = 1;
 | 
						|
    do {
 | 
						|
	ctxtlex();
 | 
						|
	if (tok == LEXERR) {
 | 
						|
	    int j;
 | 
						|
 | 
						|
	    if (!tokstr)
 | 
						|
		break;
 | 
						|
	    for (j = 0, p = tokstr; *p; p++)
 | 
						|
		if (*p == Snull || *p == Dnull)
 | 
						|
		    j++;
 | 
						|
	    if (j & 1) {
 | 
						|
		tok = STRING;
 | 
						|
		if (p > tokstr && p[-1] == ' ')
 | 
						|
		    p[-1] = '\0';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (tok == ENDINPUT || tok == LEXERR)
 | 
						|
	    break;
 | 
						|
	if (tokstr && *tokstr)
 | 
						|
	    addlinknode(foo, (p = ztrdup(tokstr)));
 | 
						|
	else
 | 
						|
	    p = NULL;
 | 
						|
	if (!got && !zleparse) {
 | 
						|
	    DPUTS(!p, "no current word in substr");
 | 
						|
	    got = 1;
 | 
						|
	    cur = i;
 | 
						|
	    swb = wb - 1;
 | 
						|
	    swe = we - 1;
 | 
						|
	    soffs = cs - swb;
 | 
						|
	    chuck(p + soffs);
 | 
						|
	    ns = dupstring(p);
 | 
						|
	}
 | 
						|
	i++;
 | 
						|
    } while (tok != ENDINPUT && tok != LEXERR);
 | 
						|
    noaliases = 0;
 | 
						|
    strinend();
 | 
						|
    inpop();
 | 
						|
    errflag = zleparse = 0;
 | 
						|
    noerrs = ne;
 | 
						|
    lexrestore();
 | 
						|
    wb = owb;
 | 
						|
    we = owe;
 | 
						|
    cs = ocs;
 | 
						|
    line = (unsigned char *) ol;
 | 
						|
    ll = oll;
 | 
						|
    if (cur < 0 || i < 1)
 | 
						|
	return 1;
 | 
						|
    owb = offs;
 | 
						|
    offs = soffs;
 | 
						|
    if ((p = check_param(ns, 0, 1))) {
 | 
						|
	for (p = ns; *p; p++)
 | 
						|
	    if (*p == Dnull)
 | 
						|
		*p = '"';
 | 
						|
	    else if (*p == Snull)
 | 
						|
		*p = '\'';
 | 
						|
    }
 | 
						|
    offs = owb;
 | 
						|
 | 
						|
    untokenize(ts = dupstring(ns));
 | 
						|
 | 
						|
    if (*ns == Snull || *ns == Dnull) {
 | 
						|
	instring = (*ns == Snull ? 1 : 2);
 | 
						|
	inbackt = 0;
 | 
						|
	swb++;
 | 
						|
	if (ns[strlen(ns) - 1] == *ns && ns[1])
 | 
						|
	    swe--;
 | 
						|
	autoq = compqstack[1] ? "" : multiquote(*ns == Snull ? "'" : "\"", 1);
 | 
						|
	qc = (*ns == Snull ? '\'' : '"');
 | 
						|
	ts++;
 | 
						|
    } else {
 | 
						|
	instring = 0;
 | 
						|
	autoq = "";
 | 
						|
    }
 | 
						|
    for (p = ns, i = swb; *p; p++, i++) {
 | 
						|
	if (INULL(*p)) {
 | 
						|
	    if (i < scs) {
 | 
						|
		soffs--;
 | 
						|
		if (remq && *p == Bnull && p[1])
 | 
						|
		    swb -= 2;
 | 
						|
	    }
 | 
						|
	    if (p[1] || *p != Bnull) {
 | 
						|
		if (*p == Bnull) {
 | 
						|
		    if (scs == i + 1)
 | 
						|
			scs++, soffs++;
 | 
						|
		} else {
 | 
						|
		    if (scs > i--)
 | 
						|
			scs--;
 | 
						|
		}
 | 
						|
	    } else {
 | 
						|
		if (scs == swe)
 | 
						|
		    scs--;
 | 
						|
	    }
 | 
						|
	    chuck(p--);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    ns = ts;
 | 
						|
 | 
						|
    if (instring && strchr(compqstack, '\\')) {
 | 
						|
	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
 | 
						|
 | 
						|
	if (ql > rl)
 | 
						|
	    swb -= ql - rl;
 | 
						|
    }
 | 
						|
    sav = s[(i = swb - sl - 1)];
 | 
						|
    s[i] = '\0';
 | 
						|
    qp = tricat(qipre, multiquote(s, 0), "");
 | 
						|
    s[i] = sav;
 | 
						|
    if (swe < swb)
 | 
						|
	swe = swb;
 | 
						|
    swe -= sl + 1;
 | 
						|
    sl = strlen(s);
 | 
						|
    if (swe > sl) {
 | 
						|
	swe = sl;
 | 
						|
	if (strlen(ns) > swe - swb + 1)
 | 
						|
	    ns[swe - swb + 1] = '\0';
 | 
						|
    }
 | 
						|
    qs = tricat(multiquote(s + swe, 0), qisuf, "");
 | 
						|
    sl = strlen(ns);
 | 
						|
    if (soffs > sl)
 | 
						|
	soffs = sl;
 | 
						|
 | 
						|
    {
 | 
						|
	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
 | 
						|
	char *oqst = compqstack;
 | 
						|
	int olws = clwsize, olwn = clwnum, olwp = clwpos;
 | 
						|
	int obr = brange, oer = erange, oof = offs;
 | 
						|
	unsigned long occ = ccont;
 | 
						|
 | 
						|
	compqstack = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
 | 
						|
			    compqstack, "");
 | 
						|
 | 
						|
	clwsize = clwnum = countlinknodes(foo);
 | 
						|
	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
 | 
						|
	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
 | 
						|
	    p = clwords[i] = (char *) getdata(n);
 | 
						|
	    untokenize(p);
 | 
						|
	}
 | 
						|
	clwords[i] = NULL;
 | 
						|
	clwpos = cur;
 | 
						|
	cmdstr = ztrdup(clwords[0]);
 | 
						|
	brange = 0;
 | 
						|
	erange = clwnum - 1;
 | 
						|
	qipre = qp;
 | 
						|
	qisuf = qs;
 | 
						|
	offs = soffs;
 | 
						|
	ccont = CC_CCCONT;
 | 
						|
	makecomplistcmd(ns, !clwpos, CFN_FIRST);
 | 
						|
	ccont = occ;
 | 
						|
	offs = oof;
 | 
						|
	zsfree(cmdstr);
 | 
						|
	cmdstr = os;
 | 
						|
	freearray(clwords);
 | 
						|
	clwords = ow;
 | 
						|
	clwsize = olws;
 | 
						|
	clwnum = olwn;
 | 
						|
	clwpos = olwp;
 | 
						|
	brange = obr;
 | 
						|
	erange = oer;
 | 
						|
	zsfree(qipre);
 | 
						|
	qipre = oqp;
 | 
						|
	zsfree(qisuf);
 | 
						|
	qisuf = oqs;
 | 
						|
	zsfree(compqstack);
 | 
						|
	compqstack = oqst;
 | 
						|
    }
 | 
						|
    autoq = oaq;
 | 
						|
    instring = ois;
 | 
						|
    inbackt = oib;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This adds the completions for the flags in the given compctl. */
 | 
						|
 | 
						|
/**/
 | 
						|
static void
 | 
						|
makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 | 
						|
{
 | 
						|
    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
 | 
						|
    int mn = mnum, ohp = haspattern;
 | 
						|
    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
 | 
						|
    struct cmlist ms;
 | 
						|
 | 
						|
    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
 | 
						|
 | 
						|
    if (incompfunc != 1 && findnode(ccstack, cc))
 | 
						|
	return;
 | 
						|
 | 
						|
    MUSTUSEHEAP("complistflags");
 | 
						|
 | 
						|
    addlinknode(ccstack, cc);
 | 
						|
 | 
						|
    if (incompfunc != 1 && allccs) {
 | 
						|
	if (findnode(allccs, cc)) {
 | 
						|
	    uremnode(ccstack, firstnode(ccstack));
 | 
						|
	    return;
 | 
						|
	}
 | 
						|
	addlinknode(allccs, cc);
 | 
						|
    }
 | 
						|
    /* Go to the end of the word if complete_in_word is not set. */
 | 
						|
    if (unset(COMPLETEINWORD) && cs != we)
 | 
						|
	cs = we, offs = strlen(s);
 | 
						|
 | 
						|
    s = dupstring(s);
 | 
						|
    delit = ispattern = 0;
 | 
						|
    usemenu = um;
 | 
						|
    patcomp = filecomp = NULL;
 | 
						|
    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
 | 
						|
	fpre = fsuf = ipre = ripre = prpre = 
 | 
						|
	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
 | 
						|
 | 
						|
    curcc = cc;
 | 
						|
 | 
						|
    mflags = 0;
 | 
						|
    gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT  : 0) |
 | 
						|
	      ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
 | 
						|
	      ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
 | 
						|
    if (cc->gname) {
 | 
						|
	endcmgroup(NULL);
 | 
						|
	begcmgroup(cc->gname, gflags);
 | 
						|
    }
 | 
						|
    if (cc->ylist) {
 | 
						|
	endcmgroup(NULL);
 | 
						|
	begcmgroup(NULL, gflags);
 | 
						|
    }
 | 
						|
    if (cc->mask & CC_REMOVE)
 | 
						|
	mflags |= CMF_REMOVE;
 | 
						|
    if (cc->explain) {
 | 
						|
	curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
 | 
						|
	curexpl->count = curexpl->fcount = 0;
 | 
						|
    } else
 | 
						|
	curexpl = NULL;
 | 
						|
    /* compadd is the number of characters we have to ignore at the  *
 | 
						|
     * beginning of the word.                                        */
 | 
						|
    if (compadd) {
 | 
						|
	ipre = dupstring(s);
 | 
						|
	ipre[compadd] = '\0';
 | 
						|
	untokenize(ipre);
 | 
						|
	wb += compadd;
 | 
						|
	s += compadd;
 | 
						|
	if ((offs -= compadd) < 0) {
 | 
						|
	    /* It's bigger than our word prefix, so we can't help here... */
 | 
						|
	    uremnode(ccstack, firstnode(ccstack));
 | 
						|
	    return;
 | 
						|
	}
 | 
						|
    } else
 | 
						|
	ipre = NULL;
 | 
						|
 | 
						|
    if (cc->matcher) {
 | 
						|
	ms.next = mstack;
 | 
						|
	ms.matcher = cc->matcher;
 | 
						|
	mstack = &ms;
 | 
						|
 | 
						|
	if (!mnum)
 | 
						|
	    add_bmatchers(cc->matcher);
 | 
						|
 | 
						|
	addlinknode(matchers, cc->matcher);
 | 
						|
	cc->matcher->refc++;
 | 
						|
    }
 | 
						|
    if (mnum && (mstack || bmatchers))
 | 
						|
	update_bmatchers();
 | 
						|
 | 
						|
    /* Insert the prefix (compctl -P), if any. */
 | 
						|
    if (cc->prefix) {
 | 
						|
	int pl = 0;
 | 
						|
 | 
						|
	if (*s) {
 | 
						|
	    char *dp = rembslash(cc->prefix);
 | 
						|
	    /* First find out how much of the prefix is already on the line. */
 | 
						|
	    sd = dupstring(s);
 | 
						|
	    untokenize(sd);
 | 
						|
	    pl = pfxlen(dp, sd);
 | 
						|
	    s += pl;
 | 
						|
	    sd += pl;
 | 
						|
	    offs -= pl;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    /* Does this compctl have a suffix (compctl -S)? */
 | 
						|
    if (cc->suffix) {
 | 
						|
	char *sdup = rembslash(cc->suffix);
 | 
						|
	int sl = strlen(sdup), suffixll;
 | 
						|
 | 
						|
	/* Ignore trailing spaces. */
 | 
						|
	for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
 | 
						|
	p[1] = '\0';
 | 
						|
 | 
						|
	if (!sd) {
 | 
						|
	    sd = dupstring(s);
 | 
						|
	    untokenize(sd);
 | 
						|
	}
 | 
						|
	/* If the suffix is already there, ignore it (and don't add *
 | 
						|
	 * it again).                                               */
 | 
						|
	if (*sd && (suffixll = strlen(sd)) >= sl &&
 | 
						|
	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
 | 
						|
	    s[suffixll - sl] = '\0';
 | 
						|
    }
 | 
						|
    /* Do we have one of the special characters `~' and `=' at the beginning? */
 | 
						|
    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
 | 
						|
	ic = 0;
 | 
						|
 | 
						|
    /* Check if we have to complete a parameter name... */
 | 
						|
    if (!incompfunc && (p = check_param(s, 1, 0))) {
 | 
						|
	s = p;
 | 
						|
	/* And now make sure that we complete parameter names. */
 | 
						|
	cc = &cc_dummy;
 | 
						|
	cc_dummy.refc = 10000;
 | 
						|
	cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
 | 
						|
    }
 | 
						|
    ooffs = offs;
 | 
						|
    /* If we have to ignore the word, do that. */
 | 
						|
    if (cc->mask & CC_DELETE) {
 | 
						|
	delit = 1;
 | 
						|
	*s = '\0';
 | 
						|
	offs = 0;
 | 
						|
	if (isset(AUTOMENU))
 | 
						|
	    usemenu = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Compute line prefix/suffix. */
 | 
						|
    lpl = offs;
 | 
						|
    lpre = zhalloc(lpl + 1);
 | 
						|
    memcpy(lpre, s, lpl);
 | 
						|
    lpre[lpl] = '\0';
 | 
						|
    qlpre = quotename(lpre, NULL);
 | 
						|
    lsuf = dupstring(s + offs);
 | 
						|
    lsl = strlen(lsuf);
 | 
						|
    qlsuf = quotename(lsuf, NULL);
 | 
						|
 | 
						|
    /* First check for ~.../... */
 | 
						|
    if (ic == Tilde) {
 | 
						|
	for (p = lpre + lpl; p > lpre; p--)
 | 
						|
	    if (*p == '/')
 | 
						|
		break;
 | 
						|
 | 
						|
	if (*p == '/')
 | 
						|
	    ic = 0;
 | 
						|
    }
 | 
						|
    /* Compute real prefix/suffix. */
 | 
						|
 | 
						|
    noreal = !delit;
 | 
						|
    for (p = lpre; *p && *p != String && *p != Tick; p++);
 | 
						|
    tt = ic && !ispar ? lpre + 1 : lpre;
 | 
						|
    rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
 | 
						|
	(noreal = 0, getreal(tt)) :
 | 
						|
	dupstring(tt);
 | 
						|
    qrpre = quotename(rpre, NULL);
 | 
						|
 | 
						|
    for (p = lsuf; *p && *p != String && *p != Tick; p++);
 | 
						|
    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
 | 
						|
    qrsuf = quotename(rsuf, NULL);
 | 
						|
 | 
						|
    /* Check if word is a pattern. */
 | 
						|
 | 
						|
    for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
 | 
						|
	 p >= rpre && (ispattern != 3 || !sf1);
 | 
						|
	 p--)
 | 
						|
	if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
 | 
						|
	    ispattern |= sf1 ? 1 : 2;
 | 
						|
	else if (*p == '/') {
 | 
						|
	    sf1++;
 | 
						|
	    if (!s1)
 | 
						|
		s1 = p;
 | 
						|
	}
 | 
						|
    rsl = strlen(rsuf);
 | 
						|
    for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
 | 
						|
	if (itok(*p))
 | 
						|
	    t |= sf2 ? 4 : 2;
 | 
						|
	else if (*p == '/') {
 | 
						|
	    sf2++;
 | 
						|
	    if (!s2)
 | 
						|
		s2 = p;
 | 
						|
	}
 | 
						|
    ispattern = ispattern | t;
 | 
						|
 | 
						|
    /* But if we were asked not to do glob completion, we never treat the *
 | 
						|
     * thing as a pattern.                                                */
 | 
						|
    if (!comppatmatch || !*comppatmatch)
 | 
						|
	ispattern = 0;
 | 
						|
 | 
						|
    if (ispattern) {
 | 
						|
	/* The word should be treated as a pattern, so compute the matcher. */
 | 
						|
	p = (char *) zhalloc(rpl + rsl + 2);
 | 
						|
	strcpy(p, rpre);
 | 
						|
	if (rpl && p[rpl - 1] != Star &&
 | 
						|
	    (!comppatmatch || *comppatmatch == '*')) {
 | 
						|
	    p[rpl] = Star;
 | 
						|
	    strcpy(p + rpl + 1, rsuf);
 | 
						|
	} else
 | 
						|
	    strcpy(p + rpl, rsuf);
 | 
						|
	patcomp = patcompile(p, 0, NULL);
 | 
						|
	haspattern = 1;
 | 
						|
    }
 | 
						|
    if (!patcomp) {
 | 
						|
	untokenize(rpre);
 | 
						|
	untokenize(rsuf);
 | 
						|
 | 
						|
	rpl = strlen(rpre);
 | 
						|
	rsl = strlen(rsuf);
 | 
						|
    }
 | 
						|
    untokenize(lpre);
 | 
						|
    untokenize(lsuf);
 | 
						|
 | 
						|
    if (!(cc->mask & CC_DELETE))
 | 
						|
	hasmatched = 1;
 | 
						|
 | 
						|
    /* Handle completion of files specially (of course). */
 | 
						|
 | 
						|
    if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
 | 
						|
	/* s1 and s2 point to the last/first slash in the prefix/suffix. */
 | 
						|
	if (!s1)
 | 
						|
	    s1 = rpre;
 | 
						|
	if (!s2)
 | 
						|
	    s2 = rsuf + rsl;
 | 
						|
 | 
						|
	/* Compute the path prefix/suffix. */
 | 
						|
	if (*s1 != '/')
 | 
						|
	    ppre = "";
 | 
						|
	else
 | 
						|
	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
 | 
						|
	psuf = dupstring(s2);
 | 
						|
 | 
						|
	if (cs != wb) {
 | 
						|
	    char save = line[cs];
 | 
						|
 | 
						|
	    line[cs] = 0;
 | 
						|
	    lppre = dupstring((char *) line + wb +
 | 
						|
			      (qipre && *qipre ?
 | 
						|
			       (strlen(qipre) -
 | 
						|
				(*qipre == '\'' || *qipre == '\"')) : 0));
 | 
						|
	    line[cs] = save;
 | 
						|
	    if (brbeg) {
 | 
						|
		Brinfo bp;
 | 
						|
 | 
						|
		for (bp = brbeg; bp; bp = bp->next)
 | 
						|
		    strcpy(lppre + bp->qpos,
 | 
						|
			   lppre + bp->qpos + strlen(bp->str));
 | 
						|
	    }
 | 
						|
	    if ((p = strrchr(lppre, '/'))) {
 | 
						|
		p[1] = '\0';
 | 
						|
		lppl = strlen(lppre);
 | 
						|
	    } else if (!sf1) {
 | 
						|
		lppre = NULL;
 | 
						|
		lppl = 0;
 | 
						|
	    } else {
 | 
						|
		lppre = ppre;
 | 
						|
		lppl = strlen(lppre);
 | 
						|
	    }
 | 
						|
	} else {
 | 
						|
	    lppre = NULL;
 | 
						|
	    lppl = 0;
 | 
						|
	}
 | 
						|
	if (cs != we) {
 | 
						|
	    int end = we;
 | 
						|
	    char save = line[end];
 | 
						|
 | 
						|
	    if (qisuf && *qisuf) {
 | 
						|
		int ql = strlen(qisuf);
 | 
						|
 | 
						|
		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
 | 
						|
	    }
 | 
						|
	    line[end] = 0;
 | 
						|
	    lpsuf = dupstring((char *) (line + cs));
 | 
						|
	    line[end] = save;
 | 
						|
	    if (brend) {
 | 
						|
		Brinfo bp;
 | 
						|
		char *p;
 | 
						|
		int bl;
 | 
						|
 | 
						|
		for (bp = brend; bp; bp = bp->next) {
 | 
						|
		    p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
 | 
						|
		    strcpy(p, p + bl);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
 | 
						|
		lpsuf = psuf;
 | 
						|
	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
 | 
						|
	} else {
 | 
						|
	    lpsuf = NULL;
 | 
						|
	    lpsl = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* And get the file prefix. */
 | 
						|
	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
 | 
						|
			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
 | 
						|
	qfpre = quotename(fpre, NULL);
 | 
						|
	/* And the suffix. */
 | 
						|
	fsuf = dupstrpfx(rsuf, s2 - rsuf);
 | 
						|
	qfsuf = quotename(fsuf, NULL);
 | 
						|
 | 
						|
	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
 | 
						|
	    int t2;
 | 
						|
 | 
						|
	    /* We have to use globbing, so compute the pattern from *
 | 
						|
	     * the file prefix and suffix with a `*' between them.  */
 | 
						|
	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
 | 
						|
	    strcpy(p, fpre);
 | 
						|
	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
 | 
						|
		(!comppatmatch || *comppatmatch == '*'))
 | 
						|
		p[t2++] = Star;
 | 
						|
	    strcpy(p + t2, fsuf);
 | 
						|
	    filecomp = patcompile(p, 0, NULL);
 | 
						|
	}
 | 
						|
	if (!filecomp) {
 | 
						|
	    untokenize(fpre);
 | 
						|
	    untokenize(fsuf);
 | 
						|
 | 
						|
	    fpl = strlen(fpre);
 | 
						|
	    fsl = strlen(fsuf);
 | 
						|
	}
 | 
						|
	addwhat = -1;
 | 
						|
 | 
						|
	/* Completion after `~', maketildelist adds the usernames *
 | 
						|
	 * and named directories.                                 */
 | 
						|
	if (ic == Tilde) {
 | 
						|
	    char *oi = ipre;
 | 
						|
 | 
						|
	    ipre = (ipre ? dyncat("~", ipre) : "~");
 | 
						|
	    maketildelist();
 | 
						|
	    ipre = oi;
 | 
						|
	} else if (ic == Equals) {
 | 
						|
	    /* Completion after `=', get the command names from *
 | 
						|
	     * the cmdnamtab and aliases from aliastab.         */
 | 
						|
	    char *oi = ipre;
 | 
						|
 | 
						|
	    ipre = (ipre ? dyncat("=", ipre) : "=");
 | 
						|
	    if (isset(HASHLISTALL))
 | 
						|
		cmdnamtab->filltable(cmdnamtab);
 | 
						|
	    dumphashtable(cmdnamtab, -7);
 | 
						|
	    dumphashtable(aliastab, -2);
 | 
						|
	    ipre = oi;
 | 
						|
	} else {
 | 
						|
	    /* Normal file completion... */
 | 
						|
	    if (ispattern & 1) {
 | 
						|
		/* But with pattern matching. */
 | 
						|
		LinkList l = newlinklist();
 | 
						|
		LinkNode n;
 | 
						|
		int ng = opts[NULLGLOB];
 | 
						|
 | 
						|
		opts[NULLGLOB] = 1;
 | 
						|
 | 
						|
		addwhat = 0;
 | 
						|
		p = (char *) zhalloc(lpl + lsl + 3);
 | 
						|
		strcpy(p, lpre);
 | 
						|
		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
 | 
						|
		    strcat(p, "*");
 | 
						|
		strcat(p, lsuf);
 | 
						|
		if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
 | 
						|
		    strcat(p, "*");
 | 
						|
 | 
						|
		/* Do the globbing. */
 | 
						|
		tokenize(p);
 | 
						|
		remnulargs(p);
 | 
						|
		addlinknode(l, p);
 | 
						|
		globlist(l);
 | 
						|
 | 
						|
		if (nonempty(l)) {
 | 
						|
		    /* And add the resulting words. */
 | 
						|
		    mflags |= CMF_FILE;
 | 
						|
		    for (n = firstnode(l); n; incnode(n))
 | 
						|
			addmatch(getdata(n), NULL);
 | 
						|
		    mflags &= !CMF_FILE;
 | 
						|
		}
 | 
						|
		opts[NULLGLOB] = ng;
 | 
						|
	    } else {
 | 
						|
		char **dirs = 0, *ta[2];
 | 
						|
 | 
						|
		/* No pattern matching. */
 | 
						|
		addwhat = CC_FILES;
 | 
						|
 | 
						|
		if (cc->withd) {
 | 
						|
		    char **pp, **npp, *tp;
 | 
						|
		    int tl = strlen(ppre) + 2, pl;
 | 
						|
 | 
						|
		    if ((pp = get_user_var(cc->withd))) {
 | 
						|
			dirs = npp =
 | 
						|
			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
 | 
						|
			while (*pp) {
 | 
						|
			    pl = strlen(*pp);
 | 
						|
			    tp = (char *) zhalloc(strlen(*pp) + tl);
 | 
						|
			    strcpy(tp, *pp);
 | 
						|
			    tp[pl] = '/';
 | 
						|
			    strcpy(tp + pl + 1, ppre);
 | 
						|
			    *npp++ = tp;
 | 
						|
			    pp++;
 | 
						|
			}
 | 
						|
			*npp = '\0';
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		if (!dirs) {
 | 
						|
		    dirs = ta;
 | 
						|
		    if (cc->withd) {
 | 
						|
			char *tp;
 | 
						|
			int pl = strlen(cc->withd);
 | 
						|
 | 
						|
			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
 | 
						|
			strcpy(tp, cc->withd);
 | 
						|
			tp[pl] = '/';
 | 
						|
			strcpy(tp + pl + 1, ppre);
 | 
						|
		    } else
 | 
						|
			ta[0] = ppre;
 | 
						|
		    ta[1] = NULL;
 | 
						|
		}
 | 
						|
		while (*dirs) {
 | 
						|
		    prpre = *dirs;
 | 
						|
 | 
						|
		    if (sf2)
 | 
						|
			/* We are in the path, so add only directories. */
 | 
						|
			gen_matches_files(1, 0, 0);
 | 
						|
		    else {
 | 
						|
			if (cc->mask & CC_FILES)
 | 
						|
			    /* Add all files. */
 | 
						|
			    gen_matches_files(0, 0, 1);
 | 
						|
			else if (cc->mask & CC_COMMPATH) {
 | 
						|
			    /* Completion of command paths. */
 | 
						|
			    if (sf1 || cc->withd)
 | 
						|
				/* There is a path prefix, so add *
 | 
						|
				 * directories and executables.   */
 | 
						|
				gen_matches_files(1, 1, 0);
 | 
						|
			    else {
 | 
						|
				/* No path prefix, so add the things *
 | 
						|
				 * reachable via the PATH variable.  */
 | 
						|
				char **pc = path, *pp = prpre;
 | 
						|
 | 
						|
				for (; *pc; pc++)
 | 
						|
				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
 | 
						|
					break;
 | 
						|
				if (*pc) {
 | 
						|
				    prpre = "./";
 | 
						|
				    gen_matches_files(1, 1, 0);
 | 
						|
				    prpre = pp;
 | 
						|
				}
 | 
						|
			    }
 | 
						|
			} else if (cc->mask & CC_DIRS)
 | 
						|
			    gen_matches_files(1, 0, 0);
 | 
						|
			/* The compctl has a glob pattern (compctl -g). */
 | 
						|
			if (cc->glob) {
 | 
						|
			    int ns, pl = strlen(prpre), o, paalloc;
 | 
						|
			    char *g = dupstring(cc->glob), *pa;
 | 
						|
			    char *p2, *p3;
 | 
						|
			    int ne = noerrs, md = opts[MARKDIRS];
 | 
						|
 | 
						|
			    /* These are used in the globbing code to make *
 | 
						|
			     * things a bit faster.                        */
 | 
						|
			    if (ispattern || mstack)
 | 
						|
				glob_pre = glob_suf = NULL;
 | 
						|
			    else {
 | 
						|
				glob_pre = fpre;
 | 
						|
				glob_suf = fsuf;
 | 
						|
			    }
 | 
						|
			    noerrs = 1;
 | 
						|
			    addwhat = -6;
 | 
						|
			    o = strlen(prpre);
 | 
						|
			    pa = (char *)zalloc(paalloc = o + PATH_MAX);
 | 
						|
			    strcpy(pa, prpre);
 | 
						|
			    opts[MARKDIRS] = 0;
 | 
						|
 | 
						|
			    /* The compctl -g string may contain more than *
 | 
						|
			     * one pattern, so we need a loop.             */
 | 
						|
			    while (*g) {
 | 
						|
				LinkList l = newlinklist();
 | 
						|
				int ng;
 | 
						|
 | 
						|
				/* Find the blank terminating the pattern. */
 | 
						|
				while (*g && inblank(*g))
 | 
						|
				    g++;
 | 
						|
				/* Oops, we already reached the end of the
 | 
						|
				   string. */
 | 
						|
				if (!*g)
 | 
						|
				    break;
 | 
						|
				for (p = g + 1; *p && !inblank(*p); p++)
 | 
						|
				    if (*p == '\\' && p[1])
 | 
						|
					p++;
 | 
						|
				/* Get the pattern string. */
 | 
						|
				tokenize(g = dupstrpfx(g, p - g));
 | 
						|
				if (*g == '=')
 | 
						|
				    *g = Equals;
 | 
						|
				if (*g == '~')
 | 
						|
				    *g = Tilde;
 | 
						|
				remnulargs(g);
 | 
						|
				if ((*g == Equals || *g == Tilde) && !cc->withd) {
 | 
						|
				/* The pattern has a `~' or `=' at the  *
 | 
						|
				 * beginning, so we expand this and use *
 | 
						|
				 * the result.                          */
 | 
						|
				    filesub(&g, 0);
 | 
						|
				    addlinknode(l, dupstring(g));
 | 
						|
				} else if (*g == '/' && !cc->withd)
 | 
						|
				/* The pattern is a full path (starting *
 | 
						|
				 * with '/'), so add it unchanged.      */
 | 
						|
				    addlinknode(l, dupstring(g));
 | 
						|
				else {
 | 
						|
				/* It's a simple pattern, so append it to *
 | 
						|
				 * the path we have on the command line.  */
 | 
						|
				    int minlen = o + strlen(g);
 | 
						|
				    if (minlen >= paalloc)
 | 
						|
					pa = (char *)
 | 
						|
					    zrealloc(pa, paalloc = minlen+1);
 | 
						|
				    strcpy(pa + o, g);
 | 
						|
				    addlinknode(l, dupstring(pa));
 | 
						|
				}
 | 
						|
				/* Do the globbing. */
 | 
						|
				ng = opts[NULLGLOB];
 | 
						|
				opts[NULLGLOB] = 1;
 | 
						|
				globlist(l);
 | 
						|
				opts[NULLGLOB] = ng;
 | 
						|
				/* Get the results. */
 | 
						|
				if (nonempty(l) && peekfirst(l)) {
 | 
						|
				    for (p2 = (char *)peekfirst(l); *p2; p2++)
 | 
						|
					if (itok(*p2))
 | 
						|
					    break;
 | 
						|
				    if (!*p2) {
 | 
						|
					if ((*g == Equals || *g == Tilde ||
 | 
						|
					     *g == '/') || cc->withd) {
 | 
						|
					    /* IF the pattern started with `~',  *
 | 
						|
					     * `=', or `/', add the result only, *
 | 
						|
					     * if it really matches what we have *
 | 
						|
					     * on the line.                      *
 | 
						|
					     * Do this if an initial directory   *
 | 
						|
					     * was specified, too.               */
 | 
						|
					    while ((p2 = (char *)ugetnode(l)))
 | 
						|
						if (strpfx(prpre, p2))
 | 
						|
						    addmatch(p2 + pl, NULL);
 | 
						|
					} else {
 | 
						|
					    /* Otherwise ignore the path we *
 | 
						|
					     * prepended to the pattern.    */
 | 
						|
					    while ((p2 = p3 =
 | 
						|
						    (char *)ugetnode(l))) {
 | 
						|
						for (ns = sf1; *p3 && ns; p3++)
 | 
						|
						    if (*p3 == '/')
 | 
						|
							ns--;
 | 
						|
 | 
						|
						addmatch(p3, NULL);
 | 
						|
					    }
 | 
						|
					}
 | 
						|
				    }
 | 
						|
				}
 | 
						|
				pa[o] = '\0';
 | 
						|
				g = p;
 | 
						|
			    }
 | 
						|
			    glob_pre = glob_suf = NULL;
 | 
						|
			    noerrs = ne;
 | 
						|
			    opts[MARKDIRS] = md;
 | 
						|
 | 
						|
			    zfree(pa, paalloc);
 | 
						|
			}
 | 
						|
		    }
 | 
						|
		    dirs++;
 | 
						|
		}
 | 
						|
		prpre = NULL;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	lppre = lpsuf = NULL;
 | 
						|
	lppl = lpsl = 0;
 | 
						|
    }
 | 
						|
    if (ic) {
 | 
						|
	/* Now change the `~' and `=' tokens to the real characters so *
 | 
						|
	 * that things starting with these characters will be added.   */
 | 
						|
	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
 | 
						|
	rpl++;
 | 
						|
	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
 | 
						|
    }
 | 
						|
    if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
 | 
						|
	/* If we have to complete commands, add alias names, *
 | 
						|
	 * shell functions and builtins too.                 */
 | 
						|
	dumphashtable(aliastab, -3);
 | 
						|
	dumphashtable(reswdtab, -3);
 | 
						|
	dumphashtable(shfunctab, -3);
 | 
						|
	dumphashtable(builtintab, -3);
 | 
						|
	if (isset(HASHLISTALL))
 | 
						|
	    cmdnamtab->filltable(cmdnamtab);
 | 
						|
	dumphashtable(cmdnamtab, -3);
 | 
						|
	/* And parameter names if autocd and cdablevars are set. */
 | 
						|
	if (isset(AUTOCD) && isset(CDABLEVARS))
 | 
						|
	    dumphashtable(paramtab, -4);
 | 
						|
    }
 | 
						|
    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
 | 
						|
 | 
						|
    if (cc->mask & CC_NAMED)
 | 
						|
	/* Add named directories. */
 | 
						|
	dumphashtable(nameddirtab, addwhat);
 | 
						|
    if (cc->mask & CC_OPTIONS)
 | 
						|
	/* Add option names. */
 | 
						|
	dumphashtable(optiontab, addwhat);
 | 
						|
    if (cc->mask & CC_VARS) {
 | 
						|
	/* And parameter names. */
 | 
						|
	dumphashtable(paramtab, -9);
 | 
						|
	addwhat = oaw;
 | 
						|
    }
 | 
						|
    if (cc->mask & CC_BINDINGS) {
 | 
						|
	/* And zle function names... */
 | 
						|
	dumphashtable(thingytab, CC_BINDINGS);
 | 
						|
	addwhat = oaw;
 | 
						|
    }
 | 
						|
    if (cc->keyvar) {
 | 
						|
	/* This adds things given to the compctl -k flag *
 | 
						|
	 * (from a parameter or a list of words).        */
 | 
						|
	char **usr = get_user_var(cc->keyvar);
 | 
						|
 | 
						|
	if (usr)
 | 
						|
	    while (*usr)
 | 
						|
		addmatch(*usr++, NULL);
 | 
						|
    }
 | 
						|
    if (cc->mask & CC_USERS) {
 | 
						|
	/* Add user names. */
 | 
						|
	maketildelist();
 | 
						|
	addwhat = oaw;
 | 
						|
    }
 | 
						|
    if (cc->func) {
 | 
						|
	/* This handles the compctl -K flag. */
 | 
						|
	List list;
 | 
						|
	char **r;
 | 
						|
	int lv = lastval;
 | 
						|
	    
 | 
						|
	/* Get the function. */
 | 
						|
	if ((list = getshfunc(cc->func)) != &dummy_list) {
 | 
						|
	    /* We have it, so build a argument list. */
 | 
						|
	    LinkList args = newlinklist();
 | 
						|
	    int osc = sfcontext;
 | 
						|
		
 | 
						|
	    addlinknode(args, cc->func);
 | 
						|
		
 | 
						|
	    if (delit) {
 | 
						|
		p = dupstrpfx(os, ooffs);
 | 
						|
		untokenize(p);
 | 
						|
		addlinknode(args, p);
 | 
						|
		p = dupstring(os + ooffs);
 | 
						|
		untokenize(p);
 | 
						|
		addlinknode(args, p);
 | 
						|
	    } else {
 | 
						|
		addlinknode(args, lpre);
 | 
						|
		addlinknode(args, lsuf);
 | 
						|
	    }
 | 
						|
		
 | 
						|
	    /* This flag allows us to use read -l and -c. */
 | 
						|
	    if (incompfunc != 1)
 | 
						|
		incompctlfunc = 1;
 | 
						|
	    sfcontext = SFC_COMPLETE;
 | 
						|
	    /* Call the function. */
 | 
						|
	    doshfunc(cc->func, list, args, 0, 1);
 | 
						|
	    sfcontext = osc;
 | 
						|
	    incompctlfunc = 0;
 | 
						|
	    /* And get the result from the reply parameter. */
 | 
						|
	    if ((r = get_user_var("reply")))
 | 
						|
		while (*r)
 | 
						|
		    addmatch(*r++, NULL);
 | 
						|
	}
 | 
						|
	lastval = lv;
 | 
						|
    }
 | 
						|
    if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
 | 
						|
	/* Get job names. */
 | 
						|
	int i;
 | 
						|
	char *j;
 | 
						|
 | 
						|
	for (i = 0; i < MAXJOB; i++)
 | 
						|
	    if ((jobtab[i].stat & STAT_INUSE) &&
 | 
						|
		jobtab[i].procs && jobtab[i].procs->text) {
 | 
						|
		int stopped = jobtab[i].stat & STAT_STOPPED;
 | 
						|
 | 
						|
		j = dupstring(jobtab[i].procs->text);
 | 
						|
		if ((cc->mask & CC_JOBS) ||
 | 
						|
		    (stopped && (cc->mask & CC_STOPPED)) ||
 | 
						|
		    (!stopped && (cc->mask & CC_RUNNING)))
 | 
						|
		    addmatch(j, NULL);
 | 
						|
	    }
 | 
						|
    }
 | 
						|
    if (cc->str) {
 | 
						|
	/* Get the stuff from a compctl -s. */
 | 
						|
	LinkList foo = newlinklist();
 | 
						|
	LinkNode n;
 | 
						|
	int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
 | 
						|
	char *tmpbuf;
 | 
						|
 | 
						|
	opts[NULLGLOB] = 1;
 | 
						|
 | 
						|
	/* Put the string in the lexer buffer and call the lexer to *
 | 
						|
	 * get the words we have to expand.                        */
 | 
						|
	zleparse = 1;
 | 
						|
	lexsave();
 | 
						|
	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
 | 
						|
	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
 | 
						|
	inpush(tmpbuf, 0, NULL);
 | 
						|
	strinbeg(0);
 | 
						|
	noaliases = 1;
 | 
						|
	do {
 | 
						|
	    ctxtlex();
 | 
						|
	    if (tok == ENDINPUT || tok == LEXERR)
 | 
						|
		break;
 | 
						|
	    if (!first && tokstr && *tokstr)
 | 
						|
		addlinknode(foo, ztrdup(tokstr));
 | 
						|
	    first = 0;
 | 
						|
	} while (tok != ENDINPUT && tok != LEXERR);
 | 
						|
	noaliases = 0;
 | 
						|
	strinend();
 | 
						|
	inpop();
 | 
						|
	errflag = zleparse = 0;
 | 
						|
	lexrestore();
 | 
						|
	/* Fine, now do full expansion. */
 | 
						|
	prefork(foo, 0);
 | 
						|
	if (!errflag) {
 | 
						|
	    globlist(foo);
 | 
						|
	    if (!errflag)
 | 
						|
		/* And add the resulting words as matches. */
 | 
						|
		for (n = firstnode(foo); n; incnode(n))
 | 
						|
		    addmatch((char *)n->dat, NULL);
 | 
						|
	}
 | 
						|
	opts[NULLGLOB] = ng;
 | 
						|
	we = oowe;
 | 
						|
	wb = oowb;
 | 
						|
    }
 | 
						|
    if (cc->hpat) {
 | 
						|
	/* We have a pattern to take things from the history. */
 | 
						|
	Patprog pprogc = NULL;
 | 
						|
	char *e, *h, hpatsav;
 | 
						|
	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
 | 
						|
	Histent he = quietgethistent(i, GETHIST_UPWARD);
 | 
						|
 | 
						|
	/* Parse the pattern, if it isn't the null string. */
 | 
						|
	if (*(cc->hpat)) {
 | 
						|
	    char *thpat = dupstring(cc->hpat);
 | 
						|
 | 
						|
	    tokenize(thpat);
 | 
						|
	    pprogc = patcompile(thpat, 0, NULL);
 | 
						|
	}
 | 
						|
	/* n holds the number of history line we have to search. */
 | 
						|
	if (!n)
 | 
						|
	    n = -1;
 | 
						|
 | 
						|
	/* Now search the history. */
 | 
						|
	while (n-- && he) {
 | 
						|
	    int iwords;
 | 
						|
	    for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
 | 
						|
		h = he->text + he->words[iwords*2];
 | 
						|
		e = he->text + he->words[iwords*2+1];
 | 
						|
		hpatsav = *e;
 | 
						|
		*e = '\0';
 | 
						|
		/* We now have a word from the history, ignore it *
 | 
						|
		 * if it begins with a quote or `$'.              */
 | 
						|
		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
 | 
						|
		    (!pprogc || pattry(pprogc, h)))
 | 
						|
		    /* Otherwise add it if it was matched. */
 | 
						|
		    addmatch(dupstring(h), NULL);
 | 
						|
		if (hpatsav)
 | 
						|
		    *e = hpatsav;
 | 
						|
	    }
 | 
						|
	    he = up_histent(he);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
 | 
						|
			 CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
 | 
						|
	/* Add various flavours of parameters. */
 | 
						|
	dumphashtable(paramtab, t);
 | 
						|
    if ((t = cc->mask & CC_SHFUNCS))
 | 
						|
	/* Add shell functions. */
 | 
						|
	dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 | 
						|
    if ((t = cc->mask & CC_BUILTINS))
 | 
						|
	/* Add builtins. */
 | 
						|
	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 | 
						|
    if ((t = cc->mask & CC_EXTCMDS)) {
 | 
						|
	/* Add external commands */
 | 
						|
	if (isset(HASHLISTALL))
 | 
						|
	    cmdnamtab->filltable(cmdnamtab);
 | 
						|
	dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 | 
						|
    }
 | 
						|
    if ((t = cc->mask & CC_RESWDS))
 | 
						|
	/* Add reserved words */
 | 
						|
	dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 | 
						|
    if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
 | 
						|
	/* Add the two types of aliases. */
 | 
						|
	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 | 
						|
    if (keypm && cc == &cc_dummy) {
 | 
						|
	/* Add the keys of the parameter in keypm. */
 | 
						|
	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
 | 
						|
	keypm = NULL;
 | 
						|
	cc_dummy.suffix = NULL;
 | 
						|
    }
 | 
						|
    if (!errflag && cc->ylist) {
 | 
						|
	/* generate the user-defined display list: if anything fails, *
 | 
						|
	 * we silently allow the normal completion list to be used.   */
 | 
						|
	char **yaptr = NULL, *uv = NULL;
 | 
						|
	List list;
 | 
						|
 | 
						|
	if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
 | 
						|
	    /* from variable */
 | 
						|
	    uv = cc->ylist + (cc->ylist[0] == '$');
 | 
						|
	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
 | 
						|
	    /* from function:  pass completions as arg list */
 | 
						|
	    LinkList args = newlinklist();
 | 
						|
	    LinkNode ln;
 | 
						|
	    Cmatch m;
 | 
						|
	    int osc = sfcontext;
 | 
						|
 | 
						|
	    addlinknode(args, cc->ylist);
 | 
						|
	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
 | 
						|
		m = (Cmatch) getdata(ln);
 | 
						|
		if (m->ppre) {
 | 
						|
		    char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
 | 
						|
					      strlen(m->psuf) + 1);
 | 
						|
 | 
						|
		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
 | 
						|
		    addlinknode(args, dupstring(p));
 | 
						|
		} else
 | 
						|
		    addlinknode(args, dupstring(m->str));
 | 
						|
	    }
 | 
						|
 | 
						|
	    /* No harm in allowing read -l and -c here, too */
 | 
						|
	    if (incompfunc != 1)
 | 
						|
		incompctlfunc = 1;
 | 
						|
	    sfcontext = SFC_COMPLETE;
 | 
						|
	    doshfunc(cc->ylist, list, args, 0, 1);
 | 
						|
	    sfcontext = osc;
 | 
						|
	    incompctlfunc = 0;
 | 
						|
	    uv = "reply";
 | 
						|
	}
 | 
						|
	if (uv)
 | 
						|
	    yaptr = get_user_var(uv);
 | 
						|
	if ((tt = cc->explain)) {
 | 
						|
	    tt = dupstring(tt);
 | 
						|
	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
 | 
						|
		singsub(&tt);
 | 
						|
		untokenize(tt);
 | 
						|
	    }
 | 
						|
	    curexpl->str = tt;
 | 
						|
	    if (cc->gname) {
 | 
						|
		endcmgroup(yaptr);
 | 
						|
		begcmgroup(cc->gname, gflags);
 | 
						|
		addexpl();
 | 
						|
	    } else {
 | 
						|
		addexpl();
 | 
						|
		endcmgroup(yaptr);
 | 
						|
		begcmgroup("default", 0);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    } else if ((tt = cc->explain)) {
 | 
						|
	tt = dupstring(tt);
 | 
						|
	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
 | 
						|
	    singsub(&tt);
 | 
						|
	    untokenize(tt);
 | 
						|
	}
 | 
						|
	curexpl->str = tt;
 | 
						|
	addexpl();
 | 
						|
    }
 | 
						|
    if (cc->subcmd) {
 | 
						|
	/* Handle -l sub-completion. */
 | 
						|
	char **ow = clwords, *os = cmdstr, *ops = NULL;
 | 
						|
	int oldn = clwnum, oldp = clwpos, br;
 | 
						|
	unsigned long occ = ccont;
 | 
						|
	
 | 
						|
	ccont = CC_CCCONT;
 | 
						|
	
 | 
						|
	/* So we restrict the words-array. */
 | 
						|
	if (brange >= clwnum)
 | 
						|
	    brange = clwnum - 1;
 | 
						|
	if (brange < 1)
 | 
						|
	    brange = 1;
 | 
						|
	if (erange >= clwnum)
 | 
						|
	    erange = clwnum - 1;
 | 
						|
	if (erange < 1)
 | 
						|
	    erange = 1;
 | 
						|
	clwnum = erange - brange + 1;
 | 
						|
	clwpos = clwpos - brange;
 | 
						|
	br = brange;
 | 
						|
 | 
						|
	if (cc->subcmd[0]) {
 | 
						|
	    /* And probably put the command name given to the flag *
 | 
						|
	     * in the array.                                       */
 | 
						|
	    clwpos++;
 | 
						|
	    clwnum++;
 | 
						|
	    incmd = 0;
 | 
						|
	    ops = clwords[br - 1];
 | 
						|
	    clwords[br - 1] = ztrdup(cc->subcmd);
 | 
						|
	    cmdstr = ztrdup(cc->subcmd);
 | 
						|
	    clwords += br - 1;
 | 
						|
	} else {
 | 
						|
	    cmdstr = ztrdup(clwords[br]);
 | 
						|
	    incmd = !clwpos;
 | 
						|
	    clwords += br;
 | 
						|
	}
 | 
						|
	/* Produce the matches. */
 | 
						|
	makecomplistcmd(s, incmd, CFN_FIRST);
 | 
						|
 | 
						|
	/* And restore the things we changed. */
 | 
						|
	clwords = ow;
 | 
						|
	zsfree(cmdstr);
 | 
						|
	cmdstr = os;
 | 
						|
	clwnum = oldn;
 | 
						|
	clwpos = oldp;
 | 
						|
	if (ops) {
 | 
						|
	    zsfree(clwords[br - 1]);
 | 
						|
	    clwords[br - 1] = ops;
 | 
						|
	}
 | 
						|
	ccont = occ;
 | 
						|
    }
 | 
						|
    if (cc->substr)
 | 
						|
	sep_comp_string(cc->substr, s, offs);
 | 
						|
    uremnode(ccstack, firstnode(ccstack));
 | 
						|
    if (cc->matcher)
 | 
						|
	mstack = mstack->next;
 | 
						|
 | 
						|
    if (mn == mnum)
 | 
						|
	haspattern = ohp;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static struct builtin bintab[] = {
 | 
						|
    BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
 | 
						|
    BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 | 
						|
};
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
setup_(Module m)
 | 
						|
{
 | 
						|
    compctlreadptr = compctlread;
 | 
						|
    createcompctltable();
 | 
						|
    cc_compos.mask = CC_COMMPATH;
 | 
						|
    cc_compos.mask2 = 0;
 | 
						|
    cc_default.refc = 10000;
 | 
						|
    cc_default.mask = CC_FILES;
 | 
						|
    cc_default.mask2 = 0;
 | 
						|
    cc_first.refc = 10000;
 | 
						|
    cc_first.mask = 0;
 | 
						|
    cc_first.mask2 = CC_CCCONT;
 | 
						|
 | 
						|
    lastccused = NULL;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
boot_(Module m)
 | 
						|
{
 | 
						|
    addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
 | 
						|
    addhookfunc("compctl_before", (Hookfn) ccbeforehookfn);
 | 
						|
    addhookfunc("compctl_after", (Hookfn) ccafterhookfn);
 | 
						|
    return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1);
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
cleanup_(Module m)
 | 
						|
{
 | 
						|
    deletehookfunc("compctl_make", (Hookfn) ccmakehookfn);
 | 
						|
    deletehookfunc("compctl_before", (Hookfn) ccbeforehookfn);
 | 
						|
    deletehookfunc("compctl_after", (Hookfn) ccafterhookfn);
 | 
						|
    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
int
 | 
						|
finish_(Module m)
 | 
						|
{
 | 
						|
    deletehashtable(compctltab);
 | 
						|
 | 
						|
    if (lastccused)
 | 
						|
	freelinklist(lastccused, (FreeFunc) freecompctl);
 | 
						|
 | 
						|
    compctlreadptr = fallback_compctlread;
 | 
						|
    return 0;
 | 
						|
}
 |