summaryrefslogtreecommitdiff
path: root/Src/Zle/compctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/compctl.c')
-rw-r--r--Src/Zle/compctl.c1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
new file mode 100644
index 000000000..658cf4161
--- /dev/null
+++ b/Src/Zle/compctl.c
@@ -0,0 +1,1085 @@
+/*
+ * compctl.c - the compctl builtin
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "compctl.mdh"
+#include "compctl.pro"
+
+#define COMP_LIST (1<<0) /* -L */
+#define COMP_COMMAND (1<<1) /* -C */
+#define COMP_DEFAULT (1<<2) /* -D */
+#define COMP_FIRST (1<<3) /* -T */
+#define COMP_REMOVE (1<<4)
+
+#define COMP_SPECIAL (COMP_COMMAND|COMP_DEFAULT|COMP_FIRST)
+
+/* Flag for listing, command, default, or first completion */
+static int cclist;
+
+/* Mask for determining what to print */
+static unsigned long showmask = 0;
+
+/* Parse the basic flags for `compctl' */
+
+/**/
+static int
+get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
+{
+ /* Parse the basic flags for completion:
+ * first is a flag that we are not in extended completion,
+ * while hx indicates or (+) completion (need to know for
+ * default and command completion as the initial compctl is special).
+ * cct is a temporary just to hold flags; it never needs freeing.
+ */
+ struct compctl cct;
+ char **argv = *av;
+ int ready = 0, hx = 0;
+
+ /* Handle `compctl + foo ...' specially: turn it into
+ * a default compctl by removing it from the hash table.
+ */
+ if (first && argv[0][0] == '+' && !argv[0][1] &&
+ !(argv[1] && argv[1][0] == '-' && argv[1][1])) {
+ argv++;
+ if(argv[0] && argv[0][0] == '-')
+ argv++;
+ *av = argv;
+ freecompctl(cc);
+ cclist = COMP_REMOVE;
+ return 0;
+ }
+
+ memset((void *)&cct, 0, sizeof(cct));
+
+ /* Loop through the flags until we have no more: *
+ * those with arguments are not properly allocated yet, *
+ * we just hang on to the argument that was passed. */
+ for (; !ready && argv[0] && argv[0][0] == '-' && (argv[0][1] || !first);) {
+ if (!argv[0][1])
+ *argv = "-+";
+ while (!ready && *++(*argv)) {
+ if(**argv == Meta)
+ *++*argv ^= 32;
+ switch (**argv) {
+ case 'f':
+ cct.mask |= CC_FILES;
+ break;
+ case 'c':
+ cct.mask |= CC_COMMPATH;
+ break;
+ case 'm':
+ cct.mask |= CC_EXTCMDS;
+ break;
+ case 'w':
+ cct.mask |= CC_RESWDS;
+ break;
+ case 'o':
+ cct.mask |= CC_OPTIONS;
+ break;
+ case 'v':
+ cct.mask |= CC_VARS;
+ break;
+ case 'b':
+ cct.mask |= CC_BINDINGS;
+ break;
+ case 'A':
+ cct.mask |= CC_ARRAYS;
+ break;
+ case 'I':
+ cct.mask |= CC_INTVARS;
+ break;
+ case 'F':
+ cct.mask |= CC_SHFUNCS;
+ break;
+ case 'p':
+ cct.mask |= CC_PARAMS;
+ break;
+ case 'E':
+ cct.mask |= CC_ENVVARS;
+ break;
+ case 'j':
+ cct.mask |= CC_JOBS;
+ break;
+ case 'r':
+ cct.mask |= CC_RUNNING;
+ break;
+ case 'z':
+ cct.mask |= CC_STOPPED;
+ break;
+ case 'B':
+ cct.mask |= CC_BUILTINS;
+ break;
+ case 'a':
+ cct.mask |= CC_ALREG | CC_ALGLOB;
+ break;
+ case 'R':
+ cct.mask |= CC_ALREG;
+ break;
+ case 'G':
+ cct.mask |= CC_ALGLOB;
+ break;
+ case 'u':
+ cct.mask |= CC_USERS;
+ break;
+ case 'd':
+ cct.mask |= CC_DISCMDS;
+ break;
+ case 'e':
+ cct.mask |= CC_EXCMDS;
+ break;
+ case 'N':
+ cct.mask |= CC_SCALARS;
+ break;
+ case 'O':
+ cct.mask |= CC_READONLYS;
+ break;
+ case 'Z':
+ cct.mask |= CC_SPECIALS;
+ break;
+ case 'q':
+ cct.mask |= CC_REMOVE;
+ break;
+ case 'U':
+ cct.mask |= CC_DELETE;
+ break;
+ case 'n':
+ cct.mask |= CC_NAMED;
+ break;
+ case 'Q':
+ cct.mask |= CC_QUOTEFLAG;
+ break;
+ case '/':
+ cct.mask |= CC_DIRS;
+ break;
+ case 'k':
+ if ((*argv)[1]) {
+ cct.keyvar = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "variable name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.keyvar = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'K':
+ if ((*argv)[1]) {
+ cct.func = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "function name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.func = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'Y':
+ cct.mask |= CC_EXPANDEXPL;
+ goto expl;
+ case 'X':
+ cct.mask &= ~CC_EXPANDEXPL;
+ expl:
+ if ((*argv)[1]) {
+ cct.explain = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.explain = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'y':
+ if ((*argv)[1]) {
+ cct.ylist = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "function/variable expected after -%c",
+ NULL, **argv);
+ } else {
+ cct.ylist = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'P':
+ if ((*argv)[1]) {
+ cct.prefix = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.prefix = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'S':
+ if ((*argv)[1]) {
+ cct.suffix = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.suffix = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'g':
+ if ((*argv)[1]) {
+ cct.glob = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "glob pattern expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.glob = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 's':
+ if ((*argv)[1]) {
+ cct.str = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "command string expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.str = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'l':
+ if ((*argv)[1]) {
+ cct.subcmd = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "command name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.subcmd = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'W':
+ if ((*argv)[1]) {
+ cct.withd = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "path expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.withd = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'H':
+ if ((*argv)[1])
+ cct.hnum = atoi((*argv) + 1);
+ else if (argv[1])
+ cct.hnum = atoi(*++argv);
+ else {
+ zwarnnam(name, "number expected after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ if (!argv[1]) {
+ zwarnnam(name, "missing pattern after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ cct.hpat = *++argv;
+ if (cct.hnum < 1)
+ cct.hnum = 0;
+ if (*cct.hpat == '*' && !cct.hpat[1])
+ cct.hpat = "";
+ *argv = "" - 1;
+ break;
+ case 'C':
+ if (first && !hx) {
+ cclist |= COMP_COMMAND;
+ } else {
+ zwarnnam(name, "misplaced command completion (-C) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'D':
+ if (first && !hx) {
+ isdef = 1;
+ cclist |= COMP_DEFAULT;
+ } else {
+ zwarnnam(name, "misplaced default completion (-D) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'T':
+ if (first && !hx) {
+ cclist |= COMP_FIRST;
+ } else {
+ zwarnnam(name, "misplaced first completion (-T) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'L':
+ if (!first || hx) {
+ zwarnnam(name, "illegal use of -L flag", NULL, 0);
+ return 1;
+ }
+ cclist |= COMP_LIST;
+ break;
+ case 'x':
+ if (!argv[1]) {
+ zwarnnam(name, "condition expected after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ if (first) {
+ argv++;
+ if (get_xcompctl(name, &argv, &cct, isdef)) {
+ if (cct.ext)
+ freecompctl(cct.ext);
+ return 1;
+ }
+ ready = 2;
+ } else {
+ zwarnnam(name, "recursive extended completion not allowed",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ default:
+ if (!first && (**argv == '-' || **argv == '+'))
+ (*argv)--, argv--, ready = 1;
+ else {
+ zwarnnam(name, "bad option: -%c", NULL, **argv);
+ return 1;
+ }
+ }
+ }
+
+ if (*++argv && (!ready || ready == 2) &&
+ **argv == '+' && !argv[0][1]) {
+ /* There's an alternative (+) completion: assign
+ * what we have so far before moving on to that.
+ */
+ if (cc_assign(name, &cc, &cct, first && !hx))
+ return 1;
+
+ hx = 1;
+ ready = 0;
+
+ if (!*++argv || **argv != '-' ||
+ (**argv == '-' && (!argv[0][1] ||
+ (argv[0][1] == '-' && !argv[0][2])))) {
+ /* No argument to +, which means do default completion */
+ if (isdef)
+ zwarnnam(name,
+ "recursive xor'd default completions not allowed",
+ NULL, 0);
+ else
+ cc->xor = &cc_default;
+ } else {
+ /* more flags follow: prepare to loop again */
+ cc->xor = (Compctl) zcalloc(sizeof(*cc));
+ cc = cc->xor;
+ memset((void *)&cct, 0, sizeof(cct));
+ }
+ }
+ }
+ if (!ready && *argv && **argv == '-')
+ argv++;
+
+ if (! (cct.mask & (CC_EXCMDS | CC_DISCMDS)))
+ cct.mask |= CC_EXCMDS;
+
+ /* assign the last set of flags we parsed */
+ if (cc_assign(name, &cc, &cct, first && !hx))
+ return 1;
+
+ *av = argv;
+
+ return 0;
+}
+
+/* Handle the -x ... -- part of compctl. */
+
+/**/
+static int
+get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
+{
+ char **argv = *av, *t, *tt, sav;
+ int n, l = 0, ready = 0;
+ Compcond m, c, o;
+ Compctl *next = &(cc->ext);
+
+ while (!ready) {
+ /* o keeps track of or's, m remembers the starting condition,
+ * c is the current condition being parsed
+ */
+ o = m = c = (Compcond) zcalloc(sizeof(*c));
+ /* Loop over each condition: something like 's[...][...], p[...]' */
+ for (t = *argv; *t;) {
+ while (*t == ' ')
+ t++;
+ /* First get the condition code */
+ switch (*t) {
+ case 's':
+ c->type = CCT_CURSUF;
+ break;
+ case 'S':
+ c->type = CCT_CURPRE;
+ break;
+ case 'p':
+ c->type = CCT_POS;
+ break;
+ case 'c':
+ c->type = CCT_CURSTR;
+ break;
+ case 'C':
+ c->type = CCT_CURPAT;
+ break;
+ case 'w':
+ c->type = CCT_WORDSTR;
+ break;
+ case 'W':
+ c->type = CCT_WORDPAT;
+ break;
+ case 'n':
+ c->type = CCT_CURSUB;
+ break;
+ case 'N':
+ c->type = CCT_CURSUBC;
+ break;
+ case 'm':
+ c->type = CCT_NUMWORDS;
+ break;
+ case 'r':
+ c->type = CCT_RANGESTR;
+ break;
+ case 'R':
+ c->type = CCT_RANGEPAT;
+ break;
+ default:
+ t[1] = '\0';
+ zwarnnam(name, "unknown condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ /* Now get the arguments in square brackets */
+ if (t[1] != '[') {
+ t[1] = '\0';
+ zwarnnam(name, "expected condition after condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ t++;
+ /* First count how many or'd arguments there are,
+ * marking the active ]'s and ,'s with unprintable characters.
+ */
+ for (n = 0, tt = t; *tt == '['; n++) {
+ for (l = 1, tt++; *tt && l; tt++)
+ if (*tt == '\\' && tt[1])
+ tt++;
+ else if (*tt == '[')
+ l++;
+ else if (*tt == ']')
+ l--;
+ else if (l == 1 && *tt == ',')
+ *tt = '\201';
+ if (tt[-1] == ']')
+ tt[-1] = '\200';
+ }
+
+ if (l) {
+ t[1] = '\0';
+ zwarnnam(name, "error after condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ c->n = n;
+
+ /* Allocate space for all the arguments of the conditions */
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ c->u.r.a = (int *)zcalloc(n * sizeof(int));
+ c->u.r.b = (int *)zcalloc(n * sizeof(int));
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE)
+ c->u.s.s = (char **)zcalloc(n * sizeof(char *));
+
+ else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ c->u.l.a = (char **)zcalloc(n * sizeof(char *));
+ c->u.l.b = (char **)zcalloc(n * sizeof(char *));
+ } else {
+ c->u.s.p = (int *)zcalloc(n * sizeof(int));
+ c->u.s.s = (char **)zcalloc(n * sizeof(char *));
+ }
+ /* Now loop over the actual arguments */
+ for (l = 0; *t == '['; l++, t++) {
+ for (t++; *t && *t == ' '; t++);
+ tt = t;
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ /* p[...] or m[...]: one or two numbers expected */
+ for (; *t && *t != '\201' && *t != '\200'; t++);
+ if (!(sav = *t)) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.r.a[l] = atoi(tt);
+ /* Second argument is optional: see if it's there */
+ if (sav == '\200')
+ /* no: copy first argument */
+ c->u.r.b[l] = c->u.r.a[l];
+ else {
+ tt = ++t;
+ for (; *t && *t != '\200'; t++);
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.r.b[l] = atoi(tt);
+ }
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE) {
+ /* -s[..] or -S[..]: single string expected */
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.s[l] = ztrdup(tt);
+ } else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ /* -r[..,..] or -R[..,..]: two strings expected */
+ for (; *t && *t != '\201'; t++);
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.l.a[l] = ztrdup(tt);
+ tt = ++t;
+ /* any more commas are text, not active */
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.l.b[l] = ztrdup(tt);
+ } else {
+ /* remaining patterns are number followed by string */
+ for (; *t && *t != '\200' && *t != '\201'; t++);
+ if (!*t || *t == '\200') {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.p[l] = atoi(tt);
+ tt = ++t;
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.s[l] = ztrdup(tt);
+ }
+ }
+ while (*t == ' ')
+ t++;
+ if (*t == ',') {
+ /* Another condition to `or' */
+ o->or = c = (Compcond) zcalloc(sizeof(*c));
+ o = c;
+ t++;
+ } else if (*t) {
+ /* Another condition to `and' */
+ c->and = (Compcond) zcalloc(sizeof(*c));
+ c = c->and;
+ }
+ }
+ /* Assign condition to current compctl */
+ *next = (Compctl) zcalloc(sizeof(*cc));
+ (*next)->cond = m;
+ argv++;
+ /* End of the condition; get the flags that go with it. */
+ if (get_compctl(name, &argv, *next, 0, isdef))
+ return 1;
+ if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
+ /* default, first, or command completion finished */
+ ready = 1;
+ else {
+ /* see if we are looking for more conditions or are
+ * ready to return (ready = 1)
+ */
+ if (!argv || !*argv || **argv != '-' ||
+ ((!argv[0][1] || argv[0][1] == '+') && !argv[1])) {
+ zwarnnam(name, "missing command names", NULL, 0);
+ return 1;
+ }
+ if (!strcmp(*argv, "--"))
+ ready = 1;
+ else if (!strcmp(*argv, "-+") && argv[1] &&
+ !strcmp(argv[1], "--")) {
+ ready = 1;
+ argv++;
+ }
+ argv++;
+ /* prepare to put the next lot of conditions on the end */
+ next = &((*next)->next);
+ }
+ }
+ /* save position at end of parsing */
+ *av = argv - 1;
+ return 0;
+}
+
+/**/
+static int
+cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
+{
+ /* Copy over the details from the values in cct to those in *ccptr */
+ Compctl cc;
+
+ if (cct->subcmd && (cct->keyvar || cct->glob || cct->str ||
+ cct->func || cct->explain || cct->ylist ||
+ cct->prefix)) {
+ zwarnnam(name, "illegal combination of options", NULL, 0);
+ return 1;
+ }
+
+ /* Handle assignment of new default or command completion */
+ if (reass && !(cclist & COMP_LIST)) {
+ /* if not listing */
+ if (cclist == (COMP_COMMAND|COMP_DEFAULT)
+ || cclist == (COMP_COMMAND|COMP_FIRST)
+ || cclist == (COMP_DEFAULT|COMP_FIRST)
+ || cclist == COMP_SPECIAL) {
+ zwarnnam(name, "can't set -D, -T, and -C simultaneously", NULL, 0);
+ /* ... because the following code wouldn't work. */
+ return 1;
+ }
+ if (cclist & COMP_COMMAND) {
+ /* command */
+ *ccptr = &cc_compos;
+ cc_reassign(*ccptr);
+ } else if (cclist & COMP_DEFAULT) {
+ /* default */
+ *ccptr = &cc_default;
+ cc_reassign(*ccptr);
+ } else if (cclist & COMP_FIRST) {
+ /* first */
+ *ccptr = &cc_first;
+ cc_reassign(*ccptr);
+ }
+ }
+
+ /* Free the old compctl */
+ cc = *ccptr;
+ zsfree(cc->keyvar);
+ zsfree(cc->glob);
+ zsfree(cc->str);
+ zsfree(cc->func);
+ zsfree(cc->explain);
+ zsfree(cc->ylist);
+ zsfree(cc->prefix);
+ zsfree(cc->suffix);
+ zsfree(cc->subcmd);
+ zsfree(cc->withd);
+ zsfree(cc->hpat);
+
+ /* and copy over the new stuff, (permanently) allocating
+ * space for strings.
+ */
+ cc->mask = cct->mask;
+ cc->keyvar = ztrdup(cct->keyvar);
+ cc->glob = ztrdup(cct->glob);
+ cc->str = ztrdup(cct->str);
+ cc->func = ztrdup(cct->func);
+ cc->explain = ztrdup(cct->explain);
+ cc->ylist = ztrdup(cct->ylist);
+ cc->prefix = ztrdup(cct->prefix);
+ cc->suffix = ztrdup(cct->suffix);
+ cc->subcmd = ztrdup(cct->subcmd);
+ cc->withd = ztrdup(cct->withd);
+ cc->hpat = ztrdup(cct->hpat);
+ cc->hnum = cct->hnum;
+
+ /* careful with extended completion: it's already allocated */
+ cc->ext = cct->ext;
+
+ return 0;
+}
+
+/**/
+static void
+cc_reassign(Compctl cc)
+{
+ /* Free up a new default or command completion:
+ * this is a hack to free up the parts which should be deleted,
+ * without removing the basic variable which is statically allocated
+ */
+ Compctl c2;
+
+ c2 = (Compctl) zcalloc(sizeof *cc);
+ c2->xor = cc->xor;
+ c2->ext = cc->ext;
+ c2->refc = 1;
+
+ freecompctl(c2);
+
+ cc->ext = cc->xor = NULL;
+}
+
+/**/
+static void
+compctl_process_cc(char **s, Compctl cc)
+{
+ Compctlp ccp;
+
+ if (cclist & COMP_REMOVE) {
+ /* Delete entries for the commands listed */
+ for (; *s; s++) {
+ if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s)))
+ compctltab->freenode((HashNode) ccp);
+ }
+ } else {
+ /* Add the compctl just read to the hash table */
+
+ cc->refc = 0;
+ for (; *s; s++) {
+ cc->refc++;
+ ccp = (Compctlp) zalloc(sizeof *ccp);
+ ccp->cc = cc;
+ compctltab->addnode(compctltab, ztrdup(*s), ccp);
+ }
+ }
+}
+
+/* Print a `compctl' */
+
+/**/
+static void
+printcompctl(char *s, Compctl cc, int printflags)
+{
+ Compctl cc2;
+ char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
+ char *mss = " pcCwWsSnNmrR";
+ unsigned long t = 0x7fffffff;
+ unsigned long flags = cc->mask;
+ unsigned long oldshowmask;
+
+ if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
+ flags &= ~CC_EXCMDS;
+
+ /* If showmask is non-zero, then print only those *
+ * commands with that flag set. */
+ if (showmask && !(flags & showmask))
+ return;
+
+ /* Temporarily clear showmask in case we make *
+ * recursive calls to printcompctl. */
+ oldshowmask = showmask;
+ showmask = 0;
+
+ /* print either command name or start of compctl command itself */
+ if (s) {
+ if (cclist & COMP_LIST) {
+ printf("compctl");
+ if (cc == &cc_compos)
+ printf(" -C");
+ if (cc == &cc_default)
+ printf(" -D");
+ if (cc == &cc_first)
+ printf(" -T");
+ } else
+ quotedzputs(s, stdout);
+ }
+
+ /* loop through flags w/o args that are set, printing them if so */
+ if (flags & t) {
+ printf(" -");
+ if ((flags & (CC_ALREG | CC_ALGLOB)) == (CC_ALREG | CC_ALGLOB))
+ putchar('a'), flags &= ~(CC_ALREG | CC_ALGLOB);
+ while (*css) {
+ if (flags & t & 1)
+ putchar(*css);
+ css++;
+ flags >>= 1;
+ t >>= 1;
+ }
+ }
+
+ /* now flags with arguments */
+ flags = cc->mask;
+ printif(cc->keyvar, 'k');
+ printif(cc->func, 'K');
+ printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
+ printif(cc->ylist, 'y');
+ printif(cc->prefix, 'P');
+ printif(cc->suffix, 'S');
+ printif(cc->glob, 'g');
+ printif(cc->str, 's');
+ printif(cc->subcmd, 'l');
+ printif(cc->withd, 'W');
+ if (cc->hpat) {
+ printf(" -H %d ", cc->hnum);
+ quotedzputs(cc->hpat, stdout);
+ }
+
+ /* now the -x ... -- extended completion part */
+ if (cc->ext) {
+ Compcond c, o;
+ int i;
+
+ cc2 = cc->ext;
+ printf(" -x");
+
+ while (cc2) {
+ /* loop over conditions */
+ c = cc2->cond;
+
+ printf(" '");
+ for (c = cc2->cond; c;) {
+ /* loop over or's */
+ o = c->or;
+ while (c) {
+ /* loop over and's */
+ putchar(mss[c->type]);
+
+ for (i = 0; i < c->n; i++) {
+ /* for all [...]'s of a given condition */
+ putchar('[');
+ switch (c->type) {
+ case CCT_POS:
+ case CCT_NUMWORDS:
+ printf("%d,%d", c->u.r.a[i], c->u.r.b[i]);
+ break;
+ case CCT_CURSUF:
+ case CCT_CURPRE:
+ printqt(c->u.s.s[i]);
+ break;
+ case CCT_RANGESTR:
+ case CCT_RANGEPAT:
+ printqt(c->u.l.a[i]);
+ putchar(',');
+ printqt(c->u.l.b[i]);
+ break;
+ default:
+ printf("%d,", c->u.s.p[i]);
+ printqt(c->u.s.s[i]);
+ }
+ putchar(']');
+ }
+ if ((c = c->and))
+ putchar(' ');
+ }
+ if ((c = o))
+ printf(" , ");
+ }
+ putchar('\'');
+ c = cc2->cond;
+ cc2->cond = NULL;
+ /* now print the flags for the current condition */
+ printcompctl(NULL, cc2, 0);
+ cc2->cond = c;
+ if ((cc2 = (Compctl) (cc2->next)))
+ printf(" -");
+ }
+ if (cclist & COMP_LIST)
+ printf(" --");
+ }
+ if (cc && cc->xor) {
+ /* print xor'd (+) completions */
+ printf(" +");
+ if (cc->xor != &cc_default)
+ printcompctl(NULL, cc->xor, 0);
+ }
+ if (s) {
+ if ((cclist & COMP_LIST) && (cc != &cc_compos)
+ && (cc != &cc_default) && (cc != &cc_first)) {
+ if(s[0] == '-' || s[0] == '+')
+ printf(" -");
+ putchar(' ');
+ quotedzputs(s, stdout);
+ }
+ putchar('\n');
+ }
+
+ showmask = oldshowmask;
+}
+
+/**/
+static void
+printcompctlp(HashNode hn, int printflags)
+{
+ Compctlp ccp = (Compctlp) hn;
+
+ /* Function needed for use by scanhashtable() */
+ printcompctl(ccp->nam, ccp->cc, printflags);
+}
+
+/* Main entry point for the `compctl' builtin */
+
+/**/
+static int
+bin_compctl(char *name, char **argv, char *ops, int func)
+{
+ Compctl cc = NULL;
+ int ret = 0;
+
+ /* clear static flags */
+ cclist = 0;
+ showmask = 0;
+
+ /* Parse all the arguments */
+ if (*argv) {
+ cc = (Compctl) zcalloc(sizeof(*cc));
+ if (get_compctl(name, &argv, cc, 1, 0)) {
+ freecompctl(cc);
+ return 1;
+ }
+
+ /* remember flags for printing */
+ showmask = cc->mask;
+ if ((showmask & CC_EXCMDS) && !(showmask & CC_DISCMDS))
+ showmask &= ~CC_EXCMDS;
+
+ /* if no command arguments or just listing, we don't want cc */
+ if (!*argv || (cclist & COMP_LIST))
+ freecompctl(cc);
+ }
+
+ /* If no commands and no -C, -T, or -D, print all the compctl's *
+ * If some flags (other than -C, -T, or -D) were given, then *
+ * only print compctl containing those flags. */
+ if (!*argv && !(cclist & COMP_SPECIAL)) {
+ scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0);
+ return ret;
+ }
+
+ /* If we're listing and we've made it to here, then there are arguments *
+ * or a COMP_SPECIAL flag (-D, -C, -T), so print only those. */
+ if (cclist & COMP_LIST) {
+ HashNode hn;
+ char **ptr;
+
+ showmask = 0;
+ for (ptr = argv; *ptr; ptr++) {
+ if ((hn = compctltab->getnode(compctltab, *ptr))) {
+ compctltab->printnode(hn, 0);
+ } else {
+ zwarnnam(name, "no compctl defined for %s", *ptr, 0);
+ ret = 1;
+ }
+ }
+ if (cclist & COMP_COMMAND)
+ printcompctl("", &cc_compos, 0);
+ if (cclist & COMP_DEFAULT)
+ printcompctl("", &cc_default, 0);
+ if (cclist & COMP_FIRST)
+ printcompctl("", &cc_first, 0);
+ return ret;
+ }
+
+ /* Assign the compctl to the commands given */
+ if (*argv) {
+ if(cclist & COMP_SPECIAL)
+ /* Ideally we'd handle this properly, setting both the *
+ * special and normal completions. For the moment, *
+ * this is better than silently failing. */
+ zwarnnam(name, "extraneous commands ignored", NULL, 0);
+ else
+ compctl_process_cc(argv, cc);
+ }
+
+ return ret;
+}
+
+static struct builtin bintab[] = {
+ BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
+};
+
+/**/
+int
+boot_compctl(Module m)
+{
+ if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+ return 1;
+ compctltab->printnode = printcompctlp;
+ return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_compctl(Module m)
+{
+ compctltab->printnode = NULL;
+ deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+ return 0;
+}
+#endif