Interaction tools: dialog.sty and menus.sty

Michael Downes

November 3, 1994

Introduction

This article describes dialog.sty and menus.sty, which provide functions for printing messages or menus on screen and reading users’ responses. The file dialog.sty contains basic message and input-reading functions; menus.sty takes dialog.sty for its base and uses some of its functions in defining more complex menu construction functions. These two files are set up in the form of L^{A}TEX documentstyle option files, but in writing them I spent some extra effort to try to make them usable with plain TEX or other common macro packages that include plain TEX in their base, such as AMS-TEX or Eplain.

The appendix describes grabhedr.sty, required by dialog.sty, which provides two useful file-handling features: (1) a command \inputfwh that when substituted for \input makes it possible to grab information such as file name, version, and date from standardized file headers in the style promoted by Nelson Beebe— and to grab it in the process of first inputting the file, as opposed to inputting the file twice, or \reading the information separately (unreliable due to systemdependent differences in the equivalence of TEX’s \input search path and \openin search path). And (2) functions \localcatcodes and \restorecatcodes that make it possible for dialog.sty (or any file) to manage internal catcode changes properly regardless of the surrounding context.

These files and a few others are combined in a suite of files that goes by the name of dialogl, available on the Internet by anonymous ftp from CTAN (Comprehensive TEX Archive Network), e.g., ftp.shsu.edu (USA), or ftp.uni-stuttgart.de (Europe). The file listout.tex is a utility for verbatim printing of plain text files, with reasonably good handling of overlong lines, tab characters, other nonprinting characters, etc. It uses menus.sty to present an elaborate menu system for changing options (like font size, line spacing, or how many spaces should be printed for a tab character).

Here’s an example from the menu system of listout.tex to demonstrate the use of some features from dialog.sty and menus.sty. First, the menu that you would see if you wanted to change the font or line spacing:

                                                                       ===============================================
                                                                         F  Change font
                                                                         S  Change font size
                                                                         L  Change line spacing
                                                                       Current settings: typewriter 8 / 10.0pt.
                                                                           Q  Quit         X  Exit        ?  Help
                                                                       ===============================================
                                                                       Your choice?

Suppose you wanted to change line spacing to 9 points, so you entered l (lowercase L) and then 9pt, except that on your first attempt you accidentally mistyped 9pe instead of 9pt. Here’s what you would see on screen:

                                                                       Your choice? l
                                                                       Desired line spacing [TeX units] ? 9pe
                                                                       ?---I don't understand "9pe".
                                                                       Desired line spacing [TeX units] ? 9pt
                                                                       * New line spacing: 9.0pt

Both lowercase l and capital L are acceptable responses, and the value given for line spacing is checked to make sure it’s a valid TEX dimension. Before continuing, the internalized version of the user’s value is echoed on screen to confirm that the entered value was read correctly.

Now here’s how the above menu is programmed in listout.tex. A function \menuF is constructed using \fxmenu:

                                                                       \fxmenu\menuF{}{
                                                                       F  Change font
                                                                       S  Change font size

L  Change line spacing
}{
Current settings: &\mainfont &\mainfontsize / %
&\the&\normalbaselineskip.
}
%
\def\moptionF{\lettermenu F}

In the definition of \moptionF, \lettermenu is a high-level function from menus.sty that prints \menuF on screen (given the argument F), reads a line of input from the user, extracts the first character and forces it to uppercase, then branches to the next menu as determined by that character. The response of l causes a branch to the function \moptionFL:

\def\moptionFL{%
\promptmesj{%
Desired line spacing [TeX units] ? }%
\readline{Q}\reply

If Q, X, or ? was entered, the test \xoptiontest will return ‘true’; then we should skip the dimension checking and go directly to \optionexec, which knows what to do with those responses:

\if\xoptiontest\reply
\else

Otherwise we check the given dimension to make sure it’s usable. If so, echo the new value as confirmation.

\checkdimen\reply\dimen@
\ifdim\dimen@>\z@
\normalbaselineskip\dimen@\relax
\normalbaselines
\confirm{New line spacing:
\the\normalbaselineskip}%
\def\reply{Q}%
\fi

If \reply was changed to Q during the above step, \optionexec will pop back up to the previous menu level (normal continuation); otherwise \reply retains its prior definition—e.g., 9pe—to which \optionexec will simply say “I don’t understand that” and repeat the current prompt.

\fi
\optionexec\reply
}

For maximum portability, listout.tex uses in its menus only lowest-common-denominator ordinary printable ASCII characters in the range 32–126. Fancier menus can be obtained at a cost of forgoing system independence, for instance by using emTEX’s /o option to output the box-drawing characters in the standard PC DOS character set.

Notation

Double-hat notation such as ^^J is used herein for control characters, as in The TEXbook, although occasionally the alternate form ‘control-J’ is used when the emphasis is away from the character’s tokenized state inside TEX. A couple of abbreviations from grabhedr.sty are used frequently in the macro code: \xp@ =

\expandafter, and \nx@ = \noexpand. Standard abbreviations from plain.tex

such as \z@ or \toks@ are used without special comment.

Part 1 Basic dialog functions: dialog.sty

1.1 History

This file, dialog.sty, was born out of a utility called listout.tex that I wrote for my personal use. The purpose of listout.tex was to facilitate printing out plain text files—electronic mail, program source files in various programming languages, and, foremost, TEX macro files and log files. An important part of my TEX programming practice is to print out a macro file on paper and read it through, marking corrections along the way, then use the marked copy as a script for editing the file. (For one thing, this allows me to analyze and mark corrections while riding the bus to work, or sitting out in the back yard to supervise the kids.) The output I normally desired was two ‘pages’ per sheet of U.S. letter-size paper printed landscape, in order to conserve paper.

Once created, listout.tex quickly became my favorite means of printing out plain text files, not to mention an indispensable tool in my debugging toolbox: I turn on \tracingmacros and \tracingcommands, then print out the resulting log file so that I can see several hundred lines of the log at once (by spreading out two or three pages on my desk with 100+ lines per page); then I trace through, cross things out, label other things, draw arrows, and so forth.

I soon added a filename prompting loop to make it convenient to print multiple files in a single run. In the process of perfecting this simple prompting routine— over two or three years—and adding the ability to optionally specify things like number of columns at run time, eventually I wrote so much dialog-related macro code that it became clear this code should be moved out of listout.tex into its own module. The result was dialog.sty.

Before getting into the macro definitions and technical commentary, here are descriptions from the user’s perspective of the functions defined in this file.

1.2 Message-sending functions

\mesj{⟨text ⟩}

Sends the message verbatim: category 12 for all special characters except braces, tab characters, and carriage returns:

 { } ^^I ^^M

Naturally, the catcode changes are effective only if \mesj is used directly, not inside a macro argument or definition replacement text.

Multiple spaces in the argument of \mesj print as multiple spaces on screen. A tab character produces always eight spaces; ‘smart’ handling of tabs is more complicated than I would care to attempt.

Line breaks in the argument of \mesj will produce line breaks on screen. That is, you don’t need to enter a special sequence such as ^^J% to get line breaks. See the technical commentary for \mesjsetup for details. Even though curly braces are left with their normal catcodes, they can be printed in a message without any problem, if they occur in balanced pairs. If not, the message should be sent using \xmesj instead of \mesj.

Because of its careful handling of the message text, \mesj is extremely easy to use. The only thing you have to worry about is having properly matched braces. Beyond that, you simply type everything exactly as you want it to appear on screen.

\xmesj{⟨text ⟩}

This is like \mesj but expands embedded control sequences instead of printing them verbatim. All special characters have category 12 except backslash, percent, braces, tab, return, and ampersand:

 \ % { } ^^I ^^M &

The first four have normal TEX catcodes to make it possible to use most normal TEX commands, and comments, in the message text. ^^I and ^^M are catcode 13 and behave as described for \mesj. The & is a special convenience, an abbreviation for \noexpand, to use for controlling expansion inside the message text.

Doubled backslash \\ in the argument will produce a single category 12 backslash character—thus, \\xxx can be used instead of \string\xxx or \noexpand\xxx (notice that this works even for outer things like \bye or \newif). Similarly \%, \{, \} and \& produce the corresponding single characters.

Category 12 space means that you cannot write something like

                                                                        \ifvmode h\else v\fi rule

in the argument of \xmesj without getting a space after the \ifvmode, \else, and \fi.^{1} Since occasionally this may be troublesome, \. is defined inside the argument of \xmesj to be a ‘control word terminator’: If the expansion of \foo is abc, then \foo\.xyz produces abcxyz on screen (as opposed to \foo xyz which would produce abc xyz). Thus the above conditional could be written as

                                                                        \ifvmode\.h\else\.v\fi\.rule

Even though the catcode changes done by \xmesj setup have no effect if \xmesj is used inside an argument or definition replacement text, I find it convenient occasionally to use \xmesj in those contexts, in order to get other aspects of the \xmesj setup. For instance, if you need to embed a message that contains a percent sign inside a definition, you can write

                                                                        \def\foo{...
                                                                          \xmesj{... this is a percent
                                                                            sign: \% (sans backslash) ...}
                                                                        ...}

To further support such uses of \xmesj, the following changes are also done by \xmesj setup: the backslash-space control symbol \␣ is made equivalent to \space; \Ω and \' are defined to produce a \newlinechar; and active tilde ~ will produce a category-12 tilde.

Among other things, this setup makes it easier to obtain newlines and multiple spaces in an embedded message. For example, in the following definition the message will have a line break on screen for each backslash at the end of a line, and the third line will be indented four spaces.

                                                                        \def\bar{...
                                                                          \xmesj{First line\
                                                                            Second line\
                                                                            \ \ \ \ Indented line\
                                                                            Last line}%
                                                                        ...}

The alternative of defining a separate message function \barfoo with \f[x]mesj and calling \barfoo inside of \bar would allow more natural entry of the newlines and the multiple spaces, but would be slightly more expensive in string pool and hash table usage.

                                                                       \promptmesj{⟨text ⟩}
                                                                       \promptxmesj{⟨text ⟩}
                                                                       These are like \mesj, \xmesj but use \message rather than \immediate\write16

internally, thus if the following operation is a \read, the user will see the cursor on

1Well, actually, you could replace each space by %⟨newline⟩ to get rid of it. But that makes the message text harder to read for the programmer.

screen at the end of the last line, as may be desired when prompting for a short reply, rather than at the beginning of the next line. The character ! is preempted internally for newlinechar, for these two functions only, which means that it cannot be actually printed in the message text. Use of a visible character such as !, rather than the normal \newlinechar ^^J, is necessary for robustness because of the fact that the \message primitive was unable to use an ‘invisible’ character (outside the range 32–126) for newlines up until TEX version 3.1415, which some users do not yet have (at the time of this writing—July 1994).

