% \iffalse meta-comment % % Copyright (C) 1993-2024 % The LaTeX Project and any individual authors listed elsewhere % in this file. % % This file is part of the LaTeX base system. % ------------------------------------------- % % It may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % of this license or (at your option) any later version. % The latest version of this license is in % https://www.latex-project.org/lppl.txt % and version 1.3c or later is part of all distributions of LaTeX % version 2008 or later. % % This file has the LPPL maintenance status "maintained". % % The list of all files belonging to the LaTeX base distribution is % given in the file `manifest.txt'. See also `legal.txt' for additional % information. % % The list of derived (unpacked) files belonging to the distribution % and covered by LPPL is defined by the unpacking scripts (with % extension .ins) which are part of the distribution. % % \fi % % \iffalse % %<*dtx> \def\latexreleaseversion{} \ProvidesFile{latexrelease.dtx} % %\ProvidesFile{latexrelease.drv} %\ProvidesPackage{fixltx2e} % [2016/12/29 v2.1a fixes to LaTeX (obsolete)] %\ProvidesPackage{latexrelease} %<*latexrelease> % \fi % \ProvidesFile{latexrelease.dtx} [2022/11/14 v1.0p LaTeX release emulation and tests (including releases up to \latexreleaseversion)] % \iffalse % %<*driver> \documentclass{ltxdoc} \newcommand\Lopt[1]{\textsf{#1}} \let\Lpack\Lopt \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} % % \fi % % \changes{v1.0i}{2017/12/17}{Add \cs{latexreleaseversion} info % to \cs{ProvidesPackage} output} % % % \let\package\textsf % % % \GetFileInfo{latexrelease.dtx} % % \title{The \Lpack{latexrelease} package\thanks{This file % has version number \fileversion, last % revised \filedate.}} % \author{The \LaTeX{} Project} % \date{\filedate} % \MaintainedByLaTeXTeam{latex} % \maketitle % % \section{Introduction} % Prior to the 2015 release of \LaTeX{}, essentially no changes had been % made to the \LaTeX\ format code for some years, with all % improvements being instead added to the package \Lpack{fixltx2e}. % % While this worked at a technical level it meant that you had to % explicitly opt-in to bug fixes and improvements, and the vast % majority of documents did not benefit. % % As described in \LaTeX\ News 22, a new policy is being implemented % in which improvements will now be added to the format by default, % and this \Lpack{latexrelease} package may be used to ensure % stability where needed, either by making a new format use an older % definition of some commands, or conversely may be used to supply the % new definitions for use with an old format. % % The basic use is: %\begin{verbatim} % \RequirePackage[2015/01/01]{latexrelease} % \documentclass{article} % .... %\end{verbatim} % % After such a declaration the document will use definitions current % in the January 2015 \LaTeX{}, whether the actual format being used is % older, or newer than that date. In the former case a copy of % |latexrelease.sty| would need to be made available for use with the % older format. This may be used, for example, to share a document % between co-workers using different \LaTeX\ releases, or to protect a % document from being affected by system updates. As well as the % definitions within the format itself, individual packages may use % the commands defined here to adjust their definitions to the % specified date as described below. % % Note that the \Lpack{latexrelease} package is intended for use % at the start of a \emph{document}. Package and class code should not % include this package as loading a package should not normally globally % reset the effective version of \LaTeX\ that is in force, so affecting all % other packages used in the document. % % % The bulk of this package, after some initial setup and option % handling consists of a series of |\IncludeInRelease| commands % which have been extracted from the main source files of the \LaTeX\ % format. These contain the old and new versions of any commands with % modified definitions. % % \section{Package Options} % \begin{itemize} % \item \emph{yyyy/mm/dd} or \emph{yyyy-nn-dd} % The package accepts any possible \LaTeX\ format date as argument, % although dates in the future for which the current release of this % package has no information will generate a warning. Dates % earlier than 2015 will work but will roll back to some point in 2015 % when the method was introduced. % The |\requestedLaTeXdate| is set to the normalized date argument % so that package rollback defaults to the specified date. % % \item% % |current| This is the default behaviour, it does not change the % effective date of the format but does ensure that the % |\IncludeInRelease| command is defined. % The |\requestedLaTeXdate| macro is reset to 0 so that package rollback % does not use the implicit date. % % \item % |latest| sets the effective date of the format to the release date % of this file, so in an older format applies all patches currently % available. % The |\requestedLaTeXdate| macro is reset to 0 so that package rollback % does not use the implicit date. % \end{itemize} % % In all cases, when the package is loaded, the |\sourceLaTeXdate| is % defined to be the numerical representation of |\fmtversion| before the % rollback/forward actually happens, so it is possible to test from which % was the original \LaTeX{} version before \Lpack{latexrelease} was loaded. % This is particularly useful when some code in a package has to be % redefined differently if rolling backwards in time or forwards. % % \section{Release Specific Code} % % The |\IncludeInRelease| mechanism allows the kernel developer to % associate code with a specific date to choose different versions of % definitions depending on the date specified as an option to the % \Lpack{latexrelease} package. Is also available for use by package % authors (or even in a document if necessary). % % \medskip % % \noindent\DescribeMacro{\IncludeInRelease} % \marg{code-date}^^A % \oarg{format-date}%^^A % \marg{label}^^A % \marg{message}^^A % \meta{code}^^A % |\EndIncludeInRelease| % % \begin{description} % % \item[\marg{code-date}] This date is associated with the \marg{code} % argument and will be compared to the requested date in the option to % the \Lpack{latexrelease}. % % \item[\oarg{format-date}] This optional argument can be used to % specify a format date with the code in addition to the mandatory % \marg{code-date} argument. This can be useful for package developers % as described below. % % \item[\marg{label}] The \marg{label} argument is an identifier % (string) that within a given package must be a unique label for each % related set of optional definitions. Per package at most one code % block from all the |\IncludeInRelease| declarations with the same % label will be executed. % % \item[\marg{message}] % The \marg{message} is an informative string that is used in % messages. It has no other function. % % \item[\meta{code}] % Any \TeX\ code after the |\IncludeInRelease| arguments % up until the and the following |\EndIncludeInRelease| % is to be % conditionally included depending on the date of the format as % described below. % % \end{description} % % The |\IncludeInRelease| declarations with a given label should be in % reverse chronological order in the file. The one chosen will depend % on this order, the effective format version and the date options, as % described below. % % If your package \Lpack{mypackage} defines a |\widget| command but % has one definition using the features available in the 2015 \LaTeX\ % release, and a different definition is required for older formats % then you can use: %\begin{verbatim} % \IncludeInRelease{2015/01/01}{\widget}{Widget Definition} % \def\widget{new version}% % \EndIncludeInRelease % % \IncludeInRelease{0000/00/00}{\widget}{Widget Definition} % \def\widget{old version}% % \EndIncludeInRelease %\end{verbatim} % % If a document using this package is used with a format with % effective release date of 2015/01/01 or later the new code will be % used, otherwise the old code will be used. Note the \emph{effective % release date} might be the original \LaTeX\ release date as shown at % the start of every \LaTeX\ job, or it may be set by the % \Lpack{latexrelease} package, so for example a document author who % wants to ensure the new version is used could use %\begin{verbatim} % \RequirePackage[2015/01/01]{latexrelease} % \documentclass{article} % \usepackage{mypackage} %\end{verbatim} % % If the document is used with a \LaTeX\ format from 2014 or before, % then \Lpack{latexrelease} will not have been part of the original % distribution, but it may be obtained from a later \LaTeX\ release or % from CTAN and distributed with the document, it will make an older % \LaTeX\ release act essentially like the 2015 release. % % \subsection{Intermediate Package Releases} % % The above example works well for testing against the latex format % but is not always ideal for controlling code by the release date of % the \emph{package}. Suppose \LaTeX\ is not updated but in March you % update the \Lpack{mypackage} package and modify the definition of % |\widget|. You could code the package as: %\begin{verbatim} % \IncludeInRelease{2015/03/01}{\widget}{Widget Definition} % \def\widget{even newer improved March version}% % \EndIncludeInRelease % % \IncludeInRelease{2015/01/01}{\widget}{Widget Definition} % \def\widget{new version}% % \EndIncludeInRelease % % \IncludeInRelease{0000/00/00}{\widget}{Widget Definition} % \def\widget{old version}% % \EndIncludeInRelease %\end{verbatim} % % This would work and allow a document author to choose a date such as %\begin{verbatim} % \RequirePackage[2015/03/01]{latexrelease} % \documentclass{article} % \usepackage{mypackage} %\end{verbatim} % % To use the latest version, however it would have disadvantage that % until the next release of \LaTeX, by default, if the document does % not use \Lpack{latexrelease} to specify a date, the new improved % code will not be selected as the effective date will be 2015/01/01 % and so the first code block will be skipped. % % For this reason |\IncludeInRelease| has an optional argument that % specifies an alternative date to use if a date option has not been % specified to \Lpack{latexrelease}. %\begin{verbatim} % \IncludeInRelease{2015/03/01}[2015/01/01]{\widget}{Widget Definition} % \def\widget{even newer improved March version}% % \EndIncludeInRelease % % \IncludeInRelease{2015/01/01}{\widget}{Widget Definition} % \def\widget{new version}% % \EndIncludeInRelease % % \IncludeInRelease{0000/00/00}{\widget}{Widget Definition} % \def\widget{old version}% % \EndIncludeInRelease %\end{verbatim} % % Now, by default on a 2015/01/01 \LaTeX\ format, the first code block % will compare the format date to the optional argument 2015/01/01 % and so will execute the \emph{even newer improved} version. The % remaining blocks using the |\widget| label argument will all then be % skipped. % % If on the other hand the document requests an explicit release date % using \Lpack{latexrelease} then this date will be used to decide what % code block to include. % % \subsection{Using \cs{IncludeInRelease} in Packages} % % If |\IncludeInRelease| is used within a package then all such % conditional code needs to be within such declarations, e.g., it is % not possible in the above example to have the ``current'' definition % of |\widget| somewhere in the main code and only the two older % definitions inside |\IncludeInRelease| declarations. If you would do % this then one of those |\IncludeInRelease| declarations would be % included overwriting the even newer code in the main part of the % package. As a result your package may get fragmented over time with % various |\IncludeInRelease| declarations sprinkled throughout your % code or you have to interrupt the reading flow by putting those % declarations together but not necessarily in the place where they % belong. % % To avoid this issue you can use the following coding % strategy: place the current |\widget| definition in the main code % where it correctly belongs. % \begin{verbatim} % ... % \def\widget {even newer improved March version} % \def\@widget{newly added helper command no defined in older releases} % ... %\end{verbatim} % Then, near the end of your package place % the following: % \begin{verbatim} % \IncludeInRelease{2015/03/01}[2015/01/01]{\widget}{Widget Definition} % \EndIncludeInRelease % % \IncludeInRelease{2015/01/01}{\widget}{Widget Definition} % \def\widget{new version}% % \let\@widget\@undefined % this doesn't exist in earlier releases % \EndIncludeInRelease % % \IncludeInRelease{0000/00/00}{\widget}{Widget Definition} % \def\widget{old version}% % \EndIncludeInRelease %\end{verbatim} % This way the empty code block hides the other |\IncludeInRelease| % declarations unless there is an explicit request with a date % 2015/01/01 or earlier. % % Now if you make a further change to |\widget| in the future you % simply copy the current definition into the empty block and add a new % empty declaration with today's date and the current format date. This % way your main code stays readable and the old versions accumulate at % the end of the package.\footnote{Of course there may be some cases % in which the old code has to be in a specific place within the % package as other code depends on it (e.g., if you % \texttt{\string\let} something to it). In that case you have to % place the code variations in the right place in your package rather % than accumulating them at the very end.} % % The only other ``extra effort'' necessary when using this approach % is that it may be advisable to undo new definitions in the code % block for the previous release, e.g., in the above example we % undefined |\@widget| as that isn't available in the 2015/01/01 % release but was defined in the main code. If all your conditional % code is within |\IncludeInRelease| declarations that wouldn't been % 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 % %\NewModuleRelease{2020/10/01}{lthooks}% % % {The~hook~management~system} % \NewDocumentCommand \NewHook { m } % { \hook_new:n {#1} } % %\IncludeInRelease{2021/06/01}{\AddToHook}{Long~argument} % \NewDocumentCommand \AddToHook { m o +m } % { \hook_gput_code:nnn {#1} {#2} {#3} } % %\EndIncludeInRelease % % % %\IncludeInRelease{2020/10/01}{\AddToHook}{Long~argument} % %\NewDocumentCommand \AddToHook { m o m } % % { \hook_gput_code:nnn {#1} {#2} {#3} } % %\EndIncludeInRelease % % % %\IncludeInRelease{0000/00/00}{lthooks}{Undefine~lthooks} % %\cs_undefine:N \NewHook % %\cs_undefine:N \AddToHook % %\EndModuleRelease % \ExplSyntaxOff % % % \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 % \LaTeX\ kernel were not made in the format source files but were % made available in the \Lpack{fixltx2e} package. That package is no % longer needed but we generate a small package from this source % that just makes a warning message but otherwise does nothing. % % % % % % \MaybeStop{} % % \section{Implementation} % % We require at least a somewhat sane version of \LaTeXe{}. Earlier % ones where really quite different from one another. % \begin{macrocode} %<*latexrelease> \NeedsTeXFormat{LaTeX2e}[1996/06/01] % \end{macrocode} % % \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 |. This macro has to be defined conditionally, so that it % isn't changed in case \file{latexrelease.sty} is reloaded, 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} \@ifundefined{sourceLaTeXdate}{% \edef\sourceLaTeXdate{% \expandafter\@parse@version\fmtversion//00\@nil}}{}% % \end{macrocode} % \end{macro} % % \begin{macro}{\IncludeInRelease} % \begin{macro}{\EndIncludeInRelease} % 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} % \begin{macrocode} \DeclareOption*{% \def\@IncludeInRelease#1[#2]{\@IncludeInRele@se{#1}}% \let\requestedpatchdate\CurrentOption} \DeclareOption{latest}{% \let\requestedpatchdate\latexreleaseversion \AtEndOfPackage{\def\requestedLaTeXdate{0}}} \DeclareOption{current}{% \let\requestedpatchdate\fmtversion \AtEndOfPackage{\def\requestedLaTeXdate{0}}} % \end{macrocode} % % \begin{macrocode} \let\requestedpatchdate\fmtversion \ProcessOptions\relax % \end{macrocode} % % Sanity check options, it allows some non-legal dates but always % ensures |requestedLaTeXdate| gets set to a number. Generate an % error if there are any non digit tokens remaining after removing the % |//|. % \changes{v1.0j}{2018/01/30}{Explain both date formats in error message} % \begin{macrocode} \def\reserved@a{% \edef\requestedLaTeXdate{\the\count@}% \reserved@b} \def\reserved@b#1\\{% \def\reserved@b{#1}% \ifx\reserved@b\@empty\else \PackageError{latexrelease}% {Unexpected option \requestedpatchdate}% {The option must be of the form yyyy/mm/dd or yyyy-mm-dd}% \fi} \afterassignment\reserved@a \count@\expandafter \@parse@version\expandafter0\requestedpatchdate//00\@nil\\ % \end{macrocode} % % less precautions needed for |\fmtversion| % \begin{macrocode} \edef\currentLaTeXdate{% \expandafter\@parse@version\fmtversion//00\@nil} % \end{macrocode} % % \begin{macrocode} \ifnum\requestedLaTeXdate=\currentLaTeXdate \PackageWarningNoLine{latexrelease}{% Current format date selected, no patches applied} \expandafter\endinput \fi % \end{macrocode} % % A newer version of latexrelease should have been distributed with % the later format. % \begin{macrocode} \ifnum\currentLaTeXdate >\expandafter\@parse@version\latexreleaseversion//00\@nil \PackageWarningNoLine{latexrelease}{% The current package is for an older LaTeX format:\MessageBreak LaTeX \latexreleaseversion\space\MessageBreak Obtain a newer version of this package!} \expandafter\endinput \fi % \end{macrocode} % can't patch into the future, could make this an error % but it has some uses to control package updates % so allow for now. % \begin{macrocode} \ifnum\requestedLaTeXdate >\expandafter\@parse@version\latexreleaseversion//00\@nil \PackageWarningNoLine{latexrelease}{% The current package is for LaTeX \latexreleaseversion:\MessageBreak It has no patches beyond that date\MessageBreak There may be an updated version\MessageBreak of this package available from CTAN} \expandafter\endinput \fi % \end{macrocode} % % Update the format version to the requested date. % \begin{macrocode} \let\fmtversion\requestedpatchdate \let\currentLaTeXdate\requestedLaTeXdate % \end{macrocode} % % % \subsection{Ignoring \texttt{\string_new} errors when rolling back} % % 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 % \xdef\latexrelease@postltexpl{\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 \@@_redefines:w #1 #2 { \quark_if_recursion_tail_stop:N #1 \tl_put_right:Nn \l_@@_restores_tl {#1} \cs_set_eq:cN { @@_ \cs_to_str:N #1 } #1 \cs_set_eq:NN #1 #2 \@@_redefines: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} \@@_redefines: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 % after ltcmd.dtx \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). % The same happens for \cs{NewMarkClass}. % \changes{v1.0p}{2022/11/14} % {Silence \cs{NewMarkClass} in rollback (gh/955)} % \begin{macrocode} \msg_redirect_name:nnn { hooks } { exists } { none } \msg_redirect_name:nnn { mark } { class-already-defined }{ none } % \end{macrocode} % % Now a one-off for |ltcmd.dtx|: we need to make \cs{NewDocumentCommand} % not complain on an already existing command, but it has to be done % after \cs{NewDocumentCommand} is defined, so this is separate from % the \cs{latexrelease@postltexpl} actions above: % \changes{v1.0o}{2022/02/28} % {Move most redefinitions to right after ltexpl.dtx} % \begin{macrocode} \cs_gset_protected:Npn \latexrelease@postltcmd { \@@_redefines:w \NewDocumentCommand \DeclareDocumentCommand \q_recursion_tail \q_recursion_tail \q_recursion_stop } % \end{macrocode} % % \begin{macrocode} }}% \endgroup % % \end{macrocode} % % \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 silenced error messages. % \changes{v1.0p}{2022/11/14} % {Unsilence \cs{NewMarkClass} error message after rollback (gh/955)} % \begin{macrocode} \msg_redirect_name:nnn { hooks } { exists } { } \msg_redirect_name:nnn { mark } { class-already-defined } { } % \end{macrocode} % % \begin{macrocode} %<@@=> \ExplSyntaxOff}% % % \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} %<*fixltx2e> \IncludeInRelease{2015/01/01}{\fixltxe}{Old fixltx2e package} \NeedsTeXFormat{LaTeX2e} \PackageWarningNoLine{fixltx2e}{% fixltx2e is not required with releases after 2015\MessageBreak All fixes are now in the LaTeX kernel.\MessageBreak See the latexrelease package for details} \EndIncludeInRelease \IncludeInRelease{0000/00/00}{\fixltxe}{Old fixltx2e package} \def\@outputdblcol{% \if@firstcolumn \global\@firstcolumnfalse \global\setbox\@leftcolumn\copy\@outputbox \splitmaxdepth\maxdimen \vbadness\maxdimen \setbox\@outputbox\vbox{\unvbox\@outputbox\unskip}% \setbox\@outputbox\vsplit\@outputbox to\maxdimen \toks@\expandafter{\topmark}% \xdef\@firstcoltopmark{\the\toks@}% \toks@\expandafter{\splitfirstmark}% \xdef\@firstcolfirstmark{\the\toks@}% \ifx\@firstcolfirstmark\@empty \global\let\@setmarks\relax \else \gdef\@setmarks{% \let\firstmark\@firstcolfirstmark \let\topmark\@firstcoltopmark}% \fi \else \global\@firstcolumntrue \setbox\@outputbox\vbox{% \hb@xt@\textwidth{% \hb@xt@\columnwidth{\box\@leftcolumn \hss}% \hfil {\normalcolor\vrule \@width\columnseprule}% \hfil \hb@xt@\columnwidth{\box\@outputbox \hss}}}% \@combinedblfloats \@setmarks \@outputpage \begingroup \@dblfloatplacement \@startdblcolumn \@whilesw\if@fcolmade \fi{\@outputpage\@startdblcolumn}% \endgroup \fi} \def\end@dblfloat{% \if@twocolumn \@endfloatbox \ifnum\@floatpenalty <\z@ \@largefloatcheck \global\dp\@currbox1sp % \@cons\@currlist\@currbox \ifnum\@floatpenalty <-\@Mii \penalty -\@Miv \@tempdima\prevdepth \vbox{}% \prevdepth\@tempdima \penalty\@floatpenalty \else \vadjust{\penalty -\@Miv \vbox{}\penalty\@floatpenalty}\@Esphack \fi \fi \else \end@float \fi } \def\@testwrongwidth #1{% \ifdim\dp#1=\f@depth \else \global\@testtrue \fi} \let\f@depth\z@ \def\@dblfloatplacement{\global\@dbltopnum\c@dbltopnumber \global\@dbltoproom \dbltopfraction\@colht \@textmin \@colht \advance \@textmin -\@dbltoproom \@fpmin \dblfloatpagefraction\textheight \@fptop \@dblfptop \@fpsep \@dblfpsep \@fpbot \@dblfpbot \def\f@depth{1sp}} \def \@doclearpage {% \ifvoid\footins \setbox\@tempboxa\vsplit\@cclv to\z@ \unvbox\@tempboxa \setbox\@tempboxa\box\@cclv \xdef\@deferlist{\@toplist\@botlist\@deferlist}% \global \let \@toplist \@empty \global \let \@botlist \@empty \global \@colroom \@colht \ifx \@currlist\@empty \else \@latexerr{Float(s) lost}\@ehb \global \let \@currlist \@empty \fi \@makefcolumn\@deferlist \@whilesw\if@fcolmade \fi{\@opcol\@makefcolumn\@deferlist}% \if@twocolumn \if@firstcolumn \xdef\@deferlist{\@dbltoplist\@deferlist}% \global \let \@dbltoplist \@empty \global \@colht \textheight \begingroup \@dblfloatplacement \@makefcolumn\@deferlist \@whilesw\if@fcolmade \fi{\@outputpage \@makefcolumn\@deferlist}% \endgroup \else \vbox{}\clearpage \fi \fi \ifx\@deferlist\@empty \else\clearpage \fi \else \setbox\@cclv\vbox{\box\@cclv\vfil}% \@makecol\@opcol \clearpage \fi } \def \@startdblcolumn {% \@tryfcolumn \@deferlist \if@fcolmade \else \begingroup \let \reserved@b \@deferlist \global \let \@deferlist \@empty \let \@elt \@sdblcolelt \reserved@b \endgroup \fi } \def\@addtonextcol{% \begingroup \@insertfalse \@setfloattypecounts \ifnum \@fpstype=8 \else \ifnum \@fpstype=24 \else \@flsettextmin \@reqcolroom \ht\@currbox \advance \@reqcolroom \@textmin \ifdim \@colroom>\@reqcolroom \@flsetnum \@colnum \ifnum\@colnum>\z@ \@bitor\@currtype\@deferlist \@testwrongwidth\@currbox \if@test \else \@addtotoporbot \fi \fi \fi \fi \fi \if@insert \else \@cons\@deferlist\@currbox \fi \endgroup } \def\@addtodblcol{% \begingroup \@insertfalse \@setfloattypecounts \@getfpsbit \tw@ \ifodd\@tempcnta \@flsetnum \@dbltopnum \ifnum \@dbltopnum>\z@ \@tempswafalse \ifdim \@dbltoproom>\ht\@currbox \@tempswatrue \else \ifnum \@fpstype<\sixt@@n \advance \@dbltoproom \@textmin \ifdim \@dbltoproom>\ht\@currbox \@tempswatrue \fi \advance \@dbltoproom -\@textmin \fi \fi \if@tempswa \@bitor \@currtype \@deferlist \@testwrongwidth\@currbox \if@test \else \@tempdima -\ht\@currbox \advance\@tempdima -\ifx \@dbltoplist\@empty \dbltextfloatsep \else \dblfloatsep \fi \global \advance \@dbltoproom \@tempdima \global \advance \@colht \@tempdima \global \advance \@dbltopnum \m@ne \@cons \@dbltoplist \@currbox \@inserttrue \fi \fi \fi \fi \if@insert \else \@cons\@deferlist\@currbox \fi \endgroup } \def \@addtocurcol {% \@insertfalse \@setfloattypecounts \ifnum \@fpstype=8 \else \ifnum \@fpstype=24 \else \@flsettextmin \advance \@textmin \@textfloatsheight \@reqcolroom \@pageht \ifdim \@textmin>\@reqcolroom \@reqcolroom \@textmin \fi \advance \@reqcolroom \ht\@currbox \ifdim \@colroom>\@reqcolroom \@flsetnum \@colnum \ifnum \@colnum>\z@ \@bitor\@currtype\@deferlist \@testwrongwidth\@currbox \if@test \else \@bitor\@currtype\@botlist \if@test \@addtobot \else \ifodd \count\@currbox \advance \@reqcolroom \intextsep \ifdim \@colroom>\@reqcolroom \global \advance \@colnum \m@ne \global \advance \@textfloatsheight \ht\@currbox \global \advance \@textfloatsheight 2\intextsep \@cons \@midlist \@currbox \if@nobreak \nobreak \@nobreakfalse \everypar{}% \else \addpenalty \interlinepenalty \fi \vskip \intextsep \box\@currbox \penalty\interlinepenalty \vskip\intextsep \ifnum\outputpenalty <-\@Mii \vskip -\parskip\fi \outputpenalty \z@ \@inserttrue \fi \fi \if@insert \else \@addtotoporbot \fi \fi \fi \fi \fi \fi \fi \if@insert \else \@resethfps \@cons\@deferlist\@currbox \fi } \def\@xtryfc #1{% \@next\reserved@a\@trylist{}{}% \@currtype \count #1% \divide\@currtype\@xxxii \multiply\@currtype\@xxxii \@bitor \@currtype \@failedlist \@testfp #1% \@testwrongwidth #1% \ifdim \ht #1>\@colht \@testtrue \fi \if@test \@cons\@failedlist #1% \else \@ytryfc #1% \fi} \def\@ztryfc #1{% \@tempcnta\count #1% \divide\@tempcnta\@xxxii \multiply\@tempcnta\@xxxii \@bitor \@tempcnta {\@failedlist \@flfail}% \@testfp #1% \@testwrongwidth #1% \@tempdimb\@tempdima \advance\@tempdimb\ht #1% \advance\@tempdimb\@fpsep \ifdim \@tempdimb >\@colht \@testtrue \fi \if@test \@cons\@flfail #1% \else \@cons\@flsucceed #1% \@tempdima\@tempdimb \fi} \def\@{\spacefactor\@m{}} \def\@tempa#1#2{#1#2\relax} \ifx\setlength\@tempa \def\setlength#1#2{#1 #2\relax} \fi \def\addpenalty#1{% \ifvmode \if@minipage \else \if@nobreak \else \ifdim\lastskip=\z@ \penalty#1\relax \else \@tempskipb\lastskip \begingroup \advance \@tempskipb \ifdim\prevdepth>\maxdepth\maxdepth\else \ifdim \prevdepth = -\@m\p@ \z@ \else \prevdepth \fi \fi \vskip -\@tempskipb \penalty#1% \vskip\@tempskipb \endgroup \vskip -\@tempskipb \vskip \@tempskipb \fi \fi \fi \else \@noitemerr \fi} \def\@fnsymbol#1{% \ifcase#1\or \TextOrMath\textasteriskcentered *\or \TextOrMath \textdagger \dagger\or \TextOrMath \textdaggerdbl \ddagger \or \TextOrMath \textsection \mathsection\or \TextOrMath \textparagraph \mathparagraph\or \TextOrMath \textbardbl \|\or \TextOrMath {\textasteriskcentered\textasteriskcentered}{**}\or \TextOrMath {\textdagger\textdagger}{\dagger\dagger}\or \TextOrMath {\textdaggerdbl\textdaggerdbl}{\ddagger\ddagger}\else \@ctrerr \fi } \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname eTeXversion\endcsname\relax \DeclareRobustCommand\TextOrMath{% \ifmmode \expandafter\@secondoftwo \else \expandafter\@firstoftwo \fi} \protected@edef\TextOrMath#1#2{\TextOrMath{#1}{#2}} \else \protected\expandafter\def\csname TextOrMath\space\endcsname{% \ifmmode \expandafter\@secondoftwo \else \expandafter\@firstoftwo \fi} \edef\TextOrMath#1#2{% \expandafter\noexpand\csname TextOrMath\space\endcsname {#1}{#2}} \fi \def\@esphack{% \relax \ifhmode \spacefactor\@savsf \ifdim\@savsk>\z@ \nobreak \hskip\z@skip % <------ \ignorespaces \fi \fi} \def\@Esphack{% \relax \ifhmode \spacefactor\@savsf \ifdim\@savsk>\z@ \nobreak \hskip\z@skip % <------ \@ignoretrue \ignorespaces \fi \fi} \DeclareRobustCommand\em {\@nomath\em \ifdim \fontdimen\@ne\font >\z@ \eminnershape \else \itshape \fi} \def\eminnershape{\upshape} \DeclareRobustCommand*\textsubscript[1]{% \@textsubscript{\selectfont#1}} \def\@textsubscript#1{% {\m@th\ensuremath{_{\mbox{\fontsize\sf@size\z@#1}}}}} \def\@DeclareMathSizes #1#2#3#4#5{% \@defaultunits\dimen@ #2pt\relax\@nnil \if $#3$% \expandafter\let\csname S@\strip@pt\dimen@\endcsname\math@fontsfalse \else \@defaultunits\dimen@ii #3pt\relax\@nnil \@defaultunits\@tempdima #4pt\relax\@nnil \@defaultunits\@tempdimb #5pt\relax\@nnil \toks@{#1}% \expandafter\xdef\csname S@\strip@pt\dimen@\endcsname{% \gdef\noexpand\tf@size{\strip@pt\dimen@ii}% \gdef\noexpand\sf@size{\strip@pt\@tempdima}% \gdef\noexpand\ssf@size{\strip@pt\@tempdimb}% \the\toks@ }% \fi } \providecommand*\MakeRobust[1]{% \@ifundefined{\expandafter\@gobble\string#1}{% \@latex@error{The control sequence `\string#1' is undefined!% \MessageBreak There is nothing here to make robust}% \@eha }% {% \@ifundefined{\expandafter\@gobble\string#1\space}% {% \expandafter\let\csname \expandafter\@gobble\string#1\space\endcsname=#1% \edef\reserved@a{\string#1}% \def\reserved@b{#1}% \edef\reserved@b{\expandafter\strip@prefix\meaning\reserved@b}% \edef#1{% \ifx\reserved@a\reserved@b \noexpand\x@protect\noexpand#1% \fi \noexpand\protect\expandafter\noexpand \csname\expandafter\@gobble\string#1\space\endcsname}% }% {\@latex@info{The control sequence `\string#1' is already robust}}% }% } \MakeRobust\( \MakeRobust\) \MakeRobust\[ \MakeRobust\] \MakeRobust\makebox \MakeRobust\savebox \MakeRobust\framebox \MakeRobust\parbox \MakeRobust\rule \MakeRobust\raisebox \def\@xfloat #1[#2]{% \@nodocument \def \@captype {#1}% \def \@fps {#2}% \@onelevel@sanitize \@fps \def \reserved@b {!}% \ifx \reserved@b \@fps \@fpsadddefault \else \ifx \@fps \@empty \@fpsadddefault \fi \fi \ifhmode \@bsphack \@floatpenalty -\@Mii \else \@floatpenalty-\@Miii \fi \ifinner \@parmoderr\@floatpenalty\z@ \else \@next\@currbox\@freelist {% \@tempcnta \sixt@@n \expandafter \@tfor \expandafter \reserved@a \expandafter :\expandafter =\@fps \do {% \if \reserved@a h% \ifodd \@tempcnta \else \advance \@tempcnta \@ne \fi \else\if \reserved@a t% \@setfpsbit \tw@ \else\if \reserved@a b% \@setfpsbit 4% \else\if \reserved@a p% \@setfpsbit 8% \else\if \reserved@a !% \ifnum \@tempcnta>15 \advance\@tempcnta -\sixt@@n\relax \fi \else \@latex@error{Unknown float option `\reserved@a'}% {Option `\reserved@a' ignored and `p' used.}% \@setfpsbit 8% \fi\fi\fi\fi\fi }% \@tempcntb \csname ftype@\@captype \endcsname \multiply \@tempcntb \@xxxii \advance \@tempcnta \@tempcntb \global \count\@currbox \@tempcnta }% \@fltovf \fi \global \setbox\@currbox \color@vbox \normalcolor \vbox \bgroup \hsize\columnwidth \@parboxrestore \@floatboxreset } \def\@stpelt#1{\global\csname c@#1\endcsname \m@ne\stepcounter{#1}} \EndIncludeInRelease % % \end{macrocode} % % \Finale % \endinput