1/*
2 * complete.c - the complete module, interface part
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1999 Sven Wischnowsky
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Sven Wischnowsky and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "complete.mdh"
31#include "complete.pro"
32
33/* global variables for shell parameters in new style completion */
34
35/**/
36mod_export
37zlong compcurrent,
38      complistmax;
39/**/
40zlong complistlines,
41      compignored;
42
43/**/
44mod_export
45char **compwords,
46     **compredirs,
47     *compprefix,
48     *compsuffix,
49     *complastprefix,
50     *complastsuffix,
51     *compisuffix,
52     *compqiprefix,
53     *compqisuffix,
54     *compquote,
55     *compqstack,
56     *comppatmatch,
57     *complastprompt;
58/**/
59char *compiprefix,
60     *compcontext,
61     *compparameter,
62     *compredirect,
63     *compquoting,
64     *comprestore,
65     *complist,
66     *compinsert,
67     *compexact,
68     *compexactstr,
69     *comppatinsert,
70     *comptoend,
71     *compoldlist,
72     *compoldins,
73     *compvared;
74
75/**/
76Param *comprpms, *compkpms;
77
78/**/
79mod_export void
80freecmlist(Cmlist l)
81{
82    Cmlist n;
83
84    while (l) {
85	n = l->next;
86	freecmatcher(l->matcher);
87	zsfree(l->str);
88
89	zfree(l, sizeof(struct cmlist));
90
91	l = n;
92    }
93}
94
95/**/
96mod_export void
97freecmatcher(Cmatcher m)
98{
99    Cmatcher n;
100
101    if (!m || --(m->refc))
102	return;
103
104    while (m) {
105	n = m->next;
106	freecpattern(m->line);
107	freecpattern(m->word);
108	freecpattern(m->left);
109	freecpattern(m->right);
110
111	zfree(m, sizeof(struct cmatcher));
112
113	m = n;
114    }
115}
116
117/**/
118void
119freecpattern(Cpattern p)
120{
121    Cpattern n;
122
123    while (p) {
124	n = p->next;
125	if (p->tp <= CPAT_EQUIV)
126	    free(p->u.str);
127	zfree(p, sizeof(struct cpattern));
128
129	p = n;
130    }
131}
132
133/* Copy a completion matcher list into permanent storage. */
134
135/**/
136mod_export Cmatcher
137cpcmatcher(Cmatcher m)
138{
139    Cmatcher r = NULL, *p = &r, n;
140
141    while (m) {
142	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
143
144	n->refc = 1;
145	n->next = NULL;
146	n->flags = m->flags;
147	n->line = cpcpattern(m->line);
148	n->llen = m->llen;
149	n->word = cpcpattern(m->word);
150	n->wlen = m->wlen;
151	n->left = cpcpattern(m->left);
152	n->lalen = m->lalen;
153	n->right = cpcpattern(m->right);
154	n->ralen = m->ralen;
155
156	p = &(n->next);
157	m = m->next;
158    }
159    return r;
160}
161
162/*
163 * Copy a single entry in a matcher pattern.
164 * If useheap is 1, it comes from the heap.
165 */
166
167/**/
168mod_export Cpattern
169cp_cpattern_element(Cpattern o)
170{
171    Cpattern n = zalloc(sizeof(struct cpattern));
172
173    n->next = NULL;
174
175    n->tp = o->tp;
176    switch (o->tp)
177    {
178    case CPAT_CCLASS:
179    case CPAT_NCLASS:
180    case CPAT_EQUIV:
181	n->u.str = ztrdup(o->u.str);
182	break;
183
184    case CPAT_CHAR:
185	n->u.chr = o->u.chr;
186	break;
187
188    default:
189	/* just to keep compiler quiet */
190	break;
191    }
192
193    return n;
194}
195
196/* Copy a completion matcher pattern. */
197
198/**/
199static Cpattern
200cpcpattern(Cpattern o)
201{
202    Cpattern r = NULL, *p = &r;
203
204    while (o) {
205	*p = cp_cpattern_element(o);
206	p = &((*p)->next);
207	o = o->next;
208    }
209    return r;
210}
211
212/* Parse a string for matcher control, containing multiple matchers. */
213
214/**/
215mod_export Cmatcher
216parse_cmatcher(char *name, char *s)
217{
218    Cmatcher ret = NULL, r = NULL, n;
219    Cpattern line, word, left, right;
220    int fl, fl2, ll, wl, lal, ral, err, both;
221
222    if (!*s)
223	return NULL;
224
225    while (*s) {
226	lal = ral = both = fl2 = 0;
227	left = right = NULL;
228
229	while (*s && inblank(*s)) s++;
230
231	if (!*s) break;
232
233	switch (*s) {
234	case 'b': fl2 = CMF_INTER;
235	case 'l': fl = CMF_LEFT; break;
236	case 'e': fl2 = CMF_INTER;
237	case 'r': fl = CMF_RIGHT; break;
238	case 'm': fl = 0; break;
239	case 'B': fl2 = CMF_INTER;
240	case 'L': fl = CMF_LEFT | CMF_LINE; break;
241	case 'E': fl2 = CMF_INTER;
242	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
243	case 'M': fl = CMF_LINE; break;
244	default:
245	    if (name)
246		zwarnnam(name, "unknown match specification character `%c'",
247			 *s);
248	    return pcm_err;
249	}
250	if (s[1] != ':') {
251	    if (name)
252		zwarnnam(name, "missing `:'");
253	    return pcm_err;
254	}
255	s += 2;
256	if (!*s) {
257	    if (name)
258		zwarnnam(name, "missing patterns");
259	    return pcm_err;
260	}
261	if ((fl & CMF_LEFT) && !fl2) {
262	    left = parse_pattern(name, &s, &lal, '|', &err);
263	    if (err)
264		return pcm_err;
265
266	    if ((both = (*s && s[1] == '|')))
267		s++;
268
269	    if (!*s || !*++s) {
270		if (name)
271		    zwarnnam(name, "missing line pattern");
272		return pcm_err;
273	    }
274	} else
275	    left = NULL;
276
277	line = parse_pattern(name, &s, &ll,
278			     (((fl & CMF_RIGHT) && !fl2) ? '|' : '='),
279			     &err);
280	if (err)
281	    return pcm_err;
282	if (both) {
283	    right = line;
284	    ral = ll;
285	    line = NULL;
286	    ll = 0;
287	}
288	if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) {
289	    if (name)
290		zwarnnam(name, "missing right anchor");
291	} else if (!(fl & CMF_RIGHT) || fl2) {
292	    if (!*s) {
293		if (name)
294		    zwarnnam(name, "missing word pattern");
295		return pcm_err;
296	    }
297	    s++;
298	}
299	if ((fl & CMF_RIGHT) && !fl2) {
300	    if (*s == '|') {
301		left = line;
302		lal = ll;
303		line = NULL;
304		ll = 0;
305		s++;
306	    }
307	    right = parse_pattern(name, &s, &ral, '=', &err);
308	    if (err)
309		return pcm_err;
310	    if (!*s) {
311		if (name)
312		    zwarnnam(name, "missing word pattern");
313		return pcm_err;
314	    }
315	    s++;
316	} else
317	    right = NULL;
318
319	if (*s == '*') {
320	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
321		if (name)
322		    zwarnnam(name, "need anchor for `*'");
323		return pcm_err;
324	    }
325	    word = NULL;
326	    if (*++s == '*') {
327		s++;
328		wl = -2;
329	    } else
330		wl = -1;
331	} else {
332	    word = parse_pattern(name, &s, &wl, 0, &err);
333
334	    if (!word && !line) {
335		if (name)
336		    zwarnnam(name, "need non-empty word or line pattern");
337		return pcm_err;
338	    }
339	}
340	if (err)
341	    return pcm_err;
342
343	n = (Cmatcher) hcalloc(sizeof(*ret));
344	n->next = NULL;
345	n->flags = fl | fl2;
346	n->line = line;
347	n->llen = ll;
348	n->word = word;
349	n->wlen = wl;
350	n->left = left;
351	n->lalen = lal;
352	n->right = right;
353	n->ralen = ral;
354
355	if (ret)
356	    r->next = n;
357	else
358	    ret = n;
359
360	r = n;
361    }
362    return ret;
363}
364
365/*
366 * Parse a pattern for matcher control.
367 * name is the name of the builtin from which this is called, for errors.
368 * *sp is the input string and will be updated to the end of the parsed
369 *   pattern.
370 * *lp will be set to the number of characters (possibly multibyte)
371 *   that the pattern will match.  This must be deterministic, given
372 *   the syntax allowed here.
373 * e, if non-zero, is the ASCII end character to match; if zero,
374 *   stop on a blank.
375 * *err is set to 1 to indicate an error, else to 0.
376 */
377
378/**/
379static Cpattern
380parse_pattern(char *name, char **sp, int *lp, char e, int *err)
381{
382    Cpattern ret = NULL, r = NULL, n;
383    char *s = *sp;
384    convchar_t inchar;
385    int l = 0, inlen;
386
387    *err = 0;
388
389    MB_METACHARINIT();
390    while (*s && (e ? (*s != e) : !inblank(*s))) {
391	n = (Cpattern) hcalloc(sizeof(*n));
392	n->next = NULL;
393
394	if (*s == '[' || *s == '{') {
395	    s = parse_class(n, s);
396	    if (!*s) {
397		*err = 1;
398		zwarnnam(name, "unterminated character class");
399		return NULL;
400	    }
401	    s++;
402	} else if (*s == '?') {
403	    n->tp = CPAT_ANY;
404	    s++;
405	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
406	    *err = 1;
407	    zwarnnam(name, "invalid pattern character `%c'", *s);
408	    return NULL;
409	} else {
410	    if (*s == '\\' && s[1])
411		s++;
412
413	    inlen = MB_METACHARLENCONV(s, &inchar);
414#ifdef MULTIBYTE_SUPPORT
415	    if (inchar == WEOF)
416		inchar = (convchar_t)(*s == Meta ? s[1] ^ 32 : *s);
417#endif
418	    s += inlen;
419	    n->tp = CPAT_CHAR;
420	    n->u.chr = inchar;
421	}
422	if (ret)
423	    r->next = n;
424	else
425	    ret = n;
426
427	r = n;
428
429	l++;
430    }
431    *sp = (char *) s;
432    *lp = l;
433    return ret;
434}
435
436/* Parse a character class for matcher control. */
437
438/**/
439static char *
440parse_class(Cpattern p, char *iptr)
441{
442    int endchar, firsttime = 1;
443    char *optr, *nptr;
444
445    if (*iptr++ == '[') {
446	endchar = ']';
447	/* TODO: surely [^]] is valid? */
448	if ((*iptr == '!' || *iptr == '^') && iptr[1] != ']') {
449	    p->tp = CPAT_NCLASS;
450	    iptr++;
451	} else
452	    p->tp = CPAT_CCLASS;
453    } else {
454	endchar = '}';
455	p->tp = CPAT_EQUIV;
456    }
457
458    /* find end of class.  End character can appear literally first. */
459    for (optr = iptr; optr == iptr || *optr != endchar; optr++)
460	if (!*optr)
461	    return optr;
462    /*
463     * We can always fit the parsed class within the same length
464     * because of the tokenization (including a null byte).
465     *
466     * As the input string is metafied, but shouldn't contain shell
467     * tokens, we can just add our own tokens willy nilly.
468     */
469    optr = p->u.str = zhalloc((optr-iptr) + 1);
470
471    while (firsttime || *iptr != endchar) {
472	int ch;
473
474	if (*iptr == '[' && iptr[1] == ':' &&
475	    (nptr = strchr((char *)iptr + 2, ':')) && nptr[1] == ']') {
476	    /* Range type */
477	    iptr += 2;
478	    ch = range_type((char *)iptr, nptr-iptr);
479	    iptr = nptr + 2;
480	    if (ch != PP_UNKWN)
481		*optr++ = STOUC(Meta) + ch;
482	} else {
483	    /* characters stay metafied */
484	    char *ptr1 = iptr;
485	    if (*iptr == Meta)
486		iptr++;
487	    iptr++;
488	    if (*iptr == '-' && iptr[1] && iptr[1] != endchar) {
489		/* a run of characters */
490		iptr++;
491		/* range token */
492		*optr++ = Meta + PP_RANGE;
493
494		/* start of range character */
495		if (*ptr1 == Meta) {
496		    *optr++ = Meta;
497		    *optr++ = ptr1[1] ^ 32;
498		} else
499		    *optr++ = *ptr1;
500
501		if (*iptr == Meta) {
502		    *optr++ = *iptr++;
503		    *optr++ = *iptr++;
504		} else
505		    *optr++ = *iptr++;
506	    } else {
507		if (*ptr1 == Meta) {
508		    *optr++ = Meta;
509		    *optr++ = ptr1[1] ^ 32;
510		} else
511		    *optr++ = *ptr1;
512	    }
513	}
514	firsttime = 0;
515    }
516
517    *optr = '\0';
518    return iptr;
519}
520
521/**/
522static int
523bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
524{
525    struct cadata dat;
526    char *p, **sp, *e, *m = NULL, *mstr = NULL;
527    int dm;
528    Cmatcher match = NULL;
529
530    if (incompfunc != 1) {
531	zwarnnam(name, "can only be called from completion function");
532	return 1;
533    }
534    dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg =
535	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp =
536	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
537    dat.match = NULL;
538    dat.flags = 0;
539    dat.aflags = CAF_MATCH;
540    dat.dummies = 0;
541
542    for (; *argv && **argv ==  '-'; argv++) {
543	if (!(*argv)[1]) {
544	    argv++;
545	    break;
546	}
547	for (p = *argv + 1; *p; p++) {
548	    sp = NULL;
549	    e = NULL;
550	    dm = 0;
551	    switch (*p) {
552	    case 'q':
553		dat.flags |= CMF_REMOVE;
554		break;
555	    case 'Q':
556		dat.aflags |= CAF_QUOTE;
557		break;
558	    case 'C':
559		dat.aflags |= CAF_ALL;
560		break;
561	    case 'f':
562		dat.flags |= CMF_FILE;
563		break;
564	    case 'e':
565		dat.flags |= CMF_ISPAR;
566		break;
567	    case 'a':
568		dat.aflags |= CAF_ARRAYS;
569		break;
570	    case 'k':
571		dat.aflags |= CAF_ARRAYS|CAF_KEYS;
572		break;
573	    case 'F':
574		sp = &(dat.ign);
575		e = "string expected after -%c";
576		break;
577	    case 'n':
578		dat.flags |= CMF_NOLIST;
579		break;
580	    case 'U':
581		dat.aflags &= ~CAF_MATCH;
582		break;
583	    case 'P':
584		sp = &(dat.pre);
585		e = "string expected after -%c";
586		break;
587	    case 'S':
588		sp = &(dat.suf);
589		e = "string expected after -%c";
590		break;
591	    case 'J':
592		sp = &(dat.group);
593		e = "group name expected after -%c";
594		break;
595	    case 'V':
596		if (!dat.group)
597		    dat.aflags |= CAF_NOSORT;
598		sp = &(dat.group);
599		e = "group name expected after -%c";
600		break;
601	    case '1':
602		if (!(dat.aflags & CAF_UNIQCON))
603		    dat.aflags |= CAF_UNIQALL;
604		break;
605	    case '2':
606		if (!(dat.aflags & CAF_UNIQALL))
607		    dat.aflags |= CAF_UNIQCON;
608		break;
609	    case 'i':
610		sp = &(dat.ipre);
611		e = "string expected after -%c";
612		break;
613	    case 'I':
614		sp = &(dat.isuf);
615		e = "string expected after -%c";
616		break;
617	    case 'p':
618		sp = &(dat.ppre);
619		e = "string expected after -%c";
620		break;
621	    case 's':
622		sp = &(dat.psuf);
623		e = "string expected after -%c";
624		break;
625	    case 'W':
626		sp = &(dat.prpre);
627		e = "string expected after -%c";
628		break;
629	    case 'M':
630		sp = &m;
631		e = "matching specification expected after -%c";
632		dm = 1;
633		break;
634	    case 'X':
635		sp = &(dat.exp);
636		e = "string expected after -%c";
637		break;
638	    case 'x':
639		sp = &(dat.mesg);
640		e = "string expected after -%c";
641		break;
642	    case 'r':
643		dat.flags |= CMF_REMOVE;
644		sp = &(dat.rems);
645		e = "string expected after -%c";
646		break;
647	    case 'R':
648		dat.flags |= CMF_REMOVE;
649		sp = &(dat.remf);
650		e = "function name expected after -%c";
651		break;
652	    case 'A':
653		sp = &(dat.apar);
654		e = "parameter name expected after -%c";
655		break;
656	    case 'O':
657		sp = &(dat.opar);
658		e = "parameter name expected after -%c";
659		break;
660	    case 'D':
661		sp = &(dat.dpar);
662		e = "parameter name expected after -%c";
663		break;
664	    case 'd':
665		sp = &(dat.disp);
666		e = "parameter name expected after -%c";
667		break;
668	    case 'l':
669		dat.flags |= CMF_DISPLINE;
670		break;
671	    case 'o':
672		dat.flags |= CMF_MORDER;
673		break;
674	    case 'E':
675                if (p[1]) {
676                    dat.dummies = atoi(p + 1);
677                    p = "" - 1;
678                } else if (argv[1]) {
679                    argv++;
680                    dat.dummies = atoi(*argv);
681                    p = "" - 1;
682                } else {
683                    zwarnnam(name, "number expected after -%c", *p);
684		    zsfree(mstr);
685                    return 1;
686                }
687                if (dat.dummies < 0) {
688                    zwarnnam(name, "invalid number: %d", dat.dummies);
689		    zsfree(mstr);
690                    return 1;
691                }
692		break;
693	    case '-':
694		argv++;
695		goto ca_args;
696	    default:
697		zwarnnam(name, "bad option: -%c", *p);
698		zsfree(mstr);
699		return 1;
700	    }
701	    if (sp) {
702		if (p[1]) {
703		    if (!*sp)
704			*sp = p + 1;
705		    p = "" - 1;
706		} else if (argv[1]) {
707		    argv++;
708		    if (!*sp)
709			*sp = *argv;
710		    p = "" - 1;
711		} else {
712		    zwarnnam(name, e, *p);
713		    zsfree(mstr);
714		    return 1;
715		}
716		if (dm) {
717		    if (mstr) {
718			char *tmp = tricat(mstr, " ", m);
719			zsfree(mstr);
720			mstr = tmp;
721		    } else
722			mstr = ztrdup(m);
723		    m = NULL;
724		}
725	    }
726	}
727    }
728
729 ca_args:
730
731    if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
732	zsfree(mstr);
733	return 1;
734    }
735    zsfree(mstr);
736
737    if (!*argv && !dat.group && !dat.mesg &&
738	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL)))
739	return 1;
740
741    dat.match = match = cpcmatcher(match);
742    dm = addmatches(&dat, argv);
743    freecmatcher(match);
744
745    return dm;
746}
747
748#define CVT_RANGENUM 0
749#define CVT_RANGEPAT 1
750#define CVT_PRENUM   2
751#define CVT_PREPAT   3
752#define CVT_SUFNUM   4
753#define CVT_SUFPAT   5
754
755/**/
756mod_export void
757ignore_prefix(int l)
758{
759    if (l) {
760	char *tmp, sav;
761	int pl = strlen(compprefix);
762
763	if (l > pl)
764	    l = pl;
765
766	sav = compprefix[l];
767
768	compprefix[l] = '\0';
769	tmp = tricat(compiprefix, compprefix, "");
770	zsfree(compiprefix);
771	compiprefix = tmp;
772	compprefix[l] = sav;
773	tmp = ztrdup(compprefix + l);
774	zsfree(compprefix);
775	compprefix = tmp;
776    }
777}
778
779/**/
780mod_export void
781ignore_suffix(int l)
782{
783    if (l) {
784	char *tmp, sav;
785	int sl = strlen(compsuffix);
786
787	if ((l = sl - l) < 0)
788	    l = 0;
789
790	tmp = tricat(compsuffix + l, compisuffix, "");
791	zsfree(compisuffix);
792	compisuffix = tmp;
793	sav = compsuffix[l];
794	compsuffix[l] = '\0';
795	tmp = ztrdup(compsuffix);
796	compsuffix[l] = sav;
797	zsfree(compsuffix);
798	compsuffix = tmp;
799    }
800}
801
802/**/
803mod_export void
804restrict_range(int b, int e)
805{
806    int wl = arrlen(compwords) - 1;
807
808    if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
809	int i;
810	char **p, **q, **pp;
811
812	if (e > wl)
813	    e = wl;
814
815	i = e - b + 1;
816	p = (char **) zshcalloc((i + 1) * sizeof(char *));
817
818	for (q = p, pp = compwords + b; i; i--, q++, pp++)
819	    *q = ztrdup(*pp);
820	freearray(compwords);
821	compwords = p;
822	compcurrent -= b;
823    }
824}
825
826/**/
827static int
828do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
829{
830    switch (test) {
831    case CVT_RANGENUM:
832	{
833	    int l = arrlen(compwords);
834
835	    if (na < 0)
836		na += l;
837	    else
838		na--;
839	    if (nb < 0)
840		nb += l;
841	    else
842		nb--;
843
844	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
845		return 0;
846	    if (mod)
847		restrict_range(na, nb);
848	    return 1;
849	}
850    case CVT_RANGEPAT:
851	{
852	    char **p;
853	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
854	    Patprog pp;
855
856	    i = compcurrent - 1;
857	    if (i < 0 || i >= l)
858		return 0;
859
860	    singsub(&sa);
861	    pp = patcompile(sa, PAT_STATIC, NULL);
862
863	    for (i--, p = compwords + i; i >= 0; p--, i--) {
864		if (pattry(pp, *p)) {
865		    b = i + 1;
866		    t = 1;
867		    break;
868		}
869	    }
870	    if (t && sb) {
871		int tt = 0;
872
873		singsub(&sb);
874		pp = patcompile(sb, PAT_STATIC, NULL);
875
876		for (i++, p = compwords + i; i < l; p++, i++) {
877		    if (pattry(pp, *p)) {
878			e = i - 1;
879			tt = 1;
880			break;
881		    }
882		}
883		if (tt && i < compcurrent)
884		    t = 0;
885	    }
886	    if (e < b)
887		t = 0;
888	    if (t && mod)
889		restrict_range(b, e);
890	    return t;
891	}
892    case CVT_PRENUM:
893    case CVT_SUFNUM:
894	if (!na)
895	    return 1;
896	if (na > 0 &&
897	    (int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
898	    if (mod) {
899		if (test == CVT_PRENUM)
900		    ignore_prefix(na);
901		else
902		    ignore_suffix(na);
903		return 1;
904	    }
905	    return 0;
906	}
907    case CVT_PREPAT:
908    case CVT_SUFPAT:
909	{
910	    Patprog pp;
911
912	    if (!na)
913		return 0;
914
915	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
916		return 0;
917
918	    if (test == CVT_PREPAT) {
919		int l, add;
920		char *p, sav;
921
922		if (!(l = strlen(compprefix)))
923		    return ((na == 1 || na == -1) && pattry(pp, compprefix));
924		if (na < 0) {
925		    p = compprefix + l;
926		    na = -na;
927		    add = -1;
928		} else {
929		    p = compprefix + 1 + (*compprefix == Meta);
930		    if (p > compprefix + l)
931			p = compprefix + l;
932		    add = 1;
933		}
934		for (;;) {
935		    sav = *p;
936		    *p = '\0';
937		    test = pattry(pp, compprefix);
938		    *p = sav;
939		    if (test && !--na)
940			break;
941		    if (add > 0) {
942			if (p == compprefix + l)
943			    return 0;
944			p = p + 1 + (*p == Meta);
945			if (p > compprefix + l)
946			    p = compprefix + l;
947		    } else {
948			if (p == compprefix)
949			    return 0;
950			p--;
951			if (p > compprefix && p[-1] == Meta)
952			    p--;
953		    }
954		}
955		if (mod)
956		    ignore_prefix(p - compprefix);
957	    } else {
958		int l, ol, add;
959		char *p;
960
961		if (!(ol = l = strlen(compsuffix)))
962		    return ((na == 1 || na == -1) && pattry(pp, compsuffix));
963		if (na < 0) {
964		    p = compsuffix;
965		    na = -na;
966		    add = 1;
967		} else {
968		    p = compsuffix + l - 1;
969		    if (p > compsuffix && p[-1] == Meta)
970			p--;
971		    add = -1;
972		}
973		for (;;) {
974		    if (pattry(pp, p) && !--na)
975			break;
976
977		    if (add > 0) {
978			if (p == compsuffix + l)
979			    return 0;
980			if (*p == Meta)
981			    p += 2;
982			else
983			    p++;
984		    } else {
985			if (p == compsuffix)
986			    return 0;
987			p--;
988			if (p > compsuffix && p[-1] == Meta)
989			    p--;
990		    }
991		}
992
993		if (mod)
994		    ignore_suffix(ol - (p - compsuffix));
995	    }
996	    return 1;
997	}
998    }
999    return 0;
1000}
1001
1002/**/
1003static int
1004bin_compset(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
1005{
1006    int test = 0, na = 0, nb = 0;
1007    char *sa = NULL, *sb = NULL;
1008
1009    if (incompfunc != 1) {
1010	zwarnnam(name, "can only be called from completion function");
1011	return 1;
1012    }
1013    if (argv[0][0] != '-') {
1014	zwarnnam(name, "missing option");
1015	return 1;
1016    }
1017    switch (argv[0][1]) {
1018    case 'n': test = CVT_RANGENUM; break;
1019    case 'N': test = CVT_RANGEPAT; break;
1020    case 'p': test = CVT_PRENUM; break;
1021    case 'P': test = CVT_PREPAT; break;
1022    case 's': test = CVT_SUFNUM; break;
1023    case 'S': test = CVT_SUFPAT; break;
1024    case 'q': return set_comp_sep();
1025    default:
1026	zwarnnam(name, "bad option -%c", argv[0][1]);
1027	return 1;
1028    }
1029    if (argv[0][2]) {
1030	sa = argv[0] + 2;
1031	sb = argv[1];
1032	na = 2;
1033    } else {
1034	if (!(sa = argv[1])) {
1035	    zwarnnam(name, "missing string for option -%c", argv[0][1]);
1036	    return 1;
1037	}
1038	sb = argv[2];
1039	na = 3;
1040    }
1041    if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
1042	 (sb && argv[na]))) {
1043	zwarnnam(name, "too many arguments");
1044	return 1;
1045    }
1046    switch (test) {
1047    case CVT_RANGENUM:
1048	na = atoi(sa);
1049	nb = (sb ? atoi(sb) : -1);
1050	break;
1051    case CVT_RANGEPAT:
1052	tokenize(sa);
1053	remnulargs(sa);
1054	if (sb) {
1055	    tokenize(sb);
1056	    remnulargs(sb);
1057	}
1058	break;
1059    case CVT_PRENUM:
1060    case CVT_SUFNUM:
1061	na = atoi(sa);
1062	break;
1063    case CVT_PREPAT:
1064    case CVT_SUFPAT:
1065	if (sb) {
1066	    na = atoi(sa);
1067	    sa = sb;
1068	} else
1069	    na = -1;
1070	tokenize(sa);
1071	remnulargs(sa);
1072	break;
1073    }
1074    return !do_comp_vars(test, na, sa, nb, sb, 1);
1075}
1076
1077/* Definitions for the special parameters. Note that these have to match the
1078 * order of the CP_* bits in comp.h */
1079
1080#define VAL(X) ((void *) (&(X)))
1081#define GSU(X) ((GsuScalar)(void *) (&(X)))
1082struct compparam {
1083    char *name;
1084    int type;
1085    void *var;
1086    GsuScalar gsu;
1087};
1088
1089static const struct gsu_scalar compvarscalar_gsu =
1090{ strvargetfn, strvarsetfn, compunsetfn };
1091static const struct gsu_scalar complist_gsu =
1092{ get_complist, set_complist, compunsetfn };
1093static const struct gsu_scalar unambig_gsu =
1094{ get_unambig, nullstrsetfn, compunsetfn };
1095static const struct gsu_scalar unambig_pos_gsu =
1096{ get_unambig_pos, nullstrsetfn, compunsetfn };
1097static const struct gsu_scalar insert_pos_gsu =
1098{ get_insert_pos, nullstrsetfn, compunsetfn };
1099static const struct gsu_scalar compqstack_gsu =
1100{ get_compqstack, nullstrsetfn, compunsetfn };
1101
1102static const struct gsu_integer compvarinteger_gsu =
1103{ intvargetfn, intvarsetfn, compunsetfn };
1104static const struct gsu_integer nmatches_gsu =
1105{ get_nmatches, NULL, compunsetfn };
1106static const struct gsu_integer unambig_curs_gsu =
1107{ get_unambig_curs, NULL, compunsetfn };
1108static const struct gsu_integer listlines_gsu =
1109{ get_listlines, NULL, compunsetfn };
1110
1111static const struct gsu_array compvararray_gsu =
1112{ arrvargetfn, arrvarsetfn, compunsetfn };
1113
1114
1115static struct compparam comprparams[] = {
1116    { "words", PM_ARRAY, VAL(compwords), NULL },
1117    { "redirections", PM_ARRAY, VAL(compredirs), NULL },
1118    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL },
1119    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL },
1120    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL },
1121    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL },
1122    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL },
1123    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL },
1124    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL },
1125    { NULL, 0, NULL, NULL }
1126};
1127
1128static struct compparam compkparams[] = {
1129    { "nmatches", PM_INTEGER | PM_READONLY, NULL, GSU(nmatches_gsu) },
1130    { "context", PM_SCALAR, VAL(compcontext), NULL },
1131    { "parameter", PM_SCALAR, VAL(compparameter), NULL },
1132    { "redirect", PM_SCALAR, VAL(compredirect), NULL },
1133    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL },
1134    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL },
1135    { "restore", PM_SCALAR, VAL(comprestore), NULL },
1136    { "list", PM_SCALAR, NULL, GSU(complist_gsu) },
1137    { "insert", PM_SCALAR, VAL(compinsert), NULL },
1138    { "exact", PM_SCALAR, VAL(compexact), NULL },
1139    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL },
1140    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL },
1141    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL },
1142    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, GSU(unambig_gsu) },
1143    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL,
1144      GSU(unambig_curs_gsu) },
1145    { "unambiguous_positions", PM_SCALAR | PM_READONLY, NULL,
1146      GSU(unambig_pos_gsu) },
1147    { "insert_positions", PM_SCALAR | PM_READONLY, NULL,
1148      GSU(insert_pos_gsu) },
1149    { "list_max", PM_INTEGER, VAL(complistmax), NULL },
1150    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL },
1151    { "to_end", PM_SCALAR, VAL(comptoend), NULL },
1152    { "old_list", PM_SCALAR, VAL(compoldlist), NULL },
1153    { "old_insert", PM_SCALAR, VAL(compoldins), NULL },
1154    { "vared", PM_SCALAR, VAL(compvared), NULL },
1155    { "list_lines", PM_INTEGER | PM_READONLY, NULL, GSU(listlines_gsu) },
1156    { "all_quotes", PM_SCALAR | PM_READONLY, NULL, GSU(compqstack_gsu) },
1157    { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL },
1158    { NULL, 0, NULL, NULL }
1159};
1160
1161#define COMPSTATENAME "compstate"
1162
1163static void
1164addcompparams(struct compparam *cp, Param *pp)
1165{
1166    for (; cp->name; cp++, pp++) {
1167	Param pm = createparam(cp->name,
1168			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
1169	if (!pm)
1170	    pm = (Param) paramtab->getnode(paramtab, cp->name);
1171	DPUTS(!pm, "param not set in addcompparams");
1172
1173	*pp = pm;
1174	pm->level = locallevel + 1;
1175	if ((pm->u.data = cp->var)) {
1176	    switch(PM_TYPE(cp->type)) {
1177	    case PM_SCALAR:
1178		pm->gsu.s = &compvarscalar_gsu;
1179		break;
1180	    case PM_INTEGER:
1181		pm->gsu.i = &compvarinteger_gsu;
1182		pm->base = 10;
1183		break;
1184	    case PM_ARRAY:
1185		pm->gsu.a = &compvararray_gsu;
1186		break;
1187	    }
1188	} else {
1189	    pm->gsu.s = cp->gsu;
1190	}
1191    }
1192}
1193
1194static const struct gsu_hash compstate_gsu =
1195{ get_compstate, set_compstate, compunsetfn };
1196
1197/**/
1198void
1199makecompparams(void)
1200{
1201    Param cpm;
1202    HashTable tht;
1203
1204    addcompparams(comprparams, comprpms);
1205
1206    if (!(cpm = createparam(COMPSTATENAME,
1207			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
1208	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
1209    DPUTS(!cpm, "param not set in makecompparams");
1210
1211    comprpms[CPN_COMPSTATE] = cpm;
1212    tht = paramtab;
1213    cpm->level = locallevel + 1;
1214    cpm->gsu.h = &compstate_gsu;
1215    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
1216    addcompparams(compkparams, compkpms);
1217    paramtab = tht;
1218}
1219
1220/**/
1221static HashTable
1222get_compstate(Param pm)
1223{
1224    return pm->u.hash;
1225}
1226
1227/**/
1228static void
1229set_compstate(UNUSED(Param pm), HashTable ht)
1230{
1231    struct compparam *cp;
1232    Param *pp;
1233    HashNode hn;
1234    int i;
1235    struct value v;
1236    char *str;
1237
1238    if (!ht)
1239        return;
1240
1241    for (i = 0; i < ht->hsize; i++)
1242	for (hn = ht->nodes[i]; hn; hn = hn->next)
1243	    for (cp = compkparams,
1244		 pp = compkpms; cp->name; cp++, pp++)
1245		if (!strcmp(hn->nam, cp->name)) {
1246		    v.isarr = v.flags = v.start = 0;
1247		    v.end = -1;
1248		    v.arr = NULL;
1249		    v.pm = (Param) hn;
1250		    if (cp->type == PM_INTEGER)
1251			*((zlong *) cp->var) = getintvalue(&v);
1252		    else if ((str = getstrvalue(&v))) {
1253			zsfree(*((char **) cp->var));
1254			*((char **) cp->var) = ztrdup(str);
1255		    }
1256		    (*pp)->node.flags &= ~PM_UNSET;
1257
1258		    break;
1259		}
1260    deleteparamtable(ht);
1261}
1262
1263/**/
1264static zlong
1265get_nmatches(UNUSED(Param pm))
1266{
1267    return (permmatches(0) ? 0 : nmatches);
1268}
1269
1270/**/
1271static zlong
1272get_listlines(UNUSED(Param pm))
1273{
1274    return list_lines();
1275}
1276
1277/**/
1278static void
1279set_complist(UNUSED(Param pm), char *v)
1280{
1281    comp_list(v);
1282}
1283
1284/**/
1285static char *
1286get_complist(UNUSED(Param pm))
1287{
1288    return complist;
1289}
1290
1291/**/
1292static char *
1293get_unambig(UNUSED(Param pm))
1294{
1295    return unambig_data(NULL, NULL, NULL);
1296}
1297
1298/**/
1299static zlong
1300get_unambig_curs(UNUSED(Param pm))
1301{
1302    int c;
1303
1304    unambig_data(&c, NULL, NULL);
1305
1306    return c;
1307}
1308
1309/**/
1310static char *
1311get_unambig_pos(UNUSED(Param pm))
1312{
1313    char *p;
1314
1315    unambig_data(NULL, &p, NULL);
1316
1317    return p;
1318}
1319
1320/**/
1321static char *
1322get_insert_pos(UNUSED(Param pm))
1323{
1324    char *p;
1325
1326    unambig_data(NULL, NULL, &p);
1327
1328    return p;
1329}
1330
1331/**/
1332static char *
1333get_compqstack(UNUSED(Param pm))
1334{
1335    char *p, *ptr, *cqp;
1336
1337    if (!compqstack)		/* TODO: don't think this can happen... */
1338	return "";
1339
1340    ptr = p = zhalloc(2*strlen(compqstack)+1);
1341
1342    for (cqp = compqstack; *cqp; cqp++) {
1343	char *str = comp_quoting_string(*cqp);
1344	*ptr++ = *str;
1345    }
1346    *ptr = '\0';
1347
1348    return p;
1349}
1350
1351/**/
1352static void
1353compunsetfn(Param pm, int exp)
1354{
1355    if (exp) {
1356	if (pm->u.data) {
1357	    if (PM_TYPE(pm->node.flags) == PM_SCALAR) {
1358		zsfree(*((char **) pm->u.data));
1359		*((char **) pm->u.data) = ztrdup("");
1360	    } else if (PM_TYPE(pm->node.flags) == PM_ARRAY) {
1361		freearray(*((char ***) pm->u.data));
1362		*((char ***) pm->u.data) = zshcalloc(sizeof(char *));
1363	    } else if (PM_TYPE(pm->node.flags) == PM_HASHED) {
1364		deleteparamtable(pm->u.hash);
1365		pm->u.hash = NULL;
1366	    }
1367	}
1368    } else if (PM_TYPE(pm->node.flags) == PM_HASHED) {
1369	Param *p;
1370	int i;
1371
1372	deletehashtable(pm->u.hash);
1373	pm->u.hash = NULL;
1374
1375	for (p = compkpms, i = CP_KEYPARAMS; i--; p++)
1376	    *p = NULL;
1377    }
1378    if (!exp) {
1379	Param *p;
1380	int i;
1381
1382	for (p = comprpms, i = CP_REALPARAMS; i; p++, i--)
1383	    if (*p == pm) {
1384		*p = NULL;
1385		break;
1386	    }
1387    }
1388}
1389
1390/**/
1391void
1392comp_setunset(int rset, int runset, int kset, int kunset)
1393{
1394    Param *p;
1395
1396    if (comprpms && (rset >= 0 || runset >= 0)) {
1397	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
1398	    if (*p) {
1399		if (rset & 1)
1400		    (*p)->node.flags &= ~PM_UNSET;
1401		if (runset & 1)
1402		    (*p)->node.flags |= PM_UNSET;
1403	    }
1404	}
1405    }
1406    if (compkpms && (kset >= 0 || kunset >= 0)) {
1407	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
1408	    if (*p) {
1409		if (kset & 1)
1410		    (*p)->node.flags &= ~PM_UNSET;
1411		if (kunset & 1)
1412		    (*p)->node.flags |= PM_UNSET;
1413	    }
1414	}
1415    }
1416}
1417
1418/**/
1419static int
1420comp_wrapper(Eprog prog, FuncWrap w, char *name)
1421{
1422    if (incompfunc != 1)
1423	return 1;
1424    else {
1425	char *orest, *opre, *osuf, *oipre, *oisuf, **owords, **oredirs;
1426	char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq;
1427	zlong ocur;
1428	unsigned int runset = 0, kunset = 0, m, sm;
1429	Param *pp;
1430
1431	m = CP_WORDS | CP_REDIRS | CP_CURRENT | CP_PREFIX | CP_SUFFIX |
1432	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
1433	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
1434	    if ((m & 1) && ((*pp)->node.flags & PM_UNSET))
1435		runset |= sm;
1436	}
1437	if (compkpms[CPN_RESTORE]->node.flags & PM_UNSET)
1438	    kunset = CP_RESTORE;
1439	orest = comprestore;
1440	comprestore = ztrdup("auto");
1441	ocur = compcurrent;
1442	opre = ztrdup(compprefix);
1443	osuf = ztrdup(compsuffix);
1444	oipre = ztrdup(compiprefix);
1445	oisuf = ztrdup(compisuffix);
1446	oqipre = ztrdup(compqiprefix);
1447	oqisuf = ztrdup(compqisuffix);
1448	oq = ztrdup(compquote);
1449	oqi = ztrdup(compquoting);
1450	oqs = ztrdup(compqstack);
1451	oaq = ztrdup(autoq);
1452	owords = zarrdup(compwords);
1453	oredirs = zarrdup(compredirs);
1454
1455	runshfunc(prog, w, name);
1456
1457	if (comprestore && !strcmp(comprestore, "auto")) {
1458	    compcurrent = ocur;
1459	    zsfree(compprefix);
1460	    compprefix = opre;
1461	    zsfree(compsuffix);
1462	    compsuffix = osuf;
1463	    zsfree(compiprefix);
1464	    compiprefix = oipre;
1465	    zsfree(compisuffix);
1466	    compisuffix = oisuf;
1467	    zsfree(compqiprefix);
1468	    compqiprefix = oqipre;
1469	    zsfree(compqisuffix);
1470	    compqisuffix = oqisuf;
1471	    zsfree(compquote);
1472	    compquote = oq;
1473	    zsfree(compquoting);
1474	    compquoting = oqi;
1475	    zsfree(compqstack);
1476	    compqstack = oqs;
1477	    zsfree(autoq);
1478	    autoq = oaq;
1479	    freearray(compwords);
1480	    freearray(compredirs);
1481	    compwords = owords;
1482            compredirs = oredirs;
1483	    comp_setunset(CP_COMPSTATE |
1484			  (~runset & (CP_WORDS | CP_REDIRS |
1485                                      CP_CURRENT | CP_PREFIX |
1486                                      CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
1487                                      CP_QIPREFIX | CP_QISUFFIX)),
1488			  (runset & CP_ALLREALS),
1489			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
1490	} else {
1491	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
1492			  (kunset & CP_RESTORE));
1493	    zsfree(opre);
1494	    zsfree(osuf);
1495	    zsfree(oipre);
1496	    zsfree(oisuf);
1497	    zsfree(oqipre);
1498	    zsfree(oqisuf);
1499	    zsfree(oq);
1500	    zsfree(oqi);
1501	    zsfree(oqs);
1502	    zsfree(oaq);
1503	    freearray(owords);
1504	    freearray(oredirs);
1505	}
1506	zsfree(comprestore);
1507	comprestore = orest;
1508
1509	return 0;
1510    }
1511}
1512
1513/**/
1514static int
1515comp_check(void)
1516{
1517    if (incompfunc != 1) {
1518	zerr("condition can only be used in completion function");
1519	return 0;
1520    }
1521    return 1;
1522}
1523
1524/**/
1525static int
1526cond_psfix(char **a, int id)
1527{
1528    if (comp_check()) {
1529	if (a[1])
1530	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
1531				0, NULL, 0);
1532	else
1533	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
1534    }
1535    return 0;
1536}
1537
1538/**/
1539static int
1540cond_range(char **a, int id)
1541{
1542    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
1543			(id ? cond_str(a, 1, 1) : NULL), 0);
1544}
1545
1546static struct builtin bintab[] = {
1547    BUILTIN("compadd", BINF_HANDLES_OPTS, bin_compadd, 0, -1, 0, NULL, NULL),
1548    BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
1549};
1550
1551static struct conddef cotab[] = {
1552    CONDDEF("after", 0, cond_range, 1, 1, 0),
1553    CONDDEF("between", 0, cond_range, 2, 2, 1),
1554    CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
1555    CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
1556};
1557
1558static struct funcwrap wrapper[] = {
1559    WRAPDEF(comp_wrapper),
1560};
1561
1562/* The order of the entries in this table has to match the *HOOK
1563 * macros in comp.h */
1564
1565/**/
1566struct hookdef comphooks[] = {
1567    HOOKDEF("insert_match", NULL, HOOKF_ALL),
1568    HOOKDEF("menu_start", NULL, HOOKF_ALL),
1569    HOOKDEF("compctl_make", NULL, 0),
1570    HOOKDEF("compctl_cleanup", NULL, 0),
1571    HOOKDEF("comp_list_matches", ilistmatches, 0),
1572};
1573
1574static struct features module_features = {
1575    bintab, sizeof(bintab)/sizeof(*bintab),
1576    cotab, sizeof(cotab)/sizeof(*cotab),
1577    NULL, 0,
1578    NULL, 0,
1579    0
1580};
1581
1582/**/
1583int
1584setup_(UNUSED(Module m))
1585{
1586    hasperm = 0;
1587
1588    comprpms = compkpms = NULL;
1589    compwords = compredirs = NULL;
1590    compprefix = compsuffix = compiprefix = compisuffix =
1591	compqiprefix = compqisuffix =
1592	compcontext = compparameter = compredirect = compquote =
1593	compquoting = comprestore = complist = compinsert =
1594	compexact = compexactstr = comppatmatch = comppatinsert =
1595	complastprompt = comptoend = compoldlist = compoldins =
1596	compvared = compqstack = NULL;
1597    complastprefix = ztrdup("");
1598    complastsuffix = ztrdup("");
1599    complistmax = 0;
1600    hascompmod = 1;
1601
1602    return 0;
1603}
1604
1605/**/
1606int
1607features_(Module m, char ***features)
1608{
1609    *features = featuresarray(m, &module_features);
1610    return 0;
1611}
1612
1613/**/
1614int
1615enables_(Module m, int **enables)
1616{
1617    return handlefeatures(m, &module_features, enables);
1618}
1619
1620/**/
1621int
1622boot_(Module m)
1623{
1624    addhookfunc("complete", (Hookfn) do_completion);
1625    addhookfunc("before_complete", (Hookfn) before_complete);
1626    addhookfunc("after_complete", (Hookfn) after_complete);
1627    addhookfunc("accept_completion", (Hookfn) accept_last);
1628    addhookfunc("reverse_menu", (Hookfn) reverse_menu);
1629    addhookfunc("list_matches", (Hookfn) list_matches);
1630    addhookfunc("invalidate_list", (Hookfn) invalidate_list);
1631    (void)addhookdefs(m, comphooks, sizeof(comphooks)/sizeof(*comphooks));
1632    return addwrapper(m, wrapper);
1633}
1634
1635/**/
1636int
1637cleanup_(Module m)
1638{
1639    deletehookfunc("complete", (Hookfn) do_completion);
1640    deletehookfunc("before_complete", (Hookfn) before_complete);
1641    deletehookfunc("after_complete", (Hookfn) after_complete);
1642    deletehookfunc("accept_completion", (Hookfn) accept_last);
1643    deletehookfunc("reverse_menu", (Hookfn) reverse_menu);
1644    deletehookfunc("list_matches", (Hookfn) list_matches);
1645    deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
1646    (void)deletehookdefs(m, comphooks,
1647			 sizeof(comphooks)/sizeof(*comphooks));
1648    deletewrapper(m, wrapper);
1649    return setfeatureenables(m, &module_features, NULL);
1650}
1651
1652/**/
1653int
1654finish_(UNUSED(Module m))
1655{
1656    if (compwords)
1657	freearray(compwords);
1658    if (compredirs)
1659	freearray(compredirs);
1660    zsfree(compprefix);
1661    zsfree(compsuffix);
1662    zsfree(complastprefix);
1663    zsfree(complastsuffix);
1664    zsfree(compiprefix);
1665    zsfree(compisuffix);
1666    zsfree(compqiprefix);
1667    zsfree(compqisuffix);
1668    zsfree(compcontext);
1669    zsfree(compparameter);
1670    zsfree(compredirect);
1671    zsfree(compquote);
1672    zsfree(compqstack);
1673    zsfree(compquoting);
1674    zsfree(comprestore);
1675    zsfree(complist);
1676    zsfree(compinsert);
1677    zsfree(compexact);
1678    zsfree(compexactstr);
1679    zsfree(comppatmatch);
1680    zsfree(comppatinsert);
1681    zsfree(complastprompt);
1682    zsfree(comptoend);
1683    zsfree(compoldlist);
1684    zsfree(compoldins);
1685    zsfree(compvared);
1686
1687    hascompmod = 0;
1688
1689    return 0;
1690}
1691