public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFAv4 0/5] Implement | (pipe) command.
@ 2019-05-30 14:21 Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 3/5] " Philippe Waroquiers
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches

Implement | (pipe) command.

This patch series adds the pipe command, that allows to send the output
of a GDB command to a shell command.

This is the fourth version of the patch, handling the additional
comments of Pedro.
The doc, help and NEWS are changed in this fourth version, so must be
re-reviewed.

* Main changes in this version:
  * Reworded the help, manual and code, e.g. use -d DELIM instead of -d SEP.
    Also, stopped forcing GDB users to read Ada syntax :).
  * Removed the patch related to WIF* macros on MinGW.
  * Reworked the arg parsing logic in pipe command.

--- Previous versions:
* Comments of Pedro:
  * Implement -d SEP (SEP being a string) instead of -dX.
  * popen has been kept as libiberty pexecute still implies to use
    the WIF* macros.  However, exit status handling reworked to go via
    convenience variables, rather than being shown to the user.
  * various small changes (== '\0', Skip, ...).
  * simplified error messages to just indicate with a static string
    what argument is wrong or missing.
  * extended the test to verify all error handling messages and check
    the new convenience variables $_shell_exitcode and $_shell_exitsignal.

* Comments from Eli :
   * better definition of WIF* macros for MinGW.
   * replace @ref by @xref in the doc.

* Comment from Abhijit Halder/Tom:
   * it not that unlikely to have | in a GDB command
     => an optional -d SEP option allows to specify an
     alternate string to use SEP to replace the | as separator
     between the GDB COMMAND and the SHELL_COMMAND.

* Comments from Tom:
  * make previous_saved_command_line static. For this, saved_command_line
    is now also static, and all repeat related functions/vars are now
    in top.c
  * various small changes (use std::swap, strchr, .empty (), ...).
  * removed the scoped_restore_current_thread restore
  * popen has been kept as libiberty pexecute still implies to use
    the WIF* macros.
  * Instead of using execute_command_to_string, use GDB redirection
    mechanism.  I did several trials for this, and at the end,
    the only one working properly was very close to the code
    of execute_command_to_string.
    => we now have a function execute_command_to_ui_file that is used
    to implement the pipe command, and also used by
    execute_command_to_string.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFAv4 2/5] Add function execute_command_to_ui_file
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
                   ` (3 preceding siblings ...)
  2019-05-30 14:21 ` [RFAv4 5/5] NEWS and documentation for " Philippe Waroquiers
@ 2019-05-30 14:21 ` Philippe Waroquiers
  2019-05-31 14:42 ` [RFAv4 0/5] Implement | (pipe) command Pedro Alves
  5 siblings, 0 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers

2019-05-30  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* gdbcmd.h (execute_command_to_ui_file): New declaration.
	top.c (execute_command_to_ui_file): New function, mostly a copy
	of execute_command_to_string.
	(execute_command_to_string): Implement by calling
	execute_command_to_ui_file.
---
 gdb/gdbcmd.h |  2 ++
 gdb/top.c    | 35 ++++++++++++++++++++++-------------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/gdb/gdbcmd.h b/gdb/gdbcmd.h
index 5d0e697d83..1b47719f18 100644
--- a/gdb/gdbcmd.h
+++ b/gdb/gdbcmd.h
@@ -139,6 +139,8 @@ extern void execute_command (const char *, int);
    as cli_styling.  */
 extern std::string execute_command_to_string (const char *p, int from_tty,
 					      bool term_out);
+extern void execute_command_to_ui_file (struct ui_file *file,
+					const char *p, int from_tty);
 
 extern void print_command_line (struct command_line *, unsigned int,
 				struct ui_file *);
diff --git a/gdb/top.c b/gdb/top.c
index 518c5ebb5f..4f55d6af1e 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -665,13 +665,12 @@ execute_command (const char *p, int from_tty)
   cleanup_if_error.release ();
 }
 
-/* Run execute_command for P and FROM_TTY.  Capture its output into the
-   returned string, do not display it to the screen.  BATCH_FLAG will be
+/* Run execute_command for P and FROM_TTY.  Sends its output to FILE,
+   do not display it to the screen.  BATCH_FLAG will be
    temporarily set to true.  */
 
