summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3file.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3file.dtx1547
1 files changed, 1419 insertions, 128 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
index 12ce5b17b17..28f53230235 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: l3file.dtx Copyright (C) 2009-2012 The LaTeX3 Project
+%% File: l3file.dtx Copyright (C) 1990-2012 The LaTeX3 Project
%%
%% It may be distributed and/or modified under the conditions of the
%% LaTeX Project Public License (LPPL), either version 1.3c of this
@@ -35,8 +35,8 @@
%
%<*driver|package>
\RequirePackage{l3names}
-\GetIdInfo$Id: l3file.dtx 3287 2012-01-31 22:08:14Z joseph $
- {L3 Experimental file operations}
+\GetIdInfo$Id: l3file.dtx 3464 2012-02-26 15:30:29Z bruno $
+ {L3 Experimental file and I/O operations}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
@@ -47,7 +47,7 @@
% \fi
%
% \title{^^A
-% The \pkg{l3file} package\\ File operations^^A
+% The \pkg{l3file} package\\ File and I/O operations^^A
% \thanks{This file describes v\ExplFileVersion,
% last revised \ExplFileDate.}^^A
% }
@@ -67,16 +67,16 @@
%
% \begin{documentation}
%
-% In contrast to the \pkg{l3io} module, which deals with the lowest
-% level of file management, the \pkg{l3file} module provides a higher
-% level interface for handling file contents. This involves providing
-% convenient wrappers around many of the functions in \pkg{l3io}
-% to make them more generally accessible.
+% This module provides functions for working with external files. Some of these
+% functions apply to an entire file, and have prefix \cs{file_\ldots}, while
+% others are used to work with files on a line by line basis and have prefix
+% \cs{ior_\ldots} (reading) or \cs{iow_\ldots} (writing).
%
-% It is important to remember that \TeX{} will attempt to locate files 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.
+% It is important to remember that when reading external files \TeX{} will
+% attempt to locate them 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.
%
% \section{File operation functions}
%
@@ -88,7 +88,7 @@
% \cs{file_input:n}.
% \end{variable}
%
-% \begin{function}[TF, updated = 2012-01-25]{\file_if_exist:n}
+% \begin{function}[TF, updated = 2012-02-10]{\file_if_exist:n}
% \begin{syntax}
% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code}
% \end{syntax}
@@ -102,14 +102,15 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}[updated = 2012-01-25]{\file_add_path:nN}
+% \begin{function}[updated = 2012-02-10]{\file_add_path:nN}
% \begin{syntax}
% \cs{file_add_path:nN} \Arg{file name} \meta{tl var}
% \end{syntax}
% Searches for \meta{file name} in the path as detailed for
% \cs{file_if_exist:nTF}, and if found sets the \meta{tl var} the
% fully-qualified name of the file, \emph{i.e}.~the path and file name.
-% If the file is not found then the \meta{tl var} will be empty.
+% If the file is not found then the \meta{tl var} will contain the
+% marker \cs{q_no_value}.
% \begin{texnote}
% The \meta{file name} may contain both literal items and expandable
% content, which should on full expansion be the desired file name.
@@ -119,7 +120,7 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}[updated = 2012-01-25]{\file_input:n}
+% \begin{function}[updated = 2012-02-17]{\file_input:n}
% \begin{syntax}
% \cs{file_input:n} \Arg{file name}
% \end{syntax}
@@ -127,7 +128,7 @@
% \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.
+% function. An error will be raised if the file is not found
% \begin{texnote}
% The \meta{file name} may contain both literal items and expandable
% content, which should on full expansion be the desired file name.
@@ -137,24 +138,63 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}[added = 2012-01-25]{\file_split_path_name_ext:nNNN}
+% \begin{function}{\file_path_include:n}
% \begin{syntax}
-% \cs{file_split_path_name_ext:nNNN} \Arg{name} \meta{path} \meta{filename} \meta{ext}
+% \cs{file_path_include:n} \Arg{path}
% \end{syntax}
-% Splits the file \meta{name} into any \meta{path} (up to the last |/|),
-% any \meta{ext} (from the last |.| to the end of the \meta{name}),
-% and the the \meta{filename} (the rest of the \meta{name}). Thus for
-% example
-% \begin{verbatim}
-% \file_split_path_name_ext:nNNN { figures / example.foo.eps }
-% \end{verbatim}
-% will result in \meta{path} \texttt{figures/}, \meta{filename}
-% \texttt{example.foo} and \meta{ext} \texttt{eps}.
-% The three parts of the \meta{name} will be stored in the
-% token list variables given as the \meta{name}, \meta{path} and
-% \meta{filename} arguments.
+% Adds \meta{path} to the list of those used to search when reading
+% files. The assignment is local.
+% \end{function}
+%
+% \begin{function}{\file_path_remove:n}
+% \begin{syntax}
+% \cs{file_path_remove:n} \Arg{path}
+% \end{syntax}
+% Removes \meta{path} from the list of those used to search when reading
+% files. The assignment is local.
+% \end{function}
+%
+% \begin{function}{\file_list:}
+% \begin{syntax}
+% \cs{file_list:}
+% \end{syntax}
+% This function will list all files loaded using \cs{file_input:n}
+% in the log file.
+% \end{function}
+%
+% \subsection{Input--output stream management}
+%
+% As \TeX{} is limited to $16$ input streams and $16$ 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.
+%
+% \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:Nn} \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{\ldots_open:Nn} function is used. Attempting to use
+% a \meta{stream} which has not been opened will result in a \TeX{}
+% error.
+% \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 file ends.
% \begin{texnote}
-% The \meta{name} may contain both literal items and expandable
+% The \meta{file name} may contain both literal items and expandable
% content, which should on full expansion be the desired file name.
% Any active characters (as declared in \cs{l_char_active_seq}) will
% \emph{not} be expanded, allowing the direct use of these in
@@ -162,30 +202,278 @@
% \end{texnote}
% \end{function}
%
-% \begin{function}{\file_path_include:n}
+% \begin{function}[updated = 2012-02-09]{\iow_open:Nn, \iow_open:cn}
% \begin{syntax}
-% \cs{file_path_include:n} \Arg{path}
+% \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 file ends. Opening a file for writing will clear
+% any existing content in the file (\emph{i.e.}~writing is \emph{not}
+% additive).
+% \begin{texnote}
+% The \meta{file name} may contain both literal items and expandable
+% content, which should on full expansion be the desired file name.
+% Any active characters (as declared in \cs{l_char_active_seq}) will
+% \emph{not} be expanded, allowing the direct use of these in
+% file names.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2011-12-27]{\ior_close:N, \ior_close:c}
+% \begin{syntax}
+% \cs{ior_close:N} \meta{stream}
% \end{syntax}
-% Adds \meta{path} to the list of those used to search for files
-% by the \cs{file_input:n} and \cs{file_if_exist:n} function. The
-% assignment is local.
+% Closes the \meta{stream}. Streams should always be closed when
+% they are finished with as this ensures that they remain available
+% to other programmer.
% \end{function}
%
-% \begin{function}{\file_path_remove:n}
+% \begin{function}[updated = 2011-12-27]{\iow_close:N, \iow_close:c}
% \begin{syntax}
-% \cs{file_path_remove:n} \Arg{path}
+% \cs{iow_close:N} \meta{stream}
% \end{syntax}
-% Removes \meta{path} from the list of those used to search for files
-% by the \cs{file_input:n} and \cs{file_if_exist:n} function. The
-% assignment is local.
+% Closes the \meta{stream}. Streams should always be closed when
+% they are finished with as this ensures that they remain available
+% to other programmer.
% \end{function}
%
-% \begin{function}{\file_list:}
+% \begin{function}{\ior_list_streams:, \iow_list_streams:}
% \begin{syntax}
-% \cs{file_list:}
+% \cs{ior_list_streams:}
+% \cs{iow_list_streams:}
% \end{syntax}
-% This function will list all files loaded using \cs{file_input:n}
-% in the log file.
+% Displays a list of the file names associated with each open
+% stream: intended for tracking down problems.
+% \end{function}
+%
+% \section{Reading from files}
+%
+% \begin{function}{\ior_to:NN,\ior_gto:NN}
+% \begin{syntax}
+% \cs{ior_to:NN} \meta{stream} \meta{token list variable}
+% \end{syntax}
+% Functions that reads one or more lines (until an equal number of left
+% and right braces are found) from the input \meta{stream} and stores
+% the result in the \meta{token list} variable, locally or globally. If the
+% \meta{stream} is not open, input is requested from the terminal.
+% The material read from the \meta{stream} will be tokenized by
+% \TeX{} according to the category codes in force when the function
+% is used.
+% \begin{texnote}
+% This protected macro expands to the \TeX{} primitives \tn{read}
+% or \tn{global}\tn{read} along with the |to| keyword.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\ior_str_to:NN,\ior_str_gto:NN}
+% \begin{syntax}
+% \cs{ior_str_to:NN} \meta{stream} \meta{token list variable}
+% \end{syntax}
+% Functions that reads one line from the input \meta{stream} and stores
+% the result in the \meta{token list} variable, locally or globally. If the
+% \meta{stream} is not open, input is requested from the terminal.
+% The material 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).
+% \begin{texnote}
+% This protected macro expands to the \eTeX{} primitives \tn{readline}
+% or \tn{global}\tn{readline} along with the |to| keyword.
+% \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 \meta{stream} has been reached during a reading
+% operation. The test will also return a \texttt{true} value if
+% the \meta{stream} is not open.
+%\end{function}
+%
+% \section{Writing to files}
+%
+% \begin{function}{\iow_now:Nn, \iow_now:Nx}
+% \begin{syntax}
+% \cs{iow_now:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions writes \meta{tokens} to the specified
+% \meta{stream} immediately (\emph{i.e.}~the write operation is called
+% on expansion of \cs{iow_now:Nn}).
+% \begin{texnote}
+% \cs{iow_now:Nx} is a protected macro which expands to
+% the two \TeX{} primitives \tn{immediate}\tn{write}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\iow_log:n, \iow_log:x}
+% \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:x}
+% \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:Nx}
+% \begin{syntax}
+% \cs{iow_shipout:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions writes \meta{tokens} to the specified
+% \meta{stream} when the current page is finalised (\emph{i.e.}~at
+% shipout). The \texttt{x}-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_x:Nn}).
+% \end{function}
+%
+% \begin{function}{\iow_shipout_x:Nn, \iow_shipout_x:Nx}
+% \begin{syntax}
+% \cs{iow_shipout_x:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions 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}
+% \cs{iow_shipout_x:Nn} is the \TeX{} primitive \tn{write} renamed.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\iow_char:N}
+% \begin{syntax}
+% \cs{iow_char:N} \meta{token}
+% \end{syntax}
+% Inserts \meta{token} into the output stream. Useful when trying to
+% write difficult characters such as |%|, |{|, |}|,
+% \emph{etc.}~in messages, for example:
+% \begin{verbatim}
+% \iow_now:Nx \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}).
+% \end{function}
+%
+% \section{Wrapping lines in output}
+%
+% \begin{function}[updated = 2011-09-21]{\iow_wrap:xnnnN}
+% \begin{syntax}
+% \cs{iow_wrap:xnnnN} \Arg{text} \Arg{run-on text} \Arg{run-on length} \Arg{set up} \meta{function}
+% \end{syntax}
+% This function will wrap 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} will be inserted. The line length targeted
+% will be the value of \cs{l_iow_line_length_int} minus the
+% \meta{run-on length}. The later value should be the number of
+% characters in the \meta{run-on text}. Additional functions may be
+% added to the wrapping by using the \meta{set up}, which is executed
+% before the wrapping takes place. The result of the wrapping operation
+% is passed as a braced argument to the \meta{function}, which will
+% typically be a wrapper around a writing operation. Within the
+% \meta{text},
+% \begin{itemize}
+% \item |\\| 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_indent:n} may be used to indent a part of the
+% message.
+% \end{itemize}
+% Both the wrapping process and the subsequent write operation
+% will perform \texttt{x}-type expansion. For this reason, material which
+% is to be written \enquote{as is} should be given as the argument to
+% \cs{token_to_str:N} or \cs{tl_to_str:n} (as appropriate) within
+% the \meta{text}. The output of \cs{iow_wrap:xnnnN} (\emph{i.e.}~the
+% argument passed to the \meta{function}) will consist of characters of
+% category code $12$ (other) and $10$ (space) only. This means that the
+% output will \emph{not} expand further when written to a file.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-21]{\iow_indent:n}
+% \begin{syntax}
+% \cs{iow_indent:n} \Arg{text}
+% \end{syntax}
+% In the context of \cs{iow_wrap:xnnnN} (for instance in messages),
+% indents \meta{text} by four spaces. This function will 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}{\l_iow_line_length_int}
+% The maximum length of a line to be written by the \cs{iow_wrap:xxnnN}
+% function. This value depends on the \TeX{} system in use: the standard
+% value is $78$, which is typically correct for unmodified \TeX{}live
+% and MiK\TeX{} systems.
+% \end{variable}
+%
+% \begin{variable}[added = 2011-09-05]{\c_catcode_other_space_tl}
+% Token list containing one character with category code $12$,
+% (\enquote{other}), and character code $32$ (space).
+% \end{variable}
+%
+% \section{Constant input--output streams}
+%
+% \begin{variable}{\c_term_ior}
+% Constant input stream for reading from the terminal. Reading from this
+% stream using \cs{ior_to:NN} or similar will result in a prompt from
+% \TeX{} of the form
+% \begin{verbatim}
+% <tl>=
+% \end{verbatim}
+% \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}
+%
+% \section{Experimental functions}
+%
+% \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 \meta{items} obtained by
+% reading one or more lines (until an equal number of left and right
+% braces are found) from the \meta{stream}. The \meta{inline function}
+% should consist of code which will receive 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} \Arg{stream} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to every \meta{line}
+% in the \meta{file}. 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
+% will receive the \meta{line} as |#1|.
% \end{function}
%
% \section{Internal file functions}
@@ -220,6 +508,67 @@
% the comma lists \tn{input@path} and \tn{@filelist} to sequences.
% \end{variable}
%
+% \section{Internal input--output functions}
+%
+% \begin{function}[added = 2012-02-09]{\file_name_sanitize:nn}
+% \begin{syntax}
+% \cs{file_name_sanitize:nn} \Arg{name} \Arg{tokens}
+% \end{syntax}
+% Exhaustively-expands the \meta{name} with the exception of any
+% category \meta{active} (catcode~$12$) tokens, which are not expanded.
+% The list of \meta{active} tokens is taken from \cs{l_char_active_seq}.
+% The \meta{sanitized name} is then inserted (in braces) after the
+% \meta{tokens}, which should further process the file name. If any
+% spaces are found in the name after expansion, an error is raised.
+% \end{function}
+%
+% \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}
+%
+% \begin{function}[added = 2012-01-23]
+% {\ior_open_unsafe:Nn, \ior_open_unsafe:No, \iow_open_unsafe:Nn}
+% \begin{syntax}
+% \cs{ior_open_unsafe:Nn} \meta{stream} \Arg{file name}
+% \end{syntax}
+% These functions have identical syntax to the generally-available
+% versions without the |_unsafe| suffix. However, these functions do not
+% take precautions against active characters in the \meta{file name}: they
+% are therefore intended to be used by higher-level functions which
+% have already fully expanded the \meta{file name} and which need to
+% perform multiple open or close operations. See for example the
+% implementation of \cs{file_add_path:Nn},
+% \end{function}
+%
+% \begin{function}{\ior_raw_new:N, \ior_raw_new:c}
+% \begin{syntax}
+% \cs{ior_raw_new:N} \meta{stream}
+% \end{syntax}
+% Directly allocates a new stream for reading, bypassing the stack
+% system. This is to be used only when a new stream is required at a
+% \TeX{} level, when a new stream is requested by the stack itself.
+% \end{function}
+%
+% \begin{function}{\iow_raw_new:N, \iow_raw_new:c}
+% \begin{syntax}
+% \cs{iow_raw_new:N} \meta{stream}
+% \end{syntax}
+% Directly allocates a new stream for writing, bypassing the stack
+% system. This is to be used only when a new stream is required at a
+% \TeX{} level, when a new stream is requested by the stack itself.
+% \end{function}
+%
% \end{documentation}
%
% \begin{implementation}
@@ -240,6 +589,8 @@
%</package>
% \end{macrocode}
%
+% \subsection{File operations}
+%
% \begin{variable}{\g_file_current_name_tl}
% The name of the current file should be available at all times.
% \begin{macrocode}
@@ -318,8 +669,33 @@
% \end{macrocode}
% \end{variable}
%
+% \begin{macro}[int]{\file_name_sanitize:nn}
+% For converting a token list to a string where active characters are treated
+% as strings from the start.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_name_sanitize:nn #1#2
+ {
+ \group_begin:
+ \seq_map_inline:Nn \l_char_active_seq
+ { \cs_set_nopar:Npx ##1 { \token_to_str:N ##1 } }
+ \tl_set:Nx \l_file_internal_name_tl {#1}
+ \tl_set:Nx \l_file_internal_name_tl
+ { \tl_to_str:N \l_file_internal_name_tl }
+ \tl_if_in:NnTF \l_file_internal_name_tl { ~ }
+ {
+ \msg_kernel_error:nnx { file } { space-in-file-name }
+ { \l_file_internal_name_tl }
+ }
+ \use:x
+ {
+ \group_end:
+ \exp_not:n {#2} { \l_file_internal_name_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{\file_add_path:nN}
-% \begin{variable}{\g_file_internal_ior}
% \begin{macro}[aux]{\file_add_path_aux:nN, \file_add_path_search:nN}
% The way to test if a file exists is to try to open it: if it does not
% exist then \TeX{} will report end-of-file. For files which are in the
@@ -328,19 +704,8 @@
% location is of course treated as the correct one. If nothing is found,
% |#2| is returned empty.
% \begin{macrocode}
-\ior_new:N \g_file_internal_ior
\cs_new_protected:Npn \file_add_path:nN #1
- {
- \group_begin:
- \tl_to_str_active_safe:Nx \l_file_internal_name_tl {#1}
- \tl_if_in:NnT \l_file_internal_name_tl { ~ }
- {
- \msg_kernel_error:nnx { io } { space-in-file-name }
- { \l_file_internal_name_tl }
- }
- \exp_args:NNo \group_end:
- \file_add_path_aux:nN \l_file_internal_name_tl
- }
+ { \file_name_sanitize:nn {#1} { \file_add_path_aux:nN } }
\cs_new_protected:Npn \file_add_path_aux:nN #1#2
{
\ior_open_unsafe:Nn \g_file_internal_ior {#1}
@@ -353,7 +718,7 @@
}
\cs_new_protected:Npn \file_add_path_search:nN #1#2
{
- \tl_clear:N #2
+ \tl_set:Nn #2 { \q_no_value }
%<*package>
\cs_if_exist:NT \input@path
{
@@ -380,7 +745,6 @@
}
% \end{macrocode}
% \end{macro}
-% \end{variable}
% \end{macro}
%
% \begin{macro}[TF]{\file_if_exist:n}
@@ -389,10 +753,10 @@
% something, whereas if the file was not located then the return value
% will be empty.
% \begin{macrocode}
-\prg_new_protected_conditional:Nnn \file_if_exist:n { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_if_exist:n #1 { T , F , TF }
{
\file_add_path:nN {#1} \l_file_internal_name_tl
- \tl_if_empty:NTF \l_file_internal_name_tl
+ \quark_if_no_value:NTF \l_file_internal_name_tl
{ \prg_return_false: }
{ \prg_return_true: }
}
@@ -400,126 +764,1053 @@
% \end{macro}
%
% \begin{macro}{\file_input:n}
+% \begin{macro}[aux]{\file_input_aux:n \file_input_aux:V, \file_input_error:n}
% Loading a file is done in a safe way, checking first that the file
% exists and loading only if it does.
% \begin{macrocode}
\cs_new_protected:Npn \file_input:n #1
{
\file_add_path:nN {#1} \l_file_internal_name_tl
- \tl_if_empty:NF \l_file_internal_name_tl
- {
+ \quark_if_no_value:NTF \l_file_internal_name_tl
+ { \file_name_sanitize:nn {#1} { \file_input_error:n } }
+ { \file_input_aux:V \l_file_internal_name_tl }
+ }
+\cs_new_protected:Npn \file_input_aux:n #1
+ {
%<*initex>
- \seq_gput_right:Nx \g_file_record_seq {#1}
+ \seq_gput_right:Nn \g_file_record_seq {#1}
%</initex>
%<*package>
- \@addtofilelist {#1}
+ \@addtofilelist {#1}
%</package>
- \seq_gpush:NV \g_file_stack_seq \g_file_current_name_tl
- \tl_gset:Nn \g_file_current_name_tl {#1}
- \exp_after:wN \tex_input:D \l_file_internal_name_tl \c_space_tl
- \seq_gpop:NN \g_file_stack_seq \g_file_current_name_tl
- }
+ \seq_gpush:Nn \g_file_stack_seq \g_file_current_name_tl
+ \tl_gset:Nn \g_file_current_name_tl {#1}
+ \tex_input:D #1 \c_space_tl
+ \seq_gpop:NN \g_file_stack_seq \g_file_current_name_tl
}
+\cs_generate_variant:Nn \file_input_aux:n { V }
+\cs_new_protected:Npn \file_input_error:n #1
+ { \msg_kernel_error:nnx { file } { file-not-found } {#1} }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \begin{macro}{\file_split_path_name_ext:nNNN}
-% \begin{macro}[aux]{\file_split_path_name_ext_aux:nNNN}
-% \begin{macro}[aux]{\file_split_path:wNNN, \file_split_name_ext:wNN}
-% Splits a file name into any path part, the file name and the extension,
-% which is considered as the part after the last |.|.
+% \begin{macro}{\file_path_include:n}
+% \begin{macro}{\file_path_remove:n}
+% Wrapper functions to manage the search path.
% \begin{macrocode}
-\cs_new_protected:Npn \file_split_path_name_ext:nNNN #1
+\cs_new_protected:Npn \file_path_include:n #1
{
- \group_begin:
- \tl_to_str_active_safe:Nx \l_file_internal_name_tl {#1}
- \tl_if_in:NnT \l_file_internal_name_tl { ~ }
- {
- \msg_kernel_error:nnx { io } { space-in-file-name }
- { \l_file_internal_name_tl }
- }
- \exp_args:NNo \group_end:
- \file_split_path_name_ext_aux:nNNN \l_file_internal_name_tl
+ \seq_if_in:NnF \l_file_search_path_seq {#1}
+ { \seq_put_right:Nn \l_file_search_path_seq {#1} }
}
-\cs_new_protected:Npn \file_split_path_name_ext_aux:nNNN #1#2#3#4
+\cs_new_protected:Npn \file_path_remove:n #1
+ { \seq_remove_all:Nn \l_file_search_path_seq {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\file_list:}
+% A function to list all files used to the log.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \file_list:
{
- \tl_clear:N #2
- \tl_clear:N #3
- \file_split_path:wNNN #1 / \q_nil / \q_stop #2#3#4
+ \seq_remove_duplicates:N \g_file_record_seq
+ \iow_log:n { *~File~List~* }
+ \seq_map_inline:Nn \g_file_record_seq { \iow_log:n {##1} }
+ \iow_log:n { ************* }
}
-\cs_new_protected:Npn \file_split_path:wNNN #1 / #2 / #3 \q_stop #4
+% \end{macrocode}
+% \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.
+% \begin{macrocode}
+%<*package>
+\AtBeginDocument
+ {
+ \seq_set_from_clist:NN \l_file_internal_seq \@filelist
+ \seq_gconcat:NNN \g_file_record_seq \g_file_record_seq \l_file_internal_seq
+ }
+%</package>
+% \end{macrocode}
+%
+% \subsection{Input--output variables constants}
+%
+% \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}
+\cs_new_eq:NN \c_term_ior \c_sixteen
+% \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}).
+% \begin{macrocode}
+\cs_new_eq:NN \c_log_iow \c_minus_one
+\cs_new_eq:NN \c_term_iow \c_sixteen
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_iow_streams_tl, \c_ior_streams_tl}
+% The list of streams available, by number.
+% \begin{macrocode}
+\tl_const:Nn \c_iow_streams_tl
{
- \quark_if_nil:nTF {#2}
- { \file_split_name_ext:wNN #1 . \q_nil . \q_stop }
+ \c_zero
+ \c_one
+ \c_two
+ \c_three
+ \c_four
+ \c_five
+ \c_six
+ \c_seven
+ \c_eight
+ \c_nine
+ \c_ten
+ \c_eleven
+ \c_twelve
+ \c_thirteen
+ \c_fourteen
+ \c_fifteen
+ }
+\cs_new_eq:NN \c_ior_streams_tl \c_iow_streams_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_iow_streams_prop, \g_ior_streams_prop}
+% The allocations for streams are stored in property lists, which
+% are set up to have a \enquote{full} set of allocations from the start.
+% In package mode, a few slots are always taken, so these are
+% blocked off from use.
+% \begin{macrocode}
+\prop_new:N \g_iow_streams_prop
+\prop_new:N \g_ior_streams_prop
+%<*package>
+\prop_put:Nnn \g_iow_streams_prop { 0 } { LaTeX2e~reserved }
+\prop_put:Nnn \g_iow_streams_prop { 1 } { LaTeX2e~reserved }
+\prop_put:Nnn \g_iow_streams_prop { 2 } { LaTeX2e~reserved }
+\prop_put:Nnn \g_ior_streams_prop { 0 } { LaTeX2e~reserved }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_iow_stream_int, \l_ior_stream_int}
+% Used to track the number allocated to the stream being created:
+% this is taken from the property list but does alter.
+% \begin{macrocode}
+\int_new:N \l_iow_stream_int
+\cs_new_eq:NN \l_ior_stream_int \l_iow_stream_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Stream management}
+%
+% \begin{macro}[int]{\ior_raw_new:N, \ior_raw_new:c}
+% \begin{macro}[int]{\iow_raw_new:N, \iow_raw_new:c}
+% The lowest level for stream management is actually creating raw \TeX{}
+% streams. As these are very limited (even with \eTeX{}), this should not
+% be addressed directly.
+% \begin{macrocode}
+%<*initex>
+\alloc_setup_type:nnn { ior } \c_zero \c_sixteen
+\cs_new_protected:Npn \ior_raw_new:N #1
+ { \alloc_reg:nNN { ior } \tex_chardef:D #1 }
+\alloc_setup_type:nnn { iow } \c_zero \c_sixteen
+\cs_new_protected:Npn \iow_raw_new:N #1
+ { \alloc_reg:nNN { iow } \tex_chardef:D #1 }
+%</initex>
+%<*package>
+\cs_set_eq:NN \iow_raw_new:N \newwrite
+\cs_set_eq:NN \ior_raw_new:N \newread
+%</package>
+\cs_generate_variant:Nn \ior_raw_new:N { c }
+\cs_generate_variant:Nn \iow_raw_new:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ior_new:N, \ior_new:c, \iow_new:N, \iow_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 }
+\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_file_internal_ior}
+% Delayed from above so that the mechanisms are in place.
+% \begin{macrocode}
+\ior_new:N \g_file_internal_ior
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\ior_open:Nn, \ior_open:cn, \iow_open:Nn, \iow_open:cn}
+% \begin{macro}[int]{\ior_open_aux:Nn}
+% \begin{macro}[TF]{\ior_open:Nn}
+% \begin{macro}[aux]{\ior_open_aux:NnTF}
+% \begin{macro}[int]
+% {\ior_open_unsafe:Nn, \ior_open_unsafe:No, \iow_open_unsafe:Nn}
+% In both cases, opening a stream starts with a call to the closing
+% function: this is safest. There is then a loop through the
+% allocation number list to find the first free stream number.
+% When one is found the allocation can take place, the information
+% can be stored and finally the file can actually be opened. Before
+% any actual file operations there is a precaution against special
+% characters in file names. For reading files, there is an intermediate
+% auxiliary to allow path addition, keeping the internal function fast
+% and avoiding an infinite loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_open:Nn #1#2
+ { \file_name_sanitize:nn {#2} { \ior_open_aux:Nn #1 } }
+\cs_generate_variant:Nn \ior_open:Nn { c }
+\cs_new_protected:Npn \iow_open:Nn #1#2
+ { \file_name_sanitize:nn {#2} { \iow_open_unsafe:Nn #1 } }
+\cs_generate_variant:Nn \iow_open:Nn { c }
+\cs_new_protected:Npn \ior_open_aux:Nn #1#2
+ {
+ \file_add_path:nN {#2} \l_file_name_internal_tl
+ \quark_if_no_value:NTF \l_file_internal_name_tl
+ { \file_input_error:n {#2} }
+ { \ior_open_unsafe:No #1 \l_file_name_internal_tl }
+ }
+\prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF }
+ { \file_name_sanitize:nn {#2} { \ior_open_aux:NnTF #1 } }
+\cs_new_protected:Npn \ior_open_aux:NnTF #1#2
+ {
+ \file_add_path:nN {#2} \l_file_name_internal_tl
+ \quark_if_no_value:NTF \l_file_internal_name_tl
+ { \prg_return_false: }
+ {
+ \ior_open_unsafe:No #1 \l_file_name_internal_tl
+ \prg_return_true:
+ }
+ }
+\cs_generate_variant:Nn \ior_open:NnT { c }
+\cs_generate_variant:Nn \ior_open:NnF { c }
+\cs_generate_variant:Nn \ior_open:NnTF { c }
+\cs_new_protected:Npn \ior_open_unsafe:Nn #1#2
+ {
+ \ior_close:N #1
+ \int_set:Nn \l_ior_stream_int \c_sixteen
+ \tl_map_function:NN \c_ior_streams_tl \ior_alloc_read:n
+ \int_compare:nNnTF \l_ior_stream_int = \c_sixteen
+ { \msg_kernel_fatal:nn { ior } { streams-exhausted } }
{
- \tl_put_right:Nn #4 { #1 / }
- \file_split_path:wNNN #2 / #3 \q_stop #4
+ \ior_stream_alloc:N #1
+ \prop_gput:NVn \g_ior_streams_prop \l_ior_stream_int {#2}
+ \tex_openin:D #1#2 \scan_stop:
}
}
-\cs_new_protected:Npn \file_split_name_ext:wNN #1 . #2 . #3 \q_stop #4#5
+\cs_generate_variant:Nn \ior_open_unsafe:Nn { No }
+\cs_new_protected:Npn \iow_open_unsafe:Nn #1#2
{
- \quark_if_nil:nTF {#2}
+ \iow_close:N #1
+ \int_set:Nn \l_iow_stream_int \c_sixteen
+ \tl_map_function:NN \c_iow_streams_tl \iow_alloc_write:n
+ \int_compare:nNnTF \l_iow_stream_int = \c_sixteen
+ { \msg_kernel_fatal:nn { iow } { streams-exhausted } }
{
- \tl_if_empty:NTF #4
+ \iow_stream_alloc:N #1
+ \prop_gput:NVn \g_iow_streams_prop \l_iow_stream_int {#2}
+ \tex_immediate:D \tex_openout:D #1#2 \scan_stop:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\ior_alloc_read:n}
+% \begin{macro}[aux]{\iow_alloc_write:n}
+% These functions are used to see if a particular stream is available.
+% The property list contains file names for streams in use, so
+% any unused ones are for the taking.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_alloc_write:n #1
+ {
+ \prop_if_in:NnF \g_iow_streams_prop {#1}
+ {
+ \int_set:Nn \l_iow_stream_int {#1}
+ \tl_map_break:
+ }
+ }
+\cs_new_protected:Npn \ior_alloc_read:n #1
+ {
+ \prop_if_in:NnF \g_iow_streams_prop {#1}
+ {
+ \int_set:Nn \l_ior_stream_int {#1}
+ \tl_map_break:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\iow_stream_alloc:N, \ior_stream_alloc:N}
+% \begin{macro}[aux]{\iow_stream_alloc_aux:, \ior_stream_alloc_aux:}
+% \begin{variable}{\g_iow_internal_iow, \g_ior_internal_ior}
+% Allocating a raw stream is much easier in \IniTeX{} mode than for
+% the package. For the format, all streams will be allocated by
+% \pkg{l3file} and so there is a simple check to see if a raw
+% stream is actually available. On the other hand, for the
+% package there will be non-managed streams. So if the managed
+% one is not open, a check is made to see if some other managed
+% stream is available before deciding to open a new one. If a new
+% one is needed, we get the number allocated by \LaTeXe{} to get
+% \enquote{back on track} with allocation.
+% \begin{macrocode}
+\iow_new:N \g_iow_internal_iow
+\ior_new:N \g_ior_internal_ior
+\cs_new_protected:Npn \iow_stream_alloc:N #1
+ {
+ \cs_if_exist:cTF { g_iow_ \int_use:N \l_iow_stream_int _iow }
+ { \cs_gset_eq:Nc #1 { g_iow_ \int_use:N \l_iow_stream_int _iow } }
+ {
+%<*package>
+ \iow_stream_alloc_aux:
+ \int_compare:nNnT \l_iow_stream_int = \c_sixteen
{
- \tl_set:Nn #4 {#1}
- \tl_clear:N #5
+ \iow_raw_new:N \g_iow_internal_iow
+ \int_set:Nn \l_iow_stream_int { \g_iow_internal_iow }
+ \cs_gset_eq:cN
+ { g_iow_ \int_use:N \l_iow_stream_int _iow } \g_iow_internal_iow
}
- { \tl_set:Nn #5 {#1} }
+%</package>
+%<*initex>
+ \iow_raw_new:c { g_iow_ \int_use:N \l_iow_stream_int _iow }
+%</initex>
+ \cs_gset_eq:Nc #1 { g_iow_ \int_use:N \l_iow_stream_int _iow }
}
+ }
+%<*package>
+\cs_new_protected_nopar:Npn \iow_stream_alloc_aux:
+ {
+ \int_incr:N \l_iow_stream_int
+ \int_compare:nNnT \l_iow_stream_int < \c_sixteen
{
- \tl_put_right:Nx #4
+ \cs_if_exist:cTF { g_iow_ \int_use:N \l_iow_stream_int _iow }
+ {
+ \prop_if_in:NVT \g_iow_streams_prop \l_iow_stream_int
+ { \iow_stream_alloc_aux: }
+ }
+ { \iow_stream_alloc_aux: }
+ }
+ }
+%</package>
+\cs_new_protected:Npn \ior_stream_alloc:N #1
+ {
+ \cs_if_exist:cTF { g_ior_ \int_use:N \l_ior_stream_int _ior }
+ { \cs_gset_eq:Nc #1 { g_ior_ \int_use:N \l_ior_stream_int _ior } }
+ {
+%<*package>
+ \ior_stream_alloc_aux:
+ \int_compare:nNnT \l_ior_stream_int = \c_sixteen
{
- \tl_if_empty:NF #4 { . }
- #1
+ \ior_raw_new:N \g_ior_internal_ior
+ \int_set:Nn \l_ior_stream_int { \g_ior_internal_ior }
+ \cs_gset_eq:cN
+ { g_ior_ \int_use:N \l_iow_stream_int _ior } \g_ior_internal_ior
}
- \file_split_name_ext:wNN #2 . #3 \q_stop #4#5
+%</package>
+%<*initex>
+ \ior_raw_new:c { g_ior_ \int_use:N \l_ior_stream_int _ior }
+%</initex>
+ \cs_gset_eq:Nc #1 { g_ior_ \int_use:N \l_ior_stream_int _ior }
+ }
+ }
+%<*package>
+\cs_new_protected_nopar:Npn \ior_stream_alloc_aux:
+ {
+ \int_incr:N \l_ior_stream_int
+ \int_compare:nNnT \l_ior_stream_int < \c_sixteen
+ {
+ \cs_if_exist:cTF { g_ior_ \int_use:N \l_ior_stream_int _ior }
+ {
+ \prop_if_in:NVT \g_ior_streams_prop \l_ior_stream_int
+ { \ior_stream_alloc_aux: }
+ }
+ { \ior_stream_alloc_aux: }
}
}
+%</package>
% \end{macrocode}
+% \end{variable}
% \end{macro}
% \end{macro}
+%
+% \begin{macro}{\ior_close:N, \ior_close:c, \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 \ior_close:N #1
+ {
+ \cs_if_exist:NT #1
+ {
+ \int_compare:nNnF #1 = \c_minus_one
+ {
+ \int_compare:nNnF #1 = \c_sixteen
+ { \tex_closein:D #1 }
+ \prop_gdel:NV \g_ior_streams_prop #1
+ \cs_gset_eq:NN #1 \c_term_ior
+ }
+ }
+ }
+\cs_new_protected:Npn \iow_close:N #1
+ {
+ \cs_if_exist:NT #1
+ {
+ \int_compare:nNnF #1 = \c_minus_one
+ {
+ \int_compare:nNnF #1 = \c_sixteen
+ { \tex_closein:D #1 }
+ \prop_gdel:NV \g_iow_streams_prop #1
+ \cs_gset_eq:NN #1 \c_term_iow
+ }
+ }
+ }
+\cs_generate_variant:Nn \ior_close:N { c }
+\cs_generate_variant:Nn \iow_close:N { c }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\file_path_include:n}
-% \begin{macro}{\file_path_remove:n}
-% Wrapper functions to manage the search path.
+% \begin{macro}{\ior_list_streams:}
+% Show the property lists, but with some \enquote{pretty printing}.
+% See the \pkg{l3msg} module. If there are no open read streams,
+% issue the message \texttt{show-no-stream}, and show an empty
+% token list. If there are open read streams, format them with
+% \cs{msg_aux_show_unbraced:nn}, and with the message
+% \texttt{show-open-streams}.
% \begin{macrocode}
-\cs_new_protected:Npn \file_path_include:n #1
+\cs_new_protected_nopar:Npn \ior_list_streams:
+ { \ior_list_streams_aux:Nn \g_ior_streams_prop { ior } }
+\cs_new_protected_nopar:Npn \iow_list_streams:
+ { \ior_list_streams_aux:Nn \g_iow_streams_prop { iow } }
+\cs_new_protected:Npn \ior_list_streams_aux:Nn #1#2
{
- \seq_if_in:NnF \l_file_search_path_seq {#1}
- { \seq_put_right:Nn \l_file_search_path_seq {#1} }
+ \msg_aux_use:nn { LaTeX / #2 }
+ { \prop_if_empty:NTF #1 { show-no-stream } { show-open-streams } }
+ \msg_aux_show:x
+ { \prop_map_function:NN #1 \msg_aux_show_unbraced:nn }
}
-\cs_new_protected:Npn \file_path_remove:n #1
- { \seq_remove_all:Nn \l_file_search_path_seq {#1} }
% \end{macrocode}
% \end{macro}
+%
+% Text for the error messages.
+% \begin{macrocode}
+\msg_kernel_new:nnnn { iow } { streams-exhausted }
+ { Output~streams~exhausted }
+ {
+ TeX~can~only~open~up~to~16~output~streams~at~one~time.\\
+ All~16 are currently~in~use,~and~something~wanted~to~open
+ another~one.
+ }
+\msg_kernel_new:nnnn { ior } { streams-exhausted }
+ { Input~streams~exhausted }
+ {
+ TeX~can~only~open~up~to~16~input~streams~at~one~time.\\
+ All~16 are currently~in~use,~and~something~wanted~to~open
+ another~one.
+ }
+% \end{macrocode}
+%
+% \subsection{Deferred writing}
+%
+% \begin{macro}{\iow_shipout_x:Nn, \iow_shipout_x:Nx}
+% First the easy part, this is the primitive.
+% \begin{macrocode}
+\cs_new_eq:NN \iow_shipout_x:Nn \tex_write:D
+\cs_generate_variant:Nn \iow_shipout_x:Nn { Nx }
+% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\file_list:}
-% A function to list all files used to the log.
+% \begin{macro}{\iow_shipout:Nn, \iow_shipout:Nx}
+% With \eTeX{} available deferred writing is easy.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \file_list:
+\cs_new_protected:Npn \iow_shipout:Nn #1#2
+ { \iow_shipout_x:Nn #1 { \exp_not:n {#2} } }
+\cs_generate_variant:Nn \iow_shipout:Nn { Nx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Immediate writing}
+%
+% \begin{macro}{\iow_now:Nx}
+% An abbreviation for an often used operation, which immediately
+% writes its second argument expanded to the output stream.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \iow_now:Nx { \tex_immediate:D \iow_shipout_x:Nn }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_now:Nn}
+% 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.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_now:Nn #1#2
+ { \iow_now:Nx #1 { \exp_not:n {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_log:n, \iow_log:x}
+% \begin{macro}{\iow_term:n, \iow_term:x}
+% Writing to the log and the terminal directly are relatively easy.
+% \begin{macrocode}
+\cs_set_protected_nopar:Npn \iow_log:x { \iow_now:Nx \c_log_iow }
+\cs_new_protected_nopar:Npn \iow_log:n { \iow_now:Nn \c_log_iow }
+\cs_set_protected_nopar:Npn \iow_term:x { \iow_now:Nx \c_term_iow }
+\cs_new_protected_nopar:Npn \iow_term:n { \iow_now:Nn \c_term_iow }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+% \subsection{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_nopar: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}
+%
+% \subsection{Hard-wrapping lines based on length}
+%
+% 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{macro}{\l_iow_line_length_int}
+% This is the \enquote{raw} length of a line which can be written to a
+% file. The standard value is the line length typically used by
+% \TeX{}Live and Mik\TeX{}.
+% \begin{macrocode}
+\int_new:N \l_iow_line_length_int
+\int_set:Nn \l_iow_line_length_int { 78 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\l_iow_target_length_int}
+% This stores the target line length: the full length minus any
+% part for a leader at the start of each line.
+% \begin{macrocode}
+\int_new:N \l_iow_target_length_int
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]
+% {
+% \l_iow_current_line_int,
+% \l_iow_current_word_int,
+% \l_iow_current_indentation_int
+% }
+% These store the number of characters in the line and word currently
+% being constructed, and the current indentation, respectively.
+% \begin{macrocode}
+\int_new:N \l_iow_current_line_int
+\int_new:N \l_iow_current_word_int
+\int_new:N \l_iow_current_indentation_int
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]
+% {
+% \l_iow_current_line_tl,
+% \l_iow_current_word_tl,
+% \l_iow_current_indentation_tl
+% }
+% These hold the current line of text and current word,
+% and a number of spaces for indentation, respectively.
+% \begin{macrocode}
+\tl_new:N \l_iow_current_line_tl
+\tl_new:N \l_iow_current_word_tl
+\tl_new:N \l_iow_current_indentation_tl
+% \end{macrocode}
+%\end{macro}
+%
+% \begin{macro}[aux]{\l_iow_wrap_tl}
+% Used for the expansion step before detokenizing.
+% \begin{macrocode}
+\tl_new:N \l_iow_wrap_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\l_iow_wrapped_tl}
+% The output from wrapping text: fully expanded and with lines
+% which are not overly long.
+% \begin{macrocode}
+\tl_new:N \l_iow_wrapped_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\l_iow_line_start_bool}
+% Boolean to avoid adding a space at the beginning of forced newlines.
+% \begin{macrocode}
+\bool_new:N \l_iow_line_start_bool
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\c_catcode_other_space_tl}
+% Lowercase a character with category code $12$ to produce an
+% \enquote{other} space. We can do everything within the group,
+% because \cs{tl_const:Nn} defines its argument globally.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \*
+ \char_set_lccode:nn {`\*} {`\ }
+ \tl_to_lowercase:n { \tl_const:Nn \c_catcode_other_space_tl { * } }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\c_iow_wrap_marker_tl}
+% \begin{macro}[aux]{
+% \c_iow_wrap_end_marker_tl,
+% \c_iow_wrap_newline_marker_tl,
+% \c_iow_wrap_indent_marker_tl,
+% \c_iow_wrap_unindent_marker_tl}
+% \begin{macro}[aux]{\iow_wrap_new_marker:n}
+% Every special action of the wrapping code is preceeded by
+% the same recognizable string, \cs{c_iow_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_iow_wrap_marker_tl} look
+% nicer. Note that \cs{iow_wrap_new_marker:n} does not
+% survive the group, but all constants are defined globally.
+% \begin{macrocode}
+\group_begin:
+ \int_set_eq:NN \tex_escapechar:D \c_minus_one
+ \tl_const:Nx \c_iow_wrap_marker_tl
+ { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } }
+ \cs_set:Npn \iow_wrap_new_marker:n #1
+ {
+ \tl_const:cx { c_iow_wrap_ #1 _marker_tl }
+ {
+ \c_catcode_other_space_tl
+ \c_iow_wrap_marker_tl
+ \c_catcode_other_space_tl
+ #1
+ \c_catcode_other_space_tl
+ }
+ }
+ \iow_wrap_new_marker:n { end }
+ \iow_wrap_new_marker:n { newline }
+ \iow_wrap_new_marker:n { indent }
+ \iow_wrap_new_marker:n { unindent }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_indent:n}
+% \begin{macro}[aux]{\iow_indent_expandable:n}
+% We give a dummy (protected) definition to \cs{iow_indent:n}
+% when outside messages. Within wrapped message, it places
+% the instruction for increasing the indentation before its
+% argument, and the instruction for unindenting afterwards.
+% Note that there will be 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 { }
+\cs_new:Npx \iow_indent_expandable:n #1
{
- \seq_remove_duplicates:N \g_file_record_seq
- \iow_log:n { *~File~List~* }
- \seq_map_inline:Nn \g_file_record_seq { \iow_log:n {##1} }
- \iow_log:n { ************* }
+ \c_iow_wrap_indent_marker_tl
+ #1
+ \c_iow_wrap_unindent_marker_tl
}
% \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.
+% \begin{macro}{\iow_wrap:xnnnN}
+% The main wrapping function works as follows. The target number of
+% characters in a line is calculated, before fully-expanding the input
+% such that |\\| and \verb*|\ | are converted into the appropriate
+% values. There is then a loop over each word in the input, which
+% will do the actual wrapping. After the loop, the resulting text is
+% passed on to the function which has been given as a post-processor.
+% The argument |#4| is available for additional set up steps for
+% the output. The definition of |\\| and \verb*|\ | use 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. The \cs{tl_to_str:N} step converts the \enquote{other}
+% space back to a normal space.
% \begin{macrocode}
+\cs_new_protected:Npn \iow_wrap:xnnnN #1#2#3#4#5
+ {
+ \group_begin:
+ \int_set:Nn \l_iow_target_length_int { \l_iow_line_length_int - ( #3 ) }
+ \int_zero:N \l_iow_current_indentation_int
+ \tl_clear:N \l_iow_current_indentation_tl
+ \int_zero:N \l_iow_current_line_int
+ \tl_clear:N \l_iow_current_line_tl
+ \tl_clear:N \l_iow_wrap_tl
+ \bool_set_true:N \l_iow_line_start_bool
+ \int_set_eq:NN \tex_escapechar:D \c_minus_one
+ \cs_set_nopar:Npx \{ { \token_to_str:N \{ }
+ \cs_set_nopar:Npx \# { \token_to_str:N \# }
+ \cs_set_nopar:Npx \} { \token_to_str:N \} }
+ \cs_set_nopar:Npx \% { \token_to_str:N \% }
+ \cs_set_nopar:Npx \~ { \token_to_str:N \~ }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \cs_set_eq:NN \\ \c_iow_wrap_newline_marker_tl
+ \cs_set_eq:NN \ \c_catcode_other_space_tl
+ \cs_set_eq:NN \iow_indent:n \iow_indent_expandable:n
+ #4
+%<*initex>
+ \tl_set:Nx \l_iow_wrap_tl {#1}
+%</initex>
%<*package>
-\AtBeginDocument
+ \protected@edef \l_iow_wrap_tl {#1}
+%</package>
+ \cs_set:Npn \\ { \iow_newline: #2 }
+ \use:x
+ {
+ \iow_wrap_loop:w
+ \tl_to_str:N \l_iow_wrap_tl
+ \tl_to_str:N \c_iow_wrap_end_marker_tl
+ \c_space_tl \c_space_tl
+ \exp_not:N \q_stop
+ }
+ \exp_args:NNo \group_end:
+ #5 \l_iow_wrapped_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\iow_wrap_loop:w}
+% The loop grabs one word in the input, and checks whether it is
+% the special marker, or a normal word.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_wrap_loop:w #1 ~ %
{
- \seq_set_from_clist:NN \l_file_internal_seq \@filelist
- \seq_gconcat:NNN \g_file_record_seq \g_file_record_seq \l_file_internal_seq
+ \tl_set:Nn \l_iow_current_word_tl {#1}
+ \tl_if_eq:NNTF \l_iow_current_word_tl \c_iow_wrap_marker_tl
+ { \iow_wrap_special:w }
+ { \iow_wrap_word: }
}
-%</package>
% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\iow_wrap_word:}
+% \begin{macro}[aux]{\iow_wrap_word_fits:}
+% \begin{macro}[aux]{\iow_wrap_word_newline:}
+% For a normal word, update the line length, then test if the current
+% word would fit in the current line, and call the appropriate function.
+% If the word fits in the current line, add it to the line, preceded by
+% a space unless it is the first word of the line.
+% Otherwise, the current line is added to the result, with the run-on text.
+% The current word (and its length) are then put in the new line.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \iow_wrap_word:
+ {
+ \int_set:Nn \l_iow_current_word_int
+ { \str_length_skip_spaces:N \l_iow_current_word_tl }
+ \int_add:Nn \l_iow_current_line_int { \l_iow_current_word_int }
+ \int_compare:nNnTF \l_iow_current_line_int < \l_iow_target_length_int
+ { \iow_wrap_word_fits: }
+ { \iow_wrap_word_newline: }
+ \iow_wrap_loop:w
+ }
+\cs_new_protected_nopar:Npn \iow_wrap_word_fits:
+ {
+ \bool_if:NTF \l_iow_line_start_bool
+ {
+ \bool_set_false:N \l_iow_line_start_bool
+ \tl_put_right:Nx \l_iow_current_line_tl
+ { \l_iow_current_indentation_tl \l_iow_current_word_tl }
+ \int_add:Nn \l_iow_current_line_int
+ { \l_iow_current_indentation_int }
+ }
+ {
+ \tl_put_right:Nx \l_iow_current_line_tl
+ { ~ \l_iow_current_word_tl }
+ \int_incr:N \l_iow_current_line_int
+ }
+ }
+\cs_new_protected_nopar:Npn \iow_wrap_word_newline:
+ {
+ \tl_put_right:Nx \l_iow_wrapped_tl
+ { \l_iow_current_line_tl \\ }
+ \int_set:Nn \l_iow_current_line_int
+ {
+ \l_iow_current_word_int
+ + \l_iow_current_indentation_int
+ }
+ \tl_set:Nx \l_iow_current_line_tl
+ { \l_iow_current_indentation_tl \l_iow_current_word_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\iow_wrap_special:w}
+% \begin{macro}[aux]{\iow_wrap_newline:w}
+% \begin{macro}[aux]{\iow_wrap_indent:w}
+% \begin{macro}[aux]{\iow_wrap_unindent:w}
+% \begin{macro}[aux]{\iow_wrap_end:w}
+% When the \enquote{special} marker is encountered,
+% read what operation to perform, as a space-delimited
+% argument, perform it, and remember to loop.
+% In fact, to avoid spurious spaces when two special
+% actions follow each other, we look ahead for another
+% copy of the marker.
+% Forced newlines are almost identical to those caused by overflow,
+% except that here the word is empty.
+% To indent more, add four spaces to the start of the indentation
+% token list. To reduce indentation, rebuild the indentation token
+% list using \cs{prg_replicate:nn}.
+% At the end, we simply save the last line (without the run-on text),
+% and prevent the loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_wrap_special:w #1 ~ #2 ~ #3 ~ %
+ {
+ \use:c { iow_wrap_#1: }
+ \str_if_eq:xxTF { #2~#3 } { ~ \c_iow_wrap_marker_tl }
+ { \iow_wrap_special:w }
+ { \iow_wrap_loop:w #2 ~ #3 ~ }
+ }
+\cs_new_protected_nopar:Npn \iow_wrap_newline:
+ {
+ \tl_put_right:Nx \l_iow_wrapped_tl
+ { \l_iow_current_line_tl \\ }
+ \int_zero:N \l_iow_current_line_int
+ \tl_clear:N \l_iow_current_line_tl
+ \bool_set_true:N \l_iow_line_start_bool
+ }
+\cs_new_protected_nopar:Npx \iow_wrap_indent:
+ {
+ \int_add:Nn \l_iow_current_indentation_int \c_four
+ \tl_put_right:Nx \exp_not:N \l_iow_current_indentation_tl
+ { \c_space_tl \c_space_tl \c_space_tl \c_space_tl }
+ }
+\cs_new_protected_nopar:Npn \iow_wrap_unindent:
+ {
+ \int_sub:Nn \l_iow_current_indentation_int \c_four
+ \tl_set:Nx \l_iow_current_indentation_tl
+ { \prg_replicate:nn \l_iow_current_indentation_int { ~ } }
+ }
+\cs_new_protected_nopar:Npn \iow_wrap_end:
+ {
+ \tl_put_right:Nx \l_iow_wrapped_tl
+ { \l_iow_current_line_tl }
+ \use_none_delimit_by_q_stop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\str_length_skip_spaces:N}
+% \begin{macro}[aux]{\str_length_skip_spaces:n}
+% \begin{macro}[aux]{\str_length_loop:NNNNNNNNN}
+% The wrapping code requires to measure the number of character
+% in each word. This could be done with \cs{tl_length:n}, but
+% it is ten times faster (literally) to use the code below.
+% \begin{macrocode}
+\cs_new_nopar:Npn \str_length_skip_spaces:N
+ { \exp_args:No \str_length_skip_spaces:n }
+\cs_new:Npn \str_length_skip_spaces:n #1
+ {
+ \int_value:w \int_eval:w
+ \exp_after:wN \str_length_loop:NNNNNNNNN \tl_to_str:n {#1}
+ { X8 } { X7 } { X6 } { X5 } { X4 } { X3 } { X2 } { X1 } { X0 } \q_stop
+ \int_eval_end:
+ }
+\cs_new:Npn \str_length_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9
+ {
+ \if_catcode:w X #9
+ \exp_after:wN \use_none_delimit_by_q_stop:w
+ \else:
+ 9 +
+ \exp_after:wN \str_length_loop:NNNNNNNNN
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Reading input}
+%
+% \begin{macro}[int]{\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.
+% \begin{macrocode}
+\prg_new_conditional:Nnn \ior_if_eof:N { p , T , F , TF }
+ {
+ \cs_if_exist:NTF #1
+ {
+ \if_int_compare:w #1 = \c_sixteen
+ \prg_return_true:
+ \else:
+ \if_eof:w #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+ { \prg_return_true: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_to:NN, \ior_gto:NN}
+% And here we read from files.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_to:NN #1#2
+ { \tex_read:D #1 to #2 }
+\cs_new_protected:Npn \ior_gto:NN #1#2
+ { \tex_global:D \tex_read:D #1 to #2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_str_to:NN, \ior_str_gto:NN}
+% Reading as strings is also a primitive wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_str_to:NN #1#2
+ { \etex_readline:D #1 to #2 }
+\cs_new_protected:Npn \ior_str_gto:NN #1#2
+ { \tex_global:D \etex_readline:D #1 to #2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Experimental functions}
+%
+% \begin{macro}{\ior_map_inline:Nn, \ior_str_map_inline:Nn}
+% \begin{macro}[aux]{\ior_str_map_inline_aux:NNn}
+% \begin{macro}[aux]{\ior_str_map_inline_aux:NNNn}
+% \begin{macro}[aux]{\ior_str_map_inline_loop:NNN}
+% \begin{variable}{\l_ior_internal_tl}
+% Mapping to 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}.
+% This mapping cannot be nested as the stream has only one \enquote{current
+% line}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ior_map_inline:Nn
+ { \ior_map_inline_aux:NNn \ior_to:NN }
+\cs_new_protected_nopar:Npn \ior_str_map_inline:Nn
+ { \ior_map_inline_aux:NNn \ior_str_to:NN }
+\cs_new_protected_nopar:Npn \ior_map_inline_aux:NNn
+ {
+ \exp_args:Nc \ior_map_inline_aux:NNNn
+ { ior_map_ \int_use:N \g_prg_map_int :n }
+ }
+\cs_new_protected:Npn \ior_map_inline_aux:NNNn #1#2#3#4
+ {
+ \cs_set:Npn #1 ##1 {#4}
+ \int_gincr:N \g_prg_map_int
+ \ior_if_eof:NF #3 { \ior_map_inline_loop:NNN #1#2#3 }
+ \prg_break_point:n { \int_gdecr:N \g_prg_map_int }
+ }
+\cs_new_protected:Npn \ior_map_inline_loop:NNN #1#2#3
+ {
+ #2 #3 \l_ior_internal_tl
+ \ior_if_eof:NF #3
+ {
+ \exp_args:No #1 \l_ior_internal_tl
+ \ior_map_inline_loop:NNN #1#2#3
+ }
+ }
+\tl_new:N \l_ior_internal_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_kernel_new:nnnn { file } { file-not-found }
+ { Space~in~file~name~'#1'. }
+ {
+ Spaces~are~not~permitted~in~files~loaded~by~LaTeX: \\
+ Further~errors~may~follow!
+ }
+\msg_kernel_new:nnnn { file } { space-in-file-name }
+ { Space~in~file~name~'#1'. }
+ {
+ Spaces~are~not~permitted~in~files~loaded~by~LaTeX: \\
+ Further~errors~may~follow!
+ }
+% \end{macrocode}
+%
+% \subsection{Deprecated functions}
+%
+% Deprecated on 2012-02-10, for removal by 2012-05-31.
+%
+% \begin{macro}{\iow_now_when_avail:Nn, \iow_now_when_avail:Nx}
+% For writing only if the stream requested is open at all.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_now_when_avail:Nn #1
+ { \cs_if_free:NTF #1 { \use_none:n } { \iow_now:Nn #1 } }
+\cs_new_protected:Npn \iow_now_when_avail:Nx #1
+ { \cs_if_free:NTF #1 { \use_none:n } { \iow_now:Nx #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% Deprecated on 2011-05-27, for removal by 2011-08-31.
+%
+% \begin{macro}{\iow_now_buffer_safe:Nn, \iow_now_buffer_safe:Nx}
+% This is much more easily done using the wrapping system: there is
+% an expansion there, so a bit of a hack is needed.
+% \begin{macrocode}
+%<*deprecated>
+\cs_new_protected:Npn \iow_now_buffer_safe:Nn #1#2
+ { \iow_wrap:xnnnN { \exp_not:n {#2} } { } \c_zero { } \iow_now:Nn #1 }
+\cs_new_protected:Npn \iow_now_buffer_safe:Nx #1#2
+ { \iow_wrap:xnnnN {#2} { } \c_zero { } \iow_now:Nn #1 }
+%</deprecated>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_open_streams:}
+% \begin{macro}{\iow_open_streams:}
+% Slightly misleading names.
+% \begin{macrocode}
+%<*deprecated>
+\cs_new_eq:NN \ior_open_streams: \ior_list_streams:
+\cs_new_eq:NN \iow_open_streams: \iow_list_streams:
+%</deprecated>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
% \begin{macrocode}
%</initex|package>