\storemesj\foo{⟨text ⟩}
\storexmesj\foo{⟨text ⟩}

These functions are similar to \mesj, \xmesj but store the given text in the control sequence \foo instead of immediately sending the message. Standard TEX parameter syntax can be used to make \foo a function with arguments, e.g. after

\storemesj\foo#1{...#1...}

then you can later write

\message{\foo{\the\hsize}}

and get the current value of \hsize into the middle of the message text. Consequently also in the x-version \storexmesj a category-12 # character can be obtained with \#.

\fmesj\foobar#1#2...{...#1...#2...}

Defines \foobar as a function that will take the given arguments, sow them into the message text {...}, and send the message. In the message text all special characters are category 12 except for braces, #, tab, and carriage return.

If an unmatched brace or a # must be printed in the message text \fxmesj must be used instead. (## could be used to insert a single category-6 # token into the message text, and TEX would print it without an error, but both \message and \write would print it as two ## characters, even though it’s only a single token internally.)

\fxmesj\foobar#1#2...{...#1...#2...}

Combination of \xmesj and \fmesj. Defines \foobar like \fmesj, but with full expansion of the replacement text and with normal category codes for backslash, percent, braces, and hash #. The control symbols \\ \% \{ \} \& and \. can be used as in \xmesj, with also \# for printing a # character of category 12.

1.3 Reading functions

\readline{⟨default ⟩}\answer

This reads a line of input from the user into the macro \answer. (The macro name can be anything chosen by the programmer, not just \answer.) Before reading, all special characters are deactivated, so that the primitive \read will not choke if the user happens to enter something like \newif or control-l or }. Depending on the operating system, certain characters—e.g., control-c, control-z, controld, control-h—might have special effects instead of being entered into the replacement text of \answer, regardless of the catcode changes. To take the most obvious example, under most operating systems, typing control-h (the Rubout or Backward-Delete key) will delete the previous character from the user’s response, instead of entering an ascii character 8 into \answer.

There is one significant exception from the catcode changes that are done for \readline: spaces and tabs retain their normal catcode of 10, so that multiple spaces in an answer will be reduced to a single space, and macros with normal space-delimited arguments will work when applied to the answer. (I can’t think of any likely scenario where category 12 for spaces would be useful.) Also, the catcode of ^^M is set to 9 (ignore) so that an empty line—meaning that the user just pressed the carriage return/enter key—will result in an empty \answer. If the answer is empty, the given default string will be substituted. The default string can be empty.

\xreadline{⟨default ⟩}\answer

Like \readline but the answer is read as executable tokens; the usual catcodes of the TEX special characters remain in effect while reading the answer. A few common outer things (\bye, \+, \newif, ^^L, among others) are neutralized before the \read is done, but the user can still cause problems by entering some other outer control sequence or unbalanced braces. I doubt there’s any bulletproof solution, if the tokens are to remain executable, short of the usual last resort: reading the answer using \readline, writing it to a file, then inputting the file.

\readchar{⟨default ⟩}\answer

This is like \readline but it reduces the answer to its first character. ⟨default⟩ is either a single character or empty.

\readChar{⟨default ⟩}\answer

This is like \readchar and also uppercases the answer.

\changecase\uppercase\answer

The function \changecase redefines its second argument, which must be a macro, to contain the same text as before, but uppercased or lowercased according to the first argument. Thus \readChar{Q}\answer is equivalent to

 \readchar{q}\answer
 \changecase\uppercase\answer

It might sometimes be desirable to force lower case before using a file name given by the user, for example.

1.4 Checking functions

\checkinteger\reply\tempcount

To read in and check an answer that is supposed to be an integer, use

\readline\reply and then apply \checkinteger to the \reply, supplying a count

register, not necessarily named \tempcount, wherein \checkinteger will leave the validated integer. If \reply does not contain a valid integer the returned value will

be -\maxdimen.

At the present time only decimal digits are handled; some valid TEX numbers such as "AB, `\@, \number\prevgraf, or a count register name, will not be recognized by \checkinteger. There seems to be no bulletproof way to allow these possibilities.

Tests that hide \checkinteger under the hood, such as a \nonnegativeinteger test, are not provided because as often as not the number being prompted for will have to be tested to see if it falls inside a more specific range, such as 0–255 for an 8bit number or 1–31 for a date, and it seems common sense to omit overhead if it would usually be redundant. It’s easy enough to define such a test for yourself, if you want one.

\checkdimen\reply\tempdim

Analog of \checkinteger for dimension values. If \reply does not contain a valid dimension the value returned in \tempdim will be -\maxdimen.

Only explicit dimensions with decimal digits, optional decimal point and more decimal digits, followed by explicit units pt cm in or whatever are checked for; some valid TEX dimensions such as \parindent, .3\baselineskip, or

\fontdimen5\font will not be recognized by \checkdimen.

What good is all this?

What good is all this stuff, practically speaking?—you may ask. Well, a typical application might be something like: At the beginning of a document, prompt interactively to find out if the user wants to print on A4 or US letter-size paper, or change the top or left margin. Such a query could be done like this:

                                                                        \promptxmesj{
                                                                        Do you want to print on A4 or US letter paper?
                                                                        Enter u or U for US letter, anything else for A4: }
                                                                        \readChar{A}\reply % default = A4
                                                                        \if U\reply \textheight=11in \textwidth=8.5in
                                                                        \else \textheight=297mm \textwidth=210mm \fi
                                                                        %    Subtract space for 1-inch margins
                                                                        \addtolength{\textheight}{-2in}
                                                                        \addtolength{\textwidth}{-2in}
                                                                        \promptxmesj{
                                                                        Left margin setting? [Return = keep current value,
                                                                        \the\oddsidemargin]: }
                                                                        \readline{\the\oddsidemargin}\reply
                                                                        \checkdimen\reply{\dimen0}
                                                                        \ifdim\dimen0>-\maxdimen
                                                                          \setlength\oddsidemargin{\dimen0}%
                                                                          \xmesj{OK, using new left margin of %
                                                                        \the\oddsidemargin.}
                                                                        \else
                                                                          \xmesj{Sorry, I don't understand %
                                                                        that reply: `\reply'.\
                                                                        Using default value: \the\oddsidemargin.}
                                                                        \fi

Although L^{A}TEX’s \typeout and \typein functions can be used for this same task, they are rather more awkward, and checking the margin value for validity would be quite difficult.

1.5 Implementation

Standard package identification:

%¡*2e¿

“NeedsTeXFormat–LaTeX2e˝

“ProvidesPackage–dialog˝ [1994/11/08 v0.9y]

%¡/2e¿

1.6 Preliminaries

%¡*2e¿

“RequirePackage–grabhedr˝

%¡/2e¿

If grabhedr.sty is not already loaded, load it now. The \trap.input function is explained in grabhedr.doc.

%¡*209¿ “csname trap.input“endcsname “input grabhedr.sty “relax “fileversiondate–dialog.sty˝ –0.9y˝ –1994/11/08˝ % %¡/209¿

   The functions \localcatcodes and \restorecatcodes are defined in

grabhedr.sty. We use them to save and restore catcodes of any special characters needed in this file whose current catcodes might not be what we want them to be. Saving and restoring catcode of at-sign @ makes this file work equally well as a L^{A}TEX documentstyle option or as a simple input file in other contexts. The double quote character " might be active for German and other languages. Saving and restoring tilde ~, hash #, caret ^, and left quote ` catcodes is normally redundant but reduces the number of assumptions we must rely on. (The following catcodes are assumed: \ 0, { 1, } 2, % 14, a–z A–Z 11, 0–9 . - 12. Also note that \endlinechar is assumed to have a non-null value.)

%% The line break is significant here: “localcatcodes–“@–11˝ “ –10˝ “ –5˝ “˜ –13˝ “”–12˝ “#–6˝ “ˆ –7˝ “‘–12˝ ˝

1.7 Definitions

For deactivating characters with special catcodes during \readline we use, instead of \dospecials, a more bulletproof (albeit slower) combination of \otherchars, \controlchars, and \highchars that covers all characters in the range 0–255 except letters and digits. Handling the characters above 127 triples the overhead done for each read operation or message definition but seems mandatory for maximum robustness.^{2}

\otherchars includes the thirty-three nonalphanumeric visible characters (counting space as visible). It is intended as an executable list like \dospecials but, as an exercise in memory conservation, it is constructed without the \dos. For the usual application of changing catcodes, the list can still be executed nicely as shown below. Also, if we arrange to make sure that each character token gets category 12, it’s not necessary to use control symbols such as \% in place of those few special characters that would otherwise be difficult to place inside of a definition. This avoids a problem that would otherwise arise if we included \+ in the list and tried to process the list with a typical definition of do: \+ is ‘outer’ in plain TEX and would cause an error message when \do attempted to read it as an argument. (As a matter of fact the catcode changes below show a different way around that problem, but a list of category-12 character tokens is a fun thing to have around anyway.)

“begingroup

First we start a group to localize \catcode changes. Then we change all relevant catcodes to 12 except for backslash, open brace, and close brace, which can be handled by judicious application of \escapechar, \string, \edef, and \xdef. By defining \do in a slightly backward way, so that it doesn’t take an argument, we don’t need to worry about the presence of \+ in the list of control symbols. Notice the absence of \` from the list of control symbols; it was already catcoded to 12 in the \localcatcodes declaration at the beginning of this file—otherwise it would be troublesome to make the definition of \do bulletproof (consider the possibilities that ` might have catcode 0, 5, 9, or 14).

“def“do–12 “catcode‘˝

“catcode‘“˜ “do“!“do“@“do“#“do“$“do“ˆ “do“& “do“*“do“(“do“)“do“-“do“˙ “do“=“do“[“do“] “do“;“do“:“do“’“do“”“do“¡“do“¿“do“,“do“. “do“/“do“?“do“—12“relax

To handle backslash and braces, we define \\, \{, and \} to produce the corresponding category-12 character tokens. Setting \escapechar to −1 means that \string will omit the leading backslash that it would otherwise produce when applied to a control sequence.

“escapechar -1 “edef““–“string““˝ “edef“––“string“–˝ “edef“˝ –“string“˝ ˝

Space and percent are done last. Then, with almost all the special characters now category 12, it’s rather easy to define \otherchars.

“catcode‘“ =12“catcode‘“%=12

“xdef“otherchars

