diff options
Diffstat (limited to 'Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex')
-rw-r--r-- | Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex | 6150 |
1 files changed, 6150 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex b/Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex new file mode 100644 index 00000000000..13e0a2a37f2 --- /dev/null +++ b/Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex @@ -0,0 +1,6150 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-core.tex', version 1.1, 2012/10/15. % +% % +% This package and accompanying files may be distributed and/or % +% modified under the conditions of the LaTeX Project Public License, % +% either version 1.3 of this license or any later version. The latest % +% version of this license is in http://www.latex-project.org/lppl.txt % +% and version 1.3 or later is part of all distributions of LaTeX % +% version 2005/12/01 or later. % +% % +% The LPPL maintenance status of this software is 'author-maintained'. % +% % +% This software is provided 'as it is', without warranty of any kind, % +% either expressed or implied, including, but not limited to, the % +% implied warranties of merchantability and fitness for a particular % +% purpose. % +% % +% The following files constitute the skeyval bundle and must be % +% distributed as a whole: % +% % +% readme, skeyval.sty, skeyval-core.tex, skeyval-for.tex, % +% skeyval-view.sty, skeyval-ltxpatch.sty, skeyval-ltxcmds.tex, % +% skeyval-pstkey.sty, skeyval-pstkey.tex, skeyval-testclass.cls, % +% and skeyval-testpkg.sty. % +% % +% Copyright (c) 2010-2012 Ahmed Musa (amusa22@gmail.com). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\csname skeyval-core-loaded\endcsname +\expandafter\let\csname skeyval-core-loaded\endcsname\endinput + +% Remove ++++++++++++++++++++++++++++++++++++++++++ +\RequirePackage[notraceifs]{codetrace} + +\begingroup +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode044 12 % , +\def\skv@prova{\endgroup + \def\do##1,{% + \ifx\do##1\else + \catcode##1\string=\the\catcode##1\relax + \expandafter\do + \fi + }% + \edef\skv@core@restorecodes{\do035,064,123,125,061,059,\do,}% +} +\skv@prova +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx\do#1\else + \edef\skv@core@restorecodes{% + \skv@core@restorecodes + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\newdimen\skvz@\skvz@=0pt\relax +\ifcase + \ifx\eTeXversion\@undefined\skvz@\else + \ifnum\eTeXversion<\tw@\skvz@\else\@ne\fi\fi + \errhelp{This package requires eTeX version 2 or higher.}% + \errmessage{skeyval package: eTeX not loaded or old version.} + \expandafter\endinput +\fi + +\newif\ifskv@inlatex +\ifx\ProvidesFile\@undefined + \skv@inlatexfalse + \message{File 'skeyval-core.tex' 2012/10/15 v1.1: + Core of 'skeyval' key-value parser (AM)} + \input skeyval-ltxcmds +\else + \skv@inlatextrue + \ProvidesFile{skeyval-core.tex} + [2012/10/15 v1.1 Base file of skeyval package (AM)] + \@addtofilelist{skeyval-core.tex} + \long\def\@nodocument#1{% + \@latex@error{'#1' appeared\on@line\space without + \noexpand\begin{document}}\@ehd + } +\fi + +% skeyval-ltxcmds.tex defines \PackageError, etc: +\protected\def\skv@warn{\PackageWarningNoLine{skeyval}} +\protected\def\skv@err{\PackageError{skeyval}} + +\protected\def\skvtracingall{% + \tracinggroups\@ne\tracingifs\@ne\loggingall\tracingassigns\@ne +} +\let\skvloggingall\skvtracingall + +% A \relax'ed command should not be redefined, because we don't know +% if it is temporarily or permanently made \relax. +\long\def\skvifdefinable#1#2{% + \ifdefined#1% + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {\@latex@error{Command '\string#1' already defined}\@ehd} + {#2}% +} + +% \skvgenloop{<parser>}{<list>}{<1.parameter.callback>} +% The parser may be empty. This is the case for nsv/tsv lists. +\def\skvgenloop#1#2#3{% + \def\skv@gendo##1#1{% + \ifx\skv@gendo##1\expandafter\@gobble\else\expandafter\@iden\fi + {#3\skv@gendo}% + }% + \skv@gendo#2#1\skv@gendo#1% +} +\def\skvnewregister#1#2{% + \def\skv@prova{#1}% + \def\skv@provb{\bool}% + % We don't want to expose \if: + \def\skv@provc{\if}% + \ifx\skv@prova\skv@provb + \let\skv@prova\skv@provc + \fi + \skvgenloop{}{#2}{% + \skvifdefinable##1{% + \csname new\expandafter\expandafter\expandafter\@gobble + \expandafter\string\skv@prova\endcsname##1% + }% + }% +} +\let\skvnewregisters\skvnewregister + +\skvnewregisters\toks{\skv@toksa\skv@toksb} + +% \skvletmanytocmd{<nsv.cmdlist>}{<cmd>} +\long\def\skvletmanytocmd#1#2{% + \skvgenloop{}{#1}{\let##1= #2}% +} + +% Sometimes we change \bgroup temporarily to avoid \futurelet confusing +% it with explicit \bgroup character. The change does confuse \skvifnextchar +% if we used \bgroup in \skvifnextchar. Hence let us use \skv@orig@bgroup +% in \skvifnextchar: +\let\skv@orig@bgroup\bgroup + +% We modify LaTeX kernel's \@ifnextchar to eliminate the need for +% doubling the hash character in #2 or #3. \@nextseen may be needed +% outside the group; so we use \global. +\long\def\skvsimpleifnextchar#1#2#3{% + \begingroup + \let\reserved@d=#1% + \edef\reserved@a{\endgroup\unexpanded{#2}}% + \edef\reserved@b{\endgroup\unexpanded{#3}}% + \global\futurelet\@nextseen\skv@simpleifnext +} +\def\skv@simpleifnext{% + \ifx\@nextseen\@sptoken + % Experience shows that this \skv@ifnextdo must be unique: If we were + % to use \next or some other generic command, it might have been + % let to \if or \fi before being assigned here and will confuse + % the above \ifx: + \let\skv@ifnextdo\skv@simpleifn@xt + \else + \ifx\@nextseen\reserved@d + \let\skv@ifnextdo\reserved@a + \else + \let\skv@ifnextdo\reserved@b + \fi + \fi + \skv@ifnextdo +} +\lowercase{\def\skv@simpleifn@xt} {% + \global\futurelet\@nextseen\skv@simpleifnext +} +\long\protected\def\skvifnextchar#1#2#3{% + \skvsimpleifnextchar\skv@orig@bgroup{% + \skvsimpleifnextchar{#1}{#2}{#3}% + }{% + % Test for \egroup, so that, eg, {\loggingall\LoadClass{article}} + % doesn't fail because of the closing brace: + \skvsimpleifnextchar\egroup{% + \skvsimpleifnextchar{#1}{#2}{#3}% + }{% + \skv@ifnextchar@a{#1}{#2}{#3} + }% + }% +} +\long\def\skv@ifnextchar@a#1#2#3#4{% + \edef\reserved@a{\skv@ifnextchar@b{#1}\skv@ifnextchar@b{#4}}% + \expandafter\ifx\reserved@a + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {#2}{#3}#4% +} +\long\def\skv@ifnextchar@b#1{% + \expandafter\skv@ifnextchar@c\string#1\relax\noboundary{#1}% +} +\long\def\skv@ifnextchar@c#1#2\noboundary#3{% + \ifx\relax#2% + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {#1}{\unexpanded{#3}}% +} + +\long\def\skvgobbleldspaces{\skvifnextchar x{}{}} +\long\def\skvgobbleleadingspaces{\skvsimpleifnextchar x{}{}} +\long\protected\def\skvstarorlong#1{% + \skvifstar{\let\l@ngrel@x\relax#1}{\let\l@ngrel@x\long#1}% +} +\long\protected\def\skv@testopt#1#2{\skvifnextchar[{#1}{#1[{#2}]}} +\long\protected\def\skvifstar#1{\skvifnextchar*{\@firstoftwo{#1}}} +\def\skv@def@#1#2#3{% + \ifdefined#3% + \ifx#3\relax\else + \skv@err{Command \detokenize{#3} already exists}\@ehd + \fi + \fi + \ifcat$\detokenize{#2}$\else + \ifx#2p\expandafter\expandafter\expandafter\protected\fi + \fi + \l@ngrel@x\csname#1def\endcsname#3% +} +\def\skv@csdef@#1#2#3{% + \def\skv@prova{\skv@def@{#1}{#2}}% + \expandafter\skv@prova\csname#3\endcsname +} +\def\skv@redef@#1#2#3{% + \ifcat$\detokenize{#2}$\else + \ifx#2p\expandafter\expandafter\expandafter\protected\fi + \fi + \l@ngrel@x\csname#1def\endcsname#3% +} +\protected\def\skvrobustdef{\skvstarorlong{\skv@def@{}{p}}} +\skvrobustdef*\skvrobustredef{\skvstarorlong{\skv@redef@{}{p}}} +\skvrobustdef*\skvrobustgdef{\skvstarorlong{\skv@def@{g}{p}}} +\skvrobustdef*\skvnewdef{\skvstarorlong{\skv@def@{}{}}} +\skvrobustdef*\skvnewedef{\skvstarorlong{\skv@def@{e}{}}} +\skvrobustdef*\skvnewgdef{\skvstarorlong{\skv@def@{g}{}}} +\skvrobustdef*\skvrobustcsdef{\skvstarorlong{\skv@csdef@{}{p}}} +\skvrobustdef*\skvnewcsdef{\skvstarorlong{\skv@csdef@{}{}}} +\skvrobustdef*\skvifplus#1{\skvifnextchar+{\@firstoftwo{#1}}} + +\skvrobustdef*\skvshowcs#1{% + \begingroup\expandafter\endgroup + \expandafter\show\csname#1\endcsname +} +\skvrobustdef*\skvcomment{% + \begingroup + \catcode`\{=12 \catcode`\}=12 \catcode`\#=12\relax + \skv@c@mment +} +\skvrobustdef\skv@c@mment#1\endcomment{\endgroup} +\skvnewedef*\skv@hashchar{\string#} +\let\nofilter\relax +% Don't use '=' at the end of the following definition; the user +% might have put '=': +\skvrobustdef*\skvnewlet#1{\skvifdefinable#1\relax\let#1 } +\skvnewlet\skv@nil\relax +\skvnewlet\skvrelax\relax +\skvnewdef*\skv@relaxtoks{\relax} +\skvnewdef*\skv@nnil{\skv@nil} +\skvnewdef\skv@car#1#2\car@nil{#1} +\skvnewdef\skv@car@detok#1#2\car@nil{\detokenize{#1}} +\skvnewdef\skv@removetonnil#1\skv@nnil{} +\skvnewdef\skv@catchtonnil#1\skv@nnil{#1} +\long\def\@gobblethree#1#2#3{} +\skvnewdef\skv@gobbletoend#1\skv@gobble@nil{} +\skvnewlet\skv@gobble@nil\relax +\def\@space{ } +\skvnewdef\skvswap#1#2{#2#1} +\skvnewdef*\skv@quark{\@gobble\skv@quark} +\skvnewlet\skvrom\romannumeral +\skvnewdef*\skv@rej{^skv^} +\skvnewdef\skvafterfi#1\fi{\fi#1} +\skvnewdef\skvafterfifi@inneriscond#1\fi#2\fi{\fi#1\fi} +\skvnewdef\skvafterfifi#1\fi#2\fi{\fi\fi#1} +\skvnewdef\skvafterelsei#1\else#2\fi{\fi#1} +\skvnewlet\skvafterelse\skvafterelsei +\skvnewdef\skvafterelseii#1\else#2\fi{\fi#2} +\skvnewedef*\skv@simplearg{\unexpanded{#1}} + +\long\def\skv@in@#1#2{% + \begingroup + \long\def\in@@##1#1##2\in@@{% + \edef\in@@{\unexpanded{##2}}% + \expandafter\endgroup + \ifx\in@@\@empty\in@false\else\in@true\fi + }% + \in@@#2{\in@@}#1\in@@ +} +\skvrobustdef*\skvcsnewif#1{% + \skvifstrcmp{#1}{x}{% + \skv@err{Redefining primitve '\@backslashchar ifx'?}\@ehd + }{% + \csname newif\expandafter\endcsname\csname if#1\endcsname + }% +} +\skvnewdef*\skv@zapornot#1{% + \ifnum#1=\skvz@ + \expandafter\@iden + \else + \expandafter\skvzapspaces + \fi +} +\skvnewdef\skvzapspaces#1{\skv@zapspaces.#1 \zap@nil} +\skvnewdef\skv@zapspaces#1 #2\zap@nil{% + \skvifblank{#2}{% + \@gobble#1% + }{% + \skv@zapspaces#1#2\zap@nil + }% +} +\skvnewdef\skvxzapspaces#1#2{% + \edef#2{.#1}% + \edef#2{\expandafter\skv@zapspaces#2 \zap@nil}% +} + +\newcommand\skvifleadspace[3]{% + \romannumeral\csname + @\skvifnull{#1}{second}{\iffalse{\fi\skv@ifleadspace.#1 x}}% + oftwo\endcsname{0 #2}{0 #3}% +} +\skvnewdef\skv@ifleadspace#1 {% + \expandafter\skvifnull\expandafter{\@gobble#1}{first}{second}% + \expandafter\@gobble\expandafter{\iffalse}\fi +} + +% \skvifldspace{ }{}{} gives <true> as desired. +\begingroup +\lccode`\&=0 \catcode`\&=7 \lccode`\!=0 \catcode`\!=8 +\lowercase{\endgroup + \skvnewdef\skvifldspace#1{\skv@ifldspace!#1! &} + \skvnewdef\skv@ifldspace#1! #2&{\skvifnotblank{#2}} + \skvnewdef\skvtrimspace#1{\skv@trimspace@a.#1& &} + \skvnewdef\skv@trimspace@a#1 &{\skv@trimspace@b#1&} + \skvnewdef\skv@trimspace@b#1{% + \unexpanded\expandafter{% + \romannumeral0% + \expandafter\skvifldspace\expandafter{\@gobble#1}{% + \@gobble#1% + }{% + \expandafter\@space\@gobble#1% + }% + }% + } +} + +\skvrobustdef*\skvdespace#1#2{\edef#2{\skvtrimspace{#1}}} +\skvrobustdef*\skvdespacecontent#1{% + \edef#1{\expandafter\skvtrimspace\expandafter{#1}}% +} +\skvnewdef*\skvcsuse#1{% + \ifcsname#1\endcsname + \csname#1\expandafter\endcsname + \fi +} +\skvrobustdef*\skvcsdef#1{\expandafter\def\csname#1\endcsname} +\skvrobustdef*\skvcsgdef#1{\expandafter\gdef\csname#1\endcsname} +\skvrobustdef*\skvcsedef#1{\expandafter\edef\csname#1\endcsname} +\skvrobustdef*\skvcsxdef#1{\expandafter\xdef\csname#1\endcsname} +\skvnewdef\skvaftercsname#1#2{% + \expandafter\skvswap\expandafter{\csname#2\endcsname}{#1}% +} +\skvnewdef*\skvifdef#1{% + \skvifblank{#1}\@secondoftwo{% + \skvifx#1\@undefined{% + \@secondoftwo + }{% + \ifx#1\relax + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + }% +} +\skvnewdef*\skvifundef#1{\skvifdef{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skvifcsdef#1{% + \skvifblank{#1}{% + \expandafter\@secondoftwo\@gobble + }{% + \ifcsname#1\endcsname + \expandafter\@firstofone + \else + \expandafter\expandafter\expandafter + \@secondoftwo\expandafter\@gobble + \fi + }{% + \expandafter\ifx\csname#1\endcsname\relax + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% +} +\skvnewdef*\skvifcsundef#1{\skvifcsdef{#1}\@secondoftwo\@firstoftwo} +\def\skv@ifx@#1#2\skv@ifx@nil{\expandafter#1} +\skvnewdef*\skvifx#1#2{% + \csname @\expandafter\expandafter\expandafter + \ifx\skv@ifx@#1\batchmode\skv@ifx@nil\skv@car#2\noboundary\car@nil + first\else second\fi oftwo\endcsname +} +\skvnewdef*\skvnotifx#1#2{\skvifx{#1}{#2}\@secondoftwo\@firstoftwo} +\skvnewdef*\skv@ghost{\@gobble\skv@ghost} +\skvnewdef\skvifempty#1{% + \ifx#1\@empty\skv@ghost + \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi +} +\skvnewdef\skvifcsempty#1{\expandafter\skvifempty\csname#1\endcsname} +\skvnewdef\skvifdefempty#1{% + \skvifblank{#1}\@secondoftwo{% + \skvifntype{#1}{% + \skvifdef{#1}{\skvifx#1\@empty}{\@secondoftwo}% + }{% + \@secondoftwo + }% + }% +} + +\let\then\iffalse +\begingroup +% Keep lccode, in case the test occurs in a table: +\lccode`\&=0 \catcode`\&=8 +\lowercase{\endgroup + \skvnewdef\skvifnull#1{% + \csname @\ifcat&\detokenize{#1}&first\else second\fi oftwo\endcsname + } + \skvnewdef\skviflacus#1\then{\ifcat&\detokenize{#1}&} +} +\skvnewdef\skvifblank#1{% + \expandafter\skvifnull\expandafter{\@gobble#1.}% +} +\skvnewdef\skvifnotblank#1{\skvifblank{#1}\@secondoftwo\@firstoftwo} +\skvnewdef\iflacuna#1\then{\if\relax\detokenize{#1}\relax} + +\skvnewdef*\skvifbool#1{% + \skvifblank{#1}{% + \@secondoftwo + }{% + \skvifcsdef{if#1}{% + \csname @\csname if#1\endcsname + first\else second\fi oftwo\endcsname + }{% + \skv@err{Undefined boolean '#1'}\@ehd + }% + }% +} +\skvnewdef*\skvifnotbool#1{\skvifbool{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skvifbooltrue#1\then{\csname if#1\endcsname} +\skvnewdef*\skvifboolfalse#1\then{\csname if#1\endcsname\else} +\skvnewdef*\skvifdefbool#1{% + \csname @\csname if#1\endcsname first\else second\fi oftwo\endcsname +} +% \skvlogicalif<bool>"<true>"<false>" +\skvnewdef*\skvlogicalif#1"#2"#3"{% + \skvifbool{#1}{#2}{#3}\skvgobbleleadingspaces +} +% \skvifboolval{<toks>}{<true>}{<false>} +\skvrobustdef*\skvifboolval#1{% + \skvxifin{,\expandafter\skvtrimspace\expandafter + {\detokenize{#1}},}{,\detokenize{true,false},}% +} +% Generate error if <toks> is not a valid boolean value, otherwise +% execute #2: +% \skvifvalidboolval{<toks>}{<true>} +\skvrobustdef*\skvifvalidboolval#1#2{% + \skvxifin{,\expandafter\skvtrimspace\expandafter + {\detokenize{#1}},}{,\detokenize{true,false},}% + {#2}{\skv@badboolerr{#1}}% +} +\skvrobustdef*\skvxifboolval#1{% + \begingroup + \edef\skv@prova{#1}% + \skvexpandtwoargs{\endgroup\skvifin} + {,\skvoxdetok\skv@prova,}{,\detokenize{true,false},}% +} +% Convert bool value (true or false) to switch value (00 or 01): +\skvrobustdef*\skvbooltoswitch#1#2{% + \begingroup + \def\reserved@a##1#1##2##3\skv@nil{% + \def#2{##2}% + \ifx#2\@nnil + \skv@err{Bad boolean value '#1' can't be + \MessageBreak converted to switch value}\@ehc + \fi + }% + \reserved@a true{00}false{01}#1{\@nil}\skv@nil + \skvaftergroupdef#2\endgroup +} +\skvrobustdef*\skv@badboolerr#1{% + \skv@err{Invalid boolean value '#1'; expected 'true' or 'false'}\@ehd +} + +% All switch functions of skeyval pacakge operate on a command +% and not on csname. +\skvnewdef*\skvnewswitch#1{% + \skvifcsdef{skv@sw@#1}{% + \skv@err{Switch '\detokenize{#1}' already exists}\@ehd + }{% + \skvcsdef{skv@sw@#1}{01}% + }% +} +\skvnewdef*\skvifswitch#1{% + \if + \skvifcsdef{skv@sw@#1}{% + \csname skv@sw@#1\endcsname + \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi + }{% + 01% + }% +} +\skvnewdef*\skvswitchtrue#1{% + \skvifcsdef{skv@sw@#1}{% + \skvcsdef{skv@sw@#1}{00}% + }{% + \skv@err{No switch '\detokenize{#1}'}\@ehd + }% +} +\skvnewdef*\skvswitchfalse#1{% + \skvifcsdef{skv@sw@#1}{% + \skvcsdef{skv@sw@#1}{01}% + }{% + \skv@err{No switch '\detokenize{#1}'}\@ehd + }% +} + +\begingroup +\def\x#1{\endgroup + \skvnewdef*\skvnegatesw##1{% + 0\ifcase##11#1\or\expandafter1\else\expandafter0\fi + }% + \skvnewdef*\skvifbothswtrue##1##2{% + 0\ifcase##1##2#1\expandafter0\else\expandafter1\fi + }% + \skvnewdef*\skvifoneswtrue##1##2{% + 0\ifnum##1##2<101#1\expandafter0\else\expandafter1\fi + }% +} +\x{ } + +\skvnewdef\skvifcond#1\fi{% + \csname @#1first\else second\fi oftwo\endcsname +} +\skvnewdef\skvifnotcond#1\fi{\skvifcond{#1}\fi\@secondoftwo\@firstoftwo} +% Maybe #1 isn't expandable: +\skvnewdef\skvifcondition#1\fi{% + #1\relax + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvnewdef*\skvifnum#1#{\skvifcond\ifnum#1\fi} +\skvnewdef\skvalloftwo#1#2{#1#2} +% If '#1' is a single, non-space and non-braced token: +\skvnewdef\skvifntype#1{% + \csname @\if0\skv@strcmp + {\skvexpandonce{\skvalloftwo#1{}{}}}{\unexpanded{#1{}}}% + first\else second\fi oftwo\endcsname +} +\skvnewdef\skvxifntype{\skvexpandarg\skvifntype} +\skvrobustdef*\skvundef#1{\let#1\@undefined} +\skvrobustdef*\skvgundef#1{\global\let#1\@undefined} +\skvrobustdef*\skvcsundef#1{\skvcslet{#1}\@undefined} +\skvrobustdef*\skvcsgundef#1{\global\skvcslet{#1}\@undefined} +\skvrobustdef*\skvundefcmdlist#1{% + \skvcommaparse{#1}\skv@prova{\expandafter\skvundef\skv@prova}% +} +\skvrobustdef*\skvemptifycmds#1{% + \skvcommaparse{#1}\skv@prova{\expandafter\def\skv@prova{}}% +} +\skvifdef\pdfstrcmp{% + \let\skv@strcmp\pdfstrcmp +}{% + \RequirePackage{pdftexcmds}% + \skvifdef\pdf@strcmp{% + \let\skv@strcmp\pdf@strcmp + }{% + \skv@err{Neither '\string\pdfstrcmp' nor + '\string\pdf@strcmp' exists}\@ehd + }% +} +\skvnewdef\skvifstrcmp#1#2{% + \csname @\ifnum\skv@strcmp{\detokenize{#1}}% + {\detokenize{#2}}=0first\else second\fi oftwo\endcsname +} +\skvnewdef\skvifstreq#1\with#2\then{% + \ifnum\skv@strcmp{\detokenize{#1}}{\detokenize{#2}}=\skvz@ +} +% \ifskvstreq may be used in, eg, \newforeach, where every user-given +% conditional must start with \if...: +\skvnewlet\ifskvstreq\skvifstreq +\skvnewdef\skvoifstrcmp#1#2{% + \csname @\ifnum\skv@strcmp{\skvexpandonce{#1}}% + {\skvexpandonce{#2}}=0first\else second\fi oftwo\endcsname +} +\skvnewdef\skvxifstrcmp#1#2{% + \csname @\ifnum\skv@strcmp{#1}{#2}=0first\else + second\fi oftwo\endcsname +} +\skvrobustdef\skvcslet#1#2{\expandafter\let\csname#1\endcsname#2} +\skvrobustdef\skvnewcslet#1{% + \skvifcsdef{#1}{% + \skv@err{Command '\skvnoexpandcs{#1}' already defined}\@ehd + }{% + \skvcslet{#1}% + }% +} +\skvrobustdef\skvletcs#1#2{% + \begingroup\expandafter\endgroup\expandafter + \let\expandafter#1\csname#2\endcsname +} +\skvrobustdef\skvnewletcs#1{% + \skvifdef{#1}{% + \skv@err{Command '\string#1' already defined}\@ehd + }{% + \skvletcs#1% + }% +} +\skvrobustdef*\skvcsletcs#1#2{% + \begingroup\expandafter\endgroup + \expandafter\let\csname#1\expandafter + \endcsname\csname#2\endcsname +} +\skvrobustdef\skvnewcsletcs#1{% + \skvifcsdef{#1}{% + \skv@err{Command '\skvnoexpandcs{#1}' already defined}\@ehd + }{% + \skvcsletcs{#1}% + }% +} +\skvrobustdef*\skvglobalizecs#1{% + \begingroup\expandafter\endgroup\expandafter + \global\expandafter\let\csname#1\expandafter\endcsname + \csname#1\endcsname +} +\skvrobustdef*\skvaftergroupdef#1\endgroup{% + \skvexpanded{\endgroup\skvcmdexit#1}% +} +\skvrobustdef*\skvaftergroupcsdef#1\endgroup{% + \skvexpanded{\endgroup\expandafter\skvcmdexit\csname#1\endcsname}% +} + +% Taking command and booleans out of a local group under \edef: +\skvnewdef*\skvcmdexit#1{% + \edef\noexpand#1{\noexpand\unexpanded{\skvexpandonce{#1}}}% +} +\skvnewdef*\skvcsexit#1{% + \skvcsedef{#1}{\noexpand\unexpanded{\skvexpandcsonce{#1}}}% +} +% \edef\x{\endgroup\skvcmdexitloop{\x\y}}: +\skvnewdef*\skvcmdexitloop#1{\skv@cmdexitloop#1\end} +\skvnewdef*\skv@cmdexitloop#1{% + \ifx\end#1\else\skvcmdexit{#1}\expandafter\skv@cmdexitloop\fi +} +\skvnewdef*\skvboolexit#1{% + \expandafter\noexpand\csname\expandafter\@gobblethree + \string#1#1true\else false\fi\endcsname +} +% \edef\x{\endgroup\skvboolexitloop{\ifboola\ifboolb}}: +\skvnewdef*\skvboolexitloop#1{\skv@boolexitloop#1\end} +\skvnewdef*\skv@boolexitloop#1{% + \ifx\end#1\else\skvboolexit{#1}\expandafter\skv@boolexitloop\fi +} +\skvnewdef*\skvprotect#{% + \ifx\protect\@typeset@protect\skv@protect\@firstofone\fi + \ifx\protect\@unexpandable@protect\skv@protect\skvunexpandable\fi + \ifx\protect\noexpand\skv@protect\unexpanded\fi + \ifx\protect\string\skv@protect\detokenize\fi + \relax\@firstofone +} +\skvnewdef*\skv@protect#1#2\relax\@firstofone{\fi#1} +\skvnewdef*\skvunexpandable#1{\unexpanded{\skvprotect{#1}}} +\skvrobustdef\skvifnotnil#1{% + \begingroup + \edef\skv@prova{\unexpanded{#1}}% + \expandafter\endgroup\ifx\skv@prova\skv@nnil + \expandafter\@gobble\else\expandafter\@firstofone\fi +} + +\skvrobustdef*\skvsetnumber#1{% + \begingroup + \def#1{\edef#1{\endgroup\def\noexpand#1{\the\count@}}#1}% + \afterassignment#1\count@ +} +\skvnewlet\skvsetno\skvsetnumber +\skvrobustdef*\skvadvanceno{\skv@advanceno\relax} +\skvrobustdef*\skvgadvanceno{\skv@advanceno\global} +\skvrobustdef*\skv@advanceno#1#2#3{% + % Raise error if #3 isn't an integer, \chardef'd or \countdef'd: + \begingroup + \count@#3\relax + \endgroup + \skvifdef#2{% + #1\edef#2{\the\numexpr#2+#3\relax}% + }{% + \skv@err{Number '\string#2' is not defined}\@ehd + }% +} +\skvrobustdef*\skvpushnumber#1{% + \skvifdef#1{% + \edef#1{\the\numexpr#1+1\relax}% + }{% + \def#1{0}% + }% +} +\skvrobustdef*\skvpopnumber#1{% + \skvifdef#1{% + \edef#1{\the\numexpr#1+1\relax}% + }{% + \skv@err{Number '\string#1' is not defined}\@ehd + }% +} + +\skvrobustdef\skv@testst#1{% + \skvifstar{\skv@tempsttrue#1}{\skv@tempstfalse#1}% +} +\skvrobustdef\skv@testcl#1{% + \skvifnextchar!{\skv@cltrue#1}{\skv@clfalse#1}% +} +\skvrobustdef\skv@teststopt#1#2{\skv@testst{\skv@testopt{#1}{#2}}} + + +% Removing spurious spaces before checking for brace. +% If we were to use \skvtrimspace for \skvifbraced, then the need +% for double \romannumeral will cause problems of premature expansion. +\begingroup +\lccode`\&=0 \catcode`\&=7 +\skvnewgdef\skvifbraced#1{% + \expandafter\skv@ifbraced@a\expandafter{% + \romannumeral-`\q\skv@ifbraced@b.#1& &% + }% +} +\skvnewgdef\skv@ifbraced@a#1{\skv@ifbraced@d#1\brace@nil{#1}} +\skvnewgdef\skv@ifbraced@b#1 &{\skv@ifbraced@c#1&} +\skvnewgdef\skv@ifbraced@c#1{% + \expandafter\skvifldspace\expandafter{\@gobble#1}% + {\@gobble#1}{\expandafter\@space\@gobble#1}% +} +\skvnewgdef\skv@ifbraced@d#1\brace@nil#2{% + \skvifstrcmp{#1}{#2}\@secondoftwo\@firstoftwo +} +\endgroup + +\skvnewdef\skvifnotbraced#1{\skvifbraced{#1}\@secondoftwo\@firstoftwo} + +% \skvstripouterbraces{<nr>}<cmd> +\skvrobustdef*\skvstripouterbraces#1#2{% + \begingroup + \@tempcnta\skvz@ + \expandafter\skv@stripouterbraces#2\strip@nil{#2}{#1}% +} +\skvrobustdef*\skv@stripouterbraces#1\strip@nil#2#3{% + \advance\@tempcnta\@ne + \skvifnum\@tempcnta=#3{% + \edef#2{\unexpanded{#1}}% + \skvaftergroupdef#2\endgroup + }{% + \skv@stripouterbraces#1\strip@nil{#2}{#3}% + }% +} + +\skvrobustdef*\skvifescaped#1{% + \begingroup\escapechar92\relax + \edef\x{\expandafter\skv@car\string#1x\car@nil}% + \expandafter\endgroup + \csname @\ifx\x\@backslashchar first\else second\fi oftwo\endcsname +} +\skvnewdef\skvifnotescaped#1{\skvifescaped{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skvremovescape#1{\expandafter\@gobble\string#1} +\skvnewdef*\skvgobblescape#1{% + \skvifblank{#1}{}{% + \skvifnum\escapechar>255{\string#1}{% + \skvifnum\escapechar<0{\string#1}{% + \skvifnum\escapechar=32{% + \expandafter\@gobblespace\string#1% + }{% + \skvremovescape{#1}% + }% + }% + }% + }% +} +\skvnewdef*\skvoxdetok#1{\detokenize\expandafter{#1}} +\skvnewdef*\skvexpandonce#1{\unexpanded\expandafter{#1}} +\skvnewlet\skvxonce\skvexpandonce +\skvnewlet\skvxaft\expandafter +\skvnewdef*\skvexpandtwice#1{% + \unexpanded\expandafter\expandafter\expandafter{#1}% +} +\skvnewdef*\skvexpandthrice#1{% + \unexpanded\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\expandafter{#1}% +} +\skvnewdef*\skvnoexpandcs#1{\skvexpandonce{\csname#1\endcsname}} +\skvnewdef*\skvexpandcsonce#1{% + \unexpanded\expandafter\expandafter\expandafter{\csname#1\endcsname}% +} +\skvrobustdef\skvexpanded#1{\begingroup\edef\x{\endgroup#1}\x} +\skvrobustdef\skvexpandarg#1#2{\skvexpanded{\unexpanded{#1}{#2}}} +\skvrobustdef\skvexpandargonce#1#2{% + \skvexpanded{\unexpanded{#1}{\skvexpandonce{#2}}}% +} +\skvrobustdef*\skvexpandtwoargs#1#2#3{\skvexpanded{\unexpanded{#1}{#2}{#3}}} +\skvrobustdef*\skvexpandtwoargsonce#1#2#3{% + \skvexpanded{\unexpanded{#1}{\skvexpandonce{#2}}{\skvexpandonce{#3}}}% +} +\skvrobustdef\skvexpandsecond#1#2{\skvexpanded{\unexpanded{#1}#2}} +\skvrobustdef\skvexpandsecondonce#1#2{% + \skvexpanded{\unexpanded{#1}\skvexpandonce{#2}}% +} +\skvnewdef\skvexpandnext#1#2{\expandafter\skvswap\expandafter{#2}{#1}} +\skvnewdef\skvexpandbracenext#1#2{% + \expandafter\skvswap\expandafter{\expandafter{#2}}{#1}% +} +\skvnewlet\skvexpbracenext\skvexpandbracenext +\skvrobustdef*\skvifin#1#2{% + \skv@in@{#1}{#2}% + \csname @\ifin@ first\else second\fi oftwo\endcsname +} +\skvrobustdef*\skvifnotin#1#2{\skvifin{#1}{#2}\@secondoftwo\@firstoftwo} +\skvrobustdef*\skvxifin#1#2{\skvexpandtwoargs\skvifin{#1}{#2}} +\skvrobustdef*\skvxifnotin#1#2{\skvxifin{#1}{#2}\@secondoftwo\@firstoftwo} +\skvrobustdef*\skvifindetok#1#2#3{% + \skvexpandtwoargs\skvifin{#1\skvoxdetok{#2}#1}{#1\skvoxdetok{#3}#1}% +} +\skvnewdef\skviffound#1\in#2\then{\skv@in@{#1}{#2}\ifin@} +\skvnewdef\skvxiffound#1\in#2\then{\skvexpandtwoargs\skv@in@{#1}{#2}\ifin@} + +% \skv@kvsplit{<kvpair>}{<parametered.callback>} +\skvrobustdef*\skv@kvsplit#1#2{% + \begingroup + \def\skv@tempa##1=##2=##3\skv@kvsplit{% + \endgroup#2% + }% + \skv@tempa#1==\skv@kvsplit +} +% \skv@slsplit{<slashlist>}{<parametered.callback>} +\skvrobustdef*\skv@slsplit#1#2{% + \begingroup + \def\skv@tempa##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@slsplit{% + \endgroup#2% + }% + \skv@tempa#1/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/\skv@slsplit +} + +\skvrobustdef*\skv@rescan#1{% + \begingroup + \endlinechar\m@ne\newlinechar\m@ne + \catcode`\@=11 + \everyeof{\skv@rescan@}% + \def\reserved@a##1\skv@rescan@{% + \endgroup\edef#1{\unexpanded{##1}}% + }% + \expandafter\reserved@a\scantokens\expandafter{#1}% +} + +\skvnewdef*\skvifdigitpresent#1{% + \skvifcond\if0\skvifblank{#1}{1}{\skv@ifdigitpresent#1\skv@nnil}\fi +} +\skvnewdef*\skv@ifdigitpresent#1{% + \skvifx#1\skv@nnil{% + 1% + }{% + \skvifnum`#1>47{% + \skvifnum`#1<58{% + 0\skv@removetonnil + }{% + \skv@ifdigitpresent + }% + }{% + \skv@ifdigitpresent + }% + }% +} + +\skvnewdef*\skvifalldigit#1{% + \skvifcond\if0\skvifblank{#1}{1}{\skv@ifalldigit#1\skv@nnil}\fi +} +\skvnewdef*\skv@ifalldigit#1{% + \skvifx#1\skv@nnil{0}{% + \skvifnum`#1>47{% + \skvifnum`#1<58{% + \skv@ifalldigit + }{% + 1\skv@removetonnil + }% + }{% + 1\skv@removetonnil + }% + }% +} + +\skvnewdef*\skvifalphapresent#1{% + \skvifcond\if0\skvifblank{#1}{1}{\skv@ifalphapresent#1\skv@nnil}\fi +} +\skvnewdef*\skv@ifalphapresent#1{% + \skvifx#1\skv@nnil{% + 1% + }{% + \skvifnum`#1>96{% + \skvifnum`#1<123{% + 0\skv@removetonnil + }{% + \skv@ifalphapresent + }% + }{% + \skv@ifalphapresent + }% + }% +} +\skvnewdef*\skvifAlphapresent#1{% + \skvifcond\if0\skvifblank{#1}{1}{\skv@ifAlphapresent#1\skv@nnil}\fi +} +\skvnewdef*\skv@ifAlphapresent#1{% + \skvifx#1\skv@nnil{% + 1% + }{% + \skvifnum`#1>64{% + \skvifnum`#1<91{% + 0\skv@removetonnil + }{% + \skv@ifAlphapresent + }% + }{% + \skv@ifAlphapresent + }% + }% +} +\skvnewdef*\skvifprimitive#1{% + \expandafter\skv@ifprimitive\meaning#1\relax +} +\skvnewdef*\skv@ifprimitive#1#2\relax{% + \skvifcond\if#1\@backslashchar\fi{% + \skvifdigitpresent{#2}\@secondoftwo\@firstoftwo + }{% + \@secondoftwo + }% +} + +\begingroup +\catcode`\&=7 +\skvnewgdef*\skvdefregistertester#1#2{% + \begingroup + \def\x##1{\skvnoexpandcs{skv@\skvremovescape#1test@##1}}% + \edef\x{\endgroup + \def\noexpand#2####1{% + \unexpanded{\skvifx#1{##1}\@secondoftwo}{% + \unexpanded{\skvifprimitive{##1}\@secondoftwo}% + {\noexpand\expandafter\x{a}\noexpand\meaning####1:&}% + }% + }% + \def\x{a}####1:####2&{\x{b}####1\string#1&}% + \def\x{b}####1\string#1####2&{\noexpand\skvifblank{####1}}% + }\x +} +\endgroup +\skvdefregistertester\skip\skvifskip +\skvdefregistertester\count\skvifcount +\skvdefregistertester\dimen\skvifdimen +\skvdefregistertester\toks\skviftoks + +% Converting units. +% Examples: +% \edef\x{\skvconvertunit{10pt}{mm}} +% \edef\x{\skvconvertunit{1in}{bp}} +\skvnewdef*\skvconvertunit#1#2{% + \strip@pt\dimexpr#1*\p@/\dimexpr1#2\relax\relax#2% +} + +% If all the tokens are digits or arithmetic: +\skvnewdef*\skvifallarithmetic#1{% + \begingroup + \skv@swatrue + \skvifdimensionable{#1}{}{% + \ifskv@isinteger\else + \def\do##1##2{% + \ifx\do##1\else + \skvifin{##1}{0123456789.+-*/()}{}{% + \skvifin{,##1##2,}{,em,ex,in,pt,pc,cm,mm,dd,cc,nd,nc,bp,sp,}{}{% + \skv@swafalse + \def\do####1\skvrelax{}% + }% + }% + \expandafter\do + \fi + ##2% + }% + \do#1\do\skvrelax + \fi + }% + \expandafter\endgroup\ifskv@swa + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} + +% Test if a token #1 can be assigned to a \dimendef'd register. We assume +% that #1 isn't a complicated expression and isn't padded with trailing +% \relax's. A complicated expression will be something like +% +% 2pt+3ex-(3pt/4+1.45cc*3) +% +% which is much harder to parse and decide on. +% +% Examples: +% +% \skvifdimensionable{01}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{01pt}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{01abpt}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable\paperwidth{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{\skip0}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{1.2pt}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{1.2\relax pt}{\def\x{T}}{\def\x{F}} +% \skvifdimensionable{1pt\relax\skvrelax}{\def\x{T}}{\def\x{F}} +% + +\skvnewregister\bool\ifskv@isinteger + +% In case the argument of \skvifinteger containes \numexpr: +\skvnewlet\skv@catch@nil\relax +% #1: Essence, #2: Test token +\skvrobustdef*\skvensureinteger#1#2{% + \begingroup + \def\skv@prova##1\skv@catch@nil{% + % If ##1 is empty or contains nothing before a \relax, it's fine: + \ifx\relax##1\relax\else + \skv@err{Token '#2' for '#1' isn't an integer}\@ehd + \fi + }% + \afterassignment\skv@prova\skv@cnta#2\relax\skv@catch@nil + \endgroup +} +\skvnewdef*\skv@validunit{em,ex,in,pt,pc,cm,mm,dd,cc,nd,nc,bp,sp} +\skvrobustdef*\skv@catchremainder#1\skv@catch@nil{% + \endgroup\def\skv@elt{#1}% +} +\skvrobustdef*\skvifinteger#1{% + \begingroup + % Don't try '\numexpr0+#1' here: + \afterassignment\skv@catchremainder + \@tempcnta\numexpr0#1\relax\skv@catch@nil + \skvifx\skv@elt\@empty{% + \@firstoftwo + }{% + \skvifx\skv@elt\skv@relaxtoks\@firstoftwo\@secondoftwo + }% +} +\skvrobustdef\skvoifinteger{\skvexpbracenext\skvifinteger} +\skvrobustdef\skvifdimensionable#1{% + \def\skv@dimunit{}% + % Does 'pt' mean 0pt or 1pt? Make it an invalid input: + \lowercase{\skvifindetok{,}{#1}}\skv@validunit{% + \@latexerr{Illegal step '#1': no number found with it}\@ehd + }{}% + \skv@isintegerfalse + \skvifinteger{#1}{% + \skv@isintegertrue\@secondoftwo + }{% + \expandafter\skv@ifdimensionable\skv@elt\skv@catch@nil + }% +} +\skvrobustdef\skv@ifdimensionable#1#2\skv@catch@nil{% + \skvifx\relax#1{% + \skvifblank{#2}{% + \skv@isintegertrue\@secondoftwo + }{% + \expandafter\skv@findunit\skv@elt\skv@catch@nil + }% + }{% + \expandafter\skv@findunit\skv@elt\skv@catch@nil + }% +} +\skvrobustdef\skv@findunit#1#2#3\skv@catch@nil{% + \def\skv@provb{% + \ifx\relax#2% + \if\relax\detokenize{#3}\relax + \expandafter\expandafter\expandafter\@firstoftwo + \else + \expandafter\expandafter\expandafter\@secondoftwo + \fi + \else + \expandafter\@secondoftwo + \fi + }% + \skvifstrcmp{.}{#1}{% + % In this case, #1 must have come with a trailing \relax. + % Remove it and re-start the test: + \def\skv@prova##1##2\relax\skv@catch@nil{% + \skvifdimensionable{##1##2}% + }% + \skv@prova#2#3\skv@catch@nil + }{% + % Check for a valid unit. May be a \dimendef'd token. + \skvifin{,#1,}{,\hsize,\vsize,}{% + \skv@provb + }{% + \skvifdimen{#1}{% + \skv@provb + }{% + \lowercase{\skvifindetok{,}{#1#2}}\skv@validunit{% + \def\skv@dimunit{#1#2}% + % Check if #3 has only one (leading) \relax: + \def\skv@prova##1##2##3\skv@catch@nil{% + \def\elt{##1}\def\skv@elt{##2}% + }% + \skv@prova#3\skv@quark\skv@quark\skv@catch@nil + \ifx\elt\skv@relaxtoks + \def\skv@prova{\skv@quark}% + \ifx\skv@elt\skv@prova + \expandafter\expandafter\expandafter\@firstoftwo + \else + % \skv@elt might contain \relax, but never mind; let us end + % the search here. The samething can be said of the tests in + % \skv@provb. + \expandafter\expandafter\expandafter\@secondoftwo + \fi + \else + \expandafter\@secondoftwo + \fi + }{% + \@secondoftwo + }% + }% + }% + }% +} + +% Evaluating a dimensionable expression. +% +% 1. This can't parse expressions containing both * and / in one component, +% eg, in 3pt*3/2. But (3pt*3)/2 or (3pt/2)*3 will pass. +% +% Examples: +% +% \skvifdimexpr{2bp*3/2}{<T>}{<F>} : fails +% \skvifdimexpr{1pt-2bp*3+(3cc-2ex)/2}{<T>}{<F>} : true +% \skvifdimexpr{(1pt-2bp+(3cc-2ex)/2)*5}{<T>}{<F>} : true +% \skvifdimexpr{1+(1pt-2bp+(3cc-2ex)/2)*5}{<T>}{<F>} : false +% +\skvrobustdef*\skvifdimexpr#1{% + \begingroup + \@tempcnta\skvz@ + \skv@dimexpr@beg + \skv@dimexpr@bgroup#1(\skv@catch@nil + \skv@dimexpr@end + \skv@dimexpr@end@end +} +\skvrobustdef*\skv@dimexpr@beg{\begingroup\@tempcnta\skvz@} +\skvrobustdef*\skv@dimexpr@end{% + \skv@dimexpr@end@end + \skv@dimexpr@true\skv@dimexpr@false +} +\skvrobustdef*\skv@dimexpr@end@end{% + \skvifnum\@tempcnta<\skvz@ + {\endgroup\@secondoftwo}{\endgroup\@firstoftwo}% +} +\skvrobustdef*\skv@dimexpr@true{\advance\@tempcnta\skvz@} +\skvrobustdef*\skv@dimexpr@false{\advance\@tempcnta\m@ne} +\skvrobustdef*\skv@dimexpr@bgroup#1(#2\skv@catch@nil{% + \skv@dimexpr@egroup#1)\skv@catch@nil + \skvifblank{#2}{}{% + \skv@dimexpr@beg + \skv@dimexpr@bgroup#2\skv@catch@nil + }% +} +\skvrobustdef*\skv@dimexpr@egroup#1)#2\skv@catch@nil{% + \skv@dimexpr@plus#1+\skv@catch@nil + \skvifblank{#2}{}{% + \skv@dimexpr@end + \skv@dimexpr@egroup#2\skv@catch@nil + }% +} +\skvrobustdef*\skv@dimexpr@plus#1+#2\skv@catch@nil{% + \skv@dimexpr@minus#1-\skv@catch@nil + \skvifblank{#2}{}{\skv@dimexpr@plus#2\skv@catch@nil}% +} +\skvrobustdef*\skv@dimexpr@minus#1-#2\skv@catch@nil{% + \skvifblank{#1}{}{% + \skvifin{*}{#1}{% + \skvifin{/}{#1}{% + \skv@err{I can't parse this: both star (*) and slash + \MessageBreak (/) are in '#1'}\@ehd + }{% + \skvifblank{#1}{}{\skv@dimexpr@times#1*\skv@catch@nil}% + }% + }{% + \skvifblank{#1}{}{\skv@dimexpr@divide#1/\skv@catch@nil}% + }% + }% + \skvifblank{#2}{}{\skv@dimexpr@minus#2\skv@catch@nil}% +} +% Because of, eg, 2pt*3/2, the test for * is more subtle: +\skvrobustdef*\skv@dimexpr@times#1*#2\skv@catch@nil{% + \skvifblank{#1}{}{% + \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false + }% + \skvifblank{#2}{}{% + \def\skv@prova##1*##2\skv@catch@nil{##1}% + \skvexpbracenext\skv@dimexpr@integer{\skv@prova#2\skv@catch@nil}% + }% +} +\skvrobustdef*\skv@dimexpr@divide#1/#2\skv@catch@nil{% + \skvifblank{#1}{}{% + \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false + }% + \skvifblank{#2}{}{% + \def\skv@prova##1/##2\skv@catch@nil{##1}% + \skvexpbracenext\skv@dimexpr@integer{\skv@prova#2\skv@catch@nil}% + }% +} +\skvrobustdef*\skv@dimexpr@integer#1{% + \skvifblank{#1}{}{% + \skvifinteger{#1}\skv@dimexpr@true\skv@dimexpr@false + }% +} + +% \skvtrimlastparser{<parser>}<cmd> +\skvrobustdef\skvtrimlastparser#1#2{% + \begingroup + % The following \relax will not be cat-12, so it won't appear in #2: + \def\skv@trim@a{% + \skvxifin{\detokenize{#1}\relax}{\skvoxdetok{#2}\relax}{% + \def\skv@trim@b####1#1\normal@nil{% + \edef#2{\unexpanded{####1}}% + \skv@trim@a + }% + \expandafter\skv@trim@b#2\normal@nil + }{}% + }% + \skv@trim@a + \skvaftergroupdef#2\endgroup +} + +% Generate parameter characters from number #1 to #2: +\skvnewdef*\skvgenerateparameters#1#2{% + \ifnum#1<\numexpr#2+1####\number#1% + \expandafter\skvgenerateparameters + \expandafter{\number\numexpr#1+1\expandafter}% + \expandafter{\number#2\expandafter}% + \fi +} + +% Normalize non-parser-separated tokenlist. Preserve outer braces. +% \edef\x{\skvtsvnormalize{ x y {z}}} +\skvnewdef*\skvtsvnormalize#1{% + \unexpanded\expandafter + {\romannumeral-`\q\skv@tsvnormalize.#1 \normal@nil}% +} +\skvnewdef*\skv@tsvnormalize#1 #2\normal@nil{% + \skvifblank{#2} + {\expandafter\expandafter\expandafter\space + \expandafter\noexpand\@gobble#1} + {\skv@tsvnormalize#1#2\normal@nil}% +} + + +% \skvnormalize{<csv/kv>}<parser>{<list>}<cmd> +\skvrobustdef*\skvnormalize#1#2#3#4{% + \def\skv@prova##1#1##2##3\skv@nil{% + \ifx\skv@nnil##2% + \skv@err{Invalid list type '#1' for command \string\skvnormalize}\@ehd + \fi + \edef#4{\unexpanded{#3}}% + ##2[#2]#4% + }% + \skv@prova csv\skvcsvnormalize kv\skvkvnormalize#1\skv@nnil\skv@nil +} +\skvnewswitch{inkv} +% \skvcsvnormalize[<parser>]<listcmd> +\skvrobustdef*\skvcsvnormalize{\skv@testopt\skv@csvnormalize,} +\skvrobustdef*\skv@csvnormalize[#1]#2{% + \begingroup + \skv@setupnormalizer{#1}% + \skvswitchfalse{inkv}% + \edef#2{\expandafter\skv@normalizelist\expandafter{#2}}% + \skvaftergroupdef#2\endgroup +} +% \skvcsvnormalizeset{<cmda>{<token1>,<cmdb2>{<token2>},...} +\skvrobustdef*\skvcsvnormalizeset{\skv@testopt\skv@csvnormalizeset,} +\skvrobustdef*\skv@csvnormalizeset[#1]#2{% + \begingroup + \toks@{}% + \skv@setupnormalizer{#1}% + \skvswitchfalse{inkv}% + \def\do##1##2,{% + \skvifnotnil{##1}{% + \edef##1{\skv@normalizelist{##2}}% + \skvexpanded{\toks@{\the\toks@\skvcmdexit##1}}% + \do + }% + }% + \do#2,\skv@nil,% + \expandafter\endgroup\the\toks@ +} +\skvrobustdef*\skvkvnormalize#1{% + \begingroup + \skv@setupnormalizer{,}% + \skvswitchtrue{inkv}% + % We put '\skv@kvguard' at the end of #1 to preserve any trailing '='. + % This can happen if a key has an empty value. But this can leave + % a trailing space or <parser> in #1. We remove the trailing space + % with \skvtrimspace. + \edef#1{\expandafter\skv@normalizelist\expandafter{#1\skv@kvguard}}% + \def\skv@kvnorm##1\skv@kvguard##2\normal@nil{% + \skv@parserequalerr##1##2,=\normal@nil + \edef#1{\skvtrimspace{##1##2}}% + \skvtrimlastparser{,}#1% + }% + \expandafter\skv@kvnorm#1\normal@nil + \skvaftergroupdef#1\endgroup +} + +\begingroup +\catcode`\~=13 \catcode`\!=13 +\skvrobustgdef\skv@setupnormalizer#1{% + \begingroup + \lccode`\~=`#1 \lccode`\!=`\= + \lowercase{\endgroup + \def\skv@normalizelist##1{% + \unexpanded\expandafter{\romannumeral-`\q + \skv@activeparser#1##1#1~\normal@nil}% + }% + \def\skv@activeparser##1~##2\normal@nil{% + \skvifblank{##2}{% + \skvifswitch{inkv}{% + \skv@activeequal##1!\normal@nil + }{% + \skv@spaceparser##1 #1\normal@nil + }% + }{% + \skv@activeparser##1#1##2\normal@nil + }% + }% + \def\skv@activeequal##1!##2\normal@nil{% + \skvifblank{##2}{% + \skv@spaceparser##1 #1\normal@nil + }{% + \skv@activeequal##1=##2\normal@nil + }% + }% + }% + \def\skv@spaceparser##1 #1##2\normal@nil{% + \skvifblank{##2}{% + \skv@parserspace##1#1 \normal@nil + }{% + \skv@spaceparser##1#1##2\normal@nil + }% + }% + \def\skv@parserspace##1#1 ##2\normal@nil{% + \skvifblank{##2}{% + \skvifswitch{inkv}{% + \skv@spaceequal##1 =\normal@nil + }{% + \skv@doubleparser##1#1#1\normal@nil + }% + }{% + \skv@parserspace##1#1##2\normal@nil + }% + }% + \def\skv@spaceequal##1 =##2\normal@nil{% + \skvifblank{##2}{% + \skv@equalspace##1= \normal@nil + }{% + \skv@spaceequal##1=##2\normal@nil + }% + }% + \def\skv@equalspace##1= ##2\normal@nil{% + \skvifblank{##2}{% + \skv@doubleparser##1#1#1\normal@nil + }{% + \skv@equalspace##1=##2\normal@nil + }% + }% + \def\skv@doubleparser##1#1#1##2\normal@nil{% + \skvifblank{##2}{% + \skvifswitch{inkv}{% + \skv@doubleequal##1==\normal@nil + }{% + \skvifblank{##1}{}{% + \skv@remleadparser##1\normal@nil + }% + }% + }{% + \skv@doubleparser##1#1##2\normal@nil + }% + }% + \def\skv@doubleequal##1==##2\normal@nil{% + \skvifblank{##2}{% + \skvifblank{##1}{}{% + \skv@remleadparser##1\normal@nil + }% + }{% + \skv@doubleequal##1=##2\normal@nil + }% + }% + \def\skv@remleadparser#1##1\normal@nil{% + \expandafter\space\noexpand##1% + }% + \def\skv@parserequalerr##1#1=##2\normal@nil{% + \skvifblank{##2}{}{% + \skv@err{There is '#1=' in your key-value list}\@ehd + }% + }% +} +\endgroup + +% Special slash normalizer, which doesn't replace double slashes with +% single slash, because a double slash might mean an intentionally +% missing token/property of a key: +\begingroup +\catcode`\~=13 \uccode`\~=`\/ +\uppercase{\endgroup + \skvnewdef\skvkeepdoubleslashnormalize#1{% + \unexpanded\expandafter{\romannumeral-`\q + \skv@activeslash#1~\normal@nil}% + }% + \skvnewdef\skv@activeslash#1~#2\normal@nil{% + \skvifblank{#2} + {\skv@spaceslash#1 /\normal@nil} + {\skv@activeslash#1/#2\normal@nil}% + }% +} +\skvnewdef\skv@spaceslash#1 /#2\normal@nil{% + \skvifblank{#2} + {\skv@slashspace#1/ \normal@nil} + {\skv@spaceslash#1/#2\normal@nil}% +} +\skvnewdef\skv@slashspace#1/ #2\normal@nil{% + \skvifblank{#2} + {\skvifblank{#1}{}{\expandafter\space\noexpand#1}} + {\skv@slashspace#1/#2\normal@nil}% +} + + +% \skvmacnormalize[<parser>]<listcmd> +\skvrobustdef*\skvmacnormalize{\skv@testopt\skv@macnormalize,} +\skvrobustdef*\skv@macnormalize[#1]#2{% + \begingroup + \skvsetupmacroparsernormalizer{#1}% + \edef#2{\expandafter\skvmacroparsernormalize\expandafter{#2}}% + \skvaftergroupdef#2\endgroup +} +% This is for normalizing lists like {x\\ y\\\\ z}, where \\ is +% a control sequence. Such parsers may be commands but we don't +% expect them to be active characters. +% Example: +% \skvsetupmacroparsernormalizer{\\} +% \edef\x{\skvmacroparsernormalize{x\\ y\\\\ z}} +\skvrobustdef*\skvsetupmacroparsernormalizer#1{% + \long\def\skvmacroparsernormalize##1{% + \unexpanded\expandafter{\romannumeral-`\q + \skv@mac@spaceparser##1 #1\normal@nil}% + }% + \long\def\skv@mac@spaceparser##1 #1##2\normal@nil{% + \skvifblank{##2} + {\skv@mac@parserspace##1#1 \normal@nil} + {\skv@mac@spaceparser##1#1##2\normal@nil}% + }% + \long\def\skv@mac@parserspace##1#1 ##2\normal@nil{% + \skvifblank{##2} + {\skv@mac@doubleparser##1#1#1\normal@nil} + {\skv@mac@parserspace##1#1##2\normal@nil}% + }% + \long\def\skv@mac@doubleparser##1#1#1##2\normal@nil{% + \skvifblank{##2}{ ##1} + {\skv@mac@doubleparser##1#1##2\normal@nil}% + }% +} +% \skvpushfunctions<base-fn>{<fn-list>}<cnt> +\skvrobustdef*\skvpushfunctions#1#2#3{% + \skvgadvanceno#3\@ne + \begingroup + \def\skv@prova##1{\skvremovescape{##1}@skv@\romannumeral#3}% + \def\do##1{\let\noexpand##1\skvnoexpandcs{\skv@prova{##1}}}% + \skvifcsdef{\skvremovescape{#1}@skv@stack}{}{% + \skvcsdef{\skvremovescape{#1}@skv@stack}{}% + }% + \skvcsxdef{\skvremovescape{#1}@skv@stack}{% + #2{\skvexpandcsonce{\skvremovescape{#1}@skv@stack}}% + }% + \def\do##1{\let\skvnoexpandcs{\skv@prova{##1}}\noexpand##1}% + \skvexpanded{\endgroup#2}% +} +% \skvpopfunctions<base-fn><counter> +\skvrobustdef*\skvpopfunctions#1#2{% + \begingroup + \edef\skv@elt{\skvnoexpandcs{\skvremovescape{#1}@skv@stack}}% + \expandafter\skvifdef\skv@elt{% + \expandafter\ifx\skv@elt\@empty + \skv@err{Stack of '\noexpand#1' is empty}\@ehd + \fi + }{% + \skv@err{Stack of '\noexpand#1' is undefined}\@ehd + }% + \edef\skv@prova{\skvexpandtwice\skv@elt}% + \edef\skv@provb##1##{\endgroup##1\gdef\skvexpandonce\skv@elt}% + \expandafter\skv@provb\skv@prova + \skvgadvanceno#2\m@ne +} + +\chardef\skv@stackdepthlimit=6\relax +% #1=stack (macro), #2=list, #3=max depth +\skvrobustdef*\skvbuildmacrostack#1#2#3{% + \begingroup + \escapechar92\relax + \@ifdefinable#1{\def#1{}}% + \@tempcnta\skvz@ + \def\do##1{% + \noexpand\skv@elt\noexpand##1% + \skvnoexpandcs{\skvremovescape{##1}@skv@\romannumeral\@tempcnta}% + }% + \def\skv@loop{% + \advance\@tempcnta\@ne + % Don't use \ifnum here, because #2 may contain booleans. + \skvifnum\@tempcnta<\numexpr#3+1\relax{% + \edef#1{\skvexpandonce#1.\romannumeral\@tempcnta.{#2}}% + \skv@loop + }{}% + }% + \skv@loop + \skvaftergroupdef#1\endgroup +} +\skvrobustdef*\skvpushstate#1#2{\skv@pushorpopstate{#1}{#2}{+}} +\skvrobustdef*\skvpopstate#1#2{\skv@pushorpopstate{#1}{#2}{-}} +% #1: stack (a macro) as built by \skvbuildmacrostack +% #2: depth counter (macro-defined) +% #3: stack direction (+/-) +\skvrobustdef*\skv@pushorpopstate#1#2#3{% + \if+#3\relax + \skvgadvanceno#2\@ne + \def\skv@elt##1##2{\let##2=##1}% + \else + \ifnum#2<\@ne + \skv@err{Can't pop stack: depth currently less than 1}\@ehd + \else + \def\skv@elt##1##2{\let##1=##2}% + \fi + \fi + \ifnum#2>\skv@stackdepthlimit + \skv@err{Stack depth limit '\number\skv@stackdepthlimit' exceeded}\@ehd + \fi + \def\skv@prova##1{% + \def\skv@prova####1.##1.####2####3\skv@nil{####2}% + \expandafter\skv@prova#1\skv@nil + }% + \expandafter\skv@prova\expandafter{\romannumeral#2}% + \let\skv@elt\relax + \if-#3\skvgadvanceno#2\m@ne\fi +} + +\skvrobustdef*\skvnewbools{\skv@testopt\skv@newbools{}} +\skvrobustdef*\skv@newbools[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdef{if#1##1}{% + \skv@err{Boolean '\skvnoexpandcs{if#1##1}' already exists}\@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \csname newif\expandafter\endcsname\csname if#1##1\endcsname}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} +% \ifskv@inoptionsec determines if we're in skeyval options section or not. +% Some checks are done in that section. + +\skvnewbools[skv@]{% + inset,st,cl,pl,kf,inpox,prepo,tracingkeys,swa,swb,% + intry,insa,success,inoptionsec,inopt,handlerswa,handlerswb% +} + +\skvnewbools{indirkeys} +\skvnewbools[skv]{indef,novalue,valuebraced} +\skvnewbools[dirkeys@]{saveunknownkeys} + +\skvrobustdef*\skvnewcounts{\skv@testopt\skv@newcounts{}} +\skvrobustdef*\skv@newcounts[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdef{#1##1}{% + \skv@err{Counter '\skvnoexpandcs{#1##1}' already exists}\@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \csname newcount\expandafter\endcsname\csname#1##1\endcsname}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} +\skvnewcounts[skv@]{cnta,cntb} + +\skvrobustdef*\skvnewnumbers{\skv@testopt\skv@newnumbers{}} +\skvrobustdef*\skv@newnumbers[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdef{#1##1}{% + \skv@err{Number '\skvnoexpandcs{#1##1}' already exists}\@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \expandafter\def\csname#1##1\endcsname{0}}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} + +\skvnewnumbers[skv@]{keydepth,setkeysdepth,makekeysdepth, + usekeysdepth,trysetkeysdepth,trysetkeysdepth@b,gendepth} +\skvnewnumbers{dirkeys@depth} +\skvnewlet\skv@novalue\relax +\skvnewdef*\skv@novaluetoks{\skv@novalue} + +% A fast key parsing scheme originally designed for \newforeach loop. +% +% \quickkeys.define{<family>}[<hp>]{<list>} +% \quickkeys.set{<family>}[<na>]{<kvlist>} +% +% 1. Quickly defines and set command/store keys. +% 2. Pointers aren't used here, to speed up parsing. +% 3. Package and class options can't be passed via quick keys. +% 4. When defining keys, the syntax of <list> is: +% +% {<key1,key2,etc>}/{<default>}/{<callback>}/{<choice list>}/{<arg>} +% ;...; +% {<key-n>}/{<default>}/{<callback>}/{<choice list>}/{<arg>} +% +% Here, keys key1 and key2 will have the same default value, callback, +% choice list and argument pattern. +% +% 5. The special key '.exec' will simply execute the code <default>, +% without defining anything. +% 6. Use .na to indicate a missing item that isn't blank. +% +% Example: +% \quickkeys.define{fam}[mp@]{% +% keya/true/\def\x##1{#1*##1}/true,false; +% keyb; +% keyc,keyd/aaa+bbb/\def\x##1{#1*##1*#2}/.na/#1+#2; +% } +% +\skvnewdef*\skv@ifrejorempty#1{% + \skvifstrcmp{#1}{^skv^}{% + \@firstoftwo + }{% + \skvifblank{#1}{% + \@firstoftwo + }{% + \@secondoftwo + }% + }% +} +\skvnewdef*\skv@ifrejorna#1{% + \skvifstrcmp{#1}{^skv^}{% + \@firstoftwo + }{% + \skvifstrcmp{#1}{.na}{% + \@firstoftwo + }{% + \@secondoftwo + }% + }% +} +\skvnewdef*\skv@ifrejornaorempty#1{% + \skvifstrcmp{#1}{^skv^}{% + \@firstoftwo + }{% + \skvifstrcmp{#1}{.na}{% + \@firstoftwo + }{% + \skvifblank{#1}{% + \@firstoftwo + }{% + \@secondoftwo + }% + }% + }% +} + +\skvrobustdef*\quickkeys#1#{% + \edef\skv@tempa{\skvtrimspace{#1}}% + \skvxifstrcmp{\skvexpandonce\skv@tempa}{.define}{% + \begingroup\endlinechar\m@ne + \skv@defquickkeys + }{% + \skvxifstrcmp{\skvexpandonce\skv@tempa}{.set}{% + \skv@setquickkeys + }{% + \skv@err{Unknown task for \string\quickkeys}\@ehd + }% + }% +} +\skvrobustdef*\skv@defquickkeys#1{\skv@testopt{\skv@defquickkeys@a{#1}}{}} +\skvrobustdef*\skv@defquickkeys@a#1[#2]#3{% + \endgroup + \edef\skv@tempa{\skvkeepdoubleslashnormalize{#3}}% + \def\skv@defquickkeys@b##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@nil{% + \skvifin{,##1,}{,.exec,.exec code,}{% + ##2\relax + }{% + % Loop over key list, if there are more than one key: + \skvparselist{,}{##1}\skv@tempa{% + % If ##2 is empty, the default will be empty: + \skv@ifrejorna{##2}{}{% + \skvcsedef{#1/\skv@tempa/qkeys.def}{\unexpanded{##2}}% + }% + \skvcsedef{#1/\skv@tempa/qkeys.cbk}{% + % ##3 may be empty: + \skv@ifrejorna{##3}{}{\unexpanded{##3}}% + }% + \skv@ifrejornaorempty{##4}{}{% + \skvcsedef{#1/\skv@tempa/qkeys.chc}{\unexpanded{##4}}% + }% + \skv@ifrejornaorempty{##5}{}{% + \skvcsedef{#1/\skv@tempa/qkeys.arg}{\unexpanded{##5}}% + }% + \skvifblank{#2}{}{% + \skvcsedef{#1/\skv@tempa/qkeys.hp}{\unexpanded{#2}}% + }% + }% + }% + }% + \skvparselist*{;}\skv@tempa\skv@tempa{% + \expandafter\skv@defquickkeys@b\skv@tempa + /^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^\skv@nil + }% +} + +\skvnewnumbers[skv@]{setqkeysdepth} + +% \quickkeys.set{<family>}[<na>]{<kvlist>} + +\skvrobustdef*\skv@setquickkeys#1{\skv@testopt{\skv@setquickkeys@a{#1}}{}} +\skvrobustdef*\skv@setquickkeys@a#1[#2]#3{% + \skvsavestate\skv@setquickkeys{\do\skv@qkeysna}\skv@setqkeysdepth + \edef\skv@qkeysna{\unexpanded{#2}}% + \skvcsvnormalize\skv@qkeysna + \edef\skv@tempa{\unexpanded{#3}}% + \skvkvnormalize\skv@tempa + \def\skv@setqkeys##1##2{% + \skvifcsdef{#1/##1/qkeys.cbk}{}{% + \skv@err{Key '#1/##1' is not defined}\@ehd + }% + \skvifstrcmp{##2}\skv@novalue{% + \skvifcsdef{#1/##1/qkeys.def}{% + \skvcsletcs{#1/##1/qkeys.val}{#1/##1/qkeys.def}% + }{% + \skv@err{No user value and no default + \MessageBreak for key '#1/##1'}\@ehd + }% + }{% + \skvcsedef{#1/##1/qkeys.val}{\unexpanded{##2}}% + }% + \skvifcsdef{#1/##1/qkeys.chc}{% + \skvletcs\reserved@a{#1/##1/qkeys.chc}% + \edef\reserved@b{\skvexpandcsonce{#1/##1/qkeys.val}}% + \skvxifin{,\skvoxdetok\reserved@b,}{,\skvoxdetok\reserved@a,}{}{% + \skv@err{Value '\detokenize{##2}' for key '##1' + \MessageBreak is not in prescribed list + \MessageBreak '\skvoxdetok\reserved@a'}\@ehd + }% + }{}% + \skvifcsdef{#1/##1/qkeys.hp}{% + \skvletcs\reserved@a{#1/##1/qkeys.hp}% + \skvcsletcs{\reserved@a##1}{#1/##1/qkeys.val}% + }{}% + \skvifcsdef{#1/##1/qkeys.arg}{% + \skvletcs\reserved@a{#1/##1/qkeys.arg}% + }{% + \let\reserved@a\skv@simplearg + }% + \skvletcs\reserved@b{#1/##1/qkeys.cbk}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter + \expandafter\reserved@a\expandafter\reserved@a\expandafter + \quickkeyseov\expandafter{\reserved@b}% + \expandafter\expandafter\expandafter + \reserved@a\csname#1/##1/qkeys.val\endcsname\quickkeyseov + }% + \def\skv@setquickkeys@c##1=##2=##3\skv@nil{% + \skvxifin{,\detokenize{##1},}{,\skvoxdetok\skv@qkeysna,}{}{% + \skv@setqkeys{##1}{##2}% + }% + }% + \skvdolist*{,}\skv@tempa\skv@tempa{% + \expandafter\skv@setquickkeys@c\skv@tempa=\skv@novalue=\skv@nil + }% + \skvrestorestate\skv@setquickkeys\skv@setqkeysdepth +} + +% \skvinitializeqkeys{<fam>}[<na>]{<kvlist>} +\skvrobustdef*\skvinitializeqkeys#1{% + \skv@testopt{\skv@initializeqkeys@a{#1}}{}% +} +\skvrobustdef*\skv@initializeqkeys@a#1[#2]#3{% + \skvindeftrue + \skv@setquickkeys{#1}[#2]{#3}% + \skvindeffalse +} + +\input skeyval-for + +% \skvifcase<comparator>{<teststr>} +% {<cases>} +% \elsedo +% {<no match>} +% \endif +% +% Example: +% +% \skvifcase\skvifstrcmp{c} +% {a}{do a} +% {b}{do b} +% {c}{do c} +% \elsedo +% no match% +% \endif +\skvnewdef\skvifcase#1#2#3\elsedo#4\endif{% + \skv@ifcase{#1}{#2}#3{#2}{#4}\skv@casestop +} +\skvnewdef\skv@ifcase#1#2#3{% + #1{#2}{#3}\skv@ifcase@domatch{\skv@ifcase@a{#1}{#2}}% +} +\skvnewdef\skv@ifcase@a#1#2#3{\skv@ifcase{#1}{#2}} +\skvnewdef\skv@ifcase@domatch#1#2\skv@casestop{\unexpanded{#1}} + +% Evaluating a series of boolean expressions. +% +% Example: +% +% \skvifexpr{% +% not ( expr { \skvifdef\xa } and expr { \skvifempty\xa } ) +% or ( expr { \skvifbool{@tempswa} } or expr { \skvifx\xa\xb } ) +% }{% +% \def\x{T} +% }{% +% \def\x{F} +% } +\skvnewnumbers[skv@]{exprcnt} +\skvrobustdef*\skvifexpr#1{% + \begingroup + \def\skv@expr@neg{01}% + \skvsetno\skv@exprcnt\skvz@ + \skv@expr@beg + \skv@expr@bgroup#1(\skv@expr@nil + \skv@expr@end + \skv@expr@end@end +} +\skvrobustdef*\skv@expr@beg{% + \begingroup + \def\skv@expr@neg{01}% + \skv@exprcnt\skvz@ +} +\skvrobustdef*\skv@expr@end{% + \skv@expr@end@end\skv@expr@true\skv@expr@false +} +\skvrobustdef*\skv@expr@end@end{% + \expandafter\endgroup\csname @\ifnum\skv@exprcnt<\skvz@ + second\else first\fi oftwo\endcsname +} +\skvrobustdef*\skv@expr@true{% + \skvadvanceno\skv@exprcnt{\if\skv@expr@neg\m@ne\else\skvz@\fi}% + \def\skv@expr@neg{01}% +} +\skvrobustdef*\skv@expr@false{% + \skvadvanceno\skv@exprcnt{\if\skv@expr@neg\skvz@\else\m@ne\fi}% + \def\skv@expr@neg{01}% +} +\skvrobustdef\skv@expr@bgroup#1(#2\skv@expr@nil{% + \skv@expr@egroup#1)\skv@expr@nil + \skvifblank{#2}{}{% + \skv@expr@beg + \skv@expr@bgroup#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@egroup#1)#2\skv@expr@nil{% + \skv@expr@and#1and\skv@expr@nil + \skvifblank{#2}{}{% + \skv@expr@end + \skv@expr@egroup#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@and#1and#2\skv@expr@nil{% + \skv@expr@or#1or\skv@expr@nil + \skvifblank{#2}{}{% + \skv@exprcnt\ifnum\skv@exprcnt<\skvz@\m@ne\else\skvz@\fi + \skv@expr@and#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@or#1or#2\skv@expr@nil{% + \skv@expr@not#1not\skv@expr@nil + \skvifblank{#2}{}{% + \skv@exprcnt\ifnum\skv@exprcnt<\skvz@\skvz@\else\@ne\fi + \skv@expr@or#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@not#1not#2\skv@expr@nil{% + \skv@expr@do#1expr\skv@expr@nil + \skvifblank{#2}{}{% + \def\skv@expr@neg{00}% + \skv@expr@not#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@do#1expr#2\skv@expr@nil{% + \skvifblank{#1}{}{% + \skv@err{Invalid \noexpand\skvifexpr test expression} + {The handicapped test part is: '\detokenize{#1}'}% + }% + \skvifblank{#2}{}{\skv@expr@do@a#2\skv@expr@nil}% +} +\skvrobustdef\skv@expr@do@a#1#2\skv@expr@nil{% + \ignorespaces#1\skv@expr@true\skv@expr@false + \skv@expr@do#2\skv@expr@nil +} + +\skvnewdef\skvwhileexpr#1\do#2{% + \skvifcond{#1}\fi{#2\skvwhileexpr{#1}\do{#2}}{}% +} + +\skvbuildmacrostack\skv@keystate{% + \do\skv@prefix\do\skvcurrentprefix\do\skv@fams\do\skvcurrentfamily + \do\skvcurrentkey\do\skv@header\do\skvcurrentpath\do\skv@na + \do\skv@naa\do\ifskv@st\do\ifskv@pl\do\ifskv@kf\do\ifskvnovalue + \do\skvcurrentvalue\do\CurrentOption\do\ifskv@intry\do\ifskv@success + \do\ifskv@inpox\do\ifskv@prepo +}\skv@stackdepthlimit + +\skvbuildmacrostack\dirkeys@state{% + \do\dirkeys@prefixlist\do\dirkeys@familylist\do\dirkeys@holderprefixtoks + \do\dirkeys@unknownkeysmacro\do\ifdirkeys@saveunknownkeys + \do\ifindirkeys +}\skv@stackdepthlimit + +\skvrobustdef*\skvappendtomacro{\skv@testst{\skv@appendtomacro{app}{e}}} +\skvrobustdef*\skvgappendtomacro{\skv@testst{\skv@appendtomacro{app}{x}}} +\skvrobustdef*\skvprependtomacro{\skv@testst{\skv@appendtomacro{pre}{e}}} +\skvrobustdef*\skvgprependtomacro{\skv@testst{\skv@appendtomacro{pre}{x}}} +\skvrobustdef\skv@appendtomacro#1#2#3#4{% + \skvifescaped{#3}{% + \@nameuse{#2def}#3{% + \skvifstrcmp{#1}{app}{% + \skvifdef#3{\skvexpandonce#3}{}% + \skvifdefbool{skv@tempst}\skvexpandonce\unexpanded{#4}% + }{% + \skvifdefbool{skv@tempst}\skvexpandonce\unexpanded{#4}% + \skvifdef#3{\skvexpandonce#3}{}% + }% + }% + }{% + \skv@err{Token '\detokenize{#3}' is not escaped}\@ehd + }% +} + +% \skvaddlist{<parser>}{<listitem>} +% This is usually called under \edef. +\skvnewdef*\skvaddlist#1#2{% + \skvifempty#2{}{\skvexpandonce#2\unexpanded{#1}}% +} +% \skvxaddtolist{<parser>}{<listcmd>}{<newitem>} +\skvrobustdef\skvxaddtolist#1#2#3{% + \edef#2{#2\ifx#2\@empty\else#1\fi#3}% +} +% \skvaddtolist[<parser>]{<listcmd>}{<newitem>} +\skvrobustdef*\skvaddtolist{\skv@teststopt{\skv@addtolist{}},} +\skvrobustdef*\skvgaddtolist{\skv@teststopt{\skv@addtolist{\global}},} +\skvrobustdef\skv@addtolist#1[#2]#3#4{% + #1\edef#3{% + \skvifdef#3{% + \skvifempty#3{}{\skvexpandonce#3#2}% + }{}% + \skvifdefbool{skv@tempst}\skvexpandonce\unexpanded{#4}% + }% +} +% \skv@declarefilter{<filter.type>} +\skvrobustdef*\skv@declarefilter#1{% + \edef\skv@filter##1##2{% + \skvifblank{#1}{\let##2=##1}{% + \skvifx#1\relax{\let##2=##1}{% + \skvifstrcmp{#1}\nofilter{\let##2=##1}{% + \unexpanded{#1}{##1}{##2}% + }% + }% + }% + }% +} + +% \skvfiltermergelist!<cmd>{<sublist>}<filter> +% The exclamation mark (!) implies that the outcome is globalized. +% When there is no filter to be applied, \skvmergelist is faster +% than \skvfiltermergelist. +\skvrobustdef*\skvfiltermergelist{\skv@testcl\skv@filtermergelist} +\skvrobustdef*\skv@filtermergelist#1#2#3{% + \begingroup + \skv@declarefilter{#3}% + \skvifdef#1{\skvkvnormalize#1}{\def#1{}}% + \edef\skv@merga{\unexpanded{#2}}% + \skvkvnormalize\skv@merga + \skvcommaloop*\skv@merga\skv@merga{% + \@tempswatrue + \skv@filter\skv@merga\skv@mergb + \let\skv@mergc#1% + \let#1\@empty + \skvcommaloop*\skv@mergc\skv@mergc{% + \skv@filter\skv@mergc\skv@mergd + \ifx\skv@mergb\skv@mergd + \@tempswafalse + \edef#1{\skvaddlist,#1\skvexpandonce\skv@merga}% + \else + \edef#1{\skvaddlist,#1\skvexpandonce\skv@mergc}% + \fi + }% + \if@tempswa + \edef#1{\skvaddlist,#1\skvexpandonce\skv@merga}% + \fi + }% + \skvaftergroupdef#1\endgroup + \ifskv@cl\global\let#1#1\fi +} +% \skvmergelist!<cmd>{<sublist>} +% The exclamation mark (!) implies that the outcome is globalized. +\skvrobustdef*\skvmergelist{\skv@testcl\skv@mergelist} +\skvrobustdef*\skv@mergelist#1#2{% + \begingroup + \skvifdef#1{\skvkvnormalize#1}{\def#1{}}% + \edef\skv@tempa{\unexpanded{#2}}% + \skvkvnormalize\skv@tempa + \skvcommaloop*\skv@tempa\skv@tempa{% + \skvxifin{,\skvoxdetok\skv@tempa,}{,\skvoxdetok#1,}{}{% + \edef#1{\skvaddlist,#1\skvexpandonce\skv@tempa}% + }% + }% + \skvaftergroupdef#1\endgroup + \ifskv@cl\global\let#1#1\fi +} + +% \skvfilterremoveelements!<cmd>{<sublist>}<filter> +% The exclamation mark (!) implies that the outcome is globalized. +% When there is no filter to be applied, \skvremoveelements is faster +% than \skvfilterremoveelements. +% \if@tempswa can be used to test if, at the end, anything was +% removed from #1. +\skvrobustdef*\skvfilterremoveelements{\skv@testcl\skv@filterremoveelements} +\skvrobustdef*\skv@filterremoveelements#1#2#3{% + \begingroup + \let\skv@origlist#1% + \skv@declarefilter{#3}% + \skvifdef#1{\skvkvnormalize#1}{\def#1{}}% + \skvcommaparse{#2}\skv@rema\skv@rema{% + \skv@filter\skv@rema\skv@remb + \let\skv@remc#1\let#1\@empty + \skvcommaloop*\skv@remc\skv@remc{% + \skv@filter\skv@remc\skv@remd + \ifx\skv@remb\skv@remd\else + \edef#1{\skvaddlist,#1\skvexpandonce\skv@remc}% + \fi + }% + }% + \skvexpanded{\endgroup + \skvcmdexit#1% + \ifx#1\@empty + \noexpand\@tempswatrue + \else + \ifx#1\skv@origlist\noexpand\@tempswafalse\else + \noexpand\@tempswatrue\fi + \fi + }% + \ifskv@cl\global\let#1#1\fi +} +% \skvremoveelements!<cmd>{<sublist>} +% The exclamation mark (!) implies that the outcome is globalized. +% \if@tempswa can be used to test if, at the end, anything was +% removed from #1. +\skvrobustdef*\skvremoveelements{\skv@testcl\skv@removeelements} +\skvrobustdef\skv@removeelements#1#2{% + \begingroup + \let\skv@origlist#1% + \skvifdef#1{}{\def#1{}}% + \edef\skv@rema{\unexpanded{#2}}% + \skvcsvnormalize\skv@rema + \let\skv@remb#1\let#1\@empty + \skvcommaparse*\skv@remb\skv@tempa{% + \skvxifin{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@rema,}{}{% + \edef#1{\skvaddlist,#1\skvexpandonce\skv@tempa}% + }% + }% + \skvexpanded{\endgroup + \skvcmdexit#1% + \ifx#1\@empty + \noexpand\@tempswatrue + \else + \ifx#1\skv@origlist\noexpand\@tempswafalse\else + \noexpand\@tempswatrue\fi + \fi + }% + \ifskv@cl\global\let#1#1\fi +} + +% \skvfilterreplaceelements!<listcmd>{<replacements>}<filter> +% <replacements> => {<old-1>}{<new-1>},...,{<old-n>}{<new-n>} +% An exclamation mark suffix (!) implies that the outcome is globalized. +% When there is no filter to be applied, \skvreplaceelements is only +% negligibly faster than \skvfilterreplaceelements. Hence \skvreplaceelements +% isn't defined in this package. When there is no <filter>, use \nofilter +% as the filter. +% Example: +% \def\cmd{a = 1 , b = 2 , c = 3} +% \skvfilterreplaceelements\cmd{{a = 1}{x = 1},{c = 3}{z=3}}\skv@getkeyname +\skvrobustdef*\skvfilterreplaceelements{\skv@testcl\skv@filterreplaceelements} +\skvrobustdef*\skv@filterreplaceelements#1#2#3{% + \begingroup + \skv@declarefilter{#3}% + \skvifdef#1{\skvkvnormalize#1}{\def#1{}}% + \def\do##1##2\skv@nil{% + \ifx#3\skv@getkeyname + \def\@do####1=####2=####3\skv@nil####4{% + \edef####4{\skvtrimspace{####1}=\skvtrimspace{####2}}% + }% + \@do##1==\skv@nil\skv@old + \@do##2==\skv@nil\skv@new + \else + \edef\skv@old{\unexpanded{##1}}% + \edef\skv@new{\unexpanded{##2}}% + \fi + }% + \skvcommaparse{#2}\skv@tempa{% + \expandafter\do\skv@tempa\skv@nil + \skv@filter\skv@old\skv@oldb + \let\skv@tempb#1\let#1\@empty + \skvcommaloop*\skv@tempb\skv@tempb{% + \skv@filter\skv@tempb\skv@tempc + \ifx\skv@tempc\skv@oldb + \edef#1{\skvaddlist,#1\skvexpandonce\skv@new}% + \else + \edef#1{\skvaddlist,#1\skvexpandonce\skv@tempb}% + \fi + }% + }% + \skvaftergroupdef#1\endgroup + \ifskv@cl\global\let#1#1\fi +} +\skvrobustdef*\skv@makeprefix#1{% + % It is required to first fully expand the prefix: + \edef\skvcurrentprefix{#1}% + \skvdespacecontent\skvcurrentprefix + \edef\skv@prefix{% + \ifx\skvcurrentprefix\@empty\else\skvcurrentprefix/\fi + }% +} +\skvrobustdef*\skv@makeheader#1{% + % Fully expand the family, in case it is given as \skvcurrentfamily or + % a local/temporary macro: + \edef\skvcurrentfamily{#1}% + \skvdespacecontent\skvcurrentfamily + \edef\skvcurrentpath{\skv@prefix\skvcurrentfamily}% + \edef\skv@header{% + \skv@prefix\ifx\skvcurrentfamily\@empty\else\skvcurrentfamily/\fi + }% +} +\skvrobustdef*\skv@testopta#1{% + \skvifstar + {\skv@sttrue\skv@t@stopta{#1}}{\skv@stfalse\skv@t@stopta{#1}}% +} +\skvrobustdef*\skv@t@stopta#1{\skvifplus{\skv@pltrue#1}{\skv@plfalse#1}} +\skvrobustdef*\skv@testoptb#1{\skv@testopt{\skv@t@stoptb{#1}}{KV}} +\skvrobustdef*\skv@t@stoptb#1[#2]#3{% + \skv@makeprefix{#2}\skv@makeheader{#3}#1% +} +\skvrobustdef*\skv@testoptc#1{\skv@testopt{\skv@t@stoptc{#1}}{KV}} +\skvrobustdef*\skv@t@stoptc#1[#2]#3{% + \skvxifin{,}{\detokenize{#2}}{% + \skv@err{Only one prefix is allowed here, + \MessageBreak but you gave '#2'}\@ehd + }{% + \skv@makeprefix{#2}% + \edef\skv@fams{#3}% + \skvxifin{,}{\detokenize{#3}}{% + \skvcsvnormalize\skv@fams + }{% + \skvdespacecontent\skv@fams + }% + \skv@testopt#1{}% + }% +} +\skvrobustdef*\skv@testoptd#1#2{% + \skv@testoptb{% + \edef\skv@provb{#2\skv@header}% + \def\skv@prova{\skv@testopt{\skv@t@stoptd{#1}}}% + \expandafter\skv@prova\expandafter{\skv@provb}% + }% +} +\skvrobustdef*\skv@t@stoptd#1[#2]#3{% + \skvifnextchar[{\skv@sttrue#1{#2}{#3}}{\skv@stfalse#1{#2}{#3}[]}% +} +\skvrobustdef*\skv@getkeyname#1#2{% + \expandafter\skv@g@tkeyname#1=\skv@nil#2% +} +\skvrobustdef*\skv@g@tkeyname#1=#2\skv@nil#3{% + \skv@strippointers{#1}\skvcurrentkey + \let#3=\skvcurrentkey +} + +% #1=key-value pairs; #2=result macro +\skvrobustdef*\skv@getkeynames#1#2{% + \begingroup + \edef\skv@prova{\unexpanded{#1}}% + \skvkvnormalize\skv@prova + \let#2\@empty + \skvcommaloop*\skv@prova\skv@prova{% + \expandafter\expandafter\expandafter + \skv@g@tkeyname\skv@prova=\skv@nil\skv@prova + \skvaddtolist*#2\skv@prova + }% + \skvaftergroupdef#2\endgroup +} +\skvnewdef*\skv@badkeynames{} +\skvnewdef*\skvaddbadkeynames{\skvappendtomacro\skv@badkeynames} +\skvnewdef\skvcurrenttriple{% + \skvcurrentprefix/\skvcurrentfamily/\skvcurrentkey +} +\skvrobustdef*\skv@definedefault#1#2{% + \ifskv@inoptionsec\else + \skvxifin{,\skvcurrentprefix,}{,skv,SKV,}{% + \skvxifin{,\skvcurrentfamily,}{,skeyval,}{% + \skv@err{Prefix/family '\skvcurrentprefix/\skvcurrentfamily' + are reserved}\@ehd + }{}% + }{}% + \fi + \ifskv@tracingkeys + \skvxifin{,#1,}{,\skv@badkeynames,}{% + \skv@err{Key name '#1' not allowed}\@ehd + }{}% + \fi + \skvcsedef{\skv@header#1.@defa}{% + \skvnoexpandcs{\skv@header#1}{\unexpanded{#2}}% + }% + \skv@recordkey{#1}{#2}% +} + +% 1. Record key and its initial/default value if the key appears in the +% 'needini' list of its family. The list 'needini' is made by the +% command \skvsaveinitialvaluekeys. +% 2. To 'save initial values of keys', the key names must have been +% entered by \skvsaveinitialvaluekeys in 'needini' list. +% 3. If a key has no default, then \skv@definedefault will not be called +% and hence \skv@recordkey won't be invoked. +% 4. In \skvdefinekeys, if a key has no default, it won't be entered in +% the 'needini' list. In \skvdefinekeys, an empty default value should +% be indicated with a slash, eg, .cmd/keya/, .ord/keyb//, +% or .ord/keyb/.na/. +% 5. In \skvdefinekeys, use the key/option '.save initial values' to +% create 'needini' lists of families. +\skvrobustdef*\skv@recordkey#1#2{% + \begingroup + \skvifcsundef{\skv@header.@needini}{% + \endgroup\skv@swafalse + }{% + \skv@swatrue + \skvxifin{,#1,}{,\skvexpandcsonce{\skv@header.@needini},}{% + \edef\skv@tempa{\skvtrimspace{#2}}% + \edef\skv@tempa{% + #1=\skvoifstrcmp{\skv@tempa}{true}{false}{\skvexpandonce\skv@tempa}% + }% + \skvletcs\skv@tempb{\skv@header.@inikv}% + \skvifdef\skv@tempb{% + \skvxifin{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@tempb,}{}{% + \skvcsedef{\skv@header.@inikv}{% + \skvaddlist,\skv@tempb\skvexpandonce\skv@tempa + }% + }% + }{% + \skvcsedef{\skv@header.@inikv}{\skvexpandonce\skv@tempa}% + }% + }{}% + }% + \ifskv@swa + \expandafter\skvaftergroupdef\csname + \skv@header.@inikv\expandafter\endcsname + \expandafter\endgroup + \fi +} + +% Key will be given in the form <pointer>{<key>}. The command +% \skv@strippointers is called both when defining and setting keys. When +% setting keys, we also call \skv@replacepointers to replace .usevalue +% pointer with the value of the key. +% +% Notes: +% 1. The pointer \savevalue (of ltxkeys package) or .save value is +% not used in the skeyval package, since the key's value is +% always saved automatically. +% +\skvrobustdef*\skv@strippointers#1#2{% + \begingroup + \edef#2{% + \skvifstrcmp{#1}{\skvcurrentkey} + {\skvcurrentkey}{\skvtrimspace{#1}}% + }% + \def\skv@prova{\expandafter\skv@findpointer\expandafter}% + % We can't use #1 in place of #2 in the following, since + % #2 may have had some pointers stripped in the last call + % to \skv@findpointer. Also, we can't \edef #2 here, since + % after each call to \skv@findpointer, we want to use the latest + % meaning of #2. + \def\do{}% + \skv@prova{#2}{.need value}#2{% + \def\do{\skvcsdef{\skv@header#2.@ndv}{00}}% + }{% + \skv@prova{#2}{.require value}#2{% + \def\do{\skvcsdef{\skv@header#2.@ndv}{00}}% + }{% + \skv@prova{#2}{.value required}#2{% + \def\do{\skvcsdef{\skv@header#2.@ndv}{00}}% + }{% + \skv@prova{#2}{.forbid value}#2{% + \def\do{\skvcsdef{\skv@header#2.@fbv}{00}}% + }{% + \skv@prova{#2}{.value forbidden}#2{% + \def\do{\skvcsdef{\skv@header#2.@fbv}{00}}% + }{% + \skv@prova{#2}{.expanded}#2{% + \edef#2{#2}% + }{% + \skv@prova{#2}{.expand once}#2{% + \edef#2{\skvexpandtwice{#2}}% + }{% + \skv@prova{#2}{.expand twice}#2{% + \edef#2{\skvexpandthrice{#2}}% + }{}% + }% + }% + }% + }% + }% + }% + }% + \skvexpanded{\endgroup + \skvcmdexit{#2}\do + }% +} +\skvrobustdef*\skv@findpointer#1#2#3{% + \begingroup + \def\skv@prova##1#2##2##3\pointer@nil{% + \endgroup + \skvifstrcmp{##2}{\skv@nil}{% + \@secondoftwo + }{% + \edef#3{\skvtrimspace{##2}}% + \@firstoftwo + }% + }% + \skv@prova#1#2{\skv@nil}\pointer@nil +} + +% Extract pseudo key name from a weird key name of the form +% 'evaluate (\x) as (\y) using (\numexpr\x*2)': +\skvrobustdef*\skv@extractpntkey#1#2{% + \begingroup + \@tempcnta\skvz@ + \def#2{}% + \def\skv@prova##1(##2){% + \def\skv@provb{##2}% + \ifx\skv@provb\@nnil\else + \advance\@tempcnta\@ne + \edef#2{\skvexpandonce#2\skvtrimspace{##1}}% + \expandafter\skv@prova + \fi + }% + \skv@prova#1(\@nil)% + \in@false + \ifx#2\@empty + \edef#2{\unexpanded{#1}}% + \else + \skvifstreq{#2}\with{\skvcurrentkey}\then + \in@true + % The number of arguments of the pseudo key is determined by the + % number of closed parentheses: + \skvcsedef{\skv@header#2.@x0arg}{% + \skvgenerateparameters{1}\@tempcnta + }% + \fi + \fi + \skvexpanded{\endgroup + \skvcmdexit#2% + \ifin@ + \expandafter\skvcmdexit\csname\skv@header#2.@x0arg\endcsname + \fi + }% +} +\skvrobustdef*\skv@extr@ctpntkey#1#2#3{% + \begingroup + \let\skv@tvalue#3% + \def#2{}\def#3{}% + \def\skv@prova##1(##2){% + \def\skv@provb{##2}% + \ifx\skv@provb\@nnil\else + \edef#2{\skvexpandonce#2\skvtrimspace{##1}}% + \edef#3{\skvexpandonce#3{\skvtrimspace{##2}}}% + \expandafter\skv@prova + \fi + }% + \skv@prova#1(\@nil)% + \ifx#2\@empty + \edef#2{\unexpanded{#1}}% + \let#3\skv@tvalue + \fi + \skvexpanded{\endgroup\skvcmdexit#2\skvcmdexit#3}% +} +\skvrobustdef*\skv@replacepointers#1{% + \begingroup + \let\skvcurrentvalue\@empty + \let\skv@tempa\@empty + \skvifbraced{#1}{% + \skv@r@placepointers{#1}.use value\skv@nil + }{% + \skv@r@placepointers#1.use value\skv@nil + }% + \skvaftergroupdef\skvcurrentvalue\endgroup +} +% In xkeyval package, if we put, eg, +% +% key2=\dimexpr\usevalue{key1}*5\relax +% +% \dimexpr will be lost (as part of the value of key2) in pointer +% replacement. We avoid that deficiency here. +% +\skvrobustdef*\skv@r@placepointers#1.use value#2{% + \edef\skv@prova{\unexpanded{#2}}% + \skvifx\skv@prova\skv@nnil{% + \edef\skvcurrentvalue{\unexpanded{#1}}% + }{% + \skvifcsundef{\skv@header#2.@value}{% + \skv@err{No value recorded for key '#2'; ignored}\@ehd + \skv@r@placepointers + }{% + \skvxifin{,\detokenize{#2},}{,\skvoxdetok\skv@tempa,}{% + \skv@err{Back linking of pointers; + \MessageBreak pointer replacement canceled}\@ehd + }{% + \edef\skv@tempa{\skvaddlist,\skv@tempa\unexpanded{#2}}% + \edef\skv@prova{\skvexpandcsonce{\skv@header#2.@value}}% + \skvexpandbracenext\skvifbraced\skv@prova{% + \skvexpandbracenext{\skv@r@placepointers#1}\skv@prova + }{% + \skvexpandnext{\skv@r@placepointers#1}\skv@prova + }% + }% + }% + }% +} + +% Assign arguments to keys: +% +% \skvassignargs[<prefixes>]{<families>}[<expansion.type>]{<keys>}{<arg>} +% +% 1. Valid expansion types are x0 (unexpanded), x1 (expand once), +% x2 (expand twice), xx (expand fully). The default expansion type is +% x0 (unexpanded). +% 2. At set keys (not at define keys), the argument to the listed keys will +% be expanded using the expansion type. +% 3. The argument will be assigned whether or not a key already has been +% defined. This allows the user to define the key later, after its +% argument has been fixed. +% 3. Under the macros \skvdefinekeys and \directkeys, there are equally +% efficient handlers for assigning arguments to keys. +% +% Example: +% +% \skvassignargs[KV1,KV2]{fam1,fam2}[x1]{keya,keyb}{#1/#2} +% \skvsetkeys[KV1]{fam2}{keya=vala/valb} +% +% For each prefix and family, this will define parameterless functions +% +% \<prefix>/<family>/key/.@x1arg. +% +% which will be used at setkeys to define and execute the key's callback. +% +\skvrobustdef*\skvassignargs{\skv@testopt\skv@assignargs{KV}} +\skvrobustdef*\skv@assignargs[#1]#2{% + \begingroup + \skv@testopt{\skv@assignargs@a{#1}{#2}}{^skv^}% +} +\skvrobustdef*\skv@assignargs@a#1#2[#3]#4#5{% + \edef\skv@tempc{\skvtrimspace{#5}}% + \def\skv@tempd{}% + \skvifstrcmp{#3}{^skv^}{% + \def\skvexpander{x0}% + }{% + \skv@checkexpander{#3}% + \edef\skvexpander{\skvtrimspace{#3}}% + }% + \def\\{\noexpand\unexpanded}% + \skvcommaloop{#1}\skv@tempa{% + \skv@makeprefix\skv@tempa + \skvcommaloop{#2}\skv@tempa{% + \skv@makeheader\skv@tempa + \skvcommaparse{#4}\skv@tempa{% + \edef\skv@tempd{\skvexpandonce\skv@tempd + \skvcsedef{\skv@header\skv@tempa.@\skvexpander arg}{% + \\{\\{\skvexpandonce\skv@tempc}}% + }% + }% + }% + }% + }% + \skvexpanded{\endgroup\skv@tempd}% +} +\skvrobustdef*\skv@checkexpander#1{% + \skvxifin{,\expandafter\skvtrimspace\expandafter + {\detokenize\expandafter{#1}},}{,\detokenize{x0,x1,x2,xx},}{}{% + \skv@err{Illegal expansion type '#1'}\@ehd + }% +} + +% Ordinary keys: +% +% \skvordkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +\skvrobustdef*\skvordkey{\skv@testoptb\skv@ordkey} +\skvrobustdef*\skv@ordkey#1{% + \skv@strippointers{#1}\skvcurrentkey + \skvexpanded{\skv@testopt{\skv@ordkey@a{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@ordkey@a#1[#2]#3{% + \skvifstrcmp{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}% + \skvcsedef{\skv@header#1.@cbk}{\unexpanded{#3}}% +} +\skvrobustdef*\skvordkeys{\skv@testoptb\skv@ordkeys} +\skvrobustdef*\skv@ordkeys#1{\skv@testopt{\skv@ordkeys@a{#1}}{^skv^}} +\skvrobustdef*\skv@ordkeys@a#1[#2]#3{% + \skvcommaparse{#1}\skv@tempa{% + \skvexpbracenext\skv@strippointers\skv@tempa\skvcurrentkey + \skvexpanded{\skv@ordkey@a{\skvcurrentkey}}[#2]{#3}% + }% +} + +% \skvvoidkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +% A void key is an invalid key. A user who tries to set that key will +% automatically be told that the key is inadmissible. The author of an +% invalid key can also use <callback> to set a message. +% +\skvrobustdef*\skvvoidkey{\skv@testoptb\skv@voidkey} +\skvrobustdef*\skv@voidkey#1{% + \skv@strippointers{#1}\skvcurrentkey + \skvexpanded{\skv@testopt{\skv@voidkey@a{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@voidkey@a#1[#2]#3{% + \skvifstrcmp{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}% + % Simply inserting '#3' in the next definition will not work because + % of the possible parameter characters in #3: + \edef\skv@tempa##1##2{% + ##2% + \skv@err{Key '#1' of family '\skvcurrentprefix/\skvcurrentfamily' + \MessageBreak has been set invalid by its author}\noexpand\@ehd + }% + \skv@toksa\expandafter{\skv@tempa{##1}{#3}}% + \skvcsedef{\skv@header#1.@cbk}{\the\skv@toksa}% +} + +%% Key names with parenthesized tokens. +% +% \skvpntkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +% The tokens in parenthesis are removed when building the key name. +% Each parenthesis gives rise to one argument of the key's callback. +% When the key is set, the tokens in parenthesis are assigned to the +% arguments. It is the user's responsibility to catch those argument +% in the key's callback. +% +% Example - a key of three arguments: +% +% \skvpntkey[KV]{fam}{evaluate (1) as (2) using (3)}{% +% \def\oldcmd{#1}\def\newcmd{#2}\def\formula{#3}% +% \def\x##1{#1*##1}% +% } +% +% \skvpntkey[KV]{fam}{evaluate (1) as (2) using (3)}[% +% \x\y{\numexpr\x*2} +% ]{% +% \edef\x#2{#3}% +% } +% +% \skvshowcs{KV/fam/evaluateasusing/.@x0arg} +% +% \skvsetkeys[KV]{fam}{evaluate (\x) as (\y) using (\numexpr\x*2)} +% +\skvrobustdef*\skvpntkey{\skv@testoptb\skv@pntkey} +\skvrobustdef*\skv@pntkey#1{% + \skv@strippointers{#1}\skvcurrentkey + \skvexpbracenext\skv@extractpntkey\skvcurrentkey\skvcurrentkey + \skvexpanded{\skv@testopt{\skv@pntkey@a{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@pntkey@a#1[#2]#3{% + \skvifstrcmp{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}% + \skvcsedef{\skv@header#1.@cbk}{\unexpanded{#3}}% +} + +%% Command keys: +% +% \skvcmdkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% +\skvrobustdef*\skvcmdkey{% + \def\skv@zapnr{0}% + \skv@testoptd\skv@cmdkey{cmd}% +} + +% \skvzcmdkey will internally zap the spaces in the key's name, but the +% key user will not notice that: he can still use the key's name with +% spaces in it. The key's author could then use the zapped name internally +% in his code. +\skvrobustdef*\skvzcmdkey{% + \def\skv@zapnr{1}% + \skv@testoptd\skv@cmdkey{cmd}% +} +% When the key's value has doubled hash characters, xkeyval's definition +% of \skv@cmdkey fails: +\skvrobustdef*\skv@cmdkey#1#2[#3]#4{% + \skv@strippointers{#2}\skvcurrentkey + \skvifdefbool{skv@st}{\skv@definedefault\skvcurrentkey{#3}}{}% + % This is to allow the key name to be expanded before possibly zapping + % spaces in key name: + \edef\skv@prova{#1\skvcurrentkey}% + \edef\skv@prova{\expandafter\skv@zapornot\expandafter + {\expandafter\skv@zapnr\expandafter}\expandafter{\skv@prova}}% + \skvcslet{\skv@header\skvcurrentkey.@hp}\skv@prova + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{\unexpanded{#4}}% +} +% \skvcmdkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>} +\skvrobustdef*\skvcmdkeys{% + \def\skv@zapnr{0}% + \skv@testoptd\skv@cmdkeys{cmd}% +} +\skvrobustdef*\skvzcmdkeys{% + \def\skv@zapnr{1}% + \skv@testoptd\skv@cmdkeys{cmd}% +} +\skvrobustdef*\skv@cmdkeys#1#2[#3]#4{% + \skvcommaparse{#2}\skv@tempa{% + \skvexpanded{\skv@cmdkey{#1}{\skv@tempa}}[#3]{#4}% + }% +} + +%% Choice keys: +% +% \skvchoicekey[<pref>]{<fam>}[<mp>]{<key>}[<bins>]{<choices>} +% [<defa>]{<callback>} +% +% The skeyval package allows holder-macro prefixes for choice keys. +\skvrobustdef*\skvchoicekey{% + \skv@testopta{\skv@testoptb + {\skv@testopt\skv@choicekey@a{choice\skv@header}}}% +} +\skvrobustdef*\skv@choicekey@a[#1]#2{% + \skv@strippointers{#2}\skvcurrentkey + \skvcsedef{\skv@header\skvcurrentkey.@hp}{% + \skvtrimspace{#1}\skvcurrentkey + }% + \skv@testopt\skv@choicekey@b{}% +} +\skvrobustdef*\skv@choicekey@b[#1]#2{% + \def\skv@bins{#1}% + \edef\skv@tempa{\unexpanded{#2}}% + \skvcsvnormalize\skv@tempa + \skv@toksa\expandafter{\skv@tempa}% + \skvifnextchar[\skv@choicekey@c{\skv@choicekey@c[^skv^]}% +} +\skvrobustdef*\skv@choicekey@c[#1]{% + \skvifstrcmp{#1}{^skv^}{}{\skv@definedefault\skvcurrentkey{#1}}% + \skvifdefbool{skv@pl}\skv@choicekey@e\skv@choicekey@d +} +\skvrobustdef*\skv@choicekey@d#1{\skv@choicekey@f{{#1}}} +\skvrobustdef*\skv@choicekey@e#1#2{\skv@choicekey@f{{#1}{#2}}} +\skvrobustdef*\skv@choicekey@f#1{% + \edef\skv@tempa##1##2{% + \ifskv@st\noexpand\skv@sttrue\else\noexpand\skv@stfalse\fi + \ifskv@pl\noexpand\skv@pltrue\else\noexpand\skv@plfalse\fi + \skv@checkchoice[\skvexpandonce\skv@bins]{##1}{\the\skv@toksa}##2% + }% + \skv@toksa\expandafter{\skv@tempa{##1}{#1}}% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{\the\skv@toksa}% +} + +%% Boolean keys: +% +% \skvboolkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% +% Boolean keys create a store to receive the key's value and also create +% a boolean internally. +\skvrobustdef*\skvboolkey{% + \def\skv@zapnr{0}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +% \skvzcmdkey will internally zap the spaces in the key's name and in +% the boolean that it creates internally, but the key user will not +% notice that: he will still be able to submit the original key's name +% with spaces in it. The key's author could then use the zapped key +% name and boolean in his code (internally). +\skvrobustdef*\skvzboolkey{% + \def\skv@zapnr{1}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +\skvrobustdef*\skv@boolkey#1#2[#3]{% + \skv@strippointers{#2}\skvcurrentkey + \skvexpanded{% + \ifskv@pl\skv@boolkey@b\else\skv@boolkey@a\fi + {\skvcurrentkey}{#1\skvcurrentkey}% + }{#3}% +} +\skvrobustdef*\skv@boolkey@a#1#2#3#4{% + \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}% + \expandafter\skv@tempa\expandafter{\expandafter{\expandafter + \csname\expandafter\skv@zapornot\expandafter{\skv@zapnr}{#2}% + \skv@value\endcsname#4}}% +} +\skvrobustdef*\skv@boolkey@b#1#2#3#4#5{% + \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}% + \expandafter\skv@tempa\expandafter{\expandafter{\expandafter + \csname\expandafter\skv@zapornot\expandafter{\skv@zapnr}{#2}% + \skv@value\endcsname#4}{#5}}% +} +\skvrobustdef*\skv@boolkey@c#1#2#3#4{% + \skvcsnewif{\skv@zapornot{\skv@zapnr}{#2}}% + \skvifdefbool{skv@st}{\skv@definedefault{#1}{#3}}{}% + \edef\skv@tempa##1##2{% + \noexpand\skv@sttrue + \ifskv@pl\noexpand\skv@pltrue\else\noexpand\skv@plfalse\fi + \skv@checkchoice[\noexpand\skv@value]{##1}{true,false}##2% + }% + \skv@toksa\expandafter{\skv@tempa{##1}{#4}}% + \skvcsedef{\skv@header#1.@cbk}{\the\skv@toksa}% +} +% \skvboolkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>} +\skvrobustdef*\skvboolkeys{% + \def\skv@zapnr{0}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +\skvrobustdef*\skvzboolkeys{% + \def\skv@zapnr{1}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +\skvrobustdef*\skv@boolkeys#1#2[#3]{% + \skvifdefbool{skv@pl}\skv@boolkeys@b\skv@boolkeys@a{#2}{#1}{#3}% +} +\skvrobustdef*\skv@boolkeys@a#1#2#3#4{\skv@boolkeys@c{#1}{#2}{#3}{#4}{^skv^}} +\skvrobustdef*\skv@boolkeys@b#1#2#3#4#5{\skv@boolkeys@c{#1}{#2}{#3}{#4}{#5}} +\skvrobustdef*\skv@boolkeys@c#1#2#3#4#5{% + \skvcommaparse{#1}\skv@tempa{% + \skvexpbracenext\skv@strippointers\skv@tempa\skvcurrentkey + \skvexpanded{% + \skvifstrcmp{#5}{^skv^}{% + \skv@boolkey@a{\skvcurrentkey}% + {#2\skvcurrentkey}\unexpanded{{#3}{#4}}% + }{% + \skv@boolkey@b{\skvcurrentkey}% + {#2\skvcurrentkey}\unexpanded{{#3}{#4}{#5}}% + }% + }% + }% +} + +% Style keys +% +% \skvappendstyles[<pref>]{<fam>}{<keys>}{<styles>}[<defa>]{<callback>} +% \skvprependstyles[<pref>]{<fam>}{<keys>}{<styles>}[<defa>]{<callback>} +% +% 1. Style keys will be created if they didn't exist. If they existed, +% (a) their default values will be overwritten if the option [<defa>] +% appears in the above syntax; (b) their callbacks will be updated with +% <callback> and <styles>. +% +% 2. If desired, use pointers/handlers .expanded, .expand once, .expand twice +% on values of styles. +% +% Example: +% +% \skvordkeys[KV]{fam}{keya,keyb}{\def\x##1{#1*##1}} +% \skvappendstyles[KV]{fam}{keyd,keye}{keya=.expanded{\vala},keyb=valb} +% [def-d&e]{\def\y##1{#1*##1}} +% +\skvrobustdef*\skvappendstyles{% + \def\skv@type{app}% + \def\skvexpanderpointer{}% + \skv@testoptb\skv@appendstyles +} +\skvnewlet\skvstylekeys\skvappendstyles +\skvrobustdef*\skvprependstyles{% + \def\skv@type{prep}% + \def\skvexpanderpointer{}% + \skv@testoptb\skv@appendstyles +} +\skvrobustdef*\skvappendstylesexpanded{% + \def\skv@type{app}% + \def\skvexpanderpointer{.expanded}% + \skv@testoptb\skv@appendstyles +} +\skvnewlet\skvstylekeysexpanded\skvappendstylesexpanded +\skvrobustdef*\skvprependstylesexpanded{% + \def\skv@type{prep}% + \def\skvexpanderpointer{.expanded}% + \skv@testoptb\skv@appendstyles +} +\skvrobustdef*\skv@appendstyles#1#2{% + \skv@testopt{\skv@appendstyles@a{#1}{#2}}{^skv^}% +} +% #2: styles, #3: default, #4: callback +\skvrobustdef*\skv@appendstyles@a#1#2[#3]#4{% + \skvcommaparse{#1}\skv@prova{% + \skvexpbracenext\skv@strippointers\skv@prova\skvcurrentkey + \skvexpanded{% + \skvifstrcmp{#3}{^skv^}{}{% + \skv@definedefault{\skvcurrentkey}{\unexpanded{#3}}% + }% + \skv@appendstyles@b{\skvcurrentkey}\unexpanded{{#2}{#4}}% + }% + }% +} +% \skv@appendstyles@b{<parent.key>}{<styles>}{<parent.key.callback>} +\skvrobustdef*\skv@appendstyles@b#1#2#3{% + \def\skv@stylelist{}% + \skvkvparse{#2}\skv@provb{% + \def\skv@prova##1=##2=##3\skv@nil{% + \skvxifstrcmp{#1}{##1}{% + \skv@err{Linking key '##1' to itself}\@ehd + }{% + \skvifcsdef{\skv@header##1.@cbk}{}{% + \skv@err{Style key '##1' is undefined}\@ehd + }% + }% + \edef\skv@stylelist{% + \skvaddlist,\skv@stylelist\unexpanded{##1}=% + \skvexpandonce\skvexpanderpointer{\unexpanded{##2}}% + }% + }% + \expandafter\skv@prova\skv@provb==\skv@nil + }% + \edef\elt{% + \skvifcsdef{\skv@header#1.@cbk} + {\skvxpandcsonce{\skv@header#1.@cbk}}{}% + }% + \edef\skv@elt{% + \noexpand\skvexpanded{\noexpand\skvsetkeys[\skvcurrentprefix]% + {\skvcurrentfamily}{\skvexpandonce\skv@stylelist}}% + }% + \skvcsedef{\skv@header#1.@cbk}{% + \skvxifstrcmp{\skv@type}{prep}{% + \skvexpandonce\skv@elt\unexpanded{#3}\skvexpandonce\elt + }{% + \skvexpandonce\elt\unexpanded{#3}\skvexpandonce\skv@elt + }% + }% +} +% +% \skvaddstyles[<pref>]{<fam>}{<keys>}{<prepend>}{<append>}[<defa>]{<callback>} +% +% Example: +% \skvordkeys[KV]{fam}{keya1,keyb1,keya2,keyb2}{\def\x##1{#1*##1}} +% \skvaddstyles[KV]{fam}{keyd,keye}{keya1=vala,keyb1=valb} +% {keya2=vala,keyb2=.expanded{\valb}}[def-d&e]{\def\y##1{#1*##1}} +% +\skvrobustdef*\skvaddstyles{% + \def\skvexpanderpointer{}% + \skv@testoptb\skv@addstyles +} +\skvrobustdef*\skv@addstyles#1#2#3{% + \skv@testopt{\skv@addstyles@a{#1}{#2}{#3}}{^skv^}% +} +% #2: prepended styles +% #3: appended styles +% #4: default +% #5: callback +\skvrobustdef*\skv@addstyles@a#1#2#3[#4]#5{% + \skvexpanded{% + \skvprependstyles[\skvcurrentprefix]{\skvcurrentfamily}% + \unexpanded{{#1}{#2}[#4]{#5}}% + \skvappendstyles[\skvcurrentprefix]{\skvcurrentfamily}% + \unexpanded{{#1}{#3}[#4]{#5}}% + }% +} + +%% Checking the admissible values of keys: +% +\skvrobustdef*\skvcheckchoice{\skv@testopta{\skv@testopt\skv@checkchoice{}}} +\skvrobustdef*\skv@checkchoice[#1]#2#3{% + \begingroup + \edef\skv@tempa{\skvtrimspace{#2}}% + \def\skv@tempb{#3}% + \skvcsvnormalize\skv@tempb + \skvexpanded{% + \ifskv@st\lowercase{\fi + \ifcat$\detokenize{#1}$% + \skv@checkchoice@b\skv@nil + {\skvexpandonce\skv@tempa}{\skvexpandonce\skv@tempb}% + \else + \skv@checkchoice@a\unexpanded{#1}\skv@nil + {\skvexpandonce\skv@tempa}{\skvexpandonce\skv@tempb}% + \fi + \ifskv@st}\fi + }% +} +\skvrobustdef*\skv@checkchoice@a#1#2\skv@nil#3#4{% + \def\skv@tempa{#2}% + \skvifx\skv@tempa\@empty{% + \skv@checkchoice@b#1{#3}{#4}% + }{% + \skv@checkchoice@c#1#2{#3}{#4}% + }% +} +\skvrobustdef*\skv@checkchoice@b#1#2#3{% + \def\skv@tempa{#1}% + \ifx\skv@tempa\skv@nnil + \def\skv@tempa{\endgroup}% + \else + \def\skv@tempa{\endgroup\def#1{#2}}% + \fi + \skvifin{,#2,}{,#3,}{% + \ifskv@pl + \skvappendtomacro\skv@tempa{\@firstoftwo}% + \else + \skvappendtomacro\skv@tempa{\@firstofone}% + \fi + }{% + \ifskv@pl + \skvappendtomacro\skv@tempa{\@secondoftwo}% + \else + \skv@err{Value '\detokenize{#2}' is not allowed}\@ehd + \skvappendtomacro\skv@tempa{\@gobble}% + \fi + }% + \skv@tempa +} +\skvrobustdef*\skv@checkchoice@c#1#2#3#4{% + \@tempcnta\skvz@ + \def\skv@tempa{#3}% + \edef\skv@tempc{\unexpanded{#4}}% + \skvcsvnormalize\skv@tempc + \def\skv@prova{\endgroup\skvexpandonce\skv@tempc\noexpand}% + \def\skv@tempb##1,{% + \def\skv@provb{##1}% + \ifx\skv@provb\skv@nnil + \def\skv@tempc{\def#1{#3}\def#2{-1}}% + \ifskv@pl + \edef\skv@tempb{\skv@prova\@secondoftwo}% + \else + \skv@err{Value '\detokenize{#3}' is not allowed}\@ehd + \edef\skv@tempb{\skv@prova\@gobble}% + \fi + \else + \edef\skv@tempc{\def\unexpanded{#1{##1}\def#2}{\the\@tempcnta}}% + \ifx\skv@provb\skv@tempa + \ifskv@pl + \edef\skv@tempb{\skv@prova\skv@checkchoice@d}% + \else + \edef\skv@tempb{\skv@prova\skv@checkchoice@e}% + \fi + \else + \advance\@tempcnta\@ne + \fi + \fi + \skv@tempb + }% + \expandafter\skv@tempb\skv@tempc,\skv@nil,% +} +\skvrobustdef*\skv@checkchoice@d#1\skv@nil,{\@firstoftwo} +\skvrobustdef*\skv@checkchoice@e#1\skv@nil,{\@firstofone} + +% \skvifkeydef[<prefs>]{<fams>}{key}{<true>}{<false>} +% 1. The search will stop as soon as the key is found in one combination +% of prefixes/families. +% 2. The prefix and header created here may be needed in #5 and #6. Hence +% we put entry prefix and header on stack before commencing the search. +\skvrobustdef*\skvifkeydef{\skv@testopt{\skv@ifkeyundef0}{KV}} +\skvrobustdef*\skvifkeyundef{\skv@testopt{\skv@ifkeyundef1}{KV}} +\skvrobustdef*\skv@ifkeyundef#1[#2]#3#4#5#6{% + \skvpushstate\skv@keystate\skv@keydepth + \edef\skv@prefs{#2}% + \edef\skv@fams{#3}% + \skvdespace{#4}\skvcurrentkey + \skv@kffalse + % Don't use \skvcurrentprefix in place of \skv@prova here. + % We use \skv@prova here because \skv@tempa, \skvcurrentprefix and + % \skvcurrentfamily might be in use already. + \skvcommaparse*\skv@prefs\skv@prova{% + \skv@makeprefix\skv@prova + \skv@ifk@yundef + \ifskv@kf\skvbreakloop\fi + }% + \skvexpanded{% + \skvifbool{skv@kf}{% + \if0#1% + \noexpand\@firstoftwo + \else + \noexpand\@secondoftwo + \fi + }{% + \if0#1% + \noexpand\@secondoftwo + \else + \noexpand\@firstoftwo + \fi + }% + }% + {#5}{#6}% + \skvpopstate\skv@keystate\skv@keydepth +} +\skvrobustdef*\skv@ifk@yundef{% + \skvcommaparse*\skv@fams\skv@prova{% + \skv@makeheader\skv@prova + \skvifcsundef{\skv@header\skvcurrentkey.@cbk}{}{% + \skv@kftrue\skvbreakloop + }% + }% +} +% \skvifkeyinfamilies[<prefs>]{<fams>}{key}{<true>}{<false>} +% The search will be done on all combinations of prefixes/families. The +% callback <true> or <false> will be executed for all the combinations. +\skvrobustdef*\skvifkeyinfamilies{% + \skv@testopt{\skv@ifkeyinfamilies0}{KV}% +} +\skvrobustdef*\skvifkeynotinfamilies{% + \skv@testopt{\skv@ifkeyinfamilies1}{KV}% +} +\skvrobustdef*\skv@ifkeyinfamilies#1[#2]#3#4#5#6{% + \skvpushstate\skv@keystate\skv@keydepth + \edef\skv@prefs{#2}% + \edef\skv@fams{#3}% + \skvdespace{#4}\skvcurrentkey + \skvcommaparse*\skv@prefs\skv@prova{% + \skv@makeprefix\skv@prova + \skv@ifk@yinfamilies{#1}{#5}{#6}% + }% +} +\skvrobustdef*\skv@ifk@yinfamilies#1#2#3{% + \skvcommaparse*\skv@fams\skv@prova{% + \skv@makeheader\skv@prova + \skvexpanded{% + \skvifcsdef{\skv@header\skvcurrentkey.@cbk}{% + \if0#1% + \noexpand\@firstoftwo + \else + \noexpand\@secondoftwo + \fi + }{% + \if0#1% + \noexpand\@secondoftwo + \else + \noexpand\@firstoftwo + \fi + }% + }{#2}{#3}% + }% + \skvpopstate\skv@keystate\skv@keydepth +} +% \skvifkeyinfamily{<prefix>/<family>}{<key>} +\skvnewdef*\skvifkeyinfamily#1#2{% + \skvifcsdef{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}% +} +% \skvifkeyincurrentfamily{<key>} +\skvnewdef*\skvifkeyincurrentfamily#1{% + \skvifcsdef{\skvcurrentpath/\skvtrimspace{#1}.@cbk}% +} +% \skvifonekeyinfamily{<prefix>/<family>}{<keys>} +% Given a list of keys, test if at least one of them is defined. +% Example: \edef\x{\skvifonekeyinfamily{KV/fam}{keya,keyb,keyc}{T}{F}}. +\skvnewdef*\skvifonekeyinfamily#1#2{% + \ifnum\numexpr0\skv@ifonekeydef{#1}#2,\@nnil,>\skvz@ + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvnewdef*\skv@ifonekeydef#1#2,{% + \ifx\@nnil#2% + \expandafter\@gobble + \else + \skvifcsndef{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}{+1}{+0}% + \expandafter\skv@ifonekeydef + \fi + {#1}% +} + +% \skvvalueof{<pref>}{<family>}{<key>} +\skvnewdef*\skvvalueof#1#2#3{\skvcsuse{#1/#2/#3.@value}} + +% When <pref> isn't given, the following will use 'KV' for <pref>. +% \skvprintvalue[<pref>]{<family>}{<key>} +\skvrobustdef*\skvprintvalue{\skv@testoptb\skv@printvalue} +\skvrobustdef*\skv@printvalue#1{\skvcsuse{\skv@header#1.@value}} + +% \skvgetvalue{<pref>}{<family>}{<key>}<cmd> +\skvrobustdef*\skvgetvalue#1#2#3#4{\skvletcs#4{#1/#2/#3.@value}} + +% \skvconveyvalue[<pref>]{<family>}{<key>}<cmd> +\skvrobustdef*\skvconveyvalue{\skv@testoptb\skv@conveyvalue} +\skvrobustdef*\skv@conveyvalue#1#2{\skvletcs#2{\skv@header#1.@value}} + +% \skvgetinikv{<pref>}{<family>}<cmd> +\skvrobustdef*\skvgetinikv#1#2#3{\skvletcs#3{#1/#2.@inikv}} + +% \skvconveyinikv[<pref>]{<family>}<cmd> +\skvrobustdef*\skvconveyinikv{\skv@testoptb\skv@conveyinikv} +\skvrobustdef*\skv@conveyinikv#1{\skvletcs#1{\skv@header.@inikv}} + +% \skvkeyslet[<pref>]{<family>}{<list>} +% <list> -> <new keyname>=<old keyname> +\skvrobustdef*\skvkeyslet{\skv@testoptb\skv@keyslet} +\skvnewlet\skvkeylet\skvkeyslet +\skvrobustdef*\skv@keyslet#1{% + \skvletcs\skv@inikv{\skv@header.@inikv}% + \skvifundef\skv@inikv{}{% + \expandafter\skv@getkeynames\expandafter{\skv@inikv}\skv@inikeys + }% + \def\skv@k@yslet##1=##2=##3\skv@nil{% + \skvifcsundef{\skv@header##2.@cbk}{% + \skv@err{Key '\skvcurrentpath/##2' is undefined}\@ehd + }{% + \skvifcsdef{\skv@header##1.@cbk}{% + \skv@err{Key '\skvcurrentpath/##1' is already defined}\@ehd + }{}% + }% + \skvcommaloop{cbk,ndv,fbv}\skv@prova{% + \skvcsletcs{\skv@header##1.@\skv@prova}{\skv@header##2.@\skv@prova}% + }% + \skvifcsdef{\skv@header##2.@defa}{% + \edef\skvcurrentkey{##2}% + \skv@getdefault\skvcurrentprefix\skvcurrentfamily\skvcurrentkey + \skvcurrentvalue + \skvcsedef{\skv@header##1.@defa}{% + \skvnoexpandcs{\skv@header##1}{\skvexpandonce\skvcurrentvalue}% + }% + \skv@swatrue + }{% + \skv@swafalse + }% + \skvifundef\skv@inikv{}{% + \skvxifnotin{,\detokenize{##2},}{,\skvoxdetok\skv@inikeys,}{}{% + \skvcsedef{\skv@header.@inikv}{% + \expandafter\skvaddlist\expandafter + ,\csname\skv@header.@inikv\endcsname + ##1\ifskv@swa=\skvexpandonce\skvcurrentvalue\fi + }% + }% + }% + \skvcommaloop{ignoredkeys,needini}\skv@prova{% + \skvletcs\skv@tempa{\skv@header.@\skv@prova}% + \skvifundef\skv@tempa{}{% + \skvxifnotin{,\detokenize{##2},}{,\skvoxdetok\skv@tempa,}{}{% + \skvcsedef{\skv@header.@\skv@prova}{\skvaddlist,\skv@tempa##1}% + }% + }% + }% + }% + \skvkvparse{#1}\skv@prova{% + \expandafter\skv@k@yslet\skv@prova==\skv@nil + }% + \let\do\skvundef + \do\skv@k@yslet\do\skv@inikv\do\skv@inikeys +} + +\skvrobustdef*\skv@disabledkeywarning#1{% + \skv@warn{Key '\skvcurrentpath/#1' has been disabled + \MessageBreak You can't set or reset key '#1' at this + \MessageBreak late stage. Perhaps you should have set it + \MessageBreak earlier \ifskv@inlatex in \noexpand\documentclass or + \string\usepackage\fi}% +} +\skvrobustdef*\skv@disabledkeyerror#1{% + \skv@err{Key '\skvcurrentpath/#1' has been disabled}% + {You can't set or reset key '#1' at this + \MessageBreak late stage. Perhaps you should have set it + \MessageBreak earlier \ifskv@inlatex in \noexpand\documentclass or + \string\usepackage\fi}% +} +\skvrobustdef*\skvdisabledkeysmessagetype#1{% + \edef\skv@prova{\skvtrimspace{#1}}% + \def\skv@provb##1{% + \def\skv@provb####1##1####2####3\@nil{% + \ifcase####2\relax + \let\skv@disabledmsg\skv@disabledkeywarning + \or + \let\skv@disabledmsg\skv@disabledkeywarning + \or + \let\skv@disabledmsg\skv@disabledkeyerror + \or + \let\skv@disabledmsg\@gobble + \else + \skv@err{Invalid message type '#1' for disabled keys}\@ehd + \fi + }% + \skv@provb warn{0}warning{1}error{2}nothing{3}##1{10}\@nil + }% + \expandafter\skv@provb\expandafter{\skv@prova}% +} +\skvdisabledkeysmessagetype{warn} + +\skvrobustdef*\skvdisablekeys{\skv@testoptb\skv@disablekeys} +\skvrobustdef*\skv@disablekeys#1{% + \skvcommaparse{#1}\skv@tempa{% + \skvifcsdef{\skv@header\skv@tempa.@cbk}{}{% + \skv@err{Key '\skv@tempa' is undefined: + \MessageBreak couldn't be disabled}\@ehd + }% + \skvifcsdef{\skv@header\skv@tempa.@defa}{% + \edef\skv@tempb{\noexpand\skv@ordkey{\skv@tempa}[]}% + }{% + \edef\skv@tempb{\noexpand\skv@ordkey{\skv@tempa}}% + }% + \expandafter\skv@tempb\expandafter{\expandafter\skv@disabledmsg + \expandafter{\skv@tempa}}% + }% +} + +% We split xkeyval's \presetkeys (which has both head and tail) into +% \skvpresetkeys (head only) and \skvpostsetkeys (tail only). +% We strengthen \presetkeys as \skvpreposetkeys. +% +% \skvpresetkeys[<pref>]{<fam>}{<keys>} +% \skvgpresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvpresetkeys{\skv@testopt\skv@presetkeys{KV}} +\skvrobustdef*\skv@presetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{#3}{}} +\skvrobustdef*\skvgpresetkeys{\skv@testopt\skv@gpresetkeys{KV}} +\skvrobustdef*\skv@gpresetkeys[#1]#2#3{\skvgpreposetkeys[#1]{#2}{#3}{}} +\skvrobustdef*\skvpostsetkeys{\skv@testopt\skv@postsetkeys{KV}} +\skvrobustdef*\skv@postsetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{}{#3}} +\skvrobustdef*\skvgpostsetkeys{\skv@testopt\skv@gpostsetkeys{KV}} +\skvrobustdef*\skv@gpostsetkeys[#1]#2#3{\skvgpreposetkeys[#1]{#2}{}{#3}} +\skvrobustdef*\skvpreposetkeys{\skv@clfalse\skv@testoptb\skv@preposetkeys} +\skvrobustdef*\skvgpreposetkeys{\skv@cltrue\skv@testoptb\skv@preposetkeys} +\skvrobustdef*\skv@preposetkeys#1#2{% + \skvifblank{#1}{}{\skv@pr@posetkeys{#1}{preset}}% + \skvifblank{#2}{}{\skv@pr@posetkeys{#2}{postset}}% +} +\skvrobustdef*\skv@pr@posetkeys#1#2{% + \skvifcsundef{\skv@header.@#2}{% + \skvnormalize{csv}{,}{#1}\skv@tempa + \ifskv@cl\expandafter\global\fi + \expandafter\def\csname\skv@header.@#2\expandafter + \endcsname\expandafter{\skv@tempa}% + }{% + % Don't change \skv@filtermergelist to \skvfiltermergelist: + \expandafter\skv@filtermergelist\csname\skv@header + .@#2\endcsname{#1}\skv@getkeyname + }% +} +% \skvremovepresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepresetkeys{% + \skv@clfalse\skv@testoptb\skv@removepresetkeys +} +% \skvgremovepresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepresetkeys{% + \skv@cltrue\skv@testoptb\skv@removepresetkeys +} +\skvrobustdef*\skv@removepresetkeys#1{% + \skvifcsdef{\skv@header.@preset}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@preset\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No preset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }% +} +% \skvundefpresetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpresetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpresetkeys +} +\skvrobustdef*\skvgundefpresetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpresetkeys +} +\skvrobustdef*\skv@undefpresetkeys{% + \skvifcsundef{\skv@header.@preset}{% + \skv@err{No preset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header.@preset}% + }% +} + +% \skvremovepostsetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepostsetkeys{% + \skv@clfalse\skv@testoptb\skv@removepostsetkeys +} +% \skvgremovepostsetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepostsetkeys{% + \skv@cltrue\skv@testoptb\skv@removepostsetkeys +} +\skvrobustdef*\skv@removepostsetkeys#1{% + \skvifcsdef{\skv@header.@postset}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@postset\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No postset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }% +} +% \skvundefpostsetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpostsetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpostsetkeys +} +\skvrobustdef*\skvgundefpostsetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpostsetkeys +} +\skvrobustdef*\skv@undefpostsetkeys{% + \skvifcsundef{\skv@header.@postset}{% + \skv@err{No postset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header.@postset}% + }% +} + +% \skvremovepreposetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepreposetkeys{% + \skv@clfalse\skv@testoptb\skv@removepreposetkeys +} +% \skvgremovepreposetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepreposetkeys{% + \skv@cltrue\skv@testoptb\skv@removepreposetkeys +} +\skvrobustdef*\skv@removepreposetkeys#1#2{% + \skv@r@movepreposetkeys{#1}{preset}% + \skv@r@movepreposetkeys{#2}{postset}% +} +\skvrobustdef*\skv@r@movepreposetkeys#1#2{% + \skvifcsdef{\skv@header.@#2}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@#2\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No preset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }% +} +% \skvundefpreposetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpreposetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpreposetkeys +} +\skvrobustdef*\skvgundefpreposetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpreposetkeys +} +\skvrobustdef*\skv@undefpreposetkeys{% + \skvifcsundef{\skv@header.@preset}{% + \skv@err{No preset keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header.@preset}% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header.@postset}% + }% +} + +%% Note about the absence of '(g)save-value-keys': +% All keys have their values saved in the macro '<header>.@value'. +% The value can be accessed via \skvgetvalue of \skvconveyvalue. When +% needed, the value can be taken outside a local group by using \global +% on the macro that has inherited the key's value via these commands. + +% \skvneedvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvneedvaluekeys{% + \skv@clfalse\skv@testoptb\skv@needvaluekeys +} +\skvrobustdef*\skvgneedvaluekeys{% + \skv@cltrue\skv@testoptb\skv@needvaluekeys +} +\skvrobustdef*\skv@needvaluekeys#1{% + \skvcommaparse{#1}\skv@tempa{% + \ifskv@cl\expandafter\global\fi + \skvcsdef{\skv@header\skv@tempa.@ndv}{}% + }% +} +% \skvremoveneedvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveneedvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removeneedvaluekeys +} +\skvrobustdef*\skvgremoveneedvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removeneedvaluekeys +} +\skvrobustdef*\skv@removeneedvaluekeys#1{% + \skvcommaparse{#1}\skv@tempa{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header\skv@tempa.@ndv}% + }% +} + +% \skvforbidvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvforbidvaluekeys{% + \skv@clfalse\skv@testoptb\skv@forbidvaluekeys +} +\skvrobustdef*\skvgforbidvaluekeys{% + \skv@cltrue\skv@testoptb\skv@forbidvaluekeys +} +\skvrobustdef*\skv@forbidvaluekeys#1{% + \skvcommaparse{#1}\skv@tempa{% + \ifskv@cl\expandafter\global\fi + \skvcsdef{\skv@header\skv@tempa.@fbv}{}% + }% +} +% \skvremoveforbidvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveforbidvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removeforbidvaluekeys +} +\skvrobustdef*\skvgremoveforbidvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removeforbidvaluekeys +} +\skvrobustdef*\skv@removeforbidvaluekeys#1{% + \skvcommaparse{#1}\skv@tempa{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header\skv@tempa.@fbv}% + }% +} + +% Save the default/initial values of keys: +% \skvsaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvsaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@saveinitialvaluekeys +} +% \skvgsaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgsaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@saveinitialvaluekeys +} +\skvrobustdef*\skv@saveinitialvaluekeys#1{% + \skvifcsundef{\skv@header.@needini}{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize\skv@tempa + \ifskv@cl\expandafter\global\fi + \skvcsedef{\skv@header.@needini}{\skvexpandonce\skv@tempa}% + }{% + \expandafter\skv@filtermergelist\csname + \skv@header.@needini\endcsname{#1}\skv@getkeyname + }% +} +% \skvremovesaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovesaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removesaveinitialvaluekeys +} +% \skvgremovesaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovesaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removesaveinitialvaluekeys +} +\skvrobustdef*\skv@removesaveinitialvaluekeys#1{% + \skvifcsundef{\skv@header.@needini}{% + \skv@err{No save-initial keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \expandafter\skv@filterremoveelements\csname + \skv@header.@needini\endcsname{#1}\skv@getkeyname + }% +} +% \skvundefsaveinitialvaluekeys[<pref>]{<fam>} +\skvrobustdef*\skvundefsaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@undefsaveinitialvaluekeys +} +% \skvgundefsaveinitialvaluekeys[<pref>]{<fam>} +\skvrobustdef*\skvgundefsaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@undefsaveinitialvaluekeys +} +\skvrobustdef*\skv@undefsaveinitialvaluekeys{% + \skvifcsundef{\skv@header.@needini}{% + \skv@err{No save-initial keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \csname skv\ifskv@cl g\fi csundef\endcsname{\skv@header.@needini}% + }% +} + +% While setting keys, ignore the listed keys. +% \skvignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvignorekeys{\skv@clfalse\skv@testoptb\skv@ignorekeys} +\skvrobustdef*\skvgignorekeys{\skv@cltrue\skv@testoptb\skv@ignorekeys} +\skvrobustdef*\skv@ignorekeys#1{% + \skvifcsdef{\skv@header.@ignoredkeys}{% + \expandafter\skv@filtermergelist\csname + \skv@header.@ignoredkeys\endcsname{#1}\skv@getkeyname + }{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[,]\skv@tempa + \ifskv@cl\expandafter\global\fi + \skvcsedef{\skv@header.@ignoredkeys}{\skvexpandonce\skv@tempa}% + }% +} +% \skvremoveignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveignorekeys{% + \skv@clfalse\skv@testoptb\skv@removeignorekeys +} +% \skvgremoveignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremoveignorekeys{% + \skv@cltrue\skv@testoptb\skv@removeignorekeys +} +\skvrobustdef*\skv@removeignorekeys#1{% + \skvifcsundef{\skv@header.@ignoredkeys}{% + \skv@err{No ignore keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \expandafter\skv@filterremoveelements\csname + \skv@header.@ignoredkeys\endcsname{#1}\skv@getkeyname + }% +} +% \skvundefignorekeys[<pref>]{<fam>} +\skvrobustdef*\skvundefignorekeys{% + \skv@clfalse\skv@testoptb\skv@undefignorekeys +} +% \skvgundefignorekeys[<pref>]{<fam>} +\skvrobustdef*\skvgundefignorekeys{% + \skv@cltrue\skv@testoptb\skv@undefignorekeys +} +\skvrobustdef*\skv@undefignorekeys{% + \skvifcsundef{\skv@header.@ignoredkeys}{% + \skv@err{No ignore keys defined for + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + \csname skv\ifskv@cl g\fi csundef\endcsname + {\skv@header.@ignoredkeys}% + }% +} + +% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +% To add (2012/08/08): +% \skvoptionkeys +% \skvnotoptionkeys +% \skvnewlet\skvnonoptionkeys\skvnotoptionkeys +%--------------------------------------------------------------- + +% \skvaddkeycode[<pref>]{<fam>}{<key>}{<precode>}{<postcode>} +\skvrobustdef*\skvaddkeycode{\skv@testoptb\skv@addkeycode} +\skvrobustdef*\skv@addkeycode#1#2#3{% + \skv@prependkeycode{prep}{#1}{#2}% + \skv@prependkeycode{app}{#1}{#3}% +} +\skvnewlet\skvaddtocallback\skvaddkeycode +% \skvprependkeycode[<pref>]{<fam>}{<key>}{<code>} +\skvrobustdef*\skvprependkeycode{\skv@testoptb{\skv@prependkeycode{prep}}} +\skvrobustdef*\skvappendkeycode{\skv@testoptb{\skv@prependkeycode{app}}} +\skvnewlet\skvprependtocallback\skvprependkeycode +\skvnewlet\skvapendtocallback\skvappendkeycode +\skvrobustdef*\skv@prependkeycode#1#2#3{% + \edef\skvcurrentkey{\skvtrimspace{#2}}% + \skvifcsdef{\skv@header\skvcurrentkey.@cbk}{% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{% + \skvifstrcmp{#1}{prep}{% + \skvtrimspace{#3}% + \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}% + }{% + \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}% + \skvtrimspace{#3}% + }% + }% + }{% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{\skvtrimspace{#3}}% + }% +} + +% Initialize remaining-keys to empty, for the listed prefixes and families: +% \skvinitializermkeys{<prefixes>}{<families>} +\skvrobustdef*\skvinitializermkeys{% + \skv@clfalse\skv@testst\skv@initializermkeys +} +\skvrobustdef*\skvginitializermkeys{% + \skv@cltrue\skv@testst\skv@initializermkeys +} +\skvrobustdef*\skv@initializermkeys#1#2{% + \skv@usetempst{#1}\skv@tempa + \skv@usetempst{#2}\skv@tempb + \skvcommaparse*\skv@tempa\skv@tempa{% + \skv@makeprefix\skv@tempa + \skvcommaparse*\skv@tempb\skv@tempb{% + \skv@makeheader\skv@tempb + \skvcslet{\skv@header.@rmkeys}\@empty + \ifskv@cl\skvglobalizecs{\skv@header.@rmkeys}\fi + }% + }% +} + +% \skvgetrmkeys[<pref>]{<fam>}<cmd> +\skvrobustdef*\skvgetrmkeys{\skv@clfalse\skv@testoptb\skv@getrmkeys} +\skvrobustdef*\skvggetrmkeys{\skv@cltrue\skv@testoptb\skv@getrmkeys} +\skvrobustdef*\skv@getrmkeys#1{% + \skvifcsdef{\skv@header.@rmkeys}{% + \skvletcs#1{\skv@header.@rmkeys}% + \ifskv@cl\global\let#1=#1\fi + }{% + \skv@err{No 'rmkeys' for family + '\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }% +} +\skvrobustdef*\skvsetkeys{% + \skv@testopta{\skv@testoptc\skv@setkeys@a}% +} +% \skv@setkeys@a[<ignore.list>]{<kvlist>} +\skvrobustdef*\skv@setkeys@a[#1]#2{% + % \skvexecuteoptions calls \skv@setkeys@a. So this is the place for + % \skv@insettrue: + \skv@insettrue + % \skv@testoptc has created \skvcurrentprefix and \skv@fams. + \ifskv@intry\else\skvafterfi + \skv@getkeynames{#2}\skv@naa + \fi + % Both #1 and #2 will be normalized in \skv@setkeys@b. + \skv@setprepokeys{#1}{preset}% + \skv@setkeys@b{#2}{#1}% + \skv@setprepokeys{#1}{postset}% + \ifnum\skv@setkeysdepth=\skvz@ + \skv@insetfalse + \fi + % Don't set \let\CurrentOption\@empty here. +} +\skvrobustdef*\skv@setprepokeys#1#2{% + \skv@prepotrue + \skvecommaloop*\skv@fams\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + \skvifcsundef{\skv@header.@#2}{}{% + \skv@toksa\expandafter\expandafter\expandafter + {\csname\skv@header.@#2\endcsname}% + \skvexpandtwoargs\skv@setkeys@b{\the\skv@toksa}% + {\skvexpandonce\skv@naa\ifx\skv@naa\@empty\else,\fi + \unexpanded{#1}}% + }% + }% + \skv@prepofalse +} + +% \skv@keydepth is called in places other than by \skvsetkeys. Hence we +% need an independent \skv@setkeysdepth here. +% \skv@setkeys@b{<kvlist>}{<ignore.list>} +\skvrobustdef*\skv@setkeys@b#1#2{% + % \skv@setprepokeys calls \skv@setkeys@b. So this is the place to + % advance \skv@setkeysdepth. + \skvgadvanceno\skv@setkeysdepth\@ne + \ifskv@st + \ifnum\skv@setkeysdepth=\@ne + \skvinitializermkeys*\skvcurrentprefix\skv@fams + \fi + \fi + \ifnum\skv@setkeysdepth>\skv@stackdepthlimit\relax + \skv@err{\noexpand\skvsetkeys is nested too deeply. + \MessageBreak Maybe there has been infinite reentry}\@ehd + \fi + % \skv@na can't be anywhere else, not in \skv@setkeys@a. + \edef\skv@na{\unexpanded{#2}}% + \skvifempty\skv@na{}{\skvcsvnormalize\skv@na}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvifempty\skv@tempa{}{\skvkvnormalize\skv@tempa}% + \def\skv@setkeys@c##1={% + \skvxifin{(}{\detokenize{##1}}{% + \skvxifin{)}{\detokenize{##1}}{% + \def\skv@ispntkey{00}% + \skv@extr@ctpntkey{##1}\skvcurrentkey\skvcurrentvalue + \ifx\skvcurrentvalue\@empty + \skvnovaluetrue + \else + \skvnovaluefalse + \fi + }{% + \skv@err{Key value has '(' but no ')'} + {The value of a parenthesized key must have both opening + '(' and closing ')'}% + }% + }{% + \def\skv@ispntkey{01}% + \skv@g@tkeyname##1=\skv@nil\skvcurrentkey + }% + \def\skv@prova####1=####2\skv@nil{% + \skvifcond\if\skv@ispntkey\fi{}{% + \edef\skvcurrentvalue{\skvexpandonce{\@gobble####1}}% + \ifx\skvcurrentvalue\skv@novaluetoks + \skvnovaluetrue + \def\skvcurrentvalue{}% + \else + \skvnovaluefalse + \fi + \skvexpbracenext\skvifbraced\skvcurrentvalue{% + \skvvaluebracedtrue + }{% + % Strip and use pointers from key value: + \skvexpbracenext\skv@strippointers\skvcurrentvalue + \skvcurrentvalue + }% + }% + \ifx\skvcurrentkey\@empty + \ifx\skvcurrentvalue\@empty\else + \skv@err{Current key is nil, but you have a value for it: + \MessageBreak\skvoxdetok\skvcurrentvalue}\@ehd + \fi + \fi + \edef\CurrentOption{% + \skvcurrentkey + \ifx\skvcurrentvalue\@empty\else\skvafterfi + =\skvxonce\skvcurrentvalue + \fi + }% + \skv@setkeys@d + }% + \skv@prova.% + }% + \skvcommaloop*\skv@tempa\CurrentOption{% + \skvvaluebracedfalse + % Expand the key name twice, in case the key and its value + % are hidden in a macro. A maverick might do just that. + \expandafter\expandafter\expandafter + \skv@setkeys@c\CurrentOption=\skv@novalue=\skv@nil + }% + \skvgadvanceno\skv@setkeysdepth\m@ne +} + +\skvrobustdef*\skv@setkeys@d{% + \skv@cnta\@ne + \skvletcs\skv@tempa{\skv@header.@ignoredkeys}% + \skvifdef\skv@tempa{% + \skvxifin{,\skvcurrentkey,}{,\skv@tempa,}{% + \skv@cnta\skvz@ + }{% + \skvxifin{,\skvcurrentkey,}{,\skv@na,}{\skv@cnta\skvz@}{}% + }% + }{% + \skvxifin{,\skvcurrentkey,}{,\skv@na,}{\skv@cnta\skvz@}{}% + }% + \skvifnum\skv@cnta=\skvz@{}{% + \skv@kffalse + \skvifdefbool{skv@prepo}{% + \skv@setkeys@e + }{% + \skvifdefbool{skv@intry}{% + \skv@setkeys@e + }{% + \skvifdefbool{skv@pl}{% + \skvecommaloop*\skv@fams\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + \skv@setkeys@e + % If we don't save rm keys here, the family may change before + % we get to the next call of \skv@setkeys@f: + \ifskv@kf\else + \ifskv@st + \expandafter\expandafter\expandafter\skv@setkeys@f + \fi + \fi + }% + }{% + \skvcommaloop*\skv@fams\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + \skv@setkeys@e + \ifskv@kf\skvbreakloop\fi + }% + }% + }% + }% + \skvifdefbool{skv@kf}{% + \ifskv@inpox\skvafterfifi@inneriscond + \ifx\skv@testclass\skv@documentclass\skvafterfi + \expandafter\skv@useoption\expandafter{\CurrentOption}% + \fi + \fi + }{% + \skvifdefbool{skv@inpox}{% + \skvifcsdef{\skv@header.@famhandler}{% + \begingroup + \let\elt\skvexpandonce + \skvexpanded{\endgroup + \skvnoexpandcs{\skv@header.@famhandler}% + {\elt\skvcurrentprefix}{\elt\skvcurrentfamily}% + {\elt\skvcurrentkey}{\elt\skvcurrentvalue}% + }% + }{% + \skvifcsdef{skv@dox@\@currname @\@currext}{% + \skvcsuse{skv@dox@\@currname @\@currext}% + }{% + \ifx\@currext\@clsextension\else + \let\CurrentOption\skvcurrentkey\@unknownoptionerror + \fi + }% + }% + }{% + \skvifdefbool{skv@intry}{% + % Continue in remaining families. When all the families have + % been searched and the key isn't found, toggle 'intry' false + % and raise error. + }{% + \skvifdefbool{skv@st}{% + \ifskv@pl + % In this case we've already saved any rm keys. + \relax + \else + \expandafter\skv@setkeys@f + \fi + }{% + \skvifcsdef{\skv@header.@famhandler}{% + \begingroup + \let\elt\skvexpandonce + \skvexpanded{\endgroup + \skvnoexpandcs{\skv@header.@famhandler}% + {\elt\skvcurrentprefix}{\elt\skvcurrentfamily}% + {\elt\skvcurrentkey}{\elt\skvcurrentvalue}% + }% + }{% + \skv@err{Key '\skvcurrenttriple' is undefined} + {I have come across an undefined key '\skvcurrentkey'.}% + }% + }% + }% + }% + }% + }% +} +\skvrobustdef*\skv@setkeys@e{% + \skvifcsundef{\skv@header\skvcurrentkey.@cbk}{% + \ifskv@intry\skv@successfalse\fi + }{% + \skv@kftrue + \ifskv@intry\skv@successtrue\fi + \ifskvnovalue + \skvifcsdef{\skv@header\skvcurrentkey.@ndv}{% + \skv@err{Key '\skvcurrenttriple' requires + \MessageBreak a user value}\@ehd + }{% + \skvifcsdef{\skv@header\skvcurrentkey.@defa}{% + \skv@getdefault\skvcurrentprefix\skvcurrentfamily\skvcurrentkey + \skvcurrentvalue + % Check if the default value is braced: + \skvexpbracenext\skvifnotbraced\skvcurrentvalue{}{% + \skvvaluebracedtrue + }% + }{% + \skv@err{No value specified for key + \MessageBreak'\skvcurrenttriple' + \MessageBreak and no default value}\@ehd + }% + }% + \else + \skvifcsdef{\skv@header\skvcurrentkey.@fbv}{% + \skv@err{Key '\skvcurrenttriple' doesn't + \MessageBreak accept a user value}\@ehd + }{}% + \fi + \skvifempty\skvcurrentvalue{}{% + \expandafter\skv@replacepointers\expandafter{\skvcurrentvalue}% + }% + % Use argument type to possibly expand the argument: + \def\do##1##2##3{% + \skvifcsdef{\skv@header\skvcurrentkey.@##1arg}{% + {\noexpand##2}% + {\skvexpandcsonce{\skv@header\skvcurrentkey.@##1arg}}% + }{% + ##3% + }% + }% + % Get key's argument and its expander type: + \edef\skv@prova{% + % This ordering is important: + \do{xx}{\@firstofone}{% + \do{x2}{\expandcstwice}{% + \do{x1}{\skvexpandonce}{% + \do{x0}{\unexpanded}{% + \do{}{\unexpanded}{{\noexpand\unexpanded}{####1}}% + }% + }% + }% + }% + }% + \def\skv@provb##1##2\skv@arg@nil{% + \edef\skv@temparg{\unexpanded{##2}}% + \skvstripouterbraces{2}\skv@temparg + % Apply expander ##1: + \edef\skvcurrentvalue{\expandafter##1\expandafter{\skvcurrentvalue}}% + % Strip outer braces of weird arguments, so that the argument can be + % grabbed by the, possibly multiparametered, callback function: + \ifx\skv@temparg\skv@simplearg\else\skvafterfi + \skvstripouterbraces{2}\skvcurrentvalue + \skvvaluebracedfalse + \fi + }% + \expandafter\skv@provb\skv@prova\skv@arg@nil + \skvcslet{\skv@header\skvcurrentkey.@value}\skvcurrentvalue + \skvifcsdef{\skv@header\skvcurrentkey.@hp}{% + \skvexpanded{\skvcslet{\skvexpandcsonce + {\skv@header\skvcurrentkey.@hp}}}\skvcurrentvalue + }{}% + \skvpushstate\skv@keystate\skv@keydepth + \skvletcs\skv@prova{\skv@header\skvcurrentkey.@cbk}% + \expandafter\expandafter\expandafter\def\expandafter\expandafter + \expandafter\skv@tempcbk\expandafter\skv@temparg\expandafter + \skeyvaleov\expandafter{\skv@prova}% + \ifx\skv@temparg\skv@simplearg\skvafterelsei + \expandafter\skv@tempcbk\expandafter{\skvcurrentvalue}\skeyvaleov + \else\skvafterfi + \expandafter\skv@tempcbk\skvcurrentvalue\skeyvaleov + \fi + \skvpopstate\skv@keystate\skv@keydepth + }% +} +% Save rm keys: +\skvrobustdef*\skv@setkeys@f{% + \edef\skv@prova{% + \skvcurrentkey + \ifskvnovalue\else\skvafterfi + =\skvexpandonce\skvcurrentvalue + \fi + }% + \skvexpanded{% + \skvmergelist\skvnoexpandcs{\skv@header.@rmkeys}% + {\skvexpandonce\skv@prova}% + % The boolean \ifdirkeys@saveunknownkeys is, by default, false. + % The default value of \dirkeys@unknownkeysmacro is \skvfaillist. + \ifdirkeys@saveunknownkeys + \skvmergelist\skvexpandonce\dirkeys@unknownkeysmacro + {{\skvcurrentprefix}/{\skvcurrentfamily}/% + {\skvexpandonce\skv@prova}}% + \fi + }% +} +\skvrobustdef*\skvsetrmkeys{\skv@testopta{\skv@testoptc\skv@setrmkeys}} +\skvrobustdef*\skv@setrmkeys[#1]{% + % Only one prefix is allowed. \skv@testoptc has created + % \skvcurrentprefix and \skv@fams. + \skvcommaloop*\skv@fams\skv@tempa{% + \skv@makeheader\skv@tempa + \skvletcs\skv@prova{\skv@header.@rmkeys}% + \skvifdef\skv@prova{% + \skvexpandbracenext{\skv@setkeys@a[#1]}\skv@prova + }{}% + }% +} +% \skv@getdefault{<pref>}{<fam>}{<key>}{<return.macro>} +\skvrobustdef*\skv@getdefault#1#2#3#4{% + \begingroup + \let\elt\relax + \edef\skv@prova{\elt{#1}/\elt{#2}/\elt{#3}}% + \let\elt\skvtrimspace + \edef\skv@prova{\skv@prova}% + \def\skv@g@tdefault##1##2\skv@nil{% + \edef\skv@provb{\skvremovescape{##1}}% + \@onelevel@sanitize\skv@prova + \ifx\skv@prova\skv@provb + \skvcsdef{\skv@prova}####1{\edef#4{\unexpanded{####1}}}% + \csname\skv@prova.@defa\endcsname + \else + \skv@err{The default value syntax of key + \MessageBreak '\skv@prova' doesn't conform to + \MessageBreak skeyval format. Maybe another package + \MessageBreak has abused the format} + {I can't extract the default value of key '\skv@prova'.}% + \fi + }% + % <default.macro><key.macro> + \expandafter\expandafter\expandafter\skv@g@tdefault + \csname\skv@prova.@defa\endcsname\skv@nil + \skvaftergroupdef#4\endgroup +} +% Set keys using their default values: +% \skvinitializekeys[<pref>]{<fams>}[<ignored.keys>] +\skvrobustdef*\skvinitializekeys{\skv@testoptc\skv@initializekeys} +% #1: ignored keys +\skvrobustdef*\skv@initializekeys[#1]{% + \begingroup + \def\skv@tempe{}% + % \skv@testoptc has created \skvcurrentprefix and \skv@fams. + \skvcommaloop*\skv@fams\skv@tempa{% + \skv@makeheader\skv@tempa + \skvletcs\skv@tempa{\skv@header.@inikv}% + \skvifundef\skv@tempa{% + \skv@err{No 'save initial value' keys defined in family + \MessageBreak'\skvcurrentprefix/\skvcurrentfamily'}\@ehd + }{% + % '\skv@header.@poxkeys' is the list of keys that have been + % instantiated as current pacakge options. + \skvletcs\skv@tempc{\skv@header.@poxkeys}% + \skvifundef\skv@tempc{% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe + \skvsetkeys[\skvcurrentprefix]{\skvcurrentfamily}% + [#1]{\skvexpandonce\skv@tempa}% + }% + }{% + % Filter pox (instantiated package/article options) list members: + \def\skv@tempd{}% + \skvcommaloop*\skv@tempa\skv@tempa{% + \expandafter\skv@g@tkeyname\skv@tempa=\skv@nil\skv@tempb + \skvxifin{,\skvoxdetok\skv@tempb,}{,\skvoxdetok\skv@tempc,}{}{% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd\ifx\skv@tempd\@empty\else,\fi + \skvexpandonce\skv@tempa + }% + }% + }% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe + \skvsetkeys[\skvcurrentprefix]{\skv@fams}[#1]% + {\skvexpandonce\skv@tempd}% + }% + }% + }% + }% + \expandafter\endgroup\skv@tempe +} + +%+++++++++++++++++++++ Compactly define keys +++++++++++++++++++++++++% +% +% \skvdefinekeys*+[<pref>]{<fams>}[<hp>]{<list>} +% * -> define only new keys. +% + -> define across the given families. +% +% The only difference between \skvedefinekeys and \skvdefinekeys is the +% value of \endlinechar. +% +% Examples: +% +% \skvdefinekeys*+[KVA]{fam1,fam2}[thp@]{% +% .ords/{keya,keyb}/{default-a}, +% .cmds/{keyc,keyd}/, +% .zcmd/key e/\def\cmde##1{##1}/ +% \edef\y{\detokenize\expandafter{\thp@keye}}\def\x##1{#1*key e*##1}, +% .choice/keyf/center/{center.do=\def\x##1{#1*##1},left,right}, +% .zbool/show center/true/\edef\cmd{\ifthp@showcenter Yes\else No\fi}, +% .args/{key1,key2}/{#1/#2}, +% .arg/key3/{#1+#2}, +% .exec/\def\x##1{##1}, +% } +% \skvshowcs{KVA/fam1/keyb.@defa} +% \def\keybval{xx} +% \skvsetkeys[KVA]{fam1}{keyb=.expand once{\keybval}} +% +\skvnewbools[skvdfk@]{saveinitialvalues,initialize} +% '.initialize' means set the keys with their default values after +% they've been defined. Leave this as ordinary keys, not zbool keys, +% since the key names contain dot (.). Zapping the spaces in the +% key name will not lead to a boolean of the \ifhp@key: +\skvordkey[SKV]{definekeys}{.initialize}[true]{% + \skvifvalidboolval{#1}{% + \csname skvdfk@initialize#1\endcsname + }% +} +% Save default values as a kv list in macro <header>.@needini: +\skvordkey[SKV]{definekeys}{.save initial values}[true]{% + \skvifvalidboolval{#1}{% + \csname skvdfk@saveinitialvalues#1\endcsname + }% +} +%\skvsetkeys[SKV]{definekeys}{.initialize=false,.save initial values=false} + +\skvnewdef*\skv@keyparser{,} +\skvnewdef*\skvdfk@afterdefinekeys{} +\skvrobustdef*\AfterDefineKeys{\skvgappendtomacro\skvdfk@afterdefinekeys} +\skvrobustdef*\skvdfk@testopta#1{% + \skvifstar{% + \skv@sttrue\skvifplus + {\skv@pltrue\skvdfk@testoptb#1}{\skv@plfalse\skvdfk@testoptb#1}% + }{% + \skv@stfalse\skvifplus + {\skv@pltrue\skvdfk@testoptb#1}{\skv@plfalse\skvdfk@testoptb#1}% + }% +} +\skvrobustdef*\skvdfk@testoptb#1{\skv@testopt{\skvdfk@t@stoptb{#1}}{KV}} +\skvrobustdef*\skvdfk@t@stoptb#1[#2]#3{% + \skvxifin{,}{\detokenize{#2}}{% + \skv@err{Only one prefix is allowed when calling + \MessageBreak \noexpand\skvdefinekeys or similar commands, + \MessageBreak but you gave '#2'}\@ehd + }{% + % The prefix and families here deserve separate names, to avoid + % confusing them with those required for setting the keys of the + % macro \skvdefinekeys. + \edef\skv@checkkeyexist{\ifskv@st00\else01\fi}% + \edef\skvdfk@pref{#2}% + \skvxifin{,}{\detokenize{#3}}{% + \edef\skvdfk@fams{#3}% + \skvcsvnormalize\skvdfk@fams + }{% + \edef\skvdfk@fams{#3}% + \skvdespacecontent\skvdfk@fams + }% + \skv@testopt#1{userhp@}% + }% +} +\skvnewnumbers{skvdfk@depth} +\skvrobustdef*\skv@pushdefinekeys{% + \skvpushfunctions\skvdefinekeys{% + \do\skvdfk@pref\do\skvdfk@fams\do\skvdfk@currlist\do\skv@accumulated + \do\skv@acc@mulated\do\skv@igkeys\do\skvdfk@keys\do\skvdfk@keyvals + \do\skv@itemfour\do\skv@itemfive\do\skv@checkkeyexist\do\skv@hp + \do\skv@type\do\skv@typenr\do\skv@default\do\skv@isarg + \do\ifskvdfk@initialize\do\ifskvdfk@saveinitialvalues\do\ifskv@st + \do\ifskv@pl\do\skvcurrentprefix\do\skvcurrentfamily\do\skvcurrentkey + }\skvdfk@depth +} +\skvrobustdef*\skvedefinekeys{% + \skv@pushdefinekeys + \begingroup + \endlinechar\m@ne + \skvdfk@testopta\skv@definekeys +} +\skvrobustdef*\skvdefinekeys{% + \skv@pushdefinekeys + \begingroup + \skvdfk@testopta\skv@definekeys +} +\skvrobustdef*\skv@definekeys[#1]#2{% + \skvexpanded{\endgroup + \skvcmdexitloop{\skv@checkkeyexist\skvdfk@pref\skvdfk@fams}% + \skvboolexit\ifskv@pl + }% + \skvindeftrue + \edef\skv@hp{#1}% + \skvdespacecontent\skv@hp + \def\skv@accumulated{}% + \skv@makeprefix\skvdfk@pref + \edef\skvdfk@currlist{\skvkeepdoubleslashnormalize{#2}}% + \def\skv@d@finekeys##1{% + \skv@makeheader{##1}% + \def\skv@acc@mulated{}% + \def\skvdfk@keys{}% + \def\skvdfk@keyvals{}% + \expandafter\skv@d@f@nekeys\expandafter{\skvdfk@currlist}% + }% + % If the plus variant is not specified, just take and use the first + % family. + \skvifdefbool{skv@pl}{% + \skvcommaloop*\skvdfk@fams\skv@tempa{% + \expandafter\skv@d@finekeys\expandafter{\skv@tempa}% + }% + }{% + \def\skv@prova##1,##2\skv@nil{% + \skv@d@finekeys{##1}% + }% + \expandafter\skv@prova\skvdfk@fams,\skv@nil + }% + \skv@accumulated + \skvpopfunctions\skvdefinekeys\skvdfk@depth + \skvindeffalse + \skvdfk@afterdefinekeys\relax + \def\skvdfk@afterdefinekeys{}% +} +\skvrobustdef*\skv@d@f@nekeys#1{% + \edef\skv@igkeys{% + \skvifcsdef{\skv@header.@ignoredkeys}{% + \skvexpandcsonce{\skv@header.@ignoredkeys}% + }{}% + }% + \def\skv@splita##1/##2/{\skv@splitb##1/##2/.}% + \def\skv@splitb##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@split@nil{% + \let\skv@gobble\relax + \edef\skv@type{\unexpanded{##1}}% + % Take the default early: it is needed in splitting multiple keys of + % the same type. In this case, the default value will not change + % until the splitting is completed. + \edef\skv@default{\skvexpandonce{\@gobble##3}}% + \def\skv@isarg{01}% + \def\skv@prova####1{% + \def\skv@prova########1####1########2########3\skv@nil{% + \def\skv@typenr{########2}% + \ifx\skv@typenr\skv@nnil + \skv@err{Invalid key type '####1'}\@ehd + \else + \ifnum\skv@typenr>6\relax + \ifnum\skv@typenr<11\relax + \def\skv@isarg{00}% + \fi + \fi + \fi + }% + \skv@prova .ord{0}.cmd{1}.zcmd{2}.bool{3}.zbool{4}.choice{5}% + .exec{6}.arg{7}.arg expanded{8}.arg expanded once{9}% + .arg expanded twice{10}.ords{11}.cmds{12}.zcmds{13}.bools{14}% + .zbools{15}.choices{16}.args{17}####1{\skv@nil}\skv@nil + }% + \expandafter\skv@prova\expandafter{\skv@type}% + \skvxifin{,\skv@typenr,}{,11,12,13,14,15,16,17,}{% + % Accumulate multiple keys of the same type: + \def\skv@prova####1s{\def\skv@type{####1}}% + \expandafter\skv@prova\skv@type + \skvcommaparse{##2}\skv@tempa{% + \skvexpanded{% + \noexpand\skv@splita + \skv@type/\skv@tempa/\skvexpandonce\skv@default/% + \unexpanded{##4/##5/##6/##7/##8/##9}\noexpand\skv@split@nil + }% + }% + % Skip the rest of \skv@splitb: + \let\skv@gobble\skv@gobbletoend + }{% + \skvifnum\skv@typenr=6{% + ##2% + \let\skv@gobble\skv@gobbletoend + }{% + % There may be multiple keys in ##2. So keep findpointers here, + % where multiple keys would have been split into separate units. + % For example, we can't check for ignored keys without first + % stripping the pointers: + \skv@strippointers{##2}\skvcurrentkey + \skvxifin{,\skvoxdetok\skvcurrentkey,}{,\skvoxdetok\skv@igkeys,}{% + \let\skv@gobble\skv@gobbletoend + }{% + \if\skv@isarg + \skvdfk@definearg\skv@type\skvcurrentkey\skv@default + \let\skv@gobble\skv@gobbletoend + \else + \let\skv@gobble\relax + \fi + }% + }% + }% + \skv@gobble + \edef\skv@itemfour{\unexpanded{##4}}% + \edef\skv@itemfive{\unexpanded{##5}}% + \skvxifin{\skv@hashchar}{\detokenize{##2}}{% + \skv@err{Key name '\detokenize{##2}' contains hash character}\@ehc + }{}% + \if\skv@checkkeyexist + \skvifcsdef{\skv@header\skvcurrentkey.@cbk}{% + \skv@err{Key '\skvcurrenttriple' already exists}\@ehd + }{}% + \fi + \ifnum\skv@typenr=5\relax + \edef\skv@prova{% + 0\ifx\skv@itemfour\@empty 1\else\ifx\skv@itemfour\skv@rej 1\fi\fi + }% + \ifcase\skv@prova\relax + \def\skv@altlista{}\def\skv@altlistb{}% + % \skv@itemfour is need here to hide unbalanced conditionals in ##4: + \expandafter\skv@getaltlist\expandafter{\skv@itemfour}% + \else + \skv@err{Empty alternate list for choice key '\skvcurrentkey'}\@ehd + \fi + \fi + \edef\skvdfk@keys{\skvaddlist,\skvdfk@keys\skvcurrentkey}% + \skvxifboolval{\skvexpandonce\skv@default}\in@true\in@false + \edef\skvdfk@keyvals{% + \skvaddlist,\skvdfk@keyvals + % If a key has no default, then it can't be initialized. An empty + % default value should be indicated with a slash, eg, keya below: + % .cmd/keya/, .bool/keyb/true. + \expandafter\skv@ifrejorna\expandafter{\skv@default}{}{% + \skvcurrentkey=\skvifdefbool{in@}{false}{\skvxonce\skv@default}% + }% + }% + \edef\skv@acc@mulated{% + \skvexpandonce\skv@acc@mulated + \skvnoexpandcs{skv\expandafter\@gobble\skv@type key}% + \ifnum\skv@typenr=5*\fi + \ifnum\skv@typenr>2\ifnum\skv@typenr<6+\fi\fi + [\skvcurrentprefix]{\skvcurrentfamily}% + % ##2 should be inserted below instead of \skvcurrentkey, because + % \skvcurrentkey has had any possible pointer stripped: + \ifnum\skv@typenr>0\ifnum\skv@typenr<6[\skv@hp]\fi\fi + {##2}% + \ifnum\skv@typenr=5% no \relax here. + [\unexpanded{\skvuserinput\skvorder}]{\skvexpandonce\skv@altlista}% + \fi + % default: + \expandafter\skv@ifrejorna\expandafter{\skv@default}{}{% + [{\skvexpandonce\skv@default}]% + }% + {% + % Key callback. If choice key, then the callback is item 5, + % otherwise it's item 4: + \skvifnum\skv@typenr=5{% + \skvifx\skv@itemfive\skv@rej{}{\skvexpandonce\skv@itemfive}% + \skv@executealt{########1}{\skvexpandonce\skv@altlistb}% + }{% + \skvifx\skv@itemfour\skv@rej{}{\skvexpandonce\skv@itemfour}% + }% + }% + % Second branch of callback for '+' variants, now used for all + % boolean and choice keys: + \ifnum\skv@typenr>2\ifnum\skv@typenr<6{\skvkeyvalueerror}\fi\fi + }% + \skv@gobble@nil + }% + \skvexpanded{\skvparselist{\skv@keyparser}}{#1}\skv@prova{% + \expandafter\skv@splita\skv@prova + /^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^\skv@split@nil + }% + % 1. \skv@accumulated is a two-layer stack: there're loops on families + % and keys. + % 2. To save initial values of keys, the key names should be + % entered by \skvsaveinitialvaluekeys in 'needini' list. This is + % the case even when the boolean '.initialize' is true. See below. + % 3. The boolean '.save initial values' is the one that will invoke + % \skvsaveinitialvaluekeys below. + \edef\skv@accumulated{% + \skvexpandonce\skv@accumulated % looping on families + \skvifdefbool{skvdfk@saveinitialvalues}{% + \noexpand\skvsaveinitialvaluekeys[\skvcurrentprefix]% + {\skvcurrentfamily}{\skvexpandonce\skvdfk@keys}% + }{}% + \skvexpandonce\skv@acc@mulated % looping on keys + \skvifdefbool{skvdfk@initialize}{% + \noexpand\skvsetkeys[\skvcurrentprefix]% + {\skvcurrentfamily}{\skvexpandonce\skvdfk@keyvals}% + }{}% + }% +} +% +% Assigning arguments within \skvdefinekeys: +% +% Eg, .args/{key1,key2}/{#1/#2} +% +\skvrobustdef*\skvdfk@definearg#1#2#3{% + \begingroup + \toks@\expandafter{#3}% + \def\skv@prova.arg##1\skv@nil{% + \if\relax\detokenize{##1}\relax + \def\skv@prova{x0}% + \else + \edef\skv@provb{\skvtrimspace{##1}}% + \def\skv@prova####1{% + \def\skv@prova########1####1########2########3\skv@nil{% + \def\skv@prova{########2}% + \ifx\skv@prova\skv@nnil + \def\skv@prova{x0}% + \fi + }% + \skv@prova + expanded{xx}expand once{x1}expand twice{x2}####1{\skv@nil}\skv@nil + }% + \expandafter\skv@prova\expandafter{\skv@provb}% + \fi + }% + \expandafter\skv@prova#1\skv@nil + \skvcsedef{\skv@header#2.@\skv@prova arg}{\the\toks@}% + \expandafter\skvaftergroupdef + \csname\skv@header#2.@\skv@prova arg\endcsname\endgroup +} + +%+++++++++++++++++++++++ Keys for \skvmakekeys +++++++++++++++++++++++% +% Example: +% \skvmakekeys[ +% prefix=KVA, families={fam1,fam2}, hp=thp@, all new, +% define in all families, initialize=true, endlinechar=-1 +% ]{% +% .ords/{keya,keyb}/{default-a}, +% .cmds/{keyc,keyd}/, +% .zcmd/key e/\def\cmde##1{##1}/ +% \edef\y{\detokenize\expandafter{\thp@keye}}\def\x##1{#1*key e*##1}, +% .choice/keyf/center/{center.do=\def\x##1{#1*##1},left,right}, +% .zbool/show center/true/\edef\cmd{\ifthp@showcenter Yes\else No\fi} +% } +% \skvshowcs{KVA/fam1/keyb.@defa} +% \def\keybval{xx} +% \skvsetkeys[KVA]{fam1}{keyb=.expand once{\keybval}} + +\skvedefinekeys*[SKV]{makekeys}[skvmk@]{% + .ords/{.new,.all new,.all are new}/true/ + \edef\skvmk@allnew{\skvifstrcmp{#1}{true}{00}{01}} + , + .ords/{.initialize,.initialize after define}/true/ + \skvifvalidboolval{#1}{% + \csname skvdfk@initialize#1\endcsname + }% + , + .ord/.save initial values/true/ + \skvifvalidboolval{#1}{% + \csname skvdfk@saveinitialvalues#1\endcsname + }% + , + .ords/{.endlinechar,.endline character}/13/ + \def\skvmk@endlinechar{#1}, + .ords/{.define in all families,.in all families}/true/ + \edef\skvmk@inallfams{\skvifstrcmp{#1}{true}{00}{01}} + , + .ord/.prefix/KV/ + \skviflacus#1\then + \def\skvmk@pref{KV}% + \else + \edef\skvmk@pref{#1}% + \skvstripouterbraces{2}\skvmk@pref + \fi + , + .ords/{.holder prefix,.hp}/userhp@/\edef\skvmk@hp{#1}, + .ords/{.family,.families}/^skv^/ + \skviflacus#1\then + \skv@err{Key family is empty\on@line}\@ehd + \else + \edef\skvmk@fams{#1}% + \skvstripouterbraces{2}\skvmk@fams + \fi + , +} +% Default settings for keys of \skvmakekeys. Presetting keys will be +% inefficient here, since many of the keys have complements. It will be +% necessary to include all the complements in the preset list, but doing +% this will be inefficient. +\skvsetkeys[SKV]{makekeys}{% + .prefix,.family=^skv^,.hp,.new=false,.define in all families=false, + .endlinechar=13,.initialize=false,.save initial values=false +} + +\skvrobustdef*\skvmakekeys{\skv@testopt\skv@makekeys{}} +\skvrobustdef*\skv@makekeys[#1]#2{% + \skvpushfunctions\skvmakekeys{% + \do\skvmk@pref\do\skvmk@fams\do\skvmk@allnew\do\skvmk@hp + \do\skvmk@endlinechar\do\skvmk@inallfams\do\ifskvdfk@initialize + \do\ifskvdfk@saveinitialvalues + }\skv@makekeysdepth + \skvsetkeys[SKV]{makekeys}{#1}% + \ifx\skvmk@fams\skv@rej + \skv@err{No family specified for \string\skvmakekeys}\@ehd + \fi + % Push the stack of \skvdefinekeys: + \skv@pushdefinekeys + % The following \begingroup is ended in \skv@definekeys: + \begingroup + \skvexpanded{% + \endlinechar=\skvmk@endlinechar\relax + \skvdfk@testopta\skv@definekeys + \if\skvmk@allnew*\fi\if\skvmk@inallfams+\fi + [\skvmk@pref]{\skvmk@fams}% + [\ifx\skvmk@hp\@empty skvhp@\else\skvmk@hp\fi]% + }% + {#2}% + \skvpopfunctions\skvmakekeys\skv@makekeysdepth +} + +\skvrobustdef*\skvkeyvalueerror{% + \skv@getinnoval\CurrentOption + \skv@err{Erroneous value '\skv@ival' for key or option + \MessageBreak'\skvcurrentkey'} + {Invalid key value encountered.}% +} +\skvrobustdef*\skv@getaltlist#1{% + \begingroup + \def\skv@g@taltlist##1.do=##2.do=##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvoxdetok\skv@prova{\skvtrimspace{##2}}}% + \skvaddtolist*\skv@altlista\skv@prova + \skvaddtolist*\skv@altlistb\skv@provb + }% + \skvcommaparse{#1}\skv@prova{% + \expandafter\skv@g@taltlist\skv@prova.do=.do=\skv@nil + }% + \skvexpanded{\endgroup + \skvcmdexitloop{\skv@altlista\skv@altlistb}% + }% +} +\skvrobustdef*\skv@executealt#1#2{% + \edef\skv@prova{\skvtrimspace{#1}}% + \edef\skv@prova{\skvoxdetok\skv@prova}% + \def\skv@provb##1{% + \def\skv@provb####1##1####2####3\skv@nil{% + \edef\skv@prova{\unexpanded{####2}}% + \skvifx\skv@prova\skv@nnil{% + \skv@err{No alternate list match for key '\skvcurrentkey'}\@ehd + }{% + ####2% + }% + }% + }% + \expandafter\skv@provb\expandafter{\skv@prova}% + \skvexpandsecond{\skv@provb#2}\skv@prova{\skv@nil}\skv@nil +} + +\skvrobustdef*\skv@getinnoval#1{% + \begingroup + \def\skv@prova##1=##2=##3\skv@nil{% + \edef\skv@provb{\detokenize{##2}}% + \skvifx\skv@provb\@empty{% + \def\skv@ival{???}% + }{% + \skv@getbasetoks{30}\skv@provb\skv@ival + }% + }% + \expandafter\skv@prova#1==\skv@nil + \skvaftergroupdef\skv@ival\endgroup +} +% \skv@getbasetoks{<nr>}{<toks.cmd>}{<result.cmd>} +\skvrobustdef*\skv@getbasetoks#1#2#3{% + \begingroup + \edef\skv@prova{\detokenize\expandafter{#2}}% + \def#3{}\@tempcnta\skvz@ + \def\do##1{% + \def\skv@prova{##1}% + \skvifx\skv@prova\skv@nnil{}{% + \advance\@tempcnta\@ne + \ifnum\@tempcnta<#1\relax + \edef#3{#3\ifx\next\@sptoken\@space\fi##1}% + \else + \def\@do####1\skv@nil{}% + \fi + \@do + }% + }% + \def\@do{\futurelet\next\do}% + \expandafter\@do\skv@prova\skv@nil + \skvaftergroupdef#3\endgroup +} + +% +++++++++++ User-defined handlers for unknown keys. +++++++++++++% +% +% \skvunknownkeyhandler[<prefixes>]{<families>}{<handler>} +% +% Note: See skeyval.sty for \skvunknownoptionhandler. +% +% Eg, for keya undefined in KV/fam: +% +% \skvunknownkeyhandler[KV]{fam}{% +% \skvordkey[#1]{#2}{#3}[#4]{\def\x##1{##1xx#4}}% +% } +% \skvsetkeys[KV]{fam}{unknownkey=val} +% +\skvrobustdef*\skvunknownkeyhandler{\skv@testopt\skv@unknownkeyhandler{KV}} +% For each prefix and family, this will define the functions +% \<prefix>/<family>/.@famhandler of 4 parameters (prefix, family, +% key name, current value). +\skvrobustdef*\skv@unknownkeyhandler[#1]#2#3{% + \skvcommaloop{#1}\skv@tempa{% + \skv@makeprefix\skv@tempa + \skvcommaloop{#2}\skv@tempb{% + \skv@makeheader\skv@tempb + \skvcsdef{\skv@header.@famhandler}##1##2##3##4{#3}% + }% + }% +} + +%%++++++++++++++++++++++ Keys for \skvusekeys: +++++++++++++++++++++++%% +% Example: +% \skvusekeys[ +% .prefix=KVA,.families={fam1,fam2},.set in all families, +% .save rm keys +% ]{% +% key1,key2 +% } + +\skvmakekeys[ + .prefix=SKV,.family=usekeys,.hp=skvuk@,.endlinechar=-1 +]{% + .ords/{.save rm,.save rm keys,.save unknown keys}/true/ + \edef\skvuk@saverm{\skvifstrcmp{#1}{true}{00}{01}} + , + .ords/{.set in all families,.in all families}/true/ + \edef\skvuk@inallfams{\skvifstrcmp{#1}{true}{00}{01}} + , + .ord/.prefix/KV/ + \skviflacus#1\then + \def\skvuk@pref{KV}% + \else + \edef\skvuk@pref{#1}% + \skvstripouterbraces{2}\skvuk@pref + \fi + , + .ords/{.family,.families}/^skv^/ + \skviflacus#1\then + \skv@err{Key family is empty\on@line}\@ehd + \else + \edef\skvuk@fams{#1}% + \skvstripouterbraces{2}\skvuk@fams + \fi + , +} +% Default settings for keys of \skvusekeys: +\skvsetkeys[SKV]{usekeys}{% + .prefix,.family=^skv^,.set in all families=false,.save rm=false +} + +\skvrobustdef*\skvusekeys{\skv@testopt\skv@usekeys{}} +\skvrobustdef*\skv@usekeys[#1]#2{% + \skvpushfunctions\skvusekeys{% + \do\skvuk@fams\do\skvuk@pref\do\skvuk@inallfams\do\skvuk@saverm + }\skv@usekeysdepth + \skvsetkeys[SKV]{usekeys}{#1}% + \ifx\skvuk@fams\skv@rej + \skv@err{No family specified for \string\skvusekeys}\@ehd + \fi + \skvexpanded{% + \noexpand\skvsetkeys\if\skvuk@saverm*\fi\if\skvuk@inallfams+\fi + [\skvuk@pref]{\skvuk@fams}{\unexpanded{#2}}% + }% + \skvpopfunctions\skvusekeys\skv@usekeysdepth +} + +%++++++++++ Utilities for handlers of the macro \directkeys +++++++++++% + +\skvnewlet\handlereov\relax +\skvrobustdef*\directkeys{\skv@testst\dirkeys@parse} +\skvnewlet\dirkeys\directkeys +\skvrobustdef*\dirkeys@parse{% + \begingroup + \ifskv@tempst\endlinechar\m@ne\fi + \dirkeys@parse@a +} +\skvrobustdef*\dirkeys@parse@a#1{% + \endgroup + \skvpushstate\dirkeys@state\dirkeys@depth + \indirkeystrue + % Note 1: Initialise all lists to empty. This note is referred to + % somewhere: don't delete its number. + \def\dirkeys@prefixlist{}% + \def\dirkeys@familylist{}% + \def\dirkeys@holderprefixtoks{}% + \dirkeys@saveunknownkeysfalse + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + \skvkvnormalize\skv@tempa + \def\dirkeys@do##1=##2=##3\dirkeys@nil{% + \edef\skv@tempa{\unexpanded{##2}}% + \skvstripouterbraces{2}\skv@tempa + \skvdespacecontent\skv@tempa + % Get the first token, to test if it's \dirkeys. If it is, then + % we have a nested \dirkeys: + \expandafter\skvifx\skv@car##1x\car@nil\dirkeys{% + \skvifempty\skv@tempa{% + ##1\relax + }{% + \skv@err{Something wrong with use of \string\dirkeys: + \MessageBreak equals ('=') found after \string\dirkeys}\@ehd + }% + }{% + \skv@ensurehandlerdot{##1}% + \skvifcsdef{dirkeys@handler@##1}{% + \csname dirkeys@handler@##1\expandafter\endcsname + \skv@tempa\handlereov + }{% + \skv@err{Unknown handler '\detokenize{##1}'\MessageBreak + in command \string\dirkeys; or no '=' sign}\@ehd + }% + }% + }% + \skvexpanded{\skvparselist*{\skv@keyparser}}\skv@tempa\skv@tempa{% + \expandafter\dirkeys@do\skv@tempa==\dirkeys@nil + }% + \indirkeysfalse + \skvpopstate\dirkeys@state\dirkeys@depth +} +\skvrobustdef*\dirkeys@setdefaultroot{% + \ifx\dirkeys@familylist\@empty + \ifskv@inopt + \edef\dirkeys@familylist{\@currname.\@currext}% + \else + \skv@err{No key's family can be empty}\@ehd + \fi + \fi + \ifx\dirkeys@prefixlist\@empty + \def\dirkeys@prefixlist{KV}% + \fi + \ifx\dirkeys@holderprefixtoks\@empty + \def\dirkeys@holderprefixtoks{user@}% + \fi +} +\skvrobustdef*\dirkeys@getrootlist{% + \def\skv@rootlist{}% + \def\do##1##2{% + \edef\skv@rootlist{% + \skvexpandonce\skv@rootlist + \noexpand\skv@doonroot{##1}{##2}% + }% + }% + \dirkeys@getcurrentroots\do +} +\skvrobustdef*\dirkeys@getcurrentroots#1{% + \begingroup + \dirkeys@setdefaultroot + \def\skv@tempa{}% + \skvcommaloop*\dirkeys@prefixlist\skv@prova{% + \skvcommaloop*\dirkeys@familylist\skv@provb{% + \edef\skv@tempa{\skvexpandonce\skv@tempa + \unexpanded{#1}{\skv@prova}{\skv@provb}}% + }% + }% + \expandafter\endgroup\skv@tempa +} + +\skvrobustdef*\dirkeys@dodefinekeys#1{% + \begingroup + % #1 may contain parameter characters that may be confused with + % ##1 and ##2 below. Hence we put it in the macro \skv@tempb. + % Ignored keys are always defined. This saves resources and has + % so far been harmless. + \edef\skv@tempb{\unexpanded{#1}}% + \def\skv@tempa{}% + \def\do##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \skvdefinekeys\ifskv@handlerswa*\fi[##1]{##2}% + [\dirkeys@holderprefixtoks]{\skvexpandonce\skv@tempb}% + }% + }% + \dirkeys@getcurrentroots\do + \expandafter\endgroup\skv@tempa +} +\skvrobustdef*\dirkeys@dosetkeys#1#2{% + \begingroup + % #1 may contain parameter characters that may be confused with + % ##1 and ##2 below. Hence we put it in the macro \skv@tempb: + \edef\skv@tempb{\unexpanded{#1}}% + \def\skv@tempa{}% + \def\do##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + #2\ifdirkeys@saveunknownkeys*\else\ifskv@handlerswa*\fi\fi + [##1]{##2}[\skvcsuse{##1/##2/.@ignoredkeys}]% + % #1 may contain unbalanced conditionals; hence the need for + % \skvifdefbool here. When setting 'rm keys', then no + % key list is needed here. + \skvifdefbool{skv@handlerswb}{}{{\skvexpandonce\skv@tempb}}% + }% + }% + \dirkeys@getcurrentroots\do + \expandafter\endgroup\skv@tempa +} + +% This is used by prepostsetkeys, etc. +% \dirkeys@dolistedkeys@a{<key.list>}{<callback>} +\skvrobustdef*\dirkeys@dolistedkeys@a#1#2{% + \begingroup + \def\skv@tempd{}% + % ##1=prefix, ##2=family + \def\do##1##2{% + \def\skv@tempb{}% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaparse{#1}\skv@tempa{% + \skvxifin{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@igkeys,}{}{% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb\ifx\skv@tempb\@empty\else,\fi + \skvexpandonce\skv@tempa + }% + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvifempty\skv@tempb{}{% + \noexpand#2[##1]{##2}{\skvexpandonce\skv@tempb}% + }% + }% + }% + \dirkeys@getcurrentroots\do + \expandafter\endgroup\skv@tempd +} + +% \dirkeys@dolistedkeys@b{<callback>} +\skvrobustdef*\dirkeys@dolistedkeys@b#1{% + \begingroup + % ##1=prefix, ##2=family + \def\skv@tempa{}% + \def\do##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \unexpanded{\skv@makeprefix{##1}\skv@makeheader{##2}#1}% + }% + }% + \dirkeys@getcurrentroots\do + \expandafter\endgroup\skv@tempa +} +\skvrobustdef*\skv@getexpander#1{% + \begingroup + \edef\skv@prova{\skvtrimspace{#1}}% + \def\skv@provb##1{% + \def\skv@provb####1##1####2####3\skv@nil{% + \endgroup + % Don't use \let for the next definition: + \def\skvexpander{####2}% + \ifx\skvexpander\@nnil + \skv@err{Unknown expansion type '\detokenize{##1}'}\@ehd + \fi + }% + \skv@provb x0{\unexpanded}x1{\skvexpandonce}x2{\skvexpandtwice}% + xx{\@iden}##1{\@nil}\skv@nil + }% + \expandafter\skv@provb\expandafter{\skv@prova}% +} +\skvrobustdef*\skv@getmaintoks#1#2{% + \def\skv@prova##1#2##2#2##3\skv@nil{% + \edef\skvcurrentmaintoks{\skvtrimspace{##1}}% + \skvstripouterbraces{2}\skvcurrentmaintoks + \ifx\skvcurrentmaintoks\@empty + \skv@err{No main key in '\detokenize{#1}'}\@ehd + \fi + \edef\skvcurrentsub{\unexpanded{##2}}% + \ifx\skvcurrentsub\skv@novaluetoks + \def\skvcurrentsub{}% + \else\skvafterfi + \skvdespacecontent\skvcurrentsub + \skvstripouterbraces{2}\skvcurrentsub + \fi + }% + \skv@prova#1#2\skv@novalue#2\skv@nil +} + +% Assigning admissible values to keys. The handlers here are: +% +% .choice, .choice expanded, .choice expanded once, .choice expanded twice. +% +% \dirkeys@savechoice{<list>}{<expander.type>} +% +% 1. <list> has the form +% +% <key1>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}},..., +% <key-n>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}} +% +% 2. If it didn't exist, the main key (eg, the above <key1>) will be +% defined with an empty default value and an empty callback. +% +\skvrobustdef*\dirkeys@savechoice#1#2{% + \begingroup + \dirkeys@getrootlist + \def\skv@tempd{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\dirkeys@s@vechoice\skv@tempa{#2}% + }% + \expandafter\endgroup\skv@tempd +} +\skvrobustdef*\dirkeys@s@vechoice#1#2{% + \skv@getmaintoks{#1}{/}% + \skvkvnormalize\skvcurrentsub + \def\skv@doonroot##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvxifin{,\skvcurrentmaintoks,}{,\skv@igkeys,}{}{% + \skvifcsdef{##1/##2/\skvcurrentmaintoks.@cbk}{}{% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvordkey[##1]{##2}{\skvcurrentmaintoks}[]{}% + }% + }% + \def\skv@choicelist{}% + \skvcommaloop*\skvcurrentsub\skv@tempa{% + \def\skv@prova####1.do=####2.do=####3\skv@nil{% + \edef\skv@choicelist{% + \skvexpandonce\skv@choicelist + {\skvtrimspace{####1}}{\skvtrimspace{####2}}% + }% + }% + \expandafter\skv@prova\skv@tempa.do=.do=\skv@nil + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvappendkeycode[##1]{##2}{\skvcurrentmaintoks}% + {\dirkeys@executechoice{#2}{########1}% + {\skvexpandonce\skv@choicelist}}% + }% + }% + \skv@rootlist +} +% \dirkeys@executechoice{<expansion.type>}{<curr.value>}{<choices.and.cbks>} +\skvrobustdef*\dirkeys@executechoice#1#2#3{% + \skv@getexpander{#1}% + \edef\skv@prova{\skvexpander{#2}}% + \skvdespacecontent\skv@prova + \def\do##1##2##3\skv@nil{% + \edef\skv@provb{\unexpanded{##1}}% + \skvifx\skv@provb\skv@prova{% + ##2% + }{% + \skvifblank{##3}{% + \skv@err{No choice match found for key '\skvcurrentkey'}\@ehd + }{% + \do##3\skv@nil + }% + }% + }% + \do#3\skv@nil +} + +% The handlers here are: +% +% .style, .style expanded, .style expanded once, .style expanded twice. +% +% Note that the handlers are preceded by 'append' or 'prepend'. +% +% \dirkeys@savestyle{<list>}{<append.or.prepend>}{<expander.type>} +% +% 1. <list> has the form +% +% {<main key1>,...,<main key-n>}/{<style1=val1>,...,<style-n=val-n>} +% ,..., +% {<main key1>,...,<main key-n>/{<style1=val1>,...,<style-n=val-n>} +% +% 2. Here the styles are linked with each main key. When the main key is +% set, the associated styles will be set with the given values. +% +% 3. Examples: +% +% a) The following example means that keyc and keyd are representations +% for the actions on their right (ie, for setting 'keya=\someright, +% keyb=\keybval') when keyc or keyd is set. The values of keya and +% keyb will be expanded twice before the keys are set: +% +% .append style expand twice={keyc,keyd}/{keya=\someright,keyb=\keybval}. +% +% b) In the following example, #1 will be the value of keye and/or keyf +% when they're set. The values of keye and keyf will be fully expanded +% before they're assigned to keyc and keyd: +% +% .prepend style expanded={keye,keyf}/{keyc=#1,keyd=#1} +% +\skvrobustdef\dirkeys@savestyle#1#2#3{% + \begingroup + \dirkeys@getrootlist + \skv@getexpander{#3}% + \def\skv@tempd{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\dirkeys@s@vestyle\skv@tempa{#2}% + }% + \expandafter\endgroup\skv@tempd +} +\skvrobustdef\dirkeys@s@vestyle#1#2{% + \skv@getmaintoks{#1}{/}% + \skvcsvnormalize\skvcurrentmaintoks + \skvkvnormalize\skvcurrentsub + \def\skv@doonroot##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaloop*\skvcurrentmaintoks\skv@maintoks{% + \skvxifin{,\skv@maintoks,}{,\skv@igkeys,}{}{% + \skvifcsdef{##1/##2/\skv@maintoks.@cbk}{}{% + % Link keys should always be set with a value. If they were + % undefined before being designated as a link key, then + % clearly they should have no default. + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvordkey[##1]{##2}{.value required{\skv@maintoks}}{}% + }% + }% + \def\skv@stylelist{}% + \skvcommaloop*\skvcurrentsub\skv@tempa{% + \def\skv@prova####1=####2=####3\skv@nil{% + \skvxifstrcmp{\skv@maintoks}{####1}{% + \skv@err{Linking key '####1' to itself}\@ehd + }{% + \skvifcsdef{##1/##2/####1.@cbk}{}{% + \skv@err{Style key '####1' is undefined}\@ehd + }% + }% + \skvxifin{,####1,}{,\skv@igkeys,}{}{% + \edef\skv@stylelist{% + \skvaddlist,\skv@stylelist\unexpanded{####1}=% + \skvexpandonce\skvexpander{\unexpanded{####2}}% + }% + }% + }% + \expandafter\skv@prova\skv@tempa==\skv@nil + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + % The styles contain value expanders \@firstofone, \unexpanded, etc. + % They will be expanded by the next \skvexpanded when the main key + % is set. + \skvifstrcmp{#2}{app}\skvappendkeycode\skvprependkeycode + [##1]{##2}{\skv@maintoks}% + {\skvexpanded{\skvsetkeys[##1]{##2}% + {\skvexpandonce\skv@stylelist}}}% + }% + }% + }% + \skv@rootlist +} + +%% +++++++++++++++ Linking keys: +% +% The handlers here are: +% +% .link, .link expanded, .link expanded once, .link expanded twice. +% +% \dirkeys@savestyle{<list>}{<append.or.prepend>}{<expander.type>} +% +% 1. <list> has the form +% +% {<linkkey-1>,..,<linkkey-n>}/{<parentkey-1>,...,<parentkey-n>} +% +% 2. The parent key will be set (with the value of link key) whenever the +% link key is set. +% 3. If the link key didn't exist, it will be defined as an ordinary key +% with the default value of the parent key. In this way, setting a +% link key without value won't give rise to a 'no value and no default +% error'. +% 4. Example: Link keyb and keyc to existing keya: +% +% .link={keyb,keyc}/keya +% +% This is equivalent to: +% +% .style={{keyb,keyc}/keya=#1} +% +% This will link keyb and keyc to keya, such that when keyb and +% keyc are set, keya too will be set with their values. +% +\skvrobustdef\dirkeys@savelink#1#2#3{% + \begingroup + \def\skv@tempe{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\skv@getmaintoks\skv@tempa{/}% + \skvcsvnormalize\skvcurrentmaintoks + \def\skv@tempd{}% + \skvcommaparse*\skvcurrentsub\skv@tempb{% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd\ifx\skv@tempd\@empty\else,\fi + \skvexpandonce\skv@tempb=####1% + }% + }% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe\ifx\skv@tempe\@empty\else,\fi + {\skvexpandonce\skvcurrentmaintoks}/{\skvexpandonce\skv@tempd}% + }% + }% + \expandafter\endgroup\expandafter\dirkeys@savestyle + \expandafter{\skv@tempe}{#2}{#3}% +} + +% #1: <key-1>=<arg-1>,...,<key-n>=<arg-n> +% #2: <arg.expander.type> +% +\skvrobustdef*\dirkeys@assignarg#1#2{% + \begingroup + % Don't take #1 inside \skv@doonroot because it contains parameters: + \edef\skv@tempd{\unexpanded{#1}}% + \def\skv@tempe{}% + \skvkvnormalize\skv@tempd + \def\skv@doonroot##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaloop*\skv@tempd\skv@tempa{% + \def\skv@prova####1=####2=####3\skv@nil{% + \skvxifin{,####1,}{,\skv@igkeys,}{}{% + \skvifcsdef{##1/##2/####1.@cbk}{% + \skvifblank{####2}{}{% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe\unexpanded{% + \skvcsedef{##1/##2/####1.@#2arg}{\unexpanded{####2}}% + }% + }% + }% + }{% + \skv@err{Key '####1' is undefined in family '##1/##2'}\@ehd + }% + }% + }% + \expandafter\skv@prova\skv@tempa==\skv@nil + }% + }% + \dirkeys@getcurrentroots\skv@doonroot + \expandafter\endgroup\skv@tempe +} + +% Use the default values of <keys> to set the keys. +% \dirkeys@setwithdefaults[<pref>]{<fam>}{<keys>} +\skvrobustdef*\dirkeys@setwithdefaults[#1]#2#3{% + \begingroup + \def\skv@defaultlist{}% + \skvcommaparse{#3}\skvcurrentkey{% + \skvletcs\skv@prova{#1/#2/\skvcurrentkey.@defa}% + \skvifdef\skv@prova{% + \skv@getdefault{#1}{#2}\skvcurrentkey\skvcurrentvalue + \edef\skv@defaultlist{% + \skvaddlist,\skv@defaultlist + \skvcurrentkey=\skvexpandonce\skvcurrentvalue + }% + }{% + \skv@err{No default value for key '\skvcurrentkey' + \MessageBreak in family '#1/#2'}\@ehd + }% + }% + \skvexpanded{\endgroup + \skvsetkeys*+[#1]{#2}{\skvexpandonce\skv@defaultlist}% + }% +} + +\skvrobustdef*\dirkeys@dounknownkeyhandler#1{% + \begingroup + \def\skv@tempa{}\def\skv@tempb{}% + \def\do##1##2{% + \skvxifin{,##1,}{,\skv@tempa,}{}{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \ifx\skv@tempa\@empty\else,\fi##1% + }% + }% + \skvxifin{,##2,}{,\skv@tempb,}{}{% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \ifx\skv@tempb\@empty\else,\fi##2% + }% + }% + }% + \dirkeys@getcurrentroots\do + \skvexpanded{\endgroup + \skvunknownkeyhandler\ifskv@handlerswa+\fi[\skv@tempa]{\skv@tempb}% + }{#1}% +} + +%%++++++++++++++++++++++++++ Try setting keys +++++++++++++++++++++++%% +% +% \skvtrysetkeys[<options>]{<keyvals>} +% .try set keys={<options>}{<keyvals>} +% +% Try to set a given key-value list on the paths given by the independent +% options '.prefix/.prefixes' and '.family/.families' of \skvtrysetkeys. If +% called within \directkeys (via the handler '.try set keys'), \skvtrysetkeys +% will ignore the paths that are currently prevailing within \directkeys. +% That is, within \directkeys, the handler '.try set keys' acts independent +% of local circumstances of \directkeys. +% +% Save the known/found keys in the macro suggested by the option +% <success list macro>. The user can inspect this macro to see the found +% keys. When many families are given by the user, the success list is likely +% to be smaller than failed list. +% +% Raise an error if the number of successful families is less than +% the minimum threshold <min success>. The default value of <min success> +% is 1. The options <min success> and <lower goal> are synonymous. +% +% \skvtrysetkeys is similar to \skvsetkeys*+ (ie, \skvsetkeys with the +% options *+), but in this case we have <try> (ie, the maximum total number +% of attempts - families to search for a given key) before backing off, +% and <upper goal> (the maximum number of successful families in which a key +% should be set). +% +% \skvtrysetkeys too will set existing preset and postset keys in the +% given families. +% +% The options of \skvtrysetkeys are: +% +% .try: +% The maximum number of the sum of successful and unsuccessful +% attempts that should be made to set a key in the given SFs. The +% default value of <try> is 12. +% .upper goal/.max success: +% The max. number of search families (SF) in which a given key should +% be set/executed, if the key existed in those families. The number of +% SFs may be more than <upper goal>, but the key may not need to be +% executed in all of them. The default value of <upper goal> is 1. +% .lower goal/.min success: +% The min. number of search families (SF) in which a given key should +% be set/executed. For any key, if <hits> is less than <lower goal> +% an error will be flagged. +% .prefix/.prefixes: +% The prefixes in which the keys should be set (default=KV). +% .families/.family: +% Families to search. +% .ignore keys: +% Ignored keys. These keys will not be set even if they are found. +% .save known keys: +% The boolean that directs that known/found keys should be saved +% in a macro. +% .save known keys in/success list macro/success list: +% The macro into which to save known keys. Each known key is +% saved in the format: <pref>/<fam>/<key>. The user can +% inspect this macro to see the found keys. The default macro is +% \skvsuccesslist. +% +% NOTE: +% +% When the paths are many, the user has to be right in choosing the +% options '.try', '.upper goal' and '.lower goal', otherwise not enough paths +% will be searched. For example, if you want some 'keya' to be set on +% paths KV1/fam1, KV1/fam2, KV2/fam1, KV2/fam2, and you set '.try=2' or +% '.upper goal=2', then only 2 paths will be traversed; not the desired 4. + +\skvedefinekeys[SKV]{skvtryset}[skvtry@]{% + .ords/{.try,.max attempt,.max attempts,.total}/12/ + \edef\skvtry@maxattempts{\number#1}% + \skvensureinteger{.max attempt}\skvtry@maxattempts + \ifnum#1<\@ne + \skv@err{The max. number of attempts ('try') + \MessageBreak can't be less than 1}\@ehd + \fi + , + .ords/{.upper goal,.max success,.max hits}/1/ + \edef\skvtry@maxsuccess{\number#1}% + \skvensureinteger{upper goal}\skvtry@maxsuccess + \ifnum#1<\@ne + \skv@err{The max. number of successful attempts + \MessageBreak ('upper goal') can't be less than 1}\@ehd + \fi + , + .ords/{.min success,.lower goal,.goal}/1/ + \edef\skvtry@minsuccess{\number#1}% + \skvensureinteger{lower goal}\skvtry@minsuccess + \ifnum#1<\@ne + \skv@err{The min. number of successful attempts + \MessageBreak ('lower goal') can't be less than 1}\@ehd + \fi + , + .ords/{.prefix,.prefixes,.add prefix,.add prefixes}/KV/ + \edef\skvtry@prefs{#1} + \skvstripouterbraces{2}\skvtry@prefs + \edef\skvtry@prefs{\skvifempty\skvtry@prefs{KV}{\skvtry@prefs}}% + \ifskv@insa + \skvexpbracenext{\skvmergelist\skvtry@prefs}\dirkeys@prefixlist + \fi + \skvcsvnormalize\skvtry@prefs + , + .ords/{.family,.families,.add family,.add families}/^skv^/ + \edef\skvtry@fams{#1} + \ifx\skvtry@fams\skv@rej\else + \skvstripouterbraces{2}\skvtry@fams + \ifskv@insa + \skvexpbracenext{\skvmergelist\skvtry@fams}\dirkeys@familylist + \fi + \skvcsvnormalize\skvtry@fams + \fi + , + .ords/{.path,.paths,.add path,.add paths,.root,.roots}/^skv^/ + \edef\skv@tempa{\skvtrimspace{#1}} + \skvstripouterbraces{2}\skv@tempa + \ifcase0\ifx\skv@tempa\skv@rej1\else + \ifx\skv@tempa\@empty1\fi\fi\relax\skvafterfi + \skvexpbracenext\skvtry@fixpaths\skv@tempa + \fi + , + .ords/{.ignore keys,.filter keys}// + \def\skvtry@igkeys{#1} + \skvstripouterbraces{2}\skvtry@igkeys + , + .ords/{.save known keys,.save success}/true/ + \edef\skvtry@savesuccess{0\skvifstrcmp{#1}{true}01} + , + .ords/{.save known keys in,.success list,.success list macro}/ + \skvsuccesslist/\def\skvtry@successlistmacro{#1} + , +} +\skvrobustdef*\skvtry@fixpaths#1{% + \def\skvtry@prefs{}% + \def\skvtry@fams{}% + \def\skv@tempc##1/##2/##3\skv@nil{% + \def\skv@tempa{##1}% + \def\skv@tempb{##2}% + \skvstripouterbraces{2}\skv@tempa + \skvstripouterbraces{2}\skv@tempb + \edef\skvtry@prefs{% + \skvtry@prefs\ifx\skvtry@prefs\@empty\else,\fi\skv@tempa + }% + \edef\skvtry@fams{% + \skvtry@fams\ifx\skvtry@fams\@empty\else,\fi\skv@tempb + }% + }% + \skvcommaparse{#1}\skv@prova{% + \expandafter\skv@tempc\skv@prova//\skv@nil + }% + \ifskv@insa + \skvexpbracenext{\skvmergelist\skvtry@prefs}\dirkeys@prefixlist + \skvexpbracenext{\skvmergelist\skvtry@fams}\dirkeys@familylist + \fi + \skvcsvnormalize\skvtry@prefs + \skvcsvnormalize\skvtry@fams +} +\skvsetkeys[SKV]{skvtryset}{.try,.upper goal,.lower goal,.prefix, + .family=^skv^,.ignore keys,.save success,.success list macro +} + +% \skvtrysetkeys[<options>]{<kvlist>} +\skvrobustdef*\skvtrysetkeys{\skv@testopt\skv@trysetkeys{}} +\skvrobustdef*\skv@trysetkeys[#1]#2{% + \skvpushfunctions\skvtrysetkeys{% + % Don't include \skvsuccesslist here! + \do\skvtry@fams\do\skvtry@prefs\do\skvtry@maxattempts + \do\skvtry@minsuccess\do\skvtry@maxsuccess\do\skvtry@igkeys + \do\skvtry@successlistmacro\do\skvtry@savesuccess + }\skv@trysetkeysdepth + \skvsetkeys[SKV]{skvtryset}{#1}% + \ifx\skvtry@fams\skv@rej + \skv@err{No families in which to 'try' to set keys}\@ehd + \fi + \skv@getkeynames{#2}\skv@naa + \ifnum\skv@trysetkeysdepth=\@ne + \expandafter\def\skvtry@successlistmacro{}% + \def\skv@tempsuccesslist{}% + \fi + \skv@intrytrue + \skvcommaparse{#2}\CurrentOption{% + \expandafter\skv@g@tkeyname\CurrentOption=\skv@nil\skvcurrentkey + \skvxifin{,\skvcurrentkey,}{,\skvtry@igkeys,}{}{% + \skv@successfalse + % Don't use \chardef here; it won't printed in the error message + % if it wasn't incremented: + \def\skv@attempts{0}% + \def\skv@hits{0}% + \def\skv@breakprefloop{01}% + \skvcommaloop*\skvtry@prefs\skvcurrentprefix{% + \skvcommaloop*\skvtry@fams\skvcurrentfamily{% + \skvpushfunctions\skv@trysetkeys@istate{% + \do\CurrentOption\do\skvcurrentkey\do\skvcurrentprefix + \do\skvcurrentfamily\do\skv@attempts\do\skv@hits + \do\skv@breakprefloop\do\ifskv@success + }\skv@trysetkeysdepth@b + \skvexpanded{% + % Don't use \skv@setkeys@a here: \skv@fams, etc. + % will be mixed up. + \skvsetkeys[\skvcurrentprefix]{\skvcurrentfamily}% + {\skvexpandonce\CurrentOption}% + }% + \skvpopfunctions\skv@trysetkeys@istate\skv@trysetkeysdepth@b + \edef\skv@attempts{\the\numexpr\skv@attempts+1}% + \ifskv@success + \edef\skv@hits{\the\numexpr\skv@hits+1}% + \if\skvtry@savesuccess + \edef\skv@tempsuccesslist{% + \skvaddlist,\skv@tempsuccesslist\skvcurrenttriple + }% + \fi + \fi + \ifnum\skv@hits<\skvtry@maxsuccess\relax + \ifnum\skv@attempts<\skvtry@maxattempts\relax\else + \skvbreakloop + \def\skv@breakprefloop{00}% + \fi + \else + \skvbreakloop + \def\skv@breakprefloop{00}% + \fi + }% + \if\skv@breakprefloop + \skvbreakloop + \fi + }% + \ifnum\skv@hits<\skvtry@minsuccess\relax + \skv@err{Success target '\skvtry@minsuccess' wasn't reached + \MessageBreak for key '\skvcurrentkey' by + \ifskv@insa'.search also set'\else + \noexpand\skvtrysetkeys\fi. + \MessageBreak Actual hits were '\skv@hits'}\@ehd + \fi + }% + }% + \expandafter\let\skvtry@successlistmacro\skv@tempsuccesslist + \ifnum\skv@trysetkeysdepth=\@ne + \skv@intryfalse\skv@insafalse + \let\do\skvundef + \do\skv@breakprefloop\do\skv@attempts\do\skv@hits + \do\skv@tempsuccesslist + \fi + \skvpopfunctions\skvtrysetkeys\skv@trysetkeysdepth +} + +%%+++++++++++++++++++++++++ 'search also' set keys +++++++++++++++++++%% +% +% \skvsasetkeys[<options>]{<keyvals>} +% .sa set keys={<options>}{<keyvals>} +% +% 1. The only difference between \skvsasetkeys and \skvtrysetkeys is that, +% when in \directkeys, the values of the options <prefix/prefixes> and +% <family/families> of \skvsasetkeys are added to the current +% active prefixes and families from the lists \dirkeys@prefixlist and +% \dirkeys@familylist. Outside of \directkeys, the commands +% \skvsasetkeys and \skvtrysetkeys are the same. +% +% 2. All the options of \skvtrysetkeys apply to \skvsasetkeys. + +\skvnewlet\skvsasetkeys\skvtrysetkeys +\skvrobustdef*\dirkeys@dosasetkeys#1{% + \skv@insatrue + \skvifundef\dirkeys@familylist{\def\dirkeys@familylist{}}{}% + \skvifundef\dirkeys@prefixlist{\def\dirkeys@prefixlist{}}{}% + \skv@trysetkeys[#1]% +} + +% \dirkeys@addorremoveroots{<list>}{<operator>} +\skvrobustdef*\dirkeys@addorremoveroots#1#2{% + \edef\skv@prova{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@prova + \def\skv@tempa{}\def\skv@tempb{}% + \skvcommaparse*\skv@prova\skv@prova{% + \def\skv@provb##1/##2/##3\skv@nil{% + \skvxifin{,##1,}{,\skv@tempa,}{}{% + \edef\skv@tempa{\skv@tempa\ifx\skv@tempa\@empty\else,\fi##1}% + }% + \skvxifin{,##2,}{,\skv@tempb,}{}{% + \edef\skv@tempb{\skv@tempb\ifx\skv@tempb\@empty\else,\fi##2}% + }% + }% + \expandafter\skv@provb\skv@prova//\skv@nil + }% + \skv@clfalse + \skvexpbracenext{#2\dirkeys@prefixlist}\skv@tempa + \skvexpbracenext{#2\dirkeys@familylist}\skv@tempb +} + + +% \dirkeys@addtocodeorvalue{app/pre}{cbk/value}{<list>} +\skvrobustdef*\dirkeys@addtocodeorvalue#1#2#3{% + \begingroup + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#3}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\do##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@kvsplit\expandafter{\skv@tempa}{% + \skvxifin{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvletcs\skv@prova{##1/##2/####1.@#2}% + \skvifundef\skv@prova{% + \skv@err{Key '####1' isn't in family '##1/##2'}\@ehd + }{% + \skvcsedef{##1/##2/####1.@#2}{% + \skvifstrcmp{#1}{pre}{% + \skvtrimspace{####2}\skvexpandonce\skv@prova + }{% + \skvexpandonce\skv@prova\skvtrimspace{####2}% + }% + }% + % To take the new macros outside the group: + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@#2}% + }% + }% + }% + }% + }% + }% + \dirkeys@getcurrentroots\do + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@assigndefault{<kvlist>} +\skvrobustdef*\dirkeys@assigndefault#1{% + \begingroup + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#1}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\do##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@kvsplit\expandafter{\skv@tempa}{% + \skvxifin{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvifcsdef{##1/##2/####1.@cbk}{% + \skvcsedef{##1/##2/####1.@defa}{% + \skvnoexpandcs{##1/##2/####1}{\skvtrimspace{####2}}% + }% + % To take the new defaults outside the group: + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@defa}% + }% + }{% + \skv@err{Key '####1' is not in family '##1/##2'}\@ehd + }% + }% + }% + }% + }% + \dirkeys@getcurrentroots\do + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@assignvalue{<kvlist>} +\skvrobustdef*\dirkeys@assignvalue#1{% + \begingroup + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#1}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\do##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@kvsplit\expandafter{\skv@tempa}{% + \skvxifin{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvifcsdef{##1/##2/####1.@cbk}{% + \skvcsedef{##1/##2/####1.@value}{\skvtrimspace{####2}}% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@value}% + }% + }{% + \skv@err{Key '####1' isn't in family '##1/##2'}\@ehd + }% + }% + }% + }% + }% + \dirkeys@getcurrentroots\do + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@inherit{value/cbk/defa}{<list>} +\skvrobustdef*\dirkeys@inherit#1#2{% + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#2}}% + % ##1=prefix, ##2=family + \def\do##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdef\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@slsplit\expandafter{\skv@tempa}{% + \skvxifin{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \ifcase\skvifcsdef{##1/##2/####1.@cbk}{0}{1}% + \skvifcsdef{##1/##2/####2.@cbk}{0}{1}\relax + \skvcsletcs{##1/##2/####1.@#1}{##1/##2/####2.@#1}% + \else + \skv@err{Key '####1' or '####2' isn't in family '##1/##2'}\@ehd + \fi + }% + }% + }% + }% + \dirkeys@getcurrentroots\do +} + +% \dirkeys@setbooleanhandler{<handler>}{<boolean>}{<bool value>} +\skvrobustdef*\dirkeys@setbooleanhandler#1#2#3{% + \skvifblank{#3}{% + \csname#2true\endcsname + }{% + \skvifboolval{#3}{% + \csname#2#3\endcsname + }{% + \skv@err{Invalid value '\detokenize{#3}' for handler '#1': + \MessageBreak its expects either 'true' or 'false'}\@ehd + }% + }% +} + +\skvrobustdef*\skv@ensurehandlerdot#1{% + \if.\skv@car@detok#1x\car@nil\else + \skv@err{A dot (.) must lead handler name '#1'}\@ehd + \fi +} +\skvrobustdef*\skv@handlerloop#1{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + \skvkvparse*\skv@tempa +} + +% \dirkeys@defhandlers{<marker>}{<list>} +% <marker> indicates if we're in <new> or <define> handler. +\skvrobustdef*\dirkeys@defhandlers#1#2{% + \def\do##1=##2=##3\handler@nil{% + \skv@ensurehandlerdot{##1}% + \if0#1% + \skvcsdef{dirkeys@handler@##1}####1{##2}% + \else + \skvifcsdef{dirkeys@handler@##1}{% + \skv@err{Handler '##1' arealdy exists}\@ehd + }{% + \skvcsdef{dirkeys@handler@##1}####1\handlereov{##2}% + }% + \fi + }% + \skv@handlerloop{#2}\skv@tempa{% + \expandafter\do\skv@tempa==\handler@nil + }% +} + +% Define handlers of more than one argument. This is slightly more +% expensive than '.new handlers': +% \dirkeys@defnarghandlers{<marker>}{<list>} +% <marker> indicates if we're in <new> or <define> handler. +\skvrobustdef*\dirkeys@defnarghandlers#1#2{% + \def\do##1=##2=##3\handler@nil{% + \skv@ensurehandlerdot{##1}% + \def\skv@tempa####1####2\handler@nil{% + % ####2 must be hidden in a macro for later use in defining the + % handler: + \edef\skv@prova{\unexpanded{####2}}% + \edef\skv@tempa{\skvgenerateparameters{1}{####1}\handlereov}% + \def\skv@provb{% + \edef\skv@tempb{\protected\def\skvnoexpandcs{dirkeys@handler@##1}}% + \expandafter\expandafter\expandafter\skv@tempb\expandafter + \skv@tempa\expandafter{\skv@prova}% + }% + \if0#1% + \skv@provb + \else + \skvifcsdef{dirkeys@handler@##1}{% + \skv@err{Handler '##1' arealdy exists}\@ehd + }{% + \skv@provb + }% + \fi + }% + \skv@tempa##2\handler@nil + }% + \skv@handlerloop{#2}\skv@tempa{% + \expandafter\do\skv@tempa==\handler@nil + }% +} +\skvrobustdef*\skvusehandler#1#2{% + \skv@ensurehandlerdot{#1}% + \skvifcsdef{dirkeys@handler@#1}{% + \csname dirkeys@handler@#1\endcsname#2\handlereov + }{% + \skv@err{Handler '#1' is undefined}\@ehd + }% +} + +% The macro that contains the default command in which unknown keys +% will be saved by some handlers of \dirkeys: +\skvnewdef*\dirkeys@unknownkeysmacro{\skvfaillist} + +% Notes: +% 1. '.new handlers' must be defined outsied \dirkeys, since it is +% the one that defines every other handler. +% 2. It defines definable handlers of one or more arguments. See also +% '.define handlers' later on (within the next call of \dirkeys). +\skvrobustcsdef*{dirkeys@handler@.new handlers}#1\handlereov{% + \dirkeys@defhandlers{1}{#1}% +} +% In case one gives no value to a handler: +\skvundef\dirkeys@ +\dirkeys*{% + .new handlers={% + .exec code=#1, + .define handlers=\dirkeys@defhandlers{0}{#1}, + .define narg handlers=\dirkeys@defnarghandlers{0}{#1}, + .new narg handlers=\dirkeys@defnarghandlers{1}{#1}, + .kill handlers={% + \skv@handlerloop{#1}\skv@tempa{% + \skvifcsdef{dirkeys@handler@\skv@tempa}{% + \skvcsundef{dirkeys@handler@\skv@tempa}% + }{% + \skv@err{Handler '\skv@tempa' is undefined}\@ehd + }% + }% + }, + .use handler=\skvusehandler{#1}, + .handler let={% + \def\do##1=##2=##3\handler@nil{% + \skvifcsdef{dirkeys@handler@##1}{% + \skv@err{Handler '##1' arealdy exists}\@ehd + }{% + \skvifcsundef{dirkeys@handler@##2}{% + \skv@err{Handler '##2' doesn't exist}\@ehd + }{% + \skvifblank{##1}{% + \skv@err{No child handler}\@ehd + }{% + \skvcsletcs{dirkeys@handler@##1}{dirkeys@handler@##2}% + }% + }% + }% + }% + \skv@handlerloop{#1}\skv@tempa{% + \expandafter\do\skv@tempa==\handler@nil + }% + }, + .prefixes={% + \def\dirkeys@prefixlist{}% + \skvusehandler{.add prefixes}{#1}% + }, + .add prefixes={% + \skv@clfalse\skv@mergelist\dirkeys@prefixlist{#1}% + }, + .families={% + \def\dirkeys@familylist{}% + \skvusehandler{.add families}{#1}% + }, + .add families={% + \skv@clfalse\skv@mergelist\dirkeys@familylist{#1}% + }, + .roots={% + \def\dirkeys@prefixlist{}% + \def\dirkeys@familylist{}% + \skvusehandler{.add roots}{#1}% + }, + .add roots={% + \dirkeys@addorremoveroots{#1}\skvmergelist + }, + .ignore roots={% + \dirkeys@addorremoveroots{#1}\skvremoveelements + }, + .restore roots={% + \skvusehandler{.add roots}{#1}% + }, + .ignore prefixes=\skvremoveelements\dirkeys@prefixlist{#1}, + .restore prefixes=\skvmergelist\dirkeys@prefixlist{#1}, + .ignore families=\skvremoveelements\dirkeys@familylist{#1}, + .restore families=\skvmergelist\dirkeys@familylist{#1}, + .ignore keys={% + \def\skv@doonroot##1##2{% + \expandafter\skvmergelist + \csname##1/##2/.@ignoredkeys\endcsname{#1}% + }% + \dirkeys@getcurrentroots\skv@doonroot + }, + .restore keys={% + \def\skv@doonroot##1##2{% + \expandafter\skvremoveelements + \csname##1/##2/.@ignoredkeys\endcsname{#1}% + }% + \dirkeys@getcurrentroots\skv@doonroot + }, + .holder prefix=\def\dirkeys@holderprefixtoks{#1}, + .push prefixes=\dirkeys@pushmeta{prefix}, + .pop prefixes=\dirkeys@popmeta{prefix}, + .push families=\dirkeys@pushmeta{family}, + .pop families=\dirkeys@popmeta{family}, + .push roots=\dirkeys@pushmeta{root}, + .pop roots=\dirkeys@popmeta{root}, + .push holder prefix=\dirkeys@pushmeta{hp}, + .pop holder prefix=\dirkeys@popmeta{hp}, + .save unknown keys={% + \dirkeys@setbooleanhandler + {.save unknown keys}{dirkeys@saveunknownkeys}{#1}% + }, + .save unknown keys in={% + \skvifblank{#1}{}{% + \skvifescaped{#1}{% + \dirkeys@saveunknownkeystrue + \def\dirkeys@unknownkeysmacro{#1}% + }{% + \skv@err{Token '\detokenize{#1}' is not escaped}\@ehd + }% + }% + }, + .define keys=\skv@handlerswafalse\dirkeys@dodefinekeys{#1}, + .new keys=\skv@handlerswatrue\dirkeys@dodefinekeys{#1}, + .initialize keys after define={% + \skvusehandler{.set boolean handler}{% + {.initialize keys after define}{skvdfk@initialize}{#1}% + }% + }, + .save initial values of keys={% + \dirkeys@setbooleanhandler + {.save initial values of keys}{skvdfk@saveinitialvalues}{#1}% + }, + .set keys={% + \skv@handlerswafalse\skv@handlerswbfalse + \dirkeys@dosetkeys{#1}\skvsetkeys + }, + .set keys*={% + \skv@handlerswatrue\skv@handlerswbfalse + \dirkeys@dosetkeys{#1}\skvsetkeys + }, + .set rmkeys={% + \skvifblank{#1}{% + \skv@handlerswafalse\skv@handlerswbtrue + \dirkeys@dosetkeys{}\skvsetrmkeys + }{% + \skv@err{Key 'set rmkeys' can't have value: + \MessageBreak Instead use the key 'ignore keys' + \MessageBreak to suggest keys to be ignored + \MessageBreak when setting 'rmkeys'}\@ehd + }% + }, + .set rmkeys*={% + \skvifblank{#1}{% + \skv@handlerswatrue\skv@handlerswbtrue + \dirkeys@dosetkeys{}\skvsetrmkeys + }{% + \skv@err{Key 'set rmkeys*' can't have value: + \MessageBreak Instead use the key 'ignore keys' to suggest keys + \MessageBreak to be ignored when setting rmkeys}\@ehd + }% + }, + .preset keys=\dirkeys@dolistedkeys@a{#1}\skv@presetkeys, + .gpreset keys=\dirkeys@dolistedkeys@a{#1}\skv@gpresetkeys, + .remove preset keys= + \dirkeys@dolistedkeys@a{#1}\skvremovepresetkeys, + .gremove preset keys= + \dirkeys@dolistedkeys@a{#1}\skvgremovepresetkeys, + .undefine preset keys= + \skv@clfalse\dirkeys@dolistedkeys@b\skv@undefpresetkeys, + .gundefine preset keys= + \skv@cltrue\dirkeys@dolistedkeys@b\skv@undefpresetkeys, + .postset keys=\dirkeys@dolistedkeys@a{#1}\skv@postsetkeys, + .gpostset keys=\dirkeys@dolistedkeys@a{#1}\skv@gpostsetkeys, + .remove postset keys=\dirkeys@dolistedkeys@a{#1}\skvremovepostsetkeys, + .gremove postset keys=\dirkeys@dolistedkeys@a{#1}\skvgremovepostsetkeys, + .undefine postset keys= + \skv@clfalse\dirkeys@dolistedkeys@b\skv@undefpostsetkeys, + .gundefine postset keys= + \skv@cltrue\dirkeys@dolistedkeys@b\skv@undefpresetkeys, + .need value keys=\dirkeys@dolistedkeys@a{#1}\skvneedvaluekeys, + .gneed value keys=\dirkeys@dolistedkeys@a{#1}\skvgneedvaluekeys, + .remove need value keys= + \dirkeys@dolistedkeys@a{#1}\skvremoveneedvaluekeys, + .gremove need value keys= + \dirkeys@dolistedkeys@a{#1}\skvgremoveneedvaluekeys, + .forbid value keys= + \dirkeys@dolistedkeys@a{#1}\skvforbidvaluekeys, + .gforbid value keys= + \dirkeys@dolistedkeys@a{#1}\skvgforbidvaluekeys, + .remove forbid value keys= + \dirkeys@dolistedkeys@a{#1}\skvremoveforbidvaluekeys, + .gremove forbid value keys= + \dirkeys@dolistedkeys@a{#1}\skvgremoveforbidvaluekeys, + .save initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvsaveinitialvaluekeys, + .gsave initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvgsaveinitialvaluekeys, + .remove save initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvremovesaveinitialvaluekeys, + .gremove save initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvgremovesaveinitialvaluekeys, + .undefine save initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvundefsaveinitialvaluekeys, + .gundefine save initial value keys= + \dirkeys@dolistedkeys@a{#1}\skvgundefsaveinitialvaluekeys, + .disable keys=\dirkeys@dolistedkeys@a{#1}\skvdisablekeys, + .disabled key message type={\skvdisabledkeysmessagetype{#1}}, + % Choices are always appended; never prepended, since the parent + % key must first be set before choices are executed. + % <key1>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}},..., + % <key-n>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}} + .choice=\dirkeys@savechoice{#1}{x0}, + .choice expand once=\dirkeys@savechoice{#1}{x1}, + .choice expand twice=\dirkeys@savechoice{#1}{x2}, + .choice expanded=\dirkeys@savechoice{#1}{xx}, + % {<key-i>,...,<key-j>}/{<stylelist-ij>},..., + % {<key-n>,...,<key-m>}/{<stylelist-nm>}: + .append style=\dirkeys@savestyle{#1}{app}{x0}, + .prepend style=\dirkeys@savestyle{#1}{prep}{x0}, + .append style expanded=\dirkeys@savestyle{#1}{app}{xx}, + .prepend style expanded=\dirkeys@savestyle{#1}{prep}{xx}, + .append style expand once=\dirkeys@savestyle{#1}{app}{x1}, + .prepend style expand once=\dirkeys@savestyle{#1}{prep}{x1}, + .append style expand twice=\dirkeys@savestyle{#1}{app}{x2}, + .prepend style expand twice=\dirkeys@savestyle{#1}{prep}{x2}, + % {<linklist-1>}/<mainkeylist-1>,...,{<linklist-n>}/<mainkeylist-n>: + .append link=\dirkeys@savelink{#1}{app}{x0}, + .prepend link=\dirkeys@savelink{#1}{prep}{x0}, + .append link expanded=\dirkeys@savelink{#1}{app}{xx}, + .prepend link expanded=\dirkeys@savelink{#1}{prep}{xx}, + .append link expand once=\dirkeys@savelink{#1}{app}{x1}, + .prepend link expand once=\dirkeys@savelink{#1}{prep}{x1}, + .append link expand twice=\dirkeys@savelink{#1}{app}{x2}, + .prepend link expand twice=\dirkeys@savelink{#1}{prep}{x2}, + .list break=\skvbreaklooptrue, + .args=\dirkeys@assignarg{#1}{x0}, + % Expand arguments of keys at key setting time, not at key definition: + .args expanded=\dirkeys@assignarg{#1}{xx}, + .args expanded once=\dirkeys@assignarg{#1}{x1}, + .args expanded twice=\dirkeys@assignarg{#1}{x2}, + .set with defaults={ + \dirkeys@dolistedkeys@a{#1}\dirkeys@setwithdefaults + }, + .unknown key handler={ + \dirkeys@dounknownkeyhandler{#1} + }, + % Redefine the default value macros of keys. + % .default values={keya=vala,...,keyn=valn} + .default values={% + \dirkeys@assigndefault{#1} + }, + % Change the current values of keys. + % .assign values={keya=vala,...,keyn=valn} + .assign values={% + \dirkeys@assignvalue{#1} + }, + % A key inherits value from a key. + % .inherit values={keya1/keya2,...,keyn1/keyn2} + .inherit values={% + \dirkeys@inherit{value}{#1} + }, + .inherit callback={% + \dirkeys@inherit{cbk}{#1} + }, + .inherit defaults={% + \dirkeys@inherit{defa}{#1} + }, + % .prepend values={keya=vala,...,keyn=valn} + .prepend values={% + \dirkeys@addtocodeorvalue{pre}{value}{#1} + }, + .append values={% + \dirkeys@addtocodeorvalue{app}{value}{#1} + }, + % .prepend code={keya=codea,...,keyn=coden} + .prepend code={% + \dirkeys@addtocodeorvalue{pre}{cbk}{#1} + }, + .append code={% + \dirkeys@addtocodeorvalue{app}{cbk}{#1} + }, + }, + .new narg handlers={% + .no such key={4}{% + % All the parameters would have been despaced when this handler + % is called. #1=prefix, #2=family, #3=key, #4=value. + \skvifcsdef{#1@#2@handler}{% + \csname#1@#2@handler\endcsname{#1}{#2}{#3}{#4} + }{% + \skv@err{Key '#3' is not in family '#1/#2'}\@ehd + }% + }, + .add style={2}{% + \skvusehandler{.prepend style}{#1}% + \skvusehandler{.append style}{#2}% + }, + .add code={2}{% + \skvusehandler{.prepend code}{#1}% + \skvusehandler{.append code}{#2}% + }, + .add value={2}{% + \skvusehandler{.prepend value}{#1}% + \skvusehandler{.append value}{#2}% + }, + % .get value={<pref>}{<fam>}{<key>}<cmd> + .get value={4}{% + \skvifcsdef{#1/#2/#3.@value}{% + \skvifescaped{#4}{% + \skvletcs#4{#1/#2/#3.@value}% + }{% + \skv@err{Token '\detokenize{#4}' isn't escaped}\@ehd + }% + }{% + \skv@err{Key '#3' isn't defined or has + \MessageBreak no value in '#1/#2'}\@ehd + }% + }, + % '.try set keys' doesn't use the active prefixes and families within + % \dirkeys. The prefix, families and other options are to be supplied + % in the argument #1. + % .try set keys={<options>}{<kvlist>} + .try set keys={2}{% + \skv@trysetkeys[{#1}]{#2} + }, + % 'search also' set keys. + % .sa set keys={<options>}{<kvlist>} + .sa set keys={2}{% + \dirkeys@dosasetkeys{#1}{#2} + }, + % .set boolean handler={<handler>}{<boolean>}{<value>} + .set boolean handler={3}{% + \dirkeys@setbooleanhandler{#1}{#2}{#3} + }, + }, + .handler let={% + .exec=.exec code, + .handlers let=.handler let, + .prefix=.prefixes, .change prefix=.prefix, .cp=.prefix, + .add prefix=.add prefixes, + .ignore prefix=.ignore prefixes, + .restore prefix=.restore prefixes, + .hp=.holder prefix, + .family=.families, .change family=.family, .cf=.family, + .add family=.add families, + .ignore family=.ignore families, + .restore family=.restore families, + .root=.roots, .change root=.roots, .cd=.roots, + .path=.roots, .paths=.roots, .change path=.roots, + .add root=.add roots, + .add path=.add roots, .add paths=.add roots, + .ignore root=.ignore roots, + .ignore path=.ignore roots, .ignore paths=.ignore roots, + .restore root=.restore roots, + .restore path=.restore roots, .restore paths=.restore roots, + .ignore key=.ignore keys, .restore key=.restore keys, + .define key=.define keys, .new key=.new keys, + .initialize after define=.initialize keys after define, + .save initial values=.save initial values of keys, + .set=.set keys, .setkeys=.set keys, + .set*=.set keys*, .setkeys*=.set keys*, + .setkeys save unknown=.set keys*, + .setrmkeys=.set rmkeys, + .set remaining keys=.set rmkeys, + .setrmkeys*=.set rmkeys*, + .set remaining keys*=.set rmkeys*, + .presetkeys=.preset keys, .initial values=.preset keys, + .postsetkeys=.postset keys, + .try set=.try set keys, + .sa set=.sa set keys, .search also set keys=.sa set keys, + .use defaults=.set with defaults, + .echoice=.choice expanded, + .style=.append style, + .styles=.append style, + .estyle=.append style expanded, + .style expand once=.append style expand once, + .style expand twice=.append style expand twice, + .link=.append link, + .links=.append link, + .elink=.append link expanded, + .link expand once=.append link expand once, + .link expand twice=.append link expand twice, + .listbreak=.list break, .break list=.list break, + .new handler=.new handlers, .define handler=.define handlers, + .kill handler=.kill handlers, + .initial value=.initial values, + .default value=.default values,.default=.default values, + .defaults=.default values, + .assign value=.assign values, + .inherit value=.inherit values, + .inherit default=.inherit defaults, + .inherit code=.inherit callback, + .prepend value=.prepend values, + .append value=.append values, + .disabled option message type=.disabled key message type, + .message type when disabled key is invoked=.disabled key message type, + .message type when disabled option is invoked=.disabled key message type, + .unknown option handler=.unknown key handler, + }, +} + +% \skvpatchcmd[<prefix>]{<cmd>}{<search>}{<replace>}{<succeed>}{<fail>} +\skvrobustdef*\skvpatchcmd{% + \begingroup + \@makeother{\#}\endlinechar\m@ne + \skv@testopt\skv@patchcmd{########1}% +} +\skvrobustdef*\skv@patchcmd[#1]#2#3#4{% + \ifskv@tracingkeys + \typeout{^^J** Debugging patches: command '\string#2'}% + \fi + \skv@p@tchcmd{#1}{#2}{#3}{#4}% +} +\skvrobustdef*\skv@p@tchcmd#1#2#3#4{% + \skvifpatchable{#2}{#3}{#4}{% + \skv@patchdebug{++}{Command is patchable}% + \skv@patchdebug{==}{Patching has begun}% + \begingroup + \edef\skv@tempa##1##2{% + \def##1####1\detokenize{macro:}####2->####3\skvrelax{% + #1\def\string#2####2{##2####3\skvrelax}% + }% + \def##2####1\detokenize{#3}####2\skvrelax{####1\detokenize{#4}####2}% + \edef##1{##1\meaning#2\skvrelax}% + }% + \skv@tempa\skv@tempa\skv@tempb + \skv@scantoksd\endgroup\skv@tempa + \skv@patchdebug{==}{Patching completed successfully}% + \@firstoftwo + }{% + \skv@patchdebug{--}{Patching couldn't be completed}% + \@secondoftwo + }% +} +\skvrobustdef*\skv@patchdebug#1#2{% + \ifskv@tracingkeys\typeout{[debug] #1 #2}\fi +} +\skvrobustdef*\skvifpatchable#1#2#3{% + \endgroup + \skvifundef{#1}{% + \skv@patchdebug{--}{Command not defined}% + \@secondoftwo + }{% + \skv@patchdebug{++}{Command is defined}% + \skvifmacro{#1}{% + \skv@patchdebug{++}{Command is a macro}% + \skvifscannable{#1}{% + \skv@patchdebug{++}{Macro can be retokenized safely after patching}% + \skvifpattern{#2}{#1}{% + \skv@patchdebug{++}{Requested search pattern found}% + \@firstoftwo + }{% + \skv@patchdebug{--}{Requested search pattern not found}% + \@secondoftwo + }% + }{% + \skv@patchdebug{--}{Nested commands or parameters}% + \@secondoftwo + }% + }{% + \skv@patchdebug{--}{Macro can't be retokenized safely after patching}% + \@secondoftwo + }% + }% +} +\skvrobustdef*\skveveryscan{% + \everyeof{\noexpand}% + \endlinechar\m@ne + \makeatletter + \catcode`\ =10 + \catcode`\\=0 + \catcode`\{\@ne + \catcode`\}\tw@ +} +\skvrobustdef*\skv@scantoksd#1#2{% + \skvexpandsecond{#1\skveveryscan\scantokens}{% + {#2}\everyeof{\the\everyeof}% + \noexpand\endlinechar\the\endlinechar\relax + \catcode`\noexpand\@=\the\catcode`\@\relax + \catcode`\noexpand\ =\the\catcode`\ \relax + \catcode`\noexpand\\=\the\catcode`\\\relax + \catcode`\noexpand\{=\the\catcode`\{\relax + \catcode`\noexpand\}=\the\catcode`\}\relax + }% +} +\skvrobustdef*\skvifscannable#1{% + % Fails if the content of #1 is already detokenized. + \begingroup + \edef\skv@tempa##1{% + \def##1####1\detokenize{macro}:####2->####3\skvrelax{% + ####1\def\string\skv@tempa####2{####3}% + }% + \edef##1{##1\meaning#1\skvrelax}% + }% + \skv@tempa\skv@tempa + \makeatletter\everyeof{\noexpand}\endlinechar\m@ne + \scantokens\expandafter{\skv@tempa}\relax + \expandafter\endgroup + \ifx#1\skv@tempa + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvrobustdef\skvifpattern#1#2{% + \begingroup + \edef\skv@tempa##1{\def##1####1\detokenize{#1}####2\skvrelax}% + \skv@tempa\skv@tempa{% + \expandafter\endgroup\ifx\\##2\\% + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + \edef\skv@tempb##1{##1\detokenize{#1}\skvrelax}% + \skv@tempb{\expandafter\skv@tempa\meaning#2}% +} +\skvrobustdef*\skvifmacro#1{% + \begingroup + \edef\skv@tempa##1{\def##1####1\detokenize{macro}:####2\skvrelax}% + \skv@tempa\skv@tempa{% + \expandafter\endgroup\ifx\@nnil##2\@nnil + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + \edef\skv@tempb##1{##1\detokenize{macro}:\skvrelax}% + \skv@tempb{\expandafter\skv@tempa\meaning#1}% +} +%\skvxzapspaces{\detokenize{\begin{\document}}}\skv@begindoc@string +\edef\skv@begindoc@string{\string\begin\string{\string\document\string}} + +\skv@core@restorecodes +\endinput + +%% End of file 'skeyval-core.tex'. |