# Write if statement to the toc

Here is a challenge:

This construction works just fine as the if is contained to a single write statement

\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\tableofcontents

\chapter{test}

\end{document}


The code bwlow, on the other hand, will not work as \testA or \testB presumably gets executed inside the \@writefile{toc}{...} statement and we end up with and ! Extra }, or forgotten \endgroup error.

\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\tableofcontents

\chapter{test}

\end{document}


So is there a good method for smuggling these two incomplete if statements into the toc file?

• I thought I figured the problem out, but I just did part of it: The first error comes up because the \write of \addtocontents is delayed until shipout, and at that time \protect has a different meaning. To remedy that you need \protect\protect\protect\testA. However, the \@writefile code uses some toks registers to handle end of lines, and then the unmatched conditionals are seen and chaos ensues. I'm not sure if this is doable without a heavy surgery in some LaTeX internals... Oct 25, 2019 at 12:02
• @campa then why do I keep getting an error on it? Oct 25, 2019 at 12:05
• Good question :-) Obviously also @PhelypeOleinik gets an error, so the problem (well, the absence thereof...) in on my side. My only clue is that on my work computer I have only TeXLive2017. Oct 25, 2019 at 12:42
• @campa Yes, this issue was introduced with LaTeX 2018/12/01. More specifically, when fixing this bug. Oct 25, 2019 at 12:44
• @campa check the aux, is the entry written there first or directly to the toc. I think is used to write it directly to the toc, nowadays it goes through the aux Oct 25, 2019 at 12:44

\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\DeclareRobustCommand\activateif{%
\let\testA\iffalse
\let\testB\fi}
\DeclareRobustCommand\deactivateif{%
\let\testA\relax
\let\testB\relax}

\begin{document}

\activateif
\protect\testA test\par
\protect\testB
\deactivateif}

\tableofcontents

\chapter{test}

\end{document}


This lets the chapter entry in the toc disappear

\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\DeclareRobustCommand\activateif{%
\let\testA\iffalse
\let\testB\fi}
\DeclareRobustCommand\deactivateif{%
\let\testA\relax
\let\testB\relax}

\begin{document}

\tableofcontents

\chapter{test}

\end{document}

• For the usecase it has to use two separate addcontents lines, the main goal in the end is to be able to selectively show or not show a single contents line, Oct 25, 2019 at 13:00
• It shouldn't matter how many \addcontentsline you use: the main point is that \testA and \testB are harmless, when the toc is written at the end of the document. Oct 25, 2019 at 13:04
• Not understod, as far as I can see you cannot break your definition into three parts. Remember we have no control over the test\par part, that is just illustration. Try making the chapter contentsline not be printed in the toc by wrapping an if around it. Oct 25, 2019 at 13:10
• @daleif I added an edit which suppress the chapter in the toc. Oct 25, 2019 at 13:26
• Interesting, I was trying \addtocontents{toc}{\activateif\protect\testA}} ... \addtocontents{toc}{\protect\testB}\deactivateif} which failed. No, now they work, no idea what went wrong Oct 25, 2019 at 13:28

### Edit:

This fix was added to the LaTeX 2ε and is available in the kernel since the 2020-02-02 release, so since TeXLive 2019.

The issue was introduced with the 2018/12/01 release of LaTeX. Before that, the macro \@writefile (which does the writing from the .aux to the .toc essentially did \write\tocfile{\unexpanded{<stuff>}}, so your unbalanced conditional was written without problems.

After the aforementioned release, a fix for this problem was introduced and the problem boils down to:

\let\testB\fi
\iftrue\else
\toks0{\testB}
\fi


TeX doesn't care about balancing braces when looking for the matching \else or \fi of a conditional. In this case, \testB is seen as a \fi and you're left with just }\fi.

I think the definition of \add@percent@to@temptokena could be changed a bit to allow avoid this sort of problem:

\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\makeatletter
\@temptokena\expandafter{#1}}
\begingroup
\catcode\%=12
\catcode\^^A=9
\@temptokena\expandafter{#1%^^A
}}
\endgroup
\makeatother

\begin{document}

\tableofcontents

\chapter{test}

\end{document}

