-/* pcomplete.c - functions to generate lists of matches for programmable
- completion. */
+/* pcomplete.c - functions to generate lists of matches for programmable completion. */
-/* Copyright (C) 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2012 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any later
- version.
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
#include <config.h>
#if defined (PREFER_STDARG)
# include <stdarg.h>
#else
-# if defined (PREFER_VARARGS)
-# include <varargs.h>
-# endif
+# include <varargs.h>
#endif
+#include <sys/time.h>
+
#include <stdio.h>
#include "bashansi.h"
+#include "bashintl.h"
#include "shell.h"
#include "pcomplete.h"
#include "alias.h"
#include "bashline.h"
+#include "execute_cmd.h"
#include "pathexp.h"
#if defined (JOB_CONTROL)
# include "trap.h"
#endif
+#include "shmbutil.h"
+
#include "builtins.h"
#include "builtins/common.h"
+#include "builtins/builtext.h"
#include <glob/glob.h>
-#include <glob/fnmatch.h>
+#include <glob/strmatch.h>
#include <readline/rlconf.h>
#include <readline/readline.h>
#include <readline/history.h>
+#define PCOMP_RETRYFAIL 256
+
#ifdef STRDUP
# undef STRDUP
#endif
extern char *strpbrk __P((char *, char *));
#endif
-extern int rl_filename_completion_desired;
extern int array_needs_making;
extern STRING_INT_ALIST word_token_alist[];
extern char *signal_names[];
+extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
+
+#if defined (DEBUG)
+#if defined (PREFER_STDARG)
+static void debug_printf (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+#endif
+#endif /* DEBUG */
+
+static int it_init_joblist __P((ITEMLIST *, int));
+
+static int it_init_aliases __P((ITEMLIST *));
+static int it_init_arrayvars __P((ITEMLIST *));
+static int it_init_bindings __P((ITEMLIST *));
+static int it_init_builtins __P((ITEMLIST *));
+static int it_init_disabled __P((ITEMLIST *));
+static int it_init_enabled __P((ITEMLIST *));
+static int it_init_exported __P((ITEMLIST *));
+static int it_init_functions __P((ITEMLIST *));
+static int it_init_helptopics __P((ITEMLIST *));
+static int it_init_hostnames __P((ITEMLIST *));
+static int it_init_jobs __P((ITEMLIST *));
+static int it_init_running __P((ITEMLIST *));
+static int it_init_stopped __P((ITEMLIST *));
+static int it_init_keywords __P((ITEMLIST *));
+static int it_init_signals __P((ITEMLIST *));
+static int it_init_variables __P((ITEMLIST *));
+static int it_init_setopts __P((ITEMLIST *));
+static int it_init_shopts __P((ITEMLIST *));
+
+static int shouldexp_filterpat __P((char *));
+static char *preproc_filterpat __P((char *, char *));
+
+static void init_itemlist_from_varlist __P((ITEMLIST *, SVFUNC *));
+
+static STRINGLIST *gen_matches_from_itemlist __P((ITEMLIST *, const char *));
+static STRINGLIST *gen_action_completions __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_globpat_matches __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_wordlist_matches __P((COMPSPEC *, const char *));
+static STRINGLIST *gen_shell_function_matches __P((COMPSPEC *, const char *,
+ const char *,
+ char *, int, WORD_LIST *,
+ int, int, int *));
+static STRINGLIST *gen_command_matches __P((COMPSPEC *, const char *,
+ const char *,
+ char *, int, WORD_LIST *,
+ int, int));
+
+static STRINGLIST *gen_progcomp_completions __P((const char *, const char *,
+ const char *,
+ int, int, int *, int *,
+ COMPSPEC **));
+
+static char *pcomp_filename_completion_function __P((const char *, int));
-static int it_init_aliases ();
-static int it_init_arrayvars ();
-static int it_init_bindings ();
-static int it_init_builtins ();
-static int it_init_disabled ();
-static int it_init_enabled ();
-static int it_init_exported ();
-static int it_init_functions ();
-static int it_init_hostnames ();
-static int it_init_jobs ();
-static int it_init_running ();
-static int it_init_stopped ();
-static int it_init_keywords ();
-static int it_init_signals ();
-static int it_init_variables ();
-static int it_init_setopts ();
-static int it_init_shopts ();
+#if defined (ARRAY_VARS)
+static SHELL_VAR *bind_comp_words __P((WORD_LIST *));
+#endif
+static void bind_compfunc_variables __P((char *, int, WORD_LIST *, int, int));
+static void unbind_compfunc_variables __P((int));
+static WORD_LIST *build_arg_list __P((char *, const char *, const char *, WORD_LIST *, int));
+static WORD_LIST *command_line_to_word_list __P((char *, int, int, int *, int *));
+#ifdef DEBUG
static int progcomp_debug = 0;
+#endif
int prog_completion_enabled = 1;
ITEMLIST it_disabled = { 0, it_init_disabled, (STRINGLIST *)0 };
ITEMLIST it_enabled = { 0, it_init_enabled, (STRINGLIST *)0 };
ITEMLIST it_exports = { LIST_DYNAMIC, it_init_exported, (STRINGLIST *)0 };
-ITEMLIST it_files = { LIST_DYNAMIC }; /* unused */
+ITEMLIST it_files = { LIST_DYNAMIC }; /* unused */
ITEMLIST it_functions = { 0, it_init_functions, (STRINGLIST *)0 };
+ITEMLIST it_helptopics = { 0, it_init_helptopics, (STRINGLIST *)0 };
ITEMLIST it_hostnames = { LIST_DYNAMIC, it_init_hostnames, (STRINGLIST *)0 };
-ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 };;
+ITEMLIST it_groups = { LIST_DYNAMIC }; /* unused */
+ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 };
ITEMLIST it_keywords = { 0, it_init_keywords, (STRINGLIST *)0 };
ITEMLIST it_running = { LIST_DYNAMIC, it_init_running, (STRINGLIST *)0 };
+ITEMLIST it_services = { LIST_DYNAMIC }; /* unused */
ITEMLIST it_setopts = { 0, it_init_setopts, (STRINGLIST *)0 };
ITEMLIST it_shopts = { 0, it_init_shopts, (STRINGLIST *)0 };
ITEMLIST it_signals = { 0, it_init_signals, (STRINGLIST *)0 };
ITEMLIST it_stopped = { LIST_DYNAMIC, it_init_stopped, (STRINGLIST *)0 };
-ITEMLIST it_users = { LIST_DYNAMIC }; /* unused */
+ITEMLIST it_users = { LIST_DYNAMIC }; /* unused */
ITEMLIST it_variables = { LIST_DYNAMIC, it_init_variables, (STRINGLIST *)0 };
-/* Debugging code */
-#if !defined (USE_VARARGS)
-static void
-debug_printf (format, arg1, arg2, arg3, arg4, arg5)
- char *format;
-{
- if (progcomp_debug == 0)
- return;
+COMPSPEC *pcomp_curcs;
+const char *pcomp_curcmd;
- fprintf (stdout, format, arg1, arg2, arg3, arg4, arg5);
- fprintf (stdout, "\n");
- rl_on_new_line ();
-}
-#else
+#ifdef DEBUG
+/* Debugging code */
static void
#if defined (PREFER_STDARG)
debug_printf (const char *format, ...)
if (progcomp_debug == 0)
return;
-#if defined (PREFER_STDARG)
- va_start (args, format);
-#else
- va_start (args);
-#endif
+ SH_VA_START (args, format);
fprintf (stdout, "DEBUG: ");
vfprintf (stdout, format, args);
va_end (args);
}
-#endif /* USE_VARARGS */
+#endif
/* Functions to manage the item lists */
if (sl)
{
if ((itp->flags & (LIST_DONTFREEMEMBERS|LIST_DONTFREE)) == 0)
- free_array_members (sl->list);
+ strvec_flush (sl->list);
if ((itp->flags & LIST_DONTFREE) == 0)
free (sl->list);
free (sl);
itp->flags &= ~(LIST_DONTFREE|LIST_DONTFREEMEMBERS|LIST_INITIALIZED|LIST_DIRTY);
}
-/* Functions to manage the string lists -- lists of matches. These should
- really be moved to a separate file. */
-
-/* Allocate a new STRINGLIST, with room for N strings. */
-STRINGLIST *
-alloc_stringlist (n)
- int n;
-{
- STRINGLIST *ret;
- register int i;
-
- ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
- if (n)
- {
- ret->list = alloc_array (n+1);
- ret->list_size = n;
- for (i = 0; i < n; i++)
- ret->list[i] = (char *)NULL;
- }
- else
- {
- ret->list = (char **)NULL;
- ret->list_size = 0;
- }
- ret->list_len = 0;
- return ret;
-}
-
-STRINGLIST *
-realloc_stringlist (sl, n)
- STRINGLIST *sl;
- int n;
-{
- register int i;
-
- if (n > sl->list_size)
- {
- sl->list = (char **)xrealloc (sl->list, (n+1) * sizeof (char *));
- for (i = sl->list_size; i <= n; i++)
- sl->list[i] = (char *)NULL;
- sl->list_size = n;
- }
- return sl;
-}
-
-void
-free_stringlist (sl)
- STRINGLIST *sl;
-{
- if (sl == 0)
- return;
- if (sl->list)
- free_array (sl->list);
- free (sl);
-}
-
-STRINGLIST *
-copy_stringlist (sl)
- STRINGLIST *sl;
-{
- STRINGLIST *new;
- register int i;
-
- new = alloc_stringlist (sl->list_size);
- /* I'd like to use copy_array, but that doesn't copy everything. */
- if (sl->list)
- {
- for (i = 0; i < sl->list_size; i++)
- new->list[i] = STRDUP (sl->list[i]);
- }
- new->list_size = sl->list_size;
- new->list_len = sl->list_len;
- /* just being careful */
- if (new->list)
- new->list[new->list_len] = (char *)NULL;
- return new;
-}
-
-/* Return a new STRINGLIST with everything from M1 and M2. */
-
-STRINGLIST *
-merge_stringlists (m1, m2)
- STRINGLIST *m1, *m2;
-{
- STRINGLIST *sl;
- int i, n, l1, l2;
-
- l1 = m1 ? m1->list_len : 0;
- l2 = m2 ? m2->list_len : 0;
-
- sl = alloc_stringlist (l1 + l2 + 1);
- for (i = n = 0; i < l1; i++, n++)
- sl->list[n] = STRDUP (m1->list[i]);
- for (i = 0; i < l2; i++, n++)
- sl->list[n] = STRDUP (m2->list[i]);
- sl->list_len = n;
- sl->list[n] = (char *)NULL;
-}
-
-/* Make STRINGLIST M1 contain everything in M1 and M2. */
-STRINGLIST *
-append_stringlist (m1, m2)
- STRINGLIST *m1, *m2;
-{
- register int i, n, len1, len2;
-
- if (m1 == 0)
- {
- m1 = copy_stringlist (m2);
- return m1;
- }
-
- len1 = m1->list_len;
- len2 = m2 ? m2->list_len : 0;
-
- if (len2)
- {
- m1 = realloc_stringlist (m1, len1 + len2 + 1);
- for (i = 0, n = len1; i < len2; i++, n++)
- m1->list[n] = STRDUP (m2->list[i]);
- m1->list[n] = (char *)NULL;
- m1->list_len = n;
- }
-
- return m1;
-}
static int
shouldexp_filterpat (s)
not = (npat[0] == '!');
t = not ? npat + 1 : npat;
- ret = alloc_stringlist (sl->list_size);
+ ret = strlist_create (sl->list_size);
for (i = 0; i < sl->list_len; i++)
{
- m = fnmatch (t, sl->list[i], FNMATCH_EXTFLAG);
+ m = strmatch (t, sl->list[i], FNMATCH_EXTFLAG);
if ((not && m == FNM_NOMATCH) || (not == 0 && m != FNM_NOMATCH))
free (sl->list[i]);
else
return ret;
}
-STRINGLIST *
-prefix_suffix_stringlist (sl, prefix, suffix)
- STRINGLIST *sl;
- char *prefix, *suffix;
-{
- int plen, slen, tlen, llen, i;
- char *t;
-
- if (sl == 0 || sl->list == 0 || sl->list_len == 0)
- return sl;
-
- plen = STRLEN (prefix);
- slen = STRLEN (suffix);
-
- if (plen == 0 && slen == 0)
- return (sl);
-
- for (i = 0; i < sl->list_len; i++)
- {
- llen = STRLEN (sl->list[i]);
- tlen = plen + llen + slen + 1;
- t = xmalloc (tlen + 1);
- if (plen)
- strcpy (t, prefix);
- strcpy (t + plen, sl->list[i]);
- if (slen)
- strcpy (t + plen + llen, suffix);
- free (sl->list[i]);
- sl->list[i] = t;
- }
-
- return (sl);
-}
-
-void
-print_stringlist (sl, prefix)
- STRINGLIST *sl;
- char *prefix;
-{
- register int i;
-
- if (sl == 0)
- return;
- for (i = 0; i < sl->list_len; i++)
- printf ("%s%s\n", prefix ? prefix : "", sl->list[i]);
-}
-
-/* Turn an array of strings returned by completion_matches into a STRINGLIST.
- This understands how completion_matches sets matches[0] (the lcd of the
+/* Turn an array of strings returned by rl_completion_matches into a STRINGLIST.
+ This understands how rl_completion_matches sets matches[0] (the lcd of the
strings in the list, unless it's the only match). */
STRINGLIST *
completions_to_stringlist (matches)
STRINGLIST *sl;
int mlen, i, n;
- mlen = (matches == 0) ? 0 : array_len (matches);
- sl = alloc_stringlist (mlen + 1);
+ mlen = (matches == 0) ? 0 : strvec_len (matches);
+ sl = strlist_create (mlen + 1);
if (matches == 0 || matches[0] == 0)
return sl;
ITEMLIST *itp;
{
#ifdef ALIAS
- alias_t **aliases;
+ alias_t **alias_list;
register int i, n;
STRINGLIST *sl;
- aliases = all_aliases ();
- if (aliases == 0)
+ alias_list = all_aliases ();
+ if (alias_list == 0)
{
itp->slist = (STRINGLIST *)NULL;
return 0;
}
- for (n = 0; aliases[n]; n++)
+ for (n = 0; alias_list[n]; n++)
;
- sl = alloc_stringlist (n+1);
+ sl = strlist_create (n+1);
for (i = 0; i < n; i++)
- sl->list[i] = STRDUP (aliases[i]->name);
+ sl->list[i] = STRDUP (alias_list[i]->name);
sl->list[n] = (char *)NULL;
sl->list_size = sl->list_len = n;
itp->slist = sl;
#else
itp->slist = (STRINGLIST *)NULL;
#endif
+ free (alias_list);
return 1;
}
register int i, n;
vlist = (*svfunc) ();
+ if (vlist == 0)
+ {
+ itp->slist = (STRINGLIST *)NULL;
+ return;
+ }
for (n = 0; vlist[n]; n++)
;
- sl = alloc_stringlist (n+1);
+ sl = strlist_create (n+1);
for (i = 0; i < n; i++)
sl->list[i] = savestring (vlist[i]->name);
sl->list[sl->list_len = n] = (char *)NULL;
STRINGLIST *sl;
/* rl_funmap_names allocates blist, but not its members */
- blist = rl_funmap_names ();
- sl = alloc_stringlist (0);
+ blist = (char **)rl_funmap_names (); /* XXX fix const later */
+ sl = strlist_create (0);
sl->list = blist;
sl->list_size = 0;
- sl->list_len = array_len (sl->list);
+ sl->list_len = strvec_len (sl->list);
itp->flags |= LIST_DONTFREEMEMBERS;
itp->slist = sl;
ITEMLIST *itp;
{
STRINGLIST *sl;
- char **list;
register int i, n;
- sl = alloc_stringlist (num_shell_builtins);
+ sl = strlist_create (num_shell_builtins);
for (i = n = 0; i < num_shell_builtins; i++)
if (shell_builtins[i].function)
sl->list[n++] = shell_builtins[i].name;
ITEMLIST *itp;
{
STRINGLIST *sl;
- char **list;
register int i, n;
- sl = alloc_stringlist (num_shell_builtins);
+ sl = strlist_create (num_shell_builtins);
for (i = n = 0; i < num_shell_builtins; i++)
{
if (shell_builtins[i].function && (shell_builtins[i].flags & BUILTIN_ENABLED))
ITEMLIST *itp;
{
STRINGLIST *sl;
- char **list;
register int i, n;
- sl = alloc_stringlist (num_shell_builtins);
+ sl = strlist_create (num_shell_builtins);
for (i = n = 0; i < num_shell_builtins; i++)
{
if (shell_builtins[i].function && ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0))
return 0;
}
+/* Like it_init_builtins, but includes everything the help builtin looks at,
+ not just builtins with an active implementing function. */
+static int
+it_init_helptopics (itp)
+ ITEMLIST *itp;
+{
+ STRINGLIST *sl;
+ register int i, n;
+
+ sl = strlist_create (num_shell_builtins);
+ for (i = n = 0; i < num_shell_builtins; i++)
+ sl->list[n++] = shell_builtins[i].name;
+ sl->list[sl->list_len = n] = (char *)NULL;
+ itp->flags |= LIST_DONTFREEMEMBERS;
+ itp->slist = sl;
+ return 0;
+}
+
static int
it_init_hostnames (itp)
ITEMLIST *itp;
{
STRINGLIST *sl;
- sl = alloc_stringlist (0);
+ sl = strlist_create (0);
sl->list = get_hostname_list ();
- sl->list_len = sl->list ? array_len (sl->list) : 0;
+ sl->list_len = sl->list ? strvec_len (sl->list) : 0;
sl->list_size = sl->list_len;
itp->slist = sl;
itp->flags |= LIST_DONTFREEMEMBERS|LIST_DONTFREE;
{
#if defined (JOB_CONTROL)
STRINGLIST *sl;
- register int i, n;
+ register int i;
register PROCESS *p;
char *s, *t;
- JOB_STATE js;
+ JOB *j;
+ JOB_STATE ws; /* wanted state */
+ ws = JNONE;
if (jstate == 0)
- js = JRUNNING;
+ ws = JRUNNING;
else if (jstate == 1)
- js = JSTOPPED;
+ ws = JSTOPPED;
- sl = alloc_stringlist (job_slots);
- for (i = job_slots - 1; i >= 0; i--)
+ sl = strlist_create (js.j_jobslots);
+ for (i = js.j_jobslots - 1; i >= 0; i--)
{
- if (jobs[i] == 0)
+ j = get_job_by_jid (i);
+ if (j == 0)
continue;
- p = jobs[i]->pipe;
- if (jstate == -1 || JOBSTATE(i) == js)
+ p = j->pipe;
+ if (jstate == -1 || JOBSTATE(i) == ws)
{
s = savestring (p->command);
t = strpbrk (s, " \t\n");
if (t)
*t = '\0';
- sl->list[sl->list_len++] = s;
+ sl->list[sl->list_len++] = s;
}
}
itp->slist = sl;
for (n = 0; word_token_alist[n].word; n++)
;
- sl = alloc_stringlist (n);
+ sl = strlist_create (n);
for (i = 0; i < n; i++)
sl->list[i] = word_token_alist[i].word;
sl->list[sl->list_len = i] = (char *)NULL;
{
STRINGLIST *sl;
- sl = alloc_stringlist (0);
+ sl = strlist_create (0);
sl->list = signal_names;
- sl->list_len = array_len (sl->list);
+ sl->list_len = strvec_len (sl->list);
itp->flags |= LIST_DONTFREE;
itp->slist = sl;
return 0;
{
STRINGLIST *sl;
- sl = alloc_stringlist (0);
+ sl = strlist_create (0);
sl->list = get_minus_o_opts ();
- sl->list_len = array_len (sl->list);
+ sl->list_len = strvec_len (sl->list);
itp->slist = sl;
itp->flags |= LIST_DONTFREEMEMBERS;
return 0;
{
STRINGLIST *sl;
- sl = alloc_stringlist (0);
+ sl = strlist_create (0);
sl->list = get_shopt_options ();
- sl->list_len = array_len (sl->list);
+ sl->list_len = strvec_len (sl->list);
itp->slist = sl;
itp->flags |= LIST_DONTFREEMEMBERS;
return 0;
/* Generate a list of all matches for TEXT using the STRINGLIST in itp->slist
as the list of possibilities. If the itemlist has been marked dirty or
it should be regenerated every time, destroy the old STRINGLIST and make a
- new one before trying the match. */
+ new one before trying the match. TEXT is dequoted before attempting a
+ match. */
static STRINGLIST *
gen_matches_from_itemlist (itp, text)
ITEMLIST *itp;
- char *text;
+ const char *text;
{
STRINGLIST *ret, *sl;
int tlen, i, n;
+ char *ntxt;
if ((itp->flags & (LIST_DIRTY|LIST_DYNAMIC)) ||
(itp->flags & LIST_INITIALIZED) == 0)
{
- if (itp->flags & (LIST_DIRTY | LIST_DYNAMIC))
+ if (itp->flags & (LIST_DIRTY|LIST_DYNAMIC))
clean_itemlist (itp);
if ((itp->flags & LIST_INITIALIZED) == 0)
initialize_itemlist (itp);
}
- ret = alloc_stringlist (itp->slist->list_len+1);
+ if (itp->slist == 0)
+ return ((STRINGLIST *)NULL);
+ ret = strlist_create (itp->slist->list_len+1);
sl = itp->slist;
- tlen = STRLEN (text);
+
+ ntxt = bash_dequote_text (text);
+ tlen = STRLEN (ntxt);
+
for (i = n = 0; i < sl->list_len; i++)
{
- if (tlen == 0 || STREQN (sl->list[i], text, tlen))
+ if (tlen == 0 || STREQN (sl->list[i], ntxt, tlen))
ret->list[n++] = STRDUP (sl->list[i]);
}
ret->list[ret->list_len = n] = (char *)NULL;
+
+ FREE (ntxt);
return ret;
}
-/* A wrapper for filename_completion_function that dequotes the filename
+/* A wrapper for rl_filename_completion_function that dequotes the filename
before attempting completions. */
static char *
pcomp_filename_completion_function (text, state)
- char *text;
+ const char *text;
int state;
{
static char *dfn; /* dequoted filename */
int qc;
+ int iscompgen, iscompleting;
if (state == 0)
{
FREE (dfn);
/* remove backslashes quoting special characters in filenames. */
- if (rl_filename_dequoting_function)
+ /* There are roughly three paths we can follow to get here:
+ 1. complete -f
+ 2. compgen -f "$word" from a completion function
+ 3. compgen -f "$word" from the command line
+ They all need to be handled.
+
+ In the first two cases, readline will run the filename dequoting
+ function in rl_filename_completion_function if it found a filename
+ quoting character in the word to be completed
+ (rl_completion_found_quote). We run the dequoting function here
+ if we're running compgen, we're not completing, and the
+ rl_filename_completion_function won't dequote the filename
+ (rl_completion_found_quote == 0). */
+ iscompgen = this_shell_builtin == compgen_builtin;
+ iscompleting = RL_ISSTATE (RL_STATE_COMPLETING);
+ if (iscompgen && iscompleting == 0 && rl_completion_found_quote == 0
+ && rl_filename_dequoting_function)
{
- qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
- dfn = (*rl_filename_dequoting_function) (text, qc);
+ /* Use rl_completion_quote_character because any single or
+ double quotes have been removed by the time TEXT makes it
+ here, and we don't want to remove backslashes inside
+ quoted strings. */
+ dfn = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character);
}
else
dfn = savestring (text);
}
- return (filename_completion_function (dfn, state));
+ return (rl_filename_completion_function (dfn, state));
}
#define GEN_COMPS(bmap, flag, it, text, glist, tlist) \
if (bmap & flag) \
{ \
tlist = gen_matches_from_itemlist (it, text); \
- glist = append_stringlist (glist, tlist); \
- free_stringlist (tlist); \
+ if (tlist) \
+ { \
+ glist = strlist_append (glist, tlist); \
+ strlist_dispose (tlist); \
+ } \
} \
} while (0)
do { \
if (bmap & flag) \
{ \
- cmatches = completion_matches (text, func); \
+ cmatches = rl_completion_matches (text, func); \
tlist = completions_to_stringlist (cmatches); \
- glist = append_stringlist (glist, tlist); \
- free_array (cmatches); \
- free_stringlist (tlist); \
+ glist = strlist_append (glist, tlist); \
+ strvec_dispose (cmatches); \
+ strlist_dispose (tlist); \
} \
} while (0)
static STRINGLIST *
gen_action_completions (cs, text)
COMPSPEC *cs;
- char *text;
+ const char *text;
{
STRINGLIST *ret, *tmatches;
- char **cmatches; /* from completion_matches ... */
+ char **cmatches; /* from rl_completion_matches ... */
unsigned long flags;
+ int t;
ret = tmatches = (STRINGLIST *)NULL;
flags = cs->actions;
GEN_COMPS (flags, CA_ENABLED, &it_enabled, text, ret, tmatches);
GEN_COMPS (flags, CA_EXPORT, &it_exports, text, ret, tmatches);
GEN_COMPS (flags, CA_FUNCTION, &it_functions, text, ret, tmatches);
+ GEN_COMPS (flags, CA_HELPTOPIC, &it_helptopics, text, ret, tmatches);
GEN_COMPS (flags, CA_HOSTNAME, &it_hostnames, text, ret, tmatches);
GEN_COMPS (flags, CA_JOB, &it_jobs, text, ret, tmatches);
GEN_COMPS (flags, CA_KEYWORD, &it_keywords, text, ret, tmatches);
GEN_XCOMPS(flags, CA_COMMAND, text, command_word_completion_function, cmatches, ret, tmatches);
GEN_XCOMPS(flags, CA_FILE, text, pcomp_filename_completion_function, cmatches, ret, tmatches);
- GEN_XCOMPS(flags, CA_USER, text, username_completion_function, cmatches, ret, tmatches);
+ GEN_XCOMPS(flags, CA_USER, text, rl_username_completion_function, cmatches, ret, tmatches);
+ GEN_XCOMPS(flags, CA_GROUP, text, bash_groupname_completion_function, cmatches, ret, tmatches);
+ GEN_XCOMPS(flags, CA_SERVICE, text, bash_servicename_completion_function, cmatches, ret, tmatches);
/* And lastly, the special case for directories */
if (flags & CA_DIRECTORY)
{
+ t = rl_filename_completion_desired;
+ rl_completion_mark_symlink_dirs = 1; /* override user preference */
cmatches = bash_directory_completion_matches (text);
+ /* If we did not want filename completion before this, and there are
+ no matches, turn off rl_filename_completion_desired so whatever
+ matches we get are not treated as filenames (it gets turned on by
+ rl_filename_completion_function unconditionally). */
+ if (t == 0 && cmatches == 0 && rl_filename_completion_desired == 1)
+ rl_filename_completion_desired = 0;
tmatches = completions_to_stringlist (cmatches);
- ret = append_stringlist (ret, tmatches);
- free_array (cmatches);
- free_stringlist (tmatches);
+ ret = strlist_append (ret, tmatches);
+ strvec_dispose (cmatches);
+ strlist_dispose (tmatches);
}
return ret;
static STRINGLIST *
gen_globpat_matches (cs, text)
COMPSPEC *cs;
- char *text;
+ const char *text;
{
STRINGLIST *sl;
- char *t;
- sl = alloc_stringlist (0);
- sl->list = glob_filename (cs->globpat);
+ sl = strlist_create (0);
+ sl->list = glob_filename (cs->globpat, 0);
if (GLOB_FAILED (sl->list))
sl->list = (char **)NULL;
if (sl->list)
- sl->list_len = sl->list_size = array_len (sl->list);
+ sl->list_len = sl->list_size = strvec_len (sl->list);
return sl;
}
static STRINGLIST *
gen_wordlist_matches (cs, text)
COMPSPEC *cs;
- char *text;
+ const char *text;
{
WORD_LIST *l, *l2;
STRINGLIST *sl;
int nw, tlen;
+ char *ntxt; /* dequoted TEXT to use in comparisons */
if (cs->words == 0 || cs->words[0] == '\0')
return ((STRINGLIST *)NULL);
/* This used to be a simple expand_string(cs->words, 0), but that won't
do -- there's no way to split a simple list into individual words
that way, since the shell semantics say that word splitting is done
- only on the results of expansion. */
- l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, (int *)NULL, (int *)NULL);
+ only on the results of expansion. split_at_delims also handles embedded
+ quoted strings and preserves the quotes for the expand_words_shellexp
+ function call that follows. */
+ /* XXX - this is where this function spends most of its time */
+ l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, 0, (int *)NULL, (int *)NULL);
if (l == 0)
return ((STRINGLIST *)NULL);
/* This will jump back to the top level if the expansion fails... */
dispose_words (l);
nw = list_length (l2);
- sl = alloc_stringlist (nw + 1);
- tlen = STRLEN (text);
+ sl = strlist_create (nw + 1);
+
+ ntxt = bash_dequote_text (text);
+ tlen = STRLEN (ntxt);
for (nw = 0, l = l2; l; l = l->next)
{
- if (tlen == 0 || STREQN (l->word->word, text, tlen))
- sl->list[nw++] = STRDUP (l->word->word);
+ if (tlen == 0 || STREQN (l->word->word, ntxt, tlen))
+ sl->list[nw++] = STRDUP (l->word->word);
}
sl->list[sl->list_len = nw] = (char *)NULL;
+ dispose_words (l2);
+ FREE (ntxt);
return sl;
}
VUNSETATTR (v, att_readonly);
if (array_p (v) == 0)
v = convert_var_to_array (v);
- v = assign_array_var_from_word_list (v, lwords);
+ v = assign_array_var_from_word_list (v, lwords, 0);
+
+ VUNSETATTR (v, att_invisible);
return v;
}
#endif /* ARRAY_VARS */
WORD_LIST *lwords;
int cw, exported;
{
- char ibuf[32];
+ char ibuf[INT_STRLEN_BOUND(int) + 1];
char *value;
SHELL_VAR *v;
+ size_t llen;
+ int c;
/* Set the variables that the function expects while it executes. Maybe
these should be in the function environment (temporary_env). */
- v = bind_variable ("COMP_LINE", line);
+ v = bind_variable ("COMP_LINE", line, 0);
if (v && exported)
VSETATTR(v, att_exported);
- value = inttostr (ind, ibuf, 32);
+ /* Post bash-4.2: COMP_POINT is characters instead of bytes. */
+ c = line[ind];
+ line[ind] = '\0';
+ llen = MB_STRLEN (line);
+ line[ind] = c;
+ value = inttostr (llen, ibuf, sizeof(ibuf));
v = bind_int_variable ("COMP_POINT", value);
if (v && exported)
VSETATTR(v, att_exported);
+ value = inttostr (rl_completion_type, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("COMP_TYPE", value);
+ if (v && exported)
+ VSETATTR(v, att_exported);
+
+ value = inttostr (rl_completion_invoking_key, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("COMP_KEY", value);
+ if (v && exported)
+ VSETATTR(v, att_exported);
+
/* Since array variables can't be exported, we don't bother making the
array of words. */
if (exported == 0)
{
#ifdef ARRAY_VARS
v = bind_comp_words (lwords);
- value = inttostr (cw, ibuf, 32);
+ value = inttostr (cw, ibuf, sizeof(ibuf));
bind_int_variable ("COMP_CWORD", value);
#endif
}
unbind_compfunc_variables (exported)
int exported;
{
- makunbound ("COMP_LINE", shell_variables);
- makunbound ("COMP_POINT", shell_variables);
+ unbind_variable ("COMP_LINE");
+ unbind_variable ("COMP_POINT");
+ unbind_variable ("COMP_TYPE");
+ unbind_variable ("COMP_KEY");
#ifdef ARRAY_VARS
- makunbound ("COMP_WORDS", shell_variables);
- makunbound ("COMP_CWORD", shell_variables);
+ unbind_variable ("COMP_WORDS");
+ unbind_variable ("COMP_CWORD");
#endif
if (exported)
array_needs_making = 1;
$0 == function or command being invoked
$1 == command name
- $2 = word to be completed (possibly null)
- $3 = previous word
+ $2 == word to be completed (possibly null)
+ $3 == previous word
Functions can access all of the words in the current command line
- with the COMP_WORDS array. External commands cannot. */
+ with the COMP_WORDS array. External commands cannot; they have to
+ make do with the COMP_LINE and COMP_POINT variables. */
static WORD_LIST *
-build_arg_list (cmd, text, lwords, ind)
+build_arg_list (cmd, cname, text, lwords, ind)
char *cmd;
- char *text;
+ const char *cname;
+ const char *text;
WORD_LIST *lwords;
int ind;
{
ret = (WORD_LIST *)NULL;
w = make_word (cmd);
- ret = make_word_list (w, (WORD_LIST *)NULL);
+ ret = make_word_list (w, (WORD_LIST *)NULL); /* $0 */
- w = (lwords && lwords->word) ? copy_word (lwords->word) : make_word ("");
+ w = make_word (cname); /* $1 */
cl = ret->next = make_word_list (w, (WORD_LIST *)NULL);
w = make_word (text);
- cl->next = make_word_list (w, (WORD_LIST *)NULL);
+ cl->next = make_word_list (w, (WORD_LIST *)NULL); /* $2 */
cl = cl->next;
/* Search lwords for current word */
variable, this does nothing if arrays are not compiled into the shell. */
static STRINGLIST *
-gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw)
+gen_shell_function_matches (cs, cmd, text, line, ind, lwords, nw, cw, foundp)
COMPSPEC *cs;
- char *text, *line;
+ const char *cmd;
+ const char *text;
+ char *line;
int ind;
WORD_LIST *lwords;
int nw, cw;
+ int *foundp;
{
char *funcname;
STRINGLIST *sl;
SHELL_VAR *f, *v;
WORD_LIST *cmdlist;
- int fval;
+ int fval, found;
+ sh_parser_state_t ps;
+ sh_parser_state_t * restrict pps;
#if defined (ARRAY_VARS)
ARRAY *a;
#endif
+ found = 0;
+ if (foundp)
+ *foundp = found;
+
funcname = cs->funcname;
f = find_function (funcname);
if (f == 0)
{
- fprintf (stderr, "gen_shell_function_matches: function `%s' not found\n", funcname);
- ding ();
+ internal_error (_("completion: function `%s' not found"), funcname);
+ rl_ding ();
rl_on_new_line ();
return ((STRINGLIST *)NULL);
}
1-based, while bash arrays are 0-based. */
bind_compfunc_variables (line, ind, lwords, cw - 1, 0);
- cmdlist = build_arg_list (funcname, text, lwords, cw);
-
+ cmdlist = build_arg_list (funcname, cmd, text, lwords, cw);
+
+ pps = &ps;
+ save_parser_state (pps);
+ begin_unwind_frame ("gen-shell-function-matches");
+ add_unwind_protect (restore_parser_state, (char *)pps);
+ add_unwind_protect (dispose_words, (char *)cmdlist);
+ add_unwind_protect (unbind_compfunc_variables, (char *)0);
+
fval = execute_shell_function (f, cmdlist);
+ discard_unwind_frame ("gen-shell-function-matches");
+ restore_parser_state (pps);
+
+ found = fval != EX_NOTFOUND;
+ if (fval == EX_RETRYFAIL)
+ found |= PCOMP_RETRYFAIL;
+ if (foundp)
+ *foundp = found;
+
/* Now clean up and destroy everything. */
dispose_words (cmdlist);
unbind_compfunc_variables (0);
if (array_p (v) == 0)
v = convert_var_to_array (v);
+ VUNSETATTR (v, att_invisible);
+
a = array_cell (v);
- if (a == 0 || array_empty (a))
+ if (found == 0 || (found & PCOMP_RETRYFAIL) || a == 0 || array_empty (a))
sl = (STRINGLIST *)NULL;
else
{
/* XXX - should we filter the list of completions so only those matching
TEXT are returned? Right now, we do not. */
- sl = alloc_stringlist (0);
+ sl = strlist_create (0);
sl->list = array_to_argv (a);
sl->list_len = sl->list_size = array_num_elements (a);
}
/* XXX - should we unbind COMPREPLY here? */
- makunbound ("COMPREPLY", shell_variables);
+ unbind_variable ("COMPREPLY");
return (sl);
#endif
STRINGLIST from the results and return it. */
static STRINGLIST *
-gen_command_matches (cs, text, line, ind, lwords, nw, cw)
+gen_command_matches (cs, cmd, text, line, ind, lwords, nw, cw)
COMPSPEC *cs;
- char *text, *line;
+ const char *cmd;
+ const char *text;
+ char *line;
int ind;
WORD_LIST *lwords;
int nw, cw;
char *csbuf, *cscmd, *t;
int cmdlen, cmdsize, n, ws, we;
WORD_LIST *cmdlist, *cl;
+ WORD_DESC *tw;
STRINGLIST *sl;
bind_compfunc_variables (line, ind, lwords, cw, 1);
- cmdlist = build_arg_list (cs->command, text, lwords, cw);
+ cmdlist = build_arg_list (cs->command, cmd, text, lwords, cw);
/* Estimate the size needed for the buffer. */
n = strlen (cs->command);
cmdsize += 2;
/* allocate the string for the command and fill it in. */
- cscmd = xmalloc (cmdsize + 1);
+ cscmd = (char *)xmalloc (cmdsize + 1);
strcpy (cscmd, cs->command); /* $0 */
cmdlen = n;
cscmd[cmdlen++] = ' ';
for (cl = cmdlist->next; cl; cl = cl->next) /* $1, $2, $3, ... */
{
- t = single_quote (cl->word->word ? cl->word->word : "");
+ t = sh_single_quote (cl->word->word ? cl->word->word : "");
n = strlen (t);
RESIZE_MALLOCED_BUFFER (cscmd, cmdlen, n + 2, cmdsize, 64);
strcpy (cscmd + cmdlen, t);
}
cscmd[cmdlen] = '\0';
- csbuf = command_substitute (cscmd, 0);
+ tw = command_substitute (cscmd, 0);
+ csbuf = tw ? tw->word : (char *)NULL;
+ if (tw)
+ dispose_word_desc (tw);
/* Now clean up and destroy everything. */
dispose_words (cmdlist);
/* Now break CSBUF up at newlines, with backslash allowed to escape a
newline, and put the individual words into a STRINGLIST. */
- sl = alloc_stringlist (16);
+ sl = strlist_create (16);
for (ws = 0; csbuf[ws]; )
{
we = ws;
}
t = substring (csbuf, ws, we);
if (sl->list_len >= sl->list_size - 1)
- realloc_stringlist (sl, sl->list_size + 16);
+ strlist_resize (sl, sl->list_size + 16);
sl->list[sl->list_len++] = t;
while (csbuf[we] == '\n') we++;
ws = we;
WORD_LIST *ret;
char *delims;
+#if 0
delims = "()<>;&| \t\n"; /* shell metacharacters break words */
- ret = split_at_delims (line, llen, delims, sentinel, nwp, cwp);
+#else
+ delims = rl_completer_word_break_characters;
+#endif
+ ret = split_at_delims (line, llen, delims, sentinel, SD_NOQUOTEDELIM, nwp, cwp);
return (ret);
}
/* Evaluate COMPSPEC *cs and return all matches for WORD. */
STRINGLIST *
-gen_compspec_completions (cs, cmd, word, start, end)
+gen_compspec_completions (cs, cmd, word, start, end, foundp)
COMPSPEC *cs;
- char *cmd;
- char *word;
+ const char *cmd;
+ const char *word;
int start, end;
+ int *foundp;
{
STRINGLIST *ret, *tmatches;
- char *line, *lword;
- int llen, nw, cw;
+ char *line;
+ int llen, nw, cw, found, foundf;
WORD_LIST *lwords;
+ WORD_DESC *lw;
+ COMPSPEC *tcs;
- debug_printf ("programmable_completions (%s, %s, %d, %d)", cmd, word, start, end);
- debug_printf ("programmable_completions: %s -> 0x%x", cmd, (int)cs);
+ found = 1;
+
+#ifdef DEBUG
+ debug_printf ("gen_compspec_completions (%s, %s, %d, %d)", cmd, word, start, end);
+ debug_printf ("gen_compspec_completions: %s -> %p", cmd, cs);
+#endif
ret = gen_action_completions (cs, word);
+#ifdef DEBUG
if (ret && progcomp_debug)
{
- debug_printf ("gen_action_completions (0x%x, %s) -->", (int)cs, word);
- print_stringlist (ret, "\t");
+ debug_printf ("gen_action_completions (%p, %s) -->", cs, word);
+ strlist_print (ret, "\t");
rl_on_new_line ();
}
+#endif
/* Now we start generating completions based on the other members of CS. */
if (cs->globpat)
tmatches = gen_globpat_matches (cs, word);
if (tmatches)
{
+#ifdef DEBUG
if (progcomp_debug)
{
- debug_printf ("gen_globpat_matches (0x%x, %s) -->", (int)cs, word);
- print_stringlist (tmatches, "\t");
+ debug_printf ("gen_globpat_matches (%p, %s) -->", cs, word);
+ strlist_print (tmatches, "\t");
rl_on_new_line ();
}
- ret = append_stringlist (ret, tmatches);
- free_stringlist (tmatches);
+#endif
+ ret = strlist_append (ret, tmatches);
+ strlist_dispose (tmatches);
rl_filename_completion_desired = 1;
}
}
tmatches = gen_wordlist_matches (cs, word);
if (tmatches)
{
+#ifdef DEBUG
if (progcomp_debug)
{
- debug_printf ("gen_wordlist_matches (0x%x, %s) -->", (int)cs, word);
- print_stringlist (tmatches, "\t");
+ debug_printf ("gen_wordlist_matches (%p, %s) -->", cs, word);
+ strlist_print (tmatches, "\t");
rl_on_new_line ();
}
- ret = append_stringlist (ret, tmatches);
- free_stringlist (tmatches);
+#endif
+ ret = strlist_append (ret, tmatches);
+ strlist_dispose (tmatches);
}
}
line = substring (rl_line_buffer, start, end);
llen = end - start;
- debug_printf ("command_line_to_word_list (%s, %d, %d, 0x%x, 0x%x)",
+#ifdef DEBUG
+ debug_printf ("command_line_to_word_list (%s, %d, %d, %p, %p)",
line, llen, rl_point - start, &nw, &cw);
+#endif
lwords = command_line_to_word_list (line, llen, rl_point - start, &nw, &cw);
+ /* If we skipped a NULL word at the beginning of the line, add it back */
+ if (lwords && lwords->word && cmd[0] == 0 && lwords->word->word[0] != 0)
+ {
+ lw = make_bare_word (cmd);
+ lwords = make_word_list (lw, lwords);
+ nw++;
+ cw++;
+ }
+#ifdef DEBUG
if (lwords == 0 && llen > 0)
debug_printf ("ERROR: command_line_to_word_list returns NULL");
else if (progcomp_debug)
fflush(stdout);
rl_on_new_line ();
}
+#endif
}
if (cs->funcname)
{
- tmatches = gen_shell_function_matches (cs, word, line, rl_point - start, lwords, nw, cw);
+ foundf = 0;
+ tmatches = gen_shell_function_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw, &foundf);
+ if (foundf != 0)
+ found = foundf;
if (tmatches)
{
+#ifdef DEBUG
if (progcomp_debug)
{
- debug_printf ("gen_shell_function_matches (0x%x, %s, 0x%x, %d, %d) -->", (int)cs, word, lwords, nw, cw);
- print_stringlist (tmatches, "\t");
+ debug_printf ("gen_shell_function_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw);
+ strlist_print (tmatches, "\t");
rl_on_new_line ();
}
- ret = append_stringlist (ret, tmatches);
- free_stringlist (tmatches);
+#endif
+ ret = strlist_append (ret, tmatches);
+ strlist_dispose (tmatches);
}
}
if (cs->command)
{
- tmatches = gen_command_matches (cs, word, line, rl_point - start, lwords, nw, cw);
+ tmatches = gen_command_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw);
if (tmatches)
{
+#ifdef DEBUG
if (progcomp_debug)
{
- debug_printf ("gen_command_matches (0x%x, %s, 0x%x, %d, %d) -->", (int)cs, word, lwords, nw, cw);
- print_stringlist (tmatches, "\t");
+ debug_printf ("gen_command_matches (%p, %s, %s, %p, %d, %d) -->", cs, cmd, word, lwords, nw, cw);
+ strlist_print (tmatches, "\t");
rl_on_new_line ();
}
- ret = append_stringlist (ret, tmatches);
- free_stringlist (tmatches);
+#endif
+ ret = strlist_append (ret, tmatches);
+ strlist_dispose (tmatches);
}
}
FREE (line);
}
+ if (foundp)
+ *foundp = found;
+
+ if (found == 0 || (found & PCOMP_RETRYFAIL))
+ {
+ strlist_dispose (ret);
+ return NULL;
+ }
+
if (cs->filterpat)
{
tmatches = filter_stringlist (ret, cs->filterpat, word);
+#ifdef DEBUG
if (progcomp_debug)
{
- debug_printf ("filter_stringlist (0x%x, %s, %s) -->", ret, cs->filterpat, word);
- print_stringlist (tmatches, "\t");
+ debug_printf ("filter_stringlist (%p, %s, %s) -->", ret, cs->filterpat, word);
+ strlist_print (tmatches, "\t");
rl_on_new_line ();
}
+#endif
if (ret && ret != tmatches)
{
FREE (ret->list);
}
if (cs->prefix || cs->suffix)
- ret = prefix_suffix_stringlist (ret, cs->prefix, cs->suffix);
+ ret = strlist_prefix_suffix (ret, cs->prefix, cs->suffix);
+
+ /* If no matches have been generated and the user has specified that
+ directory completion should be done as a default, call
+ gen_action_completions again to generate a list of matching directory
+ names. */
+ if ((ret == 0 || ret->list_len == 0) && (cs->options & COPT_DIRNAMES))
+ {
+ tcs = compspec_create ();
+ tcs->actions = CA_DIRECTORY;
+ FREE (ret);
+ ret = gen_action_completions (tcs, word);
+ compspec_dispose (tcs);
+ }
+ else if (cs->options & COPT_PLUSDIRS)
+ {
+ tcs = compspec_create ();
+ tcs->actions = CA_DIRECTORY;
+ tmatches = gen_action_completions (tcs, word);
+ ret = strlist_append (ret, tmatches);
+ strlist_dispose (tmatches);
+ compspec_dispose (tcs);
+ }
return (ret);
}
+void
+pcomp_set_readline_variables (flags, nval)
+ int flags, nval;
+{
+ /* If the user specified that the compspec returns filenames, make
+ sure that readline knows it. */
+ if (flags & COPT_FILENAMES)
+ rl_filename_completion_desired = nval;
+ /* If the user doesn't want a space appended, tell readline. */
+ if (flags & COPT_NOSPACE)
+ rl_completion_suppress_append = nval;
+ /* The value here is inverted, since the default is on and the `noquote'
+ option is supposed to turn it off */
+ if (flags & COPT_NOQUOTE)
+ rl_filename_quoting_desired = 1 - nval;
+}
+
+/* Set or unset FLAGS in the options word of the current compspec.
+ SET_OR_UNSET is 1 for setting, 0 for unsetting. */
+void
+pcomp_set_compspec_options (cs, flags, set_or_unset)
+ COMPSPEC *cs;
+ int flags, set_or_unset;
+{
+ if (cs == 0 && ((cs = pcomp_curcs) == 0))
+ return;
+ if (set_or_unset)
+ cs->options |= flags;
+ else
+ cs->options &= ~flags;
+}
+
+static STRINGLIST *
+gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs)
+ const char *ocmd;
+ const char *cmd;
+ const char *word;
+ int start, end;
+ int *foundp, *retryp;
+ COMPSPEC **lastcs;
+{
+ COMPSPEC *cs, *oldcs;
+ const char *oldcmd;
+ STRINGLIST *ret;
+
+ cs = progcomp_search (ocmd);
+
+ if (cs == 0 || cs == *lastcs)
+ {
+#if 0
+ if (foundp)
+ *foundp = 0;
+#endif
+ return (NULL);
+ }
+
+ if (*lastcs)
+ compspec_dispose (*lastcs);
+ cs->refcount++; /* XXX */
+ *lastcs = cs;
+
+ cs = compspec_copy (cs);
+
+ oldcs = pcomp_curcs;
+ oldcmd = pcomp_curcmd;
+
+ pcomp_curcs = cs;
+ pcomp_curcmd = cmd;
+
+ ret = gen_compspec_completions (cs, cmd, word, start, end, foundp);
+
+ pcomp_curcs = oldcs;
+ pcomp_curcmd = oldcmd;
+
+ /* We need to conditionally handle setting *retryp here */
+ if (retryp)
+ *retryp = foundp && (*foundp & PCOMP_RETRYFAIL);
+
+ if (foundp)
+ {
+ *foundp &= ~PCOMP_RETRYFAIL;
+ *foundp |= cs->options;
+ }
+
+ compspec_dispose (cs);
+ return ret;
+}
+
/* The driver function for the programmable completion code. Returns a list
of matches for WORD, which is an argument to command CMD. START and END
bound the command currently being completed in rl_line_buffer. */
char **
programmable_completions (cmd, word, start, end, foundp)
- char *cmd, *word;
+ const char *cmd;
+ const char *word;
int start, end, *foundp;
{
- COMPSPEC *cs;
+ COMPSPEC *cs, *lastcs;
STRINGLIST *ret;
char **rmatches, *t;
+ int found, retry, count;
- /* We look at the basename of CMD if the full command does not have
- an associated COMPSPEC. */
- cs = find_compspec (cmd);
- if (cs == 0)
- {
- t = strrchr (cmd, '/');
- if (t)
- cs = find_compspec (++t);
- }
- if (cs == 0)
+ lastcs = 0;
+ found = count = 0;
+
+ do
{
- if (foundp)
- *foundp = 0;
- return ((char **)NULL);
- }
+ retry = 0;
- /* Signal the caller that we found a COMPSPEC for this command. */
- if (foundp)
- *foundp = 1;
+ /* We look at the basename of CMD if the full command does not have
+ an associated COMPSPEC. */
+ ret = gen_progcomp_completions (cmd, cmd, word, start, end, &found, &retry, &lastcs);
+ if (found == 0)
+ {
+ t = strrchr (cmd, '/');
+ if (t && *(++t))
+ ret = gen_progcomp_completions (t, cmd, word, start, end, &found, &retry, &lastcs);
+ }
+
+ if (found == 0)
+ ret = gen_progcomp_completions (DEFAULTCMD, cmd, word, start, end, &found, &retry, &lastcs);
- ret = gen_compspec_completions (cs, cmd, word, start, end);
+ count++;
+
+ if (count > 32)
+ {
+ internal_warning ("programmable_completion: %s: possible retry loop", cmd);
+ break;
+ }
+ }
+ while (retry);
if (ret)
{
else
rmatches = (char **)NULL;
+ if (foundp)
+ *foundp = found;
+
+ if (lastcs) /* XXX - should be while? */
+ compspec_dispose (lastcs);
+
return (rmatches);
}