Introduce the "with" command

Message ID 20190614193759.21029-1-palves@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves June 14, 2019, 7:37 p.m. UTC
  ( See original discussion and prototype here:
   https://sourceware.org/ml/gdb-patches/2019-05/msg00570.html )

 (gdb) help with
 Temporarily set SETTING to VALUE, run COMMAND, and restore SETTING.
 Usage: with SETTING [VALUE] [-- COMMAND]
 Usage: w SETTING [VALUE] [-- COMMAND]
 SETTING is any setting settable with the "set" command.
 E.g.:
   with language pascal -- print obj
   with print elements unlimited -- print obj

As can be seen above, the "with" command is just like "set", but
instead of setting the setting permanently, it sets the setting, runs
a command and then restores the setting.

 (gdb) p g_s
 $1 = {a = 1, b = 2, c = 3}
 (gdb) with language ada -- print g_s
 $2 = (a => 1, b => 2, c => 3)
 Warning: the current language does not match this frame.
 (gdb) show language
 The current source language is "auto; currently c".
 (gdb) with print elements 100 -- with print object on -- print 1
 $3 = 1

You can shorten things a bit though, as long as unambiguous.  So this:

 (gdb) with print elements 100 -- with print object off -- print 1

is the same as:

 (gdb) w p el 100 -- w p o 0 -- p 1

Note that the patch adds a "w" alias for "with", as "w" is not
currently taken:

 (gdb) w
 Ambiguous command "w": watch, wh, whatis, where, while, while-stepping, winheight, ws.

Let me know if you'd prefer to reserve "w" for one of the other
commands above.  IMHO, this command will end up being used frequently
enough that it deserves the "w" shorthand.

A nice feature is that this is fully integrated with TAB-completion:

 (gdb) with p[TAB]
 pagination  print       prompt      python
 (gdb) with print [TAB]
 address                max-depth              static-members
 array                  max-symbolic-offset    symbol
 array-indexes          null-stop              symbol-filename
 asm-demangle           object                 symbol-loading
 demangle               pascal_static-members  thread-events
 elements               pretty                 type
 entry-values           raw                    union
 frame-arguments        repeats                vtbl
 inferior-events        sevenbit-strings
 (gdb) with print [TAB]

 (gdb) with print elements unlimited -- thread apply all -
 -ascending  -c          -q          -s

 (gdb) with print elements unlimited -- print -[TAB]
 -address         -max-depth       -repeats         -vtbl
 -array           -null-stop       -static-members
 -array-indexes   -object          -symbol
 -elements        -pretty          -union

The main advantage of this new command compared to command options,
like the new "print -OPT", is that this command works with any
setting, and, it works nicely when you want to override a setting
while running a user-defined command, like:

 (gdb) with print pretty -- usercmd

The disadvantage is that it isn't as compact or easy to type.  I think
of command options and this command as complementary.  I think that
even with this new command, it makes sense to continue developing the
command options in the direction of exposing most-oft-used settings as
command options.

Inspired by Philippe's "/" command proposal, if no command is
specified, then the last command is re-invoked, under the overridden
setting:

 (gdb) p g_s
 $1 = {a = 1, b = 2, c = 3}
 (gdb) with language ada
 $2 = (a => 1, b => 2, c => 3)
 Warning: the current language does not match this frame.

Note: "with" requires "--" to separate the setting from the command.
It might be possible to do without that, but, I haven't tried it yet,
and I think that this can go in without it.  We can always downgrade
to making "--" optional if we manage to make it work.

On to the patch itself, the implementation of the command is simpler
than one might expect.  I factored out a bit from pipe_command into
repeat_previous directly, because otherwise I'd need to copy&paste the
same code and same error message in the with command.  The
parse_cli_var_uinteger/parse_cli_var_zuinteger_unlimited changes are
necessary since we can now pass an empty string as argument.

The documentation bits are originally based on Philippe's docs for the
"/" command, hence the attribution in the ChangeLog.

gdb/ChangeLog:
2019-06-14  Pedro Alves  <palves@redhat.com>

	* NEWS (New commands): Mention "with".
	* cli/cli-cmds.c (pipe_command): Adjust to new repeat_previous
	interface.
	* cli/cli-setshow.c (parse_cli_var_uinteger)
	(parse_cli_var_zuinteger_unlimited): Handle empty argument
	strings.
	* command.h (repeat_previous): Now returns const char *.  Adjust
	comment.
	* printcmd.c: Include "top.h", "cli/cli-decode.h", and
	"cli/cli-setshow.h".
	(with_command, with_command_completer): New.
	(_initialize_printcmd): Install the "with" command and its "w"
	alias.
	* top.c (repeat_previous): Move bits from pipe_command here:
	Return the saved command line, if any; error out if there's no
	command to relaunch.

gdb/doc/ChangeLog:
2019-06-14  Pedro Alves  <palves@redhat.com>
	    Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* gdb.texinfo (Command Settings): New node documenting the general
	concept of settings, how to change them, and the new "with"
	command.

gdb/testsuite/ChangeLog:
2019-06-14  Pedro Alves  <palves@redhat.com>

	* gdb.base/with.c: New file.
	* gdb.base/with.exp: New file.
---
 gdb/doc/gdb.texinfo             |  87 +++++++++++++++++++++
 gdb/NEWS                        |  12 +++
 gdb/cli/cli-cmds.c              |   7 +-
 gdb/cli/cli-setshow.c           |   4 +-
 gdb/command.h                   |  19 +++--
 gdb/printcmd.c                  | 132 +++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/with.c   |  41 ++++++++++
 gdb/testsuite/gdb.base/with.exp | 168 ++++++++++++++++++++++++++++++++++++++++
 gdb/top.c                       |   7 +-
 9 files changed, 460 insertions(+), 17 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/with.c
 create mode 100644 gdb/testsuite/gdb.base/with.exp
  

Comments

Philippe Waroquiers June 16, 2019, 10:19 p.m. UTC | #1
I quickly read the patch, and played a little bit with it

2 minor comments below.

Only one (minor) strange behaviour: 'out of range' encountered in
the below 'nested' case:
  (gdb) w print repeats unlimited -- w print repeats 1 -- p "1223334444"
  $18 = "1", '2' <repeats 2 times>, '3' <repeats 3 times>, '4' <repeats 4 times>
  integer 4294967295 out of range

with set thank-you on -- echo It looks really nice :)

