gdb: move struct ui and related things to ui.{c,h}
authorSimon Marchi <simon.marchi@efficios.com>
Fri, 28 Apr 2023 18:27:11 +0000 (14:27 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Mon, 1 May 2023 19:40:54 +0000 (15:40 -0400)
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

34 files changed:
gdb/Makefile.in
gdb/annotate.c
gdb/async-event.c
gdb/breakpoint.c
gdb/bt-utils.c
gdb/cli-out.c
gdb/cli/cli-cmds.c
gdb/cli/cli-interp.c
gdb/cli/cli-script.c
gdb/compile/compile.c
gdb/event-top.c
gdb/exceptions.c
gdb/fork-child.c
gdb/guile/guile.c
gdb/guile/scm-ports.c
gdb/inf-loop.c
gdb/infcall.c
gdb/infcmd.c
gdb/infrun.c
gdb/interps.c
gdb/main.c
gdb/mi/mi-interp.c
gdb/mi/mi-main.c
gdb/python/py-dap.c
gdb/python/python.c
gdb/target.c
gdb/top.c
gdb/top.h
gdb/tui/tui-interp.c
gdb/tui/tui-io.c
gdb/tui/tui.c
gdb/ui.c [new file with mode: 0644]
gdb/ui.h [new file with mode: 0644]
gdb/utils.c

index 404975418804c4463cf178df1fef7631dcc90362..6af65357243a440630235f2f1bbc613f93908918 100644 (file)
@@ -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 \
index 60fe6ccd5c27d6fc0c1515341a6b28c5ae2cc667..d403a47ba2f23efad0d629a15fa6f6965afde404 100644 (file)
 #include "observable.h"
 #include "inferior.h"
 #include "infrun.h"
-#include "top.h"
 #include "source.h"
 #include "objfiles.h"
 #include "source-cache.h"
-\f
+#include "ui.h"
 
 /* Prototypes for local functions.  */
 
index a190e77f329451b6b227730fb0a397197c7dd783..a094f314aa3f9004689d160eba5ecde086d9ff2d 100644 (file)
@@ -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
index 20e016e8cb343c551257839d30816cea2125d6b3..547750009191e3335081cfb4f5953f6339637486 100644 (file)
@@ -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"
index 68c3f081675816631eb22a7a24cfd9b29abc5232..89440351332448f3b988af0e47e99d5b0135a72f 100644 (file)
@@ -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.  */
index fdfd0f7f0cf4ecbaf71aa625e6b845b55162f452..4c598883d4bae05a964f3335fb99bd01c8ef0e8e 100644 (file)
@@ -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 */
 
index 3b1c6a9f4bd634764ee1df0362b05618b2a853db..d466cc6c34d72fece830e82f09a081dedfd14553 100644 (file)
@@ -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"
index 5a515c603c6e632e2b32234df3d25d4dc19844bb..84fe34a101485ad9f5f076de30c7586ee395d7aa 100644 (file)
@@ -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"
index b96dd74330ca247eff3e1839d9228577e6224757..8ec5689ebcfdda09f592cd8172a39a69d3c4e921 100644 (file)
@@ -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"
index c07686c32e0b585713ac05449affa8e458469667..a93c9404660038af71d3a582ad6dbb098db42227 100644 (file)
@@ -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"
index 53ddd515be736dfec4b78adcd029837f9861776c..193ea5363ffc429d86aa6b5768a32ab57d6e7647 100644 (file)
@@ -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.  */
index 8b7858578a90571031083e1bd9818082d980ae37..2ba5a9c9fad1574334e558c18483c9876db09ec8 100644 (file)
@@ -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
index d210427f4b01c3068a2b98cc6f9b44692f769b22..9e6f80a7953efca77f5bfec3c5d1001b45ab831c 100644 (file)
@@ -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"
index 887b7fa5dc8fb7afc05c6ef395effdce9196e368..b45081fe1cc1f6d5d6b57fff2fa4486f4bcac289 100644 (file)
@@ -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"
index 48b607592344525c801c0f0c76be5f9ce7223fa1..a27ea8b3c80b6219298ff0cbfcdb7c95d135f2c2 100644 (file)
@@ -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"
index b9f25008247c55552c296faf9fbceeacccf6fb10..8e7bcb67b3e6bbffc44c753b3aa4f2c5365899b2 100644 (file)
@@ -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.  */
index e6cc6ed1a212f924534b86318fc12b9a22354340..233ef5f29e9ffb295792f453e5ac7e9bf54705b2 100644 (file)
@@ -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>
index dd3675e37c5fa32c201d506fcc26717406b78e70..b8134665f3fe0c99ee75be4bd795fab101a490d5 100644 (file)
@@ -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"
index 2f1c6cd694be9c499d02c236e6d878852dbb5cb3..efe2c00c489a60c98f323dee92ae97f2598c6f22 100644 (file)
@@ -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"
index ee451f27a639e409b67b9d92eda2d447b5623998..a066c4acfedf5af9309127c11702c8e4c3826d0d 100644 (file)
@@ -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"
index 9fb6dceb9ca25737f8d36d32145ac960e4b1c0fb..5c23714f52e0e77986bdd808715d62e2f827ab32 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "top.h"
+#include "ui.h"
 #include "target.h"
 #include "inferior.h"
 #include "symfile.h"
index ce78b0c2b58ad802af699c2bc1b2da4a2d872c59..ad33a21374abe3092f46c7586a1ce1b685e8577f 100644 (file)
@@ -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"
index 19cdf47a28354470456584fb800173a38159f6c6..35c74c407ee8967d9cffe3f936934fede6a20b61 100644 (file)
@@ -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"
index 32f927214d753ea214e966decd65ca87e2205669..52188406982bc7ad79260f1d53c0ed074a31d36c 100644 (file)
@@ -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
 {
index ea51766ec3e1a168203e368a81e850a2cb8d2116..168a0009f1b2ab6c1cdbe87ee9eb4d7e0df7520b 100644 (file)
@@ -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"
index 0cebecfafc379534180a6563d246cdbc3488311e..80d2c80d4bfb8306671716054d79bbcc3fa4afaa 100644 (file)
@@ -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"
index 81f74f72f6108f0aff8b9d61385aeb3d2fba1fc8..0b819091d114732d5df9a3693b7de6310718753e 100644 (file)
--- 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;
-}
-\f
 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;
index 5c1ccfee736f11eccfadac23011a59e9f0ae8012..47e16ca104ea0173aa102d7a89435baa2176a4c0 100644 (file)
--- a/gdb/top.h
+++ b/gdb/top.h
 #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
index 812c62c64220f4ddad67551b130d9cc3db1785cd..299cc4caea098892b9829d8de7eeb4fe061ae7d9 100644 (file)
@@ -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"
index 7752701378e7a2eca7e83b6c261f6a7a4416be59..a1eadcd937d2fad066f56304e729c1c577969a61 100644 (file)
@@ -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"
index 3604194a760c25dd25332e287c0371b927861b5d..10cf811a41e9ee5807cf97459e056730f7d2fad3 100644 (file)
@@ -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 (file)
index 0000000..5fe0012
--- /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 (file)
index 0000000..8da4b2d
--- /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 */
index e10198accd0d574e070b3ca622ed24027a126842..3cd2287a52ea4f101d068b3a29a4c182adca66ef 100644 (file)
@@ -52,6 +52,7 @@
 #include "gdbsupport/gdb_obstack.h"
 #include "gdbcore.h"
 #include "top.h"
+#include "ui.h"
 #include "main.h"
 #include "solist.h"