-std::string
-execute_command_to_string (const char *p, int from_tty,
-			   bool term_out)
+void
+execute_command_to_ui_file (struct ui_file *file, const char *p, int from_tty)
 {
   /* GDB_STDOUT should be better already restored during these
      restoration callbacks.  */
@@ -679,26 +678,36 @@ execute_command_to_string (const char *p, int from_tty,
 
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  string_file str_file (term_out);
-
   {
-    current_uiout->redirect (&str_file);
+    current_uiout->redirect (file);
     ui_out_redirect_pop redirect_popper (current_uiout);
 
     scoped_restore save_stdout
-      = make_scoped_restore (&gdb_stdout, &str_file);
+      = make_scoped_restore (&gdb_stdout, file);
     scoped_restore save_stderr
-      = make_scoped_restore (&gdb_stderr, &str_file);
+      = make_scoped_restore (&gdb_stderr, file);
     scoped_restore save_stdlog
-      = make_scoped_restore (&gdb_stdlog, &str_file);
+      = make_scoped_restore (&gdb_stdlog, file);
     scoped_restore save_stdtarg
-      = make_scoped_restore (&gdb_stdtarg, &str_file);
+      = make_scoped_restore (&gdb_stdtarg, file);
     scoped_restore save_stdtargerr
-      = make_scoped_restore (&gdb_stdtargerr, &str_file);
+      = make_scoped_restore (&gdb_stdtargerr, file);
 
     execute_command (p, from_tty);
   }
+}
+
+/* Run execute_command for P and FROM_TTY.  Capture its output into the
+   returned string, do not display it to the screen.  BATCH_FLAG will be
+   temporarily set to true.  */
+
+std::string
+execute_command_to_string (const char *p, int from_tty,
+			   bool term_out)
+{
+  string_file str_file (term_out);
 
+  execute_command_to_ui_file (&str_file, p, from_tty);
   return std::move (str_file.string ());
 }
 
-- 
2.20.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFAv4 4/5] Test the | (pipe) command.
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 3/5] " Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 1/5] Add previous_saved_command_line to allow a command to repeat a previous command Philippe Waroquiers
@ 2019-05-30 14:21 ` Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 5/5] NEWS and documentation for " Philippe Waroquiers
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers

gdb/testsuite/ChangeLog
2019-05-30  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* gdb.base/shell.exp: Test pipe command, $_shell_exitcode,
	$_shell_exitsignal.
	* gdb.base/default.exp: Update for new convenience variables.
---
 gdb/testsuite/gdb.base/default.exp |  2 +
 gdb/testsuite/gdb.base/shell.exp   | 90 +++++++++++++++++++++++++++++-
 2 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 56ec917aa3..0325b8045d 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -606,6 +606,8 @@ set show_conv_list \
 	{$_isvoid = <internal function _isvoid>} \
 	{$_gdb_major = 8} \
 	{$_gdb_minor = 4} \
+	{$_shell_exitsignal = void} \
+	{$_shell_exitcode = 0} \
     }
 if ![skip_python_tests] {
     append show_conv_list \
diff --git a/gdb/testsuite/gdb.base/shell.exp b/gdb/testsuite/gdb.base/shell.exp
index 60d6e31e4f..375575b729 100644
--- a/gdb/testsuite/gdb.base/shell.exp
+++ b/gdb/testsuite/gdb.base/shell.exp
@@ -13,7 +13,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Test that the shell and ! commands work.
+# Test that the "shell", "!", "|" and "pipe" commands work.
 
 gdb_exit
 gdb_start
@@ -22,3 +22,91 @@ gdb_test "shell echo foo" "foo"
 
 gdb_test "! echo foo" "foo"
 gdb_test "!echo foo" "foo"
+
+# Convenience variables with shell command.
+gdb_test_no_output "! exit 0"
+gdb_test "p \$_shell_exitcode" " = 0" "shell success exitcode"
+gdb_test "p \$_shell_exitsignal" " = void" "shell success exitsignal"
+
+gdb_test_no_output "! exit 1"
+gdb_test "p \$_shell_exitcode" " = 1" "shell fail exitcode"
+gdb_test "p \$_shell_exitsignal" " = void" "shell fail exitsignal"
+
+gdb_test_no_output "! kill -2 $$"
+gdb_test "p \$_shell_exitcode" " = void" "shell interrupt exitcode"
+gdb_test "p \$_shell_exitsignal" " = 2" "shell interrupt exitsignal"
+
+# Define the user command "foo", used to test "pipe" command.
+gdb_test_multiple "define foo" "define foo" {
+    -re "End with"  {
+	pass "define foo"
+    }
+}
+gdb_test \
+    [multi_line_input \
+	 { echo coucou\n }\
+	 { echo truc\n }\
+	 { echo machin\n }\
+	 { if $argc > 0 }\
+	 { echo $arg0\n}\
+	 {end}\
+	 {end}] \
+    "" \
+    "enter commands"
+
+
+gdb_test "pipe foo | wc -l" "3" "simple pipe"
+gdb_test "pipe foo brol| wc -l" "4" "simple pipe with arg"
+gdb_test "pipe foo truc2 | grep truc | wc -l" "2" "double pipe"
+
+gdb_test "| foo truc2 | grep truc | wc -l" "2" "double pipe, pipe char"
+gdb_test "|foo|grep truc|wc -l" "1" "no space around pipe char"
+
+gdb_test "echo coucou\\n" "coucou" "echo coucou"
+gdb_test "||wc -l" "1" "repeat previous command"
+
+gdb_test "| -d ! echo this contains a | character\\n ! sed -e 's/|/PIPE/'" \
+    "this contains a PIPE character" "alternate 1char delim"
+
+gdb_test "|-d ! echo this contains a | character\\n!sed -e 's/|/PIPE/'" \
+    "this contains a PIPE character" "alternate 1char delim, no space"
+
+gdb_test "| -d !!! echo this contains a | character\\n !!! sed -e 's/|/PIPE/'" \
+    "this contains a PIPE character" "alternate 3char delim"
+
+gdb_test "|-d !!! echo this contains a | character\\n!!!sed -e 's/|/PIPE/'" \
+    "this contains a PIPE character" "alternate 3char delim, no space"
+
+# Convenience variables with pipe command.
+gdb_test "|p 123| exit 0" ""
+gdb_test "p \$_shell_exitcode" " = 0" "pipe success exitcode"
+gdb_test "p \$_shell_exitsignal" " = void" "pipe success exitsignal"
+
+gdb_test "|p 123| exit 1" ""
+gdb_test "p \$_shell_exitcode" " = 1" "pipe fail exitcode"
+gdb_test "p \$_shell_exitsignal" " = void" "pipe fail exitsignal"
+
+gdb_test "|p 123| kill -2 $$" ""
+gdb_test "p \$_shell_exitcode" " = void" "pipe interrupt exitcode"
+gdb_test "p \$_shell_exitsignal" " = 2" "pipe interrupt exitsignal"
+
+# Error handling verifications.
+gdb_test "|" "Missing COMMAND" "all missing"
+gdb_test "|-d" "Missing delimiter DELIM after -d" "-d value missing"
+gdb_test "|-d    " "Missing delimiter DELIM after -d" "-d spaces value missing"
+gdb_test "| echo coucou" \
+    "Missing delimiter before SHELL_COMMAND" \
+    "| delimiter missing"
+gdb_test "|-d DELIM echo coucou" \
+    "Missing delimiter before SHELL_COMMAND" \
+    "DELIM delimiter missing"
+gdb_test "|echo coucou|" \
+    "Missing SHELL_COMMAND" \
+    "SHELL_COMMAND missing"
+gdb_test "|-d ! echo coucou !" \
+    "Missing SHELL_COMMAND" \
+    "SHELL_COMMAND missing with delimiter"
+gdb_test "|-d! echo coucou ! wc" \
+    "Missing delimiter before SHELL_COMMAND" \
+    "Delimiter missing due to missing space"
+
-- 
2.20.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFAv4 5/5] NEWS and documentation for | (pipe) command.
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
                   ` (2 preceding siblings ...)
  2019-05-30 14:21 ` [RFAv4 4/5] Test the | (pipe) command Philippe Waroquiers
@ 2019-05-30 14:21 ` Philippe Waroquiers
  2019-05-30 15:02   ` Eli Zaretskii
  2019-05-31 14:39   ` Pedro Alves
  2019-05-30 14:21 ` [RFAv4 2/5] Add function execute_command_to_ui_file Philippe Waroquiers
  2019-05-31 14:42 ` [RFAv4 0/5] Implement | (pipe) command Pedro Alves
  5 siblings, 2 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers

