diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3io.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3io.dtx | 1185 |
1 files changed, 1185 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3io.dtx b/Master/texmf-dist/source/latex/l3kernel/l3io.dtx new file mode 100644 index 00000000000..bf71c3df878 --- /dev/null +++ b/Master/texmf-dist/source/latex/l3kernel/l3io.dtx @@ -0,0 +1,1185 @@ +% \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 "expl3 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 2478 2011-06-19 21:34:23Z joseph $ + {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 \cs{immediate}\cs{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 \cs{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}{\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} +% +% \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 \cs{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 +% \cs{global} \cs{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 +% \cs{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 +% \cs{global} \cs{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. The branching versions then +% leave either \meta{true code} or \meta{false code} in the input +% stream, as appropriate to the truth of the test and the variant of +% the function chosen. The logical truth of the test is left in the +% input stream by the predicate version. +%\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 \cs{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:NnNN g { 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:NnNN g { 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 \iow_char:N + \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}{\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. +% \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_space_tl \iow_newline: \c_space_tl } + \cs_set_eq:NN \ \c_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 + { \pref_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 + { \pref_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} +\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 } +% \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} +\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 +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ior_open_streams:} +% \begin{macro}{\iow_open_streams:} +% Slightly misleading names. +% \begin{macrocode} +\cs_new_eq:NN \ior_open_streams: \ior_list_streams: +\cs_new_eq:NN \iow_open_streams: \iow_list_streams: +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macrocode} +%</initex|package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |