% \iffalse meta-comment
%
%% File: l3file.dtx Copyright (C) 1990-2016 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
%% license or (at your option) any later version.  The latest version
%% of this license is in the file
%%
%%    http://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 released version of this bundle is available from CTAN.
%%
%% -----------------------------------------------------------------------
%%
%% The development version of the bundle can be found at
%%
%%    http://www.latex-project.org/svnroot/experimental/trunk/
%%
%% for those people who are interested.
%%
%%%%%%%%%%%
%% NOTE: %%
%%%%%%%%%%%
%%
%%   Snapshots taken from the repository represent work in progress and may
%%   not work or may contain conflicting material!  We therefore ask
%%   people _not_ to put them into distributions, archives, etc. without
%%   prior consultation with the LaTeX3 Project.
%%
%% -----------------------------------------------------------------------
%
%<*driver>
\documentclass[full]{l3doc}
%</driver>
%<*driver|package>
\GetIdInfo$Id: l3file.dtx 6685 2016-08-19 16:26:52Z bruno $
  {L3 File and I/O operations}
%</driver|package>
%<*driver>
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3file} package\\ File and I/O operations^^A
%   \thanks{This file describes v\ExplFileVersion,
%      last revised \ExplFileDate.}^^A
% }
%
% \author{^^A
%  The \LaTeX3 Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released \ExplFileDate}
%
% \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{} 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.
%
% 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.  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. File names will be quoted
% using |"| tokens if they contain spaces: as a result, |"| tokens are
% \emph{not} permitted in file names.
%
% \section{File operation functions}
%
% \begin{variable}{\g_file_current_name_tl}
%   Contains the name of the current \LaTeX{} file. This variable
%   should not be modified: it is intended for information only. It
%   will be equal to \cs{c_sys_jobname_str} at the start of a \LaTeX{}
%   run and will be modified each time a file is read using
%   \cs{file_input:n}.
% \end{variable}
%
% \begin{function}[TF, updated = 2012-02-10]{\file_if_exist:n}
%   \begin{syntax}
%     \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Searches for \meta{file name} using the current \TeX{} search
%   path and the additional paths controlled by
%   \cs{file_path_include:n}).
% \end{function}
%
% \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 contain the
%   marker \cs{q_no_value}.
% \end{function}
%
% \begin{function}[updated = 2012-02-17]{\file_input:n}
%   \begin{syntax}
%     \cs{file_input:n} \Arg{file name}
%   \end{syntax}
%   Searches for \meta{file name} in the path as detailed for
%   \cs{file_if_exist:nTF}, and if found reads in the file as
%   additional \LaTeX{} source. All files read are recorded
%   for information and the file name stack is updated by this
%   function. An error will be raised if the file is not found.
% \end{function}
%
% \begin{function}[updated = 2012-07-04]{\file_path_include:n}
%   \begin{syntax}
%     \cs{file_path_include:n} \Arg{path}
%   \end{syntax}
%   Adds \meta{path} to the list of those used to search when reading
%   files. The assignment is local.
%   The \meta{path} is processed in the same way as a \meta{file name},
%   \emph{i.e.}, with \texttt{x}-type expansion except active
%   characters.
% \end{function}
%
% \begin{function}[updated = 2012-07-04]{\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.
%   The \meta{path} is processed in the same way as a \meta{file name},
%   \emph{i.e.}, with \texttt{x}-type expansion except active
%   characters.
% \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.
%
% 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.
% \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:cn}
%   \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 will clear
%   any existing content in the file (\emph{i.e.}~writing is \emph{not}
%   additive).
% \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}[updated = 2015-08-01]{\ior_list_streams:, \iow_list_streams:}
%   \begin{syntax}
%     \cs{ior_list_streams:}
%     \cs{iow_list_streams:}
%   \end{syntax}
%   Displays a list of the file names associated with each open
%   stream: intended for tracking down problems.
% \end{function}
%
% \subsection{Reading from files}
%
% \begin{function}[added = 2012-06-24]{\ior_get:NN}
%   \begin{syntax}
%     \cs{ior_get:NN} \meta{stream} \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 input \meta{stream} and stores
%   the result locally in the \meta{token list} variable. 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. Note that any blank lines will be converted to the token
%   \cs{par}. Therefore, if skipping blank lines is requires 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}
%   may be used. Also notice that if multiple lines are read to match braces
%   then the resulting token list will contain \cs{par} tokens. As normal
%   \TeX{} tokenization is in force, any lines which do not end in a comment
%   character (usually |%|) will have the line ending converted to a space,
%   so for example input
%   \begin{verbatim}
%      a b  c
%   \end{verbatim}
%   will result in a token list |a b c |.
%   \begin{texnote}
%     This protected macro expands to the \TeX{} primitive \tn{read}
%     along with the |to| keyword.
%   \end{texnote}
% \end{function}
%
% \begin{function}[added = 2012-06-24, updated = 2012-07-31]{\ior_get_str:NN}
%   \begin{syntax}
%     \cs{ior_get_str:NN} \meta{stream} \meta{token list variable}
%   \end{syntax}
%   Function that reads one line from the input \meta{stream} and stores
%   the result locally in the \meta{token list} variable. If the
%   \meta{stream} is not open, input is requested from the terminal.
%   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 will
%   always only read one line and any blank lines in the input
%   will 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}
%   will result in a token list |a b  c| with the letters |a|, |b|, and |c|
%   having category code~12.
%   \begin{texnote}
%     This protected macro is a wrapper around the \eTeX{} primitive
%     \tn{readline}.  However, the end-line character normally added by
%     this primitive is not included in the result of
%     \cs{ior_get_str:NN}.
%   \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}[updated = 2012-06-05]{\iow_now:Nn, \iow_now:Nx, \iow_now:cn, \iow_now:cx}
%   \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}).
% \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, \iow_shipout:cn, \iow_shipout:cx}
%   \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}).
%   \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} will not be recognized in
%     the argument of \cs{iow_shipout:Nn}.  This may lead to the
%     insertion of additionnal unwanted line-breaks.
%   \end{texnote}
% \end{function}
%
% \begin{function}[updated = 2012-09-08]{\iow_shipout_x:Nn, \iow_shipout_x:Nx, \iow_shipout_x:cn, \iow_shipout_x:cx}
%   \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}
%     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} will not be recognized in
%     the argument of \cs{iow_shipout:Nn}.  This may lead to the
%     insertion of additionnal 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: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}).
%   \begin{texnote}
%     When using \pkg{expl3} with a format other than \LaTeX{}, the
%     character inserted by \cs{iow_newline:} will not be recognized by
%     \TeX{}, which may lead to the insertion of additionnal unwanted
%     line-breaks.  This issue only affects \cs{iow_shipout:Nn},
%     \cs{iow_shipout_x:Nn} and direct uses of primitive operations.
%   \end{texnote}
% \end{function}
%
% \subsection{Wrapping lines in output}
%
% \begin{function}[added = 2012-06-28, updated = 2015-08-05]{\iow_wrap:nnnN}
%   \begin{syntax}
%     \cs{iow_wrap:nnnN} \Arg{text} \Arg{run-on text} \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 character count
%   targeted will be 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 |\\| 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
%       \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 will typically be a wrapper around a write
%   operation. The output of \cs{iow_wrap:nnnN} (\emph{i.e.}~the argument
%   passed to the \meta{function}) will consist of characters of category
%   \enquote{other} (category code~12), with the exception of spaces which
%   will have category \enquote{space} (category code~10). This means that the
%   output will \emph{not} expand further when written to a file.
%
%   \begin{texnote}
%     Internally, \cs{iow_wrap:nnnN} carries out an \texttt{x}-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 = 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 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}[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 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}
%
% \subsection{Constant input--output streams}
%
% \begin{variable}{\c_term_ior}
%   Constant input stream for reading from the terminal. Reading from this
%   stream using \cs{ior_get: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}
%
% \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}
%
% \subsection{Internal file functions and variables}
%
% \begin{variable}{\g__file_internal_ior}
%   Used to test for the existence of files when opening.
% \end{variable}
%
% \begin{variable}{\l__file_internal_name_tl}
%   Used to return the full name of a file for internal use. This is
%   set by \cs{file_if_exist:nTF} and \cs{__file_if_exist:nT}, and
%   the value may then be used to load a file directly provided no
%   further operations intervene.
% \end{variable}
%
% \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~$13$) 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}
%
% \subsection{Internal input--output functions}
%
% \begin{function}[added = 2012-01-23]{\__ior_open:Nn, \__ior_open:No}
%   \begin{syntax}
%     \cs{__ior_open:Nn} \meta{stream} \Arg{file name}
%   \end{syntax}
%   This function has identical syntax to the public version. However,
%   is does not take precautions against active characters in the
%   \meta{file name}, and it does not attempt to add a \meta{path} to
%   the \meta{file name}: it is therefore intended to be used by
%   higher-level
%   functions which have already fully expanded the \meta{file name} and which
%   need to perform multiple open or close operations. See for example the
%   implementation of \cs{file_add_path:nN},
% \end{function}
%
% \begin{function}[added = 2014-08-23]{\__iow_with:Nnn}
%   \begin{syntax}
%     \cs{__iow_with:Nnn} \meta{integer} \Arg{value} \Arg{code}
%   \end{syntax}
%   If the \meta{integer} is equal to the \meta{value} then this
%   function simply runs the \meta{code}.  Otherwise it saves the
%   current value of the \meta{integer}, sets it to the \meta{value},
%   runs the \meta{code}, and restores the \meta{integer} to its former
%   value.  This is used to ensure that the \tn{newlinechar} is~$10$
%   when writing to a stream, which lets \cs{iow_newline:} work, and
%   that \tn{errorcontextlines} is $-1$ when displaying a message.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3file} implementation}
%
% \TestFiles{m3file001}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=file>
%    \end{macrocode}
%
% \subsection{File operations}
%
% \begin{variable}{\g_file_current_name_tl}
%   The name of the current file should be available at all times.
%   For the format the file name needs to be picked up at the start of the
%   file. In \LaTeXe{} package mode the current file name is collected from
%   \tn{@currname}.
%    \begin{macrocode}
\tl_new:N \g_file_current_name_tl
%<*initex>
\tex_everyjob:D \exp_after:wN
  {
    \tex_the:D \tex_everyjob:D
    \tl_gset:Nx \g_file_current_name_tl { \tex_jobname:D }
  }
