% \iffalse meta-comment
%
%% File: l3io.dtx Copyright (C) 1990-2011 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|package>
\RequirePackage{l3names}
\GetIdInfo$Id: l3io.dtx 2799 2011-09-10 15:46:33Z will $
  {L3 Experimental input-output operations}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3io} package\\ Input--output 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}
%
% Reading and writing from file streams is handled in \LaTeX3 using
% functions with prefixes \cs{iow_\ldots} (file reading) and
% \cs{ior_\ldots} (file writing). Many of the basic functions are
% very similar, with reading and writing using the same syntax and
% function concepts. As a result, the reading and writing functions
% are documented together where this makes sense.
%
% 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.
%
% Reading from or writing to a file requires a \meta{stream} to
% be used. This is a csname which refers to the file being processed,
% and is independent of the name of the file (except of course that
% the file name is needed when the file is opened).
%
% \section{Opening and closing streams}
%
% \begin{function}{\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.
% \end{function}
%
% \begin{function}{\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 file 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}{\ior_close:N, \ior_close:c}
%   \begin{syntax}
%     \cs{ior_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 programmer. The name of the \meta{stream} will be freed at
%   this stage, to ensure that any further attempts to read from it results
%   in an error.
% \end{function}
%
% \begin{function}{\iow_close:N, \iow_close:c}
%   \begin{syntax}
%     \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 programmer. The name of the \meta{stream} will be freed at
%   this stage, to ensure that any further attempts to write to it results
%   in an error.
% \end{function}
%
% \begin{function}{\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}
%
% \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_now_when_avail:Nn, \iow_now_when_avail:Nx}
%   \begin{syntax}
%     \cs{iow_now_when_avail:Nn} \meta{stream} \Arg{tokens}
%   \end{syntax}
%   If \meta{stream} is open, writes the \meta{tokens} to the \meta{stream}
%   in the  same manner as \cs{iow_now:Nn}. If the \meta{stream} is not open,
%   the \meta{tokens}are simply thrown away.
% \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_stream { \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-06-03]{\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}, |\\| may be used to force a new line and \verb|\ | may be
%   used to represent a forced space (for example after a control
%   sequence). 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{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{Reading from files}
%
% \begin{function}{\ior_to: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 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.
%   \begin{texnote}
%     The is protected macro which expands to the \TeX{} primitive \tn{read}
%     along with the |to| keyword.
%   \end{texnote}
% \end{function}
%
% \begin{function}{\ior_gto:NN}
%   \begin{syntax}
%     \cs{ior_gto: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 globally 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.
%   \begin{texnote}
%     The is protected macro which expands to the \TeX{} primitives
%     \tn{global}\tn{read} along with the |to| keyword.
%   \end{texnote}
% \end{function}
%
% \begin{function}{\ior_str_to:NN}
%   \begin{syntax}
%     \cs{ior_str_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 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} 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}
%     The is protected macro which expands to the \eTeX{} primitive
%     \tn{readline} along with the |to| keyword.
%   \end{texnote}
% \end{function}
%
% \begin{function}{\ior_str_gto:NN}
%   \begin{syntax}
%     \cs{ior_str_gto: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 globally 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} 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}
%     The is protected macro which expands to the primitives
%     \tn{global}\tn{readline} along with the |to| keyword.
%   \end{texnote}
% \end{function}
%
%\begin{function}[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 or the \meta{file name} associated with
%  a \meta{stream} does not exist at all.
%\end{function}
%
% \section{Internal input--output functions}
%
% \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}{\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}
%
% \section{\pkg{l3io} implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*package>
\ProvidesExplPackage
  {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
\package_check_loaded_expl:
%</package>
%    \end{macrocode}
%
% \subsection{Primitives}
%
% \begin{macro}{\if_eof:w}
%   The primitive conditional
%    \begin{macrocode}
\cs_new_eq:NN \if_eof:w \tex_ifeof:D
%    \end{macrocode}
% \end{macro}
%
% \subsection{Variables and constants}
%
% \begin{variable}{\c_iow_term_stream, \c_ior_term_stream}
% \begin{variable}{\c_iow_log_stream, \c_ior_log_stream}
%   Here we allocate two output streams for writing to the transcript
%   file only (\cs{c_iow_log_stream}) and to both the terminal and
%   transcript file (\cs{c_iow_term_stream}). Both can be used to read
%   from and have equivalent \cs{c_ior} versions.
%    \begin{macrocode}
\cs_new_eq:NN \c_iow_term_stream \c_sixteen
\cs_new_eq:NN \c_ior_term_stream \c_sixteen
\cs_new_eq:NN \c_iow_log_stream  \c_minus_one
\cs_new_eq:NN \c_ior_log_stream  \c_minus_one
%    \end{macrocode}
% \end{variable}
% \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
  {
    \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_nopar: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_nopar: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_open:Nn, \ior_open:cn}
% \begin{macro}{\iow_open:Nn, \iow_open:cn}
%   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.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \ior_open: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_error:nn { ior } { streams-exhausted } }
      {
        \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_nopar:Npn \iow_open:Nn #1#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_error:nn { iow } { streams-exhausted } }
      {
        \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:
      }
  }
\cs_generate_variant:Nn \ior_open:Nn { c }
\cs_generate_variant:Nn \iow_open:Nn { c }
%    \end{macrocode}
% \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_nopar: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_nopar: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}
% \begin{macro}[aux]{\ior_stream_alloc:N}
% \begin{macro}[aux]{\iow_stream_alloc_aux:}
% \begin{macro}[aux]{\ior_stream_alloc_aux:}
% \begin{variable}{\g_iow_tmp_stream}
% \begin{variable}{\g_ior_tmp_stream}
%   Allocating a raw stream is much easier in \IniTeX{} mode than for
%   the package. For the format, all streams will be allocated by
%   \pkg{l3io} 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}
\cs_new_protected_nopar:Npn \iow_stream_alloc:N #1
  {
    \cs_if_exist:cTF { g_iow_ \int_use:N \l_iow_stream_int _stream }
      { \cs_gset_eq:Nc #1 { g_iow_ \int_use:N \l_iow_stream_int _stream } }
      {
%<*package>
        \iow_stream_alloc_aux:
        \int_compare:nNnT \l_iow_stream_int = \c_sixteen
          {
            \iow_raw_new:N \g_iow_tmp_stream
            \int_set:Nn \l_iow_stream_int { \g_iow_tmp_stream }
            \cs_gset_eq:cN
              { g_iow_ \int_use:N \l_iow_stream_int _stream }
              \g_iow_tmp_stream
          }
%</package>
%<*initex>
        \iow_raw_new:c { g_iow_ \int_use:N \l_iow_stream_int _stream }
%</initex>
        \cs_gset_eq:Nc #1 { g_iow_ \int_use:N \l_iow_stream_int _stream }
      }
  }
%<*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
      {
         \cs_if_exist:cTF { g_iow_ \int_use:N \l_iow_stream_int _stream }
           {
             \prop_if_in:NVT \g_iow_streams_prop \l_iow_stream_int
               { \iow_stream_alloc_aux: }
           }
           { \iow_stream_alloc_aux: }
      }
  }
%</package>
\cs_new_protected_nopar:Npn \ior_stream_alloc:N #1
  {
    \cs_if_exist:cTF { g_ior_ \int_use:N \l_ior_stream_int _stream }
      { \cs_gset_eq:Nc #1 { g_ior_ \int_use:N \l_ior_stream_int _stream } }
      {
%<*package>
        \ior_stream_alloc_aux:
        \int_compare:nNnT \l_ior_stream_int = \c_sixteen
          {
            \ior_raw_new:N \g_ior_tmp_stream
            \int_set:Nn \l_ior_stream_int { \g_ior_tmp_stream }
            \cs_gset_eq:cN
              { g_ior_ \int_use:N \l_iow_stream_int _stream }
              \g_ior_tmp_stream
          }
%</package>
%<*initex>
        \ior_raw_new:c { g_ior_ \int_use:N \l_ior_stream_int _stream }
%</initex>
        \cs_gset_eq:Nc #1 { g_ior_ \int_use:N \l_ior_stream_int _stream }
      }
  }
%<*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 _stream }
           {
             \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{variable}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\iow_close:N, \iow_close:c}
% \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_nopar:Npn \ior_close:N #1
  {
    \cs_if_exist:NT #1
      {
        \int_compare:nNnF #1 = \c_minus_one
          {
            \tex_closein:D #1
            \prop_gdel:NV \g_ior_streams_prop #1
            \cs_undefine:N #1
          }
      }
  }
\cs_new_protected_nopar:Npn \iow_close:N #1
  {
    \cs_if_exist:NT #1
      {
        \int_compare:nNnF #1 = \c_minus_one
          {
            \tex_immediate:D \tex_closeout:D #1
            \prop_gdel:NV \g_iow_streams_prop #1
            \cs_undefine:N #1
          }
      }
  }
\cs_generate_variant:Nn \ior_close:N { c }
\cs_generate_variant:Nn \iow_close:N { c }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ior_list_streams:}
% \begin{macro}{\iow_list_streams:}
% \begin{macro}[aux]{\iow_show_aux:nn}
% \begin{macro}[aux]{\ior_show_aux:nn}
% Show the property lists, but with some \enquote{pretty printing}.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \ior_list_streams:
  {
    \prop_if_empty:NTF \g_ior_streams_prop
      {
        \iow_term:x { No~input~streams~are~open }
        \tl_show:n { }
      }
      {
        \iow_term:x { The~following~input~streams~are~in~use: }
        \tl_set:Nx \l_prop_show_tl
          { \prop_map_function:NN \g_ior_streams_prop \ior_show_aux:nn }
          \etex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
            { \exp_after:wN \prop_show_aux:w \l_prop_show_tl }
      }
  }
