17

I need to mimic (= duplicate) LaTeX's "table of contents" functionality on a lower level.

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.

How do I do that, or where can I read about this?

8
21

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 from latex.ltx and change the file handle name to something different, say \mypersonaltoc
  • Copy \@writefile and rename it to,say, \@mywritefile and change tf@#1 to mypersonaltoc (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.

2
  • While perusing your answer, I might have discovered an error. "Call \addtocontents{}{your content} would write the original \@writefile{}{} to the aux and not the modified version called \@mywritefile. Isn't that true? By the way, this is a very useful answer you have provided. – Jonathan Komar Oct 30 '17 at 11:59
  • @JonathanKomar: You're probably right. I will look and eventually change later on – user31729 Oct 30 '17 at 17:47
17

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:

  1. robust commands or \protected ones are written out literally (well, almost, but it's a minor detail);

  2. the command has an additional argument, so it must be called as

    \protected@write<handle>{<settings>}{<general text>}
    

    (here braces denote explicit ones);

  3. 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.

6
  • 2
    Tooooooooooooo much details ;-) – user31729 Oct 15 '15 at 9:41
  • 5
    @ChristianHupfer “Some subtler details”, not “a few”. ;-) – egreg Oct 15 '15 at 11:33
  • @egreg If I may, what is the minor detail in the first point about \protected@write? (I happen to like details.) – Jonathan Komar Oct 30 '17 at 12:05
  • @JonathanKomar Try \write\@auxout{\bfseries} and \protected@write\@auxout{}{\bfseries}. You'll immediately see why the latter is better. – egreg Oct 30 '17 at 12:43
  • @egreg I am not confused by the difference between \write and \protected@write, rather the minor discrepancy you mentioned between the input and output of \protected@write "almost" literally. – Jonathan Komar Oct 30 '17 at 12:45
5

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!

Not the answer you're looking for? Browse other questions tagged or ask your own question.