% \iffalse
%%
%% Copyright (C) 2006, 2007 QuinScape GmbH
%% http://www.quinscape.de
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
%    http://www.latex-project.org/lppl.txt
% \fi
% \CheckSum{354}
% \section{Using \texttt{makematch}}
%
% The basic idea of \texttt{makematch} is to compile patterns and
% targets (and/or lists of them) and match the former to the latter.
% This functionality is used extensively in QuinScape's
% \href{file:qstest}{\texttt{qstest}} package for unit testing.  We'll
% use that package for documenting usage of \texttt{makematch}; the
% following construct is skips the tests when \texttt{makematch.dtx} is
% used as a standalone file.
%    \begin{macrocode}
%<*dtx>
\iffalse
%</dtx>
%<*test>
\RequirePackage{makematch,qstest}
\IncludeTests{*}
%    \end{macrocode}
%
% \texttt{makematch} requires an e\TeX-based \LaTeX\ which should be
% standard for current \TeX\ distributions.
%
% \hypertarget{patterns}{%
% \subsection{Match patterns and targets}
% } This
% package has the notion of match patterns and targets.  Patterns and
% targets get sanitized at the time they are specified; this means
% that nothing gets expanded, but replaced by a textual representation
% consisting of spaces (with catcode~10) and other characters
% (catcode~12).  Control words are usually followed by a single space
% when getting sanitized.
%
% Patterns and targets are actually generalized to pattern and target
% lists by this package: you can, when specifying either, instead give
% a list by using an optional argument for specifying a list separator
% (the lists used in \texttt{qstest} are comma-separated).
%
% Target lists are unordered: the order of targets in them is
% irrelevant.  Leading spaces in front of each target get stripped,
% all others are retained.
%
% Pattern lists similarly consist of a list of patterns, with leading
% spaces stripped from each pattern.  In contrast to target lists, the
% order of pattern lists is significant, with later patterns
% overriding earlier ones.  Also in contrast to target lists, empty
% patterns are removed.
%
% There are two special characters inside of a pattern: the first is
% the wildcard |*| which matches any number of consecutive characters
% (including the empty string) in a target.  Wildcards can occur
% anywhere and more than once in a pattern.
%
% The second special character in a pattern is only recognized at the
% beginning of a pattern, and only if that pattern is part of a
% pattern list (namely, when a list separator is
% specified).\footnote{And if |!| is not the list separator of the
%   list.}  If a pattern is preceded by |!| then the following
% pattern, if it matches, causes any previous match from the pattern
% list to be disregarded.
%
% So for example, the pattern list |{*,!foo}| matches any target list
% that does not contain the match target |foo|.
%
% An empty target list |{}| is considered to contain the empty string.
% Thus the pattern |*| matches every target list, including empty
% ones, while the pattern list |{}| does not match any keyword list,
% including empty ones.
%
% \subsection{The Interface}
%
% \DescribeMacro{\MakeMatcher} takes two mandatory arguments.  The
% first is a macro name.  This macro will become the new matcher.  The
% second argument of \cmd{\MakeMatcher} is the pattern to match.  An
% optional argument before the mandatory ones can be used for
% specifying a list separator in which case the first mandatory
% argument becomes a pattern list (only in this case are leading |!|
% characters before list elements interpreted specially).
%    \begin{macrocode}
\begin{qstest}{\MakeMatcher}{\MakeMatcher}
  \MakeMatcher\stylefiles{*.sty}
  \MakeMatcher\headbang{!*}
  \MakeMatcher[,]\truestylefiles{*.sty,!.thumbnails/*,!*/.thumbnails/*}
%    \end{macrocode}
% The matcher constructed in that manner is called with three
% arguments.  The first argument is a control sequence name containing
% a match target (or target list) prepared using
% \cmd{\MakeMatchTarget} (see below).
%
% Alternatively, the first argument can be a brace-enclosed list (note
% that you'll need \emph{two} nested levels of braces, one for
% enclosing the argument, one for specifying that this is a list)
% which will then get passed to \cmd{\MakeMatchTarget} (see below) for
% processing before use.  The inner level of braces inside of the
% first argument may be preceded by a bracketed optional argument
% specifying the list separator for this list.
%
% The second argument of the matcher is executed if the pattern list
% for which the matcher has been built matches the keyword list.  The
% third is executed if it doesn't.  List separators of pattern and
% keyword list are completely independent from each other.  Ok, we
% expect the following to result just in calls of \cmd{\true} (a call
% of \cmd{\false} is turned into a failed expectation):
%    \begin{macrocode}
  \begin{qstest}{\Makematcher literal}{\MakeMatcher}
    \begin{ExpectCallSequence}{\true{}\false{}`%
        .#1{\Expect*{\CalledName#1}{true}}+'}
      \stylefiles{{xxx/.thumbnails/blubb.sty}}{\true}{\false}
      \truestylefiles{{xxx/.thumbnails/blubb.sty}}{\false}{\true}
      \headbang{{xxx/.thumbnails/blubb.sty}}{\false}{\true}
      \stylefiles{[ ]{x.sty.gz .thumbnails/x.sty !x}}{\true}{\false}
      \truestylefiles{[ ]{x.sty.gz .thumbnails/x.sty !x}}{\false}{\true}
      \headbang{[ ]{x.sty.gz .thumbnails/x.sty !x}}{\true}{\false}
    \end{ExpectCallSequence}
  \end{qstest}
%    \end{macrocode}
%
% So how do we create a sanitized keyword list in a control sequence?
%
% \DescribeMacro{\MakeMatchTarget} is called with two mandatory
% arguments, the first being a control sequence name where the keyword
% list in the second argument will get stored in a sanitized form: it
% is converted without expansion to characters of either ``special''
% or ``space'' category (catcodes 12 and~10, respectively), and any
% leading spaces at the beginning of an element is removed.  Without
% an optional bracketed argument, not more than sanitization and
% leading space stripping is done.  If an optional bracketed argument
% before the mandatory arguments is specified, it defines the list
% separator: this has to be a single sanitized character token (either
% a space or a character of category ``other'') that is used as the
% list separator for the input (the finished list will actually always
% use the macro |\,| as a list separator).
%    \begin{macrocode}
  \begin{qstest}{\Makematcher with \MakeMatchTarget}%
                {\MakeMatcher,\MakeMatchTarget}
    \MakeMatchTarget\single{xxx/.thumbnails/blubb.sty}
    \MakeMatchTarget[ ]\multiple{x.sty.gz .thumbnails/x.sty !x}
    \begin{ExpectCallSequence}{\true{}\false{}`%
        .#1{\Expect*{\CalledName#1}{true}}+'}
      \stylefiles{\single}{\true}{\false}
      \truestylefiles\single{\false}{\true}
      \headbang\single{\false}{\true}
      \stylefiles{\multiple}{\true}{\false}
      \truestylefiles\multiple{\false}{\true}
      \headbang\multiple{\true}{\false}
    \end{ExpectCallSequence}    
  \end{qstest}
%    \end{macrocode}
% \DescribeMacro{\MatchedTarget} This will after a match process
% contain the target matched by the last matching pattern (if several
% targets in a match target list match, only the first of those is
% considered and recorded), regardless of whether the corresponding
% pattern was negated with |!|.
%
% \DescribeMacro{\RemoveMatched} After a successful match, you can
% call \cmd{\RemoveMatched} with one argument: the control sequence
% name where the list was kept, and the match will get removed from
% the list.  If every list element is removed, the list will be
% identical to \cmd{\@empty}.
%    \begin{macrocode}
  \begin{qstest}{\MatchedTarget}
                {\MakeMatcher,\MakeMatchTarget,\MatchedTarget}
    \MakeMatchTarget\single{xxx/.thumbnails/blubb.sty}
    \MakeMatchTarget[ ]\multiple{x.sty.gz .thumbnails/x.sty !x}
    \begin{ExpectCallSequence}{\true{}\false{}`%
        .#1{\Expect*{\CalledName#1}{true}}+'}
      \stylefiles{\single}{\true}{\false}
      \Expect*{\single}{xxx/.thumbnails/blubb.sty}
      \Expect*{\meaning\MatchedTarget}*{\meaning\single}
      \RemoveMatched\single
      \Expect*{\meaning\single}{macro:->}
      \truestylefiles\single{\false}{\true}
      \headbang\single{\false}{\true}
      \stylefiles{\multiple}{\true}{\false}
      \Expect*{\MatchedTarget}{.thumbnails/x.sty}
      \RemoveMatched\multiple
      \Expect\expandafter{\multiple}{x.sty.gz\,!x}
      \truestylefiles\multiple{\false}{\true}
      \Expect*{\meaning\MatchedTarget}{undefined}
      \headbang\multiple{\true}{\false}
      \Expect*{\MatchedTarget}{!x}
      \RemoveMatched\multiple
      \Expect*{\multiple}{x.sty.gz}
    \end{ExpectCallSequence}    
  \end{qstest}
\end{qstest}
%    \end{macrocode}
%
% \subsection{Notes on sanitization}
% Note that sanitization to printable characters has several
% consequences: it means that the control sequence \cmd{\,} will turn
% into the string |\| followed by the end of the keyword.  Note also
% that single-character control sequences with a nonletter name are
% not followed by a space in sanitization.  This means that
% sanitization depends on the current catcodes.  Most particularly,
% sanitizing the input |\@abc12| will turn into |\@abc 12| when |@| is
% of catcode letter, but to |\@abc12| when |@| is a nonletter.
%
% So sanitization cannot hide all effects of catcode differences.  It
% is still essential since otherwise braces would cause rather severe
% complications during matching.
%
% Another curiosity of sanitization is that explicit macro parameter
% characters (usually |#|) get duplicated while being sanitized.
%
% So this is the end of the documentation section, so we end our test
% file setup by complementing the beginning:
%    \begin{macrocode}
%</test>
%<*dtx>
\fi
%</dtx>
%    \end{macrocode}
% 
% \StopEventually{}
% \section{The driver file}
% An explicit driver file generated by docstrip will typeset the
% implementation.  Running the |.dtx| file through, in contrast, will
% only typeset the user level documentation.
%    \begin{macrocode}
%<*driver>
\documentclass{ltxdoc}
\usepackage{makematch}
\usepackage{ifpdf}
\usepackage{hyperref}
\OnlyDescription
%<driver> \AlsoImplementation
\begin{document}
\GetFileInfo{makematch.sty}
\date{\filedate}
\title{\texttt{\filename}\\\fileinfo\\version \fileversion}
\author{David Kastrup\thanks{\href{mailto:dak@gnu.org}%
    {David.Kastrup@QuinScape.de}, \href{http://quinscape.de}{QuinScape
    GmbH}}}
\maketitle
\DocInput{makematch.dtx}
\end{document}
%</driver>
%    \end{macrocode}
% \section{The installer file}
%
% There is no separate installer file for this package as it is part
% of the \texttt{qstest} bundle and uses its
% \href{file:qstest#installer}{installer}.
%
% \section{The Implementation}
% \subsection{Front matter}
% Some data and version magic from SVN keyword expansion.
%    \begin{macrocode}
%<*package>
\NeedsTeXFormat{LaTeX2e}
\def\next$#1: #2.dtx #3 #4-#5-#6 #7${%
  \ProvidesPackage{#2}[#4/#5/#6 1.#3 Pattern matching with
  wildcards]
}
\next
$Id: makematch.dtx 7925 2007-02-23 14:22:19Z dkastrup $
%    \end{macrocode}
% \subsection{Pattern matching}
% \begin{macro}{\MakeMatcher}
%   This is the principal magic for doing pattern matching.  The
%   control sequence name given in |#1| becomes a pattern matching
%   engine for the pattern list specified in |#2|.  If this control
%   sequence is executed at a later point of time, it matches the
%   compiled pattern list to the keyword list contained in the
%   sanitized macro specified in its first argument (if the first
%   argument itself starts with an additional opening brace, it is
%   considered to be a keyword list that yet needs to be sanitized).
%
%   Macros in |#2| are not expanded, and everything but spaces is
%   converted to catcode~12 characters.  At the start of a pattern or
%   a keyword, spaces get ignored.  Spaces inside of patterns or
%   keywords should work as expected, however.
%    \begin{macrocode}
\newcommand\MakeMatcher[3][\relax]{%
%<etex> \edef#2{\detokenize{#3}}%
%<*!etex>
  \toks@{#3}%
  \edef#2{\the\toks@}%
  \@onelevel@sanitize#2%
%</!etex>
  \def\qst@mmtmp##1##2#1{\qst@parseonecase{##1}{##2}}%
  \let\qst@comma= #1\relax
  \edef#2##1{\noexpand\qst@setup##1%
    \expandafter\qst@parsestart\expandafter\@firstoftwo
%    \end{macrocode}
% The following |{#1\m@ne}| sequence causes \cmd{\qst@parsestart} to
% end parsing.
%    \begin{macrocode}
    #2#1{#1\m@ne}\noexpand\qst@finish}}
%    \end{macrocode}
% \end{macro}
% We define two shorthands for matchers that match always and never,
% respectively:
% \begin{macro}{\qst@matchalways}
%    \begin{macrocode}
\long\def\qst@matchalways#1#2#3{#2}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@matchnever}
%    \begin{macrocode}
\long\def\qst@matchnever#1#2#3{#3}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\qst@setup}
%   Gets one argument, namely a macro containing the match list, or
%   otherwise a list enclosed with a level of braces, optionally
%   preceded by the list separator to be used in brackets.  This is
%   stored in \cmd{\qst@matchlist}.  Furthermore, \cmd{\qst@finish} is
%   set to the default value indicating a non-match.
%    \begin{macrocode}
\def\qst@setup{\let\MatchedTarget\@undefined
  \let\qst@finish\@secondoftwo
  \futurelet\qst@matchlist\qst@setupii}
\def\qst@setupii{\ifcase
    \ifcat\noexpand\qst@matchlist\bgroup\@ne\fi
    \ifx\qst@matchlist[\tw@\fi %]
    \m@ne
  \or
    \expandafter\MakeMatchTarget\expandafter\qst@matchlist
  \or
    \expandafter\qst@setupiii
  \else \expandafter\@gobble
  \fi}
\def\qst@setupiii[#1]{\MakeMatchTarget[{#1}]\qst@matchlist}
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\qst@parsestart}
%   This tests at the start of a new word whether we are finished or
%   whether there is an inverted condition.  Note that this will
%   remove spaces before a word.  |#1| indicates the polarity, |#2|~is
%   always a single token, no braces possible.
%    \begin{macrocode}
\def\qst@parsestart#1#2{%
  \ifcase
    \ifx\qst@comma#2\@ne\fi
    \ifx\qst@comma\relax
      \else\ifx!#2#1\tw@\@ne\fi
    \fi
    \thr@@
  \or
    \expandafter\qst@parsestart
    \expandafter\@firstoftwo
  \or
    \expandafter\qst@parsestart
    \expandafter\@secondoftwo
  \or
    \expandafter\qst@mmtmp
    \expandafter#1%
    \expandafter#2%
  \fi
}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@afterfi}
%   This is just a helper macro cleaning up a following |\fi|.
%    \begin{macrocode}
\def\qst@afterfi#1#2\fi{\fi#1}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@parseonecase}
%   We process one pattern here.  A pattern ends with |,| and we have
%   passed in |#1| the command to execute in case the pattern
%   matches.
%    \begin{macrocode}
\def\qst@parseonecase#1#2{%
  \noexpand\qst@checkname {\qst@parsewild#2*\,23456789*}%
  \noexpand#1\qst@parsestart\@firstoftwo}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@parsewild}
%   This is the parsing macro for wildcards and stuff.  |#1|~is
%   material before the next |*| character or the end of the sequence,
%   |#2|~is either empty (indicating the end of the word to match) or
%   material coming after a wildcard and terminated by |*|,
%   |#3|~contains the next unused parameter number, and |#4| contains
%   following parameter numbers.  If we emitted a wildcard code, we
%   jump into \cmd{\qst@skipwild} in order to skip additional
%   wildcards.
%    \begin{macrocode}
\def\qst@parsewild#1*#2\,#3#4*{%
  #1%
  \ifx\,#2\,%
  \else
    #####3%
    \qst@afterfi{\qst@skipwild#2\,#4*}%
  \fi}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@skipwild}
%   This caters for the case where successive wildcards are
%   encountered.  This is the case if |#1| is empty, meaning that the
%   string started with |*|, but |#2| is non-empty, indicating that we
%   are not at the terminal |*|.
%    \begin{macrocode}
\def\qst@skipwild#1*#2\,{%
  #1%
  \ifcase
    \ifx *#1*\ifx\,#2\,\else \@ne \fi \fi
    \tw@
  \or
    \expandafter\qst@skipwild
  \or
    \expandafter\qst@parsewild\expandafter*%
  \fi#2\,}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@checkname}
%   This is called with a compiled form of a test pattern in~|#1| and
%   a single token that will get assigned to \cmd{\qst@finish} in case
%   that we got a match of the pattern with any of the comma-separated
%   keywords in \cmd{\MatchList}.
%
%   Here is how \cmd{\qst@checkname} works: both test pattern and
%   option list are placed between commas (actually \cmd{\,}) so that
%   a match of the test pattern on the list implies a full match.  The
%   test pattern happens to match itself when just interpreted as an
%   unexpanded pattern string, and this pattern string does not
%   contain commas in itself.  We append the pattern string as an
%   artificial last element in the option list.  Since the trailing
%   comma placed at the end of the match pattern can't match inside of
%   an option, options are consumed only completely.  If the pattern
%   matched a string containing a comma, it actually did so across
%   more than a single option.  In that case, we remove everything up
%   to the first comma and try again.
%
%   We define \cmd{\qst@mmtmp} as a macro that matches starting at an
%   arbitrary comma (throwing away everything preceding), then
%   retaining the matched string (note that |#1| in the replacement
%   text will faithfully reproduce what was matched by |#1| in the
%   argument string, also note the difference between |#1| and |##1|),
%   appending a separator of \cmd{\@nil} in order to delimit the
%   matched sequence.
%    \begin{macrocode}
\def\qst@checkname#1{%
  \def\qst@mmtmp##1\,#1\,{#1\,\@nil}%
  \expandafter\expandafter\expandafter\qst@checknameii
  \expandafter\qst@mmtmp
  \expandafter\,\qst@matchlist\,#1\,\@nil}
%    \end{macrocode}
% \begin{macro}{\qst@checknameii}
%   So we now do the check.  If |#3| is |\@nil|, the sentinel has been
%   consumed and we did not match anything.  If this is not the case,
%   we split the matched material at the first comma into |#1|
%   and~|#2|.  If |#2| is empty, no comma is contained in the match,
%   so we matched a complete option.  Otherwise, if |#2| is non-empty,
%   the match contained a comma and was across several options
%   (possibly breaking into the sentinel without consuming it).  Ugh.
%   We remove the first option that was conflated in the match, and
%   restart the match process.
%
%   Note that |#3| has its origin from behind a comma, so at its
%   position there are no leading spaces that could get lost by it
%   being an undelimited argument.
%    \begin{macrocode}
\def\qst@checknameii#1\,#2\@nil#3{%
  \ifcase
    \ifx\@nil#3\@ne\fi
    \ifx\@nil#2\@nil \tw@\fi
    \m@ne
  \or
    \expandafter\qst@checknamenomatch
  \or
    \expandafter\qst@checknamematch
  \fi \expandafter\qst@checknameii\qst@mmtmp#1\,#2#3}
%    \end{macrocode}
% \begin{macro}{\qst@checknamenomatch}
%   This removes remaining garbage and the code corresponding to the
%   polarity of the test since the test is not taken.
%    \begin{macrocode}
\def\qst@checknamenomatch#1\@nil#2{}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@checknamematch}
%   We had a match, something which did not include a comma and at the
%   same time left the sentinel at the end of the word list intact.
%   So clean up and take over the right polarity.
%    \begin{macrocode}
\def\qst@checknamematch#1\qst@mmtmp#2\,#3\@nil{%
  \def\MatchedTarget{#2}%
  \let\qst@finish}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% Now for removing patterns: |\romannumeral\z@| expands to
% nothing, but we use it as a an \cmd{\expandafter}-Propagator.
% \begin{macro}{\RemoveMatched}
%    \begin{macrocode}
\def\RemoveMatched#1{\ifx#1\MatchedTarget
    \let#1\@empty
  \else
    \expandafter\def\expandafter\qst@mmtmp
    \expandafter##\expandafter1\expandafter\,\MatchedTarget\,{##1\,}%
    \expandafter\def\expandafter#1\expandafter{%
      \romannumeral\expandafter\expandafter\expandafter\z@
      \expandafter\qst@remmat
      \romannumeral\expandafter\expandafter\expandafter\z@
      \expandafter\qst@mmtmp\expandafter\,#1\,\@nil}%
  \fi}
%    \end{macrocode}
% \end{macro}
% \begin{macro}{\qst@remmat}
%   This is just a helper macro removing \cmd{\,} from around the
%   list.
%    \begin{macrocode}
\def\qst@remmat\,#1\,\@nil{#1}
%    \end{macrocode}
% \end{macro}
% 
% Ok, let's test this thing for some patterns.
%    \begin{macrocode}
%</package>
%<*test>
\makeatletter
\begin{qstest}{\MakeMatcher internals}{\MakeMatcher,internal}
  \MakeMatcher\sometest{}
  \Expect*{\meaning\sometest}{macro:#1->\qst@setup#1\qst@finish}
  \MakeMatcher[,]\sometest{ a, bc*, !woozle**, !*#*woz***.*x, !!*ab}
  \Expect*{\meaning\sometest}{macro:#1->%
      \qst@setup#1%
      \qst@checkname{a}\@firstoftwo
      \qst@checkname{bc##2}\@firstoftwo
      \qst@checkname{woozle##2}\@secondoftwo
      \qst@checkname{##2####3woz##4.##5x}\@secondoftwo
      \qst@checkname{##2ab}\@firstoftwo
      \qst@finish}
  \MakeMatcher[ ]\another{ a bc* ! woozle** ! *#*woz***.*x ! ! *ab}
  \Expect*{\meaning\another}*{\meaning\sometest}
\end{qstest}
%</test>
%<*package>
%    \end{macrocode}
% 
% \begin{macro}{\MakeMatchTarget}
%   This gets two obligatory arguments, a control sequence name and
%   the keyword list.  This list is sanitized and prepared for
%   matching.  The list sanitized in this manner is stored in the
%   control sequence in~|#2|, the first obligatory argument.  The
%   gobbledygook before and after the detokenized content is there to
%   get started nicely into the loop with \cmd{\qst@mmtmp} and get out
%   again without having to employ expensive tests.  The purpose of
%   \cmd{\qst@mmtmp} is to remove leading spaces from list elements
%   and replace the list separator (which defaults to `|,|' but can be
%   specified with an optional argument to \cmd{\MakeMatchTarget})
%   with the sentinel separator |\,| which can't otherwise occur in a
%   sanitized list.  There are several special values for the list
%   separator that make sense:
%   \begin{description}
%   \item[\textvisiblespace] A space character (catcode 10).  List
%     entries are separated by spaces.
%   \item[$\textrm{other}_{12}$] A character of catcode~12 that is
%     going to be used as a list separator.  Don't use anything other
%     than sanitized characters, or the effects will be as with the
%     following:
%   \item[\texttt{\string\relax}] The default, does not do any list
%     replacement.
%   \end{description}
%   Don't ever pass anything other than a single token (possibly
%   brace-enclosed) in this manner, or things will go really wrong.
%    \begin{macrocode}
\newcommand\MakeMatchTarget[3][\relax]{%
  \def\qst@mmtmp##1#1##2{##1\noexpand\,\qst@mmtmp##2}%
%<*etex>
  \edef#2{\expandafter\qst@mmtmp\expandafter
    \@gobbletwo\expandafter#1\detokenize{#3}\iffalse#1\fi}%
%</etex>
%<*!etex>
  \toks@{#3}%
  \edef#2{\the\toks@}%
  \@onelevel@sanitize#2%
  \edef#2{\expandafter\qst@mmtmp\expandafter
    \@gobbletwo\expandafter#1#2\iffalse#1\fi}%
%</!etex>
}
%    \end{macrocode}
% \end{macro}
% \subsection{The End}
% The test can be ended either in the driver file, or in a standalone
% testfile.  \cmd{\stop} is \LaTeX's way of ending the processing of
% an input file before any document processing or setup has been done.
%    \begin{macrocode}
%</package>
%<test>\stop
%    \end{macrocode}
% \Finale