\cs_new:Npn \ior_show_aux:nn #1#2
  {
    \iow_newline: > \c_space_tl \c_space_tl
    #1 \c_space_tl \c_space_tl => \c_space_tl \c_space_tl \exp_not:n {#2}
  }
\cs_new_protected_nopar:Npn \iow_list_streams:
  {
    \prop_if_empty:NTF \g_iow_streams_prop
      {
        \iow_term:x { No~output~streams~are~open }
        \tl_show:n { }
      }
      {
        \iow_term:x { The~following~output~streams~are~in~use: }
        \tl_set:Nx \l_prop_show_tl
          { \prop_map_function:NN \g_iow_streams_prop \iow_show_aux:nn }
          \etex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
            { \exp_after:wN \prop_show_aux:w \l_prop_show_tl }
      }
  }
\cs_new_eq:NN \iow_show_aux:nn \ior_show_aux:nn
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \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}{\iow_shipout:Nn, \iow_shipout:Nx}
%   With \eTeX{} available deferred writing is easy.
%    \begin{macrocode}
\cs_new_protected_nopar: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_nopar: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_iow_log_stream  }
\cs_new_protected_nopar:Npn \iow_log:n  { \iow_now:Nn \c_iow_log_stream  }
\cs_set_protected_nopar:Npn \iow_term:x { \iow_now:Nx \c_iow_term_stream }
\cs_new_protected_nopar:Npn \iow_term:n { \iow_now:Nn \c_iow_term_stream }
%    \end{macrocode}
%\end{macro}
%\end{macro}
%
% \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_nopar:Npn \iow_now_when_avail:Nn #1
  { \cs_if_free:NTF #1 { \use_none:n } { \iow_now:Nn #1 } }
\cs_new_protected_nopar:Npn \iow_now_when_avail:Nx #1
  { \cs_if_free:NTF #1 { \use_none:n } { \iow_now:Nx #1 } }
%    \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}
%   The is the \enquote{raw} length of a line which can be written to
%   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}
%   These store the number of characters in the line and word currently
%   being constructed, respectively.
%    \begin{macrocode}
\int_new:N \l_iow_current_line_int
\int_new:N \l_iow_current_word_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\l_iow_current_line_tl, \l_iow_current_word_tl}
%   These hold the current line of text and current word, respectively.
%    \begin{macrocode}
\tl_new:N \l_iow_current_line_tl
\tl_new:N \l_iow_current_word_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}{\q_iow_stop}
%   A quark which will not appear elsewhere.
%    \begin{macrocode}
\quark_new:N \q_iow_stop
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_iow_line_start_bool}
%   Boolean to avoid adding a space at the beginning of lines.
%    \begin{macrocode}
\bool_new:N \l_iow_line_start_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\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}{\iow_wrap:xnnnN}
% \begin{macro}[aux]{\iow_wrap_loop:w}
% \begin{macro}[aux]{\iow_wrap_word:}
% \begin{macro}[aux]{\iow_wrap_word_fits:}
% \begin{macro}[aux]{\iow_wrap_word_newline:}
% \begin{macro}[aux]{\iow_wrap_newline:}
% \begin{macro}[aux]{\iow_wrap_end:}
%   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_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
      \cs_set:Npx \\
        { \c_catcode_other_space_tl \iow_newline: \c_catcode_other_space_tl }
      \cs_set_eq:NN \  \c_catcode_other_space_tl
      #4
