1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
%%% ====================================================================
%%% @LaTeX-doc-source-file{
%%% filename = "patchcmd.dtx",
%%% version = "1.03",
%%% date = "2000/07/31",
%%% time = "08:40:39 EDT",
%%% author = "Michael J Downes",
%%% email = "mjd@ams.org",
%%% abstract = "Provides a way to add material at beginning or end of
%%% a macro's existing definition.",
%%% checksum = "23183 222 932 7411",
%%% docstring = "The checksum field, produced by Robert Solovay's
%%% checksum utility, gives CRC-16 checksum, lines,
%%% words, and characters.",
%%% }
%%% ====================================================================
% \iffalse
%<*driver>
\NeedsTeXFormat{LaTeX2e}
\documentclass{amsdtx}
\begin{document}
\title{The \pkg{patchcmd} package}
\author{Michael~J. Downes}
\date{Version \fileversion, \filedate}
\hDocInput{patchcmd.dtx}
\end{document}
%</driver>
% \fi
%
% \maketitle
% \section{Introduction}
%
% The \pkg{patchcmd} package provides a command \cn{patchcommand} that
% can be used to add material at the beginning and/or end of the
% replacement text of an existing macro. It works for macros with any
% number of normal arguments (0--9), including macros that were
% defined with \cn{DeclareRobustCommand}. However, it does not work
% for macros that use \cs{futurelet}, for example ones defined to
% have starred forms or optional arguments. In addition, it does not
% work for macros that have delimited arguments.
%
% This package is the result of some discussion initiated by Peter
% Wilson in the newsgroup \fn{comp.text.tex} in June 2000 and
% continued with Peter and Heiko Oberdiek.
%\begin{verbatim}
%Subject: Re: Adding stuff at end of a macro
%References:
% <3ojiks8in1f5s575eta5rg7ncoc98mpi8f@4ax.com>
% <39492C50.A962193B@boeing.com>
% <pcitvabqk2.fsf@thor.ams.org>
%\end{verbatim}
%
% \StopEventually{}
%
% \section{Implementation}
% Standard declaration of package name and date.
% \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{patchcmd}[2000/07/31 v1.03]
%% Copyright 2000 Michael John Downes
%% This file has no restrictions on its use, distribution, or sale.
% \end{macrocode}
%
% For debugging.
% \begin{macrocode}
%%\def\wrs{\immediate\write\sixt@@n}
% \end{macrocode}
%
% \begin{macrocode}
\newcommand{\patchcommand}[1]{%
\expandafter\patchcmd@a\meaning#1??->@\@nil#1%
}
% \end{macrocode}
%
% We begin by looking at the meaning string of the given token. We
% will issue an error message if the token is not a control sequence,
% if it is an undefined control sequence, or if it is a known control
% sequence but not a macro.
% \begin{macrocode}
\long\def\patchcmd@a#1#2#3->#4#5\@nil#6{%
%% \wrs{\string#6: [#1] [#2] [#3]->[#4]}%
\ifx @#4\relax \patchcmdError#6#1%
\expandafter\@gobbletwo % discard the other two arguments
\else
\if l#2\toks@{\patchcmd@e{}#6}% l in this position means \long
\else \toks@{\patchcmd@e*#6}% not \long
\fi
% \end{macrocode}
% Now \cs{patchcmd@b} will do further analysis to determine what
% kinds of arguments the macro takes. If it takes $n$ normal
% arguments, the digit $n$ (0--9) will be added to \cs{toks@}.
% \begin{macrocode}
\patchcmd@b #3@#4#5 ? ? ? \@nil#6%
\expandafter\the\expandafter\toks@
\fi
}
% \end{macrocode}
%
% We handle robust commands by scanning the first two control words
% in the macro's definition to see if the second one is followed by
% two spaces. If it is, then arg 7 will be empty. In that case we
% compare the csname preceding the two spaces to the original
% argument of \cn{patchcommand}. If they are equal it is nearly certain
% that this is a robust command in normal \LaTeX{} form. In that case
% we call \cn{patchcommand} quasi-recursively on the protected control
% sequence with the extra space at the end of its name.
% \begin{macrocode}
\def\patchcmd@b#1:#2@#3#4 #5#6 #7 #8\@nil#9{%
%% \wrs{[#1] [#2] [#3] [#4] [#5] [#6] ARG7=[#7] [#8]}%
\if \ifx @#7@\expandafter
\ifx\csname #6\endcsname#9T\else F\fi\else F\fi T%
\toks@\expandafter{\expandafter\patchcommand\csname #6 \endcsname}%
\else
\ifx @#2@% No arguments
\toks@\expandafter{\the\toks@ 0}%
\else
\patchcmd@c 0#2{\string##}0%
\fi
\fi
}
% \end{macrocode}
%
% The task of \cs{patchcmd@c} is to iterate over \texttt{\#D} pairs,
% where D is a digit in the range 1--9, until reaching the last one.
% Whenever we find such a pair, we carry the digit forward for the
% next recursive call; but we use digit 0 as a marker to terminate
% the recursion. If any other character interrupts the \texttt{\#D}
% pattern, it means that this macro has some kind of delimited
% argument.
% \begin{macrocode}
\def\patchcmd@c#1#2#3{%
\if\string###2% % yes it's a # token
\ifodd 0#31 % and it's followed by a number
\if 0#3\patchcmd@d#1\fi % number=0? then we're done
\else \patchcmd@d D% # not a number: must be a delimited arg
\fi
\else \patchcmd@d D% not a # token: must be a delmited arg
\fi
\patchcmd@c#3%
}
% \end{macrocode}
%
% \begin{macrocode}
\def\patchcmd@d#1{%
\if D#1%
\PackageError{patchcmd}{Cannot change a macro that has
delimited arguments}\@ehd
\else
\toks@\expandafter{\the\toks@ #1}%
\fi
\begingroup
\aftergroup\@gobble
\let\patchcmd@c\endgroup
}
% \end{macrocode}
%
% \begin{macrocode}
\def\patchcmd@e#1#2#3#4#5{%
\begingroup
\edef\@##1{%
\@temptokena\noexpand\expandafter{%
\noexpand#2%
\ifnum#3>0 {####1}\ifnum#3>1 {####2}\ifnum#3>2 {####3}%
\ifnum#3>3 {####4}\ifnum#3>4 {####5}\ifnum#3>5 {####6}%
\ifnum#3>6 {####7}\ifnum#3>7 {####8}\ifnum#3>8 {####9}%
\fi\fi\fi\fi\fi\fi\fi\fi\fi
##1%
}%
}
\@{#5}%
\edef\@##1{\endgroup
\noexpand\renewcommand#1\noexpand#2\ifcase#3 \else [#3]\fi
{##1\the\@temptokena}}%
\@{#4}%
%% \show#2%
}
% \end{macrocode}
%
% Two possible error messages. Optimized. Second arg is the first
% letter from the meaning string; it will be ``u'' if and only if the
% control sequence is undefined.
% \begin{macrocode}
\long\def\patchcmdError#1#2{%
\begingroup
\toks@{Not redefinable}%
\ifcat\relax\noexpand#1% Is it a control sequence?
\begingroup
\let#1=?\ifx ?\relax % Is it "\relax"?
\endgroup % accept current value of \toks@
\else \endgroup
% \end{macrocode}
%
% This is equivalent to the \cs{@ifundefined} test (true if arg 2 is
% either undefined or its meaning is \cs{relax}).
%
% \begin{macrocode}
\if\ifx\relax#1u\else #2\fi u%
\toks@{Not defined}%
\fi
\fi
\fi
% \end{macrocode}
% Apply \cs{string} preemptively to arg 1 to prevent catastrophic
% failure if it happens to be \cs{par} (\cs{PackageError} isn't
% defined as \cs{long} in older versions of \LaTeX{}).
% \begin{macrocode}
\edef\@{\endgroup
\noexpand\PackageError{patchcmd}{%
\the\toks@: \string#1}\noexpand\@ehd}%
\@
}
% \end{macrocode}
%
% The usual \cs{endinput} to ensure that random garbage at the end of
% the file doesn't get copied by \fn{docstrip}.
% \begin{macrocode}
\endinput
% \end{macrocode}
%
% \CheckSum{183}
% \Finale
|