Documentation for xcomment.sty ^{∗}^{†}

Timothy Van Zandt ^{‡}

4 September 1991

Abstract

The LaTEX style option verbatim.sty, by Rainer Schöpf, allows one to redefine an environment to be a comment, and thereby selectively omit certain environments when typesetting a document. Suppose instead that one wants to typeset only selected environments? For example, one might want to print only a document’s tables and figures, without having to enclose all the text outside these environments within comments. This style option allows such selection.

1 Usage notes

This style file defines a new environment, xcomment, which permits one to typeset only selected environments, without having to enclose all the text outside these environments within comments. The main interest in such a feature is that it allows document styles to have great control over what parts of a document are typeset, thus extending the modularity of LaTEX. For example, this option was originally written for use in a document style for seminar notes and slides. The main text of the input file consists of the notes for a seminar, and each slide goes in a slide environment. A simple modification of the style options allows one to typeset only the slides, only the notes, or both together, in a variety of styles.

\xcomment The xcomment environment has as a single mandatory argument a list (possibly empty) xcomment of environments, separated by commas and with no spaces. Within the xcomment

environment, only text within each of the specified environments is typeset. The \xcomment command can also be used directly, with the same argument, and it would typically go in the preamble. Invoking the \xcomment command is equivalent to putting \begin{xcomment} at the invokation of the command or immediately after \begin{document}, whichever occurs later in the document, and \end{xcomment} just before the end of the document. For example, if \xcomment{table,figure} is put in the preamble, only tables and figures are typeset (but see \nofloat below). If the list of environments is empty, as in \begin{xcomment}{}, the xcomment environment is essentially like the comment environment in verbatim.sty.

\rescanfile In the xcomment environment, text is processed a line at a time and discarded until \norescanfile \end{xcomment} or \begin{⟨environment⟩} is encountered, where environment is to

