mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 01:53:38 +08:00
gdb: add filename option support
This commit adds support for filename options to GDB's option sub-system (see cli/cli-option.{c,h}). The new filename options support quoted and escaped filenames, and tab completion is fully supported. This commit adds the new option, and adds these options to the 'maintenance test-options' command as '-filename', along with some tests that exercise this new option. I've split the -filename testing into two. In gdb.base/options.exp we use the -filename option with some arbitrary strings. This tests that GDB can correctly extract the value from a filename option, and that GDB can complete other options after a filename option. However, these tests don't actually pass real filenames, nor do they test filename completion. In gdb.base/filename-completion.exp I have added some tests that test the -filename option with real filenames, and exercise filename tab completion. This commit doesn't include any real uses of the new filename options, that will come in the next commit.
This commit is contained in:
parent
91f8973ba3
commit
3105b46ed1
@ -43,7 +43,7 @@ union option_value
|
||||
/* For var_enum options. */
|
||||
const char *enumeration;
|
||||
|
||||
/* For var_string options. This is malloc-allocated. */
|
||||
/* For var_string and var_filename options. This is allocated with new. */
|
||||
std::string *string;
|
||||
};
|
||||
|
||||
@ -85,7 +85,7 @@ struct option_def_and_value
|
||||
{
|
||||
if (value.has_value ())
|
||||
{
|
||||
if (option.type == var_string)
|
||||
if (option.type == var_string || option.type == var_filename)
|
||||
delete value->string;
|
||||
}
|
||||
}
|
||||
@ -102,7 +102,7 @@ private:
|
||||
{
|
||||
if (value.has_value ())
|
||||
{
|
||||
if (option.type == var_string)
|
||||
if (option.type == var_string || option.type == var_filename)
|
||||
value->string = nullptr;
|
||||
}
|
||||
}
|
||||
@ -452,6 +452,78 @@ parse_option (gdb::array_view<const option_def_group> options_group,
|
||||
return option_def_and_value {*match, match_ctx, val};
|
||||
}
|
||||
|
||||
case var_filename:
|
||||
{
|
||||
if (check_for_argument (args, "--"))
|
||||
{
|
||||
/* Treat e.g., "maint test-options -filename --" as if there
|
||||
was no argument after "-filename". */
|
||||
error (_("-%s requires an argument"), match->name);
|
||||
}
|
||||
|
||||
const char *arg_start = *args;
|
||||
std::string str = extract_string_maybe_quoted (args);
|
||||
|
||||
/* If we are performing completion, and extracting STR moved ARGS
|
||||
to the end of the line, then the user is trying to complete the
|
||||
filename value.
|
||||
|
||||
If ARGS didn't make it to the end of the line then the filename
|
||||
value is already complete and the user is trying to complete
|
||||
something later on the line. */
|
||||
if (completion != nullptr && **args == '\0')
|
||||
{
|
||||
/* Preserve the current custom word point. If the call to
|
||||
advance_to_filename_maybe_quoted_complete_word_point below
|
||||
skips to the end of the command line then the custom word
|
||||
point will have been updated even though we generate no
|
||||
completions.
|
||||
|
||||
However, *ARGS will also have been updated, and the general
|
||||
option completion code (which we will return too) also
|
||||
updates the custom word point based on the adjustment made
|
||||
to *ARGS.
|
||||
|
||||
And so, if we don't find any completions, we should restore
|
||||
the custom word point value, this leaves the generic option
|
||||
completion code free to make its own adjustments. */
|
||||
int prev_word_pt = completion->tracker.custom_word_point ();
|
||||
|
||||
/* From ARG_START move forward to the start of the completion
|
||||
word, this will skip over any opening quote if there is
|
||||
one.
|
||||
|
||||
If the word to complete is fully quoted, i.e. has an
|
||||
opening and closing quote, then this will skip over the
|
||||
word entirely and leave WORD pointing to the end of the
|
||||
input string. */
|
||||
const char *word
|
||||
= advance_to_filename_maybe_quoted_complete_word_point
|
||||
(completion->tracker, arg_start);
|
||||
|
||||
if (word == arg_start || *word != '\0')
|
||||
{
|
||||
filename_maybe_quoted_completer (nullptr, completion->tracker,
|
||||
arg_start, word);
|
||||
|
||||
if (completion->tracker.have_completions ())
|
||||
return {};
|
||||
}
|
||||
|
||||
/* No completions. Restore the custom word point. See the
|
||||
comment above for why this is needed. */
|
||||
completion->tracker.set_custom_word_point (prev_word_pt);
|
||||
}
|
||||
|
||||
/* Check we did manage to extract something. */
|
||||
if (*args == arg_start)
|
||||
error (_("-%s requires an argument"), match->name);
|
||||
|
||||
option_value val;
|
||||
val.string = new std::string (std::move (str));
|
||||
return option_def_and_value {*match, match_ctx, val};
|
||||
}
|
||||
|
||||
default:
|
||||
/* Not yet. */
|
||||
gdb_assert_not_reached ("option type not supported");
|
||||
@ -612,6 +684,7 @@ save_option_value_in_ctx (std::optional<option_def_and_value> &ov)
|
||||
= ov->value->enumeration;
|
||||
break;
|
||||
case var_string:
|
||||
case var_filename:
|
||||
*ov->option.var_address.string (ov->option, ov->ctx)
|
||||
= std::move (*ov->value->string);
|
||||
break;
|
||||
@ -701,6 +774,8 @@ get_val_type_str (const option_def &opt, std::string &buffer)
|
||||
}
|
||||
case var_string:
|
||||
return "STRING";
|
||||
case var_filename:
|
||||
return "FILENAME";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@ -856,6 +931,15 @@ add_setshow_cmds_for_options (command_class cmd_class,
|
||||
nullptr, option.show_cmd_cb,
|
||||
set_list, show_list);
|
||||
}
|
||||
else if (option.type == var_filename)
|
||||
{
|
||||
add_setshow_filename_cmd (option.name, cmd_class,
|
||||
option.var_address.string (option, data),
|
||||
option.set_doc, option.show_doc,
|
||||
option.help_doc,
|
||||
nullptr, option.show_cmd_cb,
|
||||
set_list, show_list);
|
||||
}
|
||||
else
|
||||
gdb_assert_not_reached ("option type not handled");
|
||||
}
|
||||
|
@ -308,6 +308,26 @@ struct string_option_def : option_def
|
||||
}
|
||||
};
|
||||
|
||||
/* A var_filename command line option. */
|
||||
|
||||
template<typename Context>
|
||||
struct filename_option_def : option_def
|
||||
{
|
||||
filename_option_def (const char *long_option_,
|
||||
std::string *(*get_var_address_cb_) (Context *),
|
||||
show_value_ftype *show_cmd_cb_,
|
||||
const char *set_doc_,
|
||||
const char *show_doc_ = nullptr,
|
||||
const char *help_doc_ = nullptr)
|
||||
: option_def (long_option_, var_filename, nullptr,
|
||||
(erased_get_var_address_ftype *) get_var_address_cb_,
|
||||
show_cmd_cb_,
|
||||
set_doc_, show_doc_, help_doc_)
|
||||
{
|
||||
var_address.string = detail::get_var_address<std::string, Context>;
|
||||
}
|
||||
};
|
||||
|
||||
/* A group of options that all share the same context pointer to pass
|
||||
to the options' get-current-value callbacks. */
|
||||
struct option_def_group
|
||||
|
@ -57,12 +57,13 @@
|
||||
readline, for proper testing of TAB completion.
|
||||
|
||||
These maintenance commands support options of all the different
|
||||
available kinds of commands (boolean, enum, flag, string, uinteger):
|
||||
available kinds of commands (boolean, enum, flag, string, filename,
|
||||
uinteger):
|
||||
|
||||
(gdb) maint test-options require-delimiter -[TAB]
|
||||
-bool -pinteger-unlimited -xx1
|
||||
-enum -string -xx2
|
||||
-flag -uinteger-unlimited
|
||||
-bool -flag -uinteger-unlimited
|
||||
-enum -pinteger-unlimited -xx1
|
||||
-filename -string -xx2
|
||||
|
||||
(gdb) maint test-options require-delimiter -bool o[TAB]
|
||||
off on
|
||||
@ -77,14 +78,14 @@
|
||||
Invoking the commands makes them print out the options parsed:
|
||||
|
||||
(gdb) maint test-options unknown-is-error -flag -enum yyy cmdarg
|
||||
-flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg
|
||||
-flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -filename '' -- cmdarg
|
||||
|
||||
(gdb) maint test-options require-delimiter -flag -enum yyy cmdarg
|
||||
-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -- -flag -enum yyy cmdarg
|
||||
-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -filename '' -- -flag -enum yyy cmdarg
|
||||
(gdb) maint test-options require-delimiter -flag -enum yyy cmdarg --
|
||||
Unrecognized option at: cmdarg --
|
||||
(gdb) maint test-options require-delimiter -flag -enum yyy -- cmdarg
|
||||
-flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg
|
||||
-flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -filename '' -- cmdarg
|
||||
|
||||
The "maint show test-options-completion-result" command exists in
|
||||
order to do something similar for completion:
|
||||
@ -135,6 +136,7 @@ struct test_options_opts
|
||||
unsigned int uint_unl_opt = 0;
|
||||
int pint_unl_opt = 0;
|
||||
std::string string_opt;
|
||||
std::string filename_opt;
|
||||
|
||||
test_options_opts () = default;
|
||||
|
||||
@ -146,7 +148,8 @@ struct test_options_opts
|
||||
{
|
||||
gdb_printf (file,
|
||||
_("-flag %d -xx1 %d -xx2 %d -bool %d "
|
||||
"-enum %s -uint-unl %s -pint-unl %s -string '%s' -- %s\n"),
|
||||
"-enum %s -uint-unl %s -pint-unl %s -string '%s' "
|
||||
"-filename '%s' -- %s\n"),
|
||||
flag_opt,
|
||||
xx1_opt,
|
||||
xx2_opt,
|
||||
@ -159,6 +162,7 @@ struct test_options_opts
|
||||
? "unlimited"
|
||||
: plongest (pint_unl_opt)),
|
||||
string_opt.c_str (),
|
||||
filename_opt.c_str (),
|
||||
args);
|
||||
}
|
||||
};
|
||||
@ -233,6 +237,14 @@ static const gdb::option::option_def test_options_option_defs[] = {
|
||||
nullptr, /* show_cmd_cb */
|
||||
N_("A string option."),
|
||||
},
|
||||
|
||||
/* A filename option. */
|
||||
gdb::option::filename_option_def<test_options_opts> {
|
||||
"filename",
|
||||
[] (test_options_opts *opts) { return &opts->filename_opt; },
|
||||
nullptr, /* show_cmd_cb */
|
||||
N_("A filename option."),
|
||||
},
|
||||
};
|
||||
|
||||
/* Create an option_def_group for the test_options_opts options, with
|
||||
|
@ -412,6 +412,13 @@ proc run_quoting_and_escaping_tests { root } {
|
||||
|
||||
run_mid_line_completion_tests $root $cmd
|
||||
}
|
||||
|
||||
foreach sub_cmd { require-delimiter unknown-is-error unknown-is-operand } {
|
||||
set cmd "maintenance test-options $sub_cmd -filename"
|
||||
with_test_prefix "cmd=$cmd" {
|
||||
run_quoting_and_escaping_tests_1 $root $cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Helper for run_unquoted_tests. ROOT is the root directory as setup
|
||||
|
@ -99,21 +99,21 @@ proc make_cmd {variant} {
|
||||
# operand.
|
||||
proc expect_none {operand} {
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '' -- $operand"
|
||||
-string '' -filename '' -- $operand"
|
||||
}
|
||||
|
||||
# Return a string for the expected result of running "maint
|
||||
# test-options xxx", with -flag set. OPERAND is the expected operand.
|
||||
proc expect_flag {operand} {
|
||||
return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '' -- $operand"
|
||||
-string '' -filename '' -- $operand"
|
||||
}
|
||||
|
||||
# Return a string for the expected result of running "maint
|
||||
# test-options xxx", with -bool set. OPERAND is the expected operand.
|
||||
proc expect_bool {operand} {
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '' -- $operand"
|
||||
-string '' -filename '' -- $operand"
|
||||
}
|
||||
|
||||
# Return a string for the expected result of running "maint
|
||||
@ -123,10 +123,10 @@ proc expect_bool {operand} {
|
||||
proc expect_integer {option val operand} {
|
||||
if {$option == "uinteger-unlimited"} {
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl $val\
|
||||
-pint-unl 0 -string '' -- $operand"
|
||||
-pint-unl 0 -string '' -filename '' -- $operand"
|
||||
} elseif {$option == "pinteger-unlimited"} {
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0\
|
||||
-pint-unl $val -string '' -- $operand"
|
||||
-pint-unl $val -string '' -filename '' -- $operand"
|
||||
} else {
|
||||
error "unsupported option: $option"
|
||||
}
|
||||
@ -144,12 +144,28 @@ proc expect_string {str operand} {
|
||||
set str [string range $str 1 end-1]
|
||||
}
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '$str' -- $operand"
|
||||
-string '$str' -filename '' -- $operand"
|
||||
}
|
||||
|
||||
# Return a string for the expected result of running "maint
|
||||
# test-options xxx", with -filename set to $STR. OPERAND is the
|
||||
# expected operand.
|
||||
proc expect_filename {str operand} {
|
||||
# Dequote the string in the expected output.
|
||||
if { ( [string range $str 0 0] == "\""
|
||||
&& [string range $str end end] == "\"")
|
||||
|| ([string range $str 0 0] == "'"
|
||||
&& [string range $str end end] == "'")} {
|
||||
set str [string range $str 1 end-1]
|
||||
}
|
||||
return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '' -filename '$str' -- $operand"
|
||||
}
|
||||
|
||||
set all_options {
|
||||
"-bool"
|
||||
"-enum"
|
||||
"-filename"
|
||||
"-flag"
|
||||
"-pinteger-unlimited"
|
||||
"-string"
|
||||
@ -612,7 +628,7 @@ proc_with_prefix test-flag {variant} {
|
||||
# Extract twice the same flag, separated by one space.
|
||||
gdb_test "$cmd -xx1 -xx2 -xx1 -xx2 -xx1 -- non flags args" \
|
||||
"-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
|
||||
-string '' -- non flags args"
|
||||
-string '' -filename '' -- non flags args"
|
||||
|
||||
# Extract 2 known flags in front of unknown flags.
|
||||
gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \
|
||||
@ -1031,6 +1047,70 @@ proc_with_prefix test-string {variant} {
|
||||
}
|
||||
}
|
||||
|
||||
# Filename option tests. These tests only focus on how GDB parses the
|
||||
# filename option, and ensures that GDB can complete things after the
|
||||
# filename value. The actual strings passed as filenames in this proc
|
||||
# are not actual files that exist on disk.
|
||||
#
|
||||
# Filename options do also support completion. For testing of this
|
||||
# aspect see the gdb.base/filename-completion.exp script.
|
||||
proc_with_prefix test-filename {variant} {
|
||||
global all_options
|
||||
|
||||
set cmd [make_cmd $variant]
|
||||
|
||||
# Check that "-" where a value is expected does not show the
|
||||
# command's options. I.e., a filename's value is not optional.
|
||||
# Check both completion and running the command.
|
||||
res_test_gdb_complete_none \
|
||||
"1 [expect_none ""]" \
|
||||
"$cmd -filename -"
|
||||
gdb_test "$cmd -filename --" \
|
||||
"-filename requires an argument"
|
||||
if {$variant == "require-delimiter"} {
|
||||
gdb_test "$cmd -filename" [expect_none "-filename"]
|
||||
} else {
|
||||
gdb_test "$cmd -filename" \
|
||||
"-filename requires an argument"
|
||||
}
|
||||
|
||||
foreach_with_prefix str {
|
||||
"STR"
|
||||
"\"STR\""
|
||||
"\\\"STR"
|
||||
"'STR'"
|
||||
"\\'STR"
|
||||
"\"STR AAA\""
|
||||
"'STR BBB'"
|
||||
"\"STR 'CCC' DDD\""
|
||||
"'STR \"EEE\" FFF'"
|
||||
"\"STR \\\"GGG\\\" HHH\""
|
||||
"'STR \\\'III\\\' JJJ'"
|
||||
} {
|
||||
res_test_gdb_complete_none \
|
||||
"1 [expect_none ""]" \
|
||||
"$cmd -filename ${str}"
|
||||
gdb_test "$cmd -filename ${str} --" [expect_filename "${str}" ""]
|
||||
|
||||
# Completing at "-" after parsing STR should list all options.
|
||||
res_test_gdb_complete_multiple \
|
||||
"1 [expect_filename "${str}" "-"]" \
|
||||
"$cmd -filename ${str} " "-" "" $all_options
|
||||
|
||||
# Check that only $STR is considered part of the filename's value.
|
||||
# I.e., that we stop parsing the filename at the first
|
||||
# whitespace or after the closing quote of $STR.
|
||||
if {$variant == "require-delimiter"} {
|
||||
res_test_gdb_complete_none \
|
||||
"1 [expect_filename "${str}" "BAR"]" \
|
||||
"$cmd -filename ${str} BAR"
|
||||
} else {
|
||||
res_test_gdb_complete_none "0 BAR" "$cmd -filename ${str} BAR"
|
||||
}
|
||||
gdb_test "$cmd -filename ${str} BAR --" "Unrecognized option at: BAR --"
|
||||
}
|
||||
}
|
||||
|
||||
# Run the options framework tests first.
|
||||
foreach_with_prefix cmd {
|
||||
"require-delimiter"
|
||||
@ -1045,6 +1125,7 @@ foreach_with_prefix cmd {
|
||||
}
|
||||
test-enum $cmd
|
||||
test-string $cmd
|
||||
test-filename $cmd
|
||||
}
|
||||
|
||||
# Run the print integration tests, both as "standalone", and under
|
||||
|
Loading…
Reference in New Issue
Block a user