159243Sobrien/* 259243Sobrien * tw.comp.c: File completion builtin 359243Sobrien */ 459243Sobrien/*- 559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 16100616Smp * 3. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien#include "sh.h" 3359243Sobrien#include "tw.h" 3459243Sobrien#include "ed.h" 3559243Sobrien#include "tc.h" 3659243Sobrien 3759243Sobrien/* #define TDEBUG */ 3859243Sobrienstruct varent completions; 3959243Sobrien 40167465Smpstatic int tw_result (const Char *, Char **); 41167465Smpstatic Char **tw_find (Char *, struct varent *, int); 42167465Smpstatic Char *tw_tok (Char *); 43167465Smpstatic int tw_pos (Char *, int); 44167465Smpstatic void tw_pr (Char **); 45316957Sdchaginstatic int tw_match (const Char *, const Char *, int); 46167465Smpstatic void tw_prlist (struct varent *); 47167465Smpstatic const Char *tw_dollar (const Char *,Char **, size_t, Char **, 48167465Smp Char, const char *); 4959243Sobrien 5059243Sobrien/* docomplete(): 5159243Sobrien * Add or list completions in the completion list 5259243Sobrien */ 5359243Sobrien/*ARGSUSED*/ 5459243Sobrienvoid 55167465Smpdocomplete(Char **v, struct command *t) 5659243Sobrien{ 57145479Smp struct varent *vp; 58145479Smp Char *p; 59131962Smp Char **pp; 6059243Sobrien 6159243Sobrien USE(t); 6259243Sobrien v++; 6359243Sobrien p = *v++; 6459243Sobrien if (p == 0) 6559243Sobrien tw_prlist(&completions); 6659243Sobrien else if (*v == 0) { 6759243Sobrien vp = adrof1(strip(p), &completions); 68100616Smp if (vp && vp->vec) 6959243Sobrien tw_pr(vp->vec), xputchar('\n'); 70131962Smp else 71131962Smp { 72131962Smp#ifdef TDEBUG 73131962Smp xprintf("tw_find(%s) \n", short2str(strip(p))); 74131962Smp#endif /* TDEBUG */ 75131962Smp pp = tw_find(strip(p), &completions, FALSE); 76131962Smp if (pp) 77131962Smp tw_pr(pp), xputchar('\n'); 78131962Smp } 7959243Sobrien } 8059243Sobrien else 8159243Sobrien set1(strip(p), saveblk(v), &completions, VAR_READWRITE); 8259243Sobrien} /* end docomplete */ 8359243Sobrien 8459243Sobrien 8559243Sobrien/* douncomplete(): 8659243Sobrien * Remove completions from the completion list 8759243Sobrien */ 8859243Sobrien/*ARGSUSED*/ 8959243Sobrienvoid 90167465Smpdouncomplete(Char **v, struct command *t) 9159243Sobrien{ 9259243Sobrien USE(t); 9359243Sobrien unset1(v, &completions); 9459243Sobrien} /* end douncomplete */ 9559243Sobrien 9659243Sobrien 9759243Sobrien/* tw_prlist(): 9859243Sobrien * Pretty print a list of variables 9959243Sobrien */ 10059243Sobrienstatic void 101167465Smptw_prlist(struct varent *p) 10259243Sobrien{ 103145479Smp struct varent *c; 10459243Sobrien 10559243Sobrien for (;;) { 10659243Sobrien while (p->v_left) 10759243Sobrien p = p->v_left; 10859243Sobrienx: 10959243Sobrien if (p->v_parent == 0) /* is it the header? */ 110167465Smp break; 111167465Smp if (setintr) { 112167465Smp int old_pintr_disabled; 113167465Smp 114167465Smp pintr_push_enable(&old_pintr_disabled); 115167465Smp cleanup_until(&old_pintr_disabled); 116167465Smp } 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 139167465Smptw_pr(Char **cmp) 14059243Sobrien{ 141145479Smp int sp, osp; 14259243Sobrien Char *ptr; 14359243Sobrien 14459243Sobrien for (; *cmp; cmp++) { 14559243Sobrien xputchar('\''); 14659243Sobrien for (osp = 0, ptr = *cmp; *ptr; ptr++) { 14759243Sobrien sp = Isspace(*ptr); 14859243Sobrien if (sp && osp) 14959243Sobrien continue; 150145479Smp xputwchar(*ptr); 15159243Sobrien osp = sp; 15259243Sobrien } 15359243Sobrien xputchar('\''); 15459243Sobrien if (cmp[1]) 15559243Sobrien xputchar(' '); 15659243Sobrien } 15759243Sobrien} /* end tw_pr */ 15859243Sobrien 15959243Sobrien 16059243Sobrien/* tw_find(): 16159243Sobrien * Find the first matching completion. 16259243Sobrien * For commands we only look at names that start with - 16359243Sobrien */ 16459243Sobrienstatic Char ** 165167465Smptw_find(Char *nam, struct varent *vp, int cmd) 16659243Sobrien{ 167145479Smp Char **rv; 16859243Sobrien 16959243Sobrien for (vp = vp->v_left; vp; vp = vp->v_right) { 17059243Sobrien if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL) 17159243Sobrien return rv; 17259243Sobrien if (cmd) { 17359243Sobrien if (vp->v_name[0] != '-') 17459243Sobrien continue; 17559243Sobrien if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL) 17659243Sobrien return vp->vec; 17759243Sobrien } 17859243Sobrien else 17959243Sobrien if (Gmatch(nam, vp->v_name) && vp->vec != NULL) 18059243Sobrien return vp->vec; 18159243Sobrien } 18259243Sobrien return NULL; 18359243Sobrien} /* end tw_find */ 18459243Sobrien 18559243Sobrien 18659243Sobrien/* tw_pos(): 18759243Sobrien * Return true if the position is within the specified range 18859243Sobrien */ 189145479Smpstatic int 190167465Smptw_pos(Char *ran, int wno) 19159243Sobrien{ 19259243Sobrien Char *p; 19359243Sobrien 19459243Sobrien if (ran[0] == '*' && ran[1] == '\0') 19559243Sobrien return 1; 19659243Sobrien 19759243Sobrien for (p = ran; *p && *p != '-'; p++) 19859243Sobrien continue; 19959243Sobrien 20059243Sobrien if (*p == '\0') /* range == <number> */ 20159243Sobrien return wno == getn(ran); 20259243Sobrien 20359243Sobrien if (ran == p) /* range = - <number> */ 20459243Sobrien return wno <= getn(&ran[1]); 20559243Sobrien *p++ = '\0'; 20659243Sobrien 20759243Sobrien if (*p == '\0') /* range = <number> - */ 20859243Sobrien return getn(ran) <= wno; 20959243Sobrien else /* range = <number> - <number> */ 21059243Sobrien return (getn(ran) <= wno) && (wno <= getn(p)); 21159243Sobrien} /* end tw_pos */ 21259243Sobrien 21359243Sobrien 21459243Sobrien/* tw_tok(): 21559243Sobrien * Return the next word from string, unquoteing it. 21659243Sobrien */ 21759243Sobrienstatic Char * 218167465Smptw_tok(Char *str) 21959243Sobrien{ 22059243Sobrien static Char *bf = NULL; 22159243Sobrien 22259243Sobrien if (str != NULL) 22359243Sobrien bf = str; 22459243Sobrien 22559243Sobrien /* skip leading spaces */ 22659243Sobrien for (; *bf && Isspace(*bf); bf++) 22759243Sobrien continue; 22859243Sobrien 22959243Sobrien for (str = bf; *bf && !Isspace(*bf); bf++) { 230145479Smp if (ismetahash(*bf)) 23159243Sobrien return INVPTR; 23259243Sobrien *bf = *bf & ~QUOTE; 23359243Sobrien } 23459243Sobrien if (*bf != '\0') 23559243Sobrien *bf++ = '\0'; 23659243Sobrien 23759243Sobrien return *str ? str : NULL; 23859243Sobrien} /* end tw_tok */ 23959243Sobrien 24059243Sobrien 24159243Sobrien/* tw_match(): 24259243Sobrien * Match a string against the pattern given. 24359243Sobrien * and return the number of matched characters 24459243Sobrien * in a prefix of the string. 24559243Sobrien */ 24659243Sobrienstatic int 247316957Sdchagintw_match(const Char *str, const Char *pat, int exact) 24859243Sobrien{ 249167465Smp const Char *estr; 250316957Sdchagin int rv = exact ? Gmatch(estr = str, pat) : Gnmatch(str, pat, &estr); 25159243Sobrien#ifdef TDEBUG 252316957Sdchagin xprintf("G%smatch(%s, ", exact ? "" : "n", short2str(str)); 25359243Sobrien xprintf("%s, ", short2str(pat)); 254316957Sdchagin xprintf("%s) = %d [%" TCSH_PTRDIFF_T_FMT "d]\n", short2str(estr), rv, 255316957Sdchagin estr - str); 25659243Sobrien#endif /* TDEBUG */ 25759243Sobrien return (int) (rv ? estr - str : -1); 25859243Sobrien} 25959243Sobrien 26059243Sobrien 26159243Sobrien/* tw_result(): 26259243Sobrien * Return what the completion action should be depending on the 26359243Sobrien * string 26459243Sobrien */ 26559243Sobrienstatic int 266167465Smptw_result(const Char *act, Char **pat) 26759243Sobrien{ 26859243Sobrien int looking; 26959243Sobrien static Char* res = NULL; 270167465Smp Char *p; 27159243Sobrien 27259243Sobrien if (res != NULL) 273167465Smp xfree(res), res = NULL; 27459243Sobrien 27559243Sobrien switch (act[0] & ~QUOTE) { 27659243Sobrien case 'X': 27759243Sobrien looking = TW_COMPLETION; 27859243Sobrien break; 27959243Sobrien case 'S': 28059243Sobrien looking = TW_SIGNAL; 28159243Sobrien break; 28259243Sobrien case 'a': 28359243Sobrien looking = TW_ALIAS; 28459243Sobrien break; 28559243Sobrien case 'b': 28659243Sobrien looking = TW_BINDING; 28759243Sobrien break; 28859243Sobrien case 'c': 28959243Sobrien looking = TW_COMMAND; 29059243Sobrien break; 29159243Sobrien case 'C': 29259243Sobrien looking = TW_PATH | TW_COMMAND; 29359243Sobrien break; 29459243Sobrien case 'd': 29559243Sobrien looking = TW_DIRECTORY; 29659243Sobrien break; 29759243Sobrien case 'D': 29859243Sobrien looking = TW_PATH | TW_DIRECTORY; 29959243Sobrien break; 30059243Sobrien case 'e': 30159243Sobrien looking = TW_ENVVAR; 30259243Sobrien break; 30359243Sobrien case 'f': 30459243Sobrien looking = TW_FILE; 30559243Sobrien break; 30659243Sobrien#ifdef COMPAT 30759243Sobrien case 'p': 30859243Sobrien#endif /* COMPAT */ 30959243Sobrien case 'F': 31059243Sobrien looking = TW_PATH | TW_FILE; 31159243Sobrien break; 31259243Sobrien case 'g': 31359243Sobrien looking = TW_GRPNAME; 31459243Sobrien break; 31559243Sobrien case 'j': 31659243Sobrien looking = TW_JOB; 31759243Sobrien break; 31859243Sobrien case 'l': 31959243Sobrien looking = TW_LIMIT; 32059243Sobrien break; 32159243Sobrien case 'n': 32259243Sobrien looking = TW_NONE; 32359243Sobrien break; 32459243Sobrien case 's': 32559243Sobrien looking = TW_SHELLVAR; 32659243Sobrien break; 32759243Sobrien case 't': 32859243Sobrien looking = TW_TEXT; 32959243Sobrien break; 33059243Sobrien case 'T': 33159243Sobrien looking = TW_PATH | TW_TEXT; 33259243Sobrien break; 33359243Sobrien case 'v': 33459243Sobrien looking = TW_VARIABLE; 33559243Sobrien break; 33659243Sobrien case 'u': 33759243Sobrien looking = TW_USER; 33859243Sobrien break; 33959243Sobrien case 'x': 34059243Sobrien looking = TW_EXPLAIN; 34159243Sobrien break; 34259243Sobrien 34359243Sobrien case '$': 34459243Sobrien *pat = res = Strsave(&act[1]); 34559243Sobrien (void) strip(res); 34659243Sobrien return(TW_VARLIST); 34759243Sobrien 34859243Sobrien case '(': 34959243Sobrien *pat = res = Strsave(&act[1]); 350167465Smp if ((p = Strchr(res, ')')) != NULL) 351167465Smp *p = '\0'; 35259243Sobrien (void) strip(res); 35359243Sobrien return TW_WORDLIST; 35459243Sobrien 35559243Sobrien case '`': 35659243Sobrien res = Strsave(act); 357167465Smp if ((p = Strchr(&res[1], '`')) != NULL) 358167465Smp *++p = '\0'; 35959243Sobrien 36059243Sobrien if (didfds == 0) { 36159243Sobrien /* 36259243Sobrien * Make sure that we have some file descriptors to 36359243Sobrien * play with, so that the processes have at least 0, 1, 2 36459243Sobrien * open 36559243Sobrien */ 36659243Sobrien (void) dcopy(SHIN, 0); 36759243Sobrien (void) dcopy(SHOUT, 1); 36859243Sobrien (void) dcopy(SHDIAG, 2); 36959243Sobrien } 370167465Smp if ((p = globone(res, G_APPEND)) != NULL) { 371167465Smp xfree(res), res = NULL; 372167465Smp *pat = res = Strsave(p); 373167465Smp xfree(p); 37459243Sobrien return TW_WORDLIST; 37559243Sobrien } 37659243Sobrien return TW_ZERO; 37759243Sobrien 37859243Sobrien default: 37959243Sobrien stderror(ERR_COMPCOM, short2str(act)); 38059243Sobrien return TW_ZERO; 38159243Sobrien } 38259243Sobrien 38359243Sobrien switch (act[1] & ~QUOTE) { 38459243Sobrien case '\0': 38559243Sobrien return looking; 38659243Sobrien 38759243Sobrien case ':': 38859243Sobrien *pat = res = Strsave(&act[2]); 38959243Sobrien (void) strip(res); 39059243Sobrien return looking; 39159243Sobrien 39259243Sobrien default: 39359243Sobrien stderror(ERR_COMPCOM, short2str(act)); 39459243Sobrien return TW_ZERO; 39559243Sobrien } 39659243Sobrien} /* end tw_result */ 39759243Sobrien 398167465Smp 39959243Sobrien/* tw_dollar(): 40059243Sobrien * Expand $<n> args in buffer 40159243Sobrien */ 402167465Smpstatic const Char * 403167465Smptw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep, 404167465Smp const char *msg) 40559243Sobrien{ 406167465Smp struct Strbuf buf = Strbuf_INIT; 407167465Smp Char *res; 408167465Smp const Char *sp; 40959243Sobrien 410167465Smp for (sp = str; *sp && *sp != sep;) 41159243Sobrien if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { 41259243Sobrien int num, neg = 0; 41359243Sobrien sp += 2; 41459243Sobrien if (*sp == '-') { 41559243Sobrien neg = 1; 41659243Sobrien sp++; 41759243Sobrien } 41859243Sobrien for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') 41959243Sobrien continue; 42059243Sobrien if (neg) 42159243Sobrien num = nwl - num - 1; 422167465Smp if (num >= 0 && (size_t)num < nwl) 423167465Smp Strbuf_append(&buf, wl[num]); 42459243Sobrien } 42559243Sobrien else 426167465Smp Strbuf_append1(&buf, *sp++); 42759243Sobrien 428167465Smp res = Strbuf_finish(&buf); 42959243Sobrien 430167465Smp if (*sp++ == sep) { 431167465Smp *result = res; 43259243Sobrien return sp; 433167465Smp } 43459243Sobrien 435167465Smp xfree(res); 436145479Smp /* Truncates data if WIDE_STRINGS */ 437145479Smp stderror(ERR_COMPMIS, (int)sep, msg, short2str(str)); 43859243Sobrien return --sp; 43959243Sobrien} /* end tw_dollar */ 44059243Sobrien 441167465Smp 44259243Sobrien/* tw_complete(): 44359243Sobrien * Return the appropriate completion for the command 44459243Sobrien * 44559243Sobrien * valid completion strings are: 44659243Sobrien * p/<range>/<completion>/[<suffix>/] positional 44759243Sobrien * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern 44859243Sobrien * C/<pattern>/<completion>/[<suffix>/] current word with pattern 44959243Sobrien * n/<pattern>/<completion>/[<suffix>/] next word 45059243Sobrien * N/<pattern>/<completion>/[<suffix>/] next-next word 45159243Sobrien */ 45259243Sobrienint 453167465Smptw_complete(const Char *line, Char **word, Char **pat, int looking, eChar *suf) 45459243Sobrien{ 455167465Smp Char *buf, **vec, **wl; 45659243Sobrien static Char nomatch[2] = { (Char) ~0, 0x00 }; 457167465Smp const Char *ptr; 458167465Smp size_t wordno; 459167465Smp int n; 46059243Sobrien 461167465Smp buf = Strsave(line); 462167465Smp cleanup_push(buf, xfree); 463167465Smp /* Single-character words, empty current word, terminating NULL */ 464167465Smp wl = xmalloc(((Strlen(line) + 1) / 2 + 2) * sizeof (*wl)); 465167465Smp cleanup_push(wl, xfree); 46659243Sobrien 46759243Sobrien /* find the command */ 468167465Smp if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) { 469167465Smp cleanup_until(buf); 47059243Sobrien return TW_ZERO; 471167465Smp } 47259243Sobrien 47359243Sobrien /* 47459243Sobrien * look for hardwired command completions using a globbing 47559243Sobrien * search and for arguments using a normal search. 47659243Sobrien */ 477167465Smp if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) 478167465Smp == NULL) { 479167465Smp cleanup_until(buf); 48059243Sobrien return looking; 481167465Smp } 48259243Sobrien 48359243Sobrien /* tokenize the line one more time :-( */ 48459243Sobrien for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && 48559243Sobrien wl[wordno] != INVPTR; wordno++) 48659243Sobrien continue; 48759243Sobrien 488167465Smp if (wl[wordno] == INVPTR) { /* Found a meta character */ 489167465Smp cleanup_until(buf); 49059243Sobrien return TW_ZERO; /* de-activate completions */ 491167465Smp } 49259243Sobrien#ifdef TDEBUG 49359243Sobrien { 494167465Smp size_t 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"); 511167465Smp xprintf(" w#: %lu\n", (unsigned long)wordno); 51259243Sobrien xprintf("line: %s\n", short2str(line)); 51359243Sobrien xprintf(" cmd: %s\n", short2str(wl[0])); 51459243Sobrien xprintf("word: %s\n", short2str(*word)); 515167465Smp xprintf("last: %s\n", wordno >= 2 ? short2str(wl[wordno-2]) : "n/a"); 516167465Smp xprintf("this: %s\n", wordno >= 1 ? short2str(wl[wordno-1]) : "n/a"); 51759243Sobrien#endif /* TDEBUG */ 51859243Sobrien 51959243Sobrien for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { 520167465Smp Char *ran, /* The pattern or range X/<range>/XXXX/ */ 521167465Smp *com, /* The completion X/XXXXX/<completion>/ */ 52259243Sobrien *pos = NULL; /* scratch pointer */ 523167465Smp int cmd, res; 524145479Smp Char sep; /* the command and separator characters */ 525316957Sdchagin int exact; 52659243Sobrien 52759243Sobrien if (ptr[0] == '\0') 52859243Sobrien continue; 52959243Sobrien 53059243Sobrien#ifdef TDEBUG 53159243Sobrien xprintf("match %s\n", short2str(ptr)); 53259243Sobrien#endif /* TDEBUG */ 53359243Sobrien 53459243Sobrien switch (cmd = ptr[0]) { 53559243Sobrien case 'N': 536167465Smp pos = (wordno < 3) ? nomatch : wl[wordno - 3]; 53759243Sobrien break; 53859243Sobrien case 'n': 539167465Smp pos = (wordno < 2) ? nomatch : wl[wordno - 2]; 54059243Sobrien break; 54159243Sobrien case 'c': 54259243Sobrien case 'C': 543167465Smp pos = (wordno < 1) ? nomatch : wl[wordno - 1]; 54459243Sobrien break; 54559243Sobrien case 'p': 54659243Sobrien break; 54759243Sobrien default: 54859243Sobrien stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); 54959243Sobrien return TW_ZERO; 55059243Sobrien } 55159243Sobrien 55259243Sobrien sep = ptr[1]; 55359243Sobrien if (!Ispunct(sep)) { 554145479Smp /* Truncates data if WIDE_STRINGS */ 555145479Smp stderror(ERR_COMPINV, CGETS(27, 2, "separator"), (int)sep); 55659243Sobrien return TW_ZERO; 55759243Sobrien } 55859243Sobrien 559167465Smp ptr = tw_dollar(&ptr[2], wl, wordno, &ran, sep, 56059243Sobrien CGETS(27, 3, "pattern")); 561167465Smp cleanup_push(ran, xfree); 56259243Sobrien if (ran[0] == '\0') /* check for empty pattern (disallowed) */ 56359243Sobrien { 56459243Sobrien stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : 56559243Sobrien CGETS(27, 3, "pattern"), ""); 56659243Sobrien return TW_ZERO; 56759243Sobrien } 56859243Sobrien 569167465Smp ptr = tw_dollar(ptr, wl, wordno, &com, sep, 570167465Smp CGETS(27, 5, "completion")); 571167465Smp cleanup_push(com, xfree); 57259243Sobrien 57359243Sobrien if (*ptr != '\0') { 57459243Sobrien if (*ptr == sep) 575167465Smp *suf = CHAR_ERR; 57659243Sobrien else 57759243Sobrien *suf = *ptr; 57859243Sobrien } 57959243Sobrien else 58059243Sobrien *suf = '\0'; 58159243Sobrien 58259243Sobrien#ifdef TDEBUG 583145479Smp xprintf("command: %c\nseparator: %c\n", cmd, (int)sep); 58459243Sobrien xprintf("pattern: %s\n", short2str(ran)); 58559243Sobrien xprintf("completion: %s\n", short2str(com)); 58659243Sobrien xprintf("suffix: "); 58759243Sobrien switch (*suf) { 58859243Sobrien case 0: 58959243Sobrien xprintf("*auto suffix*\n"); 59059243Sobrien break; 591167465Smp case CHAR_ERR: 59259243Sobrien xprintf("*no suffix*\n"); 59359243Sobrien break; 59459243Sobrien default: 595167465Smp xprintf("%c\n", (int)*suf); 59659243Sobrien break; 59759243Sobrien } 59859243Sobrien#endif /* TDEBUG */ 59959243Sobrien 600316957Sdchagin exact = 0; 60159243Sobrien switch (cmd) { 60259243Sobrien case 'p': /* positional completion */ 60359243Sobrien#ifdef TDEBUG 604167465Smp xprintf("p: tw_pos(%s, %lu) = ", short2str(ran), 605167465Smp (unsigned long)wordno - 1); 60659243Sobrien xprintf("%d\n", tw_pos(ran, wordno - 1)); 60759243Sobrien#endif /* TDEBUG */ 608167465Smp if (!tw_pos(ran, wordno - 1)) { 609167465Smp cleanup_until(ran); 61059243Sobrien continue; 611167465Smp } 612167465Smp break; 61359243Sobrien 61459243Sobrien case 'N': /* match with the next-next word */ 61559243Sobrien case 'n': /* match with the next word */ 616316957Sdchagin exact = 1; 617316957Sdchagin /*FALLTHROUGH*/ 61859243Sobrien case 'c': /* match with the current word */ 61959243Sobrien case 'C': 62059243Sobrien#ifdef TDEBUG 62159243Sobrien xprintf("%c: ", cmd); 62259243Sobrien#endif /* TDEBUG */ 623316957Sdchagin if ((n = tw_match(pos, ran, exact)) < 0) { 624167465Smp cleanup_until(ran); 62559243Sobrien continue; 626167465Smp } 62759243Sobrien if (cmd == 'c') 62859243Sobrien *word += n; 629167465Smp break; 63059243Sobrien 63159243Sobrien default: 632167465Smp abort(); /* Cannot happen */ 63359243Sobrien } 634195609Smp tsetenv(STRCOMMAND_LINE, line); 635167465Smp res = tw_result(com, pat); 636195609Smp Unsetenv(STRCOMMAND_LINE); 637167465Smp cleanup_until(buf); 638167465Smp return res; 63959243Sobrien } 640167465Smp cleanup_until(buf); 64159243Sobrien *suf = '\0'; 64259243Sobrien return TW_ZERO; 64359243Sobrien} /* end tw_complete */ 644