%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% % This is file 'keyreader.sty', version 0.5b, 2012/11/06. % % % % 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. % % % % Copyright (c) 2010-2012 Ahmed Musa (amusa22@gmail.com). % %++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% \ProvidesPackage{keyreader} [2012/11/06 v0.5b Robust interfaces to xkeyval package (AM)] \newdimen\krdz@ \ifcase \ifx\eTeXversion\@undefined\krdz@\else \ifnum\eTeXversion<\tw@\krdz@\else\@ne\fi\fi \PackageError{keyreader} {eTeX not loaded or old version} {This package requires eTeX version 2 or higher.}% \expandafter\endinput \fi \NeedsTeXFormat{LaTeX2e}[2011/06/27] \begingroup \catcode064 11% @ \catcode123 01% { \catcode125 02% } \catcode044 12% , \def\reserved@a{\endgroup \def\do##1,{% \ifx\end##1\else \catcode##1\string=\the\catcode##1 % \expandafter\do \fi }% \edef\krd@restorecodes{\do035,064,123,125,061,059,\end,}% } \reserved@a \catcode035 06% # \catcode064 11% @ \catcode123 01% { \catcode125 02% } \catcode061 12% = \catcode044 12% , \def\do#1=#2,{% \ifx\end#1\else \edef\krd@restorecodes{% \krd@restorecodes \catcode#1=\the\catcode#1 % }% \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,\end=,% \AtEndOfPackage{% \krdAfterEndPackage{% \krd@restorecodes \let\krd@restorecodes\relax }% } \ifx\XKeyValLoaded\endinput\else \input xkeyval \fi \let\XKV@doxs\relax \let\krd@err\@latex@error \def\krd@ehd{% There is a problem here. Investigate it before \MessageBreak proceeding. Try typing to proceed.\MessageBreak If that doesn't work, type X to quit. } \def\@space{ } \protected\def\krdnewlet#1{\@ifdefinable{#1}\relax\let#1= } \krdnewlet\krd@nil\relax \def\krd@nnil{\krd@nil} \def\krdswap#1#2{#2#1} \def\krd@quark{\@gobble\krd@quark} \newcount\krd@csvdepth \newif\ifkrd@foreach@break \newif\ifkrd@st \newif\ifkrdindef \long\def\krdifblank#1{% \ifcat$\detokenize\expandafter{\@gobble#1.}$% \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi } \begingroup \catcode`\&=8 \gdef\krdifledbyspace#1{\krd@ifledbyspace&& \trim@nil} \gdef\krd@ifledbyspace& #2\trim@nil{% \krdifblank{#2}\@secondoftwo\@firstoftwo } \gdef\krdtrimspace#1{\krd@trimspace@a.#1& &} \gdef\krd@trimspace@a#1 &{\krd@trimspace@b#1&} \gdef\krd@trimspace@b#1{% \krdexpandonce{% \romannumeral-`\q \expandafter\krdifledbyspace\expandafter{\@gobble#1}% {\expandafter\noexpand\@gobble#1}{\expandafter\@space\@gobble#1}% }% } \endgroup \def\krdzapspace#1{\krd@zapspace#1 \zap@nil} \def\krd@zapspace#1 #2\zap@nil{% \krdifblank{#2}{#1}{% \krd@zapspace#1#2\zap@nil }% } \protected\def\krdAfterEndPackage{% \krdifcsdef{\@currname.\@currext-krd@endpkghook}{}{% \@namedef{\@currname.\@currext-krd@endpkghook}{}% }% \expandafter\g@addto@macro \csname\@currname.\@currext-krd@endpkghook\endcsname } \xdef\@popfilename{% \unexpanded{% \csname\@currname.\@currext-krd@endpkghook\endcsname \expandafter\let \csname\@currname.\@currext-krd@endpkghook\endcsname \relax }% \unexpanded\expandafter{\@popfilename}% } \newcommand*\krdifdef[1]{% \krdifblank{#1}\@secondoftwo{% \ifx#1\@undefined \expandafter\@secondoftwo \else \ifx#1\relax \expandafter\expandafter\expandafter\@secondoftwo \else \expandafter\expandafter\expandafter\@firstoftwo \fi \fi }% } \newcommand*\krdifcsdef[1]{% \krdifblank{#1}{% \expandafter\@secondoftwo\@gobble }{% \ifcsname#1\endcsname \expandafter\@firstofone \else \expandafter\expandafter\expandafter \@secondoftwo\expandafter\@gobble \fi }{% \expandafter\krdifdef\csname#1\endcsname }% } \def\krd@ifx@quark{\@gobble\krd@ifx@quark} \newcommand\krdifx[2]{% \ifx#1#2\krd@ifx@quark \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi } \def\krdifbool#1{% \krdifblank{#1}{% \krd@err{No boolean}\krd@ehd }{% \krdifcsdef{if#1}{% \csname @\csname if#1\endcsname first\else second\fi oftwo\endcsname }{% \krd@err{Undefined boolean '#1'}\krd@ehd }% }% } \def\krd@ifswitch@aux#1#2\krd@nil{% \krdifblank{#2}\@secondoftwo\@firstoftwo } \def\krdifswitch#1{% \krdifblank{#1}{% \@secondoftwo }{% \expandafter\krd@ifswitch@aux\romannumeral-`\q#1\krd@nil{% \if#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi }{% \krd@err{Invalid switch '#1'}\krd@ehd }% }% } \krdifx\pdfstrcmp\@undefined{% \RequirePackage{pdftexcmds}% \krdifx\pdf@strcmp\@undefined{% \krd@err{Neither '\string\pdfstrcmp' nor '\string\pdf@strcmp' exists}\krd@ehd }{% \let\krd@pdfstrcmp\pdf@strcmp }% }{% \let\krd@pdfstrcmp\pdfstrcmp } \newcommand\krdifstrcmp[2]{% \csname @\ifnum\krd@pdfstrcmp{\detokenize{#1}}% {\detokenize{#2}}=0first\else second\fi oftwo\endcsname } \def\krdifcond#1\fi{% \csname @#1first\else second\fi oftwo\endcsname } % If we were to use \krdtrimspace for \krdifbraced, then the need % for double \romannumeral will cause problems of premature expansion. % We need the \romannumeral below to make \krdifbraced expandable: \def\krdifbraced#1{% \expandafter\krdswap\expandafter{% \expandafter{\romannumeral-`\q\krd@ifbraced@a{#1}}% }{% \expandafter\krd@ifbraced@d\romannumeral-`\q\krd@ifbraced@a{#1}\krd@nil }% } \begingroup \catcode`\&=7 \gdef\krd@ifbraced@a#1{\krd@ifbraced@b.#1& &} \gdef\krd@ifbraced@b#1 &{\krd@ifbraced@c#1&} \gdef\krd@ifbraced@c#1{\expandafter\noexpand\@gobble#1} \endgroup \def\krd@ifbraced@d#1\krd@nil#2{% \krdifstrcmp{#1}{#2}\@secondoftwo\@firstoftwo } % \krdstripouterbraces{} \protected\def\krdstripouterbraces#1#2{% \begingroup \@tempcnta\krdz@ \expandafter\krd@stripouterbraces#2\krd@stripbrace{#2}{#1}% } \protected\def\krd@stripouterbraces#1\krd@stripbrace#2#3{% \advance\@tempcnta\@ne \krdifcond\ifnum\@tempcnta=#3\fi{% \edef#2{\unexpanded{#1}}% \edef\x{\endgroup\krdaftergr#2}\x }{% \krd@stripouterbraces#1\krd@stripbrace{#2}{#3}% }% } \def\krdoxdetok#1{\detokenize\expandafter{#1}} \def\krdusearg#1#2{% \expandafter\krdswap\expandafter{\expandafter{#2}}{#1}% } \def\krdexpandonce#1{\unexpanded\expandafter{#1}} \def\krdnoexpandcs#1{\krdexpandonce{\csname#1\endcsname}} \def\krdncsname{\expandafter\noexpand\csname} \protected\def\krdexpanded#1{% \begingroup \protected@edef\reserved@a{\endgroup#1}\reserved@a } \protected\def\krdexpandarg#1#2{\krdexpanded{\unexpanded{#1}{#2}}} \protected\def\krdexpandargonce#1#2{% \krdexpanded{\unexpanded{#1}{\krdexpandonce{#2}}}% } \protected\def\krdexpandsecond#1#2{\krdexpanded{\unexpanded{#1}#2}} \protected\def\krdexpandsecondonce#1#2{% \krdexpanded{\unexpanded{#1}\krdexpandonce{#2}}% } \def\krdaftergr#1{% \edef\noexpand#1{\noexpand\unexpanded{\krdexpandonce{#1}}}% } \protected\def\krdifescaped#1{% \begingroup \escapechar92\relax \edef\x{\expandafter\@car\string#1x\@nil}% \expandafter\endgroup \csname @\ifx\x\@backslashchar first\else second\fi oftwo\endcsname } % Eg, % \newcount\nr\nr=\tw@ % \def\do#1{% % \let\noexpand#1% % \expandafter\noexpand\csname\expandafter\@gobble\string#1@% % \romannumeral\nr\endcsname % } % \edef\x{\krdfor{\cmda,\cmdb}} % \show\x -> \let\cmda\cmda@ii \let\cmdb\cmdb@ii % \def\krdfor#1{\krd@for@a#1,\krd@nnil,\krd@nil} \def\krd@for@a#1,#2\krd@nil{% \krdifblank{#1}{% \krd@for@a#2\krd@nil }{% \expandafter\krdifx\@car#1\@nil\krd@nnil {}{\do{#1}\krd@for@a#2\krd@nil}% }% } % We redefine xkeyval's loops to increase robustness. % We can't replace \@nil by \krd@nil here because xkeyval uses \@nil % to terminate the loops. \protected\long\def\XKV@for@n#1#2#3{% \edef#2{\unexpanded{#1}}% \krdifx#2\@empty{}{% \XKV@f@r#2{#3}#1,\@nil,% }% } \let\krdcommaloop\XKV@for@n \let\krdecommaloop\XKV@for@o \protected\long\def\XKV@f@r#1#2#3,{% \edef#1{\unexpanded{#3}}% \krdifx#1\@nnil{}{#2\XKV@f@r#1{#2}}% } \protected\long\def\XKV@for@eo#1#2#3{% \edef#2{\unexpanded{\XKV@f@r#2{#3}}}% \expandafter#2#1,\@nil,% } \protected\long\def\XKV@whilist#1#2#3\fi#4{% #3\relax \expandafter\@firstofone \else \expandafter\@gobble \fi {\expandafter\XKV@wh@list#1,\@nil,\@nnil#2#3\fi{#4}{}}% } \long\def\XKV@wh@list#1,#2\@nnil#3#4\fi#5#6{% \edef#3{\unexpanded{#1}}% \krdifx#3\@nnil{% \def#3{#6}\@gobble }{% #4\relax \expandafter\@firstofone \else \def#3{#6}% \expandafter\@gobble \fi }% {#5\XKV@wh@list#2\@nnil#3#4\fi{#5}{#1}}% } \protected\def\KV@@sp@def#1#2{\edef#1{\krdtrimspace{#2}}} \protected\def\XKV@sp@deflist#1#2{% \edef#1{\unexpanded{#2}}% \krdcsvnormalize#1% } \protected\def\krd@pushmacros#1#2#3{% \begingroup \global\advance#2\@ne \krdifdef#1{}{\def#1{}}% \def\reserved@a##1{\expandafter\@gobble\string##1@\romannumeral#2}% \def\do##1{\let\noexpand##1\krdnoexpandcs{\reserved@a{##1}}}% \xdef#1{#3{\krdexpandonce#1}}% \def\do##1{\let\krdnoexpandcs{\reserved@a{##1}}\noexpand##1}% \krdexpanded{\endgroup#3}% } \protected\def\krd@popmacros#1#2{% \begingroup \def\reserved@a##1##{\endgroup##1\gdef#1}% \expandafter\reserved@a#1% \global\advance#2\m@ne } \protected\def\krd@testst#1{\@ifstar{\krd@sttrue#1}{\krd@stfalse#1}} % \krdforeach[] % {}<\if...>\fi{}{}{} % \krdforeach*[] % {}<\if...>\fi{}{}{} % % 1. When the conditional <\if...> isn't true, the regular code % will be executed for every member of the list. % 2. will be executed when the conditional <\if...> is true. % may be used to first turn <\if...> off before commencing % the loop. \ifkrd@foreach@break may be used as the conditional in % <\if...>. % 3. Both and can be empty (ie, {}). % 4. Use '#1' in and to access the current element % of the list. % \protected\def\krdforeach{\krd@testst{\@testopt\krd@foreach,}} \protected\def\krd@foreach[#1]#2#3\fi#4#5#6{% \krd@pushmacros\krd@foreach@stack\krd@csvdepth{% \do\krd@foreach@do\do\krd@foreach@final\do\krd@foreach@regular \do\stopforeach }% \edef\krd@foreach@item{\krdifbool{krd@st}\krdexpandonce\unexpanded{#2}}% \krdcsvnormalize[#1]\krd@foreach@item \def\krd@foreach@final##1{#5}% \def\krd@foreach@regular##1{#6}% \krd@foreach@breakfalse \let\stopforeach\krd@foreach@breaktrue % Initial code: #4\relax \def\krd@foreach@do##1#1##2\krd@nnil{% \edef\krd@foreach@item{\krdexpandonce{\@gobble##1}}% \krdifx\krd@foreach@item\krd@nnil{% \krd@foreach@breakfalse }{% \krdifcond#3\fi{% \expandafter\krd@foreach@final\expandafter{\krd@foreach@item}\relax }{% \expandafter\krd@foreach@regular\expandafter{\krd@foreach@item}\relax \krdifbool{krd@foreach@break}{% \krd@foreach@breakfalse }{% \krd@foreach@do.##2\krd@nnil }% }% }% }% \expandafter\krd@foreach@do\expandafter .\krd@foreach@item#1\krd@nil#1\krd@nnil \krd@popmacros\krd@foreach@stack\krd@csvdepth } \protected\def\krdaddtolist{\krd@testst{\@testopt{\krd@addtolist{e}},}} \protected\def\krdgaddtolist{\krd@testst{\@testopt{\krd@addtolist{x}},}} \protected\def\krd@addtolist#1[#2]#3#4{% \@nameuse{#1def}#3{% \krdifdef#3{% \krdifx#3\@empty{}{\krdexpandonce#3#2}% }{}% \krdifbool{krd@st}\krdexpandonce\unexpanded{#4}% }% } \protected\def\krdcsvnormalize{\@testopt\krd@csvnormalize,} \protected\def\krd@csvnormalize[#1]#2{% \begingroup \krd@setupnormalize{#1}% \def\krd@inkv{01}% \edef#2{\expandafter\krd@normalizelist\expandafter{#2}}% \krdexpanded{\endgroup \edef\noexpand#2{\noexpand\unexpanded{\krdexpandonce{#2}}}% }% } \protected\def\krdkvnormalize#1{% \begingroup \krd@setupnormalize{,}% \def\krd@inkv{00}% \edef#1{\expandafter\krd@normalizelist\expandafter{#1^?^}}% \def\reserved@b##1^?^##2\krd@nil{% \edef#1{\unexpanded{##1##2}}% }% \expandafter\reserved@b#1\krd@nil \krdexpanded{\endgroup \edef\noexpand#1{\noexpand\unexpanded{\krdexpandonce{#1}}}% }% } \begingroup \catcode`\~=13 \catcode`\!=13 \protected\gdef\krd@setupnormalize#1{% \begingroup \uccode`\~=`#1% \uccode`\!=`\=% \uppercase{\endgroup \def\krd@normalizelist##1{% \unexpanded\expandafter{\romannumeral-`\q \krd@activeparser#1##1#1~\krd@nil}% }% \def\krd@activeparser##1~##2\krd@nil{% \krdifblank{##2}{% \krdifswitch\krd@inkv{% \krd@activeequal##1!\krd@nil }{% \krd@spaceparser##1 #1\krd@nil }% }{% \krd@activeparser##1#1##2\krd@nil }% }% }% \begingroup \uccode`\~=`\=% \uppercase{\endgroup \def\krd@activeequal##1~##2\krd@nil{% \krdifblank{##2}{% \krd@spaceparser##1 #1\krd@nil }{% \krd@activeequal##1=##2\krd@nil }% }% }% \def\krd@spaceparser##1 #1##2\krd@nil{% \krdifblank{##2}{% \krd@parserspace##1#1 \krd@nil }{% \krd@spaceparser##1#1##2\krd@nil }% }% \def\krd@parserspace##1#1 ##2\krd@nil{% \krdifblank{##2}{% \krdifswitch\krd@inkv{% \krd@spaceequal##1 =\krd@nil }{% \krd@doubleparser##1#1#1\krd@nil }% }{% \krd@parserspace##1#1##2\krd@nil }% }% \def\krd@spaceequal##1 =##2\krd@nil{% \krdifblank{##2}{% \krd@equalspace##1= \krd@nil }{% \krd@spaceequal##1=##2\krd@nil }% }% \def\krd@equalspace##1= ##2\krd@nil{% \krdifblank{##2}{% \krd@doubleparser##1#1#1\krd@nil }{% \krd@equalspace##1=##2\krd@nil }% }% \def\krd@doubleparser##1#1#1##2\krd@nil{% \krdifblank{##2}{% \krdifswitch\krd@inkv{% \krd@doubleequal##1==\krd@nil }{% \krdifblank{##1}{}{% \krd@remleadparser##1\krd@nil }% }% }{% \krd@doubleparser##1#1##2\krd@nil }% }% \def\krd@doubleequal##1==##2\krd@nil{% \krdifblank{##2}{% \krdifblank{##1}{}{% \krd@remleadparser##1\krd@nil }% }{% \krd@doubleequal##1=##2\krd@nil }% }% \def\krd@remleadparser#1##1\krd@nil{\noexpand##1}% \def\krd@parserequalerr##1#1=##2\krd@nil{% \krdifblank{##2}{}{% \krd@err{There is '#1=' in your key-value list}\krd@ehd }% }% } \endgroup \krd@setupnormalize{,} % Filter class options to remove '=' and option values. Do this % retroactively if this package was loaded before \documentclass. % The keyreader package can be loaded before \documentclass. \protected\def\XKV@filterclassoptions{% \XKV@sgfalse \ifx\@classoptionslist\@undefined\else \ifx\@classoptionslist\relax\else \ifx\@classoptionslist\@empty\else \XKV@sgtrue \fi \fi \fi \ifXKV@sg \let\XKV@filterclassoptions\relax \let\XKV@origclassoptionslist\@classoptionslist \let\XKV@classoptionslist\@classoptionslist \def\@classoptionslist{}% \XKV@for@o\XKV@classoptionslist\XKV@tempa{% \@expandtwoargs\in@{=}{\krdoxdetok\XKV@tempa}% \ifin@\else \edef\@classoptionslist{% \@classoptionslist\ifx\@classoptionslist\@empty\else,\fi \unexpanded\expandafter{\XKV@tempa}% }% \fi }% \fi } \def\XKV@getdocumentclass{% \let\XKV@documentclass\@undefined \XKV@sgfalse \ifdefined\@filelist \ifx\@filelist\@gobble\else \ifx\@filelist\relax\else \ifx\@filelist\@empty\else \XKV@sgtrue \fi \fi \fi \fi \ifXKV@sg \def\XKV@tempb{00}% \XKV@whilist\@filelist\XKV@tempa\if\XKV@tempb\fi{% \filename@parse\XKV@tempa \ifx\filename@ext\@clsextension \edef\XKV@tempa{\filename@area\filename@base.\filename@ext}% \XKV@ifundefined{opt@\XKV@tempa}{}{% \let\XKV@documentclass\XKV@tempa \def\XKV@tempb{01}% }% \fi }% \fi } \XKV@getdocumentclass \ifdefined\XKV@documentclass \XKV@filterclassoptions \else \let\XKV@documentclass\@empty \let\XKV@classoptionslist\@empty \xdef\@popfilename{% \unexpanded\expandafter{\@popfilename}% \noexpand\XKV@filterclassoptions }% \fi % Redefine \XKV@define@key to heed a complaint about the grabbing % of the callback: \protected\def\XKV@define@key#1{% \@ifnextchar[{% \XKV@d@fine@k@y{#1}% }{% \begingroup \toks1{\endgroup\@namedef{\XKV@header#1}####1}% \def\reserved@a{\the\toks\expandafter1\expandafter{\the\toks0}}% \afterassignment\reserved@a\toks0=% }% } \def\XKV@d@fine@k@y#1[#2]#3{% \XKV@define@default{#1}{#2}% \expandafter\def\csname\XKV@header#1\endcsname##1{#3}% } % When the key value has doubled hash characters, xkeyval's % definition of \XKV@define@cmdkey fails: \def\XKV@define@cmdkey#1#2[#3]#4{% \ifXKV@st\XKV@define@default{#2}{#3}\fi \def\XKV@tempa{\expandafter\def\csname\XKV@header#2\endcsname####1}% \begingroup\expandafter\endgroup\expandafter\XKV@tempa\expandafter {\expandafter\edef\csname#1#2\endcsname{\unexpanded{##1}}#4}% } % Allow macro prefix (default: mp@) for choice keys: \protected\def\define@choicekey{% \XKV@testopta{\XKV@testoptb {\@testopt\XKV@define@choicekey{choice\XKV@header}}}% } \def\XKV@define@choicekey[#1]#2{% \edef\XKV@macropf{\krdtrimspace{#1}\krdtrimspace{#2}}% \@testopt{\XKV@d@fine@choicekey{#2}}{}% } \def\XKV@d@fine@choicekey#1[#2]#3{% \XKV@tempa@toks{#2}% \XKV@sp@deflist\XKV@tempa{#3}% \XKV@toks\expandafter{\XKV@tempa}% \@ifnextchar[{\XKV@d@fine@ch@icekey{#1}}{\XKV@d@fine@ch@ic@key{#1}}% } \def\XKV@d@fine@ch@icekey#1[#2]{% \edef\krd@default{\krdtrimspace{#2}}% % Choice keys shouldn't have braced values: \krdstripouterbraces{2}\krd@default \krdexpanded{% \noexpand\XKV@define@default{#1}{\krdexpandonce\krd@default}% }% \XKV@d@fine@ch@ic@key{#1}% } \def\XKV@d@fine@ch@ic@key#1{% \edef\reserved@a{\krdnoexpandcs{\XKV@header#1}}% \krdifbool{XKV@pl}{% \expandafter\XKV@d@f@ne@ch@ic@k@y\reserved@a }{% \expandafter\XKV@d@f@ne@ch@ic@key\reserved@a }% } \def\XKV@d@f@ne@ch@ic@key#1#2{\XKV@d@f@n@@ch@ic@k@y#1{{#2}}} \def\XKV@d@f@ne@ch@ic@k@y#1#2#3{\XKV@d@f@n@@ch@ic@k@y#1{{#2}{#3}}} \def\XKV@d@f@n@@ch@ic@k@y#1#2{% \edef#1##1{% \edef\krdnoexpandcs{\XKV@macropf}{\noexpand\unexpanded{##1}}% \ifXKV@st\noexpand\XKV@sttrue\else\noexpand\XKV@stfalse\fi \ifXKV@pl\noexpand\XKV@pltrue\else\noexpand\XKV@plfalse\fi \noexpand\XKV@checkchoice[\the\XKV@tempa@toks]{##1}{\the\XKV@toks}% }% \def\XKV@tempa{\def#1####1}% \expandafter\XKV@tempa\expandafter{#1{##1}#2}% } \protected\def\XKV@checksanitizea#1#2{% \XKV@ch@cksanitize{#1}#2=% \ifin@\else\XKV@ch@cksanitize{#1}#2,\fi \krdifbool{in@}{\krdkvnormalize#2}{}% } \protected\def\XKV@checksanitizeb#1#2{% \XKV@ch@cksanitize{#1}#2,% \krdifbool{in@}{\krdcsvnormalize[,]#2}{}% } % Don't apply the new schemes to \setkeys, because old calls to % \setkeys will now discover that braces are preserved. This may % break their codes: \protected\def\krdsetkeys{\XKV@testopta{\XKV@testoptc\krd@setkeys}} \protected\def\krd@setkeys[#1]#2{% \edef\krd@na{\unexpanded{#1}}% \krdifx\krd@na\@empty{}{\krdcsvnormalize\krd@na}% \edef\XKV@resb{\unexpanded{#2}}% \krdkvnormalize\XKV@resb \let\XKV@naa\@empty \XKV@for@o\XKV@resb\XKV@tempa{% \expandafter\XKV@g@tkeyname\XKV@tempa=\@nil\XKV@tempa \XKV@addtolist@x\XKV@naa\XKV@tempa }% \ifnum\XKV@depth=\krdz@\let\XKV@rm\@empty\fi \expandafter\XKV@usepresetkeys\expandafter{\krd@na}{preseth}% \krdexpandsecond\krd@s@tkeys {{\krdexpandonce\XKV@resb}{\krdexpandonce\krd@na}}% \expandafter\XKV@usepresetkeys\expandafter{\krd@na}{presett}% \let\CurrentOption\@empty } \protected\def\krd@s@tkeys#1#2{% \edef\key@list{\unexpanded{#1}}% \krdkvnormalize\key@list \edef\XKV@na{\unexpanded{#2}}% \krdifx\XKV@na\@empty{}{\krdcsvnormalize\XKV@na}% \def\krd@s@tkeys@a##1={% \def\reserved@a####1=####2\@nil{% \edef\reserved@a{\expandafter\krdtrimspace\expandafter{\@gobble####1}}% \krdifx\reserved@a\@empty{% \expandafter\krd@s@tk@ys##1==\@nil }{% \expandafter\krdifbraced\expandafter{\reserved@a}{% \edef\CurrentOption{% \unexpanded{##1}={{\krdexpandonce\reserved@a}}% }% }{% \edef\CurrentOption{\unexpanded{##1}=\krdexpandonce\reserved@a}% }% \expandafter\krd@s@tk@ys\CurrentOption==\@nil }% }% \reserved@a.% }% \XKV@for@o\key@list\CurrentOption{% \expandafter\krd@s@tkeys@a\CurrentOption==\@nil }% } % I found that \XKV@s@tk@ys is responsible for the inability of % xkeyval's \setkeys to handle unbalanced conditionals. So % we redefine it here, but only for \krdsetkeys: \protected\def\krd@s@tk@ys#1=#2=#3\@nil{% \XKV@g@tkeyname#1=\@nil\XKV@tkey \krdexpandargonce{\KV@@sp@def\XKV@tkey}\XKV@tkey \krdifx\XKV@tkey\@empty{% \krdifblank{#2}{}{% \krd@err{No key specified for value \MessageBreak '\the\XKV@toks'}\krd@ehd }% }{% \@expandtwoargs\in@{,\XKV@tkey,}{,\XKV@na,}% \krdifbool{in@}{}{% \XKV@knftrue \KV@@sp@def\XKV@tempa{#2}% \krdifbool{XKV@preset}{% \XKV@s@tk@ys@{#3}% }{% \krdifbool{XKV@pl}{% \XKV@for@eo\XKV@fams\XKV@tfam{% \XKV@makehd\XKV@tfam \XKV@s@tk@ys@{#3}% }% }{% \XKV@whilist\XKV@fams\XKV@tfam\ifXKV@knf\fi{% \XKV@makehd\XKV@tfam \XKV@s@tk@ys@{#3}% }% }% }% \krdifbool{XKV@knf}{% \krdifbool{XKV@inpox}{% \krdifx\XKV@doxs\relax{% \ifx\@currext\@clsextension\else \let\CurrentOption\XKV@tkey\@unknownoptionerror \fi }{% \XKV@doxs }% }{% \krdifbool{XKV@st}{% \XKV@addtolist@o\XKV@rm\CurrentOption }{% \krd@err{Key '\XKV@tkey' is undefined in families \MessageBreak '\XKV@fams'}\krd@ehd }% }% }{% \krdifbool{XKV@inpox}{% \krdifx\XKV@testclass\XKV@documentclass{% \expandafter\XKV@useoption\expandafter{\CurrentOption}% }{}% }{}% }% }% }% } % When the key default value has doubled hash characters, % xkeyval's definition of \XKV@default fails: \protected\def\XKV@default#1#2\@nil{% \edef\XKV@tempa{\expandafter\@gobble\string#1}% \edef\XKV@tempb{\XKV@header\XKV@tkey}% \@onelevel@sanitize\XKV@tempb \krdifx\XKV@tempa\XKV@tempb{% \begingroup \@namedef{\XKV@header\XKV@tkey}##1{% \toks@{\edef\XKV@tempa{\unexpanded{##1}}}% }% \csname\XKV@header\XKV@tkey @default\endcsname \expandafter\endgroup\the\toks@ \krdifcsdef{XKV@\XKV@header save}{% \expandafter\XKV@testsavekey\csname XKV@\XKV@header save\endcsname\XKV@tkey }{}% \ifXKV@rkv \ifXKV@sg\expandafter\global\fi \expandafter\let \csname XKV@\XKV@header\XKV@tkey @value\endcsname\XKV@tempa \fi \expandafter\XKV@replacepointers\expandafter{\XKV@tempa}% \XKV@srstate{@\romannumeral\XKV@depth}{}% \expandafter#1\expandafter{\XKV@tempa}\relax \XKV@srstate{}{@\romannumeral\XKV@depth}% }{% \XKV@srstate{@\romannumeral\XKV@depth}{}% \csname\XKV@header\XKV@tkey @default\endcsname\relax \XKV@srstate{}{@\romannumeral\XKV@depth}% }% } \protected\def\XKV@replacepointers#1{% \let\XKV@tempa\@empty \let\XKV@resa\@empty \XKV@r@placepointers#1\usevalue\@nil } \def\XKV@r@placepointers#1\usevalue#2{% \XKV@addtomacro@n\XKV@tempa{#1}% \edef\XKV@tempb{\unexpanded{#2}}% \ifx\XKV@tempb\@nnil\else\XKV@afterfi \XKV@ifundefined{XKV@\XKV@header#2@value}{% \krd@err{No value has been recorded for key \MessageBreak '#2'; pointer not replaced}\krd@ehd \XKV@r@placepointers }{% \@expandtwoargs\in@{,\detokenize{#2},}{,\krdoxdetok\XKV@resa,}% \ifin@\XKV@afterelsefi \krd@err{Possible back linking of pointers; \MessageBreak pointer replacement terminated}\krd@ehd \else\XKV@afterfi \XKV@addtolist@x\XKV@resa{#2}% \expandafter\expandafter\expandafter\XKV@r@placepointers \csname XKV@\XKV@header#2@value\endcsname \fi }% \fi } \protected\def\krdsetrmkeys{\XKV@testopta{\XKV@testoptc\krd@setrmkeys}} \protected\def\krd@setrmkeys[#1]{% \krdexpandargonce{\krd@setkeys[#1]}\XKV@rm } % We alias xkeyval's \savekeys to \savevaluekeys: \protected\def\savevaluekeys{\savekeys} % We split xkeyval's \presetkeys (head and tail) into % \krdpresetkeys (head only) and \krdpostsetkeys (tail only): \protected\def\krdpresetkeys{\@testopt\krd@presetkeys{KV}} \protected\def\krd@presetkeys[#1]#2#3{\presetkeys[#1]{#2}{#3}{}} \protected\def\krdpostsetkeys{\@testopt\krd@postsetkeys{KV}} \protected\def\krd@postsetkeys[#1]#2#3{\presetkeys[#1]{#2}{}{#3}} \protected\def\krd@disabledkey@err#1{% \krd@err{Key '#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 in \string\documentclass\@space or \string\usepackage}% } \protected\def\krddisablekeys{\XKV@testoptb\krd@disablekeys} \protected\def\krd@disablekeys#1{% \def\XKV@tempa{#1}% \krdcsvnormalize\XKV@tempa \XKV@for@o\XKV@tempa\XKV@tempa{% \krdifcsdef{\XKV@header\XKV@tempa}{}{% \@latex@warning@no@line{Key '\XKV@tempa' is undefined: couldn't be disabled}% }% \krdifcsdef{\XKV@header\XKV@tempa @default}{% \edef\XKV@tempb{\noexpand\XKV@define@key{\XKV@tempa}[]}% }{% \edef\XKV@tempb{\noexpand\XKV@define@key{\XKV@tempa}}% }% \krdexpandarg{\expandafter\XKV@tempb\expandafter}% {\krd@disabledkey@err{\XKV@tempa}}% }% } \edef\krd@hashchar{\string#} % \krddefinekeys[]{}[]{ % ord///; % cmd///; % bool///; % bool+////; % choice////; % choice+/////; % } \protected\def\krddefinekeys{% \begingroup \endlinechar\m@ne \krd@testst{\@testopt\krd@definekeys{KV}}% } \protected\def\krd@definekeys[#1]#2{% \@testopt{\krd@d@finekeys{#1}{#2}}{krdmp@}% } \protected\def\krd@d@finekeys#1#2[#3]#4{% \expandafter\endgroup \edef\krd@keyst{0\ifkrd@st0\else1\fi}% \krdindeftrue \begingroup \edef\krd@prefix{\krdtrimspace{#1}}% \edef\krd@family{\krdtrimspace{#2}}% \edef\krd@macpref{\krdtrimspace{#3}}% \toks@{}% \def\krd@rej{^?^}% \def\krd@defna{.na}% \def\krd@vals{}% \let\krduserinput\relax \let\krdorder\relax \def\krd@splita##1/##2/{\krd@splitb##1/##2/.}% \def\krd@splitb##1/##2/##3/##4/##5/##6/##7/##8/##9\krd@nil{% \edef\krd@type{\krdzapspace{##1}}% \edef\krd@name{\krdtrimspace{##2}}% \edef\krd@default{\krdusearg\krdtrimspace{\@gobble##3}}% \edef\krd@itemfour{\krdtrimspace{##4}}% \edef\krd@itemfive{\krdtrimspace{##5}}% \edef\krd@itemsix{\krdtrimspace{##6}}% \@expandtwoargs\in@{\krd@hashchar}{\detokenize{##2}}% \ifin@ \krd@err{Bad key name '\detokenize{##2}'}\krd@ehd \fi \if\krd@keyst \krdifcsdef{\krd@prefix @\krd@family @##2}{% \krd@err{Key '##2' already exists in family '\krd@family'}\krd@ehd }{}% \fi \@expandtwoargs\in@ {,\krdoxdetok\krd@type,} {,\detokenize{ord,cmd,bool,bool+,choice,choice+},}% \ifin@\else \krd@err{Unknown key type '\krd@type'}\krd@ehd \fi % ord{0}cmd{1}bool{2}bool+{3}choice{4}choice+{5} \krdusearg\krd@gettypenr{\krd@type}% \ifnum\krd@typenr>\thr@@ \krdstripouterbraces{2}\krd@default \ifcase0% \ifx\krd@itemfour\@empty\else \ifx\krd@itemfour\krd@rej\else \ifx\krd@itemfour\krd@defna\else 1\fi\fi\fi\relax \krd@err{Empty nominations for choice key \krd@name}\krd@ehd \else \krd@getaltlist{##4}% \fi \fi \@expandtwoargs\in@ {,\krdoxdetok\krd@default,}{,\detokenize{true,false},}% \edef\krd@vals{% \ifx\krd@vals\@empty\else\krdexpandonce\krd@vals,\fi \ifx\krd@default\krd@rej\else \ifx\krd@default\krd@defna\else \krd@name=\ifin@ false\else\krdexpandonce\krd@default\fi \fi \fi }% \krdexpanded{% \toks@{\the\toks@ \krdncsname define@\krd@typefordef key\endcsname \if\krd@plustype+\fi [\krd@prefix]{\krd@family}% \ifnum\krd@typenr>\krdz@[\krd@macpref]\fi {\krd@name}% \ifnum\krd@typenr>\thr@@ [\krduserinput\krdorder]{\krdexpandonce\krd@altlist@a}% \fi \ifx\krd@default\krd@rej\else \ifx\krd@default\krd@defna\else [{\krdexpandonce\krd@default}]% \fi \fi {% callback-1 \ifnum\krd@typenr>\thr@@ \ifx\krd@itemfive\krd@rej\else \ifx\krd@itemfive\krd@defna\else \krdexpandonce\krd@itemfive \fi \fi \krd@executealt{########1}{\krdexpandonce\krd@altlist@b}% \else \ifx\krd@itemfour\krd@rej\else \ifx\krd@itemfour\krd@defna\else \krdexpandonce\krd@itemfour \fi \fi \fi }% \if\krd@plustype {% callback-2 \ifnum\krd@typenr>\thr@@ \ifx\krd@itemsix\krd@rej \noexpand\krd@keyvalerr \else \ifx\krd@itemsix\krd@defna \noexpand\krd@keyvalerr \else \krdexpandonce\krd@itemsix \fi \fi \else \ifx\krd@itemfive\krd@rej \noexpand\krd@keyvalerr \else \ifx\krd@itemfive\krd@defna \noexpand\krd@keyvalerr \else \krdexpandonce\krd@itemfive \fi \fi \fi }% \fi }% }% }% \edef\krd@list{\unexpanded{#4}}% % Don't normalize for slash, since double slash (meaning, eg, % "no value") will be made one slash. \krdforeach*[;]\krd@list\if01\fi{}{}{% \krd@splita##1/^?^/^?^/^?^/^?^/^?^/^?^/^?^/\krd@nil }% \krdexpanded{\endgroup \the\toks@ \noexpand\krdsetkeys[\krd@prefix]% {\krd@family}{\krdexpandonce\krd@vals}% }% \krdindeffalse } \protected\def\krd@gettypenr#1{% \def\reserved@a##1#1##2##3\krd@nil{% \def\krd@typenr{##2}% \ifx\krd@typenr\krd@nnil \krd@err{Invalid key type '#1'}\krd@ehd \else \edef\krd@typefordef{% % Using \numexpr will remove \relax, which is needed % to stop \ifcase's search for number. \ifcase\numexpr\krd@typenr\relax\or cmd\or bool\or bool\or choice\or choice\fi }% \fi }% \reserved@a ord{0}cmd{1}bool{2}bool+{3}choice{4}choice+{5}% #1{\krd@nil}\krd@nil \@expandtwoargs\in@{+\relax}{\krdoxdetok\krd@type\relax}% \edef\krd@plustype{0\ifin@ 0\else 1\fi}% } \protected\def\krd@keyvalerr{% \krd@getinnoval \krd@err{Erroneous value '\krd@ival' \MessageBreak for key or option '\XKV@tkey'} {Invalid value for key '\XKV@tkey'.}% } \protected\def\krd@getaltlist#1{% \begingroup \def\krd@altlist@a{}% \def\krd@altlist@b{}% \def\krd@g@taltlist##1.do=##2.do=##3\krd@nil{% \edef\XKV@tempa{\krdtrimspace{##1}}% \krdstripouterbraces{2}\XKV@tempa \edef\XKV@tempb{\krdoxdetok\XKV@tempa{\krdtrimspace{##2}}}% \def\@do####1####2{% \edef####1{% \krdexpandonce####1\ifx####1\@empty\else,\fi \krdexpandonce####2% }% }% \@do\krd@altlist@a\XKV@tempa \@do\krd@altlist@b\XKV@tempb }% \def\do##1{% \krd@g@taltlist##1.do=.do=\krd@nil }% \krdfor{#1}% \ifx\krd@altlist@a\@empty \krd@err{No nominations for choice key}\@ehd \def\krd@altlist@b{}% \fi \krdexpanded{\endgroup \krdaftergr\krd@altlist@a\krdaftergr\krd@altlist@b }% } \protected\def\krd@executealt#1#2{% \edef\reserved@a{\krdtrimspace{#1}}% \krdstripouterbraces{2}\reserved@a \edef\reserved@a{\krdoxdetok\reserved@a}% % There may be parameters characters in #2. So hide #2 in a macro: \edef\reserved@b{\unexpanded{#2}}% \def\reserved@c##1{% \def\reserved@a####1##1####2####3\krd@nil{% \edef\reserved@a{\unexpanded{####2}}% \krdifx\reserved@a\krd@nnil{% \krd@err{No choice match for key '\XKV@tkey'}\krd@ehd }{% ####2% }% }% \expandafter\reserved@a\reserved@b#1{\krd@nil}\krd@nil }% \krdusearg\reserved@c\reserved@a } \def\krd@lengthofival{20} \protected\def\krd@getinnoval{% \begingroup \def\reserved@a##1=##2=##3\krd@nil{% \def\krd@tval{##2}% }% \expandafter\reserved@a\CurrentOption==\krd@nil \krdifx\krd@tval\@empty{% \def\krd@ival{???}% }{% \def\krd@ival{}% \@tempcnta\krdz@ \def\do##1{% \def\reserved@a{##1}% \krdifx\reserved@a\krd@nnil{}{% \advance\@tempcnta\@ne \ifnum\@tempcnta<\krd@lengthofival\relax \edef\krd@ival{\krd@ival##1}% \expandafter\do \else \def\do####1\krd@nil{}% \expandafter\do \fi }% }% \expandafter\do\detokenize\expandafter{\krd@tval}\krd@nil }% \krdexpanded{\endgroup \def\noexpand\krd@ival{\krd@ival}% }% } \def\XKV@testopte#1{% \XKV@ifstar{\XKV@sttrue\XKV@t@stopte#1}{\XKV@stfalse\XKV@t@stopte#1}% } \def\XKV@t@stopte#1{\@testopt{\XKV@t@st@pte#1}{KV}} \def\XKV@t@st@pte#1[#2]{% \XKV@makepf{#2}% \@ifnextchar<{\XKV@@t@st@pte#1}% {\XKV@@t@st@pte#1<\@currname.\@currext>}% } \def\XKV@@t@st@pte#1<#2>{% \XKV@sp@deflist\XKV@fams{#2}% \@testopt#1{}% } \def\DeclareOptionX{% \let\@fileswith@pti@ns\@badrequireerror \XKV@ifstar\XKV@dox\XKV@d@x } \long\def\XKV@dox#1{\XKV@toks{#1}\edef\XKV@doxs{\the\XKV@toks}} \def\XKV@d@x{\@testopt\XKV@@d@x{KV}} \def\XKV@@d@x[#1]{% \@ifnextchar<{\XKV@@@d@x[#1]}{\XKV@@@d@x[#1]<\@currname.\@currext>}% } \def\XKV@@@d@x[#1]<#2>#3{\@testopt{\define@key[#1]{#2}{#3}}{}} \def\ExecuteOptionsX{\XKV@stfalse\XKV@plfalse\XKV@t@stopte\XKV@setkeys} \def\ProcessOptionsX{\XKV@plfalse\XKV@testopte\XKV@pox} \def\XKV@pox[#1]{% \let\XKV@tempa\@empty \XKV@inpoxtrue \let\@fileswith@pti@ns\@badrequireerror \edef\XKV@testclass{\@currname.\@currext}% \ifx\XKV@testclass\XKV@documentclass \let\@unusedoptionlist\XKV@classoptionslist \XKV@ifundefined{ver@xkvltxp.sty}{}{% \@onelevel@sanitize\@unusedoptionlist }% \else \ifXKV@st \def\XKV@tempb##1,{% \def\CurrentOption{##1}% \ifx\CurrentOption\@nnil\else \XKV@g@tkeyname##1=\@nil\CurrentOption \XKV@key@if@ndefined{\CurrentOption}{}{% \XKV@useoption{##1}% \XKV@addtolist@n\XKV@tempa{##1}% }% \expandafter\XKV@tempb \fi }% \expandafter\XKV@tempb\XKV@classoptionslist,\@nil,% \fi \fi \expandafter\XKV@addtolist@o\expandafter \XKV@tempa\csname opt@\@currname.\@currext\endcsname \def\XKV@tempb{\XKV@setkeys[#1]}% \expandafter\XKV@tempb\expandafter{\XKV@tempa}% \let\XKV@doxs\relax \let\XKV@rm\@empty \XKV@inpoxfalse \let\@fileswith@pti@ns\@@fileswith@pti@ns \AtEndOfPackage{\let\@unprocessedoptions\relax}% } \def\XKV@useoption#1{% \def\XKV@resa{#1}% \XKV@ifundefined{ver@xkvltxp.sty}{}{% \@onelevel@sanitize\XKV@resa }% \@expandtwoargs\@removeelement{\XKV@resa}% {\@unusedoptionlist}\@unusedoptionlist } \krdnewlet\krdDeclareOption\DeclareOptionX \krdnewlet\krdExecuteOptions\ExecuteOptionsX \krdnewlet\krdProcessOptions\ProcessOptionsX \krdDeclareOption[KV]{lengthofival}[20]{% \edef\krd@lengthofival{\number#1}% } \krdDeclareOption*{\PackageWarning{keyreader}{Unknown option '\CurrentOption' ignored}} \krdExecuteOptions[KV]{lengthofival} \krdProcessOptions[KV]\relax \endinput