%</initex>
%<*package>
\cs_if_exist:NT \@currname
  { \tl_gset_eq:NN \g_file_current_name_tl \@currname }
%</package>
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_stack_seq}
%   The input list of files is stored as a sequence stack.
%    \begin{macrocode}
\seq_new:N \g_@@_stack_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\g_@@_record_seq}
%   The total list of files used is recorded separately from the current
%   file stack, as nothing is ever popped from this list.  The current
%   file name should be included in the file list!  In format mode, this
%   is done at the very start of the \TeX{} run.  In package mode we
%   will eventually copy the contents of \cs{@filelist}.
%    \begin{macrocode}
\seq_new:N \g_@@_record_seq
%<*initex>
\tex_everyjob:D \exp_after:wN
  {
    \tex_the:D \tex_everyjob:D
    \seq_gput_right:NV \g_@@_record_seq \g_file_current_name_tl
  }
%</initex>
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_internal_tl}
%   Used as a short-term scratch variable.  It may be possible to reuse
%   \cs{l_@@_internal_name_tl} there.
%    \begin{macrocode}
\tl_new:N \l_@@_internal_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_internal_name_tl}
%   Used to return the fully-qualified name of a file.
%    \begin{macrocode}
\tl_new:N \l_@@_internal_name_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_search_path_seq}
%   The current search path.
%    \begin{macrocode}
\seq_new:N \l_@@_search_path_seq
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_saved_search_path_seq}
%   The current search path has to be saved for package use.
%    \begin{macrocode}
%<*package>
\seq_new:N \l_@@_saved_search_path_seq
%</package>
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_internal_seq}
%   Scratch space for comma list conversion in package mode.
%    \begin{macrocode}
%<*package>
\seq_new:N \l_@@_internal_seq
%</package>
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}[int]{\@@_name_sanitize:nn}
% \begin{macro}[int, aux]{\@@_name_sanitize_aux:n}
%   For converting a token list to a string where active characters are treated
%   as strings from the start. The logic to the quoting normalisation is the
%   same as used by \texttt{lualatexquotejobname}: check for balanced |"|, and
%   assuming they balance strip all of them out before quoting the entire name
%   if it contains spaces.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_name_sanitize:nn #1#2
  {
    \group_begin:
      \seq_map_inline:Nn \l_char_active_seq
        { \char_set:active:Npx ##1 { \cs_to_str:N ##1 } }
      \tl_set:Nx \l_@@_internal_name_tl {#1}
      \tl_set:Nx \l_@@_internal_name_tl
        { \tl_to_str:N \l_@@_internal_name_tl }
      \int_compare:nNnTF
        {
          \int_mod:nn
            {
              0 \tl_map_function:NN \l_@@_internal_name_tl
                \@@_name_sanitize_aux:n
            }
            \c_two
        }
        = \c_zero
        {
          \tl_remove_all:Nn \l_@@_internal_name_tl { " }
          \tl_if_in:NnT \l_@@_internal_name_tl { ~ }
            {
              \tl_set:Nx \l_@@_internal_name_tl
                { " \exp_not:V \l_@@_internal_name_tl " }
            }
        }
        {
          \__msg_kernel_error:nnx
            { kernel } { unbalanced-quote-in-filename }
            { \l_@@_internal_name_tl }
        }
      \use:x
        {
          \group_end:
          \exp_not:n {#2} { \l_@@_internal_name_tl }
        }
  }
\cs_new:Npn \@@_name_sanitize_aux:n #1
  {
    \token_if_eq_charcode:NNT #1 "
      { + \c_one }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\file_add_path:nN}
% \begin{macro}[aux]{\@@_add_path:nN, \@@_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
%   current directory, this is straight-forward.  For other locations, a
%   search has to be made looking at each potential path in turn. The first
%   location is of course treated as the correct one. If nothing is found,
%   |#2| is returned empty.
%    \begin{macrocode}
\cs_new_protected:Npn \file_add_path:nN #1
  { \@@_name_sanitize:nn {#1} { \@@_add_path:nN } }
\cs_new_protected:Npn \@@_add_path:nN #1#2
  {
    \__ior_open:Nn \g_@@_internal_ior {#1}
    \ior_if_eof:NTF \g_@@_internal_ior
      { \@@_add_path_search:nN {#1} #2 }
      { \tl_set:Nn #2 {#1} }
    \ior_close:N \g_@@_internal_ior
  }
\cs_new_protected:Npn \@@_add_path_search:nN #1#2
  {
    \tl_set:Nn #2 { \q_no_value  }
%<*package>
    \cs_if_exist:NT \input@path
      {
        \seq_set_eq:NN \l_@@_saved_search_path_seq
          \l_@@_search_path_seq
        \seq_set_split:NnV \l_@@_internal_seq { , } \input@path
        \seq_concat:NNN \l_@@_search_path_seq
          \l_@@_search_path_seq \l_@@_internal_seq
      }
%</package>
    \seq_map_inline:Nn \l_@@_search_path_seq
      {
        \__ior_open:Nn \g_@@_internal_ior { ##1 #1 }
        \ior_if_eof:NF \g_@@_internal_ior
          {
            \tl_set:Nx #2 { ##1 #1 }
            \seq_map_break:
          }
      }
%<*package>
    \cs_if_exist:NT \input@path
      {
        \seq_set_eq:NN \l_@@_search_path_seq
          \l_@@_saved_search_path_seq
      }
%</package>
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[TF]{\file_if_exist:n}
%   The test for the existence of a file is a wrapper around the function to
%   add a path to a file. If the file was found, the path will contain
%   something, whereas if the file was not located then the return value
%   will be \cs{q_no_value}.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \file_if_exist:n #1 { T , F , TF }
  {
    \file_add_path:nN {#1} \l_@@_internal_name_tl
    \quark_if_no_value:NTF \l_@@_internal_name_tl
      { \prg_return_false: }
      { \prg_return_true: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\file_input:n}
% \begin{macro}[int]{\@@_if_exist:nT}
% \begin{macro}[aux]{\@@_input:n \@@_input:V}
% \begin{macro}[aux]{\@@_input_aux:n, \@@_input_aux:o}
%   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
  {
    \@@_if_exist:nT {#1}
      { \@@_input:V \l_@@_internal_name_tl }
  }
%    \end{macrocode}
%   This code is spun out as a separate function to encapsulate the
%   error message into a easy-to-reuse form.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_if_exist:nT #1#2
  {
    \file_if_exist:nTF {#1}
      {#2}
      {
        \@@_name_sanitize:nn {#1}
          { \__msg_kernel_error:nnx { kernel } { file-not-found } }
      }
  }
\cs_new_protected:Npn \@@_input:n #1
  {
    \tl_if_in:nnTF {#1} { . }
      { \@@_input_aux:n {#1} }
      { \@@_input_aux:o { \tl_to_str:n { #1 . tex } } }
  }
\cs_generate_variant:Nn \@@_input:n { V }
\cs_new_protected:Npn \@@_input_aux:n #1
  {
%<*initex>
    \seq_gput_right:Nn \g_@@_record_seq {#1}
%</initex>
%<*package>
    \clist_if_exist:NTF \@filelist
      { \@addtofilelist {#1} }
      { \seq_gput_right:Nn \g_@@_record_seq {#1} }
%</package>
    \seq_gpush:No \g_@@_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_@@_stack_seq \l_@@_internal_tl
    \tl_gset_eq:NN \g_file_current_name_tl \l_@@_internal_tl
  }
\cs_generate_variant:Nn \@@_input_aux:n { o }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\file_path_include:n}
% \begin{macro}{\file_path_remove:n}
% \begin{macro}[aux]{\@@_path_include:n}
%   Wrapper functions to manage the search path.
%    \begin{macrocode}
\cs_new_protected:Npn \file_path_include:n #1
  { \@@_name_sanitize:nn {#1} { \@@_path_include:n } }
\cs_new_protected:Npn \@@_path_include:n #1
  {
    \seq_if_in:NnF \l_@@_search_path_seq {#1}
      { \seq_put_right:Nn \l_@@_search_path_seq {#1} }
  }
\cs_new_protected:Npn \file_path_remove:n #1
  {
    \@@_name_sanitize:nn {#1}
      { \seq_remove_all:Nn \l_@@_search_path_seq }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\file_list:}
%   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 each file name into a string.
%    \begin{macrocode}
\cs_new_protected:Npn \file_list:
  {
    \seq_set_eq:NN \l_@@_internal_seq \g_@@_record_seq
%<*package>
    \clist_if_exist:NT \@filelist
      {
        \clist_map_inline:Nn \@filelist
          {
            \seq_put_right:No \l_@@_internal_seq
              { \tl_to_str:n {##1} }
          }
      }
%</package>
    \seq_remove_duplicates:N \l_@@_internal_seq
    \iow_log:n { *~File~List~* }
    \seq_map_inline:Nn \l_@@_internal_seq { \iow_log:n {##1} }
    \iow_log:n { ************* }
  }
%    \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.  File names recorded in
% \cs{@filelist} must be turned to strings before being added to
% \cs{g_@@_record_seq}.
%    \begin{macrocode}
%<*package>
\AtBeginDocument
  {
    \clist_map_inline:Nn \@filelist
      { \seq_gput_right:No \g_@@_record_seq { \tl_to_str:n {#1} } }
  }
%</package>
%    \end{macrocode}
%
% \subsection{Input operations}
%
%    \begin{macrocode}
%<@@=ior>
%    \end{macrocode}
%
% \subsubsection{Variables and 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}{\g_@@_streams_seq}
%   A list of the currently-available input streams to be used as a
%   stack.  In format mode, all streams (from $0$ to~$15$) are
%   available, while the package requests streams to \LaTeXe{} as they
%   are needed (initially none are needed), so the starting point
%   varies!
%    \begin{macrocode}
\seq_new:N \g_@@_streams_seq
%<*initex>
\seq_gset_split:Nnn \g_@@_streams_seq { , }
  { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 }
%</initex>
%    \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 Con\TeX{}t, we need to look at |\count38| but there
%   is no subtraction: like the original plain \TeX{}/\LaTeXe{} mechanism
%   it holds the value of the \emph{last} stream allocated.
%    \begin{macrocode}
\prop_new:N \g_@@_streams_prop
%<*package>
\int_step_inline:nnnn
  { \c_zero }
  { \c_one }
  {
    \cs_if_exist:NTF \normalend
      { \tex_count:D 38 \scan_stop: }
      {
        \tex_count:D 16 \scan_stop:
          \cs_if_exist:NT \loccount { - \c_one }
      }
  }
  {
    \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
  }
%</package>
%    \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{macro}{\ior_open:Nn, \ior_open:cn}
% \begin{macro}[aux]{\@@_open_aux:Nn}
%   Opening an input stream requires a bit of pre-processing. The file name
%   is sanitized to deal with active characters, before an auxiliary adds a
%   path and checks that the file really exists. If those two tests pass, then
%   pass the information on to the lower-level function which deals with
%   streams.
%    \begin{macrocode}
\cs_new_protected:Npn \ior_open:Nn #1#2
  { \__file_name_sanitize:nn {#2} { \@@_open_aux:Nn #1 } }
\cs_generate_variant:Nn \ior_open:Nn { c }
\cs_new_protected:Npn \@@_open_aux:Nn #1#2
  {
    \file_add_path:nN {#2} \l__file_internal_name_tl
    \quark_if_no_value:NTF \l__file_internal_name_tl
      { \__msg_kernel_error:nnx { kernel } { file-not-found } {#2} }
      { \@@_open:No #1 \l__file_internal_name_tl }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[TF]{\ior_open:Nn, \ior_open:cn}
% \begin{macro}[aux]{\@@_open_aux:NnTF}
%   Much the same idea for opening a read with a conditional, except the
%   auxiliary function does not issue an error if the file is not found.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF }
  { \__file_name_sanitize:nn {#2} { \@@_open_aux:NnTF #1 } }
\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 \@@_open_aux:NnTF #1#2
  {
    \file_add_path:nN {#2} \l__file_internal_name_tl
    \quark_if_no_value:NTF \l__file_internal_name_tl
      { \prg_return_false: }
      {
        \@@_open:No #1 \l__file_internal_name_tl
        \prg_return_true:
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[int]{\@@_new:N}
%   In package mode, 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}.
%    \begin{macrocode}
%<*package>
\exp_args:NNf \cs_new_protected:Npn \@@_new:N
  { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
%</package>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@@_open:Nn, \@@_open:No}
% \begin{macro}[aux]{\@@_open_stream:Nn}
%   The stream allocation itself uses the fact that there is a list of all of
%   those available, so allocation is simply a question of using the number at
%   the top of the list. In package mode, 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 \@@_open:Nn #1#2
  {
    \ior_close:N #1
    \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
      { \@@_open_stream:Nn #1 {#2} }
%<*initex>
      { \__msg_kernel_fatal:nn { kernel } { input-streams-exhausted } }
%</initex>
%<*package>
      {
        \@@_new:N #1
        \tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
        \@@_open_stream:Nn #1 {#2}
      }
%</package>
  }
\cs_generate_variant:Nn \@@_open:Nn { No }
\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_openin:D #1 #2 \scan_stop:
  }
%    \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 { \c_minus_one < #1 < \c_sixteen }
      {
        \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_list_streams:}
% \begin{macro}[aux]{\@@_list_streams:Nn}
%   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 (in fact a question mark) otherwise.
%   The code of the message \texttt{show-streams} takes care of
%   translating |ior|/|iow| to English.  The list of streams is
%   formatted using \cs{__msg_show_item_unbraced:nn}.
%    \begin{macrocode}
\cs_new_protected:Npn \ior_list_streams:
  { \@@_list_streams:Nn \g_@@_streams_prop { ior } }
\cs_new_protected:Npn \@@_list_streams:Nn #1#2
  {
    \__msg_show_pre:nnxxxx { LaTeX / kernel } { show-streams }
      {#2} { \prop_if_empty:NF #1 { ? } } { } { }
    \__msg_show_wrap:n
      { \prop_map_function:NN #1 \__msg_show_item_unbraced:nn }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{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_get:NN}
%   And here we read from files.
%    \begin{macrocode}
\cs_new_protected:Npn \ior_get:NN #1#2
  { \tex_read:D #1 to #2 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ior_get_str:NN}
%   Reading as strings is a more complicated wrapper, as we wish to
%   remove the endline character.
%    \begin{macrocode}
\cs_new_protected:Npn \ior_get_str:NN #1#2
  {
    \use:x
      {
        \int_set_eq:NN \tex_endlinechar:D \c_minus_one
        \exp_not:n { \etex_readline:D #1 to #2 }
        \int_set:Nn \tex_endlinechar:D { \int_use:N \tex_endlinechar:D }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\g__file_internal_ior}
%   Needed by the higher-level code, but cannot be created until here.
%    \begin{macrocode}
\ior_new:N \g__file_internal_ior
%    \end{macrocode}
% \end{variable}
%
% \subsection{Output operations}
%
%    \begin{macrocode}
%<@@=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}{\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
\int_const:Nn \c_term_iow { 128 }
%    \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
%<*initex>
\seq_set_eq:NN \g_@@_streams_seq \g__ior_streams_seq
\cs_if_exist:NT \luatex_directlua:D
  {
    \int_compare:nNnT \luatex_luatexversion:D > { 80 }
      {
        \int_step_inline:nnnn { 16 } { 1 } { 127 }
          {
            \seq_gput_right:Nn \g_@@_streams_seq {#1}
          }
      }
  }
%</initex>
%    \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
%<*package>
\int_step_inline:nnnn
  { \c_zero }
  { \c_one }
  {
    \cs_if_exist:NTF \normalend
      { \tex_count:D 39 \scan_stop: }
      {
        \tex_count:D 17 \scan_stop:
          \cs_if_exist:NT \loccount { - \c_one }
      }
  }
  {
    \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
  }
%</package>
%    \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{macro}[int]{\@@_new:N}
%   As for read streams, copy \tn{newwrite} in package mode, making sure
%   that it is not \tn{outer}.
%    \begin{macrocode}
%<*package>
\exp_args:NNf \cs_new_protected:Npn \@@_new:N
  { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
%</package>
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\iow_open:Nn, \iow_open:cn}
% \begin{macro}[int]{\@@_open:Nn}
% \begin{macro}[aux]{\@@_open_stream:Nn}
%   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
  { \__file_name_sanitize:nn {#2} { \@@_open:Nn #1 } }
\cs_generate_variant:Nn \iow_open:Nn { c }
\cs_new_protected:Npn \@@_open:Nn #1#2
  {
    \iow_close:N #1
    \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
      { \@@_open_stream:Nn #1 {#2} }
%<*initex>
      { \__msg_kernel_fatal:nn { kernel } { output-streams-exhausted } }
%</initex>
%<*package>
      {
        \@@_new:N #1
        \tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
        \@@_open_stream:Nn #1 {#2}
      }
%</package>
  }
\cs_generate_variant:Nn \@@_open:Nn { No }
\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 #2 \scan_stop:
  }
%    \end{macrocode}
% \end{macro}
% \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_minus_one < #1 < \c_sixteen }
      {
        \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_ior
      }
  }
\cs_generate_variant:Nn \iow_close:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\iow_list_streams:}
% \begin{macro}{\@@_list_streams:Nn}
%   Done as for input, but with a copy of the auxiliary so the name is correct.
%    \begin{macrocode}
\cs_new_protected:Npn \iow_list_streams:
  { \@@_list_streams:Nn \g_@@_streams_prop { iow } }
\cs_new_eq:NN \@@_list_streams:Nn \__ior_list_streams:Nn
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Deferred writing}
%
% \begin{macro}{\iow_shipout_x:Nn, \iow_shipout_x:Nx, \iow_shipout_x:cn, \iow_shipout_x:cx}
%   First the easy part, this is the primitive, which expects its
%   argument to be braced.
%    \begin{macrocode}
\cs_new_protected:Npn \iow_shipout_x:Nn #1#2
  { \tex_write:D #1 {#2} }
\cs_generate_variant:Nn \iow_shipout_x:Nn { c, Nx, cx }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\iow_shipout:Nn, \iow_shipout:Nx, \iow_shipout:cn, \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 { c, Nx, cx }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Immediate writing}
%
% \begin{macro}[aux]{\@@_with:Nnn, \@@_with_aux:nNnn}
%   If the integer~|#1| is equal to~|#2|, just leave~|#3| in the input
%   stream.  Otherwise, pass the old value to an auxiliary, which sets
%   the integer to the new value, runs the code, and restores the
%   integer.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_with:Nnn #1#2
  {
    \int_compare:nNnTF {#1} = {#2}
      { \use:n }
      { \exp_args:No \@@_with_aux:nNnn { \int_use:N #1 } #1 {#2} }
  }
\cs_new_protected:Npn \@@_with_aux:nNnn #1#2#3#4
  {
    \int_set:Nn #2 {#3}
    #4
    \int_set:Nn #2 {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\iow_now:Nn, \iow_now:Nx, \iow_now:cn, \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{@@_with:Nnn} to support formats such as plain \TeX{}: otherwise,
%   \cs{iow_newline:} would not work.  We do not do this for
%   \cs{iow_shipout:Nn} or \cs{iow_shipout_x:Nn}, as \TeX{} looks at the
%   value of the \tn{newlinechar} at shipout time in those cases.
%    \begin{macrocode}
\cs_new_protected:Npn \iow_now:Nn #1#2
  {
    \@@_with:Nnn \tex_newlinechar:D { `\^^J }
      { \tex_immediate:D \tex_write:D #1 { \exp_not:n {#2} } }
  }
\cs_generate_variant:Nn \iow_now:Nn { c, Nx, cx }
%    \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:Npn \iow_log:x  { \iow_now:Nx \c_log_iow  }
\cs_new_protected:Npn \iow_log:n  { \iow_now:Nn \c_log_iow  }
\cs_set_protected:Npn \iow_term:x { \iow_now:Nx \c_term_iow }
\cs_new_protected:Npn \iow_term:n { \iow_now:Nn \c_term_iow }
%    \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{macro}[var, added = 2012-06-24]{\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 Mik\TeX{}.
%    \begin{macrocode}
\int_new:N  \l_iow_line_count_int
\int_set:Nn \l_iow_line_count_int { 78 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\l_@@_target_count_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_@@_target_count_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]
%   {
%     \l_@@_current_line_int,
%     \l_@@_current_word_int,
%     \l_@@_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_@@_current_line_int
\int_new:N \l_@@_current_word_int
\int_new:N \l_@@_current_indentation_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]
%   {
%     \l_@@_current_line_tl,
%     \l_@@_current_word_tl,
%     \l_@@_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_@@_current_line_tl
\tl_new:N \l_@@_current_word_tl
\tl_new:N \l_@@_current_indentation_tl
%    \end{macrocode}
%\end{macro}
%
% \begin{macro}[aux]{\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{macro}
%
% \begin{macro}[aux]{\l_@@_newline_tl}
%   The token list inserted to produce the new line, with the
%   \meta{run-on text}.
%    \begin{macrocode}
\tl_new:N \l_@@_newline_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\l_@@_line_start_bool}
%   Boolean to avoid adding a space at the beginning of forced newlines,
%   and to know when to add the indentation.
%    \begin{macrocode}
\bool_new:N \l_@@_line_start_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\c_catcode_other_space_tl}
%   Create a space with category code $12$: an \enquote{other} space.
%    \begin{macrocode}
\tl_const:Nx \c_catcode_other_space_tl { \char_generate:nn { `\  } { 12 } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\c_@@_wrap_marker_tl}
% \begin{macro}[aux]
%   {
%     \c_@@_wrap_end_marker_tl,
%     \c_@@_wrap_newline_marker_tl,
%     \c_@@_wrap_indent_marker_tl,
%     \c_@@_wrap_unindent_marker_tl
%   }
%   Every special action of the wrapping code is preceded by
%   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
%   nicer.
%    \begin{macrocode}
\group_begin:
  \int_set_eq:NN \tex_escapechar:D \c_minus_one
  \tl_const:Nx \c_@@_wrap_marker_tl
    { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } }
\group_end:
\tl_map_inline:nn
  { { end } { newline } { indent } { unindent } }
  {
    \tl_const:cx { c_@@_wrap_ #1 _marker_tl }
      {
        \c_catcode_other_space_tl
        \c_@@_wrap_marker_tl
        \c_catcode_other_space_tl
        #1
        \c_catcode_other_space_tl
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\iow_indent:n}
% \begin{macro}[aux]{\@@_indent:n, \@@_indent_error:n}
%   We give a (protected) error 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:Npx \@@_indent:n #1
  {
    \c_@@_wrap_indent_marker_tl
    #1
    \c_@@_wrap_unindent_marker_tl
  }
\cs_new:Npn \@@_indent_error:n #1
  {
    \__msg_kernel_expandable_error:nn { kernel } { indent-outside-wrapping-code }
    #1
  }
\cs_new_protected:Npn \iow_indent:n { \@@_indent_error:n }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\iow_wrap:nnnN}
% \begin{macro}[aux]{\@@_wrap_set:Nx}
%   The main wrapping function works as follows.  First give |\\|,
%   \verb*|\ | and other formatting commands the correct definition for
%   messages, before fully-expanding the input.  In package mode, the
%   expansion uses \LaTeXe{}'s \cs{protect} mechanism.  Afterwards, set
%   the newline marker (two assignments to fully expand, then convert
%   to a string) and its length, and initialize some registers.
%   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:nnnN #1#2#3#4
  {
    \group_begin:
      \int_set_eq:NN \tex_escapechar:D \c_minus_one
      \cs_set:Npx \{ { \token_to_str:N \{ }
      \cs_set:Npx \# { \token_to_str:N \# }
      \cs_set:Npx \} { \token_to_str:N \} }
      \cs_set:Npx \% { \token_to_str:N \% }
      \cs_set:Npx \~ { \token_to_str:N \~ }
      \int_set:Nn \tex_escapechar:D { 92 }
      \cs_set_eq:NN \\ \c_@@_wrap_newline_marker_tl
      \cs_set_eq:NN \  \c_catcode_other_space_tl
      \cs_set_eq:NN \iow_indent:n \@@_indent:n
      #3
%<*initex>
      \tl_set:Nx \l_@@_wrap_tl {#1}
%</initex>
%<*package>
      \@@_wrap_set:Nx \l_@@_wrap_tl {#1}
%</package>
%    \end{macrocode}
%   To warn users that \cs{iow_indent:n} only works in the first argument
%   of \cs{iow_wrap:nnnN} reset \cs{iow_indent:n} to its error
%   definition.  Then store a newline character and the run-on text as a
%   string in \cs{l_@@_newline_tl}, and set some variables.  The first
%   line's target count is equal to the length of the whole line.  The
%   value \cs{l_@@_target_count_int} is altered later on by
%   \cs{@@_wrap_set_target:}.
%    \begin{macrocode}
      \cs_set_eq:NN \iow_indent:n \@@_indent_error:n
      \tl_set:Nx \l_@@_newline_tl { \iow_newline: #2 }
      \tl_set:Nx \l_@@_newline_tl { \tl_to_str:N \l_@@_newline_tl }
      \int_set_eq:NN \l_@@_target_count_int \l_iow_line_count_int
      \tl_clear:N \l_@@_current_indentation_tl
      \int_zero:N \l_@@_current_line_int
      \tl_set:Nn \l_@@_current_line_tl { \use_none:n }
      \bool_set_true:N \l_@@_line_start_bool
%    \end{macrocode}
%   After some setup above (in particular the odd setting of the current line
%   to \cs{use_none:n}), a loop goes through space-delimited words in the
%   message, recognizing special markers.  To make sure that the first line
%   behaves identically to others, start with a newline marker: the
%   \cs{use_none:n} above avoids actually getting a new line in the output.
%    \begin{macrocode}
      \use:x
        {
          \exp_not:n { \tl_clear:N \l_@@_wrap_tl }
          \@@_wrap_loop:w
          \tl_to_str:N \c_@@_wrap_newline_marker_tl
          \tl_to_str:N \l_@@_wrap_tl
          \tl_to_str:N \c_@@_wrap_end_marker_tl
          \c_space_tl \c_space_tl
          \exp_not:N \q_stop
        }
    \exp_args:NNo \group_end:
    #4 \l_@@_wrap_tl
  }
%    \end{macrocode}
%   As using the generic loader will mean that \cs{protected@edef} is
%   not available, it's not placed directly in the wrap function but is set
%   up as an auxiliary. In the generic loader this can then be redefined.
%    \begin{macrocode}
%<*package>
\cs_new_eq:NN \@@_wrap_set:Nx \protected@edef
%</package>
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux]{\@@_wrap_set_target:}
%   This is called at the beginning of every line (both those forced by
%   |\\| and those due to line-breaking).  The initial call does
%   nothing except redefine \cs{@@_wrap_set_target:} itself (within the
%   group in which \cs{iow_wrap:nnnN} works).  The next call (at the
%   beginning of the second line) disables any later call and sets the
%   \cs{l_@@_target_count_int} to the correct value, namely the
%   \cs{l_iow_line_count_int} shortened by the length of the run-on
%   text (the shift by~$1$ is due to the presence of \cs{iow_newline:}
%   in \cs{l_@@_newline_tl}).
%   This is a bit of a hack to measure the string length of the run on text
%   without the \pkg{l3str} module (which is still experimental). This should
%   be replaced once the string module is finalised with something a little
%   cleaner.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_wrap_set_target:
  {
    \cs_set_protected:Npn \@@_wrap_set_target:
      {
        \cs_set_protected:Npn \@@_wrap_set_target: { }
        \tl_replace_all:Nnn \l_@@_newline_tl { ~ } { \c_space_tl }
        \int_set:Nn \l_@@_target_count_int
          { \l_iow_line_count_int - \tl_count:N \l_@@_newline_tl + \c_one }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\@@_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 \@@_wrap_loop:w #1 ~ %
  {
    \tl_set:Nn \l_@@_current_word_tl {#1}
    \tl_if_eq:NNTF \l_@@_current_word_tl \c_@@_wrap_marker_tl
      { \@@_wrap_special:w }
      { \@@_wrap_word: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\@@_wrap_word:}
% \begin{macro}[aux]{\@@_wrap_word_fits:}
% \begin{macro}[aux]{\@@_wrap_word_newline:}
%   For a normal word, update the line count, 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 character count) are then put in the new line.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_wrap_word:
  {
    \int_set:Nn \l_@@_current_word_int
      { \exp_args:No  \str_count_ignore_spaces:n \l_@@_current_word_tl }
    \int_add:Nn \l_@@_current_line_int { \l_@@_current_word_int }
    \int_compare:nNnTF \l_@@_current_line_int < \l_@@_target_count_int
      { \@@_wrap_word_fits: }
      { \@@_wrap_word_newline: }
    \@@_wrap_loop:w
  }
\cs_new_protected:Npn \@@_wrap_word_fits:
  {
    \bool_if:NTF \l_@@_line_start_bool
      {
        \bool_set_false:N \l_@@_line_start_bool
        \tl_put_right:Nx \l_@@_current_line_tl
          { \l_@@_current_indentation_tl \l_@@_current_word_tl }
        \int_add:Nn \l_@@_current_line_int
          { \l_@@_current_indentation_int }
      }
      {
        \tl_put_right:Nx \l_@@_current_line_tl
          { ~ \l_@@_current_word_tl }
        \int_incr:N \l_@@_current_line_int
      }
  }
\cs_new_protected:Npn \@@_wrap_word_newline:
  {
    \@@_wrap_set_target:
    \tl_put_right:Nx \l_@@_wrap_tl
      { \l_@@_current_line_tl \l_@@_newline_tl }
    \int_set:Nn \l_@@_current_line_int
      {
        \l_@@_current_word_int
        + \l_@@_current_indentation_int
      }
    \tl_set:Nx \l_@@_current_line_tl
      { \l_@@_current_indentation_tl \l_@@_current_word_tl }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux]{\@@_wrap_special:w}
% \begin{macro}[aux]{\@@_wrap_newline:w}
% \begin{macro}[aux]{\@@_wrap_indent:w}
% \begin{macro}[aux]{\@@_wrap_unindent:w}
% \begin{macro}[aux]{\@@_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 \@@_wrap_special:w #1 ~ #2 ~ #3 ~ %
  {
    \use:c { @@_wrap_#1: }
    \str_if_eq_x:nnTF { #2~#3 } { ~ \c_@@_wrap_marker_tl }
      { \@@_wrap_special:w }
      { \@@_wrap_loop:w #2 ~ #3 ~ }
  }
\cs_new_protected:Npn \@@_wrap_newline:
  {
    \@@_wrap_set_target:
    \tl_put_right:Nx \l_@@_wrap_tl
      { \l_@@_current_line_tl \l_@@_newline_tl }
    \int_zero:N \l_@@_current_line_int
    \tl_clear:N \l_@@_current_line_tl
    \bool_set_true:N \l_@@_line_start_bool
  }
\cs_new_protected:Npx \@@_wrap_indent:
  {
    \int_add:Nn \l_@@_current_indentation_int \c_four
    \tl_put_right:Nx \exp_not:N \l_@@_current_indentation_tl
      { \c_space_tl \c_space_tl \c_space_tl \c_space_tl }
  }
\cs_new_protected:Npn \@@_wrap_unindent:
  {
    \int_sub:Nn \l_@@_current_indentation_int \c_four
    \tl_set:Nx \l_@@_current_indentation_tl
      { \prg_replicate:nn \l_@@_current_indentation_int { ~ } }
  }
\cs_new_protected:Npn \@@_wrap_end:
  {
    \tl_put_right:Nx \l_@@_wrap_tl
      { \l_@@_current_line_tl }
    \use_none_delimit_by_q_stop:w
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Messages}
%
%    \begin{macrocode}
\__msg_kernel_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_kernel_new:nnnn { kernel } { input-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.
  }
\__msg_kernel_new:nnnn { kernel } { output-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 { kernel } { unbalanced-quote-in-filename }
  { Unbalanced~quotes~in~file~name~'#1'. }
  {
    File~names~must~contain~balanced~numbers~of~quotes~(").
  }
\__msg_kernel_new:nnn { kernel } { indent-outside-wrapping-code }
  { Only~\iow_wrap:nnnN~(arg~1)~allows~\iow_indent:n }
%    \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
