From patchwork Thu Jul 11 13:20:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 33669 Received: (qmail 32566 invoked by alias); 11 Jul 2019 13:21:09 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 32522 invoked by uid 89); 11 Jul 2019 13:21:09 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy=III, FFF, ccc, CCC X-HELO: mail-wr1-f51.google.com Received: from mail-wr1-f51.google.com (HELO mail-wr1-f51.google.com) (209.85.221.51) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 11 Jul 2019 13:21:07 +0000 Received: by mail-wr1-f51.google.com with SMTP id y4so6309492wrm.2 for ; Thu, 11 Jul 2019 06:21:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=HSA2JcmM32aOB9n1PmFfqHTsQ4qoFaJoUAmAyDXyvZQ=; b=NT8k6LJbfw1DK1PqQnTF5mvvW3xShSTRyAbVMUz1vR0I6SNXREGcnFVXarSr6xNORL Y6gStr86IRmqdKlp/Qc24DkCH4N6MZq5AVWFFMalDrZiw+pzuuYiSGUtd/d/72j8617S 4jY68ob76dMFgZhU/2uJd+vauHmo8MnujRLP6wrVQt4QYr33/69OerGQRAmIgte1/6XL pqPS/k4exwA/1W8B7ZPDCfAZWfp3nj12vm/MU9/FqwJKTEBsoHpta15gu2J1L9J8BhuD dE6X92IFQ/mEGGI5YTqSlfwhD/00onzjyRK59K2kjOD2zhE5shmuvNkE8DsLe2myW73F wLHg== Return-Path: Received: from localhost (host86-128-12-99.range86-128.btcentralplus.com. [86.128.12.99]) by smtp.gmail.com with ESMTPSA id o185sm5186219wmo.45.2019.07.11.06.21.04 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 11 Jul 2019 06:21:04 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 1/2] gdb: Allow quoting around string options in the gdb::option framework Date: Thu, 11 Jul 2019 14:20:58 +0100 Message-Id: <56de17c8da0e7911f8b1e58d0192e19035f06916.1562850845.git.andrew.burgess@embecosm.com> In-Reply-To: References: In-Reply-To: References: X-IsSubscribed: yes 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(-) diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c index 07d552b7f5b..eb8ef79d4f3 100644 --- a/gdb/cli/cli-option.c +++ b/gdb/cli/cli-option.c @@ -434,13 +434,12 @@ parse_option (gdb::array_view 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}; } diff --git a/gdb/gdbsupport/common-utils.c b/gdb/gdbsupport/common-utils.c index 384029db70a..d1059de0b33 100644 --- a/gdb/gdbsupport/common-utils.c +++ b/gdb/gdbsupport/common-utils.c @@ -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. */ diff --git a/gdb/gdbsupport/common-utils.h b/gdb/gdbsupport/common-utils.h index 52bf3437b1c..a5312cb0c49 100644 --- a/gdb/gdbsupport/common-utils.h +++ b/gdb/gdbsupport/common-utils.h @@ -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. */ diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index e8f571d9ba9..3495a0142fb 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -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.