# How to use active characters in environs \BODY

I Kong that it’s impossible to have verbatim content in \BODY but is it possible to use an active character at least?

I define <x> as a shortcut for \fbox{x} and would like to be able to activate this with \activebox. Generally it works fine in normal environments and even if I use a wrapper but not if the activation is done directly in an environ environment.

\documentclass[ngerman]{article}

\usepackage{environ}
\begingroup
\catcode< = \active
\gdef<#1>{
\fbox{#1}
}
\endgroup
\def\activatebox{
\catcode<=\active
}

\NewEnviron{test}{
\begin{itemize}
\BODY
\end{itemize}
}

\newenvironment{testwrapper}[1]{
#1
\test
} {
\endtest
}

\begin{document}
Test <Box>

\begin{itemize}
\activatebox
\item Test <Box>
\end{itemize}

\begin{test}
\activatebox
\item Test <Box>
\end{test}

\begin{testwrapper}{\activatebox}
\item Test <Box>
\end{testwrapper}
\end{document}

In my real example it is of curse a bit more complicated: The \BODY is printed out in the document and also written to a file. I can’t use \lst@BeginWriteFile here because there is a single file saving multiple \BODYs. Here’s the original definition of my environment:

More context MWE

\documentclass{article}

\usepackage{environ,xparse, etoolbox}

\begingroup
\catcode< = \active
\gdef<#1>{
\fbox{#1}
}
\endgroup
\def\activatebox{
\catcode<=\active
}

\ExplSyntaxOn% ============================================================

\bool_new:N \tobi_in_group_of_boxes_bool
\bool_set_false:N \tobi_in_group_of_boxes_bool

