]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/ui-file.c
ld: Change -z one-rosegment to --rosegment in comments
[thirdparty/binutils-gdb.git] / gdb / ui-file.c
CommitLineData
d9fcf2fb 1/* UI_FILE - a generic STDIO like output stream.
349c5d5f 2
1d506c26 3 Copyright (C) 1999-2024 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
90void
91ui_file::reset_style ()
92{
93 if (can_emit_style_escape ())
94 {
95 m_applied_style = ui_file_style ();
96 this->puts (m_applied_style.to_ansi ().c_str ());
97 }
98}
99
100/* See ui-file.h. */
101
d53fd721
TT
102void
103ui_file::printchar (int c, int quoter, bool async_safe)
104{
105 char buf[4];
106 int out = 0;
107
108 c &= 0xFF; /* Avoid sign bit follies */
109
110 if (c < 0x20 /* Low control chars */
111 || (c >= 0x7F && c < 0xA0) /* DEL, High controls */
112 || (sevenbit_strings && c >= 0x80))
113 { /* high order bit set */
114 buf[out++] = '\\';
115
116 switch (c)
117 {
118 case '\n':
119 buf[out++] = 'n';
120 break;
121 case '\b':
122 buf[out++] = 'b';
123 break;
124 case '\t':
125 buf[out++] = 't';
126 break;
127 case '\f':
128 buf[out++] = 'f';
129 break;
130 case '\r':
131 buf[out++] = 'r';
132 break;
133 case '\033':
134 buf[out++] = 'e';
135 break;
136 case '\007':
137 buf[out++] = 'a';
138 break;
139 default:
140 {
141 buf[out++] = '0' + ((c >> 6) & 0x7);
142 buf[out++] = '0' + ((c >> 3) & 0x7);
143 buf[out++] = '0' + ((c >> 0) & 0x7);
144 break;
145 }
146 }
147 }
148 else
149 {
150 if (quoter != 0 && (c == '\\' || c == quoter))
151 buf[out++] = '\\';
152 buf[out++] = c;
153 }
154
155 if (async_safe)
156 this->write_async_safe (buf, out);
157 else
158 this->write (buf, out);
159}
160
d7e74731 161\f
01124a23 162
d7e74731
PA
163void
164null_file::write (const char *buf, long sizeof_buf)
d9fcf2fb 165{
d7e74731 166 /* Discard the request. */
d9fcf2fb
JM
167}
168
d7e74731
PA
169void
170null_file::puts (const char *)
2a9d5ccf 171{
d7e74731 172 /* Discard the request. */
2a9d5ccf
HZ
173}
174
d7e74731
PA
175void
176null_file::write_async_safe (const char *buf, long sizeof_buf)
d9fcf2fb 177{
d7e74731 178 /* Discard the request. */
d9fcf2fb
JM
179}
180
d7e74731
PA
181\f
182
8a522c6c
PW
183/* true if the gdb terminal supports styling, and styling is enabled. */
184
185static bool
186term_cli_styling ()
187{
8a522c6c
PW
188 if (!cli_styling)
189 return false;
190
191 const char *term = getenv ("TERM");
192 /* Windows doesn't by default define $TERM, but can support styles
193 regardless. */
194#ifndef _WIN32
195 if (term == nullptr || !strcmp (term, "dumb"))
196 return false;
197#else
198 /* But if they do define $TERM, let us behave the same as on Posix
199 platforms, for the benefit of programs which invoke GDB as their
200 back-end. */
201 if (term && !strcmp (term, "dumb"))
202 return false;
203#endif
204 return true;
205}
206
d7e74731 207\f
d9fcf2fb 208
d7e74731
PA
209string_file::~string_file ()
210{}
d9fcf2fb
JM
211
212void
d7e74731 213string_file::write (const char *buf, long length_buf)
d9fcf2fb 214{
d7e74731 215 m_string.append (buf, length_buf);
d9fcf2fb
JM
216}
217
8a522c6c
PW
218/* See ui-file.h. */
219
220bool
221string_file::term_out ()
222{
223 return m_term_out;
224}
225
226/* See ui-file.h. */
227
228bool
229string_file::can_emit_style_escape ()
230{
231 return m_term_out && term_cli_styling ();
232}
233
d7e74731 234\f
01124a23 235
d7e74731 236stdio_file::stdio_file (FILE *file, bool close_p)
449092f6 237{
d7e74731
PA
238 set_stream (file);
239 m_close_p = close_p;
449092f6
CV
240}
241
d7e74731
PA
242stdio_file::stdio_file ()
243 : m_file (NULL),
244 m_fd (-1),
245 m_close_p (false)
246{}
d9fcf2fb 247
d7e74731 248stdio_file::~stdio_file ()
2a9d5ccf 249{
d7e74731
PA
250 if (m_close_p)
251 fclose (m_file);
2a9d5ccf
HZ
252}
253
d9fcf2fb 254void
d7e74731 255stdio_file::set_stream (FILE *file)
d9fcf2fb 256{
d7e74731
PA
257 m_file = file;
258 m_fd = fileno (file);
d9fcf2fb
JM
259}
260
d7e74731
PA
261bool
262stdio_file::open (const char *name, const char *mode)
d9fcf2fb 263{
d7e74731
PA
264 /* Close the previous stream, if we own it. */
265 if (m_close_p)
266 {
267 fclose (m_file);
268 m_close_p = false;
269 }
5d502164 270
d419f42d 271 gdb_file_up f = gdb_fopen_cloexec (name, mode);
d9fcf2fb 272
d7e74731
PA
273 if (f == NULL)
274 return false;
d9fcf2fb 275
d419f42d 276 set_stream (f.release ());
d7e74731 277 m_close_p = true;
5d502164 278
d7e74731 279 return true;
d9fcf2fb
JM
280}
281
282void
d7e74731 283stdio_file::flush ()
d9fcf2fb 284{
d7e74731 285 fflush (m_file);
d9fcf2fb
JM
286}
287
d7e74731
PA
288long
289stdio_file::read (char *buf, long length_buf)
449092f6 290{
f0881b37
PA
291 /* Wait until at least one byte of data is available, or we get
292 interrupted with Control-C. */
ad960ed2 293 {
ad960ed2 294 fd_set readfds;
f0881b37 295
ad960ed2 296 FD_ZERO (&readfds);
d7e74731
PA
297 FD_SET (m_fd, &readfds);
298 if (interruptible_select (m_fd + 1, &readfds, NULL, NULL, NULL) == -1)
ad960ed2
DJ
299 return -1;
300 }
301
d7e74731 302 return ::read (m_fd, buf, length_buf);
449092f6
CV
303}
304
d7e74731
PA
305void
306stdio_file::write (const char *buf, long length_buf)
d9fcf2fb 307{
bf1d7d9c 308 /* Calling error crashes when we are called from the exception framework. */
d7e74731 309 if (fwrite (buf, length_buf, 1, m_file))
d4fb63e1
TT
310 {
311 /* Nothing. */
312 }
d9fcf2fb
JM
313}
314
d7e74731
PA
315void
316stdio_file::write_async_safe (const char *buf, long length_buf)
01124a23 317{
9f7bc587
DE
318 /* This is written the way it is to avoid a warning from gcc about not using the
319 result of write (since it can be declared with attribute warn_unused_result).
320 Alas casting to void doesn't work for this. */
d7e74731 321 if (::write (m_fd, buf, length_buf))
d4fb63e1
TT
322 {
323 /* Nothing. */
324 }
01124a23
DE
325}
326
d7e74731
PA
327void
328stdio_file::puts (const char *linebuffer)
d9fcf2fb 329{
e4adb939
EZ
330 /* This host-dependent function (with implementations in
331 posix-hdep.c and mingw-hdep.c) is given the opportunity to
332 process the output first in host-dependent way. If it does, it
333 should return non-zero, to avoid calling fputs below. */
334 if (gdb_console_fputs (linebuffer, m_file))
335 return;
bf1d7d9c 336 /* Calling error crashes when we are called from the exception framework. */
d7e74731 337 if (fputs (linebuffer, m_file))
d4fb63e1
TT
338 {
339 /* Nothing. */
340 }
d9fcf2fb
JM
341}
342
d7e74731
PA
343bool
344stdio_file::isatty ()
d9fcf2fb 345{
d7e74731 346 return ::isatty (m_fd);
d9fcf2fb
JM
347}
348
8a522c6c
PW
349/* See ui-file.h. */
350
351bool
352stdio_file::can_emit_style_escape ()
353{
3cd52293 354 return (this->isatty ()
8a522c6c
PW
355 && term_cli_styling ());
356}
357
d7e74731 358\f
2a9d5ccf 359
d7e74731 360/* This is the implementation of ui_file method 'write' for stderr.
ffa4ac95
YQ
361 gdb_stdout is flushed before writing to gdb_stderr. */
362
d7e74731
PA
363void
364stderr_file::write (const char *buf, long length_buf)
ffa4ac95 365{
da5bd37e 366 gdb_stdout->flush ();
d7e74731 367 stdio_file::write (buf, length_buf);
ffa4ac95
YQ
368}
369
d7e74731 370/* This is the implementation of ui_file method 'puts' for stderr.
ffa4ac95
YQ
371 gdb_stdout is flushed before writing to gdb_stderr. */
372
d7e74731
PA
373void
374stderr_file::puts (const char *linebuffer)
ffa4ac95 375{
da5bd37e 376 gdb_stdout->flush ();
d7e74731 377 stdio_file::puts (linebuffer);
ffa4ac95 378}
ffa4ac95 379
d7e74731
PA
380stderr_file::stderr_file (FILE *stream)
381 : stdio_file (stream)
382{}
d9fcf2fb 383
d7e74731 384\f
e4c242d9 385
2b141965 386tee_file::tee_file (ui_file *one, ui_file *two)
d7e74731 387 : m_one (one),
2b141965 388 m_two (two)
d7e74731 389{}
e4c242d9 390
d7e74731 391tee_file::~tee_file ()
e4c242d9 392{
e4c242d9
DJ
393}
394
d7e74731
PA
395void
396tee_file::flush ()
e4c242d9 397{
d7e74731
PA
398 m_one->flush ();
399 m_two->flush ();
e4c242d9
DJ
400}
401
d7e74731
PA
402void
403tee_file::write (const char *buf, long length_buf)
e4c242d9 404{
d7e74731
PA
405 m_one->write (buf, length_buf);
406 m_two->write (buf, length_buf);
e4c242d9
DJ
407}
408
d7e74731
PA
409void
410tee_file::write_async_safe (const char *buf, long length_buf)
e4c242d9 411{
d7e74731
PA
412 m_one->write_async_safe (buf, length_buf);
413 m_two->write_async_safe (buf, length_buf);
e4c242d9
DJ
414}
415
d7e74731
PA
416void
417tee_file::puts (const char *linebuffer)
e4c242d9 418{
d7e74731
PA
419 m_one->puts (linebuffer);
420 m_two->puts (linebuffer);
e4c242d9
DJ
421}
422
d7e74731
PA
423bool
424tee_file::isatty ()
e4c242d9 425{
d7e74731 426 return m_one->isatty ();
e4c242d9 427}
8a522c6c
PW
428
429/* See ui-file.h. */
430
431bool
432tee_file::term_out ()
433{
434 return m_one->term_out ();
435}
436
437/* See ui-file.h. */
438
439bool
440tee_file::can_emit_style_escape ()
441{
3cd52293 442 return (m_one->term_out ()
8a522c6c
PW
443 && term_cli_styling ());
444}
0735b091
TT
445
446/* See ui-file.h. */
447
448void
449no_terminal_escape_file::write (const char *buf, long length_buf)
450{
451 std::string copy (buf, length_buf);
452 this->puts (copy.c_str ());
453}
454
455/* See ui-file.h. */
456
457void
458no_terminal_escape_file::puts (const char *buf)
459{
460 while (*buf != '\0')
461 {
462 const char *esc = strchr (buf, '\033');
463 if (esc == nullptr)
464 break;
465
466 int n_read = 0;
467 if (!skip_ansi_escape (esc, &n_read))
468 ++esc;
469
470 this->stdio_file::write (buf, esc - buf);
471 buf = esc + n_read;
472 }
473
474 if (*buf != '\0')
475 this->stdio_file::write (buf, strlen (buf));
476}
3c6c449e
TT
477
478void
479timestamped_file::write (const char *buf, long len)
480{
481 if (debug_timestamp)
482 {
483 /* Print timestamp if previous print ended with a \n. */
484 if (m_needs_timestamp)
485 {
486 using namespace std::chrono;
487
488 steady_clock::time_point now = steady_clock::now ();
489 seconds s = duration_cast<seconds> (now.time_since_epoch ());
490 microseconds us = duration_cast<microseconds> (now.time_since_epoch () - s);
491 std::string timestamp = string_printf ("%ld.%06ld ",
492 (long) s.count (),
493 (long) us.count ());
494 m_stream->puts (timestamp.c_str ());
495 }
496
497 /* Print the message. */
498 m_stream->write (buf, len);
499
500 m_needs_timestamp = (len > 0 && buf[len - 1] == '\n');
501 }
502 else
503 m_stream->write (buf, len);
504}