%<*initex>
      \tl_set:Nx \l_iow_wrap_tl {#1}
%</initex>
%<*package>
      \protected@edef \l_iow_wrap_tl {#1}
%</package>
      \cs_set:Npn \\ { \iow_newline: #2 }
      \use:x
        {
          \exp_not:N \iow_wrap_loop:w
          \tl_to_str:N \l_iow_wrap_tl \c_space_tl
          \exp_not:N \q_iow_stop \c_space_tl
        }
    \exp_args:NNo \group_end:
    #5 \l_iow_wrapped_tl
  }
%    \end{macrocode}
% The loop grabs one word in the input, and checks whether it is
% the end, or a forced new line, or a normal word.
%    \begin{macrocode}
\cs_new_protected:Npn \iow_wrap_loop:w #1 ~ %
  {
    \tl_set:Nn \l_iow_current_word_tl {#1}
    \tl_if_eq:NNTF \l_iow_current_word_tl \iow_newline:
      { \iow_wrap_newline: }
      {
        \tl_if_eq:NNTF \l_iow_current_word_tl \q_iow_stop
          { \iow_wrap_end: }
          { \iow_wrap_word: }
      }
  }
%    \end{macrocode}
% 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.
%    \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
  }
%    \end{macrocode}
% 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.
%    \begin{macrocode}
\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_set_eq:NN \l_iow_current_line_tl \l_iow_current_word_tl
      }
      {
        \tl_put_right:Nx \l_iow_current_line_tl
          { ~ \l_iow_current_word_tl }
        \int_incr:N \l_iow_current_line_int
      }
  }
