tw.comp.c revision 100616
1100616Smp/* $Header: /src/pub/tcsh/tw.comp.c,v 1.33 2002/06/25 19:02:11 christos Exp $ */ 259243Sobrien/* 359243Sobrien * tw.comp.c: File completion builtin 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 3459243Sobrien 35100616SmpRCSID("$Id: tw.comp.c,v 1.33 2002/06/25 19:02:11 christos Exp $") 3659243Sobrien 3759243Sobrien#include "tw.h" 3859243Sobrien#include "ed.h" 3959243Sobrien#include "tc.h" 4059243Sobrien 4159243Sobrien/* #define TDEBUG */ 4259243Sobrienstruct varent completions; 4359243Sobrien 4459243Sobrienstatic int tw_result __P((Char *, Char **)); 4559243Sobrienstatic Char **tw_find __P((Char *, struct varent *, int)); 4659243Sobrienstatic Char *tw_tok __P((Char *)); 4759243Sobrienstatic bool tw_pos __P((Char *, int)); 4859243Sobrienstatic void tw_pr __P((Char **)); 4959243Sobrienstatic int tw_match __P((Char *, Char *)); 5059243Sobrienstatic void tw_prlist __P((struct varent *)); 5159243Sobrienstatic Char *tw_dollar __P((Char *,Char **, int, Char *, 5259243Sobrien int, const char *)); 5359243Sobrien 5459243Sobrien/* docomplete(): 5559243Sobrien * Add or list completions in the completion list 5659243Sobrien */ 5759243Sobrien/*ARGSUSED*/ 5859243Sobrienvoid 5959243Sobriendocomplete(v, t) 6059243Sobrien Char **v; 6159243Sobrien struct command *t; 6259243Sobrien{ 6359243Sobrien register struct varent *vp; 6459243Sobrien register Char *p; 6559243Sobrien 6659243Sobrien USE(t); 6759243Sobrien v++; 6859243Sobrien p = *v++; 6959243Sobrien if (p == 0) 7059243Sobrien tw_prlist(&completions); 7159243Sobrien else if (*v == 0) { 7259243Sobrien vp = adrof1(strip(p), &completions); 73100616Smp if (vp && vp->vec) 7459243Sobrien tw_pr(vp->vec), xputchar('\n'); 7559243Sobrien } 7659243Sobrien else 7759243Sobrien set1(strip(p), saveblk(v), &completions, VAR_READWRITE); 7859243Sobrien} /* end docomplete */ 7959243Sobrien 8059243Sobrien 8159243Sobrien/* douncomplete(): 8259243Sobrien * Remove completions from the completion list 8359243Sobrien */ 8459243Sobrien/*ARGSUSED*/ 8559243Sobrienvoid 8659243Sobriendouncomplete(v, t) 8759243Sobrien Char **v; 8859243Sobrien struct command *t; 8959243Sobrien{ 9059243Sobrien USE(t); 9159243Sobrien unset1(v, &completions); 9259243Sobrien} /* end douncomplete */ 9359243Sobrien 9459243Sobrien 9559243Sobrien/* tw_prlist(): 9659243Sobrien * Pretty print a list of variables 9759243Sobrien */ 9859243Sobrienstatic void 9959243Sobrientw_prlist(p) 10059243Sobrien struct varent *p; 10159243Sobrien{ 10259243Sobrien register struct varent *c; 10359243Sobrien 10459243Sobrien if (setintr) 10559243Sobrien#ifdef BSDSIGS 10659243Sobrien (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT)); 10759243Sobrien#else /* BSDSIGS */ 10859243Sobrien (void) sigrelse(SIGINT); 10959243Sobrien#endif /* BSDSIGS */ 11059243Sobrien 11159243Sobrien for (;;) { 11259243Sobrien while (p->v_left) 11359243Sobrien p = p->v_left; 11459243Sobrienx: 11559243Sobrien if (p->v_parent == 0) /* is it the header? */ 11659243Sobrien return; 11759243Sobrien xprintf("%s\t", short2str(p->v_name)); 118100616Smp if (p->vec) 119100616Smp tw_pr(p->vec); 12059243Sobrien xputchar('\n'); 12159243Sobrien if (p->v_right) { 12259243Sobrien p = p->v_right; 12359243Sobrien continue; 12459243Sobrien } 12559243Sobrien do { 12659243Sobrien c = p; 12759243Sobrien p = p->v_parent; 12859243Sobrien } while (p->v_right == c); 12959243Sobrien goto x; 13059243Sobrien } 13159243Sobrien} /* end tw_prlist */ 13259243Sobrien 13359243Sobrien 13459243Sobrien/* tw_pr(): 13559243Sobrien * Pretty print a completion, adding single quotes around 13659243Sobrien * a completion argument and collapsing multiple spaces to one. 13759243Sobrien */ 13859243Sobrienstatic void 13959243Sobrientw_pr(cmp) 14059243Sobrien Char **cmp; 14159243Sobrien{ 14259243Sobrien bool sp, osp; 14359243Sobrien Char *ptr; 14459243Sobrien 14559243Sobrien for (; *cmp; cmp++) { 14659243Sobrien xputchar('\''); 14759243Sobrien for (osp = 0, ptr = *cmp; *ptr; ptr++) { 14859243Sobrien sp = Isspace(*ptr); 14959243Sobrien if (sp && osp) 15059243Sobrien continue; 15159243Sobrien xputchar(*ptr); 15259243Sobrien osp = sp; 15359243Sobrien } 15459243Sobrien xputchar('\''); 15559243Sobrien if (cmp[1]) 15659243Sobrien xputchar(' '); 15759243Sobrien } 15859243Sobrien} /* end tw_pr */ 15959243Sobrien 16059243Sobrien 16159243Sobrien/* tw_find(): 16259243Sobrien * Find the first matching completion. 16359243Sobrien * For commands we only look at names that start with - 16459243Sobrien */ 16559243Sobrienstatic Char ** 16659243Sobrientw_find(nam, vp, cmd) 16759243Sobrien Char *nam; 16859243Sobrien register struct varent *vp; 16959243Sobrien int cmd; 17059243Sobrien{ 17159243Sobrien register Char **rv; 17259243Sobrien 17359243Sobrien for (vp = vp->v_left; vp; vp = vp->v_right) { 17459243Sobrien if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL) 17559243Sobrien return rv; 17659243Sobrien if (cmd) { 17759243Sobrien if (vp->v_name[0] != '-') 17859243Sobrien continue; 17959243Sobrien if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL) 18059243Sobrien return vp->vec; 18159243Sobrien } 18259243Sobrien else 18359243Sobrien if (Gmatch(nam, vp->v_name) && vp->vec != NULL) 18459243Sobrien return vp->vec; 18559243Sobrien } 18659243Sobrien return NULL; 18759243Sobrien} /* end tw_find */ 18859243Sobrien 18959243Sobrien 19059243Sobrien/* tw_pos(): 19159243Sobrien * Return true if the position is within the specified range 19259243Sobrien */ 19359243Sobrienstatic bool 19459243Sobrientw_pos(ran, wno) 19559243Sobrien Char *ran; 19659243Sobrien int wno; 19759243Sobrien{ 19859243Sobrien Char *p; 19959243Sobrien 20059243Sobrien if (ran[0] == '*' && ran[1] == '\0') 20159243Sobrien return 1; 20259243Sobrien 20359243Sobrien for (p = ran; *p && *p != '-'; p++) 20459243Sobrien continue; 20559243Sobrien 20659243Sobrien if (*p == '\0') /* range == <number> */ 20759243Sobrien return wno == getn(ran); 20859243Sobrien 20959243Sobrien if (ran == p) /* range = - <number> */ 21059243Sobrien return wno <= getn(&ran[1]); 21159243Sobrien *p++ = '\0'; 21259243Sobrien 21359243Sobrien if (*p == '\0') /* range = <number> - */ 21459243Sobrien return getn(ran) <= wno; 21559243Sobrien else /* range = <number> - <number> */ 21659243Sobrien return (getn(ran) <= wno) && (wno <= getn(p)); 21759243Sobrien 21859243Sobrien} /* end tw_pos */ 21959243Sobrien 22059243Sobrien 22159243Sobrien/* tw_tok(): 22259243Sobrien * Return the next word from string, unquoteing it. 22359243Sobrien */ 22459243Sobrienstatic Char * 22559243Sobrientw_tok(str) 22659243Sobrien Char *str; 22759243Sobrien{ 22859243Sobrien static Char *bf = NULL; 22959243Sobrien 23059243Sobrien if (str != NULL) 23159243Sobrien bf = str; 23259243Sobrien 23359243Sobrien /* skip leading spaces */ 23459243Sobrien for (; *bf && Isspace(*bf); bf++) 23559243Sobrien continue; 23659243Sobrien 23759243Sobrien for (str = bf; *bf && !Isspace(*bf); bf++) { 23859243Sobrien if (ismeta(*bf)) 23959243Sobrien return INVPTR; 24059243Sobrien *bf = *bf & ~QUOTE; 24159243Sobrien } 24259243Sobrien if (*bf != '\0') 24359243Sobrien *bf++ = '\0'; 24459243Sobrien 24559243Sobrien return *str ? str : NULL; 24659243Sobrien} /* end tw_tok */ 24759243Sobrien 24859243Sobrien 24959243Sobrien/* tw_match(): 25059243Sobrien * Match a string against the pattern given. 25159243Sobrien * and return the number of matched characters 25259243Sobrien * in a prefix of the string. 25359243Sobrien */ 25459243Sobrienstatic int 25559243Sobrientw_match(str, pat) 25659243Sobrien Char *str, *pat; 25759243Sobrien{ 25859243Sobrien Char *estr; 25959243Sobrien int rv = Gnmatch(str, pat, &estr); 26059243Sobrien#ifdef TDEBUG 26159243Sobrien xprintf("Gnmatch(%s, ", short2str(str)); 26259243Sobrien xprintf("%s, ", short2str(pat)); 26359243Sobrien xprintf("%s) = %d [%d]\n", short2str(estr), rv, estr - str); 26459243Sobrien#endif /* TDEBUG */ 26559243Sobrien return (int) (rv ? estr - str : -1); 26659243Sobrien} 26759243Sobrien 26859243Sobrien 26959243Sobrien/* tw_result(): 27059243Sobrien * Return what the completion action should be depending on the 27159243Sobrien * string 27259243Sobrien */ 27359243Sobrienstatic int 27459243Sobrientw_result(act, pat) 27559243Sobrien Char *act, **pat; 27659243Sobrien{ 27759243Sobrien int looking; 27859243Sobrien static Char* res = NULL; 27959243Sobrien 28059243Sobrien if (res != NULL) 28159243Sobrien xfree((ptr_t) res), res = NULL; 28259243Sobrien 28359243Sobrien switch (act[0] & ~QUOTE) { 28459243Sobrien case 'X': 28559243Sobrien looking = TW_COMPLETION; 28659243Sobrien break; 28759243Sobrien case 'S': 28859243Sobrien looking = TW_SIGNAL; 28959243Sobrien break; 29059243Sobrien case 'a': 29159243Sobrien looking = TW_ALIAS; 29259243Sobrien break; 29359243Sobrien case 'b': 29459243Sobrien looking = TW_BINDING; 29559243Sobrien break; 29659243Sobrien case 'c': 29759243Sobrien looking = TW_COMMAND; 29859243Sobrien break; 29959243Sobrien case 'C': 30059243Sobrien looking = TW_PATH | TW_COMMAND; 30159243Sobrien break; 30259243Sobrien case 'd': 30359243Sobrien looking = TW_DIRECTORY; 30459243Sobrien break; 30559243Sobrien case 'D': 30659243Sobrien looking = TW_PATH | TW_DIRECTORY; 30759243Sobrien break; 30859243Sobrien case 'e': 30959243Sobrien looking = TW_ENVVAR; 31059243Sobrien break; 31159243Sobrien case 'f': 31259243Sobrien looking = TW_FILE; 31359243Sobrien break; 31459243Sobrien#ifdef COMPAT 31559243Sobrien case 'p': 31659243Sobrien#endif /* COMPAT */ 31759243Sobrien case 'F': 31859243Sobrien looking = TW_PATH | TW_FILE; 31959243Sobrien break; 32059243Sobrien case 'g': 32159243Sobrien looking = TW_GRPNAME; 32259243Sobrien break; 32359243Sobrien case 'j': 32459243Sobrien looking = TW_JOB; 32559243Sobrien break; 32659243Sobrien case 'l': 32759243Sobrien looking = TW_LIMIT; 32859243Sobrien break; 32959243Sobrien case 'n': 33059243Sobrien looking = TW_NONE; 33159243Sobrien break; 33259243Sobrien case 's': 33359243Sobrien looking = TW_SHELLVAR; 33459243Sobrien break; 33559243Sobrien case 't': 33659243Sobrien looking = TW_TEXT; 33759243Sobrien break; 33859243Sobrien case 'T': 33959243Sobrien looking = TW_PATH | TW_TEXT; 34059243Sobrien break; 34159243Sobrien case 'v': 34259243Sobrien looking = TW_VARIABLE; 34359243Sobrien break; 34459243Sobrien case 'u': 34559243Sobrien looking = TW_USER; 34659243Sobrien break; 34759243Sobrien case 'x': 34859243Sobrien looking = TW_EXPLAIN; 34959243Sobrien break; 35059243Sobrien 35159243Sobrien case '$': 35259243Sobrien *pat = res = Strsave(&act[1]); 35359243Sobrien (void) strip(res); 35459243Sobrien return(TW_VARLIST); 35559243Sobrien 35659243Sobrien case '(': 35759243Sobrien *pat = res = Strsave(&act[1]); 35859243Sobrien if ((act = Strchr(res, ')')) != NULL) 35959243Sobrien *act = '\0'; 36059243Sobrien (void) strip(res); 36159243Sobrien return TW_WORDLIST; 36259243Sobrien 36359243Sobrien case '`': 36459243Sobrien res = Strsave(act); 36559243Sobrien if ((act = Strchr(&res[1], '`')) != NULL) 36659243Sobrien *++act = '\0'; 36759243Sobrien 36859243Sobrien if (didfds == 0) { 36959243Sobrien /* 37059243Sobrien * Make sure that we have some file descriptors to 37159243Sobrien * play with, so that the processes have at least 0, 1, 2 37259243Sobrien * open 37359243Sobrien */ 37459243Sobrien (void) dcopy(SHIN, 0); 37559243Sobrien (void) dcopy(SHOUT, 1); 37659243Sobrien (void) dcopy(SHDIAG, 2); 37759243Sobrien } 37859243Sobrien if ((act = globone(res, G_APPEND)) != NULL) { 37959243Sobrien xfree((ptr_t) res), res = NULL; 38059243Sobrien *pat = res = Strsave(act); 38159243Sobrien xfree((ptr_t) act); 38259243Sobrien return TW_WORDLIST; 38359243Sobrien } 38459243Sobrien return TW_ZERO; 38559243Sobrien 38659243Sobrien default: 38759243Sobrien stderror(ERR_COMPCOM, short2str(act)); 38859243Sobrien return TW_ZERO; 38959243Sobrien } 39059243Sobrien 39159243Sobrien switch (act[1] & ~QUOTE) { 39259243Sobrien case '\0': 39359243Sobrien return looking; 39459243Sobrien 39559243Sobrien case ':': 39659243Sobrien *pat = res = Strsave(&act[2]); 39759243Sobrien (void) strip(res); 39859243Sobrien return looking; 39959243Sobrien 40059243Sobrien default: 40159243Sobrien stderror(ERR_COMPCOM, short2str(act)); 40259243Sobrien return TW_ZERO; 40359243Sobrien } 40459243Sobrien} /* end tw_result */ 40559243Sobrien 40659243Sobrien 40759243Sobrien/* tw_dollar(): 40859243Sobrien * Expand $<n> args in buffer 40959243Sobrien */ 41059243Sobrienstatic Char * 41159243Sobrientw_dollar(str, wl, nwl, buffer, sep, msg) 41259243Sobrien Char *str, **wl; 41359243Sobrien int nwl; 41459243Sobrien Char *buffer; 41559243Sobrien int sep; 41659243Sobrien const char *msg; 41759243Sobrien{ 41859243Sobrien Char *sp, *bp = buffer, *ebp = &buffer[MAXPATHLEN]; 41959243Sobrien 42059243Sobrien for (sp = str; *sp && *sp != sep && bp < ebp;) 42159243Sobrien if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { 42259243Sobrien int num, neg = 0; 42359243Sobrien sp += 2; 42459243Sobrien if (*sp == '-') { 42559243Sobrien neg = 1; 42659243Sobrien sp++; 42759243Sobrien } 42859243Sobrien for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') 42959243Sobrien continue; 43059243Sobrien if (neg) 43159243Sobrien num = nwl - num - 1; 43259243Sobrien if (num >= 0 && num < nwl) { 43359243Sobrien Char *ptr; 43459243Sobrien for (ptr = wl[num]; *ptr && bp < ebp - 1; *bp++ = *ptr++) 43559243Sobrien continue; 43659243Sobrien 43759243Sobrien } 43859243Sobrien } 43959243Sobrien else 44059243Sobrien *bp++ = *sp++; 44159243Sobrien 44259243Sobrien *bp = '\0'; 44359243Sobrien 44459243Sobrien if (*sp++ == sep) 44559243Sobrien return sp; 44659243Sobrien 44759243Sobrien stderror(ERR_COMPMIS, sep, msg, short2str(str)); 44859243Sobrien return --sp; 44959243Sobrien} /* end tw_dollar */ 45059243Sobrien 45159243Sobrien 45259243Sobrien/* tw_complete(): 45359243Sobrien * Return the appropriate completion for the command 45459243Sobrien * 45559243Sobrien * valid completion strings are: 45659243Sobrien * p/<range>/<completion>/[<suffix>/] positional 45759243Sobrien * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern 45859243Sobrien * C/<pattern>/<completion>/[<suffix>/] current word with pattern 45959243Sobrien * n/<pattern>/<completion>/[<suffix>/] next word 46059243Sobrien * N/<pattern>/<completion>/[<suffix>/] next-next word 46159243Sobrien */ 46259243Sobrienint 46359243Sobrientw_complete(line, word, pat, looking, suf) 46459243Sobrien Char *line, **word, **pat; 46559243Sobrien int looking, *suf; 46659243Sobrien{ 46759243Sobrien Char buf[MAXPATHLEN + 1], **vec, *ptr; 46859243Sobrien Char *wl[MAXPATHLEN/6]; 46959243Sobrien static Char nomatch[2] = { (Char) ~0, 0x00 }; 47059243Sobrien int wordno, n; 47159243Sobrien 47259243Sobrien copyn(buf, line, MAXPATHLEN); 47359243Sobrien 47459243Sobrien /* find the command */ 47559243Sobrien if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) 47659243Sobrien return TW_ZERO; 47759243Sobrien 47859243Sobrien /* 47959243Sobrien * look for hardwired command completions using a globbing 48059243Sobrien * search and for arguments using a normal search. 48159243Sobrien */ 48259243Sobrien if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) == NULL) 48359243Sobrien return looking; 48459243Sobrien 48559243Sobrien /* tokenize the line one more time :-( */ 48659243Sobrien for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && 48759243Sobrien wl[wordno] != INVPTR; wordno++) 48859243Sobrien continue; 48959243Sobrien 49059243Sobrien if (wl[wordno] == INVPTR) /* Found a meta character */ 49159243Sobrien return TW_ZERO; /* de-activate completions */ 49259243Sobrien#ifdef TDEBUG 49359243Sobrien { 49459243Sobrien int i; 49559243Sobrien for (i = 0; i < wordno; i++) 49659243Sobrien xprintf("'%s' ", short2str(wl[i])); 49759243Sobrien xprintf("\n"); 49859243Sobrien } 49959243Sobrien#endif /* TDEBUG */ 50059243Sobrien 50159243Sobrien /* if the current word is empty move the last word to the next */ 50259243Sobrien if (**word == '\0') { 50359243Sobrien wl[wordno] = *word; 50459243Sobrien wordno++; 50559243Sobrien } 50659243Sobrien wl[wordno] = NULL; 50759243Sobrien 50859243Sobrien 50959243Sobrien#ifdef TDEBUG 51059243Sobrien xprintf("\r\n"); 51159243Sobrien xprintf(" w#: %d\n", wordno); 51259243Sobrien xprintf("line: %s\n", short2str(line)); 51359243Sobrien xprintf(" cmd: %s\n", short2str(wl[0])); 51459243Sobrien xprintf("word: %s\n", short2str(*word)); 51559243Sobrien xprintf("last: %s\n", wordno - 2 >= 0 ? short2str(wl[wordno-2]) : "n/a"); 51659243Sobrien xprintf("this: %s\n", wordno - 1 >= 0 ? short2str(wl[wordno-1]) : "n/a"); 51759243Sobrien#endif /* TDEBUG */ 51859243Sobrien 51959243Sobrien for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { 52059243Sobrien Char ran[MAXPATHLEN+1],/* The pattern or range X/<range>/XXXX/ */ 52159243Sobrien com[MAXPATHLEN+1],/* The completion X/XXXXX/<completion>/ */ 52259243Sobrien *pos = NULL; /* scratch pointer */ 52359243Sobrien int cmd, sep; /* the command and separator characters */ 52459243Sobrien 52559243Sobrien if (ptr[0] == '\0') 52659243Sobrien continue; 52759243Sobrien 52859243Sobrien#ifdef TDEBUG 52959243Sobrien xprintf("match %s\n", short2str(ptr)); 53059243Sobrien#endif /* TDEBUG */ 53159243Sobrien 53259243Sobrien switch (cmd = ptr[0]) { 53359243Sobrien case 'N': 53459243Sobrien pos = (wordno - 3 < 0) ? nomatch : wl[wordno - 3]; 53559243Sobrien break; 53659243Sobrien case 'n': 53759243Sobrien pos = (wordno - 2 < 0) ? nomatch : wl[wordno - 2]; 53859243Sobrien break; 53959243Sobrien case 'c': 54059243Sobrien case 'C': 54159243Sobrien pos = (wordno - 1 < 0) ? nomatch : wl[wordno - 1]; 54259243Sobrien break; 54359243Sobrien case 'p': 54459243Sobrien break; 54559243Sobrien default: 54659243Sobrien stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); 54759243Sobrien return TW_ZERO; 54859243Sobrien } 54959243Sobrien 55059243Sobrien sep = ptr[1]; 55159243Sobrien if (!Ispunct(sep)) { 55259243Sobrien stderror(ERR_COMPINV, CGETS(27, 2, "separator"), sep); 55359243Sobrien return TW_ZERO; 55459243Sobrien } 55559243Sobrien 55659243Sobrien ptr = tw_dollar(&ptr[2], wl, wordno, ran, sep, 55759243Sobrien CGETS(27, 3, "pattern")); 55859243Sobrien if (ran[0] == '\0') /* check for empty pattern (disallowed) */ 55959243Sobrien { 56059243Sobrien stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : 56159243Sobrien CGETS(27, 3, "pattern"), ""); 56259243Sobrien return TW_ZERO; 56359243Sobrien } 56459243Sobrien 56559243Sobrien ptr = tw_dollar(ptr, wl, wordno, com, sep, CGETS(27, 5, "completion")); 56659243Sobrien 56759243Sobrien if (*ptr != '\0') { 56859243Sobrien if (*ptr == sep) 56959243Sobrien *suf = ~0; 57059243Sobrien else 57159243Sobrien *suf = *ptr; 57259243Sobrien } 57359243Sobrien else 57459243Sobrien *suf = '\0'; 57559243Sobrien 57659243Sobrien#ifdef TDEBUG 57759243Sobrien xprintf("command: %c\nseparator: %c\n", cmd, sep); 57859243Sobrien xprintf("pattern: %s\n", short2str(ran)); 57959243Sobrien xprintf("completion: %s\n", short2str(com)); 58059243Sobrien xprintf("suffix: "); 58159243Sobrien switch (*suf) { 58259243Sobrien case 0: 58359243Sobrien xprintf("*auto suffix*\n"); 58459243Sobrien break; 58559243Sobrien case ~0: 58659243Sobrien xprintf("*no suffix*\n"); 58759243Sobrien break; 58859243Sobrien default: 58959243Sobrien xprintf("%c\n", *suf); 59059243Sobrien break; 59159243Sobrien } 59259243Sobrien#endif /* TDEBUG */ 59359243Sobrien 59459243Sobrien switch (cmd) { 59559243Sobrien case 'p': /* positional completion */ 59659243Sobrien#ifdef TDEBUG 59759243Sobrien xprintf("p: tw_pos(%s, %d) = ", short2str(ran), wordno - 1); 59859243Sobrien xprintf("%d\n", tw_pos(ran, wordno - 1)); 59959243Sobrien#endif /* TDEBUG */ 60059243Sobrien if (!tw_pos(ran, wordno - 1)) 60159243Sobrien continue; 60259243Sobrien return tw_result(com, pat); 60359243Sobrien 60459243Sobrien case 'N': /* match with the next-next word */ 60559243Sobrien case 'n': /* match with the next word */ 60659243Sobrien case 'c': /* match with the current word */ 60759243Sobrien case 'C': 60859243Sobrien#ifdef TDEBUG 60959243Sobrien xprintf("%c: ", cmd); 61059243Sobrien#endif /* TDEBUG */ 61159243Sobrien if ((n = tw_match(pos, ran)) < 0) 61259243Sobrien continue; 61359243Sobrien if (cmd == 'c') 61459243Sobrien *word += n; 61559243Sobrien return tw_result(com, pat); 61659243Sobrien 61759243Sobrien default: 61859243Sobrien return TW_ZERO; /* Cannot happen */ 61959243Sobrien } 62059243Sobrien } 62159243Sobrien *suf = '\0'; 62259243Sobrien return TW_ZERO; 62359243Sobrien} /* end tw_complete */ 624