gdb/ChangeLog
	* NEWS: Mention new pipe command and new convenience variables.

gdb/doc/ChangeLog
	* gdb.texinfo (Shell Commands): Document pipe command.
	(Logging Output): Add a reference to pipe command.
	(Convenience Variables): Document $_shell_exitcode and
	$_shell_exitstatus.
---
 gdb/NEWS            | 19 +++++++++++
 gdb/doc/gdb.texinfo | 79 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index ab582c036d..2ae6896bfb 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -27,8 +27,27 @@
      'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
      'static_members', 'max_elements', 'repeat_threshold', and 'format'.
 
+* New built-in convenience variables $_shell_exitcode and $_shell_exitsignal
+  provide the exitcode or exit status of the shell commands launched by
+  GDB commands such as "shell", "pipe" and "make".
+
 * New commands
 
+| [COMMAND] | SHELL_COMMAND
+| -d DELIM COMMAND DELIM SHELL_COMMAND
+pipe [COMMAND] | SHELL_COMMAND
+pipe -d DELIM COMMAND DELIM SHELL_COMMAND
+  Executes COMMAND and sends its output to SHELL_COMMAND.
+  With no COMMAND, repeat the last executed command
+  and send its output to SHELL_COMMAND.
+
+set print max-depth
+show print max-depth
+  Allows deeply nested structures to be simplified when printing by
+  replacing deeply nested parts (beyond the max-depth) with ellipses.
+  The default max-depth is 20, but this can be set to unlimited to get
+  the old behavior back.
+
 set may-call-functions [on|off]
 show may-call-functions
   This controls whether GDB will attempt to call functions in
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f2d8710d7d..79c22d0774 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1454,6 +1454,68 @@ Execute the @code{make} program with the specified
 arguments.  This is equivalent to @samp{shell make @var{make-args}}.
 @end table
 
