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

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