#include "cli/cli-cmds.h"
#include "ui-out.h"
#include "interps.h"
+#include "logging-file.h"
#include "cli/cli-style.h"
#include "cli/cli-decode.h"
gdb_printf (file, _("off: Logging appends to the log file.\n"));
}
+/* The current log file, or nullptr if none. */
+static ui_file_up log_file;
+
/* Value as configured by the user. */
static bool logging_redirect;
static bool debug_redirect;
_("off: Debug output will go to both the screen and the log file.\n"));
}
+/* Values as used by the logging_file implementation. These are
+ separate and only set when logging is enabled, because historically
+ gdb required you to disable and re-enable logging to change these
+ settings. */
+
+static bool logging_redirect_for_file;
+static bool debug_redirect_for_file;
+
+/* See logging-file.h. */
+
+template<typename T>
+bool
+logging_file<T>::ordinary_output () const
+{
+ if (log_file == nullptr)
+ return true;
+ if (logging_redirect_for_file)
+ return false;
+ if (debug_redirect_for_file)
+ return !m_for_stdlog;
+ return true;
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::flush ()
+{
+ if (log_file != nullptr)
+ log_file->flush ();
+ /* Always flushing seems fine. */
+ m_out->flush ();
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+bool
+logging_file<T>::can_page () const
+{
+ /* If all output is redirected, do not page. */
+ if (!ordinary_output ())
+ return false;
+ /* In other cases, paging happens if the underlying stream can
+ page. */
+ return m_out->can_page ();
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::write (const char *buf, long length_buf)
+{
+ if (log_file != nullptr)
+ log_file->write (buf, length_buf);
+ if (ordinary_output ())
+ m_out->write (buf, length_buf);
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::write_async_safe (const char *buf, long length_buf)
+{
+ if (log_file != nullptr)
+ log_file->write_async_safe (buf, length_buf);
+ if (ordinary_output ())
+ m_out->write_async_safe (buf, length_buf);
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::puts (const char *linebuffer)
+{
+ if (log_file != nullptr)
+ log_file->puts (linebuffer);
+ if (ordinary_output ())
+ m_out->puts (linebuffer);
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::emit_style_escape (const ui_file_style &style)
+{
+ if (log_file != nullptr)
+ log_file->emit_style_escape (style);
+ if (ordinary_output ())
+ m_out->emit_style_escape (style);
+}
+
+/* See logging-file.h. */
+
+template<typename T>
+void
+logging_file<T>::puts_unfiltered (const char *str)
+{
+ if (log_file != nullptr)
+ log_file->puts_unfiltered (str);
+ if (ordinary_output ())
+ m_out->puts_unfiltered (str);
+}
+
+/* The available instantiations of logging_file. */
+template class logging_file<ui_file *>;
+template class logging_file<ui_file_up>;
+
/* If we've pushed output files, close them and pop them. */
static void
pop_output_files (void)
}
saved_filename = logging_filename;
+ logging_redirect_for_file = logging_redirect;
+ debug_redirect_for_file = debug_redirect;
/* Let the interpreter do anything it needs. */
current_interp_set_logging (std::move (log), logging_redirect,
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_LOGGING_FILE_H
+#define GDB_LOGGING_FILE_H
+
+#include "ui-file.h"
+
+/* A ui_file implementation that optionally writes its output to a
+ second logging stream. Whether logging is actually done depends on
+ the user's logging settings. The precise underlying ui_file type
+ is a template parameter, so that either owning or non-owning
+ instances can be made. */
+
+template<typename T>
+class logging_file : public ui_file
+{
+public:
+ /* This wraps another stream. Whether or not output actually goes
+ to that stream depends on the redirection settings. FOR_STDLOG
+ should only be set for a stream intended by use as gdb_stdlog;
+ this is used to implement the "debug redirect" feature. */
+ logging_file (T out, bool for_stdlog = false)
+ : m_out (std::move (out)),
+ m_for_stdlog (for_stdlog)
+ {
+ }
+
+ void write (const char *buf, long length_buf) override;
+ void write_async_safe (const char *buf, long length_buf) override;
+ void puts (const char *) override;
+ void flush () override;
+ bool can_page () const override;
+ void emit_style_escape (const ui_file_style &style) override;
+ void puts_unfiltered (const char *str) override;
+
+ bool isatty () override
+ {
+ /* Defer to the wrapped file. */
+ return m_out->isatty ();
+ }
+
+ bool term_out () override
+ {
+ /* Defer to the wrapped file. */
+ return m_out->term_out ();
+ }
+
+ bool can_emit_style_escape () override
+ {
+ /* Defer to the wrapped file. */
+ return m_out->can_emit_style_escape ();
+ }
+
+private:
+ /* A helper function that returns true if output should go to
+ M_OUT. */
+ bool ordinary_output () const;
+
+ /* The underlying file. */
+ T m_out;
+
+ /* True if this stream is used for gdb_stdlog. This is used to
+ implement the debug redirect feature. */
+ bool m_for_stdlog;
+};
+
+#endif /* GDB_LOGGING_FILE_H */