diff options
Diffstat (limited to 'contrib/tcsh/tw.comp.c')
-rw-r--r-- | contrib/tcsh/tw.comp.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/contrib/tcsh/tw.comp.c b/contrib/tcsh/tw.comp.c new file mode 100644 index 0000000..6d5652a --- /dev/null +++ b/contrib/tcsh/tw.comp.c @@ -0,0 +1,626 @@ +/* $Header: /src/pub/tcsh/tw.comp.c,v 1.31 1998/10/25 15:10:49 christos Exp $ */ +/* + * tw.comp.c: File completion builtin + */ +/*- + * Copyright (c) 1980, 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "sh.h" + +RCSID("$Id: tw.comp.c,v 1.31 1998/10/25 15:10:49 christos Exp $") + +#include "tw.h" +#include "ed.h" +#include "tc.h" + +/* #define TDEBUG */ +struct varent completions; + +static int tw_result __P((Char *, Char **)); +static Char **tw_find __P((Char *, struct varent *, int)); +static Char *tw_tok __P((Char *)); +static bool tw_pos __P((Char *, int)); +static void tw_pr __P((Char **)); +static int tw_match __P((Char *, Char *)); +static void tw_prlist __P((struct varent *)); +static Char *tw_dollar __P((Char *,Char **, int, Char *, + int, const char *)); + +/* docomplete(): + * Add or list completions in the completion list + */ +/*ARGSUSED*/ +void +docomplete(v, t) + Char **v; + struct command *t; +{ + register struct varent *vp; + register Char *p; + + USE(t); + v++; + p = *v++; + if (p == 0) + tw_prlist(&completions); + else if (*v == 0) { + vp = adrof1(strip(p), &completions); + if (vp) + tw_pr(vp->vec), xputchar('\n'); + } + else + set1(strip(p), saveblk(v), &completions, VAR_READWRITE); +} /* end docomplete */ + + +/* douncomplete(): + * Remove completions from the completion list + */ +/*ARGSUSED*/ +void +douncomplete(v, t) + Char **v; + struct command *t; +{ + USE(t); + unset1(v, &completions); +} /* end douncomplete */ + + +/* tw_prlist(): + * Pretty print a list of variables + */ +static void +tw_prlist(p) + struct varent *p; +{ + register struct varent *c; + + if (setintr) +#ifdef BSDSIGS + (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT)); +#else /* BSDSIGS */ + (void) sigrelse(SIGINT); +#endif /* BSDSIGS */ + + for (;;) { + while (p->v_left) + p = p->v_left; +x: + if (p->v_parent == 0) /* is it the header? */ + return; + xprintf("%s\t", short2str(p->v_name)); + tw_pr(p->vec); + xputchar('\n'); + if (p->v_right) { + p = p->v_right; + continue; + } + do { + c = p; + p = p->v_parent; + } while (p->v_right == c); + goto x; + } +} /* end tw_prlist */ + + +/* tw_pr(): + * Pretty print a completion, adding single quotes around + * a completion argument and collapsing multiple spaces to one. + */ +static void +tw_pr(cmp) + Char **cmp; +{ + bool sp, osp; + Char *ptr; + + for (; *cmp; cmp++) { + xputchar('\''); + for (osp = 0, ptr = *cmp; *ptr; ptr++) { + sp = Isspace(*ptr); + if (sp && osp) + continue; + xputchar(*ptr); + osp = sp; + } + xputchar('\''); + if (cmp[1]) + xputchar(' '); + } +} /* end tw_pr */ + + +/* tw_find(): + * Find the first matching completion. + * For commands we only look at names that start with - + */ +static Char ** +tw_find(nam, vp, cmd) + Char *nam; + register struct varent *vp; + int cmd; +{ + register Char **rv; + + for (vp = vp->v_left; vp; vp = vp->v_right) { + if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL) + return rv; + if (cmd) { + if (vp->v_name[0] != '-') + continue; + if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL) + return vp->vec; + } + else + if (Gmatch(nam, vp->v_name) && vp->vec != NULL) + return vp->vec; + } + return NULL; +} /* end tw_find */ + + +/* tw_pos(): + * Return true if the position is within the specified range + */ +static bool +tw_pos(ran, wno) + Char *ran; + int wno; +{ + Char *p; + + if (ran[0] == '*' && ran[1] == '\0') + return 1; + + for (p = ran; *p && *p != '-'; p++) + continue; + + if (*p == '\0') /* range == <number> */ + return wno == getn(ran); + + if (ran == p) /* range = - <number> */ + return wno <= getn(&ran[1]); + *p++ = '\0'; + + if (*p == '\0') /* range = <number> - */ + return getn(ran) <= wno; + else /* range = <number> - <number> */ + return (getn(ran) <= wno) && (wno <= getn(p)); + +} /* end tw_pos */ + + +/* tw_tok(): + * Return the next word from string, unquoteing it. + */ +static Char * +tw_tok(str) + Char *str; +{ + static Char *bf = NULL; + + if (str != NULL) + bf = str; + + /* skip leading spaces */ + for (; *bf && Isspace(*bf); bf++) + continue; + + for (str = bf; *bf && !Isspace(*bf); bf++) { + if (ismeta(*bf)) + return INVPTR; + *bf = *bf & ~QUOTE; + } + if (*bf != '\0') + *bf++ = '\0'; + + return *str ? str : NULL; +} /* end tw_tok */ + + +/* tw_match(): + * Match a string against the pattern given. + * and return the number of matched characters + * in a prefix of the string. + */ +static int +tw_match(str, pat) + Char *str, *pat; +{ + Char *estr; + int rv = Gnmatch(str, pat, &estr); +#ifdef TDEBUG + xprintf("Gnmatch(%s, ", short2str(str)); + xprintf("%s, ", short2str(pat)); + xprintf("%s) = %d [%d]\n", short2str(estr), rv, estr - str); +#endif /* TDEBUG */ + return (int) (rv ? estr - str : -1); +} + + +/* tw_result(): + * Return what the completion action should be depending on the + * string + */ +static int +tw_result(act, pat) + Char *act, **pat; +{ + int looking; + static Char* res = NULL; + + if (res != NULL) + xfree((ptr_t) res), res = NULL; + + switch (act[0] & ~QUOTE) { + case 'X': + looking = TW_COMPLETION; + break; + case 'S': + looking = TW_SIGNAL; + break; + case 'a': + looking = TW_ALIAS; + break; + case 'b': + looking = TW_BINDING; + break; + case 'c': + looking = TW_COMMAND; + break; + case 'C': + looking = TW_PATH | TW_COMMAND; + break; + case 'd': + looking = TW_DIRECTORY; + break; + case 'D': + looking = TW_PATH | TW_DIRECTORY; + break; + case 'e': + looking = TW_ENVVAR; + break; + case 'f': + looking = TW_FILE; + break; +#ifdef COMPAT + case 'p': +#endif /* COMPAT */ + case 'F': + looking = TW_PATH | TW_FILE; + break; + case 'g': + looking = TW_GRPNAME; + break; + case 'j': + looking = TW_JOB; + break; + case 'l': + looking = TW_LIMIT; + break; + case 'n': + looking = TW_NONE; + break; + case 's': + looking = TW_SHELLVAR; + break; + case 't': + looking = TW_TEXT; + break; + case 'T': + looking = TW_PATH | TW_TEXT; + break; + case 'v': + looking = TW_VARIABLE; + break; + case 'u': + looking = TW_USER; + break; + case 'x': + looking = TW_EXPLAIN; + break; + + case '$': + *pat = res = Strsave(&act[1]); + (void) strip(res); + return(TW_VARLIST); + + case '(': + *pat = res = Strsave(&act[1]); + if ((act = Strchr(res, ')')) != NULL) + *act = '\0'; + (void) strip(res); + return TW_WORDLIST; + + case '`': + res = Strsave(act); + if ((act = Strchr(&res[1], '`')) != NULL) + *++act = '\0'; + + if (didfds == 0) { + /* + * Make sure that we have some file descriptors to + * play with, so that the processes have at least 0, 1, 2 + * open + */ + (void) dcopy(SHIN, 0); + (void) dcopy(SHOUT, 1); + (void) dcopy(SHDIAG, 2); + } + if ((act = globone(res, G_APPEND)) != NULL) { + xfree((ptr_t) res), res = NULL; + *pat = res = Strsave(act); + xfree((ptr_t) act); + return TW_WORDLIST; + } + return TW_ZERO; + + default: + stderror(ERR_COMPCOM, short2str(act)); + return TW_ZERO; + } + + switch (act[1] & ~QUOTE) { + case '\0': + return looking; + + case ':': + *pat = res = Strsave(&act[2]); + (void) strip(res); + return looking; + + default: + stderror(ERR_COMPCOM, short2str(act)); + return TW_ZERO; + } +} /* end tw_result */ + + +/* tw_dollar(): + * Expand $<n> args in buffer + */ +static Char * +tw_dollar(str, wl, nwl, buffer, sep, msg) + Char *str, **wl; + int nwl; + Char *buffer; + int sep; + const char *msg; +{ + Char *sp, *bp = buffer, *ebp = &buffer[MAXPATHLEN]; + + for (sp = str; *sp && *sp != sep && bp < ebp;) + if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { + int num, neg = 0; + sp += 2; + if (*sp == '-') { + neg = 1; + sp++; + } + for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') + continue; + if (neg) + num = nwl - num - 1; + if (num >= 0 && num < nwl) { + Char *ptr; + for (ptr = wl[num]; *ptr && bp < ebp - 1; *bp++ = *ptr++) + continue; + + } + } + else + *bp++ = *sp++; + + *bp = '\0'; + + if (*sp++ == sep) + return sp; + + stderror(ERR_COMPMIS, sep, msg, short2str(str)); + return --sp; +} /* end tw_dollar */ + + +/* tw_complete(): + * Return the appropriate completion for the command + * + * valid completion strings are: + * p/<range>/<completion>/[<suffix>/] positional + * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern + * C/<pattern>/<completion>/[<suffix>/] current word with pattern + * n/<pattern>/<completion>/[<suffix>/] next word + * N/<pattern>/<completion>/[<suffix>/] next-next word + */ +int +tw_complete(line, word, pat, looking, suf) + Char *line, **word, **pat; + int looking, *suf; +{ + Char buf[MAXPATHLEN + 1], **vec, *ptr; + Char *wl[MAXPATHLEN/6]; + static Char nomatch[2] = { (Char) ~0, 0x00 }; + int wordno, n; + + copyn(buf, line, MAXPATHLEN); + + /* find the command */ + if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) + return TW_ZERO; + + /* + * look for hardwired command completions using a globbing + * search and for arguments using a normal search. + */ + if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) == NULL) + return looking; + + /* tokenize the line one more time :-( */ + for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && + wl[wordno] != INVPTR; wordno++) + continue; + + if (wl[wordno] == INVPTR) /* Found a meta character */ + return TW_ZERO; /* de-activate completions */ +#ifdef TDEBUG + { + int i; + for (i = 0; i < wordno; i++) + xprintf("'%s' ", short2str(wl[i])); + xprintf("\n"); + } +#endif /* TDEBUG */ + + /* if the current word is empty move the last word to the next */ + if (**word == '\0') { + wl[wordno] = *word; + wordno++; + } + wl[wordno] = NULL; + + +#ifdef TDEBUG + xprintf("\r\n"); + xprintf(" w#: %d\n", wordno); + xprintf("line: %s\n", short2str(line)); + xprintf(" cmd: %s\n", short2str(wl[0])); + xprintf("word: %s\n", short2str(*word)); + xprintf("last: %s\n", wordno - 2 >= 0 ? short2str(wl[wordno-2]) : "n/a"); + xprintf("this: %s\n", wordno - 1 >= 0 ? short2str(wl[wordno-1]) : "n/a"); +#endif /* TDEBUG */ + + for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { + Char ran[MAXPATHLEN+1],/* The pattern or range X/<range>/XXXX/ */ + com[MAXPATHLEN+1],/* The completion X/XXXXX/<completion>/ */ + *pos = NULL; /* scratch pointer */ + int cmd, sep; /* the command and separator characters */ + + if (ptr[0] == '\0') + continue; + +#ifdef TDEBUG + xprintf("match %s\n", short2str(ptr)); +#endif /* TDEBUG */ + + switch (cmd = ptr[0]) { + case 'N': + pos = (wordno - 3 < 0) ? nomatch : wl[wordno - 3]; + break; + case 'n': + pos = (wordno - 2 < 0) ? nomatch : wl[wordno - 2]; + break; + case 'c': + case 'C': + pos = (wordno - 1 < 0) ? nomatch : wl[wordno - 1]; + break; + case 'p': + break; + default: + stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); + return TW_ZERO; + } + + sep = ptr[1]; + if (!Ispunct(sep)) { + stderror(ERR_COMPINV, CGETS(27, 2, "separator"), sep); + return TW_ZERO; + } + + ptr = tw_dollar(&ptr[2], wl, wordno, ran, sep, + CGETS(27, 3, "pattern")); + if (ran[0] == '\0') /* check for empty pattern (disallowed) */ + { + stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : + CGETS(27, 3, "pattern"), ""); + return TW_ZERO; + } + + ptr = tw_dollar(ptr, wl, wordno, com, sep, CGETS(27, 5, "completion")); + + if (*ptr != '\0') { + if (*ptr == sep) + *suf = ~0; + else + *suf = *ptr; + } + else + *suf = '\0'; + +#ifdef TDEBUG + xprintf("command: %c\nseparator: %c\n", cmd, sep); + xprintf("pattern: %s\n", short2str(ran)); + xprintf("completion: %s\n", short2str(com)); + xprintf("suffix: "); + switch (*suf) { + case 0: + xprintf("*auto suffix*\n"); + break; + case ~0: + xprintf("*no suffix*\n"); + break; + default: + xprintf("%c\n", *suf); + break; + } +#endif /* TDEBUG */ + + switch (cmd) { + case 'p': /* positional completion */ +#ifdef TDEBUG + xprintf("p: tw_pos(%s, %d) = ", short2str(ran), wordno - 1); + xprintf("%d\n", tw_pos(ran, wordno - 1)); +#endif /* TDEBUG */ + if (!tw_pos(ran, wordno - 1)) + continue; + return tw_result(com, pat); + + case 'N': /* match with the next-next word */ + case 'n': /* match with the next word */ + case 'c': /* match with the current word */ + case 'C': +#ifdef TDEBUG + xprintf("%c: ", cmd); +#endif /* TDEBUG */ + if ((n = tw_match(pos, ran)) < 0) + continue; + if (cmd == 'c') + *word += n; + return tw_result(com, pat); + + default: + return TW_ZERO; /* Cannot happen */ + } + } + *suf = '\0'; + return TW_ZERO; +} /* end tw_complete */ |