diff --git a/dnf5-plugins/automatic_plugin/automatic.cpp b/dnf5-plugins/automatic_plugin/automatic.cpp index 3f4c4efac..d53555b70 100644 --- a/dnf5-plugins/automatic_plugin/automatic.cpp +++ b/dnf5-plugins/automatic_plugin/automatic.cpp @@ -306,6 +306,7 @@ void AutomaticCommand::pre_configure() { } context.set_output_stream(output_stream); + context.set_error_stream(output_stream); } void AutomaticCommand::configure() { diff --git a/dnf5-plugins/copr_plugin/copr_repo.cpp b/dnf5-plugins/copr_plugin/copr_repo.cpp index 05e4f23b3..70d479485 100644 --- a/dnf5-plugins/copr_plugin/copr_repo.cpp +++ b/dnf5-plugins/copr_plugin/copr_repo.cpp @@ -395,7 +395,7 @@ std::string CoprRepo::get_projectname() { void CoprRepo::save_interactive() { - std::cout << COPR_THIRD_PARTY_WARNING; + std::cerr << COPR_THIRD_PARTY_WARNING; if (!libdnf5::cli::utils::userconfirm::userconfirm(base->get_config())) return; @@ -416,9 +416,9 @@ void CoprRepo::save_interactive() { the_list << " baseurl=" << repo.get_baseurl() << std::endl; } - std::cout << std::endl; - std::cout << libdnf5::utils::sformat(COPR_EXTERNAL_DEPS_WARNING, the_list.str()); - std::cout << std::endl; + std::cerr << std::endl; + std::cerr << libdnf5::utils::sformat(COPR_EXTERNAL_DEPS_WARNING, the_list.str()); + std::cerr << std::endl; if (!libdnf5::cli::utils::userconfirm::userconfirm(base->get_config())) { for (auto & p : repositories) { auto & repo = p.second; diff --git a/dnf5/commands/history/history_store.cpp b/dnf5/commands/history/history_store.cpp index fa7cbbf5d..840ef45e4 100644 --- a/dnf5/commands/history/history_store.cpp +++ b/dnf5/commands/history/history_store.cpp @@ -62,7 +62,7 @@ void HistoryStoreCommand::run() { trans_file_path /= TRANSACTION_JSON; if (std::filesystem::exists(trans_file_path)) { - std::cout << libdnf5::utils::sformat( + std::cerr << libdnf5::utils::sformat( _("File \"{}\" already exists, it will be overwritten.\n"), trans_file_path.string()); // ask user for the file overwrite confirmation if (!libdnf5::cli::utils::userconfirm::userconfirm(get_context().get_base().get_config())) { diff --git a/dnf5/context.cpp b/dnf5/context.cpp index cdf35b627..943ec72eb 100644 --- a/dnf5/context.cpp +++ b/dnf5/context.cpp @@ -70,18 +70,18 @@ class KeyImportRepoCB : public libdnf5::repo::RepoCallbacks { return false; } - std::cout << "Importing PGP key 0x" << key_info.get_short_key_id() << ":\n"; + std::cerr << "Importing PGP key 0x" << key_info.get_short_key_id() << ":\n"; for (auto & user_id : key_info.get_user_ids()) { - std::cout << " Userid : \"" << user_id << "\"\n"; + std::cerr << " Userid : \"" << user_id << "\"\n"; } - std::cout << " Fingerprint: " << key_info.get_fingerprint() << "\n"; - std::cout << " From : " << key_info.get_url() << std::endl; + std::cerr << " Fingerprint: " << key_info.get_fingerprint() << "\n"; + std::cerr << " From : " << key_info.get_url() << std::endl; return libdnf5::cli::utils::userconfirm::userconfirm(*config); } void repokey_imported([[maybe_unused]] const libdnf5::rpm::KeyInfo & key_info) override { - std::cout << _("The key was successfully imported.") << std::endl; + std::cerr << _("The key was successfully imported.") << std::endl; } private: @@ -163,9 +163,12 @@ class Context::Impl { void set_load_available_repos(LoadAvailableRepos which) { load_available_repos = which; } LoadAvailableRepos get_load_available_repos() const noexcept { return load_available_repos; } + void print_output(std::string_view msg) const; void print_info(std::string_view msg) const; + void print_error(std::string_view msg) const; - void set_output_stream(std::ostream & new_output_stream) { output_stream = new_output_stream; } + void set_output_stream(std::ostream & new_output_stream) { out_stream = new_output_stream; } + void set_error_stream(std::ostream & new_error_stream) { err_stream = new_error_stream; } void set_transaction_store_path(std::filesystem::path path) { transaction_store_path = path; } const std::filesystem::path & get_transaction_store_path() const { return transaction_store_path; } @@ -223,7 +226,8 @@ class Context::Impl { bool show_new_leaves{false}; std::string get_cmd_line(); - std::reference_wrapper output_stream = std::cout; + std::reference_wrapper out_stream = std::cout; + std::reference_wrapper err_stream = std::cerr; std::unique_ptr plugins; std::unique_ptr goal; @@ -251,7 +255,7 @@ void Context::Impl::apply_repository_setopts() { repo->get_config().opt_binds().at(key).new_string( libdnf5::Option::Priority::COMMANDLINE, setopt.second); } catch (const std::exception & ex) { - std::cout << "setopt: \"" + setopt.first + "." + setopt.second + "\": " + ex.what() << std::endl; + print_error("setopt: \"" + setopt.first + "." + setopt.second + "\": " + ex.what()); } } } @@ -330,9 +334,9 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { auto & offline_data = state.get_data(); if (offline_data.get_status() != libdnf5::offline::STATUS_DOWNLOAD_INCOMPLETE) { - std::cout << "There is already an offline transaction queued, initiated by the following command:" << std::endl - << "\t" << offline_data.get_cmd_line() << std::endl - << "Continuing will cancel the old offline transaction and replace it with this one." << std::endl; + print_error("There is already an offline transaction queued, initiated by the following command:"); + print_error(fmt::format("\t{}", offline_data.get_cmd_line())); + print_error("Continuing will cancel the old offline transaction and replace it with this one."); if (!libdnf5::cli::utils::userconfirm::userconfirm(base.get_config())) { throw libdnf5::cli::AbortedByUserError(); } @@ -361,19 +365,19 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) { print_info("Testing offline transaction"); auto result = test_transaction.run(); if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) { - std::cerr << "Transaction failed: " << libdnf5::base::Transaction::transaction_result_to_string(result) - << std::endl; + print_error( + fmt::format("Transaction failed: {}", libdnf5::base::Transaction::transaction_result_to_string(result))); for (auto const & entry : transaction.get_gpg_signature_problems()) { - std::cerr << entry << std::endl; + print_error(entry); } for (auto & problem : test_transaction.get_transaction_problems()) { - std::cerr << " - " << problem << std::endl; + print_error(fmt::format(" - {}", problem)); } throw libdnf5::cli::SilentCommandExitError(1); } for (auto const & entry : test_transaction.get_gpg_signature_problems()) { - std::cerr << entry << std::endl; + print_error(entry); } // Download and transaction test complete. Fill out entries in offline @@ -411,9 +415,9 @@ void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) { constexpr const char * comps_in_trans_dir{"./comps"}; auto comps_location = transaction_store_path / comps_in_trans_dir; if (std::filesystem::exists(transaction_location)) { - std::cout << libdnf5::utils::sformat( - _("Location \"{}\" already contains a stored transaction, it will be overwritten.\n"), - transaction_store_path.string()); + print_error(libdnf5::utils::sformat( + _("Location \"{}\" already contains a stored transaction, it will be overwritten."), + transaction_store_path.string())); if (libdnf5::cli::utils::userconfirm::userconfirm(base.get_config())) { std::filesystem::remove_all(packages_location); std::filesystem::remove_all(comps_location); @@ -448,10 +452,10 @@ void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) { if (should_store_offline) { store_offline(transaction); - std::cout << "Transaction stored to be performed offline. Run `dnf5 offline reboot` to reboot and run the " - "transaction. To cancel the transaction and delete the downloaded files, use `dnf5 " - "offline clean`." - << std::endl; + print_info( + "Transaction stored to be performed offline. Run `dnf5 offline reboot` to reboot and run the " + "transaction. To cancel the transaction and delete the downloaded files, use `dnf5 " + "offline clean`."); return; } @@ -483,19 +487,19 @@ void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) { auto result = transaction.run(); print_info(""); if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) { - std::cerr << "Transaction failed: " << libdnf5::base::Transaction::transaction_result_to_string(result) - << std::endl; + print_error( + fmt::format("Transaction failed: {}", libdnf5::base::Transaction::transaction_result_to_string(result))); for (auto const & entry : transaction.get_gpg_signature_problems()) { - std::cerr << entry << std::endl; + print_error(entry); } for (auto & problem : transaction.get_transaction_problems()) { - std::cerr << " - " << problem << std::endl; + print_error(problem); } throw libdnf5::cli::SilentCommandExitError(1); } for (auto const & entry : transaction.get_gpg_signature_problems()) { - std::cerr << entry << std::endl; + print_error(entry); } // TODO(mblaha): print a summary of successful transaction } @@ -507,11 +511,17 @@ libdnf5::Goal * Context::Impl::get_goal(bool new_if_not_exist) { return goal.get(); } +void Context::Impl::print_output(std::string_view msg) const { + out_stream.get() << msg << std::endl; +} void Context::Impl::print_info(std::string_view msg) const { if (!quiet) { - output_stream.get() << msg << std::endl; + err_stream.get() << msg << std::endl; } } +void Context::Impl::print_error(std::string_view msg) const { + err_stream.get() << msg << std::endl; +} Context::Context(std::vector> && loggers) @@ -645,12 +655,21 @@ Context::LoadAvailableRepos Context::get_load_available_repos() const noexcept { return p_impl->get_load_available_repos(); } +void Context::print_output(std::string_view msg) const { + p_impl->print_output(msg); +} void Context::print_info(std::string_view msg) const { p_impl->print_info(msg); } +void Context::print_error(std::string_view msg) const { + p_impl->print_error(msg); +} void Context::set_output_stream(std::ostream & new_output_stream) { p_impl->set_output_stream(new_output_stream); } +void Context::set_error_stream(std::ostream & new_error_stream) { + p_impl->set_error_stream(new_error_stream); +} void Context::set_transaction_store_path(std::filesystem::path path) { p_impl->set_transaction_store_path(path); diff --git a/dnf5/download_callbacks.cpp b/dnf5/download_callbacks.cpp index 2123f1928..2c8c55beb 100644 --- a/dnf5/download_callbacks.cpp +++ b/dnf5/download_callbacks.cpp @@ -104,7 +104,7 @@ int DownloadCallbacks::mirror_failure(void * user_cb_data, const char * msg, con void DownloadCallbacks::reset_progress_bar() { multi_progress_bar.reset(); if (printed) { - std::cout << std::endl; + std::cerr << std::endl; printed = false; } } diff --git a/dnf5/include/dnf5/context.hpp b/dnf5/include/dnf5/context.hpp index 505fa8401..f0462e1a8 100644 --- a/dnf5/include/dnf5/context.hpp +++ b/dnf5/include/dnf5/context.hpp @@ -136,11 +136,19 @@ class DNF_API Context : public libdnf5::cli::session::Session { void set_load_available_repos(LoadAvailableRepos which); LoadAvailableRepos get_load_available_repos() const noexcept; - /// If quiet mode is not active, it will print `msg` to standard output. + /// Print `msg` to the output stream, which by default is stdout. + void print_output(std::string_view msg) const; + + /// If quiet mode is not active, it will print `msg` to the error stream, which by default is stderr. void print_info(std::string_view msg) const; + /// Print `msg` to the error stream, which by default is stderr. + void print_error(std::string_view msg) const; + void set_output_stream(std::ostream & new_output_stream); + void set_error_stream(std::ostream & new_error_stream); + // When set current transaction is not executed but rather stored to the specified path. void set_transaction_store_path(std::filesystem::path path); const std::filesystem::path & get_transaction_store_path() const; diff --git a/dnf5/main.cpp b/dnf5/main.cpp index 731d06695..69ffb07aa 100644 --- a/dnf5/main.cpp +++ b/dnf5/main.cpp @@ -187,11 +187,8 @@ void RootCommand::set_argument_parser() { try { cache.write_attribute(libdnf5::repo::RepoCache::ATTRIBUTE_EXPIRED); } catch (const std::exception & ex) { - std::cerr << libdnf5::utils::sformat( - _("Failed to expire repository cache in path \"{0}\": {1}"), - dir_entry.path().native(), - ex.what()) - << std::endl; + ctx.print_error(libdnf5::utils::sformat( + _("Failed to expire repository cache in path \"{0}\": {1}"), dir_entry.path().native(), ex.what())); } } return true; @@ -742,37 +739,34 @@ static void print_versions(Context & context) { constexpr const char * appl_name = "dnf5"; { const auto & version = get_application_version(); - std::cout - << fmt::format( - "{} version {}.{}.{}.{}", appl_name, version.prime, version.major, version.minor, version.micro) - << std::endl; + context.print_output(fmt::format( + "{} version {}.{}.{}.{}", appl_name, version.prime, version.major, version.minor, version.micro)); const auto & api_version = get_plugin_api_version(); - std::cout << fmt::format("{} plugin API version {}.{}", appl_name, api_version.major, api_version.minor) - << std::endl; + context.print_output( + fmt::format("{} plugin API version {}.{}", appl_name, api_version.major, api_version.minor)); } { const auto & version = libdnf5::get_library_version(); - std::cout << fmt::format( - "libdnf5 version {}.{}.{}.{}", version.prime, version.major, version.minor, version.micro) - << std::endl; + context.print_output( + fmt::format("libdnf5 version {}.{}.{}.{}", version.prime, version.major, version.minor, version.micro)); const auto & api_version = libdnf5::get_plugin_api_version(); - std::cout << fmt::format("libdnf5 plugin API version {}.{}", api_version.major, api_version.minor) << std::endl; + context.print_output(fmt::format("libdnf5 plugin API version {}.{}", api_version.major, api_version.minor)); } bool first{true}; for (const auto & plugin : context.get_plugins().get_plugins()) { if (first) { first = false; - std::cout << fmt::format("\nLoaded {} plugins:", appl_name) << std::endl; + context.print_output(fmt::format("\nLoaded {} plugins:", appl_name)); } else { - std::cout << std::endl; + context.print_output(""); } auto * iplugin = plugin->get_iplugin(); - std::cout << fmt::format(" name: {}", iplugin->get_name()) << std::endl; + context.print_output(fmt::format(" name: {}", iplugin->get_name())); const auto & version = iplugin->get_version(); - std::cout << fmt::format(" version: {}.{}.{}", version.major, version.minor, version.micro) << std::endl; + context.print_output(fmt::format(" version: {}.{}.{}", version.major, version.minor, version.micro)); const auto & api_version = iplugin->get_api_version(); - std::cout << fmt::format(" API version: {}.{}", api_version.major, api_version.minor) << std::endl; + context.print_output(fmt::format(" API version: {}.{}", api_version.major, api_version.minor)); } } @@ -835,7 +829,7 @@ static void print_transaction_size_stats(Context & context) { static void dump_main_configuration(Context & context) { libdnf5::Base & base = context.get_base(); - std::cout << _("======== Main configuration: ========") << std::endl; + context.print_output(_("======== Main configuration: ========")); for (const auto & option : base.get_config().opt_binds()) { const auto & val = option.second; std::string value; @@ -846,9 +840,9 @@ static void dump_main_configuration(Context & context) { } catch (const libdnf5::OptionError &) { } if (was_set) { - std::cout << fmt::format("{} = {}", option.first, value) << std::endl; + context.print_output(fmt::format("{} = {}", option.first, value)); } else { - std::cout << fmt::format("{}", option.first) << std::endl; + context.print_output(fmt::format("{}", option.first)); } } } @@ -876,8 +870,8 @@ static void dump_repository_configuration(Context & context, const std::vectorget_id()) - << std::endl; + context.print_output( + libdnf5::utils::sformat(_("======== \"{}\" repository configuration: ========"), repo->get_id())); for (const auto & option : repo->get_config().opt_binds()) { const auto & val = option.second; std::string value; @@ -888,19 +882,19 @@ static void dump_repository_configuration(Context & context, const std::vectorget_variables()) { const auto & val = var.second; - std::cout << fmt::format("{} = {}", var.first, val.value) << std::endl; + context.print_output(fmt::format("{} = {}", var.first, val.value)); } } @@ -937,11 +931,11 @@ static void print_new_leaves(Context & context) { } if (!new_leaves_na.empty()) { - std::cout << "New leaves:" << std::endl; + context.print_output("New leaves:"); for (const auto & leaf_pkg : new_leaves_na) { - std::cout << " " << leaf_pkg << std::endl; + context.print_output(fmt::format(" {}", leaf_pkg)); } - std::cout << std::endl; + context.print_output(""); } } @@ -1076,9 +1070,9 @@ static void print_resolve_hints(dnf5::Context & context) { } if (hints.size() > 0) { - std::cerr << _("You can try to add to command line:") << std::endl; + context.print_error(_("You can try to add to command line:")); for (const auto & hint : hints) { - std::cerr << " " << hint << std::endl; + context.print_error(fmt::format(" {}", hint)); } } } @@ -1112,7 +1106,7 @@ static void print_no_match_libdnf_plugin_patterns(dnf5::Context & context) { for (; it != no_match_pattern_set.end(); ++it) { patterns += ", " + *it; } - std::cerr << libdnf5::utils::sformat(TM_(no_match_message, 1), patterns) << std::endl; + context.print_error(libdnf5::utils::sformat(TM_(no_match_message, 1), patterns)); } } } @@ -1242,20 +1236,18 @@ int main(int argc, char * argv[]) try { } } if (help_printed) { - std::cerr << ex.what() << "." << std::endl; + context.print_error(fmt::format("{}.", ex.what())); } else { - std::cerr << ex.what() << _(". Add \"--help\" for more information about the arguments.") - << std::endl; + context.print_error(fmt::format( + "{}{}", ex.what(), _(". Add \"--help\" for more information about the arguments."))); } // If the error is an unknown top-level command, suggest // installing a package that provides the command if (auto * unknown_arg_ex = dynamic_cast(&ex)) { if (unknown_arg_ex->get_command() == "dnf5" && unknown_arg_ex->get_argument()[0] != '-') { - std::cerr - << fmt::format( - "It could be a command provided by a plugin, try: dnf5 install 'dnf5-command({})'", - unknown_arg_ex->get_argument()) - << std::endl; + context.print_error(fmt::format( + "It could be a command provided by a plugin, try: dnf5 install 'dnf5-command({})'", + unknown_arg_ex->get_argument())); } } return static_cast(libdnf5::cli::ExitCode::ARGPARSER_ERROR); @@ -1393,18 +1385,16 @@ int main(int argc, char * argv[]) try { if (auto transaction_store_path = context.get_transaction_store_path(); !transaction_store_path.empty()) { - std::cout << "The operation will only store the transaction in " << transaction_store_path << "." - << std::endl; + context.print_error(fmt::format( + "The operation will only store the transaction in {}", transaction_store_path.string())); } else if (base.get_config().get_downloadonly_option().get_value()) { - std::cout << "The operation will only download packages for the transaction." << std::endl; + context.print_error("The operation will only download packages for the transaction."); } else { for (const auto & tsflag : base.get_config().get_tsflags_option().get_value()) { if (tsflag == "test") { - std::cout - << "Test mode enabled: Only package downloads, pgp key installations and transaction " - "checks " - "will be performed." - << std::endl; + context.print_error( + "Test mode enabled: Only package downloads, pgp key installations and transaction " + "checks will be performed."); } } } @@ -1416,13 +1406,12 @@ int main(int argc, char * argv[]) try { context.download_and_run(*context.get_transaction()); } } catch (libdnf5::cli::GoalResolveError & ex) { - std::cerr << ex.what() << std::endl; + context.print_error(ex.what()); if (!any_repos_from_system_configuration && base.get_config().get_installroot_option().get_value() != "/" && !base.get_config().get_use_host_config_option().get_value()) { - std::cerr - << "No repositories were loaded from the installroot. To use the configuration and repositories " - "of the host system, pass --use-host-config." - << std::endl; + context.print_error( + "No repositories were loaded from the installroot. To use the configuration and repositories " + "of the host system, pass --use-host-config."); } else { if (context.get_transaction() != nullptr) { // download command can throw GoalResolveError without context.transaction being set @@ -1431,10 +1420,11 @@ int main(int argc, char * argv[]) try { } return static_cast(libdnf5::cli::ExitCode::ERROR); } catch (libdnf5::cli::ArgumentParserError & ex) { - std::cerr << ex.what() << _(". Add \"--help\" for more information about the arguments.") << std::endl; + context.print_error( + fmt::format("{}{}", ex.what(), _(". Add \"--help\" for more information about the arguments."))); return static_cast(libdnf5::cli::ExitCode::ARGPARSER_ERROR); } catch (libdnf5::cli::CommandExitError & ex) { - std::cerr << ex.what() << std::endl; + context.print_error(ex.what()); return ex.get_exit_code(); } catch (libdnf5::cli::SilentCommandExitError & ex) { return ex.get_exit_code(); diff --git a/dnf5daemon-client/callbacks.cpp b/dnf5daemon-client/callbacks.cpp index 32d525548..5e66be388 100644 --- a/dnf5daemon-client/callbacks.cpp +++ b/dnf5daemon-client/callbacks.cpp @@ -89,7 +89,7 @@ void DownloadCB::print() { void DownloadCB::reset_progress_bar() { multi_progress_bar.reset(); if (printed) { - std::cout << std::endl; + std::cerr << std::endl; printed = false; } } @@ -217,12 +217,12 @@ void DownloadCB::key_import(sdbus::Signal & signal) { std::string url; signal >> key_id >> user_ids >> fingerprint >> url; - std::cout << std::endl << "Importing PGP key 0x" + key_id << ":\n"; + std::cerr << std::endl << "Importing PGP key 0x" + key_id << ":\n"; for (auto & user_id : user_ids) { - std::cout << " Userid : \"" + user_id << "\"\n"; + std::cerr << " Userid : \"" + user_id << "\"\n"; } - std::cout << " Fingerprint: " + fingerprint << std::endl; - std::cout << " From : " + url << std::endl; + std::cerr << " Fingerprint: " + fingerprint << std::endl; + std::cerr << " From : " + url << std::endl; // ask user for the key import confirmation auto confirmed = libdnf5::cli::utils::userconfirm::userconfirm(context); diff --git a/include/libdnf5-cli/progressbar/multi_progress_bar.hpp b/include/libdnf5-cli/progressbar/multi_progress_bar.hpp index f20fe9fd5..e5683e0db 100644 --- a/include/libdnf5-cli/progressbar/multi_progress_bar.hpp +++ b/include/libdnf5-cli/progressbar/multi_progress_bar.hpp @@ -44,8 +44,8 @@ class LIBDNF_CLI_API MultiProgressBar { void add_bar(std::unique_ptr && bar); void print() { - std::cout << *this; - std::cout << std::flush; + std::cerr << *this; + std::cerr << std::flush; } LIBDNF_CLI_API friend std::ostream & operator<<(std::ostream & stream, MultiProgressBar & mbar); diff --git a/include/libdnf5-cli/utils/userconfirm.hpp b/include/libdnf5-cli/utils/userconfirm.hpp index 344fc1afa..3216c39e0 100644 --- a/include/libdnf5-cli/utils/userconfirm.hpp +++ b/include/libdnf5-cli/utils/userconfirm.hpp @@ -45,7 +45,7 @@ bool userconfirm(Config & config) { msg = "Is this ok [y/N]: "; } while (true) { - std::cout << msg; + std::cerr << msg; std::string choice; std::getline(std::cin, choice); diff --git a/libdnf5-cli/progressbar/multi_progress_bar.cpp b/libdnf5-cli/progressbar/multi_progress_bar.cpp index f13e41712..3d552cfb3 100644 --- a/libdnf5-cli/progressbar/multi_progress_bar.cpp +++ b/libdnf5-cli/progressbar/multi_progress_bar.cpp @@ -35,14 +35,14 @@ MultiProgressBar::MultiProgressBar() : total(0, "Total") { total.set_auto_finish(false); total.start(); if (tty::is_interactive()) { - std::cout << tty::cursor_hide; + std::cerr << tty::cursor_hide; } } MultiProgressBar::~MultiProgressBar() { if (tty::is_interactive()) { - std::cout << tty::cursor_show; + std::cerr << tty::cursor_show; } }