]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/ui-file.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / gdb / ui-file.c
CommitLineData
d9fcf2fb 1/* UI_FILE - a generic STDIO like output stream.
349c5d5f 2
d01e8234 3 Copyright (C) 1999-2025 Free Software Foundation, Inc.
d9fcf2fb
JM
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
a9762ec7 9 the Free Software Foundation; either version 3 of the License, or
d9fcf2fb
JM
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
a9762ec7 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
d9fcf2fb 19
581e13c1 20/* Implement the ``struct ui_file'' object. */
d9fcf2fb 21
d9fcf2fb 22#include "ui-file.h"
bf31fd38 23#include "gdbsupport/gdb_obstack.h"
06cc9596 24#include "gdbsupport/gdb_select.h"
268a13a5 25#include "gdbsupport/filestuff.h"
3cd52293 26#include "cli-out.h"
f64eea3a 27#include "cli/cli-style.h"
3c6c449e 28#include <chrono>
d9fcf2fb 29
d7e74731 30null_file null_stream;
d9fcf2fb 31
d7e74731
PA
32ui_file::ui_file ()
33{}
d9fcf2fb 34
d7e74731
PA
35ui_file::~ui_file ()
36{}
d9fcf2fb 37
d7e74731
PA
38void
39ui_file::printf (const char *format, ...)
d9fcf2fb 40{
d7e74731 41 va_list args;
d9fcf2fb 42
d7e74731 43 va_start (args, format);
19a7b8ab 44 vprintf (format, args);
d7e74731 45 va_end (args);
d9fcf2fb
JM
46}
47
d7e74731
PA
48void
49ui_file::putstr (const char *str, int quoter)
d9fcf2fb 50{
d53fd721
TT
51 while (*str)
52 printchar (*str++, quoter, false);
d9fcf2fb
JM
53}
54
d7e74731 55void
d53fd721 56ui_file::putstrn (const char *str, int n, int quoter, bool async_safe)
d9fcf2fb 57{
d53fd721
TT
58 for (int i = 0; i < n; i++)
59 printchar (str[i], quoter, async_safe);
d9fcf2fb
JM
60}
61
4311246b 62void
d7e74731 63ui_file::putc (int c)
449092f6 64{
a11ac3b3
TT
65 char copy = (char) c;
66 write (&copy, 1);
449092f6
CV
67}
68
d7e74731
PA
69void
70ui_file::vprintf (const char *format, va_list args)
d9fcf2fb 71{
3cd52293
TT
72 ui_out_flags flags = disallow_ui_out_field;
73 cli_ui_out (this, flags).vmessage (m_applied_style, format, args);
d9fcf2fb
JM
74}
75
d53fd721
TT
76/* See ui-file.h. */
77
c8d74a7b
TT
78void
79ui_file::emit_style_escape (const ui_file_style &style)
80{
81 if (can_emit_style_escape () && style != m_applied_style)
82 {
83 m_applied_style = style;
84 this->puts (style.to_ansi ().c_str ());
85 }
86}
87
88/* See ui-file.h. */
89
d53fd721
TT
90void
91ui_file::printchar (int c, int quoter, bool async_safe)
92{
93 char buf[4];
94 int out = 0;
95
96 c &= 0xFF; /* Avoid sign bit follies */
97
98 if (c < 0x20 /* Low control chars */
99 || (c >= 0x7F && c < 0xA0) /* DEL, High controls */
100 || (sevenbit_strings && c >= 0x80))
101 { /* high order bit set */
102 buf[out++] = '\\';
103
104 switch (c)
105 {
106 case '\n':
107 buf[out++] = 'n';
108 break;
109 case '\b':
110 buf[out++] = 'b';
111 break;
112 case '\t':
113 buf[out++] = 't';
114 break;
115 case '\f':
116 buf[out++] = 'f';
117 break;
118 case '\r':
119 buf[out++] = 'r';
120 break;
121 case '\033':
122 buf[out++] = 'e';
123 break;
124 case '\007':
125 buf[out++] = 'a';
126 break;
127 default:
128 {
129 buf[out++] = '0' + ((c >> 6) & 0x7);
130 buf[out++] = '0' + ((c >> 3) & 0x7);
131 buf[out++] = '0' + ((c >> 0) & 0x7);
132 break;
133 }
134 }
135 }
136 else
137 {
138 if (quoter != 0 && (c == '\\' || c == quoter))
139 buf[out++] = '\\';
140 buf[out++] = c;
141 }
142
143 if (async_safe)
144 this->write_async_safe (buf, out);
145 else
146 this->write (buf, out);
147}
148
d7e74731 149\f
01124a23 150
d7e74731
PA
151void
152null_file::write (const char *buf, long sizeof_buf)
d9fcf2fb 153{
d7e74731 154 /* Discard the request. */
d9fcf2fb
JM
155}
156
d7e74731
PA
157void
158null_file::puts (const char *)
2a9d5ccf 159{
d7e74731 160 /* Discard the request. */
2a9d5ccf
HZ
161}
162
d7e74731
PA
163void
164null_file::write_async_safe (const char *buf, long sizeof_buf)
d9fcf2fb 165{
d7e74731 166 /* Discard the request. */
d9fcf2fb
JM
167}
168
d7e74731
PA
169\f
170
d7e74731
PA
171string_file::~string_file ()
172{}
d9fcf2fb
JM
173
174void
d7e74731 175string_file::write (const char *buf, long length_buf)
d9fcf2fb 176{
d7e74731 177 m_string.append (buf, length_buf);
d9fcf2fb
JM
178}
179
8a522c6c
PW
180/* See ui-file.h. */
181
182bool
183string_file::term_out ()
184{
185 return m_term_out;
186}
187
188/* See ui-file.h. */
189
190bool
191string_file::can_emit_style_escape ()
192{
193 return m_term_out && term_cli_styling ();
194}
195
d7e74731 196\f
01124a23 197
d7e74731 198stdio_file::stdio_file (FILE *file, bool close_p)
449092f6 199{
d7e74731
PA
200 set_stream (file);
201 m_close_p = close_p;
449092f6
CV
202}
203
d7e74731
PA
204stdio_file::stdio_file ()
205 : m_file (NULL),
206 m_fd (-1),
207 m_close_p (false)
208{}
d9fcf2fb 209
d7e74731 210stdio_file::~stdio_file ()
2a9d5ccf 211{
d7e74731
PA
212 if (m_close_p)
213 fclose (m_file);
2a9d5ccf
HZ
214}
215
d9fcf2fb 216void
d7e74731 217stdio_file::set_stream (FILE *file)
d9fcf2fb 218{
d7e74731
PA
219 m_file = file;
220 m_fd = fileno (file);
d9fcf2fb
JM
221}
222
d7e74731
PA
223bool
224stdio_file::open (const char *name, const char *mode)
d9fcf2fb 225{
d7e74731
PA
226 /* Close the previous stream, if we own it. */
227 if (m_close_p)
228 {
229 fclose (m_file);
230 m_close_p = false;
231 }
5d502164 232
d419f42d 233 gdb_file_up f = gdb_fopen_cloexec (name, mode);
d9fcf2fb 234
d7e74731
PA
235 if (f == NULL)
236 return false;
d9fcf2fb 237
d419f42d 238 set_stream (f.release ());
d7e74731 239 m_close_p = true;
5d502164 240
d7e74731 241 return true;
d9fcf2fb
JM
242}
243
244void
d7e74731 245stdio_file::flush ()
d9fcf2fb 246{
d7e74731 247 fflush (m_file);
d9fcf2fb
JM
248}
249
d7e74731
PA
250long
251stdio_file::read (char *buf, long length_buf)
449092f6 252{
f0881b37
PA
253 /* Wait until at least one byte of data is available, or we get
254 interrupted with Control-C. */
ad960ed2 255 {
ad960ed2 256 fd_set readfds;
f0881b37 257
ad960ed2 258 FD_ZERO (&readfds);
d7e74731
PA
259 FD_SET (m_fd, &readfds);
260 if (interruptible_select (m_fd + 1, &readfds, NULL, NULL, NULL) == -1)
ad960ed2
DJ
261 return -1;
262 }
263
d7e74731 264 return ::read (m_fd, buf, length_buf);
449092f6
CV
265}
266
d7e74731
PA
267void
268stdio_file::write (const char *buf, long length_buf)
d9fcf2fb 269{
bf1d7d9c 270 /* Calling error crashes when we are called from the exception framework. */
d7e74731 271 if (fwrite (buf, length_buf, 1, m_file))
d4fb63e1
TT
272 {
273 /* Nothing. */
274 }
d9fcf2fb
JM
275}
276
d7e74731
PA
277void
278stdio_file::write_async_safe (const char *buf, long length_buf)
01124a23 279{
9f7bc587
DE
280 /* This is written the way it is to avoid a warning from gcc about not using the
281 result of write (since it can be declared with attribute warn_unused_result).
282 Alas casting to void doesn't work for this. */
d7e74731 283 if (::write (m_fd, buf, length_buf))
d4fb63e1
TT
284 {
285 /* Nothing. */
286 }
01124a23
DE
287}
288
d7e74731
PA
289void
290stdio_file::puts (const char *linebuffer)
d9fcf2fb 291{
e4adb939
EZ
292 /* This host-dependent function (with implementations in
293 posix-hdep.c and mingw-hdep.c) is given the opportunity to
294 process the output first in host-dependent way. If it does, it
295 should return non-zero, to avoid calling fputs below. */
296 if (gdb_console_fputs (linebuffer, m_file))
297 return;
bf1d7d9c 298 /* Calling error crashes when we are called from the exception framework. */
d7e74731 299 if (fputs (linebuffer, m_file))
d4fb63e1
TT
300 {
301 /* Nothing. */
302 }
d9fcf2fb
JM
303}
304
d7e74731
PA
305bool
306stdio_file::isatty ()
d9fcf2fb 307{
d7e74731 308 return ::isatty (m_fd);
d9fcf2fb
JM
309}
310
8a522c6c
PW
311/* See ui-file.h. */
312
313bool
314stdio_file::can_emit_style_escape ()
315{
3cd52293 316 return (this->isatty ()
8a522c6c
PW
317 && term_cli_styling ());
318}
319
d7e74731 320\f
2a9d5ccf 321
d7e74731 322/* This is the implementation of ui_file method 'write' for stderr.
ffa4ac95
YQ
323 gdb_stdout is flushed before writing to gdb_stderr. */
324
d7e74731
PA
325void
326stderr_file::write (const char *buf, long length_buf)
ffa4ac95 327{
da5bd37e 328 gdb_stdout->flush ();
d7e74731 329 stdio_file::write (buf, length_buf);
ffa4ac95
YQ
330}
331
d7e74731 332/* This is the implementation of ui_file method 'puts' for stderr.
ffa4ac95
YQ
333 gdb_stdout is flushed before writing to gdb_stderr. */
334
d7e74731
PA
335void
336stderr_file::puts (const char *linebuffer)
ffa4ac95 337{
da5bd37e 338 gdb_stdout->flush ();
d7e74731 339 stdio_file::puts (linebuffer);
ffa4ac95 340}
ffa4ac95 341
d7e74731
PA
342stderr_file::stderr_file (FILE *stream)
343 : stdio_file (stream)
344{}
d9fcf2fb 345
d7e74731 346\f
e4c242d9 347
2b141965 348tee_file::tee_file (ui_file *one, ui_file *two)
d7e74731 349 : m_one (one),
2b141965 350 m_two (two)
d7e74731 351{}
e4c242d9 352
d7e74731 353tee_file::~tee_file ()
e4c242d9 354{
e4c242d9
DJ
355}
356
d7e74731
PA
357void
358tee_file::flush ()
e4c242d9 359{
d7e74731
PA
360 m_one->flush ();
361 m_two->flush ();
e4c242d9
DJ
362}
363
d7e74731
PA
364void
365tee_file::write (const char *buf, long length_buf)
e4c242d9 366{
d7e74731
PA
367 m_one->write (buf, length_buf);
368 m_two->write (buf, length_buf);
e4c242d9
DJ
369}
370
d7e74731
PA
371void
372tee_file::write_async_safe (const char *buf, long length_buf)
e4c242d9 373{
d7e74731
PA
374 m_one->write_async_safe (buf, length_buf);
375 m_two->write_async_safe (buf, length_buf);
e4c242d9
DJ
376}
377
d7e74731
PA
378void
379tee_file::puts (const char *linebuffer)
e4c242d9 380{
d7e74731
PA
381 m_one->puts (linebuffer);
382 m_two->puts (linebuffer);
e4c242d9
DJ
383}
384
d7e74731
PA
385bool
386tee_file::isatty ()
e4c242d9 387{
d7e74731 388 return m_one->isatty ();
e4c242d9 389}
8a522c6c
PW
390
391/* See ui-file.h. */
392
393bool
394tee_file::term_out ()
395{
396 return m_one->term_out ();
397}
398
399/* See ui-file.h. */
400
401bool
402tee_file::can_emit_style_escape ()
403{
3cd52293 404 return (m_one->term_out ()
8a522c6c
PW
405 && term_cli_styling ());
406}
0735b091
TT
407
408/* See ui-file.h. */
409
410void
7b18593a 411escape_buffering_file::write (const char *buf, long length_buf)
0735b091
TT
412{
413 std::string copy (buf, length_buf);
414 this->puts (copy.c_str ());
415}
416
417/* See ui-file.h. */
418
419void
7b18593a
TT
420escape_buffering_file::puts (const char *buf)
421{
422 std::string local_buffer;
423 if (!m_buffer.empty ())
424 {
425 gdb_assert (m_buffer[0] == '\033');
426 m_buffer += buf;
427 /* If we need to keep buffering, we'll handle that below. */
428 local_buffer = std::move (m_buffer);
429 buf = local_buffer.c_str ();
430 }
431
432 while (*buf != '\0')
433 {
434 const char *esc = strchr (buf, '\033');
435 if (esc == nullptr)
436 break;
437
438 /* First, write out any prefix. */
439 if (esc > buf)
440 {
441 do_write (buf, esc - buf);
442 buf = esc;
443 }
444
445 int n_read = 0;
446 ansi_escape_result seen = examine_ansi_escape (esc, &n_read);
447 if (seen == ansi_escape_result::INCOMPLETE)
448 {
449 /* Start buffering. */
450 m_buffer = buf;
451 return;
452 }
453 else if (seen == ansi_escape_result::NO_MATCH)
454 {
455 /* Just emit the ESC . */
456 n_read = 1;
457 }
458 else
459 gdb_assert (seen == ansi_escape_result::MATCHED);
460
461 do_write (esc, n_read);
462 buf += n_read;
463 }
464
465 /* If there is any data remaining in BUF, we can flush it now. */
466 if (*buf != '\0')
467 do_puts (buf);
468}
469
470/* See ui-file.h. */
471
472void
473no_terminal_escape_file::do_puts (const char *buf)
0735b091
TT
474{
475 while (*buf != '\0')
476 {
477 const char *esc = strchr (buf, '\033');
478 if (esc == nullptr)
479 break;
480
481 int n_read = 0;
482 if (!skip_ansi_escape (esc, &n_read))
483 ++esc;
484
485 this->stdio_file::write (buf, esc - buf);
486 buf = esc + n_read;
487 }
488
489 if (*buf != '\0')
490 this->stdio_file::write (buf, strlen (buf));
491}
3c6c449e 492
7b18593a
TT
493void
494no_terminal_escape_file::do_write (const char *buf, long len)
495{
496 std::string copy (buf, len);
497 do_puts (copy.c_str ());
498}
499
3c6c449e
TT
500void
501timestamped_file::write (const char *buf, long len)
502{
503 if (debug_timestamp)
504 {
505 /* Print timestamp if previous print ended with a \n. */
506 if (m_needs_timestamp)
507 {
508 using namespace std::chrono;
509
510 steady_clock::time_point now = steady_clock::now ();
511 seconds s = duration_cast<seconds> (now.time_since_epoch ());
512 microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
513 std::string timestamp = string_printf ("%ld.%06ld ",
514 (long) s.count (),
515 (long) us.count ());
516 m_stream->puts (timestamp.c_str ());
517 }
518
519 /* Print the message. */
520 m_stream->write (buf, len);
521
522 m_needs_timestamp = (len > 0 && buf[len - 1] == '\n');
523 }
524 else
525 m_stream->write (buf, len);
526}