Philippe


On Fri, 2019-06-14 at 20:37 +0100, Pedro Alves wrote:
> 	* printcmd.c: Include "top.h", "cli/cli-decode.h", and
> 	"cli/cli-setshow.h".
> 	(with_command, with_command_completer): New.
> 	(_initialize_printcmd): Install the "with" command and its "w"
> 	alias.
It is slightly unexpected to have the with_command in the printcmd.c
file, but I found no real good alternate place, as cli-setshow.c
does not define any command, it just provides support to the "set"
and "show" commands.


> diff --git a/gdb/printcmd.c b/gdb/printcmd.c
....
with_command_completer is not mentioned in valprint.h,
and can be static I think.
> +/* See valprint.h.  */
> +
> +void
> +with_command_completer (struct cmd_list_element *ignore,
> +			completion_tracker &tracker,
> +			const char *text, const char * /*word*/)
  
Pedro Alves June 17, 2019, 4:20 p.m. UTC | #2
On 6/16/19 11:19 PM, Philippe Waroquiers wrote:
> I quickly read the patch, and played a little bit with it
> 
> 2 minor comments below.
> 
> Only one (minor) strange behaviour: 'out of range' encountered in
> the below 'nested' case:
>   (gdb) w print repeats unlimited -- w print repeats 1 -- p "1223334444"
>   $18 = "1", '2' <repeats 2 times>, '3' <repeats 3 times>, '4' <repeats 4 times>
>   integer 4294967295 out of range

