1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/tw.comp.c,v 1.45 2015/09/30 13:28:02 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 35316958SdchaginRCSID("$tcsh: tw.comp.c,v 1.45 2015/09/30 13:28:02 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 **); 49316958Sdchaginstatic int tw_match (const Char *, const Char *, int); 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 251316958Sdchagintw_match(const Char *str, const Char *pat, int exact) 25259243Sobrien{ 253167465Smp const Char *estr; 254316958Sdchagin int rv = exact ? Gmatch(estr = str, pat) : Gnmatch(str, pat, &estr); 25559243Sobrien#ifdef TDEBUG 256316958Sdchagin xprintf("G%smatch(%s, ", exact ? "" : "n", short2str(str)); 25759243Sobrien xprintf("%s, ", short2str(pat)); 258316958Sdchagin xprintf("%s) = %d [%" TCSH_PTRDIFF_T_FMT "d]\n", short2str(estr), rv, 259316958Sdchagin estr - str); 26059243Sobrien#endif /* TDEBUG */ 26159243Sobrien return (int) (rv ? estr - str : -1); 26259243Sobrien} 26359243Sobrien 26459243Sobrien 26559243Sobrien/* tw_result(): 26659243Sobrien * Return what the completion action should be depending on the 26759243Sobrien * string 26859243Sobrien */ 26959243Sobrienstatic int 270167465Smptw_result(const Char *act, Char **pat) 27159243Sobrien{ 27259243Sobrien int looking; 27359243Sobrien static Char* res = NULL; 274167465Smp Char *p; 27559243Sobrien 27659243Sobrien if (res != NULL) 277167465Smp xfree(res), res = NULL; 27859243Sobrien 27959243Sobrien switch (act[0] & ~QUOTE) { 28059243Sobrien case 'X': 28159243Sobrien looking = TW_COMPLETION; 28259243Sobrien break; 28359243Sobrien case 'S': 28459243Sobrien looking = TW_SIGNAL; 28559243Sobrien break; 28659243Sobrien case 'a': 28759243Sobrien looking = TW_ALIAS; 28859243Sobrien break; 28959243Sobrien case 'b': 29059243Sobrien looking = TW_BINDING; 29159243Sobrien break; 29259243Sobrien case 'c': 29359243Sobrien looking = TW_COMMAND; 29459243Sobrien break; 29559243Sobrien case 'C': 29659243Sobrien looking = TW_PATH | TW_COMMAND; 29759243Sobrien break; 29859243Sobrien case 'd': 29959243Sobrien looking = TW_DIRECTORY; 30059243Sobrien break; 30159243Sobrien case 'D': 30259243Sobrien looking = TW_PATH | TW_DIRECTORY; 30359243Sobrien break; 30459243Sobrien case 'e': 30559243Sobrien looking = TW_ENVVAR; 30659243Sobrien break; 30759243Sobrien case 'f': 30859243Sobrien looking = TW_FILE; 30959243Sobrien break; 31059243Sobrien#ifdef COMPAT 31159243Sobrien case 'p': 31259243Sobrien#endif /* COMPAT */ 31359243Sobrien case 'F': 31459243Sobrien looking = TW_PATH | TW_FILE; 31559243Sobrien break; 31659243Sobrien case 'g': 31759243Sobrien looking = TW_GRPNAME; 31859243Sobrien break; 31959243Sobrien case 'j': 32059243Sobrien looking = TW_JOB; 32159243Sobrien break; 32259243Sobrien case 'l': 32359243Sobrien looking = TW_LIMIT; 32459243Sobrien break; 32559243Sobrien case 'n': 32659243Sobrien looking = TW_NONE; 32759243Sobrien break; 32859243Sobrien case 's': 32959243Sobrien looking = TW_SHELLVAR; 33059243Sobrien break; 33159243Sobrien case 't': 33259243Sobrien looking = TW_TEXT; 33359243Sobrien break; 33459243Sobrien case 'T': 33559243Sobrien looking = TW_PATH | TW_TEXT; 33659243Sobrien break; 33759243Sobrien case 'v': 33859243Sobrien looking = TW_VARIABLE; 33959243Sobrien break; 34059243Sobrien case 'u': 34159243Sobrien looking = TW_USER; 34259243Sobrien break; 34359243Sobrien case 'x': 34459243Sobrien looking = TW_EXPLAIN; 34559243Sobrien break; 34659243Sobrien 34759243Sobrien case '$': 34859243Sobrien *pat = res = Strsave(&act[1]); 34959243Sobrien (void) strip(res); 35059243Sobrien return(TW_VARLIST); 35159243Sobrien 35259243Sobrien case '(': 35359243Sobrien *pat = res = Strsave(&act[1]); 354167465Smp if ((p = Strchr(res, ')')) != NULL) 355167465Smp *p = '\0'; 35659243Sobrien (void) strip(res); 35759243Sobrien return TW_WORDLIST; 35859243Sobrien 35959243Sobrien case '`': 36059243Sobrien res = Strsave(act); 361167465Smp if ((p = Strchr(&res[1], '`')) != NULL) 362167465Smp *++p = '\0'; 36359243Sobrien 36459243Sobrien if (didfds == 0) { 36559243Sobrien /* 36659243Sobrien * Make sure that we have some file descriptors to 36759243Sobrien * play with, so that the processes have at least 0, 1, 2 36859243Sobrien * open 36959243Sobrien */ 37059243Sobrien (void) dcopy(SHIN, 0); 37159243Sobrien (void) dcopy(SHOUT, 1); 37259243Sobrien (void) dcopy(SHDIAG, 2); 37359243Sobrien } 374167465Smp if ((p = globone(res, G_APPEND)) != NULL) { 375167465Smp xfree(res), res = NULL; 376167465Smp *pat = res = Strsave(p); 377167465Smp xfree(p); 37859243Sobrien return TW_WORDLIST; 37959243Sobrien } 38059243Sobrien return TW_ZERO; 38159243Sobrien 38259243Sobrien default: 38359243Sobrien stderror(ERR_COMPCOM, short2str(act)); 38459243Sobrien return TW_ZERO; 38559243Sobrien } 38659243Sobrien 38759243Sobrien switch (act[1] & ~QUOTE) { 38859243Sobrien case '\0': 38959243Sobrien return looking; 39059243Sobrien 39159243Sobrien case ':': 39259243Sobrien *pat = res = Strsave(&act[2]); 39359243Sobrien (void) strip(res); 39459243Sobrien return looking; 39559243Sobrien 39659243Sobrien default: 39759243Sobrien stderror(ERR_COMPCOM, short2str(act)); 39859243Sobrien return TW_ZERO; 39959243Sobrien } 40059243Sobrien} /* end tw_result */ 40159243Sobrien 402167465Smp 40359243Sobrien/* tw_dollar(): 40459243Sobrien * Expand $<n> args in buffer 40559243Sobrien */ 406167465Smpstatic const Char * 407167465Smptw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep, 408167465Smp const char *msg) 40959243Sobrien{ 410167465Smp struct Strbuf buf = Strbuf_INIT; 411167465Smp Char *res; 412167465Smp const Char *sp; 41359243Sobrien 414167465Smp for (sp = str; *sp && *sp != sep;) 41559243Sobrien if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { 41659243Sobrien int num, neg = 0; 41759243Sobrien sp += 2; 41859243Sobrien if (*sp == '-') { 41959243Sobrien neg = 1; 42059243Sobrien sp++; 42159243Sobrien } 42259243Sobrien for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') 42359243Sobrien continue; 42459243Sobrien if (neg) 42559243Sobrien num = nwl - num - 1; 426167465Smp if (num >= 0 && (size_t)num < nwl) 427167465Smp Strbuf_append(&buf, wl[num]); 42859243Sobrien } 42959243Sobrien else 430167465Smp Strbuf_append1(&buf, *sp++); 43159243Sobrien 432167465Smp res = Strbuf_finish(&buf); 43359243Sobrien 434167465Smp if (*sp++ == sep) { 435167465Smp *result = res; 43659243Sobrien return sp; 437167465Smp } 43859243Sobrien 439167465Smp xfree(res); 440145479Smp /* Truncates data if WIDE_STRINGS */ 441145479Smp stderror(ERR_COMPMIS, (int)sep, msg, short2str(str)); 44259243Sobrien return --sp; 44359243Sobrien} /* end tw_dollar */ 44459243Sobrien 445167465Smp 44659243Sobrien/* tw_complete(): 44759243Sobrien * Return the appropriate completion for the command 44859243Sobrien * 44959243Sobrien * valid completion strings are: 45059243Sobrien * p/<range>/<completion>/[<suffix>/] positional 45159243Sobrien * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern 45259243Sobrien * C/<pattern>/<completion>/[<suffix>/] current word with pattern 45359243Sobrien * n/<pattern>/<completion>/[<suffix>/] next word 45459243Sobrien * N/<pattern>/<completion>/[<suffix>/] next-next word 45559243Sobrien */ 45659243Sobrienint 457167465Smptw_complete(const Char *line, Char **word, Char **pat, int looking, eChar *suf) 45859243Sobrien{ 459167465Smp Char *buf, **vec, **wl; 46059243Sobrien static Char nomatch[2] = { (Char) ~0, 0x00 }; 461167465Smp const Char *ptr; 462167465Smp size_t wordno; 463167465Smp int n; 46459243Sobrien 465167465Smp buf = Strsave(line); 466167465Smp cleanup_push(buf, xfree); 467167465Smp /* Single-character words, empty current word, terminating NULL */ 468167465Smp wl = xmalloc(((Strlen(line) + 1) / 2 + 2) * sizeof (*wl)); 469167465Smp cleanup_push(wl, xfree); 47059243Sobrien 47159243Sobrien /* find the command */ 472167465Smp if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) { 473167465Smp cleanup_until(buf); 47459243Sobrien return TW_ZERO; 475167465Smp } 47659243Sobrien 47759243Sobrien /* 47859243Sobrien * look for hardwired command completions using a globbing 47959243Sobrien * search and for arguments using a normal search. 48059243Sobrien */ 481167465Smp if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) 482167465Smp == NULL) { 483167465Smp cleanup_until(buf); 48459243Sobrien return looking; 485167465Smp } 48659243Sobrien 48759243Sobrien /* tokenize the line one more time :-( */ 48859243Sobrien for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && 48959243Sobrien wl[wordno] != INVPTR; wordno++) 49059243Sobrien continue; 49159243Sobrien 492167465Smp if (wl[wordno] == INVPTR) { /* Found a meta character */ 493167465Smp cleanup_until(buf); 49459243Sobrien return TW_ZERO; /* de-activate completions */ 495167465Smp } 49659243Sobrien#ifdef TDEBUG 49759243Sobrien { 498167465Smp size_t i; 49959243Sobrien for (i = 0; i < wordno; i++) 50059243Sobrien xprintf("'%s' ", short2str(wl[i])); 50159243Sobrien xprintf("\n"); 50259243Sobrien } 50359243Sobrien#endif /* TDEBUG */ 50459243Sobrien 50559243Sobrien /* if the current word is empty move the last word to the next */ 50659243Sobrien if (**word == '\0') { 50759243Sobrien wl[wordno] = *word; 50859243Sobrien wordno++; 50959243Sobrien } 51059243Sobrien wl[wordno] = NULL; 51159243Sobrien 51259243Sobrien 51359243Sobrien#ifdef TDEBUG 51459243Sobrien xprintf("\r\n"); 515167465Smp xprintf(" w#: %lu\n", (unsigned long)wordno); 51659243Sobrien xprintf("line: %s\n", short2str(line)); 51759243Sobrien xprintf(" cmd: %s\n", short2str(wl[0])); 51859243Sobrien xprintf("word: %s\n", short2str(*word)); 519167465Smp xprintf("last: %s\n", wordno >= 2 ? short2str(wl[wordno-2]) : "n/a"); 520167465Smp xprintf("this: %s\n", wordno >= 1 ? short2str(wl[wordno-1]) : "n/a"); 52159243Sobrien#endif /* TDEBUG */ 52259243Sobrien 52359243Sobrien for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { 524167465Smp Char *ran, /* The pattern or range X/<range>/XXXX/ */ 525167465Smp *com, /* The completion X/XXXXX/<completion>/ */ 52659243Sobrien *pos = NULL; /* scratch pointer */ 527167465Smp int cmd, res; 528145479Smp Char sep; /* the command and separator characters */ 529316958Sdchagin int exact; 53059243Sobrien 53159243Sobrien if (ptr[0] == '\0') 53259243Sobrien continue; 53359243Sobrien 53459243Sobrien#ifdef TDEBUG 53559243Sobrien xprintf("match %s\n", short2str(ptr)); 53659243Sobrien#endif /* TDEBUG */ 53759243Sobrien 53859243Sobrien switch (cmd = ptr[0]) { 53959243Sobrien case 'N': 540167465Smp pos = (wordno < 3) ? nomatch : wl[wordno - 3]; 54159243Sobrien break; 54259243Sobrien case 'n': 543167465Smp pos = (wordno < 2) ? nomatch : wl[wordno - 2]; 54459243Sobrien break; 54559243Sobrien case 'c': 54659243Sobrien case 'C': 547167465Smp pos = (wordno < 1) ? nomatch : wl[wordno - 1]; 54859243Sobrien break; 54959243Sobrien case 'p': 55059243Sobrien break; 55159243Sobrien default: 55259243Sobrien stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); 55359243Sobrien return TW_ZERO; 55459243Sobrien } 55559243Sobrien 55659243Sobrien sep = ptr[1]; 55759243Sobrien if (!Ispunct(sep)) { 558145479Smp /* Truncates data if WIDE_STRINGS */ 559145479Smp stderror(ERR_COMPINV, CGETS(27, 2, "separator"), (int)sep); 56059243Sobrien return TW_ZERO; 56159243Sobrien } 56259243Sobrien 563167465Smp ptr = tw_dollar(&ptr[2], wl, wordno, &ran, sep, 56459243Sobrien CGETS(27, 3, "pattern")); 565167465Smp cleanup_push(ran, xfree); 56659243Sobrien if (ran[0] == '\0') /* check for empty pattern (disallowed) */ 56759243Sobrien { 56859243Sobrien stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : 56959243Sobrien CGETS(27, 3, "pattern"), ""); 57059243Sobrien return TW_ZERO; 57159243Sobrien } 57259243Sobrien 573167465Smp ptr = tw_dollar(ptr, wl, wordno, &com, sep, 574167465Smp CGETS(27, 5, "completion")); 575167465Smp cleanup_push(com, xfree); 57659243Sobrien 57759243Sobrien if (*ptr != '\0') { 57859243Sobrien if (*ptr == sep) 579167465Smp *suf = CHAR_ERR; 58059243Sobrien else 58159243Sobrien *suf = *ptr; 58259243Sobrien } 58359243Sobrien else 58459243Sobrien *suf = '\0'; 58559243Sobrien 58659243Sobrien#ifdef TDEBUG 587145479Smp xprintf("command: %c\nseparator: %c\n", cmd, (int)sep); 58859243Sobrien xprintf("pattern: %s\n", short2str(ran)); 58959243Sobrien xprintf("completion: %s\n", short2str(com)); 59059243Sobrien xprintf("suffix: "); 59159243Sobrien switch (*suf) { 59259243Sobrien case 0: 59359243Sobrien xprintf("*auto suffix*\n"); 59459243Sobrien break; 595167465Smp case CHAR_ERR: 59659243Sobrien xprintf("*no suffix*\n"); 59759243Sobrien break; 59859243Sobrien default: 599167465Smp xprintf("%c\n", (int)*suf); 60059243Sobrien break; 60159243Sobrien } 60259243Sobrien#endif /* TDEBUG */ 60359243Sobrien 604316958Sdchagin exact = 0; 60559243Sobrien switch (cmd) { 60659243Sobrien case 'p': /* positional completion */ 60759243Sobrien#ifdef TDEBUG 608167465Smp xprintf("p: tw_pos(%s, %lu) = ", short2str(ran), 609167465Smp (unsigned long)wordno - 1); 61059243Sobrien xprintf("%d\n", tw_pos(ran, wordno - 1)); 61159243Sobrien#endif /* TDEBUG */ 612167465Smp if (!tw_pos(ran, wordno - 1)) { 613167465Smp cleanup_until(ran); 61459243Sobrien continue; 615167465Smp } 616167465Smp break; 61759243Sobrien 61859243Sobrien case 'N': /* match with the next-next word */ 61959243Sobrien case 'n': /* match with the next word */ 620316958Sdchagin exact = 1; 621316958Sdchagin /*FALLTHROUGH*/ 62259243Sobrien case 'c': /* match with the current word */ 62359243Sobrien case 'C': 62459243Sobrien#ifdef TDEBUG 62559243Sobrien xprintf("%c: ", cmd); 62659243Sobrien#endif /* TDEBUG */ 627316958Sdchagin if ((n = tw_match(pos, ran, exact)) < 0) { 628167465Smp cleanup_until(ran); 62959243Sobrien continue; 630167465Smp } 63159243Sobrien if (cmd == 'c') 63259243Sobrien *word += n; 633167465Smp break; 63459243Sobrien 63559243Sobrien default: 636167465Smp abort(); /* Cannot happen */ 63759243Sobrien } 638195609Smp tsetenv(STRCOMMAND_LINE, line); 639167465Smp res = tw_result(com, pat); 640195609Smp Unsetenv(STRCOMMAND_LINE); 641167465Smp cleanup_until(buf); 642167465Smp return res; 64359243Sobrien } 644167465Smp cleanup_until(buf); 64559243Sobrien *suf = '\0'; 64659243Sobrien return TW_ZERO; 64759243Sobrien} /* end tw_complete */ 648