% \iffalse meta-comment
%
% Copyright (C) 2020-2025
%       Frank Mittelbach, Phelype Oleinik, The LaTeX Project
%
% This file is part of the LaTeX base system.
% -------------------------------------------
%
% It may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%    https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008 or later.
%
% This file has the LPPL maintenance status "maintained".
%
% The list of all files belonging to the LaTeX base distribution is
% given in the file `manifest.txt'. See also `legal.txt' for additional
% information.
%
% The list of derived (unpacked) files belonging to the distribution
% and covered by LPPL is defined by the unpacking scripts (with
% extension .ins) which are part of the distribution.
%
% \fi
%
% \iffalse
%%% From File: lthooks.dtx
%
%<*driver>
% \fi
\ProvidesFile{lthooks.dtx}
             [2025/10/01 v1.1n LaTeX Kernel (hooks)]
% \iffalse
%
\documentclass{l3doc}
\GetFileInfo{lthooks.dtx}

\providecommand\InternalDetectionOff{}
\providecommand\InternalDetectionOn{}

\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{lthooks.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \providecommand\hook[1]{\texttt{#1}}
%
% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}}
% \providecommand\fmiinline[1]{\begin{quote}\itshape\footnotesize FMi: #1\end{quote}}
% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}}
% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}}
%
%
%
% \title{\LaTeX{}'s hook management\thanks{This module has version
%    \fileversion\ dated \filedate, \copyright\ \LaTeX\
%    Project.}}
%
% \author{Frank Mittelbach\thanks{Code improvements for speed and other goodies by Phelype Oleinik}}
%
% \maketitle
%
%
% \tableofcontents
%
% \section{Introduction}
%
%    Hooks are points in the code of commands or environments where it
%    is possible to add processing code into existing commands. This
%    can be done by different packages that do not know about each
%    other, and to allow for hopefully safe processing it is necessary
%    to sort different chunks of code added by different packages into
%    a suitable processing order.
%
%    This is done by the packages adding chunks of code (via
%    \cs{AddToHook}) and labeling their code with some label by
%    default using the package name as a label.
%
%    At \verb=\begin{document}= all code for a hook is then sorted
%    according to some rules (given by \cs{DeclareHookRule}) for fast
%    execution without processing overhead. If the hook code is
%    modified afterwards (or the rules are changed),
%    a new version for fast processing is generated.
%
%    Some hooks are used already in the preamble of the document. If
%    that happens then the hook is prepared for execution (and sorted)
%    already at that point.
%
%
% \section{Package writer interface}
%
%    The hook management system is offered as a set of CamelCase
%    commands for traditional \LaTeXe{} packages (and for use in the
%    document preamble if needed) as well as \texttt{expl3} commands
%    for modern packages, that use the L3 programming layer of
%    \LaTeX{}. Behind the scenes, a single set of data structures is
%    accessed so that packages from both worlds can coexist and access
%    hooks in other packages.
%
%
%
% \subsection{\LaTeXe\ interfaces}
%
% \subsubsection{Declaring hooks}
%
%  With a few exceptions, hooks have to be declared before they can
%  be used. The exceptions are the generic hooks for commands and
%  environments (executed at \cs{begin} and \cs{end}), and the generic
%  hooks run when loading files (see section~\ref{sec:generic}).
%
% \begin{function}{\NewHook}
%   \begin{syntax}
%     \cs{NewHook} \Arg{hook}
%   \end{syntax}
%   Creates a new \meta{hook}.
%    If this hook is declared within a package it is suggested
%    that its name is always structured as follows:
%    \meta{package-name}\texttt{/}\meta{hook-name}. If necessary you
%    can further subdivide the name by adding more \texttt{/} parts.
%    If a hook name is already taken, an error is raised and the hook
%    is not created.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
%    The string \texttt{??} can't be used as a hook name because it
%    has a special significance  as a placeholder in hook rules.
%
% \end{function}
%
% \begin{function}{\NewReversedHook}
%   \begin{syntax}
%     \cs{NewReversedHook} \Arg{hook}
%   \end{syntax}
%     Like \cs{NewHook} declares a new \meta{hook}.
%     the difference is that the code chunks for this hook are in
%     reverse order by default (those added last are executed first).
%     Any rules for the hook are applied after the default ordering.
%     See sections~\ref{sec:order} and \ref{sec:reversed-order}
%    for further details.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\NewMirroredHookPair}
%   \begin{syntax}
%     \cs{NewMirroredHookPair} \Arg{hook-1} \Arg{hook-2}
%   \end{syntax}
%     A shorthand for
%    \cs{NewHook}\Arg{hook-1}\cs{NewReversedHook}\Arg{hook-2}.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
% \begin{function}{\NewHookWithArguments}
%   \begin{syntax}
%     \cs{NewHookWithArguments} \Arg{hook} \Arg{number}
%   \end{syntax}
%   Creates a new \meta{hook} whose code takes \meta{number} arguments,
%   and otherwise works exactly like \cs{NewHook}.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\NewReversedHookWithArguments}
%   \begin{syntax}
%     \cs{NewReversedHookWithArguments} \Arg{hook} \Arg{number}
%   \end{syntax}
%   Like \cs{NewReversedHook}, but creates a hook whose code takes
%   \meta{number} arguments.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\NewMirroredHookPairWithArguments}
%   \begin{syntax}
%     \cs{NewMirroredHookPairWithArguments} \Arg{hook-1} \Arg{hook-2} \Arg{number}
%   \end{syntax}
%   A shorthand for
%   \cs{NewHookWithArguments}\Arg{hook-1}\Arg{number}\\
%   \cs{NewReversedHookWithArguments}\Arg{hook-2}\Arg{number}.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
% \subsubsection{Special declarations for generic hooks}
%
%    The declarations here should normally not be used. They are available
%    to provide support for special use cases mainly involving
%    generic command hooks.
%
% \changes{v1.0p}{2021/08/20}{Documentation updates for generic hook commands (gh/638)}
%
% \begin{function}{\DisableGenericHook}
%   \begin{syntax}
%     \cs{DisableGenericHook} \Arg{hook}
%   \end{syntax}
%    After this declaration\footnotemark{} the \meta{hook} is no longer
%    usable: Any further attempt to add code to it will result in an
%    error and any use, e.g., via \cs{UseHook}, will simply do nothing.
%
%    This is intended to be used with generic command hooks (see
%    \texttt{ltcmdhooks-doc}) as depending on the definition of the
%    command such generic hooks may be unusable. If that is known, a
%    package developer can disable such hooks up front.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}\footnotetext{In the 2020/06 release this command was
%    called \cs{DisableHook}, but that name was misleading as it
%    shouldn't be used to disable non-generic hooks.}
%
%
% \begin{function}{\ActivateGenericHook}
%   \begin{syntax}
%     \cs{ActivateGenericHook} \Arg{hook}
%   \end{syntax}
%    This declaration activates a generic hook provided by a package/class
%    (e.g., one used in code with \cs{UseHook} or
%    \cs{UseOneTimeHook}) without it being explicitly declared with
%    \cs{NewHook}).
%    If the hook is already activated, this command does nothing.
%
%    Note that this command does not undo the effect of \cs{DisableGenericHook}.
%    See section~\ref{sec:generic-hooks} for a discussion of when this
%    declaration is appropriate.
% \end{function}
%
%
%
%
%
% \subsubsection{Using hooks in code}
%
% Using a hook that is executing the code that has been associated
% with it is only allowed if the hook has been previously declared
% with \cs{NewHook}. For performance reason there are no runtime
% checks for this and it is the responsibility of the programmer of a
% package to ensure that all hooks that are used in a package (with
% one of the commands in this section) are declared first.
%
% \begin{function}{\UseHook}
%   \begin{syntax}
%     \cs{UseHook} \Arg{hook}
%   \end{syntax}
%    Execute the code stored in the \meta{hook}.
%
%    Before \verb=\begin{document}= the fast execution code for a hook
%    is not set up, so in order to use a hook there it is explicitly
%    initialized first. As that involves assignments using a hook at
%    those times is not 100\% the same as using it after
%    \verb=\begin{document}=.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\UseHookWithArguments}
%   \begin{syntax}
%     \cs{UseHookWithArguments} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
%   \end{syntax}
%    Execute the code stored in the \meta{hook} and pass the arguments
%    \Arg{arg_1} through \Arg{arg_n} to the \meta{hook}.  Otherwise, it
%    works exactly like \cs{UseHook}.
%    The \meta{number} should be the number of arguments declared for
%    the hook.  If the hook is not declared, this command does nothing
%    and it will remove \meta{number} items from the input.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\UseOneTimeHook}
%   \begin{syntax}
%     \cs{UseOneTimeHook} \Arg{hook}
%   \end{syntax}
%    Some hooks are only used (and can be only used) in one place, for
%    example, those in \verb=\begin{document}= or
%    \verb=\end{document}=. From that point onwards, adding to the hook
%    through a defined \cs[no-index]{\meta{addto-cmd}} command (e.g.,
%    \cs{AddToHook} or \cs{AtBeginDocument}, etc.\@) would have no
%    effect (as would the use of such a command inside the hook code
%    itself). It is therefore customary to redefine
%    \cs{\meta{addto-cmd}} to simply  process its argument, i.e.,
%    essentially make it behave like \cs{@firstofone}.
%
%    \cs{UseOneTimeHook} does that: it records that the hook has been
%    consumed and any further attempt to add to it will result in
%    executing the code to be added immediately.
%
%    Using \cs{UseOneTimeHook} several times with the same
%    \Arg{hook} means that it only executes the first time it is used.
%    For example, if it is used in a command that can be called several times
%    then the hook executes during only the \emph{first} invocation of that
%    command; this allows its use as an \enquote{initialization hook}.
%
%    Mixing \cs{UseHook} and \cs{UseOneTimeHook} for the same
%    \Arg{hook} should be avoided, but if this is done then neither will execute
%    after the first \cs{UseOneTimeHook}.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.    See
%    section~\ref{sec:default-label} for details.
%
% \end{function}
%
% \begin{function}{\UseOneTimeHookWithArguments}
%   \begin{syntax}
%     \cs{UseOneTimeHookWithArguments} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
%   \end{syntax}
%    Works exactly like \cs{UseOneTimeHook}, but passes arguments
%    \Arg{arg_1} through \Arg{arg_n} to the \meta{hook}.
%    The \meta{number} should be the number of arguments declared for
%    the hook.  If the hook is not declared, this command does nothing
%    and it will remove \meta{number} items from the input.
%
%    It should be noted that after a one-time hook is used, it is no
%    longer possible to use \cs{AddToHookWithArguments} or similar with
%    that hook.  \cs{AddToHook} continues to work as normal.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.    See
%    section~\ref{sec:default-label} for details.
%
% \end{function}
%
%
% \subsubsection{Updating code for hooks}
%
% In contrast to the commands from the previous section, declarations
% such as \cs{AddToHook} or \cs{DeclareHookRule} can be
% used even when the hook is not yet declared. The rationale is that
% the hook declaration may be in some package that is loaded later, or
% perhaps not loaded at all.
%
% A side effect of this design is that misspellings do not raise an
% error but are simply regarded as declarations for hooks with a
% different name.
%
% \begin{function}{\AddToHook}
%   \begin{syntax}
%     \cs{AddToHook} \Arg{hook} \oarg{label} \Arg{code}
%   \end{syntax}
%    Adds \meta{code} to the \meta{hook} labeled by \meta{label}.
%    When the optional argument \meta{label} is not provided, the
%    \meta{default label} is used (see section~\ref{sec:default-label}).
%    If \cs{AddToHook} is used in a package/class, the
%    \meta{default label} is the package/class name, otherwise it is
%    \hook{top-level} (the \hook{top-level} label is treated
%    differently:  see section~\ref{sec:top-level}).
%
%    If there already exists code under the \meta{label} then the new
%    \meta{code} is appended to the existing one (even if this is a reversed hook).
%    If you want to replace existing code under the
%    \meta{label}, first apply \cs{RemoveFromHook}.
%
%    The hook doesn't have to exist for code to be added to
%    it. However, if it is not declared, then obviously the
%    added \meta{code} will never be executed.  This
%    allows for hooks to work regardless of package loading order and
%    enables packages to add to hooks from other packages without
%    worrying whether they are actually used in the current document.
%    See section~\ref{sec:querying}.
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\AddToHookWithArguments}
%   \begin{syntax}
%     \cs{AddToHookWithArguments} \Arg{hook} \oarg{label} \Arg{code}
%   \end{syntax}
%    Works exactly like \cs{AddToHook}, except that the \meta{code} can
%    access the arguments passed to the hook using \verb|#1|, \verb|#2|,
%    \ldots, \verb|#n| (up to the number of arguments declared for the
%    hook).  If the \meta{code} should contain \emph{parameter tokens}
%    (\verb|#|) that are not supposed to be understood as the arguments
%    of the hook, such tokens should be doubled.  For example, with
%    \cs{AddToHook} one can write:
%\begin{verbatim}
%   \AddToHook{myhook}{\def\foo#1{Hello, #1!}}
%\end{verbatim}
%    but to achieve the same with \cs{AddToHookWithArguments}, one should
%    write:
%\begin{verbatim}
%   \AddToHookWithArguments{myhook}{\def\foo##1{Hello, ##1!}}
%\end{verbatim}
%    because in the latter case, \verb|#1| refers to the first argument
%    of the hook \hook{myhook}.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\RemoveFromHook}
%   \begin{syntax}
%     \cs{RemoveFromHook} \Arg{hook} \oarg{label}
%   \end{syntax}
%    Removes any code labeled by \meta{label} from the \meta{hook}.
%    When the optional argument \meta{label} is not provided, the
%    \meta{default label} is used (see section~\ref{sec:default-label}).
% \end{function}
%
%    If there is no code under the \meta{label} in the \meta{hook},
%    or if the \meta{hook} does not exist, a warning is issued when
%    you attempt to \cs{RemoveFromHook}, and the command is ignored.
%    \cs{RemoveFromHook}\marginpar{\raggedleft\rightskip5pt\itshape
%        \textbf{Important:}\break The \cs{RemoveFromHook} command should be
%        only used if one has full control over the code chunk to be removed.
%        In particular it should not be used to remove code chunks from other
%        packages! For this the \texttt{voids} relation is provided.}
%    should be used only when you know exactly what
%    labels are in a hook. Typically this will be when some code gets added to a hook 
%    by a package, then later this code is removed by that same package.
%    If you want to prevent the execution of code from another
%    package, use the |voids| rule instead (see section~\ref{sec:rules}).
%
%    If the optional \meta{label} argument is \texttt{*}, then all code chunks are
%    removed. This is rather dangerous as it may well drop code from other
%    packages (that one may not know about); it should therefore not be used
%    in packages but only in document preambles!
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
%
% In contrast to the \texttt {voids} relationship between two labels
% in a \cs{DeclareHookRule} this is a destructive operation as the
% labeled code is removed from the hook data structure, whereas the
% relationship setting can be undone by providing a different
% relationship later.
%
% \medskip
%
% A useful application for this declaration inside the document body
% is when one wants to temporarily add code to hooks and later remove
% it again, e.g.,
% \changes{v1.1l}{2025/01/03}{Correct example to use \texttt{env/quote/begin}
%                             instead of \texttt{/before} (gh/1599)}
%\begin{verbatim}
%   \AddToHook{env/quote/begin}{\small}
%   \begin{quote}
%     A quote set in a smaller typeface
%   \end{quote}
%   ...
%   \RemoveFromHook{env/quote/begin}
%   ... now back to normal for further quotes
%\end{verbatim}
% Note that you can't cancel the setting with
%\begin{verbatim}
%   \AddToHook{env/quote/begin}{}
%\end{verbatim}
% because that only \enquote{adds} a further empty chunk of code to
% the hook. Adding \cs{normalsize} would work but that means the hook
% then contained \cs{small}\cs{normalsize} which means two font size
% changes for no good reason.
%
% The above is only needed if one wants to typeset several quotes in a
% smaller typeface. If the hook is only needed once then
% \cs{AddToHookNext} is simpler, because it resets itself after one use.
%
%
% \begin{function}{\AddToHookNext}
%   \begin{syntax}
%     \cs{AddToHookNext} \Arg{hook} \Arg{code}
%   \end{syntax}
%    Adds \meta{code} to the next invocation of the \meta{hook}.
%    The code is executed after the normal hook code has finished and
%    it is executed only once, i.e. it is deleted after it was used.
%
%    Using this declaration is a global operation, i.e., the code is
%    not lost even if the declaration is used inside a group and the
%    next invocation of the hook happens after the end of that group. 
%    If the declaration is
%    used several times before the hook is executed then all code is
%    executed in the order in which it was declared.\footnotemark
%
%    If this declaration is used with a one-time hook then the code 
%    is only ever used if the declaration comes before the hook’s 
%    invocation.  This is because, in contrast 
%    to \cs{AddToHook}, the code in this declaration is not 
%    executed immediately in the case when the invocation of the hook
%    has already happened---in other words, this code will truly execute
%    only on the next invocation of the hook (and in the case of a
%    one-time hook there is no such \enquote{next invocation}).  
%    This gives you a choice: should my code execute
%    always, or should it execute only at the point where the
%    one-time hook is used (and not at all if this is impossible)? For
%    both of these possibilities there are use cases.
%
%    It is possible to nest this declaration using the same hook (or
%    different hooks): e.g.,
%   \begin{quote}
%     \cs{AddToHookNext}\Arg{hook}\verb={=\meta{code-1}^^A
%     \cs{AddToHookNext}\Arg{hook}\Arg{code-2}\verb=}=
%   \end{quote}
%    will execute \meta{code-1} next time the \meta{hook} is used and at
%    that point puts \meta{code-2} into  the \meta{hook} so that it gets
%    executed on following time the hook is run.
%
%    A hook doesn't have to exist for code to be added to it.  This
%    allows for hooks to work regardless of package loading
%    order.
%    See section~\ref{sec:querying}.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name.  See section~\ref{sec:default-label}.
% \end{function}\footnotetext{There is
%    no mechanism to reorder such code chunks (or delete them).}
%
% \begin{function}{\AddToHookNextWithArguments}
%   \begin{syntax}
%     \cs{AddToHookNextWithArguments} \Arg{hook} \Arg{code}
%   \end{syntax}
%    Works exactly like \cs{AddToHookNext}, but the \meta{code} can
%    contain references to the arguments of the \meta{hook} as described
%    for \cs{AddToHookWithArguments} above.
%   Section~\ref{sec:hook-args} explains hooks with arguments.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name.  See section~\ref{sec:default-label}.
% \end{function}
%
%
%
% \begin{function}{\ClearHookNext}
%   \begin{syntax}
%     \cs{ClearHookNext} \Arg{hook}
%   \end{syntax}
%    Normally \cs{AddToHookNext} is only used when you know precisely
%    where it will apply and why you want some extra code at that
%    point. However, there are a few use cases in which such a
%    declaration needs to be canceled, for example, when
%    discarding a page with \cs{DiscardShipoutBox} (but even then not
%    always), and in such situations \cs{ClearHookNext} can be
%    used.
% \end{function}
%
%
%
%
% \subsubsection{Hook names and default labels}
% \label{sec:default-label}
%
% It is best practice to use \cs{AddToHook} in packages or classes
% \emph{without specifying a \meta{label}} because then the package
% or class name is automatically used, which is helpful if rules are
% needed, and avoids mistyping the \meta{label}.
%
% Using an explicit \meta{label} is only necessary in very specific
% situations, e.g., if you want to add several chunks of code into a
% single hook and have them placed in different parts of the hook
% (by providing some rules).
%
% The other case is when you develop a larger package with several
% sub-packages. In that case you may want to use the same
% \meta{label} throughout the sub-packages in order to avoid
% that the labels change if you internally reorganize your code.
%
% Except for \cs{UseHook}, \cs{UseOneTimeHook} and \cs{IfHookEmptyTF}
% (and their \pkg{expl3} interfaces \cs{hook_use:n},
% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}), all \meta{hook}
% and \meta{label} arguments are processed in the same way: first,
% spaces are trimmed around the argument, then it is fully expanded
% until only character tokens remain.  If the full expansion of the
% \meta{hook} or \meta{label} contains a non-expandable non-character
% token, a low-level \TeX{} error is raised (namely, the \meta{hook} is
% expanded using \TeX's \cs{csname}\ldots\cs{endcsname}, as such,
% Unicode characters are allowed in \meta{hook} and \meta{label}
% arguments).  The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, and
% \cs{IfHookEmptyTF} are
% processed much in the same way except that spaces are not trimmed
% around the argument, for better performance.
%
% It is not enforced, but highly recommended that the hooks defined by
% a package, and the \meta{labels} used to add code to other hooks
% contain the package name to easily identify the source of the code
% chunk and to prevent clashes.  This should be the standard practice,
% so this hook management code provides a shortcut to refer to the
% current package in the name of a \meta{hook} and in a \meta{label}.
% If the \meta{hook} name or the \meta{label} consist just of a single dot
% (|.|), or starts with a dot followed by a slash (|./|) then the dot
% denotes the \meta{default label} (usually the current package or class
% name---see~\cs{SetDefaultHookLabel}).
% A \enquote{|.|} or \enquote{|./|} anywhere else in a \meta{hook} or in
% \meta{label} is treated literally and is not replaced.
%
% For example,
% inside the package \texttt{mypackage.sty}, the default label is
% \texttt{mypackage}, so the instructions:
% \begin{verbatim}
%   \NewHook   {./hook}
%   \AddToHook {./hook}[.]{code}     % Same as \AddToHook{./hook}{code}
%   \AddToHook {./hook}[./sub]{code}
%   \DeclareHookRule{begindocument}{.}{before}{babel}
%   \AddToHook {file/foo.tex/after}{code}
% \end{verbatim}
%    are equivalent to:
% \begin{verbatim}
%   \NewHook   {mypackage/hook}
%   \AddToHook {mypackage/hook}[mypackage]{code}
%   \AddToHook {mypackage/hook}[mypackage/sub]{code}
%   \DeclareHookRule{begindocument}{mypackage}{before}{babel}
%   \AddToHook {file/foo.tex/after}{code}                  % unchanged
% \end{verbatim}
%
% The \meta{default label} is automatically set equal to the name of the
% current package or class at the time the package is loaded.  If the
% hook command is used outside of a package, or the current file wasn't
% loaded with \cs{usepackage} or \cs{documentclass}, then the
% \texttt{top-level} is used as the \meta{default label}.  This may have
% exceptions---see \cs{PushDefaultHookLabel}.
%
% This syntax is available in all \meta{label} arguments and most
% \meta{hook} arguments, both in the \LaTeXe{} interface, and the \LaTeX3
% interface described in section~\ref{sec:l3hook-interface}.
%
% Note,\marginpar{\raggedleft\rightskip5pt\itshape \textbf{Important:}\break The dot-syntax
% is \textbf{not} available with
% \cs{UseHook} and some other commands that are typically used within code!}
% however, that the replacement of |.| by the \meta{default label}
% takes place when the hook command is executed, so actions that are
% somehow executed after the package ends will have the wrong
% \meta{default label} if the dot-syntax is used.  For that reason,
% this syntax is not available in \cs{UseHook} (and \cs{hook_use:n})
% because the hook is most of the time used outside of the package file
% in which it was defined. This syntax is also not available in the hook
% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}), because these
% conditionals are used in some performance-critical parts of the hook
% management code, and because they are usually used to refer to other
% package's hooks, so the dot-syntax doesn't make much sense.
%
% In some cases, for example in large packages, one may want to separate
% the code in logical parts, but still use the main package name as the
% \meta{label}, then the \meta{default label} can be set using
% \cs{PushDefaultHookLabel}\verb={...}=\,\ldots\cs{PopDefaultHookLabel}
% or \cs{SetDefaultHookLabel}\verb={...}=.
%
% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
%   \begin{syntax}
%     \cs{PushDefaultHookLabel} \Arg{default label}
%     \quad \meta{code}
%     \cs{PopDefaultHookLabel}
%   \end{syntax}
%   \cs{PushDefaultHookLabel} sets the current \meta{default label} to
%   be used in \meta{label} arguments, or when replacing a leading
%   ``|.|'' (see above).  \cs{PopDefaultHookLabel} reverts the
%   \meta{default label} to its previous value.
%
%   Inside a package or class, the \meta{default label} is equal to the
%   package or class name, unless explicitly changed.  Everywhere else,
%   the \meta{default label} is |top-level| (see
%   section~\ref{sec:top-level}) unless explicitly changed.
%
%   The effect of \cs{PushDefaultHookLabel} holds until the next
%   \cs{PopDefaultHookLabel}.  \cs{usepackage} (and \cs{RequirePackage}
%   and \cs{documentclass}) internally use
%   \begin{quote}
%     \cs{PushDefaultHookLabel}\Arg{package name} \\
%     \null \quad  \meta{package code} \\
%     \cs{PopDefaultHookLabel}
%   \end{quote}
%   to set the \meta{default label} for the package or class file.
%   Inside the \meta{package code} the \meta{default label} can also be
%   changed with \cs{SetDefaultHookLabel}.  \cs{input} and other
%   file input-related commands from the \LaTeX{} kernel do not use
%   \cs{PushDefaultHookLabel}, so code within files loaded by these
%   commands does \emph{not} get a dedicated \meta{label}! (that is, the
%   \meta{default label} is the current active one when the file was
%   loaded.)
%
%   Packages that provide their own package-like interfaces
%   (Ti\textit{k}Z's \cs{usetikzlibrary}, for example) can use
%   \cs{PushDefaultHookLabel} and \cs{PopDefaultHookLabel} to set
%   dedicated labels and to emulate \cs{usepackage}-like hook behavior
%   within those contexts.
%
%   The |top-level| label is treated differently, and is reserved to the
%   user document, so it is not allowed to change the
%   \meta{default label} to |top-level|.
% \end{function}
%
% \begin{function}{\SetDefaultHookLabel}
%   \begin{syntax}
%     \cs{SetDefaultHookLabel} \Arg{default label}
%   \end{syntax}
%   Similarly to \cs{PushDefaultHookLabel},
%   sets the current \meta{default label} to
%   be used in \meta{label} arguments, or when replacing a leading
%   ``|.|''. The effect holds until the label is changed again or until
%   the next \cs{PopDefaultHookLabel}.  The difference between
%   \cs{PushDefaultHookLabel} and \cs{SetDefaultHookLabel} is that the
%   latter does not save the current \meta{default label}.
%
%   This command is useful when a large package is composed of several
%   smaller packages, but all should have the same \meta{label}, so
%   \cs{SetDefaultHookLabel} can be used at the beginning of each
%   package file to set the correct label.
%
%   \cs{SetDefaultHookLabel} is not allowed in the main document, where
%   the \meta{default label} is |top-level| and there is no
%   \cs{PopDefaultHookLabel} to end its effect.
%   It is also not allowed to change the \meta{default label} to
%   |top-level|.
% \end{function}
%
% \subsubsection{The \texttt{top-level} label}
% \label{sec:top-level}
%
% The |top-level| label, assigned to code added from the main document,
% is different from other labels.  Code added to hooks (usually
% \cs{AtBeginDocument}) in the preamble is almost always to change
% something defined by a package, so it should go at the very end of the
% hook.
%
% Therefore, code added in the |top-level| is always executed at the end
% of the hook, regardless of where it was declared.  If the hook is
% reversed (see \cs{NewReversedHook}), the |top-level| chunk is executed
% at the very beginning instead.
%
% Rules regarding |top-level| have no effect:  if a user wants to have a
% specific set of rules for a code chunk, they should use a different
% label to said code chunk, and provide a rule for that label instead.
%
% The |top-level| label is exclusive for the user, so trying to add code
% with that label from a package results in an error.
%
% \subsubsection{Defining relations between hook code}
% \label{sec:rules}
%
% The default assumption is that code added to hooks by different
% packages are independent and the order in which they are executed is
% irrelevant. While this is true in many cases it is obviously false
% in others.
%
% Before the hook management system was introduced
% packages had to take elaborate precautions to determine whether some other
% package had also been loaded (before or after) and then to find some ways to
% alter its behavior accordingly. In addition is was often the user's
% responsibility to load packages in the right order so that alterations
% made by packages were done in thsat same order; and in some cases even
% altering the loading order wouldn't resolve the conflicts.
%
% With the new hook management system it is now possible to define
% rules (i.e., relationships) between code chunks added by different
% packages and to specify explicitly the order in which they should be
% processed.
%
% The rules can be declared for hooks before the hook has been
% declared with \cs{NewHook} and they are allowed to refer to code
% labels that do not yet exist, e.g., because a package defining the
% code chunk with that label has not yet been loaded.
% When the hook code is finally sorted for fast execution, all rules that apply are acted on
% and the others are ignored.
%
% This offers the flexibility needed to handle complicated
% relationships between code from different packages and to set this up
% beforehand in a way that is independent of whether or not the packages are actually loaded in a
% specific document. The downside of this is that misspellings of
% hook names or code labels will not raise any error, instead the rule
% will simply never apply!
%
% \begin{function}{\DeclareHookRule}
%   \begin{syntax}
%     \cs{DeclareHookRule} \Arg{hook} \Arg{label1} \Arg{relation} \Arg{label2}
%   \end{syntax}
%    Defines a relation between \meta{label1} and \meta{label2} for a
%    given \meta{hook}. If \meta{hook} is \texttt{??} this defines a default
%    relation for all hooks that use the two labels, i.e., that have
%    chunks of code labeled with \meta{label1} and \meta{label2}.
%
%    Currently, the supported relations are the following:
%    \begin{itemize}
%
%    \item[\texttt{before} or \texttt{\string<}]
%
%      Code for \meta{label1} comes before code for \meta{label2}.
%
%    \item[\texttt{after} or \texttt{\string>}]
%      Code for \meta{label1} comes after code for \meta{label2}.
%
%    \item[\texttt{incompatible-warning}]
%
%      Only code for either \meta{label1} or \meta{label2} can appear
%      for that hook (a way to say that two packages---or parts of
%      them---are incompatible). A warning is raised if both labels
%      appear in the same hook.
%
%    \item[\texttt{incompatible-error}]
%
%      Like \texttt{incompatible-error} but instead of a warning a
%      \LaTeX{} error is raised, and the code for both labels are
%      dropped from that hook until the conflict is resolved.
%
%    \item[\texttt{voids}]
%
%      Code for \meta{label1} overwrites code for \meta{label2}. More
%      precisely, code for \meta{label2} is dropped for that
%      hook. This can be used, for example if one package is a
%      superset in functionality of another one and therefore wants to
%      undo code in some hook and replace it with its own version.
%
%    \item[\texttt{unrelated}]
%
%       The order of code for \meta{label1} and \meta{label2} is
%      irrelevant. This rule is there to undo an incorrect rule
%      specified earlier.
%
%    \end{itemize}
%    There can only be a single relation between two labels for a
%    given hook, i.e., a later \cs{DeclareHookRule} overwrites any
%    previous declaration.  In all cases rules specific to a given
%    hook take precedence over default rules that use \texttt{??} as
%    the \meta{hook}.
%
%    If a default rule is applied, it is done before reversing the
%    label order in a reversed hook, e.g., \texttt{before} in a
%    default rule effectively becomes \texttt{after} in such a hook.
%    In contrast, a rule for a specific hook is always applied to the
%    state after any reversal (i.e., the state you see when using
%    \cs{ShowHook} on that hook).
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
%
% \end{function}
%
%
% \begin{function}{\ClearHookRule}
%   \begin{syntax}
%     \cs{ClearHookRule} \Arg{hook} \Arg{label1} \Arg{label2}
%   \end{syntax}
%    Syntactic sugar for saying that \meta{label1} and \meta{label2}
%    are unrelated for the given \meta{hook}.
% \end{function}
%
%
%
% \begin{function}{\DeclareDefaultHookRule}
%   \begin{syntax}
%     \cs{DeclareDefaultHookRule} \Arg{label1} \Arg{relation} \Arg{label2}
%   \end{syntax}
%   This sets up a relation between \meta{label1} and \meta{label2}
%    for all hooks unless overwritten by a specific rule for a hook.
%    Useful for cases where one package has a specific relation to
%    some other package, e.g., is \texttt{incompatible} or always
%    needs a special ordering \texttt{before} or \texttt{after}.
%    (Technically it is just a shorthand for using \cs{DeclareHookRule}
%    with \texttt{??} as the hook name.)
%
%    If such a rule is applied to a reversed 
%    hook it behaves as if the rule is reversed (e.g., 
%    \texttt{after} becomes \texttt{before}) 
%    because those rules are applied first and then the order is reversed.
%    The rationale is that in hook pairs (in which the ordering in one
%    is reversed) default rules have to be reversed too in nearly all
%    scenarios. If this is not the case, a default rule can't be used
%    or has to be overwritten with an explicit \cs{DeclareHookRule}
%    for that specific hook.
%
%    Declaring default rules is only supported in the document
%    preamble.\footnotemark{}
%
%    The \meta{label} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}\footnotetext{Trying to do so, e.g., via
%    \cs{DeclareHookRule} with \texttt{??}  has bad side-effects and
%    is not supported (though not explicitly caught for performance
%    reasons).}
%
%
%
% \subsubsection{Querying hooks}
% \label{sec:querying}
%
% Simpler data types, like token lists, have three possible states; they
% can:
% \begin{itemize}
%   \item exist and be empty;
%   \item exist and be non-empty; and
%   \item not exist (in which case emptiness doesn't apply);
% \end{itemize}
% Hooks are a bit more complicated:
% a hook may exist or not, and independently it may or may not be empty.
% This means that even a hook that doesn't exist may be non-empty and
%    it can also be disabled.
%
% This seemingly strange state may happen when, for example, package~$A$
% defines hook \hook{A/foo}, and package $B$ adds some code to that
% hook.  However, a document may load package $B$ before package $A$, or
% may not load package $A$ at all.  In both cases some code is added to
% hook \hook{A/foo} without that hook being defined yet, thus that
% hook is said to be non-empty, whereas it doesn't exist.  Therefore,
% querying the existence of a hook doesn't imply its emptiness, neither
% does the other way around.
%
% Given that code or rules can be added to a hook even if it doesn't
% physically exist yet, means that a querying its existence has no
% real use case (in contrast to other variables that can only be
% update if they have already been declared). For that reason only the
% test for emptiness has a public interface.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list.  The hook
% doesn't need to be declared to have code added to its code pool.
% A hook is said to exist when it was declared with \cs{NewHook} or
% some variant thereof.  Generic hooks such as \hook{file} and \hook{env} hooks are
% automatically declared when code is added to them.
%
% \begin{function}[EXP]{\IfHookEmptyTF,\IfHookEmptyT,\IfHookEmptyF}
%   \begin{syntax}
%     \cs{IfHookEmptyTF} \Arg{hook} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to
%   it using either \cs{AddToHook} or \cs{AddToHookNext}) or such code
%    was removed again (via \cs{RemoveFromHook}), and
%   branches to either \meta{true code} or \meta{false code} depending
%   on the result.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
%
%
% \subsubsection{Displaying hook code}\label{sec:display-hooks}
%
%    If one has to adjust the code execution in a hook using a hook
%    rule it is helpful to get some information about the code
%    associated with a hook, its current order and the existing rules.
%
% \begin{function}{\ShowHook,\LogHook}
%   \begin{syntax}
%     \cs{ShowHook} \Arg{hook}
%     \cs{LogHook}  \Arg{hook}
%   \end{syntax}
%   Displays information about the \meta{hook} such as
%   \begin{itemize}
%   \item
%      the code chunks (and their labels) added to it,
%   \item
%      any rules set up to order them,
%   \item
%      the computed order in which the chunks are executed,
%   \item
%      any code executed on the next invocation only.
%   \end{itemize}
% \end{function}
%
%   \cs{LogHook} prints the information to the |.log| file, and
%   \cs{ShowHook} prints them to the terminal/command window and starts
%   \TeX's prompt (only in \cs{errorstopmode}) to wait for user action.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
%
%^^A   % Code for the listing below:
%^^A   \NewHook{example-hook}
%^^A   \AddToHook{example-hook}{[code from 'top-level']}
%^^A   \AddToHook{example-hook}[foo]{[code from package 'foo']}
%^^A   \AddToHook{example-hook}[bar]{[from package 'bar']}
%^^A   \AddToHook{example-hook}[baz]{[package 'baz' is here]}
%^^A   \AddToHookNext{example-hook}{[one-time code]}
%^^A   \DeclareHookRule{example-hook}{baz}{before}{foo}
%^^A   \DeclareDefaultHookRule{bar}{after}{baz}
%^^A   \ShowHook{example-hook}
%
%   \def\theFancyVerbLine{\textcolor[gray]{0.5}{%^^A
%     \sffamily\tiny\arabic{FancyVerbLine}}}
%
%   \bigskip
%   Suppose a hook \texttt{example-hook} whose output of
%   \cs{ShowHook}|{example-hook}| is:
%   \begin{verbatim}[numbers=left]
%   -> The hook 'example-hook':
%   > Code chunks:
%   >     foo -> [code from package 'foo']
%   >     bar -> [from package 'bar']
%   >     baz -> [package 'baz' is here]
%   > Document-level (top-level) code (executed last):
%   >     -> [code from 'top-level']
%   > Extra code for next invocation:
%   >     -> [one-time code]
%   > Rules:
%   >     foo|baz with relation >
%   >     baz|bar with default relation <
%   > Execution order (after applying rules):
%   >     baz, foo, bar.
%   \end{verbatim}
%
%   In the listing above, lines~3 to~5 show the three code chunks added
%   to the hook and their respective labels in the format
%   \begin{quote}
%   \quad \meta{label}\verb| -> |\meta{code}
%   \end{quote}
%
%   Line~7 shows the code chunk added by the user in the main document
%   (labeled |top-level|) in the format
%   \begin{quote}
%   \quad\verb|Document-level (top-level) code (executed |%^^A
%              \meta{first\texttt{\string|}last}\verb|):|\\
%   \quad\verb|    -> |\meta{\texttt{top-level} code}
%   \end{quote}
%   This code will be either the first or last code executed by the hook
%   (|last| if the hook is normal, |first| if it is reversed).  This
%   chunk is not affected by rules and does not take part in sorting.
%
%   Line~9 shows the code chunk for the next execution of the hook in
%   the format
%   \begin{quote}
%   \quad \verb|-> |\meta{next-code}
%   \end{quote}
%   This code will be used and disappear at the next
%   \verb|\UseHook{example-hook}|, in contrast to the chunks mentioned
%   earlier, which can only be removed from that hook by doing
%   \verb|\RemoveFromHook{|\meta{label}|}[example-hook]|.
%
%   Lines~11 and~12 show the rules declared that affect this hook in the
%   format
%   \begin{quote}
%   \quad \meta{label-1}\verb+|+\meta{label-2}| with |%^^A
%         \meta{\texttt{default}?}| relation |\meta{relation}
%   \end{quote}
%   which means that the \meta{relation} applies to \meta{label-1} and
%   \meta{label-2}, in that order, as detailed in \cs{DeclareHookRule}.
%   If the relation is \texttt{default} it means that this rule applies
%   to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless
%   overridden by a non-default relation).
%
%   Finally, line~14 lists the labels in the hook after sorting;
%   that is, in the order they will be executed when the hook is used.
%
%
% \subsubsection{Debugging hook code}
%
% \begin{function}{\DebugHooksOn,\DebugHooksOff}
%   \begin{syntax}
%     \cs{DebugHooksOn} \ldots\ \cs{DebugHooksOff}
%   \end{syntax}
%    Turn the debugging of hook code on or off. This displays most changes
%    made to the hook data structures. The output is rather coarse and
%      not really intended for normal use, but it can be helpful in case hooks do
%      not work as expected. See also \ref{sec:display-hooks} for
%      commands to inspect individual hooks.
% \end{function}
%
%
% \subsection{L3 programming layer (\texttt{expl3}) interfaces}
% \label{sec:l3hook-interface}
%
%
% This is a quick summary of the \LaTeX3 programming interfaces for
% use with packages written in \texttt{expl3}. In contrast to the
% \LaTeXe{} interfaces they always use mandatory arguments only, e.g.,
% you always have to specify the \meta{label} for a code chunk.  We
% therefore suggest to use the declarations discussed in the previous
% section even in \texttt{expl3} packages, but the choice is yours.
%
%
% \begin{function}{
%     \hook_new:n,
%     \hook_new_reversed:n,
%     \hook_new_pair:nn
%   }
%   \begin{syntax}
%     \cs{hook_new:n} \Arg{hook}
%     \cs{hook_new_reversed:n} \Arg{hook}
%     \cs{hook_new_pair:nn} \Arg{hook-1} \Arg{hook-2}
%   \end{syntax}
%   Creates a new \meta{hook} with normal or reverse ordering of code
%    chunks. \cs{hook_new_pair:nn} creates a pair of such hooks with
%    \Arg{hook-2} being a reversed hook.
%    If a hook name is already taken, an error is raised and the hook
%    is not created.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{
%     \hook_new_with_args:nn,
%     \hook_new_reversed_with_args:nn,
%     \hook_new_pair_with_args:nnn
%   }
%   \begin{syntax}
%     \cs{hook_new_with_args:nn} \Arg{hook} \Arg{number}
%     \cs{hook_new_reversed_with_args:nn} \Arg{hook} \Arg{number}
%     \cs{hook_new_pair_with_args:nnn} \Arg{hook-1} \Arg{hook-2} \Arg{number}
%   \end{syntax}
%   Creates a new \meta{hook} with normal or reverse ordering of code
%    chunks, that takes \meta{number} arguments from the input stream
%    when it is used. \cs{hook_new_pair_with_args:nn} creates a pair of
%    such hooks with \Arg{hook-2} being a reversed hook.
%    If a hook name is already taken, an error is raised and the hook
%    is not created.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
%
% \begin{function}{\hook_disable_generic:n}
%   \begin{syntax}
%     \cs{hook_disable_generic:n} \Arg{hook}
%   \end{syntax}
%    Marks \Arg{hook} as disabled. Any further attempt to add code to
%    it or declare it, will result in an error and any call to
%    \cs{hook_use:n} will simply do nothing.
%
%    This declaration is intended for use with generic hooks that are
%    known not to work (see \texttt{ltcmdhooks-doc}) if they receive
%    code.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\hook_activate_generic:n}
%   \begin{syntax}
%     \cs{hook_activate_generic:n} \Arg{hook}
%   \end{syntax}
%    This is like \cs{hook_new:n} but it does nothing if the hook was previously
%    declared with \cs{hook_new:n}.  This declaration should be used
%    only in special situations, e.g., when a command from another package
%    needs to be altered and it is not clear whether a
%    generic \hook{cmd} hook (for that command) has been previously 
%    explicitly declared.
%
%    Normally \cs{hook_new:n} should be used instead of this.
% \end{function}
%
%
%
%
% \begin{function}{\hook_use:n}
%   \begin{syntax}
%     ~~\cs{hook_use:n} \Arg{hook}
%   \end{syntax}
% \end{function}
% \vskip-1.2\baselineskip
% \begin{function}{\hook_use:nnw}
%   \begin{syntax}
%     \cs{hook_use:nnw} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
%   \end{syntax}
%    Executes the \Arg{hook} code followed (if set up) by the code for next
%    invocation only, then empties that next invocation code.
%    \cs{hook_use:nnw} should be used for hooks declared with arguments,
%    and should be followed by as many brace groups as the declared
%    number of arguments.
%    The \meta{number} should be the number of arguments declared for
%    the hook.  If the hook is not declared, this command does nothing
%    and it will remove \meta{number} items from the input.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\hook_use_once:n}
%   \begin{syntax}
%     \cs{hook_use_once:n} \Arg{hook}
%   \end{syntax}
% \end{function}
% \vskip-1.2\baselineskip
% \begin{function}{\hook_use_once:nnw}
%   \begin{syntax}
%     \cs{hook_use_once:nnw} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
%   \end{syntax}
%     Changes the \Arg{hook} status so that from now on any addition to
%     the hook code is executed immediately. Then execute any
%     \Arg{hook} code already set up.
%    \cs{hook_use_once:nnw} should be used for hooks declared with arguments,
%    and should be followed by as many brace groups as the declared
%    number of arguments.
%    The \meta{number} should be the number of arguments declared for
%    the hook.  If the hook is not declared, this command does nothing
%    and it will remove \meta{number} items from the input.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{
%     \hook_gput_code:nnn,
%   }
%   \begin{syntax}
%     ~~~~~~~~~\cs{hook_gput_code:nnn} \Arg{hook} \Arg{label} \Arg{code}
%   \end{syntax}
% \end{function}
% \vskip -1.2\baselineskip
% \begin{function}{
%     \hook_gput_code_with_args:nnn
%   }
%   \begin{syntax}
%     \cs{hook_gput_code_with_args:nnn} \Arg{hook} \Arg{label} \Arg{code}
%   \end{syntax}
%    Adds a chunk of \meta{code} to the \meta{hook} labeled
%    \meta{label}. If the label already exists the \meta{code} is
%    appended to the already existing code.
%
%    If \cs{hook_gput_code_with_args:nnn} is used, the \meta{code}
%    can access the arguments passed to \cs{hook_use:nnw}
%    (or~\cs{hook_use_once:nnw}) with \verb|#1|, \verb|#2|, \ldots,
%    \verb|#n| (up to the number of arguments declared for the hook).
%    In that case, if an actual parameter token should be added to the
%    code, it should be doubled.
%
%    If code is added to an external \meta{hook} (of the kernel or
%    another package) then the convention is to use the package name
%    as the \meta{label} not some internal module name or some other
%    arbitrary string.
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{
%     \hook_gput_next_code:nn,
%   }
%   \begin{syntax}
%     ~~~~~~~~~~~~~\cs{hook_gput_next_code:nn} \Arg{hook} \Arg{code}
%   \end{syntax}
% \end{function}
% \vskip-1.2\baselineskip
% \begin{function}{
%     \hook_gput_next_code_with_args:nn,
%   }
%   \begin{syntax}
%     \cs{hook_gput_next_code_with_args:nn} \Arg{hook} \Arg{code}
%   \end{syntax}
%    Adds a chunk of \meta{code} for use only in the next invocation of the
%    \meta{hook}. Once used it is gone.
%
%    If \cs{hook_gput_next_code_with_args:nn} is used, the \meta{code}
%    can access the arguments passed to \cs{hook_use:nnw}
%    (or~\cs{hook_use_once:nnw}) with \verb|#1|, \verb|#2|, \ldots,
%    \verb|#n| (up to the number of arguments declared for the hook).
%    In that case, if an actual parameter token should be added to the
%    code, it should be doubled.
%
% \end{function}
%    This is simpler than \cs{hook_gput_code:nnn}, the code is simply
%    appended to the hook in the order of declaration at the very end,
%    i.e., after all standard code for the hook got executed.
%    Thus if one needs to undo what the standard does one has to do
%    that as part of \meta{code}.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
%
%
% \begin{function}{\hook_gclear_next_code:n}
%   \begin{syntax}
%     \cs{hook_gclear_next_code:n} \Arg{hook}
%   \end{syntax}
%   Undo any earlier \cs{hook_gput_next_code:nn}.
% \end{function}
%
%
%
%  \begin{function}{\hook_gremove_code:nn}
%   \begin{syntax}
%     \cs{hook_gremove_code:nn} \Arg{hook} \Arg{label}
%   \end{syntax}
%    Removes any code for \meta{hook} labeled \meta{label}.
%
%    If there is no code under the \meta{label} in the \meta{hook},
%    or if the \meta{hook} does not exist, a warning is issued when
%    you attempt to use \cs{hook_gremove_code:nn}, and the command is ignored.
%
%    If the second argument is \texttt{*}, then all code chunks are
%    removed. This is rather dangerous as it drops code from other
%    packages one may not know about, so think twice before using
%    that!
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
% \end{function}
%
%
%  \begin{function}{\hook_gset_rule:nnnn}
%   \begin{syntax}
%     \cs{hook_gset_rule:nnnn} \Arg{hook} \Arg{label1} \Arg{relation} \Arg{label2}
%   \end{syntax}
%    Relate \meta{label1} with \meta{label2} when used in \meta{hook}.
%    See \cs{DeclareHookRule} for the allowed \meta{relation}s.
%    If \meta{hook} is \texttt{??} a default rule is specified.
%
%    The \meta{hook} and \meta{label} can be specified using the
%    dot-syntax to denote the current package name.
%    See section~\ref{sec:default-label}.
%    The dot-syntax is parsed in both \meta{label} arguments, but it
%    usually makes sense to be used in only one of them.
% \end{function}
%
% \begin{function}[pTF]{\hook_if_empty:n}
%   \begin{syntax}
%     \cs{hook_if_empty:nTF} \Arg{hook} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to
%   it using either \cs{AddToHook} or \cs{AddToHookNext}), and
%   branches to either \meta{true code} or \meta{false code} depending
%   on the result.
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
%
% \begin{function}{\hook_show:n,\hook_log:n}
%   \begin{syntax}
%     \cs{hook_show:n} \Arg{hook}
%     \cs{hook_log:n}  \Arg{hook}
%   \end{syntax}
%   Displays information about the \meta{hook} such as
%   \begin{itemize}
%   \item
%      the code chunks (and their labels) added to it,
%   \item
%      any rules set up to order them,
%   \item
%      the computed order in which the chunks are executed,
%   \item
%      any code executed on the next invocation only.
%   \end{itemize}
%
%   \cs{hook_log:n} prints the information to the |.log| file, and
%   \cs{hook_show:n} prints them to the terminal/command window and starts
%   \TeX's prompt (only if \cs{errorstopmode}) to wait for user action.
%
%    The \meta{hook} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\hook_debug_on:,\hook_debug_off:}
%   \begin{syntax}
%     \cs{hook_debug_on:}
%   \end{syntax}
%    Turns the debugging of hook code on or off. This displays changes
%    to the hook data.
% \end{function}
%
%
%
% \subsection{On the order of hook code execution} \label{sec:order}
%
%    Chunks of code for a \meta{hook} under different labels are supposed
%    to be independent if there are no special rules set up that
%    define a relation between the chunks. This means that you can't
%    make assumptions about the order of execution!
%
%    Suppose you have the following declarations:
%\begin{verbatim}
%    \NewHook{myhook}
%    \AddToHook{myhook}[packageA]{\typeout{A}}
%    \AddToHook{myhook}[packageB]{\typeout{B}}
%    \AddToHook{myhook}[packageC]{\typeout{C}}
%\end{verbatim}
%    then executing the hook with \cs{UseHook} will produce the
%    typeout \texttt{A} \texttt{B} \texttt{C} in that order.  In other
%    words, the execution order is computed to be \texttt{packageA},
%    \texttt{packageB}, \texttt{packageC} which you can verify with
%    \cs{ShowHook}\texttt{\{myhook\}}:
%\begin{verbatim}
%    -> The hook 'myhook':
%    > Code chunks:
%    >     packageA -> \typeout {A}
%    >     packageB -> \typeout {B}
%    >     packageC -> \typeout {C}
%    > Document-level (top-level) code (executed last):
%    >     ---
%    > Extra code for next invocation:
%    >     ---
%    > Rules:
%    >     ---
%    > Execution order:
%    >     packageA, packageB, packageC.
%\end{verbatim}
%    The reason is that the code chunks are internally saved in a property list
%    and the initial order of such a property list is the order in
%    which key-value pairs got added. However, that is only true if
%    nothing other than adding happens!
%
%    Suppose, for example, you want to replace the code chunk for
%    \texttt{packageA}, e.g.,
%\begin{verbatim}
%    \RemoveFromHook{myhook}[packageA]
%    \AddToHook{myhook}[packageA]{\typeout{A alt}}
%\end{verbatim}
%    then your order becomes  \texttt{packageB},
%    \texttt{packageC}, \texttt{packageA} because the label got removed
%    from the property list and then re-added (at its end).
%
%    While that may not be too surprising,  the execution order is
%    also sometimes altered if you add a redundant rule, e.g. if you specify
%\begin{verbatim}
%    \DeclareHookRule{myhook}{packageA}{before}{packageB}
%\end{verbatim}
%    instead of the previous lines we get
%\begin{verbatim}
%    -> The hook 'myhook':
%    > Code chunks:
%    >     packageA -> \typeout {A}
%    >     packageB -> \typeout {B}
%    >     packageC -> \typeout {C}
%    > Document-level (top-level) code (executed last):
%    >     ---
%    > Extra code for next invocation:
%    >     ---
%    > Rules:
%    >     packageB|packageA with relation >
%    > Execution order (after applying rules):
%    >     packageA, packageC, packageB.
%\end{verbatim}
%    As you can see the code chunks are still in the same order, but
%    in the execution order for the labels \texttt{packageB} and
%    \texttt{packageC} have
%    swapped places.
%    The reason is that, with the rule there are two orders that
%    satisfy it, and the algorithm for sorting happened to pick a
%    different one compared to the case without rules (where it
%    doesn't run at all as there is nothing to resolve).
%    Incidentally, if we had instead specified the redundant rule
%\begin{verbatim}
%    \DeclareHookRule{myhook}{packageB}{before}{packageC}
%\end{verbatim}
%    the execution order would not have changed.
%
%    In summary: it is not possible to rely on the order of execution
%    unless there are rules that partially or fully define the order
%    (in which you can rely on them being fulfilled).
%
%
% \subsection{The use of \enquote{reversed} hooks} \label{sec:reversed-order}
%
%    You may have wondered why you can declare a \enquote{reversed} hook
%    with \cs{NewReversedHook} and what that does exactly.
%
%    In short: the execution order of a reversed hook (without any
%    rules!) is exactly reversed to the order you would have gotten for
%    a hook declared with \cs{NewHook}.
%
%    This is helpful if you have a pair of hooks where you expect to see
%    code added that involves grouping, e.g., starting an environment
%    in the first and closing that environment in the second hook.
%    To give a somewhat contrived example\footnote{There are simpler
%    ways to achieve the same effect.}, suppose there is a package
%    adding the following:
%\begin{verbatim}
%    \AddToHook{env/quote/before}[package-1]{\begin{itshape}}
%    \AddToHook{env/quote/after} [package-1]{\end{itshape}}
%\end{verbatim}
%    As a result, all quotes will be in italics.
%    Now suppose further that another |package-too| makes the quotes
%    also in blue and therefore adds:
%\begin{verbatim}
%    \usepackage{color}
%    \AddToHook{env/quote/before}[package-too]{\begin{color}{blue}}
%    \AddToHook{env/quote/after} [package-too]{\end{color}}
%\end{verbatim}
%    Now if the \hook{env/quote/after} hook would be a normal hook we
%    would get the same execution order in  both hooks, namely:
%\begin{verbatim}
%    package-1, package-too
%\end{verbatim}
%    (or vice versa) and as a result, would get:
%\begin{verbatim}
%    \begin{itshape}\begin{color}{blue} ...
%    \end{itshape}\end{color}
%\end{verbatim}
%    and an error message saying that \verb=\begin{color}= was ended by
%    \verb=\end{itshape}=.
%    With \hook{env/quote/after} declared as a reversed hook the
%    execution order is reversed and so all environments are closed in
%    the correct sequence and \cs{ShowHook} would give us the
%    following output:
%\begin{verbatim}
%    -> The hook 'env/quote/after':
%    > Code chunks:
%    >     package-1 -> \end {itshape}
%    >     package-too -> \end {color}
%    > Document-level (top-level) code (executed first):
%    >     ---
%    > Extra code for next invocation:
%    >     ---
%    > Rules:
%    >     ---
%    > Execution order (after reversal):
%    >     package-too, package-1.
%\end{verbatim}
%
%    If there is a matching default rule (done with
%    \cs{DeclareDefaultHookRule} or with \texttt{??} for the hook
%    name) then this default rule is applied before the reversal so that the order
%    in the reversed hook mirrors the one in the normal hook. However,
%    all rules specific to a hook happen always after the reversal of the
%    execution order, so if you alter the order you will probably have
%    to alter it in both hooks, not just in one, but that depends on
%    the use case.
%
%
%
%
% \subsection{Difference between \enquote{normal} and
%    \enquote{one-time} hooks}
% \label{sec:onetime-hooks}
%
%    When executing a hook a developer has the choice of using
%    either \cs{UseHook} or \cs{UseOneTimeHook} (or their \pkg{expl3}
%    equivalents \cs{hook_use:n} and \cs{hook_use_once:n}).
%    This choice affects how \cs{AddToHook} is handled after the hook
%    has been executed for the first time.
%
%    With normal hooks adding code via \cs{AddToHook} means that the
%    code chunk is added to the hook data structure and then used each time
%    \cs{UseHook} is called.
%
%    With one-time hooks it this is handled slightly differently:
%    After \cs{UseOneTimeHook} has been called, any further attempts to
%    add code to the hook via \cs{AddToHook} will simply execute the
%    \meta{code} immediately.
%
%    This has some consequences one needs to be aware of:
%    \begin{itemize}
%    \item
%
%      If \meta{code} is added to a normal hook after the hook was
%      executed and it is never executed again for one or the other
%      reason, then this new \meta{code} will never be executed.
%
%    \item
%
%      In contrast if that happens with a one-time hook the \meta{code} is
%      executed immediately.
%
%    \end{itemize}
%    In particular this means that construct such as
%\begin{quote}
%    \cs{AddToHook}\verb={myhook}=\\
%    \phantom{\cs{AddToHook}}\verb={= \meta{code-1}
%                                     \cs{AddToHook}\verb={myhook}=\Arg{code-2}
%                                     \meta{code-3} \verb=}=
%\end{quote}
%    works for one-time hooks\footnote{This is sometimes used with
%    \cs{AtBeginDocument} which is why it is supported.} (all three
%    code chunks are executed one after another), but it makes little
%    sense with a normal hook, because with a normal hook the first time
%    \verb=\UseHook{myhook}= is executed it would
%    \begin{itemize}
%    \item
%       execute \meta{code-1},
%    \item
%       then execute \verb=\AddToHook{myhook}{code-2}= which adds the
%    code chunk \meta{code-2} to the hook for use on the next invocation,
%    \item
%       and finally execute \meta{code-3}.
%    \end{itemize}
%    The second time \cs{UseHook} is called it would execute the
%    above and in addition \meta{code-2} as that was added as a code
%    chunk to the hook in the meantime. So each time the hook is used
%    another copy of \meta{code-2} is added and so that code chunk
%    is executed $\meta{\# of invocations} -1$ times.
%
%
%
% \subsection{Generic hooks provided by packages}
% \label{sec:generic-hooks}
%
% \changes{v1.0p}{2021/08/20}{Section on generic hooks added (gh/638)}
%
%    The hook management system also implements a category of hooks
%    that are called \enquote{Generic Hooks}. Normally a hook has to
%    be explicitly declared before it can be used in code. This
%    ensures that different packages are not using the same hook name
%    for unrelated purposes---something that would result in absolute
%    chaos.  However, there are a number of \enquote{standard} hooks
%    where it is unreasonable to declare them beforehand, e.g, each
%    and every command has (in theory) an associated \texttt{before}
%    and \texttt{after} hook. In such cases, i.e., for command,
%    environment or file hooks, they can be used simply by adding code
%    to them with \cs{AddToHook}. For more specialized generic hooks,
%    e.g., those provided by \pkg{babel}, you have to additionally
%    enable them with \cs{ActivateGenericHook} as explained below.
%
%    The generic hooks provided by \LaTeX{} are those for
%    \hook{cmd},
%    \hook{env},
%    \hook{file},
%    \hook{include},
%    \hook{package}, and
%    \hook{class},
%    and all these are available out of the box: you only have to 
%    use \cs{AddToHook} to
%    add code to them, but you don't have to add \cs{UseHook} or
%    \cs{UseOneTimeHook} to your code, because this is already done for
%    you (or, in the case of \hook{cmd} hooks, the command’s code is
%    patched at \verb=\begin{document}=, if necessary).
%
%    However, if you want to provide further generic hooks in your own
%    code, the situation is slightly different. To do this you should 
%    use \cs{UseHook} or \cs{UseOneTimeHook}, but
%    \emph{without declaring the hook} with \cs{NewHook}.  As
%    mentioned earlier, a call to \cs{UseHook} with an undeclared hook
%    name does nothing. So as an additional setup step, you need to
%    explicitly activate your generic hook.  Note that a generic hook
%    produced in this way is always a normal hook.
%
%    For a truly generic hook, with a variable part in the hook name,
%    such upfront activation would be difficult or impossible, because
%    you typically do not know what kind of variable parts may come up
%    in real documents.
%
%    For example, \pkg{babel} provides hooks such as
%    \hook{babel/\meta{language}/afterextras}.  However, language
%    support in \pkg{babel} is often done through external language
%    packages. Thus doing the activation for all languages inside the
%    core \pkg{babel} code is not a viable approach. Instead it needs
%    to be done by each language package (or by the user who wants to
%    use a particular hook).
%
%    Because the hooks are not declared with \cs{NewHook} their names
%    should be carefully chosen to ensure that they are (likely to be)
%    unique. Best practice is to include the package or
%    command name, as was done in the \pkg{babel} example above.
%
%    Generic hooks defined in this way are always normal hooks (i.e.,
%    you can't implement reversed hooks this way). This is a
%    deliberate limitation, because it speeds up the processing
%    considerably.
%
%
% \subsection{Hooks with arguments}
% \label{sec:hook-args}
%
%    Sometimes it is necessary to pass contextual information to a hook,
%    and, for one reason or another, it is not feasible to store such
%    information in macros.  To serve this purpose, hooks can be
%    declared with arguments, so that the programmer can pass along the
%    data necessary for the code in the hook to function properly.
%
%    A hook with arguments works mostly like a regular hook, and most
%    commands that work for regular hooks, also work for hooks that take
%    arguments.  The differences are when the hook is declared
%    (\cs{NewHookWithArguments} is used instead of \cs{NewHook}), then
%    code can be added with both \cs{AddToHook} and
%    \cs{AddToHookWithArguments}, and when the hook is used
%    (\cs{UseHookWithArguments} instead of \cs{UseHook}).
%
%    \medskip
%
%    A hook with arguments must be declared as such (before it is first
%    used, as all regular hooks) using
%    \cs{NewHookWithArguments}\Arg{hook}\Arg{number}.  All code added to
%    that hook can then use \verb|#1| to access the first argument,
%    \verb|#2| to access the second, and so forth up to the number of
%    arguments declared.  However, it is still possible to add code with
%    references to the arguments of a hook that was not yet declared
%    (we will discuss that later).  At their core, hooks are macros, so
%    \TeX's limit of 9~arguments applies, and a low-level \TeX{} error
%    is raised if you try to reference an argument number that doesn't
%    exist.
%
%    \medskip
%
%    To use a hook with arguments, just write
%    \cs{UseHookWithArguments}\Arg{hook}\Arg{number} followed by a
%    braced list of the arguments.  For example, if the hook \hook{test}
%    takes three arguments, write:
%\begin{verbatim}
%    \UseHookWithArguments{test}{3}{arg-1}{arg-2}{arg-3}
%\end{verbatim}
%    then, in the \meta{code} of the hook, all instances of \verb|#1|
%    will be replaced by \verb|arg-1|, \verb|#2| by \verb|arg-2| and so
%    on.  If, at the point of usage, the programmer provides more
%    arguments than the hook is declared to take, the excess arguments
%    are simply ignored by the hook.  Behavior is
%    unpredictable\footnote
%      {The hook \emph{will} take the declared number of arguments, and
%       what will happen depends on what was grabbed, and what the hook
%       code does with its arguments.}
%    if too few arguments are provided.  If the hook isn't declared,
%    \meta{number} arguments are removed from the input stream.
%
%    \medskip
%
%    Adding code to a hook with arguments can be done with
%    \cs{AddToHookWithArguments} as well as with the regular
%    \cs{AddToHook}, to achieve different outcomes.  The main difference
%    when it comes to adding code to a hook, in this case, is firstly
%    the possibility of accessing a hook's arguments, of course, and
%    second, how parameter tokens (\verb|#|$_6$) are treated.
%
%    Using \cs{AddToHook} in a hook that takes arguments will work as it
%    does for all other hooks. This allows a package developer to add
%    arguments to a hook that otherwise had none without having to worry
%    about compatibility.  This means that, for example:
%\begin{verbatim}
%    \AddToHook{test}{\def\foo#1{Hello, #1!}}
%\end{verbatim}
%    will define the same macro \cs[no-index]{foo} regardless if the
%    hook \hook{test} takes arguments or not.
%
%    Using \cs{AddToHookWithArguments} allows the \meta{code} added to
%    access the arguments of the hook with \verb|#1|, \verb|#2|, and so
%    forth, up to the number of the arguments declared in the hook.
%    This means that if one wants to add a \verb|#|$_6$ to the
%    \meta{code} that token must be doubled in the input.  The same
%    definition from above, using \cs{AddToHookWithArguments}, needs to
%    be rewritten:
%\begin{verbatim}
%    \AddToHookWithArguments{test}{\def\foo##1{Hello, ##1!}}
%\end{verbatim}
%
%    Extending the above example to use the hook arguments, we could
%    rewrite something like (now from declaration to usage, to get the
%    whole picture):
%\begin{verbatim}
%    \NewHookWithArguments{test}{1}
%    \AddToHookWithArguments{test}{%
%      \typeout{Defining foo with "#1"}
%      \def\foo##1{Hello, ##1! Some text after: #1}%
%    }
%    \UseHook{test}{Howdy!}
%    \ShowCommand\foo
%\end{verbatim}
%    Running the code above prints in the terminal:
%\begin{verbatim}
%    Defining foo with "Howdy!"
%    > \foo=macro:
%    #1->Hello, #1! Some text after: Howdy!.
%\end{verbatim}
%    Note how \verb|##1| in the call to \cs{AddToHookWithArguments}
%    became \verb|#1|, and the \verb|#1| was replaced by the argument
%    passed to the hook.  Should the hook be used again, with a
%    different argument, the definition would naturally change.
%
%    \bigskip
%
%    It is possible to add code referencing a hook's arguments before
%    such hook is declared and the number of hooks is fixed.  However,
%    if some code is added to the hook, that references more arguments
%    than will be declared for the hook, there will be a low-level
%    \TeX{} error about an \enquote{Illegal parameter number} at the
%    time the hook is declared, which will be hard to track down because
%    at that point \TeX{} can't know whence the offending code came
%    from.  Thus it is important that package writers explicitly
%    document how many arguments (if any) each hook can take, so users
%    of those packages know how many arguments can be referenced, and
%    equally important, what each argument means.
%
% \subsection{Private \LaTeX{} kernel hooks}
%
%    There are a few places where it is absolutely essential for
%    \LaTeX{} to function correctly that code is executed in a precisely
%    defined order. Even that could have been implemented with the
%    hook management (by adding various rules to ensure the
%    appropriate ordering with respect to other code added by
%    packages). However, this makes every document unnecessary
%    slow, because there has to be sorting even though the result is
%    predetermined. Furthermore it forces package writers to
%    unnecessarily add such rules if they add further code to the hook
%    (or break \LaTeX{}).
%
%    For that reason such code is not using the hook management, but
%    instead private kernel commands directly before or after a public
%    hook with the following naming
%    convention: \cs{@kernel@before@\meta{hook}} or
%    \cs{@kernel@after@\meta{hook}}. For example, in
%    \cs{enddocument} you find
%\begin{verbatim}
%   \UseHook{enddocument}%
%   \@kernel@after@enddocument
%\end{verbatim}
%    which means first the user/package-accessible \hook{enddocument}
%    hook is executed and then the internal kernel hook. As their name
%    indicates these kernel commands should not be altered by third-party
%    packages, so please refrain from that in the interest of
%    stability and instead use the public hook next to it.\footnote{As
%    with everything in \TeX{} there is not enforcement of this rule,
%    and by looking at the code it is easy to find out how the kernel
%    adds to them. The main reason of this section is therefore to say
%    \enquote{please don't do that, this is unconfigurable code!}}
%
%
%
% \subsection{Legacy \LaTeXe{} interfaces}
%
% \newcommand\onetimetext{This is a one-time hook, so after it
%   is executed, all further
%   attempts to add code to it will execute such code immediately
%   (see section~\ref{sec:onetime-hooks}).}
%
%  \LaTeXe{} offered a small number of hooks together with commands to
%    add to them. They are listed here and are retained for backwards
%    compatibility.
%
%  With the new hook management, several additional hooks have been added
%    to \LaTeX\ and more will follow. See the next section for what
%    is already available.
%
%
% \begin{function}{\AtBeginDocument}
%   \begin{syntax}
%     \cs{AtBeginDocument} \oarg{label} \Arg{code}
%   \end{syntax}
%   If used without the optional argument \meta{label}, it works essentially
%    like before, i.e., it is adding \meta{code} to the hook
%    \hook{begindocument}
%    (which is executed inside \verb=\begin{document}=).
%    However, all code added this way is labeled with the label
%    \hook{top-level} (see section~\ref{sec:top-level})
%    if done outside of a package or class or with the
%    package/class name if called inside such a file
%    (see section~\ref{sec:default-label}).
%
%    This way one can add code to the hook using
%    \cs{AddToHook} or \cs{AtBeginDocument} using a different label
%    and explicitly order the code chunks as necessary, e.g., run some
%    code before or after another package's code.  When using the
%    optional argument the call is equivalent to running
%    \cs{AddToHook} \texttt{\{begindocument\}} \oarg{label}
%    \Arg{code}.
%
%    \cs{AtBeginDocument} is a wrapper around the \hook{begindocument}
%    hook (see section~\ref{sec:begindocument-hooks}), which is a
%    one-time hook.  As such, after the \hook{begindocument} hook is
%    executed at \verb=\begin{document}= any attempt to add \meta{code}
%    to this hook with \cs{AtBeginDocument} or with \cs{AddToHook} will
%    cause that \meta{code} to execute immediately instead.
%    See section~\ref{sec:onetime-hooks} for more on one-time hooks.
%
%    For important packages with known order requirement we may over
%    time add rules to the kernel (or to those packages) so that they
%    work regardless of the loading-order in the document.
% \end{function}
%
% \begin{function}{\AtEndDocument}
%   \begin{syntax}
%     \cs{AtEndDocument} \oarg{label} \Arg{code}
%   \end{syntax}
%   Like \cs{AtBeginDocument} but for the \hook{enddocument} hook.
% \end{function}
%
%    \bigskip
%
%    The few hooks that existed previously in \LaTeXe{} used internally
%    commands such as \cs{@begindocumenthook} and packages sometimes
%    augmented them directly rather than working through
%    \cs{AtBeginDocument}. For that reason there is currently support
%    for this, that is, if the system detects that such an internal
%    legacy hook command contains code it adds it to the new hook
%    system under the label \texttt{legacy} so that it doesn't get
%    lost.
%
%    However, over time the remaining cases of direct usage need
%    updating because in one of the future release of \LaTeX{} we will
%    turn this legacy support off, as it does unnecessary slow down
%    the processing.
%
%
% \section{\LaTeXe{} commands and environments augmented by hooks}
%
%    In this section we describe the standard hooks that are now
%    offered by \LaTeX{}, or give pointers to other documents in which
%    they are described. This section will grow over time (and
%    perhaps eventually move to usrguide3).  
%
% \subsection{Generic hooks}
% \label{sec:generic}
%
% As stated earlier, with the exception of generic hooks, all hooks must 
% be declared with \cs{NewHook} before they can be used.  
% All generic hooks have names of the form 
% \enquote{\meta{type}/\meta{name}/\meta{position}}, where \meta{type}
% is from the predefined list shown below, and \meta{name} is the variable
% part whose meaning will depend on the \meta{type}. The last component,
% \meta{position}, has more complex possibilities: 
% it can always be |before| or |after|; for |env| hooks, it can also be |begin|
% or |end|; and for |include| hooks it can also be |end|.  Each specific
% hook is documented below, or in \texttt{ltcmdhooks-doc.pdf} or
% \texttt{ltfilehook-doc.pdf}.
%
% The generic hooks provided by \LaTeX{} belong to one of the six types:
% \begin{description}
%   \item[env] Hooks executed before and after environments --
%     \meta{name} is the name of the environment, and available values
%     for \meta{position} are |before|, |begin|, |end|, and |after|;
%   \item[cmd] Hooks added to and executed before and after commands --
%     \meta{name} is the name of the command, and available values
%     for \meta{position} are |before| and |after|;
%   \item[file] Hooks executed before and after reading a file --
%     \meta{name} is the name of the file (with extension), and
%     available values for \meta{position} are |before| and |after|;
%   \item[package] Hooks executed before and after loading packages --
%     \meta{name} is the name of the package, and available values for
%     \meta{position} are |before| and |after|;
%   \item[class] Hooks executed before and after loading classes --
%     \meta{name} is the name of the class, and available values for
%     \meta{position} are |before| and |after|;
%   \item[include] Hooks executed before and after \cs{include}d files -- 
%     \meta{name} is the name of the included file (without the |.tex|
%     extension), and available values for \meta{position} are |before|,
%     |end|, and |after|.
% \end{description}
%
% Each of the hooks above are detailed in the following sections and
% in linked documentation.
% ^^A^^A^^A \pho{Wouldn't it be better to document all hooks here?}
%
% \subsubsection{Generic hooks for all environments}
%
%    Every environment \meta{env} has now four associated hooks coming
%    with it:
%    \begin{description}
%    \item[\hook{env/\meta{env}/before}]
%
%       This hook is executed as part of \cs{begin} as the very first
%       action, in particular prior to starting the environment group.
%       Its scope is therefore not restricted by the environment.
%
%    \item[\hook{env/\meta{env}/begin}]
%
%       This hook is executed as part of \cs{begin} directly in front
%       of the code specific to the environment start (e.g.,
%       the third argument of \cs{NewDocumentEnvironment} and
%       the second argument of \cs{newenvironment}).
%       Its scope is the environment body.
%
%    \item[\hook{env/\meta{env}/end}]
%
%       This hook is executed as part of \cs{end} directly in front of the
%       code specific to the end of the environment (e.g.,
%       the forth argument of \cs{NewDocumentEnvironment} and
%       the third argument of \cs{newenvironment}).
%
%    \item[\hook{env/\meta{env}/after}]
%
%       This hook is executed as part of \cs{end} after the
%       code specific to the environment end and after the environment
%       group has ended.
%       Its scope is therefore not restricted by the environment.
%
%       The hook is implemented as a reversed hook so if two packages
%       add code to \hook{env/\meta{env}/before} and to
%       \hook{env/\meta{env}/after} they can add surrounding
%       environments and the order of closing them happens in the
%       right sequence.
%
%    \end{description}
%
%    Given that these generic hook names involve \texttt{/} as part of
%    their name they would not work if one tries to define an
%    environment using a name that involves a
%    \texttt{/}.\footnote{Officially, \LaTeX{} names for environments
%    should only consist of a sequence of letters, numbers, and the
%    character \texttt{*}, i.e., this is not a new restriction.}
%
%    Generic environment hooks are never one-time hooks even with
%    environments that are supposed to appear only once in a
%    document.\footnote{Thus if one adds code to such hooks after the
%    environment has been processed, it will only be executed if the
%    environment appears again and if that doesn't happen the code
%    will never get executed.}  In contrast to other hooks there is
%    also no need to declare them using \cs{NewHook}.
%
%    The hooks are only executed if \cs{begin}\Arg{env} and
%    \cs{end}\Arg{env} is used. If the environment code is executed
%    via low-level calls to \cs[no-index]{\meta{env}} and \cs[no-index]{end\meta{env}}
%    (e.g., to avoid the environment grouping) they are not
%    available. If you want them available in code using this method,
%    you would need to add them yourself, i.e., write something like
%\begin{verbatim}
%  \UseHook{env/quote/before}\quote
%      ...
%  \endquote\UseHook{env/quote/after}
%\end{verbatim}
%    to add the outer hooks, etc.
%
%
%    Largely for compatibility with existing packages, the following
%    four commands are also available to set the environment hooks; but for
%    new packages we recommend directly using the hook names and
%    \cs{AddToHook}.
%
%
% \begin{function}{\BeforeBeginEnvironment}
%   \begin{syntax}
%     \cs{BeforeBeginEnvironment} \oarg{label} \Arg{env} \Arg{code}
%   \end{syntax}
%   This declaration adds to the \hook{env/\meta{env}/before} hook
%    using the \meta{label}.  If \meta{label} is not given, the
%    \meta{default label} is used (see section~\ref{sec:default-label}).
% \end{function}
%
% \begin{function}{\AtBeginEnvironment}
%   \begin{syntax}
%     \cs{AtBeginEnvironment} \oarg{label} \Arg{env} \Arg{code}
%   \end{syntax}
%   This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/begin} hook.
% \end{function}
%
% \begin{function}{\AtEndEnvironment}
%   \begin{syntax}
%     \cs{AtEndEnvironment} \oarg{label} \Arg{env} \Arg{code}
%   \end{syntax}
%   This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/end} hook.
% \end{function}
%
% \begin{function}{\AfterEndEnvironment}
%   \begin{syntax}
%     \cs{AfterEndEnvironment} \oarg{label} \Arg{env} \Arg{code}
%   \end{syntax}
%   This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/after} hook.
% \end{function}
%
%
% \subsubsection{Generic hooks for commands}
%
%    Similar to environments there are now (at least in theory) two
%    generic hooks available for any \LaTeX{} command. These are
%    \begin{description}
%    \item[\hook{cmd/\meta{name}/before}]
%
%       This hook is executed at the very start of the command
%       execution.
%
%    \item[\hook{cmd/\meta{name}/after}]
%       This hook is executed at the very end of the command body.  It is
%       implemented as a reversed hook.
%    \end{description}
%    In practice there are restrictions and especially the
%    \hook{after} hook works only with a subset of commands. Details
%    about these restrictions are documented in
%    \texttt{ltcmdhooks-doc.pdf} or with code in
%    \texttt{ltcmdhooks-code.pdf}.
%
%
%
%
% \subsubsection{Generic hooks provided by file loading operations}
%
%    There are several hooks added to \LaTeX{}'s process of loading
%    file via its high-level interfaces such as \cs{input},
%    \cs{include}, \cs{usepackage}, \cs{RequirePackage}, etc. These
%    are documented in \texttt{ltfilehook-doc.pdf} or with code in
%    \texttt{ltfilehook-code.pdf}.
%
%
%
% \subsection{Hooks provided by \cs{begin}\texttt{\{document\}}}
% \label{sec:begindocument-hooks}
%
%    Until 2020 \cs{begin}\texttt{\{document\}} offered exactly one
%    hook that one could add to using
%    \cs{AtBeginDocument}. Experiences over the years have shown that
%    this single hook in one place was not enough and as part of
%    adding the general hook management system a number of additional
%    hooks have been added at this point. The places for these hooks have
%    been chosen to provide the same support as offered by external
%    packages, such as \pkg{etoolbox} and others that augmented
%    \cs{document} to gain better control.
%
%    Supported are now the following hooks (all of them one-time hooks):
%    \begin{description}
%
%
%    \item[\hook{begindocument/before}]
%
%      This hook is executed at the very start of \cs{document}, one can
%      think of it as a hook for code at the end of the preamble
%      section and this is how it is used by \pkg{etoolbox}'s
%      \cs{AtEndPreamble}.
%
%      \onetimetext
%
%    \item[\hook{begindocument}]
%
%      This hook is added to by using \cs{AddToHook}\texttt{\{begindocument\}}
%      or by using \cs{AtBeginDocument} and it is
%      executed after the \texttt{.aux} file has been read and most
%      initialization are done, so they can be altered and inspected by
%      the hook code. It is followed by a small number of further
%      initializations that shouldn't be altered and are therefore
%      coming later.
%
%      The hook should not be used to add material for typesetting as
%      we are still in \LaTeX's initialization phase and not in the
%      document body. If such material needs to be added to the document
%      body use the next hook instead.
%
%      \onetimetext
%
%    \item[\hook{begindocument/end}]
%
%      This hook is executed at the end of the \cs{document} code in
%      other words at the beginning of the document body. The only
%      command that follows it is \cs{ignorespaces}.
%
%      \onetimetext
%
%    \end{description}
%    The generic hooks executed by \cs{begin} also exist, i.e.,
%    \hook{env/document/before} and \hook{env/document/begin}, but
%    with this special environment it is better use the dedicated
%    one-time hooks above.
%
%
%
%
% \subsection{Hooks provided by \cs{end}\texttt{\{document\}}}
%
%    \LaTeXe{} has always provided \cs{AtEndDocument} to add code to the
%    \verb=\end{document}=, just in front of the code that
%    is normally executed there. While this was a big improvement over
%    the situation in \LaTeX\,2.09, it was not flexible enough for a
%    number of use cases and so packages, such as \pkg{etoolbox},
%    \pkg{atveryend} and others patched \cs{enddocument} to add
%    additional points where code could be hooked into.
%
%    Patching using packages is always problematical as leads to
%    conflicts (code availability, ordering of patches, incompatible
%    patches, etc.).  For this reason a number of additional hooks
%    have been added to the \cs{enddocument} code to allow packages
%    to add code in various places in a controlled way without the
%    need for overwriting or patching the core code.
%
%    Supported are now the following hooks (all of them one-time hooks):
%    \begin{description}
%
%    \item[\hook{enddocument}]
%
%      The hook associated with \cs{AtEndDocument}. It is immediately
%      called at the beginning of \cs{enddocument}.
%
%      When this hook is executed there may be still unprocessed
%      material (e.g., floats on the deferlist) and the hook may add
%      further material to be typeset. After it, \cs{clearpage} is
%      called to ensure that all such material gets typeset. If there
%      is nothing waiting the \cs{clearpage} has no effect.
%
%      \onetimetext
%
%    \item[\hook{enddocument/afterlastpage}]
%
%      As the name indicates this hook should not receive code that
%      generates material for further pages. It is the right place to
%      do some final housekeeping and possibly write out some
%      information to the \texttt{.aux} file (which is still open at
%      this point to receive data, but since there will be no more pages you
%      need to write to it using \cs{immediate}\cs{write}). It is also the
%      correct place to
%      set up any testing code to be run when the \texttt{.aux} file
%      is re-read in the next step.
%
%
%      After this hook has been executed the \texttt{.aux} file is
%      closed for writing and then read back in to do some tests
%      (e.g., looking for missing references or duplicated labels, etc.).
%
%      \onetimetext
%
%    \item[\hook{enddocument/afteraux}]
%
%      At this point, the \texttt{.aux} file has been reprocessed and so
%      this is a possible place for final checks and display of
%      information to the user. However, for the latter you might
%      prefer the next hook, so that your information is displayed after the
%      (possibly longish) list of files if that got requested via \cs{listfiles}.
%
%      \onetimetext
%
%    \item[\hook{enddocument/info}]
%
%      This hook is meant to receive code that write final information
%      messages to the terminal. It follows immediately after the
%      previous hook (so both could have been combined, but then
%      packages adding further code would always need to also supply
%      an explicit rule to specify where it should go.
%
%      This hook already contains some code added by the kernel (under
%      the labels \texttt{kernel/filelist} and
%      \texttt{kernel/warnings}), namely the list of files when
%      \cs{listfiles} has been used and the warnings for duplicate
%      labels, missing references, font substitutions etc.
%
%      \onetimetext
%
%    \item[\hook{enddocument/end}]
%
%      Finally, this hook is executed just in front of the final call
%      to \cs{@{}@end}.
%
%      \onetimetext % is it even possible to add code after this one?
%
%    \end{description}
%
%
%    There is also the hook \hook{shipout/lastpage}. This hook is
%    executed as part of the last \cs{shipout} in the document to
%    allow package to add final \cs{special}'s to that page. Where
%    this hook is executed in relation to those from the above list
%    can vary from document to document. Furthermore to determine correctly
%    which of the \cs{shipout}s is the last one, \LaTeX{} needs to be run
%    several times, so initially it might get executed on the wrong
%    page. See section~\ref{sec:shipout} for where to find the details.
%
%
%    It is in also possible to use the generic \hook{env/document/end}
%    hook which is executed by \cs{end}, i.e., just in front of the
%    first hook above. Note however that the other generic \cs{end}
%    environment hook, i.e., \hook{env/document/after} will never get
%    executed, because by that time \LaTeX{} has finished the document
%    processing.
%
%
%
%
% \subsection{Hooks provided by \cs{shipout} operations}
% \label{sec:shipout}
%
%    There are several hooks and mechanisms added to \LaTeX{}'s
%    process of generating pages. These are documented in
%    \texttt{ltshipout-doc.pdf} or with code in
%    \texttt{ltshipout-code.pdf}.
%
%
% \subsection{Hooks provided for paragraphs}
% \label{sec:para}
%
%    The paragraph processing has been augmented to include a number of
%    internal and public hooks. These are documented in
%    \texttt{ltpara-doc.pdf} or with code in
%    \texttt{ltpara-code.pdf}.
%
%
%
% \subsection{Hooks provided in NFSS commands}
%
%    In languages that need to support for more than one script in
%    parallel (and thus several sets of fonts, e.g., supporting both Latin and
%    Japanese fonts), NFSS font commands such as \cs{sffamily} need
%    to switch both the Latin family to ``Sans Serif'' and in addition
%    alter a second set of fonts.
%
%    To support this, several NFSS commands have hooks to which 
%    such support can be added.
%    \begin{description}
%
%    \item[\hook{rmfamily}]
%
%      After \cs{rmfamily} has done its initial checks and prepared a
%      font series update, this hook is executed before \cs{selectfont}.
%
%    \item[\hook{sffamily}]
%
%      This is like the \hook{rmfamily} hook, but for the \cs{sffamily} command.
%
%    \item[\hook{ttfamily}]
%
%      This is like the \hook{rmfamily} hook, but for the \cs{ttfamily} command.
%
%    \item[\hook{normalfont}]
%
%      The \cs{normalfont} command resets the font encoding, family, series
%      and shape to their document defaults. It then executes this
%      hook and finally calls \cs{selectfont}.
%
%    \item[\hook{expand@font@defaults}]
%
%      The internal \cs{expand@font@defaults} command expands and
%      saves the current defaults for the metafamilies (rm/sf/tt) and
%      the metaseries (bf/md). If the NFSS machinery has been
%      augmented, e.g., for Chinese or Japanese fonts, then further
%      defaults may need to be set at this point. This can be done in
%      this hook which is executed at the end of this macro.
%
%    \item[\hook{bfseries/defaults}, \hook{bfseries}]
%
%      If the \cs{bfdefault} was explicitly changed by the user, its
%      new value is used to set the bf series defaults for the
%      metafamilies (rm/sf/tt) when \cs{bfseries} is called. The
%      \hook{bfseries/defaults} hook allows further adjustments to be made
%      in this case.  This hook is only executed if such a change is
%      detected. In contrast, the \hook{bfseries} hook is always
%      executed just before \cs{selectfont} is called to change to the
%      new series.
%
%
%    \item[\hook{mdseries/defaults}, \hook{mdseries}]
%
%       These two hooks are like the previous ones but they are in the
%      \cs{mdseries} command.
%
%    \item[\hook{selectfont}]
%
%      This hook is executed inside \cs{selectfont}, after the current
%      values for \textit{encoding}, \textit{family}, \textit{series},
%      \textit{shape}, and \textit{size} are evaluated and the new font
%      is selected (and if necessary loaded). After the hook has
%      executed, NFSS will still do any updates necessary for a new
%      \textit{size} (such as changing the size of \cs{strut}) and any
%      updates necessary to a change in \textit{encoding}.
%
%      This hook is intended for use cases where, in parallel to a
%      change in the main font, some other fonts need to be altered 
%      (e.g., in CJK processing where you may need to deal with several
%      different alphabets).
%
%    \end{description}
%
%
%
% \subsection{Hook provided by the mark mechanism}
%
%    See \texttt{ltmarks-doc.pdf} for details.
%    \begin{description}
%
%    \item[\hook{insertmark}]
%
%      This hook allows for a special setup while \cs{InsertMark}
%      inserts a mark. It is executed in group so local changes only
%      apply to the mark being inserted.
%
%    \end{description}
%
% \MaybeStop{\setlength\IndexMin{200pt}  \PrintIndex  }
%
%
% \section{The Implementation}
%
%
%    \begin{macrocode}
%<@@=hook>
%    \end{macrocode}
%
% \changes{v1.0i}{2021/03/18}{Use \cs{NewModuleRelease}.}
% \changes{v1.0n}{2021/05/24}{Use \cs{msg_...} instead of \cs{__kernel_msg...}}
%
%    \begin{macrocode}
%<*2ekernel|latexrelease>
\ExplSyntaxOn
%<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}
%<latexrelease>                 {The~hook~management~system}
%    \end{macrocode}
%
%  \subsection{Debugging}
%
%  \begin{macro}{\g_@@_debug_bool}
%    Holds the current debugging state.
%    \begin{macrocode}
\bool_new:N \g_@@_debug_bool
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\hook_debug_on:,\hook_debug_off:}
%  \begin{macro}{\@@_debug:n}
%  \begin{macro}{\@@_debug_gset:}
%    Turns debugging on and off by redefining \cs{@@_debug:n}.
%    \begin{macrocode}
\cs_new_eq:NN \@@_debug:n \use_none:n
\cs_new_protected:Npn \hook_debug_on:
  {
    \bool_gset_true:N \g_@@_debug_bool
    \@@_debug_gset:
  }
\cs_new_protected:Npn \hook_debug_off:
  {
    \bool_gset_false:N \g_@@_debug_bool
    \@@_debug_gset:
  }
\cs_new_protected:Npn \@@_debug_gset:
  {
    \cs_gset_protected:Npx \@@_debug:n ##1
      { \bool_if:NT \g_@@_debug_bool {##1} }
  }
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%
%
%
%  \subsection{Borrowing from internals of other kernel modules}
%
%
% \begin{macro}[EXP]{\@@_str_compare:nn}
%   Private copy of \cs{__str_if_eq:nn}
% \InternalDetectionOff
%    \begin{macrocode}
\cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn
%    \end{macrocode}
% \InternalDetectionOn
% \end{macro}
%
%  \subsection{Declarations}
%
%  \begin{macro}{\l_@@_tmpa_bool}
%    Scratch boolean used throughout the package.
%    \begin{macrocode}
\bool_new:N \l_@@_tmpa_bool
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\l_@@_return_tl,\l_@@_tmpa_tl,\l_@@_tmpb_tl}
%    Scratch variables used throughout the package.
%    \begin{macrocode}
\tl_new:N \l_@@_return_tl
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\g_@@_all_seq}
%    In a few places we need a list of all hook names ever defined so
%    we keep track if them in this sequence.
%    \begin{macrocode}
\seq_new:N \g_@@_all_seq
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\l_@@_cur_hook_tl}
%   Stores the name of the hook currently being sorted.
%    \begin{macrocode}
\tl_new:N \l_@@_cur_hook_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_work_prop}
%   A property list holding a copy of the
%   \cs[no-index]{g_@@_\meta{hook}_code_prop} of the hook being sorted
%   to work on, so that changes don't act destructively on the hook data
%   structure.
%    \begin{macrocode}
\prop_new:N \l_@@_work_prop
%    \end{macrocode}
% \end{macro}
%
%  \begin{macro}{\g_@@_used_prop}
%    All hooks that receive code (for use in debugging display).
%    \begin{macrocode}
\prop_new:N \g_@@_used_prop
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\g_@@_hook_curr_name_tl,\g_@@_name_stack_seq}
%   Default label used for hook commands, and a stack to keep track of
%   packages within packages.
%    \begin{macrocode}
\tl_new:N \g_@@_hook_curr_name_tl
\seq_new:N \g_@@_name_stack_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tmp:w}
%   Temporary macro for generic usage.
%    \begin{macrocode}
\cs_new_eq:NN \@@_tmp:w ?
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\c_@@_empty_tl}
% \begin{macro}{\c_@@_nine_parameters_tl}
%   An empty token list, and one containing nine parameters.
% \changes{v1.1a}{2023/04/06}
%         {Add auxiliary token lists (hook-args).}
%    \begin{macrocode}
\tl_const:Nn \c_@@_empty_tl { }
\tl_const:Nn \c_@@_nine_parameters_tl { #1#2#3#4#5#6#7#8#9 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[int]{
%     \tl_gremove_once:Nx,
%     \tl_show:x,
%     \tl_log:x,
%     \tl_set:Ne,
%     \cs_replacement_spec:c,
%     \prop_put:Nne,
%     \str_count:e
%   }
%   Some variants of \pkg{expl3} functions.
%   \fmiinline{should probably be moved to expl3}
%    \begin{macrocode}
\cs_generate_variant:Nn \tl_gremove_once:Nn { Nx }
\cs_generate_variant:Nn \tl_show:n { x }
\cs_generate_variant:Nn \tl_log:n { x }
\cs_generate_variant:Nn \tl_set:Nn { Ne }
\cs_generate_variant:Nn \cs_replacement_spec:N { c }
\cs_generate_variant:Nn \prop_put:Nnn { Nne }
\cs_generate_variant:Nn \str_count:n { e }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\s_@@_mark}
%   Scan mark used for delimited arguments.
%    \begin{macrocode}
\scan_new:N \s_@@_mark
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
%     \@@_use_none_delimit_by_s_mark:w,
%     \@@_use_i_delimit_by_s_mark:nw
%   }
%   Removes tokens until the next \cs{s_@@_mark}.
% \changes{v1.1a}{2023/04/06}
%         {Use standard naming scheme (hook-args).}
%    \begin{macrocode}
\cs_new:Npn \@@_use_none_delimit_by_s_mark:w #1 \s_@@_mark { }
\cs_new:Npn \@@_use_i_delimit_by_s_mark:nw #1 #2 \s_@@_mark {#1}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_set:cn}
%   Private copies of a few \pkg{expl3} functions.  \pkg{l3debug} will
%   only add debugging to the public names, not to these copies, so we
%   don't have to use \cs{debug_suspend:} and \cs{debug_resume:}
%   everywhere.
%
%   Functions like \cs{@@_tl_set:Nn} have to be redefined, rather than
%   copied because in \pkg{expl3} they use
%   \cs[no-index]{__kernel_tl_(g)set:Nx}, which is also patched by
%   \pkg{l3debug}.
% \changes{v1.0h}{2021/01/07}{Manually define some \pkg{l3tl} commands
%     to work around \pkg{expl3} changes}
% \changes{v1.1a}{2023/04/06}
%         {Clean-up unused variants (hook-args).}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_set:cn #1#2
  { \cs_set_nopar:cpx {#1} { \__kernel_exp_not:w {#2} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gset:Nn,\@@_tl_gset:Nx,
%               \@@_tl_gset:cn,\@@_tl_gset:co,\@@_tl_gset:cx}
%   Same as above.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gset:Nn #1#2
  { \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
\cs_new_protected:Npn \@@_tl_gset:Nx #1#2
  { \cs_gset_nopar:Npx #1 {#2} }
\cs_generate_variant:Nn \@@_tl_gset:Nn { c, co }
\cs_generate_variant:Nn \@@_tl_gset:Nx { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
%     \@@_tl_gput_right:Nn,
%     \@@_tl_gput_right:Ne,
%     \@@_tl_gput_right:cn,
%   }
%   Same as above.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gput_right:Nn #1#2
  { \@@_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
\cs_generate_variant:Nn \@@_tl_gput_right:Nn { Ne, cn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gput_left:Nn}
%   Same as above.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gput_left:Nn #1#2
  {
    \@@_tl_gset:Nx #1
      { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gset_eq:NN}
%   Same as above.
%    \begin{macrocode}
\cs_new_eq:NN \@@_tl_gset_eq:NN \tl_gset_eq:NN
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gclear:N,\@@_tl_gclear:c}
%   Same as above.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gclear:N #1
  { \@@_tl_gset_eq:NN #1 \c_empty_tl }
\cs_generate_variant:Nn \@@_tl_gclear:N { c }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Providing new hooks}
%
% \subsubsection{The data structures of a hook}
%
%^^A \DecribeMacro is not set up to use @@ convention so __hook is used explicitly
%
% \DescribeMacro{\g__hook_\meta{hook}_code_prop}
% \DescribeMacro{\__hook\textvisiblespace\meta{hook}}
% \DescribeMacro{\g__hook_\meta{hook}_reversed_tl}
% \DescribeMacro{\g__hook_\meta{hook}_declared_tl}
% \DescribeMacro{\g__hook_\meta{hook}_parameter_tl}
% \DescribeMacro{\__hook_next\textvisiblespace\meta{hook}}
% \DescribeMacro{\__hook_toplevel\textvisiblespace\meta{hook}}
%    Hooks have a name (called \meta{hook} in the description below)
%    and for each hook we have to
%    provide a number of data structures. These are
%    \begin{description}
%    \item[\cs{g_@@_\meta{hook}_code_prop}] A property list holding the code
%    for the hook in separate chunks. The keys are by default the
%    package names that add code to the hook, but it is possible
%    for packages to define other keys.
%
%    \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}]
%    A token list holding the relation between \meta{label1} and
%    \meta{label2} in the \meta{hook}.  The \meta{labels} are lexically
%    (reverse) sorted to ensure that two labels always point to the same
%    token list.  For global rules, the \meta{hook} name is \texttt{??}.
%
%    \item[\cs{@@\textvisiblespace\meta{hook}}] The code that is actually executed
%    when the hook is called in the document is stored in this token
%    list. It is constructed from the code chunks applying the
%    information.
%    This token list is named like that so that in case of an error
%    inside the hook, the reported token list in the error is shorter,
%    and to make it simpler to normalize hook names in
%    \cs{@@_make_name:n}.
%
%    \item[\cs{g_@@_\meta{hook}_reversed_tl}] Some hooks are
%    \enquote{reversed}.  This token list stores a |-| for such hook
%    so that it can be identified.  The |-| character is used because
%    $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed
%    ones.
%
%    \item[{\cs[no-index]{g_@@_\meta{hook}_declared_tl}}] This token
%    list serves as a marker for the hook being officially declared. Its
%    existence is tested to raise an error in case another declaration
%    is attempted.
%
%    \item[{\cs[no-index]{c_@@_\meta{hook}_parameter_tl}}] This token
%    list stores the parameter text for a declared hook (its existence
%    almost completely intersects the token list above), which is used
%    for managing hooks with arguments.
%
%    \item[\cs{@@_toplevel\textvisiblespace\meta{hook}}] This token list stores the code
%    inserted in the hook from the user's document, in the |top-level|
%    label.  This label is special, and doesn't participate in sorting.
%    Instead, all code is appended to it and executed after (or before,
%    if the hook is reversed) the normal
%    hook code, but before the |next| code chunk.
%
%    \item[\cs{@@_next\textvisiblespace\meta{hook}}] Finally there is
%    extra code (normally empty) that is used on the next invocation
%    of the hook (and then deleted). This can be used to define some
%    special behavior for a single occasion from within the document.
%    This token list follows the same naming scheme than the main
%    \cs{@@\textvisiblespace\meta{hook}} token list.  It is called
%    \cs{@@_next\textvisiblespace\meta{hook}} rather than
%    \cs[no-index]{@@\textvisiblespace next_\meta{hook}} because
%    otherwise a hook whose name is |next_|\meta{hook} would clash
%    with the next code-token list of the hook called \meta{hook}.
%
%    \end{description}
%
%
% \subsubsection{On the existence of hooks}
% \label{sec:existence}
%
%    A hook may be in different states of existence. Here we give an
%    overview of the internal commands to set up hooks and explain how the
%    different states are distinguished. The actual implementation
%    then follows in subsequent sections.
%
%    One problem we have to solve is that we need to be able to add
%    code to hooks (e.g., with \cs{AddToHook}) even if that code has
%    not yet been declared. For example, one package needs to write
%    into a hook of another package, but that package may not get
%    loaded, or is loaded only later. Another problem is that most hooks, 
%    but not the generic hooks, require a declaration.
%
%    We therefore distinguish the following states for a hook, which
%    are managed by four different tests: structure existence
%    (\cs{@@_if_structure_exist:nTF}), creation
%    (\cs{@@_if_usable:nTF}), declaration (\cs{@@_if_declared:nTF})
%    and disabled or not (\cs{@@_if_disabled:nTF})
%    \begin{description}
%    \setlist[itemize]{leftmargin=5cm,format=\cs}
%    \item[not existing]
%
%       Nothing is known about the hook so far. This state can be
%       detected with \cs{@@_if_structure_exist:nTF}
%       (which uses the  false branch).
%
%       In this state the hook can be declared, disabled, rules can be
%       defined or code could be added to it, but it is not possible
%       to use the hook (with \cs{UseHook}).

%    \item[basic data structure set up]
%
%       A hook is this state when its basic data structure has been
%       set up (using \cs{@@_init_structure:n}). The data structure setup happens
%       automatically when commands such as \cs{AddToHook}  are used
%       and the hook is at that point in state \enquote{not existing}.
%
%       In this state the four tests give the following results:
%     \begin{itemize}
%       \item [@@_if_structure_exist:nTF]      returns |true|.
%       \item [@@_if_usable:nTF]   returns |false|.
%       \item [@@_if_declared:nTF]  returns |false|.
%       \item [@@_if_disabled:nTF]  returns |false|.
%     \end{itemize}
%
%       The allowed actions are the same as in the \enquote{not
%       existing} state.
%
%    \item[declared]
%
%       A hook is in this state it is not disabled and was explicitly declared (e.g.,
%       with \cs{NewHook}). In this case the four tests give the
%       following results:
%     \begin{itemize}
%       \item [@@_if_structure_exist:nTF]      returns |true|.
%       \item [@@_if_usable:nTF]   returns |true|.
%       \item [@@_if_declared:nTF]  returns |true|.
%       \item [@@_if_disabled:nTF]  returns |false|.
%     \end{itemize}
%
%    \item[usable]
%
%       A hook is in this state if it is not disabled, was not
%       explicitly declared but nevertheless is allowed to be used
%       (with \cs{UseHook} or \cs{hook_use:n}). This state is only
%       possible for generic hooks as they do not need to be
%       declared. Therefore such hooks move directly from state
%       \enquote{not existing} to \enquote{usable} the moment a
%       declaration such as \cs{AddToHook} wants to add to the hook
%       data structure.  In this state the tests give the following
%       results:
%     \begin{itemize}
%       \item [@@_if_structure_exist:nTF]      returns |true|.
%       \item [@@_if_usable:nTF]   returns |true|.
%       \item [@@_if_declared:nTF]  returns |false|.
%       \item [@@_if_disabled:nTF]  returns |false|.
%     \end{itemize}
%
%
%    \item[disabled]
%
%       A generic hook in any state is moved to this state when
%       \cs{DisableGenericHook} is used. This changes the tests to give the
%       following results:
%     \begin{itemize}
%       \item [@@_if_structure_exist:nTF]      \emph{unchanged}.
%       \item [@@_if_usable:nTF]   returns |false|.
%       \item [@@_if_declared:nTF]  returns |true|.
%       \item [@@_if_disabled:nTF]  returns |true|.
%     \end{itemize}
%       The  structure test is unchanged (if the hook was unknown before it is
%       |false|, otherwise |true|). The usable test returns |false| so that
%       any \cs{UseHook} will bypass the hook from now on. The
%       declared test returns true so that any further \cs{NewHook}
%       generates an error and the disabled test returns true so that
%       \cs{AddToHook} can return an error.
% \fmiinline{maybe it should do this only after begin document?}
%
%    \end{description}
%
%
%
%
% \subsubsection{Setting hooks up}
%
%
%  \begin{macro}{
%    \hook_new:n,
%    \hook_new_with_args:nn
%  }
%  \begin{macro}{\@@_new:nn}
%    The \cs{hook_new:n} declaration declares a new hook and expects
%    the hook \meta{name} as its argument, e.g.,
%    \hook{begindocument}.
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_new_with_args:nn} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_with_args:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_new:n #1
  { \@@_normalize_hook_args:Nn \@@_new:nn {#1} { 0 } }
\cs_new_protected:Npn \hook_new_with_args:nn #1 #2
  { \@@_normalize_hook_args:Nn \@@_new:nn {#1} {#2} }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_new:nn #1 #2
  {
%    \end{macrocode}
%   We check if the hook was already \emph{explicitly} declared with
%   \cs{hook_new:n}, and if it already exists we complain, otherwise set
%   the \enquote{created} flag for the hook so that it errors next time
%   \cs{hook_new:n} is used.
% \changes{v1.1d}{2023/05/21}
%         {Changes to allow support arguments in cmd hooks (cmd-args).}
%    \begin{macrocode}
    \@@_if_declared:nTF {#1}
      { \msg_error:nnn { hooks } { exists } {#1} }
      {
        \tl_new:c { g_@@_#1_declared_tl }
        \cs_undefine:c { @@~#1 }
        \cs_undefine:c { c_@@_#1_parameter_tl }
        \@@_make_usable:nn {#1} {#2}
%    \end{macrocode}
%   In case there is already code in a hook, but it's undeclared, run
%   \cs{@@_update_hook_code:n} to make it ready to be executed (see test
%   \texttt{lthooks-034}).
% \changes{v1.1a}{2023/04/06}
%         {Update hook code after declaring.}
%    \begin{macrocode}
        \@@_update_hook_code:n {#1}
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_with_args:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \hook_new:n #1
%<latexrelease>  { \@@_normalize_hook_args:Nn \@@_new:n {#1} }
%<latexrelease>\cs_undefine:N \@@_new:nn
%<latexrelease>\cs_gset_protected:Npn \@@_new:n #1
%<latexrelease>  {
%<latexrelease>    \@@_if_declared:nTF {#1}
%<latexrelease>      { \msg_error:nnn { hooks } { exists } {#1} }
%<latexrelease>      {
%<latexrelease>        \tl_new:c { g_@@_#1_declared_tl }
%<latexrelease>        \@@_make_usable:n {#1}
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn \hook_new_with_args:nn #1 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
%
%  \begin{macro}{\@@_make_usable:nn}
%
%    This initializes all hook data structures for the hook but if
%    used on its own doesn't mark the hook as declared (as
%    \cs{hook_new:n} does, so a later \cs{hook_new:n} on that hook will
%    not result in an error.  This command is internally used by
%    \cs{hook_gput_code:nnn} when adding code to a generic hook.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_make_usable:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_make_usable:nn #1 #2
  {
%    \end{macrocode}
%   Now we check if the hook's data structure can be safely created
%   without \pkg{expl3} raising errors, then
%   we add the hook name to the list of all hooks and
%   allocate the necessary data structures for the new hook,
%   otherwise just do nothing.
%    \begin{macrocode}
    \@@_if_usable:nF {#1}
      {
        \seq_gput_right:Nn \g_@@_all_seq {#1}
%    \end{macrocode}
%    Here we'll define the \cs[no-index]{c_@@_\meta{hook}_parameter_tl}
%    to hold a run of parameters up to the number of arguments of the
%    hook (\verb|#2|).
%    \begin{macrocode}
        \__kernel_cs_parm_from_arg_count:nnF
          { \tl_const:cn { c_@@_#1_parameter_tl } } {#2}
          {
            \msg_error:nnnn { hooks } { too-many-args } {#1} {#2}
            \tl_const:cx { c_@@_#1_parameter_tl }
              { \exp_not:V \c_@@_nine_parameters_tl }
          }
%    \end{macrocode}
%    After that, use \cs{@@_normalise_cs_args:nn} to correct the number
%    of parameters of the macros
%    \cs[no-index]{@@_toplevel\textvisiblespace\meta{hook}} and
%    \cs[no-index]{@@_next\textvisiblespace\meta{hook}}.  We need to be
%    able to add
%    code with arguments to a hook without prior knowledge of the number
%    of arguments of that hook, so \pkg{lthooks} assumes~9 until the
%    hook is properly declared and the number of arguments is known.
%    \cs{@@_normalise_cs_args:nn} does the normalization by using the
%    \cs[no-index]{c_@@_\meta{hook}_parameter_tl} defined just above.
%    \begin{macrocode}
        \@@_normalise_cs_args:nn { _toplevel } {#1}
        \@@_normalise_cs_args:nn { _next } {#1}
%    \end{macrocode}
%    This is only used by the actual code of the current hook, so
%    declare it normally:
%    \begin{macrocode}
        \@@_code_gset:nn {#1} { }
%    \end{macrocode}
%    Now ensure that the base data structure for the hook exists:
%    \begin{macrocode}
        \@@_init_structure:n {#1}
%    \end{macrocode}
%    The call to \cs{@@_normalise_code_pool:n} will correct any improper
%    reference to arguments that don't exist in the hook, raising a
%    low-level \TeX{} error and doubling the offending parameter tokens.
%    It has to be done after \cs{@@_init_structure:n} because it
%    operates on \cs[no-index]{g_@@_\meta{hook}_code_prop}.
%    \begin{macrocode}
        \@@_normalise_code_pool:n {#1}
%    \end{macrocode}
%    The \cs{g_@@_\meta{hook}_labels_clist} holds the sorted list of
%    labels (once it got sorted). This is used only for debugging.
%    These are defined conditionally, in case \cs{@@_make_usable:nn}
%    is being used to redefine a hook.
% \changes{v1.1d}{2023/05/21}
%         {Changes to allow support arguments in cmd hooks (cmd-args).}
%    \begin{macrocode}
        \clist_if_exist:cF { g_@@_#1_labels_clist }
          {
            \clist_new:c { g_@@_#1_labels_clist }
%    \end{macrocode}
%    Some hooks should reverse the default order of code chunks. To
%    signal this we have a token list which is empty for normal hooks
%    and contains a \verb=-= for reversed hooks.
%    \begin{macrocode}
            \tl_new:c { g_@@_#1_reversed_tl }
          }
%    \end{macrocode}
%    The above is all in L3 convention, but we also provide an
%    interface to legacy \LaTeXe{} hooks of the form \cs{@...hook},
%    e.g., \cs{@begindocumenthook}.
%    there have been a few of them and they have been added to
%    using \cs{g@addto@macro}. If there exists such a macro matching
%    the name of the new hook, i.e.,
%    \verb+\@+\meta{hook-name}\texttt{hook} and it is not empty then
%    we add its contents as a code chunk under the label \texttt{legacy}.
%    \begin{quote}
%       \textbf{Warning: this support will vanish in future releases!}
%    \end{quote}
%
%    \begin{macrocode}
        \@@_include_legacy_code_chunk:n {#1}
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_make_usable:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_make_usable:nn
%<latexrelease>\cs_gset_protected:Npn \@@_make_usable:n #1
%<latexrelease>  {
%<latexrelease>    \tl_if_exist:cF { @@~#1 }
%<latexrelease>      {
%<latexrelease>        \seq_gput_right:Nn \g_@@_all_seq {#1}
%<latexrelease>        \tl_new:c { @@~#1 }
%<latexrelease>        \@@_init_structure:n {#1}
%<latexrelease>        \clist_new:c { g_@@_#1_labels_clist }
%<latexrelease>        \tl_new:c { g_@@_#1_reversed_tl }
%<latexrelease>        \@@_include_legacy_code_chunk:n {#1}
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
% \begin{macro}{\@@_init_structure:n}
%    This function declares the basic data structures for a hook
%    without explicit declaring the hook itself.  This is needed to
%    allow adding to undeclared hooks.  Here it is unnecessary to
%    check whether all variables exist, since all three are declared
%    at the same time (either all of them exist, or none).
%
%    It creates the hook code pool
%    (\cs[no-index]{g_@@_\meta{hook}_code_prop}) and the |top-level|
%    and |next| token lists.  A hook is initialized with
%    \cs{@@_init_structure:n} the first time anything is added to it.
%    Initializing a hook just with \cs{@@_init_structure:n} will not
%    make it usable with \cs{hook_use:n}.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_init_structure:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_init_structure:n #1
  {
    \@@_if_structure_exist:nF {#1}
      {
        \prop_new:c { g_@@_#1_code_prop }
        \@@_toplevel_gset:nn {#1} { }
        \@@_next_gset:nn {#1} { }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_init_structure:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_init_structure:n #1
%<latexrelease>  {
%<latexrelease>    \@@_if_structure_exist:nF {#1}
%<latexrelease>      {
%<latexrelease>        \prop_new:c { g_@@_#1_code_prop }
%<latexrelease>        \tl_new:c { @@_toplevel~#1 }
%<latexrelease>        \tl_new:c { @@_next~#1 }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{
%    \hook_new_reversed:n,
%    \hook_new_reversed_with_args:nn
%  }
%  \begin{macro}{\@@_new_reversed:nn}
%
%    Declare a new hook. The default ordering of code chunks is
%    reversed, signaled by setting the token list to a minus sign.
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_new_reversed_with_args:nn} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_reversed_with_args:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_new_reversed:n #1
  { \@@_normalize_hook_args:Nn \@@_new_reversed:nn {#1} { 0 } }
\cs_new_protected:Npn \hook_new_reversed_with_args:nn #1 #2
  { \@@_normalize_hook_args:Nn \@@_new_reversed:nn {#1} {#2} }
\cs_new_protected:Npn \@@_new_reversed:nn #1 #2
  {
    \@@_if_declared:nTF {#1}
      { \msg_error:nnn { hooks } { exists } {#1} }
      {
        \@@_new:nn {#1} {#2}
        \tl_gset:cn { g_@@_#1_reversed_tl } { - }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_reversed_with_args:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \hook_new_reversed:n #1
%<latexrelease>  { \@@_normalize_hook_args:Nn \@@_new_reversed:n {#1} }
%<latexrelease>\cs_undefine:N \@@_new_reversed:nn
%<latexrelease>\cs_gset_protected:Npn \@@_new_reversed:n #1
%<latexrelease>  {
%<latexrelease>    \@@_new:n {#1}
%<latexrelease>    \tl_gset:cn { g_@@_#1_reversed_tl } { - }
%<latexrelease>  }
%<latexrelease>\cs_undefine:N \@@_new_reversed:nn
%<latexrelease>\cs_gset_protected:Npn \hook_new_reversed_with_args:nn #1 #2 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%
%  \begin{macro}{\hook_new_pair:nn,\hook_new_pair_with_args:nnn}
%    A shorthand for declaring a normal and a (matching) reversed hook in one go.
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_new_pair_with_args:nnn} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_pair_with_args:nnn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_new_pair:nn #1#2
  { \@@_normalize_hook_args:Nnn \@@_new_pair:nnn {#1} {#2} { 0 } }
\cs_new_protected:Npn \hook_new_pair_with_args:nnn #1#2#3
  { \@@_normalize_hook_args:Nnn \@@_new_pair:nnn {#1} {#2} {#3} }
\cs_new_protected:Npn \@@_new_pair:nnn #1 #2 #3
  {
    \@@_if_declared:nTF {#1}
      { \msg_error:nnn { hooks } { exists } {#1} }
      {
        \@@_if_declared:nTF {#2}
          { \msg_error:nnn { hooks } { exists } {#2} }
          {
            \@@_new:nn {#1} {#3}
            \@@_new_reversed:nn {#2} {#3}
          }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_pair_with_args:nnn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \hook_new_pair:nn #1#2
%<latexrelease>  {
%<latexrelease>    \hook_new:n {#1}
%<latexrelease>    \hook_new_reversed:n {#2}
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn \hook_new_pair_with_args:nnn #1#2#3
%<latexrelease>  { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
% \begin{macro}{\@@_include_legacy_code_chunk:n}
%    The \LaTeX{} legacy concept for hooks uses with hooks the
%    following naming scheme in the code: \cs{@...hook}.
%
%    If this macro is not empty we add it under the label
%    \texttt{legacy} to the current hook and then empty it globally.
%    This way packages or classes directly manipulating commands such
%    as \cs{@begindocumenthook} still get their hook data added.
%    \begin{quote}
%       \textbf{Warning: this support will vanish in future releases!}
%    \end{quote}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_include_legacy_code_chunk:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_include_legacy_code_chunk:n #1
  {
%    \end{macrocode}
%    If the macro doesn't exist (which is the usual case) then nothing
%    needs to be done.
%    \begin{macrocode}
    \tl_if_exist:cT { @#1hook }
      {
%    \end{macrocode}
%    Of course if the legacy hook exists but is empty, there is no need
%    to add anything under \texttt{legacy} the legacy label.
%    \begin{macrocode}
        \tl_if_empty:cF { @#1hook }
          {
%    \end{macrocode}
%    Here we set \cs{@@_replacing_args_false:} because no legacy code
%    will reference hook arguments.
% \changes{v1.1b}{2023/04/16}
%         {\cs{__hook_replacing_args_false:} in
%          \cs{__hook_include_legacy_code_chunk:n}}
%    \begin{macrocode}
            \@@_replacing_args_false:
            \use:e
              {
                \@@_hook_gput_code_do:nnn {#1} { legacy }
                  { \exp_not:v { @#1hook } }
              }
            \@@_replacing_args_reset:
%    \end{macrocode}
%    Once added to the hook, we need to clear it otherwise it might
%    get added again  later if the hook data gets updated.
%    \begin{macrocode}
            \@@_tl_gclear:c { @#1hook }
          }
      }
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_include_legacy_code_chunk:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_include_legacy_code_chunk:n #1
%<latexrelease>  {
%<latexrelease>    \tl_if_exist:cT { @#1hook }
%<latexrelease>      {
%<latexrelease>        \tl_if_empty:cF { @#1hook }
%<latexrelease>          {
%<latexrelease>            \exp_args:Nnnv \@@_hook_gput_code_do:nnn
%<latexrelease>              {#1} { legacy } { @#1hook }
%<latexrelease>            \@@_tl_gclear:c { @#1hook }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsubsection{Disabling and providing hooks}
%
% \changes{v1.0p}{2021/08/20}{Renames of generic hook commands (gh/638)}
%
% \begin{macro}{\hook_disable_generic:n}
% \begin{macro}{\@@_disable:n}
% \begin{macro}[pTF]{\@@_if_disabled:n}
%
%    Disables a hook by creating its
%    \cs[no-index]{g_@@_\meta{hook}_declared_tl} so that the hook
%    errors when used with \cs{hook_new:n}, then it undefines
%    \cs{@@\textvisiblespace\meta{hook}} so that it may not be executed.
%
%    This does not clear any code that may be already stored in the
%    hook's structure, but doesn't allow adding more code.
%    \cs{@@_if_disabled:nTF} uses that specific combination to check
%    if the hook is disabled.
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}{\hook_disable_generic:n}
%<latexrelease>                 {Disable~hooks}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \hook_disable_generic:n #1
  { \@@_normalize_hook_args:Nn \@@_disable:n {#1} }
\cs_new_protected:Npn \@@_disable:n #1
  {
    \tl_gclear_new:c { g_@@_#1_declared_tl }
    \cs_undefine:c { @@~#1 }
  }
\prg_new_conditional:Npnn \@@_if_disabled:n #1 { p, T, F, TF }
  {
    \bool_lazy_and:nnTF
        { \tl_if_exist_p:c { g_@@_#1_declared_tl } }
        { ! \cs_if_exist_p:c { @@~#1 } }
      { \prg_return_true: }
      { \prg_return_false: }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_disable_generic:n}
%<latexrelease>                 {Disable~hooks}
%<latexrelease>
%<latexrelease>\cs_new_protected:Npn \hook_disable_generic:n #1 {}
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hook_activate_generic:n}
% \begin{macro}{\@@_activate_generic:n}
%    The \cs{hook_activate_generic:n} declaration declares a new hook if it
%    wasn't declared already, in which case it only checks that the
%    already existing hook is not a reversed hook.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_activate_generic:n}
%<latexrelease>                 {Providing~hooks}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \hook_activate_generic:n #1
  { \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} {   } }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_activate_generic:nn #1 #2
  {
%    \end{macrocode}
%    If the hook to be activated was disabled we warn (for now --- this
%    may change).
%    \begin{macrocode}
    \@@_if_disabled:nTF {#1}
      { \msg_warning:nnn { hooks } { activate-disabled } {#1} }
%    \end{macrocode}
%    Otherwise we check if the hook is not declared, and if it isn't,
%    figure out if it's reversed or not, then declare it accordingly.
%    \begin{macrocode}
      {
        \@@_if_declared:nF {#1}
          {
            \tl_new:c { g_@@_#1_declared_tl }
            \@@_make_usable:nn {#1} { 0 }
            \tl_gset:cx { g_@@_#1_reversed_tl }
              { \@@_if_generic_reversed:nT {#1} { - } }
%    \end{macrocode}
%    Reflect that we have activated the generic hook and set its
%    execution code.
% \changes{v1.0v}{2022/06/15}{Ensure that a newly activated generic hook
%               gets its execution code set}
%    \begin{macrocode}
            \@@_update_hook_code:n {#1}
          }
      }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}{\hook_activate_generic:n}
%<latexrelease>                 {Providing~hooks}
%<latexrelease>\cs_gset_protected:Npn \@@_activate_generic:nn #1 #2
%<latexrelease>  {
%<latexrelease>    \@@_if_disabled:nTF {#1}
%<latexrelease>      { \msg_warning:nnn { hooks } { activate-disabled } {#1} }
%<latexrelease>      {
%<latexrelease>        \@@_if_declared:nF {#1}
%<latexrelease>          {
%<latexrelease>            \tl_new:c { g_@@_#1_declared_tl }
%<latexrelease>            \@@_make_usable:n {#1}
%<latexrelease>            \tl_gset:cx { g_@@_#1_reversed_tl }
%<latexrelease>              { \@@_if_generic_reversed:nT {#1} { - } }
%<latexrelease>            \@@_update_hook_code:n {#1}
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_activate_generic:n}
%<latexrelease>                 {Providing~hooks}
%<latexrelease>\cs_gset_protected:Npn \hook_activate_generic:n #1 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \subsection{Parsing a label}
%
% \begin{macro}[EXP]{\@@_parse_label_default:nN}
%   This macro checks if a label was given (not \cs{c_novalue_tl}), and
%   if so, tries to parse the label looking for a leading \verb|.| to
%   replace by \cs{@@_currname_or_default:}.
%   |#2| is a boolean representing if |#1| is a label name.
% \changes{v1.1i}{2024/08/09}
%         {Distinguish between empty label and empty hook (gh/1423)}
%    \begin{macrocode}
\cs_new:Npn \@@_parse_label_default:nN #1#2
  {
    \tl_if_novalue:nTF {#1}
      { \@@_currname_or_default: }
      { \tl_trim_spaces_apply:nN {#1} \@@_parse_dot_label:nN #2 }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_parse_dot_label:nN}
% \changes{v1.1i}{2024/08/09}
%         {Distinguish between empty label and empty hook (gh/1423)}
% \begin{macro}[EXP]{
%     \@@_parse_dot_label:w,
%     \@@_parse_dot_label_cleanup:w,
%     \@@_parse_dot_label_aux:w
%   }
%   Start by checking if the label is empty, which raises an error, and
%   uses the fallback value.  If not,
%   split the label at a \verb|./|, if any, and check if no tokens are
%   before the \verb|./|, or if the only character is a \verb|.|.
%   If these requirements are fulfilled, the leading
%   \verb|.| is replaced with \cs{@@_currname_or_default:}.  Otherwise
%   the label is returned unchanged.
%   |#2| is a boolean representing if |#1| is a label name.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_dot_label:nN #1#2
  {
    \tl_if_empty:nTF {#1}
      {
        \bool_if:NTF #2
          { \msg_expandable_error:nn { hooks } { empty-label } }
          { \msg_expandable_error:nn { hooks } { empty-hook } }
        \@@_currname_or_default:
      }
      {
        \str_if_eq:nnTF {#1} { . }
          { \@@_currname_or_default: }
          { \@@_parse_dot_label:w #1 ./ \s_@@_mark }
      }
  }
\cs_new:Npn \@@_parse_dot_label:w #1 ./ #2 \s_@@_mark
  {
    \tl_if_empty:nTF {#1}
      { \@@_parse_dot_label_aux:w #2 \s_@@_mark }
      {
        \tl_if_empty:nTF {#2}
          { \@@_make_name:n {#1} }
          { \@@_parse_dot_label_cleanup:w #1 ./ #2 \s_@@_mark }
      }
  }
\cs_new:Npn \@@_parse_dot_label_cleanup:w #1 ./ \s_@@_mark {#1}
\cs_new:Npn \@@_parse_dot_label_aux:w #1 ./ \s_@@_mark
  { \@@_currname_or_default: / \@@_make_name:n {#1} }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_currname_or_default:}
%   This uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise it tries
%   \cs{@currname}.  If neither is set, it raises an error and uses the
%   fallback value \verb|label-missing|.
%    \begin{macrocode}
\cs_new:Npn \@@_currname_or_default:
  {
    \tl_if_empty:NTF \g_@@_hook_curr_name_tl
      {
        \tl_if_empty:NTF \@currname
          {
            \msg_expandable_error:nnn { latex2e } { should-not-happen }
              { Empty~default~label. }
            \@@_make_name:n { label-missing }
          }
          { \@currname }
      }
      { \g_@@_hook_curr_name_tl }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_make_name:n,\@@_make_name:w}
%   This provides a standard sanitization of a hook's name.
%   It uses \cs{cs:w} to build a control sequence out of the hook name,
%   then uses \cs{cs_to_str:N} to get the string representation of that,
%   without the escape character.  \cs{cs:w}-based expansion is used
%   instead of |e|-based because Unicode characters don't behave well
%   inside \cs{expanded}.  The macro adds the \cs[no-index]{@@\textvisiblespace} prefix to the
%   hook name to reuse the hook's code token list to build the csname
%   and avoid leaving \enquote{public} control sequences defined
%   (as~\cs[no-index]{relax}) in TeX's memory.
%    \begin{macrocode}
\cs_new:Npn \@@_make_name:n #1
  {
    \exp_after:wN \exp_after:wN \exp_after:wN \@@_make_name:w
    \exp_after:wN \token_to_str:N \cs:w @@~ #1 \cs_end:
  }
\exp_last_unbraced:NNNNo
\cs_new:Npn \@@_make_name:w #1 \tl_to_str:n { @@~ } { }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_normalize_hook_args:Nn}
% \begin{macro}{\@@_normalize_hook_args:Nnn}
% \begin{macro}{\@@_normalize_hook_rule_args:Nnnnn}
% \begin{macro}{\@@_normalize_hook_args_aux:Nn}
%   This is the standard route for normalizing hook and label arguments.  The main
%   macro does the entire operation within a group so that csnames made
%   by \cs{@@_make_name:n} are wiped off before continuing.  This means
%   that this function cannot be used for \cs{hook_use:n}!
% \changes{v1.1i}{2024/08/09}
%         {Distinguish between empty label and empty hook (gh/1423)}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_normalize_hook_args_aux:Nn #1 #2
  {
    \group_begin:
    \use:e
      {
        \group_end:
        \exp_not:N #1 #2
      }
  }
\cs_new_protected:Npn \@@_normalize_hook_args:Nn #1 #2
  {
    \@@_normalize_hook_args_aux:Nn #1
      { { \@@_parse_label_default:nN {#2} \c_false_bool } }
  }
\cs_new_protected:Npn \@@_normalize_hook_args:Nnn #1 #2 #3
  {
    \@@_normalize_hook_args_aux:Nn #1
      {
        { \@@_parse_label_default:nN {#2} \c_false_bool }
        { \@@_parse_label_default:nN {#3} \c_true_bool }
      }
  }
\cs_new_protected:Npn \@@_normalize_hook_rule_args:Nnnnn #1 #2 #3 #4 #5
  {
    \@@_normalize_hook_args_aux:Nn #1
      {
        { \@@_parse_label_default:nN {#2} \c_false_bool }
        { \@@_parse_label_default:nN {#3} \c_true_bool }
        { \tl_trim_spaces:n {#4} }
        { \@@_parse_label_default:nN {#5} \c_true_bool }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n}
% \begin{macro}{\@@_curr_name_pop:}
% \begin{macro}{\@@_end_document_label_check:}
%   The token list \cs{g_@@_hook_curr_name_tl} stores the name of the
%   current package/file to be used as the default label in hooks.
%   Providing a consistent interface is tricky because packages can
%   be loaded within packages, and some packages may not use
%   \cs{SetDefaultHookLabel} to change the default label (in which
%   case \cs{@currname} is used).
%
%   To pull that one off, we keep a stack that contains the default
%   label for each level of input.  The bottom of the stack contains the
%   default label for the |top-level| (this stack should never go
%   empty). If we're building the format, set the default label to be
%   |top-level|:
%    \begin{macrocode}
\tl_gset:Nn \g_@@_hook_curr_name_tl { top-level }
%    \end{macrocode}
%
%   Then, in case we're in \pkg{latexrelease} we push something on
%   the stack to support roll forward.  But in some rare cases,
%   \pkg{latexrelease} may be loaded inside another package (notably
%   \pkg{platexrelease}), so we'll first push the |top-level| entry:
%   \changes{v1.0i}{2021/03/18}
%           {Only add \texttt{top-level} if not already there.}
%    \begin{macrocode}
%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq
%<latexrelease>  { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } }
%    \end{macrocode}
%   then we dissect the \cs{@currnamestack}, adding \cs{@currname} to
%   the stack:
% \changes{v1.0f}{2020/11/24}{Support for roll forward (gh/434)}
%    \begin{macrocode}
%<latexrelease>\cs_set_protected:Npn \@@_tmp:w #1 #2 #3
%<latexrelease>  {
%<latexrelease>    \quark_if_recursion_tail_stop:n {#1}
%<latexrelease>    \seq_gput_right:Nn \g_@@_name_stack_seq {#1}
%<latexrelease>    \@@_tmp:w
%<latexrelease>  }
%<latexrelease>\exp_after:wN \@@_tmp:w \@currnamestack
%<latexrelease>  \q_recursion_tail \q_recursion_tail
%<latexrelease>  \q_recursion_tail \q_recursion_stop
%    \end{macrocode}
%   and finally set the default label to be the \cs{@currname}:
%   \changes{v1.0i}{2021/03/18}
%           {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.}
%    \begin{macrocode}
%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl
%    \end{macrocode}
%
%   Two commands keep track of the stack: when a file is input,
%   \cs{@@_curr_name_push:n} pushes the current default label onto the
%   stack and sets the new default label (all in one go):
%    \begin{macrocode}
\cs_new_protected:Npn \@@_curr_name_push:n #1
  { \exp_args:Nx \@@_curr_name_push_aux:n { \@@_make_name:n {#1} } }
\cs_new_protected:Npn \@@_curr_name_push_aux:n #1
  {
    \tl_if_blank:nTF {#1}
      { \msg_error:nn { hooks } { no-default-label } }
      {
        \str_if_eq:nnTF {#1} { top-level }
          {
            \msg_error:nnnnn { hooks } { set-top-level }
              { to } { PushDefaultHookLabel } {#1}
          }
          {
            \seq_gpush:NV \g_@@_name_stack_seq \g_@@_hook_curr_name_tl
            \tl_gset:Nn \g_@@_hook_curr_name_tl {#1}
          }
      }
  }
%    \end{macrocode}
%   and when an input is over, the topmost item of the stack is popped,
%   since that label will not be used again, and \cs{g_@@_hook_curr_name_tl}
%   is updated to equal the now topmost item of the stack:
%    \begin{macrocode}
\cs_new_protected:Npn \@@_curr_name_pop:
  {
    \seq_gpop:NNTF \g_@@_name_stack_seq \l_@@_return_tl
      { \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl }
      { \msg_error:nn { hooks } { extra-pop-label } }
  }
%    \end{macrocode}
%
%   At the end of the document we want to check if there was no
%   \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:}
%   (not a critical error, but it might indicate that something else is
%   not quite right):
%    \begin{macrocode}
\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage
  { \@@_end_document_label_check: }
\cs_new_protected:Npn \@@_end_document_label_check:
  {
    \seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl
      {
        \msg_error:nnx { hooks } { missing-pop-label }
          { \g_@@_hook_curr_name_tl }
        \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl
        \@@_end_document_label_check:
      }
  }
%    \end{macrocode}
%
%   The token list \cs{g_@@_hook_curr_name_tl} is but a mirror of the
%   top of the stack.
%
% \begin{macro}{\@@_set_default_hook_label:n,\@@_set_default_label:n}
%   Now define a wrapper that replaces the top of the stack with the
%   argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_set_default_hook_label:n #1
  {
    \seq_if_empty:NTF \g_@@_name_stack_seq
      {
        \msg_error:nnnnn { hooks } { set-top-level }
          { for } { SetDefaultHookLabel } {#1}
      }
      { \exp_args:Nx
        \@@_set_default_label:n { \@@_make_name:n {#1} } }
  }
\cs_new_protected:Npn \@@_set_default_label:n #1
  {
    \str_if_eq:nnTF {#1} { top-level }
      {
        \msg_error:nnnnn { hooks } { set-top-level }
          { to } { SetDefaultHookLabel } {#1}
      }
      { \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Adding or removing hook code}
%
% \begin{macro}{\hook_gput_code:nnn,\hook_gput_code_with_args:nnn}
% \begin{macro}{
%     \@@_gput_code:nnn,
%     \@@_gput_code_store:nnn,
%     \@@_hook_gput_code_do:nnn,
%     \@@_prop_gput_labeled_cleanup:nnn,
%     \@@_prop_gput_labeled_do:Nnnn
%    }
% \begin{macro}[EXP]{\@@_hash_check:nTF}
% \changes{v1.1n}{2025/09/24}{New function}
% \begin{macro}[EXP]{\@@_hash_check_aux:w}
% \changes{v1.1n}{2025/09/24}{New function}
%
%    With \cs{hook_gput_code:nnn}\Arg{hook}\Arg{label}\Arg{code} a
%    chunk of \meta{code} is added to an existing \meta{hook} labeled
%    with \meta{label}.
% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_gput_code_with_args:nnn} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gput_code:nnn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_gput_code:nnn #1 #2 #3
  {
    \@@_replacing_args_false:
    \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} {#3}
    \@@_replacing_args_reset:
  }
\cs_new_protected:Npn \hook_gput_code_with_args:nnn #1 #2 #3
  {
    \@@_replacing_args_true:
    \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} {#3}
    \@@_replacing_args_reset:
  }
%    \end{macrocode}
%
%   If \cs{AddToHookWithArguments} was used, do some sanity checking,
%   and if it's not possible to use arguments at this point, fall back
%   to regular \cs{AddToHook} by using \cs{@@_replacing_args_false:}.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
  {
    \@@_chk_args_allowed:nn {#1} { AddToHook }
%    \end{macrocode}
%   Then check if the code should be executed immediately, rather than
%   stored:
% \changes{v1.0r}{2021/09/06}{Use dedicated conditional (gh/606)}
%    \begin{macrocode}
    \@@_if_execute_immediately:nTF {#1}
      {
%    \end{macrocode}
%   \cs{AddToHookWithArguments} can't be used on one-time hooks (that
%   were already used).
%    \begin{macrocode}
        \@@_if_replacing_args:TF
          {
            \msg_error:nnnn { hooks } { one-time-args }
              {#1} { AddToHook }
          }
          { }
        \use:n
      }
      { \@@_gput_code_store:nnn {#1} {#2} }
          {#3}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code_store:nnn #1 #2 #3
  {
%    \end{macrocode}
%    Then check if the hook is usable.
%    \begin{macrocode}
    \@@_if_usable:nTF {#1}
%    \end{macrocode}
%    If so we simply add (or append) the new code to the property list
%    holding different chunks for the hook. At \verb=\begin{document}=
%    this is then sorted into a token list for fast execution.
%    \begin{macrocode}
      {
        \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
%    \end{macrocode}
%    However, if there is an update within the document we need to alter
%    this execution code which is done by
%    \cs{@@_update_hook_code:n}. In the preamble this does nothing.
%    \begin{macrocode}
        \@@_update_hook_code:n {#1}
      }
%    \end{macrocode}
%
%    If the hook is not usable, before giving up, check if it's not
%    disabled and otherwise try to declare it as a generic hook, if its
%    name matches one of the valid patterns.
%    \begin{macrocode}
      {
        \@@_if_disabled:nTF {#1}
          { \msg_error:nnn { hooks } { hook-disabled } {#1} }
          { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} }
      }
  }
%    \end{macrocode}
%
%    This macro will unconditionally add a chunk of code to the given hook.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3
  {
%    \end{macrocode}
%    However, first some debugging info if debugging is enabled:
% \changes{v1.1l}{2024/11/26}{Adjust debugging message (gh/1459)}
%    \begin{macrocode}
    \@@_debug:n{\iow_term:x{[lthooks]~ Add~ to~
                      \@@_if_usable:nF {#1} { undeclared~ }
                      hook~ '#1'~ (#2) \on@line
                      ^^J [lthooks] \@spaces
                      <-~ \tl_to_str:n{#3}} }
%    \end{macrocode}
%    Then try to get the code chunk labeled \verb=#2= from the hook.
%    If there's code already there, then append \verb=#3= to that,
%    otherwise just put \verb=#3=.  If the current label is |top-level|,
%    the code is added to a dedicated token list
%    \cs{@@_toplevel\textvisiblespace\meta{hook}} that goes at the end of the
%    hook (or at the beginning, for a reversed hook), just before
%   \cs[no-index]{@@_next\textvisiblespace\meta{hook}}.
%    \begin{macrocode}
    \str_if_eq:nnTF {#2} { top-level }
      {
        \str_if_eq:eeTF { top-level } { \@@_currname_or_default: }
          {
%    \end{macrocode}
%   If the hook's basic structure does not exist, we need to declare it
%   with \cs{@@_init_structure:n}.
%    \begin{macrocode}
            \@@_init_structure:n {#1}
%    \end{macrocode}
%   Then append to the \verb|_toplevel| container for the hook.
%    \begin{macrocode}
            \@@_cs_gput_right:nnn { _toplevel } {#1} {#3}
          }
          { \msg_error:nnn { hooks } { misused-top-level } {#1} }
      }
      {
%    \end{macrocode}
% \changes{v1.1n}{2025/09/24}{Only double hashes when needed}
%   When adding to the code pool, we have to double hashes if
%   \cs{AddToHook} was used (\verb|replacing_args| is false), so that
%   later it is turned into a single parameter token, rather than a
%   parameter to the hook macro. We skip this step if there are
%   no hashes at all in the argument: the token-by-token approach
%   otherwise becomes a major performance issue when the contents of
%   the hook are long.
%    \begin{macrocode}
        \exp_args:Nx \@@_prop_gput_labeled_cleanup:nnn
          {
            \@@_if_replacing_args:TF
              { \exp_not:n }
              {
                \@@_hash_check:nTF {#3}
                  { \exp_not:n }
                  { \@@_double_hashes:n }
              }
                {#3}
          }
          {#1} {#2}
      }
  }
\cs_new:Npx \@@_hash_check:nTF #1
  {
    \exp_not:N \exp_after:wN \exp_not:N \@@_hash_check_aux:w
      \exp_not:N \tl_to_str:n {#1} \c_hash_str \c_hash_str
      \exp_not:N \q_stop
  }
\use:x
  {
    \cs_new:Npn \exp_not:N \@@_hash_check_aux:w
      ##1 \c_hash_str ##2 \c_hash_str ##3 \exp_not:N \q_stop
  }
  { \tl_if_empty:nTF {#3} }
%    \end{macrocode}
%
%   Adds code to a hook's code pool.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_prop_gput_labeled_cleanup:nnn #1 #2 #3
  {
    \tl_set:Nn \l_@@_return_tl {#1}
    \@@_if_replacing_args:TF
      {
        \@@_if_usable:nT {#2}
          {
            \@@_set_normalise_fn:nn {#2}
              { Invalid~code~added~\msg_line_context: }
            \@@_normalise_fn:nn {#3} {#1}
            \prop_get:NnN \l_@@_work_prop {#3} \l_@@_return_tl
          }
      }
      { }
    \exp_args:NcV \@@_prop_gput_labeled_do:Nnn
      { g_@@_#2_code_prop } \l_@@_return_tl {#3}
  }
\cs_new_protected:Npn \@@_prop_gput_labeled_do:Nnn #1 #2 #3
  {
    \prop_get:NnNTF #1 {#3} \l_@@_return_tl
      { \prop_gput:Nno #1 {#3} { \l_@@_return_tl #2 } }
      { \prop_gput:Nnn #1 {#3} {#2} }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_gput_code:nnn}
%<latexrelease>                 {Providing~hooks}
%<latexrelease>\cs_gset_protected:Npn \hook_gput_code:nnn #1 #2
%<latexrelease>  { \@@_normalize_hook_args:Nnn
%<latexrelease>        \@@_gput_code:nnn {#1} {#2} }
%<latexrelease>\cs_gset_protected:Npn \@@_gput_code:nnn #1 #2 #3
%<latexrelease>  {
%<latexrelease>    \@@_if_execute_immediately:nTF {#1}
%<latexrelease>      {#3}
%<latexrelease>      {
%<latexrelease>        \@@_if_usable:nTF {#1}
%<latexrelease>          {
%<latexrelease>            \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
%<latexrelease>            \@@_update_hook_code:n {#1}
%<latexrelease>          }
%<latexrelease>          {
%<latexrelease>            \@@_if_disabled:nTF {#1}
%<latexrelease>              { \msg_error:nnn { hooks } { hook-disabled } {#1} }
%<latexrelease>              { \@@_try_declaring_generic_hook:nnn
%<latexrelease>                  {#1} {#2} {#3} }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3
%<latexrelease>  {
%<latexrelease>    \@@_debug:n{\iow_term:x{****~ Add~ to~
%<latexrelease>                      \@@_if_usable:nF {#1} { undeclared~ }
%<latexrelease>                      hook~ #1~ (#2)
%<latexrelease>                      \on@line\space <-~ \tl_to_str:n{#3}} }
%<latexrelease>    \str_if_eq:nnTF {#2} { top-level }
%<latexrelease>      {
%<latexrelease>        \str_if_eq:eeTF { top-level }
%<latexrelease>                        { \@@_currname_or_default: }
%<latexrelease>          {
%<latexrelease>            \@@_init_structure:n {#1}
%<latexrelease>            \@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
%<latexrelease>          }
%<latexrelease>          { \msg_error:nnn { hooks } { misused-top-level } {#1} }
%<latexrelease>      }
%<latexrelease>      {
%<latexrelease>        \prop_get:cnNTF
%<latexrelease>          { g_@@_#1_code_prop } {#2} \l_@@_return_tl
%<latexrelease>          {
%<latexrelease>            \prop_gput:cno { g_@@_#1_code_prop } {#2}
%<latexrelease>              { \l_@@_return_tl #3 }
%<latexrelease>          }
%<latexrelease>          { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn \hook_gput_code_with_args:nnn #1#2#3 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_chk_args_allowed:nn}
%   This macro checks if it is possible to add code with references to
%   a hook's arguments for hook \verb|#1|.  It only does something if
%   the function being run is \verb|replacing_args|.  This macro will
%   error if the hook is declared and takes no arguments, then it will
%   set \cs{@@_replacing_args_false:} so that the macro which called it
%   will add the code normally.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_chk_args_allowed:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_chk_args_allowed:nn #1 #2
  {
    \@@_if_replacing_args:TF
      {
        \@@_if_declared:nT {#1}
          { \tl_if_empty:cT { c_@@_#1_parameter_tl } { \use_ii:nn } }
        \use_none:n
          {
            \msg_error:nnnn { hooks } { without-args } {#1} {#2}
            \@@_replacing_args_false:
          }
      }
      { }
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_chk_args_allowed:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_chk_args_allowed:nn
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_gput_undeclared_hook:nnn}
%   Often it may happen that a package $A$ defines a hook \verb=foo=,
%   but package $B$, that adds code to that hook, is loaded before $A$.
%   In such case we need to add code to the hook before it is declared.
%   An implicitly declared hook doesn't have arguments (in principle),
%   so use \cs{c_false_bool} here.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
  {
    \@@_init_structure:n {#1}
    \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_try_declaring_generic_hook:nnn,
%               \@@_try_declaring_generic_next_hook:nn}
%
%   These entry-level macros just pass the arguments along to the
%   common \cs{@@_try_declaring_generic_hook:nNNnn} with the right
%   functions to execute when some action is to be taken.
%
%   The wrapper \cs{@@_try_declaring_generic_hook:nnn} then defers
%   \cs{hook_gput_code:nnn} if the generic hook was declared, or to
%   \cs{@@_gput_undeclared_hook:nnn} otherwise (the hook was tested for
%   existence before, so at this point if it isn't generic, it doesn't
%   exist).
%
%   The wrapper \cs{@@_try_declaring_generic_next_hook:nn} for
%   next-execution hooks does the same: it defers the code to
%   \cs{hook_gput_next_code:nn} if the generic hook was declared, or
%   to \cs{@@_gput_next_do:nn} otherwise.
% \changes{v1.0p}{2021/08/25}{Standardize generic hook names (gh/648)}
% \changes{v1.1d}{2023/05/21}
%         {Changes to allow support arguments in cmd hooks (cmd-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}
%<latexrelease>                 {\@@_try_declaring_generic_hook:nnn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1
  {
    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
      \@@_gput_code:nnn
      \@@_gput_undeclared_hook:nnn
        {#1}
  }
\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1
  {
    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
      \@@_gput_next_code:nn
      \@@_gput_next_do:nn
        {#1}
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2021/11/15}
%<latexrelease>                 {\@@_try_declaring_generic_hook:nnn}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_gset_protected:Npn \@@_try_declaring_generic_hook:nnn #1
%<latexrelease>  {
%<latexrelease>    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop:
%<latexrelease>      {#1}
%<latexrelease>      \hook_gput_code:nnn
%<latexrelease>      \@@_gput_undeclared_hook:nnn
%<latexrelease>        {#1}
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn
%<latexrelease>  \@@_try_declaring_generic_next_hook:nn #1
%<latexrelease>  {
%<latexrelease>    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop:
%<latexrelease>      {#1}
%<latexrelease>      \hook_gput_next_code:nn
%<latexrelease>      \@@_gput_next_do:nn
%<latexrelease>        {#1}
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}
%<latexrelease>                 {\@@_try_declaring_generic_hook:nnn}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_new_protected:Npn
%<latexrelease>  \@@_try_declaring_generic_hook:nnn #1
%<latexrelease>  {
%<latexrelease>    \@@_try_declaring_generic_hook:nNNnn {#1}
%<latexrelease>      \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn
%<latexrelease>  }
%<latexrelease>\cs_new_protected:Npn
%<latexrelease>  \@@_try_declaring_generic_next_hook:nn #1
%<latexrelease>  {
%<latexrelease>    \@@_try_declaring_generic_hook:nNNnn {#1}
%<latexrelease>      \hook_gput_next_code:nn \@@_gput_next_do:nn
%<latexrelease>  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_try_declaring_generic_hook:nNNnn,
%               \@@_try_declaring_generic_hook_split:nNNnn}
%
%   \cs{@@_try_declaring_generic_hook:nNNnn}
%   now splits the hook name
%   at the first \texttt{/} (if any) and first checks if it is a
%   file-specific hook (they require some normalization) using
%   \cs{@@_if_file_hook:wTF}. If not then check it is one of a
%   predefined set for generic names. We also split off the second
%   component to see if we have to make a reversed hook.  In either case
%   the function returns \meta{true} for a generic hook and \meta{false}
%   in other cases.
%
% \changes{v1.0s}{2021/09/28}
%                {Correct usage of older \cs{__hook_if_file_hook:wTF} (gh/675)}
% \changes{v1.1h}{2024/01/24}
%                {Correct usage of older \cs{__hook_if_file_hook:wTF} (gh/1243)}
%    \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1
%<latexrelease>  {
%<latexrelease>    \@@_if_file_hook:wTF #1 / / \s_@@_mark
%<latexrelease>      {
%<latexrelease>        \exp_args:Ne
%<latexrelease>        \@@_try_declaring_generic_hook_split:nNNnn
%<latexrelease>          { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
%<latexrelease>      }
%<latexrelease>      { \@@_try_declaring_generic_hook_split:nNNnn {#1} }
%<latexrelease>  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn
%<latexrelease>    \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3
%<latexrelease>  {
%<latexrelease>    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop:
%<latexrelease>      {#1}
%<latexrelease>      { #2 }
%<latexrelease>      { #3 } {#1}
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}
%<latexrelease>                 {\@@_try_declaring_generic_hook:wn}
%<latexrelease>                 {Hooks~with~args}
\prg_new_protected_conditional:Npnn
    \@@_try_declaring_generic_hook:wn
    #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
  {
    \@@_if_generic:nTF {#5}
      {
        \@@_if_usable:nF {#5}
          {
%    \end{macrocode}
%    If the hook doesn't exist yet we check if it is a \texttt{cmd}
%    hook and if so we attempt patching the command in addition to
%    declaring the hook.
%
%    For some commands this will not be possible, in which case
%    \cs{@@_patch_cmd_or_delay:Nnn} (defined in \texttt{ltcmdhooks})
%    will generate an appropriate error message.
% \changes{v1.1d}{2023/05/21}
%         {Changes to allow support arguments in cmd hooks (cmd-args).}
%    \begin{macrocode}
            \str_if_eq:nnT {#1} { cmd }
              {
                \@@_try_put_cmd_hook:n {#5}
                \@@_make_usable:nn {#5} { 9 }
                \use_none:nnn
              }
%    \end{macrocode}
%
%    Declare the hook always even if it can't really be used (error
%    message generated elsewhere).
%
%    Here we use \cs{@@_make_usable:nn}, so that a \cs{hook_new:n} is
%    still possible later.  Generic hooks (except \hook{cmd} hooks) take
%    no arguments, so use zero as the second argument.
%    \begin{macrocode}
            \@@_make_usable:nn {#5} { 0 }
          }
        \@@_if_generic_reversed:nT {#5}
          { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
        \prg_return_true:
      }
      {
%    \end{macrocode}
%
%    Generic hooks are all named \meta{type}/\meta{name}/\meta{place},
%    where \meta{type} and \meta{place} are predefined
%    (\cs{c_@@_generic_\meta{type}/./\meta{place}_tl}), and \meta{name}
%    is the variable component.  Older releases had some hooks with the
%    \meta{name} in the third part, so the code below supports that
%    syntax for a while, with a warning.
%
%    The \cs{exp_after:wN} |...| \cs{exp:w} trick is there to remove the
%    conditional structure inserted by
%    \cs{@@_try_declaring_generic_hook:wnTF} and thus allow access to the
%    tokens that follow it, as is needed to keep things going.
%
%    When the deprecation cycle ends, the lines below should all be
%    replaced by \cs{prg_return_false:}.
%    \begin{macrocode}
        \@@_if_deprecated_generic:nTF {#5}
          {
            \@@_deprecated_generic_warn:n {#5}
            \exp_after:wN \@@_declare_deprecated_generic:NNn
            \exp:w % \exp_end:
          }
          { \prg_return_false: }
      }
  }
%    \end{macrocode}
%
% \begin{macro}{
%     \@@_deprecated_generic_warn:Nn,
%     \@@_deprecated_generic_warn:Nw,
%   }
%   \cs{@@_deprecated_generic_warn:n} will issue a deprecation warning
%   for a given hook, and mark that hook such that the warning will not
%   be issued again (multiple warnings can be issued, but only once per
%   hook).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_deprecated_generic_warn:n #1
  { \@@_deprecated_generic_warn:w #1 \s_@@_mark }
\cs_new_protected:Npn \@@_deprecated_generic_warn:w
    #1 / #2 / #3 \s_@@_mark
  {
    \if_cs_exist:w @@~#1/#2/#3 \cs_end: \else:
      \msg_warning:nnnnn { hooks } { generic-deprecated } {#1} {#2} {#3}
    \fi:
    \cs_gset_eq:cN { @@~#1/#2/#3 } \scan_stop:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
%     \@@_do_deprecated_generic:Nn,
%     \@@_do_deprecated_generic:Nw,
%     \@@_declare_deprecated_generic:NNw,
%     \@@_declare_deprecated_generic:NNw,
%   }
%   Now that the user has been told about the deprecation, we proceed by
%   swapping \meta{name} and \meta{place} and adding the code to the
%   correct hook.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_do_deprecated_generic:Nn #1 #2
  { \@@_do_deprecated_generic:Nw #1 #2 \s_@@_mark }
\cs_new_protected:Npn \@@_do_deprecated_generic:Nw #1
         #2 / #3 / #4 \s_@@_mark
  { #1 { #2 / #4 / #3 } }
\cs_new_protected:Npn \@@_declare_deprecated_generic:NNn #1 #2 #3
  { \@@_declare_deprecated_generic:NNw #1 #2 #3 \s_@@_mark }
\cs_new_protected:Npn \@@_declare_deprecated_generic:NNw #1 #2
    #3 / #4 / #5 \s_@@_mark
  {
    \@@_try_declaring_generic_hook:wnTF #3 / #5 / #4 / \scan_stop:
        { #3 / #5 / #4 }
      #1 #2 { #3 / #5 / #4 }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}
%<latexrelease>                 {\@@_try_declaring_generic_hook:wn}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\prg_new_protected_conditional:Npnn
%<latexrelease>    \@@_try_declaring_generic_hook:wn
%<latexrelease>    #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
%<latexrelease>  {
%<latexrelease>    \@@_if_generic:nTF {#5}
%<latexrelease>      {
%<latexrelease>        \@@_if_usable:nF {#5}
%<latexrelease>          {
%<latexrelease>            \str_if_eq:nnT {#1} { cmd }
%<latexrelease>              { \@@_try_put_cmd_hook:n {#5} }
%<latexrelease>            \@@_make_usable:n {#5}
%<latexrelease>          }
%<latexrelease>        \@@_if_generic_reversed:nT {#5}
%<latexrelease>          { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease>        \prg_return_true:
%<latexrelease>      }
%<latexrelease>      {
%<latexrelease>        \@@_if_deprecated_generic:nTF {#5}
%<latexrelease>          {
%<latexrelease>            \@@_deprecated_generic_warn:n {#5}
%<latexrelease>            \exp_after:wN \@@_declare_deprecated_generic:NNn
%<latexrelease>            \exp:w % \exp_end:
%<latexrelease>          }
%<latexrelease>          { \prg_return_false: }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}
%<latexrelease>                 {\@@_try_declaring_generic_hook:wn}
%<latexrelease>                 {Support~cmd~hooks}
%<latexrelease>\prg_new_protected_conditional:Npnn
%<latexrelease>    \@@_try_declaring_generic_hook:wn
%<latexrelease>    #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
%<latexrelease>  {
%<latexrelease>    \tl_if_empty:nTF {#2}
%<latexrelease>      { \prg_return_false: }
%<latexrelease>      {
%<latexrelease>        \prop_if_in:NnTF \c_@@_generics_prop {#1}
%<latexrelease>          {
%<latexrelease>            \@@_if_usable:nF {#5}
%<latexrelease>              {
%<latexrelease>                \str_if_eq:nnT {#1} { cmd }
%<latexrelease>                  { \@@_try_put_cmd_hook:n {#5} }
%<latexrelease>                \@@_make_usable:n {#5}
%<latexrelease>              }
%<latexrelease>            \prop_if_in:NnTF
%<latexrelease>              \c_@@_generics_reversed_ii_prop {#2}
%<latexrelease>              { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease>              {
%<latexrelease>                \prop_if_in:NnT
%<latexrelease>                  \c_@@_generics_reversed_iii_prop {#3}
%<latexrelease>                  { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease>              }
%<latexrelease>            \prg_return_true:
%<latexrelease>          }
%<latexrelease>          { \prg_return_false: }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}
%<latexrelease>                 {\@@_try_declaring_generic_hook:wn}
%<latexrelease>                 {Support~cmd~hooks}
%<latexrelease>\prg_new_protected_conditional:Npnn
%<latexrelease>    \@@_try_declaring_generic_hook:wn
%<latexrelease>    #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
%<latexrelease>  {
%<latexrelease>    \tl_if_empty:nTF {#2}
%<latexrelease>      { \prg_return_false: }
%<latexrelease>      {
%<latexrelease>        \prop_if_in:NnTF \c_@@_generics_prop {#1}
%<latexrelease>          {
%<latexrelease>            \@@_if_declared:nF {#5} { \hook_new:n {#5} }
%<latexrelease>            \prop_if_in:NnTF
%<latexrelease>              \c_@@_generics_reversed_ii_prop {#2}
%<latexrelease>              { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease>              {
%<latexrelease>                \prop_if_in:NnT
%<latexrelease>                  \c_@@_generics_reversed_iii_prop {#3}
%<latexrelease>                  { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease>              }
%<latexrelease>            \prg_return_true:
%<latexrelease>          }
%<latexrelease>          { \prg_return_false: }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[pTF]{\@@_if_file_hook:w}
%   \cs{@@_if_file_hook:wTF} checks if the argument is a valid
%   file-specific hook (not, for example, |file/before|, but
%   |file/foo.tex/before|).  If it is a file-specific hook, then it
%   executes the \meta{true} branch, otherwise \meta{false}.
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_if_file_hook:w}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_if_file_hook:w}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\prg_new_conditional:Npnn \@@_if_file_hook:w
%<latexrelease>    #1 / #2 / #3 \s_@@_mark { TF }
%<latexrelease>  {
%<latexrelease>    \str_if_eq:nnTF {#1} { file }
%<latexrelease>      {
%<latexrelease>        \bool_lazy_or:nnTF
%<latexrelease>            { \tl_if_empty_p:n {#3} }
%<latexrelease>            { \str_if_eq_p:nn {#3} { / } }
%<latexrelease>          { \prg_return_false: }
%<latexrelease>          {
%<latexrelease>            \prop_if_in:NnTF \c_@@_generics_file_prop {#2}
%<latexrelease>              { \prg_return_true: }
%<latexrelease>              { \prg_return_false: }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>      { \prg_return_false: }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_file_hook_normalize:n}
% \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_file_hook_normalize:n}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%   When a file-specific hook is found, before being declared it is
%   lightly normalized by \cs{@@_file_hook_normalize:n}.  The current
%   implementation just replaces two consecutive slashes (|//|) by a
%   single one, to cope with simple cases where the user did something
%   like \verb|\def\input@path{{./mypath/}}|, in which case a hook would
%   have to be \verb|\AddToHook{file/./mypath//file.tex/after}|.
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_file_hook_normalize:n}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_new:Npn \@@_file_hook_normalize:n #1
%<latexrelease>  { \@@_strip_double_slash:n {#1} }
%<latexrelease>\cs_new:Npn \@@_strip_double_slash:n #1
%<latexrelease>  { \@@_strip_double_slash:w #1 // \s_@@_mark }
%    \end{macrocode}
%   This function is always called after testing if the argument is a
%   file hook with \cs{@@_if_file_hook:wTF}, so we can assume it has
%   three parts (it is either \verb|file/.../before| or
%   \verb|file/.../after|), so we use \verb|#1/#2/#3 //| instead of just
%   \verb|#1 //| to prevent losing a slash if the file name is empty.
%   \changes{v1.0h}{2021/01/07}{Assume hook name has at least three
%     nonempty parts (gh/464)}
%    \begin{macrocode}
%<latexrelease>\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3//#4\s_@@_mark
%<latexrelease>  {
%<latexrelease>    \tl_if_empty:nTF {#4}
%<latexrelease>      { #1/#2/#3 }
%<latexrelease>      { \@@_strip_double_slash:w #1/#2/#3 /#4\s_@@_mark }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%  \begin{macro}{
%    \c_@@_generic_cmd/./before_tl,\c_@@_generic_cmd/./after_tl,
%    \c_@@_generic_env/./before_tl,\c_@@_generic_env/./after_tl,
%    \c_@@_generic_file/./before_tl,\c_@@_generic_file/./after_tl,
%    \c_@@_generic_package/./before_tl,\c_@@_generic_package/./after_tl,
%    \c_@@_generic_class/./before_tl,\c_@@_generic_class/./after_tl,
%    \c_@@_generic_include/./before_tl,\c_@@_generic_include/./after_tl,
%    \c_@@_generic_env/./begin_tl,\c_@@_generic_env/./end_tl,
%    \c_@@_generic_include/./end_tl
%  }
%    Token lists defining the possible generic hooks.  We don't provide
%    any user interface to this as this is meant to be static.
%    \begin{description}
%    \item[\texttt{cmd}]
%      The generic hooks used for commands.
%    \item[\texttt{env}]
%      The generic hooks used in \cs{begin} and \cs{end}.
%    \item[\texttt{file}, \texttt{package}, \texttt{class}, \texttt{include}]
%      The generic hooks used when loading a file
%    \end{description}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_prop}
%<latexrelease>                 {Standardize~generic~hook~names}
\clist_map_inline:nn { cmd , env , file , package , class , include }
  {
    \tl_const:cn { c_@@_generic_#1/./before_tl } { + }
    \tl_const:cn { c_@@_generic_#1/./after_tl  } { - }
  }
\tl_const:cn { c_@@_generic_env/./begin_tl } { + }
\tl_const:cn { c_@@_generic_env/./end_tl   } { + }
%    \end{macrocode}
%
% \changes{v1.0t}{2022/04/01}{Support generic \texttt{include/.../excluded} hooks}
%    \begin{macrocode}
\tl_const:cn { c_@@_generic_include/./end_tl } { - }
\tl_const:cn { c_@@_generic_include/./excluded_tl } { + }
%    \end{macrocode}
%
%   Deprecated generic hooks:
%    \begin{macrocode}
\clist_map_inline:nn { file , package , class , include }
  {
    \tl_const:cn { c_@@_deprecated_#1/./before_tl } { }
    \tl_const:cn { c_@@_deprecated_#1/./after_tl  } { }
  }
\tl_const:cn { c_@@_deprecated_include/./end_tl } { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_prop}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_prop
%<latexrelease>     {cmd=,env=,file=,package=,class=,include=}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\c_@@_generics_reversed_ii_prop,
%                \c_@@_generics_reversed_iii_prop,
%                \c_@@_generics_file_prop}
%    The following generic hooks are supposed to use reverse ordering
%    (the |ii| and |iii| names are kept for the deprecation cycle):
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_reversed_ii_prop}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_reversed_ii_prop}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\prop_const_from_keyval:Nn
%<latexrelease>    \c_@@_generics_reversed_ii_prop {after=,end=}
%<latexrelease>\prop_const_from_keyval:Nn
%<latexrelease>    \c_@@_generics_reversed_iii_prop {after=}
%<latexrelease>\prop_const_from_keyval:Nn
%<latexrelease>    \c_@@_generics_file_prop {before=,after=}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
% \begin{macro}{
%     \c_@@_parameter_cmd/./before_tl,\c_@@_parameter_cmd/./after_tl,
%   }
%   Token lists defining the number of arguments for a given type of
%   generic hook.
% \changes{v1.1d}{2023/05/21}
%         {Token lists added (cmd-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\c_@@_parameter_cmd/./before_tl}
%<latexrelease>                 {Hooks~with~args}
%    \end{macrocode}
%
%   \hook{cmd} hooks are declared with 9 arguments because they have a
%   variable number of arguments (depending on the command they are
%   attached to), so we use the maximum here.
%    \begin{macrocode}
\tl_const:cn { c_@@_parameter_cmd/./before_tl } { #1#2#3#4#5#6#7#8#9 }
\tl_const:cn { c_@@_parameter_cmd/./after_tl }  { #1#2#3#4#5#6#7#8#9 }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_parameter_cmd/./before_tl}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\hook_gremove_code:nn}
% \begin{macro}{\@@_gremove_code:nn}
%
%    With \cs{hook_gremove_code:nn}\Arg{hook}\Arg{label} any code
%    for \meta{hook} stored under \meta{label} is removed.
% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gremove_code:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_gremove_code:nn #1 #2
  { \@@_normalize_hook_args:Nnn \@@_gremove_code:nn {#1} {#2} }
\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
  {
%    \end{macrocode}
%    First check that the hook code pool exists.  \cs{@@_if_usable:nTF}
%    isn't used here because it should be possible to remove code from a
%    hook before its defined (see section~\ref{sec:querying}).
%    \begin{macrocode}
    \@@_if_structure_exist:nTF {#1}
      {
%    \end{macrocode}
%    Then remove the chunk and run \cs{@@_update_hook_code:n} so
%    that the execution token list reflects the change if we are after
%    \verb=\begin{document}=.
%
%    If all code is to be removed, clear the code pool
%    \cs{g_@@_\meta{hook}_code_prop}, the top-level code
%    \cs{@@_toplevel\textvisiblespace\meta{hook}}, and the next-execution code
%    \cs{@@_next\textvisiblespace\meta{hook}}.
%    \begin{macrocode}
        \str_if_eq:nnTF {#2} {*}
          {
            \prop_gclear:c { g_@@_#1_code_prop }
            \@@_toplevel_gset:nn {#1} { }
            \@@_next_gset:nn {#1} { }
          }
          {
%    \end{macrocode}
%    If the label is |top-level| then clear the token list, as all code
%    there is under the same label.
%    \begin{macrocode}
            \str_if_eq:nnTF {#2} { top-level }
              { \@@_toplevel_gset:nn {#1} { } }
              {
                \prop_gpop:cnNF { g_@@_#1_code_prop }
                  {#2} \l_@@_return_tl
                  { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
              }
          }
%    \end{macrocode}
%    Finally update the code, if the hook exists.
%    \begin{macrocode}
        \@@_if_usable:nT {#1}
          { \@@_update_hook_code:n {#1} }
      }
%    \end{macrocode}
%
%    If the code pool for this hook doesn't exist, show a warning:
%    \begin{macrocode}
      {
        \@@_if_deprecated_generic:nTF {#1}
          {
            \@@_deprecated_generic_warn:n {#1}
            \@@_do_deprecated_generic:Nn
                \@@_gremove_code:nn {#1} {#2}
          }
          { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_gremove_code:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
%<latexrelease>  {
%<latexrelease>    \@@_if_structure_exist:nTF {#1}
%<latexrelease>      {
%<latexrelease>        \str_if_eq:nnTF {#2} {*}
%<latexrelease>          {
%<latexrelease>            \prop_gclear:c { g_@@_#1_code_prop }
%<latexrelease>            \@@_tl_gclear:c { @@_toplevel~#1 }
%<latexrelease>            \@@_tl_gclear:c { @@_next~#1 }
%<latexrelease>          }
%<latexrelease>          {
%<latexrelease>            \str_if_eq:nnTF {#2} { top-level }
%<latexrelease>              { \@@_tl_gclear:c { @@_toplevel~#1 } }
%<latexrelease>              {
%<latexrelease>                \prop_gpop:cnNF { g_@@_#1_code_prop }
%<latexrelease>                  {#2} \l_@@_return_tl
%<latexrelease>                  { \msg_warning:nnnn { hooks } { cannot-remove }
%<latexrelease>                                      {#1} {#2} }
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>        \@@_if_usable:nT {#1}
%<latexrelease>          { \@@_update_hook_code:n {#1} }
%<latexrelease>      }
%<latexrelease>      {
%<latexrelease>        \@@_if_deprecated_generic:nTF {#1}
%<latexrelease>          {
%<latexrelease>            \@@_deprecated_generic_warn:n {#1}
%<latexrelease>            \@@_do_deprecated_generic:Nn
%<latexrelease>                \@@_gremove_code:nn {#1} {#2}
%<latexrelease>          }
%<latexrelease>          { \msg_warning:nnnn { hooks } { cannot-remove }
%<latexrelease>                              {#1} {#2} }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_cs_gput_right:nnn}
% \begin{macro}{\@@_cs_gput_right_fast:nnn,\@@_cs_gput_right_slow:nnn}
% \begin{macro}{\@@_code_gset_auxi:nnnn,\@@_code_gset_auxi:eeen}
%   This macro is used to append code to the \verb|toplevel| and
%   \verb|next| token lists, treating them correctly depending on their
%   number of arguments, and depending on whether the code being added should
%   have parameter tokens understood as parameters, or doubled to be
%   stored as parameter tokens.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
% \changes{v1.1e}{2023/06/06}
%         {Short-circuit when the hook is declared without args (gh1078).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_cs_gput_right:nnn}
%<latexrelease>                 {Hooks~with~args}
%    \end{macrocode}
%
%   Check if the current hook is declared and takes no arguments.  In
%   this case, we short-circuit and use the simpler and much faster
%   approach that doesn't require hash-doubling.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_cs_gput_right:nnn #1 #2
  {
    \if:w T
        \@@_if_declared:nF {#2} { F }
        \tl_if_empty:cF { c_@@_#2_parameter_tl } { F }
          T
      \exp_after:wN \@@_cs_gput_right_fast:nnn
    \else:
      \exp_after:wN \@@_cs_gput_right_slow:nnn
    \fi:
        {#1} {#2}
  }
\cs_new_protected:Npn \@@_cs_gput_right_fast:nnn #1 #2 #3
  { \cs_gset:cpx { @@#1~#2 }
      { \exp_not:v { @@#1~#2 } \exp_not:n {#3} } }
\cs_new_protected:Npn \@@_cs_gput_right_slow:nnn #1 #2 #3
  {
%    \end{macrocode}
%   The auxiliary \cs{@@_code_gset_auxi:eeen} just does the assignment
%   at the end.  Its first argument is the parameter text of the macro,
%   which is chosen here depending if
%   \cs[no-index]{c_@@_\meta{hook}_parameter_tl} exists, if the hook is
%   declared, and if it's a generic hook.
%    \begin{macrocode}
    \cs_if_exist:cF { @@#1~#2 }
      { \@@_code_gset_aux:nnn {#1} {#2} { } }
    \@@_code_gset_auxi:eeen
      {
        \@@_if_declared:nTF {#2}
          { \tl_use:c { c_@@_#2_parameter_tl } }
          {
            \@@_if_generic:nTF {#2}
              { \@@_generic_parameter:n {#2} }
              { \c_@@_nine_parameters_tl }
          }
      }
%    \end{macrocode}
%   Here we take the existing code in the macro, expand it with as many
%   arguments as it takes, then double the hashes so the code can be
%   reused. \pho{Maybe can be improved. The case of adding to an empty
%     cs can be optimized by quickly checking \cs{cs_replacement_spec}.}
%    \begin{macrocode}
      {
        \exp_args:NNo \exp_args:No \@@_double_hashes:n
          {
            \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
              { \@@_braced_cs_parameter:n { @@#1~#2 } }
          }
      }
%    \end{macrocode}
%   Now the new code:  if we are replacing arguments, then hashes are
%   left untouched, otherwise they are doubled.
%    \begin{macrocode}
      {
        \@@_if_replacing_args:TF
          { \exp_not:n }
          { \@@_double_hashes:n }
            {#3}
      }
%    \end{macrocode}
%   And finally, the csname which we'll define with all the above.
%    \begin{macrocode}
      { @@#1~#2 }
  }
%    \end{macrocode}
%
%   And as promised, the auxiliary that does the definition.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_code_gset_auxi:nnnn #1 #2 #3 #4
  { \cs_gset:cpn {#4} #1 { #2 #3 } }
\cs_generate_variant:Nn \@@_code_gset_auxi:nnnn { eeen }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_cs_gput_right:nnn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_cs_gput_right:nnn
%<latexrelease>\cs_undefine:N \@@_cs_gput_right_fast:nnn
%<latexrelease>\cs_undefine:N \@@_cs_gput_right_slow:nnn
%<latexrelease>\cs_undefine:N \@@_code_gset_auxi:nnnn
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{
%     \@@_code_gset:nn,\@@_code_gset:ne,
%     \@@_toplevel_gset:nn,
%     \@@_next_gset:nn,
%     \@@_code_gset_aux:nnn
%   }
%   These macros define
%   \cs[no-index]{@@\meta{type}\textvisiblespace\meta{hook}} (with
%   \meta{type} being \verb|_next|, \verb|_toplevel|, or empty) with the
%   given code and the parameters stored in
%   \cs[no-index]{c_@@_\meta{hook}_parameter_tl} (or none, if that
%   doesn't exist).
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_code_gset:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_code_gset:nn
  { \@@_code_gset_aux:nnn { } }
\cs_new_protected:Npn \@@_toplevel_gset:nn
  { \@@_code_gset_aux:nnn { _toplevel } }
\cs_new_protected:Npn \@@_next_gset:nn
  { \@@_code_gset_aux:nnn { _next } }
\cs_new_protected:Npn \@@_code_gset_aux:nnn #1 #2 #3
  {
    \cs_gset:cpn { @@#1~#2 \exp_last_unbraced:Ne }
      { \@@_parameter:n {#2} }
      {#3}
  }
\cs_generate_variant:Nn \@@_code_gset:nn { ne }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_code_gset:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_code_gset:nn
%<latexrelease>\cs_undefine:N \@@_toplevel_gset:nn
%<latexrelease>\cs_undefine:N \@@_next_gset:nn
%<latexrelease>\cs_undefine:N \@@_code_gset_aux:nnn
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_normalise_cs_args:nn}
%   This macro normalizes the parameters of the macros
%   \cs[no-index]{@@\meta{type}\textvisiblespace\meta{hook}} to take the
%   right number of arguments after a hook is declared.  At this point
%   we know \cs[no-index]{c_@@_\meta{hook}_parameter_tl} exists, so use
%   that to count the arguments and use that as \meta{parameter text}
%   for the newly (re)defined macro.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_normalise_cs_args:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_normalise_cs_args:nn #1 #2
  {
    \cs_if_exist:cT { @@#1~#2 }
      {
        \@@_code_gset_auxi:eeen
          { \tl_use:c { c_@@_#2_parameter_tl } }
          {
            \exp_args:NNo \exp_args:No \@@_double_hashes:n
              {
                \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
                  { \@@_braced_cs_parameter:n { @@#1~#2 } }
              }
          }
          { }
          { @@#1~#2 }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_normalise_cs_args:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_normalise_cs_args:nn
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_normalise_code_pool:n}
% \begin{macro}{\@@_set_normalise_fn:nn}
%   This one's a bit of a hack.  It takes a hook, and iterates over its
%   code pool (\cs[no-index]{g_@@_\meta{hook}_code_prop}), redefining
%   each code label to use only valid arguments.  This is used when, for
%   example, a code is added referencing arguments \verb|#1| and
%   \verb|#2|, but the hook has only \verb|#1|.  In this example, every
%   reference to \verb|#2| is changed to \verb|##2|.  This is done
%   because otherwise \TeX{} will throw a low-level error every time
%   some change happens to the hook (code is added, a rule is set, etc),
%   which can get quite repetitive for no good reason.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_normalise_code_pool:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_normalise_code_pool:n #1
  {
%    \end{macrocode}
%   First, call \cs{@@_set_normalise_fn:nn} with the hook name to set
%   everything up, then we'll loop over the
%   hook's code pool applying the normalization above.  After that's
%   done, copy the temporary property list back to the hook's.
%    \begin{macrocode}
    \@@_set_normalise_fn:nn {#1} { Offending~label:~'##1' }
    \prop_clear:N \l_@@_work_prop
    \prop_map_function:cN { g_@@_#1_code_prop } \@@_normalise_fn:nn
    \prop_gset_eq:cN { g_@@_#1_code_prop } \l_@@_work_prop
  }
%    \end{macrocode}
%
%   The sole purpose of this function is to define
%   \cs{@@_normalise_fn:nn}, which will then do the correcting of the
%   code being added to the hook.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_set_normalise_fn:nn #1 #2
  {
%    \end{macrocode}
%   To start, we define two auxiliary token lists.
%   \cs[no-index]{l_@@_tmpb_tl} contains:
%\begin{verbatim}
%   {\c__hook_hashes_tl 1}
%   {\c__hook_hashes_tl 2}
%   ...
%   {\c__hook_hashes_tl 9}
%\end{verbatim}
%    \begin{macrocode}
    \cs_set:Npn \@@_tmp:w ##1##2##3##4##5##6##7##8##9 { }
    \tl_set:Ne \l_@@_tmpb_tl
      { \@@_braced_cs_parameter:n { @@_tmp:w } }
    \group_begin:
      \@@_tl_set:cn { c_@@_hash_tl } { \exp_not:N \c_@@_hashes_tl }
      \use:e
        {
    \group_end:
    \tl_set:Nn \exp_not:N \l_@@_tmpb_tl { \l_@@_tmpb_tl }
        }
%    \end{macrocode}
%   And \cs[no-index]{l_@@_tmpa_tl} contains:
%\begin{verbatim}
%   {\c__hook_hash_tl 1}
%   {\c__hook_hash_tl 2}
%   ...
%   {\c__hook_hash_tl <n>}
%\end{verbatim}
%   with \meta{n} being the number of arguments declared for the hook.
%    \begin{macrocode}
    \exp_last_unbraced:NNf
    \cs_set:Npn \@@_tmp:w { \@@_parameter:n {#1} } { }
    \tl_set:Ne \l_@@_tmpa_tl
      { \@@_braced_cs_parameter:n { @@_tmp:w } }
%    \end{macrocode}
%   Now this function does the fun part.  It is meant to be used with
%   \cs{prop_map_function:NN}, taking a label name in \verb|##1| and the
%   code stored in that label in \verb|##2|.
%    \begin{macrocode}
    \cs_gset_protected:Npx \@@_normalise_fn:nn ##1 ##2
      {
%    \end{macrocode}
%   Here we'll define two auxiliary macros:  the first one throws an
%   error when it detects an invalid argument reference.  It piggybacks
%   on \TeX's low-level \enquote{Illegal parameter number} error, but it
%   defines a weirdly-named control sequence so that the error comes out
%   nicely formatted.  For example, if the label \enquote{badpkg} adds
%   some code that references argument \verb|#3| in the hook
%   \enquote{foo}, which takes only two arguments, the error will be:
%\begin{verbatim}
%   ! Illegal parameter number in definition of hook 'foo'.
%   (hooks)             Offending label: 'badpkg'.
%   <to be read again> 
%                      3
%\end{verbatim}
%   At the point of this definition, the error is raised if the code
%   happens to reference an invalid argument.  If it was possible to
%   detect that this definition raised no error, the next step would be
%   unnecessary.  We'll do all this in a group so this weird definition
%   doesn't leak out, and set \cs{tex_escapechar:D} to $-1$ so this hack
%   shows up extra nice in the case of an error.
%    \begin{macrocode}
        \group_begin:
          \int_set:Nn \tex_escapechar:D { -1 }
          \cs_set:cpn
              {
                hook~'#1'. ^^J
                (hooks) \prg_replicate:nn { 13 } { ~ }
                #2 % more message text
              }
              \exp_not:v { c_@@_#1_parameter_tl }
            {##2}
        \group_end:
%    \end{macrocode}
%   This next macro, with a much less fabulous name, takes always nine
%   arguments, and it just transfers the code \verb|##2| under the label
%   \verb|##1| to the temporary property list.  The first \meta{n}
%   arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
%   $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
%   twice as many \verb|#| tokens as the former).  Then,
%   \cs{@@_double_hashes:n} is used to double non-argument hashes, and
%   expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
%   parameter tokens.
%    \begin{macrocode}
        \cs_set:Npn \exp_not:N \@@_tmp:w
            \exp_not:V \c_@@_nine_parameters_tl
          {
            \prop_put:Nne \exp_not:N \l_@@_work_prop
              {##1} { \exp_not:N \@@_double_hashes:n {##2} }
          }
%    \end{macrocode}
%   This next macro, with a much less fabulous name, takes always nine
%   arguments, and it just transfers the code \verb|##2| under the label
%   \verb|##1| to the temporary property list.  The first \meta{n}
%   arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
%   $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
%   twice as many \verb|#| tokens as the former).  Then,
%   \cs{@@_double_hashes:n} is used to double non-argument hashes, and
%   expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
%   parameter tokens.
%    \begin{macrocode}
        \exp_not:N \@@_tmp:w
          \exp_not:V \l_@@_tmpa_tl
          \exp_args:No \exp_not:o
            { \exp_after:wN \@@_tmp:w \l_@@_tmpb_tl }
      }
  }
\cs_new_eq:NN \@@_normalise_fn:nn ?
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_normalise_code_pool:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_normalise_code_pool:n
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_cs_if_empty:c}
%   Check if the expansion of a control sequence is empty by looking at
%   its replacement text.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_cs_if_empty:c}
%<latexrelease>                 {Hooks~with~args}
\prg_new_conditional:Npnn \@@_cs_if_empty:c #1 { p, T, F, TF }
  {
    \if:w \scan_stop: \@@_replacement_spec:c {#1} \scan_stop:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
\cs_new:Npn \@@_replacement_spec:c #1
  {
    \exp_args:Nc \token_if_macro:NT {#1}
      { \cs_replacement_spec:c {#1} }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_cs_if_empty:c}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_cs_if_empty:c
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_braced_cs_parameter:n}
% \begin{macro}{\@@_braced_hidden_loop:w}
% \begin{macro}{\@@_cs_parameter_count:N}
% \begin{macro}{\@@_cs_parameter_count:w,\@@_cs_end:w}
%   Looks at the \meta{parameter text} of a control sequence, and
%   returns a run of \enquote{hidden} braced parameters for that macro.
%   This works as long as the macros take a simple run of zero to nine
%   arguments.  The parameters are \enquote{hidden} because the
%   parameter tokens are returned inside \cs{c_@@_hash_tl} instead of
%   explicitly, so that \cs{@@_double_hashes:n} won't touch these.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_braced_cs_parameter:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new:Npn \@@_braced_cs_parameter:n #1
  {
    \exp_last_unbraced:Ne \@@_braced_hidden_loop:w
      { \exp_args:Nc \@@_cs_parameter_count:N {#1} } ? \s_@@_mark
  }
\cs_new:Npn \@@_braced_hidden_loop:w #1
  {
    \if:w ? #1
      \@@_use_i_delimit_by_s_mark:nw
    \fi:
    { \exp_not:N \c_@@_hash_tl #1 }
    \@@_braced_hidden_loop:w
  }
\cs_new:Npn \@@_cs_parameter_count:N #1
  {
    \exp_last_unbraced:Nf \@@_cs_parameter_count:w
      { \token_if_macro:NT #1 { \cs_parameter_spec:N #1 } }
      ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
      ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
      ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
      \s_@@_mark
  }
\cs_new:Npn \@@_cs_parameter_count:w #1#2 #3#4 #5#6 #7#8
  { #2 #4 #6 #8 \@@_cs_parameter_count:w }
\cs_new:Npn \@@_cs_end:w #1 \s_@@_mark { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%   This function can't be undefined when rolling back because it's used
%   at the end of this module to adequate the hook data structures to
%   previous versions.
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_braced_cs_parameter:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_braced_parameter:n}
% \begin{macro}{\@@_braced_real_loop:w}
%   This one is used in simpler cases, where no special handling of
%   hashes is required.  This is used only inside
%   \cs{@@_initialize_hook_code:n}, so it assumes
%   \cs[no-index]{c_@@_\meta{hook}_parameter_tl} is defined, but should
%   work otherwise.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_braced_parameter:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new:Npn \@@_braced_parameter:n #1
  {
    \if_case:w
      \int_eval:n
        { \exp_args:Nv \str_count:n { c_@@_#1_parameter_tl } / 3 }
      \exp_stop_f:
    \or: {##1}
    \or: {##1} {##2}
    \or: {##1} {##2} {##3}
    \or: {##1} {##2} {##3} {##4}
    \or: {##1} {##2} {##3} {##4} {##5}
    \or: {##1} {##2} {##3} {##4} {##5} {##6}
    \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7}
    \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7} {##8}
    \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7} {##8} {##9}
    \else:
      \msg_expandable_error:nnn { latex2e } { should-not-happen }
        { Invalid~parameter~spec. }
    \fi:
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_braced_parameter:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_braced_parameter:n
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_parameter:n}
%   This is just a shortcut to \verb|e|- or \verb|f|-expand to the
%   \meta{parameter text} of the hook.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_parameter:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new:Npn \@@_parameter:n #1
  {
    \cs:w c_@@_
    \tl_if_exist:cTF { c_@@_#1_parameter_tl }
      { #1_parameter } { empty }
    _tl \cs_end:
  }
\cs_new:Npn \@@_generic_parameter:n #1
  { \@@_generic_parameter:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_generic_parameter:w #1 / #2 / #3 / #4 \s_@@_mark
  {
    \cs_if_exist_use:cF { c_@@_parameter_#1/./#3_tl }
      { \c_@@_empty_tl }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_parameter:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_parameter:n
%<latexrelease>\cs_undefine:N \@@_generic_parameter:n
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
%  \subsection{Setting rules for hooks code}
%
% \begin{macro}{
%     \g_@@_??_code_prop,
%     \@@~??,
%     \g_@@_??_reversed_tl,
%     \c_@@_??_parameter_tl,
%   }
%
%    Initially these variables simply used an empty ``label'' name (not
%    two question marks). This was a bit unfortunate, because then
%    \texttt{l3doc} complains about \verb=__= in the middle of a
%    command name when trying to typeset the documentation. However
%    using a ``normal'' name such as \texttt{default} has the
%    disadvantage of that being not really distinguishable from a real
%    hook name. I now have settled for \texttt{??} which needs some
%    gymnastics to get it into the csname, but since this is used a
%    lot, the code should be fast, so this is not done with \texttt{c}
%    expansion in the code later on.
%
%    \cs{@@\textvisiblespace??} isn't used, but it has to be defined to
%    trick the code into thinking that \texttt{??} is actually a hook.
%    \begin{macrocode}
\prop_new:c { g_@@_??_code_prop }
\prop_new:c { @@~?? }
%    \end{macrocode}
%
%    Default rules are always given in normal ordering (never in
%    reversed ordering). If such a rule is applied to a reversed
%    hook it behaves as if the rule is reversed (e.g.,
%    \texttt{after} becomes \texttt{before})
%    because those rules are applied first and then the order is reversed.
%    \begin{macrocode}
\tl_new:c { g_@@_??_reversed_tl }
%    \end{macrocode}
%
%    The parameter text for the \enquote{default} hook is empty.
% \changes{v1.1a}{2023/04/06}
%         {Token list added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\c_@@_??_parameter_tl}
%<latexrelease>                 {Hooks~with~args}
\tl_const:cn { c_@@_??_parameter_tl } { }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_??_parameter_tl}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:c { c_@@_??_parameter_tl }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\hook_gset_rule:nnnn}
%  \begin{macro}{\@@_gset_rule:nnnn}
%    With
%    \cs{hook_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2}
%    a relation is defined between the two code labels for the given
%    \meta{hook}.  The special hook \texttt{??} stands for \emph{any}
%    hook, which sets a default rule (to be used if no other relation
%    between the two hooks exist).
%    \begin{macrocode}
\cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4
  {
    \@@_normalize_hook_rule_args:Nnnnn \@@_gset_rule:nnnn
      {#1} {#2} {#3} {#4}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2022/06/01}{\@@_gset_rule:nnnn}
%<latexrelease>                 {Refuse~setting~rule~for~one-time~hooks}
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
  {
    \@@_if_deprecated_generic:nT {#1}
      {
        \@@_deprecated_generic_warn:n {#1}
        \@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn {#1}
          {#2} {#3} {#4}
        \@@_use_none_delimit_by_s_mark:w
      }
    \@@_if_execute_immediately:nT {#1}
      {
        \msg_error:nnnnnn { hooks } { rule-too-late }
          {#1} {#2} {#3} {#4}
        \@@_use_none_delimit_by_s_mark:w
      }
%    \end{macrocode}
%    First we ensure the basic data structure of the hook exists:
%    \begin{macrocode}
    \@@_init_structure:n {#1}
%    \end{macrocode}
%    Then we clear any previous relationship between both labels.
%    \begin{macrocode}
    \@@_rule_gclear:nnn {#1} {#2} {#4}
%    \end{macrocode}
%    Then we call the function to handle the given rule. Throw an error if the
%    rule is invalid.
%    \begin{macrocode}
    \cs_if_exist_use:cTF { @@_rule_#3_gset:nnn }
      {
          {#1} {#2} {#4}
        \@@_update_hook_code:n {#1}
      }
      {
        \msg_error:nnnnnn { hooks } { unknown-rule }
          {#1} {#2} {#3} {#4}
      }
    \s_@@_mark
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_gset_rule:nnnn}
%<latexrelease>                 {Refuse~setting~rule~for~one-time~hooks}
%<latexrelease>\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
%<latexrelease>  {
%<latexrelease>    \@@_if_deprecated_generic:nT {#1}
%<latexrelease>      {
%<latexrelease>        \@@_deprecated_generic_warn:n {#1}
%<latexrelease>        \@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn
%<latexrelease>          {#1} {#2} {#3} {#4}
%<latexrelease>        \exp_after:wN \use_none:nnnnnnnnn \use_none:n
%<latexrelease>      }
%<latexrelease>    \@@_init_structure:n {#1}
%<latexrelease>    \@@_rule_gclear:nnn {#1} {#2} {#4}
%<latexrelease>    \cs_if_exist_use:cTF { @@_rule_#3_gset:nnn }
%<latexrelease>      {
%<latexrelease>          {#1} {#2} {#4}
%<latexrelease>        \@@_update_hook_code:n {#1}
%<latexrelease>      }
%<latexrelease>      {
%<latexrelease>        \msg_error:nnnnnn { hooks } { unknown-rule }
%<latexrelease>          {#1} {#2} {#3} {#4}
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_rule_before_gset:nnn, \@@_rule_after_gset:nnn,
%               \@@_rule_<_gset:nnn, \@@_rule_>_gset:nnn}
%    Then we add the new rule.  We need to normalize the rules here to
%    allow for faster processing later.  Given a pair of labels
%    $l_A$ and $l_B$, the rule $l_A>l_B$ is the same as $l_B<l_A$
%    only presented differently.  But by normalizing the
%    forms of the rule to a single representation, say, $l_B<l_A$, reduces
%    the time spent looking for the rules later considerably.
%
%    Here we do that normalization by using \cs[no-index]{(pdf)strcmp} to
%    lexically sort labels $l_A$ and $l_B$ to a fixed order.  This order
%    is then enforced every time these two labels are used together.
%
%    Here we use \cs{@@_label_pair:nn}~\Arg{hook}~\Arg{l_A}~\Arg{l_B}
%    to build a string \texttt{$l_B$\string|$l_A$} with a fixed order, and
%    use \cs{@@_label_ordered:nnTF} to apply the correct rule to the pair
%    of labels, depending if it was sorted or not.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_rule_before_gset:nnn #1#2#3
  {
    \@@_tl_gset:cx
      { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
      { \@@_label_ordered:nnTF {#2} {#3} { < } { > } }
  }
\cs_new_eq:cN { @@_rule_<_gset:nnn } \@@_rule_before_gset:nnn
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_rule_after_gset:nnn #1#2#3
  {
    \@@_tl_gset:cx
      { g_@@_#1_rule_ \@@_label_pair:nn {#3} {#2} _tl }
      { \@@_label_ordered:nnTF {#3} {#2} { < } { > } }
  }
\cs_new_eq:cN { @@_rule_>_gset:nnn } \@@_rule_after_gset:nnn
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_rule_voids_gset:nnn}
%   This rule removes (clears, actually) the code from label |#3| if
%   label |#2| is in the hook |#1|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_rule_voids_gset:nnn #1#2#3
  {
    \@@_tl_gset:cx
      { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
      { \@@_label_ordered:nnTF {#2} {#3} { -> } { <- } }
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{
%     \@@_rule_incompatible-error_gset:nnn,
%     \@@_rule_incompatible-warning_gset:nnn,
%   }
%   These relations make an error/warning if labels |#2| and |#3| appear
%   together in hook |#1|.
%    \begin{macrocode}
\cs_new_protected:cpn { @@_rule_incompatible-error_gset:nnn } #1#2#3
  { \@@_tl_gset:cn
      { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
      { xE }
  }
\cs_new_protected:cpn { @@_rule_incompatible-warning_gset:nnn } #1#2#3
  { \@@_tl_gset:cn
      { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
      { xW }
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_rule_unrelated_gset:nnn, \@@_rule_gclear:nnn}
%    Undo a setting. \cs{@@_rule_unrelated_gset:nnn} doesn't need to do anything,
%    since we use \cs{@@_rule_gclear:nnn} before setting any rule.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_rule_unrelated_gset:nnn #1#2#3 { }
\cs_new_protected:Npn \@@_rule_gclear:nnn #1#2#3
  { \cs_undefine:c { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[EXP]{\@@_label_pair:nn}
%   Ensure that the lexically greater label comes first.
%    \begin{macrocode}
\cs_new:Npn \@@_label_pair:nn #1#2
  {
    \if_case:w \@@_str_compare:nn {#1} {#2} \exp_stop_f:
           #1 | #1 %  0
    \or:   #1 | #2 % +1
    \else: #2 | #1 % -1
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[pTF]{\@@_label_ordered:nn}
%   Check that labels |#1| and |#2| are in the correct order (as
%   returned by \cs{@@_label_pair:nn}) and if so return true, else
%   return false.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_label_ordered:nn #1#2 { TF }
  {
    \if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[EXP]{\@@_if_label_case:nnnnn}
%   To avoid doing the string comparison twice in \cs{@@_initialize_single:NNn}
%   (once with \cs{str_if_eq:nn} and again with \cs{@@_label_ordered:nn}),
%   we use a three-way branching macro that will compare |#1| and |#2|
%   and expand to \cs{use_i:nnn} if they are equal, \cs{use_ii:nn} if
%   |#1| is lexically greater, and \cs{use_iii:nn} otherwise.
%    \begin{macrocode}
\cs_new:Npn \@@_if_label_case:nnnnn #1#2
  {
    \cs:w use_
      \if_case:w \@@_str_compare:nn {#1} {#2}
         i \or: ii \else: iii \fi: :nnn
    \cs_end:
  }
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\@@_update_hook_code:n}
%    Before \verb=\begin{document}=  this does nothing, in the body it
%    reinitializes the hook code using the altered data.
%    \begin{macrocode}
\cs_new_eq:NN \@@_update_hook_code:n \use_none:n
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\@@_initialize_all:}
%    Initialize all known hooks (at \verb=\begin{document}=), i.e.,
%    update the fast execution token lists to hold the necessary code
%    in the right order.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_all:}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_initialize_all:
  {
%    \end{macrocode}
%    First we change \cs{@@_update_hook_code:n} which so far was a
%    no-op to now initialize one hook. This way any later updates to
%    the hook will run that code and also update the execution token
%    list.
%    \begin{macrocode}
    \cs_gset_eq:NN \@@_update_hook_code:n \@@_initialize_hook_code:n
%    \end{macrocode}
%    Now we loop over all hooks that have been defined and update each
%    of them.  Here we have to determine if the hook has arguments so
%    that auxiliaries know what to do with hashes.  We look at
%    \cs[no-index]{c_@@_\meta{hook}_parameter_tl}, if it has any
%    parameters, and set \verb|replacing_args| accordingly.
%    \begin{macrocode}
    \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
    \seq_map_inline:Nn \g_@@_all_seq
      {
        \tl_if_empty:cTF { c_@@_##1_parameter_tl }
          { \@@_replacing_args_false: }
          { \@@_replacing_args_true: }
        \@@_update_hook_code:n {##1}
        \@@_replacing_args_reset:
      }
%    \end{macrocode}
%    If we are debugging we show results hook by hook for all hooks
%    that have data.
% \changes{v1.1l}{2024/11/26}{Adjust debugging message (gh/1459)}
%    \begin{macrocode}
    \@@_debug:n
      {
        \iow_term:x { ^^J[lthooks]~ All~initialized~(non-empty)~hooks: }
        \prop_map_inline:Nn \g_@@_used_prop
          {
            \iow_term:x
              { ^^J ~ ##1 ~ -> ~ \cs_replacement_spec:c { @@~##1 } ~ }
          }
      }
%    \end{macrocode}
%    After all hooks are initialized we change the ``use'' to just
%    call the hook code and not initialize it (as this was already done in the
%    preamble.
%    \begin{macrocode}
    \@@_post_initialization_defs:
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_all:}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_initialize_all:
%<latexrelease>  {
%<latexrelease>    \cs_gset_eq:NN \@@_update_hook_code:n
%<latexrelease>                   \@@_initialize_hook_code:n
%<latexrelease>    \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
%<latexrelease>    \seq_map_inline:Nn \g_@@_all_seq
%<latexrelease>      { \@@_update_hook_code:n {##1} }
%<latexrelease>    \@@_debug:n
%<latexrelease>      {
%<latexrelease>        \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:}
%<latexrelease>        \prop_map_inline:Nn \g_@@_used_prop
%<latexrelease>          {
%<latexrelease>            \iow_term:x
%<latexrelease>              { ^^J ~ ##1 ~ -> ~
%<latexrelease>                \cs_replacement_spec:c { @@~##1 } ~ }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>    \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
%<latexrelease>    \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
%<latexrelease>  }
%<@@=>
%<latexrelease>\cs_gset_eq:NN \@expl@@@initialize@all@@
%<latexrelease>               \__hook_initialize_all:
%<@@=hook>
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\@@_initialize_hook_code:n}
%    Initializing or reinitializing the fast execution hook code. In
%    the preamble this is selectively done in case a hook gets used
%    and at \verb=\begin{document}= this is done for all hooks and
%    afterwards only if the hook code changes.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
% \changes{v1.1l}{2024/11/26}{Adjust debugging message (gh/1459)}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_hook_code:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_initialize_hook_code:n #1
  {
    \@@_debug:n
      { \iow_term:x { ^^J[lthooks]~ Update~code~for~hook~'#1' \on@line :^^J } }
%    \end{macrocode}
%    This does the sorting and the updates.
%    First thing we do is to check if a legacy hook macro exists and
%    if so we add it to the hook under the label \texttt{legacy}. This
%    might make the hook non-empty so we have to do this before
%    the then following test.
%    \begin{macrocode}
    \@@_include_legacy_code_chunk:n {#1}
%    \end{macrocode}
%    If there aren't any code
%    chunks for the current hook, there is no point in even starting
%    the sorting routine so we make a quick test for that and in that
%    case just update \cs{@@\textvisiblespace\meta{hook}} to hold the |top-level| and
%    |next| code chunks. If there are code chunks we call
%    \cs{@@_initialize_single:NNn} and pass to it ready made csnames
%    as they are needed several times inside. This way we save a bit
%    on processing time if we do that up front.
% \changes{v1.0u}{2022/05/17}{Refuse sorting one-time hooks (gh/818).}
%    \begin{macrocode}
    \@@_if_usable:nT {#1}
      {
        \prop_if_empty:cTF { g_@@_#1_code_prop }
          {
            \@@_code_gset:ne {#1}
              {
%    \end{macrocode}
%   The hook may take arguments, so we add a run of braced parameters
%   after the \verb|_next| and \verb|_toplevel| macros, so that the
%   arguments passed to the hook are forwarded to them.
%    \begin{macrocode}
                \exp_not:c { @@_toplevel~#1 }
                \@@_braced_parameter:n {#1}
                \exp_not:c { @@_next~#1 }
                \@@_braced_parameter:n {#1}
              }
          }
          {
%    \end{macrocode}
%    By default the algorithm sorts the code chunks and then saves the
%    result in a token list for fast execution; this is done by adding the code chunks
%    one after another, using \cs{tl_gput_right:NV}. When we sort code for
%    a reversed hook, all we have to do is to add the code chunks in
%    the opposite order into the token list. So all we have to do
%    in preparation is to change two definitions that are used later on.
%    \begin{macrocode}
            \@@_if_reversed:nTF {#1}
              { \cs_set_eq:NN \@@_tl_gput:Nn    \@@_tl_gput_left:Nn
                \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_left:NV  }
              { \cs_set_eq:NN \@@_tl_gput:Nn    \@@_tl_gput_right:Nn
                \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV }
%    \end{macrocode}
%
%    When sorting, some relations (namely \verb|voids|) need to
%    act destructively on the code property lists to remove code that
%    shouldn't appear in the sorted hook token list, so we make a copy
%    of the code property list that we can safely work on without
%    changing the main one.
%    \begin{macrocode}
            \prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop }
            \@@_initialize_single:ccn
              { @@~#1 } { g_@@_#1_labels_clist } {#1}
%    \end{macrocode}
%    For debug display we want to keep track of those hooks that
%    actually got code added to them, so we record that in plist. We
%    use a plist to ensure that we record each hook name only once,
%    i.e., we are only interested in storing the keys and the value is
%    arbitrary.
%    \begin{macrocode}
            \@@_debug:n
              { \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop {#1} { } }
          }
      }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_hook_code:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_initialize_hook_code:n #1
%<latexrelease>  {
%<latexrelease>    \@@_debug:n
%<latexrelease>      { \iow_term:x { ^^J Update~code~for~hook~'#1'
%<latexrelease>                      \on@line :^^J } }
%<latexrelease>    \@@_include_legacy_code_chunk:n {#1}
%<latexrelease>    \@@_if_usable:nT {#1}
%<latexrelease>      {
%<latexrelease>        \prop_if_empty:cTF { g_@@_#1_code_prop }
%<latexrelease>          {
%<latexrelease>            \@@_tl_gset:co { @@~#1 }
%<latexrelease>              {
%<latexrelease>                \cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
%<latexrelease>                \cs:w @@_next~#1 \cs_end:
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>          {
%<latexrelease>            \@@_if_reversed:nTF {#1}
%<latexrelease>              { \cs_set_eq:NN \@@_tl_gput:Nn
%<latexrelease>                              \@@_tl_gput_left:Nn
%<latexrelease>                \cs_set_eq:NN \@@_clist_gput:NV
%<latexrelease>                              \clist_gput_left:NV  }
%<latexrelease>              { \cs_set_eq:NN \@@_tl_gput:Nn
%<latexrelease>                              \@@_tl_gput_right:Nn
%<latexrelease>                \cs_set_eq:NN \@@_clist_gput:NV
%<latexrelease>                              \clist_gput_right:NV }
%<latexrelease>            \prop_set_eq:Nc \l_@@_work_prop
%<latexrelease>              { g_@@_#1_code_prop }
%<latexrelease>            \@@_initialize_single:ccn
%<latexrelease>              { @@~#1 } { g_@@_#1_labels_clist } {#1}
%<latexrelease>            \@@_debug:n
%<latexrelease>              { \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop
%<latexrelease>                    {#1} { } }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
% \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n}
%   It is faster to pass a single token and expand it when necessary
%   than to pass a bunch of character tokens around.
%   \fmiinline{note to myself: verify}
%    \begin{macrocode}
\cs_new:Npn \@@_tl_csname:n #1 { l_@@_label_#1_tl }
\cs_new:Npn \@@_seq_csname:n #1 { l_@@_label_#1_seq }
%    \end{macrocode}
% \end{macro}
%
%
%  \begin{macro}{\l_@@_labels_seq,\l_@@_labels_int,\l_@@_front_tl,
%      \l_@@_rear_tl,\l_@@_label_0_tl}
%
%    For the sorting I am basically implementing Knuth's algorithm for
%    topological sorting as given in TAOCP volume 1 pages 263--266.
%    For this algorithm we need a number of local variables:
%    \begin{itemize}
%    \item
%       List of labels used in the current hook to label code chunks:
%    \begin{macrocode}
\seq_new:N \l_@@_labels_seq
%    \end{macrocode}
%    \item
%      Number of labels used in the current hook. In Knuth's algorithm
%      this is called $N$:
%    \begin{macrocode}
\int_new:N \l_@@_labels_int
%    \end{macrocode}
%    \item
%      The sorted code list to be build is managed using two pointers
%      one to the front of the queue and one to the rear. We model this
%      using token list pointers. Knuth calls them $F$ and $R$:
%    \begin{macrocode}
\tl_new:N \l_@@_front_tl
\tl_new:N \l_@@_rear_tl
%    \end{macrocode}
%    \item
%      The data for the start of the queue is kept in this token list,
%      it corresponds to what Don calls \texttt{QLINK[0]} but since we
%      aren't manipulating individual words in memory it is slightly
%      differently done:
%    \begin{macrocode}
\tl_new:c { \@@_tl_csname:n { 0 } }
%    \end{macrocode}
%
%    \end{itemize}
%  \end{macro}
%
%
%  \begin{macro}{\@@_initialize_single:NNn,\@@_initialize_single:ccn}
%
%    \cs{@@_initialize_single:NNn} implements the sorting of the code
%    chunks for a hook and saves the result in the token list for fast
%    execution (\verb=#4=). The arguments are \meta{hook-code-plist},
%    \meta{hook-code-tl}, \meta{hook-top-level-code-tl},
%    \meta{hook-next-code-tl},
%    \meta{hook-ordered-labels-clist} and \meta{hook-name} (the latter
%    is only used for debugging---the \meta{hook-rule-plist} is accessed
%    using the \meta{hook-name}).
%
%    The additional complexity compared to Don's algorithm is that we
%    do not use simple positive integers but have arbitrary
%    alphanumeric labels. As usual Don's data structures are chosen in
%    a way that one can omit a lot of tests and I have mimicked that as
%    far as possible. The result is a restriction I do not test for at
%    the moment: a label can't be equal to the number 0!
%  \fmiinline{Needs checking for, just in case ... maybe}
%
%    ^^A #1 <- \@@~#1
%    ^^A #2 <- \g_@@_#1_labels_clist
%    ^^A #3 <- #1
%
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_single:NNn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
  {
%    \end{macrocode}
%    Step T1: Initialize the data structure \ldots
%    \begin{macrocode}
    \seq_clear:N \l_@@_labels_seq
    \int_zero:N  \l_@@_labels_int
%    \end{macrocode}
%
%    Store the name of the hook:
%    \begin{macrocode}
    \tl_set:Nn \l_@@_cur_hook_tl {#3}
%    \end{macrocode}
%
%    We loop over the property list holding the code and record all
%    the labels listed there. Only the rules for those labels are of interest
%    to us. While we are at it we count them (which gives us the $N$
%    in Knuth's algorithm).  The prefix |label_| is added to the variables
%    to ensure that labels named |front|, |rear|, |labels|, or |return|
%    don't interact with our code.
%    \begin{macrocode}
    \prop_map_inline:Nn \l_@@_work_prop
       {
         \int_incr:N \l_@@_labels_int
         \seq_put_right:Nn \l_@@_labels_seq {##1}
         \@@_tl_set:cn { \@@_tl_csname:n {##1} } { 0 }
         \seq_clear_new:c { \@@_seq_csname:n {##1} }
       }
%    \end{macrocode}
%    Steps T2 and T3: Here we sort the relevant rules into the data structure\ldots
%
%    This loop constitutes a square matrix of the labels in
%    \cs{l_@@_work_prop} in the
%    vertical and the horizontal directions.  However, since the rule
%    $l_A\meta{rel}l_B$ is the same as $l_B\meta{rel}^{-1}l_A$ we can cut
%    the loop short at the diagonal of the matrix (\emph{i.e.}, when
%    both labels are equal), saving a good amount of time.  The way the
%    rules were set up (see the implementation of \cs{@@_rule_before_gset:nnn}
%    above) ensures that we have no rule in the ignored side of the
%    matrix, and all rules are seen.  The rules are applied in
%    \cs{@@_apply_label_pair:nnn}, which takes the properly-ordered pair
%    of labels as argument.
%    \begin{macrocode}
    \prop_map_inline:Nn \l_@@_work_prop
      {
        \prop_map_inline:Nn \l_@@_work_prop
          {
            \@@_if_label_case:nnnnn {##1} {####1}
              { \prop_map_break: }
              { \@@_apply_label_pair:nnn {##1} {####1} }
              { \@@_apply_label_pair:nnn {####1} {##1} }
                  {#3}
          }
      }
%    \end{macrocode}
%    Now take a breath, and look at the data structures that have
%    been set up:
%    \begin{macrocode}
    \@@_debug:n { \@@_debug_label_data:N \l_@@_work_prop }
%    \end{macrocode}
%
%
%    Step T4:
%    \begin{macrocode}
    \tl_set:Nn \l_@@_rear_tl { 0 }
    \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 }
    \seq_map_inline:Nn \l_@@_labels_seq
      {
        \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
            {
              \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } }{##1}
              \tl_set:Nn \l_@@_rear_tl {##1}
            }
      }
    \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } }
%    \end{macrocode}
%
%    \begin{macrocode}
    \@@_tl_gclear:N #1
    \clist_gclear:N #2
%    \end{macrocode}
%
%    The whole loop gets combined in steps T5--T7:
%    \begin{macrocode}
    \bool_while_do:nn { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } }
      {
%    \end{macrocode}
%    This part is step T5:
%    \begin{macrocode}
        \int_decr:N \l_@@_labels_int
        \prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl
        \exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl
%    \end{macrocode}
%
% \changes{v1.1l}{2024/11/26}{Adjust debugging message (gh/1459)}
%    \begin{macrocode}
        \@@_clist_gput:NV #2 \l_@@_front_tl
        \@@_debug:n{ \iow_term:x{[lthooks]~ Handled~ code~ for~ \l_@@_front_tl} }
%    \end{macrocode}
%
%    This is step T6, except that we don't use a pointer $P$ to move
%    through the successors, but instead use \verb=##1= of the mapping
%    function.
%    \begin{macrocode}
        \seq_map_inline:cn { \@@_seq_csname:n { \l_@@_front_tl } }
          {
            \tl_set:cx { \@@_tl_csname:n {##1} }
                       { \int_eval:n
                           { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 }
                       }
            \int_compare:nNnT
                { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
                {
                  \tl_set:cn
                    { \@@_tl_csname:n { \l_@@_rear_tl } } {##1}
                  \tl_set:Nn \l_@@_rear_tl            {##1}
                }
          }
%    \end{macrocode}
%    and here is step T7:
%    \begin{macrocode}
        \tl_set_eq:Nc \l_@@_front_tl
                      { \@@_tl_csname:n { \l_@@_front_tl } }
%    \end{macrocode}
%
%    This is step T8: If we haven't moved the code for all labels
%    (i.e., if \cs{l_@@_labels_int} is still greater than zero) we
%    have a loop and our partial order can't be flattened out.
%    \begin{macrocode}
      }
    \int_compare:nNnF \l_@@_labels_int = 0
      {
        \iow_term:x{====================}
        \iow_term:x{Error:~ label~ rules~ are~ incompatible:}
%    \end{macrocode}
%
%    This is not really the information one needs in the error case
%    but it will do for now \ldots \fmiinline{improve output on a rainy day}
%    \begin{macrocode}
        \@@_debug_label_data:N \l_@@_work_prop
        \iow_term:x{====================}
      }
%    \end{macrocode}
%    After we have added all hook code to \verb=#1=, we finish it off
%    by adding extra code for the |top-level| (\verb=#2=) and for one
%    time execution (\verb=#3=).  These should normally be empty.  The
%    |top-level| code is added with \cs{@@_tl_gput:Nn} as that might
%    change for a reversed hook (then |top-level| is the very first code
%    chunk added).  The |next| code is always added last (to the right).
%   The hook may take arguments, so we add a run of braced parameters
%   after the \verb|_next| and \verb|_toplevel| macros, so that the
%   arguments passed to the hook are forwarded to them.
%    \begin{macrocode}
    \exp_args:NNe \@@_tl_gput:Nn #1
      { \exp_not:c { @@_toplevel~#3 } \@@_braced_parameter:n {#3} }
    \@@_tl_gput_right:Ne #1
      { \exp_not:c { @@_next~#3 } \@@_braced_parameter:n {#3} }
    \use:e
      {
        \cs_gset:cpn { @@~#3 } \use:c { c_@@_#3_parameter_tl }
          { \exp_not:V #1 }
      }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \@@_initialize_single:NNn { cc }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_single:NNn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
%<latexrelease>  {
%<latexrelease>    \seq_clear:N \l_@@_labels_seq
%<latexrelease>    \int_zero:N  \l_@@_labels_int
%<latexrelease>    \tl_set:Nn \l_@@_cur_hook_tl {#3}
%<latexrelease>    \prop_map_inline:Nn \l_@@_work_prop
%<latexrelease>       {
%<latexrelease>         \int_incr:N \l_@@_labels_int
%<latexrelease>         \seq_put_right:Nn \l_@@_labels_seq {##1}
%<latexrelease>         \@@_tl_set:cn { \@@_tl_csname:n {##1} } { 0 }
%<latexrelease>         \seq_clear_new:c { \@@_seq_csname:n {##1} }
%<latexrelease>       }
%<latexrelease>    \prop_map_inline:Nn \l_@@_work_prop
%<latexrelease>      {
%<latexrelease>        \prop_map_inline:Nn \l_@@_work_prop
%<latexrelease>          {
%<latexrelease>            \@@_if_label_case:nnnnn {##1} {####1}
%<latexrelease>              { \prop_map_break: }
%<latexrelease>              { \@@_apply_label_pair:nnn {##1} {####1} }
%<latexrelease>              { \@@_apply_label_pair:nnn {####1} {##1} }
%<latexrelease>                  {#3}
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>    \@@_debug:n
%<latexrelease>      { \@@_debug_label_data:N \l_@@_work_prop }
%<latexrelease>    \tl_set:Nn \l_@@_rear_tl { 0 }
%<latexrelease>    \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 }
%<latexrelease>    \seq_map_inline:Nn \l_@@_labels_seq
%<latexrelease>      {
%<latexrelease>        \int_compare:nNnT
%<latexrelease>          { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
%<latexrelease>          {
%<latexrelease>            \tl_set:cn { \@@_tl_csname:n 
%<latexrelease>                           { \l_@@_rear_tl } } {##1}
%<latexrelease>            \tl_set:Nn \l_@@_rear_tl           {##1}
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>    \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } }
%<latexrelease>    \@@_tl_gclear:N #1
%<latexrelease>    \clist_gclear:N #2
%<latexrelease>    \bool_while_do:nn
%<latexrelease>      { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } }
%<latexrelease>      {
%<latexrelease>        \int_decr:N \l_@@_labels_int
%<latexrelease>        \prop_get:NVN \l_@@_work_prop
%<latexrelease>            \l_@@_front_tl \l_@@_return_tl
%<latexrelease>        \exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl
%<latexrelease>        \@@_clist_gput:NV #2 \l_@@_front_tl
%<latexrelease>        \@@_debug:n{ \iow_term:x
%<latexrelease>              {Handled~ code~ for~ \l_@@_front_tl} }
%<latexrelease>        \seq_map_inline:cn
%<latexrelease>              { \@@_seq_csname:n { \l_@@_front_tl } }
%<latexrelease>          {
%<latexrelease>            \tl_set:cx { \@@_tl_csname:n {##1} }
%<latexrelease>              { \int_eval:n
%<latexrelease>                { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 }
%<latexrelease>              }
%<latexrelease>            \int_compare:nNnT
%<latexrelease>              { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
%<latexrelease>              {
%<latexrelease>                \tl_set:cn { \@@_tl_csname:n
%<latexrelease>                               { \l_@@_rear_tl } } {##1}
%<latexrelease>                \tl_set:Nn \l_@@_rear_tl           {##1}
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>        \tl_set_eq:Nc \l_@@_front_tl
%<latexrelease>          { \@@_tl_csname:n { \l_@@_front_tl } }
%<latexrelease>      }
%<latexrelease>    \int_compare:nNnF \l_@@_labels_int = 0
%<latexrelease>      {
%<latexrelease>        \iow_term:x{====================}
%<latexrelease>        \iow_term:x{Error:~ label~ rules~ are~ incompatible:}
%<latexrelease>        \@@_debug_label_data:N \l_@@_work_prop
%<latexrelease>        \iow_term:x{====================}
%<latexrelease>      }
%<latexrelease>    \exp_args:NNo \@@_tl_gput:Nn #1
%<latexrelease>                    { \cs:w @@_toplevel~#3 \cs_end: }
%<latexrelease>    \@@_tl_gput_right:No #1 { \cs:w @@_next~#3 \cs_end: }
%<latexrelease>  }
%<latexrelease>\cs_generate_variant:Nn \@@_tl_gput_right:Nn { No }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_tl_gput:Nn,\@@_clist_gput:NV}
%    These append either on the right (normal hook) or on the left
%    (reversed hook). This is setup up in
%    \cs{@@_initialize_hook_code:n}, elsewhere their behavior is undefined.
%    \begin{macrocode}
\cs_new:Npn \@@_tl_gput:Nn    { \ERROR }
\cs_new:Npn \@@_clist_gput:NV { \ERROR }
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_apply_label_pair:nnn,\@@_label_if_exist_apply:nnnF}
%
%    This is the payload of steps T2 and T3 executed in the loop described
%    above. This macro assumes |#1| and |#2| are ordered, which means that
%    any rule pertaining the pair |#1| and |#2| is
%    \cs{g_@@_\meta{hook}_rule_\#1\string|\#2_tl}, and not
%    \cs{g_@@_\meta{hook}_rule_\#2\string|\#1_tl}.  This also saves a great deal
%    of time since we only need to check the order of the labels once.
%
%    The arguments here are \meta{label1}, \meta{label2}, \meta{hook}, and
%    \meta{hook-code-plist}.  We are about to apply the next rule and
%    enter it into the data structure.  \cs{@@_apply_label_pair:nnn} will
%    just call \cs{@@_label_if_exist_apply:nnnF} for the \meta{hook}, and
%    if no rule is found, also try the \meta{hook} name \texttt{??}
%    denoting a default hook rule.
%
%    \cs{@@_label_if_exist_apply:nnnF} will check if the rule exists for
%    the given hook, and if so call \cs{@@_apply_rule:nnn}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_apply_label_pair:nnn #1#2#3
  {
%    \end{macrocode}
%    Extra complication: as we use default rules and local hook specific
%    rules we first have to check if there is a local rule and if that
%    exist use it. Otherwise check if there is a default rule and use
%    that.
%    \begin{macrocode}
    \@@_label_if_exist_apply:nnnF {#1} {#2} {#3}
      {
%    \end{macrocode}
%    If there is no hook-specific rule we check for a default one and
%    use that if it exists.
%    \begin{macrocode}
        \@@_label_if_exist_apply:nnnF {#1} {#2} { ?? } { }
      }
  }
\cs_new_protected:Npn \@@_label_if_exist_apply:nnnF #1#2#3
  {
    \if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end:
%    \end{macrocode}
%    What to do precisely depends on the type of rule we have
%    encountered. If it is a \texttt{before} rule it will be handled by the
%    algorithm but other types need to be managed differently. All
%    this is done in \cs{@@_apply_rule:nnnN}.
%    \begin{macrocode}
      \@@_apply_rule:nnn {#1} {#2} {#3}
      \exp_after:wN \use_none:n
    \else:
      \use:nn
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%  \begin{macro}{\@@_apply_rule:nnn}
%    This is the code executed in steps T2 and T3 while looping through
%    the matrix  This is part of step T3. We are about to apply the next
%    rule and enter it into the data structure. The arguments are
%    \meta{label1}, \meta{label2}, \meta{hook-name}, and \meta{hook-code-plist}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_apply_rule:nnn #1#2#3
  {
    \cs:w @@_apply_
      \cs:w g_@@_#3_reversed_tl \cs_end: rule_
        \cs:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end: :nnn \cs_end:
      {#1} {#2} {#3}
  }
%    \end{macrocode}
% \end{macro}
%
%  \begin{macro}{\@@_apply_rule_<:nnn,\@@_apply_rule_>:nnn}
%    The most common cases are \texttt{\string<} and \texttt{\string>} so we handle
%    that first.  They are relations $\prec$ and $\succ$ in TAOCP, and
%    they dictate sorting.
%    \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_<:nnn } #1#2#3
  {
    \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
    \tl_set:cx { \@@_tl_csname:n {#2} }
       { \int_eval:n{ \cs:w \@@_tl_csname:n {#2} \cs_end: + 1 } }
    \seq_put_right:cn{ \@@_seq_csname:n {#1} }{#2}
  }
\cs_new_protected:cpn { @@_apply_rule_>:nnn } #1#2#3
  {
    \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
    \tl_set:cx { \@@_tl_csname:n {#1} }
       { \int_eval:n{ \cs:w \@@_tl_csname:n {#1} \cs_end: + 1 } }
    \seq_put_right:cn{ \@@_seq_csname:n {#2} }{#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_apply_rule_xE:nnn,\@@_apply_rule_xW:nnn}
%   These relations make two labels incompatible within a hook.
%   |xE| makes raises an error if the labels are found in the same
%   hook, and |xW| makes it a warning.
%    \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_xE:nnn } #1#2#3
  {
    \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
    \msg_error:nnnnnn { hooks } { labels-incompatible }
      {#1} {#2} {#3} { 1 }
    \use:c { @@_apply_rule_->:nnn } {#1} {#2} {#3}
    \use:c { @@_apply_rule_<-:nnn } {#1} {#2} {#3}
  }
\cs_new_protected:cpn { @@_apply_rule_xW:nnn } #1#2#3
  {
    \@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
    \msg_warning:nnnnnn { hooks } { labels-incompatible }
      {#1} {#2} {#3} { 0 }
  }
%    \end{macrocode}
% \end{macro}
%
%  \begin{macro}{\@@_apply_rule_->:nnn,\@@_apply_rule_<-:nnn}
%    If we see \texttt{\detokenize{->}} we have to drop code for label
%    \verb=#3= and carry on. We could do a little better and drop
%    everything for that label since it doesn't matter where we put
%    such empty code. However that would complicate the algorithm a
%    lot with little gain.\footnote{This also has the advantage that
%    the result of the sorting doesn't change, as it might otherwise do
%    (for unrelated chunks) if we aren't careful.} So we still
%    unnecessarily try to sort it in and depending on the rules that
%    might result in a loop that is otherwise resolved. If that turns
%    out to be a real issue, we can improve the code.
%
%    Here the code is removed from \cs{l_@@_cur_hook_tl} rather than
%    \verb=#3= because the latter may be \texttt{??}, and the default
%    hook doesn't store any code.  Removing it instead from \cs{l_@@_cur_hook_tl}
%    makes the default rules \verb=->= and  \verb=<-= work properly.
%    \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_->:nnn } #1#2#3
  {
    \@@_debug:n
       {
         \@@_msg_pair_found:nnn {#1} {#2} {#3}
         \iow_term:x{--->~ Drop~ '#2'~ code~ from~
           \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~
           because~ of~ '#1' }
       }
    \prop_put:Nnn \l_@@_work_prop {#2} { }
  }
\cs_new_protected:cpn { @@_apply_rule_<-:nnn } #1#2#3
  {
    \@@_debug:n
       {
         \@@_msg_pair_found:nnn {#1} {#2} {#3}
         \iow_term:x{--->~ Drop~ '#1'~ code~ from~
           \iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~
           because~ of~ '#2' }
       }
    \prop_put:Nnn \l_@@_work_prop {#1} { }
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{
%     \@@_apply_-rule_<:nnn,
%     \@@_apply_-rule_>:nnn,
%     \@@_apply_-rule_<-:nnn,
%     \@@_apply_-rule_->:nnn,
%     \@@_apply_-rule_xW:nnn,
%     \@@_apply_-rule_xE:nnn,
%   }
%   Reversed rules.
%    \begin{macrocode}
\cs_new_eq:cc { @@_apply_-rule_<:nnn } { @@_apply_rule_>:nnn }
\cs_new_eq:cc { @@_apply_-rule_>:nnn } { @@_apply_rule_<:nnn }
\cs_new_eq:cc { @@_apply_-rule_<-:nnn } { @@_apply_rule_<-:nnn }
\cs_new_eq:cc { @@_apply_-rule_->:nnn } { @@_apply_rule_->:nnn }
\cs_new_eq:cc { @@_apply_-rule_xE:nnn } { @@_apply_rule_xE:nnn }
\cs_new_eq:cc { @@_apply_-rule_xW:nnn } { @@_apply_rule_xW:nnn }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_msg_pair_found:nnn}
%   A macro to avoid moving this many tokens around.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_msg_pair_found:nnn #1#2#3
  {
    \iow_term:x{~ \str_if_eq:nnTF {#3} {??} {default} {~normal} ~
        rule~ \@@_label_pair:nn {#1} {#2}:~
        \use:c { g_@@_#3_rule_ \@@_label_pair:nn {#1} {#2} _tl } ~
        found}
  }
%    \end{macrocode}
% \end{macro}
%
%
%  \begin{macro}{\@@_debug_label_data:N}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_debug_label_data:N #1 {
  \iow_term:x{Code~ labels~ for~ sorting:}
  \iow_term:x{~ \seq_use:Nnnn\l_@@_labels_seq {~and~}{,~}{~and~} }
  \iow_term:x{^^J Data~ structure~ for~ label~ rules:}
  \prop_map_inline:Nn #1
       {
         \iow_term:x{~ ##1~ =~ \tl_use:c{ \@@_tl_csname:n {##1} }~ ->~
           \seq_use:cnnn{ \@@_seq_csname:n {##1} }{~->~}{~->~}{~->~}
         }
       }
  \iow_term:x{}
}
%    \end{macrocode}
%  \end{macro}
%
%
%
% \begin{macro}{\hook_show:n,\hook_log:n}
% \changes{v1.1m}{2025/06/27}{Support \cs{showstream} (gh/1062)}
% \begin{macro}{\@@_log_line:x,\@@_log_line_indent:x}
% \begin{macro}{\@@_log:nN}
%   This writes out information about the hook given in its argument
%   onto the \texttt{.log} file and the terminal, if \cs{show_hook:n} is
%   used.  Internally both share the same structure, except that at the
%   end, \cs{hook_show:n} triggers \TeX's prompt.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_log:n #1
  {
    \cs_set_eq:NN \@@_log_cmd:x \iow_log:x
    \@@_normalize_hook_args:Nn \@@_log:nN {#1} \tl_log:x
  }
\cs_if_exist:NTF \iow_show:e
  {
    \cs_new_protected:Npn \hook_show:n #1
      {
        \cs_set_eq:NN \@@_log_cmd:x \iow_show:e
        \@@_normalize_hook_args:Nn \@@_log:nN {#1} \tl_show:x
      }
  }
  {
    \cs_new_protected:Npn \hook_show:n #1
      {
        \cs_set_eq:NN \@@_log_cmd:x \iow_term:x
        \@@_normalize_hook_args:Nn \@@_log:nN {#1} \tl_show:x
      }
  }
\cs_new_protected:Npn \@@_log_line:x #1
  { \@@_log_cmd:x { >~#1 } }
\cs_new_protected:Npn \@@_log_line_indent:x #1
  { \@@_log_cmd:x { >~\@spaces #1 } }
%    \end{macrocode}
%
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args)}
% \changes{v1.1g}{2024/01/03}
%         {Fix expansion of \cs{__hook__print_args:nn} argument (gh/1221)}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_log:nN}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_log:nN #1 #2
  {
    \@@_if_deprecated_generic:nT {#1}
      {
        \@@_deprecated_generic_warn:n {#1}
        \@@_do_deprecated_generic:Nn \@@_log:nN {#1} #2
        \exp_after:wN \use_none:nnnnnnnnn \use_none:nnnnn
      }
    \@@_preamble_hook:n {#1}
    \@@_log_cmd:x
      {
        ^^J ->~The~
        \@@_if_generic:nT {#1} { generic~ }
        hook~'#1'
        \@@_if_disabled:nF {#1}
          {
            \exp_args:Nne \@@_print_args:nn {#1}
              {
                \int_eval:n
                  { \str_count:e { \@@_parameter:n {#1} } / 3 }
              }
          }
        :
      }
%    \end{macrocode}
%
% \changes{v1.1k}{2024/10/29}
%         {Skip mapping over undeclared \cs{g__hook_\meta{hook}_code_prop} (gh/1513)}
%    \begin{macrocode}
    \@@_if_usable:nF {#1}
      { \@@_log_line:x { The~hook~is~not~declared. } }
    \@@_if_disabled:nT {#1}
      { \@@_log_line:x { The~hook~is~disabled. } }
    \hook_if_empty:nTF {#1}
      { #2 { The~hook~is~empty } }
      {
        \@@_log_line:x { Code~chunks: }
        \bool_lazy_or:nnTF
          { ! \prop_if_exist_p:c { g_@@_#1_code_prop } }
          { \prop_if_empty_p:c { g_@@_#1_code_prop } }
          { \@@_log_line_indent:x { --- } }
          {
            \prop_map_inline:cn { g_@@_#1_code_prop }
              {
                \exp_after:wN \cs_set:Npn \exp_after:wN \@@_tmp:w
                  \c_@@_nine_parameters_tl {##2}
                \@@_log_line_indent:x
                  { ##1~->~\cs_replacement_spec:N \@@_tmp:w }
              }
          }
%    \end{macrocode}
%
%    If there is code in the |top-level| token list, print it:
%    \begin{macrocode}
        \@@_log_line:x
          {
            Document-level~(top-level)~code
            \@@_if_usable:nT {#1}
              { ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
          }
        \@@_log_line_indent:x
          {
            \@@_cs_if_empty:cTF { @@_toplevel~#1 }
              { --- }
              { -> ~ \cs_replacement_spec:c { @@_toplevel~#1 } }
          }
%    \end{macrocode}
%
%    \begin{macrocode}
        \@@_log_line:x { Extra~code~for~next~invocation: }
        \@@_log_line_indent:x
          {
            \@@_cs_if_empty:cTF { @@_next~#1 }
              { --- }
%    \end{macrocode}
%
%    If the token list is not empty we want to display it but without
%    the first tokens (the code to clear itself) so we call a helper
%    command to  get rid of them.
%    \begin{macrocode}
              {
                -> ~ \exp_last_unbraced:Nf \@@_log_next_code:w
                  { \cs_replacement_spec:c { @@_next~#1 } }
              }
          }
%    \end{macrocode}
%
%   Loop through the rules in a hook and for every rule found, print it.
%   If no rule is there, print |---|.  The boolean \cs{l_@@_tmpa_bool}
%   here indicates if the hook has no rules.
%    \begin{macrocode}
        \@@_log_line:x { Rules: }
        \bool_set_true:N \l_@@_tmpa_bool
        \@@_list_rules:nn {#1}
          {
            \bool_set_false:N \l_@@_tmpa_bool
            \@@_log_line_indent:x
              {
                ##2~ with~
                \str_if_eq:nnT {##3} {??} { default~ }
                relation~ ##1
              }
          }
        \bool_if:NT \l_@@_tmpa_bool
          { \@@_log_line_indent:x { --- } }
%    \end{macrocode}
%
%   When the hook is declared (that is, the sorting algorithm is applied
%   to that hook) and not empty
%    \begin{macrocode}
        \bool_lazy_and:nnTF
            { \@@_if_usable_p:n {#1} }
            { ! \hook_if_empty_p:n {#1} }
          {
            \@@_log_line:x
              {
                Execution~order
                \bool_if:NTF \l_@@_tmpa_bool
                  { \@@_if_reversed:nT {#1} { ~(after~reversal) } }
                  { ~(after~
                    \@@_if_reversed:nT {#1} { reversal~and~ }
                    applying~rules)
                  } :
              }
            #2 % \tl_show:n
              {
                \@spaces
                \clist_if_empty:cTF { g_@@_#1_labels_clist }
                  { --- }
                  { \clist_use:cn { g_@@_#1_labels_clist } { ,~ } }
              }
          }
          {
            \@@_log_line:x { Execution~order: }
            #2
              {
                \@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1}
                  { code~pool~is~empty }
                  { is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} }
              }
          }
      }
  }
%<latexrelease>\EndIncludeInRelease
%
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_log:nN}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \@@_log:nN #1 #2
%<latexrelease>  {
%<latexrelease>    \@@_if_deprecated_generic:nT {#1}
%<latexrelease>      {
%<latexrelease>        \@@_deprecated_generic_warn:n {#1}
%<latexrelease>        \@@_do_deprecated_generic:Nn \@@_log:nN {#1} #2
%<latexrelease>        \exp_after:wN \use_none:nnnnnnnnn \use_none:nnnnn
%<latexrelease>      }
%<latexrelease>    \@@_preamble_hook:n {#1}
%<latexrelease>    \@@_log_cmd:x
%<latexrelease>      { ^^J ->~The~ \@@_if_generic:nT
%<latexrelease>                      {#1} { generic~ } hook~'#1': }
%<latexrelease>    \@@_if_usable:nF {#1}
%<latexrelease>      { \@@_log_line:x { The~hook~is~not~declared. } }
%<latexrelease>    \@@_if_disabled:nT {#1}
%<latexrelease>      { \@@_log_line:x { The~hook~is~disabled. } }
%<latexrelease>    \hook_if_empty:nTF {#1}
%<latexrelease>      { #2 { The~hook~is~empty } }
%<latexrelease>      {
%<latexrelease>        \@@_log_line:x { Code~chunks: }
%<latexrelease>        \prop_if_empty:cTF { g_@@_#1_code_prop }
%<latexrelease>          { \@@_log_line_indent:x { --- } }
%<latexrelease>          {
%<latexrelease>            \prop_map_inline:cn { g_@@_#1_code_prop }
%<latexrelease>              { \@@_log_line_indent:x
%<latexrelease>                  { ##1~->~\tl_to_str:n {##2} } }
%<latexrelease>          }
%<latexrelease>        \@@_log_line:x
%<latexrelease>          {
%<latexrelease>            Document-level~(top-level)~code
%<latexrelease>            \@@_if_usable:nT {#1}
%<latexrelease>              { ~(executed~
%<latexrelease>                \@@_if_reversed:nTF {#1} {first} {last} ) } :
%<latexrelease>          }
%<latexrelease>        \@@_log_line_indent:x
%<latexrelease>          {
%<latexrelease>            \tl_if_empty:cTF { @@_toplevel~#1 }
%<latexrelease>              { --- }
%<latexrelease>              { -> ~ \exp_args:Nv \tl_to_str:n
%<latexrelease>                                  { @@_toplevel~#1 } }
%<latexrelease>          }
%<latexrelease>        \@@_log_line:x { Extra~code~for~next~invocation: }
%<latexrelease>        \@@_log_line_indent:x
%<latexrelease>          {
%<latexrelease>            \tl_if_empty:cTF { @@_next~#1 }
%<latexrelease>              { --- }
%<latexrelease>              { ->~ \exp_args:Nv \@@_log_next_code:n
%<latexrelease>                                   { @@_next~#1 } }
%<latexrelease>          }
%<latexrelease>        \@@_log_line:x { Rules: }
%<latexrelease>        \bool_set_true:N \l_@@_tmpa_bool
%<latexrelease>        \@@_list_rules:nn {#1}
%<latexrelease>          {
%<latexrelease>            \bool_set_false:N \l_@@_tmpa_bool
%<latexrelease>            \@@_log_line_indent:x
%<latexrelease>              {
%<latexrelease>                ##2~ with~
%<latexrelease>                \str_if_eq:nnT {##3} {??} { default~ }
%<latexrelease>                relation~ ##1
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>        \bool_if:NT \l_@@_tmpa_bool
%<latexrelease>          { \@@_log_line_indent:x { --- } }
%<latexrelease>        \bool_lazy_and:nnTF
%<latexrelease>            { \@@_if_usable_p:n {#1} }
%<latexrelease>            { ! \hook_if_empty_p:n {#1} }
%<latexrelease>          {
%<latexrelease>            \@@_log_line:x
%<latexrelease>              {
%<latexrelease>                Execution~order
%<latexrelease>                \bool_if:NTF \l_@@_tmpa_bool
%<latexrelease>                  { \@@_if_reversed:nT
%<latexrelease>                       {#1}{ ~(after~reversal) } }
%<latexrelease>                  { ~(after~
%<latexrelease>                    \@@_if_reversed:nT {#1} { reversal~and~ }
%<latexrelease>                    applying~rules)
%<latexrelease>                  } :
%<latexrelease>              }
%<latexrelease>            #2 % \tl_show:n
%<latexrelease>              {
%<latexrelease>                \@spaces
%<latexrelease>                \clist_if_empty:cTF { g_@@_#1_labels_clist }
%<latexrelease>                  { --- }
%<latexrelease>                  { \clist_use:cn
%<latexrelease>                      { g_@@_#1_labels_clist } { ,~ } }
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>          {
%<latexrelease>            \@@_log_line:x { Execution~order: }
%<latexrelease>            #2
%<latexrelease>              {
%<latexrelease>                \@spaces Not~set~because~the~hook~
%<latexrelease>                    \@@_if_usable:nTF {#1}
%<latexrelease>                  { code~pool~is~empty }
%<latexrelease>                  { is~\@@_if_disabled:nTF
%<latexrelease>                      {#1} {disabled} {undeclared} }
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
% \begin{macro}{\@@_log_next_code:n}
%    To display the code for next invocation only (i.e., from
%    \cs{AddToHookNext} we have to remove the string
%    \cs{@@_clear_next:n}\Arg{hook}, so the simplest is to use a macro
%    delimited by a \verb|}|$_12$.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_log_next_code:n}
%<latexrelease>                 {Hooks~with~args}
\exp_last_unbraced:NNNNo
\cs_new:Npn \@@_log_next_code:w #1 \c_right_brace_str { }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_log_next_code:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset:Npn \@@_log_next_code:n #1
%<latexrelease>  { \exp_args:No \tl_to_str:n { \use_none:nn #1 } }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_print_args:n}
%    Pretty-prints the number of arguments of a hook.
%    \begin{macrocode}
\cs_new:Npn \@@_print_args:nn #1 #2
  {
    \int_compare:nNnT {#2} > { 0 }
      {
        \@@_if_declared:nT {#1} { \use_none:nnn }
        \@@_if_cmd_hook:nT {#1}
          { \use_i:nnn { ~ (unknown ~ } }
        \use:n { ~ (#2 ~ }
        argument \int_compare:nNnT {#2} > { 1 } { s } )
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_list_rules:nn}
% \begin{macro}{\@@_list_one_rule:nnn,\@@_list_if_rule_exists:nnnF}
%   This macro takes a \meta{hook} and an \meta{inline function} and
%   loops through each pair of \meta{labels} in the \meta{hook}, and if
%   there is a relation between this pair of \meta{labels}, the
%   \meta{inline function} is executed with |#1|${}={}$\meta{relation},
%   |#2|${}={}$\meta{label_1}\verb=|=\meta{label_2},
%   and |#3|${}={}$\meta{hook} (the latter may be the argument |#1| to
%   \cs{@@_list_rules:nn}, or \texttt{??} if it is a default rule).
% \changes{v1.1k}{2024/10/29}
%         {Skip mapping over undeclared \cs{g__hook_\meta{hook}_code_prop} (gh/1513)}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_list_rules:nn #1 #2
  {
    \prop_if_exist:cT { g_@@_#1_code_prop }
      {
        \cs_set_protected:Npn \@@_tmp:w ##1 ##2 ##3 {#2}
        \prop_map_inline:cn { g_@@_#1_code_prop }
          {
            \prop_map_inline:cn { g_@@_#1_code_prop }
              {
                \@@_if_label_case:nnnnn {##1} {####1}
                  { \prop_map_break: }
                  { \@@_list_one_rule:nnn {##1} {####1} }
                  { \@@_list_one_rule:nnn {####1} {##1} }
                      {#1}
              }
          }
      }
  }
%    \end{macrocode}
%
%   These two are quite similar to \cs{@@_apply_label_pair:nnn} and
%   \cs{@@_label_if_exist_apply:nnnF}, respectively, but rather than
%   applying the rule, they pass it to the \meta{inline function}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_list_one_rule:nnn #1#2#3
  {
    \@@_list_if_rule_exists:nnnF {#1} {#2} {#3}
      { \@@_list_if_rule_exists:nnnF {#1} {#2} { ?? } { } }
  }
\cs_new_protected:Npn \@@_list_if_rule_exists:nnnF #1#2#3
  {
    \if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end:
      \exp_args:Nv \@@_tmp:w
        { g_@@_ #3 _rule_ #1 | #2 _tl } { #1 | #2 } {#3}
      \exp_after:wN \use_none:nn
    \fi:
    \use:n
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_debug_print_rules:n}
%   A shorthand for debugging that prints similar to \cs{prop_show:N}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_debug_print_rules:n #1
  {
    \iow_term:n { The~hook~#1~contains~the~rules: }
    \cs_set_protected:Npn \@@_tmp:w ##1
      {
        \@@_list_rules:nn {#1}
          {
            \iow_term:x
              {
                > ##1 {####2} ##1 => ##1 {####1}
                \str_if_eq:nnT {####3} {??} { ~(default) }
              }
          }
      }
    \exp_args:No \@@_tmp:w { \use:nn { ~ } { ~ } }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
%
%  \subsection{Specifying code for next invocation}
%
% \begin{macro}{\hook_gput_next_code:nn}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_gput_next_code_with_args:nn} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gput_next_code:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_gput_next_code:nn #1 #2
  {
    \@@_replacing_args_false:
    \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} {#2}
%    \end{macrocode}
%    
% \changes{v1.1l}{2024/11/26}{Add debugging message (gh/1459)}
%    \begin{macrocode}
    \@@_debug:n{\iow_term:x{[lthooks]~ Add~ to~
                      \@@_if_usable:nF {#1} { undeclared~ }
                      hook~ '#1'~ ( next~ invocation~ only )
                      \on@line
                      ^^J [lthooks] \@spaces
                      <-~ \tl_to_str:n{#2}
      }
    }
%    \end{macrocode}
%    
%    \begin{macrocode}
    \@@_replacing_args_reset:
  }
\cs_new_protected:Npn \hook_gput_next_code_with_args:nn #1 #2
  {
    \@@_replacing_args_true:
    \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} {#2}
%    \end{macrocode}
%    
% \changes{v1.1l}{2024/11/26}{Add debugging message (gh/1459)}
%    \begin{macrocode}
    \@@_debug:n{\iow_term:x{[lthooks]~ Add~ to~
                      \@@_if_usable:nF {#1} { undeclared~ }
                      hook~ '#1'~ ( next~ invocation~ only )
                      \on@line
                      ^^J [lthooks] \@spaces
                      <-~ \tl_to_str:n{#2}
      }
    }
%    \end{macrocode}
%    
%    \begin{macrocode}
    \@@_replacing_args_reset:
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_gput_next_code:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \hook_gput_next_code:nn #1
%<latexrelease>  { \@@_normalize_hook_args:Nn
%<latexrelease>        \@@_gput_next_code:nn {#1} }
%<latexrelease>\cs_gset_protected:Npn \hook_gput_next_code_with_args:nn #1 #2 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_gput_next_code:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
  {
    \@@_if_disabled:nTF {#1}
      { \msg_error:nnn { hooks } { hook-disabled } {#1} }
      {
        \@@_if_structure_exist:nTF {#1}
          { \@@_gput_next_do:nn }
          { \@@_try_declaring_generic_next_hook:nn }
              {#1} {#2}
      }
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_gput_next_do:nn}
%   Start by sanity-checking with \cs{@@_chk_args_allowed:nn}.
%   Then check if the ``next code'' token list is empty:  if so we need
%   to add a \cs{tl_gclear:c} to clear it, so the code lasts for one
%   usage only.  The token list is cleared early so that nested usages
%   don't get lost.  \cs{tl_gclear:c} is used instead of
%   \cs{tl_gclear:N} in case the hook is used in an expansion-only
%   context, so the token list doesn't expand before \cs{tl_gclear:N}:
%   that would make an infinite loop.  Also in case the main code token
%   list is empty, the hook code has to be updated to add the next
%   execution token list.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
% \changes{v1.1c}{2023/04/19}
%         {Initialize hook structure when adding 'next' code (gh/1052).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_gput_next_do:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_gput_next_do:nn #1
  {
    \@@_init_structure:n {#1}
    \@@_chk_args_allowed:nn {#1} { AddToHookNext }
    \@@_cs_if_empty:cT { @@~#1 }
      { \@@_update_hook_code:n {#1} }
    \@@_cs_if_empty:cT { @@_next~#1 }
      { \@@_next_gset:nn {#1} { \@@_clear_next:n {#1} } }
    \@@_cs_gput_right:nnn { _next } {#1}
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_gput_next_do:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_gput_next_do:nn #1
%<latexrelease>  {
%<latexrelease>    \exp_args:Nc \@@_gput_next_do:Nnn
%<latexrelease>      { @@_next~#1 } {#1}
%<latexrelease>  }
%<latexrelease>\cs_gset_protected:Npn \@@_gput_next_do:Nnn #1 #2
%<latexrelease>  {
%<latexrelease>    \tl_if_empty:cT { @@~#2 }
%<latexrelease>      { \@@_update_hook_code:n {#2} }
%<latexrelease>    \tl_if_empty:NT #1
%<latexrelease>      { \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
%<latexrelease>    \@@_tl_gput_right:Nn #1
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\hook_gclear_next_code:n}
%    Discard anything set up for next invocation of the hook.
% \changes{v1.0o}{2021/07/27}{Macro made public}
%    \begin{macrocode}
\cs_new_protected:Npn \hook_gclear_next_code:n #1
  { \@@_normalize_hook_args:Nn \@@_clear_next:n {#1} }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\@@_clear_next:n}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_clear_next:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_clear_next:n #1
  { \@@_next_gset:nn {#1} { } }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_clear_next:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_clear_next:n #1
%<latexrelease>  { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Using the hook}
%
% \begin{macro}{\hook_use:n}
% \begin{macro}[EXP]{\@@_use_initialized:n}
% \begin{macro}{\@@_preamble_hook:n}
%   \cs{hook_use:n} as defined here is used in the preamble, where
%   hooks aren't initialized by default.  \cs{@@_use_initialized:n} is
%   also defined, which is the non-\tn{protected} version for use within
%   the document.  Their definition is identical, except for the
%   \cs{@@_preamble_hook:n} (which wouldn't hurt in the expandable
%   version, but it would be an unnecessary extra expansion).
%
%   \cs{@@_use_initialized:n} holds the expandable definition while in
%   the preamble. \cs{@@_preamble_hook:n} initializes the hook in the
%   preamble, and is redefined to \cs{use_none:n} at |\begin{document}|.
%
%   Both versions do the same thing internally: they check that the hook exists as
%   given, and if so they use it as quickly as possible.
%
%   At |\begin{document}|, all hooks are initialized, and any change in
%   them causes an update, so \cs{hook_use:n} can be made expandable.
%   This one is better not protected so that it can expand into nothing
%   if containing no code. Also important in case of generic hooks that
%   we do not generate a \cs[no-index]{relax} as a side effect of
%   checking for a csname. In contrast to the \TeX{} low-level
%   \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is
%   careful to avoid this.
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_use:n #1
  {
    \@@_preamble_hook:n {#1}
    \@@_use_initialized:n {#1}
  }
\cs_new:Npn \@@_use_initialized:n #1
  {
    \if_cs_exist:w @@~#1 \cs_end:
      \cs:w @@~#1 \use_i:nn
    \fi:
    \use_none:n
    \cs_end:
  }
\cs_new_protected:Npn \@@_preamble_hook:n #1
  {
    \if_cs_exist:w @@~#1 \cs_end:
      \@@_initialize_hook_code:n {#1}
    \fi:
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\hook_use:n}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_new_protected:Npn \hook_use:n #1
%<latexrelease>  {
%<latexrelease>    \tl_if_exist:cT { @@~#1 }
%<latexrelease>      {
%<latexrelease>        \@@_preamble_hook:n {#1}
%<latexrelease>        \cs:w @@~#1 \cs_end:
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\cs_new:Npn \@@_use_initialized:n #1
%<latexrelease>  {
%<latexrelease>    \if_cs_exist:w @@~#1 \cs_end:
%<latexrelease>      \cs:w @@~#1 \exp_after:wN \cs_end:
%<latexrelease>    \fi:
%<latexrelease>  }
%<latexrelease>\cs_new_protected:Npn \@@_preamble_hook:n #1
%<latexrelease>  { \@@_initialize_hook_code:n {#1} }
%<latexrelease>\cs_new:Npn \hook_use:nnw #1 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use:n}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_new_protected:Npn \hook_use:n #1
%<latexrelease>  {
%<latexrelease>    \tl_if_exist:cTF { @@~#1 }
%<latexrelease>      {
%<latexrelease>        \@@_preamble_hook:n {#1}
%<latexrelease>        \cs:w @@~#1 \cs_end:
%<latexrelease>      }
%<latexrelease>      { \@@_use:wn #1 / \s_@@_mark {#1} }
%<latexrelease>  }
%<latexrelease>\cs_new:Npn \@@_use_initialized:n #1
%<latexrelease>  {
%<latexrelease>    \if_cs_exist:w @@~#1 \cs_end:
%<latexrelease>    \else:
%<latexrelease>      \@@_use_undefined:w
%<latexrelease>    \fi:
%<latexrelease>    \cs:w @@~#1 \@@_use_end:
%<latexrelease>  }
%<latexrelease>\cs_new:Npn \@@_use_undefined:w
%<latexrelease>    #1 #2 @@~#3 \@@_use_end:
%<latexrelease>  {
%<latexrelease>    #1 % fi
%<latexrelease>    \@@_use:wn #3 / \s_@@_mark {#3}
%<latexrelease>  }
%<latexrelease>\cs_new_protected:Npn \@@_preamble_hook:n #1
%<latexrelease>  { \@@_initialize_hook_code:n {#1} }
%<latexrelease>\cs_new_eq:NN \@@_use_end: \cs_end:
%<latexrelease>\cs_new:Npn \hook_use:nnw #1 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}{\hook_use:nnw}
% \begin{macro}[EXP]{\@@_use_initialized:nnw}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_use:nnw} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use:nnw}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_use:nnw #1
  {
    \@@_preamble_hook:n {#1}
    \@@_use_initialized:nnw {#1}
  }
\cs_new:Npn \@@_use_initialized:nnw #1 #2
  {
    \cs:w
      \if_cs_exist:w @@~#1 \cs_end:
        @@~#1
      \else:
        use_none: \prg_replicate:nn {#2} { n }
      \fi:
    \cs_end:
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use:nnw}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset:Npn \hook_use:nnw #1 #2
%<latexrelease>  { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}{\@@_post_initialization_defs:}
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_post_initialization_defs:}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_post_initialization_defs:
  {
    \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
    \cs_gset_eq:NN \hook_use:nnw \@@_use_initialized:nnw
    \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
    \cs_gset_eq:NN \@@_post_initialization_defs: \prg_do_nothing:
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_post_initialization_defs:}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_post_initialization_defs:
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\@@_use:wn}
% \begin{macro}{\@@_try_file_hook:n,\@@_if_usable_use:n}
%   \cs{@@_use:wn} does a quick check to test if the current hook is a
%   file hook: those need a special treatment.  If it is not, the hook
%   does not exist.  If it is, then \cs{@@_try_file_hook:n} is called,
%   and checks that the current hook is a file-specific hook using
%   \cs{@@_if_file_hook:wTF}.  If it's not, then it's a generic |file/|
%   hook and is used if it exist.
%
%   If it is a file-specific hook, it passes through the same
%   normalization as during declaration, and then it is used if defined.
%   \cs{@@_if_usable_use:n} checks if the hook exist, and calls
%   \cs{@@_preamble_hook:n} if so, then uses the hook.
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_use:wn}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use:wn}
%<latexrelease>                 {Standardize~generic~hook~names}
%<latexrelease>\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3
%<latexrelease>  {
%<latexrelease>    \str_if_eq:nnTF {#1} { file }
%<latexrelease>      { \@@_try_file_hook:n {#3} }
%<latexrelease>      { } % Hook doesn't exist
%<latexrelease>  }
%    \end{macrocode}
%
% \changes{v1.0s}{2021/09/28}
%                {Correct usage of older \cs{__hook_if_file_hook:wTF} (gh/675)}
% \changes{v1.1h}{2024/01/24}
%                {Correct usage of older \cs{__hook_if_file_hook:wTF} (gh/1243)}
%    \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_try_file_hook:n #1
%<latexrelease>  {
%<latexrelease>    \@@_if_file_hook:wTF #1 / / \s_@@_mark
%<latexrelease>      {
%<latexrelease>        \exp_args:Ne \@@_if_usable_use:n
%<latexrelease>          { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
%<latexrelease>      }
%<latexrelease>      { \@@_if_usable_use:n {#1} }
%<latexrelease>            % file/ generic hook (e.g. file/before)
%<latexrelease>  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_if_usable_use:n #1
%<latexrelease>  {
%<latexrelease>    \tl_if_exist:cT { @@~#1 }
%<latexrelease>      {
%<latexrelease>        \@@_preamble_hook:n {#1}
%<latexrelease>        \cs:w @@~#1 \cs_end:
%<latexrelease>      }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hook_use_once:n,\hook_use_once:nnw}
%   For hooks that can and should be used only once we have a special
%   use command that further inhibits the hook from getting more code
%   added to it.  This has the effect that any
%   further code added to the hook is executed immediately rather
%   than stored in the hook.
%
%   The code needs some gymnastics to prevent space trimming from the
%   hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are
%   documented to not trim spaces.
%
% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{hook_use_once:nnw} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use_once:nnw}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \hook_use_once:n #1
  {
    \@@_if_execute_immediately:nF {#1}
      { \@@_normalize_hook_args:Nn \@@_use_once:nn
          { \use:n {#1} } { 0 } }
  }
\cs_new_protected:Npn \hook_use_once:nnw #1 #2
  {
    \@@_if_execute_immediately:nF {#1}
      { \@@_normalize_hook_args:Nn \@@_use_once:nn
          { \use:n {#1} } {#2} }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use_once:nnw}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \hook_use_once:n #1
%<latexrelease>  {
%<latexrelease>    \@@_if_execute_immediately:nF {#1}
%<latexrelease>      { \@@_normalize_hook_args:Nn \@@_use_once:n
%<latexrelease>          { \use:n {#1} } }
%<latexrelease>  }
%<latexrelease>\cs_gset:Npn \hook_use_once:nnw #1 #2
%<latexrelease>  { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
% \begin{macro}{\@@_use_once:nn}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_use_once:nn}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_use_once:nn #1 #2
  {
    \@@_preamble_hook:n {#1}
    \@@_use_once_set:n {#1}
%    \end{macrocode}
%   When a hook has arguments, the call to \cs{@@_use_initialized:n},
%   should be the very last thing to happen, otherwise the arguments
%   grabbed will be wrong.  So, to clean up after the hook we need to
%   cheat a bit and sneak the cleanup code at the end of the hook,
%   along with the next execution code.
%    \begin{macrocode}
    \@@_replacing_args_false:
    \@@_cs_gput_right:nnn { _next } {#1}
      { \@@_use_once_clear:n {#1} }
    \@@_replacing_args_reset:
    \@@_if_usable:nTF {#1}
      { \@@_use_initialized:n {#1} }
      {
        \int_compare:nNnT {#2} > { 0 }
          { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
      }
  }
%<latexrelease>\EndIncludeInRelease
%
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use_once:nn}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_gset_protected:Npn \@@_use_once:n #1
%<latexrelease>  {
%<latexrelease>    \@@_preamble_hook:n {#1}
%<latexrelease>    \@@_use_once_set:n {#1}
%<latexrelease>    \@@_use_initialized:n {#1}
%<latexrelease>    \@@_use_once_clear:n {#1}
%<latexrelease>  }
%<latexrelease>\cs_undefine:N \@@_use_once:nn
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_use_once_set:n}
% \begin{macro}{\@@_use_once_clear:n}
%   \cs{@@_use_once_set:n} is used before the actual hook code is
%   executed so that any usage of \cs{AddToHook} inside the hook causes
%   the code to execute immediately.  Setting
%   \cs[no-index]{g_@@_\meta{hook}_reversed_tl} to |I| prevents further
%   code from being added to the hook.  \cs{@@_use_once_clear:n} then
%   clears the hook so that any further call to \cs{hook_use:n} or
%   \cs{hook_use_once:n} will expand to nothing.
% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)}
% \changes{v1.0u}{2022/05/13}{Check if prop exists to avoid l3debug error}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_use_once_clear:n}
%<latexrelease>                 {Hooks~with~args}
\cs_new_protected:Npn \@@_use_once_set:n #1
  { \@@_tl_gset:cn { g_@@_#1_reversed_tl } { I } }
\cs_new_protected:Npn \@@_use_once_clear:n #1
  {
    \@@_code_gset:nn {#1} { }
    \@@_next_gset:nn {#1} { }
    \@@_toplevel_gset:nn {#1} { }
    \prop_gclear_new:c { g_@@_#1_code_prop }
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use_once_clear:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \@@_use_once_clear:n #1
%<latexrelease>  {
%<latexrelease>    \@@_tl_gclear:c { @@~#1 }
%<latexrelease>    \@@_tl_gclear:c { @@_next~#1 }
%<latexrelease>    \@@_tl_gclear:c { @@_toplevel~#1 }
%<latexrelease>    \prop_gclear_new:c { g_@@_#1_code_prop }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_execute_immediately:n}
%   To check whether the code being added should be executed immediately
%   (that is, if the hook is a one-time hook), we check if
%   \cs[no-index]{g_@@_\meta{hook}_reversed_tl} is |I|.  The gymnastics
%   around \cs{if:w} is there to allow the |reversed| token list to be
%   empty.
% \changes{v1.0r}{2021/09/06}{Macro added (gh/606)}
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_execute_immediately:n #1 { T, F, TF }
  {
    \exp_after:wN \@@_use_none_delimit_by_s_mark:w
    \if:w I
        \if_cs_exist:w g_@@_#1_reversed_tl \cs_end:
          \cs:w g_@@_#1_reversed_tl \exp_after:wN \cs_end:
        \fi:
        X
      \s_@@_mark \prg_return_true:
    \else:
      \s_@@_mark \prg_return_false:
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
% \subsection{Querying a hook}
%
% Simpler data types, like token lists, have three possible states; they
% can exist and be empty, exist and be non-empty, and they may not
% exist, in which case emptiness doesn't apply (though
% \cs{tl_if_empty:N} returns false in this case).
%
% Hooks are a bit more complicated: they have several other states as
%    discussed in \ref{sec:existence}.
% A hook may exist or not, and either way it may or may not be empty
% (even a hook that doesn't exist may be non-empty) or may be disabled.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list.  The hook
% doesn't need to be declared to have code added to its code pool
% (it may happen that a package $A$ defines a hook \hook{foo}, but
% it's loaded after package $B$, which adds some code to that hook.
% In this case it is important that the code added by package $B$ is
% remembered until package $A$ is loaded).
%
%    All other states can only be queried with internal tests as the
%    different states are irrelevant for package code.
%
% \begin{macro}[pTF]{\hook_if_empty:n}
%   Test if a hook is empty (that is, no code was added to that hook).
%   A \meta{hook} being empty means that all three of its
%   \cs{g_@@_\meta{hook}_code_prop}, its
%   \cs{@@_toplevel\textvisiblespace\meta{hook}} and its
%   \cs{@@_next\textvisiblespace\meta{hook}} are empty.
% \changes{v1.1a}{2023/04/06}
%         {Changes to add hook arguments (hook-args).}
% \changes{v1.1c}{2023/04/19}
%         {Simpler and faster version (gh/1052).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_if_empty:n}
%<latexrelease>                 {Hooks~with~args}
\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
  {
    \if:w
        T
        \prop_if_exist:cT { g_@@_#1_code_prop }
          { \prop_if_empty:cF { g_@@_#1_code_prop } { F } }
        \@@_cs_if_empty:cF { @@_toplevel~#1 } { F }
        \@@_cs_if_empty:cF { @@_next~#1 } { F }
        T
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_if_empty:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
%<latexrelease>  {
%<latexrelease>    \@@_if_structure_exist:nTF {#1}
%<latexrelease>      {
%<latexrelease>        \bool_lazy_and:nnTF
%<latexrelease>            { \prop_if_empty_p:c { g_@@_#1_code_prop } }
%<latexrelease>            {
%<latexrelease>              \bool_lazy_and_p:nn
%<latexrelease>                { \tl_if_empty_p:c { @@_toplevel~#1 } }
%<latexrelease>                { \tl_if_empty_p:c { @@_next~#1 } }
%<latexrelease>            }
%<latexrelease>          { \prg_return_true: }
%<latexrelease>          { \prg_return_false: }
%<latexrelease>      }
%<latexrelease>      { \prg_return_true: }
%<latexrelease>  }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[pTF]{\@@_if_usable:n}
%   A hook is usable if the
%   token list that stores the sorted code for that hook,
%   \cs[no-index]{@@\textvisiblespace\meta{hook}}, exists.  The property
%   list \cs[no-index]{g_@@_\meta{hook}_code_prop} cannot be used here
%   because often it is necessary to add code to a hook without knowing
%   if such hook was already declared, or even if it will ever be
%   (for example, in case the package that defines it isn't loaded).
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_usable:n #1 { p , T , F , TF }
  {
    \cs_if_exist:cTF { @@~#1 }
      { \prg_return_true: }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_structure_exist:n}
%
%    An internal check if the hook has already its basic internal
%    structure set up with
%   \cs{@@_init_structure:n}.  This means that the hook was already used somehow
%   (a code chunk or rule was added to it), but it still wasn't declared
%   with \cs{hook_new:n}.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_structure_exist:n #1 { p , T , F , TF }
  {
    \prop_if_exist:cTF { g_@@_#1_code_prop }
      { \prg_return_true: }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
%
%  \begin{macro}[pTF]{\@@_if_declared:n}
%
%    Internal test to check if the hook was officially declared with
%    \cs{hook_new:n} or a variant.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_declared:n #1 { p, T, F, TF }
  {
    \tl_if_exist:cTF { g_@@_#1_declared_tl }
      { \prg_return_true: }
      { \prg_return_false: }
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[pTF]{\@@_if_reversed:n}
%   An internal conditional that checks if a hook is reversed.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_reversed:n #1 { p , T , F , TF }
  {
    \exp_after:wN \@@_use_none_delimit_by_s_mark:w
    \if:w - \cs:w g_@@_#1_reversed_tl \cs_end:
      \s_@@_mark \prg_return_true:
    \else:
      \s_@@_mark \prg_return_false:
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[pTF]{\@@_if_generic:n}
% \begin{macro}[pTF]{\@@_if_deprecated_generic:n}
%   An internal conditional that checks if a name belongs to a generic
%   hook.  The deprecated version needs to check if |#3| is empty to
%   avoid returning true on \hook{file/before}, for example.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_generic:n #1 { T, TF }
  { \@@_if_generic:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_if_generic:w #1 / #2 / #3 / #4 \s_@@_mark
  {
    \cs_if_exist:cTF { c_@@_generic_#1/./#3_tl }
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_new_conditional:Npnn \@@_if_deprecated_generic:n #1 { T, TF }
  { \@@_if_deprecated_generic:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_if_deprecated_generic:w #1 / #2 / #3 / #4 \s_@@_mark
  {
    \cs_if_exist:cTF { c_@@_deprecated_#1/./#2_tl }
      {
        \tl_if_empty:nTF {#3}
          { \prg_return_false: }
          { \prg_return_true: }
      }
      { \prg_return_false: }
  }
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%
% \begin{macro}[pTF]{\@@_if_cmd_hook:n}
% \begin{macro}[pTF]{\@@_if_cmd_hook:w}
%   An internal conditional that checks if a given hook is a valid
%   generic \hook{cmd} hook.
% \changes{v1.1d}{2023/05/21}
%         {Changes to allow support arguments in cmd hooks (cmd-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_if_cmd_hook:n}
%<latexrelease>                 {Hooks~with~args}
\prg_new_conditional:Npnn \@@_if_cmd_hook:n #1 { T }
  { \@@_if_cmd_hook:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_if_cmd_hook:w #1 / #2 / #3 / #4 \s_@@_mark
  {
    \if:w Y
          \str_if_eq:nnF {#1} { cmd } { N }
          \tl_if_exist:cF { c_@@_generic_#1/./#3_tl } { N }
          Y
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_if_cmd_hook:n}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_undefine:N \@@_if_cmd_hook:nT
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%
% \begin{macro}[pTF]{\@@_if_generic_reversed:n}
%   An internal conditional that checks if a name belongs to a generic
%   reversed hook.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_generic_reversed:n #1 { T }
  { \@@_if_generic_reversed:w #1 / / / \scan_stop: }
\cs_new:Npn \@@_if_generic_reversed:w #1 / #2 / #3 / #4 \scan_stop:
  {
    \if_charcode:w - \cs:w c_@@_generic_#1/./#3_tl \cs_end:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[EXP]{\@@_if_replacing_args:TF}
% \begin{macro}[EXP]{\@@_misused_if_replacing_args:nn}
% \begin{macro}{\@@_replacing_args_true:}
% \begin{macro}{\@@_replacing_args_false:}
% \begin{macro}{\@@_replacing_args_reset:}
% \begin{macro}{\g_@@_replacing_stack_seq}
%   An internal conditional that checks if the code being added to the
%   hook contains arguments.
% \changes{v1.1a}{2023/04/06}
%         {Macro added (hook-args).}
%    \begin{macrocode}
\seq_new:N \g_@@_replacing_stack_seq
\cs_new:Npn \@@_misused_if_replacing_args:nn #1 #2
  {
    \msg_expandable_error:nnn { latex2e } { should-not-happen }
      { Misused~\@@_if_replacing_args:. }
  }
\cs_new:Npn \@@_if_replacing_args:TF
  { \@@_misused_if_replacing_args:nn }
\cs_new_protected:Npn \@@_replacing_args_true:
  {
    \seq_gpush:No \g_@@_replacing_stack_seq
      { \@@_if_replacing_args:TF }
    \cs_set:Npn \@@_if_replacing_args:TF { \use_i:nn }
  }
\cs_new_protected:Npn \@@_replacing_args_false:
  {
    \seq_gpush:No \g_@@_replacing_stack_seq
      { \@@_if_replacing_args:TF }
    \cs_set:Npn \@@_if_replacing_args:TF { \use_ii:nn }
  }
\cs_new_protected:Npn \@@_replacing_args_reset:
  {
    \seq_gpop:NN \g_@@_replacing_stack_seq \l_@@_return_tl
    \cs_gset_eq:NN \@@_if_replacing_args:TF \l_@@_return_tl
  }
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%
%  \subsection{Messages}
%
%    Hook errors are LaTeX kernel errors:
%    \begin{macrocode}
\prop_gput:Nnn \g_msg_module_type_prop { hooks } { LaTeX }
%    \end{macrocode}
% \changes{v1.0q}{2021/08/27}{Internal message name changes}
%    And so are kernel errors (this should move elsewhere eventually).
%    \begin{macrocode}
\prop_gput:Nnn \g_msg_module_type_prop { latex2e } { LaTeX }
\prop_gput:Nnn \g_msg_module_name_prop { latex2e } { kernel }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { labels-incompatible }
  {
    Labels~'#1'~and~'#2'~are~incompatible
    \str_if_eq:nnF {#3} {??} { ~in~hook~'#3' } .~
    \int_compare:nNnTF {#4} = { 1 }
      { The~ code~ for~ both~ labels~ will~ be~ dropped. }
      { You~ may~ see~ errors~ later. }
  }
  { LaTeX~found~two~incompatible~labels~in~the~same~hook.~
    This~indicates~an~incompatibility~between~packages.  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { exists }
    { Hook~'#1'~ has~ already~ been~ declared. }
    { There~ already~ exists~ a~ hook~ declaration~ with~ this~
      name.\\
      Please~ use~ a~ different~ name~ for~ your~ hook.}
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{too-many-args}
%<latexrelease>                 {Hooks~with~args}
%    \end{macrocode}
%
% \changes{v1.1a}{2023/04/06}
%         {Messages 'too-many-args', 'without-args' and 'one-time-args' added (hook-args).}
%    \begin{macrocode}
\msg_new:nnnn { hooks } { too-many-args }
  { Too~many~arguments~for~hook~'#1'. }
  {
    You~tried~to~declare~a~hook~with~#2~arguments,~but~a~
    hook~can~only~have~up~to~nine.~LaTeX~will~define~this~
    hook~with~nine~arguments.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { without-args }
  { Hook~'#1'~has~no~arguments. }
  {
    You~tried~to~use~\iow_char:N\\#2WithArguments~
    on~a~hook~that~takes~no~arguments.\\
    Check~the~usage~of~the~hook~or~use~\iow_char:N\\#2~instead.\\
    \\
    LaTeX~will~use~\iow_char:N\\#2.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { one-time-args }
  { You~can't~have~arguments~in~used~one-time~hook~'#1'. }
  {
    You~tried~to~use~\iow_char:N\\#2WithArguments~
    on~a~one-time~hook~that~has~already~been~used.~
    You~have~to~add~the~code~before~the~hook~is~used,~
    or~add~the~code~without~arguments~using~\iow_char:N\\#2~instead.\\
    \\
    LaTeX~will~use~\iow_char:N\\#2.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{too-many-args}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { hook-disabled }
  { Cannot~add~code~to~disabled~hook~'#1'. }
  {
    The~hook~'#1'~you~tried~to~add~code~to~was~previously~disabled~
    with~\iow_char:N\\hook_disable_generic:n~or~
    \iow_char:N\\DisableGenericHook,~so~
    it~cannot~have~code~added~to~it.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { empty-label }
  {
    Empty~code~label~\msg_line_context:.~
    Using~'\@@_currname_or_default:'~instead.
  }
%    \end{macrocode}
%
% \changes{v1.1i}{2024/08/09}{New message ``empty-hook'' (gh/1423).}
%    \begin{macrocode}
\msg_new:nnn { hooks } { empty-hook }
  {
    Empty~hook~name~\msg_line_context:.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { no-default-label }
  {
    Missing~(empty)~default~label~\msg_line_context:. \\
    This~command~was~ignored.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { unknown-rule }
  {
    Unknown~ relationship~ '#3'~
    between~ labels~ '#2'~ and~ '#4'~
    \str_if_eq:nnF {#1} {??} { ~in~hook~'#1' }. ~
    Perhaps~ a~ misspelling?
  }
  {
    The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~
    'before'~ or~ '<',~
    'after'~ or~ '>',~
    'incompatible-warning',~
    'incompatible-error',~
    'voids'~ or~
    'unrelated'.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { rule-too-late }
  {
    Sorting~rule~for~'#1'~hook~applied~too~late.\\
    Try~setting~this~rule~earlier.
  }
  {
    You~tried~to~set~the~ordering~of~hook~'#1'~using\\
    \ \ \iow_char:N\\DeclareHookRule{#1}{#2}{#3}{#4}\\
    but~hook~'#1'~was~already~used~as~a~one-time~hook,~
    thus~sorting~is\\
    no~longer~possible.~Declare~the~rule~
    before~the~hook~is~used.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnnn { hooks } { misused-top-level }
  {
    Illegal~use~of~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
    'top-level'~is~reserved~for~the~user's~document.
  }
  {
    The~'top-level'~label~is~meant~for~user~code~only,~and~should~only~
    be~used~(sparingly)~in~the~main~document.~Use~the~default~label~
    '\@@_currname_or_default:'~for~this~\@cls@pkg,~or~another~
    suitable~label.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { set-top-level }
  {
    You~cannot~change~the~default~label~#1~'top-level'.~Illegal \\
    \use:nn { ~ } { ~ } \iow_char:N \\#2{#3} \\
    \msg_line_context:.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { extra-pop-label }
  {
    Extra~\iow_char:N \\PopDefaultHookLabel. \\
    This~command~will~be~ignored.
  }
\msg_new:nnn { hooks } { missing-pop-label }
  {
    Missing~\iow_char:N \\PopDefaultHookLabel. \\
    The~label~'#1'~was~pushed~but~never~popped.~Something~is~wrong.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { latex2e } { should-not-happen }
  {
    This~should~not~happen.~#1 \\
    Please~report~at~https://github.com/latex3/latex2e.
  }
%    \end{macrocode}
%
%
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { activate-disabled }
  {
    Cannot~ activate~ hook~ '#1'~ because~ it~ is~ disabled!
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { cannot-remove }
  {
    Cannot~remove~chunk~'#2'~from~hook~'#1'~because~
    \@@_if_structure_exist:nTF {#1}
      { it~does~not~exist~in~that~hook. }
      { the~hook~does~not~exist. }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { hooks } { generic-deprecated }
  {
    Generic~hook~'#1/#2/#3'~is~deprecated. \\
    Use~hook~'#1/#3/#2'~instead.
  }
%    \end{macrocode}
%
%
%  \subsection{\LaTeXe{} package interface commands}
%
%
%
%  \begin{macro}{\NewHook,\NewReversedHook,\NewMirroredHookPair}
%    Declaring new hooks \ldots
%    \begin{macrocode}
\NewDocumentCommand \NewHook             { m }
  { \hook_new:n {#1} }
\NewDocumentCommand \NewReversedHook     { m }
  { \hook_new_reversed:n {#1} }
\NewDocumentCommand \NewMirroredHookPair { mm }
  { \hook_new_pair:nn {#1}{#2} }
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{
%    \NewHookWithArguments,
%    \NewReversedHookWithArguments,
%    \NewMirroredHookPairWithArguments
%  }
%    Declaring new hooks with arguments\ldots
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{NewHookWithArguments} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\NewHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
\NewDocumentCommand \NewHookWithArguments             { mm }
  { \hook_new_with_args:nn {#1} {#2} }
\NewDocumentCommand \NewReversedHookWithArguments     { mm }
  { \hook_new_reversed_with_args:nn {#1} {#2} }
\NewDocumentCommand \NewMirroredHookPairWithArguments { mmm }
  { \hook_new_pair_with_args:nnn {#1} {#2} {#3} }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\NewHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \NewHookWithArguments #1 #2 { }
%<latexrelease>\cs_new_protected:Npn \NewReversedHookWithArguments #1 #2 { }
%<latexrelease>\cs_new_protected:Npn \NewMirroredHookPairWithArguments #1 #2 #3{}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}{\ActivateGenericHook}
%<latexrelease>                 {Providing~hooks}
%    \end{macrocode}
%
%  \begin{macro}{\ActivateGenericHook}
%    Providing new hooks \ldots
% \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.}
% \changes{v1.0o}{2021/08/02}{Change name}
%    \begin{macrocode}
\NewDocumentCommand \ActivateGenericHook { m }
  { \hook_activate_generic:n {#1} }
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\DisableGenericHook}
%    Disabling a generic hook.
% \changes{v1.0o}{2021/08/02}{Change name}
%    \begin{macrocode}
\NewDocumentCommand \DisableGenericHook { m }
  { \hook_disable_generic:n {#1} }
%    \end{macrocode}
%  \end{macro}
%
%    \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\ActivateGenericHook}
%<latexrelease>                 {Providing~hooks}
%<latexrelease>\def \ActivateGenericHook #1 { }
%<latexrelease>\def \DisableGenericHook #1 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%
%
%  \begin{macro}{\AddToHook,\AddToHookWithArguments}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{AddToHookWithArguments} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\AddToHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
\NewDocumentCommand \AddToHook { m o +m }
  { \hook_gput_code:nnn {#1} {#2} {#3} }
\NewDocumentCommand \AddToHookWithArguments { m o +m }
  { \hook_gput_code_with_args:nnn {#1} {#2} {#3} }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \AddToHookWithArguments #1 #2 #3 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\AddToHookNext,\AddToHookNextWithArguments}
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{AddToHookNextWithArguments} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\AddToHookNextWithArguments}
%<latexrelease>                 {Hooks~with~args}
\NewDocumentCommand \AddToHookNext { m +m }
  { \hook_gput_next_code:nn {#1} {#2} }
\NewDocumentCommand \AddToHookNextWithArguments { m +m }
  { \hook_gput_next_code_with_args:nn {#1} {#2} }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHookNextWithArguments}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new_protected:Npn \AddToHookNextWithArguments #1 #2 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\ClearHookNext}
% \changes{v1.0o}{2021/07/27}{Macro added}
%    \begin{macrocode}
\NewDocumentCommand \ClearHookNext { m }
  { \hook_gclear_next_code:n {#1} }
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\RemoveFromHook}
%    \begin{macrocode}
\NewDocumentCommand \RemoveFromHook { m o }
  { \hook_gremove_code:nn {#1} {#2} }
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}{\SetDefaultHookLabel}
% \begin{macro}{\PushDefaultHookLabel}
% \begin{macro}{\PopDefaultHookLabel}
%   Now define a wrapper that replaces the top of the stack with the
%   argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly.
%    \begin{macrocode}
\NewDocumentCommand \SetDefaultHookLabel { m }
  { \@@_set_default_hook_label:n {#1} }
%    \end{macrocode}
%
%   The label is only automatically updated with \cs{@onefilewithoptions}
%   (\cs{usepackage} and \cs{documentclass}), but some packages, like
%   Ti\emph{k}Z, define package-like interfaces, like
%   \cs{usetikzlibrary} that are wrappers around \cs{input}, so they
%   inherit the default label currently in force (usually |top-level|,
%   but it may change if loaded in another package).  To provide a
%   package-like behavior also for hooks in these files, we provide
%   high-level access to the default label stack.
%    \begin{macrocode}
\NewDocumentCommand \PushDefaultHookLabel { m }
  { \@@_curr_name_push:n {#1} }
\NewDocumentCommand \PopDefaultHookLabel { }
  { \@@_curr_name_pop: }
%    \end{macrocode}
%
%   The current label stack holds the labels for all files but the
%   current one (more or less like \cs{@currnamestack}), and the current
%   label token list, \cs{g_@@_hook_curr_name_tl}, holds the label for
%   the current file.  However \cs{@pushfilename} happens before
%   \cs{@currname} is set, so we need to look ahead to get the
%   \cs{@currname} for the label.  \pkg{expl3} also requires the current
%   file in \cs{@pushfilename}, so here we abuse
%   \cs{@expl@push@filename@aux@@@@} to do \cs{@@_curr_name_push:n}.
%    \begin{macrocode}
\cs_gset_protected:Npn \@expl@push@filename@aux@@@@ #1#2#3
  {
    \@@_curr_name_push:n {#3}
    \str_gset:Nx \g_file_curr_name_str {#3}
    #1 #2 {#3}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
%
%  \begin{macro}{
%    \UseHook,
%    \UseOneTimeHook,
%    \UseHookWithArguments,
%    \UseOneTimeHookWithArguments,
%  }
%    Avoid the overhead of \pkg{xparse} and its protection that we
%    don't want here (since the hook should vanish without trace if empty)!
% \changes{v1.1a}{2023/04/06}
%         {Add \cs{UseHookWithArguments} (hook-args).}
%    \begin{macrocode}
%<latexrelease>\IncludeInRelease{2023/06/01}{\UseHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
\cs_new:Npn \UseHook        { \hook_use:n }
\cs_new:Npn \UseOneTimeHook { \hook_use_once:n }
\cs_new:Npn \UseHookWithArguments        { \hook_use:nnw }
\cs_new:Npn \UseOneTimeHookWithArguments { \hook_use_once:nnw }
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\UseHookWithArguments}
%<latexrelease>                 {Hooks~with~args}
%<latexrelease>\cs_new:Npn \UseHookWithArguments #1 #2 { }
%<latexrelease>\cs_new:Npn \UseOneTimeHookWithArguments #1 #2 { }
%<latexrelease>\EndIncludeInRelease
%    \end{macrocode}
%  \end{macro}
%
%
%
% \begin{macro}{\ShowHook,\LogHook}
%    \begin{macrocode}
\cs_new_protected:Npn \ShowHook { \hook_show:n }
\cs_new_protected:Npn \LogHook { \hook_log:n }
%    \end{macrocode}
% \end{macro}
%
%  \begin{macro}{\DebugHooksOn,\DebugHooksOff}
%
%    \begin{macrocode}
\cs_new_protected:Npn \DebugHooksOn  { \hook_debug_on:  }
\cs_new_protected:Npn \DebugHooksOff { \hook_debug_off: }
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\DeclareHookRule}
%
%    \begin{macrocode}
\NewDocumentCommand \DeclareHookRule { m m m m }
                    { \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} }
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\DeclareDefaultHookRule}
%    This declaration is only supported before \verb=\begin{document}=.
%    \begin{macrocode}
\NewDocumentCommand \DeclareDefaultHookRule { m m m }
                    { \hook_gset_rule:nnnn {??}{#1}{#2}{#3} }
\@onlypreamble\DeclareDefaultHookRule
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\ClearHookRule}
%    A special setup rule that removes an existing relation.
%    Basically \cs{@@_rule_gclear:nnn} plus fixing the property list for debugging.
%    \fmiinline{Needs perhaps an L3 interface, or maybe it should get dropped?}
%    \begin{macrocode}
\NewDocumentCommand \ClearHookRule { m m m }
{ \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} }
%    \end{macrocode}
%  \end{macro}
%
%
% \begin{macro}[EXP]{\IfHookEmptyTF,\IfHookEmptyT,\IfHookEmptyF}
% \changes{v1.1j}{2024/10/21}{Define \cs{IfHookEmptyT}, \cs{IfHookEmptyF}}
%   Here we avoid the overhead of \pkg{xparse}, since \cs{IfHookEmptyTF}
%   is used in \cs{end} (that is, every \LaTeX{} environment).  As a
%   further optimization, use \cs{let} rather than \cs{def} to avoid one
%   expansion step.
%    \begin{macrocode}
\cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF
\cs_new_eq:NN \IfHookEmptyT \hook_if_empty:nT
\cs_new_eq:NN \IfHookEmptyF \hook_if_empty:nF
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,int]{\IfHookExistsTF}
%    Marked for removal and no longer documented in the doc section!
% \phoinline{\cs{IfHookExistsTF} is used in \texttt{jlreq.cls},
% \texttt{pxatbegshi.sty}, \texttt{pxeverysel.sty},
% \texttt{pxeveryshi.sty}, so the public name may be an alias of the
% internal conditional for a while.  Regardless, those packages' use for
% \cs{IfHookExistsTF} is not really correct and can be changed.}
%    \begin{macrocode}
\cs_new_eq:NN \IfHookExistsTF \@@_if_usable:nTF
%    \end{macrocode}
% \end{macro}
%
%
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \subsection{Deprecated that needs cleanup at some point}
%
% \changes{v1.0p}{2021/08/20}{Added deprecation warnings for
%                             old generic hook commands (gh/638)}
%
% \begin{macro}[int]{
%     \hook_disable:n,
%     \hook_provide:n,
%     \hook_provide_reversed:n,
%     \hook_provide_pair:nn,
%     \@@_activate_generic_reversed:n,
%     \@@_activate_generic_pair:nn,
%   }
%   Deprecated.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_disable:n
  {
    \@@_deprecated_warn:nn
      { hook_disable:n }
      { hook_disable_generic:n }
    \hook_disable_generic:n
  }
\cs_new_protected:Npn \hook_provide:n
  {
    \@@_deprecated_warn:nn
      { hook_provide:n }
      { hook_activate_generic:n }
    \hook_activate_generic:n
  }
\cs_new_protected:Npn \hook_provide_reversed:n
  {
    \@@_deprecated_warn:nn
      { hook_provide_reversed:n }
      { hook_activate_generic:n }
    \@@_activate_generic_reversed:n
  }
\cs_new_protected:Npn \hook_provide_pair:nn
  {
    \@@_deprecated_warn:nn
      { hook_provide_pair:nn }
      { hook_activate_generic:n }
    \@@_activate_generic_pair:nn
  }
\cs_new_protected:Npn \@@_activate_generic_reversed:n #1
  { \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} { - } }
\cs_new_protected:Npn \@@_activate_generic_pair:nn #1#2
  { \hook_activate_generic:n {#1} \@@_activate_generic_reversed:n {#2} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{
%     \DisableHook,
%     \ProvideHook,
%     \ProvideReversedHook,
%     \ProvideMirroredHookPair,
%   }
%   Deprecated.
%    \begin{macrocode}
\cs_new_protected:Npn \DisableHook
  {
    \@@_deprecated_warn:nn
      { DisableHook }
      { DisableGenericHook }
    \hook_disable_generic:n
  }
\cs_new_protected:Npn \ProvideHook
  {
    \@@_deprecated_warn:nn
      { ProvideHook }
      { ActivateGenericHook }
    \hook_activate_generic:n
  }
\cs_new_protected:Npn \ProvideReversedHook
  {
    \@@_deprecated_warn:nn
      { ProvideReversedHook }
      { ActivateGenericHook }
    \@@_activate_generic_reversed:n
  }
\cs_new_protected:Npn \ProvideMirroredHookPair
  {
    \@@_deprecated_warn:nn
      { ProvideMirroredHookPair }
      { ActivateGenericHook }
    \@@_activate_generic_pair:nn
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_deprecated_warn:nn}
%   Warns about a deprecation, telling what should be used instead.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_deprecated_warn:nn #1 #2
  { \msg_warning:nnnn { hooks } { deprecated } {#1} {#2} }
\msg_new:nnn { hooks } { deprecated }
  {
    Command~\iow_char:N\\#1~is~deprecated~and~will~be~removed~in~a~
    future~release. \\ \\
    Use~\iow_char:N\\#2~instead.
  }
%    \end{macrocode}
% \end{macro}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
% \subsection{Internal commands needed elsewhere}
%
% Here we set up a few horrible (but consistent) \LaTeXe{} names to
% allow for internal commands to be used outside this module. We
% have to unset the \texttt{@\/@} since we want double ``at'' sign
% in place of double underscores.
%
%    \begin{macrocode}
%<@@=>
%    \end{macrocode}
%
%  \begin{macro}[int]{
%      \@expl@@@initialize@all@@,
%      \@expl@@@hook@curr@name@pop@@
%  }
%
% \InternalDetectionOff
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@initialize@all@@
              \__hook_initialize_all:
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@hook@curr@name@pop@@
              \__hook_curr_name_pop:
%    \end{macrocode}
% \InternalDetectionOn
%  \end{macro}
%
%    Rolling back here doesn't undefine the interface commands as they
%    may be used in packages without rollback functionality. So we
%    just make them do nothing which may or may not work depending on
%    the code usage.
% \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed
%                             to be for \cs{AddToHook} vice versa (gh/401)}
%    \begin{macrocode}
%
%<latexrelease>\IncludeInRelease{0000/00/00}{lthooks}
%<latexrelease>                 {The~hook~management}%
%<latexrelease>
%<latexrelease>\def \NewHook#1{}
%<latexrelease>\def \NewReversedHook#1{}
%<latexrelease>\def \NewMirroredHookPair#1#2{}
%<latexrelease>
%<latexrelease>\def \DisableGenericHook #1{}
%<latexrelease>
%<latexrelease>\long\def\AddToHookNext#1#2{}
%<latexrelease>
%<latexrelease>\def\AddToHook#1{\@gobble@AddToHook@args}
%<latexrelease>\providecommand\@gobble@AddToHook@args[2][]{}
%<latexrelease>
%<latexrelease>\def\RemoveFromHook#1{\@gobble@RemoveFromHook@arg}
%<latexrelease>\providecommand\@gobble@RemoveFromHook@arg[1][]{}
%<latexrelease>
%<latexrelease>\def \UseHook        #1{}
%<latexrelease>\def \UseOneTimeHook #1{}
%<latexrelease>\def \ShowHook #1{}
%<latexrelease>\let \DebugHooksOn \@empty
%<latexrelease>\let \DebugHooksOff\@empty
%<latexrelease>
%<latexrelease>\def \DeclareHookRule #1#2#3#4{}
%<latexrelease>\def \DeclareDefaultHookRule #1#2#3{}
%<latexrelease>\def \ClearHookRule #1#2#3{}
%    \end{macrocode}
%    If the hook management is not provided we make the test for existence
%    false and the test for empty true in the hope that this is most
%    of the time reasonable. If not a package would need to guard
%    against running in an old kernel.
%    \begin{macrocode}
%<latexrelease>\long\def \IfHookExistsTF #1#2#3{#3}
%<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2}
%<latexrelease>
%<latexrelease>\EndModuleRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=hook>
%    \end{macrocode}
%
% \changes{v1.1a}{2023/04/06}
%         {Add dedicated rollback code to revert data structures (hook-args).}
%    \begin{macrocode}
%<latexrelease>\cs:w @@_rollback_tidying: \cs_end:
%<latexrelease>\bool_lazy_and:nnT
%<latexrelease>    { \int_compare_p:nNn { \sourceLaTeXdate } > { 20230600 } }
%<latexrelease>    { \int_compare_p:nNn { \requestedLaTeXdate } < { 20230601 } }
%<latexrelease>  {
%<latexrelease>    \cs_gset_protected:Npn \@@_rollback_tidying:
%<latexrelease>      {
%<latexrelease>        \@latex@error { Rollback~code~executed~twice }
%<latexrelease>          {
%<latexrelease>            Something~went~wrong~(unless~this~was~
%<latexrelease>            done~on~purpose~in~a~testing~environment).
%<latexrelease>          }
%<latexrelease>        \use_none:nnnn
%<latexrelease>      }
%<latexrelease>    \cs_set:Npn \@@_tmp:w #1 #2
%<latexrelease>      {
%<latexrelease>        \@@_tl_gset:cx { @@#1~#2 }
%<latexrelease>          {
%<latexrelease>            \exp_args:No \exp_not:o
%<latexrelease>              {
%<latexrelease>                \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
%<latexrelease>                  { \@@_braced_cs_parameter:n
%<latexrelease>                      { @@#1~#2 } }
%<latexrelease>              }
%<latexrelease>          }
%<latexrelease>      }
%<latexrelease>    \seq_map_inline:Nn \g_@@_all_seq
%<latexrelease>      {
%<latexrelease>        \exp_after:wN \cs_gset_nopar:Npn
%<latexrelease>          \cs:w g_@@_#1_code_prop \exp_args:NNo \exp_args:No
%<latexrelease>            \cs_end: { \cs:w g_@@_#1_code_prop \cs_end: }
%<latexrelease>        \@@_tmp:w { _toplevel } {#1}
%<latexrelease>        \@@_tmp:w { _next } {#1}
%<latexrelease>      }
%<latexrelease>  }
\ExplSyntaxOff
%</2ekernel|latexrelease>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=>
%    \end{macrocode}
%
% \Finale
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\endinput

^^A  Needed for emacs
^^A
^^A  Local Variables: 
^^A  mode: latex
^^A  coding: utf-8-unix
^^A  End: 
