@@ -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 @@ struct option_def_and_value
{
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