diff options
author | Chet Ramey <chet.ramey@case.edu> | 2014-02-26 09:36:43 -0500 |
---|---|---|
committer | Chet Ramey <chet.ramey@case.edu> | 2014-02-26 09:36:43 -0500 |
commit | ac50fbac377e32b98d2de396f016ea81e8ee9961 (patch) | |
tree | f71882366b98fedf1a88a063103219a4935de926 /pcomplete.c | |
parent | 4539d736f1aff232857a854fd2a68df0c98d9f34 (diff) | |
download | bash-ac50fbac377e32b98d2de396f016ea81e8ee9961.tar.gz |
Bash-4.3 distribution sources and documentationbash-4.3
Diffstat (limited to 'pcomplete.c')
-rw-r--r-- | pcomplete.c | 138 |
1 files changed, 112 insertions, 26 deletions
diff --git a/pcomplete.c b/pcomplete.c index e4f20590..a3327ed5 100644 --- a/pcomplete.c +++ b/pcomplete.c @@ -1,6 +1,6 @@ /* pcomplete.c - functions to generate lists of matches for programmable completion. */ -/* Copyright (C) 1999-2009 Free Software Foundation, Inc. +/* Copyright (C) 1999-2012 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -37,6 +37,8 @@ # include <varargs.h> #endif +#include <sys/time.h> + #include <stdio.h> #include "bashansi.h" #include "bashintl.h" @@ -56,8 +58,11 @@ # include "trap.h" #endif +#include "shmbutil.h" + #include "builtins.h" #include "builtins/common.h" +#include "builtins/builtext.h" #include <glob/glob.h> #include <glob/strmatch.h> @@ -82,6 +87,7 @@ extern char *strpbrk __P((char *, char *)); 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) @@ -99,6 +105,7 @@ 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 *)); @@ -119,10 +126,13 @@ 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 *, char *, - int, WORD_LIST *, 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 *, @@ -136,7 +146,7 @@ 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 *, WORD_LIST *, 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 @@ -157,6 +167,7 @@ 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_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_groups = { LIST_DYNAMIC }; /* unused */ ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 }; @@ -368,6 +379,7 @@ it_init_aliases (itp) #else itp->slist = (STRINGLIST *)NULL; #endif + free (alias_list); return 1; } @@ -497,6 +509,24 @@ it_init_functions (itp) 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; @@ -693,16 +723,29 @@ pcomp_filename_completion_function (text, state) { static char *dfn; /* dequoted filename */ int qc; + int iscompgen, iscompleting; if (state == 0) { FREE (dfn); /* remove backslashes quoting special characters in filenames. */ -#if 1 - if (RL_ISSTATE (RL_STATE_COMPLETING) && rl_filename_dequoting_function) -#else - if (rl_filename_dequoting_function) -#endif + /* 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) { /* Use rl_completion_quote_character because any single or double quotes have been removed by the time TEXT makes it @@ -752,6 +795,7 @@ gen_action_completions (cs, text) STRINGLIST *ret, *tmatches; char **cmatches; /* from rl_completion_matches ... */ unsigned long flags; + int t; ret = tmatches = (STRINGLIST *)NULL; flags = cs->actions; @@ -764,6 +808,7 @@ gen_action_completions (cs, text) 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); @@ -783,8 +828,15 @@ gen_action_completions (cs, text) /* 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 = strlist_append (ret, tmatches); strvec_dispose (cmatches); @@ -833,7 +885,10 @@ gen_wordlist_matches (cs, text) /* 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. */ + 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); @@ -891,6 +946,8 @@ bind_compfunc_variables (line, ind, lwords, cw, exported) 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). */ @@ -898,7 +955,12 @@ bind_compfunc_variables (line, ind, lwords, cw, exported) if (v && exported) VSETATTR(v, att_exported); - value = inttostr (ind, ibuf, sizeof(ibuf)); + /* 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); @@ -953,11 +1015,12 @@ unbind_compfunc_variables (exported) Functions can access all of the words in the current command line with the COMP_WORDS array. External commands cannot; they have to - make do with the COMP_LINE and COMP_POINT variables. */ + 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; + const char *cname; const char *text; WORD_LIST *lwords; int ind; @@ -968,13 +1031,13 @@ build_arg_list (cmd, text, lwords, 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 */ @@ -999,8 +1062,9 @@ build_arg_list (cmd, text, lwords, ind) 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, foundp) +gen_shell_function_matches (cs, cmd, text, line, ind, lwords, nw, cw, foundp) COMPSPEC *cs; + const char *cmd; const char *text; char *line; int ind; @@ -1041,7 +1105,7 @@ gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw, foundp) 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); @@ -1103,8 +1167,9 @@ gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw, foundp) 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; + const char *cmd; const char *text; char *line; int ind; @@ -1118,7 +1183,7 @@ gen_command_matches (cs, text, line, ind, lwords, nw, cw) 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); @@ -1148,7 +1213,8 @@ gen_command_matches (cs, text, line, ind, lwords, nw, cw) tw = command_substitute (cscmd, 0); csbuf = tw ? tw->word : (char *)NULL; - dispose_word_desc (tw); + if (tw) + dispose_word_desc (tw); /* Now clean up and destroy everything. */ dispose_words (cmdlist); @@ -1217,6 +1283,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) char *line; int llen, nw, cw, found, foundf; WORD_LIST *lwords; + WORD_DESC *lw; COMPSPEC *tcs; found = 1; @@ -1288,6 +1355,14 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) 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"); @@ -1306,7 +1381,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) if (cs->funcname) { foundf = 0; - tmatches = gen_shell_function_matches (cs, word, line, rl_point - start, lwords, nw, cw, &foundf); + tmatches = gen_shell_function_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw, &foundf); if (foundf != 0) found = foundf; if (tmatches) @@ -1314,7 +1389,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) #ifdef DEBUG if (progcomp_debug) { - debug_printf ("gen_shell_function_matches (%p, %s, %p, %d, %d) -->", cs, word, lwords, nw, cw); + 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 (); } @@ -1326,13 +1401,13 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) 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 (%p, %s, %p, %d, %d) -->", cs, word, lwords, nw, cw); + 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 (); } @@ -1388,6 +1463,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp) { tcs = compspec_create (); tcs->actions = CA_DIRECTORY; + FREE (ret); ret = gen_action_completions (tcs, word); compspec_dispose (tcs); } @@ -1415,6 +1491,10 @@ pcomp_set_readline_variables (flags, 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. @@ -1448,7 +1528,13 @@ gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs) cs = progcomp_search (ocmd); if (cs == 0 || cs == *lastcs) - return (NULL); + { +#if 0 + if (foundp) + *foundp = 0; +#endif + return (NULL); + } if (*lastcs) compspec_dispose (*lastcs); |