Fix PR 20559 - "eval" command and $arg0...$arg9/$argc substitution
authorPedro Alves <palves@redhat.com>
Fri, 2 Dec 2016 19:17:13 +0000 (19:17 +0000)
committerPedro Alves <palves@redhat.com>
Fri, 2 Dec 2016 19:17:13 +0000 (19:17 +0000)
It'd be handy to be able to iterate over command arguments in
user-defined commands, in order to support optional arguments
($arg0..$argN).

I thought I could make it work with "eval", but alas, it doesn't work
currently.  E.g., with:

 define test
   set $i = 0
   while $i < $argc
     eval "print $arg%d", $i
     set $i = $i + 1
   end
 end

we get:

 (gdb) test 1
 $1 = void
 (gdb) test 1 2 3
 $2 = void
 $3 = void
 $4 = void
 (gdb)

The problem is that "eval" doesn't do user-defined command arguments
substitution after expanding its own argument.  This patch fixes that,
which makes the example above work:

 (gdb) test 1
 $1 = 1
 (gdb) test 1 2 3
 $2 = 1
 $3 = 2
 $4 = 3
 (gdb)

New test included, similar the above, but also exercises expanding
$argc.

I think this is likely to simplify many scripts out there, so I'm
adding an example to the manual and mentioning it in NEWS as well.

gdb/ChangeLog:
2016-12-02  Pedro Alves  <palves@redhat.com>

PR cli/20559
* NEWS: Mention "eval" expands user-defined command arguments.
* cli/cli-script.c (execute_control_command): Adjust to rename.
(insert_args): Rename to ...
(insert_user_defined_cmd_args): ... this, and make extern.
* cli/cli-script.h (insert_user_defined_cmd_args): New
declaration.
* printcmd.c: Include "cli/cli-script.h".
(eval_command): Call insert_user_defined_cmd_args.

gdb/doc/ChangeLog:
2016-12-02  Pedro Alves  <palves@redhat.com>

PR cli/20559
* gdb.texinfo (Define): Add example of using "eval" to process a
variable number of arguments.
(Output) <eval>: Add anchor.

gdb/testsuite/ChangeLog:
2016-12-02  Pedro Alves  <palves@redhat.com>

PR cli/20559
* gdb.base/commands.exp (user_defined_command_args_eval): New
procedure.
(top level): Call it.

gdb/ChangeLog
gdb/NEWS
gdb/cli/cli-script.c
gdb/cli/cli-script.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/printcmd.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/commands.exp

index 33d824304f379b92f105f1df2a54cf797ae86622..2066268be1ab5dcfb87642da179447ef0f6563b2 100644 (file)
@@ -1,3 +1,15 @@
+2016-12-02  Pedro Alves  <palves@redhat.com>
+
+       PR cli/20559
+       * NEWS: Mention "eval" expands user-defined command arguments.
+       * cli/cli-script.c (execute_control_command): Adjust to rename.
+       (insert_args): Rename to ...
+       (insert_user_defined_cmd_args): ... this, and make extern.
+       * cli/cli-script.h (insert_user_defined_cmd_args): New
+       declaration.
+       * printcmd.c: Include "cli/cli-script.h".
+       (eval_command): Call insert_user_defined_cmd_args.
+
 2016-12-02  Tom Tromey  <tom@tromey.com>
 
        PR symtab/16264:
index a5974053e45a4f0a71d11bbff7eb88cea609c704..408912443e5b1b1db55887a9a05647ecdc6fb4a0 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
 
 * Support for Java programs compiled with gcj has been removed.
 
+* The "eval" command now expands user-defined command arguments.
+
+  This makes it easier to process a variable number of arguments:
+
+   define mycommand
+     set $i = 0
+     while $i < $argc
+       eval "print $arg%d", $i
+       set $i = $i + 1
+     end
+   end
+
 * New targets
 
 Synopsys ARC                   arc*-*-elf32
