summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/morewrites
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2011-07-26 23:28:38 +0000
committerKarl Berry <karl@freefriends.org>2011-07-26 23:28:38 +0000
commit0125ad1c56058bbc2872b885a7a4459337020e8f (patch)
tree344f17c21af48e1923066e3388e233d441080bda /Master/texmf-dist/source/latex/morewrites
parent99f9db1a1a470ee103017fc3a72fcaf07e939512 (diff)
new latex package morewrites (14jul11)
git-svn-id: svn://tug.org/texlive/trunk@23245 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/morewrites')
-rw-r--r--Master/texmf-dist/source/latex/morewrites/morewrites.dtx838
-rw-r--r--Master/texmf-dist/source/latex/morewrites/morewrites.ins19
2 files changed, 857 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx
new file mode 100644
index 00000000000..846186617df
--- /dev/null
+++ b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx
@@ -0,0 +1,838 @@
+% \iffalse meta-comment
+%
+%% File: morewrites.dtx Copyright (C) 2011 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
+%%
+%% -----------------------------------------------------------------------
+%
+%<*driver|package>
+\RequirePackage{l3names}
+\GetIdInfo$Id: morewrites.dtx 0.0 2011-07-09 00:00:00Z bruno $
+ {Always room for a new write}%
+%</driver|package>
+%<*driver>
+\RequirePackage{morewrites}
+\documentclass[full]{l3doc}
+\newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo
+\newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo
+\newwrite\foo \newwrite\foo \newwrite\foo \newwrite\foo
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \textsf{morewrites} package: \\
+% Always room for a new \cs{write}\thanks{This
+% file has version number \ExplFileVersion, last revised \ExplFileDate.}}
+% \author{Bruno Le Floch}
+% \date{\ExplFileDate}
+%
+% \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, users simply need to add the line
+% |\usepackage{morewrites}| somewhere near the beginning of the
+% \LaTeX{} file, and the \enquote{no room for a new \cs{write}}
+% error should be gone.
+%
+% 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.
+%
+% \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.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{morewrites} implementation}
+%
+%<*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.
+% \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}
+% \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).
+% \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
+% \end{macrocode}
+% \end{macro}
+% \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
+% \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{macrocode}
+\prop_new:N \g_mw_writes_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{macrocode}
+\newread \g_mw_read
+\tl_const:Nn \c_mw_tmp_file_tl { \jobname.mw }
+\newwrite \g_mw_write
+% \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{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}
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_mw_tmpa_tl,\g_mw_step_int,\g_mw_tmpa_int,\g_mw_box}
+% \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
+% \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{macrocode}
+\int_new:N \g_mw_stream_int
+\tl_new:N \g_mw_filename_tl
+\tl_new:N \g_mw_text_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[aux]{\mw_scan_stop:}
+% A recognizable version of \cs{scan_stop:}.
+% \begin{macrocode}
+\cs_new_eq:NN \mw_scan_stop: \scan_stop:
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Parsing}
+%
+% \begin{macro}[aux]{\mw_parse_number:Nw}
+% Stores a number in \cs{g_mw_stream_int} before performing |#1|.
+% \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
+ {
+ \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 }
+ }
+% \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
+% 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}
+%
+% \begin{macro}[aux]{\mw_immediate:w}
+% This is a little bit subtle: \TeX{}'s \cs{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
+ {
+ \peek_meaning:NT \mw_scan_stop:
+ { \mw_immediate_mw_scan_stop:N }
+ }
+\cs_new_protected_nopar:Npn \mw_immediate_mw_scan_stop:N #1
+ {
+ \str_if_eq:nnTF { #1 } { \mw_scan_stop: }
+ { \mw_immediate_do:NN }
+ { #1 }
+ }
+\cs_new_protected_nopar:Npn \mw_immediate_do:NN #1 #2
+ {
+ \exp_args:Nc \mw_parse_number:Nw
+ {
+ \exp_after:wN \mw_immediate_do_aux: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_ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Immediate closeout}
+%
+% \begin{macro}[aux]{\mw_immediate_closeout_test:}
+% When the user requests to close a stream, we look in
+% \cs{g_mw_reserved_writes_seq} 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:
+ {
+ \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: }
+ }
+% \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{macrocode}
+\cs_new_protected_nopar:Npn \mw_immediate_closeout_aux:
+ {
+ \exp_args:NNV \prop_pop:NnNT
+ \g_mw_writes_prop \g_mw_stream_int \l_mw_tmpa_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
+ }
+ }
+% \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,
+% followed by the file name.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \mw_immediate_openout_test:
+ {
+ \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: }
+ }
+% \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.
+%
+% We then put the stream and its associated filename in the
+% property list, and empty the corresponding token list.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \mw_immediate_openout_aux:
+ {
+ \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 }
+ }
+% \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{macrocode}
+\cs_new_protected_nopar:Npn \mw_immediate_write_test:
+ {
+ \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: }
+ }
+% \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.
+%
+% If the stream \cs{g_mw_stream_int} is not allocated,
+% then write either to the terminal or only to the log file.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \mw_immediate_write_aux:
+ {
+ \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 }
+ }
+ }
+\cs_new_protected_nopar:Npn \mw_immediate_write_aux_readlines:
+ {
+ \mw_etex_readline:w \g_mw_read to \l_tmpa_tl
+ \ior_if_eof:NF \g_mw_read
+ {
+ \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:
+ }
+ }
+% \end{macrocode}
+% \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
+ {
+ \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_gset:Nn \exp_not:N \g_mw_stream_int
+ { \exp_not:V \g_mw_stream_int }
+ #1
+ \exp_not:N #2
+ }
+ \exp_args:NNx \mw_tex_write:w \g_mw_write
+ { `( \int_use:N \g_mw_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{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:
+ {
+ \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:
+ }
+% \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{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:
+ {
+ \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:
+ }
+% \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
+% 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:
+ {
+ \seq_if_in:NVTF \g_mw_reserved_writes_seq \g_mw_stream_int
+ { \mw_tex_closeout:w \g_mw_stream_int }
+ { \mw_closeout_aux: }
+ }
+\cs_new_protected_nopar:Npn \mw_closeout_aux:
+ { \mw_late_aux:nN { } \mw_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}
+%
+% \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
+ \group_begin:
+ \int_set_eq:NN \tex_endlinechar:D \tex_newlinechar: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:
+ \group_end:
+ \seq_map_inline:Nn \g_mw_operations_seq
+ {
+ \tl_use:c
+ { c_mw_late_write_ ##1 _tl }
+ }
+ }
+\cs_new:Npn \mw_after_shipout_loop:ww #1 `( #2 )
+ {
+ \tl_if_empty:nTF {#2}
+ { \if_false: { \fi: } }
+ {
+ \exp_not:N \seq_item:n {#2}
+ \mw_after_shipout_loop:ww
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\shipout}
+% \begin{macro}[aux]{\mw_shipout:w}
+% 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.
+% \begin{macrocode}
+\IfFileExists{atbegshi.sty}
+ {
+ \RequirePackage{atbegshi}
+ \tl_replace_in:Nnn \AtBegShi@Output
+ { \AtBegShi@OrgShipout \box \AtBeginShipoutBox }
+ {
+ \mw_before_shipout:
+ \AtBegShi@OrgShipout \box \AtBeginShipoutBox
+ \mw_after_shipout:
+ }
+ \tl_replace_in:Nnn \AtBegShi@Output
+ { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox }
+ {
+ \mw_before_shipout:
+ \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox
+ \mw_after_shipout:
+ }
+ }
+ {
+ \cs_new_protected_nopar:Npn \mw_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
+ }
+ \cs_new_protected_nopar:Npn \mw_shipout_aux:
+ {
+ \int_compare:nNnTF { \g_mw_tmpa_int }
+ = { \etex_currentgrouplevel:D }
+ { \mw_shipout_aux_ii: }
+ { \tex_aftergroup:D \mw_shipout_aux_ii: }
+ }
+ \cs_new_protected_nopar:Npn \mw_shipout_aux_ii:
+ {
+ \mw_before_shipout:
+ \mw_tex_shipout:w \tex_box:D \g_mw_box
+ \mw_after_shipout:
+ }
+ \cs_gset_eq:NN \shipout \mw_shipout:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Hook at the very end}
+%
+% \begin{macro}[aux]{\mw_close_all:}
+% At the end of the document, close all the files.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \mw_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
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\mw_put_at_end:Nw}
+% 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}
+ \AtVeryVeryEnd { \mw_close_all: }
+ }
+ {
+ \cs_new_protected:Npn \mw_put_at_end:Nw #1 #2 \@@end
+ {
+ \tl_if_empty:nTF {#2}
+ { #1 \@@end }
+ { #2 \mw_put_at_end:Nw #1 \@@end }
+ }
+ \AtEndDocument { \mw_put_at_end:Nw \mw_close_all: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Modified \cs{newwrite}}
+%
+% \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$.
+% \begin{macrocode}
+\countdef \mw_write_int 17 \scan_stop:
+\cs_new:Npn \mw_newwrite:N #1
+ {
+ \if_num:w \mw_write_int = \c_sixteen
+ \int_gset:Nn \mw_write_int { 20 }
+ \fi:
+ \int_set_eq:NN \allocationnumber \mw_write_int
+ \int_gincr:N \mw_write_int
+ \cs_undefine:N #1
+ \int_const:Nn #1 { \allocationnumber }
+ \wlog {\string #1=\string \write \the \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{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
+% \end{macrocode}
+% \end{macro}
+%
+%</package>
+%
+% \end{implementation}
+%
+% \endinput
diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.ins b/Master/texmf-dist/source/latex/morewrites/morewrites.ins
new file mode 100644
index 00000000000..814fd46a489
--- /dev/null
+++ b/Master/texmf-dist/source/latex/morewrites/morewrites.ins
@@ -0,0 +1,19 @@
+\input docstrip.tex
+\askforoverwritefalse
+
+\preamble
+
+Communicate any suggestions for changing this package
+to Bruno Le Floch (blflatex@gmail.com).
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+
+\keepsilent
+
+% the package
+\generate{\file{morewrites.sty}{\from{morewrites.dtx}{package}}}
+
+\endbatchfile