[RFA,5/8] Allow defining a user command inside a user command
Commit Message
PR gdb/11750 concerns defining a command inside a user commnad, like:
define outer
define inner
echo hi\n
end
end
This patch adds this capability to gdb.
2018-04-19 Tom Tromey <tom@tromey.com>
PR gdb/11750:
* cli/cli-script.h (enum command_control_type) <define_control>:
New constant.
* cli/cli-script.c (multi_line_command_p): Handle define_control.
(build_command_line, execute_control_command_1)
(process_next_line): Likewise.
(do_define_command): New function, extracted from define_command.
(define_command): Use it.
testsuite/ChangeLog
2018-04-19 Tom Tromey <tom@tromey.com>
PR gdb/11750:
* gdb.base/define.exp: Test defining a user command inside a user
command.
* gdb.base/commands.exp (define_if_without_arg_test): Test "define".
---
gdb/ChangeLog | 11 +++++++++
gdb/cli/cli-script.c | 49 ++++++++++++++++++++++++++++++-------
gdb/cli/cli-script.h | 1 +
gdb/testsuite/ChangeLog | 7 ++++++
gdb/testsuite/gdb.base/commands.exp | 4 +--
gdb/testsuite/gdb.base/define.exp | 8 ++++++
6 files changed, 69 insertions(+), 11 deletions(-)
Comments
On 04/19/2018 08:15 PM, Tom Tromey wrote:
> build_command_line (enum command_control_type type, const char *args)
> {
> if ((args == NULL || *args == '\0')
> - && (type == if_control || type == while_control))
> - error (_("if/while commands require arguments."));
> + && (type == if_control || type == while_control
> + || type == define_control))
> + error (_("if/while/define commands require arguments."));
I'd vote for splitting those up:
if (args == NULL || *args == '\0')
{
switch (type)
{
case if_control:
error (_("if commands require arguments."));
case while_control:
error (_("while commands require arguments."));
case define_control:
error (_("define commands require arguments."));
}
}
> +/* Define a user-defined command. If COMMANDS is NULL, then this is
> + an interactive call and the commands will be read from the user.
Isn't this conflating top-level "define" command, with interactive
input? I imagine that a top-level "define" command in e.g., gdbinit will
considered "interactive call" according to the above, while in truth,
it's not really interactive, according to FROM_TTY.
> + Otherwise, it is a "define" command in a script and the commands
> + are provided.
Similarly, can't you write a "define" command inside a "define"
command interactively on the command line?
>In the non-interactive case, various prompts and
> + warnings are disabled. */
> +
> static void
> -define_command (const char *comname, int from_tty)
> +do_define_command (const char *comname, int from_tty,
> + const counted_command_line *commands)
> {
Thanks,
Pedro Alves
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
Pedro> I'd vote for splitting those up:
I agree, I did this.
Pedro> Isn't this conflating top-level "define" command, with interactive
Pedro> input? I imagine that a top-level "define" command in e.g., gdbinit will
Pedro> considered "interactive call" according to the above, while in truth,
Pedro> it's not really interactive, according to FROM_TTY.
Yes, I've re-worded this.
Tom
@@ -44,6 +44,9 @@ recurse_read_control_structure (char * (*read_next_line_func) (void),
void (*validator)(char *, void *),
void *closure);
+static void do_define_command (const char *comname, int from_tty,
+ const counted_command_line *commands);
+
static char *read_next_line (void);
/* Level of control structure when reading. */
@@ -122,6 +125,7 @@ multi_line_command_p (enum command_control_type type)
case compile_control:
case python_control:
case guile_control:
+ case define_control:
return 1;
default:
return 0;
@@ -135,8 +139,9 @@ static struct command_line *
build_command_line (enum command_control_type type, const char *args)
{
if ((args == NULL || *args == '\0')
- && (type == if_control || type == while_control))
- error (_("if/while commands require arguments."));
+ && (type == if_control || type == while_control
+ || type == define_control))
+ error (_("if/while/define commands require arguments."));
gdb_assert (args != NULL);
return new struct command_line (type, xstrdup (args));
@@ -611,6 +616,12 @@ execute_control_command_1 (struct command_line *cmd)
ret = simple_control;
break;
+ case define_control:
+ print_command_trace ("define %s", cmd->line);
+ do_define_command (cmd->line, 0, &cmd->body_list_0);
+ ret = simple_control;
+ break;
+
case python_control:
case guile_control:
{
@@ -960,6 +971,8 @@ process_next_line (char *p, struct command_line **command, int parse_commands,
{
*command = build_command_line (commands_control, line_first_arg (p));
}
+ else if (command_name_equals (cmd, "define"))
+ *command = build_command_line (define_control, line_first_arg (p));
else if (command_name_equals (cmd, "python") && !inline_cmd)
{
/* Note that we ignore the inline "python command" form
@@ -1303,8 +1316,15 @@ user_defined_command (const char *ignore, int from_tty)
{
}
+/* Define a user-defined command. If COMMANDS is NULL, then this is
+ an interactive call and the commands will be read from the user.
+ Otherwise, it is a "define" command in a script and the commands
+ are provided. In the non-interactive case, various prompts and
+ warnings are disabled. */
+
static void
-define_command (const char *comname, int from_tty)
+do_define_command (const char *comname, int from_tty,
+ const counted_command_line *commands)
{
enum cmd_hook_type
{
@@ -1331,7 +1351,7 @@ define_command (const char *comname, int from_tty)
if (c && strcmp (comname, c->name) != 0)
c = 0;
- if (c)
+ if (c && commands == nullptr)
{
int q;
@@ -1365,7 +1385,7 @@ define_command (const char *comname, int from_tty)
hookc = lookup_cmd (&tem, *list, "", -1, 0);
if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
hookc = 0;
- if (!hookc)
+ if (!hookc && commands == nullptr)
{
warning (_("Your new `%s' command does not "
"hook any existing command."),
@@ -1377,10 +1397,15 @@ define_command (const char *comname, int from_tty)
comname = xstrdup (comname);
- std::string prompt
- = string_printf ("Type commands for definition of \"%s\".", comfull);
- counted_command_line cmds = read_command_lines (prompt.c_str (), from_tty,
- 1, 0, 0);
+ counted_command_line cmds;
+ if (commands == nullptr)
+ {
+ std::string prompt
+ = string_printf ("Type commands for definition of \"%s\".", comfull);
+ cmds = read_command_lines (prompt.c_str (), from_tty, 1, 0, 0);
+ }
+ else
+ cmds = *commands;
newc = add_cmd (comname, class_user, user_defined_command,
(c && c->theclass == class_user)
@@ -1410,6 +1435,12 @@ define_command (const char *comname, int from_tty)
}
static void
+define_command (const char *comname, int from_tty)
+{
+ do_define_command (comname, from_tty, nullptr);
+}
+
+static void
document_command (const char *comname, int from_tty)
{
struct cmd_list_element *c, **list;
@@ -42,6 +42,7 @@ enum command_control_type
compile_control,
guile_control,
while_stepping_control,
+ define_control,
invalid_control
};
@@ -1014,7 +1014,7 @@ proc_with_prefix redefine_backtrace_test {} {
# Test using "if" and "while" without args when building a command list.
proc define_if_without_arg_test {} {
- foreach cmd {if while} {
+ foreach cmd {if while define} {
set test "define some_command_$cmd"
gdb_test_multiple $test $test {
-re "End with" {
@@ -1022,7 +1022,7 @@ proc define_if_without_arg_test {} {
}
}
- gdb_test "$cmd" "if/while commands require arguments." "type $cmd without args"
+ gdb_test "$cmd" "if/while/define commands require arguments." "type $cmd without args"
}
}
@@ -298,5 +298,13 @@ gdb_test_multiple "set prompt \\(gdb\\) " "reset gdb_prompt" {
}
}
+gdb_test_multiple "define do-define" "" {
+ -re "Type commands for definition of \"do-define\".\r\nEnd with a line saying just \"end\".\r\n>$" {
+ gdb_test "define do-printit\necho here\\n\nend\nend" "" "define do-define"
+ }
+}
+gdb_test_no_output "do-define" "invoke do-define"
+gdb_test "do-printit" "here" "invoke do-printit"
+
gdb_exit
return 0