diff options
author | Karl Berry <karl@freefriends.org> | 2012-08-19 22:18:46 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2012-08-19 22:18:46 +0000 |
commit | 193b74d3d594d824d23f6413dd0d784fd4a756fc (patch) | |
tree | d51c5f2613702b8e1d9596b49bbdc644446b83f1 /Master/texmf-dist/tex/latex/catoptions | |
parent | f5cea8308088d502f2e2760a784b372c4ebe711f (diff) |
catoptions (19aug12)
git-svn-id: svn://tug.org/texlive/trunk@27460 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/catoptions')
-rw-r--r-- | Master/texmf-dist/tex/latex/catoptions/catoptions.sty | 1368 |
1 files changed, 1110 insertions, 258 deletions
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{<list>}<processor> +% Expandable comma loop. The \noexpand before #1 is required when #1 +% begins with a command. <processor> 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[<pref>]{a,b}[<master>] +\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 <listcmd>, otherwise a <list> \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{<type>}[<prf>]{<variables>}[<state/value>] +% If <type> is 'toks', <state/value> is the content of the +% new token register. +% Valid <types> 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<params>{<defn>} +% \cptdofunclist[<parser>]{<list>} +% \cptdofunclist*[<parser>]{<listcmd>} +% 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 <list> 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<cs><testtoken>\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<cs><splitter/testtoken>\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 + +% [<fallback>]{<teststr>}{<{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 <str1>, etc, is in <teststr> +% [<default act>]{<teststr>}{<{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 +% <iofii> or <iiofii> 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[<parser>]<\cs>{<newitem>} +% * -> use \protected@(e)xdef. +% uadd -> don't expand <newitem> at all. +% oadd -> expand <newitem> once. +% xadd -> fully expand <newitem>. +% 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[<parser>]{<cs>}{<search>}{<replace>} +% #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 <options>. -% \ifpackagewithoptionsTF{<package>}{<options>} +% \ifpackagewithoptionsTF{<package>}{<options>}{<true>}{<false>} \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{<package>}{<true>}{<false>} \robust@def*\ifpackageloadedlaterTF{\cpt@ifpkgloadedlater\@pkgextension} @@ -3831,6 +4459,10 @@ }% }% } +% \cptloadpackages[<inner-parser><outer-parser>]{% +% pkga<inner>options<inner>date<outer> +% pkgb<inner>options<inner>date<outer> +% } \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 |