[PATCHv3 1/7] gdb: split apart two different types of filename completion

Andrew Burgess
2024-06-05T13:37:48Z

Unfortunately we have two different types of filename completion in
GDB.

The majority of commands have what I call unquoted filename
completion, this is for commands like 'set logging file ...', 'target
core ...', and 'add-auto-load-safe-path ...'. For these commands
everything after the command name (that is not a command option) is
treated as a single filename. If the filename contains white space
then this does not need to be escaped, nor does the filename need to
be quoted. In fact, the filename argument is not de-quoted, and does
not have any escaping removed, so if a user does try to add such
things, they will be treated as part of the filename. As an example:

(gdb) target core "/path/that contains/some white space"

Will look for a directory calls '"' (double quotes) in the local
directory.

A small number of commands do de-quote and remove escapes from
filename arguments. These command accept what I call quoted and
escaped filenames. Right now these are the commands that specify the
file for GDB to debug, so:

file
exec-file
symbol-file
add-symbol-file
remove-symbol-file

As an example of this in action:

(gdb) file "/path/that contains/some white space"

In this case GDB would load the file:

/path/that contains/some white space

Current filename completion always assumes that filenames can be
quoted, though escaping doesn't work in completion right now. But the
assumption that quoting is allowed is clearly wrong.

This commit splits filename completion into two. There is now a
filename completer for unquoted filenames, and a second completer to
handle quoted and escaped filenames.

To handle completion of unquoted filenames the completion needs to be
performed during the brkchars phase of completion, which is why almost
every place we use filename_completer has to change in this commit.

I've also renamed the 'filename_completer' function to reflect that it
now handles filenames that can be quoted.

The filename completion test has been extended to cover more cases.

The 'advance_to_filename_complete_word_point' function is no longer
used after this commit and so I've deleted it.
---
gdb/auto-load.c | 4 +-
gdb/breakpoint.c | 5 +-
gdb/cli/cli-cmds.c | 8 +-
gdb/cli/cli-decode.c | 12 +-
gdb/cli/cli-dump.c | 6 +-
gdb/compile/compile.c | 3 +-
gdb/completer.c | 61 ++++---
gdb/completer.h | 29 +++-
gdb/corefile.c | 4 +-
gdb/corelow.c | 4 +-
gdb/dwarf2/index-write.c | 3 +-
gdb/exec.c | 7 +-
gdb/guile/scm-cmd.c | 2 +-
gdb/infcmd.c | 15 +-
gdb/inferior.c | 2 +-
gdb/jit.c | 2 +-
gdb/python/py-cmd.c | 2 +-
gdb/record-full.c | 5 +-
gdb/record.c | 2 +-
gdb/skip.c | 2 +-
gdb/source.c | 3 +-
gdb/symfile.c | 7 +-
gdb/target-descriptions.c | 7 +-
gdb/target.c | 4 +-
gdb/target.h | 9 +-
.../gdb.base/filename-completion.exp | 156 +++++++++++++-----
gdb/tracectf.c | 3 +-
gdb/tracefile-tfile.c | 4 +-
28 files changed, 248 insertions(+), 123 deletions(-)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index afc98eb1f58..51c14e7bb02 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -1629,7 +1629,7 @@ This option has security implications for untrusted inferiors."),
See the commands 'set auto-load safe-path' and 'show auto-load safe-path' to\n\
access the current full list setting."),
&cmdlist);
- set_cmd_completer (cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (cmd, filename_completer_handle_brkchars);

cmd = add_cmd ("add-auto-load-scripts-directory", class_support,
add_auto_load_dir,
@@ -1638,7 +1638,7 @@ access the current full list setting."),
See the commands 'set auto-load scripts-directory' and\n\
'show auto-load scripts-directory' to access the current full list setting."),
&cmdlist);
- set_cmd_completer (cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (cmd, filename_completer_handle_brkchars);

add_setshow_boolean_cmd ("auto-load", class_maintenance,
&debug_auto_load, _("\
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index a973518ac5f..578d515cc5a 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15094,14 +15094,15 @@ This includes all types of breakpoints (breakpoints, watchpoints,\n\
catchpoints, tracepoints). Use the 'source' command in another debug\n\
session to restore them."),
&save_cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

cmd_list_element *save_tracepoints_cmd
= add_cmd ("tracepoints", class_trace, save_tracepoints_command, _("\
Save current tracepoint definitions as a script.\n\
Use the 'source' command in another debug session to restore them."),
&save_cmdlist);
- set_cmd_completer (save_tracepoints_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (save_tracepoints_cmd,
+ filename_completer_handle_brkchars);

c = add_com_alias ("save-tracepoints", save_tracepoints_cmd, class_trace, 0);
deprecate_cmd (c, "save tracepoints");
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 3af794cebaf..4c2a443246d 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2638,7 +2638,7 @@ The debugger's current working directory specifies where scripts and other\n\
files that can be loaded by GDB are located.\n\
In order to change the inferior's current working directory, the recommended\n\
way is to use the \"set cwd\" command."), &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

add_com ("echo", class_support, echo_command, _("\
Print a constant string. Give string as argument.\n\
@@ -2804,7 +2804,7 @@ the previous command number shown."),
= add_com ("shell", class_support, shell_command, _("\
Execute the rest of the line as a shell command.\n\
With no arguments, run an inferior shell."));
- set_cmd_completer (shell_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (shell_cmd, filename_completer_handle_brkchars);

add_com_alias ("!", shell_cmd, class_support, 0);

@@ -2893,7 +2893,7 @@ you must type \"disassemble 'foo.c'::bar\" and not \"disassemble foo.c:bar\"."))

c = add_com ("make", class_support, make_command, _("\
Run the ``make'' program using the rest of the line as arguments."));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
c = add_cmd ("user", no_class, show_user, _("\
Show definitions of non-python/scheme user defined commands.\n\
Argument is the name of the user defined command.\n\
@@ -2977,5 +2977,5 @@ Note that the file \"%s\" is read automatically in this way\n\
when GDB is started."), GDBINIT).release ();
c = add_cmd ("source", class_support, source_command,
source_help_text, &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
}
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index d9a2ab40575..a48b18bbd87 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -866,7 +866,8 @@ add_setshow_filename_cmd (const char *name, enum command_class theclass,
nullptr, nullptr, set_func,
show_func, set_list, show_list);

- set_cmd_completer (commands.set, filename_completer);
+ set_cmd_completer_handle_brkchars (commands.set,
+ filename_completer_handle_brkchars);

return commands;
}
@@ -890,7 +891,8 @@ add_setshow_filename_cmd (const char *name, command_class theclass,
nullptr, show_func, set_list,
show_list);

- set_cmd_completer (cmds.set, filename_completer);
+ set_cmd_completer_handle_brkchars (cmds.set,
+ filename_completer_handle_brkchars);

return cmds;
}
@@ -1015,7 +1017,8 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
nullptr, nullptr, set_func, show_func,
set_list, show_list);

