source: trunk/third/tcsh/tw.comp.c @ 12039

Revision 12039, 14.0 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12038, which included commits to RCS files with non-trunk default branches.
Line 
1/* $Header: /afs/dev.mit.edu/source/repository/third/tcsh/tw.comp.c,v 1.1.1.2 1998-10-03 21:10:20 danw Exp $ */
2/*
3 * tw.comp.c: File completion builtin
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by the University of
20 *      California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37#include "sh.h"
38
39RCSID("$Id: tw.comp.c,v 1.1.1.2 1998-10-03 21:10:20 danw Exp $")
40
41#include "tw.h"
42#include "ed.h"
43#include "tc.h"
44
45/* #define TDEBUG */
46struct varent completions;
47
48static int                tw_result     __P((Char *, Char **));
49static Char             **tw_find       __P((Char *, struct varent *, int));
50static Char              *tw_tok        __P((Char *));
51static bool               tw_pos        __P((Char *, int));
52static void               tw_pr         __P((Char **));
53static int                tw_match      __P((Char *, Char *));
54static void               tw_prlist     __P((struct varent *));
55static Char              *tw_dollar     __P((Char *,Char **, int, Char *,
56                                             int, const char *));
57
58/* docomplete():
59 *      Add or list completions in the completion list
60 */
61/*ARGSUSED*/
62void
63docomplete(v, t)
64    Char **v;
65    struct command *t;
66{
67    register struct varent *vp;
68    register Char *p;
69
70    USE(t);
71    v++;
72    p = *v++;
73    if (p == 0)
74        tw_prlist(&completions);
75    else if (*v == 0) {
76        vp = adrof1(strip(p), &completions);
77        if (vp)
78            tw_pr(vp->vec), xputchar('\n');
79    }
80    else
81        set1(strip(p), saveblk(v), &completions, VAR_READWRITE);
82} /* end docomplete */
83
84
85/* douncomplete():
86 *      Remove completions from the completion list
87 */
88/*ARGSUSED*/
89void
90douncomplete(v, t)
91    Char **v;
92    struct command *t;
93{
94    USE(t);
95    unset1(v, &completions);
96} /* end douncomplete */
97
98
99/* tw_prlist():
100 *      Pretty print a list of variables
101 */
102static void
103tw_prlist(p)
104    struct varent *p;
105{
106    register struct varent *c;
107
108    if (setintr)
109#ifdef BSDSIGS
110        (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
111#else                           /* BSDSIGS */
112        (void) sigrelse(SIGINT);
113#endif                          /* BSDSIGS */
114
115    for (;;) {
116        while (p->v_left)
117            p = p->v_left;
118x:
119        if (p->v_parent == 0)   /* is it the header? */
120            return;
121        xprintf("%s\t", short2str(p->v_name));
122        tw_pr(p->vec);
123        xputchar('\n');
124        if (p->v_right) {
125            p = p->v_right;
126            continue;
127        }
128        do {
129            c = p;
130            p = p->v_parent;
131        } while (p->v_right == c);
132        goto x;
133    }
134} /* end tw_prlist */
135
136
137/* tw_pr():
138 *      Pretty print a completion, adding single quotes around
139 *      a completion argument and collapsing multiple spaces to one.
140 */
141static void
142tw_pr(cmp)
143    Char **cmp;
144{
145    bool sp, osp;
146    Char *ptr;
147
148    for (; *cmp; cmp++) {
149        xputchar('\'');
150        for (osp = 0, ptr = *cmp; *ptr; ptr++) {
151            sp = Isspace(*ptr);
152            if (sp && osp)
153                continue;
154            xputchar(*ptr);
155            osp = sp;
156        }
157        xputchar('\'');
158        if (cmp[1])
159            xputchar(' ');
160    }
161} /* end tw_pr */
162
163
164/* tw_find():
165 *      Find the first matching completion.
166 *      For commands we only look at names that start with -
167 */
168static Char **
169tw_find(nam, vp, cmd)
170    Char   *nam;
171    register struct varent *vp;
172    int cmd;
173{
174    register Char **rv;
175
176    for (vp = vp->v_left; vp; vp = vp->v_right) {
177        if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL)
178            return rv;
179        if (cmd) {
180            if (vp->v_name[0] != '-')
181                continue;
182            if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL)
183                return vp->vec;
184        }
185        else
186            if (Gmatch(nam, vp->v_name) && vp->vec != NULL)
187                return vp->vec;
188    }
189    return NULL;
190} /* end tw_find */
191
192
193/* tw_pos():
194 *      Return true if the position is within the specified range
195 */
196static bool
197tw_pos(ran, wno)
198    Char *ran;
199    int   wno;
200{
201    Char *p;
202
203    if (ran[0] == '*' && ran[1] == '\0')
204        return 1;
205
206    for (p = ran; *p && *p != '-'; p++)
207        continue;
208
209    if (*p == '\0')                     /* range == <number> */
210        return wno == getn(ran);
211   
212    if (ran == p)                       /* range = - <number> */
213        return wno <= getn(&ran[1]);
214    *p++ = '\0';
215
216    if (*p == '\0')                     /* range = <number> - */
217        return getn(ran) <= wno;
218    else                                /* range = <number> - <number> */
219        return (getn(ran) <= wno) && (wno <= getn(p));
220           
221} /* end tw_pos */
222
223
224/* tw_tok():
225 *      Return the next word from string, unquoteing it.
226 */
227static Char *
228tw_tok(str)
229    Char *str;
230{
231    static Char *bf = NULL;
232
233    if (str != NULL)
234        bf = str;
235   
236    /* skip leading spaces */
237    for (; *bf && Isspace(*bf); bf++)
238        continue;
239
240    for (str = bf; *bf && !Isspace(*bf); bf++) {
241        if (ismeta(*bf))
242            return INVPTR;
243        *bf = *bf & ~QUOTE;
244    }
245    if (*bf != '\0')
246        *bf++ = '\0';
247
248    return *str ? str : NULL;
249} /* end tw_tok */
250
251
252/* tw_match():
253 *      Match a string against the pattern given.
254 *      and return the number of matched characters
255 *      in a prefix of the string.
256 */
257static int
258tw_match(str, pat)
259    Char *str, *pat;
260{
261    Char *estr;
262    int rv = Gnmatch(str, pat, &estr);
263#ifdef TDEBUG
264    xprintf("Gnmatch(%s, ", short2str(str));
265    xprintf("%s, ", short2str(pat));
266    xprintf("%s) = %d [%d]\n", short2str(estr), rv, estr - str);
267#endif /* TDEBUG */
268    return (rv ? estr - str : -1);
269}
270
271
272/* tw_result():
273 *      Return what the completion action should be depending on the
274 *      string
275 */
276static int
277tw_result(act, pat)
278    Char *act, **pat;
279{
280    int looking;
281    static Char* res = NULL;
282
283    if (res != NULL)
284        xfree((ptr_t) res), res = NULL;
285
286    switch (act[0] & ~QUOTE) {
287    case 'X':
288        looking = TW_COMPLETION;
289        break;
290    case 'S':
291        looking = TW_SIGNAL;
292        break;
293    case 'a':
294        looking = TW_ALIAS;
295        break;
296    case 'b':
297        looking = TW_BINDING;
298        break;
299    case 'c':
300        looking = TW_COMMAND;
301        break;
302    case 'C':
303        looking = TW_PATH | TW_COMMAND;
304        break;
305    case 'd':
306        looking = TW_DIRECTORY;
307        break;
308    case 'D':
309        looking = TW_PATH | TW_DIRECTORY;
310        break;
311    case 'e':
312        looking = TW_ENVVAR;
313        break;
314    case 'f':
315        looking = TW_FILE;
316        break;
317#ifdef COMPAT
318    case 'p':
319#endif /* COMPAT */
320    case 'F':
321        looking = TW_PATH | TW_FILE;
322        break;
323    case 'g':
324        looking = TW_GRPNAME;
325        break;
326    case 'j':
327        looking = TW_JOB;
328        break;
329    case 'l':
330        looking = TW_LIMIT;
331        break;
332    case 'n':
333        looking = TW_NONE;
334        break;
335    case 's':
336        looking = TW_SHELLVAR;
337        break;
338    case 't':
339        looking = TW_TEXT;
340        break;
341    case 'T':
342        looking = TW_PATH | TW_TEXT;
343        break;
344    case 'v':
345        looking = TW_VARIABLE;
346        break;
347    case 'u':
348        looking = TW_USER;
349        break;
350    case 'x':
351        looking = TW_EXPLAIN;
352        break;
353
354    case '$':
355        *pat = res = Strsave(&act[1]);
356        (void) strip(res);
357        return(TW_VARLIST);
358
359    case '(':
360        *pat = res = Strsave(&act[1]);
361        if ((act = Strchr(res, ')')) != NULL)
362            *act = '\0';
363        (void) strip(res);
364        return TW_WORDLIST;
365
366    case '`':
367        res = Strsave(act);
368        if ((act = Strchr(&res[1], '`')) != NULL)
369            *++act = '\0';
370       
371        if (didfds == 0) {
372            /*
373             * Make sure that we have some file descriptors to
374             * play with, so that the processes have at least 0, 1, 2
375             * open
376             */
377            (void) dcopy(SHIN, 0);
378            (void) dcopy(SHOUT, 1);
379            (void) dcopy(SHDIAG, 2);
380        }
381        if ((act = globone(res, G_APPEND)) != NULL) {
382            xfree((ptr_t) res), res = NULL;
383            *pat = res = Strsave(act);
384            xfree((ptr_t) act);
385            return TW_WORDLIST;
386        }
387        return TW_ZERO;
388
389    default:
390        stderror(ERR_COMPCOM, short2str(act));
391        return TW_ZERO;
392    }
393
394    switch (act[1] & ~QUOTE) {
395    case '\0':
396        return looking;
397
398    case ':':
399        *pat = res = Strsave(&act[2]);
400        (void) strip(res);
401        return looking;
402
403    default:
404        stderror(ERR_COMPCOM, short2str(act));
405        return TW_ZERO;
406    }
407} /* end tw_result */
408               
409
410/* tw_dollar():
411 *      Expand $<n> args in buffer
412 */
413static Char *
414tw_dollar(str, wl, nwl, buffer, sep, msg)
415    Char *str, **wl;
416    int nwl;
417    Char *buffer;
418    int sep;
419    const char *msg;
420{
421    Char *sp, *bp = buffer, *ebp = &buffer[MAXPATHLEN];
422
423    for (sp = str; *sp && *sp != sep && bp < ebp;)
424        if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) {
425            int num, neg = 0;
426            sp += 2;
427            if (*sp == '-') {
428                neg = 1;
429                sp++;
430            }
431            for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0')
432                continue;
433            if (neg)
434                num = nwl - num - 1;
435            if (num >= 0 && num < nwl) {
436                Char *ptr;
437                for (ptr = wl[num]; *ptr && bp < ebp - 1; *bp++ = *ptr++)
438                    continue;
439               
440            }
441        }
442        else
443            *bp++ = *sp++;
444
445    *bp = '\0';
446
447    if (*sp++ == sep)
448        return sp;
449
450    stderror(ERR_COMPMIS, sep, msg, short2str(str));
451    return --sp;
452} /* end tw_dollar */
453               
454
455/* tw_complete():
456 *      Return the appropriate completion for the command
457 *
458 *      valid completion strings are:
459 *      p/<range>/<completion>/[<suffix>/]      positional
460 *      c/<pattern>/<completion>/[<suffix>/]    current word ignore pattern
461 *      C/<pattern>/<completion>/[<suffix>/]    current word with pattern
462 *      n/<pattern>/<completion>/[<suffix>/]    next word
463 *      N/<pattern>/<completion>/[<suffix>/]    next-next word
464 */
465int
466tw_complete(line, word, pat, looking, suf)
467    Char *line, **word, **pat;
468    int looking, *suf;
469{
470    Char buf[MAXPATHLEN + 1], **vec, *ptr;
471    Char *wl[MAXPATHLEN/6];
472    static Char nomatch[2] = { (Char) ~0, 0x00 };
473    int wordno, n;
474
475    copyn(buf, line, MAXPATHLEN);
476
477    /* find the command */
478    if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR)
479        return TW_ZERO;
480
481    /*
482     * look for hardwired command completions using a globbing
483     * search and for arguments using a normal search.
484     */
485    if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) == NULL)
486        return looking;
487
488    /* tokenize the line one more time :-( */
489    for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL &&
490                      wl[wordno] != INVPTR; wordno++)
491        continue;
492
493    if (wl[wordno] == INVPTR)           /* Found a meta character */
494        return TW_ZERO;                 /* de-activate completions */
495#ifdef TDEBUG
496    {
497        int i;
498        for (i = 0; i < wordno; i++)
499            xprintf("'%s' ", short2str(wl[i]));
500        xprintf("\n");
501    }
502#endif /* TDEBUG */
503
504    /* if the current word is empty move the last word to the next */
505    if (**word == '\0') {
506        wl[wordno] = *word;
507        wordno++;
508    }
509    wl[wordno] = NULL;
510       
511
512#ifdef TDEBUG
513    xprintf("\r\n");
514    xprintf("  w#: %d\n", wordno);
515    xprintf("line: %s\n", short2str(line));
516    xprintf(" cmd: %s\n", short2str(wl[0]));
517    xprintf("word: %s\n", short2str(*word));
518    xprintf("last: %s\n", wordno - 2 >= 0 ? short2str(wl[wordno-2]) : "n/a");
519    xprintf("this: %s\n", wordno - 1 >= 0 ? short2str(wl[wordno-1]) : "n/a");
520#endif /* TDEBUG */
521   
522    for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) {
523        Char  ran[MAXPATHLEN+1],/* The pattern or range X/<range>/XXXX/ */
524              com[MAXPATHLEN+1],/* The completion X/XXXXX/<completion>/ */
525             *pos = NULL;       /* scratch pointer                      */
526        int   cmd, sep;         /* the command and separator characters */
527
528        if (ptr[0] == '\0')
529            continue;
530
531#ifdef TDEBUG
532        xprintf("match %s\n", short2str(ptr));
533#endif /* TDEBUG */
534
535        switch (cmd = ptr[0]) {
536        case 'N':
537            pos = (wordno - 3 < 0) ? nomatch : wl[wordno - 3];
538            break;
539        case 'n':
540            pos = (wordno - 2 < 0) ? nomatch : wl[wordno - 2];
541            break;
542        case 'c':
543        case 'C':
544            pos = (wordno - 1 < 0) ? nomatch : wl[wordno - 1];
545            break;
546        case 'p':
547            break;
548        default:
549            stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd);
550            return TW_ZERO;
551        }
552
553        sep = ptr[1];
554        if (!Ispunct(sep)) {
555            stderror(ERR_COMPINV, CGETS(27, 2, "separator"), sep);
556            return TW_ZERO;
557        }
558
559        ptr = tw_dollar(&ptr[2], wl, wordno, ran, sep,
560                        CGETS(27, 3, "pattern"));
561        if (ran[0] == '\0')     /* check for empty pattern (disallowed) */
562        {
563            stderror(ERR_COMPINC, cmd == 'p' ?  CGETS(27, 4, "range") :
564                     CGETS(27, 3, "pattern"), "");
565            return TW_ZERO;
566        }
567
568        ptr = tw_dollar(ptr, wl, wordno, com, sep, CGETS(27, 5, "completion"));
569
570        if (*ptr != '\0') {
571            if (*ptr == sep)
572                *suf = ~0;
573            else
574                *suf = *ptr;
575        }
576        else
577            *suf = '\0';
578
579#ifdef TDEBUG
580        xprintf("command:    %c\nseparator:  %c\n", cmd, sep);
581        xprintf("pattern:    %s\n", short2str(ran));
582        xprintf("completion: %s\n", short2str(com));
583        xprintf("suffix:     ");
584        switch (*suf) {
585        case 0:
586            xprintf("*auto suffix*\n");
587            break;
588        case ~0:
589            xprintf("*no suffix*\n");
590            break;
591        default:
592            xprintf("%c\n", *suf);
593            break;
594        }
595#endif /* TDEBUG */
596
597        switch (cmd) {
598        case 'p':                       /* positional completion */
599#ifdef TDEBUG
600            xprintf("p: tw_pos(%s, %d) = ", short2str(ran), wordno - 1);
601            xprintf("%d\n", tw_pos(ran, wordno - 1));
602#endif /* TDEBUG */
603            if (!tw_pos(ran, wordno - 1))
604                continue;
605            return tw_result(com, pat);
606
607        case 'N':                       /* match with the next-next word */
608        case 'n':                       /* match with the next word */
609        case 'c':                       /* match with the current word */
610        case 'C':
611#ifdef TDEBUG
612            xprintf("%c: ", cmd);
613#endif /* TDEBUG */
614            if ((n = tw_match(pos, ran)) < 0)
615                continue;
616            if (cmd == 'c')
617                *word += n;
618            return tw_result(com, pat);
619
620        default:
621            return TW_ZERO;     /* Cannot happen */
622        }
623    }
624    *suf = '\0';
625    return TW_ZERO;
626} /* end tw_complete */
Note: See TracBrowser for help on using the repository browser.