/* Copyright (C) 2025, 2026 Free Software Foundation, Inc.
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 . */
#include "buffered-streams.h"
#include "ui-out.h"
/* See buffered-streams.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 buffered-streams.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 (stream, msg);
prev = cur + 1;
}
}
/* See buffered-streams.h. */
void
buffer_group::wrap_here (int indent, ui_file *stream)
{
m_buffered_output.emplace_back (stream, "", indent);
}
/* See buffered-streams.h. */
void
buffer_group::flush_here (ui_file *stream)
{
m_buffered_output.emplace_back (stream, "", -1, true);
}
/* See buffered-streams.h. */
ui_file *
get_unbuffered (ui_file *stream)
{
while (true)
{
buffering_file *buf = dynamic_cast (stream);
if (buf == nullptr)
return stream;
stream = buf->stream ();
}
}
buffered_streams::buffered_streams (buffer_group *group, ui_out *uiout)
: m_buffered_stdout (group, *redirectable_stdout ()),
m_buffered_stderr (group, *redirectable_stderr ()),
m_buffered_stdlog (group, *redirectable_stdlog ()),
m_buffered_stdtarg (group, *redirectable_stdtarg ()),
m_uiout (uiout)
{
*redirectable_stdout () = &m_buffered_stdout;
*redirectable_stderr () = &m_buffered_stderr;
*redirectable_stdlog () = &m_buffered_stdlog;
*redirectable_stdtarg () = &m_buffered_stdtarg;
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 buffered-streams.h. */
void
buffered_streams::remove_buffers ()
{
if (!m_buffers_in_place)
return;
m_buffers_in_place = false;
*redirectable_stdout () = m_buffered_stdout.stream ();
*redirectable_stderr () = m_buffered_stderr.stream ();
*redirectable_stdlog () = m_buffered_stdlog.stream ();
*redirectable_stdtarg () = m_buffered_stdtarg.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 buffered-streams.h. */
void
buffer_group::flush () const
{
m_buffered_streams->remove_buffers ();
for (const output_unit &ou : m_buffered_output)
ou.flush ();
}