– !”#$%&’()*+,-./:;¡=¿?[““]ˆ ˙ ‘“–—“˝ ˜ ˝

“endgroup %  ˆ  ˆ  ˆ

\controlchars is another list for the control characters ascii 0–31 and 127. The construction of this list is similar to the construction of \otherchars. We need to turn off \endlinechar because the catcode of ^^M is going to be changed. The ^^L inside the \gdef is not a problem (as it might have been, due to the usual outerness of ^^L) because the catcode is changed from 13 to 12 before that point.

“begingroup

“endlinechar = -1 “def“do–12 “catcode‘˝ “catcode‘“ˆ ˆ @“do“ˆ ˆ A“do“ˆ ˆ B“do“ˆ ˆ C “do“ˆ ˆ D“do“ˆ ˆ E“do“ˆ ˆ F“do“ˆ ˆ G“do“ˆ ˆ H“do“ˆ ˆ I

2If you are using dialog.sty functions on a slow computer, you might want to try setting \highchars = empty to see if that helps the speed.

“do“ˆ ˆ J“do“ˆ ˆ K“do“ˆ ˆ L“do“ˆ ˆ M“do“ˆ ˆ N“do“ˆ ˆ O “do“ˆ ˆ P“do“ˆ ˆ Q“do“ˆ ˆ R“do“ˆ ˆ S“do“ˆ ˆ T“do“ˆ ˆ U “do“ˆ ˆ V“do“ˆ ˆ W“do“ˆ ˆ X“do“ˆ ˆ Y“do“ˆ ˆ Z“do“ˆ ˆ [ “do“ˆ ˆ ““do“ˆ ˆ ]“do“ˆ ˆ ˆ “do“ˆ ˆ ˙ “do“ˆ ˆ ? 12“relax % “gdef“controlchars–ˆ ˆ @ˆ ˆ Aˆ ˆ Bˆ ˆ Cˆ ˆ Dˆ ˆ Eˆ ˆ Fˆ ˆ G ˆˆ Hˆ ˆ Iˆ ˆ Jˆ ˆ Kˆ ˆ Lˆ ˆ Mˆ ˆ Nˆ ˆ Oˆ ˆ Pˆ ˆ Qˆ ˆ Rˆ ˆ Sˆ ˆ T ˆˆ Uˆ ˆ Vˆ ˆ Wˆ ˆ Xˆ ˆ Yˆ ˆ Zˆ ˆ [ˆ ˆ “ˆ ˆ ]ˆ ˆ ˆ ˆ ˆ ˙ ˆ ˆ ?˝ “endgroup

And finally, the list \highchars contains characters 128–255, the ones that have the eighth bit set.

“begingroup “def“do–12 “catcode‘˝ “catcode‘“ˆ ˆ 80“do“ˆ ˆ 81“do“ˆ ˆ 82“do“ˆ ˆ 83“do“ˆ ˆ 84 “do“ˆ ˆ 85“do“ˆ ˆ 86“do“ˆ ˆ 87“do“ˆ ˆ 88“do“ˆ ˆ 89“do“ˆ ˆ 8a “do“ˆ ˆ 8b“do“ˆ ˆ 8c“do“ˆ ˆ 8d“do“ˆ ˆ 8e“do“ˆ ˆ 8f “do“ˆ ˆ 90“do“ˆ ˆ 91“do“ˆ ˆ 92“do“ˆ ˆ 93“do“ˆ ˆ 94“do“ˆ ˆ 95

...

“do“ˆ ˆ fc“do“ˆ ˆ fd“do“ˆ ˆ fe“do“ˆ ˆ ff 12“relax % “gdef“highchars–% ˆˆ 80ˆ ˆ 81ˆ ˆ 82ˆ ˆ 83ˆ ˆ 84ˆ ˆ 85ˆ ˆ 86ˆ ˆ 87ˆ ˆ 88% ˆˆ 89ˆ ˆ 8aˆ ˆ 8bˆ ˆ 8cˆ ˆ 8dˆ ˆ 8eˆ ˆ 8f% ˆˆ 90ˆ ˆ 91ˆ ˆ 92ˆ ˆ 93ˆ ˆ 94ˆ ˆ 95ˆ ˆ 96ˆ ˆ 97ˆ ˆ 98%

...

ˆˆ f9ˆ ˆ faˆ ˆ fbˆ ˆ fcˆ ˆ fdˆ ˆ feˆ ˆ ff˝ “endgroup

The function \actively makes a given character active and carries out the assignment given as the first argument. The assignment can be embedded in the replacement text of a macro definition without requiring any special setup to produce an active character in the replacement text. The argument should be a control symbol, e.g. \@ or \# or \', rather than a single character. (Except that + is safer than \+ in plain TEX.) If the assignment is a definition (\def, \edef, \gdef, \xdef) it is allowed to take arguments in the normal TEX way. Prefixes such as \global, \long, or \outer must go inside the first argument rather than before \actively.

Usage:

\actively\def\?{⟨replacement text ⟩}
\actively\def\%#1#2{⟨replacement text ⟩}
\actively{\global\let}\^^@=\space

One place where this function can be put to good use is in making ^^M active in order to get special action at the end of each line of input. The usual way of going about this would be to write

 \def\par{something}\obeylines

which is a puzzling construction to the TEX novice who doesn’t know what \obeylines does with \par. The same effect could be gotten a little more transparently with

 \actively\def\^^M{something}

In the definition of \actively we use the unique properties of \lowercase to create an active character with the right character code, overlapping with a \begingroup \endgroup structure that localizes the necessary lc-code change.

“def“actively#1#2–“catcode‘#2“active “begingroup “lccode‘“˜ =‘#2“relax “lowercase–“endgroup#1˜ ˝ ˝

The \mesjsetup function starts a group to localize catcode changes. The group will be closed eventually by a separate function that does the actual sending or stores the message text for later retrieval.

We want to change the catcode of each character in the three lists \otherchars, \controlchars, and \highchars to 12. After giving \do a recursive definition, we apply it to each of the three lists, adding a suitable element at the end of the list to make the recursion stop there. This allows leaving out the \do tokens from the character lists, without incurring the cost of an if test at each recursion step.

“def“mesjsetup–“begingroup “count@=12 “def“do##1–“catcode‘##1“count@ “do˝ %

The abbreviation \xp@ = \expandafter is from grabhedr.sty.

“xp@“do“otherchars–a11 “@gobbletwo˝ % “xp@“do“controlchars–a11 “@gobbletwo˝ % “xp@“do“highchars–a11 “@gobbletwo˝ %

Make the tab character produce eight spaces:

“actively“edef“ˆ ˆ I– “space“space“space “space“space“space“space˝ %

The convenient treatment of newlines in the argument of \mesj (every line break produces a line break on screen) is achieved by making the ^^M character active and defining it to produce a category-12 ^^J character. Although for \mesj it would have sufficed to make ^^M category 12 and locally set \newlinechar = ^^M while sending the message, it turns out to be useful for other functions to have the ^^M character active, so that it can be remapped to an arbitrary function for handling new lines (e.g., perhaps adding extra spaces at the beginning of each line). And if \mesj treats ^^M the same, we can arrange for it to share the setup routines needed for the other functions.

“endlinechar=‘“ˆ ˆ M“actively“let“ˆ ˆ M=“relax “catcode‘“–=1 “catcode‘“˝ =2 ˝

In \sendmesj we go to a little extra trouble to make sure ^^M produces a newline character, no matter what the value of \newlinechar might be in the surrounding environment. The impending \endgroup will restore \newlinechar to its previous value. One reason for using ^^J (instead of, say, ^^M directly) is to allow e.g. \mesj{xxx^^Jxxxx} to be written inside a definition, as is sometimes convenient. This would be difficult with ^^M instead of ^^J because of catcodes.

“def“sendmesj–“newlinechar‘“ˆ ˆ J% “actively“def“ˆ ˆ M–ˆ ˆ J˝ % “immediate“write“sixt@@n–“mesjtext˝ “endgroup˝

Given the support functions defined above, the definition of \mesj is easy: Use \mesjsetup to clear all special catcodes, then set up \sendmesj to be triggered by the next assignment, then read the following balanced-braces group into \mesjtext. As soon as the definition is completed, TEX will execute \sendmesj, which will send the text and close the group that was started in \mesjsetup to localize the catcode changes.

“def“mesj–“mesjsetup “afterassignment“sendmesj “def“mesjtext˝

The \sendprompt function is just like \sendmesj except that it uses \message instead of \write, as might be desired when prompting for user input, so that the onscreen cursor stays on the same line as the prompt instead of hopping down to the beginning of the next line. In order for newlines to work with \message we must use a visible character instead of ^^J. When everyone has TEX version 3.1415 or later this will no longer be true. The choice of ! might be construed (if you wish) as editorial comment that ! should not be shouted at the user in a prompt.

“def“sendprompt–% “newlinechar‘“!“relax “actively“def“ˆ ˆ M–!˝ % “message–“mesjtext˝ “endgroup˝

This function is like \mesj but uses \sendprompt instead of \sendmesj.

“def“promptmesj–“mesjsetup “afterassignment“sendprompt “def“mesjtext˝

Arg #1 of \storemesj is the control sequence under which the message text is to be stored.

“def“storemesj#1–“mesjsetup “catcode‘“#=6 % to allow arguments if needed “afterassignment“endgroup “long“gdef#1˝

While \storemesj\foo{...} is more or less the same as \def\foo{...} with spe-

cial catcode changes, \fmesj\foo{...} corresponds to \def\foo{\mesj{...}},

that is, after \fmesj\foo the function \foo can be executed directly to send the message. Thus \storemesj is typically used for storing pieces of messages, while \fmesj is used for storing entire messages.

To read the parameter text #2, we use the peculiar #{ feature of TEX to read everything up to the opening brace.

“def“fmesj#1#2#–“mesjsetup

“catcode‘“#=6 % restore to normal

The parameter text #2 must be stored in a token register rather than a macro to avoid problems with # characters. The \long prefix is just to admit the (unlikely) possibility of using \fmesj to define something such as an error message saying ‘You can’t use #1 here’ where one of the possibilities for #1 is \string\par.

“toks@–“long“gdef#1#2˝ %

Define \@tempa to put together the first two arguments and [pseudo]argument #3 and make the definition of #1.

“def“@tempa–% “edef“@tempa–% “the“toks@–%

                                                                       The abbreviation \nx@ = \noexpand is from grabhedr.sty.

“begingroup“def“nx@“mesjtext–“the“toks2 ˝ % “nx@“sendmesj˝ %

