diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/morewrites/morewrites.dtx |
Initial commit
Diffstat (limited to 'macros/latex/contrib/morewrites/morewrites.dtx')
-rw-r--r-- | macros/latex/contrib/morewrites/morewrites.dtx | 1289 |
1 files changed, 1289 insertions, 0 deletions
diff --git a/macros/latex/contrib/morewrites/morewrites.dtx b/macros/latex/contrib/morewrites/morewrites.dtx new file mode 100644 index 0000000000..6e52eff394 --- /dev/null +++ b/macros/latex/contrib/morewrites/morewrites.dtx @@ -0,0 +1,1289 @@ +% \iffalse meta-comment +% +%% File: morewrites.dtx Copyright (C) 2011-2018 Bruno Le Floch +%% +%% 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 work has the LPPL maintenance status 'maintained' +%% and the current maintainer is Bruno Le Floch. +%% ----------------------------------------------------------------------- +% +%<*driver> +\RequirePackage{morewrites} +\ExplSyntaxOn +\prg_replicate:nn { 300 } { \newwrite \foo } +\ExplSyntaxOff +\documentclass[full]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{The \textsf{morewrites} package: \\ +% Always room for a new \tn{write}} +% \author{Bruno Le Floch} +% \date{2018/12/29} +% +% \maketitle +% \tableofcontents +% +% \begin{documentation} +% +% \section{\pkg{morewrites} documentation} +% +% This \LaTeX{} package is a solution for the error +% \enquote{no room for a new \tn{write}}, +% which occurs when a document reserves too many streams to write data +% to various auxiliary files. +% It is in principle possible to rewrite other packages so that they +% are less greedy on resources, but that is often unpractical for the +% end-user. Instead, \pkg{morewrites} hooks at the lowest level (\TeX{} +% primitives). +% +% Simply add the line |\usepackage{morewrites}| near the beginning of +% your \LaTeX{} file's preamble: the \enquote{no room for a new +% \tn{write}} error should vanish. If it does not, please +% contact me so that I can correct the problem. This can be done by +% posting a question on the \url{tex.stackexchange.com} question and +% answers website, logging an issue on GitHub +% (\url{https://github.com/blefloch/latex-morewrites}), or emailing me a +% minimal file showing the problem. +% +% Notes. +% \begin{itemize} +% \item This package loads the \pkg{expl3} package, hence the +% \pkg{l3kernel} bundle needs to be up to date. +% \item This package uses an auxiliary file, \meta{job~name}|.mw|, +% which can safely be deleted. Versions from 2015 and later will only +% use the auxiliary file if it is originally empty, to avoid +% destroying data (such as |.mw| files used by Maple). This means +% that |.mw| files generated by versions before 2015 should be +% deleted by hand. +% \item \LuaTeX{} allows $128$ \tn{write} streams, so this package does +% nothing (with a warning) when used with \LuaTeX{}. +% \end{itemize} +% +% \subsection{Commands defined or altered by \pkg{morewrites}} +% +% \begin{function}[added = 2014-07-26]{\morewritessetup} +% \begin{syntax} +% \cs{morewritessetup} \Arg{key--value list} +% \end{syntax} +% Sets the options described by the \meta{key--value list}. +% \end{function} +% +% \begin{function}[added = 2017-04-10]{allocate} +% \begin{syntax} +% \cs{morewritessetup} |{| |allocate| |=| \meta{integer} |}| +% \end{syntax} +% Sets to (at least) \meta{integer} the number of \tn{write} streams +% allocated to the inner workings of \pkg{morewrites}. By default +% this is zero but increasing this value to 10 (or so) may speed up +% \pkg{morewrites}. +% \end{function} +% +% \begin{function}[added = 2014-07-26, updated = 2015-08-01]{file} +% \begin{syntax} +% \cs{morewritessetup} |{| |file| |=| \meta{file name} |}| +% \end{syntax} +% Sets (globally) the name of the file which will be used by internal +% processes of \pkg{morewrites}. The file name is \tn{jobname}|.mw| +% by default (technically, \cs{c_sys_jobname_str}|.mw|). Contrarily +% to earlier versions of \pkg{morewrites} non-empty files will not be +% overwritten; this design choice may lead to unwanted |.mw| files +% remaining. +% \end{function} +% +% \begin{function}[updated = 2015-08-01]{\newwrite} +% This macro is redefined by \pkg{morewrites}. Since \pkg{morewrites} +% allows more than~$16$ write streams, it removes the corresponding +% restrictions in \tn{newwrite}. +% \begin{texnote} +% The revised \tn{newwrite} allocate stream numbers starting at +% $19$. This might break some code that expects stream numbers to +% be less than $16$. +% \end{texnote} +% \end{function} +% +% \begin{function}[updated = 2015-08-01]{\immediate} +% This primitive is altered by \pkg{morewrites}, to detect a following +% \tn{write} or \tn{openout} or \tn{closeout} and perform the +% appropriate action. +% \end{function} +% +% \begin{function}[updated = 2017-04-20]{\openout, \write, \closeout} +% These three primitives are altered by \pkg{morewrites} so that they +% accept stream numbers outside the normal range $[0,15]$ and +% open/write/close files as appropriate. +% \begin{texnote} +% System calls using \tn{write}|18| are detected and forwarded to the engine. +% \end{texnote} +% \end{function} +% +% \begin{function}{\shipout} +% This primitive is altered by \pkg{morewrites} to ensure that delayed +% \tn{openout}, \tn{write} and \tn{closeout} commands are performed at +% \tn{shipout} time, and in the correct order. +% \end{function} +% +% \subsection{Known deficiencies and open questions} +% +% See the bug tracker \url{https://github.com/blefloch/latex-morewrites/issues/} +% for a list of issues with \pkg{morewrites}. +% +% The package code is not good \pkg{expl3} code. \emph{Do not take this +% package as an example of how to code with \pkg{expl3}; go and see +% Joseph Wright's \pkg{siunitx} instead.} It uses +% \cs[no-index]{\ldots{}:D} primitives directly (the |:D| stands for +% \enquote{do not use}). This is unavoidable in order to hook into the +% primitives \tn{immediate}, \tn{write}, \emph{etc.\@} and to keep a +% very strong control on what every command does. +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{morewrites} implementation} +% +%<*package> +% \begin{macrocode} +\RequirePackage {expl3} [2018/02/21] +\RequirePackage {primargs} [2018/12/29] +\ProvidesExplPackage + {morewrites} {2018/12/29} {} {Always room for a new write} +% \end{macrocode} +% +% Quit early under \LuaTeX{}. +% \begin{macrocode} +\sys_if_engine_luatex:T + { + \cs_new_protected:Npn \morewritessetup #1 { } + \msg_new:nnn { morewrites } { luatex } + { The~morewrites~package~is~unnecessary~in~LuaTeX. } + \msg_warning:nn { morewrites } { luatex } + \tex_endinput:D + }% +% \end{macrocode} +% +% \begin{macrocode} +%<@@=morewrites> +% \end{macrocode} +% +% \subsection{Overview of relevant \TeX{} facts} +% +% The aim of the \pkg{morewrites} package is to lift \TeX{}'s +% restriction of only having $16$ files open for writing at the same +% time. This requires patching the primitives \tn{immediate}, +% \tn{openout}, \tn{write}, \tn{closeout}, and \tn{shipout}, and the +% macro \tn{newwrite} present in plain \TeX{} and \LaTeXe{}. +% +% Note that doing the same for \tn{read} streams is impossible due to +% the \tn{ifeof} primitive: that primitive cannot be replaced by a macro +% without breaking nesting of conditionals. +% +% The \pkg{morewrites} package should be loaded as early as possible, so +% that any package loaded later uses the redefined macros instead of the +% primitives. However, the format (plain \TeX{} or \LaTeXe{}) and the +% \pkg{expl3} programming language are always loaded before +% \pkg{morewrites}, and their interaction must be carefully monitored. +% +% Henceforth, ``\TeX{} stream'' will refer to stream numbers in the +% range $[0,15]$ provided to \TeX{}'s write primitives, while +% ``user stream'' will denote stream numbers in $[0,15]\cup[19,\infty)$ +% manipulated by the redefined \tn{openout}, \tn{write}, \tn{closeout}, +% and \tn{newwrite}. A user stream in $[0,15]$ (reserved by \LaTeXe{} +% or allocated by \pkg{expl3}) is mapped to the same \TeX{} stream +% number, while a user stream in $[19,\infty)$ is mapped to a \TeX{} +% stream according to the property list (with integer keys and values) +% \cs{l_@@_write_prop}. Stream numbers $16$, +% $17$ and $18$ are unused because \tn{write}16 is often used to write +% to the terminal, and \tn{write}18 sends its argument to a shell. +% +% The primitives \tn{openout}, \tn{write}, and \tn{closeout} expect to +% be followed by an \meta{integer}, normally in the range $[0,15]$, then +% some further arguments. +% \begin{quote} +% \tn{openout} \meta{integer} \meta{equals} \meta{file name} \\ +% \tn{write} \meta{integer} \meta{filler} \meta{general text} \\ +% \tn{closeout} \meta{integer} +% \end{quote} +% All of the primitives above perform full expansion of all tokens when +% looking for their operands. +% \begin{itemize} +% \item \meta{integer} denotes an integer in any form that \TeX{} +% accepts as the right-hand side of a primitive integer assignment of +% the form \tn{count}|0=|\meta{integer}; +% \item \meta{equals} is an arbitrary (optional) number of explicit or +% implicit space characters, an optional explicit equal sign of +% category other, and further (optional) explicit or implicit space +% characters; +% \item \meta{file name} is an arbitrary sequence of explicit or implicit +% characters with arbitrary category codes (except active characters, +% which are expanded before reaching \TeX{}'s mouth), ending either +% with a space character (character code $32$, arbitrary non-active +% category code, explicit or implicit), which is removed, or with a +% non-expandable token, with some care needed for the case of a +% \tn{notexpanded:} expandable token; +% \item \meta{filler} is an arbitrary combination of tokens whose +% meaning is \tn{relax} or whose category code is $10$; +% \item \meta{general text} is formed of braced tokens, starting with an +% explicit or implicit begin-group character, and ending with the +% matching explicit end-group character (both with any character +% code), with an equal number of explicit begin-group and end-group +% characters in between: this is precisely the right-hand side of an +% assignment of the form \tn{toks}|0=|\meta{general text}. +% \end{itemize} +% +% The \pkg{morewrites} package redefines these three control sequences +% to expect a user stream number rather than a \TeX{} stream number as +% the \meta{integer}, then map such a user stream to a \TeX{} stream to +% call the primitive with the appropriate argument. The primitive +% \tn{immediate} must also be redefined to detect \tn{openout}, +% \tn{write}, and \tn{closeout} and make them immediate, while still +% working with other primitives that can be made immediate. Finally, +% \tn{newwrite} must be patched to allocate stream numbers beyond $15$. +% +% A few comments on the behaviour of primitives concerning the +% \meta{integer} (\TeX{} stream). The \tn{openout} primitive trigger +% errors if the \meta{integer} is not in $[0,15]$. The primitive +% \tn{write} outputs to the log if the \meta{integer} is negative, and +% to the terminal if the \TeX{} stream is closed or greater than $15$, +% with the exception of \tn{write}|18| which runs code in a shell. The +% \tn{closeout} primitive triggers an error if the \meta{integer} is not +% in $[0,15]$ and silently do nothing if the \TeX{} stream is not open, +% with the exception of \tn{closeout}|18| which causes a segfault at +% least in some versions. +% +% By default, \tn{openout}, \tn{write} and \tn{closeout} are recorded in +% a whatsit node in the current list, and will be performed when the box +% containing the whatsit node is sent to the final \texttt{pdf}, +% \emph{i.e.}, at \enquote{shipout} time. In particular, the +% \meta{general text} for the \tn{write} primitive is expanded at +% shipout time. This behaviour may be modified by putting +% \tn{immediate} before any of these three primitives to force \TeX{} to +% perform the action immediately instead of recording it in a whatsit +% node. +% +% Since the \tn{openout}, \tn{write}, and \tn{closeout} primitives +% operate at \tn{shipout} time, we will have to hook into this primitive +% too. It expects to be followed by a box specification, for instance +% \tn{box}\meta{integer} or \tn{hbox}\Arg{material to typeset}. +% +% Finally, the \tn{newwrite} macro expects one token as its argument, +% and defines this token (with \tn{chardef}) to be an integer +% corresponding to the first available (\TeX{}) write stream. This must +% be extended to allocate higher (user) streams. +% +% \subsection{Preliminaries} +% +% \subsubsection{Copying some commands} +% +% \begin{macro} +% { +% \@@_tex_immediate:w , +% \@@_tex_openout:w , +% \@@_tex_write:w , +% \@@_tex_closeout:w , +% } +% Aliases for the write-related primitives, to avoid +% having |:D| throughout the code. +% \begin{macrocode} +\cs_new_eq:NN \@@_tex_immediate:w \tex_immediate:D +\cs_new_eq:NN \@@_tex_openout:w \tex_openout:D +\cs_new_eq:NN \@@_tex_write:w \tex_write:D +\cs_new_eq:NN \@@_tex_closeout:w \tex_closeout:D +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tex_newwrite:N} +% Copy \tn{newwrite} but making sure that it is +% not \tn{outer}. This copy will not be affected by redefinitions +% of \tn{newwrite} later on. +% \begin{macrocode} +\exp_args:NNf \cs_new_protected:Npn \@@_tex_newwrite:N + { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Variants} +% +% \begin{macro}{\prop_gpop:NVNT, \prop_gput:NVx, \tl_gput_right:Nv} +% We need these variants. +% \begin{macrocode} +\cs_generate_variant:Nn \prop_gpop:NnNT { NV } +\cs_generate_variant:Nn \prop_gput:Nnn { NVx } +\cs_generate_variant:Nn \tl_gput_right:Nn { Nv } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Variables} +% +% \begin{variable}{\l_@@_internal_tl} +% Used for temporary scratch purposes. +% \begin{macrocode} +\tl_new:N \l_@@_internal_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\@@_tmp:w} +% Used for temporary definitions. +% \begin{macrocode} +\cs_new_eq:NN \@@_tmp:w ? +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_later_int} +% The integer \cs{g_@@_later_int} labels the various +% non-immediate operations in the order in which they appear in the +% source. We can never reuse a number because there is no way to know +% if a whatsit was recorded in a box register, which could be reused +% in a shipped-out box: +% \begin{quote} +% \cs{vbox_set:Nn} \cs{l_my_box} \\ +% ~~|{| \cs{iow_shipout_x:Nn} \cs{c_term_iow} \Arg{text} |}| +% \tn{shipout} \tn{copy} \cs{l_my_box} +% \tn{shipout} \tn{copy} \cs{l_my_box} +% \end{quote} +% will print \meta{text} to the terminal twice. +% \begin{macrocode} +\int_new:N \g_@@_later_int +% \end{macrocode} +% \end{variable} +% +% ^^A todo: populate \g_@@_write_seq because we don't use \newwrite; this should be done in \morewritessetup +% \begin{variable}{\g_@@_write_seq} +% Keep track of \TeX{} stream numbers managed by \pkg{morewrites} that +% are currently not in use as user streams. +% \begin{macrocode} +\seq_new:N \g_@@_write_seq +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_write_prop} +% Map user streams to \TeX{} streams. +% \begin{macrocode} +\prop_new:N \g_@@_write_prop +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_write_file_prop} +% Map user streams with no associated \TeX{} streams to file names. +% \begin{macrocode} +\prop_new:N \g_@@_write_file_prop +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_code_tl} +% Stores the code to run after finding a user stream, in +% \cs{@@_get_user:n}. +% \begin{macrocode} +\tl_new:N \l_@@_code_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_user_int, \l_@@_tstr_tl} +% The user stream number following redefined primitives is stored in +% \cs{l_@@_user_int} (see \cs{@@_get_user:N}). The corresponding +% \TeX{} stream number is eventually stored in \cs{l_@@_tstr_tl} (a +% token list). +% \begin{macrocode} +\int_new:N \l_@@_user_int +\tl_new:N \l_@@_tstr_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_tstr_token} +% This token is given as an argument to \cs{@@_tex_newwrite:N}. +% \begin{macrocode} +\cs_new_eq:NN \l_@@_tstr_token ? +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\s_@@} +% A recognizable version of \cs{scan_stop:}. This is inspired +% by\footnote{Historically, this might have happened the other way +% around, since the author of this package is also on the \LaTeX3 +% Team.} scan marks (see the \pkg{l3quark} module of \LaTeX3), but +% \cs{scan_new:N} is not used directly, since it is has been made +% available in \LaTeX3 too recently. +% \begin{macrocode} +\cs_new_eq:NN \s_@@ \scan_stop: +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\g_@@_iow, \g_@@_ior} +% The expansion that \tn{write} performs is impossible to emulate (in \XeTeX{} at least) with +% anything else than \tn{write}. We will write on the stream +% \cs{g_@@_iow} to the file \cs{g_@@_tmp_file_tl} and read back from +% it in the stream \cs{g_@@_ior} for things to work properly. +% Unfortunately, this means that the file is repeatedly opened and +% closed, leaving a trace of that in the log. +% \begin{macrocode} +\newwrite \g_@@_iow +\newread \g_@@_ior +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_tmp_file_tl, \g_@@_tmp_file_bool} +% Temporary file used to do the correct expansion for each \tn{write}. +% Boolean indicating whether we have already checked that the file can +% be used by \pkg{morewrites}: before using a file, the +% \pkg{morewrites} package now checks it is empty, so as to avoid +% clobbering user data. +% \begin{macrocode} +\tl_new:N \g_@@_tmp_file_tl +\bool_new:N \g_@@_tmp_file_bool +\bool_gset_false:N \g_@@_tmp_file_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_group_level_int} +% The group level when \tn{shipout} is called: this is used to +% distinguish between explicit boxes and box registers. +% \begin{macrocode} +\int_new:N \g_@@_group_level_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_shipout_box} +% The page to be shipped out. +% \begin{macrocode} +\box_new:N \g_@@_shipout_box +% \end{macrocode} +% \end{variable} +% +% \subsubsection{Helpers for auxiliary file} +% +% \begin{macro}{\@@_set_file:n} +% Sets \cs{g_@@_tmp_file_tl} to the given value (initially +% \cs{c_sys_jobname_str}|.mw|). Mark that the file has not been +% checked. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_file:n #1 + { + \bool_gset_false:N \g_@@_tmp_file_bool + \tl_gset:Nn \g_@@_tmp_file_tl {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_empty_file:n} +% Empties the file \cs{g_@@_tmp_file_tl} by opening it and closing it +% right away. This is used when performing \tn{immediate} +% \tn{openout}. It is also used to ensure the file used by +% \pkg{morewrites} is left empty. We do this every time the auxiliary +% file is used, in case that run ends with an error mid-document. +% \begin{macrocode} +\cs_new_protected:Npn \@@_empty_file:n #1 + { + \@@_tex_immediate:w \@@_tex_openout:w + \g_@@_iow = #1 \scan_stop: + \@@_tex_immediate:w \@@_tex_closeout:w + \g_@@_iow + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_file_trivial:n} +% True if the file does not exist, or if it is empty. +% Only the \texttt{TF} variant is defined. We set +% \cs{@@_tmp:w} to \cs{prg_return_true:} or \cs{prg_return_false:} +% within the group and use it after cleaning up. The first +% \texttt{eof} test is \texttt{true} if the file does not exist. +% Then we read one line, the second \texttt{eof} test is +% \texttt{true} if the file was empty (it is \texttt{false} if +% the file contained anything, even a single space). +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_file_trivial:n #1 { TF } + { + \group_begin: + \tex_openin:D \g_@@_ior = #1 \scan_stop: + \if_eof:w \g_@@_ior + \cs_gset_eq:NN \@@_tmp:w \prg_return_true: + \else: + \int_set:Nn \tex_endlinechar:D { -1 } + \tex_readline:D \g_@@_ior to \l_@@_internal_tl + \if_eof:w \g_@@_ior + \cs_gset_eq:NN \@@_tmp:w \prg_return_true: + \else: + \cs_gset_eq:NN \@@_tmp:w \prg_return_false: + \fi: + \fi: + \tex_closein:D \g_@@_ior + \group_end: + \@@_tmp:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_chk_file:} +% Check that the file \cs{g_@@_tmp_file_tl} does not exist or is blank. +% If not, try the file name obtained by adding |.mw|. +% This avoids clobbering files that the user would not want to lose. +% \begin{macrocode} +\cs_new_protected:Npn \@@_chk_file: + { + \@@_if_file_trivial:nTF { \g_@@_tmp_file_tl } + { \bool_gset_true:N \g_@@_tmp_file_bool } + { + \msg_warning:nnxx { morewrites } { file-exists } + { \g_@@_tmp_file_tl } + { \g_@@_tmp_file_tl .mw } + \tl_gput_right:Nn \g_@@_tmp_file_tl { .mw } + \@@_chk_file: + } + } +\msg_new:nnnn { morewrites } { file-exists } + { File~'#1'~exists,~using~'#2'~instead. } + { + The~file~`#1'~exists~and~was~not~created~by~this~version~of~the~ + `morewrites'~package.~Please~move~or~delete~that~file,~or~provide~ + another~file~name~by~adding + \\ \\ + \iow_indent:n { \iow_char:N\\morewritessetup~{~file~=~other-name~} } + \\ \\ + to~your~source~file.~In~the~meantime,~the~file~`#2'~will~be~used. + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Parsing and other helpers} +% +% \begin{macro}{\@@_equals_file:N} +% Most of the parsing for primitive arguments is done using +% \pkg{primargs}, except for one case we care about: after its +% \meta{number} argument, the \tn{openout} primitive expects an +% \meta{equals} (optional spaces and |=|) and a \meta{file name}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_equals_file:N #1 + { + \group_begin: + \tex_aftergroup:D \primargs_get_file_name:N + \tex_aftergroup:D #1 + \primargs_remove_equals:N \group_end: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_user:n} +% \pkg{primargs} commands only take \texttt{N}-type arguments, but we +% often need to find an integer, save it in \cs{l_@@_user_int}, and +% run some code |#1|. This is analogous to \cs{primargs_get_number:N}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_user:n #1 + { + \tl_set:Nn \l_@@_code_tl {#1} + \tex_afterassignment:D \l_@@_code_tl + \l_@@_user_int = + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_user_to_tstr:NTF} +% The goal is to go from a user stream \cs{l_@@_user_int} to a \TeX{} +% stream \cs{l_@@_tstr_tl} (it defaults to the user stream). Streams +% less than $19$ are not managed by \pkg{morewrites}: actual \TeX{} +% streams in $[0,15]$; negative for writing to \texttt{log}; $16$, +% $17$ for writing to terminal; $18$ for shell escape. Larger stream +% numbers are looked up in the property list |#1|, namely +% \cs{g_@@_write_prop}. If present, use the corresponding value as +% the \TeX{} stream, otherwise run the \texttt{false} branch. +% \begin{macrocode} +\cs_new_protected:Npn \@@_user_to_tstr:NTF #1 + { + \tl_set:NV \l_@@_tstr_tl \l_@@_user_int + \int_compare:nNnTF { \l_@@_user_int } < { 19 } + { \use_i:nn } + { \prop_get:NVNTF #1 \l_@@_user_int \l_@@_tstr_tl } + } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_collect_next_int} +% \begin{macro} +% { +% \@@_collect:x, \@@_collect_aux:Nn, +% \@@_collect_aux:cf, \@@_collect_gput_right:N, \@@_collect_gput_right:c +% } +% When encountering very large \tn{write} statements we may need to +% collect many lines. This can easily become an $O(n^2)$ task, and +% here we make sure that it remains around $O(n\log n)$, with a large +% constant unfortunately. Each of the token lists +% \cs[no-index]{l_@@_$k$_tl} is empty or contains $2^k$ lines. As +% lines accumulate, they move to token lists with larger values +% of~$k$, and eventually all are combined. The integer +% \cs{l_@@_collect_next_int} is (one plus) the maximal $k$ among +% non-empty token lists. +% \begin{macrocode} +\int_new:N \l_@@_collect_next_int +\cs_new_protected:Npn \@@_collect:x #1 + { + \tl_set:Nx \l_@@_internal_tl {#1} + \@@_collect_aux:cf { l_@@_0_tl } { 1 } + } +\cs_new_protected:Npn \@@_collect_aux:Nn #1#2 + { + \int_compare:nNnT {#2} > \l_@@_collect_next_int + { + \tl_clear_new:N #1 + \int_set:Nn \l_@@_collect_next_int {#2} + } + \tl_if_empty:NTF #1 + { \tl_set_eq:NN #1 \l_@@_internal_tl } + { + \tl_put_left:No \l_@@_internal_tl {#1} + \tl_clear:N #1 + \@@_collect_aux:cf { l_@@_#2_tl } + { \int_eval:n { #2 + 1 } } + } + } +\cs_generate_variant:Nn \@@_collect_aux:Nn { cf } +\cs_new_protected:Npn \@@_collect_gput_right:N #1 + { + \int_compare:nNnF \l_@@_collect_next_int = 0 + { + \int_decr:N \l_@@_collect_next_int + \tl_gput_right:Nv #1 + { + l_@@_ + \int_use:N \l_@@_collect_next_int + _tl + } + \@@_collect_gput_right:N #1 + } + } +\cs_generate_variant:Nn \@@_collect_gput_right:N { c } +% \end{macrocode} +% \end{macro} +% \end{variable} +% +% \begin{macro}[EXP]{\@@_user_tl_name:n} +% The name of a global token list variable holding the text of a given +% user stream. +% \begin{macrocode} +\cs_new:Npn \@@_user_tl_name:n #1 + { g_@@_iow_ \int_eval:n {#1} _tl } +% \end{macrocode} +% \end{macro} +% +% \subsection{Writing} +% +% We can hold on to material while a file is being written and only +% write it in one go once the file closes, to avoid using a stream +% throughout. +% +% At any given time, each user stream may point to an open \TeX{} +% stream, given in \cs{g_@@_write_prop}, or may point to a token list +% that will eventually be written to a file whose file name is stored in +% \cs{g_@@_write_file_prop}, or may be closed. +% +% When a user stream points to a token list rather than a \TeX{} stream, +% any material to be written must be written to our temporary file and +% read back in to apply the same expansion as \tn{write} does. +% +% Another difficulty is that users may mix immediate and non-immediate +% operations. The biggest difficulty comes from the possibility of +% copying boxes containing delayed actions. If we ever produced a +% whatsit \tn{write}\meta{number}\Arg{text} then the \TeX{} stream +% \meta{number} would have to be reserved forever, as as copies of the +% box containing this delayed actions may be shipped out at any later +% point in the document. +% +% Each delayed action is thus saved in a separate numbered token list +% and \tn{write}\cs{g_@@_iow}\Arg{number} is inserted instead of the +% delayed action. At each \tn{shipout}, the stream \cs{g_@@_iow} is +% opened, to catch the \meta{number} of each action that should be +% performed at this \tn{shipout}. +% +% \subsubsection{Redefining \tn{immediate}} +% +% To accomodate the \tn{immediate} primitive, our versions of +% \tn{openout}, \tn{write} and \tn{closeout} will take the form +% \begin{quote} +% \cs{s_@@} \cs{use_i:nn} +% \quad \Arg{code for delayed action} \\ +% \quad \Arg{code for immediate action} \\ +% \meta{further code} +% \end{quote} +% The leading \cs{s_@@} allows the redefined \tn{immediate} to detect +% these redefined primitives, and to run the \meta{code for immediate +% action} instead of the \meta{code for delayed action} which is run by +% default. In both cases, any \meta{further code} is run. +% +% \begin{macro}[updated = 2012-12-05]{\@@_immediate:w} +% \begin{macro}{\@@_immediate_auxii:, \@@_immediate_auxiii:N} +% \TeX{}'s \tn{immediate} primitive raises a flag which is cancelled +% after \TeX{} sees a non-expandable token. We use +% \cs{primargs_read_x_token:N} to find the next non-expandable token +% then test for \tn{openout}, \tn{write}, and \tn{closeout}. More +% precisely we test for the marker \cs{s_@@} and run the appropriate +% code as described above. Otherwise we call the primitive, for cases +% where the next token is \tn{pdfobj} or similar. This code performs +% too much expansion for some nonsensical uses of \tn{noexpand} after +% \tn{immediate}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_immediate:w + { \primargs_read_x_token:N \@@_immediate_auxii: } +\cs_new_protected:Npn \@@_immediate_auxii: + { + \token_if_eq_meaning:NNTF \g_primargs_token \s_@@ + { \@@_immediate_auxiii:N } + { \@@_tex_immediate:w } + } +\cs_new_protected:Npn \@@_immediate_auxiii:N #1 + { \str_if_eq:nnTF { #1 } { \s_@@ } { \use_iii:nnn } { #1 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsubsection{Immediate actions} +% +% The \tn{openout}, \tn{write}, and \tn{closeout} primitive can be +% either delayed or immediate. In all cases they begin by looking for a +% user stream. Here, we implement the immediate versions only. +% +% \begin{macro}{\@@_closeout:w, \@@_closeout_now:, \@@_closeout_now:nn} +% In the immediate case \cs{@@_closeout_now:}, there are three cases. +% The stream may point to a \TeX{} stream, in which case it is closed, +% removed from \cs{g_@@_write_prop}, and put back in the list of +% usable streams. The stream may point to a token list, in which case +% that token list should be written to the appropriate file. The +% stream may be closed, in which case nothing happens. +% The auxiliary \cs{@@_closeout_now:nn} writes the material collected +% so far for a given user stream |#1| to the file |#2|. This uses the +% \TeX{} stream \cs{g_@@_iow}. The token list consists of multiple +% \tn{immediate} \tn{write} \cs{g_@@_iow} \Arg{text} statements +% because that is the only safe way to obtain new lines. We do not +% remove the stream/file pair from \cs{g_@@_write_file_prop}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_closeout:w + { + \s_@@ + \use_i:nn + { \@@_get_user:n { \@@_closeout_later: } } + { \@@_get_user:n { \@@_closeout_now: } } + } +\cs_new_protected:Npn \@@_closeout_now: + { + \@@_user_to_tstr:NTF \g_@@_write_prop + { + \@@_tex_immediate:w \@@_tex_closeout:w \l_@@_tstr_tl \exp_stop_f: + \int_compare:nNnF { \l_@@_tstr_tl } = { \l_@@_user_int } + { + \prop_gremove:NV \g_@@_write_prop \l_@@_user_int + \seq_gput_left:NV \g_@@_write_seq \l_@@_tstr_tl + } + } + { + \prop_gpop:NVNT \g_@@_write_file_prop \l_@@_user_int \l_@@_internal_tl + { \@@_closeout_now:nn { \l_@@_user_int } { \l_@@_internal_tl } } + } + } +\cs_new_protected:Npn \@@_closeout_now:nn #1#2 + { + \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow = #2 \scan_stop: + \group_begin: + \int_set:Nn \tex_newlinechar:D { -1 } + \tl_use:c { \@@_user_tl_name:n {#1} } + \tl_gclear:c { \@@_user_tl_name:n {#1} } + \group_end: + \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_openout:w, \@@_openout_now:n} +% In the immediate case find a file name, then allocate a \TeX{} +% stream if possible, and otherwise point the user stream to a token +% list. In all cases, close the stream to avoid losing any material +% that \TeX{} would have written, and empty the file by opening and +% closing it (actually that's done automatically by the primitive). +% \begin{macrocode} +\cs_new_protected:Npn \@@_openout:w + { + \s_@@ + \use_i:nn + { \@@_get_user:n { \@@_openout_later:w } } + { \@@_get_user:n { \@@_equals_file:N \@@_openout_now:n } } + } +\cs_new_protected:Npn \@@_openout_now:n #1 + { + \@@_closeout_now: + \int_compare:nNnTF { \l_@@_user_int } < { 19 } + { + \@@_tex_immediate:w \@@_tex_openout:w \l_@@_user_int + = \tl_to_str:n {#1} \scan_stop: + } + { + \seq_gpop:NNTF \g_@@_write_seq \l_@@_tstr_tl + { + \prop_gput:NVV \g_@@_write_prop \l_@@_user_int \l_@@_tstr_tl + \@@_tex_immediate:w \@@_tex_openout:w \l_@@_tstr_tl \exp_stop_f: + = \tl_to_str:n {#1} \scan_stop: + } + { + \@@_empty_file:n {#1} + \prop_gput:NVx \g_@@_write_file_prop \l_@@_user_int + { \tl_to_str:n {#1} } + \tl_gclear_new:c { \@@_user_tl_name:n { \l_@@_user_int } } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_write:w, \@@_write_now:w, \@@_write_now:n} +% In the immediate case we use \cs{@@_write_now_open:n} if the stream +% points to a token list, and otherwise use the primitive, with the +% dummy stream $16$ if closed (the text is then written to the +% terminal). +% \begin{macrocode} +\cs_new_protected:Npn \@@_write:w + { + \s_@@ + \use_i:nn + { \@@_get_user:n { \@@_write_later:w } } + { \@@_get_user:n { \@@_write_now:w } } + } +\cs_new_protected:Npn \@@_write_now:w + { + \@@_user_to_tstr:NTF \g_@@_write_prop + { \@@_tex_immediate:w \@@_tex_write:w \l_@@_tstr_tl \exp_stop_f: } + { \primargs_get_general_text:N \@@_write_now:n } + } +\cs_new_protected:Npn \@@_write_now:n + { + \prop_get:NVNTF \g_@@_write_file_prop \l_@@_user_int \l_@@_internal_tl + { \@@_write_now_open:n } + { \@@_tex_immediate:w \@@_tex_write:w 16 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_write_now_open:n} +% \begin{macro}{\@@_write_now_loop:} +% Only \tn{write} itself can emulate how \tn{write} expands tokens, +% because |#| don't have to be doubled, and because the +% \tn{newlinechar} has to be changed to new lines. Hence, we start by +% writing |#1| to a file (after making sure we are allowed to alter +% it), yielding some lines. The lines are then read one at a time +% using \eTeX{}'s \tn{readline} with \tn{endlinechar} set to $-1$ to +% avoid spurious characters. Each line becomes a \tn{immediate} +% \tn{write} statement added to a token list whose name is constructed +% using \cs{@@_user_tl_name:n}. This token list will be called when +% it is time to actually write to the file. At that time, +% \tn{newlinechar} will be $-1$, so that writing each line will +% produce no extra line. +% \begin{macrocode} +\cs_new_protected:Npn \@@_write_now_open:n #1 + { + \bool_if:NF \g_@@_tmp_file_bool { \@@_chk_file: } + \@@_tex_immediate:w \@@_tex_openout:w + \g_@@_iow = \g_@@_tmp_file_tl \scan_stop: + \@@_tex_immediate:w \@@_tex_write:w + \g_@@_iow {#1} + \@@_tex_immediate:w \@@_tex_closeout:w + \g_@@_iow + \group_begin: + \int_set:Nn \tex_endlinechar:D { -1 } + \tex_openin:D \g_@@_ior = \g_@@_tmp_file_tl \scan_stop: + \@@_write_now_loop: + \tex_closein:D \g_@@_ior + \@@_collect_gput_right:c + { \@@_user_tl_name:n { \l_@@_user_int } } + \group_end: + \@@_empty_file:n { \g_@@_tmp_file_tl } + } +\cs_new_protected:Npn \@@_write_now_loop: + { + \tex_readline:D \g_@@_ior to \l_@@_internal_tl + \ior_if_eof:NF \g_@@_ior + { + \@@_collect:x + { + \@@_tex_immediate:w \@@_tex_write:w + \g_@@_iow { \l_@@_internal_tl } + } + \@@_write_now_loop: + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{Delayed actions} +% +% \begin{macro}{\@@_later:n, \@@_later_do:n} +% Store the action to be done at shipout in a token list, and +% non-immediately write the label \cs{g_@@_later_int} of the +% output operation to the temporary file. +% \begin{macrocode} +\cs_new_protected:Npn \@@_later:n #1 + { + \int_gincr:N \g_@@_later_int + \tl_const:cx + { + c_@@_later_ + \int_use:N \g_@@_later_int + _tl + } + { + \int_set:Nn \exp_not:N \l_@@_user_int + { \exp_not:V \l_@@_user_int } + \exp_not:n {#1} + } + \exp_args:NNx \@@_tex_write:w \g_@@_iow + { `( \int_use:N \g_@@_later_int ) } + } +\cs_new_protected:Npn \@@_later_do:n #1 + { \tl_use:c { c_@@_later_ \int_eval:n {#1} _tl } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_closeout_later:} +% If the user stream is a \TeX{} stream, use the primitive, otherwise +% save \cs{@@_closeout_now:} for later. +% \begin{macrocode} +\cs_new_protected:Npn \@@_closeout_later: + { + \int_compare:nNnTF \l_@@_user_int < { 19 } + { \@@_tex_closeout:w \l_@@_user_int } + { \@@_later:n { \@@_closeout_now: } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_openout_later:w, \@@_openout_later:n} +% If the user stream is a \TeX{} stream use the primitive, otherwise +% find a file name and call \cs{@@_openout_now:n} later. +% \begin{macrocode} +\cs_new_protected:Npn \@@_openout_later:w + { + \int_compare:nNnTF \l_@@_user_int < { 19 } + { \@@_tex_openout:w \l_@@_user_int } + { \@@_equals_file:N \@@_openout_later:n } + } +\cs_new_protected:Npn \@@_openout_later:n #1 + { \@@_later:n { \@@_openout_now:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_write_later:w, \@@_write_later:n, \@@_write_later_aux:n} +% For \TeX{} streams use the primitive, otherwise find a general text +% and save it for later; the auxiliary is very similar to +% \cs{@@_write_now:w}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_write_later:w + { + \int_compare:nNnTF \l_@@_user_int < { 19 } + { \@@_tex_write:w \l_@@_user_int } + { \primargs_get_general_text:N \@@_write_later:n } + } +\cs_new_protected:Npn \@@_write_later:n #1 + { \@@_later:n { \@@_write_later_aux:n {#1} } } +\cs_new_protected:Npn \@@_write_later_aux:n + { + \@@_user_to_tstr:NTF \g_@@_write_prop + { \@@_tex_immediate:w \@@_tex_write:w \l_@@_tstr_tl \exp_stop_f: } + { \@@_write_now:n } + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Shipout business} +% +% In this section, we hook into the \tn{shipout} primitive, and redefine +% it to first build a box with the material to ship out, then perform +% \begin{quote} +% \cs{@@_before_shipout:} \\ +% \meta{primitive shipout} \meta{collected box} \\ +% \cs{@@_after_shipout:} +% \end{quote} +% +% Each delayed output operation has been replaced by \tn{write} +% \cs{g_@@_iow} |{`(|\meta{operation number}|)}|. The delimiters we +% chose to put around numbers must be at least two distinct characters +% on the left (then \cs{tex_newlinechar:D} cannot be equal to the +% delimiter), and at least one non-digit character on the right. +% +% \begin{macro}{\@@_before_shipout:} +% Immediately before the shipout, we must open the writing stream +% \cs{g_@@_iow} (after making sure we are allowed to alter the +% auxiliary file). +% \begin{macrocode} +\cs_new_protected:Npn \@@_before_shipout: + { + \bool_if:NF \g_@@_tmp_file_bool { \@@_chk_file: } + \@@_tex_immediate:w \@@_tex_openout:w + \g_@@_iow = \g_@@_tmp_file_tl \scan_stop: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_after_shipout:} +% \begin{macro}[rEXP]{\@@_after_shipout_loop:ww} +% Immediately after all the \tn{write}s are performed, close the file, +% then read the file with \tn{endlinechar} set to +% \tn{newlinechar}\footnote{Note that the \tn{newlinechar} used by +% \tn{write}s at \tn{shipout} time are those in effect when the page +% is shipped out, \emph{i.e.}, just after the closing brace of the +% \tn{shipout} construction, which is exactly where we have added +% this hook.} to get exactly the original characters that have been +% written, possibly with extra characters between |`(|\ldots{}|)| +% groups. The file is then read with all the appropriate category +% codes set up (no other character can appear in the file). The +% looping auxiliary \cs{@@_after_shipout_loop:ww} extracts the +% \meta{operation} numbers from the file, and makes a token list out +% of those. This token list is then used in a mapping function to +% perform the appropriate \tn{write} operations. Note that those +% operations may reuse the file, so we have to fully parse the file +% before moving on. +% \begin{macrocode} +\cs_new_protected:Npn \@@_after_shipout: + { + \@@_tex_immediate:w \@@_tex_closeout:w + \g_@@_iow + \group_begin: + \int_set_eq:NN \tex_endlinechar:D \tex_newlinechar:D + \char_set_catcode_other:n { \tex_endlinechar:D } + \tl_map_inline:nn { `(0123456789) } + { \char_set_catcode_other:n {`##1} } + \tex_everyeof:D { `() \exp_not:N } + \tl_set:Nx \l_@@_internal_tl + { + \exp_after:wN \@@_after_shipout_loop:ww + \tex_input:D \g_@@_tmp_file_tl \c_space_tl + } + \@@_empty_file:n { \g_@@_tmp_file_tl } + \exp_args:NNo + \group_end: + \tl_map_function:nN { \l_@@_internal_tl } \@@_later_do:n + } +\cs_new:Npn \@@_after_shipout_loop:ww #1 `( #2 ) + { + \tl_if_empty:nF {#2} + { + {#2} + \@@_after_shipout_loop:ww + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_shipout:w} +% \begin{macro}{\@@_shipout_i:, \@@_shipout_ii:} +% Grab the shipped out box using \tn{setbox} and regain control using +% \tn{afterassignment}. There are two cases: either the box is given +% as \tn{box} or \tn{copy} followed by a number, in which case +% \cs{@@_shipout_i:} is inserted afterwards at the same group level, +% or the box is given as \tn{hbox} (or \tn{vtop} and so on) and an +% additional \tn{aftergroup} is needed to reach a point where we can +% use the box saved in \cs{g_@@_shipout_box}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_shipout:w + { + \int_gset_eq:NN \g_@@_group_level_int \tex_currentgrouplevel:D + \tex_afterassignment:D \@@_shipout_i: + \tex_global:D \tex_setbox:D \g_@@_shipout_box + } +\cs_new_protected:Npn \@@_shipout_i: + { + \int_compare:nNnTF { \g_@@_group_level_int } + = { \tex_currentgrouplevel:D } + { \@@_shipout_ii: } + { \tex_aftergroup:D \@@_shipout_ii: } + } +\cs_new_protected:Npn \@@_shipout_ii: + { + \@@_before_shipout: + \@@_tex_shipout:w \tex_box:D \g_@@_shipout_box + \@@_after_shipout: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\shipout, \@@_tex_shipout:w} +% The task is now to locate the shipout primitive, which may have been +% renamed and hooked into by many different packages loaded before +% \pkg{morewrites}. Any of those control sequences which are equal to +% the primitive are redefined to do \cs{@@_shipout:w} instead. If the +% primitive is not located at all, the fallback is to hook into the +% control sequence \tn{shipout}. +% \begin{macrocode} +\cs_gset_protected:Npn \@@_tmp:w #1 + { + \cs_if_exist:NF \@@_tex_shipout:w + { \cs_new_eq:NN \@@_tex_shipout:w #1 } + \cs_gset_eq:NN #1 \@@_shipout:w + } +\tl_map_inline:nn + { + \xyrealshipout@ + \org@shipout + \PDFSYNCship@ut@ld + \CROP@shipout + \@soORI + \tex_shipout:D + \zwpl@Hship + \o@shipout@TP + \LL@shipout + \Shipout + \GXTorg@shipout + \AtBegShi@OrgShipout + \AtBeginShipoutOriginalShipout + \minidocument@orig@shipout + \shipout + } + { + \str_if_eq:eeT + { \cs_meaning:N #1 } + { \token_to_str:N \shipout } + { \@@_tmp:w #1 } + } +\cs_if_exist:NF \@@_tex_shipout:w + { + \cs_new_eq:NN \@@_tex_shipout:w \shipout + \cs_gset_eq:NN \shipout \@@_shipout:w + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Hook at the very end} +% +% \begin{macro}{\@@_close_all:} +% At the end of the document, close all the files. +% \begin{macrocode} +\cs_new_protected:Npn \@@_close_all: + { + \prop_map_inline:Nn \g_@@_write_prop + { \@@_tex_immediate:w \@@_tex_closeout:w ##2 \scan_stop: } + \prop_gclear:N \g_@@_write_prop + \prop_map_function:NN \g_@@_write_file_prop + \@@_closeout_now:nn + \prop_gclear:N \g_@@_write_file_prop + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_close_all_at_end:nw} +% At the end of the run, we try very hard to put some material at the +% \tn{@@end}, just in case some other very late code writes to files +% that are not yet closed. This is tried at most $5$~times, to avoid +% infinite loops in case two packages compete for that last place. +% The four |@| become two after \pkg{l3docstrip}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_close_all_at_end:nw #1#2 \@@@@end + { + \int_compare:nNnTF {#1} > 0 + { #2 \@@_close_all_at_end:nw { #1 - 1 } } + { \@@_close_all: #2 } + \@@@@end + } +\AtEndDocument { \@@_close_all_at_end:nw { 5 } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Redefining commands} +% +% \subsubsection{Modified \tn{newwrite}} +% +% \begin{variable}{\g_@@_alloc_write_int} +% Counter to allocate user streams. Initialized to $18$ so that the +% first user stream allocated by \pkg{morewrites} is $19$. Indeed, +% $18$ is reserved for shell commands and packages may expect $16$ or +% $17$ to write to the terminal. +% \begin{macrocode} +\int_new:N \g_@@_alloc_write_int +\int_gset:Nn \g_@@_alloc_write_int { 18 } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_newwrite:N} +% Reimplementation of \tn{newwrite} but protected and using a counter +% \cs{g_@@_alloc_write_int} instead of what \TeX{}/\LaTeXe{} use. +% \begin{macrocode} +\cs_new_protected:Npn \@@_newwrite:N #1 + { + \int_gincr:N \g_@@_alloc_write_int + \int_set_eq:NN \allocationnumber \g_@@_alloc_write_int + \cs_undefine:N #1 + \int_const:Nn #1 { \allocationnumber } + \wlog + { + \token_to_str:N #1 + = \token_to_str:N \write \int_use:N \allocationnumber + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_allocate:n} +% Raise to |#1| the number of \tn{write} streams allocated to \pkg{morewrites}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_allocate:n #1 + { + \prg_replicate:nn + { + \int_max:nn { 0 } + { + (#1) - \seq_count:N \g_@@_write_seq + - \prop_count:N \g_@@_write_prop + } + } + { + \@@_tex_newwrite:N \l_@@_tstr_token + \seq_gput_right:NV \g_@@_write_seq \l_@@_tstr_token + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{User commands and keys} +% +% \begin{macro}[added = 2014-07-26]{\morewritessetup} +% Set whatever keys the user passes to \cs{morewritessetup}. +% \begin{macrocode} +\cs_new_protected:Npn \morewritessetup #1 + { \keys_set:nn { @@ } {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[added = 2014-07-26]{file} +% Because of our use of |.initial:n|, this code must appear after +% \cs{@@_set_file:n} is defined. +% \begin{macrocode} +\keys_define:nn { @@ } + { + allocate .code:n = \@@_allocate:n {#1} , + file .code:n = \@@_set_file:n {#1} , + file .initial:n = \c_sys_jobname_str .mw + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[updated = 2015-08-01] +% {\immediate, \openout, \write, \closeout, \newwrite} +% \begin{macrocode} +\cs_gset_eq:NN \immediate \@@_immediate:w +\cs_gset_eq:NN \openout \@@_openout:w +\cs_gset_eq:NN \write \@@_write:w +\cs_gset_eq:NN \closeout \@@_closeout:w +\cs_gset_eq:NN \newwrite \@@_newwrite:N +% \end{macrocode} +% \end{macro} +% +%</package> +% +% \end{implementation} +% +% \clearpage +% \PrintIndex |