| /* Copyright (C) 2023-2024 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 "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 (¤t_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, true); |
| |
| top_level_interpreter ()->pre_command_loop (); |
| |
| /* 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); |
| } |