groff/troff macros: ms, mm, me or mom

This is a brief explanation of how to choose which macro package to use with the Unix typesetting system troff (or groff). I assume you are familiar with Linux or Unix.

The short answer is: use ms, unless you’re writing a novel (then use mom) or a structured document (then use mm—and find a good reference book for it).

What are troff and groff?

Troff is the original Unix typesetting software. Troff is not like a modern word processor, where the pages of your document appear on-screen just as they will appear on paper and you edit them with a mouse. With troff, your document is a plain-text file mixing text with embedded formatting commands. To see what your document looks like, you process the plain-text file with troff to generate a PDF file, then open the PDF and check to see if you got what you wanted.

An example of a troff formatting command is “.ce”, which centers the next line of text. These simple commands are called “requests.” The other kind of formatting command is called a “macro,” which is essentially a bundle of formatting commands. You could bundle together commands for indenting text, making the font bold and printing sequential numbers, all into a single macro. Then you could use this macro to automatically number and format the headings in your document.

Macros help you separate formatting from text, which can make it easier to change the formatting or the text. If you decide that instead of indenting your headings, you want to center them, you could simply change the “heading” macro instead of manually changing the formatting of each heading in your document. Macros also mean that you can easily copy text from one troff document into another. In a word processor, on the other hand, copied text will carry along formatting from the original document, and unless the two documents have exactly the same styles, you’ll probably need to reformat the copied text from scratch.

One of the greatest Unix programmers, W. Richard Stevens, used troff to typeset all of his books. But even he agreed that troff is not easy to use, saying, “Troff is an industrial strength package that I have spent years of my life learning.” But despite its difficulty, troff can be more efficient to use than a word processor.

Troff was created by Bell Labs in the early 1970’s and came with AT&T Unix for decades. But AT&T Unix is now almost completely obsolete, so you are not likely to find a copy of troff. Instead you are likely to find groff, which is a clone of troff published by the GNU Project. Just about everything above also applies to groff and from here I will only refer to groff unless the distinction is important.

What are ms, mm, me and mom?

You can, if you want, write a groff document entirely from scratch. This would mean defining your own macros for headings, footnotes, bullet lists, and so on. But there is an easier way. There are standard macro packages, each with dozens of useful macros. Four of them are called ms, mm, me and mom.

Bell Labs made the first macro package, ms, for internal use, writing technical papers and reports. It’s pretty easy to use and doesn’t have many weird surprises. You can mix up groff requests and ms macros, which is helpful if you have a clear vision for how you want your document to look. W. Richard Stevens used a customized version of ms for the books he wrote. But ms is more limited than mm or mom, so you need to use groff requests for even basic formatting such as numbered lists. The online documentation for ms is adequate but not great.

At one time, Unix was the most advanced typesetting system in the world, using the mm macro package. This macro package is a lot more full-featured than ms. For example, it has numbered lists (in arabic numerals, roman numerals or letters) built-in. It also supports cross-references, which are essential for structured documents such as technical books. Unfortunately, mm is a little less intuitive than ms and the documentation on the Internet for mm is abysmal. However, there are excellent books on mm from the glory days of Unix typesetting, which you can buy used for cheap.

A developer at the University of California, Berkeley wrote the me macro package in the 1980s. It is more complete than ms, but me’s creator has not worked on it for decades and the documentation is limited to two short papers written by its creator. I do not recommend using me.

The most modern of the four is mom, created by a novelist. It has advanced typographical features and is about as powerful as mm. It is designed for ease-of-use and has excellent online documentation. For many users, mom is the right choice. However for some uses, it may be practically impossible to use mom, and it lacks a few specific features that mm has.

There is also man, which you do not want to use for creating ordinary documents. It is used by the man command to display man pages.

Invoking groff and troff

If you’re wondering why the names of the macro packages all start with “m,” it’s because the “-m” command line argument for groff is used to choose the macro package, for example “groff -ms letter.ms”. The Bell macro packages are abbreviations for “manuscript” and “memorandum.” It seems that “me” is a joke name (as in, Eric P. Allman wrote it himself), and according to its home page, so is “mom” (meaning, it’s a friendlier macro package).

To invoke groff on the input file example.troff, run “groff example.troff > example.ps” (without a macro package) or “groff -ms example.ms > example.ps” (with the macro package ms). This produces a Postscript file. You may also have pdfroff installed, which produces a PDF file instead.

