Mimicking LaTeX's "table of contents" functionality
The relevant routines are \@starttoc
and \addcontentsline
which in turn use \addtocontents
and \@writefile
.
I try to explain the procedure (toc
stands here for toc
, lof
, lot
and other possible acronyms of personal list of somethings
)
From latex.ltx
(the LaTeX core file)
\def\@starttoc#1{%
\begingroup
\makeatletter
\@input{\jobname.#1}%
\if@filesw
\expandafter\newwrite\csname tf@#1\endcsname
\immediate\openout \csname tf@#1\endcsname \jobname.#1\relax
\fi
\@nobreakfalse
\endgroup}
\def\addcontentsline#1#2#3{%
\addtocontents{#1}{\protect\contentsline{#2}{#3}{\thepage}}}
\long\def\addtocontents#1#2{%
\protected@write\@auxout
{\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
{\string\@writefile{#1}{#2}}}
\def\contentsline#1{\csname l@#1\endcsname}
I'll shift the \@starttoc
explanation to the end.
We see, that \addtocontents
is apart from the formating of the using \contentsline
is effectively a \string\@writefile
, i.e. this string is
written into the .aux
file. (Side note: \label
, \index
and glossary
are 'gobbled', etc. 'disabled' within the .aux
file)
In the 2nd compilation run, this \@writefile
finally sets its contents to the relevant toc
file (i.e. .toc
, .lof
, .lot
. etc.)
Some lines before we find the \@writefile
command, having the #1
as the toc
signature.
\long\def\@writefile#1#2{%
\@ifundefined{tf@#1}\relax
{\@temptokena{#2}%
\immediate\write\csname tf@#1\endcsname{\the\@temptokena}%
}%
}
Now any ToC
- related file is bound to a file handle named \tf@toc
or \tf@lof
etc, i.e. \@writefile
tries to write to the file handle \tf@#1
(you remember #1
has the toc
- extension) -- it checks first if \tf@#1
exists and if so, stores the real content to a token \@temptokena
which is written to \csname tf@#1\endcsname
-- the file handle macro call has to constructed this way due to its variability (#1 as part of the name
).
Now back to \@starttoc
As long no \@starttoc{toc}
command was given, the file handle \tf@#1
does not exist and therefore the \@writefile
commands in the .aux
file does nothing.
But first, the existing \jobname.#1
(i.e. the formatted toc
file) is read and, unless \nofiles
wasn't specified, the handle \tf@#1
is generated and the old \jobname.#1
will be 'erased' and written again, for possible new entries in following runs.
Each \tableof...
or \listof...
command uses \@starttoc{toc}
etc., at least in the versions by the standard classes, e.g. from article.cls
:
\newcommand\tableofcontents{%
\section*{\contentsname
\@mkboth{%
\MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
\@starttoc{toc}%
}
The \section*
command is irrelevant here -- \@starttoc
is the important feature!
Back to \@writefile
The key tool is \@writefile
which has two arguments. You could write basically anything to .aux
capsuled within \@writefile
, which in turn creates the real 'content'
Here's a little sample document (sampletoc.tex
)
\documentclass{book}
\begin{document}
\tableofcontents
\chapter{One}
\section{First}
\section{Second}
\chapter{Two}
\section{First}
\section{Second}
\end{document}
... and its corresponding sampletoc.aux
file
\relax
\@writefile{toc}{\contentsline {chapter}{\numberline {1}One}{3}}
\@writefile{lof}{\addvspace {10\p@ }}
\@writefile{lot}{\addvspace {10\p@ }}
\@writefile{toc}{\contentsline {section}{\numberline {1.1}First}{3}}
\@writefile{toc}{\contentsline {section}{\numberline {1.2}Second}{3}}
\@writefile{toc}{\contentsline {chapter}{\numberline {2}Two}{5}}
\@writefile{lof}{\addvspace {10\p@ }}
\@writefile{lot}{\addvspace {10\p@ }}
\@writefile{toc}{\contentsline {section}{\numberline {2.1}First}{5}}
\@writefile{toc}{\contentsline {section}{\numberline {2.2}Second}{5}}
It's clear, that \contentsline
is written literally to the .toc
wile, while the section and page numbers are used from the very moment when the \addcontentsline
was used (i.e. \thesection
etc. are expanded)
Now, this is the procedure:
- Copy the
\@starttoc
command fromlatex.ltx
and change the file handle name to something different, say\mypersonaltoc
- Copy
\@writefile
and rename it to,say,\@mywritefile
and changetf@#1
tomypersonaltoc
(without\\
) - Call
\addtocontents{}{your content}
Or define your own toc
extensions, say, johnB
and use \addtocontents{johnB}{your content}
and \@starttoc{johnB}
at the appropiate place.
Adding to the nice answer by Christian, I'd like to mention some subtler aspects of writing in the .aux
file.
LaTeX defines a file handle for this, which is \@auxout
, and provides the function \protected@write
for “safe” writing in files. This function differs from ordinary \write
in three main aspects:
robust commands or
\protect
ed ones are written out literally (well, almost, but it's a minor detail);the command has an additional argument, so it must be called as
\protected@write<handle>{<settings>}{<general text>}
(here braces denote explicit ones);
the code is executed in a group where
\thepage
is set to\relax
, in addition to the<settings>
where one can specify other commands to be passed literally to the underlying\write
command (which will expand them during the output routine).
Note that the underlying \write
will do expansion, so commands to be written out literally must be preceded by \protect
(or \string
).
Other aspects of writing to \@auxout
to be considered are that the .aux
file is first read as part of the \begin{document}
routine, but in an implicit group, so if something in the file should have effect on the document it must be a global declaration.
So if one runs the following test file
\documentclass{article}
\begin{document}
\makeatletter
\protected@write\@auxout{}{\protect\newcommand\protect\mytest{This is a test macro}}
\makeatother
\ifdefined\mytest
Defined%
\else
Undefined%
\fi
\end{document}
the .aux
file will contain the line
\newcommand \mytest {This is a test macro}
but “Undefined” will be printed nonetheless at every run. Changing \protect\newcommand
into \gdef
(which doesn't need \protect
because it's unexpandable), the second run of LaTeX on the file will print “Defined” because the .aux
file will contain
\gdef \mytest {This is a test macro}
The .aux
file is read in also as part of the \end{document}
routine and this is where the \@writefile
function performs its duty. During the first reading of the .aux
file at \begin{document}
, \@writefile
is set (locally) to \@gobbletwo
.
In particular, macros found at the upper level of the .aux
file must be defined. For instance, LaTeX writes
\bibstyle{plain}
\bibdata{test}
upon finding
\bibliographystyle{plain}
\bibliography{test}
in the document. However latex.ltx
has
% latex.ltx, lines 6292-6293
\let\bibdata=\@gobble
\let\bibstyle=\@gobble
so basically the two commands in the .aux
file are ignored, as they are just for BibTeX.
Although you mention the TOC functionality, you state that your goal is:
Basically, I would like to learn how to write entries to the .aux file on the first run of LaTeX and include them on the second run.
I'll focus on this question, rather than the mysteries of the actual TOC function. Here's a simple use case that is related to the table of contents: A command that will generate a list of figures if there is one, but will do nothing otherwise. Here is a complete implementation:
\documentclass{article}
\makeatletter
\g@addto@macro\figure{\@ifundefined{so@therearefigures}
{\immediate\write\@mainaux{\string\gdef\string\so@list@figures{1}}%
\newcommand\@therearefigures{1}} {}}
\newcommand\autolistfigures{\@ifundefined{so@list@figures} {} {\listoffigures}}
\makeatother
\begin{document}
\autolistfigures
\section{The text}
\begin{figure}
\caption{There is a figure here}
\end{figure}
\end{document}
Explanation
To be able to check if there will be figures, we hook the figure environment so that it writes the macro definition \gdef\so@list@figures{1}
to the aux file the first time we make a figure. (I use the TeX command \gdef
because it doesn't care whether its argument is already defined). To detect whether it has already done this in an earlier call, {figure}
also defines (and checks) a second macro, \@therearefigures
.
\g@addto@macro\figure{\@ifundefined{so@therearefigures}
{\immediate\write\@mainaux{\string\gdef\string\so@list@figures{1}}%
\newcommand\@therearefigures{1}} {}}
That's about all it takes; when latex is re-run, the aux file will be read in and all definitions executed. Our implementation of \autolistfigures
simply checks if \so@list@figures
is defined.
\newcommand\autolistfigures{\@ifundefined{so@list@figures} {} {\listoffigures}}
Note that the macro we write out (\so@list@figures
) must be distinct from the macro we use to control the writing (\so@therearefigures
); if we used the same macro, it would write itself back out to the aux file even if the LaTeX file no longer contains any figures!