- set_cmd_completer (commands.set, filename_completer);
+ set_cmd_completer_handle_brkchars (commands.set,
+ filename_completer_handle_brkchars);

return commands;
}
@@ -1039,7 +1042,8 @@ add_setshow_optional_filename_cmd (const char *name, command_class theclass,
set_func, get_func, nullptr, show_func,
set_list,show_list);

- set_cmd_completer (cmds.set, filename_completer);
+ set_cmd_completer_handle_brkchars (cmds.set,
+ filename_completer_handle_brkchars);

return cmds;
}
diff --git a/gdb/cli/cli-dump.c b/gdb/cli/cli-dump.c
index 9b44c6edcf2..aaacdd6ede1 100644
--- a/gdb/cli/cli-dump.c
+++ b/gdb/cli/cli-dump.c
@@ -348,7 +348,7 @@ add_dump_command (const char *name,
struct dump_context *d;

c = add_cmd (name, all_commands, descr, &dump_cmdlist);
- c->completer = filename_completer;
+ c->completer_handle_brkchars = filename_completer_handle_brkchars;
d = XNEW (struct dump_context);
d->func = func;
d->mode = FOPEN_WB;
@@ -356,7 +356,7 @@ add_dump_command (const char *name,
c->func = call_dump_func;

c = add_cmd (name, all_commands, descr, &append_cmdlist);
- c->completer = filename_completer;
+ c->completer_handle_brkchars = filename_completer_handle_brkchars;
d = XNEW (struct dump_context);
d->func = func;
d->mode = FOPEN_AB;
@@ -705,6 +705,6 @@ Arguments are FILE OFFSET START END where all except FILE are optional.\n\
OFFSET will be added to the base address of the file (default zero).\n\
If START and END are given, only the file contents within that range\n\
(file relative) will be restored to target memory."));
- c->completer = filename_completer;
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
/* FIXME: completers for other commands. */
}
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index 88531a21781..2c772fddaf5 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -327,8 +327,7 @@ compile_file_command_completer (struct cmd_list_element *ignore,
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group))
return;