˝ %

“@tempa

“endgroup % Turn off the “mesjsetup catcodes ˝ % “afterassignment“@tempa “toks2=˝

\xmesjsetup is like \mesjsetup except it prepares to allow control sequence tokens and normal comments in the message text. For TEXnicians’ convenience certain other features are thrown in.

Here, unlike the setup for \xreadline, I don’t bother to remove the outerness of \bye, \newif, etc., because I presume the arguments of \xmesj, \fxmesj, \storexmesj, \fxmenu, etc. are more likely to be written by a TEXnician than by an average end user, whereas \xreadline is designed to handle arbitrary input from arbitrary users.

“def“xmesjsetup–“mesjsetup

Throw in pseudo braces just in case we are inside an \halign with \\ let equal to \cr at the time when \xmesjsetup is called. (As might happen in AMS-TEX.)

“iffalse–“fi

“catcode‘““=0 “catcode‘“%=14

Define \% \\ \{ \} \& to produce the corresponding single characters, category 12. The \lowercase trick here allows these definitions to be nonglobal.

“begingroup “lccode‘“0=‘“““lccode‘“1=‘“–% “lccode‘“2=‘“˝ “lccode‘“3=‘“%% “lowercase–“endgroup “def““–0˝ “def“––1˝ % “def“˝ –2˝ “def“%–3˝ ˝ % “iffalse˝ “fi “edef“&–“string &˝ %

Let & = \noexpand for expansion control inside the argument text; let active ^^M = \relax so that newlines will remain inert during the expansion.

“actively“let“&=“noexpand “actively“let“ˆ ˆ M=“relax

Define \. to be a noop, for terminating a control word when it is followed by letters and no space is wanted.

“def“.–˝ %

Support for use of \xmesj inside a definition replacement text or macro argument: control-space \␣ = \space, tilde ~ prints as itself, \' (i.e., a lone backslash at the end of a line) will produce a newline, also \Ω, while finally \par = blank line translates to two newlines.

“def“ – ˝ “edef˜ –“string ˜ ˝ %

Define \' to produce an active ^^M character, which (we hope) will be suitably defined to produce a newline or whatever.

“begingroup “lccode‘“˜ =‘“ˆ ˆ M%

“lowercase–“endgroup “def“ˆ ˆ M–˜ ˝ ˝ % “let“ˆ ˆ J“ˆ ˆ M “def“par–“ˆ ˆ M“ˆ ˆ M˝ % ˝

\xmesj uses \xmesjsetup and \edef.

“def“xmesj–“xmesjsetup “afterassignment“sendmesj “edef“mesjtext˝

\promptxmesj is analogous to \promptmesj, but with expansion.

“def“promptxmesj–“xmesjsetup “afterassignment“sendprompt “edef“mesjtext˝

And \storexmesj is like \storemesj, with expansion. Since we allow arguments for the function being defined, we also must define \# to produce a single category-12 # character so that there will be a way to print # in the message text.

“def“storexmesj#1#2#–“xmesjsetup “catcode‘“#=6 % to allow arguments if needed “edef“#–“string##˝ % “afterassignment“endgroup “long“xdef#1#2˝

And \fxmesj is the expansive analog of \fmesj.

“def“fxmesj#1#2#–“xmesjsetup

“catcode‘“#=6 % restore to normal

“edef“#–“string##˝ % “toks@–“long“xdef#1#2˝ % “def“@tempa–% “edef“@tempa–% “the“toks@–“begingroup “def“nx@“nx@“nx@“mesjtext–“the“toks“tw@˝ % “nx@“nx@“nx@“sendmesj˝ ˝ % “@tempa % execute the constructed xdef “endgroup % restore normal catcodes ˝ % “afterassignment“@tempa “toks“tw@=˝

1.8 Reading functions

The \readline function gets one line of input from the user. Arguments are: #1 default to be used if the user response is empty (i.e., if the user just pressed the return/enter key), #2 macro to receive the input.

“def“readline#1#2–%

“begingroup “count@ 12 %

“def“do##1–“catcode‘##1“count@ “do˝ % “xp@“do“otherchars–a11 “@gobbletwo˝ % “xp@“do“controlchars–a11 “@gobbletwo˝ % “xp@“do“highchars–a11 “@gobbletwo˝ %

Make spaces and tabs normal instead of category 12.

“catcode‘“ =10 “catcode‘“ˆ ˆ I=10 %

“catcode‘“ˆ ˆ M=9 % ignore

Reset end-of-line char to normal, just in case.

“endlinechar‘“ˆ ˆ M

We go to a little trouble to avoid \gdef-ing #2, in order to prevent save stack buildup if the user of \readline carries on unaware doing local redefinitions of #2 after the initial read.

“read“m@ne to#2%

“edef#2–“def“nx@#2–#2˝ ˝ %

“xp@“endgroup #2%

“ifx“@empty#2“def#2–#1˝ “fi

˝

\xreadline is like \readline except that it leaves almost all catcodes unchanged so that the return value is executable tokens instead of strictly character tokens of category 11 or 12.

“def“xreadline#1#2–% “begingroup

Render some outer control sequences innocuous.

“xp@“let“csname bye“endcsname“relax “xp@“let“csname newif“endcsname“relax “xp@“let“csname newcount“endcsname“relax “xp@“let“csname newdimen“endcsname“relax “xp@“let“csname newskip“endcsname“relax “xp@“let“csname newmuskip“endcsname“relax “xp@“let“csname newtoks“endcsname“relax “xp@“let“csname newbox“endcsname“relax “xp@“let“csname newinsert“endcsname“relax “xp@“let“csname +“endcsname“relax “actively“let“ˆ ˆ L“relax “catcode‘“ˆ ˆ M=9 % ignore “endlinechar‘“ˆ ˆ M% reset to normal “read“m@ne to#2% “toks@“xp@–#2˝ % “edef“@tempa–“def“nx@#2–“the“toks@˝ ˝ % “xp@“endgroup “@tempa “ifx“@empty#2“def#2–#1˝ “fi ˝

\readchar reduces the user response to a single character.

“def“readchar#1#2–% “readline–#1˝ #2%

If the user’s response and the default response are both empty, we need something after #1 to keep \@car from running away, so we add an empty pair of braces.

“edef#2–“xp@“@car#2#1–˝ “@nil˝ % ˝

\readChar reduces the user response to a single uppercase character. (This is useful to simplify testing the response later with \if.)

“def“readChar#1#2–% “readline–#1˝ #2% “changecase“uppercase#2%

Reduce #2 to its first character, or the first character of #1, if #2 is empty. The extra braces {} are to prevent a runaway argument error from \@car if #2 and #1 are both empty.

“edef#2–“xp@“@car #2#1–˝ “@nil˝ % ˝

The function \changecase uppercases or lowercases the replacement text of its second argument, which must be a macro. The first argument should be \uppercase

or \lowercase.

“def“changecase#1#2–“@casetoks“xp@–#2˝ % “edef#2–#1–“def“nx@#2–“the“@casetoks˝ ˝ ˝ #2˝

We allocate a token register just for the use of \changecase because it might be used at a low level internally where we don’t want to interfere with other uses of the scratch token registers 0–9.

“newtoks“@casetoks

A common task in reading user input is to verify, when an answer of a certain kind was requested, that the response has indeed the desired form—for example, if a nonnegative integer is required for subsequent processing, it behooves us to verify that we have a nonnegative integer in hand before doing anything that might lead to inconvenient error messages. However, it’s not easy to decide how best to handle such verification. One possibility might be to have a function

 \readnonnegativeinteger\foo

to do all the work of going out and fetching a number from the user and leaving it in the macro \foo. Another possibility would be to read the response using \readline and then apply a separate function that can be used in combination with \if, for example

   \readline{}\reply
   \if\validnumber\reply ... \else ... \fi

For maximum flexibility, a slightly lower-level approach is chosen here. The target syntax is

   \readline{}\reply
   \checkinteger\reply\tempcount

where \tempcount will be set to -\maxdimen if \reply does not contain a valid integer. (Negative integers are allowed, as long as they are greater than -\maxdimen.) Then the function that calls \checkinteger is free to make additional checks on the range of the reply and give error messages tailored to the circumstances. And the handling of an empty \reply can be arbitrarily customized, something that would tend to be inconvenient for the first method mentioned.

The first and second approaches can be built on top of the third if desired, e.g. (for the second approach)

 \def\validnumber#1{TT\fi
   \checkinteger#1\tempcount%
   \ifnum\tempcount>-\maxdimen }

The curious TT\fi...\ifnum construction is from TeXhax 1989, no. 20 and no. 38 (a suggestion of D. E. Knuth in reply to a query by S. von Bechtolsheim).

The arguments of \checkinteger’s are: #2, a count register to hold the result; #1, a macro holding zero or more arbitrary characters of category 11 or 12.

“def“checkinteger#1#2–“let“scansign@“@empty “def“scanresult@–#2˝ % “xp@“scanint#1x“endscan˝

To validate a number, the function \scanint must first scan away leading + or - signs (keeping track in \scansign@), then look at the first token after that: if it’s a digit, fine, scan that digit and any succeeding digits into the given count register (\scanresult@), ending with \endscan to get rid of any following garbage tokens that might just possibly show up.

Typical usage of \scanint includes initializing \scansign@ to empty, as in the

definition of \checkinteger.
\let\scansign@\@empty
\def\scanresult@{\tempcount}%
\xp@\scanint\reply x\endscan

Assumption: \reply is either empty or contains only category 11 or 12 characters (which it will if you used \readline!). If a separate check is done earlier to trap the case where \reply is empty—for example, by using a nonempty default for \readline—then the x before \endscan is superfluous.

Arg #1 = next character from the string being tested. The test whether #1 is a decimal digit is similar in spirit to the test \if!#1! to see if an argument is empty (The TEXbook, Appendix D, p. 376).

“def“scanint#1–% “ifodd 0#11 %

Is #1 a decimal digit? If so read all digits into \scanresult@ with the sign prefix.

“def“@tempa–“afterassignment“endscan “scanresult@=“scansign@#1˝ %

“else

“if -#1“relax

Here we flipflop the sign; watch closely.

“edef“scansign@–%

“ifx“@empty“scansign@ -“fi˝ % “def“@tempa–“scanint˝ % “else

A plus sign can just be ignored.

“if +#1“relax “def“@tempa–“scanint˝ % “else % not a valid number “def“@tempa–% “scanresult@=-“maxdimen“endscan˝ %

“fi“fi“fi “@tempa ˝

The \endscan function just gobbles any remaining garbage. It uses its own name as the argument delimiter.

