[1/3] gdb: move struct ui and related things to ui.{c,h}

Message ID 20230428182713.216446-2-simon.marchi@efficios.com
State New
Headers
Series Some struct ui / struct interps cleanup |

Commit Message

Simon Marchi April 28, 2023, 6:27 p.m. UTC
  I'd like to move some things so they become methods on struct ui.  But
first, I think that struct ui and the related things are big enough to
deserve their own file, instead of being scattered through top.{c,h} and
event-top.c.

Change-Id: I15594269ace61fd76ef80a7b58f51ff3ab6979bc
---
 gdb/Makefile.in       |   2 +
 gdb/annotate.c        |   3 +-
 gdb/async-event.c     |   1 +
 gdb/breakpoint.c      |   1 +
 gdb/bt-utils.c        |   2 +-
 gdb/cli-out.c         |   2 +-
 gdb/cli/cli-cmds.c    |   1 +
 gdb/cli/cli-interp.c  |   1 +
 gdb/cli/cli-script.c  |   1 +
 gdb/compile/compile.c |   2 +-
 gdb/event-top.c       |  78 +------------
 gdb/exceptions.c      |   2 +-
 gdb/fork-child.c      |   2 +-
 gdb/guile/guile.c     |   1 +
 gdb/guile/scm-ports.c |   2 +-
 gdb/inf-loop.c        |   1 +
 gdb/infcall.c         |   1 +
 gdb/infcmd.c          |   2 +-
 gdb/infrun.c          |   1 +
 gdb/interps.c         |   2 +-
 gdb/main.c            |   1 +
 gdb/mi/mi-interp.c    |   2 +-
 gdb/mi/mi-main.c      |   1 +
 gdb/python/py-dap.c   |   2 +-
 gdb/python/python.c   |   1 +
 gdb/target.c          |   2 +-
 gdb/top.c             | 149 +------------------------
 gdb/top.h             | 200 +--------------------------------
 gdb/tui/tui-interp.c  |   2 +-
 gdb/tui/tui-io.c      |   1 +
 gdb/tui/tui.c         |   1 +
 gdb/ui.c              | 251 ++++++++++++++++++++++++++++++++++++++++++
 gdb/ui.h              | 218 ++++++++++++++++++++++++++++++++++++
 gdb/utils.c           |   1 +
 34 files changed, 512 insertions(+), 428 deletions(-)
 create mode 100644 gdb/ui.c
 create mode 100644 gdb/ui.h
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 404975418804..6af65357243a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1209,6 +1209,7 @@  COMMON_SFILES = \
 	target-float.c \
 	type-stack.c \
 	typeprint.c \
+	ui.c \
 	ui-file.c \
 	ui-out.c \
 	ui-style.c \
@@ -1496,6 +1497,7 @@  HFILES_NO_SRCDIR = \
 	tramp-frame.h \
 	type-stack.h \
 	typeprint.h \
+	ui.h \
 	ui-file.h \
 	ui-out.h \
 	ui-style.h \
diff --git a/gdb/annotate.c b/gdb/annotate.c
index 60fe6ccd5c27..d403a47ba2f2 100644
--- a/gdb/annotate.c
+++ b/gdb/annotate.c
@@ -25,11 +25,10 @@ 
 #include "observable.h"
 #include "inferior.h"
 #include "infrun.h"
-#include "top.h"
 #include "source.h"
 #include "objfiles.h"
 #include "source-cache.h"
-
+#include "ui.h"
 
 /* Prototypes for local functions.  */
 
diff --git a/gdb/async-event.c b/gdb/async-event.c
index a190e77f3294..a094f314aa3f 100644
--- a/gdb/async-event.c
+++ b/gdb/async-event.c
@@ -21,6 +21,7 @@ 
 
 #include "ser-event.h"
 #include "top.h"
+#include "ui.h"
 
 /* PROC is a function to be invoked when the READY flag is set.  This
    happens when there has been a signal and the corresponding signal
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 46287da5f87c..7466854d5106 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -53,6 +53,7 @@ 
 #include "memattr.h"
 #include "ada-lang.h"
 #include "top.h"
+#include "ui.h"
 #include "valprint.h"
 #include "jit.h"
 #include "parser-defs.h"
diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c
index 68c3f0816758..894403513324 100644
--- a/gdb/bt-utils.c
+++ b/gdb/bt-utils.c
@@ -19,7 +19,7 @@ 
 #include "bt-utils.h"
 #include "command.h"
 #include "gdbcmd.h"
-#include "top.h"
+#include "ui.h"
 #include "cli/cli-decode.h"
 
 /* See bt-utils.h.  */
diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index fdfd0f7f0cf4..4c598883d4ba 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -26,7 +26,7 @@ 
 #include "completer.h"
 #include "readline/readline.h"
 #include "cli/cli-style.h"
-#include "top.h"
+#include "ui.h"
 
 /* These are the CLI output functions */
 
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 3b1c6a9f4bd6..d466cc6c34d7 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -45,6 +45,7 @@ 
 #include "interps.h"
 
 #include "top.h"
+#include "ui.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-script.h"
 #include "cli/cli-setshow.h"
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 5a515c603c6e..84fe34a10148 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -24,6 +24,7 @@ 
 #include "ui-out.h"
 #include "cli-out.h"
 #include "top.h"		/* for "execute_command" */
