summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3file.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-07 03:00:49 +0000
committerNorbert Preining <norbert@preining.info>2019-09-07 03:00:49 +0000
commit7345b8683c7cbeaedf53e992fda0b3fa6141084e (patch)
tree3246f6574e0921c2111b93b76720f54034238185 /macros/latex/contrib/l3kernel/l3file.dtx
parentbd3ecfd29a422ad4d7f8f1e119ddc518494e567a (diff)
CTAN sync 201909070300
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3file.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3file.dtx615
1 files changed, 453 insertions, 162 deletions
diff --git a/macros/latex/contrib/l3kernel/l3file.dtx b/macros/latex/contrib/l3kernel/l3file.dtx
index 017c9954f7..dce560e4f6 100644
--- a/macros/latex/contrib/l3kernel/l3file.dtx
+++ b/macros/latex/contrib/l3kernel/l3file.dtx
@@ -44,7 +44,7 @@
% }^^A
% }
%
-% \date{Released 2019-08-25}
+% \date{Released 2019-09-05}
%
% \maketitle
%
@@ -625,6 +625,19 @@
% \cs{q_no_value} in the case that the file does not exist.
% \end{function}
%
+% \begin{function}[added = 2019-09-03, rEXP]{\file_full_name:n, \file_full_name:V}
+% \begin{syntax}
+% \cs{file_full_name:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} in the path as detailed for
+% \cs{file_if_exist:nTF}, and if found leaves the
+% fully-qualified name of the file, \emph{i.e.}~the path and file name,
+% in the input stream.
+% This includes an extension |.tex| when the given \meta{file name}
+% has no extension but the file found has that extension.
+% If the file is not found on the path, the expansion is empty.
+% \end{function}
+%
% \begin{function}[added = 2017-06-23, updated = 2017-06-26]
% {\file_parse_full_name:nNNN, \file_parse_full_name:VNNN}
% \begin{syntax}
@@ -650,21 +663,39 @@
% quote (|"|) characters or is surrounded by a pair of quotes.
% \end{function}
%
-% \begin{function}[noTF, added = 2017-07-11, updated = 2019-02-16]
-% {\file_get_mdfive_hash:nN}
+% \begin{function}[rEXP, added = 2019-09-03]{\file_mdfive_hash:n}
% \begin{syntax}
-% \cs{file_get_mdfive_hash:nN} \Arg{file name} \meta{tl var}
+% \cs{file_mdfive_hash:nN} \Arg{file name}
% \end{syntax}
% Searches for \meta{file name} using the current \TeX{} search
% path and the additional paths controlled by \cs{l_file_search_path_seq}.
-% If found, sets the \meta{tl var} to the MD5 sum generated from the
-% content of the file. The file is read as bytes, which means that in
+% It then expands to leave the MD5 sum generated from the contents of
+% the file in the input stream. The file is read as bytes, which means that in
% contrast to most \TeX{} behaviour there will be a difference in result
% depending on the line endings used in text files. The same file will
% produce the same result between different engines: the algorithm used
-% is the same in all cases.
-% Where the file is not found, the \meta{tl var} will set to
-% \cs{q_no_value}.
+% is the same in all cases. When the file is not found, the result of
+% expansion is empty.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-11, updated = 2019-02-16]
+% {\file_get_mdfive_hash:nN}
+% \begin{syntax}
+% \cs{file_get_mdfive_hash:nN} \Arg{file name} \meta{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_mdfive_hash:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-03]{\file_size:n}
+% \begin{syntax}
+% \cs{file_size:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths controlled by \cs{l_file_search_path_seq}.
+% It then expands to leave the size of the file in bytes in the input stream.
+% When the file is not found, the result of expansion is empty.
% \end{function}
%
% \begin{function}[noTF, added = 2017-07-09, updated = 2019-02-16]
@@ -672,10 +703,24 @@
% \begin{syntax}
% \cs{file_get_size:nN} \Arg{file name} \meta{tl var}
% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_size:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-03]{\file_timestamp:n}
+% \begin{syntax}
+% \cs{file_timestamp:n} \Arg{file name}
+% \end{syntax}
% Searches for \meta{file name} using the current \TeX{} search
% path and the additional paths controlled by \cs{l_file_search_path_seq}.
-% If found, sets the \meta{tl var} to the size of the file in bytes.
-% Where the file is not found, the \meta{tl var} will set to \cs{q_no_value}.
+% It then expands to leave the modification timestamp of
+% the file in the input stream. The timestamp is of the form
+% |D:|\meta{year}\meta{month}\meta{day}\meta{hour}^^A
+% \meta{minute}\meta{second}\meta{offset}, where the latter may be |Z|
+% (UTC) or \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|.
+% When the file is not found, the result of expansion is empty.
% This is not available in older versions of \XeTeX{}.
% \end{function}
%
@@ -684,14 +729,9 @@
% \begin{syntax}
% \cs{file_get_timestamp:nN} \Arg{file name} \meta{tl var}
% \end{syntax}
-% Searches for \meta{file name} using the current \TeX{} search
-% path and the additional paths controlled by \cs{l_file_search_path_seq}.
-% If found, sets the \meta{tl var} to the modification timestamp of
-% the file in the form |D:|\meta{year}\meta{month}\meta{day}\meta{hour}^^A
-% \meta{minute}\meta{second}\meta{offset}, where the latter may be |Z|
-% (UTC) or \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|.
-% Where the file is not found, the \meta{tl var} will be set to
-% \cs{q_no_value}.
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_timestamp:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
% This is not available in older versions of \XeTeX{}.
% \end{function}
%
@@ -1327,7 +1367,11 @@
% \begin{macrocode}
\cs_new_protected:Npn \iow_open:Nn #1#2
{
- \__kernel_file_name_sanitize:nN {#2} \l_@@_file_name_tl
+ \tl_set:Nx \l_@@_file_name_tl
+ {
+ \exp_args:Ne \__kernel_file_name_quote:n
+ { \__kernel_file_name_sanitize:n {#2} }
+ }
\iow_close:N #1
\seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
{ \@@_open_stream:NV #1 \l_@@_file_name_tl }
@@ -2145,13 +2189,6 @@
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{\g_@@_internal_ior}
-% A reserved stream to test for file existence.
-% \begin{macrocode}
-\ior_new:N \g_@@_internal_ior
-% \end{macrocode}
-% \end{variable}
-%
% \begin{variable}
% {
% \g_file_curr_dir_str ,
@@ -2266,46 +2303,113 @@
% \end{macrocode}
% \end{variable}
%
-% \begin{macro}{\__kernel_file_name_sanitize:nN}
-% \begin{macro}{\@@_name_quote:nN}
-% \begin{macro}{\@@_name_sanitize_aux:n}
-% For converting a token list to a string where active characters are treated
-% as strings from the start. The logic to the quoting normalisation is the
-% same as used by \texttt{lualatexquotejobname}: check for balanced |"|, and
-% assuming they balance strip all of them out before quoting the entire name
-% if it contains spaces.
+% \begin{macro}[rEXP]{\__kernel_file_name_sanitize:n}
+% \begin{macro}[rEXP]{\__kernel_file_name_expand_loop:w}
+% \begin{macro}[rEXP]{\__kernel_file_name_expand_N_type:Nw}
+% \begin{macro}[rEXP]{\__kernel_file_name_expand_group:nw}
+% \begin{macro}[rEXP]{\__kernel_file_name_expand_space:w}
+% Expanding the file name without expanding active characters is done
+% using the same token-by-token approach as for example case changing.
+% The finale outcome only need be \texttt{e}-type expandable, so there
+% is no need for the shuffling that is seen in other locations.
% \begin{macrocode}
-\cs_new_protected:Npn \__kernel_file_name_sanitize:nN #1#2
+\cs_new:Npn \__kernel_file_name_sanitize:n #1
{
- \group_begin:
- \seq_map_inline:Nn \l_char_active_seq
- {
- \tl_set:Nx \l_@@_internal_tl { \iow_char:N ##1 }
- \char_set_active_eq:NN ##1 \l_@@_internal_tl
- }
- \tl_set:Nx \l_@@_internal_tl {#1}
- \exp_args:NNNV \group_end:
- \str_set:Nn #2 \l_@@_internal_tl
+ \__kernel_file_name_expand_loop:w #1
+ \q_recursion_tail \q_recursion_stop
}
-\cs_new_protected:Npn \@@_name_quote:nN #1#2
+\cs_new:Npn \__kernel_file_name_expand_loop:w #1 \q_recursion_stop
{
- \str_set:Nx #2 {#1}
- \int_if_even:nF
- { 0 \tl_map_function:NN #2 \@@_name_quote_aux:n }
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__kernel_file_name_expand_N_type:Nw }
{
- \__kernel_msg_error:nnx
- { kernel } { unbalanced-quote-in-filename } {#2}
+ \tl_if_head_is_group:nTF {#1}
+ { \__kernel_file_name_expand_group:nw }
+ { \__kernel_file_name_expand_space:w }
+ }
+ #1 \q_recursion_stop
+ }
+\cs_new:Npn \__kernel_file_name_expand_N_type:Nw #1
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \bool_lazy_and:nnTF
+ { \token_if_expandable_p:N #1 }
+ {
+ \bool_not_p:n
+ {
+ \bool_lazy_any_p:n
+ {
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ { \token_if_active_p:N #1 }
+ }
+ }
+ }
+ { \exp_after:wN \__kernel_file_name_expand_loop:w #1 }
+ {
+ \token_to_str:N #1
+ \__kernel_file_name_expand_loop:w
+ }
+ }
+\cs_new:Npx \__kernel_file_name_expand_group:nw #1
+ {
+ \c_left_brace_str
+ \exp_not:N \__kernel_file_name_expand_loop:w
+ #1
+ \c_right_brace_str
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npx \__kernel_file_name_expand_space:w \c_space_tl
+ {
+ \c_space_tl
+ \exp_not:N \__kernel_file_name_expand_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\__kernel_file_name_quote:n}
+% \begin{macro}[rEXP]{\__kernel_file_name_quote_auxi:nnnw}
+% \begin{macro}[rEXP]{\__kernel_file_name_quote_auxii:nnn}
+% \begin{macro}[rEXP]{\__kernel_file_name_quote_auxiii:nw}
+% Quoting file name uses basically the same approach as for
+% \texttt{luaquotejobname}: count the |"| tokens, remove them then
+% re-add at the extremities.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_file_name_quote:n #1
+ {
+ \__kernel_file_name_quote_auxi:nnnw {#1} { 0 } { }
+ #1 " \q_recursion_tail " \q_recursion_stop
+ }
+\cs_new:Npn \__kernel_file_name_quote_auxi:nnnw #1#2#3#4 "
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4}
+ { \__kernel_file_name_quote_auxii:nnn {#1} {#2} {#3} }
+ \__kernel_file_name_quote_auxi:nnnw {#1} { #2 + 1 } { #3#4 }
+ }
+\cs_new:Npn \__kernel_file_name_quote_auxii:nnn #1#2#3
+ {
+ \int_if_even:nT {#2}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { unbalanced-quote-in-filename } {#1}
}
- \tl_remove_all:Nn #2 { " }
- \tl_if_in:NnT #2 { ~ }
- { \str_set:Nx #2 { " \exp_not:V #2 " } }
+ \__kernel_file_name_quote_auxiii:nw {#3} #3 ~ \q_nil \q_stop
+ }
+\cs_new:Npn \__kernel_file_name_quote_auxiii:nw #1 #2 ~ #3 \q_stop
+ {
+ \quark_if_nil:nTF {#3}
+ { #1 }
+ { "#1" }
}
-\cs_new:Npn \@@_name_quote_aux:n #1
- { \token_if_eq_charcode:NNT #1 " { + 1 } }
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \begin{variable}{\c_@@_marker_tl}
% The same idea as the marker for rescanning token lists: this pair of
@@ -2365,10 +2469,182 @@
% \end{macro}
% \end{macro}
%
+% \begin{macro}{\@@_file_size:n}
+% A copy of the primitive where it's available, or the \LuaTeX{}
+% equivalent if relevant.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_file_size:n \tex_filesize:D
+\sys_if_engine_luatex:T
+ {
+ \cs_gset:Npn \@@_file_size:n #1
+ {
+ \lua_now:e
+ {
+ l3kernel.filesize
+ ( " \lua_escape:e {#1} " )
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\file_full_name:n, \@@_full_name:n}
+% \begin{macro}[rEXP]{\@@_full_name:nn}
+% \begin{macro}[rEXP]{\@@_full_name_aux:nn}
+% \begin{macro}[rEXP]{\@@_full_name_aux:n}
+% \begin{macro}[rEXP]{\@@_file_name_cleanup:w}
+% \begin{macro}[rEXP]{\@@_file_name_end:}
+% \begin{macro}[rEXP]{\@@_file_name_ext_check:n}
+% \begin{macro}[rEXP]
+% {
+% \@@_file_name_ext_check_auxi:nw ,
+% \@@_file_name_ext_check_auxii:nw ,
+% \@@_file_name_ext_check_auxiii:nw
+% }
+% File searching can be carried out if the \tn{pdffilesize} primitive
+% or an equivalent is available. That of course means we need to
+% arrange for everything else to here to be done by expansion too.
+% We start off by sanitizing the name and quoting if required: we
+% may need to remove those quotes, so the raw name is passed too.
+% \begin{macrocode}
+\cs_new:Npn \file_full_name:n #1
+ {
+ \exp_args:Ne \@@_full_name:n
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_new:Npn \@@_full_name:n #1
+ {
+ \exp_args:Ne \@@_full_name:nn
+ { \__kernel_file_name_quote:n {#1} } {#1}
+ }
+% \end{macrocode}
+% First, we check of the file is just here: no mapping so we do not
+% need the break part of the broader auxiliary. We are using the fact
+% that the primitive here returns nothing if the file is entirely absent.
+% For package mode, \tn{input@path} is a token list not a sequence.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name:nn #1#2
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \tl_if_blank:eTF { \@@_file_size:n {#1} }
+ {
+ \seq_map_tokens:Nn \l_file_search_path_seq
+ { \@@_full_name_aux:nn {#2} }
+%<*package>
+ \cs_if_exist:NT \input@path
+ {
+ \tl_map_tokens:Nn \input@path
+ { \@@_full_name_aux:nn {#2} }
+ }
+%</package>
+ \@@_file_name_end:
+ }
+ { \@@_file_ext_check:n {#1} }
+ }
+ }
+% \end{macrocode}
+% Two pars to the auxiliary here so we can avoid doing quoting
+% twice in the event we find the right file.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name_aux:nn #1#2
+ {
+ \exp_args:Ne \@@_full_name_aux:n
+ { \__kernel_file_name_quote:e { \tl_to_str:n {#2} #1 } }
+ }
+\cs_new:Npn \@@_full_name_aux:n #1
+ {
+ \tl_if_blank:eF { \@@_file_size:n {#1} }
+ {
+ \seq_map_break:n
+ {
+ \@@_file_ext_check:n {#1}
+ \@@_file_name_cleanup:w
+ }
+ }
+ }
+\cs_new:Npn \@@_file_name_cleanup:w #1 \@@_file_name_end:n #2 { }
+\cs_new:Npn \@@_file_name_end: { }
+% \end{macrocode}
+% As \TeX{} automatically adds |.tex| if there is no extension,
+% there is a little clean up to do here. First, find the file
+% extension if present, ignoring any quotes and avoiding dots in
+% the path.(The quoted name is retained for the common case that
+% there is no additional work to do.)
+% \begin{macrocode}
+\cs_new:Npn \@@_file_ext_check:n #1
+ { \@@_file_ext_check_auxi:nw {#1} #1 " #1 " \q_stop }
+\cs_new:Npn \@@_file_ext_check_auxi:nw #1#2 " #3 " #4 \q_stop
+ { \@@_file_ext_check_auxii:nw {#1} #3 / \q_nil / \q_stop }
+\cs_new:Npn \@@_file_ext_check_auxii:nw #1#2 / #3 / #4 \q_stop
+ {
+ \quark_if_nil:nTF {#3}
+ { \@@_file_ext_check_auxiii:nw {#1} #2 . \q_nil . \q_stop }
+ { \@@_file_ext_check_auxii:nw {#1} #3 / #4 \q_stop }
+ }
+\cs_new:Npx \@@_file_ext_check_auxiii:nw #1#2 . #3 . #4 \q_stop
+ {
+ \exp_not:N \quark_if_nil:nTF {#3}
+ { \exp_not:N \__kernel_file_name_quote:n { #1 \tl_to_str:n { .tex } } }
+ {#1}
+ }
+% \end{macrocode}
+% Deal with the fact that the primitive might not be available.
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \cs_if_exist_p:N \tex_filesize:D }
+ { \sys_if_engine_luatex_p: }
+ {
+ \cs_gset:Npn \file_full_name:n #1
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { primitive-not-available }
+ { \(pdf)filesize }
+ }
+ }
+\__kernel_msg_new:nnnn { kernel } { primitive-not-available }
+ { Primitive~\token_to_str:N #1 not~available }
+ {
+ The~version~of~your~TeX~engine~does~not~provide~functionality~equivalent~to~
+ the~#1~primitive.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \begin{macro}{\file_get_full_name:nN, \file_get_full_name:VN}
% \begin{macro}[TF]{\file_get_full_name:nN, \file_get_full_name:VN}
% \begin{macro}{\@@_get_full_name_search:nN}
-% The way to test if a file exists is to try to open it: if it does
+% These functions pre-date using \cs{tex_filesize:D} for file searching,
+% so are |get| functions with protection. To avoid having different
+% search set ups, they are simply wrappers around the code above.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_get_full_name:nN #1#2
+ {
+ \file_get_full_name:nNF {#1} #2
+ { \tl_set:Nn #2 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get_full_name:nN { V }
+\prg_new_protected_conditional:Npnn \file_get_full_name:nN #1#2 { T , F , TF }
+ {
+ \tl_set:Nx #2
+ { \file_full_name:n {#1} }
+ \tl_if_empty:NTF #2
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\cs_generate_variant:Nn \file_get_full_name:nNT { V }
+\cs_generate_variant:Nn \file_get_full_name:nNF { V }
+\cs_generate_variant:Nn \file_get_full_name:nNTF { V }
+% \end{macrocode}
+% If \cs{tex_filesize:D} is not available, the
+% way to test if a file exists is to try to open it: if it does
% not exist then \TeX{} reports end-of-file. A search is made
% looking at each potential path in turn (starting from the current
% directory). The first location is of course treated as the correct
@@ -2378,55 +2654,52 @@
% existence of the file with |.tex| extension in that directory, and
% if it exists we include the |.tex| extension in the result.
% \begin{macrocode}
-\cs_new_protected:Npn \file_get_full_name:nN #1#2
- {
- \file_get_full_name:nNF {#1} #2
- { \tl_set:Nn #2 { \q_no_value } }
- }
-\cs_generate_variant:Nn \file_get_full_name:nN { V }
-\prg_new_protected_conditional:Npnn \file_get_full_name:nN #1#2 { T , F , TF }
+\bool_lazy_or:nnF
+ { \cs_if_exist_p:N \tex_filesize:D }
+ { \sys_if_engine_luatex_p: }
{
- \__kernel_file_name_sanitize:nN {#1} \l_@@_base_name_tl
- \@@_get_full_name_search:nN { } \use:n
- \seq_map_inline:Nn \l_file_search_path_seq
- { \@@_get_full_name_search:nN { ##1 / } \seq_map_break:n }
-%<*package>
- \cs_if_exist:NT \input@path
+ \prg_set_protected_conditional:Npnn \file_get_full_name:nN #1#2 { T , F , TF }
{
- \tl_map_inline:Nn \input@path
- { \@@_get_full_name_search:nN { ##1 } \tl_map_break:n }
- }
+ \@@_get_full_name_search:nN { } \use:n
+ \seq_map_inline:Nn \l_file_search_path_seq
+ { \@@_get_full_name_search:nN { ##1 / } \seq_map_break:n }
+%<*package>
+ \cs_if_exist:NT \input@path
+ {
+ \tl_map_inline:Nn \input@path
+ { \@@_get_full_name_search:nN { ##1 } \tl_map_break:n }
+ }
%</package>
- \tl_set:Nn \l_@@_full_name_tl { \q_no_value }
- \prg_break_point:
- \quark_if_no_value:NTF \l_@@_full_name_tl
- {
- \ior_close:N \g_@@_internal_ior
- \prg_return_false:
- }
- {
- \file_parse_full_name:VNNN \l_@@_full_name_tl
- \l_@@_dir_str \l_@@_name_str \l_@@_ext_str
- \str_if_empty:NT \l_@@_ext_str
+ \tl_set:Nn \l_@@_full_name_tl { \q_no_value }
+ \prg_break_point:
+ \quark_if_no_value:NTF \l_@@_full_name_tl
{
- \__kernel_ior_open:No \g_@@_internal_ior
- { \l_@@_full_name_tl .tex }
- \ior_if_eof:NF \g_@@_internal_ior
- { \tl_put_right:Nn \l_@@_full_name_tl { .tex } }
+ \ior_close:N \g_@@_internal_ior
+ \prg_return_false:
+ }
+ {
+ \file_parse_full_name:VNNN \l_@@_full_name_tl
+ \l_@@_dir_str \l_@@_name_str \l_@@_ext_str
+ \str_if_empty:NT \l_@@_ext_str
+ {
+ \__kernel_ior_open:No \g_@@_internal_ior
+ { \l_@@_full_name_tl .tex }
+ \ior_if_eof:NF \g_@@_internal_ior
+ { \tl_put_right:Nn \l_@@_full_name_tl { .tex } }
+ }
+ \ior_close:N \g_@@_internal_ior
+ \tl_set_eq:NN #2 \l_@@_full_name_tl
+ \prg_return_true:
}
- \ior_close:N \g_@@_internal_ior
- \tl_set_eq:NN #2 \l_@@_full_name_tl
- \prg_return_true:
}
}
-\cs_generate_variant:Nn \file_get_full_name:nNT { V }
-\cs_generate_variant:Nn \file_get_full_name:nNF { V }
-\cs_generate_variant:Nn \file_get_full_name:nNTF { V }
\cs_new_protected:Npn \@@_get_full_name_search:nN #1#2
{
- \@@_name_quote:nN
- { \tl_to_str:n {#1} \l_@@_base_name_tl }
- \l_@@_full_name_tl
+ \tl_set:Nx \l_@@_full_name_tl
+ {
+ \exp_args:Ne \__kernel_file_name_quote:n
+ { \tl_to_str:n {#1} \l_@@_base_name_tl }
+ }
\__kernel_ior_open:No \g_@@_internal_ior \l_@@_full_name_tl
\ior_if_eof:NF \g_@@_internal_ior { #2 { \prg_break: } }
}
@@ -2434,17 +2707,63 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \begin{variable}{\g_@@_internal_ior}
+% A reserved stream to test for file existence, if required.
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \cs_if_exist_p:N \tex_filesize:D }
+ { \sys_if_engine_luatex_p: }
+ { \ior_new:N \g_@@_internal_ior }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[rEXP]{\file_mdfive_hash:n, \file_size:n, \file_timestamp:n}
+% \begin{macro}[rEXP]{\@@_details:nn, \@@_details_aux:nn}
+% \begin{macro}[rEXP]{\@@_mdfive_hash:n}
+% Getting file details by expansion is relatively easy if a bit repetitive.
+% As the MD5 function has a slightly different syntax from the other commands,
+% there is a little cleaning up to do.
+% \begin{macrocode}
+\cs_new:Npn \file_mdfive_hash:n #1
+ { \@@_details:nn {#1} { mdfivesum } }
+\cs_new:Npn \file_size:n #1
+ { \@@_details:nn {#1} { size } }
+\cs_new:Npn \file_timestamp:n #1
+ { \@@_details:nn {#1} { moddate } }
+\cs_new:Npn \@@_details:nn #1#2
+ {
+ \exp_args:Ne \@@_details_aux:nn
+ { \file_full_name:n {#1} } {#2}
+ }
+\cs_new:Npn \@@_details_aux:nn #1#2
+ {
+ \tl_if_blank:nF {#1}
+ { \use:c { tex_file #2 :D } {#1} }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_gset:Npn \@@_details_aux:nn #1#2
+ {
+ \lua_now:e
+ { l3kernel.file#2 ( " \lua_escape:e { #1 } " ) }
+ }
+ }
+ {
+ \cs_gset:Npn \file_mdfive_hash:n #1
+ { \exp_args:Ne \@@_mdfive_hash:n { \file_full_name:n {#1} } }
+ \cs_new:Npn \@@_mdfive_hash:n #1
+ { \tex_mdfivesum:D file {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}
% {\file_get_mdfive_hash:nN, \file_get_size:nN, \file_get_timestamp:nN}
% \begin{macro}{\@@_get_details:nnN}
-% These are all wrappers around the \pdfTeX{} primitives doing the same
-% jobs: as we want consistent file paths to be found, they are all set up
-% using \cs{file_get_full_name:nN} and so are non-expandable \texttt{get}
-% functions. Much of the code is repetitive but we need to branch for
-% \LuaTeX{} (emulation in Lua), for the slightly different syntax
-% needed for \tn{tex_mdfivesum:D} and for the fact that primitive
-% coverage varies in other engines.
+% Non-expandable wrappers around the above in the case where appropriate
+% primitive support exists.
% \begin{macrocode}
\cs_new_protected:Npn \file_get_mdfive_hash:nN #1#2
{ \file_get_mdfive_hash:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
@@ -2453,76 +2772,44 @@
\cs_new_protected:Npn \file_get_timestamp:nN #1#2
{ \file_get_timestamp:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
\prg_new_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2 { T , F , TF }
- { \@@_get_details:nnN {#1} { mdfivesum } {#2} }
+ { \@@_get_details:nnN {#1} { mdfive_hash } #2 }
\prg_new_protected_conditional:Npnn \file_get_size:nN #1#2 { T , F , TF }
- { \@@_get_details:nnN {#1} { size } {#2} }
+ { \@@_get_details:nnN {#1} { size } #2 }
\prg_new_protected_conditional:Npnn \file_get_timestamp:nN #1#2 { T , F , TF }
- { \@@_get_details:nnN {#1} { moddate } {#2} }
+ { \@@_get_details:nnN {#1} { timestamp } #2 }
\cs_new_protected:Npn \@@_get_details:nnN #1#2#3
{
- \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
- {
- \tl_set:Nx #3
- {
- \use:c { tex_file #2 :D } \exp_after:wN
- { \l_@@_full_name_tl }
- }
- \prg_return_true:
- }
+ \tl_set:Nx #3
+ { \use:c { file_ #2 :n } {#1} }
+ \tl_if_empty:NTF #3
{ \prg_return_false: }
+ { \prg_return_true: }
}
-\sys_if_engine_luatex:TF
+% \end{macrocode}
+% Where the primitive is not available, issue an error: this is a little
+% more conservative than absolutely needed, but does work.
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \cs_if_exist_p:N \tex_filesize:D }
+ { \sys_if_engine_luatex_p: }
{
\cs_set_protected:Npn \@@_get_details:nnN #1#2#3
- {
- \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
- {
- \tl_set:Nx #3
- {
- \lua_now:e
- {
- l3kernel.file#2
- ( " \lua_escape:e { \l_@@_full_name_tl } " )
- }
- }
- \prg_return_true:
- }
- { \prg_return_false: }
- }
- }
- {
- \prg_set_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2
- { T , F , TF }
- {
- \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ {
+ \tl_clear:N #3
+ \__kernel_msg_error:nnx
+ { kernel } { primitive-not-available }
{
- \tl_set:Nx #2
+ \token_to_str:N \(pdf)file
+ \str_case:nn {#2}
{
- \tex_mdfivesum:D file \exp_after:wN
- { \l_@@_full_name_tl }
+ { mdfive_hash } { mdfivesum }
+ { timestamp } { moddate }
+ { size } { size }
}
- \prg_return_true:
- }
- { \prg_return_false: }
- }
- \cs_if_exist:NF \tex_filesize:D
- {
- \cs_set_protected:Npn \@@_get_details:nnN #1#2#3
- {
- \tl_clear:N #3
- \__kernel_msg_error:nnx
- { kernel } { primitive-not-available }
- { \exp_not:c { (pdf)file #2 } }
- \prg_return_false:
}
+ \prg_return_false:
}
}
-\__kernel_msg_new:nnnn { kernel } { primitive-not-available }
- { Primitive~\token_to_str:N #1 not~available }
- {
- The~version~of~XeTeX~in~use~does~not~provide~functionality~equivalent~to~
- the~\token_to_str:N #1 primitive.
- }
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -2573,9 +2860,8 @@
% \begin{macrocode}
\cs_new_protected:Npn \__kernel_file_missing:n #1
{
- \__kernel_file_name_sanitize:nN {#1} \l_@@_base_name_tl
\__kernel_msg_error:nnx { kernel } { file-not-found }
- { \l_@@_base_name_tl }
+ { \__kernel_file_name_sanitize:n {#1} }
}
% \end{macrocode}
% \end{macro}
@@ -2778,6 +3064,11 @@
% \begin{macrocode}
\cs_new_protected:Npn \GetIdInfo
{
+ \tl_clear_new:N \ExplFileDescription
+ \tl_clear_new:N \ExplFileDate
+ \tl_clear_new:N \ExplFileName
+ \tl_clear_new:N \ExplFileExtension
+ \tl_clear_new:N \ExplFileVersion
\group_begin:
\char_set_catcode_space:n { 32 }
\exp_after:wN