Goal: Have an active character not expanded when writing to file
MWE
\documentclass{article}
\makeatletter
% == Package code -- untouchable
\def\foo{\@ifnextchar[{\@foo}{\@foo[x]}}
\DeclareRobustCommand\fooaux[1]{\message{#1}}
\def\@foo[#1]{%
\@foo@delegate{#1}}
\def\@foo@delegate#1{
(#1)
\@foo@write
}
\def\@foo@write#1{%
\protected@write\@mainaux{}{\fooaux{#1}}
}
% == User code -- can manipulate at will
\let\foo@orig\foo
\def\foo@prep{\begingroup
% 1st attempt -- builds but active char expanded in AUX
\catcode`\*=12%
% 2nd attempt -- error: ! Missing control sequence inserted. l.32 \foo@prep
% \catcode`\*=13%
% \def\@dettac{\char46}%
% \let*=\@dettac
\global\def\foo{\@ifnextchar[{\foo@ii}{\foo@ii[]}}%
\global\def\foo@ii[##1]##2{%
\foo@orig[##1]{##2}%
% 3rd attempt -- error: ! File ended while scanning definition of \@foo@arg@ii.
% \edef\@foo@arg@ii{\scantokens{##2}}%
% \foo@orig[##1]{\@foo@arg@ii}%
% 4th attempt -- ! Use of \foo@ii doesn't match its definition.
% \edef\@foo@arg@ii{
% \toks0={##2}
% \immediate\openout14=foo.tex
% \immediate\write14{\the\toks0}
% \immediate\closeout14
% \input foo.tex
% }%
% \foo@orig[##1]{\@foo@arg@ii}%
}%
\endgroup
}
\foo@prep
\def\@catted{
!!
}
\catcode`\*=13
\let*=\@catted
\makeatother
\begin{document}
Works: *
Work \foo{normal} \& \foo[ok]{works}
Do not \foo{five*four} \& \foo[is expanded in aux]{x*x}
\end{document}
The code marked Package code
is from a third party and cannot be changed. I can only change User code
.
The package defines \foo[#1]{#2}
which eventually writes to some file. (AUX here for demonstration purposes.)
The example redefines *
to be active and expand to !!
. This works.
The preamble stores the pristine definition of foo
into foo@orig
and attempts to cleverly rebuild foo
, using an outer macro foo@prep
to provide proper catcode redefinition of *
to 12 (other) and redefine foo
within this context.
The MWE contains three attempts.
Attempt 1
Redefine catcode of *
to 12, and redefine foo
macros, passing the arguments unaltered. This does not yield errors, but expands the *
as if it were still active.
Actual lines in AUX file
\fooaux {normal }
\fooaux {works }
\fooaux {five !! four }
\fooaux {x !! x }
Expected lines in AUX file
\fooaux {normal }
\fooaux {works }
\fooaux {five * four }
\fooaux {x * x }
Attempt 2
This attempt leaves *
an active character and tries to redefine it to a primitive using char42
. However, this yields an error
! Missing control sequence inserted.
l.33 \foo@prep
Attempt 3
Then I tried out the eTeX \scantokens
command by \edef
ing the macro argument to \@foo@arg@ii
representing an expanded, properly catcoded variant of its argument.
But this also yields an error
! Extra \endgroup.
l.33 \endgroup
which I cannot make any sense of.
[Edit fixed syntax error and added attempt 4]
Attempt 4
Used token list instead of \scantokens
. This one yields
! Use of \foo@ii doesn't match its definition.
\@ifnextchar ... \reserved@d =#1\def \reserved@a {
#2}\def \reserved@b {#3}\f...
l.63 Work \foo{normal}
\& \foo[ok]{works}
Interestingly, this resembles the error I get in on the real document most.
Can somebody guide me how to "deactivate" active characters passed as macro arguments?
\newcommand\foo[2][x]{(#1)\protected@write\@mainaux{}{\fooaux{#2}}}
? || The non-optional argument of\foo
seems to be used for writing only. So why not applying\string
to*
:\foo{five\string*four} \& \foo[is expanded in aux]{x\string*x}
? || Do you need expansion-protection for*
also when\fooaux
/\message
-command written to aux-file is executed?\string*
is a clever idea but becomes burdensome if you have hundreds of them.