The only AT&T troff I have is the one from Solaris 11. It is more arcane to use. To invoke troff on the input file example.troff, run “troff example.troff | /usr/lib/lp/postscript/dpost > example.ps” (without a macro package) or “troff -ms example.ms | /usr/lib/lp/postscript/dpost > example.ps” (with the macro package ms). This produces a Postscript file.

A simple example

It can be a little tricky to start with groff, so here are simple examples using bare groff, ms, mm and mom.

Plain groffmsmmmom
.sp 1i
Hello, world!
.LP
Hello, world!
.LP
Hello, world!
.PRINTSTYLE TYPESET
.START
.PP
Hello, world!

The plain groff file prints “Hello, world!” with a one-inch top margin (“.sp 1i” means “vertical space one inch”) and a one-inch left margin (the default). The rest of the page is blank. I’ve included this example so you can see that using plain groff is a viable option for simple one-page documents. This lets you avoid the frustration that comes along with using the macro packages as a beginner.

The ms file prints “Hello, world!” with one-inch top and left margins (the default). The rest of the page is blank. The “.LP” macro tells groff that the next line begins a left-flush paragraph. Notice also that groff is case-sensitive. Usually, macros are in uppercase and requests are in lowercase.

The mm file prints a page number in the header. So “Hello, world!” has a 0.963-inch (the default) left margin and a seven lines top margin, putting it slightly lower on the page. (If you are wondering why the default left margin is 0.963 inches in mm, this is 26/27 inches rounded to three decimal places. If you are wondering why 27 is significant, the C/A/T typesetter for which troff was designed had a resolution of 27×16=432 dpi.)

The mom file prints a page number in the footer, rather than the header, and prints “Hello, world!” even lower on the page than mm does. It also uses a larger font than the other three examples. The macros are quite different from ms or mm; I will talk more about this below.

A more realistic example in ms

Let’s say you want a document that has a title, one wide column at the top of the first page and the rest of the document as two columns. It could be a scientific paper or a legal contract, for example. Here I explain just enough about groff to create such a document in ms.

The most confusing part about groff to a beginner is how the paragraph macros (like “.LP” or “.PP”) work. Groff has no notion of a paragraph; it just fills lines on the page with text, one after another, until it has a reason to stop. What the paragraph macros in ms do is tell groff to stop filling lines, to reset certain parameters and to move a little down the page. These parameters include whether the text is centered, so putting “.ce” immediately before “.LP” means the “.ce” request is overridden by the “.LP” macro and therefore has no effect. This is different from a word processor, where it doesn’t matter if you tell the word processor to center text then start a new paragraph or vice-versa.

Next, fonts. As mentioned above, the paragraph macros reset certain groff parameters, and one of those parameters is what the font currently is. In older versions of AT&T troff, this means every paragraph would be set in Times roman unless specifically changed. However, groff added the concept of a font family (typeface) and the request “.fam” to change the current font family. By putting “.fam H” at the top of the document, for example, your whole document will be in Helvetica with no need to change the font in every paragraph. Another benefit of “.fam” is that it lets you use “B” and “I” to refer to bold and italic versions of the current typeface. Otherwise, if your document was set in Helvetica, every time you set a word in bold or italic you would need to ask for Helvetica bold (“HB”) or Helvetica italic (“HI”) specifically to avoid getting Times roman. By the way, fonts in groff can be changed either through a request or an “escape sequence.” An escape sequence is like a request, but it occurs in the middle of text instead of on its own line. For example, “\fB” and “\fR” do the same thing as the requests “.ft B” and “.ft R”, respectively.

Like many computer languages, groff has variables. Numeric variables are called “registers.” You set a register with the “.nr” request and you print a register with the escape sequence “\n” if the register has a one-letter name. For example, “.nr k 42” sets the register “k” to 42, and afterwards “\nk” is replaced by “42” in the document. Macro packages use some registers which you can change to alter formatting. For example, in ms the “HM” register refers to the size of the header margin (i.e., the top margin of the page). As mentioned briefly above, these formatting registers can include suffixes to indicate units of measurement, such as “i” for “inch” or “v” for vertical lines. Therefore, in ms, “.nr HM 1i” sets the header margin (top margin) to one inch. Another useful feature is setting a register to auto-increment, which means that with a single escape sequence you can automatically increase (or decrease) the register by specified amount and print the value of the register. You use the same request “.nr” to set the initial value and the step count, the amount by which the register will increase or decrease. Then you use the escape sequence “\n+” to increment and print the register. For example, “.nr k 0 1” sets the register “k” to zero and the step count to 1. Then “\n+k\n+k\n+k” would be replaced by “123” in the document.