\NewDocumentCommand { \mybox } { m } {
\fbox { #1 }
\bool_if:NF \tobi_in_group_of_boxes_bool {
\iow_shipout_x:Nx \tobi_solutions_iow {
\tl_to_str:n { \boxline }
{ \exp_not:n { \exp_not:N #1 } }
{ \exp_not:N \thepage }
}
}
}

\cs_generate_variant:Nn \iow_shipout:Nn { No }

\NewEnviron { boxgroup } {
\bool_set_true:N \tobi_in_group_of_boxes_bool
\iow_shipout_x:Nx \tobi_solutions_iow {
\tl_to_str:n { \begin { boxgroup } } { \exp_not:N \thepage }
}
\iow_shipout:No \tobi_solutions_iow { \BODY }
\iow_shipout_x:Nx \tobi_solutions_iow {
\tl_to_str:n { \end { boxgroup } }
}
\par\textsf{\color{black!35}GROUP~START}\par
\BODY
\par\textsf{\color{black!35}GROUP~END}\par
}

\iow_new:N \tobi_solutions_iow
\iow_open:Nn \tobi_solutions_iow { \c_sys_jobname_str . boxes }
\AfterEndDocument {
\iow_close:N \tobi_solutions_iow
}

\ExplSyntaxOff% ============================================================

\begin{document}
Test <Box A> \mybox{Box B}

\begin{itemize}
\activatebox
\item Test <Box C>
\end{itemize}

\begin{boxgroup}
\activatebox
Test <Box D>
\end{boxgroup}

\activatebox% must be part of the *.boxes file, thus can’t be
% outside of the environment
\begin{boxgroup}
Test <Box E>
\end{boxgroup}
\end{document}
• You need to make the char active before the enironment grabs its body (= reads its argument which fixes catcodes): \activatebox\begin{test} \item Test <Box> \end{test}. Your testwrapper actually shows this nicely… May 20 '17 at 10:27
• @clemens: I know, but I hoped that there is a way or working around this. E.g. hooking into environ somehow to check wether < is active or not. The problem with activating it outside the env (or in a wrapper) is that \activebox isn’t written to the file, which it has to be. (With the wrapper this would be possible, but I’d prefer a more elegant solution …
– Tobi
May 20 '17 at 10:32
• @clemens: Hm … in that case I’d have to test within the environment body if \activateboxes is present and then do the rescan, however that won’t work if \activateboxes should be usable somewhere mid environment. I just tried \tl_rescan:nx { \activateboxed } { \BODY }, but it didn’t work.
– Tobi
May 20 '17 at 10:43
• It works if I define new environment with the activation build in (see edited OP).
– Tobi
May 20 '17 at 10:43

Substitute something to < when it's time to typeset the contents.

\documentclass{article}
\usepackage[T1]{fontenc}

\usepackage{environ,xparse, etoolbox}

\ExplSyntaxOn
\cs_new_protected:Npn \tobi_active_less:w #1 >
{
\fbox{#1}
}
\char_set_active_eq:NN < \tobi_active_less:w
\NewDocumentCommand{\activatebox}{}
{
\char_set_catcode_active:N <
\bool_set_true:N \l_tobi_active_less_bool
}

\cs_new:Npn \tobi_perhaps_active_less:
{
\bool_if:NTF \l_tobi_active_less_bool { \tobi_active_less:w } { < }
}

\bool_new:N \l_tobi_active_less_bool
\bool_new:N \l_tobi_in_group_of_boxes_bool
\bool_set_false:N \l_tobi_in_group_of_boxes_bool

\NewDocumentCommand { \mybox } { m }
{
\fbox { #1 }
\bool_if:NF \l_tobi_in_group_of_boxes_bool
{
\iow_shipout:Nx \tobi_solutions_iow
{
\exp_not:n { \boxline{#1} } { \thepage }
}
}
}

\cs_generate_variant:Nn \iow_shipout:Nn { NV }

\NewEnviron { boxgroup }
{
\bool_set_true:N \l_tobi_in_group_of_boxes_bool
\iow_shipout:Nx \tobi_solutions_iow
{
\exp_not:N \begin{boxgroup}{\thepage}
}
\iow_shipout:NV \tobi_solutions_iow \BODY
\iow_shipout:Nn \tobi_solutions_iow
{
\end{boxgroup}
}
\par\textsf{\color{black!35}GROUP~START}\par
\tl_replace_all:Nnn \BODY { < } { \tobi_perhaps_active_less: }
\BODY
\par\textsf{\color{black!35}GROUP~END}\par
}

\iow_new:N \tobi_solutions_iow
\iow_open:Nn \tobi_solutions_iow { \c_sys_jobname_str . boxes }
\AfterEndDocument
{
\iow_close:N \tobi_solutions_iow
}

\ExplSyntaxOff

\begin{document}

Test <Box A> \mybox{Box B}

\begin{itemize}
\activatebox
\item Test <Box C>
\end{itemize}

\begin{boxgroup}
\activatebox
Test <Box D>
\end{boxgroup}

\mybox{Abc\textbf{def}}

\end{document}

This is what will be written in the .boxes file; note I changed the code so that \thepage gets expanded, which your code doesn't.

\boxline {Box B}{1}
\begin {boxgroup}{1}
\activatebox Test <Box D>
\end {boxgroup}
\boxline {Abc\textbf {def}}{1}
• Thanks! This works fine. // Why do you say, that in my code \thepage is unexpanded? It seems to work in my code as I use \iow_shipout_x:Nx, thus \theblanknumber is immediately expanded but the expansion of \thepage is delayed until the shipout to make sure I get the correct number. At least that’s my plan and I couldn’t find a problem when testing.
– Tobi
May 21 '17 at 8:51
• @Tobi \iow_shipout:Nn is simpler anyway. May 21 '17 at 9:07
• Yeah, but while testing I had the problem that in that case the pagenumber wasn’t always correct, that why I switched to \iow_shipout_x
– Tobi
May 21 '17 at 9:30
• I found a problem with your code, if the environment contains Test <Box D> $x = <5> = \frac{<10>}{<2>}$ it doesn’t work in the \frac parts …
– Tobi
May 21 '17 at 9:31
• @Tobi No, but I'm not clairvoyant. You need l3regex if you want to do the substitution at any nesting level. May 21 '17 at 9:34

I'd probably do something along the following lines (not knowing what the final goal will be):

\documentclass{article}

\usepackage{environ,xparse,etoolbox}

\ExplSyntaxOn

\cs_new_protected:Npn \tobi_frame_box:w #1 > { \fbox {#1} }
\char_set_active_eq:NN < \tobi_frame_box:w

\tl_new:N \l__tobi_tmpa_tl
\bool_new:N \l_tobi_in_group_of_boxes_bool
\bool_set_false:N \l_tobi_in_group_of_boxes_bool

\cs_new_protected:Npn \tobi_handle_boxgroup:n #1
{
\bool_set_true:N \l_tobi_in_group_of_boxes_bool
\iow_shipout_x:Nx \l_tobi_solutions_iow {
\tl_to_str:n { \begin { boxgroup } } { \exp_not:N \thepage }
}
\iow_shipout:Nn \l_tobi_solutions_iow {#1}
\iow_shipout_x:Nx \l_tobi_solutions_iow {
\tl_to_str:n { \end { boxgroup } }
}
\par\textsf{\color{black!35}GROUP~START}\par
#1
\par\textsf{\color{black!35}GROUP~END}\par
}
\cs_generate_variant:Nn \tobi_handle_boxgroup:n {V}

\NewEnviron {boxgroup} {
\tl_set_rescan:NnV \l__tobi_tmpa_tl
{ \char_set_catcode_active:N < }
\BODY
\tobi_handle_boxgroup:V \l__tobi_tmpa_tl
}
\cs_generate_variant:Nn \tl_set_rescan:Nnn {NnV}

\iow_new:N \l_tobi_solutions_iow
\iow_open:Nn \l_tobi_solutions_iow { \c_sys_jobname_str . boxes }
\AfterEndDocument {
\iow_close:N \l_tobi_solutions_iow
}

\ExplSyntaxOff

\begin{document}

\begin{boxgroup}
Test <Box D>
\end{boxgroup}

\begin{boxgroup}
Test <Box E>
\end{boxgroup}

\end{document}

## test.boxes

\begin {boxgroup}{1}
Test <Box D>
\end {boxgroup}
\begin {boxgroup}{1}
Test <Box E>
\end {boxgroup}

## document

• Thanks for your example. But as far as I can see in that case the active character is hard wired in the definition of {boxgroup}, while I’d like a version where I can use \activateboxes inside the environment to decide wether < is active or not.
– Tobi
May 21 '17 at 8:44