summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex-dev/base
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex-dev/base')
-rw-r--r--Master/texmf-dist/source/latex-dev/base/format.ins9
-rw-r--r--Master/texmf-dist/source/latex-dev/base/latexrelease.dtx329
-rw-r--r--Master/texmf-dist/source/latex-dev/base/latexrelease.ins7
-rw-r--r--Master/texmf-dist/source/latex-dev/base/letter.dtx4
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltbibl.dtx2
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltboxes.dtx4
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltclass.dtx91
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltcmd.dtx68
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx1093
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltdefns.dtx5
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltexpl.dtx37
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx24
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfiles.dtx145
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfinal.dtx73
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfloat.dtx24
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx15
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx28
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfssini.dtx58
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx16
-rw-r--r--Master/texmf-dist/source/latex-dev/base/lthooks.dtx1186
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltluatex.dtx6
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltmath.dtx8
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx2
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx15
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltpar.dtx16
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltpara.dtx381
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltpictur.dtx5
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltplain.dtx210
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltshipout.dtx8
-rw-r--r--Master/texmf-dist/source/latex-dev/base/lttab.dtx10
-rw-r--r--Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx69
-rw-r--r--Master/texmf-dist/source/latex-dev/base/ltvers.dtx121
32 files changed, 3315 insertions, 754 deletions
diff --git a/Master/texmf-dist/source/latex-dev/base/format.ins b/Master/texmf-dist/source/latex-dev/base/format.ins
index 9c90555b9f3..eece2f57ef6 100644
--- a/Master/texmf-dist/source/latex-dev/base/format.ins
+++ b/Master/texmf-dist/source/latex-dev/base/format.ins
@@ -166,12 +166,14 @@ the system are in the document `cfgguide.tex'.
% we have the extensions as soon as possible.
\from{ltexpl.dtx}{2ekernel} % expl3-based extensions
\from{ltdefns.dtx}{2ekernel}
- \from{ltcmd.dtx}{2ekernel} % Used to be xparse
+ \from{ltcmd.dtx}{2ekernel} % L3 layer module (used to be xparse)
\from{lthooks.dtx}{2ekernel} % L3 layer module
+ \from{ltcmdhooks.dtx}{2ekernel} % L3 layer module
\from{ltalloc.dtx}{2ekernel}
\from{ltcntrl.dtx}{2ekernel}
\from{lterror.dtx}{2ekernel}
\from{ltpar.dtx}{2ekernel}
+ \from{ltpara.dtx}{2ekernel} % L3 layer module
\from{ltspace.dtx}{2ekernel}
\from{ltlogos.dtx}{2ekernel}
\from{ltfiles.dtx}{2ekernel}
@@ -240,11 +242,6 @@ the system are in the document `cfgguide.tex'.
\generate{\file{structuredlog.sty}{\from{ltfilehook.dtx}{structuredlog}}}
-% hook managed para
-
-\generate{\file{ltpara.ltx}{\from{ltpara.dtx}{2ekernel}}}
-
-
% luatex support (TeX part)
\generate{\file{ltluatex.tex}{\from{ltluatex.dtx}{tex,plain}}}
diff --git a/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx b/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx
index cfcac4b3971..7d2290f4d8a 100644
--- a/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/latexrelease.dtx
@@ -37,13 +37,13 @@
%<fixltx2e>\ProvidesPackage{fixltx2e}
%<fixltx2e> [2016/12/29 v2.1a fixes to LaTeX (obsolete)]
%<latexrelease>\ProvidesPackage{latexrelease}
-%<*!fixltx2e>
+%<*latexrelease>
% \fi
% \ProvidesFile{latexrelease.dtx}
- [2021/01/23 v1.0l LaTeX release emulation and tests
+ [2021/04/29 v1.0n LaTeX release emulation and tests
(including releases up to \latexreleaseversion)]
% \iffalse
-%</!fixltx2e>
+%</latexrelease>
%<*driver>
\documentclass{ltxdoc}
\newcommand\Lopt[1]{\textsf{#1}}
@@ -51,6 +51,8 @@
\providecommand{\file}[1]{\texttt{#1}}
\providecommand{\MF}{\textsf{Metafont}}
\providecommand{\danger}{\marginpar[\hfill\protect\Huge!!]{\protect\Huge!!\hfill}}
+ \DeclareRobustCommand\cs[2][]{\texttt{\char`\\\detokenize{#2}}}
+ \DeclareRobustCommand\pkg[1]{\textsf{#1}}
\begin{document}
\DocInput{latexrelease.dtx}
\end{document}
@@ -69,7 +71,7 @@
% \title{The \Lpack{latexrelease} package\thanks{This file
% has version number \fileversion, last
% revised \filedate.}}
-% \author{The \LaTeX Project}
+% \author{The \LaTeX{} Project}
% \date{\filedate}
% \MaintainedByLaTeXTeam{latex}
% \maketitle
@@ -364,6 +366,99 @@
% necessary as the new code only gets defined if that release is
% chosen.
%
+% \section{Declaring entire modules}
+%
+% Sometimes a large chunk of code is added as a module to another larger
+% code base. As example of that in the 2020-10-01 release \LaTeX{} got
+% a new hook management system, \pkg{lthooks}, which was added in one go
+% and, as with all changes to the kernel, it was added to
+% \pkg{latexrelease}. However rolling back from a future date to the
+% 2020-10-01 release didn't work because \pkg{latexrelease} would try to
+% define again all those commands, which would result in many
+% ``already defined'' errors and similar issues.
+%
+% To solve that problem, completely new modules can be defined in
+% \pkg{latexrelease} using the commands:
+% \begin{quote}
+% \cs{NewModuleRelease}%^^A
+% \verb|{|\meta{initial release date}%^^A
+% \verb|}{|\meta{name}\verb|}{|\meta{message}\verb|}|\\
+% \null\quad\meta{module code}\\
+% \cs{IncludeInRelease}%^^A
+% \verb|{0000/00/00}{|\meta{name}\verb|}{|\meta{message}\verb|}|\\
+% \null\quad\meta{undefine module code}\\
+% \cs{EndModuleRelease}
+% \end{quote}
+% With that setup, the module \meta{name} will be declared to exist only
+% in releases equal or later \meta{initial release date}.
+%
+% If \pkg{latexrelease} is rolling backwards or forwards between dates
+% after \meta{initial release date}, then all the \meta{module code} is
+% skipped, except when inside \meta{IncludeInRelease} guards, in which
+% case the code is applied or skipped as discussed above.
+%
+% If rolling forward from a date before the module's \meta{initial
+% release date} to a date after that, then all the \meta{module code} is
+% executed to define the module, and \cs{IncludeInRelease} guards are
+% executed accordingly, depending on the date declared and the target
+% date.
+%
+% If \pkg{latexrelease} is rolling back to a date before \meta{release
+% date}, then the code in the \cs{IncludeInRelease} guard dated
+% \verb|0000/00/00| is executed instead to undefine the module. This
+% guard \emph{is not} ended by the usual \cs{EndIncludeInRelease}, but
+% instead by \cs{EndModuleRelease}.
+%
+% Finally, if rolling backwards or forwards between dates both before
+% \meta{initial release date}, the entire code between
+% \meta{NewModuleRelease} and \meta{EndModuleRelease} is entirely
+% skipped.
+%
+% \subsection{Example}
+%
+% Here is an example usage of the structure described above, as it would
+% be used in the \LaTeX{} kernel, taking \pkg{lthooks} as example:
+% \begin{verbatim}
+% %<*2ekernel|latexrelease>
+% \ExplSyntaxOn
+% %<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}%
+% %<latexrelease> {The~hook~management~system}
+% \NewDocumentCommand \NewHook { m }
+% { \hook_new:n {#1} }
+% %<latexrelease>\IncludeInRelease{2021/06/01}{\AddToHook}{Long~argument}
+% \NewDocumentCommand \AddToHook { m o +m }
+% { \hook_gput_code:nnn {#1} {#2} {#3} }
+% %<latexrelease>\EndIncludeInRelease
+% %<latexrelease>
+% %<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHook}{Long~argument}
+% %<latexrelease>\NewDocumentCommand \AddToHook { m o m }
+% %<latexrelease> { \hook_gput_code:nnn {#1} {#2} {#3} }
+% %<latexrelease>\EndIncludeInRelease
+% %<latexrelease>
+% %<latexrelease>\IncludeInRelease{0000/00/00}{lthooks}{Undefine~lthooks}
+% %<latexrelease>\cs_undefine:N \NewHook
+% %<latexrelease>\cs_undefine:N \AddToHook
+% %<latexrelease>\EndModuleRelease
+% \ExplSyntaxOff
+% %</2ekernel|latexrelease>
+% \end{verbatim}
+%
+% In the example above, \cs{NewHook} is declared only once, and
+% unchanged in the next release (2021/06/01 in the example), so it has
+% no \cs{IncludeInRelease} guards, and will only be defined if needed.
+% \cs{AddToHook}, on the other hand, changed between the two releases
+% (made up for the example; it didn't really happen)
+% and has an \cs{IncludeInRelease} block for the current release (off
+% \pkg{docstrip} guards, so it goes into the kernel too), and another
+% for the previous release (in \pkg{docstrip} guards so it goes only
+% into \pkg{latexrelease}).
+%
+% Note that in the example above, \cs{ExplSyntaxOn} and
+% \cs{ExplSyntaxOff} were added \emph{outside} the module code because,
+% as discussed above, sometimes the code outside \cs{IncludeInRelease}
+% guards may be skipped, but not the code inside them, and in that case
+% the catcodes would be wrong when defining the code.
+%
% \section{fixltx2e}
%
% As noted above, prior to the 2015 \LaTeX\ release updates to the
@@ -387,27 +482,34 @@
\NeedsTeXFormat{LaTeX2e}[1996/06/01]
% \end{macrocode}
%
-% \section{Setup}
+% \subsection{Setup}
%
% \begin{macro}{\sourceLaTeXdate}
% \changes{v1.0l}{2021/01/23}{\cs{sourceLaTeXdate} added}
+% \changes{v1.0m}{2021/03/18}{Define \cs{sourceLaTeXdate} conditionally}
% Store the original \LaTeX{} format version
-% as a number in the format |YYYYMMDD|:
+% as a number in the format |YYYYMMDD|. This macro has to
+% be defined conditionally, so that it isn't changed in case
+% \file{latexrelease.sty} is loaded twice (for tests), but it
+% can't be defined in the kernel only, otherwise
+% \file{latexrelease.sty} wouldn't work in older \LaTeX{} due
+% to the missing macro.
% \begin{macrocode}
-\edef\sourceLaTeXdate{%
- \expandafter\@parse@version\fmtversion//00\@nil}
+\@ifundefined{sourceLaTeXdate}{%
+ \edef\sourceLaTeXdate{%
+ \expandafter\@parse@version\fmtversion//00\@nil}}{}%
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\IncludeInRelease}
% \begin{macro}{\EndIncludeInRelease}
-% \begin{macrocode}
-% \end{macrocode}
+% These are defined in \file{ltvers.dtx}.
% \end{macro}
% \end{macro}
%
% \changes{v1.0c}{2015/02/19}{Swap argument order}
-% \changes{v1.0k}{2018/05/08}{reset \cs{\requestedLaTeXdate} for current and latest options, github issue 43}
+% \changes{v1.0k}{2018/05/08}{reset \cs{\requestedLaTeXdate} for current
+% and latest options, github issue 43}
% \begin{macrocode}
\DeclareOption*{%
\def\@IncludeInRelease#1[#2]{\@IncludeInRele@se{#1}}%
@@ -494,16 +596,213 @@ of this package available from CTAN}
% \end{macrocode}
%
%
-% \section{Individual Changes}
+% \subsection{Ignoring \texttt{\string_new} errors when rolling back}
%
-% The code for each change will be inserted at this point, extracted
-% from the kernel source files.
+% Enforce \cs{ExplSyntaxOn} and \cs{ExplSyntaxOff} to be \cs{relax} in
+% \pkg{latexrelease} if they are not yet defined. They are later
+% restored to be undefined if needed.
+% \begin{macrocode}
+\csname ExplSyntaxOn\endcsname
+\csname ExplSyntaxOff\endcsname
+% \end{macrocode}
+%
+% \changes{v1.0m}{2021/03/18}
+% {Make \texttt{\string_new} definitions harmless}
+%
+% Define a set of changes here, but we'll only use them later to make
+% sure they are applied after \pkg{expl3} is loaded. If loading from a
+% rather old format, we don't have \cs{ExplSyntaxOn} yet.
+% \begin{macrocode}
+\begingroup
+ \endlinechar=-1
+ \catcode95=11 % _
+ \catcode58=11 % :
+ \catcode126=10 % ~
+ \catcode32=09 % <space>
+ \xdef\latexrelease@postexpl{\unexpanded{%
+%<@@=latexrelease>
+% \end{macrocode}
+%
+% First we'll define a \cs{declarecommand} that does \cs{renewcommand}
+% if the command being defined already exists, and \cs{newcommand}
+% otherwise.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_declare_command:w
+ { \@star@or@long \@@_declare_command:Nw }
+\cs_gset_protected:Npn \@@_declare_command:Nw #1
+ { \cs_if_exist:NTF #1 { \renew@command } { \new@command } #1 }
+% \end{macrocode}
+%
+% Then define a version of \cs{e@alloc} that checks if the control
+% sequence being defined already exists, and if so, checks if its
+% meaning is the same as the one that would be defined with the call to
+% \cs{e@alloc}. If both tests pass, nothing is defined to save a
+% register. This version also takes care of setting
+% \cs{allocationnumber} to the value it would have after the register is
+% allocated.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_e@alloc:NnnnnN #1 #2 #3 #4 #5 #6
+ {
+ \cs_if_free:NTF #6
+ { \use:n }
+ {
+ \exp_after:wN \@@_e@alloc:N
+ \token_to_meaning:N #6 \scan_stop: {#2} #6
+ }
+ { \@@_e@alloc #1 {#2} {#3} {#4} {#5} #6 }
+ }
+% \end{macrocode}
+% Walk through the meaning of the control sequence token by token,
+% looking for the register allocation number.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_e@alloc:N #1
+ {
+ \if_int_compare:w 0 < 0
+ \if_int_compare:w 10 < 9#1 ~ 1 \fi:
+ \if_charcode:w " #1 1 \fi: \exp_stop_f:
+ \tex_afterassignment:D \@@_e@alloc:w
+ \@tempcnta #1
+ \use_i:nnn
+ \fi:
+ \use:n
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_iv:nnnn
+ \fi:
+ \@@_e@alloc:N
+ }
+ }
+% \end{macrocode}
+% When found, check if it is the exact same register as it would be
+% allocated, and if it is, set \cs{allocationnumber} accordingly and
+% exit, otherwise undefine the register and allocate from scratch.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_e@alloc:w #1 \scan_stop: #2 #3
+ {
+ #2 \@@_tmp:w = \@tempcnta
+ \token_if_eq_meaning:NNTF #3 \@@_tmp:w
+ { \int_set_eq:NN \allocationnumber \@tempcnta \use_none:n }
+ { \cs_set_eq:NN #3 \tex_undefined:D \use:n }
+ }
+% \end{macrocode}
+%
+% Now create a token list to hold the list of changed commands, and
+% define a temporary macro that will loop through the command list,
+% store each in \cs{l_@@_restores_tl}, save a copy, and redefine each.
+% \begin{macrocode}
+\tl_clear_new:N \l_@@_restores_tl
+\cs_gset:Npn \@@_tmp:w #1 #2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #1
+ { \cs_undefine:N \@@_tmp:w }
+ \tl_put_right:Nn \l_@@_restores_tl {#1}
+ \cs_set_eq:cN { @@_ \cs_to_str:N #1 } #1
+ \cs_set_eq:NN #1 #2
+ \@@_tmp:w
+ }
+% \end{macrocode}
%
+% The redefinitions below are needed because:
+% \begin{description}
+% \def\makelabel#1{\cs{#1}}
+% \item[__kernel_chk_if_free_cs:N] This function is used ubiquitously
+% in the \pkg{l3kernel} to check if a control sequence is definable,
+% and give an error otherwise (similar to \cs{@ifdefinable}).
+% Making it a no-op is enough for most cases (except when defining
+% new registers);
+% \item[e@alloc] In the case of new registers, we waste an allocation
+% number if we do \cs[no-index]{new\meta{thing}} in a register
+% that's already allocated, so the redefinition of \cs{e@alloc}
+% checks if the new register is really necessary. This code does
+% not clear the register, which might cause problems in the future,
+% if a register is allocated but not properly cleared before using;
+% \item[__kernel_msg_error:nnx] This command is used to error on
+% already defined scan marks. Just making the error do nothing is
+% enough, as no action is taken in that case;
+% \item[msg_new:nnnn] Used to define new messages. Making it
+% \texttt{\string_gset} is enough. Other msg commands like
+% \cs{msg_new:nnn} and \cs[no-index]{__kernel_msg_new:nnn(n)} are
+% defined in terms of \cs{msg_new:nnnn}, so there is no need to
+% change the other ones;
+% \item[NewDocumentCommand] Used to define user-level commands in the
+% kernel. Making it equal to \cs{DeclareDocumentCommand} solves the
+% problem;
+% \item[newcommand] Same as above.
+% \end{description}
+% And here we go:
+% \begin{macrocode}
+\@@_tmp:w
+ \__kernel_chk_if_free_cs:N \use_none:n
+ \e@alloc \@@_e@alloc:NnnnnN
+ \__kernel_msg_error:nnx \use_none:nnn
+ \msg_new:nnnn \msg_gset:nnnn
+ \NewDocumentCommand \DeclareDocumentCommand
+ \newcommand \@@_declare_command:w
+% \end{macrocode}
+% Temp addition \ldots
% \begin{macrocode}
+ \__kernel_msg_error:nnn \use_none:nnn % needed while redirect for kernel msgs doesn't work
+ \q_recursion_tail \q_recursion_tail
+ \q_recursion_stop
+% \end{macrocode}
+%
+% Finally, redirect the error thrown by \cs{NewHook} to nowhere so it
+% can be safely reused (the hook isn't redeclared if it already exists).
+% \begin{macrocode}
+\msg_redirect_name:nnn { hooks } { exists } { none }
+% \end{macrocode}
+%
+% \begin{macrocode}
+ }}%
+\endgroup
%</latexrelease>
% \end{macrocode}
%
-% \section{fixltx2e}
+% \subsection{Undoing the temp modifications}
+%
+% If \cs{ExplSyntaxOn} exists (defined and not equal \cs{relax}), then
+% use the \pkg{expl3} restore code, otherwise restore \cs{ExplSyntaxOn}
+% and \cs{ExplSyntaxOff} to be undefined.
+% \begin{macrocode}
+%<*latexrelease-finish>
+\@ifundefined{ExplSyntaxOn}%
+ {\let\ExplSyntaxOn\@undefined
+ \let\ExplSyntaxOff\@undefined
+ \@gobble}%
+ {\ExplSyntaxOn
+ \@firstofone}%
+ {%
+% \end{macrocode}
+%
+% Now just loop through the list of redefined commands and restore their
+% previous meanings.
+% \begin{macrocode}
+\tl_map_inline:Nn \l_@@_restores_tl
+ {
+ \cs_set_eq:Nc #1 { @@_ \cs_to_str:N #1 }
+ \cs_undefine:c { @@_ \cs_to_str:N #1 }
+ }
+\tl_clear:N \l_@@_restores_tl
+% \end{macrocode}
+%
+% And restore the hook error message.
+% \begin{macrocode}
+\msg_redirect_name:nnn { hooks } { exists } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=>
+ \ExplSyntaxOff}%
+%</latexrelease-finish>
+% \end{macrocode}
+%
+% \subsection{Individual Changes}
+%
+% The code for each change will be inserted at this point, extracted
+% from the kernel source files.
+%
+%
+% \subsection{fixltx2e}
%
% Generate a stub \Lpack{fixltx2e} package:
% \begin{macrocode}
diff --git a/Master/texmf-dist/source/latex-dev/base/latexrelease.ins b/Master/texmf-dist/source/latex-dev/base/latexrelease.ins
index b4d0aef5677..0b65114a2f9 100644
--- a/Master/texmf-dist/source/latex-dev/base/latexrelease.ins
+++ b/Master/texmf-dist/source/latex-dev/base/latexrelease.ins
@@ -94,13 +94,15 @@ extension .ins) which are part of the distribution.
\from{ltexpl.dtx} {latexrelease}% empty
\from{ltdefns.dtx} {latexrelease}%
\from{ltcmd.dtx} {latexrelease}%
- \from{lthooks.dtx} {latexrelease}% empty % L3 layer module
+ \from{lthooks.dtx} {latexrelease}% L3 layer module
+ \from{ltcmdhooks.dtx} {latexrelease}% L3 layer module
\from{ltalloc.dtx} {latexrelease}% empty
\from{ltcntrl.dtx} {latexrelease}% empty
\from{lterror.dtx} {latexrelease}% empty
\from{ltpar.dtx} {latexrelease}% empty
+ \from{ltpara.dtx} {latexrelease}% empty
\from{ltspace.dtx} {latexrelease}%
- \from{ltlogos.dtx} {latexrelease}% empty
+ \from{ltlogos.dtx} {latexrelease}% L3 layer module
\from{ltfiles.dtx} {latexrelease}%
\from{ltoutenc.dtx} {latexrelease}%
\from{ltcounts.dtx} {latexrelease}%
@@ -133,6 +135,7 @@ extension .ins) which are part of the distribution.
\from{ltoutput.dtx} {latexrelease}%
\from{ltfsscmp.dtx} {latexrelease}% only in latexrelease!
\from{ltfinal.dtx} {latexrelease}%
+ \from{latexrelease.dtx}{latexrelease-finish}%
}
diff --git a/Master/texmf-dist/source/latex-dev/base/letter.dtx b/Master/texmf-dist/source/latex-dev/base/letter.dtx
index 3c7fe56ba15..6beee81735a 100644
--- a/Master/texmf-dist/source/latex-dev/base/letter.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/letter.dtx
@@ -45,7 +45,7 @@
%<*driver>
\ProvidesFile{letter.drv}
%</driver>
- [2021/01/07 v1.3c
+ [2021/03/05 v1.3c
%<+letter> Standard LaTeX document class]
% \end{macrocode}
%
@@ -1034,7 +1034,7 @@
% \texttt{extletter.cls},\\
% \texttt{frletter.cls},
% \texttt{hletter.cls},
-% \texttt{scrlttr2.cls},
+% \texttt{scrlttr2.cls} (change rejected),
% \texttt{lettre.cls},
% \texttt{beletter.cls},\\
% \texttt{brief.cls} and perhaps others.
diff --git a/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx b/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx
index 0319342dd49..cda0fa160ad 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltbibl.dtx
@@ -329,7 +329,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\nocite}{Allow nocite in preamble}%
\def\nocite#1{\@bsphack
% \end{macrocode}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx b/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx
index 8281b4b712b..8499d3ab8b9 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltboxes.dtx
@@ -464,7 +464,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\color@begingroup}{color group settings}%
\let\color@begingroup\begingroup
\def\color@endgroup{\endgraf\endgroup}
@@ -1140,7 +1140,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@mpfootnotetext}{footnotetext tagging}%
\long\def\@mpfootnotetext#1{%
\global\setbox\@mpfootins\vbox{%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltclass.dtx b/Master/texmf-dist/source/latex-dev/base/ltclass.dtx
index 26667cbeb1b..06b0e1cf43f 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltclass.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltclass.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltclass.dtx}
- [2021/02/18 v1.3x LaTeX Kernel (Class & Package Interface)]
+ [2021/03/27 v1.4a LaTeX Kernel (Class & Package Interface)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltclass.dtx}
@@ -640,14 +640,38 @@
%<latexrelease> \@expl@push@filename@aux@@}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>
+% \end{macrocode}
+%
+% When we roll back from a release that has \pkg{expl3} preloaded, the
+% definitions of \cs{@pushfilename} and \cs{@popfilename} can't be
+% completely rolled back otherwise \pkg{expl3}-based packages won't
+% have the automatic \cs{ExplSyntaxOff} at the end. Here and below for
+% \cs{@popfilename}, we don't roll back all the way through if coming
+% from \LaTeX${}>2020-02-02$.
+% \changes{v1.4a}{2021/03/27}
+% {Do not completely roll back if \pkg{expl3} is loaded.}
+% \begin{macrocode}
%<latexrelease>\IncludeInRelease{0000/00/00}{\@pushfilename}%
%<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}%
+%<latexrelease>\ifnum\sourceLaTeXdate<20200202\relax
+%<latexrelease> \GenericInfo{}{Defining 00-00-00\string\@pushfilename.}
%<latexrelease>\def\@pushfilename{%
%<latexrelease> \xdef\@currnamestack{%
%<latexrelease> {\@currname}%
%<latexrelease> {\@currext}%
%<latexrelease> {\the\catcode`\@}%
%<latexrelease> \@currnamestack}}
+%<latexrelease>\else
+%<latexrelease> \GenericInfo{}{Defining 2020-02-02\string\@pushfilename.}
+%<latexrelease>\def\@pushfilename{%
+%<latexrelease> \@expl@push@filename@@
+%<latexrelease> \xdef\@currnamestack{%
+%<latexrelease> {\@currname}%
+%<latexrelease> {\@currext}%
+%<latexrelease> {\the\catcode`\@}%
+%<latexrelease> \@currnamestack}%
+%<latexrelease> \@expl@push@filename@aux@@}
+%<latexrelease>\fi
%<latexrelease>\EndIncludeInRelease
\@onlypreamble\@pushfilename
% \end{macrocode}
@@ -676,9 +700,21 @@
%<latexrelease> \@expl@pop@filename@@}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>
+% \end{macrocode}
+%
+% \changes{v1.4a}{2021/03/27}
+% {Do not completely roll back if \pkg{expl3} is loaded.}
+% \begin{macrocode}
%<latexrelease>\IncludeInRelease{0000/00/00}{\@popfilename}%
%<latexrelease> {Add \@expl@push@filename@@ and \@expl@push@filename@aux@@}%
+%<latexrelease>\ifnum\sourceLaTeXdate<20200202\relax
+%<latexrelease> \GenericInfo{}{Defining 00-00-00\string\@popfilename.}
%<latexrelease>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil}
+%<latexrelease>\else
+%<latexrelease> \GenericInfo{}{Defining 2020-02-02\string\@popfilename.}
+%<latexrelease>\def\@popfilename{\expandafter\@p@pfilename\@currnamestack\@nil
+%<latexrelease> \@expl@pop@filename@@}
+%<latexrelease>\fi
%<latexrelease>\EndIncludeInRelease
\@onlypreamble\@popfilename
% \end{macrocode}
@@ -1172,7 +1208,7 @@
% \changes{v1.3x}{2021/02/18}{save raw option lists (gh/85)}
% \begin{macrocode}
%</2ekernel>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@pass@ptions}{Raw option lists}%
%<*2ekernel|latexrelease>
\def\@pass@ptions#1#2#3{%
@@ -1283,7 +1319,7 @@
% unused options. Otherwise, in a package file do nothing.
% \begin{macrocode}
%</2ekernel>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\OptionNotUsed}{filter unused option list}%
%<*2ekernel|latexrelease>
\def\@remove@eq@value#1=#2\@nil{#1}
@@ -1378,19 +1414,50 @@
%
% \changes{v0.2y}{1994/02/07}
% {Add extra ,s so `two' is not matched with `twocolumn'}
+% \changes{v1.3z}{2021/03/05}{modify so braces to not give errors (gh/513)}
% \begin{macrocode}
+%</2ekernel>
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\@xprocess@ptions}{safer @xprocess@ptions}%
+%<*2ekernel|latexrelease>
\def\@xprocess@ptions{%
\ifx\@currext\@clsextension\else
+ \ifx\@classoptionslist\relax\else
\@for\CurrentOption:=\@classoptionslist\do{%
\ifx\CurrentOption\@empty\else
- \@expandtwoargs\in@{,\CurrentOption,}{,\@declaredoptions,}%
- \ifin@
+ \@ifundefined{ds@\CurrentOption}{}{%
\@use@ption
\expandafter\let\csname ds@\CurrentOption\endcsname\@empty
- \fi
+ }%
\fi}%
+ \fi
\fi
\@process@pti@ns}
+%</2ekernel|latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{0000/00/00}%
+%<latexrelease> {\@xprocess@ptions}{safer @xprocess@ptions}%
+%<latexrelease>\let\@remove@eq@value\@undefined
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\def\@xprocess@ptions{%
+%<latexrelease> \ifx\@currext\@clsextension\else
+%<latexrelease> \@for\CurrentOption:=\@classoptionslist\do{%
+%<latexrelease> \ifx\CurrentOption\@empty\else
+%<latexrelease> \@expandtwoargs\in@{,\CurrentOption,}{,\@declaredoptions,}%
+%<latexrelease> \ifin@
+%<latexrelease> \@use@ption
+%<latexrelease> \expandafter\let\csname ds@\CurrentOption\endcsname\@empty
+%<latexrelease> \fi
+%<latexrelease> \fi}%
+%<latexrelease> \fi
+%<latexrelease> \@process@pti@ns}
+%<latexrelease>\EndIncludeInRelease
+%<*2ekernel>
+% \end{macrocode}
+%
+% \begin{macrocode}
\@onlypreamble\@xprocess@ptions
% \end{macrocode}
%
@@ -1472,7 +1539,7 @@
% \changes{v1.3x}{2021/02/18}{filter out =value from unused option list (gh/85)}
% \begin{macrocode}
%</2ekernel>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@use@ption}{filter unused option list}%
%<*2ekernel|latexrelease>
\def\@use@ption{%
@@ -1954,7 +2021,7 @@
% \usepackage{some/local/path/array}
% \usepackage{array}
% \end{verbatim}
-% won't load \file{array.sty} twice. It is remotely possible that
+% won't load |array.sty| twice. It is remotely possible that
% those are two different files, but as a matter of principles, we
% will consider that the base file name uniquely identifies a
% package, regardless of where it lives. This assumption already
@@ -2669,15 +2736,15 @@
% this only works for input files ending in \texttt{.tex}. If a
% different extension is used there is no way to see that we are
% overwriting ourselves!
+% \changes{v1.3y}{2021/03/03}
+% {Fix overwrite check for files with UTF-8 (gh/415)}
% \begin{macrocode}
- \edef\reserved@a{#1}%
- \edef\reserved@a{\detokenize\expandafter{\reserved@a}}%
\edef\reserved@b{\detokenize\expandafter{\jobname}}%
- \ifx\reserved@a\reserved@b%
+ \ifx\@curr@file\reserved@b%
\@fileswtrue%
\else%
\edef\reserved@b{\reserved@b\detokenize{.tex}}%
- \ifx\reserved@a\reserved@b
+ \ifx\@curr@file\reserved@b
\@fileswtrue%
\fi%
\fi%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
index 26ce75224fb..fee555586da 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltcmd.dtx
@@ -34,14 +34,15 @@
%%% From File: ltcmd.dtx
%
% \begin{macrocode}
-\def\ltcmdversion{v1.0a}
-\def\ltcmddate{2021/01/21}
+\def\ltcmdversion{v1.0d}
+\def\ltcmddate{2021-04-19}
% \end{macrocode}
%
%<*driver>
% \fi
\ProvidesFile{ltcmd.dtx}
- [2020-11-20 v1.0 LaTeX Kernel (Document commands)]
+ [\ltcmddate\space \ltcmdversion\space
+ LaTeX Kernel (Document commands)]
% \iffalse
\documentclass{l3doc}
\GetFileInfo{ltcmd.dtx}
@@ -57,7 +58,7 @@
%
% \section{Creating document commands}
%
-% \changes{v1.0}{2020-11-20}{Initial version derived from \texttt{xparse.dtx}}
+% \changes{v1.0a}{2020/11/20}{Initial version derived from \texttt{xparse.dtx}}
%
% Document commands should be created using the tools provided by this module:
% \cs{NewDocumentCommand}, etc.\@, in almost all cases. This allows clean
@@ -78,6 +79,7 @@
%</2ekernel>
% \end{macrocode}
%
+% \changes{v1.0b}{2021/03/18}{Use \cs{NewModuleRelease}.}
% \pkg{ltcmd} code contains an |^^@| character, which usually has
% catcode~15, so \cs{IncludeInRelease} will break when this code is
% being skipped, so we'll save the catcode of |^^@| to restore later:
@@ -85,12 +87,9 @@
%<*2ekernel|latexrelease>
%<latexrelease>\edef\@latexrelease@catcode@null{\the\catcode`\^^@ }
%<latexrelease>\catcode`\^^@=12
-%<latexrelease>\IncludeInRelease{2021/05/01}%
-%<latexrelease> {\NewDocumentCommand}{Document commands}%
-% \end{macrocode}
-%
-% \begin{macrocode}
\ExplSyntaxOn
+%<latexrelease>\NewModuleRelease{2020/10/01}{ltcmd}
+%<latexrelease> {Document~command~parser}%
% \end{macrocode}
%
% \subsection{Variables and constants}
@@ -3196,7 +3195,7 @@
% \begin{macrocode}
\cs_new_protected:Npn \@@_get_arg_spec:NTF #1#2#3
{
- \@@_cmd_if_xparse:NTF #1
+ \__kernel_cmd_if_xparse:NTF #1
{
\tl_set:Nx \ArgumentSpecification { \tl_item:Nn #1 { 2 } }
#2
@@ -3374,15 +3373,22 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\@@_cmd_if_xparse:NTF}
+% \begin{macro}{\__kernel_cmd_if_xparse:NTF}
+% \changes{v1.0d}{2021/04/19}{Renamed \cs{__cmd_cmd_if_xparse:NTF} to
+% \cs{__kernel_cmd_if_xparse:NTF} for cross-module usage}
% \begin{macro}{\@@_cmd_if_xparse_aux:N}
-% To determine whether the command is an \pkg{xparse} command check
-% that its |arg_spec| is empty (this also excludes non-macros) and
-% that its |replacement_spec| starts with either \cs{@@_start:nNNnnn}
-% (non-expandable command) or \cs{@@_start_expandable:nNNNNn}
-% (expandable command) or \cs{@@_start_env:nnnnn} (environment).
+%
+% To determine whether the command is an \pkg{xparse} command check
+% that its |arg_spec| is empty (this also excludes non-macros) and
+% that its |replacement_spec| starts with either
+% \cs{@@_start:nNNnnn} (non-expandable command) or
+% \cs{@@_start_expandable:nNNNNn} (expandable command) or
+% \cs{@@_start_env:nnnnn} (environment).
+%
+% This conditional is needed in several kernel modules and is
+% therefore has a kernel-internal name.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_cmd_if_xparse:NTF #1
+\cs_new_protected:Npn \__kernel_cmd_if_xparse:NTF #1
{
\exp_args:Nf \str_case_e:nnTF
{
@@ -4008,29 +4014,15 @@
% \end{macrocode}
%
% \begin{macrocode}
-\ExplSyntaxOff
-% \end{macrocode}
-%
-% \begin{macrocode}
-%</2ekernel|latexrelease>
-%<latexrelease>\EndIncludeInRelease
-%<latexrelease>\catcode`\^^@=\@latexrelease@catcode@null\relax
-%<latexrelease>
-%<latexrelease>\IncludeInRelease{2020/10/01}%
-%<latexrelease> {\NewDocumentCommand}{Document commands}%
-%<latexrelease>\@ifundefined{NewDocumentCommand}
-%<latexrelease> {%
-%<latexrelease> \IfFileExists{xparse.ltx}
-%<latexrelease> {\input xparse.ltx }
-%<latexrelease> {\@latex@error{LaTeX requires xparse}\@ehd}%
-%<latexrelease> }
-%<latexrelease> {}
-%<latexrelease>\EndIncludeInRelease
%<latexrelease>
-%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\NewDocumentCommand}{Document commands}%
+%<latexrelease>\IncludeInRelease{0000/00/00}{ltcmd}%
+%<latexrelease> {Document~command~parser}%
%<latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\EndModuleRelease
+\ExplSyntaxOff
+%<latexrelease>\@ifundefined{ExplSyntaxOff}{}{\latexrelease@postexpl}
+%<latexrelease>\catcode`\^^@=\@latexrelease@catcode@null\relax
+%</2ekernel|latexrelease>
% \end{macrocode}
%
% We need to stop DocStrip treating |@@| in a special way at this point.
diff --git a/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx b/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx
new file mode 100644
index 00000000000..cc67f5eaa75
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/base/ltcmdhooks.dtx
@@ -0,0 +1,1093 @@
+% \iffalse meta-comment
+%
+%% File: ltcmdhooks.dtx (C) Copyright 2020-2021
+%% Frank Mittelbach, Phelype Oleinik, LaTeX Team
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+%
+%%% From File: ltcmdhooks.dtx
+%
+\def\ltcmdhooksversion{v1.0a}
+\def\ltcmdhooksdate{2021/04/30}
+%
+%
+%
+%<*driver>
+\documentclass{l3doc}
+%\usepackage{ltcmdhooks}
+\makeatletter
+\providecommand\footref[1]{%
+ \begingroup
+ \unrestored@protected@xdef\@thefnmark{\ref{#1}}%
+ \endgroup
+ \@footnotemark}
+\makeatother
+\EnableCrossrefs
+\CodelineIndex
+\begin{document}
+ \DocInput{ltcmdhooks.dtx}
+\end{document}
+%</driver>
+%
+% \fi
+%
+%
+% \providecommand\hook[1]{\texttt{#1}}
+% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}}
+% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}}
+% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}}
+%
+% \title{The \texttt{ltcmdhooks} module\thanks{This file has version
+% \ltcmdhooksversion\ dated \ltcmdhooksdate, \copyright\ \LaTeX\
+% Project.}}
+% \author{Frank Mittelbach \and Phelype Oleinik}
+%
+% \maketitle
+%
+%
+% \tableofcontents
+%
+%
+% \section{Introduction}
+%
+% This file implements generic hooks for (arbitrary) commands.
+% In theory every command \tn[no-index]{\meta{name}} offers now two
+% associated hooks to which code can be added using \tn{AddToHook}
+% or \tn{AddToHookNext}.\footnote{In practice this is not supported
+% for all types of commands, see section~\ref{sec:look-ahead} for
+% the restrictions that apply and what happens if one tries to use
+% this with commands for which this is not supported.} These are
+% \begin{description}
+% \item[\hook{cmd/\meta{name}/before}]
+%
+% This hook is executed at the very start of the command
+% execution after its arguments (if any) are parsed. The hook
+% \meta{code} is wrapped in the command inside a call to
+% \cs{UseHook}|{cmd/|\meta{name}|/before}|, so the arguments
+% passed to the command are \emph{not} available in the hook
+% \meta{code}.
+%
+% \item[\hook{cmd/\meta{name}/after}]
+%
+% This hook is similar to \hook{cmd/\meta{name}/before}, but it is
+% executed at the very end of the command body. This hook is
+% implemented as a reversed hook.
+% \end{description}
+%
+% The hooks are not physically present before
+% \verb=\begin{document}= (i.e., using a command in the preamble
+% will never execute them) and if nobody has declared any code for
+% them, then they are not added to the command code ever. For
+% example, if we have the following definition
+%\begin{verbatim}
+% \newcommand\foo[2]{Code #1 for #2!}
+%\end{verbatim}
+% then executing \verb=\foo{A}{B}= will simply run
+% \verb*=Code A for B!=
+% as it was always the case. However, if somebody, somewhere (e.g.,
+% in a package) adds
+%\begin{verbatim}
+% \AddToHook{cmd/foo/before}{<before code>}
+%\end{verbatim}
+% then, after |\begin{document}| the definition of \cs[no-index]{foo} will be:
+%\begin{verbatim}
+% \renewcommand\foo[2]{\UseHook{cmd/foo/before}Code #1 for #2!}
+%\end{verbatim}
+% and similarly \verb=\AddToHook{cmd/foo/after}{<after code>}=
+% alters the definition to
+%\begin{verbatim}
+% \renewcommand\foo[2]{Code #1 for #2!\UseHook{cmd/foo/after}}
+%\end{verbatim}
+%
+% In other words, the mechanism is similar to what \pkg{etoolbox}
+% offers with \tn{pretocmd} and \tn{apptocmd} with the important
+% differences
+% \begin{itemize}
+% \item
+%
+% that code can be prepended or appended (i.e., added to the
+% hooks) even if the command itself is not defined, because the
+% defining package has not yet been loaded
+%
+% \item
+%
+% and that by using the hook management interface it is now
+% possible to define how the code chunks added in these places
+% are ordered, if different packages want to add code at these
+% points.
+%
+% \end{itemize}
+%
+%
+%
+%
+% \section{Restrictions and Operational details}
+% \label{sec:restrictions}
+%
+% Adding arbitrary material to commands is tricky because most of the
+% time we do not know what the macro expects as arguments when expanding
+% and \TeX{} doesn't have a reliable way to see that, so some guesswork
+% has to be employed.
+%
+% \subsection{Patching}
+%
+% The code here tries to find out if a command was defined with
+% \tn{newcommand} or \tn{DeclareRobustCommand} or
+% \tn{NewDocumentCommand}, and if so it \emph{assumes} that the argument
+% specification of the command is as expected (which is not fail-proof,
+% if someone redefines the internals of these commands in devious ways,
+% but is a reasonable assumption).
+%
+% If the command is one of the defined types, the code here does a
+% sandboxed expansion of the command such that it can be redefined again
+% exactly as before, but with the hook code added.
+%
+% If however the command is not a known type (it was defined with
+% \tn{def}, for example), then the code uses an approach similar to
+% \pkg{etoolbox}'s \tn{patchcmd} to retokenize the command with the hook
+% code in place. This procedure, however, is more likely to fail if the
+% catcode settings are not the same as the ones at the time of command's
+% definition, so not always adding a hook to a command will work.
+%
+% \subsubsection{Timing}
+%
+% When \cs{AddToHook} (or its \pkg{expl3} equivalent) is called with
+% a generic |cmd| hook, say, \hook{cmd/foo/before}, for the first time
+% (that is, no code was added to that same hook before), in the preamble
+% of a document, it will store a patch instruction for that command
+% until |\begin{document}|, and only then all the commands which had
+% hooks added will be patched in one go. That means that no command in
+% the preamble will have hooks patched into them.
+%
+% At |\begin{document}| all the delayed patches will be executed, and
+% if the command doesn't exist the code is still added to the hook,
+% but it will not be executed. After |\begin{document}|, when
+% \cs{AddToHook} is called with a generic |cmd| hook the first time,
+% the command will be immediately patched to include the hook, and if
+% it doesn't exist or if it can't be patched for any reason, an error
+% is thrown; if \cs{AddToHook} was already used in the preamble no new
+% patching is attempted.
+%
+% This has the consequence that a command defined or redefined after
+% |\begin{document}| only uses generic |cmd| hook code if
+% \cs{AddToHook} is called for the first time after the definition is
+% made, or if the command explicitly uses the generic hook in its
+% definition by declaring it with \cs{NewHookPair} adding \cs{UseHook} as
+% part of the code.\footnote{We might change this behavior in the main
+% document slightly after gaining some usage experience.}
+%
+%
+% \subsection{Commands that look ahead}
+% \label{sec:look-ahead}
+%
+% Some commands are defined in different ``steps'' and they look ahead
+% in the input stream to find more arguments. If you try to add some
+% code to the \hook{cmd/\meta{name}/after} hook of such command, it will
+% not work, and it is not possible to detect that programmatically, so
+% the user has to know (or find out) which commands can or cannot have
+% hooks attached to them.
+%
+% One good example is the \tn{section} command. You can add something
+% to the \hook{cmd/section/before} hook, but if you try to add something
+% to the \hook{cmd/section/after} hook, \tn{section} will no longer
+% work. That happens because the \tn{section} macro takes no argument,
+% but instead calls a few internal \LaTeX{} macros to look for the
+% optional and mandatory arguments. By adding code to the
+% \hook{cmd/section/after} hook, you get in the way of that scanning.
+%
+%
+%
+% \section{Package Author Interface}
+%
+% The \hook{cmd} hooks are, by default, available for all commands
+% that can be patched to add the hooks. For some commands, however,
+% the very beginning or the very end of the code is not the best place
+% to put the hooks, for example, if the command looks ahead for
+% arguments (see section~\ref{sec:look-ahead}).
+%
+% If you are a package author and you want to add the hooks to your
+% own commands in the proper position you can define the command and
+% manually add the \cs{UseHook} calls inside the command in the proper
+% positions, and manually define the hooks with \cs{NewHook} or
+% \cs{NewReversedHook}. When the hooks are explicitly defined,
+% patching is not attempted so you can make sure your command works
+% properly. For example, an (admittedly not really useful) command
+% that typesets its contents in a framed box with width optionally
+% given in parentheses:
+% \begin{verbatim}
+% \newcommand\fancybox{\@ifnextchar({\@fancybox}{\@fancybox(5cm)}}
+% \def\@fancybox(#1)#2{\fbox{\parbox{#1}{#2}}}
+% \end{verbatim}
+% If you try that definition, then add some code after it with
+% \begin{verbatim}
+% \AddToHook{cmd/fancybox/after}{<code>}
+% \end{verbatim}
+% and then use the \cs[no-index]{fancybox} command you will see that it
+% will be completely broken, because the hook will get executed in the
+% middle of parsing for optional \texttt{(...)} argument.
+%
+% If, on the other hand, you want to add hooks to your command you can
+% do something like:
+% \begin{verbatim}
+% \newcommand\fancybox{\@ifnextchar({\@fancybox}{\@fancybox(5cm)}}
+% \def\@fancybox(#1)#2{\fbox{%
+% \UseHook{cmd/fancybox/before}%
+% \parbox{#1}{#2}%
+% \UseHook{cmd/fancybox/after}}}
+% \NewHook{cmd/fancybox/before}
+% \NewReversedHook{cmd/fancybox/after}
+% \end{verbatim}
+% then the hooks will be executed where they should and no patching
+% will be attempted. It is important that the hooks are declared with
+% \cs{NewHook} or \cs{NewReversedHook}, otherwise the command hook
+% code will try to patch the command. Note also that the call to
+% |\UseHook{cmd/fancybox/before}| does not need to be in the
+% definition of \cs[no-index]{fancybox}, but anywhere it makes sense
+% to insert it (in this case in the internal
+% \cs[no-index]{@fancybox}).
+%
+% Alternatively, if for whatever reason your command does not support
+% the generic hooks provided here, you can disable a hook with
+% \cs{DisableHook}\footnote{Please use \cs{DisableHook} if at all, only
+% on hooks that you \enquote{own}, i.e., for commands that your
+% package or class defines and not second guess
+% whether or not hooks of other packages should get disabled!}, so
+% that when someone tries to add code to it they will get an error.
+% Or if you don't want the error, you can simply declare the hook with
+% \cs{NewHook} and never use it.
+%
+%
+% The above approach is useful for really complex commands where for
+% one or the other reason the hooks can't be placed at the very
+% beginning and end of the command body and some hand-crafting is
+% needed. However, in the example above the real (and in fact only)
+% issue is the cascading argument parsing in the style developed long
+% ago in \LaTeX~2.09. Thus, a much simpler solution for this case is
+% to replace it with the modern \cs{NewDocumentCommand} syntax and
+% define the command as follows:
+% \begin{verbatim}
+% \DeclareDocumentCommand\fancybox{D(){5cm}m}{\fbox{\parbox{#1}{#2}}}
+% \end{verbatim}
+% If you do that then both hooks automatically work and are patched
+% into the right places.
+%
+% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex }
+%
+%
+%
+% \section{The Implementation}
+%
+% \subsection{Execution plan}
+%
+% To add |before| and |after| hooks to a command we will need to peek
+% into the definition of a command, which is always a tricky thing to
+% do. Some cases are easy because we know how the command was defined,
+% so we can assume how its \meta{parameter text} looks like (for example
+% a command defined with \tn{newcommand} may have an optional argument
+% followed by a run of mandatory arguments), so we can just expand that
+% command and make it grab |#1|, |#2|, etc.\@ as arguments and
+% define it all back with the hooks added.
+%
+% Life's usually not that easy, so with some commands we can't do that
+% (a |#1| might as well be |#|$_{12}$|1|$_{12}$ instead of the expected
+% |#|$_{6}$|1|$_{12}$, for example) so we need to resort to ``patching''
+% the command: read its \tn{meaning}, and tokenize it again with
+% \tn{scantokens} and hope for the best.
+%
+% So the overall plan is:
+% \begin{enumerate}
+% \item
+% Check if a command is of a known type (that is, defined with
+% \tn{newcommand}\footnote{It's not always possible to reliably
+% detect this case because a command defined with no optional
+% argument is indistinguishable from a \tn{def}ed command.},
+% \cs[no-index]{DeclareRobustCommand}, or
+% \cs[no-index]{New(Expandable)DocumentCommand}), and if is, take
+% appropriate action.
+% \item
+% If the command is not a known type, we'll check if the command can
+% be patched. Two things will prevent a command from being
+% patched: if it was defined in a nonstandard catcode setting, or
+% if it is an internal expl3 command with |__|\meta{module} in its
+% name, in which case we refuse to patch.
+% \item
+% If the command was defined in nonstandard catcode settings, we
+% will try a few standard ones to try our best to carry out the
+% pathing. If this doesn't help either, the code will give up and
+% throw an error.
+% \end{enumerate}
+%
+% \begin{macrocode}
+%<@@=hook>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*2ekernel|latexrelease>
+\ExplSyntaxOn
+%<latexrelease>\NewModuleRelease{2021/06/01}{ltcmdhooks}
+%<latexrelease> {The~hook~management~system~for~commands}
+% \end{macrocode}
+%
+% \subsection{Variables}
+%
+% \begin{macro}[int]{\g_hook_patch_action_list_tl}
+% Pairs of |\if<cmd>..\patch<cmd>| to be used with
+% \tn{robust@command@act} when looking for a known patching
+% rule. This token list is exposed because we see some future
+% applications (with very specialized packages, such as
+% \pkg{etoolbox} that may want to extend the pairs processed. It is
+% not meant for general use which is why it is not documented in
+% the interface documentation above.
+% \begin{macrocode}
+\tl_new:N \g_hook_patch_action_list_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\l_@@_patch_num_args_int}
+% The number of arguments in a macro being patched.
+% \begin{macrocode}
+\int_new:N \l_@@_patch_num_args_int
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\l_@@_patch_prefixes_tl}
+% \begin{macro}{\l_@@_patch_param_text_tl}
+% \begin{macro}{\l_@@_patch_replacement_tl}
+% The prefixes and parameters of the definition for the macro being
+% patched.
+% \begin{macrocode}
+\tl_new:N \l_@@_patch_prefixes_tl
+\tl_new:N \l_@@_patch_param_text_tl
+\tl_new:N \l_@@_patch_replacement_tl
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\g_@@_delayed_patches_prop}
+% A list containing the patches delayed to |\begin{document}|, so that
+% patching is not attempted twice.
+% \begin{macrocode}
+\prop_new:N \g_@@_delayed_patches_prop
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_patch_debug:x}
+% A helper for patching debug info.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_debug:x #1
+ { \@@_debug:n { \iow_term:x { [lthooks]~#1 } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Variants}
+%
+% \begin{macro}[int]{\tl_rescan:nV}
+% \pkg{expl3} function variants used throughout the code.
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_rescan:nn { nV }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Patching or delaying}
+%
+% \begin{macro}{\@@_try_put_cmd_hook:n,\@@_try_put_cmd_hook:w}
+% Before |\begin{document}| all patching is delayed.
+% This function is called from within \cs{AddToHook}, when code is
+% added to a generic |cmd| hook is newly declared. It checks whether
+% the patch position is valid, then proceeds to trying to patch or
+% delaying to |\begin{document}| if in the preamble.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_try_put_cmd_hook:n #1
+ { \@@_try_put_cmd_hook:w #1 / / / \s_@@_mark {#1} }
+\cs_new_protected:Npn \@@_try_put_cmd_hook:w
+ #1 / #2 / #3 / #4 \s_@@_mark #5
+ {
+ \@@_debug:n { \iow_term:n { ->~Adding~cmd~hook~to~'#2'~(#3): } }
+ \str_case:nnTF {#3}
+ { { before } { } { after } { } }
+ { \exp_args:Nc \@@_patch_cmd_or_delay:Nnn {#2} {#2} {#3} }
+ { \__kernel_msg_error:nnn { hooks } { wrong-cmd-hook } {#2} {#3} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_patch_cmd_or_delay:Nnn}
+% \begin{macro}{\@@_cmd_begindocument_code:}
+% In the preamble, \cs{@@_patch_cmd_or_delay:Nnn} just adds the patch
+% instruction to a property list to be executed later.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_cmd_or_delay:Nnn #1 #2 #3
+ {
+ \@@_debug:n { \iow_term:n { ->~Add~generic~cmd~hook~for~#2~(#3). } }
+ \@@_debug:n
+ { \iow_term:n { !~In~the~preamble:~delaying. } }
+ \prop_gput:Nnn \g_@@_delayed_patches_prop { #2 / #3 }
+ { \@@_cmd_try_patch:nn {#2} {#3} }
+ }
+% \end{macrocode}
+%
+% The delayed patches are added to a property list to prevent
+% duplication, and the code stored in the property list for each
+% key is executed. The function \cs{@@_patch_cmd_or_delay:Nnn} is
+% also redefined to be \cs{@@_patch_command:Nnn} so that no further
+% delaying is attempted.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cmd_begindocument_code:
+ {
+ \cs_gset_eq:NN \@@_patch_cmd_or_delay:Nnn \@@_patch_command:Nnn
+ \prop_map_function:NN \g_@@_delayed_patches_prop { \use_ii:nn }
+ \prop_gclear:N \g_@@_delayed_patches_prop
+ \cs_undefine:N \@@_cmd_begindocument_code:
+ }
+\g@addto@macro \@kernel@after@begindocument
+ { \@@_cmd_begindocument_code: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_cmd_try_patch:nn}
+% At |\begin{document}| tries patching the command if the hook
+% was not manually created in the meantime. If the document does not
+% exist, no error is raised here as it may hook into a package that
+% wasn't loaded. Hooks added to commands in the document body still
+% raise an error if the command is not defined.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cmd_try_patch:nn #1 #2
+ {
+ \@@_debug:n
+ { \iow_term:x { ->~\string\begin{document}~try~cmd / #1 / #2. } }
+ \@@_if_declared:nTF { cmd / #1 / #2 }
+ {
+ \@@_debug:n
+ { \iow_term:n { .->~Giving~up:~hook~already~created. } }
+ }
+ {
+ \cs_if_exist:cT {#1}
+ { \exp_args:Nc \@@_patch_command:Nnn {#1} {#1} {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%
+% \subsection{Patching commands}
+%
+% \begin{macro}{\@@_patch_command:Nnn}
+% \begin{macro}{\@@_patch_check:NNnn}
+% \begin{macro}[TF]{\@@_if_public_command:N}
+% \begin{macro}{\@@_if_public_command:w}
+% \cs{@@_patch_command:Nnn} will do some sanity checks on the
+% argument to detect if it is possible to add hooks to the command,
+% and raises an error otherwise. If the command can contain hooks,
+% then it uses \tn{robust@command@act} to find out what type is the
+% command, and patch it accordingly.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_command:Nnn #1 #2 #3
+ {
+ \@@_patch_debug:x { analyzing~'\token_to_str:N #1' }
+ \@@_patch_debug:x { \token_to_str:N #1 = \token_to_meaning:N #1 }
+ \@@_patch_check:NNnn \cs_if_exist:NTF #1 { undef }
+ {
+ \@@_patch_debug:x { ++~control~sequence~is~defined }
+ \@@_patch_check:NNnn \token_if_macro:NTF #1 { macro }
+ {
+ \@@_patch_debug:x { ++~control~sequence~is~a~macro }
+ \@@_patch_check:NNnn \@@_if_public_command:NTF #1 { expl3 }
+ {
+ \@@_patch_debug:x { ++~macro~is~not~private }
+ \robust@command@act
+ \g_hook_patch_action_list_tl #1
+ \@@_retokenize_patch:Nnn { #1 {#2} {#3} }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+%
+% And here's the auxiliary used above:
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_check:NNnn #1 #2 #3 #4
+ {
+ #1 #2 {#4}
+ {
+ \__kernel_msg_error:nnxx { hooks } { cant-patch }
+ { \token_to_str:N #2 } {#3}
+ }
+ }
+% \end{macrocode}
+% and a conditional \cs{@@_if_public_command:N} to check if a command
+% has |__| in its name (no other checking is performed). Primitives
+% with |:D| in their name could be included here, but they are already
+% discarded in the \cs{token_if_macro:NTF} test above.
+% \begin{macrocode}
+\use:x
+ {
+ \prg_new_protected_conditional:Npnn
+ \exp_not:N \@@_if_public_command:N ##1 { TF }
+ {
+ \exp_not:N \exp_last_unbraced:Nf
+ \exp_not:N \@@_if_public_command:w
+ { \exp_not:N \cs_to_str:N ##1 }
+ \tl_to_str:n { _ _ } \s_@@_mark
+ }
+ }
+\exp_last_unbraced:NNNNo
+\cs_new_protected:Npn \@@_if_public_command:w
+ #1 \tl_to_str:n { _ _ } #2 \s_@@_mark
+ {
+ \tl_if_empty:nTF {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+%
+%
+%
+%
+%
+%
+% \subsubsection{Patching by expansion and redefinition}
+%
+% \begin{macro}[int]{\g_hook_patch_action_list_tl}
+% This is the list of known command types and the function that
+% patches the command hooks into them. The conditionals are taken
+% from \tn{ShowCommand}, \tn{NewCommandCopy} and
+% \cs{__kernel_cmd_if_xparse:NTF} defined in \texttt{ltcmd}.
+% \begin{macrocode}
+\tl_gset:Nn \g_hook_patch_action_list_tl
+ {
+ { \@if@DeclareRobustCommand \@@_patch_DeclareRobustCommand:Nnn }
+ { \@if@newcommand \@@_patch_newcommand:Nnn }
+ { \__kernel_cmd_if_xparse:NTF \@@_cmd_patch_xparse:Nnn }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+% \begin{macro}{\@@_patch_DeclareRobustCommand:Nnn}
+% At this point we know that the commands can be patched by expanding
+% then redefining. These are the cases of commands defined with
+% \tn{newcommand} with an optional argument or with
+% \tn{DeclareRobustCommand}.
+%
+% With \cs{@@_patch_DeclareRobustCommand:Nnn} we check if the command
+% has an optional argument (with a test counter-intuitively called
+% \tn{@if@newcommand}). If so, we forward the action to
+% \cs{@@_patch_newcommand:Nnn}, otherwise call the patching engine
+% \cs{@@_patch_expand_redefine:NNnn} with a \cs{c_false_bool} to
+% indicate that there is no optional argument.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_DeclareRobustCommand:Nnn #1
+ {
+ \exp_args:Nc \@if@newcommand { \cs_to_str:N #1 ~ }
+ { \exp_args:Nc \@@_patch_newcommand:Nnn }
+ { \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_false_bool }
+ { \cs_to_str:N #1 ~ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_patch_newcommand:Nnn}
+% If the command was defined with \tn{newcommand} and an optional
+% argument, call the patching engine with a \cs{c_true_bool} to flag
+% the presence of an optional argument, and with
+% \cs[no-index]{\string\command} to patch the actual code for
+% \cs[no-index]{command}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_newcommand:Nnn #1
+ {
+ \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_true_bool
+ { \c_backslash_str \cs_to_str:N #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_cmd_patch_xparse:Nnn}
+% And for commands defined by the \pkg{xparse} commands use this
+% for patching:
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cmd_patch_xparse:Nnn #1
+ {
+ \exp_args:NNc \@@_patch_expand_redefine:NNnn \c_false_bool
+ { \cs_to_str:N #1 ~ code }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+% \begin{macro}{\@@_patch_expand_redefine:NNnn}
+% \begin{macro}[EXP]{\@@_make_prefixes:w}
+% Now the real action begins. Here we have in |#1| a boolean
+% indicating if the command has a |[|\ldots|]|-delimited argument, in
+% |#2| the command control sequence, in |#3| the name of the command
+% (note that |#1|${}\ne{}$|\csname#2\endcsname| at this point!), and
+% in |#4| the hook position, either |before| or |after|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_expand_redefine:NNnn #1 #2 #3 #4
+ {
+ \@@_patch_debug:x { ++~command~can~be~patched~without~rescanning }
+% \end{macrocode}
+% We'll start by counting the number of arguments in the command by
+% counting the number of characters in the \cs{cs_argument_spec:N} of
+% the macro, divided by two, and subtracting one if the command has an
+% optional argument (that is, an extra |[]| in its
+% \meta{parameter text}).
+% \begin{macrocode}
+ \int_set:Nn \l_@@_patch_num_args_int
+ {
+ \exp_args:Nf \str_count:n { \cs_argument_spec:N #2 } / 2
+ \bool_if:NT #1 { -1 }
+ }
+% \end{macrocode}
+% Now build two token lists:
+% \begin{description}
+% \item[\cs{l_@@_patch_param_text_tl}] will contain the
+% \meta{parameter text} to be used when redefining the macro. It
+% should be identical to the \meta{parameter text} used when
+% originally defining that macro.
+% \item[\cs{l_@@_patch_replacement_tl}] will contain braced pairs of
+% |#|$_{12}$\meta{num} to feed to the macro when expanded. This
+% token list as well as the previous will have the first item
+% surrounded by |[|\ldots|]| in the case of an optional argument.
+% \end{description}
+% \begin{macrocode}
+ \int_compare:nNnTF { \l_@@_patch_num_args_int } > { \c_zero_int }
+ {
+ \tl_set:Nx \l_@@_patch_param_text_tl
+ { \bool_if:NTF #1 { [####1] } { ####1 } }
+ \tl_set:Nx \l_@@_patch_replacement_tl
+ { \bool_if:NTF #1 { [####1] } { {####1} } }
+ \int_step_inline:nnn { 2 } { \l_@@_patch_num_args_int }
+ {
+ \tl_put_right:Nn \l_@@_patch_param_text_tl { ## ####1 }
+ \tl_put_right:Nn \l_@@_patch_replacement_tl { { ## ####1 } }
+ }
+ }
+ {
+ \tl_clear:N \l_@@_patch_param_text_tl
+ \tl_clear:N \l_@@_patch_replacement_tl
+ }
+% \end{macrocode}
+% Finally, before redefining, we need to also get the prefixes used
+% when defining the command. Here we ensure that the \tn{escapechar}
+% is printable, otherwise a macro defined with prefixes
+% |\protected \long| will have it \tn{meaning} printed as
+% |protectedlong|, making life unnecessarily complicated. Here the
+% \tn{escapechar} is changed to |/|, then we loop between pairs of
+% |/|\ldots|/| extracting the prefixes.
+% \begin{macrocode}
+ \group_begin:
+ \int_set:Nn \tex_escapechar:D { `\/ }
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nx \exp_not:N \l_@@_patch_prefixes_tl
+ { \exp_not:N \@@_make_prefixes:w \cs_prefix_spec:N #2 / / }
+ }
+% \end{macrocode}
+% Now that all the needed tools are ready, without further ado we'll
+% redefine the command |#2|. The definition uses the prefixes
+% gathered in \cs{l_@@_patch_prefixes_tl}, a primitive \cs{tex_def:D}
+% to avoid adding extra prefixes, and the \meta{parameter text} from
+% \cs{l_@@_patch_param_text_tl}.
+%
+% Then finally, in the body of the definition, we insert
+% \hook{cmd/\#3/before} if |#4| is |before|, the code of the
+% command expanded once grabbing the parameters in
+% \cs{l_@@_patch_replacement_tl}, and \hook{cmd/\#3/after} if |#4| is
+% |after|.
+% \begin{macrocode}
+ \use:x
+ {
+ \l_@@_patch_prefixes_tl \tex_def:D
+ \exp_not:N #2 \exp_not:V \l_@@_patch_param_text_tl
+ {
+ \str_if_eq:nnT {#4} { before }
+ { \exp_not:N \UseHook { cmd / #3 / #4 } }
+ \exp_args:No \exp_not:o
+ { \exp_after:wN #2 \l_@@_patch_replacement_tl }
+ \str_if_eq:nnT {#4} { after }
+ { \exp_not:N \UseHook { cmd / #3 / #4 } }
+ }
+ }
+ }
+% \end{macrocode}
+%
+% Here's the auxiliary that makes the prefix control sequences for the
+% redefinition. Each item has to be \cs{tl_trim_spaces:n}'d because
+% the last item (and not any other) has a trailing space.
+% \begin{macrocode}
+\cs_new:Npn \@@_make_prefixes:w / #1 /
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \exp_not:c { tex_ \tl_trim_spaces:n {#1} :D }
+ \@@_make_prefixes:w /
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%
+%
+%
+%
+%
+% \subsubsection{Patching by retokenization}
+%
+% At this point we've drained the possibilities of patching a command by
+% expansion-and-redefinition, so we have to resort to patching by
+% retokenizing the command. Patching by retokenization is done by
+% getting the \tn{meaning} of the command, doing the necessary
+% manipulations on the generated string, and the retokenizing that again
+% by using \tn{scantokens}.
+%
+% Patching by retokenization is definitely a riskier business, because
+% it relies that the tokens printed by \tn{meaning} produce the exact
+% same tokens as the ones in the original definition. That is, the
+% catcode régime must be exactly(ish) the same, and there is no way of
+% telling except by trial and error.
+%
+% \begin{macro}{\@@_retokenize_patch:Nnn}
+% This is the macro that will control the whole process. First we'll
+% try out one final, rather trivial case, of a command with no
+% arguments; that is, a token list. This case can be patched with
+% the expand-and-redefine routine but it has to be the very last case
+% tested for, because most (all?) robust commands start with a
+% top-level macro with no arguments, so testing this first would
+% short-circuit \tn{robust@command@act} and the top-level macros would
+% be incorrectly patched. In that case, we just check if the
+% \cs{cs_argument_spec:N} is empty, and call
+% \cs{@@_patch_expand_redefine:NNnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_retokenize_patch:Nnn #1 #2 #3
+ {
+ \@@_patch_debug:x { ..~command~can~only~be~patched~by~rescanning }
+ \str_if_eq:eeTF { \cs_argument_spec:N #1 } { }
+ { \@@_patch_expand_redefine:NNnn \c_false_bool #1 {#2} {#3} }
+ {
+% \end{macrocode}
+%
+% Otherwise, we start the actual patching by retokenization job. The
+% code calls \cs{@@_try_patch_with_catcodes:Nnnnw} with a different
+% catcode setting:
+% \begin{itemize}
+% \item The current catcode setting;
+% \item Switching the catcode of |@|;
+% \item Switching the \pkg{expl3} syntax on or off;
+% \item Both of the above.
+% \end{itemize}
+%
+% If patching succeeds, \cs{@@_try_patch_with_catcodes:Nnnnw} has the
+% side-effect of patching the macro |#1| (which may be an internal
+% from the command whose name is~|#2|).
+% \begin{macrocode}
+ \tl_set:Nx \l_@@_tmpa_tl
+ {
+ \int_compare:nNnTF { \char_value_catcode:n {`\@ } } = { 12 }
+ { \exp_not:N \makeatletter } { \exp_not:N \makeatother }
+ }
+ \tl_set:Nx \l_@@_tmpb_tl
+ {
+ \bool_if:NTF \l__kernel_expl_bool
+ { \ExplSyntaxOff } { \ExplSyntaxOn }
+ }
+ \use:x
+ {
+ \exp_not:N \@@_try_patch_with_catcodes:Nnnnw
+ \exp_not:n { #1 {#2} {#3} }
+ { \prg_do_nothing: }
+ { \exp_not:V \l_@@_tmpa_tl } % @
+ { \exp_not:V \l_@@_tmpb_tl } % _:
+ {
+ \exp_not:V \l_@@_tmpa_tl % @
+ \exp_not:V \l_@@_tmpb_tl % _:
+ }
+ }
+ \q_recursion_tail \q_recursion_stop
+% \end{macrocode}
+%
+% If no catcode setting succeeds, give up and raise an error. The
+% command isn't changed in any way in that case.
+% \begin{macrocode}
+ {
+ \__kernel_msg_error:nnxx { hooks } { cant-patch }
+ { \c_backslash_str #2 } { retok }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_try_patch_with_catcodes:Nnnnw}
+% This function is a simple wrapper around
+% \cs{@@_cmd_if_scanable:NnTF} and \cs{@@_patch_retokenize:Nnnn} if
+% the former returns \meta{true}, plus some debug messages.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_try_patch_with_catcodes:Nnnnw #1 #2 #3 #4
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4} { \use:n }
+ \@@_patch_debug:x { ++~trying~to~patch~by~retokenization }
+ \@@_cmd_if_scanable:NnTF {#1} {#4}
+ {
+ \@@_patch_debug:x { ++~macro~can~be~retokenized~cleanly }
+ \@@_patch_debug:x { ==~retokenizing~macro~now }
+ \@@_patch_retokenize:Nnnn #1 {#2} {#3} {#4}
+ \use_i_delimit_by_q_recursion_stop:nw \use_none:n
+ }
+ {
+ \@@_patch_debug:x { --~macro~cannot~be~retokenized~cleanly }
+ \@@_try_patch_with_catcodes:Nnnnw #1 {#2} {#3}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+% \begin{macro}[int]{\kerneltmpDoNotUse}
+% This is an oddity required to be safe (as safe as reasonably
+% possible) when patching the command. The entirety of
+% \begin{quote}
+% \meta{prefixes} \tn{def} \meta{cs} \meta{parameter text}
+% |{|\meta{replacement text}|}|
+% \end{quote}
+% will go through \tn{scantokens}. The \meta{parameter text} and
+% \meta{replacement text} are what we are trying to retokenize, so not
+% much worry there. The other items, however, should ``just work'',
+% so some care is needed to not use too fancy catcode settings.
+% Therefore we can't use an \pkg{expl3}-named macro for \meta{cs}, nor
+% the \pkg{expl3} versions of \tn{def} or the \meta{prefixes}.
+% That is why the definitions that will eventually go into
+% \tn{scantokens} will use the oddly (but hopefully clearly)-named
+% \cs{kerneltmpDoNotUse}:
+% \begin{macrocode}
+\cs_new_eq:NN \kerneltmpDoNotUse !
+% \end{macrocode}
+% \phoinline{Maybe this can be avoided by running the \meta{parameter text}
+% and the \meta{replacement text} separately through \tn{scantokens}
+% and then putting everything together at the end.}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_patch_required_catcodes:}
+% Here are the catcode settings that are \emph{mandatory} when
+% retokenizing commands. These are the minimum necessary settings to
+% perform the definitions: they identify control sequences, which
+% must be escaped with |\|$_0$, delimit the definition with |{|$_1$
+% and |}|$_2$, and mark parameters with |#|$_6$. Everything else may
+% be changed, but not these.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_required_catcodes:
+ {
+ \char_set_catcode_escape:N \\
+ \char_set_catcode_group_begin:N \{
+ \char_set_catcode_group_end:N \}
+ \char_set_catcode_parameter:N \#
+ % \int_set:Nn \tex_endlinechar:D { -1 }
+ % \int_set:Nn \tex_newlinechar:D { -1 }
+ }
+% \end{macrocode}
+% \phoinline{\pkg{etoolbox} sets the \tn{endlinechar} and \tn{newlinechar}
+% when patching, but as far as I tested these didn't make much of
+% a difference, so I left them out for now. Maybe
+% \tn{newlinechar}|=-1| avoids a space token being added after the
+% definition.}
+% \phoinline{If the patching is split by \meta{parameter text} and
+% \meta{replacement text}, then only \# will have to stay in that
+% list.}
+% \phoinline{Actually now that we patch
+% \texttt{\cs{UseHook}\{cmd/foo/before\}}, all the tokens there need
+% to have the right catcodes, so this list now includes all
+% lowercase letters, U and H, the slash, and whatever characters in
+% the command name\ldots sigh\ldots}
+% \end{macro}
+%
+%
+%
+%
+% \begin{macro}[TF]{\@@_cmd_if_scanable:Nn}
+% Here we'll do a quick test if the command being patched can in fact
+% be retokenized with the specific catcode setting without changing
+% in meaning. The test is straightforward:
+% \begin{enumerate}
+% \item apply \tn{meaning} to the command;
+% \item split the \meta{prefixes}, \meta{parameter text} and
+% \meta{replacement text} and arrange them as
+% \begin{quote}
+% \meta{prefixes}\tn{def}\cs{kerneltmpDoNotUse}%^^A
+% \meta{parameter text}|{|\meta{replacement text}|}|
+% \end{quote}
+% \item rescan that with the given catcode settings, and do
+% the definition; then finally
+% \item compare \cs{kerneltmpDoNotUse} with the original command.
+% \end{enumerate}
+% If both are \tn{ifx}-equal, the command can be safely patched.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \@@_cmd_if_scanable:Nn #1 #2 { TF }
+ {
+ \cs_set_eq:NN \kerneltmpDoNotUse \scan_stop:
+ \cs_set_eq:NN \@@_tmp:w \scan_stop:
+ \use:x
+ {
+ \cs_set:Npn \@@_tmp:w
+ ####1 \tl_to_str:n { macro: } ####2 -> ####3 \s_@@_mark
+ { ####1 \def \kerneltmpDoNotUse ####2 {####3} }
+ \tl_set:Nx \exp_not:N \l_@@_tmpa_tl
+ { \exp_not:N \@@_tmp:w \token_to_meaning:N #1 \s_@@_mark }
+ }
+ \tl_rescan:nV { #2 \@@_patch_required_catcodes: } \l_@@_tmpa_tl
+ \token_if_eq_meaning:NNTF #1 \kerneltmpDoNotUse
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_patch_retokenize:Nnnn}
+% Then, if \cs{@@_cmd_if_scanable:NnTF} returned true, we can go on
+% and patch the command.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_retokenize:Nnnn #1 #2 #3 #4
+ {
+% \end{macrocode}
+% Start off by making some things \tn{relax} to avoid lots of
+% \tn{noexpand} below.
+% \begin{macrocode}
+ \cs_set_eq:NN \kerneltmpDoNotUse \scan_stop:
+ \cs_set_eq:NN \@@_tmp:w \scan_stop:
+ \use:x
+ {
+% \end{macrocode}
+% Now we'll define \cs{@@_tmp:w} such that it splits the \tn{meaning}
+% of the macro (|#1|) into its three parts:
+% \begin{enumerate}
+% \def\makelabel#1{\texttt{\#\#\#\##1}}
+% \item \meta{prefixes}
+% \item \meta{parameter text}
+% \item \meta{replacement text}
+% \end{enumerate}
+% and arrange that a complete definition, then place the |before|
+% or |after| hooks around the \meta{replacement text}:
+% accordingly.
+% \begin{macrocode}
+ \cs_set:Npn \@@_tmp:w
+ ####1 \tl_to_str:n { macro: } ####2 -> ####3 \s_@@_mark
+ {
+ ####1 \def \kerneltmpDoNotUse ####2
+ {
+ \str_if_eq:nnT {#3} { before }
+ { \token_to_str:N \UseHook { cmd / #2 / #3 } }
+ ####3
+ \str_if_eq:nnT {#3} { after }
+ { \token_to_str:N \UseHook { cmd / #2 / #3 } }
+ }
+ }
+% \end{macrocode}
+% Now we just have to get the \tn{meaning} of the command being
+% patched and pass it through the meat grinder above.
+% \begin{macrocode}
+ \tl_set:Nx \exp_not:N \l_@@_tmpa_tl
+ { \exp_not:N \@@_tmp:w \token_to_meaning:N #1 \s_@@_mark }
+ }
+% \end{macrocode}
+% Now rescan with the given catcode settings (overridden by the
+% \cs{@@_patch_required_catcodes:}), and implicitly (by using the
+% rescanned token list) carry out the definition from above.
+% \begin{macrocode}
+ \tl_rescan:nV { #4 \@@_patch_required_catcodes: } \l_@@_tmpa_tl
+% \end{macrocode}
+% And to close, copy the newly-defined command into the old name and
+% the patching is finally completed:
+% \begin{macrocode}
+ \cs_set_eq:NN #1 \kerneltmpDoNotUse
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { hooks } { wrong-cmd-hook }
+ {
+ Command~hook~`cmd/#1/#2'~invalid.\\
+ The~hook~should~be~`cmd/#1/before'~or~`cmd/#1/after'.
+ }
+ {
+ You~tried~to~add~a~hook~to~command~\iow_char:N \\#1,~but~`#2'~
+ is~an~invalid~position.~Only~`before'~or~`after'~are~allowed.
+ }
+\__kernel_msg_new:nnnn { hooks } { cant-patch }
+ {
+ Command~`#1'~cannot~have~hooks~because~it~
+ \@@_unpatchable_cases:n {#2} .
+ }
+ {
+ You~tried~to~add~a~hook~to~`#1',~but~LaTeX~was~not~able~to~
+ add~the~hook~to~that~command~because~`#1'~
+ \@@_unpatchable_cases:n {#2} .
+ }
+\cs_new:Npn \@@_unpatchable_cases:n #1
+ {
+ \str_case:nn {#1}
+ {
+ { undef } { doesn't~exist }
+ { macro } { is~not~a~macro }
+ { expl3 } { is~a~private~expl3~macro }
+ { retok } { can't~be~retokenized~cleanly }
+ }
+ }
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{0000/00/00}{ltcmdhooks}%
+%<latexrelease> {The~hook~management~system~for~commands}
+%<latexrelease>
+% \end{macrocode}
+% The command \cs{@@_cmd_begindocument_code:} is used in an
+% internal hook, so we need to make sure it has a harmless
+% definition after rollback as that will not remove it from the
+% kernel hook.
+% \begin{macrocode}
+%<latexrelease>\cs_set_eq:NN \@@_cmd_begindocument_code: \prg_do_nothing:
+%<latexrelease>
+%<latexrelease>\EndModuleRelease
+\ExplSyntaxOff
+%</2ekernel|latexrelease>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=>
+% \end{macrocode}
+%
+% \Finale
+%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx b/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx
index ab9d4d0da16..7236083dfa2 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltdefns.dtx
@@ -32,7 +32,7 @@
%<*driver>
% \fi
\ProvidesFile{ltdefns.dtx}
- [2021/01/15 v1.5o LaTeX Kernel (definition commands)]
+ [2021/04/19 v1.5o LaTeX Kernel (definition commands)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltdefns.dtx}
@@ -2419,7 +2419,8 @@
%
%
% \begin{macro}{\g@addto@macro}
-% Globally add to the end of a macro.
+% Globally add to the end of a macro.
+% This macro is used by the kernel to add to its internal hooks.
% \changes{v0.2a}{1993/11/14}{Made global}
% \changes{v0.2w}{1994/01/31}
% {Use toks register to avoid `hash' problems}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx
index a9be1b05211..6d7ec3ff60b 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltexpl.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltexpl.dtx}
- [2021/01/24 v1.3a LaTeX Kernel (expl3-dependent code)]
+ [2021/04/20 v1.3c LaTeX Kernel (expl3-dependent code)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltexpl.dtx}
@@ -82,21 +82,46 @@
% \begin{macrocode}
%<*2ekernel|latexrelease>
%<latexrelease>\IncludeInRelease{2020/10/01}%
-%<latexrelease> {kernel@enddocument hooks}{Define kernel enddocument Hooks}%
-\let\@kernel@after@enddocument\@empty
-\let\@kernel@after@enddocument@afterlastpage\@empty
+%<latexrelease> {kernel@enddocument hooks}{Define several kernel hooks}
+% \end{macrocode}
+% We only initialize these kernel hooks if they are not already
+% existing. Otherwise they would be set to \cs{@empty} on rollback
+% which would be wrong because code that has been added to to them
+% may still have to be executed in the rollback situation . Instead
+% code that writes to them needs to handle the rollback as needed.
+% It is likely that we have to change that approach in the future,
+% but for now it should do.
+% (It is enough to test only for the existence of one hook, as all
+% got added at the same time.)
+% \changes{v1.3c}{2021/04/20}{Don't empty kernel hooks on rollback}
+% \begin{macrocode}
+\ifx\@kernel@after@enddocument\@undefined
+ \let \@kernel@after@enddocument \@empty
+ \let \@kernel@after@enddocument@afterlastpage \@empty
+% \end{macrocode}
+%
+% \begin{macro}{\@kernel@before@begindocument,\@kernel@after@begindocument}
+% For the similar reasons we also define those that are used in
+% \cs{document} because they too get material added to in early modules.
+% \begin{macrocode}
+ \let \@kernel@before@begindocument \@empty
+ \let \@kernel@after@begindocument \@empty
+\fi
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {kernel@enddocument hooks}{Define kernel enddocument Hooks}%
+%<latexrelease> {kernel@enddocument hooks}{Define several kernel hooks}
%<latexrelease>\let\@kernel@after@enddocument\@undefined
%<latexrelease>\let\@kernel@after@enddocument@afterlastpage\@undefined
-%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\let\@kernel@before@begindocument\@undefined
+%<latexrelease>\let\@kernel@after@begindocument\@undefined
%</2ekernel|latexrelease>
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
% First define some blank commands, so that in case something goes wrong while
% loading \textsf{expl3}, we won't get strange \texttt{Undefined control
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx
index 85c758ae608..e9309c41f78 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfilehook.dtx
@@ -31,8 +31,8 @@
%%% From File: ltfilehook.dtx
%
% \begin{macrocode}
-\providecommand\ltfilehookversion{v1.0g}
-\providecommand\ltfilehookdate{2021/02/08}
+\providecommand\ltfilehookversion{v1.0j}
+\providecommand\ltfilehookdate{2021/04/29}
% \end{macrocode}
%
%<*driver>
@@ -585,9 +585,13 @@
% 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.}
% \begin{macrocode}
\tl_new:N \l_@@_internal_tl
-\seq_new:N \g_@@_input_file_seq
+\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
@@ -601,7 +605,7 @@
\seq_gpop:NNTF \g_@@_input_file_seq \l_@@_internal_tl
{ \exp_after:wN \@@_file_pop_assign:nnnn \l_@@_internal_tl }
{
- \msg_error:nnn { hooks } { should-not-happen }
+ \__kernel_msg_error:nnn { hooks } { should-not-happen }
{ Tried~to~pop~from~an~empty~file~name~stack. }
}
}
@@ -1031,15 +1035,18 @@
% 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)}
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\set@curr@file}{Setting current file name}%
\def\set@curr@file#1{%
\begingroup
\escapechar\m@ne
\let\protect\string
+ \edef~{\string~}%
\expandafter\def\csname\expandafter\endcsname
\expandafter{\csname\endcsname}%
% \end{macrocode}
@@ -1078,6 +1085,7 @@
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}%
+%<latexrelease> {\set@curr@file}{Setting current file name}%
%<latexrelease>\def\set@curr@file#1{%
%<latexrelease> \begingroup
%<latexrelease> \escapechar\m@ne
@@ -1529,7 +1537,7 @@
%
% \begin{macrocode}
\cs_new_eq:NN \@expl@@@filehook@file@push@@
- \__filehook_file_push:
+ \__filehook_file_push:
% \end{macrocode}
%
% \begin{macrocode}
@@ -1720,8 +1728,8 @@
% \Finale
%
%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\endinput
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx b/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx
index b27f9105873..bcaac582208 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfiles.dtx
@@ -32,7 +32,7 @@
%<*driver>
% \fi
\ProvidesFile{ltfiles.dtx}
- [2021/01/15 v1.2j LaTeX Kernel (File Handling)]
+ [2021/04/17 v1.2m LaTeX Kernel (File Handling)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltfiles.dtx}
@@ -195,19 +195,6 @@
% \end{oldcomments}
%
%
-% \task{???}{Do we use @unused or mainaux?}
-% \begin{macro}{\@inputcheck}
-% \begin{macro}{\@unused}
-% Allocate read stream for testing and output stream.
-% \changes{v1.0l}{1994/11/07}
-% {move here from ltdefns, remove duplicate \cs{@mainaux}}
-% \begin{macrocode}
-\newread\@inputcheck
-\newwrite\@unused
-% \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
% \begin{macro}{\@mainaux}
% \begin{macro}{\@partaux}
% \begin{macrocode}
@@ -470,9 +457,14 @@
% \end{macrocode}
% \InternalDetectionOn
%
+% These internal hooks are already declared earlier (in
+% \texttt{ltexpl}) so that other modules could write to them.
+% \changes{v1.2m}{2021/04/17}{Move \cs{@kernel@before@begindocument} and
+% \cs{@kernel@after@begindocument} init earlier so that other modules can
+% write to the hooks}
% \begin{macrocode}
-\let \@kernel@before@begindocument \@empty
-\let \@kernel@after@begindocument \@empty
+%\let \@kernel@before@begindocument \@empty
+%\let \@kernel@after@begindocument \@empty
% \end{macrocode}
%
% \end{macro}
@@ -1072,30 +1064,114 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macrocode}
+%</2ekernel|latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{0000/00/00}%
+%<latexrelease> {\quote@name}{Quote file names}%
+%<latexrelease>
+%<latexrelease>\let\quote@name\@undefined
+%<latexrelease>\let\quote@@name\@undefined
+%<latexrelease>\let\unquote@name\@undefined
+%<latexrelease>
+%<latexrelease>\long\def \IfFileExists#1#2#3{%
+%<latexrelease> \openin\@inputcheck#1 %
+%<latexrelease> \ifeof\@inputcheck
+%<latexrelease> \ifx\input@path\@undefined
+%<latexrelease> \def\reserved@a{#3}%
+%<latexrelease> \else
+%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}%
+%<latexrelease> \fi
+%<latexrelease> \else
+%<latexrelease> \closein\@inputcheck
+%<latexrelease> \edef\@filef@und{#1 }%
+%<latexrelease> \def\reserved@a{#2}%
+%<latexrelease> \fi
+%<latexrelease> \reserved@a}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<*2ekernel>
+% \end{macrocode}
+%
+%
+%
% \begin{macro}{\IfFileExists@}
% \changes{v0.9b}{1993/12/04}{Macro added}
% \changes{v0.9p}{1994/01/18}{New Definition}
% \changes{v1.0t}{1995/05/25}{(CAR) added \cs{long}}
-% \changes{v1.2d}{2019/10/26}{quote on openin}%
+% \changes{v1.2d}{2019/10/26}{quote on openin}
+% \changes{v1.2k}{2021/03/12}{Allow unbalanced conditionals (gh/530)}
% Argument |#1| is |\@curr@file| so catcode 12 string with no quotes.
+%
+% The original definition picked up arguments |#2| and |#3| in a
+% way that they couldn't contain unbalanced conditionals. A better
+% implementation would have been not to pick up the arguments at
+% all but instead use the usual \cs{@firstoftwo} and
+% \cs{secondoftwo}. However, that changes how |#| is interpreted
+% and so we can't do that nowaways without invalidating a lot of
+% code. Therefore the somewhat curious construction near the end.
% \begin{macrocode}
+%</2ekernel>
+%<*2ekernel|latexrelease>
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals}
\long\def \IfFileExists@#1#2#3{%
\openin\@inputcheck"#1" %
\ifeof\@inputcheck
\ifx\input@path\@undefined
- \def\reserved@a{#3}%
+ \let\reserved@a\@secondoftwo
\else
- \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}%
+ \def\reserved@a{\@iffileonpath{#1}}%
\fi
\else
\closein\@inputcheck
\edef\@filef@und{"#1" }%
- \def\reserved@a{#2}%
+ \let\reserved@a\@firstoftwo
\fi
- \reserved@a}
+% \end{macrocode}
+% This is just there so that any |#| inside |#2| or |#3| needs
+% doubling (as that was the case in the past).
+% \begin{macrocode}
+ \expandafter\def\expandafter\reserved@a
+ \expandafter{\reserved@a{#2}{#3}}%
+\reserved@a}
+%</2ekernel|latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2019/10/01}%
+%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals}
+%<latexrelease>
+%<latexrelease>\long\def \IfFileExists@#1#2#3{%
+%<latexrelease> \openin\@inputcheck"#1" %
+%<latexrelease> \ifeof\@inputcheck
+%<latexrelease> \ifx\input@path\@undefined
+%<latexrelease> \def\reserved@a{#3}%
+%<latexrelease> \else
+%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}%
+%<latexrelease> \fi
+%<latexrelease> \else
+%<latexrelease> \closein\@inputcheck
+%<latexrelease> \edef\@filef@und{"#1" }%
+%<latexrelease> \def\reserved@a{#2}%
+%<latexrelease> \fi
+%<latexrelease> \reserved@a}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{0000/00/00}%
+%<latexrelease> {\IfFileExists@}{manage unbalanced conditionals}
+%<latexrelease>
+%<latexrelease>\let\IfFileExists@\@undefined
+%<latexrelease>
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<*2ekernel>
% \end{macrocode}
% \end{macro}
%
+%
+%
+%
+%
+%
+%
% \begin{macro}{\@iffileonpath}
% If the file is not found by |\openin|, and |\input@path| is defined,
% look in all the directories specified in |\input@path|.
@@ -1109,6 +1185,10 @@
% \changes{v1.2d}{2019/10/26}{quote on openin}%
% \changes{v1.2f}{2019/11/11}{make \cs{@filef@und} match quoting used on \cs{openin}}%
% \begin{macrocode}
+%</2ekernel>
+%<*2ekernel|latexrelease>
+%<latexrelease>\IncludeInRelease{2019/10/01}%
+%<latexrelease> {\@iffileonpath}{Quote file names}
\long\def\@iffileonpath#1{%
\let\reserved@a\@secondoftwo
\expandafter\@tfor\expandafter\reserved@b\expandafter
@@ -1128,28 +1208,7 @@
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\quote@name}{Quote file names}%
-%<latexrelease>
-%<latexrelease>\let\quote@name\@undefined
-%<latexrelease>\let\quote@@name\@undefined
-%<latexrelease>\let\unquote@name\@undefined
-%<latexrelease>
-%<latexrelease>\let\IfFileExists@\@undefined
-%<latexrelease>
-%<latexrelease>\long\def \IfFileExists#1#2#3{%
-%<latexrelease> \openin\@inputcheck#1 %
-%<latexrelease> \ifeof\@inputcheck
-%<latexrelease> \ifx\input@path\@undefined
-%<latexrelease> \def\reserved@a{#3}%
-%<latexrelease> \else
-%<latexrelease> \def\reserved@a{\@iffileonpath{#1}{#2}{#3}}%
-%<latexrelease> \fi
-%<latexrelease> \else
-%<latexrelease> \closein\@inputcheck
-%<latexrelease> \edef\@filef@und{#1 }%
-%<latexrelease> \def\reserved@a{#2}%
-%<latexrelease> \fi
-%<latexrelease> \reserved@a}
+%<latexrelease> {\quote@name}{Quote file names}
%<latexrelease>
%<latexrelease>\long\def\@iffileonpath#1{%
%<latexrelease> \let\reserved@a\@secondoftwo
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx b/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx
index d6fa6e8a7bf..19f82934aa6 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfinal.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltfinal.dtx}
- [2021/02/25 v2.2m LaTeX Kernel (Final Settings)]
+ [2021/04/18 v2.2o LaTeX Kernel (Final Settings)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltfinal.dtx}
@@ -242,7 +242,73 @@
% \end{macro}
% \end{macro}
%
-%
+% \begin{macro}{trace_stack_levels}
+% Now define the Lua function to emulate \cs{tracingstacklevels} and
+% install it in the \texttt{input_level_string} callback.
+% \begin{macrocode}
+%</2ekernel>
+%<*2ekernel|latexrelease>
+% \end{macrocode}
+% In \texttt{latexrelease} mode we always remove the function from
+% the callback, then add the correct version later.
+% \begin{macrocode}
+%<latexrelease>\ifx\directlua\@undefined
+%<latexrelease>\else
+%<latexrelease> \directlua{%
+%<latexrelease> if luatexbase.callbacktypes['input_level_string'] and %
+%<latexrelease> luatexbase.in_callback('input_level_string','tracingstacklevels') then
+%<latexrelease> luatexbase.remove_from_callback('input_level_string','tracingstacklevels')
+%<latexrelease> end}%
+%<latexrelease>\fi
+%<latexrelease>\IncludeInRelease{2021/06/01}{trace_stack_levels}%
+%<latexrelease> {Lua trace_stack_levels function}%
+\ifx\directlua\@undefined
+\else
+%<*2ekernel>
+ \expanded{%
+ \everyjob{\the\everyjob
+ \noexpand%\directlua
+%</2ekernel>
+ \directlua{%
+ local function trace_stack_levels (input_ptr)
+ local tracingstacklevels = tex.count.tracingstacklevels
+ if tex.tracingmacros > 0 or input_ptr < tracingstacklevels then
+ if tracingstacklevels > 0 then
+ if input_ptr < tracingstacklevels then
+ return "\string\n\string~" .. string.rep(".",input_ptr)
+ else
+ return "\string~\string~"
+ end
+ else
+ return "\string\n"
+ end
+ else
+ return ""
+ end
+ end
+%<latexrelease> if luatexbase.callbacktypes['input_level_string'] then
+ luatexbase.add_to_callback('input_level_string',
+ trace_stack_levels,'tracingstacklevels')
+%<latexrelease> end
+ }%
+%<*2ekernel>
+ }}%
+%</2ekernel>
+\fi
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+% \end{macrocode}
+% Then for the full rollback, just do nothing, since the function
+% was already taken out of the rollback above.
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{0000/00/00}{trace_stack_levels}%
+%<latexrelease> {Lua trace_stack_levels function}%
+%<latexrelease>% Nothing here
+%<latexrelease>\EndIncludeInRelease
+%</2ekernel|latexrelease>
+%<*2ekernel>
+% \end{macrocode}
+% \end{macro}
%
% The default values of the picture and |\fbox| parameters:
% \begin{macrocode}
@@ -574,7 +640,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\pdfgentounicode}{Preload glyphtounicode}%
\ifx \pdfgentounicode \@undefined \else
%<*2ekernel>
@@ -1194,7 +1260,6 @@
% {Load first aid file if existing}
% \begin{macrocode}
\@input{latex2e-first-aid-for-external-files.ltx}
-\@input{ltpara.ltx}
% \end{macrocode}
%
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx b/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx
index f2324118a04..ed25db3ed10 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfloat.dtx
@@ -31,7 +31,7 @@
%
%<*driver>
% \fi
-\ProvidesFile{ltfloat.dtx}[2021/02/10 v1.2f LaTeX Kernel (Floats)]
+\ProvidesFile{ltfloat.dtx}[2021/03/03 v1.2f LaTeX Kernel (Floats)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltfloat.dtx}
@@ -1008,7 +1008,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@savemarbox}{Explicit par for marginpar}%
\long\def \@savemarbox #1#2{%
\global\setbox #1%
@@ -1513,7 +1513,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@footnotetext}{footnotetext tagging}%
\long\def\@footnotetext#1{\insert\footins{%
\reset@font\footnotesize
@@ -1633,7 +1633,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\footref}{Add footref}%
\def\footref#1{%
\begingroup
@@ -1644,14 +1644,16 @@
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
-% We don't remove it when rolling back.
+% We don't remove it when rolling back so that packages offered it
+% in the past do not need to alter their behavior in a rollback
+% situation.
% \begin{macrocode}
-%%<latexrelease>\IncludeInRelease{0000/00/00}%
-%%<latexrelease> {\footref}{Add footref}%
-%%<latexrelease>
-%%<latexrelease>\let\footref\@undefined
-%%<latexrelease>
-%%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{0000/00/00}%
+%<latexrelease> {\footref}{Add footref}%
+%<latexrelease>
+%<latexrelease> % \let\footref\@undefined
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
%<*2ekernel>
% \end{macrocode}
% \end{macro}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx
index e0c07874556..86c84b111fb 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfssaxes.dtx
@@ -35,7 +35,7 @@
%
%
\ProvidesFile{ltfssaxes.dtx}
- [2021/01/15 v1.0h LaTeX Kernel (NFSS Axes handing)]
+ [2021/03/18 v1.0i LaTeX Kernel (NFSS Axes handing)]
% \iffalse
\documentclass{ltxdoc}
\begin{document}
@@ -699,7 +699,7 @@
%
% \begin{macrocode}
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\fontseries}{delay fontseries update}%
% \end{macrocode}
%
@@ -772,11 +772,12 @@
%<latexrelease>
% \end{macrocode}
% For a roll forward we may have to define \cs{if@forced@series}
-% but this needs doing in a somewhat roundabout way.
+% but this needs doing in a way that \TeX{} doesn't see it when
+% skipping over conditionals.
+% \changes{v1.0i}{2021/03/18}
+% {Fix rollforward definition.}
% \begin{macrocode}
-%<latexrelease>\ifx\@forced@seriestrue\@undefined \else
-%<latexrelease> \expandafter\newif\csname if@forced@series\endcsname
-%<latexrelease>\fi
+%<latexrelease>\expandafter\newif\csname if@forced@series\endcsname
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
@@ -1328,7 +1329,7 @@
%
% \begin{macrocode}
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\fontshape}{Font shape change}%
% \end{macrocode}
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx
index 6d575fc9d0b..df265d0b0fc 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfssbas.dtx
@@ -35,7 +35,7 @@
%
%
\ProvidesFile{ltfssbas.dtx}
- [2020/12/10 v3.2h LaTeX Kernel (NFSS Basic Macros)]
+ [2021/04/26 v3.2i LaTeX Kernel (NFSS Basic Macros)]
% \iffalse
\documentclass{ltxdoc}
\begin{document}
@@ -896,15 +896,35 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/02/02}%
-%<latexrelease> {\usefont}{Drop m in usefont}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\usefont}{Force font face}%
\DeclareRobustCommand\usefont[4]{\fontencoding{#1}%
\edef\f@family{#2}%
\set@target@series{#3}%
- \edef\f@shape{#4}\selectfont
+ \edef\f@shape{#4}%
+% \end{macrocode}
+% Any earlier \cs{fontseries}, etc.\ should be canceled and we
+% should switch unconditionally to the requested font face so we
+% drop any code that may have been stored in
+% \cs{delayed@f@adjustment}.
+% \changes{v3.2i}{2021/04/26}{Unconditionally switch to the requested
+% font face (gh/444)}
+% \begin{macrocode}
+ \let\delayed@f@adjustment\@empty
+ \selectfont
\ignorespaces}
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/02/02}%
+%<latexrelease> {\usefont}{Drop m in usefont}%
+%<latexrelease>
+%<latexrelease>\DeclareRobustCommand\usefont[4]{\fontencoding{#1}%
+%<latexrelease> \edef\f@family{#2}%
+%<latexrelease> \set@target@series{#3}%
+%<latexrelease> \edef\f@shape{#4}\selectfont
+%<latexrelease> \ignorespaces}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{0000/00/00}%
%<latexrelease> {\usefont}{Drop m in usefont}%
%<latexrelease>
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx b/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx
index 361adac9c36..b06fc161b08 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfssini.dtx
@@ -36,7 +36,7 @@
%
%
\ProvidesFile{ltfssini.dtx}
- [2021/01/15 v3.2f LaTeX Kernel (NFSS Initialisation)]
+ [2021/04/26 v3.2h LaTeX Kernel (NFSS Initialisation)]
% \iffalse
\documentclass{ltxdoc}
\begin{document}
@@ -1088,6 +1088,8 @@
% \end{macro}
%
%
+% \changes{v3.2g}{2021/03/18}
+% {Add legacy hook definitions for rollback.}
% \begin{macrocode}
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
@@ -1140,6 +1142,9 @@
%<latexrelease> \prepare@family@series@update{tt}\ttdefault
%<latexrelease> \@ttfamilyhook
%<latexrelease> \selectfont}
+%<latexrelease>\let\@rmfamilyhook\@empty
+%<latexrelease>\let\@sffamilyhook\@empty
+%<latexrelease>\let\@ttfamilyhook\@empty
%<latexrelease>
% \end{macrocode}
%
@@ -1765,7 +1770,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/02/02}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\normalfont}{Add hook to \normalfont}%
\DeclareRobustCommand\normalfont{%
% \end{macrocode}
@@ -1778,6 +1783,15 @@
\edef\f@series{\seriesdefault}%
\edef\f@shape{\shapedefault}%
% \end{macrocode}
+% Any earlier \cs{fontseries}, etc.\ should be canceled and we
+% should switch unconditionally to the requested font face so we
+% drop any code that may have been stored in
+% \cs{delayed@f@adjustment}.
+% \changes{v3.2h}{2021/04/26}{Unconditionally switch to the requested
+% font face (gh/444)}
+% \begin{macrocode}
+ \let\delayed@f@adjustment\@empty
+% \end{macrocode}
%
% \changes{v3.2b}{2020/08/21}{Integration of new hook management interface}
% \begin{macrocode}
@@ -1789,18 +1803,54 @@
\@defaultfamilyhook % hookname from 2020/02 will vanish
\selectfont}
% \end{macrocode}
-%
+%
% \begin{macrocode}
\let\reset@font\normalfont
% \end{macrocode}
% \end{macro}
% \end{macro}
-%
+%
%
%
% \begin{macrocode}
+% \changes{v3.2g}{2021/03/18}
+% {Add missing 2020/02/02 latexrelease entry.}
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{2020/10/01}%
+%<latexrelease> {\normalfont}{Add hook to \normalfont}%
+%<latexrelease>
+%<latexrelease>\DeclareRobustCommand\normalfont{%
+%<latexrelease> \fontencoding\encodingdefault
+%<latexrelease> \edef\f@family{\familydefault}%
+%<latexrelease> \edef\f@series{\seriesdefault}%
+%<latexrelease> \edef\f@shape{\shapedefault}%
+%<latexrelease> \UseHook{normalfont}%
+%<latexrelease> \@defaultfamilyhook % hookname from 2020/02 will vanish
+%<latexrelease> \selectfont}
+%<latexrelease>
+%<latexrelease>\let\reset@font\normalfont
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{2020/02/02}%
+%<latexrelease> {\normalfont}{Add hook to \normalfont}%
+%<latexrelease>
+%<latexrelease>\DeclareRobustCommand\normalfont{%
+%<latexrelease> \fontencoding\encodingdefault
+%<latexrelease> \edef\f@family{\familydefault}%
+%<latexrelease> \edef\f@series{\seriesdefault}%
+%<latexrelease> \edef\f@shape{\shapedefault}%
+%<latexrelease> \@defaultfamilyhook
+%<latexrelease> \selectfont}
+%<latexrelease>
+%<latexrelease>\let\reset@font\normalfont
+%<latexrelease>
+%<latexrelease>\let\@defaultfamilyhook\@empty
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
%<latexrelease>\IncludeInRelease{0000/00/00}%
%<latexrelease> {\normalfont}{Add hook to \normalfont}%
%<latexrelease>
diff --git a/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx b/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx
index 961abafc8a3..c6aec105ba5 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltfsstrc.dtx
@@ -32,12 +32,12 @@
%% Copyright (C) 1989-97 by Frank Mittelbach and Rainer Sch\"opf.
%% Copyright (C) 1994-97 by LaTeX Project. All rights reserved.
%
-%<package>\NeedsTeXFormat{LaTeX2e}[2021/05/01]
+%<package>\NeedsTeXFormat{LaTeX2e}[2021/06/01]
%<package>\ProvidesPackage{tracefnt}
%<package> [2020/12/22 v3.0n Standard LaTeX package (font tracing)]
% \fi
% \ProvidesFile{ltfsstrc.dtx}
-% [2020/12/22 v3.0n LaTeX Kernel (NFSS tracing)]
+% [2021/04/26 v3.0o LaTeX Kernel (NFSS tracing)]
%
% \iffalse
%<+checkmem>\CHECKMEM
@@ -423,7 +423,7 @@
% \begin{macrocode}
%<*2ekernel|latexrelease|package>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\selectfont}{Add hook to \selectfont}%
% \end{macrocode}
%
@@ -525,6 +525,14 @@
\let\delayed@f@adjustment\@empty
\fi
% \end{macrocode}
+% If the series was forced we should now cancel that in case the
+% next series change is done with some low-level setting to
+% \cs{f@series}.
+% \changes{v3.0o}{2021/04/26}{Unset the forced series boolean when reaching
+% \cs{selectfont} (gh/444)}
+% \begin{macrocode}
+ \@forced@seriesfalse
+% \end{macrocode}
% Then we generate the internal name of the font
% by concatenating {\em family}, {\em series},
% {\em shape}, and current {\em size},
@@ -610,7 +618,7 @@
% package to support rollback. In packages that works a bit
% differently and therefore we have to provide an empty block there.
% \begin{macrocode}
-%<package>\IncludeInRelease{2021/05/01}%
+%<package>\IncludeInRelease{2021/06/01}%
%<package> {\selectfont}{Add hook to \selectfont}%
%<package>\EndIncludeInRelease
% \end{macrocode}
diff --git a/Master/texmf-dist/source/latex-dev/base/lthooks.dtx b/Master/texmf-dist/source/latex-dev/base/lthooks.dtx
index 803914b4a74..45d6a78aba1 100644
--- a/Master/texmf-dist/source/latex-dev/base/lthooks.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/lthooks.dtx
@@ -31,8 +31,8 @@
%%% From File: lthooks.dtx
%
% \begin{macrocode}
-\def\lthooksversion{v1.0h}
-\def\lthooksdate{2021/01/15}
+\def\lthooksversion{v1.0m}
+\def\lthooksdate{2021/04/29}
% \end{macrocode}
%
%<*driver>
@@ -67,10 +67,13 @@
% \fi
%
%
-% \long\def\fmi#1{\begin{quote}\itshape FMi: #1\end{quote}}
-% \long\def\pho#1{\begin{quote}\itshape PhO: #1\end{quote}}
+% \providecommand\hook[1]{\texttt{#1}}
+%
+% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}}
+% \providecommand\fmiinline[1]{\begin{quote}\itshape\footnotesize FMi: #1\end{quote}}
+% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}}
+% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}}
%
-% \newcommand\hook[1]{\texttt{#1}}
%
%
% \title{The \texttt{lthooks} package\thanks{This package has version
@@ -122,14 +125,15 @@
%
% \subsection{\LaTeXe\ interfaces}
%
-% \subsubsection{Declaring hooks and using them in code}
+% \subsubsection{Declaring hooks}
%
-% 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.
+% With a few exceptions, hooks have to be declared before they can
+% be used. The exceptions are the generic hooks for commands,
+% 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 command,
+% environment or the
+% file name and so declaring them beforehand is not practical.
%
%
% \begin{function}{\NewHook}
@@ -176,6 +180,62 @@
% \end{function}
%
%
+% \subsubsection{Special declarations for hooks}
+%
+% The declarations here should normally not be used. They are available
+% to provide support for special use cases mainly involving
+% generic command hooks.
+%
+% \begin{function}{\DisableHook}
+% \begin{syntax}
+% \cs{DisableHook} \Arg{hook}
+% \end{syntax}
+% After this declaration the \meta{hook} is no longer usable: Any
+% attempt to add further code to it will result in an error and any
+% use, e.g., via \cs{UseHook}, will simply do nothing.
+%
+% This is intended to be used with generic command hooks (see
+% \texttt{ltcmdhooks-doc}) as depending on the definition of the
+% command such generic hooks may be unusable. If that is known, a
+% package developer can disable such hooks up front.
+%
+% The \meta{hook} can be specified using the dot-syntax to denote
+% the current package name. See section~\ref{sec:default-label}.
+% \end{function}
+%
+%
+% \begin{function}{\ProvideHook}
+% \begin{syntax}
+% \cs{ProvideHook} \Arg{hook}
+% \end{syntax}
+% Like \cs{NewHook} but does nothing if the hook was previously
+% declared with \cs{NewHook}. This declaration should only be used
+% in special situations, e.g., when command of another package
+% need to be altered and it is is not clear if for that command a
+% generic hook was already explicitly declared before.
+%
+% Normally \cs{NewHook} should be used instead.
+% \end{function}
+%
+% \begin{function}{\ProvideReversedHook}
+% \begin{syntax}
+% \cs{ProvideReversedHook} \Arg{hook}
+% \end{syntax}
+% Like \cs{NewReversedHook} but does nothing if the hook was
+% previously declared as a reversed hook.
+% \end{function}
+%
+% \begin{function}{\ProvideMirroredHookPair}
+% \begin{syntax}
+% \cs{ProvideMirroredHookPair} \Arg{hook-1} \Arg{hook-2}
+% \end{syntax}
+% A shorthand for
+% \cs{ProvideHook}\Arg{hook-1}\cs{ProvideReversedHook}\Arg{hook-2}.
+% \end{function}
+%
+%
+%
+% \subsubsection{Using hooks in code}
%
%
% \begin{function}{\UseHook}
@@ -201,7 +261,7 @@
% 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.,
+% the hook through a defined \cs[no-index]{\meta{addto-cmd}} command (e.g.,
% \cs{AddToHook} or \cs{AtBeginDocument}, etc.\@) would have no
% effect (as would the use of such a command inside the hook code
% itself). It is therefore customary to redefine
@@ -212,10 +272,12 @@
% 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?}
+% \fmiinline{Maybe add an error version as well?}
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
-% A leading |.| is treated literally.
+% A leading |.| is treated literally. See
+% section~\ref{sec:default-label} for details.
+
% \end{function}
%
%
@@ -264,9 +326,10 @@
% that label, the removal order takes action and the code is not
% added.
%
-% If the optional argument is \texttt{*}, then all code chunks are
+% If the optional \meta{label} 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!
+% packages one may not know about and should therefore not by used
+% by packages but only in document preambles!
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
@@ -299,7 +362,7 @@
%\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
+% then contained \cs{small}\cs{normalsize} which means two font size
% changes for no good reason.
%
% The above is only needed if one wants to typeset several quotes in a
@@ -359,10 +422,9 @@
% \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}
+% Except for \cs{UseHook}, \cs{UseOneTimeHook} and \cs{IfHookEmptyTF}
+% (and their \pkg{expl3} interfaces \cs{hook_use:n},
+% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}, all \meta{hook}
% and \meta{label} arguments are processed in the same way: first,
% spaces are trimmed around the argument, then it is fully expanded
% until only character tokens remain. If the full expansion of the
@@ -370,8 +432,8 @@
% 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
+% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, and
+% \cs{IfHookEmptyTF} are
% processed much in the same way except that spaces are not trimmed
% around the argument, for better performance.
%
@@ -425,8 +487,7 @@
% 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 \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}), because these
% conditionals are used in some performance-critical parts of the hook
% management code, and because they are usually used to refer to other
% package's hooks, so the dot-syntax doesn't make much sense.
@@ -435,7 +496,7 @@
% 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}.
+% \cs{PushDefaultHookLabel}\verb={..}=\,\ldots\cs{PopDefaultHookLabel}.
%
% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
% \begin{syntax}
@@ -651,9 +712,10 @@
% \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.
+% Hooks are a bit more complicated:
+% 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 and
+% it can also be disabled.
%
% This seemingly strange state may happen when, for example, package~$A$
% defines hook \hook{A/foo}, and package $B$ adds some code to that
@@ -664,11 +726,17 @@
% querying the existence of a hook doesn't imply its emptiness, neither
% does the other way around.
%
+% Given that code or rules can be added to a hook even if it doesn't
+% physically exist yet, means that a querying its existence has no
+% real use case (in contrast to other variables that can only be
+% update if they have already been declared). For that reason only the
+% test for emptiness has a public interface.
+%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
% doesn't need to be declared to have code added to its code pool.
% A hook is said to exist when it was declared with \cs{NewHook} or
-% some variant thereof. Generic \hook{file} and \hook{env} hooks are
+% some variant thereof. Generic hooks such as \hook{file} and \hook{env} hooks are
% automatically declared when code is added to them.
%
% \begin{function}[EXP]{\IfHookEmptyTF}
@@ -676,7 +744,8 @@
% \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
+% it using either \cs{AddToHook} or \cs{AddToHookNext}) or such code
+% was removed again (via \cs{RemoveFromHook}), and
% branches to either \meta{true code} or \meta{false code} depending
% on the result.
%
@@ -684,30 +753,7 @@
% 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}
%
@@ -822,7 +868,7 @@
% \begin{syntax}
% \cs{DebugHooksOn}
% \end{syntax}
-% Turn the debugging of hook code on or off. This displays changes
+% Turn the debugging of hook code on or off. This displays most changes
% made to the hook data structures. The output is rather coarse and
% not really intended for normal use.
% \end{function}
@@ -859,6 +905,53 @@
%
%
%
+% \begin{function}{\hook_disable:n}
+% \begin{syntax}
+% \cs{hook_disable:n} \Arg{hook}
+% \end{syntax}
+% Marks \Arg{hook} as disabled. Any further attempt to add code to
+% it or declare it, will result in an error and any call to
+% \cs{hook_use:n} will simply do nothing.
+%
+% This declaration is intended for use with generic hooks that are
+% known not to work (see \texttt{ltcmdhooks-doc}) if they receive
+% code.
+%
+% The \meta{hook} can be specified using the dot-syntax to denote
+% the current package name. See section~\ref{sec:default-label}.
+% \end{function}
+%
+% \begin{function}{\hook_provide:n}
+% \begin{syntax}
+% \cs{hook_provide:n} \Arg{hook}
+% \end{syntax}
+% Like \cs{hook_new:n} but does nothing if the hook was previously
+% declared with \cs{hook_new:n}. This declaration should only be used
+% in special situations, e.g., when a command of another package
+% needs to be altered and it is is not clear if for that command a
+% generic \hook{cmd} hook was already explicitly declared before.
+%
+% Normally \cs{hook_new:n} should be used instead.
+% \end{function}
+%
+% \begin{function}{\hook_provide_reversed:n}
+% \begin{syntax}
+% \cs{hook_provide_reversed:n} \Arg{hook}
+% \end{syntax}
+% Like \cs{hook_new_reversed:n} but does nothing if the hook was
+% previously declared as a reversed hook.
+% \end{function}
+%
+% \begin{function}{\hook_provide_pair:nn}
+% \begin{syntax}
+% \cs{hook_provide_pair:nn} \Arg{hook-1} \Arg{hook-2}
+% \end{syntax}
+% A shorthand for
+% \cs{hook_provide:n}\Arg{hook-1}\cs{hook_provide_reversed:n}\Arg{hook-2}.
+% \end{function}
+%
+%
+%
% \begin{function}{\hook_use:n}
% \begin{syntax}
% \cs{hook_use:n} \Arg{hook}
@@ -971,28 +1064,6 @@
% 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}
@@ -1262,8 +1333,8 @@
% 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
+% convention: \cs{@kernel@before@\meta{hook}} or
+% \cs{@kernel@after@\meta{hook}}. For example, in
% \cs{enddocument} you find
%\begin{verbatim}
% \UseHook{enddocument}%
@@ -1412,7 +1483,7 @@
%
% 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}}
+% via low-level calls to \cs[no-index]{\meta{env}} and \cs[no-index]{end\meta{env}}
% (e.g., to avoid the environment grouping) they are not
% available. If you want them available in code using this method,
% you would need to add them yourself, i.e., write something like
@@ -1455,6 +1526,38 @@
% \end{function}
%
%
+% \subsubsection{Generic hooks for commands}
+%
+% Similar to environments there are now (at least in theory) two
+% generic hooks available for any \LaTeX{} command. These are
+% \begin{description}
+% \item[\hook{cmd/\meta{name}/before}]
+%
+% This hook is executed at the very start of the command
+% execution.
+%
+% \item[\hook{cmd/\meta{name}/after}]
+% This hook is executed at the very end of the command body. It is
+% implemented as a reversed hook.
+% \end{description}
+% In practice there are restrictions and especially the
+% \hook{after} hook works only with a subset of commands. Details
+% about these restrictions are documented in
+% \texttt{ltcmdhooks-doc.pdf} or with code in
+% \texttt{ltcmdhooks-code.pdf}.
+%
+%
+%
+%
+% \subsubsection{Generic hooks provided by file loading operations}
+%
+% There are several hooks added to \LaTeX{}'s process of loading
+% file via its high-level interfaces such as \cs{input},
+% \cs{include}, \cs{usepackage}, \cs{RequirePackage}, etc. These
+% are documented in \texttt{ltfilehook-doc.pdf} or with code in
+% \texttt{ltfilehook-code.pdf}.
+%
+%
%
% \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}}
% \label{sec:begindocument-hooks}
@@ -1554,7 +1657,9 @@
% 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
+% this point to receive data, but since there will be no more pages you
+% need to write to it using \cs{immediate}\cs{write}). It is also the
+% correct place to
% set up any testing code to be run when the \texttt{.aux} file
% is re-read in the next step.
%
@@ -1621,7 +1726,7 @@
%
%
%
-% \subsubsection{Hooks provided \cs{shipout} operations}
+% \subsubsection{Hooks provided by \cs{shipout} operations}
% \label{sec:shipout}
%
% There are several hooks and mechanisms added to \LaTeX{}'s
@@ -1630,14 +1735,6 @@
% \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}
%
@@ -1706,27 +1803,19 @@
% \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.
+% \changes{v1.0i}{2021/03/18}
+% {Use \cs{NewModuleRelease}.}
% \begin{macrocode}
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/10/01}%
-%<latexrelease> {\NewHook}{The hook management}%
-% \end{macrocode}
-%
-% \begin{macrocode}
\ExplSyntaxOn
+%<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}
+%<latexrelease> {The~hook~management~system}
% \end{macrocode}
%
-%
% \subsection{Debugging}
%
% \begin{macro}{\g_@@_debug_bool}
@@ -1856,8 +1945,9 @@
% \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{macro}[int]{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
+% Some variants of \pkg{expl3} functions.
+% \fmiinline{should probably be moved to expl3}
% \begin{macrocode}
\cs_generate_variant:Nn \tl_gremove_once:Nn { Nx }
\cs_generate_variant:Nn \tl_show:n { x }
@@ -1949,25 +2039,30 @@
% \end{macro}
%
%
+%
% \subsection{Providing new hooks}
%
-% \begin{macro}{\g_@@_..._code_prop,\@@~...,\@@_next~...}
+% \subsubsection{The data structures of a hook}
%
-% Hooks have a \meta{name} and for each hook we have to provide a number of
-% data structures. These are
+% \DescribeMacro{\g_@@_\meta{hook}_code_prop}
+% \DescribeMacro{\@@\textvisiblespace\meta{hook}}
+% \DescribeMacro{\@@_next\textvisiblespace\meta{hook}}
+% Hooks have a name (called \meta{hook} in the description below)
+% and for each hook we have to
+% provide a number of data structures. These are
% \begin{description}
-% \item[\cs{g_@@_\meta{name}_code_prop}] A property list holding the code
+% \item[\cs{g_@@_\meta{hook}_code_prop}] A property list holding the code
% for the hook in separate chunks. The keys are by default the
% package names that add code to the hook, but it is possible
% for packages to define other keys.
%
-% \item[{\cs[no-index]{g_@@_\meta{name}_rule_\meta{label1}\string|\meta{label2}_tl}}]
+% \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}]
% A token list holding the relation between \meta{label1} and
-% \meta{label2} in the \meta{name}. The \meta{labels} are lexically
+% \meta{label2} in the \meta{hook}. The \meta{labels} are lexically
% (reverse) sorted to ensure that two labels always point to the same
-% token list. For global rules, the \meta{name} is |??|.
+% token list. For global rules, the \meta{hook} name is |??|.
%
-% \item[\cs{@@~\meta{name}}] The code that is actually executed
+% \item[\cs{@@\textvisiblespace\meta{hook}}] The code that is actually executed
% when the hook is called in the document is stored in this token
% list. It is constructed from the code chunks applying the
% information.
@@ -1976,54 +2071,194 @@
% and to make it simpler to normalize hook names in
% \cs{@@_make_name:n}.
%
-% \item[\cs{g_@@_\meta{name}_reversed_tl}] Some hooks are
+% \item[\cs{g_@@_\meta{hook}_reversed_tl}] Some hooks are
% \enquote{reversed}. This token list stores a |-| for such hook
% so that it can be identified. The |-| character is used because
% $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed
% ones.
+
+% \item[{\cs[no-index]{g_@@_\meta{hook}_declared_tl}}] This token
+% list serves as marker for the hook being officially declared. Its
+% existence is tested to raise an error in case another declaration
+% is attempted.
%
-% \item[\cs{@@_toplevel~\meta{name}}] This token list stores the code
+% \item[\cs{@@_toplevel\textvisiblespace\meta{hook}}] This token list stores the code
% inserted in the hook from the user's document, in the |top-level|
% label. This label is special, and doesn't participate in sorting.
% Instead, all code is appended to it and executed after (or before,
% if the hook is reversed) the normal
% hook code, but before the |next| code chunk.
%
-% \item[\cs{@@_next~\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}.
+% \item[\cs{@@_next\textvisiblespace\meta{hook}}] Finally there is
+% extra code (normally empty) that is used on the next invocation
+% of the hook (and then deleted). This can be used to define some
+% special behavior for a single occasion from within the document.
+% This token list follows the same naming scheme than the main
+% \cs{@@\textvisiblespace\meta{hook}} token list. It is called
+% \cs{@@_next\textvisiblespace\meta{hook}} rather than
+% \cs[no-index]{@@\textvisiblespace next_\meta{hook}} because
+% otherwise a hook whose name is |next_|\meta{hook} would clash
+% with the next code-token list of the hook called \meta{hook}.
%
% \end{description}
-% \end{macro}
%
%
+% \subsubsection{On the existence of hooks}
+% \label{sec:existence}
+%
+% A hook may be in different states of existence. Here we give an
+% overview of internal commands to set up hooks and explain how the
+% different states are distinguished. The actual implementation
+% then follows in the next sections.
+%
+% One problem we have to solve is, that we need to be able to add
+% code to hooks (e.g., with \cs{AddToHook}) even if that code has
+% not been declared yet. For example, one package needs to write
+% into a hook of another package, but that package ay not get
+% loaded or only loaded later. Another problem most hooks require
+% declaration but this is not the case for the generic hooks.
+%
+% We therefore distinguish the following states for a hook and they
+% are managed with four different tests: structure existence
+% (\cs{@@_if_structure_exist:nTF}), creation
+% (\cs{@@_if_usable:nTF}), declaration (\cs{@@_if_declared:nTF})
+% and disabled or not (\cs{@@_if_disabled:nTF})
+% \begin{description}
+% \setlist[itemize]{leftmargin=5cm,format=\cs}
+% \item[not existing]
+%
+% Nothing is known about the hook so far. This state can be
+% detected with \cs{@@_if_structure_exist:nTF}
+% (which uses the false branch).
+%
+% In this state the hook can be declared, disabled, rules can be
+% defined or code could be added to it, but it is not possible
+% to use the hook (with \cs{UseHook}).
+
+% \item[basic data structure set up]
+%
+% A hook is this state when its basic data structure has been
+% set up (using \cs{@@_init_structure:n}). The data structure setup happens
+% automatically when commands such as \cs{AddToHook} are used
+% and the hook is at that point in state \enquote{not existing}.
+%
+% In this state the four tests give the following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |false|.
+% \item [@@_if_declared:nTF] returns |false|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+% The allowed acctions are the same as in the \enquote{not
+% existing} state.
+%
+% \item[declared]
+%
+% A hook is in this state it is not disabled and was explicity declared (e.g.,
+% with \cs{NewHook}). In this case the four tests give the
+% following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |true|.
+% \item [@@_if_declared:nTF] returns |true|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+% \item[usable]
+%
+% A hook is in this state if it is not disabled, was not
+% explicitly declared but nevertheless is allowed to be used
+% (with \cs{UseHook} or \cs{hook_use:n}). This state is only
+% possible for generic hooks as they do not need to be
+% declared. Therefore such hooks move directly from state
+% \enquote{not existing} to \enquote{usable} the moment a
+% declaration such as \cs{AddToHook} wants to add to the hook
+% data structure. In this state the tests give the following
+% results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |true|.
+% \item [@@_if_declared:nTF] returns |false|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+%
+% \item[disabled]
+%
+% A hook in any state is moved to this state when
+% \cs{DisableHook} is used. This changes the tests to give the
+% following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] \emph{unchanged}.
+% \item [@@_if_usable:nTF] returns |false|.
+% \item [@@_if_declared:nTF] returns |true|.
+% \item [@@_if_disabled:nTF] returns |true|.
+% \end{itemize}
+% The structure test is unchanged (if the hook was unknown before it is
+% false, otherwise true). The usable test returns false so that
+% any \cs{UseHook} will bypass the hook from now on. The
+% declared test returns true so that any further \cs{NewHook}
+% generates an error and the disabled test returns true so that
+% \cs{AddToHook} can return an error.
+% \fmiinline{maybe it should do this only after begin document?}
+%
+% \end{description}
+%
+%
+%
+%
+% \subsubsection{Setting hooks up}
%
%
-% \begin{macro}{\hook_new:n}
-% The \cs{hook_new:n} declaration declare a new hook and expects
+% \begin{macro}{\hook_new:n,\@@_new:n}
+% The \cs{hook_new:n} declaration declares 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} }
+% \end{macrocode}
+%
+% \begin{macrocode}
\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.
+% We check if the hook was already \emph{explicitly} declared with
+% \cs{hook_new:n}, and if it already exists we complain, otherwise set
+% the \enquote{created} flag for the hook so that it errors next time
+% \cs{hook_new:n} is used.
+% \begin{macrocode}
+ \@@_if_declared:nTF {#1}
+ { \__kernel_msg_error:nnn { hooks } { exists } {#1} }
+ {
+ \tl_new:c { g_@@_#1_declared_tl }
+ \@@_make_usable:n {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_make_usable:n}
+%
+% This initializes all hook data structures for the hook but if
+% used on its own doesn't mark the hook as declared (as
+% \cs{hook_new:n} does, so a later \cs{hook_new:n} on that hook will
+% not result in an error. This command is internally used by
+% \cs{hook_gput_code:n} when adding code to a generic hook.
% \begin{macrocode}
- \hook_if_exist:nTF {#1}
- { \msg_error:nnn { hooks } { exists } {#1} }
+\cs_new_protected:Npn \@@_make_usable:n #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.
+% Now we check if the hook's data structure can be safely created
+% without \pkg{expl3} raising errors, then
+% we add the hook name to the list of all hooks and
+% allocate the necessary data structures for the new hook,
+% otherwise just do nothing.
% \begin{macrocode}
+ \tl_if_exist:cF { @@~#1 }
{
\seq_gput_right:Nn \g_@@_all_seq {#1}
% \end{macrocode}
@@ -2034,12 +2269,12 @@
% \end{macrocode}
% Now ensure that the base data structure for the hook exists:
% \begin{macrocode}
- \@@_declare:n {#1}
+ \@@_init_structure: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}
+ \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
@@ -2061,23 +2296,30 @@
%
% \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{macro}{\@@_init_structure:n}
+% This function declares the basic data structures for a hook
+% without explicit declaring the hook itself. This is needed to
+% allow adding to undeclared hooks. Here it is unnecessary to
+% check whether all variables exist, since all three are declared
+% at the same time (either all of them exist, or none).
+%
+% It creates the hook code pool
+% (\cs[no-index]{g_@@_\meta{hook}_code_prop}) and the |top-level|
+% and |next| token lists. A hook is initialized with
+% \cs{@@_init_structure:n} the first time anything is added to it.
+% Inizializing a hook just with \cs{@@_init_structure:n} will not
+% make it usable with \cs{hook_use:n}.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_declare:n #1
+\cs_new_protected:Npn \@@_init_structure:n #1
{
- \@@_if_exist:nF {#1}
+ \@@_if_structure_exist:nF {#1}
{
\prop_new:c { g_@@_#1_code_prop }
\tl_new:c { @@_toplevel~#1 }
@@ -2109,6 +2351,9 @@
% \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}
@@ -2159,6 +2404,140 @@
%
%
%
+% \subsubsection{Disabling and providing hooks}
+%
+% \begin{macro}{\hook_disable:n}
+% \begin{macro}{\@@_disable:n}
+% \begin{macro}[pTF]{\@@_if_disabled:n}
+%
+% Disables a hook by creating its
+% \cs[no-index]{g_@@_\meta{hook}_declared_tl} so that the hook
+% errors when used with \cs{hook_new:n}, then it undefines
+% \cs{@@\textvisiblespace\meta{hook}} so that it may not be executed.
+%
+% This does not clear any code that may be already stored in the
+% hook's structure, but doesn't allow adding more code.
+% \cs{@@_if_disabled:nTF} uses that specific combination to check
+% if the hook is disabled.
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_disable:n}{Disable~hooks}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_disable:n #1
+ { \@@_normalize_hook_args:Nn \@@_disable:n {#1} }
+\cs_new_protected:Npn \@@_disable:n #1
+ {
+ \tl_gclear_new:c { g_@@_#1_declared_tl }
+ \cs_undefine:c { @@~#1 }
+ }
+\prg_new_conditional:Npnn \@@_if_disabled:n #1 { p, T, F, TF }
+ {
+ \bool_lazy_and:nnTF
+ { \tl_if_exist_p:c { g_@@_#1_declared_tl } }
+ { ! \tl_if_exist_p:c { @@~#1 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_disable:n}{Disable~hooks}
+%<latexrelease>
+%<latexrelease>\cs_new_protected:Npn \hook_disable:n #1 {}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hook_provide:n}
+% \begin{macro}{\hook_provide_reversed:n}
+% \begin{macro}{\@@_provide:n}
+% The \cs{hook_provide:n} declaration declares a new hook if it
+% wasn't declared already, in which case it only checks that the
+% already existing hook is not a reversed hook.
+% The \cs{hook_provide_reversed:n} does the same for reversed hooks.
+% \hook{begindocument}.
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_provide:n #1
+ { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { } }
+\cs_new_protected:Npn \hook_provide_reversed:n #1
+ { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { - } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_provide:nn #1 #2
+ {
+% \end{macrocode}
+% If the hook to be provided was disabled we warn (for now --- this
+% may change).
+% \begin{macrocode}
+ \@@_if_disabled:nTF {#1}
+ { \__kernel_msg_warning:nnn { hooks } { provide-disabled } {#1} }
+% \end{macrocode}
+% Otherwise we check if it was already declared.
+% \begin{macrocode}
+ {
+ \@@_if_declared:nTF {#1}
+ {
+% \end{macrocode}
+% Issue an error if we try to provide a a hook that is reversed and
+% the already existing one is not (or vice versa).
+% \begin{macrocode}
+ \str_if_eq:eeF { \tl_use:c { g_@@_#1_reversed_tl } } {#2}
+ { \__kernel_msg_error:nnn { hooks } { provide-error } {#1} }
+ }
+% \end{macrocode}
+% If it wasn't declared, we declared as a normal or reversed hook
+% as appropriate.
+% \begin{macrocode}
+ {
+ \tl_new:c { g_@@_#1_declared_tl }
+ \@@_make_usable:n {#1}
+ \tl_gset:cn { g_@@_#1_reversed_tl } {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hook_provide_pair:nn}
+% A shorthand for providing a normal and a (matching) reversed hook in one go.
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_provide_pair:nn #1#2
+ { \hook_provide:n {#1} \hook_provide_reversed:n {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+%<latexrelease>
+%<latexrelease>\cs_new_protected:Npn \hook_provide_reversed:n #1 {}
+%<latexrelease>\cs_new_protected:Npn \hook_provide:n #1 {}
+%<latexrelease>\cs_new_protected:Npn \hook_provide_pair:nn #1#2 {}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+%
% \subsection{Parsing a label}
%
% \begin{macro}[EXP]{\@@_parse_label_default:n}
@@ -2193,7 +2572,7 @@
{
\tl_if_empty:nTF {#1}
{
- \msg_expandable_error:nn { hooks } { empty-label }
+ \__kernel_msg_expandable_error:nn { hooks } { empty-label }
\@@_currname_or_default:
}
{
@@ -2230,7 +2609,7 @@
{
\tl_if_empty:NTF \@currname
{
- \msg_expandable_error:nnn { hooks } { should-not-happen }
+ \__kernel_msg_expandable_error:nnn { hooks } { should-not-happen }
{ Empty~default~label. }
\@@_make_name:n { label-missing }
}
@@ -2247,10 +2626,10 @@
% 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
+% inside \cs{expanded}. The macro adds the \cs[no-index]{@@\textvisiblespace} prefix to the
% hook name to reuse the hook's code token list to build the csname
% and avoid leaving \enquote{public} control sequences defined
-% (as~\cs{relax}) in TeX's memory.
+% (as~\cs[no-index]{relax}) in TeX's memory.
% \begin{macrocode}
\cs_new:Npn \@@_make_name:n #1
{
@@ -2310,6 +2689,9 @@
% \end{macro}
%
%
+%
+% \subsection{Adding or removing hook code}
+%
% \begin{macro}{\hook_gput_code:nnn}
% \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn}
%
@@ -2319,6 +2701,9 @@
% \begin{macrocode}
\cs_new_protected:Npn \hook_gput_code:nnn #1 #2
{ \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
{
% \end{macrocode}
@@ -2342,7 +2727,7 @@
% If no removal is queued, we are free to add. Start by checking if
% the hook exists.
% \begin{macrocode}
- \hook_if_exist:nTF {#1}
+ \@@_if_usable:nTF {#1}
% \end{macrocode}
% If so we simply add (or append) the new code to the property list
% holding different chunks for the hook. At \verb=\begin{document}=
@@ -2363,10 +2748,17 @@
% 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} }
+ {
+ \@@_if_disabled:nTF {#1}
+ { \__kernel_msg_error:nnn { hooks } { hook-disabled } {#1} }
+ { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} }
+ }
}
}
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_generate_variant:Nn \@@_gput_code:nnn { nxv }
% \end{macrocode}
%
@@ -2378,7 +2770,7 @@
% However, first some debugging info if debugging is enabled:
% \begin{macrocode}
\@@_debug:n{\iow_term:x{****~ Add~ to~
- \hook_if_exist:nF {#1} { undeclared~ }
+ \@@_if_usable:nF {#1} { undeclared~ }
hook~ #1~ (#2)
\on@line\space <-~ \tl_to_str:n{#3}} }
% \end{macrocode}
@@ -2386,9 +2778,9 @@
% 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
+% \cs{@@_toplevel\textvisiblespace\meta{hook}} that goes at the end of the
% hook (or at the beginning, for a reversed hook), just before
-% \cs[no-index]{@@_next~\meta{hook}}.
+% \cs[no-index]{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} { top-level }
{
@@ -2396,12 +2788,12 @@
{
% \end{macrocode}
% If the hook's basic structure does not exist, we need to declare it
-% with \cs{@@_declare:n}.
+% with \cs{@@_init_structure:n}.
% \begin{macrocode}
- \@@_declare:n {#1}
+ \@@_init_structure:n {#1}
\@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
}
- { \msg_error:nnn { hooks } { misused-top-level } {#1} }
+ { \__kernel_msg_error:nnn { hooks } { misused-top-level } {#1} }
}
{
\prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
@@ -2423,14 +2815,15 @@
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
{
- \@@_declare:n {#1}
+ \@@_init_structure: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}
+% \begin{macro}{\@@_try_declaring_generic_hook:nnn,
+% \@@_try_declaring_generic_next_hook:nn}
+%
% These entry-level macros just pass the arguments along to the
% common \cs{@@_try_declaring_generic_hook:nNNnn} with the right
% functions to execute when some action is to be taken.
@@ -2457,13 +2850,14 @@
\hook_gput_next_code:nn \@@_gput_next_do:nn
}
% \end{macrocode}
+% \end{macro}
%
-% \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
+%
+% \begin{macro}{\@@_try_declaring_generic_hook:nNNnn,
+% \@@_try_declaring_generic_hook_split:nNNnn}
+%
+% \cs{@@_try_declaring_generic_hook:nNNnn}
+% now splits the hook name
% at the first \texttt{/} (if any) and first checks if it is a
% file-specific hook (they require some normalization) using
% \cs{@@_if_file_hook:wTF}. If not then check it is one of a
@@ -2481,12 +2875,24 @@
}
{ \@@_try_declaring_generic_hook_split:nNNnn {#1} }
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\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}
}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks}
\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
#1 / #2 / #3 / #4 \scan_stop: #5 { TF }
{
@@ -2495,31 +2901,70 @@
{
\prop_if_in:NnTF \c_@@_generics_prop {#1}
{
- \hook_if_exist:nF {#5} { \hook_new:n {#5} }
+ \@@_if_usable:nF {#5}
+ {
+% \end{macrocode}
+% If the hook doesn't exist yet we check if it is a \texttt{cmd}
+% hook and if so we attempt patching the command in addition to
+% declaring the hook.
+%
+% For some commands this will not be possible, in which case
+% \cs{@@_patch_cmd_or_delay:Nnn} (defined in \texttt{ltcmdhooks})
+% will generate an appropriate error message.
+% \begin{macrocode}
+ \str_if_eq:nnT {#1} { cmd }
+ { \@@_try_put_cmd_hook: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.
+%
+% Declare the hook always even if it can't really be used (error
+% message generated elsewhere).
+%
+% Here we use \cs{@@_make_usable:n}, so that a \cs{hook_new:n} is still
+% possible later.
% \begin{macrocode}
+ \@@_make_usable:n {#5}
+ }
\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: }
}
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}%
+%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks}
+%<latexrelease>
+%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
+%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:nTF {#2}
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> {
+%<latexrelease> \prop_if_in:NnTF \c_@@_generics_prop {#1}
+%<latexrelease> {
+%<latexrelease> \@@_if_declared:nF {#5} { \hook_new:n {#5} }
+%<latexrelease> \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
+%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
+%<latexrelease> {
+%<latexrelease> \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
+%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
+%<latexrelease> }
+%<latexrelease> \prg_return_true:
+%<latexrelease> }
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_file_hook:w}
@@ -2528,7 +2973,7 @@
% |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}}.
+% A file-specific hook is \hook{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
@@ -2594,14 +3039,16 @@
% 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{cmd}]
+% The generic hooks used for commands.
% \item[\texttt{env}]
% The generic hooks used in \cs{begin} and \cs{end}.
-% \item[\texttt{file}]
+% \item[\texttt{file}, \texttt{package}, \texttt{class}, \texttt{include}]
% 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=}
+ {cmd=,env=,file=,package=,class=,include=}
% \end{macrocode}
% \end{macro}
%
@@ -2628,11 +3075,11 @@
\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
{
% \end{macrocode}
-% First check that the hook code pool exists. \cs{hook_if_exist:nTF}
+% First check that the hook code pool exists. \cs{@@_if_usable:nTF}
% isn't used here because it should be possible to remove code from a
% hook before its defined (see section~\ref{sec:querying}).
% \begin{macrocode}
- \@@_if_exist:nTF {#1}
+ \@@_if_structure_exist:nTF {#1}
{
% \end{macrocode}
% Then remove the chunk and run \cs{@@_update_hook_code:n} so
@@ -2640,9 +3087,9 @@
% \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}}.
+% \cs{g_@@_\meta{hook}_code_prop}, the top-level code
+% \cs{@@_toplevel\textvisiblespace\meta{hook}}, and the next-execution code
+% \cs{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} {*}
{
@@ -2655,7 +3102,7 @@
% 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
+% was added to \cs{@@_toplevel\textvisiblespace\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
@@ -2679,7 +3126,7 @@
% \end{macrocode}
% Finally update the code, if the hook exists.
% \begin{macrocode}
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{ \@@_update_hook_code:n {#1} }
}
% \end{macrocode}
@@ -2774,10 +3221,10 @@
% 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}
+% lot, the code 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
+% \cs{@@\textvisiblespace??} 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}
@@ -2798,14 +3245,12 @@
%
% \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.
+% hook, which sets a default rule (to be used if no other relation
+% between the two hooks exist).
% \begin{macrocode}
\cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4
{
@@ -2813,14 +3258,14 @@
{#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}
+ \@@_init_structure:n {#1}
% \end{macrocode}
% Then we clear any previous relationship between both labels.
% \begin{macrocode}
@@ -2834,7 +3279,7 @@
{#1} {#2} {#4}
\@@_update_hook_code:n {#1}
}
- { \msg_error:nnnnnn { hooks } { unknown-rule }
+ { \__kernel_msg_error:nnnnnn { hooks } { unknown-rule }
{#1} {#2} {#3} {#4} }
}
% \end{macrocode}
@@ -2938,7 +3383,7 @@
{
\if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f:
\prg_return_true:
- \else
+ \else:
\prg_return_false:
\fi:
}
@@ -3038,13 +3483,13 @@
% 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
+% case just update \cs{@@\textvisiblespace\meta{hook}} to hold the |top-level| and
% |next| code chunks. If there are code chunks we call
% \cs{@@_initialize_single:NNn} and pass to it ready made csnames
% as they are needed several times inside. This way we save a bit
% on processing time if we do that up front.
% \begin{macrocode}
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{
\prop_if_empty:cTF {g_@@_#1_code_prop}
{
@@ -3097,7 +3542,7 @@
% \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}
+% \fmiinline{note to myself: verify}
% \begin{macrocode}
\cs_new:Npn \@@_tl_csname:n #1 { l_@@_label_#1_tl }
\cs_new:Npn \@@_seq_csname:n #1 { l_@@_label_#1_seq }
@@ -3161,7 +3606,7 @@
% 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}
+% checking for, just in case ... maybe}
%
% ^^A #1 <- \@@~#1
% ^^A #2 <- \g_@@_#1_labels_clist
@@ -3302,7 +3747,7 @@
% \end{macrocode}
%
% This is not really the information one needs in the error case
-% but will do for now \ldots \fmi{fix}
+% but will do for now \ldots \fmi{improve output on a rainy day}
% \begin{macrocode}
\@@_debug_label_data:N \l_@@_work_prop
\iow_term:x{====================}
@@ -3442,7 +3887,7 @@
\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 }
+ \__kernel_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}
@@ -3450,7 +3895,7 @@
\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 }
+ \__kernel_msg_warning:nnnnnn { hooks } { labels-incompatible }
{#1} {#2} {#3} { 0 }
}
% \end{macrocode}
@@ -3573,15 +4018,20 @@
{ \@@_log_cmd:x { >~#1 } }
\cs_new_protected:Npn \@@_log_line_indent:x #1
{ \@@_log_cmd:x { >~\@spaces #1 } }
+% \end{macrocode}
+%
+% \begin{macrocode}
\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! } }
+ \@@_if_usable:nF {#1}
+ { \@@_log_line:x { The~hook~is~not~declared. } }
+ \@@_if_disabled:nT {#1}
+ { \@@_log_line:x { The~hook~is~disabled. } }
\hook_if_empty:nTF {#1}
{ #2 { The~hook~is~empty } }
{
@@ -3599,7 +4049,7 @@
\@@_log_line:x
{
Document-level~(top-level)~code
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{ ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
}
\@@_log_line_indent:x
@@ -3650,7 +4100,7 @@
% to that hook) and not empty
% \begin{macrocode}
\bool_lazy_and:nnTF
- { \hook_if_exist_p:n {#1} }
+ { \@@_if_usable_p:n {#1} }
{ ! \hook_if_empty_p:n {#1} }
{
\@@_log_line:x
@@ -3672,10 +4122,12 @@
}
}
{
+ \@@_log_line:x { Execution~order: }
#2
{
- Hook~ \hook_if_exist:nTF {#1}
- {code~pool~empty} {not~declared}
+ \@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1}
+ { code~pool~is~empty }
+ { is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} }
}
}
}
@@ -3767,25 +4219,37 @@
% \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} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_gput_next_code:nn,
+% \@@_gput_next_do:nn,
+% \@@_gput_next_do:Nnn,
+% \@@_clear_next:n}
+% \begin{macrocode}
\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} }
+ \@@_if_disabled:nTF {#1}
+ { \__kernel_msg_error:nnn { hooks } { hook-disabled } {#1} }
+ {
+ \@@_init_structure:n {#1}
+ \@@_if_usable:nTF {#1}
+ { \@@_gput_next_do:nn {#1} {#2} }
+ { \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
+ }
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_do:nn #1
{
\exp_args:Nc \@@_gput_next_do:Nnn
@@ -3814,7 +4278,11 @@
{ \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
% \end{macrocode}
% \end{macro}
-% \end{macro}
+%
+%
+%
+%
+%
%
% \subsection{Using the hook}
%
@@ -3841,7 +4309,7 @@
% 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
+% we do not generate a \cs[no-index]{relax} as a side effect of checking for a
% csname. In contrast to the \TeX{} low-level
% \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is
% careful to avoid this.
@@ -3878,7 +4346,7 @@
% \end{macro}
%
% \begin{macro}[EXP]{\@@_use:wn}
-% \begin{macro}{\@@_try_file_hook:n,\@@_if_exist_use:n}
+% \begin{macro}{\@@_try_file_hook:n,\@@_if_usable_use:n}
% \cs{@@_use:wn} does a quick check to test if the current hook is a
% file hook: those need a special treatment. If it is not, the hook
% does not exist. If it is, then \cs{@@_try_file_hook:n} is called,
@@ -3888,7 +4356,7 @@
%
% 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{@@_if_usable_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
@@ -3897,16 +4365,22 @@
{ \@@_try_file_hook:n {#3} }
{ } % Hook doesn't exist
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\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 \@@_if_usable_use:n
{ \exp_args:Ne \@@_file_hook_normalize:n {#1} }
}
- { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before)
+ { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before)
}
-\cs_new_protected:Npn \@@_if_exist_use:n #1
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_usable_use:n #1
{
\tl_if_exist:cT { @@~#1 }
{
@@ -3929,7 +4403,6 @@
% 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
{
@@ -3953,9 +4426,10 @@
% 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.
+% Hooks are a bit more complicated: they have several other states as
+% discussed in \ref{sec:existence}.
% A hook may exist or not, and either way it may or may not be empty
-% (even a hook that doesn't exist may be non-empty).
+% (even a hook that doesn't exist may be non-empty) or may be disabled.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
@@ -3965,19 +4439,19 @@
% 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.
-%
+% All other states can only be queried with internal tests as the
+% different states are irrelevant for package code.
+
% \begin{macro}[pTF]{\hook_if_empty:n}
% Test if a hook is empty (that is, no code was added to that hook).
% A \meta{hook} being empty means that all three of its
-% \cs[no-index]{g_@@_\meta{hook}_code_prop}, its
-% \cs[no-index]{@@_toplevel~\meta{hook}} and its
-% \cs[no-index]{@@_next~\meta{hook}} are empty.
+% \cs{g_@@_\meta{hook}_code_prop}, its
+% \cs{@@_toplevel\textvisiblespace\meta{hook}} and its
+% \cs{@@_next\textvisiblespace\meta{hook}} are empty.
% \begin{macrocode}
\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
{
- \@@_if_exist:nTF {#1}
+ \@@_if_structure_exist:nTF {#1}
{
\bool_lazy_and:nnTF
{ \prop_if_empty_p:c { g_@@_#1_code_prop } }
@@ -3994,8 +4468,9 @@
% \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
+%
+% \begin{macro}[pTF]{\@@_if_usable:n}
+% A hook is usable if the
% token list that stores the sorted code for that hook,
% \cs[no-index]{@@~\meta{hook}}, exists. The property list
% \cs[no-index]{g_@@_\meta{hook}_code_prop} cannot be used here
@@ -4003,7 +4478,7 @@
% 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 }
+\prg_new_conditional:Npnn \@@_if_usable:n #1 { p , T , F , TF }
{
\tl_if_exist:cTF { @@~#1 }
{ \prg_return_true: }
@@ -4012,13 +4487,15 @@
% \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
+% \begin{macro}[pTF]{\@@_if_structure_exist:n}
+%
+% An internal check if the hook has already its basic internal
+% structure set up with
+% \cs{@@_init_structure:n}. This means that the hook was already used somehow
% (a code chunk or rule was added to it), but it still wasn't declared
% with \cs{hook_new:n}.
% \begin{macrocode}
-\prg_new_conditional:Npnn \@@_if_exist:n #1 { p , T , F , TF }
+\prg_new_conditional:Npnn \@@_if_structure_exist:n #1 { p , T , F , TF }
{
\prop_if_exist:cTF { g_@@_#1_code_prop }
{ \prg_return_true: }
@@ -4026,6 +4503,22 @@
}
% \end{macrocode}
% \end{macro}
+%
+%
+%
+% \begin{macro}[pTF]{\@@_if_declared:n}
+%
+% Internal test to check if the hook was officially declared with
+% \cs{hook_new:n} or a variant.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_declared:n #1 { p, T, F, TF }
+ {
+ \tl_if_exist:cTF { g_@@_#1_declared_tl }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_reversed:n}
% An internal conditional that checks if a hook is reversed.
@@ -4045,10 +4538,10 @@
% \subsection{Messages}
%
% \begin{macrocode}
-\msg_new:nnnn { hooks } { labels-incompatible }
+\__kernel_msg_new:nnnn { hooks } { labels-incompatible }
{
- Labels~`#1'~and~`#2'~are~incompatible
- \str_if_eq:nnF {#3} {??} { ~in~hook~`#3' } .~
+ 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. }
@@ -4058,23 +4551,34 @@
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnnn { hooks } { exists }
- { Hook~`#1'~ has~ already~ been~ declared. }
+\__kernel_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}
+\__kernel_msg_new:nnnn { hooks } { hook-disabled }
+ { Cannot~add~code~to~disabled~hook~'#1'. }
+ {
+ The~hook~'#1'~you~tried~to~add~code~to~was~previously~disabled~
+ with~\iow_char:N\\hook_disable:n~or~\iow_char:N\\DisableHook,~so~
+ it~cannot~have~code~added~to~it.
+ }
+% \end{macrocode}
+%
% \begin{macrocode}
-\msg_new:nnn { hooks } { empty-label }
+\__kernel_msg_new:nnn { hooks } { empty-label }
{
Empty~code~label~\msg_line_context:.~
- Using~`\@@_currname_or_default:'~instead.
+ Using~'\@@_currname_or_default:'~instead.
}
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { no-default-label }
+\__kernel_msg_new:nnn { hooks } { no-default-label }
{
Missing~(empty)~default~label~\msg_line_context:. \\
This~command~was~ignored.
@@ -4082,25 +4586,25 @@
% \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' }. ~
+\__kernel_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'.
+ 'before'~ or~ '<',~
+ 'after'~ or~ '>',~
+ 'incompatible-warning',~
+ 'incompatible-error',~
+ 'voids'~ or~
+ 'unrelated'.
}
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnnn { hooks } { misused-top-level }
+\__kernel_msg_new:nnnn { hooks } { misused-top-level }
{
Illegal~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
'top-level'~is~reserved~for~the~user's~document.
@@ -4114,16 +4618,16 @@
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { set-top-level }
+\__kernel_msg_new:nnn { hooks } { set-top-level }
{
- You~cannot~change~the~default~label~#1~`top-level'.~Illegal \\
+ 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 }
+\__kernel_msg_new:nnn { hooks } { ddhl-deprecated }
{
\iow_char:N \\DeclareDefaultHookLabel~is~deprecated.\\
Use~\iow_char:N \\SetDefaultHookLabel~instead.\\ \\
@@ -4132,26 +4636,55 @@
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { extra-pop-label }
+\__kernel_msg_new:nnn { hooks } { extra-pop-label }
{
Extra~\iow_char:N \\PopDefaultHookLabel. \\
This~command~will~be~ignored.
}
-\msg_new:nnn { hooks } { missing-pop-label }
+\__kernel_msg_new:nnn { hooks } { missing-pop-label }
{
Missing~\iow_char:N \\PopDefaultHookLabel. \\
- The~label~`#1'~was~pushed~but~never~popped.~Something~is~wrong.
+ The~label~'#1'~was~pushed~but~never~popped.~Something~is~wrong.
}
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { should-not-happen }
+\__kernel_msg_new:nnn { hooks } { should-not-happen }
{
ERROR!~This~should~not~happen.~#1 \\
Please~report~at~https://github.com/latex3/latex2e.
}
% \end{macrocode}
%
+%
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnn { hooks } { provide-disabled }
+ {
+ Can't~ provide~ hook~ '#1'~ because~ it~ is~ disabled!
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { hooks } { provide-error }
+ {
+ Hook~'#1'~ already~ declared~ as~ a~
+ \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook!
+ }
+ {
+ You~ attempted~ to~ provide~ the~ hook~'#1'~ as~ a~
+ \@@_if_reversed:nTF {#1} { normal } { reversed }~ hook,~ but~ it~
+ was~ already~ previously~ declared~ as~ a~
+ \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook.~
+ A~ redeclaration~ is~ not~ possible.
+ }
+% \end{macrocode}
+%
+%
+%
+%
+%
+%
% \subsection{\LaTeXe{} package interface commands}
%
%
@@ -4165,6 +4698,44 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+% \end{macrocode}
+%
+% \begin{macro}{\ProvideHook,\ProvideReversedHook,\ProvideMirroredHookPair}
+% Providing new hooks \ldots
+% \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.}
+% \begin{macrocode}
+\NewDocumentCommand \ProvideHook { m }{ \hook_provide:n {#1} }
+\NewDocumentCommand \ProvideReversedHook { m }{ \hook_provide_reversed:n {#1} }
+\NewDocumentCommand \ProvideMirroredHookPair { mm }{ \hook_provide_pair:nn {#1}{#2} }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\DisableHook}
+% Disabling a (generic) hook.
+% \begin{macrocode}
+\NewDocumentCommand \DisableHook { m }{ \hook_disable:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+%<latexrelease>
+%<latexrelease>\def \ProvideHook#1{}
+%<latexrelease>\def \ProvideReversedHook#1{}
+%<latexrelease>\def \ProvideMirroredHookPair#1#2{}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+%
% \begin{macro}{\AddToHook}
%
% \begin{macrocode}
@@ -4194,6 +4765,10 @@
% \begin{macro}{\PushDefaultHookLabel}
% \begin{macro}{\PopDefaultHookLabel}
% \begin{macro}{\DeclareDefaultHookLabel}
+%
+% \fmiinline{Docu task: At some point this code for this should be moved to the
+% label section earlier and here we should keep only the interface commands.}
+%
% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n}
% \begin{macro}{\@@_curr_name_pop:}
% \begin{macro}{\@@_end_document_label_check:}
@@ -4217,8 +4792,11 @@
% the stack to support roll forward. But in some rare cases,
% \pkg{latexrelease} may be loaded inside another package (notably
% \pkg{platexrelease}), so we'll first push the |top-level| entry:
+% \changes{v1.0i}{2021/03/18}
+% {Only add \texttt{top-level} if not already there.}
% \begin{macrocode}
-%<latexrelease>\seq_gput_right:Nn \g_@@_name_stack_seq { top-level }
+%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq
+%<latexrelease> { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } }
% \end{macrocode}
% then we dissect the \cs{@currnamestack}, adding \cs{@currname} to
% the stack:
@@ -4234,9 +4812,12 @@
%<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}:
+% and finally set the default label to be the \cs{@currname}:
+% \changes{v1.0i}{2021/03/18}
+% {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.}
% \begin{macrocode}
%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
+%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl
% \end{macrocode}
%
% Two commands keep track of the stack: when a file is input,
@@ -4248,11 +4829,11 @@
\cs_new_protected:Npn \@@_curr_name_push_aux:n #1
{
\tl_if_blank:nTF {#1}
- { \msg_error:nn { hooks } { no-default-label } }
+ { \__kernel_msg_error:nn { hooks } { no-default-label } }
{
\str_if_eq:nnTF {#1} { top-level }
{
- \msg_error:nnnnn { hooks } { set-top-level }
+ \__kernel_msg_error:nnnnn { hooks } { set-top-level }
{ to } { PushDefaultHookLabel } {#1}
}
{
@@ -4270,12 +4851,12 @@
{
\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 } }
+ { \__kernel_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:}
+% \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:}
% (not a critical error, but it might indicate that something else is
% not quite right):
% \begin{macrocode}
@@ -4285,7 +4866,7 @@
{
\seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl
{
- \msg_error:nnx { hooks } { missing-pop-label }
+ \__kernel_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:
@@ -4303,7 +4884,7 @@
{
\seq_if_empty:NTF \g_@@_name_stack_seq
{
- \msg_error:nnnnn { hooks } { set-top-level }
+ \__kernel_msg_error:nnnnn { hooks } { set-top-level }
{ for } { SetDefaultHookLabel } {#1}
}
{ \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } }
@@ -4312,14 +4893,14 @@
{
\str_if_eq:nnTF {#1} { top-level }
{
- \msg_error:nnnnn { hooks } { set-top-level }
+ \__kernel_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 }
+ \__kernel_msg_error:nn { hooks } { ddhl-deprecated }
\SetDefaultHookLabel {#1}
}
% \end{macrocode}
@@ -4414,7 +4995,7 @@
% \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?}
+% \fmiinline{Needs perhaps an L3 interface, or maybe it should get dropped?}
% \begin{macrocode}
\NewDocumentCommand \ClearHookRule { m m m }
{ \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} }
@@ -4422,19 +5003,33 @@
% \end{macro}
%
%
-% \begin{macro}[EXP]{\IfHookExistsTF,\IfHookEmptyTF}
+% \begin{macro}[EXP]{\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}
%
+% \begin{macro}[EXP]{\IfHookExistsTF}
+% Marked for removal and no longer documented in the doc section!
+% \phoinline{\cs{IfHookExistsTF} is used in \texttt{jlreq.cls},
+% \texttt{pxatbegshi.sty}, \texttt{pxeverysel.sty},
+% \texttt{pxeveryshi.sty}, so the public name may be an alias of the
+% internal conditional for a while. Regardless, those packages' use for
+% \cs{IfHookExistsTF} is not really correct and can be changed.}
+% \begin{macrocode}
+\cs_new_eq:NN \IfHookExistsTF \@@_if_usable:nTF
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
%
% \subsection{Internal commands needed elsewhere}
%
@@ -4463,12 +5058,6 @@
% \InternalDetectionOn
% \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
@@ -4476,14 +5065,15 @@
% \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed
% to be for \cs{AddToHook} vice versa (gh/401)}
% \begin{macrocode}
-%</2ekernel|latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%
%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\NewHook}{The hook management}%
+%<latexrelease> {lthooks}{The~hook~management}%
+%<latexrelease>
+%<latexrelease>\def \NewHook#1{}
+%<latexrelease>\def \NewReversedHook#1{}
+%<latexrelease>\def \NewMirroredHookPair#1#2{}
%<latexrelease>
-%<latexrelease>\def\NewHook#1{}
-%<latexrelease>\def\NewReversedHook#1{}
-%<latexrelease>\def\NewMirroredHookPair#1#2{}
+%<latexrelease>\def \DisableHook #1{}
%<latexrelease>
%<latexrelease>\long\def\AddToHookNext#1#2{}
%<latexrelease>
@@ -4511,7 +5101,9 @@
%<latexrelease>\long\def \IfHookExistsTF #1#2#3{#3}
%<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2}
%<latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\EndModuleRelease
+\ExplSyntaxOff
+%</2ekernel|latexrelease>
% \end{macrocode}
%
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx
index e3fa9f93437..3b9a3377312 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltluatex.dtx
@@ -28,7 +28,7 @@
\ProvidesFile{ltluatex.dtx}
%</driver>
%<*tex>
-[2021/01/15 v1.1s
+[2021/04/18 v1.1t
%</tex>
%<plain> LuaTeX support for plain TeX (core)
%<*tex>
@@ -1412,6 +1412,7 @@ local callbacktypes = callbacktypes or {
% \changes{v1.1j}{2019/06/18}{make\_extensible added}
% \changes{v1.1j}{2019/06/18}{font\_descriptor\_objnum\_provider added}
% \changes{v1.1l}{2020/02/02}{glyph\_info added}
+% \changes{v1.1t}{2021/04/18}{input\_level\_string added}
% \begin{macrocode}
define_font = exclusive,
glyph_info = exclusive,
@@ -1419,6 +1420,7 @@ local callbacktypes = callbacktypes or {
glyph_stream_provider = exclusive,
make_extensible = exclusive,
font_descriptor_objnum_provider = exclusive,
+ input_level_string = exclusive,
% \end{macrocode}
% \changes{v1.0m}{2016/02/11}{pdf\_stream\_filter\_callback removed}
% \begin{macrocode}
@@ -1477,7 +1479,7 @@ end
% functions in inverse order.
% \item[exclusive] is for functions with more complex signatures; functions in
% this type of callback are \emph{not} combined: An error is raised if
-% a second callback is registered..
+% a second callback is registered.
% \end{description}
%
% Handler for |data| callbacks.
diff --git a/Master/texmf-dist/source/latex-dev/base/ltmath.dtx b/Master/texmf-dist/source/latex-dev/base/ltmath.dtx
index ca90e1980d2..aadb96fb023 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltmath.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltmath.dtx
@@ -38,7 +38,7 @@
%<*driver>
% \fi
\ProvidesFile{ltmath.dtx}
- [2020/12/05 v1.2h LaTeX Kernel (Math Setup)]
+ [2021/04/20 v1.2i LaTeX Kernel (Math Setup)]
% \iffalse
%</driver>
%
@@ -961,11 +961,12 @@
% \end{macro}
%
% \begin{macro}{\@eqncr}
+% \changes{v1.2i}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)}
% \begin{macro}{\@xeqncr}
% \begin{macro}{\@yeqncr}
% \changes{v1.0y}{1995/10/16}{(DPC) Use \cs{@testopt} /1911}
% \begin{macrocode}
-\def\@eqncr{%
+\protected\def\@eqncr{%
{\ifnum0=`}\fi
\@ifstar{%
\global\@eqpen\@M\@yeqncr
@@ -1033,8 +1034,9 @@
\let\@seqncr=\@eqncr
% \end{macrocode}
%
+% \changes{v1.2i}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)}
% \begin{macrocode}
-\@namedef{eqnarray*}{\def\@eqncr{\nonumber\@seqncr}\eqnarray}
+\@namedef{eqnarray*}{\protected\def\@eqncr{\nonumber\@seqncr}\eqnarray}
% \end{macrocode}
%
% \begin{macrocode}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx
index 939bd51a2da..8bae3f9bfa3 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltmiscen.dtx
@@ -400,7 +400,7 @@
% \begin{macrocode}
%</2ekernel>
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2021/05/01}%
+%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\@kernel@before@enddocument}{kernel before hook}%
\def\@kernel@before@enddocument{\par}
%</2ekernel|latexrelease>
diff --git a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx
index 73146428c0d..eb2ecd1e537 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltoutenc.dtx
@@ -37,14 +37,14 @@
%<TS1>\ProvidesFile{ts1enc.def}[2001/06/05 v3.0e (jk/car/fm)
%<TU>\ProvidesFile{tuenc.def}
%<package>\ProvidesPackage{fontenc}
-%<OT1|T1|OMS|OML|OT4|TU|package> [2021/02/19 v2.0u
+%<OT1|T1|OMS|OML|OT4|TU|package> [2021/04/29 v2.0v
%<OT1|T1|OMS|OML|OT4|TS1|TU> Standard LaTeX file]
%<package> Standard LaTeX package]
%
%<*driver>
% \fi
\ProvidesFile{ltoutenc.dtx}
- [2021/02/19 v2.0u LaTeX Kernel (font encodings)]
+ [2021/04/29 v2.0v LaTeX Kernel (font encodings)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltoutenc.dtx}
@@ -3468,8 +3468,19 @@
\DeclareUnicodeComposite{\v} {o}{"01D2}
\DeclareUnicodeComposite{\v} {U}{"01D3}
\DeclareUnicodeComposite{\v} {u}{"01D4}
+% \end{macrocode}
+%
+% \changes{v2.0v}{2021/04/29}{Add composites for
+% \texttt{\protect\string\ae/\protect\string\AE/\ae/\AE} (gh/552)}
+% \begin{macrocode}
+\DeclareUnicodeComposite{\'} \AE{"01FC}
+\DeclareUnicodeComposite{\'} {Æ}{"01FC}
+\DeclareUnicodeComposite{\'} \ae{"01FD}
+\DeclareUnicodeComposite{\'} {æ}{"01FD}
\DeclareUnicodeComposite{\=} \AE{"01E2}
+\DeclareUnicodeComposite{\=} {Æ}{"01E2}
\DeclareUnicodeComposite{\=} \ae{"01E3}
+\DeclareUnicodeComposite{\=} {æ}{"01E3}
\DeclareUnicodeComposite{\v} {G}{"01E6}
\DeclareUnicodeComposite{\v} {g}{"01E7}
\DeclareUnicodeComposite{\v} {K}{"01E8}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltpar.dtx b/Master/texmf-dist/source/latex-dev/base/ltpar.dtx
index f4f3d5e63fc..969954be0a7 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltpar.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltpar.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltpar.dtx}
- [1995/04/29 v1.1c LaTeX Kernel (paragraphs)]
+ [2021/04/13 v1.1c LaTeX Kernel (paragraphs)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltpar.dtx}
@@ -67,9 +67,11 @@
% |\par| and |\everypar| when ever their function needs to be
% changed for a long time.
%
-% \StopEventually{}
-%
-% \subsection{Implementation}
+% This file here describes the interfaces that have been in the kernel
+% forever, used to implement the scenarios described below. They
+% remain valid but are now augmented in the next file
+% (\texttt{ltpara.dtx}) to add hooks to paragraphs. At some point we
+% will consolidate the two files further.
%
%
% There are two situations in which |\par| may be changed:
@@ -93,6 +95,12 @@
% \end{itemize}
% \end{itemize}
%
+%
+% \StopEventually{}
+%
+% \subsection{Implementation}
+%
+%
% \DescribeMacro{\@setpar}
% To permit the proper interaction of these two situations, long-term
% changes are made by the |\@setpar{|\meta{VAL}|}| command.
diff --git a/Master/texmf-dist/source/latex-dev/base/ltpara.dtx b/Master/texmf-dist/source/latex-dev/base/ltpara.dtx
index 1a3df95a6e2..ad503b68e25 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltpara.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltpara.dtx
@@ -11,11 +11,11 @@
% https://www.latex-project.org/lppl.txt
%
%
-%%% From File: lthooks.dtx
+%%% From File: ltpara.dtx
%
% \begin{macrocode}
-\def\ltparaversion{v1.0d}
-\def\ltparadate{2021/02/18}
+\def\ltparaversion{v1.0f}
+\def\ltparadate{2021/04/16}
% \end{macrocode}
%<*driver>
\documentclass{l3doc}
@@ -302,13 +302,14 @@
% \cs{noindent}).
%
% The indentation box to be typeset is available to the hook as
-% \cs{IndentationBox} and its automatic placement (after the hook is
-% executed) can be prevented through \cs{SuppressIndentationBox}.
+% \cs{IndentBox} and its automatic placement (after the hook is
+% executed) can be prevented through \cs{OmitIndent}.
+% More precisely \cs{OmitIndent} voids the box.
%
-% If not suppressed, then the indentation is typeset directly
+% The indentation box is then typeset directly
% after the hook execution by something equivalent to
-% \cs{box}\cs{IndentationBox} followed by the current content of
-% the token register \cs{everypar} as it is available to the
+% \cs{box}\cs{IndentBox} followed by the current content of
+% the token register \cs{everypar} that it is available to the
% kernel or to packages (that run some legacy code).
%
% One has to be careful not to add any code to the hook that
@@ -375,7 +376,7 @@
% \item[\cs{@kernel@before@para@before}]
% For future extensions, not currently used by the kernel.
%
-
+%
% \item[\cs{@kernel@after@para@after}]
% For future extensions, not currently used by the kernel.
%
@@ -437,26 +438,67 @@
% Spring release!
% \end{quote}
%
-% \begin{function}{\SuppressIndentationBox,\para_suppress_indentation:}
+% \begin{function}{\OmitIndent,\para_omit_indent:}
% Inside the \hook{para/begin} hook one can use this command to
% suppress the indentation box at the start of the
% paragraph. (Technically it is possible to use this command
% outside the hook as well, but this should not be relied upon.)
% The box itself remains available for use.
%
-% The \pkg{expl3} name for the function is \cs{para_suppress_indentation:}.
+% The \pkg{expl3} name for the function is \cs{para_omit_indent:}.
% \end{function}
%
-% \begin{variable}{\IndentationBox,\g_para_indentation_box}
+% \begin{variable}{\IndentBox,\g_para_indent_box}
% The box register holding the indentation box for the paragraph is
% available for inspection (or changes) inside hooks. It remains
-% available even if the \cs{SuppressIndentationBox} command was
+% available even if the \cs{OmitIndent} command was
% used; in that case it will just not be automatically placed.
%
-% The \pkg{expl3} name for the box register is \cs{g_para_indentation_box}.
+% The \pkg{expl3} name for the box register is \cs{g_para_indent_box}.
% \end{variable}
%
%
+% \begin{function}{\RawIndent,\para_raw_indent:,
+% \RawNoindent,\para_raw_noindent:,
+% \RawParEnd,\para_raw_end:}
+% \begin{syntax}
+% \cs{RawIndent} \textit{hmode material} \cs{RawParEnd}
+% \cs{RawNoindent} \textit{hmode material} \cs{RawParEnd}
+% \end{syntax}
+%
+% The commands \cs{RawIndent} and \cs{RawNoindent} are not meant
+% for normal paragraph building (where the result is a textual
+% paragraph in the the traditional meaning of the word), but for
+% special cases where \TeX{}'s low-level algorithm is used to
+% achieve special effects, but where the result is not a
+% \enquote{paragraph}.
+%
+% They are called \enquote{raw}, because they bypass \LaTeX{}'s
+% hook mechanism for paragraphs and simply invoke the low-level
+% \TeX{} algorithm. I.e., they are like the original \TeX{}
+% primitives \cs{indent} and \cs{noindent} (that is they execute no
+% hooks other than \cs{everypar}) except that they can only be used
+% in vertical mode and generate an error if found elsewhere.
+%
+% To avoid issues a paragraph started by them should always be
+% ended by \cs{RawParEnd}\footnote{Technical note for those who
+% know their \textit{\TeX book\/}: the \cs{RawParEnd} comand
+% invokes the original \TeX{} engine definition of \cs{par} that
+% (soley) triggers the paragraph builder in \TeX{} when found
+% inside unrestricted horizontal mode and does nothing in other
+% processing modes.}
+% and not by \cs{par} (or a blank line), because the latter will execute
+% hooks which then have no counterpart at the beginning of the
+% paragraph. It is the responsibility of the programmer to make
+% sure that they are properly paired. This also means that one
+% should not put arbitrary user content between these commands if
+% that content could contain stray \cs{par}s.
+%
+% The \pkg{expl3} names for the functions are
+% \cs{para_raw_indent:}, \cs{para_raw_indent:} and
+% \cs{para_raw_end:}.
+% \end{function}
+%
%
%
% \subsection{Examples}
@@ -574,21 +616,70 @@
% in blue.
%
%
+%
+% \subsection{Some technical notes}
+%
+% The code tries hard to be transparent for package code, but of
+% course any change means that there is a potential for breaking
+% other code. So in section we collect a few cases that may be of
+% importance if low-level code is dealing with paragraphs that are
+% now behaving slightly differently. The notes are from issues we
+% observed and will probably grow over time.
+%
+% \subsubsection{Glue items between paragraphs (found with \pkg{fancypar})}
+%
+% In the past \LaTeX{} placed two glue items between two
+% consecutive paragraph, e.g.,
+%\begin{verbatim}
+% text1 \par text2 \par
+%\end{verbatim}
+% would show something like
+%\begin{verbatim}
+% \glue(\parskip) 0.0 plus 1.0
+% \glue(\baselineskip) 5.16669
+%\end{verbatim}
+% but now there is anothe \cs{parskip} glue (that is always 0pt):
+%\begin{verbatim}
+% \glue(\parskip) 0.0 plus 1.0
+% \glue(\parskip) 0.0
+% \glue(\baselineskip) 5.16669
+%\end{verbatim}
+% The reason is that we generate a \enquote{fake}'' paragraph to
+% gain control and safely add the early hooks, but this generates
+% an additional glue item. That item doesn't contribute anything
+% vertically but ifsomebody writes code the unravels a constructed
+% list using \cs{lastbox}, \cs{unskip} and \cs{unpenalty} then the
+% code has to remove one additional glue item or else will fail.
+
+%
+% ^^A \subsubsection{}
+%
+%
+%
+% ^^A \subsubsection{}
+%
+%
+%
+%
+%
+%
% \StopEventually{\setlength\IndexMin{200pt} \PrintIndex }
%
%
% \section{The Implementation}
%
% \begin{macrocode}
-%<*2ekernel>
+%<@@=para>
% \end{macrocode}
%
+%
% \begin{macrocode}
-%<@@=para>
+%<*2ekernel|latexrelease>
\ExplSyntaxOn
+%<latexrelease>\NewModuleRelease{2021/06/01}{ltpara}
+%<latexrelease> {Paragraph~handling~and~hooks}
% \end{macrocode}
-
-
+%
%
%
% \subsection{Providing hooks for paragraphs}
@@ -641,10 +732,10 @@
\tl_gset:Nn \g_@@_standard_everypar_tl {
% \end{macrocode}
% First we remove the indentation box and store it in
-% \cs{g_para_indentation_box}. If there was none because the paragraph
+% \cs{g_para_indent_box}. If there was none because the paragraph
% was started by \cs{noindent} the box register will be void.
% \begin{macrocode}
- \box_gset_to_last:N \g_para_indentation_box
+ \box_gset_to_last:N \g_para_indent_box
% \end{macrocode}
%
% This will make the newly started horizontal list empty, so if we
@@ -683,13 +774,6 @@
% case there are nested paragraphs coming up.
% \begin{macrocode}
\tex_everypar:D{\g_@@_standard_everypar_tl}
-% \showthe\tex_everypar:D
-% \end{macrocode}
-% Then we set up \cs{@@_handle_indent:} to reinsert the indentation
-% box later. This can be undone via \cs{SuppressIndentationBox}
-% inside the hook(s).
-% \begin{macrocode}
- \cs_set_eq:NN \@@_handle_indent: \@@_add_indentation_box:
% \end{macrocode}
%
% This is followed by executing the kernel and the public hook. The
@@ -710,7 +794,7 @@
% we add that later, and indirectly.
% \begin{macrocode}
\@@_handle_indent:
-% \the \everypar % <--- done differently
+% \the \everypar % <--- done differently below
}
% \end{macrocode}
% \end{macro}
@@ -782,40 +866,38 @@
% \end{macro}
%
%
-% \begin{macro}{\g_para_indentation_box}
+% \begin{macro}{\g_para_indent_box}
% For managing the indentation we need to provide a public
% accessible box register
% \begin{macrocode}
-\box_new:N \g_para_indentation_box
+\box_new:N \g_para_indent_box
% \end{macrocode}
% \end{macro}
-% \begin{macro}{\@@_handle_indent:,\@@_add_indentation_box:}
-% Adding (typesetting) the indent box is straight forward, the
-% default action for is \cs{@@_handle_indent:} is
-% \cs{@@_add_indentation_box:}.
+% \begin{macro}{\@@_handle_indent:}
+% Adding (typesetting) the indent box is straight forward.
+% If it was emptied before it does nothing.
% \begin{macrocode}
-\cs_new:Npn \@@_add_indentation_box: {
- \box_use_drop:N \g_para_indentation_box
+\cs_new:Npn \@@_handle_indent: {
+ \box_use_drop:N \g_para_indent_box
}
-\cs_new_eq:NN \@@_handle_indent: \@@_add_indentation_box:
% \end{macrocode}
-% The declaration \cs{para_suppress_indentation:} (or
-% \cs{SuppressIndentationBox}) changes that to do nothing.
+% The declaration \cs{para_omit_indent:} (or
+% \cs{OmitIndent}) changes that to do nothing.
% \begin{macrocode}
-\cs_new:Npn \para_suppress_indentation: {
- \cs_set_eq:NN \@@_handle_indent: \prg_do_nothing:
+\cs_new:Npn \para_omit_indent: {
+ \box_gclear:N \g_para_indent_box
}
% \end{macrocode}
% \end{macro}
%
%
-% \begin{macro}{\IndentationBox,\SuppressIndentationBox}
+% \begin{macro}{\IndentBox,\OmitIndent}
% The \LaTeXe{} names for the indentation box and for suppressing it
% for use in the \hook{para/begin} hook.
% \begin{macrocode}
-\cs_set_eq:NN \IndentationBox \g_para_indentation_box
-\cs_set_eq:NN \SuppressIndentationBox \para_suppress_indentation:
+\cs_set_eq:NN \IndentBox \g_para_indent_box
+\cs_set_eq:NN \OmitIndent \para_omit_indent:
% \end{macrocode}
% \end{macro}
%
@@ -979,6 +1061,67 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+%
+% \begin{macro}{\para_raw_indent:,
+% \para_raw_noindent:,
+% \para_raw_end:}
+%
+% The commands \cs{para_raw_indent:} and \cs{para_raw_noindent:}
+% are like the primitives \cs{indent} and \cs{noindent} except that
+% they can only be used in vertical mode.
+%
+% To avoid issues a paragraph started by them should always be
+% ended by \cs{para_raw_end:} and not by \cs{para_end:} or
+% \cs{par} as the latter will execute hooks which then have no
+% counterpart at the beginning of the paragraph. It is the
+% responsibility of the programmer to make sure that they are
+% properly paired.
+% \begin{macrocode}
+\cs_new:Npn \para_raw_indent: {
+ \mode_if_vertical:TF
+ {
+ \tex_everypar:D {
+ \box_gset_to_last:N \g_para_indent_box
+ \tex_everypar:D { \g_@@_standard_everypar_tl }
+ \@@_handle_indent:
+ \the\everypar }
+ }
+ { \__kernel_msg_error:nn {para}{raw} }
+ \tex_indent:D
+}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new:Npn \para_raw_noindent: {
+ \mode_if_vertical:TF
+ {
+ \tex_everypar:D {
+ \tex_everypar:D { \g_@@_standard_everypar_tl }
+ \the\everypar }
+ }
+ { \__kernel_msg_error:nn {para}{raw} }
+ \tex_noindent:D
+}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_eq:NN \para_raw_end: \tex_par:D
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\RawIndent,\RawNoIndent,\RawParEnd}
+% The \LaTeXe{} names for starting and ending a paragraph without adding any hooks.
+% \begin{macrocode}
+\cs_set_eq:NN \RawIndent \para_raw_indent:
+\cs_set_eq:NN \RawNoindent \para_raw_noindent:
+\cs_set_eq:NN \RawParEnd \para_raw_end:
+% \end{macrocode}
+% \end{macro}
+%
+%
% This ends the \texttt{para} module code.
% \begin{macrocode}
%<@@=>
@@ -1010,7 +1153,10 @@
%
% \subsection{The error messages}
%
-% Well, one really, first argument is the hook name second the mode
+% This one is used when we detect that some hook code has changed
+% the mode where it shouldn't, e.g., by starting or ending a
+% paragraph.
+% The first argument is the hook name second the mode
% it should have stayed in but didn't.
% \begin{macrocode}
\__kernel_msg_new:nnnn {para} {mode}
@@ -1026,72 +1172,43 @@
}
% \end{macrocode}
%
+% And here is one used in the \enquote{raw} commands when they are
+% used outside of vertical mode.
% \begin{macrocode}
-\ExplSyntaxOff
-% \end{macrocode}
-%
-% \subsection{Kernel corrections}
-%
-% These corrections/changes are already part of the 2021/05
-% release, i.e., but for testing with older releases they have been
-% included here for now. So this section will vanish soon.
-%
-%
-% Marginpar boxes are missing a final internal \cs{par}.
-% \begin{macrocode}
-\IfFormatAtLeastTF{2021/05/01}{}
-{
-\long\def \@savemarbox #1#2{%
- \global\setbox #1%
- \color@vbox
- \vtop{%
- \hsize\marginparwidth
- \@parboxrestore
- \@marginparreset
- #2\par % <-- needed
- \@minipagefalse
- \outer@nobreak
- }%
- \color@endbox
-}
+\__kernel_msg_new:nnnn {para} {raw}
+ {
+ Paragraph started while not in vertical mode.
+ }
+ {
+ Starting a paragraph with \RawIndent or \RawNoindent
+ (or \para_raw_indent: or \para_raw_noindent:) is only
+ allowed if LaTeX is in vertical mode.
+ }
% \end{macrocode}
-%
-% Two sample definitions for the kernel hooks to help a bit with
-% tracing.
+
+
% \begin{macrocode}
-\newcounter{paracnt}
-\def\@kernel@before@para@begin{%
- \stepcounter{paracnt}%
- \typeout{PARA: \arabic{paracnt} start
- (\@currenvir\space level\on@line)}%
-}
-\def\@kernel@after@para@end {%
- \typeout{PARA: \arabic{paracnt} end \space\space
- (\@currenvir\space level\on@line)}%
- \addtocounter{paracnt}{-1}%
-}
-% \end{macrocode}
-%
%
-% The next lines belong in the kernel and just make sure that we
-% avoid the these days unnecessary optimization that \TeX{} doesn't
-% call on \cs{par} if a vertical box ends in hmode. Instead \TeX{}
-% directly calls the par builder in that case :-(.
-% \begin{macrocode}
-\let\color@begingroup\begingroup
-\def\color@endgroup{\endgraf\endgroup}
-\def\color@setgroup{\color@begingroup} % \def\color@setgroup{\color@begingroup\set@color}
-\def\color@hbox{\hbox\bgroup\color@begingroup}
-\def\color@vbox{\vbox\bgroup\color@begingroup}
-\def\color@endbox{\color@endgroup\egroup}
-}
+%<latexrelease>\IncludeInRelease{0000/00/00}%
+%<latexrelease> {ltpara}{Undo~hooks~for~paragraphs}
+%<latexrelease>
+%<latexrelease>\let \OmitIndent \@undefined
+%<latexrelease>\let \IndentBox \@undefined
+%<latexrelease>\let \RawIndent \@undefined
+%<latexrelease>\let \RawNoindent \@undefined
+%<latexrelease>\let \RawParEnd \@undefined
+%<latexrelease>
+%<latexrelease>\cs_set_eq:NN \par \tex_par:D
+%<latexrelease>\cs_set_eq:NN \@@par \tex_par:D
+%<latexrelease>\cs_set_eq:NN \endgraf \tex_par:D
+%<latexrelease>
+%<latexrelease>\EndModuleRelease
+\ExplSyntaxOff
+%</2ekernel|latexrelease>
% \end{macrocode}
%
%
%
-% \begin{macrocode}
-%<*2ekernel>
-% \end{macrocode}
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1099,61 +1216,3 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-%%%% unused as hok was dropped
-
-% \subsubsection{Put the first word of each paragraph in an \cs{fbox} and append
-% a \P{} sign at the end of the paragraph}
-%
-% We make the assumption that the first word ends in a space so
-% that we can grab it easily (which
-% is of course not quite right). So here is the code for that.
-%\begin{verbatim}
-% \def\firstwordboxed#1 {\fbox{#1} }
-%\end{verbatim}
-% Installing that is then just a matter of putting it into the right
-% hook. Since we don't want to box the indentation (or what comes
-% from \cs{everypar} we shouldn't use \hook{para/begin} but
-% \hook{para/textbegin}.
-% Adding the \P{} is equally simple.
-%\begin{verbatim}
-% \AddToHook{para/textbegin}{\firstwordboxed}
-% \AddToHook{para/end}{\P}
-%\end{verbatim}
-% While this will work more or less flawless (if you accept that it
-% will pick up any punctuation after the first word), it will fail
-% in at least two cases.
-% \begin{itemize}
-% \item Single word paragraphs ending with \cs{par} instead of an
-% empty line will blow up because the \cs{par} will be picked up as
-% part of the argument and error as our \cs{firstwordboxed} doesn't
-% accept a \cs{par} not being long. That is actually good, because
-% picking it up would be worse: it would get ignored inside
-% \cs{fbox} and so the paragraph would be combined with the next
-% (or worse).
-%
-% \item The second problem is, of course, that sticking it into
-% every \hook{para/textbegin} means it get applied to places like
-% \cs{section} which is not really what you want. So some more
-% elaborate mechanism would be necessary---a task for another rainy
-% day.
-% \end{itemize}
-%
-% There is one other point to note here. The \cs{firstwordboxed} is
-% expected to scan ahead. So can that kind of code be placed into a
-% hook? The answer is a kind of yes and no.
-
-% Hooks can receive code from different places, but obviously only
-% one chunk of code can do some scanning ahead, the one that comes
-% last. Any hook code in a different position would see any
-% following code from later chunks as part of the scanning which
-% may or may not be a problem. In fact, even in our simple example,
-% the argument picked up by \cs{firstwordboxed} isn't really the
-% first word of the paragraph. It is that but in front of it there
-% is also the code responsible for running
-% \verb=\AddTohookNext{para/textbegin}=. As long as this is not
-% doing anything harmful it doesn't hurt in this case, but it
-% clearly shows that this example is not as simple as it
-% claims to be and on the whole we have to conclude that hooks
-% aren't really meant to do such scanning or at least not without a
-% lot of precaution.
-%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx b/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx
index b958d43760c..ae8b58995da 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltpictur.dtx
@@ -32,7 +32,7 @@
%<*driver>
% \fi
\ProvidesFile{ltpictur.dtx}
- [2020/12/05 v1.2a LaTeX Kernel (Picture Mode)]
+ [2021/04/20 v1.2b LaTeX Kernel (Picture Mode)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltpictur.dtx}
@@ -454,9 +454,10 @@
% \end{macro}
%
% \begin{macro}{\@stackcr}
+% \changes{v1.2b}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)}
% \begin{macro}{\@ixstackcr}
% \begin{macrocode}
-\def\@stackcr{\@ifstar\@ixstackcr\@ixstackcr}
+\protected\def\@stackcr{\@ifstar\@ixstackcr\@ixstackcr}
\def\@ixstackcr{\@ifnextchar[\@istackcr{\cr\ignorespaces}}
% \end{macrocode}
% \end{macro}
diff --git a/Master/texmf-dist/source/latex-dev/base/ltplain.dtx b/Master/texmf-dist/source/latex-dev/base/ltplain.dtx
index fa53cc17ece..e0fbcecf1b6 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltplain.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltplain.dtx
@@ -32,7 +32,7 @@
%<*driver>
% \fi
\ProvidesFile{ltplain.dtx}
- [2020/11/26 v2.3d LaTeX Kernel (Plain TeX)]
+ [2021/04/18 v2.3f LaTeX Kernel (Plain TeX)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltplain.dtx}
@@ -823,6 +823,28 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+% \begin{macro}{\@inputcheck}
+% \begin{macro}{\@unused}
+% Allocate read stream for testing and output stream that is never open an
+% thus writes to the terminal.
+% \changes{v1.0l}{1994/11/07}
+% {move here from ltdefns, remove duplicate \cs{@mainaux}}
+% \changes{v2.3e}{2021/03/26}{Allocate \cs{@inputcheck} and
+% \cs{@unused} early so that they are before expl3 allocates
+% more streams (gh/538)}
+% \begin{macrocode}
+\newread\@inputcheck
+\newwrite\@unused
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%
+%
+%
+%
% \begin{macro}{\maxdimen}
% \begin{macro}{\hideskip}
% Here are some examples of allocation.
@@ -898,6 +920,37 @@
% \end{macrocode}
% \tracingcommands=0
% \tracingrestores=0
+% \end{macrocode}
+%
+% \begin{macro}{\tracingstacklevels}
+% For Lua\TeX, the \cs{tracingstacklevels} functionality was
+% implemented as a callback, so here we just define the count register
+% to hold the value of the parameter.
+% \begin{macrocode}
+%</2ekernel>
+%<*2ekernel|latexrelease>
+%<latexrelease>\IncludeInRelease{2021/06/01}{\tracingstacklevels}%
+%<latexrelease> {tracingstacklevels}%
+\ifx\directlua\@undefined
+ % \tracingstacklevels=0 % added in 2021
+\else
+ \newcount\tracingstacklevels
+ % Code for \tracingstacklevels defined in ltfinal.dtx
+\fi
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingstacklevels}%
+%<latexrelease> {tracingstacklevels}%
+%<latexrelease>\ifx\directlua\@undefined
+%<latexrelease>\else
+%<latexrelease> \let\tracingstacklevels\@undefined
+%<latexrelease>\fi
+%<latexrelease>\EndIncludeInRelease
+%</2ekernel|latexrelease>
+%<*2ekernel>
+% \end{macrocode}
+% \end{macro}
+%
% \language=0
% \begin{macrocode}
\uchyph=1
@@ -1482,42 +1535,68 @@
% \changes{v2.0b}{2012/01/20}{etex tracing if available}
% \changes{v2.0d}{2015/02/20}{Spell commands correctly :-)}
% \changes{v2.0g}{2015/03/10}{Reorganize to be less noisy}
+% \changes{v2.3f}{2021/04/18}{Drop pre-$\varepsilon$-\TeX{} support}
+% \changes{v2.3f}{2021/04/18}
+% {Add \cs{tracingstacklevels} and \cs{tracinglostchars}=3}
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2015/01/01}{\loggingall}{etex tracing}%
+%<latexrelease>\IncludeInRelease{2021/06/01}{\loggingall}
+%<latexrelease> {\tracingstacklevels and \tracinglostchars=3}%
%<*2ekernel|latexrelease>
-\ifx\tracingscantokens\@undefined
-\gdef\loggingall{%
+\edef\loggingall{%
\tracingstats\tw@
\tracingpages\@ne
- \tracinglostchars\@ne
- \tracingparagraphs\@ne
- \errorcontextlines\maxdimen
- \loggingoutput
- \tracingmacros\tw@
- \tracingcommands\tw@
- \tracingrestores\@ne
- }%
-\else
-\gdef\loggingall{%
- \tracingstats\tw@
- \tracingpages\@ne
- \tracinglostchars\tw@
+ \tracinglostchars\thr@@
\tracingparagraphs\@ne
\tracinggroups\@ne
\tracingifs\@ne
\tracingscantokens\@ne
\tracingnesting\@ne
\errorcontextlines\maxdimen
- \loggingoutput
+ \ifdefined\tracingstacklevels \tracingstacklevels\maxdimen \fi
+ \noexpand \loggingoutput
\tracingmacros\tw@
\tracingcommands\thr@@
\tracingrestores\@ne
\tracingassigns\@ne
}%
-\fi
-\gdef\tracingall{\showoverfull\loggingall}
+\def\tracingall{\showoverfull\loggingall}
%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{2015/01/01}{\loggingall}{etex tracing}%
+%<latexrelease>\ifx\tracingscantokens\@undefined
+%<latexrelease>\gdef\loggingall{%
+%<latexrelease> \tracingstats\tw@
+%<latexrelease> \tracingpages\@ne
+%<latexrelease> \tracinglostchars\@ne
+%<latexrelease> \tracingparagraphs\@ne
+%<latexrelease> \errorcontextlines\maxdimen
+%<latexrelease> \loggingoutput
+%<latexrelease> \tracingmacros\tw@
+%<latexrelease> \tracingcommands\tw@
+%<latexrelease> \tracingrestores\@ne
+%<latexrelease> }%
+%<latexrelease>\else
+%<latexrelease>\gdef\loggingall{%
+%<latexrelease> \tracingstats\tw@
+%<latexrelease> \tracingpages\@ne
+%<latexrelease> \tracinglostchars\tw@
+%<latexrelease> \tracingparagraphs\@ne
+%<latexrelease> \tracinggroups\@ne
+%<latexrelease> \tracingifs\@ne
+%<latexrelease> \tracingscantokens\@ne
+%<latexrelease> \tracingnesting\@ne
+%<latexrelease> \errorcontextlines\maxdimen
+%<latexrelease> \loggingoutput
+%<latexrelease> \tracingmacros\tw@
+%<latexrelease> \tracingcommands\thr@@
+%<latexrelease> \tracingrestores\@ne
+%<latexrelease> \tracingassigns\@ne
+%<latexrelease>}%
+%<latexrelease>\fi
+%<latexrelease>\gdef\tracingall{\showoverfull\loggingall}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
%<latexrelease>\IncludeInRelease{0000/00/00}{\loggingall}{etex tracing}%
%<latexrelease>\gdef\loggingall{\tracingcommands\tw@\tracingstats\tw@
%<latexrelease> \tracingpages\@ne\tracinglostchars\@ne
@@ -1532,29 +1611,14 @@
%
% \begin{macro}{\tracingnone}
% \changes{v2.0g}{2015/03/10}{macro added}
-% \begin{macro}{\hideoutput}
-% \changes{v2.0g}{2015/03/10}{macro added}
+% \changes{v2.3f}{2021/04/18}{Drop pre-$\varepsilon$-\TeX{} support}
+% \changes{v2.3f}{2021/04/18}
+% {Add \cs{tracingstacklevels} and \cs{tracinglostchars}=3}
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2015/01/01}{\tracingnone}%
%<latexrelease> {turn off etex tracing}%
%<*2ekernel|latexrelease>
-\ifx\tracingscantokens\@undefined
-\def\tracingnone{%
- \tracingonline\z@
- \tracingcommands\z@
- \showboxdepth\m@ne
- \showboxbreadth\m@ne
- \tracingoutput\z@
- \errorcontextlines\m@ne
- \tracingrestores\z@
- \tracingparagraphs\z@
- \tracingmacros\z@
- \tracinglostchars\@ne
- \tracingpages\z@
- \tracingstats\z@
-}%
-\else
-\def\tracingnone{%
+\edef\tracingnone{%
\tracingassigns\z@
\tracingrestores\z@
\tracingonline\z@
@@ -1563,6 +1627,7 @@
\showboxbreadth\m@ne
\tracingoutput\z@
\errorcontextlines\m@ne
+ \ifdefined\tracingstacklevels \tracingstacklevels\z@ \fi
\tracingnesting\z@
\tracingscantokens\z@
\tracingifs\z@
@@ -1573,30 +1638,77 @@
\tracingpages\z@
\tracingstats\z@
}%
-\fi
+%</2ekernel|latexrelease>
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{2015/01/01}{\tracingnone}%
+%<latexrelease> {turn off etex tracing}%
+%<latexrelease>\ifx\tracingscantokens\@undefined
+%<latexrelease>\def\tracingnone{%
+%<latexrelease> \tracingonline\z@
+%<latexrelease> \tracingcommands\z@
+%<latexrelease> \showboxdepth\m@ne
+%<latexrelease> \showboxbreadth\m@ne
+%<latexrelease> \tracingoutput\z@
+%<latexrelease> \errorcontextlines\m@ne
+%<latexrelease> \tracingrestores\z@
+%<latexrelease> \tracingparagraphs\z@
+%<latexrelease> \tracingmacros\z@
+%<latexrelease> \tracinglostchars\@ne
+%<latexrelease> \tracingpages\z@
+%<latexrelease> \tracingstats\z@
+%<latexrelease>}%
+%<latexrelease>\else
+%<latexrelease>\def\tracingnone{%
+%<latexrelease> \tracingassigns\z@
+%<latexrelease> \tracingrestores\z@
+%<latexrelease> \tracingonline\z@
+%<latexrelease> \tracingcommands\z@
+%<latexrelease> \showboxdepth\m@ne
+%<latexrelease> \showboxbreadth\m@ne
+%<latexrelease> \tracingoutput\z@
+%<latexrelease> \errorcontextlines\m@ne
+%<latexrelease> \tracingnesting\z@
+%<latexrelease> \tracingscantokens\z@
+%<latexrelease> \tracingifs\z@
+%<latexrelease> \tracinggroups\z@
+%<latexrelease> \tracingparagraphs\z@
+%<latexrelease> \tracingmacros\z@
+%<latexrelease> \tracinglostchars\@ne
+%<latexrelease> \tracingpages\z@
+%<latexrelease> \tracingstats\z@
+%<latexrelease>}%
+%<latexrelease>\fi
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingnone}%
+%<latexrelease> {turn off etex tracing}%
+%<latexrelease>\let\tracingnone\@undefined
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
+% \end{macro}
%
+% \begin{macro}{\hideoutput}
+% \changes{v2.0g}{2015/03/10}{macro added}
% \begin{macrocode}
+%<*2ekernel|latexrelease>
+%<latexrelease>\IncludeInRelease{2015/01/01}{\hideoutput}%
+%<latexrelease> {hide output from tracing}%
\def\hideoutput{%
\tracingoutput\z@
\showboxbreadth\m@ne
\showboxdepth\m@ne
\tracingonline\m@ne
}%
-% \end{macrocode}
-%
-% \begin{macrocode}
-%</2ekernel|latexrelease>
%<latexrelease>\EndIncludeInRelease
-%<latexrelease>\IncludeInRelease{0000/00/00}{\tracingnone}%
-%<latexrelease> {turn off etex tracing}%
-%<latexrelease>\let\tracingnone\@undefined
+%<latexrelease>
+%<latexrelease>\IncludeInRelease{0000/00/00}{\hideoutput}%
+%<latexrelease> {hide output from tracing}%
%<latexrelease>\let\hideoutput\@undefined
%<latexrelease>\EndIncludeInRelease
+%</2ekernel|latexrelease>
% \end{macrocode}
% \end{macro}
-% \end{macro}
-%
%
% \LaTeX\ change: |\showhyphens| Defined later.
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx
index 565f35f529c..f773cd23da2 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltshipout.dtx
@@ -33,7 +33,7 @@
%
% \begin{macrocode}
\providecommand\ltshipoutversion{v1.0i}
-\providecommand\ltshipoutdate{2021/01/25}
+\providecommand\ltshipoutdate{2021/03/17}
% \end{macrocode}
%
%<*driver>
@@ -144,6 +144,12 @@
% This box register is called \cs{ShipoutBox} (alternatively
% available via the L3 name \cs{l_shipout_box}).
%
+% This box is a ``local'' box and assignments to it should be done
+% only locally. Global assignments (as done by some packages with
+% older code where this is box is known as 255) may work but they are
+% conceptually wrong and may result in errors under certain
+% circumstances.
+%
% During the execution of \hook{shipout/before} this box contains
% the accumulated material for the page, but not yet any material
% added by other shipout hooks.
diff --git a/Master/texmf-dist/source/latex-dev/base/lttab.dtx b/Master/texmf-dist/source/latex-dev/base/lttab.dtx
index a964b365753..48627f40a3c 100644
--- a/Master/texmf-dist/source/latex-dev/base/lttab.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/lttab.dtx
@@ -31,7 +31,7 @@
%%% From File: lttab.dtx
%<*driver>
% \fi
-\ProvidesFile{lttab.dtx}[2020/04/24 v1.1r LaTeX Kernel (Columns)]
+\ProvidesFile{lttab.dtx}[2021/04/20 v1.1s LaTeX Kernel (Columns)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{lttab.dtx}
@@ -464,7 +464,7 @@
%
% \begin{macro}{\@tabcr}
% \begin{macrocode}
-\def\@tabcr{\@stopline \@ifstar{\penalty \@M \@xtabcr}\@xtabcr}
+\protected\def\@tabcr{\@stopline \@ifstar{\penalty \@M \@xtabcr}\@xtabcr}
% \end{macrocode}
% \end{macro}
%
@@ -964,8 +964,9 @@
%
% \begin{macro}{\@arraycr}
% Array version of |\\|.
+% \changes{v1.1s}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)}
% \begin{macrocode}
-\def\@arraycr{%
+\protected\def\@arraycr{%
${\ifnum0=`}\fi\@ifstar\@xarraycr\@xarraycr}
% \end{macrocode}
% \end{macro}
@@ -994,8 +995,9 @@
% \end{macro}
%
% \begin{macro}{\@tabularcr}
+% \changes{v1.1s}{2021/04/20}{Use \cs{protected} for \cs{\bslash} variant (gh/548)}
% \begin{macrocode}
-\def\@tabularcr{%
+\protected\def\@tabularcr{%
{\ifnum0=`}\fi\@ifstar\@xtabularcr\@xtabularcr}
% \end{macrocode}
% \end{macro}
diff --git a/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx b/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx
index d840f0448b9..1efc36d5637 100644
--- a/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/lttextcomp.dtx
@@ -36,7 +36,7 @@
%
%
\ProvidesFile{lttextcomp.dtx}
- [2021/01/20 v1.0d LaTeX Kernel (text companion symbols)]
+ [2021/04/16 v1.0f LaTeX Kernel (text companion symbols)]
% \iffalse
\documentclass{ltxdoc}
\begin{document}
@@ -70,20 +70,23 @@
%
% \StopEventually{}
%
+% \changes{v1.0e}{2021/03/19}
+% {Use \cs{NewModuleRelease}}
+% \begin{macrocode}
+%<*2ekernel|latexrelease>
+%<latexrelease>\NewModuleRelease{2020/02/02}{lttextcomp}
+%<latexrelease> {Text Companion symbols}
+% \end{macrocode}
%
% \begin{macro}{\oldstylenums}
% \begin{macro}{\legacyoldstylenums}
%
-%
% Preserve the old definition of \cs{oldstylenums} under a different name.
%
% This macro implements old style numerals but only works if we
% assume that the standard math fonts are used. Thus it needs
% changing in case other math encodings are used.
% \begin{macrocode}
-%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/02/02}%
-%<latexrelease> {\oldstylenums}{Old style numerals}%
\DeclareRobustCommand\legacyoldstylenums[1]{%
\begingroup
% \end{macrocode}
@@ -157,44 +160,6 @@
% \end{macrocode}
% \end{macro}
%
-%
-% \begin{macrocode}
-%</2ekernel|latexrelease>
-%<latexrelease>\EndIncludeInRelease
-%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\oldstylenums}{Old style numerals}%
-%<latexrelease>
-%<latexrelease>\DeclareRobustCommand\oldstylenums[1]{%
-%<latexrelease> \begingroup
-%<latexrelease> \spaceskip\fontdimen\tw@\font
-%<latexrelease> \usefont{OML}{\rmdefault}{\f@series}{it}%
-%<latexrelease> \mathgroup\symletters #1%
-%<latexrelease> \endgroup
-%<latexrelease>}
-%<latexrelease>\let\legacyoldstylenums\@undefined
-%<latexrelease>\def\textcompsubstdefault{cmr}
-%<latexrelease>
-%<latexrelease>\EndIncludeInRelease
-% \end{macrocode}
-%
-%
-
-
-
-
-
-% Everything else in the this file got introduced 2020/02/02, so we do a
-% single rollback (for now).
-% \begin{macrocode}
-%<*2ekernel>
-%</2ekernel>
-%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/02/02}%
-%<latexrelease> {\DeclareEncodingSubset}{Text companion symbols}%
-% \end{macrocode}
-%
-%
-%
% \begin{macro}{\DeclareEncodingSubset}
%
% The declaration takes 3 mandatory arguments: an \emph{encoding}
@@ -1300,9 +1265,19 @@
% Supporting rollback \ldots
% \begin{macrocode}
%</2ekernel|latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%<latexrelease>
%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\DeclareEncodingSubset}{Text companion symbols}%
+%<latexrelease> {lttextcomp}{Undefine text companion symbols}%
+%<latexrelease>
+%<latexrelease>\DeclareRobustCommand\oldstylenums[1]{%
+%<latexrelease> \begingroup
+%<latexrelease> \spaceskip\fontdimen\tw@\font
+%<latexrelease> \usefont{OML}{\rmdefault}{\f@series}{it}%
+%<latexrelease> \mathgroup\symletters #1%
+%<latexrelease> \endgroup
+%<latexrelease>}
+%<latexrelease>\let\legacyoldstylenums\@undefined
+%<latexrelease>\def\textcompsubstdefault{cmr}
%<latexrelease>
%<latexrelease>\let\DeclareEncodingSubset\@undefined
%<latexrelease>\let\CheckEncodingSubset\@undefined
@@ -1493,9 +1468,7 @@
%<latexrelease>\let\textlegacyperiodcentered\@undefined
%<latexrelease>\let\textlegacysection\@undefined
%<latexrelease>
-%<latexrelease>\EndIncludeInRelease
-%<*2ekernel>
-%</2ekernel>
+%<latexrelease>\EndModuleRelease
% \end{macrocode}
%
%
diff --git a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx
index 26eaee98901..fe50d75a8f1 100644
--- a/Master/texmf-dist/source/latex-dev/base/ltvers.dtx
+++ b/Master/texmf-dist/source/latex-dev/base/ltvers.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltvers.dtx}
- [2021/01/15 v1.1e LaTeX Kernel (Version Info)]
+ [2021/04/16 v1.1g LaTeX Kernel (Version Info)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltvers.dtx}
@@ -112,10 +112,10 @@
%</2ekernel>
%<latexrelease>\edef\latexreleaseversion
%<*2ekernel|latexrelease>
- {2021-05-01}
+ {2021-06-01}
%</2ekernel|latexrelease>
%<*2ekernel>
-\def\patch@level{-2}
+\def\patch@level{-3}
% \end{macrocode}
%
% \begin{macro}{\development@branch@name}
@@ -214,6 +214,11 @@
% \end{macrocode}
%
% \begin{macro}{\IncludeInRelease}
+% \begin{macro}{\EndIncludeInRelease}
+% \begin{macro}{\@IncludeInRelease}
+% \begin{macro}{\@IncludeInRele@se}
+% \begin{macro}{\@gobble@IncludeInRelease}
+% \begin{macro}{\@check@IncludeInRelease}
% \changes{v1.0w}{2015/02/21}
% {set \cs{@currname} empty here (in case \cs{IncludeInRelease} input early)}
% \begin{macrocode}
@@ -224,6 +229,7 @@
% \changes{v1.0m}{2015/01/17}{modified with \cs{@currname}}
% \changes{v1.0o}{2015/01/19}{Optional argument}
% \changes{v1.0q}{2015/02/19}{Swap argument order}
+% \changes{v1.1f}{2021/03/18}{Add support for usage in \cs{NewModuleRelease}}
%
% \begin{macrocode}
%<*2ekernel|latexrelease>
@@ -237,9 +243,29 @@
{There is an \string\EndIncludeRelease\space missing}%
\@includeinreleasefalse
\fi
- \kernel@ifnextchar[%
- {\@IncludeInRelease{#1}}
- {\@IncludeInRelease{#1}[#1]}}
+ \ifnum0%
+ \ifx\new@moduledate\@empty\else 1\fi
+ \ifnum \expandafter\@parse@version#1//00\@nil=0 1\fi
+ =11
+ \expandafter\@firstoftwo
+ \else
+ \expandafter\@secondoftwo
+ \fi
+ {\finish@module@release{#1}}%
+ {\kernel@ifnextchar[%
+ {\@IncludeInRelease{#1}}
+ {\@IncludeInRelease{#1}[#1]}}}
+\def\finish@module@release#1#2#3{%
+ \toks@{[#1] #3}%
+ \ifnum\expandafter\@parse@version\new@moduledate//00\@nil
+ >\expandafter\@parse@version\fmtversion//00\@nil
+ \GenericInfo{}{Applying: \the\toks@}%
+ \else
+ \GenericInfo{}{Skipping: \the\toks@}%
+ \expandafter\gobble@finish@module@release
+ \fi}
+\long\def\gobble@finish@module@release#1\EndModuleRelease{%
+ \EndModuleRelease}
% \end{macrocode}
%
% If a specific date has not been specified in |latexrelease|
@@ -261,9 +287,9 @@
% only really needed for rolling forward. So maybe one day \dots
% \begin{macrocode}
\ifnum\expandafter\@parse@version#1//00\@nil
- >\expandafter\@parse@version\fmtversion//00\@nil
+ >\expandafter\@parse@version\fmtversion//00\@nil
\GenericInfo{}{Skipping: \the\toks@}%
- \expandafter\expandafter\expandafter\@gobble@IncludeInRelease
+ \expandafter\expandafter\expandafter\@gobble@IncludeInRelease
\else
\GenericInfo{}{Applying: \the\toks@}%
\@includeinreleasetrue
@@ -282,6 +308,9 @@
\@includeinreleasefalse
\else
\PackageError{latexrelease}{mis-matched EndIncludeInRelease}{}%
+\fi
+\if@skipping@module
+ \expandafter\new@module@skip
\fi}
% \end{macrocode}
%
@@ -296,13 +325,87 @@
#2#3\@end@check@IncludeInRelease{%
\ifx\@check@IncludeInRelease#2\else
\PackageError{latexrelease}{skipped IncludeInRelease for tag \string#2}{}%
+ \fi
+ \if@skipping@module
+ \expandafter\new@module@skip
\fi}
% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
+% \subsection{Declaring an all-new module}
+%
+% \begin{macro}{\if@skipping@module}
+% \begin{macro}{\NewModuleRelease}
+% \begin{macro}{\EndModuleRelease}
+% \begin{macro}{\new@module@skip}
+% \begin{macro}{\new@modulename}
+% \begin{macro}{\new@moduledate}
+%
+% When we have a whole new module, we can't roll back to a date where
+% such module exists, otherwise hundreds of \enquote{command already
+% defined} errors will pop up. But we can't skip it altogether
+% either, because the module might have changes we still want applied,
+% so a more detailed cherry-picking of code chunks have to be done.
+%
+% \changes{v1.1f}{2021/03/18}{Added \cs{NewModuleRelease}.}
+% \changes{v1.1g}{2021/04/16}
+% {\cs{NewModuleRelease} with the same arguments as \cs{IncludeInRelease}.}
% \begin{macrocode}
-%</2ekernel|latexrelease>
+\let\if@skipping@module\iffalse
+\def\@skipping@moduletrue{\let\if@skipping@module\iftrue}
+\def\@skipping@modulefalse{\let\if@skipping@module\iffalse}
+\let\new@modulename\@empty
+\let\new@moduledate\@empty
+\def\NewModuleRelease#1#2#3{%
+ \ifx\new@modulename\@empty \else
+ \@latex@error{Nested \noexpand\NewModuleRelease forbidden.}\@ehd \fi
+ \edef\new@moduledate{#1}%
+ \edef\new@modulename{#2}%
+ \GenericInfo{}{BEGIN module: \new@modulename\space (\new@moduledate)}%
+ \GenericInfo{}{ \@spaces\@spaces\@spaces\space#3\@gobble}%
+ \ifnum\sourceLaTeXdate<%
+ \expandafter\@parse@version\new@moduledate//00\@nil\relax
+ \ifnum\expandafter\@parse@version\fmtversion//00\@nil<%
+ \expandafter\@parse@version\new@moduledate//00\@nil\relax
+ \GenericInfo{}{Skipping module \new@modulename}%
+ \expandafter\expandafter
+ \expandafter\gobble@finish@module@release
+ \else
+ \GenericInfo{}{Applying module \new@modulename}
+ \@skipping@modulefalse
+ \fi
+ \else
+ \GenericInfo{}{Skipping module \new@modulename}
+ \@skipping@moduletrue
+ \expandafter\new@module@skip
+ \fi}
+\long\def\new@module@skip#1\IncludeInRelease{\IncludeInRelease}
+\def\EndModuleRelease{%
+ \ifx\new@modulename\@empty
+ \@latex@error{Extra \string\EndModuleRelease.}\@eha
+ \else
+ \GenericInfo{}{END module: \new@modulename\space (\new@moduledate)}%
+ \let\new@modulename\@empty
+ \let\new@moduledate\@empty
+ \@skipping@modulefalse
+ \fi}
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</2ekernel|latexrelease>
+% \end{macrocode}
+%
% \Finale
%
\endinput