Automatic date update in version.in
[binutils-gdb.git] / gdb / cli-out.c
blobd8a542d1b9be1416b87754737e8d64cc4dcf9e08
1 /* Output generating routines for GDB CLI.
3 Copyright (C) 1999-2024 Free Software Foundation, Inc.
5 Contributed by Cygnus Solutions.
6 Written by Fernando Nasser for Cygnus.
8 This file is part of GDB.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "ui-out.h"
24 #include "cli-out.h"
25 #include "completer.h"
26 #include "readline/readline.h"
27 #include "cli/cli-style.h"
28 #include "ui.h"
30 /* These are the CLI output functions */
32 /* Mark beginning of a table */
34 void
35 cli_ui_out::do_table_begin (int nbrofcols, int nr_rows, const char *tblid)
37 if (nr_rows == 0)
38 m_suppress_output = true;
39 else
40 /* Only the table suppresses the output and, fortunately, a table
41 is not a recursive data structure. */
42 gdb_assert (!m_suppress_output);
45 /* Mark beginning of a table body */
47 void
48 cli_ui_out::do_table_body ()
50 if (m_suppress_output)
51 return;
53 /* first, close the table header line */
54 text ("\n");
57 /* Mark end of a table */
59 void
60 cli_ui_out::do_table_end ()
62 m_suppress_output = false;
65 /* Specify table header */
67 void
68 cli_ui_out::do_table_header (int width, ui_align alignment,
69 const std::string &col_name,
70 const std::string &col_hdr)
72 if (m_suppress_output)
73 return;
75 do_field_string (0, width, alignment, 0, col_hdr.c_str (),
76 ui_file_style ());
79 /* Mark beginning of a list */
81 void
82 cli_ui_out::do_begin (ui_out_type type, const char *id)
86 /* Mark end of a list */
88 void
89 cli_ui_out::do_end (ui_out_type type)
93 /* output an int field */
95 void
96 cli_ui_out::do_field_signed (int fldno, int width, ui_align alignment,
97 const char *fldname, LONGEST value,
98 const ui_file_style &style)
100 if (m_suppress_output)
101 return;
103 do_field_string (fldno, width, alignment, fldname, plongest (value),
104 style);
107 /* output an unsigned field */
109 void
110 cli_ui_out::do_field_unsigned (int fldno, int width, ui_align alignment,
111 const char *fldname, ULONGEST value)
113 if (m_suppress_output)
114 return;
116 do_field_string (fldno, width, alignment, fldname, pulongest (value),
117 ui_file_style ());
120 /* used to omit a field */
122 void
123 cli_ui_out::do_field_skip (int fldno, int width, ui_align alignment,
124 const char *fldname)
126 if (m_suppress_output)
127 return;
129 do_field_string (fldno, width, alignment, fldname, "",
130 ui_file_style ());
133 /* other specific cli_field_* end up here so alignment and field
134 separators are both handled by cli_field_string */
136 void
137 cli_ui_out::do_field_string (int fldno, int width, ui_align align,
138 const char *fldname, const char *string,
139 const ui_file_style &style)
141 int before = 0;
142 int after = 0;
144 if (m_suppress_output)
145 return;
147 if ((align != ui_noalign) && string)
149 before = width - strlen (string);
150 if (before <= 0)
151 before = 0;
152 else
154 if (align == ui_right)
155 after = 0;
156 else if (align == ui_left)
158 after = before;
159 before = 0;
161 else
162 /* ui_center */
164 after = before / 2;
165 before -= after;
170 if (before)
171 spaces (before);
173 if (string)
175 ui_file *stream = m_streams.back ();
176 stream->emit_style_escape (style);
177 stream->puts (string);
178 stream->emit_style_escape (ui_file_style ());
181 if (after)
182 spaces (after);
184 if (align != ui_noalign)
185 field_separator ();
188 /* Output field containing ARGS using printf formatting in FORMAT. */
190 void
191 cli_ui_out::do_field_fmt (int fldno, int width, ui_align align,
192 const char *fldname, const ui_file_style &style,
193 const char *format, va_list args)
195 if (m_suppress_output)
196 return;
198 std::string str = string_vprintf (format, args);
200 do_field_string (fldno, width, align, fldname, str.c_str (), style);
203 void
204 cli_ui_out::do_spaces (int numspaces)
206 if (m_suppress_output)
207 return;
209 print_spaces (numspaces, m_streams.back ());
212 void
213 cli_ui_out::do_text (const char *string)
215 if (m_suppress_output)
216 return;
218 gdb_puts (string, m_streams.back ());
221 void
222 cli_ui_out::do_message (const ui_file_style &style,
223 const char *format, va_list args)
225 if (m_suppress_output)
226 return;
228 std::string str = string_vprintf (format, args);
229 if (!str.empty ())
231 ui_file *stream = m_streams.back ();
232 stream->emit_style_escape (style);
233 stream->puts (str.c_str ());
234 stream->emit_style_escape (ui_file_style ());
238 void
239 cli_ui_out::do_wrap_hint (int indent)
241 if (m_suppress_output)
242 return;
244 m_streams.back ()->wrap_here (indent);
247 void
248 cli_ui_out::do_flush ()
250 gdb_flush (m_streams.back ());
253 /* OUTSTREAM as non-NULL will push OUTSTREAM on the stack of output streams
254 and make it therefore active. OUTSTREAM as NULL will pop the last pushed
255 output stream; it is an internal error if it does not exist. */
257 void
258 cli_ui_out::do_redirect (ui_file *outstream)
260 if (outstream != NULL)
261 m_streams.push_back (outstream);
262 else
263 m_streams.pop_back ();
266 /* Initialize a progress update to be displayed with
267 cli_ui_out::do_progress_notify. */
269 void
270 cli_ui_out::do_progress_start ()
272 m_progress_info.emplace_back ();
275 #define MIN_CHARS_PER_LINE 50
276 #define MAX_CHARS_PER_LINE 4096
278 /* Print a progress update. MSG is a string to be printed on the line above
279 the progress bar. TOTAL is the size of the download whose progress is
280 being displayed. UNIT should be the unit of TOTAL (ex. "K"). If HOWMUCH
281 is between 0.0 and 1.0, a progress bar is displayed indicating the percentage
282 of completion and the download size. If HOWMUCH is negative, a progress
283 indicator will tick across the screen. If the output stream is not a tty
284 then only MSG is printed.
286 - printed for tty, HOWMUCH between 0.0 and 1.0:
287 <MSG
288 [######### ] HOWMUCH*100% (TOTAL UNIT)\r>
289 - printed for tty, HOWMUCH < 0.0:
290 <MSG
291 [ ### ]\r>
292 - printed for not-a-tty:
293 <MSG...\n>
296 void
297 cli_ui_out::do_progress_notify (const std::string &msg,
298 const char *unit,
299 double howmuch, double total)
301 int chars_per_line = get_chars_per_line ();
302 struct ui_file *stream = get_unbuffered (m_streams.back ());
303 cli_progress_info &info (m_progress_info.back ());
305 if (chars_per_line > MAX_CHARS_PER_LINE)
306 chars_per_line = MAX_CHARS_PER_LINE;
308 if (info.state == progress_update::START)
310 if (stream->isatty ()
311 && current_ui->input_interactive_p ()
312 && chars_per_line >= MIN_CHARS_PER_LINE)
314 gdb_printf (stream, "%s\n", msg.c_str ());
315 info.state = progress_update::BAR;
317 else
319 gdb_printf (stream, "%s...\n", msg.c_str ());
320 info.state = progress_update::WORKING;
324 if (info.state != progress_update::BAR
325 || chars_per_line < MIN_CHARS_PER_LINE)
326 return;
328 if (total > 0 && howmuch >= 0 && howmuch <= 1.0)
330 std::string progress = string_printf (" %3.f%% (%.2f %s)",
331 howmuch * 100, total,
332 unit);
333 int width = chars_per_line - progress.size () - 4;
334 int max = width * howmuch;
336 std::string display = "\r[";
338 for (int i = 0; i < width; ++i)
339 if (i < max)
340 display += "#";
341 else
342 display += " ";
344 display += "]" + progress;
345 gdb_printf (stream, "%s", display.c_str ());
346 gdb_flush (stream);
348 else
350 using namespace std::chrono;
351 milliseconds diff = duration_cast<milliseconds>
352 (steady_clock::now () - info.last_update);
354 /* Advance the progress indicator at a rate of 1 tick every
355 every 0.5 seconds. */
356 if (diff.count () >= 500)
358 int width = chars_per_line - 4;
360 gdb_printf (stream, "\r[");
361 for (int i = 0; i < width; ++i)
363 if (i == info.pos % width
364 || i == (info.pos + 1) % width
365 || i == (info.pos + 2) % width)
366 gdb_printf (stream, "#");
367 else
368 gdb_printf (stream, " ");
371 gdb_printf (stream, "]");
372 gdb_flush (stream);
373 info.last_update = steady_clock::now ();
374 info.pos++;
378 return;
381 /* Clear do_progress_notify output from the current line. Overwrites the
382 notification with whitespace. */
384 void
385 cli_ui_out::clear_progress_notify ()
387 struct ui_file *stream = get_unbuffered (m_streams.back ());
388 int chars_per_line = get_chars_per_line ();
390 scoped_restore save_pagination
391 = make_scoped_restore (&pagination_enabled, false);
393 if (!stream->isatty ()
394 || !current_ui->input_interactive_p ()
395 || chars_per_line < MIN_CHARS_PER_LINE)
396 return;
398 if (chars_per_line > MAX_CHARS_PER_LINE)
399 chars_per_line = MAX_CHARS_PER_LINE;
401 gdb_printf (stream, "\r");
402 for (int i = 0; i < chars_per_line; ++i)
403 gdb_printf (stream, " ");
404 gdb_printf (stream, "\r");
406 gdb_flush (stream);
409 /* Remove the most recent progress update from the progress_info stack
410 and overwrite the current line with whitespace. */
412 void
413 cli_ui_out::do_progress_end ()
415 struct ui_file *stream = m_streams.back ();
416 cli_progress_info &info (m_progress_info.back ());
418 if (stream->isatty () && info.state != progress_update::START)
419 clear_progress_notify ();
421 m_progress_info.pop_back ();
424 /* local functions */
426 void
427 cli_ui_out::field_separator ()
429 gdb_putc (' ', m_streams.back ());
432 /* Constructor for cli_ui_out. */
434 cli_ui_out::cli_ui_out (ui_file *stream, ui_out_flags flags)
435 : ui_out (flags),
436 m_suppress_output (false)
438 gdb_assert (stream != NULL);
440 m_streams.push_back (stream);
443 cli_ui_out::~cli_ui_out ()
447 ui_file *
448 cli_ui_out::set_stream (struct ui_file *stream)
450 ui_file *old;
452 old = m_streams.back ();
453 m_streams.back () = stream;
455 return old;
458 bool
459 cli_ui_out::can_emit_style_escape () const
461 return m_streams.back ()->can_emit_style_escape ();
464 /* CLI interface to display tab-completion matches. */
466 /* CLI version of displayer.crlf. */
468 static void
469 cli_mld_crlf (const struct match_list_displayer *displayer)
471 rl_crlf ();
474 /* CLI version of displayer.putch. */
476 static void
477 cli_mld_putch (const struct match_list_displayer *displayer, int ch)
479 putc (ch, rl_outstream);
482 /* CLI version of displayer.puts. */
484 static void
485 cli_mld_puts (const struct match_list_displayer *displayer, const char *s)
487 fputs (s, rl_outstream);
490 /* CLI version of displayer.flush. */
492 static void
493 cli_mld_flush (const struct match_list_displayer *displayer)
495 fflush (rl_outstream);
498 extern "C" void _rl_erase_entire_line (void);
500 /* CLI version of displayer.erase_entire_line. */
502 static void
503 cli_mld_erase_entire_line (const struct match_list_displayer *displayer)
505 _rl_erase_entire_line ();
508 /* CLI version of displayer.beep. */
510 static void
511 cli_mld_beep (const struct match_list_displayer *displayer)
513 rl_ding ();
516 /* CLI version of displayer.read_key. */
518 static int
519 cli_mld_read_key (const struct match_list_displayer *displayer)
521 return rl_read_key ();
524 /* CLI version of rl_completion_display_matches_hook.
525 See gdb_display_match_list for a description of the arguments. */
527 void
528 cli_display_match_list (char **matches, int len, int max)
530 struct match_list_displayer displayer;
532 rl_get_screen_size (&displayer.height, &displayer.width);
533 displayer.crlf = cli_mld_crlf;
534 displayer.putch = cli_mld_putch;
535 displayer.puts = cli_mld_puts;
536 displayer.flush = cli_mld_flush;
537 displayer.erase_entire_line = cli_mld_erase_entire_line;
538 displayer.beep = cli_mld_beep;
539 displayer.read_key = cli_mld_read_key;
541 gdb_display_match_list (matches, len, max, &displayer);
542 rl_forced_update_display ();