summaryrefslogtreecommitdiff
path: root/Src/Zle/complete.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/complete.c')
-rw-r--r--Src/Zle/complete.c1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
new file mode 100644
index 000000000..283b8de62
--- /dev/null
+++ b/Src/Zle/complete.c
@@ -0,0 +1,1332 @@
+/*
+ * complete.c - the complete module
+ *
+ * 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"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+
+/**/
+void
+freecmlist(Cmlist l)
+{
+ Cmlist n;
+
+ while (l) {
+ n = l->next;
+ freecmatcher(l->matcher);
+ zsfree(l->str);
+
+ zfree(l, sizeof(struct cmlist));
+
+ l = n;
+ }
+}
+
+/**/
+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;
+ zfree(p, sizeof(struct cpattern));
+
+ p = n;
+ }
+}
+
+/* Copy a list of completion matchers. */
+
+static Cmlist
+cpcmlist(Cmlist l)
+{
+ Cmlist r = NULL, *p = &r, n;
+
+ while (l) {
+ *p = n = (Cmlist) zalloc(sizeof(struct cmlist));
+ n->next = NULL;
+ n->matcher = cpcmatcher(l->matcher);
+ n->str = ztrdup(l->str);
+
+ p = &(n->next);
+ l = l->next;
+ }
+ return r;
+}
+
+/* Copy a completion matcher list. */
+
+/**/
+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 completion matcher pattern. */
+
+/**/
+static Cpattern
+cpcpattern(Cpattern o)
+{
+ Cpattern r = NULL, *p = &r, n;
+
+ while (o) {
+ *p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+
+ n->next = NULL;
+ memcpy(n->tab, o->tab, 256);
+ n->equiv = o->equiv;
+
+ p = &(n->next);
+ o = o->next;
+ }
+ return r;
+}
+
+/* Set the global match specs. */
+
+/**/
+int
+set_gmatcher(char *name, char **argv)
+{
+ Cmlist l = NULL, *q = &l, n;
+ Cmatcher m;
+
+ while (*argv) {
+ if ((m = parse_cmatcher(name, *argv)) == pcm_err)
+ return 1;
+ *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+ n->next = NULL;
+ n->matcher = m;
+ n->str = *argv++;
+
+ q = &(n->next);
+ }
+ freecmlist(cmatcher);
+ PERMALLOC {
+ cmatcher = cpcmlist(l);
+ } LASTALLOC;
+
+ return 1;
+}
+
+/* Parse a string for matcher control, containing multiple matchers. */
+
+/**/
+Cmatcher
+parse_cmatcher(char *name, char *s)
+{
+ Cmatcher ret = NULL, r = NULL, n;
+ Cpattern line, word, left, right;
+ int fl, ll, wl, lal, ral, err;
+
+ if (!*s)
+ return NULL;
+
+ while (*s) {
+ while (*s && inblank(*s)) s++;
+
+ if (!*s) break;
+
+ switch (*s) {
+ case 'l': fl = CMF_LEFT; break;
+ case 'r': fl = CMF_RIGHT; break;
+ case 'm': fl = 0; break;
+ case 'L': fl = CMF_LEFT | CMF_LINE; break;
+ case 'R': fl = CMF_RIGHT | CMF_LINE; break;
+ case 'M': fl = CMF_LINE; break;
+ default:
+ zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
+ return pcm_err;
+ }
+ if (s[1] != ':') {
+ zwarnnam(name, "missing `:'", NULL, 0);
+ return pcm_err;
+ }
+ s += 2;
+ if (!*s) {
+ zwarnnam(name, "missing patterns", NULL, 0);
+ return pcm_err;
+ }
+ if (fl & CMF_LEFT) {
+ left = parse_pattern(name, &s, &lal, '|', &err);
+ if (err)
+ return pcm_err;
+ if (!*s || !*++s) {
+ zwarnnam(name, "missing line pattern", NULL, 0);
+ return pcm_err;
+ }
+ } else
+ left = NULL;
+
+ line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
+ &err);
+ if (err)
+ return pcm_err;
+ if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
+ zwarnnam(name, "missing right anchor", NULL, 0);
+ } else if (!(fl & CMF_RIGHT)) {
+ if (!*s) {
+ zwarnnam(name, "missing word pattern", NULL, 0);
+ return pcm_err;
+ }
+ s++;
+ }
+ if (fl & CMF_RIGHT) {
+ right = parse_pattern(name, &s, &ral, '=', &err);
+ if (err)
+ return pcm_err;
+ if (!*s) {
+ zwarnnam(name, "missing word pattern", NULL, 0);
+ return pcm_err;
+ }
+ s++;
+ } else
+ right = NULL;
+
+ if (*s == '*') {
+ if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
+ zwarnnam(name, "need anchor for `*'", NULL, 0);
+ return pcm_err;
+ }
+ word = NULL;
+ wl = -1;
+ s++;
+ } else {
+ word = parse_pattern(name, &s, &wl, 0, &err);
+
+ if (!word && !line) {
+ zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
+ return pcm_err;
+ }
+ }
+ if (err)
+ return pcm_err;
+
+ n = (Cmatcher) zcalloc(sizeof(*ret));
+ n->next = NULL;
+ n->flags = fl;
+ 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. */
+
+/**/
+static Cpattern
+parse_pattern(char *name, char **sp, int *lp, char e, int *err)
+{
+ Cpattern ret = NULL, r = NULL, n;
+ unsigned char *s = (unsigned char *) *sp;
+ int l = 0;
+
+ *err = 0;
+
+ while (*s && (e ? (*s != e) : !inblank(*s))) {
+ n = (Cpattern) hcalloc(sizeof(*n));
+ n->next = NULL;
+ n->equiv = 0;
+
+ if (*s == '[') {
+ s = parse_class(n, s + 1, ']');
+ if (!*s) {
+ *err = 1;
+ zwarnnam(name, "unterminated character class", NULL, 0);
+ return NULL;
+ }
+ } else if (*s == '{') {
+ n->equiv = 1;
+ s = parse_class(n, s + 1, '}');
+ if (!*s) {
+ *err = 1;
+ zwarnnam(name, "unterminated character class", NULL, 0);
+ return NULL;
+ }
+ } else if (*s == '?') {
+ memset(n->tab, 1, 256);
+ } else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
+ *err = 1;
+ zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
+ return NULL;
+ } else {
+ if (*s == '\\' && s[1])
+ s++;
+
+ memset(n->tab, 0, 256);
+ n->tab[*s] = 1;
+ }
+ if (ret)
+ r->next = n;
+ else
+ ret = n;
+
+ r = n;
+
+ l++;
+ s++;
+ }
+ *sp = (char *) s;
+ *lp = l;
+ return ret;
+}
+
+/* Parse a character class for matcher control. */
+
+/**/
+static unsigned char *
+parse_class(Cpattern p, unsigned char *s, unsigned char e)
+{
+ int n = 0, i = 1, j, eq = (e == '}'), k = 1;
+
+ if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
+
+ memset(p->tab, n, 256);
+
+ n = !n;
+ while (*s && (k || *s != e)) {
+ if (s[1] == '-' && s[2] != e) {
+ /* a run of characters */
+ for (j = (int) *s; j <= (int) s[2]; j++)
+ p->tab[j] = (eq ? i++ : n);
+
+ s += 3;
+ } else
+ p->tab[*s++] = (eq ? i++ : n);
+ k = 0;
+ }
+ return s;
+}
+
+/**/
+static int
+bin_compadd(char *name, char **argv, char *ops, int func)
+{
+ struct cadata dat;
+ char *p, **sp, *e, *m = NULL;
+ int dm;
+ Cmatcher match = NULL;
+
+ if (incompfunc != 1) {
+ zerrnam(name, "can only be called from completion function", NULL, 0);
+ return 1;
+ }
+ dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
+ dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp =
+ dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
+ dat.match = NULL;
+ dat.flags = 0;
+ dat.aflags = CAF_MATCH;
+
+ for (; *argv && **argv == '-'; argv++) {
+ if (!(*argv)[1]) {
+ argv++;
+ break;
+ }
+ for (p = *argv + 1; *p; p++) {
+ sp = NULL;
+ e = NULL;
+ dm = 0;
+ switch (*p) {
+ case 'q':
+ dat.flags |= CMF_REMOVE;
+ break;
+ case 'Q':
+ dat.aflags |= CAF_QUOTE;
+ break;
+ case 'f':
+ dat.flags |= CMF_FILE;
+ break;
+ case 'e':
+ dat.flags |= CMF_ISPAR;
+ 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 'y':
+ sp = &(dat.ylist);
+ e = "string expected after -%c";
+ 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 'a':
+ dat.aflags |= CAF_ALT;
+ break;
+ case 'M':
+ sp = &m;
+ e = "matching specification expected after -%c";
+ dm = 1;
+ break;
+ case 'X':
+ sp = &(dat.exp);
+ 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 '-':
+ argv++;
+ goto ca_args;
+ default:
+ zerrnam(name, "bad option: -%c", NULL, *p);
+ return 1;
+ }
+ if (sp) {
+ if (p[1]) {
+ if (!*sp)
+ *sp = p + 1;
+ p = "" - 1;
+ } else if (argv[1]) {
+ argv++;
+ if (!*sp)
+ *sp = *argv;
+ p = "" - 1;
+ } else {
+ zerrnam(name, e, NULL, *p);
+ return 1;
+ }
+ if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
+ match = NULL;
+ return 1;
+ }
+ }
+ }
+ }
+ ca_args:
+ if (!*argv)
+ return 1;
+
+ dat.match = match = cpcmatcher(match);
+ dm = addmatches(&dat, argv);
+ freecmatcher(match);
+
+ return dm;
+}
+
+#define CVT_RANGENUM 0
+#define CVT_RANGEPAT 1
+#define CVT_PRENUM 2
+#define CVT_PREPAT 3
+#define CVT_SUFNUM 4
+#define CVT_SUFPAT 5
+
+/**/
+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;
+ }
+}
+
+/**/
+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;
+ }
+}
+
+/**/
+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 **) zcalloc((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_STATIC, 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)
+ return 1;
+ if (na > 0 &&
+ strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
+ if (mod) {
+ if (test == CVT_PRENUM)
+ ignore_prefix(na);
+ else
+ ignore_suffix(na);
+ return 1;
+ }
+ return 0;
+ }
+ case CVT_PREPAT:
+ case CVT_SUFPAT:
+ {
+ Patprog pp;
+
+ if (!na)
+ return 0;
+
+ if (!(pp = patcompile(sa, PAT_STATIC, 0)))
+ return 0;
+
+ if (test == CVT_PREPAT) {
+ int l, add;
+ char *p, sav;
+
+ if (!(l = strlen(compprefix)))
+ return 0;
+ if (na < 0) {
+ p = compprefix + l;
+ na = -na;
+ add = -1;
+ } else {
+ p = compprefix + 1;
+ add = 1;
+ }
+ for (; l; l--, p += add) {
+ sav = *p;
+ *p = '\0';
+ test = pattry(pp, compprefix);
+ *p = sav;
+ if (test && !--na)
+ break;
+ }
+ if (!l)
+ return 0;
+ if (mod)
+ ignore_prefix(p - compprefix);
+ } else {
+ int l, ol, add;
+ char *p;
+
+ if (!(ol = l = strlen(compsuffix)))
+ return 0;
+ if (na < 0) {
+ p = compsuffix;
+ na = -na;
+ add = 1;
+ } else {
+ p = compsuffix + l - 1;
+ add = -1;
+ }
+ for (; l; l--, p += add)
+ if (pattry(pp, p) && !--na)
+ break;
+
+ if (!l)
+ return 0;
+ if (mod)
+ ignore_suffix(ol - (p - compsuffix));
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**/
+static int
+bin_compset(char *name, char **argv, char *ops, int func)
+{
+ int test = 0, na = 0, nb = 0;
+ char *sa = NULL, *sb = NULL;
+
+ if (incompfunc != 1) {
+ zerrnam(name, "can only be called from completion function", NULL, 0);
+ return 1;
+ }
+ if (argv[0][0] != '-') {
+ zerrnam(name, "missing option", NULL, 0);
+ 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:
+ zerrnam(name, "bad option -%c", NULL, argv[0][1]);
+ return 1;
+ }
+ if (argv[0][2]) {
+ sa = argv[0] + 2;
+ sb = argv[1];
+ na = 2;
+ } else {
+ if (!(sa = argv[1])) {
+ zerrnam(name, "missing string for option -%c", NULL, argv[0][1]);
+ return 1;
+ }
+ sb = argv[2];
+ na = 3;
+ }
+ if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
+ (sb && argv[na]))) {
+ zerrnam(name, "too many arguments", NULL, 0);
+ return 1;
+ }
+ switch (test) {
+ case CVT_RANGENUM:
+ na = atoi(sa);
+ nb = (sb ? atoi(sb) : -1);
+ break;
+ case CVT_RANGEPAT:
+ tokenize(sa);
+ sa = rembslash(sa);
+ remnulargs(sa);
+ if (sb) {
+ tokenize(sb);
+ sb = rembslash(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);
+ sa = rembslash(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)))
+struct compparam {
+ char *name;
+ int type;
+ void *var, *set, *get;
+};
+
+static struct compparam comprparams[] = {
+ { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
+ { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
+ { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
+ { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
+ { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
+ { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
+ { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
+ { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static struct compparam compkparams[] = {
+ { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
+ { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
+ { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
+ { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL },
+ { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
+ { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
+ { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
+ { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL },
+ { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
+ { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
+ { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
+ { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
+ { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
+ { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
+ { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
+ { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
+ { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
+ { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
+ { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
+ VAL(get_unambig_curs) },
+ { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+ { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
+ { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
+ { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
+ { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
+ { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
+ { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
+ { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
+ { NULL, 0, NULL, 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->sets.cfn = strvarsetfn;
+ pm->gets.cfn = strvargetfn;
+ break;
+ case PM_INTEGER:
+ pm->sets.ifn = intvarsetfn;
+ pm->gets.ifn = intvargetfn;
+ pm->ct = 10;
+ break;
+ case PM_ARRAY:
+ pm->sets.afn = arrvarsetfn;
+ pm->gets.afn = arrvargetfn;
+ break;
+ }
+ } else {
+ pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
+ pm->gets.cfn = (char *(*) _((Param))) cp->get;
+ }
+ pm->unsetfn = compunsetfn;
+ }
+}
+
+/**/
+void
+makecompparams(void)
+{
+ Param cpm;
+ HashTable tht;
+
+ addcompparams(comprparams, comprpms);
+
+ if (!(cpm = createparam(COMPSTATENAME,
+ PM_SPECIAL|PM_REMOVABLE|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;
+ cpm->gets.hfn = get_compstate;
+ cpm->sets.hfn = set_compstate;
+ cpm->unsetfn = compunsetfn;
+ 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(Param pm, HashTable ht)
+{
+ struct compparam *cp;
+ Param *pp;
+ HashNode hn;
+ int i;
+ struct value v;
+ char *str;
+
+ 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.inv = v.a = 0;
+ v.b = -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)->flags &= ~PM_UNSET;
+
+ break;
+ }
+ deleteparamtable(ht);
+}
+
+/**/
+static zlong
+get_nmatches(Param pm)
+{
+ return num_matches(1);
+}
+
+/**/
+static zlong
+get_anmatches(Param pm)
+{
+ return num_matches(0);
+}
+
+/**/
+static zlong
+get_listlines(Param pm)
+{
+ return list_lines();
+}
+
+/**/
+static void
+set_complist(Param pm, char *v)
+{
+ comp_list(v);
+}
+
+/**/
+static char *
+get_complist(Param pm)
+{
+ return complist;
+}
+
+/**/
+static char *
+get_unambig(Param pm)
+{
+ return unambig_data(NULL);
+}
+
+/**/
+static zlong
+get_unambig_curs(Param pm)
+{
+ int c;
+
+ unambig_data(&c);
+
+ return c;
+}
+
+/**/
+static void
+compunsetfn(Param pm, int exp)
+{
+ if (exp) {
+ if (PM_TYPE(pm->flags) == PM_SCALAR) {
+ zsfree(*((char **) pm->u.data));
+ *((char **) pm->u.data) = ztrdup("");
+ } else if (PM_TYPE(pm->flags) == PM_ARRAY) {
+ freearray(*((char ***) pm->u.data));
+ *((char ***) pm->u.data) = zcalloc(sizeof(char *));
+ }
+ pm->flags |= PM_UNSET;
+ }
+}
+
+/**/
+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 (rset & 1)
+ (*p)->flags &= ~PM_UNSET;
+ if (runset & 1)
+ (*p)->flags |= PM_UNSET;
+ }
+ }
+ if (comprpms && (kset >= 0 || kunset >= 0)) {
+ for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
+ if (kset & 1)
+ (*p)->flags &= ~PM_UNSET;
+ if (kunset & 1)
+ (*p)->flags |= PM_UNSET;
+ }
+ }
+}
+
+/**/
+static int
+comp_wrapper(List list, FuncWrap w, char *name)
+{
+ if (incompfunc != 1)
+ return 1;
+ else {
+ char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
+ char *oqipre, *oqisuf, *oq, *oqi;
+ zlong ocur;
+ unsigned int runset = 0, kunset = 0, m, sm;
+ Param *pp;
+
+ m = CP_WORDS | 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)->flags & PM_UNSET))
+ runset |= sm;
+ }
+ if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
+ kunset = CP_RESTORE;
+ orest = comprestore;
+ comprestore = ztrdup("auto");
+ ocur = compcurrent;
+ opre = dupstring(compprefix);
+ osuf = dupstring(compsuffix);
+ oipre = dupstring(compiprefix);
+ oisuf = dupstring(compisuffix);
+ oqipre = dupstring(compqiprefix);
+ oqisuf = dupstring(compqisuffix);
+ oq = dupstring(compquote);
+ oqi = dupstring(compquoting);
+
+ HEAPALLOC {
+ owords = arrdup(compwords);
+ } LASTALLOC;
+
+ runshfunc(list, w, name);
+
+ if (comprestore && !strcmp(comprestore, "auto")) {
+ compcurrent = ocur;
+ zsfree(compprefix);
+ compprefix = ztrdup(opre);
+ zsfree(compsuffix);
+ compsuffix = ztrdup(osuf);
+ zsfree(compiprefix);
+ compiprefix = ztrdup(oipre);
+ zsfree(compisuffix);
+ compisuffix = ztrdup(oisuf);
+ zsfree(compqiprefix);
+ compqiprefix = ztrdup(oqipre);
+ zsfree(compqisuffix);
+ compqisuffix = ztrdup(oqisuf);
+ zsfree(compquote);
+ compquote = ztrdup(oq);
+ zsfree(compquoting);
+ compquoting = ztrdup(oqi);
+ freearray(compwords);
+ PERMALLOC {
+ compwords = arrdup(owords);
+ } LASTALLOC;
+ comp_setunset(CP_COMPSTATE |
+ (~runset & (CP_WORDS | 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(comprestore);
+ comprestore = orest;
+
+ return 0;
+ }
+}
+
+/**/
+static int
+comp_check(void)
+{
+ if (incompfunc != 1) {
+ zerr("condition can only be used in completion function", NULL, 0);
+ 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 void
+cmsetfn(Param pm, char **v)
+{
+ set_gmatcher(pm->nam, v);
+}
+
+/**/
+static char **
+cmgetfn(Param pm)
+{
+ int num;
+ Cmlist p;
+ char **ret, **q;
+
+ for (num = 0, p = cmatcher; p; p = p->next, num++);
+
+ ret = (char **) zhalloc((num + 1) * sizeof(char *));
+
+ for (q = ret, p = cmatcher; p; p = p->next, q++)
+ *q = dupstring(p->str);
+ *q = NULL;
+
+ return ret;
+}
+
+/**/
+static void
+cmunsetfn(Param pm, int exp)
+{
+ char *dummy[1];
+
+ dummy[0] = NULL;
+ set_gmatcher(pm->nam, dummy);
+}
+
+static struct builtin bintab[] = {
+ BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+ BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
+};
+
+static struct conddef cotab[] = {
+ CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
+ CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
+ CONDDEF("between", 0, cond_range, 2, 2, 1),
+ CONDDEF("after", 0, cond_range, 1, 1, 0),
+};
+
+static struct funcwrap wrapper[] = {
+ WRAPDEF(comp_wrapper),
+};
+
+static struct paramdef patab[] = {
+ PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
+};
+
+/**/
+int
+setup_complete(Module m)
+{
+ makecompparamsptr = makecompparams;
+ comp_setunsetptr = comp_setunset;
+
+ return 0;
+}
+
+/**/
+int
+boot_complete(Module m)
+{
+ if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
+ addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
+ addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
+ !addwrapper(m, wrapper)))
+ return 1;
+ return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_complete(Module m)
+{
+ deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+ deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+ deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
+ deletewrapper(m, wrapper);
+ return 0;
+}
+
+/**/
+int
+finish_complete(Module m)
+{
+ makecompparamsptr = NULL;
+ comp_setunsetptr = NULL;
+
+ return 0;
+}
+
+#endif