1c80476e4SDavid E. O'Brien /*
2c80476e4SDavid E. O'Brien * tw.comp.c: File completion builtin
3c80476e4SDavid E. O'Brien */
4c80476e4SDavid E. O'Brien /*-
5c80476e4SDavid E. O'Brien * Copyright (c) 1980, 1991 The Regents of the University of California.
6c80476e4SDavid E. O'Brien * All rights reserved.
7c80476e4SDavid E. O'Brien *
8c80476e4SDavid E. O'Brien * Redistribution and use in source and binary forms, with or without
9c80476e4SDavid E. O'Brien * modification, are permitted provided that the following conditions
10c80476e4SDavid E. O'Brien * are met:
11c80476e4SDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright
12c80476e4SDavid E. O'Brien * notice, this list of conditions and the following disclaimer.
13c80476e4SDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright
14c80476e4SDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the
15c80476e4SDavid E. O'Brien * documentation and/or other materials provided with the distribution.
1629301572SMark Peek * 3. Neither the name of the University nor the names of its contributors
17c80476e4SDavid E. O'Brien * may be used to endorse or promote products derived from this software
18c80476e4SDavid E. O'Brien * without specific prior written permission.
19c80476e4SDavid E. O'Brien *
20c80476e4SDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21c80476e4SDavid E. O'Brien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22c80476e4SDavid E. O'Brien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23c80476e4SDavid E. O'Brien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24c80476e4SDavid E. O'Brien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25c80476e4SDavid E. O'Brien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26c80476e4SDavid E. O'Brien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27c80476e4SDavid E. O'Brien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28c80476e4SDavid E. O'Brien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29c80476e4SDavid E. O'Brien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30c80476e4SDavid E. O'Brien * SUCH DAMAGE.
31c80476e4SDavid E. O'Brien */
32c80476e4SDavid E. O'Brien #include "sh.h"
33c80476e4SDavid E. O'Brien #include "tw.h"
34c80476e4SDavid E. O'Brien #include "ed.h"
35c80476e4SDavid E. O'Brien #include "tc.h"
36c80476e4SDavid E. O'Brien
37c80476e4SDavid E. O'Brien /* #define TDEBUG */
38c80476e4SDavid E. O'Brien struct varent completions;
39c80476e4SDavid E. O'Brien
4045e5710bSMark Peek static int tw_result (const Char *, Char **);
4145e5710bSMark Peek static Char **tw_find (Char *, struct varent *, int);
4245e5710bSMark Peek static Char *tw_tok (Char *);
4345e5710bSMark Peek static int tw_pos (Char *, int);
4445e5710bSMark Peek static void tw_pr (Char **);
45*19d2e3deSDmitry Chagin static int tw_match (const Char *, const Char *, int);
4645e5710bSMark Peek static void tw_prlist (struct varent *);
4745e5710bSMark Peek static const Char *tw_dollar (const Char *,Char **, size_t, Char **,
4845e5710bSMark Peek Char, const char *);
49c80476e4SDavid E. O'Brien
50c80476e4SDavid E. O'Brien /* docomplete():
51c80476e4SDavid E. O'Brien * Add or list completions in the completion list
52c80476e4SDavid E. O'Brien */
53c80476e4SDavid E. O'Brien /*ARGSUSED*/
54c80476e4SDavid E. O'Brien void
docomplete(Char ** v,struct command * t)5545e5710bSMark Peek docomplete(Char **v, struct command *t)
56c80476e4SDavid E. O'Brien {
5723338178SMark Peek struct varent *vp;
5823338178SMark Peek Char *p;
59b2d5d167SMark Peek Char **pp;
60c80476e4SDavid E. O'Brien
61c80476e4SDavid E. O'Brien USE(t);
62c80476e4SDavid E. O'Brien v++;
63c80476e4SDavid E. O'Brien p = *v++;
64c80476e4SDavid E. O'Brien if (p == 0)
65c80476e4SDavid E. O'Brien tw_prlist(&completions);
66c80476e4SDavid E. O'Brien else if (*v == 0) {
67c80476e4SDavid E. O'Brien vp = adrof1(strip(p), &completions);
6829301572SMark Peek if (vp && vp->vec)
69c80476e4SDavid E. O'Brien tw_pr(vp->vec), xputchar('\n');
70b2d5d167SMark Peek else
71b2d5d167SMark Peek {
72b2d5d167SMark Peek #ifdef TDEBUG
73b2d5d167SMark Peek xprintf("tw_find(%s) \n", short2str(strip(p)));
74b2d5d167SMark Peek #endif /* TDEBUG */
75b2d5d167SMark Peek pp = tw_find(strip(p), &completions, FALSE);
76b2d5d167SMark Peek if (pp)
77b2d5d167SMark Peek tw_pr(pp), xputchar('\n');
78b2d5d167SMark Peek }
79c80476e4SDavid E. O'Brien }
80c80476e4SDavid E. O'Brien else
81c80476e4SDavid E. O'Brien set1(strip(p), saveblk(v), &completions, VAR_READWRITE);
82c80476e4SDavid E. O'Brien } /* end docomplete */
83c80476e4SDavid E. O'Brien
84c80476e4SDavid E. O'Brien
85c80476e4SDavid E. O'Brien /* douncomplete():
86c80476e4SDavid E. O'Brien * Remove completions from the completion list
87c80476e4SDavid E. O'Brien */
88c80476e4SDavid E. O'Brien /*ARGSUSED*/
89c80476e4SDavid E. O'Brien void
douncomplete(Char ** v,struct command * t)9045e5710bSMark Peek douncomplete(Char **v, struct command *t)
91c80476e4SDavid E. O'Brien {
92c80476e4SDavid E. O'Brien USE(t);
93c80476e4SDavid E. O'Brien unset1(v, &completions);
94c80476e4SDavid E. O'Brien } /* end douncomplete */
95c80476e4SDavid E. O'Brien
96c80476e4SDavid E. O'Brien
97c80476e4SDavid E. O'Brien /* tw_prlist():
98c80476e4SDavid E. O'Brien * Pretty print a list of variables
99c80476e4SDavid E. O'Brien */
100c80476e4SDavid E. O'Brien static void
tw_prlist(struct varent * p)10145e5710bSMark Peek tw_prlist(struct varent *p)
102c80476e4SDavid E. O'Brien {
10323338178SMark Peek struct varent *c;
104c80476e4SDavid E. O'Brien
105c80476e4SDavid E. O'Brien for (;;) {
106c80476e4SDavid E. O'Brien while (p->v_left)
107c80476e4SDavid E. O'Brien p = p->v_left;
108c80476e4SDavid E. O'Brien x:
109c80476e4SDavid E. O'Brien if (p->v_parent == 0) /* is it the header? */
11045e5710bSMark Peek break;
11145e5710bSMark Peek if (setintr) {
11245e5710bSMark Peek int old_pintr_disabled;
11345e5710bSMark Peek
11445e5710bSMark Peek pintr_push_enable(&old_pintr_disabled);
11545e5710bSMark Peek cleanup_until(&old_pintr_disabled);
11645e5710bSMark Peek }
117c80476e4SDavid E. O'Brien xprintf("%s\t", short2str(p->v_name));
11829301572SMark Peek if (p->vec)
119c80476e4SDavid E. O'Brien tw_pr(p->vec);
120c80476e4SDavid E. O'Brien xputchar('\n');
121c80476e4SDavid E. O'Brien if (p->v_right) {
122c80476e4SDavid E. O'Brien p = p->v_right;
123c80476e4SDavid E. O'Brien continue;
124c80476e4SDavid E. O'Brien }
125c80476e4SDavid E. O'Brien do {
126c80476e4SDavid E. O'Brien c = p;
127c80476e4SDavid E. O'Brien p = p->v_parent;
128c80476e4SDavid E. O'Brien } while (p->v_right == c);
129c80476e4SDavid E. O'Brien goto x;
130c80476e4SDavid E. O'Brien }
131c80476e4SDavid E. O'Brien } /* end tw_prlist */
132c80476e4SDavid E. O'Brien
133c80476e4SDavid E. O'Brien
134c80476e4SDavid E. O'Brien /* tw_pr():
135c80476e4SDavid E. O'Brien * Pretty print a completion, adding single quotes around
136c80476e4SDavid E. O'Brien * a completion argument and collapsing multiple spaces to one.
137c80476e4SDavid E. O'Brien */
138c80476e4SDavid E. O'Brien static void
tw_pr(Char ** cmp)13945e5710bSMark Peek tw_pr(Char **cmp)
140c80476e4SDavid E. O'Brien {
14123338178SMark Peek int sp, osp;
142c80476e4SDavid E. O'Brien Char *ptr;
143c80476e4SDavid E. O'Brien
144c80476e4SDavid E. O'Brien for (; *cmp; cmp++) {
145c80476e4SDavid E. O'Brien xputchar('\'');
146c80476e4SDavid E. O'Brien for (osp = 0, ptr = *cmp; *ptr; ptr++) {
147c80476e4SDavid E. O'Brien sp = Isspace(*ptr);
148c80476e4SDavid E. O'Brien if (sp && osp)
149c80476e4SDavid E. O'Brien continue;
15023338178SMark Peek xputwchar(*ptr);
151c80476e4SDavid E. O'Brien osp = sp;
152c80476e4SDavid E. O'Brien }
153c80476e4SDavid E. O'Brien xputchar('\'');
154c80476e4SDavid E. O'Brien if (cmp[1])
155c80476e4SDavid E. O'Brien xputchar(' ');
156c80476e4SDavid E. O'Brien }
157c80476e4SDavid E. O'Brien } /* end tw_pr */
158c80476e4SDavid E. O'Brien
159c80476e4SDavid E. O'Brien
160c80476e4SDavid E. O'Brien /* tw_find():
161c80476e4SDavid E. O'Brien * Find the first matching completion.
162c80476e4SDavid E. O'Brien * For commands we only look at names that start with -
163c80476e4SDavid E. O'Brien */
164c80476e4SDavid E. O'Brien static Char **
tw_find(Char * nam,struct varent * vp,int cmd)16545e5710bSMark Peek tw_find(Char *nam, struct varent *vp, int cmd)
166c80476e4SDavid E. O'Brien {
16723338178SMark Peek Char **rv;
168c80476e4SDavid E. O'Brien
169c80476e4SDavid E. O'Brien for (vp = vp->v_left; vp; vp = vp->v_right) {
170c80476e4SDavid E. O'Brien if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL)
171c80476e4SDavid E. O'Brien return rv;
172c80476e4SDavid E. O'Brien if (cmd) {
173c80476e4SDavid E. O'Brien if (vp->v_name[0] != '-')
174c80476e4SDavid E. O'Brien continue;
175c80476e4SDavid E. O'Brien if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL)
176c80476e4SDavid E. O'Brien return vp->vec;
177c80476e4SDavid E. O'Brien }
178c80476e4SDavid E. O'Brien else
179c80476e4SDavid E. O'Brien if (Gmatch(nam, vp->v_name) && vp->vec != NULL)
180c80476e4SDavid E. O'Brien return vp->vec;
181c80476e4SDavid E. O'Brien }
182c80476e4SDavid E. O'Brien return NULL;
183c80476e4SDavid E. O'Brien } /* end tw_find */
184c80476e4SDavid E. O'Brien
185c80476e4SDavid E. O'Brien
186c80476e4SDavid E. O'Brien /* tw_pos():
187c80476e4SDavid E. O'Brien * Return true if the position is within the specified range
188c80476e4SDavid E. O'Brien */
18923338178SMark Peek static int
tw_pos(Char * ran,int wno)19045e5710bSMark Peek tw_pos(Char *ran, int wno)
191c80476e4SDavid E. O'Brien {
192c80476e4SDavid E. O'Brien Char *p;
193c80476e4SDavid E. O'Brien
194c80476e4SDavid E. O'Brien if (ran[0] == '*' && ran[1] == '\0')
195c80476e4SDavid E. O'Brien return 1;
196c80476e4SDavid E. O'Brien
197c80476e4SDavid E. O'Brien for (p = ran; *p && *p != '-'; p++)
198c80476e4SDavid E. O'Brien continue;
199c80476e4SDavid E. O'Brien
200c80476e4SDavid E. O'Brien if (*p == '\0') /* range == <number> */
201c80476e4SDavid E. O'Brien return wno == getn(ran);
202c80476e4SDavid E. O'Brien
203c80476e4SDavid E. O'Brien if (ran == p) /* range = - <number> */
204c80476e4SDavid E. O'Brien return wno <= getn(&ran[1]);
205c80476e4SDavid E. O'Brien *p++ = '\0';
206c80476e4SDavid E. O'Brien
207c80476e4SDavid E. O'Brien if (*p == '\0') /* range = <number> - */
208c80476e4SDavid E. O'Brien return getn(ran) <= wno;
209c80476e4SDavid E. O'Brien else /* range = <number> - <number> */
210c80476e4SDavid E. O'Brien return (getn(ran) <= wno) && (wno <= getn(p));
211c80476e4SDavid E. O'Brien } /* end tw_pos */
212c80476e4SDavid E. O'Brien
213c80476e4SDavid E. O'Brien
214c80476e4SDavid E. O'Brien /* tw_tok():
215c80476e4SDavid E. O'Brien * Return the next word from string, unquoteing it.
216c80476e4SDavid E. O'Brien */
217c80476e4SDavid E. O'Brien static Char *
tw_tok(Char * str)21845e5710bSMark Peek tw_tok(Char *str)
219c80476e4SDavid E. O'Brien {
220c80476e4SDavid E. O'Brien static Char *bf = NULL;
221c80476e4SDavid E. O'Brien
222c80476e4SDavid E. O'Brien if (str != NULL)
223c80476e4SDavid E. O'Brien bf = str;
224c80476e4SDavid E. O'Brien
225c80476e4SDavid E. O'Brien /* skip leading spaces */
226c80476e4SDavid E. O'Brien for (; *bf && Isspace(*bf); bf++)
227c80476e4SDavid E. O'Brien continue;
228c80476e4SDavid E. O'Brien
229c80476e4SDavid E. O'Brien for (str = bf; *bf && !Isspace(*bf); bf++) {
23023338178SMark Peek if (ismetahash(*bf))
231c80476e4SDavid E. O'Brien return INVPTR;
232c80476e4SDavid E. O'Brien *bf = *bf & ~QUOTE;
233c80476e4SDavid E. O'Brien }
234c80476e4SDavid E. O'Brien if (*bf != '\0')
235c80476e4SDavid E. O'Brien *bf++ = '\0';
236c80476e4SDavid E. O'Brien
237c80476e4SDavid E. O'Brien return *str ? str : NULL;
238c80476e4SDavid E. O'Brien } /* end tw_tok */
239c80476e4SDavid E. O'Brien
240c80476e4SDavid E. O'Brien
241c80476e4SDavid E. O'Brien /* tw_match():
242c80476e4SDavid E. O'Brien * Match a string against the pattern given.
243c80476e4SDavid E. O'Brien * and return the number of matched characters
244c80476e4SDavid E. O'Brien * in a prefix of the string.
245c80476e4SDavid E. O'Brien */
246c80476e4SDavid E. O'Brien static int
tw_match(const Char * str,const Char * pat,int exact)247*19d2e3deSDmitry Chagin tw_match(const Char *str, const Char *pat, int exact)
248c80476e4SDavid E. O'Brien {
24945e5710bSMark Peek const Char *estr;
250*19d2e3deSDmitry Chagin int rv = exact ? Gmatch(estr = str, pat) : Gnmatch(str, pat, &estr);
251c80476e4SDavid E. O'Brien #ifdef TDEBUG
252*19d2e3deSDmitry Chagin xprintf("G%smatch(%s, ", exact ? "" : "n", short2str(str));
253c80476e4SDavid E. O'Brien xprintf("%s, ", short2str(pat));
254*19d2e3deSDmitry Chagin xprintf("%s) = %d [%" TCSH_PTRDIFF_T_FMT "d]\n", short2str(estr), rv,
255*19d2e3deSDmitry Chagin estr - str);
256c80476e4SDavid E. O'Brien #endif /* TDEBUG */
257c80476e4SDavid E. O'Brien return (int) (rv ? estr - str : -1);
258c80476e4SDavid E. O'Brien }
259c80476e4SDavid E. O'Brien
260c80476e4SDavid E. O'Brien
261c80476e4SDavid E. O'Brien /* tw_result():
262c80476e4SDavid E. O'Brien * Return what the completion action should be depending on the
263c80476e4SDavid E. O'Brien * string
264c80476e4SDavid E. O'Brien */
265c80476e4SDavid E. O'Brien static int
tw_result(const Char * act,Char ** pat)26645e5710bSMark Peek tw_result(const Char *act, Char **pat)
267c80476e4SDavid E. O'Brien {
268c80476e4SDavid E. O'Brien int looking;
269c80476e4SDavid E. O'Brien static Char* res = NULL;
27045e5710bSMark Peek Char *p;
271c80476e4SDavid E. O'Brien
272c80476e4SDavid E. O'Brien if (res != NULL)
27345e5710bSMark Peek xfree(res), res = NULL;
274c80476e4SDavid E. O'Brien
275c80476e4SDavid E. O'Brien switch (act[0] & ~QUOTE) {
276c80476e4SDavid E. O'Brien case 'X':
277c80476e4SDavid E. O'Brien looking = TW_COMPLETION;
278c80476e4SDavid E. O'Brien break;
279c80476e4SDavid E. O'Brien case 'S':
280c80476e4SDavid E. O'Brien looking = TW_SIGNAL;
281c80476e4SDavid E. O'Brien break;
282c80476e4SDavid E. O'Brien case 'a':
283c80476e4SDavid E. O'Brien looking = TW_ALIAS;
284c80476e4SDavid E. O'Brien break;
285c80476e4SDavid E. O'Brien case 'b':
286c80476e4SDavid E. O'Brien looking = TW_BINDING;
287c80476e4SDavid E. O'Brien break;
288c80476e4SDavid E. O'Brien case 'c':
289c80476e4SDavid E. O'Brien looking = TW_COMMAND;
290c80476e4SDavid E. O'Brien break;
291c80476e4SDavid E. O'Brien case 'C':
292c80476e4SDavid E. O'Brien looking = TW_PATH | TW_COMMAND;
293c80476e4SDavid E. O'Brien break;
294c80476e4SDavid E. O'Brien case 'd':
295c80476e4SDavid E. O'Brien looking = TW_DIRECTORY;
296c80476e4SDavid E. O'Brien break;
297c80476e4SDavid E. O'Brien case 'D':
298c80476e4SDavid E. O'Brien looking = TW_PATH | TW_DIRECTORY;
299c80476e4SDavid E. O'Brien break;
300c80476e4SDavid E. O'Brien case 'e':
301c80476e4SDavid E. O'Brien looking = TW_ENVVAR;
302c80476e4SDavid E. O'Brien break;
303c80476e4SDavid E. O'Brien case 'f':
304c80476e4SDavid E. O'Brien looking = TW_FILE;
305c80476e4SDavid E. O'Brien break;
306c80476e4SDavid E. O'Brien #ifdef COMPAT
307c80476e4SDavid E. O'Brien case 'p':
308c80476e4SDavid E. O'Brien #endif /* COMPAT */
309c80476e4SDavid E. O'Brien case 'F':
310c80476e4SDavid E. O'Brien looking = TW_PATH | TW_FILE;
311c80476e4SDavid E. O'Brien break;
312c80476e4SDavid E. O'Brien case 'g':
313c80476e4SDavid E. O'Brien looking = TW_GRPNAME;
314c80476e4SDavid E. O'Brien break;
315c80476e4SDavid E. O'Brien case 'j':
316c80476e4SDavid E. O'Brien looking = TW_JOB;
317c80476e4SDavid E. O'Brien break;
318c80476e4SDavid E. O'Brien case 'l':
319c80476e4SDavid E. O'Brien looking = TW_LIMIT;
320c80476e4SDavid E. O'Brien break;
321c80476e4SDavid E. O'Brien case 'n':
322c80476e4SDavid E. O'Brien looking = TW_NONE;
323c80476e4SDavid E. O'Brien break;
324c80476e4SDavid E. O'Brien case 's':
325c80476e4SDavid E. O'Brien looking = TW_SHELLVAR;
326c80476e4SDavid E. O'Brien break;
327c80476e4SDavid E. O'Brien case 't':
328c80476e4SDavid E. O'Brien looking = TW_TEXT;
329c80476e4SDavid E. O'Brien break;
330c80476e4SDavid E. O'Brien case 'T':
331c80476e4SDavid E. O'Brien looking = TW_PATH | TW_TEXT;
332c80476e4SDavid E. O'Brien break;
333c80476e4SDavid E. O'Brien case 'v':
334c80476e4SDavid E. O'Brien looking = TW_VARIABLE;
335c80476e4SDavid E. O'Brien break;
336c80476e4SDavid E. O'Brien case 'u':
337c80476e4SDavid E. O'Brien looking = TW_USER;
338c80476e4SDavid E. O'Brien break;
339c80476e4SDavid E. O'Brien case 'x':
340c80476e4SDavid E. O'Brien looking = TW_EXPLAIN;
341c80476e4SDavid E. O'Brien break;
342c80476e4SDavid E. O'Brien
343c80476e4SDavid E. O'Brien case '$':
344c80476e4SDavid E. O'Brien *pat = res = Strsave(&act[1]);
345c80476e4SDavid E. O'Brien (void) strip(res);
346c80476e4SDavid E. O'Brien return(TW_VARLIST);
347c80476e4SDavid E. O'Brien
348c80476e4SDavid E. O'Brien case '(':
349c80476e4SDavid E. O'Brien *pat = res = Strsave(&act[1]);
35045e5710bSMark Peek if ((p = Strchr(res, ')')) != NULL)
35145e5710bSMark Peek *p = '\0';
352c80476e4SDavid E. O'Brien (void) strip(res);
353c80476e4SDavid E. O'Brien return TW_WORDLIST;
354c80476e4SDavid E. O'Brien
355c80476e4SDavid E. O'Brien case '`':
356c80476e4SDavid E. O'Brien res = Strsave(act);
35745e5710bSMark Peek if ((p = Strchr(&res[1], '`')) != NULL)
35845e5710bSMark Peek *++p = '\0';
359c80476e4SDavid E. O'Brien
360c80476e4SDavid E. O'Brien if (didfds == 0) {
361c80476e4SDavid E. O'Brien /*
362c80476e4SDavid E. O'Brien * Make sure that we have some file descriptors to
363c80476e4SDavid E. O'Brien * play with, so that the processes have at least 0, 1, 2
364c80476e4SDavid E. O'Brien * open
365c80476e4SDavid E. O'Brien */
366c80476e4SDavid E. O'Brien (void) dcopy(SHIN, 0);
367c80476e4SDavid E. O'Brien (void) dcopy(SHOUT, 1);
368c80476e4SDavid E. O'Brien (void) dcopy(SHDIAG, 2);
369c80476e4SDavid E. O'Brien }
37045e5710bSMark Peek if ((p = globone(res, G_APPEND)) != NULL) {
37145e5710bSMark Peek xfree(res), res = NULL;
37245e5710bSMark Peek *pat = res = Strsave(p);
37345e5710bSMark Peek xfree(p);
374c80476e4SDavid E. O'Brien return TW_WORDLIST;
375c80476e4SDavid E. O'Brien }
376c80476e4SDavid E. O'Brien return TW_ZERO;
377c80476e4SDavid E. O'Brien
378c80476e4SDavid E. O'Brien default:
379c80476e4SDavid E. O'Brien stderror(ERR_COMPCOM, short2str(act));
380c80476e4SDavid E. O'Brien return TW_ZERO;
381c80476e4SDavid E. O'Brien }
382c80476e4SDavid E. O'Brien
383c80476e4SDavid E. O'Brien switch (act[1] & ~QUOTE) {
384c80476e4SDavid E. O'Brien case '\0':
385c80476e4SDavid E. O'Brien return looking;
386c80476e4SDavid E. O'Brien
387c80476e4SDavid E. O'Brien case ':':
388c80476e4SDavid E. O'Brien *pat = res = Strsave(&act[2]);
389c80476e4SDavid E. O'Brien (void) strip(res);
390c80476e4SDavid E. O'Brien return looking;
391c80476e4SDavid E. O'Brien
392c80476e4SDavid E. O'Brien default:
393c80476e4SDavid E. O'Brien stderror(ERR_COMPCOM, short2str(act));
394c80476e4SDavid E. O'Brien return TW_ZERO;
395c80476e4SDavid E. O'Brien }
396c80476e4SDavid E. O'Brien } /* end tw_result */
397c80476e4SDavid E. O'Brien
398c80476e4SDavid E. O'Brien
399c80476e4SDavid E. O'Brien /* tw_dollar():
400c80476e4SDavid E. O'Brien * Expand $<n> args in buffer
401c80476e4SDavid E. O'Brien */
40245e5710bSMark Peek static const Char *
tw_dollar(const Char * str,Char ** wl,size_t nwl,Char ** result,Char sep,const char * msg)40345e5710bSMark Peek tw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep,
40445e5710bSMark Peek const char *msg)
405c80476e4SDavid E. O'Brien {
40645e5710bSMark Peek struct Strbuf buf = Strbuf_INIT;
40745e5710bSMark Peek Char *res;
40845e5710bSMark Peek const Char *sp;
409c80476e4SDavid E. O'Brien
41045e5710bSMark Peek for (sp = str; *sp && *sp != sep;)
411c80476e4SDavid E. O'Brien if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) {
412c80476e4SDavid E. O'Brien int num, neg = 0;
413c80476e4SDavid E. O'Brien sp += 2;
414c80476e4SDavid E. O'Brien if (*sp == '-') {
415c80476e4SDavid E. O'Brien neg = 1;
416c80476e4SDavid E. O'Brien sp++;
417c80476e4SDavid E. O'Brien }
418c80476e4SDavid E. O'Brien for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0')
419c80476e4SDavid E. O'Brien continue;
420c80476e4SDavid E. O'Brien if (neg)
421c80476e4SDavid E. O'Brien num = nwl - num - 1;
42245e5710bSMark Peek if (num >= 0 && (size_t)num < nwl)
42345e5710bSMark Peek Strbuf_append(&buf, wl[num]);
424c80476e4SDavid E. O'Brien }
425c80476e4SDavid E. O'Brien else
42645e5710bSMark Peek Strbuf_append1(&buf, *sp++);
427c80476e4SDavid E. O'Brien
42845e5710bSMark Peek res = Strbuf_finish(&buf);
429c80476e4SDavid E. O'Brien
43045e5710bSMark Peek if (*sp++ == sep) {
43145e5710bSMark Peek *result = res;
432c80476e4SDavid E. O'Brien return sp;
43345e5710bSMark Peek }
434c80476e4SDavid E. O'Brien
43545e5710bSMark Peek xfree(res);
43623338178SMark Peek /* Truncates data if WIDE_STRINGS */
43723338178SMark Peek stderror(ERR_COMPMIS, (int)sep, msg, short2str(str));
438c80476e4SDavid E. O'Brien return --sp;
439c80476e4SDavid E. O'Brien } /* end tw_dollar */
440c80476e4SDavid E. O'Brien
441c80476e4SDavid E. O'Brien
442c80476e4SDavid E. O'Brien /* tw_complete():
443c80476e4SDavid E. O'Brien * Return the appropriate completion for the command
444c80476e4SDavid E. O'Brien *
445c80476e4SDavid E. O'Brien * valid completion strings are:
446c80476e4SDavid E. O'Brien * p/<range>/<completion>/[<suffix>/] positional
447c80476e4SDavid E. O'Brien * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern
448c80476e4SDavid E. O'Brien * C/<pattern>/<completion>/[<suffix>/] current word with pattern
449c80476e4SDavid E. O'Brien * n/<pattern>/<completion>/[<suffix>/] next word
450c80476e4SDavid E. O'Brien * N/<pattern>/<completion>/[<suffix>/] next-next word
451c80476e4SDavid E. O'Brien */
452c80476e4SDavid E. O'Brien int
tw_complete(const Char * line,Char ** word,Char ** pat,int looking,eChar * suf)45345e5710bSMark Peek tw_complete(const Char *line, Char **word, Char **pat, int looking, eChar *suf)
454c80476e4SDavid E. O'Brien {
45545e5710bSMark Peek Char *buf, **vec, **wl;
456c80476e4SDavid E. O'Brien static Char nomatch[2] = { (Char) ~0, 0x00 };
45745e5710bSMark Peek const Char *ptr;
45845e5710bSMark Peek size_t wordno;
45945e5710bSMark Peek int n;
460c80476e4SDavid E. O'Brien
46145e5710bSMark Peek buf = Strsave(line);
46245e5710bSMark Peek cleanup_push(buf, xfree);
46345e5710bSMark Peek /* Single-character words, empty current word, terminating NULL */
46445e5710bSMark Peek wl = xmalloc(((Strlen(line) + 1) / 2 + 2) * sizeof (*wl));
46545e5710bSMark Peek cleanup_push(wl, xfree);
466c80476e4SDavid E. O'Brien
467c80476e4SDavid E. O'Brien /* find the command */
46845e5710bSMark Peek if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) {
46945e5710bSMark Peek cleanup_until(buf);
470c80476e4SDavid E. O'Brien return TW_ZERO;
47145e5710bSMark Peek }
472c80476e4SDavid E. O'Brien
473c80476e4SDavid E. O'Brien /*
474c80476e4SDavid E. O'Brien * look for hardwired command completions using a globbing
475c80476e4SDavid E. O'Brien * search and for arguments using a normal search.
476c80476e4SDavid E. O'Brien */
47745e5710bSMark Peek if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND)))
47845e5710bSMark Peek == NULL) {
47945e5710bSMark Peek cleanup_until(buf);
480c80476e4SDavid E. O'Brien return looking;
48145e5710bSMark Peek }
482c80476e4SDavid E. O'Brien
483c80476e4SDavid E. O'Brien /* tokenize the line one more time :-( */
484c80476e4SDavid E. O'Brien for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL &&
485c80476e4SDavid E. O'Brien wl[wordno] != INVPTR; wordno++)
486c80476e4SDavid E. O'Brien continue;
487c80476e4SDavid E. O'Brien
48845e5710bSMark Peek if (wl[wordno] == INVPTR) { /* Found a meta character */
48945e5710bSMark Peek cleanup_until(buf);
490c80476e4SDavid E. O'Brien return TW_ZERO; /* de-activate completions */
49145e5710bSMark Peek }
492c80476e4SDavid E. O'Brien #ifdef TDEBUG
493c80476e4SDavid E. O'Brien {
49445e5710bSMark Peek size_t i;
495c80476e4SDavid E. O'Brien for (i = 0; i < wordno; i++)
496c80476e4SDavid E. O'Brien xprintf("'%s' ", short2str(wl[i]));
497c80476e4SDavid E. O'Brien xprintf("\n");
498c80476e4SDavid E. O'Brien }
499c80476e4SDavid E. O'Brien #endif /* TDEBUG */
500c80476e4SDavid E. O'Brien
501c80476e4SDavid E. O'Brien /* if the current word is empty move the last word to the next */
502c80476e4SDavid E. O'Brien if (**word == '\0') {
503c80476e4SDavid E. O'Brien wl[wordno] = *word;
504c80476e4SDavid E. O'Brien wordno++;
505c80476e4SDavid E. O'Brien }
506c80476e4SDavid E. O'Brien wl[wordno] = NULL;
507c80476e4SDavid E. O'Brien
508c80476e4SDavid E. O'Brien
509c80476e4SDavid E. O'Brien #ifdef TDEBUG
510c80476e4SDavid E. O'Brien xprintf("\r\n");
51145e5710bSMark Peek xprintf(" w#: %lu\n", (unsigned long)wordno);
512c80476e4SDavid E. O'Brien xprintf("line: %s\n", short2str(line));
513c80476e4SDavid E. O'Brien xprintf(" cmd: %s\n", short2str(wl[0]));
514c80476e4SDavid E. O'Brien xprintf("word: %s\n", short2str(*word));
51545e5710bSMark Peek xprintf("last: %s\n", wordno >= 2 ? short2str(wl[wordno-2]) : "n/a");
51645e5710bSMark Peek xprintf("this: %s\n", wordno >= 1 ? short2str(wl[wordno-1]) : "n/a");
517c80476e4SDavid E. O'Brien #endif /* TDEBUG */
518c80476e4SDavid E. O'Brien
519c80476e4SDavid E. O'Brien for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) {
52045e5710bSMark Peek Char *ran, /* The pattern or range X/<range>/XXXX/ */
52145e5710bSMark Peek *com, /* The completion X/XXXXX/<completion>/ */
522c80476e4SDavid E. O'Brien *pos = NULL; /* scratch pointer */
52345e5710bSMark Peek int cmd, res;
52423338178SMark Peek Char sep; /* the command and separator characters */
525*19d2e3deSDmitry Chagin int exact;
526c80476e4SDavid E. O'Brien
527c80476e4SDavid E. O'Brien if (ptr[0] == '\0')
528c80476e4SDavid E. O'Brien continue;
529c80476e4SDavid E. O'Brien
530c80476e4SDavid E. O'Brien #ifdef TDEBUG
531c80476e4SDavid E. O'Brien xprintf("match %s\n", short2str(ptr));
532c80476e4SDavid E. O'Brien #endif /* TDEBUG */
533c80476e4SDavid E. O'Brien
534c80476e4SDavid E. O'Brien switch (cmd = ptr[0]) {
535c80476e4SDavid E. O'Brien case 'N':
53645e5710bSMark Peek pos = (wordno < 3) ? nomatch : wl[wordno - 3];
537c80476e4SDavid E. O'Brien break;
538c80476e4SDavid E. O'Brien case 'n':
53945e5710bSMark Peek pos = (wordno < 2) ? nomatch : wl[wordno - 2];
540c80476e4SDavid E. O'Brien break;
541c80476e4SDavid E. O'Brien case 'c':
542c80476e4SDavid E. O'Brien case 'C':
54345e5710bSMark Peek pos = (wordno < 1) ? nomatch : wl[wordno - 1];
544c80476e4SDavid E. O'Brien break;
545c80476e4SDavid E. O'Brien case 'p':
546c80476e4SDavid E. O'Brien break;
547c80476e4SDavid E. O'Brien default:
548c80476e4SDavid E. O'Brien stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd);
549c80476e4SDavid E. O'Brien return TW_ZERO;
550c80476e4SDavid E. O'Brien }
551c80476e4SDavid E. O'Brien
552c80476e4SDavid E. O'Brien sep = ptr[1];
553c80476e4SDavid E. O'Brien if (!Ispunct(sep)) {
55423338178SMark Peek /* Truncates data if WIDE_STRINGS */
55523338178SMark Peek stderror(ERR_COMPINV, CGETS(27, 2, "separator"), (int)sep);
556c80476e4SDavid E. O'Brien return TW_ZERO;
557c80476e4SDavid E. O'Brien }
558c80476e4SDavid E. O'Brien
55945e5710bSMark Peek ptr = tw_dollar(&ptr[2], wl, wordno, &ran, sep,
560c80476e4SDavid E. O'Brien CGETS(27, 3, "pattern"));
56145e5710bSMark Peek cleanup_push(ran, xfree);
562c80476e4SDavid E. O'Brien if (ran[0] == '\0') /* check for empty pattern (disallowed) */
563c80476e4SDavid E. O'Brien {
564c80476e4SDavid E. O'Brien stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") :
565c80476e4SDavid E. O'Brien CGETS(27, 3, "pattern"), "");
566c80476e4SDavid E. O'Brien return TW_ZERO;
567c80476e4SDavid E. O'Brien }
568c80476e4SDavid E. O'Brien
56945e5710bSMark Peek ptr = tw_dollar(ptr, wl, wordno, &com, sep,
57045e5710bSMark Peek CGETS(27, 5, "completion"));
57145e5710bSMark Peek cleanup_push(com, xfree);
572c80476e4SDavid E. O'Brien
573c80476e4SDavid E. O'Brien if (*ptr != '\0') {
574c80476e4SDavid E. O'Brien if (*ptr == sep)
57545e5710bSMark Peek *suf = CHAR_ERR;
576c80476e4SDavid E. O'Brien else
577c80476e4SDavid E. O'Brien *suf = *ptr;
578c80476e4SDavid E. O'Brien }
579c80476e4SDavid E. O'Brien else
580c80476e4SDavid E. O'Brien *suf = '\0';
581c80476e4SDavid E. O'Brien
582c80476e4SDavid E. O'Brien #ifdef TDEBUG
58323338178SMark Peek xprintf("command: %c\nseparator: %c\n", cmd, (int)sep);
584c80476e4SDavid E. O'Brien xprintf("pattern: %s\n", short2str(ran));
585c80476e4SDavid E. O'Brien xprintf("completion: %s\n", short2str(com));
586c80476e4SDavid E. O'Brien xprintf("suffix: ");
587c80476e4SDavid E. O'Brien switch (*suf) {
588c80476e4SDavid E. O'Brien case 0:
589c80476e4SDavid E. O'Brien xprintf("*auto suffix*\n");
590c80476e4SDavid E. O'Brien break;
59145e5710bSMark Peek case CHAR_ERR:
592c80476e4SDavid E. O'Brien xprintf("*no suffix*\n");
593c80476e4SDavid E. O'Brien break;
594c80476e4SDavid E. O'Brien default:
59545e5710bSMark Peek xprintf("%c\n", (int)*suf);
596c80476e4SDavid E. O'Brien break;
597c80476e4SDavid E. O'Brien }
598c80476e4SDavid E. O'Brien #endif /* TDEBUG */
599c80476e4SDavid E. O'Brien
600*19d2e3deSDmitry Chagin exact = 0;
601c80476e4SDavid E. O'Brien switch (cmd) {
602c80476e4SDavid E. O'Brien case 'p': /* positional completion */
603c80476e4SDavid E. O'Brien #ifdef TDEBUG
60445e5710bSMark Peek xprintf("p: tw_pos(%s, %lu) = ", short2str(ran),
60545e5710bSMark Peek (unsigned long)wordno - 1);
606c80476e4SDavid E. O'Brien xprintf("%d\n", tw_pos(ran, wordno - 1));
607c80476e4SDavid E. O'Brien #endif /* TDEBUG */
60845e5710bSMark Peek if (!tw_pos(ran, wordno - 1)) {
60945e5710bSMark Peek cleanup_until(ran);
610c80476e4SDavid E. O'Brien continue;
61145e5710bSMark Peek }
61245e5710bSMark Peek break;
613c80476e4SDavid E. O'Brien
614c80476e4SDavid E. O'Brien case 'N': /* match with the next-next word */
615c80476e4SDavid E. O'Brien case 'n': /* match with the next word */
616*19d2e3deSDmitry Chagin exact = 1;
617*19d2e3deSDmitry Chagin /*FALLTHROUGH*/
618c80476e4SDavid E. O'Brien case 'c': /* match with the current word */
619c80476e4SDavid E. O'Brien case 'C':
620c80476e4SDavid E. O'Brien #ifdef TDEBUG
621c80476e4SDavid E. O'Brien xprintf("%c: ", cmd);
622c80476e4SDavid E. O'Brien #endif /* TDEBUG */
623*19d2e3deSDmitry Chagin if ((n = tw_match(pos, ran, exact)) < 0) {
62445e5710bSMark Peek cleanup_until(ran);
625c80476e4SDavid E. O'Brien continue;
62645e5710bSMark Peek }
627c80476e4SDavid E. O'Brien if (cmd == 'c')
628c80476e4SDavid E. O'Brien *word += n;
62945e5710bSMark Peek break;
630c80476e4SDavid E. O'Brien
631c80476e4SDavid E. O'Brien default:
63245e5710bSMark Peek abort(); /* Cannot happen */
633c80476e4SDavid E. O'Brien }
634a15e6f9aSMark Peek tsetenv(STRCOMMAND_LINE, line);
63545e5710bSMark Peek res = tw_result(com, pat);
636a15e6f9aSMark Peek Unsetenv(STRCOMMAND_LINE);
63745e5710bSMark Peek cleanup_until(buf);
63845e5710bSMark Peek return res;
639c80476e4SDavid E. O'Brien }
64045e5710bSMark Peek cleanup_until(buf);
641c80476e4SDavid E. O'Brien *suf = '\0';
642c80476e4SDavid E. O'Brien return TW_ZERO;
643c80476e4SDavid E. O'Brien } /* end tw_complete */
644