# Temporarily changing catcode of %

Scenario

I have a bunch of TeX files which may be processed by an external filter. The filter is (unfortunately) immutable: it does a string replacement where, for example, %d is expanded to a certain date. All the "primitives" are of the form %X where X is a letter. The date, for example, will need to be inserted in various places in the document.

However, I also want the document to still be compilable (by LaTeX) if the user has forgotten to run the filter; in this case the date references should be left as %d.

One obvious solution Define a string which contains the objects that will be changed by the filter. At time of definition switch the catcode for the % character.

\documentclass{article}
\catcode\%=12
\gdef\datestring{ %d }
\catcode\%=14

\begin{document}
\datestring
\end{document}


This seems to do what I want it to do. Is there something that I am overlooking? Is there a better solution?

For clarification, after running the filter the TeX test file above gets transformed to (for example)

\documentclass{article}
\catcode\%=12
\gdef\datestring{ 2015-06-10 }
\catcode\%=14

\begin{document}
\datestring
\end{document}


which is why I cannot just use some other form of the %; the string has to appear exactly as %d in the TeX file.

• Why not run the filter using write18? Or alternatively run a script using sed to replace % with \% in the unfiltered file. Given your response to Manuel I guess this would meaning compiling a parent file -- maybe you do that anyway. Otherwise what's stopping you compiling from a script (including a custom macro attached to a button in your editor if necessary). Jun 12 '15 at 9:05
• How complicated is the filter? If you make % an active character then %d could easily be defined to be a command that does the same thing, or invokes the filter automatically.
– user30471
Jun 12 '15 at 9:09
• @ChrisH for external reasons neither of those are viable. (The full reasons are too long to explain, but let's just say that write18 is a security risk, and if I cannot count on the filter being run [which will moot the entire problem], I cannot count on the sed script being run either.) Jun 12 '15 at 9:13
• @Andrew: the filter depends on external information; there's no way I can code that in TeX short of calling write18, which at the moment is not possible. Jun 12 '15 at 9:17
• Guys: I really appreciate you brainstorming alternative solutions (and do keep them coming, since I know I have not thought of everything). But the point of this question is really simply: is there something wrong with the code I posted above, and is there someway to write it in a better way? Jun 12 '15 at 9:18

There's no problem in what you're doing, although I'd prefer leaving the restoration of the category code:

\documentclass{article}

\begingroup
\catcode\%=12
\gdef\datestring{ %d }
\endgroup

\begin{document}
\datestring
\end{document}


If the filter has changed %d into something else, there will be no problem, because the definition of \datestring is global anyway.

• Thanks, that's exactly the sort of comment that I am looking for, and thinking about it I can see the advantage to what you wrote. +1 Jun 12 '15 at 10:23
• why wouldn't \xdef be better than \gdef? Jun 12 '15 at 15:07
• @barbarabeeton There's nothing to expand. Jun 12 '15 at 15:08

There is "another form" [of percent command] already defined for this use.

\@percentchar


which is defined by exactly the construct you show.

So in package or class code you can just use \@percentchar directly. However to use in the document preamble ypu need to make @ letter which gives you exactly the same problem, but with @ rather than %. In that case though you can use \makeatother rather than an explicit catcode setting

an alternative that avoids setting catcodes or global definitions and might work with your filter is

\def\datestring{
%d
}


This will give the same definition once %d is replaced, although the default definition is now white space rather than %d

• \@percentchar is known to TeX. It is not known to my external filter, which, as I tried to emphasize, I cannot change. Someone else already suggested the similar "solution" in a previous comment (which unfortunately now is deleted). // Please see the edit below the hline in the OP. Jun 12 '15 at 9:22
• @WillieWong the emphasis was added later to the question:-). I guess the question then is all about your filter an not about tex so probably off topic. If you have an existing solution, but can not change anything, what is your question? Jun 12 '15 at 9:28
• I tagged it "code review" and "best practices" because I only tested it on a limited number of test cases and wanted to check (a) is there an obvious use-case that will break my code and (b) whether the same effect can be achieved with different, more "idiomatically correct" code. Jun 12 '15 at 9:32
• @WillieWong see update Jun 12 '15 at 9:51
• @ChrisH I think the OP wants to define it always (so its use later doesn't generate an error) I did start to write a version with \ifx z%d<newline>zdate format marker here\fi` test but seemed unnecessarily complicated if white space is acceptable, so didn't post it. Jun 12 '15 at 11:13