+#include "ui.h"
 #include "infrun.h"
 #include "observable.h"
 #include "gdbthread.h"
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 294a5f18fe6c..2798faf82ec5 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -23,6 +23,7 @@ 
 
 #include "ui-out.h"
 #include "top.h"
+#include "ui.h"
 #include "breakpoint.h"
 #include "tracepoint.h"
 #include "cli/cli-cmds.h"
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index c07686c32e0b..a93c94046600 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -18,7 +18,7 @@ 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "top.h"
+#include "ui.h"
 #include "ui-out.h"
 #include "command.h"
 #include "cli/cli-script.h"
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 53ddd515be73..193ea5363ffc 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -21,6 +21,7 @@ 
 
 #include "defs.h"
 #include "top.h"
+#include "ui.h"
 #include "inferior.h"
 #include "infrun.h"
 #include "target.h"
@@ -480,12 +481,6 @@  top_level_prompt (void)
   return prompt;
 }
 
-/* See top.h.  */
-
-struct ui *main_ui;
-struct ui *current_ui;
-struct ui *ui_list;
-
 /* Get a reference to the current UI's line buffer.  This is used to
    construct a whole line of input from partial input.  */
 
@@ -495,77 +490,6 @@  get_command_line_buffer (void)
   return current_ui->line_buffer;
 }
 
-/* When there is an event ready on the stdin file descriptor, instead
-   of calling readline directly throught the callback function, or
-   instead of calling gdb_readline_no_editing_callback, give gdb a
-   chance to detect errors and do something.  */
-
-static void
-stdin_event_handler (int error, gdb_client_data client_data)
-{
-  struct ui *ui = (struct ui *) client_data;
-
-  if (error)
-    {
-      /* Switch to the main UI, so diagnostics always go there.  */
-      current_ui = main_ui;
-
-      ui->unregister_file_handler ();
-      if (main_ui == ui)
-	{
-	  /* If stdin died, we may as well kill gdb.  */
-	  gdb_printf (gdb_stderr, _("error detected on stdin\n"));
-	  quit_command ((char *) 0, 0);
-	}
-      else
-	{
-	  /* Simply delete the UI.  */
-	  delete ui;
-	}
-    }
-  else
-    {
-      /* Switch to the UI whose input descriptor woke up the event
-	 loop.  */
-      current_ui = ui;
-
-      /* This makes sure a ^C immediately followed by further input is
-	 always processed in that order.  E.g,. with input like
-	 "^Cprint 1\n", the SIGINT handler runs, marks the async
-	 signal handler, and then select/poll may return with stdin
-	 ready, instead of -1/EINTR.  The
-	 gdb.base/double-prompt-target-event-error.exp test exercises
-	 this.  */
-      QUIT;
-
-      do
-	{
-	  call_stdin_event_handler_again_p = 0;
-	  ui->call_readline (client_data);
-	}
-      while (call_stdin_event_handler_again_p != 0);
-    }
-}
-
-/* See top.h.  */
-
-void
-ui::register_file_handler ()
-{
-  if (input_fd != -1)
-    add_file_handler (input_fd, stdin_event_handler, this,
-		      string_printf ("ui-%d", num), true);
-}
-
-/* See top.h.  */
-
-void
-ui::unregister_file_handler ()
-{
-  if (input_fd != -1)
-    delete_file_handler (input_fd);
-}
-
 /* Re-enable stdin after the end of an execution command in
    synchronous mode, or after an error from the target, and we aborted
    the exec operation.  */
diff --git a/gdb/exceptions.c b/gdb/exceptions.c
index 8b7858578a90..2ba5a9c9fad1 100644
--- a/gdb/exceptions.c
+++ b/gdb/exceptions.c
@@ -26,7 +26,7 @@ 
 #include "ui-out.h"
 #include "serial.h"
 #include "gdbthread.h"
-#include "top.h"
+#include "ui.h"
 #include "gdbsupport/gdb_optional.h"
 
 static void
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index d210427f4b01..9e6f80a7953e 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -24,7 +24,7 @@ 
 #include "gdbcmd.h"
 #include "terminal.h"
 #include "gdbthread.h"
-#include "top.h"
+#include "ui.h"
 #include "gdbsupport/job-control.h"
 #include "gdbsupport/filestuff.h"
 #include "nat/fork-inferior.h"
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 887b7fa5dc8f..b45081fe1cc1 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -28,6 +28,7 @@ 
 #include "command.h"
 #include "gdbcmd.h"
 #include "top.h"
+#include "ui.h"
 #include "extension-priv.h"
 #include "utils.h"
 #include "gdbsupport/version.h"
diff --git a/gdb/guile/scm-ports.c b/gdb/guile/scm-ports.c
index 48b607592344..a27ea8b3c80b 100644
--- a/gdb/guile/scm-ports.c
+++ b/gdb/guile/scm-ports.c
@@ -23,7 +23,7 @@ 
 
 #include "defs.h"
 #include "gdbsupport/gdb_select.h"
-#include "top.h"
+#include "ui.h"
 #include "target.h"
 #include "guile-internal.h"
 #include "gdbsupport/gdb_optional.h"
diff --git a/gdb/inf-loop.c b/gdb/inf-loop.c
index b9f25008247c..8e7bcb67b3e6 100644
--- a/gdb/inf-loop.c
+++ b/gdb/inf-loop.c
@@ -28,6 +28,7 @@ 
 #include "gdbthread.h"
 #include "interps.h"
 #include "top.h"
+#include "ui.h"
 #include "observable.h"
 
 /* General function to handle events in the inferior.  */