index ce4d8cb103b92a2e7f54d7450fb1ebe01be7692d..15947a9a78a18ef31034971a463ec9e38ddf18ff 100644 (file)
@@ -41,8 +41,6 @@ recurse_read_control_structure (char * (*read_next_line_func) (void),
                                void (*validator)(char *, void *),
                                void *closure);
 
-static std::string insert_args (const char *line);
-
 static struct cleanup * setup_user_args (char *p);
 
 static char *read_next_line (void);
@@ -455,7 +453,7 @@ execute_control_command (struct command_line *cmd)
     case simple_control:
       {
        /* A simple command, execute it and return.  */
-       std::string new_line = insert_args (cmd->line);
+       std::string new_line = insert_user_defined_cmd_args (cmd->line);
        execute_command (&new_line[0], 0);
        ret = cmd->control_type;
        break;
@@ -486,7 +484,7 @@ execute_control_command (struct command_line *cmd)
        print_command_trace (buffer);
 
        /* Parse the loop control expression for the while statement.  */
-       std::string new_line = insert_args (cmd->line);
+       std::string new_line = insert_user_defined_cmd_args (cmd->line);
        expression_up expr = parse_expression (new_line.c_str ());
 
        ret = simple_control;
@@ -551,7 +549,7 @@ execute_control_command (struct command_line *cmd)
        print_command_trace (buffer);
 
        /* Parse the conditional for the if statement.  */
-       std::string new_line = insert_args (cmd->line);
+       std::string new_line = insert_user_defined_cmd_args (cmd->line);
        expression_up expr = parse_expression (new_line.c_str ());
 
        current = NULL;
@@ -591,7 +589,7 @@ execute_control_command (struct command_line *cmd)
       {
        /* Breakpoint commands list, record the commands in the
           breakpoint's command list and return.  */
-       std::string new_line = insert_args (cmd->line);
+       std::string new_line = insert_user_defined_cmd_args (cmd->line);
        ret = commands_from_control_command (new_line.c_str (), cmd);
        break;
       }
@@ -782,13 +780,12 @@ locate_arg (const char *p)
   return NULL;
 }
 
-/* Insert the user defined arguments stored in user_arg into the $arg
-   arguments found in line.  */
+/* See cli-script.h.  */
 
