% \iffalse meta-comment
%%
%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach,
%%                                      Phelype Oleinik & LaTeX Team
%
% 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
%
%    \begin{macrocode}
\def\lthooksversion{v1.0h}
\def\lthooksdate{2021/01/07}
%    \end{macrocode}
%
%<*driver>
\documentclass{l3doc}

% bug fix fo l3doc.cls
\ExplSyntaxOn
\cs_set_protected:Npn \__codedoc_macro_typeset_one:nN #1#2
  {
    \vbox_set:Nn \l__codedoc_macro_box
      {
        \vbox_unpack_drop:N \l__codedoc_macro_box
        \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2
            \MacroFont       % <----- without it the \ is in lmr10 if a link is made
            \      
        } }
      }
    \int_incr:N \l__codedoc_macro_int
  }
\ExplSyntaxOff

\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{lthooks.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \long\def\fmi#1{\begin{quote}\itshape FMi: #1\end{quote}}
% \long\def\pho#1{\begin{quote}\itshape PhO: #1\end{quote}}
%
% \newcommand\hook[1]{\texttt{#1}}
%    
%
% \title{The \texttt{lthooks} package\thanks{This package has version
%    \lthooksversion\ dated \lthooksdate, \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 and using them in code}
%
%    With two exceptions, hooks have to be declared before they can be
%    used. The exceptions are hooks in environments (i.e., executed at
%    \cs{begin} and \cs{end}) and hooks run when loading files,
%    e.g. before and after a package is loaded, etc. Their hook names
%    depend on the environment or the file name and so declaring them
%    beforehand is difficult.
%
%
% \begin{function}{\NewHook}
%   \begin{syntax}
%     \cs{NewHook} \Arg{hook}
%   \end{syntax}
%   Creates a new \meta{hook}.
%    If this is a hook provided as part of a package it is suggested
%    that the \meta{hook} 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}.
% \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{hooks} can be specified using the dot-syntax to denote
%    the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
%
%
% \begin{function}{\UseHook}
%   \begin{syntax}
%     \cs{UseHook} \Arg{hook}
%   \end{syntax}
%    Execute the hook code inside a command or environment.
%
%    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}{\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}=. Once we have passed that point adding to
%    the hook through a defined \cs{\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.
%
%    \fmi{Maybe add an error version as well?}
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
%
% \subsubsection{Updating code for hooks}
%
% \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}{\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}).
%
%    If the code for that \meta{label} wasn't yet added to the
%    \meta{hook}, an order is set so that when some code attempts to add
%    that label, the removal order takes action and the code is not
%    added.
%
%    If the optional 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!
%
%    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}
%
% \medskip
%
% 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.
%
% 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.,
%\begin{verbatim}
%   \AddToHook{env/quote/before}{\small}
%   \begin{quote}
%     A quote set in a smaller typeface
%   \end{quote}
%   ...
%   \RemoveFromHook{env/quote/before}
%   ... now back to normal for further quotes
%\end{verbatim}
% Note that you can't cancel the setting with
%\begin{verbatim}
%   \AddToHook{env/quote/before}{}
%\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 to 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 the 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 happens after the 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
%
%    It is possible to nest declarations 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).}
%
% \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}, \cs{IfHookEmptyTF}, and
% \cs{IfHookExistsTF} (and their \pkg{expl3} interfaces \cs{hook_use:n},
% \cs{hook_use_once:n}, \cs{hook_if_empty:nTF}, and
% \cs{hook_if_exist: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},
% \cs{IfHookEmptyTF}, and \cs{IfHookExistsTF} 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/after/foo.tex}{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/after/foo.tex}{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, 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}) and
% \cs{IfHookExistsTF} (and \cs{hook_if_exist: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
% it in logical parts, but still use the main package name as
% \meta{label}, then the \meta{default label} can be set using
% \cs{SetDefaultHookLabel} or
% \cs{PushDefaultHookLabel}..\cs{PopDefaultHookLabel}.
%
% \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 emulate \cs{usepackage}-like hook behaviour
%   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}
%
% 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 precaution to determine of some other
% package got loaded as well (before or after) and 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 code
% added to hooks got added in the right order and 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 explicitly describe in which order they should be
% processed.
%
% \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}.
%    Rules specific to a given hook take precedence over default
%    rules that use \texttt{??} as the \meta{hook}.
%
%    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
%    delcaration.
%
%    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.)
%
%    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: they have four possible states.
% A hook may exist or not, and either way it may or may not be empty.
% This means that even a hook that doesn't exist may be non-empty.
%
% 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.
%
% 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 \hook{file} and \hook{env} hooks are
% automatically declared when code is added to them.
%
% \begin{function}[EXP]{\IfHookEmptyTF}
%   \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}), 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}[EXP]{\IfHookExistsTF}
%   \begin{syntax}
%     \cs{IfHookExistsTF} \Arg{hook} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{hook} exists (if it was created with either
%   \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and
%   branches to either \meta{true code} or \meta{false code} depending
%   on the result.
%
%   The existence of a hook usually doesn't mean much from the viewpoint
%   of code that tries to add/remove code from that hook, since package
%   loading order may vary, thus the creation of hooks is asynchronous
%   to adding and removing code from it, so this test should be used
%   sparingly.
%
%   Generic hooks are declared at the time code is added to them, so the
%   result of \cs{hook_if_exist:n} will change once code is added to
%   said hook (unless the hook was previously declared).
%
%    The \meta{hook} \emph{cannot} be specified using the dot-syntax.
%    A leading |.| is treated literally.
% \end{function}
%
% \fmi{Would be helpful if we provide some use cases}
%
% \subsubsection{Displaying hook code}
%
%    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}
%   \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 that rule applies
%   to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless
%   overrided 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}
%   \end{syntax}
%    Turn the debugging of hook code on or off. This displays changes
%    made to the hook data structures. The output is rather coarse and
%      not really intended for normal use.
% \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_use:n}
%   \begin{syntax}
%     \cs{hook_use:n} \Arg{hook}
%   \end{syntax}
%    Executes the \Arg{hook} code followed (if set up) by the code for next
%    invocation only, then empties that next invocation code.
%
%    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}
%     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.
%
%    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}
%    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 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}
%    Adds a chunk of \meta{code} for use only in the next invocation of the
%    \meta{hook}. Once used it is gone.
%
%    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}.
% \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 the code for that \meta{label} wasn't yet added to the
%    \meta{hook}, an order is set so that when some code attempts to add
%    that label, the removal order takes action and the code is not
%    added.
%
%    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}[pTF]{\hook_if_exist:n}
%   \begin{syntax}
%     \cs{hook_if_exist:nTF} \Arg{hook} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Tests if the \meta{hook} exists (if it was created with either
%   \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and
%   branches to either \meta{true code} or \meta{false code} depending
%   on the result.
%
%   The existence of a hook usually doesn't mean much from the viewpoint
%   of code that tries to add/remove code from that hook, since package
%   loading order may vary, thus the creation of hooks is asynchronous
%   to adding and removing code from it, so this test should be used
%   sparingly.
%
%   Generic hooks are declared at the time code is added to them, so the
%   result of \cs{hook_if_exist:n} will change once code is added to
%   said hook (unless the hook was previously declared).
%
%    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}
%   \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, or 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 that \verb=\begin{color}= 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}
%
%    The reversal of the execution order happens before applying any
%    rules, 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{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 through 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{hookname}} or
%    \cs{@kernel@after@\meta{hookname}}. 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 further 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}
%
%
%
% \begin{function}{\AtBeginDvi}
%   \begin{syntax}
%     \cs{AtBeginDvi} \oarg{label} \Arg{code}
%   \end{syntax}
%   This hook is discussed in conjunction with the shipout hooks.
% \end{function}
%
%    \bigskip
%
%    The few hooks that existed previously in \LaTeXe{} used internally
%    commands such as \cs{@begindocumenthook} and packages sometimes
%    augemented 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.
%
%
% \subsection{\LaTeXe{} commands and environments augmented by
%    hooks}
%
%  \emph{intro to be written}
%
% \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
%       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 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}
%    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{\meta{env}} and \cs{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.
%
%
% \begin{function}{\BeforeBeginEnvironment}
%   \begin{syntax}
%     \cs{BeforeBeginEnvironment} \oarg{label} \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{code}
%   \end{syntax}
%   Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/begin} hook.
% \end{function}
%
% \begin{function}{\AtEndEnvironment}
%   \begin{syntax}
%     \cs{AtEndEnvironment} \oarg{label} \Arg{code}
%   \end{syntax}
%   Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/end} hook.
% \end{function}
%
% \begin{function}{\AfterEndEnvironment}
%   \begin{syntax}
%     \cs{AfterEndEnvironment} \oarg{label} \Arg{code}
%   \end{syntax}
%   Like \cs{BeforeBeginEnvironment} but adds to the \hook{env/\meta{env}/after} hook.
% \end{function}
%
%
%
% \subsubsection{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 when using \cs{AtBeginDocument} and it is
%      executed after the \texttt{.aux} file as be read in 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.
%
%
%
%
% \subsubsection{Hooks provided by \cs{end}\texttt{\{document\}}}
%
%    \LaTeXe{} always provided \cs{AtEndDocument} to add code to the
%    execution of \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). 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 execuded 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.
%
%
%
%
% \subsubsection{Hooks provided \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}.
%
%
% \subsubsection{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}, etc. These are documented in
%    \texttt{ltfilehook-doc.pdf} or with code in
%    \texttt{ltfilehook-code.pdf}.
%
%
% \subsubsection{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., 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 have hooks in which such support can
%    be added.
%    \begin{description}
%
%    \item[\hook{rmfamily}]
%
%      After \cs{rmfamily} has done its initial checks and prepared a
%      any font series update this hook is executed and only
%      afterwards \cs{selectfont}.
%
%    \item[\hook{sffamily}]
%
%      Like the \hook{rmfamily} hook but for the \cs{sffamily} command.
%
%    \item[\hook{ttfamily}]
%
%      Like the \hook{rmfamily} hook but for the \cs{ttfamily} command.
%
%    \item[\hook{normalfont}]
%
%      The \cs{normalfont} command resets 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 meta families (rm/sf/tt) and
%      the meta series (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 meta
%      families (rm/sf/tt) when \cs{bfseries} is called. In the
%      \hook{bfseries/defaults} hook further adjustments can 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 used in
%      \cs{mdseries} command.
%
%    \end{description}
%
%
% \StopEventually{\setlength\IndexMin{200pt}  \PrintIndex  }
%
%
% \section{The Implementation}
%
%
% \subsection{Loading further extensions}
%
%    \begin{macrocode}
%<@@=hook>
%    \end{macrocode}
%
%
%    At the moment the whole module rolls back in one go, but if we
%    make any modifications in later releases this will then need
%    splitting.
%    \begin{macrocode}
%<*2ekernel|latexrelease>
%<latexrelease>\IncludeInRelease{2020/10/01}%
%<latexrelease>                 {\NewHook}{The hook management}%
%    \end{macrocode}
%    
%    \begin{macrocode}
\ExplSyntaxOn
%    \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}
%    \begin{macrocode}
\cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn
%    \end{macrocode}
% \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}{\g_@@_removal_list_prop}
%   A token list to hold delayed removals.
%    \begin{macrocode}
\tl_new:N \g_@@_removal_list_tl
%    \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_@@_execute_immediately_prop}
%    List of hooks that from no on should not longer receive code.
%    \begin{macrocode}
\prop_new:N \g_@@_execute_immediately_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}{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
%   Some variants of \pkg{expl3} functions. \fmi{should 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 }
%    \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}{\@@_tl_set:Nn,\@@_tl_set:Nx,
%               \@@_tl_set:cn,\@@_tl_set:cx}
%   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}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tl_set:Nn #1#2
  { \cs_set_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
\cs_new_protected:Npn \@@_tl_set:Nx #1#2
  { \cs_set_nopar:Npx #1 {#2} }
\cs_generate_variant:Nn \@@_tl_set:Nn { c }
\cs_generate_variant:Nn \@@_tl_set:Nx { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gset:Nn,\@@_tl_gset:No,\@@_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:No #1#2
  { \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
\cs_new_protected:Npn \@@_tl_gset:Nx #1#2
  { \cs_gset_nopar:Npx #1 {#2} }
\cs_generate_variant:Nn \@@_tl_gset:Nn { c }
\cs_generate_variant:Nn \@@_tl_gset:No { c }
\cs_generate_variant:Nn \@@_tl_gset:Nx { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gput_right:Nn,\@@_tl_gput_right:No,\@@_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 { No, cn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gput_left:Nn,\@@_tl_gput_left:No}
%   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} }
  }
\cs_generate_variant:Nn \@@_tl_gput_left:Nn { No }
%    \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}
%
% \begin{macro}{\g_@@_..._code_prop,\@@~...,\@@_next~...}
%
%    Hooks have a \meta{name} and for each hook we have to provide a number of
%    data structures. These are
%    \begin{description}
%    \item[\cs{g_@@_\meta{name}_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{name}_rule_\meta{label1}\string|\meta{label2}_tl}}]
%    A token list holding the relation between \meta{label1} and
%    \meta{label2} in the \meta{name}.  The \meta{labels} are lexically
%    (reverse) sorted to ensure that two labels always point to the same
%    token list.  For global rules, the \meta{name} is |??|.
%
%    \item[\cs{@@~\meta{name}}] 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{name}_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{@@_toplevel~\meta{name}}] 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~\meta{name}}] 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{@@~\meta{name}}
%    token list.  It is called \cs{@@_next~\meta{name}} rather than
%    \cs{@@~next_\meta{name}} because otherwise a hook whose name is
%    |next_|\meta{name} would clash with the next code-token list of the
%    hook called \meta{name}.
%
%    \end{description}
%  \end{macro}
%
%
%
%
%  \begin{macro}{\hook_new:n}
%    The \cs{hook_new:n} declaration declare a new hook and expects
%    the hook \meta{name} as its argument, e.g.,
%    \hook{begindocument}.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_new:n #1
  { \@@_normalize_hook_args:Nn \@@_new:n {#1} }
\cs_new_protected:Npn \@@_new:n #1
  {
%    \end{macrocode}
%    We check for one of the internal data structures and if it
%    already exists we complain.
%    \begin{macrocode}
    \hook_if_exist:nTF {#1}
      { \msg_error:nnn { hooks } { exists } {#1} }
%    \end{macrocode}
%    Otherwise we add the hook name to the list of all hooks and
%    allocate the necessary data structures for the new hook.
%    \begin{macrocode}
      {
        \seq_gput_right:Nn \g_@@_all_seq {#1}
%    \end{macrocode}
%    This is only used by the actual code of the current hook, so
%    declare it normally:
%    \begin{macrocode}
        \tl_new:c { @@~#1 }
%    \end{macrocode}
%    Now ensure that the base data structure for the hook exists:
%    \begin{macrocode}
        \@@_declare: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.
%    \begin{macrocode}
        \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}
     }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%
% \begin{macro}{\@@_declare:n}
%   This function declares the basic data structures for a hook without
%   actually 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).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_declare:n #1
  {
    \@@_if_exist:nF {#1}
      {
        \prop_new:c { g_@@_#1_code_prop }
        \tl_new:c { @@_toplevel~#1 }
        \tl_new:c { @@_next~#1 }
      }
  }
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\hook_new_reversed:n,\@@_new_reversed:n}
%
%    Declare a new hook. The default ordering of code chunks is
%    reversed, signaled by setting the token list to a minus sign.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_new_reversed:n #1
  { \@@_normalize_hook_args:Nn \@@_new_reversed:n {#1} }
\cs_new_protected:Npn \@@_new_reversed:n #1
  {
    \@@_new:n {#1}
%    \end{macrocode}
%    If the hook already exists the above will generate an error
%    message, so the next line should be executed (but it is --- too
%    bad).
%    \begin{macrocode}
    \tl_gset:cn { g_@@_#1_reversed_tl } { - }
  }
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\hook_new_pair:nn}
%    A shorthand for declaring a normal and a (matching) reversed hook in one go.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_new_pair:nn #1#2
  { \hook_new:n {#1} \hook_new_reversed:n {#2} }
%    \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}
\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 }
          {
            \exp_args:Nnnv \@@_hook_gput_code_do:nnn {#1}
                                  { legacy } { @#1hook }
%    \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 }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Parsing a label}
%
% \begin{macro}[EXP]{\@@_parse_label_default:n}
%   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:}.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_label_default:n #1
  {
    \tl_if_novalue:nTF {#1}
      { \@@_currname_or_default: }
      { \tl_trim_spaces_apply:nN {#1} \@@_parse_dot_label:n }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_parse_dot_label:n}
% \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.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_dot_label:n #1
  {
    \tl_if_empty:nTF {#1}
      {
        \msg_expandable_error:nn { hooks } { empty-label }
        \@@_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:}
%   Uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise tries
%   \cs{@currname}.  If neither is set, 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 { hooks } { 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}
%   Provides a standard sanitisation 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{@@~} 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{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}
%   Standard route for normalising 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}!
%    \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:n {#2} } }
  }
\cs_new_protected:Npn \@@_normalize_hook_args:Nnn #1 #2 #3
  {
    \@@_normalize_hook_args_aux:Nn #1
      {
        { \@@_parse_label_default:n {#2} }
        { \@@_parse_label_default:n {#3} }
      }
  }
\cs_new_protected:Npn \@@_normalize_hook_rule_args:Nnnnn #1 #2 #3 #4 #5
  {
    \@@_normalize_hook_args_aux:Nn #1
      {
        { \@@_parse_label_default:n {#2} }
        { \@@_parse_label_default:n {#3} }
        { \tl_trim_spaces:n {#4} }
        { \@@_parse_label_default:n {#5} }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}{\hook_gput_code:nnn}
% \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn}
%
%    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}.
%    \begin{macrocode}
\cs_new_protected:Npn \hook_gput_code:nnn #1 #2
  { \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
  {
%    \end{macrocode}
%    First check if the hook was used as a one-time hook:
%    \begin{macrocode}
    \prop_if_in:NnTF \g_@@_execute_immediately_prop {#1}
      {#3}
      {
%    \end{macrocode}
%    Then check if the current \meta{hook}/\meta{label} pair was marked
%    for removal, in which case \cs{@@_unmark_removal:nn} is used to
%    remove that mark (once).  This may happen when a package removes
%    code from another package which was not yet loaded:  the removal
%    order is stored, and at this stage it is executed by not adding to
%    the hook.
%    \begin{macrocode}
        \@@_if_marked_removal:nnTF {#1} {#2}
          { \@@_unmark_removal:nn {#1} {#2} }
          {
%    \end{macrocode}
%    If no removal is queued, we are free to add.  Start by checking if
%    the hook exists.
%    \begin{macrocode}
            \hook_if_exist: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 does not exist, however, before giving up try to
%    declare it as a generic hook, if its name matches one of the valid
%    patterns.
%    \begin{macrocode}
              { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} }
          }
      }
  }
\cs_generate_variant:Nn \@@_gput_code:nnn { nxv }
%    \end{macrocode}
%
%    This macro will unconditionally add a chunk of code to the given hook.
%    \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:
%    \begin{macrocode}
    \@@_debug:n{\iow_term:x{****~ Add~ to~
                      \hook_if_exist:nF {#1} { undeclared~ }
                      hook~ #1~ (#2)
                      \on@line\space <-~ \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[no-index]{@@_toplevel~\meta{hook}} that goes at the end of the
%    hook (or at the beginning, for a reversed hook), just before
%   \cs[no-index]{@@_next~\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{@@_declare:n}.
%    \begin{macrocode}
            \@@_declare:n {#1}
            \@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
          }
          { \msg_error:nnn { hooks } { misused-top-level } {#1} }
      }
      {
        \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
          {
            \prop_gput:cno { g_@@_#1_code_prop } {#2}
              { \l_@@_return_tl #3 }
          }
          { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
      }
  }
%    \end{macrocode}
% \end{macro}
% \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 its declared.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
  {
    \@@_declare:n {#1}
    \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_try_declaring_generic_hook:nnn}
% \begin{macro}{\@@_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.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1
  {
    \@@_try_declaring_generic_hook:nNNnn {#1}
      \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn
  }
\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1
  {
    \@@_try_declaring_generic_hook:nNNnn {#1}
      \hook_gput_next_code:nn \@@_gput_next_do:nn
  }
%    \end{macrocode}
%
% \begin{macro}{
%     \@@_try_declaring_generic_hook:nNNnn,
%     \@@_try_declaring_generic_hook_split:nNNnn
%   }
% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
%   \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.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1
  {
    \@@_if_file_hook:wTF #1 / / \s_@@_mark
      {
        \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn
          { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
      }
      { \@@_try_declaring_generic_hook_split:nNNnn {#1} }
  }
\cs_new_protected:Npn \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3
  {
    \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
      { #2 }
      { #3 } {#1}
  }
\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
    #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
  {
    \tl_if_empty:nTF {#2}
      { \prg_return_false: }
      {
        \prop_if_in:NnTF \c_@@_generics_prop {#1}
          {
            \hook_if_exist:nF {#5} { \hook_new:n {#5} }
%    \end{macrocode}
%    After having declared the hook we check the second component (for
%    file hooks) or the third component for environment hooks) and
%    if it is on the list of components for which we should have declared
%    a reversed hook we alter the hook data structure accordingly.
%    \begin{macrocode}
            \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
              { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
              {
                \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
                  { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
              }
%    \end{macrocode}
%    Now that we know that the hook is declared we can add the code to it.
%    \begin{macrocode}
            \prg_return_true:
          }
          { \prg_return_false: }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \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/before/foo.tex|).  If it is a file-specific hook, then it
%   executes the \meta{true} branch, otherwise \meta{false}.
%
%   A file-specific hook is \texttt{file/\meta{position}/\meta{name}}.
%   If any of these parts don't exist, it is a general file hook or not
%   a file hook at all, so the conditional evaluates to \meta{false}.
%   Otherwise, it checks that the first part is |file| and that the
%   \meta{position} is in the \cs{c_@@_generics_file_prop}.
%
%   A property list is used here to avoid having to worry with catcodes,
%   because \pkg{expl3}'s file name parsing turns all characters into
%   catcode-12 tokens, which might differ from hand-input letters.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_file_hook:w
    #1 / #2 / #3 \s_@@_mark { TF }
  {
    \str_if_eq:nnTF {#1} { file }
      {
        \bool_lazy_or:nnTF
            { \tl_if_empty_p:n {#3} }
            { \str_if_eq_p:nn {#3} { / } }
          { \prg_return_false: }
          {
            \prop_if_in:NnTF \c_@@_generics_file_prop {#2}
              { \prg_return_true: }
              { \prg_return_false: }
          }
      }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_file_hook_normalize:n}
% \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w}
%   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/after/./mypath//file.tex}|.
%    \begin{macrocode}
\cs_new:Npn \@@_file_hook_normalize:n #1
  { \@@_strip_double_slash:n {#1} }
\cs_new:Npn \@@_strip_double_slash:n #1
  { \@@_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}
\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3 // #4 \s_@@_mark
  {
    \tl_if_empty:nTF {#4}
      { #1/#2/#3 }
      { \@@_strip_double_slash:w #1/#2/#3 / #4 \s_@@_mark }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%  \begin{macro}{\c_@@_generics_prop}
%    Property list holding the generic names. We don't provide any user
%    interface to this as this is meant to be static.
%    \begin{description}
%    \item[\texttt{env}]
%      The generic hooks used in \cs{begin} and \cs{end}.
%    \item[\texttt{file}]
%      The generic hooks used when loading a file
%    \end{description}
%    \begin{macrocode}
\prop_const_from_keyval:Nn \c_@@_generics_prop
  {env=,file=,package=,class=,include=}
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\c_@@_generics_reversed_ii_prop,
%                \c_@@_generics_reversed_iii_prop,
%                \c_@@_generics_file_prop}
%    Some of the generic hooks are supposed to use reverse ordering, these are
%    the following (only the second or third sub-component is checked):
%    \begin{macrocode}
\prop_const_from_keyval:Nn \c_@@_generics_reversed_ii_prop {after=,end=}
\prop_const_from_keyval:Nn \c_@@_generics_reversed_iii_prop {after=}
\prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=}
%    \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.
%    \begin{macrocode}
\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{hook_if_exist: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_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[no-index]{g_@@_\meta{hook}_code_prop}, the top-level code
%    \cs[no-index]{@@_toplevel~\meta{hook}}, and the next-execution code
%    \cs[no-index]{@@_next~\meta{hook}}.
%    \begin{macrocode}
        \str_if_eq:nnTF {#2} {*}
          {
            \prop_gclear:c { g_@@_#1_code_prop }
            \@@_tl_gclear:c { @@_toplevel~#1 }
            \@@_tl_gclear:c { @@_next~#1 }
          }
          {
%    \end{macrocode}
%    If the label is |top-level| then clear the token list, as all code
%    there is under the same label.  Marked removal is not implemented
%    for |top-level| because it is hard to reliably know that no code
%    was added to \cs[no-index]{@@_toplevel~\meta{hook}} (granted that
%    an empty code could be interpreted as that, but then it differs in
%    behaviour from other labels, in which an empty chunk is still valid
%    for removal).  Besides, it doesn't make much (if any) sense for
%    packages to remove |top-level| code.  So here the chunk is just
%    cleared unconditionally.
%    \begin{macrocode}
            \str_if_eq:nnTF {#2} { top-level }
              { \@@_tl_gclear:c { @@_toplevel~#1 } }
              {
%    \end{macrocode}
%    Otherwise check if the label being removed exists in the code pool.
%    If it does, just call \cs{@@_gremove_code_do:nn} to do the removal,
%    otherwise mark it to be removed.
%    \begin{macrocode}
                \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
                  { \@@_gremove_code_do:nn }
                  { \@@_mark_removal:nn }
                      {#1} {#2}
              }
          }
%    \end{macrocode}
%    Finally update the code, if the hook exists.
%    \begin{macrocode}
        \hook_if_exist:nT {#1}
          { \@@_update_hook_code:n {#1} }
      }
%    \end{macrocode}
%
%    If the code pool for this hook doesn't exist it means that nothing
%    tried to add to it before, so we just queue this removal order for
%    later.
%    \begin{macrocode}
      { \@@_mark_removal:nn {#1} {#2} }
  }
%    \end{macrocode}
%
% \begin{macro}{\@@_gremove_code_do:nn}
%   Remove code for a given label.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gremove_code_do:nn #1 #2
  { \prop_gremove:cn { g_@@_#1_code_prop } {#2} }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_mark_removal:nn}
%   Marks \meta{label} (\verb=#2=) to be removed from \meta{hook}
%   (\verb=#1=).  The number of removals should be fairly small, and
%   \cs{tl_gremove_once:Nx} is fairly efficient even for longer token
%   lists, so we use a single global token list, rather than one for
%   each hook.
%
%   A hand-crafted token list is used here because property lists don't
%   hold repeated items, so multiple usages of \cs{@@_mark_removal:nn}
%   would be cancelled by a single \cs{@@_unmark_removal:nn}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_mark_removal:nn #1 #2
  {
    \tl_gput_right:Nx \g_@@_removal_list_tl
      { \@@_removal_tl:nn {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_unmark_removal:nn}
%   Unmarks \meta{label} (\verb=#2=) to be removed from \meta{hook}
%   (\verb=#1=).  \cs{tl_gremove_once:Nx} is used rather than
%   \cs{tl_gremove_all:Nx} so that two additions are needed to cancel
%   two marked removals, rather than only one.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_unmark_removal:nn #1 #2
  {
    \tl_gremove_once:Nx \g_@@_removal_list_tl
      { \@@_removal_tl:nn {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF]{\@@_if_marked_removal:nn}
%   Checks if the \cs{g_@@_removal_list_tl} contains the current
%   \meta{label} (\verb=#2=) and \meta{hook} (\verb=#1=).
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_if_marked_removal:nn #1 #2 { TF }
  {
    \exp_args:NNx \tl_if_in:NnTF \g_@@_removal_list_tl
      { \@@_removal_tl:nn {#1} {#2} }
      { \prg_return_true: } { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[rEXP]{\@@_removal_tl:nn}
%   Builds a token list with \verb=#1= and \verb=#2= which can only be
%   matched by \verb=#1= and \verb=#2=.  The |&|$_4$ anchors a removal,
%   so that \verb=#1= can't be mistaken by \verb=#2= and vice versa, and
%   the two |$|$_3$ delimit the two arguments
%    \begin{macrocode}
\cs_new:Npn \@@_removal_tl:nn #1 #2
  { & \tl_to_str:n {#2} $ \tl_to_str:n {#1} $ }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{
%     \g_@@_??_code_prop,
%     \@@~??,
%     \g_@@_??_reversed_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 things should be fast, so this is not done with \texttt{c}
%    expansion in the code later on.
%
%    \cs{@@~??} isn't used, but it has to be defined to trick
%    the code into thinking that \verb=??= 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}
%  \end{macro}
%
%  \subsection{Setting rules for hooks code}
%
%  \begin{macro}{\hook_gset_rule:nnnn}
%  \begin{macro}{\@@_gset_rule:nnnn}
%
%    \fmi{needs docu correction given new implementation}
%
%    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 describing a default rule.
%    \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}
\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
  {
%    \end{macrocode}
%    First we ensure the basic data structure of the hook exists:
%    \begin{macrocode}
    \@@_declare: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}        }
  }
%    \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.
%    \begin{macrocode}
\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.
%    \begin{macrocode}
  \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
  \seq_map_inline:Nn \g_@@_all_seq
      {
        \@@_update_hook_code:n {##1}
      }
%    \end{macrocode}
%    If we are debugging we show results hook by hook for all hooks
%    that have data. 
%    \begin{macrocode}
  \@@_debug:n
     { \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:}
       \prop_map_inline:Nn \g_@@_used_prop
           { \iow_term:x{^^J~ ##1~ ->~
               \exp_not:v {@@~##1}~ }
           }
     }
%    \end{macrocode}
%    After all hooks are initialized we change the ``use'' to just
%    call the hook code and not initialize it (as it was done in the
%    preamble.
%    \begin{macrocode}
  \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
  \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
}
%    \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.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_initialize_hook_code:n #1
  {
    \@@_debug:n{ \iow_term:x{^^JUpdate~ 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{@@~\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.
%    \begin{macrocode}
    \hook_if_exist:nT {#1}
      {
        \prop_if_empty:cTF {g_@@_#1_code_prop}
          {
            \@@_tl_gset:co { @@~#1 }
              {
                \cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
                \cs:w @@_next~#1 \cs_end:
              }
          }
          {
%    \end{macrocode}
%    By default the algorithm sorts the code chunks and then saves the
%    result in a token list for fast execution by adding the code 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 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 temporarily
%    save the old code property list so that it can be restored later.
%    \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}{} }
          }
      }
  }
%    \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.
%   \fmi{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!  \fmi{Needs
%    checking for, just in case}
%
%    ^^A #1 <- \@@~#1
%    ^^A #2 <- \g_@@_#1_labels_clist
%    ^^A #3 <- #1
%    \begin{macrocode}
\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
%    labels listed there. Only 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: 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}
%    Take a breath and take a 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 combines 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}
%    
%    \begin{macrocode}
        \@@_clist_gput:NV #2 \l_@@_front_tl
        \@@_debug:n{ \iow_term:x{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 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 will do for now \ldots \fmi{fix}
%    \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
%    with 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.
%    \begin{macrocode}
    \exp_args:NNo \@@_tl_gput:Nn #1 { \cs:w @@_toplevel~#3 \cs_end: }
    \@@_tl_gput_right:No #1 { \cs:w @@_next~#3 \cs_end: }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_generate_variant:Nn \@@_initialize_single:NNn { cc }
%    \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 \verb=??=
%    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 sort
%    in the empty code. However that would complicate the algorithm a
%    lot with little gain.\footnote{This also hase the advantage that
%    the result of the sorting doesn't change which might otherwise
%    (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 \verb=??=, and the default
%    hook doesn't store any code.  Removing from \cs{l_@@_cur_hook_tl}
%    makes 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_x: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}
% \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_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 } }
\cs_new_protected:Npn \@@_log:nN #1 #2
  {
    \@@_preamble_hook:n {#1}
    \@@_log_cmd:x { ^^J ->~The~hook~'#1': }
%    \end{macrocode}
%    
%    \begin{macrocode}
    \hook_if_exist:nF {#1}
      { \@@_log_line:x { is~not~declared! } }
    \hook_if_empty:nTF {#1}
      { #2 { The~hook~is~empty } }
      {
        \@@_log_line:x { Code~chunks: }
        \prop_if_empty:cTF { g_@@_#1_code_prop }
          { \@@_log_line_indent:x { --- } }
          {
            \prop_map_inline:cn { g_@@_#1_code_prop }
              { \@@_log_line_indent:x { ##1~->~\tl_to_str:n {##2} } }
          }
%    \end{macrocode}
%
%    If there is code in the |top-level| token list, print it:
%    \begin{macrocode}
        \@@_log_line:x
          {
            Document-level~(top-level)~code
            \hook_if_exist:nT {#1}
              { ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
          }
        \@@_log_line_indent:x
          {
            \tl_if_empty:cTF { @@_toplevel~#1 }
              { --- }
              { -> ~ \exp_args:Nv \tl_to_str:n { @@_toplevel~#1 } }
          }
%    \end{macrocode}
%
%    \begin{macrocode}
        \@@_log_line:x { Extra~code~for~next~invocation: }
        \@@_log_line_indent:x
          {
            \tl_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_args:Nv \@@_log_next_code:n { @@_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
            { \hook_if_exist_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} { ,~ } }
              }
          }
          {
            #2
              {
                Hook~ \hook_if_exist:nTF {#1}
                  {code~pool~empty} {not~declared}
              }
          }
      }
  }
%    \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 first two tokens at the
%    front which are \cs{tl_gclear:N} and the token list to clear.
%    \begin{macrocode}
\cs_new:Npn \@@_log_next_code:n #1
  { \exp_args:No \tl_to_str:n { \use_none:nn #1 } }
%    \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 |??| if it is a default rule).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_list_rules:nn #1 #2
  {
    \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}
% \begin{macro}{%
%     \@@_gput_next_code:nn,
%     \@@_gput_next_do:nn,
%     \@@_gput_next_do:Nnn,
%     \@@_clear_next:n
%   }
%    \begin{macrocode}
\cs_new_protected:Npn \hook_gput_next_code:nn #1
  { \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
\cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
  {
    \@@_declare:n {#1}
    \hook_if_exist:nTF {#1}
      { \@@_gput_next_do:nn {#1} {#2} }
      { \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
  }
\cs_new_protected:Npn \@@_gput_next_do:nn #1
  {
    \exp_args:Nc \@@_gput_next_do:Nnn
      { @@_next~#1 } {#1}
  }
%    \end{macrocode}
%   First 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.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_do:Nnn #1 #2
  {
    \tl_if_empty:cT { @@~#2 }
      { \@@_update_hook_code:n {#2} }
    \tl_if_empty:NT #1
      { \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
    \@@_tl_gput_right:Nn #1
  }
\cs_new_protected:Npn \@@_clear_next:n #1
  { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%
% \subsection{Using the hook}
%
% \begin{macro}{\hook_use:n}
% \begin{macro}[EXP]{\@@_use_initialized:n}
% \begin{macro}{\@@_use_undefined:w,\@@_use_end:}
% \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 internally:  check if the hook exist as
%   given, and if so use it as quickly as possible.  If it doesn't
%   exist, the a call to \cs{@@_use:wn} checks for file hooks.
%
%   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{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}
\cs_new_protected:Npn \hook_use:n #1
  {
    \tl_if_exist:cTF { @@~#1 }
      {
        \@@_preamble_hook:n {#1}
        \cs:w @@~#1 \cs_end:
      }
      { \@@_use:wn #1 / \s_@@_mark {#1} }
  }
\cs_new:Npn \@@_use_initialized:n #1
  {
    \if_cs_exist:w @@~#1 \cs_end:
    \else:
      \@@_use_undefined:w
    \fi:
    \cs:w @@~#1 \@@_use_end:
  }
\cs_new:Npn \@@_use_undefined:w #1 #2 @@~#3 \@@_use_end:
  {
    #1 % fi
    \@@_use:wn #3 / \s_@@_mark {#3}
  }
\cs_new_protected:Npn \@@_preamble_hook:n #1
  { \@@_initialize_hook_code:n {#1} }
\cs_new_eq:NN \@@_use_end: \cs_end:
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_use:wn}
% \begin{macro}{\@@_try_file_hook:n,\@@_if_exist_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_exist_use:n} checks if the hook exist, and calls
%   \cs{@@_preamble_hook:n} if so, then uses the hook.
%    \begin{macrocode}
\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3
  {
    \str_if_eq:nnTF {#1} { file }
      { \@@_try_file_hook:n {#3} }
      { } % Hook doesn't exist
  }
\cs_new_protected:Npn \@@_try_file_hook:n #1
  {
    \@@_if_file_hook:wTF #1 / / \s_@@_mark
      {
        \exp_args:Ne \@@_if_exist_use:n
          { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
      }
      { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before)
  }
\cs_new_protected:Npn \@@_if_exist_use:n #1
  {
    \tl_if_exist:cT { @@~#1 }
      {
        \@@_preamble_hook:n {#1}
        \cs:w @@~#1 \cs_end:
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%  \begin{macro}{\hook_use_once:n}
%    For hooks that can and should be used only once we have a special
%    use command that remembers the hook name in
%    \cs{g_@@_execute_immediately_prop}. 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.
%
%    \pho{Should this raise an error if the hook doesn't exist?}
%    \begin{macrocode}
\cs_new_protected:Npn \hook_use_once:n #1
  {
    \tl_if_exist:cT { @@~#1 }
      {
        \tl_set:Nn \l_@@_return_tl {#1}
        \@@_normalize_hook_args:Nn \@@_use_once_store:n
          { \l_@@_return_tl }
        \hook_use:n {#1}
      }
  }
\cs_new_protected:Npn \@@_use_once_store:n #1
  { \prop_gput:Nnn \g_@@_execute_immediately_prop {#1} { } }
%    \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 four possible states.
% 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).
%
% 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).
%
% A hook is said to exist when it was declared with \cs{hook_new:n} or
% some variant thereof.
%
% \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[no-index]{g_@@_\meta{hook}_code_prop}, its
%   \cs[no-index]{@@_toplevel~\meta{hook}} and its
%   \cs[no-index]{@@_next~\meta{hook}} are empty.
%    \begin{macrocode}
\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
  {
    \@@_if_exist:nTF {#1}
      {
        \bool_lazy_and:nnTF
            { \prop_if_empty_p:c { g_@@_#1_code_prop } }
            {
              \bool_lazy_and_p:nn
                { \tl_if_empty_p:c { @@_toplevel~#1 } }
                { \tl_if_empty_p:c { @@_next~#1 } }
            }
          { \prg_return_true: }
          { \prg_return_false: }
      }
      { \prg_return_true: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\hook_if_exist:n}
%   A canonical way to test if a hook exists.  A hook exists if the
%   token list that stores the sorted code for that hook,
%   \cs[no-index]{@@~\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 \hook_if_exist:n #1 { p , T , F , TF }
  {
    \tl_if_exist:cTF { @@~#1 }
      { \prg_return_true: }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_exist:n}
%   An internal check if the hook has already been declared with
%   \cs{@@_declare: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_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_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 }
  {
    \if_int_compare:w \cs:w g_@@_#1_reversed_tl \cs_end: 1 < 0 \exp_stop_f:
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }
%    \end{macrocode}
% \end{macro}
%
%
%  \subsection{Messages}
%
%    \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}
\msg_new:nnn { hooks } { empty-label }
  {
    Empty~code~label~\msg_line_context:.~
    Using~`\@@_currname_or_default:'~instead.
  }
%    \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~ missspelling?
  }
  {
    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 } { misused-top-level }
  {
    Illegal~\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 } { ddhl-deprecated }
  {
    \iow_char:N \\DeclareDefaultHookLabel~is~deprecated.\\
    Use~\iow_char:N \\SetDefaultHookLabel~instead.\\ \\
    The~deprecated~name~will~be~removed~in~the~next~release.
  }
%    \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 { hooks } { should-not-happen }
  {
    ERROR!~This~should~not~happen.~#1 \\
    Please~report~at~https://github.com/latex3/latex2e.
  }
%    \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}{\AddToHook}
%    
%    \begin{macrocode}
\NewDocumentCommand \AddToHook { m o +m }
  { \hook_gput_code:nnn {#1} {#2} {#3} }
%    \end{macrocode}
%  \end{macro}
%
%  \begin{macro}{\AddToHookNext}
%    
%    \begin{macrocode}
\NewDocumentCommand \AddToHookNext { m +m }
  { \hook_gput_next_code:nn {#1} {#2} }
%    \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}
% \begin{macro}{\DeclareDefaultHookLabel}
% \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 label for 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:
%    \begin{macrocode}
%<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 finalle set the default label to be the \cs{@currname}:
%    \begin{macrocode}
%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
%    \end{macrocode}
%
%   Two commands keep track of the stack: when a file is input,
%   \cs{@@_curr_name_push:n} pushes the current default label to the
%   stack, and sets the new default label 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 the label will not be used again, and \cs{g_@@_hook_curr_name_tl}
%   is updated to 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:} 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.
%
%   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 }
  {
    \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} }
  }
\NewDocumentCommand \DeclareDefaultHookLabel { m }
  {
    \msg_error:nn { hooks } { ddhl-deprecated }
    \SetDefaultHookLabel {#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 behaviour 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}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
%
%  \begin{macro}{\UseHook,\UseOneTimeHook}
%    Avoid the overhead of \pkg{xparse} and its protection that we
%    don't want here (since the hook should vanish without trace if empty)!
%    \begin{macrocode}
\cs_new:Npn \UseHook        { \hook_use:n }
\cs_new:Npn \UseOneTimeHook { \hook_use_once:n }
%    \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 {@@_rule_gclear:nnn} plus fixing the property list for debugging.
%    \fmi{Need 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]{\IfHookExistsTF,\IfHookEmptyTF}
%   Here we avoid the overhead of \pkg{xparse}, since \cs{IfHookEmptyTF}
%   is used in \cs{end} (that is, every \LaTeX{} environment).  As a
%   further optimisation, use \cs{let} rather than \cs{def} to avoid one
%   expansion step.
%    \begin{macrocode}
\cs_new_eq:NN \IfHookExistsTF \hook_if_exist:nTF
\cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF
%    \end{macrocode}
% \end{macro}
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
%
% \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}{\@expl@@@initialize@all@@,
%                \@expl@@@hook@curr@name@pop@@}
%
%    \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}
%  \end{macro}
%
%    
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
%
%    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} vize versa (gh/401)}
%    \begin{macrocode}
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{0000/00/00}%
%<latexrelease>                 {\NewHook}{The hook management}%
%<latexrelease>
%<latexrelease>\def\NewHook#1{}
%<latexrelease>\def\NewReversedHook#1{}
%<latexrelease>\def\NewMirroredHookPair#1#2{}
%<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>\EndIncludeInRelease
%    \end{macrocode}
%
%
% \Finale
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
\endinput
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