+@table @code
+@kindex pipe
+@kindex |
+@cindex send the output of a gdb command to a shell command
+@anchor{pipe}
+@item pipe [@var{command}] | @var{shell_command}
+@itemx | [@var{command}] | @var{shell_command}
+@itemx pipe -d @var{delim} @var{command} @var{delim} @var{shell_command}
+@itemx | -d @var{delim} @var{command} @var{delim} @var{shell_command}
+Executes @var{command} and sends its output to @var{shell_command}.
+Note that no space is needed around @code{|}.
+If no @var{command} is provided, the last command executed is repeated.
+
+In case the @var{command} contains a @code{|}, the option @code{-d @var{delim}}
+can be used to specify an alternate delimiter string @var{delim} that separates
+the @var{command} from the @var{shell_command}.
+
+Example:
+@smallexample
+@group
+(gdb) p var
+$1 = @{
+  black = 144,
+  red = 233,
+  green = 377,
+  blue = 610,
+  white = 987
+@}
+@end group
+@group
+(gdb) pipe p var|wc
+      7      19      80
+(gdb) |p var|wc -l
+7
+@end group
+@group
+(gdb) p /x var
+$4 = @{
+  black = 0x90,
+  red = 0xe9,
+  green = 0x179,
+  blue = 0x262,
+  white = 0x3db
+@}
+(gdb) ||grep red
+  red => 0xe9,
+@end group
+@group
+(gdb) | -d ! echo this contains a | char\n ! sed -e 's/|/PIPE/'
+this contains a PIPE char
+(gdb) | -d xxx echo this contains a | char!\n xxx sed -e 's/|/PIPE/'
+this contains a PIPE char!
+(gdb)
+@end group
+@end smallexample
+@end table
+
+The convenience variables @code{$_shell_exitcode} and @code{$_shell_exitsignal}
+can be used to examine the exit status of the last shell command launched
+by @code{shell}, @code{make}, @code{pipe} and @code{|}.
+@xref{Convenience Vars,, Convenience Variables}.
+
 @node Logging Output
 @section Logging Output
 @cindex logging @value{GDBN} output
@@ -1485,6 +1547,8 @@ Set @code{debugredirect} if you want debug output to go only to the log file.
 Show the current values of the logging settings.
 @end table
 
+You can also redirect the output of a @value{GDBN} command to a
+shell command.  @xref{pipe}.
 @node Commands
 @chapter @value{GDBN} Commands
 
@@ -11304,6 +11368,21 @@ the value 12 for @code{$_gdb_minor}.  These variables allow you to
 write scripts that work with different versions of @value{GDBN}
 without errors caused by features unavailable in some of those
 versions.
+
+@item $_shell_exitcode
+@itemx $_shell_exitsignal
+@vindex $_shell_exitcode@r{, convenience variable}
+@vindex $_shell_exitsignal@r{, convenience variable}
+@cindex shell command, exit code
+@cindex shell command, exit signal
+@cindex exit status of shell commands
+@value{GDBN} commands such as @code{shell} and @code{|} are launching
+shell commands.  When a launched command terminates, @value{GDBN}
+automatically maintains the variables @code{$_shell_exitcode}
+and @code{$_shell_exitsignal} according to the exit status of the last
+launched command.  These variables are set and used similarly to
+the variables @code{$_exitcode} and @code{$_exitsignal}.
+
 @end table
 
 @node Convenience Funs
-- 
2.20.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFAv4 1/5] Add previous_saved_command_line to allow a command to repeat a previous command.
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 3/5] " Philippe Waroquiers
@ 2019-05-30 14:21 ` Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 4/5] Test the | (pipe) command Philippe Waroquiers
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers

Currently, a previous command can be repeated when the user types an
empty line.  This is implemented in handle_line_of_input by
returning saved_command_line in case an empty line has been input.

If we want a command to repeat the previous command, we need to save
the previous saved_command_line, as when a command runs, the saved_command_line
already contains the current command line of the command being executed.

As suggested by Tom, the previous_saved_command_line is made static.
At the same time, saved_command_line is also made static.
The support functions/variables for the repeat command logic are now all
located inside top.c.

gdb/ChangeLog
2019-05-30  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* top.h (saved_command_line): Remove declaration.
	* top.c (previous_saved_command_line, previous_repeat_arguments):
	New variables.
	(saved_command_line): Make static, define together with other
	'repeat variables'.
	(dont_repeat): Clear repeat_arguments.
	(repeat_previous, get_saved_command_line, save_command_line):
	New functions.
	(gdb_init): Initialize saved_command_line
	and previous_saved_command_line.
	* main.c (captured_main_1): Remove saved_command_line initialization.
	* event-top.c (handle_line_of_input): Update to use
	the new 'repeat' related functions instead of direct access to
	saved_command_line.
	* command.h (repeat_previous, get_saved_command_line,
	save_command_line): New declarations.
	(dont_repeat): Add comment.
---
 gdb/command.h   | 36 +++++++++++++++++++++++-
 gdb/event-top.c | 16 +++++------
 gdb/main.c      |  2 --
 gdb/top.c       | 74 +++++++++++++++++++++++++++++++++++++++++--------
 gdb/top.h       |  1 -
 5 files changed, 105 insertions(+), 24 deletions(-)

diff --git a/gdb/command.h b/gdb/command.h
index 35006cc339..26e4029008 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -448,7 +448,29 @@ extern void cmd_show_list (struct cmd_list_element *, int, const char *);
 
 extern void error_no_arg (const char *) ATTRIBUTE_NORETURN;
 
-extern void dont_repeat (void);
+
+/* Command line saving and repetition.
+   Each input line executed is saved to possibly be repeated either
+   when the user types an empty line, or be repeated by a command
+   that wants to repeat the previously executed command.  The below
+   functions control command repetition.  */
+
+/* Commands call dont_repeat if they do not want to be repeated by null
+   lines or by repeat_previous ().  */
+
+extern void dont_repeat ();
+
+/* Commands call repeat_previous if they want to repeat the previous command.
+   Such commands that repeat the previous command must indicate
+   to not repeat themselves, to avoid recursive repeat.
+   repeat_previous will mark the current command as not repeating,
+   and will ensure get_saved_command_line returns the previous command,
+   so that the currently executing command can repeat it.  */
+
+extern void repeat_previous ();
+
+/* Prevent dont_repeat from working, and return a cleanup that
+   restores the previous state.  */
 
 extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
 
@@ -457,6 +479,18 @@ extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
 
 extern void set_repeat_arguments (const char *args);
 
+/* Returns the saved command line to repeat.
+   When a command is being executed, this is the currently executing
+   command line, unless the currently executing command has called
+   repeat_previous (): in this case, get_saved_command_line returns
+   the previously saved command line.  */
+
+extern char *get_saved_command_line ();
+
+/* Takes a copy of CMD, for possible repetition.  */
+
+extern void save_command_line (const char *cmd);
+
 /* Used to mark commands that don't do anything.  If we just leave the
    function field NULL, the command is interpreted as a help topic, or
    as a class of commands.  */
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 93b7d2d28b..3facb38aae 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -634,11 +634,10 @@ command_line_append_input_line (struct buffer *cmd_line_buffer, const char *rl)
    If REPEAT, handle command repetitions:
 
      - If the input command line is NOT empty, the command returned is
-       copied into the global 'saved_command_line' var so that it can
-       be repeated later.
+       saved using save_command_line () so that it can be repeated later.
 
-     - OTOH, if the input command line IS empty, return the previously
-       saved command instead of the empty input line.
+     - OTOH, if the input command line IS empty, return the saved
+       command instead of the empty input line.
 */
 
 char *
@@ -673,7 +672,7 @@ handle_line_of_input (struct buffer *cmd_line_buffer,
   server_command = startswith (cmd, SERVER_COMMAND_PREFIX);
   if (server_command)
     {
-      /* Note that we don't set `saved_command_line'.  Between this
+      /* Note that we don't call `save_command_line'.  Between this
          and the check in dont_repeat, this insures that repeating
          will still do the right thing.  */
       return cmd + strlen (SERVER_COMMAND_PREFIX);
