From patchwork Thu Sep 6 23:29:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 29230 Received: (qmail 70125 invoked by alias); 6 Sep 2018 23:29:15 -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 70113 invoked by uid 89); 6 Sep 2018 23:29:15 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-24.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.2 spammy=ab, Within, string_view, quoting X-HELO: mail-wr1-f65.google.com Received: from mail-wr1-f65.google.com (HELO mail-wr1-f65.google.com) (209.85.221.65) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 06 Sep 2018 23:29:12 +0000 Received: by mail-wr1-f65.google.com with SMTP id a108-v6so13031054wrc.13 for ; Thu, 06 Sep 2018 16:29:12 -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; bh=ceATFR/sNEgZhU149jPwR3h3duwzgm7NfljohywTsGw=; b=FbSSTvHRMK/CiS5wRLoUg7EzT9n26UbULBwH1HovYGVoIOKCQIcT17qty3m7FTkipo hUMnTsCVI9w9KHwitBpVmZt5fQfEju/IvfQubi/NXRzfVI3I0S5MMT7bgL1hMDcSASGb o5za8VkV4W6UtNzG60JvysQf8ZR6GuvJB4ok7Qs7VigKoE7oF9WZHi1SkhYOyywBYCLx ubzwwZ7PQZF9wtgmxTa1U5Gu7Ex4rd1Iz6cqAoRcR4B/pttrVrTtJ17Cx6s5v/RUw9Ux bn4a83zF+rl1j7KHVpm37GZrkOUt6xsCAv6mqBw8Aoni5dZ+xWBSR0Nam5QpLp9J9FKJ xbvQ== Return-Path: Received: from localhost ([92.54.160.98]) by smtp.gmail.com with ESMTPSA id 132-v6sm5505541wmd.13.2018.09.06.16.29.07 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 06 Sep 2018 16:29:08 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Philippe Waroquiers , Tom Tromey , Andrew Burgess Subject: [PATCHv2] gdb: Rewrite argument handling for user-defined commands Date: Fri, 7 Sep 2018 00:29:04 +0100 Message-Id: <20180906232904.13286-1-andrew.burgess@embecosm.com> In-Reply-To: References: X-IsSubscribed: yes Here's a new version of the quoting patch which now uses single and double quotes for quoting arguments. I look forward to any feedback. Eli - I suspect that the documentation changes would need some work, but you should probably wait to review, as I suspect this patch will change again before it can be merged. Thanks, Andrew --- This commit rewrites argument passing for user-defined commands. The rewrite was inspired after this mailing list thread: https://sourceware.org/ml/gdb-patches/2018-08/msg00391.html The summary is that it was felt that in order to pass arguments that include whitespace, then single or double quotes should be used for quoting the argument. The problem is that currently, the quotes are included in the argument that is passed into the user-defined command, so passing the argument "1 + 1" will currently litterally pass "1 + 1" (including the quotes) to GDB, which is no good if what you want to do is to pass an expression. This commit changes how quoting works so that the quotes are NOT now included in the argument passed. If the user wants to include quotes, they would now need to use nested quotes, so "\"abc\"" will pass the argument "abc". It is also possible to use single quotes, so '"abc"' will also pass the argument "abc". As currently there's no documentation for how quoting works in user-defined commands this commit adds documentation for the new behaviour. The big risk with this commit is that this does change how arguments are passed to user-defined commands, and this might causes issues for existing users. gdb/ChangeLog: * cli/cli-script.c (user_args::m_command_line): Remove. (user_args::m_args): Change type. (user_args::user_args): Rewrite to build arguments into std::string. (user_args::insert_args): Update to take account of m_args type change. gdb/doc/ChangeLog: * gdb.texinfo (Define): Additional documentation about argument syntax. gdb/testsuite/ChangeLog: * gdb.base/commands.exp (user_defined_command_arg_quoting): New proc, which is added to the list of procs to call. * gdb.base/run.c (global_var): Defined global. --- gdb/ChangeLog | 9 ++++ gdb/cli/cli-script.c | 88 ++++++++++++++++++------------------- gdb/doc/ChangeLog | 5 +++ gdb/doc/gdb.texinfo | 66 +++++++++++++++++++++++++--- gdb/testsuite/ChangeLog | 6 +++ gdb/testsuite/gdb.base/commands.exp | 45 +++++++++++++++++++ gdb/testsuite/gdb.base/run.c | 3 ++ 7 files changed, 172 insertions(+), 50 deletions(-) diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index 8496fb85e6f..f2110331765 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -78,12 +78,8 @@ private: user_args (const user_args &) =delete; user_args &operator= (const user_args &) =delete; - /* It is necessary to store a copy of the command line to ensure - that the arguments are not overwritten before they are used. */ - std::string m_command_line; - - /* The arguments. Each element points inside M_COMMAND_LINE. */ - std::vector m_args; + /* The arguments. Parsed from the LINE passed into the constructor. */ + std::vector m_args; }; /* The stack of arguments passed to user defined functions. We need a @@ -749,56 +745,58 @@ user_args::user_args (const char *command_line) if (command_line == NULL) return; - m_command_line = command_line; - p = m_command_line.c_str (); + p = command_line; while (*p) { - const char *start_arg; - int squote = 0; - int dquote = 0; - int bsquote = 0; + std::string arg; + + bool bquote = false; + bool squote = false; + bool dquote = false; /* Strip whitespace. */ - while (*p == ' ' || *p == '\t') + while (isspace (*p)) p++; - /* P now points to an argument. */ - start_arg = p; - /* Get to the end of this argument. */ while (*p) { - if (((*p == ' ' || *p == '\t')) && !squote && !dquote && !bsquote) - break; - else - { - if (bsquote) - bsquote = 0; - else if (*p == '\\') - bsquote = 1; - else if (squote) - { - if (*p == '\'') - squote = 0; - } - else if (dquote) - { - if (*p == '"') - dquote = 0; - } - else - { - if (*p == '\'') - squote = 1; - else if (*p == '"') - dquote = 1; - } - p++; - } + /* If we find whitespace and we're not inside a single or double + quote then we have found the end of this argument. */ + if (isspace (*p) && !(squote || dquote)) + break; + else if (bquote) + bquote = 0; + else + { + /* If we're inside a single quote and we find another single + quote then this is the end of the argument. */ + if (*p == '\'' && !dquote) + { + ++p; + squote = !squote; + continue; + } + + /* If we're inside a double quote and we find another double + quote then this is the end of the argument. */ + if (*p == '"' && !squote) + { + ++p; + dquote = !dquote; + continue; + } + + if (*p == '\\' && !squote) + bquote = true; + } + + arg += *p; + ++p; } - m_args.emplace_back (start_arg, p - start_arg); + m_args.emplace_back (arg); } } @@ -863,7 +861,7 @@ user_args::insert_args (const char *line) const error (_("Missing argument %ld in user function."), i); else { - new_line.append (m_args[i].data (), m_args[i].length ()); + new_line.append (m_args[i]); line = tmp; } } diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f2d1155b4db..b159df3b217 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -25245,14 +25245,70 @@ @noindent This defines the command @code{adder}, which prints the sum of -its three arguments. Note the arguments are text substitutions, so they may -reference variables, use complex expressions, or even perform inferior -functions calls. +its three arguments. + +The arguments to user-defined commands are text substitutions, so they +may reference variables, use complex expressions, or even perform +inferior functions calls. Each argument is separated with whitespace, +so in the previous example three arguments were passed. The following +example also passes three arguments, though the arguments are more +complex: + +@smallexample +adder 10+1 10+2 10+3 +@end smallexample + +@noindent +However, if whitespace were added around the @code{+} characters, then +9 arguments would be passed, @code{adder} only uses the first 3 of +these arguments, and the others would be silently ignored, for example: + +@smallexample +adder 10 + 1 10 + 2 10 + 3 +@end smallexample + +Causes @value{GDBN} to try and evaluate the following, which is likely +invalid: + +@smallexample +print 10 + + + 1 +@end smallexample + +@noindent +Arguments can be quoted with double (@code{"}) or single (@code{'}) +quotes. These quotes are not passed through as part of the argument, +so the complex arguments from the previous example can be written as: + +@smallexample +adder '10 + 1' '10 + 2' '10 + 3' +@end smallexample + +@noindent +As the quotes are not passed through, then the previous example causes +@value{GDBN} to evaluate: + +@smallexample +print 10 + 1 + 10 + 2 + 10 + 3 +@end smallexample + +@noindent +Outside of quotes, a backslash can be used to pass a quote as part of +an argument, for example, @code{\'} will pass a single quote as an +argument, and @code{\"} passes a double quote as an argument. + +Within double quotes a backslash can be used to pass a literal double +quote, so @code{"\"abc\""} will pass the argument @code{"abc"}. + +Within single quotes a backslash does not escape a single quote, the +next single quote always ends a single quoted argument, and +backslashes within a single quoted argument are passed straight +through, so @code{'abc\'} will pass the argument @code{abc\}. @cindex argument count in user-defined commands @cindex how many arguments (user-defined commands) -In addition, @code{$argc} may be used to find out how many arguments have -been passed. +@noindent +Within a user-defined command @code{$argc} may be used to find out how +many arguments have been passed. @smallexample define adder diff --git a/gdb/testsuite/gdb.base/commands.exp b/gdb/testsuite/gdb.base/commands.exp index 52a22bb5ddc..4e398b84a95 100644 --- a/gdb/testsuite/gdb.base/commands.exp +++ b/gdb/testsuite/gdb.base/commands.exp @@ -1167,6 +1167,50 @@ proc_with_prefix backslash_in_multi_line_command_test {} { gdb_test "print 1" "" "run command" } +proc_with_prefix user_defined_command_arg_quoting {} { + gdb_test_multiple "define show_args" "define show_args" { + -re "End with" { + pass "define show_args" + } + } + + # This test should alternate between 0xdeadbeef and 0xfeedface two times. + gdb_test \ + [multi_line_input \ + {printf "nargs=%d:", $argc} \ + {set $i = 0} \ + {while $i < $argc} \ + {printf " "} \ + {eval "echo '$arg%d'", $i} \ + {set $i = $i + 1} \ + {end} \ + {printf "\n"} \ + {end}] \ + "" \ + "enter commands" + + gdb_test "show_args 1 2 3" \ + "nargs=3: '1' '2' '3'" + + gdb_test "show_args 1 '1 + 1' '1 + (1 + 1)'" \ + "nargs=3: '1' '1 \\+ 1' '1 \\+ \\(1 \\+ 1\\)'" + + gdb_test "show_args '{unsigned long long} &global_var'" \ + "nargs=1: '{unsigned long long} &global_var'" + + gdb_test "show_args '*((unsigned long long *) &global_var)'" \ + "nargs=1: '\\*\\(\\(unsigned long long \\*\\) &global_var\\)'" + + gdb_test "show_args '\"' \"'\" \"''\"" \ + "nargs=3: '\"' ''' ''''" + + gdb_test "show_args \\n a\\'b" \ + "nargs=2: '\r\n' 'a'b'" + + gdb_test "show_args \"This\\nIs\\nA\\nMulti Line\\nMessage" \ + "nargs=1: 'This\r\nIs\r\nA\r\nMulti Line\r\nMessage'" +} + gdbvar_simple_if_test gdbvar_simple_while_test gdbvar_complex_if_while_test @@ -1182,6 +1226,7 @@ user_defined_command_case_sensitivity user_defined_command_args_eval user_defined_command_args_stack_test user_defined_command_manyargs_test +user_defined_command_arg_quoting watchpoint_command_test test_command_prompt_position deprecated_command_test diff --git a/gdb/testsuite/gdb.base/run.c b/gdb/testsuite/gdb.base/run.c index 614b018260d..d89bad78bb4 100644 --- a/gdb/testsuite/gdb.base/run.c +++ b/gdb/testsuite/gdb.base/run.c @@ -8,6 +8,9 @@ #include "../lib/unbuffer_output.c" +/* Used by commands.exp test script. */ +volatile unsigned long long global_var = 34; + int factorial (int); int