3

How can I get something to typeset the output identical to that of a verbatim environment, e.g. of this code

\documentclass[12pt]{article}
\begin{document}

before
\begin{verbatim}
123456_789$^%
\end{verbatim}
after

\end{document}

but without requiring changing catcode (for example to use it inside command argument/definition)?

(example: for \verb there's \texttt.)

To clarify: I understand that without catcode change, it's necessary to escape some characters (\textbackslash or \_How does one insert a backslash or a tilde (~) into LaTeX?). I'm okay with that (that's what \texttt uses too).

6
  • You can't have exactly the same syntax without catcode-changes. E.g., without catcode-changes _ and ^ and $ are not printable symbols but directives for how to typeset subsequent typesettable material. Jan 5, 2022 at 13:51
  • Respecting line breaks, maybe?
    – egreg
    Jan 5, 2022 at 13:51
  • == question edited to address some comments above
    – user202729
    Jan 5, 2022 at 13:55
  • Are you free to use LuaLaTeX? If you are, the posting How to handle verbatim material in LuaLaTeX? may be of interest to you.
    – Mico
    Jan 5, 2022 at 14:07
  • @Mico Okay, I already know about that feature.
    – user202729
    Jan 5, 2022 at 14:10

3 Answers 3

4

You can reimplement the verbatim-environment without commands for changing catcodes. If you do this you need to ensure proper typesetting of non-printable characters and non-linebreaking-behavior of spaces and prevent the collapsing of several spaces into a single space and probably prevent expansion/carrying out of active characters etc manually:

\documentclass[12pt]{article}

\makeatletter
\newenvironment{LookLikeVerbatimWithoutCatcodeChanges}{%
  \trivlist
  \item\relax
  \if@minipage\else\vskip\parskip\fi 
  \leftskip \@totalleftmargin
  \rightskip\z@skip
  \parindent\z@
  \parfillskip\@flushglue
  \parskip\z@skip
  \@@par
  \language\l@nohyphenation
  \@tempswafalse
  \def\par{%
    \if@tempswa
      \leavevmode
      \null
      \@@par
      \penalty\interlinepenalty
    \else
      \@tempswatrue
      \ifhmode\@@par\penalty\interlinepenalty\fi
    \fi
  }%
  %%%\let\do\@makeother\dospecials\obeylines 
  \verbatim@font
  \@noligs
  \everypar\expandafter{\the\everypar\unpenalty}%
  \frenchspacing
  %%%\@vobeyspaces\@xverbatim
}{%
  \if@newlist \leavevmode \fi \endtrivlist 
}%
\DeclareRobustCommand\MYspace{\leavevmode\nobreak\ }%
\DeclareRobustCommand\MYlinebreak{\par}%
\makeatother

\begin{document}

\medskip\hrule\medskip

before
\begin{verbatim}
123 456_789$^%
\end{verbatim}
after

\medskip\hrule\medskip

before\begin{LookLikeVerbatimWithoutCatcodeChanges}\MYlinebreak123\MYspace 456\string_789\string$\string^\%\MYlinebreak\end{LookLikeVerbatimWithoutCatcodeChanges}after

\medskip\hrule\medskip

\end{document}

enter image description here

Drawbacks/pitfalls:

Be aware that something like \fbox{before\begin{LookLikeVerbatimWithoutCatcodeChanges}...\end{LookLikeVerbatimWithoutCatcodeChanges}after} yields an error-message about missing item.

This has nothing to do with category codes. The reason is that the environments verbatim and LookLikeVerbatimWithoutCatcodeChanges internally are based on \trivlist while \trivlist "wants" vertical/horizontal mode and does not "like" things like restricted horizontal mode.

Within moving arguments, e.g., arguments of sectioning-commands, which end up in the table of contents, in bookmarks and wherever they are referenced via \nameref you may need \texorpdfstring and the like. The behavior of \nameref may not be as expected.

1
0

The problem with the verbatim-environment is:

The verbatim-environment expects its "body" and the phrase \end{verbatim} to be tokenized under some non-standard-catcode-régime, yielding a set of tokens differing from the set of tokens one would obtain by tokenizing under standard-catcode-régime.

If coming from the argument of a macro which is tokenized under standard-catcode-régime, then the phrase \end{verbatim} won't be recognized as the end of the environment verbatim.

As a workaround I can offer a routine

\DefineVerbatimToScantokens{⟨control-word-token⟩}{⟨xparse-argument-specifiers⟩}{%
  ⟨verbatim-material to be passed to \scantokens⟩
}%

⟨verbatim-material to be passed to \scantokens⟩ is read and tokenized under verbatim-catcode-régime.

Then within ⟨verbatim-material to be passed to \scantokens⟩ every # is replaced by a character-token of catcode 6(parameter).

Then ⟨control-word-token⟩ is defined to process arguments according to ⟨xparse-argument-specifiers⟩ and to pass ⟨verbatim-material to be passed to \scantokens⟩ to \scantokens.

Caveats:

  • The routine does not take from you the task of ensuring that things are referenced/called in circumstances where they can be processed/typeset properly.

    E.g., if you define a ⟨control-word-token⟩ denoting only tokens that require math-mode, then you need to ensure math-mode when executing the ⟨control-word-token⟩.

    E.g., if you have a ⟨control-word-token⟩ expanding to a verbatim-environment, which is based on \trivlist, then you need to ensure that the ⟨control-word-token⟩ is carried out only in situations where \trivlist can be typeset, which is not the case, e.g., with restricted horizontal mode.

  • ⟨control-word-token⟩ can be used within macro-arguments but you need to take care of its usage within moving arguments: ⟨control-word-token⟩ must already be defined when the moving argument is used. This might be a problem. That's why I added another answer where macros \verblabel/\verbref are defined.

\documentclass{article}

%=== Code of \DefineVerbatimToScantokens ========================
% With older LaTeX-releases uncomment the following line:
%\usepackage{xparse}   
\NewDocumentCommand\DefineVerbatimToScantokens{mm}{%
  \begingroup
  \catcode`\^^I=12\relax
  \InnerDefineVerbatimToScantokens{#1}{#2}%
}%
\begingroup
\makeatletter
\def\InnerDefineVerbatimToScantokens#1#2{%
  \endgroup
  \NewDocumentCommand\InnerDefineVerbatimToScantokens{mm+v}{%
    \endgroup\ReplaceHashloop{##3}{##1}{##2}%
  }%
  \newcommand\ReplaceHashloop[3]{%
    \ifcat$\detokenize\expandafter{\Hashcheck##1#1}$%
    \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {%
      \NewDocumentCommand{##2}{##3}{%
         \begingroup\newlinechar=\endlinechar
         \scantokens{\endgroup##1#2}%
      }%
    }{%
      \expandafter\ReplaceHashloop\expandafter{\Hashreplace##1}{##2}{##3}%
    }%
  }%
  \@ifdefinable\Hashcheck{\long\def\Hashcheck##1#1{}}%
  \@ifdefinable\Hashreplace{\long\def\Hashreplace##1#1{##1####}}%
}%
\catcode`\%=12\relax
\catcode`\#=12\relax
\InnerDefineVerbatimToScantokens{#}{%}%
%=== End of code of \DefineVerbatimToScantokens =================


% Be aware that indenting does matter within \DefineVerbatimToScantokens's
% <verbatim-material to be passed to \scantokens>-argument:

% We don't want arguments, thus the <xparse-argument-specifiers>-argument
% is left empty. As \DefineVerbatimToScantokens is a command for defining
% macros, the rules for hash-doubling apply:

\DefineVerbatimToScantokens\mymacro{}{%
before
\begin{verbatim}
12##345  6_789$^%

12##345  6_789$^%
\end{verbatim}
after%
}%



\begin{document}

\medskip\hrule\medskip

before
\begin{verbatim}
12#345  6_789$^%

12#345  6_789$^%
\end{verbatim}
after%

\medskip\hrule\medskip

\mymacro

\medskip\hrule\medskip

\end{document}

enter image description here

0

If you are interested I can offer an interface

\verblabel{⟨referencing-label⟩}⟨verbatim-argument⟩

and

\verbref{⟨referencing-label⟩}

where \verblabel reads the ⟨verbatim-argument⟩ under verbatim-catcode-régime and copies it to .aux-file so that it can be referenced by \verbref in any place of the document. At the time of referencing \verbref passes the ⟨verbatim-argument⟩ to \scantokens.

The mechanism is based on the kernel-macro \@newl@bel, but with some wrappers for copying/reading arguments under verbatim-catcode-régime. This way you get warnings about multiply defined labels and about labels being changed. (\@setref cannot be used but needs to be implemented analogously as that is redefined by hyperref.)

(This could easily be implemented by means of xparse if xparse provided means for getting hold of the verbatim-delimiter in use when reading a v/+v-argument.)

Caveats:

  • The mechanism does not take from you the task of ensuring that things are referenced/called in circumstances where they can be processed/typeset properly.

    E.g., if you have a verb-label denoting only tokens that require math-mode, then you need to ensure math-mode when referencing them.

    E.g., if you have a verb-label denoting a verbatim-environment, which is based on \trivlist, then you need to ensure that it is referenced only in situations where \trivlist can be typeset, which is not the case, e.g., with restricted horizontal mode.

  • In the first LaTeX-run/while no .aux-file does exist yet cross-referencing-labels/verb-labels are undefined and you get ? instead. This implies that usage with things that need matching counterparts might cause problems at least in the first LaTeX run. E.g., don't do something like:

    \[\left(x^2+y^2\verbref{foobar}%
    \verblabel{foobar}{=z^2\right)\]}%
    

    In the first LaTeX-run this will produce a bunch of errors as the verb-label foobar is not defined yet.

  • The interface uses the .aux-file. Therefore TeX-distributions where some characters are written to .aux-file in ^^-notation, e.g., due to character-translation/tcx-files, might cause problems if such characters occur in verbatim-environments/if at the time of referencing such characters are processed by \scantokens in circumstances where ^ does not have category code 7(superscript).

\makeatletter
%%======================Code for \UDcollectverbarg=============================
%% \UDcollectverbarg{<mandatory 1>}{<mandatory 2>}|<verbatim arg>|
%% 
%% reads <verbatim arg> under verbatim-catcode-regime and delivers:
%%
%%    <mandatory 1>{<mandatory 2>{<verbatim arg>}{|<verbatim arg>|}}
%%
%% Instead of verbatim-delimiter | the <verbatim arg> can be nested in braces.
%% You cannot use percent or spaces or horizontal tab as verbatim-delimiter.
%%
%% You can use <mandatory 1> for nesting calls to \UDcollectverbarg.
%% <mandatory 2> gets the <verbatim arg> twice: Once without verbatim-delimiters/braces,
%% once surrounded by verbatim-delimiters/braces.
%% Reason: When you feed it to \scantokens you don't need the verbatim-delimiters.
%%         When you use it for writing to temporary files and reading back,
%%         you need them.
%%=============================================================================
%% Check whether argument is empty:
%%=============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
  \expandafter\UD@stopromannumeral\@firstoftwo}%
}%
%%=============================================================================
\newcommand\UDcollectverbarg[2]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \catcode`\%=14     % <- make percent comment.
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}%
  }%
}%
\newcommand\UD@collectverbarg[3]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \do\%%   <- Give the percent-character the verbatim-catcode.
  \long\def\@tempb##1#3{%
    \def\@tempb{##1}%
    \UD@CheckWhetherNull{#3}{%
      \def\@tempc{{##1}}%
    }{%
      \def\@tempc{#3##1#3}%
    }%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \@onelevel@sanitize\@tempc
    \expandafter\expandafter\expandafter\UD@@collectverbarg% <- this "spits out the result.
    \expandafter\expandafter\expandafter{%
    \expandafter\@tempb\expandafter}%
    \expandafter{\@tempc}{#1}{#2}%
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[4]{%
  \endgroup
  #3{#4{#1}{#2}}%
}%
%%================= End of code for \UDcollectverbarg =========================
%%
\DeclareRobustCommand\verblabel[1]{%
   \@bsphack\UDcollectverbarg{\@firstofone}{\UD@writeverblabel{#1}}%
}%
\newcommand\UD@writeverblabel[3]{%
  \begingroup
  \newlinechar=\endlinechar
  \immediate\write\@auxout{\string\newverblabel{#1}#3\@percentchar}%
  \endgroup
  \@esphack
}%
\newcommand\newverblabel[1]{%
  \UDcollectverbarg{\@firstofone}{\UD@defineverblabel{#1}}%
}%
\begingroup
\catcode`\Z=14
\catcode`\%=12
\@firstofone{Z
  \endgroup
  \newcommand\UD@defineverblabel[3]{Z
    \@newl@bel{vrblbl}{#1}{Z
      \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#2%}Z
    }Z
  }Z
}%
\DeclareRobustCommand\verbref[1]{%
  % \expandafter\@setref\csname vrblbl@#1\endcsname\@empty{#1}%
  \expandafter\ifx\csname vrblbl@#1\endcsname\relax 
    \protect\G@refundefinedtrue
    \nfss@text{\reset@font\bfseries??}%
    \@latex@warning {Reference `#1' on page \thepage\space undefined}%
  \else 
    \csname vrblbl@#1\expandafter\endcsname
  \fi
}%
\makeatother

\documentclass{article}

%\usepackage{hyperref}

\begin{document}

% Referencing:
% ============

\verbref{verblabel 2}

\medskip\hrule\medskip

\verbref{verblabel 1}

% Defining:
% =========

\verblabel{verblabel 1}|something with

linebreaks|

%   Indenting does matter with verbatim-arguments:

\verblabel{verblabel 2}{%
before
\begin{verbatim*}
12#345  6_789$^%

12#345  6_789$^%
\end{verbatim*}
after
}%<--- end of \verblabel's argument

% This will throw a bunch of errors in the first LaTeX-run/while the
% verb-label "foobar" is undefined:
%\[\left(x^2+y^2\verbref{foobar}%
%\verblabel{foobar}{=z^2\right)\]}%

\end{document}

enter image description here

You must log in to answer this question.

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