be included. The remaining text on the line is not thrown away, as it is in verbatim.sty. Instead, it is rescanned, and the only restrictions are that it have balanced braces and that it not contain commands that again shift into and out of “comment” mode. This is an important feature, because the included environments may have arguments that are best placed on the same line as the \begin{⟨environment⟩} command. However, the rescanning creates a temporary file. By default, the file is \jobname.tmp. The command \rescanfile{⟨file⟩} causes ⟨file⟩ to be created instead. It is possible to suppress the creation of a temporary file by specifying the filename trash, in which case the leftover text is thrown away, or bounce, in which case the text is left as is with category codes 0 (escape \), 1 (begin group {), 2 (end group }) and 6 (parameter #) switched to 12 (other). The command \norescanfile is equivalent to

∗This file has version number v0.9b dated 91/09/04. The documentation was last revised on 91/09/04.

†This file is a modification of verbatim.sty, v.1.4c (90/10/18) Copyright (C) 1989, 1990 by Rainer Schöpf.

‡Author’s address: Department of Economics, Princeton University, Princeton, NJ 08544-1021, USA. Internet: tvz@Princeton.EDU

\rescanfile{bounce}.

\envirsep The command \envirsep is executed between the environments that are typeset. Its

default definition is \par.

\newxcomment The command

\newxcomment[⟨environment list⟩]{⟨name⟩} defines \name and the name environment to work like \xcomment and the xcomment environment. A list of environments to be included can be given as an optional first argument to \newxcomment, in which case the new command and environment do not take an argument. For example, if you put \newxcomment[]{mycomment} in the preamble, the mycomment environment works like the comment environment in verbatim.sty, except for the rescanning.

\nofloat The command \nofloat{⟨environment list⟩} is provided to disable the floating of environments in the list (also separated by commas and without spaces), since if the \ref’s to floats are commented out, the floats will accumulate to the end of the document and TEX may run out of memory. Nested xcomment environments (with the same name) can be nested, but the lower level environments must be inside text that is typeset as specified by the xcomment environment the next level up. xcomment environments with different names can be nested without restrictions. Here is an example of such nesting. Suppose we want to include only figures, but we also want to be able to comment out some of the figures so that they are not included. This is achieved in the following example:

    \newxcomment[]{mycomment}
    \begin{xcomment}{figure,mycomment}
    This is stuff that is not included.
    \begin{figure}
    This figure is included.
    \end{figure}
    More stuff that is not included.
    \begin{mycomment}               % Out and back into comment mode
    \begin{figure}                  % Ignored by mycomment envir.
    This figure is NOT included.
    \end{figure}                    % Also ignored by mycomment envir.
    \end{mycomment}                 % In and back out of comment mode.
    More stuff that is not included.
    \end{xcomment}

Caveats:

• \end and \begin should not be redefined within the xcomment environment.

• If \xcomment is invoked in the preamble, \document should not be subsequently redefined.

• Be careful what argument you give to \rescanfile, since any existing file with that name will be destroyed, and the extension tex is added if no extension is given.

• \input and \endinput commands are ignored in “comment” mode.

2 The implementation

The code is an adaption of code in verbatim.sty, and in fact the latter’s structure has been preserved when possible. On the one hand, the code is simpler than in verbatim.sty because we throw away text from the input file rather than typeset it verbatim. On the other hand, it is more involved because we have to check for more possible endings to the xcomment than in verbatim.sty and we have to preserve the input that follows the \beginning of an included environment on the same line. As in verbatim.sty and the \comment\endcomment commands in AMS-TEX, the basic strategy is to change the category codes so that control sequences and other troublesome special characters are neutralized, and then to scan for the strings that mark the end of the comment. This would be a 3-liner in awk, but this is TEX, and the amount of code required to do the scanning increases rapidly as we allow for a greater variety of ending strings. We begin by ensuring that file is not read in twice, and then we identify the file on the VDU and the transcript file.

 1 \@ifundefined{xcomment@@@}{}{\endinput}
 2 \typeout{Style-Option: `xcomment' \fileversion \space <\filedate> (tvz) }

2.1 User commands

\xcomment \newxcomment The main toplevel macro is \@xcomment, which is defined in the next section. It works like \xcomment as described above, except that it has as a first argument an environment so that it stops when it encounters \end{environment}. \newxcomment then has a simple definition, and \xcomment is defined using the \newxcomment command.

 3 \def\newxcomment{\@ifnextchar [{\@newxcommentwitharg}%
 4 {\@newxcomment}}
 5 \def\@newxcomment#1{%
 6 \expandafter\def\csname #1\endcsname##1{\@xcomment{#1}{##1}}%
 7 \expandafter\let\csname end#1\endcsname\relax}%
 8 \def\@newxcommentwitharg[#1]#2{%
 9 \expandafter\def\csname #2\endcsname{\@xcomment{#2}{#1}}%
10 \expandafter\let\csname end#2\endcsname\relax}
11 \newxcomment{xcomment}

\envirsep \rescanfile \norescanfile \envirsep is inserted between the environments that are typeset, and is set to \par by default. \rescanfile is the name of the temporary file used for rescanning, and

is set to \jobname.tmp by default. \norescanfile sets it to bounce.
12 \def\envirsep{\par}
13 \def\rescanfile#1{\def\@rescanfile{#1}} \rescanfile{\jobname.tmp}
14 \def\norescanfile{\rescanfile{bounce}}

\nofloat \nofloat disables floating, and \vfill is inserted forcefully on each side of each float. The macro is just a hack, and is mainly for printing only floats. It may not work well even for that. \envirsep is still inserted between floats, and if it is set to \vspace{}, the added space is inserted only between floats on the same page, given the user some control over spacing.

15 \def\@nofloat#1{\hrule height\z@\nobreak\vfill\vbox\bgroup\def\@captype{#1}}
16 \def\end@nofloat{\egroup\nobreak\vfill\nobreak\hrule height\z@\medbreak}
17 \def\nofloat#1{\@for\@tempa:=#1\do{\@namedef{#1}{\@nofloat{#1}}%
18 \@namedef{end#1}{\end@nofloat}}}

2.2 Preliminaries

\addto@hook \addto@hook is a macro for adding to a token list. It is used here to add to a token list that is not a hook, but the name \addto@hook is used because it is becoming standard in the Mainz distribution.

19 \def\addto@hook#1#2{#1\expandafter{\the#1#2}}
\test@string  \test@string{#1}{#2} sets \if@test to \iftrue if #1 and #2 are equal after expan-

sion and otherwise sets \if@test to \iffalse. In this macro, as in \test@member, we set the scratch conditional \if@test rather than having arguments that specify what to do \if@test and \else, because such arguments may be long and the resulting code confusing.

20 \def\test@string#1#2{\edef\@@tempa{#1}\edef\@@tempb{#2}%
21 \ifx\@@tempa\@@tempb \@testtrue \else \@testfalse \fi}

\test@member The second argument to \test@member is a comma-separated list. \test@member checks whether the first argument is a member of this list (after expanding both the list and the first argument), setting \if@test appropriately.

22 \def\test@member#1#2{%
23 \edef\@tempg{,#2,#1,}%
24 \edef\@temph{####1,#1,}%
25 \expandafter\def\expandafter\@temph\@temph##2\@nil{%
26 \def\@tempg{##2}%
27 \ifx\@tempg\@empty\@testfalse\else\@testtrue\fi}%
28 \expandafter\@temph\@tempg\@nil}

\@afterspaces \@afterspaces{#1} eats all spaces that follow and then invokes \cs.

29 \def\@afterspaces#1{\@ifnextchar+{#1}{#1}}

\xc@makeother \xc@makeother takes as argument a character and changes its category code to 12 (other) if its category code is originally 0, 1, 2 or 6. We do not have to change as many catcodes as in verbatim.sty, because the input is either thrown away or rescanned. The fewer codes we change the better.

30 \def\xc@makeother#1{%
31 \ifnum\the\catcode`#1=0\catcode`#112%
32 \else \ifnum\the\catcode`#1=1\catcode`#112%
33  \else \ifnum\the\catcode`#1=2\catcode`#112%
34  \else \ifnum\the\catcode`#1=6\catcode`#112%
35 \fi\fi\fi\fi\relax}

\rescan This macro changes the category codes of a token list that is already in TEX’s stomach. The code is a modification of a \retokenize macro by Raymond Chen.

36 \newwrite\tokout
37 \newread\tokin

The argument of \rescan is a token list register, say, \mytoks. \mytoks presumably contains a string from the input file, sent to \TeX’s stomach as a stream of tokens. \rescan gives \mytoks the token list that would have arisen if \TeX had had the current catcodes in effect when it read the input string, with the following exceptions:

• The token list must have balanced braces under the new catcodes.

• Parameter tokens in the original list are written twice, and so mess things up.

• Some characters with catcode 10 (space) under the old catcodes are lost, and all that remain are treated as character 32 (␣) under the new catcodes, whatever this character’s new catcode is.

• Escape characters under the old catcodes are treated as escape characters even if their catcodes have changed. (This can be fixed by setting \escapchar during the write to the escape character under the old catcodes, assuming there was only one and it is known)

• Leading spaces under the new catcodes are not ignored, which is wrong if and only if the string started at the beginning of a line and the space characters were not spaces under the old catcodes.

Only the balanced braces exception is a problem in this application, but the other exceptions are pointed out since such a rescanning macro has other applications.

38 \def\rescan#1{%

First we check if \rescanfile, which contains the name of the temporary file to be used, has a special meaning. If bounce, do nothing. If trash, empty the token register.

39 \test@string{\@rescanfile}{bounce}\if@test
40 \else \test@string{\@rescanfile}{trash}\if@test #1{}%

Put the list of tokens in braces and write them to the temporary file.

41 \else
42  \immediate\openout\tokout=\@rescanfile
43  \immediate\write\tokout{{\the#1}\relax}%
44  \immediate\closeout\tokout

Read the contents of the file \@tempd.

45  \openin\tokin=\@rescanfile
46  \read\tokin to\@tempd
47  \closein\tokin

Suppose #1 is \mytok, and ⟨token list⟩ is the list of tokens with the current catcodes. Then the next line expands to \mytok{⟨token list⟩}\relax.

48  \expandafter#1\@tempd%
49  \fi\fi}

2.3 Toplevel macros

\@xcomment \@xcomment checks whether it was invoked before \begin{document} and outside of an environment. If so, it modifies \begin{document} to invoke \@xcomment with the same arguments. Otherwise, it processes its arguments and goes into “comment” mode by invoking \xc@begin.

50 \def\@xcomment#1#2{%
51 \ifx\@preamblecmds\@notprerr
52  \def\xc@csname{#1}%
53  \edef\xc@envirlist{#2}%
54  \ifx\xc@envirlist\@empty \@bsphack \else
55  \begingroup

\@envirsep is what is actually placed before each environment. Initially, it is empty because nothing should precede the first environment that is typeset.

56  \def\@envirsep{}%

\do@end is appended to the meaning of \end, and it is initially set to \xc@begin. This means that the \end of an included environment switches us back into “comment” mode, as desired. The \end of nested environments should have their usual meaning, however. Therefore, \do@begin is prepended to the definition of \begin, and it sets \do@end to \relax. Included environment are begun with \normal@begin because we only want to reset \do@end for nested environements. The group begun above keeps the \end of the xcomment environment from be being affected by these changes.

57  \@ifundefined{normal@begin}{\let\normal@begin\begin}{}%
58  \@ifundefined{normal@end}{\let\normal@end\end}{}%
59  \def\begin##1{\do@begin{##1}\normal@begin{##1}}%
60  \def\end##1{\normal@end{##1}\do@end}%
61  \def\do@begin##1{\@ifundefined{##1}{}{\def\do@end{}}}%
62  \let\do@end\xc@begin
63  \fi
64  \let\next\xc@begin

65 \else
66  \expandafter\def\expandafter\document\expandafter{%
67  \document\relax\@xcomment{#1}{#2}}%
68  \let\next\relax
69 \fi
70 \next}
71 \def\end@xcomment{\ifx\xc@envirlist\@empty \@esphack \else \endgroup \fi}%

\xc@begin \xc@begin starts up “comment” mode by changing the category codes and invoking \xcomment@. The \begingroup keeps these catcode changes local.

72 \def\xc@begin{\begingroup
73  \let\do\xc@makeother\dospecials\catcode`\^^M\active
74 \xcomment@}

2.4 The main loop

Since the code in the main loop uses literal \, {, } and newline characters, we represent them with !, [, ] and ~ by setting the corresponding lccodes to the correponding character but with catcode 12, and then changing the code to lowercase before executing the macro definitions. See verbatim.sty for more details. \vrb@catcodes makes sure that !, [ and ] have catcode 12 before reassigning their lccodes.

75 \@ifundefined{vrb@catcodes}%
76 {\def\vrb@catcodes{%
77  \catcode`\!12\catcode`\[12\catcode`\]12}}{}

The next \begingroup keeps these catcode changes local. The macros must therefore be defined globally.

78 \begingroup
79 \vrb@catcodes
80 \lccode`\!=`\\ \lccode`\[=`\{ \lccode`\]=`\}
81 \catcode`\~=\active \lccode`\~=`\^^M
82 \lowercase{%
\xcomment@ The three macros \xcomment@, \xcomment@@, and \xcomment@@@ form the main loop

when in “comment” mode. \xcomment@ reads a line of input. \xcomment@@ searches for the first occurence of a backslash. \xcomment@@@ checks what the next character is, and depending an the answer various macros are invoked to see if one of the termination strings has been found. \xcomment@ takes the line it has read, puts \@nnil \ and \@nil as delimiters at the end, and then calls \xcomment@@.

83 \gdef\xcomment@#1~{\xcomment@@#1\@nnil!\@nil}%

\xcomment@@ \xcomment@@ takes everything up to the next occurrence of \ as its argument and throws it away together with the \. Then \xcomment@@@ reads the next token on the line, including the delimiters added by \xcomment@.

84 \gdef\xcomment@@#1!{\xcomment@@@}%
\xcomment@@@  \xcomment@@@ checks the token it has read.

• If this next token is \@nil, then the \ we found is the one we added as a delimiter or was the last character in the line. Therefore, we read the next line with

    \xcomment@.

• If the token is b, then we test for a \begin{} statement.

• If it is e, we test for an \end{} statement.

• If the next token is any other character, we invoke \xcomment@@ and search for the next \. This is appropriate even if the next token is a \, since then we have found the control sequence \\ and the appended \ delimiter remains (hence the first \@nnil delimiter).

\if@test keeps track of whether we are searching for \end or \begin.

85 \gdef\xcomment@@@#1{%
86  \ifx \@nil#1\relax \let\next\xcomment@
87  \else \ifx b#1\count@=0\@testfalse\let\next\xc@testbegin%
88  \else \ifx e#1\count@=0\@testtrue\let\next\xc@testend%
89  \else \let\next\xcomment@@
90  \fi\fi\fi
91  \next}%

\xc@testbegin \xc@testend We check for end or begin one character at a time. \count@ keeps track of how many characters we have checked. This may seem laborious, but it is actually the best way given that if \end is not found anywhere on the line, there might still be a \begin to be checked for, and vice-versa.

• If the next character is \, then we have not found \end this time, and we invoke \xcomment@@@ to see if the \ we found is the beginning of \end or \begin.

• If we are on the first character after \e and it is an n, then we check the next character. If it is not an n, we search for the next \ with \xcomment@@.

• If we are on the second character after \e and it is d, then we have found \end. We skip over any spaces and invoke \xc@checklbrace to see if the next character is a {. Otherwise, we search for the next \ with \xcomment@@.

92 \gdef\xc@testend#1{%
93  \ifx !#1\let\next\xcomment@@@
94  \else \let\next\xcomment@@
95  \ifcase\count@ \ifx n#1\advance\count@ by 1 \let\next\xc@testend \fi
96  \or \ifx d#1\def\next{\@afterspaces\xc@checklbrace}\fi
97  \fi\fi
98  \next}%
\xc@testbegin is similar to \xc@testend.
99  \gdef\xc@testbegin#1{%
100  \ifx !#1\let\next\xcomment@@@
101  \else \let\next\xcomment@@
102  \ifcase\count@ \ifx e#1\advance\count@ by 1 \let\next\xc@testbegin \fi
103  \or \ifx g#1\advance\count@ by 1 \let\next\xc@testbegin\fi
104  \or \ifx i#1\advance\count@ by 1 \let\next\xc@testbegin\fi
105  \or \ifx n#1\def\next{\@afterspaces\xc@checklbrace}\fi
106  \fi\fi
107  \next}%
\xc@checklbrace \xc@gather \xc@checkrbrace If \end or \begin has been found on a line, spaces are skipped and \xc@checklbrace

checks the next character. If it is {, then we skip spaces and proceed to gather the argument with \xc@gather. If it is a \, then we go back to \xcomment@@@ to see if this is the beginning of \end or \begin. Otherwise, we search for the next \ in the

line with \xcomment@@.
108  \gdef\xc@checklbrace#1{%
109  \ifx#1[\toks@{}\def\next{\@afterspaces\xc@gather}%
110  \else \ifx#1!\let\next\xcomment@@@
111  \else \let\next\xcomment@@
112  \fi\fi
113  \next}%

\xc@gather adds characters to \toks@ one at a time, until one of the following occurs: When a space is reached, spaces are skipped and \xc@checkrbrace is invoked to check the next character. If a } is reached, we are ready to check the argument agains permissible name with \xc@checkname. If a \ is reached, then we go back to \xcomment@@@. If a \@nnil is reached, we invoke \xcomment@@ to eat the last two delimiters before reading the next line.

114  \gdef\xc@gather#1{%
115  \ifx#1
116  \def\next{\@afterspaces\xc@checkrbrace}%
117  \else \ifx#1]\let\next\xc@checkname
118  \else \ifx#1!\let\next\xcomment@@@
119  \else \ifx#1\@nnil \let\next\xcomment@@
120  \else \addto@hook\toks@{#1}\let\next\xc@gather
121  \fi\fi\fi\fi
122  \next}%
\xc@checkrbrace acts like \xc@gather, but if it does not find a \ or }, \xcomment@@

is invoked.

123  \gdef\xc@checkrbrace#1{%
124  \ifx#1]\let\next\xc@checkname
125  \else \ifx#1!\let\next\xcomment@@@
126  \else \let\next\xcomment@@
127  \fi\fi
128  \next}%

\xc@checkname \xc@checkname is invoked when we have potentially found a complete string that indicates the end of the “comment” mode. If we have found \end{document} or \end{\xc@csname}, then we exit xcomment permanently. If we have found the beginning of one of the environments to be included, then we begin typesetting text by invoking \xc@end.

129  \gdef\xc@checkname{%
130 \let\next\xcomment@@
131  \if@test
132  \test@string{\the\toks@}{document}
133  \if@test \def\next{\endgroup\end@xcomment\end{document}}\fi
134  \test@string{\the\toks@}{\xc@csname}
135  \if@test \let\next\end@@xcomment \fi
136  \else
137  \@for\@tempa:=\xc@envirlist\do{\test@string{\@tempa}{\the\toks@}%
138  \if@test \let\next\xc@end \fi}%
139  \fi
140  \next}%

\end@@xcomment When exiting the xcomment environment, we only have to be careful to rescan the line. Note that if get this far, all three delimiters appended to the current line are intact. We append a space before rescanning, because a ⟨return⟩ that was stripped by

\xcomment@.
141 \gdef\end@@xcomment#1\@nnil!\@nil{%
142  \xdef\@gtempb{\xc@csname}%
143  \endgroup
144  \toks@{#1 }\rescan{\toks@}%
145  \edef\next{\noexpand\end@xcomment\noexpand\end{\@gtempb}\the\toks@}%
146  \next}%

\xc@end When exiting “comment” mode to typeset an included environment, we have to get rid of the delimiters added to the current line.

147 \gdef\xc@end#1\@nnil!\@nil{%

148  \xdef\@gtempb{\the\toks@}%

This \endgroup ends the scope of the catcodes changes.

149  \endgroup

The first time an environment is typeset, \@envirsep is null. Thereafter, it is set to

\envirsep.
150  \@envirsep \let\@envirsep\envirsep
151  \toks@{#1 }\rescan{\toks@}%
152  \edef\next{\noexpand\normal@begin{\@gtempb}\the\toks@}%
153  \next}%

Now we end the lowercase command and the group that were used to insert literal \, { and } characters in the code.

154 }%  END OF LOWERCASE
155 \endgroup

Index 10

Index

The italic numbers denote the pages where the corresponding entry is described, numbers underlined point to the definition, all others indicate the places where it is used.

Symbols

\@afterspaces . . . . . . . . . . . . . . . . . \underline{29} \@xcomment . . . . . . . . . . . . . . . . . . . \underline{50}

A

\addto@hook . . . . . . . . . . . . . . . . . . . \underline{19}

E

\end@@xcomment . . . . . . . . . . . . . . . \underline{141} environments:

xcomment . . . . . . . . . . . . . . . . . . . 1 \envirsep . . . . . . . . . . . . . . . . . . 2, \underline{12}

N

\newxcomment . . . . . . . . . . . . . . . . . 2, \underline{3} \nofloat . . . . . . . . . . . . . . . . . . . 2, \underline{15} \norescanfile . . . . . . . . . . . . . . . 1, \underline{12}

R

\rescan . . . . . . . . . . . . . . . . . . . . . . \underline{36} \rescanfile . . . . . . . . . . . . . . . . . 1, \underline{12}

T

\test@member . . . . . . . . . . . . . . . . . . \underline{22} \test@string . . . . . . . . . . . . . . . . . . \underline{20}

X

\xc@begin . . . . . . . . . . . . . . . . . . . . \underline{72} \xc@checklbrace . . . . . . . . . . . . . . \underline{108} \xc@checkname . . . . . . . . . . . . . . . . \underline{129} \xc@checkrbrace . . . . . . . . . . . . . . \underline{108} \xc@end . . . . . . . . . . . . . . . . . . . . . \underline{147} \xc@gather . . . . . . . . . . . . . . . . . . \underline{108} \xc@makeother . . . . . . . . . . . . . . . . . \underline{30} \xc@testbegin . . . . . . . . . . . . . . . . . \underline{92} \xc@testend . . . . . . . . . . . . . . . . . . . \underline{92} \xcomment . . . . . . . . . . . . . . . . . . . 1, \underline{3} xcomment (environment) . . . . . . . . . . . 1 \xcomment@ . . . . . . . . . . . . . . . . . . . \underline{83} \xcomment@@ . . . . . . . . . . . . . . . . . . . \underline{84} \xcomment@@@ . . . . . . . . . . . . . . . . . . \underline{85}