#include "config.h"
#if defined (READLINE)
#include "bashtypes.h"
#include "posixstat.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (HAVE_GRP_H)
# include <grp.h>
#endif
#if defined (HAVE_NETDB_H)
# include <netdb.h>
#endif
#include <stdio.h>
#include "chartypes.h"
#include "bashansi.h"
#include "shell.h"
#include "input.h"
#include "builtins.h"
#include "bashhist.h"
#include "bashline.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "pathexp.h"
#include "builtins/common.h"
#include <readline/rlconf.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <glob/glob.h>
#if defined (ALIAS)
# include "alias.h"
#endif
#if defined (PROGRAMMABLE_COMPLETION)
# include "pcomplete.h"
#endif
#ifndef EMACS_EDITING_MODE
# define NO_EDITING_MODE -1
# define EMACS_EDITING_MODE 1
# define VI_EDITING_MODE 0
#endif
#if defined (BRACE_COMPLETION)
extern int bash_brace_completion __P((int, int));
#endif
static int shell_expand_line __P((int, int));
static int display_shell_version __P((int, int));
static int operate_and_get_next __P((int, int));
static int bash_ignore_filenames __P((char **));
static int bash_ignore_everything __P((char **));
#if defined (BANG_HISTORY)
static char *history_expand_line_internal __P((char *));
static int history_expand_line __P((int, int));
static int tcsh_magic_space __P((int, int));
#endif
#ifdef ALIAS
static int alias_expand_line __P((int, int));
#endif
#if defined (BANG_HISTORY) && defined (ALIAS)
static int history_and_alias_expand_line __P((int, int));
#endif
static int bash_directory_completion_hook __P((char **));
static int filename_completion_ignore __P((char **));
static int bash_push_line __P((void));
static void cleanup_expansion_error __P((void));
static void maybe_make_readline_line __P((char *));
static void set_up_new_line __P((char *));
static int check_redir __P((int));
static char **attempt_shell_completion __P((const char *, int, int));
static char *variable_completion_function __P((const char *, int));
static char *hostname_completion_function __P((const char *, int));
static char *command_subst_completion_function __P((const char *, int));
static void build_history_completion_array __P((void));
static char *history_completion_generator __P((const char *, int));
static int dynamic_complete_history __P((int, int));
static void initialize_hostname_list __P((void));
static void add_host_name __P((char *));
static void snarf_hosts_from_file __P((char *));
static char **hostnames_matching __P((char *));
static void _ignore_completion_names __P((char **, sh_ignore_func_t *));
static int name_is_acceptable __P((const char *));
static int test_for_directory __P((const char *));
static int return_zero __P((const char *));
static char *bash_dequote_filename __P((char *, int));
static char *quote_word_break_chars __P((char *));
static char *bash_quote_filename __P((char *, int, char *));
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 *));
static int set_saved_history __P((void));
#if defined (ALIAS)
static int posix_edit_macros __P((int, int));
#endif
#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 *prog_complete_return __P((const char *, int));
static char **prog_complete_matches;
#endif
extern int current_command_line_count;
extern int posixly_correct, no_symbolic_links;
extern char *current_prompt_string, *ps1_prompt;
extern STRING_INT_ALIST word_token_alist[];
#define SPECIFIC_COMPLETION_FUNCTIONS
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int bash_specific_completion __P((int, rl_compentry_func_t *));
static int bash_complete_filename_internal __P((int));
static int bash_complete_username_internal __P((int));
static int bash_complete_hostname_internal __P((int));
static int bash_complete_variable_internal __P((int));
static int bash_complete_command_internal __P((int));
static int bash_complete_filename __P((int, int));
static int bash_possible_filename_completions __P((int, int));
static int bash_complete_username __P((int, int));
static int bash_possible_username_completions __P((int, int));
static int bash_complete_hostname __P((int, int));
static int bash_possible_hostname_completions __P((int, int));
static int bash_complete_variable __P((int, int));
static int bash_possible_variable_completions __P((int, int));
static int bash_complete_command __P((int, int));
static int bash_possible_command_completions __P((int, int));
static char *glob_complete_word __P((const char *, int));
static int bash_glob_completion_internal __P((int));
static int bash_glob_complete_word __P((int, int));
static int bash_glob_expand_word __P((int, int));
static int bash_glob_list_expansions __P((int, int));
#endif
static int edit_and_execute_command __P((int, int, int, char *));
#if defined (VI_MODE)
static int vi_edit_and_execute_command __P((int, int));
static int bash_vi_complete __P((int, int));
#endif
static int emacs_edit_and_execute_command __P((int, int));
int bash_readline_initialized = 0;
int perform_hostname_completion = 1;
int no_empty_command_completion;
static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
#define COMPLETE_DQUOTE 1
#define COMPLETE_SQUOTE 2
#define COMPLETE_BSQUOTE 3
static int completion_quoting_style = COMPLETE_BSQUOTE;
void
posix_readline_initialize (on_or_off)
int on_or_off;
{
if (on_or_off) {
rl_variable_bind ("comment-begin", "#");
rl_variable_bind ("visible-stats", "on");
}
#if defined (VI_MODE)
rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
#endif
}
int
enable_hostname_completion (on_or_off)
int on_or_off;
{
int old_value;
old_value = perform_hostname_completion;
if (on_or_off)
{
perform_hostname_completion = 1;
rl_special_prefixes = "$@";
rl_completer_word_break_characters = bash_completer_word_break_characters;
}
else
{
perform_hostname_completion = 0;
rl_special_prefixes = "$";
rl_completer_word_break_characters = bash_nohostname_word_break_characters;
}
return (old_value);
}
void
initialize_readline ()
{
if (bash_readline_initialized)
return;
rl_terminal_name = get_string_value ("TERM");
rl_instream = stdin;
rl_outstream = stderr;
rl_readline_name = "Bash";
rl_add_defun ("shell-expand-line", shell_expand_line, -1);
#ifdef BANG_HISTORY
rl_add_defun ("history-expand-line", history_expand_line, -1);
rl_add_defun ("magic-space", tcsh_magic_space, -1);
#endif
#ifdef ALIAS
rl_add_defun ("alias-expand-line", alias_expand_line, -1);
# ifdef BANG_HISTORY
rl_add_defun ("history-and-alias-expand-line", history_and_alias_expand_line, -1);
# endif
#endif
rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);
rl_add_defun ("operate-and-get-next", operate_and_get_next, -1);
rl_add_defun ("display-shell-version", display_shell_version, -1);
rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1);
#if defined (BRACE_COMPLETION)
rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
#endif
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
rl_add_defun ("complete-filename", bash_complete_filename, -1);
rl_add_defun ("possible-filename-completions", bash_possible_filename_completions, -1);
rl_add_defun ("complete-username", bash_complete_username, -1);
rl_add_defun ("possible-username-completions", bash_possible_username_completions, -1);
rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
rl_add_defun ("possible-hostname-completions", bash_possible_hostname_completions, -1);
rl_add_defun ("complete-variable", bash_complete_variable, -1);
rl_add_defun ("possible-variable-completions", bash_possible_variable_completions, -1);
rl_add_defun ("complete-command", bash_complete_command, -1);
rl_add_defun ("possible-command-completions", bash_possible_command_completions, -1);
rl_add_defun ("glob-complete-word", bash_glob_complete_word, -1);
rl_add_defun ("glob-expand-word", bash_glob_expand_word, -1);
rl_add_defun ("glob-list-expansions", bash_glob_list_expansions, -1);
#endif
rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
rl_initialize ();
rl_bind_key_in_map (CTRL('E'), shell_expand_line, emacs_meta_keymap);
#ifdef BANG_HISTORY
rl_bind_key_in_map ('^', history_expand_line, emacs_meta_keymap);
#endif
rl_bind_key_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap);
rl_bind_key_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);
rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
#endif
#if defined (BRACE_COMPLETION)
rl_bind_key_in_map ('{', bash_brace_completion, emacs_meta_keymap);
#endif
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
rl_bind_key_in_map ('/', bash_complete_filename, emacs_meta_keymap);
rl_bind_key_in_map ('/', bash_possible_filename_completions, emacs_ctlx_keymap);
rl_bind_key_in_map ('~', bash_complete_username, emacs_meta_keymap);
rl_bind_key_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);
rl_bind_key_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
rl_bind_key_in_map ('@', bash_possible_hostname_completions, emacs_ctlx_keymap);
rl_bind_key_in_map ('$', bash_complete_variable, emacs_meta_keymap);
rl_bind_key_in_map ('$', bash_possible_variable_completions, emacs_ctlx_keymap);
rl_bind_key_in_map ('!', bash_complete_command, emacs_meta_keymap);
rl_bind_key_in_map ('!', bash_possible_command_completions, emacs_ctlx_keymap);
rl_bind_key_in_map ('g', bash_glob_complete_word, emacs_meta_keymap);
rl_bind_key_in_map ('*', bash_glob_expand_word, emacs_ctlx_keymap);
rl_bind_key_in_map ('g', bash_glob_list_expansions, emacs_ctlx_keymap);
#endif
rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
rl_attempted_completion_function = attempt_shell_completion;
rl_directory_completion_hook = bash_directory_completion_hook;
rl_ignore_some_completions_function = filename_completion_ignore;
rl_bind_key_in_map (CTRL ('E'), emacs_edit_and_execute_command, emacs_ctlx_keymap);
#if defined (VI_MODE)
rl_bind_key_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
rl_bind_key_in_map ('\\', bash_vi_complete, vi_movement_keymap);
rl_bind_key_in_map ('*', bash_vi_complete, vi_movement_keymap);
rl_bind_key_in_map ('=', bash_vi_complete, vi_movement_keymap);
# if defined (ALIAS)
rl_bind_key_in_map ('@', posix_edit_macros, vi_movement_keymap);
# endif
#endif
rl_completer_quote_characters = "'\"";
enable_hostname_completion (perform_hostname_completion);
rl_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{";
rl_filename_quoting_function = bash_quote_filename;
rl_filename_dequoting_function = bash_dequote_filename;
rl_char_is_quoted_p = char_is_quoted;
#if 0
if (posixly_correct)
posix_readline_initialize (1);
#endif
bash_readline_initialized = 1;
}
void
bashline_reinitialize ()
{
tilde_initialize ();
rl_attempted_completion_function = attempt_shell_completion;
rl_completion_entry_function = NULL;
rl_directory_completion_hook = bash_directory_completion_hook;
rl_ignore_some_completions_function = filename_completion_ignore;
}
static char *push_to_readline = (char *)NULL;
static int
bash_push_line ()
{
if (push_to_readline)
{
rl_insert_text (push_to_readline);
free (push_to_readline);
push_to_readline = (char *)NULL;
rl_startup_hook = old_rl_startup_hook;
}
return 0;
}
int
bash_re_edit (line)
char *line;
{
FREE (push_to_readline);
push_to_readline = savestring (line);
old_rl_startup_hook = rl_startup_hook;
rl_startup_hook = bash_push_line;
return (0);
}
static int
display_shell_version (count, c)
int count, c;
{
rl_crlf ();
show_shell_version (0);
putc ('\r', rl_outstream);
fflush (rl_outstream);
rl_on_new_line ();
rl_redisplay ();
return 0;
}
static char **hostname_list = (char **)NULL;
static int hostname_list_size;
static int hostname_list_length;
int hostname_list_initialized = 0;
static void
initialize_hostname_list ()
{
char *temp;
temp = get_string_value ("HOSTFILE");
if (temp == 0)
temp = get_string_value ("hostname_completion_file");
if (temp == 0)
temp = DEFAULT_HOSTS_FILE;
snarf_hosts_from_file (temp);
if (hostname_list)
hostname_list_initialized++;
}
static void
add_host_name (name)
char *name;
{
if (hostname_list_length + 2 > hostname_list_size)
{
hostname_list_size = (hostname_list_size + 32) - (hostname_list_size % 32);
hostname_list = strvec_resize (hostname_list, hostname_list_size);
}
hostname_list[hostname_list_length++] = savestring (name);
hostname_list[hostname_list_length] = (char *)NULL;
}
#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
static void
snarf_hosts_from_file (filename)
char *filename;
{
FILE *file;
char *temp, buffer[256], name[256];
register int i, start;
file = fopen (filename, "r");
if (file == 0)
return;
while (temp = fgets (buffer, 255, file))
{
for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++)
;
if (buffer[i] == '\0' || buffer[i] == '#')
continue;
if (strncmp (buffer + i, "$include ", 9) == 0)
{
char *incfile, *t;
for (incfile = buffer + i + 9; *incfile && whitespace (*incfile); incfile++)
;
for (t = incfile; *t && cr_whitespace (*t) == 0; t++)
;
*t = '\0';
snarf_hosts_from_file (incfile);
continue;
}
if (DIGIT (buffer[i]))
for (; buffer[i] && cr_whitespace (buffer[i]) == 0; i++);
while (buffer[i])
{
for (; cr_whitespace (buffer[i]); i++)
;
if (buffer[i] == '\0' || buffer[i] == '#')
break;
for (start = i; buffer[i] && cr_whitespace (buffer[i]) == 0; i++)
;
if (i == start)
continue;
strncpy (name, buffer + start, i - start);
name[i - start] = '\0';
add_host_name (name);
}
}
fclose (file);
}
char **
get_hostname_list ()
{
if (hostname_list_initialized == 0)
initialize_hostname_list ();
return (hostname_list);
}
void
clear_hostname_list ()
{
register int i;
if (hostname_list_initialized == 0)
return;
for (i = 0; i < hostname_list_length; i++)
free (hostname_list[i]);
hostname_list_length = 0;
}
static char **
hostnames_matching (text)
char *text;
{
register int i, len, nmatch, rsize;
char **result;
if (hostname_list_initialized == 0)
initialize_hostname_list ();
if (hostname_list_initialized == 0)
return ((char **)NULL);
if (*text == '\0')
{
result = strvec_create (1 + hostname_list_length);
for (i = 0; i < hostname_list_length; i++)
result[i] = hostname_list[i];
result[i] = (char *)NULL;
return (result);
}
len = strlen (text);
result = (char **)NULL;
for (i = nmatch = rsize = 0; i < hostname_list_length; i++)
{
if (STREQN (text, hostname_list[i], len) == 0)
continue;
if (nmatch >= (rsize - 1))
{
rsize = (rsize + 16) - (rsize % 16);
result = strvec_resize (result, rsize);
}
result[nmatch++] = hostname_list[i];
}
if (nmatch)
result[nmatch] = (char *)NULL;
return (result);
}
static int saved_history_line_to_use = -1;
static int
set_saved_history ()
{
if (saved_history_line_to_use >= 0)
rl_get_previous_history (history_length - saved_history_line_to_use, 0);
saved_history_line_to_use = -1;
rl_startup_hook = old_rl_startup_hook;
return (0);
}
static int
operate_and_get_next (count, c)
int count, c;
{
int where;
rl_newline (1, c);
where = where_history ();
if ((history_is_stifled () && (history_length >= history_max_entries)) ||
(where >= history_length - 1))
saved_history_line_to_use = where;
else
saved_history_line_to_use = where + 1;
old_rl_startup_hook = rl_startup_hook;
rl_startup_hook = set_saved_history;
return 0;
}
#define VI_EDIT_COMMAND "fc -e ${VISUAL:-${EDITOR:-vi}}"
#define EMACS_EDIT_COMMAND "fc -e ${VISUAL:-${EDITOR:-emacs}}"
static int
edit_and_execute_command (count, c, editing_mode, edit_command)
int count, c, editing_mode;
char *edit_command;
{
char *command;
int r, cclc, rrs;
rrs = rl_readline_state;
cclc = current_command_line_count;
rl_newline (1, c);
if (rl_explicit_arg)
{
command = (char *)xmalloc (strlen (edit_command) + 8);
sprintf (command, "%s %d", edit_command, count);
}
else
{
using_history ();
bash_add_history (rl_line_buffer);
bash_add_history ("");
history_lines_this_session++;
using_history ();
command = savestring (edit_command);
}
r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
current_command_line_count = cclc;
rl_line_buffer[0] = '\0';
rl_point = rl_end = 0;
rl_done = 0;
rl_readline_state = rrs;
rl_forced_update_display ();
return r;
}
#if defined (VI_MODE)
static int
vi_edit_and_execute_command (count, c)
int count, c;
{
return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
}
#endif
static int
emacs_edit_and_execute_command (count, c)
int count, c;
{
return (edit_and_execute_command (count, c, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
}
#if defined (ALIAS)
static int
posix_edit_macros (count, key)
int count, key;
{
int c;
char alias_name[3], *alias_value, *macro;
c = rl_read_key ();
alias_name[0] = '_';
alias_name[1] = c;
alias_name[2] = '\0';
alias_value = get_alias_value (alias_name);
if (alias_value && *alias_value)
{
macro = savestring (alias_value);
rl_push_macro_input (macro);
}
return 0;
}
#endif
#define COMMAND_SEPARATORS ";|&{(`"
static int
check_redir (ti)
int ti;
{
register int this_char, prev_char;
this_char = rl_line_buffer[ti];
prev_char = rl_line_buffer[ti - 1];
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)))
return (1);
return (0);
}
#if defined (PROGRAMMABLE_COMPLETION)
static int
find_cmd_start (start)
int start;
{
register int s, os;
os = 0;
while (((s = skip_to_delim (rl_line_buffer, os, COMMAND_SEPARATORS)) <= start) &&
rl_line_buffer[s])
os = s+1;
return os;
}
static int
find_cmd_end (end)
int end;
{
register int e;
e = skip_to_delim (rl_line_buffer, end, COMMAND_SEPARATORS);
return e;
}
static char *
find_cmd_name (start)
int start;
{
char *name;
register int s, e;
for (s = start; whitespace (rl_line_buffer[s]); s++)
;
e = skip_to_delim (rl_line_buffer, s, "()<>;&| \t\n");
name = substring (rl_line_buffer, s, e);
return (name);
}
static char *
prog_complete_return (text, matchnum)
const char *text;
int matchnum;
{
static int ind;
if (matchnum == 0)
ind = 0;
if (prog_complete_matches == 0 || prog_complete_matches[ind] == 0)
return (char *)NULL;
return (prog_complete_matches[ind++]);
}
#endif
static char **
attempt_shell_completion (text, start, end)
const char *text;
int start, end;
{
int in_command_position, ti, saveti, qc;
char **matches, *command_separator_chars;
command_separator_chars = COMMAND_SEPARATORS;
matches = (char **)NULL;
rl_ignore_some_completions_function = filename_completion_ignore;
ti = start - 1;
saveti = qc = -1;
while ((ti > -1) && (whitespace (rl_line_buffer[ti])))
ti--;
#if 1
if (rl_line_buffer[ti] == '"' || rl_line_buffer[ti] == '\'')
{
qc = rl_line_buffer[ti];
saveti = ti--;
while (ti > -1 && (whitespace (rl_line_buffer[ti])))
ti--;
}
#endif
in_command_position = 0;
if (ti < 0)
{
if (current_prompt_string == ps1_prompt)
in_command_position++;
}
else if (member (rl_line_buffer[ti], command_separator_chars))
{
in_command_position++;
if (check_redir (ti) == 1)
in_command_position = 0;
}
else
{
}
if (in_command_position && ti >= 0 && rl_line_buffer[ti] == '`' &&
*text != '`' && unclosed_pair (rl_line_buffer, end, "`") == 0)
in_command_position = 0;
if (*text == '`' &&
(in_command_position || (unclosed_pair (rl_line_buffer, start, "`") &&
unclosed_pair (rl_line_buffer, end, "`"))))
matches = rl_completion_matches (text, command_subst_completion_function);
#if defined (PROGRAMMABLE_COMPLETION)
if (!matches && in_command_position == 0 && prog_completion_enabled &&
(progcomp_size () > 0) && current_prompt_string == ps1_prompt)
{
int s, e, foundcs;
char *n;
if (prog_complete_matches)
free (prog_complete_matches);
prog_complete_matches = (char **)NULL;
s = find_cmd_start (start);
e = find_cmd_end (end);
n = find_cmd_name (s);
if (e > s)
prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
else
foundcs = 0;
FREE (n);
if (foundcs)
{
if (foundcs & COPT_FILENAMES)
rl_filename_completion_desired = 1;
if (foundcs & COPT_NOSPACE)
rl_completion_suppress_append = 1;
matches = rl_completion_matches (text, prog_complete_return);
if ((foundcs & COPT_DEFAULT) == 0)
rl_attempted_completion_over = 1;
return (matches);
}
}
#endif
if (!matches && *text == '$')
{
if (qc != '\'' && text[1] == '(')
matches = rl_completion_matches (text, command_subst_completion_function);
else
matches = rl_completion_matches (text, variable_completion_function);
}
if (!matches && *text == '~' && !xstrchr (text, '/'))
matches = rl_completion_matches (text, rl_username_completion_function);
if (!matches && perform_hostname_completion && *text == '@')
matches = rl_completion_matches (text, hostname_completion_function);
if (!matches && in_command_position)
{
if (start == 0 && end == 0 && text[0] == '\0' && no_empty_command_completion)
{
matches = (char **)NULL;
rl_ignore_some_completions_function = bash_ignore_everything;
}
else
{
matches = rl_completion_matches (text, command_word_completion_function);
if (matches == (char **)NULL)
rl_ignore_some_completions_function = bash_ignore_filenames;
}
}
if (!matches && glob_pattern_p (text))
{
matches = rl_completion_matches (text, glob_complete_word);
if (matches && matches[1] && rl_completion_type == TAB)
{
strvec_dispose (matches);
matches = (char **)0;
}
}
return (matches);
}
char *
command_word_completion_function (hint_text, state)
const char *hint_text;
int state;
{
static char *hint = (char *)NULL;
static char *path = (char *)NULL;
static char *val = (char *)NULL;
static char *filename_hint = (char *)NULL;
static int path_index, hint_len, istate;
static int mapping_over, local_index;
static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
#if defined (ALIAS)
static alias_t **alias_list = (alias_t **)NULL;
#endif
if (!state)
{
if (hint)
free (hint);
mapping_over = 0;
val = (char *)NULL;
if (absolute_program (hint_text))
{
if (*hint_text == '~')
hint = bash_tilde_expand (hint_text, 0);
else
hint = savestring (hint_text);
hint_len = strlen (hint);
if (filename_hint)
free (filename_hint);
filename_hint = savestring (hint);
mapping_over = 4;
istate = 0;
goto inner;
}
hint = savestring (hint_text);
hint_len = strlen (hint);
path = get_string_value ("PATH");
path_index = 0;
local_index = 0;
if (varlist)
free (varlist);
varlist = all_visible_functions ();
#if defined (ALIAS)
if (alias_list)
free (alias_list);
alias_list = all_aliases ();
#endif
}
switch (mapping_over)
{
case 0:
#if defined (ALIAS)
while (alias_list && alias_list[local_index])
{
register char *alias;
alias = alias_list[local_index++]->name;
if (STREQN (alias, hint, hint_len))
return (savestring (alias));
}
#endif
local_index = 0;
mapping_over++;
case 1:
{
while (word_token_alist[local_index].word)
{
register char *reserved_word;
reserved_word = word_token_alist[local_index++].word;
if (STREQN (reserved_word, hint, hint_len))
return (savestring (reserved_word));
}
local_index = 0;
mapping_over++;
}
case 2:
while (varlist && varlist[local_index])
{
register char *varname;
varname = varlist[local_index++]->name;
if (STREQN (varname, hint, hint_len))
return (savestring (varname));
}
local_index = 0;
mapping_over++;
case 3:
for (; local_index < num_shell_builtins; local_index++)
{
if (!shell_builtins[local_index].function ||
(shell_builtins[local_index].flags & BUILTIN_ENABLED) == 0)
continue;
if (STREQN (shell_builtins[local_index].name, hint, hint_len))
{
int i = local_index++;
return (savestring (shell_builtins[i].name));
}
}
local_index = 0;
mapping_over++;
}
outer:
istate = (val != (char *)NULL);
if (!istate)
{
char *current_path;
if (!path || !path[path_index] ||
(current_path = extract_colon_unit (path, &path_index)) == 0)
return ((char *)NULL);
if (*current_path == 0)
{
free (current_path);
current_path = savestring (".");
}
if (*current_path == '~')
{
char *t;
t = bash_tilde_expand (current_path, 0);
free (current_path);
current_path = t;
}
if (filename_hint)
free (filename_hint);
filename_hint = sh_makepath (current_path, hint, 0);
free (current_path);
}
inner:
val = rl_filename_completion_function (filename_hint, istate);
istate = 1;
if (val == 0)
{
if (absolute_program (hint))
return ((char *)NULL);
goto outer;
}
else
{
int match, freetemp;
char *temp;
if (absolute_program (hint))
{
match = strncmp (val, hint, hint_len) == 0;
if (*hint_text == '~')
{
int l, tl, vl;
vl = strlen (val);
tl = strlen (hint_text);
l = vl - hint_len;
temp = (char *)xmalloc (l + 2 + tl);
strcpy (temp, hint_text);
strcpy (temp + tl, val + vl - l);
}
else
temp = savestring (val);
freetemp = 1;
}
else
{
temp = strrchr (val, '/');
if (temp)
{
temp++;
freetemp = match = strncmp (temp, hint, hint_len) == 0;
if (match)
temp = savestring (temp);
}
else
freetemp = match = 0;
}
if (match && executable_or_directory (val))
{
free (val);
val = "";
return (temp);
}
else
{
if (freetemp)
free (temp);
free (val);
goto inner;
}
}
}
static char *
command_subst_completion_function (text, state)
const char *text;
int state;
{
static char **matches = (char **)NULL;
static const char *orig_start;
static char *filename_text = (char *)NULL;
static int cmd_index, start_len;
char *value;
if (state == 0)
{
if (filename_text)
free (filename_text);
orig_start = text;
if (*text == '`')
text++;
else if (*text == '$' && text[1] == '(')
text += 2;
start_len = text - orig_start;
filename_text = savestring (text);
if (matches)
free (matches);
for (value = filename_text + strlen (filename_text) - 1; value > filename_text; value--)
if (whitespace (*value) || member (*value, COMMAND_SEPARATORS))
break;
if (value <= filename_text)
matches = rl_completion_matches (filename_text, command_word_completion_function);
else
{
value++;
start_len += value - filename_text;
if (whitespace (value[-1]))
matches = rl_completion_matches (value, rl_filename_completion_function);
else
matches = rl_completion_matches (value, command_word_completion_function);
}
cmd_index = matches && matches[0] && matches[1];
}
if (!matches || !matches[cmd_index])
{
rl_filename_quoting_desired = 0;
return ((char *)NULL);
}
else
{
value = (char *)xmalloc (1 + start_len + strlen (matches[cmd_index]));
if (start_len == 1)
value[0] = *orig_start;
else
strncpy (value, orig_start, start_len);
strcpy (value + start_len, matches[cmd_index]);
cmd_index++;
return (value);
}
}
static char *
variable_completion_function (text, state)
const char *text;
int state;
{
static char **varlist = (char **)NULL;
static int varlist_index;
static char *varname = (char *)NULL;
static int namelen;
static int first_char, first_char_loc;
if (!state)
{
if (varname)
free (varname);
first_char_loc = 0;
first_char = text[0];
if (first_char == '$')
first_char_loc++;
if (text[first_char_loc] == '{')
first_char_loc++;
varname = savestring (text + first_char_loc);
namelen = strlen (varname);
if (varlist)
strvec_dispose (varlist);
varlist = all_variables_matching_prefix (varname);
varlist_index = 0;
}
if (!varlist || !varlist[varlist_index])
{
return ((char *)NULL);
}
else
{
char *value;
value = (char *)xmalloc (4 + strlen (varlist[varlist_index]));
if (first_char_loc)
{
value[0] = first_char;
if (first_char_loc == 2)
value[1] = '{';
}
strcpy (value + first_char_loc, varlist[varlist_index]);
if (first_char_loc == 2)
strcat (value, "}");
varlist_index++;
return (value);
}
}
static char *
hostname_completion_function (text, state)
const char *text;
int state;
{
static char **list = (char **)NULL;
static int list_index = 0;
static int first_char, first_char_loc;
if (state == 0)
{
FREE (list);
list = (char **)NULL;
first_char_loc = 0;
first_char = *text;
if (first_char == '@')
first_char_loc++;
list = hostnames_matching ((char *)text+first_char_loc);
list_index = 0;
}
if (list && list[list_index])
{
char *t;
t = (char *)xmalloc (2 + strlen (list[list_index]));
*t = first_char;
strcpy (t + first_char_loc, list[list_index]);
list_index++;
return (t);
}
return ((char *)NULL);
}
char *
bash_servicename_completion_function (text, state)
const char *text;
int state;
{
#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GETSERVENT)
return ((char *)NULL);
#else
static char *sname = (char *)NULL;
static struct servent *srvent;
static int snamelen, firstc;
char *value;
char **alist, *aentry;
int afound;
if (state == 0)
{
FREE (sname);
firstc = *text;
sname = savestring (text);
snamelen = strlen (sname);
setservent (0);
}
while (srvent = getservent ())
{
afound = 0;
if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen)))
break;
for (alist = srvent->s_aliases; aentry = *alist; alist++)
{
if (STREQN (sname, aentry, snamelen))
{
afound = 1;
break;
}
}
if (afound)
break;
}
if (srvent == 0)
{
endservent ();
return ((char *)NULL);
}
value = afound ? savestring (aentry) : savestring (srvent->s_name);
return value;
#endif
}
char *
bash_groupname_completion_function (text, state)
const char *text;
int state;
{
#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GRP_H)
return ((char *)NULL);
#else
static char *gname = (char *)NULL;
static struct group *grent;
static int gnamelen;
char *value;
if (state == 0)
{
FREE (gname);
gname = savestring (text);
gnamelen = strlen (gname);
setgrent ();
}
while (grent = getgrent ())
{
if (gnamelen == 0 || (STREQN (gname, grent->gr_name, gnamelen)))
break;
}
if (grent == 0)
{
endgrent ();
return ((char *)NULL);
}
value = savestring (grent->gr_name);
return (value);
#endif
}
#if defined (BANG_HISTORY)
static char *
history_expand_line_internal (line)
char *line;
{
char *new_line;
new_line = pre_process_line (line, 0, 0);
return (new_line == line) ? savestring (line) : new_line;
}
#endif
static void
cleanup_expansion_error ()
{
char *to_free;
fprintf (rl_outstream, "\r\n");
to_free = pre_process_line (rl_line_buffer, 1, 0);
if (to_free != rl_line_buffer)
free (to_free);
putc ('\r', rl_outstream);
rl_forced_update_display ();
}
static void
maybe_make_readline_line (new_line)
char *new_line;
{
if (strcmp (new_line, rl_line_buffer) != 0)
{
rl_point = rl_end;
rl_add_undo (UNDO_BEGIN, 0, 0, 0);
rl_delete_text (0, rl_point);
rl_point = rl_end = rl_mark = 0;
rl_insert_text (new_line);
rl_add_undo (UNDO_END, 0, 0, 0);
}
}
static void
set_up_new_line (new_line)
char *new_line;
{
int old_point, at_end;
old_point = rl_point;
at_end = rl_point == rl_end;
maybe_make_readline_line (new_line);
free (new_line);
if (at_end)
rl_point = rl_end;
else if (old_point < rl_end)
{
rl_point = old_point;
if (!whitespace (rl_line_buffer[rl_point]))
rl_forward_word (1, 0);
}
}
#if defined (ALIAS)
static int
alias_expand_line (count, ignore)
int count, ignore;
{
char *new_line;
new_line = alias_expand (rl_line_buffer);
if (new_line)
{
set_up_new_line (new_line);
return (0);
}
else
{
cleanup_expansion_error ();
return (1);
}
}
#endif
#if defined (BANG_HISTORY)
static int
history_expand_line (count, ignore)
int count, ignore;
{
char *new_line;
new_line = history_expand_line_internal (rl_line_buffer);
if (new_line)
{
set_up_new_line (new_line);
return (0);
}
else
{
cleanup_expansion_error ();
return (1);
}
}
static int
tcsh_magic_space (count, ignore)
int count, ignore;
{
int dist_from_end, old_point;
old_point = rl_point;
dist_from_end = rl_end - rl_point;
if (history_expand_line (count, ignore) == 0)
{
rl_point = (old_point == 0) ? old_point : rl_end - dist_from_end;
rl_insert (1, ' ');
return (0);
}
else
return (1);
}
#endif
static int
history_and_alias_expand_line (count, ignore)
int count, ignore;
{
char *new_line;
new_line = pre_process_line (rl_line_buffer, 0, 0);
if (new_line == rl_line_buffer)
new_line = savestring (new_line);
#if defined (ALIAS)
if (new_line)
{
char *alias_line;
alias_line = alias_expand (new_line);
free (new_line);
new_line = alias_line;
}
#endif
if (new_line)
{
set_up_new_line (new_line);
return (0);
}
else
{
cleanup_expansion_error ();
return (1);
}
}
static int
shell_expand_line (count, ignore)
int count, ignore;
{
char *new_line;
WORD_LIST *expanded_string;
new_line = pre_process_line (rl_line_buffer, 0, 0);
if (new_line == rl_line_buffer)
new_line = savestring (new_line);
#if defined (ALIAS)
if (new_line)
{
char *alias_line;
alias_line = alias_expand (new_line);
free (new_line);
new_line = alias_line;
}
#endif
if (new_line)
{
int old_point = rl_point;
int at_end = rl_point == rl_end;
maybe_make_readline_line (new_line);
free (new_line);
new_line = savestring (rl_line_buffer);
expanded_string = expand_string (new_line, 0);
FREE (new_line);
if (expanded_string == 0)
{
new_line = (char *)xmalloc (1);
new_line[0] = '\0';
}
else
{
new_line = string_list (expanded_string);
dispose_words (expanded_string);
}
maybe_make_readline_line (new_line);
free (new_line);
if (at_end)
rl_point = rl_end;
else if (old_point < rl_end)
{
rl_point = old_point;
if (!whitespace (rl_line_buffer[rl_point]))
rl_forward_word (1, 0);
}
return 0;
}
else
{
cleanup_expansion_error ();
return 1;
}
}
static struct ignorevar fignore =
{
"FIGNORE",
(struct ign *)0,
0,
(char *)0,
(sh_iv_item_func_t *) 0,
};
static void
_ignore_completion_names (names, name_func)
char **names;
sh_ignore_func_t *name_func;
{
char **newnames;
int idx, nidx;
#ifdef NO_FORCE_FIGNORE
char **oldnames;
int oidx;
#endif
if (names[1] == (char *)0)
{
#ifndef NO_FORCE_FIGNORE
if ((*name_func) (names[0]) == 0)
{
free (names[0]);
names[0] = (char *)NULL;
}
#endif
return;
}
for (nidx = 1; names[nidx]; nidx++)
;
newnames = strvec_create (nidx + 1);
#ifdef NO_FORCE_FIGNORE
oldnames = strvec_create (nidx - 1);
oidx = 0;
#endif
newnames[0] = names[0];
for (idx = nidx = 1; names[idx]; idx++)
{
if ((*name_func) (names[idx]))
newnames[nidx++] = names[idx];
else
#ifndef NO_FORCE_FIGNORE
free (names[idx]);
#else
oldnames[oidx++] = names[idx];
#endif
}
newnames[nidx] = (char *)NULL;
if (nidx == 1)
{
#ifndef NO_FORCE_FIGNORE
free (names[0]);
names[0] = (char *)NULL;
#else
free (oldnames);
#endif
free (newnames);
return;
}
#ifdef NO_FORCE_FIGNORE
while (oidx)
free (oldnames[--oidx]);
free (oldnames);
#endif
if (nidx == 2)
{
free (names[0]);
names[0] = newnames[1];
names[1] = (char *)NULL;
free (newnames);
return;
}
for (nidx = 1; newnames[nidx]; nidx++)
names[nidx] = newnames[nidx];
names[nidx] = (char *)NULL;
free (newnames);
}
static int
name_is_acceptable (name)
const char *name;
{
struct ign *p;
int nlen;
for (nlen = strlen (name), p = fignore.ignores; p->val; p++)
{
if (nlen > p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
return (0);
}
return (1);
}
#if 0
static int
ignore_dot_names (name)
char *name;
{
return (name[0] != '.');
}
#endif
static int
filename_completion_ignore (names)
char **names;
{
#if 0
if (glob_dot_filenames == 0)
_ignore_completion_names (names, ignore_dot_names);
#endif
setup_ignore_patterns (&fignore);
if (fignore.num_ignores == 0)
return 0;
_ignore_completion_names (names, name_is_acceptable);
return 0;
}
static int
test_for_directory (name)
const char *name;
{
struct stat finfo;
char *fn;
fn = bash_tilde_expand (name, 0);
if (stat (fn, &finfo) != 0)
{
free (fn);
return 0;
}
free (fn);
return (S_ISDIR (finfo.st_mode));
}
static int
bash_ignore_filenames (names)
char **names;
{
_ignore_completion_names (names, test_for_directory);
return 0;
}
static int
return_zero (name)
const char *name;
{
return 0;
}
static int
bash_ignore_everything (names)
char **names;
{
_ignore_completion_names (names, return_zero);
return 0;
}
static int
bash_directory_completion_hook (dirname)
char **dirname;
{
char *local_dirname, *new_dirname, *t;
int return_value, should_expand_dirname;
WORD_LIST *wl;
return_value = should_expand_dirname = 0;
local_dirname = *dirname;
#if 0
should_expand_dirname = xstrchr (local_dirname, '$') || xstrchr (local_dirname, '`');
#else
if (xstrchr (local_dirname, '$'))
should_expand_dirname = 1;
else
{
t = xstrchr (local_dirname, '`');
if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
should_expand_dirname = 1;
}
#endif
if (should_expand_dirname)
{
new_dirname = savestring (local_dirname);
wl = expand_prompt_string (new_dirname, 0);
if (wl)
{
*dirname = string_list (wl);
return_value = STREQ (local_dirname, *dirname) == 0;
free (local_dirname);
free (new_dirname);
dispose_words (wl);
local_dirname = *dirname;
}
else
{
free (new_dirname);
free (local_dirname);
*dirname = (char *)xmalloc (1);
**dirname = '\0';
return 1;
}
}
if (!no_symbolic_links && (local_dirname[0] != '.' || local_dirname[1]))
{
char *temp1, *temp2;
int len1, len2;
t = get_working_directory ("symlink-hook");
temp1 = make_absolute (local_dirname, t);
free (t);
temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
if (temp2 == 0)
{
free (temp1);
return 1;
}
len1 = strlen (temp1);
if (temp1[len1 - 1] == '/')
{
len2 = strlen (temp2);
temp2 = (char *)xrealloc (temp2, len2 + 2);
temp2[len2] = '/';
temp2[len2 + 1] = '\0';
}
free (local_dirname);
*dirname = temp2;
free (temp1);
}
return (return_value);
}
static char **history_completion_array = (char **)NULL;
static int harry_size;
static int harry_len;
static void
build_history_completion_array ()
{
register int i, j;
HIST_ENTRY **hlist;
char **tokens;
if (harry_size)
{
strvec_dispose (history_completion_array);
history_completion_array = (char **)NULL;
harry_size = 0;
harry_len = 0;
}
hlist = history_list ();
if (hlist)
{
for (i = 0; hlist[i]; i++)
{
tokens = history_tokenize (hlist[i]->line);
for (j = 0; tokens && tokens[j]; j++)
{
if (harry_len + 2 > harry_size)
history_completion_array = strvec_resize (history_completion_array, harry_size += 10);
history_completion_array[harry_len++] = tokens[j];
history_completion_array[harry_len] = (char *)NULL;
}
free (tokens);
}
qsort (history_completion_array, harry_len, sizeof (char *), (QSFUNC *)strvec_strcmp);
}
}
static char *
history_completion_generator (hint_text, state)
const char *hint_text;
int state;
{
static int local_index, len;
static const char *text;
if (state == 0)
{
local_index = 0;
build_history_completion_array ();
text = hint_text;
len = strlen (text);
}
while (history_completion_array && history_completion_array[local_index])
{
if (strncmp (text, history_completion_array[local_index++], len) == 0)
return (savestring (history_completion_array[local_index - 1]));
}
return ((char *)NULL);
}
static int
dynamic_complete_history (count, key)
int count, key;
{
int r;
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
orig_func = rl_completion_entry_function;
orig_attempt_func = rl_attempted_completion_function;
rl_completion_entry_function = history_completion_generator;
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
if (rl_last_func == dynamic_complete_history)
r = rl_complete_internal ('?');
else
r = rl_complete_internal (TAB);
rl_completion_entry_function = orig_func;
rl_attempted_completion_function = orig_attempt_func;
return r;
}
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int
bash_complete_username (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_username_internal (rl_completion_mode (bash_complete_username));
}
static int
bash_possible_username_completions (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_username_internal ('?');
}
static int
bash_complete_username_internal (what_to_do)
int what_to_do;
{
return bash_specific_completion (what_to_do, rl_username_completion_function);
}
static int
bash_complete_filename (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_filename_internal (rl_completion_mode (bash_complete_filename));
}
static int
bash_possible_filename_completions (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_filename_internal ('?');
}
static int
bash_complete_filename_internal (what_to_do)
int what_to_do;
{
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
rl_icppfunc_t *orig_dir_func;
const char *orig_rl_completer_word_break_characters;
int r;
orig_func = rl_completion_entry_function;
orig_attempt_func = rl_attempted_completion_function;
orig_dir_func = rl_directory_completion_hook;
orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
rl_completion_entry_function = rl_filename_completion_function;
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
rl_completer_word_break_characters = " \t\n\"\'";
r = rl_complete_internal (what_to_do);
rl_completion_entry_function = orig_func;
rl_attempted_completion_function = orig_attempt_func;
rl_directory_completion_hook = orig_dir_func;
rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
return r;
}
static int
bash_complete_hostname (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_hostname_internal (rl_completion_mode (bash_complete_hostname));
}
static int
bash_possible_hostname_completions (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_hostname_internal ('?');
}
static int
bash_complete_variable (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_variable_internal (rl_completion_mode (bash_complete_variable));
}
static int
bash_possible_variable_completions (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_variable_internal ('?');
}
static int
bash_complete_command (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_command_internal (rl_completion_mode (bash_complete_command));
}
static int
bash_possible_command_completions (ignore, ignore2)
int ignore, ignore2;
{
return bash_complete_command_internal ('?');
}
static int
bash_complete_hostname_internal (what_to_do)
int what_to_do;
{
return bash_specific_completion (what_to_do, hostname_completion_function);
}
static int
bash_complete_variable_internal (what_to_do)
int what_to_do;
{
return bash_specific_completion (what_to_do, variable_completion_function);
}
static int
bash_complete_command_internal (what_to_do)
int what_to_do;
{
return bash_specific_completion (what_to_do, command_word_completion_function);
}
static char *globtext;
static char *globorig;
static char *
glob_complete_word (text, state)
const char *text;
int state;
{
static char **matches = (char **)NULL;
static int ind;
int glen;
char *ret, *ttext;
if (state == 0)
{
rl_filename_completion_desired = 1;
FREE (matches);
if (globorig != globtext)
FREE (globorig);
FREE (globtext);
ttext = bash_tilde_expand (text, 0);
if (rl_explicit_arg)
{
globorig = savestring (ttext);
glen = strlen (ttext);
globtext = (char *)xmalloc (glen + 2);
strcpy (globtext, ttext);
globtext[glen] = '*';
globtext[glen+1] = '\0';
}
else
globtext = globorig = savestring (ttext);
if (ttext != text)
free (ttext);
matches = shell_glob_filename (globtext);
if (GLOB_FAILED (matches))
matches = (char **)NULL;
ind = 0;
}
ret = matches ? matches[ind] : (char *)NULL;
ind++;
return ret;
}
static int
bash_glob_completion_internal (what_to_do)
int what_to_do;
{
return bash_specific_completion (what_to_do, glob_complete_word);
}
static char *
bash_glob_quote_filename (s, rtype, qcp)
char *s;
int rtype;
char *qcp;
{
if (globorig && qcp && *qcp == '\0' && STREQ (s, globorig))
return (savestring (s));
else
return (bash_quote_filename (s, rtype, qcp));
}
static int
bash_glob_complete_word (count, key)
int count, key;
{
int r;
rl_quote_func_t *orig_quoting_function;
rl_explicit_arg = 1;
if ((rl_editing_mode == VI_EDITING_MODE) && (rl_point == rl_end - 1))
rl_point++;
orig_quoting_function = rl_filename_quoting_function;
rl_filename_quoting_function = bash_glob_quote_filename;
r = bash_glob_completion_internal (rl_completion_mode (bash_glob_complete_word));
rl_filename_quoting_function = orig_quoting_function;
return r;
}
static int
bash_glob_expand_word (count, key)
int count, key;
{
return bash_glob_completion_internal ('*');
}
static int
bash_glob_list_expansions (count, key)
int count, key;
{
return bash_glob_completion_internal ('?');
}
static int
bash_specific_completion (what_to_do, generator)
int what_to_do;
rl_compentry_func_t *generator;
{
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
int r;
orig_func = rl_completion_entry_function;
orig_attempt_func = rl_attempted_completion_function;
rl_completion_entry_function = generator;
rl_attempted_completion_function = NULL;
r = rl_complete_internal (what_to_do);
rl_completion_entry_function = orig_func;
rl_attempted_completion_function = orig_attempt_func;
return r;
}
#endif
#if defined (VI_MODE)
static int
bash_vi_complete (count, key)
int count, key;
{
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
int p, r;
char *t;
if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
{
if (!whitespace (rl_line_buffer[rl_point + 1]))
rl_vi_end_word (1, 'E');
rl_point++;
}
t = 0;
if (rl_point > 0)
{
p = rl_point;
rl_vi_bWord (1, 'B');
r = rl_point;
rl_point = p;
p = r;
t = substring (rl_line_buffer, p, rl_point);
}
if (t && glob_pattern_p (t) == 0)
rl_explicit_arg = 1;
FREE (t);
if (key == '*')
r = bash_glob_expand_word (count, key);
else if (key == '=')
r = bash_glob_list_expansions (count, key);
else if (key == '\\')
r = bash_glob_complete_word (count, key);
else
r = rl_complete (0, key);
if (key == '*' || key == '\\')
rl_vi_start_inserting (key, 1, 1);
return (r);
#else
return rl_vi_complete (count, key);
#endif
}
#endif
static char *
bash_dequote_filename (text, quote_char)
char *text;
int quote_char;
{
char *ret, *p, *r;
int l, quoted;
l = strlen (text);
ret = (char *)xmalloc (l + 1);
for (quoted = quote_char, p = text, r = ret; p && *p; p++)
{
if (*p == '\\')
{
*r++ = *++p;
if (*p == '\0')
break;
continue;
}
if (quoted && *p == quoted)
{
quoted = 0;
continue;
}
if (quoted == 0 && (*p == '\'' || *p == '"'))
{
quoted = *p;
continue;
}
*r++ = *p;
}
*r = '\0';
return ret;
}
static char *
quote_word_break_chars (text)
char *text;
{
char *ret, *r, *s;
int l;
l = strlen (text);
ret = (char *)xmalloc ((2 * l) + 1);
for (s = text, r = ret; *s; s++)
{
if (*s == '\\')
{
*r++ = '\\';
*r++ = *++s;
if (*s == '\0')
break;
continue;
}
if (xstrchr (rl_completer_word_break_characters, *s))
*r++ = '\\';
*r++ = *s;
}
*r = '\0';
return ret;
}
static char *
bash_quote_filename (s, rtype, qcp)
char *s;
int rtype;
char *qcp;
{
char *rtext, *mtext, *ret;
int rlen, cs;
rtext = (char *)NULL;
mtext = s;
if (mtext[0] == '~' && rtype == SINGLE_MATCH)
mtext = bash_tilde_expand (s, 0);
cs = completion_quoting_style;
if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && xstrchr (mtext, '\n'))
cs = COMPLETE_SQUOTE;
else if (*qcp == '"')
cs = COMPLETE_DQUOTE;
else if (*qcp == '\'')
cs = COMPLETE_SQUOTE;
#if defined (BANG_HISTORY)
else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
cs = COMPLETE_BSQUOTE;
if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
{
cs = COMPLETE_BSQUOTE;
*qcp = '\0';
}
#endif
switch (cs)
{
case COMPLETE_DQUOTE:
rtext = sh_double_quote (mtext);
break;
case COMPLETE_SQUOTE:
rtext = sh_single_quote (mtext);
break;
case COMPLETE_BSQUOTE:
rtext = sh_backslash_quote (mtext);
break;
}
if (mtext != s)
free (mtext);
if (rtext && cs == COMPLETE_BSQUOTE)
{
mtext = quote_word_break_chars (rtext);
free (rtext);
rtext = mtext;
}
rlen = strlen (rtext);
ret = (char *)xmalloc (rlen + 1);
strcpy (ret, rtext);
if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
ret[rlen - 1] = '\0';
free (rtext);
return ret;
}
static Keymap cmd_xmap;
static int
bash_execute_unix_command (count, key)
int count;
int key;
{
Keymap ckmap;
Keymap xkmap;
register int i;
char *cmd;
int old_line_count;
int *ts;
ckmap = rl_get_keymap ();
if (ckmap != rl_executing_keymap)
{
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 ();
return 1;
}
rl_crlf ();
old_line_count = current_command_line_count;
ts = save_token_state ();
cmd = savestring (cmd);
parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST);
current_command_line_count = old_line_count;
restore_token_state (ts);
rl_forced_update_display ();
return 0;
}
static void
init_unix_command_map ()
{
cmd_xmap = rl_make_bare_keymap ();
}
static int
isolate_sequence (string, ind, need_dquote, startp)
char *string;
int ind, need_dquote, *startp;
{
register int i;
int c, passc, delim;
for (i = ind; string[i] && whitespace (string[i]); i++)
;
if (need_dquote && string[i] != '"')
{
builtin_error ("%s: first non-whitespace character is not `\"'", string);
return -1;
}
delim = (string[i] == '"' || string[i] == '\'') ? string[i] : 0;
if (startp)
*startp = delim ? ++i : i;
for (passc = 0; c = string[i]; i++)
{
if (passc)
{
passc = 0;
continue;
}
if (c == '\\')
{
passc++;
continue;
}
if (c == delim)
break;
}
if (delim && string[i] != delim)
{
builtin_error ("%s: no closing `%c'", string, delim);
return -1;
}
return i;
}
int
bind_keyseq_to_unix_command (line)
char *line;
{
Keymap kmap;
char *kseq, *value;
int i, kstart;
if (cmd_xmap == 0)
init_unix_command_map ();
kmap = rl_get_keymap ();
i = isolate_sequence (line, 0, 1, &kstart);
if (i < 0)
return -1;
kseq = substring (line, kstart, i);
for ( ; line[i] && line[i] != ':'; i++)
;
if (line[i] != ':')
{
builtin_error ("%s: missing colon separator", line);
return -1;
}
i = isolate_sequence (line, i + 1, 0, &kstart);
if (i < 0)
return -1;
value = substring (line, kstart, i);
rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
rl_set_key (kseq, bash_execute_unix_command, kmap);
return 0;
}
char **
bash_directory_completion_matches (text)
const char *text;
{
char **m1;
char *dfn;
int qc;
qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
dfn = bash_dequote_filename ((char *)text, qc);
m1 = rl_completion_matches (dfn, rl_filename_completion_function);
free (dfn);
if (m1 == 0 || m1[0] == 0)
return m1;
(void)bash_ignore_filenames (m1);
return m1;
}
#endif