[RFA 1/4] Implement user defined prefix.
Simon Marchi
simark@simark.ca
Sun Nov 24 21:33:00 GMT 2019
On 2019-09-29 4:54 p.m., Philippe Waroquiers wrote:
> This patch adds the new 'prefix-define' 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 prefix-define, a command can be defined and
> afterwards marked as a prefix. Also, it is easier to define a
> 'prefix' only command in one operation.
It's a bit a detail, but one we can't change later on, so I'll mention it
anyway. I think "define-prefix" would fit more with the existing GDB
commands, which seem to be mostly "ACTION-OBJECT". For example, add-inferior,
compare-sections, generate-core-file, queue-signal. I found flash-erase as
a counter example, but that's about it.
> 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 <philippe.waroquiers@skynet.be>
>
> * cli/cli-script.c (do_define_command): Ensure a redefined
> prefix command is kept as a prefix command.
> (prefix_define_command): New function.
> (show_user_1): Report user defined prefixes.
> (_initialize_cli_script): Create the new 'prefix-define' 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 | 107 +++++++++++++++++++++++++++++++++++++------
> gdb/top.c | 10 ++++
> 2 files changed, 103 insertions(+), 14 deletions(-)
>
> diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
> index 4fc9c70259..c70c0e4fb3 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"
> @@ -1444,10 +1445,22 @@ 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 ? c->prefixlist : nullptr;
> + const char *c_prefixname = c ? c->prefixname : nullptr;
> +
> + 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);
> +
> + if (c_prefixlist != nullptr)
> + {
> + newc->prefixlist = c_prefixlist;
> + newc->prefixname = c_prefixname;
> + newc->allow_unknown = newc->user_commands.get () != nullptr;
> + }
> + }
I know that it wasn't done right before, but could you please use
c != nullptr in the lines above (and throughout the patch actually)?
Can you also add a bit of comments above to explain the intent of the code? I
don't really understand what happens there. I guess it's for when we re-define
a prefix command (do "define foo" after having done "prefix-define foo"), but
it would be nice if we did not have to guess.
> /* If this new command is a hook, then mark both commands as being
> tied. */
> @@ -1522,6 +1535,52 @@ document_command (const char *comname, int from_tty)
> c->doc = doc;
> }
> }
> +
> +/* Implementation of the "prefix-define" command. */
> +
> +static void
> +prefix_define_command (const char *comname, int from_tty)
> +{
> + struct cmd_list_element *c, **list;
> + const char *tem;
> + const char *comfull;
> + char *prefixname;
> +
> + 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 && strcmp (comname, c->name) != 0)
> + c = nullptr;
> +
> + if (c && c->theclass != class_user)
> + error (_("Command \"%s\" is built-in."), comfull);
> +
> + if (c && c->prefixlist != nullptr)
> + {
> + /* c is already a user defined prefix command. */
> + return;
> + }
> +
> + if (c == nullptr)
> + {
> + comname = xstrdup (comname);
> + c = add_cmd (comname, class_user, user_defined_command,
> + xstrdup ("User-defined."), list);
Hmm, the memory ownership management for the command names looks quite complicated.
> + }
Around here too, I'd appreciate a few comments. It doesn't have to
be super verbose, but just enough to understand the intent, e.g.:
/* If the command does not exist at all, create it. */
and below:
/* Allocate the c->prefixlist, which marks the command as a prefix command. */
> +
> + c->prefixlist = new struct cmd_list_element*;
> + *(c->prefixlist) = nullptr;
> + prefixname = (char *) xmalloc (strlen (comfull) + 2);
> + prefixname[0] = 0;
> + strcat (prefixname, comfull);
> + strcat (prefixname, " ");
> + c->prefixname = prefixname;
Can that be simplified using xstrprintf:
c->prefixname = xstrprinf("%s ", comfull);
?
> + c->allow_unknown = c->user_commands.get () != nullptr;
I'd also like a comment here (and in do_define_command also, where we
also set allow_unknown), to explain why we set allow_unknown to that
particular value. I guess it's because for user-defined prefix commands,
we don't want to allow passing unknown subcommands, but again it would
be nice to record your intent as an author of the code.
> +}
> +
>
> /* Used to implement source_command. */
>
> @@ -1562,7 +1621,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)
> {
> @@ -1571,25 +1644,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 "prefix-define" 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\
> @@ -1598,6 +1669,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 ("prefix-define", class_support, prefix_define_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 a1a08e0b99..abd07bd6fe 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -615,6 +615,16 @@ 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,
> + report the list of subcommands. */
How does one create a user-defined prefix that allows unknown subcommands?
> + {
> + printf_unfiltered
> + ("\"%.*s\" must be followed by the name of an %s command.\n",
The "an" preceding the "%s" will sound weird when the prefix is a word that
starts with a consonant:
(gdb) prefix-define banana
(gdb) banana
"banana" must be followed by the name of an banana command.
You could instead just say:
"banana" must be followed by the name a sub-command.
Simon
More information about the Gdb-patches
mailing list