From: [email protected]
Date: 2018-03-04
Subject: Formatting for Gopher with GNU troff

Gopher  is a text-first medium.  Gopher clients are responsible for
formatting the output that a user sees, and this  usually  consists
of  no  more  than wrapping lines at the terminal width.  Even in a
plain text environment, the design principles of contrast,  repeti-
tion, alignment, and proximity play an important role.  There are a
number of ways to take plain text formatting beyond  line-wrapping.
There  are  processors  like fold, par, and pandoc that will handle
basic formatting.  GNU troff gives us a rich set  of  commands  for
formatting text.

In the context of Gopher, formatting means wrapping lines at a pre-
determined length, justifying and filling output,  inserting  para-
graph  breaks,  controlling  indentation, centering text, and more.
Troff (pronounced 'tee-roff') has a long history. [1] The first it-
erations  of this tool were used to format text for output on tele-
types.  Later iterations were used to drive phototypesetters.   To-
day,  GNU troff comes with a variety of preprocessors and post pro-
cessors and can output plain text, HTML, PDF, Postscript, and a va-
riety of other formats.

Troff  uses in-line formatting requests to format content.  For the
purpose of this discussion, there are two types of commands, primi-
tives  and macros.  Primitives can be thought of as the native com-
mand set.  Macros are composed of primitives and/or  other  macros.
Adding  troff formatting requests to a document is not unlike mark-
ing it up with HTML tags.  Troff then processes the source and gen-
erates a separate output file.

GNU  troff  comes with mm and ms macro packages.  These macro pack-
ages aim to make it easier for users to format documents.  Increas-
ing  ease-of-use  usually  comes  at the expense of flexibility and
choice, and that's certainly true in this case.  The authors of the
mm,  ms, and other macro packages wrote them with certain use-cases
in mind.  If you are formatting a memo for  use  at  Bell  Labs  in
1976,  the mm macros are very useful.  Outside of that, it can be a
struggle to format text just the way you want it using one of these
macro packages.  But we can use troff primitives and even write our
own macros.

Because the macro packages didn't let me format my  documents  just
so,  I  started formatting my documents using the troff primitives.
Formatted this way, a short post might look like this:

    .ll 67             \" Set the line length to 67 characters
    .ce 2              \" Center the next two output lines
    Centered Document Title
    .br                \" Look familiar? This inserts a line break.
    Centered Document Subtitle
    .sp 2              \" Insert two vertical spaces (aka blank lines).
    Content goes here.
    .sp 2
    Another paragraph.

We can also control the indentation to  format  code  snippets  and
block quotes.

    .in +5             \" Increase indentation by five
    .ll -5             \" Reduce line length by five
    Block quoted content
    .ll                \" Restore previous line length value
    .in                \" Restore previous indent value

As  you  can  see,  this  leads  to a lot of magic numbers floating
around our source files.  If we want  to  left-justify  headers  or
make  the  block quotes narrower, we have a lot of files to update.
We need to be able to define  these  styles  elsewhere  and  create
aliases that refer to them.

In  our  example  above,  we start by setting the line length to 67
characters.  Let's create an initialization macro  that  will  hold
these document-level settings.  To create a macro, we use the '.de'
request.

    .de IZ             \" Define a macro named IZ
    .ll 67             \" Set the line length to 67
    ..                 \" End the macro definition

That's all there is to it.  Now, we can include '.IZ' at the top of
each document and it will use our macro to set the line length.  In
the future, if we want to change the line length, we only  need  to
change the definition of the IZ macro.

We  can  do  something  similar with our centered headings and sub-
headings.  This is a little different because we may want  to  have
one  or  two sub-headings.  To account for this variability, we are
going to define a macro that accepts the  heading  and  sub-heading
strings  as  arguments,  and uses conditional statements to produce
the output.

    .de H
    .ce
    \\$1
    .if !"\\$2"" \{\
    .br
    .ce
    \\$2\}
    .if !"\\$3"" \{\
    .br
    .ce
    \\$3\}
    .sp 2
    ..

This macro just checks to see if the second and third arguments are
there  before  creating  requests  to break the line and center the
output.  At the end, it inserts two blank lines, but we're  repeat-
ing  our magic number problem.  Let's create a paragraph macro that
will define what happens between paragraphs.

    .de P              \" Define a macro called P
    .sp 2              \" insert two blank lines
    ..

This is a simple macro, but it could be made to do a lot more, like
indent the first line of the following text.  Now we can update our
H macro to use our P macro.

    .de H
    .ce
    \\$1
    .if !"\\$2"" \{\
    .br
    .ce
    \\$2\}
    .if !"\\$3"" \{\
    .br
    .ce
    \\$3\}
    .P
    ..

We're going to put all of our macro definitions in their  own  file
called  style.groff.   Now  that  we  have  a "style sheet", we can
recreate the document from our first example like so:

    .IZ
    .H "Centered Document Title" "Centered Document Subtitle"
    .                  \" A line with just a dot has no effect,
    .                  \" but is useful for structuring documents
    Content goes here.
    .P
    Another paragraph.

Once we have our source document with our  content  and  formatting
commands, we can use groff to ingest our source and produce format-
ted output.  I'm skipping over some details, here, but the  command
will look like this:

    groff -Tascii style.groff <sourcefile> > <outputfile>

You  could  also  leave  off the redirection to a file and pipe the
output to a pager.  It's important to note that when given multiple
file names, as we've done here, groff simply concatenates the files
before processing them.

In my posts folder, I have a subfolder, groff, that contains all my
source  files.   I  have  a  small script I call publish that loops
through all of the source files and creates formatted output in the
posts folder.

    #!
    for f in groff/*.groff
    do
      outfile="${f#groff/}"
      outfile="${outfile%.*}.txt"
      groff -Tascii style.groff "$f" > "$outfile"
    done

There  are  many reference sources available for troff and friends.
The man pages and info pages for groff and the macro  packages  are
comprehensive.   There are a number of excellent printed references
as well:

    B. Srinivasan, "UNIX  Document  Processing  and  Typeset-
    ting", World Scientific, 1993

    Narain  Gehani, "Document Formatting & Typesetting on the
    UNIX System", Silicon Press, 1987

    Tim O'Reilly, Dale  Dougherty,  "UNIX  Text  Processing",
    Hayden Books, 1987
References

[1]: http://troff.org
[2]: gopher://sdf.org/0/users/dbucklin/gopher_groff.groff
[3]: gopher://sdf.org/0/users/dbucklin/style.groff