% \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.1 2011-09-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, 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.
%
% \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_once:Nnn \AtBegShi@Output
      { \AtBegShi@OrgShipout \box \AtBeginShipoutBox }
      {
        \mw_before_shipout:
        \AtBegShi@OrgShipout \box \AtBeginShipoutBox
        \mw_after_shipout:
      }
    \tl_replace_once: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} [2011/06/29]
 %     \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
