/* bashline.c -- Bash's interface to the readline library. */
-/* Copyright (C) 1987-2017 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2024 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "shmbutil.h"
#include "trap.h"
#include "flags.h"
+#include "timer.h"
#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
# include <mbstr.h> /* mbschr */
#endif
#include "builtins/common.h"
+#include "builtins/builtext.h" /* for read_builtin */
#include <readline/rlconf.h>
#include <readline/readline.h>
# define VI_EDITING_MODE 0
#endif
+/* Copied from rldefs.h, since that's not a public readline header file. */
+#ifndef FUNCTION_TO_KEYMAP
+
+#if defined (CRAY)
+# define FUNCTION_TO_KEYMAP(map, key) (Keymap)((int)map[key].function)
+# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)((int)(data))
+#else
+# define FUNCTION_TO_KEYMAP(map, key) (Keymap)(map[key].function)
+# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)(data)
+#endif
+
+#endif
+
#define RL_BOOLEAN_VARIABLE_VALUE(s) ((s)[0] == 'o' && (s)[1] == 'n' && (s)[2] == '\0')
#if defined (BRACE_COMPLETION)
-extern int bash_brace_completion __P((int, int));
+extern int bash_brace_completion (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)));
+extern int tputs (const char *string, int nlines, void (*outx)(int));
#else
-extern int tputs __P((const char *string, int nlines, int (*outx)(int)));
+extern int tputs (const char *string, int nlines, int (*outx)(int));
#endif
/* Forward declarations */
/* Functions bound to keys in Readline for Bash users. */
-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 shell_expand_line (int, int);
+static int display_shell_version (int, int);
-static int bash_ignore_filenames __P((char **));
-static int bash_ignore_everything __P((char **));
+static int bash_ignore_filenames (char **);
+static int bash_ignore_everything (char **);
+static int bash_progcomp_ignore_filenames (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));
+static char *history_expand_line_internal (char *);
+static int history_expand_line (int, int);
+static int tcsh_magic_space (int, int);
#endif /* BANG_HISTORY */
#ifdef ALIAS
-static int alias_expand_line __P((int, int));
+static int alias_expand_line (int, int);
#endif
#if defined (BANG_HISTORY) && defined (ALIAS)
-static int history_and_alias_expand_line __P((int, int));
+static int history_and_alias_expand_line (int, int);
#endif
-static int bash_forward_shellword __P((int, int));
-static int bash_backward_shellword __P((int, int));
-static int bash_kill_shellword __P((int, int));
-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 restore_directory_hook __P((rl_icppfunc_t));
-
-static int directory_exists __P((const char *, int));
-
-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 bash_forward_shellword (int, int);
+static int bash_backward_shellword (int, int);
+static int bash_kill_shellword (int, int);
+static int bash_backward_kill_shellword (int, int);
+static int bash_transpose_shellwords (int, int);
-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 int bash_spell_correct_shellword (int, 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 int bash_dabbrev_expand __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 void set_filename_bstab __P((const char *));
-static char *bash_quote_filename __P((char *, int, char *));
+/* Helper functions for Readline. */
+static char *restore_tilde (const char *, char *);
+static char *maybe_restore_tilde (char *, char *);
+
+static char *bash_filename_rewrite_hook (char *, int);
+
+static void bash_directory_expansion (char **);
+static char *bash_expand_filename (char *);
+static int bash_filename_stat_hook (char **);
+static int bash_command_name_stat_hook (char **);
+static int bash_directory_completion_hook (char **);
+static int filename_completion_ignore (char **);
+static int bash_push_line (void);
+
+static int executable_completion (const char *, int);
+
+static rl_icppfunc_t *save_directory_hook (void);
+static void restore_directory_hook (rl_icppfunc_t);
+
+static int directory_exists (const char *, int);
+
+static void cleanup_expansion_error (void);
+static void maybe_make_readline_line (const char *);
+static void set_up_new_line (char *);
+
+static int check_redir (int);
+static char **attempt_shell_completion (const char *, int, int);
+static char *variable_completion_function (const char *, int);
+static char *hostname_completion_function (const char *, int);
+static char *command_subst_completion_function (const char *, int);
+
+static void build_history_completion_array (void);
+static char *history_completion_generator (const char *, int);
+static int dynamic_complete_history (int, int);
+static int bash_dabbrev_expand (int, int);
+
+static void initialize_hostname_list (void);
+static void add_host_name (const char *);
+static void snarf_hosts_from_file (const char *);
+static char **hostnames_matching (const char *);
+
+static void _ignore_completion_names (char **, sh_ignore_func_t *);
+static int name_is_acceptable (const char *);
+static int test_for_directory (const char *);
+static int test_for_canon_directory (const char *);
+static int return_zero (const char *);
+
+static char *bash_dequote_filename (char *, int);
+static char *quote_word_break_chars (char *);
+static int bash_check_expchar (char *, int, int *, int *);
+static void set_filename_quote_chars (int, int, int);
+static void set_filename_bstab (const char *);
+static char *bash_quote_filename (char *, int, char *);
#ifdef _MINIX
-static void putx __P((int));
+static void putx (int);
#else
-static int putx __P((int));
+static int putx (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 *));
+static int readline_get_char_offset (int);
+static void readline_set_char_offset (int, int *);
-static int set_saved_history __P((void));
+static Keymap get_cmd_xmap_from_edit_mode (void);
+static Keymap get_cmd_xmap_from_keymap (Keymap);
+
+static void init_unix_command_map (void);
+static int isolate_sequence (char *, int, int, int *);
+
+static int set_saved_history (void);
#if defined (ALIAS)
-static int posix_edit_macros __P((int, int));
+static int posix_edit_macros (int, int);
#endif
-static int bash_event_hook __P((void));
+static int bash_event_hook (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, int *, int *));
-static char *prog_complete_return __P((const char *, int));
+static int find_cmd_start (int);
+static int find_cmd_end (int);
+static char *find_cmd_name (int, int *, int *);
+static char *prog_complete_return (const char *, int);
static char **prog_complete_matches;
#endif
extern int no_symbolic_links;
extern STRING_INT_ALIST word_token_alist[];
+extern sh_timer *read_timeout;
/* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
completion functions which indicate what type of completion should be
#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));
+static int bash_specific_completion (int, rl_compentry_func_t *);
+
+static int bash_complete_filename_internal (int);
+static int bash_complete_username_internal (int);
+static int bash_complete_hostname_internal (int);
+static int bash_complete_variable_internal (int);
+static int bash_complete_command_internal (int);
+
+static int bash_complete_filename (int, int);
+static int bash_possible_filename_completions (int, int);
+static int bash_complete_username (int, int);
+static int bash_possible_username_completions (int, int);
+static int bash_complete_hostname (int, int);
+static int bash_possible_hostname_completions (int, int);
+static int bash_complete_variable (int, int);
+static int bash_possible_variable_completions (int, int);
+static int bash_complete_command (int, int);
+static int bash_possible_command_completions (int, int);
+
+static int completion_glob_pattern (const char *);
+static char *glob_complete_word (const char *, int);
+static int bash_glob_completion_internal (int);
+static int bash_glob_complete_word (int, int);
+static int bash_glob_expand_word (int, int);
+static int bash_glob_list_expansions (int, int);
#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
-static int edit_and_execute_command __P((int, int, int, char *));
+static int edit_and_execute_command (int, int, int, const char *);
#if defined (VI_MODE)
-static int vi_edit_and_execute_command __P((int, int));
-static int bash_vi_complete __P((int, int));
+static int vi_edit_and_execute_command (int, int);
+static int bash_vi_complete (int, int);
#endif
-static int emacs_edit_and_execute_command __P((int, int));
+static int emacs_edit_and_execute_command (int, int);
-/* Non-zero once initalize_readline () has been called. */
+/* Non-zero once initialize_readline () has been called. */
int bash_readline_initialized = 0;
/* If non-zero, we do hostname completion, breaking words at `@' and
/* 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.
+ variable reference, 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;
COMPLETE_DQUOTE = double-quoting the filename
COMPLETE_SQUOTE = single_quoting the filename
COMPLETE_BSQUOTE = backslash-quoting special chars in the filename
+ COMPLETE_DQUOTE2 = double-quote filename, but leave $ and ` unquoted
*/
#define COMPLETE_DQUOTE 1
#define COMPLETE_SQUOTE 2
#define COMPLETE_BSQUOTE 3
+#define COMPLETE_DQUOTE2 4
+
static int completion_quoting_style = COMPLETE_BSQUOTE;
/* Flag values for the final argument to bash_default_completion */
#define DEFCOMP_CMDPOS 1
+static rl_command_func_t *vi_tab_binding = rl_complete;
+
/* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
Called when the shell is put into or out of `posix' mode. */
void
-posix_readline_initialize (on_or_off)
- int on_or_off;
+posix_readline_initialize (int on_or_off)
{
+ static char kseq[2] = { CTRL ('I'), 0 }; /* TAB */
+
if (on_or_off)
rl_variable_bind ("comment-begin", "#");
#if defined (VI_MODE)
- rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
+ if (on_or_off)
+ {
+ vi_tab_binding = rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL);
+ rl_bind_key_in_map (CTRL ('I'), rl_insert, vi_insertion_keymap);
+ }
+ else
+ {
+ if (rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL) == rl_insert)
+ rl_bind_key_in_map (CTRL ('I'), vi_tab_binding, vi_insertion_keymap);
+ }
#endif
}
void
-reset_completer_word_break_chars ()
+reset_completer_word_break_chars (void)
{
rl_completer_word_break_characters = perform_hostname_completion ? savestring (bash_completer_word_break_characters) : savestring (bash_nohostname_word_break_characters);
}
/* When this function returns, rl_completer_word_break_characters points to
dynamically allocated memory. */
int
-enable_hostname_completion (on_or_off)
- int on_or_off;
+enable_hostname_completion (int on_or_off)
{
int old_value;
- char *at, *nv, *nval;
+ char *nv, *nval;
+ const char *at;
old_value = perform_hostname_completion;
strcpy (nval + 1, rl_completer_word_break_characters);
}
- free (rl_completer_word_break_characters);
+ free ((void *)rl_completer_word_break_characters);
rl_completer_word_break_characters = nval;
}
/* Called once from parse.y if we are going to use readline. */
void
-initialize_readline ()
+initialize_readline (void)
{
rl_command_func_t *func;
char kseq[2];
rl_add_defun ("shell-backward-word", bash_backward_shellword, -1);
rl_add_defun ("shell-kill-word", bash_kill_shellword, -1);
rl_add_defun ("shell-backward-kill-word", bash_backward_kill_shellword, -1);
+ rl_add_defun ("shell-transpose-words", bash_transpose_shellwords, -1);
+
+ rl_add_defun ("spell-correct-word", bash_spell_correct_shellword, -1);
+ rl_bind_key_if_unbound_in_map ('s', bash_spell_correct_shellword, emacs_ctlx_keymap);
#ifdef ALIAS
rl_add_defun ("alias-expand-line", alias_expand_line, -1);
/* Backwards compatibility. */
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 (VI_MODE)
+ rl_add_defun ("vi-edit-and-execute-command", vi_edit_and_execute_command, -1);
+#endif
#if defined (BRACE_COMPLETION)
rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
rl_bind_key_if_unbound_in_map ('^', history_expand_line, emacs_meta_keymap);
#endif
- rl_bind_key_if_unbound_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap);
rl_bind_key_if_unbound_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);
/* In Bash, the user can switch editing modes with "set -o [vi emacs]",
if (func == rl_vi_editing_mode)
rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
- rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
+ kseq[0] = CTRL('E');
+ func = rl_function_of_keyseq (kseq, vi_movement_keymap, (int *)NULL);
+ if (func == rl_emacs_editing_mode)
+ rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
#endif
#if defined (BRACE_COMPLETION)
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = attempt_shell_completion;
- /* Tell the completer that we might want to follow symbolic links or
- do other expansion on directory names. */
- set_directory_hook ();
-
- rl_filename_rewrite_hook = bash_filename_rewrite_hook;
-
- rl_filename_stat_hook = bash_filename_stat_hook;
+ bashline_set_filename_hooks ();
/* Tell the filename completer we want a chance to ignore some names. */
rl_ignore_some_completions_function = filename_completion_ignore;
rl_bind_key_if_unbound_in_map ('@', posix_edit_macros, vi_movement_keymap);
# endif
+ rl_add_defun ("bash-vi-complete", bash_vi_complete, -1);
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);
rl_filename_dequoting_function = bash_dequote_filename;
rl_char_is_quoted_p = char_is_quoted;
+ /* Add some default bindings for the "shellwords" functions, roughly
+ parallelling the default word bindings in emacs mode. */
+ rl_bind_key_if_unbound_in_map (CTRL('B'), bash_backward_shellword, emacs_meta_keymap);
+ rl_bind_key_if_unbound_in_map (CTRL('D'), bash_kill_shellword, emacs_meta_keymap);
+ rl_bind_key_if_unbound_in_map (CTRL('F'), bash_forward_shellword, emacs_meta_keymap);
+ rl_bind_key_if_unbound_in_map (CTRL('T'), bash_transpose_shellwords, emacs_meta_keymap);
+
#if 0
/* This is superfluous and makes it impossible to use tab completion in
vi mode even when explicitly binding it in ~/.inputrc. sv_strict_posix()
}
void
-bashline_reinitialize ()
+bashline_reinitialize (void)
{
bash_readline_initialized = 0;
}
void
-bashline_set_event_hook ()
+bashline_set_event_hook (void)
{
rl_signal_event_hook = bash_event_hook;
}
void
-bashline_reset_event_hook ()
+bashline_reset_event_hook (void)
{
rl_signal_event_hook = 0;
}
word. This just resets all the completion functions to the right thing.
It's called from throw_to_top_level(). */
void
-bashline_reset ()
+bashline_reset (void)
{
tilde_initialize ();
rl_attempted_completion_function = attempt_shell_completion;
rl_completion_entry_function = NULL;
rl_ignore_some_completions_function = filename_completion_ignore;
+
+ complete_fullquote = 1;
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;
+ rl_filename_quoting_function = bash_quote_filename;
+
+ bashline_set_filename_hooks ();
bashline_reset_event_hook ();
/* Push the contents of push_to_readline into the
readline buffer. */
static int
-bash_push_line ()
+bash_push_line (void)
{
if (push_to_readline)
{
/* Call this to set the initial text for the next line to read
from readline. */
int
-bash_re_edit (line)
- char *line;
+bash_re_edit (const char *line)
{
FREE (push_to_readline);
}
static int
-display_shell_version (count, c)
- int count, c;
+display_shell_version (int count, int c)
{
rl_crlf ();
show_shell_version (0);
static char **hostname_list = (char **)NULL;
/* The physical size of the above list. */
-static int hostname_list_size;
+static size_t hostname_list_size;
/* The number of hostnames in the above list. */
-static int hostname_list_length;
+static size_t hostname_list_length;
/* Whether or not HOSTNAME_LIST has been initialized. */
int hostname_list_initialized = 0;
/* Initialize the hostname completion table. */
static void
-initialize_hostname_list ()
+initialize_hostname_list (void)
{
char *temp;
/* Add NAME to the list of hosts. */
static void
-add_host_name (name)
- char *name;
+add_host_name (const char *name)
{
if (hostname_list_length + 2 > hostname_list_size)
{
#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
static void
-snarf_hosts_from_file (filename)
- char *filename;
+snarf_hosts_from_file (const char *filename)
{
FILE *file;
char *temp, buffer[256], name[256];
register int i, start;
+#ifdef __MSYS__
+ file = fopen (filename, "rt");
+#else
file = fopen (filename, "r");
+#endif
if (file == 0)
return;
/* Return the hostname list. */
char **
-get_hostname_list ()
+get_hostname_list (void)
{
if (hostname_list_initialized == 0)
initialize_hostname_list ();
}
void
-clear_hostname_list ()
+clear_hostname_list (void)
{
register int i;
Initialize the hostname list the first time if necessary.
The array is malloc ()'ed, but not the individual strings. */
static char **
-hostnames_matching (text)
- char *text;
+hostnames_matching (const char *text)
{
- register int i, len, nmatch, rsize;
+ int i, nmatch;
char **result;
+ size_t rsize, len;
if (hostname_list_initialized == 0)
initialize_hostname_list ();
continue;
/* OK, it matches. Add it to the list. */
- if (nmatch >= (rsize - 1))
+ if (nmatch + 1 >= rsize)
{
rsize = (rsize + 16) - (rsize % 16);
result = strvec_resize (result, rsize);
return (result);
}
-/* 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);
- last_saved_history_line = saved_history_line_to_use;
- }
- 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;
-
- /* Accept the current line. */
- rl_newline (1, c);
-
- /* Find the current line, and find the next line to use. */
- where = rl_explicit_arg ? count : where_history ();
-
- if (HISTORY_FULL () || (where >= history_length - 1) || rl_explicit_arg)
- 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;
-}
-
/* This vi mode command causes VI_EDIT_COMMAND to be run on the current
command being entered (if no explicit argument is given), otherwise on
a command from the history file. */
#define POSIX_VI_EDIT_COMMAND "fc -e vi"
static int
-edit_and_execute_command (count, c, editing_mode, edit_command)
- int count, c, editing_mode;
- char *edit_command;
+edit_and_execute_command (int count, int c, int editing_mode, const char *edit_command)
{
char *command, *metaval;
int r, rrs, metaflag;
if (rl_explicit_arg)
{
- command = (char *)xmalloc (strlen (edit_command) + 8);
- sprintf (command, "%s %d", edit_command, count);
+ size_t clen;
+ /* 32 exceeds strlen (itos (INTMAX_MAX)) (19) */
+ clen = strlen (edit_command) + 32;
+ command = (char *)xmalloc (clen);
+ snprintf (command, clen, "%s %d", edit_command, count);
}
else
{
/* 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 ();
- if (rl_line_buffer[0])
- {
- current_command_line_count++; /* for rl_newline above */
- bash_add_history (rl_line_buffer);
- }
+ 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++;
if (rl_deprep_term_function)
(*rl_deprep_term_function) ();
+ rl_clear_signals ();
save_parser_state (&ps);
r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
restore_parser_state (&ps);
+
+ /* if some kind of reset_parser was called, undo it. */
+ reset_readahead_token ();
+
if (rl_prep_term_function)
(*rl_prep_term_function) (metaflag);
+ rl_set_signals ();
current_command_line_count = saved_command_line_count;
#if defined (VI_MODE)
static int
-vi_edit_and_execute_command (count, c)
- int count, c;
+vi_edit_and_execute_command (int count, int key)
{
if (posixly_correct)
- return (edit_and_execute_command (count, c, VI_EDITING_MODE, POSIX_VI_EDIT_COMMAND));
+ return (edit_and_execute_command (count, key, VI_EDITING_MODE, POSIX_VI_EDIT_COMMAND));
else
- return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
+ return (edit_and_execute_command (count, key, VI_EDITING_MODE, VI_EDIT_COMMAND));
}
#endif /* VI_MODE */
static int
-emacs_edit_and_execute_command (count, c)
- int count, c;
+emacs_edit_and_execute_command (int count, int key)
{
- return (edit_and_execute_command (count, c, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
+ return (edit_and_execute_command (count, key, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
}
#if defined (ALIAS)
static int
-posix_edit_macros (count, key)
- int count, key;
+posix_edit_macros (int count, int key)
{
int c;
char alias_name[3], *alias_value, *macro;
c = rl_read_key ();
+ if (c <= 0)
+ return 0;
alias_name[0] = '_';
alias_name[1] = c;
alias_name[2] = '\0';
#define WORDDELIM(c) (shellmeta(c) || shellblank(c))
static int
-bash_forward_shellword (count, key)
- int count, key;
+bash_forward_shellword (int count, int key)
{
size_t slen;
int c, p;
}
static int
-bash_backward_shellword (count, key)
- int count, key;
+bash_backward_shellword (int count, int key)
{
size_t slen;
- int c, p;
+ int c, p, prev_p;
DECLARE_MBSTATE;
if (count < 0)
p = rl_point;
slen = rl_end;
- if (p == rl_end && p > 0)
- p--;
-
while (count)
{
if (p == 0)
return 0;
}
- /* Move backward until we hit a non-metacharacter. */
+ /* Move backward until we hit a non-metacharacter. We want to deal
+ with the characters before point, so we move off a word if we're
+ at its first character. */
+ BACKUP_CHAR (rl_line_buffer, slen, p);
while (p > 0)
{
c = rl_line_buffer[p];
return 0;
}
- /* Now move backward until we hit a metacharacter or BOL. */
+ /* Now move backward until we hit a metacharacter or BOL. Leave point
+ at the start of the shellword or at BOL. */
+ prev_p = p;
while (p > 0)
{
c = rl_line_buffer[p];
if (WORDDELIM (c) && char_is_quoted (rl_line_buffer, p) == 0)
- break;
+ {
+ p = prev_p;
+ break;
+ }
+ prev_p = p;
BACKUP_CHAR (rl_line_buffer, slen, p);
}
}
static int
-bash_kill_shellword (count, key)
- int count, key;
+bash_kill_shellword (int count, int key)
{
int p;
rl_kill_text (p, rl_point);
rl_point = p;
- if (rl_editing_mode == 1) /* 1 == emacs_mode */
+ if (rl_editing_mode == EMACS_EDITING_MODE) /* 1 == emacs_mode */
rl_mark = rl_point;
return 0;
}
static int
-bash_backward_kill_shellword (count, key)
- int count, key;
+bash_backward_kill_shellword (int count, int key)
{
int p;
if (rl_point != p)
rl_kill_text (p, rl_point);
- if (rl_editing_mode == 1) /* 1 == emacs_mode */
+ if (rl_editing_mode == EMACS_EDITING_MODE) /* 1 == emacs_mode */
rl_mark = rl_point;
return 0;
}
+static int
+bash_transpose_shellwords (int count, int key)
+{
+ char *word1, *word2;
+ int w1_beg, w1_end, w2_beg, w2_end;
+ int orig_point = rl_point;
+
+ if (count == 0)
+ return 0;
+
+ /* Find the two shell words. */
+ bash_forward_shellword (count, key);
+ w2_end = rl_point;
+ bash_backward_shellword (1, key);
+ w2_beg = rl_point;
+ bash_backward_shellword (count, key);
+ w1_beg = rl_point;
+ bash_forward_shellword (1, key);
+ w1_end = rl_point;
+
+ /* check that there really are two words. */
+ if ((w1_beg == w2_beg) || (w2_beg < w1_end))
+ {
+ rl_ding ();
+ rl_point = orig_point;
+ return 1;
+ }
+
+ /* Get the text of the words. */
+ word1 = rl_copy_text (w1_beg, w1_end);
+ word2 = rl_copy_text (w2_beg, w2_end);
+
+ /* We are about to do many insertions and deletions. Remember them
+ as one operation. */
+ rl_begin_undo_group ();
+
+ /* Do the stuff at word2 first, so that we don't have to worry
+ about word1 moving. */
+ rl_point = w2_beg;
+ rl_delete_text (w2_beg, w2_end);
+ rl_insert_text (word1);
+
+ rl_point = w1_beg;
+ rl_delete_text (w1_beg, w1_end);
+ rl_insert_text (word2);
+
+ /* This is exactly correct since the text before this point has not
+ changed in length. */
+ rl_point = w2_end;
+
+ /* I think that does it. */
+ rl_end_undo_group ();
+ xfree (word1);
+ xfree (word2);
+
+ return 0;
+}
+
+/* Directory name spelling correction on the current word (not shellword).
+ COUNT > 1 is not exactly correct yet. */
+static int
+bash_spell_correct_shellword (int count, int key)
+{
+ int wbeg, wend;
+ char *text, *newdir;
+
+ while (count)
+ {
+ bash_backward_shellword (1, key);
+ wbeg = rl_point;
+ bash_forward_shellword (1, key);
+ wend = rl_point;
+
+ if (wbeg > wend)
+ break;
+
+ text = rl_copy_text (wbeg, wend);
+ if (text == 0 || *text == 0)
+ break;
+
+ newdir = dirspell (text);
+ if (newdir)
+ {
+ rl_begin_undo_group ();
+ rl_delete_text (wbeg, wend);
+ rl_point = wbeg;
+ if (*newdir)
+ rl_insert_text (newdir);
+ rl_mark = wbeg;
+ rl_end_undo_group ();
+ }
+
+ free (text);
+ free (newdir);
+
+ if (rl_point >= rl_end)
+ break;
+
+ count--;
+
+ if (count)
+ bash_forward_shellword (1, key); /* XXX */
+ }
+
+ return 0;
+}
/* **************************************************************** */
/* */
/* check for redirections and other character combinations that are not
command separators */
-static int
-check_redir (ti)
- int ti;
+static inline int
+check_redir (int ti)
{
- register int this_char, prev_char;
+ int this_char, prev_char, next_char;
/* Handle the two character tokens `>&', `<&', and `>|'.
We are not in a command position after one of these. */
this_char = rl_line_buffer[ti];
prev_char = (ti > 0) ? rl_line_buffer[ti - 1] : 0;
+ next_char = (ti < rl_end) ? rl_line_buffer[ti + 1] : 0;
if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
(this_char == '|' && prev_char == '>'))
return (1);
- else if (this_char == '{' && prev_char == '$') /*}*/
+ else if (this_char == '{' && prev_char == '$' && FUNSUB_CHAR (next_char) == 0) /*}*/
return (1);
#if 0 /* Not yet */
else if (this_char == '(' && prev_char == '$') /*)*/
* happen, but fix later after 2.05a-release.
*/
static int
-find_cmd_start (start)
- int start;
+find_cmd_start (int start)
{
register int s, os, ns;
rl_line_buffer[s])
{
/* Handle >| token crudely; treat as > not | */
- if (rl_line_buffer[s] == '|' && rl_line_buffer[s-1] == '>')
+ if (s > 0 && rl_line_buffer[s] == '|' && rl_line_buffer[s-1] == '>')
{
ns = skip_to_delim (rl_line_buffer, s+1, COMMAND_SEPARATORS, SD_NOJMP|SD_COMPLETE/*|SD_NOSKIPCMD*/);
if (ns > start || rl_line_buffer[ns] == 0)
os = ns+1;
continue;
}
+ /* The only reserved word in COMMAND_SEPARATORS is `{', so handle that
+ specially, making sure it's in a spot acceptable for reserved words */
+ if (s >= os && rl_line_buffer[s] == '{')
+ {
+ int pc, nc; /* index of previous non-whitespace, next char */
+ for (pc = (s > os) ? s - 1 : os; pc > os && whitespace(rl_line_buffer[pc]); pc--)
+ ;
+ nc = rl_line_buffer[s+1];
+ /* must be preceded by a command separator or be the first non-
+ whitespace character since the last command separator, and
+ followed by a shell break character (not another `{') to be a reserved word. */
+ if ((pc > os && (rl_line_buffer[s-1] == '{' || strchr (COMMAND_SEPARATORS, rl_line_buffer[pc]) == 0)) ||
+ (shellbreak(nc) == 0)) /* }} */
+ {
+ /* Not a reserved word, look for another delim */
+ ns = skip_to_delim (rl_line_buffer, s+1, COMMAND_SEPARATORS, SD_NOJMP|SD_COMPLETE/*|SD_NOSKIPCMD*/);
+ if (ns > start || rl_line_buffer[ns] == 0)
+ return os;
+ os = ns+1;
+ continue;
+ }
+ }
os = s+1;
}
return os;
}
static int
-find_cmd_end (end)
- int end;
+find_cmd_end (int end)
{
register int e;
}
static char *
-find_cmd_name (start, sp, ep)
- int start;
- int *sp, *ep;
+find_cmd_name (int start, int *sp, int *ep)
{
char *name;
register int s, e;
}
static char *
-prog_complete_return (text, matchnum)
- const char *text;
- int matchnum;
+prog_complete_return (const char *text, int matchnum)
{
static int ind;
/* Try and catch completion attempts that are syntax errors or otherwise
invalid. */
static int
-invalid_completion (text, ind)
- const char *text;
- int ind;
+invalid_completion (const char *text, int ind)
{
int pind;
/* Do some completion on TEXT. The indices of TEXT in RL_LINE_BUFFER are
at START and END. Return an array of matches, or NULL if none. */
static char **
-attempt_shell_completion (text, start, end)
- const char *text;
- int start, end;
+attempt_shell_completion (const char *text, int start, int end)
{
int in_command_position, ti, qc, dflags;
char **matches, *command_separator_chars;
matches = (char **)NULL;
rl_ignore_some_completions_function = filename_completion_ignore;
+ complete_fullquote = 1; /* full filename quoting by default */
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_set_filename_hooks ();
rl_sort_completion_matches = 1; /* sort by default */
in_command_position++;
if (check_redir (ti) == 1)
- in_command_position = 0;
+ in_command_position = -1; /* sentinel that we're not the first word on the line */
}
else
{
assignments. */
}
- if (in_command_position && invalid_completion (text, ti))
+ if (in_command_position > 0 && invalid_completion (text, ti))
{
rl_attempted_completion_over = 1;
return ((char **)NULL);
/* Check that we haven't incorrectly flagged a closed command substitution
as indicating we're in a command position. */
- if (in_command_position && ti >= 0 && rl_line_buffer[ti] == '`' &&
+ if (in_command_position > 0 && ti >= 0 && rl_line_buffer[ti] == '`' &&
*text != '`' && unclosed_pair (rl_line_buffer, end, "`") == 0)
- in_command_position = 0;
+ in_command_position = -1; /* not following a command separator */
/* Special handling for command substitution. If *TEXT is a backquote,
it can be the start or end of an old-style command substitution, or
succeed. Don't bother if readline found a single quote and we are
completing on the substring. */
if (*text == '`' && rl_completion_quote_character != '\'' &&
- (in_command_position || (unclosed_pair (rl_line_buffer, start, "`") &&
- unclosed_pair (rl_line_buffer, end, "`"))))
+ (in_command_position > 0 || (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)
have_progcomps = prog_completion_enabled && (progcomp_size () > 0);
iw_compspec = progcomp_search (INITIALWORD);
if (matches == 0 &&
- (in_command_position == 0 || text[0] == '\0' || (in_command_position && iw_compspec)) &&
+ (in_command_position == 0 || text[0] == '\0' || (in_command_position > 0 && iw_compspec)) &&
current_prompt_string == ps1_prompt)
{
int s, e, s1, e1, os, foundcs;
s1 = s = e1;
break;
}
+ else if (s > e)
+ {
+ s = s1 = start; e = e1 = end; /* reset */
+ }
/* 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
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 */
+ if (start == 0 && end == 0 && e != 0 && e1 < rl_end && 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 (start == 0 && start == end && start < s1 && e != 0 && e1 > end && text[0] == '\0' && have_progcomps) /* no command name, leading whitespace only */
+ prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs);
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 (start == end && text[0] == '\0' && s1 > start && whitespace (rl_line_buffer[start]))
/* command completion if programmable completion fails */
/* If we have a completion for the initial word, we can prefer that */
in_command_position = s == start && (iw_compspec || STREQ (n, text)); /* XXX */
- foundcs = foundcs && (iw_compspec == 0);
+ if (iw_compspec && in_command_position)
+ foundcs = 0;
}
/* empty command name following command separator */
else if (s >= e && n[0] == '\0' && text[0] == '\0' && start > 0 &&
/* If we have defined a compspec for the initial (command) word, call
it and process the results like any other programmable completion. */
if (in_command_position && have_progcomps && foundcs == 0 && iw_compspec)
- prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+ {
+ /* Do some sanity checking on S and E. Room for more here. */
+ if (s > rl_point)
+ s = rl_point;
+ if (e > rl_end)
+ e = rl_end;
+ prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+ }
FREE (n);
/* XXX - if we found a COMPSPEC for the command, just return whatever
if (matches == 0)
{
dflags = 0;
- if (in_command_position)
+ if (in_command_position > 0)
dflags |= DEFCOMP_CMDPOS;
matches = bash_default_completion (text, start, end, qc, dflags);
}
}
char **
-bash_default_completion (text, start, end, qc, compflags)
- const char *text;
- int start, end, qc, compflags;
+bash_default_completion (const char *text, int start, int end, int qc, int compflags)
{
char **matches, *t;
{
if (qc != '\'' && text[1] == '(') /* ) */
matches = rl_completion_matches (text, command_subst_completion_function);
+ else if (qc != '\'' && text[1] == '{' && FUNSUB_CHAR (text[2])) /* } */
+ matches = rl_completion_matches (text, command_subst_completion_function);
else
{
matches = rl_completion_matches (text, variable_completion_function);
rl_completion_suppress_append = 1;
rl_filename_completion_desired = 0;
}
+#if 1
+ else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) &&
+ matches[2] && STREQ (matches[1], matches[2]) && CMD_IS_DIR (matches[0]))
+#else
else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0]))
+#endif
/* There are multiple instances of the same match (duplicate
completions haven't yet been removed). In this case, all of
the matches will be the same, and the duplicate removal code
/* This could be a globbing pattern, so try to expand it using pathname
expansion. */
- if (!matches && glob_pattern_p (text))
+ if (!matches && completion_glob_pattern (text))
{
matches = rl_completion_matches (text, glob_complete_word);
/* A glob expression that matches more than one filename is problematic.
}
static int
-bash_command_name_stat_hook (name)
- char **name;
+bash_command_name_stat_hook (char **name)
{
char *cname, *result;
result = search_for_command (cname, 0);
if (result)
{
+ FREE (*name);
*name = result;
return 1;
}
}
static int
-executable_completion (filename, searching_path)
- const char *filename;
- int searching_path;
+executable_completion (const char *filename, int searching_path)
{
- char *f;
+ char *f, c;
int r;
- f = savestring (filename);
+ /* This gets an unquoted filename, so we need to quote special characters
+ in the filename before the completion hook gets it. */
+ c = 0;
+ f = bash_quote_filename ((char *)filename, SINGLE_MATCH, &c);
bash_directory_completion_hook (&f);
r = searching_path ? executable_file (f) : executable_or_directory (f);
that match. It also scans aliases, function names, and the shell_builtin
table. */
char *
-command_word_completion_function (hint_text, state)
- const char *hint_text;
- int state;
+command_word_completion_function (const char *hint_text, int state)
{
static char *hint = (char *)NULL;
static char *path = (char *)NULL;
static char *dequoted_hint = (char *)NULL;
static char *directory_part = (char *)NULL;
static char **glob_matches = (char **)NULL;
- static int path_index, hint_len, istate, igncase;
+ static int path_index, istate, igncase;
+ static size_t hint_len;
static int mapping_over, local_index, searching_path, hint_is_dir;
static int old_glob_ignore_case, globpat;
static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
free (dequoted_hint);
if (hint)
free (hint);
+ dequoted_hint = hint = (char *)NULL;
mapping_over = searching_path = 0;
hint_is_dir = CMD_IS_DIR (hint_text);
temp = rl_variable_value ("completion-ignore-case");
igncase = RL_BOOLEAN_VARIABLE_VALUE (temp);
+ old_glob_ignore_case = glob_ignore_case;
+
if (glob_matches)
{
free (glob_matches);
glob_matches = (char **)NULL;
}
- globpat = glob_pattern_p (hint_text);
+ globpat = completion_glob_pattern (hint_text);
/* If this is an absolute program name, do not check it against
aliases, reserved words, functions or builtins. We must check
if (rl_completion_found_quote && rl_completion_quote_character == 0)
dequoted_hint = bash_dequote_filename (hint, 0);
- path = get_string_value ("PATH");
+ path = path_value ("PATH", 0);
path_index = dot_in_path = 0;
/* Initialize the variables for each type of command word. */
{
if (state == 0)
{
+ rl_filename_completion_desired = 1;
+
glob_ignore_case = igncase;
- glob_matches = shell_glob_filename (hint);
+ glob_matches = shell_glob_filename (hint, 0);
glob_ignore_case = old_glob_ignore_case;
if (GLOB_FAILED (glob_matches) || glob_matches == 0)
local_index = 0;
if (glob_matches[1] && rl_completion_type == TAB) /* multiple matches are bad */
- return ((char *)NULL);
+ {
+ strvec_dispose (glob_matches);
+ glob_matches = (char **)NULL;
+ return ((char *)NULL);
+ }
}
while (val = glob_matches[local_index++])
free (fnhint);
if (filename_hint)
free (filename_hint);
+ fnhint = filename_hint = (char *)NULL;
filename_hint = sh_makepath (current_path, hint, 0);
/* Need a quoted version (though it doesn't matter much in most
if (absolute_program (hint))
{
+#if 0
if (igncase == 0)
match = strncmp (val, hint, hint_len) == 0;
else
match = strncasecmp (val, hint, hint_len) == 0;
+#else
+ /* Why duplicate the comparison rl_filename_completion_function
+ already performs? */
+ match = 1;
+#endif
/* If we performed tilde expansion, restore the original
filename. */
t1 = make_absolute (val, t);
free (t);
cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+ free (t1);
}
else
#endif
/* Completion inside an unterminated command substitution. */
static char *
-command_subst_completion_function (text, state)
- const char *text;
- int state;
+command_subst_completion_function (const char *text, int state)
{
static char **matches = (char **)NULL;
static const char *orig_start;
text++;
else if (*text == '$' && text[1] == '(') /* ) */
text += 2;
+ else if (*text == '$' && text[1] == '{' && FUNSUB_CHAR (text[2])) /*}*/
+ text += 3; /* nofork command substitution */
/* If the text was quoted, suppress any quote character that the
readline completion code would insert. */
rl_completion_suppress_quote = 1;
start_len = text - orig_start;
filename_text = savestring (text);
if (matches)
- free (matches);
+ {
+ free (matches);
+ matches = (char **)NULL;
+ }
/*
* At this point we can entertain the idea of re-parsing
/* Okay, now we write the entry_function for variable completion. */
static char *
-variable_completion_function (text, state)
- const char *text;
- int state;
+variable_completion_function (const char *text, int state)
{
static char **varlist = (char **)NULL;
static int varlist_index;
/* How about a completion function for hostnames? */
static char *
-hostname_completion_function (text, state)
- const char *text;
- int state;
+hostname_completion_function (const char *text, int state)
{
static char **list = (char **)NULL;
static int list_index = 0;
* A completion function for service names from /etc/services (or wherever).
*/
char *
-bash_servicename_completion_function (text, state)
- const char *text;
- int state;
+bash_servicename_completion_function (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;
+ static size_t snamelen;
char *value;
char **alist, *aentry;
int afound;
* A completion function for group names from /etc/group (or wherever).
*/
char *
-bash_groupname_completion_function (text, state)
- const char *text;
- int state;
+bash_groupname_completion_function (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;
+ static size_t gnamelen;
char *value;
if (state == 0)
is done, pre_process_line() returns what it was passed, so we need to
allocate a new line here. */
static char *
-history_expand_line_internal (line)
- char *line;
+history_expand_line_internal (char *line)
{
char *new_line;
int old_verify;
/* There was an error in expansion. Let the preprocessor print
the error here. */
static void
-cleanup_expansion_error ()
+cleanup_expansion_error (void)
{
char *to_free;
#if defined (BANG_HISTORY)
undo record to get from the readline line buffer contents to the new
line and make NEW_LINE the current readline line. */
static void
-maybe_make_readline_line (new_line)
- char *new_line;
+maybe_make_readline_line (const char *new_line)
{
if (new_line && strcmp (new_line, rl_line_buffer) != 0)
{
}
}
-/* Make NEW_LINE be the current readline line. This frees NEW_LINE. */
+/* Make NEW_LINE be the current readline line. This frees NEW_LINE. We use
+ several heuristics to decide where to put rl_point. */
+
static void
-set_up_new_line (new_line)
- char *new_line;
+set_up_new_line (char *new_line)
{
- int old_point, at_end;
+
+ int old_point, old_end, dist, nb;
+ char *s, *t;
+
+ /* If we didn't expand anything, don't change anything. */
+ if (STREQ (new_line, rl_line_buffer))
+ {
+ free (new_line);
+ return;
+ }
old_point = rl_point;
- at_end = rl_point == rl_end;
+ old_end = rl_end;
+ dist = rl_end - rl_point;
+ nb = rl_end - old_end;
/* If the line was history and alias expanded, then make that
be one thing to undo. */
free (new_line);
/* Place rl_point where we think it should go. */
- if (at_end)
+ if (dist == 0)
rl_point = rl_end;
- else if (old_point < rl_end)
+ else if (old_point == 0)
+ {
+ /* this is what the old code did, but separate it out so we can treat
+ it differently */
+ rl_point = 0;
+ if (!whitespace (rl_line_buffer[rl_point]))
+ rl_forward_word (1, 0);
+ }
+ else if (rl_point < old_point+nb)
{
+ /* let's assume that this means the new point is within the changed region */
rl_point = old_point;
if (!whitespace (rl_line_buffer[rl_point]))
rl_forward_word (1, 0);
}
+ else
+ rl_point = rl_end - dist; /* try to put point the same distance from end */
+
+ if (rl_point < 0)
+ rl_point = 0;
+ else if (rl_point > rl_end)
+ rl_point = rl_end;
+ rl_mark = 0; /* XXX */
}
#if defined (ALIAS)
/* Expand aliases in the current readline line. */
static int
-alias_expand_line (count, ignore)
- int count, ignore;
+alias_expand_line (int count, int ignore)
{
char *new_line;
#if defined (BANG_HISTORY)
/* History expand the line. */
static int
-history_expand_line (count, ignore)
- int count, ignore;
+history_expand_line (int count, int ignore)
{
char *new_line;
/* Expand history substitutions in the current line and then insert a
space (hopefully close to where we were before). */
static int
-tcsh_magic_space (count, ignore)
- int count, ignore;
+tcsh_magic_space (int count, int ignore)
{
int dist_from_end, old_point;
/* History and alias expand the line. */
static int
-history_and_alias_expand_line (count, ignore)
- int count, ignore;
+history_and_alias_expand_line (int count, int ignore)
{
char *new_line;
because we want the variable expansions as a separate undo'able
set of operations. */
static int
-shell_expand_line (count, ignore)
- int count, ignore;
+shell_expand_line (int count, int ignore)
{
- char *new_line;
+ char *new_line, *t;
WORD_LIST *expanded_string;
WORD_DESC *w;
new_line = history_expand_line_internal (rl_line_buffer);
#endif
+ t = expand_string_dollar_quote (new_line ? new_line : rl_line_buffer, 0);
+ FREE (new_line);
+ new_line = t;
+
#if defined (ALIAS)
if (new_line)
{
};
static void
-_ignore_completion_names (names, name_func)
- char **names;
- sh_ignore_func_t *name_func;
+_ignore_completion_names (char **names, sh_ignore_func_t *name_func)
{
char **newnames;
- int idx, nidx;
+ size_t idx, nidx;
char **oldnames;
int oidx;
}
static int
-name_is_acceptable (name)
- const char *name;
+name_is_acceptable (const char *name)
{
struct ign *p;
- int nlen;
+ size_t 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]))
+ if (nlen >= p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
return (0);
}
#if 0
static int
-ignore_dot_names (name)
- char *name;
+ignore_dot_names (const char *name)
{
return (name[0] != '.');
}
#endif
static int
-filename_completion_ignore (names)
- char **names;
+filename_completion_ignore (char **names)
{
#if 0
if (glob_dot_filenames == 0)
/* Return 1 if NAME is a directory. NAME undergoes tilde expansion. */
static int
-test_for_directory (name)
- const char *name;
+test_for_directory (const char *name)
{
char *fn;
int r;
fn = bash_tilde_expand (name, 0);
+
+#if __CYGWIN
+ /* stat("//server") can only be successful as a directory, but can take
+ seconds to time out on failure. It is much faster to assume that
+ "//server" is a valid name than it is to wait for a stat, even if it
+ gives false positives on bad names. */
+ if (fn[0] == '/' && fn[1] == '/' && ! strchr (&fn[2], '/'))
+ {
+ free (fn);
+ return 1;
+ }
+#endif
+
+ r = file_isdir (fn);
+ free (fn);
+
+ return (r);
+}
+
+static int
+test_for_canon_directory (const char *name)
+{
+ char *fn;
+ int r;
+
+ fn = (*name == '~') ? bash_tilde_expand (name, 0) : savestring (name);
+ bash_filename_stat_hook (&fn);
r = file_isdir (fn);
free (fn);
/* Remove files from NAMES, leaving directories. */
static int
-bash_ignore_filenames (names)
- char **names;
+bash_ignore_filenames (char **names)
{
_ignore_completion_names (names, test_for_directory);
return 0;
}
static int
-return_zero (name)
- const char *name;
+bash_progcomp_ignore_filenames (char **names)
+{
+ _ignore_completion_names (names, test_for_canon_directory);
+ return 0;
+}
+
+static int
+return_zero (const char *name)
{
return 0;
}
static int
-bash_ignore_everything (names)
- char **names;
+bash_ignore_everything (char **names)
{
_ignore_completion_names (names, return_zero);
return 0;
is an expanded filename. DIRECTORY_PART is the tilde-prefix portion
of the un-tilde-expanded version of VAL (what the user typed). */
static char *
-restore_tilde (val, directory_part)
- char *val, *directory_part;
+restore_tilde (const char *val, char *directory_part)
{
- int l, vl, dl2, xl;
+ size_t l, vl, dl2, xl;
char *dh2, *expdir, *ret, *v;
vl = strlen (val);
}
static char *
-maybe_restore_tilde (val, directory_part)
- char *val, *directory_part;
+maybe_restore_tilde (char *val, char *directory_part)
{
rl_icppfunc_t *save;
char *ret;
rl_filename_completion_function. This must be called with the address of
a pointer to malloc'd memory. */
static void
-bash_directory_expansion (dirname)
- char **dirname;
+bash_directory_expansion (char **dirname)
{
char *d, *nd;
free (d);
*dirname = nd;
}
+ else
+ free (d);
}
/* If necessary, rewrite directory entry */
static char *
-bash_filename_rewrite_hook (fname, fnlen)
- char *fname;
- int fnlen;
+bash_filename_rewrite_hook (char *fname, int fnlen)
{
char *conv;
/* Functions to save and restore the appropriate directory hook */
/* This is not static so the shopt code can call it */
void
-set_directory_hook ()
+set_directory_hook (void)
{
if (dircomplete_expand)
{
}
static rl_icppfunc_t *
-save_directory_hook ()
+save_directory_hook (void)
{
rl_icppfunc_t *ret;
}
static void
-restore_directory_hook (hookf)
- rl_icppfunc_t *hookf;
+restore_directory_hook (rl_icppfunc_t *hookf)
{
if (dircomplete_expand)
rl_directory_completion_hook = hookf;
rl_directory_rewrite_hook = hookf;
}
+/* Set the readline hooks that affect how directories and filenames are
+ converted. Extern so other parts of the shell can use. */
+void
+bashline_set_filename_hooks (void)
+{
+ /* Tell the completer that we might want to follow symbolic links or
+ do other expansion on directory names. */
+ set_directory_hook ();
+
+ rl_filename_rewrite_hook = bash_filename_rewrite_hook;
+ rl_completion_rewrite_hook = bash_filename_rewrite_hook;
+ rl_filename_stat_hook = bash_filename_stat_hook;
+}
+
/* Check whether not DIRNAME, with any trailing slash removed, exists. If
SHOULD_DEQUOTE is non-zero, we dequote the directory name first. */
static int
-directory_exists (dirname, should_dequote)
- const char *dirname;
- int should_dequote;
+directory_exists (const char *dirname, int should_dequote)
{
char *new_dirname;
int dirlen, r;
free (new_dirname);
return (r);
}
+
+static char *
+bash_expand_filename (char *filename)
+{
+ char *newname;
+ int global_nounset;
+ WORD_LIST *wl;
+
+ newname = savestring (filename);
+ /* no error messages, and expand_prompt_string doesn't longjmp so we don't
+ have to worry about restoring this setting. */
+ global_nounset = unbound_vars_is_error;
+ unbound_vars_is_error = 0;
+ wl = expand_prompt_string (newname, 0, W_NOCOMSUB|W_NOPROCSUB|W_COMPLETE); /* does the right thing */
+ unbound_vars_is_error = global_nounset;
+ free (newname);
+
+ if (wl == 0)
+ return filename;
+ else
+ {
+ newname = string_list (wl);
+ dispose_words (wl);
+ if (newname && *newname && STREQ (newname, filename))
+ {
+ free (newname);
+ return filename;
+ }
+ return newname;
+ }
+}
/* 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;
+bash_filename_stat_hook (char **dirname)
{
char *local_dirname, *new_dirname, *t;
int should_expand_dirname, return_value;
the DIRNAME argument, 0 otherwise. It should make sure not to modify
DIRNAME if it returns 0. */
static int
-bash_directory_completion_hook (dirname)
- char **dirname;
+bash_directory_completion_hook (char **dirname)
{
char *local_dirname, *new_dirname, *t;
int return_value, should_expand_dirname, nextch, closer;
return_value = should_expand_dirname = nextch = closer = 0;
local_dirname = *dirname;
- if (t = mbschr (local_dirname, '$'))
- {
- should_expand_dirname = '$';
- nextch = t[1];
- /* Deliberately does not handle the deprecated $[...] arithmetic
- expansion syntax */
- if (nextch == '(')
- closer = ')';
- else if (nextch == '{')
- closer = '}';
- else
- nextch = 0;
-
- if (closer)
- {
- int p;
- char delims[2];
-
- delims[0] = closer; delims[1] = 0;
- p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE);
- if (t[p] != closer)
- should_expand_dirname = 0;
- }
- }
- else if (local_dirname[0] == '~')
- should_expand_dirname = '~';
- else
- {
- t = mbschr (local_dirname, '`');
- if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
- should_expand_dirname = '`';
- }
+ should_expand_dirname = bash_check_expchar (local_dirname, 1, &nextch, &closer);
if (should_expand_dirname && directory_exists (local_dirname, 1))
should_expand_dirname = 0;
free (new_dirname);
dispose_words (wl);
local_dirname = *dirname;
- /* XXX - change rl_filename_quote_characters here based on
- should_expand_dirname/nextch/closer. This is the only place
- custom_filename_quote_characters is modified. */
- if (rl_filename_quote_characters && *rl_filename_quote_characters)
- {
- int i, j, c;
- i = strlen (default_filename_quote_characters);
- custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
- for (i = j = 0; c = default_filename_quote_characters[i]; i++)
- {
- if (c == should_expand_dirname || c == nextch || c == closer)
- continue;
- custom_filename_quote_characters[j++] = c;
- }
- custom_filename_quote_characters[j] = '\0';
- rl_filename_quote_characters = custom_filename_quote_characters;
- set_filename_bstab (rl_filename_quote_characters);
- }
+
+ set_filename_quote_chars (should_expand_dirname, nextch, closer);
}
else
{
if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
{
char *temp1, *temp2;
- int len1, len2;
+ size_t len1, len2;
/* If we have a relative path
(local_dirname[0] != '/' && local_dirname[0] != '.')
subsequent directory checks don't fail. */
if (temp2 == 0 && dircomplete_spelling && dircomplete_expand)
{
+ size_t l1, l2;
+
temp2 = dirspell (temp1);
+ l2 = STRLEN (temp2);
+ /* Don't take matches if they are shorter than the original path */
+ if (temp2 && l2 < strlen (temp1) && STREQN (temp1, temp2, l2))
+ {
+ free (temp2);
+ temp2 = 0;
+ }
if (temp2)
{
free (temp1);
}
static char **history_completion_array = (char **)NULL;
-static int harry_size;
-static int harry_len;
+static size_t harry_size;
+static size_t harry_len;
static void
-build_history_completion_array ()
+build_history_completion_array (void)
{
register int i, j;
HIST_ENTRY **hlist;
}
static char *
-history_completion_generator (hint_text, state)
- const char *hint_text;
- int state;
+history_completion_generator (const char *hint_text, int state)
{
- static int local_index, len;
+ static int local_index;
+ static size_t len;
static const char *text;
/* If this is the first call to the generator, then initialize the
}
static int
-dynamic_complete_history (count, key)
- int count, key;
+dynamic_complete_history (int count, int key)
{
int r;
rl_compentry_func_t *orig_func;
}
static int
-bash_dabbrev_expand (count, key)
- int count, key;
+bash_dabbrev_expand (int count, int key)
{
int r, orig_suppress, orig_sort;
rl_compentry_func_t *orig_func;
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int
-bash_complete_username (ignore, ignore2)
- int ignore, ignore2;
+bash_complete_username (int ignore, int ignore2)
{
return bash_complete_username_internal (rl_completion_mode (bash_complete_username));
}
static int
-bash_possible_username_completions (ignore, ignore2)
- int ignore, ignore2;
+bash_possible_username_completions (int ignore, int ignore2)
{
return bash_complete_username_internal ('?');
}
static int
-bash_complete_username_internal (what_to_do)
- int what_to_do;
+bash_complete_username_internal (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;
+bash_complete_filename (int ignore, int ignore2)
{
return bash_complete_filename_internal (rl_completion_mode (bash_complete_filename));
}
static int
-bash_possible_filename_completions (ignore, ignore2)
- int ignore, ignore2;
+bash_possible_filename_completions (int ignore, int ignore2)
{
return bash_complete_filename_internal ('?');
}
static int
-bash_complete_filename_internal (what_to_do)
- int what_to_do;
+bash_complete_filename_internal (int what_to_do)
{
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
rl_icppfunc_t *orig_dir_func;
rl_compignore_func_t *orig_ignore_func;
- /*const*/ char *orig_rl_completer_word_break_characters;
+ const char *orig_rl_completer_word_break_characters;
int r;
orig_func = rl_completion_entry_function;
}
static int
-bash_complete_hostname (ignore, ignore2)
- int ignore, ignore2;
+bash_complete_hostname (int ignore, int ignore2)
{
return bash_complete_hostname_internal (rl_completion_mode (bash_complete_hostname));
}
static int
-bash_possible_hostname_completions (ignore, ignore2)
- int ignore, ignore2;
+bash_possible_hostname_completions (int ignore, int ignore2)
{
return bash_complete_hostname_internal ('?');
}
static int
-bash_complete_variable (ignore, ignore2)
- int ignore, ignore2;
+bash_complete_variable (int ignore, int ignore2)
{
return bash_complete_variable_internal (rl_completion_mode (bash_complete_variable));
}
static int
-bash_possible_variable_completions (ignore, ignore2)
- int ignore, ignore2;
+bash_possible_variable_completions (int ignore, int ignore2)
{
return bash_complete_variable_internal ('?');
}
static int
-bash_complete_command (ignore, ignore2)
- int ignore, ignore2;
+bash_complete_command (int ignore, int ignore2)
{
return bash_complete_command_internal (rl_completion_mode (bash_complete_command));
}
static int
-bash_possible_command_completions (ignore, ignore2)
- int ignore, ignore2;
+bash_possible_command_completions (int ignore, int ignore2)
{
return bash_complete_command_internal ('?');
}
static int
-bash_complete_hostname_internal (what_to_do)
- int what_to_do;
+bash_complete_hostname_internal (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;
+bash_complete_variable_internal (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;
+bash_complete_command_internal (int what_to_do)
{
return bash_specific_completion (what_to_do, command_word_completion_function);
}
+static int
+completion_glob_pattern (const char *string)
+{
+ return (glob_pattern_p (string) == 1);
+}
+
static char *globtext;
static char *globorig;
static char *
-glob_complete_word (text, state)
- const char *text;
- int state;
+glob_complete_word (const char *text, int state)
{
static char **matches = (char **)NULL;
static int ind;
- int glen;
+ size_t glen;
char *ret, *ttext;
if (state == 0)
{
rl_filename_completion_desired = 1;
FREE (matches);
+ matches = (char **)NULL;
if (globorig != globtext)
FREE (globorig);
FREE (globtext);
+ globorig = globtext = (char *)NULL;
ttext = bash_tilde_expand (text, 0);
if (ttext != text)
free (ttext);
- matches = shell_glob_filename (globtext);
+ matches = shell_glob_filename (globtext, 0);
if (GLOB_FAILED (matches))
matches = (char **)NULL;
ind = 0;
}
static int
-bash_glob_completion_internal (what_to_do)
- int what_to_do;
+bash_glob_completion_internal (int what_to_do)
{
return bash_specific_completion (what_to_do, glob_complete_word);
}
/* A special quoting function so we don't end up quoting globbing characters
in the word if there are no matches or multiple matches. */
static char *
-bash_glob_quote_filename (s, rtype, qcp)
- char *s;
- int rtype;
- char *qcp;
+bash_glob_quote_filename (char *s, int rtype, char *qcp)
{
if (globorig && qcp && *qcp == '\0' && STREQ (s, globorig))
return (savestring (s));
}
static int
-bash_glob_complete_word (count, key)
- int count, key;
+bash_glob_complete_word (int count, int key)
{
int r;
rl_quote_func_t *orig_quoting_function;
}
static int
-bash_glob_expand_word (count, key)
- int count, key;
+bash_glob_expand_word (int count, int key)
{
return bash_glob_completion_internal ('*');
}
static int
-bash_glob_list_expansions (count, key)
- int count, key;
+bash_glob_list_expansions (int count, int key)
{
return bash_glob_completion_internal ('?');
}
static int
-bash_specific_completion (what_to_do, generator)
- int what_to_do;
- rl_compentry_func_t *generator;
+bash_specific_completion (int what_to_do, rl_compentry_func_t *generator)
{
rl_compentry_func_t *orig_func;
rl_completion_func_t *orig_attempt_func;
#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
#if defined (VI_MODE)
+/* This does pretty much what _rl_vi_advance_point does. */
+static inline int
+vi_advance_point (void)
+{
+ int point;
+ DECLARE_MBSTATE;
+
+ point = rl_point;
+ if (rl_point < rl_end)
+#if defined (HANDLE_MULTIBYTE)
+ {
+ if (locale_mb_cur_max == 1)
+ rl_point++;
+ else
+ {
+ point = rl_point;
+ ADVANCE_CHAR (rl_line_buffer, rl_end, rl_point);
+ if (point == rl_point || rl_point > rl_end)
+ rl_point = rl_end;
+ }
+ }
+#else
+ rl_point++:
+#endif
+ return point;
+}
+
/* Completion, from vi mode's point of view. This is a modified version of
rl_vi_complete which uses the bash globbing code to implement what POSIX
- specifies, which is to append a `*' and attempt filename generation (which
- has the side effect of expanding any globbing characters in the word). */
+ specifies, which is to optinally append a `*' and attempt filename
+ generation (which has the side effect of expanding any globbing characters
+ in the word). */
static int
-bash_vi_complete (count, key)
- int count, key;
+bash_vi_complete (int count, int key)
{
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
int p, r;
{
if (!whitespace (rl_line_buffer[rl_point + 1]))
rl_vi_end_word (1, 'E');
- rl_point++;
+ vi_advance_point ();
}
/* Find boundaries of current word, according to vi definition of a
t = substring (rl_line_buffer, p, rl_point);
}
- if (t && glob_pattern_p (t) == 0)
+ if (t && completion_glob_pattern (t) == 0)
rl_explicit_arg = 1; /* XXX - force glob_complete_word to append `*' */
FREE (t);
quotes, and backslashes). It allows single quotes to appear
within double quotes, and vice versa. It should be smarter. */
static char *
-bash_dequote_filename (text, quote_char)
- char *text;
- int quote_char;
+bash_dequote_filename (char *text, int quote_char)
{
char *ret, *p, *r;
- int l, quoted;
+ int quoted;
+ size_t l;
l = strlen (text);
ret = (char *)xmalloc (l + 1);
for (quoted = quote_char, p = text, r = ret; p && *p; p++)
{
- /* Allow backslash-escaped characters to pass through unscathed. */
- if (*p == '\\')
+ /* Allow backslash-escaped characters to pass through unscathed. Backslashes
+ aren't special in single quotes. */
+ if (quoted != '\'' && *p == '\\')
{
- /* Backslashes are preserved within single quotes. */
- if (quoted == '\'')
- *r++ = *p;
/* Backslashes are preserved within double quotes unless the
character is one that is defined to be escaped */
- else if (quoted == '"' && ((sh_syntaxtab[(unsigned char)p[1]] & CBSDQUOTE) == 0))
+ if (quoted == '"' && ((sh_syntaxtab[(unsigned char)p[1]] & CBSDQUOTE) == 0))
*r++ = *p;
*r++ = *++p;
word break characters with backslashes. Pass backslash-quoted
characters through without examination. */
static char *
-quote_word_break_chars (text)
- char *text;
+quote_word_break_chars (char *text)
{
char *ret, *r, *s;
- int l;
+ size_t l;
l = strlen (text);
ret = (char *)xmalloc ((2 * l) + 1);
return ret;
}
+/* Return a character in DIRNAME that will cause shell expansion to be
+ performed. If NEXTP is non-null, *NEXTP gets the expansion character that
+ follows RET (e.g., '{' or `(' for `$'). If CLOSERP is non-null, *CLOSERP
+ gets the character that should close <RET><NEXTP>. If NEED_CLOSER is non-
+ zero, any expansion pair that isn't closed causes this function to
+ return 0, which indicates that we didn't find an expansion character. It's
+ used in case DIRNAME is going to be expanded. If DIRNAME is just going to
+ be quoted, NEED_CLOSER will be 0. */
+static int
+bash_check_expchar (char *dirname, int need_closer, int *nextp, int *closerp)
+{
+ char *t;
+ int ret, n, c;
+
+ ret = n = c = 0;
+ if (t = mbschr (dirname, '$'))
+ {
+ ret = '$';
+ n = t[1];
+ /* Deliberately does not handle the deprecated $[...] arithmetic
+ expansion syntax */
+ if (n == '(')
+ c = ')';
+ else if (n == '{')
+ c = '}';
+ else
+ n = 0;
+
+ if (c && need_closer) /* XXX */
+ {
+ int p;
+ char delims[2];
+
+ delims[0] = c; delims[1] = 0;
+ p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE);
+ if (t[p] != c)
+ ret = 0;
+ }
+ }
+ else if (dirname[0] == '~')
+ ret = '~';
+ else
+ {
+ t = mbschr (dirname, '`');
+ if (t)
+ {
+ if (need_closer == 0)
+ ret = '`';
+ else if (unclosed_pair (dirname, strlen (dirname), "`") == 0)
+ ret = '`';
+ }
+ }
+
+ if (nextp)
+ *nextp = n;
+ if (closerp)
+ *closerp = c;
+
+ return ret;
+}
+
+/* Make sure EXPCHAR and, if non-zero, NEXTCH and CLOSER are not in the set
+ of characters to be backslash-escaped. This is the only place
+ custom_filename_quote_characters is modified. */
+static void
+set_filename_quote_chars (int expchar, int nextch, int closer)
+{
+ size_t i, j;
+ int c;
+
+ if (rl_filename_quote_characters && *rl_filename_quote_characters)
+ {
+ i = strlen (default_filename_quote_characters);
+ custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
+ for (i = j = 0; c = default_filename_quote_characters[i]; i++)
+ {
+ if (c == expchar || c == nextch || c == closer)
+ continue;
+ custom_filename_quote_characters[j++] = c;
+ }
+ custom_filename_quote_characters[j] = '\0';
+ rl_filename_quote_characters = custom_filename_quote_characters;
+ set_filename_bstab (rl_filename_quote_characters);
+ }
+}
+
/* 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;
+set_filename_bstab (const char *string)
{
const char *s;
memset (filename_bstab, 0, sizeof (filename_bstab));
for (s = string; s && *s; s++)
- filename_bstab[*s] = 1;
+ filename_bstab[(unsigned char)*s] = 1;
}
/* Quote a filename using double quotes, single quotes, or backslashes
quote_word_break_chars on the result. This returns newly-allocated
memory. */
static char *
-bash_quote_filename (s, rtype, qcp)
- char *s;
- int rtype;
- char *qcp;
+bash_quote_filename (char *s, int rtype, char *qcp)
{
char *rtext, *mtext, *ret;
- int rlen, cs;
+ size_t rlen;
+ int cs, expchar, nextch, closer;
rtext = (char *)NULL;
the word being completed contains newlines, since those are not
quoted correctly using backslashes (a backslash-newline pair is
special to the shell parser). */
- if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n'))
+ expchar = nextch = closer = 0;
+ if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && dircomplete_expand == 0 &&
+ (expchar = bash_check_expchar (s, 0, &nextch, &closer)) &&
+ file_exists (s) == 0)
+ {
+ /* If it looks like the name is subject to expansion, see if we want to
+ double-quote it. */
+ if (expchar == '$' || expchar == '`')
+ {
+ char *newname;
+ newname = bash_expand_filename (s);
+ if (newname && strpbrk (newname, rl_filename_quote_characters))
+ cs = COMPLETE_DQUOTE2;
+ if (newname != s)
+ free (newname);
+ }
+ /* Usually this will have been set by bash_directory_completion_hook,
+ but there are cases where it will not be. */
+ if (rl_filename_quote_characters != custom_filename_quote_characters)
+ set_filename_quote_chars (expchar, nextch, closer);
+ complete_fullquote = 0;
+ }
+ else if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n'))
cs = COMPLETE_SQUOTE;
else if (*qcp == '"')
- cs = COMPLETE_DQUOTE;
+ {
+ if ((expchar = bash_check_expchar (s, 0, &nextch, &closer)) == '$' || expchar == '`')
+ cs = COMPLETE_DQUOTE2;
+ else
+ cs = COMPLETE_DQUOTE;
+ }
else if (*qcp == '\'')
cs = COMPLETE_SQUOTE;
#if defined (BANG_HISTORY)
switch (cs)
{
+ case COMPLETE_DQUOTE2:
+ rtext = sh_mkdoublequoted (mtext, strlen (mtext), 1); /* For now */
+ break;
case COMPLETE_DQUOTE:
rtext = sh_double_quote (mtext);
break;
/* We may need to quote additional characters: those that readline treats
as word breaks that are not quoted by backslash_quote. */
- if (rtext && cs == COMPLETE_BSQUOTE)
+ /* XXX - test complete_fullquote here? */
+ if (rtext && cs == COMPLETE_BSQUOTE && rl_completer_word_break_characters)
{
mtext = quote_word_break_chars (rtext);
free (rtext);
return ret;
}
-/* Support for binding readline key sequences to Unix commands. */
-static Keymap cmd_xmap;
+/* Support for binding readline key sequences to Unix commands. Each editing
+ mode has a separate Unix command keymap. */
+
+static Keymap emacs_std_cmd_xmap;
+#if defined (VI_MODE)
+static Keymap vi_insert_cmd_xmap;
+static Keymap vi_movement_cmd_xmap;
+#endif
#ifdef _MINIX
static void
#else
static int
#endif
-putx(c)
- int c;
+putx(int c)
{
int x;
x = putc (c, rl_outstream);
return x;
#endif
}
-
+
static int
-bash_execute_unix_command (count, key)
- int count; /* ignored */
- int key;
+readline_get_char_offset (int ind)
+{
+ int r, old_ch;
+
+ r = ind;
+#if defined (HANDLE_MULTIBYTE)
+ if (locale_mb_cur_max > 1)
+ {
+ old_ch = rl_line_buffer[ind];
+ rl_line_buffer[ind] = '\0';
+ r = MB_STRLEN (rl_line_buffer);
+ rl_line_buffer[ind] = old_ch;
+ }
+#endif
+ return r;
+}
+
+static void
+readline_set_char_offset (int ind, int *varp)
+{
+ int i;
+
+ i = ind;
+
+#if defined (HANDLE_MULTIBYTE)
+ if (i > 0 && locale_mb_cur_max > 1)
+ i = _rl_find_next_mbchar (rl_line_buffer, 0, i, 0); /* XXX */
+#endif
+ if (i != *varp)
+ {
+ if (i > rl_end)
+ i = rl_end;
+ else if (i < 0)
+ i = 0;
+ *varp = i;
+ }
+}
+
+void
+uw_restore_parser_state (void *ps)
+{
+ restore_parser_state (ps);
+}
+
+void
+uw_rl_set_signals (void *ignore)
+{
+ rl_set_signals ();
+}
+
+static void
+unbind_readline_variables (void)
+{
+ check_unbind_variable ("READLINE_LINE");
+ check_unbind_variable ("READLINE_POINT");
+ check_unbind_variable ("READLINE_MARK");
+ check_unbind_variable ("READLINE_ARGUMENT");
+ array_needs_making = 1;
+}
+
+static void
+uw_unbind_readline_variables (void *ignore)
+{
+ unbind_readline_variables ();
+}
+
+int
+bash_execute_unix_command (int count, int key)
{
int type;
register int i, r;
intmax_t mi;
sh_parser_state_t ps;
- char *cmd, *value, *ce, old_ch;
+ char *cmd, *value, *ce;
SHELL_VAR *v;
char ibuf[INT_STRLEN_BOUND(int) + 1];
+ Keymap cmd_xmap;
+ const char *kseq;
+ size_t kslen;
+
+ kseq = rl_executing_keyseq;
+ kslen = rl_key_sequence_length;
+
+ /* If we have a numeric argument, chop it off the front of the key sequence */
+ if (count != 1 || rl_explicit_arg)
+ {
+ i = rl_trim_arg_from_keyseq (rl_executing_keyseq, rl_key_sequence_length, rl_get_keymap ());
+ if (i > 0)
+ {
+ kseq = rl_executing_keyseq + i;
+ kslen = rl_key_sequence_length - i;
+ }
+ }
/* First, we need to find the right command to execute. This is tricky,
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_len (rl_executing_keyseq, rl_key_sequence_length, cmd_xmap, &type);
-
+ cmd_xmap = get_cmd_xmap_from_keymap (rl_get_keymap ());
+ cmd = (char *)rl_function_of_keyseq_len (kseq, kslen, cmd_xmap, &type);
+
+ if (type == ISKMAP && (type = ((Keymap) cmd)[ANYOTHERKEY].type) == ISMACR)
+ cmd = (char*)((Keymap) cmd)[ANYOTHERKEY].function;
+
if (cmd == 0 || type != ISMACR)
{
rl_crlf ();
ce = rl_get_termcap ("ce");
if (ce) /* clear current line */
{
-#if 0
- fprintf (rl_outstream, "\r");
- tputs (ce, 1, putx);
-#else
rl_clear_visible_line ();
-#endif
fflush (rl_outstream);
}
else
v = bind_variable ("READLINE_LINE", rl_line_buffer, 0);
if (v)
VSETATTR (v, att_exported);
- i = rl_point;
-#if defined (HANDLE_MULTIBYTE)
- if (MB_CUR_MAX > 1)
- {
- old_ch = rl_line_buffer[rl_point];
- rl_line_buffer[rl_point] = '\0';
- i = MB_STRLEN (rl_line_buffer);
- rl_line_buffer[rl_point] = old_ch;
- }
-#endif
+
+ i = readline_get_char_offset (rl_point);
value = inttostr (i, ibuf, sizeof (ibuf));
v = bind_int_variable ("READLINE_POINT", value, 0);
if (v)
VSETATTR (v, att_exported);
+
+ i = readline_get_char_offset (rl_mark);
+ value = inttostr (i, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("READLINE_MARK", value, 0);
+ if (v)
+ VSETATTR (v, att_exported);
+
+ if (count != 1 || rl_explicit_arg)
+ {
+ value = inttostr (count, ibuf, sizeof (ibuf));
+ v = bind_int_variable ("READLINE_ARGUMENT", value, 0);
+ if (v)
+ VSETATTR (v, att_exported);
+ }
array_needs_making = 1;
+ begin_unwind_frame ("execute-unix-command");
save_parser_state (&ps);
- r = parse_and_execute (savestring (cmd), "bash_execute_unix_command", SEVAL_NOHIST|SEVAL_NOFREE);
+ rl_clear_signals ();
+ add_unwind_protect (uw_unbind_readline_variables, 0);
+ add_unwind_protect (uw_restore_parser_state, &ps);
+ add_unwind_protect (uw_rl_set_signals, 0);
+ r = parse_and_execute (savestring (cmd), "bash_execute_unix_command", SEVAL_NOHIST);
+ rl_set_signals ();
restore_parser_state (&ps);
v = find_variable ("READLINE_LINE");
maybe_make_readline_line (v ? value_cell (v) : 0);
v = find_variable ("READLINE_POINT");
- if (v && legal_number (value_cell (v), &mi))
- {
- i = mi;
-#if defined (HANDLE_MULTIBYTE)
- if (i > 0 && MB_CUR_MAX > 1)
- i = _rl_find_next_mbchar (rl_line_buffer, 0, i, 0);
-#endif
- if (i != rl_point)
- {
- rl_point = i;
- if (rl_point > rl_end)
- rl_point = rl_end;
- else if (rl_point < 0)
- rl_point = 0;
- }
- }
+ if (v && valid_number (value_cell (v), &mi))
+ readline_set_char_offset (mi, &rl_point);
- check_unbind_variable ("READLINE_LINE");
- check_unbind_variable ("READLINE_POINT");
- array_needs_making = 1;
+ v = find_variable ("READLINE_MARK");
+ if (v && valid_number (value_cell (v), &mi))
+ readline_set_char_offset (mi, &rl_mark);
+
+ unbind_readline_variables ();
+ discard_unwind_frame ("execute-unix-command");
/* and restore the readline buffer and display after command execution. */
/* If we clear the last line of the prompt above, redraw only that last
return 0;
}
+/* This only has to handle macros/shell commandsfrom print_unix_command_map */
+static void
+print_unix_command (const char *kseq, const char *value, int readable, const char *prefix)
+{
+ if (readable)
+ fprintf (rl_outstream, "\"%s%s\" \"%s\"\n", prefix ? prefix : "", kseq, value ? value : "");
+ else
+ fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "", kseq, value ? value : "");
+}
+
int
-print_unix_command_map ()
+print_unix_command_map (void)
{
- Keymap save;
+ Keymap save, cmd_xmap;
+ rl_macro_print_func_t *old_printer;
save = rl_get_keymap ();
+ cmd_xmap = get_cmd_xmap_from_keymap (save);
rl_set_keymap (cmd_xmap);
+ old_printer = rl_macro_display_hook;
+ rl_macro_display_hook = print_unix_command;
rl_macro_dumper (1);
+ rl_macro_display_hook = old_printer;
rl_set_keymap (save);
return 0;
}
static void
-init_unix_command_map ()
+init_unix_command_map (void)
+{
+ emacs_std_cmd_xmap = rl_make_bare_keymap ();
+
+ emacs_std_cmd_xmap[CTRL('X')].type = ISKMAP;
+ emacs_std_cmd_xmap[CTRL('X')].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap ());
+ emacs_std_cmd_xmap[ESC].type = ISKMAP;
+ emacs_std_cmd_xmap[ESC].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap ());
+
+#if defined (VI_MODE)
+ vi_insert_cmd_xmap = rl_make_bare_keymap ();
+ vi_movement_cmd_xmap = rl_make_bare_keymap ();
+#endif
+}
+
+static Keymap
+get_cmd_xmap_from_edit_mode (void)
{
- cmd_xmap = rl_make_bare_keymap ();
+ if (emacs_std_cmd_xmap == 0)
+ init_unix_command_map ();
+
+ switch (rl_editing_mode)
+ {
+ case EMACS_EDITING_MODE:
+ return emacs_std_cmd_xmap;
+#if defined (VI_MODE)
+ case VI_EDITING_MODE:
+ return (get_cmd_xmap_from_keymap (rl_get_keymap ()));
+#endif
+ default:
+ return (Keymap)NULL;
+ }
+}
+
+static Keymap
+get_cmd_xmap_from_keymap (Keymap kmap)
+{
+ if (emacs_std_cmd_xmap == 0)
+ init_unix_command_map ();
+
+ if (kmap == emacs_standard_keymap)
+ return emacs_std_cmd_xmap;
+ else if (kmap == emacs_meta_keymap)
+ return (FUNCTION_TO_KEYMAP (emacs_std_cmd_xmap, ESC));
+ else if (kmap == emacs_ctlx_keymap)
+ return (FUNCTION_TO_KEYMAP (emacs_std_cmd_xmap, CTRL('X')));
+#if defined (VI_MODE)
+ else if (kmap == vi_insertion_keymap)
+ return vi_insert_cmd_xmap;
+ else if (kmap == vi_movement_keymap)
+ return vi_movement_cmd_xmap;
+#endif
+ else
+ return (Keymap)NULL;
}
static int
-isolate_sequence (string, ind, need_dquote, startp)
- char *string;
- int ind, need_dquote, *startp;
+isolate_sequence (char *string, int ind, int need_dquote, int *startp)
{
register int i;
int c, passc, delim;
}
int
-bind_keyseq_to_unix_command (line)
- char *line;
+bind_keyseq_to_unix_command (char *line)
{
- Keymap kmap;
+ Keymap kmap, cmd_xmap;
char *kseq, *value;
- int i, kstart;
-
- if (cmd_xmap == 0)
- init_unix_command_map ();
+ int i, kstart, translate;
kmap = rl_get_keymap ();
/* We duplicate some of the work done by rl_parse_and_bind here, but
- this code only has to handle `"keyseq": ["]command["]' and can
+ this code only has to handle `"keyseq"[:][ \t]+["]command["]' and can
generate an error for anything else. */
i = isolate_sequence (line, 0, 1, &kstart);
if (i < 0)
/* Create the key sequence string to pass to rl_generic_bind */
kseq = substring (line, kstart, i);
- for ( ; line[i] && line[i] != ':'; i++)
+ /* Allow colon or whitespace to separate the key sequence and command string. */
+ for ( ; line[i] && line[i] != ':' && whitespace (line[i]) == 0; i++)
;
- if (line[i] != ':')
+ if (line[i] != ':' && whitespace (line[i]) == 0)
{
- builtin_error (_("%s: missing colon separator"), line);
+ builtin_error (_("%s: missing separator"), line);
FREE (kseq);
return -1;
}
- i = isolate_sequence (line, i + 1, 0, &kstart);
+ /* If we have a whitespace separator we're going to call rl_macro_bind so
+ we get the readline-translated version of the value (backslash-escapes
+ handled, etc.) */
+ translate = line[i] != ':';
+
+ /* Kind of tricky. If we use whitespace as a delimiter, we can backslash-
+ quote double quotes and have them preserved in the value. However, we
+ still want to be able to auto-detect quoted strings and only require
+ them with whitespace delimiters. */
+ i = isolate_sequence (line, i + 1, translate, &kstart);
if (i < 0)
{
FREE (kseq);
value = substring (line, kstart, i);
/* Save the command to execute and the key sequence in the CMD_XMAP */
- rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+ cmd_xmap = get_cmd_xmap_from_keymap (kmap);
+ if (translate)
+ rl_macro_bind (kseq, value, cmd_xmap);
+ else
+ rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
/* and bind the key sequence in the current keymap to a function that
understands how to execute from CMD_XMAP */
return 0;
}
+int
+unbind_unix_command (char *kseq)
+{
+ Keymap cmd_xmap;
+
+ cmd_xmap = get_cmd_xmap_from_keymap (rl_get_keymap ());
+ if (rl_bind_keyseq_in_map (kseq, (rl_command_func_t *)NULL, cmd_xmap) != 0)
+ {
+ builtin_error (_("`%s': cannot unbind in command keymap"), kseq);
+ return 0;
+ }
+ return 1;
+}
+
/* Used by the programmable completion code. Complete TEXT as a filename,
but return only directories as matches. Dequotes the filename before
attempting to find matches. */
char **
-bash_directory_completion_matches (text)
- const char *text;
+bash_directory_completion_matches (const char *text)
{
char **m1;
char *dfn;
/* We don't bother recomputing the lcd of the matches, because it will just
get thrown away by the programmable completion code and recomputed
later. */
- (void)bash_ignore_filenames (m1);
+ (void)bash_progcomp_ignore_filenames (m1);
return m1;
}
char *
-bash_dequote_text (text)
- const char *text;
+bash_dequote_text (const char *text)
{
char *dtxt;
int qc;
and fatal signals without executing too much code in a signal handler
context. */
static int
-bash_event_hook ()
+bash_event_hook (void)
{
+ int sig;
+
+ /* XXX - see if we need to do anything here if sigterm_received == 1,
+ we probably don't want to reset the event hook since we will not be
+ jumping to the top level */
+ if (sigterm_received)
+ {
+ /* RESET_SIGTERM; */
+ return 0;
+ }
+
+ sig = 0;
+ if (terminating_signal)
+ sig = terminating_signal;
+ else if (interrupt_state)
+ sig = SIGINT;
+ else if (read_timeout && read_timeout->alrmflag)
+ sig = SIGALRM;
+ else if (RL_ISSTATE (RL_STATE_TIMEOUT)) /* just in case */
+ {
+ sig = SIGALRM;
+ if (read_timeout)
+ read_timeout->alrmflag = 1;
+ }
+ else
+ sig = first_pending_trap ();
+
/* 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. The check for sigalrm_seen is
- to clean up the read builtin's state. */
- if (terminating_signal || interrupt_state || sigalrm_seen)
+ calling run_interrupt_trap along the way. The check against read_timeout
+ is so we can clean up the read builtin's state. */
+ if (terminating_signal || interrupt_state || (read_timeout && read_timeout->alrmflag))
rl_cleanup_after_signal ();
bashline_reset_event_hook ();
+
+ RL_UNSETSTATE (RL_STATE_TIMEOUT); /* XXX */
+
+ /* posix mode SIGINT during read -e. We only get here if SIGINT is trapped. */
+ if (posixly_correct && this_shell_builtin == read_builtin && sig == SIGINT)
+ {
+ last_command_exit_value = 128|SIGINT;
+ throw_to_top_level ();
+ }
+
check_signals_and_traps (); /* XXX */
return 0;
}