This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC] Add support for user-defined prefixed commands
- From: Daniel Jacobowitz <drow at false dot org>
- To: gdb-patches at sourceware dot org
- Cc: Tom Tromey <tromey at redhat dot com>, Eli Zaretskii <eliz at gnu dot org>
- Date: Mon, 1 Dec 2008 16:06:08 -0500
- Subject: [RFC] Add support for user-defined prefixed commands
Tom, here's the patch I mentioned to you over the weekend. We've been
using this at CodeSourcery for a couple of months now and it's pretty
handy.
This patch allows "define" to take more than one word for the name of
the command. It does not allow the creation of new prefix commands,
but you can add commands to a prefix. For instance, "define target
foo" to implement the "target foo" command, which sits next to
"target sim" and "target remote" and works just like them.
The same thing works for hook commands. From the manual:
+@var{commandname} may be a bare command name consisting of letters,
+numbers, dashes, and underscores. It may also start with any predefined
+prefix command. For example, @samp{define target my-target} creates
+a user-defined @samp{target my-target} command.
+You can hook a multi-word command by adding @code{hook-} or
+@code{hookpost-} to the last word of the command, e.g.@:
+@samp{define target hook-remote} to add a hook to @samp{target remote}.
Any comments? Eli, is the documentation OK?
--
Daniel Jacobowitz
CodeSourcery
2008-12-01 Daniel Jacobowitz <dan@codesourcery.com>
* cli/cli-cmds.c (show_user): Update calls to show_user_1. Call
show_user_1 for prefix commands.
* cli/cli-decode.c (help_cmd_list): Recurse for "help user-defined".
* cli/cli-script.c (validate_comname): Rewrite to handle prefix
commands. Return the containing command list.
(define_command, document_command): Update to handle prefix commands.
(show_user_1): Add prefix and name arguments. Handle prefix
commands.
* cli/cli-script.h (show_user_1): Update prototype.
gdb/doc/
* gdb.texinfo (Define, Hooks): Document prefix command support.
gdb/testsuite/
* gdb.base/define.exp: Test defining and hooking prefix commands.
* gdb.python/python.exp: Update test for "show user" output.
Index: cli/cli-cmds.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-cmds.c,v
retrieving revision 1.80
diff -u -p -r1.80 cli-cmds.c
--- cli/cli-cmds.c 10 Nov 2008 20:38:24 -0000 1.80
+++ cli/cli-cmds.c 1 Dec 2008 20:39:03 -0000
@@ -1048,17 +1048,18 @@ show_user (char *args, int from_tty)
if (args)
{
- c = lookup_cmd (&args, cmdlist, "", 0, 1);
+ char *comname = args;
+ c = lookup_cmd (&comname, cmdlist, "", 0, 1);
if (c->class != class_user)
error (_("Not a user command."));
- show_user_1 (c, gdb_stdout);
+ show_user_1 (c, "", args, gdb_stdout);
}
else
{
for (c = cmdlist; c; c = c->next)
{
- if (c->class == class_user)
- show_user_1 (c, gdb_stdout);
+ if (c->class == class_user || c->prefixlist != NULL)
+ show_user_1 (c, "", c->name, gdb_stdout);
}
}
}
Index: cli/cli-decode.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-decode.c,v
retrieving revision 1.71
diff -u -p -r1.71 cli-decode.c
--- cli/cli-decode.c 30 Aug 2008 01:54:11 -0000 1.71
+++ cli/cli-decode.c 1 Dec 2008 20:39:03 -0000
@@ -996,6 +996,10 @@ help_cmd_list (struct cmd_list_element *
{
print_help_for_command (c, prefix, recurse, stream);
}
+ else if (c->abbrev_flag == 0 && recurse
+ && class == class_user && c->prefixlist != NULL)
+ /* User-defined commands may be subcommands. */
+ help_cmd_list (*c->prefixlist, class, c->prefixname, recurse, stream);
}
}
Index: cli/cli-script.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-script.c,v
retrieving revision 1.48
diff -u -p -r1.48 cli-script.c
--- cli/cli-script.c 28 Oct 2008 15:22:13 -0000 1.48
+++ cli/cli-script.c 1 Dec 2008 20:39:03 -0000
@@ -45,8 +45,6 @@ static char *insert_args (char *line);
static struct cleanup * setup_user_args (char *p);
-static void validate_comname (char *);
-
/* Level of control structure when reading. */
static int control_level;
@@ -1254,21 +1252,57 @@ copy_command_lines (struct command_line
return result;
}
-static void
-validate_comname (char *comname)
+/* Validate that *COMNAME is a valid name for a command. Return the
+ containing command list, in case it starts with a prefix command.
+ The prefix must already exist. *COMNAME is advanced to point after
+ any prefix, and a NUL character overwrites the space after the
+ prefix. */
+
+static struct cmd_list_element **
+validate_comname (char **comname)
{
- char *p;
+ struct cmd_list_element **list = &cmdlist;
+ char *p, *last_word;
- if (comname == 0)
+ if (*comname == 0)
error_no_arg (_("name of command to define"));
- p = comname;
+ /* Find the last word of the argument. */
+ p = *comname + strlen (*comname);
+ while (p > *comname && isspace (p[-1]))
+ p--;
+ while (p > *comname && !isspace (p[-1]))
+ p--;
+ last_word = p;
+
+ /* Find the corresponding command list. */
+ if (last_word != *comname)
+ {
+ struct cmd_list_element *c;
+ char saved_char, *tem = *comname;
+
+ /* Separate the prefix and the command. */
+ saved_char = last_word[-1];
+ last_word[-1] = '\0';
+
+ c = lookup_cmd (&tem, cmdlist, "", 0, 1);
+ if (c->prefixlist == NULL)
+ error (_("\"%s\" is not a prefix command."), *comname);
+
+ list = c->prefixlist;
+ last_word[-1] = saved_char;
+ *comname = last_word;
+ }
+
+ p = *comname;
while (*p)
{
if (!isalnum (*p) && *p != '-' && *p != '_')
error (_("Junk in argument list: \"%s\""), p);
p++;
}
+
+ return list;
}
/* This is just a placeholder in the command data structures. */
@@ -1288,9 +1322,8 @@ define_command (char *comname, int from_
CMD_POST_HOOK
};
struct command_line *cmds;
- struct cmd_list_element *c, *newc, *oldc, *hookc = 0;
- char *tem = comname;
- char *tem2;
+ struct cmd_list_element *c, *newc, *oldc, *hookc = 0, **list;
+ char *tem, *tem2, *comfull;
char tmpbuf[MAX_TMPBUF];
int hook_type = CMD_NO_HOOK;
int hook_name_size = 0;
@@ -1300,10 +1333,12 @@ define_command (char *comname, int from_
#define HOOK_POST_STRING "hookpost-"
#define HOOK_POST_LEN 9
- validate_comname (comname);
+ comfull = comname;
+ list = validate_comname (&comname);
/* Look it up, and verify that we got an exact match. */
- c = lookup_cmd (&tem, cmdlist, "", -1, 1);
+ tem = comname;
+ c = lookup_cmd (&tem, *list, "", -1, 1);
if (c && strcmp (comname, c->name) != 0)
c = 0;
@@ -1337,13 +1372,13 @@ define_command (char *comname, int from_
{
/* Look up cmd it hooks, and verify that we got an exact match. */
tem = comname + hook_name_size;
- hookc = lookup_cmd (&tem, cmdlist, "", -1, 0);
+ hookc = lookup_cmd (&tem, *list, "", -1, 0);
if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
hookc = 0;
if (!hookc)
{
warning (_("Your new `%s' command does not hook any existing command."),
- comname);
+ comfull);
if (!query ("Proceed? "))
error (_("Not confirmed."));
}
@@ -1357,7 +1392,7 @@ define_command (char *comname, int from_
if (isupper (*tem))
*tem = tolower (*tem);
- sprintf (tmpbuf, "Type commands for definition of \"%s\".", comname);
+ sprintf (tmpbuf, "Type commands for definition of \"%s\".", comfull);
cmds = read_command_lines (tmpbuf, from_tty, 1);
if (c && c->class == class_user)
@@ -1365,7 +1400,7 @@ define_command (char *comname, int from_
newc = add_cmd (comname, class_user, user_defined_command,
(c && c->class == class_user)
- ? c->doc : savestring ("User-defined.", 13), &cmdlist);
+ ? c->doc : savestring ("User-defined.", 13), list);
newc->user_commands = cmds;
/* If this new command is a hook, then mark both commands as being
@@ -1393,18 +1428,20 @@ void
document_command (char *comname, int from_tty)
{
struct command_line *doclines;
- struct cmd_list_element *c;
- char *tem = comname;
+ struct cmd_list_element *c, **list;
+ char *tem, *comfull;
char tmpbuf[128];
- validate_comname (comname);
+ comfull = comname;
+ list = validate_comname (&comname);
- c = lookup_cmd (&tem, cmdlist, "", 0, 1);
+ tem = comname;
+ c = lookup_cmd (&tem, *list, "", 0, 1);
if (c->class != class_user)
- error (_("Command \"%s\" is built-in."), comname);
+ error (_("Command \"%s\" is built-in."), comfull);
- sprintf (tmpbuf, "Type documentation for \"%s\".", comname);
+ sprintf (tmpbuf, "Type documentation for \"%s\".", comfull);
doclines = read_command_lines (tmpbuf, from_tty, 0);
if (c->doc)
@@ -1505,17 +1542,29 @@ script_from_file (FILE *stream, char *fi
do_cleanups (old_cleanups);
}
+/* Print the definition of user command C to STREAM. Or, if C is a
+ prefix command, show the definitions of all user commands under C
+ (recursively). PREFIX and NAME combined are the name of the
+ current command. */
void
-show_user_1 (struct cmd_list_element *c, struct ui_file *stream)
+show_user_1 (struct cmd_list_element *c, char *prefix, char *name,
+ struct ui_file *stream)
{
struct command_line *cmdlines;
+ if (c->prefixlist != NULL)
+ {
+ char *prefixname = c->prefixname;
+ for (c = *c->prefixlist; c != NULL; c = c->next)
+ if (c->class == class_user || c->prefixlist != NULL)
+ show_user_1 (c, prefixname, c->name, gdb_stdout);
+ return;
+ }
+
cmdlines = c->user_commands;
if (!cmdlines)
return;
- fputs_filtered ("User command ", stream);
- fputs_filtered (c->name, stream);
- fputs_filtered (":\n", stream);
+ fprintf_filtered (stream, "User command \"%s%s\":\n", prefix, name);
print_command_lines (uiout, cmdlines, 1);
fputs_filtered ("\n", stream);
Index: cli/cli-script.h
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-script.h,v
retrieving revision 1.12
diff -u -p -r1.12 cli-script.h
--- cli/cli-script.h 6 Aug 2008 19:41:32 -0000 1.12
+++ cli/cli-script.h 1 Dec 2008 20:39:04 -0000
@@ -33,7 +33,8 @@ extern void while_command (char *arg, in
extern void if_command (char *arg, int from_tty);
-extern void show_user_1 (struct cmd_list_element *c, struct ui_file *stream);
+extern void show_user_1 (struct cmd_list_element *c, char *prefix,
+ char *name, struct ui_file *stream);
/* Exported to gdb/breakpoint.c */
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.537
diff -u -p -r1.537 gdb.texinfo
--- doc/gdb.texinfo 27 Nov 2008 09:23:01 -0000 1.537
+++ doc/gdb.texinfo 1 Dec 2008 20:39:09 -0000
@@ -17569,6 +17569,10 @@ end
@item define @var{commandname}
Define a command named @var{commandname}. If there is already a command
by that name, you are asked to confirm that you want to redefine it.
+@var{commandname} may be a bare command name consisting of letters,
+numbers, dashes, and underscores. It may also start with any predefined
+prefix command. For example, @samp{define target my-target} creates
+a user-defined @samp{target my-target} command.
The definition of the command is made up of other @value{GDBN} command lines,
which are given following the @code{define} command. The end of these
@@ -17703,6 +17707,10 @@ not for command aliases; you should defi
name, e.g.@: @code{backtrace} rather than @code{bt}.
@c FIXME! So how does Joe User discover whether a command is an alias
@c or not?
+You can hook a multi-word command by adding @code{hook-} or
+@code{hookpost-} to the last word of the command, e.g.@:
+@samp{define target hook-remote} to add a hook to @samp{target remote}.
+
If an error occurs during the execution of your hook, execution of
@value{GDBN} commands stops and @value{GDBN} issues a prompt
(before the command that you actually typed had a chance to run).
Index: testsuite/gdb.base/define.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/define.exp,v
retrieving revision 1.12
diff -u -p -r1.12 define.exp
--- testsuite/gdb.base/define.exp 16 Aug 2008 20:36:30 -0000 1.12
+++ testsuite/gdb.base/define.exp 1 Dec 2008 20:39:09 -0000
@@ -346,6 +346,37 @@ gdb_expect {
timeout {fail "(timeout) define hook undefined command: bar"}
}
+# Test creation of an additional target subcommand.
+gdb_test_multiple "define target testsuite" "" {
+ -re "Type commands for definition of \"target testsuite\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "printf \"hello\\n\"\nend" "" "define target testsuite"
+ }
+}
+gdb_test_multiple "document target testsuite" "" {
+ -re "Type documentation for \"target testsuite\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "A test target.\nend" "" "document target testsuite"
+ }
+}
+
+gdb_test "help target" ".*A test target.*"
+gdb_test "target testsuite" "hello"
+gdb_test "show user target testsuite" "User command \"target testsuite\":\r\n printf \"hello\\\\n\"\r\n"
+
+# We should even be able to hook subcommands.
+gdb_test_multiple "define target hook-testsuite" "" {
+ -re "Type commands for definition of \"target hook-testsuite\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "printf \"one\\n\"\nend" "" "define target hook-testsuite"
+ }
+}
+
+gdb_test_multiple "define target hookpost-testsuite" "" {
+ -re "Type commands for definition of \"target hookpost-testsuite\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "printf \"two\\n\"\nend" "" "define target hookpost-testsuite"
+ }
+}
+
+gdb_test "target testsuite" "one\r\nhello\r\ntwo" "target testsuite with hooks"
+
# This is a quasi-define command: Verify that the user can redefine
# GDB's gdb_prompt.
#
@@ -367,9 +398,3 @@ gdb_expect {
gdb_exit
return 0
-
-
-
-
-
-
Index: testsuite/gdb.python/python.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/python.exp,v
retrieving revision 1.2
diff -u -p -r1.2 python.exp
--- testsuite/gdb.python/python.exp 16 Aug 2008 20:36:30 -0000 1.2
+++ testsuite/gdb.python/python.exp 1 Dec 2008 20:39:09 -0000
@@ -63,7 +63,7 @@ gdb_py_test_multiple "show python comman
"print 23" "" \
"end" "" \
"end" "" \
- "show user zzq" "User command zzq:.* python.*print 23.* end"
+ "show user zzq" "User command \"zzq\":.* python.*print 23.* end"
gdb_py_test_multiple "indented multi-line python command" \
"python" "" \