aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/cli-out.c10
-rw-r--r--gdb/cli-out.h3
-rw-r--r--gdb/debuginfod-support.c15
-rw-r--r--gdb/infrun.c16
-rw-r--r--gdb/mi/mi-out.h3
-rw-r--r--gdb/python/py-uiout.h3
-rw-r--r--gdb/stack.c35
-rw-r--r--gdb/thread.c180
-rw-r--r--gdb/ui-out.c141
-rw-r--r--gdb/ui-out.h184
10 files changed, 492 insertions, 98 deletions
diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 69fd069..2a1e721 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -299,7 +299,7 @@ cli_ui_out::do_progress_notify (const std::string &msg,
double howmuch, double total)
{
int chars_per_line = get_chars_per_line ();
- struct ui_file *stream = m_streams.back ();
+ struct ui_file *stream = get_unbuffered (m_streams.back ());
cli_progress_info &info (m_progress_info.back ());
if (chars_per_line > MAX_CHARS_PER_LINE)
@@ -384,7 +384,7 @@ cli_ui_out::do_progress_notify (const std::string &msg,
void
cli_ui_out::clear_progress_notify ()
{
- struct ui_file *stream = m_streams.back ();
+ struct ui_file *stream = get_unbuffered (m_streams.back ());
int chars_per_line = get_chars_per_line ();
scoped_restore save_pagination
@@ -413,10 +413,12 @@ void
cli_ui_out::do_progress_end ()
{
struct ui_file *stream = m_streams.back ();
- m_progress_info.pop_back ();
+ cli_progress_info &info (m_progress_info.back ());
- if (stream->isatty ())
+ if (stream->isatty () && info.state != progress_update::START)
clear_progress_notify ();
+
+ m_progress_info.pop_back ();
}
/* local functions */
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index 9bffdf3..f17cb0c 100644
--- a/gdb/cli-out.h
+++ b/gdb/cli-out.h
@@ -35,6 +35,9 @@ public:
bool can_emit_style_escape () const override;
+ ui_file *current_stream () const override
+ { return m_streams.back (); }
+
protected:
virtual void do_table_begin (int nbrofcols, int nr_rows,
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 023479a..7d8ada3 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -155,7 +155,8 @@ progressfn (debuginfod_client *c, long cur, long total)
if (check_quit_flag ())
{
- gdb_printf ("Cancelling download of %s %s...\n",
+ ui_file *outstream = get_unbuffered (gdb_stdout);
+ gdb_printf (outstream, _("Cancelling download of %s %s...\n"),
data->desc, styled_fname.c_str ());
return 1;
}
@@ -295,10 +296,14 @@ static void
print_outcome (int fd, const char *desc, const char *fname)
{
if (fd < 0 && fd != -ENOENT)
- gdb_printf (_("Download failed: %s. Continuing without %s %ps.\n"),
- safe_strerror (-fd),
- desc,
- styled_string (file_name_style.style (), fname));
+ {
+ ui_file *outstream = get_unbuffered (gdb_stdout);
+ gdb_printf (outstream,
+ _("Download failed: %s. Continuing without %s %ps.\n"),
+ safe_strerror (-fd),
+ desc,
+ styled_string (file_name_style.style (), fname));
+ }
}
/* See debuginfod-support.h */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3f0f69b..d8c1bca 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -9277,10 +9277,10 @@ print_stop_location (const target_waitstatus &ws)
print_stack_frame (get_selected_frame (nullptr), 0, source_flag, 1);
}
-/* See infrun.h. */
+/* See `print_stop_event` in infrun.h. */
-void
-print_stop_event (struct ui_out *uiout, bool displays)
+static void
+do_print_stop_event (struct ui_out *uiout, bool displays)
{
struct target_waitstatus last;
struct thread_info *tp;
@@ -9309,6 +9309,16 @@ print_stop_event (struct ui_out *uiout, bool displays)
}
}
+/* See infrun.h. This function itself sets up buffered output for the
+ duration of do_print_stop_event, which performs the actual event
+ printing. */
+
+void
+print_stop_event (struct ui_out *uiout, bool displays)
+{
+ do_with_buffered_output (do_print_stop_event, uiout, displays);
+}
+
/* See infrun.h. */
void
diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h
index cffd8e4..a7afb15 100644
--- a/gdb/mi/mi-out.h
+++ b/gdb/mi/mi-out.h
@@ -45,6 +45,9 @@ public:
return false;
}
+ ui_file *current_stream () const override
+ { return m_streams.back (); }
+
protected:
virtual void do_table_begin (int nbrofcols, int nr_rows, const char *tblid)
diff --git a/gdb/python/py-uiout.h b/gdb/python/py-uiout.h
index e80e272..a2fc90e 100644
--- a/gdb/python/py-uiout.h
+++ b/gdb/python/py-uiout.h
@@ -56,6 +56,9 @@ public:
return std::move (current ().obj);
}
+ ui_file *current_stream () const override
+ { return nullptr; }
+
protected:
void do_progress_end () override { }
diff --git a/gdb/stack.c b/gdb/stack.c
index 2de3e36..96a9cd4 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -220,7 +220,8 @@ static void print_frame_local_vars (frame_info_ptr frame,
const char *regexp, const char *t_regexp,
int num_tabs, struct ui_file *stream);
-static void print_frame (const frame_print_options &opts,
+static void print_frame (struct ui_out *uiout,
+ const frame_print_options &opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
struct symtab_and_line sal);
@@ -1020,16 +1021,15 @@ get_user_print_what_frame_info (std::optional<enum print_what> *what)
Used in "where" output, and to emit breakpoint or step
messages. */
-void
-print_frame_info (const frame_print_options &fp_opts,
- frame_info_ptr frame, int print_level,
- enum print_what print_what, int print_args,
- int set_current_sal)
+static void
+do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
+ frame_info_ptr frame, int print_level,
+ enum print_what print_what, int print_args,
+ int set_current_sal)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
int source_print;
int location_print;
- struct ui_out *uiout = current_uiout;
if (!current_uiout->is_mi_like_p ()
&& fp_opts.print_frame_info != print_frame_info_auto)
@@ -1105,7 +1105,8 @@ print_frame_info (const frame_print_options &fp_opts,
|| print_what == LOC_AND_ADDRESS
|| print_what == SHORT_LOCATION);
if (location_print || !sal.symtab)
- print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
+ print_frame (uiout, fp_opts, frame, print_level,
+ print_what, print_args, sal);
source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
@@ -1185,6 +1186,20 @@ print_frame_info (const frame_print_options &fp_opts,
gdb_flush (gdb_stdout);
}
+/* Redirect output to a temporary buffer for the duration
+ of do_print_frame_info. */
+
+void
+print_frame_info (const frame_print_options &fp_opts,
+ frame_info_ptr frame, int print_level,
+ enum print_what print_what, int print_args,
+ int set_current_sal)
+{
+ do_with_buffered_output (do_print_frame_info, current_uiout,
+ fp_opts, frame, print_level, print_what,
+ print_args, set_current_sal);
+}
+
/* See stack.h. */
void
@@ -1309,13 +1324,13 @@ find_frame_funname (frame_info_ptr frame, enum language *funlang,
}
static void
-print_frame (const frame_print_options &fp_opts,
+print_frame (struct ui_out *uiout,
+ const frame_print_options &fp_opts,
frame_info_ptr frame, int print_level,
enum print_what print_what, int print_args,
struct symtab_and_line sal)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
- struct ui_out *uiout = current_uiout;
enum language funlang = language_unknown;
struct value_print_options opts;
struct symbol *func;
diff --git a/gdb/thread.c b/gdb/thread.c
index 62fd9c6..2252356 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1089,6 +1089,107 @@ thread_target_id_str (thread_info *tp)
return target_id;
}
+/* Print thread TP. GLOBAL_IDS indicates whether REQUESTED_THREADS
+ is a list of global or per-inferior thread ids. */
+
+static void
+do_print_thread (ui_out *uiout, const char *requested_threads,
+ int global_ids, int pid, int show_global_ids,
+ int default_inf_num, thread_info *tp,
+ thread_info *current_thread)
+{
+ int core;
+
+ /* In case REQUESTED_THREADS contains $_thread. */
+ if (current_thread != nullptr)
+ switch_to_thread (current_thread);
+
+ if (!should_print_thread (requested_threads, default_inf_num,
+ global_ids, pid, tp))
+ return;
+
+ ui_out_emit_tuple tuple_emitter (uiout, NULL);
+
+ if (!uiout->is_mi_like_p ())
+ {
+ if (tp == current_thread)
+ uiout->field_string ("current", "*");
+ else
+ uiout->field_skip ("current");
+
+ uiout->field_string ("id-in-tg", print_thread_id (tp));
+ }
+
+ if (show_global_ids || uiout->is_mi_like_p ())
+ uiout->field_signed ("id", tp->global_num);
+
+ /* Switch to the thread (and inferior / target). */
+ switch_to_thread (tp);
+
+ /* For the CLI, we stuff everything into the target-id field.
+ This is a gross hack to make the output come out looking
+ correct. The underlying problem here is that ui-out has no
+ way to specify that a field's space allocation should be
+ shared by several fields. For MI, we do the right thing
+ instead. */
+
+ if (uiout->is_mi_like_p ())
+ {
+ uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
+
+ const char *extra_info = target_extra_thread_info (tp);
+ if (extra_info != nullptr)
+ uiout->field_string ("details", extra_info);
+
+ const char *name = thread_name (tp);
+ if (name != NULL)
+ uiout->field_string ("name", name);
+ }
+ else
+ {
+ uiout->field_string ("target-id", thread_target_id_str (tp));
+ }
+
+ if (tp->state == THREAD_RUNNING)
+ uiout->text ("(running)\n");
+ else
+ {
+ /* The switch above put us at the top of the stack (leaf
+ frame). */
+ print_stack_frame (get_selected_frame (NULL),
+ /* For MI output, print frame level. */
+ uiout->is_mi_like_p (),
+ LOCATION, 0);
+ }
+
+ if (uiout->is_mi_like_p ())
+ {
+ const char *state = "stopped";
+
+ if (tp->state == THREAD_RUNNING)
+ state = "running";
+ uiout->field_string ("state", state);
+ }
+
+ core = target_core_of_thread (tp->ptid);
+ if (uiout->is_mi_like_p () && core != -1)
+ uiout->field_signed ("core", core);
+}
+
+/* Redirect output to a temporary buffer for the duration
+ of do_print_thread. */
+
+static void
+print_thread (ui_out *uiout, const char *requested_threads,
+ int global_ids, int pid, int show_global_ids,
+ int default_inf_num, thread_info *tp, thread_info *current_thread)
+
+{
+ do_with_buffered_output (do_print_thread, uiout, requested_threads,
+ global_ids, pid, show_global_ids,
+ default_inf_num, tp, current_thread);
+}
+
/* Like print_thread_info, but in addition, GLOBAL_IDS indicates
whether REQUESTED_THREADS is a list of global or per-inferior
thread ids. */
@@ -1176,86 +1277,13 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
for (inferior *inf : all_inferiors ())
for (thread_info *tp : inf->threads ())
{
- int core;
-
any_thread = true;
+
if (tp == current_thread && tp->state == THREAD_EXITED)
current_exited = true;
- /* In case REQUESTED_THREADS contains $_thread. */
- if (current_thread != nullptr)
- switch_to_thread (current_thread);
-
- if (!should_print_thread (requested_threads, default_inf_num,
- global_ids, pid, tp))
- continue;
-
- ui_out_emit_tuple tuple_emitter (uiout, NULL);
-
- if (!uiout->is_mi_like_p ())
- {
- if (tp == current_thread)
- uiout->field_string ("current", "*");
- else
- uiout->field_skip ("current");
-
- uiout->field_string ("id-in-tg", print_thread_id (tp));
- }
-
- if (show_global_ids || uiout->is_mi_like_p ())
- uiout->field_signed ("id", tp->global_num);
-
- /* Switch to the thread (and inferior / target). */
- switch_to_thread (tp);
-
- /* For the CLI, we stuff everything into the target-id field.
- This is a gross hack to make the output come out looking
- correct. The underlying problem here is that ui-out has no
- way to specify that a field's space allocation should be
- shared by several fields. For MI, we do the right thing
- instead. */
-
- if (uiout->is_mi_like_p ())
- {
- uiout->field_string ("target-id", target_pid_to_str (tp->ptid));
-
- const char *extra_info = target_extra_thread_info (tp);
- if (extra_info != nullptr)
- uiout->field_string ("details", extra_info);
-
- const char *name = thread_name (tp);
- if (name != NULL)
- uiout->field_string ("name", name);
- }
- else
- {
- uiout->field_string ("target-id", thread_target_id_str (tp));
- }
-
- if (tp->state == THREAD_RUNNING)
- uiout->text ("(running)\n");
- else
- {
- /* The switch above put us at the top of the stack (leaf
- frame). */
- print_stack_frame (get_selected_frame (NULL),
- /* For MI output, print frame level. */
- uiout->is_mi_like_p (),
- LOCATION, 0);
- }
-
- if (uiout->is_mi_like_p ())
- {
- const char *state = "stopped";
-
- if (tp->state == THREAD_RUNNING)
- state = "running";
- uiout->field_string ("state", state);
- }
-
- core = target_core_of_thread (tp->ptid);
- if (uiout->is_mi_like_p () && core != -1)
- uiout->field_signed ("core", core);
+ print_thread (uiout, requested_threads, global_ids, pid,
+ show_global_ids, default_inf_num, tp, current_thread);
}
/* This end scope restores the current thread and the frame
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 0b96231..23f4ce0 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -871,3 +871,144 @@ ui_out::ui_out (ui_out_flags flags)
ui_out::~ui_out ()
{
}
+
+/* See ui-out.h. */
+
+void
+buffer_group::output_unit::flush () const
+{
+ if (!m_msg.empty ())
+ m_stream->puts (m_msg.c_str ());
+
+ if (m_wrap_hint >= 0)
+ m_stream->wrap_here (m_wrap_hint);
+
+ if (m_flush)
+ m_stream->flush ();
+}
+
+/* See ui-out.h. */
+
+void
+buffer_group::write (const char *buf, long length_buf, ui_file *stream)
+{
+ /* Record each line separately. */
+ for (size_t prev = 0, cur = 0; cur < length_buf; ++cur)
+ if (buf[cur] == '\n' || cur == length_buf - 1)
+ {
+ std::string msg (buf + prev, cur - prev + 1);
+
+ if (m_buffered_output.size () > 0
+ && m_buffered_output.back ().m_wrap_hint == -1
+ && m_buffered_output.back ().m_stream == stream
+ && m_buffered_output.back ().m_msg.size () > 0
+ && m_buffered_output.back ().m_msg.back () != '\n')
+ m_buffered_output.back ().m_msg.append (msg);
+ else
+ {
+ m_buffered_output.emplace_back (msg);
+ m_buffered_output.back ().m_stream = stream;
+ }
+ prev = cur + 1;
+ }
+}
+
+/* See ui-out.h. */
+
+void
+buffer_group::wrap_here (int indent, ui_file *stream)
+{
+ m_buffered_output.emplace_back ("", indent);
+ m_buffered_output.back ().m_stream = stream;
+}
+
+/* See ui-out.h. */
+
+void
+buffer_group::flush_here (ui_file *stream)
+{
+ m_buffered_output.emplace_back ("", -1, true);
+ m_buffered_output.back ().m_stream = stream;
+}
+
+/* See ui-out.h. */
+
+ui_file *
+get_unbuffered (ui_file *stream)
+{
+ buffering_file *buf = dynamic_cast<buffering_file *> (stream);
+
+ if (buf == nullptr)
+ return stream;
+
+ return get_unbuffered (buf->stream ());
+}
+
+buffered_streams::buffered_streams (buffer_group *group, ui_out *uiout)
+ : m_buffered_stdout (group, gdb_stdout),
+ m_buffered_stderr (group, gdb_stderr),
+ m_buffered_stdlog (group, gdb_stdlog),
+ m_buffered_stdtarg (group, gdb_stdtarg),
+ m_buffered_stdtargerr (group, gdb_stdtargerr),
+ m_uiout (uiout)
+{
+ gdb_stdout = &m_buffered_stdout;
+ gdb_stderr = &m_buffered_stderr;
+ gdb_stdlog = &m_buffered_stdlog;
+ gdb_stdtarg = &m_buffered_stdtarg;
+ gdb_stdtargerr = &m_buffered_stdtargerr;
+
+ ui_file *stream = current_uiout->current_stream ();
+ if (stream != nullptr)
+ {
+ m_buffered_current_uiout.emplace (group, stream);
+ current_uiout->redirect (&(*m_buffered_current_uiout));
+ }
+
+ stream = m_uiout->current_stream ();
+ if (stream != nullptr && current_uiout != m_uiout)
+ {
+ m_buffered_uiout.emplace (group, stream);
+ m_uiout->redirect (&(*m_buffered_uiout));
+ }
+
+ m_buffers_in_place = true;
+}
+
+/* See ui-out.h. */
+
+void
+buffered_streams::remove_buffers ()
+{
+ if (!m_buffers_in_place)
+ return;
+
+ m_buffers_in_place = false;
+
+ gdb_stdout = m_buffered_stdout.stream ();
+ gdb_stderr = m_buffered_stderr.stream ();
+ gdb_stdlog = m_buffered_stdlog.stream ();
+ gdb_stdtarg = m_buffered_stdtarg.stream ();
+ gdb_stdtargerr = m_buffered_stdtargerr.stream ();
+
+ if (m_buffered_current_uiout.has_value ())
+ current_uiout->redirect (nullptr);
+
+ if (m_buffered_uiout.has_value ())
+ m_uiout->redirect (nullptr);
+}
+
+buffer_group::buffer_group (ui_out *uiout)
+ : m_buffered_streams (new buffered_streams (this, uiout))
+{ /* Nothing. */ }
+
+/* See ui-out.h. */
+
+void
+buffer_group::flush () const
+{
+ m_buffered_streams->remove_buffers ();
+
+ for (const output_unit &ou : m_buffered_output)
+ ou.flush ();
+}
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index fd54b7b..71add90 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -278,6 +278,9 @@ class ui_out
escapes. */
virtual bool can_emit_style_escape () const = 0;
+ /* Return the ui_file currently used for output. */
+ virtual ui_file *current_stream () const = 0;
+
/* An object that starts and finishes displaying progress updates. */
class progress_update
{
@@ -470,4 +473,185 @@ private:
struct ui_out *m_uiout;
};
+struct buffered_streams;
+
+/* Organizes writes to a collection of buffered output streams
+ so that when flushed, output is written to all streams in
+ chronological order. */
+
+struct buffer_group
+{
+ buffer_group (ui_out *uiout);
+
+ /* Flush all buffered writes to the underlying output streams. */
+ void flush () const;
+
+ /* Record contents of BUF and associate it with STREAM. */
+ void write (const char *buf, long length_buf, ui_file *stream);
+
+ /* Record a wrap_here and associate it with STREAM. */
+ void wrap_here (int indent, ui_file *stream);
+
+ /* Record a call to flush and associate it with STREAM. */
+ void flush_here (ui_file *stream);
+
+private:
+
+ struct output_unit
+ {
+ output_unit (std::string msg, int wrap_hint = -1, bool flush = false)
+ : m_msg (msg), m_wrap_hint (wrap_hint), m_flush (flush)
+ {}
+
+ /* Write contents of this output_unit to the underlying stream. */
+ void flush () const;
+
+ /* Underlying stream for which this output unit will be written to. */
+ ui_file *m_stream;
+
+ /* String to be written to underlying buffer. */
+ std::string m_msg;
+
+ /* Argument to wrap_here. -1 indicates no wrap. Used to call wrap_here
+ during buffer flush. */
+ int m_wrap_hint;
+
+ /* Indicate that the underlying output stream's flush should be called. */
+ bool m_flush;
+ };
+
+ /* Output_units to be written to buffered output streams. */
+ std::vector<output_unit> m_buffered_output;
+
+ /* Buffered output streams. */
+ std::unique_ptr<buffered_streams> m_buffered_streams;
+};
+
+/* If FILE is a buffering_file, return it's underlying stream. */
+
+extern ui_file *get_unbuffered (ui_file *file);
+
+/* Buffer output to gdb_stdout and gdb_stderr for the duration of FUNC. */
+
+template<typename F, typename... Arg>
+void
+do_with_buffered_output (F func, ui_out *uiout, Arg... args)
+{
+ buffer_group g (uiout);
+
+ try
+ {
+ func (uiout, std::forward<Arg> (args)...);
+ }
+ catch (const gdb_exception &ex)
+ {
+ /* Ideally flush would be called in the destructor of buffer_group,
+ however flushing might cause an exception to be thrown. Catch it
+ and ensure the first exception propagates. */
+ try
+ {
+ g.flush ();
+ }
+ catch (const gdb_exception &)
+ {
+ }
+
+ throw (std::move (ex));
+ }
+
+ /* Try was successful. Let any further exceptions propagate. */
+ g.flush ();
+}
+
+/* Accumulate writes to an underlying ui_file. Output to the
+ underlying file is deferred until required. */
+
+struct buffering_file : public ui_file
+{
+ buffering_file (buffer_group *group, ui_file *stream)
+ : m_group (group),
+ m_stream (stream)
+ { /* Nothing. */ }
+
+ /* Return the underlying output stream. */
+ ui_file *stream () const
+ {
+ return m_stream;
+ }
+
+ /* Record the contents of BUF. */
+ void write (const char *buf, long length_buf) override
+ {
+ m_group->write (buf, length_buf, m_stream);
+ }
+
+ /* Record a wrap_here call with argument INDENT. */
+ void wrap_here (int indent) override
+ {
+ m_group->wrap_here (indent, m_stream);
+ }
+
+ /* Return true if the underlying stream is a tty. */
+ bool isatty () override
+ {
+ return m_stream->isatty ();
+ }
+
+ /* Return true if ANSI escapes can be used on the underlying stream. */
+ bool can_emit_style_escape () override
+ {
+ return m_stream->can_emit_style_escape ();
+ }
+
+ /* Flush the underlying output stream. */
+ void flush () override
+ {
+ return m_group->flush_here (m_stream);
+ }
+
+private:
+
+ /* Coordinates buffering across multiple buffering_files. */
+ buffer_group *m_group;
+
+ /* The underlying output stream. */
+ ui_file *m_stream;
+};
+
+/* Attaches and detaches buffers for each of the gdb_std* streams. */
+
+struct buffered_streams
+{
+ buffered_streams (buffer_group *group, ui_out *uiout);
+
+ ~buffered_streams ()
+ {
+ this->remove_buffers ();
+ }
+
+ /* Remove buffering_files from all underlying streams. */
+ void remove_buffers ();
+
+private:
+
+ /* True if buffers are still attached to each underlying output stream. */
+ bool m_buffers_in_place;
+
+ /* Buffers for each gdb_std* output stream. */
+ buffering_file m_buffered_stdout;
+ buffering_file m_buffered_stderr;
+ buffering_file m_buffered_stdlog;
+ buffering_file m_buffered_stdtarg;
+ buffering_file m_buffered_stdtargerr;
+
+ /* Buffer for current_uiout's output stream. */
+ std::optional<buffering_file> m_buffered_current_uiout;
+
+ /* Additional ui_out being buffered. */
+ ui_out *m_uiout;
+
+ /* Buffer for m_uiout's output stream. */
+ std::optional<buffering_file> m_buffered_uiout;
+};
+
#endif /* UI_OUT_H */