2

I'd like to output LaTeX code to a file, with the caveat that all backslashes are doubled (ie, \ is replaced with \\). This output will be consumed by another program.

I've adapted egreg's answer from Removing a backslash from a character sequence. It works when the resulting string is typeset, but I've been unable to write it to a file.

Here's a MWE with my attempts:

\documentclass{article}
\usepackage{xstring, etextools}
\begin{document}

%% Doubles all backslashes in the input argument
%% Adapted from: https://tex.stackexchange.com/a/42337/90126 (egreg)
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\DoubleBackslashes#1{\StrSubstitute{#1}{|}{||}}}

\def\TestContents{Here are my contents: \UndefinedMacro and \\}

%% Prepare the output file
\newwrite\testfile
\immediate\openout\testfile=\jobname.TESTFILE

%% (1) This produces the expected output:
{\tt \DoubleBackslashes{\detokenize\expandafter{\TestContents}}}

%% (2) However, this doesn't:
%\immediate\write\testfile{\DoubleBackslashes{\detokenize\expandafter{\TestContents}}}

%% (3) We are able to write the un-doubled macro (as expected):
\immediate\write\testfile{\unexpanded\expandafter{\TestContents}}

%% (4) Using a intermediate macro doesn't work:
%\edef\TestContentsProtected{\expandnext\DoubleBackslashes{\detokenize\expandafter{\TestContents}}}
%\immediate\write\testfile{\unexpanded\expandafter{\TestContentsProtected}}

\immediate\closeout\testfile
\end{document}
  • What am I missing here?
  • How can this be fixed?
MBlanc
  • 123

2 Answers2

2

You can use following code. Backslash is searched and doubled in the loop. The \doublebb macro is fully expandable, so you can use it in \write arguments (output to a file) like \immediate\write\testfile{\doublebb\yourmacro}

\catcode`\/=0 \catcode`\\=12
/def/doublebb#1{/expandafter/doublebbA/detokenize/expandafter{#1}\/end\}
/def/doublebbA#1\{#1/doublebbB}
/def/doublebbB#1\{/ifx/end#1/empty/else \\#1/expandafter/doublebbB/fi}
/catcode`\=0 \catcode`\/=12

\def\test{Here are my contents: \UndefinedMacro and \others}
\message{\doublebb\test}

\bye
wipet
  • 74,238
2

Since \StrSubstitute is not expandable, you need to do the substitution before writing:

\documentclass{article}
\usepackage{xstring}

\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\DoubleBackslashes#1{\StrSubstitute{\detokenize{#1}}{|}{||}}}

\newcommand{\doublebackslashwrite}[2]{%
  \DoubleBackslashes{#2}[\dbstemp]%
  \immediate\write#1{\dbstemp}%
}

%% Prepare the output file
\newwrite\testfile
\immediate\openout\testfile=\jobname.TESTFILE

\begin{document}

\texttt{\DoubleBackslashes{Here are my contents: \UndefinedMacro and \\}}

\doublebackslashwrite\testfile{Here are my contents: \UndefinedMacro and \\}

\immediate\closeout\testfile

\end{document}

Here's the contents of the written file:

Here are my contents: \\UndefinedMacro and \\\\

A different implementation that allows for several output streams and can distinguish between input given explicitly or via a macro.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\setupstream}{ O{default} m }
 {
  \iow_new:c { g_mblanc_dbswrite_#1_stream }
  \iow_open:cn { g_mblanc_dbswrite_#1_stream } { #2 }
  \AtEndDocument { \iow_close:c { g_mblanc_dbswrite_#1_stream } }
 }

\NewDocumentCommand{\dbswrite}{ s O{default} m }
 {
  \IfBooleanTF { #1 }
   {% argument is a macro
    \mblanc_dbswrite:nV { #2 } #3
   }
   {% argument is explicit
    \mblanc_dbswrite:nn { #2 } { #3 }
   }
 }

\tl_new:N \l__mblanc_dbswrite_text_tl

\cs_new_protected:Nn \mblanc_dbswrite:nn
 {
  \tl_set:Nx \l__mblanc_dbswrite_text_tl { \tl_to_str:n { #2 } }
  \tl_replace_all:Nxx \l__mblanc_dbswrite_text_tl
   { \c_backslash_str } { \c_backslash_str \c_backslash_str }
  \iow_now:cV { g_mblanc_dbswrite_#1_stream } \l__mblanc_dbswrite_text_tl
 }
\cs_generate_variant:Nn \mblanc_dbswrite:nn { nV }

\cs_generate_variant:Nn \tl_replace_all:Nnn { Nxx }
\cs_generate_variant:Nn \iow_now:Nn { cV }

\ExplSyntaxOff

\setupstream{\jobname.TESTFILE}
\setupstream[secondary]{\jobname.TESTFILESEC}

\newcommand{\test}{Here are my contents: \UndefinedMacro and \\}

\begin{document}

\dbswrite{Here are my contents: \UndefinedMacro and \\}
\dbswrite[secondary]{Here are my contents: \UndefinedMacro and \\}

\dbswrite*{\test}
\dbswrite*[secondary]{\test}

\end{document}
egreg
  • 1,121,712