gdb: Allow quoting around string options in the gdb::option framework
authorAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 11 Jul 2019 10:08:42 +0000 (11:08 +0100)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 11 Jul 2019 19:18:11 +0000 (20:18 +0100)
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
gdb/cli/cli-option.c
gdb/gdbsupport/common-utils.c
gdb/gdbsupport/common-utils.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/options.exp

index 90da3d42c1d4c0f5fa427ee913d60e34598d2ba8..5f9872b8dfa3d1146c1da5a909fb89c5663235ca 100644 (file)
@@ -1,3 +1,10 @@
+2019-07-11  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * 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.
+
 2019-07-11  Tom Tromey  <tromey@adacore.com>
 
        * main.c (get_init_files): Use GDBINIT, not gdbinit.
index 07d552b7f5bbdaff7bc6f11a4e5c0ba999879ef6..eb8ef79d4f361fac573ea76e12d3a448cb25485f 100644 (file)
@@ -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};
       }
 
index 384029db70a29d66c1b5f0f91af20f58574d4012..d1059de0b33f0d6c3364047e53bb3c9e259eafb0 100644 (file)
@@ -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.  */
 
index 52bf3437b1cd443f0901d2bc34a8eb5d0fbd710f..a5312cb0c4918b08f8f1702ce4e45afdeef56300 100644 (file)
@@ -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.  */
index 247171abaee7466869f682e9c0368ca44571f89f..238fcfc892565d3a327be0121938e15851072838 100644 (file)
@@ -1,3 +1,9 @@
+2019-07-11  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * gdb.base/options.exp (expect_string): Dequote strings in
+       results.
+       (test-string): Test strings with different quoting and reindent.
+
 2019-07-10  Tom Tromey  <tromey@adacore.com>
 
        * gdb.ada/mi_ex_cond.exp: Update expected results.
index e8f571d9ba9a712537d1d97123ca754cc731a986..561ca1d1b52c47e71e4fbe08481c1de8b9916e81 100644 (file)
@@ -128,6 +128,13 @@ 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 +974,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.
This page took 0.032911 seconds and 4 git commands to generate.