Oh, thanks for catching that.  The reason is that a user can type
0 to mean "unlimited", but, "unlimited" is stored internally as -1,
which is 4294967295 in unsigned int.  The patch is currently passing
the internal "-1" directly to the set command.  We need to translate
the -1 back to "unlimited" or 0.

We already have code to do that in do_show_command, so I'll reuse it.

As for testing, I'm going to tweak the recently added
"maint test-settings set/show" commands, rename them to
"maint set/show test-settings", and then I'll add a
"maint with" command, which is just like "with", but
works with "maint set" settings.  This will allow 
testing all kind of different combinations similarly to
your example above, like:

 (gdb) maint with test-settings uinteger unlimited -- maint with test-settings uinteger 1
 (gdb) maint with test-settings uinteger 1 -- maint with test-settings uinteger unlimited

and it'll be easy to do the same thing for all types of settings
(var_uinteger, var_zuinteger, var_string, etc.).

> 
> with set thank-you on -- echo It looks really nice :)

Cool. :-)

> 
> Philippe
> 
> 
> On Fri, 2019-06-14 at 20:37 +0100, Pedro Alves wrote:
>> 	* printcmd.c: Include "top.h", "cli/cli-decode.h", and
>> 	"cli/cli-setshow.h".
>> 	(with_command, with_command_completer): New.
>> 	(_initialize_printcmd): Install the "with" command and its "w"
>> 	alias.
> It is slightly unexpected to have the with_command in the printcmd.c
> file, but I found no real good alternate place, as cli-setshow.c
> does not define any command, it just provides support to the "set"
> and "show" commands.

Yeah.  I put it here, because it's where the set_command is defined,
I guess because "set" has a double purpose of being a prefix command,
and implementing "set VAR = EXP".  See just above with_command.
show_command is in cli-cmds.c, I suppose we could put "with" there.

> 
> 
>> diff --git a/gdb/printcmd.c b/gdb/printcmd.c
> ....
> with_command_completer is not mentioned in valprint.h,
> and can be static I think.
>> +/* See valprint.h.  */
>> +
>> +void
>> +with_command_completer (struct cmd_list_element *ignore,
>> +			completion_tracker &tracker,
>> +			const char *text, const char * /*word*/)
Thanks, will fix, though I'll need to export a version of this
for "maint with" in the new version.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a5026dfc034..dff71d07e23 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1560,6 +1560,7 @@  show you the alternatives available, if there is more than one possibility).
 
 @menu
 * Command Syntax::              How to give commands to @value{GDBN}
+* Command Settings::            How to change default behavior of commands
 * Completion::                  Command completion
 * Command Options::             Command options
 * Help::                        How to ask @value{GDBN} for help
@@ -1616,6 +1617,92 @@  commands.  This command accepts the current line, like @key{RET}, and
 then fetches the next line relative to the current line from the history
 for editing.
 
