From 193b74d3d594d824d23f6413dd0d784fd4a756fc Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 19 Aug 2012 22:18:46 +0000 Subject: catoptions (19aug12) git-svn-id: svn://tug.org/texlive/trunk@27460 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/catoptions/README | 7 +- .../doc/latex/catoptions/catoptions-guide.tex | 37 +- .../texmf-dist/tex/latex/catoptions/catoptions.sty | 1368 ++++++++++++++++---- 3 files changed, 1143 insertions(+), 269 deletions(-) diff --git a/Master/texmf-dist/doc/latex/catoptions/README b/Master/texmf-dist/doc/latex/catoptions/README index 424170cc7d4..a37cfebb289 100644 --- a/Master/texmf-dist/doc/latex/catoptions/README +++ b/Master/texmf-dist/doc/latex/catoptions/README @@ -2,7 +2,7 @@ This is the README file for the 'catoptions' package. VERSION -Version 0.2.7b, 2011/12/17 +Version 0.2.7d, 2012/08/02 SUMMARY @@ -35,8 +35,9 @@ Ahmed Musa RELATED PACKAGES -The catcode section of the 'catoptions' package is based on the -'pcatcode' package by Michael J. Downes and David M. Jones. +The catcode section of the 'catoptions' package is an extension of +the 'pcatcode' package by Michael J. Downes and David M. Jones. + diff --git a/Master/texmf-dist/doc/latex/catoptions/catoptions-guide.tex b/Master/texmf-dist/doc/latex/catoptions/catoptions-guide.tex index 553438cd5b1..e4386d91223 100644 --- a/Master/texmf-dist/doc/latex/catoptions/catoptions-guide.tex +++ b/Master/texmf-dist/doc/latex/catoptions/catoptions-guide.tex @@ -17,6 +17,27 @@ \makeindex \let\footnote\ltsfootnote + +\newcount\nr +\def\alist{a1/b1/c1; a2/b2/c2; a3/b3/c3} +\trace* +\cptforeach*[;] \xa/\ya/\za \in \alist \do{% + \nr\z@pt + \cptforeach[,] \xb|\yb|\zb \in s1|t1|u1, s2|t2|u2, s3|t3|u3 \do{% + \advance\nr\@ne + \ifnum\nr>\@ne\ltsloopbreak\fi + % It is permissible to use #1, #2, #3, ##1, ##2, ##3, etc, + % to refer to the elements of each list, but they won't be + % despaced as \xa, \ya, \za, etc will be: + % \edef\cmda####1{#1#2#3*####1*##1##2##3}% + \edef\cmdb####1{\xa\ya\za*####1*\xb\yb\zb}% + }% +} + + +% \ifsingletokenTF +% \ifblankTF + \begin{document} \begin{frontmatter} @@ -793,7 +814,7 @@ Here are some points to note about these list processors: \item White spaces before and after the list separator are always ignored by the normalizer called by \ffx'{\csv@@parse,\kv@@parse}. If an item contains \ang{parser} or starts with a space, it must, therefore, be wrapped in curly braces before commencing these loops. \item Since \ffx'{\csv@@loop,\kv@@loop} don't call the normalizer, they preserve outer/surrounding spaces in the entries. Empty entries in \ang{list} or \ang{listcmd} will be processed by \ffx'{\csv@@loop,\kv@@loop} if the boolean \fx{cpt@useempty} is true. You may thus issue the command \fx{\UseEmptyEntry} or \fx{\DiscardEmptyEntry}, which are based on the boolean \fx{cpt@useempty}, before commencing the iteration. If empty entries are important to the task at hand, then issuing \fx{\UseEmptyEntry} or \fx{\DiscardEmptyEntry} prior to the commencement of the loop is recommended, because a previous call to either \fx{\csv@@loop} or \fx{\kv@@loop} (perhaps by another package) could have set \fx{cpt@useempty} to a state that is no longer valid or desired. \item The commands \ffx'{\csv@@loop,\csv@@parse,\kv@@loop,\kv@@parse} can be nested to any level and can be mixed. -\item In the commands \ffx'{\csv@@loop,\csv@@parse,\kv@@loop,\kv@@parse}, it is always possible to break out of the loop prematurely at any level of nesting, simply by issuing the command \fx{\loopbreak} (see the example below). Breaking an inner loop doesn't affect the continuation of the outer loop, and vice versa; that is, loop breaks are nest-level-dependent. +\item In the commands \ffx'{\csv@@loop,\csv@@parse,\kv@@loop,\kv@@parse}, it is always possible to break out of the loop prematurely at any level of nesting, simply by issuing the command \fx{\ltsloopbreak} (see the example below). Breaking an inner loop doesn't affect the continuation of the outer loop, and vice versa; that is, loop breaks are nest-level-dependent. \item The user can insert \fx{\csvbreak} as an element in \ang{list} for any of the commands \ffx'{\csv@@loop, \csv@@parse, \kv@@loop, \kv@@parse, \dofunclist} with the aim of automatically breaking out of the list processing prematurely (\ie, before the list is exhausted). The tokens \ffx'{\listbreak, \breaklist} are not defined or used by the \pkg'{catoptions}, to avoid name clashes with other packages. The \pkg'{catoptions} instead uses \fx{\csvbreak}, which is an unexpandable token; if it were to be expandable, then experience has shown that a chaotic infinite loop could arise in an expansion context. Breaking out of the loop prematurely on the current nest level doesn't affect the continuation of the loop on the other levels. \end{enum} @@ -820,7 +841,7 @@ Here are some points to note about these list processors: \def\csv@do##1{% \pushnumber\nrb |com(Breaking the inner loop here doesn't affect the outer loop:) - \ifnum\nrb>\@ne|R(\loopbreak)\fi + \ifnum\nrb>\@ne|R(\ltsloopbreak)\fi \csn@def{arg@\romannumeral\nra @\romannumeral\nrb}{#1,##1}% }% \csv@@parse*[;]\xb @@ -911,7 +932,7 @@ Empty entries in \ang{list} will be ignored by the normalizer called by \fx{\dof \chardef\n@\z@ \def\do##1{% \pushnumber\n@ - \ifnum\n@>\@ne|R(\loopbreak)\fi + \ifnum\n@>\@ne|R(\ltsloopbreak)\fi \csn@def{w@\romannumeral\m@ @\romannumeral\n@}{#1,##1}% }% \dofunclist[;]{x;{y};z}% @@ -932,13 +953,13 @@ Imagine an instance in which, while processing a csv list, you need not only the \finish{newmacro} \fxim*{\indrisloop,\indrisdepth,\currindris,\currindris@i, \nextindris,\nextindris@i,\indrisnr,\iflastindris} -\fxi*{\ifloopbreak} +\fxi*{\ifLTS@loopbreak} The \fx{\indrisloop} command can be used to iterate over a \ang{parser}-separated \ang{list} or \ang{listcmd} and execute the user-supplied one-parameter command \ang{fn} for every item in the list, passing the item as an argument and preserving outer braces. -The \fx{\indrisloop} iterator is of particular interest. The loop provides the macros \ffx'{\indrisdepth, \currindris, \currindris@i, \nextindris, \nextindris@i, \indrisnr, \iflastindris, \ifloopbreak}, which mean, respectively, the current nested depth/level, the current item on the current level, the current item on an arbitrary level \quotedfx{i}, the next item on the current level, the next item on an arbitrary level \quotedfx{i}, the numerical order of the current item on the current level, the boolean that indicates that the last item of the list has been reached on the current level of nesting, and the boolean that can be used to break the loop prematurely (before the list is exhausted) on any level. +The \fx{\indrisloop} iterator is of particular interest. The loop provides the macros \ffx'{\indrisdepth, \currindris, \currindris@i, \nextindris, \nextindris@i, \indrisnr, \iflastindris, \ifLTS@loopbreak}, which mean, respectively, the current nested depth/level, the current item on the current level, the current item on an arbitrary level \quotedfx{i}, the next item on the current level, the next item on an arbitrary level \quotedfx{i}, the numerical order of the current item on the current level, the boolean that indicates that the last item of the list has been reached on the current level of nesting, and the boolean that can be used to break the loop prematurely (before the list is exhausted) on any level. -You can do \fx{\iflastindris...\fi} on any level. On any given level, it is possible to break out of the current loop by simply issuing \fx{\loopbreak}. This would not affect the progress of the loops on other levels. When \fx{\iflastindris} is true on a given level \quotedfx{i}, then \ffx'{\nextindris,\nextindris@i} are empty on that level, but not necessarily the macros \ffx'{\currindris,\currindris@i}. +You can do \fx{\iflastindris...\fi} on any level. On any given level, it is possible to break out of the current loop by simply issuing \fx{\ltsloopbreak}. This would not affect the progress of the loops on other levels. When \fx{\iflastindris} is true on a given level \quotedfx{i}, then \ffx'{\nextindris,\nextindris@i} are empty on that level, but not necessarily the macros \ffx'{\currindris,\currindris@i}. The following provides a simple application of \fx{\indrisloop}, which I posted on \comptexttex in July~2011 in response to a posted question. @@ -1009,7 +1030,7 @@ The starred variants of \ffx'{\tsv@@loop,\tsv@@parse} expand \ang{listcmd} once Empty entries in \ang{list} or \ang{listcmd} will be processed if the boolean \fx{cpt@useempty} is true. You may thus issue the command \fxi{\UseEmptyEntry} or \fxi{\DiscardEmptyEntry} before commencing the iteration. Both commands \ffx'{\tsv@@loop,\tsv@@parse} can be nested to any level and can be mixed. -In the commands \ffx'{\tsv@loop,\tsv@@parse}, it is always possible to break out of the loop prematurely at any level of nesting by issuing the command \fx{\loopbreak}. Again, breaking an inner loop doesn't affect the continuation of the outer loop, and vice versa. The user can insert \fx{\tsvbreak} as an element in \ang{list} for any of the commands \ffx'{\tsv@@loop, \tsv@@parse} so as to automatically break out of the list processing prematurely (\ie, before the list is exhausted). +In the commands \ffx'{\tsv@loop,\tsv@@parse}, it is always possible to break out of the loop prematurely at any level of nesting by issuing the command \fx{\ltsloopbreak}. Again, breaking an inner loop doesn't affect the continuation of the outer loop, and vice versa. The user can insert \fx{\tsvbreak} as an element in \ang{list} for any of the commands \ffx'{\tsv@@loop, \tsv@@parse} so as to automatically break out of the list processing prematurely (\ie, before the list is exhausted). \start+{example}[\tsv@@loop,\tsv@@parse] \newcount\m @@ -1025,7 +1046,7 @@ In the commands \ffx'{\tsv@loop,\tsv@@parse}, it is always possible to break out \tsv@@loop{ a {b} {c} } \finish{example} - + \docsection(sec:version-hist){Version history} The following change history highlights significant changes that affect user utilities and interfaces; mutations of technical nature are not documented in this section. The numbers on the right-hand side of the following lists are section numbers; the \stsign means the subject features in the package but is not reflected anywhere in this user guide. diff --git a/Master/texmf-dist/tex/latex/catoptions/catoptions.sty b/Master/texmf-dist/tex/latex/catoptions/catoptions.sty index 1678941a59a..b5cd097fc37 100644 --- a/Master/texmf-dist/tex/latex/catoptions/catoptions.sty +++ b/Master/texmf-dist/tex/latex/catoptions/catoptions.sty @@ -1,5 +1,5 @@ %+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% -% This is 'catoptions' package, version 0.2.7b, 2011/12/17. % +% This is 'catoptions' package, version 0.2.7d, 2012/08/02. % % % % The catoptions package changes LaTeX package loading internals % % so that all subsequently loaded packages can rely on having % @@ -20,8 +20,7 @@ % % % The LPPL maintenance status of this software is 'author-maintained'. % % % -% Copyright (c) 2010-2011 Ahmed Musa (amusa22@gmail.com). % -% % +% Copyright (c) 2010-2012 Ahmed Musa (amusa22@gmail.com). % %+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% \begingroup @@ -85,15 +84,21 @@ \catcode94=7 \catcode126=13 \AtEndOfPackage{% + % Commands to discard later, after all the packages that + % need them have been updated on CTAN: \newletcsset{% \dofunclist=\cptdofunclist, \docommalist=\cptdocommalist, + \dosisolist=\cptdosisolist, \newvariables=\cptnewvariables, \stripallouterbraces=\cptstripallouterbraces, \stripouterbraces=\cptstripNouterbraces, + \stripallouterbracesincs=\cptstripallouterbracesincs, \gobble@to@relax=\cptgobbletorelax, \cpt@gobbletorelax=\cptgobbletorelax, - \cpt@gobbletorelax@fi@endgroup=\cptbgobbletorelax + \cpt@gobbletorelax@fi@endgroup=\cptbgobbletorelax, + \ifcasse=\cptifcasse, + \docasse=\cptdocasse, }% } \edef\cpt@hashchar{\string#} @@ -107,7 +112,7 @@ \chardef\active=13 \NeedsTeXFormat{LaTeX2e}[2011/06/27] \ProvidesPackage{catoptions} - [2011/12/17 v0.2.7b Securing catcodes and parsing options (AM)] + [2012/08/02 v0.2.7d Securing catcodes and parsing options (AM)] \ifcase% \ifx\eTeXversion\cpt@undefined\z@pt\else \ifnum\eTeXversion<\tw@\z@pt\else\@ne\fi\fi @@ -115,34 +120,49 @@ {This package requires eTeX version 2 or higher.}% \expandafter\endinput \fi +\protected\def\cptloggingall{% + \tracinggroups\@ne\tracingifs\@ne\tracingall\tracingassigns\@ne +} +\let\cpttracingall\cptloggingall % \ifcsdefinable, \cpt@starorlong, \cptcommanormalize, etc, % change later on: \@ifdefinable\ifcsdefinable{\let\ifcsdefinable\@ifdefinable} -\@ifdefinable\ifdefinablelet\relax -\protected\def\ifdefinablelet#1{\ifcsdefinable#1\relax\let#1} -\ifdefinablelet\cpt@nil\relax +\@ifdefinable\cptnewlet\relax +% Don't put '=' after \let#1, in case the caller uses '=': +\protected\def\cptnewlet#1{\ifcsdefinable#1\relax\let#1} +\cptnewlet\cpt@nil\relax \newcommand*\cpt@nnil{\cpt@nil} \let\@iden\@firstofone -\ifdefinablelet\iofii\@firstoftwo -\ifdefinablelet\iiofii\@secondoftwo +\cptnewlet\iofii\@firstoftwo +\cptnewlet\iiofii\@secondoftwo \@ifdefinable\ifforbreak{\newif\ifforbreak} -\ifdefinablelet\cpt@starorlong\@star@or@long -\ifdefinablelet\cpt@testopt\@testopt -\ifdefinablelet\cptcommanormalize\unexpanded -\protected\def\ifnot@nil#1{% +\cptnewlet\cpt@starorlong\@star@or@long +\cptnewlet\cpt@testopt\@testopt +\cptnewlet\cptcommanormalize\unexpanded +\protected\def\ifnotnilTF#1#2{% \begingroup - \edef\reserved@a{\unexpanded{#1}}% + \edef\reserved@a{\cpttrimspace{#1}}% + \def\reserved@b{#2}% \expandafter\endgroup - \ifx\reserved@a\@nnil\expandafter\@gobble + \ifx\reserved@a\reserved@b\expandafter\@gobble \else\expandafter\@iden\fi } +\newcommand*\defifxTF[2]{% + \begingroup + \edef\reserved@a{\cpttrimspace{#1}}% + \expandafter\endgroup\ifx#2\reserved@a + \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi +} \newcommand*\cpt@for@nil{\cpt@for@nil} +% Simple, 1-dimensional comma loop. \forbreak can be used to break +% out of the loop. \do may appear in #1; so we can't use \do as delimiter: \protected\def\cptfor#1\dofor#2{% \def\cpt@fordo##1,{\cpt@for@a,\cpt@fordo{##1}{#2}}% \edef\reserved@a{\cptcommanormalize{#1}}% \forbreakfalse \expandafter\cpt@fordo\reserved@a,\cpt@for@nil,% } +% Simple, 1-dimensional tsv loop: \protected\def\cpttfor#1\dofor#2{% \def\cpt@tfordo##1{\cpt@for@a{}\cpt@tfordo{##1}{#2}}% \forbreakfalse\cpt@tfordo#1\cpt@for@nil @@ -155,6 +175,19 @@ {\def\reserved@a##1\cpt@for@nil#1{}\reserved@a}{#4#2}% }% } +% \foreachmeer{} +% Expandable comma loop. The \noexpand before #1 is required when #1 +% begins with a command. must be pre-defined by the user: +\newcommand*\foreachmeer[2]{% + \expandafter\cpt@foreachmeer@a\romannumeral + -`\q\cptcommanormalize{\noexpand#1},\cpt@nnil,\meer@nil{#2}% +} +\def\cpt@foreachmeer@a#1,#2\meer@nil#3{% + \expandafter\ifx\cpt@car#1\car@nil\cpt@nnil + \expandafter\iofii\else\expandafter\iiofii\fi + {}{#3{#1}\cpt@foreachmeer@a#2\meer@nil{#3}}% +} +% Unique \chardef'd token: \ifcsdefinable\cptchardef{% \protected\def\cptchardef#1#2 {% \ifcsdefinable#1{% @@ -183,8 +216,19 @@ {\expandafter\newcount\csname#1##1\endcsname}% }% } -\newcommand*\cptnewcounters[2][]{% - \cptfor#2\dofor{\newcounter{#1##1}}% +% \cptnewcounters[]{a,b}[] +\newcommand*\cptnewcounters{\cpt@testopt\cpt@newcounters{}} +\def\cpt@newcounters[#1]#2{% + \cpt@testopt{\cpt@newc@unters{#1}{#2}}{}% +} +\def\cpt@newc@unters#1#2[#3]{% + \cptfor#2\dofor{% + \ifcat&\detokenize{#3}&\relax + \newcounter{#1##1}% + \else + \newcounter{#1##1}[#3]% + \fi + }% } \newcommand*\cptnewdimens[2][]{% \cptfor#2\dofor{% @@ -211,8 +255,11 @@ }% } \newcommand\ifxFT[2]{\ifxTF{#1}{#2}\iiofii\iofii} +\def\cpt@ifcseq@#1#2\cpt@ifcseq@nil{\expandafter#1} \newcommand\ifcseqTF[2]{% - \ifx#1#2\cpt@quark\expandafter\iofii\else\expandafter\iiofii\fi + \csname @\expandafter\expandafter\expandafter + \ifx\cpt@ifcseq@#1\batchmode\cpt@ifcseq@nil\cpt@car#2\noboundary\car@nil + first\else second\fi oftwo\endcsname } \newcommand\ifcseqFT[2]{\ifcseqTF{#1}{#2}\iiofii\iofii} \newcommand*\ifxcseqTF[2]{% @@ -224,12 +271,6 @@ \iofii\iiofii }% } -\newcommand*\defifxTF[2]{% - \begingroup - \edef\reserved@a{\unexpanded{#1}}% - \expandafter\endgroup\ifx#2\reserved@a - \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi -} \newcommand\ifnullTF[3]{% \romannumeral\iffalse{\fi\expandafter\@secondoftwo\expandafter {\expandafter{\string#1}\expandafter\@secondoftwo\string}% @@ -247,20 +288,9 @@ \ifcat$\detokenize\expandafter{\@gobble#1.}$% } \newcommand\ifbracedTF[3]{% - \romannumeral - \ifcase - \iffalse{{{\fi\expandafter\expandafter\expandafter - \@gobble\expandafter\expandafter\expandafter - {\expandafter\expandafter\expandafter{\expandafter - \iffalse\expandafter}\expandafter}\expandafter\fi - \string#1}\m@one}\@ne}\z@pt - \expandafter\@secondoftwo - \or - \expandafter\@secondoftwo - \else - \expandafter\@firstoftwo - \fi - {0 #2}{0 #3}% + \romannumeral\expandafter\@secondoftwo\expandafter{\expandafter + {\string#1.}\expandafter\@firstoftwo\expandafter{\iffalse}\fi + 0 #2}{0 #3}% } \newcommand\ifbracedFT[1]{\ifbracedTF{#1}\iiofii\iofii} \newcommand\ifleadspaceTF[3]{% @@ -272,7 +302,6 @@ \expandafter\ifnullTF\expandafter{\@gobble#1}{first}{second}% \expandafter\@gobble\expandafter{\iffalse}\fi } -%%++ Ulrich Diez: \newcommand\iftrailspaceTF[1]{% \romannumeral-`\q \csname @\expandafter\@firstofone\expandafter{% @@ -299,7 +328,6 @@ \expandafter\cpt@iftrailspace\expandafter{\cpt@removetospace.#1}% }% } -%%-- \ifxTF\pdfstrcmp\cpt@undefined{% \RequirePackage{pdftexcmds}% @@ -517,7 +545,7 @@ \def\neededfutureletchars{}% \def\cpt@tempa##1\@##2\@{% \ifx##1\end\else - \cptgaddtolist\neededfutureletchars{##1}% + \cptguaddtolist\neededfutureletchars{##1}% \expandafter\cpt@tempa \fi }% @@ -662,6 +690,7 @@ \ifcat\noexpand#1\noexpand~13\else -1\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi } + \robust@def\cpt@ifnextchar#1#2#3{% \begingroup \let\reserved@a=#1% @@ -704,6 +733,7 @@ \ifx\x first\else second\fi oftwo\endcsname {#2}{#3}#4% } +% Must be expandable: \new@def\cpt@ifn@xtch@racter#1{% \expandafter\cpt@ifn@xtch@r@cter\string#1\relax\noboundary{#1}% } @@ -714,8 +744,12 @@ \robust@redef*\cpt@starorlong#1{% \cpt@ifstar{\let\l@ngrel@x\relax#1}{\let\l@ngrel@x\long#1}% } -\new@def*\ifsecondisnnilTF#1#2\cpt@nil{% - \expandafter\ifxTF\cpt@car#2\car@nil\cpt@nnil +\new@def*\ifsecondisnnilTF#1{% + \expandafter\expandafter\expandafter\cpt@ifsecondisnnil + \expandafter\string\noexpand#1\cpt@nnil\cpt@nil +} +\new@def*\cpt@ifsecondisnnil#1#2\cpt@nil{% + \expandafter\ifcseqTF\cpt@car#2\car@nil\cpt@nnil } \new@def\aftercsname#1#2{% \expandafter\cptswap\expandafter{\csname#2\endcsname}{#1}% @@ -732,7 +766,7 @@ } %% For internal use; #1 must be one token: \new@def*\simpleexpandarg#1#2{\expandafter#1\expandafter{#2}} -\ifdefinablelet\s@expandarg\simpleexpandarg +\cptnewlet\s@expandarg\simpleexpandarg %%-- \new@def*\cptexpandnext#1#2{% @@ -745,7 +779,7 @@ \new@def*\cptexpandbracenext#1#2{% \expandafter\cptswapbracefirst\expandafter{#2}{#1}% } -\ifdefinablelet\cptexpandbrnext\cptexpandbracenext +\cptnewlet\cptexpandbrnext\cptexpandbracenext \new@def*\cptexpandsome#1\expandthis#2#3\expandstop{% \cptexpandnext{#1}{#2}#3% } @@ -773,11 +807,21 @@ \ifdefTF{#1}{#1}{\cpt@notdeferr{#1}}% } \new@def*\usecsifdef#1{\ifdefTF#1{#1}{}} +\new@def*\expandonceifmacroelsenil#1{% + \ifmacroTF{#1}{% + \ifexpandableTF{#1}{% + \expandcsonce{#1}% + }{% + \unexpanded{#1}% + }% + }{}% +} \new@def\usename#1{\csname#1\endcsname}% long! \new@def\usecsn#1{% \ifcsname#1\endcsname\expandafter\@iden\else \expandafter\@gobble\fi{\usename{#1}}% } +% \usecsnifdef is stronger than \usecsn: \new@def*\usecsnifdef#1{\ifcsndefTF{#1}{\usename{#1}}{}} \new@def\uselivecsn#1{% \ifcsndefTF{#1}{% @@ -787,11 +831,13 @@ \cpt@err{Command '\@backslashchar#1' undefined}\@ehc }% } -\new@def*\ifcondTF#1\fi#2#3{% - \romannumeral#1 \expandafter\@firstoftwo\else - \expandafter\@secondoftwo\fi{0 #2}{0 #3}% + +% The following syntax is needed for calling \ifcondTF in \edef. +\new@def\ifcondTF#1\endcond#2#3{% + \romannumeral\csname @#1first\else second\fi oftwo\endcsname + {0 #2}{0 #3}% } -\long\def\ifcondFT#1\fi#2#3{\ifcondTF#1\fi{#3}{#2}} +\new@def\ifcondFT#1\endcond#2#3{\ifcondTF#1\endcond{#3}{#2}} \new@def*\ifcsnameTF#1{% \ifblankTF{#1}\@secondoftwo{% \usename{@\ifcsname#1\endcsname first\else second\fi oftwo}% @@ -825,9 +871,9 @@ \cptpassexpanded{% \endgroup \ifcase#1\relax - \ifboolTF{cpt@choicefd}\iofii\iiofii + \ifdefboolTF{cpt@choicefd}\iofii\iiofii \or - \ifboolTF{cpt@choicefd}\iiofii\iofii + \ifdefboolTF{cpt@choicefd}\iiofii\iofii \else \cpt@err{Invalid number '#1'}\noexpand\@ehc \fi @@ -838,6 +884,9 @@ {\detokenize{macro parameter character}} {\meaning\@let@token}% } +% \cptcommanormalize is cheap but is restricted to comma lists only. +% Also, it isn't meant for parsing keyval lists. See \kv@@normalize +% and \kv@@parse: \begingroup \uccode`\~=`\,% \uppercase{\endgroup @@ -968,6 +1017,9 @@ \cpt@tempb\cpt@tempa } \endgroup + +% We need the equivalent of '\protected\let' and we want to be able to +% do \shelllet\x=\y: \robust@def*\shelllet#1{\cpt@ifeql{\cpt@shelllet{#1}}{\cpt@shelllet{#1}}} \robust@def*\cpt@shelllet#1#2{% \ifblankTF{#2}{% @@ -975,6 +1027,7 @@ }{% \expandafter\ifblankTF\expandafter{\@gobble#2}{% \ifcsdefinable#1{% + % Here we must use \let and not \def (for immediate inheritance): \ifprotectedTF#2{\let#1=#2}{% \ifcsndefinable{shell@\cptremovescape#1}{% \letcsntocs{shell@\cptremovescape#1}=#2% @@ -1005,6 +1058,8 @@ \new@def\cpt@car#1#2\car@nil{#1} \new@def\cpt@cdr#1#2\cdr@nil{#2} \ifcsdefinable\cpt@relax{\let\cpt@relax\relax} +% This is needed to define phantom environments: +\newcommand*\phantom@env{\relax} \new@def\cptgobbletorelax#1\cpt@relax{} \new@def\cptbgobbletorelax\fi#1\cpt@relax{\fi\endgroup} \new@def\cpt@removetonnil#1\cpt@nnil{} @@ -1023,6 +1078,7 @@ \ifcseqTF\reserved@a\cpt@csvbreak } \ifcsdefinable\cptdoremainder{\let\cptdoremainder\@gobble} +% #1: sentinel; #2: parser \robust@def*\gobble@to@sentinel#1#2{% \defpass\reserved@a##1#1#2{\cptdoremainder{##1}}% } @@ -1045,7 +1101,7 @@ } \new@def*\cptdotimes#1{% \ifblankTF{#1}{% - \ifboolTF{cpt@useempty}{\cpt@dotimes{m}}\@gobble + \ifdefboolTF{cpt@useempty}{\cpt@dotimes{m}}\@gobble }{% \expandafter\cpt@dotimes\expandafter {\romannumeral\number#1000}% @@ -1078,7 +1134,7 @@ \new@def*\normal@reverse#1{\cpt@normalreverse{}#1\@nnil} \long\def\cpt@normalreverse#1#2{% \expandafter\ifxTF\cpt@car#2\car@nil\@nnil{% - \ifboolTF{@test}\@iden\unexpanded{#1}% + \ifdefboolTF{@test}\@iden\unexpanded{#1}% }{% \cpt@normalreverse{#2#1}% }% @@ -1101,7 +1157,7 @@ } \long\def\cpt@r@bustreverse#1#2.#3{% \expandafter\ifxTF\cpt@car#3\car@nil\@nnil{% - \ifboolTF{@test}\@iden\unexpanded{#1}% + \ifdefboolTF{@test}\@iden\unexpanded{#1}% }{% #2{\cpt@robustreverse{{#3}#1}}{\cpt@robustreverse{#3#1}}.% }% @@ -1161,7 +1217,22 @@ \robust@def*\despacecontent#1{% \edef#1{\simpleexpandarg\cpttrimspaces{#1}}% } +\robust@def\cptusesimpletrimspaces{% + \let\cptcurrtrimspaces\cpttrimspace +} +\cptusesimpletrimspaces +\let\cptusesimpletrimspace\cptusesimpletrimspaces +\robust@def\cptusehardtrimspaces{% + \let\cptcurrtrimspaces\cpttrimspaces +} +\let\cptusehardtrimspace\cptusehardtrimspaces +% It is more efficient to use \csv@@normalize in place +% of \cptdespacecsvlist. Also, \csv@@normalize allows parsers +% to be made innocent. But the following is needed in some +% instances (eg, where we don't want to sanitize the parser): \robust@def*\cptdespacecsvlist{\cpt@teststopt\cpt@despacecsvlist,} +% #1: arbitrary parser +% * -> #2 is a , otherwise a \robust@def*\cpt@despacecsvlist[#1]#2{% \cpt@stchoose{cpt@st}{#2}\cpt@tempa\cpt@despacecsvlist \ifcsnullTF\cpt@tempa{}{% @@ -1169,13 +1240,17 @@ \UseEmptyEntry \def\csv@do##1{% \edef\cpt@tempa{\cpttrimspaces{##1}}% - \cptaddtolist[#1]\cpt@rsvda\cpt@tempa + \cptoaddtolist[#1]\cpt@rsvda\cpt@tempa }% \csv@@loop*[#1]\cpt@tempa \ifdefboolFT{cpt@st}{}{\let#2=\cpt@rsvda}% }% } % \cptnewvariables{}[]{}[] +% If is 'toks', is the content of the +% new token register. +% Valid are 'if, toks, dimen, count, tog, switch'. +% Boxes can't be initialized by this command. \robust@def*\cptnewvariables#1{\cpt@testopt{\cpt@newvariables{#1}}{}} \robust@def*\cpt@newvariables#1[#2]#3{% \edef\reserved@a{\noexpand\cpt@testopt{% @@ -1219,6 +1294,7 @@ }% }{% \@tempswatrue + % Declare the new switch: \ifstrcmpTF{#1}{switch}{% \lowercase{% \ifstrcmpTF{#4}{true}{% @@ -1238,8 +1314,10 @@ \expandafter\let\csname#2##1\endcsname\reserved@a }{% \@tempswafalse + % Declare the new variable: \csname new#1\expandafter\endcsname\csname \cpt@tempb#2##1\endcsname + % Initialize the new variable: \ifstrcmpTF{#1}{if}{% \cpt@rsvda{false}% \csname#2##1\cpt@tempc\endcsname @@ -1300,8 +1378,28 @@ \endgroup \fi } -\cptnewvariables{if}[cpt@]{usepox,optfd,igopt,inpox,olddx, + +\cptnewvariables{if}[cpt@]{usepox,optfd,noval,igopt,inpox,olddx, in,check,choicefd,useempty,swa}[false] + +% st -> I saw star +% pl -> I saw plus +% pm -> I saw prime +% cl -> I saw exclamation mark +% verbose -> log information (package option) +% usepox -> use \XDeclareOption and \XProcessOptions for +% \DeclareOption and \ProcessOptions (package option) +% igopt -> ignore option +% optfd -> option found +% inpox -> we're in \XProcessOptions +% olddx -> we're in \DeclareOption and not in \XDeclareOption +% in -> for use in \cpt@in +% check,sw -> generic booleans ('check' for short intervals; +% 'sw' for longer intervals) +% choicefd -> generic (choice found) +% loopbreak -> terminate loop +% useempty -> use empty entry (in loops) +% \cptnewvariables{tog}{biboolopts}[false] \cptnewvariables{count}[cpt@]{optdepth,cnta}[\z@pt] \new@def*\UseEmptyEntry{\global\cpt@useemptytrue} @@ -1370,7 +1468,16 @@ \robust@def*\ltxmsg@info{\@latex@info@no@line} \robust@def*\ltxmsg@err{\@latex@error} \robust@redef\cpt@testopt#1#2{\ifnextcharTF[{#1}{#1[{#2}]}} + +% Eg, \def\csa{\cpt@testcharopt\csb<{default}>} +% \def\csb<#1>{...} \robust@def*\cpt@testcharopt#1#2#3#4{\ifnextcharTF#2{#1}{#1#2{#3}#4}} + +% Expandable \@testopt: +% Eg, \def\macroa#1{\cpt@xtestopt{#1}\macrob{Mr.}} +% \def\macrob[#1]#2{#1 #2} +% \edef\testa{\macroa{John Johnson}} +% \edef\testb{\macroa[Dr.]{Haiki Heiki}} \new@def*\cpt@xtestopt#1#2#3{% \cpt@xifbrack#1\\[\\{#2#1}{#2[{#3}]{#1}}% } @@ -1378,7 +1485,13 @@ \ifcat$\detokenize{#1#2}$\expandafter\@firstoftwo \else\expandafter\@secondoftwo\fi } +% Eg, \def\macroa{\cpt@newxtestopt\macrob{Mr.}} +% \def\macrob[#1]#2{#1 #2} +% \edef\testa{\expandafter\macroa\activespace{John Johnson}} +% \edef\testb{\macroa[Dr.]{Haiki Heiki}} \new@def*\cpt@newxtestopt#1#2#3{% + % #3 will be a space only if that space is active. But + % \ifstrcmpTF will detokenize that space before comparison. \ifstrcmpTF{ }{#3}{% \cpt@newxtestopt#1{#2}% }{% @@ -1456,8 +1569,15 @@ \cpt@notonetokenerr{#1}% }% } -% Eg, \newletcsset{\a=\b,\x\y} +\robust@def*\letcsset{% + \@tempswafalse + \cpt@ifstar + {\let\dolet\ltsfulllet\cpt@newletcsset}% + {\let\dolet\let\cpt@newletcsset}% +} +% Eg, \newletcsset{\a\b,\x\y} \robust@def*\newletcsset{% + \@tempswatrue \cpt@ifstar {\let\dolet\ltsfulllet\cpt@newletcsset}% {\let\dolet\let\cpt@newletcsset}% @@ -1470,11 +1590,15 @@ \ifblankFT{##2}{}{% \cpt@err{The partner of command '\string##1' is void}\@ehc }% - \ifdefTF{##1}{% - \ifx##1##2\else\cpt@isdeferr{##1}\fi - }{% + \if@tempswa + \ifdefTF{##1}{% + \ifx##1##2\else\cpt@isdeferr{##1}\fi + }{% + \dolet##1=##2% + }% + \else \dolet##1=##2% - }% + \fi }% \cptfor#1\dofor{% \ifinsetTF{=}{##1}{% @@ -1504,6 +1628,7 @@ \gdef#2##1\string\count##2&{\noexpand\ifblankTF{##1}}% } \cpt@ifcounter@a\cpt@ifcounter@a\cpt@ifcounter@b +% Similar to \ifcounterTF. Don't call this \ifdimTF: \new@gdef*\ifdimenTF#1{% \ifcseqTF#1\dimen{% \@secondoftwo @@ -1518,6 +1643,7 @@ \cpt@ifdimen@a\cpt@ifdimen@a\cpt@ifdimen@b \endgroup +% #1 can be arbitrary number (\countdef'd or otherwise): \robust@def*\pushnumber#1{% \ifsingletokenTF{#1}{% \ifdefTF{#1}{% @@ -1552,12 +1678,17 @@ \cpt@notonetokenerr{#1}% }% } +% #1: calling/base function to push +% #2: comma-separated state list (subsidiary functions) to push +% #3: arbitrary number \robust@def*\pushfunctions#1#2#3{% \pushnumber{#3}% \begingroup \edef\cpt@stack{\cptremovescape{#1}@stack}% - \ifcsndefTF\cpt@stack{}{\csn@gdef\cpt@stack{}}% + \ifcsndefTF\cpt@stack{}{\csn@def\cpt@stack{}}% \toks@{}\toks1{}\escapechar92\relax + % Using \csv@@loop or \csv@@parse here will bomb out, + % because \pushfunctions is called within those loops: \let\xp\expandcsonce \cptfor#2\dofor{% \ifescapedTF{##1}{}{\cpt@notescapederr{##1}}% @@ -1571,6 +1702,8 @@ \csn@xdef\cpt@stack{\the\toks1\cpt@nil{\expandcsnonce\cpt@stack}}% \expandafter\endgroup\the\toks@ } +% #1: calling/base function to pop +% #2: depth counter \robust@def*\popfunctions#1#2{% \ifdefTF{#2}{% \ifnum#2=\z@pt @@ -1592,6 +1725,9 @@ \popnumber{#2}% } \cptchardef\cpt@optionstacklimit=4 +% #1: calling (base) function +% #2: state list (a macro) +% #3: state counter \robust@def*\cpt@pushstate#1#2#3{% \ifnum#3>\cpt@optionstacklimit\relax \let\x\cpt@optionstacklimit @@ -1605,9 +1741,11 @@ \fi \cptexpandargonce{\pushfunctions#1}{#2}{#3}% } +% #1: calling/base function +% #2: state counter \robust@def*\cpt@popstate#1#2{\popfunctions{#1}{#2}} \robust@def*\cpt@stchoose#1#2#3#4{% - \ifboolTF{#1}{% + \ifdefboolTF{#1}{% \ifmacroTF{#2}{% \let#3=#2% }{% @@ -1619,7 +1757,7 @@ \edef#3{\unexpanded{#2}}% }% } -\robust@def*\cpt@delimitcs#1#2{% +\robust@def*\cptdelimitcs#1#2{% \begingroup \def\cpt@tempb##1{% \edef\cpt@tempa####1{% @@ -1642,15 +1780,23 @@ \cpt@err{Your command \noexpand#1 isn't scannable. \MsgBrk I needed to delimit its last parameter text}\@ehc }% + % Because of parameters, \postgroupdef can be used here: \gletcs#1\cpt@tempa \endgroup } +% First provide \def\do{} +% \cptdofunclist[]{} +% \cptdofunclist*[]{} +% The command expects \do, which can be multi-parametered. +% Parameters of \do are delimited internally, relieving the user +% of this need. Calling \dofunclist is equivalent to defining +% a multi-parametered \do within \csv@do: \robust@def*\cptdofunclist{% \pushnumber\dofunclistdepth \begingroup \edef\sav@do{\cptmakecs{sav@do@\cptrm\dofunclistdepth}}% \expandafter\let\sav@do\do - \expandafter\cpt@delimitcs\sav@do\cpt@nil + \expandafter\cptdelimitcs\sav@do\cpt@nil \toks@{\def\csv@do##1}% \toks1\expandafter{\expandafter\expandafter\sav@do \cptrm\ifbracedTF{##1}{0{##1}}{0 ##1}\cpt@nil}% @@ -1661,44 +1807,45 @@ \robust@def*\cptdocommalist{\cpt@testst\cpt@docommalist} \robust@def*\cpt@docommalist#1#2{% \def\csv@do##1{#2}% - \edefpass\reserved@a{\csv@@parse\ifcpt@st*\fi}% - {#1}% -} -\robust@def*\csv@@loop{\cpt@csvkvparse{01}{01}} -\robust@def*\csv@@parse{\cpt@csvkvparse{00}{01}} -\robust@def*\kv@@loop{\cpt@csvkvparse{01}{00}} -\robust@def*\kv@@parse{\cpt@csvkvparse{00}{00}} -\new@def*\cpt@csvkvparse#1#2{% + \cptexpanded{\csv@@parse\ifcpt@st*\fi}{#1}% +} +\robust@def*\csv@@loop{\cpt@listparse{01}{01}} +\robust@def*\csv@@parse{\cpt@listparse{00}{01}} +\robust@def*\kv@@loop{\cpt@listparse{01}{00}} +\robust@def*\kv@@parse{\cpt@listparse{00}{00}} +\new@def*\cpt@listparse#1#2{% \def\cpt@nmlz{#1}\def\cpt@inkv{#2}% - \cpt@teststopt\cpt@csvkvp@rse,% + \cpt@teststopt\cpt@listp@rse,% } \newletcs\cpt@csvnil\relax \new@def*\cpt@csvnnil{\cpt@csvnil} -\newcommand*\cpt@csvkvp@rse[2][,]{% - \cpt@stchoose{cpt@st}{#2}\cpt@csvarg\cpt@csvkvparse - \pushfunctions\cpt@csvkvparse{\cpt@csvdo@a,\cpt@csvdo@b,\sav@csv@do,% - \cpt@loopcbk,\ifcpt@useempty,\ifloopbreak,\cptdoremainder}\cpt@csvdepth - \letcstocsn\sav@csv@do{\if\cpt@inkv kv\else csv\fi @do}% - \ifdefTF\sav@csv@do{}{% - \cpt@err{'\string\csv@do' or '\string\kv@do' undefined}\@ehc - }% - \def\cpt@csvdo@a{% - \ifboolTF{loopbreak} - {\gobble@to@sentinel\cpt@csvnil#1}{\cpt@csvdo@b.}% - }% - \def\cpt@csvdo@b##1#1{% +\newcommand*\cpt@listp@rse[2][,]{% + \cpt@stchoose{cpt@st}{#2}\cpt@csvarg\cpt@listparse + % \cpt@nmlz and \cpt@inkv are used before the commencement + % of the loop: no need to include them in the state list. + % \cpt@loopcbk is meant for callbacks with hash characters. + % It can be defined outside \csv@do or \kv@do and called within + % \csv@do or \kv@do. + \pushfunctions\cpt@listparse{\cpt@listdo,\curr@do,\cpt@loopcbk,% + \ifcpt@useempty,\ifloopbreak,\cptdoremainder}\cpt@csvdepth + \letcstocsn\curr@do{\if\cpt@inkv kv\else csv\fi @do}% + \ifdefTF\curr@do{}{% + \cpt@err{'\string\csv@do' or '\string\kv@do' is undefined}\@ehc + }% + \def\cpt@listdo##1#1{% \edef\cpt@csvarg{\expandcsonce{\@gobble##1}}% \ifcseqTF\cpt@csvarg\cpt@csvnnil{}{% \ifcseqTF\cpt@csvarg\cpt@csvbreak{% \loopbreaktrue }{% \ifcsemptyTF\cpt@csvarg{% - \ifboolFT{cpt@useempty}{}{\sav@csv@do{}\relax}% + \ifboolFT{cpt@useempty}{}{\curr@do{}\relax}% }{% - \expandafter\sav@csv@do\expandafter{\cpt@csvarg}\relax + \s@expandarg\curr@do\cpt@csvarg\relax }% }% - \cpt@csvdo@a + \ifdefboolTF{loopbreak} + {\gobble@to@sentinel\cpt@csvnil#1}{\cpt@listdo.}% }% }% \ifswitchFT{cpt@nmlz}{}{% @@ -1709,59 +1856,164 @@ }% }% \loopbreakfalse - \expandafter\cpt@csvdo@a\cpt@csvarg#1\cpt@csvnil#1% - \popfunctions\cpt@csvkvparse\cpt@csvdepth + \expandafter\cpt@listdo\expandafter.\cpt@csvarg#1\cpt@csvnil#1% + \popfunctions\cpt@listparse\cpt@csvdepth \ifnum\cpt@csvdepth=\z@pt - \cptfor\csv@do,\kv@do,\dofunclistdepth\dofor{\undefcs##1}% + % So that these functions are defined before the loop is called: + \undefcs\csv@do\undefcs\kv@do\undefcs\dofunclistdepth \fi } -\robust@def\indris@nil#1\fi#2\indris@do#3\indris@nil{\fi#1#2} +% #1=keyval list, #2=processor +\robust@def*\kv@@process#1#2{% + \pushfunctions\kv@@process{\kv@do,\ifloopbreak,#2}\cpt@csvdepth + \def\kv@do##1,{% + \defifxTF{##1}\cpt@nnil{}{% + \def\reserved@a####1={% + \edef\key@name{\unexpanded{####1}}% + \def\reserved@a########1=########2\cpt@nil{% + \edef\key@value{\expandcsonce{\@gobble########1}}% + \ifx\key@value\cpt@rej + \cpt@novaltrue\def\key@value{}% + \else + \cpt@novalfalse + \fi + \cptexpandtwoargsonce{#2}\key@name\key@value + }% + \reserved@a.% + }% + \reserved@a##1=^?^=\cpt@nil + \ifloopbreak + \def\kv@do####1\cpt@nil,{}% + \fi + \kv@do + }% + }% + \kv@@normalize{#1}% + \expandafter\kv@do\normalized@list,\cpt@nil,% + \popfunctions\kv@@process\cpt@csvdepth +} +\robust@def*\cpt@setupuserdo#1{% + \ifsingletokenTF{#1}{% + \ifmacroTF{#1}{\let\user@do#1}{\cpt@notcserr{#1}}% + }{% + \cpt@notonetokenerr{#1}% + }% +} +\robust@def*\cpt@setupnextindris#1{% + \def\getnextindris##1#1##2\indris@nil{% + \if\cpt@incsv + \edef\nextindris{\expandcsonce{\@gobble##1}}% + \else + \edef\nextindris{\s@expandarg\cpttrimspace{\@gobble##1}}% + \fi + \letcsntocs{nextindris@\cptrm\indrisdepth}\nextindris + }% +} \cptnewvariables{if}{lastindris}[false] +\cptnewvariables{count}{indrisdepth}[\z@pt] \robust@def*\indrisloop{\cpt@teststopt\cpt@indrisloop,} +% #1: parser +% #2: list +% #3: a one-parameter recurrent macro (eg, \do) \robust@def*\cpt@indrisloop[#1]#2#3{% - \pushfunctions\cpt@indrisloop{% - \indris@do,\indrisnr,\iflastindris,\ifloopbreak,% - \user@do,\getnextindris,\cptdoremainder - }\cpt@csvdepth - \pushnumber\indrisdepth + \pushfunctions\indrisloop{\indris@do,\indrisnr,% + \iflastindris,\ifloopbreak,\user@do,\getnextindris,% + \cptdoremainder}\cpt@csvdepth + \advance\indrisdepth\@ne \cpt@stchoose{cpt@st}{#2}\cpt@rsvda\indrisloop \cpt@sttrue\cpt@csvnormalize[#1]\cpt@rsvda \lastindrisfalse\loopbreakfalse \chardef\indrisnr\z@pt - \ifsingletokenTF{#3}{% - \ifmacroTF{#3}{}{\cpt@notcserr{#3}}% - }{% - \cpt@notonetokenerr{#3}% + \cpt@setupuserdo{#3}% + \cpt@setupnextindris{#1}% + \let\cpt@incsv\cptswtrue + \def\indris@do##1#1##2\indris@nil{% + \s@expandarg\ifcsvbreakTF{\@gobble##1}\loopbreak\relax + \ifdefboolTF{loopbreak}{% + \cptdoremainder{##2}% + }{% + \edef\currindris{\expandcsonce{\@gobble##1}}% + \letcsntocs{currindris@\cptrm\indrisdepth}\currindris + \edef\indrisnr{\the\numexpr\indrisnr+1}% + \getnextindris.##2\indris@nil + \ifcseqTF\nextindris\cpt@nnil{% + \lastindristrue\def\nextindris{}% + \csn@def{nextindris@\cptrm\indrisdepth}{}% + \s@expandarg\user@do\currindris\relax + }{% + \s@expandarg\user@do\currindris\relax + \indris@do.##2\indris@nil + }% + }% }% - \let\user@do#3% - \def\getnextindris##1#1##2\indris@nil{% - \edef\nextindris{\unexpanded\expandafter{\@gobble##1}}% - \letcsntocs{nextindris@\cptrm\indrisdepth}\nextindris + \expandafter\indris@do\expandafter.\cpt@rsvda#1\cpt@nil#1\indris@nil + \popfunctions\indrisloop\cpt@csvdepth + \advance\indrisdepth\m@one +} + +% #1=list, #2=operator/format +% Eg, +% \def\pkg{% +% \ifnextcharTF'% +% {\@firstoftwo{\def\pkgsw{00}\@pkg}} +% {\def\pkgsw{01}\@pkg}% +% } +% \def\@pkg#1{% +% \def\do##1{\textcolor{ForestGreen}{\texttt{##1}}}% +% \cptprintcommalist{#1}\do +% \if\pkgsw\@space package\ifnum\maxindrisnr>\@ne s\fi\fi +% } +\robust@def*\cptprintcommalist{\cpt@testst\cpt@printcommalist} +\robust@def*\cpt@printcommalist#1#2{% + \cpt@stchoose{cpt@st}{#1}\cpt@tempa\cptprintcommalist + \def\print@do##1{% + \ifnum\indrisnr>\@ne + \iflastindris + \edef\maxindrisnr{\number\indrisnr}% + \@space and\@space + \else + ,\@space + \fi + \fi + #2{##1}% }% - \def\indris@do##1#1##2\indris@nil{% - \edef\currindris{\unexpanded\expandafter{\@gobble##1}}% - \letcsntocs{currindris@\cptrm\indrisdepth}\currindris - \ifcat$\detokenize\expandafter{\@gobble##2.}$\indris@nil - \lastindristrue\fi - \ifboolTF{lastindris}{% - \def\nextindris{}% - \csn@def{nextindris@\cptrm\indrisdepth}{}% + \indrisloop*\cpt@tempa\print@do +} +\robust@def*\indristloop#1#2{\cpt@testst\cpt@indristloop} +\robust@def*\cpt@indristloop#1#2{% + \pushfunctions\indristloop{\user@do,\indrisnr,\iflastindris, + \ifloopbreak}\cpt@csvdepth + \cpt@stchoose{cpt@st}{#1}\cpt@rsvda\indristloop + \advance\indrisdepth\@ne + \lastindrisfalse\loopbreakfalse + \chardef\indrisnr\z@pt + \cpt@setupuserdo{#2}% + \cpt@setupnextindris{}% + \let\cpt@incsv\cptswfalse + \def\cpt@indristl@op##1##2\indris@nil{% + \ifdefboolTF{loopbreak}{% + \cptdoremainder{##2}% }{% - \ifcsvbreakTF{##1}\loopbreak\relax - \ifboolTF{loopbreak}{% - \defpass\reserved@a####1\indris@nil{\cptdoremainder{####1}}% + \edef\currindris{\s@expandarg\cpttrimspace{\@gobble##1}}% + \letcsntocs{currindris@\cptrm\indrisdepth}\currindris + \edef\indrisnr{\the\numexpr\indrisnr+1}% + \getnextindris.##2\indris@nil + \ifcseqTF\nextindris\cpt@nnil{% + \lastindristrue + \def\nextindris{}% + \csn@def{nextindris@\cptrm\indrisdepth}{}% + \s@expandarg\user@do\currindris\relax }{% - \getnextindris.##2\indris@nil + \s@expandarg\user@do\currindris\relax + \cpt@indristl@op##2\indris@nil }% }% - \pushnumber\indrisnr - \expandafter\user@do\expandafter{\@gobble##1}% - \indris@do.##2\indris@nil }% - \expandafter\indris@do\expandafter.\cpt@rsvda#1\indris@nil - \popfunctions\cpt@indrisloop\cpt@csvdepth - \popnumber\indrisdepth + \expandafter\cpt@indristl@op\expandafter.\cpt@rsvda\cpt@nil\indris@nil + \popfunctions\indristloop\cpt@csvdepth + \advance\indrisdepth\m@one } +%% Robust, multi-level, general-purpose tsv processor: \robust@def*\tsv@@loop{% \let\tsv@inparse\cptswfalse \cpt@testst\cpt@tsvparse@a @@ -1773,6 +2025,9 @@ \new@def*\cpt@tsvnil{\cpt@tsvnil} \robust@def\cpt@tsvparse@a#1{% \cpt@stchoose{cpt@st}{#1}\cpt@tsvarg\cpt@tsvparse@a + % Use \cpt@csvdepth here also, to avoid premature undefining + % of pushed counterparts of \ifcpt@useempty, \ifloopbreak and + % \cptdoremainder by \popfunctions: \pushfunctions\tsv@@parse{\sav@tsv@do,\ifcpt@useempty, \ifloopbreak,\cptdoremainder,\tsv@inparse}\cpt@csvdepth \ifdefTF\tsv@do{}{% @@ -1787,12 +2042,13 @@ \fi } \robust@def*\cpt@tsvparse@b{% - \ifboolTF{loopbreak}{% - \defpass\reserved@a##1\cpt@tsvnil{\cptdoremainder{##1}}% + \ifdefboolTF{loopbreak}{% + \gobble@to@sentinel\cpt@tsvnil{}% }{% \begingroup \let\bgr@up\bgroup \let\bgroup\cpt@undefined + % Don't use \ifnextcharTF here: \cpt@ifnextchar\bgr@up\cpt@tsvparse@c\cpt@tsvparse@c }% } @@ -1822,27 +2078,98 @@ % \siso@@loop strips outer braces in its arguments and % doesn't trim leading and trailing spaces, but can % be nested to any level: -\new@def*\cpt@sisonil{\cpt@sisonil} -\robust@def*\siso@@loop#1{% +\newletcs\cpt@sisonil\relax +\new@def*\cpt@sisonnil{\cpt@sisonil} +\robust@def*\siso@@loop{\cpt@testst\cpt@sisoloop} +\robust@def*\cpt@sisoloop#1{% + \cpt@stchoose{cpt@st}{#1}\siso@arg\siso@@loop + % Use \cpt@csvdepth here also, to avoid premature un-defining + % of pushed counterparts of \ifloopbreak: \pushfunctions\siso@@loop{\sav@siso@do,\ifloopbreak}\cpt@csvdepth \ifdefTF\siso@do{}{\cpt@notdeferr\siso@do}% \let\sav@siso@do\siso@do \loopbreakfalse - \cpt@sisoloop#1\cpt@sisonil + \expandafter\cpt@sisoloop@a\siso@arg\cpt@sisonil \popfunctions\siso@@loop\cpt@csvdepth \ifnum\cpt@csvdepth=\z@pt\undefcs\siso@do\fi } -\robust@def*\cpt@sisoloop#1{% +\robust@def*\cpt@sisoloop@a#1{% \edef\siso@arg{\unexpanded{#1}}% - \ifcseqTF\siso@arg\cpt@sisonil{}{% - \ifboolTF{loopbreak}{% - \defpass\reserved@a##1\cpt@sisonil{% - \cptdoremainder{##1}% - }% + \ifcseqTF\siso@arg\cpt@sisonnil{}{% + \ifdefboolTF{loopbreak}{% + \gobble@to@sentinel\cpt@sisonil{}% + }{% + \sav@siso@do{#1}\relax\cpt@sisoloop@a + }% + }% +} +\robust@def*\cptdosisolist{\cpt@testst\cpt@dosisolist} +\robust@def*\cpt@dosisolist#1#2{% + \def\siso@do##1{#2}% + \cptexpanded{\siso@@loop\ifcpt@st*\fi}{#1}% +} +% Any type of argument and delimiter is allowed in \cptforeach. +% Eg, +% \newcount\nr +% \cptforeach[;] \xa/\ya/\za \in a1/b1/c1; a2/b2/c2; a3/b3/c3 \do{% +% \nr\z@pt +% It is permissible to use #1, #2, #3 to refer to the elements +% of each list, but they won't be despaced as \x, \y, \z will be: +% \cptforeach[,] \xs|\ys|\zs \in s1|t1|u1, s2|t2|u2, s3|t3|u3 \do{% +% \advance\nr\@ne +% \ifnum\nr>\@ne\loopbreak\fi +% \edef\cmda####1{#1#2#3*####1*##1##2##3}% +% \edef\cmdb####1{\xa\ya\za*####1*\xs\ys\zs}% +% }% +% } +% Control sequences can't be used as delimiters in #2. +% Star (*) suffix implies that is a command. +\robust@def*\cptforeach{\cpt@teststopt\cpt@foreach,} +\robust@def*\cpt@foreach[#1]#2\in#3\do#4{% + \let\ifforeach@st\ifcpt@st + \def\cpt@tempa{}% + \cpttfor#2\dofor{% + \ifescapedTF{##1}{\cptuaddtolist\cpt@tempa{##1}}{}% + }% + \cptexpandargonce{\pushfunctions\cptforeach}\cpt@tempa\cpt@csvdepth + \begingroup + % We need a special scanner here: + \def\rescan##1{% + \begingroup + \endlinechar\m@one\newlinechar\m@one + \everyeof{\@@nnil}% + \def\reserved@a####1\@@nnil{\endgroup\edef##1{\unexpanded{####1}}}% + \expandafter\reserved@a\scantokens\expandafter{##1}% + }% + \@tempcnta\z@pt + \def\cmds{}\def\params{}% + \cpttfor#2\dofor{% + \ifescapedTF{##1}{% + \advance\@tempcnta\@ne + \edef\cmds{\expandcsonce\cmds\edef\unexpanded{##1}% + {\noexpand\cpttrimspace{\cpt@hashchar\number\@tempcnta}}}% + \edef\params{\expandcsonce\params\cpt@hashchar\number\@tempcnta}% }{% - \sav@siso@do{#1}\relax\cpt@sisoloop + \ifnum\@tempcnta=\z@pt + \cpt@err{First item of \noexpand\cptforeach can't + \MsgBrk be a delimiter}\@ehc + \fi + \edef\params{\expandcsonce\params\unexpanded{##1}}% }% }% + \rescan\cmds\rescan\params + \cptexpanded{% + \endgroup + \def\noexpandcsn{foreach@do@\romannumeral\cpt@csvdepth}% + \expandcsonce\params\noexpand\foreach@nil + {\expandcsonce\cmds\unexpanded{#4}}% + \def\noexpand\csv@do####1{\noexpandcsn + {foreach@do@\romannumeral\cpt@csvdepth}####1\noexpand\foreach@nil}% + \csv@@parse\ifforeach@st*\fi + }% + [#1]{#3}% + \popfunctions\cptforeach\cpt@csvdepth + \undefcs\ifforeach@st } \robust@def*\cpt@oncurrlevel#1#2{% \ifdefTF\curr@level{% @@ -1856,6 +2183,8 @@ \cptexpandsecond{#1} {\noexpandcsn{\cptgobblescape#2@\romannumeral\curr@level}}% } +% #1: parser +% #2: token list \new@def*\csliststack#1#2{% \iflacus#2\dolacus\else \expandafter\ifnullTF\expandafter{\iiofii#2{}}{% @@ -1895,7 +2224,7 @@ } \robust@def*\cpt@emptifycsset#1{% \cptfor#1\dofor{% - \ifsingletoken@sp@TF{##1}{% + \ifsingletokenTF{##1}{% \ifescapedTF{##1}{% \ifcpt@cl\global\fi\def##1{}% }{% @@ -1913,7 +2242,7 @@ \AtEndOfPackage{% \AfterStartOfDocument{% \def\do#1{% - \ifcsnameTF{\cptgobblescape#1}{% + \ifdefTF#1{% \gdef#1{% \@latex@error{'\string#1' is a preamble command} {Move command '\string#1' to document preamble.}% @@ -1966,6 +2295,8 @@ \expandafter\csv@@loop\reserved@a[#1]{#2}% \postgroupdef\cptelemnr\endgroup } +% Eg, \ifelemcountTF[;](>)[2]{a;b;c}{T}{F}; +% * -> #4 is a macro \robust@def*\ifelemcountTF{\cpt@teststopt\cpt@ifelemcount@a,} \robust@def*\cpt@ifelemcount@a[#1]{% \cpt@testpnopt{\cpt@ifelemcount@b{#1}}=% @@ -2014,7 +2345,9 @@ \new@def*\ifcsnullFT#1{\ifcsnullTF{#1}\iiofii\iofii} \new@def*\oifcsnullTF#1{\expandafter\ifcsnullTF\expandafter{#1}} \new@def*\oifcsnullFT#1{\expandafter\ifcsnullFT\expandafter{#1}} -\new@def\ifcsemptyTF#1{\ifcondTF\ifx#1\@empty\cpt@quark\fi} +\new@def\ifcsemptyTF#1{% + \expandafter\ifcseqTF\cpt@car#1\@undefined\car@nil\@empty +} \new@def*\ifcsnnullTF#1{% \ifblankTF{#1}{% \@secondoftwo @@ -2129,29 +2462,31 @@ }% \fi \endgroup - \interactionmode\thr@@ } \SetStyleFileMessages[cpt@]{info}{warn}{err} -\new@def*\ifdefboolTF#1{% +\new@def*\ifboolTF#1{% \ifblankTF{#1}{% \cpt@err{Empty boolean}\@ehc }{% \ifcsndefTF{if#1}{% - \ifboolTF{#1}% + \ifdefboolTF{#1}% }{% \cpt@err{Boolean '\@backslashchar#1' undefined}\@ehc }% }% } -\new@def*\ifdefboolFT#1{\ifdefboolTF{#1}\iiofii\iofii} -\new@def*\ifboolTF#1{% +\new@def*\ifdefboolFT#1{\ifboolTF{#1}\iiofii\iofii} +\new@def*\ifdefboolTF#1{% \usename{@\usename{if#1}first\else second\fi oftwo}% } -\new@def*\ifboolFT#1{\ifboolTF{#1}\iiofii\iofii} +\new@def*\ifboolFT#1{\ifdefboolTF{#1}\iiofii\iofii} +\new@def*\ifgroupboolTF#1{% + \ifboolTF{#1}{\endgroup\iofii}{\endgroup\iiofii}% +} \new@def*\ifbooleanTF#1#{% \ifsingletokenTF{#1}{% \ifescapedTF{#1}{% - \ifdefboolTF{\cptremovescape#1}\iofii\iiofii + \ifboolTF{\cptremovescape#1}\iofii\iiofii }{% \cpt@notescapederr }% @@ -2166,7 +2501,7 @@ #1\relax\expandafter\cpt@iterate\fi\@gobble{#1}% } \new@def\cptwhileexpr#1\do#2{% - #1\cptswap{#2\cptwhileexpr{#1}\do{#2}}\fi + #1 \cptswap{#2\cptwhileexpr{#1}\do{#2}}\fi } \new@def*\cpt@numlt#1<{#1<\numexpr} \new@def*\cpt@numgt#1>{#1>\numexpr} @@ -2174,11 +2509,13 @@ \new@def*\cpt@dimlt#1<{#1<\dimexpr} \new@def*\cpt@dimgt#1>{#1>\dimexpr} \new@def*\cpt@dimeq#1={#1=\dimexpr} +% Don't put \numexpr in the arg #2 when calling \cptwhilenum: \new@def*\cptwhilenum#1\do#2{% \ifnum\numexpr\cpt@numlt\cpt@numgt\cpt@numeq#1% \cpt@removetonnil<=>\cpt@nnil\relax \cptswap{#2\cptwhilenum{#1}\do{#2}}\fi } +% Don't put \dimexpr in the arg #2 when calling \cptwhiledim: \new@def*\cptwhiledim#1\do#2{% \ifdim\dimexpr\cpt@dimlt\cpt@dimgt\cpt@dimeq#1% \cpt@removetonnil<=>\cpt@nnil\relax @@ -2194,6 +2531,8 @@ \cpt@removetonnil<=>\cpt@nnil first\else second\fi oftwo}% } \new@def*\ifdimcmpFT#1#{\usename{@\ifdimcmpTF#1{second}{first}oftwo}} +% Define control sequences to check for a token (eg, dash): +% \cpt@defcheck\cpt@nil \robust@def*\cpt@defcheck#1#2\cpt@nil{% \new@def*#1##1\cpt@nil{% \begingroup @@ -2204,6 +2543,9 @@ }% } \cpt@defcheck\cpt@checkeq=\cpt@nil +%%-- +% Define control sequences to split tokens (eg, at a dash): +% \cpt@defsplit@at\cpt@nil \robust@def*\cpt@defsplit@at#1#2\cpt@nil{% \def#1##1\cpt@nil{% \begingroup @@ -2218,6 +2560,9 @@ }% } \cpt@defsplit@at\cpt@split@ateq=\cpt@nil + +% Define a control sequence to check for the presence +% of a suffix in/for another control sequence: \robust@def\DefSuffixChecker#1#2#3#4{% \long\gdef#1##1##2##3##4{% \begintoksgroup @@ -2441,11 +2786,12 @@ \robust@def\oifstrcmpFT#1#2{% \oifstrcmpTF{#1}{#2}\iiofii\iofii } +% Expandable: \new@def\ifstreqTF#1#2{% - \expandafter\cptswapbracefirst\expandafter - {\romannumeral-`\q\cpttrimspaces{#1}} + \expandafter\cptswap\expandafter + {\expandafter{\romannumeral-`\q\cpttrimspaces{#1}}} {\expandafter\ifstrcmpTF\expandafter - {\romannumeral-`\q\cpttrimspaces{#2}}} + {\romannumeral-`\q\cpttrimspaces{#2}}}% } \robust@def\ifstreqFT#1#2{\ifstreqTF{#1}{#2}\iiofii\iofii} \robust@def\oifstreqTF#1#2{\cptexpandtwoargsonce\ifstreqTF{#1}{#2}} @@ -2470,6 +2816,7 @@ } \new@def\ifdefeqFT#1#2{\ifdefeqTF{#1}{#2}\iiofii\iofii} \new@def\ifcateqTF#1#{\usename{@\ifcat#1first\else second\fi oftwo}} +% Expand and detokenize before comparison: \new@def\ifcharcmpTF#1#2{% \ifsingletokenTF{#1}{% \ifsingletokenTF{#2}{% @@ -2503,17 +2850,22 @@ } \new@def\ifsingletokenFT#1{\ifsingletokenTF{#1}\iiofii\iofii} % Don't test for \ifleadspaceTF: -\new@def\ifsingletoken@sp@TF#1#2#3{% - \romannumeral\ifblankTF{#1}{% - \@secondoftwo +\new@def\ifsingleindrisTF#1{% + \romannumeral + \ifblankTF{#1}{% + 0 \iiofii }{% - \ifbracedTF{#1}{% - \@secondoftwo - }{% - \expandafter\ifblankTF\expandafter{\@gobble#1}% - \iofii\iiofii - }% - }{0 #2}{0 #3}% + % Eat one token and take the rest to \cpt@ifsingleindris: + \expandafter + \cpt@ifsingleindris\cpt@cdr#1\cdr@nil\single@nil{#1}% + }% +} +\new@def\cpt@ifsingleindris#1\single@nil#2{% + \ifblankTF{#1}{% + \xifstrcmpTF{\expandafter\cpt@car + \detokenize{#2}x\car@nil}\cpt@leftbracechar + {0 \iiofii}{0 \iofii}% + }{0 \iiofii}% } \robust@def*\ifsingledigitTF#1{% \begingroup @@ -2535,7 +2887,7 @@ }{% \ifsingletokenTF{#1}{% \xifinsetTF{\detokenize{#1}}\cpt@parserlist{% - #2% + #2% Don't put \relax here! }{% \cpt@listparsererr{#1}% }% @@ -2548,7 +2900,8 @@ \ifblankTF{#1}{% \cpt@listparsererr{#1}% }{% - \expandafter\ifsecondisnnilTF\string#1\cpt@nnil\cpt@nil{% + % if #1 is a single token: + \ifsecondisnnilTF{#1}{% \xifinsetTF{\detokenize{#1}}\cpt@parserlist{}{% \cpt@listparsererr{#1}% }% @@ -2566,6 +2919,8 @@ \robust@def*\postgroupcsndef#1\endgroup{% \aftercsname\postgroupdef{#1}\endgroup } +% #1: macro; +% #2: arbitrary token (eg, \endgroup): \robust@def*\cptpassacross#1#2{% \cptpassexpanded{\unexpanded{#2}\cpt@csexit#1}% } @@ -2604,6 +2959,7 @@ \edef#2{\unexpanded{#1}}% }% } +% Expandable: \new@def*\cptexpandcs#1{% \ifsingletokenTF{#1}{% \ifmacroTF{#1}{% @@ -2619,6 +2975,8 @@ \unexpanded{#1}% }% } +% Expandable full expand; strips braces and leading +% spaces, but not trailing spaces: \begingroup \catcode`\&=7 \new@gdef*\cptfullexpand#1{% @@ -2642,6 +3000,9 @@ \ifblankTF{#2}{0 #3#1}{\cpt@fullexpand{#2}{#3#1}}% } \endgroup + +% []{}{<{str1}{act1}{str2}{act2}...>} +% Expandable: \new@def*\cptifstreqcase#1{\cpt@xtestopt{#1}\cpt@ifstreqcase{}} \new@def\cpt@ifstreqcase[#1]#2#3{% \expandafter\cptswapbracefirst\expandafter{#2}% @@ -2654,6 +3015,10 @@ }% }% } +% #1: test string (choice) +% #2: nominations and callbacks, eg, {{a}1,{b}2,{ x }3} +% #3: no-match fallback +% Expandable: \new@def\cptcheckchoice#1#2#3{% \expandafter\cptswapbracefirst\expandafter {\romannumeral-`\q\cpttrimspace{#1}} @@ -2668,6 +3033,8 @@ }% }% } +% Test if , etc, is in +% []{}{<{str1}{act1}{str2}{act2}...>} \new@def*\cptifstrpresentcase#1{\cpt@xtestopt{#1}\cpt@ifstrincase{}} \new@def\cpt@ifstrincase[#1]#2#3{% \expandafter\cptswapbracefirst\expandafter{#2}% @@ -2680,15 +3047,41 @@ }% }% } -\new@def\all@to@endif#1\endif{#1} -\new@def\first@to@endif#1#2\endif{#1} -\new@def*\ifcassenumcmpTF#1#2#3{\ifdimcmpTF#1pt#2pt{#3}} +\new@def\do@else@part\do#1#2\endif{\unexpanded{#1}} +\new@def\all@to@endif#1\endif{\unexpanded{#1}} +\new@def\first@to@endif#1#2\endif{\unexpanded{#1}} +\new@def\gobble@to@endif#1\endif{} +\new@def*\ifcassedimcmpTF#1#2#3{\ifdimcmpTF#1#2{#3}} +\new@def*\ifcassenumcmpTF#1#2#3{\ifnumcmpTF#1#2{#3}} +% Eg, +% \edef\x{% +% \cptifcasse\ifstrcmpTF{teststr} +% {str1}{case1 callback} +% {str2}{case2 callback} +% \ifnone +% no match +% \endif +% } +% \edef\x{% +% \cptifcasse\ifcassenumcmpTF{testnum} +% {=2}{equal to 2} +% {<3}{less than 3} +% {>4}{greater than 4} +% \ifnone +% no match +% \endif +% } +% The comparator command is any test command that yields +% or but \ifnumcmpTF can't be used directly. +% Use \ifcassenumcmpTF instead. The user can also define his own +% logical test command and pass it to \cptifcasse, eg, +% \def\test#1#2{\ifinsetTF{#2}{#1}} \new@def*\cptifcasse#1#2{% - \ifstrcmpTF{#1}\elseif{% + \ifstrcmpTF{#1}\ifnone{% \all@to@endif }{% \ifstrcmpTF{#1}\endif{}{% - \ifstrcmpTF{#2}\elseif{% + \ifstrcmpTF{#2}\ifnone{% \all@to@endif }{% \ifstrcmpTF{#2}\endif{}{\cpt@ifcasse{#1}{#2}}% @@ -2697,7 +3090,7 @@ }% } \new@def*\cpt@ifcasse#1#2#3{% - \ifstrcmpTF{#3}\elseif{% + \ifstrcmpTF{#3}\ifnone{% \all@to@endif }{% \ifstrcmpTF{#3}\endif{}{% @@ -2706,12 +3099,89 @@ }% } \new@def*\cpt@ifcasse@i#1#2#3{\cpt@ifcasse{#1}{#2}} + +% Eg, +% \edef\x{% +% \cptdocasse\ifcassedimcmpTF{1pt+2pt+3pt} +% {=2pt}\do{equal to 2pt} +% \elseif +% {<3pt}\do{less than 3pt} +% \elseif +% \do{no match} +% \endif +% } +% \edef\x{% +% \cptdocasse\ifcassenumcmpTF{1+2+3} +% {=2}\do{equal to 2} +% \elseif +% {<3}\do{less than 3} +% \elseif +% \do{no match} +% \endif +% } +% \edef\x{% +% \cptdocasse\ifcassenumcmpTF{5} +% {=2}\do{equal to 2} +% \elseif +% {<3}\do{less than 3} +% \elseif +% {>4}\do{greater than 4} +% \elseif +% \do{no match} +% \endif +% } +% \edef\x{% +% \cptdocasse\ifstrcmpTF{x} +% a\do{\def\y{a}} +% \elseif +% b\do{\def\y{b}} +% \elseif +% \endif +% } +\new@def*\cptdocasse#1#2{% + \ifstrcmpTF{#1}\elseif{% + \gobble@to@endif + }{% + \ifstrcmpTF{#1}\endif{}{% + \ifstrcmpTF{#2}\elseif{% + \gobble@to@endif + }{% + \ifstrcmpTF{#2}\endif{}{\cptdocasse@i{#1}{#2}}% + }% + }% + }% +} +\new@def*\cptdocasse@i#1#2#3{% + \ifstrcmpTF{#3}\elseif{% + \cptdocasse@i{#1}{#2}% + }{% + \ifstrcmpTF{#3}\endif{}{% + \ifstrcmpTF{#3}\do{% + \do@else@part\do + }{% + \cptdocasse@ii{#1}{#2}{#3}% + }% + }% + }% +} +\new@def*\cptdocasse@ii#1#2#3\do#4{% + \ifstrcmpTF{#4}\elseif{% + \cptdocasse@ii{#1}{#2}% + }{% + \ifstrcmpTF{#4}\endif{}{% + #1{#2}{#3}{% + \unexpanded{#4}% + \gobble@to@endif + }{% + \cptdocasse@i{#1}{#2}% + }% + }% + }% +} + \robust@def\cpt@in#1#2{% \long\def\in@@##1#1##2\in@@{% - \begingroup - \toks@{##2}% - \edef\in@@{\the\toks@}% - \expandafter\endgroup + \edef\in@@{\unexpanded{##2}}% \ifx\in@@\@empty\cpt@infalse\else\cpt@intrue\fi }% \in@@#2{\in@@}#1\in@@ @@ -2754,22 +3224,32 @@ \cpt@notonetokenerr{#2}% }% } -\robust@def*\cptaddtolist{\cpt@teststopt{\cpt@addtolist\expandcsonce{e}},} -\robust@def*\cptgaddtolist{\cpt@teststopt{\cpt@addtolist\expandcsonce{x}},} +% \cptaddtolist[]<\cs>{} +% * -> use \protected@(e)xdef. +% uadd -> don't expand at all. +% oadd -> expand once. +% xadd -> fully expand . +% gadd -> add globally. +\robust@def*\cptuaddtolist{\cpt@teststopt{\cpt@addtolist\unexpanded{e}},} +\robust@def*\cptguaddtolist{\cpt@teststopt{\cpt@addtolist\unexpanded{x}},} +\robust@def*\cptoaddtolist{\cpt@teststopt{\cpt@addtolist\expandcsonce{e}},} +\robust@def*\cptgoaddtolist{\cpt@teststopt{\cpt@addtolist\expandcsonce{x}},} \robust@def*\cptxaddtolist{\cpt@teststopt{\cpt@addtolist\@firstofone{e}},} \robust@def*\cptgxaddtolist{\cpt@teststopt{\cpt@addtolist\@firstofone{x}},} +\newletcs\cptaddtolist\cptuaddtolist +\newletcs\cptgaddtolist\cptguaddtolist \robust@def\cpt@addtolist#1#2[#3]#4#5{% \ifcpt@check\cpt@checklistparser{#3}\relax\fi \begingroup \toks@{\endgroup \ifdefTF#4{% - \ifboolTF{cpt@st}{% + \ifdefboolTF{cpt@st}{% \usename{protected@#2def}#4{\csliststack#3#4#1{#5}}% }{% \usename{#2def}#4{\csliststack#3#4#1{#5}}% }% }{% - \ifboolTF{cpt@st}{% + \ifdefboolTF{cpt@st}{% \usename{protected@#2def}#4{#1{#5}}% }{% \usename{#2def}#4{#1{#5}}% @@ -2782,6 +3262,25 @@ \the\toks@ }% } +\robust@def*\cptapptomacro{\cpt@testst{\cpt@hooktomacro>}} +\robust@def*\cptpreptomacro{\cpt@testst{\cpt@hooktomacro<}} +\robust@def\cpt@hooktomacro#1#2#3{% + \cpt@getmacroprefix{#2}% + \ifcpt@st\let\reserved@a\expandcsonce\else\let\reserved@a\unexpanded\fi + \ifdefTF{#2}{% + \ifx#1>% + \macroprefix\edef#2{\expandcsonce{#2}\reserved@a{#3}}% + \else + \macroprefix\edef#2{\reserved@a{#3}\expandcsonce{#2}}% + \fi + }{% + \macroprefix\edef#2{\reserved@a{#3}}% + }% +} +% #1 : optional list parser +% #2 : control sequence of main list +% #3 : sublist to remove (not cs) +% #4 : filter/wrapper \robust@def*\cptfilterremovecsv{% \begingroup \let\cpt@inkv\cptswfalse @@ -2812,8 +3311,9 @@ \expandafter\def\temp@do##1{% \edef\cpt@tempa{\unexpanded{##1}}% \cpt@usefilter\cpt@tempa\cpt@tempb + % no \oifinset@sp@TF here (see detokenization above): \expandafter\xifinsetTF\cpt@tempd\cpt@tempb{}{% - \cptaddtolist[#1]\cpt@tempc{##1}% + \cptuaddtolist[#1]\cpt@tempc{##1}% }% }% \ifcsnullTF#2{}{% @@ -2835,6 +3335,10 @@ }% }% } +% #1 : parser +% #2 : old list (a control sequence) of parser-separated list +% #3 : new update (a non-command list) +% #4 : element-wise filter/wrapper \newletcs\everymergeitem=\@iden \robust@def*\cptfiltermergecsv{% \begingroup @@ -2867,8 +3371,8 @@ \expandafter\def\temp@do####1{% \edef\cpt@merge@c{\unexpanded{####1}}% \cpt@usefilter\cpt@merge@c\cpt@merge@d - \cptscantokens\cpt@merge@b - \cptscantokens\cpt@merge@d + \cptscancs\cpt@merge@b + \cptscancs\cpt@merge@d \ifx\cpt@merge@b\cpt@merge@d \cpt@choicefdtrue\cpt@add\cpt@merge@a \else @@ -2876,7 +3380,7 @@ \fi }% \cptexpandargonce{\def#2{}\temp@parse[#1]}#2% - \ifboolTF{cpt@choicefd}{}{\cpt@add\cpt@merge@a}% + \ifdefboolTF{cpt@choicefd}{}{\cpt@add\cpt@merge@a}% }% \ifcsnullTF#2{% \edef#2{\expandcsonce\cpt@update}% @@ -2887,8 +3391,16 @@ }% \postgroupdef#2\endgroup \let\everymergeitem\@firstofone + % No need to undefine \temp@parse, \temp@do, etc: + % they have been defined in a local group. } \cptnewvariables{switch}[cpt@]{fromrepl,fromrem}[false] + +% See also \cptfilterremovecsv: +% #1 : optional list parser +% #2 : listcmd +% #3 : single element to remove +% * -> remove all occurrences of #3 \robust@def*\cptremoveoneelement{\cpt@teststopt\cpt@removeoneelement,} \robust@def\cpt@removeoneelement[#1]#2#3{% \begingroup @@ -2901,12 +3413,15 @@ }% \xifinsetTF{\detokenize{#1##1#1}}{\pser\cptoxdetok{#2}\pser}{% \expandafter\cpt@remove@b\expandafter#1#2#1\remove@nil - \ifboolTF{removest}{% + % If removing all occurrences: + \ifdefboolTF{removest}{% \expandafter\cpt@remove@a\expandafter{\cpt@remove@c}% }{}% }{}% }% \ifdefswitchTF{cpt@fromrem}{% + % We're coming from \cptremoveelements, #3 has been + % trimmed and #2 has already been normalized: \edef\cpt@remove@c{\unexpanded{#3}}% }{% \cpt@sttrue\cpt@csvnormalize[#1]#2% @@ -2922,6 +3437,9 @@ }% \postgroupdef#2\endgroup } +% #1 : optional list parser +% #2 : control sequence of list +% #3 : sublist to remove \robust@def*\cptremoveelements{\cpt@teststopt\cpt@removeelements,} \robust@def\cpt@removeelements[#1]#2#3{% \begingroup @@ -2934,6 +3452,12 @@ \csv@@parse[#1]{#3}% \postgroupdef#2\endgroup } +% \cptreplaceoneelement[]{}{}{} +% #1: optional list parser +% #2: control sequence of list +% #3: single item to replace +% #4: item to substitute +% * -> replace all occurrences \robust@def*\cptreplaceoneelement{\cpt@teststopt\cpt@replaceoneelement,} \robust@def\cpt@replaceoneelement[#1]#2#3#4{% \begingroup @@ -2946,12 +3470,15 @@ }% \xifinsetTF{\detokenize{#1##1#1}}{\pser\cptoxdetok#2\pser}{% \expandafter\cpt@replace@b\expandafter#1#2#1\rep@nil - \ifboolTF{replst}{% + % If replacing all occurrences of #3 in #2: + \ifdefboolTF{replst}{% \expandafter\cpt@replace@a\expandafter{\cpt@replace@c}% }{}% }{}% }% \ifdefswitchTF{cpt@fromrepl}{% + % We're coming from \cptreplaceelements, #3 has been + % trimmed and #2 has already been normalized: \edef\cpt@replace@c{\unexpanded{#3}}% }{% \cpt@sttrue\cpt@csvnormalize[#1]#2% @@ -2969,6 +3496,11 @@ }% \postgroupdef#2\endgroup } +% #1: optional list parser +% #2: listcmd +% #3: items to replace +% #4: items to substitute +% * -> replace all occurrences \robust@def*\cptreplaceelements{\cpt@teststopt\cpt@replaceelements,} \robust@def\cpt@replaceelements[#1]#2#3#4{% \begingroup @@ -3064,7 +3596,7 @@ \robust@def*\cpt@undefcsset#1{% \cpt@stchoose{cpt@st}{#1}\cpt@rsvda\undefcsset \expandafter\cptfor\cpt@rsvda\dofor{% - \ifsingletoken@sp@TF{##1}{% + \ifsingletokenTF{##1}{% \ifescapedTF{##1}{% \ifcpt@cl\global\fi\undefcs##1% }{% @@ -3075,6 +3607,8 @@ }% }% } +% #1: parser, #2: listcmd +% This is one way to preserve braces in #2: \robust@def*\cpttrimleadparsers#1#2{% \ifblankTF{#1}{% \cpt@err{Empty parser in \string\cpttrimleadparsers}\@ehc @@ -3096,6 +3630,7 @@ \expandafter\reserved@e\expandafter.#2#1\noboundary \postgroupdef#2\endgroup\cpt@relax } +% #1: parser, #2: list command \robust@def\cpttrimtrailparsers#1#2{% \begingroup \def\cpt@trima{% @@ -3120,6 +3655,7 @@ \cpt@notonetokenerr{#1}% }% } +% #1: token on command, #2: test token: \robust@def\cpt@stripsuffix#1#2{% \ifblankTF{#2}{% \cpt@err{Blank test token for \string\cpt@stripsuffix}\@ehc @@ -3129,6 +3665,7 @@ }% }% } +% Strip #1 number of outer braces in #2; return result in #3: \robust@def*\cptstripNouterbraces{\cpt@testopt\cpt@stripouterbraces\@ne} \newletcs\cptstripnbr\cptstripNouterbraces \robust@def*\cpt@stripouterbraces[#1]{% @@ -3200,6 +3737,7 @@ \repeat \postgroupdef#3\endgroup } +% Expandable: \edef\ifmacroTF#1{% \noexpand\ifblankTF{#1}{% \noexpand\@secondoftwo @@ -3229,6 +3767,10 @@ {@\ifx\x\@backslashchar first\else second\fi oftwo}% } \robust@def*\ifescapedFT#1{\ifescapedTF{#1}\iiofii\iofii} +% \robustaliascount counts the number of 'robustified' commands. +% It may be used to set a threshold for the no. of allowable +% aliases. \cptrobustify avoids \scantokens, because some commands +% aren't scannable: \cptchardef\maxrobustalias50 \robust@def*\cptrobustify#1{% \def\csv@do##1{% @@ -3300,13 +3842,45 @@ \catcode`\{\@ne \catcode`\}\tw@ } -\robust@def*\cptscantokens#1{% +% #1 must be a non-parametered macro: +\robust@def*\cptscancs#1{% \begingroup \edef#1{\long\def\string#1{\cptoxdetok{#1}}}% \cpteveryscan \scantokens\expandafter{#1}\relax \postgroupdef#1\endgroup } +\newletcs\cptscantokens\cptscancs +\begingroup + \lccode`\A =`\@ % + \lccode`\B =`\@ % + \catcode`\A = 8 % + \catcode`\B = 3 % +\lowercase{\endgroup\def\cpt@rescan@marker{AB}} +\robust@def*\cptrescan{\cpt@rescan@a\def} +\robust@def*\cptgrescan{\cpt@rescan@a\gdef} +% #1 = \def or \gdef +% #2 = command to receive the re-scanned stuff +% #3 = arbitrary code to execute before \scantokens +% #4 = stuff to rescan +\robust@def*\cpt@rescan@a#1#2#3#4{% + \begingroup + \everyeof\expandafter{\cpt@rescan@marker\noexpand}% + \endlinechar\m@one\newlinechar\m@one + #3% + \cptexpanded{\endgroup + #1\noexpand#2{% + \expandafter\cpt@rescan@b\expandafter\@empty + \scantokens\expandafter{#4}% + }% + }% +} +\edefpass\reserved@a{% + % The \expandafter here is to get rid of \@empty: + \def\noexpand\cpt@rescan@b##1\cpt@rescan@marker{% + \noexpand\unexpanded\noexpand\expandafter{##1}% + }% +} \robust@def*\cpt@lowercasecs#1{% \ifmacroTF{#1}{% \cptexpandarg\lowercase{\def\noexpand#1{#1}}% @@ -3432,6 +4006,26 @@ }% } \reserved@a{ } +% Convert bool value (true or false) to switch value (00 or 01): +\robust@def*\booltoswitch#1#2{% + \begingroup + \def\reserved@a##1#1##2##3\@nil{% + \def#2{##2}% + \ifx#2\@nnil + \cpt@err{Bad boolean value '#1' can't be + \MsgBrk converted to switch value}\@ehc + \fi + }% + \reserved@a true{00}false{01}#1{\@nil}\@nil + \postgroupdef#2\endgroup +} +% Expandable: +\new@def*\bbooltoswitch#1{% + \expandafter\ifstrcmpTF\expandafter + {\romannumeral-`\q\expandafter\cpttrimspace + \expandafter{\romannumeral-`\q#1}}{true}{00}{01}% +} + \robust@def*\newswitch#1{\cpt@testopt{\cpt@newswitch#1}{false}} \robust@def*\cpt@newswitch#1[#2]{% \ifinsetTF{,}{#1}{% @@ -3457,7 +4051,7 @@ \robust@def*\cpt@noswitcherr#1{% \cpt@err{Switch '\string#1' is undefined}\@ehc } -\robust@def*\setswitch#1#2{% +\robust@def*\cptsetswitch#1#2{% \ifcsndefTF{#1}{% \cpt@getswitchstate{#2}% \letcsntocsn{#1}{cptsw\cpt@switchstate}% @@ -3465,36 +4059,38 @@ \cpt@noswitcherr{#1}% }% } -\robust@def*\setswitchtrue#1{% +\robust@def*\cptsetswitchtrue#1{% \ifcsndefTF{#1}{% \letcsntocs{#1}\cptswtrue }{% \cpt@noswitcherr{#1}% }% } -\newletcs\switchtrue\setswitchtrue -\robust@def*\setswitchfalse#1{% +\newletcs\cptswitchtrue\cptsetswitchtrue +\robust@def*\cptsetswitchfalse#1{% \ifcsndefTF{#1}{% \letcsntocs{#1}\cptswfalse }{% \cpt@noswitcherr{#1}% }% } -\newletcs\switchfalse\setswitchfalse +\newletcs\cptswitchfalse\cptsetswitchfalse \new@def*\ifdefswitchTF#1{% \ifcsndefFT{#1}{% \cpt@err{Switch '#1' is not defined}\@ehc \@gobbletwo }{% - \ifcondTF\if\usename{#1}\fi + \if\usename{#1}\expandafter\iofii\else\expandafter\iiofii\fi }% } \new@def*\ifdefswitchFT#1{\ifdefswitchTF{#1}\iiofii\iofii} -\new@def*\ifswitchTF#1{\ifcondTF\if\usename{#1}\fi} +\new@def*\ifswitchTF#1{% + \if\usename{#1}\expandafter\iofii\else\expandafter\iiofii\fi +} \new@def*\ifswitchFT#1{\ifswitchTF{#1}\iiofii\iofii} -\new@def*\thebool@normal#1{\ifdefboolTF{#1}{true}{false}} -\new@def*\thebool@reverse#1{\ifdefboolTF{#1}{false}{true}} -\cptnewcounts{cpt@exprcnt} +\new@def*\thebool@normal#1{\ifboolTF{#1}{true}{false}} +\new@def*\thebool@reverse#1{\ifboolTF{#1}{false}{true}} +\cptnewcounts[cpt@]{exprcnt} \robust@def*\ifexprTF#1{% \begingroup \let\cpt@expr@neg\cptswfalse @@ -3581,7 +4177,7 @@ \robust@def\cpt@expr@bool@a#1#2\expr@nil{% \edef\reserved@a{\cpttrimspaces{#1}}% \ifcsndefTF{if\reserved@a}{% - \ifboolTF\reserved@a\cpt@expr@true\cpt@expr@false + \ifdefboolTF\reserved@a\cpt@expr@true\cpt@expr@false }{% \cpt@err{Invalid test expression: \MsgBrk boolean '\reserved@a' is undefined}\@ehc @@ -3630,6 +4226,8 @@ \expandafter\endgroup\ifcpt@choicefd \expandafter\iofii\else\expandafter\iiofii\fi } +% #1: listcmd +% #2: comparator: < (ascending) or > (descending) % Eg, \def\x{13251{8/2}} \cptnumbersort\x> \robust@def*\cptnumbersort#1#2{% \begingroup @@ -3652,6 +4250,8 @@ {You need to first load 'fp' package before \MsgBrk calling \string\cptbignumbersort.}% }% + % If #1 contains expressions, evaluate them first before + % sorting the results: \cptexpandarg{\cptifonepresentTF{/*-+}}{\cptoxdetok{#1}}{% \def\siso@do##1{% \FPeval\resulta{clip(##1)}% @@ -3672,23 +4272,25 @@ } \robust@def*\cpt@sortlist#1#2{% \def\sortedlist{}% - \cpt@s@rtlist{#1}{#2}% + \cpt@sortlist@a{#1}{#2}% \edefpass#1{\endgroup\def\noexpand#1{\sortedlist}}% } \new@def*\cpt@sortwrapper#1{{#1}} -\robust@def*\cpt@s@rtlist#1#2{% +\robust@def*\cpt@sortlist@a#1#2{% + % Get the largest/smallest number and return: \def\do##1{% \def\smaller@bigger{}% \defpass\do####1{% \defifxTF{####1}\@nnil{% + % Store the largest/smallest number: \edef\sortedlist{\sortedlist\cpt@sortwrapper{##1}}% \ifcsemptyTF\smaller@bigger{}{% \let#1=\smaller@bigger - \cpt@s@rtlist#1#2% + \cpt@sortlist@a#1#2% }% }{% \numcmp####1#2##1{% - \cpt@s@rtl@st{#1}{#2}{{####1}{##1}}.% + \cpt@sortlist@b{#1}{#2}{{####1}{##1}}.% }{% \edef\smaller@bigger{\smaller@bigger{####1}}% \do @@ -3698,11 +4300,11 @@ }% \expandafter\do#1\@nil } -\robust@def*\cpt@s@rtl@st#1#2#3#4\@nil{% +\robust@def*\cpt@sortlist@b#1#2#3#4\@nil{% + % \@gobble is for gobbling the dot: \edef#1{#3\smaller@bigger\@gobble#4}% - \cpt@s@rtlist{#1}{#2}% + \cpt@sortlist@a{#1}{#2}% } - \robust@def*\BeforeProcessOptions{% \ifcsndefTF{\@currname @\@currext @beginprocessopt}{% \cpt@warn{\noexpand\BeforeProcessOptions wrongly located}% @@ -3773,7 +4375,7 @@ }% } % If package has been loaded with . -% \ifpackagewithoptionsTF{}{} +% \ifpackagewithoptionsTF{}{}{}{} \robust@def*\ifpackagewithoptionsTF{\cpt@ifoptions\@pkgextension} \robust@def*\ifclasswithoptionsTF{\cpt@ifoptions\@clsextension} \robust@def*\cpt@ifoptions#1#2{% @@ -3802,9 +4404,35 @@ \checkopts }% }% - \ifboolTF{cpt@choicefd} + \ifdefboolTF{cpt@choicefd} {\endgroup\@firstoftwo}{\endgroup\@secondoftwo}% } +% #1=package, #2=options +\robust@def*\cptcheckoptionclash#1#2{% + \begingroup + \edef\cpt@rsvda{\detokenize\expandafter + {\romannumeral-`\q\cpt@optionlist{#1.\@pkgextension}}}% + \@tempswafalse + \ifblankTF{#2}{% + \ifcsnullTF\cpt@rsvda{}\@tempswatrue + }{% + \ifcsnullTF\cpt@rsvda{% + \@tempswatrue + }{% + \def\kv@do####1{% + \xifinsetTF{,\detokenize{####1},}{,\cpt@rsvda,}{}{% + \@tempswatrue\loopbreak + }% + }% + \kv@@parse[,]{#2}% + }% + }% + \expandafter\endgroup + \if@tempswa + \cpt@err{Option clash for package ##1.\@pkgextension}\@ehc + \expandafter\@gobble + \fi +} % If package was later loaded (before begin-document). % \ifpackageloadedlaterTF{}{}{} \robust@def*\ifpackageloadedlaterTF{\cpt@ifpkgloadedlater\@pkgextension} @@ -3831,6 +4459,10 @@ }% }% } +% \cptloadpackages[]{% +% pkgaoptionsdate +% pkgboptionsdate +% } \robust@def*\cptloadpackages{% \begingroup \endlinechar=-1 @@ -3839,6 +4471,7 @@ \robust@def\cpt@loadpackages[#1]#2{% \begingroup \toks@{}% + % ##1: inner parser; ##2 outer parser: \def\cpt@tempa##1##2\cpt@nil{% \ifblankTF{##2}{% \cpt@err{No outer parser or something wrong @@ -3851,6 +4484,7 @@ \expandafter\@gobble }% }% + % \cpt@lo@dpackages can't be taken outside \cpt@loadpackages: \def\cpt@lo@dpackages####1##1####2##1####3##1####4\cpt@nil{% \ifblankTF{####1}{% \cpt@err{No package name while in \string\cptloadpackages}\@ehc @@ -3862,6 +4496,7 @@ \cptstripallouterbraces{####2}\cpt@rsvda }% \@ifpackageloaded{####1}{% + % Check if package was previously loaded with options: \edef\cpt@rsvdb{\detokenize\expandafter {\romannumeral-`\q\cpt@optionlist{####1.\@pkgextension}}}% \@tempswafalse @@ -3902,6 +4537,86 @@ \cpt@tempa#1\cpt@nil \expandafter\endgroup\the\toks@ } +% Eg, +% \cptrequirepackages{% +% pkga|options|date; pkgb|options|date; etc. +% } +\robust@def*\cptrequirepackages#1{% + \begintoksgroup + \let\RequirePackage\relax + \def\cpt@requirepackage##1|##2|##3|##4\@nil{% + \edef\cpt@tempa{\cpttrimspaces{##1}}% + \edef\cpt@tempb{\cpttrimspaces{##2}}% + \edef\cpt@tempc{\cpttrimspaces{##3}}% + \@ifpackageloaded{\cpt@tempa}{% + \xifstrcmpTF\cpt@tempc{^?^}{}{% + \@ifpackagelater\cpt@tempa\cpt@tempc{% + \cptexpandbracenext\cptstripallouterbraces\cpt@tempb\cpt@tempb + \cptexpandsecond\cptcheckoptionclash + {{\expandcsonce\cpt@tempa}{\expandcsonce\cpt@tempb}}% + }{% + \addto@hook\toks@{\@latex@error{Loaded version of + '\cpt@tempa' package is not current}\@ehc}% + }% + }% + }{% + \xifstrcmpTF\cpt@tempb{^?^}{% + \xifstrcmpTF\cpt@tempc{^?^}{% + \addto@hook\toks@{\RequirePackage{\cpt@tempa}}% + }{% + \addto@hook\toks@{\RequirePackage{\cpt@tempa}[\cpt@tempc]}% + }% + }{% + \xifstrcmpTF\cpt@tempc{^?^}{% + \addto@hook\toks@{\RequirePackage[\cpt@tempb]{\cpt@tempa}}% + }{% + \addto@hook\toks@{\RequirePackage[\cpt@tempb]{\cpt@tempa}[\cpt@tempc]}% + }% + }% + }% + }% + \def\csv@do##1{% + \cpt@requirepackage##1|^?^|^?^|\@nil + }% + \csv@@parse[;]{#1}% + \endtoksgroup +} +% #1=base, #2=modules to be load +\robust@def*\cptloadmodules#1#2{% + \begintoksgroup + \def\clash@list{}% + \def\do##1##2{% + \ifcpt@noval + \def\curr@optlist{}% + \else + \cptstripallouterbraces{##2}\curr@optlist + \fi + \@ifpackageloaded{##1}{% + % check option clash + \cptexpandarg{\cpt@ifoptions{sty}{##1}}{\expandcsonce\curr@optlist}{}{% + \ifx\curr@optlist\@empty + \cptuaddtolist\clash@list{##1}% + \else + \cptuaddtolist\clash@list{##1=##2}% + \fi + }% + }{% + \cptexpanded{\toks@{\the\toks@ + \noexpand\RequirePackage[\expandcsonce\curr@optlist]{% + \ifblankTF{#1}{}{#1-}##1% + }}% + }% + }% + }% + \kv@@process{#2}\do + \ifx\clash@list\@empty + \expandafter\endtoksgroup + \else + \cptexpanded{\endgroup + \cpt@err{Option clash for module(s): + \noexpand\unexpanded{\expandcsonce\clash@list}}}\@ehc + \fi +} \robust@def*\AfterEndOfStyleFile{% \begingroup \def\cpt@tempa{\endgroup @@ -3914,9 +4629,7 @@ \cpt@tempa }% } -\robust@def*\ifinstylefileTF{% - \ifcsemptyTF\@currname\iiofii\iofii -} +\robust@def*\ifinstylefileTF{\ifcsemptyTF\@currname\iiofii\iofii} \robust@def*\cpt@pushhash{% \begingroup \@makeother{\#}% @@ -3968,6 +4681,7 @@ \edef#2{\the\toks@}% \postgroupdef#2\endgroup } +% * -> #2 is a listcmd, else it is a list: \robust@def*\csv@@normalize{\cpt@teststopt\cpt@csvnormalize,} \robust@def\cpt@csvnormalize[#1]#2{% \let\cpt@inkv\cptswfalse @@ -3990,8 +4704,10 @@ \def\normalized@list{}% \cptbgobbletorelax \fi + % It is absolutely necessary to check parser here, since this + % normalizer doesn't take multiple parsers at once: \cpt@ch@cklistparser{#1}% - \expandafter\setup@parser@nmlz\string#1% + \expandafter\SetupParserNormalizer\string#1% \toks@\expandafter{\expandafter#1\reserved@e#1}% \nmlz@activeparser \expandafter\nmlz@spaceletter\string#1% @@ -4001,16 +4717,19 @@ \nmlz@activeequal \nmlz@spaceletter=% \nmlz@letterspace=% + % We put '^?' at the end of #1 to preserve any trailing '='. + % This can happen if a key has an empty value. \toks@\expandafter{\the\toks@^?}% \nmlz@doubleletter=% \cpt@commaequalerr - \def\reserved@a##1^?##2\cpt@nil{\toks@{##1##2}}% - \expandafter\reserved@a\the\toks@\cpt@nil - }% - \edef\normalized@list{\the\toks@}% - \cpttrimleadparsers#1\normalized@list - \cptexpandsecond\endgroup{% - \ifboolTF{@nmlzst}{% + \def\reserved@a##1^?##2\nmlz@nil{\toks@{##1##2}}% + \expandafter\reserved@a\the\toks@\nmlz@nil + }% + \def\reserved@a#1##1\nmlz@nil{\edef\normalized@list{\unexpanded{##1}}}% + \expandafter\reserved@a\the\toks@\nmlz@nil +% \cpttrimleadparsers#1\normalized@list + \cptexpanded{\endgroup + \ifdefboolTF{@nmlzst}{% \edef\noexpand#2{\ox@unexpanded\normalized@list}% }{% \cpt@csexit\normalized@list @@ -4020,7 +4739,7 @@ } \endgroup -\def\setup@parser@nmlz#1{% +\new@def*\SetupParserNormalizer#1{% \begingroup \uccode`\~=`#1% \uppercase{\endgroup @@ -4043,11 +4762,8 @@ \uppercase{\endgroup \def\nmlz@activeequal{% \def\cpt@tempa##1~##2\nmlz@nil{% - \ifblankTF{##2}{% - \toks@{##1}% - }{% - \cpt@tempa##1=##2\nmlz@nil - }% + \ifblankTF{##2} + {\toks@{##1}}{\cpt@tempa##1=##2\nmlz@nil}% }% \toks@\expandafter{\expandafter}\expandafter \cpt@tempa\the\toks@~\nmlz@nil @@ -4055,32 +4771,23 @@ } \def\nmlz@doubleletter#1{% \def\cpt@tempa##1#1#1##2\nmlz@nil{% - \ifblankTF{##2}{% - \toks@{##1}% - }{% - \cpt@tempa##1#1##2\nmlz@nil - }% + \ifblankTF{##2} + {\toks@{##1}}{\cpt@tempa##1#1##2\nmlz@nil}% }% \expandafter\cpt@tempa\the\toks@#1#1\nmlz@nil } \defpass\cpt@tempa#1{% \def\nmlz@spaceletter##1{% \def\cpt@tempa####1#1##1####2\nmlz@nil{% - \ifblankTF{####2}{% - \toks@{####1}% - }{% - \cpt@tempa####1##1####2\nmlz@nil - }% + \ifblankTF{####2} + {\toks@{####1}}{\cpt@tempa####1##1####2\nmlz@nil}% }% \expandafter\cpt@tempa\the\toks@#1##1\nmlz@nil }% \def\nmlz@letterspace##1{% \def\cpt@tempa####1##1#1####2\nmlz@nil{% - \ifblankTF{####2}{% - \toks@{####1}% - }{% - \cpt@tempa####1##1####2\nmlz@nil - }% + \ifblankTF{####2} + {\toks@{####1}}{\cpt@tempa####1##1####2\nmlz@nil}% }% \expandafter\cpt@tempa\the\toks@##1#1\nmlz@nil }% @@ -4094,6 +4801,108 @@ }% \expandafter\cpt@tempa\the\toks@,=\nmlz@nil } +% Double parser normalizer. This is for normalizing lists +% like {x|| y|||| z}, where the parser is double bar ||: +\new@def*\SetupDoubleParserNormalizer#1{% + \begingroup + \uccode`\~=`#1% + \uppercase{\endgroup + \def\cptnormalizedoubleparser##1{% + \unexpanded\expandafter{\romannumeral-`\q + \nmlz@activedoubleparser#1#1##1#1#1~~\nmlz@nil}% + }% + \def\nmlz@activedoubleparser##1~~##2\nmlz@nil{% + \ifblankTF{##2} + {\nmlz@spacedoubleparser##1 #1#1\nmlz@nil} + {\nmlz@activedoubleparser##1#1#1##2\nmlz@nil}% + }% + }% + \def\nmlz@spacedoubleparser##1 #1#1##2\nmlz@nil{% + \ifblankTF{##2} + {\nmlz@doubleparserspace##1#1#1 \nmlz@nil} + {\nmlz@spacedoubleparser##1#1#1##2\nmlz@nil}% + }% + \def\nmlz@doubleparserspace##1#1#1 ##2\nmlz@nil{% + \ifblankTF{##2} + {\nmlz@fourparser##1#1#1#1#1\nmlz@nil} + {\nmlz@doubleparserspace##1#1#1##2\nmlz@nil}% + }% + \def\nmlz@fourparser##1#1#1#1#1##2\nmlz@nil{% + \ifblankTF{##2} + {\ifblankTF{##1}{}{\@gobbletwo##1}} + {\nmlz@fourparser##1#1#1##2\nmlz@nil}% + }% +} +% This is for normalizing lists like {x\\ y\\\\ z}, where \\ is +% a control sequence: +\robust@def*\SetupSpecialParserNormalizer#1{% + \def\cptspecialnormalize##1{% + \unexpanded\expandafter{\romannumeral-`\q + \spnmlz@spaceparser##1 #1\nmlz@nil}% + }% + \def\spnmlz@spaceparser##1 #1##2\nmlz@nil{% + \ifblankTF{##2} + {\spnmlz@parserspace##1#1 \nmlz@nil} + {\spnmlz@spaceparser##1#1##2\nmlz@nil}% + }% + \def\spnmlz@parserspace##1#1 ##2\nmlz@nil{% + \ifblankTF{##2} + {\spnmlz@doubleparser##1#1#1\nmlz@nil} + {\spnmlz@parserspace##1#1##2\nmlz@nil}% + }% + \def\spnmlz@doubleparser##1#1#1##2\nmlz@nil{% + \ifblankTF{##2}{##1} + {\spnmlz@doubleparser##1#1##2\nmlz@nil}% + }% +} +% See file 'commands-with-different-meaning-in-environment' for examples: +\robust@def*\AtBeginColony#1{\grightaddtocsn{atbegin@#1@colonyhook}} +\robust@def*\AtEndColony#1{\grightaddtocsn{atend@#1@colonyhook}} +\new@def*\colonyarg#1{\@nameuse{colony@arg@\romannumeral#1}} +\robust@def*\newcolony{\cpt@starorlong\new@colony} +\def\new@colony#1{\cpt@testopt{\newcolony@a#1}0} +\def\newcolony@a#1[#2]{% + \cpt@ifbrack{\newcolony@b{#1}{#2}}{\newcolony@c{#1}{[#2]}}% +} +\def\newcolony@b#1#2[#3]{\newcolony@c{#1}{[#2][{#3}]}} +\robust@def*\renewcolony{\cpt@starorlong\renew@colony} +\def\renew@colony#1{% + \ifcsndefTF{#1}{}{\cpt@err{Environment '#1' is undefined}\@ehc}% + \expandafter\let\csname#1\endcsname\relax + \expandafter\let\csname end#1\endcsname\relax + \new@colony{#1}% +} +\long\def\newcolony@c#1#2#3#4{% + \ifcsndefTF{#1}{}{\letcsntocsn{#1}{end#1}}% + \def\reserved@a[##1]##2\@nil{% + \chardef\@tempa\z@pt + \loop\ifnum\@tempa<##1\relax + \edef\@tempa{\the\numexpr\@tempa+1}% + \cptexpandarg{\rightaddtocs\@tempb}{% + \csn@edef{colony@arg@\romannumeral\@tempa}% + {\noexpand\unexpanded{################\number\@tempa}}% + }% + \repeat + }% + \reserved@a#2\@nil + \cptexpandbracenext{\aftercsname\new@command{#1}#2}{% + \@tempb + \@nameuse{atbegin@#1@colonyhook}% + \@nameuse{#1@env@defs}#3% + }% + \l@ngrel@x\csn@def{end#1}{% + #4\@nameuse{atend@#1@colonyhook}% + }% +} +% #1 = environment name; #2 = command +\robust@def*\newcolonycmd{\cpt@testst\new@colonycmd} +\robust@def\new@colonycmd#1#2{% + \aftercsname\rightaddtocs{#1@env@defs}{\colonyletcs{#1}{#2}}% + \cptexpanded{\noexpand\newcommand\ifcpt@st*\fi + \noexpandcsn{#1@\cptgobblescape#2}}% +} +\def\colonyletcs#1#2{\letcstocsn{#2}{#1@\cptgobblescape#2}} + \robust@def*\cptcheckbool#1#2#3{% \lowercase{% \edef\cpt@resa{\s@expandarg\cpttrimspaces{\cptrm-`\q#1}}% @@ -4104,6 +4913,14 @@ \@latex@error{Invalid value '#1' for '#2'}\@ehc }% } +\robust@def*\cptcheckswitch#1#2{% + \edef\cpt@resa{\s@expandarg\cpttrimspaces{\cptrm-`\q#1}}% + \xifinsetTF{,\cpt@resa,}{,00,01,}{% + #2\relax + }{% + \@latex@error{Invalid switch value '#1'}\@ehc + }% +} \robust@def*\RecoverAtRequirePackage#1{% \ifblankFT{#1}{}{% \cpt@isblankerr\RecoverAtRequirePackage @@ -4112,6 +4929,8 @@ \cptfor#1\dofor{% \ifescapedTF{##1}{% \ifdefTF{##1}{}{% + % If the argument was undefined, then it couldn't be + % executed later: \cpt@notdeferr{##1}% \@gobble }% @@ -4184,7 +5003,7 @@ }% \def\csv@do##1{% \ifinsetTF{=}{##1}{}{% - \cptgaddtolist\@classoptionslist{##1}% + \cptguaddtolist\@classoptionslist{##1}% }% }% \expandafter\def\expandafter\@classoptionslist\expandafter @@ -4219,7 +5038,10 @@ } \robust@def\cpt@d@clareoption#1#2[#3]#4{% \cpt@d@cl@reoption{#1}{#2}{#3}% - \ifboolTF{cpt@olddx}{% + \ifdefboolTF{cpt@olddx}{% + % The old style of \DeclareOption may have used the + % parameter character in a format different from the + % system here: \xifstrcmpTF{\@currname.\@currext}{xcolor.sty}{% \csn@xdef{#1@ds@#2}{% \unexpanded{\edef\XC@@opt{\XC@@opt#2,}#4}% @@ -4269,6 +5091,8 @@ Second invalid option handler for family '##1'? }% }{% + % The error message will replace key macro, hence + % it needs a parameter: \csn@gdef{##1@invalidoptionerr}####1{#2####1}% }% }% @@ -4308,7 +5132,7 @@ \MsgBrk is invalid or null}{% Invalid option '##1' of family '#1'.}% }% - \ifboolTF{cpt@olddx}{% + \ifdefboolTF{cpt@olddx}{% \csn@xdef{#1@ds@##1}{\cpt@temp@err}% }{% \csn@xdef{#1@ds@##1}####1{\cpt@temp@err}% @@ -4429,10 +5253,16 @@ \cptexpandsecond\cpt@tempa{\cpt@curropts\cpt@nil{\cpt@currfam}}% \cpt@tempa{#5}{#6}% } +% #1: primary option name +% #2: secondary option name +% #3: default value +% #4: macro prefix +% #5: current family +% #6: callback for primary option \robust@def\cpt@d@cl@rebib@oloptions#1#2#3#4#5#6{% \cpt@d@cl@recmdoption{#5}{#1}{#3}(#4){% #6\relax - % If called by sister, don't call back: + % If called by sister, don't callback: \iftogTF{#5@#1}{}{% \settogtrue{#5@#2}% \settogtrue{biboolopts}% @@ -4503,7 +5333,7 @@ family '#1'\MsgBrk (in \@cls@pkg\@space'\@currname')% }% }{% - \aftercsname{\cptaddtolist*}{#1@declaredoptions}{#2}% + \aftercsname{\cptoaddtolist*}{#1@declaredoptions}{#2}% }% }{% \csn@xdef{#1@declaredoptions}{\expandcsonce{#2}}% @@ -4612,7 +5442,7 @@ \cpt@ifoptdis@bled{##1}{#2}% }% \csv@@parse*[,]\cpt@currfams - \ifboolTF{cpt@swa}\ifboolTF\ifdefboolFT{cpt@choicefd}% + \ifdefboolTF{cpt@swa}\ifdefboolTF\ifdefboolFT{cpt@choicefd}% } \robust@def*\cpt@ifoptdis@bled#1#2{% \ifcsndefFT{#1@disabledoptions}{}{% @@ -4630,11 +5460,12 @@ \robust@def*\cpt@ifoptdef<#1>#2{% \def\cpt@currfams{#1}% \cpt@choicefdfalse + % may break out of loop: \def\csv@do##1{% \cpt@ifoptd@f{##1}{#2}% }% \csv@@parse*[,]\cpt@currfams - \ifboolTF{cpt@swa}\ifboolTF\ifdefboolFT{cpt@choicefd}% + \ifdefboolTF{cpt@swa}\ifdefboolTF\ifdefboolFT{cpt@choicefd}% } \robust@def*\cpt@ifoptd@f#1#2{% \ifcsndefFT{#1@ds@#2}{}{% @@ -4684,11 +5515,17 @@ }% } \newletcs\rsv@cpt@define@default@ds\cpt@define@default@ds +% In case a \RequirePackage comes up before end of package: \RecoverAtRequirePackage\cpt@define@default@ds + \new@def*\cpt@optionlist#1{% \ifcsndefFT{opt@#1}{}{\unexpanded\cptsevenxp{\usename{opt@#1}}}% } +% Use \kv@@parse and \csv@@parse for all options processing +% because there might be the need to break out of the loop +% prematurely. \newletcs\cpt@processedoptions\@empty + \robust@def*\XProcessOptions{% \csn@def{\@currname @\@currext @beginprocessopt}{}% \letcstocsn\reserved@a{\@currname @\@currext @beforeprocessopt}% @@ -4710,7 +5547,7 @@ \cpt@testst{\cpt@testfam\cpt@processoptions}% } \robust@def*\cpt@processoptions<#1>{% - \ifboolTF{cpt@st}{% + \ifdefboolTF{cpt@st}{% \cpt@testopt{\cpt@xprocessoptions#1}{}% }{% \cpt@testopt{\cpt@yprocessoptions#1}{}% @@ -4726,7 +5563,7 @@ \cpt@ypr@cessoptions{##1}% }% \csv@@parse{#1}% - \cpt@pr@cessoptions + \cpt@pr@cessoptions{#1}% \cpt@pr@cessopti@ns } \robust@def*\cpt@ypr@cessoptions#1{% @@ -4769,7 +5606,7 @@ \cpt@xpr@cessoptions{##1}% }% \csv@@parse{#1}% - \cpt@pr@cessoptions + \cpt@pr@cessoptions{#1}% \cpt@pr@cessopti@ns } \robust@def*\cpt@xpr@cessoptions#1{% @@ -4798,7 +5635,7 @@ \ifcsnullTF\cpt@classoptions{} {\kv@@parse*[,]\cpt@classoptions}% } -\robust@def*\cpt@pr@cessoptions{% +\robust@def*\cpt@pr@cessoptions#1{% \def\kv@do##1{% \edef\CurrentOption{\unexpanded{##1}}% \ifcsnullTF\CurrentOption{}{% @@ -4831,7 +5668,7 @@ }% }% }% - \csv@@loop*[,]\cpt@currfams + \csv@@loop[,]{#1}% }% }% \ifcsnullTF\@curroptions{}{\kv@@parse*[,]\@curroptions}% @@ -4967,11 +5804,11 @@ \string\XExecuteOptions:\MsgBrk re-executed}% \fi }% - \aftercsname{\cptgaddtolist*} + \aftercsname{\cptgoaddtolist} {\cpt@currfam @exec@warnlist}\cpt@curropt }% }{% - \aftercsname{\cptgaddtolist*} + \aftercsname{\cptgoaddtolist} {\cpt@currfam @executedoptions}\cpt@curropt }% \cpt@useoption @@ -4983,6 +5820,7 @@ }% \kv@@loop*[,]\cpt@curroptvals } +% Alias keys must always come from the parent's family: \robust@def*\XExecuteAliasOption#1{% \cpt@testopt{\cpt@executealiasoption#1}{}% } @@ -4993,6 +5831,10 @@ \cptexpandsecond\cpt@executeoptions {<\cpt@currfam>{#1=\expandcsonce\reserved@a}}\relax } +% \OptionFound is always the option with its current value, +% while \CurrentOption may have no value (as in +% \cpt@ypr@cessoptions, the only function calling +% \cpt@findoption@a): \robust@def\cpt@findoption@a{% \cpt@optfdfalse \def\kv@do##1{% @@ -5012,9 +5854,11 @@ \fi }% }% + % This loop may break prematurely: \kv@@loop*[,]\cpt@classoptions } \robust@def\cpt@findoption@b{% + % Here \CurrentOption may have a value: \let\OptionFound\CurrentOption \xifinsetTF{=}{\cptoxdetok\CurrentOption}{% \expandafter\cpt@splitoption\CurrentOption=\opt@split@nil @@ -5039,19 +5883,20 @@ \robust@def*\cpt@useoption{% \ifdefboolFT{cpt@inpox}{}{% \oifinset@sp@TF,\OptionFound\cpt@processedoptions{}{% - \cptgaddtolist*\cpt@processedoptions\OptionFound + \cptgoaddtolist\cpt@processedoptions\OptionFound }% \oifinset@sp@TF,\CurrentOption\cpt@processedoptions{}{% - \cptgaddtolist*\cpt@processedoptions\CurrentOption + \cptgoaddtolist\cpt@processedoptions\CurrentOption }% \ifcsnullTF\OptionFound{}{\cpt@removecurroption\OptionFound}% \ifcseqTF\OptionFound\CurrentOption{}{% \cpt@removecurroption\CurrentOption }% }% - \ifboolTF{cpt@igopt}{}{% + \ifdefboolTF{cpt@igopt}{}{% + % Don't attempt to catch undefined options here: \ifcsndefFT{\cpt@currfam @ds@\cpt@curropt}{}{% - \ifboolTF{cpt@olddx}{% + \ifdefboolTF{cpt@olddx}{% \usename{\cpt@currfam @ds@\cpt@curropt}% }{% \ifcsnullTF\cpt@currval{% @@ -5092,16 +5937,17 @@ \robust@def*\cpt@addto@undecl@opts{% \edef\everymergeitem##1{\@space##1}% \cptexpandarg{\cptfiltermergekv[;]\cpt@undeclaredopts}% - {\cpt@currfam,\cpt@curropt,\cpt@currval}\nofilter + {\cpt@currfam,\cpt@curropt,\expandcsonce\cpt@currval}\nofilter } \robust@def*\cpt@addto@undef@opts{% \edef\everymergeitem##1{\@space##1}% \oifinset@sp@TF,\cpt@curropt\XExternalOptions{}{% \cptexpandarg{\cptfiltermergekv[;]\cpt@undefinedopts}% - {\cpt@currfam,\cpt@curropt,\cpt@currval}\nofilter + {\cpt@currfam,\cpt@curropt,\expandcsonce\cpt@currval}\nofilter }% } \AfterStartOfDocument{\AtEndDocument{% + % \cpt@undeclaredopts from \XExecuteOptions: \ifdefFT\cpt@undeclaredopts{}{% \ifcsnullTF\cpt@undeclaredopts{}{% \despacecontent\cpt@undeclaredopts @@ -5112,6 +5958,7 @@ }% }% }% + % \cpt@undefinedopts from \XProcessOptions: \ifdefFT\cpt@undefinedopts{}{% \ifcsnullTF\cpt@undefinedopts{}{% \despacecontent\cpt@undefinedopts @@ -5157,6 +6004,7 @@ \cptexpandargonce{\cptfilterremovekv[,]% \@unusedoptionlist}{#1}\nofilter } +% Remove processed options from \@unusedoptionlist: \robust@def*\cpt@optioncleanup#1{% \ifmacroTF{#1}{}{\cpt@notcserr{#1}}% \BeforeStartOfDocument{% @@ -5383,6 +6231,10 @@ \let\@fileswith@pti@ns\@@fileswith@pti@ns \AtEndOfPackage{\let\@unprocessedoptions\relax}% } +% If catoptions is loaded before \documentclass, it will +% automatically use \XDeclareOption, etc. At \documentclass, +% if usepox is false, the package will revert to \DeclareOption, +% etc: \cptpassunexpanded{% \begingroup \def\cpt@tempa{\endgroup -- cgit v1.2.3