diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/morewrites/morewrites.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/morewrites/morewrites.dtx | 1147 |
1 files changed, 593 insertions, 554 deletions
diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx index cc8d2d23121..4766f3abe5e 100644 --- a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx +++ b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: morewrites.dtx Copyright (C) 2011 Bruno Le Floch +%% File: morewrites.dtx Copyright (C) 2011-2012 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 @@ -13,8 +13,6 @@ % %<*driver|package> \RequirePackage{l3names} -\GetIdInfo$Id: morewrites.dtx 0.1 2011-09-09 00:00:00Z bruno $ - {Always room for a new write}% %</driver|package> %<*driver> \RequirePackage{morewrites} @@ -22,6 +20,7 @@ \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo +\newcommand{\proc}[1]{\texttt{#1}} \begin{document} \DocInput{\jobname.dtx} \end{document} @@ -30,47 +29,55 @@ % % \title{The \textsf{morewrites} package: \\ % Always room for a new \cs{write}\thanks{This -% file has version number \ExplFileVersion, last revised \ExplFileDate.}} +% file has version number v0.2, last revised 2012-07-10.}} % \author{Bruno Le Floch} -% \date{\ExplFileDate} +% \date{July 10, 2012} % % \maketitle % \tableofcontents % % \begin{documentation} % -% \section{\pkg{morewrites} documentation} -% -% This \LaTeX{} package is meant to be a solution for the error -% \enquote{no room for a new \cs{write}}, which occurs when too -% many macro packages reserve streams to write data to various -% auxiliary files. It is in principle possible to rewrite 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). If I did my job -% correctly, you simply need to add the line -% |\usepackage{morewrites}| somewhere near the beginning of your -% \LaTeX{} file, and the \enquote{no room for a new \cs{write}} -% error should vanish. -% -% As this is a rather new package, it has not been tested very -% thoroughly yet. I thus encourage you to check that references -% are correct after loading that package: if they are correct -% without \pkg{morewrites}, but wrong with, please send me a -% minimal file showing the problem, or post a question on the -% \url{tex.stackexchange.com} question and answers website, -% or the \url{comp.text.tex} newsgroup. -% -% This package loads the \pkg{expl3} package, hence the -% \pkg{l3kernel} bundle needs to be up to date. If Heiko -% Oberdiek's package %^^A \pkg{atveryend} -% \pkg{atbegshi} is available, it will be used. -% -% \section{Known deficiencies} -% -% Some distributions of \TeX{} allow a quoted syntax for file -% names with spaces. I haven't yet coded that. A temporary fix -% is to avoid file names with spaces. +% \section{\pkg{morewrites} documentation} +% +% This \LaTeX{} package is meant to be a solution for the error +% \enquote{no room for a new \cs{write}}, which occurs when too many +% macro packages reserve streams to write data to various auxiliary +% files. It is in principle possible to rewrite 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). If I did my job correctly, you simply need to add the +% line |\usepackage{morewrites}| somewhere near the beginning of your +% \LaTeX{} file, and the \enquote{no room for a new \cs{write}} error +% should vanish. +% +% I have tried to make the code as robust as possible, but there may +% still be bugs lurking as this package has not been tested very +% thoroughly yet. I thus encourage you to check that references are +% correct after loading that package: if they are correct without +% \pkg{morewrites}, but wrong with, please send me a minimal file +% showing the problem, or post a question on the +% \url{tex.stackexchange.com} question and answers website, or the +% \url{comp.text.tex} newsgroup. +% +% This package loads the \pkg{expl3} package, hence the \pkg{l3kernel} +% bundle needs to be up to date. If Heiko Oberdiek's package +% \pkg{atbegshi} is available, it will be used. +% +% \section{Known deficiencies} +% +% Some distributions of \TeX{} allow a quoted syntax for file names with +% spaces. I haven't yet coded that. A temporary fix is to avoid file +% names with spaces. +% +% The package code is not very legible, and definitely uses too many +% |:D| control sequences, whose name means \enquote{do not use}. The +% author does not see a way to avoid using primitives in this package, +% since hooking into the primitives \tn{immediate}, \tn{write}, +% \emph{etc.} requires having a very strong control on what every +% command does. \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.} % % \end{documentation} % @@ -81,637 +88,647 @@ %<*package> % \begin{macrocode} \ProvidesExplPackage - {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription} -\RequirePackage{expl3} -% \end{macrocode} -% -% Let us recall the syntax of the commands that we need to modify -% (from Chapter 24 of the \TeX{}book): each of those can be preceeded -% by \cs{immediate}, which we will thus also have to modify. + {morewrites} {2012/07/10} {0.2} {Always room for a new write} +\RequirePackage{expl3, primargs} +% \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. We must thus patch $4$ primitives, \tn{openout}, \tn{write}, +% \tn{closeout} and \tn{immediate}, and the \cs{newwrite} macro, defined +% by \LaTeX{} (and plain \TeX{}). Each of those commands must be made +% to accept numbers outside the range $[0,15]$. Let us review the +% syntax of the various commands we need to alter (see Chapter~24 of the +% \TeX{}book). +% +% We start with the three \enquote{actions}. +% \begin{syntax} +% \tn{openout} \meta{integer} \meta{equals} \meta{file name} +% \end{syntax} +% \TeX{} searches the path for a file with a name given by +% \meta{file name}. If found, this file is opened in the writing stream +% \meta{integer}, which must be a number in the range $[0,15]$. +% \begin{syntax} +% \tn{write} \meta{integer} \meta{filler} \meta{general text} +% \end{syntax} +% \TeX{} expands the \meta{general text} as for an \texttt{x}-type +% expansion, with the caveat that macro parameter characters do not need +% to be doubled; converts the result to a string, and writes it in the +% writing stream \meta{integer}. If the writing stream \meta{integer} +% is open (in particular it must be in the range $[0,15]$), then this +% writes to the corresponding file. Otherwise, if the \meta{integer} is +% negative, the text is written to the log file, and a non-negative +% \meta{integer} writes to the terminal. One exception: if the +% \meta{integer} is $18$, the text is sent to a shell to be run as shell +% code. +% \begin{syntax} +% \tn{closeout} \meta{integer} +% \end{syntax} +% If the writing stream \meta{integer} is open, it is closed. +% Otherwise, if the \meta{integer} is not in the range $[0,15]$ an error +% may be raised, or nothing happens. +% +% By default, each one of those three \enquote{actions} 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 the three \enquote{actions} 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 such as +% \tn{box}\meta{integer}, or \tn{hbox}\Arg{material to typeset}. +% +% Finally, the \cs{newwrite} macro expects one token as its argument, +% and defines this token (with \tn{chardef}) to be an integer +% corresponding to the first available writing stream. We must extend +% it to let it allocate higher (virtual) write registers. +% +% All of the primitives above perform full expansion of all tokens when +% looking for their operands. In most cases, only the \tn{meaning} of +% tokens encountered in this way matters. Specifically, % \begin{itemize} -% \item \cs{openout} \meta{4-bit number} \meta{equals} \meta{filename} -% \item \cs{write} \meta{number} \meta{general text} -% with a special case when \meta{number} is $18$. -% \item \cs{closeout} \meta{4-bit number} +% \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 a character with category code $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} -% Finally, we also need to modify \cs{newwrite} to let it allocate -% more than $16$ streams. % -% \begin{macro}[aux]{\mw_tex_openin:w,\mw_tex_read:w, -% \mw_etex_readline:w,\mw_tex_closein:w} -% \begin{macro}[aux]{\mw_tex_immediate:w,\mw_tex_openout:w, -% \mw_tex_write:w,\mw_tex_closeout:w} -% First save the primitives (including those for input, -% because this package might be extended to cover that -% as well). +% \subsection{Renaming primitives (again)} +% +% \begin{macro}[int] +% { +% \@@_tex_immediate:w , +% \@@_tex_openout:w , +% \@@_tex_write:w , +% \@@_tex_closeout:w +% } +% First save the output-related primitives. % \begin{macrocode} -\cs_new_eq:NN \mw_tex_openin:w \tex_openin:D -\cs_new_eq:NN \mw_tex_read:w \tex_read:D -\cs_new_eq:NN \mw_etex_readline:w \etex_readline:D -\cs_new_eq:NN \mw_tex_closein:w \tex_closein:D -\cs_new_eq:NN \mw_tex_immediate:w \tex_immediate:D -\cs_new_eq:NN \mw_tex_openout:w \tex_openout:D -\cs_new_eq:NN \mw_tex_write:w \tex_write:D -\cs_new_eq:NN \mw_tex_closeout:w \tex_closeout:D -\cs_new_eq:NN \mw_tex_shipout:w \tex_shipout:D +\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}[int]{\@@_tex_shipout:w} +% Since the non-\tn{immediate} output primitives act at \tn{shipout} +% time, we need to alter this primitive too. +% %^^A Not sure if the shipout primitive is done right. +% \begin{macrocode} +\cs_new_eq:NN \@@_tex_shipout:w \tex_shipout:D +% \end{macrocode} % \end{macro} % % \subsection{Variables} % -% \begin{variable}{\g_mw_late_write_int} -% The integer \cs{g_mw_late_write_int} labels the various -% non-immediate operations in the order in which they appear -% in the source. -% \begin{macrocode} -\int_new:N \g_mw_late_write_int +% \begin{variable}{\g_@@_late_write_int} +% The integer \cs{g_@@_late_write_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} |}| +% \cs{tex_shipout:D} \cs{tex_copy:D} \cs{l_my_box} +% \cs{tex_shipout:D} \cs{tex_copy:D} \cs{l_my_box} +% \end{quote} +% will print \meta{text} to the terminal twice. +% \begin{macrocode} +\int_new:N \g_@@_late_write_int % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_writes_prop} -% The property list \cs{g_mw_writes_prop} -% associates a file name to each open stream. +% \begin{variable}{\g_@@_iow_prop} +% The property list \cs{g_@@_iow_prop} associates a file name to each +% open stream. % \begin{macrocode} -\prop_new:N \g_mw_writes_prop +\prop_new:N \g_@@_iow_prop % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_read,\c_mw_tmp_file_tl} -% The expansion that \cs{write} performs is impossible -% to emulate with anything else than \cs{write}. We will -% need to write to a file and read back from it for things -% to work properly. +% \begin{variable}{\g_@@_iow, \g_@@_ior, \g_@@_tmp_file_tl} +% The expansion that \tn{write} performs is impossible to emulate 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} -\newread \g_mw_read -\tl_const:Nn \c_mw_tmp_file_tl { \jobname.mw } -\newwrite \g_mw_write +\newwrite \g_@@_iow +\newread \g_@@_ior +\tl_new:N \g_@@_tmp_file_tl +\tl_gset:Nn \g_@@_tmp_file_tl { \jobname.mw } % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_reserved_writes_seq} -% Some of the writing streams are already allocated when loading -% this package, and we let \LaTeX{} manage them. +% \begin{variable}{\g_@@_reserved_iow_clist} +% Some of the writing streams are already allocated when loading this +% package, and we let the engine manage them. This variable is a +% clist because it only contains integers and the main task is to test +% if a given integer is in the comma list. % \begin{macrocode} -\seq_new:N \g_mw_reserved_writes_seq -\prg_stepwise_inline:nnnn {0} {1} { \g_mw_write - 1 } - { \seq_gput_right:Nn \g_mw_reserved_writes_seq {#1} } -\seq_gput_right:Nn \g_mw_reserved_writes_seq {18} +\clist_new:N \g_@@_reserved_iow_clist +\int_step_inline:nnnn {0} {1} { \g_@@_iow - 1 } + { \clist_gput_right:Nn \g_@@_reserved_iow_clist {#1} } +\clist_gput_right:Nn \g_@@_reserved_iow_clist {18} % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_mw_tmpa_tl,\g_mw_step_int,\g_mw_tmpa_int,\g_mw_box} +% \begin{variable}[int]{\g_@@_stream_int} +% An integer holding the \meta{number} argument of various primitives, +% namely a writing stream. % \begin{macrocode} -\tl_new:N \l_mw_tmpa_tl -\int_new:N \g_mw_step_int -\int_new:N \g_mw_tmpa_int -\newbox \g_mw_box +\int_new:N \g_@@_stream_int % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_mw_stream_int,\g_mw_filename_tl,\g_mw_text_tl} -% What parsing finds is stored in those three variables. +% \begin{macro}[int]{\s_@@} +% A recognizable version of \cs{scan_stop:}. This is inspired +% from\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 +% note that we don't use \cs{__scan_new:N} directly, since it is +% internal to \LaTeX3. % \begin{macrocode} -\int_new:N \g_mw_stream_int -\tl_new:N \g_mw_filename_tl -\tl_new:N \g_mw_text_tl +\cs_new_eq:NN \s_@@ \scan_stop: % \end{macrocode} -% \end{variable} +% \end{macro} % -% \begin{macro}[aux]{\mw_scan_stop:} -% A recognizable version of \cs{scan_stop:}. +% \begin{variable}[int]{\l_@@_internal_tl} +% Temporary token list, used for scratch purposes. % \begin{macrocode} -\cs_new_eq:NN \mw_scan_stop: \scan_stop: +\tl_new:N \l_@@_internal_tl % \end{macrocode} -% \end{macro} +% \end{variable} % % \subsection{Parsing} % -% \begin{macro}[aux]{\mw_parse_number:Nw} -% Stores a number in \cs{g_mw_stream_int} before performing |#1|. +% \begin{macro}[aux]{\@@_equals_file_name: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_nopar:Npn \mw_parse_number:Nw #1 - { - \tex_afterassignment:D #1 - \tex_global:D \g_mw_stream_int - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[aux]{\mw_parse_text:Nw} -% This is used for the \cs{write} command: to grab -% a \meta{general text} we assign a \cs{toks}, within -% a group to avoid clobbering \cs{toks}\texttt{0}. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_text:Nw #1 +\cs_new_protected:Npn \@@_equals_file_name:N #1 { \group_begin: \tex_aftergroup:D #1 - \tex_afterassignment:D \mw_parse_text_aux: - \tex_toks:D \c_zero = - } -\cs_new_protected_nopar:Npn \mw_parse_text_aux: - { - \tl_gset:Nx \g_mw_text_tl { \tex_the:D \tex_toks:D \c_zero } - \group_end: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[aux]{\mw_parse_equals_filename:Nw} -% Remove a potential \texttt{=} sign. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_equals_filename:Nw #1 - { - \peek_meaning_remove:NTF = - { \mw_parse_filename:Nw #1 } { \mw_parse_filename:Nw #1 } + \primargs_remove_equals:N \@@_parse_file_name: } +\cs_new_protected:Npn \@@_parse_file_name: + { \primargs_get_file_name:N \group_end: } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_parse_filename:Nw} -% Empty the filename, and build it one character at a time. -% We need to expand tokens as in an f-type expansion, but -% without losing spaces. For that, we use \cs{peek_gafter:Nw} -% to find out whether the coming token is expandable or not, -% and distinguish further depending on what kind of -% non-expandable token it is. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename:Nw #1 - { - \group_begin: - \group_align_safe_begin: - \tex_aftergroup:D #1 - \tl_gclear:N \g_mw_filename_tl - \mw_parse_filename_loop:w - } -\cs_new_protected_nopar:Npn \mw_parse_filename_loop:w - { \peek_gafter:Nw \mw_parse_filename_loop_test: } -% \end{macrocode} -% The test \cs{token_if_expandable:NTF} should not be used here, -% since it returns false for undefined control sequences (by design). -% But when parsing a file name, undefined control sequences would -% be expanded, and cause errors. In order to keep this behaviour, -% we do the test ourselves. -% -% A non-expandable token is either a non-space character, a space -% character, or a control sequence (either a primitive or a register). -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_loop_test: - { - \exp_after:wN \if_meaning:w \exp_not:N \g_peek_token \g_peek_token - \exp_after:wN \use_ii:nn - \else: - \exp_after:wN \use_i:nn - \fi: - { \exp_after:wN \mw_parse_filename_loop:w } - { - \token_if_cs:NTF \g_peek_token - { \mw_parse_filename_end: } - { - \mw_parse_filename_space_test:NTF \g_peek_token - { \mw_parse_filename_space_do:w } - { - \exp_after:wN \mw_parse_filename_push:w - \token_to_meaning:N - } - } - } - } -% \end{macrocode} -% We stop at the first non-expandable non-character token. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_end: - { - \group_align_safe_end: - \group_end: - } -% \end{macrocode} -% We also stop when seeing a space, after removing it. -% \begin{macrocode} - %^^A TeXlive's behaviour (?): space with any catcode -\cs_new_protected_nopar:Npn \mw_parse_filename_space_test:NTF - { \token_if_eq_charcode:NNTF \c_space_token } -\cs_new_protected_nopar:Npn \mw_parse_filename_space_do:w - { - \tex_afterassignment:D \mw_parse_filename_end: - \cs_set_eq:NN \g_peek_token - } -% \end{macrocode} -% Finally, the case of normal characters. It seems -% that e.g., \cs{bgroup} is seen in the same way as an -% explicit left-brace character. Hence, we work with -% the \cs{meaning} of the character we are looking at. -% Most meanings of characters are two words, followed -% by the character. Three exceptions: ``math shift character'', -% ``alignment tab character'', and ``macro parameter character'', -% which have three words. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_parse_filename_push:w #1 ~ % - { - \tl_if_in:nnTF { math ~ alignment ~ macro } { #1 } - { \mw_parse_filename_push_three:w } - { \mw_parse_filename_push_two:w } - } -\cs_new_protected_nopar:Npn \mw_parse_filename_push_two:w #1 ~ #2 - { - \tl_gput_right:Nn \g_mw_filename_tl {#2} - \mw_parse_filename_loop:w - } -\cs_new_protected_nopar:Npn \mw_parse_filename_push_three:w #1 ~ #2 ~ #3 - { - \tl_gput_right:Nn \g_mw_filename_tl {#3} - \mw_parse_filename_loop:w - } -% \end{macrocode} -% \end{macro} -% -% % \subsection{Immediate (writing)} % % In the context of immediate writing, we can store the text % in a token list, and only write it at the corresponding -% \cs{closeout} command. We keep track of a property list, -% \cs{g_mw_writes_prop}, of the writes which are +% \tn{closeout} command. We keep track of a property list, +% \cs{g_@@_iow_prop}, of the writes which are % open (from the point of view of the user), with the % corresponding file name. % -% \subsubsection{What follows \cs{immediate}} -% -% \begin{macro}[aux]{\mw_prg_case_meaning:Nnn} -% \begin{macro}[aux]{\mw_prg_case_meaning_aux:Nw} -% See \cs{prg_case_tl:Nnn}. Here, it is used by \cs{mw_immediate:w} -% to decide whether the following token is a writing command. -% \begin{macrocode} -\cs_new:Npn \mw_prg_case_meaning:Nnn #1#2#3 - { \mw_prg_case_meaning_aux:Nw #1 #2 #1 {#3} \q_recursion_stop } -\cs_new:Npn \mw_prg_case_meaning_aux:Nw #1#2#3 - { - \token_if_eq_meaning:NNTF #1 #2 - { \prg_case_end:nw {#3} } - { \mw_prg_case_meaning_aux:Nw #1 } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[aux]{\mw_exp_after:Nf} -% Eventually, this should be replaced by a slow \cs{futurelet}-based -% expansion which does not lose a space. -% \begin{macrocode} -\cs_new:Npn \mw_exp_after:Nf #1 - { \exp_after:wN #1 \tex_romannumeral:D -`0 } -% \end{macrocode} -% \end{macro} +% \subsubsection{What follows \tn{immediate}} % -% \begin{macro}[aux]{\mw_immediate:w} -% This is a little bit subtle: \TeX{}'s \cs{immediate} primitive +% \begin{macro}[aux]{\@@_immediate:w} +% \begin{macro}[aux]{\@@_immediate_ii:, \@@_immediate_iii:N} +% \begin{macro}[aux]{\@@_immediate_iv:NN} +% \begin{macro}[aux, EXP]{\@@_immediate_v:w} +% This is a little bit subtle: \TeX{}'s \tn{immediate} primitive % raises a flag which is cancelled once \TeX{} sees a non-expandable -% token. This can be emulated by expanding tokens in front of it until -% the first non-expandable token, and checking if it is either -% \cs{openout}, \cs{write}, or \cs{closeout}. We will use f-type -% expansion.\footnote{Unfortunately, this is not quite correct, since it -% loses a space if someone puts \cs{immediate} at places where it -% does not belong, such as \cs{immediate} \cs{c_space_tl}.} -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate:w - { - \scan_stop: %^^A? - \mw_exp_after:Nf \mw_immediate_aux:w - } -\cs_new_protected_nopar:Npn \mw_immediate_aux:w +% token. We use \pkg{primargs}'s \texttt{read_x_token} function to +% fully expand in the \TeX{} way, then test for \tn{openout}, +% \tn{write}, or \tn{closeout}. We don't test for the primitives +% themselves, but rather for a recognizable marker, \cs{s_@@}, equal +% to \tn{relax}. If present, replace |morewrites| by +% |morewrites_immediate| in the csname of the second token after it +% (it turns out that this is the correct structure). +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_immediate:w + { \primargs_read_x_token:N \@@_immediate_ii: } +\cs_new_protected_nopar:Npn \@@_immediate_ii: { - \peek_meaning:NT \mw_scan_stop: - { \mw_immediate_mw_scan_stop:N } + \token_if_eq_meaning:NNT \g_primargs_token \s_@@ + { \@@_immediate_iii:N } } -\cs_new_protected_nopar:Npn \mw_immediate_mw_scan_stop:N #1 +\cs_new_protected:Npn \@@_immediate_iii:N #1 { - \str_if_eq:nnTF { #1 } { \mw_scan_stop: } - { \mw_immediate_do:NN } + \tl_if_eq:nnTF { #1 } { \s_@@ } + { \@@_immediate_iv:NN } { #1 } } -\cs_new_protected_nopar:Npn \mw_immediate_do:NN #1 #2 +\cs_new_protected:Npn \@@_immediate_iv:NN #1 #2 { - \exp_args:Nc \mw_parse_number:Nw + \exp_args:Nc #1 { - \exp_after:wN \mw_immediate_do_aux:w + \exp_after:wN \@@_immediate_v:w \token_to_str:N #2 } } \use:x { - \cs_new_protected_nopar:Npn \exp_not:N \mw_immediate_do_aux:w - ##1 \tl_to_str:n {mw_} { mw_immediate_ } + \cs_new:Npn \exp_not:N \@@_immediate_v:w + ##1 \tl_to_str:n { @@ } { @@_immediate } } % \end{macrocode} % \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} % % \subsubsection{Immediate closeout} % -% \begin{macro}[aux]{\mw_immediate_closeout_test:} +% \begin{macro}[aux]{\@@_immediate_closeout_test:n} % When the user requests to close a stream, we look in -% \cs{g_mw_reserved_writes_seq} to see if it is a reserved +% \cs{g_@@_reserved_iow_clist} to see if it is a reserved % stream: in this case, we simply use the primitive. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_closeout_test: +\cs_new_protected:Npn \@@_immediate_closeout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_stream_int } - { \mw_immediate_closeout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_stream_int } + { \@@_immediate_closeout_aux: } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_closeout_aux:} -% We then look in \cs{g_mw_writes_prop} to find -% the file name corresponding to that stream number. If the -% stream does not appear as a key in the property list, -% then it was not open yet, and we do nothing. +% \begin{macro}[aux]{\@@_immediate_closeout_aux:} +% We then look in \cs{g_@@_iow_prop} to find the file name +% corresponding to that stream number. If the stream does not appear +% as a key in the property list, then it was not open yet, and we do +% nothing. Otherwise, the key is removed, and we write the collected +% material to the file. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_closeout_aux: +\cs_new_protected_nopar:Npn \@@_immediate_closeout_aux: { - \exp_args:NNV \prop_pop:NnNT - \g_mw_writes_prop \g_mw_stream_int \l_mw_tmpa_tl + \exp_args:NNV \prop_pop:NnNT \g_@@_iow_prop + \g_@@_stream_int \l_@@_internal_tl { - \mw_tex_immediate:w \mw_tex_openout:w - \g_mw_write \l_mw_tmpa_tl \scan_stop: - \group_begin: - \int_set_eq:NN \tex_newlinechar:D \c_minus_one - \tl_use:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - \tl_gclear:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - \group_end: - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write + \@@_immediate_write_and_close:nn + { \g_@@_stream_int } { \l_@@_internal_tl } } } % \end{macrocode} % \end{macro} % +% \begin{macro}[aux]{\@@_immediate_write_and_close:nn} +% The code to write the material collected so far for a given output +% \meta{stream} is in the token list \cs{g_@@_iow_\meta{stream}_tl}. +% We do this writing in the actual stream \cs{g_@@_iow}, briefly +% opened and closed on the file |#2|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_immediate_write_and_close:nn #1#2 + { + \@@_tex_immediate:w \@@_tex_openout:w + \g_@@_iow #2 \scan_stop: + \group_begin: + \int_set_eq:NN \tex_newlinechar:D \c_minus_one + \tl_use:c { g_@@_iow_ \int_eval:n {#1} _tl } + \tl_gclear:c { g_@@_iow_ \int_eval:n {#1} _tl } + \group_end: + \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Immediate openout} % -% \begin{macro}[aux]{\mw_immediate_openout_test:} -% Read the stream number. If it is one of the reserved streams, -% we use the primitive. Otherwise, parse an optional equal sign, +% \begin{macro}[aux]{\@@_immediate_openout_test:n} +% Read the stream number. If it is one of the reserved streams, we +% use the primitive. Otherwise, parse an optional equal sign, % followed by the file name. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_openout_test: +\cs_new_protected:Npn \@@_immediate_openout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_openout:w \g_mw_stream_int } - { \mw_parse_equals_filename:Nw \mw_immediate_openout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_openout:w \g_@@_stream_int } + { \@@_equals_file_name:N \@@_immediate_openout_aux:n } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_openout_aux:} -% When the user requests to open a stream, it might already be -% open, with another file as its destination. We thus need to -% first close the stream, writing all that we collected so far -% to that other file. This has no effect if the stream was not -% open yet. +% \begin{macro}[aux]{\@@_immediate_openout_aux:n} +% When the user requests to open a stream, it might already be open, +% with another file as its destination. We thus need to first close +% the stream, writing all that we collected so far to that other +% file. This has no effect if the stream was not open yet. % -% We then put the stream and its associated filename in the -% property list, and empty the corresponding token list. +% We then put the stream and its associated file name in the property +% list, and empty/create the corresponding token list. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_openout_aux: +\cs_new_protected:Npn \@@_immediate_openout_aux:n #1 { - \mw_immediate_closeout_aux: - \prop_gput:NVV \g_mw_writes_prop \g_mw_stream_int \g_mw_filename_tl - \tl_gclear_new:c { g_mw_write_ \int_use:N \g_mw_stream_int _tl } + \@@_immediate_closeout_aux: + \prop_gput:NVn \g_@@_iow_prop \g_@@_stream_int {#1} + \tl_gclear_new:c { g_@@_iow_ \int_use:N \g_@@_stream_int _tl } } % \end{macrocode} % \end{macro} % % \subsubsection{Immediate write} % -% \begin{macro}[aux]{\mw_immediate_write_test:} -% Read the stream number. If it is one of the reserved streams, -% we use the primitive. Otherwise, parse the text. +% \begin{macro}[aux]{\@@_immediate_write_test:n} +% Read the stream number. If it is one of the reserved streams, we +% use the primitive. Otherwise, parse the text. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_write_test: +\cs_new_protected:Npn \@@_immediate_write_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_immediate:w \mw_tex_write:w \g_mw_stream_int } - { \mw_parse_text:Nw \mw_immediate_write_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_immediate:w \@@_tex_write:w \g_@@_stream_int } + { \primargs_get_general_text:N \@@_immediate_write_aux:n } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_immediate_write_aux:} -% Only \cs{write} itself can emulate how \cs{write} expands tokens, -% because |#| don't have to be doubled. Hence, we write to a file, -% and re-read from it, setting \cs{endlinechar} to $-1$ to avoid -% spurious characters. Lines will be written one at a time: this -% allows us to set \cs{newlinechar} to $-1$ when writing, so that -% no extra line will be created. +% \begin{macro}[aux]{\@@_immediate_write_aux:n} +% Test whether the stream is allocated or not. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_immediate_write_aux:n + { + \prop_if_in:NVTF \g_@@_iow_prop \g_@@_stream_int + { \@@_immediate_write_open:n } + { \@@_immediate_write_closed:n } + } +% \end{macrocode} +% \end{macro} % -% If the stream \cs{g_mw_stream_int} is not allocated, -% then write either to the terminal or only to the log file. +% \begin{macro}[aux]{\@@_immediate_write_closed:n} +% If the stream \cs{g_@@_stream_int} is not allocated, then write +% either to the terminal or only to the log file, depending on the +% sign. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_immediate_write_aux: +\cs_new_protected:Npn \@@_immediate_write_closed:n #1 { - \prop_if_in:NVTF \g_mw_writes_prop \g_mw_stream_int - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write - \c_mw_tmp_file_tl \scan_stop: - \mw_tex_immediate:w \mw_tex_write:w \g_mw_write { \g_mw_text_tl } - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write - \group_begin: - \int_set_eq:NN \tex_endlinechar:D \c_minus_one - \mw_tex_openin:w \g_mw_read \c_mw_tmp_file_tl \scan_stop: - \mw_immediate_write_aux_readlines: - \mw_tex_closein:w \g_mw_read - \group_end: - } - { - \mw_tex_immediate:w \mw_tex_write:w - \if_num:w \g_mw_stream_int < \c_zero - -1 - \else: - 16 - \fi: - { \g_mw_text_tl } - } + \@@_tex_immediate:w \@@_tex_write:w + \if_num:w \g_@@_stream_int < \c_zero + -1 + \else: + 16 + \fi: + {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux]{\@@_immediate_write_open:n} +% \begin{macro}[aux]{\@@_immediate_write_readlines_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, 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 the +% token list \cs{g_@@_iow_\meta{stream}_tl}. 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 \@@_immediate_write_open:n #1 + { + \@@_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_eq:NN \tex_endlinechar:D \c_minus_one + \tex_openin:D \g_@@_ior \g_@@_tmp_file_tl \scan_stop: + \@@_immediate_write_readlines_loop: + \tex_closein:D \g_@@_ior + \group_end: } -\cs_new_protected_nopar:Npn \mw_immediate_write_aux_readlines: +\cs_new_protected_nopar:Npn \@@_immediate_write_readlines_loop: { - \mw_etex_readline:w \g_mw_read to \l_tmpa_tl - \ior_if_eof:NF \g_mw_read + \etex_readline:D \g_@@_ior to \l_@@_internal_tl + \ior_if_eof:NF \g_@@_ior { \tl_gput_right:cx - { g_mw_write_ \int_use:N \g_mw_stream_int _tl } - { \mw_tex_immediate:w \mw_tex_write:w \g_mw_write { \l_tmpa_tl } } - \mw_immediate_write_aux_readlines: + { g_@@_iow_ \int_use:N \g_@@_stream_int _tl } + { + \@@_tex_immediate:w \@@_tex_write:w \g_@@_iow + { \l_@@_internal_tl } + } + \@@_immediate_write_readlines_loop: } } % \end{macrocode} % \end{macro} +% \end{macro} % % % \subsection{Non-immediate writing} % -% This is trickier, because the expansion of the text for -% a non-immediate \cs{write} takes place immediately after -% the page containing it is shipped out. We store each -% non-immediate \cs{openout}, \cs{write}, or \cs{closeout} -% without expansion in separate token lists -% \cs{g_mw_late_write_}\meta{N}\texttt{_tl} to be used later, -% and instead write |`(|\meta{N}|)| to a file (including the -% strange delimiters). After each shipout, we can read the -% file to see which output operations we need to perform, -% and in what order. -% -% \begin{macro}[aux]{\mw_late_aux:nN} -% Store the action to be done at shipout in a token list, -% and non-immediately write the label \cs{g_mw_late_write_int} -% of the output operation to the temporary file. -% Here, |#1| holds an assignment similar to the lines above it, -% and |#2| holds the relevant immediate action to be performed -% after shipout. -% \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_late_aux:nN #1 #2 +% This is trickier, because the expansion of the text for a +% non-immediate \tn{write} takes place immediately after the page +% containing it is shipped out. We store each non-immediate +% \tn{openout}, \tn{write}, or \tn{closeout} without expansion in +% separate token lists \cs{g_@@_late_write_\meta{stream}_tl} to be used +% later, and instead write |`(|\meta{stream}|)| to a file (including the +% strange delimiters). After each shipout, we can read the file to see +% which output operations we need to perform, and in what order. +% +% ^^A todo: check "_nopar"? +% +% \subsubsection{Replacement for primitives} +% +% \begin{macro}[aux]{\@@_late:n} +% Store the action to be done at shipout in a token list, and +% non-immediately write the label \cs{g_@@_late_write_int} of the +% output operation to the temporary file. Here, |#1| holds an +% assignment similar to the lines above it, and |#2| holds the +% relevant immediate action to be performed after shipout. +% \begin{macrocode} +\cs_new_protected:Npn \@@_late:n #1 { - \int_gincr:N \g_mw_late_write_int - \tl_const:cx { c_mw_late_write_ \int_use:N \g_mw_late_write_int _tl } + \int_gincr:N \g_@@_late_write_int + \tl_const:cx { - \int_gset:Nn \exp_not:N \g_mw_stream_int - { \exp_not:V \g_mw_stream_int } - #1 - \exp_not:N #2 + c_@@_late_write_ + \int_use:N \g_@@_late_write_int + _tl + } + { + \int_gset:Nn \exp_not:N \g_@@_stream_int + { \exp_not:V \g_@@_stream_int } + \exp_not:n {#1} } - \exp_args:NNx \mw_tex_write:w \g_mw_write - { `( \int_use:N \g_mw_late_write_int ) } + \exp_args:NNx \@@_tex_write:w \g_@@_iow + { `( \int_use:N \g_@@_late_write_int ) } } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_openout:w} -% \begin{macro}[aux]{\mw_openout_test:,\mw_openout_aux:} -% \cs{openout} tests if the number to come is among reserved streams. -% If it is, use the primitive, otherwise, parse a filename. +% \begin{macro}[aux]{\@@_openout:w} +% \begin{macro}[aux]{\@@_openout_test:n, \@@_openout_aux:n} +% \tn{openout} tests if the number to come is among reserved streams. +% If it is, use the primitive, otherwise, parse a file name. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_openout:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_openout_test: } -\cs_new_protected_nopar:Npn \mw_openout_test: - { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_openout:w \g_mw_stream_int } - { \mw_parse_equals_filename:Nw \mw_openout_aux: } - } -\cs_new_protected_nopar:Npn \mw_openout_aux: +\cs_new_protected_nopar:Npn \@@_openout:w + { \s_@@ \primargs_get_number:N \@@_openout_test:n } +\cs_new_protected:Npn \@@_openout_test:n #1 { - \mw_late_aux:nN - { - \tl_gset:Nn \exp_not:N \g_mw_filename_tl - { \exp_not:V \g_mw_filename_tl } - } - \mw_immediate_openout_aux: + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_openout:w \g_@@_stream_int } + { \@@_equals_file_name:N \@@_openout_aux:n } } +\cs_new_protected:Npn \@@_openout_aux:n #1 + { \@@_late:n { \@@_immediate_openout_aux:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_write:w} -% \begin{macro}[aux]{\mw_write_test:,\mw_write_aux:} -% Same idea for \cs{write}, except that we parse a text. +% \begin{macro}[aux]{\@@_write:w} +% \begin{macro}[aux]{\@@_write_test:n, \@@_write_aux:n} +% Same idea for \tn{write}, except that we parse a text. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_write:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_write_test: } -\cs_new_protected_nopar:Npn \mw_write_test: +\cs_new_protected_nopar:Npn \@@_write:w + { \s_@@ \primargs_get_number:N \@@_write_test:n } +\cs_new_protected:Npn \@@_write_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_write:w \g_mw_stream_int } - { \mw_parse_text:Nw \mw_write_aux: } - } -\cs_new_protected_nopar:Npn \mw_write_aux: - { - \mw_late_aux:nN - { - \tl_gset:Nn \exp_not:N \g_mw_text_tl - { \exp_not:V \g_mw_text_tl } - } - \mw_immediate_write_aux: + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_write:w \g_@@_stream_int } + { \primargs_get_general_text:N \@@_write_aux:n } } +\cs_new_protected:Npn \@@_write_aux:n #1 + { \@@_late:n { \@@_immediate_write_aux:n {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_closeout:w} -% \begin{macro}[aux]{\mw_closeout_test:,\mw_closeout_aux:} -% Same idea for \cs{closeout}, and we don't need to parse +% \begin{macro}[aux]{\@@_closeout:w} +% \begin{macro}[aux]{\@@_closeout_test:n, \@@_closeout_aux:} +% Same idea for \tn{closeout}, and we don't need to parse % anything else than the number. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_closeout:w - { \mw_scan_stop: \mw_parse_number:Nw \mw_closeout_test: } -\cs_new_protected_nopar:Npn \mw_closeout_test: +\cs_new_protected_nopar:Npn \@@_closeout:w + { \s_@@ \primargs_get_number:N \@@_closeout_test:n } +\cs_new_protected:Npn \@@_closeout_test:n #1 { - \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int - { \mw_tex_closeout:w \g_mw_stream_int } - { \mw_closeout_aux: } + \int_gset:Nn \g_@@_stream_int {#1} + \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1} + { \@@_tex_closeout:w \g_@@_stream_int } + { \@@_closeout_aux: } } -\cs_new_protected_nopar:Npn \mw_closeout_aux: - { \mw_late_aux:nN { } \mw_immediate_closeout_aux: } +\cs_new_protected_nopar:Npn \@@_closeout_aux: + { \@@_late:n { \@@_immediate_closeout_aux: } } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}[aux]{\mw_before_shipout:} -% Immediately before the shipout, we must open the writing stream -% \cs{g_mw_write}. Each delayed output operation has been replaced -% by \cs{write} \cs{g_mw_write} |{`(|\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{macrocode} -\cs_new_protected_nopar:Npn \mw_before_shipout: - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write - \c_mw_tmp_file_tl \scan_stop: - } -% \end{macrocode} -% \end{macro} +% \subsubsection{Shipout business} % -% \begin{macro}[aux]{\mw_after_shipout:} -% After all the \cs{write}s are performed, we read the file, -% and grab the relevant pieces in a seq. -% \begin{macrocode} -\seq_new:N \g_mw_operations_seq -\cs_new_protected_nopar:Npn \mw_after_shipout: - { - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write +% \begin{macro}[aux]{\@@_before_shipout:} +% Immediately before the shipout, we must open the writing stream +% \cs{g_@@_iow}. 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{macrocode} +\cs_new_protected_nopar:Npn \@@_before_shipout: + { + \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow + \g_@@_tmp_file_tl \scan_stop: + } +% \end{macrocode} +% \end{macro} +% +%% ^^A todo: \shipout{\immediate\write4{}} => bug. +% \begin{macro}[aux]{\@@_after_shipout:} +% \begin{macro}[aux, 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} extract 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_nopar: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} } \etex_everyeof:D { `() \exp_not:N } - \tl_gset:Nx \g_mw_operations_seq - { \if_false: } \fi: - \exp_after:wN \mw_after_shipout_loop:ww - \tex_input:D \c_mw_tmp_file_tl \scan_stop: + \exp_args:NNx \group_end: - \seq_map_inline:Nn \g_mw_operations_seq + \tl_map_inline:nn { - \tl_use:c - { c_mw_late_write_ ##1 _tl } + \exp_after:wN \@@_after_shipout_loop:ww + \tex_input:D \g_@@_tmp_file_tl \c_space_tl %^^A bug? } + { \tl_use:c { c_@@_late_write_ ##1 _tl } } } -\cs_new:Npn \mw_after_shipout_loop:ww #1 `( #2 ) +\cs_new:Npn \@@_after_shipout_loop:ww #1 `( #2 ) { - \tl_if_empty:nTF {#2} - { \if_false: { \fi: } } + \tl_if_empty:nF {#2} { - \exp_not:N \seq_item:n {#2} - \mw_after_shipout_loop:ww + {#2} + \@@_after_shipout_loop:ww } } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}[aux]{\shipout} -% \begin{macro}[aux]{\mw_shipout:w} +% \begin{macro}[aux]{\@@_shipout:w} +% \begin{variable}{\g_@@_group_level_int, \g_@@_shipout_box} % If \pkg{atbegshi} is available, patch it by adding -% \cs{mw_after_shipout:} at the right place. Otherwise, -% redefine \cs{shipout} to add a hook. +% \cs{@@_before_shipout:} and \cs{@@_after_shipout:} at the right +% place: the two transformations are needed to cover several versions +% of the package. Otherwise, redefine \tn{shipout} to add a hook (see +% Heiko's \pkg{atbegshi} for details). % \begin{macrocode} \IfFileExists{atbegshi.sty} { @@ -719,120 +736,142 @@ \tl_replace_once:Nnn \AtBegShi@Output { \AtBegShi@OrgShipout \box \AtBeginShipoutBox } { - \mw_before_shipout: + \@@_before_shipout: \AtBegShi@OrgShipout \box \AtBeginShipoutBox - \mw_after_shipout: + \@@_after_shipout: } \tl_replace_once:Nnn \AtBegShi@Output { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox } { - \mw_before_shipout: + \@@_before_shipout: \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox - \mw_after_shipout: + \@@_after_shipout: } } { - \cs_new_protected_nopar:Npn \mw_shipout:w + \int_new:N \g_@@_group_level_int + \box_new:N \g_@@_shipout_box + \cs_new_protected_nopar:Npn \@@_shipout:w { - \int_gset_eq:NN \g_mw_tmpa_int \etex_currentgrouplevel:D - \tex_afterassignment:D \mw_shipout_aux: - \tex_global:D \tex_setbox:D \g_mw_box + \int_gset_eq:NN \g_@@_group_level_int \etex_currentgrouplevel:D + \tex_afterassignment:D \@@_shipout_i: + \tex_global:D \tex_setbox:D \g_@@_shipout_box } - \cs_new_protected_nopar:Npn \mw_shipout_aux: + \cs_new_protected_nopar:Npn \@@_shipout_i: { - \int_compare:nNnTF { \g_mw_tmpa_int } + \int_compare:nNnTF { \g_@@_group_level_int } = { \etex_currentgrouplevel:D } - { \mw_shipout_aux_ii: } - { \tex_aftergroup:D \mw_shipout_aux_ii: } + { \@@_shipout_ii: } + { \tex_aftergroup:D \@@_shipout_ii: } } - \cs_new_protected_nopar:Npn \mw_shipout_aux_ii: + \cs_new_protected_nopar:Npn \@@_shipout_ii: { - \mw_before_shipout: - \mw_tex_shipout:w \tex_box:D \g_mw_box - \mw_after_shipout: + \@@_before_shipout: + \@@_tex_shipout:w \tex_box:D \g_@@_shipout_box + \@@_after_shipout: } - \cs_gset_eq:NN \shipout \mw_shipout:w + \AtBeginDocument { \cs_gset_eq:NN \shipout \@@_shipout:w } } % \end{macrocode} +% \end{variable} % \end{macro} % \end{macro} % % \subsection{Hook at the very end} % -% \begin{macro}[aux]{\mw_close_all:} +% \begin{variable}{\g_@@_at_end_int} +% At the end of the run, we try very hard to put some material at the +% |\@@end|. This integer controls how many times to call +% \cs{@@_close_all_at_end:w}, to avoid infinite loops in case two +% packages compete for that last place. +% \begin{macrocode} +\int_new:N \g_@@_at_end_int +\int_gset:Nn \g_@@_at_end_int { 10 } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[aux]{\@@_close_all:} % At the end of the document, close all the files. % \begin{macrocode} -\cs_new_protected_nopar:Npn \mw_close_all: +\cs_new_protected_nopar:Npn \@@_close_all: { - \prop_map_inline:Nn \g_mw_writes_prop - { - \mw_tex_immediate:w \mw_tex_openout:w \g_mw_write ##2 \scan_stop: - \group_begin: - \int_set_eq:NN \tex_newlinechar:D \c_minus_one - \tl_use:c { g_mw_write_ ##1 _tl } - \tl_gclear:c { g_mw_write_ ##1 _tl } - \group_end: - \mw_tex_immediate:w \mw_tex_closeout:w \g_mw_write - } - \prop_gclear:N \g_mw_writes_prop + \prop_map_function:NN \g_@@_iow_prop + \@@_immediate_write_and_close:nn + \prop_gclear:N \g_@@_iow_prop } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux]{\mw_put_at_end:Nw} +% \begin{macro}[aux]{\@@_close_all_at_end:w} % This pushes its first argument to the very end of the \LaTeX{} run, -% recursively, just in case some other code adds things there. -% If \pkg{atveryend} is available, we use it instead. -% \begin{macrocode} - % \IfFileExists{atveryend.sty} { \use_i:nn } { \use_ii:nn } - % { - % \RequirePackage {atveryend} [2011/06/29] - % \AtVeryVeryEnd { \mw_close_all: } - % } - % { - \cs_new_protected:Npn \mw_put_at_end:Nw #1 #2 \@@end +% recursively (at most $10$ times, initial value of +% \cs{g_@@_at_end_int}), just in case some other code adds things +% there. +% \begin{macrocode} +\cs_set:Npn \@@_tmp:w #1 + { + \cs_new_protected:Npn \@@_close_all_at_end:w ##1 #1 { - \tl_if_empty:nTF {#2} - { #1 \@@end } - { #2 \mw_put_at_end:Nw #1 \@@end } + \int_gdecr:N \g_@@_at_end_int + \int_compare:nNnTF \g_@@_at_end_int > \c_zero + { + \tl_if_empty:nTF {##1} + { ##1 \@@_close_all: } + { ##1 \@@_close_all_at_end:w } + } + { \@@_close_all: ##1 } + #1 } - \AtEndDocument { \mw_put_at_end:Nw \mw_close_all: } - % } + } +\exp_args:Nc \@@_tmp:w { @ @ end } +\AtEndDocument { \@@_close_all_at_end:w } % \end{macrocode} % \end{macro} % % \subsection{Modified \cs{newwrite}} % +% \begin{variable}{\g_@@_alloc_int} +% The counter that \LaTeXe{} uses to allocate \tn{write} registers. +% \begin{macrocode} +\tex_countdef:D \g_@@_alloc_int 17 \scan_stop: +% \end{macrocode} +% \end{variable} +% % \begin{macro}[aux]{\newwrite} % We need to allow \cs{newwrite} to allocate more than $16$ writes, % but beware that $18$ is reserved, and that packages might expect -% $16$ or $17$ to write to the terminal. So instead skip until $20$. +% $16$ or $17$ to write to the terminal. So instead skip until $20$, +% to be on the safe side. +% % ^^A todo: redefine the LaTeX3 things too? % \begin{macrocode} -\countdef \mw_write_int 17 \scan_stop: -\cs_new:Npn \mw_newwrite:N #1 +\cs_new:Npn \@@_newwrite:N #1 { - \if_num:w \mw_write_int = \c_sixteen - \int_gset:Nn \mw_write_int { 20 } + \int_gincr:N \g_@@_alloc_int + \if_num:w \g_@@_alloc_int = \c_sixteen + \int_gset:Nn \g_@@_alloc_int { 20 } \fi: - \int_set_eq:NN \allocationnumber \mw_write_int - \int_gincr:N \mw_write_int + \int_set_eq:NN \allocationnumber \g_@@_alloc_int \cs_undefine:N #1 \int_const:Nn #1 { \allocationnumber } - \wlog {\string #1=\string \write \the \allocationnumber } + \wlog + { + \token_to_str:N #1 + = \token_to_str:N \write \int_use:N \allocationnumber + } } % \end{macrocode} % \end{macro} % % \subsection{Redefining the ``normal'' control sequences} % -% \begin{macro}[aux]{\immediate,\openout,\write,\closeout,\newwrite} -% \cs{shipout} has been redefined earlier. +% \begin{macro}[aux]{\immediate, \openout, \write, \closeout, \newwrite} +% \tn{shipout} has been redefined earlier. % \begin{macrocode} -\cs_gset_eq:NN \immediate \mw_immediate:w -\cs_gset_eq:NN \openout \mw_openout:w -\cs_gset_eq:NN \write \mw_write:w -\cs_gset_eq:NN \closeout \mw_closeout:w -\cs_gset_eq:NN \newwrite \mw_newwrite:N +\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} % |