% \iffalse ---!!! FIRST META-COMMENT !!!---
%
%
% This file is unhook-code.dtx from version 1.0
% of the free and open-source LaTeX package "unhook,"
% released September 2026.
%
% Running Plain TeX on unhook-code.dtx will
% produce the following files:
%
% (1) the package file unhook.tex;
%
% (2) the derived files unhook-heading.tex
% and unhook-user-guide.tex, which are
% used for typesetting documentation;
%
% and
%
% (3) a number of other derived files.
%
% Running LaTeX on unhook-code.dtx will produce the
% files listed above as well as the following:
%
% (4) the pdf documentation file unhook-code.pdf;
%
% and
%
% (5) a number of other derived files.
%
% To install unhook on your computer, run this file
% through Plain TeX or LaTeX and move unhook.tex
% to a directory searchable by TeX. See the associated
% README.txt file for installation information.
%
%
% \fi
% \iffalse ---!!! SECOND META-COMMENT !!!---
%
%
% This file is unhook_code.dtx from version 1.0 of the free
% and open-source LaTeX package "unhook," released September 2026.
%
% Copyright 2026 Conrad Kosowsky
%
% This file may be used, distributed, and modified under the
% terms of the LaTeX Public Project License, version 1.3c or
% any later version. The most recent version of this license
% is available online at
%
% https://www.latex-project.org/lppl/
%
% This Work has the LPPL status "maintained," and the current
% maintainer is the package author, Conrad Kosowsky. He can
% be reached at kosowsky.latex@gmail.com. The Work consists
% of the following items:
%
% (1) the base file:
% unhook-code.dtx
%
% (2) the package file:
% unhook.tex
%
% (3) the pdf documentation files:
% unhook-code.pdf
% unhook-user-guide.pdf
%
% (4) the derived files:
% unhook-user-guide.tex
% unhook-heading.tex
%
% (5) all other files created through the configuration
% process
%
% and
%
% (6) the associated README.txt file
%
% PLEASE KNOW THAT THIS FREE SOFTWARE IS PROVIDED WITHOUT
% ANY WARRANTY. SPECIFICALLY, THE "NO WARRANTY" SECTION OF
% THE LATEX PROJECT PUBLIC LICENSE STATES THE FOLLOWING:
%
% THERE IS NO WARRANTY FOR THE WORK. EXCEPT WHEN OTHERWISE
% STATED IN WRITING, THE COPYRIGHT HOLDER PROVIDES THE WORK
% `AS IS’, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
% OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
% PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
% OF THE WORK IS WITH YOU. SHOULD THE WORK PROVE DEFECTIVE,
% YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
% CORRECTION.
%
% IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
% TO IN WRITING WILL THE COPYRIGHT HOLDER, OR ANY AUTHOR
% NAMED IN THE COMPONENTS OF THE WORK, OR ANY OTHER PARTY
% WHO MAY DISTRIBUTE AND/OR MODIFY THE WORK AS PERMITTED
% ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
% SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT
% OF ANY USE OF THE WORK OR OUT OF INABILITY TO USE THE WORK
% (INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA, DATA BEING
% RENDERED INACCURATE, OR LOSSES SUSTAINED BY ANYONE AS A
% RESULT OF ANY FAILURE OF THE WORK TO OPERATE WITH ANY
% OTHER PROGRAMS), EVEN IF THE COPYRIGHT HOLDER OR SAID
% AUTHOR OR SAID OTHER PARTY HAS BEEN ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGES.
%
% For more information, see the LaTeX Project Public License.
% Derivative works based on this software may come with their
% own license or terms of use, and the package author is not
% responsible for any third-party software.
%
% Happy TeXing!
%
%
% \fi
% \iffalse
%
% The installation and driver files are incorporated into
% unhook_code.dtx, so we do not need to generate them separately.
% The and tags are for reference.
%
%<*batchfile>
\begingroup
\input docstrip.tex
\keepsilent
\askforoverwritefalse
\preamble
This file is from version 1.0 of the free and open-source
LaTeX package "unhook," released September 2026.
Copyright 2026 Conrad Kosowsky
This file may be distributed and modified under the terms
of the LaTeX Public Project License, version 1.3c or any
later version. The most recent version of this license is
available online at
https://www.latex-project.org/lppl/
This work has the LPPL status "maintained," and the current
maintainer is the package author, Conrad Kosowsky. He can
be reached at kosowsky.latex@gmail.com.
PLEASE KNOW THAT THIS FREE SOFTWARE IS PROVIDED WITHOUT
ANY WARRANTY. SPECIFICALLY, THE "NO WARRANTY" SECTION OF
THE LATEX PROJECT PUBLIC LICENSE STATES THE FOLLOWING:
THERE IS NO WARRANTY FOR THE WORK. EXCEPT WHEN OTHERWISE
STATED IN WRITING, THE COPYRIGHT HOLDER PROVIDES THE WORK
`AS IS’, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
OF THE WORK IS WITH YOU. SHOULD THE WORK PROVE DEFECTIVE,
YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
TO IN WRITING WILL THE COPYRIGHT HOLDER, OR ANY AUTHOR
NAMED IN THE COMPONENTS OF THE WORK, OR ANY OTHER PARTY
WHO MAY DISTRIBUTE AND/OR MODIFY THE WORK AS PERMITTED
ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT
OF ANY USE OF THE WORK OR OUT OF INABILITY TO USE THE WORK
(INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA, DATA BEING
RENDERED INACCURATE, OR LOSSES SUSTAINED BY ANYONE AS A
RESULT OF ANY FAILURE OF THE WORK TO OPERATE WITH ANY
OTHER PROGRAMS), EVEN IF THE COPYRIGHT HOLDER OR SAID
AUTHOR OR SAID OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
For more information, see the LaTeX Project Public License.
Derivative works based on this software may come with their
own license or terms of use, and the package author is not
responsible for any third-party software.
Happy TeXing!
\endpreamble
\generate{\file{unhook.tex}{\from{unhook-code.dtx}{code}}
\file{unhook-user-guide.tex}{\from{unhook-code.dtx}{user}}
\file{unhook-heading.tex}{\from{unhook-code.dtx}{heading}}}
\catcode`\ =12\relax
\immediate\write0{^^J%
***************************************************^^J%
* Step 1 of the package installation is complete! *^^J%
***************************************************^^J^^J%
***************************************************^^J%
* To finish installing unhook, move *^^J%
* unhook.tex to a directory searchable by TeX *^^J%
* after unhook-code.tex is done typesetting *^^J%
***************************************************^^J}
\endgroup
\ifx\LaTeX\undefined
\immediate\write0{Plain TeX format used; quitting now.}
\immediate\write0{To create unhook-code.pdf, run^^J%
unhook-code.dtx through LaTeX.^^J^^J}
\expandafter\end
\fi
%
%<*driver>
\makeatletter
\documentclass[12pt,doc2,letterpaper]{ltxdoc}
\usepackage[margin=72.27pt]{geometry}
\usepackage[factor=700,stretch=14,shrink=14,step=1]{microtype}
\parskip\z@
\begin{document}
\expandafter\def\expandafter\@listi\expandafter{\@listi
\parsep\z@
\topsep\medskipamount
\itemsep\z@}
\@listi
\def\documentname{Implementation}
\c@CodelineNo=59\relax
\input unhook-heading.tex
\DocInput{unhook-code.dtx}
\end{document}
%
%<*code>
%
% \fi
%
%
% \CheckSum{635}
% \init@checksum
%
% \makeatother
% \CharacterTable
% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
% Digits \0\1\2\3\4\5\6\7\8\9
% Exclamation \! Double quote \" Hash (number) \#
% Dollar \$ Percent \% Ampersand \&
% Acute accent \' Left paren \( Right paren \)
% Asterisk \* Plus \+ Comma \,
% Minus \- Point \. Solidus \/
% Colon \: Semicolon \; Less than \<
% Equals \= Greater than \> Question mark \?
% Commercial at \@ Left bracket \[ Backslash \\
% Right bracket \] Circumflex \^ Underscore \_
% Grave accent \` Left brace \{ Vertical bar \|
% Right brace \} Tilde \~}
% \makeatletter
%
%
% \noindent This file documents the code for the \textsf{unhook} package. It is not a user guide! For discussion of how \textsf{unhook} works and how to load it, see |unhook-user-guide.pdf|, which is included with the \textsf{unhook} installation and is available on \textsc{ctan}. I believe the \LaTeX\ team incorporated \LaTeX3 into the kernel in August 2020, so developers probably won't gain anything when using \textsf{unhook} with an earlier \LaTeX\ format. (But you should not be using a \TeX\ distribution that is that old anyway.) We're skipping package declaration because we're not allowing users to load \textsf{unhook} with |\usepackage|, so it is a good idea to start with a check of the format. The first 59 lines of |unhook.tex| are comments. Before anything else, we save and set the catcode of |\%| to 14 in case we're loading the file in a situation where the catcode is something else, e.g. while using \textsf{docstrip}.
% \begin{macrocode}
\edef\temp{\number\catcode`\%}
\catcode`\%=14\relax
\ifx\LaTeX\undefined
\begingroup
\newlinechar=`\^^J
\errhelp{The file unhook.tex is for use with the LaTeX^^J%
format. I'm guessing you're using plain TeX,^^J%
in which case there's no point loading it, so^^J%
I'm going to stop reading it in. To resolve^^J%
this error, typeset your document with LaTeX.^^J}
% \end{macrocode}
% We need an error message if \textsf{unhook} can't detect \LaTeX. This next code is based on |lterror.dtx| and incorporates what the documentation describes as a ``horrible hack,'' which I think is actually ingenious, to make an error message that looks like \LaTeX\ errors, i.e. it ends in a line containing \verb*( ...( followed by a blank line. When \TeX\ encounters |\errmessage|, it prints the argument of the control sequence on the terminal. If the |\errmessage| command appears inside a macro, \TeX\ then prints the definition of the macro up to |\errmessage| on one line followed by any remaining portion of the macro definition on the next line. \TeX\ may shorten this text by replacing some of it with an ellipsis. It follows that
% \begin{enumerate}
% \item If the macro name is stored internally as a space, the line with the macro definition will begin with a space followed by |...|
% \item If the final characters of |\errmessage| and the closing brace are stored internally as spaces, then the line with the macro definition will contain nothing after the |...|
% \item If |\errmessage| appears last in the macro, \TeX\ will include a blank line after the error
% \end{enumerate}
% We can achieve these three conditions by (a) defining the error using |~|; (b) using |\lccode| to turn |~| and |}| into a space; and (c) ending the |\errmessage| with a macro whose name ends in a large number of space characters and that expands to nothing. Inside |\errmessage|, the actual error message doesn't show the control sequence because it expands when \TeX\ prints the message, and \TeX\ doesn't show anything after the |...| because all final characters of the definition of |~| are stored in memory as spaces.
% \begin{macrocode}
\catcode`\~=13\relax
\lccode`\~=`\ %
\lccode`\}=`\ %
% \end{macrocode}
% We set other |\lccode|'s so that |\lowercase| doesn't mess up the text of our error message.
% ^^A This is actually a horrible hack to make the commented line show up
% ^^A in the documentation and the tex file. (Way more horrible than what's
% ^^A in lterror.dtx in my opinion.) We can force the commented line to
% ^^A show up in the sty file with the correct guards, but then we have to
% ^^A remove them from the documentation. We turn Q into an active char
% ^^A that gets rid of the guards and tokenize it before the macrocode
% ^^A environment. We also need to make < active because apparently it is
% ^^A an active character inside macrocode??
% \bgroup
% \catcode`\Q=\active
% \catcode`\<=\active
% \@firstofone{
% \begin{macrocode}\edefQ{##1\@percentchar\noexpand<\noexpand|
\empty %
{}%
\lowercase{%
\def~{\errmessage{^^J^^JPackage unhook error: Needs LaTeX format^^J^^J%
See the unhook package documentation for explanation.^^J%
Type H for immediate help%
\empty %
}}~%
% \end{macrocode}
% \egroup
% Brace that matches |\lowercase{| appears on its own line so that the most recent line that \TeX\ has scanned contains minimal text. This means \TeX\ will print minimal extra text after the error message.
% \begin{macrocode}
}
\endgroup
\expandafter\endinput % balance the conditional before \endinput
\fi
% \end{macrocode}
% Now check that |@| has catcode 11. If no, stop loading the package and print a warning message. We could change the catcode ourselves, but if we don't, it serves as a check to prevent casual users from loading the file. If someone needs to use this package, they will understand what to do without further explanation. Yes, I am deliberately making it inaccessible to load the package---if you are persistent enough to get this far and don't understand what's happening, feel free to email me, and I will explain in detail.
% \begin{macrocode}
\ifnum\catcode`\@=11\relax
\else
% \end{macrocode}
% Scan past the end of |\PackageError| so that minimal extra text (just a |}|) gets printed with the error message.
% \begin{macrocode}
\csname @firstofone\endcsname{%
\PackageError{unhook}{Wrong catcode for @}
{The catcode of @ needs to be 11 when you load unhook.tex.^^J%
I'm going to stop reading in the package file now.^^J}}
}
\expandafter\endinput % balance the conditional before \endinput
\fi
% \end{macrocode}
% Reset the primitives |\@@par|, |\everypar|, and |\shipout|. We reset |\@@par| because this is traditionally how the \LaTeX\ kernel stores |\par|. We are assuming that if the \LaTeX3 names of these primitives don't exist, then we don't need to restore the original control sequences.
% \begin{macrocode}
\ifcsname tex_par:D\endcsname
\expandafter\let\expandafter\@@par\csname tex_par:D\endcsname
\fi
\ifcsname tex_everypar:D\endcsname
\expandafter\let\expandafter\everypar\csname tex_everypar:D\endcsname
\everypar{}
\fi
\ifcsname tex_shipout:D\endcsname
\expandafter\let\expandafter\shipout\csname tex_shipout:D\endcsname
\fi
% \end{macrocode}
% More |\par| resetting. The kernel expects |\par| to be the same as |\@@par| by default, and |\endgraf| is a synonym for |\par|.
% \begin{macrocode}
\let\par\@@par
\let\endgraf\@@par
% \end{macrocode}
% Supporting macro that checks if the next token is |[| and gobbles an optional argument if yes.
% \begin{macrocode}
\def\unhook@n@@pt[#1]{}
\def\unhook@noopt{\@ifnextchar[\unhook@n@@pt\relax}
% \end{macrocode}
% We disable some of the code for using hooks.
% \begin{macrocode}
\protected\def\UseHook#1{}
% \end{macrocode}
% The |WithArguments| hooks are harder because they have a variable number of arguments. We need to gobble the first argument, then process the second argument, which should be a number, and then remove that many arguments from the input stream.
% \begin{macrocode}
\def\unhook@gobblex#1{%
\count@=\numexpr#1\relax
% \end{macrocode}
% If the number of arguments is non-positive, we assume that means zero arguments, in which case we have nothing to do. Otherwise, we have to remove arguments from the input stream. If the user requested more than 9 arguments, raise an error and cap the request at 9.
% \begin{macrocode}
\ifnum\count@>0\relax
\ifnum\count@>9\relax
\@latex@error{Hook limited to 9 arguments}\@ehc
\count@=9\relax
\fi
% \end{macrocode}
% The next few lines get a bit wild. We are using parameter arguments to change the parameter specification of |\@tempa| from |#1#2#3#4#5#6#7#8#9| to only |\count@| arguments. Here is the value of |\@tempa| immediately after each of the next three lines, where \meta{number} is the value of |#1|:
% \begin{enumerate}
% \item |->####1|\meta{number}|####2|
% \item |#1|\meta{number}|#2\@nil->#1|\meta{number}
% \item |#1#2|\dots|#|\meta{number}|->|
% \end{enumerate}
% In the third line below, the application of |\@tempa| yields
% \begin{trivlist}
% \item\hskip 2em |#1<-\def\@tempa##1| \dots\ |##|\meta{$\hbox{number\/}-1$}|##|
% \item\hskip 2em |#2<-##|\meta{$\hbox{number\/}+1$} \dots\ |##9\@nil|
% \end{trivlist}
% so we end up with |\def\@tempa| followed by the correct number of parameters.
% \begin{macrocode}
\edef\@tempa{####1\the\count@####2}%
\expandafter\edef\expandafter\@tempa\@tempa\@nil{##1\the\count@}%
\@tempa\def\@tempa##1##2##3##4##5##6##7##8##9\@nil{}%
% \end{macrocode}
% Now we are ready to gobble from the input stream.
% \begin{macrocode}
\expandafter\@tempa
\fi}
% \end{macrocode}
% We can use this macro to disable the commands for using hook with arguments.
% \begin{macrocode}
\protected\def\UseHookWithArguments#1{\unhook@gobblex}
\protected\def\UseOneTimeHookWithArguments#1{\unhook@gobblex}
% \end{macrocode}
% Also disable code for modifying hooks.
% \begin{macrocode}
\protected\def\AddToHook#1{\expandafter\@gobble\unhook@noopt}
\protected\def\AddToHookWithArguments#1{\expandafter\@gobble\unhook@noopt}
\protected\def\RemoveFromHook#1{\unhook@noopt}
\protected\def\AddToHookNext#1#2{}
\protected\def\AddToHookNextWithArguments#1#2{}
\protected\def\ClearHookNext#1{}
\protected\def\PushDefaultHookLabel#1\PopDefaultHookLabel{}
\protected\def\SetDefaultHookLabel#1{}
% \end{macrocode}
% When I've seen |\IfHookEmptyTF|, it has come after |\romannumeral| to force expansion. If |\romannumeral| appears right before |\IfHookEmptyTF|, we want it to expand to 0 (or something negative) to satisfy |\romannumeral| and make it disappear. I believe this is the purpose of |\exp_end:|, which is defined as |\char"0| and appears in the original definitions of these macros.
% \begin{macrocode}
\def\IfHookEmptyTF#1#2#3{0\relax}
\def\IfHookEmptyT#1#2{0\relax}
\def\IfHookEmptyF#1#2{0\relax}
% \end{macrocode}
% Disable some of the code for using sockets.
% \begin{macrocode}
\protected\def\UseSocket#1{}
\protected\def\UseTaggingSocket#1{}
% \end{macrocode}
% I'm assuming it is the same situation for the checks of sockets.
% \begin{macrocode}
\def\IfSocketExistsTF#1#2#3{0\relax}
\def\IfSocketPlugExistsTF#1#2#3#4{0\relax}
\def\IfSocketPlugAssignedTF#1#2#3#4{0\relax}
% \end{macrocode}
% As far as I can tell, all the material for the beginning of the document gets stored in \verb*(\__hook_toplevel begindocument(. We redefine |\UseOneTimeHook| to call this macro in place of processing the |begindocument| hook name and similarly for |enddocument|. For any other hook name, the macro gobbles its argument. We implement this approach by making |\@tempa| be |\relax| unless we're at the beginning or end of the document, in which case it holds the material from the hook.
% \begin{macrocode}
\protected\def\UseOneTimeHook#1{%
\let\@tempa\relax
\def\@tempb{#1}%
\def\@tempc{begindocument}%
\ifx\@tempb\@tempc
\expandafter\let\expandafter\@tempa
\csname __hook_toplevel begindocument\endcsname
\else
\def\@tempc{enddocument}%
\ifx\@tempb\@tempc
\expandafter\let\expandafter\@tempa
\csname __hook_toplevel enddocument\endcsname
\fi
\fi
\@tempa}
% \end{macrocode}
% We redefine |\AtBeginDocument| to append code to \verb*(\__hook_toplevel begindocument(. This macro is global in the kernel, so we use |\xdef| when we redefine it. If the macro does not exist, we do not redefine it because that could mess up another implementation of |\AtBeginDocument|.
% \begin{macrocode}
\ifcsname __hook_toplevel begindocument\endcsname
\protected\def\AtBeginDocument#1{%
\bgroup
\toks@\expandafter\expandafter\expandafter{%
\csname __hook_toplevel begindocument\endcsname}%
\@temptokena{#1}%
\expandafter\xdef
\csname __hook_toplevel begindocument\endcsname{%
\the\toks@\the\@temptokena}%
\egroup}
\fi
% \end{macrocode}
% And do |\AtEndDocument|.
% \begin{macrocode}
\ifcsname __hook_toplevel enddocument\endcsname
\protected\def\AtEndDocument#1{%
\bgroup
\toks@\expandafter\expandafter\expandafter{%
\csname __hook_toplevel enddocument\endcsname}%
\@temptokena{#1}%
\expandafter\xdef
\csname __hook_toplevel enddocument\endcsname{%
\the\toks@\the\@temptokena}%
\egroup}
\fi
% \end{macrocode}
% If the next few commands are undefined, letting them |\relax| or |\@gobble| their argument shouldn't be a problem.
% \begin{macrocode}
\let\@expl@sys@load@backend@@ \relax
\let\@kernel@before@begindocument \relax
\let\@kernel@after@begindocument \relax
\let\@kernel@after@begindocument@before \relax
\let\@kernel@before@enddocument \relax
\let\@kernel@after@enddocument \relax
\let\@kernel@before@enddocument@afterlastpage\relax
\let\@kernel@after@enddocument@afterlastpage \relax
\let\@kernel@before@para@before \relax
\let\@kernel@before@para@begin \relax
\let\@kernel@after@para@after \relax
\let\@kernel@after@para@end \relax
\let\@execute@begin@hook \@gobble
% \end{macrocode}
% We also need to restore a small piece of the output routine.
% \begin{macrocode}
\def\@opcol{%
\if@twocolumn
\@outputdblcol
\else
\@outputpage
%\global\@colht\textheight
\fi
\global\@mparbottom\z@
\global\@textfloatsheight\z@
\@floatplacement}
% \end{macrocode}
% The rest of the code here is from an older version of |ltfiles.dtx|. We start with |\@iinput|, which is from the \LaTeX\ version of |\input| when followed by a square bracket.
% \begin{macrocode}
\def\@iinput#1{%
\InputIfFileExists{#1}{}%
{\filename@parse{#1}%
\edef\reserved@a{\noexpand\@missingfileerror
{\filename@area\filename@base}%
{\ifx\filename@ext\relax tex\else\filename@ext\fi}}%
\reserved@a}}
% \end{macrocode}
% The next two macros are the ``safe'' file-loading commands.
% \begin{macrocode}
\long\def\InputIfFileExists#1#2{%
\IfFileExists{#1}%
{#2\@addtofilelist{#1}\@@input\@filef@und}}
\long\def\IfFileExists#1#2#3{%
\openin\@inputcheck#1 %
\ifeof\@inputcheck
\ifx\input@path\@undefined
\def\reserved@a{#3}%
\else
\def\reserved@a{\@iffileonpath{#1}{#2}{#3}}%
\fi
\else
\closein\@inputcheck
\edef\@filef@und{#1 }%
\def\reserved@a{#2}%
\fi
\reserved@a}
% \end{macrocode}
% Macro for handling file paths.
% \begin{macrocode}
\long\def\@iffileonpath#1{%
\let\reserved@a\@secondoftwo
\expandafter\@tfor\expandafter\reserved@b\expandafter
:\expandafter=\input@path\do{%
\openin\@inputcheck\reserved@b#1 %
\ifeof\@inputcheck\else
\edef\@filef@und{\reserved@b#1 }%
\let\reserved@a\@firstoftwo%
\closein\@inputcheck
\@break@tfor
\fi}%
\reserved@a}
% \end{macrocode}
% The next three macros are for loading package and class files and handling their options.
% \begin{macrocode}
\def\@fileswith@pti@ns#1[#2]#3[#4]{%
\ifx#1\@clsextension
\ifx\@classoptionslist\relax
\xdef\@classoptionslist{\zap@space#2 \@empty}%
\def\reserved@a{%
\@onefilewithoptions#3[{#2}][{#4}]#1%
\@documentclasshook}%
\else
\def\reserved@a{%
\@onefilewithoptions#3[{#2}][{#4}]#1}%
\fi
\else
\def\reserved@b##1,{%
\ifx\@nnil##1\relax
\else
\ifx\@nnil##1\@nnil
\else
\noexpand\@onefilewithoptions##1[{#2}][{#4}]%
\noexpand\@pkgextension
\fi
\expandafter\reserved@b
\fi}%
\edef\reserved@a{\zap@space#3 \@empty}%
\edef\reserved@a{\expandafter\reserved@b\reserved@a,\@nnil,}%
\fi
\reserved@a}
\let\@@fileswith@pti@ns\@fileswith@pti@ns
\def\@onefilewithoptions#1[#2][#3]#4{%
\@pushfilename
\xdef\@currname{#1}%
\global\let\@currext#4%
\expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@empty
\let\CurrentOption\@empty
\@reset@ptions
\makeatletter
\def\reserved@a{%
\@ifl@aded\@currext{#1}%
{\@if@ptions\@currext{#1}{#2}{}%
{\@latex@error
{Option clash for \@cls@pkg\space #1}%
{The package #1 has already been loaded
with options:\MessageBreak
\space\space[\@ptionlist{#1.\@currext}]\MessageBreak
There has now been an attempt to load it
with options\MessageBreak
\space\space[#2]\MessageBreak
Adding the global options:\MessageBreak
\space\space\@ptionlist{#1.\@currext},#2\MessageBreak
to your \noexpand\documentclass declaration may fix this.%
\MessageBreak
Try typing \space \space to proceed.}}}%
{\@pass@ptions\@currext{#2}{#1}%
\global\expandafter
\let\csname ver@\@currname.\@currext\endcsname\@empty
\InputIfFileExists
{\@currname.\@currext}%
{}%
{\@missingfileerror\@currname\@currext}%
\let\@unprocessedoptions\@@unprocessedoptions
\csname\@currname.\@currext-h@@k\endcsname
\expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@undefined
\@unprocessedoptions}
\@ifl@ter\@currext{#1}{#3}{}%
{\@latex@warning@no@line
{You have requested,\on@line,
version\MessageBreak
`#3' of \@cls@pkg\space #1,\MessageBreak
but only version\MessageBreak
`\csname ver@#1.\@currext\endcsname'\MessageBreak
is available}}%
\ifx\@currext\@clsextension\let\LoadClass\@twoloadclasserror\fi
\@popfilename
\@reset@ptions}%
\reserved@a}
% \end{macrocode}
% Macro for passing options to a class or package file.
% \begin{macrocode}
\def\@pass@ptions#1#2#3{%
\expandafter\xdef\csname opt@#3.#1\endcsname{%
\@ifundefined{opt@#3.#1}\@empty
{\csname opt@#3.#1\endcsname,}%
\zap@space#2 \@empty}}
% \end{macrocode}
% The last three macros are for handling the filename stack.
% \begin{macrocode}
\def\@pushfilename{%
\xdef\@currnamestack{%
{\@currname}%
{\@currext}%
{\the\catcode`\@}%
\@currnamestack}}
\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil}
\def\@p@pfilename#1#2#3#4\@nil{%
\gdef\@currname{#1}%
\gdef\@currext{#2}%
\catcode`\@#3\relax
\gdef\@currnamestack{#4}}
% \end{macrocode}
% And reset |%|
% \begin{macrocode}
\catcode`\%=\temp\relax
% \end{macrocode}
% Finished!
%
% \check@checksum
%
%
% \vfill\eject
% \section*{Version History}
%
% New features and updates with each version. Listed in no particular order.
%
% \begin{multicols*}{2}
% \raggedright\parskip\z@\parindent\z@\leftskip1em\obeylines
% \setbox0\hbox{\hskip 1pt.\hskip 1pt}
% \def\version#1#2{\par\bigskip
% \hbox to \hsize{\textbf{#1}\kern2pt%
% \cleaders\copy0\hfill\kern2pt#2\strut}\par}
% \def\item{\leavevmode\raise0.5ex\hbox{\vrule height 1pt width 0.5em}\kern1pt}
% \def\item{---\kern0.2ex\relax}^^A I like the em dash better than a vrule
%
% \version{1.0}{September 2026}
% \item initial release
%
%
%
%
%
% \end{multicols*}
%
%
%
%
%
%
%
%
%
%
%
%
%
% \iffalse
%
%<*user>
%
\makeatletter
\documentclass[12pt]{article}
\usepackage[margin=1in,letterpaper]{geometry}
\usepackage[factor=700,stretch=14,shrink=14,step=1]{microtype}
\usepackage[colorlinks=true,allcolors=blue,implicit=false]{hyperref}
\usepackage{soul}
\usepackage{shortvrb}
\MakeShortVerb{|}
\pagestyle{empty}
\begin{document}
\def\documentname{User Guide}
\input unhook-heading.tex
\noindent\LaTeX3 offers extensive functionality on top of traditional \LaTeXe, but it is often much harder to follow the output from |\tracing| commands. For example, with traditional \LaTeXe\ and full tracing, \TeX\ prints |{\par}| on the terminal at blank lines, but with \LaTeX3, the output looks like this:
\setbox0\hbox{\ttfamily\footnotesize a}
% The active Q here is a trick to let us avoid explicitly typing
% ifhmode. doc doesn't realize it's verbatim text, and it
% thinks there is an unbalanced conditional if we explicitly
% type ifhmode, which is a problem because this
% portion of the document is supposed to be iffalse (commented
% out)
\bgroup
\topsep\medskipamount
\partopsep\z@
\footnotesize
\catcode`\Q=\active
\edefQ{\expandafter\string\csname ifhmode\endcsname}
\@firstofone{%
% the line of code is 79 characters long, so we say 79\wd0
\begin{verbatim}\leftskip\dimexpr(\hsize-79\wd0)/2\relax}
~.\par ->\scan_stop: \mode_if_horizontal:TF {\mode_if_inner:F {\tex_unskip:D \h
ook_use:n {para/end}\@kernel@after@para@end \mode_if_horizontal:TF {\if_int_com
pare:w 11=\tex_lastnodetype:D \tex_hskip:D \c_zero_dim \fi: \tex_par:D \hook_us
e:n {para/after}\@kernel@after@para@after }{\msg_error:nnnn {hooks}{para-mode}{
end}{horizontal}}}}\tex_par:D
{\relax}
~..\mode_if_horizontal:TF ->\if_mode_horizontal: \__prg_TF_true:w \fi: \use_ii:
nn
{\ifhmode: (level 1) entered on line 13}
{false}
{\fi: Q (level 1) entered on line 13}
~...\use_ii:nn #1#2->#2
~..#1<-\mode_if_inner:F {\tex_unskip:D \hook_use:n {para/end}\@kernel@after@par
a@end \mode_if_horizontal:TF {\if_int_compare:w 11=\tex_lastnodetype:D \tex_hsk
ip:D \c_zero_dim \fi: \tex_par:D \hook_use:n {para/after}\@kernel@after@para@af
ter }{\msg_error:nnnn {hooks}{para-mode}{end}{horizontal}}}
~..#2<-\tex_par:D
{\par}
\end{verbatim}
\egroup
\noindent Hooks generate even more text, and as a result, tracing becomes ineffective for debugging \LaTeXe\ code. To fix this problem, \textsf{unhook} restores |\@@par|, |\everypar|, and |\shipout|, disables some commands that activate hooks and sockets, and replaces part of the file-loading interface with older (simpler) code. These changes should slim down output from |\tracingall|, but please reach out to the package author if you see unexpected material in the terminal. The package file is called |unhook.tex|, so you have to input it directly instead of loading with |\usepackage|. You must also set the catcode of |@| to be 11, and if that does not happen, \textsf{unhook} will stop loading and issue an error. The package does not define any user-level commands. Users who find \textsf{unhook} helpful may also appreciate the \textsf{trace} package.\footnote{Frank Mittlebach, ``\textsf{trace}---Make sensible use of \TeX\ tracing in \LaTeX,'' \href{https://ctan.org/pkg/trace}{\ttfamily https://ctan.org/pkg/trace}.}
\end{document}
%
%
%<*heading>
%
\def\packageversion{1.0}
\def\packagedate{September 2026}
% penalties
\pretolerance=-1
\hyphenpenalty=20
\exhyphenpenalty=15
\brokenpenalty=0
\clubpenalty=0
\widowpenalty=0
\finalhyphendemerits=500
\doublehyphendemerits=2000
% macro formatting
\@ifpackageloaded{doc}
{\MacroIndent=1.3em
\edef\MacroFont{\unexpanded\expandafter{\MacroFont}%
\baselineskip=\the\baselineskip plus 0.5pt minus 0.5pt\relax}}{\relax}
% header and footer
\def\@oddhead{%
\lower 0.08in\vbox to 0pt{\vss
\hrule width \textwidth height \p@
\vskip -\p@
\@@line{\vrule width \p@\hskip-\p@
\vbox{\medskip
\hb@xt@\textwidth{\hss\strut Warning! This package deliberately breaks certain functionality. Use only for debugging.\hss}
\vskip\medskipamount}\relax
\hskip-\p@\vrule width \p@}\vskip-\p@
\hrule width \textwidth height \p@}}
% heading
{\large\centering
{\strut\Large Package \textsf{unhook} v.\ \packageversion\ \documentname}\par
\strut Conrad Kosowsky\par
\strut \packagedate\par
\strut\texttt{kosowsky.latex@gmail.com}\par}
\medskip
% abstract/overview
{\small
\leftskip=0.5in
\rightskip=0.5in
\centerline{\bfseries Overview\strut}
\noindent The \textsf{unhook} package disables various \LaTeX3 features to improve tracing. It is intended for \LaTeXe\ package developers to make debugging easier, and you should not load it for any other purpose because it breaks a lot of things. The package file is |unhook.tex|, so you should input it directly instead of calling |\usepackage|.\par}
\bigskip\smallskip\nointerlineskip
\centerline{\vrule height 0.5pt width 2.5in}\bigskip\smallskip
\nointerlineskip
%
%
% \fi
%
%
\endinput