From cacc7d77918eb955a4bc5bed894e8ea1445a6ba4 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 16 Apr 2017 22:19:40 +0000 Subject: morewrites (14apr17) git-svn-id: svn://tug.org/texlive/trunk@43862 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/morewrites/README | 15 - Master/texmf-dist/doc/latex/morewrites/README.md | 16 + .../texmf-dist/doc/latex/morewrites/morewrites.pdf | Bin 592811 -> 649033 bytes .../texmf-dist/doc/latex/morewrites/primargs.pdf | Bin 525621 -> 603450 bytes .../source/latex/morewrites/morewrites.dtx | 1537 +++++++++++++------- .../source/latex/morewrites/primargs.dtx | 685 ++++++--- .../texmf-dist/tex/latex/morewrites/morewrites.sty | 612 +++++--- .../texmf-dist/tex/latex/morewrites/primargs.sty | 242 ++- 8 files changed, 2134 insertions(+), 973 deletions(-) delete mode 100644 Master/texmf-dist/doc/latex/morewrites/README create mode 100644 Master/texmf-dist/doc/latex/morewrites/README.md diff --git a/Master/texmf-dist/doc/latex/morewrites/README b/Master/texmf-dist/doc/latex/morewrites/README deleted file mode 100644 index ba3e7ba1b15..00000000000 --- a/Master/texmf-dist/doc/latex/morewrites/README +++ /dev/null @@ -1,15 +0,0 @@ ----------------------------------------------------------------- -morewrites --- Always room for a new write -E-mail: blflatex@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! diff --git a/Master/texmf-dist/doc/latex/morewrites/README.md b/Master/texmf-dist/doc/latex/morewrites/README.md new file mode 100644 index 00000000000..8fdde32361d --- /dev/null +++ b/Master/texmf-dist/doc/latex/morewrites/README.md @@ -0,0 +1,16 @@ +Provide more writing streams in LaTeX +===================================== +* E-mail: blflatex@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 + + diff --git a/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf b/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf index 899759df521..3940031ee03 100644 Binary files a/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf and b/Master/texmf-dist/doc/latex/morewrites/morewrites.pdf differ diff --git a/Master/texmf-dist/doc/latex/morewrites/primargs.pdf b/Master/texmf-dist/doc/latex/morewrites/primargs.pdf index 29fa67e0a4a..3c997efdc5a 100644 Binary files a/Master/texmf-dist/doc/latex/morewrites/primargs.pdf and b/Master/texmf-dist/doc/latex/morewrites/primargs.pdf differ diff --git a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx index 5df1e58d848..b86c4a3206a 100644 --- a/Master/texmf-dist/source/latex/morewrites/morewrites.dtx +++ b/Master/texmf-dist/source/latex/morewrites/morewrites.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: morewrites.dtx Copyright (C) 2011-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. -% -% I have tried to make the code as robust as possible, but there may -% still be bugs lurking as this package has not been tested very -% thoroughly yet. I thus encourage you to check that references are -% correct after loading that package: if they are correct without -% \pkg{morewrites}, but wrong with, please send me a minimal file -% showing the problem, or post a question on the -% \url{tex.stackexchange.com} question and answers website, or the -% \url{comp.text.tex} newsgroup. -% -% This package loads the \pkg{expl3} package, hence the \pkg{l3kernel} -% bundle needs to be up to date. If Heiko Oberdiek's package -% \pkg{atbegshi} is available, it will be used. -% -% \section{Known deficiencies and open questions} -% -% Some distributions of \TeX{} allow a quoted syntax for file names with -% spaces. I haven't yet coded that. A temporary fix is to avoid file -% names with spaces. -% -% The 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. -% -% 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.} -% -% Things that I need to do. +% primitives). +% +% 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. +% +% Notes. % \begin{itemize} -% \item Redefine the \LaTeX3 functions to use \pkg{morewrites} too? -% \item Should \tn{newwrite} be protected? +% \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} % +% \subsection{Commands defined or altered by \pkg{morewrites}} +% +% \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} +% +% \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} +% +% \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} +% +% \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,10 +155,22 @@ % %<*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} @@ -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). -% -% We start with the three \enquote{actions}. -% \begin{syntax} -% \tn{openout} \meta{integer} \meta{equals} \meta{file name} -% \end{syntax} -% \TeX{} searches the path for a file with a name given by -% \meta{file name}. If found, this file is opened in the writing stream -% \meta{integer}, which must be a number in the range $[0,15]$. -% \begin{syntax} -% \tn{write} \meta{integer} \meta{filler} \meta{general text} -% \end{syntax} -% \TeX{} expands the \meta{general text} as for an \texttt{x}-type -% expansion, with the caveat that macro parameter characters do not need -% to be doubled; converts the result to a string, and writes it in the -% writing stream \meta{integer}. If the writing stream \meta{integer} -% is open (in particular it must be in the range $[0,15]$), then this -% writes to the corresponding file. Otherwise, if the \meta{integer} is -% negative, the text is written to the log file, and a non-negative -% \meta{integer} writes to the terminal. One exception: if the -% \meta{integer} is $18$, the text is sent to a shell to be run as shell -% code. -% \begin{syntax} -% \tn{closeout} \meta{integer} -% \end{syntax} -% If the writing stream \meta{integer} is open, it is closed. -% Otherwise, if the \meta{integer} is not in the range $[0,15]$ an error -% may be raised, or nothing happens. -% -% By default, each one of those three \enquote{actions} are recorded in -% a whatsit node in the current list, and will be performed when the box -% containing the whatsit node is sent to the final \texttt{pdf}, -% \emph{i.e.}, at \enquote{shipout} time. In particular, the -% \meta{general text} for the \tn{write} primitive is expanded at -% shipout time. This behaviour may be modified by putting -% \tn{immediate} before any of the three \enquote{actions} to force -% \TeX{} to perform the action immediately instead of recording it in a -% whatsit node. -% -% Since the \tn{openout}, \tn{write}, and \tn{closeout} primitives -% operate at \tn{shipout} time, we will have to hook into this primitive -% too. It expects to be followed by a box specification such as -% \tn{box}\meta{integer}, or \tn{hbox}\Arg{material to typeset}. -% -% Finally, the \cs{newwrite} macro expects one token as its argument, -% and defines this token (with \tn{chardef}) to be an integer -% corresponding to the first available writing stream. We must extend -% it to let it allocate higher (virtual) write registers. -% +% 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{}. +% +% 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. +% +% 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. +% +% 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$. +% +% 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. +% +% 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. +% +% 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}. % -% \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} +% 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{Renaming primitives (again)} +% \subsection{Preliminaries} % -% \begin{macro}[int] +% \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} +% +% ^^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} +\seq_new:N \g_@@_read_seq +\seq_new:N \g_@@_write_seq +% \end{macrocode} +% \end{variable} +% +% \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}{\g_@@_iow_prop} -% The property list \cs{g_@@_iow_prop} associates a file name to each -% open stream. +% \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} -\prop_new:N \g_@@_iow_prop +\int_new:N \l_@@_user_int +\tl_new:N \l_@@_tstr_tl % \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]{\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} +% +% \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]{\@@_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} % -% \subsection{Parsing} +% \subsubsection{Parsing and other helpers} % -% \begin{macro}[aux]{\@@_equals_file_name:N} +% \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)} -% -% 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]{\@@_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} % -% \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{macrocode} -\cs_new_protected_nopar:Npn \@@_immediate:w - { \primargs_read_x_token:N \@@_immediate_ii: } -\cs_new_protected_nopar:Npn \@@_immediate_ii: +% \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: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 + { + \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 } { - \exp_after:wN \@@_immediate_v:w - \token_to_str:N #2 + \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} -% -% \subsubsection{Immediate closeout} +% \end{variable} % -% \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} +% +% 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. % -% We then put the stream and its associated file name in the property -% list, and empty/create the corresponding token list. +% \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} -% -% \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. +% \subsubsection{Immediate actions} +% +% 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 + { + \s_@@ + \use_i:nn + { \@@_get_user:n { \@@_closeout_later: } } + { \@@_get_user:n { \@@_closeout_now: } } + } +\cs_new_protected:Npn \@@_closeout_now: { - \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 } + \@@_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,132 +1017,111 @@ % \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: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 } } -\cs_new_protected_nopar:Npn \@@_closeout_aux: - { \@@_late:n { \@@_immediate_closeout_aux: } } % \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:} \\ % \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,30 +1138,32 @@ % 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 % 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 } \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,136 +1177,159 @@ % \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{macrocode} -\IfFileExists{atbegshi.sty} - { - \RequirePackage{atbegshi} - \tl_replace_once:Nnn \AtBegShi@Output - { \AtBegShi@OrgShipout \box \AtBeginShipoutBox } - { - \@@_before_shipout: - \AtBegShi@OrgShipout \box \AtBeginShipoutBox - \@@_after_shipout: - } - \tl_replace_once:Nnn \AtBegShi@Output - { \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox } - { - \@@_before_shipout: - \AtBeginShipoutOriginalShipout \box \AtBeginShipoutBox - \@@_after_shipout: - } +% \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} +\cs_new_protected: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: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@shipout + \PDFSYNCship@ut@ld + \CROP@shipout + \@soORI + \tex_shipout:D + \zwpl@Hship + \o@shipout@TP + \LL@shipout + \Shipout + \GXTorg@shipout + \AtBegShi@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_protected:Npn \@@_newread:N #1 + { + \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:Npn \@@_newwrite:N #1 +\cs_new_protected:Npn \@@_newwrite: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_write_int + \int_set_eq:NN \allocationnumber \g_@@_alloc_write_int \cs_undefine:N #1 \int_const:Nn #1 { \allocationnumber } \wlog @@ -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} +% +% \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}[aux, updated = 2012-12-05]{\immediate} -% \begin{macro}[aux]{\openout, \write, \closeout, \newwrite} -% \tn{shipout} has been redefined earlier. +% \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} % % % % \end{implementation} % -% \endinput +% \clearpage +% \PrintIndex diff --git a/Master/texmf-dist/source/latex/morewrites/primargs.dtx b/Master/texmf-dist/source/latex/morewrites/primargs.dtx index f6c3f78ce33..851263575a6 100644 --- a/Master/texmf-dist/source/latex/morewrites/primargs.dtx +++ b/Master/texmf-dist/source/latex/morewrites/primargs.dtx @@ -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,102 +32,195 @@ % % \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} -% -% 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} +% \subsection{Removing 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} +% +% \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} % -% \subsection{Internal functions} +% \begin{function}[updated = 2017-04-10, tested = primargs004]{\primargs_get_file_name:N, \primargs_get_input_file_name:N} +% \begin{syntax} +% \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}{\@@_get_rhs:NnN, \@@_get_rhs:NoN} +% \begin{function}{\__primargs_get_rhs:NnN, \__primargs_get_rhs:NoN} % \begin{syntax} -% \cs{@@_get_rhs:NnN} \meta{register} \Arg{register rhs} \meta{function} +% \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 @@ -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,41 +262,47 @@ % %<*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} %<@@=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,17 +489,60 @@ \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} % @@ -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} -% -% [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} +% \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} % -% +% % % \end{implementation} % -% \endinput +% \PrintIndex diff --git a/Master/texmf-dist/tex/latex/morewrites/morewrites.sty b/Master/texmf-dist/tex/latex/morewrites/morewrites.sty index 37b4f4838b6..5f87462496a 100644 --- a/Master/texmf-dist/tex/latex/morewrites/morewrites.sty +++ b/Master/texmf-dist/tex/latex/morewrites/morewrites.sty @@ -9,7 +9,7 @@ %% Communicate any suggestions for changing this package %% to Bruno Le Floch (blflatex@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,225 +19,401 @@ %% 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 + { + \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 } { - \exp_after:wN \__morewrites_immediate_v:w - \token_to_str:N #2 + \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_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 + { + \__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:Npn \__morewrites_immediate_closeout_test:n #1 +\cs_new_protected:Npn \__morewrites_read: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 { } { } + \__morewrites_tex_read:w \l__morewrites_tstr_tl \exp_stop_f: + } } -\cs_new_protected_nopar:Npn \__morewrites_immediate_closeout_aux: +\cs_new_protected:Npn \__morewrites_readline: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_readline: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_closein: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 { } { } + \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 } \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@Output - { \AtBegShi@OrgShipout \box \AtBeginShipoutBox } - { - \__morewrites_before_shipout: - \AtBegShi@OrgShipout \box \AtBeginShipoutBox - \__morewrites_after_shipout: - } - \tl_replace_once:Nnn \AtBegShi@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@shipout + \PDFSYNCship@ut@ld + \CROP@shipout + \@soORI + \tex_shipout:D + \zwpl@Hship + \o@shipout@TP + \LL@shipout + \Shipout + \GXTorg@shipout + \AtBegShi@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 diff --git a/Master/texmf-dist/tex/latex/morewrites/primargs.sty b/Master/texmf-dist/tex/latex/morewrites/primargs.sty index e4b0d10abc3..908e61f948d 100644 --- a/Master/texmf-dist/tex/latex/morewrites/primargs.sty +++ b/Master/texmf-dist/tex/latex/morewrites/primargs.sty @@ -9,7 +9,7 @@ %% Communicate any suggestions for changing this package %% to Bruno Le Floch (blflatex@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,49 +19,58 @@ %% 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 } \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,58 +165,103 @@ #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 } \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'. -- cgit v1.2.3