If you work with macros every day, sooner or later you'll get the urge to write one. Sometimes it's a mild case of the disease: You're creating a document with mm, but you want paragraphs with a first-line indent and no extra space between paragraphs.
Occasionally you want to do something more elaboratelike create a macro package for formatting that screen play.
Before you start, make sure you're familiar with the building blocks. troff provides you with the following: troff primitives (discussed in detail in Chapter 21, "Basic Formatting with troff/nroff"); escape sequences, such as \e and \^ (also
discussed in detail in Chapter 21); other macros, either from a standard macro package, or ones you've written; number registers; and defined strings.
The next section reviews just what a macro is made of and introduces you to concepts that will be explained in detail later in the chapter.
With embedded troff primitives, you can format a page just about any way you want. The trouble is you have to reinvent the wheel every time you write a new document. And every time you format a first-level heading, you have to remember just what
sequence of primitives you used to produce that centered 14-point Helvetica Bold heading. Then you have to type three or four troff requests, the heading itself, and another three or four requests to return to your normal body style. (This is practical
only if you're being paid by the line.) It's a laborious process and one that makes it difficultperhaps impossibleto maintain consistency over a set of files.
Good news: You can use macros to simplify formatting and ensure consistency.
Macros take advantage of one of the UNIX system's distinguishing characteristicsthe ability to build complex processes from basic (primitive) units. A macro is nothing more than a series of troff requests, specified and named, that perform a
special formatting task.
Macros can be simple or complex, short or long, straightforward or cryptic. For example, a new paragraph macro might entail
.sp .5 .ti .5i
This produces spacing of half a line space (nroff) or half an em (troff) between paragraphs, and indents the first line of each paragraph half an inch.
Macro names consist of a period followed by one or two characters. Traditionally, these characters are uppercase, to distinguish them from primitives. (The me package is the exception to this rule.) The paragraph macro above could be called .P or .PP or
.XX.
Macros are invoked in a text file by typing their names. (The period must be in the first position on the line.) Macros can also be invoked with an apostrophe (single quote) instead of a period as the first character. This delays processing of the macro
until the current line has been filled.
A fairly straightforward example of a macro is that centered heading I mentioned earlier. To create it, you need to provide spacing information before and after the heading, font information, size information, and position information (centering).
You could do this as follows:
.sp 2 \"space before the heading .ce 99 \"turns on centering for the next 99 lines . \"to accommodate headings that might start . \"out as 1 line and then get longer .ft HB \"changes the font to Helvetica Bold .ps 14 \changes the point size to 14 points .vs 16 \"changes vertical spacing to 16 points first line of heading second line of heading (optional) third line of heading (optional) .sp \"space after the heading .ce 0 \turns off centering .ft \"returns to default font .ps \returns to default point size .vs \"returns to default vertical space
That simple series of troff primitives illustrates several important points.
Most important, it is full of comments. Comments are identified by the sequence.\"
Note, however, that you can have as many spaces as you want between the initial period and the backslash. In this way, you can put a comment on a line by itself or you can add a comment at the end of a line of troff requests. You can use spaces to line
up your comments so they're easy to read.
Another useful technique, illustrated in the sample above might be called generalization or thinking ahead. Instead of providing for a 1-line heading with a simple .ce, which centers the next line of text, the sample code turns centering on by
requesting .ce 99 (which centers the next 99 lines of text). Most headings are not much longer than that. After the heading lines are specified, the code turns centering off with a .ce 0.
All of that code could be combined into a single pair of macros, called .H1 (for first-level heading) and .HE (for heading end), so that all you need type is
.H1 heading .HE
A big improvement!
But wait. What if the heading came near the bottom of the page? There's nothing in the .H1 macro to prevent the heading from printing all by itself just before a page break. You need at least three lines of text after the heading. Fortunately there's a
troff primitive trained for just this job.ne.
.ne (for need) says "I need the number of lines specified right after me or else I'm going to start a new page." (This is similar to the "Keep with Next" feature of the better word processors and desktop publishing software.)
Perfect. How many lines do you need? Three (so there will be at least three lines of text after the heading), plus one for the heading itself, two more for the spaces before the heading, and one last line for the space after the heading. So a real working
version of the sample heading macro might have .ne 7 at the top.
This may seem like a lot of detail. (It gets worse.) If all you want to do is use a macro package to format documents, you may not want to learn how macros work. But, even if you have no intention of writing a macro yourself, it can be useful to
understand how they work. (It can save you a lot of debugging time.) The more you know about the way macros are written, the easier it is to format a document.
What else might be done with a heading macro like .H1? Well, most often it would be combined with the .HE macro, so all you must type is
.H1 "heading"
Simple for the user, a bit harder for the macro writer.
To provide this kind of service, the .H1 macro would have to allow an argument. (An argument provides additional information for the macro or primitivelike the 7 specified for the .ne primitive. The section "Arguments," later in this
chapter goes into more detail.)
An argument is coded in a special way. troff recognizes $1 as the first argument following the invocation of the macro. (This is a common UNIX convention.) There can be several (up to nine) arguments to a single macro (again, a common UNIX convention).
The code would look something like this:
.ne 7 \"need 7 spaces on page or start new page .sp 2 \"space down 2 spaces before the heading .ce 99 \"turn centering on \f(HB\s+2\\$1\fP\s0 . \"the font is Helvetica Bold, 2 points larger than . \"the body type, the heading itself - $1 -and then return to . \"previous font and default point size .ce 0 \"turn centering off .sp \"space down after the heading
This macro is beginning to get complicated. (That means it's beginning to look like the kind of macro you'll see in a macro package.) But all it really says is the same old thing in a different way. UNIX is famous for providing 75 ways to do everything.
troff code is no exception to this rule.
In the example above, the font change is accomplished by an escape sequence (\f(HB), instead of the .ft primitive. The point size is accomplished the same way (\s+2 instead of .ps), but note that a relative point sizethe current point size plus
2is specified. Next comes the heading itself, the first argument to .H1, specified as $1.
To return to the previous font, use the escape sequence \fP. In many cases, \f1 works just as well. \f1 returns you to the default body type. To return to your original point size, use \s0 (or \s-2). \s0 returns you to the default point size. Since you
don't always know what this is, \s0 can be very useful.
There's just one more concept you need: conditional execution (if statements). Details on conditional statements can be found later in this chapter in the section, "Conditional Statements." How would that work with the heading macro?
For one thing, you could change the macro name to plain .H and then use an argument to specify the heading level.
.H 1 "first-level heading" .H 2 "second-level heading" . . . .H 7 "seventh-level heading"
And this is just what most macro packages do. They provide a general heading macro, and you supply the level and the text for the heading.
What would the code look like?
if \\$1 1 { \"if the heading level is 1, do everything within the . \"curly braces; otherwise skip everything within them . . . \f(HB\s+2\\$fP\s0 . . . }
Similarly,
if \\$1 2 {
for a second-level heading, and so on.
Number registers are locations that store values. They store only whole numbers which can, but need not, have units attached to them. There are three things you can do with a number register:
Number registers are used very frequently in macro definitions. They contain such information as line length, page offset, current font number, previous font number, current indent, current list item number, and so on.
For example, if you're formatting an automatic list (with mm), you would find the following information in number registers: current indent; current nesting level (That is, is the list a list-within-a-list?); item number; format of item number (that is,
Arabic, uppercase roman numeral, lowercase roman numeral, uppercase alphabetic, or lowercase alphabetic).
Every time troff processes a .LI, the number registers that control these characteristics are interpolated. Some of them (the list item number, for example) are also incremented.
This information can be useful if you are formatting what I call a "discontinuous list" (a list that has ordinary text between two of the list items).
Before you insert the ordinary text, you must end the current list. When you want to continue the list, another .AL and .LI will start the list at 1. However, you want it to start at 5. If you know which number register stores this information, you can
reset it.
To set a number register:
.nr a 0 .nr aa 0 .nr AA 1i .nr b +1i
The units are optionaland very tricky. If you specify a unit of measurement (called a scaling factor) with a number register, the value is stored by troff in troff units (u), no matter what unit you specify. Thus, (for a 300dpi device) 1i is
stored as 300u. When you add 1 to the register, you are adding 1 troff unitunless you specify units. Note the following:
.nr x 21 \"has a value of 600u .nr x +1 \"now has a value of 601u .nr x 2i \"has a value of 600u .nr x +1 \"now has a value of 900u
You also have the option of specifying the increment/decrement to the register when you define it:
.nr b 10 1 .nr bb 0 2
Note that you do not specify whether 1 (in the first instance) or 2 (in the second instance) is to be an increment or a decrement. That is done when you interpolate the register.
To interpolate the contents of a number register:
\\na \"one-character name \\n(aa \"two-character name \\n+a \"increments register b \\n-(bb \"decrements register bb
Number registers contain numbers. They are often used in arithmetic expressions:
.if \\na<1 .if \\na=\\nb .if \\na+\\nb<\\nc
There is another arithmetic expression, common in troff, that looks unfinished:
.if \\na \"if a is greater than 0 .if \\na-\\nb \"if a minus b is greater than 0 .if !\\na \"if a is not greater than 0
To increment or decrement a number register, use
.nr a \\na+1 .nr a \\na-1
(Note that you don't use an equal sign when you set a register.)
You can define a number register in terms of another number register (or two):
.nr z (\\nx+\\ny)
Toward the end of this chapter there are two tables of number registers predefined by troff. You will not want to use those names for your own number registers. If you are working with a macro package like ms or mm, however, you must also check the
number registers used by your macro package because you don't want to overwrite the contents of the number register that numbers lists or stores indents.
Sometimes you need to remove registers. This is especially necessary if your macros use a large number of registers. It's a good idea to get into the habit of removing temporary registers as soon as you're done with them. To remove a register, use the
.rr primitive:
.rr a \"remove register a
A defined string is a set of characters to which you assign a name. The string is always treated as a literal, and you cannot perform any arithmetic operation on it. You can, however, compare it to another string, or even compare the string
"2" to the contents of a number register.
A string definition looks a lot like a macro definition:
.ds name value .ds name "value that has a lot of separate words in it .dsU U\s-1NIX\s0 .dsUU "UNIX Unleashed
String names consist of one or two characters. The names come from the same pool as macro names, so be careful to choose a unique name for your string. In the examples above, note that the .ds can (but does not have to be) followed by a space. Note also
that you use only the opening quotation marks when your string consists of multiple words. (If you forget and include a closing quotation mark, it will be printed as part of the string).
To invoke the string:
\\*a \"one-character name \\*(aa \"two-character name
Sometimes a string is a better choice than a number register. If you're dealing with alphabetic characters, a string may be your only choice.
Consider the following: You want to define something to hold the number of your current chapter. If you use a number register, you can increment these numbers very easily. You'll only have to set the value once, at the beginning of the guide. Unless you
have appendixes. If you have appendixes, you'll have to reset to 1 when you reach Appendix A, and then you'll have to translate that number into a letter.
Perhaps a string would be simpler. You'll have to redefine the string at the beginning of each chapter, but you won't have to do any diddling.
Strings can be used as general purpose abbreviations, although this is not their primary purpose, nor even the best use of strings. A better use is to define a string containing the preliminary name of the product you are documenting. Then, when the
marketing people finally decide to call their new brainchild "XYZZY Universal Widget," you don't have to do any searching or grepping to replace the temporary name. You can just redefine the string.
Define a string near the top of your file as the preliminary name of the product:
.ds Pn "Buzzy \"code name for product
Remember that strings cannot share names with macros.
When the ugly duckling "Buzzy" becomes the swan "XYZZY Universal Widget," just change the definition:
.ds Pn "XYZZY Universal Widget \"official name for product
Like macros, strings can have information appended. To add to a string, use the troff primitive .as. Although it's hard to imagine a use for this primitive, consider the following:
You are documenting three versions of the XYZZY Universal Widget in three separate documents. For the first document, you could add "Version 1.0" to the string:
.as Pn "(Version 1.0)
The other versions can be similarly identified in their documents as "Version 2.0" and "Version 3.0."
To define a macro, you use the .de primitive and end the definition with two periods. A macro to indent the first line of a paragraph could be defined like this:
.dePX \"macro to create indented paragraphs, no space between .ti 3P ..
This is a very simple example. A "real" paragraph macro would check to make sure there was room for two or three lines and, if not, go to the next page. Nevertheless, this simple definition illustrates some important points. The macro name can
consist of one or two characters. If you use a name that's already assigned to a macro, the definition in your text file overrides the definition in a macro package. The macro name follows the .de. It can be separated from the .de by a space, but a space
is not necessary.
A macro definition can include troff primitives and other macros. A brief description of the macro is included on the definition line. This is crucial. You can forget more about macros in two weeks that you can learn in two years. Comment lavishly. And
make sure you include a comment on the definition line to identify the macro. This helps when you grep for macro definitions and then sort them.
There is one more constraint on macro names: A macro cannot have the same name as a defined string. (Macros are perfectly happy sharing names with number registers, however.)
If, instead of defining a new macro, you want to redefine an existing one, then you use the existing macro's name:
.deP .ti 3P ..
If you redefine the .P macro, the old definition is no longer used (although it's still sitting there in the mm macro package). To return to the old definition, you must get rid of your new definition (delete it from the top of your file or delete the
file containing the definition).
The benefit to writing a new macro with a new name is that the old definition is still usable. The drawback is that you're used to typing .P, so you'll probably forget to type .PX when you want to use your new macro.
In addition to defining a new macro and redefining an existing macro, you can remove a macro, and you can add to an existing macro.
To define a macro, use .de. You can end the definition, as shown above, with .., or you can use the delimiters of your choice, like this:
,deP!! .ti 3P !!
(I've never actually known anyone who used this form of the definition primitive, but it's there, if you want it. You know how UNIX always provides you with many roads to the same end.)
Once you have written your macro definition, you can add it to an existing macro package, add it to your own file of new macros, and source the file into your text files with .so, or just put the macro definition in your text file.
To remove a macro, use the .rm primitive:
.rmP
Again, the space between the .rm and the macro name is optional.
This is not something that you do on a whim. Removing a macro requires serious, mature consideration. You might do it if you were experimenting with a better version of an existing macroa list end macro (.LE) that left the right amount of space
after it, for example. Your new, improved macro might be called .lE, or .Le. You could encourage people to use your new macro by removing .LE. (This is unlikely to be wise; you always forget to tell your department head who is working on a weekend on a
crucial document, andwell, you can imagine the rest.) A safer way to use your new .Le might be to substitute the definition of .Le for .LE (after it's been tested and found to be truly superior), but to leave the .LE macro definition in the package
and remove it at the end of the macro package file. (Even better, you could comment it out.)
Unless you are very knowledgeable about macros and are in charge of maintaining one or more macro packages, you will never remove a macro.
To rename a macro, use the .rn primitive:
.rnP Pp
As usual, the space between the .rn and the macro name is optional. The space between the old name and the new name is not optional.
Renaming a macro is almost as serious as removing it. And it can be a great deal more complicated. For example, you might want to fix mm's list macro by adding some space after the .LE. You can do this by renaming. Here's what you do:
The new .LE (which is the old .LE plus a half-line space) takes the place of the old .LE.
You might think of using .rn so that you could include the .TH (table column heading) macro in a man file. (.TH is the basic title heading macro used in all man files.)
This seems to be a reasonable idea. If this sort of thing interests you, you can think through the process with me. (Otherwise, skip to "Adding to a Macro.")
The first thing to establish is the conditions for each of the .TH macros: When should .TH mean table heading, and when should it mean title?
That's easy to answer. You want the normal .TH to mean title all the time except following a .TS H. So when do you rename .THand which .TH do you rename? And, if you boldly put .rnTH Th in your file, to which .TH does it refer?
Think about that, and you'll begin to see that maybe .TH is not the ideal candidate for renaming.
To add to a macro definition, use .am:
.amP .ne 2 \"of course this isn't the right place for this request ..
(Yes, the space after the .am is optional.)
Adding to a macro, while not a task for beginners, is a lot more straightforward. .am is often used to collect information for a table of contents. Whenever the file has a .H (of any level, or of specified levels) you want to write that information into
a data file which will be processed and turned into a TOC.
Suppose you've found yourself in the unenviable position of typing a term paper for your child, spouse, or self. It's easy enough to double space the paperjust use .ls 2. But, if you're not using ms, you don't have an easy way of handling long
quotes (which are supposed to be single spaced and indented from the left and right). What do you have to do every time you type a long quotation?
.in +1i .ll -2i .ls 1
And at the end of the quotation, you have to reverse that coding:
.in -1i .ll +2i .ls 2
Instead of typing those three lines, you could define a .Qb and a .Qe macro. Those two sets of three lines are the definitions. All you need to add is a .deQb (or .deQe) to start the macro definition and two dots to end it. If you want to refine the
definition, you can add some space before and after the quotation and a .ne 2 so you don't get one line of the quotation at the bottom of page 5 and the other six lines on page 6:
.deQb .sp .ls 1 .ne 2 .in +1i .ll -2i .. .deOe .br .ls 2 .sp .ne 2 .in -1i .ll +2i ..
Macros, like other UNIX constructs, can take arguments. You specify an argument every time you type a heading after a .H 1 or a .NH. You specify arguments to primitives, too, like .sp .5 or .in +3P. In a macro definition, arguments are represented by
\$1 through \$9. (Yes, you are limited to nine arguments.)
A couple of examples of arguments are:
.deCo \"computer output (CW) font \f(CW\\$1\fP .. .dePi \"paragraph indented amount specified by $1 .br .ne 2 .ti \\$1 ..
(Note that you must hide the argument (with the extra backslash) in order to survive copy mode.)
If you omit an argument, troff treats it as a null argument. In the case of the .Co macro, nothing at all would happen. In the case of the .Pi macro, the paragraph would not be indented. If you specify too many arguments (which would happen if you had
.Co Press Enter in your file), troff merrily throws away the extras. You'd get "Press" in CW font; "Enter" would disappear. Use double quotation marks (.Co "Press Enter") to hide spaces from troff.
A conditional statement says, "Do this under certain (specified) conditions." It may add, "and under any other conditions, do that." You know the conditional statement as an "if" or an "if-else." The troff
versions are .if (if) and .ie (if-else). The troff if has a different syntax from the shell if, but the principle is the same.
A simple if is coded like this:
.if condition simple-action .if condition \{ complex-action \}
The backslash-brace combinations delimit the actions to be taken when the condition is true.
The if-else works like this:
.ie condition simple-action .el simple-action .ie condition \{ complex-action \} .el \{ complex-action \}
You use the conditional statement whenever you want to test for a condition. Is this an even page? Okay, then use the even-page footer. Are these files being nroffed (as opposed to troffed)? Okay, then make the next few lines bold instead of increasing
the point size.
Believe it or not, troff has four built-in conditions to test for just those conditions:
o |
current page is odd |
e |
current page is even |
t |
file is being formatted by troff |
n |
file is being formatted by nroff |
The odd-even conditions simplify writing page header and footer macros. You can simply say:
.if o .tl '''%' \"if odd - page no. on right .if e .tl '%''' \"if even - page no. on left
The single quotation marks delimit fields (left, center, and right). Thus, '''%' places the page number on the right side of the page and '%''' places it on the left side.
You could do the same thing with .ie:
.ie o .tl '''%' \"if odd - page no. on right .el .tl '%''' \"else if even - page no. on left
The .if, even when it requires a seemingly endless list of conditions, is easier to use.
Suppose you are writing that heading macro discussed earlier in this chapter. You want to specify different spacing and different point sizes, depending on the level of the heading. You might start like this:
.deH .if \\$1=1 \{ .bp \s14\f(HB\\$1\fP\s0 .sp \} .if \\$1=2 \{ .sp 2 \s12\f(HB\\$1\fP\s0 .sp |} . . .
You can compare strings, but you use delimiters instead of an equal sign:
.if "\\$1"A" .if '\\$2'Index'
In addition to comparing numbers and strings, you can also test for inverse conditions. troff recognizes the exclamation mark (!) as the reverse of an expression, for example:
.if !o \"same as .if e .if !\\$1=0 \"if $1 is not equal to 0 .if !"\\$1""
(The last example above tests for a null argument.)
Be careful when you use !. It must precede the expression being reversed. For example, to check for an unequal condition, you must write .if !\\na=\\nb. You cannot write .if \\na!=\\nb.
As you see, conditional statements are often combined with arithmetic expressions. You can also use logical expressions. troff understands all of the following:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unlike other UNIX programs, troff has no notion of precedence. An expression like \\$1+\\$2*\\$3-\\$4 is evaluated strictly from left to right. Thus, to troff, 2+3*5-10\2 equals 7.5. This is hard to get used to and easy to forget.
Always specify units.
Diversions let you store text in a particular location (actually a macro that you define), from which the text can be retrieved when you need it. Diversions are used in the "keep" macros and in footnotes.
The diversion command is .di followed by the name of the macro in which the ensuing text is to be stored. A diversion is ended by .di on a line by itself.
Diverted text is processed (formatted) before it is stored, so when you want to print the stored text, all you have to do is specify the macro name. Since there is virtually no limit either to the number of diversions you can have in a file or to the
length of any diversion, you can use diversions to store repeated text.
For example, suppose the following text is repeated many, many times in your document:
.AL 1 ,LI Log in as root. .LI Invoke the UNIX system administrative menu by typing \f(CWsysadm\fP and pressing Enter. .P The system administrative menu is displayed. .LI Select \f(CWEquine Systems\fP by highlighting the line and pressing Enter. .P The Equine Systems menu is displayed
You could store this text in .Em (for Equine Menu) by prefacing it with .diEm and ending it with .di.
Note that your diversion contains an unterminated list. If this is likely to cause problems, add .LE to the diverted text.
To print the Equine Systems text, just put .Em in your file.
In addition to .di, there is a .da (diversion append) primitive that works like .am. .da is used to add text to an existing diversion. It can be used over and over, each time adding more text to the diversion. (To overwrite the text in a given
diversion, just define it again with a .diEm.) The .am primitive can be used, like .am, to create TOC data.
You can even have a diversion within a diversion. The "inside" diversion can be used on its own, as well.
troff provides several kinds of traps: page traps (.wh and .ch); diversion traps (.dt); and input line traps (.it).
Page traps usually invoke macros. For example, when troff gets near the bottom of a page, the trap that produces the page footer is sprung. A simple illustration of this is the following.
Suppose you wanted to print the current date one inch from the bottom of every page in your document. Use the .wh primitive:
.deDa \"define date macro \\n(mo/\\n(dy/18\\n(yr \"set date .. .wh 1i Da \"set the trap
The order of the arguments is important.
To remove this kind of trap, invoke it with the position, but without the macro name: .wh 1i.
The .ch primitive changes a trap. If you wanted the date an inch from the bottom of the page on page 1 of your document, but an inch and a half from the bottom of the page on all subsequent pages, you could use .ch Da 1.5i.
(Note that the argument order is different.)
Diversion traps are set with the .dt primitive, for example:
.dt 1i Xx
This diversion trap, set within the diversion, invokes the .Xx macro when (if) the diversion comes within one inch of the bottom of the page.
Input text traps are set with the .it primitive. This trap is activated after a specified number of lines in your text file.
There is a fourth kind of trap, though it isn't usually thought of as a trap. This is the end macro (.em) primitive. .em is activated automatically at the end of your text file. It can be used to print overflow footnotes, TOCs, bibliographies, etc.
The .ev (environment) primitive gives you the ability to switch to a completely new and independent set of parameters, such as line length, point size, font, and so forth. It lets you return to your original set of parameters just as easily. This
process is known as environment switching. The concept is used in page headers, for example, where the font and point size are always the sameand always different from the font and point size in the rest of the document.
Three environments are available: ev 0 (the normal, or default, environment); ev 1; and ev 2.
To switch from the normal environment, just enter .ev 1 or .ev 2 on a line by itself and specify the new parameters. These new parameters will be in effect until you specify a different environment. To return to your normal environment, use .ev or .ev
0.
You could use environment switching instead of writing the .Qb and .Qe macros. Here's how it would work:
.ev 1 \"long quote begins .sp .ls 1 .in +1i .ll -2i text of quotation .sp .ev
Environments are often used with diversions or with footnotes where the text is set in a smaller point size than body type. It is to accommodate diversions within diversions that the third environment is provided.
Debugging macros is slow and often painful. If you have a version of troff that includes a trace option, use itbut be warned: It produces miles of paper. If you don't have a trace option, you can use the .tm primitive (for terminal message) to
print the value of a number register at certain points in your file. The value is sent to standard error, which is probably your screen. Use .tm like this:
.tm Before calling the Xx macro, the value of xX is \n(xX. .Xx .tm After calling the Xx macro, the value of xX is \n(xX.
(Note that you don't hide the number register from copy mode because you put these lines right in your text file. Remember to delete them before the document goes to the printer.)
Sometimes you have to look at troff output. It's not a pretty sight, but after the first few files, it begins to make sense. Here's the troff code produced by a file with two words in it: UNIX Unleashed.
(By the way, use troff -o > outputfile to produce this output.)
x T post x res 720 1 1 x init v0 p1 x font 1 R x font 2 I x font 3 B x font 4 BI x font 5 CW x font 6 H x font 7 HI x font 8 HB x font 9 S1 x font 10 S s10 f1 H720 V120 cU 72N72I33Xw97U72n50l28e44as39h50e44dn120 0 x trailer v7920 x stop
If you look hard, you can pick out the text in the long line. The numbers are horizontal motions reflecting the width of the letters. You can also see where the font positions are defined. The s10 on a line by itself is the point size. f1 is the font in
position 1 (in this case, Times-Roman). The H and V numbers following the font definition specify the starting horizontal and vertical position on the page.
PostScript output is a little easier to read, but the set-up lines are endless. Where UNIX Unleashed generates 24 lines of troff code, the same two words generate more than 800 lines of PostScript code. The significant lines are at the beginning and the
end. The last 17 lines of the PostScript file are as follows:
setup 2 setdecoding %%EndSetUp %%Page: 1 1 /saveobj save def mark 1 pagesetup 10 R f (\255 1 \255)2 166 1 2797 490 t (UNIX Uleashed) 1 695 1 720 960 t cleartomark showpage saveobj restore %%EndPage: 1 1\%%Trailer done %%Pages: 1 %%DocumentFonts: Times-Roman
Font and point size are specified as 10 R f (10 point Roman). Text is enclosed in parentheses (which makes it easy to find). The showpage is crucial. Every page in your document needs a showpage in the PostScript file. Occasionally, PostScript output is
truncated and the last showpage is lost. No showpage means no printed page.
The following suggestions may be helpful. Most of them are very obvious, but, since I've made all these mistakes myself at one time or another, I pass on this advice:
Starting from scratch is necessary if you intend to sell your macro package. If you just want to provide a nice format for your group, use ms or mm as a basis. Remove all the macros you don't need and add the ones you do need (lists from mm, if you're
using ms, boxes from ms, if you're using mm). Don't reinvent the wheel. Copy, steal, and plagiarize.
Make sure to include autoindexing and automatic generation of master and chapter TOCs.
Write a format script for your users to send their files to the printer, preferably one that will prompt for options if they aren't given on the command line.
Writeand usea test file that includes all the difficult macros you can think of (lists, tables, headers and footers, etc.).
Try to enlist one or two reliable friends to pre-test your package.
You'll never be able to anticipate all the weird things users do to macro packages. Start with a reasonable selection. Save lists within tables within diversions within lists for later.
Don't replace your current macro package with the new one while people are working. Do it at night or after sufficient warning.
Make sure the old macro package is accessible to your users (but not easily accessible, or they won't use your new one).
Don't use PostScript shading if most of your documents are Xeroxed rather than typeset. Copiers wreak havoc on shading. Also, there's always one person in your group who doesn't use a PostScript printer.
If you've gone to the trouble of creating an entire macro package, you want it to be easy to use, and you want it to do everything your users could possible desire. This means that you should provide users with a format script. Although actual programs
for these tools are beyond the scope of this chapter, the following hints should get you started:
Table 26.1 lists the number registers that are predefined by troff. You can change the contents of these registers, but, whatever you do, don't use these names for your own number registers.
Register Name |
Description |
% |
current page number |
ct |
character type (set by \w) |
dl |
(maximum) width of last completed |
dn |
height (vertical size) of last completed diversion |
dw |
current day of the week (1-7) |
dy |
current day of the month (1-31) |
ln |
output line number |
mo |
current month (1-12) |
nl |
vertical position of last printed baseline |
sb |
depth of string below baseline (generated by \w) |
st |
height of string above baseline (generated by \w) |
yr |
last 2 digits of current year |
Table 26.2 lists the read-only number registers that are predefined by troff. You cannot change the contents of these registers, but you can inspect them and use their contents in condition statements and arithmetic expressions.
Register Name |
Description |
$$ |
process id of troff or nroff |
.$ |
number of arguments available at the current macro level |
.a |
post-line extra line-space most recently used in \x'N' |
.A |
set to 1 in troff if -a option used; always 1 in nroff |
.b |
emboldening level |
.c |
number of lines read from current input file |
.d |
current vertical place in current diversion; equal to n1 if no diversion |
.f |
current font number |
.F |
current input filename |
.h |
text baseline high-water mark on current page or diversion |
.H |
available horizontal resolution in basic (troff) units |
.I |
current indent |
.j |
current ad mode |
.k |
current output horizontal position |
.l |
current line length |
.L |
current ls value |
.n |
length of text portion on previous output line |
.o |
current page offset |
.p |
current page length |
.R |
number of unused number registers |
.T |
set to 1 in nroff, if -T option used; always 0 in troff |
.s |
current point size |
.t |
distance to the next trap |
.u |
equal to 1 in fill mode and 0 in no-fill mode |
.v |
current vertical line spacing |
.V |
available vertical resolution in basic (troff) units |
.w |
width of previous character |
.x |
reserved version-dependent register |
.y |
reserved version-dependent register |
.z |
name of current diversion |
Writing one or two macros can be fun and can greatly simplify your life. Start small and easyno number registers defined by other number registers, no renaming, and (if you can manage it) no traps or diversions. Writing macros helps to understand
macro processing, which makes you a more valuable employee.
Writing an entire macro package is a long, difficult process, one that continues for months, even years, after you write that last macro, because someday some user will combine a couple of macros in ways you never dreamed of. Don't write a macro package unless you're prepared to maintain it, provide documentation and user support, and modify it.