1195609Smp/* $Header: /p/tcsh/cvsroot/tcsh/tw.comp.c,v 1.42 2007/10/01 21:52:00 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 35195609SmpRCSID("$tcsh: tw.comp.c,v 1.42 2007/10/01 21:52:00 christos Exp $") 3659243Sobrien 3759243Sobrien#include "tw.h" 3859243Sobrien#include "ed.h" 3959243Sobrien#include "tc.h" 4059243Sobrien 4159243Sobrien/* #define TDEBUG */ 4259243Sobrienstruct varent completions; 4359243Sobrien 44167465Smpstatic int tw_result (const Char *, Char **); 45167465Smpstatic Char **tw_find (Char *, struct varent *, int); 46167465Smpstatic Char *tw_tok (Char *); 47167465Smpstatic int tw_pos (Char *, int); 48167465Smpstatic void tw_pr (Char **); 49167465Smpstatic int tw_match (const Char *, const Char *); 50167465Smpstatic void tw_prlist (struct varent *); 51167465Smpstatic const Char *tw_dollar (const Char *,Char **, size_t, Char **, 52167465Smp Char, const char *); 5359243Sobrien 5459243Sobrien/* docomplete(): 5559243Sobrien * Add or list completions in the completion list 5659243Sobrien */ 5759243Sobrien/*ARGSUSED*/ 5859243Sobrienvoid 59167465Smpdocomplete(Char **v, struct command *t) 6059243Sobrien{ 61145479Smp struct varent *vp; 62145479Smp Char *p; 63131962Smp Char **pp; 6459243Sobrien 6559243Sobrien USE(t); 6659243Sobrien v++; 6759243Sobrien p = *v++; 6859243Sobrien if (p == 0) 6959243Sobrien tw_prlist(&completions); 7059243Sobrien else if (*v == 0) { 7159243Sobrien vp = adrof1(strip(p), &completions); 72100616Smp if (vp && vp->vec) 7359243Sobrien tw_pr(vp->vec), xputchar('\n'); 74131962Smp else 75131962Smp { 76131962Smp#ifdef TDEBUG 77131962Smp xprintf("tw_find(%s) \n", short2str(strip(p))); 78131962Smp#endif /* TDEBUG */ 79131962Smp pp = tw_find(strip(p), &completions, FALSE); 80131962Smp if (pp) 81131962Smp tw_pr(pp), xputchar('\n'); 82131962Smp } 8359243Sobrien } 8459243Sobrien else 8559243Sobrien set1(strip(p), saveblk(v), &completions, VAR_READWRITE); 8659243Sobrien} /* end docomplete */ 8759243Sobrien 8859243Sobrien 8959243Sobrien/* douncomplete(): 9059243Sobrien * Remove completions from the completion list 9159243Sobrien */ 9259243Sobrien/*ARGSUSED*/ 9359243Sobrienvoid 94167465Smpdouncomplete(Char **v, struct command *t) 9559243Sobrien{ 9659243Sobrien USE(t); 9759243Sobrien unset1(v, &completions); 9859243Sobrien} /* end douncomplete */ 9959243Sobrien 10059243Sobrien 10159243Sobrien/* tw_prlist(): 10259243Sobrien * Pretty print a list of variables 10359243Sobrien */ 10459243Sobrienstatic void 105167465Smptw_prlist(struct varent *p) 10659243Sobrien{ 107145479Smp struct varent *c; 10859243Sobrien 10959243Sobrien for (;;) { 11059243Sobrien while (p->v_left) 11159243Sobrien p = p->v_left; 11259243Sobrienx: 11359243Sobrien if (p->v_parent == 0) /* is it the header? */ 114167465Smp break; 115167465Smp if (setintr) { 116167465Smp int old_pintr_disabled; 117167465Smp 118167465Smp pintr_push_enable(&old_pintr_disabled); 119167465Smp cleanup_until(&old_pintr_disabled); 120167465Smp } 12159243Sobrien xprintf("%s\t", short2str(p->v_name)); 122100616Smp if (p->vec) 123100616Smp tw_pr(p->vec); 12459243Sobrien xputchar('\n'); 12559243Sobrien if (p->v_right) { 12659243Sobrien p = p->v_right; 12759243Sobrien continue; 12859243Sobrien } 12959243Sobrien do { 13059243Sobrien c = p; 13159243Sobrien p = p->v_parent; 13259243Sobrien } while (p->v_right == c); 13359243Sobrien goto x; 13459243Sobrien } 13559243Sobrien} /* end tw_prlist */ 13659243Sobrien 13759243Sobrien 13859243Sobrien/* tw_pr(): 13959243Sobrien * Pretty print a completion, adding single quotes around 14059243Sobrien * a completion argument and collapsing multiple spaces to one. 14159243Sobrien */ 14259243Sobrienstatic void 143167465Smptw_pr(Char **cmp) 14459243Sobrien{ 145145479Smp int sp, osp; 14659243Sobrien Char *ptr; 14759243Sobrien 14859243Sobrien for (; *cmp; cmp++) { 14959243Sobrien xputchar('\''); 15059243Sobrien for (osp = 0, ptr = *cmp; *ptr; ptr++) { 15159243Sobrien sp = Isspace(*ptr); 15259243Sobrien if (sp && osp) 15359243Sobrien continue; 154145479Smp xputwchar(*ptr); 15559243Sobrien osp = sp; 15659243Sobrien } 15759243Sobrien xputchar('\''); 15859243Sobrien if (cmp[1]) 15959243Sobrien xputchar(' '); 16059243Sobrien } 16159243Sobrien} /* end tw_pr */ 16259243Sobrien 16359243Sobrien 16459243Sobrien/* tw_find(): 16559243Sobrien * Find the first matching completion. 16659243Sobrien * For commands we only look at names that start with - 16759243Sobrien */ 16859243Sobrienstatic Char ** 169167465Smptw_find(Char *nam, struct varent *vp, int cmd) 17059243Sobrien{ 171145479Smp 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 */ 193145479Smpstatic int 194167465Smptw_pos(Char *ran, int wno) 19559243Sobrien{ 19659243Sobrien Char *p; 19759243Sobrien 19859243Sobrien if (ran[0] == '*' && ran[1] == '\0') 19959243Sobrien return 1; 20059243Sobrien 20159243Sobrien for (p = ran; *p && *p != '-'; p++) 20259243Sobrien continue; 20359243Sobrien 20459243Sobrien if (*p == '\0') /* range == <number> */ 20559243Sobrien return wno == getn(ran); 20659243Sobrien 20759243Sobrien if (ran == p) /* range = - <number> */ 20859243Sobrien return wno <= getn(&ran[1]); 20959243Sobrien *p++ = '\0'; 21059243Sobrien 21159243Sobrien if (*p == '\0') /* range = <number> - */ 21259243Sobrien return getn(ran) <= wno; 21359243Sobrien else /* range = <number> - <number> */ 21459243Sobrien return (getn(ran) <= wno) && (wno <= getn(p)); 21559243Sobrien} /* end tw_pos */ 21659243Sobrien 21759243Sobrien 21859243Sobrien/* tw_tok(): 21959243Sobrien * Return the next word from string, unquoteing it. 22059243Sobrien */ 22159243Sobrienstatic Char * 222167465Smptw_tok(Char *str) 22359243Sobrien{ 22459243Sobrien static Char *bf = NULL; 22559243Sobrien 22659243Sobrien if (str != NULL) 22759243Sobrien bf = str; 22859243Sobrien 22959243Sobrien /* skip leading spaces */ 23059243Sobrien for (; *bf && Isspace(*bf); bf++) 23159243Sobrien continue; 23259243Sobrien 23359243Sobrien for (str = bf; *bf && !Isspace(*bf); bf++) { 234145479Smp if (ismetahash(*bf)) 23559243Sobrien return INVPTR; 23659243Sobrien *bf = *bf & ~QUOTE; 23759243Sobrien } 23859243Sobrien if (*bf != '\0') 23959243Sobrien *bf++ = '\0'; 24059243Sobrien 24159243Sobrien return *str ? str : NULL; 24259243Sobrien} /* end tw_tok */ 24359243Sobrien 24459243Sobrien 24559243Sobrien/* tw_match(): 24659243Sobrien * Match a string against the pattern given. 24759243Sobrien * and return the number of matched characters 24859243Sobrien * in a prefix of the string. 24959243Sobrien */ 25059243Sobrienstatic int 251167465Smptw_match(const Char *str, const Char *pat) 25259243Sobrien{ 253167465Smp const Char *estr; 25459243Sobrien int rv = Gnmatch(str, pat, &estr); 25559243Sobrien#ifdef TDEBUG 25659243Sobrien xprintf("Gnmatch(%s, ", short2str(str)); 25759243Sobrien xprintf("%s, ", short2str(pat)); 25859243Sobrien xprintf("%s) = %d [%d]\n", short2str(estr), rv, estr - str); 25959243Sobrien#endif /* TDEBUG */ 26059243Sobrien return (int) (rv ? estr - str : -1); 26159243Sobrien} 26259243Sobrien 26359243Sobrien 26459243Sobrien/* tw_result(): 26559243Sobrien * Return what the completion action should be depending on the 26659243Sobrien * string 26759243Sobrien */ 26859243Sobrienstatic int 269167465Smptw_result(const Char *act, Char **pat) 27059243Sobrien{ 27159243Sobrien int looking; 27259243Sobrien static Char* res = NULL; 273167465Smp Char *p; 27459243Sobrien 27559243Sobrien if (res != NULL) 276167465Smp xfree(res), res = NULL; 27759243Sobrien 27859243Sobrien switch (act[0] & ~QUOTE) { 27959243Sobrien case 'X': 28059243Sobrien looking = TW_COMPLETION; 28159243Sobrien break; 28259243Sobrien case 'S': 28359243Sobrien looking = TW_SIGNAL; 28459243Sobrien break; 28559243Sobrien case 'a': 28659243Sobrien looking = TW_ALIAS; 28759243Sobrien break; 28859243Sobrien case 'b': 28959243Sobrien looking = TW_BINDING; 29059243Sobrien break; 29159243Sobrien case 'c': 29259243Sobrien looking = TW_COMMAND; 29359243Sobrien break; 29459243Sobrien case 'C': 29559243Sobrien looking = TW_PATH | TW_COMMAND; 29659243Sobrien break; 29759243Sobrien case 'd': 29859243Sobrien looking = TW_DIRECTORY; 29959243Sobrien break; 30059243Sobrien case 'D': 30159243Sobrien looking = TW_PATH | TW_DIRECTORY; 30259243Sobrien break; 30359243Sobrien case 'e': 30459243Sobrien looking = TW_ENVVAR; 30559243Sobrien break; 30659243Sobrien case 'f': 30759243Sobrien looking = TW_FILE; 30859243Sobrien break; 30959243Sobrien#ifdef COMPAT 31059243Sobrien case 'p': 31159243Sobrien#endif /* COMPAT */ 31259243Sobrien case 'F': 31359243Sobrien looking = TW_PATH | TW_FILE; 31459243Sobrien break; 31559243Sobrien case 'g': 31659243Sobrien looking = TW_GRPNAME; 31759243Sobrien break; 31859243Sobrien case 'j': 31959243Sobrien looking = TW_JOB; 32059243Sobrien break; 32159243Sobrien case 'l': 32259243Sobrien looking = TW_LIMIT; 32359243Sobrien break; 32459243Sobrien case 'n': 32559243Sobrien looking = TW_NONE; 32659243Sobrien break; 32759243Sobrien case 's': 32859243Sobrien looking = TW_SHELLVAR; 32959243Sobrien break; 33059243Sobrien case 't': 33159243Sobrien looking = TW_TEXT; 33259243Sobrien break; 33359243Sobrien case 'T': 33459243Sobrien looking = TW_PATH | TW_TEXT; 33559243Sobrien break; 33659243Sobrien case 'v': 33759243Sobrien looking = TW_VARIABLE; 33859243Sobrien break; 33959243Sobrien case 'u': 34059243Sobrien looking = TW_USER; 34159243Sobrien break; 34259243Sobrien case 'x': 34359243Sobrien looking = TW_EXPLAIN; 34459243Sobrien break; 34559243Sobrien 34659243Sobrien case '$': 34759243Sobrien *pat = res = Strsave(&act[1]); 34859243Sobrien (void) strip(res); 34959243Sobrien return(TW_VARLIST); 35059243Sobrien 35159243Sobrien case '(': 35259243Sobrien *pat = res = Strsave(&act[1]); 353167465Smp if ((p = Strchr(res, ')')) != NULL) 354167465Smp *p = '\0'; 35559243Sobrien (void) strip(res); 35659243Sobrien return TW_WORDLIST; 35759243Sobrien 35859243Sobrien case '`': 35959243Sobrien res = Strsave(act); 360167465Smp if ((p = Strchr(&res[1], '`')) != NULL) 361167465Smp *++p = '\0'; 36259243Sobrien 36359243Sobrien if (didfds == 0) { 36459243Sobrien /* 36559243Sobrien * Make sure that we have some file descriptors to 36659243Sobrien * play with, so that the processes have at least 0, 1, 2 36759243Sobrien * open 36859243Sobrien */ 36959243Sobrien (void) dcopy(SHIN, 0); 37059243Sobrien (void) dcopy(SHOUT, 1); 37159243Sobrien (void) dcopy(SHDIAG, 2); 37259243Sobrien } 373167465Smp if ((p = globone(res, G_APPEND)) != NULL) { 374167465Smp xfree(res), res = NULL; 375167465Smp *pat = res = Strsave(p); 376167465Smp xfree(p); 37759243Sobrien return TW_WORDLIST; 37859243Sobrien } 37959243Sobrien return TW_ZERO; 38059243Sobrien 38159243Sobrien default: 38259243Sobrien stderror(ERR_COMPCOM, short2str(act)); 38359243Sobrien return TW_ZERO; 38459243Sobrien } 38559243Sobrien 38659243Sobrien switch (act[1] & ~QUOTE) { 38759243Sobrien case '\0': 38859243Sobrien return looking; 38959243Sobrien 39059243Sobrien case ':': 39159243Sobrien *pat = res = Strsave(&act[2]); 39259243Sobrien (void) strip(res); 39359243Sobrien return looking; 39459243Sobrien 39559243Sobrien default: 39659243Sobrien stderror(ERR_COMPCOM, short2str(act)); 39759243Sobrien return TW_ZERO; 39859243Sobrien } 39959243Sobrien} /* end tw_result */ 40059243Sobrien 401167465Smp 40259243Sobrien/* tw_dollar(): 40359243Sobrien * Expand $<n> args in buffer 40459243Sobrien */ 405167465Smpstatic const Char * 406167465Smptw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep, 407167465Smp const char *msg) 40859243Sobrien{ 409167465Smp struct Strbuf buf = Strbuf_INIT; 410167465Smp Char *res; 411167465Smp const Char *sp; 41259243Sobrien 413167465Smp for (sp = str; *sp && *sp != sep;) 41459243Sobrien if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { 41559243Sobrien int num, neg = 0; 41659243Sobrien sp += 2; 41759243Sobrien if (*sp == '-') { 41859243Sobrien neg = 1; 41959243Sobrien sp++; 42059243Sobrien } 42159243Sobrien for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') 42259243Sobrien continue; 42359243Sobrien if (neg) 42459243Sobrien num = nwl - num - 1; 425167465Smp if (num >= 0 && (size_t)num < nwl) 426167465Smp Strbuf_append(&buf, wl[num]); 42759243Sobrien } 42859243Sobrien else 429167465Smp Strbuf_append1(&buf, *sp++); 43059243Sobrien 431167465Smp res = Strbuf_finish(&buf); 43259243Sobrien 433167465Smp if (*sp++ == sep) { 434167465Smp *result = res; 43559243Sobrien return sp; 436167465Smp } 43759243Sobrien 438167465Smp xfree(res); 439145479Smp /* Truncates data if WIDE_STRINGS */ 440145479Smp stderror(ERR_COMPMIS, (int)sep, msg, short2str(str)); 44159243Sobrien return --sp; 44259243Sobrien} /* end tw_dollar */ 44359243Sobrien 444167465Smp 44559243Sobrien/* tw_complete(): 44659243Sobrien * Return the appropriate completion for the command 44759243Sobrien * 44859243Sobrien * valid completion strings are: 44959243Sobrien * p/<range>/<completion>/[<suffix>/] positional 45059243Sobrien * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern 45159243Sobrien * C/<pattern>/<completion>/[<suffix>/] current word with pattern 45259243Sobrien * n/<pattern>/<completion>/[<suffix>/] next word 45359243Sobrien * N/<pattern>/<completion>/[<suffix>/] next-next word 45459243Sobrien */ 45559243Sobrienint 456167465Smptw_complete(const Char *line, Char **word, Char **pat, int looking, eChar *suf) 45759243Sobrien{ 458167465Smp Char *buf, **vec, **wl; 45959243Sobrien static Char nomatch[2] = { (Char) ~0, 0x00 }; 460167465Smp const Char *ptr; 461167465Smp size_t wordno; 462167465Smp int n; 46359243Sobrien 464167465Smp buf = Strsave(line); 465167465Smp cleanup_push(buf, xfree); 466167465Smp /* Single-character words, empty current word, terminating NULL */ 467167465Smp wl = xmalloc(((Strlen(line) + 1) / 2 + 2) * sizeof (*wl)); 468167465Smp cleanup_push(wl, xfree); 46959243Sobrien 47059243Sobrien /* find the command */ 471167465Smp if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) { 472167465Smp cleanup_until(buf); 47359243Sobrien return TW_ZERO; 474167465Smp } 47559243Sobrien 47659243Sobrien /* 47759243Sobrien * look for hardwired command completions using a globbing 47859243Sobrien * search and for arguments using a normal search. 47959243Sobrien */ 480167465Smp if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) 481167465Smp == NULL) { 482167465Smp cleanup_until(buf); 48359243Sobrien return looking; 484167465Smp } 48559243Sobrien 48659243Sobrien /* tokenize the line one more time :-( */ 48759243Sobrien for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && 48859243Sobrien wl[wordno] != INVPTR; wordno++) 48959243Sobrien continue; 49059243Sobrien 491167465Smp if (wl[wordno] == INVPTR) { /* Found a meta character */ 492167465Smp cleanup_until(buf); 49359243Sobrien return TW_ZERO; /* de-activate completions */ 494167465Smp } 49559243Sobrien#ifdef TDEBUG 49659243Sobrien { 497167465Smp size_t i; 49859243Sobrien for (i = 0; i < wordno; i++) 49959243Sobrien xprintf("'%s' ", short2str(wl[i])); 50059243Sobrien xprintf("\n"); 50159243Sobrien } 50259243Sobrien#endif /* TDEBUG */ 50359243Sobrien 50459243Sobrien /* if the current word is empty move the last word to the next */ 50559243Sobrien if (**word == '\0') { 50659243Sobrien wl[wordno] = *word; 50759243Sobrien wordno++; 50859243Sobrien } 50959243Sobrien wl[wordno] = NULL; 51059243Sobrien 51159243Sobrien 51259243Sobrien#ifdef TDEBUG 51359243Sobrien xprintf("\r\n"); 514167465Smp xprintf(" w#: %lu\n", (unsigned long)wordno); 51559243Sobrien xprintf("line: %s\n", short2str(line)); 51659243Sobrien xprintf(" cmd: %s\n", short2str(wl[0])); 51759243Sobrien xprintf("word: %s\n", short2str(*word)); 518167465Smp xprintf("last: %s\n", wordno >= 2 ? short2str(wl[wordno-2]) : "n/a"); 519167465Smp xprintf("this: %s\n", wordno >= 1 ? short2str(wl[wordno-1]) : "n/a"); 52059243Sobrien#endif /* TDEBUG */ 52159243Sobrien 52259243Sobrien for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { 523167465Smp Char *ran, /* The pattern or range X/<range>/XXXX/ */ 524167465Smp *com, /* The completion X/XXXXX/<completion>/ */ 52559243Sobrien *pos = NULL; /* scratch pointer */ 526167465Smp int cmd, res; 527145479Smp Char sep; /* the command and separator characters */ 52859243Sobrien 52959243Sobrien if (ptr[0] == '\0') 53059243Sobrien continue; 53159243Sobrien 53259243Sobrien#ifdef TDEBUG 53359243Sobrien xprintf("match %s\n", short2str(ptr)); 53459243Sobrien#endif /* TDEBUG */ 53559243Sobrien 53659243Sobrien switch (cmd = ptr[0]) { 53759243Sobrien case 'N': 538167465Smp pos = (wordno < 3) ? nomatch : wl[wordno - 3]; 53959243Sobrien break; 54059243Sobrien case 'n': 541167465Smp pos = (wordno < 2) ? nomatch : wl[wordno - 2]; 54259243Sobrien break; 54359243Sobrien case 'c': 54459243Sobrien case 'C': 545167465Smp pos = (wordno < 1) ? nomatch : wl[wordno - 1]; 54659243Sobrien break; 54759243Sobrien case 'p': 54859243Sobrien break; 54959243Sobrien default: 55059243Sobrien stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); 55159243Sobrien return TW_ZERO; 55259243Sobrien } 55359243Sobrien 55459243Sobrien sep = ptr[1]; 55559243Sobrien if (!Ispunct(sep)) { 556145479Smp /* Truncates data if WIDE_STRINGS */ 557145479Smp stderror(ERR_COMPINV, CGETS(27, 2, "separator"), (int)sep); 55859243Sobrien return TW_ZERO; 55959243Sobrien } 56059243Sobrien 561167465Smp ptr = tw_dollar(&ptr[2], wl, wordno, &ran, sep, 56259243Sobrien CGETS(27, 3, "pattern")); 563167465Smp cleanup_push(ran, xfree); 56459243Sobrien if (ran[0] == '\0') /* check for empty pattern (disallowed) */ 56559243Sobrien { 56659243Sobrien stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : 56759243Sobrien CGETS(27, 3, "pattern"), ""); 56859243Sobrien return TW_ZERO; 56959243Sobrien } 57059243Sobrien 571167465Smp ptr = tw_dollar(ptr, wl, wordno, &com, sep, 572167465Smp CGETS(27, 5, "completion")); 573167465Smp cleanup_push(com, xfree); 57459243Sobrien 57559243Sobrien if (*ptr != '\0') { 57659243Sobrien if (*ptr == sep) 577167465Smp *suf = CHAR_ERR; 57859243Sobrien else 57959243Sobrien *suf = *ptr; 58059243Sobrien } 58159243Sobrien else 58259243Sobrien *suf = '\0'; 58359243Sobrien 58459243Sobrien#ifdef TDEBUG 585145479Smp xprintf("command: %c\nseparator: %c\n", cmd, (int)sep); 58659243Sobrien xprintf("pattern: %s\n", short2str(ran)); 58759243Sobrien xprintf("completion: %s\n", short2str(com)); 58859243Sobrien xprintf("suffix: "); 58959243Sobrien switch (*suf) { 59059243Sobrien case 0: 59159243Sobrien xprintf("*auto suffix*\n"); 59259243Sobrien break; 593167465Smp case CHAR_ERR: 59459243Sobrien xprintf("*no suffix*\n"); 59559243Sobrien break; 59659243Sobrien default: 597167465Smp xprintf("%c\n", (int)*suf); 59859243Sobrien break; 59959243Sobrien } 60059243Sobrien#endif /* TDEBUG */ 60159243Sobrien 60259243Sobrien switch (cmd) { 60359243Sobrien case 'p': /* positional completion */ 60459243Sobrien#ifdef TDEBUG 605167465Smp xprintf("p: tw_pos(%s, %lu) = ", short2str(ran), 606167465Smp (unsigned long)wordno - 1); 60759243Sobrien xprintf("%d\n", tw_pos(ran, wordno - 1)); 60859243Sobrien#endif /* TDEBUG */ 609167465Smp if (!tw_pos(ran, wordno - 1)) { 610167465Smp cleanup_until(ran); 61159243Sobrien continue; 612167465Smp } 613167465Smp break; 61459243Sobrien 61559243Sobrien case 'N': /* match with the next-next word */ 61659243Sobrien case 'n': /* match with the next word */ 61759243Sobrien case 'c': /* match with the current word */ 61859243Sobrien case 'C': 61959243Sobrien#ifdef TDEBUG 62059243Sobrien xprintf("%c: ", cmd); 62159243Sobrien#endif /* TDEBUG */ 622167465Smp if ((n = tw_match(pos, ran)) < 0) { 623167465Smp cleanup_until(ran); 62459243Sobrien continue; 625167465Smp } 62659243Sobrien if (cmd == 'c') 62759243Sobrien *word += n; 628167465Smp break; 62959243Sobrien 63059243Sobrien default: 631167465Smp abort(); /* Cannot happen */ 63259243Sobrien } 633195609Smp tsetenv(STRCOMMAND_LINE, line); 634167465Smp res = tw_result(com, pat); 635195609Smp Unsetenv(STRCOMMAND_LINE); 636167465Smp cleanup_until(buf); 637167465Smp return res; 63859243Sobrien } 639167465Smp cleanup_until(buf); 64059243Sobrien *suf = '\0'; 64159243Sobrien return TW_ZERO; 64259243Sobrien} /* end tw_complete */ 643