|  | /* MI Command Set - output generating routines. | 
|  |  | 
|  | Copyright (C) 2000-2024 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by Cygnus Solutions (a Red Hat company). | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "mi-out.h" | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "interps.h" | 
|  | #include "ui-out.h" | 
|  | #include "utils.h" | 
|  | #include "gdbsupport/gdb-checked-static-cast.h" | 
|  |  | 
|  | /* Mark beginning of a table.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_table_begin (int nr_cols, int nr_rows, | 
|  | const char *tblid) | 
|  | { | 
|  | open (tblid, ui_out_type_tuple); | 
|  | do_field_signed (-1, -1, ui_left, "nr_rows", nr_rows, ui_file_style ()); | 
|  | do_field_signed (-1, -1, ui_left, "nr_cols", nr_cols, ui_file_style ()); | 
|  | open ("hdr", ui_out_type_list); | 
|  | } | 
|  |  | 
|  | /* Mark beginning of a table body.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_table_body () | 
|  | { | 
|  | /* close the table header line if there were any headers */ | 
|  | close (ui_out_type_list); | 
|  | open ("body", ui_out_type_list); | 
|  | } | 
|  |  | 
|  | /* Mark end of a table.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_table_end () | 
|  | { | 
|  | close (ui_out_type_list); /* body */ | 
|  | close (ui_out_type_tuple); | 
|  | } | 
|  |  | 
|  | /* Specify table header.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_table_header (int width, ui_align alignment, | 
|  | const std::string &col_name, | 
|  | const std::string &col_hdr) | 
|  | { | 
|  | open (NULL, ui_out_type_tuple); | 
|  | do_field_signed (0, 0, ui_center, "width", width, ui_file_style ()); | 
|  | do_field_signed (0, 0, ui_center, "alignment", alignment, ui_file_style ()); | 
|  | do_field_string (0, 0, ui_center, "col_name", col_name.c_str (), | 
|  | ui_file_style ()); | 
|  | do_field_string (0, width, alignment, "colhdr", col_hdr.c_str (), | 
|  | ui_file_style ()); | 
|  | close (ui_out_type_tuple); | 
|  | } | 
|  |  | 
|  | /* Mark beginning of a list.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_begin (ui_out_type type, const char *id) | 
|  | { | 
|  | open (id, type); | 
|  | } | 
|  |  | 
|  | /* Mark end of a list.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_end (ui_out_type type) | 
|  | { | 
|  | close (type); | 
|  | } | 
|  |  | 
|  | /* Output an int field.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_field_signed (int fldno, int width, ui_align alignment, | 
|  | const char *fldname, LONGEST value, | 
|  | const ui_file_style &style) | 
|  | { | 
|  | do_field_string (fldno, width, alignment, fldname, plongest (value), | 
|  | style); | 
|  | } | 
|  |  | 
|  | /* Output an unsigned field.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_field_unsigned (int fldno, int width, ui_align alignment, | 
|  | const char *fldname, ULONGEST value) | 
|  | { | 
|  | do_field_string (fldno, width, alignment, fldname, pulongest (value), | 
|  | ui_file_style ()); | 
|  | } | 
|  |  | 
|  | /* Used to omit a field.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_field_skip (int fldno, int width, ui_align alignment, | 
|  | const char *fldname) | 
|  | { | 
|  | } | 
|  |  | 
|  | /* Other specific mi_field_* end up here so alignment and field | 
|  | separators are both handled by mi_field_string. */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_field_string (int fldno, int width, ui_align align, | 
|  | const char *fldname, const char *string, | 
|  | const ui_file_style &style) | 
|  | { | 
|  | ui_file *stream = m_streams.back (); | 
|  | field_separator (); | 
|  |  | 
|  | if (fldname) | 
|  | gdb_printf (stream, "%s=", fldname); | 
|  | gdb_printf (stream, "\""); | 
|  | if (string) | 
|  | stream->putstr (string, '"'); | 
|  | gdb_printf (stream, "\""); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_field_fmt (int fldno, int width, ui_align align, | 
|  | const char *fldname, const ui_file_style &style, | 
|  | const char *format, va_list args) | 
|  | { | 
|  | ui_file *stream = m_streams.back (); | 
|  | field_separator (); | 
|  |  | 
|  | if (fldname) | 
|  | gdb_printf (stream, "%s=\"", fldname); | 
|  | else | 
|  | gdb_puts ("\"", stream); | 
|  | gdb_vprintf (stream, format, args); | 
|  | gdb_puts ("\"", stream); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_spaces (int numspaces) | 
|  | { | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_text (const char *string) | 
|  | { | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_message (const ui_file_style &style, | 
|  | const char *format, va_list args) | 
|  | { | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_wrap_hint (int indent) | 
|  | { | 
|  | m_streams.back ()->wrap_here (indent); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_flush () | 
|  | { | 
|  |  | 
|  | gdb_flush (m_streams.back ()); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_redirect (ui_file *outstream) | 
|  | { | 
|  | if (outstream != NULL) | 
|  | m_streams.push_back (outstream); | 
|  | else | 
|  | m_streams.pop_back (); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::field_separator () | 
|  | { | 
|  | if (m_suppress_field_separator) | 
|  | m_suppress_field_separator = false; | 
|  | else | 
|  | gdb_putc (',', m_streams.back ()); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::open (const char *name, ui_out_type type) | 
|  | { | 
|  | ui_file *stream = m_streams.back (); | 
|  |  | 
|  | field_separator (); | 
|  | m_suppress_field_separator = true; | 
|  |  | 
|  | if (name) | 
|  | gdb_printf (stream, "%s=", name); | 
|  |  | 
|  | switch (type) | 
|  | { | 
|  | case ui_out_type_tuple: | 
|  | gdb_putc ('{', stream); | 
|  | break; | 
|  |  | 
|  | case ui_out_type_list: | 
|  | gdb_putc ('[', stream); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | internal_error (_("bad switch")); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_ui_out::close (ui_out_type type) | 
|  | { | 
|  | ui_file *stream = m_streams.back (); | 
|  |  | 
|  | switch (type) | 
|  | { | 
|  | case ui_out_type_tuple: | 
|  | gdb_putc ('}', stream); | 
|  | break; | 
|  |  | 
|  | case ui_out_type_list: | 
|  | gdb_putc (']', stream); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | internal_error (_("bad switch")); | 
|  | } | 
|  |  | 
|  | m_suppress_field_separator = false; | 
|  | } | 
|  |  | 
|  | string_file * | 
|  | mi_ui_out::main_stream () | 
|  | { | 
|  | gdb_assert (m_streams.size () == 1); | 
|  |  | 
|  | return (string_file *) m_streams.back (); | 
|  | } | 
|  |  | 
|  | /* Initialize a progress update to be displayed with | 
|  | mi_ui_out::do_progress_notify.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_progress_start () | 
|  | { | 
|  | m_progress_info.emplace_back (); | 
|  | } | 
|  |  | 
|  | /* Indicate that a task described by MSG is in progress.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_progress_notify (const std::string &msg, const char *unit, | 
|  | double cur, double total) | 
|  | { | 
|  | mi_progress_info &info (m_progress_info.back ()); | 
|  |  | 
|  | if (info.state == progress_update::START) | 
|  | { | 
|  | gdb_printf ("%s...\n", msg.c_str ()); | 
|  | info.state = progress_update::WORKING; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Remove the most recent progress update from the progress_info stack.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::do_progress_end () | 
|  | { | 
|  | m_progress_info.pop_back (); | 
|  | } | 
|  |  | 
|  | /* Clear the buffer.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::rewind () | 
|  | { | 
|  | main_stream ()->clear (); | 
|  | } | 
|  |  | 
|  | /* Dump the buffer onto the specified stream.  */ | 
|  |  | 
|  | void | 
|  | mi_ui_out::put (ui_file *where) | 
|  | { | 
|  | string_file *mi_stream = main_stream (); | 
|  |  | 
|  | where->write (mi_stream->data (), mi_stream->size ()); | 
|  | mi_stream->clear (); | 
|  | } | 
|  |  | 
|  | /* Return the current MI version.  */ | 
|  |  | 
|  | int | 
|  | mi_ui_out::version () | 
|  | { | 
|  | return m_mi_version; | 
|  | } | 
|  |  | 
|  | /* Constructor for an `mi_out_data' object.  */ | 
|  |  | 
|  | mi_ui_out::mi_ui_out (int mi_version) | 
|  | : ui_out (make_flags (mi_version)), | 
|  | m_suppress_field_separator (false), | 
|  | m_suppress_output (false), | 
|  | m_mi_version (mi_version) | 
|  | { | 
|  | string_file *stream = new string_file (); | 
|  | m_streams.push_back (stream); | 
|  | } | 
|  |  | 
|  | mi_ui_out::~mi_ui_out () | 
|  | { | 
|  | } | 
|  |  | 
|  | /* See mi/mi-out.h.  */ | 
|  |  | 
|  | std::unique_ptr<mi_ui_out> | 
|  | mi_out_new (const char *mi_version) | 
|  | { | 
|  | if (streq (mi_version, INTERP_MI4) ||  streq (mi_version, INTERP_MI)) | 
|  | return std::make_unique<mi_ui_out> (4); | 
|  |  | 
|  | if (streq (mi_version, INTERP_MI3)) | 
|  | return std::make_unique<mi_ui_out> (3); | 
|  |  | 
|  | if (streq (mi_version, INTERP_MI2)) | 
|  | return std::make_unique<mi_ui_out> (2); | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | /* Helper function to return the given UIOUT as an mi_ui_out.  It is an error | 
|  | to call this function with an ui_out that is not an MI.  */ | 
|  |  | 
|  | static mi_ui_out * | 
|  | as_mi_ui_out (ui_out *uiout) | 
|  | { | 
|  | return gdb::checked_static_cast<mi_ui_out *> (uiout); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_out_put (ui_out *uiout, struct ui_file *stream) | 
|  | { | 
|  | return as_mi_ui_out (uiout)->put (stream); | 
|  | } | 
|  |  | 
|  | void | 
|  | mi_out_rewind (ui_out *uiout) | 
|  | { | 
|  | return as_mi_ui_out (uiout)->rewind (); | 
|  | } |