diff --git a/gdb/infcall.c b/gdb/infcall.c
index e6cc6ed1a212..233ef5f29e9f 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -38,6 +38,7 @@ 
 #include "event-top.h"
 #include "observable.h"
 #include "top.h"
+#include "ui.h"
 #include "interps.h"
 #include "thread-fsm.h"
 #include <algorithm>
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 103899432f76..80107c946cc6 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -49,7 +49,7 @@ 
 #include "inf-loop.h"
 #include "linespec.h"
 #include "thread-fsm.h"
-#include "top.h"
+#include "ui.h"
 #include "interps.h"
 #include "skip.h"
 #include "gdbsupport/gdb_optional.h"
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2f1c6cd694be..efe2c00c489a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -34,6 +34,7 @@ 
 #include "annotate.h"
 #include "symfile.h"
 #include "top.h"
+#include "ui.h"
 #include "inf-loop.h"
 #include "regcache.h"
 #include "value.h"
diff --git a/gdb/interps.c b/gdb/interps.c
index ee451f27a639..a066c4acfedf 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -36,7 +36,7 @@ 
 #include "event-top.h"
 #include "interps.h"
 #include "completer.h"
-#include "top.h"		/* For command_loop.  */
+#include "ui.h"
 #include "main.h"
 #include "gdbsupport/buildargv.h"
 #include "gdbsupport/scope-exit.h"
diff --git a/gdb/main.c b/gdb/main.c
index 0bf6f98a797b..ac7cf8e3b580 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -19,6 +19,7 @@ 
 
 #include "defs.h"
 #include "top.h"
+#include "ui.h"
 #include "target.h"
 #include "inferior.h"
 #include "symfile.h"
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index ce78b0c2b58a..ad33a21374ab 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -27,7 +27,7 @@ 
 #include "inferior.h"
 #include "infrun.h"
 #include "ui-out.h"
-#include "top.h"
+#include "ui.h"
 #include "mi-main.h"
 #include "mi-cmds.h"
 #include "mi-out.h"
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 0013e5dfafdb..ee4ded0368ef 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -25,6 +25,7 @@ 
 #include "inferior.h"
 #include "infrun.h"
 #include "top.h"
+#include "ui.h"
 #include "gdbthread.h"
 #include "mi-cmds.h"
 #include "mi-parse.h"
diff --git a/gdb/python/py-dap.c b/gdb/python/py-dap.c
index 32f927214d75..52188406982b 100644
--- a/gdb/python/py-dap.c
+++ b/gdb/python/py-dap.c
@@ -21,7 +21,7 @@ 
 #include "python-internal.h"
 #include "interps.h"
 #include "cli-out.h"
