/* 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_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 ();
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);
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 */
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]))
/* 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
rl_completion_suppress_append = 1;
rl_filename_completion_desired = 0;
}
-#if 0
- /* TAG:bash-5.3 jidanni@jidanni.org 11/11/2022 */
+#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
result = search_for_command (cname, 0);
if (result)
{
+ FREE (*name);
*name = result;
return 1;
}
/* This gets an unquoted filename, so we need to quote special characters
in the filename before the completion hook gets it. */
-#if 0
- f = savestring (filename);
-#else
c = 0;
f = bash_quote_filename ((char *)filename, SINGLE_MATCH, &c);
-#endif
bash_directory_completion_hook (&f);
r = searching_path ? executable_file (f) : executable_or_directory (f);
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);
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. */
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
t1 = make_absolute (val, t);
free (t);
cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+ free (t1);
}
else
#endif
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
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);
}
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
{
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);
}
}
+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)
{
}
array_needs_making = 1;
+ begin_unwind_frame ("execute-unix-command");
save_parser_state (&ps);
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);
maybe_make_readline_line (v ? value_cell (v) : 0);
v = find_variable ("READLINE_POINT");
- if (v && legal_number (value_cell (v), &mi))
+ if (v && valid_number (value_cell (v), &mi))
readline_set_char_offset (mi, &rl_point);
v = find_variable ("READLINE_MARK");
- if (v && legal_number (value_cell (v), &mi))
+ if (v && valid_number (value_cell (v), &mi))
readline_set_char_offset (mi, &rl_mark);
- check_unbind_variable ("READLINE_LINE");
- check_unbind_variable ("READLINE_POINT");
- check_unbind_variable ("READLINE_MARK");
- check_unbind_variable ("READLINE_ARGUMENT");
- array_needs_making = 1;
+ 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 (void)
{
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;
}
{
Keymap kmap, cmd_xmap;
char *kseq, *value;
- int i, kstart;
+ 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);
/* Save the command to execute and the key sequence in the CMD_XMAP */
cmd_xmap = get_cmd_xmap_from_keymap (kmap);
- rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+ 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 */