diff options
Diffstat (limited to 'macros/latex-dev/base/ltfilehook.dtx')
-rw-r--r-- | macros/latex-dev/base/ltfilehook.dtx | 254 |
1 files changed, 199 insertions, 55 deletions
diff --git a/macros/latex-dev/base/ltfilehook.dtx b/macros/latex-dev/base/ltfilehook.dtx index ecb499f347..b5b5de629a 100644 --- a/macros/latex-dev/base/ltfilehook.dtx +++ b/macros/latex-dev/base/ltfilehook.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment %% -%% File: ltfilehook.dtx (C) Copyright 2020 Frank Mittelbach, +%% File: ltfilehook.dtx (C) Copyright 2020,2021 Frank Mittelbach, %% Phelype Oleinik & LaTeX Team % % This file is part of the LaTeX base system. @@ -31,8 +31,8 @@ %%% From File: ltfilehook.dtx % % \begin{macrocode} -\providecommand\ltfilehookversion{v1.0c} -\providecommand\ltfilehookdate{2020/10/18} +\providecommand\ltfilehookversion{v1.0e} +\providecommand\ltfilehookdate{2021/01/07} % \end{macrocode} % %<*driver> @@ -75,7 +75,7 @@ % \ltfilehookversion\ dated \ltfilehookdate, \copyright\ \LaTeX\ % Project.}} % -% \author{Frank Mittelbach} +% \author{Frank Mittelbach, Phelype Oleinik, \LaTeX{} Project Team} % % \maketitle % @@ -339,9 +339,12 @@ % % \subsection{High-level interfaces for \LaTeX{}} % -% We do not provide any high-level \LaTeX{} commands (like -% \pkg{filehook} or \pkg{scrlfile} do) but think that for package -% writers the commands from for hook management are sufficient. +% We do not provide any additional wrappers around the hooks (like +% \pkg{filehook} or \pkg{scrlfile} do) because we believe that for +% package writers the high-level commands from the hook management, +% e.g., \cs{AddToHook}, etc.\ +% are sufficient and in fact easier to work with, given that the hooks +% have consistent naming conventions. % % % @@ -371,7 +374,7 @@ % \em % Please do not misuse this functionality and replace a file with % another unless if really needed and only if the new version is -% implementating the same functionality as the original one! +% implementing the same functionality as the original one! % \end{quote} % \end{function} % @@ -514,11 +517,10 @@ % \begin{macro}{ % \@@_file_parse_full_name:nN, % \@@_full_name:nn, -% \@@_set_curr_file_assign:nnnNN % } % A utility macro to trigger \pkg{expl3}'s file-parsing and lookup, % and return a normalized representation of the file name. If the -% queried file doesn't exist, no normalisation takes place. +% queried file doesn't exist, no normalization takes place. % The output of \cs{@@_file_parse_full_name:nN} is passed on to the % |#2|---a 3-argument macro that takes the \meta{path}, \meta{base}, % and \meta{ext} parts of the file name. @@ -615,6 +617,62 @@ % \begin{macrocode} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +% \end{macrocode} +% +% When rolling forward the following expl3 functions may not be defined. +% If we roll back the code does nothing. +% \changes{v1.0d}{2020/11/24}{Support for roll forward (gh/434)} +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{2020/10/01}% +%<latexrelease> {\file_parse_full_name_apply:nN}{Roll forward help}% +%<latexrelease> +%<latexrelease>\ExplSyntaxOn +%<latexrelease>\cs_if_exist:NF\file_parse_full_name_apply:nN +%<latexrelease>{ +%<latexrelease>\cs_new:Npn \file_parse_full_name_apply:nN #1 +%<latexrelease> { +%<latexrelease> \exp_args:Ne \__file_parse_full_name_auxi:nN +%<latexrelease> { \__kernel_file_name_sanitize:n {#1} } +%<latexrelease> } +%<latexrelease>\cs_new:Npn \__file_parse_full_name_auxi:nN #1 +%<latexrelease> { +%<latexrelease> \__file_parse_full_name_area:nw { } #1 +%<latexrelease> / \s__file_stop +%<latexrelease> } +%<latexrelease>\cs_new:Npn \__file_parse_full_name_area:nw #1 #2 / #3 \s__file_stop +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#3} +%<latexrelease> { \__file_parse_full_name_base:nw { } #2 . \s__file_stop {#1} } +%<latexrelease> { \__file_parse_full_name_area:nw { #1 / #2 } #3 \s__file_stop } +%<latexrelease> } +%<latexrelease>\cs_new:Npn \__file_parse_full_name_base:nw #1 #2 . #3 \s__file_stop +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#3} +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#1} +%<latexrelease> { +%<latexrelease> \tl_if_empty:nTF {#2} +%<latexrelease> { \__file_parse_full_name_tidy:nnnN { } { } } +%<latexrelease> { \__file_parse_full_name_tidy:nnnN { .#2 } { } } +%<latexrelease> } +%<latexrelease> { \__file_parse_full_name_tidy:nnnN {#1} { .#2 } } +%<latexrelease> } +%<latexrelease> { \__file_parse_full_name_base:nw { #1 . #2 } #3 \s__file_stop } +%<latexrelease> } +%<latexrelease>\cs_new:Npn \__file_parse_full_name_tidy:nnnN #1 #2 #3 #4 +%<latexrelease> { +%<latexrelease> \exp_args:Nee #4 +%<latexrelease> { +%<latexrelease> \str_if_eq:nnF {#3} { / } { \use_none:n } +%<latexrelease> #3 \prg_do_nothing: +%<latexrelease> } +%<latexrelease> { \use_none:n #1 \prg_do_nothing: } +%<latexrelease> {#2} +%<latexrelease> } +%<latexrelease>} +%<latexrelease>\ExplSyntaxOff +%<latexrelease> +%<latexrelease>\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % @@ -643,6 +701,8 @@ % kernel we do this only now and not in \texttt{ltfiles}. % % \begin{macro}{\InputIfFileExists} +% \begin{macro}{\@input@file@exists@with@hooks} +% \begin{macro}{\unqu@tefilef@und} % \cs{InputIfFileExists} loads any file if it is available so we % have to add the hooks \texttt{file/before} and % \texttt{file/after} in the right places. If the file doesn't @@ -653,19 +713,38 @@ %<latexrelease> {\InputIfFileExists}{Hook management (files)}% %<*2ekernel|latexrelease> % \end{macrocode} -% +% % \begin{macrocode} \let\InputIfFileExists\@undefined \DeclareRobustCommand \InputIfFileExists[2]{% \IfFileExists{#1}% {% - \@expl@@@filehook@file@push@@ - \@filehook@set@CurrentFile + \@expl@@@filehook@file@push@@ + \@filehook@set@CurrentFile +% \end{macrocode} +% We pre-expand \cs{@filef@und} so that in case another file is +% loaded in the true branch of \cs{InputIfFileExists}, these don't +% change their value meanwhile. This isn't a worry with +% \cs[no-index]{CurrentFile...} because they are kept in a stack. +% +% \changes{v1.0d}{2020/11/20} +% {Move loading to \cs{@input@file@exists@with@hooks} and expand +% \cs{@filef@und} to avoid getting the wrong file name in the case of +% a substitution.} +% \begin{macrocode} + \expandafter\@swaptwoargs\expandafter + {\expandafter\@input@file@exists@with@hooks + \expandafter{\@filef@und}}% + {#2}% + \@expl@@@filehook@file@pop@@ + }% +} +\def\@input@file@exists@with@hooks#1{% % \end{macrocode} % If the file exists then \cs{CurrentFile} holds its name. But we % can't rely on that still being true after the file has been % processed. Thus for using the name in the file hooks we need to -% preserve the name and then restored it for the +% preserve the name and then restore it for the % \texttt{file/after/...} hook. % % The hook always refers to the file requested by the user. The hook @@ -682,34 +761,54 @@ % the file name and extension are returned so that file hooks can % refer to the file by their name only. The path to the file is % returned in \cs{CurrentFilePath}. -% \begin{macrocode} - \edef\reserved@a{\@filef@und - \@expl@@@filehook@file@pop@assign@@nnnn - {\CurrentFilePathUsed}% - {\CurrentFileUsed}% - {\CurrentFilePath}% - {\CurrentFile}}% - \expandafter\@swaptwoargs\expandafter - {\reserved@a}% - {% - #2% - \@addtofilelist{#1}% - \UseHook{file/before}% +% \changes{v1.0e}{2021/01/07}{Restore \cs[no-index]{CurrentFile(Path)(Used)} +% after the input (gh/464)} +% \begin{macrocode} + \edef\reserved@a{% + \@expl@@@filehook@file@pop@assign@@nnnn + {\CurrentFilePathUsed}% + {\CurrentFileUsed}% + {\CurrentFilePath}% + {\CurrentFile}}% + \expandafter\@swaptwoargs\expandafter{\reserved@a}% +% \end{macrocode} +% +% Before adding to the file list we need to make all (letter) characters +% catcode~11, because several packages use constructions like +% \begin{verbatim} +% \filename@parse{<filename>} +% \ifx\filename@ext\@clsextension +% ... +% \fi +% \end{verbatim} +% and that doesn't work if \cs{filename@ext} is \cs{detokenize}d. +% Making \cs{@clsextension} a string doesn't help much because some +% packages define their own \cs[no-index]{<prefix>@someextension} with +% normal catcodes. This is not entirely correct because packages loaded +% (somehow) with catcode~12 alphabetic tokens (say, as the result of +% a \cs{string} or \cs{detokenize} command, or from a \TeX{} string like +% \cs{jobname}) will have these character tokens incorrectly turned into +% letter tokens. This however is rare, so we'll go for the all-letters +% approach (grepping the packages in \TeX{} Live didn't bring up any +% obvious candidate for breaking with this catcode change). +% \begin{macrocode} + {\edef\reserved@a{\unqu@tefilef@und#1\@nil}% + \@addtofilelist{\string@makeletter\reserved@a}% + \UseHook{file/before}% % \end{macrocode} % The current file name is available in \cs{CurrentFile} so we use % that in the specific hook. % \begin{macrocode} - \UseHook{file/before/\CurrentFile}% - \@@input - }% + \UseHook{file/before/\CurrentFile}% + \@@input #1% <- trailing space comes from \@filef@und + }% % \end{macrocode} -% And it is restored here so we can use it once more. +% And here, \cs{CurrentFile} is restored +% (by \cs{@expl@@@filehook@file@pop@assign@@nnnn}) so we can use it once more. % \begin{macrocode} - \UseHook{file/after/\CurrentFile}% - \UseHook{file/after}% - \@expl@@@filehook@file@pop@@ - }% -} + \UseHook{file/after/\CurrentFile}% + \UseHook{file/after}} +\def\unqu@tefilef@und"#1" \@nil{#1} %<latexrelease>\EndIncludeInRelease %</2ekernel|latexrelease> % \end{macrocode} @@ -737,6 +836,8 @@ %<latexrelease> {% %<latexrelease> \expandafter\@swaptwoargs\expandafter %<latexrelease> {\@filef@und}{#2\@addtofilelist{#1}\@@input}}} +%<latexrelease>\let\@input@file@exists@with@hooks\@undefined +%<latexrelease>\let\unqu@tefilef@und\@undefined %<latexrelease>\EndIncludeInRelease % \end{macrocode} % @@ -746,10 +847,14 @@ %<latexrelease>\long\def \InputIfFileExists#1#2{% %<latexrelease> \IfFileExists{#1}% %<latexrelease> {#2\@addtofilelist{#1}\@@input \@filef@und}} +%<latexrelease>\let\@input@file@exists@with@hooks\@undefined +%<latexrelease>\let\unqu@tefilef@und\@undefined %<latexrelease>\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} % % % @@ -772,8 +877,8 @@ % % % \begin{macro}{\@@_subst_add:nn,\@@_subst_remove:n, -% \@@_subst_file_normalize:n,\@@_subst_empty_name_chk:NN} -% \cs{@@_substitution_lthooadd:nn} declares a file substitution by +% \@@_subst_file_normalize:Nn,\@@_subst_empty_name_chk:NN} +% \cs{@@_subst_add:nn} declares a file substitution by % doing a (global) definition of the form % |\def|\cs{@file-subst@\meta{file}}|{|\meta{replacement}|}|. % The file names are properly sanitised, and normalized with the same @@ -787,8 +892,12 @@ \group_begin: \cs_set:cpx { } { \exp_not:o { \cs:w\cs_end: } } \int_set:Nn \tex_escapechar:D { -1 } - \cs_gset:cpx { @file-subst@ \@@_subst_file_normalize:n {#1} } - { \@@_subst_file_normalize:n {#2} } + \cs_gset:cpx + { + @file-subst@ + \@@_subst_file_normalize:Nn \use_ii_iii:nnn {#1} + } + { \@@_subst_file_normalize:Nn \@@_file_name_compose:nnn {#2} } \group_end: } \cs_new_protected:Npn \@@_subst_remove:n #1 @@ -796,14 +905,18 @@ \group_begin: \cs_set:cpx { } { \exp_not:o { \cs:w\cs_end: } } \int_set:Nn \tex_escapechar:D { -1 } - \cs_undefine:c { @file-subst@ \@@_subst_file_normalize:n {#1} } + \cs_undefine:c + { + @file-subst@ + \@@_subst_file_normalize:Nn \use_ii_iii:nnn {#1} + } \group_end: } -\cs_new:Npn \@@_subst_file_normalize:n #1 +\cs_new:Npn \@@_subst_file_normalize:Nn #1 #2 { \exp_after:wN \@@_subst_empty_name_chk:NN \cs:w \exp_after:wN \cs_end: - \cs:w \@@_file_parse_full_name:nN {#1} \use_ii_iii:nnn \cs_end: + \cs:w \@@_file_parse_full_name:nN {#2} #1 \cs_end: } \cs_new:Npn \@@_subst_empty_name_chk:NN #1 #2 { \if_meaning:w #1 #2 .tex \else: \token_to_str:N #2 \fi: } @@ -847,12 +960,18 @@ %<latexrelease>\EndIncludeInRelease % \end{macrocode} % +% We are not fully rolling back the file substitutions in case a +% rollback encounters a package that contains them, but is itself +% not setup for rollback. So we just bypass them and hope for the +% best. +% \changes{v1.0d}{2020/12/04}{Don't drop file substitution commands on +% rollback} % \begin{macrocode} %<latexrelease>\IncludeInRelease{0000/00/00}% %<latexrelease> {\declare@file@substitution}{File substitution}% %<latexrelease> -%<latexrelease>\let \declare@file@substitution \@undefined -%<latexrelease>\let \undeclare@file@substitution \@undefined +%<latexrelease>\let \declare@file@substitution \@gobbletwo +%<latexrelease>\let \undeclare@file@substitution \@gobble %<latexrelease> %<latexrelease>\EndIncludeInRelease %<*2ekernel> @@ -878,7 +997,7 @@ % A file name is built using % \cs{expandafter}\cs{string}\cs{csname}\meta{filename}\cs{endcsname} % to avoid expanding utf8 active characters. The \cs{csname} expands -% the normalisation machinery and the routine to resolve a file +% the normalization machinery and the routine to resolve a file % substitution, returning a control sequence with the same name as the % file. % @@ -908,7 +1027,7 @@ % actual loaded file. \cs{@curr@file} is resolved first, to check if % a substitution happens. If it doesn't, % \cs{@expl@@@filehook@if@file@replaced@@TF} short-cuts and just copies -% \cs{@curr@file}, otherwise the full normalisation procedure is +% \cs{@curr@file}, otherwise the full normalization procedure is % executed. % % At this stage the file name is parsed and normalized, but if the @@ -929,6 +1048,7 @@ \if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file@reqd \fi}% {\if@tempswa \@expl@@@filehook@drop@extension@@N\@curr@file \fi \global\let\@curr@file@reqd\@curr@file}% + \@expl@@@filehook@clear@replacement@flag@@ \endgroup} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease @@ -1059,7 +1179,7 @@ % \begin{macro}{\@@_resolve_file_subst:w} % \begin{macro}{\@@_normalize_file_name:w} % \begin{macro}{\@@_file_name_compose:nnn} -% Start by sanitising the file with \cs{@@_file_parse_full_name:nN} +% Start by sanitizing the file with \cs{@@_file_parse_full_name:nN} % then do \cs{@@_file_subst_begin:nnn}\Arg{path}\Arg{name}\Arg{ext}. % \begin{macrocode} %</2ekernel> @@ -1079,18 +1199,25 @@ % % \begin{macro}{flag @@_file_replaced} % \begin{macro}{\@@_if_file_replaced:TF} +% \begin{macro}{\@@_clear_replacement_flag:} % Since the file replacement is done expandably in a \cs{csname}, use % a flag to remember if a substitution happened. We use this in % \cs{set@curr@file} to short-circuit some of it in case no % substitution happened (by far the most common case, so it's worth -% optimising). +% optimizing). The flag raised during the file substitution algorithm +% must be explicitly cleared after the \cs{@@_if_file_replaced:TF} +% conditional is no longer needed, otherwise further uses of +% \cs{@@_if_file_replaced:TF} will wrongly return true. % \begin{macrocode} \flag_new:n { @@_file_replaced } \cs_new:Npn \@@_if_file_replaced:TF #1 #2 - { \flag_if_raised:nTF { @@_file_replaced } {#1} {#2} } + { \flag_if_raised:nTF { @@_file_replaced } {#1} {#2} } +\cs_new_protected:Npn \@@_clear_replacement_flag: + { \flag_clear:n { @@_file_replaced } } % \end{macrocode} % \end{macro} % \end{macro} +% \end{macro} % % \begin{macro}{\@@_file_subst_begin:nnn} % First off, start by checking if the current file ($\meta{name} + @@ -1140,8 +1267,8 @@ % the macro below, \cs{@@_file_subst_loop:cc} is called with % \cs[no-index]{@file-subst@\meta{file}} and % \cs[no-index]{@file-subst@\cs[no-index]{@file-subst@\meta{file}}}; -% that is, the substitution of \meta{file} and the substution of that -% substution: the Tortoise walks one step while the Hare walks two. +% that is, the substitution of \meta{file} and the substitution of that +% substitution: the Tortoise walks one step while the Hare walks two. % % Within \cs{@@_file_subst_loop:NN} the two substitutions are % compared, and if they lead to the same file it means that there is @@ -1343,7 +1470,12 @@ \cs_new_eq:NN \@expl@@@filehook@if@file@replaced@@TF \__filehook_if_file_replaced:TF % \end{macrocode} -% +% +% \begin{macrocode} +\cs_new_eq:NN \@expl@@@filehook@clear@replacement@flag@@ + \__filehook_clear_replacement_flag: +% \end{macrocode} +% % \begin{macrocode} \cs_new_eq:NN \@expl@@@filehook@drop@extension@@N \__filehook_drop_extension:N @@ -1368,10 +1500,22 @@ % \begin{macrocode} \ExplSyntaxOff % \end{macrocode} -% +% +% This one specifically has to be undefined because it is left over in +% the input stream from \cs{InputIfFileExists} and executed when +% \pkg{latexrelease} is loaded. It cannot be \cs{let} to \cs{@undefined} +% otherwise it would error as well, so it is \cs{let} to \cs{relax} to +% be silently ignored when loading \cs{latexrelease}. +% \changes{v1.0e}{2021/01/07}{Added rollback for this case to avoid +% spurious errors (part of gh/463)} % \begin{macrocode} %</2ekernel|latexrelease> %<latexrelease>\EndIncludeInRelease +%<latexrelease> +%<latexrelease>\IncludeInRelease{0000/00/00}% +%<latexrelease> {\@expl@@@filehook@if@no@extension@@nTF}{2e tmp interfaces}% +%<latexrelease>\let\@expl@@@filehook@file@pop@@\relax +%<latexrelease>\EndIncludeInRelease %<*2ekernel> % \end{macrocode} % @@ -1410,7 +1554,7 @@ % \end{macro} % % \begin{macro}{\@@_log_file_record:n} -% This macro is responsible for increasing and decresing the file +% This macro is responsible for increasing and decreasing the file % nesting level, as well as printing to the log. The argument is % either |STOPTART| or |STOP| and the action it takes on the nesting % integer depends on that. |