String variables are called “strings” and the request “.ds” is used to define a string. As you might expect, one common use for strings is for unchanging blobs of text to be inserted several times in a document, such as the name of the addressee in a letter. Strings can also include escape sequences for registers (including auto-increment registers) and formatting. You might need to use extra backslashes when putting an escape sequence into a string (consult a reference for more information). To insert a string you use the escape sequence “\*” if the string has a one character name. For example, “\*N” is replaced by “answer” if “.ds N answer” appears earlier in the document.

Now, onto the example. I’ve linked the full ms source and output PDF file. First we setup the document and print the title.

.ds N \\fB\\n+n.\\fRI’m defining a string named “N” using the request “.ds” and I will use this string “N” to number the paragraphs, using an auto-increment register to keep track of the paragraph number. I have named the register “n” and the “.” is just a dot I want printed after the number. The font “R” normally refers to Times roman (non-italic, non-bold Times), but because I will have changed the font family to Helvetica before inserting this string, the font “R” instead refers to non-italic, non-bold Helvetica. Because I am defining a string, all three escape sequences need extra backslashes.
.nr HM 0.75i
.nr FM 0.75i
.nr PO 0.75i
.nr LL 7i
I want to reduce the margins to 0.75 inches on each side. So I’ve set the ms registers for header margin, footer margin and page offset (i.e., left margin) to 0.75 inches. There is no right-margin register in ms. Instead, ms has a line-length register, the amount of text on each line (i.e., the page width minus the left margin minus the right margin). I’ve set the line length to seven inches.
.fam H
.ss 12 0
.nr PS 12
.nr VS 14
.nr PD 0.4v
The document will be set in Helvetica. I’ve set the point-size and vertical spacing (leading) ms registers to 12 and 14. The request “.ss” controls the interword spacing, which should normally be the same as the font size, and controls the the extra space for inter-sentence spacing. (Note for troff purists: The second argument to “.ss” is a groff extension.) The point size is 12, so I’ve set the interword spacing to 12, and consistent with modern design principles, I do not want extra space after the ends of my sentences, so I set the extra inter-sentence space to zero. I’ve set the paragraph distance ms register to 0.4 lines of text; this is the extra vertical space between paragraphs. (This is actually the groff default value, which I’ve included for clarity.)
.LP
.ce 1
.nr n 0 1
.ft B
BABY SHARK
I’ve centered and set in bold the next line of text (not counting request and macro lines), which is the title, “BABY SHARK.” I’m setting the register “n” to zero, and it will be incremented by one.

Next we print the intro paragraph.

.ss 10 0
.nr PS 10
.nr VS 12
Now I set the interword spacing and point size to 10 and the vertical spacing to 12.
.LP
Baby shark, doo doo doo doo doo doo.
Baby...
Left flush paragraph, print the text as one paragraph. I don’t need to change the font to not-bold because “.LP” resets the font to “R” in the current font family.

Next we print the numbered paragraphs.

.sp -0.6v
.MC 3.375i 0.25i
The “.MC” macro in ms turns on multi-column mode. I say how wide I want the columns and the gutter (3⅜ inches and ¼ inch, respectively) and then ms figures out that I want two columns. (Note: if I switch the number of columns again, ms will issue a page break before changing the number of columns.) As a side effect, ms inserts a blank line before the column change, so I space up 0.6 of a line to compensate.
\*N  Baby shark, doo doo doo doo doo doo.
Baby...
I don’t use “.LP” immediately after “.MC” because it would insert unwanted extra space. The escape sequence “\*N” inserts the bold heading number in the string “N,” which is followed by the text.
.LP
\*N  Baby shark, doo doo doo doo doo doo.
Baby...
Same thing. Repeat several times.

Next we print the end, where the signatures would go in a contract. This shows how tabs work in groff.

