ls(1): Add --color=when

--color may be set to one of: 'auto', 'always', and 'never'.

'auto' is the default behavior- output colors only if -G or COLORTERM are
set, and only if stdout is a tty.

'always' is a new behavior- output colors always. termcap(5) will be
consulted unless TERM is unset or not a recognized terminal, in which case
ls(1) will fall back to explicitly outputting ANSI escape sequences.

'never' to turn off any environment variable and -G usage.

Reviewed by:	cem, 0mp (both modulo last-minute manpage changes
Differential Revision:	https://reviews.freebsd.org/D16741
This commit is contained in:
Kyle Evans 2018-08-17 04:15:51 +00:00
parent b14959dacc
commit e10ba80063
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=337956
6 changed files with 166 additions and 16 deletions

View file

@ -35,9 +35,9 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 12.x IS SLOW:
ls(1) now respects the COLORTERM environment variable used in other
systems and software to indicate that a colored terminal is both
supported and desired. If ls(1) is suddenly emitting colors, they may
be disabled again by removing the unwanted COLORTERM from your
environment. The ls(1) specific CLICOLOR may not be observed in a
future release.
be disabled again by either removing the unwanted COLORTERM from your
environment, or using `ls --color=never`. The ls(1) specific CLICOLOR
may not be observed in a future release.
20180808:
The default pager for most commands has been changed to "less". To

View file

@ -32,6 +32,8 @@
* $FreeBSD$
*/
#include <stdbool.h>
int acccmp(const FTSENT *, const FTSENT *);
int revacccmp(const FTSENT *, const FTSENT *);
int birthcmp(const FTSENT *, const FTSENT *);
@ -64,5 +66,12 @@ extern char *ansi_bgcol;
extern char *ansi_coloff;
extern char *attrs_off;
extern char *enter_bold;
extern int colorflag;
extern bool explicitansi;
#define COLORFLAG_NEVER 0
#define COLORFLAG_AUTO 1
#define COLORFLAG_ALWAYS 2
#endif
extern int termwidth;

View file

@ -32,7 +32,7 @@
.\" @(#)ls.1 8.7 (Berkeley) 7/29/94
.\" $FreeBSD$
.\"
.Dd August 8, 2018
.Dd August 16, 2018
.Dt LS 1
.Os
.Sh NAME
@ -41,6 +41,7 @@
.Sh SYNOPSIS
.Nm
.Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,
.Op Fl -color Ns = Ns Ar when
.Op Fl D Ar format
.Op Ar
.Sh DESCRIPTION
@ -210,6 +211,47 @@ This option is not defined in
.St -p1003.1-2001 .
.It Fl c
Use time when file status was last changed for sorting or printing.
.It Fl -color Ns = Ns Ar when
Output colored escape sequences based on
.Ar when ,
which may be set to either
.Cm always ,
.Cm auto
(default), or
.Cm never .
.Pp
.Cm always
will make
.Nm
always output color.
If
.Ev TERM
is unset or set to an invalid terminal, then
.Nm
will fall back to explicit
.Tn ANSI
escape sequences without the help of
.Xr termcap 5 .
.Cm always
is the default if
.Fl -color
is specified without an argument.
.Pp
.Cm auto
will make
.Nm
output escape sequences based on
.Xr termcap 5 ,
but only if
.Dv stdout
is a tty and either the
.Fl G
flag is specified or the
.Ev COLORTERM
environment variable is set and not empty.
.Pp
.Cm never
will disable color regardless of environment variables.
.It Fl d
Directories are listed as plain files (not searched recursively).
.It Fl f
@ -620,7 +662,10 @@ Colorization
is silently disabled if the output is not directed to a terminal
unless the
.Ev CLICOLOR_FORCE
variable is defined.
variable is defined or
.Fl -color
is set to
.Dq always .
.It Ev CLICOLOR_FORCE
Color sequences are normally disabled if the output is not directed to
a terminal.

View file

@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <getopt.h>
#include <grp.h>
#include <inttypes.h>
#include <limits.h>
@ -99,6 +100,16 @@ static void display(const FTSENT *, FTSENT *, int);
static int mastercmp(const FTSENT * const *, const FTSENT * const *);
static void traverse(int, char **, int);
#define COLOR_OPT (CHAR_MAX + 1)
static const struct option long_opts[] =
{
#ifdef COLORLS
{"color", optional_argument, NULL, COLOR_OPT},
#endif
{NULL, no_argument, NULL, 0}
};
static void (*printfcn)(const DISPLAY *);
static int (*sortfcn)(const FTSENT *, const FTSENT *);
@ -140,10 +151,10 @@ static int f_stream; /* stream the output, separate with commas */
static int f_timesort; /* sort by time vice name */
int f_type; /* add type character for non-regular files */
static int f_whiteout; /* show whiteout entries */
#ifdef COLORLS
int colorflag = COLORFLAG_AUTO; /* passed in colorflag */
int f_color; /* add type in color for non-regular files */
bool explicitansi; /* Explicit ANSI sequences, no termcap(5) */
char *ansi_bgcol; /* ANSI sequence to set background colour */
char *ansi_fgcol; /* ANSI sequence to set foreground colour */
char *ansi_coloff; /* ANSI sequence to reset colours */
@ -176,6 +187,19 @@ do_color_from_env(void)
(isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")));
}
static bool
do_color(void)
{
#ifdef COLORLS
if (colorflag == COLORFLAG_NEVER)
return (false);
else if (colorflag == COLORFLAG_ALWAYS)
return (true);
#endif
return (do_color_from_env());
}
int
main(int argc, char *argv[])
{
@ -187,7 +211,7 @@ main(int argc, char *argv[])
#ifdef COLORLS
char termcapbuf[1024]; /* termcap definition buffer */
char tcapbuf[512]; /* capability buffer */
char *bp = tcapbuf;
char *bp = tcapbuf, *term;
#endif
(void)setlocale(LC_ALL, "");
@ -215,8 +239,9 @@ main(int argc, char *argv[])
fts_options = FTS_PHYSICAL;
if (getenv("LS_SAMESORT"))
f_samesort = 1;
while ((ch = getopt(argc, argv,
"1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) {
while ((ch = getopt_long(argc, argv,
"+1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,", long_opts,
NULL)) != -1) {
switch (ch) {
/*
* The -1, -C, -x and -l options all override each other so
@ -379,6 +404,19 @@ main(int argc, char *argv[])
case 'y':
f_samesort = 1;
break;
#ifdef COLORLS
case COLOR_OPT:
if (optarg == NULL || strcmp(optarg, "always") == 0)
colorflag = COLORFLAG_ALWAYS;
else if (strcmp(optarg, "auto") == 0)
colorflag = COLORFLAG_AUTO;
else if (strcmp(optarg, "never") == 0)
colorflag = COLORFLAG_NEVER;
else
errx(2, "unsupported --color value '%s' (must be always, auto, or never)",
optarg);
break;
#endif
default:
case '?':
usage();
@ -391,10 +429,14 @@ main(int argc, char *argv[])
if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot)
f_listdot = 1;
/* Enabling of colours is conditional on the environment. */
if (do_color_from_env())
/*
* Enabling of colours is conditional on the environment in conjunction
* with the --color and -G arguments, if supplied.
*/
if (do_color()) {
#ifdef COLORLS
if (tgetent(termcapbuf, getenv("TERM")) == 1) {
if ((term = getenv("TERM")) != NULL &&
tgetent(termcapbuf, term) == 1) {
ansi_fgcol = tgetstr("AF", &bp);
ansi_bgcol = tgetstr("AB", &bp);
attrs_off = tgetstr("me", &bp);
@ -408,10 +450,19 @@ main(int argc, char *argv[])
ansi_coloff = tgetstr("oc", &bp);
if (ansi_fgcol && ansi_bgcol && ansi_coloff)
f_color = 1;
} else if (colorflag == COLORFLAG_ALWAYS) {
/*
* If we're *always* doing color but we don't have
* a functional TERM supplied, we'll fallback to
* outputting raw ANSI sequences.
*/
f_color = 1;
explicitansi = true;
}
#else
warnx("color support not compiled in");
#endif /*COLORLS*/
}
#ifdef COLORLS
if (f_color) {

View file

@ -73,6 +73,8 @@ static void printtime(time_t);
static int printtype(u_int);
static void printsize(size_t, off_t);
#ifdef COLORLS
static void endcolor_termcap(int);
static void endcolor_ansi(void);
static void endcolor(int);
static int colortype(mode_t);
#endif
@ -540,7 +542,7 @@ writech(int c)
}
static void
printcolor(Colors c)
printcolor_termcap(Colors c)
{
char *ansiseq;
@ -560,12 +562,55 @@ printcolor(Colors c)
}
static void
endcolor(int sig)
printcolor_ansi(Colors c)
{
printf("\033[");
if (colors[c].bold)
printf("1");
if (colors[c].num[0] != -1)
printf(";3%d", colors[c].num[0]);
if (colors[c].num[1] != -1)
printf(";4%d", colors[c].num[1]);
printf("m");
}
static void
printcolor(Colors c)
{
if (explicitansi)
printcolor_ansi(c);
else
printcolor_termcap(c);
}
static void
endcolor_termcap(int sig)
{
tputs(ansi_coloff, 1, sig ? writech : putch);
tputs(attrs_off, 1, sig ? writech : putch);
}
static void
endcolor_ansi(void)
{
printf("\33[m");
}
static void
endcolor(int sig)
{
if (explicitansi)
endcolor_ansi();
else
endcolor_termcap(sig);
}
static int
colortype(mode_t mode)
{

View file

@ -227,7 +227,7 @@ usage(void)
{
(void)fprintf(stderr,
#ifdef COLORLS
"usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [-D format]"
"usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [--color=when] [-D format]"
#else
"usage: ls [-ABCFHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [-D format]"
#endif