“def“endscan#1“endscan–˝

\dimenfirstpart, a count register, receives the digits, if any, preceding the decimal point. \dimentoks, a token register, receives any digits after the decimal point.

“newcount“dimenfirstpart

“newtoks“dimentoks

\scandimen is similar to \scanint but has to call some auxiliary functions to scan the various subcomponents of a dimension (leading digits, decimal point, fractional part, and units, with optional true, in addition to the sign). The minimum requirements of TEX’s syntax for dimensions are a digit or decimal point + the units; all the other components are optional (The TEXbook, Exercise 10.3, p. 58).

When scanning for the digits of a fractional part, we can’t throw away leading zeros; therefore we don’t read the fractional part into a count register as we did for the digits before the decimal point; instead we read the digits one by one and store them in \dimentoks.

The function that calls \scandimen should initialize \scansign@ to \@empty,

                                                                       \dimenfirstpart to \z@, \dimentoks to empty {}, and \dimentrue@ to \@empty.
                                                                          Test values: 0pt, 1.1in, -2cm, .3mm, 0.4dd, 5.cc, .10000000009pc, \hsize,
                                                                       em.

“def“scandimen#1–%

“ifodd 0#11 “def“@tempa–“def“@tempa–“scandimenb˝ % “afterassignment“@tempa “dimenfirstpart#1˝ %

“else

The following test resolves to true if #1 is either a period or a comma (both recognized by TEX as decimal point characters).

“if “if,#1.“else#1“fi.%

“def“@tempa–“scandimenc˝ % “else “if -#1% then flipflop the sign “edef“scansign@–% “ifx“@empty“scansign@ -“fi˝ % “def“@tempa–“scandimen˝ % “else “if +#1% then ignore it “def“@tempa–“scandimen˝ % “else % not a valid dimen “def“@tempa–% “scanresult@=-“maxdimen“endscan˝ %

“fi“fi“fi“fi “@tempa ˝

Scan for an optional decimal point.

“def“scandimenb#1–% “if “if,#1.“else#1“fi.% “def“@tempa–“scandimenc˝ % “else

If the decimal point is absent, we need to put back #2 and rescan it to see if it is the first letter of the units.

“def“@tempa–“scanunitsa#1˝ % “fi “@tempa ˝

Scan for the fractional part: digits after the decimal point.

“def“scandimenc#1–%

If #1 is a digit, add it to \dimentoks.

“ifodd 0#11 “dimentoks“xp@–% “the“dimentoks#1˝ % “def“@tempa–“scandimenc˝ % “else

Otherwise rescan #1, presumably the first letter of the units.

“def“@tempa–“scanunitsa#1˝ % “fi “@tempa ˝

“def“scanunitsa#1“endscan–%

Check for true qualifier.

“def“@tempa##1true##2##3“@tempa–##2˝ %

The peculiar nature of \lowercase is evident here as we are able to apply it to only the test part of the conditional without running into brace-matching problems. (Compare the braces in this example to something like \message{\iffalse

A}\else B}\fi.)

“lowercase–%

“xp@“ifx“xp@“end

“@tempa#1true“end“@tempa ˝ %

No true was found:

“let“dimentrue@“@empty

“def“@tempa–“scanunitsb#1“endscan˝ % “else “def“dimentrue@–true˝ % “def“@tempa##1true##2“@tempa–% “def“@tempa–##1˝ % “ifx“@tempa“@empty “def“@tempa–“scanunitsb##2“endscan˝ % “else “def“@tempa–“scanunitsb xx“endscan˝ % “fi˝ % “@tempa#1“@tempa “fi “@tempa ˝

Scan for the name of the units and complete the assignment of the scanned value to \scanresult@. Notice that, because of the way \scanunitsb picks up #1 and #2 as macro arguments, p t is allowed as a variation of pt. Eliminating this permissiveness doesn’t seem worth the speed penalty that would be incurred in \scanunitsb.

The method for detecting a valid units string is to define the scratch function \@tempa to apply TEX’s parameter-matching abilities to a special string that will yield a boolean value of true if and only if the given string is a valid TEX unit.

“def“scanunitsb#1#2–% “def“@tempa##1#1#2##2##3“@nil–##2˝ % “def“@tempb##1–T“@tempa pcTptTcmTccTemTexTinTmmTddTspT##1F“@nil˝ %

Force lowercase just in case the units were entered with uppercase letters (accepted by TEX, so we had better accept uppercase also).

“lowercase–% “if“@tempb–#1#2˝ % ˝ % “scanresult@=“scansign@ “number“dimenfirstpart.“the“dimentoks

“dimentrue@#1#2“relax “else “scanresult@=-“maxdimen “fi

Call \endscan to gobble garbage tokens, if any.

“endscan ˝

Argument #2 must be a dimen register; #1 is expected to be a macro holding zero or more arbitrary characters of category 11 or 12.

“def“checkdimen#1#2–% “let“scansign@“@empty “def“scanresult@–#2˝ % “let“dimentrue@“@empty “dimenfirstpart“z@ “dimentoks–˝ % “xp@“scandimen#1xx“endscan ˝

Finish up.

“restorecatcodes “endinput

Part 2 Menu functions: menus.sty

2.1 Function descriptions

\fmenu\foobar{

⟨preliminary text ⟩

}{

⟨menu lines ⟩

}{

⟨following text ⟩

}

Defines \foobar as a function that puts the preliminary text, the menu lines (list of choices), and the after text on screen. Normal usage:

\foobar           % print the menu on screen
\readline{}\reply % read the answer

(See the description of \readline in dialog.doc.) In the various text parts all special characters have category 12 except for braces, as with \mesj. Note the recommended placement of the braces: no closing brace falls at the end of a line, except the very last one. Because of the special catcodes in effect when reading the final three arguments, a ^^M or % between arguments would be read as an active character or category-12 character respectively, instead of being ignored. But actually, after some rather difficult programming, I managed to make it possible to write just about anything (except brace characters) between the arguments and have it be ignored, so the recommended style is not mandatory. The first and last newline of each argument are stripped off anyway in order to produce consistent clean connections with \menuprefix etc.; see below.

Menu functions created by \fmenu are allowed optionally to have arguments, like functions created with \fmesj (from dialog.sty), so that pieces of text can be inserted at the time of use. This makes it possible for several similar menus to share the same menu function if there are only minor variations between them.

\menuprefix, \menusuffix
\inmenuA, \inmenuB

The text \menuprefix will be added at the beginning of each menu; \menusuffix will be added at the end. The text \inmenuA and \inmenuB will be added between the first and second, respectively second and third parts of the menu; their default values produce a blank line on screen. (But \inmenuA will be omitted if the first part is empty, and \inmenuB will be omitted if the last part is empty.) To change any of these texts, use \storemesj or \storexmesj. For example:

\storemesj\menuprefix{********* MENU **********}
\menuprompt

Furthermore, the function \menuprompt is called at the very end of the menu, so that for example a standard prompt such as Enter a number: could be applied at the end of all menus, if desired. To change \menuprompt, use \fmesj or \fxmesj.

\menuline, \endmenuline
\menutopline, \menubotline

Each line in the middle argument of \fmenu (the list of choices) is embedded in a statement \menuline...\endmenuline. The default definition of \menuline is to add two spaces at the beginning and a newline at the end. Lines in the top or bottom part of the menu are embedded in \menutopline...\endmenuline or \menubotline...\endmenuline respectively. (Notice that all three share the same ending delimiter; if different actions are wanted at the end of a top or bottom line as opposed to a middle menu line, they must be obtained by defining \menutopline or \menubotline to read the entire line as an argument and perform the desired processing.)

An enclosing box for a menu can be obtained by defining \menuline and its relatives appropriately and using \fxmenu (see below).

\fxmenu\foobar{

⟨preliminary text ⟩

}{

⟨menu lines ⟩

}{

⟨following text ⟩

}

Similar to \fmenu but with full expansion in each part of the text, as with \xmesj.

To get an enclosing box for a menu, write \. at the end of each menu line (to protect the preceding spaces from TEX’s propensity to remove character 32 at the end of a line, regardless of its catcode), and then make sure that \menuline and \endmenuline put in the appropriate box-drawing characters on either side. I.e.:

\fxmenu\foobar{
First line                                   \.
Second line                                  \.
}{
Third line                                   \.
...
}{
Last line                                    \.
}

With the /o option of emTEX, you can use the box-drawing characters in the standard PC DOS character set.

\nmenu\Alph\foobar#1{

⟨preliminary text ⟩

}{

⟨menu lines ⟩

}{

⟨following text ⟩

}

\nmenu and \nxmenu are like \fmenu, \fxmenu except that they automatically number each line of the middle part of the menu. (This allows menu choices to be added or deleted without tedious renumbering.) The first argument indicates the type of numbers to be used: \alph, \Alph, \arabic, \roman, \Roman (following L^{A}TEX). These are not yet implemented.

The function \menunumber (taking one argument) is applied to each automatically generated number. The default value is to add brackets and a space after:

\def\menunumber#1{[#1] }

but by redefining \menunumber you can add parentheses or extra spaces or what have you around each number. Internally a line of an autonumbered menu is stored as

 \menuline\menunumber{5}Text text ...\endmenuline
\optionexec\answer

This is a companion function for \readChar and the menu functions: it checks to see if the answer is equal to any one of the characters ? Q q X x, and if so executes \moption? or \moptionQ or \moptionX respectively, otherwise executes

 \csname moption\curmenu C\endcsname

where C means the character that was read and \curmenu is a string identifying the current location in the menu system. (\optionexec pushes and pops \curmenu when going between menus, to keep it up to date.) If this control sequence is undefined, \optionexec gives a generic “Sorry, I don’t understand” message and repeats the current menu.

Thus the major work involved in making a menu system is to define the menu screens using \fmenu, \fxmenu, and then define corresponding functions \moptionXXX that display one of the menu screens, read a menu choice, and call \optionexec to branch to the next action.

\specialhelp\answer{Substitute message}

As it turns out, it is sometimes desirable to substitute some other message in place of the generic “Sorry, I don’t understand” message given by \optionexec. For instance, suppose a given menu choice leads to a secondary prompt where you ask the user to enter a number of columns for printing some data. If the user accidentally mistypes 0, it would be better to respond with something like

 Number of columns must be greater than 0.

than with the generic message. The function \specialhelp allows you to do this. The first argument is the name of the macro that will be passed to \optionexec. \specialhelp modifies that macro to a flag value that will trigger the substitute message. (But does it carefully, so that you can still use the macro naturally in the substitute message text.)

