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/>. */
25 #include "completer.h"
26 #include "readline/readline.h"
27 #include "cli/cli-style.h"
30 /* These are the CLI output functions */
32 /* Mark beginning of a table */
35 cli_ui_out::do_table_begin (int nbrofcols
, int nr_rows
, const char *tblid
)
38 m_suppress_output
= true;
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 */
48 cli_ui_out::do_table_body ()
50 if (m_suppress_output
)
53 /* first, close the table header line */
57 /* Mark end of a table */
60 cli_ui_out::do_table_end ()
62 m_suppress_output
= false;
65 /* Specify table header */
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
)
75 do_field_string (0, width
, alignment
, 0, col_hdr
.c_str (),
79 /* Mark beginning of a list */
82 cli_ui_out::do_begin (ui_out_type type
, const char *id
)
86 /* Mark end of a list */
89 cli_ui_out::do_end (ui_out_type type
)
93 /* output an int field */
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
)
103 do_field_string (fldno
, width
, alignment
, fldname
, plongest (value
),
107 /* output an unsigned field */
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
)
116 do_field_string (fldno
, width
, alignment
, fldname
, pulongest (value
),
120 /* used to omit a field */
123 cli_ui_out::do_field_skip (int fldno
, int width
, ui_align alignment
,
126 if (m_suppress_output
)
129 do_field_string (fldno
, width
, alignment
, fldname
, "",
133 /* other specific cli_field_* end up here so alignment and field
134 separators are both handled by cli_field_string */
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
)
144 if (m_suppress_output
)
147 if ((align
!= ui_noalign
) && string
)
149 before
= width
- strlen (string
);
154 if (align
== ui_right
)
156 else if (align
== ui_left
)
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 ());
184 if (align
!= ui_noalign
)
188 /* Output field containing ARGS using printf formatting in FORMAT. */
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
)
198 std::string str
= string_vprintf (format
, args
);
200 do_field_string (fldno
, width
, align
, fldname
, str
.c_str (), style
);
204 cli_ui_out::do_spaces (int numspaces
)
206 if (m_suppress_output
)
209 print_spaces (numspaces
, m_streams
.back ());
213 cli_ui_out::do_text (const char *string
)
215 if (m_suppress_output
)
218 gdb_puts (string
, m_streams
.back ());
222 cli_ui_out::do_message (const ui_file_style
&style
,
223 const char *format
, va_list args
)
225 if (m_suppress_output
)
228 std::string str
= string_vprintf (format
, args
);
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 ());
239 cli_ui_out::do_wrap_hint (int indent
)
241 if (m_suppress_output
)
244 m_streams
.back ()->wrap_here (indent
);
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. */
258 cli_ui_out::do_redirect (ui_file
*outstream
)
260 if (outstream
!= NULL
)
261 m_streams
.push_back (outstream
);
263 m_streams
.pop_back ();
266 /* Initialize a progress update to be displayed with
267 cli_ui_out::do_progress_notify. */
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:
288 [######### ] HOWMUCH*100% (TOTAL UNIT)\r>
289 - printed for tty, HOWMUCH < 0.0:
292 - printed for not-a-tty:
297 cli_ui_out::do_progress_notify (const std::string
&msg
,
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
;
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
)
328 if (total
> 0 && howmuch
>= 0 && howmuch
<= 1.0)
330 std::string progress
= string_printf (" %3.f%% (%.2f %s)",
331 howmuch
* 100, total
,
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
)
344 display
+= "]" + progress
;
345 gdb_printf (stream
, "%s", display
.c_str ());
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
, "#");
368 gdb_printf (stream
, " ");
371 gdb_printf (stream
, "]");
373 info
.last_update
= steady_clock::now ();
381 /* Clear do_progress_notify output from the current line. Overwrites the
382 notification with whitespace. */
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
)
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");
409 /* Remove the most recent progress update from the progress_info stack
410 and overwrite the current line with whitespace. */
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 */
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
)
436 m_suppress_output (false)
438 gdb_assert (stream
!= NULL
);
440 m_streams
.push_back (stream
);
443 cli_ui_out::~cli_ui_out ()
448 cli_ui_out::set_stream (struct ui_file
*stream
)
452 old
= m_streams
.back ();
453 m_streams
.back () = stream
;
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. */
469 cli_mld_crlf (const struct match_list_displayer
*displayer
)
474 /* CLI version of displayer.putch. */
477 cli_mld_putch (const struct match_list_displayer
*displayer
, int ch
)
479 putc (ch
, rl_outstream
);
482 /* CLI version of displayer.puts. */
485 cli_mld_puts (const struct match_list_displayer
*displayer
, const char *s
)
487 fputs (s
, rl_outstream
);
490 /* CLI version of displayer.flush. */
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. */
503 cli_mld_erase_entire_line (const struct match_list_displayer
*displayer
)
505 _rl_erase_entire_line ();
508 /* CLI version of displayer.beep. */
511 cli_mld_beep (const struct match_list_displayer
*displayer
)
516 /* CLI version of displayer.read_key. */
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. */
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 ();