mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-30 17:50:58 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1757 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1757 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * complete.c - the complete module, interface part
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1999 Sven Wischnowsky
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Sven Wischnowsky and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "complete.mdh"
 | |
| #include "complete.pro"
 | |
| 
 | |
| /* global variables for shell parameters in new style completion */
 | |
| 
 | |
| /**/
 | |
| mod_export
 | |
| zlong compcurrent,
 | |
|       complistmax;
 | |
| /**/
 | |
| zlong complistlines,
 | |
|       compignored;
 | |
| 
 | |
| /**/
 | |
| mod_export
 | |
| char **compwords,
 | |
|      **compredirs,
 | |
|      *compprefix,
 | |
|      *compsuffix,
 | |
|      *complastprefix,
 | |
|      *complastsuffix,
 | |
|      *compisuffix,
 | |
|      *compqiprefix,
 | |
|      *compqisuffix,
 | |
|      *compquote,
 | |
|      *compqstack,      /* compstate[all_quotes] */
 | |
|      *comppatmatch,
 | |
|      *complastprompt;
 | |
| /**/
 | |
| char *compiprefix,
 | |
|      *compcontext,
 | |
|      *compparameter,
 | |
|      *compredirect,
 | |
|      *compquoting,
 | |
|      *comprestore,
 | |
|      *complist,
 | |
|      *compinsert,
 | |
|      *compexact,
 | |
|      *compexactstr,
 | |
|      *comppatinsert,
 | |
|      *comptoend,      /* compstate[to_end]; populates 'movetoend' */
 | |
|      *compoldlist,
 | |
|      *compoldins,
 | |
|      *compvared;
 | |
| 
 | |
| /*
 | |
|  * An array of Param structures for compsys special parameters;
 | |
|  * see 'comprparams' below.  An entry for $compstate is added
 | |
|  * by makecompparams().
 | |
|  *
 | |
|  * See CP_REALPARAMS.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| Param *comprpms;
 | |
| 
 | |
| /* 
 | |
|  * An array of Param structures for elemens of $compstate; see
 | |
|  * 'compkparams' below.
 | |
|  *
 | |
|  * See CP_KEYPARAMS.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| Param *compkpms;
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| freecmlist(Cmlist l)
 | |
| {
 | |
|     Cmlist n;
 | |
| 
 | |
|     while (l) {
 | |
| 	n = l->next;
 | |
| 	freecmatcher(l->matcher);
 | |
| 	zsfree(l->str);
 | |
| 
 | |
| 	zfree(l, sizeof(struct cmlist));
 | |
| 
 | |
| 	l = n;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| freecmatcher(Cmatcher m)
 | |
| {
 | |
|     Cmatcher n;
 | |
| 
 | |
|     if (!m || --(m->refc))
 | |
| 	return;
 | |
| 
 | |
|     while (m) {
 | |
| 	n = m->next;
 | |
| 	freecpattern(m->line);
 | |
| 	freecpattern(m->word);
 | |
| 	freecpattern(m->left);
 | |
| 	freecpattern(m->right);
 | |
| 
 | |
| 	zfree(m, sizeof(struct cmatcher));
 | |
| 
 | |
| 	m = n;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| freecpattern(Cpattern p)
 | |
| {
 | |
|     Cpattern n;
 | |
| 
 | |
|     while (p) {
 | |
| 	n = p->next;
 | |
| 	if (p->tp <= CPAT_EQUIV)
 | |
| 	    free(p->u.str);
 | |
| 	zfree(p, sizeof(struct cpattern));
 | |
| 
 | |
| 	p = n;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Copy a completion matcher list into permanent storage. */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmatcher
 | |
| cpcmatcher(Cmatcher m)
 | |
