The terminal, as powerful as it might be, has a not undeserved fame of
being boring. Boring white (or some other, fixed, color) on boring
black (or some other, fixed, color) for everything. Yet displays
nowadays are capable of showing millions of colors, and have been able
to display at least four since the eighties at least. There's a
resurgence of “colorization” options for the terminal, from the shell
prompt to the multiplexers (screen
, tmux
), from the output of
commands such as ls
to the syntax highlighting options of editors and
pagers. A lot of modern programs will even try to use colors in their
output right from the start, making it easier to tell apart semantically
different parts of it.
One of the last strongholds of the boring white-on-black (or conversely)
terminal displays is man
, the manual page reader. Man pages constitute
the backbone of technical documentation in Unix-like systems, and range
from the description of the syntax and behaviour of command-line
programs to the details of system calls and programming interfaces of
libraries, passing through a description of the syntax of configuration
files, and whatever else one might feel like documenting for ease of
access.
The problem is, man pages are boring. They usually have all the same
structures, with sections that follow a common convention both in the
naming and in the sequence (NAME
, SYNOPSIS
, DESCRIPTION
, SEE
ALSO
, etc.), and they are all boringly black and white, with a sprinkle
of bold and italics/
There's to be said that bold and underline don't really “cut it” in console: undoubtedly, things would stand out more if colors were used to highlight relevant parts of the manual pages (headings, examples, code, etc) rather than simply bold and underline or italics.
Thanks to the power and flexibility of the pagers used to actually
visualize those man pages, a number of people have come out with
simple tricks that colorize pages by just reinterpreting
bold/most
) that does this by default. Of course, most
is otherwise
inferior in many ways to the more common less
pager, so there are
solutions to do the same trick (color replacement) with less
. Both
solutions, as well as a number of other tricks based on the same
principle, are pretty well documented in a number of places, and can be
found summarized on the Arch wiki page on man pages.
I must say I'm not too big a fan of this approach: while it has the huge
advantage of being very generic (in fact, maybe a little too generic),
it has a hackish feeling, which had me look for a cleaner, lower level
approach: making man
itself (or rather the groff
typesetter it uses)
colorize its output.
Colorizing man pages with *roff
The approach I'm going to present will only work if man
uses (a recent
enough version of) groff
that actually supports colors. Also, the
approach is limited to specific markup. It can be extended, but doing so
robustly is non-trivial.
We will essentially do three things:
- tell
groff
to look for (additional) macros in specific directories; - override some typical man page markup to include colors;
- tell
groff
(or rathergrotty
, the terminal post-processor ofgroff
) to enable support for SGR escapes.
Extending the groff
search path
By default, groff
will look for macro packages in a lot of places,
among which the user's home directory. Since cluttering home isn't nice,
we will create a ~/groff
directory and put out overrides in there, but
we also need to tell groff
to look there, which is done by setting
the GROFF_TMAC_PATH
environment variables. So I have in my ~/.profile
the following lines:
GROFF_TMAC_PATH="$HOME/groff"
export GROFF_TMAC_PATH
(Remember to source ~/.profile
if you want to test the benefits of
your override in your live sessions.)
Overriding the man page markup
The groff
macro package used to typeset man pages includes an
arbitrary man.local
file that can be used to override definitions. For
example, in Debian this is used to do some character substitutions based
on whether UTF-8 is enabled or not, and it's found under /etc/groff
.
We will write our own man.local
, and place it under ~/groff
instead,
to override the markup we want to colorize.
Sadly, most of the markup in man pages is presentational rather than
semantic: things are explicitly typeset in
bold/.SH
command to typeset section headers.
So in this example we will only override .SH
to set section headers to
green, leaving the rest of the man pages as-is.
Instead of re-defining .SH
from scratch, we will simply expand it by
adding stuff around the original definition. This can be achieved with
the following lines (put them in your ~/groff/man.local
):
.rn SH SHorg
.de SH
. gcolor green
. SHorg \\$*
. gcolor
..
The code above renames .SH
to .SHorg
, and then defines a new .SH
command that:
- sets the color to green;
- calls
.SHorg
(i.e. the original.SH
), passing all the arguments over to it; - resets the color to whatever it was before.
The exact same approach can be used to colorize the second-level section
header macro, .SS
; just repeat the same code with a general
replacement of H
to S
, and tune the color to your liking.
Another semantic markup that is rather easy to override, even though
it's only rarely used in actual man pages (possibly because it's a GNU
extension), is the .UR
/.UE
pair of commands to typeset URLs, and
its counterpart .MT
/.ME
pair of commands to typeset email addresses.
Both work by storing the email address as the variable \m1
, so all we
need is to override its definition before it's actually used, in the
second element of the pair; for example, if we want to typeset both
URLs and email addresses in cyan, we would use:
.rn ME MEorg
.de ME
. ds m1 \\m[cyan]\\*(m1\\m[]\"
. MEorg \\$*
..
.rn UE UEorg
.de UE
. ds m1 \\m[cyan]\\*(m1\\m[]\"
. UEorg \\$*
..
(Keep in mind that I'm not a groff
expert, so there might be better
ways to achieve these overrides.)
Enabling SGR escapes
The more recent versions of grotty
(the groff
post-processor for
terminal output) uses ANSI (SGR) escape codes for formatting, supporting
both colors and emboldening, italicizing and underlining. On some
distributions (Debian, for example), this is disabled by default, and
must be enabled with some not-well-document method (e.g. exporting
specific environment variables).
Since we already have various overrides in our ~/groff/man.local
,
we can restore the default behavior (enabling SGR escapes by default,
unless the environment variable GROFF_NO_SGR
is set) with the lines:
.if '\V[GROFF_NO_SGR]'' \
. output x X tty: sgr 1
Of course, you should also make sure the pager used by man
supports
SGR escape sequences, for example by making your pager be less
(which
is likely to be the default already, if available) and telling it to
interpret SGR sequences (e.g. by setting the environment variable LESS
to include the -R
option).
Limitations and future work
That's it. Now section headers, emails and URLs will come out typeset in color, provided man pages are written using semantic markup.
It is also possible to override the non-semantic markup that is used
everywhere else, such as all the macros that combine or alternate B
,
I
and R
to mark options, parameters, arguments and types. This would
definitely make pages more colored, but whether or not they will
actually come out decently is all to be seen.
A much harder thing to achieve is the override of commands that
explicitly set the font (e.g. *roff
escape sequences such as \fB
,
often used inline in the code). But at this point the question
becomes: is it worth the effort?
Wouldn't it be better to start a work on the cleanup and extension of
the man macro package for groff
to include (and use!) more semantic
markup, with built-in colorization support?
Bonus track: italics
If your terminal emulator truly supports italics (honestly, a lot of
modern terminals do, except possibly the non-graphical consoles), you
can configure grotty
to output instructions for italics instead of the
usual behavior of replacing italics with underline. This is achieved by
passing the -i
option to grotty
. Since grotty
is rarely (if ever)
called directly, one would usually pass -P-i
option to groff
.
This can be achieved in man
by editing your ~/.manpath
file and
adding the following two lines:
DEFINE troff groff -mandoc -P-i
DEFINE nroff groff -mandoc -P-i
And voilà, italicized rather than underlined italics.