diff options
Diffstat (limited to 'Src/Zle/computil.c')
-rw-r--r-- | Src/Zle/computil.c | 1498 |
1 files changed, 1119 insertions, 379 deletions
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index aed3d9808..a844ee1ef 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -33,22 +33,32 @@ /* Help for `_display'. */ +/* Calculation state. */ + typedef struct cdisp *Cdisp; struct cdisp { - int pre, suf, colon; + int pre; /* prefix length */ + int suf; /* suffix length */ + int colon; /* number of strings with descriptions */ }; +/* Calculate longest prefix and suffix and count the strings with + * descriptions. */ + static void cdisp_calc(Cdisp disp, char **args) { char *cp; - int i; + int i, nbc; for (; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { + for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++, nbc++; + if (*cp == ':' && cp[1]) { disp->colon++; - if ((i = cp - *args) > disp->pre) + if ((i = cp - *args - nbc) > disp->pre) disp->pre = i; if ((i = strlen(cp + 1)) > disp->suf) disp->suf = i; @@ -56,78 +66,29 @@ cdisp_calc(Cdisp disp, char **args) } } -static char ** -cdisp_build(Cdisp disp, char *sep, char **args) -{ - int sl = strlen(sep), pre = disp->pre, suf; - VARARR(char, buf, disp->pre + disp->suf + sl + 1); - char **ret, **rp, *cp; - - ret = (char **) zalloc((arrlen(args) + 1) * sizeof(char *)); - - memcpy(buf + pre, sep, sl); - suf = pre + sl; - - for (rp = ret; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { - memset(buf, ' ', pre); - memcpy(buf, *args, (cp - *args)); - strcpy(buf + suf, cp + 1); - *rp++ = ztrdup(buf); - } else { - if (cp) - *cp = '\0'; - *rp++ = ztrdup(*args); - if (cp) - *cp = ':'; - } - } - *rp = NULL; - - return ret; -} - -/**/ -static int -bin_compdisplay(char *nam, char **args, char *ops, int func) -{ - struct cdisp disp; - - if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); - return 1; - } - disp.pre = disp.suf = disp.colon = 0; - - cdisp_calc(&disp, args + 2); - setaparam(args[0], cdisp_build(&disp, args[1], args + 2)); - - return !disp.colon; -} - /* Help fuer `_describe'. */ typedef struct cdset *Cdset; struct cdstate { - int showd; - char *sep; - Cdset sets; - struct cdisp disp; + int showd; /* != 0 if descriptions should be shown */ + char *sep; /* the separator string */ + Cdset sets; /* the sets of matches */ + struct cdisp disp; /* used to calculate the alignment */ }; struct cdset { - Cdset next; - char **opts; - char **strs; - char **matches; + Cdset next; /* guess what */ + char **opts; /* the compadd-options */ + char **strs; /* the display-strings */ + char **matches; /* the matches (or NULL) */ }; static struct cdstate cd_state; static int cd_parsed = 0; static void -free_cdsets(Cdset p) +freecdsets(Cdset p) { Cdset n; @@ -143,6 +104,8 @@ free_cdsets(Cdset p) } } +/* Initialisation. Store and calculate the string and matches and so on. */ + static int cd_init(char *nam, char *sep, char **args, int disp) { @@ -151,7 +114,7 @@ cd_init(char *nam, char *sep, char **args, int disp) if (cd_parsed) { zsfree(cd_state.sep); - free_cdsets(cd_state.sets); + freecdsets(cd_state.sets); } setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); @@ -164,24 +127,20 @@ cd_init(char *nam, char *sep, char **args, int disp) setp = &(set->next); if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->strs = arrdup(ap); - } LASTALLOC; + set->strs = zarrdup(ap); if (disp) cdisp_calc(&(cd_state.disp), set->strs); if (*++args && **args != '-') { if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->matches = arrdup(ap); - } LASTALLOC; + set->matches = zarrdup(ap); args++; } for (ap = args; *args && @@ -190,15 +149,15 @@ cd_init(char *nam, char *sep, char **args, int disp) tmp = *args; *args = NULL; - PERMALLOC { - set->opts = arrdup(ap); - } LASTALLOC; + set->opts = zarrdup(ap); if ((*args = tmp)) args++; } return 0; } +/* Get the next set. */ + static int cd_get(char **params) { @@ -206,15 +165,21 @@ cd_get(char **params) if ((set = cd_state.sets)) { char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; - char **p, **mp, *cp; + char **p, **mp, *cp, *copy, *cpp, oldc; int dl = 1, sl = 1, sepl = strlen(cd_state.sep); int pre = cd_state.disp.pre, suf = cd_state.disp.suf; VARARR(char, buf, pre + suf + sepl + 1); for (p = set->strs; *p; p++) - if (cd_state.showd && (cp = strchr(*p, ':')) && cp[1]) - dl++; - else + if (cd_state.showd) { + for (cp = *p; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++; + if (*cp == ':' && cp[1]) + dl++; + else + sl++; + } else sl++; sd = (char **) zalloc(dl * sizeof(char *)); @@ -226,41 +191,44 @@ cd_get(char **params) memcpy(buf + pre, cd_state.sep, sepl); suf = pre + sepl; } + + /* Build the aligned display strings. */ + for (sdp = sd, ssp = ss, mdp = md, msp = ms, p = set->strs, mp = set->matches; *p; p++) { - if ((cp = strchr(*p, ':')) && cp[1] && cd_state.showd) { + copy = dupstring(*p); + for (cp = cpp = copy; *cp && *cp != ':'; cp++) { + if (*cp == '\\' && cp[1]) + cp++; + *cpp++ = *cp; + } + oldc = *cpp; + *cpp = '\0'; + if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] && + cd_state.showd) { memset(buf, ' ', pre); - memcpy(buf, *p, (cp - *p)); + memcpy(buf, copy, (cpp - copy)); strcpy(buf + suf, cp + 1); *sdp++ = ztrdup(buf); if (mp) { *mdp++ = ztrdup(*mp); if (*mp) mp++; - } else { - *cp = '\0'; - *mdp++ = ztrdup(*p); - *cp = ':'; - } + } else + *mdp++ = ztrdup(copy); } else { - if (cp) - *cp = '\0'; - *ssp++ = ztrdup(*p); + *ssp++ = ztrdup(copy); if (mp) { *msp++ = ztrdup(*mp); if (*mp) mp++; } else - *msp++ = ztrdup(*p); - if (cp) - *cp = ':'; + *msp++ = ztrdup(copy); } } *sdp = *ssp = *mdp = *msp = NULL; - PERMALLOC { - p = arrdup(set->opts); - } LASTALLOC; + p = zarrdup(set->opts); setaparam(params[0], p); setaparam(params[1], sd); @@ -270,7 +238,7 @@ cd_get(char **params) cd_state.sets = set->next; set->next = NULL; - free_cdsets(set); + freecdsets(set); return 0; } @@ -282,34 +250,36 @@ static int bin_compdescribe(char *nam, char **args, char *ops, int func) { if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (!args[0][0] || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } switch (args[0][1]) { case 'i': + cd_parsed = 1; + return cd_init(nam, "", args + 1, 0); case 'I': cd_parsed = 1; - return cd_init(nam, args[1], args + 2, (args[0][1] == 'I')); + return cd_init(nam, args[1], args + 2, 1); case 'g': if (cd_parsed) { int n = arrlen(args); if (n != 6) { - zerrnam(nam, (n < 6 ? "not enough arguments" : + zwarnnam(nam, (n < 6 ? "not enough arguments" : "too many arguments"), NULL, 0); return 1; } return cd_get(args + 1); } else { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } } - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } @@ -319,29 +289,34 @@ typedef struct cadef *Cadef; typedef struct caopt *Caopt; typedef struct caarg *Caarg; +/* Cache for a set of _arguments-definitions. */ + struct cadef { - Cadef next; - Caopt opts; - int nopts, ndopts, nodopts; - Caarg args; - Caarg rest; - char **defs; - int ndefs; - int lastt; - Caopt *single; - char *match; - int argsactive; + Cadef next; /* next in cache */ + Caopt opts; /* the options */ + int nopts, ndopts, nodopts; /* number of options/direct/optional direct */ + Caarg args; /* the normal arguments */ + Caarg rest; /* the rest-argument */ + char **defs; /* the original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time this was used */ + Caopt *single; /* array of single-letter options */ + char *match; /* -M spec to use */ + int argsactive; /* if arguments are still allowed */ + /* used while parsing a command line */ }; +/* Description for an option. */ + struct caopt { Caopt next; - char *name; - char *descr; - char **xor; - int type; - Caarg args; - int active; - int num; + char *name; /* option name */ + char *descr; /* the description */ + char **xor; /* if this, then not ... */ + int type; /* type, CAO_* */ + Caarg args; /* option arguments */ + int active; /* still allowed on command line */ + int num; /* it's the num'th option */ }; #define CAO_NEXT 1 @@ -349,13 +324,18 @@ struct caopt { #define CAO_ODIRECT 3 #define CAO_EQUAL 4 +/* Description for an argument */ + struct caarg { Caarg next; - char *descr; - char *action; - int type; - char *end; - int num; + char *descr; /* description */ + char **xor; /* if this, then not ... */ + char *action; /* what to do for it */ + int type; /* CAA_* below */ + char *end; /* end-pattern for ::<pat>:... */ + char *opt; /* option name if for an option */ + int num; /* it's the num'th argument */ + int active; /* still allowed on command line */ }; #define CAA_NORMAL 1 @@ -364,9 +344,13 @@ struct caarg { #define CAA_RARGS 4 #define CAA_RREST 5 +/* The cache of parsed descriptons. */ + #define MAX_CACACHE 8 static Cadef cadef_cache[MAX_CACACHE]; +/* Compare two arrays of strings for equality. */ + static int arrcmp(char **a, char **b) { @@ -383,45 +367,54 @@ arrcmp(char **a, char **b) } } +/* Memory stuff. Obviously. */ + static void -free_caargs(Caarg a) +freecaargs(Caarg a) { Caarg n; for (; a; a = n) { n = a->next; zsfree(a->descr); + if (a->xor) + freearray(a->xor); zsfree(a->action); zsfree(a->end); + zsfree(a->opt); zfree(a, sizeof(*a)); } } static void -free_cadef(Cadef d) +freecadef(Cadef d) { if (d) { Caopt p, n; zsfree(d->match); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->opts; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->args); + if (p->xor) + freearray(p->xor); + freecaargs(p->args); zfree(p, sizeof(*p)); } - free_caargs(d->args); - free_caargs(d->rest); + freecaargs(d->args); + freecaargs(d->rest); if (d->single) zfree(d->single, 256 * sizeof(Caopt)); zfree(d, sizeof(*d)); } } +/* Remove backslashes before colons. */ + static char * rembslashcolon(char *s) { @@ -439,16 +432,41 @@ rembslashcolon(char *s) return r; } +/* Add backslashes before colons. */ + +static char * +bslashcolon(char *s) +{ + char *p, *r; + + r = p = zhalloc((2 * strlen(s)) + 1); + + while (*s) { + if (*s == ':') + *p++ = '\\'; + *p++ = *s++; + } + *p = '\0'; + + return r; +} + +/* Parse an argument definition. */ + static Caarg -parse_caarg(int mult, int type, int num, char **def) +parse_caarg(int mult, int type, int num, char *oname, char **def) { Caarg ret = (Caarg) zalloc(sizeof(*ret)); char *p = *def, *d, sav; ret->next = NULL; ret->descr = ret->action = ret->end = NULL; + ret->xor = NULL; ret->num = num; ret->type = type; + ret->opt = ztrdup(oname); + + /* Get the description. */ for (d = p; *p && *p != ':'; p++) if (*p == '\\' && p[1]) @@ -456,6 +474,9 @@ parse_caarg(int mult, int type, int num, char **def) sav = *p; *p = '\0'; ret->descr = ztrdup(rembslashcolon(d)); + + /* Get the action if there is one. */ + if (sav) { if (mult) { for (d = ++p; *p && *p != ':'; p++) @@ -474,6 +495,8 @@ parse_caarg(int mult, int type, int num, char **def) return ret; } +/* Parse an array of definitions. */ + static Cadef parse_cadef(char *nam, char **args) { @@ -485,6 +508,8 @@ parse_cadef(char *nam, char **args) nopts = ndopts = nodopts = 0; + /* First string is the auto-description definition. */ + for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); if (*p) { @@ -495,6 +520,8 @@ parse_cadef(char *nam, char **args) } else adpre = adsuf = NULL; + /* Now get the -s and -M options. */ + args++; while ((p = *args)) { if (!strcmp(p, "-s")) @@ -515,26 +542,30 @@ parse_cadef(char *nam, char **args) if (!*args) return NULL; - PERMALLOC { - ret = (Cadef) zalloc(sizeof(*ret)); - ret->next = NULL; - ret->opts = NULL; - ret->args = ret->rest = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - if (single) { - ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); - memset(ret->single, 0, 256 * sizeof(Caopt)); - } else - ret->single = NULL; - ret->match = ztrdup(match); - } LASTALLOC; + /* Looks good. Optimistically allocate the cadef structure. */ + + ret = (Cadef) zalloc(sizeof(*ret)); + ret->next = NULL; + ret->opts = NULL; + ret->args = ret->rest = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); + if (single) { + ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); + memset(ret->single, 0, 256 * sizeof(Caopt)); + } else + ret->single = NULL; + ret->match = ztrdup(match); + + /* Get the definitions. */ for (optp = &(ret->opts); *args; args++) { p = dupstring(*args); xnum = 0; if (*p == '(') { + /* There is a xor list, get it. */ + LinkList list = newlinklist(); LinkNode node; char **xp, sav; @@ -555,9 +586,10 @@ parse_cadef(char *nam, char **args) xnum++; *p = sav; } + /* Oops, end-of-string. */ if (*p != ')') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -568,9 +600,10 @@ parse_cadef(char *nam, char **args) p++; } else xor = NULL; - + if (*p == '-' || *p == '+' || (*p == '*' && (p[1] == '-' || p[1] == '+'))) { + /* It's an option. */ Caopt opt; Caarg oargs = NULL; int multi, otype = CAO_NEXT, again = 0; @@ -578,25 +611,39 @@ parse_cadef(char *nam, char **args) rec: + /* Allowed more than once? */ if ((multi = (*p == '*'))) p++; - if ((p[0] == '-' && p[1] == '+') || - (p[0] == '+' && p[1] == '-')) { + if (((p[0] == '-' && p[1] == '+') || + (p[0] == '+' && p[1] == '-')) && + p[2] && p[2] != ':' && p[2] != '[' && + p[2] != '=' && p[2] != '-' && p[2] != '+') { + /* It's a -+ or +- definition. We just execute the whole + * stuff twice for such things. */ name = ++p; *p = (again ? '-' : '+'); again = 1 - again; } else { name = p; + /* If it's a long option skip over the first `-'. */ if (p[0] == '-' && p[1] == '-') p++; } + if (!p[1]) { + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + + /* Skip over the name. */ for (p++; *p && *p != ':' && *p != '[' && ((*p != '-' && *p != '+' && *p != '=') || (p[1] != ':' && p[1] != '[')); p++) if (*p == '\\' && p[1]) p++; + /* The character after the option name specifies the type. */ c = *p; *p = '\0'; if (c == '-') { @@ -609,14 +656,15 @@ parse_cadef(char *nam, char **args) otype = CAO_EQUAL; c = *++p; } + /* Get the optional description, if any. */ if (c == '[') { for (descr = ++p; *p && *p != ']'; p++) if (*p == '\\' && p[1]) p++; if (!*p) { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -625,10 +673,11 @@ parse_cadef(char *nam, char **args) descr = NULL; if (c && c != ':') { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } + /* Add the option name to the xor list if not `*-...'. */ if (!multi) { if (!xor) { xor = (char **) zalloc(2 * sizeof(char *)); @@ -637,27 +686,39 @@ parse_cadef(char *nam, char **args) xor[xnum] = ztrdup(name); } if (c == ':') { + /* There's at least one argument. */ + Caarg *oargp = &oargs; - int atype, rest; + int atype, rest, oanum = 1; char *end; + /* Loop over the arguments. */ + while (c == ':') { rest = 0; end = NULL; + /* Get the argument type. */ if (*++p == ':') { atype = CAA_OPT; p++; } else if (*p == '*') { if (*++p != ':') { - for (end = ++p; *p && *p != ':'; p++) + char sav; + + for (end = p++; *p && *p != ':'; p++) if (*p == '\\' && p[1]) p++; + sav = *p; + *p = '\0'; + end = dupstring(end); + tokenize(end); + *p = sav; } if (*p != ':') { - free_cadef(ret); - free_caargs(oargs); - zerrnam(nam, "invalid option definition: %s", + freecadef(ret); + freecaargs(oargs); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } @@ -672,54 +733,74 @@ parse_cadef(char *nam, char **args) rest = 1; } else atype = CAA_NORMAL; - *oargp = parse_caarg(!rest, atype, 0, &p); + + /* And the definition. */ + + *oargp = parse_caarg(!rest, atype, oanum++, name, &p); + if (end) + (*oargp)->end = ztrdup(end); oargp = &((*oargp)->next); if (rest) break; c = *p; } } - PERMALLOC { - *optp = opt = (Caopt) zalloc(sizeof(*opt)); - optp = &((*optp)->next); - - opt->next = NULL; - opt->name = ztrdup(name); - if (descr) - opt->descr = ztrdup(descr); - else if (adpre && oargs && !oargs->next) + /* Store the option definition. */ + + *optp = opt = (Caopt) zalloc(sizeof(*opt)); + optp = &((*optp)->next); + + opt->next = NULL; + opt->name = ztrdup(rembslashcolon(name)); + if (descr) + opt->descr = ztrdup(descr); + else if (adpre && oargs && !oargs->next) { + char *d; + + for (d = oargs->descr; *d; d++) + if (!iblank(*d)) + break; + + if (*d) opt->descr = tricat(adpre, oargs->descr, adsuf); else opt->descr = NULL; - opt->xor = xor; - opt->type = otype; - opt->args = oargs; - opt->num = nopts++; - } LASTALLOC; + } else + opt->descr = NULL; + opt->xor = xor; + opt->type = otype; + opt->args = oargs; + opt->num = nopts++; if (otype == CAO_DIRECT) ndopts++; else if (otype == CAO_ODIRECT || otype == CAO_EQUAL) nodopts++; + /* If this is for single-letter option we also store a + * pointer for the definition in the array for fast lookup. */ + if (single && name[1] && !name[2]) ret->single[STOUC(name[1])] = opt; if (again) { + /* Do it all again for `*-...'. */ p = dupstring(*args); goto rec; } } else if (*p == '*') { + /* It's a rest-argument definition. */ + int type = CAA_REST; if (*++p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid rest argument definition: %s", *args, 0); return NULL; } if (ret->rest) { - free_cadef(ret); - zerrnam(nam, "doubled rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "doubled rest argument definition: %s", *args, 0); return NULL; } if (*++p == ':') { @@ -729,40 +810,49 @@ parse_cadef(char *nam, char **args) } else type = CAA_RARGS; } - ret->rest = parse_caarg(0, type, -1, &p); + ret->rest = parse_caarg(0, type, -1, NULL, &p); + ret->rest->xor = xor; } else { + /* It's a normal argument definition. */ + int type = CAA_NORMAL; Caarg arg, tmp, pre; if (idigit(*p)) { + /* Argment number is given. */ int num = 0; while (*p && idigit(*p)) - num = (num * 10) + ((int) *p++); + num = (num * 10) + (((int) *p++) - '0'); anum = num + 1; } else + /* Default number. */ anum++; if (*p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } if (*++p == ':') { + /* Optional argument. */ type = CAA_OPT; p++; } - arg = parse_caarg(0, type, anum - 1, &p); + arg = parse_caarg(0, type, anum - 1, NULL, &p); + arg->xor = xor; + + /* Sort the new definition into the existing list. */ for (tmp = ret->args, pre = NULL; tmp && tmp->num < anum - 1; pre = tmp, tmp = tmp->next); if (tmp && tmp->num == anum - 1) { - free_cadef(ret); - free_caargs(arg); - zerrnam(nam, "doubled argument definition: %s", *args, 0); + freecadef(ret); + freecaargs(arg); + zwarnnam(nam, "doubled argument definition: %s", *args, 0); return NULL; } arg->next = tmp; @@ -779,13 +869,16 @@ parse_cadef(char *nam, char **args) return ret; } +/* Given an array of definitions, return the cadef for it. From the cache + * are newly built. */ + static Cadef get_cadef(char *nam, char **args) { Cadef *p, *min, new; int i, na = arrlen(args); - for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i--; p++) + for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i; p++, i--) if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { (*p)->lastt = time(0); @@ -795,26 +888,36 @@ get_cadef(char *nam, char **args) if (i) min = p; if ((new = parse_cadef(nam, args))) { - free_cadef(*min); + freecadef(*min); *min = new; } return new; } +/* Get the option used in a word from the line, if any. */ + static Caopt ca_get_opt(Cadef d, char *line, int full, char **end) { Caopt p; - if (full) { - for (p = d->opts; p; p = p->next) - if (p->active && !strcmp(p->name, line)) - return p; - } else { + /* The full string may be an option. */ + + for (p = d->opts; p; p = p->next) + if (p->active && !strcmp(p->name, line)) { + if (end) + *end = line + strlen(line); + + return p; + } + + if (!full) { + /* The string from the line probably only begins with an option. */ for (p = d->opts; p; p = p->next) - if (p->active && p->args && p->type != CAO_NEXT && - strpfx(p->name, line)) { + if (p->active && ((!p->args || p->type == CAO_NEXT) ? + !strcmp(p->name, line) : strpfx(p->name, line))) { if (end) { + /* Return a pointer to the end of the option. */ int l = strlen(p->name); if (p->type == CAO_EQUAL && line[l] == '=') @@ -828,12 +931,13 @@ ca_get_opt(Cadef d, char *line, int full, char **end) return NULL; } +/* Same as above, only for single-letter-style. */ + static Caopt ca_get_sopt(Cadef d, char *line, int full, char **end) { Caopt p; - - line++; + char pre = *line++; if (full) { for (p = NULL; *line; line++) @@ -844,7 +948,7 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) } else { for (p = NULL; *line; line++) if ((p = d->single[STOUC(*line)]) && p->active && - p->args && p->type != CAO_NEXT) { + p->args && p->type != CAO_NEXT && p->name[0] == pre) { if (end) { line++; if (p->type == CAO_EQUAL && *line == '=') @@ -852,13 +956,18 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) *end = line; } break; - } else if (!p || !p->active || (line[1] && p->args)) + } else if (!p || !p->active || (line[1] && p->args) || + p->name[0] != pre) return NULL; + if (p && end) + *end = line; return p; } return NULL; } +/* Return the n'th argument definition. */ + static Caarg ca_get_arg(Cadef d, int n) { @@ -868,14 +977,16 @@ ca_get_arg(Cadef d, int n) while (a && a->num < n) a = a->next; - if (a && a->num == n) + if (a && a->num == n && a->active) return a; - return d->rest; + return (d->rest && d->rest->active ? d->rest : NULL); } return NULL; } +/* Use a xor list, marking options as inactive. */ + static void ca_inactive(Cadef d, char **xor) { @@ -885,17 +996,32 @@ ca_inactive(Cadef d, char **xor) for (; *xor; xor++) { if (xor[0][0] == ':' && !xor[0][1]) d->argsactive = 0; - else if ((opt = ca_get_opt(d, *xor, 1, NULL))) + else if (xor[0][0] == '*' && !xor[0][1]) { + if (d->rest) + d->rest->active = 0; + } else if (xor[0][0] >= '0' && xor[0][0] <= '9') { + int n = atoi(xor[0]); + Caarg a = d->args; + + while (a && a->num < n) + a = a->next; + + if (a && a->num == n) + a->active = 0; + } else if ((opt = ca_get_opt(d, *xor, 1, NULL))) opt->active = 0; } } } +/* State when parsing a command line. */ + struct castate { Cadef d; + int nopts; Caarg def, ddef; Caopt curopt; - int opt, arg, argbeg, optbeg, nargbeg, restbeg; + int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos; int inopt, inrest, inarg, nth, doff, singles; LinkList args; LinkList *oargs; @@ -904,40 +1030,55 @@ struct castate { static struct castate ca_laststate; static int ca_parsed = 0, ca_alloced = 0; +/* Pars a command line. */ + static void ca_parse_line(Cadef d) { Caarg adef, ddef; - Caopt ptr; + Caopt ptr, wasopt; struct castate state; - char *line, *pe; + char *line, *pe, **argxor = NULL; int cur, doff; Patprog endpat = NULL; + /* Free old state. */ + if (ca_alloced) { - int i = ca_laststate.d->nopts; + int i = ca_laststate.nopts; LinkList *p = ca_laststate.oargs; freelinklist(ca_laststate.args, freestr); while (i--) if (*p++) freelinklist(p[-1], freestr); + + zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList)); } + /* Mark everything as active. */ + for (ptr = d->opts; ptr; ptr = ptr->next) ptr->active = 1; d->argsactive = 1; + if (d->rest) + d->rest->active = 1; + for (adef = d->args; adef; adef = adef->next) + adef->active = 1; + + /* Default values for the state. */ state.d = d; + state.nopts = d->nopts; state.def = state.ddef = NULL; state.curopt = NULL; state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; state.inrest = state.doff = state.singles = state.doff = 0; - PERMALLOC { - state.args = newlinklist(); - state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); - memset(state.oargs, 0, d->nopts * sizeof(LinkList)); - } LASTALLOC; + state.curpos = compcurrent; + state.args = znewlinklist(); + state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); + memset(state.oargs, 0, d->nopts * sizeof(LinkList)); + ca_alloced = 1; memcpy(&ca_laststate, &state, sizeof(state)); @@ -947,46 +1088,67 @@ ca_parse_line(Cadef d) return; } + /* Loop over the words from the line. */ + for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL; line; line = compwords[cur++]) { ddef = adef = NULL; doff = state.singles = 0; + + ca_inactive(d, argxor); + + /* We've a definition for an argument, skip to the next. */ + if (state.def) { state.arg = 0; - if (state.curopt) { - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(line)); - } LASTALLOC; - } - state.opt = (state.def->type == CAA_OPT && line[0] && line[1]); + if (state.curopt) + zaddlinknode(state.oargs[state.curopt->num], ztrdup(line)); + + state.opt = (state.def->type == CAA_OPT); if (state.def->type == CAA_REST || state.def->type == CAA_RARGS || state.def->type == CAA_RREST) { if (state.def->end && pattry(endpat, line)) { state.def = NULL; state.curopt = NULL; + state.opt = state.arg = 1; continue; } } else if ((state.def = state.def->next)) state.argbeg = cur; - else + else { state.curopt = NULL; + state.opt = 1; + } } else { - state.opt = (line[0] && line[1]); - state.arg = 1; + state.opt = state.arg = 1; state.curopt = NULL; } + if (state.opt) + state.opt = (line[0] ? (line[1] ? 2 : 1) : 0); + pe = NULL; - if (state.opt && (state.curopt = ca_get_opt(d, line, 0, &pe))) { + wasopt = NULL; + + /* See if it's an option. */ + + if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) && + (state.curopt->type != CAO_EQUAL || + compwords[cur] || pe[-1] == '=')) { + ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - PERMALLOC { - state.oargs[state.curopt->num] = newlinklist(); - } LASTALLOC; + state.singles = (d->single && (!pe || !*pe) && + state.curopt->name[1] && !state.curopt->name[2]); + + state.oargs[state.curopt->num] = znewlinklist(); + ca_inactive(d, state.curopt->xor); + /* Collect the argument strings. Maybe. */ + if (state.def && (state.curopt->type == CAO_DIRECT || (state.curopt->type == CAO_ODIRECT && pe[0]) || @@ -996,27 +1158,32 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else { + if (!d->single || (state.curopt->name[1] && state.curopt->name[2])) + wasopt = state.curopt; state.curopt = NULL; - } else if (state.opt && d->single && + } + } else if (state.opt == 2 && d->single && (state.curopt = ca_get_sopt(d, line, 0, &pe))) { + /* Or maybe it's a single-letter option? */ + char *p; Caopt tmpopt; ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - state.singles = !*pe; + state.singles = (!pe || !*pe); - for (p = line + 1; p <= pe; p++) { + for (p = line + 1; p < pe; p++) { if ((tmpopt = d->single[STOUC(*p)])) { - PERMALLOC { - state.oargs[tmpopt->num] = newlinklist(); - } LASTALLOC; + state.oargs[tmpopt->num] = znewlinklist(); + ca_inactive(d, tmpopt->xor); } } @@ -1029,31 +1196,40 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else state.curopt = NULL; } else if (state.arg) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; + /* Otherwise it's a normal argument. */ + if (state.inopt) { + state.inopt = 0; + state.nargbeg = cur - 1; + } if ((adef = state.def = ca_get_arg(d, state.nth)) && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { state.inrest = 0; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; - } + state.opt = (cur == state.nargbeg + 1); + state.optbeg = state.nargbeg; + state.argbeg = cur - 1; + + for (; line; line = compwords[cur++]) + zaddlinknode(state.args, ztrdup(line)); + + memcpy(&ca_laststate, &state, sizeof(state)); + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - if (state.inopt) { - state.inopt = 0; - state.nargbeg = cur - 1; - } + zaddlinknode(state.args, ztrdup(line)); + + if (state.def) + argxor = state.def->xor; + if (state.def && state.def->type != CAA_NORMAL && state.def->type != CAA_OPT && state.inarg) { state.restbeg = cur; @@ -1064,6 +1240,8 @@ ca_parse_line(Cadef d) state.nth++; state.def = NULL; } + /* Do the end-pattern test if needed. */ + if (state.def && state.curopt && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { if (state.def->end) @@ -1071,52 +1249,77 @@ ca_parse_line(Cadef d) else { LinkList l = state.oargs[state.curopt->num]; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(l, line); - } LASTALLOC; - } + if (cur < compcurrent) + memcpy(&ca_laststate, &state, sizeof(state)); + + for (; line; line = compwords[cur++]) + zaddlinknode(l, ztrdup(line)); + + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - } + } else if (state.def && state.def->end) + endpat = patcompile(state.def->end, 0, NULL); + + /* Copy the state into the global one. */ + if (cur + 1 == compcurrent) { memcpy(&ca_laststate, &state, sizeof(state)); ca_laststate.ddef = NULL; ca_laststate.doff = 0; } else if (cur == compcurrent && !ca_laststate.def) { - if ((ca_laststate.def = ddef)) - ca_laststate.doff = doff; - else { + if ((ca_laststate.def = ddef)) { + ca_laststate.singles = state.singles; + if (state.curopt && state.curopt->type == CAO_NEXT) { + ca_laststate.ddef = ddef; + ca_laststate.def = NULL; + ca_laststate.opt = 1; + state.curopt->active = 1; + } else { + ca_laststate.doff = doff; + ca_laststate.opt = 0; + } + } else { ca_laststate.def = adef; ca_laststate.ddef = NULL; - ca_laststate.argbeg = state.nargbeg; - ca_laststate.optbeg = state.restbeg; + ca_laststate.optbeg = state.nargbeg; + ca_laststate.argbeg = state.restbeg; ca_laststate.singles = state.singles; + if (wasopt) + wasopt->active = 1; } } } } +/* Build a colon-list from a list. */ + static char * ca_colonlist(LinkList l) { if (l) { LinkNode n; - int len = 1; + int len = 0; char *p, *ret, *q; - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n; incnode(n)) { + len++; for (p = (char *) getdata(n); *p; p++) len += (*p == ':' ? 2 : 1); - + } ret = q = (char *) zalloc(len); - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n;) { for (p = (char *) getdata(n); *p; p++) { if (*p == ':') *q++ = '\\'; *q++ = *p; } + incnode(n); + if (n) + *q++ = ':'; + } *q = '\0'; return ret; @@ -1130,36 +1333,37 @@ bin_comparguments(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !ca_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'O': min = 4; max = 4; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 's': min = 1; max = 1; break; case 'M': min = 1; max = 1; break; case 'a': min = 0; max = 0; break; case 'W': min = 2; max = 2; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1189,19 +1393,45 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[1], ztrdup(arg->descr)); setsparam(args[2], ztrdup(arg->action)); - ignore_prefix(ca_laststate.doff); + if (ca_laststate.doff > 0) + ignore_prefix(ca_laststate.doff); if (arg->type == CAA_RARGS) - restrict_range(ca_laststate.argbeg - 1, + restrict_range(ca_laststate.optbeg, arrlen(compwords) - 1); else if (arg->type == CAA_RREST) - restrict_range(ca_laststate.optbeg - 1, + restrict_range(ca_laststate.argbeg, arrlen(compwords) - 1); return 0; } return 1; } + case 'C': + { + Caarg arg = ca_laststate.def; + + if (arg) { + char buf[20]; + + if (arg->num > 0) + sprintf(buf, "%d", arg->num); + else + strcpy(buf, "rest"); + + setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) : + tricat("argument-", buf, ""))); + return 0; + } + return 1; + } case 'O': - if (ca_laststate.opt) { + if ((ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) || + (ca_laststate.def && + (ca_laststate.def->type == CAA_OPT || + ca_laststate.def->type >= CAA_RARGS))) && + (!ca_laststate.def || ca_laststate.def->type < CAA_RARGS || + (ca_laststate.def->type == CAA_RARGS ? + (ca_laststate.curpos == ca_laststate.argbeg + 1) : + (compcurrent == 1)))) { LinkList next = newlinklist(); LinkList direct = newlinklist(); LinkList odirect = newlinklist(); @@ -1218,14 +1448,15 @@ bin_comparguments(char *nam, char **args, char *ops, int func) default: l = equal; break; } if (p->descr) { - int len = strlen(p->name) + strlen(p->descr) + 2; + char *n = bslashcolon(p->name); + int len = strlen(n) + strlen(p->descr) + 2; str = (char *) zhalloc(len); - strcpy(str, p->name); + strcpy(str, n); strcat(str, ":"); strcat(str, p->descr); } else - str = p->name; + str = bslashcolon(p->name); addlinknode(l, str); } } @@ -1235,8 +1466,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) set_list_array(args[4], equal); return 0; - } else - return 1; + } + return 1; case 'L': { Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL); @@ -1245,12 +1476,16 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[2], ztrdup(opt->args->descr)); setsparam(args[3], ztrdup(opt->args->action)); + if (args[4]) + setsparam(args[4], tricat(opt->name, "-1", "")); + return 0; } return 1; } case 's': - if (ca_laststate.d->single && ca_laststate.singles) { + if (ca_laststate.d->single && ca_laststate.singles && + ca_laststate.opt) { setsparam(args[1], ztrdup(ca_laststate.ddef ? (ca_laststate.ddef->type == CAO_DIRECT ? @@ -1258,8 +1493,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) (ca_laststate.ddef->type == CAO_EQUAL ? "equal" : "next")) : "")); return 0; - } else - return 1; + } + return 1; case 'M': setsparam(args[1], ztrdup(ca_laststate.d->match)); return 0; @@ -1310,67 +1545,79 @@ bin_comparguments(char *nam, char **args, char *ops, int func) typedef struct cvdef *Cvdef; typedef struct cvval *Cvval; +/* Definitions for _values. */ + struct cvdef { - char *descr; - int hassep; - char sep; - Cvdef next; - Cvval vals; - char **defs; - int ndefs; - int lastt; + char *descr; /* global description */ + int hassep; /* multiple values allowed */ + char sep; /* separator character */ + Cvdef next; /* next in cache */ + Cvval vals; /* value definitions */ + char **defs; /* original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time used */ }; +/* One value definition. */ + struct cvval { Cvval next; - char *name; - char *descr; - char **xor; - int type; - Caarg arg; - int active; + char *name; /* value name */ + char *descr; /* description */ + char **xor; /* xor-list */ + int type; /* CVV_* below */ + Caarg arg; /* argument definition */ + int active; /* still allowed */ }; #define CVV_NOARG 0 #define CVV_ARG 1 #define CVV_OPT 2 +/* Cache. */ + #define MAX_CVCACHE 8 static Cvdef cvdef_cache[MAX_CVCACHE]; +/* Memory stuff. */ + static void -free_cvdef(Cvdef d) +freecvdef(Cvdef d) { if (d) { Cvval p, n; zsfree(d->descr); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->vals; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->arg); + if (p->xor) + freearray(p->xor); + freecaargs(p->arg); zfree(p, sizeof(*p)); } zfree(d, sizeof(*d)); } } +/* Parse option definitions. */ + static Cvdef parse_cvdef(char *nam, char **args) { Cvdef ret; Cvval val, *valp; Caarg arg; - char **oargs = args, sep, *name, *descr, *p, *q, **xor, c; - int xnum, multi, vtype, hassep; + char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c; + int xnum, multi, vtype, hassep = 0; if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) { if (args[1][0] && args[1][1]) { - zerrnam(nam, "invalid separator: %s", args[1], 0); + zwarnnam(nam, "invalid separator: %s", args[1], 0); return NULL; } hassep = 1; @@ -1378,26 +1625,26 @@ parse_cvdef(char *nam, char **args) args += 2; } if (!args[0] || !args[1]) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return NULL; } descr = *args++; - PERMALLOC { - ret = (Cvdef) zalloc(sizeof(*ret)); - ret->descr = ztrdup(descr); - ret->hassep = hassep; - ret->sep = sep; - ret->next = NULL; - ret->vals = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - } LASTALLOC; + ret = (Cvdef) zalloc(sizeof(*ret)); + ret->descr = ztrdup(descr); + ret->hassep = hassep; + ret->sep = sep; + ret->next = NULL; + ret->vals = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); for (valp = &(ret->vals); *args; args++) { p = dupstring(*args); xnum = 0; + + /* xor list? */ if (*p == '(') { LinkList list = newlinklist(); LinkNode node; @@ -1420,8 +1667,8 @@ parse_cvdef(char *nam, char **args) *p = sav; } if (*p != ')') { - free_cvdef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -1433,18 +1680,23 @@ parse_cvdef(char *nam, char **args) } else xor = NULL; + /* More than once allowed? */ if ((multi = (*p == '*'))) p++; + /* Skip option name. */ + for (name = p; *p && *p != ':' && *p != '['; p++) if (*p == '\\' && p[1]) p++; if (hassep && !sep && name + 1 != p) { - free_cvdef(ret); - zerrnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); return NULL; } + /* Optional description? */ + if ((c = *p) == '[') { *p = '\0'; for (descr = ++p; *p && *p != ']'; p++) @@ -1452,8 +1704,8 @@ parse_cvdef(char *nam, char **args) p++; if (!*p) { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -1463,8 +1715,8 @@ parse_cvdef(char *nam, char **args) descr = NULL; } if (c && c != ':') { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } if (!multi) { @@ -1474,10 +1726,12 @@ parse_cvdef(char *nam, char **args) } xor[xnum] = ztrdup(name); } + /* Get argument? */ + if (c == ':') { if (hassep && !sep) { - free_cvdef(ret); - zerrnam(nam, "no value with argument with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no value with argument with empty separator allowed", NULL, 0); return NULL; } if (*++p == ':') { @@ -1485,26 +1739,26 @@ parse_cvdef(char *nam, char **args) vtype = CVV_OPT; } else vtype = CVV_ARG; - arg = parse_caarg(0, 0, 0, &p); + arg = parse_caarg(0, 0, 0, name, &p); } else { vtype = CVV_NOARG; arg = NULL; } - PERMALLOC { - *valp = val = (Cvval) zalloc(sizeof(*val)); - valp = &((*valp)->next); - - val->next = NULL; - val->name = ztrdup(name); - val->descr = ztrdup(descr); - val->xor = xor; - val->type = vtype; - val->arg = arg; - } LASTALLOC; + *valp = val = (Cvval) zalloc(sizeof(*val)); + valp = &((*valp)->next); + + val->next = NULL; + val->name = ztrdup(name); + val->descr = ztrdup(descr); + val->xor = xor; + val->type = vtype; + val->arg = arg; } return ret; } +/* Get the definition from the cache or newly built. */ + static Cvdef get_cvdef(char *nam, char **args) { @@ -1521,12 +1775,14 @@ get_cvdef(char *nam, char **args) if (i) min = p; if ((new = parse_cvdef(nam, args))) { - free_cvdef(*min); + freecvdef(*min); *min = new; } return new; } +/* Get the definition for a value. */ + static Cvval cv_get_val(Cvdef d, char *name) { @@ -1539,6 +1795,8 @@ cv_get_val(Cvdef d, char *name) return NULL; } +/* Handle a xor list. */ + static void cv_inactive(Cvdef d, char **xor) { @@ -1551,6 +1809,8 @@ cv_inactive(Cvdef d, char **xor) } } +/* Parse state. */ + struct cvstate { Cvdef d; Caarg def; @@ -1561,6 +1821,8 @@ struct cvstate { static struct cvstate cv_laststate; static int cv_parsed = 0, cv_alloced = 0; +/* Parse the current word. */ + static void cv_parse_word(Cvdef d) { @@ -1577,9 +1839,8 @@ cv_parse_word(Cvdef d) state.d = d; state.def = NULL; state.val = NULL; - PERMALLOC { - state.vals = (LinkList) newlinklist(); - } LASTALLOC; + state.vals = (LinkList) znewlinklist(); + cv_alloced = 1; if (d->hassep) { @@ -1596,10 +1857,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1625,10 +1884,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1647,10 +1904,8 @@ cv_parse_word(Cvdef d) for (str = compprefix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1658,10 +1913,8 @@ cv_parse_word(Cvdef d) for (str = compsuffix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1696,35 +1949,36 @@ bin_compvalues(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !cv_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'V': min = 3; max = 3; break; case 's': min = 1; max = 1; break; case 'd': min = 1; max = 1; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 'v': min = 1; max = 1; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1758,6 +2012,17 @@ bin_compvalues(char *nam, char **args, char *ops, int func) } return 1; } + case 'C': + { + Caarg arg = cv_laststate.def; + + if (arg) { + setsparam(args[1], ztrdup(arg->opt)); + + return 0; + } + return 1; + } case 'V': { LinkList noarg = newlinklist(); @@ -1813,6 +2078,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func) setsparam(args[2], val->arg->descr); setsparam(args[3], val->arg->action); + if (args[4]) + setsparam(args[4], ztrdup(val->name)); + return 0; } return 1; @@ -1838,37 +2106,508 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } +static int +bin_compquote(char *nam, char **args, char *ops, int func) +{ + char *name; + struct value vbuf; + Value v; + + /* Anything to do? */ + + if (!compqstack || !*compqstack) + return 0; + + /* For all parameters given... */ + + while ((name = *args++)) { + name = dupstring(name); + if ((v = getvalue(&vbuf, &name, 0))) { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + { + char *val = getstrvalue(v); + + val = bslashquote(val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : 0))); + + setstrvalue(v, ztrdup(val)); + } + break; + case PM_ARRAY: + { + char **val = v->pm->gets.afn(v->pm); + char **new = (char **) zalloc((arrlen(val) + 1) * + sizeof(char *)); + char **p = new; + + for (; *val; val++, p++) + *p = ztrdup(bslashquote(*val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : + 0)))); + *p = NULL; + + setarrvalue(v, new); + } + break; + default: + zwarnnam(nam, "invalid parameter type: %s", args[-1], 0); + } + } else + zwarnnam(nam, "unknown parameter: %s", args[-1], 0); + } + return 0; +} + +/* Tags stuff. */ + +typedef struct ctags *Ctags; +typedef struct ctset *Ctset; + +/* A bunch of tag sets. */ + +struct ctags { + char **all; /* all tags offered */ + char *context; /* the current context */ + int init; /* not yet used */ + Ctset sets; /* the tag sets */ +}; + +/* A tag set. */ + +struct ctset { + Ctset next; + char **tags; /* the tags */ + char *tag; /* last tag checked for -A */ + char **ptr; /* ptr into tags for -A */ +}; + +/* Array of tag-set infos. Index is the locallevel. */ + +#define MAX_TAGS 256 +static Ctags comptags[MAX_TAGS]; + +/* locallevel at last comptags -i */ + +static int lasttaglevel; + +static void +freectset(Ctset s) +{ + Ctset n; + + while (s) { + n = s->next; + + if (s->tags) + freearray(s->tags); + zsfree(s->tag); + zfree(s, sizeof(*s)); + + s = n; + } +} + +static void +freectags(Ctags t) +{ + if (t) { + if (t->all) + freearray(t->all); + zsfree(t->context); + freectset(t->sets); + zfree(t, sizeof(*t)); + } +} + +/* Set the tags for the current local level. */ + +static void +settags(int level, char **tags) +{ + Ctags t; + + if (comptags[level]) + freectags(comptags[level]); + + comptags[level] = t = (Ctags) zalloc(sizeof(*t)); + + t->all = zarrdup(tags + 1); + t->context = ztrdup(*tags); + t->sets = NULL; + t->init = 1; +} + +/* Check if an array contains a string. */ + +static int +arrcontains(char **a, char *s, int colon) +{ + char *p, *q; + + while (*a) { + if (colon) { + for (p = s, q = *a++; *p && *q && *p != ':' && *q != ':'; p++, q++) + if (*p != *q) + break; + if ((!*p || *p == ':') && (!*q || *q == ':')) + return 1; + } else if (!strcmp(*a++, s)) + return 1; + } + return 0; +} + +static int +bin_comptags(char *nam, char **args, char *ops, int func) +{ + int min, max, n, level; + + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || + (args[0][2] && (args[0][2] != '-' || args[0][3]))) { + zwarnnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + level = locallevel - (args[0][2] ? 1 : 0); + if (level >= MAX_TAGS) { + zwarnnam(nam, "nesting level too deep", NULL, 0); + return 1; + } + if (args[0][1] != 'i' && args[0][1] != 'I' && !comptags[level]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': min = 2; max = -1; break; + case 'C': min = 1; max = 1; break; + case 'T': min = 0; max = 0; break; + case 'N': min = 0; max = 0; break; + case 'R': min = 1; max = 1; break; + case 'S': min = 1; max = 1; break; + case 'A': min = 2; max = 2; break; + default: + zwarnnam(nam, "invalid option: %s", args[0], 0); + return 1; + } + n = arrlen(args) - 1; + if (n < min) { + zwarnnam(nam, "not enough arguments", NULL, 0); + return 1; + } else if (max >= 0 && n > max) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': + settags(level, args + 1); + lasttaglevel = level; + break; + case 'C': + setsparam(args[1], ztrdup(comptags[level]->context)); + break; + case 'T': + return !comptags[level]->sets; + case 'N': + { + Ctset s; + + if (comptags[level]->init) + comptags[level]->init = 0; + else if ((s = comptags[level]->sets)) { + comptags[level]->sets = s->next; + s->next = NULL; + freectset(s); + } + return !comptags[level]->sets; + } + case 'R': + { + Ctset s; + + return !((s = comptags[level]->sets) && + arrcontains(s->tags, args[1], 1)); + } + case 'A': + { + Ctset s; + + if (comptags[level] && (s = comptags[level]->sets)) { + char **q, *v = NULL; + int l = strlen(args[1]); + + if (!s->tag || strcmp(s->tag, args[1])) { + zsfree(s->tag); + s->tag = ztrdup(args[1]); + s->ptr = s->tags; + } + for (q = s->ptr; *q; q++) { + if (strpfx(args[1], *q)) { + if (!(*q)[l]) { + v = *q; + break; + } else if ((*q)[l] == ':') { + v = (*q) + l + 1; + break; + } + } + } + if (!v) { + zsfree(s->tag); + s->tag = NULL; + return 1; + } + s->ptr = q + 1; + setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v)); + return 0; + } + return 1; + } + case 'S': + if (comptags[level]->sets) { + char **ret; + + ret = zarrdup(comptags[level]->sets->tags); + setaparam(args[1], ret); + } else + return 1; + + break; + } + return 0; +} + +static int +bin_comptry(char *nam, char **args, char *ops, int func) +{ + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (!lasttaglevel || !comptags[lasttaglevel]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + if (*args) { + if (!strcmp(*args, "-m")) { + char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all; + LinkList list = newlinklist(); + LinkNode node; + int num = 0; + Ctset set; + + while ((s = *++args)) { + while (*s) { + while (*s && iblank(*s)) + s++; + for (p = q = s, c = NULL; *s && !iblank(*s); s++) { + if (!c && *s == ':') + c = p; + if (*s == '\\' && s[1]) + s++; + *p++ = *s; + } + if (*s) + s++; + *p = '\0'; + if (*q) { + char *qq = dupstring(q); + if (c) + *c = '\0'; + + tokenize(qq); + if (haswilds(qq)) { + Patprog prog; + + if ((prog = patcompile(qq, PAT_STATIC, NULL))) { + char **a, *n; + int l = (c ? strlen(c + 1) + 2 : 1), al; + + for (a = all; *a; a++) { + if (pattry(prog, *a)) { + n = (char *) zhalloc((al = strlen(*a)) + l); + strcpy(n, *a); + if (c) { + n[al] = ':'; + strcpy(n + al + 1, c + 1); + } + addlinknode(list, n); + num++; + } + } + } + } else if (arrcontains(all, q, 0)) { + for (set = comptags[lasttaglevel]->sets; set; + set = set->next) + if (arrcontains(set->tags, q, 0)) + break; + if (!set) { + addlinknode(list, q); + num++; + } + } + if (c) + *c = ':'; + } + } + if (num) { + char **a; + Ctset l; + + set = (Ctset) zalloc(sizeof(*set)); + + a = set->tags = (char **) zalloc((num + 1) * sizeof(char *)); + for (node = firstnode(list); node; incnode(node)) + *a++ = ztrdup((char *) getdata(node)); + + *a = NULL; + set->next = NULL; + set->ptr = NULL; + set->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = set; + } else + comptags[lasttaglevel]->sets = set; + } + } + } else { + char **p, **q, **all; + int sep = 0; + + if ((sep = !strcmp(*args, "-s"))) + args++; + + for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++) + if (arrcontains(all, *p, 1)) { + Ctset s; + + for (s = comptags[lasttaglevel]->sets; s; s = s->next) + if (arrcontains(s->tags, *p, 0)) + break; + + if (!s) + *q++ = *p; + } + *q = NULL; + + if (*args) { + char *dummy[2]; + + do { + Ctset s = (Ctset) zalloc(sizeof(*s)), l; + + if (sep) { + dummy[0] = *args++; + dummy[1] = NULL; + s->tags = zarrdup(dummy); + } else + s->tags = zarrdup(args); + s->next = NULL; + s->ptr = NULL; + s->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = s; + } else + comptags[lasttaglevel]->sets = s; + } while (sep && *args); + } + } + } + return 0; +} + +static char * +fmtstr(char *str, char c, char *repl) +{ + int len, num, rlen; + char *s, *ret, *rp; + + len = strlen(str); + rlen = strlen(repl); + + for (num = 0, s = str; *s; s++) + if (*s == '%' && s[1] == c) + num++, s++; + + ret = (char *) zhalloc((num * (rlen - 2)) + len + 1); + + for (s = str, rp = ret; *s; s++) { + if (*s == '%' && s[1] == c) { + strcpy(rp, repl); + rp += rlen; + s++; + } else + *rp++ = *s; + } + *rp = '\0'; + + return ret; +} + +static int +bin_compfmt(char *nam, char **args, char *ops, int func) +{ + char *param = args[0], *str = args[1]; + + for (args += 2; *args; args++) { + if (args[0][1] != ':') { + zwarnnam(nam, "invalid argument `%s'", args[0], 0); + return 1; + } + str = fmtstr(str, **args, *args + 2); + } + setsparam(param, ztrdup(str)); + return 0; +} static struct builtin bintab[] = { - BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL), BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL), BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL), BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL), + BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL), + BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL), + BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL), + BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL), }; /**/ int -setup_computil(Module m) +setup_(Module m) { memset(cadef_cache, 0, sizeof(cadef_cache)); memset(cvdef_cache, 0, sizeof(cvdef_cache)); + memset(comptags, 0, sizeof(comptags)); + + lasttaglevel = 0; + return 0; } /**/ int -boot_computil(Module m) +boot_(Module m) { return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); } -#ifdef MODULE - /**/ int -cleanup_computil(Module m) +cleanup_(Module m) { deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); return 0; @@ -1876,16 +2615,17 @@ cleanup_computil(Module m) /**/ int -finish_computil(Module m) +finish_(Module m) { int i; for (i = 0; i < MAX_CACACHE; i++) - free_cadef(cadef_cache[i]); + freecadef(cadef_cache[i]); for (i = 0; i < MAX_CVCACHE; i++) - free_cvdef(cvdef_cache[i]); + freecvdef(cvdef_cache[i]); + + for (i = 0; i < MAX_TAGS; i++) + freectags(comptags[i]); return 0; } - -#endif |