From patchwork Tue Nov 26 23:34:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Waroquiers X-Patchwork-Id: 36287 Received: (qmail 31695 invoked by alias); 26 Nov 2019 23:34:39 -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 31631 invoked by uid 89); 26 Nov 2019 23:34:38 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-15.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.1 spammy=WHILE X-HELO: mailsec117.isp.belgacom.be Received: from mailsec117.isp.belgacom.be (HELO mailsec117.isp.belgacom.be) (195.238.20.113) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 26 Nov 2019 23:34:36 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=skynet.be; i=@skynet.be; q=dns/txt; s=securemail; t=1574811276; x=1606347276; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=8dVt4g9OX04xQ5K4coQEv+S/mNCGsrX+sMIFY7zjYdI=; b=WuGLB9zj7z23q0z3Q1t1BqtBUUlxauQQXV8nO8cCFFpLQfR7ceTxdW9K mH/O/XaFawUaFV+r3QqXoXHVL9Oa+Q==; IronPort-SDR: WQcqJEeL3Qi9rJWA4qmMzPmLrNJYmj20YbL4mUjKNPJqz6teenRCubOsk0+OKv+bHTowdcg3zV gPAdrVNk0uqOnXpJvXcU3vJNpmDfz2uPzVsAbOXrDiuEuCqwVmvc2FfxmUxGCQSf/ypN1XfVqY cotmzd6dyX/PHo9dO3XQyRTabamDdy2WRgxTkdzDcB9sXW05HdvPVwQeIQOEtJGunzU3yLQBVJ iXXEQYMQF4YNvrT4+Cl41U2qQtPbR41sQ3RmJ/yNaCYeDyhe1X7YkvOgc2F/aWX4hIbwccQv1g PgI= Received: from 136.173-134-109.adsl-dyn.isp.belgacom.be (HELO md.home) ([109.134.173.136]) by relay.skynet.be with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 27 Nov 2019 00:34:29 +0100 From: Philippe Waroquiers To: gdb-patches@sourceware.org Cc: Philippe Waroquiers Subject: [RFAv2 1/4] Implement user defined prefix. Date: Wed, 27 Nov 2019 00:34:20 +0100 Message-Id: <20191126233423.14801-2-philippe.waroquiers@skynet.be> In-Reply-To: <20191126233423.14801-1-philippe.waroquiers@skynet.be> References: <20191126233423.14801-1-philippe.waroquiers@skynet.be> MIME-Version: 1.0 X-IsSubscribed: yes This patch adds the new 'define-prefix' command that creates (or mark an existing user defined command) as a prefix command. This approach was preferred compared to add a -prefix option to 'define' command : with define-prefix, a command can be defined and afterwards marked as a prefix. Also, it is easier to define a 'prefix' only command in one operation. This patch also adds completers for the 'define' and 'document' commands. This makes it easier for the user to type the prefixes for 'define' and type the documented command name for 'document'. gdb/ChangeLog YYYY-MM-DD Philippe Waroquiers * cli/cli-script.c (do_define_command): Ensure a redefined prefix command is kept as a prefix command. (define_prefix_command): New function. (show_user_1): Report user defined prefixes. (_initialize_cli_script): Create the new 'define-prefix' command. Add completers for 'define' and 'document'. * top.c (execute_command): If command is a user-defined prefix only command, report the list of commands for this prefix command. --- gdb/cli/cli-script.c | 124 +++++++++++++++++++++++++++++++++++++------ gdb/top.c | 12 +++++ 2 files changed, 121 insertions(+), 15 deletions(-) diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index 06cdcd6bfe..e0463cb5b7 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -29,6 +29,7 @@ #include "cli/cli-cmds.h" #include "cli/cli-decode.h" #include "cli/cli-script.h" +#include "cli/cli-style.h" #include "extension.h" #include "interps.h" @@ -1395,7 +1396,15 @@ do_define_command (const char *comname, int from_tty, int q; if (c->theclass == class_user || c->theclass == class_alias) - q = query (_("Redefine command \"%s\"? "), c->name); + { + /* if C is a prefix command, tell the user its subcommands will + be kept, and ask if ok to redefine the command. */ + if (c->prefixlist != nullptr) + q = query (_("Keeping subcommands of prefix command \"%s\".\n" + "Redefine command \"%s\"? "), c->name, c->name); + else + q = query (_("Redefine command \"%s\"? "), c->name); + } else q = query (_("Really redefine built-in command \"%s\"? "), c->name); if (!q) @@ -1446,10 +1455,27 @@ do_define_command (const char *comname, int from_tty, else cmds = *commands; - newc = add_cmd (comname, class_user, user_defined_command, - (c && c->theclass == class_user) - ? c->doc : xstrdup ("User-defined."), list); - newc->user_commands = std::move (cmds); + { + struct cmd_list_element **c_prefixlist + = c == nullptr ? nullptr : c->prefixlist; + const char *c_prefixname = c == nullptr ? nullptr : c->prefixname; + + newc = add_cmd (comname, class_user, user_defined_command, + (c != nullptr && c->theclass == class_user) + ? c->doc : xstrdup ("User-defined."), list); + newc->user_commands = std::move (cmds); + + /* If we define or re-define a command that was previously defined + as a prefix, keep the prefix information. */ + if (c_prefixlist != nullptr) + { + newc->prefixlist = c_prefixlist; + newc->prefixname = c_prefixname; + /* allow_unknown: see explanation in equivalent logic in + define_prefix_command (). */ + newc->allow_unknown = newc->user_commands.get () != nullptr; + } + } /* If this new command is a hook, then mark both commands as being tied. */ @@ -1524,6 +1550,54 @@ document_command (const char *comname, int from_tty) c->doc = doc; } } + +/* Implementation of the "define-prefix" command. */ + +static void +define_prefix_command (const char *comname, int from_tty) +{ + struct cmd_list_element *c, **list; + const char *tem; + const char *comfull; + + comfull = comname; + list = validate_comname (&comname); + + /* Look it up, and verify that we got an exact match. */ + tem = comname; + c = lookup_cmd (&tem, *list, "", -1, 1); + if (c != nullptr && strcmp (comname, c->name) != 0) + c = nullptr; + + if (c != nullptr && c->theclass != class_user) + error (_("Command \"%s\" is built-in."), comfull); + + if (c != nullptr && c->prefixlist != nullptr) + { + /* c is already a user defined prefix command. */ + return; + } + + /* If the command does not exist at all, create it. */ + if (c == nullptr) + { + comname = xstrdup (comname); + c = add_cmd (comname, class_user, user_defined_command, + xstrdup ("User-defined."), list); + } + + /* Allocate the c->prefixlist, which marks the command as a prefix + command. */ + c->prefixlist = new struct cmd_list_element*; + *(c->prefixlist) = nullptr; + c->prefixname = xstrprintf ("%s ", comfull); + /* If the prefix command C is not a command, then it must be followed + by known subcommands. Otherwise, if C is also a normal command, + it can be followed by C args that must not cause a 'subcommand' + not recognised error, and thus we must allow unknown. */ + c->allow_unknown = c->user_commands.get () != nullptr; +} + /* Used to implement source_command. */ @@ -1564,7 +1638,21 @@ void show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name, struct ui_file *stream) { - struct command_line *cmdlines; + if (cli_user_command_p (c)) + { + struct command_line *cmdlines = c->user_commands.get (); + + fprintf_filtered (stream, "User %scommand \"", + c->prefixlist == NULL ? "" : "prefix "); + fprintf_styled (stream, title_style.style (), "%s%s", + prefix, name); + fprintf_filtered (stream, "\":\n"); + if (cmdlines) + { + print_command_lines (current_uiout, cmdlines, 1); + fputs_filtered ("\n", stream); + } + } if (c->prefixlist != NULL) { @@ -1573,25 +1661,23 @@ show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name, for (c = *c->prefixlist; c != NULL; c = c->next) if (c->theclass == class_user || c->prefixlist != NULL) show_user_1 (c, prefixname, c->name, gdb_stdout); - return; } - cmdlines = c->user_commands.get (); - fprintf_filtered (stream, "User command \"%s%s\":\n", prefix, name); - - if (!cmdlines) - return; - print_command_lines (current_uiout, cmdlines, 1); - fputs_filtered ("\n", stream); } void _initialize_cli_script (void) { - add_com ("document", class_support, document_command, _("\ + struct cmd_list_element *c; + + /* "document", "define" and "define-prefix" use command_completer, + as this helps the user to either type the command name and/or + its prefixes. */ + c = add_com ("document", class_support, document_command, _("\ Document a user-defined command.\n\ Give command name as argument. Give documentation on following lines.\n\ End with a line of just \"end\".")); + set_cmd_completer (c, command_completer); define_cmd_element = add_com ("define", class_support, define_command, _("\ Define a new command name. Command name is argument.\n\ Definition appears on following lines, one command per line.\n\ @@ -1600,6 +1686,14 @@ Use the \"document\" command to give documentation for the new command.\n\ Commands defined in this way may accept an unlimited number of arguments\n\ accessed via $arg0 .. $argN. $argc tells how many arguments have\n\ been passed.")); + set_cmd_completer (define_cmd_element, command_completer); + c = add_com ("define-prefix", class_support, define_prefix_command, + _("\ +Define or mark a command as a user-defined prefix command.\n\ +User defined prefix commands can be used as prefix commands for\n\ +other user defined commands.\n\ +If the command already exists, it is changed to a prefix command.")); + set_cmd_completer (c, command_completer); while_cmd_element = add_com ("while", class_support, while_command, _("\ Execute nested commands WHILE the conditional expression is non zero.\n\ diff --git a/gdb/top.c b/gdb/top.c index 2953eac819..e8ed3b2747 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -627,6 +627,18 @@ execute_command (const char *p, int from_tty) /* c->user_commands would be NULL in the case of a python command. */ if (c->theclass == class_user && c->user_commands) execute_user_command (c, arg); + else if (c->theclass == class_user + && c->prefixlist && !c->allow_unknown) + /* If this is a user defined prefix that does not allow unknown + (in other words, C is a prefix command and not a command + that can be followed by its args), report the list of + subcommands. */ + { + printf_unfiltered + ("\"%.*s\" must be followed by the name of a subcommand.\n", + (int) strlen (c->prefixname) - 1, c->prefixname); + help_list (*c->prefixlist, c->prefixname, all_commands, gdb_stdout); + } else if (c->type == set_cmd) do_set_command (arg, from_tty, c); else if (c->type == show_cmd)