Bash-4.3 distribution sources and documentation
This commit is contained in:
530
bashline.c
530
bashline.c
@@ -1,6 +1,6 @@
|
||||
/* bashline.c -- Bash's interface to the readline library. */
|
||||
|
||||
/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include "chartypes.h"
|
||||
#include "bashansi.h"
|
||||
@@ -51,6 +53,7 @@
|
||||
#include "findcmd.h"
|
||||
#include "pathexp.h"
|
||||
#include "shmbutil.h"
|
||||
#include "trap.h"
|
||||
|
||||
#include "builtins/common.h"
|
||||
|
||||
@@ -83,7 +86,11 @@ extern int bash_brace_completion __P((int, int));
|
||||
#endif /* BRACE_COMPLETION */
|
||||
|
||||
/* To avoid including curses.h/term.h/termcap.h and that whole mess. */
|
||||
#ifdef _MINIX
|
||||
extern int tputs __P((const char *string, int nlines, void (*outx)(int)));
|
||||
#else
|
||||
extern int tputs __P((const char *string, int nlines, int (*outx)(int)));
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
@@ -114,15 +121,21 @@ static int bash_backward_kill_shellword __P((int, int));
|
||||
|
||||
/* Helper functions for Readline. */
|
||||
static char *restore_tilde __P((char *, char *));
|
||||
static char *maybe_restore_tilde __P((char *, char *));
|
||||
|
||||
static char *bash_filename_rewrite_hook __P((char *, int));
|
||||
|
||||
static void bash_directory_expansion __P((char **));
|
||||
static int bash_filename_stat_hook __P((char **));
|
||||
static int bash_command_name_stat_hook __P((char **));
|
||||
static int bash_directory_completion_hook __P((char **));
|
||||
static int filename_completion_ignore __P((char **));
|
||||
static int bash_push_line __P((void));
|
||||
|
||||
static int executable_completion __P((const char *, int));
|
||||
|
||||
static rl_icppfunc_t *save_directory_hook __P((void));
|
||||
static void reset_directory_hook __P((rl_icppfunc_t *));
|
||||
static void restore_directory_hook __P((rl_icppfunc_t));
|
||||
|
||||
static void cleanup_expansion_error __P((void));
|
||||
static void maybe_make_readline_line __P((char *));
|
||||
@@ -151,9 +164,14 @@ static int return_zero __P((const char *));
|
||||
|
||||
static char *bash_dequote_filename __P((char *, int));
|
||||
static char *quote_word_break_chars __P((char *));
|
||||
static void set_filename_bstab __P((const char *));
|
||||
static char *bash_quote_filename __P((char *, int, char *));
|
||||
|
||||
#ifdef _MINIX
|
||||
static void putx __P((int));
|
||||
#else
|
||||
static int putx __P((int));
|
||||
#endif
|
||||
static int bash_execute_unix_command __P((int, int));
|
||||
static void init_unix_command_map __P((void));
|
||||
static int isolate_sequence __P((char *, int, int, int *));
|
||||
@@ -164,10 +182,12 @@ static int set_saved_history __P((void));
|
||||
static int posix_edit_macros __P((int, int));
|
||||
#endif
|
||||
|
||||
static int bash_event_hook __P((void));
|
||||
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
static int find_cmd_start __P((int));
|
||||
static int find_cmd_end __P((int));
|
||||
static char *find_cmd_name __P((int));
|
||||
static char *find_cmd_name __P((int, int *, int *));
|
||||
static char *prog_complete_return __P((const char *, int));
|
||||
|
||||
static char **prog_complete_matches;
|
||||
@@ -247,8 +267,20 @@ int force_fignore = 1;
|
||||
int dircomplete_spelling = 0;
|
||||
|
||||
/* Expand directory names during word/filename completion. */
|
||||
#if DIRCOMPLETE_EXPAND_DEFAULT
|
||||
int dircomplete_expand = 1;
|
||||
int dircomplete_expand_relpath = 1;
|
||||
#else
|
||||
int dircomplete_expand = 0;
|
||||
int dircomplete_expand_relpath = 0;
|
||||
#endif
|
||||
|
||||
/* When non-zero, perform `normal' shell quoting on completed filenames
|
||||
even when the completed name contains a directory name with a shell
|
||||
variable referene, so dollar signs in a filename get quoted appropriately.
|
||||
Set to zero to remove dollar sign (and braces or parens as needed) from
|
||||
the set of characters that will be quoted. */
|
||||
int complete_fullquote = 1;
|
||||
|
||||
static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
|
||||
static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
|
||||
@@ -256,6 +288,7 @@ static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
|
||||
|
||||
static const char *default_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/
|
||||
static char *custom_filename_quote_characters = 0;
|
||||
static char filename_bstab[256];
|
||||
|
||||
static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
@@ -515,6 +548,8 @@ initialize_readline ()
|
||||
|
||||
rl_filename_rewrite_hook = bash_filename_rewrite_hook;
|
||||
|
||||
rl_filename_stat_hook = bash_filename_stat_hook;
|
||||
|
||||
/* Tell the filename completer we want a chance to ignore some names. */
|
||||
rl_ignore_some_completions_function = filename_completion_ignore;
|
||||
|
||||
@@ -540,6 +575,7 @@ initialize_readline ()
|
||||
|
||||
/* characters that need to be quoted when appearing in filenames. */
|
||||
rl_filename_quote_characters = default_filename_quote_characters;
|
||||
set_filename_bstab (rl_filename_quote_characters);
|
||||
|
||||
rl_filename_quoting_function = bash_quote_filename;
|
||||
rl_filename_dequoting_function = bash_dequote_filename;
|
||||
@@ -563,6 +599,18 @@ bashline_reinitialize ()
|
||||
bash_readline_initialized = 0;
|
||||
}
|
||||
|
||||
void
|
||||
bashline_set_event_hook ()
|
||||
{
|
||||
rl_signal_event_hook = bash_event_hook;
|
||||
}
|
||||
|
||||
void
|
||||
bashline_reset_event_hook ()
|
||||
{
|
||||
rl_signal_event_hook = 0;
|
||||
}
|
||||
|
||||
/* On Sun systems at least, rl_attempted_completion_function can end up
|
||||
getting set to NULL, and rl_completion_entry_function set to do command
|
||||
word completion if Bash is interrupted while trying to complete a command
|
||||
@@ -576,8 +624,12 @@ bashline_reset ()
|
||||
rl_completion_entry_function = NULL;
|
||||
rl_ignore_some_completions_function = filename_completion_ignore;
|
||||
rl_filename_quote_characters = default_filename_quote_characters;
|
||||
set_filename_bstab (rl_filename_quote_characters);
|
||||
|
||||
set_directory_hook ();
|
||||
rl_filename_stat_hook = bash_filename_stat_hook;
|
||||
|
||||
bashline_reset_event_hook ();
|
||||
}
|
||||
|
||||
/* Contains the line to push into readline. */
|
||||
@@ -772,7 +824,7 @@ clear_hostname_list ()
|
||||
}
|
||||
|
||||
/* Return a NULL terminated list of hostnames which begin with TEXT.
|
||||
Initialize the hostname list the first time if neccessary.
|
||||
Initialize the hostname list the first time if necessary.
|
||||
The array is malloc ()'ed, but not the individual strings. */
|
||||
static char **
|
||||
hostnames_matching (text)
|
||||
@@ -823,12 +875,25 @@ hostnames_matching (text)
|
||||
/* The equivalent of the Korn shell C-o operate-and-get-next-history-line
|
||||
editing command. */
|
||||
static int saved_history_line_to_use = -1;
|
||||
static int last_saved_history_line = -1;
|
||||
|
||||
#define HISTORY_FULL() (history_is_stifled () && history_length >= history_max_entries)
|
||||
|
||||
static int
|
||||
set_saved_history ()
|
||||
{
|
||||
/* XXX - compensate for assumption that history was `shuffled' if it was
|
||||
actually not. */
|
||||
if (HISTORY_FULL () &&
|
||||
hist_last_line_added == 0 &&
|
||||
saved_history_line_to_use < history_length - 1)
|
||||
saved_history_line_to_use++;
|
||||
|
||||
if (saved_history_line_to_use >= 0)
|
||||
rl_get_previous_history (history_length - saved_history_line_to_use, 0);
|
||||
{
|
||||
rl_get_previous_history (history_length - saved_history_line_to_use, 0);
|
||||
last_saved_history_line = saved_history_line_to_use;
|
||||
}
|
||||
saved_history_line_to_use = -1;
|
||||
rl_startup_hook = old_rl_startup_hook;
|
||||
return (0);
|
||||
@@ -846,8 +911,7 @@ operate_and_get_next (count, c)
|
||||
/* Find the current line, and find the next line to use. */
|
||||
where = where_history ();
|
||||
|
||||
if ((history_is_stifled () && (history_length >= history_max_entries)) ||
|
||||
(where >= history_length - 1))
|
||||
if (HISTORY_FULL () || (where >= history_length - 1))
|
||||
saved_history_line_to_use = where;
|
||||
else
|
||||
saved_history_line_to_use = where + 1;
|
||||
@@ -895,7 +959,9 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
|
||||
/* This breaks down when using command-oriented history and are not
|
||||
finished with the command, so we should not ignore the last command */
|
||||
using_history ();
|
||||
current_command_line_count++; /* for rl_newline above */
|
||||
bash_add_history (rl_line_buffer);
|
||||
current_command_line_count = 0; /* for dummy history entry */
|
||||
bash_add_history ("");
|
||||
history_lines_this_session++;
|
||||
using_history ();
|
||||
@@ -1192,7 +1258,11 @@ bash_backward_kill_shellword (count, key)
|
||||
|
||||
#define COMMAND_SEPARATORS ";|&{(`"
|
||||
/* )} */
|
||||
#define COMMAND_SEPARATORS_PLUS_WS ";|&{(` \t"
|
||||
/* )} */
|
||||
|
||||
/* check for redirections and other character combinations that are not
|
||||
command separators */
|
||||
static int
|
||||
check_redir (ti)
|
||||
int ti;
|
||||
@@ -1207,8 +1277,19 @@ check_redir (ti)
|
||||
if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
|
||||
(this_char == '|' && prev_char == '>'))
|
||||
return (1);
|
||||
else if ((this_char == '{' && prev_char == '$') || /* } */
|
||||
(char_is_quoted (rl_line_buffer, ti)))
|
||||
else if (this_char == '{' && prev_char == '$') /*}*/
|
||||
return (1);
|
||||
#if 0 /* Not yet */
|
||||
else if (this_char == '(' && prev_char == '$') /*)*/
|
||||
return (1);
|
||||
else if (this_char == '(' && prev_char == '<') /*)*/
|
||||
return (1);
|
||||
#if defined (EXTENDED_GLOB)
|
||||
else if (extended_glob && this_char == '(' && prev_char == '!') /*)*/
|
||||
return (1);
|
||||
#endif
|
||||
#endif
|
||||
else if (char_is_quoted (rl_line_buffer, ti))
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
@@ -1226,7 +1307,10 @@ find_cmd_start (start)
|
||||
register int s, os;
|
||||
|
||||
os = 0;
|
||||
while (((s = skip_to_delim (rl_line_buffer, os, COMMAND_SEPARATORS, SD_NOJMP|SD_NOSKIPCMD)) <= start) &&
|
||||
/* Flags == SD_NOJMP only because we want to skip over command substitutions
|
||||
in assignment statements. Have to test whether this affects `standalone'
|
||||
command substitutions as individual words. */
|
||||
while (((s = skip_to_delim (rl_line_buffer, os, COMMAND_SEPARATORS, SD_NOJMP/*|SD_NOSKIPCMD*/)) <= start) &&
|
||||
rl_line_buffer[s])
|
||||
os = s+1;
|
||||
return os;
|
||||
@@ -1243,8 +1327,9 @@ find_cmd_end (end)
|
||||
}
|
||||
|
||||
static char *
|
||||
find_cmd_name (start)
|
||||
find_cmd_name (start, sp, ep)
|
||||
int start;
|
||||
int *sp, *ep;
|
||||
{
|
||||
char *name;
|
||||
register int s, e;
|
||||
@@ -1257,6 +1342,11 @@ find_cmd_name (start)
|
||||
|
||||
name = substring (rl_line_buffer, s, e);
|
||||
|
||||
if (sp)
|
||||
*sp = s;
|
||||
if (ep)
|
||||
*ep = e;
|
||||
|
||||
return (name);
|
||||
}
|
||||
|
||||
@@ -1286,13 +1376,18 @@ attempt_shell_completion (text, start, end)
|
||||
{
|
||||
int in_command_position, ti, saveti, qc, dflags;
|
||||
char **matches, *command_separator_chars;
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
int have_progcomps, was_assignment;
|
||||
#endif
|
||||
|
||||
command_separator_chars = COMMAND_SEPARATORS;
|
||||
matches = (char **)NULL;
|
||||
rl_ignore_some_completions_function = filename_completion_ignore;
|
||||
|
||||
rl_filename_quote_characters = default_filename_quote_characters;
|
||||
set_filename_bstab (rl_filename_quote_characters);
|
||||
set_directory_hook ();
|
||||
rl_filename_stat_hook = bash_filename_stat_hook;
|
||||
|
||||
/* Determine if this could be a command word. It is if it appears at
|
||||
the start of the line (ignoring preceding whitespace), or if it
|
||||
@@ -1323,6 +1418,8 @@ attempt_shell_completion (text, start, end)
|
||||
are prompting at the top level. */
|
||||
if (current_prompt_string == ps1_prompt)
|
||||
in_command_position++;
|
||||
else if (parser_in_command_position ())
|
||||
in_command_position++;
|
||||
}
|
||||
else if (member (rl_line_buffer[ti], command_separator_chars))
|
||||
{
|
||||
@@ -1356,11 +1453,11 @@ attempt_shell_completion (text, start, end)
|
||||
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
/* Attempt programmable completion. */
|
||||
have_progcomps = prog_completion_enabled && (progcomp_size () > 0);
|
||||
if (matches == 0 && (in_command_position == 0 || text[0] == '\0') &&
|
||||
prog_completion_enabled && (progcomp_size () > 0) &&
|
||||
current_prompt_string == ps1_prompt)
|
||||
{
|
||||
int s, e, foundcs;
|
||||
int s, e, s1, e1, os, foundcs;
|
||||
char *n;
|
||||
|
||||
/* XXX - don't free the members */
|
||||
@@ -1368,13 +1465,62 @@ attempt_shell_completion (text, start, end)
|
||||
free (prog_complete_matches);
|
||||
prog_complete_matches = (char **)NULL;
|
||||
|
||||
s = find_cmd_start (start);
|
||||
os = start;
|
||||
n = 0;
|
||||
s = find_cmd_start (os);
|
||||
e = find_cmd_end (end);
|
||||
n = find_cmd_name (s);
|
||||
if (e == 0 && e == s && text[0] == '\0')
|
||||
do
|
||||
{
|
||||
/* Skip over assignment statements preceding a command name. If we
|
||||
don't find a command name at all, we can perform command name
|
||||
completion. If we find a partial command name, we should perform
|
||||
command name completion on it. */
|
||||
FREE (n);
|
||||
n = find_cmd_name (s, &s1, &e1);
|
||||
s = e1 + 1;
|
||||
}
|
||||
while (was_assignment = assignment (n, 0));
|
||||
s = s1; /* reset to index where name begins */
|
||||
|
||||
/* s == index of where command name begins (reset above)
|
||||
e == end of current command, may be end of line
|
||||
s1 = index of where command name begins
|
||||
e1 == index of where command name ends
|
||||
start == index of where word to be completed begins
|
||||
end == index of where word to be completed ends
|
||||
if (s == start) we are doing command word completion for sure
|
||||
if (e1 == end) we are at the end of the command name and completing it */
|
||||
if (start == 0 && end == 0 && e != 0 && text[0] == '\0') /* beginning of non-empty line */
|
||||
foundcs = 0;
|
||||
else if (start == end && start == s1 && e != 0 && e1 > end) /* beginning of command name, leading whitespace */
|
||||
foundcs = 0;
|
||||
else if (e == 0 && e == s && text[0] == '\0' && have_progcomps) /* beginning of empty line */
|
||||
prog_complete_matches = programmable_completions ("_EmptycmD_", text, s, e, &foundcs);
|
||||
else if (e > s && assignment (n, 0) == 0)
|
||||
prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
|
||||
else if (start == end && text[0] == '\0' && s1 > start && whitespace (rl_line_buffer[start]))
|
||||
foundcs = 0; /* whitespace before command name */
|
||||
else if (e > s && was_assignment == 0 && e1 == end && rl_line_buffer[e] == 0 && whitespace (rl_line_buffer[e-1]) == 0)
|
||||
{
|
||||
/* not assignment statement, but still want to perform command
|
||||
completion if we are composing command word. */
|
||||
foundcs = 0;
|
||||
in_command_position = s == start && STREQ (n, text); /* XXX */
|
||||
}
|
||||
else if (e > s && was_assignment == 0 && have_progcomps)
|
||||
{
|
||||
prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
|
||||
/* command completion if programmable completion fails */
|
||||
in_command_position = s == start && STREQ (n, text); /* XXX */
|
||||
}
|
||||
else if (s >= e && n[0] == '\0' && text[0] == '\0' && start > 0)
|
||||
{
|
||||
foundcs = 0; /* empty command name following assignments */
|
||||
in_command_position = was_assignment;
|
||||
}
|
||||
else if (s == start && e == end && STREQ (n, text) && start > 0)
|
||||
{
|
||||
foundcs = 0; /* partial command name following assignments */
|
||||
in_command_position = 1;
|
||||
}
|
||||
else
|
||||
foundcs = 0;
|
||||
FREE (n);
|
||||
@@ -1414,7 +1560,7 @@ bash_default_completion (text, start, end, qc, compflags)
|
||||
const char *text;
|
||||
int start, end, qc, compflags;
|
||||
{
|
||||
char **matches;
|
||||
char **matches, *t;
|
||||
|
||||
matches = (char **)NULL;
|
||||
|
||||
@@ -1424,7 +1570,19 @@ bash_default_completion (text, start, end, qc, compflags)
|
||||
if (qc != '\'' && text[1] == '(') /* ) */
|
||||
matches = rl_completion_matches (text, command_subst_completion_function);
|
||||
else
|
||||
matches = rl_completion_matches (text, variable_completion_function);
|
||||
{
|
||||
matches = rl_completion_matches (text, variable_completion_function);
|
||||
if (matches && matches[0] && matches[1] == 0)
|
||||
{
|
||||
t = savestring (matches[0]);
|
||||
bash_filename_stat_hook (&t);
|
||||
/* doesn't use test_for_directory because that performs tilde
|
||||
expansion */
|
||||
if (file_isdir (t))
|
||||
rl_completion_append_character = '/';
|
||||
free (t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the word starts in `~', and there is no slash in the word, then
|
||||
@@ -1500,11 +1658,55 @@ bash_default_completion (text, start, end, qc, compflags)
|
||||
strvec_dispose (matches);
|
||||
matches = (char **)0;
|
||||
}
|
||||
else if (matches && matches[1] && rl_completion_type == '!')
|
||||
{
|
||||
rl_completion_suppress_append = 1;
|
||||
rl_filename_completion_desired = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (matches);
|
||||
}
|
||||
|
||||
static int
|
||||
bash_command_name_stat_hook (name)
|
||||
char **name;
|
||||
{
|
||||
char *cname, *result;
|
||||
|
||||
/* If it's not something we're going to look up in $PATH, just call the
|
||||
normal filename stat hook. */
|
||||
if (absolute_program (*name))
|
||||
return (bash_filename_stat_hook (name));
|
||||
|
||||
cname = *name;
|
||||
/* XXX - we could do something here with converting aliases, builtins,
|
||||
and functions into something that came out as executable, but we don't. */
|
||||
result = search_for_command (cname, 0);
|
||||
if (result)
|
||||
{
|
||||
*name = result;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
executable_completion (filename, searching_path)
|
||||
const char *filename;
|
||||
int searching_path;
|
||||
{
|
||||
char *f;
|
||||
int r;
|
||||
|
||||
f = savestring (filename);
|
||||
bash_directory_completion_hook (&f);
|
||||
|
||||
r = searching_path ? executable_file (f) : executable_or_directory (f);
|
||||
free (f);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* This is the function to call when the word to complete is in a position
|
||||
where a command word can be found. It grovels $PATH, looking for commands
|
||||
that match. It also scans aliases, function names, and the shell_builtin
|
||||
@@ -1518,6 +1720,7 @@ command_word_completion_function (hint_text, state)
|
||||
static char *path = (char *)NULL;
|
||||
static char *val = (char *)NULL;
|
||||
static char *filename_hint = (char *)NULL;
|
||||
static char *fnhint = (char *)NULL;
|
||||
static char *dequoted_hint = (char *)NULL;
|
||||
static char *directory_part = (char *)NULL;
|
||||
static char **glob_matches = (char **)NULL;
|
||||
@@ -1528,12 +1731,14 @@ command_word_completion_function (hint_text, state)
|
||||
#if defined (ALIAS)
|
||||
static alias_t **alias_list = (alias_t **)NULL;
|
||||
#endif /* ALIAS */
|
||||
char *temp;
|
||||
char *temp, *cval;
|
||||
|
||||
/* We have to map over the possibilities for command words. If we have
|
||||
no state, then make one just for that purpose. */
|
||||
if (state == 0)
|
||||
{
|
||||
rl_filename_stat_hook = bash_command_name_stat_hook;
|
||||
|
||||
if (dequoted_hint && dequoted_hint != hint)
|
||||
free (dequoted_hint);
|
||||
if (hint)
|
||||
@@ -1595,7 +1800,7 @@ command_word_completion_function (hint_text, state)
|
||||
if (filename_hint)
|
||||
free (filename_hint);
|
||||
|
||||
filename_hint = savestring (hint);
|
||||
fnhint = filename_hint = savestring (hint);
|
||||
|
||||
istate = 0;
|
||||
|
||||
@@ -1606,7 +1811,7 @@ command_word_completion_function (hint_text, state)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dircomplete_expand && dot_or_dotdot (filename_hint))
|
||||
if (dircomplete_expand && path_dot_or_dotdot (filename_hint))
|
||||
{
|
||||
dircomplete_expand = 0;
|
||||
set_directory_hook ();
|
||||
@@ -1742,9 +1947,9 @@ globword:
|
||||
{
|
||||
if (executable_or_directory (val))
|
||||
{
|
||||
if (*hint_text == '~')
|
||||
if (*hint_text == '~' && directory_part)
|
||||
{
|
||||
temp = restore_tilde (val, directory_part);
|
||||
temp = maybe_restore_tilde (val, directory_part);
|
||||
free (val);
|
||||
val = temp;
|
||||
}
|
||||
@@ -1803,15 +2008,25 @@ globword:
|
||||
if (current_path[0] == '.' && current_path[1] == '\0')
|
||||
dot_in_path = 1;
|
||||
|
||||
if (fnhint && fnhint != filename_hint)
|
||||
free (fnhint);
|
||||
if (filename_hint)
|
||||
free (filename_hint);
|
||||
|
||||
filename_hint = sh_makepath (current_path, hint, 0);
|
||||
/* Need a quoted version (though it doesn't matter much in most
|
||||
cases) because rl_filename_completion_function dequotes the
|
||||
filename it gets, assuming that it's been quoted as part of
|
||||
the input line buffer. */
|
||||
if (strpbrk (filename_hint, "\"'\\"))
|
||||
fnhint = sh_backslash_quote (filename_hint, filename_bstab, 0);
|
||||
else
|
||||
fnhint = filename_hint;
|
||||
free (current_path); /* XXX */
|
||||
}
|
||||
|
||||
inner:
|
||||
val = rl_filename_completion_function (filename_hint, istate);
|
||||
val = rl_filename_completion_function (fnhint, istate);
|
||||
if (mapping_over == 4 && dircomplete_expand)
|
||||
set_directory_hook ();
|
||||
|
||||
@@ -1840,7 +2055,7 @@ globword:
|
||||
/* If we performed tilde expansion, restore the original
|
||||
filename. */
|
||||
if (*hint_text == '~')
|
||||
temp = restore_tilde (val, directory_part);
|
||||
temp = maybe_restore_tilde (val, directory_part);
|
||||
else
|
||||
temp = savestring (val);
|
||||
freetemp = 1;
|
||||
@@ -1863,19 +2078,38 @@ globword:
|
||||
freetemp = match = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* If we have found a match, and it is an executable file or a
|
||||
directory name, return it. */
|
||||
if (match && executable_or_directory (val))
|
||||
#else
|
||||
/* If we have found a match, and it is an executable file, return it.
|
||||
We don't return directory names when searching $PATH, since the
|
||||
bash execution code won't find executables in directories which
|
||||
appear in directories in $PATH when they're specified using
|
||||
relative pathnames. */
|
||||
if (match && (searching_path ? executable_file (val) : executable_or_directory (val)))
|
||||
#endif
|
||||
relative pathnames. */
|
||||
#if 0
|
||||
/* If we're not searching $PATH and we have a relative pathname, we
|
||||
need to re-canonicalize it before testing whether or not it's an
|
||||
executable or a directory so the shell treats .. relative to $PWD
|
||||
according to the physical/logical option. The shell already
|
||||
canonicalizes the directory name in order to tell readline where
|
||||
to look, so not doing it here will be inconsistent. */
|
||||
/* XXX -- currently not used -- will introduce more inconsistency,
|
||||
since shell does not canonicalize ../foo before passing it to
|
||||
shell_execve(). */
|
||||
if (match && searching_path == 0 && *val == '.')
|
||||
{
|
||||
char *t, *t1;
|
||||
|
||||
t = get_working_directory ("command-word-completion");
|
||||
t1 = make_absolute (val, t);
|
||||
free (t);
|
||||
cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
cval = val;
|
||||
|
||||
if (match && executable_completion ((searching_path ? val : cval), searching_path))
|
||||
{
|
||||
if (cval != val)
|
||||
free (cval);
|
||||
free (val);
|
||||
val = ""; /* So it won't be NULL. */
|
||||
return (temp);
|
||||
@@ -1884,6 +2118,8 @@ globword:
|
||||
{
|
||||
if (freetemp)
|
||||
free (temp);
|
||||
if (cval != val)
|
||||
free (cval);
|
||||
free (val);
|
||||
goto inner;
|
||||
}
|
||||
@@ -1952,7 +2188,7 @@ command_subst_completion_function (text, state)
|
||||
rl_completion_suppress_append = 1;
|
||||
}
|
||||
|
||||
if (!matches || !matches[cmd_index])
|
||||
if (matches == 0 || matches[cmd_index] == 0)
|
||||
{
|
||||
rl_filename_quoting_desired = 0; /* disable quoting */
|
||||
return ((char *)NULL);
|
||||
@@ -2676,6 +2912,20 @@ restore_tilde (val, directory_part)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static char *
|
||||
maybe_restore_tilde (val, directory_part)
|
||||
char *val, *directory_part;
|
||||
{
|
||||
rl_icppfunc_t *save;
|
||||
char *ret;
|
||||
|
||||
save = (dircomplete_expand == 0) ? save_directory_hook () : (rl_icppfunc_t *)0;
|
||||
ret = restore_tilde (val, directory_part);
|
||||
if (save)
|
||||
restore_directory_hook (save);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Simulate the expansions that will be performed by
|
||||
rl_filename_completion_function. This must be called with the address of
|
||||
a pointer to malloc'd memory. */
|
||||
@@ -2687,8 +2937,11 @@ bash_directory_expansion (dirname)
|
||||
|
||||
d = savestring (*dirname);
|
||||
|
||||
if (rl_directory_rewrite_hook)
|
||||
(*rl_directory_rewrite_hook) (&d);
|
||||
if ((rl_directory_rewrite_hook) && (*rl_directory_rewrite_hook) (&d))
|
||||
{
|
||||
free (*dirname);
|
||||
*dirname = d;
|
||||
}
|
||||
else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&d))
|
||||
{
|
||||
free (*dirname);
|
||||
@@ -2763,6 +3016,83 @@ restore_directory_hook (hookf)
|
||||
rl_directory_rewrite_hook = hookf;
|
||||
}
|
||||
|
||||
/* Expand a filename before the readline completion code passes it to stat(2).
|
||||
The filename will already have had tilde expansion performed. */
|
||||
static int
|
||||
bash_filename_stat_hook (dirname)
|
||||
char **dirname;
|
||||
{
|
||||
char *local_dirname, *new_dirname, *t;
|
||||
int should_expand_dirname, return_value;
|
||||
WORD_LIST *wl;
|
||||
struct stat sb;
|
||||
|
||||
local_dirname = *dirname;
|
||||
should_expand_dirname = return_value = 0;
|
||||
if (t = mbschr (local_dirname, '$'))
|
||||
should_expand_dirname = '$';
|
||||
else if (t = mbschr (local_dirname, '`')) /* XXX */
|
||||
should_expand_dirname = '`';
|
||||
|
||||
#if defined (HAVE_LSTAT)
|
||||
if (should_expand_dirname && lstat (local_dirname, &sb) == 0)
|
||||
#else
|
||||
if (should_expand_dirname && stat (local_dirname, &sb) == 0)
|
||||
#endif
|
||||
should_expand_dirname = 0;
|
||||
|
||||
if (should_expand_dirname)
|
||||
{
|
||||
new_dirname = savestring (local_dirname);
|
||||
wl = expand_prompt_string (new_dirname, 0, W_NOCOMSUB); /* does the right thing */
|
||||
if (wl)
|
||||
{
|
||||
free (new_dirname);
|
||||
new_dirname = string_list (wl);
|
||||
/* Tell the completer we actually expanded something and change
|
||||
*dirname only if we expanded to something non-null -- stat
|
||||
behaves unpredictably when passed null or empty strings */
|
||||
if (new_dirname && *new_dirname)
|
||||
{
|
||||
free (local_dirname); /* XXX */
|
||||
local_dirname = *dirname = new_dirname;
|
||||
return_value = STREQ (local_dirname, *dirname) == 0;
|
||||
}
|
||||
else
|
||||
free (new_dirname);
|
||||
dispose_words (wl);
|
||||
}
|
||||
else
|
||||
free (new_dirname);
|
||||
}
|
||||
|
||||
/* This is very similar to the code in bash_directory_completion_hook below,
|
||||
but without spelling correction and not worrying about whether or not
|
||||
we change relative pathnames. */
|
||||
if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
|
||||
{
|
||||
char *temp1, *temp2;
|
||||
|
||||
t = get_working_directory ("symlink-hook");
|
||||
temp1 = make_absolute (local_dirname, t);
|
||||
free (t);
|
||||
temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
|
||||
|
||||
/* If we can't canonicalize, bail. */
|
||||
if (temp2 == 0)
|
||||
{
|
||||
free (temp1);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
free (local_dirname);
|
||||
*dirname = temp2;
|
||||
free (temp1);
|
||||
}
|
||||
|
||||
return (return_value);
|
||||
}
|
||||
|
||||
/* Handle symbolic link references and other directory name
|
||||
expansions while hacking completion. This should return 1 if it modifies
|
||||
the DIRNAME argument, 0 otherwise. It should make sure not to modify
|
||||
@@ -2792,6 +3122,8 @@ bash_directory_completion_hook (dirname)
|
||||
else
|
||||
nextch = 0;
|
||||
}
|
||||
else if (local_dirname[0] == '~')
|
||||
should_expand_dirname = '~';
|
||||
else
|
||||
{
|
||||
t = mbschr (local_dirname, '`');
|
||||
@@ -2836,6 +3168,7 @@ bash_directory_completion_hook (dirname)
|
||||
}
|
||||
custom_filename_quote_characters[j] = '\0';
|
||||
rl_filename_quote_characters = custom_filename_quote_characters;
|
||||
set_filename_bstab (rl_filename_quote_characters);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2886,8 +3219,10 @@ bash_directory_completion_hook (dirname)
|
||||
free (t);
|
||||
temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
|
||||
|
||||
/* Try spelling correction if initial canonicalization fails. */
|
||||
if (temp2 == 0 && dircomplete_spelling)
|
||||
/* Try spelling correction if initial canonicalization fails. Make
|
||||
sure we are set to replace the directory name with the results so
|
||||
subsequent directory checks don't fail. */
|
||||
if (temp2 == 0 && dircomplete_spelling && dircomplete_expand)
|
||||
{
|
||||
temp2 = dirspell (temp1);
|
||||
if (temp2)
|
||||
@@ -3486,6 +3821,20 @@ quote_word_break_chars (text)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Use characters in STRING to populate the table of characters that should
|
||||
be backslash-quoted. The table will be used for sh_backslash_quote from
|
||||
this file. */
|
||||
static void
|
||||
set_filename_bstab (string)
|
||||
const char *string;
|
||||
{
|
||||
const char *s;
|
||||
|
||||
memset (filename_bstab, 0, sizeof (filename_bstab));
|
||||
for (s = string; s && *s; s++)
|
||||
filename_bstab[*s] = 1;
|
||||
}
|
||||
|
||||
/* Quote a filename using double quotes, single quotes, or backslashes
|
||||
depending on the value of completion_quoting_style. If we're
|
||||
completing using backslashes, we need to quote some additional
|
||||
@@ -3551,7 +3900,7 @@ bash_quote_filename (s, rtype, qcp)
|
||||
rtext = sh_single_quote (mtext);
|
||||
break;
|
||||
case COMPLETE_BSQUOTE:
|
||||
rtext = sh_backslash_quote (mtext);
|
||||
rtext = sh_backslash_quote (mtext, complete_fullquote ? 0 : filename_bstab, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3569,9 +3918,17 @@ bash_quote_filename (s, rtype, qcp)
|
||||
|
||||
/* Leave the opening quote intact. The readline completion code takes
|
||||
care of avoiding doubled opening quotes. */
|
||||
rlen = strlen (rtext);
|
||||
ret = (char *)xmalloc (rlen + 1);
|
||||
strcpy (ret, rtext);
|
||||
if (rtext)
|
||||
{
|
||||
rlen = strlen (rtext);
|
||||
ret = (char *)xmalloc (rlen + 1);
|
||||
strcpy (ret, rtext);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = (char *)xmalloc (rlen = 1);
|
||||
ret[0] = '\0';
|
||||
}
|
||||
|
||||
/* If there are multiple matches, cut off the closing quote. */
|
||||
if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
|
||||
@@ -3583,14 +3940,19 @@ bash_quote_filename (s, rtype, qcp)
|
||||
/* Support for binding readline key sequences to Unix commands. */
|
||||
static Keymap cmd_xmap;
|
||||
|
||||
#ifdef _MINIX
|
||||
static void
|
||||
#else
|
||||
static int
|
||||
#endif
|
||||
putx(c)
|
||||
int c;
|
||||
{
|
||||
int x;
|
||||
|
||||
x = putc (c, rl_outstream);
|
||||
return (x);
|
||||
#ifndef _MINIX
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3600,6 +3962,8 @@ bash_execute_unix_command (count, key)
|
||||
{
|
||||
Keymap ckmap; /* current keymap */
|
||||
Keymap xkmap; /* unix command executing keymap */
|
||||
rl_command_func_t *func;
|
||||
int type;
|
||||
register int i, r;
|
||||
intmax_t mi;
|
||||
sh_parser_state_t ps;
|
||||
@@ -3608,34 +3972,15 @@ bash_execute_unix_command (count, key)
|
||||
char ibuf[INT_STRLEN_BOUND(int) + 1];
|
||||
|
||||
/* First, we need to find the right command to execute. This is tricky,
|
||||
because we might have already indirected into another keymap. */
|
||||
ckmap = rl_get_keymap ();
|
||||
if (ckmap != rl_executing_keymap)
|
||||
because we might have already indirected into another keymap, so we
|
||||
have to walk cmd_xmap using the entire key sequence. */
|
||||
cmd = (char *)rl_function_of_keyseq (rl_executing_keyseq, cmd_xmap, &type);
|
||||
|
||||
if (cmd == 0 || type != ISMACR)
|
||||
{
|
||||
/* bogus. we have to search. only handle one level of indirection. */
|
||||
for (i = 0; i < KEYMAP_SIZE; i++)
|
||||
{
|
||||
if (ckmap[i].type == ISKMAP && (Keymap)ckmap[i].function == rl_executing_keymap)
|
||||
break;
|
||||
}
|
||||
if (i < KEYMAP_SIZE)
|
||||
xkmap = (Keymap)cmd_xmap[i].function;
|
||||
else
|
||||
{
|
||||
rl_crlf ();
|
||||
internal_error (_("bash_execute_unix_command: cannot find keymap for command"));
|
||||
rl_forced_update_display ();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
xkmap = cmd_xmap;
|
||||
|
||||
cmd = (char *)xkmap[key].function;
|
||||
|
||||
if (cmd == 0)
|
||||
{
|
||||
rl_ding ();
|
||||
rl_crlf ();
|
||||
internal_error (_("bash_execute_unix_command: cannot find keymap for command"));
|
||||
rl_forced_update_display ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3690,6 +4035,18 @@ bash_execute_unix_command (count, key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
print_unix_command_map ()
|
||||
{
|
||||
Keymap save;
|
||||
|
||||
save = rl_get_keymap ();
|
||||
rl_set_keymap (cmd_xmap);
|
||||
rl_macro_dumper (1);
|
||||
rl_set_keymap (save);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
init_unix_command_map ()
|
||||
{
|
||||
@@ -3773,12 +4130,16 @@ bind_keyseq_to_unix_command (line)
|
||||
if (line[i] != ':')
|
||||
{
|
||||
builtin_error (_("%s: missing colon separator"), line);
|
||||
FREE (kseq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = isolate_sequence (line, i + 1, 0, &kstart);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
{
|
||||
FREE (kseq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create the value string containing the command to execute. */
|
||||
value = substring (line, kstart, i);
|
||||
@@ -3789,7 +4150,8 @@ bind_keyseq_to_unix_command (line)
|
||||
/* and bind the key sequence in the current keymap to a function that
|
||||
understands how to execute from CMD_XMAP */
|
||||
rl_bind_keyseq_in_map (kseq, bash_execute_unix_command, kmap);
|
||||
|
||||
|
||||
free (kseq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3829,4 +4191,22 @@ bash_dequote_text (text)
|
||||
dtxt = bash_dequote_filename ((char *)text, qc);
|
||||
return (dtxt);
|
||||
}
|
||||
|
||||
/* This event hook is designed to be called after readline receives a signal
|
||||
that interrupts read(2). It gives reasonable responsiveness to interrupts
|
||||
and fatal signals without executing too much code in a signal handler
|
||||
context. */
|
||||
static int
|
||||
bash_event_hook ()
|
||||
{
|
||||
/* If we're going to longjmp to top_level, make sure we clean up readline.
|
||||
check_signals will call QUIT, which will eventually longjmp to top_level,
|
||||
calling run_interrupt_trap along the way. */
|
||||
if (interrupt_state)
|
||||
rl_cleanup_after_signal ();
|
||||
bashline_reset_event_hook ();
|
||||
check_signals_and_traps (); /* XXX */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* READLINE */
|
||||
|
||||
Reference in New Issue
Block a user