<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">% \iffalse meta-comment
%
% Copyright (C) 2020-2024
%     Frank Mittelbach, Phelype Oleinik and The LaTeX Project
%
% This file is part of the LaTeX base system.
% -------------------------------------------
%
% It may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%    https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008 or later.
%
% This file has the LPPL maintenance status "maintained".
%
% The list of all files belonging to the LaTeX base distribution is
% given in the file `manifest.txt'. See also `legal.txt' for additional
% information.
%
% The list of derived (unpacked) files belonging to the distribution
% and covered by LPPL is defined by the unpacking scripts (with
% extension .ins) which are part of the distribution.
%
% \fi
%
% \iffalse
%
%%% From File: ltfilehook.dtx
%
% \fi
%\iffalse
%&lt;*driver,structuredlog&gt;
%\fi
\def\ltfilehookdate{2024/03/13}
\def\ltfilehookversion{v1.0o}
%\iffalse
%&lt;/driver,structuredlog&gt;
%&lt;*driver&gt;
%\fi
\expanded{\noexpand\ProvidesFile{ltfilehook.dtx}
[\ltfilehookdate\space \ltfilehookversion\space LaTeX Kernel (hooks)]}
% \iffalse
%
\documentclass{l3doc}

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


\usepackage{structuredlog}  % for demonstration

\EnableCrossrefs
\CodelineIndex
\begin{document}
  \DocInput{ltfilehook.dtx}
\end{document}
%&lt;/driver&gt;
%
% \fi
%
%
% \long\def\fmi#1{\begin{quote}\itshape Todo: #1\end{quote}}
%
% \providecommand\hook[1]{\texttt{#1}}
%
%
% \title{The \texttt{ltfilehook} documentation\thanks{This code has version
%    \ltfilehookversion\ dated \ltfilehookdate, \copyright\ \LaTeX\
%    Project.}}
%
% \author{Frank Mittelbach, Phelype Oleinik, \LaTeX{} Project Team}
%
% \maketitle
%
%
%
% \tableofcontents
%
% \section{Introduction}
%
%
%
% \subsection{Provided hooks}
%
%    The code offers a number of hooks into which packages (or the
%    user) can add code to support different use cases.
%    Many hooks are offered as pairs (i.e., the second hook is
%    reversed. Also important to know is that these pairs are
%    properly nested with respect to other pairs of hooks.
%
%    There are hooks that are executed for all files of a certain type
%    (if they contain code), e.g., for all \enquote{include files} or
%    all \enquote{packages},
%    and there are also hooks that are specific to a single file,
%    e.g., do something after the package \texttt{foo.sty} has been
%    loaded.
%
%
% \subsection{General hooks for file reading}
% \label{sec:general-file-hooks}
%
%    There are four hooks that are called for each file that is read
%    using document-level commands such as \cs{input}, \cs{include},
%    \cs{usepackage}, etc.  They are not called for files read using
%    internal low-level methods, such as \cs{@input} or \cs{openin}.
%
%  \begin{variable}{file/before,file/.../before,
%                   file/.../after,file/after,
%                   }
%    These are:
%    \begin{description}
%    \item[\hook{file/before}, \hook{file/\meta{file-name}/before}]
%
%       These hooks are executed in that order just before the file is
%       loaded for reading. The code of the first hook is used
%       with every file, while the second is executed only for the
%       file with matching \meta{file-name} allowing you to specify
%       code that only applies to one file.
%
%    \item[\hook{file/\meta{file-name}/after}, \hook{file/after}]
%
%       These hooks are after the file with name \meta{file-name} has
%       been fully consumed. The order is swapped (the specific one
%       comes first) so that the \hook{/before} and \hook{/after}
%       hooks nest properly, which is important if any of them involve
%       grouping (e.g., contain environments, for example).
%       Furthermore both hooks are reversed hooks to support correct
%       nesting of different packages adding code to both
%       \hook{/before} and \hook{/after} hooks.
%
%    \end{description}
%  \end{variable}
%
%
%     So the overall sequence of hook processing for any file read
%     through the user interface commands of \LaTeX{} is:
%
%  \begin{tabbing}
%    mm\=mm\=mm\=mm\=\kill
%    \&gt;\cs{UseHook}\marg{\hook{file/before}} \\
%    \&gt;\cs{UseHook}\marg{\hook{file/\meta{file name}/before}} \\
%    \&gt;\&gt; \meta{file contents} \\
%    \&gt;\cs{UseHook}\marg{\hook{file/\meta{file name}/after}} \\
%    \&gt;\cs{UseHook}\marg{\hook{file/after}}
%  \end{tabbing}
%
%    The file hooks only refer to the file by its name and extension,
%    so the \meta{file name} should be the file name as it is on the
%    filesystem with extension (if any) and without paths.  Different
%    from \cs{input} and similar commands, the \texttt{.tex}
%    extension is not assumed in hook \meta{file name}, so \texttt{.tex}
%    files must be specified
%    with their extension to be recognized.
%    Files within subfolders should also be addressed by their name and
%    extension only.
%
%    Extensionless files also work, and should then be given without
%    extension.  Note however that \TeX{} prioritizes \texttt{.tex}
%    files, so if two files \texttt{foo} and \texttt{foo.tex} exist in
%    the search path, only the latter will be seen.
%
%    When a file is input, the \meta{file name} is available in
%    \cs{CurrentFile}, which is then used when accessing the
%    \hook{file/\meta{file name}/before} and
%    \hook{file/\meta{file name}/after}.
%
%  \begin{variable}{\CurrentFile}
%    The name of the file about to be read (or just finished) is
%    available to the hooks through \cs{CurrentFile} (there is no
%    \texttt{expl3} name for it for now).  The file is always provided
%    with its extension, i.e., how it appears on your hard drive, but
%    without any specified path to it. For example,
%    \verb=\input{sample}= and \verb=\input{app/sample.tex}= would
%    both have \cs{CurrentFile} being \texttt{sample.tex}.
%  \end{variable}
%
%  \begin{variable}{\CurrentFilePath}
%    The path to the current file (complement to \cs{CurrentFile}) is
%    available in \cs{CurrentFilePath} if needed.
%    The paths returned in \cs{CurrentFilePath} are only user paths,
%    given through \cs{input@path} (or \pkg{expl3}'s equivalent
%    \cs{l_file_search_path_seq}) or by directly typing in the path
%    in the \cs{input} command or equivalent.  Files located by
%    \texttt{kpsewhich} get the path added internally by the \TeX{}
%    implementation, so at the macro level it looks as if the file were
%    in the current folder, so the path in \cs{CurrentFilePath} is empty
%    in these cases (package and class files, mostly).
%  \end{variable}
%
%  \begin{variable}{\CurrentFileUsed,\CurrentFilePathUsed}
%    In normal circumstances these are identical to \cs{CurrentFile} and
%    \cs{CurrentFilePath}.  They will differ when a file substitution
%    has occurred for \cs{CurrentFile}.  In that case,
%    \cs{CurrentFileUsed} and \cs{CurrentFilePathUsed} will hold the
%    actual file name and path loaded by \LaTeX, while \cs{CurrentFile}
%    and \cs{CurrentFilePath} will hold the names that were
%    \emph{asked for}.  Unless doing very specific work on the file
%    being read, \cs{CurrentFile} and \cs{CurrentFilePath} should be
%    enough.
%  \end{variable}
%
% \subsection{Hooks for package and class files}
%
%    Commands to load package and class files (e.g., \cs{usepackage},
%    \cs{RequirePackage}, \cs{LoadPackageWithOptions}, etc.) offer the
%    hooks from section~\ref{sec:general-file-hooks} when they are
%    used to load a  package or class file, e.g.,
%    \hook{file/array.sty/after} would be called after the
%    \pkg{array} package got loaded. But as packages and classes form as special group
%    of files, there are some additional hooks available that only
%    apply when a package or class is loaded.
%
%
%  \begin{variable}{
%                   package/before,package/after,
%                   package/.../before,package/.../after,
%                   class/before,class/after,
%                   class/.../before,class/.../after,
%                   }
%    These are:
%    \begin{description}
%    \item[\hook{package/before}, \hook{package/after}]
%
%      These hooks are called for each package being loaded.
%
%    \item[\hook{package/\meta{name}/before},
%    \hook{package/\meta{name}/after}]
%
%      These hooks are additionally called if the package name is
%      \meta{name} (without extension).
%
%    \item[\hook{class/before}, \hook{class/after}]
%
%      These hooks are called for each class being loaded.
%
%    \item[\hook{class/\meta{name}/before}, \hook{class/\meta{name}/after}]
%
%      These hooks are additionally called if the class name is
%      \meta{name} (without extension).
%
%    \end{description}
%  \end{variable}
%     All \hook{/after} hooks are implemented as reversed hooks.
%
%     \noindent The overall sequence of execution for \cs{usepackage}
%     and friends is therefore:
%  \begin{tabbing}
%    mm\=mm\=mm\=mm\=\kill
%    \&gt;\cs{UseHook}\marg{\hook{package/before}} \\
%    \&gt;\cs{UseHook}\marg{\hook{package/\meta{package name}/before}} \\[5pt]
%    \&gt;\&gt;\cs{UseHook}\marg{\hook{file/before}} \\
%    \&gt;\&gt;\cs{UseHook}\marg{\hook{file/\meta{package name}.sty/before}} \\
%    \&gt;\&gt;\&gt; \meta{package contents} \\
%    \&gt;\&gt;\cs{UseHook}\marg{\hook{file/\meta{package name}.sty/after}} \\
%    \&gt;\&gt;\cs{UseHook}\marg{\hook{file/after}} \\[5pt]
%    \&gt;\&gt;\emph{code from \cs{AtEndOfPackage} if
%                used inside the package} \\[5pt]
%    \&gt;\cs{UseHook}\marg{\hook{package/\meta{package name}/after}} \\
%    \&gt;\cs{UseHook}\marg{\hook{package/after}}
%  \end{tabbing}
%    and similar for class file loading, except that \hook{package/}
%    is replaced by \hook{class/} and \cs{AtEndOfPackage} by
%    \cs{AtEndOfClass}.
%
%    If a package or class is not loaded (or it was loaded before the
%    hooks were set) none of the hooks are executed!
%
%    All class or package hooks involving the name of the class or
%    package are implemented as
%    one-time hooks, whereas all other such hooks are normal hooks. 
%    This allows for the following use case
%\begin{verbatim}
%  \AddToHook{package/varioref/after}
%        { ... apply my customizations if the package gets
%          loaded (or was loaded already) ... }
%\end{verbatim}
%    without the need to first test if the package is already loaded.
%
%
%
%
% \subsection{Hooks for \cs{include} files}
%
%    To manage \cs{include} files, \LaTeX{} issues a \cs{clearpage}
%    before and after loading such a file. Depending on the use case
%    one may want to execute code before or after these
%    \cs{clearpage}s especially for the one that is issued at the end.
%
%    Executing code before the final \cs{clearpage}, means that the
%    code is processed while the last page of the included material is
%    still under construction.  Executing code after it means that all
%    floats from inside the include file are placed (which
%    might have added further pages) and the final page has finished.
%
%    Because of these different scenarios we offer hooks in three
%    places.\footnote{If you want to execute code before the first
%     \cs{clearpage} there is no need to use a hook---you can write it
%     directly in front of the \cs{include}.}
%    None of the hooks are executed when an \cs{include} file is
%    bypassed because of an \cs{includeonly} declaration. They are,
%    however, all executed if \LaTeX{} makes an attempt to load the
%    \cs{include} file (even if it doesn't exist and all that happens
%    is \enquote{\texttt{No file \meta{filename}.tex}}).
%
%
%  \begin{variable}{include/before,include/.../before,
%                   include/end,include/.../end,
%                   include/after,include/.../after,
%                  }
%    These are:
%    \begin{description}
%
%    \item[\hook{include/before}, \hook{include/\meta{name}/before}]
%
%      These hooks are executed (in that order) after the initial
%      \cs{clearpage} and after \texttt{.aux} file is changed to use
%      \texttt{\meta{name}.aux}, but before the
%      \texttt{\meta{name}.tex} file is loaded. In other words they are executed
%      at the very beginning of the first page of the \cs{include}
%      file.
%
%
%    \item[\hook{include/\meta{name}/end}, \hook{include/end}]
%
%      These hooks are executed (in that order) after \LaTeX{} has
%      stopped reading from the \cs{include} file, but before it has
%      issued a \cs{clearpage} to output any deferred floats.
%
%
%    \item[\hook{include/\meta{name}/after}, \hook{include/after}]
%
%      These hooks are executed (in that order) after \LaTeX{} has
%      issued the \cs{clearpage} but before is has switched back
%      writing to the main \texttt{.aux} file. Thus technically we are
%      still inside the \cs{include} and if the hooks generate any
%      further typeset material including anything that writes to the
%      \texttt{.aux} file, then it would be considered part of the
%      included material and bypassed if it is not loaded because of
%      some \cs{includeonly} statement.\footnotemark
%
%    \item[\hook{include/excluded}, \hook{include/\meta{name}/excluded}]
%
%      The above hooks for \cs{include} files are only executed when
%      the file is loaded (or more exactly the load is attempted). If,
%      however, the \cs{include} file is explicitly excluded (through
%      an \cs{includeonly} statement) the above
%      hooks are bypassed and instead the \hook{include/excluded}
%      hook followed by the \hook{include/\meta{name}/excluded} hook
%      are executed. This happens after
%      \LaTeX{} has loaded the \texttt{.aux} file for this include file,
%      i.e., after \LaTeX{} has updated its counters to pretend that the file
%      was seen.
%
%    \end{description}
%  \end{variable}\footnotetext{For that reason
%      another \cs{clearpage} is executed after these hooks which
%      normally does nothing, but starts a new page if further material
%      got added this way.}
%
%
%    All \hook{include} hooks involving the name of the included file are implemented as
%    one-time hooks (whereas all other such hooks are normal hooks).
%
%    If you want to execute code that is run for every \cs{include}
%    regardless of whether or not it is excluded, use the
%    \hook{cmd/include/before} or \hook{cmd/include/after} hooks.
%
%
%
% \subsection{High-level interfaces for \LaTeX{}}
%
%    We do not provide any additional wrappers around the hooks (like
%    \pkg{filehook} or \pkg{scrlfile} do) because we believe that for
%    package writers the high-level commands from the hook management,
%    e.g., \cs{AddToHook}, etc.\
%    are sufficient and in fact easier to work with, given that the hooks
%    have consistent naming conventions.
%
%
%
% \subsection{Kernel, class, and package interfaces for \LaTeX{}}
%
% \begin{function}{\declare@file@substitution,\undeclare@file@substitution}
%   \begin{syntax}
%     \cs{declare@file@substitution}   \Arg{file} \Arg{replacement-file}
%     \cs{undeclare@file@substitution} \Arg{file}
%   \end{syntax}
%    If \meta{file} is requested for loading replace it with
%    \meta{replacement-file}. \cs{CurrentFile} remains pointing to
%    \meta{file} but \cs{CurrentFileUsed} will show the file actually
%    loaded.
%
%    The main use case for this declaration is to provide a corrected
%    version of a package that can't be changed (due to its license)
%    but no longer functions because of \LaTeX{} kernel changes, for
%    example, or to provide a version that makes use of new kernel
%    functionality while the original package remains available for
%    use with older releases.
%    As such it is mainly meant for use in the \LaTeX{} kernel but
%    other use cases are conceivable.
%
%    The \cs{undeclare@file@substitution} declaration undoes a
%    substitution made earlier.
%
%    \begin{quote}
%      \em
%      Please do not misuse this functionality and replace a file with
%      another unless if really needed and only if the new version is
%      implementing the same functionality as the original one!
%    \end{quote}
%  \end{function}
%
% \begin{function}{\disable@package@load,\reenable@package@load}
%   \begin{syntax}
%     \cs{disable@package@load}  \Arg{package} \Arg{alternate-code}
%     \cs{reenable@package@load} \Arg{package}
%   \end{syntax}
%    If \meta{package} is requested, do not load it but instead run
%    \meta{alternate-code} which could issue a warning, error or any
%    other code.
%
%    The main use case is for classes that want to restrict the set of
%    supported packages or contain code that make the use of some
%    packages impossible. So rather than waiting until the document
%    breaks they can set up informative messages why certain packages
%    are not available.
%
%    The function is only implemented for packages not for arbitrary
%    files and again it should only be applied if there are good
%    reasons for doing this.\footnote{Just to be sure: \enquote{I don't
%    like this package by somebody else} is not a good one :-)}
%  \end{function}
%
%
% \subsection{A sample package for structuring the log output}
%
%    As an application we provide the package \pkg{structuredlog} that
%    adds lines to the \texttt{.log} when a file is opened and closed
%    for reading keeping track of nesting level es well.
%    For example, for the current document it adds the lines
%\begin{verbatim}
%    = (LEVEL 1 START) t1lmr.fd
%    = (LEVEL 1 STOP) t1lmr.fd
%    = (LEVEL 1 START) supp-pdf.mkii
%    = (LEVEL 1 STOP) supp-pdf.mkii
%    = (LEVEL 1 START) nameref.sty
%    == (LEVEL 2 START) refcount.sty
%    == (LEVEL 2 STOP) refcount.sty
%    == (LEVEL 2 START) gettitlestring.sty
%    == (LEVEL 2 STOP) gettitlestring.sty
%    = (LEVEL 1 STOP) nameref.sty
%    = (LEVEL 1 START) ltfilehook-doc.out
%    = (LEVEL 1 STOP) ltfilehook-doc.out
%    = (LEVEL 1 START) ltfilehook-doc.out
%    = (LEVEL 1 STOP) ltfilehook-doc.out
%    = (LEVEL 1 START) ltfilehook-doc.hd
%    = (LEVEL 1 STOP) ltfilehook-doc.hd
%    = (LEVEL 1 START) ltfilehook.dtx
%    == (LEVEL 2 START) ot1lmr.fd
%    == (LEVEL 2 STOP) ot1lmr.fd
%    == (LEVEL 2 START) omllmm.fd
%    == (LEVEL 2 STOP) omllmm.fd
%    == (LEVEL 2 START) omslmsy.fd
%    == (LEVEL 2 STOP) omslmsy.fd
%    == (LEVEL 2 START) omxlmex.fd
%    == (LEVEL 2 STOP) omxlmex.fd
%    == (LEVEL 2 START) umsa.fd
%    == (LEVEL 2 STOP) umsa.fd
%    == (LEVEL 2 START) umsb.fd
%    == (LEVEL 2 STOP) umsb.fd
%    == (LEVEL 2 START) ts1lmr.fd
%    == (LEVEL 2 STOP) ts1lmr.fd
%    == (LEVEL 2 START) t1lmss.fd
%    == (LEVEL 2 STOP) t1lmss.fd
%    = (LEVEL 1 STOP) ltfilehook.dtx
%\end{verbatim}
%    Thus if you inspect an issue in the \texttt{.log} it is easy to
%    figure out in which file it occurred, simply by searching back for
%    \texttt{LEVEL} and if it is a \texttt{STOP} then remove 1 from
%    the level value and search further for \texttt{LEVEL} with that value
%    which should then be the \texttt{START}  level of the file you are in.
%
% \MaybeStop{\setlength\IndexMin{200pt}  \PrintIndex  }
%
%
% \section{The Implementation}
%
%    \begin{macrocode}
%&lt;*2ekernel&gt;
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;@@=filehook&gt;
%    \end{macrocode}
%
% \changes{v1.0k}{2021/05/24}{Use \cs{msg_...} instead of \cs{__kernel_msg...}}
%
%
% \subsection{Document and package-level commands}
%
%
% \begin{macro}{\CurrentFile,\CurrentFilePath}
% \begin{macro}{\CurrentFileUsed,\CurrentFilePathUsed}
%   User-level macros that hold the current file name and file path.
%   These are used internally as well because the code takes care to
%   protect against a possible redefinition of these macros in the
%   loaded file (it's necessary anyway to make hooks work with nested
%   \cs{input}).  The versions |\...Used| hold the \emph{actual} file
%   name and path that is loaded by \LaTeX, whereas the other two hold
%   the name as requested.  They will differ in case there's a file
%   substitution.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;                 {\CurrentFile}{Hook management file}%
\ExplSyntaxOn
\tl_new:N \CurrentFile
\tl_new:N \CurrentFilePath
\tl_new:N \CurrentFileUsed
\tl_new:N \CurrentFilePathUsed
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;                 {\CurrentFile}{Hook management file}%
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\let \CurrentFile         \@undefined
%&lt;latexrelease&gt;\let \CurrentFilePath     \@undefined
%&lt;latexrelease&gt;\let \CurrentFileUsed     \@undefined
%&lt;latexrelease&gt;\let \CurrentFilePathUsed \@undefined
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
% \subsection{\pkg{expl3} helpers}
%
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\@@_file_parse_full_name:nN}{File helpers}%
\ExplSyntaxOn
%    \end{macrocode}
%
% \begin{macro}{
%     \@@_file_parse_full_name:nN,
%     \@@_full_name:nn,
%   }
%   A utility macro to trigger \pkg{expl3}'s file-parsing and lookup,
%   and return a normalized representation of the file name.  If the
%   queried file doesn't exist, no normalization takes place.
%   The output of \cs{@@_file_parse_full_name:nN} is passed on to the
%   |#2|---a 3-argument macro that takes the \meta{path}, \meta{base},
%   and \meta{ext} parts of the file name.
%
%    \begin{macrocode}
\cs_new:Npn \@@_file_parse_full_name:nN #1
  {
    \exp_args:Nf \file_parse_full_name_apply:nN
      {
        \exp_args:Nf \@@_full_name:nn
          { \file_full_name:n {#1} } {#1}
      }
  }
\cs_new:Npn \@@_full_name:nn #1 #2
  {
    \tl_if_empty:nTF {#1}
      { \tl_trim_spaces:n {#2} }
      { \tl_trim_spaces:n {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{
%     \@@_if_no_extension:nTF,
%     \@@_drop_extension:N
%   }
%   Some actions depend on whether the file extension was explicitly
%   given, and sometimes the extension has to be removed.  The macros
%   below use \cs{@@_file_parse_full_name:nN} to split up the file name
%   and either check if \meta{ext} (|#3|) is empty, or discard it.
%    \begin{macrocode}
\cs_new:Npn \@@_if_no_extension:nTF #1
  {
    \exp_args:Ne \tl_if_empty:nTF
      { \file_parse_full_name_apply:nN {#1} \use_iii:nnn }
  }
\cs_new_protected:Npn \@@_drop_extension:N #1
  {
    \tl_gset:Nx #1
      {
        \exp_args:NV \@@_file_parse_full_name:nN #1
          \@@_drop_extension_aux:nnn
      }
  }
\cs_new:Npn \@@_drop_extension_aux:nnn #1 #2 #3
   { \tl_if_empty:nF {#1} { #1 / } #2 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\g_@@_input_file_seq,\l_@@_internal_tl}
% \begin{macro}{\@@_file_push:,\@@_file_pop:}
% \begin{macro}{\@@_file_pop_assign:nnnn}
%   Yet another stack, to keep track of \cs{CurrentFile} and
%   \cs{CurrentFilePath} with nested \cs{input}s.  At the beginning of
%   \cs{InputIfFileExists}, the current value of \cs{CurrentFilePath}
%   and \cs{CurrentFile} is pushed to \cs{g_@@_input_file_seq}, and
%   at the end, it is popped and the value reassigned.  Some other
%   places don't use \cs{InputIfFileExists} directly (\cs{include}) or
%   need \cs{CurrentFile} earlier (\cs{@onefilewithoptions}), so these
%   are manually used elsewhere as well.
%   \changes{v1.0h}{2021/03/18}
%           {Define \cs{g_@@_input_file_seq} to avoid losing data when
%            rolling back.}
%   \changes{v1.0l}{2021/08/27}{Internal message name changes}
%    \begin{macrocode}
\tl_new:N \l_@@_internal_tl
\seq_if_exist:NF \g_@@_input_file_seq
  { \seq_new:N \g_@@_input_file_seq }
\cs_new_protected:Npn \@@_file_push:
  {
    \seq_gpush:Nx \g_@@_input_file_seq
      {
        { \CurrentFilePathUsed } { \CurrentFileUsed }
        { \CurrentFilePath     } { \CurrentFile     }
      }
  }
\cs_new_protected:Npn \@@_file_pop:
  {
    \seq_gpop:NNTF \g_@@_input_file_seq \l_@@_internal_tl
      { \exp_after:wN \@@_file_pop_assign:nnnn \l_@@_internal_tl }
      {
        \msg_error:nnn { latex2e } { should-not-happen }
          { Tried~to~pop~from~an~empty~file~name~stack. }
      }
  }
\cs_new_protected:Npn \@@_file_pop_assign:nnnn #1 #2 #3 #4
  {
    \tl_set:Nn \CurrentFilePathUsed {#1}
    \tl_set:Nn \CurrentFileUsed {#2}
    \tl_set:Nn \CurrentFilePath {#3}
    \tl_set:Nn \CurrentFile {#4}
  }
\ExplSyntaxOff
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    When rolling forward the following expl3 functions may not be defined.
%    If we roll back the code does nothing.
% \changes{v1.0d}{2020/11/24}{Support for roll forward (gh/434)}
% \InternalDetectionOff
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;                 {\file_parse_full_name_apply:nN}{Roll forward help}%
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\ExplSyntaxOn
%&lt;latexrelease&gt;\cs_if_exist:NF\file_parse_full_name_apply:nN
%&lt;latexrelease&gt;{
%&lt;latexrelease&gt;\cs_new:Npn \file_parse_full_name_apply:nN #1
%&lt;latexrelease&gt;  {
%&lt;latexrelease&gt;    \exp_args:Ne \__file_parse_full_name_auxi:nN
%&lt;latexrelease&gt;      { \__kernel_file_name_sanitize:n {#1} }
%&lt;latexrelease&gt;  }
%&lt;latexrelease&gt;\cs_new:Npn \__file_parse_full_name_auxi:nN #1
%&lt;latexrelease&gt;  {
%&lt;latexrelease&gt;    \__file_parse_full_name_area:nw { } #1
%&lt;latexrelease&gt;      / \s__file_stop
%&lt;latexrelease&gt;  }
%&lt;latexrelease&gt;\cs_new:Npn \__file_parse_full_name_area:nw #1 #2 / #3 \s__file_stop
%&lt;latexrelease&gt;  {
%&lt;latexrelease&gt;    \tl_if_empty:nTF {#3}
%&lt;latexrelease&gt;      { \__file_parse_full_name_base:nw { } #2 . \s__file_stop {#1} }
%&lt;latexrelease&gt;      { \__file_parse_full_name_area:nw { #1 / #2 }
%&lt;latexrelease&gt;                                        #3 \s__file_stop }
%&lt;latexrelease&gt;  }
%&lt;latexrelease&gt;\cs_new:Npn \__file_parse_full_name_base:nw #1 #2 . #3 \s__file_stop
%&lt;latexrelease&gt;  {
%&lt;latexrelease&gt;    \tl_if_empty:nTF {#3}
%&lt;latexrelease&gt;      {
%&lt;latexrelease&gt;        \tl_if_empty:nTF {#1}
%&lt;latexrelease&gt;          {
%&lt;latexrelease&gt;            \tl_if_empty:nTF {#2}
%&lt;latexrelease&gt;              { \__file_parse_full_name_tidy:nnnN { } { } }
%&lt;latexrelease&gt;              { \__file_parse_full_name_tidy:nnnN { .#2 } { } }
%&lt;latexrelease&gt;          }
%&lt;latexrelease&gt;          { \__file_parse_full_name_tidy:nnnN {#1} { .#2 } }
%&lt;latexrelease&gt;      }
%&lt;latexrelease&gt;      { \__file_parse_full_name_base:nw { #1 . #2 }
%&lt;latexrelease&gt;                                        #3 \s__file_stop }
%&lt;latexrelease&gt;  }
%&lt;latexrelease&gt;\cs_new:Npn \__file_parse_full_name_tidy:nnnN #1 #2 #3 #4
%&lt;latexrelease&gt;  {
%&lt;latexrelease&gt;    \exp_args:Nee #4
%&lt;latexrelease&gt;      {
%&lt;latexrelease&gt;        \str_if_eq:nnF {#3} { / } { \use_none:n }
%&lt;latexrelease&gt;        #3 \prg_do_nothing:
%&lt;latexrelease&gt;      }
%&lt;latexrelease&gt;      { \use_none:n #1 \prg_do_nothing: }
%&lt;latexrelease&gt;      {#2}
%&lt;latexrelease&gt;  }
%&lt;latexrelease&gt;}
%&lt;latexrelease&gt;\ExplSyntaxOff
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \InternalDetectionOn
%
%    \begin{macrocode}
%&lt;@@=&gt;
%    \end{macrocode}
%
% \subsection{Declaring the file-related hooks}
%
% These hooks have names with three-parts that 
% start with \hook{file/}, \hook{include/},
% \hook{class/} or \hook{package/} and end with \hook{/before} or
% \hook{/after} (or \hook{/end} in the case of \hook{include/}). 
% They are all generic hooks
% so will be declared only if code is added to them; 
% this declaration is done for you automatically and, indeed, they should
% not be declared explicitly. 
%
% Those named \hook{.../after} and \hook{include/.../end}
% are, when code is added, declared as reversed hooks.
%
%
% \subsection{Patching \LaTeX{}'s \cs{InputIfFileExists} command}
%
%   Most of what we have to do is adding \cs{UseHook} into several
%  \LaTeXe{} core commands, because of some circular dependencies in the
%  kernel we  do this only now and not in \texttt{ltfiles}.
%
% \begin{macro}{\InputIfFileExists}
% \begin{macro}{\@input@file@exists@with@hooks}
% \begin{macro}{\unqu@tefilef@und}
%    \cs{InputIfFileExists} loads any file if it is available so we
%    have to add the hooks \hook{file/before} and
%    \hook{file/after} in the right places. If the file doesn't
%    exist no hooks should be executed.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\InputIfFileExists}{Hook management (files)}%
%&lt;*2ekernel|latexrelease&gt;
%    \end{macrocode}
%
%    \begin{macrocode}
\let\InputIfFileExists\@undefined
\DeclareRobustCommand \InputIfFileExists[2]{%
  \IfFileExists{#1}%
    {%
      \@expl@@@filehook@file@push@@
      \@filehook@set@CurrentFile
%    \end{macrocode}
%    We pre-expand \cs{@filef@und} so that in case another file is
%    loaded in the true branch of \cs{InputIfFileExists}, these don't
%    change their value meanwhile. This isn't a worry with
%    \cs[no-index]{CurrentFile...} because they are kept in a stack.
%
% \changes{v1.0d}{2020/11/20}
%   {Move loading to \cs{@input@file@exists@with@hooks} and expand
%    \cs{@filef@und} to avoid getting the wrong file name in the case of
%    a substitution.}
%    \begin{macrocode}
      \expandafter\@swaptwoargs\expandafter
        {\expandafter\@input@file@exists@with@hooks
          \expandafter{\@filef@und}}%
        {#2}%
      \@expl@@@filehook@file@pop@@
    }%
}
\def\@input@file@exists@with@hooks#1{%
%    \end{macrocode}
%    If the file exists then \cs{CurrentFile} holds its name. But we
%    can't rely on that still being true after the file has been
%    processed. Thus for using the name in the file hooks we need to
%    preserve the name and then restore it for the
%    \hook{file/.../after} hook.
%
%    The hook always refers to the file requested by the user.  The hook
%    is \emph{always} loaded for \cs{CurrentFile} which usually is the
%    same as \cs{CurrentFileUsed}.  In the case of a file replacement,
%    the \cs{CurrentFileUsed} holds the actual file loaded.  In any case
%    the file names are normalized so that the hooks work on the real
%    file name, rather than what the user typed in.
%
%    \pkg{expl3}'s \cs{file_full_name:n} normalizes the file
%    name (to factor out differences in the |.tex| extension), and
%    then does a file lookup to take into account a possible path from
%    \cs{l_file_search_path_seq} and \cs{input@path}.  However only
%    the file name and extension are returned so that file hooks can
%    refer to the file by their name only.  The path to the file is
%    returned in \cs{CurrentFilePath}.
%    \changes{v1.0e}{2021/01/07}{Restore \cs[no-index]{CurrentFile(Path)(Used)}
%      after the input (gh/464)}
%    \begin{macrocode}
  \edef\reserved@a{%
    \@expl@@@filehook@file@pop@assign@@nnnn
      {\CurrentFilePathUsed}%
      {\CurrentFileUsed}%
      {\CurrentFilePath}%
      {\CurrentFile}}%
  \expandafter\@swaptwoargs\expandafter{\reserved@a}%
%    \end{macrocode}
%
% Before adding to the file list we need to make all (letter) characters
% catcode~11, because several packages use constructions like
% \begin{verbatim}
% \filename@parse{&lt;filename&gt;}
% \ifx\filename@ext\@clsextension
%   ...
% \fi
% \end{verbatim}
% and that doesn't work if \cs{filename@ext} is \cs{detokenize}d.
% Making \cs{@clsextension} a string doesn't help much because some
% packages define their own \cs[no-index]{&lt;prefix&gt;@someextension} with
% normal catcodes.  This is not entirely correct because packages loaded
% (somehow) with catcode~12 alphabetic tokens (say, as the result of
% a \cs{string} or \cs{detokenize} command, or from a \TeX{} string like
% \cs{jobname}) will have these character tokens incorrectly turned into
% letter tokens.  This however is rare, so we'll go for the all-letters
% approach (grepping the packages in \TeX{} Live didn't bring up any
% obvious candidate for breaking with this catcode change).
%    \begin{macrocode}
    {\edef\reserved@a{\unqu@tefilef@und#1\@nil}%
     \@addtofilelist{\string@makeletter\reserved@a}%
     \UseHook{file/before}%
%    \end{macrocode}
%    The current file name is available in \cs{CurrentFile} so we use
%    that in the specific hook.
%    \begin{macrocode}
     \UseHook{file/\CurrentFile/before}%
     \@@input #1% &lt;- trailing space comes from \@filef@und
    }%
%    \end{macrocode}
%    And here, \cs{CurrentFile} is restored
%    (by \cs{@expl@@@filehook@file@pop@assign@@nnnn}) so we can use it once more.
%    \begin{macrocode}
  \UseHook{file/\CurrentFile/after}%
  \UseHook{file/after}}
\def\unqu@tefilef@und"#1" \@nil{#1}
%    \end{macrocode}
%
% \changes{v1.0l}{2021/08/25}{Declare non-generic file hooks}
%   Now declare the non-generic file hooks used above:
%    \begin{macrocode}
\NewHook{file/before}
\NewReversedHook{file/after}
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;/2ekernel|latexrelease&gt;
%    \end{macrocode}
%
% \changes{v0.9b}
%         {1993/12/04}{Macro added}
% \changes{v0.9p}
%         {1994/01/18}{New Definition}
% \changes{v0.3b}{1994/03/13}
%         {Use new cmd \cs{@addtofilelist}}
% Now define |\InputIfFileExists| to input |#1| if it seems to exist.
% Immediately prior to the input, |#2| is executed.
% If the file |#1| does not exist, execute `|#3|'.
% \changes{v1.0t}{1995/05/25}
%         {(CAR) added \cs{long}}
% \changes{v1.1o}{2019/02/07}{Expand \cs{@filef@und} before executing
%   second argument (github/109)}
% \changes{v1.2b}{2019/08/27}{Make command robust}
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{2019/10/01}%
%&lt;latexrelease&gt;          {\InputIfFileExists}{Hook management (files)}%
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\DeclareRobustCommand \InputIfFileExists[2]{%
%&lt;latexrelease&gt;  \IfFileExists{#1}%
%&lt;latexrelease&gt;    {%
%&lt;latexrelease&gt;  \expandafter\@swaptwoargs\expandafter
%&lt;latexrelease&gt;      {\@filef@und}{#2\@addtofilelist{#1}\@@input}}}
%&lt;latexrelease&gt;\let\@input@file@exists@with@hooks\@undefined
%&lt;latexrelease&gt;\let\unqu@tefilef@und\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;          {\InputIfFileExists}{Hook management (files)}%
%&lt;latexrelease&gt;\long\def \InputIfFileExists#1#2{%
%&lt;latexrelease&gt;  \IfFileExists{#1}%
%&lt;latexrelease&gt;    {#2\@addtofilelist{#1}\@@input \@filef@und}}
%    \end{macrocode}
%
%    Also undo the internal command as some packages unfortunately test
%    for their existence instead of using \cs{IfFormatAtLeastTF}.
% \changes{v1.0g}{2021/02/08}{Undo the internal for robust
%                 \cs{InputIfFileExists} in rollback (gh/494)}
%    \begin{macrocode}
%&lt;latexrelease&gt;\expandafter\let\csname InputIfFileExists \endcsname\@undefined
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\let\@input@file@exists@with@hooks\@undefined
%&lt;latexrelease&gt;\let\unqu@tefilef@und\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%
%
%
%
%
%
% \subsection{Declaring a file substitution}
%
%    \begin{macrocode}
%&lt;@@=filehook&gt;
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\@@_subst_add:nn}{Declaring file substitution}%
\ExplSyntaxOn
%    \end{macrocode}
%
%
% \begin{macro}{\@@_subst_add:nn,\@@_subst_remove:n,
%               \@@_subst_file_normalize:Nn,\@@_subst_empty_name_chk:NN}
%   \cs{@@_subst_add:nn} declares a file substitution by
%   doing a (global) definition of the form
%   |\def|\cs{@file-subst@\meta{file}}|{|\meta{replacement}|}|.
%   The file names are properly sanitised, and normalized with the same
%   treatment done for the file hooks.  That is, a file replacement is
%   declared by using the file name (and extension, if any) only, and
%   the file path should not be given.  If a file name is empty it is
%   replaced by |.tex| (the empty csname is used to check that).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_subst_add:nn #1 #2
  {
    \group_begin:
      \cs_set:cpx { } { \exp_not:o { \cs:w\cs_end: } }
      \int_set:Nn \tex_escapechar:D { -1 }
      \cs_gset:cpx
        {
          @file-subst@
          \@@_subst_file_normalize:Nn \use_ii_iii:nnn {#1}
        }
        { \@@_subst_file_normalize:Nn \@@_file_name_compose:nnn
                                      {#2} }
    \group_end:
  }
\cs_new_protected:Npn \@@_subst_remove:n #1
  {
    \group_begin:
      \cs_set:cpx { } { \exp_not:o { \cs:w\cs_end: } }
      \int_set:Nn \tex_escapechar:D { -1 }
      \cs_undefine:c
        {
          @file-subst@
          \@@_subst_file_normalize:Nn \use_ii_iii:nnn {#1}
        }
    \group_end:
  }
\cs_new:Npn \@@_subst_file_normalize:Nn #1 #2
  {
    \exp_after:wN \@@_subst_empty_name_chk:NN
      \cs:w \exp_after:wN \cs_end:
        \cs:w \@@_file_parse_full_name:nN {#2} #1 \cs_end:
  }
\cs_new:Npn \@@_subst_empty_name_chk:NN #1 #2
  { \if_meaning:w #1 #2 .tex \else: \token_to_str:N #2 \fi: }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\use_ii_iii:nnn}
%    A variant of \cs[no-index]{use_...} to discard the first of three
%    arguments.
%    \fmi{this should move to \pkg{expl3}}
%    \begin{macrocode}
\cs_gset:Npn \use_ii_iii:nnn #1 #2 #3 {#2 #3}
%    \end{macrocode}
% \end{macro}
%
%
%    \begin{macrocode}
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
%
%
%
% \begin{macro}{\declare@file@substitution}
% \begin{macro}{\undeclare@file@substitution}
%    For two internals we provide \LaTeXe{} names so that we can use
%    them elsewhere in the kernel (and so that they can be used in
%    packages if really needed, e.g., \pkg{scrlfile}).
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\declare@file@substitution}{File substitution}%
\ExplSyntaxOn
\cs_new_eq:NN \declare@file@substitution   \@@_subst_add:nn
\cs_new_eq:NN \undeclare@file@substitution \@@_subst_remove:n
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    We are not fully rolling back the file substitutions in case a
%    rollback encounters a package that contains them, but is itself
%    not setup for rollback. So we just bypass them and hope for the
%    best.
% \changes{v1.0d}{2020/12/04}{Don't drop file substitution commands on
%    rollback}
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;          {\declare@file@substitution}{File substitution}%
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\let \declare@file@substitution   \@gobbletwo
%&lt;latexrelease&gt;\let \undeclare@file@substitution \@gobble
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
%
%    \begin{macrocode}
%&lt;@@=&gt;
%    \end{macrocode}
%
% \subsection{Selecting a file (\cs{set@curr@file})}
%
% \begin{macro}{\set@curr@file,\set@curr@file@nosearch}
% \begin{macro}{\@curr@file,\@curr@file@reqd}
%  \changes{v1.0f}{2021/01/31}{set \cs{protect} to \cs{string} gh/481}
%   Now we hook into \cs{set@curr@file} to resolve a possible file
%   substitution, and add \cs{@expl@@@filehook@set@curr@file@@nNN}
%   at the end, after \cs{@curr@file} is set.
%
%   A file name is built using
%   \cs{expandafter}\cs{string}\cs{csname}\meta{filename}\cs{endcsname}
%   to avoid expanding utf8 active characters.  The \cs{csname} expands
%   the normalization machinery and the routine to resolve a file
%   substitution, returning a control sequence with the same name as the
%   file.
%
%   It happens that when \meta{filename} is empty, the generated control
%   sequence is \cs{csname\cs{endcsname}}, and doing \cs{string} on
%   that results in the file |csnameendcsname.tex|.  To guard against
%   that we \cs{ifx}-compare the generated control sequence with the
%   empty csname.  To do so, \cs{csname\cs{endcsname}} has to be
%   defined, otherwise it would be equal to \cs{relax} and we would have
%   false positives.  Here we define \cs{csname\cs{endcsname}} to
%   expand to itself to avoid it matching the definition of some other
%   control sequence.
%   \changes{v1.0i}{2021/04/20}
%           {Make \string~ expand to a string (tracks change in l3kernel)}
%   \changes{v1.0m}{2022/03/10}
%           {Add \cs{set@curr@file@nosearch} for \pkg{graphicx}}
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2022/06/01}%
%&lt;latexrelease&gt;          {\set@curr@file}{Setting current file name}%
\def\set@curr@file{%
  \begingroup
    \set@curr@file@aux}
\edef\set@curr@file@nosearch{%
  \begingroup
    \let\noexpand\input@path\noexpand\@empty
    \csname seq_clear:N\endcsname
      \expandafter\noexpand\csname l_file_search_path_seq\endcsname
    \noexpand\set@curr@file@aux}
\def\set@curr@file@aux#1{%
    \escapechar\m@ne
    \let\protect\string
    \edef~{\string~}%
    \expandafter\def\csname\expandafter\endcsname
      \expandafter{\csname\endcsname}%
%    \end{macrocode}
%   Two file names are set here: \cs{@curr@file@reqd} which is the file
%   requested by the user, and \cs{@curr@file} which should be the same,
%   except when we have a file substitution, in which case it holds the
%   actual loaded file.  \cs{@curr@file} is resolved first, to check if
%   a substitution happens.  If it doesn't,
%   \cs{@expl@@@filehook@if@file@replaced@@TF} short-cuts and just copies
%   \cs{@curr@file}, otherwise the full normalization procedure is
%   executed.
%
%   At this stage the file name is parsed and normalized, but if the
%   input doesn't have an extension, the default |.tex| is \emph{not}
%   added to \cs{@curr@file} because for applications other than
%   \cs{input} (graphics, for example) the default extension may not
%   be |.tex|.  First check if the input has an extension, then if the
%   input had no extension, call \cs{@expl@@@filehook@drop@extension@@N}.  In case
%   of a file substitution, \cs{@curr@file} will have an extension.
%    \begin{macrocode}
    \@expl@@@filehook@if@no@extension@@nTF{#1}%
      {\@tempswatrue}{\@tempswafalse}%
    \@kernel@make@file@csname\@curr@file
      \@expl@@@filehook@resolve@file@subst@@w {#1}%
    \@expl@@@filehook@if@file@replaced@@TF
      {\@kernel@make@file@csname\@curr@file@reqd
         \@expl@@@filehook@normalize@file@name@@w{#1}%
       \if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file@reqd \fi}%
      {\if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file \fi
       \global\let\@curr@file@reqd\@curr@file}%
    \@expl@@@filehook@clear@replacement@flag@@
  \endgroup}
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{2021/06/01}%
%&lt;latexrelease&gt;          {\set@curr@file}{Setting current file name}%
%&lt;latexrelease&gt;\def\set@curr@file#1{%
%&lt;latexrelease&gt;  \begingroup
%&lt;latexrelease&gt;    \escapechar\m@ne
%&lt;latexrelease&gt;    \let\protect\string
%&lt;latexrelease&gt;    \edef~{\string~}%
%&lt;latexrelease&gt;    \expandafter\def\csname\expandafter\endcsname
%&lt;latexrelease&gt;      \expandafter{\csname\endcsname}%
%&lt;latexrelease&gt;    \@expl@@@filehook@if@no@extension@@nTF{#1}%
%&lt;latexrelease&gt;      {\@tempswatrue}{\@tempswafalse}%
%&lt;latexrelease&gt;    \@kernel@make@file@csname\@curr@file
%&lt;latexrelease&gt;      \@expl@@@filehook@resolve@file@subst@@w {#1}%
%&lt;latexrelease&gt;    \@expl@@@filehook@if@file@replaced@@TF
%&lt;latexrelease&gt;      {\@kernel@make@file@csname\@curr@file@reqd
%&lt;latexrelease&gt;         \@expl@@@filehook@normalize@file@name@@w{#1}%
%&lt;latexrelease&gt;       \if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file@reqd \fi}%
%&lt;latexrelease&gt;      {\if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file \fi
%&lt;latexrelease&gt;       \global\let\@curr@file@reqd\@curr@file}%
%&lt;latexrelease&gt;    \@expl@@@filehook@clear@replacement@flag@@
%&lt;latexrelease&gt;  \endgroup}
%&lt;latexrelease&gt;\let\set@curr@file@nosearch\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\set@curr@file}{Setting current file name}%
%&lt;latexrelease&gt;\def\set@curr@file#1{%
%&lt;latexrelease&gt;  \begingroup
%&lt;latexrelease&gt;    \escapechar\m@ne
%&lt;latexrelease&gt;    \expandafter\def\csname\expandafter\endcsname
%&lt;latexrelease&gt;      \expandafter{\csname\endcsname}%
%&lt;latexrelease&gt;    \@expl@@@filehook@if@no@extension@@nTF{#1}%
%&lt;latexrelease&gt;      {\@tempswatrue}{\@tempswafalse}%
%&lt;latexrelease&gt;    \@kernel@make@file@csname\@curr@file
%&lt;latexrelease&gt;      \@expl@@@filehook@resolve@file@subst@@w {#1}%
%&lt;latexrelease&gt;    \@expl@@@filehook@if@file@replaced@@TF
%&lt;latexrelease&gt;      {\@kernel@make@file@csname\@curr@file@reqd
%&lt;latexrelease&gt;         \@expl@@@filehook@normalize@file@name@@w{#1}%
%&lt;latexrelease&gt;       \if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file@reqd \fi}%
%&lt;latexrelease&gt;      {\if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file \fi
%&lt;latexrelease&gt;       \global\let\@curr@file@reqd\@curr@file}%
%&lt;latexrelease&gt;    \@expl@@@filehook@clear@replacement@flag@@
%&lt;latexrelease&gt;  \endgroup}
%&lt;latexrelease&gt;\let\set@curr@file@nosearch\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{2019/10/01}%
%&lt;latexrelease&gt;          {\set@curr@file}{Setting current file name}%
%&lt;latexrelease&gt;\def\set@curr@file#1{%
%&lt;latexrelease&gt;  \begingroup
%&lt;latexrelease&gt;    \escapechar\m@ne
%&lt;latexrelease&gt;    \xdef\@curr@file{%
%&lt;latexrelease&gt;      \expandafter\expandafter\expandafter\unquote@name
%&lt;latexrelease&gt;      \expandafter\expandafter\expandafter{%
%&lt;latexrelease&gt;      \expandafter\string
%&lt;latexrelease&gt;        \csname\@firstofone#1\@empty\endcsname}}%
%&lt;latexrelease&gt;  \endgroup
%&lt;latexrelease&gt;}
%&lt;latexrelease&gt;\let\set@curr@file@nosearch\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%    \end{macrocode}
%
%    \begin{macrocode}
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;          {\set@curr@file}{Setting current file name}%
%&lt;latexrelease&gt;\let\set@curr@file\@undefined
%&lt;latexrelease&gt;\let\set@curr@file@nosearch\@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
%
% \begin{macro}{\@filehook@set@CurrentFile}
% \begin{macro}{\@kernel@make@file@csname,\@set@curr@file@aux}
%
%    \fmi{This should get internalized using \texttt{@expl@} names}
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\@kernel@make@file@csname}{Make file csname}%
%    \end{macrocode}
%
%    \begin{macrocode}
\def\@kernel@make@file@csname#1#2#3{%
  \xdef#1{\expandafter\@set@curr@file@aux
    \csname\expandafter#2\@firstofone#3\@nil\endcsname}}
%    \end{macrocode}
%   This auxiliary compares \cs{\meta{filename}} with
%   \cs{csname\cs{endcsname}} to check if the empty |.tex| file was
%   requested.
%   \changes{v1.0o}{2023/04/02}
%           {Make \cs{@set@curr@file@aux} \cs{long} gh/942}
%    \begin{macrocode}
\long\def\@set@curr@file@aux#1{%
  \expandafter\ifx\csname\endcsname#1%
    .tex\else\string#1\fi}
%    \end{macrocode}
%
%   \begin{sloppypar}
%   Then we call \cs{@expl@@@filehook@set@curr@file@@nNN} once for
%   \cs{@curr@file} to set \cs[no-index]{CurrentFile(Path)Used} and once for
%   \cs{@curr@file@reqd} to set \cs[no-index]{CurrentFile(Path)}.
%   Here too the slower route is only used if a substitution happened,
%   but here \cs{@expl@@@filehook@if@file@replaced@@TF} can't be used because
%   the flag is reset at the \cs{endgroup} above, so we check if
%   \cs{@curr@file} and \cs{@curr@file@reqd} differ.  This macro is
%   issued separate from \cs{set@curr@file} because it changes
%   \cs{CurrentFile}, and side-effects would quickly get out of control.
%   \end{sloppypar}
%    \begin{macrocode}
\def\@filehook@set@CurrentFile{%
  \@expl@@@filehook@set@curr@file@@nNN{\@curr@file}%
    \CurrentFileUsed\CurrentFilePathUsed
  \ifx\@curr@file@reqd\@curr@file
    \let\CurrentFile\CurrentFileUsed
    \let\CurrentFilePath\CurrentFilePathUsed
  \else
    \@expl@@@filehook@set@curr@file@@nNN{\@curr@file@reqd}%
      \CurrentFile\CurrentFilePath
  \fi}
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
% \begin{macro}{\@@_set_curr_file:nNN,
%               \@@_set_curr_file_assign:nnnNN}
%   When inputting a file, \cs{set@curr@file} does a file lookup
%   (in \cs{input@path} and \cs{l_file_search_path_seq}) and returns the
%   actual file name (\meta{base} plus \meta{ext}) in
%   \cs{CurrentFileUsed}, and in case there's a file substitution, the
%   requested file in \cs{CurrentFile} (otherwise both are the same).
%   Only the base and extension are returned,
%   regardless of the input (both \texttt{path/to/file.tex} and
%   \texttt{file.tex} end up as \texttt{file.tex} in \cs{CurrentFile}).
%   The path is returned in \cs{CurrentFilePath}, in case it's needed.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {@@_set_curr_file:nNN}{Set curr file}%
\ExplSyntaxOn
%&lt;@@=filehook&gt;
\cs_new_protected:Npn \@@_set_curr_file:nNN #1
  {
    \exp_args:Nf \@@_file_parse_full_name:nN {#1}
      \@@_set_curr_file_assign:nnnNN
  }
\cs_new_protected:Npn \@@_set_curr_file_assign:nnnNN #1 #2 #3 #4 #5
  {
    \str_set:Nn #5 {#1}
    \str_set:Nn #4 {#2#3}
  }
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \subsection{Replacing a file and detecting loops}
%
% \begin{macro}{\@@_resolve_file_subst:w}
% \begin{macro}{\@@_normalize_file_name:w}
% \begin{macro}{\@@_file_name_compose:nnn}
%   Start by sanitizing the file with \cs{@@_file_parse_full_name:nN}
%   then do \cs{@@_file_subst_begin:nnn}\Arg{path}\Arg{name}\Arg{ext}.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;     {\@@_resolve_file_subst:w}{Replace files detect loops}%
\ExplSyntaxOn
\cs_new:Npn \@@_resolve_file_subst:w #1 \@nil
  { \@@_file_parse_full_name:nN {#1} \@@_file_subst_begin:nnn }
\cs_new:Npn \@@_normalize_file_name:w #1 \@nil
  { \@@_file_parse_full_name:nN {#1} \@@_file_name_compose:nnn }
\cs_new:Npn \@@_file_name_compose:nnn #1 #2 #3
  { \tl_if_empty:nF {#1} { #1 / } #2#3 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{@@_file_replaced}
% \begin{macro}{\@@_if_file_replaced:TF}
% \begin{macro}{\@@_clear_replacement_flag:}
%   Since the file replacement is done expandably in a \cs{csname}, use
%   a flag to remember if a substitution happened.  We use this in
%   \cs{set@curr@file} to short-circuit some of it in case no
%   substitution happened (by far the most common case, so it's worth
%   optimizing).  The flag raised during the file substitution algorithm
%   must be explicitly cleared after the \cs{@@_if_file_replaced:TF}
%   conditional is no longer needed, otherwise further uses of
%   \cs{@@_if_file_replaced:TF} will wrongly return true.
%    \begin{macrocode}
\flag_new:n { @@_file_replaced }
\cs_new:Npn \@@_if_file_replaced:TF #1 #2
  { \flag_if_raised:nTF { @@_file_replaced } {#1} {#2} }
\cs_new_protected:Npn \@@_clear_replacement_flag:
  { \flag_clear:n { @@_file_replaced } }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_file_subst_begin:nnn}
%   First off, start by checking if the current file ($\meta{name} +
%   \meta{ext}$) has a declared substitution.  If not, then just put
%   that as the name (including a possible \meta{path} in this case):
%   this is the default case with no substitutions, so it's the first to
%   be checked.  The auxiliary \cs{@@_file_subst_tortoise_hare:nn} sees
%   that there's no replacement for |#2#3| and does nothing else.
%    \begin{macrocode}
\cs_new:Npn \@@_file_subst_begin:nnn #1 #2 #3
  {
    \@@_file_subst_tortoise_hare:nn { #2#3 } { #2#3 }
      { \@@_file_name_compose:nnn {#1} {#2} {#3} }
  }
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \subsubsection{The Tortoise and Hare algorithm}
%
% \begin{macro}{\@@_file_subst_tortoise_hare:nn}
% \begin{macro}{\@@_file_subst_loop:NN,\@@_file_subst_loop:cc}
%   If there is a substitution (\meta{true} in the first
%   \cs{cs_if_exist:cTF} below), then first check if there is no
%   substitution down the line:  this should be the second most common
%   case, of one file replaced by another.  In that case just leave the
%   substitution there and the job is done.  If any substitution
%   happens, then the \cs{flag @@_file_replaced} is raised
%   (conditionally, because checking if a flag is raised is much faster
%   than raising it over and over again).
%
%   If, however there are more substitutions, then we need to check for
%   a possible loop in the substitutions, which would otherwise put
%   \TeX{} in an infinite loop if just an exhaustive expansion was used.
%
%   To detect a loop, the \emph{Tortoise and Hare} algorithm is used.
%   The name of the algorithm is an analogy to Aesop's fable, in which
%   the Hare outruns a Tortoise.  The two pointers here are the csnames
%   which contains each file replacement, both of which start at the
%   position zero, which is the file requested.  In the inner part of
%   the macro below, \cs{@@_file_subst_loop:cc} is called with
%   \cs[no-index]{@file-subst@\meta{file}} and
%   \cs[no-index]{@file-subst@\cs[no-index]{@file-subst@\meta{file}}};
%   that is, the substitution of \meta{file} and the substitution of that
%   substitution: the Tortoise walks one step while the Hare walks two.
%
%   Within \cs{@@_file_subst_loop:NN} the two substitutions are
%   compared, and if they lead to the same file it means that there is
%   a loop in the substitutions.  If there's no loop,
%   \cs{@@_file_subst_tortoise_hare:nn} is called again with the
%   Tortoise at position~1 and the hare at~2.  Again, the substitutions
%   are checked ahead of the Hare pointer to check that it won't run too
%   far;  in case there is no loop in the declarations, eventually one
%   of the \cs{cs_if_exist:cTF} below will go \meta{false} and the
%   algorithm will end;  otherwise it will run until the Hare reaches
%   the same spot as the tortoise and a loop is detected.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;  {\@@_file_subst_tortoise_hare:nn}{Tortoise and Hare}%
\ExplSyntaxOn
\cs_new:Npn \@@_file_subst_tortoise_hare:nn #1 #2 #3
  {
    \cs_if_exist:cTF { @file-subst@ #2 }
      {
        \flag_if_raised:nF { @@_file_replaced }
          { \flag_raise:n { @@_file_replaced } }
        \cs_if_exist:cTF { @file-subst@ \use:c { @file-subst@ #2 } }
          {
            \@@_file_subst_loop:cc
              { @file-subst@ #1 }
              { @file-subst@ \use:c { @file-subst@ #2 } }
          }
          { \use:c { @file-subst@ #2 } }
      }
      { #3 }
  }
%    \end{macrocode}
%   This is just an auxiliary to check if a loop was found, and continue
%   the algorithm otherwise.  If a loop is found, the |.tex| file is
%   used as fallback and \cs{@@_file_subst_cycle_error:cN} is called to
%   report the error.
%    \begin{macrocode}
\cs_new:Npn \@@_file_subst_loop:NN #1 #2
  {
    \token_if_eq_meaning:NNTF #1 #2
      {
        .tex
        \@@_file_subst_cycle_error:cN { @file-subst@ #1 } #1
      }
      { \@@_file_subst_tortoise_hare:nn {#1} {#2} {#2} }
  }
\cs_generate_variant:Nn \@@_file_subst_loop:NN { cc }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{
%     \@@_file_subst_cycle_error:NN,
%     \@@_file_subst_cycle_error:cN,
%   }
%   \changes{v1.0l}{2021/08/27}{Use \cs{msg_...} not \cs{__kernel_msg_...}}
%   Showing this type of error expandably is tricky, as we have a very
%   limited amount of characters to show and a potentially large list.
%   As a work around, several errors are printed, each showing one step
%   of the loop, until all the error messages combined show the loop.
%    \begin{macrocode}
\cs_new:Npn \@@_file_subst_cycle_error:NN #1 #2
  {
    \msg_expandable_error:nnff { latex2e } { file-cycle }
      {#1} { \use:c { @file-subst@ #1 } }
    \token_if_eq_meaning:NNF #1 #2
      { \@@_file_subst_cycle_error:cN { @file-subst@ #1 } #2 }
  }
\cs_generate_variant:Nn \@@_file_subst_cycle_error:NN { c }
%    \end{macrocode}
%
%   And the error message:
%    \begin{macrocode}
\msg_new:nnn { latex2e } { file-cycle }
  { File~loop!~#1~replaced~by~#2... }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
\ExplSyntaxOff
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
%
%
%    \begin{macrocode}
%&lt;@@=&gt;
%    \end{macrocode}
%
%
% \subsection{Preventing a package from loading}
%
%    We support the use case of preventing a package from loading but not
%    any other type of files (e.g., classes).
%
% \begin{macro}{\disable@package@load}
% \begin{macro}{\reenable@package@load}
% \begin{macro}{\@disable@packageload@do}
%   \cs{disable@package@load} defines
%   \cs[no-index]{@pkg-disable@\meta{package}} to expand to some code |#2|
%   instead of loading the package.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;          {\disable@package@load}{Disable packages}%
\def\disable@package@load#1#2{%
  \global\@namedef{@pkg-disable@#1.\@pkgextension}{#2}}
%    \end{macrocode}
%
% \changes{v1.0n}{2022/08/18}{Inhibit checking the loaded version when
%   package is load-disabled, and write to the .log (gh/888)}
%   Here we check if a control sequence named
%   \cs[no-index]{@pkg-disable@\meta{name}.sty} is defined, and if so
%   don't use the package loading code |#2|, but use the replacement
%   code stored in that control sequence, write something to the log,
%   and then prevent \cs{@onefilewithoptions} from sanity-checking the
%   requested package date (the \tn{expandafter} here triggers one in
%   \cs{@onefilewithoptions} that ends a conditional there, and the
%   \tn{@gobbletwo} removes the date checking code from the input
%   stream).
%    \begin{macrocode}
\def\@disable@packageload@do#1#2{%
  \@ifundefined{@pkg-disable@#1}%
     {#2}%
     {\@nameuse{@pkg-disable@#1}%
      \@latex@info{Package '#1' has been disabled.%
       \MessageBreak Load request ignored}%
      \expandafter\@gobbletwo}}
%    \end{macrocode}
%
%   \cs{reenable@package@load} undefines
%   \cs[no-index]{@pkg-disable@\meta{package}} to reallow loading a package.
%    \begin{macrocode}
\def\reenable@package@load#1{%
  \global\expandafter\let
  \csname @pkg-disable@#1.\@pkgextension \endcsname \@undefined}
%    \end{macrocode}
%
%
%    \begin{macrocode}
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;          {\disable@package@load}{Disable packages}%
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\let\disable@package@load   \@undefined
%&lt;latexrelease&gt;\let\@disable@packageload@do\@undefined
%&lt;latexrelease&gt;\let\reenable@package@load  \@undefined
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
%
%
% \subsection{High-level interfaces for \LaTeX{}}
%
%    None so far and the general feeling for now is that the hooks are
%    enough. Packages like \pkg{filehook}, etc., may use them to set
%    up their interfaces (samples are given below) but for the now the
%    kernel will not provide any.
%
%
%
% \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 (and
%    in parts that still use \LaTeXe{} syntax. We have to unset the
%    \texttt{@\/@} since we want double ``at'' sign in place of double
%    underscores.
%
%    \begin{macrocode}
%&lt;@@=&gt;
%    \end{macrocode}
%  \InternalDetectionOff
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%&lt;*2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{2020/10/01}%
%&lt;latexrelease&gt;    {\@expl@@@filehook@if@no@extension@@nTF}{2e tmp interfaces}%
\ExplSyntaxOn
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@if@no@extension@@nTF
              \__filehook_if_no_extension:nTF
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@set@curr@file@@nNN
              \__filehook_set_curr_file:nNN
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@resolve@file@subst@@w
              \__filehook_resolve_file_subst:w
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@normalize@file@name@@w
              \__filehook_normalize_file_name:w
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@if@file@replaced@@TF
              \__filehook_if_file_replaced:TF
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@clear@replacement@flag@@
              \__filehook_clear_replacement_flag:
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@drop@extension@@N
              \__filehook_drop_extension:N
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@file@push@@
              \__filehook_file_push:
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@file@pop@@
              \__filehook_file_pop:
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@file@pop@assign@@nnnn
              \__filehook_file_pop_assign:nnnn
%    \end{macrocode}
%  \InternalDetectionOn
%
%
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
% This one specifically has to be undefined because it is left over in
% the input stream from \cs{InputIfFileExists} and executed when
% \pkg{latexrelease} is loaded.  It cannot be \cs{let} to \cs{@undefined}
% otherwise it would error as well, so it is \cs{let} to \cs{relax} to
% be silently ignored when loading \cs{latexrelease}.
% \changes{v1.0e}{2021/01/07}{Added rollback for this case to avoid
%   spurious errors (part of gh/463)}
%    \begin{macrocode}
%&lt;/2ekernel|latexrelease&gt;
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;latexrelease&gt;
%&lt;latexrelease&gt;\IncludeInRelease{0000/00/00}%
%&lt;latexrelease&gt;    {\@expl@@@filehook@if@no@extension@@nTF}{2e tmp interfaces}%
%&lt;latexrelease&gt;\let\@expl@@@filehook@file@pop@@\relax
%&lt;latexrelease&gt;\EndIncludeInRelease
%&lt;*2ekernel&gt;
%    \end{macrocode}
%
%    This ends the kernel code in this file.
%    \begin{macrocode}
%&lt;/2ekernel&gt;
%    \end{macrocode}
%
%
%
% \section{A sample package for structuring the log output}
%
%    \begin{macrocode}
%&lt;*structuredlog&gt;
%&lt;@@=filehook&gt;
%    \end{macrocode}
%
%    \begin{macrocode}
\ProvidesExplPackage
    {structuredlog}{\ltfilehookdate}{\ltfilehookversion}
    {Structuring the TeX transcript file}
%    \end{macrocode}
%
% \begin{macro}{\g_@@_nesting_level_int}
%   Stores the current package nesting level.
%    \begin{macrocode}
\int_new:N \g_@@_nesting_level_int
%    \end{macrocode}
%   Initialise the counter with the number of files in the
%   \cs{@currnamestack} (the number of items divided by $3$) minus one,
%   because this package is skipped when printing to the log.
%    \begin{macrocode}
\int_gset:Nn \g_@@_nesting_level_int
  { ( \tl_count:N \@currnamestack ) / 3 - 1 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_log_file_record:n}
%   This macro is responsible for increasing and decreasing the file
%   nesting level, as well as printing to the log.  The argument is
%   either |STOPTART| or |STOP| and the action it takes on the nesting
%   integer depends on that.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_log_file_record:n #1
  {
    \str_if_eq:nnT {#1} {START} { \int_gincr:N \g_@@_nesting_level_int }
    \iow_term:x
      {
        \prg_replicate:nn { \g_@@_nesting_level_int } { = } ~
        ( LEVEL ~ \int_use:N \g_@@_nesting_level_int \c_space_tl #1 ) ~
        \CurrentFileUsed
%    \end{macrocode}
%   If there was a file replacement, show that as well:
%    \begin{macrocode}
        \str_if_eq:NNF \CurrentFileUsed \CurrentFile
          { ~ ( \CurrentFile \c_space_tl requested ) }
        \iow_newline:
      }
    \str_if_eq:nnT {#1} {STOP} { \int_gdecr:N \g_@@_nesting_level_int }
  }
%    \end{macrocode}
%
%   Now just hook the macro above in the generic |file/before|\ldots
%    \begin{macrocode}
\AddToHook{file/before}{ \@@_log_file_record:n { START } }
%    \end{macrocode}
%   \ldots and |file/after| hooks.
%   We don't want to install the \hook{file/after} hook immediately,
%   because that would mean it is the first time executed when the
%   package finishes. We therefore put the declaration inside
%   \cs{AddToHookNext} so that it gets only installed when we have
%   left this package.
%    \begin{macrocode}
\AddToHookNext{file/after}
  { \AddToHook{file/after}{ \@@_log_file_record:n { STOP } } }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%&lt;@@=&gt;
%&lt;/structuredlog&gt;
%    \end{macrocode}
%
%
%
%
%
%
% \section{Package emulations}
%
%
% \subsection{Package \pkg{atveryend} emulation}
%
%    With the new hook management and the hooks in \cs{enddocument}
%    all of \pkg{atveryend} is taken care of.
%    We can make an emulation only here after the substitution
%    functionality is available:
%    \begin{macrocode}
%&lt;*2ekernel&gt;
\declare@file@substitution{atveryend.sty}{atveryend-ltx.sty}
%&lt;/2ekernel&gt;
%    \end{macrocode}
%
%    Here is the package file we point to:
%    \begin{macrocode}
%&lt;*atveryend-ltx&gt;
\ProvidesPackage{atveryend-ltx}
   [2020/08/19 v1.0a
     Emulation of the original atveryend package^^Jwith kernel methods]
%    \end{macrocode}
%
%
%    Here are new definitions for its interfaces now pointing to the
%    hooks in \cs{enddocument}
%    \begin{macrocode}
\newcommand\AfterLastShipout  {\AddToHook{enddocument/afterlastpage}}
\newcommand\AtVeryEndDocument {\AddToHook{enddocument/afteraux}}
%    \end{macrocode}
%    Next one is a bit of a fake, but the result should normally be as
%    expected. If not, one needs to add a rule to sort the code chunks
%    in \hook{enddocument/info}.
%    \begin{macrocode}
\newcommand\AtEndAfterFileList{\AddToHook{enddocument/info}}
%    \end{macrocode}
%
%    \begin{macrocode}
\newcommand\AtVeryVeryEnd     {\AddToHook{enddocument/end}}
%    \end{macrocode}
%
%  \begin{macro}{\BeforeClearDocument}
%    This one is the only one we don't implement or rather don't have
%    a dedicated hook in the code.
%    \begin{macrocode}
\ExplSyntaxOn
\newcommand\BeforeClearDocument[1]
  { \AtEndDocument{#1}
    \atveryend@DEPRECATED{BeforeClearDocument \tl_to_str:n{#1}}
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_new:Npn\atveryend@DEPRECATED #1
   {\iow_term:x{======~DEPRECATED~USAGE~#1~==========}}
\ExplSyntaxOff
%    \end{macrocode}
%  \end{macro}
%
%
%    \begin{macrocode}
%&lt;/atveryend-ltx&gt;
%    \end{macrocode}
%
%
%
%    \Finale
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\endinput
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
</pre></body></html>