\optionfileexec\answer

Like \optionexec, but gets the next menu from a file instead of from main memory, if applicable. This is not yet implemented. The technical complications involved in managing the menu files are many—for example: How do you prevent the usual file name message of TEX from intruding on your carefully designed menu screens, if \input is used to read the next menu file? Alternatively if you try to use \read to read the next menu file, how do you deal with catcode changes?

\lettermenu{MN}

This is an abbreviation for

 \menuMN \readChar{Q}\reply \optionexec\reply

It calls the menu function associated with the menu name MN, reads a single uppercase letter into \reply, and then calls \optionexec to branch to the case selected by the reply.

\if\xoptiontest\answer ... \else ... \fi

The function \xoptiontest is for use with \readline or \xreadline, to trap the special responses ? Q q X x before executing some conditional code. It returns a ‘true’ value suitable for \if testing, if and only if the replacement text of \answer is a single character matching one of those listed. This is used when you are prompting for a response that can be an arbitrary string of characters, but you want to allow the user still to get help or quit with the same one-character responses that are recognized in other situations.

2.2 Implementation

Standard package identification:

%¡*2e¿ “NeedsTeXFormat–LaTeX2e˝ “ProvidesPackage–menus˝ [1994/11/08 v0.9x]

Load the dialog package if necessary.

“RequirePackage–dialog˝ %¡/2e¿

   This file requires grabhedr.sty and dialog.sty. If grabhedr.sty is not al-

ready loaded, load it now and call \fileversiondate, since it’s too late to apply \inputfwh to this file. See the documentation of \trap.input in grabhedr.doc.

%¡*209¿ “csname trap.input“endcsname “input grabhedr.sty “relax “fileversiondate–menus.sty˝ –0.9x˝ –1994/11/08˝ “inputfwh–dialog.sty˝ %¡/209¿

2.3 Definitions

We start by using the \localcatcodes function from grabhedr.sty to save current catcodes and set new catcodes for certain significant characters, as explained at more length in dialog.doc.

“localcatcodes–“@–11˝ %

“˜ –13˝ “”–12˝ “#–6˝ “ˆ –7˝ “‘–12˝ “$–3˝ “:–12˝ ˝

\menuprefix is a string added at the beginning of each menu to pretty it up a little (or uglify it a little, depending on your taste). The length of the default string is 70 characters, not counting the two newline characters. By using \storexmesj we get embedded newlines corresponding to the ones seen here. [That is, except for the extra line break (where the newline character is commented out), needed to make this fit in the current column width.]

                                                                        \storexmesj\menuprefix{
                                                                        ===================================%
                                                                        ===================================
                                                                        }

The default value for \menusuffix is the same as for \menuprefix.

“let“menusuffix=“menuprefix

The default for \inmenuA and \inmenuB is a single newline, which will produce a blank line on screen because they will occur after an \endmenuline, which also contains a newline.

“storemesj“inmenuA–

˝

“storemesj“inmenuB–

˝

The default value for \menuline is two spaces. This means that each line in the middle section of a menu defined by \fmenu or \fxmenu will be indented two spaces.

“storemesj“menuline– ˝

By default, no spaces are added at the beginning of a line in the top or bottom section of a menu:

“def“menutopline–˝ “def“menubotline–˝

\endmenuline is just a newline.

“storemesj“endmenuline–

˝ %

This definition of \menunumber adds square brackets and a following space around each item number.

“def“menunumber#1–[#1] ˝

This definition of \menuprompt is suitable for the purposes of listout.tex but will probably need to be no-op’d or changed for other applications.

“def“menuprompt–“promptmesj–Your choice? ˝ ˝

Each of the three pieces of a menu gets its own token register.

“newtoks“menufirstpart “newtoks“menuchoices “newtoks“menulastpart

The ‘arguments’ of \fmenu are #1 menu name, #2 optional argument specifiers, #3 preliminary text, #4 list of menu choices, #5 following text. But at first we read only the first two because we want to change some catcodes before reading the others. The auxiliary function \fxmenub is shared with \fxmenu.

Because of the catcode changes done by \mesjsetup, newlines, spaces, or percent signs between the three final arguments will not be ignored. To get around this, we use the peculiar #{ feature of TEX, in intermediate scratch functions called \@tempa, to read and discard anything that may occur between one closing brace and the next opening brace. Token register assignments are used to read the arguments proper.

“def“fmenu#1#2#–“mesjsetup “catcode‘“#=6 % for parameters “toks@–“fxmenub–“gdef˝ –“begingroup˝ –˝ #1–#2˝ ˝ % “def“@tempa##1##–% “def“@tempa####1####–% “def“@tempa–“the“toks@˝ % “afterassignment“@tempa “menulastpart˝ % “afterassignment“@tempa “menuchoices˝ % “afterassignment“@tempa “menufirstpart ˝

Before proceeding to define \fxmenub, we must deal with a subproblem. What we will have to work with is three pieces of text in the token registers \menufirstpart, \menuchoices, and \menulastpart, containing active ^^M characters to mark line breaks, including possibly but not necessarily ^^M at the beginning and at the end of each piece. What we would like to do, for each piece, is to remove the first ^^M, if there is one, and the last one, if there is one. The function \stripcontrolMs does this.

If you are one of those rare TEX hackers who might actually want to understand the workings of \stripcontrolMs, the best way is probably to watch it in action with \tracingmacros = \tracingcommands = 2, rather than attempt to follow my labored commentary below.

“begingroup % localize “lccode change “lccode‘“˜ =‘“ˆ ˆ M

The functions \stripM, \stripMa, \stripMb, … are auxiliary functions for \stripcontrolMs. They all carry along an extra last argument, the name of the token register originally given to \stripcontrolMs, so that when we finally reach \stripMd we can carry out the assignment of the token register.

When \stripM is called, it should be called like this:

% \expandafter\stripM\expandafter$\the\sometoks
% $^^M$$\stripM\sometoks
%

That is, $ should be added at the beginning and $^^M$$ at the end of the text to be processed. And \expandafter’s should be added to pre-expand the token register.

These examples illustrate the possible contents of (e.g.) \menufirstpart, before processing

 (a) ^^Maaa^^Mbbb^^M
 (b) aaa^^Mbbb
 (c) ^^Maaa^^Mbbb
 (d) aaa^^Mbbb^^M

The processing of example (a) would proceed as follows. Call \stripM, adding $ at the beginning and $^^M$$ at the end.

% \stripM $^^Maaa^^Mbbb^^M$^^M$$\stripM
%

This finds a match with the $^^M at the beginning. The remaining text is passed on to \stripMb. We know that there is now an extra $^^M$$ at the end, but we don’t know if the first $ is preceded by ^^M.

% \stripMb aaa^^Mbbb^^M$^^M$$\stripMb
%

If it turns out that #2 = $, then there was not a ^^M at the end of the original text, and we need to strip off a last remaining $ sign. Otherwise we are finished if we just discard #2 and #3 (the remaining ^^M and $ characters of the ones that we added).

We use $ as a marker since any $ characters that happen to occur in the menu text will have category 12 and thus not match the category-3 $ used in the definition of \stripMa etc. A control sequence could also be used as a marker if we took care to give it a bizarre name that would never arise in the menu text (\fxmenub is used not just by \fmenu but also by \fxmenu, which might have arbitrary control sequences in its arguments); but that means using up one more hash table entry and additional string pool.

“lowercase–% “long“gdef“stripM#1$˜ #2#3“stripM#4–% “ifx$#2% “stripMa#1“stripMa#4% “else “stripMb#2#3“stripMb#4% “fi ˝ ˝ % end lowercase

“lowercase–% “long“gdef“stripMa $#1“stripMa#2–% “stripMb#1$˜ $$“stripMb#2˝ ˝ % end lowercase

“lowercase–% “long“gdef“stripMb#1˜ $#2#3“stripMb#4–% “ifx$#2% “stripMc#1“stripMc#4% “else “stripMd#1“stripMd#4% “fi ˝ ˝ % end lowercase

“long“gdef“stripMc#1$#2“stripMc#3–% “stripMd#1“stripMd#3˝

“long“gdef“stripMd#1“stripMd#2–#2–#1˝ ˝ “endgroup

Some tests.

% %\lowercase{\def\test#1{\stripM $#1$~$$\stripM}
% %\tracingmacros=2 \tracingcommands=2 \tracingonline=1
% %\test{~aaa~bbb~}
% %\test{aaa~bbb}
% %\test{~aaa~bbb}
% %\test{aaa~bbb~}

% %\tracingmacros=0 \tracingcommands=0 \tracingonline=0
% %}% end lowercase
%

The argument of \stripcontrolMs is a token register. The text of the token register will be stripped of a leading and trailing ^^M if either or both are present, and the remainder text will be left in the token register.

“begingroup “lccode‘“˜ =‘“ˆ ˆ M “lowercase–% “gdef“stripcontrolMs#1–“xp@“stripM “xp@$“the#1$˜ $$“stripM#1˝ ˝ % end lowercase

“lowercase–% “gdef“addmenulines#1#2#3–%

Add #2 at the beginning and #3 at the end of every line of token register #1.

“def ˜ ##1˜ ##2–%

#1“xp@–“the#1#2##1#3˝ %

“ifx“end##2“xp@“@gobbletwo“fi˜ ##2˝ % “edef“@tempa–“nx@˜ “the#1“nx@˜ ˝ #1–˝ % “@tempa“end˝ ˝ % end lowercase “endgroup % restore lccode of ˜

The function \fxmenub is the one that does most of the hard work for \fmenu and \fxmenu. Argument #4 is the name of the menu, #5 is the argument specifiers (maybe empty). Arguments #1#2#3 are assignment type, extra setup, and expansion control; specifically, these arguments are \gdef \begingroup \empty for

\fmenu or \xdef \xmesjsetup and an extra \noexpand for \fxmenu.

That this function actually works should probably be regarded as a miracle rather than a result of my programming efforts.

3

“def“fxmenub#1#2#3#4#5–% “stripcontrolMs“menufirstpart “stripcontrolMs“menulastpart “stripcontrolMs“menuchoices “addmenulines“menuchoices“menuline“endmenuline “actively“let“ˆ ˆ M“relax % needed for “xdef

Define #4. Expansion control is rather tricky because of the possibility of parame-

ter markers inside \menufirstpart, \menuchoices or \menulastpart.

“toks@–“long#1#4#5˝ % e.g. “xdef“foo##1##2

3Let’s see, three miracles is a prerequisite for sainthood in the Catholic church—only two more needed for Don Knuth to be a candidate …

