[1/2] gdb: Allow quoting around string options in the gdb::option framework
Commit Message
Currently string options must be a single string with no whitespace,
this limitation prevents the gdb::option framework being used in some
places.
After this commit, string options can be quoted in single or double
quotes, and quote characters can be escaped with a backslash if needed
to either place them within quotes, or to avoid starting a quoted
argument.
This test adds a new function extract_string_maybe_quoted which is
basically a copy of extract_arg_maybe_quoted from cli/cli-utils.c,
however, the cli-utils.c function will be deleted in the next commit.
There are tests to exercise the new quoting mechanism.
gdb/ChangeLog:
* cli/cli-option.c (parse_option): Use extract_string_maybe_quoted
to extract string arguments.
* common/common-utils.c (extract_string_maybe_quoted): New function.
* common/common-utils.h (extract_string_maybe_quoted): Declare.
gdb/testsuite/ChangeLog:
* gdb.base/options.exp (expect_string): Dequote strings in
results.
(test-string): Test strings with different quoting and reindent.
---
gdb/ChangeLog | 7 +++++
gdb/cli/cli-option.c | 5 ++--
gdb/gdbsupport/common-utils.c | 59 ++++++++++++++++++++++++++++++++++++++
gdb/gdbsupport/common-utils.h | 10 +++++++
gdb/testsuite/ChangeLog | 6 ++++
gdb/testsuite/gdb.base/options.exp | 54 +++++++++++++++++++++++-----------
6 files changed, 121 insertions(+), 20 deletions(-)
Comments
>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
Andrew> proc expect_string {str operand} {
Andrew> + # Dequote the string in the expected output.
Andrew> + if { ( [string range $str 0 0] == "\"" && [string range $str end end] == "\"") \
Andrew> + || ([string range $str 0 0] == "'" && [string range $str end end] == "'")} {
These lines look over-long.
Also the trailing "\" isn't needed here.
Tom
* Tom Tromey <tom@tromey.com> [2019-07-11 10:17:43 -0600]:
> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
>
> Andrew> proc expect_string {str operand} {
> Andrew> + # Dequote the string in the expected output.
> Andrew> + if { ( [string range $str 0 0] == "\"" && [string range $str end end] == "\"") \
> Andrew> + || ([string range $str 0 0] == "'" && [string range $str end end] == "'")} {
>
> These lines look over-long.
> Also the trailing "\" isn't needed here.
Pushed with this fix.
Thanks,
Andrew
@@ -434,13 +434,12 @@ parse_option (gdb::array_view<const option_def_group> options_group,
}
const char *arg_start = *args;
- *args = skip_to_space (*args);
-
+ std::string str = extract_string_maybe_quoted (args);
if (*args == arg_start)
error (_("-%s requires an argument"), match->name);
option_value val;
- val.string = savestring (arg_start, *args - arg_start);
+ val.string = xstrdup (str.c_str ());
return option_def_and_value {*match, match_ctx, val};
}
@@ -160,6 +160,65 @@ savestring (const char *ptr, size_t len)
return p;
}
+/* See documentation in common-utils.h. */
+
+std::string
+extract_string_maybe_quoted (const char **arg)
+{
+ bool squote = false;
+ bool dquote = false;
+ bool bsquote = false;
+ std::string result;
+ const char *p = *arg;
+
+ /* Find the start of the argument. */
+ p = skip_spaces (p);
+
+ /* Parse p similarly to gdb_argv buildargv function. */
+ while (*p != '\0')
+ {
+ if (isspace (*p) && !squote && !dquote && !bsquote)
+ break;
+ else
+ {
+ if (bsquote)
+ {
+ bsquote = false;
+ result += *p;
+ }
+ else if (*p == '\\')
+ bsquote = true;
+ else if (squote)
+ {
+ if (*p == '\'')
+ squote = false;
+ else
+ result += *p;
+ }
+ else if (dquote)
+ {
+ if (*p == '"')
+ dquote = false;
+ else
+ result += *p;
+ }
+ else
+ {
+ if (*p == '\'')
+ squote = true;
+ else if (*p == '"')
+ dquote = true;
+ else
+ result += *p;
+ }
+ p++;
+ }
+ }
+
+ *arg = p;
+ return result;
+}
+
/* The bit offset of the highest byte in a ULONGEST, for overflow
checking. */
@@ -94,6 +94,16 @@ void string_vappendf (std::string &dest, const char* fmt, va_list args)
char *savestring (const char *ptr, size_t len);
+/* Extract the next word from ARG. The next word is defined as either,
+ everything up to the next space, or, if the next word starts with either
+ a single or double quote, then everything up to the closing quote. The
+ enclosing quotes are not returned in the result string. The pointer in
+ ARG is updated to point to the first character after the end of the
+ word, or, for quoted words, the first character after the closing
+ quote. */
+
+std::string extract_string_maybe_quoted (const char **arg);
+
/* The strerror() function can return NULL for errno values that are
out of range. Provide a "safe" version that always returns a
printable string. */
@@ -128,6 +128,11 @@ proc expect_integer {option val operand} {
# test-options xxx", with -string set to $STR. OPERAND is the
# expected operand.
proc expect_string {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 0 -zuint-unl 0 -string '$str' -- $operand"
}
@@ -967,26 +972,41 @@ proc_with_prefix test-string {variant} {
"-string requires an argument"
}
- res_test_gdb_complete_none \
- "1 [expect_none ""]" \
- "$cmd -string STR"
- gdb_test "$cmd -string STR --" [expect_string "STR" ""]
+ 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 -string ${str}"
+ gdb_test "$cmd -string ${str} --" [expect_string "${str}" ""]
- # Completing at "-" after parsing STR should list all options.
- res_test_gdb_complete_multiple \
- "1 [expect_string "STR" "-"]" \
- "$cmd -string STR " "-" "" $all_options
+ # Completing at "-" after parsing STR should list all options.
+ res_test_gdb_complete_multiple \
+ "1 [expect_string "${str}" "-"]" \
+ "$cmd -string ${str} " "-" "" $all_options
- # Check that only FOO is considered part of the string's value.
- # I.e., that we stop parsing the string at the first whitespace.
- if {$variant == "require-delimiter"} {
- res_test_gdb_complete_none \
- "1 [expect_string "FOO" "BAR"]" \
- "$cmd -string FOO BAR"
- } else {
- res_test_gdb_complete_none "0 BAR" "$cmd -string FOO BAR"
+ # Check that only $STR is considered part of the string's value.
+ # I.e., that we stop parsing the string at the first
+ # whitespace or after the closing quote of $STR.
+ if {$variant == "require-delimiter"} {
+ res_test_gdb_complete_none \
+ "1 [expect_string "${str}" "BAR"]" \
+ "$cmd -string ${str} BAR"
+ } else {
+ res_test_gdb_complete_none "0 BAR" "$cmd -string ${str} BAR"
+ }
+ gdb_test "$cmd -string ${str} BAR --" "Unrecognized option at: BAR --"
}
- gdb_test "$cmd -string FOO BAR --" "Unrecognized option at: BAR --"
}
# Run the options framework tests first.