@@ -713,7 +712,7 @@ handle_line_of_input (struct buffer *cmd_line_buffer,
   for (p1 = cmd; *p1 == ' ' || *p1 == '\t'; p1++)
     ;
   if (repeat && *p1 == '\0')
-    return saved_command_line;
+    return get_saved_command_line ();
 
   /* Add command to history if appropriate.  Note: lines consisting
      solely of comments are also added to the command history.  This
@@ -728,9 +727,8 @@ handle_line_of_input (struct buffer *cmd_line_buffer,
   /* Save into global buffer if appropriate.  */
   if (repeat)
     {
-      xfree (saved_command_line);
-      saved_command_line = xstrdup (cmd);
-      return saved_command_line;
+      save_command_line (cmd);
+      return get_saved_command_line ();
     }
   else
     return cmd;
diff --git a/gdb/main.c b/gdb/main.c
index 35df1e497f..ef9bfe8fc6 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -499,8 +499,6 @@ captured_main_1 (struct captured_main_args *context)
 
   notice_open_fds ();
 
-  saved_command_line = (char *) xstrdup ("");
-
 #ifdef __MINGW32__
   /* Ensure stderr is unbuffered.  A Cygwin pty or pipe is implemented
      as a Windows pipe, and Windows buffers on pipes.  */
diff --git a/gdb/top.c b/gdb/top.c
index 1e17ebee87..518c5ebb5f 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -134,8 +134,26 @@ show_confirm (struct ui_file *file, int from_tty,
 char *current_directory;
 
 /* The last command line executed on the console.  Used for command
-   repetitions.  */
-char *saved_command_line;
+   repetitions when the user enters an empty line.  */
+
+static char *saved_command_line;
+
+/* If not NULL, the arguments that should be passed if
+   saved_command_line is repeated.  */
+
+static const char *repeat_arguments;
+
+/* The previous last command line executed on the console.  Used for command
+   repetitions when a command wants to relaunch the previously launched
+   command.  We need this as when a command is running, saved_command_line
+   already contains the line of the currently executing command.  */
+
+char *previous_saved_command_line;
+
+/* If not NULL, the arguments that should be passed if the
+   previous_saved_command_line is repeated.  */
+
+static const char *previous_repeat_arguments;
 
 /* Nonzero if the current command is modified by "server ".  This
    affects things like recording into the command history, commands
@@ -521,11 +539,6 @@ maybe_wait_sync_command_done (int was_sync)
     wait_sync_command_done ();
 }
 
-/* If not NULL, the arguments that should be passed if the current
-   command is repeated.  */
-
-static const char *repeat_arguments;
-
 /* See command.h.  */
 
 void
@@ -695,7 +708,7 @@ execute_command_to_string (const char *p, int from_tty,
 
 static int suppress_dont_repeat = 0;
 
-/* Commands call this if they do not want to be repeated by null lines.  */
+/* See command.h  */
 
 void
 dont_repeat (void)
@@ -709,11 +722,27 @@ dont_repeat (void)
      thing read from stdin in line and don't want to delete it.  Null
      lines won't repeat here in any case.  */
   if (ui->instream == ui->stdin_stream)
-    *saved_command_line = 0;
+    {
+      *saved_command_line = 0;
+      repeat_arguments = NULL;
+    }
+}
+
+/* See command.h  */
+
+void
+repeat_previous ()
+{
+  /* Do not repeat this command, as this command is a repeating command.  */
+  dont_repeat ();
+
+  /* We cannot free saved_command_line, as this line is being executed,
+     so swap it with previous_saved_command_line.  */
+  std::swap (previous_saved_command_line, saved_command_line);
+  std::swap (previous_repeat_arguments, repeat_arguments);
 }
 
-/* Prevent dont_repeat from working, and return a cleanup that
-   restores the previous state.  */
+/* See command.h.  */
 
 scoped_restore_tmpl<int>
 prevent_dont_repeat (void)
@@ -721,6 +750,26 @@ prevent_dont_repeat (void)
   return make_scoped_restore (&suppress_dont_repeat, 1);
 }
 
+/* See command.h.  */
+
+char *
+get_saved_command_line ()
+{
+  return saved_command_line;
+}
+
+/* See command.h.  */
+
+void
+save_command_line (const char *cmd)
+{
+  xfree (previous_saved_command_line);
+  previous_saved_command_line = saved_command_line;
+  previous_repeat_arguments = repeat_arguments;
+  saved_command_line = xstrdup (cmd);
+  repeat_arguments = NULL;
+}
+
 \f
 /* Read a line from the stream "instream" without command line editing.
 
@@ -2179,6 +2228,9 @@ The second argument is the terminal the UI runs on.\n"), &cmdlist);
 void
 gdb_init (char *argv0)
 {
+  saved_command_line = xstrdup ("");
+  previous_saved_command_line = xstrdup ("");
+
   if (pre_init_ui_hook)
     pre_init_ui_hook ();
 
diff --git a/gdb/top.h b/gdb/top.h
index 025d9389d6..aab03c13d6 100644
--- a/gdb/top.h
+++ b/gdb/top.h
@@ -217,7 +217,6 @@ extern void ui_register_input_event_handler (struct ui *ui);
 extern void ui_unregister_input_event_handler (struct ui *ui);
 
 /* From top.c.  */
-extern char *saved_command_line;
 extern int confirm;
 extern int inhibit_gdbinit;
 extern const char gdbinit[];
-- 
2.20.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFAv4 3/5] Implement | (pipe) command.
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
@ 2019-05-30 14:21 ` Philippe Waroquiers
  2019-05-30 14:21 ` [RFAv4 1/5] Add previous_saved_command_line to allow a command to repeat a previous command Philippe Waroquiers
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-30 14:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Philippe Waroquiers