%    \end{macrocode}
% 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_newline:
  {
    \tl_put_right:Nx \l_iow_wrapped_tl
      { \l_iow_current_line_tl \\ }
    \int_set_eq:NN \l_iow_current_line_int \l_iow_current_word_int
    \tl_set_eq:NN  \l_iow_current_line_tl  \l_iow_current_word_tl
  }
%    \end{macrocode}
% Forced newlines are almost identical to those caused by overflow,
% except that here the word is empty. And remember to continue the loop!
%    \begin{macrocode}
\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
    \iow_wrap_loop:w
  }
%    \end{macrocode}
% At the end, we simply save the last line (without the run-on text).
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \iow_wrap_end:
  {
    \tl_put_right:Nx \l_iow_wrapped_tl
      { \l_iow_current_line_tl }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\str_length_skip_spaces:N}
% \begin{macro}{\str_length_skip_spaces:n}
% \begin{macro}{\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{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{Reading input}
%
% \begin{macro}[pTF]{\ior_if_eof_p:N}
% To test if some particular input stream is exhausted the following
% conditional is provided. As the pool model means that closed
% streams are undefined control sequences, the test has two parts.
%    \begin{macrocode}
\prg_new_conditional:Nnn \ior_if_eof:N { p , T , F , TF }
  {
    \cs_if_exist:NTF #1
      {
        \if_eof:w #1
          \prg_return_true:
        \else:
          \prg_return_false:
        \fi:
      }
      { \prg_return_true: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ior_to:NN}
% \begin{macro}{\ior_gto:NN}
%  And here we read from files.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \ior_to:NN #1#2
  { \tex_read:D #1 to #2 }
\cs_new_protected_nopar:Npn \ior_gto:NN #1#2
  { \tex_global:D \tex_read:D #1 to #2 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ior_str_to:NN}
% \begin{macro}{\ior_str_gto:NN}
%  Reading as strings is also a primitive wrapper.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \ior_str_to:NN #1#2
  { \etex_readline:D #1 to #2 }
\cs_new_protected_nopar:Npn \ior_str_gto:NN #1#2
  { \tex_global:D \etex_readline:D #1 to #2 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Deprecated functions}
%
% 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_new:N, \ior_new:c}
% \begin{macro}{\iow_new:N, \iow_new:c}
%   As input--output operations are done using a stack, |new| operations seem
%   out-of-place. They are therefore set up just to gobble the input.
%    \begin{macrocode}
%<*deprecated>
\cs_new_eq:NN \ior_new:N \use_none:n
\cs_new_eq:NN \ior_new:c \use_none:n
\cs_new_eq:NN \iow_new:N \use_none:n
\cs_new_eq:NN \iow_new:c \use_none:n
%</deprecated>
%    \end{macrocode}
% \end{macro}
% \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>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