+
+@node Command Settings
+@section Command Settings
+@cindex change default behavior of commands
+@cindex change default settings
+
+Many commands change their behavior according to command-specific
+variables or settings.  These settings can be changed with the
+@code{set} subcommands.  For example, the @code{print} command
+(@pxref{Data, ,Examining Data}) prints arrays differently depending on
+settings changeable with the commands @code{set print elements
+NUMBER-OF-ELEMENTS} and @code{set print array-indexes}, among others.
+
+You can set these settings to your preference in the gdbinit files
+loaded at @value{GDBN} startup.  @xref{Startup}.
+
+The settings can also be changed interactively during the debugging
+session.  For example, to change the limit of array elements to print,
+you can do the following:
+@smallexample
+(@value{GDBN}) set print elements 10
+(@value{GDBN}) print some_array
+$1 = @{0, 10, 20, 30, 40, 50, 60, 70, 80, 90...@}
+@end smallexample
+
+The above @code{set print elements 10} command changes the number of
+elements to print from the default of 200 to 10.  If you only intend
+this limit of 10 to be used for printing @code{some_array}, then you
+must restore the limit back to 200, with @code{set print elements
+200}.
+
+Some commands allow overriding settings with command options.  For
+example, the @code{print} command supports a number of options that
+allow overriding relevant global print settings as set by @code{set
+print} subcommands.  @xref{print options}.  The example above could be
+rewritten as:
+@smallexample
+(@value{GDBN}) print -elements 10 -- some_array
+$1 = @{0, 10, 20, 30, 40, 50, 60, 70, 80, 90...@}
+@end smallexample
+
+Alternatively, you can use the @code{with} command to change a setting
+temporarily, for the duration of a command invocation.
+
+@table @code
+@kindex with command
+@kindex w @r{(@code{with})}
+@cindex settings
+@cindex temporarily change settings
+@item with @var{setting} [@var{value}] [-- @var{command}]
+@itemx w @var{setting} [@var{value}] [-- @var{command}]
+Temporarily set @var{setting} to @var{value} for the duration of
+@var{command}.
+
+If no @var{command} is provided, the last command executed is
+repeated.
+
+@var{setting} is any setting settable with the @code{set} subcommands.
+
+For example, the command
+@smallexample
+(@value{GDBN}) with print array on -- print some_array
+@end smallexample
+@noindent
+is equivalent to the following 3 commands:
+@smallexample
+(@value{GDBN}) set print array on
+(@value{GDBN}) print some_array
+(@value{GDBN}) set print array off
+@end smallexample
+
+The @code{with} command is particularly useful when you want to
+override a setting while running user-defined commands, or commands
+defined in Python or Guile.  @xref{Extending GDB,, Extending GDB}.
+
+@smallexample
+(@value{GDBN}) with print pretty on -- my_complex_command
+@end smallexample
+
+To change several settings for the same command, you can nest
+@code{with} commands.  For example, @code{with language ada -- with
+print elements 10} temporarily changes the language to Ada and sets a
+limit of 10 elements to print for arrays and strings.
+
+@end table
+
 @node Completion
 @section Command Completion
 
diff --git a/gdb/NEWS b/gdb/NEWS
index 8718c400d1e..dd993481af8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -46,6 +46,18 @@  pipe -d DELIM COMMAND DELIM SHELL_COMMAND
   With no COMMAND, repeat the last executed command
   and send its output to SHELL_COMMAND.
 
+with SETTING [VALUE] [-- COMMAND]
+w SETTING [VALUE] [-- COMMAND]
+  Temporarily set SETTING, run COMMAND, and restore SETTING.
+  Usage: with SETTING -- COMMAND
+  With no COMMAND, repeats the last executed command.
+  SETTING is any GDB setting settable with the "set" command.
+  For example, 'with language c -- print someobj' temporarily switches
+  to the C language in order to print someobj.  Settings can be
+  combined: 'w lang c -- w print elements unlimited -- usercmd'
+  switches to the C language and runs usercmd with no limit on array
+  elements to print.
+
 set may-call-functions [on|off]
 show may-call-functions
   This controls whether GDB will attempt to call functions in
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 62f4d7f0c5e..05aa9525b02 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -878,12 +878,7 @@  pipe_command (const char *arg, int from_tty)
   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"));
