Automatic Copyright Year update after running gdb/copyright.py
[deliverable/binutils-gdb.git] / gdb / cli / cli-interp.c
index 88a570cc8343bd5f36cf164b3d532d4eee65ec90..56c42882e7662ddbc8140b0c5b3cf6e32745d4d0 100644 (file)
@@ -1,7 +1,6 @@
 /* CLI Definitions for GDB, the GNU debugger.
 
-   Copyright (c) 2002, 2003, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 2002-2022 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "cli-interp.h"
 #include "interps.h"
-#include "wrapper.h"
 #include "event-top.h"
 #include "ui-out.h"
 #include "cli-out.h"
 #include "top.h"               /* for "execute_command" */
-#include "gdb_string.h"
-#include "exceptions.h"
+#include "infrun.h"
+#include "observable.h"
+#include "gdbthread.h"
+#include "thread-fsm.h"
+#include "inferior.h"
 
-struct ui_out *cli_uiout;
+cli_interp_base::cli_interp_base (const char *name)
+  : interp (name)
+{}
 
-/* These are the ui_out and the interpreter for the console
-   interpreter.  */
+cli_interp_base::~cli_interp_base ()
+{}
+
+/* The console interpreter.  */
+
+class cli_interp final : public cli_interp_base
+{
+ public:
+  explicit cli_interp (const char *name);
+  ~cli_interp ();
+
+  void init (bool top_level) override;
+  void resume () override;
+  void suspend () override;
+  gdb_exception exec (const char *command_str) override;
+  ui_out *interp_ui_out () override;
+
+  /* The ui_out for the console interpreter.  */
+  cli_ui_out *cli_uiout;
+};
+
+cli_interp::cli_interp (const char *name)
+  : cli_interp_base (name)
+{
+  /* Create a default uiout builder for the CLI.  */
+  this->cli_uiout = cli_out_new (gdb_stdout);
+}
+
+cli_interp::~cli_interp ()
+{
+  delete cli_uiout;
+}
+
+/* Suppress notification struct.  */
+struct cli_suppress_notification cli_suppress_notification =
+  {
+    0   /* user_selected_context_changed */
+  };
+
+/* Returns the INTERP's data cast as cli_interp if INTERP is a CLI,
+   and returns NULL otherwise.  */
+
+static struct cli_interp *
+as_cli_interp (struct interp *interp)
+{
+  return dynamic_cast<cli_interp *> (interp);
+}
 
 /* Longjmp-safe wrapper for "execute_command".  */
 static struct gdb_exception safe_execute_command (struct ui_out *uiout,
-                                                 char *command, 
+                                                 const char *command, 
                                                  int from_tty);
