summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/morewrites
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2012-07-24 16:55:17 +0000
committerKarl Berry <karl@freefriends.org>2012-07-24 16:55:17 +0000
commit3accf11dd41cac5f2c81b83a2a712c77ffab2b6a (patch)
treee1b5a34217c371d2c469c5b9d79fe6b12f3624e4 /Master/texmf-dist/source/latex/morewrites
parentc1841336503cc8eb2355c2eab40012c856f18369 (diff)
morewrites (18jul12)
git-svn-id: svn://tug.org/texlive/trunk@27138 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/morewrites')
-rw-r--r--Master/texmf-dist/source/latex/morewrites/morewrites.dtx1147
-rw-r--r--Master/texmf-dist/source/latex/morewrites/morewrites.ins22
-rw-r--r--Master/texmf-dist/source/latex/morewrites/primargs.dtx619
3 files changed, 1233 insertions, 555 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}
%
diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.ins b/Master/texmf-dist/source/latex/morewrites/morewrites.ins
index 814fd46a489..d9e17d55e00 100644
--- a/Master/texmf-dist/source/latex/morewrites/morewrites.ins
+++ b/Master/texmf-dist/source/latex/morewrites/morewrites.ins
@@ -1,4 +1,23 @@
-\input docstrip.tex
+\iffalse meta-comment
+
+File morewrites.ins Copyright (C) 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
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ http://www.latex-project.org/lppl.txt
+
+This file is part of the "morewrites bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+\fi
+
+\let\jobname\relax
+\input l3docstrip.tex
\askforoverwritefalse
\preamble
@@ -15,5 +34,6 @@ to Bruno Le Floch (blflatex@gmail.com).
% the package
\generate{\file{morewrites.sty}{\from{morewrites.dtx}{package}}}
+\generate{\file{primargs.sty}{\from{primargs.dtx}{package}}}
\endbatchfile
diff --git a/Master/texmf-dist/source/latex/morewrites/primargs.dtx b/Master/texmf-dist/source/latex/morewrites/primargs.dtx
new file mode 100644
index 00000000000..a88bc6169e8
--- /dev/null
+++ b/Master/texmf-dist/source/latex/morewrites/primargs.dtx
@@ -0,0 +1,619 @@
+% \iffalse meta-comment
+%
+%% File: primargs.dtx Copyright (C) 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
+%% 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}
+%</driver|package>
+%<*driver>
+\documentclass[full]{l3doc}
+\newcommand{\proc}[1]{\texttt{#1}}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \textsf{primargs} package: \\
+% Parsing arguments of primitives\thanks{This
+% file has version number v0.1, last revised 2012-07-10.}}
+% \author{Bruno Le Floch}
+% \date{2012-07-10}
+%
+% \maketitle
+% \tableofcontents
+%
+% \begin{documentation}
+%
+% \section{\pkg{primargs} documentation}
+%
+% This \LaTeX{} package is currently used by \pkg{morewrites} when
+% redefining primitives: it allows to read arguments of primitives in
+% place of \TeX{}. Of course, this is much slower than letting \TeX{}
+% do things directly, but it may not be possible.
+%
+% \subsection{Read one token}
+%
+% \begin{variable}{\g_primargs_token}
+% The result of \cs{primargs_read_x_token:N} and
+% \cs{primargs_read_token:N}.
+% \end{variable}
+%
+% \begin{function}{\primargs_read_x_token:N}
+% \begin{syntax}
+% \cs{primargs_read_x_token:N} \meta{function}
+% \end{syntax}
+% Expands tokens recursively with \cs{exp_after:wN}, until
+% encountering a non-expandable token, and calls the \meta{function}
+% afterwards. The non-expandable token found is stored as
+% \cs{g_primargs_token}.
+% \end{function}
+%
+% \begin{function}{\primargs_read_token:N}
+% \begin{syntax}
+% \cs{primargs_read_token:N} \meta{function}
+% \end{syntax}
+% Sets \cs{g_primargs_token} globally to the value of the next token,
+% then calls the \meta{function}.
+% \end{function}
+%
+% \subsection{Grabbing arguments}
+%
+% A whole lot of functions have the same syntax:
+% \begin{syntax}
+% \cs{primargs_get_\meta{foo}:N} \meta{function}
+% \end{syntax}
+% Finds what \TeX{}'s grammar calls a \meta{foo} in the input stream,
+% and feeds its value as a braced argument to the \meta{function}. Here
+% is a list of the functions currently defined:
+% \begin{itemize}
+% \item \cs{primargs_get_number:N}
+% \item \cs{primargs_get_dimen:N}
+% \item \cs{primargs_get_glue:N}
+% \item \cs{primargs_get_mudimen:N} (\emph{not implemented properly})
+% \item \cs{primargs_get_muglue:N}
+% \item \cs{primargs_get_general_text:N}
+% \item \cs{primargs_get_file_name:N}
+% \end{itemize}
+%
+% \subsection{Discarding tokens}
+%
+% \begin{function}{\primargs_remove_token:N}
+% \begin{syntax}
+% \cs{primargs_remove_token:N} \meta{function}
+% \end{syntax}
+% Removes one \meta{token} following the \meta{function}, which is
+% performed next.
+% \end{function}
+%
+% \begin{function}{\primargs_remove_one_optional_space:N}
+% \begin{syntax}
+% \cs{primargs_remove_one_optional_space:N} \meta{function}
+% \end{syntax}
+% Removes \meta{one optional space} after the \meta{function}.
+% \end{function}
+%
+% \begin{function}{\primargs_remove_optional_spaces:N}
+% \begin{syntax}
+% \cs{primargs_remove_optional_spaces:N} \meta{function}
+% \end{syntax}
+% Removes \meta{optional spaces} after the \meta{function}.
+% \end{function}
+%
+% \begin{function}{\primargs_remove_equals:N}
+% \begin{syntax}
+% \cs{primargs_remove_equals:N} \meta{function}
+% \end{syntax}
+% Removes \meta{equals}, namely \meta{optional spaces} followed
+% optionally by an explicit |=| character with category other.
+% \end{function}
+%
+% \subsection{Afterassignment and \tn{globaldefs}}
+%
+% The \tn{globaldefs} parameter is not taken into account yet, and
+% setting it to a non-zero value may make everything crash. It is
+% straightforward to fix that for negative \tn{globaldefs}, but positive
+% \tn{globaldefs} make things complicated.
+%
+% Tokens inserted using \tn{afterassignment} may be lost when using this
+% package, since it uses \tn{afterassignment} internally.
+%
+% \subsection{Internal functions}
+%
+% \begin{function}{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
+% \begin{syntax}
+% \cs{@@_get_rhs:NnN} \meta{register} \Arg{register rhs} \meta{function}
+% \end{syntax}
+% Use the \meta{register} to find a right-hand side of a valid
+% assignment for this type of variable, and feed the value found to
+% the \meta{function}. The value of the \meta{register} is then
+% restored using \meta{register} |=| \meta{register rhs}, where the
+% \meta{register rhs} should be the initial value of the
+% \meta{register}. All those assignments are performed within a
+% group, but some are automatically global, and \tn{globaldefs} may
+% cause trouble with others.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{primargs} implementation}
+%
+%<*package>
+% \begin{macrocode}
+\ProvidesExplPackage
+ {primargs}{2012/07/10}{0.1}{Parsing~arguments~of~primitives}
+\RequirePackage{expl3}
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=primargs>
+% \end{macrocode}
+%
+% \subsection{Helpers}
+%
+% \begin{macro}[aux]{\g_@@_code_tl}
+% Used to contain temporary code.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \g_@@_code_tl { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_no_afterassignment:}
+% Supersedes any \tn{afterassignment} token.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_no_afterassignment:
+ { \tex_afterassignment:D \tex_relax:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_no_localdefs:}
+% This function, which must be called in a group, ensures that
+% \tn{global} indeed makes definitions global.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_no_localdefs:
+ {
+ \@@_no_afterassignment:
+ \int_compare:nNnF \tex_globaldefs:D > \c_zero
+ { \tex_globaldefs:D = \c_zero }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Read token with or without expansion}
+%
+% \TeX{} often calls the \proc{get_x_token} procedure when parsing
+% various parts of its grammar. This expands tokens recursively until
+% reaching a non-expandable token. We emulate this by expanding with
+% \tn{expandafter}, then checking whether the upcoming token is
+% expandable or not using \tn{futurelet}, and if it is, expanding again.
+%
+% One thing to be careful about is that
+% \begin{quote}
+% \tn{expandafter} \tn{show} \tn{noexpand} \cs{space}
+% \end{quote}
+% shows the \tn{meaning} of the \tn{notexpanded:} \cs{space},
+% namely \tn{relax} (frozen, in fact, hence a bit different from the
+% normal \tn{relax}), while expanding twice with
+% \begin{quote}
+% \tn{expandafter} \tn{expandafter} \tn{expandafter} \tn{show}
+% \tn{noexpand} \cs{space}
+% \end{quote}
+% expands the \cs{space} to the underlying space character token.
+% What this means is that we must first check if the token is expandable
+% or not, and only then expand, and that the token should not be queried
+% again using \tn{futurelet}. On this latter point, run
+% \begin{verbatim}
+% \def \test { \show \next \futurelet \next \test }
+% \expandafter \test \noexpand \space
+% \end{verbatim}
+% to see how \cs{next} changes from \tn{relax} to becoming a macro.
+%
+% \begin{macro}{\primargs_read_x_token:N}
+% \begin{macro}[aux]{\@@_read_x_token:N, \@@_read_x_token_ii:N}
+% First query the following token. Then test whether it is
+% expandable, using a variant of the \cs{token_if_expandable:NTF}
+% test.\footnote{This \LaTeX3 test returns \texttt{false} for
+% undefined tokens (by design), but \TeX{}'s \proc{get_x_token}
+% expands those undefined tokens, causing errors, so we should as
+% well.} If the token is expandable, \cs{exp_not:N} will change its
+% \tn{meaning} to \tn{relax}, the test is \texttt{false}, we expand,
+% and call the loop. Otherwise, we stop. Interestingly, we don't
+% ever need to take the user's function as an argument.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_read_x_token:N
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \@@_read_x_token:N
+ }
+\cs_new_protected_nopar:Npn \@@_read_x_token:N
+ {
+ \tex_afterassignment:D \@@_read_x_token_ii:N
+ \tex_global:D \tex_futurelet:D \g_primargs_token
+ }
+\cs_new_protected_nopar:Npn \@@_read_x_token_ii:N
+ {
+ \exp_after:wN
+ \if_meaning:w \exp_not:N \g_primargs_token \g_primargs_token
+ \group_end:
+ \exp_after:wN \use_none:nnn
+ \fi:
+ \exp_after:wN \@@_read_x_token:N \exp_after:wN
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\primargs_read_token:N}
+% The same without expansion, for use when we already know that what
+% follows is expanded.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_read_token:N
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_afterassignment:D \group_end:
+ \tex_global:D \tex_futurelet:D \g_primargs_token
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Removing tokens}
+%
+% \subsubsection{One token}
+%
+% \begin{macro}{\primargs_remove_token:N}
+% Remove token using \tn{let} (note the presence of |=| and a space,
+% to correctly remove explicit space characters), then insert the
+% \meta{function} with \tn{afterassignment}.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_token:N #1
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_aftergroup:D #1
+ \tex_afterassignment:D \group_end:
+ \tex_global:D \tex_let:D \g_primargs_token = ~
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Optional space token}
+%
+% ^^A Not sure of the interaction with \notexpanded:
+%
+% \begin{macro}{\primargs_remove_one_optional_space:N}
+% \begin{macro}[aux]{\@@_remove_one_optional_space:}
+% Start a group: we will insert the \meta{function} at its end.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_one_optional_space:N #1
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_aftergroup:D #1
+ \primargs_read_x_token:N \@@_remove_one_optional_space:
+ }
+\exp_args:NNo \cs_new_protected_nopar:Npn \@@_remove_one_optional_space:
+ {
+ \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \exp_after:wN \primargs_remove_token:N
+ \fi:
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\primargs_remove_optional_spaces:N}
+% \begin{macro}[aux]
+% {\@@_remove_optional_spaces:, \@@_remove_optional_spaces_ii:}
+% Start a group: we will insert the \meta{function} at its end.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_optional_spaces:N #1
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_aftergroup:D #1
+ \@@_remove_optional_spaces:
+ }
+\cs_new_protected_nopar:Npn \@@_remove_optional_spaces:
+ { \primargs_read_x_token:N \@@_remove_optional_spaces_ii: }
+\exp_args:NNo \cs_new_protected_nopar:Npn \@@_remove_optional_spaces_ii:
+ {
+ \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \exp_after:wN \primargs_remove_token:N
+ \exp_after:wN \@@_remove_optional_spaces:
+ \else:
+ \exp_after:wN \group_end:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\primargs_remove_equals:N}
+% Remove \meta{optional spaces}, then test for an explicit |=|, both
+% in \tn{meaning} and as a token list (once we know its \tn{meaning},
+% we can grab it safely).
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_equals:N #1
+ {
+ \group_begin:
+ \tex_aftergroup:D #1
+ \primargs_remove_optional_spaces:N \@@_remove_equals:
+ }
+\cs_new_protected_nopar:Npn \@@_remove_equals:
+ {
+ \if_meaning:w = \g_primargs_token
+ \exp_after:wN \@@_remove_equals_ii:NN
+ \fi:
+ \group_end:
+ }
+\cs_new_protected:Npn \@@_remove_equals_ii:NN #1#2
+ { \tl_if_eq:nnTF { #2 } { = } { #1 } { #1 #2 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Right-hand sides of assignments}
+%
+% The naive approach to reading an integer, or a general text, is to let
+% \TeX{} perform an assignment to a \tn{count}, or a \tn{toks}, register
+% and regain control using \tn{afterassignment}. The question is then
+% to know which \tn{count} or \tn{toks} register to use. One might
+% think that any can be used as long as the assignment happens in a
+% group.
+%
+% However, there comes the question of the \tn{globaldefs} parameter.
+% If this parameter is positive, every assignment is global, including
+% assignments to the parameter itself, preventing us from setting it to
+% zero locally; hence, we are stuck with global assignments (if
+% \tn{globaldefs} is negative, we can change it, locally, to whatever
+% value pleases us, as done by \cs{@@_no_localdefs:}). We may thus not
+% use scratch registers to parse integers, general texts, and other
+% pieces of \TeX{}'s grammar.
+%
+% For integers, we will use \tn{deadcycles}, a parameter which is
+% automatically assigned globally, and we revert it to its previous
+% value afterwards. For general text, we will use \tn{errhelp}, which
+% we will assign locally if possible (\tn{globaldefs} negative or zero),
+% and otherwise reset to be empty.
+%
+% \begin{macro}[int]{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
+% The last two lines of this function are the key: assign to |#1|,
+% then take control using \tn{afterassignment}. After the assignment,
+% we expand the value found, |\tex_the:D #1|, within a brace group,
+% then restore |#1| using its initial value |#2|, and end the group.
+% The earlier use of \cs{tex_aftergroup:D} inserts the \meta{function}
+% |#3| before the brace group containing the value found.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_rhs:NnN #1#2#3
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_aftergroup:D #3
+ \tl_gset:Nn \g_@@_code_tl
+ {
+ \use:x
+ {
+ \exp_not:n { #1 = #2 \group_end: }
+ { \tex_the:D #1 }
+ }
+ }
+ \tex_afterassignment:D \g_@@_code_tl
+ #1 =
+ }
+\cs_generate_variant:Nn \@@_get_rhs:NnN { No }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_number:N}
+% We use the general \cs{@@_get_rhs:NoN}, using the internal register
+% \tn{deadcycles}, for which all assignments are global: thus,
+% restoring its value will not interact badly with groups.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_number:N
+ {
+ \@@_get_rhs:NoN \tex_deadcycles:D
+ { \tex_the:D \tex_deadcycles:D }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_dimen:N}
+% Use \tn{hoffset} as a register since it is not too likely to be
+% changed locally (anyways, which register we use is not that
+% important since normally, \tn{globaldefs} is zero, and everything is
+% done within a group).
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_dimen:N
+ {
+ \@@_get_rhs:NoN \tex_hoffset:D
+ { \tex_the:D \tex_hoffset:D }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_glue:N}
+% Use \tn{topskip}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_glue:N
+ {
+ \@@_get_rhs:NoN \tex_topskip:D
+ { \tex_the:D \tex_topskip:D }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_mudimen:N}
+% There is no such thing as a \meta{mudimen variable}, so we're on our
+% own to parse a \meta{mudimen}. Warn about that problem, and parse a
+% \meta{muglue} instead.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_mudimen:N
+ {
+ \msg_warning:nn { primargs } { get-mudimen }
+ \primargs_get_muglue:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_muglue:N}
+% Use \tn{thinmuskip}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_muglue:N
+ {
+ \@@_get_rhs:NoN \tex_thinmuskip:D
+ { \tex_the:D \tex_thinmuskip:D }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\primargs_get_general_text:N}
+% For a general text, use \tn{errhelp}, since it shouldn't be a big
+% problem if that's changed. We don't revert it to its value, but to
+% be empty (note the extra braces, though, since it's a token
+% register), because it is probably better to have no help than the
+% wrong help hanging around. Besides, \tn{errhelp} is always set
+% for immediate use.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \primargs_get_general_text:N
+ { \@@_get_rhs:NoN \tex_errhelp:D { { } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Get file name}
+%
+% ^^A todo: Figure out when \group_align_safe_begin: is needed.
+%
+% \begin{variable}{\g_@@_file_name_tl}
+% Token list used to build a file name, one character at a time.
+% \begin{macrocode}
+\tl_new:N \g_@@_file_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\primargs_get_file_name:N}
+% Empty the file name (globally), and build it one character at a time.
+% The \meta{function} is added at the end of a group, started here.
+% As described in the \TeX{}book, a \meta{file name} should start with
+% \meta{optional spaces}, which we remove, then character tokens,
+% ending with a non-expandable character or control sequence. After
+% space removal, \cs{g_primargs_token} contains the next token, so no
+% need for \cs{primargs_read_token:N}.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_get_file_name:N #1
+ {
+ \group_begin:
+ \@@_no_localdefs:
+ \tex_aftergroup:D #1
+ \tl_gclear:N \g_@@_file_name_tl
+ \primargs_remove_optional_spaces:N \@@_get_file_name_test:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_get_file_name_test:}
+% The token read is in \cs{g_primargs_token}, and is non-expandable.
+% If it is a control sequence, end the \meta{file name}. If it is a
+% space, the the \meta{file name} ends after its removal. Otherwise,
+% we extract the character from the \tn{meaning} of the \meta{token},
+% which we remove anyways: in that case, we'll recurse.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_get_file_name_test:
+ {
+ \token_if_cs:NTF \g_primargs_token
+ { \@@_get_file_name_end: }
+ {
+ \token_if_eq_charcode:NNTF \c_space_token \g_primargs_token
+ { \primargs_remove_token:N \@@_get_file_name_end: }
+ { \primargs_remove_token:N \@@_get_file_name_char: }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_get_file_name_end:}
+% When the end of the file name is reached, end the group, after
+% expanding the contents of \cs{g_@@_file_name_tl}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_get_file_name_end:
+ { \exp_args:No \group_end: \g_@@_file_name_tl }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_get_file_name_char:}
+% \begin{macro}[aux, EXP]
+% {\@@_get_file_name_char_ii:w, \@@_get_file_name_char_iii:w}
+% With an explicit character, applying \tn{string} would give the
+% character code. Here, implicit characters have to be converted too,
+% so we must work with the \tn{meaning}, which is two or three words
+% separated by spaces, then the character. The \texttt{ii} auxiliary
+% removes the first two words, and duplicates the remainder (either
+% one character, or a word and a character), and the second auxiliary
+% leaves the second piece in the definition (in both cases, the
+% character). Then loop with expansion.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_get_file_name_char:
+ {
+ \tl_gput_right:Nx \g_@@_file_name_tl
+ {
+ \exp_after:wN \@@_get_file_name_char_ii:w
+ \token_to_meaning:N \g_primargs_token
+ \q_stop
+ }
+ \primargs_read_x_token:N \@@_get_file_name_test:
+ }
+\cs_new:Npn \@@_get_file_name_char_ii:w #1 ~ #2 ~ #3 \q_stop
+ { \@@_get_file_name_char_iii:w #3 ~ #3 ~ \q_stop }
+\cs_new:Npn \@@_get_file_name_char_iii:w #1 ~ #2 ~ #3 \q_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \section{Examples}
+%
+% [Old text which I don't want to remove yet.] A few examples of what
+% this package can parse.
+% \begin{itemize}
+% \item \meta{integer} denotes an integer in any form that \TeX{}
+% accepts as the right-hand side of a primitive integer assignment
+% of the form \tn{count}|0=|\meta{integer};
+% \item \meta{equals} is an arbitrary (optional) number of explicit or
+% implicit space characters, an optional explicit equal sign of
+% category other, and further (optional) explicit or implicit space
+% characters;
+% \item \meta{file name} is an arbitrary sequence of explicit or
+% implicit characters with arbitrary category codes (except active
+% characters, which are expanded before reaching \TeX{}'s mouth),
+% ending either with a space character (character code $32$,
+% arbitrary non-active category code, explicit or implicit), which
+% is removed, or with a non-expandable token, with some care needed
+% for the case of a \tn{notexpanded:} expandable token;
+% \item \meta{filler} is an arbitrary combination of tokens each of
+% whose meaning is \tn{relax} or a character with category code
+% $10$;
+% \item \meta{general text} is a \meta{filler}, followed by 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}
+%
+%</package>
+%
+% \end{implementation}
+%
+% \endinput