-#include "top.h"
+#include "ui.h"
 
 class dap_interp final : public interp
 {
diff --git a/gdb/python/python.c b/gdb/python/python.c
index ea51766ec3e1..168a0009f1b2 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -64,6 +64,7 @@  static const char *gdbpy_should_print_stack = python_excp_message;
 #include "cli/cli-decode.h"
 #include "charset.h"
 #include "top.h"
+#include "ui.h"
 #include "python-internal.h"
 #include "linespec.h"
 #include "source.h"
diff --git a/gdb/target.c b/gdb/target.c
index 0cebecfafc37..80d2c80d4bfb 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -44,7 +44,7 @@ 
 #include "gdbsupport/agent.h"
 #include "auxv.h"
 #include "target-debug.h"
-#include "top.h"
+#include "ui.h"
 #include "event-top.h"
 #include <algorithm>
 #include "gdbsupport/byte-vector.h"
diff --git a/gdb/top.c b/gdb/top.c
index 81f74f72f610..0b819091d114 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -39,6 +39,7 @@ 
 #include "annotate.h"
 #include "completer.h"
 #include "top.h"
+#include "ui.h"
 #include "gdbsupport/version.h"
 #include "serial.h"
 #include "main.h"
@@ -253,13 +254,9 @@  void (*deprecated_call_command_hook) (struct cmd_list_element * c,
 
 void (*deprecated_context_hook) (int id);
 
-/* The highest UI number ever assigned.  */
-static int highest_ui_num;
-
-/* Unbuffer STREAM.  This is a wrapper around setbuf(STREAM, nullptr)
-   which applies some special rules for MS-Windows hosts.  */
+/* See top.h.  */
 
-static void
+void
 unbuffer_stream (FILE *stream)
 {
   /* Unbuffer the input stream so that in gdb_readline_no_editing_callback,
@@ -291,118 +288,6 @@  unbuffer_stream (FILE *stream)
 #endif
 }
 
-/* See top.h.  */
-
-ui::ui (FILE *instream_, FILE *outstream_, FILE *errstream_)
-  : num (++highest_ui_num),
-    stdin_stream (instream_),
-    instream (instream_),
-    outstream (outstream_),
-    errstream (errstream_),
-    input_fd (fileno (instream)),
-    m_input_interactive_p (ISATTY (instream)),
-    m_gdb_stdout (new pager_file (new stdio_file (outstream))),
-    m_gdb_stdin (new stdio_file (instream)),
-    m_gdb_stderr (new stderr_file (errstream)),
-    m_gdb_stdlog (new timestamped_file (m_gdb_stderr))
-{
-  unbuffer_stream (instream_);
-
-  if (ui_list == NULL)
-    ui_list = this;
-  else
-    {
-      struct ui *last;
-
-      for (last = ui_list; last->next != NULL; last = last->next)
-	;
-      last->next = this;
-    }
-}
-
-ui::~ui ()
-{
-  struct ui *ui, *uiprev;
-
-  uiprev = NULL;
-
-  for (ui = ui_list; ui != NULL; uiprev = ui, ui = ui->next)
-    if (ui == this)
-      break;
-
-  gdb_assert (ui != NULL);
-
-  if (uiprev != NULL)
-    uiprev->next = next;
-  else
-    ui_list = next;
-
-  delete m_gdb_stdin;
-  delete m_gdb_stdout;
-  delete m_gdb_stderr;
-}
-
-/* Open file named NAME for read/write, making sure not to make it the
-   controlling terminal.  */
-
-static gdb_file_up
-open_terminal_stream (const char *name)
-{
-  scoped_fd fd = gdb_open_cloexec (name, O_RDWR | O_NOCTTY, 0);
-  if (fd.get () < 0)
-    perror_with_name  (_("opening terminal failed"));
-
-  return fd.to_file ("w+");
-}
-
-/* Implementation of the "new-ui" command.  */
-
-static void
-new_ui_command (const char *args, int from_tty)
-{
-  int argc;
-  const char *interpreter_name;
-  const char *tty_name;
-
-  dont_repeat ();
-
-  gdb_argv argv (args);
-  argc = argv.count ();
-
-  if (argc < 2)
-    error (_("Usage: new-ui INTERPRETER TTY"));
-
-  interpreter_name = argv[0];
-  tty_name = argv[1];
-
-  {
-    scoped_restore save_ui = make_scoped_restore (&current_ui);
-
-    /* Open specified terminal.  Note: we used to open it three times,
-       once for each of stdin/stdout/stderr, but that does not work
-       with Windows named pipes.  */
-    gdb_file_up stream = open_terminal_stream (tty_name);
-
-    std::unique_ptr<ui> ui
-      (new struct ui (stream.get (), stream.get (), stream.get ()));
-
-    ui->async = 1;
-
-    current_ui = ui.get ();
-
-    set_top_level_interpreter (interpreter_name);
-
-    interp_pre_command_loop (top_level_interpreter ());
-
-    /* Make sure the file is not closed.  */
-    stream.release ();
-
-    ui.release ();
-  }
-
-  gdb_printf ("New UI allocated\n");
-}
-
 /* Handler for SIGHUP.  */
 
 #ifdef SIGHUP
@@ -1917,8 +1802,9 @@  quit_force (int *exit_arg, int from_tty)
   exit (exit_code);
 }
 
-/* The value of the "interactive-mode" setting.  */
-static enum auto_boolean interactive_mode = AUTO_BOOLEAN_AUTO;
+/* See top.h.  */
+
+auto_boolean interactive_mode = AUTO_BOOLEAN_AUTO;
 
 /* Implement the "show interactive-mode" option.  */
 
@@ -1935,20 +1821,6 @@  show_interactive_mode (struct ui_file *file, int from_tty,
     gdb_printf (file, "Debugger's interactive mode is %s.\n", value);
 }
 
-/* Returns whether GDB is running on an interactive terminal.  */
-
-bool
-ui::input_interactive_p () const
-{
-  if (batch_flag)
-    return false;
-
-  if (interactive_mode != AUTO_BOOLEAN_AUTO)
-    return interactive_mode == AUTO_BOOLEAN_TRUE;
-
-  return m_input_interactive_p;
-}
-
 static void
 dont_repeat_command (const char *ignored, int from_tty)
 {
@@ -2241,8 +2113,6 @@  show_startup_quiet (struct ui_file *file, int from_tty,
 static void
 init_main (void)
 {
-  struct cmd_list_element *c;
-
   /* Initialize the prompt to a simple "(gdb) " prompt or to whatever
      the DEFAULT_PROMPT is.  */
   set_prompt (DEFAULT_PROMPT);
@@ -2395,13 +2265,6 @@  affect future GDB sessions."),
 			       show_startup_quiet,
 			       &setlist, &showlist);
 
-  c = add_cmd ("new-ui", class_support, new_ui_command, _("\
-Create a new UI.\n\
-Usage: new-ui INTERPRETER TTY\n\
-The first argument is the name of the interpreter to run.\n\
-The second argument is the terminal the UI runs on."), &cmdlist);
-  set_cmd_completer (c, interpreter_completer);
-
   struct internalvar *major_version_var = create_internalvar ("_gdb_major");
   struct internalvar *minor_version_var = create_internalvar ("_gdb_minor");
   int vmajor = 0, vminor = 0, vrevision = 0;
diff --git a/gdb/top.h b/gdb/top.h
index 5c1ccfee736f..47e16ca104ea 100644
--- a/gdb/top.h
+++ b/gdb/top.h
@@ -24,203 +24,10 @@ 
 #include "gdbsupport/next-iterator.h"
 #include "value.h"
 
-/* Prompt state.  */
-
-enum prompt_state
-{
-  /* The command line is blocked simulating synchronous execution.
-     This is used to implement the foreground execution commands
-     ('run', 'continue', etc.).  We won't display the prompt and
-     accept further commands until the execution is actually over.  */
-  PROMPT_BLOCKED,
-
-  /* The command finished; display the prompt before returning back to
-     the top level.  */
-  PROMPT_NEEDED,
-
-  /* We've displayed the prompt already, ready for input.  */
-  PROMPTED,
-};
-
-/* All about a user interface instance.  Each user interface has its
-   own I/O files/streams, readline state, its own top level
-   interpreter (for the main UI, this is the interpreter specified
-   with -i on the command line) and secondary interpreters (for
-   interpreter-exec ...), etc.  There's always one UI associated with
-   stdin/stdout/stderr, but the user can create secondary UIs, for
-   example, to create a separate MI channel on its own stdio
-   streams.  */
-
-struct ui
-{
-  /* Create a new UI.  */
-  ui (FILE *instream, FILE *outstream, FILE *errstream);
-  ~ui ();
-
-  DISABLE_COPY_AND_ASSIGN (ui);
-
-  /* Pointer to next in singly-linked list.  */
-  struct ui *next = nullptr;
-
-  /* Convenient handle (UI number).  Unique across all UIs.  */
-  int num;
-
-  /* The UI's command line buffer.  This is to used to accumulate
-     input until we have a whole command line.  */
-  std::string line_buffer;
-
-  /* The callback used by the event loop whenever an event is detected
-     on the UI's input file descriptor.  This function incrementally
-     builds a buffer where it accumulates the line read up to the
-     point of invocation.  In the special case in which the character
-     read is newline, the function invokes the INPUT_HANDLER callback
-     (see below).  */
-  void (*call_readline) (gdb_client_data) = nullptr;
-
-  /* The function to invoke when a complete line of input is ready for
-     processing.  */
-  void (*input_handler) (gdb::unique_xmalloc_ptr<char> &&) = nullptr;
-
-  /* True if this UI is using the readline library for command
-     editing; false if using GDB's own simple readline emulation, with
-     no editing support.  */
-  int command_editing = 0;
-
-  /* Each UI has its own independent set of interpreters.  */
-  struct ui_interp_info *interp_info = nullptr;
-
-  /* True if the UI is in async mode, false if in sync mode.  If in
-     sync mode, a synchronous execution command (e.g, "next") does not
-     return until the command is finished.  If in async mode, then
-     running a synchronous command returns right after resuming the
-     target.  Waiting for the command's completion is later done on
-     the top event loop.  For the main UI, this starts out disabled,
-     until all the explicit command line arguments (e.g., `gdb -ex
-     "start" -ex "next"') are processed.  */
-  int async = 0;
-
-  /* The number of nested readline secondary prompts that are
-     currently active.  */
-  int secondary_prompt_depth = 0;
-
-  /* The UI's stdin.  Set to stdin for the main UI.  */
-  FILE *stdin_stream;
-
-  /* stdio stream that command input is being read from.  Set to stdin
-     normally.  Set by source_command to the file we are sourcing.
-     Set to NULL if we are executing a user-defined command or
-     interacting via a GUI.  */
-  FILE *instream;
-  /* Standard output stream.  */
-  FILE *outstream;
-  /* Standard error stream.  */
-  FILE *errstream;
-
-  /* The file descriptor for the input stream, so that we can register
-     it with the event loop.  This can be set to -1 to prevent this
-     registration.  */
-  int input_fd;
-
-  /* Whether ISATTY returns true on input_fd.  Cached here because
-     quit_force needs to know this _after_ input_fd might be
-     closed.  */
-  bool m_input_interactive_p;
-
-  /* See enum prompt_state's description.  */
-  enum prompt_state prompt_state = PROMPT_NEEDED;
-
-  /* The fields below that start with "m_" are "private".  They're
-     meant to be accessed through wrapper macros that make them look
-     like globals.  */
-
-  /* The ui_file streams.  */
-  /* Normal results */
-  struct ui_file *m_gdb_stdout;
-  /* Input stream */
-  struct ui_file *m_gdb_stdin;
-  /* Serious error notifications */
-  struct ui_file *m_gdb_stderr;
-  /* Log/debug/trace messages that should bypass normal stdout/stderr
-     filtering.  */
-  struct ui_file *m_gdb_stdlog;
-
-  /* The current ui_out.  */
-  struct ui_out *m_current_uiout = nullptr;
-
-  /* Register the UI's input file descriptor in the event loop.  */
-  void register_file_handler ();
-
-  /* Unregister the UI's input file descriptor from the event loop.  */
-  void unregister_file_handler ();
-
-  /* Return true if this UI's input fd is a tty.  */
-  bool input_interactive_p () const;
-};
-
-/* The main UI.  This is the UI that is bound to stdin/stdout/stderr.
-   It always exists and is created automatically when GDB starts
-   up.  */
-extern struct ui *main_ui;
-
-/* The current UI.  */
-extern struct ui *current_ui;
-
-/* The list of all UIs.  */
-extern struct ui *ui_list;
-
-/* State for SWITCH_THRU_ALL_UIS.  */
-class switch_thru_all_uis
-{
-public:
-
-  switch_thru_all_uis () : m_iter (ui_list), m_save_ui (&current_ui)
-  {
-    current_ui = ui_list;
-  }
-
-  DISABLE_COPY_AND_ASSIGN (switch_thru_all_uis);
-
-  /* If done iterating, return true; otherwise return false.  */
-  bool done () const
-  {
-    return m_iter == NULL;
-  }
-
-  /* Move to the next UI, setting current_ui if iteration is not yet
-     complete.  */
-  void next ()
-  {
-    m_iter = m_iter->next;
-    if (m_iter != NULL)
-      current_ui = m_iter;
-  }
-
- private:
-
-  /* Used to iterate through the UIs.  */
-  struct ui *m_iter;
-
-  /* Save and restore current_ui.  */
-  scoped_restore_tmpl<struct ui *> m_save_ui;
-};
-
-  /* Traverse through all UI, and switch the current UI to the one
-     being iterated.  */
-#define SWITCH_THRU_ALL_UIS()		\
-  for (switch_thru_all_uis stau_state; !stau_state.done (); stau_state.next ())
-
-using ui_range = next_range<ui>;
-
-/* An adapter that can be used to traverse over all UIs.  */
-static inline
-ui_range all_uis ()
-{
-  return ui_range (ui_list);
-}
-
 /* From top.c.  */
 extern bool confirm;
 extern int inhibit_gdbinit;
+extern auto_boolean interactive_mode;
 
 /* Print the GDB version banner to STREAM.  If INTERACTIVE is false,
    then information referring to commands (e.g., "show configuration")
@@ -297,4 +104,9 @@  extern const char *handle_line_of_input (std::string &cmd_line_buffer,
 
 extern bool check_quiet_mode ();
 
+/* Unbuffer STREAM.  This is a wrapper around setbuf(STREAM, nullptr)
+   which applies some special rules for MS-Windows hosts.  */
+
+extern void unbuffer_stream (FILE *stream);
+
 #endif
diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 812c62c64220..299cc4caea09 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -20,7 +20,7 @@ 
 #include "defs.h"
 #include "cli/cli-interp.h"
 #include "interps.h"
-#include "top.h"
+#include "ui.h"
 #include "event-top.h"
 #include "gdbsupport/event-loop.h"
 #include "ui-out.h"
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index 7752701378e7..a1eadcd937d2 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -25,6 +25,7 @@ 
 #include "event-top.h"
 #include "command.h"
 #include "top.h"
+#include "ui.h"
 #include "tui/tui.h"
 #include "tui/tui-data.h"
 #include "tui/tui-io.h"
diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
index 3604194a760c..10cf811a41e9 100644
--- a/gdb/tui/tui.c
+++ b/gdb/tui/tui.c
@@ -41,6 +41,7 @@ 
 #include "source.h"
 #include "terminal.h"
 #include "top.h"
+#include "ui.h"
 
 #include <ctype.h>
 #include <signal.h>
diff --git a/gdb/ui.c b/gdb/ui.c
new file mode 100644
index 000000000000..5fe001262a83
--- /dev/null
+++ b/gdb/ui.c
@@ -0,0 +1,251 @@ 
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "ui.h"
+
+#include "cli/cli-cmds.h"
+#include "event-top.h"
+#include "gdbsupport/buildargv.h"
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/gdb_file.h"
+#include "gdbsupport/scoped_fd.h"
+#include "interps.h"
+#include "pager.h"
+#include "main.h"
+#include "top.h"
+
+/* See top.h.  */
+
+struct ui *main_ui;
+struct ui *current_ui;
+struct ui *ui_list;
+
+/* The highest UI number ever assigned.  */
+
+static int highest_ui_num;
+
+/* See top.h.  */
+
+ui::ui (FILE *instream_, FILE *outstream_, FILE *errstream_)
+  : num (++highest_ui_num),
+    stdin_stream (instream_),
+    instream (instream_),
+    outstream (outstream_),
+    errstream (errstream_),
+    input_fd (fileno (instream)),
+    m_input_interactive_p (ISATTY (instream)),
+    m_gdb_stdout (new pager_file (new stdio_file (outstream))),
+    m_gdb_stdin (new stdio_file (instream)),
+    m_gdb_stderr (new stderr_file (errstream)),
+    m_gdb_stdlog (new timestamped_file (m_gdb_stderr))
+{
+  unbuffer_stream (instream_);
+
+  if (ui_list == NULL)
+    ui_list = this;
+  else
+    {
+      struct ui *last;
+
+      for (last = ui_list; last->next != NULL; last = last->next)
+	;
+      last->next = this;
+    }
+}
+
+ui::~ui ()
+{
+  struct ui *ui, *uiprev;
+
+  uiprev = NULL;
+
+  for (ui = ui_list; ui != NULL; uiprev = ui, ui = ui->next)
+    if (ui == this)
+      break;
+
+  gdb_assert (ui != NULL);
+
+  if (uiprev != NULL)
+    uiprev->next = next;
+  else
+    ui_list = next;
+
+  delete m_gdb_stdin;
+  delete m_gdb_stdout;
+  delete m_gdb_stderr;
+}
+
+
+/* Returns whether GDB is running on an interactive terminal.  */
+
+bool
+ui::input_interactive_p () const
+{
+  if (batch_flag)
+    return false;
+
+  if (interactive_mode != AUTO_BOOLEAN_AUTO)
+    return interactive_mode == AUTO_BOOLEAN_TRUE;
+
+  return m_input_interactive_p;
+}
+
+
+/* When there is an event ready on the stdin file descriptor, instead
+   of calling readline directly throught the callback function, or
+   instead of calling gdb_readline_no_editing_callback, give gdb a
+   chance to detect errors and do something.  */
+
+static void
+stdin_event_handler (int error, gdb_client_data client_data)
+{
+  struct ui *ui = (struct ui *) client_data;
+
+  if (error)
+    {
+      /* Switch to the main UI, so diagnostics always go there.  */
+      current_ui = main_ui;
+
+      ui->unregister_file_handler ();
+      if (main_ui == ui)
+	{
+	  /* If stdin died, we may as well kill gdb.  */
+	  gdb_printf (gdb_stderr, _("error detected on stdin\n"));
+	  quit_command ((char *) 0, 0);
+	}
+      else
+	{
+	  /* Simply delete the UI.  */
+	  delete ui;
+	}
+    }
+  else
+    {
+      /* Switch to the UI whose input descriptor woke up the event
+	 loop.  */
+      current_ui = ui;
+
+      /* This makes sure a ^C immediately followed by further input is
+	 always processed in that order.  E.g,. with input like
+	 "^Cprint 1\n", the SIGINT handler runs, marks the async
+	 signal handler, and then select/poll may return with stdin
+	 ready, instead of -1/EINTR.  The
+	 gdb.base/double-prompt-target-event-error.exp test exercises
+	 this.  */
+      QUIT;
+
+      do
+	{
+	  call_stdin_event_handler_again_p = 0;
+	  ui->call_readline (client_data);
+	}
+      while (call_stdin_event_handler_again_p != 0);
+    }
+}
+
+/* See top.h.  */
+
+void
+ui::register_file_handler ()
+{
+  if (input_fd != -1)
+    add_file_handler (input_fd, stdin_event_handler, this,
+		      string_printf ("ui-%d", num), true);
+}
+
+/* See top.h.  */
+
+void
+ui::unregister_file_handler ()
+{
+  if (input_fd != -1)
+    delete_file_handler (input_fd);
+}
+
+/* Open file named NAME for read/write, making sure not to make it the
+   controlling terminal.  */
+
+static gdb_file_up
+open_terminal_stream (const char *name)
+{
+  scoped_fd fd = gdb_open_cloexec (name, O_RDWR | O_NOCTTY, 0);
+  if (fd.get () < 0)
+    perror_with_name  (_("opening terminal failed"));
+
+  return fd.to_file ("w+");
+}
+
+/* Implementation of the "new-ui" command.  */
+
+static void
+new_ui_command (const char *args, int from_tty)
+{
+  int argc;
+  const char *interpreter_name;
+  const char *tty_name;
+
+  dont_repeat ();
+
+  gdb_argv argv (args);
+  argc = argv.count ();
+
+  if (argc < 2)
+    error (_("Usage: new-ui INTERPRETER TTY"));
+
+  interpreter_name = argv[0];
+  tty_name = argv[1];
+
+  {
+    scoped_restore save_ui = make_scoped_restore (&current_ui);
+
+    /* Open specified terminal.  Note: we used to open it three times,
+       once for each of stdin/stdout/stderr, but that does not work
+       with Windows named pipes.  */
+    gdb_file_up stream = open_terminal_stream (tty_name);
+
+    std::unique_ptr<ui> ui
+      (new struct ui (stream.get (), stream.get (), stream.get ()));
+
+    ui->async = 1;
+
+    current_ui = ui.get ();
+
+    set_top_level_interpreter (interpreter_name);
+
+    interp_pre_command_loop (top_level_interpreter ());
+
+    /* Make sure the file is not closed.  */
+    stream.release ();
+
+    ui.release ();
+  }
+
+  gdb_printf ("New UI allocated\n");
+}
+
+void _initialize_ui ();
+void
+_initialize_ui ()
+{
+  cmd_list_element *c = add_cmd ("new-ui", class_support, new_ui_command, _("\
+Create a new UI.\n\
+Usage: new-ui INTERPRETER TTY\n\
+The first argument is the name of the interpreter to run.\n\
+The second argument is the terminal the UI runs on."), &cmdlist);
+  set_cmd_completer (c, interpreter_completer);
+}
diff --git a/gdb/ui.h b/gdb/ui.h
new file mode 100644
index 000000000000..8da4b2d8eeed
--- /dev/null
+++ b/gdb/ui.h
@@ -0,0 +1,218 @@ 
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef UI_H
+#define UI_H
+
+#include "gdbsupport/event-loop.h"
+#include "gdbsupport/next-iterator.h"
+
+/* Prompt state.  */
+
+enum prompt_state
+{
+  /* The command line is blocked simulating synchronous execution.
+     This is used to implement the foreground execution commands
+     ('run', 'continue', etc.).  We won't display the prompt and
+     accept further commands until the execution is actually over.  */
+  PROMPT_BLOCKED,
+
+  /* The command finished; display the prompt before returning back to
+     the top level.  */
+  PROMPT_NEEDED,
+
+  /* We've displayed the prompt already, ready for input.  */
+  PROMPTED,
+};
+
+/* All about a user interface instance.  Each user interface has its
+   own I/O files/streams, readline state, its own top level
+   interpreter (for the main UI, this is the interpreter specified
+   with -i on the command line) and secondary interpreters (for
+   interpreter-exec ...), etc.  There's always one UI associated with
+   stdin/stdout/stderr, but the user can create secondary UIs, for
+   example, to create a separate MI channel on its own stdio
+   streams.  */
+
+struct ui
+{
+  /* Create a new UI.  */
+  ui (FILE *instream, FILE *outstream, FILE *errstream);
+  ~ui ();
+
+  DISABLE_COPY_AND_ASSIGN (ui);
+
+  /* Pointer to next in singly-linked list.  */
+  struct ui *next = nullptr;
+
+  /* Convenient handle (UI number).  Unique across all UIs.  */
+  int num;
+
+  /* The UI's command line buffer.  This is to used to accumulate
+     input until we have a whole command line.  */
+  std::string line_buffer;
+
+  /* The callback used by the event loop whenever an event is detected
+     on the UI's input file descriptor.  This function incrementally
+     builds a buffer where it accumulates the line read up to the
+     point of invocation.  In the special case in which the character
+     read is newline, the function invokes the INPUT_HANDLER callback
+     (see below).  */
+  void (*call_readline) (gdb_client_data) = nullptr;
+
+  /* The function to invoke when a complete line of input is ready for
+     processing.  */
+  void (*input_handler) (gdb::unique_xmalloc_ptr<char> &&) = nullptr;
+
+  /* True if this UI is using the readline library for command
+     editing; false if using GDB's own simple readline emulation, with
+     no editing support.  */
+  int command_editing = 0;
+
+  /* Each UI has its own independent set of interpreters.  */
+  struct ui_interp_info *interp_info = nullptr;
+
+  /* True if the UI is in async mode, false if in sync mode.  If in
+     sync mode, a synchronous execution command (e.g, "next") does not
+     return until the command is finished.  If in async mode, then
+     running a synchronous command returns right after resuming the
+     target.  Waiting for the command's completion is later done on
+     the top event loop.  For the main UI, this starts out disabled,
+     until all the explicit command line arguments (e.g., `gdb -ex
+     "start" -ex "next"') are processed.  */
+  int async = 0;
+
+  /* The number of nested readline secondary prompts that are
+     currently active.  */
+  int secondary_prompt_depth = 0;
+
+  /* The UI's stdin.  Set to stdin for the main UI.  */
+  FILE *stdin_stream;
+
+  /* stdio stream that command input is being read from.  Set to stdin
+     normally.  Set by source_command to the file we are sourcing.
+     Set to NULL if we are executing a user-defined command or
+     interacting via a GUI.  */
+  FILE *instream;
+  /* Standard output stream.  */
+  FILE *outstream;
+  /* Standard error stream.  */
+  FILE *errstream;
+
+  /* The file descriptor for the input stream, so that we can register
+     it with the event loop.  This can be set to -1 to prevent this
+     registration.  */
+  int input_fd;
+
+  /* Whether ISATTY returns true on input_fd.  Cached here because
+     quit_force needs to know this _after_ input_fd might be
+     closed.  */
+  bool m_input_interactive_p;
+
+  /* See enum prompt_state's description.  */
+  enum prompt_state prompt_state = PROMPT_NEEDED;
+
+  /* The fields below that start with "m_" are "private".  They're
+     meant to be accessed through wrapper macros that make them look
+     like globals.  */
+
+  /* The ui_file streams.  */
+  /* Normal results */
+  struct ui_file *m_gdb_stdout;
+  /* Input stream */
+  struct ui_file *m_gdb_stdin;
+  /* Serious error notifications */
+  struct ui_file *m_gdb_stderr;
+  /* Log/debug/trace messages that should bypass normal stdout/stderr
+     filtering.  */
+  struct ui_file *m_gdb_stdlog;
+
+  /* The current ui_out.  */
+  struct ui_out *m_current_uiout = nullptr;
+
+  /* Register the UI's input file descriptor in the event loop.  */
+  void register_file_handler ();
+
+  /* Unregister the UI's input file descriptor from the event loop.  */
+  void unregister_file_handler ();
+
+  /* Return true if this UI's input fd is a tty.  */
+  bool input_interactive_p () const;
+};
+
+/* The main UI.  This is the UI that is bound to stdin/stdout/stderr.
+   It always exists and is created automatically when GDB starts
+   up.  */
+extern struct ui *main_ui;
+
+/* The current UI.  */
+extern struct ui *current_ui;
+
+/* The list of all UIs.  */
+extern struct ui *ui_list;
+
+/* State for SWITCH_THRU_ALL_UIS.  */
+class switch_thru_all_uis
+{
+public:
+
+  switch_thru_all_uis () : m_iter (ui_list), m_save_ui (&current_ui)
+  {
+    current_ui = ui_list;
+  }
+
+  DISABLE_COPY_AND_ASSIGN (switch_thru_all_uis);
+
+  /* If done iterating, return true; otherwise return false.  */
+  bool done () const
+  {
+    return m_iter == NULL;
+  }
+
+  /* Move to the next UI, setting current_ui if iteration is not yet
+     complete.  */
+  void next ()
+  {
+    m_iter = m_iter->next;
+    if (m_iter != NULL)
+      current_ui = m_iter;
+  }
+
+ private:
+
+  /* Used to iterate through the UIs.  */
+  struct ui *m_iter;
+
+  /* Save and restore current_ui.  */
+  scoped_restore_tmpl<struct ui *> m_save_ui;
+};
+
+  /* Traverse through all UI, and switch the current UI to the one
+     being iterated.  */
+#define SWITCH_THRU_ALL_UIS()		\
+  for (switch_thru_all_uis stau_state; !stau_state.done (); stau_state.next ())
+
+using ui_range = next_range<ui>;
+
+/* An adapter that can be used to traverse over all UIs.  */
+static inline
+ui_range all_uis ()
+{
+  return ui_range (ui_list);
+}
+
+#endif /* UI_H */
diff --git a/gdb/utils.c b/gdb/utils.c
index b5bb84ce85db..bf1475285988 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -52,6 +52,7 @@ 
 #include "gdbsupport/gdb_obstack.h"
 #include "gdbcore.h"
 #include "top.h"
+#include "ui.h"
 #include "main.h"
 #include "solist.h"