+
+/* See cli-interp.h.
+
+   Breakpoint hits should always be mirrored to a console.  Deciding
+   what to mirror to a console wrt to breakpoints and random stops
+   gets messy real fast.  E.g., say "s" trips on a breakpoint.  We'd
+   clearly want to mirror the event to the console in this case.  But
+   what about more complicated cases like "s&; thread n; s&", and one
+   of those steps spawning a new thread, and that thread hitting a
+   breakpoint?  It's impossible in general to track whether the thread
+   had any relation to the commands that had been executed.  So we
+   just simplify and always mirror breakpoints and random events to
+   all consoles.
+
+   OTOH, we should print the source line to the console when stepping
+   or other similar commands, iff the step was started by that console
+   (or in MI's case, by a console command), but not if it was started
+   with MI's -exec-step or similar.  */
+
+int
+should_print_stop_to_console (struct interp *console_interp,
+                             struct thread_info *tp)
+{
+  if ((bpstat_what (tp->control.stop_bpstat).main_action
+       == BPSTAT_WHAT_STOP_NOISY)
+      || tp->thread_fsm == NULL
+      || tp->thread_fsm->command_interp == console_interp
+      || !tp->thread_fsm->finished_p ())
+    return 1;
+  return 0;
+}
+
+/* Observers for several run control events.  If the interpreter is
+   quiet (i.e., another interpreter is being run with
+   interpreter-exec), print nothing.  */
+
+/* Observer for the normal_stop notification.  */
+
+static void
+cli_on_normal_stop (struct bpstats *bs, int print_frame)
+{
+  if (!print_frame)
+    return;
+
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct interp *interp = top_level_interpreter ();
+      struct cli_interp *cli = as_cli_interp (interp);
+      struct thread_info *thread;
+
+      if (cli == NULL)
+       continue;
+
+      thread = inferior_thread ();
+      if (should_print_stop_to_console (interp, thread))
+       print_stop_event (cli->cli_uiout);
+    }
+}
+
+/* Observer for the signal_received notification.  */
+
+static void
+cli_on_signal_received (enum gdb_signal siggnal)
+{
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      print_signal_received_reason (cli->cli_uiout, siggnal);
+    }
+}
+
+/* Observer for the end_stepping_range notification.  */
+
+static void
+cli_on_end_stepping_range (void)
+{
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      print_end_stepping_range_reason (cli->cli_uiout);
+    }
+}
+
+/* Observer for the signalled notification.  */
+
+static void
+cli_on_signal_exited (enum gdb_signal siggnal)
+{
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      print_signal_exited_reason (cli->cli_uiout, siggnal);
+    }
+}
+
+/* Observer for the exited notification.  */
+
+static void
+cli_on_exited (int exitstatus)
+{
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      print_exited_reason (cli->cli_uiout, exitstatus);
+    }
+}
+
+/* Observer for the no_history notification.  */
+
+static void
+cli_on_no_history (void)
+{
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      print_no_history_reason (cli->cli_uiout);
+    }
+}
+
+/* Observer for the sync_execution_done notification.  */
+
+static void
+cli_on_sync_execution_done (void)
+{
+  struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+  if (cli == NULL)
+    return;
+
+  display_gdb_prompt (NULL);
+}
+
+/* Observer for the command_error notification.  */
+
+static void
+cli_on_command_error (void)
+{
+  struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+  if (cli == NULL)
+    return;
+
+  display_gdb_prompt (NULL);
+}
+
+/* Observer for the user_selected_context_changed notification.  */
+
+static void
+cli_on_user_selected_context_changed (user_selected_what selection)
+{
+  /* This event is suppressed.  */
+  if (cli_suppress_notification.user_selected_context)
+    return;
+
+  thread_info *tp = inferior_ptid != null_ptid ? inferior_thread () : NULL;
+
+  SWITCH_THRU_ALL_UIS ()
+    {
+      struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+      if (cli == NULL)
+       continue;
+
+      if (selection & USER_SELECTED_INFERIOR)
+       print_selected_inferior (cli->cli_uiout);
+
+      if (tp != NULL
+         && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
+       print_selected_thread_frame (cli->cli_uiout, selection);
+    }
+}
+
+/* pre_command_loop implementation.  */
+
+void
+cli_interp_base::pre_command_loop ()
+{
+  display_gdb_prompt (0);
+}
+
 /* These implement the cli out interpreter: */
 
-static void *
-cli_interpreter_init (int top_level)
+void
+cli_interp::init (bool top_level)
 {
-  return NULL;
 }
 
-static int
-cli_interpreter_resume (void *data)
+void
+cli_interp::resume ()
 {
+  struct ui *ui = current_ui;
+  struct cli_interp *cli = this;
   struct ui_file *stream;
 
   /*sync_execution = 1; */
@@ -56,48 +306,34 @@ cli_interpreter_resume (void *data)
      previously writing to gdb_stdout, then set it to the new
      gdb_stdout afterwards.  */
 
-  stream = cli_out_set_stream (cli_uiout, gdb_stdout);
+  stream = cli->cli_uiout->set_stream (gdb_stdout);
   if (stream != gdb_stdout)
     {
-      cli_out_set_stream (cli_uiout, stream);
+      cli->cli_uiout->set_stream (stream);
       stream = NULL;
     }
 
-  gdb_setup_readline ();
+  gdb_setup_readline (1);
 
-  if (stream != NULL)
-    cli_out_set_stream (cli_uiout, gdb_stdout);
+  ui->input_handler = command_line_handler;
 
-  return 1;
+  if (stream != NULL)
+    cli->cli_uiout->set_stream (gdb_stdout);
 }
 
-static int
-cli_interpreter_suspend (void *data)
+void
+cli_interp::suspend ()
 {
   gdb_disable_readline ();
-  return 1;
-}
-
-/* Don't display the prompt if we are set quiet.  */
-static int
-cli_interpreter_display_prompt_p (void *data)
-{
-  if (interp_quiet_p (NULL))
-    return 0;
-  else
-    return 1;
 }
 
-static struct gdb_exception
-cli_interpreter_exec (void *data, const char *command_str)
+gdb_exception
+cli_interp::exec (const char *command_str)
 {
+  struct cli_interp *cli = this;
   struct ui_file *old_stream;
   struct gdb_exception result;
 
-  /* FIXME: cagney/2003-02-01: Need to const char *propogate
-     safe_execute_command.  */
-  char *str = strcpy (alloca (strlen (command_str) + 1), command_str);
-
   /* gdb_stdout could change between the time cli_uiout was
      initialized and now.  Since we're probably using a different
      interpreter which has a new ui_file for gdb_stdout, use that one
@@ -105,46 +341,157 @@ cli_interpreter_exec (void *data, const char *command_str)
 
      It is important that it gets reset everytime, since the user
      could set gdb to use a different interpreter.  */
-  old_stream = cli_out_set_stream (cli_uiout, gdb_stdout);
-  result = safe_execute_command (cli_uiout, str, 1);
-  cli_out_set_stream (cli_uiout, old_stream);
+  old_stream = cli->cli_uiout->set_stream (gdb_stdout);
+  result = safe_execute_command (cli->cli_uiout, command_str, 1);
+  cli->cli_uiout->set_stream (old_stream);
   return result;
 }
 
+bool
+cli_interp_base::supports_command_editing ()
+{
+  return true;
+}
+
 static struct gdb_exception
-safe_execute_command (struct ui_out *uiout, char *command, int from_tty)
+safe_execute_command (struct ui_out *command_uiout, const char *command,
+                     int from_tty)
 {
-  volatile struct gdb_exception e;
+  struct gdb_exception e;
 
-  TRY_CATCH (e, RETURN_MASK_ALL)
+  /* Save and override the global ``struct ui_out'' builder.  */
+  scoped_restore saved_uiout = make_scoped_restore (&current_uiout,
+                                                   command_uiout);
+
+  try
     {
       execute_command (command, from_tty);
     }
+  catch (gdb_exception &exception)
+    {
+      e = std::move (exception);
+    }
+
   /* FIXME: cagney/2005-01-13: This shouldn't be needed.  Instead the
      caller should print the exception.  */
   exception_print (gdb_stderr, e);
   return e;
 }
 
+ui_out *
+cli_interp::interp_ui_out ()
+{
+  struct cli_interp *cli = (struct cli_interp *) this;
 
-/* Standard gdb initialization hook.  */
-extern initialize_file_ftype _initialize_cli_interp; /* -Wmissing-prototypes */
+  return cli->cli_uiout;
+}
+
+/* These hold the pushed copies of the gdb output files.
+   If NULL then nothing has yet been pushed.  */
+struct saved_output_files
+{
+  ui_file *out;
+  ui_file *err;
+  ui_file *log;
+  ui_file *targ;
+  ui_file *targerr;
+  ui_file *file_to_delete;
+};
+static saved_output_files saved_output;
+
+/* See cli-interp.h.  */
 
 void
-_initialize_cli_interp (void)
-{
-  static const struct interp_procs procs = {
-    cli_interpreter_init,      /* init_proc */
-    cli_interpreter_resume,    /* resume_proc */
-    cli_interpreter_suspend,   /* suspend_proc */
-    cli_interpreter_exec,      /* exec_proc */
-    cli_interpreter_display_prompt_p   /* prompt_proc_p */
-  };
-  struct interp *cli_interp;
+cli_interp_base::set_logging (ui_file_up logfile, bool logging_redirect,
+                             bool debug_redirect)
+{
+  if (logfile != nullptr)
+    {
+      saved_output.out = gdb_stdout;
+      saved_output.err = gdb_stderr;
+      saved_output.log = gdb_stdlog;
+      saved_output.targ = gdb_stdtarg;
+      saved_output.targerr = gdb_stdtargerr;
 
-  /* Create a default uiout builder for the CLI.  */
-  cli_uiout = cli_out_new (gdb_stdout);
-  cli_interp = interp_new (INTERP_CONSOLE, NULL, cli_uiout, &procs);
+      /* If something is being redirected, then grab logfile.  */
+      ui_file *logfile_p = nullptr;
+      if (logging_redirect || debug_redirect)
+       {
+         logfile_p = logfile.get ();
+         saved_output.file_to_delete = logfile_p;
+       }
+
+      /* If something is not being redirected, then a tee containing both the
+        logfile and stdout.  */
+      ui_file *tee = nullptr;
+      if (!logging_redirect || !debug_redirect)
+       {
+         tee = new tee_file (gdb_stdout, std::move (logfile));
+         saved_output.file_to_delete = tee;
+       }
+
+      /* Make sure that the call to logfile's dtor does not delete the
+         underlying pointer if we still keep a reference to it.  If
+         logfile_p is not referenced as the file_to_delete, then either
+         the logfile is not used (no redirection) and it should be
+         deleted, or a tee took ownership of the pointer. */
+      if (logfile_p != nullptr && saved_output.file_to_delete == logfile_p)
+       logfile.release ();
+
+      gdb_stdout = logging_redirect ? logfile_p : tee;
+      gdb_stdlog = debug_redirect ? logfile_p : tee;
+      gdb_stderr = logging_redirect ? logfile_p : tee;
+      gdb_stdtarg = logging_redirect ? logfile_p : tee;
+      gdb_stdtargerr = logging_redirect ? logfile_p : tee;
+    }
+  else
+    {
+      /* Delete the correct file.  If it's the tee then the logfile will also
+        be deleted.  */
+      delete saved_output.file_to_delete;
+
+      gdb_stdout = saved_output.out;
+      gdb_stderr = saved_output.err;
+      gdb_stdlog = saved_output.log;
+      gdb_stdtarg = saved_output.targ;
+      gdb_stdtargerr = saved_output.targerr;
+
+      saved_output.out = nullptr;
+      saved_output.err = nullptr;
+      saved_output.log = nullptr;
+      saved_output.targ = nullptr;
+      saved_output.targerr = nullptr;
+      saved_output.file_to_delete = nullptr;
+    }
+}
+
+/* Factory for CLI interpreters.  */
+
+static struct interp *
+cli_interp_factory (const char *name)
+{
+  return new cli_interp (name);
+}
+
+/* Standard gdb initialization hook.  */
+
+void _initialize_cli_interp ();
+void
+_initialize_cli_interp ()
+{
+  interp_factory_register (INTERP_CONSOLE, cli_interp_factory);
 
-  interp_add (cli_interp);
+  /* If changing this, remember to update tui-interp.c as well.  */
+  gdb::observers::normal_stop.attach (cli_on_normal_stop, "cli-interp");
+  gdb::observers::end_stepping_range.attach (cli_on_end_stepping_range,
+                                            "cli-interp");
+  gdb::observers::signal_received.attach (cli_on_signal_received, "cli-interp");
+  gdb::observers::signal_exited.attach (cli_on_signal_exited, "cli-interp");
+  gdb::observers::exited.attach (cli_on_exited, "cli-interp");
+  gdb::observers::no_history.attach (cli_on_no_history, "cli-interp");
+  gdb::observers::sync_execution_done.attach (cli_on_sync_execution_done,
+                                             "cli-interp");
+  gdb::observers::command_error.attach (cli_on_command_error, "cli-interp");
+  gdb::observers::user_selected_context_changed.attach
+    (cli_on_user_selected_context_changed, "cli-interp");
 }
This page took 0.02909 seconds and 4 git commands to generate.