%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% % This is file 'skeyval-core.tex', version 1.3, 2013/05/15. % % % % This package and accompanying files may be distributed and/or % % modified under the conditions of the LaTeX Project Public License, % % either version 1.3 of this license or any later version. The latest % % version of this license is in http://www.latex-project.org/lppl.txt % % and version 1.3 or later is part of all distributions of LaTeX % % version 2005/12/01 or later. % % % % The LPPL maintenance status of this software is 'author-maintained'. % % % % This software is provided 'as it is', without warranty of any kind, % % either expressed or implied, including, but not limited to, the % % implied warranties of merchantability and fitness for a particular % % purpose. % % % % The following files constitute the skeyval bundle and must be % % distributed as a whole: % % % % README, skeyval.sty, skeyval-core.tex, skeyval-for.tex, % % skeyval-view.sty, skeyval-ltxpatch.sty, skeyval-ltxcmds.tex, % % skeyval-pstkey.sty, skeyval-pstkey.tex, skeyval-testclass.cls, % % skeyval-testpkg.sty, skeyval-pokayoke1, skeyval-pokayoke2, % % skeyval-view-pokayoke1. % % % % 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/05/15 v1.3: Core of 'skeyval' key-value parser (AM)} \input skeyval-ltxcmds \else \skv@latextrue \ProvidesFile{skeyval-core.tex} [2013/05/15 v1.3 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 \MessageBreak to proceed. If that doesn't work, type X then \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{}{}{<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{} \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{}{<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@temptoks,\skv@toksa} % 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 \let\skv@orig@egroup\egroup \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\skv@orig@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 (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 % \skvaddtotoks{}{} \skvrobustdef*\skvaddtotoks#1#2{#1\expandafter{\the#1#2}} \skvrobustdef*\skvoaddtotoks#1#2{#1\expandafter{\the\expandafter#1#2}} \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{}{}{} \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 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{% \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 \else \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{}{}{} \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 is not a valid boolean value, otherwise % execute #2: % \skvifboolvalT{}{} \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 have values of either 00 or 01. % \skvifknobTF, unlike \skvifswitchTF, is for use with plain switches, % ie, switches that have no separate namespace. \skvnewdef*\skvifknobTF#1{% \if\@nameuse{#1}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi } \skvnewdef*\skvifknobFT#1{% \if\@nameuse{#1}\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi } % Switches and toggles have separate namespaces. \skvrobustdef*\skvnewswitches#1{% \skvcommaloop{#1}\skv@prova{% \skvifcsdefTF{skv@switch@\skv@prova}{% \skv@err{Switch '\skvoxdetok\skv@prova' already exists}\skv@ehd }{% \skvcsdef{skv@switch@\skv@prova}{01}% }% }% } \skvnewlet\skvnewswitch\skvnewswitches \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{% \skvcommaloop{#1}\skv@prova{% \skvifcsdefTF{skv@toggle@\skv@prova}{% \skv@err{Switch '\skvoxdetok\skv@prova' already exists}\skv@ehd }{% \skvcslet{skv@toggle@\skv@prova}\@secondoftwo }% }% } \skvrobustdef*\skvdeftog#1{% \skvcommaloop{#1}\skv@prova{% \skvcslet{skv@toggle@\skv@prova}\@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 }% } % Allow 'f' to stop the search for number: \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{} % % 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\gdl...} \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{}{} \skvrobustdef*\skvsetnumber#1{% \begingroup \def\x{\edef\x{\endgroup\def\noexpand#1{\the\count@}}\x}% \afterassignment\x\count@ } \skvnewlet\skvsetno\skvsetnumber % \skvadvanceno{}{} \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{% \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{} % 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{}{} \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{}{} % 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{}{}{} % Here, has the form {/}. \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{}{}{} % Here, has the form {}{}. % 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{}{} % % Format given in the form % % {/},...,{/} % % into % % \skv@pathdo{}{}...\skv@pathdo{}{} % \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{}{}{} % {} % % Attach prefix to families, to make pathlist. 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{} \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{} \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} % Note: If there're more than 1 alpha/Alpha character, both \skvifloweralpha % and \skvifupperalpha will return false. Hence double (or more) aphabetic % characters can't be auto-completed by \skvautocompletelist. \skvnewdef*\skvifloweralpha#1{% \skvifblankTF{#1}{% \@secondoftwo }{% \skvifntypeTF{#1}{% \skvifnumTF`#1>96{% \skvifnumTF`#1<123{\@firstoftwo}{\@secondoftwo}% }{% \@secondoftwo }% }{% \@secondoftwo }% }% } \skvnewdef*\skvifupperalpha#1{% \skvifblankTF{#1}{% \@secondoftwo }{% \skvifntypeTF{#1}{% \skvifnumTF`#1>64{% \skvifnumTF`#1<91{\@firstoftwo}{\@secondoftwo}% }{% \@secondoftwo }% }{% \@secondoftwo }% }% } % 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 } \skvnewregister\bool{\ifskv@isinteger} % In case the argument of \skvifintegerTF containes \numexpr: \skvnewlet\skv@catch@nil\relax % \skvensureinteger{}{} \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} % 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}} % \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}{}{} : fails % \skvifdimexpr{1pt-2bp*3+(3cc-2ex)/2}{}{} : true % \skvifdimexpr{(1pt-2bp+(3cc-2ex)/2)*5}{}{} : true % \skvifdimexpr{1+(1pt-2bp+(3cc-2ex)/2)*5}{}{} : 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{} \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\skv@normal@nil{% \edef#2{\unexpanded{####1}}% \skv@trim@a }% \expandafter\skv@trim@b#2\skv@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 \skv@normal@nil}% } \skvnewdef*\skv@tsvnormalize#1 #2\skv@normal@nil{% \skvifblankTF{#2} {\expandafter\expandafter\expandafter\space \expandafter\noexpand\@gobble#1} {\skv@tsvnormalize#1#2\skv@normal@nil}% } % \skvnormalize{}{} \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 } \skvnewdef*\skv@switch@inkv{01} % \skvcsvnormalize[] \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{{,{},...} \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{} \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 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\skv@normal@nil{% \skv@parserequalerr##1##2,=\skv@normal@nil \edef#1{\skvtrimspace{##1##2}}% \skvtrimlastparser{,}#1% }% \expandafter\skv@kvnorm#1\skv@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~\skv@normal@nil}% }% \def\skv@activeparser##1~##2\skv@normal@nil{% \skvifblankTF{##2}{% \skvifdefswitchTF{inkv}{% \skv@activeequal##1!\skv@normal@nil }{% \skv@spaceparser##1 #1\skv@normal@nil }% }{% \skv@activeparser##1#1##2\skv@normal@nil }% }% \def\skv@activeequal##1!##2\skv@normal@nil{% \skvifblankTF{##2}{% \skv@spaceparser##1 #1\skv@normal@nil }{% \skv@activeequal##1=##2\skv@normal@nil }% }% }% \def\skv@spaceparser##1 #1##2\skv@normal@nil{% \skvifblankTF{##2}{% \skv@parserspace##1#1 \skv@normal@nil }{% \skv@spaceparser##1#1##2\skv@normal@nil }% }% \def\skv@parserspace##1#1 ##2\skv@normal@nil{% \skvifblankTF{##2}{% \skvifdefswitchTF{inkv}{% \skv@spaceequal##1 =\skv@normal@nil }{% \skv@doubleparser##1#1#1\skv@normal@nil }% }{% \skv@parserspace##1#1##2\skv@normal@nil }% }% \def\skv@spaceequal##1 =##2\skv@normal@nil{% \skvifblankTF{##2}{% \skv@equalspace##1= \skv@normal@nil }{% \skv@spaceequal##1=##2\skv@normal@nil }% }% \def\skv@equalspace##1= ##2\skv@normal@nil{% \skvifblankTF{##2}{% \skv@doubleparser##1#1#1\skv@normal@nil }{% \skv@equalspace##1=##2\skv@normal@nil }% }% \def\skv@doubleparser##1#1#1##2\skv@normal@nil{% \skvifblankTF{##2}{% \skvifdefswitchTF{inkv}{% \skv@doubleequal##1==\skv@normal@nil }{% \skvifblankTF{##1}{}{% \skv@remleadparser##1\skv@normal@nil }% }% }{% \skv@doubleparser##1#1##2\skv@normal@nil }% }% \def\skv@doubleequal##1==##2\skv@normal@nil{% \skvifblankTF{##2}{% \skvifblankTF{##1}{}{% \skv@remleadparser##1\skv@normal@nil }% }{% \skv@doubleequal##1=##2\skv@normal@nil }% }% \def\skv@remleadparser#1##1\skv@normal@nil{% \expandafter\space\noexpand##1% }% \def\skv@parserequalerr##1#1=##2\skv@normal@nil{% \skvifblankTF{##2}{}{% \skv@err{There is '#1=' in your key-value list}\skv@ehd }% }% } \endgroup % Define a special parser normalizer that doesn't replace double parsers % with single parser, because a double parser might mean an intentionally % missing token/property of a key: \begingroup \catcode`\~=13 \skvrobustgdef*\skvdefinekeepdoubleparsernormalizer#1#2{% \begingroup \uccode`\~=`#1 \uppercase{\endgroup \long\def#2##1{% \unexpanded\expandafter{\romannumeral-`\q \skv@keepdouble@activeparser##1~\skv@normal@nil}% }% \long\def\skv@keepdouble@activeparser##1~##2\skv@normal@nil{% \skvifblankTF{##2} {\skv@keepdouble@spaceparser##1 #1\skv@normal@nil} {\skv@keepdouble@activeparser##1#1##2\skv@normal@nil}% }% }% \long\def\skv@keepdouble@spaceparser##1 #1##2\skv@normal@nil{% \skvifblankTF{##2} {\skv@keepdouble@parserspace##1#1 \skv@normal@nil} {\skv@keepdouble@spaceparser##1#1##2\skv@normal@nil}% }% \long\def\skv@keepdouble@parserspace##1#1 ##2\skv@normal@nil{% \skvifblankTF{##2} {\skvifblankTF{##1}{}{\expandafter\space\noexpand##1}} {\skv@keepdouble@parserspace##1#1##2\skv@normal@nil}% }% } \endgroup \skvdefinekeepdoubleparsernormalizer{/}\skvkeepdoubleslashnormalize % \skvmacnormalize[] \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\skv@normal@nil}% }% \long\def\skv@mac@spaceparser##1 #1##2\skv@normal@nil{% \skvifblankTF{##2} {\skv@mac@parserspace##1#1 \skv@normal@nil} {\skv@mac@spaceparser##1#1##2\skv@normal@nil}% }% \long\def\skv@mac@parserspace##1#1 ##2\skv@normal@nil{% \skvifblankTF{##2} {\skv@mac@doubleparser##1#1#1\skv@normal@nil} {\skv@mac@parserspace##1#1##2\skv@normal@nil}% }% \long\def\skv@mac@doubleparser##1#1#1##2\skv@normal@nil{% \skvifblankTF{##2}{ ##1} {\skv@mac@doubleparser##1#1##2\skv@normal@nil}% }% } % \skvletmanytocmd{}{} \skvrobustdef*\skvletmanytocmd#1#2{% \skvcommaparse{#1}\skv@prova{% \expandafter\let\skv@prova= #2% }% } % \skvnewletmanytocmd{}{} \skvrobustdef*\skvnewletmanytocmd#1#2{% \skvcommaparse{#1}\skv@prova{% \expandafter\skv@newcastcommand\skv@prova=#2=\cast@nil }% } % \skvcastcommands{} % % -> {=,...,=} % % In , 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{} % % -> {=,...,=} % \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{} % % 1. 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 \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,tempsw,swa,verbose,intry,insa,success,inoptionsec,% inreservedhandler,ignorepointers,addslots@appending% } \skvnewbools{indirectkeys} \skvnewbools[skv]{indef,novalue,valuebraced} \skvnewbools[dirkeys@]{saveunknownkeys} % \skvnewcounts[]{} \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[]{} \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. % % \skvquickkeys.define{}[]{} % \skvquickkeys.set{}[]{} % % 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 is: % % {}/{}/{}/{}/{} % ;...; % {}/{}/{}/{}/{} % % 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 , 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: % \skvquickkeys.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{}{} \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*\skvquickkeys#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\skvquickkeys}\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}}% }% }% }% }% }% } % \skvquickkeys.preset{}{} % \skvrobustdef*\skv@presetquickkeys#1#2{% \edef\skv@prova{\unexpanded{#2}}% \skvkvnormalize\skv@prova \skvcslet{\skvtrimspace{#1}/qkeys.preset}\skv@prova } \skvnewnumbers[skv@]{setqkeysdepth} % \skvquickkeys.set{}[]{} \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{,\skvoxdetok\skv@tempval,}{,\skvoxdetok\reserved@a,}{}{% \skv@err{Value '\skvoxdetok\skv@tempval' 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\skvquickkeys.set' can't be nested in a preset \MessageBreak key's callback: this will cause a cyclic call \MessageBreak to '\string\skvquickkeys.set'}\skv@ehd } % \skvinitializequickkeys{}[]{} \skvrobustdef*\skvinitializequickkeys#1{% \skv@testopt{\skv@initializequickkeys@a{#1}}{}% } \skvrobustdef*\skv@initializequickkeys@a#1[#2]#3{% \skvindeftrue \skv@setquickkeys{#1}[#2]{#3}% \skvindeffalse } \input skeyval-for % \skvifcase{} % {} % \elsedo % {} % \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{} % {} % \elsedo % {} % \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\then % {} % {} % \fi % % 1. {} 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\skvxappto#1#2{\edef#1{\skvexpandonce{#1}#2}} \skvrobustdef\skvgappto#1#2{\xdef#1{\skvexpandonce{#1}\unexpanded{#2}}} \skvrobustdef\skvxgappto#1#2{\xdef#1{\skvexpandonce{#1}#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{}{} % This is usually called under \edef. must have been % initialized. \skvnewdef*\skvaddlist#1#2{% \skvifemptyTF#2{}{\skvexpandonce#2\unexpanded{#1}}% } % \skvxaddtolist{}{}{} \skvrobustdef\skvxaddtolist#1#2#3{\edef#2{\skvaddlist#1#2#3}} % \skvaddtolist[]{}{} \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{} \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![](){} % % 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. will be applied to all the elements (new and old). % The default value of 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![](){} % % 1. The exclamation mark (!) implies that the outcome is globalized. % 2. will be applied to only the new elements. The default % value of 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!{} % 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!{} % 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!{} % % => {}{},...,{}{} % % 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 , 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{} \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{}{} \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{}[]{} \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{}{} \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 {}. 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{} % \skv@strippointersfromkey*{} % % 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{} % \skv@strippointersfromvalue*{} % % Strip pointers from a key's value. The pointers that may be used on % values of keys are % % .expand once process list -> Expand value once and process the % expanded value as a list. % .expand twice process list -> Expand value twice and process the % expanded value as a list. % .expand process list -> Fully expand value and process the % expanded value as a list. % .eprocess list -> Fully expand value and process the % expanded value as a list. % .process list -> Process the given value as a list. % .list -> Process the given value as a list. % .expand once -> Expand value once before parsing it. % .expand twice -> Expand value twice before parsing it. % .expanded -> Fully expand value before parsing it. % .expand -> Fully expand value before parsing it. % % 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{.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, '.expanded{value}', since in this case % applying \edef to the value macro may be unsafe on : \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{}{} % % 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{} % % 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[]{}{}{.{}} % % 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}{keya,keyb}{.expanded{#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 % % \//.@xxarg. % % 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#3#4{% \begingroup \let\skv@temparg\skv@simplearg \def\skv@expandertype{x0}% \edef\skv@tempa{\skvtrimspace{#4}}% \def\do##1##2{% \ifx\do##1\else \skvxifinTF{\detokenize{##1}}{\skvoxdetok\skv@tempa}{% \def\skv@prova####1##1####2####3\skv@arg@nil{% \edef\skv@temparg{\unexpanded{####2}}% \def\skv@expandertype{##2}% }% \expandafter\skv@prova\skv@tempa\skv@arg@nil \def\do####1\do\do{}% }{}% \expandafter\do \fi }% \do{.unexpanded}{x0}{.expand once}{x1}{.expand twice}{x2}% {.expanded}{xx}{.expand}{xx}\do\do \def\skv@tempc{}% % Loop over prefixes: \skvcommaparse{#1}\skv@tempa{% \skv@makeprefix\skv@tempa % Loop over families: \skvcommaparse{#2}\skv@tempa{% \skv@makeheader\skv@tempa % Loop over keys: \skvcommaparse{#3}\skv@tempa{% \edef\skv@tempc{% \skvexpandonce\skv@tempc \skvcsedef{\skv@header\skv@tempa.@\skv@expandertype arg}{% \noexpand\unexpanded{\skvexpandonce\skv@temparg}% }% }% }% }% }% \expandafter\endgroup\skv@tempc } % \skv@getexpander{} % % 1. is one of 'x0', 'x1', 'x2', 'xx', which had been % saved in a key-dependent macro. % % 2. \skv@getexpander returns macro \skvexpander, which will % contain one of \unexpanded, \skvexpandonce, \skvexpandtwice, \@iden. % \skvrobustdef*\skv@getexpander#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{} % % 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[]{}{}[]{} % \skvrobustdef*\skvordkey{\skv@testoptb{\skv@ordkey{e}}} \skvrobustdef*\skvordkeys{\skv@testoptb{\skv@ordkeys{e}}} % % \skvgordkey[]{}{}[]{} % % 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[]{}{}[]{} % % 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 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 % []{}{}{}[]{} % % 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 to set a % message for the user. can be empty. % 3. Pointers can be used on both and . % \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[]{}{}[]{} % % 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[]{}[]{}[]{} % \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[]{}[]{}[]{} \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[]{}[]{}[]{} % \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[]{}[]{}[]{} \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 {} for choice keys. % % \skvchoicekey[]{}[]{}[]{} % []{} % \skvchoicekey+[]{}[]{}[]{} % []{}{} % \skvchoicekey*[]{}[]{}[]{} % []{} % \skvchoicekey*+[]{}[]{}[]{} % []{}{} % % \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}{} \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}{}{} \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 the state pattern difficult or impossible: \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@temptoks\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@temptoks }% }% } \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[]{}[]{}[]{} % \skvboolkey+[]{}[]{}[]{}{} % % 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{}{}[] \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{}{}{}{} \skvrobustdef*\skv@boolkey@a#1#2#3#4{% \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}% \skvifknobTF{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{}{}{}{}{} \skvrobustdef*\skv@boolkey@b#1#2#3#4#5{% \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}% \skvifknobTF{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{}{}{}{{}{}} \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@temptoks\expandafter{\skv@tempa{##1}{#4}}% \skvcsedef{\skv@header#1.@cbk}{\the\skv@temptoks}% } % % \skvboolkeys[]{}[]{}[]{} % \skvboolkeys+[]{}[]{}[]{}{} % \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{}{}[] \skvrobustdef*\skv@boolkeys#1#2[#3]{% \skvifdefboolTF{skv@pl}\skv@boolkeys@b\skv@boolkeys@a{#2}{#1}{#3}% } % \skv@boolkeys@a{}{}{}{} \skvrobustdef*\skv@boolkeys@a#1#2#3#4{\skv@boolkeys@c{#1}{#2}{#3}{#4}{^skv^}} % \skv@boolkeys@b{}{}{}{}{} \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[]{}[]{}[] % {}{} % \skvbiboolkeys+[]{}[]{}[] % {}{}{} % % 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{}{}{}{}{} \skvrobustdef*\skv@biboolkeys@a#1#2#3#4#5{% \skv@biboolkeys@c{#1}{#2}{#3}{#4}{#5}{^skv^}% } % \skv@bilboolkeys@a{}{}{} % {}{}{} \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{}{}{}{} \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[]{}[]{}[]{} % \skvtogkey+[]{}[]{}[]{}{} % \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[]{}[]{}[] % {}{} % \skvbitogkeys+[]{}[]{}[] % {}{}{} \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[]{}{}{} % []{} % \skvprependslots[]{}{}{} % []{} % % 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}} % % 1. 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 . % % 3. Here, it helps to consider as parents (or signals/ % subjects/observables) and as children (or slots/observers). % % 4. 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. % \AtEndOfPackage{% \skvnewcastcommands{% \skvslotkeys=\skvappendslots,\skvstylekeys=\skvappendslots, \skvprependstyles=\skvprependslots, \skvappendstylesexpanded=\skvappendslotsexpanded, \skvprependstylesexpanded=\skvprependslotsexpanded, \skvaddstyles=\skvaddslots, \skvobserverkeys=\skvappendslots, \skvprependobservers=\skvprependslots, \skvappendobserversexpanded=\skvappendslotsexpanded, \skvprependobserversexpanded=\skvprependslotsexpanded, \skvaddobservers=\skvaddslots }% } \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{}{}{} \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 using % \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[]{}{}{} % {}[]{} % % 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{}{}{} % []{} \skvrobustdef*\skv@addslots@a#1#2#3[#4]#5{% \skvexpanded{% \skvprependslots[\skvcurrentprefix]{\skvcurrentfamily}% \unexpanded{{#1}{#2}[#4]{#5}}% % Without \ifskv@addslots@appending, when using \skvaddslots, % signal keys will otherwise be added in the database twice % (while treating prepended and appended slots). \noexpand\skv@addslots@appendingtrue \skvappendslots[\skvcurrentprefix]{\skvcurrentfamily}% \unexpanded{{#1}{#3}[#4]{#5}}% \noexpand\skv@addslots@appendingfalse }% } % 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 \skvxifinTF{,\detokenize{#2},}{,\detokenize{#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[]{}{key}{}{} % % 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[]{}{key}{}{} % % The search will be done on all combinations of prefixes/families. The % callback or will be executed for ALL the combinations. % \skvrobustdef*\skvifkeyinfamiliesTF{% \skv@testopt{\skv@ifkeyinfamilies0}\skvdefaultprefix } % \skvdoonallpathsfound[]{}{key}{} \skvrobustdef*\skvdoonallpathsfound{% \skv@testopt\skv@doonallpathsfound\skvdefaultprefix } \skvrobustdef*\skv@doonallpathsfound[#1]#2#3#4{% \skv@ifkeyinfamilies0[#1]{#2}{#3}{#4}{}% } % \skvdoonallpathsnotfound[]{}{key}{} \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{/}{} \skvnewdef*\skvifkeyinfamilyTF#1#2{% \skvifcsdefTF{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}% } % \skvifkeyincurrentfamilyTF{} \skvnewdef*\skvifkeyincurrentfamilyTF#1{% \skvifcsdefTF{\skvcurrentpath/\skvtrimspace{#1}.@cbk}% } \skvnewlet\skvifkeyoncurrentpathTF\skvifkeyincurrentfamilyTF % \skvifonekeyinfamilyTF{/}{} % % 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[]{}{} % % 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{}{}{} \skvnewdef*\skvvalueof#1#2#3{\skvcsuse{#1/#2/#3.@value}} % \skvvalueofkey{//} \skvnewdef*\skvvalueofkey#1{\skvcsuse{#1.@value}} % When isn't given, the following will use 'default KV' for . % \skvprintvalue[]{}{} \skvrobustdef*\skvprintvalue{\skv@testoptb\skv@printvalue} \skvrobustdef*\skv@printvalue#1{\skvcsuse{\skv@header#1.@value}} % \skvgetvalue{}{}{} \skvrobustdef*\skvgetvalue#1#2#3#4{\skvletcs#4{#1/#2/#3.@value}} % \skvconveyvalue[]{}{} \skvrobustdef*\skvconveyvalue{\skv@testoptb\skv@conveyvalue} \skvrobustdef*\skv@conveyvalue#1#2{\skvletcs#2{\skv@header#1.@value}} % \skvgetinikv{}{} \skvrobustdef*\skvgetinikv#1#2#3{\skvletcs#3{#1/#2.@inikv}} % \skvconveyinikv[]{} \skvrobustdef*\skvconveyinikv{\skv@testoptb\skv@conveyinikv} \skvrobustdef*\skv@conveyinikv#1{\skvletcs#1{\skvcurrentpath.@inikv}} % \skvkeyslet[]{}{} % % -> {,,...,}={} % % Note: 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@disable@temp{\skv@ordkey@a{e}}% \skv@testoptb\skv@disablekeys } \skvrobustdef*\skvgdisablekeys{% \def\skv@disable@temp{\skv@ordkey@a{x}}% \skv@testoptb\skv@disablekeys } \skvrobustdef*\skv@disablekeys#1{% \skvcommaparse{#1}\skv@prova{% \skvifnamedefTF{\skv@header\skv@prova.@cbk}{% \skvifnamedefTF{\skv@header\skv@prova.@defa}{% \edef\skv@provb{\noexpand\skv@disable@temp{\skv@prova}[]}% }{% \edef\skv@provb{\noexpand\skv@disable@temp{\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 }% }% \skvundef\skv@disable@temp } % 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[]{}{} % \skvgpresetkeys[]{}{} \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[]{}{} \skvrobustdef*\skvremovepresetkeys{% \skv@clfalse\skv@testoptb\skv@removepresetkeys } % \skvgremovepresetkeys[]{}{} \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[]{} \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[]{}{} \skvrobustdef*\skvremovepostsetkeys{% \skv@clfalse\skv@testoptb\skv@removepostsetkeys } % \skvgremovepostsetkeys[]{}{} \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[]{} \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[]{}{} \skvrobustdef*\skvremovepreposetkeys{% \skv@clfalse\skv@testoptb\skv@removepreposetkeys } % \skvgremovepreposetkeys[]{}{} \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[]{} \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 '
.@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[]{}{} \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[]{}{} \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[]{}{} \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[]{}{} \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[]{}{} \skvrobustdef*\skvoptionkeys{% \skv@clfalse\skv@testoptb{\skv@optionkeys{}}% } \skvrobustdef*\skvgoptionkeys{% \skv@cltrue\skv@testoptb{\skv@optionkeys{}}% } % \skvnonoptionkeys[]{}{} \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[]{}{} \skvrobustdef*\skvremoveoptionkeys{% \skv@clfalse\skv@testoptb{\skv@removeoptionkeys{}}% } \skvrobustdef*\skvgremoveoptionkeys{% \skv@cltrue\skv@testoptb{\skv@removeoptionkeys{}}% } % \skvremovenonoptionkeys[]{}{} \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[]{}{} \skvrobustdef*\skvsaveinitialvaluekeys{% \skv@clfalse\skv@testoptb\skv@saveinitialvaluekeys } % \skvgsaveinitialvaluekeys[]{}{} \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[]{}{} \skvrobustdef*\skvremovesaveinitialvaluekeys{% \skv@clfalse\skv@testoptb\skv@removesaveinitialvaluekeys } % \skvgremovesaveinitialvaluekeys[]{}{} \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[]{} \skvrobustdef*\skvundefsaveinitialvaluekeys{% \skv@clfalse\skv@testoptb\skv@undefsaveinitialvaluekeys } % \skvgundefsaveinitialvaluekeys[]{} \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[]{}{} \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[]{}{} \skvrobustdef*\skvremoveignorekeys{% \skv@clfalse\skv@testoptb\skv@removeignorekeys } % \skvgremoveignorekeys[]{}{} \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[]{} \skvrobustdef*\skvundefignorekeys{% \skv@clfalse\skv@testoptb\skv@undefignorekeys } % \skvgundefignorekeys[]{} \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[]{}{}{}{} \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[]{}{}{} \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[]{} \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{}{} % % \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{% \skvifknobTF{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', etc, 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 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 \edef\skv@callback{% \let\noexpand\this\noexpand\skvcurrentkey \skvexpandonce\skv@callback }% % 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 may be more complex: \ifx\skv@argpattern\skv@simplearg\else \ifcsname\skv@header\skvcurrentkey.@listkey\endcsname \skv@err{A listkey must have only 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{}{}{}{} \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 }% % \expandafter\expandafter\expandafter\skv@g@tdefault \csname\skv@prova.@defa\endcsname\skv@nil \skvaftergroupdef#4\endgroup } % Set keys using their default values: % \skvinitializekeys[]{}[] \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. % Pox keys can't be initialized. \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*+[]{}[]{} % * -> 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
.@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} % Set aliases for handlers used in \skvdefinekeys: \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}}% \skvifknobTF{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@getshortformofcontent{30}\skv@provb\skv@ival }% }% \skvaftergroupdef\skv@ival\endgroup } % \skv@getshortformofcontent{}{}{} \skvrobustdef*\skv@getshortformofcontent#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[]{}{} % % 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 % \//.@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@'. \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{}{}{} \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{}{} \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{} \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{}{} % % 1. has the form % % {}/{.do={},...,.do={}} % ,..., % /{.do={},...,.do={}} % % 2. If it didn't exist, the main key (eg, the above ) 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{}{}{} \skvrobustdef*\dirkeys@executechoice#1#2#3{% \skv@getexpander{#1}% \edef\skv@prova{\skvexpander{#2}}% \skvdespacecontent\skv@prova \def\do##1##2##3\skv@nil{% \edef\skv@provb{\unexpanded{##1}}% \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{}{}{} % % 1. has the form % % {,...,}/{,...,} % ,..., % {,...,/{,...,} % % 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{#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{}{}{} % % 1. has the form % % {,..,}/{,...,} % % 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{}{} % % 1. has the form % % /,...,/ % % 2. Examples: % % .store expanded twice value in={keyc/\valc,keyd/\vald} % \skvrobustdef*\dirkeys@savestores#1#2{% \begingroup \skv@getexpander{#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: =,...,= % #2: % \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 to set the keys. % \dirkeys@setwithdefaults[]{}{} \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[]{} % % \directkeys{ % .try set keys={}, where % includes and key-value list (see below). % } % % Try to set a given key-value list on the paths given by the independent % 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 . % % Save the known/found keys in the macro suggested by the option % . 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 . The default value of % is 1. The options and are synonymous. % % \skvtrysetkeys is similar to \skvsetkeys*+ (ie, \skvsetkeys with the % options *+), but in this case we have (ie, the maximum total number % of attempts - families to search for a given key) before backing off, % and (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 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 , but the key may not need to be % executed in all of them. The default value of 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 is less than % 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 /,...,/. % .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 ,...,. % .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 //. 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 //. 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=/,...,/ .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[]{} \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[]{} % .sa set keys={} % % When using '.sa set keys', the key '.key values' must be part of % . % % 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{} % Elements of will have the form '{}/{}' \skvrobustdef*\dirkeys@addpaths#1{% \skv@formatpaths{#1}\dirkeys@pathlist } % \dirkeys@removepaths{} \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{} \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{}{}{} % Collect current prefixes in , families in and % ignored keys in . \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}{} % has the form ={},...,={} \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{} \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{} \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}{} \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{}{}{} \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{}{} % % indicates if we're in (01) or (00) handler. % % -> {}={},...,{}={} % \skvrobustdef*\dirkeys@defhandlers#1#2{% \begingroup \def\skv@collection{}% \def\skv@param{####1\skvhandlereov}% \def\skv@defhandlers@do##1{% \skv@ensurehandlerdot{##1}% \skvifknobTF{#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 }% } \skvnewlet\dirkeysdefhandlers\dirkeys@defhandlers % \dirkeys@defnarghandlers{}{} % % Define handlers of more than one argument. This is slightly more % expensive than '.new handlers': % % indicates whether we're in (01) or (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}}% \skvifknobTF{#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 }% } \skvnewlet\dirkeysdefnarghandlers\dirkeys@defnarghandlers % \dirkeys@handlerlet{}{} % % 1. is either 00 (for define) or 01 (for new handler). % 2. has the form % % {}={} % \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 \skvifknobTF{#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={} {.try set keys,.try set,.try}={ \dirkeys@trysetkeys{#1} }, % 'search also' set keys. % .sa set keys={} {.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. % /{.do={},...,.do={}},..., % /{.do={},...,.do={}} {.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} }, % {,...,}/{},..., % {,...,}/{}: {.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} }, % {}/,...,{}/: {.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}/: .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={}{}{} .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={}{}{} % 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[]{}{}{}{}{} % \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'.