If \menufirstpart is empty, we don’t add the separator material \inmenuA.

“edef“@tempa–“the“menufirstpart˝ % “ifx“@tempa“@empty “let“nxa@“@gobble “else “addmenulines“menufirstpart “menutopline“endmenuline “let“nxa@“nx@ “fi

If \menulastpart is empty, we don’t add the separator material \inmenuB.

“edef“@tempa–“the“menulastpart˝ % “ifx“@tempa“@empty “let“nxb@“@gobble “else “addmenulines“menulastpart “menubotline“endmenuline “let“nxb@“nx@ “fi

Set up the definition statement that will create the new menu. #2 = \begingroup

or \xmesjsetup.

“edef“@tempa––#3“nx@#3#2% “def#3“nx@#3“mesjtext–% #3“nx@#3“menuprefix “the“menufirstpart #3“nxa@#3“inmenuA “the“menuchoices #3“nxb@#3“inmenuB “the“menulastpart #3“nx@#3“menusuffix˝ % #3“nx@#3“sendmesj #3“nx@#3“menuprompt˝ ˝ % “toks2 “xp@–“@tempa˝ % “edef“@tempa–“the“toks@“the“toks2 ˝ %

Temporarily \relaxify \menuline etc. in order to prevent their premature expansion if \xdef is applied.

“let“menutopline“relax “let“menuline“relax “let“menubotline“relax “let“endmenuline“relax “let“menunumber“relax “@tempa % finally, execute the “gdef or “xdef “endgroup % matches “mesjsetup done by “fxmenu ˝ % end “fxmenub

Expanding analog of \fmenu.

“def“fxmenu#1#2#–“xmesjsetup “toks@–“fxmenub–“xdef˝ –“xmesjsetup˝ “nx@#1–#2˝ ˝ % “def“@tempa##1##–% “def“@tempa####1####–% “def“@tempa–“the“toks@˝ % “afterassignment“@tempa “menulastpart˝ % “afterassignment“@tempa “menuchoices˝ % “afterassignment“@tempa “menufirstpart ˝

“def“notyet#1–%

“errmessage–Not yet implemented: “string#1˝ ˝

These two functions aren’t implemented yet.

“long“def“nmenu#1#2#3#4#5–“notyet“nmenu˝ “long“def“nxmenu#1#2#3#4#5–“notyet“nxmenu˝

2.4 Menu traversal functions

For reliable travel up and down the menu tree, we need to push and pop the value of \curmenu as we go along. Among other things, \curmenu is used to repeat the current menu after a help message.

“newtoks“optionstack

“let“curmenu“@empty

Start of a stack element.

“let“estart“relax

End of a stack element.

“let“eend“relax

“def“pushoptions#1–% “edef“pushtemp–“estart “def“nx@“curmenu–“curmenu˝ % “eend “the“optionstack˝ % “global“optionstack“xp@–“pushtemp˝ % “edef“curmenu–“curmenu#1˝ % ˝ “def“popoptions–% “edef“@tempa–“the“optionstack˝ % “ifx“@empty“@tempa “errmessage–Can’t pop empty stack (“string“optionstack)˝ %

“else “def“estart##1“eend##2“@nil–% “global“optionstack–##2˝ % “let“estart“relax##1˝ % “the“optionstack“@nil “fi ˝

The X option is a total exit from the menu maze, as compared to \moptionQ, which returns you to the previous menu level.

“fmesj“moptionX–Exiting …˝

“def“repeatoption–% “csname moption“curmenu“endcsname˝

“def“moptionQ–“popoptions “repeatoption˝

The sole reason for using \fxmesj rather than \fmesj here is to use % to comment out the initial newline, as the line break was needed only for convenient printing of this documentation within a narrow column width.

“fxmesj“badoptionmesj#1–% ?---I don’t understand ”#1”.˝

The function \optionexec takes one argument, which it uses together with \curmenu to determine the next action. The argument is expected to be a macro containing a single letter, the most recent menu choice received from the user.

Common options such as ?, Q, or X that may occur at any level of the menu system are handled specially, to cut down on the number of control sequence names needed for a csname implementation of the menus.

“def“optionexec#1–% “if ?#1“relax “let“@tempa“moptionhelp “else “if Q#1“relax “ifx“curmenu“@empty “let“@tempa“moptionX “else “let“@tempa“moptionQ “fi “else “if X#1“relax “let“@tempa“moptionX “else

Because special characters, including backslash, are deactivated by \readChar, we can apply \csname without fearing problems from responses such as \relax.

“xp@“let“xp@“@tempa

“csname moption“curmenu#1“endcsname “ifx“@tempa“relax “badoptionmesj–#1˝ “let“@tempa“repeatoption “else “pushoptions–#1˝ % “fi “fi“fi“fi

We save up the next action in \@tempa and execute it last, to get tail recursion.

“@tempa ˝

Really big menu systems could get around TEX memory limits by storing individual menus or groups of menus in separate files and using \optionfileexec in place of \optionexec to retrieve the menu text from disk storage instead of from main memory. However there are a number of technical complications and I probably won’t get around to working on them in the near future.

“def“optionfileexec#1–“notyet“optionfileexec˝

The function \xoptiontest must return true if and only if the macro #1 consists entirely of one of the one-letter responses ? Q q X x that correspond to special menu actions. The rather cautious implementation with \aftergroup avoids rescanning the contents of #1, just in case it contains anything that’s \outer.

“def“xoptiontest#1–TT“fi “begingroup “def“0–?˝ “def“1–Q˝ % “def“2–q˝ “def“3–x˝ “def“4–X˝ % “aftergroup“if“aftergroup T% “ifx“0#1“aftergroup T% “else“ifx“1#1“aftergroup T% “else“ifx“2#1“aftergroup T% “else“ifx“3#1“aftergroup T% “else“ifx“4#1“aftergroup T% “else “aftergroup F% “fi“fi“fi“fi“fi “endgroup ˝

Default help message, can be redefined if necessary. The extra newlines commented out with % are here only for convenient printing within a narrow column width.

“fxmesj“menuhelpmesj–&“menuprefix% A response of Q will usually send you back to % the previous menu. A response of X will get you entirely out of % the menu system. &“menusuffix% Press the ¡Return¿ key ( Enter ) to continue: ˝

“def“moptionhelp–% “menuhelpmesj “readline–˝ “reply “repeatoption˝

\moptionhelp is the branch that will be taken if the user enters a question mark in response to a menu.

“def“moptionhelp–% “menuhelpmesj “readline–˝ “reply “repeatoption˝

“xp@“def“csname moption?“endcsname–% “moptionhelp˝

The function \specialhelp can be used to provide a one-time alternate help message tailored to a specifc response given by the user. It defines the first argument (the macro containing the response) to contain ?, then redefines \menuhelpmesj to use the message text given in arg #2.

“def“specialhelp#1#2–% “let“specialhelpreply=#1“def#1–?˝ “begingroup “def“menuhelpmesj–“let#1“specialhelpreply “promptxmesj–#2“

Press ¡return¿ to continue:˝ “endgroup˝ % ˝

Init.

“def“specialhelpreply–˝

This is a convenient abbreviation for an often-used combination.

“def“lettermenu#1–% “csname menu#1“endcsname “readChar–Q˝ “reply “optionexec“reply ˝

Restore any catcodes changed locally, and depart.

“restorecatcodes “endinput

Appendix Miscellaneous support functions: grabhedr.sty

A.1 Introduction

This file defines a function \inputfwh to be used instead of \input, to allow TEX to grab information from standardized file headers in the form proposed by Nelson Beebe during his term as president of the TEX Users Group. Usage:

\inputfwh{file.nam}

Functions \localcatcodes and \restorecatcodes for managing catcode changes are also defined herein, as well as a handful of utility functions, many of them

borrowed from latex.tex: \@empty, \@gobble, \@gobbletwo, \@car, \@@input,
\toks@, \afterfi, \fileversiondate, \trap.input.
   The use of \inputfwh, \fileversiondate, and \trap.input as illustrated in

dialog.sty is cumbersome klugery that in fact would better be handled by appropriate functionality built into the format file. But none of the major formats have anything along these lines yet. (It would also help if TEX made the current input file name accessible, like \inputlineno.)

A.2 Implementation

Standard package identification:

%¡*2e¿ “NeedsTeXFormat–LaTeX2e˝ “ProvidesPackage–grabhedr˝ [1994/11/08 v0.9j] %¡/2e¿

By enclosing this entire file in a group, saving and restoring catcodes ‘by hand’ is rendered unnecessary. This is perhaps the best way to locally change catcodes, better than the \localcatcodes function defined below. But it tends to be inconvenient for the TEX programmer: every time you add something you have to remember to make it global; if you’re like me, you end up making every change twice, with an abortive test run of TEX in between, in which you discover that a certain control sequence is undefined because you didn’t assign it globally. (Using \globaldefs = 1 is dangerous in my experience; you have to take care not to accidentally change any variables that you don’t want to be changed globally.)

“begingroup

Inside this group, enforce normal catcodes. All definitions must be global in order to persist beyond the \endgroup.

“catcode96 12 % left quote “catcode‘“= 12 “catcode‘“–=1 “catcode‘“˝ =2 “catcode‘“#=6 “catcode‘“$=3 “catcode‘“˜ =13 “catcode‘“ˆ =7 “catcode‘“˙ =8 “catcode‘“ˆ ˆ M=5 “catcode‘“”=12

Make @ a letter for use in ‘private’ control sequences.

“catcode‘“@=11

A.3 Preliminaries

For \@empty, \@gobble, … we use the L^{A}TEX names so that if grabhedr.sty is used with L^{A}TEX we won’t waste hash table and string pool space.

Empty macro, for \ifx tests or initialization of variables.

“gdef“@empty–˝

Functions for gobbling unwanted tokens.

“long“gdef“@gobble#1–˝ “long“gdef“@gobbletwo#1#2–˝ “long“gdef“@gobblethree#1#2#3–˝

The function \@car, though not really needed by grabhedr.sty, is needed by the principal customers of grabhedr.sty (e.g., dialog.sty).

“long“gdef“@car#1#2“@nil–#1˝

To define \@@input as in L^{A}TEX we want to let it equal to the primitive \input. But if a L^{A}TEX format is being used we don’t want to execute that assignment because by now \input has changed its meaning. And if some other format is being used it behooves us to check, before defining \@@input, whether \input still has its primitive meaning. Otherwise there’s a good chance \inputfwh will fail to work properly.

“ifx“UndEFiNed“@@input % LaTeX not loaded.

