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