-static std::string
-insert_args (const char *line)
+std::string
+insert_user_defined_cmd_args (const char *line)
 {
-  /* If we are not in a user-defined function, treat $argc, $arg0, et
+  /* If we are not in a user-defined command, treat $argc, $arg0, et
      cetera as normal convenience variables.  */
   if (user_args == NULL)
     return line;
index 7db6e82e6ff6b9e0fc5642e38bafcb7c4c13d333..8920d44fde4e796f195f9e046ae788b12f525da2 100644 (file)
@@ -53,6 +53,12 @@ extern struct cleanup *
 
 extern void execute_user_command (struct cmd_list_element *c, char *args);
 
+/* If we're in a user-defined command, replace any $argc/$argN
+   reference found in LINE with the arguments that were passed to the
+   command.  Otherwise, treat $argc/$argN as normal convenience
+   variables.  */
+extern std::string insert_user_defined_cmd_args (const char *line);
+
 /* Exported to top.c */
 
 extern void print_command_trace (const char *cmd);
index dc8244c2d01e3e7675cd762a6e609c154f352481..18564b6549edfc118572db3c8a7c294976f4e104 100644 (file)
@@ -1,3 +1,10 @@
+2016-12-02  Pedro Alves  <palves@redhat.com>
+
+       PR cli/20559
+       * gdb.texinfo (Define): Add example of using "eval" to process a
+       variable number of arguments.
+       (Output) <eval>: Add anchor.
+
 2016-11-30  Simon Marchi  <simon.marchi@polymtl.ca>
 
        * Makefile.in: Likewise.
index 6ad275400929647b0e97cbb4920e21bec8e34366..f4dfac2047fb032ab8e8168c6c6431ff6200ac76 100644 (file)
@@ -24096,6 +24096,21 @@ define adder
 end
 @end smallexample
 
+Combining with the @code{eval} command (@pxref{eval}) makes it easier
+to process a variable number of arguments:
+
+@smallexample
+define adder
+  set $i = 0
+  set $sum = 0
+  while $i < $argc
+    eval "set $sum = $sum + $arg%d", $i
+    set $i = $i + 1
+  end
+  print $sum
+end
+@end smallexample
+
 @table @code
 
 @kindex define
@@ -24526,6 +24541,7 @@ Here's an example of printing DFP types using the above conversion letters:
 printf "D32: %Hf - D64: %Df - D128: %DDf\n",1.2345df,1.2E10dd,1.2E1dl
 @end smallexample
 
+@anchor{eval}
 @kindex eval
 @item eval @var{template}, @var{expressions}@dots{}
 Convert the values of one or more @var{expressions} under the control of
index f434f5f70ab4810c0cf2eac7b86ca73bdca92470..bab13fb1d2582f06b43a7de14bb389c479c2f5c1 100644 (file)
@@ -45,6 +45,7 @@
 #include "charset.h"
 #include "arch-utils.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-script.h"
 #include "format.h"
 #include "source.h"
 
@@ -2723,6 +2724,8 @@ eval_command (char *arg, int from_tty)
 
   std::string expanded = ui_file_as_string (ui_out);
 
+  expanded = insert_user_defined_cmd_args (expanded.c_str ());
+
   execute_command (&expanded[0], from_tty);
 
   do_cleanups (cleanups);
index 5a0330c618fb36abc55b662f582ef50e6f2e6925..c6ee46279f33e078d252c29c7765594606bb9b4b 100644 (file)
@@ -1,3 +1,10 @@
+2016-12-02  Pedro Alves  <palves@redhat.com>
+
+       PR cli/20559
+       * gdb.base/commands.exp (user_defined_command_args_eval): New
+       procedure.
+       (top level): Call it.
+
 2016-12-02  Luis Machado  <lgustavo@codesourcery.com>
 
        * gdb.base/ovldbreak.exp (take_gdb_out_of_choice_menu): Restore
index 8c06e8cb316d5164349d8025cc99f7907a1879e4..3308a9b9dd893641f8c4045bddfef9ac5dbc7452 100644 (file)
@@ -352,6 +352,36 @@ proc_with_prefix user_defined_command_test {} {
        "display user-defined empty command"
 }
 
+# Test that "eval" in a user-defined command expands $argc/$argN.
+
+proc_with_prefix user_defined_command_args_eval {} {
+    global gdb_prompt
+
+    gdb_test_multiple "define command_args_eval" \
+       "define command_args_eval" {
+           -re "End with"  {
+               pass "define"
+           }
+       }
+
+    # Make a command that constructs references to $argc and $argN via
+    # eval.
+    gdb_test \
+       [multi_line \
+            {eval "printf \"argc = %%d,\", $arg%c", 'c'} \
+            {set $i = 0} \
+            {while $i < $argc} \
+            {  eval "printf \" %%d\", $arg%d", $i} \
+            {  set $i = $i + 1} \
+            {end} \
+            {printf "\n"} \
+            {end}] \
+       "" \
+       "enter commands"
+
+    gdb_test "command_args_eval 1 2 3" "argc = 3, 1 2 3" "execute command"
+}
+
 proc_with_prefix watchpoint_command_test {} {
     global gdb_prompt
 
@@ -882,6 +912,7 @@ if_while_breakpoint_command_test
 infrun_breakpoint_command_test
 breakpoint_command_test
 user_defined_command_test
+user_defined_command_args_eval
 watchpoint_command_test
 test_command_prompt_position
 deprecated_command_test