• Interesting, and probably cheating (at least in the case that this requires a recent latex, though not a bad requirement IMO) Oct 25, 2019 at 13:14
• @daleif Not really, this should probably be changed in the kernel for safety (GitHub issue). And the code above will work with prior releases, as they don't use \add@percent@to@temptokena (try adding \RequirePackage[2018/11/01]{latexrelease} before \documentclass). But as extra safety you can test for the existence of \add@percent@to@temptokena. Oct 25, 2019 at 13:30
• The fix proposed by you doesn't let users write comments to external files. Besides this the \ifx!#2!-comparison is still there which means that #2 can still erroneously match up the \ifx, e.g., when doing things like \@writefile{toc}{Some text\protected@file@percent My nice comment with \fi}. In any case I'd prefer a routine which replaces each \protected@file@percent. (A minor challenge would be handling \protected@file@percent nested in curly braces...) Oct 26, 2019 at 8:47
• @UlrichDiez “write comments” is odd, but I assume you mean a catcode-12 % character. If that's correct, it's possible to do so, given the correct tricks to have the .aux see the % as catcode-12, not 14. It is possible, but I don't see why someone would need that, though. You are correct, of course: using \protected@file@percent in the argument would break things, but again I don't see why anyone would need that written to the .toc. It's just an “anchor” control sequence to be used by \add@percent@to@temptokena. Oct 26, 2019 at 13:15
• Not necessarily in the toc-file. But I've seen more than one person using \newwrite\tf@extension..\addtocontents{extension} for creating more or less arbitrary external text files \jobname.extension via the .aux-file, whereof some form, e.g., separate additional LaTeX documents... (That's something that I would probably do by means of docstrip or the like. Nonetheless I've seen people doing this. In such scenarios you might like to keep things behind \protected@file@percent as comments...) Oct 26, 2019 at 13:55

It seems that in more recent LaTeX2e-releases some \add@percent@to@temptokena-mechanism was added to \@writefile:

> \@writefile=\long macro:
ted@file@percent \add@percent@to@temptokena \immediate \write \csname tf@#1\end
csname {\the \@temptokena }}.
l.19 \show\@writefile

\expandafter {#1}\else \@temptokena \expandafter {#1% }\fi .


I assume this \ifx !#2!... is intended to be an empty-check for detecting whether \@writefile's second argument contains the token \protected@file@percent. (If so, the first such token not nested in curly braces shall be replaced by % and everything behind it shall be dropped.)

1. I don't reccomend trying to write a percent-char trailed by an exclamation-mark as in:
\@writefile{toc}{something\protected@file@percent! This is a very important comment.}
2. In case \add@percent@to@temptokena's first argument contains unbalanced \else/\fi (as is the case with your scenario), these will erroneously match up the \ifx of
\ifx !#2!\@temptokena\expandafter{#1}.

I foresee that this \add@percent@to@temptokena-thingie breaks a lot of home-brewed code written by users who rely on having the possibility of writing unmatched \if.., \else or \fi by means of \@writefile.

I might suggest something like:

\documentclass[a4paper]{book}

\begingroup
\makeatletter
\catcode\&=14 %
\catcode\%=12 &
\@firstofone{&
\endgroup
&&-----------------------------------------------------------------------------
&&.............................................................................
\ifcat A\detokenize{#2}A\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\@temptokena\expandafter{#1}}{\@temptokena\expandafter{#1% }}&
}&
}%

\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\tableofcontents

\chapter{test}

\end{document}


If you don't like the e-TeX-\detokenize-thingie:

\documentclass[a4paper]{book}

\begingroup
\makeatletter
\catcode\&=14 %
\catcode\%=12 &
\@firstofone{&
\endgroup
&&-----------------------------------------------------------------------------
&& Check whether argument is empty:
&&.............................................................................
&& \UD@CheckWhetherNull{<Argument which is to be checked>}
&&                     {<Tokens to be delivered in case that argument
&&                       which is to be checked is empty>}
&&                     {<Tokens to be delivered in case that argument
&&                       which is to be checked is not empty>}
&&
&& The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
\newcommand\UD@CheckWhetherNull[1]{&
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}&
{\@firstoftwo\expandafter{} \@firstoftwo}&
}&
&&-----------------------------------------------------------------------------
&&.............................................................................
\UD@CheckWhetherNull{#2}&
{\@temptokena\expandafter{#1}}&
{\@temptokena\expandafter{#1% }}&
}&
}%

\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\tableofcontents

\chapter{test}

\end{document}


By the way:

One could as well redefine \@writefile to only replace the first \protected@file@percent by % while leaving everything else in place:

\documentclass[a4paper]{book}

\makeatletter
%%-----------------------------------------------------------------------------
%% Change \@writefile to replace the first non-brace-nested
%% \@protected@file@percent in #2 by %
%%.............................................................................
\@ifdefinable\RemoveTo@protected@file@percent{%
\long\def\RemoveTo@protected@file@percent#1\protected@file@percent{}%
}%
\begingroup
\catcode\&=14 %
\catcode\%=12 &
\@firstofone{&
\endgroup
\@ifdefinable\Replace@protected@file@percent{&
\long\def\Replace@protected@file@percent#1\protected@file@percent{&
\@firstoftwo{ }#1%&
}&
}&
}%
\long\def\@writefile#1#2{%
\@ifundefined{tf@#1}\relax{%
\immediate\write\csname tf@#1\endcsname{%
\unexpanded\expandafter{\romannumeral0%
\ifcat A\detokenize\expandafter{%
\RemoveTo@protected@file@percent#2\protected@file@percent
}A\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{ }%
{\Replace@protected@file@percent.}%
#2%
}%
}%
}%
}%
\makeatother

\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\makeatletter
\makeatother

\tableofcontents

\chapter{test}

\end{document}


Without \detokenize and without \unexpanded:

\documentclass[a4paper]{book}

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Change \@writefile to replace the first non-brace-nested
%% \@protected@file@percent in #2 by %
%%.............................................................................
\@ifdefinable\RemoveTo@protected@file@percent{%
\long\def\RemoveTo@protected@file@percent#1\protected@file@percent{}%
}%
\begingroup
\catcode\&=14 %
\catcode\%=12 &
\@firstofone{&
\endgroup
\@ifdefinable\Replace@protected@file@percent{&
\long\def\Replace@protected@file@percent#1\protected@file@percent{&
\@firstoftwo{}#1%&
}&
}&
}%
\long\def\@writefile#1#2{%
\@ifundefined{tf@#1}\relax{%
\expandafter\UD@CheckWhetherNull\expandafter{\RemoveTo@protected@file@percent#2\protected@file@percent}%
{%
\@temptokena{#2}%
}{%
\@temptokena\expandafter\expandafter\expandafter{\Replace@protected@file@percent.#2}%
}%
\immediate\write\csname tf@#1\endcsname{\the\@temptokena}%
}%
}%
\makeatother

\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi

\begin{document}

\makeatletter
\makeatother

\tableofcontents

\chapter{test}

\end{document}

• The !#2! is actually safe enough as \protected@file@percent is an internal kernel command that nobody should have occasion to use anywhere. (it is defined as \protected\def\protected@file@percent{} so it doesn't do anything useful as such) The case of the unbalanced conditional was an oversight, but otherwise the code obviously didn't "break a lot of home-brewed code written by user" given that is that way in the kernel for about a year now :-) Oct 26, 2019 at 8:37
• @FrankMittelbach Most of the people using LaTeX I know don't use most recent releases but use older releases integrated in whatsoever Linux distributions. Therefore they are not yet affected by changes occurring in a kernel which is just a year old. Despite the statement in source2e that kernel macros can change at any time, which might break one's own work based on kernel macros, a number of people in my surrounding use kernel macros like \@writefile/\@newl@bel /\@starttoc/\if@filesw in their own ways in the endeavor of not having to reinvent the whole wheelwork. Oct 26, 2019 at 9:06
• @FrankMittelbach If I got it right, \protected@file@percent is intended to be used within the kernel as the last token of the second argument of \addtocontents in order to have it winding up in the second argument of the corresponding \@writefile-entry in the .aux-file for preventing the coming into being of a space-token at the end of the line in question when processing the external file (.toc/.lot/.lof/whatever) that came into being due to \@starttoc{...} and \@writefile{...}{...}. Oct 26, 2019 at 9:20
• There are clearly many people that don't use a recent version of LaTeX, but there are equally many who use miktex or TL and stay with recent versions and so far this issue was the first and only that showed. As to !#2! the #2 has no payload it is either empty or \protected@file@percent unless somebody would start to use \protected@file@percent in random places. Oct 26, 2019 at 15:04