From patchwork Thu Jun 27 19:14:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 33464 Received: (qmail 86173 invoked by alias); 27 Jun 2019 19:14:36 -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 86078 invoked by uid 89); 27 Jun 2019 19:14:36 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=understands X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 27 Jun 2019 19:14:34 +0000 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1CD1D81F0E for ; Thu, 27 Jun 2019 19:14:33 +0000 (UTC) Received: from localhost.localdomain (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id A26136012E for ; Thu, 27 Jun 2019 19:14:32 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 5/5] pipe command completer Date: Thu, 27 Jun 2019 20:14:27 +0100 Message-Id: <20190627191427.20742-6-palves@redhat.com> In-Reply-To: <20190627191427.20742-1-palves@redhat.com> References: <20190627191427.20742-1-palves@redhat.com> This commit adds a completer for the "pipe" command. It can complete "pipe"'s options, and the specified GDB command. To make the completer aware of the "-d" option, this converts the option processing to use gdb::option. Tests included. gdb/ChangeLog: yyyy-mm-dd Pedro Alves * cli/cli-cmds.c (struct pipe_cmd_opts): New. (pipe_cmd_option_defs): New. (make_pipe_cmd_options_def_group): New. (pipe_command): Use gdb::option::process_options. (pipe_command_completer): New function. (_initialize_cli_cmds): Install completer for "pipe" command. gdb/testsuite/ChangeLog: yyyy-mm-dd Pedro Alves * gdb.base/shell.exp: Load completion-support.exp. Adjust expected error output. Add completion tests. --- gdb/cli/cli-cmds.c | 92 ++++++++++++++++++++++++++++++++++++---- gdb/testsuite/gdb.base/shell.exp | 47 +++++++++++++++++++- 2 files changed, 128 insertions(+), 11 deletions(-) diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index bc32fbbaf88..054d80b9b96 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -960,32 +960,68 @@ edit_command (const char *arg, int from_tty) xfree (p); } +/* The options for the "pipe" command. */ + +struct pipe_cmd_opts +{ + /* For "-d". */ + char *delimiter = nullptr; + + ~pipe_cmd_opts () + { + xfree (delimiter); + } +}; + +static const gdb::option::option_def pipe_cmd_option_defs[] = { + + gdb::option::string_option_def { + "d", + [] (pipe_cmd_opts *opts) { return &opts->delimiter; }, + nullptr, + N_("Indicates to use the specified delimiter string to separate\n\ +COMMAND from SHELL_COMMAND, in alternative to |. This is useful in\n\ +case COMMAND contains a | character."), + }, + +}; + +/* Create an option_def_group for the "pipe" command's options, with + OPTS as context. */ + +static inline gdb::option::option_def_group +make_pipe_cmd_options_def_group (pipe_cmd_opts *opts) +{ + return {{pipe_cmd_option_defs}, opts}; +} + /* Implementation of the "pipe" command. */ static void pipe_command (const char *arg, int from_tty) { - std::string delim ("|"); + pipe_cmd_opts opts; - if (arg != nullptr && check_for_argument (&arg, "-d", 2)) - { - delim = extract_arg (&arg); - if (delim.empty ()) - error (_("Missing delimiter DELIM after -d")); - } + auto grp = make_pipe_cmd_options_def_group (&opts); + gdb::option::process_options + (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp); + + const char *delim = "|"; + if (opts.delimiter != nullptr) + delim = opts.delimiter; const char *command = arg; if (command == nullptr) error (_("Missing COMMAND")); - arg = strstr (arg, delim.c_str ()); + arg = strstr (arg, delim); if (arg == nullptr) error (_("Missing delimiter before SHELL_COMMAND")); std::string gdb_cmd (command, arg - command); - arg += delim.length (); /* Skip the delimiter. */ + arg += strlen (delim); /* Skip the delimiter. */ if (gdb_cmd.empty ()) gdb_cmd = repeat_previous (); @@ -1019,6 +1055,43 @@ pipe_command (const char *arg, int from_tty) exit_status_set_internal_vars (exit_status); } +/* Completer for the pipe command. */ + +static void +pipe_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word_ignored) +{ + pipe_cmd_opts opts; + + const char *org_text = text; + auto grp = make_pipe_cmd_options_def_group (&opts); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp)) + return; + + const char *delimiter = "|"; + if (opts.delimiter != nullptr) + delimiter = opts.delimiter; + + /* Check if we're past option values already. */ + if (text > org_text && !isspace (text[-1])) + return; + + const char *delim = strstr (text, delimiter); + + /* If we're still not past the delimiter, complete the gdb + command. */ + if (delim == nullptr || delim == text) + { + complete_nested_command_line (tracker, text); + return; + } + + /* We're past the delimiter. What follows is a shell command, which + we don't know how to complete. */ +} + static void list_command (const char *arg, int from_tty) { @@ -2044,6 +2117,7 @@ case COMMAND contains a | character.\n\ \n\ With no COMMAND, repeat the last executed command\n\ and send its output to SHELL_COMMAND.")); + set_cmd_completer_handle_brkchars (c, pipe_command_completer); add_com_alias ("|", "pipe", class_support, 0); add_com ("list", class_files, list_command, _("\ diff --git a/gdb/testsuite/gdb.base/shell.exp b/gdb/testsuite/gdb.base/shell.exp index 2136d48919f..719435d4eb5 100644 --- a/gdb/testsuite/gdb.base/shell.exp +++ b/gdb/testsuite/gdb.base/shell.exp @@ -15,6 +15,8 @@ # Test that the "shell", "!", "|" and "pipe" commands work. +load_lib completion-support.exp + gdb_exit gdb_start @@ -92,8 +94,8 @@ gdb_test "p \$_shell_exitsignal" " = 2" "pipe interrupt exitsignal" # Error handling verifications. gdb_test "|" "Missing COMMAND" "all missing" -gdb_test "|-d" "Missing delimiter DELIM after -d" "-d value missing" -gdb_test "|-d " "Missing delimiter DELIM after -d" "-d spaces value missing" +gdb_test "|-d" "-d requires an argument" "-d value missing" +gdb_test "|-d " "-d requires an argument" "-d spaces value missing" gdb_test "| echo coucou" \ "Missing delimiter before SHELL_COMMAND" \ "| delimiter missing" @@ -110,3 +112,44 @@ gdb_test "|-d! echo coucou ! wc" \ "Missing delimiter before SHELL_COMMAND" \ "delimiter missing due to missing space" +# Completion tests. + +test_gdb_complete_unique \ + "pipe" \ + "pipe" + +# Note that unlike "pipe", "|" doesn't require a space. This checks +# that completion behaves that way too. +foreach cmd {"pipe " "| " "|"} { + test_gdb_completion_offers_commands "$cmd" + + # There's only one option. + test_gdb_complete_unique \ + "${cmd}-" \ + "${cmd}-d" + + # Cannot complete "-d"'s argument. + test_gdb_complete_none "${cmd}-d " + test_gdb_complete_none "${cmd}-d main" + + # Check completing a GDB command, with and without -d. + test_gdb_complete_unique \ + "${cmd}maint set test-se" \ + "${cmd}maint set test-settings" + test_gdb_complete_unique \ + "${cmd}-d XXX maint set test-se" \ + "${cmd}-d XXX maint set test-settings" + + # Check that GDB doesn't try to complete the shell command. + test_gdb_complete_none \ + "${cmd}print 1 | " + + # Same, while making sure that the completer understands "-d". + test_gdb_complete_unique \ + "${cmd}-d XXX maint set" \ + "${cmd}-d XXX maint set" + test_gdb_complete_none \ + "${cmd}-d set maint set" + test_gdb_complete_none \ + "${cmd}-d set maint set " +}