diff options
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3file.dtx')
-rw-r--r-- | macros/latex/contrib/l3kernel/l3file.dtx | 4019 |
1 files changed, 0 insertions, 4019 deletions
diff --git a/macros/latex/contrib/l3kernel/l3file.dtx b/macros/latex/contrib/l3kernel/l3file.dtx deleted file mode 100644 index 3a4d1c06dc..0000000000 --- a/macros/latex/contrib/l3kernel/l3file.dtx +++ /dev/null @@ -1,4019 +0,0 @@ -% \iffalse meta-comment -% -%% File: l3file.dtx -% -% Copyright (C) 1990-2024 The LaTeX Project -% -% It may be distributed and/or modified under the conditions of the -% LaTeX Project Public License (LPPL), either version 1.3c of this -% license or (at your option) any later version. The latest version -% of this license is in the file -% -% https://www.latex-project.org/lppl.txt -% -% This file is part of the "l3kernel bundle" (The Work in LPPL) -% and all files in that bundle must be distributed together. -% -% ----------------------------------------------------------------------- -% -% The development version of the bundle can be found at -% -% https://github.com/latex3/latex3 -% -% for those people who are interested. -% -%<*driver> -\documentclass[full,kernel]{l3doc} -\begin{document} -\DocInput{\jobname.dtx} -\PrintIndex -\end{document} -%</driver> -% \fi -% -% \title{^^A -% The \pkg{l3file} module\\ File and I/O operations^^A -% } -% -% \author{^^A -% The \LaTeX{} Project\thanks -% {^^A -% E-mail: -% \href{mailto:latex-team@latex-project.org} -% {latex-team@latex-project.org}^^A -% }^^A -% } -% -% \date{Released 2024-03-14} -% -% \maketitle -% -% \begin{documentation} -% -% This module provides functions for working with external files. Some of these -% functions apply to an entire file, and have prefix \cs[no-index]{file_\ldots}, while -% others are used to work with files on a line by line basis and have prefix -% \cs[no-index]{ior_\ldots} (reading) or \cs[no-index]{iow_\ldots} (writing). -% -% It is important to remember that when reading external files \TeX{} -% attempts to locate them using both the operating system path and entries in the -% \TeX{} file database (most \TeX{} systems use such a database). Thus the -% \enquote{current path} for \TeX{} is somewhat broader than that for other -% programs. -% -% For functions which expect a \meta{file name} argument, this argument -% may contain both literal items and expandable content, which should on -% full expansion be the desired file name. Active characters (as -% declared in \cs{l_char_active_seq}) are \emph{not} expanded, -% allowing the direct use of these in file names. Quote tokens (|"|) are -% not permitted in file names as they are reserved for internal use by some -% \TeX{} primitives. -% -% Spaces are trimmed at the beginning and end of the file name: -% this reflects the fact that some file systems do not allow or interact -% unpredictably with spaces in these positions. When no extension is given, -% this will trim spaces from the start of the name only. -% -% \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 -% internal pool of streams is maintained, and these are allocated and -% deallocated as needed by other modules. As a result, the programmer should -% close streams when they are no longer needed, to release them for other -% processes. -% -% Note that I/O operations are global: streams should all be declared -% with global names and treated accordingly. -% -% \begin{function}[added = 2011-09-26, updated = 2011-12-27] -% {\ior_new:N, \ior_new:c, \iow_new:N, \iow_new:c} -% \begin{syntax} -% \cs{ior_new:N} \meta{stream} -% \cs{iow_new:N} \meta{stream} -% \end{syntax} -% Globally reserves the name of the \meta{stream}, either for reading -% or for writing as appropriate. The \meta{stream} is not opened until -% the appropriate \cs[no-index]{\ldots_open:Nn} function is used. Attempting to -% use a \meta{stream} which has not been opened is an error, and the -% \meta{stream} will behave as the corresponding \cs[no-index]{c_term_\ldots}. -% \end{function} -% -% \begin{function}[updated = 2012-02-10]{\ior_open:Nn, \ior_open:cn} -% \begin{syntax} -% \cs{ior_open:Nn} \meta{stream} \Arg{file name} -% \end{syntax} -% Opens \meta{file name} for reading using \meta{stream} as the -% control sequence for file access. If the \meta{stream} was already -% open it is closed before the new operation begins. The -% \meta{stream} is available for access immediately and will remain -% allocated to \meta{file name} until a \cs{ior_close:N} instruction -% is given or the \TeX{} run ends. -% If the file is not found, an error is raised. -% \end{function} -% -% \begin{function}[added = 2013-01-12, TF]{\ior_open:Nn, \ior_open:cn} -% \begin{syntax} -% \cs{ior_open:NnTF} \meta{stream} \Arg{file name} \Arg{true code} \Arg{false code} -% \end{syntax} -% Opens \meta{file name} for reading using \meta{stream} as the -% control sequence for file access. If the \meta{stream} was already -% open it is closed before the new operation begins. The -% \meta{stream} is available for access immediately and will remain -% allocated to \meta{file name} until a \cs{ior_close:N} instruction -% is given or the \TeX{} run ends. The \meta{true code} is then inserted -% into the input stream. If the file is not found, no error is raised and -% the \meta{false code} is inserted into the input stream. -% \end{function} -% -% \begin{function}[updated = 2012-02-09] -% {\iow_open:Nn, \iow_open:NV, \iow_open:cn, \iow_open:cV} -% \begin{syntax} -% \cs{iow_open:Nn} \meta{stream} \Arg{file name} -% \end{syntax} -% Opens \meta{file name} for writing using \meta{stream} as the -% control sequence for file access. If the \meta{stream} was already -% open it is closed before the new operation begins. The -% \meta{stream} is available for access immediately and will remain -% allocated to \meta{file name} until a \cs{iow_close:N} instruction -% is given or the \TeX{} run ends. Opening a file for writing clears -% any existing content in the file (\emph{i.e.}~writing is \emph{not} -% additive). -% \end{function} -% -% \begin{function}[added = 2019-05-08]{\ior_shell_open:Nn} -% \begin{syntax} -% \cs{ior_shell_open:Nn} \meta{stream} \Arg{shell~command} -% \end{syntax} -% Opens the \emph{pseudo}-file created by the output of the -% \meta{shell command} for reading using \meta{stream} as the -% control sequence for access. If the \meta{stream} was already -% open it is closed before the new operation begins. The -% \meta{stream} is available for access immediately and will remain -% allocated to \meta{shell command} until a \cs{ior_close:N} instruction -% is given or the \TeX{} run ends. -% If piped system calls are disabled an error is raised. -% -% For details of handling of the \meta{shell command}, see -% \cs{sys_get_shell:nnNTF}. -% \end{function} -% -% \begin{function}[added = 2023-05-25]{\iow_shell_open:Nn} -% \begin{syntax} -% \cs{iow_shell_open:Nn} \meta{stream} \Arg{shell~command} -% \end{syntax} -% Opens the \emph{pseudo}-file created by the output of the -% \meta{shell command} for writing using \meta{stream} as the -% control sequence for access. If the \meta{stream} was already -% open it is closed before the new operation begins. The -% \meta{stream} is available for access immediately and will remain -% allocated to \meta{shell command} until a \cs{iow_close:N} instruction -% is given or the \TeX{} run ends. -% If piped system calls are disabled an error is raised. -% -% For details of handling of the \meta{shell command}, see -% \cs{sys_get_shell:nnNTF}. -% \end{function} -% -% \begin{function}[updated = 2012-07-31] -% {\ior_close:N, \ior_close:c, \iow_close:N, \iow_close:c} -% \begin{syntax} -% \cs{ior_close:N} \meta{stream} -% \cs{iow_close:N} \meta{stream} -% \end{syntax} -% Closes the \meta{stream}. Streams should always be closed when -% they are finished with as this ensures that they remain available -% to other programmers. -% \end{function} -% -% \begin{function}[added = 2021-05-11] -% { -% \ior_show:N, \ior_show:c, \ior_log:N, \ior_log:c, -% \iow_show:N, \iow_show:c, \iow_log:N, \iow_log:c -% } -% \begin{syntax} -% \cs{ior_show:N} \meta{stream} -% \cs{ior_log:N} \meta{stream} -% \cs{iow_show:N} \meta{stream} -% \cs{iow_log:N} \meta{stream} -% \end{syntax} -% Display (to the terminal or log file) the file name associated to -% the (read or write) \meta{stream}. -% \end{function} -% -% \begin{function}[added = 2017-06-27] -% { -% \ior_show_list:, \ior_log_list:, -% \iow_show_list:, \iow_log_list: -% } -% \begin{syntax} -% \cs{ior_show_list:} -% \cs{ior_log_list:} -% \cs{iow_show_list:} -% \cs{iow_log_list:} -% \end{syntax} -% Display (to the terminal or log file) a list of the file names -% associated with each open (read or write) stream. This is intended -% for tracking down problems. -% \end{function} -% -% \subsection{Reading from files} -% -% Reading from files and reading from the terminal are separate processes in -% \pkg{expl3}. The functions \cs{ior_get:NN} and \cs{ior_str_get:NN}, and their -% branching equivalents, are designed to work with \emph{files}. -% -% \begin{function}[noTF, added = 2012-06-24, updated = 2019-03-23]{\ior_get:NN} -% \begin{syntax} -% \cs{ior_get:NN} \meta{stream} \meta{token list variable} -% \cs{ior_get:NNTF} \meta{stream} \meta{token list variable} \meta{true code} \meta{false code} -% \end{syntax} -% Function that reads one or more lines (until an equal number of left -% and right braces are found) from the file input \meta{stream} and stores -% the result locally in the \meta{token list} variable. -% The material read from the \meta{stream} is tokenized by \TeX{} -% according to the category codes and \tn{endlinechar} in force when -% the function is used. Assuming normal settings, any lines which do -% not end in a comment character~|%| have the line ending -% converted to a space, so for example input -% \begin{verbatim} -% a b c -% \end{verbatim} -% results in a token list \verb*|a b c |. Any blank line is -% converted to the token \cs{par}. Therefore, blank lines can be -% skipped by using a test such as -% \begin{verbatim} -% \ior_get:NN \l_my_stream \l_tmpa_tl -% \tl_set:Nn \l_tmpb_tl { \par } -% \tl_if_eq:NNF \l_tmpa_tl \l_tmpb_tl -% ... -% \end{verbatim} -% Also notice that if multiple lines are read to match braces -% then the resulting token list can contain \cs{par} tokens. -% In the non-branching version, where the \meta{stream} is not open -% the \meta{tl var} is set to \cs{q_no_value}. -% \begin{texnote} -% This protected macro is a wrapper around the \TeX{} primitive -% \tn{read}. Regardless of settings, \TeX{} replaces trailing space -% and tab characters (character codes 32 and~9) in each line by an -% end-of-line character (character code \tn{endlinechar}, omitted if -% \tn{endlinechar} is negative or too large) before turning -% characters into tokens according to current category codes. With -% default settings, spaces appearing at the beginning of lines are -% also ignored. -% \end{texnote} -% \end{function} -% -% \begin{function}[noTF, added = 2016-12-04, updated = 2019-03-23] -% {\ior_str_get:NN} -% \begin{syntax} -% \cs{ior_str_get:NN} \meta{stream} \meta{token list variable} -% \cs{ior_str_get:NNTF} \meta{stream} \meta{token list variable} \meta{true code} \meta{false code} -% \end{syntax} -% Function that reads one line from the file input \meta{stream} and stores -% the result locally in the \meta{token list} variable. -% The material is read from the \meta{stream} as a series of tokens with -% category code $12$ (other), with the exception of space -% characters which are given category code $10$ (space). -% Multiple whitespace characters are retained by this process. It -% always only reads one line and any blank lines in the input -% result in the \meta{token list variable} being empty. Unlike -% \cs{ior_get:NN}, line ends do not receive any special treatment. Thus -% input -% \begin{verbatim} -% a b c -% \end{verbatim} -% results in a token list |a b c| with the letters |a|, |b|, and |c| -% having category code~12. -% In the non-branching version, where the\meta{stream} is not open -% the \meta{tl var} is set to \cs{q_no_value}. -% \begin{texnote} -% This protected macro is a wrapper around the \eTeX{} primitive -% \tn{readline}. Regardless of settings, \TeX{} removes trailing -% space and tab characters (character codes 32 and~9). However, the -% end-line character normally added by this primitive is not -% included in the result of \cs{ior_str_get:NN}. -% \end{texnote} -% \end{function} -% -% All mappings are done at the current group level, \emph{i.e.}~any -% local assignments made by the \meta{function} or \meta{code} discussed -% below remain in effect after the loop. -% -% \begin{function}[added = 2012-02-11]{\ior_map_inline:Nn} -% \begin{syntax} -% \cs{ior_map_inline:Nn} \meta{stream} \Arg{inline function} -% \end{syntax} -% Applies the \meta{inline function} to each set of \meta{lines} -% obtained by calling \cs{ior_get:NN} until reaching the end of the -% file. \TeX{} ignores any trailing new-line marker from the file it -% reads. The \meta{inline function} should consist of code which -% receives the \meta{line} as |#1|. -% \end{function} -% -% \begin{function}[added = 2012-02-11]{\ior_str_map_inline:Nn} -% \begin{syntax} -% \cs{ior_str_map_inline:Nn} \meta{stream} \Arg{inline function} -% \end{syntax} -% Applies the \meta{inline function} to every \meta{line} -% in the \meta{stream}. The material is read from the \meta{stream} -% as a series of tokens with category code $12$ (other), with the -% exception of space characters which are given category code $10$ -% (space). The \meta{inline function} should consist of code which -% receives the \meta{line} as |#1|. -% Note that \TeX{} removes trailing space and tab characters -% (character codes 32 and 9) from every line upon input. \TeX{} also -% ignores any trailing new-line marker from the file it reads. -% \end{function} -% -% \begin{function}[added = 2019-01-13]{\ior_map_variable:NNn} -% \begin{syntax} -% \cs{ior_map_variable:NNn} \meta{stream} \meta{tl~var} \Arg{code} -% \end{syntax} -% For each set of \meta{lines} obtained by calling \cs{ior_get:NN} -% until reaching the end of the file, stores the \meta{lines} in the -% \meta{tl~var} then applies the \meta{code}. The \meta{code} will -% usually make use of the \meta{variable}, but this is not enforced. -% The assignments to the \meta{variable} are local. -% Its value after the loop is the last set of \meta{lines}, or its -% original value if the \meta{stream} is empty. \TeX{} ignores -% any trailing new-line marker from the file it reads. -% This function is typically faster than \cs{ior_map_inline:Nn}. -% \end{function} -% -% \begin{function}[added = 2019-01-13]{\ior_str_map_variable:NNn} -% \begin{syntax} -% \cs{ior_str_map_variable:NNn} \meta{stream} \meta{variable} \Arg{code} -% \end{syntax} -% For each \meta{line} in the \meta{stream}, stores the \meta{line} in -% the \meta{variable} then applies the \meta{code}. The material is -% read from the \meta{stream} as a series of tokens with category code -% $12$ (other), with the exception of space characters which are given -% category code $10$ (space). The \meta{code} will usually make use -% of the \meta{variable}, but this is not enforced. The assignments -% to the \meta{variable} are local. Its value after the loop is the -% last \meta{line}, or its original value if the \meta{stream} is -% empty. Note that \TeX{} removes trailing -% space and tab characters (character codes 32 and 9) from every line -% upon input. \TeX{} also ignores any trailing new-line marker from -% the file it reads. -% This function is typically faster than \cs{ior_str_map_inline:Nn}. -% \end{function} -% -% \begin{function}[added = 2012-06-29]{\ior_map_break:} -% \begin{syntax} -% \cs{ior_map_break:} -% \end{syntax} -% Used to terminate a \cs[no-index]{ior_map_\ldots} function before all -% lines from the \meta{stream} have been processed. This -% normally takes place within a conditional statement, for example -% \begin{verbatim} -% \ior_map_inline:Nn \l_my_ior -% { -% \str_if_eq:nnTF { #1 } { bingo } -% { \ior_map_break: } -% { -% % Do something useful -% } -% } -% \end{verbatim} -% Use outside of a \cs[no-index]{ior_map_\ldots} scenario leads to low -% level \TeX{} errors. -% \begin{texnote} -% When the mapping is broken, additional tokens may be inserted -% before further items are taken -% from the input stream. This depends on the design of the mapping -% function. -% \end{texnote} -% \end{function} -% -% \begin{function}[added = 2012-06-29]{\ior_map_break:n} -% \begin{syntax} -% \cs{ior_map_break:n} \Arg{code} -% \end{syntax} -% Used to terminate a \cs[no-index]{ior_map_\ldots} function before all -% lines in the \meta{stream} have been processed, inserting -% the \meta{code} after the mapping has ended. This -% normally takes place within a conditional statement, for example -% \begin{verbatim} -% \ior_map_inline:Nn \l_my_ior -% { -% \str_if_eq:nnTF { #1 } { bingo } -% { \ior_map_break:n { <code> } } -% { -% % Do something useful -% } -% } -% \end{verbatim} -% Use outside of a \cs[no-index]{ior_map_\ldots} scenario leads to low -% level \TeX{} errors. -% \begin{texnote} -% When the mapping is broken, additional tokens may be inserted -% before the \meta{code} is -% inserted into the input stream. -% This depends on the design of the mapping function. -% \end{texnote} -% \end{function} -% -% \begin{function}[updated = 2012-02-10, EXP, pTF]{\ior_if_eof:N} -% \begin{syntax} -% \cs{ior_if_eof_p:N} \meta{stream} \\ -% \cs{ior_if_eof:NTF} \meta{stream} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests if the end of a file \meta{stream} has been reached during a reading -% operation. The test also returns a \texttt{true} value if -% the \meta{stream} is not open. -% \end{function} -% -% \subsection{Reading from the terminal} -% -% \begin{function}[added = 2019-03-23]{\ior_get_term:nN, \ior_str_get_term:nN} -% \begin{syntax} -% \cs{ior_get_term:nN} \meta{prompt} \meta{token list variable} -% \end{syntax} -% Function that reads one or more lines (until an equal number of left -% and right braces are found) from the terminal and stores -% the result locally in the \meta{token list} variable. Tokenization -% occurs as described for \cs{ior_get:NN} or \cs{ior_str_get:NN}, respectively. -% When the \meta{prompt} -% is empty, \TeX{} will wait for input without any other indication: -% typically the programmer will have provided a suitable text using -% e.g.~\cs{iow_term:n}. Where the \meta{prompt} is given, it will appear -% in the terminal followed by an |=|, e.g. -% \begin{verbatim} -% prompt= -% \end{verbatim} -% \end{function} -% -% \subsection{Writing to files} -% -% \begin{function}[updated = 2012-06-05] -% { -% \iow_now:Nn, \iow_now:NV, \iow_now:Ne, -% \iow_now:cn, \iow_now:cV, \iow_now:ce -% } -% \begin{syntax} -% \cs{iow_now:Nn} \meta{stream} \Arg{tokens} -% \end{syntax} -% This function writes \meta{tokens} to the specified -% \meta{stream} immediately (\emph{i.e.}~the write operation is called -% on expansion of \cs{iow_now:Nn}). -% \end{function} -% -% \begin{function}{\iow_log:n, \iow_log:e} -% \begin{syntax} -% \cs{iow_log:n} \Arg{tokens} -% \end{syntax} -% This function writes the given \meta{tokens} to the log (transcript) -% file immediately: it is a dedicated version of \cs{iow_now:Nn}. -% \end{function} -% -% \begin{function}{\iow_term:n, \iow_term:e} -% \begin{syntax} -% \cs{iow_term:n} \Arg{tokens} -% \end{syntax} -% This function writes the given \meta{tokens} to the terminal -% file immediately: it is a dedicated version of \cs{iow_now:Nn}. -% \end{function} -% -% \begin{function} -% { -% \iow_shipout:Nn, \iow_shipout:Ne, -% \iow_shipout:cn, \iow_shipout:ce -% } -% \begin{syntax} -% \cs{iow_shipout:Nn} \meta{stream} \Arg{tokens} -% \end{syntax} -% This function writes \meta{tokens} to the specified -% \meta{stream} when the current page is finalised (\emph{i.e.}~at -% shipout). The \texttt{e}-type variants expand the \meta{tokens} -% at the point where the function is used but \emph{not} when the -% resulting tokens are written to the \meta{stream} -% (\emph{cf.}~\cs{iow_shipout_e:Nn}). -% \begin{texnote} -% When using \pkg{expl3} with a format other than \LaTeX{}, new line -% characters inserted using \cs{iow_newline:} or using the -% line-wrapping code \cs{iow_wrap:nnnN} are not recognized in -% the argument of \cs{iow_shipout:Nn}. This may lead to the -% insertion of additional unwanted line-breaks. -% \end{texnote} -% \end{function} -% -% \begin{function}[updated = 2023-09-17] -% { -% \iow_shipout_e:Nn, \iow_shipout_e:Ne, -% \iow_shipout_e:cn, \iow_shipout_e:ce -% } -% \begin{syntax} -% \cs{iow_shipout_e:Nn} \meta{stream} \Arg{tokens} -% \end{syntax} -% This function writes \meta{tokens} to the specified -% \meta{stream} when the current page is finalised (\emph{i.e.}~at -% shipout). The \meta{tokens} are expanded at the time of writing -% in addition to any expansion when the function is used. This makes -% these functions suitable for including material finalised during -% the page building process (such as the page number integer). -% \begin{texnote} -% This is a wrapper around the \TeX{} primitive \tn{write}. -% When using \pkg{expl3} with a format other than \LaTeX{}, new line -% characters inserted using \cs{iow_newline:} or using the -% line-wrapping code \cs{iow_wrap:nnnN} are not recognized in -% the argument of \cs{iow_shipout:Nn}. This may lead to the -% insertion of additional unwanted line-breaks. -% \end{texnote} -% \end{function} -% -% \begin{function}[EXP]{\iow_char:N} -% \begin{syntax} -% \cs{iow_char:N} |\|\meta{char} -% \end{syntax} -% Inserts \meta{char} into the output stream. Useful when trying to -% write difficult characters such as |%|, |{|, |}|, -% \emph{etc.}~in messages, for example: -% \begin{verbatim} -% \iow_now:Ne \g_my_iow { \iow_char:N \{ text \iow_char:N \} } -% \end{verbatim} -% The function has no effect if writing is taking place without -% expansion (\emph{e.g.}~in the second argument of \cs{iow_now:Nn}). -% \end{function} -% -% \begin{function}[EXP]{\iow_newline:} -% \begin{syntax} -% \cs{iow_newline:} -% \end{syntax} -% Function to add a new line within the \meta{tokens} written to a -% file. The function has no effect if writing is taking place without -% expansion (\emph{e.g.}~in the second argument of \cs{iow_now:Nn}). -% \begin{texnote} -% When using \pkg{expl3} with a format other than \LaTeX{}, the -% character inserted by \cs{iow_newline:} is not recognized by -% \TeX{}, which may lead to the insertion of additional unwanted -% line-breaks. This issue only affects \cs{iow_shipout:Nn}, -% \cs{iow_shipout_e:Nn} and direct uses of primitive operations. -% \end{texnote} -% \end{function} -% -% \subsection{Wrapping lines in output} -% -% \begin{function}[added = 2012-06-28, updated = 2017-12-04] -% {\iow_wrap:nnnN, \iow_wrap:nenN} -% \begin{syntax} -% \cs{iow_wrap:nnnN} \Arg{text} \Arg{run-on text} \Arg{set up} \meta{function} -% \end{syntax} -% This function wraps the \meta{text} to a fixed number of -% characters per line. At the start of each line which is wrapped, -% the \meta{run-on text} is inserted. The line character count -% targeted is the value of \cs{l_iow_line_count_int} minus the -% number of characters in the \meta{run-on text} for all lines except -% the first, for which the target number of characters is simply -% \cs{l_iow_line_count_int} since there is no run-on text. The -% \meta{text} and \meta{run-on text} are exhaustively expanded by the -% function, with the following substitutions: -% \begin{itemize} -% \item |\\| or \cs{iow_newline:} may be used to force a new line, -% \item \verb*|\ | may be used to represent a forced space -% (for example after a control sequence), -% \item |\#|, |\%|, |\{|, |\}|, |\~| may be used to represent -% the corresponding character, -% \item \cs{iow_wrap_allow_break:} may be used to allow a line-break -% without inserting a space, -% \item \cs{iow_indent:n} may be used to indent a part of the -% \meta{text} (not the \meta{run-on text}). -% \end{itemize} -% Additional functions may be added to the wrapping by using the -% \meta{set up}, which is executed before the wrapping takes place: this -% may include overriding the substitutions listed. -% -% Any expandable material in the \meta{text} which is not to be expanded -% on wrapping should be converted to a string using \cs{token_to_str:N}, -% \cs{tl_to_str:n}, \cs{tl_to_str:N}, \emph{etc.} -% -% The result of the wrapping operation is passed as a braced argument to the -% \meta{function}, which is typically a wrapper around a write -% operation. The output of \cs{iow_wrap:nnnN} (\emph{i.e.}~the argument -% passed to the \meta{function}) consists of characters of category -% \enquote{other} (category code~12), with the exception of spaces which -% have category \enquote{space} (category code~10). This means that the -% output does \emph{not} expand further when written to a file. -% -% \begin{texnote} -% Internally, \cs{iow_wrap:nnnN} carries out an \texttt{e}-type expansion -% on the \meta{text} to expand it. This is done in such a way that -% \cs{exp_not:N} or \cs{exp_not:n} \emph{could} be used to prevent -% expansion of material. However, this is less conceptually clear than -% conversion to a string, which is therefore the supported method for -% handling expandable material in the \meta{text}. -% \end{texnote} -% \end{function} -% -% \begin{function}[added = 2023-04-25]{\iow_wrap_allow_break:} -% \begin{syntax} -% \cs{iow_wrap_allow_break:} -% \end{syntax} -% In the first argument of \cs{iow_wrap:nnnN} (for instance in -% messages), inserts a break-point that allows a line break. If -% no break occurs, this function adds nothing to the output. -% \end{function} -% -% \begin{function}[added = 2011-09-21]{\iow_indent:n} -% \begin{syntax} -% \cs{iow_indent:n} \Arg{text} -% \end{syntax} -% In the first argument of \cs{iow_wrap:nnnN} (for instance in messages), -% indents \meta{text} by four spaces. This function does not cause -% a line break, and only affects lines which start within the scope -% of the \meta{text}. In case the indented \meta{text} should appear -% on separate lines from the surrounding text, use |\\| to force -% line breaks. -% \end{function} -% -% \begin{variable}[added = 2012-06-24]{\l_iow_line_count_int} -% The maximum number of characters in a line to be written -% 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 \hologo{MiKTeX} systems. -% \end{variable} -% -% \subsection{Constant input--output streams, and variables} -% -% \begin{variable}[added = 2017-12-11]{\g_tmpa_ior, \g_tmpb_ior} -% Scratch input stream for global use. These are never used by -% the kernel code, and so are safe for use with any \LaTeX3-defined -% function. However, they may be overwritten by other non-kernel -% code and so should only be used for short-term storage. -% \end{variable} -% -% \begin{variable}{\c_log_iow, \c_term_iow} -% Constant output streams for writing to the log and to the terminal -% (plus the log), respectively. -% \end{variable} -% -% \begin{variable}[added = 2017-12-11]{\g_tmpa_iow, \g_tmpb_iow} -% Scratch output stream for global use. These are never used by -% the kernel code, and so are safe for use with any \LaTeX3-defined -% function. However, they may be overwritten by other non-kernel -% code and so should only be used for short-term storage. -% \end{variable} -% -% \subsection{Primitive conditionals} -% -% \begin{function}[EXP]{\if_eof:w} -% \begin{syntax} -% \cs{if_eof:w} \meta{stream} -% ~~\meta{true code} -% \cs{else:} -% ~~\meta{false code} -% \cs{fi:} -% \end{syntax} -% Tests if the \meta{stream} returns \enquote{end of file}, which is true -% for non-existent files. The \cs{else:} branch is optional. -% \begin{texnote} -% This is the \TeX{} primitive \tn{ifeof}. -% \end{texnote} -% \end{function} -% -% \section{File opertions} -% -% \subsection{Basic file operations} -% -% \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{dir} and -% \meta{ext} part for the main (top level) file and that this file -% always has empty \meta{dir} and \meta{ext} components. -% 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, updated = 2023-06-15] -% {\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 need -% not include the trailing slash. 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}[EXP, pTF, updated = 2023-09-18] -% {\file_if_exist:n, \file_if_exist:V} -% \begin{syntax} -% \cs{file_if_exist_p:n} \Arg{file name} -% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code} -% \end{syntax} -% Tests if \meta{file name} is found in the path as detailed for -% \cs{file_if_exist:nTF}. -% \end{function} -% -% \subsection{Information about files and file contents} -% -% Functions in this section return information about files as \pkg{expl3} -% \texttt{str} data, \emph{except} that the non-expandable functions set their -% return \emph{token list} to \cs{q_no_value} if the file requested is not -% found. As such, comparison of file names, hashes, sizes, etc., should use -% \cs{str_if_eq:nnTF} rather than \cs{tl_if_eq:nnTF} and so on. -% -% \begin{function}[rEXP, added = 2019-11-19] -% { -% \file_hex_dump:n, \file_hex_dump:V, -% \file_hex_dump:nnn, \file_hex_dump:Vnn -% } -% \begin{syntax} -% \cs{file_hex_dump:n} \Arg{file name} -% \cs{file_hex_dump:nnn} \Arg{file name} \Arg{start index} \Arg{end index} -% \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 hexadecimal dump of the file content 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. When the file is not found, the result of -% expansion is empty. The \Arg{start index} and \Arg{end index} values -% work as described for \cs{str_range:nnn}. -% \end{function} -% -% \begin{function}[noTF, added = 2019-11-19] -% { -% \file_get_hex_dump:nN, \file_get_hex_dump:VN, -% \file_get_hex_dump:nnnN, \file_get_hex_dump:VnnN -% } -% \begin{syntax} -% \cs{file_get_hex_dump:nN} \Arg{file name} \meta{tl var} -% \cs{file_get_hex_dump:nnnN} \Arg{file name} \Arg{start index} \Arg{end index} \meta{tl var} -% \end{syntax} -% Sets the \meta{tl var} to the result of applying -% \cs{file_hex_dump:n}/\cs{file_hex_dump:nnn} 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_mdfive_hash:n, \file_mdfive_hash:V} -% \begin{syntax} -% \cs{file_mdfive_hash: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 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. 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, \file_get_mdfive_hash:VN} -% \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, \file_size:V} -% \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] -% {\file_get_size:nN, \file_get_size:VN} -% \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, \file_timestamp:V} -% \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}. -% 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} -% -% \begin{function}[noTF, added = 2017-07-09, updated = 2019-02-16] -% {\file_get_timestamp:nN, \file_get_timestamp:VN} -% \begin{syntax} -% \cs{file_get_timestamp:nN} \Arg{file name} \meta{tl var} -% \end{syntax} -% 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} -% -% \begin{function}[added = 2019-05-13, updated = 2019-09-20, pTF, EXP] -% { -% \file_compare_timestamp:nNn, -% \file_compare_timestamp:nNV, -% \file_compare_timestamp:VNn, -% \file_compare_timestamp:VNV -% } -% \begin{syntax} -% \cs{file_compare_timestamp_p:nNn} \Arg{file-1} \meta{comparator} \Arg{file-2} -% \cs{file_compare_timestamp:nNnTF} \Arg{file-1} \meta{comparator} \Arg{file-2} \Arg{true code} \Arg{false code} -% \end{syntax} -% Compares the file stamps on the two \meta{files} as indicated by -% the \meta{comparator}, and inserts either the \meta{true code} -% or \meta{false case} as required. A file which is not found -% is treated as older than any file which is found. This allows for -% example the construct -% \begin{verbatim} -% \file_compare_timestamp:nNnT { source-file } > { derived-file } -% { -% % Code to regenerate derived file -% } -% \end{verbatim} -% to work when the derived file is entirely absent. The timestamp -% of two absent files is regarded as different. -% This is not available in older versions of \XeTeX{}. -% \end{function} -% -% \begin{function}[noTF, updated = 2019-02-16] -% {\file_get_full_name:nN, \file_get_full_name:VN} -% \begin{syntax} -% \cs{file_get_full_name:nN} \Arg{file name} \meta{tl} -% \cs{file_get_full_name:nNTF} \Arg{file name} \meta{tl} \Arg{true code} \Arg{false code} -% \end{syntax} -% Searches for \meta{file name} in the path as detailed for -% \cs{file_if_exist:nTF}, and if found sets the \meta{tl 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. -% In the non-branching version, the \meta{tl var} will be set to -% \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 = 2020-06-24] -% {\file_parse_full_name:nNNN, \file_parse_full_name:VNNN} -% \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} -% -% Before parsing, the \meta{full name} is expanded until only non-expandable -% tokens remain, except that active characters are also not expanded. -% Quotes (|"|) are invalid in file names and are discarded from the input. -% \end{function} -% -% \begin{function}[EXP, added = 2020-06-24] -% {\file_parse_full_name:n, \file_parse_full_name:V} -% \begin{syntax} -% \cs{file_parse_full_name:n} \Arg{full name} -% \end{syntax} -% Parses the \meta{full name} as described for \cs{file_parse_full_name:nNNN}, -% and leaves \meta{dir}, \meta{name}, and \meta{ext} in the input stream, -% each inside a pair of braces. -% \end{function} -% -% \begin{function}[EXP, added = 2020-06-24] -% {\file_parse_full_name_apply:nN, \file_parse_full_name_apply:VN} -% \begin{syntax} -% \cs{file_parse_full_name_apply:nN} \Arg{full name} \meta{function} -% \end{syntax} -% Parses the \meta{full name} as described for \cs{file_parse_full_name:nNNN}, -% and passes \meta{dir}, \meta{name}, and \meta{ext} as arguments to \meta{function}, -% as an \texttt{n}-type argument each, in this order. -% \end{function} -% -% \subsection{Accessing file contents} -% -% \begin{function}[noTF, added = 2019-01-16, updated = 2019-02-16] -% {\file_get:nnN, \file_get:VnN} -% \begin{syntax} -% \cs{file_get:nnN} \Arg{file name} \Arg{setup} \meta{tl} -% \cs{file_get:nnNTF} \Arg{file name} \Arg{setup} \meta{tl} \Arg{true code} \Arg{false code} -% \end{syntax} -% Defines \meta{tl} to the contents of \meta{file name}. -% Category codes may need to be set appropriately via the \meta{setup} -% argument. -% The non-branching version sets the \meta{tl} to \cs{q_no_value} if the file is -% not found. The branching version runs the \meta{true code} after the -% assignment to \meta{tl} if the file is found, and \meta{false code} -% otherwise. The file content will be tokenized using the current -% category code régime, -% \end{function} -% -% \begin{function}[updated = 2017-06-26]{\file_input:n, \file_input:V} -% \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}[added = 2023-05-18, EXP] -% {\file_input_raw:n, \file_input_raw:V} -% \begin{syntax} -% \cs{file_input_raw: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 \TeX{} source. No data concerning the file is -% tracked. If the file is not found, no action is taken. -% \begin{texnote} -% This function is intended only for contexts where files must -% be read purely by expansion, for example at the start of a -% table cell in an \tn{halign}. -% \end{texnote} -% \end{function} -% -% \begin{function}[added = 2014-07-02] -% { -% \file_if_exist_input:n, -% \file_if_exist_input:V, -% \file_if_exist_input:nF, -% \file_if_exist_input:VF -% } -% \begin{syntax} -% \cs{file_if_exist_input:n} \Arg{file name} -% \cs{file_if_exist_input:nF} \Arg{file name} \Arg{false code} -% \end{syntax} -% Searches for \meta{file name} using the current \TeX{} search -% path and the additional paths included in \cs{l_file_search_path_seq}. -% If found then -% reads in the file as additional \LaTeX{} source as described for -% \cs{file_input:n}, otherwise inserts the \meta{false code}. -% Note that these functions do not raise -% an error if the file is not found, in contrast to \cs{file_input:n}. -% \end{function} -% -% \begin{function}[added = 2017-07-07]{\file_input_stop:} -% \begin{syntax} -% \cs{file_input_stop:} -% \end{syntax} -% Ends the reading of a file started by \cs{file_input:n} or similar before -% the end of the file is reached. Where the file reading is being terminated -% due to an error, \cs[index = msg_critical:nn]{msg_critical:nn(nn)} -% should be preferred. -% \begin{texnote} -% This function must be used on a line on its own: \TeX{} reads files -% line-by-line and so any additional tokens in the \enquote{current} line -% will still be read. -% -% This is also true if the function is hidden inside another function -% (which will be the normal case), i.e., all tokens on the same line -% in the source file are still processed. Putting it on a line by itself -% in the definition doesn't help as it is the line where it is used that -% counts! -% \end{texnote} -% \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} -% -% \end{documentation} -% -% \begin{implementation} -% -% \section{\pkg{l3file} implementation} -% -% \TestFiles{m3file001} -% -% \begin{macrocode} -%<*package> -% \end{macrocode} -% -% \subsection{Input operations} -% -% \begin{macrocode} -%<@@=ior> -% \end{macrocode} -% -% \subsubsection{Variables and constants} -% -% \begin{variable}{\l_@@_internal_tl} -% Used as a short-term scratch variable. -% \begin{macrocode} -\tl_new:N \l_@@_internal_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\c_@@_term_ior} -% Reading from the terminal (with a prompt) is done using a positive -% but non-existent stream number. Unlike writing, there is no concept -% of reading from the log. -% \begin{macrocode} -\int_const:Nn \c_@@_term_ior { 16 } -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\g_@@_streams_seq} -% A list of the currently-available input streams to be used as a -% stack. -% \begin{macrocode} -\seq_new:N \g_@@_streams_seq -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_stream_tl} -% Used to recover the raw stream number from the stack. -% \begin{macrocode} -\tl_new:N \l_@@_stream_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\g_@@_streams_prop} -% The name of the file attached to each stream is tracked in a property -% list. To get the correct number of reserved streams in package mode the -% 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 \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 -\int_step_inline:nnn - { 0 } - { - \cs_if_exist:NTF \contextversion - { \tex_count:D 38 ~ } - { - \tex_count:D 16 ~ % - \cs_if_exist:NT \loccount { - 1 } - } - } - { - \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format } - } -% \end{macrocode} -% \end{variable} -% -% \subsubsection{Stream management} -% -% \begin{macro}{\ior_new:N, \ior_new:c} -% Reserving a new stream is done by defining the name as equal to using the -% terminal. -% \begin{macrocode} -\cs_new_protected:Npn \ior_new:N #1 { \cs_new_eq:NN #1 \c_@@_term_ior } -\cs_generate_variant:Nn \ior_new:N { c } -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\g_tmpa_ior, \g_tmpb_ior} -% The usual scratch space. -% \begin{macrocode} -\ior_new:N \g_tmpa_ior -\ior_new:N \g_tmpb_ior -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\ior_open:Nn, \ior_open:cn} -% 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} { \__kernel_file_missing:n {#2} } } -\cs_generate_variant:Nn \ior_open:Nn { c } -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\l_@@_file_name_tl} -% Data storage. -% \begin{macrocode} -\tl_new:N \l_@@_file_name_tl -% \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 -% which deals with streams. The |full_name| is empty when the file is -% not found. -% \begin{macrocode} -\prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF } - { - \file_get_full_name:nNTF {#2} \l_@@_file_name_tl - { - \__kernel_ior_open:No #1 \l_@@_file_name_tl - \prg_return_true: - } - { \prg_return_false: } - } -\prg_generate_conditional_variant:Nnn \ior_open:Nn { c } { T , F , TF } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_new:N} -% Streams are reserved using \tn{newread} before they -% can be managed by \pkg{ior}. To prevent \pkg{ior} from being -% affected by redefinitions of \tn{newread} (such as done by the -% third-party package \pkg{morewrites}), this macro is saved here -% under a private name. The complicated code ensures that -% \cs{@@_new:N} is not \tn{outer} despite plain \TeX{}'s \tn{newread} -% being \tn{outer}. For \ConTeXt{}, we have to deal with the fact -% that \tn{newread} works like our own: it actually checks before -% altering definition. -% \begin{macrocode} -\exp_args:NNf \cs_new_protected:Npn \@@_new:N - { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } } -\cs_if_exist:NT \contextversion - { - \cs_new_eq:NN \@@_new_aux:N \@@_new:N - \cs_gset_protected:Npn \@@_new:N #1 - { - \cs_undefine:N #1 - \@@_new_aux:N #1 - } - } -% \end{macrocode} -% \end{macro} -% -% \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. Life gets more complex as it's -% important to keep things in sync. That is done using a two-part approach: -% any streams that have already been taken up by \pkg{ior} but are now free -% 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 \__kernel_ior_open:Nn #1#2 - { - \ior_close:N #1 - \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl - { \@@_open_stream:Nn #1 {#2} } - { - \@@_new:N #1 - \__kernel_tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} } - \@@_open_stream:Nn #1 {#2} - } - } -\cs_generate_variant:Nn \__kernel_ior_open:Nn { No } -% \end{macrocode} -% Here, we act defensively in case \LuaTeX{} is in use with an -% extensionless file name. -% \begin{macrocode} -\cs_new_protected:Npe \@@_open_stream:Nn #1#2 - { - \tex_global:D \tex_chardef:D #1 = \exp_not:N \l_@@_stream_tl \scan_stop: - \prop_gput:NVn \exp_not:N \g_@@_streams_prop #1 {#2} - \tex_openin:D #1 - \sys_if_engine_luatex:TF - { {#2} } - { \exp_not:N \__kernel_file_name_quote:n {#2} \scan_stop: } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ior_shell_open:Nn} -% \begin{macro}{\@@_shell_open:nN, \@@_shell_open:oN} -% Actually much easier than either the standard open or input versions! -% When calling \cs{__kernel_ior_open:Nn} the file the pipe is added to -% signal a shell command, but the quotes are not added yet---they are -% added later by \cs{__kernel_file_name_quote:n}. -% \begin{macrocode} -\cs_new_protected:Npn \ior_shell_open:Nn #1#2 - { - \sys_if_shell:TF - { \@@_shell_open:oN { \tl_to_str:n {#2} } #1 } - { \msg_error:nn { kernel } { pipe-failed } } - } -\cs_new_protected:Npn \@@_shell_open:nN #1#2 - { - \tl_if_in:nnTF {#1} { " } - { - \msg_error:nne - { kernel } { quote-in-shell } {#1} - } - { \__kernel_ior_open:Nn #2 { |#1 } } - } -\cs_generate_variant:Nn \@@_shell_open:nN { o } -\msg_new:nnnn { kernel } { pipe-failed } - { Cannot~run~piped~system~commands. } - { - LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\ - Try~the~"--shell-escape"~(or~"--enable-pipes")~option. - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ior_close:N, \ior_close:c} -% Closing a stream means getting rid of it at the \TeX{} level and -% removing from the various data structures. Unless the name passed -% is an invalid stream number (outside the range $[0,15]$), it can be -% closed. On the other hand, it only gets added to the stack if it -% was not already there, to avoid duplicates building up. -% \begin{macrocode} -\cs_new_protected:Npn \ior_close:N #1 - { - \int_compare:nT { -1 < #1 < \c_@@_term_ior } - { - \tex_closein:D #1 - \prop_gremove:NV \g_@@_streams_prop #1 - \seq_if_in:NVF \g_@@_streams_seq #1 - { \seq_gpush:NV \g_@@_streams_seq #1 } - \cs_gset_eq:NN #1 \c_@@_term_ior - } - } -\cs_generate_variant:Nn \ior_close:N { c } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ior_show:N, \ior_log:N, \@@_show:NN} -% Seek the stream in the \cs{g_@@_streams_prop} list, then show the -% stream as open or closed accordingly. -% \begin{macrocode} -\cs_new_protected:Npn \ior_show:N { \@@_show:NN \tl_show:n } -\cs_generate_variant:Nn \ior_show:N { c } -\cs_new_protected:Npn \ior_log:N { \@@_show:NN \tl_log:n } -\cs_generate_variant:Nn \ior_log:N { c } -\cs_new_protected:Npn \@@_show:NN #1#2 - { - \__kernel_chk_defined:NT #2 - { - \prop_get:NVNTF \g_@@_streams_prop #2 \l_@@_internal_tl - { - \exp_args:Ne #1 - { \token_to_str:N #2 ~ open: ~ \l_@@_internal_tl } - } - { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } } - } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ior_show_list:, \ior_log_list:} -% \begin{macro}{\@@_list:N} -% Show the property lists, but with some \enquote{pretty printing}. -% See the \pkg{l3msg} module. The first argument of the message is -% |ior| (as opposed to |iow|) and the second is empty if no read -% stream is open and non-empty (the list of streams formatted using -% \cs{msg_show_item_unbraced:nn}) otherwise. The code of the message -% \texttt{show-streams} takes care of translating |ior|/|iow| to -% English. -% \begin{macrocode} -\cs_new_protected:Npn \ior_show_list: { \@@_list:N \msg_show:nneeee } -\cs_new_protected:Npn \ior_log_list: { \@@_list:N \msg_log:nneeee } -\cs_new_protected:Npn \@@_list:N #1 - { - #1 { kernel } { show-streams } - { ior } - { - \prop_map_function:NN \g_@@_streams_prop - \msg_show_item_unbraced:nn - } - { } { } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsubsection{Reading input} -% -% \begin{macro}{\if_eof:w} -% The primitive conditional -% \begin{macrocode} -\cs_new_eq:NN \if_eof:w \tex_ifeof:D -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[pTF]{\ior_if_eof:N} -% To test if some particular input stream is exhausted the following -% conditional is provided. The primitive test can only deal with -% numbers in the range $[0,15]$ so we catch outliers (they are -% exhausted). -% \begin{macrocode} -\prg_new_conditional:Npnn \ior_if_eof:N #1 { p , T , F , TF } - { - \if_int_compare:w -1 < #1 - \if_int_compare:w #1 < \c_@@_term_ior - \if_eof:w #1 - \prg_return_true: - \else: - \prg_return_false: - \fi: - \else: - \prg_return_true: - \fi: - \else: - \prg_return_true: - \fi: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ior_get:NN, \@@_get:NN} -% \begin{macro}[TF]{\ior_get:NN} -% And here we read from files. -% \begin{macrocode} -\cs_new_protected:Npn \ior_get:NN #1#2 - { \ior_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } } -\cs_new_protected:Npn \@@_get:NN #1#2 - { \tex_read:D #1 to #2 } -\prg_new_protected_conditional:Npnn \ior_get:NN #1#2 { T , F , TF } - { - \ior_if_eof:NTF #1 - { \prg_return_false: } - { - \@@_get:NN #1 #2 - \prg_return_true: - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ior_str_get:NN, \@@_str_get:NN} -% \begin{macro}[TF]{\ior_str_get:NN} -% Reading as strings is a more complicated wrapper, as we wish to -% remove the endline character and restore it afterwards. -% \begin{macrocode} -\cs_new_protected:Npn \ior_str_get:NN #1#2 - { \ior_str_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } } -\cs_new_protected:Npn \@@_str_get:NN #1#2 - { - \exp_args:Nno \use:n - { - \int_set:Nn \tex_endlinechar:D { -1 } - \tex_readline:D #1 to #2 - \int_set:Nn \tex_endlinechar:D - } { \int_use:N \tex_endlinechar:D } - } -\prg_new_protected_conditional:Npnn \ior_str_get:NN #1#2 { T , F , TF } - { - \ior_if_eof:NTF #1 - { \prg_return_false: } - { - \@@_str_get:NN #1 #2 - \prg_return_true: - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{variable}{\c_@@_term_noprompt_ior} -% For reading without a prompt. -% \begin{macrocode} -\int_const:Nn \c_@@_term_noprompt_ior { -1 } -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\ior_get_term:nN, \ior_str_get_term:nN} -% \begin{macro}{\@@_get_term:NnN} -% Getting from the terminal is better with pretty-printing. -% \begin{macrocode} -\cs_new_protected:Npn \ior_get_term:nN #1#2 - { \@@_get_term:NnN \@@_get:NN {#1} #2 } -\cs_new_protected:Npn \ior_str_get_term:nN #1#2 - { \@@_get_term:NnN \@@_str_get:NN {#1} #2 } -\cs_new_protected:Npn \@@_get_term:NnN #1#2#3 - { - \group_begin: - \tex_escapechar:D = -1 \scan_stop: - \tl_if_blank:nTF {#2} - { \exp_args:NNc #1 \c_@@_term_noprompt_ior } - { \exp_args:NNc #1 \c_@@_term_ior } - {#2} - \exp_args:NNNv \group_end: - \tl_set:Nn #3 {#2} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\ior_map_break:, \ior_map_break:n} -% Usual map breaking functions. -% \begin{macrocode} -\cs_new:Npn \ior_map_break: - { \prg_map_break:Nn \ior_map_break: { } } -\cs_new:Npn \ior_map_break:n - { \prg_map_break:Nn \ior_map_break: } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\ior_map_inline:Nn, \ior_str_map_inline:Nn} -% \begin{macro}{\@@_map_inline:NNn} -% \begin{macro}{\@@_map_inline:NNNn} -% \begin{macro}{\@@_map_inline_loop:NNN} -% Mapping over an input stream can be done on either a token or a string -% basis, hence the set up. Within that, there is a check to avoid reading -% past the end of a file, hence the two applications of \cs{ior_if_eof:N} -% and its lower-level analogue \cs{if_eof:w}. -% This mapping cannot be nested with twice the same stream, as the -% stream has only one \enquote{current line}. -% \begin{macrocode} -\cs_new_protected:Npn \ior_map_inline:Nn - { \@@_map_inline:NNn \@@_get:NN } -\cs_new_protected:Npn \ior_str_map_inline:Nn - { \@@_map_inline:NNn \@@_str_get:NN } -\cs_new_protected:Npn \@@_map_inline:NNn - { - \int_gincr:N \g__kernel_prg_map_int - \exp_args:Nc \@@_map_inline:NNNn - { @@_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__kernel_prg_map_int } - } -\cs_new_protected:Npn \@@_map_inline_loop:NNN #1#2#3 - { - #2 #3 \l_@@_internal_tl - \if_eof:w #3 - \exp_after:wN \ior_map_break: - \fi: - \exp_args:No #1 \l_@@_internal_tl - \@@_map_inline_loop:NNN #1#2#3 - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\ior_map_variable:NNn, \ior_str_map_variable:NNn} -% \begin{macro}{\@@_map_variable:NNNn} -% \begin{macro}{\@@_map_variable_loop:NNNn} -% Since the \TeX{} primitive (\tn{read} or \tn{readline}) assigns the -% tokens read in the same way as a token list assignment, we simply -% call the appropriate primitive. The end-of-loop is checked using -% the primitive conditional for speed. -% \begin{macrocode} -\cs_new_protected:Npn \ior_map_variable:NNn - { \@@_map_variable:NNNn \ior_get:NN } -\cs_new_protected:Npn \ior_str_map_variable:NNn - { \@@_map_variable:NNNn \ior_str_get:NN } -\cs_new_protected:Npn \@@_map_variable:NNNn #1#2#3#4 - { - \ior_if_eof:NF #2 { \@@_map_variable_loop:NNNn #1#2#3 {#4} } - \prg_break_point:Nn \ior_map_break: { } - } -\cs_new_protected:Npn \@@_map_variable_loop:NNNn #1#2#3#4 - { - #1 #2 #3 - \if_eof:w #2 - \exp_after:wN \ior_map_break: - \fi: - #4 - \@@_map_variable_loop:NNNn #1#2#3 {#4} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsection{Output operations} -% -% \begin{macrocode} -%<@@=iow> -% \end{macrocode} -% -% There is a lot of similarity here to the input operations, at least for -% many of the basics. Thus quite a bit is copied from the earlier material -% with minor alterations. -% -% \subsubsection{Variables and constants} -% -% \begin{variable}{\l_@@_internal_tl} -% Used as a short-term scratch variable. -% \begin{macrocode} -\tl_new:N \l_@@_internal_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\c_log_iow, \c_term_iow} -% Here we allocate two output streams for writing to the transcript -% file only (\cs{c_log_iow}) and to both the terminal and transcript -% file (\cs{c_term_iow}). Recent \LuaTeX{} provide $128$ write -% streams; we also use \cs{c_term_iow} as the first non-allowed write -% stream so its value depends on the engine. -% \begin{macrocode} -\int_const:Nn \c_log_iow { -1 } -\int_const:Nn \c_term_iow - { - \bool_lazy_and:nnTF - { \sys_if_engine_luatex_p: } - { \int_compare_p:nNn \tex_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. -% \begin{macrocode} -\seq_new:N \g_@@_streams_seq -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_stream_tl} -% Used to recover the raw stream number from the stack. -% \begin{macrocode} -\tl_new:N \l_@@_stream_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\g_@@_streams_prop} -% As for reads with the appropriate adjustment of the register numbers to -% check on. -% \begin{macrocode} -\prop_new:N \g_@@_streams_prop -\int_step_inline:nnn - { 0 } - { - \cs_if_exist:NTF \contextversion - { \tex_count:D 39 ~ } - { - \tex_count:D 17 ~ - \cs_if_exist:NT \loccount { - 1 } - } - } - { - \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format } - } -% \end{macrocode} -% \end{variable} -% -% \subsubsection{Internal auxiliaries} -% -% \begin{variable}{\s_@@_mark,\s_@@_stop} -% Internal scan marks. -% \begin{macrocode} -\scan_new:N \s_@@_mark -\scan_new:N \s_@@_stop -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[EXP]{\@@_use_i_delimit_by_s_stop:nw} -% Functions to gobble up to a scan mark. -% \begin{macrocode} -\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1} -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\q_@@_nil} -% Internal quarks. -% \begin{macrocode} -\quark_new:N \q_@@_nil -% \end{macrocode} -% \end{variable} -% -% \subsection{Stream management} -% -% \begin{macro}{\iow_new:N, \iow_new:c} -% Reserving a new stream is done by defining the name as equal to writing -% to the terminal: odd but at least consistent. -% \begin{macrocode} -\cs_new_protected:Npn \iow_new:N #1 { \cs_new_eq:NN #1 \c_term_iow } -\cs_generate_variant:Nn \iow_new:N { c } -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\g_tmpa_iow, \g_tmpb_iow} -% The usual scratch space. -% \begin{macrocode} -\iow_new:N \g_tmpa_iow -\iow_new:N \g_tmpb_iow -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\@@_new:N} -% As for read streams, copy \tn{newwrite}, making sure -% that it is not \tn{outer}. For \ConTeXt{}, we have to -% deal with the fact that \tn{newwrite} works like our -% own: it actually checks before altering definition. -% \begin{macrocode} -\exp_args:NNf \cs_new_protected:Npn \@@_new:N - { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } } -\cs_if_exist:NT \contextversion - { - \cs_new_eq:NN \@@_new_aux:N \@@_new:N - \cs_gset_protected:Npn \@@_new:N #1 - { - \cs_undefine:N #1 - \@@_new_aux:N #1 - } - } -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\l_@@_file_name_tl} -% Data storage. -% \begin{macrocode} -\tl_new:N \l_@@_file_name_tl -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\iow_open:Nn, \iow_open:NV, \iow_open:cn, \iow_open:cV} -% \begin{macro}{\@@_open_stream:Nn, \@@_open_stream:NV} -% The same idea as for reading, but without the path and without the need -% to allow for a conditional version. -% \begin{macrocode} -\cs_new_protected:Npn \iow_open:Nn #1#2 - { - \__kernel_tl_set:Nx \l_@@_file_name_tl - { \__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 } - { - \@@_new:N #1 - \__kernel_tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} } - \@@_open_stream:NV #1 \l_@@_file_name_tl - } - } -\cs_generate_variant:Nn \iow_open:Nn { NV , c , cV } -\cs_new_protected:Npn \@@_open_stream:Nn #1#2 - { - \tex_global:D \tex_chardef:D #1 = \l_@@_stream_tl \scan_stop: - \prop_gput:NVn \g_@@_streams_prop #1 {#2} - \tex_immediate:D \tex_openout:D - #1 \__kernel_file_name_quote:n {#2} \scan_stop: - } -\cs_generate_variant:Nn \@@_open_stream:Nn { NV } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\iow_shell_open:Nn} -% \begin{macro}{\@@_shell_open:nN, \@@_shell_open:oN} -% Very similar to the \texttt{ior} version -% \begin{macrocode} -\cs_new_protected:Npn \iow_shell_open:Nn #1#2 - { - \sys_if_shell:TF - { \@@_shell_open:oN { \tl_to_str:n {#2} } #1 } - { \msg_error:nn { kernel } { pipe-failed } } - } -\cs_new_protected:Npn \@@_shell_open:nN #1#2 - { - \tl_if_in:nnTF {#1} { " } - { - \msg_error:nne - { kernel } { quote-in-shell } {#1} - } - { \__kernel_iow_open:Nn #2 { |#1 } } - } -\cs_generate_variant:Nn \@@_shell_open:nN { o } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\iow_close:N, \iow_close:c} -% Closing a stream is not quite the reverse of opening one. First, -% the close operation is easier than the open one, and second as the -% stream is actually a number we can use it directly to show that the -% slot has been freed up. -% \begin{macrocode} -\cs_new_protected:Npn \iow_close:N #1 - { - \int_compare:nT { \c_log_iow < #1 < \c_term_iow } - { - \tex_immediate:D \tex_closeout:D #1 - \prop_gremove:NV \g_@@_streams_prop #1 - \seq_if_in:NVF \g_@@_streams_seq #1 - { \seq_gpush:NV \g_@@_streams_seq #1 } - \cs_gset_eq:NN #1 \c_term_iow - } - } -\cs_generate_variant:Nn \iow_close:N { c } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\iow_show:N, \iow_log:N, \@@_show:NN} -% Seek the stream in the \cs{g_@@_streams_prop} list, then show the -% stream as open or closed accordingly. -% \begin{macrocode} -\cs_new_protected:Npn \iow_show:N { \@@_show:NN \tl_show:n } -\cs_generate_variant:Nn \iow_show:N { c } -\cs_new_protected:Npn \iow_log:N { \@@_show:NN \tl_log:n } -\cs_generate_variant:Nn \iow_log:N { c } -\cs_new_protected:Npn \@@_show:NN #1#2 - { - \__kernel_chk_defined:NT #2 - { - \prop_get:NVNTF \g_@@_streams_prop #2 \l_@@_internal_tl - { - \exp_args:Ne #1 - { \token_to_str:N #2 ~ open: ~ \l_@@_internal_tl } - } - { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } } - } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\iow_show_list:, \iow_log_list:} -% \begin{macro}{\@@_list:N} -% Done as for input, but with a copy of the auxiliary so the name is correct. -% \begin{macrocode} -\cs_new_protected:Npn \iow_show_list: { \@@_list:N \msg_show:nneeee } -\cs_new_protected:Npn \iow_log_list: { \@@_list:N \msg_log:nneeee } -\cs_new_protected:Npn \@@_list:N #1 - { - #1 { kernel } { show-streams } - { iow } - { - \prop_map_function:NN \g_@@_streams_prop - \msg_show_item_unbraced:nn - } - { } { } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsubsection{Deferred writing} -% -% \begin{macro} -% { -% \iow_shipout_e:Nn, \iow_shipout_e:Ne, -% \iow_shipout_e:cn, \iow_shipout_e:ce -% } -% First the easy part, this is the primitive, which expects its -% argument to be braced. -% \begin{macrocode} -\cs_new_protected:Npn \iow_shipout_e:Nn #1#2 - { \tex_write:D #1 {#2} } -\cs_generate_variant:Nn \iow_shipout_e:Nn { Ne , c, ce } -% \end{macrocode} -% \end{macro} -% -% \begin{macro} -% { -% \iow_shipout:Nn, \iow_shipout:Ne, -% \iow_shipout:Nx, -% \iow_shipout:cn, \iow_shipout:ce, -% \iow_shipout:cx -% } -% With \eTeX{} available deferred writing without expansion is easy. -% \begin{macrocode} -\cs_new_protected:Npn \iow_shipout:Nn #1#2 - { \tex_write:D #1 { \exp_not:n {#2} } } -\cs_generate_variant:Nn \iow_shipout:Nn { Ne , c, ce } -\cs_generate_variant:Nn \iow_shipout:Nn { Nx , cx } -% \end{macrocode} -% \end{macro} -% -% \subsubsection{Immediate writing} -% -% \begin{macro}{\__kernel_iow_with:Nnn} -% \begin{macro}{\@@_with:nNnn, \@@_with:oNnn} -% 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 \__kernel_iow_with:Nnn #1#2 - { - \int_compare:nNnTF {#1} = {#2} - { \use:n } - { \@@_with:oNnn { \int_use:N #1 } #1 {#2} } - } -\cs_new_protected:Npn \@@_with:nNnn #1#2#3#4 - { - \int_set:Nn #2 {#3} - #4 - \int_set:Nn #2 {#1} - } -\cs_generate_variant:Nn \@@_with:nNnn { o } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro} -% { -% \iow_now:Nn, \iow_now:NV, \iow_now:Ne, -% \iow_now:Nx, -% \iow_now:cn, \iow_now:cV, \iow_now:ce, -% \iow_now:cx -% } -% This routine writes the second argument onto the output stream without -% expansion. If this stream isn't open, the output goes to the terminal -% instead. If the first argument is no output stream at all, we get an -% internal error. We don't use the expansion done by \tn{write} to -% 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{__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 - { - \__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 { NV , Ne , c , cV , ce } -\cs_generate_variant:Nn \iow_now:Nn { Nx , cx } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\iow_log:n, \iow_log:e, \iow_log:x} -% \begin{macro}{\iow_term:n, \iow_term:e, \iow_term:x} -% Writing to the log and the terminal directly are relatively easy; -% as we need the two \texttt{e}-type variants for bootstrapping, -% they are redefinitions here. -% \begin{macrocode} -\cs_new_protected:Npn \iow_log:n { \iow_now:Nn \c_log_iow } -\cs_set_protected:Npn \iow_log:e { \iow_now:Ne \c_log_iow } -\cs_generate_variant:Nn \iow_log:n { x } -\cs_new_protected:Npn \iow_term:n { \iow_now:Nn \c_term_iow } -\cs_set_protected:Npn \iow_term:e { \iow_now:Ne \c_term_iow } -\cs_generate_variant:Nn \iow_term:n { x } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsubsection{Special characters for writing} -% -% \begin{macro}{\iow_newline:} -% Global variable holding the character that forces a new line when -% something is written to an output stream. -% \begin{macrocode} -\cs_new:Npn \iow_newline: { ^^J } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\iow_char:N} -% Function to write any escaped char to an output stream. -% \begin{macrocode} -\cs_new_eq:NN \iow_char:N \cs_to_str:N -% \end{macrocode} -% \end{macro} -% -% \subsubsection{Hard-wrapping lines to a character count} -% -% The code here implements a generic hard-wrapping function. This is -% used by the messaging system, but is designed such that it is -% available for other uses. -% -% \begin{variable}{\l_iow_line_count_int} -% 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 \hologo{MiKTeX}. -% \begin{macrocode} -\int_new:N \l_iow_line_count_int -\int_set:Nn \l_iow_line_count_int { 78 } -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_newline_tl} -% The token list inserted to produce a new line, with the -% \meta{run-on text}. -% \begin{macrocode} -\tl_new:N \l_@@_newline_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_line_target_int} -% This stores the target line count: the full number of characters -% in a line, minus any part for a leader at the start of each line. -% \begin{macrocode} -\int_new:N \l_@@_line_target_int -% \end{macrocode} -% \end{variable} -% -% \begin{macro}{\@@_set_indent:n} -% \begin{macro}{\@@_unindent:w} -% \begin{variable}{\l_@@_one_indent_tl, \l_@@_one_indent_int} -% The \texttt{one_indent} variables hold one indentation marker and -% its length. The \cs{@@_unindent:w} auxiliary removes one -% indentation. The function \cs{@@_set_indent:n} (that could possibly -% be public) sets the indentation in a consistent way. We set it to -% four spaces by default. -% \begin{macrocode} -\tl_new:N \l_@@_one_indent_tl -\int_new:N \l_@@_one_indent_int -\cs_new:Npn \@@_unindent:w { } -\cs_new_protected:Npn \@@_set_indent:n #1 - { - \__kernel_tl_set:Nx \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 { } - } -\exp_args:Ne \@@_set_indent:n { \prg_replicate:nn { 4 } { ~ } } -% \end{macrocode} -% \end{variable} -% \end{macro} -% \end{macro} -% -% \begin{variable}{\l_@@_indent_tl, \l_@@_indent_int} -% The current indentation (some copies of \cs{l_@@_one_indent_tl}) and -% its number of characters. -% \begin{macrocode} -\tl_new:N \l_@@_indent_tl -\int_new:N \l_@@_indent_int -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_line_tl, \l_@@_line_part_tl} -% These hold the current line of text and a partial line to be added -% to it, respectively. -% \begin{macrocode} -\tl_new:N \l_@@_line_tl -\tl_new:N \l_@@_line_part_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_line_break_bool} -% Indicates whether the line was broken precisely at a chunk boundary. -% \begin{macrocode} -\bool_new:N \l_@@_line_break_bool -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_wrap_tl} -% Used for the expansion step before detokenizing, and for the output -% from wrapping text: fully expanded and with lines which are not -% overly long. -% \begin{macrocode} -\tl_new:N \l_@@_wrap_tl -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\c_@@_wrap_marker_tl} -% \begin{variable} -% { -% \c_@@_wrap_end_marker_tl, -% \c_@@_wrap_newline_marker_tl, -% \c_@@_wrap_allow_break_marker_tl, -% \c_@@_wrap_indent_marker_tl, -% \c_@@_wrap_unindent_marker_tl -% } -% Every special action of the wrapping code is starts with -% the same recognizable string, \cs{c_@@_wrap_marker_tl}. -% Upon seeing that \enquote{word}, the wrapping code reads -% one space-delimited argument to know what operation to -% perform. The setting of \tn{escapechar} here is not -% very important, but makes \cs{c_@@_wrap_marker_tl} look -% marginally nicer. -% \begin{macrocode} -\group_begin: - \int_set:Nn \tex_escapechar:D { -1 } - \tl_const:Ne \c_@@_wrap_marker_tl - { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } } -\group_end: -\tl_map_inline:nn - { { end } { newline } { allow_break } { indent } { unindent } } - { - \tl_const:ce { c_@@_wrap_ #1 _marker_tl } - { - \c_@@_wrap_marker_tl - #1 - \c_catcode_other_space_tl - } - } -% \end{macrocode} -% \end{variable} -% \end{variable} -% -% \begin{macro}{\iow_wrap_allow_break:} -% \begin{macro}[EXP]{\@@_wrap_allow_break:} -% \begin{macro}[EXP]{\@@_wrap_allow_break_error:} -% We set \cs{iow_wrap_allow_break:n} to produce an error when outside -% messages. Within wrapped message, it is set to \cs{@@_wrap_allow_break:} -% when valid and otherwise to \cs{@@_wrap_allow_break_error:}. The second -% produces an error expandably. -% \begin{macrocode} -\cs_new_protected:Npn \iow_wrap_allow_break: - { - \msg_error:nnnn { kernel } { iow-indent } - { \iow_wrap:nnnN } { \iow_wrap_allow_break: } - } -\cs_new:Npe \@@_wrap_allow_break: { \c_@@_wrap_allow_break_marker_tl } -\cs_new:Npn \@@_wrap_allow_break_error: - { - \msg_expandable_error:nnnn { kernel } { iow-indent } - { \iow_wrap:nnnN } { \iow_wrap_allow_break: } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\iow_indent:n} -% \begin{macro}[EXP]{\@@_indent:n} -% \begin{macro}[EXP]{\@@_indent_error:n} -% We set \cs{iow_indent:n} to produce an error when outside -% messages. Within wrapped message, it is set to \cs{@@_indent:n} when -% valid and otherwise to \cs{@@_indent_error:n}. The first places the -% instruction for increasing the indentation before its argument, and -% the instruction for unindenting afterwards. The second produces an -% error expandably. Note that there are no forced line-break, so -% the indentation only changes when the next line is started. -% \begin{macrocode} -\cs_new_protected:Npn \iow_indent:n #1 - { - \msg_error:nnnnn { kernel } { iow-indent } - { \iow_wrap:nnnN } { \iow_indent:n } {#1} - #1 - } -\cs_new:Npe \@@_indent:n #1 - { - \c_@@_wrap_indent_marker_tl - #1 - \c_@@_wrap_unindent_marker_tl - } -\cs_new:Npn \@@_indent_error:n #1 - { - \msg_expandable_error:nnnnn { kernel } { iow-indent } - { \iow_wrap:nnnN } { \iow_indent:n } {#1} - #1 - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\iow_wrap:nnnN, \iow_wrap:nenN} -% The main wrapping function works as follows. First give |\\|, -% \verb*|\ | and other formatting commands the correct definition for -% messages and perform the given setup~|#3|. The definition of -% \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: - \cs_if_exist_use:N \conditionally@traceoff - \int_set:Nn \tex_escapechar:D { -1 } - \cs_set:Npe \{ { \token_to_str:N \{ } - \cs_set:Npe \# { \token_to_str:N \# } - \cs_set:Npe \} { \token_to_str:N \} } - \cs_set:Npe \% { \token_to_str:N \% } - \cs_set:Npe \~ { \token_to_str:N \~ } - \int_set:Nn \tex_escapechar:D { 92 } - \cs_set_eq:NN \\ \iow_newline: - \cs_set_eq:NN \ \c_catcode_other_space_tl - \cs_set_eq:NN \iow_wrap_allow_break: \@@_wrap_allow_break: - \cs_set_eq:NN \iow_indent:n \@@_indent:n - #3 -% \end{macrocode} -% Then fully-expand the input: in package mode, the expansion uses -% \LaTeXe{}'s \tn{protect} mechanism in the same way as \tn{typeout}. -% In generic mode this setting is useless but harmless. As soon as -% the expansion is done, reset \cs{iow_indent:n} to its error -% definition: it only works in the first argument of -% \cs{iow_wrap:nnnN}. -% \begin{macrocode} - \cs_set_eq:NN \protect \token_to_str:N - \__kernel_tl_set:Nx \l_@@_wrap_tl {#1} - \cs_set_eq:NN \iow_wrap_allow_break: \@@_wrap_allow_break_error: - \cs_set_eq:NN \iow_indent:n \@@_indent_error:n -% \end{macrocode} -% Afterwards, set the newline marker (two assignments to fully expand, -% then convert to a string) and initialize the target count for lines -% (the first line has target count \cs{l_iow_line_count_int} instead). -% \begin{macrocode} - \__kernel_tl_set:Nx \l_@@_newline_tl { \iow_newline: #2 } - \__kernel_tl_set:Nx \l_@@_newline_tl { \tl_to_str:N \l_@@_newline_tl } - \int_set:Nn \l_@@_line_target_int - { \l_iow_line_count_int - \str_count:N \l_@@_newline_tl + 1 } -% \end{macrocode} -% Sanity check. -% \begin{macrocode} - \int_compare:nNnT { \l_@@_line_target_int } < 0 - { - \tl_set:Nn \l_@@_newline_tl { \iow_newline: } - \int_set:Nn \l_@@_line_target_int - { \l_iow_line_count_int + 1 } - } -% \end{macrocode} -% There is then a loop over the input, which stores the wrapped -% result in \cs{l_@@_wrap_tl}. After the loop, the resulting text is -% passed on to the function which has been given as a post-processor. -% The \cs{tl_to_str:N} step converts the \enquote{other} spaces back -% to normal spaces. The \texttt{f}-expansion removes a leading space -% from \cs{l_@@_wrap_tl}. -% \begin{macrocode} - \@@_wrap_do: - \exp_args:NNf \group_end: - #4 { \tl_to_str:N \l_@@_wrap_tl } - } -\cs_generate_variant:Nn \iow_wrap:nnnN { ne } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_do:, \@@_wrap_fix_newline:w, \@@_wrap_start:w} -% Escape spaces and change newlines to \cs{c_@@_wrap_newline_marker_tl}. -% Set up a few variables, in particular the initial -% value of \cs{l_@@_wrap_tl}: the space stops the -% \texttt{f}-expansion of the main wrapping function and -% \cs{use_none:n} removes a newline marker inserted by later code. -% The main loop consists of repeatedly calling the \texttt{chunk} -% auxiliary to wrap chunks delimited by (newline or indentation) -% markers. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_do: - { - \__kernel_tl_set:Nx \l_@@_wrap_tl - { - \exp_args:No \__kernel_str_to_other_fast:n \l_@@_wrap_tl - \c_@@_wrap_end_marker_tl - } - \__kernel_tl_set:Nx \l_@@_wrap_tl - { - \exp_after:wN \@@_wrap_fix_newline:w \l_@@_wrap_tl - ^^J \q_@@_nil ^^J \s_@@_stop - } - \exp_after:wN \@@_wrap_start:w \l_@@_wrap_tl - } -\cs_new:Npn \@@_wrap_fix_newline:w #1 ^^J #2 ^^J - { - #1 - \if_meaning:w \q_@@_nil #2 - \@@_use_i_delimit_by_s_stop:nw - \fi: - \c_@@_wrap_newline_marker_tl - \@@_wrap_fix_newline:w #2 ^^J - } -\cs_new_protected:Npn \@@_wrap_start:w - { - \bool_set_false:N \l_@@_line_break_bool - \tl_clear:N \l_@@_line_tl - \tl_clear:N \l_@@_line_part_tl - \tl_set:Nn \l_@@_wrap_tl { ~ \use_none:n } - \int_zero:N \l_@@_indent_int - \tl_clear:N \l_@@_indent_tl - \@@_wrap_chunk:nw { \l_iow_line_count_int } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_chunk:nw, \@@_wrap_next:nw} -% The \texttt{chunk} and \texttt{next} auxiliaries are defined -% indirectly to obtain the expansions of \cs{c_catcode_other_space_tl} -% and \cs{c_@@_wrap_marker_tl} in their definition. The \texttt{next} -% auxiliary calls a function corresponding to the type of marker (its -% |##2|), which can be \texttt{newline} or \texttt{indent} or -% \texttt{unindent} or \texttt{end}. The first argument of the -% \texttt{chunk} auxiliary is a target number of characters and the -% second is some string to wrap. If the chunk is empty simply call -% \texttt{next}. Otherwise, set up a call to \cs{@@_wrap_line:nw}, -% including the indentation if the current line is empty, and -% including a trailing space (|#1|) before the -% \cs{@@_wrap_end_chunk:w} auxiliary. -% \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1#2 - { - \cs_new_protected:Npn \@@_wrap_chunk:nw ##1##2 #2 - { - \tl_if_empty:nTF {##2} - { - \tl_clear:N \l_@@_line_part_tl - \@@_wrap_next:nw {##1} - } - { - \tl_if_empty:NTF \l_@@_line_tl - { - \@@_wrap_line:nw - { \l_@@_indent_tl } - ##1 - \l_@@_indent_int ; - } - { \@@_wrap_line:nw { } ##1 ; } - ##2 #1 - \@@_wrap_end_chunk:w 7 6 5 4 3 2 1 0 \s_@@_stop - } - } - \cs_new_protected:Npn \@@_wrap_next:nw ##1##2 #1 - { \use:c { @@_wrap_##2:n } {##1} } - } -\exp_args:NVV \@@_tmp:w \c_catcode_other_space_tl \c_@@_wrap_marker_tl -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_line:nw} -% \begin{macro}[EXP] -% { -% \@@_wrap_line_loop:w, -% \@@_wrap_line_aux:Nw, -% \@@_wrap_line_seven:nnnnnnn, -% \@@_wrap_line_end:NnnnnnnnN, -% \@@_wrap_line_end:nw, -% \@@_wrap_end_chunk:w -% } -% This is followed by \Arg{string} \meta{int expr} |;|. It stores the -% \meta{string} and up to \meta{int expr} characters from the current -% chunk into \cs{l_@@_line_part_tl}. Characters are grabbed 8~at a -% time and left in \cs{l_@@_line_part_tl} by the \texttt{line_loop} -% auxiliary. When $k<8$ remain to be found, the \texttt{line_aux} -% auxiliary calls the \texttt{line_end} auxiliary followed by (the -% single digit) $k$, then $7-k$ empty brace groups, then the chunk's -% remaining characters. The \texttt{line_end} auxiliary leaves -% $k$~characters from the chunk in the line part, then ends the -% assignment. Ignore the \cs{use_none:nnnnn} line for now. If the -% next character is a space the line can be broken there: -% store what we found into the result and get the next line. -% Otherwise some work is needed to find a break-point. So far we have -% ignored what happens if the chunk is shorter than the requested -% number of characters: this is dealt with by the \texttt{end_chunk} -% auxiliary, which gets treated like a character by the rest of the -% code. It ends up being called either as one of the arguments -% |#2|--|#9| of the \texttt{line_loop} auxiliary or as one of the -% arguments |#2|--|#8| of the \texttt{line_end} auxiliary. In both -% cases stop the assignment and work out how many characters are still -% needed. Notice that when we have exactly seven arguments to clean up, -% a \cs{exp_stop_f:} has to be inserted to stop the \cs{exp:w}. -% The weird \cs{use_none:nnnnn} ensures that the required -% data is in the right place. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_line:nw #1 - { - \tex_edef:D \l_@@_line_part_tl { \if_false: } \fi: - #1 - \exp_after:wN \@@_wrap_line_loop:w - \int_value:w \int_eval:w - } -\cs_new:Npn \@@_wrap_line_loop:w #1 ; #2#3#4#5#6#7#8#9 - { - \if_int_compare:w #1 < 8 \exp_stop_f: - \@@_wrap_line_aux:Nw #1 - \fi: - #2 #3 #4 #5 #6 #7 #8 #9 - \exp_after:wN \@@_wrap_line_loop:w - \int_value:w \int_eval:w #1 - 8 ; - } -\cs_new:Npn \@@_wrap_line_aux:Nw #1#2#3 \exp_after:wN #4 ; - { - #2 - \exp_after:wN \@@_wrap_line_end:NnnnnnnnN - \exp_after:wN #1 - \exp:w \exp_end_continue_f:w - \exp_after:wN \exp_after:wN - \if_case:w #1 \exp_stop_f: - \prg_do_nothing: - \or: \use_none:n - \or: \use_none:nn - \or: \use_none:nnn - \or: \use_none:nnnn - \or: \use_none:nnnnn - \or: \use_none:nnnnnn - \or: \@@_wrap_line_seven:nnnnnnn - \fi: - { } { } { } { } { } { } { } #3 - } -\cs_new:Npn \@@_wrap_line_seven:nnnnnnn #1#2#3#4#5#6#7 { \exp_stop_f: } -\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 - \token_if_eq_charcode:NNTF \c_space_token #9 - { \@@_wrap_line_end:nw { } } - { \if_false: { \fi: } \@@_wrap_break:w #9 } - } -\cs_new:Npn \@@_wrap_line_end:nw #1 - { - \if_false: { \fi: } - \@@_wrap_store_do:n {#1} - \@@_wrap_next_line:w - } -\cs_new:Npn \@@_wrap_end_chunk:w - #1 \int_eval:w #2 - #3 ; #4#5 \s_@@_stop - { - \if_false: { \fi: } - \exp_args:Nf \@@_wrap_next:nw { \int_eval:n { #2 - #4 } } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\@@_wrap_break:w} -% \begin{macro}[EXP] -% { -% \@@_wrap_break_first:w, -% \@@_wrap_break_none:w, -% \@@_wrap_break_loop:w, -% \@@_wrap_break_end:w, -% } -% Functions here are defined indirectly: \cs{@@_tmp:w} is eventually -% called with an \enquote{other} space as its argument. The goal is -% to remove from \cs{l_@@_line_part_tl} the part after the last space. -% In most cases this is done by repeatedly calling the -% \texttt{break_loop} auxiliary, which leaves \enquote{words} -% (delimited by spaces) until it hits the trailing space: then its -% argument |##3| is |?| \cs{@@_wrap_break_end:w} instead of a single -% token, and that \texttt{break_end} auxiliary leaves in the -% assignment the line until the last space, then calls -% \cs{@@_wrap_line_end:nw} to finish up the line and move on to the -% next. If there is no space in \cs{l_@@_line_part_tl} then the -% \texttt{break_first} auxiliary calls the \texttt{break_none} -% auxiliary. In that case, if the current line is empty, the complete -% word (including |##4|, characters beyond what we had grabbed) is -% added to the line, making it over-long. Otherwise, the word is -% used for the following line (and the last space of the line so far -% is removed because it was inserted due to the presence of a marker). -% \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1 - { - \cs_new:Npn \@@_wrap_break:w - { - \tex_edef:D \l_@@_line_part_tl - { \if_false: } \fi: - \exp_after:wN \@@_wrap_break_first:w - \l_@@_line_part_tl - #1 - { ? \@@_wrap_break_end:w } - \s_@@_mark - } - \cs_new:Npn \@@_wrap_break_first:w ##1 #1 ##2 - { - \use_none:nn ##2 \@@_wrap_break_none:w - \@@_wrap_break_loop:w ##1 #1 ##2 - } - \cs_new:Npn \@@_wrap_break_none:w ##1##2 #1 ##3 \s_@@_mark ##4 #1 - { - \tl_if_empty:NTF \l_@@_line_tl - { ##2 ##4 \@@_wrap_line_end:nw { } } - { \@@_wrap_line_end:nw { \@@_wrap_trim:N } ##2 ##4 #1 } - } - \cs_new:Npn \@@_wrap_break_loop:w ##1 #1 ##2 #1 ##3 - { - \use_none:n ##3 - ##1 #1 - \@@_wrap_break_loop:w ##2 #1 ##3 - } - \cs_new:Npn \@@_wrap_break_end:w ##1 #1 ##2 ##3 #1 ##4 \s_@@_mark - { ##1 \@@_wrap_line_end:nw { } ##3 } - } -\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_wrap_next_line:w} -% The special case where the end of a line coincides with the end of a -% chunk is detected here, to avoid a spurious empty line. Otherwise, -% call \cs{@@_wrap_line:nw} to find characters for the next line -% (remembering to account for the indentation). -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_next_line:w #1#2 \s_@@_stop - { - \tl_clear:N \l_@@_line_tl - \token_if_eq_meaning:NNTF #1 \@@_wrap_end_chunk:w - { - \tl_clear:N \l_@@_line_part_tl - \bool_set_true:N \l_@@_line_break_bool - \@@_wrap_next:nw { \l_@@_line_target_int } - } - { - \@@_wrap_line:nw - { \l_@@_indent_tl } - \l_@@_line_target_int - \l_@@_indent_int ; - #1 #2 \s_@@_stop - } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_allow_break:n} -% This is called after a chunk has been wrapped. The -% \cs{l_@@_line_part_tl} typically ends with a space (except at the -% beginning of a line?), which we remove since the -% \texttt{allow\_break} marker should not insert a space. Then move -% on with the next chunk, making sure to adjust the target number of -% characters for the line in case we did remove a space. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_allow_break:n #1 - { - \__kernel_tl_set:Nx \l_@@_line_tl - { \l_@@_line_tl \@@_wrap_trim:N \l_@@_line_part_tl } - \bool_set_false:N \l_@@_line_break_bool - \tl_if_empty:NTF \l_@@_line_part_tl - { \@@_wrap_chunk:nw {#1} } - { \exp_args:Nf \@@_wrap_chunk:nw { \int_eval:n { #1 + 1 } } } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_indent:n, \@@_wrap_unindent:n} -% These functions are called after a chunk has been wrapped, when -% encountering \texttt{indent}/\texttt{unindent} markers. Add the -% line part (last line part of the previous chunk) to the line so far -% and reset a boolean denoting the presence of a line-break. Most -% importantly, add or remove one indent from the current indent (both -% the integer and the token list). Finally, continue wrapping. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_indent:n #1 - { - \tl_put_right:Ne \l_@@_line_tl { \l_@@_line_part_tl } - \bool_set_false:N \l_@@_line_break_bool - \int_add:Nn \l_@@_indent_int { \l_@@_one_indent_int } - \tl_put_right:No \l_@@_indent_tl { \l_@@_one_indent_tl } - \@@_wrap_chunk:nw {#1} - } -\cs_new_protected:Npn \@@_wrap_unindent:n #1 - { - \tl_put_right:Ne \l_@@_line_tl { \l_@@_line_part_tl } - \bool_set_false:N \l_@@_line_break_bool - \int_sub:Nn \l_@@_indent_int { \l_@@_one_indent_int } - \__kernel_tl_set:Nx \l_@@_indent_tl - { \exp_after:wN \@@_unindent:w \l_@@_indent_tl } - \@@_wrap_chunk:nw {#1} - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_newline:n, \@@_wrap_end:n} -% These functions are called after a chunk has been line-wrapped, when -% encountering a \texttt{newline}/\texttt{end} marker. Unless we just -% took a line-break, store the line part and the line so far into the -% whole \cs{l_@@_wrap_tl}, trimming a trailing space. In the -% \texttt{newline} case look for a new line (of length -% \cs{l_@@_line_target_int}) in a new chunk. -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_newline:n #1 - { - \bool_if:NF \l_@@_line_break_bool - { \@@_wrap_store_do:n { \@@_wrap_trim:N } } - \bool_set_false:N \l_@@_line_break_bool - \@@_wrap_chunk:nw { \l_@@_line_target_int } - } -\cs_new_protected:Npn \@@_wrap_end:n #1 - { - \bool_if:NF \l_@@_line_break_bool - { \@@_wrap_store_do:n { \@@_wrap_trim:N } } - \bool_set_false:N \l_@@_line_break_bool - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_wrap_store_do:n} -% First add the last line part to the line, then append it to -% \cs{l_@@_wrap_tl} with the appropriate new line (with -% \enquote{run-on} text), possibly with its last space removed (|#1| -% is empty or \cs{@@_wrap_trim:N}). -% \begin{macrocode} -\cs_new_protected:Npn \@@_wrap_store_do:n #1 - { - \__kernel_tl_set:Nx \l_@@_line_tl - { \l_@@_line_tl \l_@@_line_part_tl } - \__kernel_tl_set:Nx \l_@@_wrap_tl - { - \l_@@_wrap_tl - \l_@@_newline_tl - #1 \l_@@_line_tl - } - \tl_clear:N \l_@@_line_tl - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP]{\@@_wrap_trim:N, \@@_wrap_trim:w, \@@_wrap_trim_aux:w} -% Remove one trailing \enquote{other} space from the argument if present. -% \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1 - { - \cs_new:Npn \@@_wrap_trim:N ##1 - { \exp_after:wN \@@_wrap_trim:w ##1 \s_@@_mark #1 \s_@@_mark \s_@@_stop } - \cs_new:Npn \@@_wrap_trim:w ##1 #1 \s_@@_mark - { \@@_wrap_trim_aux:w ##1 \s_@@_mark } - \cs_new:Npn \@@_wrap_trim_aux:w ##1 \s_@@_mark ##2 \s_@@_stop {##1} - } -\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl -% \end{macrocode} -% \end{macro} -% -% \begin{macrocode} -%<@@=file> -% \end{macrocode} -% -% \subsection{File operations} -% -% \begin{variable}{\l_@@_internal_tl} -% Used as a short-term scratch variable. -% \begin{macrocode} -\tl_new:N \l_@@_internal_tl -% \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: -% the name itself is set dynamically. -% \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 -% \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. In pre-loading, -% \tn{@currnamestack} is empty so is skipped. -% \begin{macrocode} -\seq_new:N \g_@@_stack_seq -\group_begin: - \cs_set_protected:Npn \@@_tmp:w #1#2#3 - { - \tl_if_blank:nTF {#1} - { - \cs_set:Npn \@@_tmp:w ##1 " ##2 " ##3 \s_@@_stop - { { } {##2} { } } - \seq_gput_right:Ne \g_@@_stack_seq - { - \exp_after:wN \@@_tmp:w \tex_jobname:D - " \tex_jobname:D " \s_@@_stop - } - } - { - \seq_gput_right:Nn \g_@@_stack_seq { { } {#1} {#2} } - \@@_tmp:w - } - } - \cs_if_exist:NT \@currnamestack - { - \tl_if_empty:NF \@currnamestack - { \exp_after:wN \@@_tmp:w \@currnamestack } - } -\group_end: -% \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! We -% will eventually copy the contents of \cs{@filelist}. -% \begin{macrocode} -\seq_new:N \g_@@_record_seq -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\l_@@_base_name_tl, \l_@@_full_name_tl} -% For storing the basename and full path whilst passing data internally. -% \begin{macrocode} -\tl_new:N \l_@@_base_name_tl -\tl_new:N \l_@@_full_name_tl -% \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. -% \begin{macrocode} -\seq_new:N \l_@@_tmp_seq -% \end{macrocode} -% \end{variable} -% -% \subsubsection{Internal auxiliaries} -% -% \begin{variable}{\s_@@_stop} -% Internal scan marks. -% \begin{macrocode} -\scan_new:N \s_@@_stop -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\q_@@_nil} -% Internal quarks. -% \begin{macrocode} -\quark_new:N \q_@@_nil -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[pTF]{\@@_quark_if_nil:n} -% Branching quark conditional. -% \begin{macrocode} -\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:n { TF } -% \end{macrocode} -% \end{macro} -% -% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop} -% Internal recursion quarks. -% \begin{macrocode} -\quark_new:N \q_@@_recursion_tail -\quark_new:N \q_@@_recursion_stop -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[EXP]{ -% \@@_if_recursion_tail_break:NN, -% \@@_if_recursion_tail_stop_do:Nn -% } -% Functions to query recursion quarks. -% \begin{macrocode} -\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:N -\__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:nn -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP]{\__kernel_file_name_sanitize:n} -% \begin{macro}[EXP]{ -% \@@_name_expand:n, -% \@@_name_expand_cleanup:Nw, -% \@@_name_expand_cleanup:w, -% \@@_name_expand_end:, -% \@@_name_expand_error:Nw, -% \@@_name_expand_error_aux:Nw, -% } -% \begin{macro}[EXP]{ -% \@@_name_strip_quotes:n, -% \@@_name_strip_quotes:nnnw, -% \@@_name_strip_quotes:nnn, -% } -% \begin{macro}[EXP]{ -% \@@_name_trim_spaces:n, -% \@@_name_trim_spaces:nw, -% \@@_name_trim_spaces_aux:n, -% \@@_name_trim_spaces_aux:w, -% } -% Expanding the file name uses a \tn{csname}-based approach, and -% relies on active characters (for example from UTF-8 characters) -% being properly set up to expand to a expansion-safe version using -% \cs{ifcsname}. This is less conservative than the token-by-token -% approach used before, but it is much faster. -% \begin{macrocode} -\cs_new:Npn \__kernel_file_name_sanitize:n #1 - { - \exp_args:Ne \@@_name_trim_spaces:n - { - \exp_args:Ne \@@_name_strip_quotes:n - { \@@_name_expand:n {#1} } - } - } -% \end{macrocode} -% -% We'll use \cs{cs:w} to start expanding the file name, and to avoid -% creating csnames equal to \tn{relax} with \enquote{common} names, -% there's a prefix |__file_name=| to the csname. There's also a guard -% token at the end so we can check if there was an error during the -% process and (try to) clean up gracefully. -% \begin{macrocode} -\cs_new:Npn \@@_name_expand:n #1 - { - \exp_after:wN \@@_name_expand_cleanup:Nw - \cs:w @@_name = #1 \cs_end: - \@@_name_expand_end: - } -% \end{macrocode} -% With the csname built, we grab it, and grab the remaining tokens -% delimited by \cs{@@_name_expand_end:}. If there are any remaining -% tokens, something bad happened, so we'll call the error procedure -% \cs{@@_name_expand_error:Nw}. -% If everything went according to plan, then use \cs{token_to_str:N} -% on the csname built, and call \cs{@@_name_expand_cleanup:w} to -% remove the prefix we added a while back. -% \cs{@@_name_expand_cleanup:w} takes a leading argument so we don't -% have to bother about the value of \cs{tex_escapechar:D}. -% \begin{macrocode} -\cs_new:Npn \@@_name_expand_cleanup:Nw #1 #2 \@@_name_expand_end: - { - \tl_if_empty:nF {#2} - { \@@_name_expand_error:Nw #2 \@@_name_expand_end: } - \exp_after:wN \@@_name_expand_cleanup:w \token_to_str:N #1 - } -\exp_last_unbraced:NNNNo -\cs_new:Npn \@@_name_expand_cleanup:w #1 \tl_to_str:n { @@_name = } { } -% \end{macrocode} -% In non-error cases \cs{@@_name_expand_end:} should not expand. It -% will only do so in case there is a \cs{csname} too much in the file -% name, so it will throw an error (while expanding), then insert the -% missing \cs{cs_end:} and yet another \cs{@@_name_expand_end:} that -% will be used as a delimiter by \cs{@@_name_expand_cleanup:Nw} (or -% that will expand again if yet another \cs{endcsname} is missing). -% \begin{macrocode} -\cs_new:Npn \@@_name_expand_end: - { - \msg_expandable_error:nn - { kernel } { filename-missing-endcsname } - \cs_end: \@@_name_expand_end: - } -% \end{macrocode} -% Now to the error case. \cs{@@_name_expand_error:Nw} adds an extra -% \cs{cs_end:} so that in case there was an extra \tn{csname} in the -% file name, then \cs{@@_name_expand_error_aux:Nw} throws the error. -% \begin{macrocode} -\cs_new:Npn \@@_name_expand_error:Nw #1 #2 \@@_name_expand_end: - { \@@_name_expand_error_aux:Nw #1 #2 \cs_end: \@@_name_expand_end: } -\cs_new:Npn \@@_name_expand_error_aux:Nw #1 #2 \cs_end: #3 - \@@_name_expand_end: - { - \msg_expandable_error:nnff - { kernel } { filename-chars-lost } - { \token_to_str:N #1 } { \exp_stop_f: #2 } - } -% \end{macrocode} -% Quoting file name uses basically the same approach as for -% \texttt{luaquotejobname}: count the |"| tokens and remove them. -% \begin{macrocode} -\cs_new:Npn \@@_name_strip_quotes:n #1 - { - \@@_name_strip_quotes:nw { 0 } - #1 " \q_@@_recursion_tail " \q_@@_recursion_stop {#1} - } -\cs_new:Npn \@@_name_strip_quotes:nw #1#2 " - { - \if_meaning:w \q_@@_recursion_tail #2 - \@@_name_strip_quotes_end:wnwn - \fi: - #2 - \@@_name_strip_quotes:nw { #1 + 1 } - } -\cs_new:Npn \@@_name_strip_quotes_end:wnwn \fi: #1 - \@@_name_strip_quotes:nw #2 \q_@@_recursion_stop #3 - { - \fi: - \int_if_odd:nT {#2} - { - \msg_expandable_error:nnn - { kernel } { unbalanced-quote-in-filename } {#3} - } - } -% \end{macrocode} -% Spaces need to be trimmed from the start of the name and from the end of -% any extension. However, the name we are passed might not have an extension: -% that means we have to look for one. If there is no extension, we still use -% the standard trimming function but deliberately prevent any spaces being -% removed at the end. -% \begin{macrocode} -\cs_new:Npn \@@_name_trim_spaces:n #1 - { \@@_name_trim_spaces:nw {#1} #1 . \q_@@_nil . \s_@@_stop } -\cs_new:Npn \@@_name_trim_spaces:nw #1#2 . #3 . #4 \s_@@_stop - { - \@@_quark_if_nil:nTF {#3} - { - \tl_trim_spaces_apply:nN { #1 \s_@@_stop } - \@@_name_trim_spaces_aux:n - } - { \tl_trim_spaces:n {#1} } - } -\cs_new:Npn \@@_name_trim_spaces_aux:n #1 - { \@@_name_trim_spaces_aux:w #1 } -\cs_new:Npn \@@_name_trim_spaces_aux:w #1 \s_@@_stop {#1} -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\__kernel_file_name_quote:n} -% \begin{macro}[EXP]{\@@_name_quote:nw} -% \begin{macrocode} -\cs_new:Npn \__kernel_file_name_quote:n #1 - { \@@_name_quote:nw {#1} #1 ~ \q_@@_nil \s_@@_stop } -\cs_new:Npn \@@_name_quote:nw #1 #2 ~ #3 \s_@@_stop - { - \@@_quark_if_nil:nTF {#3} - { #1 } - { "#1" } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{variable}{\c_@@_marker_tl} -% The same idea as the marker for rescanning token lists: this pair of -% tokens cannot appear in a file that is being input. -% \begin{macrocode} -\tl_const:Ne \c_@@_marker_tl { : \token_to_str:N : } -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[TF]{\file_get:nnN, \file_get:VnN} -% \begin{macro}{\file_get:nnN,\@@_get_aux:nnN,\@@_get_do:Nw} -% The approach here is similar to that for \cs{tl_set_rescan:Nnn}. -% The file contents are grabbed as an argument delimited by -% \cs{c_@@_marker_tl}. A few subtleties: braces in \cs{if_false:} -% \ldots{} \cs{fi:} to deal with possible alignment tabs, -% \tn{tracingnesting} to avoid a warning about a group being closed -% inside the \tn{scantokens}, and \cs{prg_return_true:} is placed -% after the end-of-file marker. -% \begin{macrocode} -\cs_new_protected:Npn \file_get:nnN #1#2#3 - { - \file_get:nnNF {#1} {#2} #3 - { \tl_set:Nn #3 { \q_no_value } } - } -\cs_generate_variant:Nn \file_get:nnN { V } -\prg_new_protected_conditional:Npnn \file_get:nnN #1#2#3 { T , F , TF } - { - \file_get_full_name:nNTF {#1} \l_@@_full_name_tl - { - \exp_args:NV \@@_get_aux:nnN - \l_@@_full_name_tl - {#2} #3 - \prg_return_true: - } - { \prg_return_false: } - } -\prg_generate_conditional_variant:Nnn \file_get:nnN { V } { T , F , TF } -\cs_new_protected:Npe \@@_get_aux:nnN #1#2#3 - { - \exp_not:N \if_false: { \exp_not:N \fi: - \group_begin: - \int_set_eq:NN \tex_tracingnesting:D \c_zero_int - \exp_not:N \exp_args:No \tex_everyeof:D - { \exp_not:N \c_@@_marker_tl } - #2 \scan_stop: - \exp_not:N \exp_after:wN \exp_not:N \@@_get_do:Nw - \exp_not:N \exp_after:wN #3 - \exp_not:N \exp_after:wN \exp_not:N \prg_do_nothing: - \exp_not:N \tex_input:D - \sys_if_engine_luatex:TF - { {#1} } - { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: } - \exp_not:N \if_false: } \exp_not:N \fi: - } -\exp_args:Nno \use:nn - { \cs_new_protected:Npn \@@_get_do:Nw #1#2 } - { \c_@@_marker_tl } - { - \group_end: - \tl_set:No #1 {#2} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_size:n} -% A copy of the primitive where it's available. -% \begin{macrocode} -\cs_new_eq:NN \@@_size:n \tex_filesize:D -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP]{\file_full_name:n, \@@_full_name:n, \@@_full_name_aux:n} -% \begin{macro}[EXP]{\@@_full_name_auxi:nn, \@@_full_name_auxii:nn} -% \begin{macro}[EXP]{\@@_full_name_aux:Nnn} -% \begin{macro}[EXP]{\@@_full_name_slash:n} -% \begin{macro}[EXP]{\@@_full_name_slash:w} -% \begin{macro}[EXP]{\@@_full_name_aux:nN} -% \begin{macro}[EXP]{\@@_full_name_aux:nnN} -% \begin{macro}[EXP]{\@@_name_cleanup:w} -% \begin{macro}[EXP]{\@@_name_end:} -% \begin{macro}[EXP]{\@@_name_ext_check:nn} -% \begin{macro}[EXP]{\@@_name_ext_check:nnw} -% \begin{macro}[EXP]{\@@_name_ext_check:nnnw} -% \begin{macro}[EXP]{\@@_name_ext_check:nnn} -% \begin{macro}[EXP]{\@@_name_ext_check:nnnn} -% 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_generate_variant:Nn \file_full_name:n { V } -% \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. -% To avoid unnecessary filesystem lookups, the result of \tn{pdffilesize} -% is kept available as an argument. -% For package mode, \tn{input@path} is a token list not a sequence. -% \begin{macrocode} -\cs_new:Npn \@@_full_name:n #1 - { - \tl_if_blank:nF {#1} - { \exp_args:Nne \@@_full_name_auxii:nn {#1} { \@@_full_name_aux:n {#1} } } - } -% \end{macrocode} -% To avoid repeated reading of files we need to cache the loading: -% this is important as the code here is used by \emph{all} file checks. -% The same marker is used in the \LaTeXe{} kernel, meaning that we get a -% double-saving with for example \cs{IfFileExists}. As this is all about -% performance, we use the low-level approach for the conditionals. For -% a file already seen, the size is reported as $-1$ so it's distinct from -% any non-cached ones. -% \begin{macrocode} -\cs_new:Npn \@@_full_name_aux:n #1 - { - \if_cs_exist:w @@_seen_ \tl_to_str:n {#1} : \cs_end: - -1 - \else: - \exp_args:Ne \@@_full_name_auxi:nn { \@@_size:n {#1} } {#1} - \fi: - } -% \end{macrocode} -% We will need the size of files later, and we have to avoid the -% \cs{scan_stop:} causing issues if we are raising the flag. Thus there is -% a slightly odd gobble here. -% \begin{macrocode} -\cs_new:Npn \@@_full_name_auxi:nn #1#2 - { - \if:w \scan_stop: #1 \scan_stop: - \else: - \exp_after:wN \use_none:n - \cs:w @@_seen_ \tl_to_str:n {#2} : \cs_end: - #1 - \fi: - } -\cs_new:Npn \@@_full_name_auxii:nn #1 #2 - { - \tl_if_blank:nTF {#2} - { - \seq_map_tokens:Nn \l_file_search_path_seq - { \@@_full_name_aux:Nnn \seq_map_break:n {#1} } - \cs_if_exist:NT \input@path - { - \tl_map_tokens:Nn \input@path - { \@@_full_name_aux:Nnn \tl_map_break:n {#1} } - } - \@@_name_end: - } - { \@@_ext_check:nn {#1} {#2} } - } -% \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:Nnn #1#2#3 - { - \exp_args:Ne \@@_full_name_aux:nN - { \@@_full_name_slash:n {#3} #2 } - #1 - } -\cs_new:Npn \@@_full_name_slash:n #1 - { - \@@_full_name_slash:nw {#1} #1 \q_nil / \q_nil / \q_nil \q_stop - } -\cs_new:Npn \@@_full_name_slash:nw #1#2 / \q_nil / #3 \q_stop - { - \quark_if_nil:nTF {#3} - { #1 / } - { #2 / } - } -\cs_new:Npn \@@_full_name_aux:nN #1 - { \exp_args:Nne \@@_full_name_aux:nnN {#1} { \@@_full_name_aux:n {#1} } } -\cs_new:Npn \@@_full_name_aux:nnN #1 #2 #3 - { - \tl_if_blank:nF {#2} - { - #3 - { - \@@_ext_check:nn {#1} {#2} - \@@_name_cleanup:w - } - } - } -\cs_new:Npn \@@_name_cleanup:w #1 \@@_name_end: { } -\cs_new:Npn \@@_name_end: { } -% \end{macrocode} -% As \TeX{} automatically adds |.tex| if there is no extension, -% there is a little clean up to do here. First, make sure we are not in the -% directory part, saving that. Then check for an extension. -% \begin{macrocode} -\cs_new:Npn \@@_ext_check:nn #1 #2 -{ \@@_ext_check:nnw {#2} { / } #1 / \q_@@_nil / \s_@@_stop } -\cs_new:Npn \@@_ext_check:nnw #1 #2 #3 / #4 / #5 \s_@@_stop - { - \@@_quark_if_nil:nTF {#4} - { - \exp_args:No \@@_ext_check:nnnw - { \use_none:n #2 } {#1} {#3} #3 . \q_@@_nil . \s_@@_stop - } - { \@@_ext_check:nnw {#1} { #2 #3 / } #4 / #5 \s_@@_stop } - } -\cs_new:Npe \@@_ext_check:nnnw #1#2#3#4 . #5 . #6 \s_@@_stop - { - \exp_not:N \@@_quark_if_nil:nTF {#5} - { - \exp_not:N \@@_ext_check:nnn - { #1 #3 \tl_to_str:n { .tex } } { #1 #3 } {#2} - } - { #1 #3 } - } -\cs_new:Npn \@@_ext_check:nnn #1 - { \exp_args:Nne \@@_ext_check:nnnn {#1} { \@@_full_name_aux:n {#1} } } -\cs_new:Npn \@@_ext_check:nnnn #1#2#3#4 - { - \tl_if_blank:nTF {#2} - {#3} - { - \bool_lazy_or:nnTF - { \int_compare_p:nNn {#4} = {#2} } - { \int_compare_p:nNn {#2} = { -1 } } - {#1} - {#3} - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \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} -% 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 } - { - \__kernel_tl_set:Nx #2 - { \file_full_name:n {#1} } - \tl_if_empty:NTF #2 - { \prg_return_false: } - { \prg_return_true: } - } -\prg_generate_conditional_variant:Nnn \file_get_full_name:nN - { V } { T , F , TF } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{variable}{\g_@@_internal_ior} -% A reserved stream to test for opening a shell. -% \begin{macrocode} -\ior_new:N \g_@@_internal_ior -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[rEXP] -% { -% \file_mdfive_hash:n, \file_mdfive_hash:V, -% \file_size:n, \file_size:V, -% \file_timestamp:n, \file_timestamp:V -% } -% \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_size:n #1 - { \@@_details:nn {#1} { size } } -\cs_generate_variant:Nn \file_size:n { V } -\cs_new:Npn \file_timestamp:n #1 - { \@@_details:nn {#1} { moddate } } -\cs_generate_variant:Nn \file_timestamp:n { V } -\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} } - } -\cs_new:Npn \file_mdfive_hash:n #1 - { \exp_args:Ne \@@_mdfive_hash:n { \file_full_name:n {#1} } } -\cs_generate_variant:Nn \file_mdfive_hash:n { V } -\cs_new:Npn \@@_mdfive_hash:n #1 - { \tex_mdfivesum:D file {#1} } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[rEXP]{\file_hex_dump:nnn, \file_hex_dump:Vnn, \@@_hex_dump_auxi:nnn} -% \begin{macro}[rEXP]{\@@_hex_dump_auxii:nnnn, \@@_hex_dump_auxiii:nnnn} -% \begin{macro}[rEXP]{\@@_hex_dump_auxiiv:nnn} -% \begin{macro}[rEXP]{\file_hex_dump:n, \file_hex_dump:V, \@@_hex_dump:n} -% These are separate as they need multiple arguments \emph{or} the -% file size. For \LuaTeX{}, the emulation does not need the file -% size so we save a little on expansion. -% \begin{macrocode} -\cs_new:Npn \file_hex_dump:nnn #1#2#3 - { - \exp_args:Neee \@@_hex_dump_auxi:nnn - { \file_full_name:n {#1} } - { \int_eval:n {#2} } - { \int_eval:n {#3} } - } -\cs_generate_variant:Nn \file_hex_dump:nnn { V } -\cs_new:Npn \@@_hex_dump_auxi:nnn #1#2#3 - { - \bool_lazy_any:nF - { - { \tl_if_blank_p:n {#1} } - { \int_compare_p:nNn {#2} = 0 } - { \int_compare_p:nNn {#3} = 0 } - } - { - \exp_args:Ne \@@_hex_dump_auxii:nnnn - { \@@_details_aux:nn {#1} { size } } - {#1} {#2} {#3} - } - } -\cs_new:Npn \@@_hex_dump_auxii:nnnn #1#2#3#4 - { - \int_compare:nNnTF {#3} > 0 - { \@@_hex_dump_auxiii:nnnn {#3} } - { - \exp_args:Ne \@@_hex_dump_auxiii:nnnn - { \int_eval:n { #1 + #3 } } - } - {#1} {#2} {#4} - } -\cs_new:Npn \@@_hex_dump_auxiii:nnnn #1#2#3#4 - { - \int_compare:nNnTF {#4} > 0 - { \@@_hex_dump_auxiv:nnn {#4} } - { - \exp_args:Ne \@@_hex_dump_auxiv:nnn - { \int_eval:n { #2 + #4 } } - } - {#1} {#3} - } -\cs_new:Npn \@@_hex_dump_auxiv:nnn #1#2#3 - { - \tex_filedump:D - offset ~ \int_eval:n { #2 - 1 } ~ - length ~ \int_eval:n { #1 - #2 + 1 } - {#3} - } -\cs_new:Npn \file_hex_dump:n #1 - { \exp_args:Ne \@@_hex_dump:n { \file_full_name:n {#1} } } -\cs_generate_variant:Nn \file_hex_dump:n { V } -\sys_if_engine_luatex:TF - { - \cs_new:Npn \@@_hex_dump:n #1 - { - \tl_if_blank:nF {#1} - { \tex_filedump:D whole {#1} {#1} } - } - } - { - \cs_new:Npn \@@_hex_dump:n #1 - { - \tl_if_blank:nF {#1} - { \tex_filedump:D length \tex_filesize:D {#1} {#1} } - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[noTF] -% { -% \file_get_hex_dump:nN, \file_get_hex_dump:VN, -% \file_get_mdfive_hash:nN, \file_get_mdfive_hash:VN, -% \file_get_size:nN, \file_get_size:VN, -% \file_get_timestamp:nN, \file_get_timestamp:VN -% } -% \begin{macro}{\@@_get_details:nnN} -% Non-expandable wrappers around the above in the case where appropriate -% primitive support exists. -% \begin{macrocode} -\cs_new_protected:Npn \file_get_hex_dump:nN #1#2 - { \file_get_hex_dump:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } } -\cs_generate_variant:Nn \file_get_hex_dump:nN { V } -\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 } } } -\cs_generate_variant:Nn \file_get_mdfive_hash:nN { V } -\cs_new_protected:Npn \file_get_size:nN #1#2 - { \file_get_size:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } } -\cs_generate_variant:Nn \file_get_size:nN { V } -\cs_new_protected:Npn \file_get_timestamp:nN #1#2 - { \file_get_timestamp:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } } -\cs_generate_variant:Nn \file_get_timestamp:nN { V } -\prg_new_protected_conditional:Npnn \file_get_hex_dump:nN #1#2 { T , F , TF } - { \@@_get_details:nnN {#1} { hex_dump } #2 } -\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nN - { V } { T , F , TF } -\prg_new_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2 { T , F , TF } - { \@@_get_details:nnN {#1} { mdfive_hash } #2 } -\prg_generate_conditional_variant:Nnn \file_get_mdfive_hash:nN - { V } { T , F , TF } -\prg_new_protected_conditional:Npnn \file_get_size:nN #1#2 { T , F , TF } - { \@@_get_details:nnN {#1} { size } #2 } -\prg_generate_conditional_variant:Nnn \file_get_size:nN - { V } { T , F , TF } -\prg_new_protected_conditional:Npnn \file_get_timestamp:nN #1#2 { T , F , TF } - { \@@_get_details:nnN {#1} { timestamp } #2 } -\prg_generate_conditional_variant:Nnn \file_get_timestamp:nN - { V } { T , F , TF } -\cs_new_protected:Npn \@@_get_details:nnN #1#2#3 - { - \__kernel_tl_set:Nx #3 - { \use:c { file_ #2 :n } {#1} } - \tl_if_empty:NTF #3 - { \prg_return_false: } - { \prg_return_true: } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[noTF]{\file_get_hex_dump:nnnN, \file_get_hex_dump:VnnN} -% Custom code due to the additional arguments. -% \begin{macrocode} -\cs_new_protected:Npn \file_get_hex_dump:nnnN #1#2#3#4 - { - \file_get_hex_dump:nnnNF {#1} {#2} {#3} #4 - { \tl_set:Nn #4 { \q_no_value } } - } -\cs_generate_variant:Nn \file_get_hex_dump:nnnN { V } -\prg_new_protected_conditional:Npnn \file_get_hex_dump:nnnN #1#2#3#4 - { T , F , TF } - { - \__kernel_tl_set:Nx #4 - { \file_hex_dump:nnn {#1} {#2} {#3} } - \tl_if_empty:NTF #4 - { \prg_return_false: } - { \prg_return_true: } - } -\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nnnN - { V } { T , F , TF } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP]{\@@_str_cmp:nn} -% As we are doing a fixed-length \enquote{big} integer comparison, it -% is easiest to use the low-level behavior of string comparisons. -% \begin{macrocode} -\cs_new_eq:NN \@@_str_cmp:nn \tex_strcmp:D -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[EXP, pTF] -% { -% \file_compare_timestamp:nNn, -% \file_compare_timestamp:nNV, -% \file_compare_timestamp:VNn, -% \file_compare_timestamp:VNV -% } -% \begin{macro}[EXP]{\@@_compare_timestamp:nnN} -% \begin{macro}[EXP]{\@@_timestamp:n} -% Comparison of file date can be done by using the low-level nature of the -% string comparison functions. -% \begin{macrocode} -\prg_new_conditional:Npnn \file_compare_timestamp:nNn #1#2#3 - { p , T , F , TF } - { - \exp_args:Nee \@@_compare_timestamp:nnN - { \file_full_name:n {#1} } - { \file_full_name:n {#3} } - #2 - } -\prg_generate_conditional_variant:Nnn \file_compare_timestamp:nNn - { nNV , V , VNV } { p , T , F , TF } -\cs_new:Npn \@@_compare_timestamp:nnN #1#2#3 - { - \tl_if_blank:nTF {#1} - { - \if_charcode:w #3 < - \prg_return_true: - \else: - \prg_return_false: - \fi: - } - { - \tl_if_blank:nTF {#2} - { - \if_charcode:w #3 > - \prg_return_true: - \else: - \prg_return_false: - \fi: - } - { - \if_int_compare:w - \@@_str_cmp:nn - { \@@_timestamp:n {#1} } - { \@@_timestamp:n {#2} } - #3 \c_zero_int - \prg_return_true: - \else: - \prg_return_false: - \fi: - } - } - } -\cs_new_eq:NN \@@_timestamp:n \tex_filemoddate:D -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[pTF]{\file_if_exist:n, \file_if_exist:V} -% 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_conditional:Npnn \file_if_exist:n #1 { p , T , F , TF } - { - \tl_if_blank:eTF { \file_full_name:n {#1} } - { \prg_return_false: } - { \prg_return_true: } - } -\prg_generate_conditional_variant:Nnn \file_if_exist:n { V } { p , T , F , TF } -% \end{macrocode} -% \end{macro} -% -% \begin{macro} -% { -% \file_if_exist_input:n, -% \file_if_exist_input:V, -% \file_if_exist_input:nF, -% \file_if_exist_input:VF -% } -% Input of a file with a test for existence. We do not define the |T| -% or |TF| variants because the most useful place to place the -% \meta{true code} would be inconsistent with other conditionals. -% \begin{macrocode} -\cs_new_protected:Npn \file_if_exist_input:n #1 - { - \file_get_full_name:nNT {#1} \l_@@_full_name_tl - { \@@_input:V \l_@@_full_name_tl } - } -\cs_generate_variant:Nn \file_if_exist_input:n { V } -\cs_new_protected:Npn \file_if_exist_input:nF #1#2 - { - \file_get_full_name:nNTF {#1} \l_@@_full_name_tl - { \@@_input:V \l_@@_full_name_tl } - {#2} - } -\cs_generate_variant:Nn \file_if_exist_input:nF { V } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\file_input_stop:} -% A simple rename. -% \begin{macrocode} -\cs_new_protected:Npn \file_input_stop: { \tex_endinput:D } -% \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 - { - \msg_error:nne { kernel } { file-not-found } - { \__kernel_file_name_sanitize:n {#1} } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\file_input:n, \file_input:V} -% \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:nNTF {#1} \l_@@_full_name_tl - { \@@_input:V \l_@@_full_name_tl } - { \__kernel_file_missing:n {#1} } - } -\cs_generate_variant:Nn \file_input:n { V } -\cs_new_protected:Npe \@@_input:n #1 - { - \exp_not:N \clist_if_exist:NTF \exp_not:N \@filelist - { \exp_not:N \@addtofilelist {#1} } - { \seq_gput_right:Nn \exp_not:N \g_@@_record_seq {#1} } - \exp_not:N \@@_input_push:n {#1} - \exp_not:N \tex_input:D - \sys_if_engine_luatex:TF - { {#1} } - { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: } - \exp_not:N \@@_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:Ne \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_eq:NN \__kernel_file_input_push:n \@@_input_push:n -\cs_new_protected:Npn \@@_input_pop: - { - \seq_gpop:NN \g_@@_stack_seq \l_@@_internal_tl - \exp_after:wN \@@_input_pop:nnn \l_@@_internal_tl - } -\cs_new_eq:NN \__kernel_file_input_pop: \@@_input_pop: -\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_input_raw:n, \file_input_raw:V, \@@_input_raw:nn} -% No error checking, no tracking. -% \begin{macrocode} -\cs_new:Npn \file_input_raw:n #1 - { \exp_args:Ne \@@_input_raw:nn { \file_full_name:n {#1} } {#1} } -\cs_generate_variant:Nn \file_input_raw:n { V } -\cs_new:Npe \@@_input_raw:nn #1#2 - { - \exp_not:N \tl_if_blank:nTF {#1} - { - \exp_not:N \exp_args:Nnne \exp_not:N \msg_expandable_error:nnn - { kernel } { file-not-found } - { \exp_not:N \__kernel_file_name_sanitize:n {#2} } - } - { - \exp_not:N \tex_input:D - \sys_if_engine_luatex:TF - { {#1} } - { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: } - } - } -\exp_args_generate:n { nne } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\file_parse_full_name:n, \file_parse_full_name:V} -% \begin{macro}{\file_parse_full_name_apply:nN, \file_parse_full_name_apply:VN} -% The main parsing macro \cs{file_parse_full_name_apply:nN} passes the -% file name |#1| through \cs{__kernel_file_name_sanitize:n} so that we -% have a single normalised way to treat files internally. -% \cs{file_parse_full_name:n} uses the former, with -% \cs{prg_do_nothing:} to -% leave each part of the name within a pair of braces. -% \begin{macrocode} -\cs_new:Npn \file_parse_full_name:n #1 - { - \file_parse_full_name_apply:nN {#1} - \prg_do_nothing: - } -\cs_generate_variant:Nn \file_parse_full_name:n { V } -\cs_new:Npn \file_parse_full_name_apply:nN #1 - { - \exp_args:Ne \@@_parse_full_name_auxi:nN - { \__kernel_file_name_sanitize:n {#1} } - } -\cs_generate_variant:Nn \file_parse_full_name_apply:nN { V } -% \end{macrocode} -% -% \begin{macro}{\@@_parse_full_name_auxi:nN} -% \begin{macro}{\@@_parse_full_name_area:nw} -% \cs{@@_parse_full_name_area:nw} splits the file name into chunks -% separated by |/|, until the last one is reached. The last chunk is -% the file name plus the extension, and everything before that is the -% path. When \cs{@@_parse_full_name_area:nw} is done, it leaves -% the path within braces after the scan mark \cs{s_@@_stop} and -% proceeds parsing the actual file name. -% \begin{macrocode} -\cs_new:Npn \@@_parse_full_name_auxi:nN #1 - { - \@@_parse_full_name_area:nw { } #1 - / \s_@@_stop - } -\cs_new:Npn \@@_parse_full_name_area:nw #1 #2 / #3 \s_@@_stop - { - \tl_if_empty:nTF {#3} - { \@@_parse_full_name_base:nw { } #2 . \s_@@_stop {#1} } - { \@@_parse_full_name_area:nw { #1 / #2 } #3 \s_@@_stop } - } -% \end{macrocode} -% -% \begin{macro}{\@@_parse_full_name_base:nw} -% \cs{@@_parse_full_name_base:nw} does roughly the same as above, but -% it separates the chunks at each period. However here there's some -% extra complications: In case |#1| is empty, it is assumed that the -% extension is actually empty, and the file name is |#2|. Besides, an -% extra |.| has to be added to |#2| because it is later removed in -% \cs{@@_parse_full_name_tidy:nnnN}. In any case, if there's an -% extension, it is returned with a leading |.|. -% \begin{macrocode} -\cs_new:Npn \@@_parse_full_name_base:nw #1 #2 . #3 \s_@@_stop - { - \tl_if_empty:nTF {#3} - { - \tl_if_empty:nTF {#1} - { - \tl_if_empty:nTF {#2} - { \@@_parse_full_name_tidy:nnnN { } { } } - { \@@_parse_full_name_tidy:nnnN { .#2 } { } } - } - { \@@_parse_full_name_tidy:nnnN {#1} { .#2 } } - } - { \@@_parse_full_name_base:nw { #1 . #2 } #3 \s_@@_stop } - } -% \end{macrocode} -% -% \begin{macro}{\@@_parse_full_name_tidy:nnnN} -% Now we just need to tidy some bits left loose before. The loop -% used in the two macros above start with a leading |/| and |.| in the -% file path an name, so here we need to remove them, except in the -% path, if it is a single |/|, in which case it's left as is. After -% all's done, pass to |#4|. -% \begin{macrocode} -\cs_new:Npn \@@_parse_full_name_tidy:nnnN #1 #2 #3 #4 - { - \exp_args:Nee #4 - { - \str_if_eq:nnF {#3} { / } { \use_none:n } - #3 \prg_do_nothing: - } - { \use_none:n #1 \prg_do_nothing: } - {#2} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\file_parse_full_name:nNNN, \file_parse_full_name:VNNN} -% \begin{macrocode} -\cs_new_protected:Npn \file_parse_full_name:nNNN #1 #2 #3 #4 - { - \file_parse_full_name_apply:nN {#1} - \@@_full_name_assign:nnnNNN #2 #3 #4 - } -\cs_new_protected:Npn \@@_full_name_assign:nnnNNN #1 #2 #3 #4 #5 #6 - { - \str_set:Nn #4 {#1} - \str_set:Nn #5 {#2} - \str_set:Nn #6 {#3} - } -\cs_generate_variant:Nn \file_parse_full_name:nNNN { V } -% \end{macrocode} -% \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:nneeee } -\cs_new_protected:Npn \file_log_list: { \@@_list:N \msg_log:nneeee } -\cs_new_protected:Npn \@@_list:N #1 - { - \seq_clear:N \l_@@_tmp_seq - \clist_if_exist:NT \@filelist - { - \exp_args:NNe \seq_set_from_clist:Nn \l_@@_tmp_seq - { \tl_to_str:N \@filelist } - } - \seq_concat:NNN \l_@@_tmp_seq \l_@@_tmp_seq \g_@@_record_seq - \seq_remove_duplicates:N \l_@@_tmp_seq - #1 { 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} -\cs_if_exist:NT \@filelist - { - \AtBeginDocument - { - \exp_args:NNe \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 - } - } -% \end{macrocode} -% -% \subsection{GetIdInfo} -% -% \begin{macro}{\GetIdInfo} -% \begin{macro}{\@@_id_info_auxi:w, \@@_id_info_auxii:w, \@@_id_info_auxiii:w} -% As documented in \pkg{expl3.dtx} this function extracts file name -% etc from an \textsc{svn} \texttt{Id} line. This used to be how we -% got version number and so on in all modules, so it had to be defined -% in \pkg{l3bootstrap}. Now it's more convenient to define it after -% we have set up quite a lot of tools, and \pkg{l3file} seems the -% least unreasonable place for it. -% -% The idea here is to extract out the information needed from a standard -% \textsc{svn} \texttt{Id} line, but to avoid a line that would get -% changed when the file is checked in. Hence the fact that none of the -% lines here include both a dollar sign and the \texttt{Id} keyword! -% \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 - \group_end: - \@@_id_info_auxi:w - } -% \end{macrocode} -% A first check for a completely empty \textsc{svn} field. If that is -% not the case, there is a second case when a file created using -% \texttt{svn cp} but has not been checked in. That leaves a special -% marker \texttt{-1} version, which has no further data. Dealing -% correctly with that is the reason for the space in the line to use -% \cs{@@_id_info_auxii:w}. -% \begin{macrocode} -\cs_new_protected:Npn \@@_id_info_auxi:w $ #1 $ #2 - { - \tl_set:Nn \ExplFileDescription {#2} - \str_if_eq:nnTF {#1} { Id } - { - \tl_set:Nn \ExplFileDate { 0000/00/00 } - \tl_set:Nn \ExplFileName { [unknown] } - \tl_set:Nn \ExplFileExtension { [unknown~extension] } - \tl_set:Nn \ExplFileVersion {-1} - } - { \@@_id_info_auxii:w #1 ~ \s_@@_stop } - } -% \end{macrocode} -% Here, |#1| is |Id|, |#2| is the file name, |#3| is the extension, -% |#4| is the version, |#5| is the check in date and |#6| is the check -% in time and user, plus some trailing spaces. If |#4| is the marker -% |-1| value then |#5| and |#6| are empty. -% \begin{macrocode} -\cs_new_protected:Npn \@@_id_info_auxii:w - #1 ~ #2.#3 ~ #4 ~ #5 ~ #6 \s_@@_stop - { - \tl_set:Nn \ExplFileName {#2} - \tl_set:Nn \ExplFileExtension {#3} - \tl_set:Nn \ExplFileVersion {#4} - \str_if_eq:nnTF {#4} {-1} - { \tl_set:Nn \ExplFileDate { 0000/00/00 } } - { \@@_id_info_auxiii:w #5 - 0 - 0 - \s_@@_stop } - } -% \end{macrocode} -% Convert an \textsc{svn}-style date into a \LaTeX{}-style one. -% \begin{macrocode} -\cs_new_protected:Npn \@@_id_info_auxiii:w #1 - #2 - #3 - #4 \s_@@_stop - { \tl_set:Nn \ExplFileDate { #1/#2/#3 } } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsection{Checking the version of kernel dependencies} -% -% \begin{macro}{\__kernel_dependency_version_check:Nn} -% \begin{macro}{\__kernel_dependency_version_check:nn} -% \begin{macro}{\@@_kernel_dependency_compare:nnn,\@@_parse_version:w} -% This function is responsible for checking if dependencies of the -% \LaTeX3 kernel match the version preloaded in the \LaTeXe{} kernel. -% If versions don't match, the function attempts to tell why by -% searching for a possible stray format file. -% -% The function starts by checking that the kernel date is defined, and -% if not zero is used to force the error route. The kernel date is -% then compared with the argument requested date (ususally the -% packaging date of the dependency). If the kernel date is less than -% the required date, it's an error and the loading should abort. -% \begin{macrocode} -\cs_new_protected:Npn \__kernel_dependency_version_check:Nn #1 - { \exp_args:NV \__kernel_dependency_version_check:nn #1 } -\cs_new_protected:Npn \__kernel_dependency_version_check:nn #1 - { - \cs_if_exist:NTF \c__kernel_expl_date_tl - { - \exp_args:NV \@@_kernel_dependency_compare:nnn - \c__kernel_expl_date_tl {#1} - } - { \@@_kernel_dependency_compare:nnn { 0000-00-00 } {#1} } - } -\cs_new_protected:Npn \@@_kernel_dependency_compare:nnn #1 #2 #3 - { - \int_compare:nNnT - { \@@_parse_version:w #1 \s_@@_stop } < - { \@@_parse_version:w #2 \s_@@_stop } - { \@@_mismatched_dependency_error:nn {#2} {#3} } - } -\cs_new:Npn \@@_parse_version:w #1 - #2 - #3 \s_@@_stop {#1#2#3} -% \end{macrocode} -% -% \begin{macro}{\@@_mismatched_dependency_error:nn} -% If the versions differ, then we try to give the user some guidance. -% This function starts by taking the engine name \cs{c_sys_engine_str} -% and replacing |tex| by |latex|, then building a command of the form: -% \begin{texttt} -% kpsewhich --all --engine=\meta{engine} \meta{format}[-dev].fmt -% \end{texttt} -% to query the format files available. A shell is opened and each -% line is read into a sequence. -% \begin{macrocode} -\cs_new_protected:Npn \@@_mismatched_dependency_error:nn #1 #2 - { - \exp_args:NNe \ior_shell_open:Nn \g_@@_internal_ior - { - kpsewhich ~ --all ~ - --engine = \c_sys_engine_exec_str - \c_space_tl \c_sys_engine_format_str - \bool_lazy_and:nnT - { \tl_if_exist_p:N \development@branch@name } - { ! \tl_if_empty_p:N \development@branch@name } - { -dev } .fmt - } - \seq_clear:N \l_@@_tmp_seq - \ior_map_inline:Nn \g_@@_internal_ior - { \seq_put_right:Nn \l_@@_tmp_seq {##1} } - \ior_close:N \g_@@_internal_ior - \msg_error:nnnn { kernel } { mismatched-support-file } - {#1} {#2} -% \end{macrocode} -% And finish by ending the current file. -% \begin{macrocode} - \tex_endinput:D - } -% \end{macrocode} -% -% Now define the actual error message: -% \begin{macrocode} -\msg_new:nnnn { kernel } { mismatched-support-file } - { - Mismatched~LaTeX~support~files~detected. \\ - Loading~'#2'~aborted! -% \end{macrocode} -% \cs{c__kernel_expl_date_tl} may not exist, due to an older format, -% so only print the dates when the sentinel token list exists: -% \begin{macrocode} - \tl_if_exist:NT \c__kernel_expl_date_tl - { - \\ \\ - The~L3~programming~layer~in~the~LaTeX~format \\ - is~dated~\c__kernel_expl_date_tl,~but~in~your~TeX~ - tree~the~files~require \\ at~least~#1. - } - } - { -% \end{macrocode} -% The sequence containing the format files should have exactly one -% item: the format file currently being run. If that's the case, the -% cause of the error is not that, so print a generic help with some -% possible causes. If more than one format file was found, then print -% the list to the user, with appropriate indications of what's in the -% system and what's in the user tree. -% \begin{macrocode} - \int_compare:nNnTF { \seq_count:N \l_@@_tmp_seq } > 1 - { - The~cause~seems~to~be~an~old~format~file~in~the~user~tree. \\ - LaTeX~found~these~files: - \seq_map_tokens:Nn \l_@@_tmp_seq { \\~-~\use:n } \\ - Try~deleting~the~file~in~the~user~tree~then~run~LaTeX~again. - } - { - The~most~likely~causes~are: - \\~-~A~recent~format~generation~failed; - \\~-~A~stray~format~file~in~the~user~tree~which~needs~ - to~be~removed~or~rebuilt; - \\~-~You~are~running~a~manually~installed~version~of~#2 \\ - \ \ \ which~is~incompatible~with~the~version~in~LaTeX. \\ - } - \\ - LaTeX~will~abort~loading~the~incompatible~support~files~ - but~this~may~lead~to \\ later~errors.~Please~ensure~that~ - your~LaTeX~format~is~correctly~regenerated. - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsection{Messages} -% -% \begin{macrocode} -\msg_new:nnnn { kernel } { file-not-found } - { File~'#1'~not~found. } - { - The~requested~file~could~not~be~found~in~the~current~directory,~ - in~the~TeX~search~path~or~in~the~LaTeX~search~path. - } -\msg_new:nnn { kernel } { file-list } - { - >~File~List~< - #1 \\ - ............. - } -\msg_new:nnnn { kernel } { filename-chars-lost } - { #1~invalid~in~file~name.~Lost:~#2. } - { - There~was~an~invalid~token~in~the~file~name~that~caused~ - the~characters~following~it~to~be~lost. - } -\msg_new:nnnn { kernel } { filename-missing-endcsname } - { Missing~\iow_char:N\\endcsname~inserted~in~filename. } - { - The~file~name~had~more~\iow_char:N\\csname~commands~than~ - \iow_char:N\\endcsname~ones.~LaTeX~will~add~the~missing~ - \iow_char:N\\endcsname~and~try~to~continue~as~best~as~it~can. - } -\msg_new:nnnn { kernel } { unbalanced-quote-in-filename } - { Unbalanced~quotes~in~file~name~'#1'. } - { - File~names~must~contain~balanced~numbers~of~quotes~("). - } -\msg_new:nnnn { kernel } { iow-indent } - { Only~#1 allows~#2 } - { - The~command~#2 can~only~be~used~in~messages~ - which~will~be~wrapped~using~#1. - \tl_if_empty:nF {#3} { ~ It~was~called~with~argument~'#3'. } - } -% \end{macrocode} -% -% \subsection{Functions delayed from earlier modules} -% -%<@@=sys> -% -% \begin{variable}{\c_sys_platform_str} -% Detecting the platform on \LuaTeX{} is easy: for other engines, we use -% the fact that the two common cases have special null files. It is possible -% to probe further (see package \pkg{platform}), but that requires shell -% escape and seems unlikely to be useful. This is set up here as it requires -% file searching. -% \begin{macrocode} -\sys_if_engine_luatex:TF - { - \str_const:Ne \c_sys_platform_str - { \tex_directlua:D { tex.print(os.type) } } - } - { - \file_if_exist:nTF { nul: } - { - \file_if_exist:nF { /dev/null } - { \str_const:Nn \c_sys_platform_str { windows } } - } - { - \file_if_exist:nT { /dev/null } - { \str_const:Nn \c_sys_platform_str { unix } } - } - } -\cs_if_exist:NF \c_sys_platform_str - { \str_const:Nn \c_sys_platform_str { unknown } } -% \end{macrocode} -% \end{variable} -% \begin{macro}[pTF]{\sys_if_platform_unix:} -% \begin{macro}[pTF]{\sys_if_platform_windows:} -% We can now set up the tests. -% \begin{macrocode} -\clist_map_inline:nn { unix , windows } - { - \@@_const:nn { sys_if_platform_ #1 } - { \str_if_eq_p:Vn \c_sys_platform_str { #1 } } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macrocode} -%</package> -% \end{macrocode} -% -% \end{implementation} -% -% \PrintIndex |