Fix building Loongarch BFD with a 32-bit compiler
[binutils-gdb.git] / gdb / cli-out.c
blob1c303f096626308d1942920d69c8787e3cd3e20b
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)
99 if (m_suppress_output)
100 return;
102 do_field_string (fldno, width, alignment, fldname, plongest (value),
103 ui_file_style ());
106 /* output an unsigned field */
108 void
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)
113 return;
115 do_field_string (fldno, width, alignment, fldname, pulongest (value),
116 ui_file_style ());
119 /* used to omit a field */
121 void
122 cli_ui_out::do_field_skip (int fldno, int width, ui_align alignment,
123 const char *fldname)
125 if (m_suppress_output)
126 return;
128 do_field_string (fldno, width, alignment, fldname, "",
129 ui_file_style ());
132 /* other specific cli_field_* end up here so alignment and field
133 separators are both handled by cli_field_string */
135 void
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)
140 int before = 0;
141 int after = 0;
143 if (m_suppress_output)
144 return;
146 if ((align != ui_noalign) && string)
148 before = width - strlen (string);
149 if (before <= 0)
150 before = 0;
151 else
153 if (align == ui_right)
154 after = 0;
155 else if (align == ui_left)
157 after = before;
158 before = 0;
160 else
161 /* ui_center */
163 after = before / 2;
164 before -= after;
169 if (before)
170 spaces (before);
172 if (string)
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 ());
180 if (after)
181 spaces (after);
183 if (align != ui_noalign)
184 field_separator ();
187 /* Output field containing ARGS using printf formatting in FORMAT. */
189 void
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)
195 return;
197 std::string str = string_vprintf (format, args);
199 do_field_string (fldno, width, align, fldname, str.c_str (), style);
202 void
203 cli_ui_out::do_spaces (int numspaces)
205 if (m_suppress_output)
206 return;
208 print_spaces (numspaces, m_streams.back ());
211 void
212 cli_ui_out::do_text (const char *string)
214 if (m_suppress_output)
215 return;
217 gdb_puts (string, m_streams.back ());
220 void
221 cli_ui_out::do_message (const ui_file_style &style,
222 const char *format, va_list args)
224 if (m_suppress_output)
225 return;
227 std::string str = string_vprintf (format, args);
228 if (!str.empty ())
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 ());
237 void
238 cli_ui_out::do_wrap_hint (int indent)
240 if (m_suppress_output)
241 return;
243 m_streams.back ()->wrap_here (indent);
246 void
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. */
256 void
257 cli_ui_out::do_redirect (ui_file *outstream)
259 if (outstream != NULL)
260 m_streams.push_back (outstream);
261 else
262 m_streams.pop_back ();
265 /* Initialize a progress update to be displayed with
266 cli_ui_out::do_progress_notify. */
268 void
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:
286 <MSG
287 [######### ] HOWMUCH*100% (TOTAL UNIT)\r>
288 - printed for tty, HOWMUCH < 0.0:
289 <MSG
290 [ ### ]\r>
291 - printed for not-a-tty:
292 <MSG...\n>
295 void
296 cli_ui_out::do_progress_notify (const std::string &msg,
297 const char *unit,
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;
316 else
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)
325 return;
327 if (total > 0 && howmuch >= 0 && howmuch <= 1.0)
329 std::string progress = string_printf (" %3.f%% (%.2f %s)",
330 howmuch * 100, total,
331 unit);
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)
338 if (i < max)
339 display += "#";
340 else
341 display += " ";
343 display += "]" + progress;
344 gdb_printf (stream, "%s", display.c_str ());
345 gdb_flush (stream);
347 else
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, "#");
366 else
367 gdb_printf (stream, " ");
370 gdb_printf (stream, "]");
371 gdb_flush (stream);
372 info.last_update = steady_clock::now ();
373 info.pos++;
377 return;
380 /* Clear do_progress_notify output from the current line. Overwrites the
381 notification with whitespace. */
383 void
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)
395 return;
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");
405 gdb_flush (stream);
408 /* Remove the most recent progress update from the progress_info stack
409 and overwrite the current line with whitespace. */
411 void
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 */
425 void
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)
434 : ui_out (flags),
435 m_suppress_output (false)
437 gdb_assert (stream != NULL);
439 m_streams.push_back (stream);
442 cli_ui_out::~cli_ui_out ()
446 ui_file *
447 cli_ui_out::set_stream (struct ui_file *stream)
449 ui_file *old;
451 old = m_streams.back ();
452 m_streams.back () = stream;
454 return old;
457 bool
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. */
467 static void
468 cli_mld_crlf (const struct match_list_displayer *displayer)
470 rl_crlf ();
473 /* CLI version of displayer.putch. */
475 static void
476 cli_mld_putch (const struct match_list_displayer *displayer, int ch)
478 putc (ch, rl_outstream);
481 /* CLI version of displayer.puts. */
483 static void
484 cli_mld_puts (const struct match_list_displayer *displayer, const char *s)
486 fputs (s, rl_outstream);
489 /* CLI version of displayer.flush. */
491 static void
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. */
501 static void
502 cli_mld_erase_entire_line (const struct match_list_displayer *displayer)
504 _rl_erase_entire_line ();
507 /* CLI version of displayer.beep. */
509 static void
510 cli_mld_beep (const struct match_list_displayer *displayer)
512 rl_ding ();
515 /* CLI version of displayer.read_key. */
517 static int
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. */
526 void
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 ();