| {
 | |
|     Cmatcher r = NULL, *p = &r, n;
 | |
| 
 | |
|     while (m) {
 | |
| 	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
 | |
| 
 | |
| 	n->refc = 1;
 | |
| 	n->next = NULL;
 | |
| 	n->flags = m->flags;
 | |
| 	n->line = cpcpattern(m->line);
 | |
| 	n->llen = m->llen;
 | |
| 	n->word = cpcpattern(m->word);
 | |
| 	n->wlen = m->wlen;
 | |
| 	n->left = cpcpattern(m->left);
 | |
| 	n->lalen = m->lalen;
 | |
| 	n->right = cpcpattern(m->right);
 | |
| 	n->ralen = m->ralen;
 | |
| 
 | |
| 	p = &(n->next);
 | |
| 	m = m->next;
 | |
|     }
 | |
|     return r;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Copy a single entry in a matcher pattern.
 | |
|  * If useheap is 1, it comes from the heap.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export Cpattern
 | |
| cp_cpattern_element(Cpattern o)
 | |
| {
 | |
|     Cpattern n = zalloc(sizeof(struct cpattern));
 | |
| 
 | |
|     n->next = NULL;
 | |
| 
 | |
|     n->tp = o->tp;
 | |
|     switch (o->tp)
 | |
|     {
 | |
|     case CPAT_CCLASS:
 | |
|     case CPAT_NCLASS:
 | |
|     case CPAT_EQUIV:
 | |
| 	n->u.str = ztrdup(o->u.str);
 | |
| 	break;
 | |
| 
 | |
|     case CPAT_CHAR:
 | |
| 	n->u.chr = o->u.chr;
 | |
| 	break;
 | |
| 
 | |
|     default:
 | |
| 	/* just to keep compiler quiet */
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|     return n;
 | |
| }
 | |
| 
 | |
| /* Copy a completion matcher pattern. */
 | |
| 
 | |
| /**/
 | |
| static Cpattern
 | |
| cpcpattern(Cpattern o)
 | |
| {
 | |
|     Cpattern r = NULL, *p = &r;
 | |
| 
 | |
|     while (o) {
 | |
| 	*p = cp_cpattern_element(o);
 | |
| 	p = &((*p)->next);
 | |
| 	o = o->next;
 | |
|     }
 | |
|     return r;
 | |
| }
 | |
| 
 | |
| /* 
 | |
|  * Parse a string for matcher control, containing multiple matchers.
 | |
|  *
 | |
|  * 's' is the string to be parsed.
 | |
|  *
 | |
|  * 'name' is the name of the builtin from which this is called, for errors.
 | |
|  *
 | |
|  * Return 'pcm_err' on error; a NULL return value means ...
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| mod_export Cmatcher
 | |
| parse_cmatcher(char *name, char *s)
 | |
| {
 | |
|     Cmatcher ret = NULL, r = NULL, n;
 | |
|     Cpattern line, word, left, right;
 | |
|     int fl, fl2, ll, wl, lal, ral, err, both;
 | |
| 
 | |
|     if (!*s)
 | |
| 	return NULL;
 | |
| 
 | |
|     while (*s) {
 | |
| 	lal = ral = both = fl2 = 0;
 | |
| 	left = right = NULL;
 | |
| 
 | |
| 	while (*s && inblank(*s)) s++;
 | |
| 
 | |
| 	if (!*s) break;
 | |
| 
 | |
| 	switch (*s) {
 | |
| 	case 'b': fl2 = CMF_INTER; /* FALLTHROUGH */
 | |
| 	case 'l': fl = CMF_LEFT; break;
 | |
| 	case 'e': fl2 = CMF_INTER; /* FALLTHROUGH */
 | |
| 	case 'r': fl = CMF_RIGHT; break;
 | |
| 	case 'm': fl = 0; break;
 | |
| 	case 'B': fl2 = CMF_INTER; /* FALLTHROUGH */
 | |
| 	case 'L': fl = CMF_LEFT | CMF_LINE; break;
 | |
| 	case 'E': fl2 = CMF_INTER; /* FALLTHROUGH */
 | |
| 	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
 | |
| 	case 'M': fl = CMF_LINE; break;
 | |
| 	case 'x': break;
 | |
| 	default:
 | |
| 	    if (name)
 | |
| 		zwarnnam(name, "unknown match specification character `%c'",
 | |
| 			 *s);
 | |
| 	    return pcm_err;
 | |
| 	}
 | |
| 	if (s[1] != ':') {
 | |
| 	    if (name)
 | |
| 		zwarnnam(name, "missing `:'");
 | |
| 	    return pcm_err;
 | |
| 	}
 | |
| 	if (*s == 'x') {
 | |
| 	    if (s[2] && !inblank(s[2])) {
 | |
| 		if (name)
 | |
| 		    zwarnnam(name,
 | |
| 			"unexpected pattern following x: specification");
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	    return ret;
 | |
| 	}
 | |
| 	s += 2;
 | |
| 	if (!*s) {
 | |
| 	    if (name)
 | |
| 		zwarnnam(name, "missing patterns");
 | |
| 	    return pcm_err;
 | |
| 	}
 | |
| 	if ((fl & CMF_LEFT) && !fl2) {
 | |
| 	    left = parse_pattern(name, &s, &lal, '|', &err);
 | |
| 	    if (err)
 | |
| 		return pcm_err;
 | |
| 
 | |
| 	    if ((both = (*s && s[1] == '|')))
 | |
| 		s++;
 | |
| 
 | |
| 	    if (!*s || !*++s) {
 | |
| 		if (name) {
 | |
|                    if (both)
 | |
|                        zwarnnam(name, "missing right anchor");
 | |
|                    else
 | |
|                        zwarnnam(name, "missing line pattern");
 | |
| 		}
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	} else
 | |
| 	    left = NULL;
 | |
| 
 | |
| 	line = parse_pattern(name, &s, &ll,
 | |
| 			     (((fl & CMF_RIGHT) && !fl2) ? '|' : '='),
 | |
| 			     &err);
 | |
| 	if (err)
 | |
| 	    return pcm_err;
 | |
| 	if (both) {
 | |
| 	    right = line;
 | |
| 	    ral = ll;
 | |
| 	    line = NULL;
 | |
| 	    ll = 0;
 | |
| 	}
 | |
| 	if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) {
 | |
| 	    if (name)
 | |
| 		zwarnnam(name, "missing right anchor");
 | |
|            return pcm_err;
 | |
| 	} else if (!(fl & CMF_RIGHT) || fl2) {
 | |
| 	    if (!*s) {
 | |
| 		if (name)
 | |
| 		    zwarnnam(name, "missing word pattern");
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	    s++;
 | |
| 	}
 | |
| 	if ((fl & CMF_RIGHT) && !fl2) {
 | |
| 	    if (*s == '|') {
 | |
| 		left = line;
 | |
| 		lal = ll;
 | |
| 		line = NULL;
 | |
| 		ll = 0;
 | |
| 		s++;
 | |
| 	    }
 | |
| 	    right = parse_pattern(name, &s, &ral, '=', &err);
 | |
| 	    if (err)
 | |
| 		return pcm_err;
 | |
| 	    if (!*s) {
 | |
| 		if (name)
 | |
| 		    zwarnnam(name, "missing word pattern");
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	    s++;
 | |
|        }
 | |
| 
 | |
| 	if (*s == '*') {
 | |
| 	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
 | |
| 		if (name)
 | |
| 		    zwarnnam(name, "need anchor for `*'");
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	    word = NULL;
 | |
| 	    if (*++s == '*') {
 | |
| 		s++;
 | |
| 		wl = -2;
 | |
| 	    } else
 | |
| 		wl = -1;
 | |
| 	} else {
 | |
| 	    word = parse_pattern(name, &s, &wl, 0, &err);
 | |
| 
 | |
| 	    if (!word && !line) {
 | |
| 		if (name)
 | |
| 		    zwarnnam(name, "need non-empty word or line pattern");
 | |
| 		return pcm_err;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (err)
 | |
| 	    return pcm_err;
 | |
| 
 | |
| 	n = (Cmatcher) hcalloc(sizeof(*ret));
 | |
| 	n->next = NULL;
 | |
| 	n->flags = fl | fl2;
 | |
| 	n->line = line;
 | |
| 	n->llen = ll;
 | |
| 	n->word = word;
 | |
| 	n->wlen = wl;
 | |
| 	n->left = left;
 | |
| 	n->lalen = lal;
 | |
| 	n->right = right;
 | |
| 	n->ralen = ral;
 | |
| 
 | |
| 	if (ret)
 | |
| 	    r->next = n;
 | |
| 	else
 | |
| 	    ret = n;
 | |
| 
 | |
| 	r = n;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Parse a pattern for matcher control. 
 | |
|  * name is the name of the builtin from which this is called, for errors.
 | |
|  * *sp is the input string and will be updated to the end of the parsed
 | |
|  *   pattern.
 | |
|  * *lp will be set to the number of characters (possibly multibyte)
 | |
|  *   that the pattern will match.  This must be deterministic, given
 | |
|  *   the syntax allowed here.
 | |
|  * e, if non-zero, is the ASCII end character to match; if zero,
 | |
|  *   stop on a blank.
 | |
|  * *err is set to 1 to indicate an error, else to 0.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| static Cpattern
 | |
| parse_pattern(char *name, char **sp, int *lp, char e, int *err)
 | |
| {
 | |
|     Cpattern ret = NULL, r = NULL, n;
 | |
|     char *s = *sp;
 | |
|     convchar_t inchar;
 | |
|     int l = 0, inlen;
 | |
| 
 | |
|     *err = 0;
 | |
| 
 | |
|     MB_METACHARINIT();
 | |
|     while (*s && (e ? (*s != e) : !inblank(*s))) {
 | |
| 	n = (Cpattern) hcalloc(sizeof(*n));
 | |
| 	n->next = NULL;
 | |
| 
 | |
| 	if (*s == '[' || *s == '{') {
 | |
| 	    s = parse_class(n, s);
 | |
| 	    if (!*s) {
 | |
| 		*err = 1;
 | |
| 		zwarnnam(name, "unterminated character class");
 | |
| 		return NULL;
 | |
| 	    }
 | |
| 	    s++;
 | |
| 	} else if (*s == '?') {
 | |
| 	    n->tp = CPAT_ANY;
 | |
| 	    s++;
 | |
| 	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
 | |
| 	    *err = 1;
 | |
| 	    zwarnnam(name, "invalid pattern character `%c'", *s);
 | |
| 	    return NULL;
 | |
| 	} else {
 | |
| 	    if (*s == '\\' && s[1])
 | |
| 		s++;
 | |
| 
 | |
| 	    inlen = MB_METACHARLENCONV(s, &inchar);
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	    if (inchar == WEOF)
 | |
| 		inchar = (convchar_t)(*s == Meta ? s[1] ^ 32 : *s);
 | |
| #endif
 | |
| 	    s += inlen;
 | |
| 	    n->tp = CPAT_CHAR;
 | |
| 	    n->u.chr = inchar;
 | |
| 	}
 | |
| 	if (ret)
 | |
| 	    r->next = n;
 | |
| 	else
 | |
| 	    ret = n;
 | |
| 
 | |
| 	r = n;
 | |
| 
 | |
| 	l++;
 | |
|     }
 | |
|     *sp = (char *) s;
 | |
|     *lp = l;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Parse a character class for matcher control. */
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| parse_class(Cpattern p, char *iptr)
 | |
| {
 | |
|     int endchar, firsttime = 1;
 | |
|     char *optr, *nptr;
 | |
| 
 | |
|     if (*iptr++ == '[') {
 | |
| 	endchar = ']';
 | |
| 	/* TODO: surely [^]] is valid? */
 | |
| 	if ((*iptr == '!' || *iptr == '^') && iptr[1] != ']') {
 | |
| 	    p->tp = CPAT_NCLASS;
 | |
| 	    iptr++;
 | |
| 	} else
 | |
| 	    p->tp = CPAT_CCLASS;
 | |
|     } else {
 | |
| 	endchar = '}';
 | |
| 	p->tp = CPAT_EQUIV;
 | |
|     }
 | |
| 
 | |
|     /* find end of class.  End character can appear literally first. */
 | |
|     for (optr = iptr; optr == iptr || *optr != endchar; optr++)
 | |
| 	if (!*optr)
 | |
| 	    return optr;
 | |
|     /*
 | |
|      * We can always fit the parsed class within the same length
 | |
|      * because of the tokenization (including a null byte).
 | |
|      *
 | |
|      * As the input string is metafied, but shouldn't contain shell
 | |
|      * tokens, we can just add our own tokens willy nilly.
 | |
|      */
 | |
|     optr = p->u.str = zhalloc((optr-iptr) + 1);
 | |
| 
 | |
|     while (firsttime || *iptr != endchar) {
 | |
| 	int ch;
 | |
| 
 | |
| 	if (*iptr == '[' && iptr[1] == ':' &&
 | |
| 	    (nptr = strchr((char *)iptr + 2, ':')) && nptr[1] == ']') {
 | |
| 	    /* Range type */
 | |
| 	    iptr += 2;
 | |
| 	    ch = range_type((char *)iptr, nptr-iptr);
 | |
| 	    iptr = nptr + 2;
 | |
| 	    if (ch != PP_UNKWN)
 | |
| 		*optr++ = STOUC(Meta) + ch;
 | |
| 	} else {
 | |
| 	    /* characters stay metafied */
 | |
| 	    char *ptr1 = iptr;
 | |
| 	    if (*iptr == Meta)
 | |
| 		iptr++;
 | |
| 	    iptr++;
 | |
| 	    if (*iptr == '-' && iptr[1] && iptr[1] != endchar) {
 | |
| 		/* a run of characters */
 | |
| 		iptr++;
 | |
| 		/* range token */
 | |
| 		*optr++ = Meta + PP_RANGE;
 | |
| 
 | |
| 		/* start of range character */
 | |
| 		if (*ptr1 == Meta) {
 | |
| 		    *optr++ = Meta;
 | |
| 		    *optr++ = ptr1[1] ^ 32;
 | |
| 		} else
 | |
| 		    *optr++ = *ptr1;
 | |
| 
 | |
| 		if (*iptr == Meta) {
 | |
| 		    *optr++ = *iptr++;
 | |
| 		    *optr++ = *iptr++;
 | |
| 		} else
 | |
| 		    *optr++ = *iptr++;
 | |
| 	    } else {
 | |
| 		if (*ptr1 == Meta) {
 | |
| 		    *optr++ = Meta;
 | |
| 		    *optr++ = ptr1[1] ^ 32;
 | |
| 		} else
 | |
| 		    *optr++ = *ptr1;
 | |
| 	    }
 | |
| 	}
 | |
| 	firsttime = 0;
 | |
|     }
 | |
| 
 | |
|     *optr = '\0';
 | |
|     return iptr;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 | |
| {
 | |
|     struct cadata dat;
 | |
|     char *mstr = NULL; /* argument of -M options, accumulated */
 | |
|     int added; /* return value */
 | |
|     Cmatcher match = NULL;
 | |
| 
 | |
|     if (incompfunc != 1) {
 | |
| 	zwarnnam(name, "can only be called from completion function");
 | |
| 	return 1;
 | |
|     }
 | |
|     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg =
 | |
| 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
 | |
| 	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
 | |
|     dat.match = NULL;
 | |
|     dat.flags = 0;
 | |
|     dat.aflags = CAF_MATCH;
 | |
|     dat.dummies = -1;
 | |
| 
 | |
|     for (; *argv && **argv ==  '-'; argv++) {
 | |
| 	char *p; /* loop variable, points into argv */
 | |
| 	if (!(*argv)[1]) {
 | |
| 	    argv++;
 | |
| 	    break;
 | |
| 	}
 | |
| 	for (p = *argv + 1; *p; p++) {
 | |
| 	    char *m = NULL; /* argument of -M option (this one only) */
 | |
| 	    char **sp = NULL; /* the argument to an option should be copied
 | |
| 				 to *sp. */
 | |
| 	    const char *e; /* error message */
 | |
| 	    switch (*p) {
 | |
| 	    case 'q':
 | |
| 		dat.flags |= CMF_REMOVE;
 | |
| 		break;
 | |
| 	    case 'Q':
 | |
| 		dat.aflags |= CAF_QUOTE;
 | |
| 		break;
 | |
| 	    case 'C':
 | |
| 		dat.aflags |= CAF_ALL;
 | |
| 		break;
 | |
| 	    case 'f':
 | |
| 		dat.flags |= CMF_FILE;
 | |
| 		break;
 | |
| 	    case 'e':
 | |
| 		dat.flags |= CMF_ISPAR;
 | |
| 		break;
 | |
| 	    case 'a':
 | |
| 		dat.aflags |= CAF_ARRAYS;
 | |
| 		break;
 | |
| 	    case 'k':
 | |
| 		dat.aflags |= CAF_ARRAYS|CAF_KEYS;
 | |
| 		break;
 | |
| 	    case 'F':
 | |
| 		sp = &(dat.ign);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'n':
 | |
| 		dat.flags |= CMF_NOLIST;
 | |
| 		break;
 | |
| 	    case 'U':
 | |
| 		dat.aflags &= ~CAF_MATCH;
 | |
| 		break;
 | |
| 	    case 'P':
 | |
| 		sp = &(dat.pre);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'S':
 | |
| 		sp = &(dat.suf);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'J':
 | |
| 		sp = &(dat.group);
 | |
| 		e = "group name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'V':
 | |
| 		if (!dat.group)
 | |
| 		    dat.aflags |= CAF_NOSORT;
 | |
| 		sp = &(dat.group);
 | |
| 		e = "group name expected after -%c";
 | |
| 		break;
 | |
| 	    case '1':
 | |
| 		if (!(dat.aflags & CAF_UNIQCON))
 | |
| 		    dat.aflags |= CAF_UNIQALL;
 | |
| 		break;
 | |
| 	    case '2':
 | |
| 		if (!(dat.aflags & CAF_UNIQALL))
 | |
| 		    dat.aflags |= CAF_UNIQCON;
 | |
| 		break;
 | |
| 	    case 'i':
 | |
| 		sp = &(dat.ipre);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'I':
 | |
| 		sp = &(dat.isuf);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'p':
 | |
| 		sp = &(dat.ppre);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 's':
 | |
| 		sp = &(dat.psuf);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'W':
 | |
| 		sp = &(dat.prpre);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'M':
 | |
| 		sp = &m;
 | |
| 		e = "matching specification expected after -%c";
 | |
| 		break;
 | |
| 	    case 'X':
 | |
| 		sp = &(dat.exp);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'x':
 | |
| 		sp = &(dat.mesg);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'r':
 | |
| 		dat.flags |= CMF_REMOVE;
 | |
| 		sp = &(dat.rems);
 | |
| 		e = "string expected after -%c";
 | |
| 		break;
 | |
| 	    case 'R':
 | |
| 		dat.flags |= CMF_REMOVE;
 | |
| 		sp = &(dat.remf);
 | |
| 		e = "function name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'A':
 | |
| 		sp = &(dat.apar);
 | |
| 		e = "parameter name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'O':
 | |
| 		sp = &(dat.opar);
 | |
| 		e = "parameter name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'D':
 | |
| 		sp = &(dat.dpar);
 | |
| 		e = "parameter name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'd':
 | |
| 		sp = &(dat.disp);
 | |
| 		e = "parameter name expected after -%c";
 | |
| 		break;
 | |
| 	    case 'l':
 | |
| 		dat.flags |= CMF_DISPLINE;
 | |
| 		break;
 | |
| 	    case 'o':
 | |
| 		dat.flags |= CMF_MORDER;
 | |
| 		break;
 | |
| 	    case 'E':
 | |
|                 if (p[1]) {
 | |
|                     dat.dummies = atoi(p + 1);
 | |
| 		    p += strlen(p+1);
 | |
|                 } else if (argv[1]) {
 | |
|                     argv++;
 | |
|                     dat.dummies = atoi(*argv);
 | |
|                 } else {
 | |
|                     zwarnnam(name, "number expected after -%c", *p);
 | |
| 		    zsfree(mstr);
 | |
|                     return 1;
 | |
|                 }
 | |
|                 if (dat.dummies < 0) {
 | |
|                     zwarnnam(name, "invalid number: %d", dat.dummies);
 | |
| 		    zsfree(mstr);
 | |
|                     return 1;
 | |
|                 }
 | |
| 		break;
 | |
| 	    case '-':
 | |
| 		argv++;
 | |
| 		goto ca_args;
 | |
| 	    default:
 | |
| 		zwarnnam(name, "bad option: -%c", *p);
 | |
| 		zsfree(mstr);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	    if (sp) {
 | |
| 		if (p[1]) {
 | |
| 		    /* Pasted argument: -Xfoo. */
 | |
| 		    if (!*sp)
 | |
| 			*sp = p + 1;
 | |
| 		    p += strlen(p+1);
 | |
| 		} else if (argv[1]) {
 | |
| 		    /* Argument in a separate word: -X foo. */
 | |
| 		    argv++;
 | |
| 		    if (!*sp)
 | |
| 			*sp = *argv;
 | |
| 		} else {
 | |
| 		    /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
 | |
| 		    zwarnnam(name, e, *p);
 | |
| 		    zsfree(mstr);
 | |
| 		    return 1;
 | |
| 		}
 | |
| 		if (m) {
 | |
| 		    if (mstr) {
 | |
| 			char *tmp = tricat(mstr, " ", m);
 | |
| 			zsfree(mstr);
 | |
| 			mstr = tmp;
 | |
| 		    } else
 | |
| 			mstr = ztrdup(m);
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|  ca_args:
 | |
| 
 | |
|     if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
 | |
| 	zsfree(mstr);
 | |
| 	return 1;
 | |
|     }
 | |
|     zsfree(mstr);
 | |
| 
 | |
|     if (!*argv && !dat.group && !dat.mesg &&
 | |
| 	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL)))
 | |
| 	return 1;
 | |
| 
 | |
|     dat.match = match = cpcmatcher(match);
 | |
|     added = addmatches(&dat, argv);
 | |
|     freecmatcher(match);
 | |
| 
 | |
|     return added;
 | |
| }
 | |
| 
 | |
| #define CVT_RANGENUM 0
 | |
| #define CVT_RANGEPAT 1
 | |
| #define CVT_PRENUM   2
 | |
| #define CVT_PREPAT   3
 | |
| #define CVT_SUFNUM   4
 | |
| #define CVT_SUFPAT   5
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| ignore_prefix(int l)
 | |
| {
 | |
|     if (l) {
 | |
| 	char *tmp, sav;
 | |
| 	int pl = strlen(compprefix);
 | |
| 
 | |
| 	if (l > pl)
 | |
| 	    l = pl;
 | |
| 
 | |
| 	sav = compprefix[l];
 | |
| 
 | |
| 	compprefix[l] = '\0';
 | |
| 	tmp = tricat(compiprefix, compprefix, "");
 | |
| 	zsfree(compiprefix);
 | |
| 	compiprefix = tmp;
 | |
| 	compprefix[l] = sav;
 | |
| 	tmp = ztrdup(compprefix + l);
 | |
| 	zsfree(compprefix);
 | |
| 	compprefix = tmp;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| ignore_suffix(int l)
 | |
| {
 | |
|     if (l) {
 | |
| 	char *tmp, sav;
 | |
| 	int sl = strlen(compsuffix);
 | |
| 
 | |
| 	if ((l = sl - l) < 0)
 | |
| 	    l = 0;
 | |
| 
 | |
| 	tmp = tricat(compsuffix + l, compisuffix, "");
 | |
| 	zsfree(compisuffix);
 | |
| 	compisuffix = tmp;
 | |
| 	sav = compsuffix[l];
 | |
| 	compsuffix[l] = '\0';
 | |
| 	tmp = ztrdup(compsuffix);
 | |
| 	compsuffix[l] = sav;
 | |
| 	zsfree(compsuffix);
 | |
| 	compsuffix = tmp;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| restrict_range(int b, int e)
 | |
| {
 | |
|     int wl = arrlen(compwords) - 1;
 | |
| 
 | |
|     if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
 | |
| 	int i;
 | |
| 	char **p, **q, **pp;
 | |
| 
 | |
| 	if (e > wl)
 | |
| 	    e = wl;
 | |
| 
 | |
| 	i = e - b + 1;
 | |
| 	p = (char **) zshcalloc((i + 1) * sizeof(char *));
 | |
| 
 | |
| 	for (q = p, pp = compwords + b; i; i--, q++, pp++)
 | |
| 	    *q = ztrdup(*pp);
 | |
| 	freearray(compwords);
 | |
| 	compwords = p;
 | |
| 	compcurrent -= b;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 | |
| {
 | |
|     switch (test) {
 | |
|     case CVT_RANGENUM:
 | |
| 	{
 | |
| 	    int l = arrlen(compwords);
 | |
| 
 | |
| 	    if (na < 0)
 | |
| 		na += l;
 | |
| 	    else
 | |
| 		na--;
 | |
| 	    if (nb < 0)
 | |
| 		nb += l;
 | |
| 	    else
 | |
| 		nb--;
 | |
| 
 | |
| 	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
 | |
| 		return 0;
 | |
| 	    if (mod)
 | |
| 		restrict_range(na, nb);
 | |
| 	    return 1;
 | |
| 	}
 | |
|     case CVT_RANGEPAT:
 | |
| 	{
 | |
| 	    char **p;
 | |
| 	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
 | |
| 	    Patprog pp;
 | |
| 
 | |
| 	    i = compcurrent - 1;
 | |
| 	    if (i < 0 || i >= l)
 | |
| 		return 0;
 | |
| 
 | |
| 	    singsub(&sa);
 | |
| 	    pp = patcompile(sa, PAT_HEAPDUP, NULL);
 | |
| 
 | |
| 	    for (i--, p = compwords + i; i >= 0; p--, i--) {
 | |
| 		if (pattry(pp, *p)) {
 | |
| 		    b = i + 1;
 | |
| 		    t = 1;
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if (t && sb) {
 | |
| 		int tt = 0;
 | |
| 
 | |
| 		singsub(&sb);
 | |
| 		pp = patcompile(sb, PAT_STATIC, NULL);
 | |
| 
 | |
| 		for (i++, p = compwords + i; i < l; p++, i++) {
 | |
| 		    if (pattry(pp, *p)) {
 | |
| 			e = i - 1;
 | |
| 			tt = 1;
 | |
| 			break;
 | |
| 		    }
 | |
| 		}
 | |
| 		if (tt && i < compcurrent)
 | |
| 		    t = 0;
 | |
| 	    }
 | |
| 	    if (e < b)
 | |
| 		t = 0;
 | |
| 	    if (t && mod)
 | |
| 		restrict_range(b, e);
 | |
| 	    return t;
 | |
| 	}
 | |
|     case CVT_PRENUM:
 | |
|     case CVT_SUFNUM:
 | |
| 	if (na < 0)
 | |
| 	    return 0;
 | |
| 	if (na > 0 && mod) {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	    if (isset(MULTIBYTE)) {
 | |
| 		if (test == CVT_PRENUM) {
 | |
| 		    const char *ptr = compprefix;
 | |
| 		    int len = 1;
 | |
| 		    int sum = 0;
 | |
| 		    while (*ptr && na && len) {
 | |
| 			wint_t wc;
 | |
| 			len = mb_metacharlenconv(ptr, &wc);
 | |
| 			ptr += len;
 | |
| 			sum += len;
 | |
| 			na--;
 | |
| 		    }
 | |
| 		    if (na)
 | |
| 			return 0;
 | |
| 		    na = sum;
 | |
| 		} else {
 | |
| 		    char *end = compsuffix + strlen(compsuffix);
 | |
| 		    char *ptr = end;
 | |
| 		    while (na-- && ptr > compsuffix)
 | |
| 			 ptr = backwardmetafiedchar(compsuffix, ptr, NULL);
 | |
| 		    if (na >= 0)
 | |
| 			return 0;
 | |
| 		    na = end - ptr;
 | |
| 		}
 | |
| 	    } else
 | |
| #endif
 | |
| 	    if ((int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na)
 | |
| 		return 0;
 | |
| 	    if (test == CVT_PRENUM)
 | |
| 		ignore_prefix(na);
 | |
| 	    else
 | |
| 		ignore_suffix(na);
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	return 1;
 | |
|     case CVT_PREPAT:
 | |
|     case CVT_SUFPAT:
 | |
| 	{
 | |
| 	    Patprog pp;
 | |
| 
 | |
| 	    if (!na)
 | |
| 		return 0;
 | |
| 
 | |
| 	    if (!(pp = patcompile(sa, PAT_HEAPDUP, 0)))
 | |
| 		return 0;
 | |
| 
 | |
| 	    if (test == CVT_PREPAT) {
 | |
| 		int l, add;
 | |
| 		char *p, sav;
 | |
| 
 | |
| 		if (!(l = strlen(compprefix)))
 | |
| 		    return ((na == 1 || na == -1) && pattry(pp, compprefix));
 | |
| 		if (na < 0) {
 | |
| 		    p = compprefix + l;
 | |
| 		    na = -na;
 | |
| 		    add = -1;
 | |
| 		} else {
 | |
| 		    p = compprefix + 1 + (*compprefix == Meta);
 | |
| 		    if (p > compprefix + l)
 | |
| 			p = compprefix + l;
 | |
| 		    add = 1;
 | |
| 		}
 | |
| 		for (;;) {
 | |
| 		    sav = *p;
 | |
| 		    *p = '\0';
 | |
| 		    test = pattry(pp, compprefix);
 | |
| 		    *p = sav;
 | |
| 		    if (test && !--na)
 | |
| 			break;
 | |
| 		    if (add > 0) {
 | |
| 			if (p == compprefix + l)
 | |
| 			    return 0;
 | |
| 			p = p + 1 + (*p == Meta);
 | |
| 			if (p > compprefix + l)
 | |
| 			    p = compprefix + l;
 | |
| 		    } else {
 | |
| 			if (p == compprefix)
 | |
| 			    return 0;
 | |
| 			p--;
 | |
| 			if (p > compprefix && p[-1] == Meta)
 | |
| 			    p--;
 | |
| 		    }
 | |
| 		}
 | |
| 		if (mod)
 | |
| 		    ignore_prefix(p - compprefix);
 | |
| 	    } else {
 | |
| 		int l, ol, add;
 | |
| 		char *p;
 | |
| 
 | |
| 		if (!(ol = l = strlen(compsuffix)))
 | |
| 		    return ((na == 1 || na == -1) && pattry(pp, compsuffix));
 | |
| 		if (na < 0) {
 | |
| 		    p = compsuffix;
 | |
| 		    na = -na;
 | |
| 		    add = 1;
 | |
| 		} else {
 | |
| 		    p = compsuffix + l - 1;
 | |
| 		    if (p > compsuffix && p[-1] == Meta)
 | |
| 			p--;
 | |
| 		    add = -1;
 | |
| 		}
 | |
| 		for (;;) {
 | |
| 		    if (pattry(pp, p) && !--na)
 | |
| 			break;
 | |
| 
 | |
| 		    if (add > 0) {
 | |
| 			if (p == compsuffix + l)
 | |
| 			    return 0;
 | |
| 			if (*p == Meta)
 | |
| 			    p += 2;
 | |
| 			else
 | |
| 			    p++;
 | |
| 		    } else {
 | |
| 			if (p == compsuffix)
 | |
| 			    return 0;
 | |
| 			p--;
 | |
| 			if (p > compsuffix && p[-1] == Meta)
 | |
| 			    p--;
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
| 		if (mod)
 | |
| 		    ignore_suffix(ol - (p - compsuffix));
 | |
| 	    }
 | |
| 	    return 1;
 | |
| 	}
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_compset(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 | |
| {
 | |
|     int test = 0, na = 0, nb = 0;
 | |
|     char *sa = NULL, *sb = NULL;
 | |
| 
 | |
|     if (incompfunc != 1) {
 | |
| 	zwarnnam(name, "can only be called from completion function");
 | |
| 	return 1;
 | |
|     }
 | |
|     if (argv[0][0] != '-') {
 | |
| 	zwarnnam(name, "missing option");
 | |
| 	return 1;
 | |
|     }
 | |
|     switch (argv[0][1]) {
 | |
|     case 'n': test = CVT_RANGENUM; break;
 | |
|     case 'N': test = CVT_RANGEPAT; break;
 | |
|     case 'p': test = CVT_PRENUM; break;
 | |
|     case 'P': test = CVT_PREPAT; break;
 | |
|     case 's': test = CVT_SUFNUM; break;
 | |
|     case 'S': test = CVT_SUFPAT; break;
 | |
|     case 'q': return set_comp_sep();
 | |
|     default:
 | |
| 	zwarnnam(name, "bad option -%c", argv[0][1]);
 | |
| 	return 1;
 | |
|     }
 | |
|     if (argv[0][2]) {
 | |
| 	sa = argv[0] + 2;
 | |
| 	sb = argv[1];
 | |
| 	na = 2;
 | |
|     } else {
 | |
| 	if (!(sa = argv[1])) {
 | |
| 	    zwarnnam(name, "missing string for option -%c", argv[0][1]);
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	sb = argv[2];
 | |
| 	na = 3;
 | |
|     }
 | |
|     if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
 | |
| 	 (sb && argv[na]))) {
 | |
| 	zwarnnam(name, "too many arguments");
 | |
| 	return 1;
 | |
|     }
 | |
|     switch (test) {
 | |
|     case CVT_RANGENUM:
 | |
| 	na = atoi(sa);
 | |
| 	nb = (sb ? atoi(sb) : -1);
 | |
| 	break;
 | |
|     case CVT_RANGEPAT:
 | |
| 	tokenize(sa);
 | |
| 	remnulargs(sa);
 | |
| 	if (sb) {
 | |
| 	    tokenize(sb);
 | |
| 	    remnulargs(sb);
 | |
| 	}
 | |
| 	break;
 | |
|     case CVT_PRENUM:
 | |
|     case CVT_SUFNUM:
 | |
| 	na = atoi(sa);
 | |
| 	break;
 | |
|     case CVT_PREPAT:
 | |
|     case CVT_SUFPAT:
 | |
| 	if (sb) {
 | |
| 	    na = atoi(sa);
 | |
| 	    sa = sb;
 | |
| 	} else
 | |
| 	    na = -1;
 | |
| 	tokenize(sa);
 | |
| 	remnulargs(sa);
 | |
| 	break;
 | |
|     }
 | |
|     return !do_comp_vars(test, na, sa, nb, sb, 1);
 | |
| }
 | |
| 
 | |
| /* Definitions for the special parameters. Note that these have to match the
 | |
|  * order of the CP_* bits in comp.h */
 | |
| 
 | |
| #define VAL(X) ((void *) (&(X)))
 | |
| #define GSU(X) ((GsuScalar)(void *) (&(X)))
 | |
| struct compparam {
 | |
|     char *name;
 | |
|     int type;
 | |
|     void *var;
 | |
|     GsuScalar gsu;
 | |
| };
 | |
| 
 | |
| static const struct gsu_scalar compvarscalar_gsu =
 | |
| { strvargetfn, strvarsetfn, compunsetfn };
 | |
| static const struct gsu_scalar complist_gsu =
 | |
| { get_complist, set_complist, compunsetfn };
 | |
| static const struct gsu_scalar unambig_gsu =
 | |
| { get_unambig, nullstrsetfn, compunsetfn };
 | |
| static const struct gsu_scalar unambig_pos_gsu =
 | |
| { get_unambig_pos, nullstrsetfn, compunsetfn };
 | |
| static const struct gsu_scalar insert_pos_gsu =
 | |
| { get_insert_pos, nullstrsetfn, compunsetfn };
 | |
| static const struct gsu_scalar compqstack_gsu =
 | |
| { get_compqstack, nullstrsetfn, compunsetfn };
 | |
| 
 | |
| static const struct gsu_integer compvarinteger_gsu =
 | |
| { intvargetfn, intvarsetfn, compunsetfn };
 | |
| static const struct gsu_integer nmatches_gsu =
 | |
| { get_nmatches, NULL, compunsetfn };
 | |
| static const struct gsu_integer unambig_curs_gsu =
 | |
| { get_unambig_curs, NULL, compunsetfn };
 | |
| static const struct gsu_integer listlines_gsu =
 | |
| { get_listlines, NULL, compunsetfn };
 | |
| 
 | |
| static const struct gsu_array compvararray_gsu =
 | |
| { arrvargetfn, arrvarsetfn, compunsetfn };
 | |
| 
 | |
| 
 | |
| static struct compparam comprparams[] = {
 | |
|     { "words", PM_ARRAY, VAL(compwords), NULL },
 | |
|     { "redirections", PM_ARRAY, VAL(compredirs), NULL },
 | |
|     { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL },
 | |
|     { "PREFIX", PM_SCALAR, VAL(compprefix), NULL },
 | |
|     { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL },
 | |
|     { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL },
 | |
|     { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL },
 | |
|     { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL },
 | |
|     { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL },
 | |
|     { NULL, 0, NULL, NULL }
 | |
| };
 | |
| 
 | |
| static struct compparam compkparams[] = {
 | |
|     { "nmatches", PM_INTEGER | PM_READONLY, NULL, GSU(nmatches_gsu) },
 | |
|     { "context", PM_SCALAR, VAL(compcontext), NULL },
 | |
|     { "parameter", PM_SCALAR, VAL(compparameter), NULL },
 | |
|     { "redirect", PM_SCALAR, VAL(compredirect), NULL },
 | |
|     { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL },
 | |
|     { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL },
 | |
|     { "restore", PM_SCALAR, VAL(comprestore), NULL },
 | |
|     { "list", PM_SCALAR, NULL, GSU(complist_gsu) },
 | |
|     { "insert", PM_SCALAR, VAL(compinsert), NULL },
 | |
|     { "exact", PM_SCALAR, VAL(compexact), NULL },
 | |
|     { "exact_string", PM_SCALAR, VAL(compexactstr), NULL },
 | |
|     { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL },
 | |
|     { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL },
 | |
|     { "unambiguous", PM_SCALAR | PM_READONLY, NULL, GSU(unambig_gsu) },
 | |
|     { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL,
 | |
|       GSU(unambig_curs_gsu) },
 | |
|     { "unambiguous_positions", PM_SCALAR | PM_READONLY, NULL,
 | |
|       GSU(unambig_pos_gsu) },
 | |
|     { "insert_positions", PM_SCALAR | PM_READONLY, NULL,
 | |
|       GSU(insert_pos_gsu) },
 | |
|     { "list_max", PM_INTEGER, VAL(complistmax), NULL },
 | |
|     { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL },
 | |
|     { "to_end", PM_SCALAR, VAL(comptoend), NULL },
 | |
|     { "old_list", PM_SCALAR, VAL(compoldlist), NULL },
 | |
|     { "old_insert", PM_SCALAR, VAL(compoldins), NULL },
 | |
|     { "vared", PM_SCALAR, VAL(compvared), NULL },
 | |
|     { "list_lines", PM_INTEGER | PM_READONLY, NULL, GSU(listlines_gsu) },
 | |
|     { "all_quotes", PM_SCALAR | PM_READONLY, NULL, GSU(compqstack_gsu) },
 | |
|     { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL },
 | |
|     { NULL, 0, NULL, NULL }
 | |
| };
 | |
| 
 | |
| #define COMPSTATENAME "compstate"
 | |
| 
 | |
| static void
 | |
| addcompparams(struct compparam *cp, Param *pp)
 | |
| {
 | |
|     for (; cp->name; cp++, pp++) {
 | |
| 	Param pm = createparam(cp->name,
 | |
| 			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
 | |
| 	if (!pm)
 | |
| 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 | |
| 	DPUTS(!pm, "param not set in addcompparams");
 | |
| 
 | |
| 	*pp = pm;
 | |
| 	pm->level = locallevel + 1;
 | |
| 	if ((pm->u.data = cp->var)) {
 | |
| 	    switch(PM_TYPE(cp->type)) {
 | |
| 	    case PM_SCALAR:
 | |
| 		pm->gsu.s = &compvarscalar_gsu;
 | |
| 		break;
 | |
| 	    case PM_INTEGER:
 | |
| 		pm->gsu.i = &compvarinteger_gsu;
 | |
| 		pm->base = 10;
 | |
| 		break;
 | |
| 	    case PM_ARRAY:
 | |
| 		pm->gsu.a = &compvararray_gsu;
 | |
| 		break;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    pm->gsu.s = cp->gsu;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const struct gsu_hash compstate_gsu =
 | |
| { get_compstate, set_compstate, compunsetfn };
 | |
| 
 | |
| /**/
 | |
| void
 | |
| makecompparams(void)
 | |
| {
 | |
|     Param cpm;
 | |
|     HashTable tht;
 | |
| 
 | |
|     addcompparams(comprparams, comprpms);
 | |
| 
 | |
|     if (!(cpm = createparam(
 | |
| 	      COMPSTATENAME,
 | |
| 	      PM_SPECIAL|PM_REMOVABLE|PM_SINGLE|PM_LOCAL|PM_HASHED)))
 | |
| 	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
 | |
|     DPUTS(!cpm, "param not set in makecompparams");
 | |
| 
 | |
|     comprpms[CPN_COMPSTATE] = cpm;
 | |
|     tht = paramtab;
 | |
|     cpm->level = locallevel + 1;
 | |
|     cpm->gsu.h = &compstate_gsu;
 | |
|     cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
 | |
|     addcompparams(compkparams, compkpms);
 | |
|     paramtab = tht;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static HashTable
 | |
| get_compstate(Param pm)
 | |
| {
 | |
|     return pm->u.hash;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| set_compstate(UNUSED(Param pm), HashTable ht)
 | |
| {
 | |
|     struct compparam *cp;
 | |
|     Param *pp;
 | |
|     HashNode hn;
 | |
|     int i;
 | |
|     struct value v;
 | |
|     char *str;
 | |
| 
 | |
|     if (!ht)
 | |
|         return;
 | |
| 
 | |
|     for (i = 0; i < ht->hsize; i++)
 | |
| 	for (hn = ht->nodes[i]; hn; hn = hn->next)
 | |
| 	    for (cp = compkparams,
 | |
| 		 pp = compkpms; cp->name; cp++, pp++)
 | |
| 		if (!strcmp(hn->nam, cp->name)) {
 | |
| 		    v.isarr = v.flags = v.start = 0;
 | |
| 		    v.end = -1;
 | |
| 		    v.arr = NULL;
 | |
| 		    v.pm = (Param) hn;
 | |
| 		    if (cp->type == PM_INTEGER)
 | |
| 			*((zlong *) cp->var) = getintvalue(&v);
 | |
| 		    else if ((str = getstrvalue(&v))) {
 | |
| 			zsfree(*((char **) cp->var));
 | |
| 			*((char **) cp->var) = ztrdup(str);
 | |
| 		    }
 | |
| 		    (*pp)->node.flags &= ~PM_UNSET;
 | |
| 
 | |
| 		    break;
 | |
| 		}
 | |
|     if (ht != pm->u.hash)
 | |
| 	deleteparamtable(ht);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static zlong
 | |
| get_nmatches(UNUSED(Param pm))
 | |
| {
 | |
|     return (permmatches(0) ? 0 : nmatches);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static zlong
 | |
| get_listlines(UNUSED(Param pm))
 | |
| {
 | |
|     return list_lines();
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| set_complist(UNUSED(Param pm), char *v)
 | |
| {
 | |
|     comp_list(v);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| get_complist(UNUSED(Param pm))
 | |
| {
 | |
|     return complist;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| get_unambig(UNUSED(Param pm))
 | |
| {
 | |
|     return unambig_data(NULL, NULL, NULL);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static zlong
 | |
| get_unambig_curs(UNUSED(Param pm))
 | |
| {
 | |
|     int c;
 | |
| 
 | |
|     unambig_data(&c, NULL, NULL);
 | |
| 
 | |
|     return c;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| get_unambig_pos(UNUSED(Param pm))
 | |
| {
 | |
|     char *p;
 | |
| 
 | |
|     unambig_data(NULL, &p, NULL);
 | |
| 
 | |
|     return p;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| get_insert_pos(UNUSED(Param pm))
 | |
| {
 | |
|     char *p;
 | |
| 
 | |
|     unambig_data(NULL, NULL, &p);
 | |
| 
 | |
|     return p;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static char *
 | |
| get_compqstack(UNUSED(Param pm))
 | |
| {
 | |
|     char *p, *ptr, *cqp;
 | |
| 
 | |
|     if (!compqstack)		/* TODO: don't think this can happen... */
 | |
| 	return "";
 | |
| 
 | |
|     ptr = p = zhalloc(2*strlen(compqstack)+1);
 | |
| 
 | |
|     for (cqp = compqstack; *cqp; cqp++) {
 | |
| 	char *str = comp_quoting_string(*cqp);
 | |
| 	*ptr++ = *str;
 | |
|     }
 | |
|     *ptr = '\0';
 | |
| 
 | |
|     return p;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| compunsetfn(Param pm, int exp)
 | |
| {
 | |
|     if (exp) {
 | |
| 	if (pm->u.data) {
 | |
| 	    if (PM_TYPE(pm->node.flags) == PM_SCALAR) {
 | |
| 		zsfree(*((char **) pm->u.data));
 | |
| 		*((char **) pm->u.data) = ztrdup("");
 | |
| 	    } else if (PM_TYPE(pm->node.flags) == PM_ARRAY) {
 | |
| 		freearray(*((char ***) pm->u.data));
 | |
| 		*((char ***) pm->u.data) = zshcalloc(sizeof(char *));
 | |
| 	    } else if (PM_TYPE(pm->node.flags) == PM_HASHED) {
 | |
| 		deleteparamtable(pm->u.hash);
 | |
| 		pm->u.hash = NULL;
 | |
| 	    }
 | |
| 	}
 | |
|     } else if (PM_TYPE(pm->node.flags) == PM_HASHED) {
 | |
| 	Param *p;
 | |
| 	int i;
 | |
| 
 | |
| 	deletehashtable(pm->u.hash);
 | |
| 	pm->u.hash = NULL;
 | |
| 
 | |
| 	for (p = compkpms, i = CP_KEYPARAMS; i--; p++)
 | |
| 	    *p = NULL;
 | |
|     }
 | |
|     if (!exp) {
 | |
| 	Param *p;
 | |
| 	int i;
 | |
| 
 | |
| 	for (p = comprpms, i = CP_REALPARAMS; i; p++, i--)
 | |
| 	    if (*p == pm) {
 | |
| 		*p = NULL;
 | |
| 		break;
 | |
| 	    }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| comp_setunset(int rset, int runset, int kset, int kunset)
 | |
| {
 | |
|     Param *p;
 | |
| 
 | |
|     if (comprpms && (rset >= 0 || runset >= 0)) {
 | |
| 	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
 | |
| 	    if (*p) {
 | |
| 		if (rset & 1)
 | |
| 		    (*p)->node.flags &= ~PM_UNSET;
 | |
| 		if (runset & 1)
 | |
| 		    (*p)->node.flags |= PM_UNSET;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     if (compkpms && (kset >= 0 || kunset >= 0)) {
 | |
| 	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
 | |
| 	    if (*p) {
 | |
| 		if (kset & 1)
 | |
| 		    (*p)->node.flags &= ~PM_UNSET;
 | |
| 		if (kunset & 1)
 | |
| 		    (*p)->node.flags |= PM_UNSET;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| comp_wrapper(Eprog prog, FuncWrap w, char *name)
 | |
| {
 | |
|     if (incompfunc != 1)
 | |
| 	return 1;
 | |
|     else {
 | |
| 	char *orest, *opre, *osuf, *oipre, *oisuf, **owords, **oredirs;
 | |
| 	char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq;
 | |
| 	zlong ocur;
 | |
| 	unsigned int runset = 0, kunset = 0, m, sm;
 | |
| 	Param *pp;
 | |
| 
 | |
| 	m = CP_WORDS | CP_REDIRS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
 | |
| 	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
 | |
| 	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
 | |
| 	    if ((m & 1) && ((*pp)->node.flags & PM_UNSET))
 | |
| 		runset |= sm;
 | |
| 	}
 | |
| 	if (compkpms[CPN_RESTORE]->node.flags & PM_UNSET)
 | |
| 	    kunset = CP_RESTORE;
 | |
| 	orest = comprestore;
 | |
| 	comprestore = ztrdup("auto");
 | |
| 	ocur = compcurrent;
 | |
| 	opre = ztrdup(compprefix);
 | |
| 	osuf = ztrdup(compsuffix);
 | |
| 	oipre = ztrdup(compiprefix);
 | |
| 	oisuf = ztrdup(compisuffix);
 | |
| 	oqipre = ztrdup(compqiprefix);
 | |
| 	oqisuf = ztrdup(compqisuffix);
 | |
| 	oq = ztrdup(compquote);
 | |
| 	oqi = ztrdup(compquoting);
 | |
| 	oqs = ztrdup(compqstack);
 | |
| 	oaq = ztrdup(autoq);
 | |
| 	owords = zarrdup(compwords);
 | |
| 	oredirs = zarrdup(compredirs);
 | |
| 
 | |
| 	runshfunc(prog, w, name);
 | |
| 
 | |
| 	if (comprestore && !strcmp(comprestore, "auto")) {
 | |
| 	    compcurrent = ocur;
 | |
| 	    zsfree(compprefix);
 | |
| 	    compprefix = opre;
 | |
| 	    zsfree(compsuffix);
 | |
| 	    compsuffix = osuf;
 | |
| 	    zsfree(compiprefix);
 | |
| 	    compiprefix = oipre;
 | |
| 	    zsfree(compisuffix);
 | |
| 	    compisuffix = oisuf;
 | |
| 	    zsfree(compqiprefix);
 | |
| 	    compqiprefix = oqipre;
 | |
| 	    zsfree(compqisuffix);
 | |
| 	    compqisuffix = oqisuf;
 | |
| 	    zsfree(compquote);
 | |
| 	    compquote = oq;
 | |
| 	    zsfree(compquoting);
 | |
| 	    compquoting = oqi;
 | |
| 	    zsfree(compqstack);
 | |
| 	    compqstack = oqs;
 | |
| 	    zsfree(autoq);
 | |
| 	    autoq = oaq;
 | |
| 	    freearray(compwords);
 | |
| 	    freearray(compredirs);
 | |
| 	    compwords = owords;
 | |
|             compredirs = oredirs;
 | |
| 	    comp_setunset(CP_COMPSTATE |
 | |
| 			  (~runset & (CP_WORDS | CP_REDIRS |
 | |
|                                       CP_CURRENT | CP_PREFIX |
 | |
|                                       CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
 | |
|                                       CP_QIPREFIX | CP_QISUFFIX)),
 | |
| 			  (runset & CP_ALLREALS),
 | |
| 			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
 | |
| 	} else {
 | |
| 	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
 | |
| 			  (kunset & CP_RESTORE));
 | |
| 	    zsfree(opre);
 | |
| 	    zsfree(osuf);
 | |
| 	    zsfree(oipre);
 | |
| 	    zsfree(oisuf);
 | |
| 	    zsfree(oqipre);
 | |
| 	    zsfree(oqisuf);
 | |
| 	    zsfree(oq);
 | |
| 	    zsfree(oqi);
 | |
| 	    zsfree(oqs);
 | |
| 	    zsfree(oaq);
 | |
| 	    freearray(owords);
 | |
| 	    freearray(oredirs);
 | |
| 	}
 | |
| 	zsfree(comprestore);
 | |
| 	comprestore = orest;
 | |
| 
 | |
| 	return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| comp_check(void)
 | |
| {
 | |
|     if (incompfunc != 1) {
 | |
| 	zerr("condition can only be used in completion function");
 | |
| 	return 0;
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| cond_psfix(char **a, int id)
 | |
| {
 | |
|     if (comp_check()) {
 | |
| 	if (a[1])
 | |
| 	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
 | |
| 				0, NULL, 0);
 | |
| 	else
 | |
| 	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| cond_range(char **a, int id)
 | |
| {
 | |
|     return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
 | |
| 			(id ? cond_str(a, 1, 1) : NULL), 0);
 | |
| }
 | |
| 
 | |
| static struct builtin bintab[] = {
 | |
|     BUILTIN("compadd", BINF_HANDLES_OPTS, bin_compadd, 0, -1, 0, NULL, NULL),
 | |
|     BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
 | |
| };
 | |
| 
 | |
| static struct conddef cotab[] = {
 | |
|     CONDDEF("after", 0, cond_range, 1, 1, 0),
 | |
|     CONDDEF("between", 0, cond_range, 2, 2, 1),
 | |
|     CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
 | |
|     CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
 | |
| };
 | |
| 
 | |
| static struct funcwrap wrapper[] = {
 | |
|     WRAPDEF(comp_wrapper),
 | |
| };
 | |
| 
 | |
| /* The order of the entries in this table has to match the *HOOK
 | |
|  * macros in comp.h */
 | |
| 
 | |
| /**/
 | |
| struct hookdef comphooks[] = {
 | |
|     HOOKDEF("insert_match", NULL, HOOKF_ALL),
 | |
|     HOOKDEF("menu_start", NULL, HOOKF_ALL),
 | |
|     HOOKDEF("compctl_make", NULL, 0),
 | |
|     HOOKDEF("compctl_cleanup", NULL, 0),
 | |
|     HOOKDEF("comp_list_matches", ilistmatches, 0),
 | |
| };
 | |
| 
 | |
| static struct features module_features = {
 | |
|     bintab, sizeof(bintab)/sizeof(*bintab),
 | |
|     cotab, sizeof(cotab)/sizeof(*cotab),
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     0
 | |
| };
 | |
| 
 | |
| /**/
 | |
| int
 | |
| setup_(UNUSED(Module m))
 | |
| {
 | |
|     hasperm = 0;
 | |
| 
 | |
|     comprpms = compkpms = NULL;
 | |
|     compwords = compredirs = NULL;
 | |
|     compprefix = compsuffix = compiprefix = compisuffix = 
 | |
| 	compqiprefix = compqisuffix =
 | |
| 	compcontext = compparameter = compredirect = compquote =
 | |
| 	compquoting = comprestore = complist = compinsert =
 | |
| 	compexact = compexactstr = comppatmatch = comppatinsert =
 | |
| 	complastprompt = comptoend = compoldlist = compoldins =
 | |
| 	compvared = compqstack = NULL;
 | |
|     complastprefix = ztrdup("");
 | |
|     complastsuffix = ztrdup("");
 | |
|     complistmax = 0;
 | |
|     hascompmod = 1;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| features_(Module m, char ***features)
 | |
| {
 | |
|     *features = featuresarray(m, &module_features);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| enables_(Module m, int **enables)
 | |
| {
 | |
|     return handlefeatures(m, &module_features, enables);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| boot_(Module m)
 | |
| {
 | |
|     addhookfunc("complete", (Hookfn) do_completion);
 | |
|     addhookfunc("before_complete", (Hookfn) before_complete);
 | |
|     addhookfunc("after_complete", (Hookfn) after_complete);
 | |
|     addhookfunc("accept_completion", (Hookfn) accept_last);
 | |
|     addhookfunc("list_matches", (Hookfn) list_matches);
 | |
|     addhookfunc("invalidate_list", (Hookfn) invalidate_list);
 | |
|     (void)addhookdefs(m, comphooks, sizeof(comphooks)/sizeof(*comphooks));
 | |
|     return addwrapper(m, wrapper);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| cleanup_(Module m)
 | |
| {
 | |
|     deletehookfunc("complete", (Hookfn) do_completion);
 | |
|     deletehookfunc("before_complete", (Hookfn) before_complete);
 | |
|     deletehookfunc("after_complete", (Hookfn) after_complete);
 | |
|     deletehookfunc("accept_completion", (Hookfn) accept_last);
 | |
|     deletehookfunc("list_matches", (Hookfn) list_matches);
 | |
|     deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
 | |
|     (void)deletehookdefs(m, comphooks,
 | |
| 			 sizeof(comphooks)/sizeof(*comphooks));
 | |
|     deletewrapper(m, wrapper);
 | |
|     return setfeatureenables(m, &module_features, NULL);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| finish_(UNUSED(Module m))
 | |
| {
 | |
|     if (compwords)
 | |
| 	freearray(compwords);
 | |
|     if (compredirs)
 | |
| 	freearray(compredirs);
 | |
|     zsfree(compprefix);
 | |
|     zsfree(compsuffix);
 | |
|     zsfree(complastprefix);
 | |
|     zsfree(complastsuffix);
 | |
|     zsfree(compiprefix);
 | |
|     zsfree(compisuffix);
 | |
|     zsfree(compqiprefix);
 | |
|     zsfree(compqisuffix);
 | |
|     zsfree(compcontext);
 | |
|     zsfree(compparameter);
 | |
|     zsfree(compredirect);
 | |
|     zsfree(compquote);
 | |
|     zsfree(compqstack);
 | |
|     zsfree(compquoting);
 | |
|     zsfree(comprestore);
 | |
|     zsfree(complist);
 | |
|     zsfree(compinsert);
 | |
|     zsfree(compexact);
 | |
|     zsfree(compexactstr);
 | |
|     zsfree(comppatmatch);
 | |
|     zsfree(comppatinsert);
 | |
|     zsfree(complastprompt);
 | |
|     zsfree(comptoend);
 | |
|     zsfree(compoldlist);
 | |
|     zsfree(compoldins);
 | |
|     zsfree(compvared);
 | |
| 
 | |
|     hascompmod = 0;
 | |
| 
 | |
|     return 0;
 | |
| }
 |