-    }
+    gdb_cmd = repeat_previous ();
 
   const char *shell_command = skip_spaces (arg);
   if (*shell_command == '\0')
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 8199fa7c0b0..91170627b3d 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -190,7 +190,7 @@  parse_cli_var_uinteger (var_types var_type, const char **arg,
 {
   LONGEST val;
 
-  if (*arg == nullptr)
+  if (*arg == nullptr || **arg == '\0')
     {
       if (var_type == var_uinteger)
 	error_no_arg (_("integer to set it to, or \"unlimited\"."));
@@ -225,7 +225,7 @@  parse_cli_var_zuinteger_unlimited (const char **arg, bool expression)
 {
   LONGEST val;
 
-  if (*arg == nullptr)
+  if (*arg == nullptr || **arg == '\0')
     error_no_arg (_("integer to set it to, or \"unlimited\"."));
 
   if (is_unlimited_literal (arg, expression))
diff --git a/gdb/command.h b/gdb/command.h
index 4d52f00de51..648950b5a44 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -461,14 +461,17 @@  extern void error_no_arg (const char *) ATTRIBUTE_NORETURN;
 
 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 ();
+/* 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 marks the current command as not repeating, and
+   ensures get_saved_command_line returns the previous command, so
+   that the currently executing command can repeat it.  If there's no
+   previous command, throws an error.  Otherwise, returns the result
+   of get_saved_command_line, which now points at the command to
+   repeat.  */
+
+extern const char *repeat_previous ();
 
 /* Prevent dont_repeat from working, and return a cleanup that
    restores the previous state.  */
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 0509360581e..801651b1e9f 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -44,10 +44,13 @@ 
 #include "parser-defs.h"
 #include "charset.h"
 #include "arch-utils.h"
+#include "top.h"
 #include "cli/cli-utils.h"
 #include "cli/cli-option.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-setshow.h"
 #include "common/format.h"
 #include "source.h"
 #include "common/byte-vector.h"
@@ -1289,6 +1292,123 @@  set_command (const char *exp, int from_tty)
   evaluate_expression (expr.get ());
 }
 
+/* The "with" command.  */
+
+static void
+with_command (const char *args, int from_tty)
+{
+  const char *delim = strstr (args, "--");
+  const char *nested_cmd = nullptr;
+
+  if (delim == args)
+    error (_("Missing setting before '--' delimiter\n"));
+
+  if (delim == nullptr || *skip_spaces (&delim[2]) == '\0')
+    nested_cmd = repeat_previous ();
+
+  const char *cmd = args;
+  cmd_list_element *set_cmd = lookup_cmd (&args, setlist, "with", 0, 1);
+
+  if (set_cmd == nullptr)
+    error (_("Unknown setting %s"), cmd);
+
+  if (set_cmd->var == nullptr)
+    error (_("Cannot use this setting with the \"with\" command"));
+
+  std::string temp_value
+    = (delim == nullptr ? args : std::string (args, delim - args));
+
+  if (nested_cmd == nullptr)
+    nested_cmd = skip_spaces (delim + 2);
+
+  std::string org_value;
+
+  switch (set_cmd->var_type)
+    {
+    case var_boolean:
+    case var_auto_boolean:
+    case var_integer:
+    case var_zinteger:
+    case var_zuinteger_unlimited:
+      org_value = std::to_string (*(int *) set_cmd->var);
+      break;
+    case var_uinteger:
+    case var_zuinteger:
+      org_value = std::to_string (*(unsigned int *) set_cmd->var);
+      break;
+    case var_string:
+    case var_string_noescape:
+    case var_filename:
+    case var_optional_filename:
+    case var_enum:
+      org_value = *(const char **) set_cmd->var;
+      break;
+    default:
+      gdb_assert_not_reached ("unhandled var_type");
+    }
+
+  /* Tweak the setting to the new temporary value.  */
+  do_set_command (temp_value.c_str (), from_tty, set_cmd);
+
+  try
+    {
+      scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
+
+      /* Execute the nested command.  */
+      execute_command (nested_cmd, from_tty);
+    }
+  catch (const gdb_exception &ex)
+    {
+      /* Restore the setting and rethrow.  If restoring the setting
+	 throws, swallow the new exception and warn.  There's nothing
+	 else we can reasonably do.  */
+      try
+	{
+	  do_set_command (org_value.c_str (), from_tty, set_cmd);
+	}
+      catch (...)
+	{
+	  warning (_("Couldn't restore setting"));
+	}
+
+      throw;
+    }
+
+  /* Restore the setting.  */
+  do_set_command (org_value.c_str (), from_tty, set_cmd);
+}
+
+/* See valprint.h.  */
+
+void
+with_command_completer (struct cmd_list_element *ignore,
+			completion_tracker &tracker,
+			const char *text, const char * /*word*/)
+{
+  tracker.set_use_custom_word_point (true);
+
+  const char *delim = strstr (text, "--");
+
+  /* If we're still not past the "--" delimiter, complete the "with"
+     command as if it was a "set" command.  */
+  if (delim == text
+      || delim == nullptr
+      || !isspace (delim[-1])
+      || !(isspace (delim[2]) || delim[2] == '\0'))
+    {
+      std::string new_text = std::string ("set ") + text;
+      tracker.advance_custom_word_point_by (-(int) strlen ("with"));
+      complete_nested_command_line (tracker, new_text.c_str ());
+      return;
+    }
+
+  /* We're past the "--" delimiter.  Complete on the sub command.  */
+  const char *nested_cmd = skip_spaces (delim + 2);
+  tracker.advance_custom_word_point_by (nested_cmd - text);
+  complete_nested_command_line (tracker, nested_cmd);
+}
+
+
 static void
 info_symbol_command (const char *arg, int from_tty)
 {
@@ -2750,6 +2870,18 @@  Like \"print\" but don't put in value history and don't print newline.\n\
 Usage: output EXP\n\
 This is useful in user-defined commands."));
 
+  c = add_com ("with", class_vars, with_command, _("\
+Temporarily set SETTING to VALUE, run COMMAND, and restore SETTING.\n\
+Usage: with SETTING [VALUE] [-- COMMAND]\n\
+Usage: w SETTING [VALUE] [-- COMMAND]\n\
+SETTING is any setting settable with the \"set\" command.\n\
+E.g.:\n\
+  with language pascal -- print obj\n\
+  with print elements unlimited -- print obj\n\
+\n"));
+  set_cmd_completer_handle_brkchars (c, with_command_completer);
+  add_com_alias ("w", "with", class_vars, 1);
+
   add_prefix_cmd ("set", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR\n\
 Usage: set VAR = EXP\n\
diff --git a/gdb/testsuite/gdb.base/with.c b/gdb/testsuite/gdb.base/with.c
new file mode 100644
index 00000000000..c6426625d4a
--- /dev/null
+++ b/gdb/testsuite/gdb.base/with.c
@@ -0,0 +1,41 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int xxx1 = 123;
+
+struct S
+{
+  int a;
+  int b;
+  int c;
+};
+
+struct S g_s = {1, 2, 3};
+
+static void
+inc ()
+{
+  g_s.a++;;
+}
+
+int
+main ()
+{
+  inc ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/with.exp b/gdb/testsuite/gdb.base/with.exp
new file mode 100644
index 00000000000..40f0a0f265f
--- /dev/null
+++ b/gdb/testsuite/gdb.base/with.exp
@@ -0,0 +1,168 @@ 
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# 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 the "with" command.
+
+load_lib completion-support.exp
+
+standard_testfile .c
+
+if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
+clean_restart $binfile
+
+# Basic/core tests.
+with_test_prefix "basics" {
+    gdb_test "print g_s" " = {a = 1, b = 2, c = 3}"
+    gdb_test "with print pretty -- print g_s" \
+	[multi_line  \
+	     " = {" \
+	     "  a = 1," \
+	     "  b = 2," \
+	     "  c = 3" \
+	     "}"]
+
+    # A boolean setting.
+    gdb_test "with non-stop on -- show non-stop" \
+	"Controlling the inferior in non-stop mode is on\\."
+    gdb_test "show non-stop" \
+	"Controlling the inferior in non-stop mode is off\\."
+
+    # Language.
+    gdb_test "with language pascal -- show language" \
+	"The current source language is \"pascal\"\\."
+
+    gdb_test "show language" \
+	"The current source language is \"auto; currently c\"\\."
+
+    gdb_test "with language ada -- print g_s" \
+	" = \\(a => 1, b => 2, c => 3\\)"
+
+    # Nested "with"s.
+    gdb_test "with language ada -- with language c -- print g_s" \
+	" = {a = 1, b = 2, c = 3}"
+}
+
+# Check a user defined command.
+with_test_prefix "user defined" {
+    # A user defined command.
+    set test "define usercmd"
+    gdb_test_multiple "define usercmd" $test {
+	-re "End with"  {
+	    gdb_test \
+		[multi_line_input \
+		     {print g_s} \
+		     {end}] \
+		"" \
+		$test
+	}
+    }
+    gdb_test "with language ada -- usercmd" \
+	" = \\(a => 1, b => 2, c => 3\\)"
+}
+
+# Check repeating.
+with_test_prefix "repeat" {
+
+    clean_restart $binfile
+
+    # "with" with no command reinvokes the previous command.
+    gdb_test "with language ada" \
+	"No previous command to relaunch" \
+	"reinvoke with no previous command to relaunch"
+
+    gdb_test "print g_s" " = {a = 1, b = 2, c = 3}"
+
+    gdb_test "with language ada" \
+	" = \\(a => 1, b => 2, c => 3\\)" \
+	"reinvoke with language"
+
+    # Same, but with "--".
+    gdb_test "with language fortran --" \
+	" = \\( a = 1, b = 2, c = 3 \\)" \
+	"reinvoke with language and --"
+
+    # Repeating repeats the original "print g_s", not the last "with"
+    # command.
+    set test "repeat command line"
+    send_gdb "\n"
+    gdb_test_multiple "" $test {
+	-re " = {a = 1, b = 2, c = 3}\r\n$gdb_prompt $" {
+	    pass $test
+	}
+    }
+}
+
+# Basic run control.
+with_test_prefix "run control" {
+    clean_restart $binfile
+
+    if ![runto_main] {
+	fail "cannot run to main"
+	return
+    }
+
+    # Check "with" with a synchronous execution command.
+    gdb_test "with disassemble-next-line on -- next" \
+	"return 0;.*=>.*"
+}
+
+# Check errors.
+with_test_prefix "errors" {
+    gdb_test "with variable xxx=1" \
+	"Cannot use this setting with the \"with\" command"
+
+    gdb_test "with print elements -- p 1" \
+	"Argument required \\(integer to set it to, or \"unlimited\"\\.\\)\\."
+
+    gdb_test "with -- p 1" \
+	"Missing setting before '--' delimiter"
+
+    # Check that the setting is restored even if the command throws.
+    gdb_test "with print elements 1 -- unknowncommand" \
+	"Undefined command: \"unknowncommand\"\\.  Try \"help\"\\."
+    gdb_test "show print elements" \
+	"Limit on string chars or array elements to print is 200\\."
+}
+
+# Check completion.
+with_test_prefix "completion" {
+    test_gdb_complete_unique \
+	"with pri" \
+	"with print"
+
+    test_gdb_complete_unique \
+	"with print ele" \
+	"with print elements"
+
+    test_gdb_complete_unique \
+	"with print elements u" \
+	"with print elements unlimited"
+
+    test_gdb_complete_none \
+	"with print elements unlimited "
+
+    test_gdb_completion_offers_commands "with print elements unlimited -- "
+
+    # Check that the completer nests into the nested command line's
+    # completer.
+    test_gdb_complete_unique \
+	"with print elements unlimited -- with print ele" \
+	"with print elements unlimited -- with print elements"
+}
diff --git a/gdb/top.c b/gdb/top.c
index b46de90f755..0c02bd1d721 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -739,7 +739,7 @@  dont_repeat (void)
 
 /* See command.h  */
 
-void
+const char *
 repeat_previous ()
 {
   /* Do not repeat this command, as this command is a repeating command.  */
@@ -749,6 +749,11 @@  repeat_previous ()
      so swap it with previous_saved_command_line.  */
   std::swap (previous_saved_command_line, saved_command_line);
   std::swap (previous_repeat_arguments, repeat_arguments);
+
+  const char *prev = skip_spaces (get_saved_command_line ());
+  if (*prev == '\0')
+    error (_("No previous command to relaunch"));
+  return prev;
 }
 
 /* See command.h.  */