1

Variations of this question have been asked before, but those answers don't seem to work when used within \NewDocumentEnvironment from xparse. For example, see Write environment body verbatim to a file

The minimal goal is to save the body of the environment verbatim to a file, and replace the body with some other file. Reading in the replacement file is easy, but saving the body is hard.

\documentclass[10pt]{article}

\ExplSyntaxOn

% I'm using xparse because I'm doing messy things with the arguments. That part works.
\NewDocumentEnvironment{intfig} { > { \SplitArgument { 1 } { , } } m o }
{
  \group_begin:

  \str_set:Nn \l_intfig_fout_str { test.out }
  \str_set:Nn \l_intfig_fin_str { test.in }

  % Here, I want to save the body to test.out.
  % I tried variations with \VerbatimOut and \endVerbatimOut, along
  % with elements of the listings package (\lst@BeginWriteFile).

  % Reading in the replacement works.
  \file_if_exist:nTF { \l_intfig_fin_str } { \input { \l_intfig_fin_str }}{}

  \group_end:
}{
}

\ExplSyntaxOff

\begin{document}

\begin{intfig}{junk,14.4pt}[2cm,rubbish]
  Send me to an external file, you !#^%?

  Don't mess with anything in here either. I want

  every            space, semi-colon;;; etc.
\end{intfig}

\end{document}

@Phelype says that doing this is currently impossible, and links to some information about the current status.

Is there a way to wrap several lines in an environment so that they are passed to the containing environment as a verbatim body? Something like this

\begin{intfig}{junk,14.4pt}[2cm,rubbish]
 \begin{makeintoverbatim}
  Send me to an external file, you !#^%?

  Don't mess with anything in here either. I want

  every            space, semi-colon;;; etc.
  \end{makeintoverbatim}
\end{intfig}

The idea is that this would pass what is in makeintoverbatim to \NewDocumentEnvironment unmolested. If that were possible, then using b (or b+) might work when defining the intfig environment.

5
  • Not yet possible: github.com/latex3/latex3/issues/591 Commented Feb 11, 2022 at 16:21
  • I saw that at some point in my search for information. Surely, this is some way of doing this. TeX/LaTeX can do anything! Commented Feb 11, 2022 at 16:28
  • @RandallFairman the discussion Phelype pointed you to doesn't say it is impossible, only that we haven't done anything on that. It is not easy to get right, but it is possible and perhaps will come one day ... but not any time soon. We aren't going to provide something we then have to retract later again, and right now other areas are more important. Commented Feb 11, 2022 at 16:51
  • @RandallFairman As Frank says, it is possible, but not yet with ltcmd (xparse). Maybe the scontents package comes close to what you want? Commented Feb 11, 2022 at 17:11
  • Thanks, as always. I'll look into that. The help given here is wonderful. Commented Feb 11, 2022 at 17:16

2 Answers 2

2

The xsimverb package seems to provide what you want:

\documentclass[10pt]{article}

\usepackage{xsimverb,listings}

\ExplSyntaxOn
\cs_generate_variant:Nn \file_if_exist:nT {V}
\cs_generate_variant:Nn \file_input:n {V}

\str_new:N \l_intfig_fout_str
\str_new:N \l_intfig_fin_str

\NewDocumentEnvironment{intfig}{ >{ \SplitArgument {1} {,} } m!o }
  {
    \str_set:Nn \l_intfig_fout_str {test.out}
    \str_set:Nn \l_intfig_fin_str  {test.in}
    \IfValueTF {#2}
      { \xsim_file_write_start:nn { \c_true_bool } }
      { \xsim_file_write_start:nn { \c_false_bool } }
    { \l_intfig_fout_str }
  }
  {
    \xsim_file_write_stop:
    \file_if_exist:VT \l_intfig_fin_str { \file_input:V \l_intfig_fin_str }
  }
\ExplSyntaxOff

\begin{document}

\begin{intfig}{junk,14.4pt}[2cm,rubbish]
  Send me to an external file, you !#^%?

  Don't mess with anything in here either. I want

  every            space, semi-colon;;; etc.
\end{intfig}
\lstset{basicstyle=\ttfamily,showspaces=true}
\lstinputlisting{test.out}

\begin{intfig}{junk,14.4pt}
  another test
\end{intfig}
\lstinputlisting{test.out}

\end{document}

enter image description here

Some remarks:

The first argument to \xsim_file_write_start:nn is a boolean which denotes if an optional argument follows or not (only important if the optional argument is the last argument of the environment) and the second the file name to write the contents to.

Also note that then the optional argument should be specified as !o as otherwise the leading spaces of the first line are gobbled if no optional argument is given. See the section about optional arguments and spaces in the xparse manual and check the difference in the output if you leave the ! away.


The commands \xsim_file_write_start:nn and \xsim_file_write_stop: may not be documented (yet?) but are public and can be used. Their 2e counterparts \XSIMfilewritestart and \XSIMfilewritestop are documented.

2
  • Thank you for explaining the use of !. That could be important. Commented Feb 12, 2022 at 0:07
  • It looks like these commands, \xsim_file_write_start, and so on, are not part of the public interface. They're not mentioned in the pdf document for the xsim package. Of course, their definitions are available in xsimverb.sty. Commented Feb 12, 2022 at 0:11
2

Based on the excellent information given above, and some additional research and tinkering, here's a summary.

One method is relatively straightforward and uses xparse:

\NewDocumentCommand{\takeverbatim} { +v }
{
  \str_set:Nn \g_intfig_verb_str { #1 }
}

% You can use the above to associate (almost) arbitrary text with a global
% variable.
\takeverbatim {
verbatim text that can include anything you like ($#%^_/)
but NOT a closing brace.
}

The downside with this method is that the text that would naturally go within the environment you're interested in must be given outside that environment.

Another method uses scontents:

\begin{scontents}[store-env=testoutput]
  \begin{verbatim}
Verbatim text again, but this time a closing brace would be OK.
  \end{verbatim}
\end{scontents}

scontents stores the verbatim text in a module variable. By looking at the source code of scontents, it appears that you can access the verbatim text elsewhere with

\__scontents_getstored_internal:nn { 1 }{ testoutput }

This is nice because the verbatim text may include closing braces, but it still requires that text you care about be given separately from the environment you actually want to define.

The nicest solution (IMO) uses xsim as suggested by @cgnieder

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .