texlive[43862] Master/texmf-dist: morewrites (14apr17)
commits+karl at tug.org
commits+karl at tug.org
Mon Apr 17 00:19:40 CEST 2017
Revision: 43862
http://tug.org/svn/texlive?view=revision&revision=43862
Author: karl
Date: 2017-04-17 00:19:40 +0200 (Mon, 17 Apr 2017)
Log Message:
-----------
morewrites (14apr17)
Modified Paths:
--------------
trunk/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf
trunk/Master/texmf-dist/doc/latex/morewrites/primargs.pdf
trunk/Master/texmf-dist/source/latex/morewrites/morewrites.dtx
trunk/Master/texmf-dist/source/latex/morewrites/primargs.dtx
trunk/Master/texmf-dist/tex/latex/morewrites/morewrites.sty
trunk/Master/texmf-dist/tex/latex/morewrites/primargs.sty
Added Paths:
-----------
trunk/Master/texmf-dist/doc/latex/morewrites/README.md
Removed Paths:
-------------
trunk/Master/texmf-dist/doc/latex/morewrites/README
Deleted: trunk/Master/texmf-dist/doc/latex/morewrites/README
===================================================================
--- trunk/Master/texmf-dist/doc/latex/morewrites/README 2017-04-16 22:19:27 UTC (rev 43861)
+++ trunk/Master/texmf-dist/doc/latex/morewrites/README 2017-04-16 22:19:40 UTC (rev 43862)
@@ -1,15 +0,0 @@
-----------------------------------------------------------------
-morewrites --- Always room for a new write
-E-mail: blflatex at gmail.com
-Released under the LaTeX Project Public License v1.3c or later
-See http://www.latex-project.org/lppl.txt
-----------------------------------------------------------------
-
-The morewrites package silently hooks into the TeX primitives
-responsible for writing to files to lift the restriction on
-the number of files open at the same time (16). Load the package
-with "\usepackage{morewrites}" as early as possible.
-
-This package uses the l3kernel bundle.
-
-This is work in progress, all suggestions/comments/bug reports are welcome!
Added: trunk/Master/texmf-dist/doc/latex/morewrites/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/morewrites/README.md (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/morewrites/README.md 2017-04-16 22:19:40 UTC (rev 43862)
@@ -0,0 +1,16 @@
+Provide more writing streams in LaTeX
+=====================================
+* E-mail: blflatex at gmail.com
+* Released under the LaTeX Project Public License v1.3c or later
+ See http://www.latex-project.org/lppl.txt
+
+The morewrites LaTeX package should be used when encountering the
+`No room for a new \write ` error. This is done by redefining a
+few TeX primitives related to output.
+
+This package uses the `l3kernel` bundle.
+
+This is work in progress, all suggestions/comments/bug reports
+are welcome! See https://github.com/blefloch/latex-morewrites
+
+
Property changes on: trunk/Master/texmf-dist/doc/latex/morewrites/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf
===================================================================
(Binary files differ)
Modified: trunk/Master/texmf-dist/doc/latex/morewrites/primargs.pdf
===================================================================
(Binary files differ)
Modified: trunk/Master/texmf-dist/source/latex/morewrites/morewrites.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/morewrites/morewrites.dtx 2017-04-16 22:19:27 UTC (rev 43861)
+++ trunk/Master/texmf-dist/source/latex/morewrites/morewrites.dtx 2017-04-16 22:19:40 UTC (rev 43862)
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: morewrites.dtx Copyright (C) 2011-2013 Bruno Le Floch
+%% File: morewrites.dtx Copyright (C) 2011-2017 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,11 +13,10 @@
%
%<*driver>
\RequirePackage{morewrites}
+\ExplSyntaxOn
+\prg_replicate:nn { 300 } { \newwrite \foo }
+\ExplSyntaxOff
\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
-\newcommand{\proc}[1]{\texttt{#1}}
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
@@ -25,10 +24,9 @@
% \fi
%
% \title{The \textsf{morewrites} package: \\
-% Always room for a new \cs{write}\thanks{This
-% file has version number v0.2e, last revised 2013/01/08.}}
+% Always room for a new \tn{write}}
% \author{Bruno Le Floch}
-% \date{2013/01/08}
+% \date{2017/04/10}
%
% \maketitle
% \tableofcontents
@@ -37,54 +35,118 @@
%
% \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
+% This \LaTeX{} package is a solution for the errors
+% \begin{itemize}
+% \item \enquote{no room for a new \tn{write}},
+% \item \enquote{no room for a new \tn{read}},
+% \end{itemize}
+% which occur when a document reserves too many streams to write data
+% to various auxiliary files or read from them.
+% It is in principle possible to rewrite other packages so that they
% are less greedy on resources, but that is often unpractical for the
% end-user. Instead, \pkg{morewrites} hooks at the lowest level (\TeX{}
-% primitives). 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.
+% primitives).
%
-% 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.
+% Simply add the line |\usepackage{morewrites}| near the beginning of
+% your \LaTeX{} file's preamble: the \enquote{no room for a new
+% \tn{write}/\tn{read}} error should vanish. If it does not, please
+% contact me so that I can correct the problem. This can be done by
+% posting a question on the \url{tex.stackexchange.com} question and
+% answers website, logging an issue on GitHub
+% (\url{https://github.com/blefloch/latex-morewrites}), or emailing me a
+% minimal file showing the problem.
%
-% 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.
+% Notes.
+% \begin{itemize}
+% \item This package loads the \pkg{expl3} package, hence the
+% \pkg{l3kernel} bundle needs to be up to date.
+% \item This package uses an auxiliary file, \meta{job~name}|.mw|,
+% which can safely be deleted. Versions from 2015 and later will only
+% use the auxiliary file if it is originally empty, to avoid
+% destroying data (such as |.mw| files used by Maple). This means
+% that |.mw| files generated by versions before 2015 should be
+% deleted by hand.
+% \item \LuaTeX{} allows $128$ \tn{write} streams, so this package does
+% nothing (with a warning) when used with \LuaTeX{}.
+% \end{itemize}
%
-% \section{Known deficiencies and open questions}
+% \subsection{Commands defined or altered by \pkg{morewrites}}
%
-% 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.
+% \begin{function}[added = 2014-07-26]{\morewritessetup}
+% \begin{syntax}
+% \cs{morewritessetup} \Arg{key--value list}
+% \end{syntax}
+% Sets the options described by the \meta{key--value list}.
+% \end{function}
%
-% The current code trims spaces at the end of every line that is written
-% to a file. I might be able to change the code to avoid this.
+% \begin{function}[added = 2017-04-10]{allocate}
+% \begin{syntax}
+% \cs{morewritessetup} |{| |allocate| |=| \meta{integer} |}|
+% \end{syntax}
+% Sets to (at least) \meta{integer} the number of \tn{write} streams
+% allocated to the inner workings of \pkg{morewrites}. By default
+% this is zero but increasing this value may speed up
+% \pkg{morewrites}.
+% \end{function}
%
-% 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.}
+% \begin{function}[added = 2014-07-26, updated = 2015-08-01]{file}
+% \begin{syntax}
+% \cs{morewritessetup} |{| |file| |=| \meta{file name} |}|
+% \end{syntax}
+% Sets (globally) the name of the file which will be used by internal
+% processes of \pkg{morewrites}. The file name is \tn{jobname}|.mw|
+% by default (technically, \cs{c_sys_jobname_str}|.mw|). Contrarily
+% to earlier versions of \pkg{morewrites} non-empty files will not be
+% overwritten; this design choice may lead to unwanted |.mw| files
+% remaining.
+% \end{function}
%
-% Things that I need to do.
-% \begin{itemize}
-% \item Redefine the \LaTeX3 functions to use \pkg{morewrites} too?
-% \item Should \tn{newwrite} be protected?
-% \end{itemize}
+% \begin{function}[updated = 2015-08-01]{\newread, \newwrite}
+% These macros are redefined by \pkg{morewrites}.
+% Since \pkg{morewrites} allows more than~$16$ read/write streams, it
+% removes the corresponding restrictions in \tn{newread} and
+% \tn{newwrite}.
+% \begin{texnote}
+% The revised \tn{newread} and \tn{newwrite} allocate stream
+% numbers starting at $19$. This might break some
+% code that expects stream numbers to be less than $16$.
+% \end{texnote}
+% \end{function}
%
+% \begin{function}[updated = 2015-08-01]{\immediate}
+% This primitive is altered by \pkg{morewrites}, to detect a following
+% \tn{write} or \tn{openout} or \tn{closeout} and perform the
+% appropriate action.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-01]{\openin, \read, \readline, \closein, \openout, \write, \closeout}
+% These seven primitives are altered by \pkg{morewrites} so that they
+% accept stream numbers outside the normal range $[0,15]$ and
+% open/read/write/close files as appropriate.
+% \begin{texnote}
+% System calls using \tn{write}|18| are detected and forwarded to the engine.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\shipout}
+% This primitive is altered by \pkg{morewrites} to ensure that delayed
+% \tn{openout}, \tn{write} and \tn{closeout} commands are performed at
+% \tn{shipout} time, and in the correct order.
+% \end{function}
+%
+% \subsection{Known deficiencies and open questions}
+%
+% See the bug tracker \url{https://github.com/blefloch/latex-morewrites/issues/}
+% for a list of issues with \pkg{morewrites}.
+%
+% The package code is not good \pkg{expl3} code. \emph{Do not take this
+% package as an example of how to code with \pkg{expl3}; go and see
+% Joseph Wright's \pkg{siunitx} instead.} It uses
+% \cs[no-index]{\ldots{}:D} primitives directly (the |:D| stands for
+% \enquote{do not use}). This is unavoidable in order to hook into the
+% primitives \tn{immediate}, \tn{write}, \emph{etc.\@} and to keep a
+% very strong control on what every command does.
+%
% \end{documentation}
%
% \begin{implementation}
@@ -93,13 +155,25 @@
%
%<*package>
% \begin{macrocode}
-\RequirePackage {expl3} [2012/08/14]
-\RequirePackage {primargs} [2013/01/08]
+\RequirePackage {expl3} [2017/03/18]
+\RequirePackage {primargs} [2017/04/10]
\ProvidesExplPackage
- {morewrites} {2013/01/08} {0.2e} {Always room for a new write}
+ {morewrites} {2017/04/10} {} {Always room for a new write}
% \end{macrocode}
%
+% Quit early under \LuaTeX{}.
% \begin{macrocode}
+\sys_if_engine_luatex:T
+ {
+ \cs_new_protected:Npn \morewritessetup #1 { }
+ \msg_new:nnn { morewrites } { luatex }
+ { The~morewrites~package~is~unnecessary~in~LuaTeX. }
+ \msg_warning:nn { morewrites } { luatex }
+ \tex_endinput:D
+ }%
+% \end{macrocode}
+%
+% \begin{macrocode}
%<@@=morewrites>
% \end{macrocode}
%
@@ -106,64 +180,47 @@
% \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).
+% restriction of only having $16$ files open for reading at the same
+% time, and the same restriction for writing. This requires patching
+% the primitives \tn{openin}, \tn{read}, \tn{readline}, \tn{closein},
+% \tn{immediate}, \tn{openout}, \tn{write}, \tn{closeout}, and
+% \tn{shipout}, and the macros \tn{newread} and \tn{newwrite} present
+% in plain \TeX{} and \LaTeXe{}.
%
-% 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.
+% The \pkg{morewrites} package should be loaded as early as possible, so
+% that any package loaded later uses the redefined macros instead of the
+% primitives. However, the format (plain \TeX{} or \LaTeXe{}) and the
+% \pkg{expl3} programming language are always loaded before
+% \pkg{morewrites}, and their interaction must be carefully monitored.
%
-% 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.
+% Henceforth, ``\TeX{} stream'' will refer to stream numbers in the
+% range $[0,15]$ provided to \TeX{}'s read/write primitives, while
+% ``user stream'' will denote stream numbers in $[0,15]\cup[19,\infty)$
+% manipulated by the redefined \tn{openin}, \tn{read}, \tn{readline},
+% \tn{closein}, \tn{openout}, \tn{write}, \tn{closeout}, \tn{newread},
+% and \tn{newwrite}. A user stream in $[0,15]$ (reserved by \LaTeXe{}
+% or allocated by \pkg{expl3}) is mapped to the same \TeX{} stream
+% number, while a user stream in $[19,\infty)$ is mapped to a \TeX{}
+% stream according to the property lists (with integer keys and values)
+% \cs{l_@@_read_prop} and \cs{l_@@_write_prop}. Stream numbers $16$,
+% $17$ and $18$ are unused because \tn{write}16 is often used to write
+% to the terminal, and \tn{write}18 sends its argument to a shell.
%
-% 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.
-%
+% The primitives \tn{openin}, \tn{read}, \tn{readline}, \tn{closein},
+% \tn{openout}, \tn{write}, and \tn{closeout} expect to be followed by
+% an \meta{integer}, normally in the range $[0,15]$, then some further
+% arguments.
+% \begin{quote}
+% \tn{openin} \meta{integer} \meta{equals} \meta{file name} \\
+% \tn{read} \meta{integer} \texttt{to} \meta{control sequence} \\
+% \tn{readline} \meta{integer} \texttt{to} \meta{control sequence} \\
+% \tn{closein} \meta{integer} \\
+% \tn{openout} \meta{integer} \meta{equals} \meta{file name} \\
+% \tn{write} \meta{integer} \meta{filler} \meta{general text} \\
+% \tn{closeout} \meta{integer}
+% \end{quote}
% All of the primitives above perform full expansion of all tokens when
-% looking for their operands. In most cases, only the \tn{meaning} of
-% tokens encountered in this way matters. Specifically,
+% looking for their operands.
% \begin{itemize}
% \item \meta{integer} denotes an integer in any form that \TeX{}
% accepts as the right-hand side of a primitive integer assignment of
@@ -180,7 +237,7 @@
% 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$;
+% meaning is \tn{relax} or whose category code is $10$;
% \item \meta{general text} is formed of braced tokens, starting with an
% explicit or implicit begin-group character, and ending with the
% matching explicit end-group character (both with any character
@@ -189,46 +246,120 @@
% assignment of the form \tn{toks}|0=|\meta{general text}.
% \end{itemize}
%
-% \subsection{Variants}
+% The \pkg{morewrites} package redefines these seven control sequences
+% to expect a user stream number rather than a \TeX{} stream number as
+% the \meta{integer}, then map such a user stream to a \TeX{} stream to
+% call the primitive with the appropriate argument. The primitive
+% \tn{immediate} must also be redefined to detect \tn{openout},
+% \tn{write}, and \tn{closeout} and make them immediate, while still
+% working with other primitives that can be made immediate. Finally,
+% \tn{newread} and \tn{newwrite} must be patched to allocate stream
+% numbers beyond $15$.
%
-% \begin{macro}[aux]{\prop_gpop:NVNT}
-% We need this function later.
-% \begin{macrocode}
-\cs_generate_variant:Nn \prop_gpop:NnNT { NV }
-% \end{macrocode}
-% \end{macro}
+% A few comments on the behaviour of primitives concerning the
+% \meta{integer} (\TeX{} stream). The primitives \tn{openin} and
+% \tn{openout} trigger errors if the \meta{integer} is not in $[0,15]$.
+% The primitives \tn{read} and \tn{readline} prompt the user for input
+% if the \TeX{} stream is closed or beyond $[0,15]$, with no explicit
+% prompt if the stream number is negative. The primitive \tn{write}
+% outputs to the log if the \meta{integer} is negative, and to the
+% terminal if the \TeX{} stream is closed or greater than $15$, with the
+% exception of \tn{write}|18| which runs code in a shell. The
+% primitives \tn{closein} and \tn{closeout} trigger errors if the
+% \meta{integer} is not in $[0,15]$ and silently do nothing if the
+% \TeX{} stream is not open, with the exception of \tn{closeout}|18|
+% which causes a segfault at least in some versions.
%
-% \subsection{Renaming primitives (again)}
+% By default, \tn{openout}, \tn{write} and \tn{closeout} are recorded in
+% a whatsit node in the current list, and will be performed when the box
+% containing the whatsit node is sent to the final \texttt{pdf},
+% \emph{i.e.}, at \enquote{shipout} time. In particular, the
+% \meta{general text} for the \tn{write} primitive is expanded at
+% shipout time. This behaviour may be modified by putting
+% \tn{immediate} before any of these three primitives to force
+% \TeX{} to perform the action immediately instead of recording it in a
+% whatsit node.
%
-% \begin{macro}[int]
+% Since the \tn{openout}, \tn{write}, and \tn{closeout} primitives
+% operate at \tn{shipout} time, we will have to hook into this primitive
+% too. It expects to be followed by a box specification, for instance
+% \tn{box}\meta{integer} or \tn{hbox}\Arg{material to typeset}.
+%
+% Finally, the \tn{newread} and \tn{newwrite} macros expect one token as
+% their argument, and define this token (with \tn{chardef}) to be an
+% integer corresponding to the first available (\TeX{}) read/write
+% stream. This must be extended to allocate higher (user) streams.
+%
+% \subsection{Preliminaries}
+%
+% \subsubsection{Copying some commands}
+%
+% \begin{macro}[aux]
% {
% \@@_tex_immediate:w ,
% \@@_tex_openout:w ,
% \@@_tex_write:w ,
-% \@@_tex_closeout:w
+% \@@_tex_closeout:w ,
+% \@@_tex_openin:w ,
+% \@@_tex_read:w ,
+% \@@_tex_readline:w ,
+% \@@_tex_closein:w ,
% }
-% First save the output-related primitives.
+% Aliases for the read- and write-related primitives, to avoid
+% having |:D| throughout the code.
% \begin{macrocode}
\cs_new_eq:NN \@@_tex_immediate:w \tex_immediate:D
\cs_new_eq:NN \@@_tex_openout:w \tex_openout:D
\cs_new_eq:NN \@@_tex_write:w \tex_write:D
\cs_new_eq:NN \@@_tex_closeout:w \tex_closeout:D
+\cs_new_eq:NN \@@_tex_openin:w \tex_openin:D
+\cs_new_eq:NN \@@_tex_read:w \tex_read:D
+\cs_new_eq:NN \@@_tex_readline:w \etex_readline:D
+\cs_new_eq:NN \@@_tex_closein:w \tex_closein: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{macro}[aux]{\@@_tex_newread:N, \@@_tex_newwrite:N}
+% Copy \tn{newread} and \tn{newwrite} but making sure that they are
+% not \tn{outer}. These copies will not be affected by redefinitions
+% of \tn{newread} and \tn{newwrite} later on.
% \begin{macrocode}
-\cs_new_eq:NN \@@_tex_shipout:w \tex_shipout:D
+\exp_args:NNf \cs_new_protected:Npn \@@_tex_newread:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
+\exp_args:NNf \cs_new_protected:Npn \@@_tex_newwrite:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
% \end{macrocode}
% \end{macro}
%
-% \subsection{Variables}
+% \subsubsection{Variants}
%
-% \begin{variable}{\g_@@_late_write_int}
-% The integer \cs{g_@@_late_write_int} labels the various
+% \begin{macro}[aux]{\prop_gpop:NVNT, \prop_gput:NVx, \tl_gput_right:Nv}
+% We need these variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \prop_gpop:NnNT { NV }
+\cs_generate_variant:Nn \prop_gput:Nnn { NVx }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nv }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Variables}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Used for temporary scratch purposes.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\@@_tmp:w}
+% Used for temporary definitions.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_tmp:w ?
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\g_@@_later_int}
+% The integer \cs{g_@@_later_int} labels the various
% non-immediate operations in the order in which they appear in the
% source. We can never reuse a number because there is no way to know
% if a whatsit was recorded in a box register, which could be reused
@@ -236,25 +367,81 @@
% \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}
+% \tn{shipout} \tn{copy} \cs{l_my_box}
+% \tn{shipout} \tn{copy} \cs{l_my_box}
% \end{quote}
% will print \meta{text} to the terminal twice.
% \begin{macrocode}
-\int_new:N \g_@@_late_write_int
+\int_new:N \g_@@_later_int
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{\g_@@_iow_prop}
-% The property list \cs{g_@@_iow_prop} associates a file name to each
-% open stream.
+% ^^A todo: populate \g_@@_write_seq because we don't use \newwrite; this should be done in \morewritessetup
+% \begin{variable}[aux]{\g_@@_read_seq, \g_@@_write_seq}
+% Keep track of \TeX{} stream numbers managed by \pkg{morewrites} that
+% are currently not in use as user streams.
% \begin{macrocode}
-\prop_new:N \g_@@_iow_prop
+\seq_new:N \g_@@_read_seq
+\seq_new:N \g_@@_write_seq
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}{\g_@@_iow, \g_@@_ior, \g_@@_tmp_file_tl}
-% The expansion that \tn{write} performs is impossible to emulate with
+% \begin{variable}[aux]{\g_@@_read_prop, \g_@@_write_prop}
+% Map user streams to \TeX{} streams.
+% \begin{macrocode}
+\prop_new:N \g_@@_read_prop
+\prop_new:N \g_@@_write_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\g_@@_write_file_prop}
+% Map user streams with no associated \TeX{} streams to file names.
+% \begin{macrocode}
+\prop_new:N \g_@@_write_file_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\l_@@_code_tl}
+% Stores the code to run after finding a user stream, in
+% \cs{@@_get_user:n}.
+% \begin{macrocode}
+\tl_new:N \l_@@_code_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\l_@@_user_int, \l_@@_tstr_tl}
+% The user stream number following redefined primitives is stored in
+% \cs{l_@@_user_int} (see \cs{@@_get_user:N}). The corresponding
+% \TeX{} stream number is eventually stored in \cs{l_@@_tstr_tl} (a
+% token list).
+% \begin{macrocode}
+\int_new:N \l_@@_user_int
+\tl_new:N \l_@@_tstr_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\l_@@_tstr_token}
+% This token is given as an argument to \cs{@@_tex_newread:N} or
+% \cs{@@_tex_newwrite:N}.
+% \begin{macrocode}
+\cs_new_eq:NN \l_@@_tstr_token ?
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[aux]{\s_@@}
+% A recognizable version of \cs{scan_stop:}. This is inspired
+% by\footnote{Historically, this might have happened the other way
+% around, since the author of this package is also on the \LaTeX3
+% Team.} scan marks (see the \pkg{l3quark} module of \LaTeX3), but
+% \cs{__scan_new:N} is not used directly, since it is currently
+% internal to \LaTeX3.
+% \begin{macrocode}
+\cs_new_eq:NN \s_@@ \scan_stop:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}[aux]{\g_@@_iow, \g_@@_ior}
+% The expansion that \tn{write} performs is impossible to emulate (in \XeTeX{} at least) with
% anything else than \tn{write}. We will write on the stream
% \cs{g_@@_iow} to the file \cs{g_@@_tmp_file_tl} and read back from
% it in the stream \cs{g_@@_ior} for things to work properly.
@@ -263,309 +450,566 @@
% \begin{macrocode}
\newwrite \g_@@_iow
\newread \g_@@_ior
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[aux]{\g_@@_tmp_file_tl, \g_@@_tmp_file_bool}
+% Temporary file used to do the correct expansion for each \tn{write}.
+% Boolean indicating whether we have already checked that the file can
+% be used by \pkg{morewrites}: before using a file, the
+% \pkg{morewrites} package now checks it is empty, so as to avoid
+% clobbering user data.
+% \begin{macrocode}
\tl_new:N \g_@@_tmp_file_tl
-\tl_gset:Nn \g_@@_tmp_file_tl { \jobname.mw }
+\bool_new:N \g_@@_tmp_file_bool
+\bool_gset_false:N \g_@@_tmp_file_bool
% \end{macrocode}
% \end{variable}
%
-% \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{variable}[aux]{\g_@@_group_level_int}
+% The group level when \tn{shipout} is called: this is used to
+% distinguish between explicit boxes and box registers.
% \begin{macrocode}
-\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}
+\int_new:N \g_@@_group_level_int
% \end{macrocode}
% \end{variable}
%
-% \begin{variable}[int]{\g_@@_stream_int}
-% An integer holding the \meta{number} argument of various primitives,
-% namely a writing stream.
+% \begin{variable}[aux]{\g_@@_shipout_box}
+% The page to be shipped out.
% \begin{macrocode}
-\int_new:N \g_@@_stream_int
+\box_new:N \g_@@_shipout_box
% \end{macrocode}
% \end{variable}
%
-% \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.
+% \subsubsection{Helpers for auxiliary file}
+%
+% \begin{macro}{\@@_set_file:n}
+% Sets \cs{g_@@_tmp_file_tl} to the given value (initially
+% \cs{c_sys_jobname_str}|.mw|). Mark that the file has not been
+% checked.
% \begin{macrocode}
-\cs_new_eq:NN \s_@@ \scan_stop:
+\cs_new_protected:Npn \@@_set_file:n #1
+ {
+ \bool_gset_false:N \g_@@_tmp_file_bool
+ \tl_gset:Nn \g_@@_tmp_file_tl {#1}
+ }
% \end{macrocode}
% \end{macro}
%
-% \begin{variable}[int]{\l_@@_internal_tl}
-% Temporary token list, used for scratch purposes.
+% \begin{macro}[aux]{\@@_empty_file:n}
+% Empties the file \cs{g_@@_tmp_file_tl} by opening it and closing it
+% right away. This is used when performing \tn{immediate}
+% \tn{openout}. It is also used to ensure the file used by
+% \pkg{morewrites} is left empty. We do this every time the auxiliary
+% file is used, in case that run ends with an error mid-document.
% \begin{macrocode}
-\tl_new:N \l_@@_internal_tl
+\cs_new_protected:Npn \@@_empty_file:n #1
+ {
+ \@@_tex_immediate:w \@@_tex_openout:w
+ \g_@@_iow = #1 \scan_stop:
+ \@@_tex_immediate:w \@@_tex_closeout:w
+ \g_@@_iow
+ }
% \end{macrocode}
-% \end{variable}
+% \end{macro}
%
-% \subsection{Parsing}
+% \begin{macro}[aux,TF]{\@@_if_file_trivial:n}
+% True if the file does not exist, or if it is empty.
+% Only the \texttt{TF} variant is defined. We set
+% \cs{@@_tmp:w} to \cs{prg_return_true:} or \cs{prg_return_false:}
+% within the group and use it after cleaning up. The first
+% \texttt{eof} test is \texttt{true} if the file does not exist.
+% Then we read one line, the second \texttt{eof} test is
+% \texttt{true} if the file was empty (it is \texttt{false} if
+% the file contained anything, even a single space).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_file_trivial:n #1 { TF }
+ {
+ \group_begin:
+ \tex_openin:D \g_@@_ior = #1 \scan_stop:
+ \if_eof:w \g_@@_ior
+ \cs_gset_eq:NN \@@_tmp:w \prg_return_true:
+ \else:
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \etex_readline:D \g_@@_ior to \l_@@_internal_tl
+ \if_eof:w \g_@@_ior
+ \cs_gset_eq:NN \@@_tmp:w \prg_return_true:
+ \else:
+ \cs_gset_eq:NN \@@_tmp:w \prg_return_false:
+ \fi:
+ \fi:
+ \tex_closein:D \g_@@_ior
+ \group_end:
+ \@@_tmp:w
+ }
+% \end{macrocode}
+% \end{macro}
%
-% \begin{macro}[aux]{\@@_equals_file_name:N}
+% \begin{macro}[aux]{\@@_chk_file:}
+% Check that the file \cs{g_@@_tmp_file_tl} does not exist or is blank.
+% If not, try the file name obtained by adding |.mw|.
+% This avoids clobbering files that the user would not want to lose.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_file:
+ {
+ \@@_if_file_trivial:nTF { \g_@@_tmp_file_tl }
+ { \bool_gset_true:N \g_@@_tmp_file_bool }
+ {
+ \msg_warning:nnxx { morewrites } { file-exists }
+ { \g_@@_tmp_file_tl }
+ { \g_@@_tmp_file_tl .mw }
+ \tl_gput_right:Nn \g_@@_tmp_file_tl { .mw }
+ \@@_chk_file:
+ }
+ }
+\msg_new:nnnn { morewrites } { file-exists }
+ { File~'#1'~exists,~using~'#2'~instead. }
+ {
+ The~file~`#1'~exists~and~was~not~created~by~this~version~of~the~
+ `morewrites'~package.~Please~move~or~delete~that~file,~or~provide~
+ another~file~name~by~adding
+ \\ \\
+ \iow_indent:n { \iow_char:N\\morewritessetup~{~file~=~other-name~} }
+ \\ \\
+ to~your~source~file.~In~the~meantime,~the~file~`#2'~will~be~used.
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Parsing and other helpers}
+%
+% \begin{macro}[aux]{\@@_equals_file:N}
% Most of the parsing for primitive arguments is done using
% \pkg{primargs}, except for one case we care about: after its
% \meta{number} argument, the \tn{openout} primitive expects an
% \meta{equals} (optional spaces and |=|) and a \meta{file name}.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_equals_file_name:N #1
+\cs_new_protected:Npn \@@_equals_file:N #1
{
\group_begin:
+ \tex_aftergroup:D \primargs_get_file_name:N
\tex_aftergroup:D #1
- \primargs_remove_equals:N \@@_parse_file_name:
+ \primargs_remove_equals:N \group_end:
}
-\cs_new_protected_nopar:Npn \@@_parse_file_name:
- { \primargs_get_file_name:N \group_end: }
% \end{macrocode}
% \end{macro}
%
-% \subsection{Immediate (writing)}
+% \begin{macro}[aux]{\@@_get_user:n}
+% \pkg{primargs} commands only take \texttt{N}-type arguments, but we
+% often need to find an integer, save it in \cs{l_@@_user_int}, and
+% run some code |#1|. This is analogous to \cs{primargs_get_number:N}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_user:n #1
+ {
+ \tl_set:Nn \l_@@_code_tl {#1}
+ \tex_afterassignment:D \l_@@_code_tl
+ \l_@@_user_int =
+ }
+% \end{macrocode}
+% \end{macro}
%
-% In the context of immediate writing, we can store the text
-% in a token list, and only write it at the corresponding
-% \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 \tn{immediate}}
-%
-% \begin{macro}[aux, updated = 2012-12-05]{\@@_immediate:w}
-% \begin{macro}[aux, updated = 2012-12-05]{\@@_immediate_ii:}
-% \begin{macro}[aux]{\@@_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. 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). If absent, what
-% follows may be a command that should not appear after
-% \tn{immediate}, but it may also be a non-\TeX\ primitive such as
-% \tn{pdfobj} that the \pkg{morewrite} does not know about; hence we
-% must still call the primitive \tn{immediate}.
+% \begin{macro}[aux]{\@@_user_to_tstr:NTF}
+% The goal is to go from a user stream \cs{l_@@_user_int} to a \TeX{}
+% stream \cs{l_@@_tstr_tl} (it defaults to the user stream). Streams
+% less than $19$ are not managed by \pkg{morewrites}: actual \TeX{}
+% streams in $[0,15]$; negative for writing to \texttt{log} and
+% reading without prompt; $16$, $17$ for writing to terminal and
+% reading with prompt; $18$ for shell escape. Larger stream numbers
+% are looked up in the property list |#1|, either \cs{g_@@_read_prop}
+% or \cs{g_@@_write_prop}. If present, use the corresponding value as
+% the \TeX{} stream, otherwise run the \texttt{false} branch.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_immediate:w
- { \primargs_read_x_token:N \@@_immediate_ii: }
-\cs_new_protected_nopar:Npn \@@_immediate_ii:
+\cs_new_protected:Npn \@@_user_to_tstr:NTF #1
{
- \token_if_eq_meaning:NNTF \g_primargs_token \s_@@
- { \@@_immediate_iii:N }
- { \@@_tex_immediate:w }
+ \tl_set:NV \l_@@_tstr_tl \l_@@_user_int
+ \int_compare:nNnTF { \l_@@_user_int } < { 19 }
+ { \use_i:nn }
+ { \prop_get:NVNTF #1 \l_@@_user_int \l_@@_tstr_tl }
}
-\cs_new_protected:Npn \@@_immediate_iii:N #1
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}[aux]{\l_@@_collect_next_int}
+% \begin{macro}[aux]
+% {
+% \@@_collect:x, \@@_collect_aux:Nn,
+% \@@_collect_aux:cf, \@@_collect_gput_right:N, \@@_collect_gput_right:c
+% }
+% When encountering very large \tn{write} statements we may need to
+% collect many lines. This can easily become an $O(n^2)$ task, and
+% here we make sure that it remains around $O(n\log n)$, with a large
+% constant unfortunately. Each of the token lists
+% \cs[no-index]{l_@@_$k$_tl} is empty or contains $2^k$ lines. As
+% lines accumulate, they move to token lists with larger values
+% of~$k$, and eventually all are combined. The integer
+% \cs{l_@@_collect_next_int} is (one plus) the maximal $k$ among
+% non-empty token lists.
+% \begin{macrocode}
+\int_new:N \l_@@_collect_next_int
+\cs_new_protected:Npn \@@_collect:x #1
{
- \tl_if_eq:nnTF { #1 } { \s_@@ }
- { \@@_immediate_iv:NN }
- { #1 }
+ \tl_set:Nx \l_@@_internal_tl {#1}
+ \@@_collect_aux:cf { l_@@_0_tl } { 1 }
}
-\cs_new_protected:Npn \@@_immediate_iv:NN #1 #2
+\cs_new_protected:Npn \@@_collect_aux:Nn #1#2
{
- \exp_args:Nc #1
+ \int_compare:nNnT {#2} > \l_@@_collect_next_int
{
- \exp_after:wN \@@_immediate_v:w
- \token_to_str:N #2
+ \tl_clear_new:N #1
+ \int_set:Nn \l_@@_collect_next_int {#2}
}
+ \tl_if_empty:NTF #1
+ { \tl_set_eq:NN #1 \l_@@_internal_tl }
+ {
+ \tl_put_left:No \l_@@_internal_tl {#1}
+ \tl_clear:N #1
+ \@@_collect_aux:cf { l_@@_#2_tl }
+ { \int_eval:n { #2 + 1 } }
+ }
}
-\use:x
+\cs_generate_variant:Nn \@@_collect_aux:Nn { cf }
+\cs_new_protected:Npn \@@_collect_gput_right:N #1
{
- \cs_new:Npn \exp_not:N \@@_immediate_v:w
- ##1 \tl_to_str:n { @@ } { @@_immediate }
+ \int_compare:nNnF \l_@@_collect_next_int = 0
+ {
+ \int_decr:N \l_@@_collect_next_int
+ \tl_gput_right:Nv #1
+ {
+ l_@@_
+ \int_use:N \l_@@_collect_next_int
+ _tl
+ }
+ \@@_collect_gput_right:N #1
+ }
}
+\cs_generate_variant:Nn \@@_collect_gput_right:N { c }
% \end{macrocode}
% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
+% \end{variable}
%
-% \subsubsection{Immediate closeout}
-%
-% \begin{macro}[aux]{\@@_immediate_closeout_test:n}
-% When the user requests to close a stream, we look in
-% \cs{g_@@_reserved_iow_clist} to see if it is a reserved
-% stream: in this case, we simply use the primitive.
+% \begin{macro}[aux, EXP]{\@@_user_tl_name:n}
+% The name of a global token list variable holding the text of a given
+% user stream.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_closeout_test:n #1
- {
- \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: }
- }
+\cs_new:Npn \@@_user_tl_name:n #1
+ { g_@@_iow_ \int_eval:n {#1} _tl }
% \end{macrocode}
% \end{macro}
%
-% \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.
+% \subsection{Reading}
+%
+% \begin{macro}[aux]{\@@_openin:w}
+% Set \cs{l_@@_user_int} to a user stream then convert it to a \TeX{}
+% stream \cs{l_@@_tstr_tl} and call the primitive. If the user stream
+% is closed (not associated to any \TeX{} stream), the \texttt{false}
+% branch of \cs{@@_user_to_tstr:NTF} is taken. Then get a stream from
+% \cs{g_@@_read_seq} (\TeX{} streams managed by \pkg{morewrites} but
+% not in use) or use the copy of \tn{newread} as a fallback. Store
+% the new mapping from user stream to \TeX{} stream into
+% \cs{g_@@_read_prop}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_immediate_closeout_aux:
+\cs_new_protected:Npn \@@_openin:w
{
- \prop_gpop:NVNT \g_@@_iow_prop \g_@@_stream_int \l_@@_internal_tl
+ \@@_get_user:n
{
- \@@_immediate_write_and_close:nn
- { \g_@@_stream_int } { \l_@@_internal_tl }
+ \@@_user_to_tstr:NTF \g_@@_read_prop { }
+ {
+ \seq_pop:NNF \g_@@_read_seq \l_@@_tstr_tl
+ {
+ \@@_tex_newread:N \l_@@_tstr_token
+ \tl_set:NV \l_@@_tstr_tl \l_@@_tstr_token
+ }
+ \prop_gput:NVV \g_@@_read_prop \l_@@_user_int \l_@@_tstr_tl
+ }
+ \@@_tex_openin:w \l_@@_tstr_tl \exp_stop_f:
}
}
% \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{macro}[aux]{\@@_read:w, \@@_readline:w}
+% Set \cs{l_@@_user_int} to a user stream and convert it to a \TeX{}
+% stream \cs{l_@@_tstr_tl} then call the primitive. Nothing needs to
+% be done for streams not managed by \pkg{morewrites} or for user
+% streams that do not correspond to any \TeX{} stream, as the
+% primitive will simply read user input from the terminal as \TeX{}
+% would do.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_write_and_close:nn #1#2
+\cs_new_protected:Npn \@@_read:w
{
- \@@_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
+ \@@_get_user:n
+ {
+ \@@_user_to_tstr:NTF \g_@@_read_prop { } { }
+ \@@_tex_read:w \l_@@_tstr_tl \exp_stop_f:
+ }
}
+\cs_new_protected:Npn \@@_readline:w
+ {
+ \@@_get_user:n
+ {
+ \@@_user_to_tstr:NTF \g_@@_read_prop { } { }
+ \@@_tex_readline:w \l_@@_tstr_tl \exp_stop_f:
+ }
+ }
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Immediate openout}
-%
-% \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{macro}[aux]{\@@_closein:w}
+% There is slightly more work than for |read| and |readline| because
+% closing a user stream managed by \pkg{morewrites} means removing it
+% from the mapping and putting it back in the list of available
+% streams. To avoid useless work, only do this if there was indeed a
+% non-trivial mapping between user and \TeX{} stream.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_openout_test:n #1
+\cs_new_protected:Npn \@@_closein:w
{
- \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 }
+ \@@_get_user:n
+ {
+ \@@_user_to_tstr:NTF \g_@@_read_prop { } { }
+ \int_compare:nNnF { \l_@@_tstr_tl } = { \l_@@_user_int }
+ {
+ \prop_gremove:NV \g_@@_read_prop \l_@@_user_int
+ \seq_gput_left:NV \g_@@_read_seq \l_@@_tstr_tl
+ }
+ \@@_tex_closein:w \l_@@_tstr_tl \exp_stop_f:
+ }
}
% \end{macrocode}
% \end{macro}
%
-% \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.
+% \subsection{Writing}
%
-% We then put the stream and its associated file name in the property
-% list, and empty/create the corresponding token list.
+% Writing is much harder than reading for several reasons.
+%
+% Contrarily to reading, it is possible to hold on to material while a
+% file is being written and only write it in one go once the file
+% closes, to avoid using a stream throughout. At any given time, each
+% user stream may point to an open \TeX{} stream, given in
+% \cs{g_@@_write_prop} (like we did for reading), or may point to a
+% token list that will eventually be written to a file whose file name
+% is stored in \cs{g_@@_write_file_prop}, or may be closed.
+%
+% When a user stream points to a token list rather than a \TeX{} stream,
+% any material to be written must be written to our temporary file and
+% read back in to apply the same expansion as \tn{write} does.
+%
+% Another difficulty is that users may mix immediate and non-immediate
+% operations. The biggest difficulty comes from the possibility of
+% copying boxes containing delayed actions. If we ever produced a
+% whatsit \tn{write}\meta{number}\Arg{text} then the \TeX{} stream
+% \meta{number} would have to be reserved forever, as as copies of the
+% box containing this delayed actions may be shipped out at any later
+% point in the document.
+%
+% Each delayed action is thus saved in a separate numbered token list
+% and \tn{write}\cs{g_@@_iow}\Arg{number} is inserted instead of the
+% delayed action. At each \tn{shipout}, the stream \cs{g_@@_iow} is
+% opened, to catch the \meta{number} of each action that should be
+% performed at this \tn{shipout}.
+%
+% \subsubsection{Redefining \tn{immediate}}
+%
+% To accomodate the \tn{immediate} primitive, our versions of
+% \tn{openout}, \tn{write} and \tn{closeout} will take the form
+% \begin{quote}
+% \cs{s_@@} \cs{use_i:nn}
+% \quad \Arg{code for delayed action} \\
+% \quad \Arg{code for immediate action} \\
+% \meta{further code}
+% \end{quote}
+% The leading \cs{s_@@} allows the redefined \tn{immediate} to detect
+% these redefined primitives, and to run the \meta{code for immediate
+% action} instead of the \meta{code for delayed action} which is run by
+% default. In both cases, any \meta{further code} is run.
+%
+% \begin{macro}[aux, updated = 2012-12-05]{\@@_immediate:w}
+% \begin{macro}[aux]{\@@_immediate_auxii:, \@@_immediate_auxiii:N}
+% \TeX{}'s \tn{immediate} primitive raises a flag which is cancelled
+% after \TeX{} sees a non-expandable token. We use
+% \cs{primargs_read_x_token:N} to find the next non-expandable token
+% then test for \tn{openout}, \tn{write}, and \tn{closeout}. More
+% precisely we test for the marker \cs{s_@@} and run the appropriate
+% code as described above. Otherwise we call the primitive, for cases
+% where the next token is \tn{pdfobj} or similar. This code performs
+% too much expansion for some nonsensical uses of \tn{noexpand} after
+% \tn{immediate}.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_openout_aux:n #1
+\cs_new_protected:Npn \@@_immediate:w
+ { \primargs_read_x_token:N \@@_immediate_auxii: }
+\cs_new_protected:Npn \@@_immediate_auxii:
{
- \@@_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 }
+ \token_if_eq_meaning:NNTF \g_primargs_token \s_@@
+ { \@@_immediate_auxiii:N }
+ { \@@_tex_immediate:w }
}
+\cs_new_protected:Npn \@@_immediate_auxiii:N #1
+ { \str_if_eq:nnTF { #1 } { \s_@@ } { \use_iii:nnn } { #1 } }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \subsubsection{Immediate write}
+% \subsubsection{Immediate actions}
%
-% \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.
+% The \tn{openout}, \tn{write}, and \tn{closeout} primitive can be
+% either delayed or immediate. In all cases they begin by looking for a
+% user stream. Here, we implement the immediate versions only.
+%
+% \begin{macro}[aux]{\@@_closeout:w, \@@_closeout_now:, \@@_closeout_now:nn}
+% In the immediate case \cs{@@_closeout_now:}, there are three cases.
+% The stream may point to a \TeX{} stream, in which case it is closed,
+% removed from \cs{g_@@_write_prop}, and put back in the list of
+% usable streams. The stream may point to a token list, in which case
+% that token list should be written to the appropriate file. The
+% stream may be closed, in which case nothing happens.
+% The auxiliary \cs{@@_closeout_now:nn} writes the material collected
+% so far for a given user stream |#1| to the file |#2|. This uses the
+% \TeX{} stream \cs{g_@@_iow}. The token list consists of multiple
+% \tn{immediate} \tn{write} \cs{g_@@_iow} \Arg{text} statements
+% because that is the only safe way to obtain new lines. We do not
+% remove the stream/file pair from \cs{g_@@_write_file_prop}.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_write_test:n #1
+\cs_new_protected:Npn \@@_closeout:w
{
- \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 }
+ \s_@@
+ \use_i:nn
+ { \@@_get_user:n { \@@_closeout_later: } }
+ { \@@_get_user:n { \@@_closeout_now: } }
}
+\cs_new_protected:Npn \@@_closeout_now:
+ {
+ \@@_user_to_tstr:NTF \g_@@_write_prop
+ {
+ \@@_tex_immediate:w \@@_tex_closeout:w \l_@@_tstr_tl \exp_stop_f:
+ \int_compare:nNnF { \l_@@_tstr_tl } = { \l_@@_user_int }
+ {
+ \prop_gremove:NV \g_@@_write_prop \l_@@_user_int
+ \seq_gput_left:NV \g_@@_write_seq \l_@@_tstr_tl
+ }
+ }
+ {
+ \prop_gpop:NVNT \g_@@_write_file_prop \l_@@_user_int \l_@@_internal_tl
+ { \@@_closeout_now:nn { \l_@@_user_int } { \l_@@_internal_tl } }
+ }
+ }
+\cs_new_protected:Npn \@@_closeout_now:nn #1#2
+ {
+ \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow = #2 \scan_stop:
+ \group_begin:
+ \int_set:Nn \tex_newlinechar:D { -1 }
+ \tl_use:c { \@@_user_tl_name:n {#1} }
+ \tl_gclear:c { \@@_user_tl_name:n {#1} }
+ \group_end:
+ \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow
+ }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_immediate_write_aux:n}
-% Test whether the stream is allocated or not.
+% \begin{macro}[aux]{\@@_openout:w, \@@_openout_now:n}
+% In the immediate case find a file name, then allocate a \TeX{}
+% stream if possible, and otherwise point the user stream to a token
+% list. In all cases, close the stream to avoid losing any material
+% that \TeX{} would have written, and empty the file by opening and
+% closing it (actually that's done automatically by the primitive).
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_immediate_write_aux:n
+\cs_new_protected:Npn \@@_openout:w
{
- \prop_get:NVNTF \g_@@_iow_prop \g_@@_stream_int \l_@@_internal_tl
- { \@@_immediate_write_open:n }
- { \@@_immediate_write_closed:n }
+ \s_@@
+ \use_i:nn
+ { \@@_get_user:n { \@@_openout_later:w } }
+ { \@@_get_user:n { \@@_equals_file:N \@@_openout_now:n } }
}
+\cs_new_protected:Npn \@@_openout_now:n #1
+ {
+ \@@_closeout_now:
+ \seq_pop:NNTF \g_@@_write_seq \l_@@_tstr_tl
+ {
+ \prop_gput:NVV \g_@@_write_prop \l_@@_user_int \l_@@_tstr_tl
+ \@@_tex_immediate:w \@@_tex_openout:w \l_@@_tstr_tl \exp_stop_f:
+ = \tl_to_str:n {#1} \scan_stop:
+ }
+ {
+ \@@_empty_file:n {#1}
+ \prop_gput:NVx \g_@@_write_file_prop \l_@@_user_int
+ { \tl_to_str:n {#1} }
+ \tl_gclear_new:c { \@@_user_tl_name:n { \l_@@_user_int } }
+ }
+ }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[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{macro}[aux]{\@@_write:w, \@@_write_now:w, \@@_write_now:n}
+% In the immediate case we use \cs{@@_write_now_open:n} if the stream
+% points to a token list, and otherwise use the primitive, with the
+% dummy stream $16$ if closed (the text is then written to the
+% terminal).
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_write_closed:n #1
+\cs_new_protected:Npn \@@_write:w
{
- \@@_tex_immediate:w \@@_tex_write:w
- \if_int_compare:w \g_@@_stream_int < \c_zero
- -1
- \else:
- 16
- \fi:
- {#1}
+ \s_@@
+ \use_i:nn
+ { \@@_get_user:n { \@@_write_later:w } }
+ { \@@_get_user:n { \@@_write_now:w } }
}
+\cs_new_protected:Npn \@@_write_now:w
+ {
+ \@@_user_to_tstr:NTF \g_@@_write_prop
+ { \@@_tex_immediate:w \@@_tex_write:w \l_@@_tstr_tl \exp_stop_f: }
+ { \primargs_get_general_text:N \@@_write_now:n }
+ }
+\cs_new_protected:Npn \@@_write_now:n
+ {
+ \prop_get:NVNTF \g_@@_write_file_prop \l_@@_user_int \l_@@_internal_tl
+ { \@@_write_now_open:n }
+ { \@@_tex_immediate:w \@@_tex_write:w 16 }
+ }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_immediate_write_open:n}
-% \begin{macro}[aux]{\@@_immediate_write_readlines_loop:}
+% \begin{macro}[aux]{\@@_write_now_open:n}
+% \begin{macro}[aux]{\@@_write_now_loop:}
% Only \tn{write} itself can emulate how \tn{write} expands tokens,
% because |#| don't have to be doubled, and because the
% \tn{newlinechar} has to be changed to new lines. Hence, we start by
-% writing |#1| to a file, 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,
+% writing |#1| to a file (after making sure we are allowed to alter
+% it), yielding some lines. The lines are then read one at a time
+% using \eTeX{}'s \tn{readline} with \tn{endlinechar} set to $-1$ to
+% avoid spurious characters. Each line becomes a \tn{immediate}
+% \tn{write} statement added to a token list whose name is constructed
+% using \cs{@@_user_tl_name:n}. This token list will be called when
+% it is time to actually write to the file. At that time,
% \tn{newlinechar} will be $-1$, so that writing each line will
% produce no extra line.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_immediate_write_open:n #1
+\cs_new_protected:Npn \@@_write_now_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
+ \bool_if:NF \g_@@_tmp_file_bool { \@@_chk_file: }
+ \@@_tex_immediate:w \@@_tex_openout:w
+ \g_@@_iow = \g_@@_tmp_file_tl \scan_stop:
+ \@@_tex_immediate:w \@@_tex_write:w
+ \g_@@_iow {#1}
+ \@@_tex_immediate:w \@@_tex_closeout:w
+ \g_@@_iow
\group_begin:
- \int_set_eq:NN \tex_endlinechar:D \c_minus_one
- \tex_openin:D \g_@@_ior \g_@@_tmp_file_tl \scan_stop:
- \@@_immediate_write_readlines_loop:
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \tex_openin:D \g_@@_ior = \g_@@_tmp_file_tl \scan_stop:
+ \@@_write_now_loop:
\tex_closein:D \g_@@_ior
+ \@@_collect_gput_right:c
+ { \@@_user_tl_name:n { \l_@@_user_int } }
\group_end:
+ \@@_empty_file:n { \g_@@_tmp_file_tl }
}
-\cs_new_protected_nopar:Npn \@@_immediate_write_readlines_loop:
+\cs_new_protected:Npn \@@_write_now_loop:
{
\etex_readline:D \g_@@_ior to \l_@@_internal_tl
\ior_if_eof:NF \g_@@_ior
{
- \tl_gput_right:cx
- { g_@@_iow_ \int_use:N \g_@@_stream_int _tl }
+ \@@_collect:x
{
- \@@_tex_immediate:w \@@_tex_write:w \g_@@_iow
- { \l_@@_internal_tl }
+ \@@_tex_immediate:w \@@_tex_write:w
+ \g_@@_iow { \l_@@_internal_tl }
}
- \@@_immediate_write_readlines_loop:
+ \@@_write_now_loop:
}
}
% \end{macrocode}
@@ -573,108 +1017,88 @@
% \end{macro}
%
%
-% \subsection{Non-immediate writing}
+% \subsubsection{Delayed actions}
%
-% 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.
-%
-% \subsubsection{Replacement for primitives}
-%
-% \begin{macro}[aux]{\@@_late:n}
+% \begin{macro}[aux]{\@@_later:n, \@@_later_do:n}
% Store the action to be done at shipout in a token list, and
-% non-immediately write the label \cs{g_@@_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.
+% non-immediately write the label \cs{g_@@_later_int} of the
+% output operation to the temporary file.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_late:n #1
+\cs_new_protected:Npn \@@_later:n #1
{
- \int_gincr:N \g_@@_late_write_int
+ \int_gincr:N \g_@@_later_int
\tl_const:cx
{
- c_@@_late_write_
- \int_use:N \g_@@_late_write_int
+ c_@@_later_
+ \int_use:N \g_@@_later_int
_tl
}
{
- \int_gset:Nn \exp_not:N \g_@@_stream_int
- { \exp_not:V \g_@@_stream_int }
+ \int_set:Nn \exp_not:N \l_@@_user_int
+ { \exp_not:V \l_@@_user_int }
\exp_not:n {#1}
}
\exp_args:NNx \@@_tex_write:w \g_@@_iow
- { `( \int_use:N \g_@@_late_write_int ) }
+ { `( \int_use:N \g_@@_later_int ) }
}
+\cs_new_protected:Npn \@@_later_do:n #1
+ { \tl_use:c { c_@@_later_ \int_eval:n {#1} _tl } }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[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{macro}[aux]{\@@_closeout_later:}
+% If the user stream is a \TeX{} stream, use the primitive, otherwise
+% save \cs{@@_closeout_now:} for later.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_openout:w
- { \s_@@ \primargs_get_number:N \@@_openout_test:n }
-\cs_new_protected:Npn \@@_openout_test:n #1
+\cs_new_protected:Npn \@@_closeout_later:
{
- \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 }
+ \int_compare:nNnTF \l_@@_user_int < { 19 }
+ { \@@_tex_closeout:w \l_@@_user_int }
+ { \@@_later:n { \@@_closeout_now: } }
}
-\cs_new_protected:Npn \@@_openout_aux:n #1
- { \@@_late:n { \@@_immediate_openout_aux:n {#1} } }
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \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{macro}[aux]{\@@_openout_later:w, \@@_openout_later:n}
+% If the user stream is a \TeX{} stream use the primitive, otherwise
+% find a file name and call \cs{@@_openout_now:n} later.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_write:w
- { \s_@@ \primargs_get_number:N \@@_write_test:n }
-\cs_new_protected:Npn \@@_write_test:n #1
+\cs_new_protected:Npn \@@_openout_later:w
{
- \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 }
+ \int_compare:nNnTF \l_@@_user_int < { 19 }
+ { \@@_tex_openout:w \l_@@_user_int }
+ { \@@_equals_file:N \@@_openout_later:n }
}
-\cs_new_protected:Npn \@@_write_aux:n #1
- { \@@_late:n { \@@_immediate_write_aux:n {#1} } }
+\cs_new_protected:Npn \@@_openout_later:n #1
+ { \@@_later:n { \@@_openout_now:n {#1} } }
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \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{macro}[aux]{\@@_write_later:w, \@@_write_later:n, \@@_write_later_aux:n}
+% For \TeX{} streams use the primitive, otherwise find a general text
+% and save it for later; the auxiliary is very similar to
+% \cs{@@_write_now:w}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_closeout:w
- { \s_@@ \primargs_get_number:N \@@_closeout_test:n }
-\cs_new_protected:Npn \@@_closeout_test:n #1
+\cs_new_protected:Npn \@@_write_later:w
{
- \int_gset:Nn \g_@@_stream_int {#1}
- \clist_if_in:NnTF \g_@@_reserved_iow_clist {#1}
- { \@@_tex_closeout:w \g_@@_stream_int }
- { \@@_closeout_aux: }
+ \int_compare:nNnTF \l_@@_user_int < { 19 }
+ { \@@_tex_write:w \l_@@_user_int }
+ { \primargs_get_general_text:N \@@_write_later:n }
}
-\cs_new_protected_nopar:Npn \@@_closeout_aux:
- { \@@_late:n { \@@_immediate_closeout_aux: } }
+\cs_new_protected:Npn \@@_write_later:n #1
+ { \@@_later:n { \@@_write_later_aux:n {#1} } }
+\cs_new_protected:Npn \@@_write_later_aux:n
+ {
+ \@@_user_to_tstr:NTF \g_@@_write_prop
+ { \@@_tex_immediate:w \@@_tex_write:w \l_@@_tstr_tl \exp_stop_f: }
+ { \@@_write_now:n }
+ }
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
% \subsubsection{Shipout business}
%
-% In this section, we hook into the \cs{shipout} primitive, and redefine
+% In this section, we hook into the \tn{shipout} primitive, and redefine
% it to first build a box with the material to ship out, then perform
% \begin{quote}
% \cs{@@_before_shipout:} \\
@@ -681,24 +1105,23 @@
% \meta{primitive shipout} \meta{collected box} \\
% \cs{@@_after_shipout:}
% \end{quote}
-% This is correct even if the values of the \tn{newlinechar} is changed
-% within the user code which builds the shipped out box, because the
-% value that \TeX{} uses is the value in effect immediately after
-% \tn{shipout}.
%
+% Each delayed output operation has been replaced by \tn{write}
+% \cs{g_@@_iow} |{`(|\meta{operation number}|)}|. The delimiters we
+% chose to put around numbers must be at least two distinct characters
+% on the left (then \cs{tex_newlinechar:D} cannot be equal to the
+% delimiter), and at least one non-digit character on the right.
+%
% \begin{macro}[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.
+% \cs{g_@@_iow} (after making sure we are allowed to alter the
+% auxiliary file).
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_before_shipout:
+\cs_new_protected:Npn \@@_before_shipout:
{
- \@@_tex_immediate:w \@@_tex_openout:w \g_@@_iow
- \g_@@_tmp_file_tl \scan_stop:
+ \bool_if:NF \g_@@_tmp_file_bool { \@@_chk_file: }
+ \@@_tex_immediate:w \@@_tex_openout:w
+ \g_@@_iow = \g_@@_tmp_file_tl \scan_stop:
}
% \end{macrocode}
% \end{macro}
@@ -715,7 +1138,7 @@
% 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
+% looping auxiliary \cs{@@_after_shipout_loop:ww} extracts the
% \meta{operation} numbers from the file, and makes a token list out
% of those. This token list is then used in a mapping function to
% perform the appropriate \tn{write} operations. Note that those
@@ -722,9 +1145,10 @@
% 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:
+\cs_new_protected:Npn \@@_after_shipout:
{
- \@@_tex_immediate:w \@@_tex_closeout:w \g_@@_iow
+ \@@_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 }
@@ -731,14 +1155,15 @@
\tl_map_inline:nn { `(0123456789) }
{ \char_set_catcode_other:n {`##1} }
\etex_everyeof:D { `() \exp_not:N }
- \tl_gset:Nx \g_@@_internal_tl
+ \tl_set:Nx \l_@@_internal_tl
{
\exp_after:wN \@@_after_shipout_loop:ww
\tex_input:D \g_@@_tmp_file_tl \c_space_tl
}
+ \@@_empty_file:n { \g_@@_tmp_file_tl }
+ \exp_args:NNo
\group_end:
- \tl_map_inline:Nn \g_@@_internal_tl
- { \tl_use:c { c_@@_late_write_ ##1 _tl } }
+ \tl_map_function:nN { \l_@@_internal_tl } \@@_later_do:n
}
\cs_new:Npn \@@_after_shipout_loop:ww #1 `( #2 )
{
@@ -752,141 +1177,164 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[aux]{\shipout}
% \begin{macro}[aux]{\@@_shipout:w}
-% \begin{variable}{\g_@@_group_level_int, \g_@@_shipout_box}
-% If \pkg{atbegshi} is available, patch it by adding
-% \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{macro}[aux]{\@@_shipout_i:, \@@_shipout_ii:}
+% Grab the shipped out box using \tn{setbox} and regain control using
+% \tn{afterassignment}. There are two cases: either the box is given
+% as \tn{box} or \tn{copy} followed by a number, in which case
+% \cs{@@_shipout_i:} is inserted afterwards at the same group level,
+% or the box is given as \tn{hbox} (or \tn{vtop} and so on) and an
+% additional \tn{aftergroup} is needed to reach a point where we can
+% use the box saved in \cs{g_@@_shipout_box}.
% \begin{macrocode}
-\IfFileExists{atbegshi.sty}
+\cs_new_protected:Npn \@@_shipout:w
{
- \RequirePackage{atbegshi}
- \tl_replace_once:Nnn \AtBegShi at Output
- { \AtBegShi at OrgShipout \box \AtBeginShipoutBox }
- {
- \@@_before_shipout:
- \AtBegShi at OrgShipout \box \AtBeginShipoutBox
- \@@_after_shipout:
- }
- \tl_replace_once:Nnn \AtBegShi at Output
- { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox }
- {
- \@@_before_shipout:
- \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox
- \@@_after_shipout:
- }
+ \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:Npn \@@_shipout_i:
{
- \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_@@_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 \@@_shipout_i:
- {
- \int_compare:nNnTF { \g_@@_group_level_int }
- = { \etex_currentgrouplevel:D }
- { \@@_shipout_ii: }
- { \tex_aftergroup:D \@@_shipout_ii: }
- }
- \cs_new_protected_nopar:Npn \@@_shipout_ii:
- {
- \@@_before_shipout:
- \@@_tex_shipout:w \tex_box:D \g_@@_shipout_box
- \@@_after_shipout:
- }
- \AtBeginDocument { \cs_gset_eq:NN \shipout \@@_shipout:w }
+ \int_compare:nNnTF { \g_@@_group_level_int }
+ = { \etex_currentgrouplevel:D }
+ { \@@_shipout_ii: }
+ { \tex_aftergroup:D \@@_shipout_ii: }
}
+\cs_new_protected:Npn \@@_shipout_ii:
+ {
+ \@@_before_shipout:
+ \@@_tex_shipout:w \tex_box:D \g_@@_shipout_box
+ \@@_after_shipout:
+ }
% \end{macrocode}
-% \end{variable}
% \end{macro}
% \end{macro}
%
-% \subsection{Hook at the very end}
-%
-% \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{macro}[aux]{\shipout, \@@_tex_shipout:w}
+% The task is now to locate the shipout primitive, which may have been
+% renamed and hooked into by many different packages loaded before
+% \pkg{morewrites}. Any of those control sequences which are equal to
+% the primitive are redefined to do \cs{@@_shipout:w} instead. If the
+% primitive is not located at all, the fallback is to hook into the
+% control sequence \tn{shipout}.
% \begin{macrocode}
-\int_new:N \g_@@_at_end_int
-\int_gset:Nn \g_@@_at_end_int { 10 }
+\cs_gset_protected:Npn \@@_tmp:w #1
+ {
+ \cs_if_exist:NF \@@_tex_shipout:w
+ { \cs_new_eq:NN \@@_tex_shipout:w #1 }
+ \cs_gset_eq:NN #1 \@@_shipout:w
+ }
+\tl_map_inline:nn
+ {
+ \xyrealshipout@
+ \org at shipout
+ \PDFSYNCship at ut@ld
+ \CROP at shipout
+ \@soORI
+ \tex_shipout:D
+ \zwpl at Hship
+ \o at shipout@TP
+ \LL at shipout
+ \Shipout
+ \GXTorg at shipout
+ \AtBegShi at OrgShipout
+ \AtBeginShipoutOriginalShipout
+ \shipout
+ }
+ {
+ \str_if_eq_x:nnT
+ { \cs_meaning:N #1 }
+ { \token_to_str:N \shipout }
+ { \@@_tmp:w #1 }
+ }
+\cs_if_exist:NF \@@_tex_shipout:w
+ {
+ \cs_new_eq:NN \@@_tex_shipout:w \shipout
+ \cs_gset_eq:NN \shipout \@@_shipout:w
+ }
% \end{macrocode}
-% \end{variable}
+% \end{macro}
%
+% \subsubsection{Hook at the very end}
+%
% \begin{macro}[aux]{\@@_close_all:}
% At the end of the document, close all the files.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_close_all:
+\cs_new_protected:Npn \@@_close_all:
{
- \prop_map_function:NN \g_@@_iow_prop
- \@@_immediate_write_and_close:nn
- \prop_gclear:N \g_@@_iow_prop
+ \prop_map_function:NN \g_@@_write_prop
+ \@@_closeout_now:nn
+ \prop_gclear:N \g_@@_write_prop
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_close_all_at_end:w}
-% This pushes its first argument to the very end of the \LaTeX{} run,
-% recursively (at most $10$ times, initial value of
-% \cs{g_@@_at_end_int}), just in case some other code adds things
-% there.
+% \begin{macro}[aux]{\@@_close_all_at_end:nw}
+% At the end of the run, we try very hard to put some material at the
+% \tn{@@end}, just in case some other very late code writes to files
+% that are not yet closed. This is tried at most $5$~times, to avoid
+% infinite loops in case two packages compete for that last place.
+% The four |@| become two after \pkg{l3docstrip}.
% \begin{macrocode}
-\cs_set:Npn \@@_tmp:w #1
+\cs_new_protected:Npn \@@_close_all_at_end:nw #1#2 \@@@@end
{
- \cs_new_protected:Npn \@@_close_all_at_end:w ##1 #1
- {
- \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
- }
+ \int_compare:nNnTF {#1} > \c_zero
+ { #2 \@@_close_all_at_end:nw { #1 - 1 } }
+ { \@@_close_all: #2 }
+ \@@@@end
}
-\exp_args:Nc \@@_tmp:w { @ @ end }
-\AtEndDocument { \@@_close_all_at_end:w }
+\AtEndDocument { \@@_close_all_at_end:nw { 5 } }
% \end{macrocode}
% \end{macro}
%
-% \subsection{Modified \cs{newwrite}}
+% \subsection{Redefining commands}
%
-% \begin{variable}{\g_@@_alloc_int}
-% The counter that \LaTeXe{} uses to allocate \tn{write} registers.
+% \subsubsection{Modified \tn{newwrite}}
+%
+% \begin{variable}[aux]{\g_@@_alloc_read_int, \g_@@_alloc_write_int}
+% Counters to allocate user streams. Initialized to $18$ so that the
+% first user stream allocated by \pkg{morewrites} is $19$. Indeed,
+% $18$ is reserved for shell commands and packages may expect $16$ or
+% $17$ to write to the terminal.
% \begin{macrocode}
-\tex_countdef:D \g_@@_alloc_int 17 \scan_stop:
+\int_new:N \g_@@_alloc_read_int
+\int_set:Nn \g_@@_alloc_read_int { 18 }
+\int_new:N \g_@@_alloc_write_int
+\int_set:Nn \g_@@_alloc_write_int { 18 }
% \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$,
-% to be on the safe side. This really ought to be \tn{protected}, but
-% none of the formats does that.
+% \begin{macro}[aux]{\@@_newread:N}
+% Reimplementation of \tn{newread} but protected and using a counter
+% \cs{g_@@_alloc_read_int} instead of what \TeX{}/\LaTeXe{} use.
% \begin{macrocode}
-\cs_new:Npn \@@_newwrite:N #1
+\cs_new_protected:Npn \@@_newread:N #1
{
- \int_gincr:N \g_@@_alloc_int
- \if_int_compare:w \g_@@_alloc_int = \c_sixteen
- \int_gset:Nn \g_@@_alloc_int { 20 }
- \fi:
- \int_set_eq:NN \allocationnumber \g_@@_alloc_int
+ \int_gincr:N \g_@@_alloc_read_int
+ \int_set_eq:NN \allocationnumber \g_@@_alloc_read_int
\cs_undefine:N #1
\int_const:Nn #1 { \allocationnumber }
\wlog
{
\token_to_str:N #1
+ = \token_to_str:N \read \int_use:N \allocationnumber
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_newwrite:N}
+% Same as for \tn{newread}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_newwrite:N #1
+ {
+ \int_gincr:N \g_@@_alloc_write_int
+ \int_set_eq:NN \allocationnumber \g_@@_alloc_write_int
+ \cs_undefine:N #1
+ \int_const:Nn #1 { \allocationnumber }
+ \wlog
+ {
+ \token_to_str:N #1
= \token_to_str:N \write \int_use:N \allocationnumber
}
}
@@ -893,12 +1341,61 @@
% \end{macrocode}
% \end{macro}
%
-% \subsection{Redefining the ``normal'' control sequences}
+% \begin{macro}[aux]{\@@_allocate:n}
+% Raise to |#1| the number of \tn{write} streams allocated to \pkg{morewrites}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_allocate:n #1
+ {
+ \prg_replicate:nn
+ {
+ \int_max:nn { 0 }
+ {
+ (#1) - \seq_count:N \g_@@_write_seq
+ - \prop_count:N \g_@@_write_prop
+ }
+ }
+ {
+ \@@_tex_newwrite:N \l_@@_tstr_token
+ \seq_put_right:NV \g_@@_write_seq \l_@@_tstr_token
+ }
+ }
+% \end{macrocode}
+% \end{macro}
%
-% \begin{macro}[aux, updated = 2012-12-05]{\immediate}
-% \begin{macro}[aux]{\openout, \write, \closeout, \newwrite}
-% \tn{shipout} has been redefined earlier.
+% \subsection{User commands and keys}
+%
+% \begin{macro}[added = 2014-07-26]{\morewritessetup}
+% Set whatever keys the user passes to \cs{morewritessetup}.
% \begin{macrocode}
+\cs_new_protected:Npn \morewritessetup #1
+ { \keys_set:nn { @@ } {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[added = 2014-07-26]{file}
+% Because of our use of |.initial:n|, this code must appear after
+% \cs{@@_set_file:n} is defined.
+% \begin{macrocode}
+\keys_define:nn { @@ }
+ {
+ allocate .code:n = \@@_allocate:n {#1} ,
+ file .code:n = \@@_set_file:n {#1} ,
+ file .initial:n = \c_sys_jobname_str .mw
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[updated = 2015-08-01]
+% {
+% \openin, \read, \readline, \closein, \newread,
+% \immediate, \openout, \write, \closeout, \newwrite
+% }
+% \begin{macrocode}
+\cs_gset_eq:NN \openin \@@_openin:w
+\cs_gset_eq:NN \read \@@_read:w
+\cs_gset_eq:NN \readline \@@_readline:w
+\cs_gset_eq:NN \closein \@@_closein:w
+\cs_gset_eq:NN \newread \@@_newread:N
\cs_gset_eq:NN \immediate \@@_immediate:w
\cs_gset_eq:NN \openout \@@_openout:w
\cs_gset_eq:NN \write \@@_write:w
@@ -906,10 +1403,10 @@
\cs_gset_eq:NN \newwrite \@@_newwrite:N
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
%</package>
%
% \end{implementation}
%
-% \endinput
+% \clearpage
+% \PrintIndex
Modified: trunk/Master/texmf-dist/source/latex/morewrites/primargs.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/morewrites/primargs.dtx 2017-04-16 22:19:27 UTC (rev 43861)
+++ trunk/Master/texmf-dist/source/latex/morewrites/primargs.dtx 2017-04-16 22:19:40 UTC (rev 43862)
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: primargs.dtx Copyright (C) 2012-2013 Bruno Le Floch
+%% File: primargs.dtx Copyright (C) 2012-2017 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
@@ -21,10 +21,9 @@
% \fi
%
% \title{The \textsf{primargs} package: \\
-% Parsing arguments of primitives\thanks{This
-% file has version number v0.2e, last revised 2013/01/08.}}
+% Parsing arguments of primitives}
% \author{Bruno Le Floch}
-% \date{2013/01/08}
+% \date{2017/04/10}
%
% \maketitle
% \tableofcontents
@@ -33,103 +32,196 @@
%
% \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.
+% This \TeX{} and \LaTeX{} package is currently used by \pkg{morewrites}
+% when redefining primitives: it allows to read arguments of primitives
+% in place of \TeX{}, which is useful to add hooks to primitives. Of
+% course, this is much slower than letting \TeX{} do things directly.
%
-% \subsection{Read one token}
+% All assignments done by this package are global. While a negative
+% value of the \tn{globaldefs} (primitive) parameter normally makes all
+% assignments local, this package makes sure \tn{globaldefs} is
+% non-negative before assignments.
%
-% \begin{variable}{\g_primargs_token}
-% The result of \cs{primargs_read_x_token:N} and
-% \cs{primargs_read_token:N}.
+% \subsection{Reading one token without removing it}
+%
+% \begin{variable}[tested = primargs001]{\g_primargs_token}
+% The token read by \cs{primargs_read_token:N} or
+% \cs{primargs_read_x_token:N}. Its value is always set globally.
+% It can be an \tn{outer} macro.
% \end{variable}
%
-% \begin{function}{\primargs_read_x_token:N}
+% \begin{function}[tested = primargs001]{\primargs_read_token:N}
% \begin{syntax}
-% \cs{primargs_read_x_token:N} \meta{function}
+% \cs{primargs_read_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}.
+% Sets \cs{g_primargs_token} equal to the token following the
+% \meta{function}, then calls the \meta{function}. The token
+% following the \meta{function} is not removed.
+% \begin{texnote}
+% This is essentially \tn{global} \tn{futurelet}
+% \cs{g_primargs_token} \meta{function}, with the added guarantee
+% that the assignment is global even when $\tn{globaldefs}$ is negative.
+% \end{texnote}
% \end{function}
%
-% \begin{function}{\primargs_read_token:N}
+% \begin{function}[tested = primargs001]{\primargs_read_x_token:N}
% \begin{syntax}
-% \cs{primargs_read_token:N} \meta{function}
+% \cs{primargs_read_x_token:N} \meta{function}
% \end{syntax}
-% Sets \cs{g_primargs_token} globally to the value of the next token,
-% then calls the \meta{function}.
+% Expands tokens recursively with \cs{exp_after:wN} until encountering
+% a non-expandable token and afterwards calls the \meta{function}.
+% The non-expandable token following the \meta{function} is not
+% removed and \cs{g_primargs_token} is also set (globally) equal to
+% that token.
% \end{function}
%
-% \subsection{Grabbing arguments}
+% \subsection{Removing tokens}
%
-% 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{function}[tested = primargs005]{\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.
+% Removes the \meta{token} which follows the \meta{function}, then
+% calls the \meta{function}. This also sets \cs{g_primargs_token}
+% (globally) equal to the removed token.
% \end{function}
%
-% \begin{function}{\primargs_remove_one_optional_space:N}
+% \begin{function}[tested = primargs005]{\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}.
+% Expands tokens following the \meta{function} until a non-expandable
+% token is found, and sets \cs{g_primargs_token} (globally) equal to this token,
+% then removes the token if it has catcode~$10$ (space). Finally,
+% call the \meta{function}.
% \end{function}
%
-% \begin{function}{\primargs_remove_optional_spaces:N}
+% \begin{function}[tested = primargs005]{\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}.
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space), then sets \cs{g_primargs_token} (globally) equal to
+% the first non-space token and calls the \meta{function}.
% \end{function}
%
-% \begin{function}{\primargs_remove_equals:N}
+% \begin{function}[tested = primargs005]{\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.
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space), then sets \cs{g_primargs_token} (globally) equal to
+% the first non-space token. If this token is an explicit~|=|
+% character token with catcode~$12$ (other), then it is removed as
+% well. Finally, calls the \meta{function}.
% \end{function}
%
-% \subsection{Afterassignment and \tn{globaldefs}}
+% \begin{function}[added = 2014-08-06, tested = primargs005]{\primargs_remove_filler:N}
+% \begin{syntax}
+% \cs{primargs_remove_filler:N} \meta{function}
+% \end{syntax}
+% Expands tokens following the \meta{function}, removing any token
+% with catcode~$10$ (space) or equal to \tn{relax}, then sets
+% \cs{g_primargs_token} (globally) equal to the next
+% token. Finally, calls the \meta{function}.
+% \end{function}
%
-% 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.
+% \subsection{Grabbing arguments}
%
-% Tokens inserted using \tn{afterassignment} may be lost when using this
-% package, since it uses \tn{afterassignment} internally.
+% \begin{function}[tested = primargs002]
+% {
+% \primargs_get_number:N,
+% \primargs_get_dimen:N,
+% \primargs_get_glue:N,
+% \primargs_get_mudimen:N,
+% \primargs_get_muglue:N,
+% }
+% \begin{syntax}
+% \cs{primargs_get_number:N} \meta{function}
+% \end{syntax}
+% Reads a number/dimension/glue/math dimension/math glue following the
+% \meta{function}, then calls the \meta{function} with a braced
+% argument containing the value found. For instance,
+% \begin{verbatim}
+% \primargs_get_glue:N \test 3sp plus \numexpr 2-3 fill X
+% \end{verbatim}
+% yields
+% \begin{verbatim}
+% \test {3sp plus -1fill}X
+% \end{verbatim}
+% A word of warning: the \cs{primargs_get_mudimen:N} function
+% currently parses a \meta{muskip} instead of a \meta{mudimen}.
+% \end{function}
%
-% \subsection{Internal functions}
+% \begin{function}[updated = 2014-08-06, tested = primargs003]
+% {\primargs_get_general_text:N}
+% \begin{syntax}
+% \cs{primargs_get_general_text:N} \meta{function}
+% \end{syntax}
+% Finds what \TeX{}'s grammar calls a \meta{general text} (that is, a
+% \meta{filler}, a catcode~$1$ token, a \meta{balanced text}, and an
+% explicit catcode~$2$ token) following the \meta{function}, and calls
+% the \meta{function} with the \meta{balanced text} as a braced
+% argument.
+% \end{function}
%
-% \begin{function}{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
+% \begin{function}[updated = 2017-04-10, tested = primargs004]{\primargs_get_file_name:N, \primargs_get_input_file_name:N}
% \begin{syntax}
-% \cs{@@_get_rhs:NnN} \meta{register} \Arg{register rhs} \meta{function}
+% \cs{primargs_get_file_name:N} \meta{function}
+% \cs{primargs_get_input_file_name:N} \meta{function}
% \end{syntax}
+% Reads a \meta{file name} following the \meta{function} and calls the
+% \meta{function} with this \meta{file name} as a braced argument.
+% The two functions are identical except in the \LuaTeX{} engine where
+% \cs{primargs_get_input_file_name:N} allows braced file names:
+% \LuaTeX{} allows such braced file names for some primitives (\tn{input}
+% and \tn{openin}) but not others (\tn{openout}).
+% \begin{texnote}
+% When braced file names are disallowed, the file name is obtained
+% by discarding \meta{optional spaces} then repeatedly doing the
+% following. Fully expand what follows in the input stream. If
+% the next token is an explicit or implicit character token
+% (regardless of its catcode) then add that character to the
+% file name and remove it from the input stream, and go back to
+% expanding tokens, except in one case: if the character code
+% is~$32$ (space) and the number of quote characters (code~$34$)
+% already in the file name is even, then the space is removed from
+% the input stream, not included in the file name, and parsing
+% ends. Finally, if the next token is a non-expandable command
+% (be it a control sequence or an active character) then the file
+% name ends and the command is left in the input stream.
+%
+% When braced file names are allowed, the following steps are added
+% prior to the procedure above. First remove a \meta{filler}. If
+% the next token is of catcode~$1$ then fully expand tokens one by
+% one and add their string representation (with \cs{tl_to_str:n},
+% not \cs{token_to_str:N}) to the file name.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Comments and internal functions}
+%
+% This package is not idiomatic \pkg{expl3} and should not be used as an
+% example of good coding practices. It uses \cs[no-index]{\ldots{}:D}
+% primitives directly:
+% \begin{itemize}
+% \item to cope with \tn{outer} tokens, since this package is meant to
+% be used quite broadly;
+% \item for primitives with (rightfully) no \pkg{expl3} interface (or a
+% slightly incomplete interface), namely \tn{afterassignment},
+% \tn{globaldefs}, \tn{aftergroup}, \tn{the}, \tn{deadcycles},
+% \tn{hoffset}, \tn{topskip}, \tn{thinmuskip}, \tn{unexpanded};
+% \item to test that a token's meaning is a given primitive when the
+% \pkg{expl3} interface is not (or not obviously) a copy of the primitive.
+% \end{itemize}
+% As a result, \emph{do not take this package as an example of how to
+% code with \pkg{expl3}; go and see Joseph Wright's \pkg{siunitx} for
+% instance.}
+%
+% \begin{function}{\__primargs_get_rhs:NnN, \__primargs_get_rhs:NoN}
+% \begin{syntax}
+% \cs{__primargs_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
@@ -140,6 +232,28 @@
% cause trouble with others.
% \end{function}
%
+% Despite large efforts expended to make this package robust against
+% changes to the \tn{globaldefs} parameter, setting it to a non-zero
+% value may make some parts of this package crash.
+%
+% Tokens inserted using \tn{afterassignment} may be lost when using this
+% package, since it uses \tn{afterassignment} internally.
+%
+% Todo list.
+% \begin{itemize}
+% \item Test all functions within alignments and understand their
+% interaction with the master counter.
+% \item Correct the parsing of \meta{mudimen}.
+% \item Perhaps parse \meta{muglue} and \meta{glue} by hand to avoid bad
+% interactions with \tn{globaldefs}. Otherwise put up a warning about
+% \tn{globaldefs} when relevant. Better partial fix: declare a skip and a muskip.
+% \item Write tests of engine behaviour, especially \LuaTeX{}'s \tn{input},
+% \tn{openin}, \tn{openout} including behaviour of |#| and spaces and
+% character-code-zero, to detect unexpected changes. In
+% \tn{input}|{|\ldots{}\tn{input}\ldots{}|}|, \LuaTeX{} expands the
+% inner \tn{input} but uses the inner file name as the outer file name.
+% \end{itemize}
+%
% \end{documentation}
%
% \begin{implementation}
@@ -148,9 +262,9 @@
%
%<*package>
% \begin{macrocode}
-\RequirePackage {expl3} [2012/08/14]
+\RequirePackage {expl3} [2017/03/18]
\ProvidesExplPackage
- {primargs} {2013/01/08} {0.2e} {Parsing arguments of primitives}
+ {primargs} {2017/04/10} {} {Parsing arguments of primitives}
% \end{macrocode}
%
% \begin{macrocode}
@@ -157,32 +271,38 @@
%<@@=primargs>
% \end{macrocode}
%
-% \subsection{Helpers}
+% \subsection{Variables and helpers}
%
% \begin{macro}[aux]{\g_@@_code_tl}
% Used to contain temporary code.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \g_@@_code_tl { }
+\tl_new:N \g_@@_code_tl
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux]{\@@_no_afterassignment:}
-% Supersedes any \tn{afterassignment} token.
+% \begin{variable}[int]{\g_@@_file_name_tl, \g_@@_file_name_level_tl}
+% Token list used to build a file name, one character at a time.
+% Token list holding the level of nesting in quotes or braces.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_no_afterassignment:
- { \tex_afterassignment:D \tex_relax:D }
+\tl_new:N \g_@@_file_name_tl
+\tl_new:N \g_@@_file_name_level_tl
% \end{macrocode}
-% \end{macro}
+% \end{variable}
%
-% \begin{macro}[aux]{\@@_no_localdefs:}
-% This function, which must be called in a group, ensures that
-% \tn{global} indeed makes definitions global.
+% \begin{macro}[aux]{\@@_safe:}
+% This function, which must be called in a group, cancels any
+% \tn{afterassignment} token and makes the \tn{globaldefs} parameter
+% non-negative. This ensures that assignments prefixed with
+% \tn{global} are indeed global. When \tn{globaldefs} is positive,
+% every assignment is global, and it is not possible to safely
+% (locally) set it to zero.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_no_localdefs:
+\cs_new_protected:Npn \@@_safe:
{
- \@@_no_afterassignment:
- \int_compare:nNnF \tex_globaldefs:D > \c_zero
- { \tex_globaldefs:D = \c_zero }
+ \tex_afterassignment:D \tex_relax:D
+ \if_int_compare:w 0 > \tex_globaldefs:D
+ \int_zero:N \tex_globaldefs:D
+ \fi:
}
% \end{macrocode}
% \end{macro}
@@ -191,22 +311,23 @@
%
% \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.
+% reaching a non-expandable token. We emulate this by reading the next
+% token with \tn{futurelet}, checking whether it is expandable or not by
+% comparing its meaning to its meaning when acted upon by \tn{noexpand},
+% and expanding it with \tn{expandafter} if it is expandable.
%
% One thing to be careful about is that
% \begin{quote}
-% \tn{expandafter} \tn{show} \tn{noexpand} \cs{space}
+% \tn{expandafter} \tn{show} \tn{noexpand} \tn{space}
% \end{quote}
-% shows the \tn{meaning} of the \tn{notexpanded:} \cs{space},
+% shows the \tn{meaning} of the |\notexpanded: \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}
+% \tn{noexpand} \tn{space}
% \end{quote}
-% expands the \cs{space} to the underlying space character token.
+% expands the \tn{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
@@ -214,52 +335,73 @@
% \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.
+% to see how \tn{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}
+% \begin{macro}[aux]
+% {
+% \@@_read_x_token:N, \@@_read_x_token_aux:N,
+% \@@_read_x_token_std:N, \@@_read_x_token_file:N
+% }
+% This is a bit messy, because we need to support the fact that
+% \TeX{} does not consider \tn{input} as expandable when it is
+% looking for a file name. This variation is encapsulated by
+% letting \cs{@@_read_x_token_aux:N} equal to either a standard
+% (\texttt{std}) version or a version specific to file names
+% (\texttt{file}).
+%
% 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
+% 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.
+% and call the loop. Otherwise, we stop. In the \texttt{file}
+% version there is an extra test for \cs{tex_input:D}. By default
+% use the standard version.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_read_x_token:N
+\cs_new_protected:Npn \primargs_read_x_token:N
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\@@_read_x_token:N
}
-\cs_new_protected_nopar:Npn \@@_read_x_token:N
+\cs_new_protected:Npn \@@_read_x_token:N
{
- \tex_afterassignment:D \@@_read_x_token_ii:N
+ \tex_afterassignment:D \@@_read_x_token_aux:N
\tex_global:D \tex_futurelet:D \g_primargs_token
}
-\cs_new_protected_nopar:Npn \@@_read_x_token_ii:N
+\cs_new_protected:Npn \@@_read_x_token_std:N
{
\exp_after:wN
\if_meaning:w \exp_not:N \g_primargs_token \g_primargs_token
- \group_end:
- \exp_after:wN \use_none:nnn
+ \group_end: \use_i:nnnn
\fi:
\exp_after:wN \@@_read_x_token:N \exp_after:wN
}
+\cs_new_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_std:N
+\cs_new_protected:Npn \@@_read_x_token_file:N
+ {
+ \if_meaning:w \tex_input:D \g_primargs_token
+ \use_i_ii:nnn \group_end:
+ \fi:
+ \@@_read_x_token_std:N
+ }
% \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.
+% The same without expansion, useful for instance when we already know
+% that what follows is expanded. Interestingly, we don't ever need to
+% take the user's function as an argument.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_read_token:N
+\cs_new_protected:Npn \primargs_read_token:N
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_afterassignment:D \group_end:
\tex_global:D \tex_futurelet:D \g_primargs_token
}
@@ -268,17 +410,15 @@
%
% \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}.
+% \meta{function} after closing the group.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_remove_token:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_aftergroup:D #1
\tex_afterassignment:D \group_end:
\tex_global:D \tex_let:D \g_primargs_token = ~
@@ -286,10 +426,6 @@
% \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.
@@ -297,13 +433,13 @@
\cs_new_protected:Npn \primargs_remove_one_optional_space:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\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:
+\cs_new_protected:Npn \@@_remove_one_optional_space:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\fi:
\group_end:
@@ -314,21 +450,23 @@
%
% \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.
+% {\@@_remove_optional_spaces:, \@@_remove_optional_spaces_aux:}
+% Start a group, make assignments safe, then recursively expand tokens
+% and remove any token with catcode~$10$ (space). Once another token
+% is found, close the group hence insert the \meta{function}~|#1|.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_remove_optional_spaces:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\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:
+\cs_new_protected:Npn \@@_remove_optional_spaces:
+ { \primargs_read_x_token:N \@@_remove_optional_spaces_aux: }
+\cs_new_protected:Npn \@@_remove_optional_spaces_aux:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\exp_after:wN \@@_remove_optional_spaces:
\else:
@@ -340,9 +478,10 @@
% \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{macro}[aux]{\@@_remove_equals:, \@@_remove_equals_aux:NN}
+% 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
{
@@ -350,18 +489,61 @@
\tex_aftergroup:D #1
\primargs_remove_optional_spaces:N \@@_remove_equals:
}
-\cs_new_protected_nopar:Npn \@@_remove_equals:
+\cs_new_protected:Npn \@@_remove_equals:
{
\if_meaning:w = \g_primargs_token
- \exp_after:wN \@@_remove_equals_ii:NN
+ \exp_after:wN \@@_remove_equals_aux:NN
\fi:
\group_end:
}
-\cs_new_protected:Npn \@@_remove_equals_ii:NN #1#2
+\cs_new_protected:Npn \@@_remove_equals_aux:NN #1#2
{ \tl_if_eq:nnTF { #2 } { = } { #1 } { #1 #2 } }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
+% \begin{macro}{\primargs_remove_filler:N}
+% \begin{macro}[aux]
+% {
+% \@@_remove_filler:,
+% \@@_remove_filler_aux:,
+% \@@_remove_filler_end:NNNNN
+% }
+% Within a group remove a \meta{filler}, and insert the user's |#1|
+% after closing the group. A \meta{filler} consists of tokens with
+% catcode~$10$ (space) or equal to \tn{relax} or to the
+% \enquote{frozen \tn{relax}} command.
+% \begin{macrocode}
+\cs_new_protected:Npn \primargs_remove_filler:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \@@_remove_filler:
+ }
+\cs_new_protected:Npn \@@_remove_filler:
+ { \primargs_read_x_token:N \@@_remove_filler_aux: }
+\cs_new_protected:Npn \@@_remove_filler_aux:
+ {
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
+ \else:
+ \if_meaning:w \tex_relax:D \g_primargs_token
+ \else:
+ \exp_after:wN
+ \if_meaning:w \exp_not:N \prg_do_nothing: \g_primargs_token
+ \else:
+ \@@_remove_filler_end:NNNNN
+ \fi:
+ \fi:
+ \fi:
+ \primargs_remove_token:N \@@_remove_filler:
+ }
+\cs_new_protected:Npn \@@_remove_filler_end:NNNNN #1#2#3#4#5
+ { #1 #2 #3 \group_end: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Right-hand sides of assignments}
%
% The naive approach to reading an integer, or a general text, is to let
@@ -376,15 +558,13 @@
% 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
+% value pleases us, as done by \cs{@@_safe:}). 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.
+% value afterwards.
%
% \begin{macro}[int]{\@@_get_rhs:NnN, \@@_get_rhs:NoN}
% The last two lines of this function are the key: assign to |#1|,
@@ -397,7 +577,7 @@
\cs_new_protected:Npn \@@_get_rhs:NnN #1#2#3
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
\tex_aftergroup:D #3
\tl_gset:Nn \g_@@_code_tl
{
@@ -419,7 +599,7 @@
% \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
+\cs_new_protected:Npn \primargs_get_number:N
{
\@@_get_rhs:NoN \tex_deadcycles:D
{ \tex_the:D \tex_deadcycles:D }
@@ -433,7 +613,7 @@
% 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
+\cs_new_protected:Npn \primargs_get_dimen:N
{
\@@_get_rhs:NoN \tex_hoffset:D
{ \tex_the:D \tex_hoffset:D }
@@ -444,7 +624,7 @@
% \begin{macro}{\primargs_get_glue:N}
% Use \tn{topskip}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_glue:N
+\cs_new_protected:Npn \primargs_get_glue:N
{
\@@_get_rhs:NoN \tex_topskip:D
{ \tex_the:D \tex_topskip:D }
@@ -457,7 +637,7 @@
% 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
+\cs_new_protected:Npn \primargs_get_mudimen:N
{
\msg_warning:nn { primargs } { get-mudimen }
\primargs_get_muglue:N
@@ -470,7 +650,7 @@
% \begin{macro}{\primargs_get_muglue:N}
% Use \tn{thinmuskip}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_muglue:N
+\cs_new_protected:Npn \primargs_get_muglue:N
{
\@@_get_rhs:NoN \tex_thinmuskip:D
{ \tex_the:D \tex_thinmuskip:D }
@@ -479,44 +659,72 @@
% \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{macro}[aux]
+% {\@@_get_general_text:, \@@_get_general_text_error:n}
+% Getting a \meta{general text} is more tricky, as an assignment to
+% \tn{errhelp} (for instance) would also allow constructions such as
+% |\toks0|. Instead, we remove a \meta{filler} then test whether the
+% next token (already expanded) is a catcode~$1$ token, in which case
+% we replace it by an explicit left brace before calling the function.
+% When the next token is not of catcode~$1$, we produce an error,
+% attempting to imitate as closely as possible the \TeX{} error.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \primargs_get_general_text:N
- { \@@_get_rhs:NoN \tex_errhelp:D { { } } }
+\cs_new_protected:Npn \primargs_get_general_text:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \tex_aftergroup:D { \if_false: } \fi:
+ \primargs_remove_filler:N \@@_get_general_text:
+ }
+\cs_new_protected:Npn \@@_get_general_text:
+ {
+ \if_catcode:w \c_group_begin_token \g_primargs_token
+ \exp_after:wN \primargs_remove_token:N
+ \else:
+ \group_begin:
+ \tex_aftergroup:D \@@_get_general_text_error:n
+ \if_catcode:w \c_group_end_token \g_primargs_token
+ \tex_aftergroup:D {
+ \tex_aftergroup:D }
+ \fi:
+ \fi:
+ \group_end:
+ }
+\cs_new_protected:Npn \@@_get_general_text_error:n #1
+ {
+ \exp_after:wN \group_end:
+ \etex_unexpanded:D \if_int_compare:w `{ = \c_zero \fi: #1 }
+ }
% \end{macrocode}
% \end{macro}
+% \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,
+% \meta{optional spaces} (\LuaTeX{} changes that to \meta{filler}),
+% 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}.
+% When \TeX{} reads a file name, the \tn{input} primitive is
+% temporarily not expandable, so we temporarily change
+% \cs{primargs_read_x_token:N} to not expand this primitive. This is
+% reverted by \cs{@@_get_file_name_end:}.
% \begin{macrocode}
\cs_new_protected:Npn \primargs_get_file_name:N #1
{
\group_begin:
- \@@_no_localdefs:
+ \@@_safe:
+ \cs_gset_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_file:N
\tex_aftergroup:D #1
\tl_gclear:N \g_@@_file_name_tl
+ \tl_gset:Nn \g_@@_file_name_level_tl { 0 }
\primargs_remove_optional_spaces:N \@@_get_file_name_test:
}
% \end{macrocode}
@@ -524,18 +732,18 @@
%
% \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,
+% If it is a control sequence, end the \meta{file name}. Spaces are
+% special (quotes too, but that is treated elsewhere). 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:
+\cs_new_protected: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_space: }
{ \primargs_remove_token:N \@@_get_file_name_char: }
}
}
@@ -543,17 +751,42 @@
% \end{macro}
%
% \begin{macro}[aux]{\@@_get_file_name_end:}
-% When the end of the file name is reached, end the group, after
+% When the end of the file name is reached, reinstate the original
+% definition of |read_x_token| so as to make \tn{input} expandable
+% again, then 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 }
+\cs_new_protected:Npn \@@_get_file_name_end:
+ {
+ \cs_gset_eq:NN \@@_read_x_token_aux:N
+ \@@_read_x_token_std:N
+ \exp_args:No \group_end: \g_@@_file_name_tl
+ }
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[aux]{\@@_get_file_name_space:}
+% We have already removed the space from the input stream. If
+% there is an odd number of quotes so far, add a space to the
+% file name and continue. Otherwise the file name ends.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_file_name_space:
+ {
+ \int_if_odd:nTF { \g_@@_file_name_level_tl }
+ {
+ \tl_gput_right:Nn \g_@@_file_name_tl { ~ }
+ \primargs_read_x_token:N \@@_get_file_name_test:
+ }
+ { \@@_get_file_name_end: }
+ }
+% \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}
+% Check for a quote, which switches \cs{g_@@_file_name_level_tl}
+% from $0$ to $1$ or back.
% 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
@@ -564,8 +797,13 @@
% character). Then loop with expansion. This technique would fail if
% the character could be a space (character code~$32$).
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_get_file_name_char:
+\cs_new_protected:Npn \@@_get_file_name_char:
{
+ \token_if_eq_charcode:NNT " \g_primargs_token
+ {
+ \tl_set:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { 1 - \g_@@_file_name_level_tl } }
+ }
\tl_gput_right:Nx \g_@@_file_name_tl
{
\exp_after:wN \@@_get_file_name_char_ii:w
@@ -581,39 +819,98 @@
% \end{macro}
% \end{macro}
%
-% \section{Examples}
+% \begin{macro}{\primargs_get_input_file_name:N}
+% \begin{macro}[aux]
+% {
+% \@@_get_input_file_name_first:,
+% \@@_get_input_file_name_loop:, \@@_get_input_file_name_test:,
+% \@@_get_input_file_name_brace:, \@@_get_input_file_name_aux:N
+% }
+% For most engines this is an alias of \cs{primargs_get_file_name:N}.
+% In \LuaTeX{} we test for a catcode $1$ token (after a filler) then
+% expand and collect tokens (turned to strings) one by one, counting
+% begin-group and end-group tokens in \cs{g_@@_file_name_level_tl}.
+% The control sequence \tn{par} is ignored. After removing a filler
+% or after expansion, \cs{g_primargs_token} cannot be \tn{outer} hence
+% the tests are safe. We use primitives to cope with outer macro
+% hidden by \tn{noexpand} upon first expansion.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \primargs_get_input_file_name:N #1
+ {
+ \group_begin:
+ \@@_safe:
+ \tex_aftergroup:D #1
+ \tl_gclear:N \g_@@_file_name_tl
+ \tl_gset:Nn \g_@@_file_name_level_tl { 1 }
+ \primargs_remove_filler:N \@@_get_input_file_name_first:
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_first:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ { \primargs_remove_token:N \@@_get_input_file_name_loop: }
+ { \primargs_get_file_name:N \group_end: }
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_loop:
+ { \primargs_read_x_token:N \@@_get_input_file_name_test: }
+ \cs_new_protected:Npn \@@_get_input_file_name_test:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ {
+ \tl_gset:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { \g_@@_file_name_level_tl + 1 } }
+ \primargs_remove_token:N \@@_get_input_file_name_brace:
+ }
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_end_token
+ {
+ \tl_gset:Nx \g_@@_file_name_level_tl
+ { \int_eval:n { \g_@@_file_name_level_tl - 1 } }
+ \int_compare:nNnTF { \g_@@_file_name_level_tl } > 0
+ { \primargs_remove_token:N \@@_get_input_file_name_brace: }
+ { \primargs_remove_token:N \@@_get_file_name_end: }
+ }
+ {
+ \token_if_eq_meaning:NNTF \g_primargs_token \c_space_token
+ {
+ \tl_gput_right:Nn \g_@@_file_name_tl { ~ }
+ \primargs_remove_token:N \@@_get_input_file_name_loop:
+ }
+ { \exp_after:wN \@@_get_input_file_name_aux:N \exp_not:N }
+ }
+ }
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_brace:
+ {
+ \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
+ }
+ \@@_get_input_file_name_loop:
+ }
+ \cs_new_protected:Npn \@@_get_input_file_name_aux:N #1
+ {
+ \exp_after:wN \str_if_eq_x:nnT
+ \exp_after:wN { \token_to_str:N #1 } { \token_to_str:N \par }
+ { \use_none:nnn }
+ \tex_xdef:D \g_@@_file_name_tl
+ {
+ \g_@@_file_name_tl
+ \exp_after:wN \tl_to_str:n \exp_after:wN { \exp_not:N #1 }
+ }
+ \@@_get_input_file_name_loop:
+ }
+ }
+ { \cs_new_eq:NN \primargs_get_input_file_name:N \primargs_get_file_name:N }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
-% [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>
%
-%</package>
-%
% \end{implementation}
%
-% \endinput
+% \PrintIndex
Modified: trunk/Master/texmf-dist/tex/latex/morewrites/morewrites.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/morewrites/morewrites.sty 2017-04-16 22:19:27 UTC (rev 43861)
+++ trunk/Master/texmf-dist/tex/latex/morewrites/morewrites.sty 2017-04-16 22:19:40 UTC (rev 43862)
@@ -9,7 +9,7 @@
%% Communicate any suggestions for changing this package
%% to Bruno Le Floch (blflatex at gmail.com).
%%
-%% File: morewrites.dtx Copyright (C) 2011-2013 Bruno Le Floch
+%% File: morewrites.dtx Copyright (C) 2011-2017 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
@@ -19,211 +19,386 @@
%% http://www.latex-project.org/lppl.txt
%%
%% -----------------------------------------------------------------------
-\RequirePackage {expl3} [2012/08/14]
-\RequirePackage {primargs} [2013/01/08]
+\RequirePackage {expl3} [2017/03/18]
+\RequirePackage {primargs} [2017/04/10]
\ProvidesExplPackage
- {morewrites} {2013/01/08} {0.2e} {Always room for a new write}
-\cs_generate_variant:Nn \prop_gpop:NnNT { NV }
+ {morewrites} {2017/04/10} {} {Always room for a new write}
+\sys_if_engine_luatex:T
+ {
+ \cs_new_protected:Npn \morewritessetup #1 { }
+ \msg_new:nnn { morewrites } { luatex }
+ { The~morewrites~package~is~unnecessary~in~LuaTeX. }
+ \msg_warning:nn { morewrites } { luatex }
+ \tex_endinput:D
+ }%
\cs_new_eq:NN \__morewrites_tex_immediate:w \tex_immediate:D
\cs_new_eq:NN \__morewrites_tex_openout:w \tex_openout:D
\cs_new_eq:NN \__morewrites_tex_write:w \tex_write:D
\cs_new_eq:NN \__morewrites_tex_closeout:w \tex_closeout:D
-\cs_new_eq:NN \__morewrites_tex_shipout:w \tex_shipout:D
-\int_new:N \g__morewrites_late_write_int
-\prop_new:N \g__morewrites_iow_prop
+\cs_new_eq:NN \__morewrites_tex_openin:w \tex_openin:D
+\cs_new_eq:NN \__morewrites_tex_read:w \tex_read:D
+\cs_new_eq:NN \__morewrites_tex_readline:w \etex_readline:D
+\cs_new_eq:NN \__morewrites_tex_closein:w \tex_closein:D
+\exp_args:NNf \cs_new_protected:Npn \__morewrites_tex_newread:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
+\exp_args:NNf \cs_new_protected:Npn \__morewrites_tex_newwrite:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
+\cs_generate_variant:Nn \prop_gpop:NnNT { NV }
+\cs_generate_variant:Nn \prop_gput:Nnn { NVx }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nv }
+\tl_new:N \l__morewrites_internal_tl
+\cs_new_eq:NN \__morewrites_tmp:w ?
+\int_new:N \g__morewrites_later_int
+\seq_new:N \g__morewrites_read_seq
+\seq_new:N \g__morewrites_write_seq
+\prop_new:N \g__morewrites_read_prop
+\prop_new:N \g__morewrites_write_prop
+\prop_new:N \g__morewrites_write_file_prop
+\tl_new:N \l__morewrites_code_tl
+\int_new:N \l__morewrites_user_int
+\tl_new:N \l__morewrites_tstr_tl
+\cs_new_eq:NN \l__morewrites_tstr_token ?
+\cs_new_eq:NN \s__morewrites \scan_stop:
\newwrite \g__morewrites_iow
\newread \g__morewrites_ior
\tl_new:N \g__morewrites_tmp_file_tl
-\tl_gset:Nn \g__morewrites_tmp_file_tl { \jobname.mw }
-\clist_new:N \g__morewrites_reserved_iow_clist
-\int_step_inline:nnnn {0} {1} { \g__morewrites_iow - 1 }
- { \clist_gput_right:Nn \g__morewrites_reserved_iow_clist {#1} }
-\clist_gput_right:Nn \g__morewrites_reserved_iow_clist {18}
-\int_new:N \g__morewrites_stream_int
-\cs_new_eq:NN \s__morewrites \scan_stop:
-\tl_new:N \l__morewrites_internal_tl
-\cs_new_protected:Npn \__morewrites_equals_file_name:N #1
+\bool_new:N \g__morewrites_tmp_file_bool
+\bool_gset_false:N \g__morewrites_tmp_file_bool
+\int_new:N \g__morewrites_group_level_int
+\box_new:N \g__morewrites_shipout_box
+\cs_new_protected:Npn \__morewrites_set_file:n #1
{
+ \bool_gset_false:N \g__morewrites_tmp_file_bool
+ \tl_gset:Nn \g__morewrites_tmp_file_tl {#1}
+ }
+\cs_new_protected:Npn \__morewrites_empty_file:n #1
+ {
+ \__morewrites_tex_immediate:w \__morewrites_tex_openout:w
+ \g__morewrites_iow = #1 \scan_stop:
+ \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w
+ \g__morewrites_iow
+ }
+\prg_new_conditional:Npnn \__morewrites_if_file_trivial:n #1 { TF }
+ {
\group_begin:
+ \tex_openin:D \g__morewrites_ior = #1 \scan_stop:
+ \if_eof:w \g__morewrites_ior
+ \cs_gset_eq:NN \__morewrites_tmp:w \prg_return_true:
+ \else:
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \etex_readline:D \g__morewrites_ior to \l__morewrites_internal_tl
+ \if_eof:w \g__morewrites_ior
+ \cs_gset_eq:NN \__morewrites_tmp:w \prg_return_true:
+ \else:
+ \cs_gset_eq:NN \__morewrites_tmp:w \prg_return_false:
+ \fi:
+ \fi:
+ \tex_closein:D \g__morewrites_ior
+ \group_end:
+ \__morewrites_tmp:w
+ }
+\cs_new_protected:Npn \__morewrites_chk_file:
+ {
+ \__morewrites_if_file_trivial:nTF { \g__morewrites_tmp_file_tl }
+ { \bool_gset_true:N \g__morewrites_tmp_file_bool }
+ {
+ \msg_warning:nnxx { morewrites } { file-exists }
+ { \g__morewrites_tmp_file_tl }
+ { \g__morewrites_tmp_file_tl .mw }
+ \tl_gput_right:Nn \g__morewrites_tmp_file_tl { .mw }
+ \__morewrites_chk_file:
+ }
+ }
+\msg_new:nnnn { morewrites } { file-exists }
+ { File~'#1'~exists,~using~'#2'~instead. }
+ {
+ The~file~`#1'~exists~and~was~not~created~by~this~version~of~the~
+ `morewrites'~package.~Please~move~or~delete~that~file,~or~provide~
+ another~file~name~by~adding
+ \\ \\
+ \iow_indent:n { \iow_char:N\\morewritessetup~{~file~=~other-name~} }
+ \\ \\
+ to~your~source~file.~In~the~meantime,~the~file~`#2'~will~be~used.
+ }
+\cs_new_protected:Npn \__morewrites_equals_file:N #1
+ {
+ \group_begin:
+ \tex_aftergroup:D \primargs_get_file_name:N
\tex_aftergroup:D #1
- \primargs_remove_equals:N \__morewrites_parse_file_name:
+ \primargs_remove_equals:N \group_end:
}
-\cs_new_protected_nopar:Npn \__morewrites_parse_file_name:
- { \primargs_get_file_name:N \group_end: }
-\cs_new_protected_nopar:Npn \__morewrites_immediate:w
- { \primargs_read_x_token:N \__morewrites_immediate_ii: }
-\cs_new_protected_nopar:Npn \__morewrites_immediate_ii:
+\cs_new_protected:Npn \__morewrites_get_user:n #1
{
- \token_if_eq_meaning:NNTF \g_primargs_token \s__morewrites
- { \__morewrites_immediate_iii:N }
- { \__morewrites_tex_immediate:w }
+ \tl_set:Nn \l__morewrites_code_tl {#1}
+ \tex_afterassignment:D \l__morewrites_code_tl
+ \l__morewrites_user_int =
}
-\cs_new_protected:Npn \__morewrites_immediate_iii:N #1
+\cs_new_protected:Npn \__morewrites_user_to_tstr:NTF #1
{
- \tl_if_eq:nnTF { #1 } { \s__morewrites }
- { \__morewrites_immediate_iv:NN }
- { #1 }
+ \tl_set:NV \l__morewrites_tstr_tl \l__morewrites_user_int
+ \int_compare:nNnTF { \l__morewrites_user_int } < { 19 }
+ { \use_i:nn }
+ { \prop_get:NVNTF #1 \l__morewrites_user_int \l__morewrites_tstr_tl }
}
-\cs_new_protected:Npn \__morewrites_immediate_iv:NN #1 #2
+\int_new:N \l__morewrites_collect_next_int
+\cs_new_protected:Npn \__morewrites_collect:x #1
{
- \exp_args:Nc #1
+ \tl_set:Nx \l__morewrites_internal_tl {#1}
+ \__morewrites_collect_aux:cf { l__morewrites_0_tl } { 1 }
+ }
+\cs_new_protected:Npn \__morewrites_collect_aux:Nn #1#2
+ {
+ \int_compare:nNnT {#2} > \l__morewrites_collect_next_int
{
- \exp_after:wN \__morewrites_immediate_v:w
- \token_to_str:N #2
+ \tl_clear_new:N #1
+ \int_set:Nn \l__morewrites_collect_next_int {#2}
}
+ \tl_if_empty:NTF #1
+ { \tl_set_eq:NN #1 \l__morewrites_internal_tl }
+ {
+ \tl_put_left:No \l__morewrites_internal_tl {#1}
+ \tl_clear:N #1
+ \__morewrites_collect_aux:cf { l__morewrites_#2_tl }
+ { \int_eval:n { #2 + 1 } }
+ }
}
-\use:x
+\cs_generate_variant:Nn \__morewrites_collect_aux:Nn { cf }
+\cs_new_protected:Npn \__morewrites_collect_gput_right:N #1
{
- \cs_new:Npn \exp_not:N \__morewrites_immediate_v:w
- ##1 \tl_to_str:n { __morewrites } { __morewrites_immediate }
+ \int_compare:nNnF \l__morewrites_collect_next_int = 0
+ {
+ \int_decr:N \l__morewrites_collect_next_int
+ \tl_gput_right:Nv #1
+ {
+ l__morewrites_
+ \int_use:N \l__morewrites_collect_next_int
+ _tl
+ }
+ \__morewrites_collect_gput_right:N #1
+ }
}
-\cs_new_protected:Npn \__morewrites_immediate_closeout_test:n #1
+\cs_generate_variant:Nn \__morewrites_collect_gput_right:N { c }
+\cs_new:Npn \__morewrites_user_tl_name:n #1
+ { g__morewrites_iow_ \int_eval:n {#1} _tl }
+\cs_new_protected:Npn \__morewrites_openin:w
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w \g__morewrites_stream_int }
- { \__morewrites_immediate_closeout_aux: }
+ \__morewrites_get_user:n
+ {
+ \__morewrites_user_to_tstr:NTF \g__morewrites_read_prop { }
+ {
+ \seq_pop:NNF \g__morewrites_read_seq \l__morewrites_tstr_tl
+ {
+ \__morewrites_tex_newread:N \l__morewrites_tstr_token
+ \tl_set:NV \l__morewrites_tstr_tl \l__morewrites_tstr_token
+ }
+ \prop_gput:NVV \g__morewrites_read_prop \l__morewrites_user_int \l__morewrites_tstr_tl
+ }
+ \__morewrites_tex_openin:w \l__morewrites_tstr_tl \exp_stop_f:
+ }
}
-\cs_new_protected_nopar:Npn \__morewrites_immediate_closeout_aux:
+\cs_new_protected:Npn \__morewrites_read:w
{
- \prop_gpop:NVNT \g__morewrites_iow_prop \g__morewrites_stream_int \l__morewrites_internal_tl
+ \__morewrites_get_user:n
{
- \__morewrites_immediate_write_and_close:nn
- { \g__morewrites_stream_int } { \l__morewrites_internal_tl }
+ \__morewrites_user_to_tstr:NTF \g__morewrites_read_prop { } { }
+ \__morewrites_tex_read:w \l__morewrites_tstr_tl \exp_stop_f:
}
}
-\cs_new_protected:Npn \__morewrites_immediate_write_and_close:nn #1#2
+\cs_new_protected:Npn \__morewrites_readline:w
{
- \__morewrites_tex_immediate:w \__morewrites_tex_openout:w
- \g__morewrites_iow #2 \scan_stop:
+ \__morewrites_get_user:n
+ {
+ \__morewrites_user_to_tstr:NTF \g__morewrites_read_prop { } { }
+ \__morewrites_tex_readline:w \l__morewrites_tstr_tl \exp_stop_f:
+ }
+ }
+\cs_new_protected:Npn \__morewrites_closein:w
+ {
+ \__morewrites_get_user:n
+ {
+ \__morewrites_user_to_tstr:NTF \g__morewrites_read_prop { } { }
+ \int_compare:nNnF { \l__morewrites_tstr_tl } = { \l__morewrites_user_int }
+ {
+ \prop_gremove:NV \g__morewrites_read_prop \l__morewrites_user_int
+ \seq_gput_left:NV \g__morewrites_read_seq \l__morewrites_tstr_tl
+ }
+ \__morewrites_tex_closein:w \l__morewrites_tstr_tl \exp_stop_f:
+ }
+ }
+\cs_new_protected:Npn \__morewrites_immediate:w
+ { \primargs_read_x_token:N \__morewrites_immediate_auxii: }
+\cs_new_protected:Npn \__morewrites_immediate_auxii:
+ {
+ \token_if_eq_meaning:NNTF \g_primargs_token \s__morewrites
+ { \__morewrites_immediate_auxiii:N }
+ { \__morewrites_tex_immediate:w }
+ }
+\cs_new_protected:Npn \__morewrites_immediate_auxiii:N #1
+ { \str_if_eq:nnTF { #1 } { \s__morewrites } { \use_iii:nnn } { #1 } }
+\cs_new_protected:Npn \__morewrites_closeout:w
+ {
+ \s__morewrites
+ \use_i:nn
+ { \__morewrites_get_user:n { \__morewrites_closeout_later: } }
+ { \__morewrites_get_user:n { \__morewrites_closeout_now: } }
+ }
+\cs_new_protected:Npn \__morewrites_closeout_now:
+ {
+ \__morewrites_user_to_tstr:NTF \g__morewrites_write_prop
+ {
+ \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w \l__morewrites_tstr_tl \exp_stop_f:
+ \int_compare:nNnF { \l__morewrites_tstr_tl } = { \l__morewrites_user_int }
+ {
+ \prop_gremove:NV \g__morewrites_write_prop \l__morewrites_user_int
+ \seq_gput_left:NV \g__morewrites_write_seq \l__morewrites_tstr_tl
+ }
+ }
+ {
+ \prop_gpop:NVNT \g__morewrites_write_file_prop \l__morewrites_user_int \l__morewrites_internal_tl
+ { \__morewrites_closeout_now:nn { \l__morewrites_user_int } { \l__morewrites_internal_tl } }
+ }
+ }
+\cs_new_protected:Npn \__morewrites_closeout_now:nn #1#2
+ {
+ \__morewrites_tex_immediate:w \__morewrites_tex_openout:w \g__morewrites_iow = #2 \scan_stop:
\group_begin:
- \int_set_eq:NN \tex_newlinechar:D \c_minus_one
- \tl_use:c { g__morewrites_iow_ \int_eval:n {#1} _tl }
- \tl_gclear:c { g__morewrites_iow_ \int_eval:n {#1} _tl }
+ \int_set:Nn \tex_newlinechar:D { -1 }
+ \tl_use:c { \__morewrites_user_tl_name:n {#1} }
+ \tl_gclear:c { \__morewrites_user_tl_name:n {#1} }
\group_end:
\__morewrites_tex_immediate:w \__morewrites_tex_closeout:w \g__morewrites_iow
}
-\cs_new_protected:Npn \__morewrites_immediate_openout_test:n #1
+\cs_new_protected:Npn \__morewrites_openout:w
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_immediate:w \__morewrites_tex_openout:w \g__morewrites_stream_int }
- { \__morewrites_equals_file_name:N \__morewrites_immediate_openout_aux:n }
+ \s__morewrites
+ \use_i:nn
+ { \__morewrites_get_user:n { \__morewrites_openout_later:w } }
+ { \__morewrites_get_user:n { \__morewrites_equals_file:N \__morewrites_openout_now:n } }
}
-\cs_new_protected:Npn \__morewrites_immediate_openout_aux:n #1
+\cs_new_protected:Npn \__morewrites_openout_now:n #1
{
- \__morewrites_immediate_closeout_aux:
- \prop_gput:NVn \g__morewrites_iow_prop \g__morewrites_stream_int {#1}
- \tl_gclear_new:c { g__morewrites_iow_ \int_use:N \g__morewrites_stream_int _tl }
+ \__morewrites_closeout_now:
+ \seq_pop:NNTF \g__morewrites_write_seq \l__morewrites_tstr_tl
+ {
+ \prop_gput:NVV \g__morewrites_write_prop \l__morewrites_user_int \l__morewrites_tstr_tl
+ \__morewrites_tex_immediate:w \__morewrites_tex_openout:w \l__morewrites_tstr_tl \exp_stop_f:
+ = \tl_to_str:n {#1} \scan_stop:
+ }
+ {
+ \__morewrites_empty_file:n {#1}
+ \prop_gput:NVx \g__morewrites_write_file_prop \l__morewrites_user_int
+ { \tl_to_str:n {#1} }
+ \tl_gclear_new:c { \__morewrites_user_tl_name:n { \l__morewrites_user_int } }
+ }
}
-\cs_new_protected:Npn \__morewrites_immediate_write_test:n #1
+\cs_new_protected:Npn \__morewrites_write:w
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_immediate:w \__morewrites_tex_write:w \g__morewrites_stream_int }
- { \primargs_get_general_text:N \__morewrites_immediate_write_aux:n }
+ \s__morewrites
+ \use_i:nn
+ { \__morewrites_get_user:n { \__morewrites_write_later:w } }
+ { \__morewrites_get_user:n { \__morewrites_write_now:w } }
}
-\cs_new_protected_nopar:Npn \__morewrites_immediate_write_aux:n
+\cs_new_protected:Npn \__morewrites_write_now:w
{
- \prop_get:NVNTF \g__morewrites_iow_prop \g__morewrites_stream_int \l__morewrites_internal_tl
- { \__morewrites_immediate_write_open:n }
- { \__morewrites_immediate_write_closed:n }
+ \__morewrites_user_to_tstr:NTF \g__morewrites_write_prop
+ { \__morewrites_tex_immediate:w \__morewrites_tex_write:w \l__morewrites_tstr_tl \exp_stop_f: }
+ { \primargs_get_general_text:N \__morewrites_write_now:n }
}
-\cs_new_protected:Npn \__morewrites_immediate_write_closed:n #1
+\cs_new_protected:Npn \__morewrites_write_now:n
{
- \__morewrites_tex_immediate:w \__morewrites_tex_write:w
- \if_int_compare:w \g__morewrites_stream_int < \c_zero
- -1
- \else:
- 16
- \fi:
- {#1}
+ \prop_get:NVNTF \g__morewrites_write_file_prop \l__morewrites_user_int \l__morewrites_internal_tl
+ { \__morewrites_write_now_open:n }
+ { \__morewrites_tex_immediate:w \__morewrites_tex_write:w 16 }
}
-\cs_new_protected:Npn \__morewrites_immediate_write_open:n #1
+\cs_new_protected:Npn \__morewrites_write_now_open:n #1
{
- \__morewrites_tex_immediate:w \__morewrites_tex_openout:w \g__morewrites_iow
- \g__morewrites_tmp_file_tl \scan_stop:
- \__morewrites_tex_immediate:w \__morewrites_tex_write:w \g__morewrites_iow {#1}
- \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w \g__morewrites_iow
+ \bool_if:NF \g__morewrites_tmp_file_bool { \__morewrites_chk_file: }
+ \__morewrites_tex_immediate:w \__morewrites_tex_openout:w
+ \g__morewrites_iow = \g__morewrites_tmp_file_tl \scan_stop:
+ \__morewrites_tex_immediate:w \__morewrites_tex_write:w
+ \g__morewrites_iow {#1}
+ \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w
+ \g__morewrites_iow
\group_begin:
- \int_set_eq:NN \tex_endlinechar:D \c_minus_one
- \tex_openin:D \g__morewrites_ior \g__morewrites_tmp_file_tl \scan_stop:
- \__morewrites_immediate_write_readlines_loop:
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \tex_openin:D \g__morewrites_ior = \g__morewrites_tmp_file_tl \scan_stop:
+ \__morewrites_write_now_loop:
\tex_closein:D \g__morewrites_ior
+ \__morewrites_collect_gput_right:c
+ { \__morewrites_user_tl_name:n { \l__morewrites_user_int } }
\group_end:
+ \__morewrites_empty_file:n { \g__morewrites_tmp_file_tl }
}
-\cs_new_protected_nopar:Npn \__morewrites_immediate_write_readlines_loop:
+\cs_new_protected:Npn \__morewrites_write_now_loop:
{
\etex_readline:D \g__morewrites_ior to \l__morewrites_internal_tl
\ior_if_eof:NF \g__morewrites_ior
{
- \tl_gput_right:cx
- { g__morewrites_iow_ \int_use:N \g__morewrites_stream_int _tl }
+ \__morewrites_collect:x
{
- \__morewrites_tex_immediate:w \__morewrites_tex_write:w \g__morewrites_iow
- { \l__morewrites_internal_tl }
+ \__morewrites_tex_immediate:w \__morewrites_tex_write:w
+ \g__morewrites_iow { \l__morewrites_internal_tl }
}
- \__morewrites_immediate_write_readlines_loop:
+ \__morewrites_write_now_loop:
}
}
-\cs_new_protected:Npn \__morewrites_late:n #1
+\cs_new_protected:Npn \__morewrites_later:n #1
{
- \int_gincr:N \g__morewrites_late_write_int
+ \int_gincr:N \g__morewrites_later_int
\tl_const:cx
{
- c__morewrites_late_write_
- \int_use:N \g__morewrites_late_write_int
+ c__morewrites_later_
+ \int_use:N \g__morewrites_later_int
_tl
}
{
- \int_gset:Nn \exp_not:N \g__morewrites_stream_int
- { \exp_not:V \g__morewrites_stream_int }
+ \int_set:Nn \exp_not:N \l__morewrites_user_int
+ { \exp_not:V \l__morewrites_user_int }
\exp_not:n {#1}
}
\exp_args:NNx \__morewrites_tex_write:w \g__morewrites_iow
- { `( \int_use:N \g__morewrites_late_write_int ) }
+ { `( \int_use:N \g__morewrites_later_int ) }
}
-\cs_new_protected_nopar:Npn \__morewrites_openout:w
- { \s__morewrites \primargs_get_number:N \__morewrites_openout_test:n }
-\cs_new_protected:Npn \__morewrites_openout_test:n #1
+\cs_new_protected:Npn \__morewrites_later_do:n #1
+ { \tl_use:c { c__morewrites_later_ \int_eval:n {#1} _tl } }
+\cs_new_protected:Npn \__morewrites_closeout_later:
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_openout:w \g__morewrites_stream_int }
- { \__morewrites_equals_file_name:N \__morewrites_openout_aux:n }
+ \int_compare:nNnTF \l__morewrites_user_int < { 19 }
+ { \__morewrites_tex_closeout:w \l__morewrites_user_int }
+ { \__morewrites_later:n { \__morewrites_closeout_now: } }
}
-\cs_new_protected:Npn \__morewrites_openout_aux:n #1
- { \__morewrites_late:n { \__morewrites_immediate_openout_aux:n {#1} } }
-\cs_new_protected_nopar:Npn \__morewrites_write:w
- { \s__morewrites \primargs_get_number:N \__morewrites_write_test:n }
-\cs_new_protected:Npn \__morewrites_write_test:n #1
+\cs_new_protected:Npn \__morewrites_openout_later:w
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_write:w \g__morewrites_stream_int }
- { \primargs_get_general_text:N \__morewrites_write_aux:n }
+ \int_compare:nNnTF \l__morewrites_user_int < { 19 }
+ { \__morewrites_tex_openout:w \l__morewrites_user_int }
+ { \__morewrites_equals_file:N \__morewrites_openout_later:n }
}
-\cs_new_protected:Npn \__morewrites_write_aux:n #1
- { \__morewrites_late:n { \__morewrites_immediate_write_aux:n {#1} } }
-\cs_new_protected_nopar:Npn \__morewrites_closeout:w
- { \s__morewrites \primargs_get_number:N \__morewrites_closeout_test:n }
-\cs_new_protected:Npn \__morewrites_closeout_test:n #1
+\cs_new_protected:Npn \__morewrites_openout_later:n #1
+ { \__morewrites_later:n { \__morewrites_openout_now:n {#1} } }
+\cs_new_protected:Npn \__morewrites_write_later:w
{
- \int_gset:Nn \g__morewrites_stream_int {#1}
- \clist_if_in:NnTF \g__morewrites_reserved_iow_clist {#1}
- { \__morewrites_tex_closeout:w \g__morewrites_stream_int }
- { \__morewrites_closeout_aux: }
+ \int_compare:nNnTF \l__morewrites_user_int < { 19 }
+ { \__morewrites_tex_write:w \l__morewrites_user_int }
+ { \primargs_get_general_text:N \__morewrites_write_later:n }
}
-\cs_new_protected_nopar:Npn \__morewrites_closeout_aux:
- { \__morewrites_late:n { \__morewrites_immediate_closeout_aux: } }
-\cs_new_protected_nopar:Npn \__morewrites_before_shipout:
+\cs_new_protected:Npn \__morewrites_write_later:n #1
+ { \__morewrites_later:n { \__morewrites_write_later_aux:n {#1} } }
+\cs_new_protected:Npn \__morewrites_write_later_aux:n
{
- \__morewrites_tex_immediate:w \__morewrites_tex_openout:w \g__morewrites_iow
- \g__morewrites_tmp_file_tl \scan_stop:
+ \__morewrites_user_to_tstr:NTF \g__morewrites_write_prop
+ { \__morewrites_tex_immediate:w \__morewrites_tex_write:w \l__morewrites_tstr_tl \exp_stop_f: }
+ { \__morewrites_write_now:n }
}
-\cs_new_protected_nopar:Npn \__morewrites_after_shipout:
+\cs_new_protected:Npn \__morewrites_before_shipout:
{
- \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w \g__morewrites_iow
+ \bool_if:NF \g__morewrites_tmp_file_bool { \__morewrites_chk_file: }
+ \__morewrites_tex_immediate:w \__morewrites_tex_openout:w
+ \g__morewrites_iow = \g__morewrites_tmp_file_tl \scan_stop:
+ }
+\cs_new_protected:Npn \__morewrites_after_shipout:
+ {
+ \__morewrites_tex_immediate:w \__morewrites_tex_closeout:w
+ \g__morewrites_iow
\group_begin:
\int_set_eq:NN \tex_endlinechar:D \tex_newlinechar:D
\char_set_catcode_other:n { \tex_endlinechar:D }
@@ -230,14 +405,15 @@
\tl_map_inline:nn { `(0123456789) }
{ \char_set_catcode_other:n {`##1} }
\etex_everyeof:D { `() \exp_not:N }
- \tl_gset:Nx \g__morewrites_internal_tl
+ \tl_set:Nx \l__morewrites_internal_tl
{
\exp_after:wN \__morewrites_after_shipout_loop:ww
\tex_input:D \g__morewrites_tmp_file_tl \c_space_tl
}
+ \__morewrites_empty_file:n { \g__morewrites_tmp_file_tl }
+ \exp_args:NNo
\group_end:
- \tl_map_inline:Nn \g__morewrites_internal_tl
- { \tl_use:c { c__morewrites_late_write_ ##1 _tl } }
+ \tl_map_function:nN { \l__morewrites_internal_tl } \__morewrites_later_do:n
}
\cs_new:Npn \__morewrites_after_shipout_loop:ww #1 `( #2 )
{
@@ -247,81 +423,93 @@
\__morewrites_after_shipout_loop:ww
}
}
-\IfFileExists{atbegshi.sty}
+\cs_new_protected:Npn \__morewrites_shipout:w
{
- \RequirePackage{atbegshi}
- \tl_replace_once:Nnn \AtBegShi at Output
- { \AtBegShi at OrgShipout \box \AtBeginShipoutBox }
- {
- \__morewrites_before_shipout:
- \AtBegShi at OrgShipout \box \AtBeginShipoutBox
- \__morewrites_after_shipout:
- }
- \tl_replace_once:Nnn \AtBegShi at Output
- { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox }
- {
- \__morewrites_before_shipout:
- \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox
- \__morewrites_after_shipout:
- }
+ \int_gset_eq:NN \g__morewrites_group_level_int \etex_currentgrouplevel:D
+ \tex_afterassignment:D \__morewrites_shipout_i:
+ \tex_global:D \tex_setbox:D \g__morewrites_shipout_box
}
+\cs_new_protected:Npn \__morewrites_shipout_i:
{
- \int_new:N \g__morewrites_group_level_int
- \box_new:N \g__morewrites_shipout_box
- \cs_new_protected_nopar:Npn \__morewrites_shipout:w
- {
- \int_gset_eq:NN \g__morewrites_group_level_int \etex_currentgrouplevel:D
- \tex_afterassignment:D \__morewrites_shipout_i:
- \tex_global:D \tex_setbox:D \g__morewrites_shipout_box
- }
- \cs_new_protected_nopar:Npn \__morewrites_shipout_i:
- {
- \int_compare:nNnTF { \g__morewrites_group_level_int }
- = { \etex_currentgrouplevel:D }
- { \__morewrites_shipout_ii: }
- { \tex_aftergroup:D \__morewrites_shipout_ii: }
- }
- \cs_new_protected_nopar:Npn \__morewrites_shipout_ii:
- {
- \__morewrites_before_shipout:
- \__morewrites_tex_shipout:w \tex_box:D \g__morewrites_shipout_box
- \__morewrites_after_shipout:
- }
- \AtBeginDocument { \cs_gset_eq:NN \shipout \__morewrites_shipout:w }
+ \int_compare:nNnTF { \g__morewrites_group_level_int }
+ = { \etex_currentgrouplevel:D }
+ { \__morewrites_shipout_ii: }
+ { \tex_aftergroup:D \__morewrites_shipout_ii: }
}
-\int_new:N \g__morewrites_at_end_int
-\int_gset:Nn \g__morewrites_at_end_int { 10 }
-\cs_new_protected_nopar:Npn \__morewrites_close_all:
+\cs_new_protected:Npn \__morewrites_shipout_ii:
{
- \prop_map_function:NN \g__morewrites_iow_prop
- \__morewrites_immediate_write_and_close:nn
- \prop_gclear:N \g__morewrites_iow_prop
+ \__morewrites_before_shipout:
+ \__morewrites_tex_shipout:w \tex_box:D \g__morewrites_shipout_box
+ \__morewrites_after_shipout:
}
-\cs_set:Npn \__morewrites_tmp:w #1
+\cs_gset_protected:Npn \__morewrites_tmp:w #1
{
- \cs_new_protected:Npn \__morewrites_close_all_at_end:w ##1 #1
+ \cs_if_exist:NF \__morewrites_tex_shipout:w
+ { \cs_new_eq:NN \__morewrites_tex_shipout:w #1 }
+ \cs_gset_eq:NN #1 \__morewrites_shipout:w
+ }
+\tl_map_inline:nn
+ {
+ \xyrealshipout@
+ \org at shipout
+ \PDFSYNCship at ut@ld
+ \CROP at shipout
+ \@soORI
+ \tex_shipout:D
+ \zwpl at Hship
+ \o at shipout@TP
+ \LL at shipout
+ \Shipout
+ \GXTorg at shipout
+ \AtBegShi at OrgShipout
+ \AtBeginShipoutOriginalShipout
+ \shipout
+ }
+ {
+ \str_if_eq_x:nnT
+ { \cs_meaning:N #1 }
+ { \token_to_str:N \shipout }
+ { \__morewrites_tmp:w #1 }
+ }
+\cs_if_exist:NF \__morewrites_tex_shipout:w
+ {
+ \cs_new_eq:NN \__morewrites_tex_shipout:w \shipout
+ \cs_gset_eq:NN \shipout \__morewrites_shipout:w
+ }
+\cs_new_protected:Npn \__morewrites_close_all:
+ {
+ \prop_map_function:NN \g__morewrites_write_prop
+ \__morewrites_closeout_now:nn
+ \prop_gclear:N \g__morewrites_write_prop
+ }
+\cs_new_protected:Npn \__morewrites_close_all_at_end:nw #1#2 \@@end
+ {
+ \int_compare:nNnTF {#1} > \c_zero
+ { #2 \__morewrites_close_all_at_end:nw { #1 - 1 } }
+ { \__morewrites_close_all: #2 }
+ \@@end
+ }
+\AtEndDocument { \__morewrites_close_all_at_end:nw { 5 } }
+\int_new:N \g__morewrites_alloc_read_int
+\int_set:Nn \g__morewrites_alloc_read_int { 18 }
+\int_new:N \g__morewrites_alloc_write_int
+\int_set:Nn \g__morewrites_alloc_write_int { 18 }
+\cs_new_protected:Npn \__morewrites_newread:N #1
+ {
+ \int_gincr:N \g__morewrites_alloc_read_int
+ \int_set_eq:NN \allocationnumber \g__morewrites_alloc_read_int
+ \cs_undefine:N #1
+ \int_const:Nn #1 { \allocationnumber }
+ \wlog
{
- \int_gdecr:N \g__morewrites_at_end_int
- \int_compare:nNnTF \g__morewrites_at_end_int > \c_zero
- {
- \tl_if_empty:nTF {##1}
- { ##1 \__morewrites_close_all: }
- { ##1 \__morewrites_close_all_at_end:w }
- }
- { \__morewrites_close_all: ##1 }
- #1
+ \token_to_str:N #1
+ = \token_to_str:N \read \int_use:N \allocationnumber
}
}
-\exp_args:Nc \__morewrites_tmp:w { @ @ end }
-\AtEndDocument { \__morewrites_close_all_at_end:w }
-\tex_countdef:D \g__morewrites_alloc_int 17 \scan_stop:
-\cs_new:Npn \__morewrites_newwrite:N #1
+\cs_new_protected:Npn \__morewrites_newwrite:N #1
{
- \int_gincr:N \g__morewrites_alloc_int
- \if_int_compare:w \g__morewrites_alloc_int = \c_sixteen
- \int_gset:Nn \g__morewrites_alloc_int { 20 }
- \fi:
- \int_set_eq:NN \allocationnumber \g__morewrites_alloc_int
+ \int_gincr:N \g__morewrites_alloc_write_int
+ \int_set_eq:NN \allocationnumber \g__morewrites_alloc_write_int
\cs_undefine:N #1
\int_const:Nn #1 { \allocationnumber }
\wlog
@@ -330,6 +518,34 @@
= \token_to_str:N \write \int_use:N \allocationnumber
}
}
+\cs_new_protected:Npn \__morewrites_allocate:n #1
+ {
+ \prg_replicate:nn
+ {
+ \int_max:nn { 0 }
+ {
+ (#1) - \seq_count:N \g__morewrites_write_seq
+ - \prop_count:N \g__morewrites_write_prop
+ }
+ }
+ {
+ \__morewrites_tex_newwrite:N \l__morewrites_tstr_token
+ \seq_put_right:NV \g__morewrites_write_seq \l__morewrites_tstr_token
+ }
+ }
+\cs_new_protected:Npn \morewritessetup #1
+ { \keys_set:nn { __morewrites } {#1} }
+\keys_define:nn { __morewrites }
+ {
+ allocate .code:n = \__morewrites_allocate:n {#1} ,
+ file .code:n = \__morewrites_set_file:n {#1} ,
+ file .initial:n = \c_sys_jobname_str .mw
+ }
+\cs_gset_eq:NN \openin \__morewrites_openin:w
+\cs_gset_eq:NN \read \__morewrites_read:w
+\cs_gset_eq:NN \readline \__morewrites_readline:w
+\cs_gset_eq:NN \closein \__morewrites_closein:w
+\cs_gset_eq:NN \newread \__morewrites_newread:N
\cs_gset_eq:NN \immediate \__morewrites_immediate:w
\cs_gset_eq:NN \openout \__morewrites_openout:w
\cs_gset_eq:NN \write \__morewrites_write:w
Modified: trunk/Master/texmf-dist/tex/latex/morewrites/primargs.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/morewrites/primargs.sty 2017-04-16 22:19:27 UTC (rev 43861)
+++ trunk/Master/texmf-dist/tex/latex/morewrites/primargs.sty 2017-04-16 22:19:40 UTC (rev 43862)
@@ -9,7 +9,7 @@
%% Communicate any suggestions for changing this package
%% to Bruno Le Floch (blflatex at gmail.com).
%%
-%% File: primargs.dtx Copyright (C) 2012-2013 Bruno Le Floch
+%% File: primargs.dtx Copyright (C) 2012-2017 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
@@ -19,42 +19,51 @@
%% http://www.latex-project.org/lppl.txt
%%
%% -----------------------------------------------------------------------
-\RequirePackage {expl3} [2012/08/14]
+\RequirePackage {expl3} [2017/03/18]
\ProvidesExplPackage
- {primargs} {2013/01/08} {0.2e} {Parsing arguments of primitives}
-\cs_new_protected_nopar:Npn \g__primargs_code_tl { }
-\cs_new_protected_nopar:Npn \__primargs_no_afterassignment:
- { \tex_afterassignment:D \tex_relax:D }
-\cs_new_protected_nopar:Npn \__primargs_no_localdefs:
+ {primargs} {2017/04/10} {} {Parsing arguments of primitives}
+\tl_new:N \g__primargs_code_tl
+\tl_new:N \g__primargs_file_name_tl
+\tl_new:N \g__primargs_file_name_level_tl
+\cs_new_protected:Npn \__primargs_safe:
{
- \__primargs_no_afterassignment:
- \int_compare:nNnF \tex_globaldefs:D > \c_zero
- { \tex_globaldefs:D = \c_zero }
+ \tex_afterassignment:D \tex_relax:D
+ \if_int_compare:w 0 > \tex_globaldefs:D
+ \int_zero:N \tex_globaldefs:D
+ \fi:
}
-\cs_new_protected_nopar:Npn \primargs_read_x_token:N
+\cs_new_protected:Npn \primargs_read_x_token:N
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\__primargs_read_x_token:N
}
-\cs_new_protected_nopar:Npn \__primargs_read_x_token:N
+\cs_new_protected:Npn \__primargs_read_x_token:N
{
- \tex_afterassignment:D \__primargs_read_x_token_ii:N
+ \tex_afterassignment:D \__primargs_read_x_token_aux:N
\tex_global:D \tex_futurelet:D \g_primargs_token
}
-\cs_new_protected_nopar:Npn \__primargs_read_x_token_ii:N
+\cs_new_protected:Npn \__primargs_read_x_token_std:N
{
\exp_after:wN
\if_meaning:w \exp_not:N \g_primargs_token \g_primargs_token
- \group_end:
- \exp_after:wN \use_none:nnn
+ \group_end: \use_i:nnnn
\fi:
\exp_after:wN \__primargs_read_x_token:N \exp_after:wN
}
-\cs_new_protected_nopar:Npn \primargs_read_token:N
+\cs_new_eq:NN \__primargs_read_x_token_aux:N
+ \__primargs_read_x_token_std:N
+\cs_new_protected:Npn \__primargs_read_x_token_file:N
{
+ \if_meaning:w \tex_input:D \g_primargs_token
+ \use_i_ii:nnn \group_end:
+ \fi:
+ \__primargs_read_x_token_std:N
+ }
+\cs_new_protected:Npn \primargs_read_token:N
+ {
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\tex_afterassignment:D \group_end:
\tex_global:D \tex_futurelet:D \g_primargs_token
}
@@ -61,7 +70,7 @@
\cs_new_protected:Npn \primargs_remove_token:N #1
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\tex_aftergroup:D #1
\tex_afterassignment:D \group_end:
\tex_global:D \tex_let:D \g_primargs_token = ~
@@ -69,13 +78,13 @@
\cs_new_protected:Npn \primargs_remove_one_optional_space:N #1
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\tex_aftergroup:D #1
\primargs_read_x_token:N \__primargs_remove_one_optional_space:
}
-\exp_args:NNo \cs_new_protected_nopar:Npn \__primargs_remove_one_optional_space:
+\cs_new_protected:Npn \__primargs_remove_one_optional_space:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\fi:
\group_end:
@@ -83,15 +92,15 @@
\cs_new_protected:Npn \primargs_remove_optional_spaces:N #1
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\tex_aftergroup:D #1
\__primargs_remove_optional_spaces:
}
-\cs_new_protected_nopar:Npn \__primargs_remove_optional_spaces:
- { \primargs_read_x_token:N \__primargs_remove_optional_spaces_ii: }
-\exp_args:NNo \cs_new_protected_nopar:Npn \__primargs_remove_optional_spaces_ii:
+\cs_new_protected:Npn \__primargs_remove_optional_spaces:
+ { \primargs_read_x_token:N \__primargs_remove_optional_spaces_aux: }
+\cs_new_protected:Npn \__primargs_remove_optional_spaces_aux:
{
- \use:n { \if_catcode:w } ~ \exp_not:N \g_primargs_token
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
\exp_after:wN \primargs_remove_token:N
\exp_after:wN \__primargs_remove_optional_spaces:
\else:
@@ -104,19 +113,45 @@
\tex_aftergroup:D #1
\primargs_remove_optional_spaces:N \__primargs_remove_equals:
}
-\cs_new_protected_nopar:Npn \__primargs_remove_equals:
+\cs_new_protected:Npn \__primargs_remove_equals:
{
\if_meaning:w = \g_primargs_token
- \exp_after:wN \__primargs_remove_equals_ii:NN
+ \exp_after:wN \__primargs_remove_equals_aux:NN
\fi:
\group_end:
}
-\cs_new_protected:Npn \__primargs_remove_equals_ii:NN #1#2
+\cs_new_protected:Npn \__primargs_remove_equals_aux:NN #1#2
{ \tl_if_eq:nnTF { #2 } { = } { #1 } { #1 #2 } }
+\cs_new_protected:Npn \primargs_remove_filler:N #1
+ {
+ \group_begin:
+ \__primargs_safe:
+ \tex_aftergroup:D #1
+ \__primargs_remove_filler:
+ }
+\cs_new_protected:Npn \__primargs_remove_filler:
+ { \primargs_read_x_token:N \__primargs_remove_filler_aux: }
+\cs_new_protected:Npn \__primargs_remove_filler_aux:
+ {
+ \if_catcode:w \c_space_token \exp_not:N \g_primargs_token
+ \else:
+ \if_meaning:w \tex_relax:D \g_primargs_token
+ \else:
+ \exp_after:wN
+ \if_meaning:w \exp_not:N \prg_do_nothing: \g_primargs_token
+ \else:
+ \__primargs_remove_filler_end:NNNNN
+ \fi:
+ \fi:
+ \fi:
+ \primargs_remove_token:N \__primargs_remove_filler:
+ }
+\cs_new_protected:Npn \__primargs_remove_filler_end:NNNNN #1#2#3#4#5
+ { #1 #2 #3 \group_end: }
\cs_new_protected:Npn \__primargs_get_rhs:NnN #1#2#3
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
\tex_aftergroup:D #3
\tl_gset:Nn \g__primargs_code_tl
{
@@ -130,22 +165,22 @@
#1 =
}
\cs_generate_variant:Nn \__primargs_get_rhs:NnN { No }
-\cs_new_protected_nopar:Npn \primargs_get_number:N
+\cs_new_protected:Npn \primargs_get_number:N
{
\__primargs_get_rhs:NoN \tex_deadcycles:D
{ \tex_the:D \tex_deadcycles:D }
}
-\cs_new_protected_nopar:Npn \primargs_get_dimen:N
+\cs_new_protected:Npn \primargs_get_dimen:N
{
\__primargs_get_rhs:NoN \tex_hoffset:D
{ \tex_the:D \tex_hoffset:D }
}
-\cs_new_protected_nopar:Npn \primargs_get_glue:N
+\cs_new_protected:Npn \primargs_get_glue:N
{
\__primargs_get_rhs:NoN \tex_topskip:D
{ \tex_the:D \tex_topskip:D }
}
-\cs_new_protected_nopar:Npn \primargs_get_mudimen:N
+\cs_new_protected:Npn \primargs_get_mudimen:N
{
\msg_warning:nn { primargs } { get-mudimen }
\primargs_get_muglue:N
@@ -152,36 +187,81 @@
}
\msg_new:nnn { primargs } { get-mudimen }
{ The~\iow_char:N\\primargs_get_mudimen:N~function~is~buggy. }
-\cs_new_protected_nopar:Npn \primargs_get_muglue:N
+\cs_new_protected:Npn \primargs_get_muglue:N
{
\__primargs_get_rhs:NoN \tex_thinmuskip:D
{ \tex_the:D \tex_thinmuskip:D }
}
-\cs_new_protected_nopar:Npn \primargs_get_general_text:N
- { \__primargs_get_rhs:NoN \tex_errhelp:D { { } } }
-\tl_new:N \g__primargs_file_name_tl
+\cs_new_protected:Npn \primargs_get_general_text:N #1
+ {
+ \group_begin:
+ \__primargs_safe:
+ \tex_aftergroup:D #1
+ \tex_aftergroup:D { \if_false: } \fi:
+ \primargs_remove_filler:N \__primargs_get_general_text:
+ }
+\cs_new_protected:Npn \__primargs_get_general_text:
+ {
+ \if_catcode:w \c_group_begin_token \g_primargs_token
+ \exp_after:wN \primargs_remove_token:N
+ \else:
+ \group_begin:
+ \tex_aftergroup:D \__primargs_get_general_text_error:n
+ \if_catcode:w \c_group_end_token \g_primargs_token
+ \tex_aftergroup:D {
+ \tex_aftergroup:D }
+ \fi:
+ \fi:
+ \group_end:
+ }
+\cs_new_protected:Npn \__primargs_get_general_text_error:n #1
+ {
+ \exp_after:wN \group_end:
+ \etex_unexpanded:D \if_int_compare:w `{ = \c_zero \fi: #1 }
+ }
\cs_new_protected:Npn \primargs_get_file_name:N #1
{
\group_begin:
- \__primargs_no_localdefs:
+ \__primargs_safe:
+ \cs_gset_eq:NN \__primargs_read_x_token_aux:N
+ \__primargs_read_x_token_file:N
\tex_aftergroup:D #1
\tl_gclear:N \g__primargs_file_name_tl
+ \tl_gset:Nn \g__primargs_file_name_level_tl { 0 }
\primargs_remove_optional_spaces:N \__primargs_get_file_name_test:
}
-\cs_new_protected_nopar:Npn \__primargs_get_file_name_test:
+\cs_new_protected:Npn \__primargs_get_file_name_test:
{
\token_if_cs:NTF \g_primargs_token
{ \__primargs_get_file_name_end: }
{
\token_if_eq_charcode:NNTF \c_space_token \g_primargs_token
- { \primargs_remove_token:N \__primargs_get_file_name_end: }
+ { \primargs_remove_token:N \__primargs_get_file_name_space: }
{ \primargs_remove_token:N \__primargs_get_file_name_char: }
}
}
-\cs_new_protected_nopar:Npn \__primargs_get_file_name_end:
- { \exp_args:No \group_end: \g__primargs_file_name_tl }
-\cs_new_protected_nopar:Npn \__primargs_get_file_name_char:
+\cs_new_protected:Npn \__primargs_get_file_name_end:
{
+ \cs_gset_eq:NN \__primargs_read_x_token_aux:N
+ \__primargs_read_x_token_std:N
+ \exp_args:No \group_end: \g__primargs_file_name_tl
+ }
+\cs_new_protected:Npn \__primargs_get_file_name_space:
+ {
+ \int_if_odd:nTF { \g__primargs_file_name_level_tl }
+ {
+ \tl_gput_right:Nn \g__primargs_file_name_tl { ~ }
+ \primargs_read_x_token:N \__primargs_get_file_name_test:
+ }
+ { \__primargs_get_file_name_end: }
+ }
+\cs_new_protected:Npn \__primargs_get_file_name_char:
+ {
+ \token_if_eq_charcode:NNT " \g_primargs_token
+ {
+ \tl_set:Nx \g__primargs_file_name_level_tl
+ { \int_eval:n { 1 - \g__primargs_file_name_level_tl } }
+ }
\tl_gput_right:Nx \g__primargs_file_name_tl
{
\exp_after:wN \__primargs_get_file_name_char_ii:w
@@ -193,6 +273,76 @@
\cs_new:Npn \__primargs_get_file_name_char_ii:w #1 ~ #2 ~ #3 \q_stop
{ \__primargs_get_file_name_char_iii:w #3 ~ #3 ~ \q_stop }
\cs_new:Npn \__primargs_get_file_name_char_iii:w #1 ~ #2 ~ #3 \q_stop {#2}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \primargs_get_input_file_name:N #1
+ {
+ \group_begin:
+ \__primargs_safe:
+ \tex_aftergroup:D #1
+ \tl_gclear:N \g__primargs_file_name_tl
+ \tl_gset:Nn \g__primargs_file_name_level_tl { 1 }
+ \primargs_remove_filler:N \__primargs_get_input_file_name_first:
+ }
+ \cs_new_protected:Npn \__primargs_get_input_file_name_first:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ { \primargs_remove_token:N \__primargs_get_input_file_name_loop: }
+ { \primargs_get_file_name:N \group_end: }
+ }
+ \cs_new_protected:Npn \__primargs_get_input_file_name_loop:
+ { \primargs_read_x_token:N \__primargs_get_input_file_name_test: }
+ \cs_new_protected:Npn \__primargs_get_input_file_name_test:
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_begin_token
+ {
+ \tl_gset:Nx \g__primargs_file_name_level_tl
+ { \int_eval:n { \g__primargs_file_name_level_tl + 1 } }
+ \primargs_remove_token:N \__primargs_get_input_file_name_brace:
+ }
+ {
+ \token_if_eq_catcode:NNTF \g_primargs_token \c_group_end_token
+ {
+ \tl_gset:Nx \g__primargs_file_name_level_tl
+ { \int_eval:n { \g__primargs_file_name_level_tl - 1 } }
+ \int_compare:nNnTF { \g__primargs_file_name_level_tl } > 0
+ { \primargs_remove_token:N \__primargs_get_input_file_name_brace: }
+ { \primargs_remove_token:N \__primargs_get_file_name_end: }
+ }
+ {
+ \token_if_eq_meaning:NNTF \g_primargs_token \c_space_token
+ {
+ \tl_gput_right:Nn \g__primargs_file_name_tl { ~ }
+ \primargs_remove_token:N \__primargs_get_input_file_name_loop:
+ }
+ { \exp_after:wN \__primargs_get_input_file_name_aux:N \exp_not:N }
+ }
+ }
+ }
+ \cs_new_protected:Npn \__primargs_get_input_file_name_brace:
+ {
+ \tl_gput_right:Nx \g__primargs_file_name_tl
+ {
+ \exp_after:wN \__primargs_get_file_name_char_ii:w
+ \token_to_meaning:N \g_primargs_token
+ \q_stop
+ }
+ \__primargs_get_input_file_name_loop:
+ }
+ \cs_new_protected:Npn \__primargs_get_input_file_name_aux:N #1
+ {
+ \exp_after:wN \str_if_eq_x:nnT
+ \exp_after:wN { \token_to_str:N #1 } { \token_to_str:N \par }
+ { \use_none:nnn }
+ \tex_xdef:D \g__primargs_file_name_tl
+ {
+ \g__primargs_file_name_tl
+ \exp_after:wN \tl_to_str:n \exp_after:wN { \exp_not:N #1 }
+ }
+ \__primargs_get_input_file_name_loop:
+ }
+ }
+ { \cs_new_eq:NN \primargs_get_input_file_name:N \primargs_get_file_name:N }
%%
%%
%% End of file `primargs.sty'.
More information about the tex-live-commits
mailing list