8

I have a local class that a number of people use. One of the new things I want it to do is write out various user-defined data to a file, so that a follow-up LaTeX document can do something different, but with the same user-defined parameters. I'm somewhat new to reading and writing external files, so my question may be elementary.

Here's an MWE that demonstrates the problem:

\documentclass{article}
\begin{document}
%% USER CONTROLS THIS DEFINITION
\def\userdef{abc}        % Works Great
%\def\userdef{abc\\def}  % Breaks \write
%% I CONTROL THE FOLLOWING
\def\userdefContent{\noexpand\def\noexpand\userdef{\userdef}}%
\newwrite\tempfile%
\immediate\openout\tempfile=userdata.tex
\immediate\write\tempfile{\userdefContent}
\immediate\closeout\tempfile
\end{document}

The user defines some known variable using a \def and I'd like to write it to a file in the form of \def\userdef{blah blah blah} where blah blah blah is how the user defined it. In this way, I can \input it into another file and have that second document pre-initialized with the same data the user defined for the first round.

The problem is that there are some user variables I want to write that may be populated with \\ characters which, apparently, break the \write. Uncomment the one line in the MWE to demonstrate this.

I am flexible on a solution. Ideally, I would like my output file to contain

\def \userdef {abc\\def}

but I would be willing to settle for

\def \userdef {abc*LINEFEED*def}

where *LINEFEED* is an arbitrary string sequence to alert me that I will later need to stick back in a \\. I would even, as a last resort, settle for

\def \userdef {abc def}

where the \\ has been stripped.

I suppose I would, if there were no other solution, instruct the userbase not to use \\ in their \def but some other macro that I define (which I can then redefine prior to the \write). But I think they might resist this last approach. I also would resist it, because it would break backward compatibility to older documents.

I am also open to a totally different approach, if there is a standard LaTeX way of transferring data between different documents. I would add that I have a stringstings solution that works when \userdef is composed purely of catcode 11 and 12 material and \\ characters, but I can't guarantee in advance that the user won't stick some other macros in his \def.

1 Answer 1

11

You have to disable the expandability of \\ when doing the write. But in general \write is problematic when "free" input is allowed: \textbf or similar commands will surely break.

It's better to build an "immediate" version of \protected@write:

\documentclass{article}
\usepackage{xpatch}
\makeatletter
% get a copy of `\protected@write
\let\protected@iwrite\protected@write
% patch the copy to add \immediate
\xpatchcmd{\protected@iwrite}{\write}{\immediate\write}{}{}
\makeatother

\newwrite\tempfile

\begin{document}
\def\userdef{abc\\def}  % Breaks \write

\immediate\openout\tempfile=userdata.tex
\makeatletter
\protected@iwrite\tempfile{\let\\\relax}{\userdef}
\immediate\closeout\tempfile
\makeatother

\end{document}

Note that \protected@iwrite has, like its sibling, a second argument where declarations can be performed to change the meaning of commands for the scope of the write; in this case I set \\ equal to \relax so it becomes unexpandable. Without this the output would be

abc\protect \\def

With the temporary assignment it is

abc\\def

as desired.

A possible simplification is using the same method LaTeX uses for \typeout (thanks to David Carlisle for suggesting it):

\documentclass{article}
\makeatletter
\def\protected@iwrite#1#2#3{%
  \begingroup\set@display@protect
  #2% local assignments
  \immediate\write#1{#3}\endgroup}
\makeatother

\begin{document}
\def\userdef{abc\\\textbf{def}}

\immediate\openout\tempfile=userdata.tex
\makeatletter
\protected@iwrite\tempfile{}{\def\protect\userdef{\userdef}}
\immediate\closeout\tempfile
\makeatother

\end{document}

The output is \def\userdef{abc\\\textbf {def}}. Notice that also \textbf is OK. Fragile commands need \protect, of course.

7
  • I am still not able to overcome one impediment with this approach. When I give the argument to the iwrite as {\noexpand\def\noexpand\userdef{\userdef}} hoping to get \def\userdef{abc\\def} in the file, I instead get \def abc\\def{abc\\def} Apr 26, 2013 at 15:02
  • @StevenB.Segletes {\def\noexpand\noexpand\noexpand\userdef{\userdef}}
    – egreg
    Apr 26, 2013 at 15:08
  • @StevenB.Segletes \noexpand isn't a LaTeX command, use \protect (also \def isn't expandable anyway) so \protected@iwrite\tempfile{\let\\\relax}{\def\protect\userdef{\userdef}} Apr 26, 2013 at 15:11
  • @StevenB.Segletes oops our comments crossed I told you not to use \noexpand and egreg said to use three of them. (we said same thing really though). Apr 26, 2013 at 15:13
  • @DavidCarlisle "Ditto." Indeed, I thought a Clash of the Titans was about to unfold. Apr 26, 2013 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.