The pipe command allows to run a GDB command, and pipe its output
to a shell command:
  (gdb) help pipe
  Send the output of a gdb command to a shell command.
  Usage: | [COMMAND] | SHELL_COMMAND
  Usage: | -d DELIM COMMAND DELIM SHELL_COMMAND
  Usage: pipe [COMMAND] | SHELL_COMMAND
  Usage: pipe -d DELIM COMMAND DELIM SHELL_COMMAND

  Executes COMMAND and sends its output to SHELL_COMMAND.

  The -d option indicates to use the string DELIM to separate COMMAND
  from SHELL_COMMAND, in alternative to |.  This is useful in
  case COMMAND contains a | character.

  With no COMMAND, repeat the last executed command
  and send its output to SHELL_COMMAND.
  (gdb)

For example:
  (gdb) pipe print some_data_structure | grep -B3 -A3 something

The pipe character is defined as an alias for pipe command, so that
the above can be typed as:
  (gdb) | print some_data_structure | grep -B3 -A3 something

If no GDB COMMAND is given, then the previous command is relaunched,
and its output is sent to the given SHELL_COMMAND.

This also defines convenience vars $_shell_exitcode and $_shell_exitsignal
to record the exit code and exit signal of the last shell command
launched by GDB e.g. by "shell", "pipe", ...

gdb/ChangeLog
2019-05-30  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* cli/cli-cmds.c (pipe_command): New function.
	(_initialize_cli_cmds): Call add_com for pipe_command.
	Define | as an alias for pipe.
	(exit_status_set_internal_vars): New function.
	(shell_escape): Call exit_status_set_internal_vars.
	cli/cli-decode.c (find_command_name_length): Recognize | as
	a single character command.
---
 gdb/cli/cli-cmds.c   | 104 +++++++++++++++++++++++++++++++++++++++++++
 gdb/cli/cli-decode.c |   4 +-
 2 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index daf409a558..bb2b04ddd8 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -24,6 +24,7 @@
 #include "completer.h"
 #include "target.h"	/* For baud_rate, remote_debug and remote_timeout.  */
 #include "common/gdb_wait.h"	/* For shell escape implementation.  */
+#include "gdbcmd.h"
 #include "gdb_regex.h"	/* Used by apropos_command.  */
 #include "gdb_vfork.h"
 #include "linespec.h"
@@ -41,6 +42,7 @@
 #include "block.h"
 
 #include "ui-out.h"
+#include "interps.h"
 
 #include "top.h"
 #include "cli/cli-decode.h"
@@ -668,6 +670,25 @@ echo_command (const char *text, int from_tty)
   gdb_flush (gdb_stdout);
 }
 
