aboutsummaryrefslogtreecommitdiff
path: root/gdb/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/ui.c')
-rw-r--r--gdb/ui.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/gdb/ui.c b/gdb/ui.c
new file mode 100644
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);
+}