ls colours
Latest update:
While reading about Poettering's adventures in determining the best
$TERM value for serial & VM terminals in systemd, I accidentally
discovered that ls from coreutils doesn't consult terminfo to check
whether a terminal emulator (TE) supports colour.
What does it consult then? $TERM? Yes and no.
At first glance, with --color=auto
flag, it ignores $TERM
completely, trusting the LS_COLORS
environment variable. But if
$LS_COLORS is absent, it still prints in colour for some
terminals--yet, simultaneously, not for all file types. What is going
on?
You may have seen /etc/DIR_COLORS
file. Usually, your distro
includes some default sh scripts that invoke the dircolors(1) program
with that file as an argument, generating a big, ugly looking string
value for $LS_COLORS.
The interesting thing about /etc/DIR_COLORS
is that it's just a copy
of a file embedded into ls and dircolors programs (as a single
static char const G_line[]
variable) during compilation, meaning
that in the absence of /etc/DIR_COLORS
they technically still have a
default colour scheme.
ls doesn't read that (probably modified by a user) file directly--it
reads LS_COLORS
environment variable, presumably because, in 1996,
dynamically generating $LS_COLORS values was considered too
costly. Hence, ls has a separate table (color_indicator[]
, for the
curious) listing "important" file types. That table is consulted in
case $LS_COLORS is unset.
But that doesn't explain why with no $LS_COLORS in sight, ls
suddenly starts looking at $TERM. The DIR_COLORS
file also contains
a list of TEs ls considers capable of printing in colour. So why,
then, is vt100 on that list when, according to the interwebs, nobody
in 1978 in their right mind thought a colour terminal made any sense,
unless they were driving a Lamborghini Countach? (I might be
exaggerating a little.)
Anyhow, ls employs its embedded DIR_COLORS
file to:
/* Check if the content of TERM is a valid name in dircolors. */
static bool known_term_type(void) {
char const *term = getenv("TERM");
if (!term || !*term)
return false;
char const *line = G_line;
while (line - G_line < sizeof(G_line)) {
if (STRNCMP_LIT(line, "TERM ") == 0) {
if (fnmatch(line + 5, term, 0) == 0)
return true;
}
line += strlen(line) + 1;
}
return false;
}
See? Now it's all come together. Moreover, to please
Lennart, in the
next release of coreutils, vt220 will be joining the illustrious crew
of colour terminals too. What a time to be alive.
Tags: ойті
Authors: ag