+/* Sets the last launched shell command convenience variables based on
+   EXIT_STATUS.  */
+
+static void
+exit_status_set_internal_vars (int exit_status)
+{
+  struct internalvar *var_code = lookup_internalvar ("_shell_exitcode");
+  struct internalvar *var_signal = lookup_internalvar ("_shell_exitsignal");
+
+  clear_internalvar (var_code);
+  clear_internalvar (var_signal);
+  if (WIFEXITED (exit_status))
+    set_internalvar_integer (var_code, WEXITSTATUS (exit_status));
+  else if (WIFSIGNALED (exit_status))
+    set_internalvar_integer (var_signal, WTERMSIG (exit_status));
+  else
+    warning (_("unexpected shell command exit status %d\n"), exit_status);
+}
+
 static void
 shell_escape (const char *arg, int from_tty)
 {
@@ -689,6 +710,7 @@ shell_escape (const char *arg, int from_tty)
   /* Make sure to return to the directory GDB thinks it is, in case
      the shell command we just ran changed it.  */
   chdir (current_directory);
+  exit_status_set_internal_vars (rc);
 #endif
 #else /* Can fork.  */
   int status, pid;
@@ -716,6 +738,7 @@ shell_escape (const char *arg, int from_tty)
     waitpid (pid, &status, 0);
   else
     error (_("Fork failed"));
+  exit_status_set_internal_vars (status);
 #endif /* Can fork.  */
 }
 
@@ -827,6 +850,70 @@ edit_command (const char *arg, int from_tty)
   xfree (p);
 }
 