- word = advance_to_filename_complete_word_point (tracker, text);
- filename_completer (ignore, tracker, text, word);
+ filename_completer_handle_brkchars (ignore, tracker, text, word);
}

/* Handle the input from the 'compile code' command. The
diff --git a/gdb/completer.c b/gdb/completer.c
index f1f44109bdc..640dfc29a8d 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -203,15 +203,15 @@ noop_completer (struct cmd_list_element *ignore,
{
}

-/* Complete on filenames. */
+/* Generate filename completions of WORD, storing the completions into
+ TRACKER. This is used for generating completions for commands that
+ only accept unquoted filenames as well as for commands that accept
+ quoted and escaped filenames. */

-void
-filename_completer (struct cmd_list_element *ignore,
- completion_tracker &tracker,
- const char *text, const char *word)
+static void
+filename_completer_generate_completions (completion_tracker &tracker,
+ const char *word)
{
- rl_completer_quote_characters = gdb_completer_file_name_quote_characters;
-
int subsequent_name = 0;
while (1)
{
@@ -249,13 +249,23 @@ filename_completer (struct cmd_list_element *ignore,
}
}

-/* The corresponding completer_handle_brkchars
- implementation. */
+/* Complete on filenames. */
+
+void
+filename_maybe_quoted_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ rl_completer_quote_characters = gdb_completer_file_name_quote_characters;
+ filename_completer_generate_completions (tracker, word);
+}
+
+/* The corresponding completer_handle_brkchars implementation. */

static void
-filename_completer_handle_brkchars (struct cmd_list_element *ignore,
- completion_tracker &tracker,
- const char *text, const char *word)
+filename_maybe_quoted_completer_handle_brkchars
+ (struct cmd_list_element *ignore, completion_tracker &tracker,
+ const char *text, const char *word)
{
set_rl_completer_word_break_characters
(gdb_completer_file_name_break_characters);
@@ -263,6 +273,18 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
rl_completer_quote_characters = gdb_completer_file_name_quote_characters;
}

+/* See completer.h. */
+
+void
+filename_completer_handle_brkchars
+ (struct cmd_list_element *ignore, completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ gdb_assert (word == nullptr);
+ tracker.set_use_custom_word_point (true);
+ filename_completer_generate_completions (tracker, text);
+}
+
/* Find the bounds of the current word for completion purposes, and
return a pointer to the end of the word. This mimics (and is a
modified version of) readline's _rl_find_completion_word internal
@@ -443,17 +465,6 @@ advance_to_expression_complete_word_point (completion_tracker &tracker,

/* See completer.h. */

