]>
Commit | Line | Data |
---|---|---|
1d506c26 | 1 | /* Copyright (C) 2023-2024 Free Software Foundation, Inc. |
13d03262 SM |
2 | |
3 | This file is part of GDB. | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #include "defs.h" | |
19 | #include "ui.h" | |
20 | ||
21 | #include "cli/cli-cmds.h" | |
22 | #include "event-top.h" | |
23 | #include "gdbsupport/buildargv.h" | |
24 | #include "gdbsupport/filestuff.h" | |
25 | #include "gdbsupport/gdb_file.h" | |
26 | #include "gdbsupport/scoped_fd.h" | |
27 | #include "interps.h" | |
28 | #include "pager.h" | |
29 | #include "main.h" | |
30 | #include "top.h" | |
31 | ||
32 | /* See top.h. */ | |
33 | ||
34 | struct ui *main_ui; | |
35 | struct ui *current_ui; | |
36 | struct ui *ui_list; | |
37 | ||
38 | /* The highest UI number ever assigned. */ | |
39 | ||
40 | static int highest_ui_num; | |
41 | ||
42 | /* See top.h. */ | |
43 | ||
44 | ui::ui (FILE *instream_, FILE *outstream_, FILE *errstream_) | |
45 | : num (++highest_ui_num), | |
46 | stdin_stream (instream_), | |
47 | instream (instream_), | |
48 | outstream (outstream_), | |
49 | errstream (errstream_), | |
50 | input_fd (fileno (instream)), | |
51 | m_input_interactive_p (ISATTY (instream)), | |
52 | m_gdb_stdout (new pager_file (new stdio_file (outstream))), | |
53 | m_gdb_stdin (new stdio_file (instream)), | |
54 | m_gdb_stderr (new stderr_file (errstream)), | |
55 | m_gdb_stdlog (new timestamped_file (m_gdb_stderr)) | |
56 | { | |
57 | unbuffer_stream (instream_); | |
58 | ||
59 | if (ui_list == NULL) | |
60 | ui_list = this; | |
61 | else | |
62 | { | |
63 | struct ui *last; | |
64 | ||
65 | for (last = ui_list; last->next != NULL; last = last->next) | |
66 | ; | |
67 | last->next = this; | |
68 | } | |
69 | } | |
70 | ||
71 | ui::~ui () | |
72 | { | |
73 | struct ui *ui, *uiprev; | |
74 | ||
75 | uiprev = NULL; | |
76 | ||
77 | for (ui = ui_list; ui != NULL; uiprev = ui, ui = ui->next) | |
78 | if (ui == this) | |
79 | break; | |
80 | ||
81 | gdb_assert (ui != NULL); | |
82 | ||
83 | if (uiprev != NULL) | |
84 | uiprev->next = next; | |
85 | else | |
86 | ui_list = next; | |
87 | ||
88 | delete m_gdb_stdin; | |
89 | delete m_gdb_stdout; | |
90 | delete m_gdb_stderr; | |
91 | } | |
92 | ||
93 | ||
94 | /* Returns whether GDB is running on an interactive terminal. */ | |
95 | ||
96 | bool | |
97 | ui::input_interactive_p () const | |
98 | { | |
99 | if (batch_flag) | |
100 | return false; | |
101 | ||
102 | if (interactive_mode != AUTO_BOOLEAN_AUTO) | |
103 | return interactive_mode == AUTO_BOOLEAN_TRUE; | |
104 | ||
105 | return m_input_interactive_p; | |
106 | } | |
107 | ||
108 | ||
109 | /* When there is an event ready on the stdin file descriptor, instead | |
110 | of calling readline directly throught the callback function, or | |
111 | instead of calling gdb_readline_no_editing_callback, give gdb a | |
112 | chance to detect errors and do something. */ | |
113 | ||
114 | static void | |
115 | stdin_event_handler (int error, gdb_client_data client_data) | |
116 | { | |
117 | struct ui *ui = (struct ui *) client_data; | |
118 | ||
119 | if (error) | |
120 | { | |
121 | /* Switch to the main UI, so diagnostics always go there. */ | |
122 | current_ui = main_ui; | |
123 | ||
124 | ui->unregister_file_handler (); | |
125 | if (main_ui == ui) | |
126 | { | |
127 | /* If stdin died, we may as well kill gdb. */ | |
128 | gdb_printf (gdb_stderr, _("error detected on stdin\n")); | |
129 | quit_command ((char *) 0, 0); | |
130 | } | |
131 | else | |
132 | { | |
133 | /* Simply delete the UI. */ | |
134 | delete ui; | |
135 | } | |
136 | } | |
137 | else | |
138 | { | |
139 | /* Switch to the UI whose input descriptor woke up the event | |
140 | loop. */ | |
141 | current_ui = ui; | |
142 | ||
143 | /* This makes sure a ^C immediately followed by further input is | |
144 | always processed in that order. E.g,. with input like | |
145 | "^Cprint 1\n", the SIGINT handler runs, marks the async | |
146 | signal handler, and then select/poll may return with stdin | |
147 | ready, instead of -1/EINTR. The | |
148 | gdb.base/double-prompt-target-event-error.exp test exercises | |
149 | this. */ | |
150 | QUIT; | |
151 | ||
152 | do | |
153 | { | |
154 | call_stdin_event_handler_again_p = 0; | |
155 | ui->call_readline (client_data); | |
156 | } | |
157 | while (call_stdin_event_handler_again_p != 0); | |
158 | } | |
159 | } | |
160 | ||
161 | /* See top.h. */ | |
162 | ||
163 | void | |
164 | ui::register_file_handler () | |
165 | { | |
166 | if (input_fd != -1) | |
167 | add_file_handler (input_fd, stdin_event_handler, this, | |
168 | string_printf ("ui-%d", num), true); | |
169 | } | |
170 | ||
171 | /* See top.h. */ | |
172 | ||
173 | void | |
174 | ui::unregister_file_handler () | |
175 | { | |
176 | if (input_fd != -1) | |
177 | delete_file_handler (input_fd); | |
178 | } | |
179 | ||
180 | /* Open file named NAME for read/write, making sure not to make it the | |
181 | controlling terminal. */ | |
182 | ||
183 | static gdb_file_up | |
184 | open_terminal_stream (const char *name) | |
185 | { | |
186 | scoped_fd fd = gdb_open_cloexec (name, O_RDWR | O_NOCTTY, 0); | |
187 | if (fd.get () < 0) | |
188 | perror_with_name (_("opening terminal failed")); | |
189 | ||
190 | return fd.to_file ("w+"); | |
191 | } | |
192 | ||
193 | /* Implementation of the "new-ui" command. */ | |
194 | ||
195 | static void | |
196 | new_ui_command (const char *args, int from_tty) | |
197 | { | |
198 | int argc; | |
199 | const char *interpreter_name; | |
200 | const char *tty_name; | |
201 | ||
202 | dont_repeat (); | |
203 | ||
204 | gdb_argv argv (args); | |
205 | argc = argv.count (); | |
206 | ||
207 | if (argc < 2) | |
208 | error (_("Usage: new-ui INTERPRETER TTY")); | |
209 | ||
210 | interpreter_name = argv[0]; | |
211 | tty_name = argv[1]; | |
212 | ||
213 | { | |
214 | scoped_restore save_ui = make_scoped_restore (¤t_ui); | |
215 | ||
216 | /* Open specified terminal. Note: we used to open it three times, | |
217 | once for each of stdin/stdout/stderr, but that does not work | |
218 | with Windows named pipes. */ | |
219 | gdb_file_up stream = open_terminal_stream (tty_name); | |
220 | ||
221 | std::unique_ptr<ui> ui | |
222 | (new struct ui (stream.get (), stream.get (), stream.get ())); | |
223 | ||
224 | ui->async = 1; | |
225 | ||
226 | current_ui = ui.get (); | |
227 | ||
228 | set_top_level_interpreter (interpreter_name); | |
229 | ||
bec941b3 | 230 | top_level_interpreter ()->pre_command_loop (); |
13d03262 SM |
231 | |
232 | /* Make sure the file is not closed. */ | |
233 | stream.release (); | |
234 | ||
235 | ui.release (); | |
236 | } | |
237 | ||
238 | gdb_printf ("New UI allocated\n"); | |
239 | } | |
240 | ||
241 | void _initialize_ui (); | |
242 | void | |
243 | _initialize_ui () | |
244 | { | |
245 | cmd_list_element *c = add_cmd ("new-ui", class_support, new_ui_command, _("\ | |
246 | Create a new UI.\n\ | |
247 | Usage: new-ui INTERPRETER TTY\n\ | |
248 | The first argument is the name of the interpreter to run.\n\ | |
249 | The second argument is the terminal the UI runs on."), &cmdlist); | |
250 | set_cmd_completer (c, interpreter_completer); | |
251 | } |