mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-06-22 22:58:03 +02:00
2549 lines
56 KiB
C
2549 lines
56 KiB
C
/*
|
|
* 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 GLOBAL_PROTOTYPES
|
|
#include "zle_tricky.pro"
|
|
#undef GLOBAL_PROTOTYPES
|
|
|
|
#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_LISTMATCH (1<<5) /* -L and -M */
|
|
|
|
#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;
|
|
|
|
/* This is a special return value for parse_cmatcher(), *
|
|
* signalling an error. */
|
|
|
|
#define pcm_err ((Cmatcher) 1)
|
|
|
|
/* 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. */
|
|
|
|
/**/
|
|
static 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;
|
|
}
|
|
|
|
/* Try to get the global matcher from the given compctl. */
|
|
|
|
/**/
|
|
static int
|
|
get_gmatcher(char *name, char **argv)
|
|
{
|
|
if (!strcmp(*argv, "-M")) {
|
|
char **p = ++argv;
|
|
Cmlist l = NULL, *q = &l, n;
|
|
Cmatcher m;
|
|
|
|
while (*p) {
|
|
if (**p++ == '-')
|
|
return 0;
|
|
}
|
|
while (*argv) {
|
|
if ((m = parse_cmatcher(name, *argv)) == pcm_err)
|
|
return 2;
|
|
*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;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This prints the global matcher definitions. */
|
|
|
|
/**/
|
|
static void
|
|
print_gmatcher(int ac)
|
|
{
|
|
Cmlist p;
|
|
|
|
if ((p = cmatcher)) {
|
|
printf((ac ? "compctl -M" : "MATCH"));
|
|
|
|
while (p) {
|
|
printf(" \'%s\'", p->str);
|
|
|
|
p = p->next;
|
|
}
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
/* Parse a string for matcher control, containing multiple matchers. */
|
|
|
|
/**/
|
|
static 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 (!*s || !*++s) {
|
|
zwarnnam(name, ((fl & CMF_RIGHT) ? "missing right anchor" : "missing word pattern"), NULL, 0);
|
|
return pcm_err;
|
|
}
|
|
if (fl & CMF_RIGHT) {
|
|
right = parse_pattern(name, &s, &ral, '=', &err);
|
|
if (err)
|
|
return pcm_err;
|
|
if (!*s || !*++s) {
|
|
zwarnnam(name, "missing word pattern", NULL, 0);
|
|
return pcm_err;
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
/* Parse the basic flags for `compctl' */
|
|
|
|
/**/
|
|
static int
|
|
get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
|
|
{
|
|
/* 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;
|
|
if (cl)
|
|
return 1;
|
|
else {
|
|
freecompctl(cc);
|
|
cclist = COMP_REMOVE;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
memset((void *)&cct, 0, sizeof(cct));
|
|
cct.mask2 = CC_CCCONT;
|
|
|
|
/* 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 't':
|
|
{
|
|
char *p;
|
|
|
|
if (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
}
|
|
if ((*argv)[1]) {
|
|
p = (*argv) + 1;
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "retry specification expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
p = *++argv;
|
|
*argv = "" - 1;
|
|
}
|
|
switch (*p) {
|
|
case '+':
|
|
cct.mask2 = CC_XORCONT;
|
|
break;
|
|
case 'n':
|
|
cct.mask2 = 0;
|
|
break;
|
|
case '-':
|
|
cct.mask2 = CC_PATCONT;
|
|
break;
|
|
case 'x':
|
|
cct.mask2 = CC_DEFCONT;
|
|
break;
|
|
default:
|
|
zwarnnam(name, "invalid retry specification character `%c'",
|
|
NULL, *p);
|
|
return 1;
|
|
}
|
|
if (p[1]) {
|
|
zwarnnam(name, "too many retry specification characters: `%s'",
|
|
p + 1, 0);
|
|
return 1;
|
|
}
|
|
}
|
|
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 'i':
|
|
if ((*argv)[1]) {
|
|
cct.widget = (*argv) + 1;
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "function name expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
cct.widget = *++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 (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
} else 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 'h':
|
|
if (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
} else if ((*argv)[1]) {
|
|
cct.substr = (*argv) + 1;
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "command name expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
cct.substr = *++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 'J':
|
|
if ((*argv)[1]) {
|
|
cct.gname = (*argv) + 1;
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "group name expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
cct.gname = *++argv;
|
|
*argv = "" - 1;
|
|
}
|
|
break;
|
|
case 'V':
|
|
if ((*argv)[1]) {
|
|
cct.gname = (*argv) + 1;
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "group name expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
cct.gname = *++argv;
|
|
*argv = "" - 1;
|
|
}
|
|
cct.mask2 |= CC_NOSORT;
|
|
break;
|
|
case 'M':
|
|
if (cclist & COMP_LIST) {
|
|
cclist |= COMP_LISTMATCH;
|
|
} else if ((*argv)[1]) {
|
|
if ((cct.matcher =
|
|
parse_cmatcher(name, (cct.mstr = (*argv) + 1))) ==
|
|
pcm_err) {
|
|
cct.matcher = NULL;
|
|
cct.mstr = NULL;
|
|
return 1;
|
|
}
|
|
*argv = "" - 1;
|
|
} else if (!argv[1]) {
|
|
zwarnnam(name, "matching specification expected after -%c", NULL,
|
|
**argv);
|
|
return 1;
|
|
} else {
|
|
if ((cct.matcher =
|
|
parse_cmatcher(name, (cct.mstr = *++argv))) ==
|
|
pcm_err) {
|
|
cct.matcher = NULL;
|
|
cct.mstr = NULL;
|
|
return 1;
|
|
}
|
|
*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 (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
}
|
|
if (first && !hx) {
|
|
cclist |= COMP_COMMAND;
|
|
} else {
|
|
zwarnnam(name, "misplaced command completion (-C) flag",
|
|
NULL, 0);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'D':
|
|
if (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
}
|
|
if (first && !hx) {
|
|
isdef = 1;
|
|
cclist |= COMP_DEFAULT;
|
|
} else {
|
|
zwarnnam(name, "misplaced default completion (-D) flag",
|
|
NULL, 0);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'T':
|
|
if (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
}
|
|
if (first && !hx) {
|
|
cclist |= COMP_FIRST;
|
|
} else {
|
|
zwarnnam(name, "misplaced first completion (-T) flag",
|
|
NULL, 0);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'L':
|
|
if (cl) {
|
|
zerrnam(name, "illegal option -%c", NULL, **argv);
|
|
return 1;
|
|
}
|
|
if (!first || hx) {
|
|
zwarnnam(name, "illegal use of -L flag", NULL, 0);
|
|
return 1;
|
|
}
|
|
cclist |= COMP_LIST;
|
|
break;
|
|
case 'x':
|
|
if (cl) {
|
|
zerrnam(name, "extended completion not allowed", NULL, 0);
|
|
return 1;
|
|
}
|
|
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]) {
|
|
if (cl) {
|
|
zerrnam(name, "xor'ed completion illegal", NULL, 0);
|
|
return 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));
|
|
cct.mask2 = CC_CCCONT;
|
|
}
|
|
}
|
|
}
|
|
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 'q':
|
|
c->type = CCT_QUOTE;
|
|
break;
|
|
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->type == CCT_QUOTE)
|
|
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 ||
|
|
c->type == CCT_QUOTE) {
|
|
/* -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) {
|
|
int hc;
|
|
|
|
/* -r[..,..] or -R[..,..]: two strings expected */
|
|
for (; *t && *t != '\201' && *t != '\200'; t++);
|
|
if (!*t) {
|
|
zwarnnam(name, "error in condition", NULL, 0);
|
|
freecompcond(m);
|
|
return 1;
|
|
}
|
|
hc = (*t == '\201');
|
|
*t = '\0';
|
|
c->u.l.a[l] = ztrdup(tt);
|
|
if (hc) {
|
|
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
|
|
c->u.l.b[l] = NULL;
|
|
} 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, 0))
|
|
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;
|
|
|
|
/* 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->widget);
|
|
zsfree(cc->explain);
|
|
zsfree(cc->ylist);
|
|
zsfree(cc->prefix);
|
|
zsfree(cc->suffix);
|
|
zsfree(cc->subcmd);
|
|
zsfree(cc->substr);
|
|
zsfree(cc->withd);
|
|
zsfree(cc->hpat);
|
|
zsfree(cc->gname);
|
|
zsfree(cc->mstr);
|
|
freecmatcher(cc->matcher);
|
|
|
|
/* and copy over the new stuff, (permanently) allocating
|
|
* space for strings.
|
|
*/
|
|
cc->mask = cct->mask;
|
|
cc->mask2 = cct->mask2;
|
|
cc->keyvar = ztrdup(cct->keyvar);
|
|
cc->glob = ztrdup(cct->glob);
|
|
cc->str = ztrdup(cct->str);
|
|
cc->func = ztrdup(cct->func);
|
|
cc->widget = ztrdup(cct->widget);
|
|
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->substr = ztrdup(cct->substr);
|
|
cc->withd = ztrdup(cct->withd);
|
|
cc->gname = ztrdup(cct->gname);
|
|
cc->hpat = ztrdup(cct->hpat);
|
|
cc->hnum = cct->hnum;
|
|
PERMALLOC {
|
|
cc->matcher = cpcmatcher(cct->matcher);
|
|
} LASTALLOC;
|
|
cc->mstr = ztrdup(cct->mstr);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Check if the given string is a pattern. If so, return one and tokenize *
|
|
* it. If not, we just remove the backslashes. */
|
|
|
|
static int
|
|
compctl_name_pat(char **p)
|
|
{
|
|
char *s = *p;
|
|
|
|
tokenize(s = dupstring(s));
|
|
remnulargs(s);
|
|
|
|
if (haswilds(s)) {
|
|
*p = s;
|
|
return 1;
|
|
} else
|
|
*p = rembslash(*p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Delete the pattern compctl with the given name. */
|
|
|
|
static void
|
|
delpatcomp(char *n)
|
|
{
|
|
Patcomp p, q;
|
|
|
|
for (q = 0, p = patcomps; p; q = p, p = p->next) {
|
|
if (!strcmp(n, p->pat)) {
|
|
if (q)
|
|
q->next = p->next;
|
|
else
|
|
patcomps = p->next;
|
|
zsfree(p->pat);
|
|
freecompctl(p->cc);
|
|
free(p);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
compctl_process_cc(char **s, Compctl cc)
|
|
{
|
|
Compctlp ccp;
|
|
char *n;
|
|
|
|
if (cclist & COMP_REMOVE) {
|
|
/* Delete entries for the commands listed */
|
|
for (; *s; s++) {
|
|
n = *s;
|
|
if (compctl_name_pat(&n))
|
|
delpatcomp(n);
|
|
else if ((ccp = (Compctlp) compctltab->removenode(compctltab, n)))
|
|
compctltab->freenode((HashNode) ccp);
|
|
}
|
|
} else {
|
|
/* Add the compctl just read to the hash table */
|
|
|
|
cc->refc = 0;
|
|
for (; *s; s++) {
|
|
n = *s;
|
|
|
|
cc->refc++;
|
|
if (compctl_name_pat(&n)) {
|
|
Patcomp pc;
|
|
|
|
delpatcomp(n);
|
|
pc = zalloc(sizeof *pc);
|
|
pc->pat = ztrdup(n);
|
|
pc->cc = cc;
|
|
pc->next = patcomps;
|
|
patcomps = pc;
|
|
} else {
|
|
ccp = (Compctlp) zalloc(sizeof *ccp);
|
|
ccp->cc = cc;
|
|
compctltab->addnode(compctltab, ztrdup(n), ccp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Print a `compctl' */
|
|
|
|
/**/
|
|
static void
|
|
printcompctl(char *s, Compctl cc, int printflags, int ispat)
|
|
{
|
|
Compctl cc2;
|
|
char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
|
|
char *mss = " pcCwWsSnNmrRq";
|
|
unsigned long t = 0x7fffffff;
|
|
unsigned long flags = cc->mask, flags2 = cc->mask2;
|
|
unsigned long oldshowmask;
|
|
|
|
/* Printflags is used outside the standard compctl commands*/
|
|
if (printflags & PRINT_LIST)
|
|
cclist |= COMP_LIST;
|
|
else if (printflags & PRINT_TYPE)
|
|
cclist &= ~COMP_LIST;
|
|
|
|
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 if (ispat) {
|
|
char *p = dupstring(s);
|
|
|
|
untokenize(p);
|
|
quotedzputs(p, stdout);
|
|
} else
|
|
quotedzputs(bslashquote(s, NULL, 0), 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;
|
|
}
|
|
}
|
|
if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) {
|
|
printf(" -t");
|
|
if (flags2 & CC_XORCONT)
|
|
putchar('+');
|
|
if (flags2 & CC_PATCONT)
|
|
putchar('-');
|
|
if (flags2 & CC_DEFCONT)
|
|
putchar('x');
|
|
} else if (!(flags2 & CC_CCCONT))
|
|
printf(" -tn");
|
|
/* now flags with arguments */
|
|
printif(cc->mstr, 'M');
|
|
if (flags2 & CC_NOSORT)
|
|
printif(cc->gname, 'V');
|
|
else
|
|
printif(cc->gname, 'J');
|
|
printif(cc->keyvar, 'k');
|
|
printif(cc->func, 'K');
|
|
printif(cc->widget, 'i');
|
|
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->substr, 'h');
|
|
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:
|
|
case CCT_QUOTE:
|
|
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, 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, 0);
|
|
}
|
|
if (s) {
|
|
if ((cclist & COMP_LIST) && (cc != &cc_compos)
|
|
&& (cc != &cc_default) && (cc != &cc_first)) {
|
|
if(s[0] == '-' || s[0] == '+')
|
|
printf(" -");
|
|
putchar(' ');
|
|
if (ispat) {
|
|
char *p = dupstring(s);
|
|
|
|
untokenize(p);
|
|
quotedzputs(p, stdout);
|
|
} else {
|
|
char *p = dupstring(s);
|
|
|
|
untokenize(p);
|
|
quotedzputs(bslashquote(p, NULL, 0), 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, 0);
|
|
}
|
|
|
|
/* 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) {
|
|
/* Let's see if this is a global matcher definition. */
|
|
if ((ret = get_gmatcher(name, argv)))
|
|
return ret - 1;
|
|
|
|
cc = (Compctl) zcalloc(sizeof(*cc));
|
|
if (get_compctl(name, &argv, cc, 1, 0, 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|COMP_LISTMATCH))) {
|
|
Patcomp pc;
|
|
|
|
for (pc = patcomps; pc; pc = pc->next)
|
|
printcompctl(pc->pat, pc->cc, 0, 1);
|
|
|
|
scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
|
|
printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0, 0);
|
|
printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
|
|
printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
|
|
print_gmatcher((cclist & COMP_LIST));
|
|
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, *n;
|
|
|
|
showmask = 0;
|
|
for (ptr = argv; *ptr; ptr++) {
|
|
n = *ptr;
|
|
if (compctl_name_pat(&n)) {
|
|
Patcomp pc;
|
|
|
|
for (pc = patcomps; pc; pc = pc->next)
|
|
if (!strcmp(n, pc->pat)) {
|
|
printcompctl(pc->pat, pc->cc, 0, 1);
|
|
n = NULL;
|
|
break;
|
|
}
|
|
} else if ((hn = compctltab->getnode(compctltab, n))) {
|
|
compctltab->printnode(hn, 0);
|
|
n = NULL;
|
|
}
|
|
if (n) {
|
|
zwarnnam(name, "no compctl defined for %s", n, 0);
|
|
ret = 1;
|
|
}
|
|
}
|
|
if (cclist & COMP_COMMAND)
|
|
printcompctl("", &cc_compos, 0, 0);
|
|
if (cclist & COMP_DEFAULT)
|
|
printcompctl("", &cc_default, 0, 0);
|
|
if (cclist & COMP_FIRST)
|
|
printcompctl("", &cc_first, 0, 0);
|
|
if (cclist & COMP_LISTMATCH)
|
|
print_gmatcher(COMP_LIST);
|
|
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 int
|
|
bin_compgen(char *name, char **argv, char *ops, int func)
|
|
{
|
|
Compctl cc;
|
|
int ret = 0;
|
|
|
|
if (incompfunc != 1) {
|
|
zerrnam(name, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
cc = (Compctl) zcalloc(sizeof(*cc));
|
|
cclist = 0;
|
|
showmask = 0;
|
|
|
|
if (get_compctl(name, &argv, cc, 1, 0, 1))
|
|
ret = 1;
|
|
else if (*argv) {
|
|
zerrnam(name, "command names illegal", NULL, 0);
|
|
ret = 1;
|
|
} else
|
|
ret = makecomplistcallptr(cc);
|
|
|
|
freecompctl(cc);
|
|
return ret;
|
|
}
|
|
|
|
/**/
|
|
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.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 '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 '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 '-':
|
|
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]) {
|
|
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 = addmatchesptr(&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
|
|
|
|
static void
|
|
ignore_prefix(int l)
|
|
{
|
|
char *tmp, 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;
|
|
}
|
|
|
|
static void
|
|
ignore_suffix(int l)
|
|
{
|
|
char *tmp, sav;
|
|
|
|
l = strlen(compsuffix) - l;
|
|
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;
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
restrict_range(int b, int e)
|
|
{
|
|
int i = e - b + 1;
|
|
char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
|
|
|
|
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;
|
|
|
|
restrict_range(na, nb);
|
|
return 1;
|
|
}
|
|
case CVT_RANGEPAT:
|
|
{
|
|
char **p;
|
|
int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
|
|
Comp c;
|
|
|
|
i = compcurrent - 1;
|
|
if (i < 0 || i >= l)
|
|
return 0;
|
|
|
|
singsub(&sa);
|
|
c = parsereg(sa);
|
|
|
|
for (i--, p = compwords + i; i >= 0; p--, i--) {
|
|
if (domatch(*p, c, 0)) {
|
|
b = i + 1;
|
|
t = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (t && sb) {
|
|
int tt = 0;
|
|
|
|
singsub(&sb);
|
|
c = parsereg(sb);
|
|
|
|
for (i++, p = compwords + i; i < l; p++, i++) {
|
|
if (domatch(*p, c, 0)) {
|
|
e = i - 1;
|
|
tt = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (tt && i < compcurrent)
|
|
t = 0;
|
|
}
|
|
if (e < b)
|
|
t = 0;
|
|
if (t)
|
|
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:
|
|
{
|
|
Comp c;
|
|
|
|
if (!na)
|
|
return 0;
|
|
|
|
if (!(c = parsereg(sa)))
|
|
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 = domatch(compprefix, c, 0);
|
|
*p = sav;
|
|
if (test && !--na)
|
|
break;
|
|
}
|
|
if (!l)
|
|
return 0;
|
|
|
|
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 (domatch(p, c, 0) && !--na)
|
|
break;
|
|
|
|
if (!l)
|
|
return 0;
|
|
|
|
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_sepptr();
|
|
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);
|
|
if (sb) {
|
|
tokenize(sb);
|
|
sb = rembslash(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);
|
|
break;
|
|
}
|
|
return !do_comp_vars(test, na, sa, nb, sb, 1);
|
|
}
|
|
|
|
/**/
|
|
static int
|
|
bin_compcall(char *name, char **argv, char *ops, int func)
|
|
{
|
|
if (incompfunc != 1) {
|
|
zerrnam(name, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
return makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) |
|
|
(ops['D'] ? 0 : CFN_DEFAULT));
|
|
}
|
|
|
|
/* 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, VAL(compnmatches), NULL, NULL },
|
|
{ "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, VAL(complist), NULL, NULL },
|
|
{ "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 },
|
|
{ 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 char *
|
|
get_unambig(Param pm)
|
|
{
|
|
return unambig_dataptr(NULL);
|
|
}
|
|
|
|
/**/
|
|
static zlong
|
|
get_unambig_curs(Param pm)
|
|
{
|
|
int c;
|
|
|
|
unambig_dataptr(&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),
|
|
0, NULL, 0);
|
|
else
|
|
return do_comp_vars(id, -1, cond_str(a, 0), 0, NULL, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
static int
|
|
cond_range(char **a, int id)
|
|
{
|
|
return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0), 0,
|
|
(id ? cond_str(a, 1) : NULL), 0);
|
|
}
|
|
|
|
static struct builtin bintab[] = {
|
|
BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
|
|
BUILTIN("compgen", 0, bin_compgen, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
|
|
BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
|
|
BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", 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),
|
|
};
|
|
|
|
/**/
|
|
int
|
|
setup_compctl(Module m)
|
|
{
|
|
compctltab->printnode = printcompctlp;
|
|
makecompparamsptr = makecompparams;
|
|
comp_setunsetptr = comp_setunset;
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
boot_compctl(Module m)
|
|
{
|
|
if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
|
|
addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
|
|
!addwrapper(m, wrapper)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef MODULE
|
|
|
|
/**/
|
|
int
|
|
cleanup_compctl(Module m)
|
|
{
|
|
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
|
deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
|
|
deletewrapper(m, wrapper);
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
finish_compctl(Module m)
|
|
{
|
|
compctltab->printnode = NULL;
|
|
makecompparamsptr = NULL;
|
|
comp_setunsetptr = NULL;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|