+/* Implementation of the "pipe" command.  */
+
+static void
+pipe_command (const char *arg, int from_tty)
+{
+  std::string delim ("|");
+
+  if (arg != nullptr && check_for_argument (&arg, "-d", 2))
+    {
+      delim = extract_arg (&arg);
+      if (delim.empty ())
+	error (_("Missing delimiter DELIM after -d"));
+    }
+
+  const char *command = arg;
+  if (command == nullptr)
+    error (_("Missing COMMAND"));
+
+  arg = strstr (arg, delim.c_str ());
+
+  if (arg == nullptr)
+    error (_("Missing delimiter before SHELL_COMMAND"));
+
+  std::string gdb_cmd (command, arg - command);
+
+  arg += delim.length (); /* Skip the delimiter.  */
+
+  if (gdb_cmd.empty ())
+    {
+      repeat_previous ();
+      gdb_cmd = skip_spaces (get_saved_command_line ());
+      if (gdb_cmd.empty ())
+	error (_("No previous command to relaunch"));
+    }
+
+  const char *shell_command = skip_spaces (arg);
+  if (*shell_command == '\0')
+    error (_("Missing SHELL_COMMAND"));
+
+  FILE *to_shell_command = popen (shell_command, "w");
+
+  if (to_shell_command == nullptr)
+    error (_("Error launching \"%s\""), shell_command);
+
+  try
+    {
+      stdio_file pipe_file (to_shell_command);
+
+      execute_command_to_ui_file (&pipe_file, gdb_cmd.c_str (), from_tty);
+    }
+  catch (...)
+    {
+      pclose (to_shell_command);
+      throw;
+    }
+
+  int exit_status = pclose (to_shell_command);
+
+  if (exit_status < 0)
+    error (_("shell command \"%s\" failed: %s"), shell_command,
+           safe_strerror (errno));
+  exit_status_set_internal_vars (exit_status);
+}
+
 static void
 list_command (const char *arg, int from_tty)
 {
@@ -1818,6 +1905,23 @@ Uses EDITOR environment variable contents as editor (or ex as default)."));
 
   c->completer = location_completer;
 
+  c = add_com ("pipe", class_support, pipe_command, _("\
+Send the output of a gdb command to a shell command.\n\
+Usage: | [COMMAND] | SHELL_COMMAND\n\
+Usage: | -d DELIM COMMAND DELIM SHELL_COMMAND\n\
+Usage: pipe [COMMAND] | SHELL_COMMAND\n\
+Usage: pipe -d DELIM COMMAND DELIM SHELL_COMMAND\n\
+\n\
+Executes COMMAND and sends its output to SHELL_COMMAND.\n\
+\n\
+The -d option indicates to use the string DELIM to separate COMMAND\n\
+from SHELL_COMMAND, in alternative to |.  This is useful in\n\
+case COMMAND contains a | character.\n\
+\n\
+With no COMMAND, repeat the last executed command\n\
+and send its output to SHELL_COMMAND."));
+  add_com_alias ("|", "pipe", class_support, 0);
+
   add_com ("list", class_files, list_command, _("\
 List specified function or line.\n\
 With no argument, lists ten more lines after or around previous listing.\n\
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 72e2a97009..e3076f263c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -1311,9 +1311,9 @@ find_command_name_length (const char *text)
      Note that this is larger than the character set allowed when
      creating user-defined commands.  */
 
-  /* Recognize '!' as a single character command so that, e.g., "!ls"
+  /* Recognize the single character commands so that, e.g., "!ls"
      works as expected.  */
-  if (*p == '!')
+  if (*p == '!' || *p == '|')
     return 1;
 
   while (isalnum (*p) || *p == '-' || *p == '_'
-- 
2.20.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFAv4 5/5] NEWS and documentation for | (pipe) command.
  2019-05-30 14:21 ` [RFAv4 5/5] NEWS and documentation for " Philippe Waroquiers
@ 2019-05-30 15:02   ` Eli Zaretskii
  2019-05-31 14:39   ` Pedro Alves
  1 sibling, 0 replies; 10+ messages in thread
From: Eli Zaretskii @ 2019-05-30 15:02 UTC (permalink / raw)
  To: Philippe Waroquiers; +Cc: gdb-patches

> From: Philippe Waroquiers <philippe.waroquiers@skynet.be>
> Cc: Philippe Waroquiers <philippe.waroquiers@skynet.be>
> Date: Thu, 30 May 2019 16:21:06 +0200
> 
> gdb/ChangeLog
> 	* NEWS: Mention new pipe command and new convenience variables.
> 
> gdb/doc/ChangeLog
> 	* gdb.texinfo (Shell Commands): Document pipe command.
> 	(Logging Output): Add a reference to pipe command.
> 	(Convenience Variables): Document $_shell_exitcode and
> 	$_shell_exitstatus.

This part is OK.  Thanks.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFAv4 5/5] NEWS and documentation for | (pipe) command.
  2019-05-30 14:21 ` [RFAv4 5/5] NEWS and documentation for " Philippe Waroquiers
  2019-05-30 15:02   ` Eli Zaretskii
@ 2019-05-31 14:39   ` Pedro Alves
  1 sibling, 0 replies; 10+ messages in thread
From: Pedro Alves @ 2019-05-31 14:39 UTC (permalink / raw)
  To: Philippe Waroquiers, gdb-patches

On 5/30/19 3:21 PM, Philippe Waroquiers wrote:
> +
> +set print max-depth
> +show print max-depth
> +  Allows deeply nested structures to be simplified when printing by
> +  replacing deeply nested parts (beyond the max-depth) with ellipses.
> +  The default max-depth is 20, but this can be set to unlimited to get
> +  the old behavior back.

Looks like this ended up here by mistake?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFAv4 0/5] Implement | (pipe) command.
  2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
                   ` (4 preceding siblings ...)
  2019-05-30 14:21 ` [RFAv4 2/5] Add function execute_command_to_ui_file Philippe Waroquiers
@ 2019-05-31 14:42 ` Pedro Alves
  2019-05-31 15:29   ` Philippe Waroquiers
  5 siblings, 1 reply; 10+ messages in thread
From: Pedro Alves @ 2019-05-31 14:42 UTC (permalink / raw)
  To: Philippe Waroquiers, gdb-patches

On 5/30/19 3:21 PM, Philippe Waroquiers wrote:
> Implement | (pipe) command.
> 
> This patch series adds the pipe command, that allows to send the output
> of a GDB command to a shell command.
> 
> This is the fourth version of the patch, handling the additional
> comments of Pedro.
> The doc, help and NEWS are changed in this fourth version, so must be
> re-reviewed.

This version looks good to me, apart for the comment I sent about NEWS.

From the nit department, in the testcase patch, in the last test,
use lowercase for "Delimiter" in the test message.

With those fixed, this is good to go.  Please push.

Thanks for doing this.

Pedro Alves

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFAv4 0/5] Implement | (pipe) command.
  2019-05-31 14:42 ` [RFAv4 0/5] Implement | (pipe) command Pedro Alves
@ 2019-05-31 15:29   ` Philippe Waroquiers
  0 siblings, 0 replies; 10+ messages in thread
From: Philippe Waroquiers @ 2019-05-31 15:29 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On Fri, 2019-05-31 at 15:41 +0100, Pedro Alves wrote:
> On 5/30/19 3:21 PM, Philippe Waroquiers wrote:
> > Implement | (pipe) command.
> > 
> > This patch series adds the pipe command, that allows to send the output
> > of a GDB command to a shell command.
> > 
> > This is the fourth version of the patch, handling the additional
> > comments of Pedro.
> > The doc, help and NEWS are changed in this fourth version, so must be
> > re-reviewed.
> 
> This version looks good to me, apart for the comment I sent about NEWS.
> 
> From the nit department, in the testcase patch, in the last test,
> use lowercase for "Delimiter" in the test message.
> 
> With those fixed, this is good to go.  Please push.
Pushed after fixing the above 2 things.

Thanks for the reviews

Philippe

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-05-31 15:29 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-30 14:21 [RFAv4 0/5] Implement | (pipe) command Philippe Waroquiers
2019-05-30 14:21 ` [RFAv4 3/5] " Philippe Waroquiers
2019-05-30 14:21 ` [RFAv4 1/5] Add previous_saved_command_line to allow a command to repeat a previous command Philippe Waroquiers
2019-05-30 14:21 ` [RFAv4 4/5] Test the | (pipe) command Philippe Waroquiers
2019-05-30 14:21 ` [RFAv4 5/5] NEWS and documentation for " Philippe Waroquiers
2019-05-30 15:02   ` Eli Zaretskii
2019-05-31 14:39   ` Pedro Alves
2019-05-30 14:21 ` [RFAv4 2/5] Add function execute_command_to_ui_file Philippe Waroquiers
2019-05-31 14:42 ` [RFAv4 0/5] Implement | (pipe) command Pedro Alves
2019-05-31 15:29   ` Philippe Waroquiers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).