.LP
.ft B
Agreed as of the Effective Date:
Pretty self-explanatory.
.LP
.ft B
.sp 1v
Mommy shark
.sp 1v
Pretty self-explanatory.
.LP
.nf
.ta 3.375i
.tc \(ru
Doo doo:  (tab)
Doo doo:  (tab)
.sp 1v
The request “.nf” turns off “fill mode.” It means that lines won’t be combined into paragraphs. It is useful for poetry, computer code, or in this case, signature lines on a contract. I set a tab stop using the request “.ta” at the right-edge of the column. Using the request “.tc” and the glyph “\(ru)” (baseline rule), I set the tab character to an underline. The “(tab)” in this table represents an actual tab character in the file. It is replaced by an underline extending to the right-edge of the column. I have a few spaces before the tab character because I want a small gap between the colon and the underline.
The “.LP” macro resets tab stops, but not the tab character. So in the new paragraph, I need to set the tab stop again but I do not need to set the tab character again.

Some complications with ms

In “A simple example,” I moved my text down the page in plain groff by using the “.sp” request. I could also have inserted blank lines. But this would not have worked with the ms macro package. The reason is that at the top of every page, ms enables “no-space mode,” which gobbles up space from blank lines or “.sp” requests, until a new paragraph starts. To turn off no-space mode, you use the requests “.fl” and “.rs” (if you’re working with the first page) or “.rs” (if you’re working on any other page). Consult a reference for more information.

You might think from my examples that ms does not number its pages. In fact it does number its pages. But because ms was created for writing technical papers and reports at Bell Labs, it assumes that the first page is a cover page which should not have a number. In this section’s example, I created a one-page document so ms’s default behavior is what I wanted. If you want a page number on the first page, you will need to redefine the page title “.PT” macro at the top of your input document. Again, consult a reference for more information.

This is more of an obscure issue, but groff ms is not fully initialized until one of the following macros is called: .AB, .LP, .IP, .PP, .XP, .QP, .RS, .NH, .SH, .MC, .RT or .XS. If you are finding that a macro is mysteriously not defined, this could be the problem. Modern troff ms does not have the same issue.

The same example in mm

It’s a little more cumbersome to do the same example (“Baby Shark,” formatted as a one-page contract) using mm instead of ms. The biggest difference is that mm does not allow reducing the margins after the mm macros have been read. Therefore, we set the left margin, page length and line length registers from the command line: “groff -mm -rO0.75 -rP9.5i -rW7i babyshark.mm > babyshark.ps”.

The changes in the input file are minor. First the title:

.ds N \\fB\\n+n.\\fR
.de TP
. sp 0.75i
..
There is no easy-to-use header-margin macro in mm. To reduce the top margin, I redefine the “.TP” macro.
.fam H
.ss 12 0
.S 12 14
.nr Ps 0.8
The “.S” macro sets the point size and vertical spacing. The inter-paragraph spacing is set in an arcane way: the “Ps” register is set to twice the desired amount.
.P
.ce 1
.nr n 0 1
.ft B
BABY SHARK
The “.P” macro replaces the various paragraph macros in ms. The default mode is to be left-flush like “.LP” in ms.

Next the intro paragraph.

.ss 10 0
.S 10 12
Pretty self-explanatory.
.P
.ft R
Baby shark, doo doo doo doo doo doo.
Baby...
Unlike the ms macros, “.P” does not reset the font.

Next we print the numbered paragraphs.

.sp 0.4v
.MC 3.375i 0.25i
Unlike ms, mm does not insert any space when changing the number of columns. So instead of taking away unwanted space, I add space.
\*N Baby shark, doo doo doo doo doo doo. Baby...
Pretty self-explanatory.
.P
\*N Baby shark, doo doo doo doo doo doo.
Baby...
Pretty self-explanatory.

And the end.

.P
.ft B
Agreed as of the Effective Date:
Pretty self-explanatory.
.P
.ft B
.sp 1v
Mommy shark
.sp 1v
Pretty self-explanatory.
.P
.nf
.ta 3.375i
.tc \(ru
Doo doo:  (tab)
Doo doo:  (tab)
.sp 1v
Pretty self-explanatory.
.P
.ft B
Daddy shark
.sp 1v
.P
.nf
.ta 3.375i
Doo doo:  (tab)
Doo doo:  (tab)
Pretty self-explanatory.

The same example in mom

As for mom, this file is actually impossible in any practical way. To set the number of columns in mom, it is necessary to do so before the “.START” macro, however that precludes printing the title and introductory paragraph in single-column mode. You could avoid using the “.START” macro (i.e., not use the mom document processing macros and only the typesetting macros) but then you would not have a way to automatically put the text into columns. Overall, mom is a terrific, well-documented package but not the right choice for every situation.