diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3file.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3file.dtx | 1248 |
1 files changed, 602 insertions, 646 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx index 76a88d152d4..25449f418c5 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx @@ -42,7 +42,7 @@ % }^^A % } % -% \date{Released 2018/03/05} +% \date{Released 2018-04-30} % % \maketitle % @@ -67,112 +67,7 @@ % using |"| tokens if they contain spaces: as a result, |"| tokens are % \emph{not} permitted in file names. % -% \section{File operation functions} -% -% \begin{variable}[added = 2017-06-21] -% { -% \g_file_curr_dir_str, -% \g_file_curr_name_str, -% \g_file_curr_ext_str -% } -% Contain the directory, name and extension of the current file. The -% directory is empty if the file was loaded without an explicit -% path (\emph{i.e.}~if it is in the \TeX{} search path), and does -% \emph{not} end in |/| other than the case that it is exactly equal -% to the root directory. The \meta{name} and \meta{ext} parts together -% make up the file name, thus the \meta{name} part may be thought of -% as the \enquote{job name} for the current file. Note that \TeX{} does -% not provide information on the \meta{ext} part for the main (top -% level) file and that this file always has an empty \meta{dir} component. -% Also, the \meta{name} here will be equal to \cs{c_sys_jobname_str}, -% which may be different from the real file name (if set using -% |--jobname|, for example). -% \end{variable} -% -% \begin{variable}[added = 2017-06-18]{\l_file_search_path_seq} -% Each entry is the path to a directory which should be searched when -% seeking a file. Each path can be relative or absolute, and should -% not include the trailing slash. The entries are not expanded when -% used so may contain active characters but should not feature any -% variable content. Spaces need not be quoted. -% -% \begin{texnote} -% When working as a package in \LaTeXe{}, \pkg{expl3} will -% automatically append the current \tn{input@path} to the -% set of values from \cs{l_file_search_path_seq}. -% \end{texnote} -% \end{variable} -% -% \begin{function}[TF, updated = 2012-02-10]{\file_if_exist:n} -% \begin{syntax} -% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code} -% \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}. -% \end{function} -% -% \begin{function}[updated = 2017-06-26] -% {\file_get_full_name:nN, \file_get_full_name:VN} -% \begin{syntax} -% \cs{file_get_full_name:nN} \Arg{file name} \meta{str var} -% \end{syntax} -% Searches for \meta{file name} in the path as detailed for -% \cs{file_if_exist:nTF}, and if found sets the \meta{str var} the -% fully-qualified name of the file, \emph{i.e.}~the path and file name. -% 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 then the \meta{str var} is empty. -% \end{function} -% -% \begin{function}[added = 2017-06-23, updated = 2017-06-26] -% {\file_parse_full_name:nNNN} -% \begin{syntax} -% \cs{file_parse_full_name:nNNN} \Arg{full name} \meta{dir} \meta{name} \meta{ext} -% \end{syntax} -% Parses the \meta{full name} and splits it into three parts, each of -% which is returned by setting the appropriate local string variable: -% \begin{itemize} -% \item The \meta{dir}: everything up to the last |/| (path separator) -% in the \meta{file path}. As with system \texttt{PATH} variables -% and related functions, the \meta{dir} does \emph{not} include the -% trailing |/| unless it points to the root directory. If there is no path (only -% a file name), \meta{dir} is empty. -% \item The \meta{name}: everything after the last |/| up to the last |.|, -% where both of those characters are optional. The \meta{name} may -% contain multiple |.| characters. It is empty if \meta{full name} -% consists only of a directory name. -% \item The \meta{ext}: everything after the last |.| (including the dot). -% The \meta{ext} is empty if there is no |.| after the last |/|. -% \end{itemize} -% This function does not expand the \meta{full name} before turning it -% to a string. It assume that the \meta{full name} either contains no -% quote (|"|) characters or is surrounded by a pair of quotes. -% \end{function} -% -% \begin{function}[updated = 2017-06-26]{\file_input:n} -% \begin{syntax} -% \cs{file_input: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 reads in the file as -% additional \LaTeX{} source. All files read are recorded -% for information and the file name stack is updated by this -% function. An error is raised if the file is not found. -% \end{function} -% -% \begin{function}{\file_show_list:, \file_log_list:} -% \begin{syntax} -% \cs{file_show_list:} -% \cs{file_log_list:} -% \end{syntax} -% These functions list all files loaded by \LaTeXe{} commands that -% populate \tn{@filelist} or by \cs{file_input:n}. While -% \cs{file_show_list:} displays the list in the terminal, -% \cs{file_log_list:} outputs it to the log file only. -% \end{function} -% -% \subsection{Input--output stream management} +% \section{Input--output stream management} % % As \TeX{} engines have a limited number of input and output streams, direct % use of the streams by the programmer is not supported in \LaTeX3. Instead, an @@ -597,7 +492,7 @@ % by the \cs{iow_wrap:nnnN} % function. This value depends on the \TeX{} system in use: the standard % value is $78$, which is typically correct for unmodified \TeX{}live -% and MiK\TeX{} systems. +% and \hologo{MiKTeX} systems. % \end{variable} % % \subsection{Constant input--output streams, and variables} @@ -647,74 +542,109 @@ % \end{texnote} % \end{function} % -% \subsection{Internal file functions and variables} +% \section{File operation functions} % -% \begin{variable}{\g__file_internal_ior} -% Used to test for the existence of files when opening. +% \begin{variable}[added = 2017-06-21] +% { +% \g_file_curr_dir_str, +% \g_file_curr_name_str, +% \g_file_curr_ext_str +% } +% Contain the directory, name and extension of the current file. The +% directory is empty if the file was loaded without an explicit +% path (\emph{i.e.}~if it is in the \TeX{} search path), and does +% \emph{not} end in |/| other than the case that it is exactly equal +% to the root directory. The \meta{name} and \meta{ext} parts together +% make up the file name, thus the \meta{name} part may be thought of +% as the \enquote{job name} for the current file. Note that \TeX{} does +% not provide information on the \meta{ext} part for the main (top +% level) file and that this file always has an empty \meta{dir} component. +% Also, the \meta{name} here will be equal to \cs{c_sys_jobname_str}, +% which may be different from the real file name (if set using +% |--jobname|, for example). % \end{variable} % -% \begin{variable}{\l__file_base_name_str, \l__file_full_name_str} -% Used to store and transfer the file name (including extension) -% and (partial) file path whilst reading files. (The file base is the -% base name plus any preceding directory name.) +% \begin{variable}[added = 2017-06-18]{\l_file_search_path_seq} +% Each entry is the path to a directory which should be searched when +% seeking a file. Each path can be relative or absolute, and should +% not include the trailing slash. The entries are not expanded when +% used so may contain active characters but should not feature any +% variable content. Spaces need not be quoted. +% +% \begin{texnote} +% When working as a package in \LaTeXe{}, \pkg{expl3} will +% automatically append the current \tn{input@path} to the +% set of values from \cs{l_file_search_path_seq}. +% \end{texnote} % \end{variable} % -% \begin{function}[added = 2017-06-25]{\__file_missing:n} +% \begin{function}[TF, updated = 2012-02-10]{\file_if_exist:n} % \begin{syntax} -% \cs{__file_missing:n} \Arg{name} +% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code} % \end{syntax} -% Expands the \meta{name} as per \cs{__file_name_sanitize:nN} then -% produces an error message indicating that that file was not found. +% Searches for \meta{file name} using the current \TeX{} search +% path and the additional paths controlled by +% \cs{l_file_search_path_seq}. % \end{function} % -% \begin{function}[added = 2017-06-19]{\__file_name_sanitize:nN} +% \begin{function}[updated = 2017-06-26] +% {\file_get_full_name:nN, \file_get_full_name:VN} % \begin{syntax} -% \cs{__file_name_sanitize:nN} \Arg{name} \meta{str var} +% \cs{file_get_full_name:nN} \Arg{file name} \meta{str var} % \end{syntax} -% Exhaustively-expands the \meta{name} with the exception of any -% category \meta{active} (catcode~$13$) tokens, which are not expanded. -% The list of \meta{active} tokens is taken from \cs{l_char_active_seq}. -% The \meta{str var} is then set to the \meta{sanitized name}. +% Searches for \meta{file name} in the path as detailed for +% \cs{file_if_exist:nTF}, and if found sets the \meta{str var} the +% fully-qualified name of the file, \emph{i.e.}~the path and file name. +% 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 then the \meta{str var} is empty. % \end{function} % -% \begin{function}[added = 2017-06-19, updated = 2017-06-25] -% {\__file_name_quote:nN} +% \begin{function}[added = 2017-06-23, updated = 2017-06-26] +% {\file_parse_full_name:nNNN} % \begin{syntax} -% \cs{__file_name_quote:nN} \Arg{name} \meta{str var} +% \cs{file_parse_full_name:nNNN} \Arg{full name} \meta{dir} \meta{name} \meta{ext} % \end{syntax} -% Expands the \meta{name} (without special-casing active tokens), then -% sets the \meta{str var} to the \meta{name} quoted using |"| at each -% end if required by the presence of spaces in the \meta{name}. Any existing -% |"| tokens is removed and if their number is odd an error is raised. +% Parses the \meta{full name} and splits it into three parts, each of +% which is returned by setting the appropriate local string variable: +% \begin{itemize} +% \item The \meta{dir}: everything up to the last |/| (path separator) +% in the \meta{file path}. As with system \texttt{PATH} variables +% and related functions, the \meta{dir} does \emph{not} include the +% trailing |/| unless it points to the root directory. If there is no path (only +% a file name), \meta{dir} is empty. +% \item The \meta{name}: everything after the last |/| up to the last |.|, +% where both of those characters are optional. The \meta{name} may +% contain multiple |.| characters. It is empty if \meta{full name} +% consists only of a directory name. +% \item The \meta{ext}: everything after the last |.| (including the dot). +% The \meta{ext} is empty if there is no |.| after the last |/|. +% \end{itemize} +% This function does not expand the \meta{full name} before turning it +% to a string. It assume that the \meta{full name} either contains no +% quote (|"|) characters or is surrounded by a pair of quotes. % \end{function} % -% \subsection{Internal input--output functions} -% -% \begin{function}[added = 2012-01-23]{\__ior_open:Nn, \__ior_open:No} +% \begin{function}[updated = 2017-06-26]{\file_input:n} % \begin{syntax} -% \cs{__ior_open:Nn} \meta{stream} \Arg{file name} +% \cs{file_input:n} \Arg{file name} % \end{syntax} -% This function has identical syntax to the public version. However, -% is does not take precautions against active characters in the -% \meta{file name}, and it does not attempt to add a \meta{path} to -% the \meta{file name}: it is therefore intended to be used by -% higher-level -% functions which have already fully expanded the \meta{file name} and which -% need to perform multiple open or close operations. See for example the -% implementation of \cs{file_get_full_name:nN}, +% Searches for \meta{file name} in the path as detailed for +% \cs{file_if_exist:nTF}, and if found reads in the file as +% additional \LaTeX{} source. All files read are recorded +% for information and the file name stack is updated by this +% function. An error is raised if the file is not found. % \end{function} % -% \begin{function}[added = 2014-08-23]{\__iow_with:Nnn} +% \begin{function}{\file_show_list:, \file_log_list:} % \begin{syntax} -% \cs{__iow_with:Nnn} \meta{integer} \Arg{value} \Arg{code} +% \cs{file_show_list:} +% \cs{file_log_list:} % \end{syntax} -% If the \meta{integer} is equal to the \meta{value} then this -% function simply runs the \meta{code}. Otherwise it saves the -% current value of the \meta{integer}, sets it to the \meta{value}, -% runs the \meta{code}, and restores the \meta{integer} to its former -% value. This is used to ensure that the \tn{newlinechar} is~$10$ -% when writing to a stream, which lets \cs{iow_newline:} work, and -% that \tn{errorcontextlines} is $-1$ when displaying a message. +% These functions list all files loaded by \LaTeXe{} commands that +% populate \tn{@filelist} or by \cs{file_input:n}. While +% \cs{file_show_list:} displays the list in the terminal, +% \cs{file_log_list:} outputs it to the log file only. % \end{function} % % \end{documentation} @@ -729,427 +659,6 @@ %<*initex|package> % \end{macrocode} % -% \begin{macrocode} -%<@@=file> -% \end{macrocode} -% -% \subsection{File operations} -% -% \begin{variable} -% { -% \g_file_curr_dir_str , -% \g_file_curr_ext_str , -% \g_file_curr_name_str -% } -% The name of the current file should be available at all times. -% For the format the file name needs to be picked up at the start of the -% run. In \LaTeXe{} package mode the current file name is collected from -% \tn{@currname}. -% \begin{macrocode} -\str_new:N \g_file_curr_dir_str -\str_new:N \g_file_curr_ext_str -\str_new:N \g_file_curr_name_str -%<*initex> -\tex_everyjob:D \exp_after:wN - { - \tex_the:D \tex_everyjob:D - \str_gset:Nx \g_file_curr_name_str { \tex_jobname:D } - } -%</initex> -%<*package> -\cs_if_exist:NT \@currname - { \str_gset_eq:NN \g_file_curr_name_str \@currname } -%</package> -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\g_@@_stack_seq} -% The input list of files is stored as a sequence stack. In package -% mode we can recover the information from the details held by -% \LaTeXe{} (we must be in the preamble and loaded using \tn{usepackage} -% or \tn{RequirePackage}). As \LaTeXe{} doesn't store directory and -% name separately, we stick to the same convention here. -% \begin{macrocode} -\seq_new:N \g_@@_stack_seq -%<*package> -\group_begin: - \cs_set_protected:Npn \@@_tmp:w #1#2#3 - { - \tl_if_blank:nTF {#1} - { - \cs_set:Npn \@@_tmp:w ##1 " ##2 " ##3 \q_stop { { } {##2} { } } - \seq_gput_right:Nx \g_@@_stack_seq - { - \exp_after:wN \@@_tmp:w \tex_jobname:D - " \tex_jobname:D " \q_stop - } - } - { - \seq_gput_right:Nn \g_@@_stack_seq { { } {#1} {#2} } - \@@_tmp:w - } - } - \cs_if_exist:NT \@currnamestack - { \exp_after:wN \@@_tmp:w \@currnamestack } -\group_end: -%</package> -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\g_@@_record_seq} -% The total list of files used is recorded separately from the current -% file stack, as nothing is ever popped from this list. The current -% file name should be included in the file list! In format mode, this -% is done at the very start of the \TeX{} run. In package mode we -% will eventually copy the contents of \cs{@filelist}. -% \begin{macrocode} -\seq_new:N \g_@@_record_seq -%<*initex> -\tex_everyjob:D \exp_after:wN - { - \tex_the:D \tex_everyjob:D - \seq_gput_right:NV \g_@@_record_seq \g_file_curr_name_str - } -%</initex> -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_tmp_tl} -% Used as a short-term scratch variable. -% \begin{macrocode} -\tl_new:N \l_@@_tmp_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l__file_base_name_str, \l__file_full_name_str} -% For storing the basename and full path whilst passing data internally. -% \begin{macrocode} -\str_new:N \l__file_base_name_str -\str_new:N \l__file_full_name_str -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_dir_str, \l_@@_ext_str, \l_@@_name_str} -% Used in parsing a path into parts: in contrast to the above, these are -% never used outside of the current module. -% \begin{macrocode} -\str_new:N \l_@@_dir_str -\str_new:N \l_@@_ext_str -\str_new:N \l_@@_name_str -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_file_search_path_seq} -% The current search path. -% \begin{macrocode} -\seq_new:N \l_file_search_path_seq -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_tmp_seq} -% Scratch space for comma list conversion in package mode. -% \begin{macrocode} -%<*package> -\seq_new:N \l_@@_tmp_seq -%</package> -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\__file_name_sanitize:nN} -% \begin{macro}{\__file_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{macrocode} -\cs_new_protected:Npn \__file_name_sanitize:nN #1#2 - { - \group_begin: - \seq_map_inline:Nn \l_char_active_seq - { - \tl_set:Nx \l_@@_tmp_tl { \iow_char:N ##1 } - \char_set_active_eq:NN ##1 \l_@@_tmp_tl - } - \tl_set:Nx \l_@@_tmp_tl {#1} - \tl_set:Nx \l_@@_tmp_tl - { \tl_to_str:N \l_@@_tmp_tl } - \exp_args:NNNV \group_end: - \str_set:Nn #2 \l_@@_tmp_tl - } -\cs_new_protected:Npn \__file_name_quote:nN #1#2 - { - \str_set:Nx #2 {#1} - \int_if_even:nF - { 0 \tl_map_function:NN #2 \@@_name_quote_aux:n } - { - \__kernel_msg_error:nnx - { kernel } { unbalanced-quote-in-filename } {#2} - } - \tl_remove_all:Nn #2 { " } - \tl_if_in:NnT #2 { ~ } - { \str_set:Nx #2 { " \exp_not:V #2 " } } - } -\cs_new:Npn \@@_name_quote_aux:n #1 - { \token_if_eq_charcode:NNT #1 " { + 1 } } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\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 -% 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 -% one: this is done by jumping to \cs{__prg_break_point:}. If nothing -% is found, |#2| is returned empty. A special case when there is no -% extension is that once the first location is found we test the -% 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_name_sanitize:nN {#1} \l__file_base_name_str - \@@_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> - \str_clear:N \l__file_full_name_str - \__prg_break_point: - \str_if_empty:NF \l__file_full_name_str - { - \exp_args:NV \file_parse_full_name:nNNN \l__file_full_name_str - \l_@@_dir_str \l_@@_name_str \l_@@_ext_str - \str_if_empty:NT \l_@@_ext_str - { - \__ior_open:No \g_@@_internal_ior - { \l__file_full_name_str .tex } - \ior_if_eof:NF \g_@@_internal_ior - { \str_put_right:Nn \l__file_full_name_str { .tex } } - } - } - \str_set_eq:NN #2 \l__file_full_name_str - \ior_close:N \g_@@_internal_ior - } -\cs_generate_variant:Nn \file_get_full_name:nN { V } -\cs_new_protected:Npn \@@_get_full_name_search:nN #1#2 - { - \__file_name_quote:nN - { \tl_to_str:n {#1} \l__file_base_name_str } - \l__file_full_name_str - \__ior_open:No \g_@@_internal_ior \l__file_full_name_str - \ior_if_eof:NF \g_@@_internal_ior { #2 { \__prg_break: } } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[TF]{\file_if_exist:n} -% The test for the existence of a file is a wrapper around the function to -% add a path to a file. If the file was found, the path contains -% something, whereas if the file was not located then the return value -% is empty. -% \begin{macrocode} -\prg_new_protected_conditional:Npnn \file_if_exist:n #1 { T , F , TF } - { - \file_get_full_name:nN {#1} \l__file_full_name_str - \str_if_empty:NTF \l__file_full_name_str - { \prg_return_false: } - { \prg_return_true: } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\__file_missing:n} -% An error message for a missing file, also used in \cs{ior_open:Nn}. -% \begin{macrocode} -\cs_new_protected:Npn \__file_missing:n #1 - { - \__file_name_sanitize:nN {#1} \l__file_base_name_str - \__kernel_msg_error:nnx { kernel } { file-not-found } - { \l__file_base_name_str } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\file_input:n} -% \begin{macro}{\@@_input:n, \@@_input:V} -% \begin{macro}{\@@_input_push:n} -% \begin{macro}{\@@_input_pop:} -% \begin{macro}{\@@_input_pop:nnn} -% Loading a file is done in a safe way, checking first that the file -% exists and loading only if it does. Push the file name on the -% \cs{g_@@_stack_seq}, and add it to the file list, either -% \cs{g_@@_record_seq}, or \cs{@filelist} in package mode. -% \begin{macrocode} -\cs_new_protected:Npn \file_input:n #1 - { - \file_get_full_name:nN {#1} \l__file_full_name_str - \str_if_empty:NTF \l__file_full_name_str - { \__file_missing:n {#1} } - { \@@_input:V \l__file_full_name_str } - } -\cs_new_protected:Npn \@@_input:n #1 - { -%<*initex> - \seq_gput_right:Nn \g_@@_record_seq {#1} -%</initex> -%<*package> - \clist_if_exist:NTF \@filelist - { \@addtofilelist {#1} } - { \seq_gput_right:Nn \g_@@_record_seq {#1} } -%</package> - \@@_input_push:n {#1} - \tex_input:D #1 \c_space_tl - \@@_input_pop: - } -\cs_generate_variant:Nn \@@_input:n { V } -% \end{macrocode} -% Keeping a track of the file data is easy enough: we store the separated -% parts so we do not need to parse them twice. -% \begin{macrocode} -\cs_new_protected:Npn \@@_input_push:n #1 - { - \seq_gpush:Nx \g_@@_stack_seq - { - { \g_file_curr_dir_str } - { \g_file_curr_name_str } - { \g_file_curr_ext_str } - } - \file_parse_full_name:nNNN {#1} - \l_@@_dir_str \l_@@_name_str \l_@@_ext_str - \str_gset_eq:NN \g_file_curr_dir_str \l_@@_dir_str - \str_gset_eq:NN \g_file_curr_name_str \l_@@_name_str - \str_gset_eq:NN \g_file_curr_ext_str \l_@@_ext_str - } -\cs_new_protected:Npn \@@_input_pop: - { - \seq_gpop:NN \g_@@_stack_seq \l_@@_tmp_tl - \exp_after:wN \@@_input_pop:nnn \l_@@_tmp_tl - } -\cs_new_protected:Npn \@@_input_pop:nnn #1#2#3 - { - \str_gset:Nn \g_file_curr_dir_str {#1} - \str_gset:Nn \g_file_curr_name_str {#2} - \str_gset:Nn \g_file_curr_ext_str {#3} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\file_parse_full_name:nNNN} -% \begin{macro} -% {\@@_parse_full_name_auxi:w, \@@_parse_full_name_split:nNNNTF} -% Parsing starts by stripping off any surrounding quotes. Then find -% the directory |#4| by splitting at the last~|/|. (The auxiliary -% returns \texttt{true}/\texttt{false} depending on whether it found -% the delimiter.) We correct for the case of a file in the root |/|, -% as in that case we wish to keep the trailing (and only) slash. Then -% split the base name |#5| at the last dot. If there was indeed a -% dot, |#5| contains the name and |#6| the extension without the dot, -% which we add back for convenience. In the special case of no -% extension given, the auxiliary stored the name into |#6|, we just -% have to move it to |#5|. -% \begin{macrocode} -\cs_new_protected:Npn \file_parse_full_name:nNNN #1#2#3#4 - { - \exp_after:wN \@@_parse_full_name_auxi:w - \tl_to_str:n { #1 " #1 " } \q_stop #2#3#4 - } -\cs_new_protected:Npn \@@_parse_full_name_auxi:w #1 " #2 " #3 \q_stop #4#5#6 - { - \@@_parse_full_name_split:nNNNTF {#2} / #4 #5 - { \str_if_empty:NT #4 { \str_set:Nn #4 { / } } } - { } - \exp_args:No \@@_parse_full_name_split:nNNNTF {#5} . #5 #6 - { \str_put_left:Nn #6 { . } } - { - \str_set_eq:NN #5 #6 - \str_clear:N #6 - } - } -\cs_new_protected:Npn \@@_parse_full_name_split:nNNNTF #1#2#3#4 - { - \cs_set_protected:Npn \@@_tmp:w ##1 ##2 #2 ##3 \q_stop - { - \tl_if_empty:nTF {##3} - { - \str_set:Nn #4 {##2} - \tl_if_empty:nTF {##1} - { - \str_clear:N #3 - \use_ii:nn - } - { - \str_set:Nx #3 { \str_tail:n {##1} } - \use_i:nn - } - } - { \@@_tmp:w { ##1 #2 ##2 } ##3 \q_stop } - } - \@@_tmp:w { } #1 #2 \q_stop - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\file_show_list:, \file_log_list:, \@@_list:N} -% \begin{macro}[EXP]{\@@_list_aux:n} -% A function to list all files used to the log, without duplicates. -% In package mode, if \cs{@filelist} is still defined, we need to take -% this list of file names into account (we capture it -% \cs{AtBeginDocument} into \cs{g_@@_record_seq}), turning it to a -% string (this does not affect the commas of this comma list). -% \begin{macrocode} -\cs_new_protected:Npn \file_show_list: { \@@_list:N \msg_show:nnxxxx } -\cs_new_protected:Npn \file_log_list: { \@@_list:N \msg_log:nnxxxx } -\cs_new_protected:Npn \@@_list:N #1 - { - \seq_clear:N \l_@@_tmp_seq -%<*package> - \clist_if_exist:NT \@filelist - { - \exp_args:NNx \seq_set_from_clist:Nn \l_@@_tmp_seq - { \tl_to_str:N \@filelist } - } -%</package> - \seq_concat:NNN \l_@@_tmp_seq \l_@@_tmp_seq \g_@@_record_seq - \seq_remove_duplicates:N \l_@@_tmp_seq - #1 { LaTeX/kernel } { file-list } - { \seq_map_function:NN \l_@@_tmp_seq \@@_list_aux:n } { } { } { } - } -\cs_new:Npn \@@_list_aux:n #1 { \iow_newline: #1 } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% When used as a package, there is a need to hold onto the standard file -% list as well as the new one here. File names recorded in -% \cs{@filelist} must be turned to strings before being added to -% \cs{g_@@_record_seq}. -% \begin{macrocode} -%<*package> -\AtBeginDocument - { - \exp_args:NNx \seq_set_from_clist:Nn \l_@@_tmp_seq - { \tl_to_str:N \@filelist } - \seq_gconcat:NNN \g_@@_record_seq \g_@@_record_seq \l_@@_tmp_seq - } -%</package> -% \end{macrocode} -% % \subsection{Input operations} % % \begin{macrocode} @@ -1195,15 +704,14 @@ % underlying mechanism needs to be queried. For \LaTeXe{} and plain \TeX{} % this data is stored in |\count16|: with the \pkg{etex} package loaded % we need to subtract $1$ as the register holds the number of the next -% stream to use. In Con\TeX{}t, we need to look at |\count38| but there +% stream to use. In \ConTeXt{}, we need to look at |\count38| but there % is no subtraction: like the original plain \TeX{}/\LaTeXe{} mechanism % it holds the value of the \emph{last} stream allocated. % \begin{macrocode} \prop_new:N \g_@@_streams_prop %<*package> -\int_step_inline:nnnn +\int_step_inline:nnn { 0 } - { 1 } { \cs_if_exist:NTF \normalend { \tex_count:D 38 ~ } @@ -1242,11 +750,18 @@ % Use the conditional version, with an error if the file is not found. % \begin{macrocode} \cs_new_protected:Npn \ior_open:Nn #1#2 - { \ior_open:NnF #1 {#2} { \__file_missing:n {#2} } } + { \ior_open:NnF #1 {#2} { \__kernel_file_missing:n {#2} } } \cs_generate_variant:Nn \ior_open:Nn { c } % \end{macrocode} % \end{macro} % +% \begin{variable}{\l_@@_file_name_str} +% Data storage. +% \begin{macrocode} +\str_new:N \l_@@_file_name_str +% \end{macrocode} +% \end{variable} +% % \begin{macro}[TF]{\ior_open:Nn, \ior_open:cn} % An auxiliary searches for the file in the \TeX{}, \LaTeXe{} and % \LaTeX3 paths. Then pass the file found to the lower-level function @@ -1255,11 +770,11 @@ % \begin{macrocode} \prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF } { - \file_get_full_name:nN {#2} \l__file_full_name_str - \str_if_empty:NTF \l__file_full_name_str + \file_get_full_name:nN {#2} \l_@@_file_name_str + \str_if_empty:NTF \l_@@_file_name_str { \prg_return_false: } { - \@@_open:No #1 \l__file_full_name_str + \__kernel_ior_open:No #1 \l_@@_file_name_str \prg_return_true: } } @@ -1283,7 +798,7 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_open:Nn, \@@_open:No} +% \begin{macro}{\__kernel_ior_open:Nn, \__kernel_ior_open:No} % \begin{macro}{\@@_open_stream:Nn} % The stream allocation itself uses the fact that there is a list of all of % those available, so allocation is simply a question of using the number at @@ -1293,7 +808,7 @@ % are tracked, so we first try those. If that fails, ask plain \TeX{} or \LaTeXe{} % for a new stream and use that number (after a bit of conversion). % \begin{macrocode} -\cs_new_protected:Npn \@@_open:Nn #1#2 +\cs_new_protected:Npn \__kernel_ior_open:Nn #1#2 { \ior_close:N #1 \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl @@ -1309,7 +824,7 @@ } %</package> } -\cs_generate_variant:Nn \@@_open:Nn { No } +\cs_generate_variant:Nn \__kernel_ior_open:Nn { No } \cs_new_protected:Npn \@@_open_stream:Nn #1#2 { \tex_global:D \tex_chardef:D #1 = \l_@@_stream_tl \scan_stop: @@ -1412,16 +927,16 @@ % % \begin{macro}{\ior_str_get:NN} % Reading as strings is a more complicated wrapper, as we wish to -% remove the endline character. +% remove the endline character and restore it afterwards. % \begin{macrocode} \cs_new_protected:Npn \ior_str_get:NN #1#2 { - \use:x + \exp_args:Nno \use:n { \int_set:Nn \tex_endlinechar:D { -1 } - \exp_not:n { \etex_readline:D #1 to #2 } - \int_set:Nn \tex_endlinechar:D { \int_use:N \tex_endlinechar:D } - } + \etex_readline:D #1 to #2 + \int_set:Nn \tex_endlinechar:D + } { \int_use:N \tex_endlinechar:D } } % \end{macrocode} % \end{macro} @@ -1430,9 +945,9 @@ % Usual map breaking functions. % \begin{macrocode} \cs_new:Npn \ior_map_break: - { \__prg_map_break:Nn \ior_map_break: { } } + { \prg_map_break:Nn \ior_map_break: { } } \cs_new:Npn \ior_map_break:n - { \__prg_map_break:Nn \ior_map_break: } + { \prg_map_break:Nn \ior_map_break: } % \end{macrocode} % \end{macro} % @@ -1453,16 +968,16 @@ { \@@_map_inline:NNn \ior_str_get:NN } \cs_new_protected:Npn \@@_map_inline:NNn { - \int_gincr:N \g__prg_map_int + \int_gincr:N \g__kernel_prg_map_int \exp_args:Nc \@@_map_inline:NNNn - { __prg_map_ \int_use:N \g__prg_map_int :n } + { @@_map_ \int_use:N \g__kernel_prg_map_int :n } } \cs_new_protected:Npn \@@_map_inline:NNNn #1#2#3#4 { \cs_gset_protected:Npn #1 ##1 {#4} \ior_if_eof:NF #3 { \@@_map_inline_loop:NNN #1#2#3 } - \__prg_break_point:Nn \ior_map_break: - { \int_gdecr:N \g__prg_map_int } + \prg_break_point:Nn \ior_map_break: + { \int_gdecr:N \g__kernel_prg_map_int } } \cs_new_protected:Npn \@@_map_inline_loop:NNN #1#2#3 { @@ -1481,13 +996,6 @@ % \end{macro} % \end{macro} % -% \begin{variable}{\g__file_internal_ior} -% Needed by the higher-level code, but cannot be created until here. -% \begin{macrocode} -\ior_new:N \g__file_internal_ior -% \end{macrocode} -% \end{variable} -% % \subsection{Output operations} % % \begin{macrocode} @@ -1510,30 +1018,30 @@ \int_const:Nn \c_log_iow { -1 } \int_const:Nn \c_term_iow { - \cs_if_exist:NTF \luatex_directlua:D - { - \int_compare:nNnTF \luatex_luatexversion:D > { 80 } - { 128 } - { 16 } - } + \bool_lazy_and:nnTF + { \sys_if_engine_luatex_p: } + { \int_compare_p:nNn \luatex_luatexversion:D > { 80 } } + { 128 } { 16 } } % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_streams_seq} -% A list of the currently-available output streams to be used as a stack. +% A list of the currently-available output streams to be used as a +% stack. The stream $18$ is special, as \tn{write}18 is used to +% denote commands to be sent to the OS\@. % \begin{macrocode} \seq_new:N \g_@@_streams_seq %<*initex> -\use:x +\exp_args:Nnx \use:n + { \seq_gset_split:Nnn \g_@@_streams_seq { } } { - \exp_not:n { \seq_gset_split:Nnn \g_@@_streams_seq { } } - { - \int_step_function:nnnN { 0 } { 1 } { \c_term_iow } - \prg_do_nothing: - } + \int_step_function:nnN { 0 } { \c_term_iow } + \prg_do_nothing: } +\int_compare:nNnF \c_term_iow < { 18 } + { \seq_gremove_all:Nn \g_@@_streams_seq { 18 } } %</initex> % \end{macrocode} % \end{variable} @@ -1551,9 +1059,8 @@ % \begin{macrocode} \prop_new:N \g_@@_streams_prop %<*package> -\int_step_inline:nnnn +\int_step_inline:nnn { 0 } - { 1 } { \cs_if_exist:NTF \normalend { \tex_count:D 39 ~ } @@ -1599,6 +1106,13 @@ % \end{macrocode} % \end{macro} % +% \begin{variable}{\l_@@_file_name_str} +% Data storage. +% \begin{macrocode} +\str_new:N \l_@@_file_name_str +% \end{macrocode} +% \end{variable} +% % \begin{macro}{\iow_open:Nn, \iow_open:cn} % \begin{macro}{\@@_open_stream:Nn, \@@_open_stream:NV} % The same idea as for reading, but without the path and without the need @@ -1606,10 +1120,10 @@ % \begin{macrocode} \cs_new_protected:Npn \iow_open:Nn #1#2 { - \__file_name_sanitize:nN {#2} \l__file_base_name_str + \__kernel_file_name_sanitize:nN {#2} \l_@@_file_name_str \iow_close:N #1 \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl - { \@@_open_stream:NV #1 \l__file_base_name_str } + { \@@_open_stream:NV #1 \l_@@_file_name_str } %<*initex> { \__kernel_msg_fatal:nn { kernel } { output-streams-exhausted } } %</initex> @@ -1617,7 +1131,7 @@ { \@@_new:N #1 \tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} } - \@@_open_stream:NV #1 \l__file_base_name_str + \@@_open_stream:NV #1 \l_@@_file_name_str } %</package> } @@ -1697,20 +1211,20 @@ % % \subsubsection{Immediate writing} % -% \begin{macro}{\@@_with:Nnn} -% \begin{macro}{\@@_with_aux:nNnn} +% \begin{macro}{\__kernel_iow_with:Nnn} +% \begin{macro}{\@@_with:nNnn} % If the integer~|#1| is equal to~|#2|, just leave~|#3| in the input % stream. Otherwise, pass the old value to an auxiliary, which sets % the integer to the new value, runs the code, and restores the % integer. % \begin{macrocode} -\cs_new_protected:Npn \@@_with:Nnn #1#2 +\cs_new_protected:Npn \__kernel_iow_with:Nnn #1#2 { \int_compare:nNnTF {#1} = {#2} { \use:n } - { \exp_args:No \@@_with_aux:nNnn { \int_use:N #1 } #1 {#2} } + { \exp_args:No \@@_with:nNnn { \int_use:N #1 } #1 {#2} } } -\cs_new_protected:Npn \@@_with_aux:nNnn #1#2#3#4 +\cs_new_protected:Npn \@@_with:nNnn #1#2#3#4 { \int_set:Nn #2 {#3} #4 @@ -1728,14 +1242,14 @@ % get the |Nx| variant, because it differs in subtle ways from % \texttt{x}-expansion, namely, macro parameter characters would not % need to be doubled. We set the \tn{newlinechar} to~$10$ using -% \cs{@@_with:Nnn} to support formats such as plain \TeX{}: otherwise, +% \cs{__kernel_iow_with:Nnn} to support formats such as plain \TeX{}: otherwise, % \cs{iow_newline:} would not work. We do not do this for % \cs{iow_shipout:Nn} or \cs{iow_shipout_x:Nn}, as \TeX{} looks at the % value of the \tn{newlinechar} at shipout time in those cases. % \begin{macrocode} \cs_new_protected:Npn \iow_now:Nn #1#2 { - \@@_with:Nnn \tex_newlinechar:D { `\^^J } + \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J } { \tex_immediate:D \tex_write:D #1 { \exp_not:n {#2} } } } \cs_generate_variant:Nn \iow_now:Nn { c, Nx, cx } @@ -1781,7 +1295,7 @@ % This is the \enquote{raw} number of characters in a line which % can be written to the terminal. % The standard value is the line length typically used by -% \TeX{}Live and Mik\TeX{}. +% \TeX{}Live and \hologo{MiKTeX}. % \begin{macrocode} \int_new:N \l_iow_line_count_int \int_set:Nn \l_iow_line_count_int { 78 } @@ -1819,8 +1333,9 @@ \cs_new_protected:Npn \@@_set_indent:n #1 { \tl_set:Nx \l_@@_one_indent_tl - { \exp_args:No \__str_to_other_fast:n { \tl_to_str:n {#1} } } - \int_set:Nn \l_@@_one_indent_int { \str_count:N \l_@@_one_indent_tl } + { \exp_args:No \__kernel_str_to_other_fast:n { \tl_to_str:n {#1} } } + \int_set:Nn \l_@@_one_indent_int + { \str_count:N \l_@@_one_indent_tl } \exp_last_unbraced:NNo \cs_set:Npn \@@_unindent:w \l_@@_one_indent_tl { } } @@ -1940,10 +1455,14 @@ % \verb*|\ | uses an \enquote{other} space rather than a normal space, % because the latter might be absorbed by \TeX{} to end a number or % other \texttt{f}-type expansions. +% Use \cs{conditionally@traceoff} if defined; it is introduced by the +% \pkg{trace} package and suppresses uninteresting tracing of the +% wrapping code. % \begin{macrocode} \cs_new_protected:Npn \iow_wrap:nnnN #1#2#3#4 { \group_begin: +%<package> \use:c { conditionally@traceoff } \int_set:Nn \tex_escapechar:D { -1 } \cs_set:Npx \{ { \token_to_str:N \{ } \cs_set:Npx \# { \token_to_str:N \# } @@ -2004,7 +1523,7 @@ { \tl_set:Nx \l_@@_wrap_tl { - \exp_args:No \__str_to_other_fast:n \l_@@_wrap_tl + \exp_args:No \__kernel_str_to_other_fast:n \l_@@_wrap_tl \c_@@_wrap_end_marker_tl } \tl_set:Nx \l_@@_wrap_tl @@ -2115,7 +1634,7 @@ \tex_edef:D \l_@@_line_part_tl { \if_false: } \fi: #1 \exp_after:wN \@@_wrap_line_loop:w - \__int_value:w \__int_eval:w + \int_value:w \int_eval:w } \cs_new:Npn \@@_wrap_line_loop:w #1 ; #2#3#4#5#6#7#8#9 { @@ -2124,7 +1643,7 @@ \fi: #2 #3 #4 #5 #6 #7 #8 #9 \exp_after:wN \@@_wrap_line_loop:w - \__int_value:w \__int_eval:w #1 - 8 ; + \int_value:w \int_eval:w #1 - 8 ; } \cs_new:Npn \@@_wrap_line_aux:Nw #1#2#3 \exp_after:wN #4 ; { @@ -2148,7 +1667,7 @@ \cs_new:Npn \@@_wrap_line_end:NnnnnnnnN #1#2#3#4#5#6#7#8#9 { #2 #3 #4 #5 #6 #7 #8 - \use_none:nnnnn \__int_eval:w 8 - ; #9 + \use_none:nnnnn \int_eval:w 8 - ; #9 \token_if_eq_charcode:NNTF \c_space_token #9 { \@@_wrap_line_end:nw { } } { \if_false: { \fi: } \@@_wrap_break:w #9 } @@ -2160,7 +1679,7 @@ \@@_wrap_next_line:w } \cs_new:Npn \@@_wrap_end_chunk:w - #1 \__int_eval:w #2 - #3 ; #4#5 \q_stop + #1 \int_eval:w #2 - #3 ; #4#5 \q_stop { \if_false: { \fi: } \exp_args:Nf \@@_wrap_next:nw { \int_eval:n { #2 - #4 } } @@ -2333,15 +1852,452 @@ % \begin{macro}[EXP]{\@@_wrap_trim:N, \@@_wrap_trim:w} % Remove one trailing \enquote{other} space from the argument. % \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1 +\cs_new:Npn \@@_wrap_trim:N #1 + { \tl_if_empty:NF #1 { \exp_after:wN \@@_wrap_trim:w #1 \q_stop } } +\exp_last_unbraced:NNNNo + \cs_new:Npn \@@_wrap_trim:w #1 \c_catcode_other_space_tl \q_stop {#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%<@@=file> +% \end{macrocode} +% +% \subsection{File operations} +% +% \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 , +% \g_file_curr_ext_str , +% \g_file_curr_name_str +% } +% The name of the current file should be available at all times. +% For the format the file name needs to be picked up at the start of the +% run. In \LaTeXe{} package mode the current file name is collected from +% \tn{@currname}. +% \begin{macrocode} +\str_new:N \g_file_curr_dir_str +\str_new:N \g_file_curr_ext_str +\str_new:N \g_file_curr_name_str +%<*initex> +\tex_everyjob:D \exp_after:wN { - \cs_new:Npn \@@_wrap_trim:N ##1 - { \tl_if_empty:NF ##1 { \exp_after:wN \@@_wrap_trim:w ##1 \q_stop } } - \cs_new:Npn \@@_wrap_trim:w ##1 #1 \q_stop {##1} + \tex_the:D \tex_everyjob:D + \str_gset:Nx \g_file_curr_name_str { \tex_jobname:D } + } +%</initex> +%<*package> +\cs_if_exist:NT \@currname + { \str_gset_eq:NN \g_file_curr_name_str \@currname } +%</package> +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_stack_seq} +% The input list of files is stored as a sequence stack. In package +% mode we can recover the information from the details held by +% \LaTeXe{} (we must be in the preamble and loaded using \tn{usepackage} +% or \tn{RequirePackage}). As \LaTeXe{} doesn't store directory and +% name separately, we stick to the same convention here. +% \begin{macrocode} +\seq_new:N \g_@@_stack_seq +%<*package> +\group_begin: + \cs_set_protected:Npn \@@_tmp:w #1#2#3 + { + \tl_if_blank:nTF {#1} + { + \cs_set:Npn \@@_tmp:w ##1 " ##2 " ##3 \q_stop + { { } {##2} { } } + \seq_gput_right:Nx \g_@@_stack_seq + { + \exp_after:wN \@@_tmp:w \tex_jobname:D + " \tex_jobname:D " \q_stop + } + } + { + \seq_gput_right:Nn \g_@@_stack_seq { { } {#1} {#2} } + \@@_tmp:w + } + } + \cs_if_exist:NT \@currnamestack + { \exp_after:wN \@@_tmp:w \@currnamestack } +\group_end: +%</package> +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_record_seq} +% The total list of files used is recorded separately from the current +% file stack, as nothing is ever popped from this list. The current +% file name should be included in the file list! In format mode, this +% is done at the very start of the \TeX{} run. In package mode we +% will eventually copy the contents of \cs{@filelist}. +% \begin{macrocode} +\seq_new:N \g_@@_record_seq +%<*initex> +\tex_everyjob:D \exp_after:wN + { + \tex_the:D \tex_everyjob:D + \seq_gput_right:NV \g_@@_record_seq \g_file_curr_name_str + } +%</initex> +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_tmp_tl} +% Used as a short-term scratch variable. +% \begin{macrocode} +\tl_new:N \l_@@_tmp_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_base_name_str, \l_@@_full_name_str} +% For storing the basename and full path whilst passing data internally. +% \begin{macrocode} +\str_new:N \l_@@_base_name_str +\str_new:N \l_@@_full_name_str +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_dir_str, \l_@@_ext_str, \l_@@_name_str} +% Used in parsing a path into parts: in contrast to the above, these are +% never used outside of the current module. +% \begin{macrocode} +\str_new:N \l_@@_dir_str +\str_new:N \l_@@_ext_str +\str_new:N \l_@@_name_str +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_file_search_path_seq} +% The current search path. +% \begin{macrocode} +\seq_new:N \l_file_search_path_seq +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_tmp_seq} +% Scratch space for comma list conversion in package mode. +% \begin{macrocode} +%<*package> +\seq_new:N \l_@@_tmp_seq +%</package> +% \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{macrocode} +\cs_new_protected:Npn \__kernel_file_name_sanitize:nN #1#2 + { + \group_begin: + \seq_map_inline:Nn \l_char_active_seq + { + \tl_set:Nx \l_@@_tmp_tl { \iow_char:N ##1 } + \char_set_active_eq:NN ##1 \l_@@_tmp_tl + } + \tl_set:Nx \l_@@_tmp_tl {#1} + \tl_set:Nx \l_@@_tmp_tl + { \tl_to_str:N \l_@@_tmp_tl } + \exp_args:NNNV \group_end: + \str_set:Nn #2 \l_@@_tmp_tl + } +\cs_new_protected:Npn \@@_name_quote:nN #1#2 + { + \str_set:Nx #2 {#1} + \int_if_even:nF + { 0 \tl_map_function:NN #2 \@@_name_quote_aux:n } + { + \__kernel_msg_error:nnx + { kernel } { unbalanced-quote-in-filename } {#2} + } + \tl_remove_all:Nn #2 { " } + \tl_if_in:NnT #2 { ~ } + { \str_set:Nx #2 { " \exp_not:V #2 " } } + } +\cs_new:Npn \@@_name_quote_aux:n #1 + { \token_if_eq_charcode:NNT #1 " { + 1 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\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 +% 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 +% one: this is done by jumping to \cs{prg_break_point:}. If nothing +% is found, |#2| is returned empty. A special case when there is no +% extension is that once the first location is found we test the +% 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 + { + \__kernel_file_name_sanitize:nN {#1} \l_@@_base_name_str + \@@_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> + \str_clear:N \l_@@_full_name_str + \prg_break_point: + \str_if_empty:NF \l_@@_full_name_str + { + \exp_args:NV \file_parse_full_name:nNNN \l_@@_full_name_str + \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_str .tex } + \ior_if_eof:NF \g_@@_internal_ior + { \str_put_right:Nn \l_@@_full_name_str { .tex } } + } + } + \str_set_eq:NN #2 \l_@@_full_name_str + \ior_close:N \g_@@_internal_ior + } +\cs_generate_variant:Nn \file_get_full_name:nN { V } +\cs_new_protected:Npn \@@_get_full_name_search:nN #1#2 + { + \@@_name_quote:nN + { \tl_to_str:n {#1} \l_@@_base_name_str } + \l_@@_full_name_str + \__kernel_ior_open:No \g_@@_internal_ior \l_@@_full_name_str + \ior_if_eof:NF \g_@@_internal_ior { #2 { \prg_break: } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF]{\file_if_exist:n} +% The test for the existence of a file is a wrapper around the function to +% add a path to a file. If the file was found, the path contains +% something, whereas if the file was not located then the return value +% is empty. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \file_if_exist:n #1 { T , F , TF } + { + \file_get_full_name:nN {#1} \l_@@_full_name_str + \str_if_empty:NTF \l_@@_full_name_str + { \prg_return_false: } + { \prg_return_true: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\__kernel_file_missing:n} +% An error message for a missing file, also used in \cs{ior_open:Nn}. +% \begin{macrocode} +\cs_new_protected:Npn \__kernel_file_missing:n #1 + { + \__kernel_file_name_sanitize:nN {#1} \l_@@_base_name_str + \__kernel_msg_error:nnx { kernel } { file-not-found } + { \l_@@_base_name_str } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\file_input:n} +% \begin{macro}{\@@_input:n, \@@_input:V} +% \begin{macro}{\@@_input_push:n, \__kernel_file_input_push:n} +% \begin{macro}{\@@_input_pop:, \__kernel_file_input_pop:} +% \begin{macro}{\@@_input_pop:nnn} +% Loading a file is done in a safe way, checking first that the file +% exists and loading only if it does. Push the file name on the +% \cs{g_@@_stack_seq}, and add it to the file list, either +% \cs{g_@@_record_seq}, or \cs{@filelist} in package mode. +% \begin{macrocode} +\cs_new_protected:Npn \file_input:n #1 + { + \file_get_full_name:nN {#1} \l_@@_full_name_str + \str_if_empty:NTF \l_@@_full_name_str + { \__kernel_file_missing:n {#1} } + { \@@_input:V \l_@@_full_name_str } + } +\cs_new_protected:Npn \@@_input:n #1 + { +%<*initex> + \seq_gput_right:Nn \g_@@_record_seq {#1} +%</initex> +%<*package> + \clist_if_exist:NTF \@filelist + { \@addtofilelist {#1} } + { \seq_gput_right:Nn \g_@@_record_seq {#1} } +%</package> + \@@_input_push:n {#1} + \tex_input:D #1 \c_space_tl + \@@_input_pop: + } +\cs_generate_variant:Nn \@@_input:n { V } +% \end{macrocode} +% Keeping a track of the file data is easy enough: we store the separated +% parts so we do not need to parse them twice. +% \begin{macrocode} +\cs_new_protected:Npn \@@_input_push:n #1 + { + \seq_gpush:Nx \g_@@_stack_seq + { + { \g_file_curr_dir_str } + { \g_file_curr_name_str } + { \g_file_curr_ext_str } + } + \file_parse_full_name:nNNN {#1} + \l_@@_dir_str \l_@@_name_str \l_@@_ext_str + \str_gset_eq:NN \g_file_curr_dir_str \l_@@_dir_str + \str_gset_eq:NN \g_file_curr_name_str \l_@@_name_str + \str_gset_eq:NN \g_file_curr_ext_str \l_@@_ext_str + } +%<*package> +\cs_new_eq:NN \__kernel_file_input_push:n \__file_input_push:n +%</package> +\cs_new_protected:Npn \@@_input_pop: + { + \seq_gpop:NN \g_@@_stack_seq \l_@@_tmp_tl + \exp_after:wN \@@_input_pop:nnn \l_@@_tmp_tl + } +%<*package> +\cs_new_eq:NN \__kernel_file_input_pop: \__file_input_pop: +%</package> +\cs_new_protected:Npn \@@_input_pop:nnn #1#2#3 + { + \str_gset:Nn \g_file_curr_dir_str {#1} + \str_gset:Nn \g_file_curr_name_str {#2} + \str_gset:Nn \g_file_curr_ext_str {#3} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\file_parse_full_name:nNNN} +% \begin{macro} +% {\@@_parse_full_name_auxi:w, \@@_parse_full_name_split:nNNNTF} +% Parsing starts by stripping off any surrounding quotes. Then find +% the directory |#4| by splitting at the last~|/|. (The auxiliary +% returns \texttt{true}/\texttt{false} depending on whether it found +% the delimiter.) We correct for the case of a file in the root |/|, +% as in that case we wish to keep the trailing (and only) slash. Then +% split the base name |#5| at the last dot. If there was indeed a +% dot, |#5| contains the name and |#6| the extension without the dot, +% which we add back for convenience. In the special case of no +% extension given, the auxiliary stored the name into |#6|, we just +% have to move it to |#5|. +% \begin{macrocode} +\cs_new_protected:Npn \file_parse_full_name:nNNN #1#2#3#4 + { + \exp_after:wN \@@_parse_full_name_auxi:w + \tl_to_str:n { #1 " #1 " } \q_stop #2#3#4 + } +\cs_new_protected:Npn \@@_parse_full_name_auxi:w + #1 " #2 " #3 \q_stop #4#5#6 + { + \@@_parse_full_name_split:nNNNTF {#2} / #4 #5 + { \str_if_empty:NT #4 { \str_set:Nn #4 { / } } } + { } + \exp_args:No \@@_parse_full_name_split:nNNNTF {#5} . #5 #6 + { \str_put_left:Nn #6 { . } } + { + \str_set_eq:NN #5 #6 + \str_clear:N #6 + } + } +\cs_new_protected:Npn \@@_parse_full_name_split:nNNNTF #1#2#3#4 + { + \cs_set_protected:Npn \@@_tmp:w ##1 ##2 #2 ##3 \q_stop + { + \tl_if_empty:nTF {##3} + { + \str_set:Nn #4 {##2} + \tl_if_empty:nTF {##1} + { + \str_clear:N #3 + \use_ii:nn + } + { + \str_set:Nx #3 { \str_tail:n {##1} } + \use_i:nn + } + } + { \@@_tmp:w { ##1 #2 ##2 } ##3 \q_stop } + } + \@@_tmp:w { } #1 #2 \q_stop } -\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl % \end{macrocode} % \end{macro} +% \end{macro} +% +% \begin{macro}{\file_show_list:, \file_log_list:, \@@_list:N} +% \begin{macro}[EXP]{\@@_list_aux:n} +% A function to list all files used to the log, without duplicates. +% In package mode, if \cs{@filelist} is still defined, we need to take +% this list of file names into account (we capture it +% \cs{AtBeginDocument} into \cs{g_@@_record_seq}), turning it to a +% string (this does not affect the commas of this comma list). +% \begin{macrocode} +\cs_new_protected:Npn \file_show_list: { \@@_list:N \msg_show:nnxxxx } +\cs_new_protected:Npn \file_log_list: { \@@_list:N \msg_log:nnxxxx } +\cs_new_protected:Npn \@@_list:N #1 + { + \seq_clear:N \l_@@_tmp_seq +%<*package> + \clist_if_exist:NT \@filelist + { + \exp_args:NNx \seq_set_from_clist:Nn \l_@@_tmp_seq + { \tl_to_str:N \@filelist } + } +%</package> + \seq_concat:NNN \l_@@_tmp_seq \l_@@_tmp_seq \g_@@_record_seq + \seq_remove_duplicates:N \l_@@_tmp_seq + #1 { LaTeX/kernel } { file-list } + { \seq_map_function:NN \l_@@_tmp_seq \@@_list_aux:n } + { } { } { } + } +\cs_new:Npn \@@_list_aux:n #1 { \iow_newline: #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% When used as a package, there is a need to hold onto the standard file +% list as well as the new one here. File names recorded in +% \cs{@filelist} must be turned to strings before being added to +% \cs{g_@@_record_seq}. +% \begin{macrocode} +%<*package> +\AtBeginDocument + { + \exp_args:NNx \seq_set_from_clist:Nn \l_@@_tmp_seq + { \tl_to_str:N \@filelist } + \seq_gconcat:NNN + \g_@@_record_seq + \g_@@_record_seq + \l_@@_tmp_seq + } +%</package> +% \end{macrocode} % % \subsection{Messages} % @@ -2399,7 +2355,7 @@ \tl_gset:Nn \g_file_current_name_tl { \g_file_curr_name_str } \__kernel_deprecation_code:nn { - \__deprecation_error:Nnn \g_file_current_name_tl + \__kernel_deprecation_error:Nnn \g_file_current_name_tl { \g_file_curr_name_str } { 2018-12-31 } } { \tex_def:D \g_file_current_name_tl { \g_file_curr_name_str } } @@ -2414,16 +2370,16 @@ { \seq_put_right:Nn \l_file_search_path_seq } \cs_new_protected:Npn \file_path_include:n #1 { - \__file_name_sanitize:nN {#1} \l__file_full_name_str - \seq_if_in:NVF \l_file_search_path_seq \l__file_full_name_str - { \seq_put_right:NV \l_file_search_path_seq \l__file_full_name_str } + \__kernel_file_name_sanitize:nN {#1} \l_@@_full_name_str + \seq_if_in:NVF \l_file_search_path_seq \l_@@_full_name_str + { \seq_put_right:NV \l_file_search_path_seq \l_@@_full_name_str } } \__kernel_patch_deprecation:nnNNpn { 2018-12-31 } { \seq_remove_all:Nn \l_file_search_path_seq } \cs_new_protected:Npn \file_path_remove:n #1 { - \__file_name_sanitize:nN {#1} \l__file_full_name_str - \seq_remove_all:NV \l_file_search_path_seq \l__file_full_name_str + \__kernel_file_name_sanitize:nN {#1} \l_@@_full_name_str + \seq_remove_all:NV \l_file_search_path_seq \l_@@_full_name_str } % \end{macrocode} % \end{macro} |