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