This code shows a fairly easy way to check whether the meaning of a primitive control sequence is still the original meaning.

“edef“0–“meaning“input˝ “edef“1–“string“input˝ % “ifx“0“1% “global“let“@@input“input “else “errhelp–%

Grabhedr.sty needs to know the name of the “input primitive in order to define “inputfwh properly. Consult a TeXnician for help.˝

“errmessage–% Non-primitive “noexpand“input detected˝ %

“fi “fi

Scratch token register.

“global“toksdef“toks@=0

Sonja Maus’s function for throwing code over the \fi (“An Expansion Power Lemma”, TUGboat vol 12 no 2 June 1991). (Except that she called this func-

                                                                       tion \beforefi.)

“long“gdef“afterfi#1“fi–“fi#1˝

We will be using \noexpand a lot; this abbreviation improves the readability of the code.

“global“let“nx@“noexpand

Another convenient abbreviation.

“global“let“xp@“expandafter

A.4 Reading standard file headers

The function \inputfwh (‘input file with header’) inputs the given file, checking first to see if it starts with a standardized file header; if so, the filename, version and date are scanned for and stored in a control sequence.

For maximum robustness, we strive to rely on the fewest possible assumptions about what the file that is about to be input might contain.

Assumption 1: Percent character % has category 14. I.e., if the first line of the file to be input starts with %, it is OK to throw away that line.

“begingroup “lccode‘“.=‘“%%

“lowercase–“gdef“@percentchar–.˝ ˝ %

“endgroup

The function \fileversiondate is not only a useful support function for \inputfwh, it can also be used by itself at the beginning of a file to set file name, version, and date correctly even if the file is input by some means other than \inputfwh—assuming that the arguments of the \fileversiondate command are kept properly up to date.

“gdef“fileversiondate#1#2#3–%

“xp@“xdef“csname#1“endcsname–#2 (#3)˝ % “def“filename–#1˝ “def“fileversion–#2˝ % “def“filedate–#3˝ % “message–#1 “csname#1“endcsname˝ % ˝

And now apply \fileversiondate to this file.

%¡*209¿ “fileversiondate–grabhedr.sty˝ –0.9j˝ –1994/11/08˝ %¡/209¿

filehdr.el by default adds a string of equal signs (with an initial comment prefix) at the very top of a file header. This string must be scanned away first before we can start looking for the real information of the file header.

“xdef“@filehdrstart–% “@percentchar“@percentchar“@percentchar“space ==================================% ==================================˝

The purpose of this function is just to scan up to the opening brace that marks the beginning of the file header body. Everything before that is ignored, not needed for our present purposes.

“gdef“@scanfileheader#1@#2#–“@xscanfileheader˝

Throw in some dummy values of version and date at the end so that all we require from a file header is that the filename field must be present.

“long“gdef“@xscanfileheader#1–% “@yscanfileheader#1–˝ version = ”??”, date = ”??”,“@yscanfileheader˝

This function assumes that filename, version, and date of a file are listed in that order (but not necessarily adjacent). It’s possible for the version and date to be missing, or out of order, but the corresponding TEX variables \fileversion and \filedate will not get set properly unless the order is: filename, […,] version, […,] date. Trying to handle different orderings would be desirable but I haven’t yet been struck by a suitable flash of insight on how to do it without grubby, timeconsuming picking apart of the entire file header.

“long“gdef“@yscanfileheader #1 filename = ”#2”,#3 version = ”#4”,% #5 date = ”#6”,#7“@yscanfileheader–% “endgroup “csname fileversiondate“endcsname–#2˝ –#4˝ –#6˝ % ˝

This function has to look at the first line of the file to see if it has the expected form for the first line of a file header.

“begingroup

“lccode‘“$=‘“ˆ ˆ M

“lowercase–“gdef“@readfirstheaderline#1$˝ –%

“toks@–#1˝ %

“edef“@tempa–“@percentchar“the“toks@˝ % “ifx“@tempa“@filehdrstart “endgroup “begingroup “catcode‘“%=9 “catcode‘“ˆ ˆ M=5 “catcode‘“@=11

Double quote and equals sign need to be category 12 in order for the parameter matching of \@xscanfileheader to work, and space needs its normal catcode of 10.

“catcode‘“ =10 “catcode‘“==12 “catcode‘“”=12 “xp@“@scanfileheader “else “message–(* Missing file header? *)˝ % “afterfi“endgroup “fi˝ “endgroup

An auxiliary function.

“gdef“@xinputfwh–%

“ifx“next“@readfirstheaderline

Sanitize a few characters. Otherwise an unmatched brace or other special character might cause a problem in the process of reading the first line as a macro argument.

“catcode‘“%=12 “catcode‘“–=12 “catcode‘“˝ =12 “catcode‘““=12 “catcode‘“ˆ ˆ L=12 “catcode‘“ˆ =12

% Unique terminator token for the first line.

“catcode‘“ˆ ˆ M=3“relax

“else “endgroup“fi

˝

Auxiliary function, carries out the necessary \futurelet.

“gdef“@inputfwh–“futurelet“next“@xinputfwh˝

Strategy for (almost) bulletproof reading of the first line of the input file is like this: Give the percent sign a special catcode, then use \futurelet to freeze the catcode of the first token in the input file. If the first token is not a percent character, then fine, just close the group wherein the percent character had its special catcode, and proceed with normal input; the first token will have its proper catcode because we did not change anything except the percent character. Otherwise, we still proceed with ‘normal’ input execution, but by making % active and defining it suitably, we can carry out further tests to see if the first file line has the expected form (three percent signs plus lots of equal signs).

“gdef“inputfwh#1–% “begingroup“catcode‘“%=“active “endlinechar‘“ˆ ˆ M“relax “lccode‘“˜ =‘“%“relax “lowercase–“let˜ ˝ “@readfirstheaderline “xp@“@inputfwh“@@input #1“relax ˝

A.5 Managing catcode changes

A survey of other methods for saving and restoring catcodes would be more work than I have time for at the moment. The method given here is the best one I know (other methods use up one extra control sequence name per file, or don’t robustly handle multiple levels of file nesting).

The \localcatcodes function changes catcodes according to the character/catcode pairs given in its argument, saving the previous catcode values of those characters on a stack so that they can be retrieved later with \restorecatcodes. Example:

\localcatcodes{\@{11}\"\active}

to change the catcode of \@ to 11 (letter) and the catcode of " to 13 (active). In plain TEX you’d better be careful to use + instead of \+ in the argument of \localcatcodes because of the outerness of \+.

This function works by using token registers 0 and 4 to accumulate catcode assignment statements: in \toks0 we put the statements necessary to restore catcodes to their previous values, while in \toks 4 we put the statements necessary to set catcodes to their new values.

“gdef“localcatcodes#1–% “ifx“@empty“@catcodestack “gdef“@catcodestack––˝ ˝ % “fi “def“do##1##2–% “ifnum##2¿“z@ “catcode“number‘##1 “space “number“catcode‘##1“relax “expandafter“do“fi˝ % “xdef“@catcodestack––“do#1“relax“m@ne˝ % “@catcodestack˝ % “def“do##1##2–“catcode‘##1 ##2“relax“do˝ % “do#1“ –“catcode32“let“do˝ % ˝

Init the stack with an empty element; otherwise popping the next-to-last element would wrongly remove braces from the last element. But as a matter of fact we could just as well initialize \@catcodestack to empty because \localcatcodes is careful to add an empty final element if necessary.

“gdef“@catcodestack––˝ ˝

The function \restorecatcodes has to pop the stack and execute the popped code.

“gdef“restorecatcodes–%

“begingroup

“ifx“@empty“@catcodestack “errmessage–Can’t pop catcodes; “nx@“@catcodestack = empty˝ % “endgroup “else “def“do##1##2“do–% “gdef“@catcodestack–##2˝ %

Notice the placement of #1 after the \endgroup, so that the catcode assignments are local assignments.

“endgroup##1˝ %

“xp@“do“@catcodestack“do

“fi

˝

A.6 Trapping redundant input statements

The utility listout.tex calls menus.sty, which calls dialog.sty, and all three of these files start by loading grabhedr.sty in order to take advantage of its functions

                                                                       \fileversiondate, \localcatcodes, and \inputfwh. But consequently, when

listout.tex is used there will be two redundant attempts to load grabhedr.sty. The straightforward way to avoid the redundant input attempts would be to surround them with an \ifx test:

                                                                        \ifx\undefined\fileversiondate
                                                                          \input grabhedr.sty \relax
                                                                          \fileversiondate{foo.bar}{0.9e}{10-Jun-1993}
                                                                        \fi

This method has a few drawbacks, however: (1) the conditional remains open throughout the processing of everything in grabhedr.sty and the \fileversiondate statement, which makes any \else or \fi mismatch problems harder to debug; (2) if \undefined becomes accidentally defined the \ifx test will fail; (3) choosing the right control sequence to test against \undefined requires a little care.

In a situation where we know that the file to be input has had \fileversiondate applied to it, if it was already input, then we have a failsafe control sequence that we can test to find out whether the file has already been input—the name of the file. Assuming a standard form for the input statement (one that will work with either plain TEX or L^{A}TEX, and makes as few assumptions as possible), we can write a function that will trap input statements and execute them only if the given file has not yet been loaded:

 \csname trap.input\endcsname
 \input grabhedr.sty \relax
 \fileversiondate{foo.bar}{1.2}{1993-Jun-07}

The function \trap.input scans for an input statement in canonical form and executes it if and only if the file has not yet been input (more precisely, if the control sequence consisting of the file name is undefined, which means that it has not had \fileversiondate applied to it). The canonical form that I consider to be the best is \input ⟨full file name⟩␣\relax. Having the \relax means that the input statement will not try to expand beyond the end of the line if \endlinechar is catcoded to 9 (ignore), as is done rather frequently now by progressive TEX programmers. The \relax would ordinarily render the space after the file name unnecessary, but I prefer leaving the space in to avoid interfering with redefinitions of \input to take a space-delimited argument that are occasionally done to achieve other special effects (see, for example, “Organizing a large collection of stylefiles”, by Angelika Binding, Cahiers GUTenberg, numéro 10–11, septembre 1991, p. 175.) L^{A}TEX’s argument form \input{...} cannot, unfortunately, be the canonical form if plain TEX compatibility is required.

“expandafter“gdef“csname trap.input“endcsname

“input#1 “relax–% “expandafter“ifx“csname#1“endcsname“relax “afterfi“inputfwh–#1˝ “relax “fi˝

End the group that encloses this entire file, and then call \endinput.

“endgroup “endinput