forked from lix-project/lix
fix: check if it is a Real terminal, not just if it is a terminal
This will stop printing stuff to dumb terminals that they don't support.
I've overall audited usage of isatty and replaced the ones with intent
to mean "is a Real terminal" with checking for that. I've also caught a
case of carelessly assuming "is a tty" means "should be colour" in
nix-env.
Change-Id: I6d83725d9a2d932ac94ff2294f92c0a1100d23c9
This commit is contained in:
parent
3775b6ac88
commit
292567e0b0
|
@ -5,9 +5,9 @@
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "loggers.hh"
|
#include "loggers.hh"
|
||||||
#include "current-process.hh"
|
#include "current-process.hh"
|
||||||
|
#include "terminal.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
|
||||||
|
|
||||||
RunPager::RunPager()
|
RunPager::RunPager()
|
||||||
{
|
{
|
||||||
if (!isatty(STDOUT_FILENO)) return;
|
if (!isOutputARealTerminal(StandardOutputStream::Stdout)) return;
|
||||||
char * pager = getenv("NIX_PAGER");
|
char * pager = getenv("NIX_PAGER");
|
||||||
if (!pager) pager = getenv("PAGER");
|
if (!pager) pager = getenv("PAGER");
|
||||||
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
|
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
|
||||||
|
|
|
@ -37,7 +37,15 @@ void Logger::warn(const std::string & msg)
|
||||||
|
|
||||||
void Logger::writeToStdout(std::string_view s)
|
void Logger::writeToStdout(std::string_view s)
|
||||||
{
|
{
|
||||||
writeFull(STDOUT_FILENO, filterANSIEscapes(s, !shouldANSI(), std::numeric_limits<unsigned int>::max(), false));
|
writeFull(
|
||||||
|
STDOUT_FILENO,
|
||||||
|
filterANSIEscapes(
|
||||||
|
s,
|
||||||
|
!shouldANSI(StandardOutputStream::Stdout),
|
||||||
|
std::numeric_limits<unsigned int>::max(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
writeFull(STDOUT_FILENO, "\n");
|
writeFull(STDOUT_FILENO, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
bool shouldANSI()
|
bool isOutputARealTerminal(StandardOutputStream fileno)
|
||||||
|
{
|
||||||
|
return isatty(int(fileno)) && getEnv("TERM").value_or("dumb") != "dumb";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldANSI(StandardOutputStream fileno)
|
||||||
{
|
{
|
||||||
// Implements the behaviour described by https://bixense.com/clicolors/
|
// Implements the behaviour described by https://bixense.com/clicolors/
|
||||||
// As well as https://force-color.org/ for compatibility, since it fits in the same shape.
|
// As well as https://force-color.org/ for compatibility, since it fits in the same shape.
|
||||||
|
@ -16,14 +21,14 @@ bool shouldANSI()
|
||||||
// unset x set Yes
|
// unset x set Yes
|
||||||
// unset x unset If attached to a terminal
|
// unset x unset If attached to a terminal
|
||||||
// [we choose the "modern" approach of colour-by-default]
|
// [we choose the "modern" approach of colour-by-default]
|
||||||
auto compute = []() -> bool {
|
auto compute = [](StandardOutputStream fileno) -> bool {
|
||||||
bool mustNotColour = getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value();
|
bool mustNotColour = getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value();
|
||||||
bool shouldForce = getEnv("CLICOLOR_FORCE").has_value() || getEnv("FORCE_COLOR").has_value();
|
bool shouldForce = getEnv("CLICOLOR_FORCE").has_value() || getEnv("FORCE_COLOR").has_value();
|
||||||
bool isTerminal = isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb";
|
bool isTerminal = isOutputARealTerminal(fileno);
|
||||||
return !mustNotColour && (shouldForce || isTerminal);
|
return !mustNotColour && (shouldForce || isTerminal);
|
||||||
};
|
};
|
||||||
static bool cached = compute();
|
static bool cached[2] = {compute(StandardOutputStream::Stdout), compute(StandardOutputStream::Stderr)};
|
||||||
return cached;
|
return cached[int(fileno) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(jade): replace with TerminalCodeEater. wowie this is evil code.
|
// FIXME(jade): replace with TerminalCodeEater. wowie this is evil code.
|
||||||
|
|
|
@ -6,6 +6,26 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
enum class StandardOutputStream {
|
||||||
|
Stdout = 1,
|
||||||
|
Stderr = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the output is a real terminal (i.e. not dumb, not a pipe).
|
||||||
|
*
|
||||||
|
* This is probably not what you want, you may want shouldANSI() or something
|
||||||
|
* more specific. Think about how the output should work with a pager or
|
||||||
|
* entirely non-interactive scripting use.
|
||||||
|
*
|
||||||
|
* The user may be redirecting the Lix output to a pager, but have stderr
|
||||||
|
* connected to a terminal. Think about where you are outputting the text when
|
||||||
|
* deciding whether to use STDERR_FILENO or STDOUT_FILENO.
|
||||||
|
*
|
||||||
|
* \param fileno file descriptor number to check if it is a tty
|
||||||
|
*/
|
||||||
|
bool isOutputARealTerminal(StandardOutputStream fileno);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether ANSI escape sequences are appropriate for the
|
* Determine whether ANSI escape sequences are appropriate for the
|
||||||
* present output.
|
* present output.
|
||||||
|
@ -18,8 +38,10 @@ namespace nix {
|
||||||
* - CLICOLOR_FORCE or FORCE_COLOR set -> enable colour
|
* - CLICOLOR_FORCE or FORCE_COLOR set -> enable colour
|
||||||
* - The output is a tty; TERM != "dumb" -> enable colour
|
* - The output is a tty; TERM != "dumb" -> enable colour
|
||||||
* - Otherwise -> disable colour
|
* - Otherwise -> disable colour
|
||||||
|
*
|
||||||
|
* \param fileno which file descriptor number to consider. Use the one you are outputting to
|
||||||
*/
|
*/
|
||||||
bool shouldANSI();
|
bool shouldANSI(StandardOutputStream fileno = StandardOutputStream::Stderr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Truncate a string to 'width' printable characters. If 'filterAll'
|
* Truncate a string to 'width' printable characters. If 'filterAll'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "common-eval-args.hh"
|
#include "common-eval-args.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "terminal.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
@ -17,7 +18,6 @@
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
#include "eval-settings.hh" // for defexpr
|
#include "eval-settings.hh" // for defexpr
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -1092,7 +1092,6 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tty = isatty(STDOUT_FILENO);
|
|
||||||
RunPager pager;
|
RunPager pager;
|
||||||
|
|
||||||
Table table;
|
Table table;
|
||||||
|
@ -1171,7 +1170,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto column = (std::string) "" + ch + " " + version;
|
auto column = (std::string) "" + ch + " " + version;
|
||||||
if (diff == cvGreater && tty)
|
if (diff == cvGreater && shouldANSI(StandardOutputStream::Stdout))
|
||||||
column = ANSI_RED + column + ANSI_NORMAL;
|
column = ANSI_RED + column + ANSI_NORMAL;
|
||||||
columns.push_back(column);
|
columns.push_back(column);
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,6 +362,7 @@ void mainWrapped(int argc, char * * argv)
|
||||||
setLogFormat(LogFormat::bar);
|
setLogFormat(LogFormat::bar);
|
||||||
Finally f([] { logger->pause(); });
|
Finally f([] { logger->pause(); });
|
||||||
settings.verboseBuild = false;
|
settings.verboseBuild = false;
|
||||||
|
// FIXME: stop messing about with log verbosity depending on if it is interactive use
|
||||||
if (isatty(STDERR_FILENO)) {
|
if (isatty(STDERR_FILENO)) {
|
||||||
verbosity = lvlNotice;
|
verbosity = lvlNotice;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "filetransfer.hh"
|
#include "filetransfer.hh"
|
||||||
#include "finally.hh"
|
|
||||||
#include "tarfile.hh"
|
#include "tarfile.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh" // IWYU pragma: keep
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
|
#include "terminal.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||||
if (args.size() > 2)
|
if (args.size() > 2)
|
||||||
throw UsageError("too many arguments");
|
throw UsageError("too many arguments");
|
||||||
|
|
||||||
if (isatty(STDERR_FILENO))
|
if (isOutputARealTerminal(StandardOutputStream::Stderr))
|
||||||
setLogFormat(LogFormat::bar);
|
setLogFormat(LogFormat::bar);
|
||||||
|
|
||||||
auto store = openStore();
|
auto store = openStore();
|
||||||
|
|
Loading…
Reference in a new issue