summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3io.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3io.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3io.dtx1185
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