Not using optional argument makes environment break

I'm defining a wrapper environment for filecontents so that I can modify its behaviour.

Everything works fine for most of the test cases. For example, I define an environment testenv which takes one mandatory argument and writes its value to the console:

\newenvironment{testenv}[1]{%
\typeout{Argument = #1}%
\filec@ntents{\jobname.pfc}%
}{}


so when I call

\begin{testenv}{hello}
hi
\end{testenv}


LaTeX prints Argument = hello to the console and writes a file called \jobname.pfc which contains

hi


This works for environments without arguments, and with mandatory arguments. For environments with optional arguments it works as long as the optional argument is present, otherwise it fails.

The code below illustrates all the cases. The last one will throw a Missing \begin{document} error:

\documentclass{book}

\usepackage{filecontents}
\makeatletter

\newenvironment{testenv}{%
\filec@ntents{\jobname.pfc}%
}{}
% The environment works without arguments
\begin{testenv}
hi
\end{testenv}

\renewenvironment{testenv}[1]{%
\typeout{Argument = #1}%
\filec@ntents{\jobname.pfc}%
}{}
% and with mandatory arguments
\begin{testenv}{hello}
hi
\end{testenv}

\renewenvironment{testenv}[1][abcde]{%
\typeout{Argument = #1}%
\filec@ntents{\jobname.pfc}%
}{}
% and it works with optional arguments
% only if the argument is given
\begin{testenv}[hello]
hi
\end{testenv}

% otherwise it fails :/
\begin{testenv}
hi
\end{testenv}

\begin{document}
\end{document}


The actual use of the argument is for some text to be typeset after the contents of the environment, so the catcode will not be of much importance.

Make sure the catcode-régime used by the filecontents-environment is in effect before having your environment process whatsoever argument.

Processing optional arguments is possible under filecontents-catcode-régime as applying that régime does not change the catcode of [.

Depending on what you wish to do with the optional argument within your real-life-application, you might need to have it retokenized under normal catcode-régime.
(For a simple \typeout this is not necessary.)

This can be done via \scantokens from the eTeX-extensions.

If eTeX-extensions are not available, this can be done by unexpanded-writing it to a temporary file and via \input (TeX) / \@@input (LaTeX2e) making (La)TeX read/tokenize that temporary file.

\documentclass{book}
\usepackage{filecontents}
\makeatletter
\newcommand\setcatcodesoffilecontents{%
\let\do\@makeother\dospecials
%
\count0=128\relax
\loop %
\catcode\count0=11\relax
\ifnum\count0<256 %
\repeat
%
\catcode\^^L\active
\catcode\^^I\active
\catcode\^^M\active
}%
\newenvironment{testenv}{%
\begingroup
\setcatcodesoffilecontents
\innertestenv
}{}%
\newcommand\innertestenv[1][abcde]{%
\endgroup
\typeout{Argument = #1}%
\@tempswatrue   %<-As file-preamble write the phrase
%   %% LaTeX2e file \jobname.pfc'
%   %% generated by the testenv' environment
%   %% from source test' on 2018/06/30.
%\@tempswafalse  %<-Don't write a file-preamble.
\filec@ntents{\jobname.pfc}%
}%

% With optional argument:
%
%\begin{testenv}[hello]
%  hi
%\end{testenv}

% Without optional argument:
%
\begin{testenv}
hi
\end{testenv}

\begin{document}
\end{document}

• Ooh, neat. Thanks Ulrich :) This time the optional argument just some bunch of text, so the catcode doesn't matter much. Thanks a lot :) – Phelype Oleinik Jun 30 '18 at 16:31

Collecting material using filecontents or similar uses verbatim approaches. That means that any tokenization can (and here will) cause issues. The problem you see is that when looking for an optional [, TeX tokenizes up to and including the h. In particular, this means the line end has 'vanished'. Collecting up tokens relies on grabbing up to the line end (^^M), so here we 'loose' the h, which triggers \everypar and thus the error.

Something similar comes up in beamer, where extra steps are taken to 'make safe' the first token after the start of a line. However, that is at best somewhat fragile and I would not rely on it.

• Thanks Joseph :) I see, the ^^M is consumed, and filecontents's verbatim processor requires the ^^M to start doing its thing. Is there a way to make this work that justifies the effort or should I just make that argument mandatory? – Phelype Oleinik Jun 29 '18 at 21:56