[Tex/LaTex] Mimicking LaTeX’s “table of contents” functionality

auxiliary-filestable of contents

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?

Best Answer

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.

Related Question