%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%
% This is file 'skeyval-core.tex', version 1.2, 2013/02/10.                %
%                                                                          %
% 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-2013 Ahmed Musa (amusa22@gmail.com).                  %
%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%

\csname skeyval-core-loaded\endcsname
\expandafter\let\csname skeyval-core-loaded\endcsname\endinput

\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{\do35,64,123,125,61,59,13,\do,}%
}
\skv@prova

\edef\skv@core@restorecodes{%
  \unexpanded\expandafter{\skv@core@restorecodes}%
  \endlinechar\the\endlinechar\relax
  \catcode`\^^M=\the\catcode`\^^M\relax
}
\endlinechar13 %
\catcode013 05 % ^^M
\catcode035 06 % #
\catcode064 11 % @
\catcode123 01 % {
\catcode125 02 % }
\catcode061 12 % =
\catcode044 12 % ,
\def\do#1=#2,{%
  \ifx\do#1\else
    \edef\skv@core@restorecodes{%
      \unexpanded\expandafter{\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,13=5,\do=,%

\newdimen\skvz@\skvz@=0pt\relax
\newcount\skvm@ne\skvm@ne=-1\relax
\ifcase
  \ifx\eTeXversion\@undefined 0\else
    \ifnum\eTeXversion<\tw@ 0\else 1\fi
  \fi\relax
  \errhelp{This package requires eTeX version 2 or higher.}
  \errmessage{skeyval package: eTeX not loaded or old version.}
  \expandafter\endinput
\fi

\newif\ifskv@latex
\newif\ifskv@tempst
\ifx\ProvidesFile\@undefined
  \skv@latexfalse
  \message{File 'skeyval-core.tex' 2013/02/10 v1.2:
    Core of 'skeyval' key-value parser (AM)}
  \input skeyval-ltxcmds
\else
  \skv@latextrue
  \ProvidesFile{skeyval-core.tex}
    [2013/02/10 v1.2 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}}
% No \protected for \skv@ehd:
\def\skv@ehd{%
  I have encountered a problem here. Try typing <return>
  \MessageBreak to proceed. If that doesn't work, type X then
  <return>\MessageBreak to quit.
}
\let\skvgenericexception\@latexerr
\protected\def\skvtracingall{%
  \tracinggroups\@ne\tracingifs\@ne\loggingall\tracingassigns\@ne
}
\let\skvloggingall\skvtracingall
\long\def\skviofii#1#2{#1}
\long\def\skviiofii#1#2{#2}
% 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}\skv@ehd}
  {#2}%
}
% \skvgenloop{<parser>}{<list>}{<1.parameter.callback>}
%
% 1. The parser may be empty. This is the case for nsv/tsv lists.
% 2. The normalization of the list, including dealing with active parsers,
%    is left to the caller.
% 3. \@iden will help remove spurious leading spaces.
%
\protected\def\skvgenloop#1#2#3{%
  \def\skv@gendo##1#1{%
    \ifx\skv@gendo##1\expandafter\@gobble\else\expandafter\@iden\fi
    {#3\expandafter\skv@gendo\@iden}%
  }%
  \expandafter\skv@gendo\@iden#2#1\skv@gendo#1%
}
% \skvrescan{<macro>}
\protected\def\skvrescan#1{%
  \begingroup
  \endlinechar\m@ne\newlinechar\m@ne
  \catcode`\@=11\relax
  \everyeof{\skv@rescan@guard}%
  \def\reserved@a##1\skv@rescan@guard{%
    \endgroup
    \edef#1{\unexpanded\expandafter{\@gobble##1}}%
  }%
  % \relax protects any blank space leading the content of #1:
  \expandafter\reserved@a\scantokens\expandafter{\expandafter\relax#1}%
}

% Process a comma list with a simple callback. This can't be used if #2
% isn't scannable or if scanning #2 will change the signature of
% its token list.
%
% \skv@commaact{<list>}{<1.parameter.callback>}
%
% Example:
%
%   \def\emptify{\skv@commaact{\def##1{}}}
%
\begingroup
\catcode`\,=\active
\protected\gdef\skv@commaact#1#2{%
  \begingroup
  \toks@{}%
  \def,##1{%
    \ifx\skv@commaact##1\else
      \ifx,##1%
        % Double comma seen:
        \expandafter\expandafter\expandafter,%
      \else
        \toks@\expandafter{\the\toks@#2}%
      \fi
    \fi
  }%
  \begingroup
  \edef\skv@tempa{\unexpanded{,#1}}%
  \catcode`\,=\active
  \skvrescan\skv@tempa
  % Add a trailing \skv@commaact, in case the list is ended by a comma (,):
  \expandafter\endgroup\skv@tempa,\skv@commaact
  \expandafter\endgroup\the\toks@
}
\endgroup

\protected\def\skvnewregister#1#2{%
  \def\skv@prova{#1}%
  \def\skv@provb{\bool}%
  % We don't want to expose \if while in the next conditional:
  \def\skv@provc{\if}%
  \ifx\skv@prova\skv@provb
    \let\skv@prova\skv@provc
  \fi
  \skv@commaact{#2}{%
    \skvifdefinable##1{%
      \csname new\expandafter\expandafter\expandafter\@gobble
      \expandafter\string\skv@prova\endcsname##1%
    }%
  }%
}
\let\skvnewregisters\skvnewregister
\skvnewregisters\toks{\skv@toksa,\skv@toksb}

% 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

\def\reserved@a#1{%
  \def\skvifnextisouter{%
    \expandafter\skv@ifnextisouter\meaning\skv@nextseen#1\relax
  }%
  \def\skv@ifnextisouter##1#1##2\relax{%
    \ifcat$##2$\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
  }%
}
\expandafter\reserved@a\expandafter{\detokenize{outer macro}:}

% We modify LaTeX kernel's \@ifnextchar to eliminate the need for
% doubling the hash character in #2 or #3. \skv@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\skv@nextseen\skv@simpleifnext
}
\def\skv@simpleifnext{%
  \ifx\skv@nextseen\@sptoken
    \let\skv@letnext\skv@simpleifn@xt
  \else
    \ifx\skv@nextseen\reserved@d
      \let\skv@letnext\reserved@a
    \else
      \let\skv@letnext\reserved@b
    \fi
  \fi
  \skv@letnext
}
\lowercase{\def\skv@simpleifn@xt} {%
  \global\futurelet\skv@nextseen\skv@simpleifnext
}

% Tests for \skvifnextchar:
%
% \outer\def\foo{}
% \iftrue\skvifnextchar*\relax\relax\foo\fi
% \iftrue\skvifnextchar*\relax\relax\fi
% \skvifnextchar*\relax\relax\iftrue\fi
%
\protected\long\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}%
    }{%
      \skvifnextisouter{%
        \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}}%
}
\let\skv@sav@ifnextchar\skvifnextchar
\protected\def\skvusesimpleifnextchar{%
  \let\skvifnextchar\skvsimpleifnextchar
}
\let\skvuselatexifnextchar\skvusesimpleifnextchar
\protected\def\skvuserobustifnextchar{%
  \let\skvifnextchar\skv@sav@ifnextchar
}

% Expandable \@testopt:
%
% Eg,
%     \def\macroa{\skvnewxtestopt\macrob{Mr.}}
%     \def\macrob[#1]#2{#1 #2}
%     \edef\testa{\expandafter\macroa\activespace{David Carlisle}}
%     \edef\testb{\macroa[Mr.]{Heiko Oberdiek}}
%
\long\def\skvnewxtestopt#1#2#3{%
  % #3 will be seen as a space only if that space is active. But
  % \skvifstrcmpTF will detokenize that space before comparison.
  \skvifstrcmpTF{ }{#3}{%
    \skvnewxtestopt#1{#2}%
  }{%
    \skvifstrcmpTF{#3}{[}%
      {#1[}{\skvifntypeTF{#3}{#1[#2]#3}{#1[#2]{#3}}}%
  }%
}

% Expandable \@ifnextchar:
%
% Eg,
%    \def\temp{\skvxifnextchar*{\def\x{T}}{\def\x{F}}}
%    \temp* (star seen) or \temp<some token> (star not seen)
%
% ** This can't be used to test the presence of explicit left brace.
%
\long\def\skvxifnextchar#1#2#3#4{%
  % #4 will be seen as a space only if that space is active. But
  % \skvifstrcmpTF will detokenize that space before comparison.
  \skvifstrcmpTF{ }{#4}{%
    \skvxifnextchar#1{#2}{#3}%
  }{%
    \skvifstrcmpTF{#4}{#1}{#2}{\skvifntypeTF{#4}{#3#4}{#3{#4}}}%
  }%
}

\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\skv@testpnopt#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}\skv@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*\skvrenewdef{\skvstarorlong{\skv@redef@{}{}}}
\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
  \def\do##1{\catcode`##1=12\relax}%
  \dospecials
  \skv@endcomment
}
\begingroup
\catcode`\!=0
\catcode`\\=12
!long!gdef!skv@endcomment#1\endcomment{!endgroup}
!endgroup
%\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@truetoks{true}
\skvnewdef*\skv@falsetoks{false}
\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{ }
\let\then\iffalse
\skvnewdef\skvswap#1#2{#2#1}
\skvnewdef*\skv@quark{\@gobble\skv@quark}
\skvnewlet\skvrom\romannumeral
\skvnewdef*\skv@rej{^skv^}
\skvnewdef*\skv@dotna{.na}
\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}
\skvnewdef*\skv@simplearg{##1}
\skvnewlet\skvsanitizemacro\@onelevel@sanitize
\skvrobustdef*\skv@usetempst#1#2{%
  \edef#2{\skvifdefboolTF{skv@tempst}\skvexpandonce\unexpanded{#1}}%
}
\skvrobustdef*\skvtestifin#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{%
  \skvifstrcmpTF{#1}{x}{%
    \skv@err{Redefining primitve '\@backslashchar ifx'?}\skv@ehd
  }{%
    \csname newif\expandafter\endcsname\csname if#1\endcsname
  }%
}
\skvnewdef*\skv@zapornot#1{%
  \if#1%
    \expandafter\skvzapspaces
  \else
    \expandafter\@iden
  \fi
}
\skvnewdef\skvzapspaces#1{\skv@zapspaces.#1 \zap@nil}
\skvnewdef\skv@zapspaces#1 #2\zap@nil{%
  \skvifblankTF{#2}{%
    \@gobble#1%
  }{%
    \skv@zapspaces#1#2\zap@nil
  }%
}
\skvnewdef\skvxzapspaces#1#2{%
  \edef#2{.#1}%
  \edef#2{\expandafter\skv@zapspaces#2 \zap@nil}%
}
% \skvifleadspaceTF{<toks>}{<true>}{<false>}
\newcommand\skvifleadspaceTF[3]{%
  \romannumeral\csname
  @\skvifnullTF{#1}{second}{\iffalse{\fi\skv@ifleadspace.#1 x}}%
  oftwo\endcsname{0 #2}{0 #3}%
}
\skvnewdef\skv@ifleadspace#1 {%
  \expandafter\skvifnullTF\expandafter{\@gobble#1}{first}{second}%
  \expandafter\@gobble\expandafter{\iffalse}\fi
}

% \skvifldspaceTF is faster than \skvifleadspaceTF.
%
% \skvifldspaceTF{ }{}{} gives <true> as desired.
\begingroup
\lccode`\&=0 \catcode`\&=7 \lccode`\!=0 \catcode`\!=8
\lowercase{\endgroup
  \skvnewdef\skvifldspaceTF#1{\skv@ifldspace!#1! &}
  \skvnewdef\skv@ifldspace#1! #2&{\skvifblankFT{#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\skvifldspaceTF\expandafter{\@gobble#1}{%
        \@gobble#1%
      }{%
        \expandafter\@space\@gobble#1%
      }%
    }%
  }
}
\skvnewdef\skvotrimspace{\expandafter\skvtrimspace\expandafter}
\skvnewdef\skvxtrimspace#1{%
  \expandafter\skvtrimspace\expandafter{\romannumeral-`\q#1}%
}
\skvrobustdef*\skvdespace#1#2{\edef#2{\skvtrimspace{#1}}}
\skvrobustdef*\skvdespacecontent#1{%
  \edef#1{\expandafter\skvtrimspace\expandafter{#1}}%
}
\skvnewdef*\skvcsuse#1{%
  \skvifcsname#1\then
    \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}%
}
\skvnewlet\skvaftercs\skvaftercsname
\skvnewdef*\skvifdef#1\then{\if0\skvifdefTF{#1}{0}{1}}
\skvnewdef*\skvifundef#1\then{\if0\skvifdefTF{#1}{1}{0}}
% \skvifcsname x\then\def\x{T}\else\def\x{F}\fi
\skvnewdef*\skvifcsname#1\then{\if0\skvifcsdefTF{#1}{0}{1}}
\skvnewdef*\skvifdefTF#1{%
  \skvifxTF{#1}\skv@undefined{%
    \@secondoftwo
  }{%
    \ifx\relax#1%
      \expandafter\@secondoftwo
    \else
      \expandafter\@firstoftwo
    \fi
  }%
}
\skvnewdef*\skvifdefFT#1{\skvifdefTF{#1}\@secondoftwo\@firstoftwo}
\skvnewdef*\skvifcsdefTF#1{%
  \skvifblankTF{#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*\skvifcsdefFT#1{\skvifcsdefTF{#1}\@secondoftwo\@firstoftwo}
% Don't check for blank #1. For internal use:
\skvnewdef*\skvifnamedefTF#1{%
  \ifcsname#1\endcsname
    \expandafter\ifx\csname#1\endcsname\relax
      \expandafter\expandafter\expandafter\@secondoftwo
    \else
      \expandafter\expandafter\expandafter\@firstoftwo
    \fi
  \else
    \expandafter\@secondoftwo
  \fi
}
\skvnewdef*\skvifnamedefFT#1{\skvifnamedefTF{#1}\@secondoftwo\@firstoftwo}
\skvnewdef*\skv@ifx@#1#2\skv@ifx@nil{\expandafter#1}
\skvnewdef*\skv@ifx@@#1#2\skv@ifx@nil{#1}
\skvnewdef*\skvifxTF#1#2{%
  \csname @\expandafter\expandafter\expandafter
  \ifx\skv@ifx@#1\batchmode\skv@ifx@nil\skv@ifx@@#2\noboundary\skv@ifx@nil
  first\else second\fi oftwo\endcsname
}
\skvnewdef*\skvifxFT#1#2{\skvifxTF{#1}{#2}\@secondoftwo\@firstoftwo}
\skvnewdef*\skv@ghost{\@gobble\skv@ghost}
\skvnewdef\skvifemptyTF#1{%
  \ifx#1\@empty\skv@ghost
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\skvnewdef\skvifcsemptyTF#1{\expandafter\skvifemptyTF\csname#1\endcsname}
\skvnewdef\skvifdefemptyTF#1{%
  \skvifblankTF{#1}\@secondoftwo{%
    \skvifntypeTF{#1}{%
      \skvifdefTF{#1}{\skvifxTF#1\@empty}{\@secondoftwo}%
    }{%
      \@secondoftwo
    }%
  }%
}
\begingroup
% Keep lccode, in case the test occurs in a table:
\lccode`\&=0 \catcode`\&=8
\lowercase{\endgroup
  \skvnewdef\skvifnullTF#1{%
    \csname @\ifcat&\detokenize{#1}&first\else second\fi oftwo\endcsname
  }
  \skvnewdef\skviflacus#1\then{\ifcat&\detokenize{#1}&}
}
\skvnewdef\skvifblankTF#1{%
  \expandafter\skvifnullTF\expandafter{\@gobble#1.}%
}
\skvnewdef\skvifblankFT#1{\skvifblankTF{#1}\@secondoftwo\@firstoftwo}

\skvnewdef*\skvifboolTF#1{%
  \skvifblankTF{#1}{%
    \@secondoftwo
  }{%
    \skvifcsdefTF{if#1}{%
      \csname @\csname if#1\endcsname
        first\else second\fi oftwo\endcsname
    }{%
      \skv@err{Undefined boolean '#1'}\skv@ehd
    }%
  }%
}
\skvnewdef*\skvifboolFT#1{\skvifboolTF{#1}\@secondoftwo\@firstoftwo}
% \skvifbool sw1\then <true> \else <false> \fi
\skvnewdef*\skvifbool#1\then{\if0\skvifboolTF{#1}01}
\skvnewdef*\skvifdefboolTF#1{%
  \csname @\csname if#1\endcsname first\else second\fi oftwo\endcsname
}
\skvnewdef*\skvifdefboolFT#1{\skvifdefboolTF{#1}\@secondoftwo\@firstoftwo}
\skvrobustdef*\skvaftergroupifboolTF#1\endgroup{%
  \skvifboolTF{#1}{\endgroup\@firstoftwo}{\endgroup\@secondoftwo}%
}
\skvrobustdef*\skvaftergroupifboolFT#1\endgroup{%
  \skvifboolTF{#1}{\endgroup\@secondoftwo}{\endgroup\@firstoftwo}%
}
% \skvifboolvalTF{<toks>}{<true>}{<false>}
\skvrobustdef*\skvifboolvalTF#1{%
  \skvxifinTF{,\expandafter\skvtrimspace\expandafter
    {\detokenize{#1}},}{,\detokenize{true,false},}%
}
\skvrobustdef*\skvxifboolvalTF#1{%
  \begingroup
  \edef\skv@prova{#1}%
  \skvdespacecontent\skv@prova
  \skvexpandtwoargs{\endgroup\skvifinTF}
    {,\skvoxdetok\skv@prova,}{,\detokenize{true,false},}%
}
% Generate error if <toks> is not a valid boolean value, otherwise
% execute #2:
% \skvifboolvalT{<toks>}{<true>}
\skvrobustdef*\skvifboolvalT#1#2{%
  \skvxifinTF{,\expandafter\skvtrimspace\expandafter
    {\detokenize{#1}},}{,\detokenize{true,false},}%
    {#2}{\skv@badboolerr{#1}}%
}
% 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'}\skv@ehd
}

% Switches and toggles have separate namespaces.
\skvrobustdef*\skvnewswitch#1{%
  \skvifcsdefTF{skv@switch@#1}{%
    \skv@err{Switch '\detokenize{#1}' already exists}\skv@ehd
  }{%
    \skvcsdef{skv@switch@#1}{01}%
  }%
}
\skvnewdef*\skvifswitchTF#1{%
  \if
  \skvifcsdefTF{skv@switch@#1}{%
    \csname skv@switch@#1\endcsname
  }{%
    01%
  }%
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\skvnewdef*\skvifdefswitchTF#1{%
  \if\csname skv@switch@#1\endcsname
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\skvrobustdef*\skvswitchtrue#1{%
  \skvifcsdefTF{skv@switch@#1}{%
    \skvcsdef{skv@switch@#1}{00}%
  }{%
    \skv@err{No switch '\detokenize{#1}'}\skv@ehd
  }%
}
\skvrobustdef*\skvswitchfalse#1{%
  \skvifcsdefTF{skv@switch@#1}{%
    \skvcsdef{skv@switch@#1}{01}%
  }{%
    \skv@err{No switch '\detokenize{#1}'}\skv@ehd
  }%
}
\skvrobustdef*\skvnewtog#1{%
  \skvifcsdefTF{skv@toggle@#1}{%
    \skv@err{Switch '\detokenize{#1}' already exists}\skv@ehd
  }{%
    \skvcslet{skv@toggle@#1}\@secondoftwo
  }%
}
\skvrobustdef*\skvdeftog#1{\skvcslet{skv@toggle@#1}\@secondoftwo}
\skvnewdef*\skviftogTF#1{%
  \skvifcsdefTF{skv@toggle@#1}{%
    \csname skv@toggle@#1\endcsname
  }{%
    \@secondoftwo
  }%
}
\skvnewdef*\skviftogFT#1{\skviftogTF{#1}\@secondoftwo\@firstoftwo}
\skvnewdef*\skvifdeftogTF#1{\csname skv@toggle@#1\endcsname}
\skvrobustdef*\skvsettogtrue#1{%
  \skvifcsdefTF{skv@toggle@#1}{%
    \skvcslet{skv@toggle@#1}\@firstoftwo
  }{%
    \skv@err{No toggle '\detokenize{#1}'}\skv@ehd
  }%
}
\skvrobustdef*\skvsettogfalse#1{%
  \skvifcsdefTF{skv@toggle@#1}{%
    \skvcslet{skv@toggle@#1}\@secondoftwo
  }{%
    \skv@err{No toggle '\detokenize{#1}'}\skv@ehd
  }%
}

% plasw=plain switch (ie, 00 or 01).
\skvnewdef*\skvifplaswTF#1{%
  \if#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\skvnewdef*\skvifplaswFT#1{%
  \if#1\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
}
\skvnewdef\skvifcondTF#1\fi{%
  \csname @#1first\else second\fi oftwo\endcsname
}
\skvnewdef\skvifcondFT#1\fi{\skvifcondTF{#1}\fi\@secondoftwo\@firstoftwo}
% In case #1 isn't expandable. This is the difference between
% \skvifcondTF and \skvifconditionTF.
\skvnewdef\skvifconditionTF#1\fi{%
  #1\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\skvnewdef*\skvifnumTF#1#{\skvifcondTF\ifnum#1\fi}
\skvnewdef*\skvifdim#1#{\skvifcondTF\ifdim#1\fi}
\skvnewdef*\skvifnumFT#1#{\skvifnumTF#1{\@secondoftwo}{\@firstoftwo}}
\skvnewdef\skvalloftwo#1#2{#1#2}
% If '#1' is a single, non-space and non-braced token:
\skvnewdef\skvifntypeTF#1{%
  \csname @\if0\skv@strcmp
  {\skvexpandonce{\skvalloftwo#1{}{}}}{\unexpanded{#1{}}}%
  first\else second\fi oftwo\endcsname
}
\skvnewdef\skvxifntypeTF{\skvexpandarg\skvifntypeTF}
\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*\skvemptify#1{\skv@commaact{#1}{\def##1{}}}
\skvrobustdef*\skvinizero#1{\skv@commaact{#1}{\def##1{0}}}
\skvrobustdef*\skvundefcmdlist#1{%
  \skvcommaparse{#1}\skv@prova{\expandafter\skvundef\skv@prova}%
}
\skvrobustdef*\skvemptifycmds#1{%
  \skvcommaparse{#1}\skv@prova{\expandafter\def\skv@prova{}}%
}
% \skv@addtocollection{<toks>}
%
% Collect tokens in a group for later exiting the group with them:
%
\skvrobustdef*\skv@addtocollection#1{%
  \edef\skv@collection{%
    \ifdefined\skv@collection\skvexpandonce\skv@collection\fi
    \unexpanded{#1}%
  }%
}
% Don't use \do for \dl, in case \do appears as a garbage:
% \skvgarbageitems{\dl<cmd1>\gdl<cmd2>...}
\skvrobustdef*\skvgarbageitems{\skvappto\skv@garbagelist}
\skvrobustdef*\skvdumpgarbage{%
  \let\gdl\skvgundef\let\dl\skvundef
  \skv@garbagelist
  \def\skv@garbagelist{}%
}

\skvifdefTF\pdfstrcmp{%
  \let\skv@strcmp\pdfstrcmp
}{%
  \RequirePackage{pdftexcmds}%
  \skvifdefTF\pdf@strcmp{%
    \let\skv@strcmp\pdf@strcmp
  }{%
    \skv@err{Neither '\string\pdfstrcmp' nor '\string\pdf@strcmp' exists}\skv@ehd
  }%
}
\skvnewdef\skvifstrcmpTF#1#2{%
  \csname @\ifnum\skv@strcmp{\detokenize{#1}}%
    {\detokenize{#2}}=0first\else second\fi oftwo\endcsname
}
\skvnewdef*\skvifstrcmpFT#1#2{\skvifstrcmpTF{#1}{#2}\@secondoftwo\@firstoftwo}
\skvnewdef\skvifstreq#1#2\then{%
  \ifnum\skv@strcmp{\detokenize{#1}}{\detokenize{#2}}=\skvz@
}
\skvnewdef\skvoifstrcmpTF#1#2{%
  \csname @\ifnum\skv@strcmp{\skvexpandonce{#1}}%
    {\skvexpandonce{#2}}=0first\else second\fi oftwo\endcsname
}
\skvnewdef\skvxifstrcmpTF#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{%
  \skvifcsdefTF{#1}{%
    \skv@err{Command '\skvnoexpandcs{#1}' already defined}\skv@ehd
  }{%
    \skvcslet{#1}%
  }%
}
\skvrobustdef\skvletcs#1#2{%
  \begingroup\expandafter\endgroup\expandafter
  \let\expandafter#1\csname#2\endcsname
}
\skvrobustdef\skvnewletcs#1{%
  \skvifdefTF{#1}{%
    \skv@err{Command '\string#1' already defined}\skv@ehd
  }{%
    \skvletcs#1%
  }%
}
\skvrobustdef*\skvcsletcs#1#2{%
  \begingroup\expandafter\endgroup
  \expandafter\let\csname#1\expandafter
  \endcsname\csname#2\endcsname
}
\skvrobustdef\skvnewcsletcs#1{%
  \skvifcsdefTF{#1}{%
    \skv@err{Command '\skvnoexpandcs{#1}' already defined}\skv@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 commands and booleans out of a local group under \edef:
\skvnewdef*\skvcmdexit#1{%
  \skvifdefTF{#1}{%
    \edef\noexpand#1{\noexpand\unexpanded{\skvexpandonce{#1}}}%
  }{}%
}
\skvnewdef*\skvcsexit#1{%
  % If #1 is undefined, \skvexpandcsonce will make it relax. The
  % preliminary test in \skvcmdexit will deal with that situation.
  \skvaftercs\skvcmdexit{#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
}
% \skvadvanceno{<cmd>}{<number>}
\skvrobustdef*\skvsetnumber#1{%
  \begingroup
  \def\x{\edef\x{\endgroup\def\noexpand#1{\the\count@}}\x}%
  \afterassignment\x\count@
}
\skvnewlet\skvsetno\skvsetnumber
% \skvadvanceno{<cmd>}{<number>}
\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
  \skvifdefTF#2{%
    #1\edef#2{\the\numexpr#2+#3\relax}%
  }{%
    \skv@err{Number '\string#2' is not defined}\skv@ehd
  }%
}
\skvrobustdef*\skvpushnumber#1{%
  \skvifdefTF#1{%
    \edef#1{\the\numexpr#1+1\relax}%
  }{%
    \def#1{0}%
  }%
}
\skvrobustdef*\skvpopnumber#1{%
  \skvifdefTF#1{%
    \edef#1{\the\numexpr#1+1\relax}%
  }{%
    \skv@err{Number '\string#1' is not defined}\skv@ehd
  }%
}

\skvrobustdef\skv@testst#1{%
  \skvifstar{\skv@tempsttrue#1}{\skv@tempstfalse#1}%
}
% For internal use:
\skvrobustdef\skv@i@ifstar#1{\@ifnextchar*{\@firstoftwo{#1}}}
\skvrobustdef\skv@i@testst#1{%
  \skv@i@ifstar{\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}}}

% Remove spurious leading and trailing spaces before checking for outer brace.
% Such spaces do hide the outer brace.
% If we were to use \skvtrimspace for \skvifbracedTF, then the need
% for double \romannumeral will cause problems of premature expansion.
\begingroup
\lccode`\&=0 \catcode`\&=7
\skvnewgdef\skvifbracedTF#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\skvifldspaceTF\expandafter{\@gobble#1}%
    {\@gobble#1}{\expandafter\@space\@gobble#1}%
}
\skvnewgdef\skv@ifbraced@d#1\brace@nil#2{%
  \skvifstrcmpTF{#1}{#2}\@secondoftwo\@firstoftwo
}
\endgroup

\skvnewdef\skvifbracedFT#1{\skvifbracedTF{#1}\@secondoftwo\@firstoftwo}

% \skvstripouterbraces{<nr>}<cmd>
% Example:
% \def\x{{{{X}}}}
% \skvstripouterbraces{2}\x
\skvrobustdef*\skvstripouterbraces#1#2{%
  \skvifemptyTF#2{}{%
    \begingroup
    \@tempcnta\skvz@
    \expandafter\skv@stripouterbraces#2\strip@nil{#2}{#1}%
  }%
}
\skvrobustdef*\skv@stripouterbraces#1\strip@nil#2#3{%
  \edef\skv@prova{\unexpanded{#1}}%
  \skvifxTF#2\skv@prova{%
    \skvaftergroupdef#2\endgroup
  }{%
    \advance\@tempcnta\@ne
    \skvifnumTF\@tempcnta=#3{%
      \let#2\skv@prova
      \skvaftergroupdef#2\endgroup
    }{%
      \let#2\skv@prova
      \expandafter\skv@stripouterbraces#2\strip@nil{#2}{#3}%
    }%
  }%
}
\skvrobustdef*\skvifescapedTF#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\skvifescapedFT#1{\skvifescapedTF{#1}\@secondoftwo\@firstoftwo}
\skvnewdef*\skvremovescape#1{\expandafter\@gobble\string#1}
\skvnewdef*\skvgobblescape#1{%
  \skvifblankTF{#1}{}{%
    \skvifnumTF\escapechar>255{\string#1}{%
      \skvifnumTF\escapechar<0{\string#1}{%
        \skvifnumTF\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{%
  \expandafter\skvexpandonce\expandafter{\csname#1\endcsname}%
}
\skvnewdef*\skvexpandcstwice#1{%
  \expandafter\skvexpandtwice\expandafter{\csname#1\endcsname}%
}
\skvnewdef*\skvexpandcsthrice#1{%
  \expandafter\skvexpandthrice\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
% Eg, \skvexpandintonext\x[\y], \skvexpandintonext\x{\y}.
% This isn't expandable, because #1 may not be just one token.
\skvnewdef\skvexpandintonext#1{%
  \begingroup
  \edef\reserved@a{\endgroup\unexpanded{#1}}%
  \expandafter\reserved@a\expandafter
}
\skvrobustdef*\skvifinTF#1#2{%
  \skvtestifin{#1}{#2}%
  \csname @\ifin@ first\else second\fi oftwo\endcsname
}
\skvrobustdef*\skvifinFT#1#2{\skvifinTF{#1}{#2}\@secondoftwo\@firstoftwo}
\skvrobustdef*\skvxifinTF#1#2{\skvexpandtwoargs\skvifinTF{#1}{#2}}
\skvrobustdef*\skvxifinFT#1#2{\skvxifinTF{#1}{#2}\@secondoftwo\@firstoftwo}
\skvrobustdef*\skvifindetokTF#1#2#3{%
  \skvexpandtwoargs\skvifinTF{#1\skvoxdetok{#2}#1}{#1\skvoxdetok{#3}#1}%
}
\skvnewdef\skviffound#1\in#2\then{\skvtestifin{#1}{#2}\ifin@}
\skvnewdef\skvxiffound#1\in#2\then{\skvexpandtwoargs\skvtestifin{#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
}
\skvrobustdef*\skv@okvsplit#1{\skvexpbracenext\skv@kvsplit{#1}}
% \skv@slashsplit{<slashlist>}{<parametered.callback>}
% Patrons calling \skv@slashsplit should note that it may return
% ^skv^ for any missing components:
\skvrobustdef*\skv@slashsplit#1#2{%
  \begingroup
  \def\skv@tempa##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@slashsplit{%
    \endgroup#2%
  }%
  \skv@tempa#1/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^\skv@slashsplit
}
\skvrobustdef*\skv@oslashsplit#1{\skvexpbracenext\skv@slashsplit{#1}}

% \skv@splitpath@a{<path.macro>}{<pref.macro>}{<fam.macro>}
% Here, <path.macro> has the form {<pref>/<fam>}.
\skvrobustdef*\skv@splitpath@a#1#2#3{%
  \begingroup
  \ifx#1\@empty
    \skv@err{No key can have empty path}\skv@ehd
  \else
    \skv@oslashsplit{#1}{%
      \edef#2{\skvtrimspace{##1}}%
      \edef#3{\skvtrimspace{##2}}%
      \skvstripouterbraces{2}#2%
      \skvstripouterbraces{2}#3%
      \ifx#3\skv@rej
        \let#3#2%
        \let#2\skvdefaultprefix
      \fi
    }%
  \fi
  \skvexpanded{\endgroup\skvcmdexit#2\skvcmdexit#3}%
}
% \skv@splitpath@b{<path.macro>}{<pref.macro>}{<fam.macro>}
% Here, <path.macro> has the form {<pref>}{<fam>}.
% Here, we don't expect to find empty entries in #1, since
% \skv@splitpath@a wouldn't have made such an entry in #1. But don't
% mind, let us still test for empty.
\skvrobustdef*\skv@splitpath@b#1#2#3{%
  \begingroup
  \def\skv@prova##1##2\@nil{%
    \edef#2{\skvtrimspace{##1}}%
    \edef#3{\skvtrimspace{##2}}%
    \ifx#3\@empty
      \let#3#2%
      \let#2\skvdefaultprefix
    \fi
  }%
  \expandafter\skv@prova#1\@nil
  \skvexpanded{\endgroup\skvcmdexit#2\skvcmdexit#3}%
}
% \skv@formatpaths{<paths>}{<macro.to.reveive.formated.paths>}
%
% Format <paths> given in the form
%
%   {<pref1>/<fam1>},...,{<pref-n>/<fam-n>}
%
% into
%
%   \skv@pathdo{<pref1>}{<fam1>}...\skv@pathdo{<pref-n>}{<fam-n>}
%
\skvrobustdef*\skv@formatpaths#1#2{%
  \begingroup
  \skvifdefTF#2{}{\def#2{}}%
  \edef\skv@prova{#1}%
  \skvcsvnormalize[/]\skv@prova
  \skvcommaparse*\skv@prova\skv@prova{%
    \skv@splitpath@a\skv@prova\skv@tempa\skv@tempb
    \edef\skv@prova{{\skv@tempa}{\skv@tempb}}%
    \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok{#2}}{}{%
      \edef#2{\skvexpandonce#2\noexpand\skv@pathdo\skv@prova}%
    }%
  }%
  \skvaftergroupdef#2\endgroup
}
% \skv@makepaths{<prefix.macro>}{<families.macro>}{<pathlist.macro>}
%    {<wrapper.macro>}
%
% Attach prefix to families, to make pathlist. <prefix> must contain
% only one element.
%
\skvrobustdef*\skv@makepaths#1#2#3#4{%
  \begingroup
  \skv@ifnoprefix#1{\let#1\skvdefaultprefix}{}%
  \edef\skv@pref{#1}%
  \skvdespacecontent\skv@pref
  \skvstripouterbraces{2}\skv@pref
  \skv@ifoneprefix\skv@pref{%
    \skvifdefTF#3{}{\def#3{}}%
    \skvcommaparse*#2\skv@prova{%
      \skvstripouterbraces{2}\skv@prova
      \edef\skv@prova{{\skv@pref}{\skv@prova}}%
      \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok{#3}}{}{%
        \edef#3{\skvexpandonce{#3}\noexpand#4\skv@prova}%
      }%
    }%
  }{%
    \skv@err{Only one prefix allowed here}
      {The service you've called takes only one key prefix}%
  }%
  \skvaftergroupdef#3\endgroup
}
% \skv@ifoneprefix{<prefix>}
\skvrobustdef*\skv@ifoneprefix#1{%
  \skvxifinTF{,}{\skvoxdetok{#1}}{%
    \begingroup
    \def\skv@prova##1,##2\skv@nil{%
      \endgroup
      \skvifblankTF{##2}{%
        \def#1{##1}%
      }{%
        \@secondoftwo
      }%
    }%
    \expandafter\skv@prova#1\skv@nil
  }{%
    \@firstoftwo
  }%
}
% \skv@ifnoprefix{<prefix.macro>}
\skvrobustdef*\skv@ifnoprefix#1{%
  % Think twice before changing this conditional!
  \ifcase0%
    \ifx#1\skv@undefined\else\ifx#1\@empty\else 1\fi\fi\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\skvrobustdef*\skvifmacroTF#1{%
  \skvifntypeTF{#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}%
  }{%
    \@secondoftwo
  }%
}
\skvrobustdef*\skvifmacroFT#1{\skvifmacroTF{#1}\@secondoftwo\@firstoftwo}

% If there is a digit present in #1:
\skvnewdef*\skvifdigitpresentTF#1{%
  \skvifcondTF\if0\skvifblankTF{#1}{1}{\skv@ifdigitpresent#1\skv@nnil}\fi
}
\skvnewdef*\skv@ifdigitpresent#1{%
  \skvifxTF#1\skv@nnil{%
    1%
  }{%
    \skvifnumTF`#1>47{%
      \skvifnumTF`#1<58{%
        0\skv@removetonnil
      }{%
        \skv@ifdigitpresent
      }%
    }{%
      \skv@ifdigitpresent
    }%
  }%
}

\skvnewdef*\skvifalldigitTF#1{%
  \skvifcondTF\if0\skvifblankTF{#1}{1}{\skv@ifalldigit#1\skv@nnil}\fi
}
\skvnewdef*\skv@ifalldigit#1{%
  \skvifxTF#1\skv@nnil{0}{%
    \skvifnumTF`#1>47{%
      \skvifnumTF`#1<58{%
        \skv@ifalldigit
      }{%
        1\skv@removetonnil
      }%
    }{%
      1\skv@removetonnil
    }%
  }%
}

\skvnewdef*\skvifalphapresentTF#1{%
  \skvifcondTF\if0\skvifblankTF{#1}{1}{\skv@ifalphapresent#1\skv@nnil}\fi
}
\skvnewdef*\skv@ifalphapresent#1{%
  \skvifxTF#1\skv@nnil{%
    1%
  }{%
    \skvifnumTF`#1>96{%
      \skvifnumTF`#1<123{%
        0\skv@removetonnil
      }{%
        \skv@ifalphapresent
      }%
    }{%
      \skv@ifalphapresent
    }%
  }%
}
\skvnewdef*\skvifAlphapresentTF#1{%
  \skvifcondTF\if0\skvifblankTF{#1}{1}{\skv@ifAlphapresent#1\skv@nnil}\fi
}
\skvnewdef*\skv@ifAlphapresent#1{%
  \skvifxTF#1\skv@nnil{%
    1%
  }{%
    \skvifnumTF`#1>64{%
      \skvifnumTF`#1<91{%
        0\skv@removetonnil
      }{%
        \skv@ifAlphapresent
      }%
    }{%
      \skv@ifAlphapresent
    }%
  }%
}
\skvnewdef*\skvifprimitiveTF#1{%
  \expandafter\skv@ifprimitive\meaning#1\relax
}
\skvnewdef*\skv@ifprimitive#1#2\relax{%
  \skvifcondTF\if#1\@backslashchar\fi{%
    \skvifdigitpresentTF{#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{\skvifxTF#1{##1}\@secondoftwo}{%
        \unexpanded{\skvifprimitiveTF{##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\skvifblankTF{####1}}%
  }\x
}
\endgroup
\skvdefregistertester\skip\skvifskipTF
\skvdefregistertester\count\skvifcountTF
\skvdefregistertester\dimen\skvifdimenTF
\skvdefregistertester\toks\skviftoksTF

% 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*\skvifallarithmeticTF#1{%
  \begingroup
  \skv@swatrue
  \skvifdimensionable{#1}{}{%
    \ifskv@isinteger\else
      \def\do##1##2{%
        \ifx\do##1\else
          \skvifinTF{##1}{0123456789.+-*/()}{}{%
            \skvifinTF{,##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 \skvifintegerTF containes \numexpr:
\skvnewlet\skv@catch@nil\relax
% \skvensureinteger{<essence>}{<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}\skv@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*\skvifintegerTF#1{%
  \begingroup
  % Don't try '\numexpr0+#1' here:
  \afterassignment\skv@catchremainder
  \@tempcnta\numexpr0#1\relax\skv@catch@nil
  \skvifxTF\skv@elt\@empty{%
    \@firstoftwo
  }{%
    \skvifxTF\skv@elt\skv@relaxtoks\@firstoftwo\@secondoftwo
  }%
}
\skvrobustdef\skvoifinteger{\skvexpbracenext\skvifintegerTF}
\skvrobustdef\skvifdimensionable#1{%
  \def\skv@dimunit{}%
  % Does 'pt' mean 0pt or 1pt? Make it an invalid input:
  \lowercase{\skvifindetokTF{,}{#1}}\skv@validunit{%
    \skv@err{Illegal step '#1': no number found with it}\skv@ehd
  }{}%
  \skv@isintegerfalse
  \skvifintegerTF{#1}{%
    \skv@isintegertrue\@secondoftwo
  }{%
    \expandafter\skv@ifdimensionable\skv@elt\skv@catch@nil
  }%
}
\skvrobustdef\skv@ifdimensionable#1#2\skv@catch@nil{%
  \skvifxTF\relax#1{%
    \skvifblankTF{#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
  }%
  \skvifstrcmpTF{.}{#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.
    \skvifinTF{,#1,}{,\hsize,\vsize,}{%
      \skv@provb
    }{%
      \skvifdimenTF{#1}{%
        \skv@provb
      }{%
        \lowercase{\skvifindetokTF{,}{#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{%
  \skvifnumTF\@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
  \skvifblankTF{#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
  \skvifblankTF{#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
  \skvifblankTF{#2}{}{\skv@dimexpr@plus#2\skv@catch@nil}%
}
\skvrobustdef*\skv@dimexpr@minus#1-#2\skv@catch@nil{%
  \skvifblankTF{#1}{}{%
    \skvifinTF{*}{#1}{%
      \skvifinTF{/}{#1}{%
        \skv@err{I can't parse this: both star (*) and slash
          \MessageBreak (/) are in '#1'}\skv@ehd
      }{%
        \skvifblankTF{#1}{}{\skv@dimexpr@times#1*\skv@catch@nil}%
      }%
    }{%
      \skvifblankTF{#1}{}{\skv@dimexpr@divide#1/\skv@catch@nil}%
    }%
  }%
  \skvifblankTF{#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{%
  \skvifblankTF{#1}{}{%
    \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false
  }%
  \skvifblankTF{#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{%
  \skvifblankTF{#1}{}{%
    \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false
  }%
  \skvifblankTF{#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{%
  \skvifblankTF{#1}{}{%
    \skvifintegerTF{#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{%
    \skvxifinTF{\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{%
  \skvifblankTF{#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}\skv@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@
}
% \skvkvnormalize{<kvlistcmd>}
\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{%
      \skvifblankTF{##2}{%
        \skvifdefswitchTF{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{%
      \skvifblankTF{##2}{%
        \skv@spaceparser##1 #1\normal@nil
      }{%
        \skv@activeequal##1=##2\normal@nil
      }%
    }%
  }%
  \def\skv@spaceparser##1 #1##2\normal@nil{%
    \skvifblankTF{##2}{%
      \skv@parserspace##1#1 \normal@nil
    }{%
      \skv@spaceparser##1#1##2\normal@nil
    }%
  }%
  \def\skv@parserspace##1#1 ##2\normal@nil{%
    \skvifblankTF{##2}{%
      \skvifdefswitchTF{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{%
    \skvifblankTF{##2}{%
      \skv@equalspace##1= \normal@nil
    }{%
      \skv@spaceequal##1=##2\normal@nil
    }%
  }%
  \def\skv@equalspace##1= ##2\normal@nil{%
    \skvifblankTF{##2}{%
      \skv@doubleparser##1#1#1\normal@nil
    }{%
      \skv@equalspace##1=##2\normal@nil
    }%
  }%
  \def\skv@doubleparser##1#1#1##2\normal@nil{%
    \skvifblankTF{##2}{%
      \skvifdefswitchTF{inkv}{%
        \skv@doubleequal##1==\normal@nil
      }{%
        \skvifblankTF{##1}{}{%
          \skv@remleadparser##1\normal@nil
        }%
      }%
    }{%
      \skv@doubleparser##1#1##2\normal@nil
    }%
  }%
  \def\skv@doubleequal##1==##2\normal@nil{%
    \skvifblankTF{##2}{%
      \skvifblankTF{##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{%
    \skvifblankTF{##2}{}{%
      \skv@err{There is '#1=' in your key-value list}\skv@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{%
    \skvifblankTF{#2}
      {\skv@spaceslash#1 /\normal@nil}
      {\skv@activeslash#1/#2\normal@nil}%
  }%
}
\skvnewdef\skv@spaceslash#1 /#2\normal@nil{%
  \skvifblankTF{#2}
    {\skv@slashspace#1/ \normal@nil}
    {\skv@spaceslash#1/#2\normal@nil}%
}
\skvnewdef\skv@slashspace#1/ #2\normal@nil{%
  \skvifblankTF{#2}
    {\skvifblankTF{#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{%
    \skvifblankTF{##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{%
    \skvifblankTF{##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{%
    \skvifblankTF{##2}{ ##1}
      {\skv@mac@doubleparser##1#1##2\normal@nil}%
  }%
}
% \skvletmanytocmd{<cmd list>}{<cmd>}
\skvrobustdef*\skvletmanytocmd#1#2{%
  \skvcommaparse{#1}\skv@prova{%
    \expandafter\let\skv@prova= #2%
  }%
}
% \skvnewletmanytocmd{<cmd list>}{<cmd>}
\skvrobustdef*\skvnewletmanytocmd#1#2{%
  \skvcommaparse{#1}\skv@prova{%
    \expandafter\skv@newcastcommand\skv@prova=#2=\cast@nil
  }%
}
% \skvcastcommands{<cmd list>}
%
%   <cmd list> -> {<cmd-a1>=<cmd-a2>,...,<cmd-b1>=<cmd-b2>}
%
% In <cmd list>, cast left-hand command to the right-hand command.
% Raise error if left-hand command already exists and isn't equal to
% the right-hand command.
%
% Example:
%
%   \skvcastcommands{\ifxx=\ifx,\fii=\fi}
%
\skvrobustdef*\skvcastcommands#1{%
  \def\skv@provb##1=##2=##3\cast@nil{\let##1= ##2}%
  \skvkvparse{#1}\skv@prova{%
    \expandafter\skv@provb\skv@prova==\cast@nil
  }%
}
% \skvnewcastcommands{<cmd list>}
%
%   <cmd list> -> {<cmd-a1>=<cmd-a2>,...,<cmd-b1>=<cmd-b2>}
%
\skvrobustdef*\skvnewcastcommands#1{%
  \skvkvparse{#1}\skv@prova{%
    \expandafter\skv@newcastcommand\skv@prova==\cast@nil
  }%
}
\skvrobustdef*\skv@newcastcommand#1=#2=#3\cast@nil{%
  \skvifdefTF{#1}{%
    \ifx#1#2\else
      \skv@err{Command \noexpand#1 already exists:
        \MessageBreak it can't be cast to \string#2}\skv@ehd
    \fi
  }{%
    \let#1= #2%
  }%
}
% \skvpushfunctions<base-fn>{<fn-list>}<counter-number>
%
% 1. <counter-number> is a number, not a counter.
% 2. See file skeyval-for.tex for \skvsavestate and \skvrestorestate.
%
\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}}}%
  \skvifcsdefTF{\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-number>
\skvrobustdef*\skvpopfunctions#1#2{%
  \begingroup
  \edef\skv@elt{\skvnoexpandcs{\skvremovescape{#1}@skv@stack}}%
  \expandafter\skvifdefTF\skv@elt{%
    \expandafter\ifx\skv@elt\@empty
      \skv@err{Stack of '\noexpand#1' is empty}\skv@ehd
    \fi
  }{%
    \skv@err{Stack of '\noexpand#1' is undefined}\skv@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 (a macro), #2=list, #3=max depth
\skvrobustdef*\skvbuildmacrostack#1#2#3{%
  \begingroup
  \escapechar92\relax
  \ifdefined#1%
    \skv@err{Stack \noexpand#1 already exists}\skv@ehd
  \else
    \def#1{}%
  \fi
  \@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.
    \skvifnumTF\@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}\skv@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}\skv@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
      \skvifcsdefTF{if#1##1}{%
        \skv@err{Boolean '\skvnoexpandcs{if#1##1}' already exists}\skv@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@
}

% Some booleans:
%
% inset             = in \skvsetkeys
% viarootofsetkeys  = a key is being set via the root of the command
%                     \skvsetkeys. This is needed because sometimes we
%                     bypass the root of \skvsetkeys, eg, by calling
%                     \skv@setkeys@a.
% inclass           = in class file.
% kf                = key has been found while in \skvsetkeys.
% intry             = in 'try set keys'.
% insa              = in 'search also set keys'.
% success           = key has been found while in 'try set keys'.
% inopt             = in process/execute options.
% inpox             = in process options.
% inprepo           = presetting or post-setting keys.
% inoptionsec       = in skeyval package's options processing section.
%                     Some checks are done in that section.
%
\skvnewbools[skv@]{%
  inclass,inset,viarootofsetkeys,processvalueaslist,st,cl,pl,kf,inpox,inopt,%
  inprepo,tracingkeys,swa,swb,verbose,intry,insa,success,inoptionsec,%
  inreservedhandler,ignorepointers%
}

\skvnewbools{indirectkeys}
\skvnewbools[skv]{indef,novalue,valuebraced}
\skvnewbools[dirkeys@]{saveunknownkeys}
% \skvnewcounts[<optional prefix>]{<name list>}
\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
      \skvifcsdefTF{#1##1}{%
        \skv@err{Counter '\skvnoexpandcs{#1##1}' already exists}\skv@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@]{tempcnt,cnta}
% \skvnewnumbers[<optional prefix>]{<name list>}
\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
      \skvifcsdefTF{#1##1}{%
        \skv@err{Number '\skvnoexpandcs{#1##1}' already exists}\skv@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@
}
\skvnewlet\skvnewnumber\skvnewnumbers
\skvnewnumbers[skv@]{keydepth,setkeysdepth,preposetdepth,makekeysdepth,
  usekeysdepth,trysetkeysdepth,trysetkeysidepth,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 define and set command/store keys.
% 2. Pointers aren't used here, to speed up parsing. Use other key
%    machineries instead.
% 3. Package and class options can't be passed via quick keys.
% 4. When defining keys, the syntax of <list> is:
%
%    {<keya-1,...,keya-n>}/{<default>}/{<callback>}/{<choice list>}/{<arg>}
%    ;...;
%    {<keyb-1,...,keyb-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 keys '.exec' and '.exec code' will simply execute the
%    code <default>, without defining anything.
% 6. Use the token '.na' to indicate a missing item that isn't blank or empty.
% 7. The parser for defining the keys can be specified via \setquickkeysparser.
%    The default parser is semicolon (;).
%
% 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@ifstrrejorempty#1{%
  \skvifstrcmpTF{#1}{^skv^}{%
    \@firstoftwo
  }{%
    \skvifblankTF{#1}%
  }%
}
\skvnewdef*\skv@ifstrrejordotna#1{%
  \skvifstrcmpTF{#1}{^skv^}{%
    \@firstoftwo
  }{%
    \skvifstrcmpTF{#1}{.na}%
  }%
}
\skvnewdef*\skv@ifstrnut#1{%
  \skvifstrcmpTF{#1}{^skv^}{%
    \@firstoftwo
  }{%
    \skvifstrcmpTF{#1}{.na}{%
      \@firstoftwo
    }{%
      \skvifblankTF{#1}%
    }%
  }%
}
\skvnewdef*\skv@ifcmdrejorempty#1{%
  \skvifxTF#1\skv@rej{%
    \@firstoftwo
  }{%
    \skvifemptyTF#1%
  }%
}
\skvnewdef*\skv@ifcmdrejordotna#1{%
  \skvifxTF#1\skv@rej{%
    \@firstoftwo
  }{%
    \skvifxTF#1\skv@dotna
  }%
}
\skvnewdef*\skv@ifcmdnut#1{%
  \skvifxTF#1\skv@rej{%
    \@firstoftwo
  }{%
    \skvifxTF#1\skv@dotna{%
      \@firstoftwo
    }{%
      \skvifemptyTF#1%
    }%
  }%
}
% \skv@qkeysgetkeynames{<kvlist.cmd>}{<return.cmd>}
\skvrobustdef*\skv@qkeysgetkeynames#1#2{%
  \begingroup
  \let#2\@empty
  \skvkvparse*#1\skv@prova{%
    \def\do##1=##2=##3\skv@nil{%
      \edef#2{\skvaddlist,#2##1}%
    }%
    \expandafter\do\skv@prova==\skv@nil
  }%
  \skvaftergroupdef#2\endgroup
}
\skvrobustdef*\skvsetquickkeysparser{\def\skv@qkeysparser}
\skvsetquickkeysparser{;}
\skvrobustdef*\quickkeys#1#{%
  \edef\skv@tempa{\skvtrimspace{#1}}%
  \skvxifstrcmpTF{\skvexpandonce\skv@tempa}{.define}{%
    \begingroup\endlinechar\m@ne
    \skv@defquickkeys
  }{%
    \skvxifstrcmpTF{\skvexpandonce\skv@tempa}{.set}{%
      \skv@setquickkeys
    }{%
      \skvxifstrcmpTF{\skvexpandonce\skv@tempa}{.preset}{%
        \skv@presetquickkeys
      }{%
        \skv@err{Unknown task for \string\quickkeys}\skv@ehd
      }%
    }%
  }%
}
\skvrobustdef*\skv@defquickkeys#1{\skv@testopt{\skv@defquickkeys@a{#1}}{}}
\skvrobustdef*\skv@defquickkeys@a#1[#2]#3{%
  \endgroup
  \edef\skv@qkeysfam{\skvtrimspace{#1}}%
  \edef\skv@tempa{\skvkeepdoubleslashnormalize{#3}}%
  \skvexpanded{\skvparselist*{\skv@qkeysparser}}\skv@tempa\skv@tempa{%
    \skv@oslashsplit\skv@tempa{%
      \skvifinTF{,##1,}{,.exec,.exec code,}{%
        ##2\relax
      }{%
        % Loop over key list, in case there are more than one key:
        \skvparselist{,}{##1}\skv@tempa{%
          % If ##2 is empty, the default will be empty:
          \skv@ifstrrejordotna{##2}{}{%
            \skvcsedef{\skv@qkeysfam/\skv@tempa/qkeys.def}{\unexpanded{##2}}%
          }%
          \skvcsedef{\skv@qkeysfam/\skv@tempa/qkeys.cbk}{%
            \skvifblankTF{#2}{}{%
              \skvcsletcs{#2\skv@tempa}{\skv@qkeysfam/\skv@tempa/qkeys.val}%
            }%
            \skv@ifstrrejordotna{##3}{}{\unexpanded{##3}}%
          }%
          \skv@ifstrnut{##4}{}{%
            \skvcsedef{\skv@qkeysfam/\skv@tempa/qkeys.chc}{\unexpanded{##4}}%
          }%
          \skv@ifstrnut{##5}{}{%
            \skvcsedef{\skv@qkeysfam/\skv@tempa/qkeys.arg}{\unexpanded{##5}}%
          }%
        }%
      }%
    }%
  }%
}

% \quickkeys.preset{<family>}{<kvlist>}
%
\skvrobustdef*\skv@presetquickkeys#1#2{%
  \edef\skv@prova{\unexpanded{#2}}%
  \skvkvnormalize\skv@prova
  \skvcslet{\skvtrimspace{#1}/qkeys.preset}\skv@prova
}

\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@qkeysfam\do\skv@qkeysna\do\skv@qkeyskvlist
    \do\skv@qkeyscurrnames
  }\skv@setqkeysdepth
  \edef\skv@qkeysna{\unexpanded{#2}}%
  \skvcsvnormalize\skv@qkeysna
  \edef\skv@qkeyskvlist{\unexpanded{#3}}%
  \skvkvnormalize\skv@qkeyskvlist
  \edef\skv@qkeysfam{\skvtrimspace{#1}}%
  \def\skv@setqkeys@a##1##2{%
    \skvifcsdefTF{\skv@qkeysfam/##1/qkeys.cbk}{}{%
      \skv@err{Key '\skv@qkeysfam/##1'
        \MessageBreak is not defined}\skv@ehd
    }%
    \skvifstrcmpTF{##2}\skv@novalue{%
      \skvifcsdefTF{\skv@qkeysfam/##1/qkeys.def}{%
        \skvcsletcs{\skv@qkeysfam/##1/qkeys.val}{\skv@qkeysfam/##1/qkeys.def}%
      }{%
        \skv@err{No user value and no default
          \MessageBreak for key '\skv@qkeysfam/##1'}\skv@ehd
      }%
    }{%
      \skvcsedef{\skv@qkeysfam/##1/qkeys.val}{\unexpanded{##2}}%
    }%
    \skvletcs\skv@tempval{\skv@qkeysfam/##1/qkeys.val}%
    \skvletcs\reserved@a{\skv@qkeysfam/##1/qkeys.chc}%
    \skvifdefTF\reserved@a{%
      \skvxifinTF{,\detokenize{##2},}{,\skvoxdetok\reserved@a,}{}{%
        \skv@err{Value '\detokenize{##2}' for key '##1'
          \MessageBreak is not in prescribed list
          \MessageBreak '\skvoxdetok\reserved@a'}\skv@ehd
      }%
    }{}%
    \skvifcsdefTF{\skv@qkeysfam/##1/qkeys.arg}{%
      \skvletcs\skv@argpattern{\skv@qkeysfam/##1/qkeys.arg}%
    }{%
      \let\skv@argpattern\skv@simplearg
    }%
    \skvletcs\skv@callback{\skv@qkeysfam/##1/qkeys.cbk}%
    \expandafter\expandafter\expandafter\def\expandafter\expandafter
      \expandafter\reserved@a\expandafter\skv@argpattern\expandafter
      \quickkeyseov\expandafter{\skv@callback}%
    \expandafter\reserved@a\skv@tempval\quickkeyseov
  }%
  \def\skv@setqkeys@b##1=##2=##3\skv@setqkeys@nil{%
    \skvxifinTF{,\detokenize{##1},}{,\skvoxdetok\skv@qkeysna,}{}{%
      \skv@setqkeys@a{##1}{##2}%
    }%
  }%
  \skvifcsdefFT{\skv@qkeysfam/qkeys.preset}{}{%
    \skv@qkeysgetkeynames\skv@qkeyskvlist\skv@qkeyscurrnames
    \def\skv@setqkeys@c##1=##2=##3\skv@setqkeys@nil{%
      \skvxifinTF{,\detokenize{##1},}{,\skvoxdetok\skv@qkeyscurrnames,}{}{%
        \let\skv@setquickkeys@a\skv@bad@setquickkeys@a
        \skv@setqkeys@b##1={##2}=##3\skv@setqkeys@nil
        \let\skv@setquickkeys@a\skv@sav@setquickkeys@a
      }%
    }%
    \skvletcs\skv@tempa{\skv@qkeysfam/qkeys.preset}%
    \skvkvparse*\skv@tempa\skv@tempa{%
      \expandafter\skv@setqkeys@c\skv@tempa=\skv@novalue=\skv@setqkeys@nil
    }%
  }%
  \skvkvparse*\skv@qkeyskvlist\skv@tempa{%
    \expandafter\skv@setqkeys@b\skv@tempa=\skv@novalue=\skv@setqkeys@nil
  }%
  \skvrestorestate\skv@setquickkeys\skv@setqkeysdepth
}
\skvnewlet\skv@sav@setquickkeys@a\skv@setquickkeys@a
\skvrobustdef*\skv@bad@setquickkeys@a{%
  \skv@err{'\string\quickkeys.set' can't be nested in a preset
    \MessageBreak key's callback: this will cause a cyclic call
    \MessageBreak to '\string\quickkeys.set'}\skv@ehd
}

% \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
%
% 1. \skvifcase is expandable but, to simplify the code, \elsedo is mandatory.
%    See \ltsifcases of ltxtools package; it is expandable and the 'else part'
%    can be omitted in it.
% 2. \skvifcasse and \skvifcases aren't expandable, but \elsedo isn't
%    mandatory.
%
% Example:
%
%  \skvifcase\skvifstrcmpTF{c}
%    {a}{do a}
%    {b}{do b}
%    {c}{do c}
%  \elsedo
%    no match%
%  \endif
%
% \skvifcase is expandable.
%
\skvnewdef\skvifcase#1#2#3\elsedo#4\endif{%
  % {#2}{#4} represent default action:
  \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{\skvtrimspace{#1}}

%  \skvifcasse{<teststr>}
%    {<cases>}
%  \elsedo
%    {<no match>}
%  \endif
%
% 1. \elsedo can be omitted by the user, ie, it is optional. See also
%    \skvifcases.
% 2. Because of the need to remove blank spaces before 'case',
%    \skvifcasse isn't expandable.
%
% Example:
%
%  \skvifcasse{x}
%     case{a}{\def\x{do a}}
%     case{b}{\def\x{do b}}
%     case{c}{\def\x{do c}}
%  \elsedo
%     \def\x{no match}
%  \endif
%
\skvrobustdef*\skvifcasse#1#2\endif{%
  \skvxifinTF{\detokenize{\elsedo}}{\detokenize{#2}}{%
    \begingroup
    \def\skv@prova##1\elsedo##2\skv@ifcasse@nil{%
      \endgroup
      \skv@ifcasse@a{#1}##1case{#1}{##2}\skv@casse@stop
    }%
    \skv@prova#2\skv@ifcasse@nil
  }{%
    \skv@ifcasse@a{#1}#2case{#1}{}\skv@casse@stop
  }%
}
\skvrobustdef*\skv@ifcasse@a#1{%
  \@ifnextchar\relax{\skv@ifcasse@b#1}{\skv@ifcasse@b#1}%
}
\skvrobustdef*\skv@ifcasse@b#1case#2{%
  \skvxifstrcmpTF{#1}{#2}{%
    \skv@ifcasse@domatch
  }{%
    \skv@ifcasse@c{#1}%
  }%
}
\skvrobustdef*\skv@ifcasse@c#1#2{\skv@ifcasse@a{#1}}
\skvrobustdef*\skv@ifcasse@domatch#1#2\skv@casse@stop{\skvtrimspace{#1}}

%  \skvifcases<teststr>\then
%    <cases>{<acts>}
%    <default>{<act>}
%  \fi
%
% 1. <default>{<act>} can be omitted by the user, ie, it is optional.
% 2. \skvifcases isn't expandable.
%
% Example:
%  \skvifcases x\then
%     case{a}{\def\x{do a}}
%     case{b}{\def\x{do b}}
%     case{c}{\def\x{do c}}
%     default{\def\x{no match}}
%  \fi
%
\skvrobustdef*\skvifcases#1\then#2\fi{%
  % Any space trailing #2 will be absorbed by ##3 of the following
  % \skv@prova.
  \begingroup
  \def\skv@prova##1default##2##3\skv@ifcases@nil{%
    \endgroup
    \skvifstrcmpTF{##2}{\@nil}{%
      \skv@ifcases@a{#1}##1case{#1}{}\skv@cases@stop
    }{%
      \skv@ifcases@a{#1}##1case{#1}{##2}\skv@cases@stop
    }%
  }%
  \skv@prova#2default{\@nil}\skv@ifcases@nil
}
\skvrobustdef*\skv@ifcases@a#1{%
  \@ifnextchar\relax{\skv@ifcases@b#1}{\skv@ifcases@b#1}%
}
\skvrobustdef*\skv@ifcases@b#1case#2{%
  \skvxifstrcmpTF{#1}{#2}{%
    \skv@ifcases@domatch
  }{%
    \skv@ifcases@c{#1}%
  }%
}
\skvrobustdef*\skv@ifcases@c#1#2{\skv@ifcases@a{#1}}
\skvrobustdef*\skv@ifcases@domatch#1#2\skv@cases@stop{#1}

% Evaluating a series of boolean expressions.
%
% Example:
%
% \skvifexprTF{%
%   not ( expr { \skvifdefTF\xa } and expr { \skvifemptyTF\xa } )
%   or ( expr { \skvifboolTF{@tempswa} } or expr { \skvifxTF\xa\xb } )
% }{%
%   \def\x{T}
% }{%
%   \def\x{F}
% }
\skvnewnumbers[skv@]{exprcnt}
\skvrobustdef*\skvifexprTF#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
  \skvifblankTF{#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
  \skvifblankTF{#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
  \skvifblankTF{#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
  \skvifblankTF{#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
  \skvifblankTF{#2}{}{%
    \def\skv@expr@neg{00}%
    \skv@expr@not#2\skv@expr@nil
  }%
}
\skvrobustdef\skv@expr@do#1expr#2\skv@expr@nil{%
  \skvifblankTF{#1}{}{%
    \skv@err{Invalid \noexpand\skvifexprTF test expression}
      {The handicapped test part is: '\detokenize{#1}'}%
  }%
  \skvifblankTF{#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{%
  \skvifcondTF{#1}\fi{#2\skvwhileexpr{#1}\do{#2}}{}%
}

\skvbuildmacrostack\skv@keystate{%
  \do\skvcurrentprefix\do\skv@fams\do\skvcurrentfamily
  \do\skvcurrentkey\do\skv@header\do\skvcurrentpath\do\skv@na
  \do\skv@currentnames\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@inprepo\do\skv@pathdo\do\skv@rmkeys
  \do\ifskv@viarootofsetkeys\do\skv@currpref\do\skv@currfam
}\skv@stackdepthlimit

\skvbuildmacrostack\skv@dirkeys@state{%
  \do\dirkeys@pathlist\do\dirkeys@holderprefixtoks\do\dirkeys@parser
  \do\dirkeys@unknownkeysmacro\do\ifdirkeys@saveunknownkeys
  \do\ifindirectkeys\do\ifskvdfk@initialize\do\ifskvdfk@saveinitialvalues
}\skv@stackdepthlimit

\skvrobustdef\skvappto#1#2{%
  \edef#1{\skvexpandonce{#1}\unexpanded{#2}}%
}
\skvrobustdef\skvgappto#1#2{%
  \xdef#1{\skvexpandonce{#1}\unexpanded{#2}}%
}
\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{%
  \skvifescapedTF{#3}{%
    \@nameuse{#2def}#3{%
      \skvifstrcmpTF{#1}{app}{%
        \skvifdefTF#3{\skvexpandonce#3}{}%
        \skvifdefboolTF{skv@tempst}\skvexpandonce\unexpanded{#4}%
      }{%
        \skvifdefboolTF{skv@tempst}\skvexpandonce\unexpanded{#4}%
        \skvifdefTF#3{\skvexpandonce#3}{}%
      }%
    }%
  }{%
    \skv@err{Token '\detokenize{#3}' is not escaped}\skv@ehd
  }%
}

% \skvaddlist{<parser>}{<listcmd>}
% This is usually called under \edef. <listcmd> must have been
% initialized.
\skvnewdef*\skvaddlist#1#2{%
  \skvifemptyTF#2{}{\skvexpandonce#2\unexpanded{#1}}%
}
% \skvxaddtolist{<parser>}{<listcmd>}{<newitem>}
\skvrobustdef\skvxaddtolist#1#2#3{\edef#2{\skvaddlist#1#2#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{%
    \skvifdefTF#3{%
      \skvifemptyTF#3{}{\skvexpandonce#3#2}%
    }{}%
    \skvifdefboolTF{skv@tempst}\skvexpandonce\unexpanded{#4}%
  }%
}
% \skv@declarefilter{<filter.type>}
\skvrobustdef*\skv@declarefilter#1{%
  \edef\skv@filter##1##2{%
    \skvifblankTF{#1}{\let##2=##1}{%
      \skvifxTF#1\relax{\let##2=##1}{%
        \skvifstrcmpTF{#1}\nofilter{\let##2=##1}{%
          \unexpanded{#1}{##1}{##2}%
        }%
      }%
    }%
  }%
}

% \skvfiltermergelist![<parser>](<wrapper>)<cmd>{<sublist>}<filter>
%
% 1. The exclamation mark (!) implies that the outcome is globalized.
% 2. When there is no filter to be applied, \skvmergelist is faster
%    than \skvfiltermergelist.
% 3. <wrapper> will be applied to all the elements (new and old).
%    The default value of <wrapper> is \skvexpandonce.
% 4. Example:
%      \def\alist{a;b;c}
%      \def\wrap#1{{#1}}
%      \skvfiltermergelist[;](\wrap)\alist{a;d;e}\nofilter
%      \show\alist
%
\skvrobustdef*\skvfiltermergelist{%
  \skv@testcl{\skv@testopt\skv@filtermergelist,}%
}
\skvrobustdef*\skv@filtermergelist[#1]{%
  \skv@testpnopt{\skv@f@ltermergelist{#1}}\skvexpandonce
}
\skvrobustdef*\skv@f@ltermergelist#1(#2)#3#4#5{%
  \begingroup
  \skv@declarefilter{#5}%
  \skvifdefTF#3{\skvcsvnormalize[#1]#3}{\def#3{}}%
  \edef\skv@merga{\unexpanded{#4}}%
  \skvcsvnormalize[#1]\skv@merga
  \skvdolist*{#1}\skv@merga\skv@merga{%
    \@tempswatrue
    \skv@filter\skv@merga\skv@mergb
    \let\skv@mergc#3%
    \let#3\@empty
    \skvdolist*{#1}\skv@mergc\skv@mergc{%
      \skv@filter\skv@mergc\skv@mergd
      \ifx\skv@mergb\skv@mergd
        \@tempswafalse
        \edef#3{\skvaddlist#1#3#2{\skv@merga}}%
      \else
        \edef#3{\skvaddlist#1#3#2{\skv@mergc}}%
      \fi
    }%
    \if@tempswa
      \edef#3{\skvaddlist#1#3#2{\skv@merga}}%
    \fi
  }%
  \skvaftergroupdef#3\endgroup
  \ifskv@cl\global\let#3#3\fi
}
% \skvmergelist![<parser>](<wrapper>)<cmd>{<sublist>}
%
% 1. The exclamation mark (!) implies that the outcome is globalized.
% 2. <wrapper> will be applied to only the new elements. The default
%    value of <wrapper> is \skvexpandonce.
\skvrobustdef*\skvmergelist{\skv@testcl{\skv@testopt\skv@mergelist,}}
\skvrobustdef*\skv@mergelist[#1]{%
  \skv@testpnopt{\skv@m@rgelist{#1}}\skvexpandonce
}
\skvrobustdef*\skv@m@rgelist#1(#2)#3#4{%
  \begingroup
  \skvifdefTF#3{\skvcsvnormalize[#1]#3}{\def#3{}}%
  \edef\skv@tempa{\unexpanded{#4}}%
  \skvcsvnormalize[#1]\skv@tempa
  \skvdolist*{#1}\skv@tempa\skv@tempa{%
    \skvxifinTF{#1\skvoxdetok\skv@tempa#1}{#1\skvoxdetok#3#1}{}{%
      \edef#3{\skvaddlist#1#3#2{\skv@tempa}}%
    }%
  }%
  \skvaftergroupdef#3\endgroup
  \ifskv@cl\global\let#3#3\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}%
  \skvifdefTF#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%
  \skvifdefTF#1{}{\def#1{}}%
  \edef\skv@rema{\unexpanded{#2}}%
  \skvcsvnormalize\skv@rema
  \let\skv@remb#1\let#1\@empty
  \skvcommaparse*\skv@remb\skv@tempa{%
    \skvxifinTF{,\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}%
  \skvifdefTF#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
}
\skvnewdef*\skvcurrentfullkey{\skv@header\skvcurrentkey}
\skvnewlet\skvcurrenttriple\skvcurrentfullkey
\skvrobustdef*\skvsetdefaultprefix#1{%
  \edef\skvdefaultprefix{\skvtrimspace{#1}}%
  \let\skv@defapf\skvdefaultprefix
}
\skvsetdefaultprefix{KV}
\skvrobustdef*\skv@makeprefix#1{%
  % It is required to first fully expand the prefix:
  \edef\skvcurrentprefix{#1}%
  \skvdespacecontent\skvcurrentprefix
}
% \skv@makeheader{<family>}
\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
  \ifx\skvcurrentprefix\@undefined
    \let\skvcurrentprefix\skvdefaultprefix
  \fi
  \edef\skv@header{%
    \ifx\skvcurrentprefix\@empty\else\skvcurrentprefix/\fi
    \ifx\skvcurrentfamily\@empty\else\skvcurrentfamily/\fi
  }%
  \ifx\skv@header\@empty
    \def\skv@header{/}%
    \let\skvcurrentpath\skv@header
  \else
    \def\reserved@a##1/\@nil{##1}%
    \edef\skvcurrentpath{\expandafter\reserved@a\skv@header\@nil}%
    \ifx\skvcurrentpath\@empty
      \def\skvcurrentpath{/}%
    \fi
  \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}}\skvdefaultprefix
}
\skvrobustdef*\skv@t@stoptb#1[#2]#3{%
  \skv@makeprefix{#2}\skv@makeheader{#3}#1%
}
\skvrobustdef*\skv@testoptc#1{%
  \skv@testopt{\skv@t@stoptc{#1}}\skvdefaultprefix
}
\skvrobustdef*\skv@t@stoptc#1[#2]#3{%
  \skvxifinTF{,}{\detokenize{#2}}{%
    \skv@err{Only one prefix is allowed here,
      \MessageBreak but you gave '#2'}\skv@ehd
  }{%
    \skv@makeprefix{#2}%
    \edef\skv@fams{#3}%
    \skvxifinTF{,}{\detokenize{#3}}{%
      \skvcsvnormalize\skv@fams
    }{%
      \skvdespacecontent\skv@fams
    }%
    \skv@testopt#1{}%
  }%
}
% \skv@testoptd{<cmd>}{<pre-mp>}
\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}%
  }%
}
% \skv@t@stoptd{<cmd>}[<mp>]{<key>}
\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@getname@nil#2%
}
\skvrobustdef*\skv@g@tkeyname#1=#2\skv@getname@nil#3{%
  \skv@strippointersfromkey{#1}%
  \let#3=\skvcurrentkey
}
\skvrobustdef*\skv@getkeyvalue#1#2{%
  \expandafter\skv@g@tkeyvalue#1==\skv@getvalue@nil#2%
}
\skvrobustdef*\skv@g@tkeyvalue#1={\skv@g@tk@yvalue#1=.}
\skvrobustdef*\skv@g@tk@yvalue#1=#2=#3\skv@getvalue@nil#4{%
  \edef#4{\unexpanded\expandafter{\@gobble#2}}%
  \skvexpbracenext\skvifbracedTF#4{%
    \skvvaluebracedtrue
  }{%
    \skvvaluebracedfalse
    \skvexpbracenext\skv@strippointersfromvalue#4%
  }%
  \let#4=\skvcurrentvalue
}
% \skv@getnamesofkeys{<kv list>}{<result macro>}
\skvrobustdef*\skv@getnamesofkeys#1#2{%
  \begingroup
  \edef\skv@prova{\unexpanded{#1}}%
  \skvkvnormalize\skv@prova
  \let#2\@empty
  \skvcommaloop*\skv@prova\skv@prova{%
    \expandafter\skv@g@tkeyname\skv@prova=\skv@getname@nil\skv@prova
    \edef#2{\skvaddlist,#2\skvexpandonce\skv@prova}%
  }%
  \skvaftergroupdef#2\endgroup
}
\skvnewdef*\skv@badkeynames{}
\skvnewdef*\skvaddbadkeynames{\skvappendtomacro\skv@badkeynames}
\skvrobustdef*\skv@definedefault#1#2{%
  \ifskv@inoptionsec\else
    \skvxifinTF{,\skvcurrentprefix,}{,skv,SKV,}{%
      \skvxifinTF{,\skvcurrentfamily,}{,skeyval,}{%
        \skv@err{Prefix/family '\skvcurrentpath' are reserved}\skv@ehd
      }{}%
    }{}%
  \fi
  \ifskv@tracingkeys
    \skvxifinTF{,#1,}{,\skv@badkeynames,}{%
      \skv@err{Key name '#1' not allowed}\skv@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
  \skvifcsdefFT{\skvcurrentpath.@needini}{%
    \endgroup\skv@swafalse
  }{%
    \skv@swatrue
    \skvxifinTF{,#1,}{,\skvexpandcsonce{\skvcurrentpath.@needini},}{%
      \edef\skv@tempa{\skvtrimspace{#2}}%
      \edef\skv@tempa{%
        #1=\skvoifstrcmpTF{\skv@tempa}{true}{false}{\skvexpandonce\skv@tempa}%
      }%
      \skvletcs\skv@tempb{\skvcurrentpath.@inikv}%
      \skvifdefTF\skv@tempb{%
        \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@tempb,}{}{%
          \skvcsedef{\skvcurrentpath.@inikv}{%
            \skvaddlist,\skv@tempb\skvexpandonce\skv@tempa
          }%
        }%
      }{%
        \skvcsedef{\skvcurrentpath.@inikv}{\skvexpandonce\skv@tempa}%
      }%
    }{}%
  }%
  \ifskv@swa
    \expandafter\skvaftergroupdef\csname
      \skvcurrentpath.@inikv\expandafter\endcsname
      \expandafter\endgroup
  \fi
}

% Key will be given in the form <pointer>{<key>}. The command
% \skv@strippointersfromkey is called both when defining and setting keys.
%
% Notes:
%  1.  When setting keys, we also call \skv@replacepointers to replace
%      '.use value' pointer with the value of the key.
%  2.  There is no pointer '.save value', since the key's value is
%      always saved automatically.
%
% \skv@strippointersfromkey{<key name>}
% \skv@strippointersfromkey*{<key name cmd>}
%
% Return \skvcurrentkey.
%
\skvrobustdef*\skv@strippointersfromkey{%
  \skv@i@testst\skv@str@ppointersfromkey
}
\skvrobustdef*\skv@str@ppointersfromkey#1{%
  \begingroup
  \toks@{}%
  \skv@usetempst{#1}\skv@tempa
  % We can choose to ignore the search for pointers where we don't
  % use pointers. This is useful at least in one respect: during debugging
  % in non-pointer circumstances. In general, it is not advisable to ignore
  % pointers.
  \ifskv@ignorepointers\else
    \def\skv@tempb{\expandafter\skv@findpointer\expandafter{\skv@tempa}}%
    \def\skv@add##1{\edef\x{\toks@{\the\toks@##1}}\x}%
    \skv@tempb{.need value}\skv@tempa{%
      \skv@add{\skvcsdef{\skv@header\skv@tempa.@ndv}{}}%
    }{%
      \skv@tempb{.gneed value}\skv@tempa{%
        \skv@add{\skvcsgdef{\skv@header\skv@tempa.@ndv}{}}%
      }{%
        \skv@tempb{.forbid value}\skv@tempa{%
          \skv@add{\skvcsdef{\skv@header\skv@tempa.@fbv}{}}%
        }{%
          \skv@tempb{.gforbid value}\skv@tempa{%
            \skv@add{\skvcsgdef{\skv@header\skv@tempa.@fbv}{}}%
          }{}%
        }%
      }%
    }%
  \fi
  \skvexpanded{\endgroup
    \the\toks@
    % The following complication is because of, e.g., pntkeys:
    \edef\noexpand\skvcurrentkey{%
      \noexpand\unexpanded{\skvexpandonce\skv@tempa}%
    }%
  }%
}
% \skv@strippointersfromvalue{<key value>}
% \skv@strippointersfromvalue*{<key value cmd>}
%
% Return \skvcurrentvalue.
%
\skvrobustdef*\skv@strippointersfromvalue{%
  \skv@i@testst\skv@str@ppointersfromvalue
}
\skvrobustdef*\skv@str@ppointersfromvalue#1{%
  \begingroup
  \skv@usetempst{#1}\skv@tempa
  \ifskv@ignorepointers\else
    \def\skv@tempb{\expandafter\skv@findpointer\expandafter{\skv@tempa}}%
    % The following order is important, since '.expand' will match
    % '.expand once' and erroneously give the key's value as 'o'.
    \skv@tempb{.expand once process list}\skv@tempa{%
      \skv@processvalueaslisttrue
      \edef\skv@tempa{\skvexpandtwice\skv@tempa}%
    }{%
      \skv@tempb{.expand twice process list}\skv@tempa{%
        \skv@processvalueaslisttrue
        \edef\skv@tempa{\skvexpandthrice\skv@tempa}%
      }{%
        \skv@tempb{.expand process list}\skv@tempa{%
          \skv@processvalueaslisttrue
          \edef\skv@tempa{\skv@tempa}%
        }{%
          \skv@tempb{.eprocess list}\skv@tempa{%
            \skv@processvalueaslisttrue
            \edef\skv@tempa{\skv@tempa}%
          }{%
            \skv@tempb{.process list}\skv@tempa{%
              \skv@processvalueaslisttrue
            }{%
              \skv@tempb{.expand once}\skv@tempa{%
                \edef\skv@tempa{\skvexpandtwice\skv@tempa}%
              }{%
                \skv@tempb{.expand twice}\skv@tempa{%
                  \edef\skv@tempa{\skvexpandthrice\skv@tempa}%
                }{%
                  \skv@tempb{.expanded}\skv@tempa{%
                    \edef\skv@tempa{\skv@tempa}%
                  }{%
                    \skv@tempb{.expand}\skv@tempa{%
                      \edef\skv@tempa{\skv@tempa}%
                    }{}%
                  }%
                }%
              }%
            }%
          }%
        }%
      }%
    }%
  \fi
  \let\skvcurrentvalue\skv@tempa
  \skvexpanded{\endgroup
    \skvcmdexit\skvcurrentvalue
    \skvboolexit\ifskv@processvalueaslist
  }%
}
\skvrobustdef*\skv@findpointer#1#2#3{%
  \def\skv@f@ndpointer##1#2##2##3\skv@pointer@nil{%
    \skvifstrcmpTF{##2}{\skv@nil}{%
      \@secondoftwo
    }{%
      % We don't want, eg, '<tokens>.expanded{value}', since in this case
      % applying \edef to the value macro may be unsafe on <tokens>:
      \skvifblankTF{##1}{}{%
        \skv@err{Nothing should come before the
          \MessageBreak pointer '#2' on key '##2'.
          \MessageBreak If you require the value of the key to be expanded,
          \MessageBreak you should have done the expansion yourself}\skv@ehd
      }%
      \edef#3{\skvtrimspace{##2}}%
      \@firstoftwo
    }%
  }%
  \skv@f@ndpointer#1#2{\skv@nil}\skv@pointer@nil
}
% \skv@extractpntkey@atdefkey{<toks>}{<cmd>}
%
% At define key, extract pseudo key name from a weird key name of the form
%
%     evaluate (?) as (?) using (?)
%
\skvrobustdef*\skv@extractpntkey@atdefkey#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)%
  \ifx#2\@empty
    \edef#2{\unexpanded{#1}}%
  \else
    % 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
  \skvexpanded{\endgroup
    \skvcmdexit#2%
    \skvcsexit{\skv@header#2.@x0arg}%
  }%
}
% \skv@extractpntkey@atsetkey{<toks>}
%
% At set key, extract pseudo key name from a weird key name of the form
%
%     evaluate (?) as (?) using (?)
%
\skvrobustdef*\skv@extractpntkey@atsetkey#1{%
  \begingroup
  \def\skvcurrentkey{}%
  \def\skvcurrentvalue{}%
  \def\skv@prova##1(##2){%
    \def\skv@provb{##2}%
    \ifx\skv@provb\@nnil\else
      \edef\skvcurrentkey{\skvxonce\skvcurrentkey\skvtrimspace{##1}}%
      \edef\skvcurrentvalue{\skvxonce\skvcurrentvalue{\skvtrimspace{##2}}}%
      \expandafter\skv@prova
    \fi
  }%
  \skv@prova#1(\@nil)%
  \ifx\skvcurrentkey\@empty
    \skv@err{No key name for a parenthesized key}\skv@ehd
    \expandafter\@nodocument
  \fi
  \skvexpanded{\endgroup
    \skvcmdexit\skvcurrentkey\skvcmdexit\skvcurrentvalue
  }%
}
\skvrobustdef*\skv@replacepointers#1{%
  \begingroup
  \let\skvcurrentvalue\@empty
  \let\skv@tempa\@empty
  \skvifbracedTF{#1}{%
    \skv@r@placepointers{#1}.use value\skv@nil
  }{%
    \skv@r@placepointers#1.use value\skv@nil
  }%
  \skvaftergroupdef\skvcurrentvalue\endgroup
}
% In the following scheme, if we have, eg,
%
%    key2=\dimexpr.use value{key1}*5\relax
%
% \dimexpr will be retained as part of the value of key2 in pointer
% replacement.
%
\skvrobustdef*\skv@r@placepointers#1.use value#2{%
  % In case #1 isn't empty:
  \edef\skvcurrentvalue{\skvexpandonce\skvcurrentvalue\unexpanded{#1}}%
  \edef\skv@prova{\unexpanded{#2}}%
  \skvifxTF\skv@prova\skv@nnil{}{%
    \skvifcsdefFT{\skv@header#2.@value}{%
      \skv@err{No value recorded for key '#2'; ignored}\skv@ehd
      \skv@r@placepointers
    }{%
      \skvxifinTF{,\detokenize{#2},}{,\skvoxdetok\skv@tempa,}{%
        \skv@err{Back linking of pointers;
          \MessageBreak pointer replacement canceled}\skv@ehd
      }{%
        \edef\skv@tempa{\skvaddlist,\skv@tempa\unexpanded{#2}}%
        \skvletcs\skv@prova{\skv@header#2.@value}%
        \skvexpbracenext\skvifbracedTF\skv@prova\skvexpbracenext\skvexpandnext
        \skv@r@placepointers\skv@prova
      }%
    }%
  }%
}

% Assign arguments to keys:
%
% \skvassignargs[<prefixes>]{<families>}[<expansion.type>]{<keys>}{<arg>}
%
% 1. Valid expansion types are .expand once, .expand twice, .expanded.
% 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}[.expand once]{keya,keyb}{#1/#2}
%
% The keys could then be set as
%
%   \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\skvdefaultprefix}
\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@tempb{\skvtrimspace{#5}}%
  \def\skv@tempc{}%
  \skvifstrcmpTF{#3}{^skv^}{%
    \def\skvexpander{x0}%
  }{%
    \skv@getexpander@a{#3}\skvexpander
  }%
  \skvcommaparse{#1}\skv@tempa{%
    \skv@makeprefix\skv@tempa
    \skvcommaparse{#2}\skv@tempa{%
      \skv@makeheader\skv@tempa
      \skvcommaparse{#4}\skv@tempa{%
        \edef\skv@tempc{%
          \skvexpandonce\skv@tempc
          \skvcsedef{\skv@header\skv@tempa.@\skvexpander arg}{%
            \noexpand\unexpanded{\skvexpandonce\skv@tempb}%
          }%
        }%
      }%
    }%
  }%
  \expandafter\endgroup\skv@tempc
}

% \skv@getexpander@a{<expander.type>}{<return.cmd>}
%
% 1. <return.cmd> will contain one of 'x0', 'x1', 'x2', 'xx', which will be
%    saved in a key-dependent macro that represents the key's argument.
%
\skvrobustdef*\skv@getexpander@a#1#2{%
  \begingroup
  \edef\skv@prova{\skvotrimspace{#1}}%
  \def\skv@provb##1{%
    \def\skv@provb####1##1####2####3\skv@nil{%
      \endgroup
      \def#2{####2}%
      \ifx#2\@nnil
        \skv@err{Unknown expansion type '\detokenize{##1}'}\skv@ehd
      \fi
    }%
    \skv@provb .expand once{x1},.expand twice{x2},.expanded{xx},%
      .expand{xx},##1{\@nil}\skv@nil
  }%
  \expandafter\skv@provb\expandafter{\skv@prova}%
}
% \skv@getexpander@b{<expander.type>}
%
% 1. <expander.type> is one of 'x0', 'x1', 'x2', 'xx', which had been
%    saved in a key-dependent macro.
%
% 2. \skv@getexpander@b returns macro \skvexpander, which will
%    contain one of \unexpanded, \skvexpandonce, \skvexpandtwice, \@iden.
%
\skvrobustdef*\skv@getexpander@b#1{%
  \begingroup
  \edef\skv@prova{\skvtrimspace{#1}}%
  \def\skv@provb##1{%
    \def\skv@provb####1##1####2####3\skv@nil{%
      \endgroup
      \def\skvexpander{####2}%
      \ifx\skvexpander\@nnil
        \skv@err{Unknown expansion type '\detokenize{##1}'}\skv@ehd
      \fi
    }%
    \skv@provb x0{\unexpanded},x1{\skvexpandonce},x2{\skvexpandtwice},%
      xx{\@iden},##1{\@nil}\skv@nil
  }%
  \expandafter\skv@provb\expandafter{\skv@prova}%
}
% \skv@getargpattern{<callback token list>}
%
% Get argument type from the key's callback. The valid tags are
%
%   .arg, .arg unexpanded, .arg expanded, .arg expand, .arg expand once,
%   .arg expand twice.
%
\skvrobustdef*\skv@getargpattern{%
  \let\skv@argpattern\@undefined
  \def\do##1##2{%
    \ifx\do##1\else
      \skvxifinTF{\detokenize{##1}}{\skvoxdetok\skv@callback}{%
        \def\skv@prova####1##1####2####3\skv@argpattern@nil{%
          \edef\skv@argpattern{\unexpanded{####2}}%
          \ifx\skv@argpattern\@empty
            \let\skv@argpattern\skv@simplearg
          \fi
          \edef\skv@callback{\skvtrimspace{####1}\skvtrimspace{####3}}%
        }%
        \expandafter\skv@prova\skv@callback\skv@argpattern@nil
        \def\skvexpandertype{##2}%
        \def\do####1\do\do{}%
      }{}%
      \expandafter\do
    \fi
  }%
  % The following order is important, to avoid incorrect match
  % of the search term:
  \do{.arg unexpanded}{x0}{.arg expand once}{x1}{.arg expand twice}{x2}%
    {.arg expanded}{xx}{.arg expand}{xx}{.arg}{x0}\do\do
  \ifdefined\skv@argpattern\else
    % Argument pattern might have been specified outside the key's
    % callback.
    \def\do##1{%
      \ifx\do##1\else
        \skvifnamedefTF{\skvcurrentfullkey.@##1arg}{%
          \skvletcs\skv@argpattern{\skvcurrentfullkey.@##1arg}%
          \def\skvexpandertype{##1}%
          \def\do####1\do{}%
        }{}%
        \expandafter\do
      \fi
    }%
    \do{xx}{x2}{x1}{x0}\do
  \fi
  \ifdefined\skv@argpattern
    % Possibly expand value:
    \skvstripouterbraces{2}\skv@argpattern
    \def\do##1##2{%
      \ifx\do##1\else
        \skvxifstrcmpTF{##2}\skvexpandertype{%
          \edef\skvcurrentvalue{%
            \expandafter##1\expandafter{\skvcurrentvalue}%
          }%
          \def\do####1\do\do{}%
        }{}%
        \expandafter\do
      \fi
    }%
    \do\@iden{xx}\skvexpandtwice{x2}\skvexpandonce{x1}\unexpanded{x0}\do\do
  \else
    \let\skv@argpattern\skv@simplearg
  \fi
}
% +++++++++++++ Ordinary keys:
%
% \skvordkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>}
%
\skvrobustdef*\skvordkey{\skv@testoptb{\skv@ordkey{e}}}
\skvrobustdef*\skvordkeys{\skv@testoptb{\skv@ordkeys{e}}}
%
% \skvgordkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>}
%
% Global ordinary keys. So far, these are needed only by \skvgdisablekey.
%
\skvrobustdef*\skvgordkey{\skv@testoptb{\skv@ordkey{x}}}
\skvrobustdef*\skvgordkeys{\skv@testoptb{\skv@ordkeys{x}}}
\skvrobustdef*\skv@ordkey#1#2{%
  \skv@strippointersfromkey{#2}%
  \skvexpanded{\skv@testopt{\skv@ordkey@a{#1}{\skvcurrentkey}}{^skv^}}%
}
\skvrobustdef*\skv@ordkey@a#1#2[#3]#4{%
  \skvifstrcmpTF{#3}{^skv^}{}{\skv@definedefault{#2}{#3}}%
  % The pointer '.hp' may be used to set a holder prefix for the key.
  \skvxifinTF{\detokenize{.hp}}{\detokenize{#4}}{%
    \def\skv@prova##1.hp##2##3\skv@ord@nil{%
      \let\skv@gobble@or@iden\@iden
      \edef\skv@hp{\unexpanded{##2}}%
      \edef\skv@callback{\unexpanded{##1##3}}%
    }%
    \skv@prova#4\skv@ord@nil
  }{%
    \let\skv@gobble@or@iden\@gobble
    \edef\skv@callback{\unexpanded{#4}}%
  }%
  \csname skvcs#1def\endcsname{\skv@header#2.@cbk}{%
    % This can be called in #3 access the value of ordinary key.
    % No prefix is used here, to reduce macro name:
    \skv@gobble@or@iden{\skvcslet{\skv@hp#2}\noexpand\skvcurrentvalue}%
    \skvexpandonce\skv@callback
  }%
}
\skvrobustdef*\skv@ordkeys#1#2{\skv@testopt{\skv@ordkeys@a{#1}{#2}}{^skv^}}
\skvrobustdef*\skv@ordkeys@a#1#2[#3]#4{%
  \skvcommaparse{#2}\skv@prova{%
    \skvexpbracenext\skv@strippointersfromkey\skv@prova
    \skvexpbracenext{\skv@ordkey@a{#1}}\skvcurrentkey[#3]{#4}%
  }%
}
% \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@strippointersfromkey{#1}%
  \skvexpanded{\skv@testopt{\skv@voidkey@a{\skvcurrentkey}}{^skv^}}%
}
\skvrobustdef*\skv@voidkey@a#1[#2]#3{%
  \skvifstrcmpTF{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}%
  \skvcsedef{\skv@header#1.@cbk}{%
    \unexpanded{#3}\relax
    \skv@err{Key '#1' of family '\skvcurrentpath'
      \MessageBreak has been set invalid by its author}\noexpand\skv@ehd
  }%
}
% \skvobsoletekey
%    [<pref>]{<fam>}{<obsolete key>}{<new key>}[<defa>]{<callback>}
%
% 1. An obsolete key is a key that is no longer in use and has
%    been replaced by a new key. A user who tries to set an obsolete
%    key will automatically be told that the key is obsolete and that
%    he/she should instead use the new key.
% 2. The author of an obsolete key can also use <callback> to set a
%    message for the user. <callback> can be empty.
% 3. Pointers can be used on both <obsolete key> and <new key>.
%
\skvrobustdef*\skvobsoletekey{\skv@testoptb\skv@obsoletekey}
\skvrobustdef*\skv@obsoletekey#1#2{%
  \skv@strippointersfromkey{#1}%
  \skvexpanded{%
    \skv@testopt{\skv@obsoletekey@a{\skvcurrentkey}{#2}}{^skv^}%
  }%
}
\skvrobustdef*\skv@obsoletekey@a#1#2[#3]#4{%
  \skvifstrcmpTF{#3}{^skv^}{}{\skv@definedefault{#1}{#3}}%
  \skvcsedef{\skv@header#1.@cbk}{%
    \unexpanded{#4}\relax
    \noexpand\skv@warn{Key '\skvcurrentpath/#1'
      \MessageBreak is now obsolete. Instead of using the obsolete
      \MessageBreak key '#1', I will now set the new key
      \MessageBreak '#2' with the value you have given for
      \MessageBreak key '#1'. You can avoid this warning by
      \MessageBreak using the new key '#2' instead of the
      \MessageBreak obsolete key '#1'}\noexpand\skv@ehd
  }%
}

%% 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@strippointersfromkey{#1}%
  \skvexpbracenext\skv@extractpntkey@atdefkey\skvcurrentkey\skvcurrentkey
  \skvexpanded{\skv@testopt{\skv@pntkey@a{\skvcurrentkey}}{^skv^}}%
}
\skvrobustdef*\skv@pntkey@a#1[#2]#3{%
  \skvifstrcmpTF{#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@zapsw{01}%
  \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@zapsw{00}%
  \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@strippointersfromkey{#2}%
  \skvifdefboolTF{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@zapsw\expandafter}\expandafter{\skv@prova}}%
  \skvcsedef{\skv@header\skvcurrentkey.@cbk}{%
    \skvcslet{\skv@prova}\noexpand\skvcurrentvalue
    \unexpanded{#4}%
  }%
}
% \skvcmdkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}
\skvrobustdef*\skvcmdkeys{%
  \def\skv@zapsw{01}%
  \skv@testoptd\skv@cmdkeys{cmd}%
}
\skvrobustdef*\skvzcmdkeys{%
  \def\skv@zapsw{00}%
  \skv@testoptd\skv@cmdkeys{cmd}%
}
\skvrobustdef*\skv@cmdkeys#1#2[#3]#4{%
  \skvcommaparse{#2}\skv@tempa{%
    \skvexpanded{\skv@cmdkey{#1}{\skv@tempa}}[#3]{#4}%
  }%
}

%% List processor keys.
%
% \skvlistkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>}
%
\skvrobustdef*\skv@getlistkeyparser#1{%
  \begingroup
  \let\skv@prova\undefined
  \skvxifinTF{\detokenize{\listsep}}{\detokenize{#1}}{%
    \skvxifinTF{\detokenize{\listparser}}{\detokenize{#1}}{%
      \skv@err{I find both \noexpand\listsep and \noexpand\listparser in the
        \MessageBreak callback of key '\skvcurrentfullkey'}\skv@ehd
    }{%
      \def\skv@prova{\listsep}%
    }%
  }{%
    \skvxifinTF{\detokenize{\listparser}}{\detokenize{#1}}{%
      \def\skv@prova{\listparser}%
    }{}%
  }%
  \skvifdefTF\skv@prova{%
    % Hide the parameter characters in #1 here:
    \edef\skv@provc{\unexpanded{#1}}%
    \def\skv@provb##1{%
      \def\skv@provb####1##1####2####3\skv@listparser@nil{%
        \skvifblankTF{####2}{%
          \def\skv@listparser{,}%
        }{%
          \edef\skv@listparser{\skvtrimspace{####2}}%
        }%
        \edef\skv@callback{\skvtrimspace{####1}\skvtrimspace{####3}}%
      }%
      \expandafter\skv@provb\skv@provc\skv@listparser@nil
    }%
    \expandafter\skv@provb\expandafter{\skv@prova}%
  }{%
    \def\skv@listparser{,}%
    \edef\skv@callback{\skvtrimspace{#1}}%
  }%
  \skvexpanded{\endgroup
    \skvcmdexit\skv@listparser\skvcmdexit\skv@callback
  }%
}
\skvnewlet\skvstopprocessinglistkey\skvbreakloop
\skvrobustdef*\skvlistkey{%
  \def\skv@zapsw{01}%
  \skv@testoptd\skv@listkey{list}%
}
\skvrobustdef*\skvzlistkey{%
  \def\skv@zapsw{00}%
  \skv@testoptd\skv@listkey{list}%
}
\skvrobustdef*\skv@listkey#1#2[#3]#4{%
  \skv@strippointersfromkey{#2}%
  \skvifdefboolTF{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@provb{#1\skvcurrentkey}%
  \edef\skv@provb{\expandafter\skv@zapornot\expandafter
    {\expandafter\skv@zapsw\expandafter}\expandafter{\skv@provb}}%
  \skv@getlistkeyparser{#4}%
  % If there are more parameters in #4 than ####1, the next step
  % will raise an error:
  \skvxifinTF{\detokenize{##2}}{\skvoxdetok\skv@callback}{%
    \skv@err{A listkey can't have more than one
      \MessageBreak parameter character}\skv@ehd
  }{%
    \def\skv@prova{\skvcsdef{\skv@header\skvcurrentkey.@auxcbk}####1}%
    \expandafter\skv@prova\expandafter{\skv@callback}%
  }%
  \let\do\noexpand
  \skvcsedef{\skv@header\skvcurrentkey.@cbk}{%
    \skvcslet{\skv@provb}\do\skvcurrentvalue
    \edef\do\skv@prova{\do\unexpanded{####1}}%
    \do\skvstripouterbraces{2}\do\skv@prova
    % 1. \skvprocesslist processes an empty list.
    % 2. You can call \skvlistcount when processing listkeys, since
    %    \skvprocesslist initializes and increments it.
    \do\skvprocesslist*{\skv@listparser}\do\skv@prova\do\skv@prova{%
      \do\csname\skv@header\skvcurrentkey.@auxcbk%
      \do\expandafter\endcsname\do\expandafter{\do\skv@prova}%
    }%
  }%
  % A listkey must have a 1-parameter argument. We use the following
  % definition to enforce this requirement:
  \skvcsdef{\skv@header\skvcurrentkey.@listkey}{}%
}
% \skvlistkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}
\skvrobustdef*\skvlistkeys{%
  \def\skv@zapsw{01}%
  \skv@testoptd\skv@listkeys{list}%
}
\skvrobustdef*\skvzlistkeys{%
  \def\skv@zapsw{00}%
  \skv@testoptd\skv@listkeys{list}%
}
\skvrobustdef*\skv@listkeys#1#2[#3]#4{%
  \skvcommaparse{#2}\skv@tempa{%
    \skvexpanded{\skv@listkey{#1}{\skv@tempa}}[#3]{#4}%
  }%
}

%% Choice keys:
%
% The skeyval package allows holder-macro prefixes {<mp>} for choice keys.
%
%  \skvchoicekey[<pref>]{<fam>}[<mp>]{<key>}[<bins>]{<choices>}
%     [<default>]{<callback>}
%  \skvchoicekey+[<pref>]{<fam>}[<mp>]{<key>}[<bins>]{<choices>}
%     [<default>]{<callback>}{<fallback>}
%  \skvchoicekey*[<pref>]{<fam>}[<mp>]{<key>}[<bins>]{<choices>}
%     [<default>]{<callback>}
%  \skvchoicekey*+[<pref>]{<fam>}[<mp>]{<key>}[<bins>]{<choices>}
%     [<default>]{<callback>}{<fallback>}
%
%  \skvchoicekeys[KV]{fam}[mp@]{key1,key2}[\val\nr]{%
%    center.do=\def\vala{#1},
%    right.do=\def\valb{#1},
%    left.do=\def\valc{#1}
%  }[center]{\def\x##1{#1*##1}}
%
% \skv@preparenominations{00.or.01}{<altlist>}
\skvrobustdef*\skv@preparenominations#1#2{%
  \begingroup
  \def\skv@altlista{}%
  \def\skv@altlistb{}%
  \def\skv@provc##1.do=##2.do=##3\skv@choice@nil{%
    \if#1\lowercase{\fi
      \edef\skv@prova{\skvtrimspace{##1}}%
    \if#1}\fi
    \skvstripouterbraces{2}\skv@prova
    \edef\skv@altlista{%
      \skvaddlist,\skv@altlista\skvxonce\skv@prova
    }%
    \edef\reserved@a{%
      \skvnoexpandcs{\skvoxdetok\skv@prova @skvch}%
      {\skvtrimspace{##2}}%
    }%
    \edef\skv@altlistb{\skvxonce\skv@altlistb\skvxonce\reserved@a}%
  }%
  \skvcommaparse{#2}\skv@prova{%
    \expandafter\skv@provc\skv@prova.do=.do=\skv@choice@nil
  }%
  \ifx\skv@altlista\@empty
    \skv@err{No nominations (state pattern) for choice key}\skv@ehd
    \def\skv@altlistb{}%
  \fi
  \skvexpanded{\endgroup
    \skvcmdexit\skv@altlista\skvcmdexit\skv@altlistb
  }%
}
% \skv@executechoice{00.or.01}{<currentvalue>}{<altlist>}
\skvrobustdef*\skv@executechoice#1#2#3{%
  \begingroup
  \if#1\lowercase{\fi
    \edef\skv@prova{\skvtrimspace{#2}}%
  \if#1}\fi
  \skvstripouterbraces{2}\skv@prova
  \edef\do{%
    \expandafter\noexpand\csname\detokenize
    \expandafter{\skv@prova}@skvch\endcsname
  }%
  % Hide #3 in a macro to avoid confusing any parameter
  % characters in #3 with the parameters of \skv@prova:
  \edef\skv@provb{\unexpanded{#3}}%
  \edef\skv@prova{\def\noexpand\skv@prova####1\skvxonce\do####2####3}%
  \skv@prova\skv@choice@nil{%
    \toks@{##2}%
    \edef\skv@prova{\unexpanded{##2}}%
    \ifx\skv@prova\skv@nnil
      \skv@err{No state pattern match found for key
        \MessageBreak '\skvcurrentkey'}\skv@ehd
      \toks@{}%
    \fi
  }%
  \expandafter\expandafter\expandafter\skv@prova\expandafter
    \skv@provb\do{\skv@nil}\skv@choice@nil
  \expandafter\endgroup\the\toks@
}
\skvrobustdef*\skvchoicekey{%
  \skv@testopta{\skv@testoptb
    {\skv@testopt\skv@choicekey@a{choice\skv@header}}}%
}
\skvnewlet\skvchoicekeys\skvchoicekey
\skvrobustdef*\skv@choicekey@a[#1]#2{%
  \skvxifinTF{\skv@hashchar}{\detokenize{#2}}{%
    \skv@err{Hash character '\skv@hashchar' not allowed
      \MessageBreak in key name}\skv@ehd
  }{%
    \edef\skv@currlist{\unexpanded{#2}}%
    \edef\skv@hp{\skvtrimspace{#1}}%
    \skv@testopt\skv@choicekey@b{}%
  }%
}
\skvrobustdef*\skv@choicekey@b[#1]#2{%
  \edef\skv@bins{\unexpanded{#1}}%
  % Check the syntax of bins. This also helps catch wrong use syntax
  % of \skvchoicekeys: it's easy to get the syntax of \skvchoicekeys
  % wrong. The default value may have been put in place of bins.
  \ifx\skv@bins\@empty\else
    \skvtfor\skv@prova:=#1\do{%
      \skvexpbracenext\skvifescapedTF\skv@prova{}{%
        \skv@err{Token '\skvoxdetok\skv@prova' of bin of
          \MessageBreak choice key isn't escaped}\skv@ehd
      }%
    }%
  \fi
  \skvifblankTF{#2}{%
    \skv@err{No nominations (state pattern) for
      \MessageBreak choice keys '\skv@currlist'}\skv@ehd
  }{%
    \skvifboolTF{skv@st}{%
      \skv@preparenominations{00}{#2}%
    }{%
      \skv@preparenominations{01}{#2}%
    }%
  }%
  \skvifnextchar[\skv@choicekey@c{\skv@choicekey@c[^skv^]}%
}
\skvrobustdef*\skv@choicekey@c[#1]{%
  \edef\skv@default{\skvtrimspace{#1}}%
  % Choice can't have braced values, since that will make matching
  % to state pattern difficult:
  \skvstripouterbraces{2}\skv@default
  \skvifdefboolTF{skv@pl}\skv@choicekey@e\skv@choicekey@d
}
\skvrobustdef*\skv@choicekey@d#1{%
  \skvexpanded{%
    \noexpand\skv@choicekey@f{{%
      \noexpand\skv@executechoice\ifskv@st{00}\else{01}\fi
        {####1}{\skvxonce\skv@altlistb}%
      \unexpanded{#1}%
    }}%
  }%
}
\skvrobustdef*\skv@choicekey@e#1#2{%
  \skvexpanded{%
    \noexpand\skv@choicekey@f{{%
      \noexpand\skv@executechoice\ifskv@st{00}\else{01}\fi
        {####1}{\skvxonce\skv@altlistb}%
      \unexpanded{#1}%
    }{\unexpanded{#2}}}%
  }%
}
\skvrobustdef*\skv@choicekey@f#1{%
  \edef\skv@prova##1##2{%
    \ifskv@st\noexpand\skv@sttrue\else\noexpand\skv@stfalse\fi
    \ifskv@pl\noexpand\skv@pltrue\else\noexpand\skv@plfalse\fi
    \noexpand\skv@checkchoice
    \ifx\skv@bins\@empty\else[\skvxonce\skv@bins]\fi
    {##1}{\skvxonce\skv@altlista}##2%
  }%
  \skv@toksa\expandafter{\skv@prova{##1}{#1}}%
  % Why separate \skv@choicekey@g? It is needed by skeyval-view.sty:
  \skv@choicekey@g
}
\skvrobustdef*\skv@choicekey@g{%
  \skvcommaparse*\skv@currlist\skv@prova{%
    \skvexpbracenext\skv@strippointersfromkey\skv@prova
    \ifx\skv@default\skv@rej\else
      \skvexpanded{%
        \noexpand\skv@definedefault
          {\skvxonce\skvcurrentkey}{\skvxonce\skv@default}%
      }%
    \fi
    \skvcsedef{\skv@header\skvcurrentkey.@cbk}{%
      \skvcslet{\skv@hp\skvcurrentkey}\noexpand\skvcurrentvalue
      \the\skv@toksa
    }%
  }%
}

\skvrobustdef*\skv@togkey@settog#1#2#3{%
  \edef\skv@prova{\skv@zapornot{#1}{#2}}%
  \csname skvsettog#3\expandafter\endcsname\expandafter{\skv@prova}%
}

%% Boolean keys:
%
% \skvboolkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>}
% \skvboolkey+[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}{<fallback>}
%
% Boolean keys create a store to receive the key's value and also create
% a boolean internally.
%
\skvrobustdef*\skvboolkey{%
  \def\skv@typetogkey{01}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@boolkey{}}%
}
% \skvzboolkey 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@typetogkey{01}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@boolkey{}}%
}
% \skv@boolkeys{<mp>}{<key>}[<default>]
\skvrobustdef*\skv@boolkey#1#2[#3]{%
  \skv@strippointersfromkey{#2}%
  \skvexpanded{%
    \ifskv@pl\skv@boolkey@b\else\skv@boolkey@a\fi
    {\skvcurrentkey}{#1\skvcurrentkey}%
  }{#3}%
}
% \skv@boolkey@a{<key>}{<mp.key>}{<default>}{<callback>}
\skvrobustdef*\skv@boolkey@a#1#2#3#4{%
  \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}%
  \skvifplaswTF\skv@typetogkey{%
    \expandafter\skv@tempa\expandafter{\expandafter{\expandafter
      \skv@togkey@settog\expandafter{\skv@zapsw}{#2}\skv@value#4}}%
  }{%
    \expandafter\skv@tempa\expandafter{\expandafter{\expandafter
      \csname\expandafter\skv@zapornot\expandafter{\skv@zapsw}{#2}%
      \skv@value\endcsname#4}}%
  }%
}
% \skv@boolkey@b{<key>}{<mp.key>}{<default>}{<callback>}{<fallback>}
\skvrobustdef*\skv@boolkey@b#1#2#3#4#5{%
  \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}%
  \skvifplaswTF\skv@typetogkey{%
    \expandafter\skv@tempa\expandafter{\expandafter{\expandafter
      \skv@togkey@settog\expandafter{\skv@zapsw}{#2}\skv@value#4}{#5}}%
  }{%
    \expandafter\skv@tempa\expandafter{\expandafter{\expandafter
      \csname\expandafter\skv@zapornot\expandafter{\skv@zapsw}{#2}%
      \skv@value\endcsname#4}{#5}}%
  }%
}
% \skv@boolkey@c{<key>}{<mp.key>}{<default>}{{<callback>}{<fallback>}}
\skvrobustdef*\skv@boolkey@c#1#2#3#4{%
  \if\skv@typetogkey
    \skvdeftog{\skv@zapornot{\skv@zapsw}{#2}}%
  \else
    \skvcsnewif{\skv@zapornot{\skv@zapsw}{#2}}%
  \fi
  \skvifdefboolTF{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>}
% \skvboolkeys+[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}{<fallback>}
%
\skvrobustdef*\skvboolkeys{%
  \def\skv@typetogkey{01}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}%
}
\skvrobustdef*\skvzboolkeys{%
  \def\skv@typetogkey{01}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}%
}
% \skv@boolkeys{<mp>}{<keys>}[<default>]
\skvrobustdef*\skv@boolkeys#1#2[#3]{%
  \skvifdefboolTF{skv@pl}\skv@boolkeys@b\skv@boolkeys@a{#2}{#1}{#3}%
}
% \skv@boolkeys@a{<keys>}{<mp>}{<default>}{<callback>}
\skvrobustdef*\skv@boolkeys@a#1#2#3#4{\skv@boolkeys@c{#1}{#2}{#3}{#4}{^skv^}}
% \skv@boolkeys@b{<keys>}{<mp>}{<default>}{<callback>}{<fallback>}
\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@strippointersfromkey\skv@tempa
    \skvexpanded{%
      \skvifstrcmpTF{#5}{^skv^}{%
        \skv@boolkey@a{\skvcurrentkey}%
          {#2\skvcurrentkey}\unexpanded{{#3}{#4}}%
      }{%
        \skv@boolkey@b{\skvcurrentkey}%
          {#2\skvcurrentkey}\unexpanded{{#3}{#4}{#5}}%
      }%
    }%
  }%
}
%
% \skvbiboolkeys[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>]
%    {<callback.for.key1>}{<callback.for.key2>}
% \skvbiboolkeys+[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>]
%    {<callback.for.key1>}{<callback.for.key2>}{<fallback>}
%
% Example:
%
% \skvbiboolkeys+[KV]{fam}[mp@]{.need value{keya},keyb}[true]{%
%    \ifmp@keya\def\xa##1{##1}\fi
% }{%
%    \ifmp@keyb\def\xb##1{##1}\fi
% }{%
%    \skv@warn{Value '#1' is invalid}%
% }
%
\skvrobustdef*\skvbiboolkeys{%
  \def\skv@typetogkey{01}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}%
}
\skvrobustdef*\skvzbiboolkeys{%
  \def\skv@typetogkey{01}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}%
}
\skvrobustdef*\skv@biboolkeys#1#2[#3]{%
  \begingroup
  \edef\skv@currlist{\unexpanded{#2}}%
  \skvcsvnormalize\skv@currlist
  \@tempcnta\skvz@
  \skvcommaloop*\skv@currlist\skv@tempa{%
    \advance\@tempcnta\@ne
    \ifnum\@tempcnta>\tw@
      \skvbreakloop
    \fi
  }%
  \ifnum\@tempcnta=\tw@\else
    \skv@err{Number of bi-keys isn't 2:
      \MessageBreak ||\skv@currlist||}\skv@ehd
  \fi
  \skvexpanded{%
    \ifskv@pl\skv@biboolkeys@b\else\skv@biboolkeys@a\fi
    {\skv@currlist}%
  }{#1}{#3}%
}
% \skv@bilboolkeys@a{<key1,key2>}{<mp>}{<default>}{<callback1>}{<callback2>}
\skvrobustdef*\skv@biboolkeys@a#1#2#3#4#5{%
  \skv@biboolkeys@c{#1}{#2}{#3}{#4}{#5}{^skv^}%
}
% \skv@bilboolkeys@a{<key1,key2>}{<mp>}{<default>}
%    {<callback1>}{<callback2>}{<fallback>}
\skvrobustdef*\skv@biboolkeys@b#1#2#3#4#5#6{%
  \skv@biboolkeys@c{#1}{#2}{#3}{#4}{#5}{#6}%
}
\skvrobustdef*\skv@biboolkeys@c#1#2#3#4#5#6{%
  % Don't group the following: pointers will be lost outide the group.
  % 'bicallerid' is used to indicate that the master key is the one
  % that has instantiated the callback of the slave. This avoids
  % infinite re-entrance of \skvsetkeys.
  \edef\skv@default{\unexpanded{#3}}%
  \edef\skv@tempc{\unexpanded{#4}}%
  \edef\skv@tempd{\unexpanded{#5}}%
  \edef\skv@tempe{\unexpanded{#6}}%
  \toks@{}\def\skv@provd{}%
  \let\nx\noexpand
  \def\@do##1##2{%
    \skvifcsname\skv@header##1.@##2\then
      \edef\skv@provd{%
        \skv@provd
        \skvcsdef{\skv@header##1.@##2}{%
          \csname\skv@header##1.@##2\endcsname
        }%
      }%
    \fi
  }%
  \def\skv@provc##1##2##3##4{%
    \skvsettogtrue{\skv@header bicallerid/##3}%
    \skvxonce\@elt
    \nx\skvifxTF\nx\skvcurrentvalue\nx\skv@truetoks{%
      \nx\skviftogTF{\skv@header bicallerid/##4}{}{%
        \skvsetkeys[##1]{##2}{##4=false}%
      }%
    }{%
      \nx\skviftogTF{\skv@header bicallerid/##4}{}{%
        \skvsetkeys[##1]{##2}{##4=true}%
      }%
    }%
    \skvsettogfalse{\skv@header bicallerid/##3}%
  }%
  % \skv@provb{<KV>}{<fam>}{<primary.key>}{<secondary.key>}
  \def\skv@provb##1##2##3##4{%
    \@do{##3}{ndv}\@do{##3}{fbv}%
    \skvexpanded{%
      \toks@{\the\toks@
        \skvdeftog{\skv@header bicallerid/##3}%
        \skvifstrcmpTF{#6}{^skv^}{%
          \skv@boolkey@a
            {##3}{#2##3}{\skvxonce\skv@default}{%
              \skv@provc{##1}{##2}{##3}{##4}%
            }%
        }{%
          \skv@boolkey@b
            {##3}{#2##3}{\skvxonce\skv@default}{%
              \skv@provc{##1}{##2}{##3}{##4}%
            }{\skvxonce\skv@tempe}%
        }%
      }%
    }%
  }%
  \def\skv@prova##1,##2\skv@nil{%
    \skv@strippointersfromkey{##1}%
    \let\skv@tempa\skvcurrentkey
    \skv@strippointersfromkey{##2}%
    \let\skv@tempb\skvcurrentkey
    \skvexpanded{%
      \let\nx\@elt\nx\skv@tempc
      \nx\skv@provb{\skvcurrentprefix}{\skvcurrentfamily}%
        {\skv@tempa}{\skv@tempb}%
      \let\nx\@elt\nx\skv@tempd
      \nx\skv@provb{\skvcurrentprefix}{\skvcurrentfamily}%
        {\skv@tempb}{\skv@tempa}%
    }%
  }%
  \skv@prova#1\skv@nil
  \skvexpanded{\endgroup
    \skv@provd\the\toks@
  }%
}

%% Toggle keys:
%
% \skvtogkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>}
% \skvtogkey+[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>}{<fallback>}
%
\skvrobustdef*\skvtogkey{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@boolkey{}}%
}
\skvrobustdef*\skvztogkey{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@boolkey{}}%
}
\skvrobustdef*\skvtogkeys{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}%
}
\skvrobustdef*\skvztogkeys{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}%
}
% \skvbitogkeys[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>]
%    {<callback.for.key1>}{<callback.for.key2>}
% \skvbitogkeys+[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>]
%    {<callback.for.key1>}{<callback.for.key2>}{<fallback>}
\skvrobustdef*\skvbitogkeys{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{01}%
  \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}%
}
\skvrobustdef*\skvzbitogkeys{%
  \def\skv@typetogkey{00}%
  \def\skv@zapsw{00}%
  \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}%
}
\skvrobustdef*\skvSignalkeyPresetException#1{%
  \edef\skv@signalpreseterr{\unexpanded{#1}}%
}
\skvSignalkeyPresetException{%
  \@latex@error{%
    Key '\skvcurrentfullkey'
    \MessageBreak is a signal key. It shouldn't have
    \MessageBreak been preset. Presetting signal keys
    \MessageBreak does lead to cyclic key setting
  }\skv@ehd
}

% Declare prefix and family and then define/set keys on them (without
% the need to give prefix and family). This can be handy if all the keys
% in a project have the same prefix and family.
%
% Example:
%
%   \skvsetprefix{KV}
%   \skvsetfamily{fam}
%   \skvdefineordkey{key1}[default]{\def\x##1{##1*#1}}
%   \skvprocesskeys{key1=value1}
%
\skvrobustdef*\skvsetprefix{\def\skv@currpref}
\skvrobustdef*\skvsetfamily{\def\skv@currfam}
\skvrobustdef*\skv@defineonmeta#1{%
  \ifdefined\skv@currpref\else
    \let\skv@currpref\skvdefaultprefix
  \fi
  \skvexpanded{\noexpand#1[\skv@currpref]{\skv@currfam}}%
}
\skvrobustdef*\skvdefineordkey{\skv@defineonmeta\skvordkey}
\skvrobustdef*\skvdefineordkeys{\skv@defineonmeta\skvordkeys}
\skvrobustdef*\skvdefinecmdkey{\skv@defineonmeta\skvcmdkey}
\skvrobustdef*\skvdefinecmdkeys{\skv@defineonmeta\skvcmdkeys}
\skvrobustdef*\skvdefineboolkey{\skv@defineonmeta\skvboolkey}
\skvrobustdef*\skvdefineboolkeys{\skv@defineonmeta\skvboolkeys}
\skvrobustdef*\skvdefinebiboolkeys{\skv@defineonmeta\skvbiboolkeys}
\skvrobustdef*\skvdefinetogkey{\skv@defineonmeta\skvtogkey}
\skvrobustdef*\skvdefinetogkeys{\skv@defineonmeta\skvtogkeys}
\skvrobustdef*\skvdefinechoicekey{\skv@defineonmeta\skvchoicekey}
\skvrobustdef*\skvdefinechoicekeys{\skv@defineonmeta\skvchoicekeys}
\skvrobustdef*\skvprocesskeys{\skv@defineonmeta\skvsetkeys}

% Signal-slot keys:
%
% \skvappendslots[<pref>]{<fam>}{<signal.keys>}{<slots>}
%     [<signal default>]{<signal callback>}
% \skvprependslots[<pref>]{<fam>}{<signal.keys>}{<slots>}
%     [<signal default>]{<signal callback>}
%
% 1. <slots> must pre-exist. If slot key is undefined, then the signal-slot
%    system isn't useful here since the slot will serve no purpose.
%
% 2. If desired, use pointers/handlers .expanded, .expand once, .expand twice
%    on values of <slots>.
%
% 3. Here, it helps to consider <keys> as parents (or signals or
%   subjects) and <slots> as children (or slots or observers).
%
% 4. <signal.keys> can't be preset, to avoid cyclic setting of keys.
%    Consider the following, for a signal key in the preset list:
%
%       set any key on a path -> preset 'signal'
%       -> set 'slot' -> preset 'signal'.
%
%    In the attempt to set a slot key, the preset list (that includes
%    the signal key) will be instantiated.
%
% Example:
%
%  \skvordkeys[KV]{fam}{keya,keyb}{\def\x##1{#1*##1}}
%  \skvappendslots[KV]{fam}{keyd,keye}{keya=.expanded{\vala},keyb=valb}
%     [def-d&e]{\def\y##1{#1*##1}}
%
\skvrobustdef*\skvappendslots{%
  \def\skv@type{app}%
  \let\skvexpanderpointer\@iden
  \skv@testoptb\skv@appendslots
}
\skvrobustdef*\skvprependslots{%
  \def\skv@type{prep}%
  \let\skvexpanderpointer\@iden
  \skv@testoptb\skv@appendslots
}
\skvrobustdef*\skvappendslotsexpanded{%
  \def\skv@type{app}%
  \def\skvexpanderpointer{.expanded}%
  \skv@testoptb\skv@appendslots
}
\skvrobustdef*\skvprependslotsexpanded{%
  \def\skv@type{prep}%
  \def\skvexpanderpointer{.expanded}%
  \skv@testoptb\skv@appendslots
}
\skvrobustdef*\skv@appendslots#1#2{%
  \skv@testopt{\skv@appendslots@a{#1}{#2}}{^skv^}%
}
% #1=signal keys, #2=slot keys, #3=signal default, #4=signal callback
\skvrobustdef*\skv@appendslots@a#1#2[#3]#4{%
  \skvcommaparse{#1}\skv@prova{%
    \skvexpbracenext\skv@strippointersfromkey\skv@prova
    \skvexpanded{%
      \skvifstrcmpTF{#3}{^skv^}{}{%
        % If the signal aready exists, the default value will be
        % overridden:
        \skv@definedefault{\skvcurrentkey}{\unexpanded{#3}}%
      }%
      \skv@appendslots@b{\skvcurrentkey}\unexpanded{{#2}{#4}}%
    }%
  }%
}
% \skv@appendslots@b{<signal>}{<slots>}{<signal.callback>}
\skvrobustdef*\skv@appendslots@b#1#2#3{%
  \def\skv@slotlist{}%
  \skvkvparse{#2}\skv@prova{%
    \skv@okvsplit\skv@prova{%
      \skvxifstrcmpTF{#1}{##1}{%
        \skv@err{Linking key '##1' to itself}\skv@ehd
      }{%
        % If slot key is undefined, then the signal-slot system isn't
        % useful, since the slot won't be able to do anything with the
        % emitted signal.
        \skvifcsdefTF{\skv@header##1.@cbk}{}{%
          \skv@err{Slot key '\skvcurrentpath/##1'
            \MessageBreak is undefined}\skv@ehd
        }%
      }%
      \edef\skv@slotlist{%
        \skvaddlist,\skv@slotlist\unexpanded{##1}=%
        \skvexpanderpointer{\unexpanded{##2}}%
      }%
    }%
  }%
  \edef\skv@elt{%
    % Signal keys shouldn't be preset, to avoid cyclic setting of keys.
    % See note above.
    \noexpand\skvifdefboolTF{skv@inprepo}{%
      \noexpand\skv@signalpreseterr
    }{%
      \noexpand\skvsetkeys[\skvcurrentprefix]%
        {\skvcurrentfamily}{\skvexpandonce\skv@slotlist}%
    }%
  }%
  % The signal key might already exists, especially when calling
  % \skvaddslots:
  \edef\elt{%
    \skvifcsdefTF{\skv@header#1.@cbk}
      {\skvexpandcsonce{\skv@header#1.@cbk}}{}%
  }%
  \skvcsedef{\skv@header#1.@cbk}{%
    \skvxifstrcmpTF{\skv@type}{prep}{%
      \skvexpandonce\skv@elt\skvtrimspace{#3}\skvexpandonce\elt
    }{%
      \skvexpandonce\elt\skvtrimspace{#3}\skvexpandonce\skv@elt
    }%
  }%
}
%
% \skvaddslots[<pref>]{<fam>}{<signals>}{<prepended.slots>}
%     {<appended.slots>}[<signal.default>]{<signal.callback>}
%
% Slot keys are observer keys that receive signals from signal
% (ie, subject) keys.
%
% Examples:
%   \skvordkeys[KV]{fam}{keya1,keyb1,keya2,keyb2}{\def\x##1{#1*##1}}
%   \skvaddslots[KV]{fam}{keyd,keye}{keya1=vala,keyb1=valb}
%      {keya2=vala,keyb2=.expanded{\valb}}[def-d&e]{\def\y##1{#1*##1}}
%
%   \skvordkeys{graph}{avertices,bvertices}[6]{\def\x##1{#1+##1}}
%   \skvaddslots{graph}{vertices}{avertices=4}{bvertices=8}[6]
%       {\def\y##1{#1*##1}}
%   \skvsetkeys{graph}{vertices=6}
%
\skvrobustdef*\skvaddslots{%
  \let\skvexpanderpointer\@iden
  \skv@testoptb\skv@addslots
}
\skvrobustdef*\skv@addslots#1#2#3{%
  \skv@testopt{\skv@addslots@a{#1}{#2}{#3}}{^skv^}%
}
% \skv@addslots@a{<signals>}{<prepended slots>}{<appended slots>}
%     [<signal default>]{<signal callback>}
\skvrobustdef*\skv@addslots@a#1#2#3[#4]#5{%
  \skvexpanded{%
    \skvprependslots[\skvcurrentprefix]{\skvcurrentfamily}%
      \unexpanded{{#1}{#2}[#4]{#5}}%
    \skvappendslots[\skvcurrentprefix]{\skvcurrentfamily}%
      \unexpanded{{#1}{#3}[#4]{#5}}%
  }%
}
\skvnewcastcommands{%
  \skvslotkeys=\skvappendslots,\skvstylekeys=\skvappendslots,
  \skvprependstyles=\skvprependslots,
  \skvappendstylesexpanded=\skvappendslotsexpanded,
  \skvprependstylesexpanded=\skvprependslotsexpanded,
  \skvaddstyles=\skvaddslots,
  \skvobserverkeys=\skvappendslots,
  \skvprependobservers=\skvprependslots,
  \skvappendobserversexpanded=\skvappendslotsexpanded,
  \skvprependobserversexpanded=\skvprependslotsexpanded,
  \skvaddobservers=\skvaddslots
}
% 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}%
  \skvifxTF\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
  \skvifinTF{,#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}\skv@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}\skv@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}

% \skvifkeydefTF[<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*\skvifkeydefTF{\skv@testopt{\skv@ifkeyundef0}\skvdefaultprefix}
\skvrobustdef*\skvifkeydefFT{\skv@testopt{\skv@ifkeyundef1}\skvdefaultprefix}
\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
    % Break outer loop as needed:
    \ifskv@kf\skvbreakloop\fi
  }%
  \skvexpanded{%
    \skvifboolTF{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
    \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{}{%
      \skv@kftrue\skvbreakloop
    }%
  }%
}
% \skvifkeyinfamiliesTF[<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*\skvifkeyinfamiliesTF{%
  \skv@testopt{\skv@ifkeyinfamilies0}\skvdefaultprefix
}
% \skvdoonallpathsfound[<prefs>]{<fams>}{key}{<true>}
\skvrobustdef*\skvdoonallpathsfound{%
  \skv@testopt\skv@doonallpathsfound\skvdefaultprefix
}
\skvrobustdef*\skv@doonallpathsfound[#1]#2#3#4{%
  \skv@ifkeyinfamilies0[#1]{#2}{#3}{#4}{}%
}
% \skvdoonallpathsnotfound[<prefs>]{<fams>}{key}{<false>}
\skvrobustdef*\skvdoonallpathsnotfound{%
  \skv@testopt\skv@doonallpathsnotfound\skvdefaultprefix
}
\skvrobustdef*\skv@doonallpathsnotfound[#1]#2#3#4{%
  \skv@ifkeyinfamilies0[#1]{#2}{#3}{}{#4}%
}
\skvrobustdef*\skvifkeyinfamiliesFT{%
  \skv@testopt{\skv@ifkeyinfamilies1}\skvdefaultprefix
}
\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{%
      \skvifcsdefTF{\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
}
% \skvifkeyinfamilyTF{<prefix>/<family>}{<key>}
\skvnewdef*\skvifkeyinfamilyTF#1#2{%
  \skvifcsdefTF{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}%
}
% \skvifkeyincurrentfamilyTF{<key>}
\skvnewdef*\skvifkeyincurrentfamilyTF#1{%
  \skvifcsdefTF{\skvcurrentpath/\skvtrimspace{#1}.@cbk}%
}
\skvnewlet\skvifkeyoncurrentpathTF\skvifkeyincurrentfamilyTF
% \skvifonekeyinfamilyTF{<prefix>/<family>}{<keys>}
% Given a list of keys, test if at least one of them is defined.
% Example: \edef\x{\skvifonekeyinfamilyTF{KV/fam}{keya,keyb,keyc}{T}{F}}.
\skvnewdef*\skvifonekeyinfamilyTF#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
    \skvifcsdefTF{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}{+1}{+0}%
    \expandafter\skv@ifonekeydef
  \fi
  {#1}%
}

% \skvgetdefinedkeys[<pref>]{<fams>}{<keys>}
%
% 1. Get keys defined on the given paths.
% 2. Remove any redundant star (*) and plus (+) suffix on
%    \skvgetdefinedkeys.
\skvrobustdef*\skvgetdefinedkeys{%
  \skv@testopta{\skv@testopt\skv@getdefinedkeys\skvdefaultprefix}%
}
\skvrobustdef*\skv@getdefinedkeys[#1]#2#3{%
  \begingroup
  \skv@makeprefix{#1}%
  \def\skv@tempa{}%
  \skvcommaparse{#2}\skvcurrentfamily{%
    \skv@makeheader\skvcurrentfamily
    % Prepare to take defined keys outside the group:
    \skvxifinTF{\skvoxdetok\skv@header}{\skvoxdetok\skv@tempa}{}{%
      \edef\skv@tempa{%
        \skvexpandonce\skv@tempa
        \noexpand\skvcsexit{\skvcurrentpath.@definedkeys}%
      }%
    }%
    \skvcommaparse{#3}\skvcurrentkey{%
      \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{}{%
        \skvletcs\skv@prova{\skvcurrentpath.@definedkeys}%
        \skvifdefTF\skv@prova{%
          \skvxifinTF{,\skvoxdetok\skvcurrentkey,}{,\skvoxdetok\skv@prova,}{}{%
            \skvcsedef{\skvcurrentpath.@definedkeys}{\skv@prova,\skvcurrentkey}%
          }%
        }{%
          \skvcsedef{\skvcurrentpath.@definedkeys}{\skvcurrentkey}%
        }%
      }%
    }%
  }%
  \skvexpanded{\endgroup\skv@tempa}%
}

% \skvvalueof{<pref>}{<family>}{<key>}
\skvnewdef*\skvvalueof#1#2#3{\skvcsuse{#1/#2/#3.@value}}
% \skvvalueofkey{<pref>/<family>/<key>}
\skvnewdef*\skvvalueofkey#1{\skvcsuse{#1.@value}}

% When <pref> isn't given, the following will use 'default 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{\skvcurrentpath.@inikv}}

% \skvkeyslet[<pref>]{<family>}{<list>}
%
%   <list> -> {<newkey1>,<newkey2>,...,<newkey-n>}={<oldkey>}
%
% Note: <oldkey> must be only one key, to avoid letting one newkey
% to two oldkeys.
%
\skvrobustdef*\skvkeyslet{\skv@testoptb\skv@keyslet}
\skvnewlet\skvkeyinherit\skvkeyslet
\skvrobustdef*\skv@keyslet#1{%
  \skvletcs\skv@inikv{\skvcurrentpath.@inikv}%
  \skvifdefFT\skv@inikv{}{%
    \skvexpbracenext\skv@getnamesofkeys\skv@inikv\skv@inikeys
  }%
  \def\skv@keyslet@do@a##1=##2=##3\skv@nil{%
    \skvifinTF{,}{##2}{%
      \skv@err{There more than one old key:\MessageBreak '##2'}\skv@ehd
    }{%
      \skvcommaparse{##1}\skv@prova{%
        \skvexpbracenext\skv@keyslet@do@b\skv@prova{##2}%
      }%
    }%
  }%
  \def\skv@keyslet@do@b##1##2{%
    \skvifnamedefFT{\skv@header##2.@cbk}{%
      \skv@err{Key '\skvcurrentpath/##2'\MessageBreak is undefined}\skv@ehd
    }{%
      \skvifnamedefTF{\skv@header##1.@cbk}{%
        \skv@err{Key '\skvcurrentpath/##1' is already defined}\skv@ehd
      }{}%
    }%
    \skvcommaloop{cbk,ndv,gnv,fbv,gfv}\skv@prova{%
      \skvcsletcs{\skv@header##1.@\skv@prova}{\skv@header##2.@\skv@prova}%
    }%
    \skvifnamedefTF{\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
    }%
    \skvifdefFT\skv@inikv{}{%
      \skvxifinFT{,\detokenize{##2},}{,\skvoxdetok\skv@inikeys,}{}{%
        \skvcsedef{\skvcurrentpath.@inikv}{%
          \skvcsaddlist,{\skvcurrentpath.@inikv}%
          ##1\ifskv@swa=\skvexpandonce\skvcurrentvalue\fi
        }%
      }%
    }%
    \skvcommaloop{ignoredkeys,needini}\skv@prova{%
      \skvletcs\skv@tempa{\skvcurrentpath.@\skv@prova}%
      \skvifdefFT\skv@tempa{}{%
        \skvxifinFT{,\detokenize{##2},}{,\skvoxdetok\skv@tempa,}{}{%
          \skvcsedef{\skvcurrentpath.@\skv@prova}{%
            \skvaddlist,\skv@tempa##1%
          }%
        }%
      }%
    }%
  }%
  \skvkvparse{#1}\skv@prova{%
    \expandafter\skv@keyslet@do@a\skv@prova==\skv@nil
  }%
  \let\do\skvundef
  \do\skv@keyslet@do\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@latex 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@latex in \noexpand\documentclass or
    \string\usepackage\fi}%
}
% A key may be disabled long before \skvmessagetypefordisabledkey
% is called.
\skvrobustdef*\skvmessagetypefordisabledkey#1{%
  \edef\skv@prova{\skvtrimspace{#1}}%
  \def\skv@provb##1{%
    \def\skv@provb####1##1####2####3\@nil{%
      \ifcase####2\relax
        \let\skv@disabledkeymsg\skv@disabledkeywarning
      \or
        \let\skv@disabledkeymsg\skv@disabledkeywarning
      \or
        \let\skv@disabledkeymsg\skv@disabledkeyerror
      \or
        \let\skv@disabledkeymsg\@gobble
      \else
        \skv@err{Invalid message type '#1' for disabled keys}\skv@ehd
      \fi
    }%
    \skv@provb warn{0}warning{1}error{2}nothing{3}##1{10}\@nil
  }%
  \expandafter\skv@provb\expandafter{\skv@prova}%
}
\skvnewlet\skvdisabledkeysmessagetype\skvmessagetypefordisabledkey
\skvdisabledkeysmessagetype{warn}

\skvrobustdef*\skvdisablekeys{%
  \def\skv@tempa{\skv@ordkey@a{e}}%
  \skv@testoptb\skv@disablekeys
}
\skvrobustdef*\skvgdisablekeys{%
  \def\skv@tempa{\skv@ordkey@a{x}}%
  \skv@testoptb\skv@disablekeys
}
\skvrobustdef*\skv@disablekeys#1{%
  \skvcommaparse{#1}\skv@prova{%
    \skvifcsdefTF{\skv@header\skv@prova.@cbk}{%
      \skvifcsdefTF{\skv@header\skv@prova.@defa}{%
        \edef\skv@provb{\noexpand\skv@tempa{\skv@prova}[]}%
      }{%
        \edef\skv@provb{\noexpand\skv@tempa{\skv@prova}[^skv^]}%
      }%
      \expandafter\skv@provb\expandafter{\expandafter
        \skv@disabledkeymsg\expandafter{\skv@prova}}%
    }{%
      \skv@err{Key '\skv@prova' is undefined:
        \MessageBreak couldn't be disabled}\skv@ehd
    }%
  }%
}

% 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\skvdefaultprefix}
\skvrobustdef*\skv@presetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{#3}{}}
\skvrobustdef*\skvgpresetkeys{\skv@testopt\skv@gpresetkeys\skvdefaultprefix}
\skvrobustdef*\skv@gpresetkeys[#1]#2#3{\skvgpreposetkeys[#1]{#2}{#3}{}}
\skvrobustdef*\skvpostsetkeys{\skv@testopt\skv@postsetkeys\skvdefaultprefix}
\skvrobustdef*\skv@postsetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{}{#3}}
\skvrobustdef*\skvgpostsetkeys{\skv@testopt\skv@gpostsetkeys\skvdefaultprefix}
\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{%
  \skvifblankTF{#1}{}{\skv@pr@posetkeys{#1}{preset}}%
  \skvifblankTF{#2}{}{\skv@pr@posetkeys{#2}{postset}}%
}
\skvrobustdef*\skv@pr@posetkeys#1#2{%
  \skvifcsdefFT{\skvcurrentpath.@#2}{%
    \skvnormalize{csv}{,}{#1}\skv@tempa
    \ifskv@cl\expandafter\global\fi
    \expandafter\def\csname\skvcurrentpath.@#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{%
  \skvifcsdefTF{\skvcurrentpath.@preset}{%
    \expandafter\skv@filterremoveelements\csname\skv@header
      .@preset\endcsname{#1}\skv@getkeyname
  }{%
    \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd
  }%
}
% \skvundefpresetkeys[<pref>]{<fam>}
\skvrobustdef*\skvundefpresetkeys{%
  \skv@clfalse\skv@testoptb\skv@undefpresetkeys
}
\skvrobustdef*\skvgundefpresetkeys{%
  \skv@cltrue\skv@testoptb\skv@undefpresetkeys
}
\skvrobustdef*\skv@undefpresetkeys{%
  \skvifcsdefFT{\skvcurrentpath.@preset}{%
    \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skvcurrentpath.@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{%
  \skvifcsdefTF{\skvcurrentpath.@postset}{%
    \expandafter\skv@filterremoveelements\csname\skv@header
      .@postset\endcsname{#1}\skv@getkeyname
  }{%
    \skv@err{No postset keys defined for '\skvcurrentpath'}\skv@ehd
  }%
}
% \skvundefpostsetkeys[<pref>]{<fam>}
\skvrobustdef*\skvundefpostsetkeys{%
  \skv@clfalse\skv@testoptb\skv@undefpostsetkeys
}
\skvrobustdef*\skvgundefpostsetkeys{%
  \skv@cltrue\skv@testoptb\skv@undefpostsetkeys
}
\skvrobustdef*\skv@undefpostsetkeys{%
  \skvifcsdefFT{\skvcurrentpath.@postset}{%
    \skv@err{No postset keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skvcurrentpath.@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{%
  \skvifcsdefTF{\skvcurrentpath.@#2}{%
    \expandafter\skv@filterremoveelements\csname\skv@header
      .@#2\endcsname{#1}\skv@getkeyname
  }{%
    \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd
  }%
}
% \skvundefpreposetkeys[<pref>]{<fam>}
\skvrobustdef*\skvundefpreposetkeys{%
  \skv@clfalse\skv@testoptb\skv@undefpreposetkeys
}
\skvrobustdef*\skvgundefpreposetkeys{%
  \skv@cltrue\skv@testoptb\skv@undefpreposetkeys
}
\skvrobustdef*\skv@undefpreposetkeys{%
  \skvifcsdefFT{\skvcurrentpath.@preset}{%
    \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skvcurrentpath.@preset}%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skvcurrentpath.@postset}%
  }%
}

%% Note about the absence of \skvsavevaluekeys (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@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsdef{\skv@header\skv@prova.@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@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skv@header\skv@prova.@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@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsdef{\skv@header\skv@prova.@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@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skv@header\skv@prova.@fbv}%
  }%
}
% \skvoptionkeys[<pref>]{<fam>}{<keys>}
\skvrobustdef*\skvoptionkeys{%
  \skv@clfalse\skv@testoptb{\skv@optionkeys{}}%
}
\skvrobustdef*\skvgoptionkeys{%
  \skv@cltrue\skv@testoptb{\skv@optionkeys{}}%
}
% \skvnonoptionkeys[<pref>]{<fam>}{<keys>}
\skvrobustdef*\skvnonoptionkeys{%
  \skv@clfalse\skv@testoptb{\skv@optionkeys{non}}%
}
\skvrobustdef*\skvgnonoptionkeys{%
  \skv@cltrue\skv@testoptb{\skv@optionkeys{non}}%
}
\skvrobustdef*\skv@optionkeys#1#2{%
  \skvcommaparse{#2}\skv@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsdef{\skv@header\skv@prova.@#1opt}{}%
  }%
}
% \skvremoveoptionkeys[<pref>]{<fam>}{<keys>}
\skvrobustdef*\skvremoveoptionkeys{%
  \skv@clfalse\skv@testoptb{\skv@removeoptionkeys{}}%
}
\skvrobustdef*\skvgremoveoptionkeys{%
  \skv@cltrue\skv@testoptb{\skv@removeoptionkeys{}}%
}
% \skvremovenonoptionkeys[<pref>]{<fam>}{<keys>}
\skvrobustdef*\skvremovenonoptionkeys{%
  \skv@clfalse\skv@testoptb{\skv@removenonoptionkeys{non}}%
}
\skvrobustdef*\skvgremovenonoptionkeys{%
  \skv@cltrue\skv@testoptb{\skv@removenonoptionkeys{non}}%
}
\skvrobustdef*\skv@removeoptionkeys#1#2{%
  \skvcommaparse{#2}\skv@prova{%
    \ifskv@cl\expandafter\global\fi
    \skvcsundef{\skv@header\skv@prova.@#1opt}%
  }%
}
% 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{%
  \skvifcsdefFT{\skvcurrentpath.@needini}{%
    \edef\skv@tempa{\unexpanded{#1}}%
    \skvcsvnormalize\skv@tempa
    \ifskv@cl\expandafter\global\fi
    \skvcsedef{\skvcurrentpath.@needini}{\skvexpandonce\skv@tempa}%
  }{%
    \expandafter\skv@filtermergelist\csname
    \skvcurrentpath.@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{%
  \skvifcsdefFT{\skvcurrentpath.@needini}{%
    \skv@err{No save-initial keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \expandafter\skv@filterremoveelements\csname
      \skvcurrentpath.@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{%
  \skvifcsdefFT{\skvcurrentpath.@needini}{%
    \skv@err{No save-initial keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \csname skv\ifskv@cl g\fi csundef\endcsname{\skvcurrentpath.@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{%
  \skvifcsdefTF{\skvcurrentpath.@ignoredkeys}{%
    \expandafter\skv@filtermergelist\csname
      \skvcurrentpath.@ignoredkeys\endcsname{#1}\skv@getkeyname
  }{%
    \edef\skv@tempa{\unexpanded{#1}}%
    \skvcsvnormalize[,]\skv@tempa
    \ifskv@cl\expandafter\global\fi
    \skvcsedef{\skvcurrentpath.@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{%
  \skvifcsdefFT{\skvcurrentpath.@ignoredkeys}{%
    \skv@err{No ignore keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \expandafter\skv@filterremoveelements\csname
      \skvcurrentpath.@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{%
  \skvifcsdefFT{\skvcurrentpath.@ignoredkeys}{%
    \skv@err{No ignore keys defined for '\skvcurrentpath'}\skv@ehd
  }{%
    \csname skv\ifskv@cl g\fi csundef\endcsname
    {\skvcurrentpath.@ignoredkeys}%
  }%
}
%
% Adding to the callback of existing keys.
%
% \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}}%
  \skvifcsdefTF{\skv@header\skvcurrentkey.@cbk}{%
    \skvcsedef{\skv@header\skvcurrentkey.@cbk}{%
      \skvifstrcmpTF{#1}{prep}{%
        \skvtrimspace{#3}%
        \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}%
      }{%
        \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}%
        \skvtrimspace{#3}%
      }%
    }%
  }{%
    \skvcsedef{\skv@header\skvcurrentkey.@cbk}{\skvtrimspace{#3}}%
  }%
}
\skvrobustdef*\skvsetkeys{%
  \skv@viarootofsetkeystrue
  \skv@testopta{\skv@testoptc\skv@setkeys@a}%
}
\skvrobustdef*\skvsetrmkeys{%
  \skv@viarootofsetkeystrue
  \skv@testopta{\skv@testoptc\skv@setrmkeys}%
}
\skvrobustdef*\skv@setrmkeys[#1]{%
  % Only one prefix is allowed here. \skv@testoptc has created
  % \skvcurrentprefix and \skv@fams.
  \skvifdefTF\skv@rmkeys{%
    \skvexpbracenext{\skv@setkeys@a[#1]}\skv@rmkeys
  }{}%
}
% \skv@setkeys@a[<ignored.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.
  \skvifdefboolTF{skv@intry}{}{%
    \skv@getnamesofkeys{#2}\skv@currentnames
  }%
  % 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
    \skv@viarootofsetkeysfalse
  \fi
  % Don't do \let\CurrentOption\@empty here.
}
\skvnewlet\skv@sav@setkeys@a\skv@setkeys@a
\skvrobustdef*\skv@badsetkeysinpreset[#1]#2{%
  \skv@err{%
    \noexpand\skvsetkeys or \noexpand\skvsetrmkeys can't be nested in a preset
    \MessageBreak key's callback: this will cause a cyclic call
    \MessageBreak to \noexpand\skvsetkeys or \noexpand\skvsetrmkeys
  }\skv@ehd
}
\skvrobustdef*\skv@setprepokeys#1#2{%
  \skv@inprepotrue
  \let\skv@setkeys@a\skv@badsetkeysinpreset
  % Preposet keys doesn't use \skv@fams in \skv@setkeys@d; it
  % branches off to \skv@setkeys@e. Hence the following loop is in
  % order.
  \skvecommaloop*\skv@fams\skvcurrentfamily{%
    \skv@makeheader\skvcurrentfamily
    \skvletcs\reserved@a{\skvcurrentpath.@#2}%
    \skvifdefFT\reserved@a{}{%
      \skvexpandtwoargs\skv@setkeys@b{\skvexpandonce\reserved@a}%
        {\skvaddlist,\skv@currentnames\unexpanded{#1}}%
    }%
  }%
  \let\skv@setkeys@a\skv@sav@setkeys@a
  \skv@inprepofalse
}
% \skv@setkeys@b{<kvlist>}{<ignored.list>}
%
% \skv@keydepth is called in places other than by \skvsetkeys. Hence we
% need an independent \skv@setkeysdepth here.
%
\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
  \ifnum\skv@setkeysdepth>\skv@stackdepthlimit\relax
    \skv@err{\noexpand\skvsetkeys is nested too deeply.
      \MessageBreak Maybe there has been infinite reentry}\skv@ehd
  \fi
  % \skv@na can't be anywhere else, not in \skv@setkeys@a.
  \edef\skv@na{\unexpanded{#2}}%
  \skvifemptyTF\skv@na{}{\skvcsvnormalize\skv@na}%
  \skvkvparse{#1}\CurrentOption{%
    \skv@processvalueaslistfalse
    % 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@setkeys@nil
  }%
  \skvgadvanceno\skv@setkeysdepth\m@ne
}
\skvrobustdef*\skv@setkeys@c#1={%
  \skvxifinTF{(}{\detokenize{#1}}{%
    \skvxifinTF{)}{\detokenize{#1}}{%
      % A pntkey can't carry pointers:
      \edef\skvcurrentkey{\unexpanded{#1}}%
      \let\skv@tkey\skvcurrentkey
      \skv@strippointersfromkey*\skvcurrentkey
      \skv@strippointersfromvalue*\skvcurrentkey
      \ifx\skvcurrentvalue\skv@tkey
        \skv@extractpntkey@atsetkey{#1}%
        \def\skv@ispntkey{00}%
        \ifx\skvcurrentvalue\@empty
          \skvnovaluetrue
        \else
          \skvnovaluefalse
        \fi
      \else
        \skv@err{A parenthesized key can't have pointers}\skv@ehd
      \fi
    }{%
      \skv@err{Key value has '(' but no ')'}
        {The value of a parenthesized key must have both opening
          \MessageBreak parenthesis '(' and closing ')'.}%
    }%
  }{%
    \def\skv@ispntkey{01}%
    \skv@g@tkeyname#1=\skv@getname@nil\skvcurrentkey
  }%
  % ##1=value:
  \def\skv@prova##1=##2\skv@setkeys@nil{%
    \skvifplaswTF\skv@ispntkey{%
      % \skv@extractpntkey@atsetkey would have extracted key name and
      % value for the pntkey.
    }{%
      \edef\skvcurrentvalue{\skvexpandonce{\@gobble##1}}%
      \ifx\skvcurrentvalue\skv@novaluetoks
        \skvnovaluetrue
        \def\skvcurrentvalue{}%
      \else
        \skvnovaluefalse
      \fi
      \skvexpbracenext\skvifbracedTF\skvcurrentvalue{%
        \skvvaluebracedtrue
      }{%
        \skvvaluebracedfalse
        \skv@strippointersfromvalue*\skvcurrentvalue
      }%
    }%
    \ifx\skvcurrentkey\@empty
      \ifx\skvcurrentvalue\@empty\else
        \skv@err{Current key is nil, but you have a value for it:
          \MessageBreak\skvoxdetok\skvcurrentvalue}\skv@ehd
      \fi
    \fi
    \skvifdefboolTF{skv@processvalueaslist}{%
      % You can use the pointer '.process list' on the current value to
      % signify that the value be processed as a list, in the
      % manner of a list key. This scheme is costlier than defining the
      % key as a listkey, using \skvlistkey, but it is more versatile.
      % This allows any key to accept and process a value that is a list,
      % irrespective of its argument pattern.
      \skv@processvalueaslist
    }{%
      \edef\CurrentOption{%
        \skvcurrentkey
        \ifx\skvcurrentvalue\@empty\else=\skvxonce\skvcurrentvalue\fi
      }%
      \skv@setkeys@d
    }%
  }%
  \skv@prova.%
}
\skvrobustdef*\skv@processvalueaslist{%
  \skvpushstate\skv@keystate\skv@keydepth
  \skvifemptyTF\skvcurrentvalue{%
    \let\CurrentOption\skvcurrentkey
    \skv@setkeys@d
  }{%
    \skvcommaparse*\skvcurrentvalue\skvcurrentvalue{%
      \edef\CurrentOption{%
        \skvcurrentkey
        \ifx\skvcurrentvalue\@empty\else=\skvxonce\skvcurrentvalue\fi
      }%
      \skv@setkeys@d
    }%
  }%
  \skvpopstate\skv@keystate\skv@keydepth
}
\skvrobustdef*\skv@setkeys@d{%
  \begingroup
  \skv@swatrue
  \skvxifinTF{,\skvcurrentkey,}{,\skv@na,}{\skv@swafalse}{}%
  \skvletcs\skv@tempa{\skvcurrentpath.@ignoredkeys}%
  \skvifdefTF\skv@tempa{%
    \skvxifinTF{,\skvcurrentkey,}{,\skv@tempa,}{\skv@swafalse}{}%
  }{}%
  \skvaftergroupifboolFT skv@swa\endgroup{}{%
    \skv@kffalse
    \skvifdefboolTF{skv@inprepo}{%
      \skv@setkeys@e
    }{%
      \skvifdefboolTF{skv@pl}{%
        % Set the key in all families in which it exists.
        \skvecommaloop*\skv@fams\skvcurrentfamily{%
          \skv@makeheader\skvcurrentfamily
          \skv@setkeys@e
        }%
      }{%
        % Stop searching as soon as the key is found in one family.
        \skvcommaloop*\skv@fams\skvcurrentfamily{%
          \skv@makeheader\skvcurrentfamily
          \skv@setkeys@e
          \ifskv@kf\skvbreakloop\fi
        }%
      }%
    }%
    \skvifdefboolTF{skv@kf}{%
      \skvifdefboolTF{skv@inpox}{%
        \ifskv@inclass\skvafterfi
          \expandafter\skv@removeoption\expandafter{\CurrentOption}%
        \fi
      }{}%
    }{%
      \skvifdefboolTF{skv@inpox}{%
        \skvifcsdefTF{\skvcurrentpath.@famhandler}{%
          \begingroup
          \let\elt\skvexpandonce
          \skvexpanded{\endgroup
            \skvnoexpandcs{\skvcurrentpath.@famhandler}%
              {\elt\skvcurrentprefix}{\elt\skvcurrentfamily}%
              {\elt\skvcurrentkey}{\elt\skvcurrentvalue}%
          }%
        }{%
          \skvifcsdefTF{skv@dox@\@currname.\@currext}{%
            \skvcsuse{skv@dox@\@currname.\@currext}%
          }{%
            \ifx\@currext\@clsextension\else
              \skv@err{%
                Unknown option '\skvcurrentfullkey'
                \MessageBreak for \@cls@pkg\space'\@currname'
              }{%
                The option '\skvcurrentfullkey'
                \MessageBreak was not declared in \@cls@pkg\space'\@currname'.
                \MessageBreak Perhaps you misspelled its name.
                \MessageBreak Try typing <return> to proceed.
              }%
            \fi
          }%
        }%
      }{%
        \skvifdefboolTF{skv@st}{%
          \skv@setkeys@f
        }{%
          \skvifcsdefTF{\skvcurrentpath.@famhandler}{%
            \begingroup
            \let\elt\skvexpandonce
            \skvexpanded{\endgroup
              \skvnoexpandcs{\skvcurrentpath.@famhandler}%
              {\elt\skvcurrentprefix}{\elt\skvcurrentfamily}%
              {\elt\skvcurrentkey}{\elt\skvcurrentvalue}%
            }%
          }{%
            % \ifskv@viarootofsetkeys is true, then \skvsetkeys is nested
            % in the callback of a .try key. In that case, throw the exception:
            \ifskv@intry
              \ifskv@viarootofsetkeys
                \skv@err{Key '\skvcurrentfullkey'
                  \MessageBreak is undefined}\skv@ehd
              \fi
            \else
              \skv@err{Key '\skvcurrentfullkey'
                \MessageBreak is undefined}\skv@ehd
            \fi
          }%
        }%
      }%
    }%
  }%
}
\skvrobustdef*\skv@setkeys@e{%
  \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{%
    \ifskv@intry\skv@successfalse\fi
  }{%
    \skv@kftrue
    \ifskv@intry\skv@successtrue\fi
    \ifskv@inpox
      \skvifcsname\skv@header\skvcurrentkey.@nonopt\then
        \skv@err{Key '\skvcurrenttriple'
          \MessageBreak is a non-option key but it has appeared
          \MessageBreak as a class or package option}\skv@ehd
      \fi
    \else
      \skvifcsname\skv@header\skvcurrentkey.@opt\then
        \skv@err{Key '\skvcurrenttriple'
          \MessageBreak is an option key but it has appeared
          \MessageBreak outside class or package option list}\skv@ehd
      \fi
    \fi
    \ifskvnovalue
      \skvifcsdefTF{\skv@header\skvcurrentkey.@ndv}{%
        \skv@err{Key '\skvcurrenttriple'
          \MessageBreak requires a user value.
          \MessageBreak Maybe it's a signal key}\skv@ehd
      }{%
        \skvifcsdefTF{\skv@header\skvcurrentkey.@defa}{%
          \skv@getdefault\skvcurrentprefix\skvcurrentfamily
            \skvcurrentkey\skvcurrentvalue
          % Check if the default value is braced:
          \skvexpbracenext\skvifbracedFT\skvcurrentvalue{}{%
            \skvvaluebracedtrue
          }%
        }{%
          \skv@err{No value specified for key
            \MessageBreak'\skvcurrenttriple'
            \MessageBreak and no default value}\skv@ehd
        }%
      }%
    \else
      \skvifcsdefTF{\skv@header\skvcurrentkey.@fbv}{%
        \skv@err{Key '\skvcurrenttriple' doesn't
          \MessageBreak accept a user value: key value forbidden.}\skv@ehd
      }{}%
    \fi
    \skvifemptyTF\skvcurrentvalue{}{%
      \expandafter\skv@replacepointers\expandafter{\skvcurrentvalue}%
    }%
    \skvletcs\skv@callback{\skv@header\skvcurrentkey.@cbk}%
    \skv@getargpattern
    % 1. Strip outer braces of \skvcurrentvalue, so that the argument can be
    %    grabbed by the, possibly multi-parametered, callback function.
    % 2. The argument has been obtained from possible explicit definition
    %    of the argument. But if the obtained argument is simple, then the
    %    argument in the key's call maybe more complex:
    \ifx\skv@argpattern\skv@simplearg\else
      \ifcsname\skv@header\skvcurrentkey.@listkey\endcsname
        \skv@err{A listkey must have a one-parameter argument}\skv@ehd
      \else
        \skvstripouterbraces{2}\skvcurrentvalue
        \skvvaluebracedfalse
      \fi
    \fi
    \skvcslet{\skv@header\skvcurrentkey.@value}\skvcurrentvalue
    \skvpushstate\skv@keystate\skv@keydepth
    \expandafter\expandafter\expandafter\def\expandafter\expandafter
      \expandafter\skv@prova\expandafter\skv@argpattern\expandafter
      \skeyvaleov\expandafter{\skv@callback}%
    \skvifxTF\skv@argpattern\skv@simplearg{%
      \expandafter\skv@prova\expandafter{\skvcurrentvalue}\skeyvaleov
    }{%
      \expandafter\skv@prova\skvcurrentvalue\skeyvaleov
    }%
    \skvpopstate\skv@keystate\skv@keydepth
  }%
}
% Save rm keys:
\skvrobustdef*\skv@setkeys@f{%
  \skvexpanded{%
    \skvmergelist\noexpand\skv@rmkeys{\skvexpandonce\CurrentOption}%
    % The boolean \ifdirkeys@saveunknownkeys is, by default, false.
    % The default value of \dirkeys@unknownkeysmacro is \skvfaillist.
    \ifindirectkeys
      \ifdirkeys@saveunknownkeys
        \skvmergelist\skvexpandonce\dirkeys@unknownkeysmacro
          {{\skvcurrentprefix}/{\skvcurrentfamily}/%
          {\skvexpandonce\CurrentOption}}%
      \fi
    \fi
  }%
}
% \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{\skvcurrentpath.@inikv}%
    \skvifdefFT\skv@tempa{%
      \skv@err{No 'save initial value' keys defined in family
        \MessageBreak'\skvcurrentpath'}\skv@ehd
    }{%
      % '\skvcurrentpath.@poxkeys' is the list of keys that have been
      % instantiated as current pacakge options.
      \skvletcs\skv@tempc{\skvcurrentpath.@poxkeys}%
      \skvifdefFT\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@getname@nil\skv@tempb
          \skvxifinTF{,\skvoxdetok\skv@tempb,}{,\skvoxdetok\skv@tempc,}{}{%
            \edef\skv@tempd{%
              \skvaddlist,\skv@tempd\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 in all the given families.
%
% The only difference between \skvedefinekeys and \skvdefinekeys is the
% value of \endlinechar.
%
% Examples:
%
% \skvdefinekeys*+[KVA]{fam1,fam2}[thp@]{%
%    .initialize keys after define=true,
%    .save initial values,
%    .prepend to every key name=X@,
%    .ord/{keya,keyb}/{default-a},
%    .cmd/{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}/
%      \def\f##1{#1*##1}/\@@warning{Invalid value for keyf},
%    .zbool/show center/true/\edef\cmd{\ifthp@showcenter Yes\else No\fi}/
%      \@@warning{Invalid value for show center},
%    .arg/{key1,key2}/{#1/#2},
%    .arg/key3/{#1+#2},
%    .exec/\def\x##1{##1},
%    .exec code=\def\y##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]{%
  \skvifboolvalT{#1}{%
    \csname skvdfk@initialize#1\endcsname
  }%
}

% Save default values as a kv list in macro <header>.@needini:
\skvordkey[SKV]{definekeys}{.save initial values}[true]{%
  \skvifboolvalT{#1}{%
    \csname skvdfk@saveinitialvalues#1\endcsname
  }%
}
\skvordkey[SKV]{definekeys}{.parser}[,]{%
  \skvifntypeTF{#1}{%
    \def\skvdfk@parser{#1}%
    \skvstripouterbraces{2}\skvdfk@parser
  }{%
    \skv@err{Parser '#1' is invalid; must be a single character}\@ehd
  }%
}
\skvrobustdef*\skvsetdefinekeysparser{\def\skvdfk@parser}
\skvsetdefinekeysparser{,}
\skvrobustdef*\skvsetupdefinekeys#1{\skvsetkeys[SKV]{definekeys}{#1}}

\skvsetupdefinekeys{.initialize=false,.save initial values=false}

\skvrobustdef*\skvtypecastfordefinekeys#1{%
  \begingroup
  \def\skv@tempa{}%
  \skvkvparse{#1}\skv@prova{%
    \skv@okvsplit\skv@prova{%
      \def\skv@keytypecast####1{%
        \skvifcsdefTF{skvdfk@typealias@####1}{%
          \skv@err{Key type alias '####1' already defined}\skv@ehd
        }{%
          \skvifstrcmpTF{##2}{^skv^}{%
            \skv@err{Key type alias '####1' has no master}\skv@ehd
          }{%
            \edef\skv@tempa{%
              \skvexpandonce\skv@tempa
              \skvcsdef{skvdfk@typealias@####1}{##2}%
            }%
          }%
        }%
      }%
      \skvcommaparse{##1}\skv@prova{%
        \expandafter\skv@keytypecast\expandafter{\skv@prova}%
      }%
    }%
  }%
  \expandafter\endgroup\skv@tempa
}
\skvtypecastfordefinekeys{%
  {.singleton,.state pattern}=.choice,
  .exec code=.exec,
  {.arg,.args}=.argx0,
  .arg expand once=.argx1,
  .arg expand twice=.argx2,
  .arg expanded=.argxx,
  {.initialize keys after define,.initialize after define,
    .initialize keys}=.initialize,
  {.save initial values,.save initial values of keys}=.saveini,
  {.prepend to key name,.prepend to every key name}=.preptokey,
  {.append to key name,.append to every key name}=.apptokey,
  {.prepend to callback,.prepend to every callback}=.preptocode,
  {.append to callback,.append to every callback}=.apptocode,
  {.exec after define,.after define}=.afterdef,
}
\skvnewdef*\skvdfk@afterdefine@hook{}
\skvrobustdef*\skvafterdefinekeys{%
  \skvgappendtomacro\skvdfk@afterdefine@hook
}
\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}}\skvdefaultprefix
}
\skvrobustdef*\skvdfk@t@stoptb#1[#2]#3{%
  \skvxifinTF{,}{\detokenize{#2}}{%
    \skv@err{Only one prefix is allowed when calling
      \MessageBreak \noexpand\skvdefinekeys or similar commands,
      \MessageBreak but you gave '#2'}\skv@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}%
    \skvxifinTF{,}{\detokenize{#3}}{%
      \edef\skvdfk@fams{#3}%
      \skvcsvnormalize\skvdfk@fams
    }{%
      \edef\skvdfk@fams{#3}%
      \skvdespacecontent\skvdfk@fams
    }%
    \skv@testopt#1{userhp@}%
  }%
}
\skvrobustdef*\skvedefinekeysifdefinable{\skvedefinekeys*}
% \skvedefinekeys has no stack, since everything is done in a local group.
% It is \skvmakekeys that has a stack.
\skvrobustdef*\skvedefinekeys{%
  \begingroup
  \endlinechar\m@ne
  \skvdfk@testopta\skv@definekeys@a
}
\skvrobustdef*\skvdefinekeys{%
  \begingroup
  \skvdfk@testopta\skv@definekeys@a
}
\skvrobustdef*\skv@definekeys@a[#1]#2{%
  \skvindeftrue
  \def\skv@validtypes{%
    .ord,.cmd,.zcmd,.bool,.zbool,.tog,.ztog,.choice,.choice*,%
    .exec,.afterdef,.initialize,.saveini,.argx0,.argx1,.argx2,.argxx,%
    .preptokey,.apptokey,.preptocode,.apptocode%
  }%
  \def\iftypeis##1\then{%
    \ifnum\skv@strcmp{\skvoxdetok\skv@type}{\detokenize{##1}}=\skvz@
  }%
  \def\do##1{\def##1{}}%
  \do\skv@prependtoeverykeyname\do\skv@appendtoeverykeyname
  \do\skv@prependtoeverycallback\do\skv@appendtoeverycallback
  % Take \skvindeftrue outside the current scope:
  \def\skv@accumulateonfams{\skvindeftrue}%
  \edef\skv@hp{#1}%
  \skvdespacecontent\skv@hp
  \skv@makeprefix\skvdfk@pref
  \edef\skvdfk@currlist{\skvkeepdoubleslashnormalize{#2}}%
  \def\skv@definekeys@b##1{%
    \skv@makeheader{##1}%
    \def\skv@accumulateonkeys{}%
    \def\skvdfk@keys{}%
    \def\skvdfk@keyvals{}%
    \skvexpbracenext\skv@definekeys@c\skvdfk@currlist
  }%
  % If the plus variant is not specified, just take and use the first
  % family.
  \skvifdefboolTF{skv@pl}{%
    \skvcommaloop*\skvdfk@fams\skv@tempa{%
      \skvexpbracenext\skv@definekeys@b\skv@tempa
    }%
  }{%
    \def\skv@prova##1,##2\skv@nil{%
      \skv@definekeys@b{##1}%
    }%
    \expandafter\skv@prova\skvdfk@fams,\skv@nil
  }%
  \edef\skv@accumulateonfams{%
    \skvxonce\skv@accumulateonfams
    \skvxonce\skvdfk@afterdefine@hook\relax
  }%
  \expandafter\endgroup\skv@accumulateonfams
  \skvindeffalse
  % 1. No need to reinitialize \skvdfk@afterdefine@hook here. Any changes
  %    done within the exited group haven't survived outside the group.
  % 2. Don't call \skvdfk@initializefalse and \skvdfk@saveinitialvaluesfalse
  %    here while in \directkeys: \skvdefinekeys is reentered for each path.
  %    \ifskvdfk@initialize and \ifskvdfk@saveinitialvalues are in the
  %    stack of \directkeys
  \ifindirectkeys\else
    \skvdfk@initializefalse\skvdfk@saveinitialvaluesfalse
  \fi
}
\skvrobustdef*\skv@definekeys@c#1{%
  \edef\skv@igkeys{%
    \skvifcsname\skvcurrentpath.@ignoredkeys\then
      \skvexpandcsonce{\skvcurrentpath.@ignoredkeys}%
    \fi
  }%
  \def\skv@splita##1/##2/{\skv@splitb##1/##2/.}%
  \def\skv@splitb##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@split@nil{%
    \skvxifinTF{\skv@hashchar}{\detokenize{##2}}{%
      \skv@err{Key name(s) '\detokenize{##2}' contain hash character}\skv@ehd
    }{%
      % In case we have, eg, .exec=\def\x{?} instead of .exec/\def\x{?}:
      \skvxifinTF{=}{\detokenize{##1}}{%
        \skv@kvsplit{##1}{%
          \edef\skv@type{\skvtrimspace{####1}}%
          \edef\skv@itemtwo{\skvtrimspace{####2}}%
        }%
      }{%
        \edef\skv@type{\unexpanded{##1}}%
        \edef\skv@itemtwo{\unexpanded{##2}}%
      }%
    }%
    \skvifcsname skvdfk@typealias@\skv@type\then
      \edef\skv@type{\@nameuse{skvdfk@typealias@\skv@type}}%
    \fi
    \edef\skv@default{\skvexpandonce{\@gobble##3}}%
    % To avoid mixing parameter characters in \skv@prova:
    \edef\skv@rest{\unexpanded{{##4}/{##5}/{##6}/{##7}/{##8}/{##9}}}%
    \skvexpbracenext\skv@definekeys@d\skv@type
  }%
  \def\skv@splitc##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@split@nil{%
    \edef\skv@itemfour{\unexpanded{##4}}%
    \edef\skv@itemfive{\unexpanded{##5}}%
    \edef\skv@itemsix{\unexpanded{##6}}%
    \skvifplaswTF\skv@isarg{%
      \skvdfk@definearg{##1}{##2}{##3}%
    }{%
      \if\skv@checkkeyexist
        \skvifcsname\skv@header##2.@cbk\then
          \skv@err{Key '\skvcurrenttriple' already exists}\skv@ehd
        \fi
      \fi
      \if\skv@ischoice
        \skv@ifcmdnut\skv@itemfour{%
          \skv@err{Empty nominations (state pattern) for
            \MessageBreak choice key '##2'}\skv@ehd
        }{}%
      \fi
      \edef\skvdfk@keys{\skvaddlist,\skvdfk@keys##2}%
      \skvxifboolvalTF{\skvexpandonce\skv@default}\in@true\in@false
      \edef\skvdfk@keyvals{%
        \skvaddlist,\skvdfk@keyvals
        \skv@ifcmdrejordotna\skv@default{}{%
          ##2=\skvifdefboolTF{in@}{false}{\skvexpandonce\skv@default}%
        }%
      }%
      \if\skv@isboolorchoice
        \if\skv@ischoice
          \let\skv@altact\skv@itemsix
        \else
          % bool or tog:
          \let\skv@altact\skv@itemfive
        \fi
        \skv@ifcmdnut\skv@altact{\let\skv@altact\skvkeyvalueerror}{}%
      \fi
      \iftypeis.choice*\then
        \def\skv@prova####1*{\def\skv@typeb{####1}}%
        \expandafter\skv@prova\skv@type
      \else
        \let\skv@typeb\skv@type
      \fi
      % Remove leading dot (.):
      \edef\skv@typeb{\expandafter\@gobble\skv@typeb}%
      \edef\skv@accumulateonkeys{%
        \skvexpandonce\skv@accumulateonkeys
        \skvnoexpandcs{skv\skv@typeb key}%
        \iftypeis.choice*\then*\fi
        \if\skv@isboolorchoice+\fi
        [\skvcurrentprefix]{\skvcurrentfamily}%
        \if\skv@needhp[\skv@hp]\fi
        {##2}%
        \if\skv@ischoice
          [\unexpanded{\skvuserinput\skvorder}]{\skvxonce\skv@itemfour}%
        \fi
        % default:
        \skv@ifcmdrejordotna\skv@default{}{[{\skvxonce\skv@default}]}%
        {% Key callback:
          \skvxonce\skv@prependtoeverycallback
          % If choice key, then the callback is item 5, otherwise
          % it's item 4:
          \if\skv@ischoice
            \skv@ifcmdnut\skv@itemfive{}{\skvxonce\skv@itemfive}%
          \else
            \skv@ifcmdnut\skv@itemfour{}{\skvxonce\skv@itemfour}%
          \fi
          \skvxonce\skv@appendtoeverycallback
        }%
        % Second branch of callback for '+' variants, now used for all
        % boolean and choice keys:
        \if\skv@isboolorchoice{\skvexpandonce\skv@altact}\fi
      }%
    }%
  }%
  \skvexpanded{\skvparselist{\skvdfk@parser}}{#1}\skv@prova{%
    \expandafter\skv@splita\skv@prova
      /^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^\skv@split@nil
  }%
  % 1. \skv@accumulateonfams 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.
  \def\@elt{%
    [\skvcurrentprefix]{\skvcurrentfamily}{\skvxonce\skvdfk@keyvals}%
  }%
  \edef\skv@accumulateonfams{%
    \skvexpandonce\skv@accumulateonfams
    \ifskvdfk@saveinitialvalues
      \noexpand\skvsaveinitialvaluekeys\@elt
    \fi
    \skvexpandonce\skv@accumulateonkeys
    \ifskvdfk@initialize
      \noexpand\skvsetkeys\@elt
    \fi
  }%
}
\skvrobustdef*\skv@definekeys@d#1{%
  \skvxifinFT{,#1,}{,\skv@validtypes,}{%
    \skv@err{Invalid key type '#1' in \string\skvdefinekeys}\skv@ehd
  }{%
    \@tempswatrue
    \iftypeis.exec\then
      \@tempswafalse
      \edef\skv@accumulateonkeys{%
        \skvexpandonce\skv@accumulateonkeys\skvexpandonce\skv@itemtwo
      }%
    \else
      \iftypeis.initialize\then
        \@tempswafalse
        \skv@ifcmdnut\skv@itemtwo{%
          \edef\skv@itemtwo{true}%
        }{%
          \skvexpbracenext\skvifboolvalT\skv@itemtwo\relax
        }%
        \csname skvdfk@initialize\skv@itemtwo\endcsname
        \edef\skv@accumulateonfams{%
          \skvexpandonce\skv@accumulateonfams
          \noexpand\csname skvdfk@initialize\skv@itemtwo\endcsname
        }%
      \else
        \iftypeis.saveini\then
          \@tempswafalse
          \skv@ifcmdnut\skv@itemtwo{%
            \edef\skv@itemtwo{true}%
          }{%
            \skvexpbracenext\skvifboolvalT\skv@itemtwo\relax
          }%
          \csname skvdfk@saveinitialvalues\skv@itemtwo\endcsname
          \edef\skv@accumulateonfams{%
            \skvexpandonce\skv@accumulateonfams
            \noexpand\csname skvdfk@saveinitialvalues\skv@itemtwo
              \endcsname
          }%
        \else
          \iftypeis.preptokey\then
            \@tempswafalse
            \let\skv@prependtoeverykeyname\skv@itemtwo
          \else
            \iftypeis.apptokey\then
              \@tempswafalse
              \let\skv@appendtoeverykeyname\skv@itemtwo
            \else
              \iftypeis.preptocode\then
                \@tempswafalse
                \let\skv@prependtoeverycallback\skv@itemtwo
              \else
                \iftypeis.apptocode\then
                  \@tempswafalse
                  \let\skv@appendtoeverycallback\skv@itemtwo
                \fi
              \fi
            \fi
          \fi
        \fi
      \fi
    \fi
    \skvifdefboolTF{@tempswa}{%
      % Leave these tests here, so that they aren't repeated for each
      % key in the next loop:
      \def\do##1{\def##1{01}}%
      \do\skv@isboolorchoice\do\skv@ischoice\do\skv@needhp\do\skv@isarg
      \skvifinTF{,#1,}{,.choice,.choice*,}{%
        \skvstripouterbraces{2}\skv@default
        \def\do##1{\def##1{00}}%
        \do\skv@ischoice\do\skv@isboolorchoice\do\skv@needhp
      }{%
        \skvifinTF{,#1,}{,.bool,.zbool,.tog,.ztog,}{%
          \def\skv@isboolorchoice{00}%
          \def\skv@needhp{00}%
        }{%
          \skvifinTF{,#1,}{,.cmd,.zcmd,}{%
            \def\skv@needhp{00}%
          }{%
            \skvifinTF{,#1,}{,.argx0,.argx1,.argx2,.argxx,}{%
              \def\skv@isarg{00}%
            }{}%
          }%
        }%
      }%
      \skvcommaloop*\skv@itemtwo\skv@tempa{%
        \skv@strippointersfromkey*\skv@tempa
        \edef\skvcurrentkey{%
          \skv@prependtoeverykeyname\skvcurrentkey
          \skv@appendtoeverykeyname
        }%
        \let\do\skvoxdetok
        \skvxifinTF{,\do\skvcurrentkey,}{,\do\skv@igkeys,}{}{%
          \edef\do{%
            \noexpand\skv@splitc#1/\skvcurrentkey/%
              {\skvexpandonce\skv@default}/\skvexpandonce\skv@rest
          }%
          \do\skv@split@nil
        }%
      }%
    }{%
      \skvifinFT{,#1,}{,.preptokey,.apptokey,.preptocode,.apptocode,}{}{%
        \let\skv@tempa\skv@itemtwo
        \skv@strippointersfromkey*\skv@itemtwo
        \let\skv@itemtwo\skvcurrentkey
        \ifx\skv@tempa\skv@itemtwo\else
          \skv@err{The token ||\skvoxdetok\skv@itemtwo||
            \MessageBreak shouldn't contain pointers for the
            given key type or requested action}\skv@ehd
        \fi
      }%
    }%
  }%
}
%
% Assigning arguments within \skvdefinekeys:
%
% Eg, using the type .arg:
%
%   .arg/{key1,key2}/{#1/#2}
%
\skvrobustdef*\skvdfk@definearg#1#2#3{%
  \begingroup
  \def\skv@prova.arg##1\skv@nil{%
    \edef\skv@prova{\skvtrimspace{##1}}%
  }%
  \skv@prova#1\skv@nil
  \edef\skv@accumulateonkeys{%
    \skvexpandonce\skv@accumulateonkeys
    \skvcsedef{\skv@header#2.@\skv@prova arg}{%
      \noexpand\unexpanded{\unexpanded{#3}}%
    }%
  }%
  \skvaftergroupdef\skv@accumulateonkeys\endgroup
}

%+++++++++++++++++++++++ Keys for \skvmakekeys +++++++++++++++++++++++%
% Example:
% \skvmakekeys[
%    .prefix=KVA, .families={fam1,fam2}, .hp=thp@, .all new,
%    .define in all families, .initialize=true, .endlinechar=-1
% ]{%
%    .ord/{keya,keyb}/{default-a},
%    .cmd/{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@]{%
  .ord/{.new,.all new,.all are new}/true/
    \edef\skvmk@allnew{0\skvifstrcmpTF{#1}{true}{0}{1}}
  ,
  .ord/{.initialize,.initialize after define,.initialize keys after define}
    /true/
    \skvifboolvalT{#1}{%
      \csname skvdfk@initialize#1\endcsname
    }%
  ,
  .ord/.save initial values,.save initial values of keys/true/
    \skvifboolvalT{#1}{%
      \csname skvdfk@saveinitialvalues#1\endcsname
    }%
  ,
  .ord/{.endlinechar,.endline character}/13/
    \def\skvmk@endlinechar{#1},
  .ord/{.define in all families,.in all families}/true/
    \edef\skvmk@inallfams{0\skvifstrcmpTF{#1}{true}{0}{1}}
  ,
  .ord/.prefix/\skvdefaultprefix/
    \skviflacus#1\then
      \let\skvmk@pref\skvdefaultprefix
    \else
      \edef\skvmk@pref{#1}%
      \skvstripouterbraces{2}\skvmk@pref
    \fi
  ,
  .ord/{.family,.families}/\skv@nil/
    \skviflacus#1\then
      \skv@err{Key family is empty\on@line}\skv@ehd
    \else
      \edef\skvmk@fams{#1}%
      \skvstripouterbraces{2}\skvmk@fams
    \fi
  ,
   .ord/{.holder prefix,.hp}/userhp@/\edef\skvmk@hp{#1}
}

% 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,.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@nnil
    \skv@err{No family specified for \string\skvmakekeys}\skv@ehd
  \fi
  % The following \begingroup is ended in \skv@definekeys:
  \begingroup
  \skvexpanded{%
    \endlinechar=\skvmk@endlinechar\relax
    \skvdfk@testopta\skv@definekeys@a
    \if\skvmk@allnew*\fi\if\skvmk@inallfams+\fi
    [\skvmk@pref]{\skvmk@fams}%
    [\ifx\skvmk@hp\@empty userhp@\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@getinnoval#1{%
  \begingroup
  \skv@okvsplit{#1}{%
    \edef\skv@provb{\detokenize{##2}}%
    \skvifxTF\skv@provb\@empty{%
      \def\skv@ival{???}%
    }{%
      \skv@getbasetoks{30}\skv@provb\skv@ival
    }%
  }%
  \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}%
    \skvifxTF\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\skvdefaultprefix
}
% 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{\skvcurrentpath.@famhandler}##1##2##3##4{#3}%
    }%
  }%
}

%%++++++++++++++++++++++ Keys for \skvusekeys: +++++++++++++++++++++++%%
% Example:
% \skvusekeys[
%    .prefix=KVA,.families={fam1,fam2},.set in all families,
%    .save rm keys
% ]{%
%    key1=value1, key2=value2
% }
\skvmakekeys[
  .prefix=SKV,.family=usekeys,.hp=skvuk@,.endlinechar=-1
]{%
  .ord/{.save rm,.save rm keys,.save unknown keys}/true/
    \edef\skvuk@saverm{\skvifstrcmpTF{#1}{true}{00}{01}}
  ,
  .ord/{.set in all families,.in all families}/true/
    \edef\skvuk@inallfams{\skvifstrcmpTF{#1}{true}{00}{01}}
  ,
  .ord/.prefix/\skvdefaultprefix/
    \skviflacus#1\then
      \let\skvuk@pref\skvdefaultprefix
    \else
      \edef\skvuk@pref{#1}%
      \skvstripouterbraces{2}\skvuk@pref
    \fi
  ,
  .ord/{.family,.families}/\skv@nil/
    \skviflacus#1\then
      \skv@err{Key family is empty\on@line}\skv@ehd
    \else
      \edef\skvuk@fams{#1}%
      \skvstripouterbraces{2}\skvuk@fams
    \fi
  ,
  .ord/{.path,paths}/\skv@nil/
    \skvifblankTF{#1}{%
      \skv@err{Key family is empty\on@line}\skv@ehd
    }{%
      \edef\skv@tempa{#1}%
      \ifx\skv@tempa\skv@nnil
        \skv@err{No path specified for \string\skvusekeys}\skv@ehd
      \else
        \skvstripouterbraces{2}\skv@tempa
        \skv@splitpath@a\skv@tempa\skvuk@pref\skvuk@fams
      \fi
    }%
  ,
}

% Default settings for keys of \skvusekeys:
\skvsetkeys[SKV]{usekeys}{%
  .prefix,.family,.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@nnil
    \skv@err{No family specified for \string\skvusekeys}\skv@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\skvhandlereov\relax
% Undefine \dirkeys@handler@, in case a handler function is called
% without the handler name. Handler functions usually require
% 'dirkeys@handler@<handler.name>'.
\skvundef\dirkeys@handler@

\skvrobustdef*\skvsetdirectkeysparser{\def\dirkeys@parser}
\skvsetdirectkeysparser{,}
\skvrobustdef*\directkeys{\skv@testst\skv@dirkeys@parse@a}
\skvrobustdef*\skv@dirkeys@parse@a{%
  \begingroup
  \ifskv@tempst\endlinechar\m@ne\fi
  \skv@dirkeys@parse@b
}
\skvrobustdef*\skv@dirkeys@parse@b#1{%
  \endgroup
  \skvpushstate\skv@dirkeys@state\dirkeys@depth
  \indirectkeystrue
  \def\dirkeys@pathlist{}%
  \def\dirkeys@holderprefixtoks{}%
  \dirkeys@saveunknownkeysfalse
  \skvdfk@initializefalse
  \skvdfk@saveinitialvaluesfalse
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvstripouterbraces{2}\skv@tempa
  % Normalize with respect to equality (=) and comma (,), since
  % \skvkvnormalize can't normalize with respect to \dirkeys@parser
  % that isn't comma (,). \skvparselist will normalize with respect
  % to \dirkeys@parser:
  \skvkvnormalize\skv@tempa
  \def\skv@dirkeys@split##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 \directkeys. If it is, then
    % we have a nested \dirkeys:
    \expandafter\skvifxTF\skv@car##1x\car@nil\directkeys{%
      \skvifemptyTF\skv@tempa{%
        ##1\relax
      }{%
        \skv@err{Something wrong with use of \string\dirkeys:
          \MessageBreak equals ('=') found after \string\dirkeys}\skv@ehd
      }%
    }{%
      \skv@ensurehandlerdot{##1}%
      \skvifcsdefTF{dirkeys@handler@##1}{%
        \csname dirkeys@handler@##1\expandafter\endcsname
          \skv@tempa\skvhandlereov
      }{%
        \skv@err{Unknown handler '\detokenize{##1}'
          \MessageBreak in command \string\directkeys, or no '=' sign
          \MessageBreak after handler name}\skv@ehd
      }%
    }%
  }%
  % Normalize with respect to \dirkeys@parser:
  \skvexpanded{\skvparselist*{\dirkeys@parser}}\skv@tempa\skv@tempa{%
    \expandafter\skv@dirkeys@split\skv@tempa==\dirkeys@nil
  }%
  \indirectkeysfalse
  \skvpopstate\skv@dirkeys@state\dirkeys@depth
}
\skvrobustdef*\dirkeys@setdefaultpath{%
  \ifx\dirkeys@pathlist\@empty
    \ifskv@inopt
      \edef\dirkeys@pathlist{%
        \noexpand\skv@pathdo{KV}{\@currname.\@currext}%
      }%
    \else
      \skv@err{No key can have empty or nil path/family}\skv@ehd
    \fi
  \fi
  \ifx\dirkeys@holderprefixtoks\@empty
    \def\dirkeys@holderprefixtoks{user@}%
  \fi
}
\skvnewnumbers[skv@]{pathdepth}
\skvrobustdef*\skv@pushpathdo{%
  \edef\skv@pathdepth{\the\numexpr\skv@pathdepth+1}%
  \skvcslet{skv@pathdo@\romannumeral\skv@pathdepth}\skv@pathdo
}
\skvrobustdef*\skv@poppathdo{%
  \skvletcs\skv@pathdo{skv@pathdo@\romannumeral\skv@pathdepth}%
  \edef\skv@pathdepth{\the\numexpr\skv@pathdepth-1}%
}
\skvrobustdef*\dirkeys@dodefinekeys#1#2{%
  \begingroup
  % #2 may contain parameter characters that may be confused with
  % ##1 and ##2 below. Hence we put it in the macro \skv@tempb.
  % Note: Ignored keys are omitted by \skvdefinekeys.
  \edef\skv@tempb{\unexpanded{#2}}%
  \def\skv@tempa{}%
  \def\skv@pathdo##1##2{%
    \edef\skv@tempa{%
      \skvexpandonce\skv@tempa
      \skvdefinekeys#1[##1]{##2}%
      [\dirkeys@holderprefixtoks]{\skvexpandonce\skv@tempb}%
    }%
  }%
  \dirkeys@pathlist
  \expandafter\endgroup\skv@tempa
}
% \dirkeys@definetypekeys{<type>}{<star.or.empty>}{<keylist>}
\skvrobustdef*\dirkeys@definetypekeys#1#2#3{%
  \begingroup
  \def\skv@tempa{}%
  \skvexpanded{\skvparselist{\skvdfk@parser}}{#3}\skv@prova{%
    \edef\skv@tempa{%
      \skvexpandonce\skv@tempa
      \ifx\skv@tempa\@empty\else\skvdfk@parser\fi
      .#1/\skvexpandonce\skv@prova
    }%
  }%
  \skvexpanded{\endgroup
    \dirkeys@dodefinekeys{#2}{\skvexpandonce\skv@tempa}%
  }%
}
\skvrobustdef*\dirkeys@setkeys#1#2#3#4{%
  \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{#4}}%
  \def\skv@tempa{}%
  \def\skv@pathdo##1##2{%
    \edef\skv@tempa{%
      \skvexpandonce\skv@tempa
      #3\ifdirkeys@saveunknownkeys*\else#1\fi
      [##1]{##2}[\skvcsuse{##1/##2/.@ignoredkeys}]%
      \skvifstrcmpTF{#2}{rm}{}{{\skvexpandonce\skv@tempb}}%
    }%
  }%
  \dirkeys@pathlist
  \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\skv@pathdo##1##2{%
    \def\skv@tempb{}%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvcommaparse{#1}\skv@tempa{%
      \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@igkeys,}{}{%
        \edef\skv@tempb{\skvaddlist,\skv@tempb\skvexpandonce\skv@tempa}%
      }%
    }%
    \edef\skv@tempd{%
      \skvexpandonce\skv@tempd
      \skvifemptyTF\skv@tempb{}{%
        \noexpand#2[##1]{##2}{\skvexpandonce\skv@tempb}%
      }%
    }%
  }%
  \dirkeys@pathlist
  \expandafter\endgroup\skv@tempd
}

% \dirkeys@dolistedkeys@b{<callback>}
\skvrobustdef*\dirkeys@dolistedkeys@b#1{%
  \begingroup
  \def\skv@tempa{}%
  % ##1=prefix, ##2=family
  \def\skv@pathdo##1##2{%
    \edef\skv@tempa{%
      \skvexpandonce\skv@tempa
      \unexpanded{\skv@makeprefix{##1}\skv@makeheader{##2}#1}%
    }%
  }%
  \dirkeys@pathlist
  \expandafter\endgroup\skv@tempa
}
\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}'}\skv@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
}

% Pushing and popping path or holder prefix.
%
\skvnewnumbers[dirkeys@meta]{pathdepth,hpdepth}
\skvrobustdef*\dirkeys@pushmeta#1{%
  \skvifcasse{#1}
    case{path}{\def\@elt{pathlist}}
    case{hp}{\def\@elt{holderprefixtoks}}
  \elsedo
    \skv@err{Unknown meta type '#1'}\skv@ehd
  \endif
  \skvaftercs\skvgadvanceno{dirkeys@meta#1depth}\@ne
  \edef\skv@elt{\skvrom\csname dirkeys@meta#1depth\endcsname}%
  \skvcsletcs{dirkeys@\@elt @meta@\skv@elt}{dirkeys@\@elt}%
}
\skvrobustdef*\dirkeys@popmeta#1{%
  \skvifcase\skvifstrcmpTF{#1}%
    {path}{\def\@elt{pathlist}}%
    {hp}{\def\@elt{holderprefixtoks}}%
  \elsedo
    \skv@err{Unknown meta type '#1'}\skv@ehd
  \endif
  \edef\skv@elt{\skvrom\csname dirkeys@meta#1depth\endcsname}%
  \skvcsletcs{dirkeys@\@elt}{dirkeys@\@elt @meta@\skv@elt}%
  \skvaftercs\skvgadvanceno{dirkeys@meta#1depth}\m@ne
}

% Assigning admissible values to keys. The handlers here are:
%
% .state pattern, .state pattern expanded, .state pattern expand once,
% .state pattern expand twice.
%
% \dirkeys@savechoice{<list>}{<expander.type>}
%
% 1. <list> has the form
%
%      {<key1,...,keyn>}/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}}
%              ,...,
%      <keyx-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
  \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@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvcommaparse*\skvcurrentmaintoks\skv@tempb{%
      \skvxifinTF{,\skv@tempb,}{,\skv@igkeys,}{}{%
        \skvifcsdefTF{##1/##2/\skv@tempb.@cbk}{}{%
          \edef\skv@tempd{%
            \skvexpandonce\skv@tempd
            \skvordkey[##1]{##2}{\skv@tempb}[]{}%
          }%
        }%
        \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}{\skv@tempb}%
          {\dirkeys@executechoice{#2}{########1}%
          {\skvexpandonce\skv@choicelist}}%
      }%
    }%
  }%
  \dirkeys@pathlist
}
% \dirkeys@executechoice{<expansion.type>}{<curr.value>}{<choices.and.cbks>}
\skvrobustdef*\dirkeys@executechoice#1#2#3{%
  \skv@getexpander@b{#1}%
  \edef\skv@prova{\skvexpander{#2}}%
  \skvdespacecontent\skv@prova
  \def\do##1##2##3\skv@nil{%
    \edef\skv@provb{\unexpanded{##1}}%
    \skvifxTF\skv@provb\skv@prova{%
      ##2%
    }{%
      \skvifblankTF{##3}{%
        \skv@err{No choice match found for key '\skvcurrentkey'}\skv@ehd
      }{%
        \do##3\skv@nil
      }%
    }%
  }%
  \do#3\skv@nil
}

% Slot/style keys represent a limited signal-slot (or subject-observer)
% concept from Java.
%
% The handlers here are:
%
% .slot, .slot value expanded, .slot expand once, .slot expand twice,
% .prepend slot, .prepend slot value expanded, .prepend slot expand once,
% .prepend slot expand twice, .append slot, .append slot value expanded,
% .append slot expand once, .append slot expand twice
%
% Note that the handlers are preceded by 'append' or 'prepend'.
%
% \dirkeys@saveslots{<list>}{<append.or.prepend>}{<expander.type>}
%
% 1. <list> has the form
%
%    {<signal key1>,...,<signal key-n>}/{<slot1=val1>,...,<slot-n=val-n>}
%                            ,...,
%    {<signal key1>,...,<signal key-n>/{<slot1=val1>,...,<slot-n=val-n>}
%
% 2. Here the slots are linked with each signal key. When the signal key is
%    set, the associated slots will be set with the given values.
%
% 3. Examples:
%
%    a) The following example means that keyc and keyd are signals
%       (representations for the actions on their right, ie, for setting
%       the slots '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 slot 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 slot value expanded={keye,keyf}/{keyc=#1,keyd=#1}
%
\skvrobustdef*\dirkeys@saveslots#1#2#3{%
  \begingroup
  \skv@getexpander@b{#3}%
  \def\skv@tempd{}%
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvcsvnormalize[/]\skv@tempa
  \skvcommaparse*\skv@tempa\skv@tempa{%
    \skvexpbracenext\dirkeys@s@veslots\skv@tempa{#2}%
  }%
  \expandafter\endgroup\skv@tempd
}
\skvrobustdef*\dirkeys@s@veslots#1#2{%
  \skv@getmaintoks{#1}{/}%
  \skvcsvnormalize\skvcurrentmaintoks
  \skvkvnormalize\skvcurrentsub
  \def\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvcommaloop*\skvcurrentmaintoks\skv@signalkey{%
      \skvxifinTF{,\skv@signalkey,}{,\skv@igkeys,}{}{%
        \skvifcsdefTF{##1/##2/\skv@signalkey.@cbk}{}{%
          % Signal keys should always be set with a value. If they were
          % undefined before being designated as a signal key, then
          % clearly they should have no default. The empty callback will
          % later be replaced by signal emitters to the slots.
          \edef\skv@tempd{%
            \skvexpandonce\skv@tempd
            \skvordkey[##1]{##2}{.need value{\skv@signalkey}}{}%
          }%
        }%
        % Insert expanders in the sub list:
        \def\skv@slotlist{}%
        \skvcommaloop*\skvcurrentsub\skv@tempa{%
          \skv@okvsplit\skv@tempa{%
            \skvxifstrcmpTF{\skv@signalkey}{####1}{%
              \skv@err{Linking key '####1' to itself}\skv@ehd
            }{%
              % Slot keys must always be predefined, otherwise the signal
              % will be useless:
              \skvifcsdefTF{##1/##2/####1.@cbk}{}{%
                \skv@err{Slot key '####1' is undefined}\skv@ehd
              }%
            }%
            \skvxifinTF{,####1,}{,\skv@igkeys,}{}{%
              \edef\skv@slotlist{%
                \skvaddlist,\skv@slotlist\unexpanded{####1}=%
                \skvexpandonce\skvexpander{\unexpanded{####2}}%
              }%
            }%
          }%
        }%
      }%
      \edef\skv@tempd{%
        \skvexpandonce\skv@tempd
        % The slots contain value expanders \@firstofone, \unexpanded, etc.
        % They will be expanded by the next \skvexpanded when the main key
        % is set.
        \skvifstrcmpTF{#2}{app}{\skvappendkeycode}{\skvprependkeycode}%
          [##1]{##2}{\skv@signalkey}{%
          % Signal keys shouldn't be preset, to avoid cyclic setting
          % of keys. See note above.
          \noexpand\skvifdefboolTF{skv@inprepo}{%
            \noexpand\skv@signalpreseterr
          }{%
            % The following \skvexpanded works on expanders of slot values:
            \skvexpanded{\skvsetkeys[##1]{##2}{\skvexpandonce\skv@slotlist}}%
          }%
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
}

%% +++++++++++++++ Linking keys:
%
% The handlers here are:
%
% .link, .link expanded, .link expand once, .link expand twice,
% .prepend link, .prepend link expanded, .prepend link expand once,
% .prepend link expand twice,
% .append link, .append link expanded, .append link expand once,
% .append link expand twice
%
% \dirkeys@savelinks{<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:
%
%        .slot={{keyb,keyc}/keya=#1} OR .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@savelinks#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{%
        \skvaddlist,\skv@tempd\skvexpandonce\skv@tempb=####1%
      }%
    }%
    \edef\skv@tempe{%
      \skvaddlist,\skv@tempe
      {\skvexpandonce\skvcurrentmaintoks}/{\skvexpandonce\skv@tempd}%
    }%
  }%
  \expandafter\endgroup\expandafter\dirkeys@saveslots
    \expandafter{\skv@tempe}{#2}{#3}%
}
% ++++++++++++ Storing values of keys:
%
% The handlers here are:
%
% .store value in, .store expanded value in, .store expanded once value in,
% .store expanded twice value in.
%
% \dirkeys@savestores{<list>}{<expander.type>}
%
% 1. <list> has the form
%
%    <key-1>/<cmd-1>,...,<key-n>/<cmd-n>
%
% 2. Examples:
%
%    .store expanded twice value in={keyc/\valc,keyd/\vald}
%
\skvrobustdef*\dirkeys@savestores#1#2{%
  \begingroup
  \skv@getexpander@b{#2}%
  \def\skv@tempd{}%
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvcsvnormalize[/]\skv@tempa
  \skvcommaparse*\skv@tempa\skv@tempa{%
    \skv@oslashsplit\skv@tempa{%
      \skvifstrcmpTF{##2}{^skv^}{%
        \skv@err{No holder commands for key(s) '\detokenize{##1}'}\skv@ehd
      }{%
        \skvcommaparse{##1}\skv@tempa{%
          \skvexpbracenext\dirkeys@s@vestore\skv@tempa{##2}%
        }%
      }%
    }%
  }%
  \expandafter\endgroup\skv@tempd
}
\skvrobustdef*\dirkeys@s@vestore#1#2{%
  \def\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvxifinTF{,#1,}{,\skv@igkeys,}{}{%
      \skvifcsdefFT{##1/##2/#1.@cbk}{%
        \skv@err{Key '#1' isn't defined
          \MessageBreak on path '##1/##2'}\skv@ehd
      }{%
        \edef\skv@tempd{%
          \skvexpandonce\skv@tempd
          \skvprependkeycode[##1]{##2}{#1}{%
            \skvletcs\noexpand\reserved@a{##1/##2/#1.@value}%
            \edef\noexpand#2{%
              % Don't use \skvcurrentvalue here, since this
              % changes when .setkeys is nested:
              \skvexpandonce\skvexpander{\noexpand\reserved@a}%
            }%
          }%
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
}

\skvrobustdef*\dirkeys@keyslet#1{%
  \def\skv@pathdo##1##2{%
    \skv@makeprefix{##1}%
    \skv@makeheader{##2}%
    \skv@keyslet{#1}%
  }%
  \dirkeys@pathlist
}

% #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@pathdo because it contains parameter
  % characters:
  \edef\skv@tempd{\unexpanded{#1}}%
  \def\skv@tempe{}%
  \skvkvnormalize\skv@tempd
  \def\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvcommaloop*\skv@tempd\skv@tempa{%
      \skv@okvsplit\skv@tempa{%
        \skvxifinTF{,####1,}{,\skv@igkeys,}{}{%
          \skvifcsdefTF{##1/##2/####1.@cbk}{%
            \skvifblankTF{####2}{}{%
              \edef\skv@tempe{%
                \skvexpandonce\skv@tempe\unexpanded{%
                  \skvcsedef{##1/##2/####1.@#2arg}{\unexpanded{####2}}%
                }%
              }%
            }%
          }{%
            \skv@err{Key '####1' is undefined
              \MessageBreak in family '##1/##2'}\skv@ehd
          }%
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
  \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}%
    \skvifdefTF\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'}\skv@ehd
    }%
  }%
  \skvexpanded{\endgroup
    \skvsetkeys*+[#1]{#2}{\skvexpandonce\skv@defaultlist}%
  }%
}
\skvrobustdef*\dirkeys@setunknownkeyhandler#1{%
  \begingroup
  \edef\skv@tempa{\unexpanded{#1}}%
  \def\skv@tempb{}%
  \toks@{##1##2##3##4}%
  \def\skv@pathdo##1##2{%
    \skv@makeprefix{##1}%
    \skv@makeheader{##2}%
    \edef\skv@tempb{%
      \skvexpandonce\skv@tempb\protected
      \skvcsdef{\skvcurrentpath.@famhandler}\the\toks@{%
        \skvexpandonce\skv@tempa
      }%
    }%
  }%
  \dirkeys@pathlist
  \expandafter\endgroup\skv@tempb
}

%%++++++++++++++++++++++++++  Try setting keys +++++++++++++++++++++++%%
%
% \skvtrysetkeys[<paths>]{<keyvals>}
%
% \directkeys{
%   .try set keys={<options>}, where
%    <options> includes <paths> and key-value list (see below).
% }
%
% Try to set a given key-value list on the paths given by the independent
% <paths> 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
% and uses its own paths from <options>.
%
% 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 100.
% .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. The default of this is 0, meaning that
%      by default no error message will be returned if a key isn't found
%      on any of the paths.
% .path/.paths:
%      Paths to search <pref1>/<fam1>,...,<pref-n>/<fam-n>.
% .prefix
%      The prefix to search (default: KV). The number of prefixes can't
%      be more than 1. If you have multiple prefixes, then use the
%      .paths key. The given prefix will be prepended to all the given
%      families.
% .families/.family
%      The families to search <fam1>,...,<fam-n>.
% .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.
% .save unknown keys:
%      The boolean that directs that unknown keys should be saved
%      in a macro.
% .save unknown keys in/fail list macro/fail list:
%      The macro into which to save unknown keys. Each unknown key is
%      saved in the format <pref>/<fam>/<key>. The user can
%      inspect this macro to see the unknown keys. The default macro is
%      \skvfaillist.
% .path exception
%      The user-supplied action (code) to take when a key hasn't been found
%      on the curent path of the given paths. The default path exception
%      is nil.
% .hits exception
%      The user-supplied action (code) to take when the number of hits is
%      less than preferred minimum success. This may specify retrying on
%      some other paths. The default exception is an error message.
%
% NOTES:
%
% 1. 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@]{%
  .initialize keys after define,
  .ord/{.try,.max attempt,.max attempts,.total}/100/
    \skvensureinteger{.max attempt}{#1}%
    \def\skvtry@maxattempts{#1}%
    \skvstripouterbraces{2}\skvtry@maxattempts
    \edef\skvtry@maxattempts{\number\skvtry@maxattempts}%
    \ifnum\skvtry@maxattempts<\@ne
      \skv@err{The max. number of attempts ('try')
        \MessageBreak can't be less than 1}\skv@ehd
    \fi
  ,
  .ord/{.upper goal,.max success,.max hits}/1/
    \skvensureinteger{upper goal}{#1}%
    \def\skvtry@maxsuccess{#1}%
    \skvstripouterbraces{2}\skvtry@maxsuccess
    \edef\skvtry@maxsuccess{\number\skvtry@maxsuccess}%
    \ifnum\skvtry@maxsuccess<\@ne
      \skv@err{The max. number of successful attempts
        \MessageBreak ('upper goal') can't be less than 1}\skv@ehd
    \fi
  ,
  .ord/{.min success,.lower goal,.goal}/0/
    \skvensureinteger{lower goal}{#1}%
    \def\skvtry@minsuccess{#1}%
    \skvstripouterbraces{2}\skvtry@minsuccess
    \edef\skvtry@minsuccess{\number\skvtry@minsuccess}%
    \ifnum\skvtry@minsuccess<\skvz@
      \skv@err{The min. number of successful attempts
        \MessageBreak ('lower goal') can't be less than 0}\skv@ehd
    \fi
  ,
  % #1=<pref1>/<fam1>,...,<pref-n>/<fam-n>
  .ord/{.path,.paths,.add path,.add paths}/\skv@nil/{
    \edef\skv@tempa{#1}%
    \skvstripouterbraces{2}\skv@tempa
    \ifx\skv@tempa\skv@nnil\def\skv@tempa{}\fi
    \ifx\skv@tempa\@empty\else
      \skvcommaparse*\skv@tempa\skv@prova{%
        \skv@splitpath@a\skv@prova\skv@tempa\skv@tempb
        \edef\skv@prova{{\skv@tempa}{\skv@tempb}}%
        \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok\skvtry@pathlist}{}{%
          \edef\skvtry@pathlist{%
            \skvexpandonce\skvtry@pathlist\noexpand\skvtry@pathdo\skv@prova
          }%
        }%
      }%
      \ifskv@insa
        \begingroup
        \def\skv@pathdo##1##2{%
          \edef\skv@prova{\detokenize{{##1}{##2}}}%
          \skvxifinTF{\skv@prova}{\skvoxdetok\skvtry@pathlist}{}{%
            \edef\skvtry@pathlist{%
              \skvexpandonce\skvtry@pathlist
              \noexpand\skvtry@pathdo{##1}{##2}%
            }%
          }%
        }%
        \dirkeys@pathlist
        \skvaftergroupdef\skvtry@pathlist\endgroup
      \fi
    \fi
  },
  % Prefix is used in making \skvtry@pathlist in .family:
  .ord/{.prefix}/\skvdefaultprefix/
      \skv@ifoneprefix{#1}{%
        \def\skvtry@pref{#1}
        \skvstripouterbraces{2}\skvtry@pref
      }{%
        \skv@err{Only one prefix is allowed in '.try set keys'}\skv@ehd
      }
  ,
  .ord/.prefixes//
    \ifskvindef\else
      \skv@err{Key '.prefixes' not defined; try '.paths'}\skv@ehd
    \fi
  ,
  .ord/{.family,.families}/\skv@nil/
    \edef\skvtry@fams{#1}
    \ifskvindef\else
      \ifx\skvtry@fams\skv@nnil
        \skv@err{No family specified for command
          \MessageBreak\string\skvtrysetkeys or handler '.try set keys'}\skv@ehd
      \fi
    \fi
    \skvstripouterbraces{2}\skvtry@fams
    \skv@makepaths\skvtry@pref\skvtry@fams\skvtry@pathlist\skvtry@pathdo
  ,
  .ord/{.ignore keys}//
    \def\skvtry@igkeys{#1}
    \ifx\skvtry@igkeys\@empty\else
      \skvstripouterbraces{2}\skvtry@igkeys
    \fi
  ,
  .ord/{.save known keys in,.success list,.success list macro}/
    \skvsuccesslist/
    \skviflacus#1\then
      \def\skvtry@knownkeyslistmacro{\skvsuccesslist}%
    \else
      \def\skvtry@knownkeyslistmacro{#1}
    \fi
    \skvstripouterbraces{2}\skvtry@knownkeyslistmacro
  ,
  .ord/{.save unknown keys in,.fail list,.fail list macro}/
    \skvfaillist/
    \skviflacus#1\then
      \def\skvtry@unknownkeyslistmacro{\skvfaillist}%
    \else
      \def\skvtry@unknownkeyslistmacro{#1}
    \fi
    \skvstripouterbraces{2}\skvtry@unknownkeyslistmacro
  ,
  % Exception on each path, if key isn't found:
  .ord/{.if any path fails,.if a path fails,.if path fails}//
    \edef\skvtry@pathexception{\unexpanded{#1}}
    \skvstripouterbraces{1}\skvtry@pathexception
  ,
  % Exception to be triggered after all paths have been searched and the key
  % isn't found:
  .ord/{.if hits less than target,.if hits less than goal,
    .if hits are less than goal}//
    \edef\skvtry@hitsexception{\unexpanded{#1}}
    \skvstripouterbraces{1}\skvtry@hitsexception
  ,
}

% \skvtrysetkeys[<options>]{<kvlist>}
\skvrobustdef*\skvtrysetkeys{\skv@testopt\skv@trysetkeys{}}
\skvrobustdef*\skv@trysetkeys[#1]#2{%
  \skvpushfunctions\skvtrysetkeys{%
    \do\skvtry@maxattempts\do\skvtry@hitsexception\do\skvtry@pathexception
    \do\skvtry@minsuccess\do\skvtry@maxsuccess\do\skvtry@igkeys
    \do\skvtry@knownkeyslistmacro\do\skv@tempknownkeys
    \do\skvtry@unknownkeyslistmacro\do\skv@tempunknownkeys
    \do\skv@triedlist\do\CurrentOption\do\skvtry@pref\do\skvtry@fams
    \do\skvtry@pathlist\do\skv@pathdo\do\skvcurrentkey\do\skvcurrentpath
    \do\skvcurrentprefix\do\skvcurrentfamily
  }\skv@trysetkeysdepth
  % Initialize \skvtry@pathlist; see the definition of the keys of
  % \skvtrysetkeys above.
  \def\skvtry@pathlist{}%
  \skvsetkeys[SKV]{skvtryset}{#1}%
  \ifx\skvtry@pathlist\@empty
    \skv@err{No families in which to 'try' to set keys}\skv@ehd
  \fi
  % To avoid repeated computation of \skv@currentnames in \skv@setkeys@a
  % for each key in the next loop:
  \skv@getnamesofkeys{#2}\skv@currentnames
  \expandafter\def\skvtry@knownkeyslistmacro{}%
  \expandafter\def\skvtry@unknownkeyslistmacro{}%
  \skvemptify{\skv@tempknownkeys,\skv@tempunknownkeys,\skv@triedlist}%
  \skv@intrytrue
  \skvcommaparse{#2}\CurrentOption{%
    \expandafter\skv@g@tkeyname\CurrentOption=\skv@getname@nil\skvcurrentkey
    \skvifemptyTF\skvtry@igkeys{%
      \@firstofone
    }{%
      \skvxifinTF{,\skvcurrentkey,}{,\skvtry@igkeys,}{}%
    }{%
      % \skv@triedlist saves non-ignored list:
      \edef\skv@triedlist{\skvaddlist,\skv@triedlist\skvcurrentkey}%
      \skvpushfunctions\skv@trysetkeys@istate{%
        \do\CurrentOption\do\skvcurrentkey\do\skv@attempts
        \do\skv@hits\do\skv@failedattempts\do\ifskv@success\do\skvtry@pathdo
      }\skv@trysetkeysidepth
      \skv@successfalse
      \skvinizero{\skv@attempts,\skv@hits,\skv@failedattempts}%
      \def\skvtry@pathdo##1##2{%
        \edef\skv@fams{##2}%
        \skv@makeprefix{##1}%
        % \ifskv@st is 'false' because we always save unknown keys while
        % in .try; and the branch for \ifskv@st is never taken when
        % in .try (see \skv@setkeys@d).
        \skv@stfalse\skv@plfalse\skv@viarootofsetkeysfalse
        \skvexpbracenext{\skv@setkeys@a[]}\CurrentOption
        \edef\skv@attempts{\the\numexpr\skv@attempts+1}%
        \ifskv@success
          \edef\skv@hits{\the\numexpr\skv@hits+1}%
          \edef\skv@tempknownkeys{%
            \skvaddlist,\skv@tempknownkeys\skvcurrenttriple
          }%
        \else
          % \skv@failedattempts is currently unused.
          \edef\skv@failedattempts{\the\numexpr\skv@failedattempts+1}%
          \edef\skv@tempunknownkeys{%
            \skvaddlist,\skv@tempunknownkeys\skvcurrenttriple
          }%
          \expandafter\expandafter\expandafter\skvtry@pathexception
        \fi
        \ifnum\skv@hits<\skvtry@maxsuccess\relax
          \ifnum\skv@attempts<\skvtry@maxattempts\relax\else
            \let\skvtry@pathdo\@gobbletwo
          \fi
        \else
          \let\skvtry@pathdo\@gobbletwo
        \fi
      }%
      \skvtry@pathlist
      \ifnum\skv@hits<\skvtry@minsuccess\relax
        \ifx\skv@triedlist\@empty
          \skv@err{Empty try list; all keys were probably ignored}\skv@ehd
        \else
          \ifx\skvtry@hitsexception\@empty
            \skv@err{Success target '\skvtry@minsuccess' wasn't reached
              \MessageBreak for key '\skvcurrentkey' by
              \ifskv@insa'.search also set'\else
              '.try set keys' or \noexpand\skvtrysetkeys\fi.
              \MessageBreak Actual hits were '\skv@hits'}\skv@ehd
          \else
            \expandafter\expandafter\expandafter\expandafter\expandafter
            \expandafter\expandafter\skvtry@hitsexception
          \fi
        \fi
      \fi
      \skvpopfunctions\skv@trysetkeys@istate\skv@trysetkeysidepth
    }%
  }%
  \ifx\skv@triedlist\@empty
    \skv@warn{Empty try list in '.try set keys';
      \MessageBreak all keys were probably ignored}\skv@ehd
  \fi
  \expandafter\let\skvtry@knownkeyslistmacro\skv@tempknownkeys
  \expandafter\let\skvtry@unknownkeyslistmacro\skv@tempunknownkeys
  \ifnum\skv@trysetkeysdepth=\@ne
    \skv@intryfalse\skv@insafalse
    \let\do\skvundef
    \do\skv@triedlist\do\skv@attempts\do\skv@hits\do\skv@failedattempts
    \do\skv@tempknownkeys\do\skv@tempunknownkeys
  \fi
  \skvpopfunctions\skvtrysetkeys\skv@trysetkeysdepth
}
% In \directkeys, \dirkeys@trysetkeys will be called instead of \skvtrysetkeys:
\skvrobustdef*\dirkeys@trysetkeys#1{%
  \begingroup
  \@tempcnta\skvz@
  \def\skv@tempc{}\def\skv@tempd{}%
  % Prepare to preserve outer brace in key value:
  \def\skv@tempa##1={%
    \begingroup
    \let\bgroup\relax
    \@ifnextchar\skv@orig@bgroup{%
      \endgroup\@tempswatrue\skv@tempb##1=%
    }{%
      \endgroup\@tempswafalse\skv@tempb##1=%
    }%
  }%
  % Because of the need to preserve outer braces in key values, don't
  % use \skv@kvsplit here.
  \def\skv@tempb##1=##2=##3\skv@nil{%
    \skvxifinTF{,##1,}{,.key values,.kv,.kv list,}{%
      \advance\@tempcnta\@ne
      \skvifnumTF\@tempcnta>\@ne{%
        \skv@err{Multiple entries for key
          \MessageBreak '.key values' or '.kv' or '.kv list'}\skv@ehd
      }{%
        \edef\skv@tempd{\skvifstrcmpTF{##2}{^skv^}{}{\unexpanded{##2}}}%
      }%
    }{%
      \edef\skv@tempc{%
        \skvaddlist,\skv@tempc##1%
        \skvifstrcmpTF{##2}{^skv^}{}{%
          =\if@tempswa{\fi\unexpanded{##2}\if@tempswa}\fi
        }%
      }%
    }%
  }%
  \skvkvparse{#1}\skv@prova{%
    \expandafter\skv@tempa\skv@prova=^skv^=\skv@nil
  }%
  \ifx\skv@tempd\@empty
    \skv@err{No key-value pairs for handler '.try set keys'}\skv@ehd
  \fi
  \skvexpanded{\endgroup
    \noexpand\skv@trysetkeys[\skvexpandonce\skv@tempc]%
      {\skvexpandonce\skv@tempd}%
  }%
}

%%+++++++++++++++++++++++++ 'search also' set keys +++++++++++++++++++%%
%
% \skvsasetkeys[<options>]{<keyvals>}
% .sa set keys={<options>}
%
% When using '.sa set keys', the key '.key values' must be part of
% <options>.
%
% 1. The only difference between \skvsasetkeys and \skvtrysetkeys is that,
%    when in \directkeys, the values of the option 'paths' of \skvsasetkeys
%    are added to the current active paths from the lists
%    \dirkeys@pathlist. 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@sasetkeys#1{%
  \skv@insatrue
  \dirkeys@trysetkeys{#1}%
}
% \dirkeys@addpaths{<paths>}
% Elements of <path> will have the form '{<pref>}/{<fam>}'
\skvrobustdef*\dirkeys@addpaths#1{%
  \skv@formatpaths{#1}\dirkeys@pathlist
}
% \dirkeys@removepaths{<list>}
\skvrobustdef*\dirkeys@removepaths#1{%
  \begingroup
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvcsvnormalize[,]\skv@tempa
  \skvcsvnormalize[/]\skv@tempa
  \def\skv@tempb{}%
  \def\skv@pathdo##1##2{%
    \skvxifinTF{,\detokenize{##1/##2},}{,\skvoxdetok\skv@tempa,}{}{%
      \edef\skv@tempb{%
        \skvexpandonce\skv@tempb
        \noexpand\skv@pathdo{##1}{##2}%
      }%
    }%
  }%
  \dirkeys@pathlist
  \let\dirkeys@pathlist\skv@tempb
  \skvaftergroupdef\dirkeys@pathlist\endgroup
}
% \dirkeys@removefams{<list>}
\skvrobustdef*\dirkeys@removefams#1{%
  \begingroup
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvcsvnormalize\skv@tempa
  \def\skv@tempb{}%
  \def\skv@pathdo##1##2{%
    \skvxifinTF{,\detokenize{##2},}{,\skvoxdetok\skv@tempa,}{}{%
      \edef\skv@tempb{%
        \skvexpandonce\skv@tempb
        \noexpand\skv@pathdo{##1}{##2}%
      }%
    }%
  }%
  \dirkeys@pathlist
  \let\dirkeys@pathlist\skv@tempb
  \skvaftergroupdef\dirkeys@pathlist\endgroup
}
% \dirkeys@collectpaths{<pref.macro>}{<fam.macro>}{<ig.macro>}
% Collect current prefixes in <pref.macro>, families in <fam.macro> and
% ignored keys in <ig.macro>.
\skvrobustdef*\dirkeys@collectpaths#1#2#3{%
  \begingroup
  \skvemptify{#1,#2,#3}%
  \def\skv@pathdo##1##2{%
    \skvxifinTF{,##1,}{,#1,}{}{\edef#1{\skvaddlist,#1##1}}%
    \skvxifinTF{,##2,}{,#2,}{}{\edef#2{\skvaddlist,#2##2}}%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \edef#3{\skvaddlist,#3\skv@igkeys}%
  }%
  \dirkeys@pathlist
  \let\do\skvcmdexit
  \skvexpanded{\endgroup\do#1\do#2\do#3}%
}
% Remove leading and trailing slashes in #1.
\skvrobustdef*\skv@trimslashes#1{%
  \begingroup
  \def\skv@tempa{%
    \skvxifinFT{\skvrelax/}{\skvrelax#1}{}{%
      \def\do/##1\skv@nil{\def#1{##1}}%
      \expandafter\do#1\skv@nil
      \skv@tempa
    }%
  }%
  \skv@tempa
  \def\skv@tempa{%
    \skvxifinFT{/\skvrelax}{#1\skvrelax}{}{%
      \def\do##1/\skv@nil{\def#1{##1}}%
      \expandafter\do#1\skv@nil
      \skv@tempa
    }%
  }%
  \skv@tempa
  \skvaftergroupdef#1\endgroup
}

% \dirkeys@addtocodeorvalue{app.or.pre}{cbk.or.value}{<list>}
% <list> has the form <key1>={<value1>},...,<key-n>={<value-n>}
\skvrobustdef*\dirkeys@addtocodeorvalue#1#2#3{%
  \begingroup
  % Hid the list from the parameters of \skv@pathdo:
  \edef\skv@tempa{\unexpanded{#3}}%
  \def\skv@tempb{}%
  % ##1=prefix, ##2=family
  \def\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvkvparse*\skv@tempa\skv@tempa{%
      \skv@okvsplit\skv@tempa{%
        \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{%
          \skvletcs\skv@prova{##1/##2/####1.@#2}%
          \skvifdefFT\skv@prova{%
            \skv@err{Key '####1' isn't in family
              \MessageBreak '##1/##2'}\skv@ehd
          }{%
            \skvcsedef{##1/##2/####1.@#2}{%
              \skvifstrcmpTF{#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@pathlist
  \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\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvkvparse*\skv@tempa\skv@tempa{%
      \expandafter\skv@kvsplit\expandafter{\skv@tempa}{%
        \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{%
          \skvifcsdefTF{##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'}\skv@ehd
          }%
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
  \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\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvkvparse*\skv@tempa\skv@tempa{%
      \expandafter\skv@kvsplit\expandafter{\skv@tempa}{%
        \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{%
          \skvifcsdefTF{##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'}\skv@ehd
          }%
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
  \skvexpanded{\endgroup\skv@tempb}%
}

% \dirkeys@inheritproperty{value.or.cbk.or.defa}{<list>}
\skvrobustdef*\dirkeys@inheritproperty#1#2{%
  \begingroup
  \def\skv@tempb{}%
  % Hid the list from the parameters of \do:
  \edef\skv@tempa{\unexpanded{#2}}%
  % ##1=prefix, ##2=family
  \def\skv@pathdo##1##2{%
    \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}%
    \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}%
    \skvkvparse*\skv@tempa\skv@tempa{%
      \skv@oslashsplit\skv@tempa{%
        % \skv@oslashsplit may return ^skv^ for ####1 or ####2, but
        % this doesn't matter here, since they will show that the keys
        % aren't defined.
        \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{%
          \ifcase\skvifcsdefTF{##1/##2/####1.@cbk}{0}{1}%
            \skvifcsdefTF{##1/##2/####2.@cbk}{0}{1}\relax
            \edef\skv@tempb{%
              \skvexpandonce\skv@tempb
              \skvcsletcs{##1/##2/####1.@#1}{##1/##2/####2.@#1}%
            }%
          \else
            \skv@err{Key '####1' or '####2' isn't in family '##1/##2'}\skv@ehd
          \fi
        }%
      }%
    }%
  }%
  \dirkeys@pathlist
  \expandafter\endgroup\skv@tempb
}

% \dirkeys@setbooleanhandler{<handler>}{<boolean>}{<bool.value>}
\skvrobustdef*\dirkeys@setbooleanhandler#1#2#3{%
  \skvifcsdefTF{if#2}{%
    \skvifblankTF{#3}{%
      \csname#2true\endcsname
    }{%
      \skvifboolvalTF{#3}{%
        \csname#2#3\endcsname
      }{%
        \skv@err{Invalid value '\detokenize{#3}' for handler '#1':
          \MessageBreak its expects either 'true' or 'false'}\skv@ehd
      }%
    }%
  }{%
    \skv@err{No boolean '\detokenize{#2}'}\skv@ehd
  }%
}

\skvrobustdef*\skv@ensurehandlerdot#1{%
  \if.\skv@car@detok#1x\car@nil\else
    \skv@err{A dot (.) must lead handler name '#1'}\skv@ehd
  \fi
}
\skvrobustdef*\dirkeys@handlerloop#1{%
  \edef\skv@tempa{\unexpanded{#1}}%
  \skvstripouterbraces{2}\skv@tempa
  \skvkvparse*\skv@tempa
}
\skvnewdef*\dirkeys@reservedhandlers{}
\skvrobustdef*\skv@reservedhandlererr{%
  \skv@err{Handler '##1' is reserved.
    \MessageBreak Reserved handlers can't be redefined whatsoever}\skv@ehd
}
\skvrobustdef*\dirkeys@defhandlers@aux#1{%
  \skvxifinTF{,#1,}{,\dirkeys@reservedhandlers,}{%
    \skv@reservedhandlererr
  }{%
    \ifskv@inreservedhandler
      \edef\dirkeys@reservedhandlers{%
        \dirkeys@reservedhandlers
        \ifx\dirkeys@reservedhandlers\@empty\else,\fi#1%
      }%
    \fi
    \edef\skv@collection{%
      \skvexpandonce\skv@collection
      \protected\skvcsdef{dirkeys@handler@#1}\skvxonce\skv@param{%
        % We can call \skvcurrenthandler in \skv@body, but when the
        % handler is cast into another handler, the inheriting handler
        % will not be the one in \skvcurrenthandler.
        \def\noexpand\skvcurrenthandler{#1}%
        \skvexpandonce\skv@body
      }%
    }%
  }%
}
% \dirkeys@defhandlers{<flag>}{<list>}
%
% <flag> indicates if we're in <new> (01) or <define> (00) handler.
%
\skvrobustdef*\dirkeys@defhandlers#1#2{%
  \begingroup
  \def\skv@collection{}%
  \def\skv@param{####1\skvhandlereov}%
  \def\skv@defhandlers@do##1{%
    \skv@ensurehandlerdot{##1}%
    \skvifplaswTF{#1}{%
      \dirkeys@defhandlers@aux{##1}%
    }{%
      \skvifcsdefTF{dirkeys@handler@##1}{%
        \skv@err{Handler '##1' arealdy exists}\skv@ehd
      }{%
        \dirkeys@defhandlers@aux{##1}%
      }%
    }%
  }%
  \dirkeys@handlerloop{#2}\skv@prova{%
    \skv@okvsplit\skv@prova{%
      % Hide the parameter characters in body:
      \edef\skv@prova{\unexpanded{##1}}%
      \skvstripouterbraces{2}\skv@prova
      \edef\skv@body{\unexpanded{##2}}%
      \skvcommaparse*\skv@prova\skv@prova{%
        \expandafter\skv@defhandlers@do\expandafter{\skv@prova}%
      }%
    }%
  }%
  \skvexpanded{\endgroup
    \skvexpandonce\skv@collection
    \skvcmdexit\dirkeys@reservedhandlers
  }%
}
% \dirkeys@defnarghandlers{<flag>}{<list>}
%
% Define handlers of more than one argument. This is slightly more
% expensive than '.new handlers':
%
% <flag> indicates whether we're in <new> (01) or <define> (00) handler.
%
\skvrobustdef*\dirkeys@defnarghandlers#1#2{%
  \begingroup
  \def\skv@collection{}%
  \def\skv@defhandlers@do##1{%
    \skv@ensurehandlerdot{##1}%
    \def\skv@prova####1####2\handler@nil{%
      \edef\skv@param{\skvgenerateparameters{1}{####1}\skvhandlereov}%
      \edef\skv@body{\unexpanded{####2}}%
      \skvifplaswTF{#1}{%
        \dirkeys@defhandlers@aux{##1}%
      }{%
        \skvifcsdefTF{dirkeys@handler@##1}{%
          \skv@err{Handler '##1' arealdy exists}\skv@ehd
        }{%
          \dirkeys@defhandlers@aux{##1}%
        }%
      }%
    }%
    \expandafter\skv@prova\skv@provb\handler@nil
  }%
  \dirkeys@handlerloop{#2}\skv@prova{%
    \skv@okvsplit\skv@prova{%
      \edef\skv@prova{\unexpanded{##1}}%
      \skvstripouterbraces{2}\skv@prova
      % Here \skv@body is in \skv@defhandlers@do.
      \edef\skv@provb{\unexpanded{##2}}%
      \skvcommaparse*\skv@prova\skv@prova{%
        \expandafter\skv@defhandlers@do\expandafter{\skv@prova}%
      }%
    }%
  }%
  \skvexpanded{\endgroup
    \skvexpandonce\skv@collection
    \skvcmdexit\dirkeys@reservedhandlers
  }%
}
% \dirkeys@handlerlet{<flag>}{<kvlist>}
%
% 1. <flag> is either 00 (for define) or 01 (for new handler).
% 2. <kvlist> has the form
%
%       {<child handler list>}={<parent handler>}
%
\skvrobustdef*\dirkeys@handlerlet#1#2{%
  \begingroup
  \def\skv@collection{}%
  \def\skv@handlerlet@do##1##2{%
    \skvifblankTF{##1}{}{%
      \skvxifinTF{,##1,}{,\dirkeys@reservedhandlers,}{%
        \skv@reservedhandlererr
      }{%
        \ifskv@inreservedhandler
          \edef\dirkeys@reservedhandlers{%
            \dirkeys@reservedhandlers
            \ifx\dirkeys@reservedhandlers\@empty\else,\fi##1%
          }%
        \fi
        \skvifplaswTF{#1}{}{%
          \skvifnamedefFT{dirkeys@handler@##1}{}{%
            \skv@err{Handler '##1' arealdy exists}\skv@ehd
          }%
        }%
        \edef\skv@collection{%
          \skvexpandonce\skv@collection
          \skvcsletcs{dirkeys@handler@##1}{dirkeys@handler@##2}%
        }%
      }%
    }%
  }%
  \dirkeys@handlerloop{#2}\skv@tempa{%
    \skv@okvsplit\skv@tempa{%
      \skvifnamedefTF{dirkeys@handler@##2}{%
        \skvcommaparse{##1}\skv@prova{%
          \skvexpbracenext\skv@handlerlet@do\skv@prova{##2}%
        }%
      }{%
        \skv@err{Handler '##2' doesn't exist}\skv@ehd
      }%
    }%
  }%
  \expandafter\endgroup\skv@collection
}
\skvrobustdef*\skvusehandler#1#2{%
  \skv@ensurehandlerdot{#1}%
  \skvifnamedefTF{dirkeys@handler@#1}{%
    \csname dirkeys@handler@#1\endcsname#2\skvhandlereov
  }{%
    \skv@err{Handler '#1' is undefined}\skv@ehd
  }%
}

% The macro that contains the default command in which unknown keys
% will be saved by some handlers of \directkeys:
\skvnewdef*\dirkeys@unknownkeysmacro{\skvfaillist}

% Notes:
% 1.  '.new reserved handlers' must be defined outsied \directkeys,
%     since we have used it here to 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 \directkeys).
%
\skvrobustcsdef*{dirkeys@handler@.new reserved handlers}#1\skvhandlereov{%
  \skv@inreservedhandlertrue
  \dirkeys@defhandlers{01}{#1}%
  \skv@inreservedhandlerfalse
}
\directkeys*{%
  % Defining new handlers that can't be overloaded:
  .new reserved handlers={
    % '.new reserved handlers' and '.define reserved handlers' are the
    % same thing since reserved handlers can't be overridden or overloaded.
    {.new reserved handler,.define reserved handlers,
      .define reserved handler}={
      \skvusehandler{.new reserved handlers}{#1}
    },
    {.exec,.exec code}={#1},
    {.define handler,.define handlers}={
      \dirkeys@defhandlers{00}{#1}
    },
    {.new handler,.new handlers}={
      \dirkeys@defhandlers{01}{#1}
    },
    {.define narg handler,.define narg handlers}={
      \dirkeys@defnarghandlers{00}{#1}
    },
    {.new narg handler,.new narg handlers}={
      \dirkeys@defnarghandlers{01}{#1}
    },
    % See comment above about '.new reserved handlers':
    {.new reserved narg handler,.new reserved narg handlers,
      .define reserved narg handler,.define reserved narg handlers}={
      \skv@inreservedhandlertrue
      \dirkeys@defnarghandlers{01}{#1}
      \skv@inreservedhandlerfalse
    },
    {.reserve handlers,.reserve handler}={
      \edef\skv@prova{\unexpanded{#1}}%
      \skvstripouterbraces{2}\skv@prova
      \skvcsvnormalize\skv@prova
      \edef\dirkeys@reservedhandlers{
        \dirkeys@reservedhandlers
        \ifx\dirkeys@reservedhandlers\@empty\else,\fi\skv@prova
      }
    },
    {.kill handler,.kill handlers}={
      \dirkeys@handlerloop{#1}\skv@tempa{
        \skvifcsdefTF{dirkeys@handler@\skv@tempa}{
          \skvcsundef{dirkeys@handler@\skv@tempa}
        }{
          \skv@err{Handler '\skv@tempa' is undefined}\skv@ehd
        }
      }
    },
    .use handler=\skvusehandler{#1},
    % Allow override:
    {.handler let*,.handlers let*,.let handler*,.let handlers*,
      .force handler let}={
      \dirkeys@handlerlet{00}{#1}
    },
    % Disallow override:
    {.handler let,.handlers let,.let handler,.let handlers}={
      \dirkeys@handlerlet{01}{#1}
    },
    % Let and reserved handlers:
    {.handler let reserved,.handlers let reserved}={
      \skv@inreservedhandlertrue
      \dirkeys@handlerlet{01}{#1}
      \skv@inreservedhandlerfalse
    },
    {.paths,.path,.change path,.change paths,.cd}={
      \def\dirkeys@pathlist{}\dirkeys@addpaths{#1}
    },
    {.add path,.add paths}={
      \dirkeys@addpaths{#1}
    },
    {.ignore path,.ignore paths}={
      \dirkeys@removepaths{#1}
    },
    {.restore path,.restore paths}={
      \dirkeys@addpaths{#1}
    },
    % Prefix is used in building \dirkeys@pathlist in '.family' handler.
    % There must be only one current prefix. Use .path to submit multiple
    % prefixes and families.
    {.prefix,.change prefix}={
      \skv@ifoneprefix{#1}{
        \edef\dirkeys@pref{#1}
        \skvstripouterbraces{2}\dirkeys@pref
      }{
        \skv@err{List of prefixes isn't allowed for handler
          \MessageBreak '.prefix' in \string\directkeys. You can use
          \MessageBreak '.paths' to submit multiple prefixes and
          \MessageBreak families}\skv@ehd
      }
    },
    % Since there can be only one prefix, you couldn't ignore it.
    {.prefixes,.change prefixes,.ignore prefix,.ignore prefixes}={
      \skv@err{No handler '\skvcurrenthandler'}\skv@ehd
    },
    % See .change path for .cd:
    {.families,.family,.change family,.change families,.cf}={
      \edef\dirkeys@fams{#1}
      \skvstripouterbraces{2}\dirkeys@fams
      \def\dirkeys@pathlist{}%
      \skv@makepaths\dirkeys@pref\dirkeys@fams\dirkeys@pathlist\skv@pathdo
    },
    {.add family,.add families}={
      \edef\dirkeys@fams{#1}
      \skvstripouterbraces{2}\dirkeys@fams
      \skv@makepaths\dirkeys@pref\dirkeys@fams\dirkeys@pathlist\skv@pathdo
    },
    {.ignore family,.ignore families}={
      \dirkeys@removefams{#1}
    },
    {.restore family,.restore families}={
      \skvusehandler{.add family}{#1}
    },
    {.ignore key,.ignore keys}={
      \skv@pushpathdo
      \def\skv@pathdo##1##2{
        \expandafter\skvmergelist\csname##1/##2/.@ignoredkeys\endcsname{#1}
      }
      \dirkeys@pathlist
      \skv@poppathdo
    },
    {.restore key,.restore keys}={
      \skv@pushpathdo
      \def\skv@pathdo##1##2{
        \expandafter\skvremoveelements
          \csname##1/##2/.@ignoredkeys\endcsname{#1}
      }
      \dirkeys@pathlist
      \skv@poppathdo
    },
    {.holder prefix,.hp}={
      \def\dirkeys@holderprefixtoks{#1}
    },
    % There is no need pushing prefix or family, since they're made
    % into paths by their key callbacks.
    {.push path,.push paths}={
      \dirkeys@pushmeta{path}
    },
    {.pop path,.pop paths}={
      \dirkeys@popmeta{path}
    },
    {.push holder prefix,.push hp}={
      \dirkeys@pushmeta{hp}
    },
    {.pop holder prefix,.pop hp}={
      \dirkeys@popmeta{hp}
    },
    .save unknown keys={
      \dirkeys@setbooleanhandler
        {.save unknown keys}{dirkeys@saveunknownkeys}{#1}
    },
    .save unknown keys in={
      \skvifblankTF{#1}{%
        \skv@err{Empty value for handler '.save unknown keys in'}\skv@ehd
      }{
        \skvifescapedTF{#1}{
          \dirkeys@saveunknownkeystrue
          \def\dirkeys@unknownkeysmacro{#1}
        }{
          \skv@err{Token '\detokenize{#1}' is not escaped}\skv@ehd
        }
      }
    },
    {.define key,.define keys}={
      \dirkeys@dodefinekeys{}{#1}
    },
    {.new key,.new keys}={
      \dirkeys@dodefinekeys{*}{#1}
    },
    {.define ordinary key,.define ordinary keys,.define ord keys}={
      \dirkeys@definetypekeys{ord}{}{#1}
    },
    {.new ordinary key,.new ordinary keys,.new ord keys}={
      \dirkeys@definetypekeys{ord}{*}{#1}
    },
    {.define command key,.define command keys,.define cmd keys}={
      \dirkeys@definetypekeys{cmd}{}{#1}
    },
    {.new command key,.new command keys,.new cmd keys}={
      \dirkeys@definetypekeys{cmd}{*}{#1}
    },
    {.define boolean key,.define boolean keys,.define bool keys}={
      \dirkeys@definetypekeys{bool}{}{#1}
    },
    {.new boolean key,.new boolean keys,.new bool keys}={
      \dirkeys@definetypekeys{bool}{*}{#1}
    },
    {.define toggle key,.define toggle keys,.define tog keys}={
      \dirkeys@definetypekeys{bool}{}{#1}
    },
    {.new toggle key,.new toggle keys,.new tog keys}={
      \dirkeys@definetypekeys{bool}{*}{#1}
    },
    {.define choice key,.define choice keys}={
      \dirkeys@definetypekeys{choice}{}{#1}
    },
    {.new choice key,.new choice keys}={
      \dirkeys@definetypekeys{choice}{*}{#1}
    },
    {.initialize keys after define,.initialize after define}={
      \dirkeys@setbooleanhandler
        {.initialize keys after define}{skvdfk@initialize}{#1}
    },
    {.save initial values of keys,.save initial values}={
      \dirkeys@setbooleanhandler
        {.save initial values of keys}{skvdfk@saveinitialvalues}{#1}
    },
    {.set keys,.setkeys,.set}={
      \dirkeys@setkeys{}{}\skvsetkeys{#1}
    },
    {.set keys*,.setkeys*,.set*,.setkeys save unknown,
     .set keys save unknown}={
      \dirkeys@setkeys{*}{}\skvsetkeys{#1}
    },
    {.set rmkeys,.setrmkeys,.set remaining keys}={
      \skvifblankTF{#1}{
        \dirkeys@setkeys{}{rm}\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'}\skv@ehd
      }
    },
    {.set rmkeys*,.setrmkeys*,.set remaining keys*}={
      \skvifblankTF{#1}{
        \dirkeys@setkeys{*}{rm}\skvsetrmkeys{}
      }{
        \skv@err{Key 'set rmkeys*' can't have value:
        \MessageBreak Instead use the handler '.ignore keys' to suggest keys
        \MessageBreak to be ignored when setting rmkeys}\skv@ehd
      }
    },
    % '.try set keys' doesn't use the active prefixes and families within
    % \directkeys. The prefix, families and other options are to be supplied
    % in the argument #1. The key .key values (or .kv) is used to
    % supply the key-value pairs.
    % .try set keys={<options>}
    {.try set keys,.try set,.try}={
      \dirkeys@trysetkeys{#1}
    },
    % 'search also' set keys.
    % .sa set keys={<options>}
    {.search also set keys,.sa set keys}={
      \dirkeys@sasetkeys{#1}
    },
    {.presetkeys,.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
    },
    {.postsetkeys,.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
    },
    .option keys={
      \dirkeys@dolistedkeys@a{#1}\skvoptionkeys
    },
    .goption keys={
      \dirkeys@dolistedkeys@a{#1}\skvgoptionkeys
    },
    .remove option keys={
      \dirkeys@dolistedkeys@a{#1}\skvremoveoptionkeys
    },
    .gremove option keys={
      \dirkeys@dolistedkeys@a{#1}\skvgremoveoptionkeys
    },
    .nonoption keys={
      \dirkeys@dolistedkeys@a{#1}\skvnonoptionkeys
    },
    .gnonoption keys={
      \dirkeys@dolistedkeys@a{#1}\skvgnonoptionkeys
    },
    .remove nonoption keys={
      \dirkeys@dolistedkeys@a{#1}\skvremovenonoptionkeys
    },
    .gremove nonoption keys={
      \dirkeys@dolistedkeys@a{#1}\skvgremovenonoptionkeys
    },
    .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,.disable key}={
      \dirkeys@dolistedkeys@a{#1}\skvdisablekeys
    },
    {.disabled key message type,.disabled key exception 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>}}
    {.state pattern,.choice,.nominations}={
      \dirkeys@savechoice{#1}{x0}
    },
    .state pattern expand once={
      \dirkeys@savechoice{#1}{x1}
    },
    .state pattern expand twice={
      \dirkeys@savechoice{#1}{x2}
    },
    {.state pattern expanded,.estate pattern,.enominations,.echoice}={
      \dirkeys@savechoice{#1}{xx}
    },
    % {<key-i>,...,<key-j>}/{<slotlist-ij>},...,
    %    {<key-n>,...,<key-m>}/{<slotlist-nm>}:
    {.append slot,.append slots,.slot,.slots,.style,.styles,.observers}={
      \dirkeys@saveslots{#1}{app}{x0}
    },
    {.prepend slot,.prepend slots}={
      \dirkeys@saveslots{#1}{prep}{x0}
    },
    {.append slot value expanded,.append slot expanded,
      .append slots value expanded,.append slots expanded,
      .eslot,.eslots,.estyle,.estyles}={
      \dirkeys@saveslots{#1}{app}{xx}
    },
    {.prepend slot value expanded,.prepend slot expanded,
      .prepend slots value expanded,.prepend slots expanded}={
      \dirkeys@saveslots{#1}{prep}{xx}
    },
    {.append slot expand value once,.append slot expand once,
      .append slots expand value once,.append slots expand once,
      .slot expand once,.slots expand once,.style expand once,
      .styles expand once}={
      \dirkeys@saveslots{#1}{app}{x1}
    },
    {.prepend slot expand value once,.prepend slot expand once,
      .prepend slots expand value once,.prepend slots expand once}={
      \dirkeys@saveslots{#1}{prep}{x1}
    },
    {.append slot expand value twice,.append slot expand twice,
      .append slots expand value twice,.append slots expand twice,
      .slot expand twice,.slots expand twice,.style expand twice,
      .styles expand twice}={
      \dirkeys@saveslots{#1}{app}{x2}
    },
    {.prepend slot expand value twice,.prepend slot expand twice,
      .prepend slots expand value twice,.prepend slots expand twice}={
      \dirkeys@saveslots{#1}{prep}{x2}
    },
    % {<linklist-1>}/<mainkeylist-1>,...,{<linklist-n>}/<mainkeylist-n>:
    {.append link,.append links,.link,.links}={
      \dirkeys@savelinks{#1}{app}{x0}
    },
    {.prepend link,.prepend links}={
      \dirkeys@savelinks{#1}{prep}{x0}
    },
    {.append link expanded value,.append link expanded,
      .append links expanded value,.append links expanded,.elink,.elinks}={
      \dirkeys@savelinks{#1}{app}{xx}
    },
    {.prepend link expanded value,.prepend link expanded,
      .prepend links expanded value,.prepend links expanded}={
      \dirkeys@savelinks{#1}{prep}{xx}
    },
    {.append link expand value once,.append link expand once,
      .append links expand value once,.append links expand once,
      .link expand once,.links expand once}={
      \dirkeys@savelinks{#1}{app}{x1}
    },
    {.prepend link expand value once,.prepend link expand once,
      .prepend links expand value once,.prepend links expand once}={
      \dirkeys@savelinks{#1}{prep}{x1}
    },
    {.append link expand value twice,.append link expand twice,
      .append links expand value twice,.append links expand twice,
      .link expand twice,.links expand twice}={
      \dirkeys@savelinks{#1}{app}{x2}
    },
    {.prepend link expand value twice,.prepend link expand twice,
      .prepend links expand value twice,.prepend links expand twice}={
      \dirkeys@savelinks{#1}{prep}{x2}
    },
    % .store value in={key1,key2}/<cmd>:
    .store value in={
      \dirkeys@savestores{#1}{x0}
    },
    {.store expanded value in,.store evalue in}={
      \dirkeys@savestores{#1}{xx}
    },
    .store expanded once value in={
      \dirkeys@savestores{#1}{x1}
    },
    .store expanded twice value in={
      \dirkeys@savestores{#1}{x2}
    },
    {.list break,.listbreak,.break list}={
      \skvbreaklooptrue
    },
    {.arg,.args}={
      \dirkeys@assignarg{#1}{x0}
    },
    % Expand arguments of keys at key setting time, not at key definition:
    {.arg expanded,.args expanded}={
      \dirkeys@assignarg{#1}{xx}
    },
    {.arg expand once,.args expand once}={
      \dirkeys@assignarg{#1}{x1}
    },
    {.arg expand twice,.args expand twice}={
      \dirkeys@assignarg{#1}{x2}
    },
    {.use defaults,.set with defaults}={
      \dirkeys@dolistedkeys@a{#1}\dirkeys@setwithdefaults
    },
    {.unknown key handler,.unknown option handler}={
      \dirkeys@setunknownkeyhandler{#1}
    },
    % Redefine the default value macros of keys.
    % .default values={keya=vala,...,keyn=valn}
    {.default value,.default values,.default,.defaults}={
      \dirkeys@assigndefault{#1}
    },
    % Change the current values of keys.
    % .assign values={keya=vala,...,keyn=valn}
    {.assign value,.assign values}={
      \dirkeys@assignvalue{#1}
    },
    {.inherit,.keys let,.key let}={
      \dirkeys@keyslet{#1}
    },
    % A key inherits value from a key.
    % .inherit values={keya1/keya2,...,keyn1/keyn2}
    {.inherit value,.inherit values}={
      \dirkeys@inheritproperty{value}{#1}
    },
    {.inherit callback,.inherit callbacks,.inherit code}={
      \dirkeys@inheritproperty{cbk}{#1}
    },
    {.inherit default,.inherit defaults}={
      \dirkeys@inheritproperty{defa}{#1}
    },
    % .prepend values={key1=val1,...,keyn=valn}
    {.prepend value,.prepend values}={
      \dirkeys@addtocodeorvalue{pre}{value}{#1}
    },
    {.append value,.append values}={
      \dirkeys@addtocodeorvalue{app}{value}{#1}
    },
    % .prepend code={key1=code1,...,keyn=coden}
    {.prepend code,.prepend codes}={
      \dirkeys@addtocodeorvalue{pre}{cbk}{#1}
    },
    {.append code,.append codes}={
      \dirkeys@addtocodeorvalue{app}{cbk}{#1}
    },
  },
  .new narg handlers={
    % All the parameters would have been despaced when '.no such key'
    % is called. #1=prefix, #2=family, #3=key, #4=value.
    .no such key={4}{
      \skvifcsdefTF{#1@#2@handler}{
        \csname#1@#2@handler\endcsname{#1}{#2}{#3}{#4}
      }{
        \skv@err{Key '#3' is not in family '#1/#2'}\skv@ehd
      }
    },
    .add slot={2}{
      \skvusehandler{.prepend slot}{#1}
      \skvusehandler{.append slot}{#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}{
      \skvifnamedefTF{#1/#2/#3.@value}{
        \skvifescapedTF{#4}{
          \skvletcs#4{#1/#2/#3.@value}
        }{
          \skv@err{Token '\detokenize{#4}' isn't escaped}\skv@ehd
        }
      }{
        \skv@err{Key '#3' isn't defined or has
          \MessageBreak no value in '#1/#2'}\skv@ehd
      }
    },
    % .set boolean handler={<handler>}{<boolean>}{<boolean.value>}
    %   See example use above.
    .set boolean handler={3}{
      \dirkeys@setbooleanhandler{#1}{#2}{#3}
    },
  },
  .handlers let reserved={
    {.disabled option message type,
      .message type when disabled key is invoked,
      .message type when disabled option is invoked
    }=.disabled key message type,
  },
}

% \skvpatchcmd[<prefix>]{<cmd>}{<search>}{<replace>}{<success>}{<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{%
  \skvifpatchableTF{#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*\skvifpatchableTF#1#2#3{%
  \endgroup
  \skvifdefFT{#1}{%
    \skv@patchdebug{--}{Command not defined}%
    \@secondoftwo
  }{%
    \skv@patchdebug{++}{Command is defined}%
    \skvifmacroTF{#1}{%
      \skv@patchdebug{++}{Command is a macro}%
      \skvifscannableTF{#1}{%
        \skv@patchdebug{++}{Macro can be retokenized safely after patching}%
        \skvifpatternTF{#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*\skvifscannableTF#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\skvifpatternTF#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}%
}
\edef\skv@begindoctoks{\string\begin\string{\string\document\string}}
\skvrobustdef*\skvAtBeginEnvironment#1{%
  \skvaftercs\skvappendtomacro{lps@atbegin@#1@hook}%
}
\skvrobustdef*\skvAtEndEnvironment#1{%
  \skvaftercs\skvappendtomacro{lps@atend@#1@hook}%
}

\skvpatchcmd\begin
  {\csname#1\endcsname}
  {\skvcsuse{lps@atbegin@#1@hook}\csname#1\endcsname}
  {}
  {\skv@err{Patching '\string\begin' failed!\MessageBreak
     '\string\skvAtBeginEnvironment' will not work\@gobble}\@ehd
  }

\skvpatchcmd\end
  {\csname end#1\endcsname}
  {\skvcsuse{lps@atend@#1@hook}\csname end#1\endcsname}
  {}
  {\skv@err{Patching '\string\end' failed!\MessageBreak
     '\string\skvAtEndEnvironment' will not work\@gobble}\@ehd
  }

\skv@core@restorecodes

\endinput

%% End of file 'skeyval-core.tex'.
