summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex')
-rw-r--r--Master/texmf-dist/tex/latex/skeyval/skeyval-core.tex6150
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&#2{%
+ \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&#2{%
+ \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'.