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
)
99 if (m_suppress_output
)
102 do_field_string (fldno
, width
, alignment
, fldname
, plongest (value
),
106 /* output an unsigned field */
109 cli_ui_out::do_field_unsigned (int fldno
, int width
, ui_align alignment
,
110 const char *fldname
, ULONGEST value
)
112 if (m_suppress_output
)
115 do_field_string (fldno
, width
, alignment
, fldname
, pulongest (value
),
119 /* used to omit a field */
122 cli_ui_out::do_field_skip (int fldno
, int width
, ui_align alignment
,
125 if (m_suppress_output
)
128 do_field_string (fldno
, width
, alignment
, fldname
, "",
132 /* other specific cli_field_* end up here so alignment and field
133 separators are both handled by cli_field_string */
136 cli_ui_out::do_field_string (int fldno
, int width
, ui_align align
,
137 const char *fldname
, const char *string
,
138 const ui_file_style
&style
)
143 if (m_suppress_output
)
146 if ((align
!= ui_noalign
) && string
)
148 before
= width
- strlen (string
);
153 if (align
== ui_right
)
155 else if (align
== ui_left
)
174 ui_file
*stream
= m_streams
.back ();
175 stream
->emit_style_escape (style
);
176 stream
->puts (string
);
177 stream
->emit_style_escape (ui_file_style ());
183 if (align
!= ui_noalign
)
187 /* Output field containing ARGS using printf formatting in FORMAT. */
190 cli_ui_out::do_field_fmt (int fldno
, int width
, ui_align align
,
191 const char *fldname
, const ui_file_style
&style
,
192 const char *format
, va_list args
)
194 if (m_suppress_output
)
197 std::string str
= string_vprintf (format
, args
);
199 do_field_string (fldno
, width
, align
, fldname
, str
.c_str (), style
);
203 cli_ui_out::do_spaces (int numspaces
)
205 if (m_suppress_output
)
208 print_spaces (numspaces
, m_streams
.back ());
212 cli_ui_out::do_text (const char *string
)
214 if (m_suppress_output
)
217 gdb_puts (string
, m_streams
.back ());
221 cli_ui_out::do_message (const ui_file_style
&style
,
222 const char *format
, va_list args
)
224 if (m_suppress_output
)
227 std::string str
= string_vprintf (format
, args
);
230 ui_file
*stream
= m_streams
.back ();
231 stream
->emit_style_escape (style
);
232 stream
->puts (str
.c_str ());
233 stream
->emit_style_escape (ui_file_style ());
238 cli_ui_out::do_wrap_hint (int indent
)
240 if (m_suppress_output
)
243 m_streams
.back ()->wrap_here (indent
);
247 cli_ui_out::do_flush ()
249 gdb_flush (m_streams
.back ());
252 /* OUTSTREAM as non-NULL will push OUTSTREAM on the stack of output streams
253 and make it therefore active. OUTSTREAM as NULL will pop the last pushed
254 output stream; it is an internal error if it does not exist. */
257 cli_ui_out::do_redirect (ui_file
*outstream
)
259 if (outstream
!= NULL
)
260 m_streams
.push_back (outstream
);
262 m_streams
.pop_back ();
265 /* Initialize a progress update to be displayed with
266 cli_ui_out::do_progress_notify. */
269 cli_ui_out::do_progress_start ()
271 m_progress_info
.emplace_back ();
274 #define MIN_CHARS_PER_LINE 50
275 #define MAX_CHARS_PER_LINE 4096
277 /* Print a progress update. MSG is a string to be printed on the line above
278 the progress bar. TOTAL is the size of the download whose progress is
279 being displayed. UNIT should be the unit of TOTAL (ex. "K"). If HOWMUCH
280 is between 0.0 and 1.0, a progress bar is displayed indicating the percentage
281 of completion and the download size. If HOWMUCH is negative, a progress
282 indicator will tick across the screen. If the output stream is not a tty
283 then only MSG is printed.
285 - printed for tty, HOWMUCH between 0.0 and 1.0:
287 [######### ] HOWMUCH*100% (TOTAL UNIT)\r>
288 - printed for tty, HOWMUCH < 0.0:
291 - printed for not-a-tty:
296 cli_ui_out::do_progress_notify (const std::string
&msg
,
298 double howmuch
, double total
)
300 int chars_per_line
= get_chars_per_line ();
301 struct ui_file
*stream
= get_unbuffered (m_streams
.back ());
302 cli_progress_info
&info (m_progress_info
.back ());
304 if (chars_per_line
> MAX_CHARS_PER_LINE
)
305 chars_per_line
= MAX_CHARS_PER_LINE
;
307 if (info
.state
== progress_update::START
)
309 if (stream
->isatty ()
310 && current_ui
->input_interactive_p ()
311 && chars_per_line
>= MIN_CHARS_PER_LINE
)
313 gdb_printf (stream
, "%s\n", msg
.c_str ());
314 info
.state
= progress_update::BAR
;
318 gdb_printf (stream
, "%s...\n", msg
.c_str ());
319 info
.state
= progress_update::WORKING
;
323 if (info
.state
!= progress_update::BAR
324 || chars_per_line
< MIN_CHARS_PER_LINE
)
327 if (total
> 0 && howmuch
>= 0 && howmuch
<= 1.0)
329 std::string progress
= string_printf (" %3.f%% (%.2f %s)",
330 howmuch
* 100, total
,
332 int width
= chars_per_line
- progress
.size () - 4;
333 int max
= width
* howmuch
;
335 std::string display
= "\r[";
337 for (int i
= 0; i
< width
; ++i
)
343 display
+= "]" + progress
;
344 gdb_printf (stream
, "%s", display
.c_str ());
349 using namespace std::chrono
;
350 milliseconds diff
= duration_cast
<milliseconds
>
351 (steady_clock::now () - info
.last_update
);
353 /* Advance the progress indicator at a rate of 1 tick every
354 every 0.5 seconds. */
355 if (diff
.count () >= 500)
357 int width
= chars_per_line
- 4;
359 gdb_printf (stream
, "\r[");
360 for (int i
= 0; i
< width
; ++i
)
362 if (i
== info
.pos
% width
363 || i
== (info
.pos
+ 1) % width
364 || i
== (info
.pos
+ 2) % width
)
365 gdb_printf (stream
, "#");
367 gdb_printf (stream
, " ");
370 gdb_printf (stream
, "]");
372 info
.last_update
= steady_clock::now ();
380 /* Clear do_progress_notify output from the current line. Overwrites the
381 notification with whitespace. */
384 cli_ui_out::clear_progress_notify ()
386 struct ui_file
*stream
= get_unbuffered (m_streams
.back ());
387 int chars_per_line
= get_chars_per_line ();
389 scoped_restore save_pagination
390 = make_scoped_restore (&pagination_enabled
, false);
392 if (!stream
->isatty ()
393 || !current_ui
->input_interactive_p ()
394 || chars_per_line
< MIN_CHARS_PER_LINE
)
397 if (chars_per_line
> MAX_CHARS_PER_LINE
)
398 chars_per_line
= MAX_CHARS_PER_LINE
;
400 gdb_printf (stream
, "\r");
401 for (int i
= 0; i
< chars_per_line
; ++i
)
402 gdb_printf (stream
, " ");
403 gdb_printf (stream
, "\r");
408 /* Remove the most recent progress update from the progress_info stack
409 and overwrite the current line with whitespace. */
412 cli_ui_out::do_progress_end ()
414 struct ui_file
*stream
= m_streams
.back ();
415 cli_progress_info
&info (m_progress_info
.back ());
417 if (stream
->isatty () && info
.state
!= progress_update::START
)
418 clear_progress_notify ();
420 m_progress_info
.pop_back ();
423 /* local functions */
426 cli_ui_out::field_separator ()
428 gdb_putc (' ', m_streams
.back ());
431 /* Constructor for cli_ui_out. */
433 cli_ui_out::cli_ui_out (ui_file
*stream
, ui_out_flags flags
)
435 m_suppress_output (false)
437 gdb_assert (stream
!= NULL
);
439 m_streams
.push_back (stream
);
442 cli_ui_out::~cli_ui_out ()
447 cli_ui_out::set_stream (struct ui_file
*stream
)
451 old
= m_streams
.back ();
452 m_streams
.back () = stream
;
458 cli_ui_out::can_emit_style_escape () const
460 return m_streams
.back ()->can_emit_style_escape ();
463 /* CLI interface to display tab-completion matches. */
465 /* CLI version of displayer.crlf. */
468 cli_mld_crlf (const struct match_list_displayer
*displayer
)
473 /* CLI version of displayer.putch. */
476 cli_mld_putch (const struct match_list_displayer
*displayer
, int ch
)
478 putc (ch
, rl_outstream
);
481 /* CLI version of displayer.puts. */
484 cli_mld_puts (const struct match_list_displayer
*displayer
, const char *s
)
486 fputs (s
, rl_outstream
);
489 /* CLI version of displayer.flush. */
492 cli_mld_flush (const struct match_list_displayer
*displayer
)
494 fflush (rl_outstream
);
497 extern "C" void _rl_erase_entire_line (void);
499 /* CLI version of displayer.erase_entire_line. */
502 cli_mld_erase_entire_line (const struct match_list_displayer
*displayer
)
504 _rl_erase_entire_line ();
507 /* CLI version of displayer.beep. */
510 cli_mld_beep (const struct match_list_displayer
*displayer
)
515 /* CLI version of displayer.read_key. */
518 cli_mld_read_key (const struct match_list_displayer
*displayer
)
520 return rl_read_key ();
523 /* CLI version of rl_completion_display_matches_hook.
524 See gdb_display_match_list for a description of the arguments. */
527 cli_display_match_list (char **matches
, int len
, int max
)
529 struct match_list_displayer displayer
;
531 rl_get_screen_size (&displayer
.height
, &displayer
.width
);
532 displayer
.crlf
= cli_mld_crlf
;
533 displayer
.putch
= cli_mld_putch
;
534 displayer
.puts
= cli_mld_puts
;
535 displayer
.flush
= cli_mld_flush
;
536 displayer
.erase_entire_line
= cli_mld_erase_entire_line
;
537 displayer
.beep
= cli_mld_beep
;
538 displayer
.read_key
= cli_mld_read_key
;
540 gdb_display_match_list (matches
, len
, max
, &displayer
);
541 rl_forced_update_display ();