diff options
Diffstat (limited to 'gdb/ui-out.h')
-rw-r--r-- | gdb/ui-out.h | 184 |
1 files changed, 184 insertions, 0 deletions
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 */ |