]>
Commit | Line | Data |
---|---|---|
3185942a | 1 | /* pcomplete.c - functions to generate lists of matches for programmable completion. */ |
bb70624e | 2 | |
74091dd4 | 3 | /* Copyright (C) 1999-2021 Free Software Foundation, Inc. |
bb70624e JA |
4 | |
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
3185942a JA |
7 | Bash is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
bb70624e | 11 | |
3185942a JA |
12 | Bash is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
bb70624e | 16 | |
3185942a JA |
17 | You should have received a copy of the GNU General Public License |
18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
bb70624e JA |
20 | |
21 | #include <config.h> | |
22 | ||
23 | #if defined (PROGRAMMABLE_COMPLETION) | |
24 | ||
25 | #include "bashtypes.h" | |
26 | #include "posixstat.h" | |
27 | ||
28 | #if defined (HAVE_UNISTD_H) | |
29 | # include <unistd.h> | |
30 | #endif | |
31 | ||
32 | #include <signal.h> | |
33 | ||
34 | #if defined (PREFER_STDARG) | |
35 | # include <stdarg.h> | |
36 | #else | |
7117c2d2 | 37 | # include <varargs.h> |
bb70624e JA |
38 | #endif |
39 | ||
8868edaf | 40 | #include "posixtime.h" |
ac50fbac | 41 | |
bb70624e JA |
42 | #include <stdio.h> |
43 | #include "bashansi.h" | |
b80f6443 | 44 | #include "bashintl.h" |
bb70624e JA |
45 | |
46 | #include "shell.h" | |
47 | #include "pcomplete.h" | |
48 | #include "alias.h" | |
49 | #include "bashline.h" | |
f73dda09 | 50 | #include "execute_cmd.h" |
bb70624e JA |
51 | #include "pathexp.h" |
52 | ||
53 | #if defined (JOB_CONTROL) | |
54 | # include "jobs.h" | |
55 | #endif | |
56 | ||
57 | #if !defined (NSIG) | |
58 | # include "trap.h" | |
59 | #endif | |
60 | ||
ac50fbac CR |
61 | #include "shmbutil.h" |
62 | ||
bb70624e JA |
63 | #include "builtins.h" |
64 | #include "builtins/common.h" | |
ac50fbac | 65 | #include "builtins/builtext.h" |
bb70624e JA |
66 | |
67 | #include <glob/glob.h> | |
f73dda09 | 68 | #include <glob/strmatch.h> |
bb70624e JA |
69 | |
70 | #include <readline/rlconf.h> | |
71 | #include <readline/readline.h> | |
72 | #include <readline/history.h> | |
73 | ||
74 | #ifdef STRDUP | |
75 | # undef STRDUP | |
76 | #endif | |
77 | #define STRDUP(x) ((x) ? savestring (x) : (char *)NULL) | |
78 | ||
79 | typedef SHELL_VAR **SVFUNC (); | |
80 | ||
81 | #ifndef HAVE_STRPBRK | |
8868edaf | 82 | extern char *strpbrk PARAMS((char *, char *)); |
bb70624e JA |
83 | #endif |
84 | ||
bb70624e JA |
85 | extern STRING_INT_ALIST word_token_alist[]; |
86 | extern char *signal_names[]; | |
87 | ||
7117c2d2 JA |
88 | #if defined (DEBUG) |
89 | #if defined (PREFER_STDARG) | |
f73dda09 JA |
90 | static void debug_printf (const char *, ...) __attribute__((__format__ (printf, 1, 2))); |
91 | #endif | |
7117c2d2 | 92 | #endif /* DEBUG */ |
f73dda09 | 93 | |
8868edaf CR |
94 | static int it_init_joblist PARAMS((ITEMLIST *, int)); |
95 | ||
96 | static int it_init_aliases PARAMS((ITEMLIST *)); | |
97 | static int it_init_arrayvars PARAMS((ITEMLIST *)); | |
98 | static int it_init_bindings PARAMS((ITEMLIST *)); | |
99 | static int it_init_builtins PARAMS((ITEMLIST *)); | |
100 | static int it_init_disabled PARAMS((ITEMLIST *)); | |
101 | static int it_init_enabled PARAMS((ITEMLIST *)); | |
102 | static int it_init_exported PARAMS((ITEMLIST *)); | |
103 | static int it_init_functions PARAMS((ITEMLIST *)); | |
104 | static int it_init_helptopics PARAMS((ITEMLIST *)); | |
105 | static int it_init_hostnames PARAMS((ITEMLIST *)); | |
106 | static int it_init_jobs PARAMS((ITEMLIST *)); | |
107 | static int it_init_running PARAMS((ITEMLIST *)); | |
108 | static int it_init_stopped PARAMS((ITEMLIST *)); | |
109 | static int it_init_keywords PARAMS((ITEMLIST *)); | |
110 | static int it_init_signals PARAMS((ITEMLIST *)); | |
111 | static int it_init_variables PARAMS((ITEMLIST *)); | |
112 | static int it_init_setopts PARAMS((ITEMLIST *)); | |
113 | static int it_init_shopts PARAMS((ITEMLIST *)); | |
114 | ||
115 | static int shouldexp_filterpat PARAMS((char *)); | |
116 | static char *preproc_filterpat PARAMS((char *, const char *)); | |
117 | ||
118 | static void init_itemlist_from_varlist PARAMS((ITEMLIST *, SVFUNC *)); | |
119 | ||
120 | static STRINGLIST *gen_matches_from_itemlist PARAMS((ITEMLIST *, const char *)); | |
121 | static STRINGLIST *gen_action_completions PARAMS((COMPSPEC *, const char *)); | |
122 | static STRINGLIST *gen_globpat_matches PARAMS((COMPSPEC *, const char *)); | |
123 | static STRINGLIST *gen_wordlist_matches PARAMS((COMPSPEC *, const char *)); | |
124 | static STRINGLIST *gen_shell_function_matches PARAMS((COMPSPEC *, const char *, | |
ac50fbac | 125 | const char *, |
f73dda09 | 126 | char *, int, WORD_LIST *, |
0001803f | 127 | int, int, int *)); |
8868edaf | 128 | static STRINGLIST *gen_command_matches PARAMS((COMPSPEC *, const char *, |
ac50fbac CR |
129 | const char *, |
130 | char *, int, WORD_LIST *, | |
131 | int, int)); | |
f73dda09 | 132 | |
8868edaf | 133 | static STRINGLIST *gen_progcomp_completions PARAMS((const char *, const char *, |
0001803f CR |
134 | const char *, |
135 | int, int, int *, int *, | |
136 | COMPSPEC **)); | |
137 | ||
8868edaf | 138 | static char *pcomp_filename_completion_function PARAMS((const char *, int)); |
f73dda09 JA |
139 | |
140 | #if defined (ARRAY_VARS) | |
8868edaf | 141 | static SHELL_VAR *bind_comp_words PARAMS((WORD_LIST *)); |
f73dda09 | 142 | #endif |
8868edaf CR |
143 | static void bind_compfunc_variables PARAMS((char *, int, WORD_LIST *, int, int)); |
144 | static void unbind_compfunc_variables PARAMS((int)); | |
145 | static WORD_LIST *build_arg_list PARAMS((char *, const char *, const char *, WORD_LIST *, int)); | |
146 | static WORD_LIST *command_line_to_word_list PARAMS((char *, int, int, int *, int *)); | |
bb70624e | 147 | |
7117c2d2 | 148 | #ifdef DEBUG |
bb70624e | 149 | static int progcomp_debug = 0; |
7117c2d2 | 150 | #endif |
bb70624e JA |
151 | |
152 | int prog_completion_enabled = 1; | |
153 | ||
d233b485 CR |
154 | #ifdef ALIAS |
155 | int progcomp_alias = 0; /* unavailable to user code for now */ | |
156 | #endif | |
157 | ||
bb70624e JA |
158 | /* These are used to manage the arrays of strings for possible completions. */ |
159 | ITEMLIST it_aliases = { 0, it_init_aliases, (STRINGLIST *)0 }; | |
160 | ITEMLIST it_arrayvars = { LIST_DYNAMIC, it_init_arrayvars, (STRINGLIST *)0 }; | |
161 | ITEMLIST it_bindings = { 0, it_init_bindings, (STRINGLIST *)0 }; | |
162 | ITEMLIST it_builtins = { 0, it_init_builtins, (STRINGLIST *)0 }; | |
163 | ITEMLIST it_commands = { LIST_DYNAMIC }; /* unused */ | |
164 | ITEMLIST it_directories = { LIST_DYNAMIC }; /* unused */ | |
165 | ITEMLIST it_disabled = { 0, it_init_disabled, (STRINGLIST *)0 }; | |
166 | ITEMLIST it_enabled = { 0, it_init_enabled, (STRINGLIST *)0 }; | |
167 | ITEMLIST it_exports = { LIST_DYNAMIC, it_init_exported, (STRINGLIST *)0 }; | |
7117c2d2 | 168 | ITEMLIST it_files = { LIST_DYNAMIC }; /* unused */ |
bb70624e | 169 | ITEMLIST it_functions = { 0, it_init_functions, (STRINGLIST *)0 }; |
ac50fbac | 170 | ITEMLIST it_helptopics = { 0, it_init_helptopics, (STRINGLIST *)0 }; |
bb70624e | 171 | ITEMLIST it_hostnames = { LIST_DYNAMIC, it_init_hostnames, (STRINGLIST *)0 }; |
7117c2d2 | 172 | ITEMLIST it_groups = { LIST_DYNAMIC }; /* unused */ |
f73dda09 | 173 | ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 }; |
bb70624e JA |
174 | ITEMLIST it_keywords = { 0, it_init_keywords, (STRINGLIST *)0 }; |
175 | ITEMLIST it_running = { LIST_DYNAMIC, it_init_running, (STRINGLIST *)0 }; | |
7117c2d2 | 176 | ITEMLIST it_services = { LIST_DYNAMIC }; /* unused */ |
bb70624e JA |
177 | ITEMLIST it_setopts = { 0, it_init_setopts, (STRINGLIST *)0 }; |
178 | ITEMLIST it_shopts = { 0, it_init_shopts, (STRINGLIST *)0 }; | |
179 | ITEMLIST it_signals = { 0, it_init_signals, (STRINGLIST *)0 }; | |
180 | ITEMLIST it_stopped = { LIST_DYNAMIC, it_init_stopped, (STRINGLIST *)0 }; | |
7117c2d2 | 181 | ITEMLIST it_users = { LIST_DYNAMIC }; /* unused */ |
bb70624e JA |
182 | ITEMLIST it_variables = { LIST_DYNAMIC, it_init_variables, (STRINGLIST *)0 }; |
183 | ||
3185942a JA |
184 | COMPSPEC *pcomp_curcs; |
185 | const char *pcomp_curcmd; | |
2b76266c | 186 | const char *pcomp_curtxt; |
3185942a | 187 | |
d233b485 CR |
188 | char *pcomp_line; |
189 | int pcomp_ind; | |
190 | ||
7117c2d2 | 191 | #ifdef DEBUG |
bb70624e | 192 | /* Debugging code */ |
bb70624e JA |
193 | static void |
194 | #if defined (PREFER_STDARG) | |
195 | debug_printf (const char *format, ...) | |
196 | #else | |
197 | debug_printf (format, va_alist) | |
198 | const char *format; | |
199 | va_dcl | |
200 | #endif | |
201 | { | |
202 | va_list args; | |
203 | ||
204 | if (progcomp_debug == 0) | |
205 | return; | |
206 | ||
7117c2d2 | 207 | SH_VA_START (args, format); |
bb70624e JA |
208 | |
209 | fprintf (stdout, "DEBUG: "); | |
210 | vfprintf (stdout, format, args); | |
211 | fprintf (stdout, "\n"); | |
212 | ||
213 | rl_on_new_line (); | |
214 | ||
215 | va_end (args); | |
216 | } | |
7117c2d2 | 217 | #endif |
bb70624e JA |
218 | |
219 | /* Functions to manage the item lists */ | |
220 | ||
221 | void | |
222 | set_itemlist_dirty (it) | |
223 | ITEMLIST *it; | |
224 | { | |
225 | it->flags |= LIST_DIRTY; | |
226 | } | |
227 | ||
228 | void | |
229 | initialize_itemlist (itp) | |
230 | ITEMLIST *itp; | |
231 | { | |
232 | (*itp->list_getter) (itp); | |
233 | itp->flags |= LIST_INITIALIZED; | |
234 | itp->flags &= ~LIST_DIRTY; | |
235 | } | |
236 | ||
237 | void | |
238 | clean_itemlist (itp) | |
239 | ITEMLIST *itp; | |
240 | { | |
241 | STRINGLIST *sl; | |
242 | ||
243 | sl = itp->slist; | |
244 | if (sl) | |
245 | { | |
246 | if ((itp->flags & (LIST_DONTFREEMEMBERS|LIST_DONTFREE)) == 0) | |
7117c2d2 | 247 | strvec_flush (sl->list); |
bb70624e JA |
248 | if ((itp->flags & LIST_DONTFREE) == 0) |
249 | free (sl->list); | |
250 | free (sl); | |
251 | } | |
252 | itp->slist = (STRINGLIST *)NULL; | |
253 | itp->flags &= ~(LIST_DONTFREE|LIST_DONTFREEMEMBERS|LIST_INITIALIZED|LIST_DIRTY); | |
254 | } | |
255 | ||
bb70624e JA |
256 | |
257 | static int | |
258 | shouldexp_filterpat (s) | |
259 | char *s; | |
260 | { | |
261 | register char *p; | |
262 | ||
263 | for (p = s; p && *p; p++) | |
264 | { | |
265 | if (*p == '\\') | |
266 | p++; | |
267 | else if (*p == '&') | |
268 | return 1; | |
269 | } | |
270 | return 0; | |
271 | } | |
272 | ||
273 | /* Replace any instance of `&' in PAT with TEXT. Backslash may be used to | |
274 | quote a `&' and inhibit substitution. Returns a new string. This just | |
275 | calls stringlib.c:strcreplace(). */ | |
276 | static char * | |
277 | preproc_filterpat (pat, text) | |
278 | char *pat; | |
a0c0a00f | 279 | const char *text; |
bb70624e JA |
280 | { |
281 | char *ret; | |
282 | ||
283 | ret = strcreplace (pat, '&', text, 1); | |
284 | return ret; | |
285 | } | |
286 | ||
287 | /* Remove any match of FILTERPAT from SL. A `&' in FILTERPAT is replaced by | |
288 | TEXT. A leading `!' in FILTERPAT negates the pattern; in this case | |
289 | any member of SL->list that does *not* match will be removed. This returns | |
290 | a new STRINGLIST with the matching members of SL *copied*. Any | |
291 | non-matching members of SL->list are *freed*. */ | |
292 | STRINGLIST * | |
293 | filter_stringlist (sl, filterpat, text) | |
294 | STRINGLIST *sl; | |
a0c0a00f CR |
295 | char *filterpat; |
296 | const char *text; | |
bb70624e JA |
297 | { |
298 | int i, m, not; | |
299 | STRINGLIST *ret; | |
300 | char *npat, *t; | |
301 | ||
302 | if (sl == 0 || sl->list == 0 || sl->list_len == 0) | |
303 | return sl; | |
304 | ||
305 | npat = shouldexp_filterpat (filterpat) ? preproc_filterpat (filterpat, text) : filterpat; | |
306 | ||
a0c0a00f CR |
307 | #if defined (EXTENDED_GLOB) |
308 | not = (npat[0] == '!' && (extended_glob == 0 || npat[1] != '(')); /*)*/ | |
309 | #else | |
bb70624e | 310 | not = (npat[0] == '!'); |
a0c0a00f | 311 | #endif |
bb70624e JA |
312 | t = not ? npat + 1 : npat; |
313 | ||
7117c2d2 | 314 | ret = strlist_create (sl->list_size); |
bb70624e JA |
315 | for (i = 0; i < sl->list_len; i++) |
316 | { | |
a0c0a00f | 317 | m = strmatch (t, sl->list[i], FNMATCH_EXTFLAG | FNMATCH_IGNCASE); |
bb70624e JA |
318 | if ((not && m == FNM_NOMATCH) || (not == 0 && m != FNM_NOMATCH)) |
319 | free (sl->list[i]); | |
320 | else | |
321 | ret->list[ret->list_len++] = sl->list[i]; | |
322 | } | |
323 | ||
324 | ret->list[ret->list_len] = (char *)NULL; | |
325 | if (npat != filterpat) | |
326 | free (npat); | |
327 | ||
328 | return ret; | |
329 | } | |
330 | ||
28ef6c31 JA |
331 | /* Turn an array of strings returned by rl_completion_matches into a STRINGLIST. |
332 | This understands how rl_completion_matches sets matches[0] (the lcd of the | |
bb70624e JA |
333 | strings in the list, unless it's the only match). */ |
334 | STRINGLIST * | |
335 | completions_to_stringlist (matches) | |
336 | char **matches; | |
337 | { | |
338 | STRINGLIST *sl; | |
339 | int mlen, i, n; | |
340 | ||
7117c2d2 JA |
341 | mlen = (matches == 0) ? 0 : strvec_len (matches); |
342 | sl = strlist_create (mlen + 1); | |
bb70624e JA |
343 | |
344 | if (matches == 0 || matches[0] == 0) | |
345 | return sl; | |
346 | ||
347 | if (matches[1] == 0) | |
348 | { | |
349 | sl->list[0] = STRDUP (matches[0]); | |
350 | sl->list[sl->list_len = 1] = (char *)NULL; | |
351 | return sl; | |
352 | } | |
353 | ||
354 | for (i = 1, n = 0; i < mlen; i++, n++) | |
355 | sl->list[n] = STRDUP (matches[i]); | |
356 | sl->list_len = n; | |
357 | sl->list[n] = (char *)NULL; | |
358 | ||
359 | return sl; | |
360 | } | |
361 | ||
362 | /* Functions to manage the various ITEMLISTs that we populate internally. | |
363 | The caller is responsible for setting ITP->flags correctly. */ | |
364 | ||
365 | static int | |
366 | it_init_aliases (itp) | |
367 | ITEMLIST *itp; | |
368 | { | |
369 | #ifdef ALIAS | |
f73dda09 | 370 | alias_t **alias_list; |
bb70624e JA |
371 | register int i, n; |
372 | STRINGLIST *sl; | |
373 | ||
f73dda09 JA |
374 | alias_list = all_aliases (); |
375 | if (alias_list == 0) | |
bb70624e JA |
376 | { |
377 | itp->slist = (STRINGLIST *)NULL; | |
378 | return 0; | |
379 | } | |
f73dda09 | 380 | for (n = 0; alias_list[n]; n++) |
bb70624e | 381 | ; |
7117c2d2 | 382 | sl = strlist_create (n+1); |
bb70624e | 383 | for (i = 0; i < n; i++) |
f73dda09 | 384 | sl->list[i] = STRDUP (alias_list[i]->name); |
bb70624e JA |
385 | sl->list[n] = (char *)NULL; |
386 | sl->list_size = sl->list_len = n; | |
387 | itp->slist = sl; | |
388 | #else | |
389 | itp->slist = (STRINGLIST *)NULL; | |
390 | #endif | |
ac50fbac | 391 | free (alias_list); |
bb70624e JA |
392 | return 1; |
393 | } | |
394 | ||
395 | static void | |
396 | init_itemlist_from_varlist (itp, svfunc) | |
397 | ITEMLIST *itp; | |
398 | SVFUNC *svfunc; | |
399 | { | |
400 | SHELL_VAR **vlist; | |
401 | STRINGLIST *sl; | |
402 | register int i, n; | |
403 | ||
404 | vlist = (*svfunc) (); | |
b80f6443 JA |
405 | if (vlist == 0) |
406 | { | |
407 | itp->slist = (STRINGLIST *)NULL; | |
408 | return; | |
409 | } | |
bb70624e JA |
410 | for (n = 0; vlist[n]; n++) |
411 | ; | |
7117c2d2 | 412 | sl = strlist_create (n+1); |
bb70624e JA |
413 | for (i = 0; i < n; i++) |
414 | sl->list[i] = savestring (vlist[i]->name); | |
415 | sl->list[sl->list_len = n] = (char *)NULL; | |
416 | itp->slist = sl; | |
74091dd4 | 417 | free (vlist); |
bb70624e JA |
418 | } |
419 | ||
420 | static int | |
421 | it_init_arrayvars (itp) | |
422 | ITEMLIST *itp; | |
423 | { | |
424 | #if defined (ARRAY_VARS) | |
425 | init_itemlist_from_varlist (itp, all_array_variables); | |
426 | return 1; | |
427 | #else | |
428 | return 0; | |
429 | #endif | |
430 | } | |
431 | ||
432 | static int | |
433 | it_init_bindings (itp) | |
434 | ITEMLIST *itp; | |
435 | { | |
436 | char **blist; | |
437 | STRINGLIST *sl; | |
438 | ||
439 | /* rl_funmap_names allocates blist, but not its members */ | |
f73dda09 | 440 | blist = (char **)rl_funmap_names (); /* XXX fix const later */ |
7117c2d2 | 441 | sl = strlist_create (0); |
bb70624e JA |
442 | sl->list = blist; |
443 | sl->list_size = 0; | |
7117c2d2 | 444 | sl->list_len = strvec_len (sl->list); |
bb70624e JA |
445 | itp->flags |= LIST_DONTFREEMEMBERS; |
446 | itp->slist = sl; | |
447 | ||
448 | return 0; | |
449 | } | |
450 | ||
451 | static int | |
452 | it_init_builtins (itp) | |
453 | ITEMLIST *itp; | |
454 | { | |
455 | STRINGLIST *sl; | |
bb70624e JA |
456 | register int i, n; |
457 | ||
7117c2d2 | 458 | sl = strlist_create (num_shell_builtins); |
bb70624e JA |
459 | for (i = n = 0; i < num_shell_builtins; i++) |
460 | if (shell_builtins[i].function) | |
461 | sl->list[n++] = shell_builtins[i].name; | |
462 | sl->list[sl->list_len = n] = (char *)NULL; | |
463 | itp->flags |= LIST_DONTFREEMEMBERS; | |
464 | itp->slist = sl; | |
465 | return 0; | |
466 | } | |
467 | ||
468 | static int | |
469 | it_init_enabled (itp) | |
470 | ITEMLIST *itp; | |
471 | { | |
472 | STRINGLIST *sl; | |
bb70624e JA |
473 | register int i, n; |
474 | ||
7117c2d2 | 475 | sl = strlist_create (num_shell_builtins); |
bb70624e JA |
476 | for (i = n = 0; i < num_shell_builtins; i++) |
477 | { | |
478 | if (shell_builtins[i].function && (shell_builtins[i].flags & BUILTIN_ENABLED)) | |
479 | sl->list[n++] = shell_builtins[i].name; | |
480 | } | |
481 | sl->list[sl->list_len = n] = (char *)NULL; | |
482 | itp->flags |= LIST_DONTFREEMEMBERS; | |
483 | itp->slist = sl; | |
484 | return 0; | |
485 | } | |
486 | ||
487 | static int | |
488 | it_init_disabled (itp) | |
489 | ITEMLIST *itp; | |
490 | { | |
491 | STRINGLIST *sl; | |
bb70624e JA |
492 | register int i, n; |
493 | ||
7117c2d2 | 494 | sl = strlist_create (num_shell_builtins); |
bb70624e JA |
495 | for (i = n = 0; i < num_shell_builtins; i++) |
496 | { | |
497 | if (shell_builtins[i].function && ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0)) | |
498 | sl->list[n++] = shell_builtins[i].name; | |
499 | } | |
500 | sl->list[sl->list_len = n] = (char *)NULL; | |
501 | itp->flags |= LIST_DONTFREEMEMBERS; | |
502 | itp->slist = sl; | |
503 | return 0; | |
504 | } | |
505 | ||
506 | static int | |
507 | it_init_exported (itp) | |
508 | ITEMLIST *itp; | |
509 | { | |
510 | init_itemlist_from_varlist (itp, all_exported_variables); | |
511 | return 0; | |
512 | } | |
513 | ||
514 | static int | |
515 | it_init_functions (itp) | |
516 | ITEMLIST *itp; | |
517 | { | |
518 | init_itemlist_from_varlist (itp, all_visible_functions); | |
519 | return 0; | |
520 | } | |
521 | ||
ac50fbac CR |
522 | /* Like it_init_builtins, but includes everything the help builtin looks at, |
523 | not just builtins with an active implementing function. */ | |
524 | static int | |
525 | it_init_helptopics (itp) | |
526 | ITEMLIST *itp; | |
527 | { | |
528 | STRINGLIST *sl; | |
529 | register int i, n; | |
530 | ||
531 | sl = strlist_create (num_shell_builtins); | |
532 | for (i = n = 0; i < num_shell_builtins; i++) | |
533 | sl->list[n++] = shell_builtins[i].name; | |
534 | sl->list[sl->list_len = n] = (char *)NULL; | |
535 | itp->flags |= LIST_DONTFREEMEMBERS; | |
536 | itp->slist = sl; | |
537 | return 0; | |
538 | } | |
539 | ||
bb70624e JA |
540 | static int |
541 | it_init_hostnames (itp) | |
542 | ITEMLIST *itp; | |
543 | { | |
544 | STRINGLIST *sl; | |
545 | ||
7117c2d2 | 546 | sl = strlist_create (0); |
bb70624e | 547 | sl->list = get_hostname_list (); |
7117c2d2 | 548 | sl->list_len = sl->list ? strvec_len (sl->list) : 0; |
bb70624e JA |
549 | sl->list_size = sl->list_len; |
550 | itp->slist = sl; | |
551 | itp->flags |= LIST_DONTFREEMEMBERS|LIST_DONTFREE; | |
552 | return 0; | |
553 | } | |
554 | ||
555 | static int | |
556 | it_init_joblist (itp, jstate) | |
557 | ITEMLIST *itp; | |
558 | int jstate; | |
559 | { | |
560 | #if defined (JOB_CONTROL) | |
561 | STRINGLIST *sl; | |
f73dda09 | 562 | register int i; |
bb70624e JA |
563 | register PROCESS *p; |
564 | char *s, *t; | |
95732b49 JA |
565 | JOB *j; |
566 | JOB_STATE ws; /* wanted state */ | |
bb70624e | 567 | |
3185942a | 568 | ws = JNONE; |
bb70624e | 569 | if (jstate == 0) |
95732b49 | 570 | ws = JRUNNING; |
bb70624e | 571 | else if (jstate == 1) |
95732b49 | 572 | ws = JSTOPPED; |
bb70624e | 573 | |
95732b49 JA |
574 | sl = strlist_create (js.j_jobslots); |
575 | for (i = js.j_jobslots - 1; i >= 0; i--) | |
bb70624e | 576 | { |
95732b49 JA |
577 | j = get_job_by_jid (i); |
578 | if (j == 0) | |
bb70624e | 579 | continue; |
95732b49 JA |
580 | p = j->pipe; |
581 | if (jstate == -1 || JOBSTATE(i) == ws) | |
bb70624e JA |
582 | { |
583 | s = savestring (p->command); | |
584 | t = strpbrk (s, " \t\n"); | |
585 | if (t) | |
586 | *t = '\0'; | |
28ef6c31 | 587 | sl->list[sl->list_len++] = s; |
bb70624e JA |
588 | } |
589 | } | |
590 | itp->slist = sl; | |
591 | #else | |
592 | itp->slist = (STRINGLIST *)NULL; | |
593 | #endif | |
594 | return 0; | |
595 | } | |
596 | ||
597 | static int | |
598 | it_init_jobs (itp) | |
599 | ITEMLIST *itp; | |
600 | { | |
601 | return (it_init_joblist (itp, -1)); | |
602 | } | |
603 | ||
604 | static int | |
605 | it_init_running (itp) | |
606 | ITEMLIST *itp; | |
607 | { | |
608 | return (it_init_joblist (itp, 0)); | |
609 | } | |
610 | ||
611 | static int | |
612 | it_init_stopped (itp) | |
613 | ITEMLIST *itp; | |
614 | { | |
615 | return (it_init_joblist (itp, 1)); | |
616 | } | |
617 | ||
618 | static int | |
619 | it_init_keywords (itp) | |
620 | ITEMLIST *itp; | |
621 | { | |
622 | STRINGLIST *sl; | |
623 | register int i, n; | |
624 | ||
625 | for (n = 0; word_token_alist[n].word; n++) | |
626 | ; | |
7117c2d2 | 627 | sl = strlist_create (n); |
bb70624e JA |
628 | for (i = 0; i < n; i++) |
629 | sl->list[i] = word_token_alist[i].word; | |
630 | sl->list[sl->list_len = i] = (char *)NULL; | |
631 | itp->flags |= LIST_DONTFREEMEMBERS; | |
632 | itp->slist = sl; | |
633 | return 0; | |
634 | } | |
635 | ||
636 | static int | |
637 | it_init_signals (itp) | |
638 | ITEMLIST *itp; | |
639 | { | |
640 | STRINGLIST *sl; | |
641 | ||
7117c2d2 | 642 | sl = strlist_create (0); |
bb70624e | 643 | sl->list = signal_names; |
7117c2d2 | 644 | sl->list_len = strvec_len (sl->list); |
bb70624e JA |
645 | itp->flags |= LIST_DONTFREE; |
646 | itp->slist = sl; | |
647 | return 0; | |
648 | } | |
649 | ||
650 | static int | |
651 | it_init_variables (itp) | |
652 | ITEMLIST *itp; | |
653 | { | |
654 | init_itemlist_from_varlist (itp, all_visible_variables); | |
655 | return 0; | |
656 | } | |
657 | ||
658 | static int | |
659 | it_init_setopts (itp) | |
660 | ITEMLIST *itp; | |
661 | { | |
662 | STRINGLIST *sl; | |
663 | ||
7117c2d2 | 664 | sl = strlist_create (0); |
bb70624e | 665 | sl->list = get_minus_o_opts (); |
7117c2d2 | 666 | sl->list_len = strvec_len (sl->list); |
bb70624e JA |
667 | itp->slist = sl; |
668 | itp->flags |= LIST_DONTFREEMEMBERS; | |
669 | return 0; | |
670 | } | |
671 | ||
672 | static int | |
673 | it_init_shopts (itp) | |
674 | ITEMLIST *itp; | |
675 | { | |
676 | STRINGLIST *sl; | |
677 | ||
7117c2d2 | 678 | sl = strlist_create (0); |
bb70624e | 679 | sl->list = get_shopt_options (); |
7117c2d2 | 680 | sl->list_len = strvec_len (sl->list); |
bb70624e JA |
681 | itp->slist = sl; |
682 | itp->flags |= LIST_DONTFREEMEMBERS; | |
683 | return 0; | |
684 | } | |
685 | ||
686 | /* Generate a list of all matches for TEXT using the STRINGLIST in itp->slist | |
687 | as the list of possibilities. If the itemlist has been marked dirty or | |
688 | it should be regenerated every time, destroy the old STRINGLIST and make a | |
b80f6443 JA |
689 | new one before trying the match. TEXT is dequoted before attempting a |
690 | match. */ | |
bb70624e JA |
691 | static STRINGLIST * |
692 | gen_matches_from_itemlist (itp, text) | |
693 | ITEMLIST *itp; | |
28ef6c31 | 694 | const char *text; |
bb70624e JA |
695 | { |
696 | STRINGLIST *ret, *sl; | |
697 | int tlen, i, n; | |
b80f6443 | 698 | char *ntxt; |
bb70624e JA |
699 | |
700 | if ((itp->flags & (LIST_DIRTY|LIST_DYNAMIC)) || | |
701 | (itp->flags & LIST_INITIALIZED) == 0) | |
702 | { | |
0001803f | 703 | if (itp->flags & (LIST_DIRTY|LIST_DYNAMIC)) |
bb70624e JA |
704 | clean_itemlist (itp); |
705 | if ((itp->flags & LIST_INITIALIZED) == 0) | |
706 | initialize_itemlist (itp); | |
707 | } | |
f73dda09 JA |
708 | if (itp->slist == 0) |
709 | return ((STRINGLIST *)NULL); | |
7117c2d2 | 710 | ret = strlist_create (itp->slist->list_len+1); |
bb70624e | 711 | sl = itp->slist; |
b80f6443 JA |
712 | |
713 | ntxt = bash_dequote_text (text); | |
714 | tlen = STRLEN (ntxt); | |
715 | ||
bb70624e JA |
716 | for (i = n = 0; i < sl->list_len; i++) |
717 | { | |
b80f6443 | 718 | if (tlen == 0 || STREQN (sl->list[i], ntxt, tlen)) |
bb70624e JA |
719 | ret->list[n++] = STRDUP (sl->list[i]); |
720 | } | |
721 | ret->list[ret->list_len = n] = (char *)NULL; | |
b80f6443 JA |
722 | |
723 | FREE (ntxt); | |
bb70624e JA |
724 | return ret; |
725 | } | |
726 | ||
28ef6c31 | 727 | /* A wrapper for rl_filename_completion_function that dequotes the filename |
bb70624e JA |
728 | before attempting completions. */ |
729 | static char * | |
730 | pcomp_filename_completion_function (text, state) | |
28ef6c31 | 731 | const char *text; |
bb70624e JA |
732 | int state; |
733 | { | |
734 | static char *dfn; /* dequoted filename */ | |
ac50fbac | 735 | int iscompgen, iscompleting; |
bb70624e JA |
736 | |
737 | if (state == 0) | |
738 | { | |
739 | FREE (dfn); | |
740 | /* remove backslashes quoting special characters in filenames. */ | |
ac50fbac CR |
741 | /* There are roughly three paths we can follow to get here: |
742 | 1. complete -f | |
743 | 2. compgen -f "$word" from a completion function | |
744 | 3. compgen -f "$word" from the command line | |
745 | They all need to be handled. | |
746 | ||
747 | In the first two cases, readline will run the filename dequoting | |
748 | function in rl_filename_completion_function if it found a filename | |
749 | quoting character in the word to be completed | |
750 | (rl_completion_found_quote). We run the dequoting function here | |
751 | if we're running compgen, we're not completing, and the | |
752 | rl_filename_completion_function won't dequote the filename | |
753 | (rl_completion_found_quote == 0). */ | |
754 | iscompgen = this_shell_builtin == compgen_builtin; | |
755 | iscompleting = RL_ISSTATE (RL_STATE_COMPLETING); | |
756 | if (iscompgen && iscompleting == 0 && rl_completion_found_quote == 0 | |
757 | && rl_filename_dequoting_function) | |
bb70624e | 758 | { |
b80f6443 JA |
759 | /* Use rl_completion_quote_character because any single or |
760 | double quotes have been removed by the time TEXT makes it | |
761 | here, and we don't want to remove backslashes inside | |
762 | quoted strings. */ | |
3185942a | 763 | dfn = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); |
bb70624e | 764 | } |
2b76266c CR |
765 | /* Intended to solve a mismatched assumption by bash-completion. If |
766 | the text to be completed is empty, but bash-completion turns it into | |
767 | a quoted string ('') assuming that this code will dequote it before | |
768 | calling readline, do the dequoting. */ | |
769 | else if (iscompgen && iscompleting && | |
770 | pcomp_curtxt && *pcomp_curtxt == 0 && | |
771 | text && (*text == '\'' || *text == '"') && text[1] == text[0] && text[2] == 0 && | |
772 | rl_filename_dequoting_function) | |
773 | dfn = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); | |
774 | /* Another mismatched assumption by bash-completion. If compgen is being | |
775 | run as part of bash-completion, and the argument to compgen is not | |
776 | the same as the word originally passed to the programmable completion | |
777 | code, dequote the argument if it has quote characters. It's an | |
778 | attempt to detect when bash-completion is quoting its filename | |
779 | argument before calling compgen. */ | |
780 | /* We could check whether gen_shell_function_matches is in the call | |
781 | stack by checking whether the gen-shell-function-matches tag is in | |
782 | the unwind-protect stack, but there's no function to do that yet. | |
783 | We could simply check whether we're executing in a function by | |
784 | checking variable_context, and may end up doing that. */ | |
785 | else if (iscompgen && iscompleting && rl_filename_dequoting_function && | |
786 | pcomp_curtxt && text && | |
787 | STREQ (pcomp_curtxt, text) == 0 && | |
788 | variable_context && | |
789 | sh_contains_quotes (text)) /* guess */ | |
790 | dfn = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); | |
bb70624e JA |
791 | else |
792 | dfn = savestring (text); | |
793 | } | |
794 | ||
28ef6c31 | 795 | return (rl_filename_completion_function (dfn, state)); |
bb70624e JA |
796 | } |
797 | ||
798 | #define GEN_COMPS(bmap, flag, it, text, glist, tlist) \ | |
799 | do { \ | |
800 | if (bmap & flag) \ | |
801 | { \ | |
802 | tlist = gen_matches_from_itemlist (it, text); \ | |
f73dda09 JA |
803 | if (tlist) \ |
804 | { \ | |
7117c2d2 JA |
805 | glist = strlist_append (glist, tlist); \ |
806 | strlist_dispose (tlist); \ | |
f73dda09 | 807 | } \ |
bb70624e JA |
808 | } \ |
809 | } while (0) | |
810 | ||
811 | #define GEN_XCOMPS(bmap, flag, text, func, cmatches, glist, tlist) \ | |
812 | do { \ | |
813 | if (bmap & flag) \ | |
814 | { \ | |
28ef6c31 | 815 | cmatches = rl_completion_matches (text, func); \ |
bb70624e | 816 | tlist = completions_to_stringlist (cmatches); \ |
7117c2d2 JA |
817 | glist = strlist_append (glist, tlist); \ |
818 | strvec_dispose (cmatches); \ | |
819 | strlist_dispose (tlist); \ | |
bb70624e JA |
820 | } \ |
821 | } while (0) | |
822 | ||
823 | /* Functions to generate lists of matches from the actions member of CS. */ | |
824 | ||
825 | static STRINGLIST * | |
826 | gen_action_completions (cs, text) | |
827 | COMPSPEC *cs; | |
28ef6c31 | 828 | const char *text; |
bb70624e JA |
829 | { |
830 | STRINGLIST *ret, *tmatches; | |
28ef6c31 | 831 | char **cmatches; /* from rl_completion_matches ... */ |
bb70624e | 832 | unsigned long flags; |
ac50fbac | 833 | int t; |
bb70624e JA |
834 | |
835 | ret = tmatches = (STRINGLIST *)NULL; | |
836 | flags = cs->actions; | |
837 | ||
838 | GEN_COMPS (flags, CA_ALIAS, &it_aliases, text, ret, tmatches); | |
839 | GEN_COMPS (flags, CA_ARRAYVAR, &it_arrayvars, text, ret, tmatches); | |
840 | GEN_COMPS (flags, CA_BINDING, &it_bindings, text, ret, tmatches); | |
841 | GEN_COMPS (flags, CA_BUILTIN, &it_builtins, text, ret, tmatches); | |
842 | GEN_COMPS (flags, CA_DISABLED, &it_disabled, text, ret, tmatches); | |
843 | GEN_COMPS (flags, CA_ENABLED, &it_enabled, text, ret, tmatches); | |
844 | GEN_COMPS (flags, CA_EXPORT, &it_exports, text, ret, tmatches); | |
845 | GEN_COMPS (flags, CA_FUNCTION, &it_functions, text, ret, tmatches); | |
ac50fbac | 846 | GEN_COMPS (flags, CA_HELPTOPIC, &it_helptopics, text, ret, tmatches); |
bb70624e JA |
847 | GEN_COMPS (flags, CA_HOSTNAME, &it_hostnames, text, ret, tmatches); |
848 | GEN_COMPS (flags, CA_JOB, &it_jobs, text, ret, tmatches); | |
849 | GEN_COMPS (flags, CA_KEYWORD, &it_keywords, text, ret, tmatches); | |
850 | GEN_COMPS (flags, CA_RUNNING, &it_running, text, ret, tmatches); | |
851 | GEN_COMPS (flags, CA_SETOPT, &it_setopts, text, ret, tmatches); | |
852 | GEN_COMPS (flags, CA_SHOPT, &it_shopts, text, ret, tmatches); | |
853 | GEN_COMPS (flags, CA_SIGNAL, &it_signals, text, ret, tmatches); | |
854 | GEN_COMPS (flags, CA_STOPPED, &it_stopped, text, ret, tmatches); | |
855 | GEN_COMPS (flags, CA_VARIABLE, &it_variables, text, ret, tmatches); | |
856 | ||
857 | GEN_XCOMPS(flags, CA_COMMAND, text, command_word_completion_function, cmatches, ret, tmatches); | |
858 | GEN_XCOMPS(flags, CA_FILE, text, pcomp_filename_completion_function, cmatches, ret, tmatches); | |
28ef6c31 | 859 | GEN_XCOMPS(flags, CA_USER, text, rl_username_completion_function, cmatches, ret, tmatches); |
f73dda09 | 860 | GEN_XCOMPS(flags, CA_GROUP, text, bash_groupname_completion_function, cmatches, ret, tmatches); |
7117c2d2 | 861 | GEN_XCOMPS(flags, CA_SERVICE, text, bash_servicename_completion_function, cmatches, ret, tmatches); |
bb70624e JA |
862 | |
863 | /* And lastly, the special case for directories */ | |
864 | if (flags & CA_DIRECTORY) | |
865 | { | |
ac50fbac | 866 | t = rl_filename_completion_desired; |
7117c2d2 | 867 | rl_completion_mark_symlink_dirs = 1; /* override user preference */ |
bb70624e | 868 | cmatches = bash_directory_completion_matches (text); |
ac50fbac CR |
869 | /* If we did not want filename completion before this, and there are |
870 | no matches, turn off rl_filename_completion_desired so whatever | |
871 | matches we get are not treated as filenames (it gets turned on by | |
872 | rl_filename_completion_function unconditionally). */ | |
873 | if (t == 0 && cmatches == 0 && rl_filename_completion_desired == 1) | |
874 | rl_filename_completion_desired = 0; | |
bb70624e | 875 | tmatches = completions_to_stringlist (cmatches); |
7117c2d2 JA |
876 | ret = strlist_append (ret, tmatches); |
877 | strvec_dispose (cmatches); | |
878 | strlist_dispose (tmatches); | |
bb70624e JA |
879 | } |
880 | ||
881 | return ret; | |
882 | } | |
883 | ||
884 | /* Generate a list of matches for CS->globpat. Unresolved: should this use | |
885 | TEXT as a match prefix, or just go without? Currently, the code does not | |
886 | use TEXT, just globs CS->globpat and returns the results. If we do decide | |
887 | to use TEXT, we should call quote_string_for_globbing before the call to | |
74091dd4 | 888 | glob_filename (in which case we could use shell_glob_filename). */ |
bb70624e JA |
889 | static STRINGLIST * |
890 | gen_globpat_matches (cs, text) | |
891 | COMPSPEC *cs; | |
28ef6c31 | 892 | const char *text; |
bb70624e JA |
893 | { |
894 | STRINGLIST *sl; | |
74091dd4 | 895 | int gflags; |
bb70624e | 896 | |
7117c2d2 | 897 | sl = strlist_create (0); |
74091dd4 CR |
898 | gflags = glob_star ? GX_GLOBSTAR : 0; |
899 | sl->list = glob_filename (cs->globpat, gflags); | |
bb70624e JA |
900 | if (GLOB_FAILED (sl->list)) |
901 | sl->list = (char **)NULL; | |
902 | if (sl->list) | |
7117c2d2 | 903 | sl->list_len = sl->list_size = strvec_len (sl->list); |
bb70624e JA |
904 | return sl; |
905 | } | |
906 | ||
907 | /* Perform the shell word expansions on CS->words and return the results. | |
908 | Again, this ignores TEXT. */ | |
909 | static STRINGLIST * | |
910 | gen_wordlist_matches (cs, text) | |
911 | COMPSPEC *cs; | |
28ef6c31 | 912 | const char *text; |
bb70624e JA |
913 | { |
914 | WORD_LIST *l, *l2; | |
915 | STRINGLIST *sl; | |
0628567a | 916 | int nw, tlen; |
b80f6443 | 917 | char *ntxt; /* dequoted TEXT to use in comparisons */ |
bb70624e JA |
918 | |
919 | if (cs->words == 0 || cs->words[0] == '\0') | |
920 | return ((STRINGLIST *)NULL); | |
921 | ||
922 | /* This used to be a simple expand_string(cs->words, 0), but that won't | |
923 | do -- there's no way to split a simple list into individual words | |
924 | that way, since the shell semantics say that word splitting is done | |
ac50fbac CR |
925 | only on the results of expansion. split_at_delims also handles embedded |
926 | quoted strings and preserves the quotes for the expand_words_shellexp | |
927 | function call that follows. */ | |
928 | /* XXX - this is where this function spends most of its time */ | |
0001803f | 929 | l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, 0, (int *)NULL, (int *)NULL); |
bb70624e JA |
930 | if (l == 0) |
931 | return ((STRINGLIST *)NULL); | |
932 | /* This will jump back to the top level if the expansion fails... */ | |
933 | l2 = expand_words_shellexp (l); | |
934 | dispose_words (l); | |
935 | ||
936 | nw = list_length (l2); | |
7117c2d2 | 937 | sl = strlist_create (nw + 1); |
b80f6443 JA |
938 | |
939 | ntxt = bash_dequote_text (text); | |
940 | tlen = STRLEN (ntxt); | |
bb70624e JA |
941 | |
942 | for (nw = 0, l = l2; l; l = l->next) | |
943 | { | |
b80f6443 | 944 | if (tlen == 0 || STREQN (l->word->word, ntxt, tlen)) |
28ef6c31 | 945 | sl->list[nw++] = STRDUP (l->word->word); |
bb70624e JA |
946 | } |
947 | sl->list[sl->list_len = nw] = (char *)NULL; | |
948 | ||
95732b49 | 949 | dispose_words (l2); |
b80f6443 | 950 | FREE (ntxt); |
bb70624e JA |
951 | return sl; |
952 | } | |
953 | ||
954 | #ifdef ARRAY_VARS | |
955 | ||
956 | static SHELL_VAR * | |
957 | bind_comp_words (lwords) | |
958 | WORD_LIST *lwords; | |
959 | { | |
960 | SHELL_VAR *v; | |
961 | ||
d233b485 | 962 | v = find_variable_noref ("COMP_WORDS"); |
bb70624e JA |
963 | if (v == 0) |
964 | v = make_new_array_variable ("COMP_WORDS"); | |
d233b485 CR |
965 | if (nameref_p (v)) |
966 | VUNSETATTR (v, att_nameref); | |
967 | #if 0 | |
bb70624e JA |
968 | if (readonly_p (v)) |
969 | VUNSETATTR (v, att_readonly); | |
d233b485 | 970 | #endif |
bb70624e JA |
971 | if (array_p (v) == 0) |
972 | v = convert_var_to_array (v); | |
95732b49 | 973 | v = assign_array_var_from_word_list (v, lwords, 0); |
eb873671 JA |
974 | |
975 | VUNSETATTR (v, att_invisible); | |
bb70624e JA |
976 | return v; |
977 | } | |
978 | #endif /* ARRAY_VARS */ | |
979 | ||
980 | static void | |
981 | bind_compfunc_variables (line, ind, lwords, cw, exported) | |
982 | char *line; | |
983 | int ind; | |
984 | WORD_LIST *lwords; | |
985 | int cw, exported; | |
986 | { | |
f73dda09 | 987 | char ibuf[INT_STRLEN_BOUND(int) + 1]; |
bb70624e JA |
988 | char *value; |
989 | SHELL_VAR *v; | |
ac50fbac CR |
990 | size_t llen; |
991 | int c; | |
bb70624e JA |
992 | |
993 | /* Set the variables that the function expects while it executes. Maybe | |
994 | these should be in the function environment (temporary_env). */ | |
95732b49 | 995 | v = bind_variable ("COMP_LINE", line, 0); |
bb70624e JA |
996 | if (v && exported) |
997 | VSETATTR(v, att_exported); | |
998 | ||
ac50fbac CR |
999 | /* Post bash-4.2: COMP_POINT is characters instead of bytes. */ |
1000 | c = line[ind]; | |
1001 | line[ind] = '\0'; | |
1002 | llen = MB_STRLEN (line); | |
1003 | line[ind] = c; | |
1004 | value = inttostr (llen, ibuf, sizeof(ibuf)); | |
d233b485 | 1005 | v = bind_int_variable ("COMP_POINT", value, 0); |
bb70624e JA |
1006 | if (v && exported) |
1007 | VSETATTR(v, att_exported); | |
1008 | ||
3185942a | 1009 | value = inttostr (rl_completion_type, ibuf, sizeof (ibuf)); |
d233b485 | 1010 | v = bind_int_variable ("COMP_TYPE", value, 0); |
3185942a JA |
1011 | if (v && exported) |
1012 | VSETATTR(v, att_exported); | |
1013 | ||
1014 | value = inttostr (rl_completion_invoking_key, ibuf, sizeof (ibuf)); | |
d233b485 | 1015 | v = bind_int_variable ("COMP_KEY", value, 0); |
3185942a JA |
1016 | if (v && exported) |
1017 | VSETATTR(v, att_exported); | |
1018 | ||
bb70624e JA |
1019 | /* Since array variables can't be exported, we don't bother making the |
1020 | array of words. */ | |
1021 | if (exported == 0) | |
1022 | { | |
1023 | #ifdef ARRAY_VARS | |
1024 | v = bind_comp_words (lwords); | |
f73dda09 | 1025 | value = inttostr (cw, ibuf, sizeof(ibuf)); |
d233b485 | 1026 | bind_int_variable ("COMP_CWORD", value, 0); |
bb70624e JA |
1027 | #endif |
1028 | } | |
1029 | else | |
1030 | array_needs_making = 1; | |
1031 | } | |
1032 | ||
1033 | static void | |
1034 | unbind_compfunc_variables (exported) | |
1035 | int exported; | |
1036 | { | |
a0c0a00f CR |
1037 | unbind_variable_noref ("COMP_LINE"); |
1038 | unbind_variable_noref ("COMP_POINT"); | |
1039 | unbind_variable_noref ("COMP_TYPE"); | |
1040 | unbind_variable_noref ("COMP_KEY"); | |
bb70624e | 1041 | #ifdef ARRAY_VARS |
a0c0a00f CR |
1042 | unbind_variable_noref ("COMP_WORDS"); |
1043 | unbind_variable_noref ("COMP_CWORD"); | |
bb70624e JA |
1044 | #endif |
1045 | if (exported) | |
1046 | array_needs_making = 1; | |
1047 | } | |
1048 | ||
1049 | /* Build the list of words to pass to a function or external command | |
1050 | as arguments. When the function or command is invoked, | |
1051 | ||
1052 | $0 == function or command being invoked | |
1053 | $1 == command name | |
17345e5a JA |
1054 | $2 == word to be completed (possibly null) |
1055 | $3 == previous word | |
bb70624e JA |
1056 | |
1057 | Functions can access all of the words in the current command line | |
17345e5a | 1058 | with the COMP_WORDS array. External commands cannot; they have to |
ac50fbac | 1059 | make do with the COMP_LINE and COMP_POINT variables. */ |
bb70624e JA |
1060 | |
1061 | static WORD_LIST * | |
ac50fbac | 1062 | build_arg_list (cmd, cname, text, lwords, ind) |
bb70624e | 1063 | char *cmd; |
ac50fbac | 1064 | const char *cname; |
f73dda09 | 1065 | const char *text; |
bb70624e JA |
1066 | WORD_LIST *lwords; |
1067 | int ind; | |
1068 | { | |
1069 | WORD_LIST *ret, *cl, *l; | |
1070 | WORD_DESC *w; | |
1071 | int i; | |
1072 | ||
1073 | ret = (WORD_LIST *)NULL; | |
1074 | w = make_word (cmd); | |
ac50fbac | 1075 | ret = make_word_list (w, (WORD_LIST *)NULL); /* $0 */ |
bb70624e | 1076 | |
ac50fbac | 1077 | w = make_word (cname); /* $1 */ |
bb70624e JA |
1078 | cl = ret->next = make_word_list (w, (WORD_LIST *)NULL); |
1079 | ||
1080 | w = make_word (text); | |
ac50fbac | 1081 | cl->next = make_word_list (w, (WORD_LIST *)NULL); /* $2 */ |
bb70624e JA |
1082 | cl = cl->next; |
1083 | ||
1084 | /* Search lwords for current word */ | |
1085 | for (l = lwords, i = 1; l && i < ind-1; l = l->next, i++) | |
1086 | ; | |
1087 | w = (l && l->word) ? copy_word (l->word) : make_word (""); | |
1088 | cl->next = make_word_list (w, (WORD_LIST *)NULL); | |
1089 | ||
1090 | return ret; | |
1091 | } | |
1092 | ||
1093 | /* Build a command string with | |
1094 | $0 == cs->funcname (function to execute for completion list) | |
1095 | $1 == command name (command being completed) | |
1096 | $2 = word to be completed (possibly null) | |
1097 | $3 = previous word | |
1098 | and run in the current shell. The function should put its completion | |
1099 | list into the array variable COMPREPLY. We build a STRINGLIST | |
1100 | from the results and return it. | |
1101 | ||
1102 | Since the shell function should return its list of matches in an array | |
1103 | variable, this does nothing if arrays are not compiled into the shell. */ | |
1104 | ||
1105 | static STRINGLIST * | |
ac50fbac | 1106 | gen_shell_function_matches (cs, cmd, text, line, ind, lwords, nw, cw, foundp) |
bb70624e | 1107 | COMPSPEC *cs; |
ac50fbac | 1108 | const char *cmd; |
f73dda09 JA |
1109 | const char *text; |
1110 | char *line; | |
bb70624e JA |
1111 | int ind; |
1112 | WORD_LIST *lwords; | |
1113 | int nw, cw; | |
0001803f | 1114 | int *foundp; |
bb70624e JA |
1115 | { |
1116 | char *funcname; | |
1117 | STRINGLIST *sl; | |
1118 | SHELL_VAR *f, *v; | |
1119 | WORD_LIST *cmdlist; | |
0001803f | 1120 | int fval, found; |
95732b49 | 1121 | sh_parser_state_t ps; |
17345e5a | 1122 | sh_parser_state_t * restrict pps; |
bb70624e JA |
1123 | #if defined (ARRAY_VARS) |
1124 | ARRAY *a; | |
1125 | #endif | |
1126 | ||
0001803f CR |
1127 | found = 0; |
1128 | if (foundp) | |
1129 | *foundp = found; | |
1130 | ||
bb70624e JA |
1131 | funcname = cs->funcname; |
1132 | f = find_function (funcname); | |
1133 | if (f == 0) | |
1134 | { | |
b80f6443 | 1135 | internal_error (_("completion: function `%s' not found"), funcname); |
28ef6c31 | 1136 | rl_ding (); |
bb70624e JA |
1137 | rl_on_new_line (); |
1138 | return ((STRINGLIST *)NULL); | |
1139 | } | |
1140 | ||
1141 | #if !defined (ARRAY_VARS) | |
1142 | return ((STRINGLIST *)NULL); | |
1143 | #else | |
1144 | ||
1145 | /* We pass cw - 1 because command_line_to_word_list returns indices that are | |
1146 | 1-based, while bash arrays are 0-based. */ | |
1147 | bind_compfunc_variables (line, ind, lwords, cw - 1, 0); | |
1148 | ||
ac50fbac | 1149 | cmdlist = build_arg_list (funcname, cmd, text, lwords, cw); |
95732b49 | 1150 | |
17345e5a | 1151 | pps = &ps; |
89a92869 | 1152 | save_parser_state (pps); |
17345e5a JA |
1153 | begin_unwind_frame ("gen-shell-function-matches"); |
1154 | add_unwind_protect (restore_parser_state, (char *)pps); | |
1155 | add_unwind_protect (dispose_words, (char *)cmdlist); | |
1156 | add_unwind_protect (unbind_compfunc_variables, (char *)0); | |
1157 | ||
bb70624e | 1158 | fval = execute_shell_function (f, cmdlist); |
17345e5a JA |
1159 | |
1160 | discard_unwind_frame ("gen-shell-function-matches"); | |
1161 | restore_parser_state (pps); | |
bb70624e | 1162 | |
0001803f CR |
1163 | found = fval != EX_NOTFOUND; |
1164 | if (fval == EX_RETRYFAIL) | |
1165 | found |= PCOMP_RETRYFAIL; | |
1166 | if (foundp) | |
1167 | *foundp = found; | |
1168 | ||
bb70624e JA |
1169 | /* Now clean up and destroy everything. */ |
1170 | dispose_words (cmdlist); | |
1171 | unbind_compfunc_variables (0); | |
1172 | ||
1173 | /* The list of completions is returned in the array variable COMPREPLY. */ | |
1174 | v = find_variable ("COMPREPLY"); | |
1175 | if (v == 0) | |
1176 | return ((STRINGLIST *)NULL); | |
d233b485 | 1177 | if (array_p (v) == 0 && assoc_p (v) == 0) |
bb70624e JA |
1178 | v = convert_var_to_array (v); |
1179 | ||
eb873671 JA |
1180 | VUNSETATTR (v, att_invisible); |
1181 | ||
bb70624e | 1182 | a = array_cell (v); |
d233b485 | 1183 | if (found == 0 || (found & PCOMP_RETRYFAIL) || a == 0 || array_p (v) == 0 || array_empty (a)) |
bb70624e JA |
1184 | sl = (STRINGLIST *)NULL; |
1185 | else | |
1186 | { | |
1187 | /* XXX - should we filter the list of completions so only those matching | |
1188 | TEXT are returned? Right now, we do not. */ | |
7117c2d2 | 1189 | sl = strlist_create (0); |
8868edaf | 1190 | sl->list = array_to_argv (a, 0); |
bb70624e JA |
1191 | sl->list_len = sl->list_size = array_num_elements (a); |
1192 | } | |
1193 | ||
1194 | /* XXX - should we unbind COMPREPLY here? */ | |
a0c0a00f | 1195 | unbind_variable_noref ("COMPREPLY"); |
bb70624e JA |
1196 | |
1197 | return (sl); | |
1198 | #endif | |
1199 | } | |
1200 | ||
1201 | /* Build a command string with | |
1202 | $0 == cs->command (command to execute for completion list) | |
1203 | $1 == command name (command being completed) | |
a0c0a00f CR |
1204 | $2 == word to be completed (possibly null) |
1205 | $3 == previous word | |
1206 | and run it with command substitution. Parse the results, one word | |
bb70624e JA |
1207 | per line, with backslashes allowed to escape newlines. Build a |
1208 | STRINGLIST from the results and return it. */ | |
1209 | ||
1210 | static STRINGLIST * | |
ac50fbac | 1211 | gen_command_matches (cs, cmd, text, line, ind, lwords, nw, cw) |
bb70624e | 1212 | COMPSPEC *cs; |
ac50fbac | 1213 | const char *cmd; |
f73dda09 JA |
1214 | const char *text; |
1215 | char *line; | |
bb70624e JA |
1216 | int ind; |
1217 | WORD_LIST *lwords; | |
1218 | int nw, cw; | |
1219 | { | |
1220 | char *csbuf, *cscmd, *t; | |
1221 | int cmdlen, cmdsize, n, ws, we; | |
1222 | WORD_LIST *cmdlist, *cl; | |
3185942a | 1223 | WORD_DESC *tw; |
bb70624e JA |
1224 | STRINGLIST *sl; |
1225 | ||
1226 | bind_compfunc_variables (line, ind, lwords, cw, 1); | |
ac50fbac | 1227 | cmdlist = build_arg_list (cs->command, cmd, text, lwords, cw); |
bb70624e JA |
1228 | |
1229 | /* Estimate the size needed for the buffer. */ | |
1230 | n = strlen (cs->command); | |
1231 | cmdsize = n + 1; | |
1232 | for (cl = cmdlist->next; cl; cl = cl->next) | |
1233 | cmdsize += STRLEN (cl->word->word) + 3; | |
1234 | cmdsize += 2; | |
1235 | ||
1236 | /* allocate the string for the command and fill it in. */ | |
f73dda09 | 1237 | cscmd = (char *)xmalloc (cmdsize + 1); |
bb70624e JA |
1238 | |
1239 | strcpy (cscmd, cs->command); /* $0 */ | |
1240 | cmdlen = n; | |
1241 | cscmd[cmdlen++] = ' '; | |
1242 | for (cl = cmdlist->next; cl; cl = cl->next) /* $1, $2, $3, ... */ | |
1243 | { | |
28ef6c31 | 1244 | t = sh_single_quote (cl->word->word ? cl->word->word : ""); |
bb70624e JA |
1245 | n = strlen (t); |
1246 | RESIZE_MALLOCED_BUFFER (cscmd, cmdlen, n + 2, cmdsize, 64); | |
1247 | strcpy (cscmd + cmdlen, t); | |
1248 | cmdlen += n; | |
1249 | if (cl->next) | |
1250 | cscmd[cmdlen++] = ' '; | |
1251 | free (t); | |
1252 | } | |
1253 | cscmd[cmdlen] = '\0'; | |
1254 | ||
d233b485 | 1255 | tw = command_substitute (cscmd, 0, 0); |
3185942a | 1256 | csbuf = tw ? tw->word : (char *)NULL; |
ac50fbac CR |
1257 | if (tw) |
1258 | dispose_word_desc (tw); | |
bb70624e JA |
1259 | |
1260 | /* Now clean up and destroy everything. */ | |
1261 | dispose_words (cmdlist); | |
1262 | free (cscmd); | |
1263 | unbind_compfunc_variables (1); | |
1264 | ||
1265 | if (csbuf == 0 || *csbuf == '\0') | |
1266 | { | |
1267 | FREE (csbuf); | |
1268 | return ((STRINGLIST *)NULL); | |
1269 | } | |
1270 | ||
1271 | /* Now break CSBUF up at newlines, with backslash allowed to escape a | |
1272 | newline, and put the individual words into a STRINGLIST. */ | |
7117c2d2 | 1273 | sl = strlist_create (16); |
bb70624e JA |
1274 | for (ws = 0; csbuf[ws]; ) |
1275 | { | |
1276 | we = ws; | |
1277 | while (csbuf[we] && csbuf[we] != '\n') | |
1278 | { | |
1279 | if (csbuf[we] == '\\' && csbuf[we+1] == '\n') | |
1280 | we++; | |
1281 | we++; | |
1282 | } | |
1283 | t = substring (csbuf, ws, we); | |
1284 | if (sl->list_len >= sl->list_size - 1) | |
7117c2d2 | 1285 | strlist_resize (sl, sl->list_size + 16); |
bb70624e JA |
1286 | sl->list[sl->list_len++] = t; |
1287 | while (csbuf[we] == '\n') we++; | |
1288 | ws = we; | |
1289 | } | |
1290 | sl->list[sl->list_len] = (char *)NULL; | |
1291 | ||
1292 | free (csbuf); | |
1293 | return (sl); | |
1294 | } | |
1295 | ||
1296 | static WORD_LIST * | |
1297 | command_line_to_word_list (line, llen, sentinel, nwp, cwp) | |
1298 | char *line; | |
1299 | int llen, sentinel, *nwp, *cwp; | |
1300 | { | |
1301 | WORD_LIST *ret; | |
74091dd4 | 1302 | const char *delims; |
0001803f CR |
1303 | |
1304 | #if 0 | |
1305 | delims = "()<>;&| \t\n"; /* shell metacharacters break words */ | |
1306 | #else | |
1307 | delims = rl_completer_word_break_characters; | |
1308 | #endif | |
a0c0a00f | 1309 | ret = split_at_delims (line, llen, delims, sentinel, SD_NOQUOTEDELIM|SD_COMPLETE, nwp, cwp); |
bb70624e JA |
1310 | return (ret); |
1311 | } | |
1312 | ||
1313 | /* Evaluate COMPSPEC *cs and return all matches for WORD. */ | |
1314 | ||
1315 | STRINGLIST * | |
0001803f | 1316 | gen_compspec_completions (cs, cmd, word, start, end, foundp) |
bb70624e | 1317 | COMPSPEC *cs; |
28ef6c31 JA |
1318 | const char *cmd; |
1319 | const char *word; | |
bb70624e | 1320 | int start, end; |
0001803f | 1321 | int *foundp; |
bb70624e JA |
1322 | { |
1323 | STRINGLIST *ret, *tmatches; | |
f73dda09 | 1324 | char *line; |
0001803f | 1325 | int llen, nw, cw, found, foundf; |
bb70624e | 1326 | WORD_LIST *lwords; |
ac50fbac | 1327 | WORD_DESC *lw; |
b80f6443 | 1328 | COMPSPEC *tcs; |
bb70624e | 1329 | |
0001803f CR |
1330 | found = 1; |
1331 | ||
7117c2d2 JA |
1332 | #ifdef DEBUG |
1333 | debug_printf ("gen_compspec_completions (%s, %s, %d, %d)", cmd, word, start, end); | |
1334 | debug_printf ("gen_compspec_completions: %s -> %p", cmd, cs); | |
1335 | #endif | |
bb70624e | 1336 | ret = gen_action_completions (cs, word); |
7117c2d2 | 1337 | #ifdef DEBUG |
bb70624e JA |
1338 | if (ret && progcomp_debug) |
1339 | { | |
f73dda09 | 1340 | debug_printf ("gen_action_completions (%p, %s) -->", cs, word); |
7117c2d2 | 1341 | strlist_print (ret, "\t"); |
bb70624e JA |
1342 | rl_on_new_line (); |
1343 | } | |
7117c2d2 | 1344 | #endif |
bb70624e JA |
1345 | |
1346 | /* Now we start generating completions based on the other members of CS. */ | |
1347 | if (cs->globpat) | |
1348 | { | |
1349 | tmatches = gen_globpat_matches (cs, word); | |
1350 | if (tmatches) | |
1351 | { | |
7117c2d2 | 1352 | #ifdef DEBUG |
bb70624e JA |
1353 | if (progcomp_debug) |
1354 | { | |
f73dda09 | 1355 | debug_printf ("gen_globpat_matches (%p, %s) -->", cs, word); |
7117c2d2 | 1356 | strlist_print (tmatches, "\t"); |
bb70624e JA |
1357 | rl_on_new_line (); |
1358 | } | |
7117c2d2 JA |
1359 | #endif |
1360 | ret = strlist_append (ret, tmatches); | |
1361 | strlist_dispose (tmatches); | |
bb70624e JA |
1362 | rl_filename_completion_desired = 1; |
1363 | } | |
1364 | } | |
1365 | ||
1366 | if (cs->words) | |
1367 | { | |
1368 | tmatches = gen_wordlist_matches (cs, word); | |
1369 | if (tmatches) | |
1370 | { | |
7117c2d2 | 1371 | #ifdef DEBUG |
bb70624e JA |
1372 | if (progcomp_debug) |
1373 | { | |
f73dda09 | 1374 | debug_printf ("gen_wordlist_matches (%p, %s) -->", cs, word); |
7117c2d2 | 1375 | strlist_print (tmatches, "\t"); |
bb70624e JA |
1376 | rl_on_new_line (); |
1377 | } | |
7117c2d2 JA |
1378 | #endif |
1379 | ret = strlist_append (ret, tmatches); | |
1380 | strlist_dispose (tmatches); | |
bb70624e JA |
1381 | } |
1382 | } | |
1383 | ||
1384 | lwords = (WORD_LIST *)NULL; | |
1385 | line = (char *)NULL; | |
1386 | if (cs->command || cs->funcname) | |
1387 | { | |
1388 | /* If we have a command or function to execute, we need to first break | |
1389 | the command line into individual words, find the number of words, | |
1390 | and find the word in the list containing the word to be completed. */ | |
d233b485 | 1391 | line = substring (pcomp_line, start, end); |
bb70624e JA |
1392 | llen = end - start; |
1393 | ||
7117c2d2 | 1394 | #ifdef DEBUG |
f73dda09 | 1395 | debug_printf ("command_line_to_word_list (%s, %d, %d, %p, %p)", |
d233b485 | 1396 | line, llen, pcomp_ind - start, &nw, &cw); |
7117c2d2 | 1397 | #endif |
d233b485 | 1398 | lwords = command_line_to_word_list (line, llen, pcomp_ind - start, &nw, &cw); |
ac50fbac CR |
1399 | /* If we skipped a NULL word at the beginning of the line, add it back */ |
1400 | if (lwords && lwords->word && cmd[0] == 0 && lwords->word->word[0] != 0) | |
1401 | { | |
1402 | lw = make_bare_word (cmd); | |
1403 | lwords = make_word_list (lw, lwords); | |
1404 | nw++; | |
1405 | cw++; | |
1406 | } | |
7117c2d2 | 1407 | #ifdef DEBUG |
bb70624e JA |
1408 | if (lwords == 0 && llen > 0) |
1409 | debug_printf ("ERROR: command_line_to_word_list returns NULL"); | |
1410 | else if (progcomp_debug) | |
1411 | { | |
1412 | debug_printf ("command_line_to_word_list -->"); | |
1413 | printf ("\t"); | |
1414 | print_word_list (lwords, "!"); | |
1415 | printf ("\n"); | |
1416 | fflush(stdout); | |
1417 | rl_on_new_line (); | |
1418 | } | |
7117c2d2 | 1419 | #endif |
bb70624e JA |
1420 | } |
1421 | ||
1422 | if (cs->funcname) | |
1423 | { | |
0001803f | 1424 | foundf = 0; |
d233b485 | 1425 | tmatches = gen_shell_function_matches (cs, cmd, word, line, pcomp_ind - start, lwords, nw, cw, &foundf); |
0001803f CR |
1426 | if (foundf != 0) |
1427 | found = foundf; | |
bb70624e JA |
1428 | if (tmatches) |
1429 | { | |
7117c2d2 | 1430 | #ifdef DEBUG |
bb70624e JA |
1431 | if (progcomp_debug) |
1432 | { | |
ac50fbac | 1433 | debug_printf ("gen_shell_function_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw); |
7117c2d2 | 1434 | strlist_print (tmatches, "\t"); |
bb70624e JA |
1435 | rl_on_new_line (); |
1436 | } | |
7117c2d2 JA |
1437 | #endif |
1438 | ret = strlist_append (ret, tmatches); | |
1439 | strlist_dispose (tmatches); | |
bb70624e JA |
1440 | } |
1441 | } | |
1442 | ||
1443 | if (cs->command) | |
1444 | { | |
d233b485 | 1445 | tmatches = gen_command_matches (cs, cmd, word, line, pcomp_ind - start, lwords, nw, cw); |
bb70624e JA |
1446 | if (tmatches) |
1447 | { | |
7117c2d2 | 1448 | #ifdef DEBUG |
bb70624e JA |
1449 | if (progcomp_debug) |
1450 | { | |
ac50fbac | 1451 | debug_printf ("gen_command_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw); |
7117c2d2 | 1452 | strlist_print (tmatches, "\t"); |
bb70624e JA |
1453 | rl_on_new_line (); |
1454 | } | |
7117c2d2 JA |
1455 | #endif |
1456 | ret = strlist_append (ret, tmatches); | |
1457 | strlist_dispose (tmatches); | |
bb70624e JA |
1458 | } |
1459 | } | |
1460 | ||
1461 | if (cs->command || cs->funcname) | |
1462 | { | |
1463 | if (lwords) | |
1464 | dispose_words (lwords); | |
1465 | FREE (line); | |
1466 | } | |
1467 | ||
0001803f CR |
1468 | if (foundp) |
1469 | *foundp = found; | |
1470 | ||
1471 | if (found == 0 || (found & PCOMP_RETRYFAIL)) | |
1472 | { | |
1473 | strlist_dispose (ret); | |
1474 | return NULL; | |
1475 | } | |
1476 | ||
bb70624e JA |
1477 | if (cs->filterpat) |
1478 | { | |
1479 | tmatches = filter_stringlist (ret, cs->filterpat, word); | |
7117c2d2 | 1480 | #ifdef DEBUG |
bb70624e JA |
1481 | if (progcomp_debug) |
1482 | { | |
f73dda09 | 1483 | debug_printf ("filter_stringlist (%p, %s, %s) -->", ret, cs->filterpat, word); |
7117c2d2 | 1484 | strlist_print (tmatches, "\t"); |
bb70624e JA |
1485 | rl_on_new_line (); |
1486 | } | |
7117c2d2 | 1487 | #endif |
bb70624e JA |
1488 | if (ret && ret != tmatches) |
1489 | { | |
1490 | FREE (ret->list); | |
1491 | free (ret); | |
1492 | } | |
1493 | ret = tmatches; | |
1494 | } | |
1495 | ||
1496 | if (cs->prefix || cs->suffix) | |
7117c2d2 | 1497 | ret = strlist_prefix_suffix (ret, cs->prefix, cs->suffix); |
bb70624e | 1498 | |
28ef6c31 JA |
1499 | /* If no matches have been generated and the user has specified that |
1500 | directory completion should be done as a default, call | |
1501 | gen_action_completions again to generate a list of matching directory | |
1502 | names. */ | |
1503 | if ((ret == 0 || ret->list_len == 0) && (cs->options & COPT_DIRNAMES)) | |
1504 | { | |
b80f6443 JA |
1505 | tcs = compspec_create (); |
1506 | tcs->actions = CA_DIRECTORY; | |
ac50fbac | 1507 | FREE (ret); |
b80f6443 JA |
1508 | ret = gen_action_completions (tcs, word); |
1509 | compspec_dispose (tcs); | |
1510 | } | |
1511 | else if (cs->options & COPT_PLUSDIRS) | |
1512 | { | |
1513 | tcs = compspec_create (); | |
1514 | tcs->actions = CA_DIRECTORY; | |
1515 | tmatches = gen_action_completions (tcs, word); | |
1516 | ret = strlist_append (ret, tmatches); | |
1517 | strlist_dispose (tmatches); | |
1518 | compspec_dispose (tcs); | |
28ef6c31 JA |
1519 | } |
1520 | ||
bb70624e JA |
1521 | return (ret); |
1522 | } | |
1523 | ||
3185942a JA |
1524 | void |
1525 | pcomp_set_readline_variables (flags, nval) | |
1526 | int flags, nval; | |
1527 | { | |
1528 | /* If the user specified that the compspec returns filenames, make | |
1529 | sure that readline knows it. */ | |
1530 | if (flags & COPT_FILENAMES) | |
1531 | rl_filename_completion_desired = nval; | |
1532 | /* If the user doesn't want a space appended, tell readline. */ | |
1533 | if (flags & COPT_NOSPACE) | |
1534 | rl_completion_suppress_append = nval; | |
ac50fbac CR |
1535 | /* The value here is inverted, since the default is on and the `noquote' |
1536 | option is supposed to turn it off */ | |
1537 | if (flags & COPT_NOQUOTE) | |
1538 | rl_filename_quoting_desired = 1 - nval; | |
a0c0a00f CR |
1539 | if (flags & COPT_NOSORT) |
1540 | rl_sort_completion_matches = 1 - nval; | |
3185942a JA |
1541 | } |
1542 | ||
1543 | /* Set or unset FLAGS in the options word of the current compspec. | |
1544 | SET_OR_UNSET is 1 for setting, 0 for unsetting. */ | |
1545 | void | |
1546 | pcomp_set_compspec_options (cs, flags, set_or_unset) | |
1547 | COMPSPEC *cs; | |
1548 | int flags, set_or_unset; | |
1549 | { | |
1550 | if (cs == 0 && ((cs = pcomp_curcs) == 0)) | |
1551 | return; | |
1552 | if (set_or_unset) | |
1553 | cs->options |= flags; | |
1554 | else | |
1555 | cs->options &= ~flags; | |
1556 | } | |
1557 | ||
0001803f CR |
1558 | static STRINGLIST * |
1559 | gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs) | |
1560 | const char *ocmd; | |
28ef6c31 JA |
1561 | const char *cmd; |
1562 | const char *word; | |
0001803f CR |
1563 | int start, end; |
1564 | int *foundp, *retryp; | |
1565 | COMPSPEC **lastcs; | |
bb70624e | 1566 | { |
3185942a | 1567 | COMPSPEC *cs, *oldcs; |
2b76266c | 1568 | const char *oldcmd, *oldtxt; |
0001803f | 1569 | STRINGLIST *ret; |
bb70624e | 1570 | |
0001803f CR |
1571 | cs = progcomp_search (ocmd); |
1572 | ||
1573 | if (cs == 0 || cs == *lastcs) | |
ac50fbac CR |
1574 | { |
1575 | #if 0 | |
1576 | if (foundp) | |
1577 | *foundp = 0; | |
1578 | #endif | |
1579 | return (NULL); | |
1580 | } | |
0001803f CR |
1581 | |
1582 | if (*lastcs) | |
1583 | compspec_dispose (*lastcs); | |
1584 | cs->refcount++; /* XXX */ | |
1585 | *lastcs = cs; | |
bb70624e | 1586 | |
b80f6443 JA |
1587 | cs = compspec_copy (cs); |
1588 | ||
3185942a JA |
1589 | oldcs = pcomp_curcs; |
1590 | oldcmd = pcomp_curcmd; | |
2b76266c | 1591 | oldtxt = pcomp_curtxt; |
3185942a JA |
1592 | |
1593 | pcomp_curcs = cs; | |
1594 | pcomp_curcmd = cmd; | |
2b76266c | 1595 | pcomp_curtxt = word; |
3185942a | 1596 | |
0001803f | 1597 | ret = gen_compspec_completions (cs, cmd, word, start, end, foundp); |
bb70624e | 1598 | |
3185942a JA |
1599 | pcomp_curcs = oldcs; |
1600 | pcomp_curcmd = oldcmd; | |
2b76266c | 1601 | pcomp_curtxt = oldtxt; |
3185942a | 1602 | |
0001803f CR |
1603 | /* We need to conditionally handle setting *retryp here */ |
1604 | if (retryp) | |
1605 | *retryp = foundp && (*foundp & PCOMP_RETRYFAIL); | |
1606 | ||
1607 | if (foundp) | |
1608 | { | |
1609 | *foundp &= ~PCOMP_RETRYFAIL; | |
1610 | *foundp |= cs->options; | |
1611 | } | |
1612 | ||
b80f6443 | 1613 | compspec_dispose (cs); |
0001803f CR |
1614 | return ret; |
1615 | } | |
1616 | ||
1617 | /* The driver function for the programmable completion code. Returns a list | |
1618 | of matches for WORD, which is an argument to command CMD. START and END | |
d233b485 CR |
1619 | bound the command currently being completed in pcomp_line (usually |
1620 | rl_line_buffer). */ | |
0001803f CR |
1621 | char ** |
1622 | programmable_completions (cmd, word, start, end, foundp) | |
1623 | const char *cmd; | |
1624 | const char *word; | |
1625 | int start, end, *foundp; | |
1626 | { | |
d233b485 | 1627 | COMPSPEC *lastcs; |
0001803f CR |
1628 | STRINGLIST *ret; |
1629 | char **rmatches, *t; | |
1630 | int found, retry, count; | |
d233b485 CR |
1631 | char *ocmd; |
1632 | int oend; | |
1633 | #if defined (ALIAS) | |
1634 | alias_t *al; | |
1635 | #endif | |
0001803f CR |
1636 | |
1637 | lastcs = 0; | |
1638 | found = count = 0; | |
1639 | ||
d233b485 CR |
1640 | pcomp_line = rl_line_buffer; |
1641 | pcomp_ind = rl_point; | |
1642 | ||
1643 | ocmd = (char *)cmd; | |
1644 | oend = end; | |
1645 | ||
0001803f CR |
1646 | do |
1647 | { | |
1648 | retry = 0; | |
1649 | ||
1650 | /* We look at the basename of CMD if the full command does not have | |
1651 | an associated COMPSPEC. */ | |
d233b485 | 1652 | ret = gen_progcomp_completions (ocmd, ocmd, word, start, oend, &found, &retry, &lastcs); |
0001803f CR |
1653 | if (found == 0) |
1654 | { | |
d233b485 | 1655 | t = strrchr (ocmd, '/'); |
0001803f | 1656 | if (t && *(++t)) |
d233b485 | 1657 | ret = gen_progcomp_completions (t, ocmd, word, start, oend, &found, &retry, &lastcs); |
0001803f CR |
1658 | } |
1659 | ||
1660 | if (found == 0) | |
d233b485 CR |
1661 | ret = gen_progcomp_completions (DEFAULTCMD, ocmd, word, start, oend, &found, &retry, &lastcs); |
1662 | ||
1663 | #if defined (ALIAS) | |
1664 | /* Look up any alias for CMD, try to gen completions for it */ | |
1665 | /* Look up the alias, find the value, build a new line replacing CMD | |
1666 | with that value, offsetting PCOMP_IND and END appropriately, reset | |
1667 | PCOMP_LINE to the new line and OCMD with the new command name, then | |
1668 | call gen_progcomp_completions again. We could use alias_expand for | |
1669 | this, but it does more (and less) than we need right now. */ | |
1670 | if (found == 0 && retry == 0 && progcomp_alias && (al = find_alias (ocmd))) | |
1671 | { | |
1672 | char *ncmd, *nline, *ntxt; | |
1673 | int ind, lendiff; | |
1674 | size_t nlen, olen, llen; | |
1675 | ||
1676 | /* We found an alias for OCMD. Take the value and build a new line */ | |
1677 | ntxt = al->value; | |
1678 | nlen = strlen (ntxt); | |
1679 | if (nlen == 0) | |
1680 | break; | |
1681 | olen = strlen (ocmd); | |
1682 | lendiff = nlen - olen; /* can be negative */ | |
1683 | llen = strlen (pcomp_line); | |
1684 | ||
1685 | nline = (char *)xmalloc (llen + lendiff + 1); | |
1686 | if (start > 0) | |
1687 | strncpy (nline, pcomp_line, start); | |
1688 | strncpy (nline + start, ntxt, nlen); | |
1689 | strcpy (nline + start + nlen, pcomp_line + start + olen); | |
1690 | ||
1691 | /* Find the first word of the alias value and use that as OCMD. We | |
1692 | don't check the alias value to see whether it begins with a valid | |
1693 | command name, so this can be fooled. */ | |
1694 | ind = skip_to_delim (ntxt, 0, "()<>;&| \t\n", SD_NOJMP|SD_COMPLETE); | |
1695 | if (ind > 0) | |
1696 | ncmd = substring (ntxt, 0, ind); | |
1697 | else | |
1698 | { | |
1699 | free (nline); | |
1700 | break; /* will free pcomp_line and ocmd later */ | |
1701 | } | |
1702 | ||
1703 | /* Adjust PCOMP_IND and OEND appropriately */ | |
1704 | pcomp_ind += lendiff; | |
1705 | oend += lendiff; | |
1706 | ||
1707 | /* Set up values with new line. WORD stays the same. */ | |
1708 | if (ocmd != cmd) | |
1709 | free (ocmd); | |
1710 | if (pcomp_line != rl_line_buffer) | |
1711 | free (pcomp_line); | |
1712 | ||
1713 | ocmd = ncmd; | |
1714 | pcomp_line = nline; | |
1715 | ||
1716 | /* And go back and start over. */ | |
1717 | retry = 1; | |
1718 | } | |
1719 | #endif /* ALIAS */ | |
0001803f CR |
1720 | |
1721 | count++; | |
1722 | ||
1723 | if (count > 32) | |
1724 | { | |
a0c0a00f | 1725 | internal_warning (_("programmable_completion: %s: possible retry loop"), cmd); |
0001803f CR |
1726 | break; |
1727 | } | |
1728 | } | |
1729 | while (retry); | |
b80f6443 | 1730 | |
d233b485 CR |
1731 | if (pcomp_line != rl_line_buffer) |
1732 | free (pcomp_line); | |
1733 | if (ocmd != cmd) | |
1734 | free (ocmd); | |
1735 | ||
bb70624e JA |
1736 | if (ret) |
1737 | { | |
1738 | rmatches = ret->list; | |
1739 | free (ret); | |
1740 | } | |
1741 | else | |
1742 | rmatches = (char **)NULL; | |
1743 | ||
0001803f CR |
1744 | if (foundp) |
1745 | *foundp = found; | |
1746 | ||
1747 | if (lastcs) /* XXX - should be while? */ | |
1748 | compspec_dispose (lastcs); | |
1749 | ||
d233b485 CR |
1750 | /* XXX restore pcomp_line and pcomp_ind? */ |
1751 | pcomp_line = rl_line_buffer; | |
1752 | pcomp_ind = rl_point; | |
1753 | ||
bb70624e JA |
1754 | return (rmatches); |
1755 | } | |
1756 | ||
1757 | #endif /* PROGRAMMABLE_COMPLETION */ |