-const char *
-advance_to_filename_complete_word_point (completion_tracker &tracker,
- const char *text)
-{
- const char *brk_chars = gdb_completer_file_name_break_characters;
- const char *quote_chars = gdb_completer_file_name_quote_characters;
- return advance_to_completion_word (tracker, brk_chars, quote_chars, text);
-}
-
-/* See completer.h. */
-
bool
completion_tracker::completes_to_completion_word (const char *word)
{
@@ -1877,8 +1888,8 @@ default_completer_handle_brkchars (struct cmd_list_element *ignore,
completer_handle_brkchars_ftype *
completer_handle_brkchars_func_for_completer (completer_ftype *fn)
{
- if (fn == filename_completer)
- return filename_completer_handle_brkchars;
+ if (fn == filename_maybe_quoted_completer)
+ return filename_maybe_quoted_completer_handle_brkchars;

if (fn == location_completer)
return location_completer_handle_brkchars;
diff --git a/gdb/completer.h b/gdb/completer.h
index 98a12f3907c..fa21156bd1f 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -563,12 +563,6 @@ extern completion_result
const char *advance_to_expression_complete_word_point
(completion_tracker &tracker, const char *text);

-/* Assuming TEXT is an filename, find the completion word point for
- TEXT, emulating the algorithm readline uses to find the word
- point. */
-extern const char *advance_to_filename_complete_word_point
- (completion_tracker &tracker, const char *text);
-
extern void noop_completer (struct cmd_list_element *,
completion_tracker &tracker,
const char *, const char *);
@@ -577,6 +571,29 @@ extern void filename_completer (struct cmd_list_element *,
completion_tracker &tracker,
const char *, const char *);

+/* Filename completer that can be registered for the brkchars phase of
+ completion. This should be used by commands that don't allow the
+ filename to be quoted, and whitespace does not need to be escaped.
+
+ NOTE: If you are considering using this function as your commands
+ completer, then consider updating your function to use gdb_argv or
+ extract_string_maybe_quoted to allow for possibly quoted filenames
+ instead. You would then use filename_maybe_quoted_completer for
+ filename completion. The benefit of this is that in the future it is
+ possible to add additional arguments to your new command. */
+
+extern void filename_completer_handle_brkchars
+ (struct cmd_list_element *ignore, completion_tracker &tracker,
+ const char *text, const char *word);
+
+/* Filename completer for commands where the filename argument can be
+ quoted, and whitespace within an unquoted filename should be escaped
+ with a backslash. */
+
+extern void filename_maybe_quoted_completer (struct cmd_list_element *,
+ completion_tracker &tracker,
+ const char *, const char *);
+
extern void expression_completer (struct cmd_list_element *,
completion_tracker &tracker,
const char *, const char *);
diff --git a/gdb/corefile.c b/gdb/corefile.c
index 044c084ab6f..b0a48e1aead 100644
--- a/gdb/corefile.c
+++ b/gdb/corefile.c
@@ -406,9 +406,9 @@ Use FILE as core dump for examining memory and registers.\n\
Usage: core-file FILE\n\
No arg means have no core file. This command has been superseded by the\n\
`target core' and `detach' commands."), &cmdlist);
- set_cmd_completer (core_file_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (core_file_cmd,
+ filename_completer_handle_brkchars);

-
set_show_commands set_show_gnutarget
= add_setshow_string_noescape_cmd ("gnutarget", class_files,
&gnutarget_string, _("\
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 49da9be07ef..1c4f62bdf6b 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -1520,7 +1520,9 @@ void _initialize_corelow ();
void
_initialize_corelow ()
{
- add_target (core_target_info, core_target_open, filename_completer);
+ struct cmd_list_element *c
+ = add_target (core_target_info, core_target_open);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
add_cmd ("core-file-backed-mappings", class_maintenance,
maintenance_print_core_file_backed_mappings,
_("Print core file's file-backed mappings."),
diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c
index 2a50e3b6c2d..03cff7b88cd 100644
--- a/gdb/dwarf2/index-write.c
+++ b/gdb/dwarf2/index-write.c
@@ -1621,8 +1621,7 @@ gdb_save_index_cmd_completer (struct cmd_list_element *ignore,
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp))
return;

- word = advance_to_filename_complete_word_point (tracker, text);
- filename_completer (ignore, tracker, text, word);
+ filename_completer_handle_brkchars (ignore, tracker, text, word);
}

/* Implementation of the `save gdb-index' command.
diff --git a/gdb/exec.c b/gdb/exec.c
index 88915260d60..cb522a3fc26 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -1065,14 +1065,14 @@ and it is the program executed when you use the `run' command.\n\
If FILE cannot be found as specified, your execution directory path\n\
($PATH) is searched for a command of that name.\n\
No arg means to have no executable file and no symbols."), &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer (c, filename_maybe_quoted_completer);

c = add_cmd ("exec-file", class_files, exec_file_command, _("\
Use FILE as program for getting contents of pure memory.\n\
If FILE cannot be found as specified, your execution directory path\n\
is searched for a command of that name.\n\
No arg means have no executable file."), &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer (c, filename_maybe_quoted_completer);

add_com ("section", class_files, set_section_command, _("\
Change the base address of section SECTION of the exec file to ADDR.\n\
@@ -1110,5 +1110,6 @@ will be loaded as well."),
show_exec_file_mismatch_command,
&setlist, &showlist);

- add_target (exec_target_info, exec_target_open, filename_completer);
+ c = add_target (exec_target_info, exec_target_open);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
}
diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index 2a5507686b0..8255529a2fe 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -110,7 +110,7 @@ struct cmdscm_completer
static const struct cmdscm_completer cmdscm_completers[] =
{
{ "COMPLETE_NONE", noop_completer },
- { "COMPLETE_FILENAME", filename_completer },
+ { "COMPLETE_FILENAME", filename_maybe_quoted_completer },
{ "COMPLETE_LOCATION", location_completer },
{ "COMPLETE_COMMAND", command_completer },
{ "COMPLETE_SYMBOL", symbol_completer },
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 69db665da75..c769603873a 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -3113,7 +3113,8 @@ Follow this command with any number of args, to be passed to the program."),
get_args_value,
show_args_command,
&setlist, &showlist);
- set_cmd_completer (args_set_show.set, filename_completer);
+ set_cmd_completer_handle_brkchars (args_set_show.set,
+ filename_completer_handle_brkchars);

auto cwd_set_show
= add_setshow_string_noescape_cmd ("cwd", class_run, _("\
@@ -3129,7 +3130,8 @@ working directory."),
set_cwd_value, get_inferior_cwd,
show_cwd_command,
&setlist, &showlist);
- set_cmd_completer (cwd_set_show.set, filename_completer);
+ set_cmd_completer_handle_brkchars (cwd_set_show.set,
+ filename_completer_handle_brkchars);

c = add_cmd ("environment", no_class, environment_info, _("\
The environment to give the program, or one variable's value.\n\
@@ -3163,7 +3165,7 @@ This path is equivalent to the $PATH shell variable. It is a list of\n\
directories, separated by colons. These directories are searched to find\n\
fully linked executable files and separately compiled object files as \
needed."));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

c = add_cmd ("paths", no_class, path_info, _("\
Current search path for finding object files.\n\
@@ -3313,18 +3315,19 @@ Specifying -a and an ignore count simultaneously is an error."));
= add_com ("run", class_run, run_command, _("\
Start debugged program.\n"
RUN_ARGS_HELP));
- set_cmd_completer (run_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (run_cmd,
+ filename_completer_handle_brkchars);
add_com_alias ("r", run_cmd, class_run, 1);

c = add_com ("start", class_run, start_command, _("\
Start the debugged program stopping at the beginning of the main procedure.\n"
RUN_ARGS_HELP));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

c = add_com ("starti", class_run, starti_command, _("\
Start the debugged program stopping at the first instruction.\n"
RUN_ARGS_HELP));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

add_com ("interrupt", class_run, interrupt_command,
_("Interrupt the execution of the debugged program.\n\
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 0522cb5c14d..b46b638c361 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -1112,7 +1112,7 @@ as main program.\n\
By default, the new inferior inherits the current inferior's connection.\n\
If -no-connection is specified, the new inferior begins with\n\
no target connection yet."));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

add_com ("remove-inferiors", no_class, remove_inferior_command, _("\
Remove inferior ID (or list of IDs).\n\
diff --git a/gdb/jit.c b/gdb/jit.c
index 797be95a8da..3179f3aef19 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1328,7 +1328,7 @@ Usage: jit-reader-load FILE\n\
Try to load file FILE as a debug info reader (and unwinder) for\n\
JIT compiled code. The file is loaded from " JIT_READER_DIR ",\n\
relocated relative to the GDB executable if required."));
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

c = add_com ("jit-reader-unload", no_class,
jit_reader_unload_command, _("\
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index f83b45dd210..4deeb35eba0 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -39,7 +39,7 @@ struct cmdpy_completer
static const struct cmdpy_completer completers[] =
{
{ "COMPLETE_NONE", noop_completer },
- { "COMPLETE_FILENAME", filename_completer },
+ { "COMPLETE_FILENAME", filename_maybe_quoted_completer },
{ "COMPLETE_LOCATION", location_completer },
{ "COMPLETE_COMMAND", command_completer },
{ "COMPLETE_SYMBOL", symbol_completer },
diff --git a/gdb/record-full.c b/gdb/record-full.c
index eb62d186fa5..122f4b97735 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -2891,12 +2891,13 @@ _initialize_record_full ()
_("Restore the execution log from a file.\n\
Argument is filename. File must be created with 'record save'."),
&record_full_cmdlist);
- set_cmd_completer (record_full_restore_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (record_full_restore_cmd,
+ filename_completer_handle_brkchars);

/* Deprecate the old version without "full" prefix. */
c = add_alias_cmd ("restore", record_full_restore_cmd, class_obscure, 1,
&record_cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
deprecate_cmd (c, "record full restore");

add_setshow_prefix_cmd ("full", class_support,
diff --git a/gdb/record.c b/gdb/record.c
index b25445713fd..8c3cd7c1e4a 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -821,7 +821,7 @@ A size of \"unlimited\" means unlimited lines. The default is 10."),
Usage: record save [FILENAME]\n\
Default filename is 'gdb_record.PROCESS_ID'."),
&record_cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

cmd_list_element *record_delete_cmd
= add_cmd ("delete", class_obscure, cmd_record_delete,
diff --git a/gdb/skip.c b/gdb/skip.c
index 72f4efd0e29..c9b312195af 100644
--- a/gdb/skip.c
+++ b/gdb/skip.c
@@ -684,7 +684,7 @@ Ignore a file while stepping.\n\
Usage: skip file [FILE-NAME]\n\
If no filename is given, ignore the current file."),
&skiplist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

c = add_cmd ("function", class_breakpoint, skip_function_command, _("\
Ignore a function while stepping.\n\
diff --git a/gdb/source.c b/gdb/source.c
index 24a8769da91..77016293d63 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -1916,7 +1916,8 @@ directory in which the source file was compiled into object code.\n\
With no argument, reset the search path to $cdir:$cwd, the default."),
&cmdlist);

- set_cmd_completer (directory_cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (directory_cmd,
+ filename_completer_handle_brkchars);

add_setshow_optional_filename_cmd ("directories",
class_files,
diff --git a/gdb/symfile.c b/gdb/symfile.c
index f7f5be5a39a..7d89bda8759 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3851,7 +3851,7 @@ Usage: symbol-file [-readnow | -readnever] [-o OFF] FILE\n\
OFF is an optional offset which is added to each section address.\n\
The `file' command can also load symbol tables, as well as setting the file\n\
to execute.\n" READNOW_READNEVER_HELP), &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer (c, filename_maybe_quoted_completer);

c = add_cmd ("add-symbol-file", class_files, add_symbol_file_command, _("\
Load symbols from FILE, assuming FILE has been dynamically loaded.\n\
@@ -3865,7 +3865,7 @@ OFF is an optional offset which is added to the default load addresses\n\
of all sections for which no other address was specified.\n"
READNOW_READNEVER_HELP),
&cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer (c, filename_maybe_quoted_completer);

c = add_cmd ("remove-symbol-file", class_files,
remove_symbol_file_command, _("\
@@ -3875,6 +3875,7 @@ Usage: remove-symbol-file FILENAME\n\
The file to remove can be identified by its filename or by an address\n\
that lies within the boundaries of this symbol file in memory."),
&cmdlist);
+ set_cmd_completer (c, filename_maybe_quoted_completer);

c = add_cmd ("load", class_files, load_command, _("\
Dynamically load FILE into the running program.\n\
@@ -3883,7 +3884,7 @@ Usage: load [FILE] [OFFSET]\n\
An optional load OFFSET may also be given as a literal address.\n\
When OFFSET is provided, FILE must also be provided. FILE can be provided\n\
on its own."), &cmdlist);
- set_cmd_completer (c, filename_completer);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);

cmd_list_element *overlay_cmd
= add_basic_prefix_cmd ("overlay", class_support,
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 4f210441623..6e7925b01b4 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -1762,8 +1762,7 @@ maint_print_c_tdesc_cmd_completer (struct cmd_list_element *ignore,
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp))
return;

- word = advance_to_filename_complete_word_point (tracker, text);
- filename_completer (ignore, tracker, text, word);
+ filename_completer_handle_brkchars (ignore, tracker, text, word);
}

/* Implement the maintenance print xml-tdesc command. */
@@ -1947,7 +1946,7 @@ that feature within an already existing target_desc object."), grp);
cmd = add_cmd ("xml-tdesc", class_maintenance, maint_print_xml_tdesc_cmd, _("\
Print the current target description as an XML file."),
&maintenanceprintlist);
- set_cmd_completer (cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (cmd, filename_completer_handle_brkchars);

cmd = add_cmd ("xml-descriptions", class_maintenance,
maintenance_check_xml_descriptions, _("\
@@ -1956,5 +1955,5 @@ Check the target descriptions created in GDB equal the descriptions\n\
created from XML files in the directory.\n\
The parameter is the directory name."),
&maintenancechecklist);
- set_cmd_completer (cmd, filename_completer);
+ set_cmd_completer_handle_brkchars (cmd, filename_completer_handle_brkchars);
}
diff --git a/gdb/target.c b/gdb/target.c
index 276e215945e..58c1b8e7971 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -841,7 +841,7 @@ open_target (const char *args, int from_tty, struct cmd_list_element *command)

/* See target.h. */

-void
+struct cmd_list_element *
add_target (const target_info &t, target_open_ftype *func,
completer_ftype *completer)
{
@@ -865,6 +865,8 @@ information on the arguments for a particular protocol, type\n\
c->func = open_target;
if (completer != NULL)
set_cmd_completer (c, completer);
+
+ return c;
}

/* See target.h. */
diff --git a/gdb/target.h b/gdb/target.h
index 81de4a678c3..5aa1f455aea 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2403,11 +2403,12 @@ typedef void target_open_ftype (const char *args, int from_tty);

/* Add the target described by INFO to the list of possible targets
and add a new command 'target $(INFO->shortname)'. Set COMPLETER
- as the command's completer if not NULL. */
+ as the command's completer if not NULL. Return the new target
+ command. */

-extern void add_target (const target_info &info,
- target_open_ftype *func,
- completer_ftype *completer = NULL);
+extern struct cmd_list_element *add_target
+ (const target_info &info, target_open_ftype *func,
+ completer_ftype *completer = NULL);

/* Adds a command ALIAS for the target described by INFO and marks it
deprecated. This is useful for maintaining backwards compatibility
diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp
index b700977cec5..1ccaaff9afc 100644
--- a/gdb/testsuite/gdb.base/filename-completion.exp
+++ b/gdb/testsuite/gdb.base/filename-completion.exp
@@ -23,8 +23,16 @@ load_lib completion-support.exp
#
# root/ [ DIRECTORY ]
# aaa/ [ DIRECTORY ]
+# aa bb [ FILE ]
+# aa cc [ FILE ]
+# aaa/ [ DIRECTORY ]
# bb1/ [ DIRECTORY ]
# bb2/ [ DIRECTORY ]
+# dir 1/ [ DIRECTORY ]
+# unique file [ FILE ]
+# dir 2/ [ DIRECTORY ]
+# file 1 [ FILE ]
+# file 2 [ FILE ]
# cc1/ [ DIRECTORY ]
# cc2 [ FILE ]
proc setup_directory_tree {} {
@@ -36,68 +44,139 @@ proc setup_directory_tree {} {
remote_exec host "mkdir -p ${root}/bb2"
remote_exec host "mkdir -p ${root}/cc1"
remote_exec host "touch ${root}/cc2"
-
remote_exec host "touch \"${root}/aaa/aa bb\""
remote_exec host "touch \"${root}/aaa/aa cc\""
+ remote_exec host "mkdir -p \"${root}/bb2/dir 1\""
+ remote_exec host "mkdir -p \"${root}/bb2/dir 2\""
+ remote_exec host "touch \"${root}/bb2/dir 1/unique file\""
+ remote_exec host "touch \"${root}/bb2/dir 2/file 1\""
+ remote_exec host "touch \"${root}/bb2/dir 2/file 2\""

return $root
}

-# Run filename completetion tests. ROOT is the base directory as
-# returned from setup_directory_tree, though, if ROOT is a
-# sub-directory of the user's home directory ROOT might have been
-# modified to replace the $HOME prefix with a single "~" character.
-proc run_tests { root } {
-
- # Completing 'thread apply all ...' commands uses a custom word
- # point. At one point we had a bug where doing this would break
- # completion of quoted filenames that contained white space.
- test_gdb_complete_unique "thread apply all hel" \
- "thread apply all help" " " false \
- "complete a 'thread apply all' command"
-
- foreach_with_prefix qc [list "" "'" "\""] {
- test_gdb_complete_none "file ${qc}${root}/xx" \
+# Run filename completetion tests for those command that accept quoting and
+# escaping of the filename argument.
+#
+# ROOT is the base directory as returned from setup_directory_tree, though,
+# if ROOT is a sub-directory of the user's home directory ROOT might have
+# been modified to replace the $HOME prefix with a single "~" character.
+proc run_quoting_and_escaping_tests { root } {
+ # Test all the commands which allow quoting of filenames, and
+ # which require whitespace to be escaped in unquoted filenames.
+ foreach_with_prefix cmd { file exec-file symbol-file add-symbol-file \
+ remove-symbol-file } {
+ gdb_start
+
+ # Completing 'thread apply all ...' commands uses a custom word
+ # point. At one point we had a bug where doing this would break
+ # completion of quoted filenames that contained white space.
+ test_gdb_complete_unique "thread apply all hel" \
+ "thread apply all help" " " false \
+ "complete a 'thread apply all' command"
+
+ foreach_with_prefix qc [list "" "'" "\""] {
+ test_gdb_complete_none "$cmd ${qc}${root}/xx" \
+ "expand a non-existent filename"
+
+ test_gdb_complete_unique "$cmd ${qc}${root}/a" \
+ "$cmd ${qc}${root}/aaa/" "" false \
+ "expand a unique filename"
+
+ test_gdb_complete_multiple "$cmd ${qc}${root}/" \
+ "b" "b" {
+ "bb1/"
+ "bb2/"
+ } "" "${qc}" false \
+ "expand multiple directory names"
+
+ test_gdb_complete_multiple "$cmd ${qc}${root}/" \
+ "c" "c" {
+ "cc1/"
+ "cc2"
+ } "" "${qc}" false \
+ "expand mixed directory and file names"
+
+ # GDB does not currently escape word break characters
+ # (e.g. white space) correctly in unquoted filenames.
+ if { $qc ne "" } {
+ set sp " "
+
+ test_gdb_complete_multiple "$cmd ${qc}${root}/aaa/" \
+ "a" "a${sp}" {
+ "aa bb"
+ "aa cc"
+ } "" "${qc}" false \
+ "expand filenames containing spaces"
+ }
+ }
+
+ gdb_exit
+ }
+}
+
+# Run filename completetion tests for a sample of commands that take an
+# unquoted, unescaped filename as an argument. Only a sample of commands
+# are (currently) tested as there's a lot of commands that accept this style
+# of filename argument.
+#
+# ROOT is the base directory as returned from setup_directory_tree, though,
+# if ROOT is a sub-directory of the user's home directory ROOT might have
+# been modified to replace the $HOME prefix with a single "~" character.
+proc run_unquoted_tests { root } {
+ # Test all the commands which allow quoting of filenames, and
+ # which require whitespace to be escaped in unquoted filenames.
+ foreach_with_prefix cmd { "set logging file" "target core" \
+ "add-auto-load-safe-path" } {
+ gdb_start
+
+ test_gdb_complete_none "$cmd ${root}/xx" \
"expand a non-existent filename"

- test_gdb_complete_unique "file ${qc}${root}/a" \
- "file ${qc}${root}/aaa/" "" false \
+ test_gdb_complete_unique "$cmd ${root}/a" \
+ "$cmd ${root}/aaa/" "" false \
"expand a unique filename"

- test_gdb_complete_multiple "file ${qc}${root}/" \
+ test_gdb_complete_unique "$cmd ${root}/bb2/dir 1/uni" \
+ "$cmd ${root}/bb2/dir 1/unique file" " " false \
+ "expand a unique filename containing whitespace"
+
+ test_gdb_complete_multiple "$cmd ${root}/" \
"b" "b" {
"bb1/"
"bb2/"
- } "" "${qc}" false \
+ } "" "" false \
"expand multiple directory names"

- test_gdb_complete_multiple "file ${qc}${root}/" \
+ test_gdb_complete_multiple "$cmd ${root}/" \
"c" "c" {
"cc1/"
"cc2"
- } "" "${qc}" false \
+ } "" "" false \
"expand mixed directory and file names"

- # GDB does not currently escape word break characters
- # (e.g. white space) correctly in unquoted filenames.
- if { $qc ne "" } {
- set sp " "
-
- test_gdb_complete_multiple "file ${qc}${root}/aaa/" \
- "a" "a${sp}" {
- "aa bb"
- "aa cc"
- } "" "${qc}" false \
- "expand filenames containing spaces"
- }
+ test_gdb_complete_multiple "$cmd ${root}/aaa/" \
+ "a" "a " {
+ "aa bb"
+ "aa cc"
+ } "" "" false \
+ "expand filenames containing spaces"
+
+ test_gdb_complete_multiple "$cmd ${root}/bb2/dir 2/" \
+ "fi" "le " {
+ "file 1"
+ "file 2"
+ } "" "" false \
+ "expand filenames containing spaces in path"
+
+ gdb_exit
}
}

-gdb_start
-
set root [setup_directory_tree]

-run_tests $root
+run_quoting_and_escaping_tests $root
+run_unquoted_tests $root

# This test relies on using the $HOME directory. We could make this
# work for remote hosts, but right now, this isn't supported.
@@ -114,7 +193,8 @@ if {![is_remote host]} {

with_test_prefix "with tilde" {
# And rerun the tests.
- run_tests $tilde_root
+ run_quoting_and_escaping_tests $tilde_root
+ run_unquoted_tests $tilde_root
}
}
}
diff --git a/gdb/tracectf.c b/gdb/tracectf.c
index 282a8250ac1..94b8a283ac6 100644
--- a/gdb/tracectf.c
+++ b/gdb/tracectf.c
@@ -1721,6 +1721,7 @@ void
_initialize_ctf ()
{
#if HAVE_LIBBABELTRACE
- add_target (ctf_target_info, ctf_target_open, filename_completer);
+ struct cmd_list_element *c = add_target (ctf_target_info, ctf_target_open);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
#endif
}
diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c
index eb879c17970..5a1aa0bc9a1 100644
--- a/gdb/tracefile-tfile.c
+++ b/gdb/tracefile-tfile.c
@@ -1120,5 +1120,7 @@ void _initialize_tracefile_tfile ();
void
_initialize_tracefile_tfile ()
{
- add_target (tfile_target_info, tfile_target_open, filename_completer);
+ struct cmd_list_element *c =
+ add_target (tfile_target_info, tfile_target_open);
+ set_cmd_completer_handle_brkchars (c, filename_completer_handle_brkchars);
}
--
2.25.4