diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/skeyval/tex |
Initial commit
Diffstat (limited to 'macros/latex/contrib/skeyval/tex')
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-bc.sty | 3194 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-core.tex | 8466 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-for.tex | 2862 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-ltxcmds.tex | 316 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-ltxpatch.sty | 444 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-pstkey.sty | 87 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-pstkey.tex | 94 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-testclass.cls | 321 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-testpkg.sty | 456 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval-view.sty | 425 | ||||
-rw-r--r-- | macros/latex/contrib/skeyval/tex/skeyval.sty | 971 |
11 files changed, 17636 insertions, 0 deletions
diff --git a/macros/latex/contrib/skeyval/tex/skeyval-bc.sty b/macros/latex/contrib/skeyval/tex/skeyval-bc.sty new file mode 100644 index 0000000000..6d90b9958e --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-bc.sty @@ -0,0 +1,3194 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-bc.sty', version 0.73 of skeyval package, % +% 2011/01/01. % +% NOTE % +% This file is provided only for backward compatibility (bc) of the % +% skeyval package. Beginning from version 1.0, September 2012, the % +% package has changed radically. Users of pre-version 1.0 of the package % +% can load it now by calling: % +% % +% \usepackage[compatibility]{skeyval} % +% \RequirePackage[compatibility]{skeyval} % +% or % +% \usepackage{skeyval-bc} % +% \RequirePackage{skeyval-bc} % +% % +% 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\begingroup +\catcode064 11% @ +\catcode123 01% { +\catcode125 02% } +\def\SKV@tempa{\endgroup + \edef\SKV@restorecodes{% + \catcode035 \the\catcode035% # + \catcode064 \the\catcode064% @ + \catcode123 \the\catcode123% { + \catcode125 \the\catcode125% } + \catcode061 \the\catcode061% = + }% +} +\SKV@tempa +\catcode035 06% # +\catcode061 12% = +\catcode064 11% @ +\catcode123 01% { +\catcode125 02% } +\def\SKV@setcode#1#2{% + \edef\SKV@restorecodes{% + \SKV@restorecodes + \catcode#1 \the\catcode#1\relax + }% + \catcode#1 #2\relax +} +\AtEndOfPackage{% + \SKV@AfterPackageEnd{% + \SKV@restorecodes + \SKV@Undef!{\SKV@setcode\SKV@restorecodes}% + }% +} +\SKV@setcode{010}{12}% ^^J +\SKV@setcode{032}{10}% SP +\SKV@setcode{033}{12}% ! +\SKV@setcode{036}{03}% $ +\SKV@setcode{038}{03}% & +\SKV@setcode{040}{12}% ( +\SKV@setcode{041}{12}% ) +\SKV@setcode{042}{12}% * +\SKV@setcode{043}{12}% + +\SKV@setcode{044}{12}% , +\SKV@setcode{045}{12}% - +\SKV@setcode{047}{12}% / +\SKV@setcode{058}{12}% : +\SKV@setcode{059}{12}% ; +\SKV@setcode{060}{12}% > +\SKV@setcode{062}{12}% < +\SKV@setcode{063}{12}% ? +\SKV@setcode{091}{12}% [ +\SKV@setcode{093}{12}% ] +\SKV@setcode{096}{12}% ` +\SKV@setcode{126}{13}% ~ +\def\SKV@rcsid$#1: #2 #3 #4 #5${#4 v#3} +\ProvidesPackage{skeyval}[% + \SKV@rcsid $Id: skeyval.sty,v 0.73 2011/01/01 09:00:00 Ahmed Musa $ + Extensions to the xkeyval parser (AM) +] +\NeedsTeXFormat{LaTeX2e}[1996/12/01] +\ifnum0=0% + \ifx\eTeXversion\SKV@undefined1\else\ifnum\eTeXversion<\tw@1\fi\fi +\else + \PackageError{skeyval}{eTeX not loaded or old version}% + {This package requires eTeX version 2 or later.}% + \endinput +\fi +\newtoks\SKV@toks +\newcount\SKV@depth +\protected\def\@nil{\@nil} +\def\@nnil{\@nil} +\def\@space{ } +\long\def\@firstofthree#1#2#3{#1} +\long\def\@secondofthree#1#2#3{#2} +\long\def\@thirdofthree#1#2#3{#3} +\long\def\@gobblethree#1#2#3{} +\long\def\SKV@car#1#2\@nil{#1} +\long\def\SKV@cdr#1#2\@nil{#2} +\long\def\SKV@carcube#1#2#3#4\@nil{#1#2#3} +\def\SKV@threexp{\expandafter\expandafter\expandafter} +\def\SKV@aftercs#1#2{\expandafter#1\csname#2\endcsname} +\def\SKV@xaftercs#1#2{\SKV@threexp#1\csname#2\endcsname} +\SKV@aftercs\let{SKV@1of1}\@firstofone +\SKV@aftercs\let{SKV@1of2}\@firstoftwo +\SKV@aftercs\let{SKV@2of2}\@secondoftwo +\SKV@aftercs\let{SKV@1of3}\@firstofthree +\SKV@aftercs\let{SKV@2of3}\@secondofthree +\SKV@aftercs\let{SKV@3of3}\@thirdofthree +\def\SKV@ifx#1#2{\@nameuse{SKV@\ifx#1#21\else2\fi of2}} +\def\SKV@unifx#1#2{\@nameuse{SKV@\ifx#1#22\else1\fi of2}} +\def\SKV@csifx#1#2{\begingroup\expandafter\endgroup\expandafter + \SKV@ifx\csname#1\endcsname#2} +\def\SKV@uncsifx#1#2{\begingroup\expandafter\endgroup\expandafter + \SKV@unifx\csname#1\endcsname#2} +\def\SKV@ifxcs#1#2{\begingroup\expandafter\endgroup\expandafter + \SKV@ifx\expandafter#1\csname#2\endcsname} +\def\SKV@unifxcs#1#2{\begingroup\expandafter\endgroup\expandafter + \SKV@unifx\expandafter#1\csname#2\endcsname} +\def\SKV@ifdefined#1{\@nameuse{SKV@\ifdefined#11\else2\fi of2}} +\def\SKV@oifdefined#1{\@nameuse{SKV@\expandafter\SKV@ifdefined#1{1}{2}of2}} +\def\SKV@unifdefined#1{\@nameuse{SKV@\SKV@ifdefined{#1}{2}{1}of2}} +\def\SKV@ifcsname#1{\@nameuse{SKV@\ifcsname#1\endcsname1\else2\fi of2}} +\def\SKV@unifcsname#1{\@nameuse{SKV@\SKV@ifcsname{#1}{2}{1}of2}} +\def\SKV@ifnum#1#2#3{\@nameuse{SKV@\ifnum#1#2#3 1\else2\fi of2}} +\protected\def\SKV@AtEndAfterPackageEnd#1{\AtEndOfPackage + {\SKV@AfterPackageEnd{#1}}} +\begingroup +\def\SKV@tempa{% + \def\SKV@nodocerr{% + \PackageError{skeyval}{'skeyval' package loaded before + \string\documentclass}{Load skeyval package after + \string\documentclass.}% + \endinput + }% + \def\SKV@tempb##1\@nil,{}% + \def\SKV@tempa##1,{% + \def\@currname{##1}% + \SKV@ifx\SKV@documentclass\SKV@undefined{% + \SKV@ifx\@currname\@nnil{}{% + \filename@parse\@currname + \SKV@unifx\filename@ext\@clsextension{}{% + \SKV@unifcsname{opt@\filename@area\filename@base.% + \filename@ext}{}{% + \edef\SKV@documentclass{\filename@area% + \filename@base.\filename@ext}% + }% + }% + \SKV@tempa + }% + }{% + \let\SKV@tempa\SKV@tempb\SKV@tempa + }% + }% + \SKV@ifx\@filelist\relax\SKV@nodocerr{% + \SKV@ifx\@filelist\@gobble\SKV@nodocerr{% + \expandafter\SKV@tempa\@filelist,\@nil,% + }% + }% + \SKV@ifx\SKV@documentclass\SKV@undefined\SKV@nodocerr\relax +} +\SKV@tempa\endgroup +\def\SKV@notdefinable{% + \PackageError{skeyval}% + {Command '\@backslashchar\resvd@a' not definable, + \MessageBreak or name '\@backslashchar\@qend...' illegal}% + {Command '\@backslashchar\resvd@a' already defined.}% +} +\def\SKV@ifdefinable#1{% + \begingroup + \escapechar92\relax + \xdef\resvd@a{\expandafter\@gobble\string#1}% + \edef\resvd@b{\expandafter\SKV@car\string#1\@nil}% + \SKV@ifx\resvd@b\@backslashchar{}{% + \PackageError{skeyval}{Backslash required}{Backslash + required in argument of '\string\SKV@ifdefinable'.}% + }% + \SKV@ifdefined#1{% + \SKV@ifx{#1}\relax{\aftergroup\@firstofone}% + {\aftergroup\SKV@notdefinable}% + }{% + \edef\resvd@b{\unexpanded\expandafter{\csname end\resvd@a\endcsname}}% + \expandafter\SKV@ifx\resvd@b\relax{% + \SKV@ifx\resvd@a\@qrelax{\aftergroup\SKV@notdefinable}% + {\aftergroup\@firstofone} + }{\aftergroup\SKV@notdefinable}% + }% + \endgroup +} +\def\SKV@csifdefinable#1{\begingroup\expandafter\endgroup + \expandafter\SKV@ifdefinable\csname#1\endcsname} +\newcommand*\@ifpackagecurrent[2]{% + \@nameuse{SKV@\ifnum\SKV@threexp\@parse@version + \csname ver@#1.\@pkgextension\endcsname//00\@nil + <\expandafter\@parse@version#2//00\@nil2\else1\fi of2}% +} +\newcommand*\@ifpackagenotcurrent[2]{% + \@nameuse{SKV@\@ifpackagecurrent{#1}{#2}{2}{1}of2}} +\newcommand*\@ifloadedandcurrent[2]{% + \begingroup + \@ifpackageloaded{#1}{% + \@ifpackagecurrent{#1}{#2}{\aftergroup\@firstofthree}{% + \aftergroup\@secondofthree}}{\aftergroup\@thirdofthree}% + \endgroup +} +\@ifloadedandcurrent{xkeyval}{2008/08/13}{}{% + \PackageError{skeyval}% + {Your loaded version of xkeyval package is not current}% + {You need version 2.6a (or higher) of xkeyval package.}% + \endinput +}{\RequirePackage{xkeyval}[2008/08/13]} +\newcommand*\SKV@ifdefinable@n[1]{% + \XKV@sp@deflist\SKV@tempa{#1}% + \XKV@for@o\SKV@tempa\SKV@tempa{% + \edef\resvd@a{\SKV@threexp\SKV@car + \expandafter\string\SKV@tempa\@nil}% + \SKV@unifx\resvd@a\@backslashchar{}{% + \PackageError{skeyval}{Backslash not allowed}{Backslash not + allowed in argument of '\string\SKV@ifdefinable@n'.}% + }% + \SKV@csifdefinable\SKV@tempa\relax + }% +} +\SKV@ifdefinable@n{SKV@def,SKV@d@f,SKV@pdef,SKV@pd@f} +\protected\def\SKV@def{\@star@or@long\SKV@d@f} +\def\SKV@d@f#1{\SKV@ifdefinable#1{\l@ngrel@x\def#1}} +\protected\def\SKV@pdef{\@star@or@long\SKV@pd@f} +\def\SKV@pd@f#1{\SKV@ifdefinable#1{\l@ngrel@x\protected\def#1}} +\SKV@pdef*\SKV@gdef{\@star@or@long\SKV@gd@f} +\SKV@def*\SKV@gd@f#1{\SKV@ifdefinable#1{\l@ngrel@x\gdef#1}} +\SKV@pdef*\SKV@edef{\@star@or@long\SKV@ed@f} +\SKV@def*\SKV@ed@f#1{\SKV@ifdefinable#1{\l@ngrel@x\edef#1}} +\SKV@pdef*\SKV@pgdef{\@star@or@long\SKV@pgd@f} +\SKV@def*\SKV@pgd@f#1{\SKV@ifdefinable#1{\l@ngrel@x\protected\gdef#1}} +\SKV@pdef*\SKV@pedef{\@star@or@long\SKV@ped@f} +\SKV@def*\SKV@ped@f#1{\SKV@ifdefinable#1{\l@ngrel@x\protected\edef#1}} +\SKV@pdef*\SKV@pxdef{\@star@or@long\SKV@pxd@f} +\SKV@def*\SKV@pxd@f#1{\SKV@ifdefinable#1{\l@ngrel@x\protected\xdef#1}} +\SKV@pdef*\SKV@redef{\@star@or@long\SKV@red@f} +\SKV@def*\SKV@red@f#1{% + \SKV@ensurescape{#1}% + \SKV@unifdefined{#1}{}{% + \SKV@ifx{#1}\relax{}{% + \SKV@vbmsg{info}{Command '\string#1' redefined\on@line}% + }% + }% + \l@ngrel@x\def#1% +} +\SKV@pdef*\SKV@csredef{\SKV@aftercs\SKV@redef} +\SKV@pdef*\SKV@predef{\@star@or@long\SKV@pred@f} +\SKV@def*\SKV@pred@f#1{% + \SKV@ensurescape{#1}% + \SKV@unifdefined{#1}{}{% + \SKV@ifx{#1}\relax{}{% + \SKV@vbmsg{info}{Command '\string#1' redefined\on@line}% + }% + }% + \l@ngrel@x\protected\def#1% +} +\SKV@pdef*\SKV@cspredef{\SKV@aftercs\SKV@predef} +\SKV@def*\SKV@vbmsg#1#2{% + \begingroup + \def\@i{info}\def\@ii{warn}\def\@iii{#1}% + \@nameuse{SKV@\ifSKV@verbose1\else2\fi of2}% + {\SKV@ifx\@i\@iii + {\SKV@info{#2}}% + {\SKV@ifx\@ii\@iii{\SKV@warn{#2}}{}}% + }{}% + \endgroup +} +\define@boolkeys[KV]{skeyval@i}[SKV@]{st,pl,cl,rt, + lt,sw,sk,loc,uv}[true] +\setkeys[KV]{skeyval@i}{st,pl,cl,rt,lt,sw,sk,loc,uv} +\define@boolkey+[KV]{skeyval}[SKV@]{verbose}[true]{}{% + \SKV@inputerr{#1}{verbose}% +} +\setkeys[KV]{skeyval}{verbose=false} +\SKV@pdef\SKV@info#1{\PackageInfo{skeyval}{#1\@gobble}} +\SKV@pdef\SKV@warn#1{\PackageWarningNoLine{skeyval}{#1}} +\SKV@pdef\SKV@err#1#2{\PackageError{skeyval}{#1}{#2}} +\SKV@def*\SKV@MB{\MessageBreak} +\SKV@pdef\SKV@afterfi#1\fi{\fi#1} +\SKV@pdef\SKV@afterelsefi#1\else#2\fi{\fi#1} +\SKV@pdef\SKV@afterfifi#1\fi#2\fi{\fi#2\fi#1} +\SKV@def*\SKV@badargerr#1#2{% + \SKV@err{Invalid #2 argument for command '\@backslashchar#1'}% + {Bad #2 argument for command '\@backslashchar#1'.\SKV@MB + You need to look again at user instruction\SKV@MB + for command '\@backslashchar#1'.}% +} +\SKV@def*\SKV@state#1#2#3{% + \ifcase#1\relax + \def\SKV@ste{XKV@prefix,XKV@fams,XKV@tkey,XKV@na,ifXKV@st,% + ifXKV@pl,ifXKV@knf,CurrentOption,ifSKV@sk}% + \SKV@ifempty{#3}{\advance\XKV@depth\@ne}{}% + \or + \def\SKV@ste{ifSKV@st,ifSKV@pl,ifSKV@cl,ifSKV@rt,ifSKV@lt}% + \SKV@ifempty{#3}{\advance\SKV@depth\@ne}{}% + \else + \SKV@err{Unknown state identifier '#1'}% + {I expected '\string#1' to be number 0 or 1.}% + \fi + \XKV@for@o\SKV@ste\SKV@ste{% + \SKV@aftercs\let{\SKV@ste#2\SKV@aftercs}{\SKV@ste#3}% + }% + \ifcase#1\relax + \SKV@ifempty{#2}{\advance\XKV@depth\m@ne}{}% + \or + \SKV@ifempty{#2}{\advance\SKV@depth\m@ne}{}% + \fi +} +\SKV@def*\XKV@savstate{\SKV@state{0}{@\romannumeral\XKV@depth}{}} +\SKV@def*\XKV@resstate{\SKV@state{0}{}{@\romannumeral\XKV@depth}} +\SKV@def*\SKV@savstate{\SKV@state{1}{@\romannumeral\SKV@depth}{}} +\SKV@def*\SKV@resstate{\SKV@state{1}{}{@\romannumeral\SKV@depth}} +\def\XKV@srstate#1#2{\SKV@state{0}{#1}{#2}} +\SKV@def*\SKV@scantoksa{\SKV@savstate\SKV@testoptf\SKV@sc@ntoksa} +\SKV@def\SKV@sc@ntoksa[#1]#2{% + \ifSKV@cl\let\gl@l@x\global\else\let\gl@l@x\relax\fi + \SKV@for[#1]{#2}\SKV@resvda{% + \ifSKV@pl + \SKV@ifcsdef\SKV@resvda{}{% + \SKV@err{Command '\SKV@csexpox\SKV@resvda' undefined}% + {I can't scan contents of undefined command + '\SKV@csexpox\SKV@resvda'}% + }% + \SKV@expnext\SKV@ensurenoscape\SKV@resvda + \expandafter\let\expandafter\SKV@resvdb% + \csname\SKV@resvda\endcsname + \else + \expandafter\SKV@ifdef\SKV@resvda{}{% + \SKV@err{Command '\SKV@expox\SKV@resvda' undefined}% + {I can't scan contents of undefined command + '\SKV@expox\SKV@resvda'}% + }% + \SKV@expnext\SKV@ensurescape\SKV@resvda + \expandafter\let\expandafter\SKV@resvdb\SKV@resvda + \fi + \SKV@noexpparse\SKV@resvdb + \begingroup + \edef\SKV@resvdb##1##2{##1{##2{\SKV@expox\SKV@resvdb}}}% + \everyeof{\noexpand}\endlinechar\m@ne\makeatletter + \SKV@resvdb{\xdef\SKV@resvdb}{\scantokens\expandafter}% + \endgroup + \ifSKV@pl + \gl@l@x\SKV@aftercs\let\SKV@resvda\SKV@resvdb + \else + \gl@l@x\expandafter\let\SKV@resvda\SKV@resvdb + \fi + }% + \SKV@resstate +} +\SKV@def*\SKV@emptycmds{\SKV@savstate\SKV@testoptf\SKV@em@tycmds} +\SKV@def\SKV@em@tycmds[#1]#2{% + \ifSKV@cl\let\gl@l@x\global\else\let\gl@l@x\relax\fi + \SKV@for[#1]{#2}\SKV@resvda{% + \ifSKV@pl + \SKV@expnext\SKV@ensurenoscape\SKV@resvda + \gl@l@x\SKV@aftercs\let\SKV@resvda\@empty + \else + \SKV@expnext\SKV@ensurescape\SKV@resvda + \gl@l@x\expandafter\let\SKV@resvda\@empty + \fi + }% + \SKV@resstate +} +\SKV@pdef*\SKV@csdef{\SKV@savstate\SKV@t@stoptb{\SKV@newcs\def}} +\SKV@pdef*\SKV@csgdef{\SKV@savstate\SKV@t@stoptb{\SKV@newcs\gdef}} +\SKV@pdef*\SKV@csedef{\SKV@savstate\SKV@t@stoptb{\SKV@newcs\edef}} +\SKV@pdef*\SKV@csxdef{\SKV@savstate\SKV@t@stoptb{\SKV@newcs\xdef}} +\SKV@def*\SKV@newcs#1#2{% + \ifSKV@st\let\l@ngrel@x\relax\else\let\l@ngrel@x\long\fi + \ifSKV@pl + \let\SKV@next\SKV@n@wcs + \else + \def\SKV@next{\l@ngrel@x\SKV@aftercs}% + \fi + \SKV@resstate\SKV@next#1{#2}% +} +\SKV@def*\SKV@n@wcs#1#2{% + \SKV@ifcsname{#2}% + {\SKV@csifx{#2}\relax{}{\def\resvd@a{#2}\SKV@notdefinable}}% + {\l@ngrel@x\SKV@aftercs{#1}{#2}}% +} +\SKV@def*\SKV@csuse#1{\SKV@ifcsname{#1}{\@nameuse{#1}}{}} +\SKV@pdef\SKV@expargs#1#2#3#4\@nil{% + \begingroup + \protected@edef\SKV@tempa{\endgroup + \ifcase#1\relax + \unexpanded{#2}{#3}% + \or + \unexpanded{#2#3}{#4}% + \or + \unexpanded{#2}{#3}{#4}% + \or + \unexpanded{#2}#3\unexpanded{#4}% + \else + \SKV@err{Unknown expansion type '\string#1' for + \string\SKV@expargs}{I expected your first + argument '\string#1' to be between 0 and 3.}% + \fi + }% + \SKV@tempa +} +\SKV@def*\SKV@findescape#1{% + \begingroup\escapechar92\relax + \edef\resvd@a{\expandafter\SKV@car\string#1\@nil}% + \expandafter\aftergroup\csname SKV@% + \SKV@ifx\resvd@a\@backslashchar{1}{2}of2\endcsname + \endgroup +} +\SKV@def*\SKV@ensurescape#1{% + \SKV@findescape{#1}{}% + {\SKV@err{Backslash required in '\string#1'}% + {I expected a backslash to be the leading\SKV@MB + character in your argument '\string#1'.}}% +} +\SKV@def*\SKV@ensurenoscape#1{% + \SKV@findescape{#1}% + {\SKV@err{Backslash not allowed in '\string#1'}% + {I didn't expect a backslash to be the leading\SKV@MB + character in your argument '\string#1'.}}{}% +} +\SKV@pdef\SKV@ifnextchar#1#2#3{% + \let\SKV@nxc#1\def\SKV@nxa{#2}\def\SKV@nxb{#3}% + \futurelet\@temptoks\SKV@ifnextch@r +} +\SKV@def*\SKV@ifnextch@r{% + \ifx\@temptoks\SKV@nxc\let\SKV@nxb\SKV@nxa\fi\SKV@nxb +} +\SKV@pdef\SKV@ifchar#1#2#3{% + \SKV@ifnextchar#1{\begingroup + \def\SKV@tempa#1{\endgroup#2}\SKV@tempa}{#3}% +} +\SKV@pdef*\SKV@ifstar{\SKV@ifchar{*}} +\SKV@pdef*\SKV@ifplus{\SKV@ifchar{+}} +\SKV@pdef*\SKV@ifask{\SKV@ifchar{?}} +\SKV@pdef*\SKV@ifclam{\SKV@ifchar{!}} +\SKV@pdef*\SKV@ifright{\SKV@ifchar{>}} +\SKV@pdef*\SKV@ifleft{\SKV@ifchar{<}} +\SKV@pdef*\SKV@if@brack#1#2{\SKV@ifnextchar[{#1}{#2}} +\SKV@pdef*\SKV@if@paren#1#2{\SKV@ifnextchar({#1}{#2}} +\SKV@pdef*\SKV@if@left#1#2{\SKV@ifnextchar<{#1}{#2}} +\SKV@pdef*\SKV@testopt#1#2{\SKV@if@brack{#1}{#1[{#2}]}} +\SKV@pdef*\SKV@testopta#1{\SKV@t@stopta{\SKV@t@st@pta{#1}}} +\SKV@pdef*\SKV@t@stopta#1{\SKV@ifstar{\SKV@sttrue#1}{\SKV@stfalse#1}} +\SKV@pdef*\SKV@t@st@pta#1{\SKV@ifplus{\SKV@pltrue#1}{\SKV@plfalse#1}} +\SKV@pdef*\SKV@t@st@p@a#1{\SKV@ifclam{\SKV@cltrue#1}{\SKV@clfalse#1}} +\SKV@pdef*\SKV@testoptb#1{\SKV@t@stopta{\SKV@t@st@pta{\SKV@t@st@p@a{#1}}}} +\SKV@pdef*\SKV@t@stoptb#1{\SKV@t@stopta{\SKV@t@st@pta{#1}}} +\SKV@pdef*\SKV@testoptc#1{\SKV@t@st@pta{\SKV@t@st@p@a{#1}}} +\SKV@pdef*\SKV@testoptd#1{\SKV@t@stopta{\SKV@testopt#1{,}}} +\SKV@pdef*\SKV@testopte#1{\SKV@testoptb{\SKV@testopt#1{,}}} +\SKV@pdef*\SKV@t@stopte#1{\SKV@t@stoptb{\SKV@testopt#1{,}}} +\SKV@pdef*\SKV@testoptf#1{\SKV@testoptc{\SKV@testopt#1{,}}} +\SKV@pdef*\SKV@testoptg#1{\SKV@ifright{\SKV@rttrue#1}{\SKV@rtfalse#1}} +\SKV@pdef*\SKV@t@stoptg#1{\SKV@ifleft{\SKV@lttrue#1}{\SKV@ltfalse#1}} +\SKV@pdef*\SKV@testopth#1#2{\SKV@if@left{#1}{#1<{#2}>}} +\SKV@pdef*\SKV@testoptp#1#2{\SKV@if@paren{#1}{#1({#2})}} +\SKV@def*\SKV@Test@pt#1{\@nameuse{SKV@\SKV@ifempty{#1}{1}{2}of2}} +\SKV@def*\SKV@IfBrack#1#2#3{\SKV@IfBr@ck#1&[&{#2}{#3{#1}}} +\SKV@def*\SKV@IfBr@ck#1[#2#{\SKV@Test@pt{#1#2}} +\SKV@def*\SKV@TestOpt#1#2#3{\SKV@IfBr@ck#1&[&{#2#1}{#2[{#3}]{#1}}} +\SKV@def*\SKV@IfStar#1#2#3{\SKV@IfSt@r#1&*&{#2}{#3{#1}}} +\SKV@def*\SKV@IfSt@r#1*#2#{\SKV@Test@pt{#1#2}} +\SKV@def*\SKV@IfPlus#1#2#3{\SKV@IfPl@s#1&+&{#2}{#3{#1}}} +\SKV@def*\SKV@IfPl@s#1+#2#{\SKV@Test@pt{#1#2}} +\SKV@def*\SKV@IfAsk#1#2#3{\SKV@IfA@k#1&?&{#2}{#3{#1}}} +\SKV@def*\SKV@IfA@k#1?#2#{\SKV@Test@pt{#1#2}} +\SKV@def*\SKV@csalloc#1{\@nameuse{new#1\SKV@aftercs}} +\SKV@pdef*\csnewif{\SKV@csalloc{if}} +\SKV@pdef*\csnewtoks{\SKV@csalloc{toks}} +\SKV@pdef*\csnewcount{\SKV@csalloc{count}} +\SKV@pdef*\csnewdimen{\SKV@csalloc{dimen}} +\SKV@pdef*\csnewbox{\SKV@csalloc{box}} +\SKV@pdef*\csnewwrite{\SKV@csalloc{write}} +\SKV@pdef*\csnewread{\SKV@csalloc{read}} +\SKV@pdef*\NewWrites{\SKV@swtrue\SKV@testopt\SKV@NewWrites{}} +\SKV@pdef*\DefWrites{\SKV@swfalse\SKV@testopt\SKV@NewWrites{}} +\SKV@def*\SKV@NewWrites[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Write stream '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewwrite{#1\SKV@tempa}% + }% +} +\SKV@pdef*\NewReads{\SKV@swtrue\SKV@testopt\SKV@NewReads{}} +\SKV@pdef*\DefReads{\SKV@swfalse\SKV@testopt\SKV@NewReads{}} +\SKV@def*\SKV@NewReads[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Read stream '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewread{#1\SKV@tempa}% + }% +} +\SKV@pdef*\NewIfs{\SKV@swtrue\SKV@testopt\SKV@NewIfs{}} +\SKV@pdef*\DefIfs{\SKV@swfalse\SKV@testopt\SKV@NewIfs{}} +\SKV@def*\SKV@NewIfs[#1]#2{\SKV@testopt{\SKV@N@wIfs[#1]{#2}}{false}} +\SKV@def*\SKV@N@wIfs[#1]#2[#3]{% + \ifSKV@sw + \SKV@getswstate\NewIfs{#3}% + \else + \SKV@getswstate\DefIfs{#3}% + \fi + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \begingroup + \SKV@oifstrequal{\SKV@tempa}{x}{\SKV@swnameerr{x}}% + {\SKV@oifstrequal{\SKV@tempa}{on}{\SKV@swnameerr{on}}% + {\SKV@oifstrequal{\SKV@tempa}{off}{\SKV@swnameerr{off}}% + {\SKV@oifstrequal{\SKV@tempa}{tog}{\SKV@swnameerr{tog}}% + {\SKV@oifstrequal{\SKV@tempa}{switch}{\SKV@swnameerr{switch}}% + {}}}}}\endgroup + \ifSKV@sw + \SKV@csifdefinable{if#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{if#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Boolean '\@backslashchar + if#1\SKV@tempa' redefined\on@line}% + }% + \fi + \SKV@aftercs\SKV@N@wIf@{if#1\SKV@tempa}% + \@nameuse{#1\SKV@tempa\SKV@swval}% + }% +} +\SKV@def*\SKV@N@wIf@#1{% + \@tempcnta\escapechar\escapechar\m@ne + \let#1\iffalse + \SKV@If#1\iftrue + \SKV@If#1\iffalse + \let\ifon\iftrue + \let\ifoff\iffalse + \SKV@If#1\ifon + \SKV@If#1\ifoff + \escapechar\@tempcnta +} +\def\SKV@If#1#2{% + \SKV@aftercs\def{\expandafter\@gobbletwo\string#1% + \expandafter\@gobbletwo\string#2}{\let#1#2}% +} +\SKV@pdef*\NewBooleans{\SKV@swtrue\SKV@testopt\SKV@NewBooleans{}} +\SKV@pdef*\DefBooleans{\SKV@swfalse\SKV@testopt\SKV@NewBooleans{}} +\SKV@def*\SKV@NewBooleans[#1]#2{% + \SKV@testopt{\SKV@B@oleans[#1]{#2}}{false}} +\SKV@def*\SKV@B@oleans[#1]#2[#3]{% + \ifSKV@sw + \SKV@getswstate\NewBooleans{#3}% + \else + \SKV@getswstate\DefBooleans{#3}% + \fi + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \SKV@oifstrequal{\SKV@tempa}{tog}{% + \SKV@err{'tog' not allowed as a boolean name}\@ehc + }{% + \SKV@oifstrequal{\SKV@tempa}{switch}{% + \SKV@err{'switch' not allowed as a boolean name}\@ehc + }{}% + }% + \SKV@ifstrnotequal{#1\SKV@tempa}{x}{}{% + \SKV@err{'\string\ifx' is a TeX primitive}% + {Why do you want to redefine '\string\ifx'? This is dangerous.}% + }% + \ifSKV@sw + \SKV@csifdefinable{if#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{if#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Switch '\@backslashchar if#1\SKV@tempa' + redefined\on@line}% + }% + \fi + \csnewif{if#1\SKV@tempa}% + \@nameuse{#1\SKV@tempa\SKV@swval}% + }% +} +\SKV@pdef*\NewTogs{\SKV@swtrue\SKV@testopt\SKV@NewTogs{}} +\SKV@pdef*\DefTogs{\SKV@swfalse\SKV@testopt\SKV@NewTogs{}} +\SKV@def*\SKV@NewTogs[#1]#2{\SKV@testopt{\SKV@NewT@gs[#1]{#2}}{false}} +\SKV@def*\SKV@NewT@gs[#1]#2[#3]{% + \ifSKV@sw + \SKV@getswstate\NewTogs{#3}% + \else + \SKV@getswstate\DefTogs{#3}% + \fi + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \edef\SKV@tempa{#1\SKV@tempa}% + \ifSKV@sw + \SKV@expnext\newtog\SKV@tempa + \else + \SKV@expnext\deftog\SKV@tempa + \fi + \SKV@expargs{0}{\@nameuse{tog\SKV@swval}}\SKV@tempa\@nil + }% +} +\SKV@pdef*\NewToks{\SKV@swtrue\SKV@testopt\SKV@NewToks{}} +\SKV@pdef*\DefToks{\SKV@swfalse\SKV@testopt\SKV@NewToks{}} +\SKV@def*\SKV@NewToks[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Token register '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewtoks{#1\SKV@tempa}% + }% +} +\SKV@pdef*\NewCounts{\SKV@swtrue\SKV@testopt\SKV@NewCounts{}} +\SKV@pdef*\DefCounts{\SKV@swfalse\SKV@testopt\SKV@NewCounts{}} +\SKV@def*\SKV@NewCounts[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Counter register '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewcount{#1\SKV@tempa}% + }% +} +\SKV@pdef*\NewDimens{\SKV@swtrue\SKV@testopt\SKV@NewDimens{}} +\SKV@pdef*\DefDimens{\SKV@swfalse\SKV@testopt\SKV@NewDimens{}} +\SKV@def*\SKV@NewDimens[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Dimension register '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewdimen{#1\SKV@tempa}% + }% +} +\SKV@pdef*\NewBoxes{\SKV@swtrue\SKV@testopt\SKV@NewBoxes{}} +\SKV@pdef*\DefBoxes{\SKV@swfalse\SKV@testopt\SKV@NewBoxes{}} +\SKV@def*\SKV@NewBoxes[#1]#2{% + \SKV@for[,]{#2}\SKV@tempa{% + \SKV@expnext\SKV@ensurenoscape\SKV@tempa + \ifSKV@sw + \SKV@csifdefinable{#1\SKV@tempa}\relax + \else + \SKV@ifcsundef{#1\SKV@tempa}{}{% + \SKV@vbmsg{warn}{Box register '\@backslashchar + #1\SKV@tempa' redefined\on@line}% + }% + \fi + \csnewbox{#1\SKV@tempa}% + }% +} +\SKV@def\SKV@expnext#1#2{% + \SKV@ifblank{#1}{% + \SKV@badargerr{SKV@expnext}{first}% + }{% + \SKV@ifblank{#2}{% + \SKV@badargerr{SKV@expnext}{second}% + }{% + \ifcat$\detokenize\expandafter{\SKV@cdr#1\@nil}$% + \SKV@afterelsefi + \expandafter#1\expandafter{#2}% + \else + \SKV@afterfi + \SKV@badargerr{SKV@expnext}{first}% + \fi + }% + }% +} +\SKV@pdef*\SKV@ifnewcmd{\SKV@savstate\SKV@t@stopte\SKV@ifn@wcmd} +\SKV@def*\SKV@ifn@wcmd[#1]#2{% + \ifSKV@st + \ifSKV@pl + \def\SKV@ndl{\SKV@for*[#1]#2}% + \else + \def\SKV@ndl{\SKV@tfor*#2}% + \fi + \else + \ifSKV@pl + \def\SKV@ndl{\SKV@for[#1]{#2}}% + \else + \def\SKV@ndl{\SKV@tfor{#2}}% + \fi + \fi + \SKV@ndl\SKV@ndl{% + \ifSKV@pl + \SKV@despacectt\SKV@ndl + \SKV@csifdefinable\SKV@ndl\relax + \else + \expandafter\SKV@ifdefinable\SKV@ndl\relax + \fi + }% + \SKV@resstate +} +\let\SKV@no\z@ +\SKV@pdef*\SKV@aftergroup{% + \SKV@ifstar{\SKV@aft@rgroup\@firstofone}% + {\SKV@aft@rgroup\unexpanded}% +} +\SKV@def\SKV@aft@rgroup#1#2{% + \edef\SKV@no{\the\numexpr\SKV@no+1}% + \SKV@aftercs\xdef{SKV@aft@rgr@up\SKV@no}{% + \SKV@csgundef{SKV@aft@rgr@up\SKV@no}% + #1{#2}% + }% + \SKV@aftercs\aftergroup{SKV@aft@rgr@up\SKV@no}% +} +\SKV@pdef*\SKV@afterassignment{% + \SKV@ifstar{\SKV@aft@rassignment\@firstofone}% + {\SKV@aft@rassignment\unexpanded}% +} +\SKV@def\SKV@aft@rassignment#1#2{% + \begingroup + \edef\SKV@no{\the\numexpr\SKV@no+1}% + \SKV@aftercs\xdef{SKV@aft@rassignm@nt\SKV@no}{% + \SKV@csgundef{SKV@aft@rassignm@nt\SKV@no}% + #1{#2}% + }% + \expandafter\endgroup\expandafter\afterassignment + \csname SKV@aft@rassignm@nt\SKV@no\endcsname +} +\SKV@def*\SKV@sp@def#1#2{% + \edef\@sp@def{\the\SKV@toks}% + \SKV@toks\expandafter{% + \romannumeral-`\q\SKV@sp@d@f\noexpand\noexpand#1& &}% + \edef#2{\the\SKV@toks}% + \SKV@toks\expandafter{\@sp@def}% + \SKV@undef\@sp@def +} +\SKV@def*\SKV@sp@d@f#1 &{\SKV@sp@d@f@#1&} +\SKV@def*\SKV@sp@d@f@#1{\romannumeral-`\q#1} +\SKV@def*\SKV@expox#1{\unexpanded\expandafter{#1}} +\SKV@def*\SKV@csexpox#1{\SKV@aftercs\SKV@expox{#1}} +\SKV@def*\SKV@exptx#1{\unexpanded\SKV@threexp{#1}} +\SKV@def*\SKV@csexptx#1{\SKV@aftercs\SKV@exptx{#1}} +\SKV@def\SKV@noexpcs#1{\unexpanded\expandafter{\csname#1\endcsname}} +\SKV@pdef*\SKV@undef#1{\let#1\SKV@undefined} +\SKV@pdef*\SKV@gundef{\global\SKV@undef} +\SKV@pdef*\SKV@csundef#1{\SKV@aftercs\SKV@undef{#1}} +\SKV@pdef*\SKV@csgundef{\global\SKV@csundef} +\SKV@def*\SKV@undeflist{\SKV@savstate\SKV@t@st@p@a\SKV@und@flist} +\SKV@def*\SKV@und@flist#1{% + \ifSKV@cl\let\gl@l@x\global\else\let\gl@l@x\relax\fi + \XKV@for@n{#1}\SKV@resvd{% + \SKV@expnext\SKV@ensurescape\SKV@resvd + \gl@l@x\expandafter\SKV@undef\SKV@resvd + }% + \SKV@resstate +} +\SKV@pdef*\SKV@Undef{\SKV@savstate\SKV@testopte\SKV@Und@f} +\SKV@def*\SKV@Und@f[#1]#2{% + \ifSKV@st + \ifSKV@pl + \def\SKV@ndl{\SKV@for*[#1]#2}% + \else + \def\SKV@ndl{\SKV@tfor*#2}% + \fi + \else + \ifSKV@pl + \def\SKV@ndl{\SKV@for[#1]{#2}}% + \else + \def\SKV@ndl{\SKV@tfor{#2}}% + \fi + \fi + \SKV@ndl\SKV@ndl{% + \ifSKV@pl + \SKV@despacectt\SKV@ndl + \ifSKV@cl\expandafter\global\fi + \SKV@threexp\let + \expandafter\csname\SKV@ndl\endcsname\SKV@undefined + \else + \ifSKV@cl\expandafter\global\fi + \expandafter\let\SKV@ndl\SKV@undefined + \fi + }% + \SKV@resstate +} +\SKV@def*\SKV@ifdef#1{% + \SKV@ifdefined{#1}% + {\@nameuse{SKV@\SKV@ifx{#1}\relax{2}{1}of2}}\@secondoftwo +} +\SKV@def*\SKV@ifundef#1{\@nameuse{SKV@\SKV@ifdef{#1}{2}{1}of2}} +\SKV@def*\SKV@ifcsdef#1{% + \SKV@ifcsname{#1}% + {\@nameuse{SKV@\expandafter\SKV@ifx\csname#1\endcsname + \relax{2}{1}of2}}\@secondoftwo +} +\SKV@def*\SKV@ifcsundef#1{\@nameuse{SKV@\SKV@ifcsdef{#1}{2}{1}of2}} +\SKV@def*\SKV@ifonedef#1{% + \def\SKV@resvda{#1}\SKV@swtrue + \XKV@whilist\SKV@resvda\SKV@resvdb\ifSKV@sw\fi{% + \SKV@oifdefined\SKV@resvdb\SKV@swfalse\SKV@swtrue + }% + \@nameuse{SKV@\ifSKV@sw2\else1\fi of2}% +} +\SKV@def\DeclarePackageCommand#1#2{% + \SKV@ensurescape{#2}% + \SKV@ifdef#2{% + \edef\resvd@a{\expandafter\@gobble\string#2}% + \SKV@cslet{Saved\resvd@a}#2% + \edef\resvd@a##1{\SKV@AtEndAfterPackageEnd% + {##1Saved\resvd@a\noexpand\endcsname}}% + \resvd@a{\expandafter\let\expandafter#2\csname}% + \SKV@undef\resvd@a + }{\SKV@AtEndAfterPackageEnd{\SKV@Undef!#2}}% + #1#2% +} +\SKV@pdef*\SKV@providedef{\@star@or@long{\SKV@pr@videdef{}}} +\SKV@pdef*\SKV@providerobustdef{\@star@or@long{\SKV@pr@videdef\protected}} +\SKV@def\SKV@pr@videdef#1#2{% + \SKV@ensurescape{#2}% + \edef\SKV@prov@d{\expandafter\@gobble\string#2}% + \SKV@aftercs\let{SKV@test@\SKV@prov@d}\SKV@undefined + \SKV@ifdef#2{% + \SKV@vbmsg{info}{Command '\string#2' already defined:\SKV@MB + Not redefined\on@line}% + \SKV@afterassignment{\SKV@gundef\SKV@pr@vid}% + \def\SKV@pr@vid + }{% + \SKV@afterassignment{% + \SKV@aftercs\edef{SKV@test@\SKV@prov@d}% + {\expandafter\strip@prefix\meaning#2}% + }% + \l@ngrel@x#1\def#2% + }% +} +\SKV@def\TestProvidedCommand#1{% + \SKV@ensurescape{#1}% + \begingroup + \SKV@ifmacro#1{% + \edef\SKV@resvda{\expandafter\@gobble\string#1}% + \edef\SKV@resvdb{\expandafter\strip@prefix\meaning#1}% + \expandafter\aftergroup\csname SKV@\expandafter + \ifx\expandafter\SKV@resvdb + \csname SKV@test@\SKV@resvda\endcsname + 1\else2\fi of2\endcsname + }{\aftergroup\@secondoftwo}% + \endgroup +} +\SKV@pdef*\SKV@newdef{\SKV@def} +\SKV@providerobustdef*\newdef{\SKV@def} +\SKV@providerobustdef*\redef{\SKV@redef} +\SKV@pdef*\SKV@robustdef{\SKV@pdef} +\SKV@providerobustdef*\robustdef{\SKV@pdef} +\SKV@providerobustdef*\robustredef{\SKV@predef} +\SKV@providerobustdef*\providedef{\SKV@providedef} +\SKV@providerobustdef*\providerobustdef{\SKV@providerobustdef} +\SKV@providerobustdef*\aftercommission{\SKV@afterassignment} +\SKV@providerobustdef*\aftercoterie{\SKV@aftergroup} +\SKV@def*\SKV@ifmacro#1{% + \begingroup + \edef\SKV@tempa##1{\def##1####1\detokenize{macro}:####2&}% + \SKV@tempa\SKV@tempa{\expandafter\aftergroup + \csname SKV@\SKV@ifblank{##2}{2}{1}of2\endcsname}% + \edef\SKV@tempb##1{##1\detokenize{macro}:&}% + \SKV@tempb{\expandafter\SKV@tempa\meaning#1}% + \endgroup +} +\SKV@def\SKV@csifmacro{\SKV@aftercs\SKV@ifmacro} +\SKV@def\SKV@ifnotmacro#1{\SKV@ifmacro{#1}\@secondoftwo\@firstoftwo} +\SKV@def*\SKV@simplecmdpatch{\begingroup\@makeother{\#}% + \SKV@testopt\SKV@simpl@cmdpatch{########1}} +\SKV@def*\SKV@simpl@cmdpatch[#1]#2#3#4{% + \edef\SKV@resvda##1##2{% + \def##1####1\detokenize{macro:}####2->####3&{% + #1\def\string#2####2{##2####3&}% + }% + \def##2####1\detokenize{#3}####2&{####1\detokenize{#4}####2}% + \edef##1{##1\meaning#2&}% + }% + \SKV@resvda\SKV@resvda\SKV@resvdb + \everyeof{\noexpand}\endlinechar\m@ne\makeatletter + \catcode`\#=6\relax + \scantokens\expandafter{\SKV@resvda}% + \global\let#2#2% + \endgroup +} +\SKV@def*\SKV@ifrelax#1{% + \SKV@ensurescape{#1}% + \SKV@ifdefined{#1}% + {\@nameuse{SKV@\SKV@ifx{#1}\relax{1}{2}of2}}\@secondoftwo +} +\SKV@def*\SKV@ifcsrelax#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsname{#1}% + {\@nameuse{SKV@\expandafter\SKV@ifx\csname#1\endcsname + \relax{1}{2}of2}}\@secondoftwo +} +\SKV@def*\SKV@ifdefax#1{% + \SKV@ensurescape{#1}% + \SKV@ifdefined{#1}% + {\@nameuse{SKV@\SKV@ifx{#1}\relax{2}{1}of3}}\@thirdofthree +} +\SKV@def*\SKV@ifcsdefax#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsname{#1}% + {\@nameuse{SKV@\expandafter\SKV@ifx\csname#1\endcsname + \relax{2}{1}of3}}\@thirdofthree +} +\SKV@def*\SKV@despacectt#1{% + \SKV@ensurescape{#1}% + \SKV@expnext\SKV@sp@def{#1}#1% +} +\SKV@def\SKV@detokdespace#1{% + \edef#1{\detokenize\expandafter{#1}}% + \SKV@despacectt#1% +} +\SKV@def\SKV@ifblank#1{\SKV@ifbl@nk#1&&\@secondoftwo\@firstoftwo:} +\SKV@def\SKV@ifbl@nk#1#2#4#5:{#4} +\SKV@def\SKV@ifnotblank#1{\SKV@ifbl@nk#1&&\@firstoftwo\@secondoftwo:} +\SKV@def\SKV@oifblank#1{\expandafter\SKV@ifblank\expandafter{#1}} +\SKV@def\SKV@xifblank#1{\SKV@expargs{0}\SKV@ifblank{#1}\@nil} +\SKV@def\SKV@ifstrequal#1#2{% + \begingroup + \edef\resvd@a{\detokenize{#1}}% + \edef\resvd@b{\detokenize{#2}}% + \SKV@despacectt\resvd@a\SKV@despacectt\resvd@b + \expandafter\aftergroup + \csname SKV@\SKV@ifx\resvd@a\resvd@b{1}{2}of2\endcsname + \endgroup +} +\SKV@def\SKV@ifstrnotequal#1#2{% + \SKV@ifstrequal{#1}{#2}\@secondoftwo\@firstoftwo} +\SKV@def\SKV@oifstrequal#1#2{% + \@expandtwoargs\SKV@ifstrequal{\SKV@expox{#1}}{\SKV@expox{#2}}} +\SKV@def\SKV@xifstrequal#1#2{\@expandtwoargs\SKV@ifstrequal{#1}{#2}} +\SKV@def\SKV@ifempty#1{\@nameuse{SKV@\ifcat$\detokenize{#1}$1\else2\fi of2}} +\SKV@def\SKV@ifnotempty#1{\@nameuse{SKV@\SKV@ifempty{#1}{2}{1}of2}} +\SKV@def\SKV@oifempty#1{\SKV@expnext\SKV@ifempty{#1}} +\SKV@def\SKV@xifempty#1{\SKV@expargs{0}\SKV@ifempty{#1}\@nil} +\SKV@def*\SKV@remparser{\SKV@testopt\SKV@remp@rser{,}} +\SKV@def\SKV@remp@rser[#1]#2{% + \begingroup + \ifx#2\@empty\else + \edef#2{#1\SKV@expox{#2}}% + \def\SKV@tempa#1##1\@nil{% + \gdef#2{##1}\futurelet\@temptoks\SKV@t@mp##1\@nil + }% + \def\SKV@t@mp##1\@nil{% + \ifx\@temptoks\@sptoken + \SKV@expnext\SKV@sp@def{#2}#2% + \expandafter\SKV@tempa\expandafter#1#2\@nil + \else + \if#1\@temptoks\expandafter\SKV@tempa#2\@nil\fi + \fi + }% + \expandafter\SKV@tempa#2\@nil + \fi + \endgroup +} +\SKV@pdef*\SKV@appto{\SKV@savstate\SKV@testoptb{\SKV@hooktomac{>}}} +\SKV@pdef*\SKV@prepto{\SKV@savstate\SKV@testoptb{\SKV@hooktomac{<}}} +\SKV@def\SKV@hooktomac#1#2#3{% + \ifSKV@st\let\SKV@iii\SKV@expox\else\let\SKV@iii\unexpanded\fi + \ifSKV@pl + \let\@ifundef\SKV@ifcsundef\let\SKV@iv\SKV@csexpox + \def\SKV@i##1{\def\SKV@ii####1{\SKV@aftercs##1{####1}}}% + \ifSKV@cl\SKV@i\xdef\else\SKV@i\edef\fi + \else + \let\@ifundef\SKV@ifundef\let\SKV@iv\SKV@expox + \ifSKV@cl\def\SKV@ii##1{\xdef##1}\else + \def\SKV@ii##1{\edef##1}\fi + \fi + \@ifundef{#2}{% + \SKV@ii{#2}{\SKV@iii{#3}}% + }{% + \SKV@ifstrequal{#1}{>}% + {\SKV@ii{#2}{\SKV@iv{#2}\SKV@iii{#3}}}% + {\SKV@ii{#2}{\SKV@iii{#3}\SKV@iv{#2}}}% + }% + \SKV@resstate + \SKV@undeflist!{\SKV@i,\SKV@ii,\SKV@iii,\SKV@iv}% +} +\SKV@pdef\apptomac#1#2{% + \SKV@ifundef{#1}{\edef#1{\unexpanded{#2}}}% + {\edef#1{\SKV@expox{#1}\unexpanded{#2}}}% +} +\SKV@pdef*\gapptomac{\global\apptomac} +\SKV@pdef*\csapptomac{\SKV@aftercs\apptomac} +\SKV@pdef*\csgapptomac{\global\csapptomac} +\SKV@pdef\preptomac#1#2{% + \SKV@ifundef{#1}{\edef#1{\unexpanded{#2}}}% + {\edef#1{\unexpanded{#2}\SKV@expox{#1}}}% +} +\SKV@pdef*\gpreptomac{\global\preptomac} +\SKV@pdef*\cspreptomac{\SKV@aftercs\preptomac} +\SKV@pdef*\csgpreptomac{\global\cspreptomac} +\SKV@pdef*\SKV@addtolist{\SKV@savstate\SKV@testoptc{% + \SKV@t@stoptg{\SKV@testopt\SKV@addt@list{,}}}} +\SKV@def*\SKV@addt@list[#1]{% + \def\parser{#1}% + \ifSKV@cl\let\glob@l@def\xdef\else\let\glob@l@def\edef\fi + \ifSKV@pl + \def\SKV@next##1{\SKV@aftercs\SKV@addt@l@st{##1}}% + \else + \let\SKV@next\SKV@addt@l@st + \fi + \SKV@next +} +\SKV@def\SKV@addt@l@st#1#2{% + \SKV@ifundef{#1}{% + \glob@l@def#1{\SKV@expox{#2}}% + }{% + \SKV@ifx#1\@empty{% + \glob@l@def#1{\SKV@expox{#2}}% + }{% + \ifSKV@lt + \glob@l@def#1{\SKV@expox{#2}\parser\SKV@expox{#1}}% + \else + \glob@l@def#1{\SKV@expox{#1}\parser\SKV@expox{#2}}% + \fi + }% + }% + \SKV@resstate +} +\SKV@pdef*\SKV@relax#1{\let#1\relax} +\SKV@pdef*\SKV@grelax{\global\SKV@relax} +\SKV@pdef*\SKV@csrelax{\SKV@aftercs\SKV@relax} +\SKV@pdef*\SKV@csgrelax{\global\SKV@csrelax} +\@ifpackageloaded{atveryend}{% + \@ifpackagecurrent{atveryend}{2010/03/24}{}{% + \SKV@err{Your loaded version of atveryend package is + not current}\@ehc}}{\RequirePackage{atveryend}[2010/03/24]} +\SKV@pdef*\SKV@notprerr{% + \SKV@err{You have issued a preamble command here}% + {Move shown command to document preamble.}% +} +\SKV@pdef*\SKV@notenderr{% + \SKV@err{You've called an \string\enddocument\space command\SKV@MB + at the wrong place}{Move command to before \string\enddocument.}% +} +\SKV@pdef*\SKV@AtDocumentStart{\AtBeginDocument} +\SKV@pdef*\SKV@BeforeDocumentStart{\SKV@appto!\SKV@beforedocstart} +\def\SKV@beforedocstart{} +\SKV@prepto\document{% + \endgroup + \let\SKV@BeforeDocumentStart\@firstofone + \SKV@beforedocstart + \let\SKV@BeforeDocumentStart\SKV@notprerr + \SKV@gundef\SKV@beforedocstart + \begingroup +} +\SKV@pdef*\SKV@AfterDocumentStart{\SKV@appto!\SKV@afterdocstart} +\def\SKV@afterdocstart{} +\SKV@appto\document{% + \let\SKV@AfterDocumentStart\@firstofone + \SKV@afterdocstart + \let\SKV@AfterDocumentStart\SKV@notprerr + \SKV@gundef\SKV@afterdocstart\ignorespaces +} +\SKV@BeforeDocumentStart{\let\SKV@AtDocumentStart\@firstofone} +\SKV@pdef*\SKV@AtDocumentEnd{\AtEndDocument} +\SKV@pdef*\SKV@BeforeLastPage{\SKV@appto!\SKV@beforelastpage} +\def\SKV@beforelastpage{} +\SKV@AtDocumentEnd{% + \let\SKV@bef@relastpage\AtVeryEnd@BeforeClearDocumentHook + \def\AtVeryEnd@BeforeClearDocumentHook{% + \let\SKV@BeforeLastPage\@firstofone + \SKV@beforelastpage\SKV@bef@relastpage + \let\SKV@BeforeLastPage\SKV@notenderr + \SKV@Undef!{\SKV@beforelastpage\SKV@bef@relastpage}% + }% +} +\SKV@pdef*\SKV@AfterLastPage{\SKV@appto!\SKV@afterlastpage} +\def\SKV@afterlastpage{} +\SKV@AtDocumentEnd{% + \let\SKV@afterl@stpage\AtVeryEnd@AfterLastShipoutHook + \def\AtVeryEnd@AfterLastShipoutHook{% + \let\SKV@AfterLastPage\@firstofone + \SKV@afterlastpage\SKV@afterl@stpage + \let\SKV@AfterLastPage\SKV@notenderr + \SKV@Undef!{\SKV@afterlastpage\SKV@afterl@stpage}% + }% +} +\SKV@pdef*\SKV@AfterDocumentEnd{\SKV@appto!\SKV@afterenddoc} +\def\SKV@afterenddoc{} +\SKV@AtDocumentEnd{% + \let\SKV@aft@renddoc\AtVeryEnd@AtVeryEndDocumentHook + \def\AtVeryEnd@AtVeryEndDocumentHook{% + \let\SKV@AfterDocumentEnd\@firstofone + \SKV@afterenddoc\SKV@aft@renddoc + \let\SKV@AfterDocumentEnd\SKV@notenderr + \SKV@Undef!{\SKV@afterenddoc\SKV@aft@renddoc}% + }% +} +\SKV@pdef\SKV@AtPackageEnd{\AtEndOfPackage} +\SKV@pdef*\SKV@BeforePackageEnd{% + \SKV@prepto+{\@currname.\@currext-h@@k}% +} +\SKV@pdef*\SKV@AfterPackageEnd{% + \SKV@appto+{\@currname.\@currext-skvh@@k}% +} +\let\SKV@AtClassEnd\SKV@AtPackageEnd +\let\SKV@BeforeClassEnd\SKV@BeforePackageEnd +\let\SKV@AfterClassEnd\SKV@AfterPackageEnd +\SKV@prepto\@popfilename{% + \SKV@csuse{\@currname.\@currext-skvh@@k}% + \SKV@csgundef{\@currname.\@currext-skvh@@k}% +} +\SKV@pdef\@afterpackageloaded#1{% + \SKV@AtDocumentStart{% + \SKV@ifcsdef{ver@#1.\@pkgextension}{}{% + \SKV@warn{Package '#1' was never loaded}% + }% + } + \SKV@ifcsdef{ver@#1.\@pkgextension}{% + \let\SKV@next\@firstofone + }{% + \def\SKV@next{\SKV@appto+{#1.\@pkgextension-skvh@@k}}% + }% + \SKV@next +} +\let\SKV@steno\z@ +\SKV@pdef*\SKV@for{\SKV@savstate\SKV@testoptd\SKV@f@r} +\SKV@pdef*\SKV@f@r[#1]{% + \let\SKV@steno\@ne + \ifSKV@st + \def\SKV@next{\SKV@for@b[#1]}% + \else + \def\SKV@next{\SKV@CommandGenParser{#1}\SKV@for@a}% + \fi + \SKV@next +} +\SKV@pdef*\SKV@CommandGenParser#1{% + \def\parser{#1}% + \protected\long\def\SKV@for@a##1##2##3{% + \SKV@ifblank{##1}\SKV@fornoop\SKV@forloop + ##2{##3}##1#1\@nil + }% + \long\def\SKV@forloop##1##2##3#1##4\@nil{% + \edef##1{\unexpanded{##3}}% + \SKV@ifstrequal{##3}{\listbreak}{% + \SKV@keylistrema##4\@nil#1% + }{% + \SKV@ifblank{##4}{% + ##2\relax + \ifnum\SKV@steno=\@ne\expandafter\SKV@resstate\fi + \let\SKV@steno\z@ + }{% + ##2\relax\SKV@forloop##1{##2}##4\@nil + }% + }% + }% + \long\def\SKV@fornoop##1\@nil{}% +} +\SKV@pdef*\SKV@for@b{\SKV@testopt\SKV@f@r@b{,}} +\SKV@def\SKV@f@r@b[#1]#2{% + \SKV@CommandGenParser{#1}\SKV@expnext\SKV@for@a{#2}% +} +\SKV@pdef*\SKV@tfor{\SKV@ifstar\SKV@tfor@b\SKV@tfor@a} +\SKV@pdef*\SKV@tfor@a#1#2#3{% + \SKV@ifblank{#1}{% + \SKV@vbmsg{warn}{Empty list for '\string\SKV@tfor@a'\on@line}% + }{% + \SKV@ifblank{#3}{% + \SKV@vbmsg{warn}% + {Empty function for '\string\SKV@tfor@a'\on@line}% + }{% + \SKV@tforloop#1\@nil\@nil{#3}% + }% + }% +} +\SKV@def\SKV@tforloop#1#2#4{% + \long\def#3{#1}% + \SKV@ifstrequal{#1}{\listbreak}{% + \let\SKV@next\SKV@tforrema + }{% + \SKV@ifstrequal{#1}{\@nil}{% + \let\SKV@next\SKV@tfornoop + }{% + #4\relax\let\SKV@next\SKV@tforloop + }% + }% + \SKV@next#2{#4}% +} +\SKV@def\SKV@tfornoop#1#3{} +\SKV@pdef*\SKV@tforrema#1\@nil\@nil#3{% + \long\edef\SKV@remainder{\unexpanded{#1}}% +} +\SKV@pdef*\SKV@tfor@b#1#2#3{\SKV@expnext\SKV@tfor@a{#1}#2{#3}} +\SKV@def*\SKV@preamblecmds{} +\SKV@pdef*\SKV@onlypreamble{\SKV@savstate\SKV@t@st@pta\SKV@onlypre@mble} +\SKV@pdef*\SKV@onlypre@mble#1{% + \SKV@for[,]{#1}\SKV@resvd{% + \SKV@despacectt\SKV@resvd + \ifSKV@pl + \SKV@expnext\SKV@ensurenoscape\SKV@resvd + \edef\SKV@resvd##1##2{\SKV@appto!##1{##2\SKV@noexpcs{\SKV@resvd}}}% + \else + \SKV@expnext\SKV@ensurescape\SKV@resvd + \edef\SKV@resvd##1##2{\SKV@appto!##1{##2\SKV@expox{\SKV@resvd}}}% + \fi + \SKV@resvd\SKV@preamblecmds\SKV@dopreamble + }% + \SKV@resstate +} +\SKV@AfterDocumentStart{% + \gdef\SKV@dopreamble#1{% + \gdef#1{\SKV@err{'\string#1' is a preamble command}% + {Move command '\string#1' to document preamble.}}% + }% + \SKV@preamblecmds + \global\let\SKV@dopreamble\@gobble +} +\SKV@pdef*\SKV@onlypackage{% + \SKV@xifempty{\@currname\@currext}{% + \SKV@err{Not in package environment}% + {The command at top level of this message\SKV@MB + is available only in package environment.}% + }{}% +} +\def\SKV@aftergroupdef#1#2{% + \expandafter\endgroup\expandafter\def\expandafter#1% + \expandafter{#2}% +} +\SKV@def*\SKV@noexpparse#1{% + \begingroup + \def\SKV@resa{}% + \edef\SKV@tempa{\detokenize\expandafter{#1}\@backslashchar&}% + \edef\SKV@tempb##1{\def##1####1\@backslashchar####2&}% + \SKV@tempb\SKV@tempb{% + \SKV@ifempty{##2}{% + \edef\SKV@resa{\SKV@expox\SKV@resa##1}% + }{% + \edef\SKV@resa{\SKV@expox\SKV@resa##1% + \string\noexpand\@backslashchar}% + \SKV@tempb##2&% + }% + }% + \expandafter\SKV@tempb\SKV@tempa + \expandafter\endgroup\expandafter\toks@\expandafter{\SKV@resa}% + \edef#1{\the\toks@}% +} +\SKV@def*\SKV@CommandKeyParser#1{% + \long\def\SKV@d@fine@keylist##1{% + \global\let\ifSKV@st\ifSKV@st + \endgroup + \SKV@undeflist!{\skif,\skifx,\skifcase,\skor,\skelse,\skfi}% + \ifSKV@st + \edef\@keylist{\detokenize\expandafter{##1}}% + \else + \edef\@keylist{\detokenize{##1}}% + \fi + \SKV@scantoksa\@keylist + \SKV@resstate + \SKV@expnext\SKV@checksanitizec\@keylist\SKV@tempa + \expandafter\SKV@readkeylist\SKV@tempa#1\@nil + }% + \long\def\SKV@readkeylist##1#1##2\@nil{% + \SKV@ifblank{##1}{}% + {% + \SKV@ifstrequal{##1}{\listbreak}{% + \SKV@keylistrema##2\@nil#1% + }{% + \SKV@define@multkeys##1\@nil + \SKV@ifblank{##2}\relax{\SKV@readkeylist##2\@nil}% + }% + }% + }% +} +\SKV@def*\SKV@checksanitizec#1#2{% + \XKV@ch@cksanitize{#1}#2;% + \ifin@\else\XKV@ch@cksanitize{#1}#2,\fi + \ifin@\@selective@sanitize[0]{,;}#2\fi +} +\SKV@pdef\SKV@checkchoice#1#2#3#4{% + \XKV@savstate\XKV@sttrue\XKV@pltrue + \SKV@expargs{3}{\XKV@checkchoice[\val\nr]}{{#1}{#2}}{{#3}{#4}}\@nil + \XKV@resstate +} +\SKV@simplecmdpatch\XKV@ch@ckch@ice{% + \XKV@err{value `\the\XKV@toks' is not allowed}% +}{% + \SKV@err{Value '\the\XKV@toks' is not allowed}% + {You have submitted an invalid value for a key.\SKV@MB + You may proceed if you like but expect funny results.}% +} +\SKV@simplecmdpatch\XKV@@ch@ckchoice{% + \XKV@err{value `\the\XKV@toks' is not allowed}% +}{% + \SKV@err{Value '\the\XKV@toks' is not allowed}% + {You have submitted an invalid value for a key.\SKV@MB + You may proceed if you like but expect funny results.}% +} +\SKV@pdef*\SKV@inputerr#1#2{% + \SKV@err{Erroneous value '#1' for key '#2'}% + {Please use the correct value for key '#2'\SKV@MB + of families '\XKV@fams'.}% +} +\SKV@def*\SKV@despacelist{\SKV@savstate\SKV@testoptd\SKV@desp@celist} +\SKV@def*\SKV@desp@celist[#1]#2{% + \begingroup + \let\SKV@resa\@empty + \ifSKV@st + \def\SKV@dsl{\SKV@for*[#1]#2}% + \else + \def\SKV@dsl{\SKV@for[#1]{#2}}% + \fi + \SKV@dsl\SKV@dsl{% + \SKV@despacectt\SKV@dsl + \SKV@appto*\SKV@resa{\expandafter#1\SKV@dsl}% + }% + \ifx\SKV@resa\@empty\else\SKV@remparser[#1]\SKV@resa\fi + \ifSKV@st\global\let#2\SKV@resa\fi + \endgroup + \SKV@resstate +} +\SKV@def*\SKV@tdespacelt#1{% + \SKV@tfor{#1}\SKV@dsl{\expandafter\SKV@despacectt\SKV@dsl}% +} +\SKV@pdef*\ReserveKeyPrefixNames{\SKV@ifstar\SKV@rkpn@m@s\SKV@rkpn@mes} +\SKV@def*\SKV@rkpn@mes#1{\SKV@appto!\SKV@rkpnames{,#1}} +\SKV@def*\SKV@rkpn@m@s#1{\SKV@AfterPackageEnd{\SKV@appto!\SKV@rkpnames{,#1}}} +\SKV@pdef*\ReserveMacroPrefixNames{\SKV@ifstar\SKV@rmpn@m@s\SKV@rmpn@mes} +\SKV@def*\SKV@rmpn@mes#1{\SKV@appto!\SKV@rmpnames{,#1}} +\SKV@def*\SKV@rmpn@m@s#1{\SKV@AfterPackageEnd{\SKV@appto!\SKV@rmpnames{,#1}}} +\SKV@pdef*\ReserveFamilyNames{\SKV@ifstar\SKV@rfn@m@s\SKV@rfn@mes} +\SKV@def*\SKV@rfn@mes#1{\SKV@appto!\SKV@rfnames{,#1}} +\SKV@def*\SKV@rfn@m@s#1{\SKV@AfterPackageEnd{\SKV@appto!\SKV@rfnames{,#1}}} +\SKV@def*\SKV@rkpnames{XKV} +\SKV@def*\SKV@rmpnames{XKV} +\SKV@def*\SKV@rfnames{XKV} +\ReserveKeyPrefixNames*{SKV} +\ReserveMacroPrefixNames*{SKV} +\ReserveFamilyNames*{skeyval} +\SKV@def*\SKV@newlet{\SKV@savstate\SKV@t@stopta\SKV@n@wlet} +\SKV@def*\SKV@n@wlet#1#2{% + \SKV@ifdef#1{% + \ifSKV@st + \SKV@ifdef#2{% + \let#1#2% + \SKV@vbmsg{info}{Command '\string#1' redefined\on@line}% + }{% + \SKV@err{Command '\string#2' undefined}% + {I can't let '\string#1' to '\string#2'.}% + }% + \else + \SKV@err{Command '\string#1' already defined}% + {I can't let '\string#1' to '\string#2'.}% + \fi + }{% + \SKV@ifdef#2{\let#1#2}{% + \SKV@err{Command '\string#2' undefined}% + {I can't let '\string#1' to '\string#2'.}% + }% + }% + \SKV@resstate +} +\AtEndOfPackage{\SKV@providedef*\NewLet{\SKV@newlet}} +\SKV@pdef*\SKV@cslet{\SKV@savstate\SKV@t@stopta\SKV@csl@t} +\SKV@def*\SKV@csl@t#1#2{% + \ifSKV@st + \def\SKV@next{\SKV@aftercs\let{#1}#2}% + \else + \def\SKV@next{% + \SKV@ifdef{#2}{% + \SKV@aftercs\let{#1}#2% + }{% + \SKV@err{Command '\string#2' undefined}% + {You may use the starred version of \string\SKV@cslet.}% + }% + }% + \fi + \SKV@resstate\SKV@next +} +\SKV@pdef*\SKV@letcs{\SKV@savstate\SKV@t@stopta\SKV@l@tcs} +\SKV@def*\SKV@l@tcs#1#2{% + \ifSKV@st + \def\SKV@next{% + \begingroup\expandafter\endgroup + \expandafter\let\expandafter#1\csname#2\endcsname + }% + \else + \def\SKV@next{% + \SKV@ifcsdef{#2}{% + \expandafter\let\expandafter#1\csname#2\endcsname + }{% + \SKV@err{Command '\@backslashchar#2' undefined}% + {You may use the starred version of \string\SKV@letcs.}% + }% + }% + \fi + \SKV@resstate\SKV@next +} +\SKV@pdef*\SKV@csletcs{\SKV@savstate\SKV@t@stopta\SKV@csl@tcs} +\SKV@def*\SKV@csl@tcs#1#2{% + \ifSKV@st + \def\SKV@next{% + \begingroup\expandafter\endgroup + \expandafter\let\csname#1\SKV@aftercs\endcsname{#2}% + }% + \else + \def\SKV@next{% + \SKV@ifcsdef{#2}{% + \SKV@aftercs\let{#1\SKV@aftercs}{#2}% + }{% + \SKV@err{Command '\@backslashchar#2' undefined}% + {You may use the starred version of \string\SKV@csletcs.}% + }% + }% + \fi + \SKV@resstate\SKV@next +} +\SKV@pdef*\SKV@numdef{\SKV@savstate\SKV@testoptc{\SKV@expr{num}}} +\SKV@pdef*\SKV@dimdef{\SKV@savstate\SKV@testoptc{\SKV@expr{dim}}} +\SKV@def*\SKV@expr#1{% + \ifSKV@cl\let\gl@l@x\global\else\let\gl@l@x\relax\fi + \ifSKV@pl + \def\SKV@next##1{% + \edef\resvd@a####1{####1{#1}\SKV@csexpox{##1}}% + \resvd@a\SKV@ex@r + }% + \else + \def\SKV@next{\SKV@ex@r{#1}}% + \fi + \SKV@resstate\SKV@next +} +\SKV@def*\SKV@ex@r#1#2#3{% + \SKV@ensurescape@ne{#2}{#3}% + \SKV@ifstrequal{#1}{num}% + {\def\resvd@a{count}}{\def\resvd@a{dimen}}% + \SKV@ifundef{#2}{% + \@nameuse{new\resvd@a}#2% + \gl@l@x#2=\@nameuse{#1expr}#3\relax + }{% + \gl@l@x#2=\@nameuse{#1expr}#3\relax + }% +} +\SKV@def*\SKV@ensurescape@ne#1#2{% + \SKV@ensurescape{#1}% + \SKV@ifnotempty{#2}{}% + {\SKV@err{Argument '\string#2' is empty}% + {Empty argument '\string#2' is invalid.\SKV@MB + I expected a nonempty argument here.}% + }% +} +\SKV@def*\SKV@badkeylistparser{% + \SKV@err{Key list parser '\SKV@keyparser' not allowed}% + {Use semicolon (preferable) or colon as parser.}% +} +\define@boolkey+[KV]{skeyval}[SKV@]{xchoicelist}[true]{}{% + \SKV@inputerr{#1}{xchoicelist}% +} +\define@cmdkey[KV]{skeyval}[SKV@]{genparser}[,]{% + \@onelevel@sanitize\SKV@genparser + \SKV@despacectt\SKV@genparser + \SKV@expargs{0}\SKV@CommandGenParser\SKV@genparser\@nil +} +\define@cmdkey[KV]{skeyval}[SKV@]{keyparser}[;]{% + \@onelevel@sanitize\SKV@keyparser + \SKV@xifblank\SKV@keyparser\SKV@badkeylistparser\relax + \SKV@despacectt\SKV@keyparser + \begingroup + \def\SKV@tempa{,}\SKV@detokdespace\SKV@tempa + \SKV@ifx\SKV@keyparser\SKV@tempa + {\aftergroup\SKV@badkeylistparser}{}% + \endgroup + \SKV@expargs{0}\SKV@CommandKeyParser\SKV@keyparser\@nil +} +\SKV@def*\SKV@keyprefix@err{% + \SKV@err{Key prefix '\SKV@keyprefix' is not allowed}% + {Use another prefix name instead of '\SKV@keyprefix'.}% +} +\define@cmdkey[KV]{skeyval}[SKV@]{keyprefix}[KV]{% + \SKV@xifblank\SKV@keyprefix\SKV@keyprefix@err\relax + \SKV@despacectt\SKV@keyprefix + \SKV@expargs{3}\XKV@checksanitizeb{{\SKV@rkpnames}}% + \SKV@tempa\@nil + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@ifx\SKV@keyprefix\SKV@tempa\SKV@keyprefix@err\relax + }% +} +\SKV@def*\SKV@keyfamily@err{% + \SKV@err{Key family '\SKV@keyfamily' is not allowed}% + {Use another family name instead of '\SKV@keyfamily'.}% +} +\define@cmdkey[KV]{skeyval}[SKV@]{keyfamily}[fam]{% + \SKV@xifblank\SKV@keyfamily\SKV@keyfamily@err\relax + \SKV@despacectt\SKV@keyfamily + \SKV@expargs{3}\XKV@checksanitizeb{{\SKV@rfnames}}% + \SKV@tempa\@nil + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@ifx\SKV@keyfamily\SKV@tempa\SKV@keyfamily@err\relax + }% +} +\SKV@def*\SKV@macroprefix@err{% + \SKV@err{Macro prefix '\SKV@macroprefix' is not allowed}% + {Use another macro prefix name instead of '\SKV@macroprefix'.}% +} +\define@cmdkey[KV]{skeyval}[SKV@]{macroprefix}[mp@]{% + \SKV@xifblank\SKV@macroprefix\SKV@macroprefix@err\relax + \SKV@despacectt\SKV@macroprefix + \SKV@expargs{3}\XKV@checksanitizeb{{\SKV@rmpnames}}% + \SKV@tempa\@nil + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@ifx\SKV@macroprefix\SKV@tempa\SKV@macroprefix@err\relax + }% +} +\SKV@BeforePackageEnd{\deftog{draft}\deftog{final}} +\define@boolkey+[KV]{skeyval}[SKV@]{draft}[true]{% + \ifSKV@draft + \setkeys[KV]{skeyval}{final=false}% + \SKV@AtPackageEnd{\ifSKV@draft\togon{draft}\else + \togoff{draft}\fi}% + \fi +}{% + \SKV@inputerr{#1}{draft}% +} +\define@boolkey+[KV]{skeyval}[SKV@]{final}[true]{% + \ifSKV@final + \setkeys[KV]{skeyval}{draft=false}% + \SKV@AtPackageEnd{\ifSKV@final\togon{final}\else + \togoff{final}\fi}% + \fi +}{% + \SKV@inputerr{#1}{final}% +} +\DeclareOptionX*{\SKV@warn{Unknown option '\CurrentOption' ignored}} +\ExecuteOptionsX[KV]<skeyval>{genparser,keyparser,keyprefix, + keyfamily,macroprefix,final} +\ProcessOptionsX*[KV]<skeyval>\relax +\SKV@pdef*\skvoptions#1{\setkeys[KV]{skeyval}{#1}} +\SKV@pdef*\SKV@ifdraft{\@nameuse{SKV@\ifSKV@draft1\else2\fi of2}} +\SKV@pdef*\SKV@iffinal{\@nameuse{SKV@\ifSKV@final1\else2\fi of2}} +\SKV@pdef*\SKV@ifnotdraft{\@nameuse{SKV@\SKV@ifdraft2\else1\fi of2}} +\SKV@pdef*\SKV@ifnotfinal{\@nameuse{SKV@\SKV@iffinal2\else1\fi of2}} +\def\SKV@useinfo#1{Not using '\@backslashchar#1' of 'skyeval' package} +\SKV@pdef*\SKV@in@#1#2{% + \def\in@@##1#1##2##3\in@@{\ifx\in@##2\in@false\else\in@true\fi}% + \in@@#2\@mil#1\in@\in@@ +} +\SKV@pdef*\SKV@in@n#1#2{% + \SKV@in@{#1}{#2}% + \@nameuse{SKV@\ifin@1\else2\fi of2}% +} +\SKV@pdef*\SKV@in@o#1#2{% + \@expandtwoargs\SKV@in@{\SKV@expox{#1}}{\SKV@expox{#2}}% + \@nameuse{SKV@\ifin@1\else2\fi of2}% +} +\SKV@pdef*\SKV@in@x#1#2{% + \@expandtwoargs\SKV@in@{#1}{#2}% + \@nameuse{SKV@\ifin@1\else2\fi of2}% +} +\SKV@pdef*\in@tog#1#2{% + \SKV@in@{#1}{#2}% + \ifin@\togon{in@}\else\togoff{in@}\fi +} +\AtEndOfPackage{\deftog{in@}} +\SKV@pdef*\in@tok#1#2{% + \begingroup + \SKV@expnext\SKV@sp@def{\detokenize{#1}}\SKV@tempa + \SKV@expnext\SKV@sp@def{\detokenize{#2}}\SKV@tempb + \@expandtwoargs\SKV@in@\SKV@tempa\SKV@tempb + \global\let\ifin@\ifin@ + \endgroup +} +\AtBeginDocument{% + \SKV@ifundef\ifdraft + {\SKV@pdef*\ifdraft{\SKV@ifdraft}}% + {\SKV@vbmsg{info}{\SKV@useinfo{ifdraft}}}% +} +\SKV@ifundef\ifnotdraft + {\SKV@pdef*\ifnotdraft{\SKV@ifnotdraft}}% + {\SKV@vbmsg{info}{\SKV@useinfo{ifnotdraft}}} +\SKV@ifundef\iffinal + {\SKV@pdef*\iffinal{\SKV@iffinal}}% + {\SKV@vbmsg{info}{\SKV@useinfo{iffinal}}} +\SKV@ifundef\ifnotfinal + {\SKV@pdef*\ifnotfinal{\SKV@ifnotfinal}}% + {\SKV@vbmsg{info}{\SKV@useinfo{ifnotfinal}}} +\SKV@def\SKV@star@or@long#1{% + \SKV@ifstar + {\let\l@ngrel@x\relax\def\@star@or@none{*}#1}% + {\let\l@ngrel@x\long\def\@star@or@none{}#1}% +} +\SKV@pdef\requirecmd{\SKV@star@or@long\SKV@require@cmd} +\SKV@def\SKV@require@cmd#1{% + \SKV@ensurescape{#1}% + \SKV@ifdefined{#1}% + {\CheckCommand\@star@or@none}{\newcommand\@star@or@none}{#1}% +} +\SKV@redef\@check@eq#1#2{% + \SKV@ifx{#1}{#2}{}{% + \SKV@vbmsg{warn}{% + Previous definition of '\string#1'\SKV@MB + doesn't match your new definition.\SKV@MB + * I haven't redefined '\string#1' *}% + }% +} +\SKV@def*\SKV@getswstate#1#2{% + \SKV@checkchoice{#2}{true,on,false,off}{% + \ifcase\nr\relax + \def\SKV@swval{true}% + \or + \def\SKV@swval{true}% + \or + \def\SKV@swval{false}% + \or + \def\SKV@swval{false}% + \fi + }{% + \SKV@swvalerr{#1}{#2}% + }% +} +\SKV@pdef*\newswitch{\SKV@swtrue\SKV@testopt\SKV@newswitch{}} +\SKV@pdef*\defswitch{\SKV@swfalse\SKV@testopt\SKV@newswitch{}} +\SKV@def*\SKV@newswitch[#1]#2{\SKV@testopt{\SKV@n@wswitch[#1]{#2}}{false}} +\SKV@def*\SKV@n@wswitch[#1]#2[#3]{% + \SKV@in@n{,}{#2}{% + \SKV@err{% + Command \ifSKV@sw'\string\newswitch'\else'\string\defswitch'\fi\space + doesn't accept list + }{% + Please submit only one switch to \ifSKV@sw'\string\newswitch'\else + '\string\defswitch'\fi\SKV@MB at a time. You may consider using + \ifSKV@sw'\string\NewSwitches'\else'\string\DefSwitches'\fi\SKV@MB + if you really want to define a set of switches. + }% + }{}% + \SKV@ensurenoscape{#2}% + \SKV@ifstrnotequal{#1#2}{x}{}{% + \SKV@err{'\string\ifx' is a TeX primitive}% + {You can't redefine '\string\ifx'! This is dangerous.}% + }% + \ifSKV@sw + \SKV@getswstate\newswitch{#3}% + \SKV@csifdefinable{if#1#2}\relax + \else + \SKV@getswstate\defswitch{#3}% + \SKV@ifcsundef{if#1#2}{}{% + \SKV@vbmsg{warn}{Switch '\@backslashchar if#1#2' redefined\on@line}% + }% + \fi + \csnewif{if#1#2}% + \@nameuse{#1#2\SKV@swval}% +} +\SKV@newlet\NewSwitches\NewBooleans +\SKV@newlet\DefSwitches\DefBooleans +\SKV@def*\setswitch#1#2{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsundef{if#1}{% + \SKV@noswerr{#1}% + }{% + \SKV@getswstate\setswitch{#2}% + \csname#1\SKV@swval\endcsname + }% +} +\SKV@def*\switchon#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsundef{if#1}{\SKV@noswerr{#1}}{\@nameuse{#1true}}% +} +\SKV@newlet\switchtrue\switchon +\SKV@def*\switchoff#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsundef{if#1}{\SKV@noswerr{#1}}{\@nameuse{#1false}}% +} +\SKV@newlet\switchfalse\switchoff +\SKV@def*\ifswitchon#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsundef{if#1}{\SKV@noswerr{#1}\@gobbletwo}% + {\@nameuse{SKV@\@nameuse{if#1}1\else2\fi of2}}% +} +\SKV@newlet\ifswitchtrue\ifswitchon +\SKV@def*\ifswitchoff#1{% + \SKV@ensurenoscape{#1}% + \SKV@ifcsundef{if#1}{\SKV@noswerr{#1}\@gobbletwo}% + {\@nameuse{SKV@\@nameuse{if#1}2\else1\fi of2}}% +} +\SKV@newlet\ifswitchfalse\ifswitchoff +\SKV@pdef*\SKV@noswerr#1{% + \SKV@err{Switch '\string#1' is undefined}% + {You need to first define the switch '\string#1'.}% +} +\SKV@pdef*\SKV@swnameerr#1{% + \SKV@err{'\string\if#1' not allowed as a boolean}% + {Invalid boolean name '\string#1'.}% +} +\SKV@def*\SKV@swvalerr#1#2{% + \SKV@err{\string#1\space error: invalid boolean value '#2'}% + {Valid boolean values are 'true', 'on', 'false', and 'off'.}% +} +\SKV@pdef*\SKV@undeftog@err#1{% + \SKV@err{Toggle '#1' undefined}% + {You need to first define '#1' before use.}% +} +\SKV@pdef*\newtog{\SKV@swtrue\SKV@testopt\SKV@newtog{}} +\SKV@pdef*\deftog{\SKV@swfalse\SKV@testopt\SKV@newtog{}} +\SKV@def*\SKV@newtog[#1]#2{\SKV@testopt{\SKV@n@wtog[#1]{#2}}{false}} +\SKV@def*\SKV@n@wtog[#1]#2[#3]{% + \SKV@in@n{,}{#2}{% + \SKV@err{% + Command \ifSKV@sw'\string\newtog'\else'\string\deftog'\fi\space + doesn't accept list + }{% + Please submit only one tog to \ifSKV@sw'\string\newtog'\else + '\string\deftog'\fi\space at a time.\SKV@MB You can use + \ifSKV@sw'\string\NewTogs'\else'\string\DefTogs'\fi\space + to define a set of togs. + }% + }{}% + \SKV@ensurenoscape{#2}% + \ifSKV@sw + \SKV@getswstate\newtog{#3}% + \SKV@ifcsdef{SKV@toggle@#1#2}{% + \SKV@err{Toggle '#1#2' already defined}% + {Please provide a new toggle or use '\string\deftog'.}% + }{}% + \else + \SKV@getswstate\deftog{#3}% + \SKV@ifcsundef{SKV@toggle@#1#2}{}{% + \SKV@vbmsg{warn}{Toggle '#1#2' redefined\on@line}% + }% + \fi + \SKV@csletcs{SKV@toggle@#1#2}{SKV@t@ggle\SKV@swval} +} +\SKV@pdef*\providetog#1{% + \SKV@ifcsdef{SKV@toggle@#1}{% + \SKV@vbmsg{info}% + {Toggle '#1' already defined.\SKV@MB Not redefined\on@line}% + }{\SKV@cslet{SKV@toggle@#1}\@secondoftwo}% +} +\SKV@pdef*\requiretog#1{\SKV@aftercs\requirecmd{SKV@toggle@#1}[2]{##2}} +\SKV@def*\settog#1#2{% + \SKV@ifcsdef{SKV@toggle@#1}{% + \SKV@ifcsdef{SKV@t@ggle#2}{% + \SKV@csletcs{SKV@toggle@#1}{SKV@t@ggle#2}% + }{% + \SKV@err{Inadmissible value '#2' for toggle '#1'}% + {Valid values for toggles are 'true', 'or', 'false', and 'off'.}% + }% + }{\SKV@undeftog@err{#1}}% +} +\SKV@def*\togon#1{% + \SKV@ifcsdef{SKV@toggle@#1}{% + \SKV@cslet{SKV@toggle@#1}\SKV@t@ggleon + }{\SKV@undeftog@err{#1}}% +} +\SKV@def*\togoff#1{% + \SKV@ifcsdef{SKV@toggle@#1}{% + \SKV@cslet{SKV@toggle@#1}\SKV@t@ggleoff + }{\SKV@undeftog@err{#1}}% +} +\SKV@newlet\togtrue\togon +\SKV@newlet\togfalse\togoff +\SKV@newlet\SKV@t@ggleon\@firstoftwo +\SKV@newlet\SKV@t@ggleoff\@secondoftwo +\SKV@newlet\SKV@t@ggletrue\@firstoftwo +\SKV@newlet\SKV@t@gglefalse\@secondoftwo +\SKV@def*\iftogon#1{% + \SKV@ifcsdef{SKV@toggle@#1}{\@nameuse{SKV@toggle@#1}}% + {\SKV@undeftog@err{#1}}% +} +\SKV@def*\iftogoff#1{% + \SKV@ifcsdef{SKV@toggle@#1}{% + \@nameuse{SKV@toggle@#1}\@secondoftwo\@firstoftwo}% + {\SKV@undeftog@err{#1}\@gobbletwo}% +} +\SKV@newlet\iftogtrue\iftogon +\SKV@newlet\iftogfalse\iftogoff +\deftog{pdf} +\SKV@ifundef\pdfoutput{}{% + \ifnum\pdfoutput<\@ne + \SKV@pdef*\SKV@ifpdf{\@secondoftwo}% + \else + \togon{pdf}% + \SKV@pdef*\SKV@ifpdf{\@firstoftwo}% + \fi + \SKV@pdef*\SKV@ifnotpdf{\SKV@ifpdf\@secondoftwo\@firstoftwo}% +} +\SKV@ifundef\ifnotpdf + {\let\ifnotpdf\SKV@ifnotpdf}% + {\SKV@vbmsg{info}{\SKV@useinfo{ifnotpdf}}} +\SKV@AtDocumentStart{% + \SKV@ifundef\ifpdf + {\SKV@pdef*\ifpdf{\SKV@ifpdf}}% + {\SKV@vbmsg{info}{\SKV@useinfo{ifpdf}}}% +} +\SKV@pdef*\SKV@definedkey@err#1{% + \SKV@ifcsundef{\XKV@header#1}{}{% + \SKV@err{Key '#1' already defined in family '\XKV@tfam'\SKV@MB + with prefix '\SKV@pref'}{Use another key name instead of '#1'.}% + }% +} +\SKV@pdef*\define@togkey{\SKV@skfalse + \XKV@t@stopta{\XKV@testoptd\SKV@define@togkey{}}} +\SKV@def*\SKV@define@togkey#1#2[#3]{% + \SKV@findkeypointers{#2}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@Undef+{\XKV@header\SKV@resa,SKV@toggle@#1\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \ifXKV@pl + \def\SKV@next{\SKV@aftercs\SKV@d@f@ne@togkey}% + \else + \def\SKV@next{\SKV@aftercs\SKV@d@fine@togkey}% + \fi + \begingroup + \edef\SKV@tempa{\endgroup + {\XKV@header\SKV@resa}{\SKV@resa}{#1\SKV@resa}{#3}}% + \aftergroup\SKV@next\SKV@tempa +} +\SKV@def*\SKV@d@fine@togkey#1#2#3#4#5{% + \SKV@d@f@ne@t@gkey#1{#2}{#3}{#4}% + {{\SKV@expargs{0}{\SKV@getswstate\SKV@d@fine@togkey}\XKV@resa\@nil + \settog{#3}{\SKV@swval}#5}}% +} +\SKV@def*\SKV@d@f@ne@togkey#1#2#3#4#5#6{% + \SKV@d@f@ne@t@gkey#1{#2}{#3}{#4}% + {{\SKV@expargs{0}{\SKV@getswstate\SKV@d@f@ne@togkey}\XKV@resa\@nil + \settog{#3}{\SKV@swval}#5}{#6}}% +} +\SKV@def*\SKV@d@f@ne@t@gkey#1#2#3#4#5{% + \deftog{#3}% + \ifXKV@st\XKV@define@default{#2}{#4}\fi + \edef#1##1{% + \def#1####1{##1\unexpanded{\XKV@sttrue\XKV@checkchoice[\XKV@resa]}% + {####1}{true,false,on,off}\unexpanded{#5}% + }% + }% + \ifXKV@pl#1\XKV@pltrue\else#1\XKV@plfalse\fi +} +\SKV@pdef*\define@togkeys{\SKV@skfalse + \XKV@plfalse\XKV@testoptd\SKV@define@togkeys{}} +\SKV@def*\SKV@define@togkeys#1#2[#3]{% + \SKV@despacelist{#2}% + \SKV@for*[,]\SKV@resa\SKV@tempa{% + \SKV@expargs{3}% + \SKV@d@fine@togkeys{{\SKV@tempa}}{{#1}{#3}}\@nil + }% +} +\SKV@def*\SKV@d@fine@togkeys#1#2#3{% + \SKV@findkeypointers{#1}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@Undef+{\XKV@header\SKV@resa,SKV@toggle@#2\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \edef\SKV@tempa##1##2##3{% + ##1\@nameuse{\XKV@header\SKV@resa}% + {\SKV@resa}{#2\SKV@resa}{#3}{{##2{#2\SKV@resa}{##3}}}% + }% + \SKV@tempa\SKV@d@f@ne@t@gkey\settog\XKV@resa +} +\SKV@pdef*\newordkey{\SKV@skfalse\XKV@testoptb\SKV@newordkey} +\SKV@def*\SKV@newordkey#1{% + \SKV@findkeypointers{#1}% + \SKV@expnext\SKV@definedkey@err\SKV@resa + \begingroup + \edef\SKV@tempa{\endgroup + \SKV@if@brack{\noexpand\XKV@d@fine@key{\SKV@resa}}% + {\noexpand\SKV@d@fine@key{\SKV@resa}}% + }% + \SKV@tempa +} +\SKV@pdef*\newcmdkey{\SKV@skfalse\XKV@testoptd\SKV@newcmdkey{cmd}} +\SKV@def*\SKV@newcmdkey#1#2[#3]#4{% + \SKV@reservmp{#1}\SKV@rmpnames + \SKV@findkeypointers{#2}% + \SKV@expnext\SKV@definedkey@err\SKV@resa + \ifXKV@st + \SKV@afterfi + \SKV@expnext\XKV@define@default\SKV@resa{#3}% + \fi + \begingroup + \edef\SKV@tempa{\def\SKV@noexpcs{\XKV@header\SKV@resa}####1}% + \edef\SKV@tempb{\def\SKV@noexpcs{#1\SKV@resa}\unexpanded{{##1}#4}}% + \expandafter\expandafter\expandafter\endgroup + \expandafter\SKV@tempa\expandafter{\SKV@tempb}% +} +\SKV@pdef*\newcmdkeys{\SKV@skfalse\XKV@testoptd\SKV@newcmdkeys{cmd}} +\SKV@def*\SKV@newcmdkeys#1#2[#3]{% + \SKV@despacelist{#2}% + \SKV@for*[,]\SKV@resa\SKV@tempa{% + \edef\SKV@tempa{\noexpand\SKV@newcmdkey{#1}{\SKV@tempa}}% + \SKV@tempa[#3]{}% + }% +} +\SKV@pdef*\newchoicekey{\SKV@skfalse + \XKV@testopta{\XKV@testoptb\SKV@newchoicekey}} +\SKV@def*\SKV@newchoicekey#1{% + \SKV@findkeypointers{#1}% + \SKV@expnext\SKV@definedkey@err\SKV@resa + \begingroup + \edef\SKV@tempa##1{\endgroup\SKV@testopt{##1{\SKV@resa}}{}}% + \SKV@tempa\XKV@d@fine@choicekey +} +\let\newmenukey\newchoicekey +\SKV@pdef*\newboolkey{\SKV@skfalse + \XKV@t@stopta{\XKV@testoptd\SKV@newboolkey{}}} +\SKV@def*\SKV@newboolkey#1#2[#3]{% + \SKV@findkeypointers{#2}% + \SKV@expnext\SKV@definedkey@err\SKV@resa + \ifXKV@pl + \def\SKV@next{\SKV@aftercs\XKV@d@f@ne@boolkey}% + \else + \def\SKV@next{\SKV@aftercs\XKV@d@fine@boolkey}% + \fi + \edef\SKV@tempa{{\XKV@header\SKV@resa}{\SKV@resa}{#1\SKV@resa}{#3}}% + \expandafter\SKV@next\SKV@tempa +} +\SKV@pdef*\newboolkeys{\SKV@skfalse + \XKV@plfalse\XKV@testoptd\SKV@newboolkeys{}} +\SKV@def*\SKV@newboolkeys#1#2[#3]{% + \SKV@despacelist{#2}% + \SKV@for*[,]\SKV@resa\SKV@tempa{% + \SKV@definedkey@err\SKV@tempa + \SKV@expargs{3}% + \XKV@d@fine@boolkeys{{\SKV@tempa}}{{#1}{#3}}\@nil + }% +} +\SKV@pdef*\newtogkey{\SKV@skfalse + \XKV@t@stopta{\XKV@testoptd\SKV@newtogkey{}}} +\SKV@def*\SKV@newtogkey#1#2[#3]{% + \SKV@findkeypointers{#2}% + \SKV@expnext\SKV@definedkey@err\SKV@resa + \ifXKV@pl + \def\SKV@next{\SKV@aftercs\SKV@d@f@ne@togkey}% + \else + \def\SKV@next{\SKV@aftercs\SKV@d@fine@togkey}% + \fi + \begingroup + \edef\SKV@tempa{\endgroup + {\XKV@header\SKV@resa}{\SKV@resa}{#1\SKV@resa}{#3}}% + \aftergroup\SKV@next\SKV@tempa +} +\SKV@pdef*\newtogkeys{\SKV@skfalse + \XKV@plfalse\XKV@testoptd\SKV@newtogkeys{}% +} +\SKV@def*\SKV@newtogkeys#1#2[#3]{% + \SKV@despacelist{#2}% + \SKV@for*[,]\SKV@resa\SKV@tempa{% + \SKV@definedkey@err\SKV@tempa + \SKV@expargs{3}\SKV@d@fine@togkeys{{\SKV@tempa}}{{#1}{#3}}\@nil + }% +} +\SKV@pdef*\define@biboolkeys{\SKV@compopta\define@boolkey\SKV@biboolkeys} +\SKV@pdef*\newbiboolkeys{\SKV@compopta\newboolkey\SKV@biboolkeys} +\SKV@pdef*\define@uniboolkeys{\SKV@compopta\define@boolkey\SKV@uniboolkeys} +\SKV@pdef*\newuniboolkeys{\SKV@compopta\newboolkey\SKV@uniboolkeys} +\SKV@pdef*\define@biuniboolkeys{% + \SKV@savstate + \SKV@t@stoptb{\SKV@compopta\define@boolkey\SKV@biuniboolkeys}% +} +\SKV@pdef*\newbiuniboolkeys{% + \SKV@savstate + \SKV@t@stoptb{\SKV@compopta\newboolkey\SKV@biuniboolkeys}% +} +\SKV@pdef*\define@bitogkeys{\SKV@compopta\define@togkey\SKV@bitogkeys} +\SKV@pdef*\newbitogkeys{\SKV@compopta\newtogkey\SKV@bitogkeys} +\SKV@pdef*\define@unitogkeys{\SKV@compopta\define@togkey\SKV@unitogkeys} +\SKV@pdef*\newunitogkeys{\SKV@compopta\newtogkey\SKV@unitogkeys} +\SKV@pdef*\define@biunitogkeys{% + \SKV@savstate + \SKV@t@stoptb{\SKV@compopta\define@togkey\SKV@biunitogkeys}% +} +\SKV@pdef*\newbiunitogkeys{% + \SKV@savstate + \SKV@t@stoptb{\SKV@compopta\newtogkey\SKV@biunitogkeys}% +} +\SKV@def*\SKV@compopta#1#2{% + \let\SKV@c@mpkey#1\let\SKV@co@pkey#2% + \SKV@testopt{\SKV@compoptb}{KV}% +} +\SKV@def*\SKV@compoptb[#1]#2{\SKV@testopt{\SKV@compoptc[#1]{#2}}{mp@}} +\SKV@def*\SKV@compoptc[#1]#2[#3]#4{% + \SKV@testopt{\SKV@co@pkey[#1]{#2}[#3]{#4}}{true}% +} +\SKV@def*\SKV@biboolkeys[#1]#2[#3]#4[#5]#6#7#8{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \ifswitchon{#3#4}{\switchoff{#3#6}}{\switchon{#3#6}#8\relax}% + }% + \@namedef{#1@#2@#6@func}{% + #8\relax + \ifswitchon{#3#6}{\switchoff{#3#4}}{\switchon{#3#4}#7\relax}% + }% + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@uniboolkeys[#1]#2[#3]#4[#5]#6#7#8{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \ifswitchon{#3#4}{\switchon{#3#6}#8\relax}{\switchoff{#3#6}}% + }% + \@namedef{#1@#2@#6@func}{% + #8\relax + \ifswitchon{#3#6}{\switchon{#3#4}#7\relax}{\switchoff{#3#4}}% + }% + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@biuniboolkeys[#1]#2[#3]#4[#5]#6#7#8{% + \SKV@aftercs\def{#1@#2@#6@func}{#8\relax}% + \ifswitchon{SKV@st}{% + \ifswitchon{SKV@pl}{% + \@namedef{#1@#2@#4@func}{% + #7\relax\ifswitchon{#3#4}{}{\switchoff{#3#6}}% + }% + }{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \ifswitchon{#3#4}{}{\switchon{#3#6}\@nameuse{#1@#2@#6@func}}% + }% + }% + }{% + \ifswitchon{SKV@pl}{% + \@namedef{#1@#2@#4@func}{% + #7\relax\ifswitchon{#3#4}{\switchoff{#3#6}}{}% + }% + }{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \ifswitchon{#3#4}{\switchon{#3#6}\@nameuse{#1@#2@#6@func}}{}% + }% + }% + }% + \SKV@resstate + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@bitogkeys[#1]#2[#3]#4[#5]#6#7#8{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \iftogon{#3#4}{\togoff{#3#6}}{\togon{#3#6}#8\relax}% + }% + \@namedef{#1@#2@#6@func}{% + #8\relax + \iftogon{#3#6}{\togoff{#3#4}}{\togon{#3#4}#7\relax}% + }% + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@unitogkeys[#1]#2[#3]#4[#5]#6#7#8{% + \@namedef{#1@#2@#4@func}{% + #7\relax + \iftogon{#3#4}{\togon{#3#6}#8\relax}{\togoff{#3#6}}% + }% + \@namedef{#1@#2@#6@func}{% + #8\relax + \iftogon{#3#6}{\togon{#3#4}#7\relax}{\togoff{#3#4}}% + }% + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@biunitogkeys[#1]#2[#3]#4[#5]#6#7#8{% + \@namedef{#1@#2@#6@func}{#8}% + \ifswitchon{SKV@st}{% + \ifswitchon{SKV@pl}{% + \@namedef{#1@#2@#4@func}{% + #7\relax\iftogoff{#3#4}{\togoff{#3#6}}{}% + }% + }{% + \@namedef{#1@#2@#4@func}{% + #7\relax\iftogoff{#3#4}{\togon{#3#6}#8\relax}{}% + }% + }% + }{% + \ifswitchon{SKV@pl}{% + \@namedef{#1@#2@#4@func}{% + #7\relax\iftogon{#3#4}{\togoff{#3#6}}{}% + }% + }{% + \@namedef{#1@#2@#4@func}{% + #7\relax\iftogon{#3#4}{\togon{#3#6}#8\relax}{}% + }% + }% + }% + \SKV@resstate + \SKV@compk@y{#1}{#2}{#3}{#4}{#5}{#6}% +} +\SKV@def*\SKV@compk@y#1#2#3#4#5#6{% + \SKV@c@mpkey+[#1]{#2}[#3]{#4}[#5]{\@nameuse{#1@#2@#4@func}}% + {\SKV@inputerr{##1}{#4}}% + \SKV@c@mpkey+[#1]{#2}[#3]{#6}[#5]{\@nameuse{#1@#2@#6@func}}% + {\SKV@inputerr{##1}{#6}}% +} +\SKV@pdef*\SKV@keylistrema#1\@nil#2{% + \long\edef\SKV@remainder{\unexpanded{#1}}% + \SKV@oifblank{\SKV@remainder}{}{% + \def\SKV@tempa##1#2\@nil{\def\SKV@remainder{##1}}% + \expandafter\SKV@tempa\SKV@remainder\@nil + }% +} +\SKV@pdef*\define@keylist{% + \SKV@define@keylist\define@key\define@cmdkey + \define@boolkey\define@togkey\define@choicekey +} +\SKV@pdef*\newkeylist{% + \SKV@define@keylist\newordkey\newcmdkey + \newboolkey\newtogkey\newchoicekey +} +\newcommand*\SKV@define@keylist[5]{% + \let\SKV@def@ordkey#1% + \let\SKV@def@cmdkey#2% + \let\SKV@def@boolkey#3% + \let\SKV@def@togkey#4% + \let\SKV@def@choicekey#5% + \SKV@savstate + \begingroup + \@makeother{\#}% + \SKV@t@stopta\SKV@d@fine@keylist +} +\SKV@def*\choicekeyvalues{% + \SKV@t@stopta{\SKV@testopt\SKV@choic@keyvalues\SKV@keyprefix}} +\SKV@def*\SKV@choic@keyvalues[#1]{% + \SKV@testopt{\SKV@choic@keyv@lues[#1]}\SKV@keyfamily} +\SKV@def*\SKV@choic@keyv@lues[#1][#2]#3#4{% + \ifSKV@st + \SKV@csedef{#1@#2@#3@altlist}{#4}% + \else + \SKV@ifcsundef{#1@#2@#3@altlist}{% + \SKV@csedef{#1@#2@#3@altlist}{#4}% + }{% + \SKV@err + {Problem with \string\choicekeyvalues\space for key '#3'}% + {You have attempted to set two \string\choicekeyvalues\SKV@MB + for choice key '#3' in the same family '#2'\SKV@MB + with the same prefix '#1'. You can only do\SKV@MB + that if you use the starred variant of \string\choicekeyvalues.}% + }% + \fi +} +\SKV@newlet\CKVS\choicekeyvalues +\SKV@def*\SKV@detok@z@p#1{% + \edef#1{\detokenize\expandafter{#1}\space}% + \edef#1{\expandafter\zap@space#1\@empty}% +} +\SKV@def*\SKV@detok@zsp#1{% + \SKV@ensurescape{#1}% + \begingroup + \SKV@for*[,]#1\SKV@tempa{% + \edef\SKV@tempa{\detokenize\expandafter{\SKV@tempa}\space}% + \edef\SKV@tempa{\expandafter\zap@space\SKV@tempa\@empty}% + \SKV@csxdef*{SKV@zsp@\expandafter\@gobble\SKV@tempa}% + {\SKV@expox{\SKV@tempa}}% + }% + \endgroup +} +\SKV@def*\SKV@detok@dsp#1{% + \SKV@ensurescape{#1}% + \begingroup + \SKV@for*[,]#1\SKV@tempa{% + \edef\SKV@tempa{\detokenize\expandafter{\SKV@tempa}}% + \SKV@despacectt\SKV@tempa + \SKV@csxdef*{SKV@dsp@\expandafter\@gobble\SKV@tempa}% + {\SKV@expox{\SKV@tempa}}% + }% + \endgroup +} +\SKV@def*\SKV@exceplist{% + \par,\if,\ifcase,\or,\else,\skif,\iftog,\ifswitch} +\SKV@detok@dsp\SKV@exceplist +\SKV@def*\SKV@noiferr#1#2{% + \SKV@err{You can't use '\@backslashchar #1' in\SKV@MB + key macro within keylist}% + {Use '\@backslashchar #2' instead of '\@backslashchar #1'.}% +} +\SKV@pdef*\uservalue{\uservalue} +\SKV@pdef*\guservalue{\guservalue} +\SKV@pdef*\savevalue{\savevalue} +\SKV@pdef*\gsavevalue{\gsavevalue} +\SKV@def\SKV@define@multkeys#1,#2,#3,#4\@nil{% + \let\SKV@altlisterr\z@ + \SKV@sp@def{#1}\SKV@tempa + \SKV@checkchoice{\SKV@tempa}{ord,cmd,bool,tog,choice,menu}{% + \ifnum\nr=5% + \let\SKV@keytype\nr + \else + \edef\SKV@keytype{\the\numexpr\nr+1}% + \fi + }{% + \SKV@checkchoice{\SKV@tempa}{1,2,3,4,5}{% + \edef\SKV@keytype{\the\numexpr\nr+1}% + }{% + \SKV@err{Unknown key type/identifier '\string#1'}% + {Valid key types are 'ord', 'cmd', 'bool', 'tog', + 'choice', 'menu',\SKV@MB and '1', '2', '3', '4', '5'}% + }% + }% + \SKV@ifnotempty{#2}{}{% + \SKV@err{No key name specified for key type '\string#1'}% + {\string\define@keylist: key name required for all keys}% + }% + \begingroup + \SKV@ifblank{#4}{}{% + \edef\SKV@tempb{\detokenize{#4}\space}% + \edef\SKV@tempb{\expandafter\zap@space\SKV@tempb\@empty}% + \@expandtwoargs\SKV@in@\SKV@dsp@if\SKV@tempb + \ifin@ + \@expandtwoargs\SKV@in@\SKV@dsp@iftog\SKV@tempb + \ifin@\else + \@expandtwoargs\SKV@in@\SKV@dsp@ifswitch\SKV@tempb + \ifin@\else\SKV@noiferr{if}{skif}\fi + \fi + \fi + \@expandtwoargs\SKV@in@\SKV@dsp@par\SKV@tempb + \ifin@\SKV@afterfi\SKV@noiferr{par}{endgraf}\fi + \@expandtwoargs\SKV@in@\SKV@dsp@ifcase\SKV@tempb + \ifin@\SKV@afterfi\SKV@noiferr{ifcase}{skifcase}\fi + \@expandtwoargs\SKV@in@\SKV@dsp@or\SKV@tempb + \ifin@\SKV@afterfi\SKV@noiferr{or}{skor}\fi + \@expandtwoargs\SKV@in@\SKV@dsp@else\SKV@tempb + \ifin@\SKV@afterfi\SKV@noiferr{else}{skelse}\fi + }% + \protected@edef\SKV@tempa{\endgroup + \ifcase\SKV@keytype\relax + \or + \noexpand\SKV@def@ordkey[\SKV@keyprefix]{\SKV@keyfamily}% + {#2}[#3]{\unexpanded{#4}}% + \or + \noexpand\SKV@def@cmdkey[\SKV@keyprefix]{\SKV@keyfamily}% + [\SKV@macroprefix]{#2}[#3]{\unexpanded{#4}}% + \or + \noexpand\SKV@def@boolkey+[\SKV@keyprefix]{\SKV@keyfamily}% + [\SKV@macroprefix]{#2}[#3]{\unexpanded{#4}}{% + \unexpanded{\SKV@inputerr{##1}{#2}}% + }% + \or + \noexpand\SKV@def@togkey+[\SKV@keyprefix]{\SKV@keyfamily}% + [\SKV@macroprefix]{#2}[#3]{\unexpanded{#4}}{% + \unexpanded{\SKV@inputerr{##1}{#2}}% + }% + \or + \SKV@ifcsdef{\SKV@keyprefix @\SKV@keyfamily @#2@altlist}{}% + {\let\SKV@altlisterr\@ne}% + \noexpand\SKV@def@choicekey*+[\SKV@keyprefix]{\SKV@keyfamily}{#2}% + [\unexpanded{\val\nr}]{% + \SKV@csuse{\SKV@keyprefix @\SKV@keyfamily @#2@altlist}% + }[#3]{\unexpanded{#4}}{\unexpanded{\SKV@inputerr{##1}{#2}}}% + \fi + }% + \SKV@tempa + \ifnum\SKV@altlisterr=\@ne + \SKV@afterfi + \SKV@err{% + Choice key '#2' can't be defined.\SKV@MB + Alternate key values list for '#2' is empty + }{% + You need to first define admissible\SKV@MB + key values through '\string\choicekeyvalues'\SKV@MB + for choice key '#2'. + }% + \fi +} +\SKV@def*\SKV@resvfampf#1#2{% + \begingroup + \SKV@expnext\XKV@checksanitizeb#2\SKV@tempa + \SKV@despacelist*\SKV@tempa + \XKV@knftrue + \XKV@whilist\SKV@tempa\SKV@tempa\ifXKV@knf\fi{% + \SKV@aftercs\ifx{XKV@#1}\SKV@tempa\XKV@knffalse\fi + }% + \unless\ifXKV@knf + \SKV@afterfi + \SKV@err{Key #1 '\SKV@tempa' is reserved}% + {You have used a reserved key #1: '\SKV@tempa'.\SKV@MB + Use another #1 name instead of '\SKV@tempa'.}% + \fi + \endgroup +} +\SKV@redef*\XKV@makepf#1{% + \SKV@sp@def{#1}\XKV@prefix + \unless\ifSKV@sk + \SKV@resvfampf{prefix}\SKV@rkpnames + \fi + \edef\SKV@pref{\ifx\XKV@prefix\@empty\else\XKV@prefix\fi}% + \edef\XKV@prefix{\ifx\XKV@prefix\@empty\else\XKV@prefix @\fi}% +} +\SKV@redef*\XKV@makehd#1{% + \SKV@expargs{3}\SKV@sp@def{{#1}}\XKV@header\@nil + \unless\ifSKV@sk + \SKV@resvfampf{family}\SKV@rfnames + \fi + \edef\XKV@header{% + \XKV@prefix\ifx\XKV@header\@empty\else\XKV@header @\fi}% +} +\SKV@def*\SKV@resolvekey#1#2\@nil#3{% + \def\SKV@resolve##1#2##2##3\@nil##4{% + \edef##4{\unexpanded{##2}}% + \ifx##4\@nnil + \edef##4{\unexpanded{##1}}% + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + \SKV@resolve#1#2{\@nil}\@nil#3% +} +\SKV@def*\SKV@addtouserval{% + \SKV@addtolist+{SKV@\XKV@header uservalue}\SKV@resa} +\SKV@def*\SKV@gaddtouserval{% + \SKV@addtolist+!{SKV@\XKV@header uservalue}\SKV@resa} +\SKV@def*\SKV@addtosave{\SKV@addtolist+{XKV@\XKV@header save}\SKV@resa} +\SKV@def*\SKV@gaddtosave{\begingroup + \edef\SKV@tempa{\endgroup + \SKV@addtolist+!{XKV@\XKV@header save}{\global{\SKV@resa}}}% + \SKV@tempa +} +\SKV@def*\SKV@findkeypointers#1{% + \SKV@resolvekey{#1}\uservalue\savevalue\@nil\SKV@resa + {\SKV@addtouserval\SKV@addtosave}% + {\SKV@resolvekey{#1}\guservalue\savevalue\@nil\SKV@resa + {\SKV@gaddtouserval\SKV@addtosave}% + {\SKV@resolvekey{#1}\savevalue\uservalue\@nil\SKV@resa + {\SKV@addtouserval\SKV@addtosave}% + {\SKV@resolvekey{#1}\gsavevalue\uservalue\@nil\SKV@resa + {\SKV@addtouserval\SKV@gaddtosave}% + {\SKV@resolvekey{#1}\guservalue\gsavevalue\@nil\SKV@resa + {\SKV@gaddtouserval\SKV@gaddtosave}% + {\SKV@resolvekey{#1}\gsavevalue\guservalue\@nil\SKV@resa + {\SKV@gaddtouserval\SKV@gaddtosave}% + {\SKV@resolvekey{#1}\uservalue\@nil\SKV@resa + {\SKV@addtouserval}% + {\SKV@resolvekey{#1}\guservalue\@nil\SKV@resa + {\SKV@gaddtouserval}% + {\SKV@resolvekey{#1}\savevalue\@nil\SKV@resa + {\SKV@addtosave}% + {\SKV@resolvekey{#1}\gsavevalue\@nil\SKV@resa + {\SKV@gaddtosave}% + {}}}}}}}}}}% +} +\SKV@predef*\define@key{\SKV@skfalse\XKV@testoptb\XKV@define@key} +\def\XKV@define@key#1{% + \SKV@findkeypointers{#1}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@csundef{\XKV@header\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \begingroup + \edef\SKV@tempa{\endgroup + \SKV@if@brack{\noexpand\XKV@d@fine@key{\SKV@resa}}% + {\noexpand\SKV@d@fine@key{\SKV@resa}}% + }% + \SKV@tempa +} +\def\XKV@d@fine@key#1[#2]#3{% + \XKV@define@default{#1}{#2}% + \@namedef{\XKV@header#1}##1{#3}% +} +\def\SKV@d@fine@key#1#2{\@namedef{\XKV@header#1}##1{#2}} +\SKV@def*\SKV@reservmp#1#2{% + \begingroup + \SKV@sp@def{#1}\SKV@tempb + \SKV@expnext\XKV@checksanitizeb#2\SKV@tempa + \SKV@despacelist*\SKV@tempa + \XKV@knftrue + \XKV@whilist\SKV@tempa\SKV@tempa\ifXKV@knf\fi{% + \ifx\SKV@tempb\SKV@tempa\XKV@knffalse\fi + }% + \unless\ifXKV@knf + \SKV@afterfi + \SKV@err{Macro prefix '\SKV@tempa' is reserved}% + {You have used a reserved macro prefix: '\SKV@tempa'.\SKV@MB + Use another macro prefix name instead of '\SKV@tempa'.}% + \fi + \endgroup +} +\SKV@predef*\define@cmdkey{% + \SKV@skfalse\XKV@testoptd\XKV@define@cmdkey{cmd}} +\SKV@predef*\define@cmdkeys{% + \SKV@skfalse\XKV@testoptd\XKV@define@cmdkeys{cmd}} +\def\XKV@define@cmdkey#1#2[#3]#4{% + \SKV@reservmp{#1}\SKV@rmpnames + \SKV@findkeypointers{#2}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@Undef+{\XKV@header\SKV@resa,#1\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \ifXKV@st + \SKV@afterfi + \SKV@expnext\XKV@define@default\SKV@resa{#3}% + \fi + \begingroup + \def\SKV@tempa{% + \expandafter\def\csname\XKV@header\SKV@resa\endcsname####1}% + \SKV@threexp\endgroup\expandafter\SKV@tempa\expandafter% + {\expandafter\def\csname#1\SKV@resa\endcsname{##1}#4}% +} +\SKV@predef*\define@choicekey{\SKV@skfalse + \XKV@testopta{\XKV@testoptb\XKV@define@choicekey}} +\SKV@newlet\define@menukey\define@choicekey +\def\XKV@define@choicekey#1{% + \SKV@findkeypointers{#1}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@csundef{\XKV@header\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \begingroup + \edef\SKV@tempa{\endgroup + \SKV@testopt{\noexpand\XKV@d@fine@choicekey{\SKV@resa}}{}% + }% + \SKV@tempa +} +\SKV@predef*\define@boolkey{\SKV@skfalse + \XKV@t@stopta{\XKV@testoptd\XKV@define@boolkey{}}} +\SKV@predef*\define@boolkeys{\SKV@skfalse + \XKV@plfalse\XKV@testoptd\XKV@define@boolkeys{}} +\def\XKV@define@boolkey#1#2[#3]{% + \SKV@findkeypointers{#2}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line}% + \SKV@Undef+{\XKV@header\SKV@resa,#1\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \ifXKV@pl + \def\SKV@next{\SKV@aftercs\XKV@d@f@ne@boolkey}% + \else + \def\SKV@next{\SKV@aftercs\XKV@d@fine@boolkey}% + \fi + \edef\SKV@tempa{{\XKV@header\SKV@resa}{\SKV@resa}{#1\SKV@resa}{#3}}% + \expandafter\SKV@next\SKV@tempa +} +\def\XKV@d@fine@boolkey#1#2#3#4#5{% + \XKV@d@f@ne@b@olkey#1{#2}{#3}{#4}% + {{\SKV@expargs{0}{\SKV@getswstate\XKV@d@fine@boolkey}\XKV@resa\@nil + \csname#3\SKV@swval\endcsname#5}}% +} +\def\XKV@d@f@ne@boolkey#1#2#3#4#5#6{% + \XKV@d@f@ne@b@olkey#1{#2}{#3}{#4}% + {{\SKV@expargs{0}{\SKV@getswstate\XKV@d@f@ne@boolkey}\XKV@resa\@nil + \csname#3\SKV@swval\endcsname#5}{#6}}% +} +\def\XKV@d@f@ne@b@olkey#1#2#3#4#5{% + \csnewif{if#3}% + \ifXKV@st\XKV@define@default{#2}{#4}\fi + \edef#1##1{% + \def#1####1{##1\unexpanded{\XKV@sttrue\XKV@checkchoice[\XKV@resa]}% + {####1}{true,false,on,off}\unexpanded{#5}% + }% + }% + \ifXKV@pl#1\XKV@pltrue\else#1\XKV@plfalse\fi +} +\def\XKV@d@fine@boolkeys#1#2#3{% + \SKV@findkeypointers{#1}% + \SKV@ifcsundef{\XKV@header\SKV@resa}{}{% + \SKV@vbmsg{warn}{% + Key '\SKV@resa' in family '\XKV@tfam' with\SKV@MB + prefix '\SKV@pref' redefined\on@line} + \SKV@Undef+{\XKV@header\SKV@resa,#2\SKV@resa}% + }% + \SKV@expnext\SKV@reservkey@err\SKV@resa + \SKV@expnext\SKV@suspkey@err\SKV@resa + \edef\SKV@tempa##1##2##3{% + ##1\@nameuse{\XKV@header\SKV@resa}{\SKV@resa}% + {#2\SKV@resa}{#3}{{##2{#2\SKV@resa##3}}}% + }% + \SKV@tempa\XKV@d@f@ne@b@olkey\@nameuse\XKV@resa +} +\SKV@pdef*\reserve@keys{\@testopt\SKV@reserve@keys{KV}} +\SKV@def*\SKV@reserve@keys[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@res@rve@keys\SKV@tempa{#2}{#3}% + }% +} +\SKV@def*\SKV@res@rve@keys#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@for@b\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam + \SKV@res@rve@k@ys{#3}% + }% +} +\SKV@def*\SKV@res@rve@k@ys#1{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@ifcsdef{\XKV@header\SKV@tempa}{% + \SKV@err{% + Key '\SKV@tempa' with prefix '\SKV@pref' in family + \SKV@MB '\XKV@tfam' is already defined + }{% + Key '\SKV@tempa' in family '\XKV@tfam' can't be reserved. + \SKV@MB You may wish to remove it before reserving it. + \SKV@MB In that case, you can use '\string\remove@keys'. + }% + }{% + \SKV@csedef*{\XKV@header\SKV@tempa}{% + \SKV@err{% + Key '\SKV@tempa' with prefix '\SKV@pref' in family + '\XKV@tfam'\SKV@MB was reserved\on@line + }{% + Unreserve '\SKV@tempa' before use. + }% + }% + \SKV@csdef*{\XKV@header\SKV@tempa @reser}{}% + }% + }% +} +\SKV@def*\SKV@reservkey@err#1{% + \SKV@ifcsdef{\XKV@header#1@reser}\SKV@err\@gobbletwo + {Key '#1' with prefix '\SKV@pref'\SKV@MB + is reserved in family '\XKV@tfam'}{Unreserve '#1' before use.}% +} +\SKV@def*\ifkeyreserved{\@testopt\SKV@ifkeyreserved{KV}} +\SKV@def*\SKV@ifkeyreserved[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@swtrue + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@ifkey@reserved\SKV@tempa{#2}{#3}% + }% + \@nameuse{SKV@\ifSKV@sw2\else1\fi of2}% +} +\SKV@def*\SKV@ifkey@reserved#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@sp@def{#3}\XKV@tkey + \XKV@whilist\XKV@fams\XKV@tfam\ifSKV@sw\fi{% + \XKV@makehd\XKV@tfam + \SKV@ifcsdef{\XKV@header\XKV@tkey @reser}\SKV@swfalse\relax + }% +} +\SKV@def*\ifkeysuspended{\@testopt\SKV@ifkeysuspended{KV}} +\SKV@def*\SKV@ifkeysuspended[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@swtrue + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@ifkey@suspended\SKV@tempa{#2}{#3}% + }% + \@nameuse{SKV@\ifSKV@sw2\else1\fi of2}% +} +\SKV@def*\SKV@ifkey@suspended#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@sp@def{#3}\XKV@tkey + \XKV@whilist\XKV@fams\XKV@tfam\ifSKV@sw\fi{% + \XKV@makehd\XKV@tfam + \SKV@ifcsdef{\XKV@header\XKV@tkey @susp}\SKV@swfalse\relax + }% +} +\SKV@def*\ifkeydefined{\@testopt\SKV@ifkeydefined{KV}} +\SKV@def*\SKV@ifkeydefined[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@swtrue + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@ifk@ydefined\SKV@tempa{#2}{#3}% + }% + \@nameuse{SKV@\ifSKV@sw2\else1\fi of2}% +} +\SKV@def*\SKV@ifk@ydefined#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@sp@def{#3}\XKV@tkey + \XKV@whilist\XKV@fams\XKV@tfam\ifSKV@sw\fi{% + \XKV@makehd\XKV@tfam + \SKV@ifcsdef{\XKV@header\XKV@tkey}\SKV@swfalse\relax + }% +} +\SKV@def*\SKV@disabledkey@err{% + \SKV@err{% + Key '\SKV@disabledkey' with prefix '\SKV@pref' in family + \SKV@MB '\XKV@tfam' was disabled\on@line + }{% + You can't set or reset '\SKV@disabledkey' at this\SKV@MB + late stage. Perhaps you're required to set it \SKV@MB + earlier, within a package or in the document's preamble. + }% +} +\def\disable@keys{% + \SKV@ifstar{\@testopt\XKV@disable@keys{KV}}% + {\@testopt\SKV@disable@keys{KV}}% +} +\SKV@def*\SKV@disable@keys[#1]#2#3{% + \SKV@locfalse + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@dis@ble@keys\SKV@tempa{#2}{#3}% + }% +} +\SKV@def*\SKV@dis@ble@keys#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@for@b\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam + \SKV@dis@ble@k@ys{#3}% + }% +} +\SKV@def*\SKV@dis@ble@k@ys#1{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@ifcsundef{\XKV@header\SKV@tempa}{% + \ifSKV@verbose\SKV@disabledkey@warn\fi + }{% + \SKV@dis@ble@k@y@\SKV@tempa + }% + }% +} +\def\SKV@disabledkey@warn{% + \SKV@warn{Key '\SKV@tempa' with prefix '\SKV@pref' + \SKV@MB is undefined in family '\XKV@tfam': + \SKV@MB can't be \ifSKV@loc localized + \else disabled\fi\on@line + }% +} +\def\SKV@disabledkey@info{% + \SKV@info{Key '\SKV@disabledkey' with prefix '\SKV@pref' + in family '\XKV@tfam'\SKV@MB was \ifSKV@loc localized + \else disabled\fi\on@line + }% +} +\SKV@def*\SKV@dis@ble@k@y@#1{% + \edef\SKV@disabledkey{#1}% + \SKV@ifcsundef{\XKV@header#1@default}{% + \edef#1{\noexpand\XKV@define@key{#1}}% + }{% + \edef#1{\noexpand\XKV@define@key{#1}[]}% + }% + \SKV@expargs{0}#1\SKV@disabledkey@err\@nil + \ifSKV@verbose\SKV@disabledkey@info\fi +} +\SKV@pdef*\localize@keys{% + \SKV@onlypackage\SKV@savstate + \SKV@t@stopta{\@testopt\SKV@localize@keys{KV}}% +} +\SKV@def*\SKV@localize@keys[#1]#2#3{% + \SKV@loctrue + \ifSKV@st + \expandafter\SKV@BeforeDocumentStart + \else + \expandafter\SKV@AfterPackageEnd + \fi + {% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@dis@ble@keys\SKV@tempa{#2}{#3}% + }% + }% + \SKV@resstate +} +\SKV@def*\SKV@suspkey@err#1{% + \SKV@ifcsdef{\XKV@header#1@susp}\SKV@err\@gobbletwo + {Key '#1' was suspended from family '\XKV@tfam'}% + {You can use \string\restore@key\space to restore '#1'.}% +} +\SKV@def*\SKV@suspkey@e@r{% + \SKV@err{Key '\SKV@suspkey' in family '\XKV@tfam' was\SKV@MB + suspended\on@line}{You can use \string\restore@key\space + to restore '\SKV@suspkey'.}% +} +\SKV@pdef*\suspend@keys{\@testopt\SKV@suspend@keys{KV}} +\SKV@def*\SKV@suspend@keys[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@s@spend@keys\SKV@tempa{#2}{#3}% + }% +} +\SKV@def*\SKV@s@spend@keys#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@for@b\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam + \SKV@s@sp@nd@keys{#3}% + }% +} +\SKV@def*\SKV@s@sp@nd@keys#1{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@ifcsundef{\XKV@header\SKV@tempa}{% + \SKV@vbmsg{warn}{% + Key '\SKV@tempa' with prefix '\SKV@pref' + \SKV@MB is undefined in family '\XKV@tfam': + \SKV@MB can't be suspended\on@line}% + }{% + \SKV@s@sp@nd@k@ys\SKV@tempa + }% + }% +} +\SKV@def*\SKV@s@sp@nd@k@ys#1{% + \SKV@ifcsdef{\XKV@header#1@susp}{% + \SKV@err{% + Key '#1' with prefix '\SKV@pref' + was suspended from family '\XKV@tfam' + }{% + You can use \string\restore@key\space to restore '#1'. + }% + }{% + \edef\SKV@suspkey{#1}% + \global\SKV@csletcs{\XKV@header#1@susp}{\XKV@header#1}% + \SKV@csedef*{\XKV@header#1}{\SKV@suspkey@e@r}% + \SKV@vbmsg{info}{Key '\SKV@suspkey' with prefix '\SKV@pref' in + \SKV@MB family '\XKV@tfam' was suspended\on@line}% + }% +} +\SKV@pdef*\restore@keys{\@testopt\SKV@restore@keys{KV}} +\SKV@def*\SKV@restore@keys[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@r@store@keys\SKV@tempa{#2}{#3}% + }% +} +\SKV@def*\SKV@r@store@keys#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@for@b\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam + \SKV@r@st@re@keys{#3}% + }% +} +\SKV@def*\SKV@r@st@re@keys#1{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@ifcsundef{\XKV@header\SKV@tempa}{% + \SKV@vbmsg{warn}{Key '\SKV@tempa' with prefix '\SKV@pref' + \SKV@MB is undefined in family '\XKV@tfam': + \SKV@MB can't be restored\on@line}% + }{% + \SKV@r@st@re@k@ys\SKV@tempa + }% + }% +} +\SKV@def*\SKV@r@st@re@k@ys#1{% + \SKV@ifcsundef{\XKV@header#1@susp}{% + \SKV@err{Key '#1' wasn't suspended from family + \SKV@MB '\XKV@tfam': can't be restored\on@line}% + {I can't restore unsuspended key '#1'}% + }{% + \edef\SKV@restoredkey{#1}% + \SKV@csletcs{\XKV@header#1}{\XKV@header#1@susp}% + \SKV@csundef{\XKV@header#1@susp}% + \SKV@vbmsg{info}{Key '\SKV@restoredkey' with prefix + '\SKV@pref' in family \SKV@MB '\XKV@tfam' restored\on@line}% + }% +} +\SKV@pdef*\remove@keys{\@testopt\SKV@remove@keys{KV}} +\SKV@def*\SKV@remove@keys[#1]#2#3{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for*[,]\SKV@tempa\SKV@tempa{% + \SKV@expnext\SKV@r@move@keys\SKV@tempa{#2}{#3}% + }% +} +\SKV@def*\SKV@r@move@keys#1#2#3{% + \XKV@makepf{#1}% + \XKV@checksanitizeb{#2}\XKV@fams + \SKV@despacelist*\XKV@fams + \SKV@for@b\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam + \SKV@r@m@ve@keys{#3}% + }% +} +\SKV@def*\SKV@r@m@ve@keys#1{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@despacelist*\SKV@tempa + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@ifcsundef{\XKV@header\SKV@tempa @susp}{}{% + \SKV@err{Key '\SKV@tempa' with prefix '\SKV@pref' + \SKV@MB was suspended from family '\XKV@tfam': + \SKV@MB I can't remove/unreserve it\on@line. + \SKV@MB You need to first restore key '\SKV@tempa' + \SKV@MB before you can remove/unreserve it}% + {First restore key '\SKV@tempa' before removing it.}% + }% + \SKV@ifcsundef{\XKV@header\SKV@tempa}{% + \SKV@vbmsg{warn}{Key '\SKV@tempa' with prefix '\SKV@pref' + \SKV@MB is undefined in family '\XKV@tfam': + \SKV@MB I can't remove or unreserve it\on@line}% + }{% + \SKV@r@m@ve@k@ys\SKV@tempa + }% + }% +} +\SKV@def*\SKV@r@m@ve@k@ys#1{% + \begingroup + \edef\SKV@tempa{\XKV@header#1,\XKV@header#1@default, + \XKV@header#1@func,\XKV@header#1@altlist, + \XKV@header#1@reser + }% + \SKV@for@b\SKV@tempa\SKV@tempa{% + \SKV@despacectt\SKV@tempa + \SKV@csgundef{\SKV@tempa}% + }% + \endgroup + \SKV@vbmsg{info}{Key '#1' with prefix '\SKV@pref' in family\SKV@MB + '\XKV@tfam' was removed\on@line}% +} +\SKV@newlet\unreserve@keys\remove@keys +\SKV@def*\uservaluekeys{\XKV@testoptb\SKV@uservaluekeys} +\SKV@def*\SKV@uservaluekeys#1{% + \SKV@ifcsundef{SKV@\XKV@header uservalue}{% + \XKV@checksanitizeb{#1}\SKV@tempa + \SKV@csxdef*{SKV@\XKV@header uservalue}{\SKV@expox{\SKV@tempa}}% + }{% + \SKV@aftercs\XKV@merge{SKV@\XKV@header uservalue}{#1}\XKV@getsg + }% +} +\SKV@def*\SKV@undefinedkeys#1#2#3{% + \edef\SKV@resvd##1{\SKV@addtolist![;]##1{(#1:#2:#3)}}% + \SKV@resvd\SKV@und@finedkeys +} +\SKV@predef*\ExecuteOptionsX{% + \SKV@sktrue\XKV@stfalse\XKV@plfalse\XKV@t@stopte\XKV@setkeys} +\SKV@predef*\setkeys{\SKV@sktrue\XKV@testopta{\XKV@testoptc\XKV@setkeys}} +\SKV@newlet\SKVSetKeys\setkeys +\SKV@pdef*\UseSkeyvalSetkeys{% + \SKV@ifx\setkeys\SKVSetKeys\relax{\let\setkeys\SKVSetKeys}% +} +\SKV@redef*\XKV@setkeys[#1]#2{% + \def\skif##1{% + \SKV@ensurenoscape{##1}% + \SKV@ifcsdef{if##1}{}{% + \SKV@err{Undefined boolean '##1'}% + {'\string\skif' error: boolean '##1' undefined.}% + }% + \csname if##1\endcsname + }% + \let\skifx\ifx + \let\skifcase\ifcase + \let\skor\or + \let\skelse\else + \let\skfi\fi + \let\SKV@und@finedkeys\@empty + \XKV@checksanitizea{#2}\XKV@resb + \SKV@normalizea\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=\z@\let\XKV@rm\@empty\fi + \XKV@usepresetkeys{#1}{preseth}% + \SKV@expnext\XKV@s@tkeys\XKV@resb{#1}% + \XKV@usepresetkeys{#1}{presett}% + \let\CurrentOption\@empty + \SKV@skfalse + \SKV@ifx\SKV@und@finedkeys\@empty{}{% + \def\SKV@tempa{stop}% + \SKV@warn{\string\setkeys\space error: Unknown keys:\SKV@MB + \SKV@expox\SKV@und@finedkeys}% + \typein[\answer]{% + ^^JThe following keys are undefined and couldn't be set: + ^^J\SKV@expox\SKV@und@finedkeys + ^^JEnter 'stop' or 'go' and <return> to continue.}% + \ifx\answer\SKV@tempa\stop\fi + }% +} +\SKV@redef*\XKV@s@tk@ys#1=#2=#3\@nil{% + \XKV@g@tkeyname#1=\@nil\XKV@tkey + \SKV@expnext\SKV@sp@def\XKV@tkey\XKV@tkey + \SKV@ifx\XKV@tkey\@empty{% + \SKV@ifempty{#2}{}{% + \SKV@err{No key specified for value '#2'}% + {You have supplied a value '#2' without key name.\SKV@MB + This is certainly an error on your part.\SKV@MB + I can't process this value without key name.\SKV@MB + Please enter 'X' and <return>. Correct the error and rerun.} + }% + }{% + \@expandtwoargs\in@{,\XKV@tkey,}{,\XKV@na,}% + \unless\ifin@ + \XKV@knftrue + \SKV@sp@def{#2}\XKV@tempa + \ifXKV@preset + \XKV@s@tk@ys@{#3}% + \else + \ifXKV@pl + \XKV@for@eo\XKV@fams\XKV@tfam{% + \XKV@makehd\XKV@tfam\XKV@s@tk@ys@{#3}% + }% + \else + \XKV@whilist\XKV@fams\XKV@tfam\ifXKV@knf\fi{% + \XKV@makehd\XKV@tfam\XKV@s@tk@ys@{#3}% + }% + \fi + \fi + \ifXKV@knf + \ifXKV@inpox + \SKV@ifx\XKV@doxs\relax{% + \unless\ifx\@currext\@clsextension + \let\CurrentOption\XKV@tkey\@unknownoptionerror + \fi + }{% + \XKV@doxs + }% + \else + \ifXKV@st + \XKV@addtolist@o\XKV@rm\CurrentOption + \else + \SKV@undefinedkeys\SKV@pref\XKV@fams\XKV@tkey + \fi + \fi + \else + \ifXKV@inpox\ifx\XKV@testclass\XKV@documentclass + \SKV@expnext\XKV@useoption\CurrentOption + \fi\fi + \fi + \fi + }% +} +\SKV@redef*\XKV@s@tk@ys@#1{% + \SKV@ifcsundef{\XKV@header\XKV@tkey}{% + \SKV@vbmsg{warn}{No key macro for key '\XKV@tkey'\SKV@MB + in families '\XKV@fams'}% + }{% + \XKV@knffalse + \SKV@ifcsundef{SKV@\XKV@header uservalue}{}{% + \SKV@aftercs\SKV@testuservaluekey + {SKV@\XKV@header uservalue}\XKV@tkey + \ifSKV@uv + \SKV@ifnotblank{#1}{}{% + \SKV@err{You're required to supply a value\SKV@MB + for key '\XKV@tkey' in families '\XKV@fams'}% + {Please supply a value for key '\XKV@tkey'\SKV@MB + of families '\XKV@fams' and rerun.\SKV@MB + The originator of this key meant it to\SKV@MB + require a user value.}% + }% + \fi + }% + \SKV@ifcsundef{XKV@\XKV@header save}{}{% + \SKV@aftercs\XKV@testsavekey{XKV@\XKV@header save}\XKV@tkey + }% + \ifXKV@rkv + \ifXKV@sg\expandafter\global\fi + \SKV@aftercs\let{XKV@\XKV@header\XKV@tkey @value}\XKV@tempa + \fi + \SKV@expnext\XKV@replacepointers\XKV@tempa + \SKV@ifblank{#1}{% + \SKV@ifcsundef{\XKV@header\XKV@tkey @default}{% + \SKV@err{'\XKV@tkey' in family '\XKV@tfam' with\SKV@MB + prefix '\XKV@pref' has no default value and\SKV@MB + you haven't supplied a value for it either}% + {Supply a value for key '\XKV@tkey' and rerun.}% + }{% + \SKV@xaftercs\XKV@default{\XKV@header\XKV@tkey @default}\@nil + }% + }{% + \XKV@savstate + \csname\XKV@header\XKV@tkey\expandafter + \endcsname\expandafter{\XKV@tempa}\relax + \XKV@resstate + }% + }% +} +\SKV@def*\SKV@testuservaluekey#1#2{% + \SKV@swtrue\SKV@uvfalse + \XKV@whilist#1\SKV@tempa\ifSKV@sw\fi{% + \ifx#2\SKV@tempa\SKV@uvtrue\SKV@swfalse\fi + }% +} +\SKV@def*\SKV@remspaceletter#1#2{% + \begingroup + \SKV@toks\expandafter{#2 #1}% + \def\SKV@tempa##1 #1##2\@nil{% + \SKV@ifblank{##2}{% + \SKV@toks{##1}% + }{% + \SKV@toks{##1#1##2}% + \expandafter\SKV@tempa\the\SKV@toks\@nil + }% + }% + \expandafter\SKV@tempa\the\SKV@toks\@nil + \xdef#2{\the\SKV@toks}% + \endgroup +} +\SKV@def*\SKV@remletterspace#1#2{% + \begingroup + \SKV@toks\expandafter{#2#1 }% + \def\SKV@tempa##1#1 ##2\@nil{% + \SKV@ifblank{##2}{% + \SKV@toks{##1}% + }{% + \SKV@toks{##1#1##2}% + \expandafter\SKV@tempa\the\SKV@toks\@nil + }% + }% + \expandafter\SKV@tempa\the\SKV@toks\@nil + \xdef#2{\the\SKV@toks}% + \endgroup +} +\SKV@def*\SKV@remletterletter#1#2{% + \begingroup + \SKV@toks\expandafter{#2#1#1}% + \def\SKV@tempa##1#1#1##2\@nil{% + \SKV@toks{##1#1##2}% + \SKV@ifblank{##2}{}{% + \expandafter\SKV@tempa\the\SKV@toks\@nil}% + }% + \expandafter\SKV@tempa\the\SKV@toks\@nil + \def\SKV@tempa##1#1#1\@nil{\gdef#2{##1}}% + \expandafter\SKV@tempa\the\SKV@toks#1\@nil + \endgroup +} +\SKV@def*\SKV@remcommaequal#1#2{% + \begingroup + \SKV@toks\expandafter{#2#1}% + \def\SKV@tempa##1#1##2\@nil{% + \SKV@ifblank{##2}{% + \SKV@toks{##1}% + }{% + \SKV@toks{##1=##2}% + \expandafter\SKV@tempa\the\SKV@toks\@nil + }% + }% + \expandafter\SKV@tempa\the\SKV@toks\@nil + \xdef#2{\the\SKV@toks}% + \endgroup +} +\SKV@def*\SKV@normalizea#1{% + \@expandtwoargs\SKV@in@{ ,}{\SKV@expox{#1}}% + \ifin@\SKV@remspaceletter{,}#1\fi + \@expandtwoargs\SKV@in@{, }{\SKV@expox{#1}}% + \ifin@\SKV@remletterspace{,}#1\fi + \@expandtwoargs\SKV@in@{,,}{\SKV@expox{#1}}% + \ifin@\SKV@remletterletter{,}#1\fi + \@expandtwoargs\SKV@in@{ =}{\SKV@expox{#1}}% + \ifin@\SKV@remspaceletter{=}#1\fi + \@expandtwoargs\SKV@in@{= }{\SKV@expox{#1}}% + \ifin@\SKV@remletterspace{=}#1\fi + \@expandtwoargs\SKV@in@{==}{\SKV@expox{#1}}% + \ifin@\SKV@remletterletter{=}#1\fi + \@expandtwoargs\SKV@in@{,=}{\SKV@expox{#1}}% + \ifin@\SKV@remcommaequal{,=}#1\fi + \@expandtwoargs\SKV@in@{=,}{\SKV@expox{#1}}% + \ifin@\SKV@remcommaequal{=,}#1\fi +} +\SKV@def*\SKV@gobblescape{% + \ifnum\escapechar>255\else + \ifnum\escapechar<0\else + \expandafter\expandafter + \csname @gobble\ifnum\escapechar=32 space\fi + \SKV@threexp\endcsname + \fi\fi + \string% +} +\SKV@def*\SKV@save#1{% + \SKV@ensurescape{#1}% + \edef\SKV@tempa{saved\SKV@gobblescape#1}% + \SKV@ifcsdef\SKV@tempa{% + \SKV@err{% + \string#1\space already saved. You've to restore\SKV@MB + it or undefine '\SKV@csexpox{\SKV@tempa}' before\SKV@MB + you can save \string#1\space again + }\@ehc + }{% + \SKV@ifdef{#1}{% + \SKV@cslet\SKV@tempa{#1}% + }{% + \SKV@err{Undefined '\string#1' can't be saved\on@line}% + {You can't restore undefined commands.\SKV@MB + So why save undefined '\string#1' here?}% + }% + }% +} +\SKV@def*\SKV@restore#1{% + \SKV@ensurescape{#1}% + \edef\SKV@tempa{saved\SKV@gobblescape#1}% + \SKV@ifcsdef\SKV@tempa{% + \def\SKV@next{\SKV@letcs#1\SKV@tempa\SKV@csundef\SKV@tempa}% + }{% + \SKV@err{% + '\@nameuse\SKV@tempa' is undefined:\SKV@MB + '\string#1' can't be restored\on@line + }{% + '\string#1' can't be restored\on@line.\SKV@MB + Note that each call to \string\SKV@restore\space + undefines\SKV@MB the saved command. + }% + }% + \SKV@next +} +\SKV@providedef*\input@path{} +\SKV@pdef*\InputFileOnce{% + \def\SKV@inputfile##1##2{% + \InputIfFileExists{##2}{% + \SKV@cslet{##2@read}\@empty + \let\SKV@inp@tfile\relax + }{% + \typein[\answer]{^^J! File '\string##2' does not exist.^^J + Enter filename or 'no' and <return> to continue.}% + \SKV@oifstrequal{\answer}{no}{% + \let\SKV@inp@tfile\relax + }{% + \def\SKV@inp@tfile{% + \SKV@expargs{0}{\SKV@InputFileOnce[##1]}\answer\@nil + }}}}% + \let\SKV@no\z@ + \SKV@save\input@path + \SKV@t@stopta{\SKV@testopt\SKV@InputFileOnce{.}}% +} +\SKV@def*\SKV@InputFileOnce[#1]#2{% + \SKV@appto!\input@path{{#1/}}% + \ifSKV@st + \SKV@afterelsefi + \SKV@inputfile{#1}{#2}% + \else + \SKV@afterfi + \SKV@ifcsdef{#2@read}{% + \SKV@vbmsg{warn}{File '#2' already read:\SKV@MB + File '#2' can't be read again}% + }{% + \SKV@inputfile{#1}{#2}% + }% + \fi + \SKV@inp@tfile + \ifnum\SKV@no=\z@\SKV@restore\input@path\fi + \let\SKV@no\@ne +} +\SKV@pdef*\SKV@notloadederr#1{% + \SKV@ifcsdef{ver@#1.\@pkgextension}{}{% + \SKV@err{Package '#1' wasn't loaded}% + {Document has begun and package '#1' wasn't loaded.\SKV@MB + You wanted package '#1' loaded.}% + }% +} +\SKV@def*\SKV@notloaded{} +\SKV@pdef*\@ensurepackageloaded#1{% + \SKV@for[,]{#1}\SKV@resvd{% + \edef\SKV@resvd##1##2{\SKV@appto!##1{##2{\SKV@resvd}}}% + \SKV@resvd\SKV@notloaded\SKV@doloaded + }% +} +\SKV@AtDocumentStart{% + \let\SKV@doloaded\SKV@notloadederr + \SKV@notloaded + \global\let\SKV@doloaded\@gobble +} +\SKV@onlypreamble{% + \SKV@preamblecmds,\SKV@onlypreamble,\@afterpackageloaded, + \@ifpackagecurrent,\@ifpackagenotcurrent,\@ifloadedandcurrent, + \@ensurepackageloaded,\SKV@AtPackageEnd,\SKV@AtClassEnd, + \SKV@BeforePackageEnd,\SKV@BeforeClassEnd,\SKV@AfterPackageEnd, + \SKV@AfterClassEnd,\SKV@BeforeLastPage,\SKV@AfterLastPage, + \SKV@AfterDocumentEnd,\UseSkeyvalSetkeys +} + +\endinput + +%%% End of file skeyval.sty %%% diff --git a/macros/latex/contrib/skeyval/tex/skeyval-core.tex b/macros/latex/contrib/skeyval/tex/skeyval-core.tex new file mode 100644 index 0000000000..e0ae452a73 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-core.tex @@ -0,0 +1,8466 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% 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 <return> + \MessageBreak to proceed. If that doesn't work, type X then + <return>\MessageBreak to quit. +} +\let\skvgenericexception\@latexerr +\protected\def\skvtracingall{% + \tracinggroups\@ne\tracingifs\@ne\loggingall\tracingassigns\@ne +} +\let\skvloggingall\skvtracingall +\long\def\skviofii#1#2{#1} +\long\def\skviiofii#1#2{#2} +% A \relax'ed command should not be redefined, because we don't know +% if it is temporarily or permanently made \relax. +\long\def\skvifdefinable#1#2{% + \ifdefined#1% + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {\@latex@error{Command '\string#1' already defined}\skv@ehd} + {#2}% +} +% \skvgenloop{<parser>}{<list>}{<1.parameter.callback>} +% +% 1. The parser may be empty. This is the case for nsv/tsv lists. +% 2. The normalization of the list, including dealing with active parsers, +% is left to the caller. +% 3. \@iden will help remove spurious leading spaces. +% +\protected\def\skvgenloop#1#2#3{% + \def\skv@gendo##1#1{% + \ifx\skv@gendo##1\expandafter\@gobble\else\expandafter\@iden\fi + {#3\expandafter\skv@gendo\@iden}% + }% + \expandafter\skv@gendo\@iden#2#1\skv@gendo#1% +} +% \skvrescan{<macro>} +\protected\def\skvrescan#1{% + \begingroup + \endlinechar\m@ne\newlinechar\m@ne + \catcode`\@=11\relax + \everyeof{\skv@rescan@guard}% + \def\reserved@a##1\skv@rescan@guard{% + \endgroup + \edef#1{\unexpanded\expandafter{\@gobble##1}}% + }% + % \relax protects any blank space leading the content of #1: + \expandafter\reserved@a\scantokens\expandafter{\expandafter\relax#1}% +} + +% Process a comma list with a simple callback. This can't be used if #2 +% isn't scannable or if scanning #2 will change the signature of +% its token list. +% +% \skv@commaact{<list>}{<1.parameter.callback>} +% +% Example: +% +% \def\emptify{\skv@commaact{\def##1{}}} +% +\begingroup +\catcode`\,=\active +\protected\gdef\skv@commaact#1#2{% + \begingroup + \toks@{}% + \def,##1{% + \ifx\skv@commaact##1\else + \ifx,##1% + % Double comma seen: + \expandafter\expandafter\expandafter,% + \else + \toks@\expandafter{\the\toks@#2}% + \fi + \fi + }% + \begingroup + \edef\skv@tempa{\unexpanded{,#1}}% + \catcode`\,=\active + \skvrescan\skv@tempa + % Add a trailing \skv@commaact, in case the list is ended by a comma (,): + \expandafter\endgroup\skv@tempa,\skv@commaact + \expandafter\endgroup\the\toks@ +} +\endgroup + +\protected\def\skvnewregister#1#2{% + \def\skv@prova{#1}% + \def\skv@provb{\bool}% + % We don't want to expose \if while in the next conditional: + \def\skv@provc{\if}% + \ifx\skv@prova\skv@provb + \let\skv@prova\skv@provc + \fi + \skv@commaact{#2}{% + \skvifdefinable##1{% + \csname new\expandafter\expandafter\expandafter\@gobble + \expandafter\string\skv@prova\endcsname##1% + }% + }% +} +\let\skvnewregisters\skvnewregister +\skvnewregisters\toks{\skv@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<some token> (star not seen) +% +% ** This can't be used to test the presence of explicit left brace. +% +\long\def\skvxifnextchar#1#2#3#4{% + % #4 will be seen as a space only if that space is active. But + % \skvifstrcmpTF will detokenize that space before comparison. + \skvifstrcmpTF{ }{#4}{% + \skvxifnextchar#1{#2}{#3}% + }{% + \skvifstrcmpTF{#4}{#1}{#2}{\skvifntypeTF{#4}{#3#4}{#3{#4}}}% + }% +} + +\long\def\skvgobbleleadingspaces{\skvsimpleifnextchar x{}{}} +\long\protected\def\skvstarorlong#1{% + \skvifstar{\let\l@ngrel@x\relax#1}{\let\l@ngrel@x\long#1}% +} +\long\protected\def\skv@testopt#1#2{\skvifnextchar[{#1}{#1[{#2}]}} +\long\protected\def\skv@testpnopt#1#2{\skvifnextchar({#1}{#1({#2})}} +\long\protected\def\skvifstar#1{\skvifnextchar*{\@firstoftwo{#1}}} +\def\skv@def@#1#2#3{% + \ifdefined#3% + \ifx#3\relax\else + \skv@err{Command \detokenize{#3} already exists}\skv@ehd + \fi + \fi + \ifcat$\detokenize{#2}$\else + \ifx#2p\expandafter\expandafter\expandafter\protected\fi + \fi + \l@ngrel@x\csname#1def\endcsname#3% +} +\def\skv@csdef@#1#2#3{% + \def\skv@prova{\skv@def@{#1}{#2}}% + \expandafter\skv@prova\csname#3\endcsname +} +\def\skv@redef@#1#2#3{% + \ifcat$\detokenize{#2}$\else + \ifx#2p\expandafter\expandafter\expandafter\protected\fi + \fi + \l@ngrel@x\csname#1def\endcsname#3% +} +\protected\def\skvrobustdef{\skvstarorlong{\skv@def@{}{p}}} +\skvrobustdef*\skvrobustredef{\skvstarorlong{\skv@redef@{}{p}}} +\skvrobustdef*\skvrobustgdef{\skvstarorlong{\skv@def@{g}{p}}} +\skvrobustdef*\skvnewdef{\skvstarorlong{\skv@def@{}{}}} +\skvrobustdef*\skvrenewdef{\skvstarorlong{\skv@redef@{}{}}} +\skvrobustdef*\skvnewedef{\skvstarorlong{\skv@def@{e}{}}} +\skvrobustdef*\skvnewgdef{\skvstarorlong{\skv@def@{g}{}}} +\skvrobustdef*\skvrobustcsdef{\skvstarorlong{\skv@csdef@{}{p}}} +\skvrobustdef*\skvnewcsdef{\skvstarorlong{\skv@csdef@{}{}}} +\skvrobustdef*\skvifplus#1{\skvifnextchar+{\@firstoftwo{#1}}} + +\skvrobustdef*\skvshowcs#1{% + \begingroup\expandafter\endgroup + \expandafter\show\csname#1\endcsname +} +\skvrobustdef*\skvcomment{% + \begingroup + \def\do##1{\catcode`##1=12\relax}% + \dospecials + \skv@endcomment +} +\begingroup +\catcode`\!=0 +\catcode`\\=12 +!long!gdef!skv@endcomment#1\endcomment{!endgroup} +!endgroup +%\skvrobustdef\skv@c@mment#1\endcomment{\endgroup} + +\skvnewedef*\skv@hashchar{\string#} +\let\nofilter\relax +% Don't use '=' at the end of the following definition; the user +% might have put '=': +\skvrobustdef*\skvnewlet#1{\skvifdefinable#1\relax\let#1 } +\skvnewlet\skv@nil\relax +\skvnewlet\skvrelax\relax +\skvnewdef*\skv@relaxtoks{\relax} +\skvnewdef*\skv@truetoks{true} +\skvnewdef*\skv@falsetoks{false} +\skvnewdef*\skv@nnil{\skv@nil} +\skvnewdef\skv@car#1#2\car@nil{#1} +\skvnewdef\skv@car@detok#1#2\car@nil{\detokenize{#1}} +\skvnewdef\skv@removetonnil#1\skv@nnil{} +\skvnewdef\skv@catchtonnil#1\skv@nnil{#1} +\long\def\@gobblethree#1#2#3{} +\skvnewdef\skv@gobbletoend#1\skv@gobble@nil{} +\skvnewlet\skv@gobble@nil\relax +\def\@space{ } +\let\then\iffalse +\skvnewdef\skvswap#1#2{#2#1} +\skvnewdef*\skv@quark{\@gobble\skv@quark} +\skvnewlet\skvrom\romannumeral +\skvnewdef*\skv@rej{^skv^} +\skvnewdef*\skv@dotna{.na} +\skvnewdef\skvafterfi#1\fi{\fi#1} +\skvnewdef\skvafterfifi@inneriscond#1\fi#2\fi{\fi#1\fi} +\skvnewdef\skvafterfifi#1\fi#2\fi{\fi\fi#1} +\skvnewdef\skvafterelsei#1\else#2\fi{\fi#1} +\skvnewlet\skvafterelse\skvafterelsei +\skvnewdef\skvafterelseii#1\else#2\fi{\fi#2} +\skvnewdef*\skv@simplearg{##1} +\skvnewlet\skvsanitizemacro\@onelevel@sanitize +% \skvaddtotoks{<token register>}{<token to add>} +\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{<toks>}{<true>}{<false>} +\newcommand\skvifleadspaceTF[3]{% + \romannumeral\csname + @\skvifnullTF{#1}{second}{\iffalse{\fi\skv@ifleadspace.#1 x}}% + oftwo\endcsname{0 #2}{0 #3}% +} +\skvnewdef\skv@ifleadspace#1 {% + \expandafter\skvifnullTF\expandafter{\@gobble#1}{first}{second}% + \expandafter\@gobble\expandafter{\iffalse}\fi +} + +% \skvifldspaceTF is faster than \skvifleadspaceTF. +% +% \skvifldspaceTF{ }{}{} gives <true> as desired. +\begingroup +\lccode`\&=0 \catcode`\&=7 \lccode`\!=0 \catcode`\!=8 +\lowercase{\endgroup + \skvnewdef\skvifldspaceTF#1{\skv@ifldspace!#1! &} + \skvnewdef\skv@ifldspace#1! #2&{\skvifblankFT{#2}} + \skvnewdef\skvtrimspace#1{\skv@trimspace@a.#1& &} + \skvnewdef\skv@trimspace@a#1 &{\skv@trimspace@b#1&} + \skvnewdef\skv@trimspace@b#1{% + \unexpanded\expandafter{% + \romannumeral0% + \expandafter\skvifldspaceTF\expandafter{\@gobble#1}{% + \@gobble#1% + }{% + \expandafter\@space\@gobble#1% + }% + }% + } +} +\skvnewdef\skvotrimspace{\expandafter\skvtrimspace\expandafter} +\skvnewdef\skvxtrimspace#1{% + \expandafter\skvtrimspace\expandafter{\romannumeral-`\q#1}% +} +\skvrobustdef*\skvdespace#1#2{\edef#2{\skvtrimspace{#1}}} +\skvrobustdef*\skvdespacecontent#1{% + \edef#1{\expandafter\skvtrimspace\expandafter{#1}}% +} +\skvnewdef*\skvcsuse#1{% + \skvifcsname#1\then + \csname#1\expandafter\endcsname + \fi +} +\skvrobustdef*\skvcsdef#1{\expandafter\def\csname#1\endcsname} +\skvrobustdef*\skvcsgdef#1{\expandafter\gdef\csname#1\endcsname} +\skvrobustdef*\skvcsedef#1{\expandafter\edef\csname#1\endcsname} +\skvrobustdef*\skvcsxdef#1{\expandafter\xdef\csname#1\endcsname} +\skvnewdef\skvaftercsname#1#2{% + \expandafter\skvswap\expandafter{\csname#2\endcsname}{#1}% +} +\skvnewlet\skvaftercs\skvaftercsname +\skvnewdef*\skvifdef#1\then{\if0\skvifdefTF{#1}{0}{1}} +\skvnewdef*\skvifundef#1\then{\if0\skvifdefTF{#1}{1}{0}} +% \skvifcsname x\then\def\x{T}\else\def\x{F}\fi +\skvnewdef*\skvifcsname#1\then{\if0\skvifcsdefTF{#1}{0}{1}} +\skvnewdef*\skvifdefTF#1{% + \skvifxTF{#1}\skv@undefined{% + \@secondoftwo + }{% + \ifx\relax#1% + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% +} +\skvnewdef*\skvifdefFT#1{\skvifdefTF{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skvifcsdefTF#1{% + \skvifblankTF{#1}{% + \expandafter\@secondoftwo\@gobble + }{% + \ifcsname#1\endcsname + \expandafter\@firstofone + \else + \expandafter\expandafter\expandafter + \@secondoftwo\expandafter\@gobble + \fi + }{% + \expandafter\ifx\csname#1\endcsname\relax + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% +} +\skvnewdef*\skvifcsdefFT#1{\skvifcsdefTF{#1}\@secondoftwo\@firstoftwo} +% Don't check for blank #1. For internal use: +\skvnewdef*\skvifnamedefTF#1{% + \ifcsname#1\endcsname + \expandafter\ifx\csname#1\endcsname\relax + \expandafter\expandafter\expandafter\@secondoftwo + \else + \expandafter\expandafter\expandafter\@firstoftwo + \fi + \else + \expandafter\@secondoftwo + \fi +} +\skvnewdef*\skvifnamedefFT#1{\skvifnamedefTF{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skv@ifx@#1#2\skv@ifx@nil{\expandafter#1} +\skvnewdef*\skv@ifx@@#1#2\skv@ifx@nil{#1} +\skvnewdef*\skvifxTF#1#2{% + \csname @\expandafter\expandafter\expandafter + \ifx\skv@ifx@#1\batchmode\skv@ifx@nil\skv@ifx@@#2\noboundary\skv@ifx@nil + first\else second\fi oftwo\endcsname +} +\skvnewdef*\skvifxFT#1#2{\skvifxTF{#1}{#2}\@secondoftwo\@firstoftwo} +\skvnewdef*\skv@ghost{\@gobble\skv@ghost} +\skvnewdef\skvifemptyTF#1{% + \ifx#1\@empty\skv@ghost + \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi +} +\skvnewdef\skvifcsemptyTF#1{\expandafter\skvifemptyTF\csname#1\endcsname} +\skvnewdef\skvifdefemptyTF#1{% + \skvifblankTF{#1}\@secondoftwo{% + \skvifntypeTF{#1}{% + \skvifdefTF{#1}{\skvifxTF#1\@empty}{\@secondoftwo}% + }{% + \@secondoftwo + }% + }% +} +\begingroup +% Keep lccode, in case the test occurs in a table: +\lccode`\&=0 \catcode`\&=8 +\lowercase{\endgroup + \skvnewdef\skvifnullTF#1{% + \csname @\ifcat&\detokenize{#1}&first\else second\fi oftwo\endcsname + } + \skvnewdef\skviflacus#1\then{\ifcat&\detokenize{#1}&} +} +\skvnewdef\skvifblankTF#1{% + \expandafter\skvifnullTF\expandafter{\@gobble#1.}% +} +\skvnewdef\skvifblankFT#1{\skvifblankTF{#1}\@secondoftwo\@firstoftwo} + +\skvnewdef*\skvifboolTF#1{% + \skvifblankTF{#1}{% + \@secondoftwo + }{% + \skvifcsdefTF{if#1}{% + \csname @\csname if#1\endcsname + first\else second\fi oftwo\endcsname + }{% + \skv@err{Undefined boolean '#1'}\skv@ehd + }% + }% +} +\skvnewdef*\skvifboolFT#1{\skvifboolTF{#1}\@secondoftwo\@firstoftwo} +% \skvifbool sw1\then <true> \else <false> \fi +\skvnewdef*\skvifbool#1\then{\if0\skvifboolTF{#1}01} +\skvnewdef*\skvifdefboolTF#1{% + \csname @\csname if#1\endcsname first\else second\fi oftwo\endcsname +} +\skvnewdef*\skvifdefboolFT#1{\skvifdefboolTF{#1}\@secondoftwo\@firstoftwo} +\skvrobustdef*\skvaftergroupifboolTF#1\endgroup{% + \skvifboolTF{#1}{\endgroup\@firstoftwo}{\endgroup\@secondoftwo}% +} +\skvrobustdef*\skvaftergroupifboolFT#1\endgroup{% + \skvifboolTF{#1}{\endgroup\@secondoftwo}{\endgroup\@firstoftwo}% +} +% \skvifboolvalTF{<toks>}{<true>}{<false>} +\skvrobustdef*\skvifboolvalTF#1{% + \skvxifinTF{,\expandafter\skvtrimspace\expandafter + {\detokenize{#1}},}{,\detokenize{true,false},}% +} +\skvrobustdef*\skvxifboolvalTF#1{% + \begingroup + \edef\skv@prova{#1}% + \skvdespacecontent\skv@prova + \skvexpandtwoargs{\endgroup\skvifinTF} + {,\skvoxdetok\skv@prova,}{,\detokenize{true,false},}% +} +% Generate error if <toks> is not a valid boolean value, otherwise +% execute #2: +% \skvifboolvalT{<toks>}{<true>} +\skvrobustdef*\skvifboolvalT#1#2{% + \skvxifinTF{,\expandafter\skvtrimspace\expandafter + {\detokenize{#1}},}{,\detokenize{true,false},}% + {#2}{\skv@badboolerr{#1}}% +} +% Convert bool value (true or false) to switch value (00 or 01): +\skvrobustdef*\skvbooltoswitch#1#2{% + \begingroup + \def\reserved@a##1#1##2##3\skv@nil{% + \def#2{##2}% + \ifx#2\@nnil + \skv@err{Bad boolean value '#1' can't be + \MessageBreak converted to switch value}\@ehc + \fi + }% + \reserved@a true{00}false{01}#1{\@nil}\skv@nil + \skvaftergroupdef#2\endgroup +} +\skvrobustdef*\skv@badboolerr#1{% + \skv@err{Invalid boolean value '#1'; expected 'true' or 'false'}\skv@ehd +} + +% Switches 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{<toks>} +% +% Collect tokens in a group for later exiting the group with them: +% +\skvrobustdef*\skv@addtocollection#1{% + \edef\skv@collection{% + \ifdefined\skv@collection\skvexpandonce\skv@collection\fi + \unexpanded{#1}% + }% +} +% Don't use \do for \dl, in case \do appears as a garbage: +% \skvgarbageitems{\dl<cmd1>\gdl<cmd2>...} +\skvrobustdef*\skvgarbageitems{\skvappto\skv@garbagelist} +\skvrobustdef*\skvdumpgarbage{% + \let\gdl\skvgundef\let\dl\skvundef + \skv@garbagelist + \def\skv@garbagelist{}% +} + +\skvifdefTF\pdfstrcmp{% + \let\skv@strcmp\pdfstrcmp +}{% + \RequirePackage{pdftexcmds}% + \skvifdefTF\pdf@strcmp{% + \let\skv@strcmp\pdf@strcmp + }{% + \skv@err{Neither '\string\pdfstrcmp' nor '\string\pdf@strcmp' exists}\skv@ehd + }% +} +\skvnewdef\skvifstrcmpTF#1#2{% + \csname @\ifnum\skv@strcmp{\detokenize{#1}}% + {\detokenize{#2}}=0first\else second\fi oftwo\endcsname +} +\skvnewdef*\skvifstrcmpFT#1#2{\skvifstrcmpTF{#1}{#2}\@secondoftwo\@firstoftwo} +\skvnewdef\skvifstreq#1#2\then{% + \ifnum\skv@strcmp{\detokenize{#1}}{\detokenize{#2}}=\skvz@ +} +\skvnewdef\skvoifstrcmpTF#1#2{% + \csname @\ifnum\skv@strcmp{\skvexpandonce{#1}}% + {\skvexpandonce{#2}}=0first\else second\fi oftwo\endcsname +} +\skvnewdef\skvxifstrcmpTF#1#2{% + \csname @\ifnum\skv@strcmp{#1}{#2}=0first\else + second\fi oftwo\endcsname +} +\skvrobustdef\skvcslet#1#2{\expandafter\let\csname#1\endcsname#2} +\skvrobustdef\skvnewcslet#1{% + \skvifcsdefTF{#1}{% + \skv@err{Command '\skvnoexpandcs{#1}' already defined}\skv@ehd + }{% + \skvcslet{#1}% + }% +} +\skvrobustdef\skvletcs#1#2{% + \begingroup\expandafter\endgroup\expandafter + \let\expandafter#1\csname#2\endcsname +} +\skvrobustdef\skvnewletcs#1{% + \skvifdefTF{#1}{% + \skv@err{Command '\string#1' already defined}\skv@ehd + }{% + \skvletcs#1% + }% +} +\skvrobustdef*\skvcsletcs#1#2{% + \begingroup\expandafter\endgroup + \expandafter\let\csname#1\expandafter + \endcsname\csname#2\endcsname +} +\skvrobustdef\skvnewcsletcs#1{% + \skvifcsdefTF{#1}{% + \skv@err{Command '\skvnoexpandcs{#1}' already defined}\skv@ehd + }{% + \skvcsletcs{#1}% + }% +} +\skvrobustdef*\skvglobalizecs#1{% + \begingroup\expandafter\endgroup\expandafter + \global\expandafter\let\csname#1\expandafter\endcsname + \csname#1\endcsname +} +\skvrobustdef*\skvaftergroupdef#1\endgroup{% + \skvexpanded{\endgroup\skvcmdexit#1}% +} +\skvrobustdef*\skvaftergroupcsdef#1\endgroup{% + \skvexpanded{\endgroup\expandafter\skvcmdexit\csname#1\endcsname}% +} + +% Taking commands and booleans out of a local group under \edef: +\skvnewdef*\skvcmdexit#1{% + \skvifdefTF{#1}{% + \edef\noexpand#1{\noexpand\unexpanded{\skvexpandonce{#1}}}% + }{}% +} +\skvnewdef*\skvcsexit#1{% + % If #1 is undefined, \skvexpandcsonce will make it relax. The + % preliminary test in \skvcmdexit will deal with that situation. + \skvaftercs\skvcmdexit{#1}% +} +% \edef\x{\endgroup\skvcmdexitloop{\x\y}}: +\skvnewdef*\skvcmdexitloop#1{\skv@cmdexitloop#1\end} +\skvnewdef*\skv@cmdexitloop#1{% + \ifx\end#1\else\skvcmdexit{#1}\expandafter\skv@cmdexitloop\fi +} +\skvnewdef*\skvboolexit#1{% + \expandafter\noexpand\csname\expandafter\@gobblethree + \string#1#1true\else false\fi\endcsname +} +% \edef\x{\endgroup\skvboolexitloop{\ifboola\ifboolb}}: +\skvnewdef*\skvboolexitloop#1{\skv@boolexitloop#1\end} +\skvnewdef*\skv@boolexitloop#1{% + \ifx\end#1\else\skvboolexit{#1}\expandafter\skv@boolexitloop\fi +} + +\skvnewdef*\skvprotect#{% + \ifx\protect\@typeset@protect\skv@protect\@firstofone\fi + \ifx\protect\@unexpandable@protect\skv@protect\skvunexpandable\fi + \ifx\protect\noexpand\skv@protect\unexpanded\fi + \ifx\protect\string\skv@protect\detokenize\fi + \relax\@firstofone +} +\skvnewdef*\skv@protect#1#2\relax\@firstofone{\fi#1} +\skvnewdef*\skvunexpandable#1{\unexpanded{\skvprotect{#1}}} +\skvrobustdef\skvifnotnil#1{% + \begingroup + \edef\skv@prova{\unexpanded{#1}}% + \expandafter\endgroup\ifx\skv@prova\skv@nnil + \expandafter\@gobble\else\expandafter\@firstofone\fi +} +% \skvadvanceno{<cmd>}{<number>} +\skvrobustdef*\skvsetnumber#1{% + \begingroup + \def\x{\edef\x{\endgroup\def\noexpand#1{\the\count@}}\x}% + \afterassignment\x\count@ +} +\skvnewlet\skvsetno\skvsetnumber +% \skvadvanceno{<cmd>}{<number>} +\skvrobustdef*\skvadvanceno{\skv@advanceno\relax} +\skvrobustdef*\skvgadvanceno{\skv@advanceno\global} +\skvrobustdef*\skv@advanceno#1#2#3{% + % Raise error if #3 isn't an integer, \chardef'd or \countdef'd: + \begingroup + \count@#3\relax + \endgroup + \skvifdefTF#2{% + #1\edef#2{\the\numexpr#2+#3\relax}% + }{% + \skv@err{Number '\string#2' is not defined}\skv@ehd + }% +} +\skvrobustdef*\skvpushnumber#1{% + \skvifdefTF#1{% + \edef#1{\the\numexpr#1+1\relax}% + }{% + \def#1{0}% + }% +} +\skvrobustdef*\skvpopnumber#1{% + \skvifdefTF#1{% + \edef#1{\the\numexpr#1+1\relax}% + }{% + \skv@err{Number '\string#1' is not defined}\skv@ehd + }% +} + +\skvrobustdef\skv@testst#1{% + \skvifstar{\skv@tempsttrue#1}{\skv@tempstfalse#1}% +} +% For internal use: +\skvrobustdef\skv@i@ifstar#1{\@ifnextchar*{\@firstoftwo{#1}}} +\skvrobustdef\skv@i@testst#1{% + \skv@i@ifstar{\skv@tempsttrue#1}{\skv@tempstfalse#1}% +} +\skvrobustdef\skv@testcl#1{% + \skvifnextchar!{\skv@cltrue#1}{\skv@clfalse#1}% +} +\skvrobustdef\skv@teststopt#1#2{\skv@testst{\skv@testopt{#1}{#2}}} + +% Remove spurious leading and trailing spaces before checking for outer brace. +% Such spaces do hide the outer brace. +% If we were to use \skvtrimspace for \skvifbracedTF, then the need +% for double \romannumeral will cause problems of premature expansion. +\begingroup +\lccode`\&=0 \catcode`\&=7 +\skvnewgdef\skvifbracedTF#1{% + \expandafter\skv@ifbraced@a\expandafter{% + \romannumeral-`\q\skv@ifbraced@b.#1& &% + }% +} +\skvnewgdef\skv@ifbraced@a#1{\skv@ifbraced@d#1\brace@nil{#1}} +\skvnewgdef\skv@ifbraced@b#1 &{\skv@ifbraced@c#1&} +\skvnewgdef\skv@ifbraced@c#1{% + \expandafter\skvifldspaceTF\expandafter{\@gobble#1}% + {\@gobble#1}{\expandafter\@space\@gobble#1}% +} +\skvnewgdef\skv@ifbraced@d#1\brace@nil#2{% + \skvifstrcmpTF{#1}{#2}\@secondoftwo\@firstoftwo +} +\endgroup + +\skvnewdef\skvifbracedFT#1{\skvifbracedTF{#1}\@secondoftwo\@firstoftwo} + +% \skvstripouterbraces{<nr>}<cmd> +% Example: +% \def\x{{{{X}}}} +% \skvstripouterbraces{2}\x +\skvrobustdef*\skvstripouterbraces#1#2{% + \skvifemptyTF#2{}{% + \begingroup + \@tempcnta\skvz@ + \expandafter\skv@stripouterbraces#2\strip@nil{#2}{#1}% + }% +} +\skvrobustdef*\skv@stripouterbraces#1\strip@nil#2#3{% + \edef\skv@prova{\unexpanded{#1}}% + \skvifxTF#2\skv@prova{% + \skvaftergroupdef#2\endgroup + }{% + \advance\@tempcnta\@ne + \skvifnumTF\@tempcnta=#3{% + \let#2\skv@prova + \skvaftergroupdef#2\endgroup + }{% + \let#2\skv@prova + \expandafter\skv@stripouterbraces#2\strip@nil{#2}{#3}% + }% + }% +} +\skvrobustdef*\skvifescapedTF#1{% + \begingroup\escapechar92\relax + \edef\x{\expandafter\skv@car\string#1x\car@nil}% + \expandafter\endgroup + \csname @\ifx\x\@backslashchar first\else second\fi oftwo\endcsname +} +\skvnewdef\skvifescapedFT#1{\skvifescapedTF{#1}\@secondoftwo\@firstoftwo} +\skvnewdef*\skvremovescape#1{\expandafter\@gobble\string#1} +\skvnewdef*\skvgobblescape#1{% + \skvifblankTF{#1}{}{% + \skvifnumTF\escapechar>255{\string#1}{% + \skvifnumTF\escapechar<0{\string#1}{% + \skvifnumTF\escapechar=32{% + \expandafter\@gobblespace\string#1% + }{% + \skvremovescape{#1}% + }% + }% + }% + }% +} +\skvnewdef*\skvoxdetok#1{\detokenize\expandafter{#1}} +\skvnewdef*\skvexpandonce#1{\unexpanded\expandafter{#1}} +\skvnewlet\skvxonce\skvexpandonce +\skvnewlet\skvxaft\expandafter +\skvnewdef*\skvexpandtwice#1{% + \unexpanded\expandafter\expandafter\expandafter{#1}% +} +\skvnewdef*\skvexpandthrice#1{% + \unexpanded\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\expandafter{#1}% +} +\skvnewdef*\skvnoexpandcs#1{\skvexpandonce{\csname#1\endcsname}} +\skvnewdef*\skvexpandcsonce#1{% + \expandafter\skvexpandonce\expandafter{\csname#1\endcsname}% +} +\skvnewdef*\skvexpandcstwice#1{% + \expandafter\skvexpandtwice\expandafter{\csname#1\endcsname}% +} +\skvnewdef*\skvexpandcsthrice#1{% + \expandafter\skvexpandthrice\expandafter{\csname#1\endcsname}% +} +\skvrobustdef\skvexpanded#1{\begingroup\edef\x{\endgroup#1}\x} +\skvrobustdef\skvexpandarg#1#2{\skvexpanded{\unexpanded{#1}{#2}}} +\skvrobustdef\skvexpandargonce#1#2{% + \skvexpanded{\unexpanded{#1}{\skvexpandonce{#2}}}% +} +\skvrobustdef*\skvexpandtwoargs#1#2#3{\skvexpanded{\unexpanded{#1}{#2}{#3}}} +\skvrobustdef*\skvexpandtwoargsonce#1#2#3{% + \skvexpanded{\unexpanded{#1}{\skvexpandonce{#2}}{\skvexpandonce{#3}}}% +} +\skvrobustdef\skvexpandsecond#1#2{\skvexpanded{\unexpanded{#1}#2}} +\skvrobustdef\skvexpandsecondonce#1#2{% + \skvexpanded{\unexpanded{#1}\skvexpandonce{#2}}% +} +\skvnewdef\skvexpandnext#1#2{\expandafter\skvswap\expandafter{#2}{#1}} +\skvnewdef\skvexpandbracenext#1#2{% + \expandafter\skvswap\expandafter{\expandafter{#2}}{#1}% +} +\skvnewlet\skvexpbracenext\skvexpandbracenext +% Eg, \skvexpandintonext\x[\y], \skvexpandintonext\x{\y}. +% This isn't expandable, because #1 may not be just one token. +\skvnewdef\skvexpandintonext#1{% + \begingroup + \edef\reserved@a{\endgroup\unexpanded{#1}}% + \expandafter\reserved@a\expandafter +} +\skvrobustdef*\skvifinTF#1#2{% + \skvtestifin{#1}{#2}% + \csname @\ifin@ first\else second\fi oftwo\endcsname +} +\skvrobustdef*\skvifinFT#1#2{\skvifinTF{#1}{#2}\@secondoftwo\@firstoftwo} +\skvrobustdef*\skvxifinTF#1#2{\skvexpandtwoargs\skvifinTF{#1}{#2}} +\skvrobustdef*\skvxifinFT#1#2{\skvxifinTF{#1}{#2}\@secondoftwo\@firstoftwo} +\skvrobustdef*\skvifindetokTF#1#2#3{% + \skvexpandtwoargs\skvifinTF{#1\skvoxdetok{#2}#1}{#1\skvoxdetok{#3}#1}% +} +\skvnewdef\skviffound#1\in#2\then{\skvtestifin{#1}{#2}\ifin@} +\skvnewdef\skvxiffound#1\in#2\then{\skvexpandtwoargs\skvtestifin{#1}{#2}\ifin@} + +% \skv@kvsplit{<kvpair>}{<parametered.callback>} +\skvrobustdef*\skv@kvsplit#1#2{% + \begingroup + \def\skv@tempa##1=##2=##3\skv@kvsplit{\endgroup#2}% + \skv@tempa#1==\skv@kvsplit +} +\skvrobustdef*\skv@okvsplit#1{\skvexpbracenext\skv@kvsplit{#1}} +% \skv@slashsplit{<slashlist>}{<parametered.callback>} +% Patrons calling \skv@slashsplit should note that it may return +% ^skv^ for any missing components: +\skvrobustdef*\skv@slashsplit#1#2{% + \begingroup + \def\skv@tempa##1/##2/##3/##4/##5/##6/##7/##8/##9\skv@slashsplit{% + \endgroup#2% + }% + \skv@tempa#1/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^/^skv^\skv@slashsplit +} +\skvrobustdef*\skv@oslashsplit#1{\skvexpbracenext\skv@slashsplit{#1}} + +% \skv@splitpath@a{<path.macro>}{<pref.macro>}{<fam.macro>} +% Here, <path.macro> has the form {<pref>/<fam>}. +\skvrobustdef*\skv@splitpath@a#1#2#3{% + \begingroup + \ifx#1\@empty + \skv@err{No key can have empty path}\skv@ehd + \else + \skv@oslashsplit{#1}{% + \edef#2{\skvtrimspace{##1}}% + \edef#3{\skvtrimspace{##2}}% + \skvstripouterbraces{2}#2% + \skvstripouterbraces{2}#3% + \ifx#3\skv@rej + \let#3#2% + \let#2\skvdefaultprefix + \fi + }% + \fi + \skvexpanded{\endgroup\skvcmdexit#2\skvcmdexit#3}% +} +% \skv@splitpath@b{<path.macro>}{<pref.macro>}{<fam.macro>} +% Here, <path.macro> has the form {<pref>}{<fam>}. +% Here, we don't expect to find empty entries in #1, since +% \skv@splitpath@a wouldn't have made such an entry in #1. But don't +% mind, let us still test for empty. +\skvrobustdef*\skv@splitpath@b#1#2#3{% + \begingroup + \def\skv@prova##1##2\@nil{% + \edef#2{\skvtrimspace{##1}}% + \edef#3{\skvtrimspace{##2}}% + \ifx#3\@empty + \let#3#2% + \let#2\skvdefaultprefix + \fi + }% + \expandafter\skv@prova#1\@nil + \skvexpanded{\endgroup\skvcmdexit#2\skvcmdexit#3}% +} +% \skv@formatpaths{<paths>}{<macro.to.reveive.formated.paths>} +% +% Format <paths> given in the form +% +% {<pref1>/<fam1>},...,{<pref-n>/<fam-n>} +% +% into +% +% \skv@pathdo{<pref1>}{<fam1>}...\skv@pathdo{<pref-n>}{<fam-n>} +% +\skvrobustdef*\skv@formatpaths#1#2{% + \begingroup + \skvifdefTF#2{}{\def#2{}}% + \edef\skv@prova{#1}% + \skvcsvnormalize[/]\skv@prova + \skvcommaparse*\skv@prova\skv@prova{% + \skv@splitpath@a\skv@prova\skv@tempa\skv@tempb + \edef\skv@prova{{\skv@tempa}{\skv@tempb}}% + \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok{#2}}{}{% + \edef#2{\skvexpandonce#2\noexpand\skv@pathdo\skv@prova}% + }% + }% + \skvaftergroupdef#2\endgroup +} +% \skv@makepaths{<prefix.macro>}{<families.macro>}{<pathlist.macro>} +% {<wrapper.macro>} +% +% Attach prefix to families, to make pathlist. <prefix> must contain +% only one element. +% +\skvrobustdef*\skv@makepaths#1#2#3#4{% + \begingroup + \skv@ifnoprefix#1{\let#1\skvdefaultprefix}{}% + \edef\skv@pref{#1}% + \skvdespacecontent\skv@pref + \skvstripouterbraces{2}\skv@pref + \skv@ifoneprefix\skv@pref{% + \skvifdefTF#3{}{\def#3{}}% + \skvcommaparse*#2\skv@prova{% + \skvstripouterbraces{2}\skv@prova + \edef\skv@prova{{\skv@pref}{\skv@prova}}% + \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok{#3}}{}{% + \edef#3{\skvexpandonce{#3}\noexpand#4\skv@prova}% + }% + }% + }{% + \skv@err{Only one prefix allowed here} + {The service you've called takes only one key prefix}% + }% + \skvaftergroupdef#3\endgroup +} +% \skv@ifoneprefix{<prefix>} +\skvrobustdef*\skv@ifoneprefix#1{% + \skvxifinTF{,}{\skvoxdetok{#1}}{% + \begingroup + \def\skv@prova##1,##2\skv@nil{% + \endgroup + \skvifblankTF{##2}{% + \def#1{##1}% + }{% + \@secondoftwo + }% + }% + \expandafter\skv@prova#1\skv@nil + }{% + \@firstoftwo + }% +} +% \skv@ifnoprefix{<prefix.macro>} +\skvrobustdef*\skv@ifnoprefix#1{% + % Think twice before changing this conditional! + \ifcase0% + \ifx#1\skv@undefined\else\ifx#1\@empty\else 1\fi\fi\relax + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvrobustdef*\skvifmacroTF#1{% + \skvifntypeTF{#1}{% + \begingroup + \edef\skv@tempa##1{\def##1####1\detokenize{macro}:####2\skvrelax}% + \skv@tempa\skv@tempa{% + \expandafter\endgroup\ifx\@nnil##2\@nnil + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + \edef\skv@tempb##1{##1\detokenize{macro}:\skvrelax}% + \skv@tempb{\expandafter\skv@tempa\meaning#1}% + }{% + \@secondoftwo + }% +} +\skvrobustdef*\skvifmacroFT#1{\skvifmacroTF{#1}\@secondoftwo\@firstoftwo} +% 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{<essence>}{<test token>} +\skvrobustdef*\skvensureinteger#1#2{% + \begingroup + \def\skv@prova##1\skv@catch@nil{% + % If ##1 is empty or contains nothing before a \relax, it's fine: + \ifx\relax##1\relax\else + \skv@err{Token '#2' for '#1' isn't an integer}\skv@ehd + \fi + }% + \afterassignment\skv@prova\skv@cnta#2\relax\skv@catch@nil + \endgroup +} +\skvnewdef*\skv@validunit{em,ex,in,pt,pc,cm,mm,dd,cc,nd,nc,bp,sp} +\skvrobustdef*\skv@catchremainder#1\skv@catch@nil{% + \endgroup\def\skv@elt{#1}% +} +\skvrobustdef*\skvifintegerTF#1{% + \begingroup + % Don't try '\numexpr0+#1' here: + \afterassignment\skv@catchremainder + \@tempcnta\numexpr0#1\relax\skv@catch@nil + \skvifxTF\skv@elt\@empty{% + \@firstoftwo + }{% + \skvifxTF\skv@elt\skv@relaxtoks\@firstoftwo\@secondoftwo + }% +} +\skvrobustdef\skvoifinteger{\skvexpbracenext\skvifintegerTF} + +% 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}{<T>}{<F>} : fails +% \skvifdimexpr{1pt-2bp*3+(3cc-2ex)/2}{<T>}{<F>} : true +% \skvifdimexpr{(1pt-2bp+(3cc-2ex)/2)*5}{<T>}{<F>} : true +% \skvifdimexpr{1+(1pt-2bp+(3cc-2ex)/2)*5}{<T>}{<F>} : false +% +\skvrobustdef*\skvifdimexpr#1{% + \begingroup + \@tempcnta\skvz@ + \skv@dimexpr@beg + \skv@dimexpr@bgroup#1(\skv@catch@nil + \skv@dimexpr@end + \skv@dimexpr@end@end +} +\skvrobustdef*\skv@dimexpr@beg{\begingroup\@tempcnta\skvz@} +\skvrobustdef*\skv@dimexpr@end{% + \skv@dimexpr@end@end + \skv@dimexpr@true\skv@dimexpr@false +} +\skvrobustdef*\skv@dimexpr@end@end{% + \skvifnumTF\@tempcnta<\skvz@ + {\endgroup\@secondoftwo}{\endgroup\@firstoftwo}% +} +\skvrobustdef*\skv@dimexpr@true{\advance\@tempcnta\skvz@} +\skvrobustdef*\skv@dimexpr@false{\advance\@tempcnta\m@ne} +\skvrobustdef*\skv@dimexpr@bgroup#1(#2\skv@catch@nil{% + \skv@dimexpr@egroup#1)\skv@catch@nil + \skvifblankTF{#2}{}{% + \skv@dimexpr@beg + \skv@dimexpr@bgroup#2\skv@catch@nil + }% +} +\skvrobustdef*\skv@dimexpr@egroup#1)#2\skv@catch@nil{% + \skv@dimexpr@plus#1+\skv@catch@nil + \skvifblankTF{#2}{}{% + \skv@dimexpr@end + \skv@dimexpr@egroup#2\skv@catch@nil + }% +} +\skvrobustdef*\skv@dimexpr@plus#1+#2\skv@catch@nil{% + \skv@dimexpr@minus#1-\skv@catch@nil + \skvifblankTF{#2}{}{\skv@dimexpr@plus#2\skv@catch@nil}% +} +\skvrobustdef*\skv@dimexpr@minus#1-#2\skv@catch@nil{% + \skvifblankTF{#1}{}{% + \skvifinTF{*}{#1}{% + \skvifinTF{/}{#1}{% + \skv@err{I can't parse this: both star (*) and slash + \MessageBreak (/) are in '#1'}\skv@ehd + }{% + \skvifblankTF{#1}{}{\skv@dimexpr@times#1*\skv@catch@nil}% + }% + }{% + \skvifblankTF{#1}{}{\skv@dimexpr@divide#1/\skv@catch@nil}% + }% + }% + \skvifblankTF{#2}{}{\skv@dimexpr@minus#2\skv@catch@nil}% +} +% Because of, eg, 2pt*3/2, the test for * is more subtle: +\skvrobustdef*\skv@dimexpr@times#1*#2\skv@catch@nil{% + \skvifblankTF{#1}{}{% + \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false + }% + \skvifblankTF{#2}{}{% + \def\skv@prova##1*##2\skv@catch@nil{##1}% + \skvexpbracenext\skv@dimexpr@integer{\skv@prova#2\skv@catch@nil}% + }% +} +\skvrobustdef*\skv@dimexpr@divide#1/#2\skv@catch@nil{% + \skvifblankTF{#1}{}{% + \skvifdimensionable{#1}\skv@dimexpr@true\skv@dimexpr@false + }% + \skvifblankTF{#2}{}{% + \def\skv@prova##1/##2\skv@catch@nil{##1}% + \skvexpbracenext\skv@dimexpr@integer{\skv@prova#2\skv@catch@nil}% + }% +} +\skvrobustdef*\skv@dimexpr@integer#1{% + \skvifblankTF{#1}{}{% + \skvifintegerTF{#1}\skv@dimexpr@true\skv@dimexpr@false + }% +} + +% \skvtrimlastparser{<parser>}<cmd> +\skvrobustdef\skvtrimlastparser#1#2{% + \begingroup + % The following \relax will not be cat-12, so it won't appear in #2: + \def\skv@trim@a{% + \skvxifinTF{\detokenize{#1}\relax}{\skvoxdetok{#2}\relax}{% + \def\skv@trim@b####1#1\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{<csv/kv>}<parser>{<list>}<cmd> +\skvrobustdef*\skvnormalize#1#2#3#4{% + \def\skv@prova##1#1##2##3\skv@nil{% + \ifx\skv@nnil##2% + \skv@err{Invalid list type '#1' for command \string\skvnormalize}\skv@ehd + \fi + \edef#4{\unexpanded{#3}}% + ##2[#2]#4% + }% + \skv@prova csv\skvcsvnormalize kv\skvkvnormalize#1\skv@nnil\skv@nil +} +\skvnewdef*\skv@switch@inkv{01} +% \skvcsvnormalize[<parser>]<listcmd> +\skvrobustdef*\skvcsvnormalize{\skv@testopt\skv@csvnormalize,} +\skvrobustdef*\skv@csvnormalize[#1]#2{% + \begingroup + \skv@setupnormalizer{#1}% + \skvswitchfalse{inkv}% + \edef#2{\expandafter\skv@normalizelist\expandafter{#2}}% + \skvaftergroupdef#2\endgroup +} +% \skvcsvnormalizeset{<cmda>{<token1>,<cmdb2>{<token2>},...} +\skvrobustdef*\skvcsvnormalizeset{\skv@testopt\skv@csvnormalizeset,} +\skvrobustdef*\skv@csvnormalizeset[#1]#2{% + \begingroup + \toks@{}% + \skv@setupnormalizer{#1}% + \skvswitchfalse{inkv}% + \def\do##1##2,{% + \skvifnotnil{##1}{% + \edef##1{\skv@normalizelist{##2}}% + \skvexpanded{\toks@{\the\toks@\skvcmdexit##1}}% + \do + }% + }% + \do#2,\skv@nil,% + \expandafter\endgroup\the\toks@ +} +% \skvkvnormalize{<kvlistcmd>} +\skvrobustdef*\skvkvnormalize#1{% + \begingroup + \skv@setupnormalizer{,}% + \skvswitchtrue{inkv}% + % We put '\skv@kvguard' at the end of #1 to preserve any trailing '='. + % This can happen if a key has an empty value. But this can leave + % a trailing space or <parser> in #1. We remove the trailing space + % with \skvtrimspace. + \edef#1{\expandafter\skv@normalizelist\expandafter{#1\skv@kvguard}}% + \def\skv@kvnorm##1\skv@kvguard##2\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[<parser>]<listcmd> +\skvrobustdef*\skvmacnormalize{\skv@testopt\skv@macnormalize,} +\skvrobustdef*\skv@macnormalize[#1]#2{% + \begingroup + \skvsetupmacroparsernormalizer{#1}% + \edef#2{\expandafter\skvmacroparsernormalize\expandafter{#2}}% + \skvaftergroupdef#2\endgroup +} +% This is for normalizing lists like {x\\ y\\\\ z}, where \\ is +% a control sequence. Such parsers may be commands but we don't +% expect them to be active characters. +% +% Example: +% +% \skvsetupmacroparsernormalizer{\\} +% \edef\x{\skvmacroparsernormalize{x\\ y\\\\ z}} +% +\skvrobustdef*\skvsetupmacroparsernormalizer#1{% + \long\def\skvmacroparsernormalize##1{% + \unexpanded\expandafter{\romannumeral-`\q + \skv@mac@spaceparser##1 #1\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{<cmd list>}{<cmd>} +\skvrobustdef*\skvletmanytocmd#1#2{% + \skvcommaparse{#1}\skv@prova{% + \expandafter\let\skv@prova= #2% + }% +} +% \skvnewletmanytocmd{<cmd list>}{<cmd>} +\skvrobustdef*\skvnewletmanytocmd#1#2{% + \skvcommaparse{#1}\skv@prova{% + \expandafter\skv@newcastcommand\skv@prova=#2=\cast@nil + }% +} +% \skvcastcommands{<cmd list>} +% +% <cmd list> -> {<cmd-a1>=<cmd-a2>,...,<cmd-b1>=<cmd-b2>} +% +% In <cmd list>, cast left-hand command to the right-hand command. +% Raise error if left-hand command already exists and isn't equal to +% the right-hand command. +% +% Example: +% +% \skvcastcommands{\ifxx=\ifx,\fii=\fi} +% +\skvrobustdef*\skvcastcommands#1{% + \def\skv@provb##1=##2=##3\cast@nil{\let##1= ##2}% + \skvkvparse{#1}\skv@prova{% + \expandafter\skv@provb\skv@prova==\cast@nil + }% +} +% \skvnewcastcommands{<cmd list>} +% +% <cmd list> -> {<cmd-a1>=<cmd-a2>,...,<cmd-b1>=<cmd-b2>} +% +\skvrobustdef*\skvnewcastcommands#1{% + \skvkvparse{#1}\skv@prova{% + \expandafter\skv@newcastcommand\skv@prova==\cast@nil + }% +} +\skvrobustdef*\skv@newcastcommand#1=#2=#3\cast@nil{% + \skvifdefTF{#1}{% + \ifx#1#2\else + \skv@err{Command \noexpand#1 already exists: + \MessageBreak it can't be cast to \string#2}\skv@ehd + \fi + }{% + \let#1= #2% + }% +} +% \skvpushfunctions<base-fn>{<fn-list>}<counter-number> +% +% 1. <counter-number> is a number, not a counter. +% 2. See file skeyval-for.tex for \skvsavestate and \skvrestorestate. +% +\skvrobustdef*\skvpushfunctions#1#2#3{% + \skvgadvanceno#3\@ne + \begingroup + \def\skv@prova##1{\skvremovescape{##1}@skv@\romannumeral#3}% + \def\do##1{\let\noexpand##1\skvnoexpandcs{\skv@prova{##1}}}% + \skvifcsdefTF{\skvremovescape{#1}@skv@stack}{}{% + \skvcsdef{\skvremovescape{#1}@skv@stack}{}% + }% + \skvcsxdef{\skvremovescape{#1}@skv@stack}{% + #2{\skvexpandcsonce{\skvremovescape{#1}@skv@stack}}% + }% + \def\do##1{\let\skvnoexpandcs{\skv@prova{##1}}\noexpand##1}% + \skvexpanded{\endgroup#2}% +} +% \skvpopfunctions<base-fn><counter-number> +\skvrobustdef*\skvpopfunctions#1#2{% + \begingroup + \edef\skv@elt{\skvnoexpandcs{\skvremovescape{#1}@skv@stack}}% + \expandafter\skvifdefTF\skv@elt{% + \expandafter\ifx\skv@elt\@empty + \skv@err{Stack of '\noexpand#1' is empty}\skv@ehd + \fi + }{% + \skv@err{Stack of '\noexpand#1' is undefined}\skv@ehd + }% + \edef\skv@prova{\skvexpandtwice\skv@elt}% + \edef\skv@provb##1##{\endgroup##1\gdef\skvexpandonce\skv@elt}% + \expandafter\skv@provb\skv@prova + \skvgadvanceno#2\m@ne +} + +\chardef\skv@stackdepthlimit=6\relax +% #1=stack (a macro), #2=list, #3=max depth +\skvrobustdef*\skvbuildmacrostack#1#2#3{% + \begingroup + \escapechar92\relax + \ifdefined#1% + \skv@err{Stack \noexpand#1 already exists}\skv@ehd + \else + \def#1{}% + \fi + \@tempcnta\skvz@ + \def\do##1{% + \noexpand\skv@elt\noexpand##1% + \skvnoexpandcs{\skvremovescape{##1}@skv@\romannumeral\@tempcnta}% + }% + \def\skv@loop{% + \advance\@tempcnta\@ne + % Don't use \ifnum here, because #2 may contain booleans. + \skvifnumTF\@tempcnta<\numexpr#3+1\relax{% + \edef#1{\skvexpandonce#1.\romannumeral\@tempcnta.{#2}}% + \skv@loop + }{}% + }% + \skv@loop + \skvaftergroupdef#1\endgroup +} +\skvrobustdef*\skvpushstate#1#2{\skv@pushorpopstate{#1}{#2}{+}} +\skvrobustdef*\skvpopstate#1#2{\skv@pushorpopstate{#1}{#2}{-}} +% #1: stack (a macro) as built by \skvbuildmacrostack +% #2: depth counter (macro-defined) +% #3: stack direction (+/-) +\skvrobustdef*\skv@pushorpopstate#1#2#3{% + \if+#3\relax + \skvgadvanceno#2\@ne + \def\skv@elt##1##2{\let##2=##1}% + \else + \ifnum#2<\@ne + \skv@err{Can't pop stack: depth currently less than 1}\skv@ehd + \else + \def\skv@elt##1##2{\let##1=##2}% + \fi + \fi + \ifnum#2>\skv@stackdepthlimit + \skv@err{Stack depth limit '\number\skv@stackdepthlimit' exceeded}\skv@ehd + \fi + \def\skv@prova##1{% + \def\skv@prova####1.##1.####2####3\skv@nil{####2}% + \expandafter\skv@prova#1\skv@nil + }% + \expandafter\skv@prova\expandafter{\romannumeral#2}% + \let\skv@elt\relax + \if-#3\skvgadvanceno#2\m@ne\fi +} + +\skvrobustdef*\skvnewbools{\skv@testopt\skv@newbools{}} +\skvrobustdef*\skv@newbools[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdefTF{if#1##1}{% + \skv@err{Boolean '\skvnoexpandcs{if#1##1}' already exists}\skv@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \csname newif\expandafter\endcsname\csname if#1##1\endcsname}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} + +% Some booleans: +% +% inset = in \skvsetkeys +% viarootofsetkeys = a key is being set via the root of the command +% \skvsetkeys. This is needed because sometimes we +% bypass the root of \skvsetkeys, eg, by calling +% \skv@setkeys@a. +% inclass = in class file. +% kf = key has been found while in \skvsetkeys. +% intry = in 'try set keys'. +% insa = in 'search also set keys'. +% success = key has been found while in 'try set keys'. +% inopt = in process/execute options. +% inpox = in process options. +% inprepo = presetting or post-setting keys. +% inoptionsec = in skeyval package's options processing section. +% Some checks are done in that section. +% +\skvnewbools[skv@]{% + inclass,inset,viarootofsetkeys,processvalueaslist,st,cl,pl,kf,inpox,inopt,% + inprepo,tracingkeys,tempsw,swa,verbose,intry,insa,success,inoptionsec,% + inreservedhandler,ignorepointers,addslots@appending% +} + +\skvnewbools{indirectkeys} +\skvnewbools[skv]{indef,novalue,valuebraced} +\skvnewbools[dirkeys@]{saveunknownkeys} +% \skvnewcounts[<optional prefix>]{<name list>} +\skvrobustdef*\skvnewcounts{\skv@testopt\skv@newcounts{}} +\skvrobustdef*\skv@newcounts[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdefTF{#1##1}{% + \skv@err{Counter '\skvnoexpandcs{#1##1}' already exists}\skv@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \csname newcount\expandafter\endcsname\csname#1##1\endcsname}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} +\skvnewcounts[skv@]{tempcnt,cnta} +% \skvnewnumbers[<optional prefix>]{<name list>} +\skvrobustdef*\skvnewnumbers{\skv@testopt\skv@newnumbers{}} +\skvrobustdef*\skv@newnumbers[#1]#2{% + \begingroup + \toks@{}% + \def\skv@prova{#2}% + \skvcsvnormalize\skv@prova + \def\do##1,{% + \ifx\do##1\else + \skvifcsdefTF{#1##1}{% + \skv@err{Number '\skvnoexpandcs{#1##1}' already exists}\skv@ehd + }{% + \toks@\expandafter{\the\expandafter\toks@ + \expandafter\def\csname#1##1\endcsname{0}}% + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@prova,\do,% + \expandafter\endgroup\the\toks@ +} +\skvnewlet\skvnewnumber\skvnewnumbers +\skvnewnumbers[skv@]{keydepth,setkeysdepth,preposetdepth,makekeysdepth, + usekeysdepth,trysetkeysdepth,trysetkeysidepth,gendepth} +\skvnewnumbers{dirkeys@depth} +\skvnewlet\skv@novalue\relax +\skvnewdef*\skv@novaluetoks{\skv@novalue} + +% A fast key parsing scheme originally designed for \newforeach loop. +% +% \skvquickkeys.define{<family>}[<hp>]{<list>} +% \skvquickkeys.set{<family>}[<na>]{<kvlist>} +% +% 1. Quickly define and set command/store keys. +% 2. Pointers aren't used here, to speed up parsing. Use other key +% machineries instead. +% 3. Package and class options can't be passed via quick keys. +% 4. When defining keys, the syntax of <list> is: +% +% {<keya-1,...,keya-n>}/{<default>}/{<callback>}/{<choice list>}/{<arg>} +% ;...; +% {<keyb-1,...,keyb-n>}/{<default>}/{<callback>}/{<choice list>}/{<arg>} +% +% Here, keys key1 and key2 will have the same default value, callback, +% choice list and argument pattern. +% +% 5. The special keys '.exec' and '.exec code' will simply execute the +% code <default>, without defining anything. +% 6. Use the token '.na' to indicate a missing item that isn't blank or empty. +% 7. The parser for defining the keys can be specified via \setquickkeysparser. +% The default parser is semicolon (;). +% +% Example: +% \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{<kvlist.cmd>}{<return.cmd>} +\skvrobustdef*\skv@qkeysgetkeynames#1#2{% + \begingroup + \let#2\@empty + \skvkvparse*#1\skv@prova{% + \def\do##1=##2=##3\skv@nil{% + \edef#2{\skvaddlist,#2##1}% + }% + \expandafter\do\skv@prova==\skv@nil + }% + \skvaftergroupdef#2\endgroup +} +\skvrobustdef*\skvsetquickkeysparser{\def\skv@qkeysparser} +\skvsetquickkeysparser{;} +\skvrobustdef*\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{<family>}{<kvlist>} +% +\skvrobustdef*\skv@presetquickkeys#1#2{% + \edef\skv@prova{\unexpanded{#2}}% + \skvkvnormalize\skv@prova + \skvcslet{\skvtrimspace{#1}/qkeys.preset}\skv@prova +} + +\skvnewnumbers[skv@]{setqkeysdepth} + +% \skvquickkeys.set{<family>}[<na>]{<kvlist>} + +\skvrobustdef*\skv@setquickkeys#1{\skv@testopt{\skv@setquickkeys@a{#1}}{}} +\skvrobustdef*\skv@setquickkeys@a#1[#2]#3{% + \skvsavestate\skv@setquickkeys{% + \do\skv@qkeysfam\do\skv@qkeysna\do\skv@qkeyskvlist + \do\skv@qkeyscurrnames + }\skv@setqkeysdepth + \edef\skv@qkeysna{\unexpanded{#2}}% + \skvcsvnormalize\skv@qkeysna + \edef\skv@qkeyskvlist{\unexpanded{#3}}% + \skvkvnormalize\skv@qkeyskvlist + \edef\skv@qkeysfam{\skvtrimspace{#1}}% + \def\skv@setqkeys@a##1##2{% + \skvifcsdefTF{\skv@qkeysfam/##1/qkeys.cbk}{}{% + \skv@err{Key '\skv@qkeysfam/##1' + \MessageBreak is not defined}\skv@ehd + }% + \skvifstrcmpTF{##2}\skv@novalue{% + \skvifcsdefTF{\skv@qkeysfam/##1/qkeys.def}{% + \skvcsletcs{\skv@qkeysfam/##1/qkeys.val}{\skv@qkeysfam/##1/qkeys.def}% + }{% + \skv@err{No user value and no default + \MessageBreak for key '\skv@qkeysfam/##1'}\skv@ehd + }% + }{% + \skvcsedef{\skv@qkeysfam/##1/qkeys.val}{\unexpanded{##2}}% + }% + \skvletcs\skv@tempval{\skv@qkeysfam/##1/qkeys.val}% + \skvletcs\reserved@a{\skv@qkeysfam/##1/qkeys.chc}% + \skvifdefTF\reserved@a{% + \skvxifinTF{,\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{<fam>}[<na>]{<kvlist>} +\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<comparator>{<teststr>} +% {<cases>} +% \elsedo +% {<no match>} +% \endif +% +% 1. \skvifcase is expandable but, to simplify the code, \elsedo is mandatory. +% See \ltsifcases of ltxtools package; it is expandable and the 'else part' +% can be omitted in it. +% 2. \skvifcasse and \skvifcases aren't expandable, but \elsedo isn't +% mandatory. +% +% Example: +% +% \skvifcase\skvifstrcmpTF{c} +% {a}{do a} +% {b}{do b} +% {c}{do c} +% \elsedo +% no match% +% \endif +% +% \skvifcase is expandable. +% +\skvnewdef\skvifcase#1#2#3\elsedo#4\endif{% + % {#2}{#4} represent default action: + \skv@ifcase{#1}{#2}#3{#2}{#4}\skv@casestop +} +\skvnewdef\skv@ifcase#1#2#3{% + #1{#2}{#3}{% + \skv@ifcase@domatch + }{% + \skv@ifcase@a{#1}{#2}% + }% +} +\skvnewdef\skv@ifcase@a#1#2#3{\skv@ifcase{#1}{#2}} +\skvnewdef\skv@ifcase@domatch#1#2\skv@casestop{\skvtrimspace{#1}} + +% \skvifcasse{<teststr>} +% {<cases>} +% \elsedo +% {<no match>} +% \endif +% +% 1. \elsedo can be omitted by the user, ie, it is optional. See also +% \skvifcases. +% 2. Because of the need to remove blank spaces before 'case', +% \skvifcasse isn't expandable. +% +% Example: +% +% \skvifcasse{x} +% case{a}{\def\x{do a}} +% case{b}{\def\x{do b}} +% case{c}{\def\x{do c}} +% \elsedo +% \def\x{no match} +% \endif +% +\skvrobustdef*\skvifcasse#1#2\endif{% + \skvxifinTF{\detokenize{\elsedo}}{\detokenize{#2}}{% + \begingroup + \def\skv@prova##1\elsedo##2\skv@ifcasse@nil{% + \endgroup + \skv@ifcasse@a{#1}##1case{#1}{##2}\skv@casse@stop + }% + \skv@prova#2\skv@ifcasse@nil + }{% + \skv@ifcasse@a{#1}#2case{#1}{}\skv@casse@stop + }% +} +\skvrobustdef*\skv@ifcasse@a#1{% + \@ifnextchar\relax{\skv@ifcasse@b#1}{\skv@ifcasse@b#1}% +} +\skvrobustdef*\skv@ifcasse@b#1case#2{% + \skvxifstrcmpTF{#1}{#2}{% + \skv@ifcasse@domatch + }{% + \skv@ifcasse@c{#1}% + }% +} +\skvrobustdef*\skv@ifcasse@c#1#2{\skv@ifcasse@a{#1}} +\skvrobustdef*\skv@ifcasse@domatch#1#2\skv@casse@stop{\skvtrimspace{#1}} + +% \skvifcases<teststr>\then +% <cases>{<acts>} +% <default>{<act>} +% \fi +% +% 1. <default>{<act>} can be omitted by the user, ie, it is optional. +% 2. \skvifcases isn't expandable. +% +% Example: +% \skvifcases x\then +% case{a}{\def\x{do a}} +% case{b}{\def\x{do b}} +% case{c}{\def\x{do c}} +% default{\def\x{no match}} +% \fi +% +\skvrobustdef*\skvifcases#1\then#2\fi{% + % Any space trailing #2 will be absorbed by ##3 of the following + % \skv@prova. + \begingroup + \def\skv@prova##1default##2##3\skv@ifcases@nil{% + \endgroup + \skvifstrcmpTF{##2}{\@nil}{% + \skv@ifcases@a{#1}##1case{#1}{}\skv@cases@stop + }{% + \skv@ifcases@a{#1}##1case{#1}{##2}\skv@cases@stop + }% + }% + \skv@prova#2default{\@nil}\skv@ifcases@nil +} +\skvrobustdef*\skv@ifcases@a#1{% + \@ifnextchar\relax{\skv@ifcases@b#1}{\skv@ifcases@b#1}% +} +\skvrobustdef*\skv@ifcases@b#1case#2{% + \skvxifstrcmpTF{#1}{#2}{% + \skv@ifcases@domatch + }{% + \skv@ifcases@c{#1}% + }% +} +\skvrobustdef*\skv@ifcases@c#1#2{\skv@ifcases@a{#1}} +\skvrobustdef*\skv@ifcases@domatch#1#2\skv@cases@stop{#1} + +% Evaluating a series of boolean expressions. +% +% Example: +% +% \skvifexprTF{% +% not ( expr { \skvifdefTF\xa } and expr { \skvifemptyTF\xa } ) +% or ( expr { \skvifboolTF{@tempswa} } or expr { \skvifxTF\xa\xb } ) +% }{% +% \def\x{T} +% }{% +% \def\x{F} +% } +\skvnewnumbers[skv@]{exprcnt} +\skvrobustdef*\skvifexprTF#1{% + \begingroup + \def\skv@expr@neg{01}% + \skvsetno\skv@exprcnt\skvz@ + \skv@expr@beg + \skv@expr@bgroup#1(\skv@expr@nil + \skv@expr@end + \skv@expr@end@end +} +\skvrobustdef*\skv@expr@beg{% + \begingroup + \def\skv@expr@neg{01}% + \skv@exprcnt\skvz@ +} +\skvrobustdef*\skv@expr@end{% + \skv@expr@end@end\skv@expr@true\skv@expr@false +} +\skvrobustdef*\skv@expr@end@end{% + \expandafter\endgroup\csname @\ifnum\skv@exprcnt<\skvz@ + second\else first\fi oftwo\endcsname +} +\skvrobustdef*\skv@expr@true{% + \skvadvanceno\skv@exprcnt{\if\skv@expr@neg\m@ne\else\skvz@\fi}% + \def\skv@expr@neg{01}% +} +\skvrobustdef*\skv@expr@false{% + \skvadvanceno\skv@exprcnt{\if\skv@expr@neg\skvz@\else\m@ne\fi}% + \def\skv@expr@neg{01}% +} +\skvrobustdef\skv@expr@bgroup#1(#2\skv@expr@nil{% + \skv@expr@egroup#1)\skv@expr@nil + \skvifblankTF{#2}{}{% + \skv@expr@beg + \skv@expr@bgroup#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@egroup#1)#2\skv@expr@nil{% + \skv@expr@and#1and\skv@expr@nil + \skvifblankTF{#2}{}{% + \skv@expr@end + \skv@expr@egroup#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@and#1and#2\skv@expr@nil{% + \skv@expr@or#1or\skv@expr@nil + \skvifblankTF{#2}{}{% + \skv@exprcnt\ifnum\skv@exprcnt<\skvz@\m@ne\else\skvz@\fi + \skv@expr@and#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@or#1or#2\skv@expr@nil{% + \skv@expr@not#1not\skv@expr@nil + \skvifblankTF{#2}{}{% + \skv@exprcnt\ifnum\skv@exprcnt<\skvz@\skvz@\else\@ne\fi + \skv@expr@or#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@not#1not#2\skv@expr@nil{% + \skv@expr@do#1expr\skv@expr@nil + \skvifblankTF{#2}{}{% + \def\skv@expr@neg{00}% + \skv@expr@not#2\skv@expr@nil + }% +} +\skvrobustdef\skv@expr@do#1expr#2\skv@expr@nil{% + \skvifblankTF{#1}{}{% + \skv@err{Invalid \noexpand\skvifexprTF test expression} + {The handicapped test part is: '\detokenize{#1}'}% + }% + \skvifblankTF{#2}{}{\skv@expr@do@a#2\skv@expr@nil}% +} +\skvrobustdef\skv@expr@do@a#1#2\skv@expr@nil{% + \ignorespaces#1\skv@expr@true\skv@expr@false + \skv@expr@do#2\skv@expr@nil +} + +\skvnewdef\skvwhileexpr#1\do#2{% + \skvifcondTF{#1}\fi{#2\skvwhileexpr{#1}\do{#2}}{}% +} + +\skvbuildmacrostack\skv@keystate{% + \do\skvcurrentprefix\do\skv@fams\do\skvcurrentfamily + \do\skvcurrentkey\do\skv@header\do\skvcurrentpath\do\skv@na + \do\skv@currentnames\do\ifskv@st\do\ifskv@pl\do\ifskv@kf\do\ifskvnovalue + \do\skvcurrentvalue\do\CurrentOption\do\ifskv@intry\do\ifskv@success + \do\ifskv@inpox\do\ifskv@inprepo\do\skv@pathdo\do\skv@rmkeys + \do\ifskv@viarootofsetkeys\do\skv@currpref\do\skv@currfam +}\skv@stackdepthlimit + +\skvbuildmacrostack\skv@dirkeys@state{% + \do\dirkeys@pathlist\do\dirkeys@holderprefixtoks\do\dirkeys@parser + \do\dirkeys@unknownkeysmacro\do\ifdirkeys@saveunknownkeys + \do\ifindirectkeys\do\ifskvdfk@initialize\do\ifskvdfk@saveinitialvalues +}\skv@stackdepthlimit + +\skvrobustdef\skvappto#1#2{\edef#1{\skvexpandonce{#1}\unexpanded{#2}}} +\skvrobustdef\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{<parser>}{<listcmd>} +% This is usually called under \edef. <listcmd> must have been +% initialized. +\skvnewdef*\skvaddlist#1#2{% + \skvifemptyTF#2{}{\skvexpandonce#2\unexpanded{#1}}% +} +% \skvxaddtolist{<parser>}{<listcmd>}{<newitem>} +\skvrobustdef\skvxaddtolist#1#2#3{\edef#2{\skvaddlist#1#2#3}} +% \skvaddtolist[<parser>]{<listcmd>}{<newitem>} +\skvrobustdef*\skvaddtolist{\skv@teststopt{\skv@addtolist{}},} +\skvrobustdef*\skvgaddtolist{\skv@teststopt{\skv@addtolist{\global}},} +\skvrobustdef\skv@addtolist#1[#2]#3#4{% + #1\edef#3{% + \skvifdefTF#3{% + \skvifemptyTF#3{}{\skvexpandonce#3#2}% + }{}% + \skvifdefboolTF{skv@tempst}\skvexpandonce\unexpanded{#4}% + }% +} +% \skv@declarefilter{<filter.type>} +\skvrobustdef*\skv@declarefilter#1{% + \edef\skv@filter##1##2{% + \skvifblankTF{#1}{\let##2=##1}{% + \skvifxTF#1\relax{\let##2=##1}{% + \skvifstrcmpTF{#1}\nofilter{\let##2=##1}{% + \unexpanded{#1}{##1}{##2}% + }% + }% + }% + }% +} + +% \skvfiltermergelist![<parser>](<wrapper>)<cmd>{<sublist>}<filter> +% +% 1. The exclamation mark (!) implies that the outcome is globalized. +% 2. When there is no filter to be applied, \skvmergelist is faster +% than \skvfiltermergelist. +% 3. <wrapper> will be applied to all the elements (new and old). +% The default value of <wrapper> is \skvexpandonce. +% 4. Example: +% \def\alist{a;b;c} +% \def\wrap#1{{#1}} +% \skvfiltermergelist[;](\wrap)\alist{a;d;e}\nofilter +% \show\alist +% +\skvrobustdef*\skvfiltermergelist{% + \skv@testcl{\skv@testopt\skv@filtermergelist,}% +} +\skvrobustdef*\skv@filtermergelist[#1]{% + \skv@testpnopt{\skv@f@ltermergelist{#1}}\skvexpandonce +} +\skvrobustdef*\skv@f@ltermergelist#1(#2)#3#4#5{% + \begingroup + \skv@declarefilter{#5}% + \skvifdefTF#3{\skvcsvnormalize[#1]#3}{\def#3{}}% + \edef\skv@merga{\unexpanded{#4}}% + \skvcsvnormalize[#1]\skv@merga + \skvdolist*{#1}\skv@merga\skv@merga{% + \@tempswatrue + \skv@filter\skv@merga\skv@mergb + \let\skv@mergc#3% + \let#3\@empty + \skvdolist*{#1}\skv@mergc\skv@mergc{% + \skv@filter\skv@mergc\skv@mergd + \ifx\skv@mergb\skv@mergd + \@tempswafalse + \edef#3{\skvaddlist#1#3#2{\skv@merga}}% + \else + \edef#3{\skvaddlist#1#3#2{\skv@mergc}}% + \fi + }% + \if@tempswa + \edef#3{\skvaddlist#1#3#2{\skv@merga}}% + \fi + }% + \skvaftergroupdef#3\endgroup + \ifskv@cl\global\let#3#3\fi +} +% \skvmergelist![<parser>](<wrapper>)<cmd>{<sublist>} +% +% 1. The exclamation mark (!) implies that the outcome is globalized. +% 2. <wrapper> will be applied to only the new elements. The default +% value of <wrapper> is \skvexpandonce. +\skvrobustdef*\skvmergelist{\skv@testcl{\skv@testopt\skv@mergelist,}} +\skvrobustdef*\skv@mergelist[#1]{% + \skv@testpnopt{\skv@m@rgelist{#1}}\skvexpandonce +} +\skvrobustdef*\skv@m@rgelist#1(#2)#3#4{% + \begingroup + \skvifdefTF#3{\skvcsvnormalize[#1]#3}{\def#3{}}% + \edef\skv@tempa{\unexpanded{#4}}% + \skvcsvnormalize[#1]\skv@tempa + \skvdolist*{#1}\skv@tempa\skv@tempa{% + \skvxifinTF{#1\skvoxdetok\skv@tempa#1}{#1\skvoxdetok#3#1}{}{% + \edef#3{\skvaddlist#1#3#2{\skv@tempa}}% + }% + }% + \skvaftergroupdef#3\endgroup + \ifskv@cl\global\let#3#3\fi +} + +% \skvfilterremoveelements!<cmd>{<sublist>}<filter> +% The exclamation mark (!) implies that the outcome is globalized. +% When there is no filter to be applied, \skvremoveelements is faster +% than \skvfilterremoveelements. +% \if@tempswa can be used to test if, at the end, anything was +% removed from #1. +\skvrobustdef*\skvfilterremoveelements{% + \skv@testcl\skv@filterremoveelements +} +\skvrobustdef*\skv@filterremoveelements#1#2#3{% + \begingroup + \let\skv@origlist#1% + \skv@declarefilter{#3}% + \skvifdefTF#1{\skvkvnormalize#1}{\def#1{}}% + \skvcommaparse{#2}\skv@rema\skv@rema{% + \skv@filter\skv@rema\skv@remb + \let\skv@remc#1\let#1\@empty + \skvcommaloop*\skv@remc\skv@remc{% + \skv@filter\skv@remc\skv@remd + \ifx\skv@remb\skv@remd\else + \edef#1{\skvaddlist,#1\skvexpandonce\skv@remc}% + \fi + }% + }% + \skvexpanded{\endgroup + \skvcmdexit#1% + \ifx#1\@empty + \noexpand\@tempswatrue + \else + \ifx#1\skv@origlist\noexpand\@tempswafalse\else + \noexpand\@tempswatrue\fi + \fi + }% + \ifskv@cl\global\let#1#1\fi +} +% \skvremoveelements!<cmd>{<sublist>} +% The exclamation mark (!) implies that the outcome is globalized. +% \if@tempswa can be used to test if, at the end, anything was +% removed from #1. +\skvrobustdef*\skvremoveelements{\skv@testcl\skv@removeelements} +\skvrobustdef\skv@removeelements#1#2{% + \begingroup + \let\skv@origlist#1% + \skvifdefTF#1{}{\def#1{}}% + \edef\skv@rema{\unexpanded{#2}}% + \skvcsvnormalize\skv@rema + \let\skv@remb#1\let#1\@empty + \skvcommaparse*\skv@remb\skv@tempa{% + \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@rema,}{}{% + \edef#1{\skvaddlist,#1\skvexpandonce\skv@tempa}% + }% + }% + \skvexpanded{\endgroup + \skvcmdexit#1% + \ifx#1\@empty + \noexpand\@tempswatrue + \else + \ifx#1\skv@origlist\noexpand\@tempswafalse\else + \noexpand\@tempswatrue\fi + \fi + }% + \ifskv@cl\global\let#1#1\fi +} + +% \skvfilterreplaceelements!<listcmd>{<replacements>}<filter> +% +% <replacements> => {<old-1>}{<new-1>},...,{<old-n>}{<new-n>} +% +% An exclamation mark suffix (!) implies that the outcome is globalized. +% When there is no filter to be applied, \skvreplaceelements is only +% negligibly faster than \skvfilterreplaceelements. Hence \skvreplaceelements +% isn't defined in this package. When there is no <filter>, use \nofilter +% as the filter. +% +% Example: +% +% \def\cmd{a = 1 , b = 2 , c = 3} +% \skvfilterreplaceelements\cmd{{a = 1}{x = 1},{c = 3}{z=3}}\skv@getkeyname +% +\skvrobustdef*\skvfilterreplaceelements{% + \skv@testcl\skv@filterreplaceelements +} +\skvrobustdef*\skv@filterreplaceelements#1#2#3{% + \begingroup + \skv@declarefilter{#3}% + \skvifdefTF#1{\skvkvnormalize#1}{\def#1{}}% + \def\do##1##2\skv@nil{% + \ifx#3\skv@getkeyname + \def\@do####1=####2=####3\skv@nil####4{% + \edef####4{\skvtrimspace{####1}=\skvtrimspace{####2}}% + }% + \@do##1==\skv@nil\skv@old + \@do##2==\skv@nil\skv@new + \else + \edef\skv@old{\unexpanded{##1}}% + \edef\skv@new{\unexpanded{##2}}% + \fi + }% + \skvcommaparse{#2}\skv@tempa{% + \expandafter\do\skv@tempa\skv@nil + \skv@filter\skv@old\skv@oldb + \let\skv@tempb#1\let#1\@empty + \skvcommaloop*\skv@tempb\skv@tempb{% + \skv@filter\skv@tempb\skv@tempc + \ifx\skv@tempc\skv@oldb + \edef#1{\skvaddlist,#1\skvexpandonce\skv@new}% + \else + \edef#1{\skvaddlist,#1\skvexpandonce\skv@tempb}% + \fi + }% + }% + \skvaftergroupdef#1\endgroup + \ifskv@cl\global\let#1#1\fi +} +\skvnewdef*\skvcurrentfullkey{\skv@header\skvcurrentkey} +\skvnewlet\skvcurrenttriple\skvcurrentfullkey +\skvrobustdef*\skvsetdefaultprefix#1{% + \edef\skvdefaultprefix{\skvtrimspace{#1}}% + \let\skv@defapf\skvdefaultprefix +} +\skvsetdefaultprefix{KV} +\skvrobustdef*\skv@makeprefix#1{% + % It is required to first fully expand the prefix: + \edef\skvcurrentprefix{#1}% + \skvdespacecontent\skvcurrentprefix +} +% \skv@makeheader{<family>} +\skvrobustdef*\skv@makeheader#1{% + % Fully expand the family, in case it is given as \skvcurrentfamily or + % a local/temporary macro: + \edef\skvcurrentfamily{#1}% + \skvdespacecontent\skvcurrentfamily + \ifx\skvcurrentprefix\@undefined + \let\skvcurrentprefix\skvdefaultprefix + \fi + \edef\skv@header{% + \ifx\skvcurrentprefix\@empty\else\skvcurrentprefix/\fi + \ifx\skvcurrentfamily\@empty\else\skvcurrentfamily/\fi + }% + \ifx\skv@header\@empty + \def\skv@header{/}% + \let\skvcurrentpath\skv@header + \else + \def\reserved@a##1/\@nil{##1}% + \edef\skvcurrentpath{\expandafter\reserved@a\skv@header\@nil}% + \ifx\skvcurrentpath\@empty + \def\skvcurrentpath{/}% + \fi + \fi +} +\skvrobustdef*\skv@testopta#1{% + \skvifstar + {\skv@sttrue\skv@t@stopta{#1}} + {\skv@stfalse\skv@t@stopta{#1}}% +} +\skvrobustdef*\skv@t@stopta#1{\skvifplus{\skv@pltrue#1}{\skv@plfalse#1}} +\skvrobustdef*\skv@testoptb#1{% + \skv@testopt{\skv@t@stoptb{#1}}\skvdefaultprefix +} +\skvrobustdef*\skv@t@stoptb#1[#2]#3{% + \skv@makeprefix{#2}\skv@makeheader{#3}#1% +} +\skvrobustdef*\skv@testoptc#1{% + \skv@testopt{\skv@t@stoptc{#1}}\skvdefaultprefix +} +\skvrobustdef*\skv@t@stoptc#1[#2]#3{% + \skvxifinTF{,}{\detokenize{#2}}{% + \skv@err{Only one prefix is allowed here, + \MessageBreak but you gave '#2'}\skv@ehd + }{% + \skv@makeprefix{#2}% + \edef\skv@fams{#3}% + \skvxifinTF{,}{\detokenize{#3}}{% + \skvcsvnormalize\skv@fams + }{% + \skvdespacecontent\skv@fams + }% + \skv@testopt#1{}% + }% +} +% \skv@testoptd{<cmd>}{<pre-mp>} +\skvrobustdef*\skv@testoptd#1#2{% + \skv@testoptb{% + \edef\skv@provb{#2\skv@header}% + \def\skv@prova{\skv@testopt{\skv@t@stoptd{#1}}}% + \expandafter\skv@prova\expandafter{\skv@provb}% + }% +} +% \skv@t@stoptd{<cmd>}[<mp>]{<key>} +\skvrobustdef*\skv@t@stoptd#1[#2]#3{% + \skvifnextchar[{\skv@sttrue#1{#2}{#3}}{\skv@stfalse#1{#2}{#3}[]}% +} +\skvrobustdef*\skv@getkeyname#1#2{% + \expandafter\skv@g@tkeyname#1=\skv@getname@nil#2% +} +\skvrobustdef*\skv@g@tkeyname#1=#2\skv@getname@nil#3{% + \skv@strippointersfromkey{#1}% + \let#3=\skvcurrentkey +} +\skvrobustdef*\skv@getkeyvalue#1#2{% + \expandafter\skv@g@tkeyvalue#1==\skv@getvalue@nil#2% +} +\skvrobustdef*\skv@g@tkeyvalue#1={\skv@g@tk@yvalue#1=.} +\skvrobustdef*\skv@g@tk@yvalue#1=#2=#3\skv@getvalue@nil#4{% + \edef#4{\unexpanded\expandafter{\@gobble#2}}% + \skvexpbracenext\skvifbracedTF#4{% + \skvvaluebracedtrue + }{% + \skvvaluebracedfalse + \skvexpbracenext\skv@strippointersfromvalue#4% + }% + \let#4=\skvcurrentvalue +} +% \skv@getnamesofkeys{<kv list>}{<result macro>} +\skvrobustdef*\skv@getnamesofkeys#1#2{% + \begingroup + \edef\skv@prova{\unexpanded{#1}}% + \skvkvnormalize\skv@prova + \let#2\@empty + \skvcommaloop*\skv@prova\skv@prova{% + \expandafter\skv@g@tkeyname\skv@prova=\skv@getname@nil\skv@prova + \edef#2{\skvaddlist,#2\skvexpandonce\skv@prova}% + }% + \skvaftergroupdef#2\endgroup +} +\skvnewdef*\skv@badkeynames{} +\skvnewdef*\skvaddbadkeynames{\skvappendtomacro\skv@badkeynames} +\skvrobustdef*\skv@definedefault#1#2{% + \ifskv@inoptionsec\else + \skvxifinTF{,\skvcurrentprefix,}{,skv,SKV,}{% + \skvxifinTF{,\skvcurrentfamily,}{,skeyval,}{% + \skv@err{Prefix/family '\skvcurrentpath' are reserved}\skv@ehd + }{}% + }{}% + \fi + \ifskv@tracingkeys + \skvxifinTF{,#1,}{,\skv@badkeynames,}{% + \skv@err{Key name '#1' not allowed}\skv@ehd + }{}% + \fi + \skvcsedef{\skv@header#1.@defa}{% + \skvnoexpandcs{\skv@header#1}{\unexpanded{#2}}% + }% + \skv@recordkey{#1}{#2}% +} + +% 1. Record key and its initial/default value if the key appears in the +% 'needini' list of its family. The list 'needini' is made by the +% command \skvsaveinitialvaluekeys. +% 2. To 'save initial values of keys', the key names must have been +% entered by \skvsaveinitialvaluekeys in 'needini' list. +% 3. If a key has no default, then \skv@definedefault will not be called +% and hence \skv@recordkey won't be invoked. +% 4. In \skvdefinekeys, if a key has no default, it won't be entered in +% the 'needini' list. In \skvdefinekeys, an empty default value should +% be indicated with a slash, eg, .cmd/keya/, .ord/keyb//, +% or .ord/keyb/.na/. +% 5. In \skvdefinekeys, use the key/option '.save initial values' to +% create 'needini' lists of families. +\skvrobustdef*\skv@recordkey#1#2{% + \begingroup + \skvifcsdefFT{\skvcurrentpath.@needini}{% + \endgroup\skv@swafalse + }{% + \skv@swatrue + \skvxifinTF{,#1,}{,\skvexpandcsonce{\skvcurrentpath.@needini},}{% + \edef\skv@tempa{\skvtrimspace{#2}}% + \edef\skv@tempa{% + #1=\skvoifstrcmpTF{\skv@tempa}{true}{false}{\skvexpandonce\skv@tempa}% + }% + \skvletcs\skv@tempb{\skvcurrentpath.@inikv}% + \skvifdefTF\skv@tempb{% + \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@tempb,}{}{% + \skvcsedef{\skvcurrentpath.@inikv}{% + \skvaddlist,\skv@tempb\skvexpandonce\skv@tempa + }% + }% + }{% + \skvcsedef{\skvcurrentpath.@inikv}{\skvexpandonce\skv@tempa}% + }% + }{}% + }% + \ifskv@swa + \expandafter\skvaftergroupdef\csname + \skvcurrentpath.@inikv\expandafter\endcsname + \expandafter\endgroup + \fi +} + +% Key will be given in the form <pointer>{<key>}. The command +% \skv@strippointersfromkey is called both when defining and setting keys. +% +% Notes: +% 1. When setting keys, we also call \skv@replacepointers to replace +% '.use value' pointer with the value of the key. +% 2. There is no pointer '.save value', since the key's value is +% always saved automatically. +% +% \skv@strippointersfromkey{<key name>} +% \skv@strippointersfromkey*{<key name cmd>} +% +% Return \skvcurrentkey. +% +\skvrobustdef*\skv@strippointersfromkey{% + \skv@i@testst\skv@str@ppointersfromkey +} +\skvrobustdef*\skv@str@ppointersfromkey#1{% + \begingroup + \toks@{}% + \skv@usetempst{#1}\skv@tempa + % We can choose to ignore the search for pointers where we don't + % use pointers. This is useful at least in one respect: during debugging + % in non-pointer circumstances. In general, it is not advisable to ignore + % pointers. + \ifskv@ignorepointers\else + \def\skv@tempb{\expandafter\skv@findpointer\expandafter{\skv@tempa}}% + \def\skv@add##1{\edef\x{\toks@{\the\toks@##1}}\x}% + \skv@tempb{.need value}\skv@tempa{% + \skv@add{\skvcsdef{\skv@header\skv@tempa.@ndv}{}}% + }{% + \skv@tempb{.gneed value}\skv@tempa{% + \skv@add{\skvcsgdef{\skv@header\skv@tempa.@ndv}{}}% + }{% + \skv@tempb{.forbid value}\skv@tempa{% + \skv@add{\skvcsdef{\skv@header\skv@tempa.@fbv}{}}% + }{% + \skv@tempb{.gforbid value}\skv@tempa{% + \skv@add{\skvcsgdef{\skv@header\skv@tempa.@fbv}{}}% + }{}% + }% + }% + }% + \fi + \skvexpanded{\endgroup + \the\toks@ + % The following complication is because of, e.g., pntkeys: + \edef\noexpand\skvcurrentkey{% + \noexpand\unexpanded{\skvexpandonce\skv@tempa}% + }% + }% +} +% \skv@strippointersfromvalue{<key value>} +% \skv@strippointersfromvalue*{<key value cmd>} +% +% 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, '<tokens>.expanded{value}', since in this case + % applying \edef to the value macro may be unsafe on <tokens>: + \skvifblankTF{##1}{}{% + \skv@err{Nothing should come before the + \MessageBreak pointer '#2' on key '##2'. + \MessageBreak If you require the value of the key to be expanded, + \MessageBreak you should have done the expansion yourself}\skv@ehd + }% + \edef#3{\skvtrimspace{##2}}% + \@firstoftwo + }% + }% + \skv@f@ndpointer#1#2{\skv@nil}\skv@pointer@nil +} +% \skv@extractpntkey@atdefkey{<toks>}{<cmd>} +% +% At define key, extract pseudo key name from a weird key name of the form +% +% evaluate (?) as (?) using (?) +% +\skvrobustdef*\skv@extractpntkey@atdefkey#1#2{% + \begingroup + \@tempcnta\skvz@ + \def#2{}% + \def\skv@prova##1(##2){% + \def\skv@provb{##2}% + \ifx\skv@provb\@nnil\else + \advance\@tempcnta\@ne + \edef#2{\skvexpandonce#2\skvtrimspace{##1}}% + \expandafter\skv@prova + \fi + }% + \skv@prova#1(\@nil)% + \ifx#2\@empty + \edef#2{\unexpanded{#1}}% + \else + % The number of arguments of the pseudo key is determined by the + % number of closed parentheses: + \skvcsedef{\skv@header#2.@x0arg}{\skvgenerateparameters{1}\@tempcnta}% + \fi + \skvexpanded{\endgroup + \skvcmdexit#2% + \skvcsexit{\skv@header#2.@x0arg}% + }% +} +% \skv@extractpntkey@atsetkey{<toks>} +% +% At set key, extract pseudo key name from a weird key name of the form +% +% evaluate (?) as (?) using (?) +% +\skvrobustdef*\skv@extractpntkey@atsetkey#1{% + \begingroup + \def\skvcurrentkey{}% + \def\skvcurrentvalue{}% + \def\skv@prova##1(##2){% + \def\skv@provb{##2}% + \ifx\skv@provb\@nnil\else + \edef\skvcurrentkey{\skvxonce\skvcurrentkey\skvtrimspace{##1}}% + \edef\skvcurrentvalue{\skvxonce\skvcurrentvalue{\skvtrimspace{##2}}}% + \expandafter\skv@prova + \fi + }% + \skv@prova#1(\@nil)% + \ifx\skvcurrentkey\@empty + \skv@err{No key name for a parenthesized key}\skv@ehd + \expandafter\@nodocument + \fi + \skvexpanded{\endgroup + \skvcmdexit\skvcurrentkey\skvcmdexit\skvcurrentvalue + }% +} +\skvrobustdef*\skv@replacepointers#1{% + \begingroup + \let\skvcurrentvalue\@empty + \let\skv@tempa\@empty + \skvifbracedTF{#1}{% + \skv@r@placepointers{#1}.use value\skv@nil + }{% + \skv@r@placepointers#1.use value\skv@nil + }% + \skvaftergroupdef\skvcurrentvalue\endgroup +} +% In the following scheme, if we have, eg, +% +% key2=\dimexpr.use value{key1}*5\relax +% +% \dimexpr will be retained as part of the value of key2 in pointer +% replacement. +% +\skvrobustdef*\skv@r@placepointers#1.use value#2{% + % In case #1 isn't empty: + \edef\skvcurrentvalue{\skvexpandonce\skvcurrentvalue\unexpanded{#1}}% + \edef\skv@prova{\unexpanded{#2}}% + \skvifxTF\skv@prova\skv@nnil{}{% + \skvifcsdefFT{\skv@header#2.@value}{% + \skv@err{No value recorded for key '#2'; ignored}\skv@ehd + \skv@r@placepointers + }{% + \skvxifinTF{,\detokenize{#2},}{,\skvoxdetok\skv@tempa,}{% + \skv@err{Back linking of pointers; + \MessageBreak pointer replacement canceled}\skv@ehd + }{% + \edef\skv@tempa{\skvaddlist,\skv@tempa\unexpanded{#2}}% + \skvletcs\skv@prova{\skv@header#2.@value}% + \skvexpbracenext\skvifbracedTF\skv@prova\skvexpbracenext\skvexpandnext + \skv@r@placepointers\skv@prova + }% + }% + }% +} + +% Assign arguments to keys: +% +% \skvassignargs[<prefixes>]{<families>}{<keys>}{.<expansion type>{<arg>}} +% +% 1. Valid expansion types are .expand once, .expand twice, .expanded. +% 2. At set keys (not at define keys), the argument to the listed keys will +% be expanded using the expansion type. +% 3. The argument will be assigned whether or not a key already has been +% defined. This allows the user to define the key later, after its +% argument has been fixed. +% 3. Under the macros \skvdefinekeys and \directkeys, there are equally +% efficient handlers for assigning arguments to keys. +% +% Example: +% +% \skvassignargs[KV1,KV2]{fam1,fam2}{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 +% +% \<prefix>/<family>/<key>.@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{<expander.type>} +% +% 1. <expander.type> 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{<callback token list>} +% +% Get argument type from the key's callback. The valid tags are +% +% .arg, .arg unexpanded, .arg expanded, .arg expand, .arg expand once, +% .arg expand twice. +% +\skvrobustdef*\skv@getargpattern{% + \let\skv@argpattern\@undefined + \def\do##1##2{% + \ifx\do##1\else + \skvxifinTF{\detokenize{##1}}{\skvoxdetok\skv@callback}{% + \def\skv@prova####1##1####2####3\skv@argpattern@nil{% + \edef\skv@argpattern{\unexpanded{####2}}% + \ifx\skv@argpattern\@empty + \let\skv@argpattern\skv@simplearg + \fi + \edef\skv@callback{\skvtrimspace{####1}\skvtrimspace{####3}}% + }% + \expandafter\skv@prova\skv@callback\skv@argpattern@nil + \def\skvexpandertype{##2}% + \def\do####1\do\do{}% + }{}% + \expandafter\do + \fi + }% + % The following order is important, to avoid incorrect match + % of the search term: + \do{.arg unexpanded}{x0}{.arg expand once}{x1}{.arg expand twice}{x2}% + {.arg expanded}{xx}{.arg expand}{xx}{.arg}{x0}\do\do + \ifdefined\skv@argpattern\else + % Argument pattern might have been specified outside the key's + % callback. + \def\do##1{% + \ifx\do##1\else + \skvifnamedefTF{\skvcurrentfullkey.@##1arg}{% + \skvletcs\skv@argpattern{\skvcurrentfullkey.@##1arg}% + \def\skvexpandertype{##1}% + \def\do####1\do{}% + }{}% + \expandafter\do + \fi + }% + \do{xx}{x2}{x1}{x0}\do + \fi + \ifdefined\skv@argpattern + % Possibly expand value: + \skvstripouterbraces{2}\skv@argpattern + \def\do##1##2{% + \ifx\do##1\else + \skvxifstrcmpTF{##2}\skvexpandertype{% + \edef\skvcurrentvalue{% + \expandafter##1\expandafter{\skvcurrentvalue}% + }% + \def\do####1\do\do{}% + }{}% + \expandafter\do + \fi + }% + \do\@iden{xx}\skvexpandtwice{x2}\skvexpandonce{x1}\unexpanded{x0}\do\do + \else + \let\skv@argpattern\skv@simplearg + \fi +} +% +++++++++++++ Ordinary keys: +% +% \skvordkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +\skvrobustdef*\skvordkey{\skv@testoptb{\skv@ordkey{e}}} +\skvrobustdef*\skvordkeys{\skv@testoptb{\skv@ordkeys{e}}} +% +% \skvgordkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +% Global ordinary keys. So far, these are needed only by \skvgdisablekey. +% +\skvrobustdef*\skvgordkey{\skv@testoptb{\skv@ordkey{x}}} +\skvrobustdef*\skvgordkeys{\skv@testoptb{\skv@ordkeys{x}}} +\skvrobustdef*\skv@ordkey#1#2{% + \skv@strippointersfromkey{#2}% + \skvexpanded{\skv@testopt{\skv@ordkey@a{#1}{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@ordkey@a#1#2[#3]#4{% + \skvifstrcmpTF{#3}{^skv^}{}{\skv@definedefault{#2}{#3}}% + % The pointer '.hp' may be used to set a holder prefix for the key. + \skvxifinTF{\detokenize{.hp}}{\detokenize{#4}}{% + \def\skv@prova##1.hp##2##3\skv@ord@nil{% + \let\skv@gobble@or@iden\@iden + \edef\skv@hp{\unexpanded{##2}}% + \edef\skv@callback{\unexpanded{##1##3}}% + }% + \skv@prova#4\skv@ord@nil + }{% + \let\skv@gobble@or@iden\@gobble + \edef\skv@callback{\unexpanded{#4}}% + }% + \csname skvcs#1def\endcsname{\skv@header#2.@cbk}{% + % This can be called in #3 access the value of ordinary key. + % No prefix is used here, to reduce macro name: + \skv@gobble@or@iden{\skvcslet{\skv@hp#2}\noexpand\skvcurrentvalue}% + \skvexpandonce\skv@callback + }% +} +\skvrobustdef*\skv@ordkeys#1#2{\skv@testopt{\skv@ordkeys@a{#1}{#2}}{^skv^}} +\skvrobustdef*\skv@ordkeys@a#1#2[#3]#4{% + \skvcommaparse{#2}\skv@prova{% + \skvexpbracenext\skv@strippointersfromkey\skv@prova + \skvexpbracenext{\skv@ordkey@a{#1}}\skvcurrentkey[#3]{#4}% + }% +} +% \skvvoidkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +% A void key is an invalid key. A user who tries to set that key will +% automatically be told that the key is inadmissible. The author of an +% invalid key can also use <callback> to set a message. +% +\skvrobustdef*\skvvoidkey{\skv@testoptb\skv@voidkey} +\skvrobustdef*\skv@voidkey#1{% + \skv@strippointersfromkey{#1}% + \skvexpanded{\skv@testopt{\skv@voidkey@a{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@voidkey@a#1[#2]#3{% + \skvifstrcmpTF{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}% + \skvcsedef{\skv@header#1.@cbk}{% + \unexpanded{#3}\relax + \skv@err{Key '#1' of family '\skvcurrentpath' + \MessageBreak has been set invalid by its author}\noexpand\skv@ehd + }% +} +% \skvobsoletekey +% [<pref>]{<fam>}{<obsolete key>}{<new key>}[<defa>]{<callback>} +% +% 1. An obsolete key is a key that is no longer in use and has +% been replaced by a new key. A user who tries to set an obsolete +% key will automatically be told that the key is obsolete and that +% he/she should instead use the new key. +% 2. The author of an obsolete key can also use <callback> to set a +% message for the user. <callback> can be empty. +% 3. Pointers can be used on both <obsolete key> and <new key>. +% +\skvrobustdef*\skvobsoletekey{\skv@testoptb\skv@obsoletekey} +\skvrobustdef*\skv@obsoletekey#1#2{% + \skv@strippointersfromkey{#1}% + \skvexpanded{% + \skv@testopt{\skv@obsoletekey@a{\skvcurrentkey}{#2}}{^skv^}% + }% +} +\skvrobustdef*\skv@obsoletekey@a#1#2[#3]#4{% + \skvifstrcmpTF{#3}{^skv^}{}{\skv@definedefault{#1}{#3}}% + \skvcsedef{\skv@header#1.@cbk}{% + \unexpanded{#4}\relax + \noexpand\skv@warn{Key '\skvcurrentpath/#1' + \MessageBreak is now obsolete. Instead of using the obsolete + \MessageBreak key '#1', I will now set the new key + \MessageBreak '#2' with the value you have given for + \MessageBreak key '#1'. You can avoid this warning by + \MessageBreak using the new key '#2' instead of the + \MessageBreak obsolete key '#1'}\noexpand\skv@ehd + }% +} + +%% Key names with parenthesized tokens. +% +% \skvpntkey[<pref>]{<fam>}{<key>}[<defa>]{<callback>} +% +% The tokens in parenthesis are removed when building the key name. +% Each parenthesis gives rise to one argument of the key's callback. +% When the key is set, the tokens in parenthesis are assigned to the +% arguments. It is the user's responsibility to catch those argument +% in the key's callback. +% +% Example - a key of three arguments: +% +% \skvpntkey[KV]{fam}{evaluate (1) as (2) using (3)}{% +% \def\oldcmd{#1}\def\newcmd{#2}\def\formula{#3}% +% \def\x##1{#1*##1}% +% } +% +% \skvpntkey[KV]{fam}{evaluate (1) as (2) using (3)}[\x\y{\numexpr\x*2}]{% +% \edef\x#2{#3}% +% } +% +% \skvshowcs{KV/fam/evaluateasusing/.@x0arg} +% +% \skvsetkeys[KV]{fam}{evaluate (\x) as (\y) using (\numexpr\x*2)} +% +\skvrobustdef*\skvpntkey{\skv@testoptb\skv@pntkey} +\skvrobustdef*\skv@pntkey#1{% + \skv@strippointersfromkey{#1}% + \skvexpbracenext\skv@extractpntkey@atdefkey\skvcurrentkey\skvcurrentkey + \skvexpanded{\skv@testopt{\skv@pntkey@a{\skvcurrentkey}}{^skv^}}% +} +\skvrobustdef*\skv@pntkey@a#1[#2]#3{% + \skvifstrcmpTF{#2}{^skv^}{}{\skv@definedefault{#1}{#2}}% + \skvcsedef{\skv@header#1.@cbk}{\unexpanded{#3}}% +} + +%% Command keys: +% +% \skvcmdkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% +\skvrobustdef*\skvcmdkey{% + \def\skv@zapsw{01}% + \skv@testoptd\skv@cmdkey{cmd}% +} +% \skvzcmdkey will internally zap the spaces in the key's name, but the +% key user will not notice that: he can still use the key's name with +% spaces in it. The key's author could then use the zapped name internally +% in his code. +\skvrobustdef*\skvzcmdkey{% + \def\skv@zapsw{00}% + \skv@testoptd\skv@cmdkey{cmd}% +} +% When the key's value has doubled hash characters, xkeyval's definition +% of \skv@cmdkey fails: +\skvrobustdef*\skv@cmdkey#1#2[#3]#4{% + \skv@strippointersfromkey{#2}% + \skvifdefboolTF{skv@st}{\skv@definedefault\skvcurrentkey{#3}}{}% + % This is to allow the key name to be expanded before possibly zapping + % spaces in key name: + \edef\skv@prova{#1\skvcurrentkey}% + \edef\skv@prova{\expandafter\skv@zapornot\expandafter + {\expandafter\skv@zapsw\expandafter}\expandafter{\skv@prova}}% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{% + \skvcslet{\skv@prova}\noexpand\skvcurrentvalue + \unexpanded{#4}% + }% +} +% \skvcmdkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>} +\skvrobustdef*\skvcmdkeys{% + \def\skv@zapsw{01}% + \skv@testoptd\skv@cmdkeys{cmd}% +} +\skvrobustdef*\skvzcmdkeys{% + \def\skv@zapsw{00}% + \skv@testoptd\skv@cmdkeys{cmd}% +} +\skvrobustdef*\skv@cmdkeys#1#2[#3]#4{% + \skvcommaparse{#2}\skv@tempa{% + \skvexpanded{\skv@cmdkey{#1}{\skv@tempa}}[#3]{#4}% + }% +} + +%% List processor keys. +% +% \skvlistkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% +\skvrobustdef*\skv@getlistkeyparser#1{% + \begingroup + \let\skv@prova\undefined + \skvxifinTF{\detokenize{\listsep}}{\detokenize{#1}}{% + \skvxifinTF{\detokenize{\listparser}}{\detokenize{#1}}{% + \skv@err{I find both \noexpand\listsep and \noexpand\listparser in the + \MessageBreak callback of key '\skvcurrentfullkey'}\skv@ehd + }{% + \def\skv@prova{\listsep}% + }% + }{% + \skvxifinTF{\detokenize{\listparser}}{\detokenize{#1}}{% + \def\skv@prova{\listparser}% + }{}% + }% + \skvifdefTF\skv@prova{% + % Hide the parameter characters in #1 here: + \edef\skv@provc{\unexpanded{#1}}% + \def\skv@provb##1{% + \def\skv@provb####1##1####2####3\skv@listparser@nil{% + \skvifblankTF{####2}{% + \def\skv@listparser{,}% + }{% + \edef\skv@listparser{\skvtrimspace{####2}}% + }% + \edef\skv@callback{\skvtrimspace{####1}\skvtrimspace{####3}}% + }% + \expandafter\skv@provb\skv@provc\skv@listparser@nil + }% + \expandafter\skv@provb\expandafter{\skv@prova}% + }{% + \def\skv@listparser{,}% + \edef\skv@callback{\skvtrimspace{#1}}% + }% + \skvexpanded{\endgroup + \skvcmdexit\skv@listparser\skvcmdexit\skv@callback + }% +} +\skvnewlet\skvstopprocessinglistkey\skvbreakloop +\skvrobustdef*\skvlistkey{% + \def\skv@zapsw{01}% + \skv@testoptd\skv@listkey{list}% +} +\skvrobustdef*\skvzlistkey{% + \def\skv@zapsw{00}% + \skv@testoptd\skv@listkey{list}% +} +\skvrobustdef*\skv@listkey#1#2[#3]#4{% + \skv@strippointersfromkey{#2}% + \skvifdefboolTF{skv@st}{\skv@definedefault\skvcurrentkey{#3}}{}% + % This is to allow the key name to be expanded before possibly zapping + % spaces in key name: + \edef\skv@provb{#1\skvcurrentkey}% + \edef\skv@provb{\expandafter\skv@zapornot\expandafter + {\expandafter\skv@zapsw\expandafter}\expandafter{\skv@provb}}% + \skv@getlistkeyparser{#4}% + % If there are more parameters in #4 than ####1, the next step + % will raise an error: + \skvxifinTF{\detokenize{##2}}{\skvoxdetok\skv@callback}{% + \skv@err{A listkey can't have more than one + \MessageBreak parameter character}\skv@ehd + }{% + \def\skv@prova{\skvcsdef{\skv@header\skvcurrentkey.@auxcbk}####1}% + \expandafter\skv@prova\expandafter{\skv@callback}% + }% + \let\do\noexpand + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{% + \skvcslet{\skv@provb}\do\skvcurrentvalue + \edef\do\skv@prova{\do\unexpanded{####1}}% + \do\skvstripouterbraces{2}\do\skv@prova + % 1. \skvprocesslist processes an empty list. + % 2. You can call \skvlistcount when processing listkeys, since + % \skvprocesslist initializes and increments it. + \do\skvprocesslist*{\skv@listparser}\do\skv@prova\do\skv@prova{% + \do\csname\skv@header\skvcurrentkey.@auxcbk% + \do\expandafter\endcsname\do\expandafter{\do\skv@prova}% + }% + }% + % A listkey must have a 1-parameter argument. We use the following + % definition to enforce this requirement: + \skvcsdef{\skv@header\skvcurrentkey.@listkey}{}% +} +% \skvlistkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>} +\skvrobustdef*\skvlistkeys{% + \def\skv@zapsw{01}% + \skv@testoptd\skv@listkeys{list}% +} +\skvrobustdef*\skvzlistkeys{% + \def\skv@zapsw{00}% + \skv@testoptd\skv@listkeys{list}% +} +\skvrobustdef*\skv@listkeys#1#2[#3]#4{% + \skvcommaparse{#2}\skv@tempa{% + \skvexpanded{\skv@listkey{#1}{\skv@tempa}}[#3]{#4}% + }% +} + +%% Choice keys: +% +% The skeyval package allows holder-macro prefixes {<mp>} for choice keys. +% +% \skvchoicekey[<pref>]{<fam>}[<mp>]{<keys>}[<bins>]{<choices>} +% [<default>]{<callback>} +% \skvchoicekey+[<pref>]{<fam>}[<mp>]{<keys>}[<bins>]{<choices>} +% [<default>]{<callback>}{<fallback>} +% \skvchoicekey*[<pref>]{<fam>}[<mp>]{<keys>}[<bins>]{<choices>} +% [<default>]{<callback>} +% \skvchoicekey*+[<pref>]{<fam>}[<mp>]{<keys>}[<bins>]{<choices>} +% [<default>]{<callback>}{<fallback>} +% +% \skvchoicekeys[KV]{fam}[mp@]{key1,key2}[\val\nr]{% +% center.do=\def\vala{#1}, +% right.do=\def\valb{#1}, +% left.do=\def\valc{#1} +% }[center]{\def\x##1{#1*##1}} +% +% \skv@preparenominations{00.or.01}{<altlist>} +\skvrobustdef*\skv@preparenominations#1#2{% + \begingroup + \def\skv@altlista{}% + \def\skv@altlistb{}% + \def\skv@provc##1.do=##2.do=##3\skv@choice@nil{% + \if#1\lowercase{\fi + \edef\skv@prova{\skvtrimspace{##1}}% + \if#1}\fi + \skvstripouterbraces{2}\skv@prova + \edef\skv@altlista{% + \skvaddlist,\skv@altlista\skvxonce\skv@prova + }% + \edef\reserved@a{% + \skvnoexpandcs{\skvoxdetok\skv@prova @skvch}% + {\skvtrimspace{##2}}% + }% + \edef\skv@altlistb{\skvxonce\skv@altlistb\skvxonce\reserved@a}% + }% + \skvcommaparse{#2}\skv@prova{% + \expandafter\skv@provc\skv@prova.do=.do=\skv@choice@nil + }% + \ifx\skv@altlista\@empty + \skv@err{No nominations (state pattern) for choice key}\skv@ehd + \def\skv@altlistb{}% + \fi + \skvexpanded{\endgroup + \skvcmdexit\skv@altlista\skvcmdexit\skv@altlistb + }% +} +% \skv@executechoice{00 or 01}{<currentvalue>}{<altlist>} +\skvrobustdef*\skv@executechoice#1#2#3{% + \begingroup + \if#1\lowercase{\fi + \edef\skv@prova{\skvtrimspace{#2}}% + \if#1}\fi + \skvstripouterbraces{2}\skv@prova + \edef\do{% + \expandafter\noexpand\csname\detokenize + \expandafter{\skv@prova}@skvch\endcsname + }% + % Hide #3 in a macro to avoid confusing any parameter + % characters in #3 with the parameters of \skv@prova: + \edef\skv@provb{\unexpanded{#3}}% + \edef\skv@prova{\def\noexpand\skv@prova####1\skvxonce\do####2####3}% + \skv@prova\skv@choice@nil{% + \toks@{##2}% + \edef\skv@prova{\unexpanded{##2}}% + \ifx\skv@prova\skv@nnil + \skv@err{No state pattern match found for key + \MessageBreak '\skvcurrentkey'}\skv@ehd + \toks@{}% + \fi + }% + \expandafter\expandafter\expandafter\skv@prova\expandafter + \skv@provb\do{\skv@nil}\skv@choice@nil + \expandafter\endgroup\the\toks@ +} +\skvrobustdef*\skvchoicekey{% + \skv@testopta{\skv@testoptb + {\skv@testopt\skv@choicekey@a{choice\skv@header}}}% +} +\skvnewlet\skvchoicekeys\skvchoicekey +\skvrobustdef*\skv@choicekey@a[#1]#2{% + \skvxifinTF{\skv@hashchar}{\detokenize{#2}}{% + \skv@err{Hash character '\skv@hashchar' not allowed + \MessageBreak in key name}\skv@ehd + }{% + \edef\skv@currlist{\unexpanded{#2}}% + \edef\skv@hp{\skvtrimspace{#1}}% + \skv@testopt\skv@choicekey@b{}% + }% +} +\skvrobustdef*\skv@choicekey@b[#1]#2{% + \edef\skv@bins{\unexpanded{#1}}% + % Check the syntax of bins. This also helps catch wrong use syntax + % of \skvchoicekeys: it's easy to get the syntax of \skvchoicekeys + % wrong. The default value may have been put in place of bins. + \ifx\skv@bins\@empty\else + \skvtfor\skv@prova:=#1\do{% + \skvexpbracenext\skvifescapedTF\skv@prova{}{% + \skv@err{Token '\skvoxdetok\skv@prova' of bin of + \MessageBreak choice key isn't escaped}\skv@ehd + }% + }% + \fi + \skvifblankTF{#2}{% + \skv@err{No nominations (state pattern) for + \MessageBreak choice keys '\skv@currlist'}\skv@ehd + }{% + \skvifboolTF{skv@st}{% + \skv@preparenominations{00}{#2}% + }{% + \skv@preparenominations{01}{#2}% + }% + }% + \skvifnextchar[\skv@choicekey@c{\skv@choicekey@c[^skv^]}% +} +\skvrobustdef*\skv@choicekey@c[#1]{% + \edef\skv@default{\skvtrimspace{#1}}% + % Choice can't have braced values, since that will make matching + % to 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[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% \skvboolkey+[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}{<fallback>} +% +% Boolean keys create a store to receive the key's value and also create +% a boolean internally. +% +\skvrobustdef*\skvboolkey{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +% \skvzboolkey will internally zap the spaces in the key's name and in +% the boolean that it creates internally, but the key user will not +% notice that: he will still be able to submit the original key's name +% with spaces in it. The key's author could then use the zapped key +% name and boolean in his code (internally). +\skvrobustdef*\skvzboolkey{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +% \skv@boolkeys{<mp>}{<key>}[<default>] +\skvrobustdef*\skv@boolkey#1#2[#3]{% + \skv@strippointersfromkey{#2}% + \skvexpanded{% + \ifskv@pl\skv@boolkey@b\else\skv@boolkey@a\fi + {\skvcurrentkey}{#1\skvcurrentkey}% + }{#3}% +} +% \skv@boolkey@a{<key>}{<mp.key>}{<default>}{<callback>} +\skvrobustdef*\skv@boolkey@a#1#2#3#4{% + \def\skv@tempa{\skv@boolkey@c{#1}{#2}{#3}}% + \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{<key>}{<mp.key>}{<default>}{<callback>}{<fallback>} +\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{<key>}{<mp.key>}{<default>}{{<callback>}{<fallback>}} +\skvrobustdef*\skv@boolkey@c#1#2#3#4{% + \if\skv@typetogkey + \skvdeftog{\skv@zapornot{\skv@zapsw}{#2}}% + \else + \skvcsnewif{\skv@zapornot{\skv@zapsw}{#2}}% + \fi + \skvifdefboolTF{skv@st}{\skv@definedefault{#1}{#3}}{}% + \edef\skv@tempa##1##2{% + \noexpand\skv@sttrue + \ifskv@pl\noexpand\skv@pltrue\else\noexpand\skv@plfalse\fi + \skv@checkchoice[\noexpand\skv@value]{##1}{true,false}##2% + }% + \skv@temptoks\expandafter{\skv@tempa{##1}{#4}}% + \skvcsedef{\skv@header#1.@cbk}{\the\skv@temptoks}% +} +% +% \skvboolkeys[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>} +% \skvboolkeys+[<pref>]{<fam>}[<mp>]{<keys>}[<defa>]{<callback>}{<fallback>} +% +\skvrobustdef*\skvboolkeys{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +\skvrobustdef*\skvzboolkeys{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +% \skv@boolkeys{<mp>}{<keys>}[<default>] +\skvrobustdef*\skv@boolkeys#1#2[#3]{% + \skvifdefboolTF{skv@pl}\skv@boolkeys@b\skv@boolkeys@a{#2}{#1}{#3}% +} +% \skv@boolkeys@a{<keys>}{<mp>}{<default>}{<callback>} +\skvrobustdef*\skv@boolkeys@a#1#2#3#4{\skv@boolkeys@c{#1}{#2}{#3}{#4}{^skv^}} +% \skv@boolkeys@b{<keys>}{<mp>}{<default>}{<callback>}{<fallback>} +\skvrobustdef*\skv@boolkeys@b#1#2#3#4#5{\skv@boolkeys@c{#1}{#2}{#3}{#4}{#5}} +\skvrobustdef*\skv@boolkeys@c#1#2#3#4#5{% + \skvcommaparse{#1}\skv@tempa{% + \skvexpbracenext\skv@strippointersfromkey\skv@tempa + \skvexpanded{% + \skvifstrcmpTF{#5}{^skv^}{% + \skv@boolkey@a{\skvcurrentkey}% + {#2\skvcurrentkey}\unexpanded{{#3}{#4}}% + }{% + \skv@boolkey@b{\skvcurrentkey}% + {#2\skvcurrentkey}\unexpanded{{#3}{#4}{#5}}% + }% + }% + }% +} +% +% \skvbiboolkeys[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>] +% {<callback.for.key1>}{<callback.for.key2>} +% \skvbiboolkeys+[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>] +% {<callback.for.key1>}{<callback.for.key2>}{<fallback>} +% +% Example: +% +% \skvbiboolkeys+[KV]{fam}[mp@]{.need value{keya},keyb}[true]{% +% \ifmp@keya\def\xa##1{##1}\fi +% }{% +% \ifmp@keyb\def\xb##1{##1}\fi +% }{% +% \skv@warn{Value '#1' is invalid}% +% } +% +\skvrobustdef*\skvbiboolkeys{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}% +} +\skvrobustdef*\skvzbiboolkeys{% + \def\skv@typetogkey{01}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}% +} +\skvrobustdef*\skv@biboolkeys#1#2[#3]{% + \begingroup + \edef\skv@currlist{\unexpanded{#2}}% + \skvcsvnormalize\skv@currlist + \@tempcnta\skvz@ + \skvcommaloop*\skv@currlist\skv@tempa{% + \advance\@tempcnta\@ne + \ifnum\@tempcnta>\tw@ + \skvbreakloop + \fi + }% + \ifnum\@tempcnta=\tw@\else + \skv@err{Number of bi-keys isn't 2: + \MessageBreak ||\skv@currlist||}\skv@ehd + \fi + \skvexpanded{% + \ifskv@pl\skv@biboolkeys@b\else\skv@biboolkeys@a\fi + {\skv@currlist}% + }{#1}{#3}% +} +% \skv@bilboolkeys@a{<key1,key2>}{<mp>}{<default>}{<callback1>}{<callback2>} +\skvrobustdef*\skv@biboolkeys@a#1#2#3#4#5{% + \skv@biboolkeys@c{#1}{#2}{#3}{#4}{#5}{^skv^}% +} +% \skv@bilboolkeys@a{<key1,key2>}{<mp>}{<default>} +% {<callback1>}{<callback2>}{<fallback>} +\skvrobustdef*\skv@biboolkeys@b#1#2#3#4#5#6{% + \skv@biboolkeys@c{#1}{#2}{#3}{#4}{#5}{#6}% +} +\skvrobustdef*\skv@biboolkeys@c#1#2#3#4#5#6{% + % Don't group the following: pointers will be lost outide the group. + % 'bicallerid' is used to indicate that the master key is the one + % that has instantiated the callback of the slave. This avoids + % infinite re-entrance of \skvsetkeys. + \edef\skv@default{\unexpanded{#3}}% + \edef\skv@tempc{\unexpanded{#4}}% + \edef\skv@tempd{\unexpanded{#5}}% + \edef\skv@tempe{\unexpanded{#6}}% + \toks@{}\def\skv@provd{}% + \let\nx\noexpand + \def\@do##1##2{% + \skvifcsname\skv@header##1.@##2\then + \edef\skv@provd{% + \skv@provd + \skvcsdef{\skv@header##1.@##2}{% + \csname\skv@header##1.@##2\endcsname + }% + }% + \fi + }% + \def\skv@provc##1##2##3##4{% + \skvsettogtrue{\skv@header bicallerid/##3}% + \skvxonce\@elt + \nx\skvifxTF\nx\skvcurrentvalue\nx\skv@truetoks{% + \nx\skviftogTF{\skv@header bicallerid/##4}{}{% + \skvsetkeys[##1]{##2}{##4=false}% + }% + }{% + \nx\skviftogTF{\skv@header bicallerid/##4}{}{% + \skvsetkeys[##1]{##2}{##4=true}% + }% + }% + \skvsettogfalse{\skv@header bicallerid/##3}% + }% + % \skv@provb{<KV>}{<fam>}{<primary.key>}{<secondary.key>} + \def\skv@provb##1##2##3##4{% + \@do{##3}{ndv}\@do{##3}{fbv}% + \skvexpanded{% + \toks@{\the\toks@ + \skvdeftog{\skv@header bicallerid/##3}% + \skvifstrcmpTF{#6}{^skv^}{% + \skv@boolkey@a + {##3}{#2##3}{\skvxonce\skv@default}{% + \skv@provc{##1}{##2}{##3}{##4}% + }% + }{% + \skv@boolkey@b + {##3}{#2##3}{\skvxonce\skv@default}{% + \skv@provc{##1}{##2}{##3}{##4}% + }{\skvxonce\skv@tempe}% + }% + }% + }% + }% + \def\skv@prova##1,##2\skv@nil{% + \skv@strippointersfromkey{##1}% + \let\skv@tempa\skvcurrentkey + \skv@strippointersfromkey{##2}% + \let\skv@tempb\skvcurrentkey + \skvexpanded{% + \let\nx\@elt\nx\skv@tempc + \nx\skv@provb{\skvcurrentprefix}{\skvcurrentfamily}% + {\skv@tempa}{\skv@tempb}% + \let\nx\@elt\nx\skv@tempd + \nx\skv@provb{\skvcurrentprefix}{\skvcurrentfamily}% + {\skv@tempb}{\skv@tempa}% + }% + }% + \skv@prova#1\skv@nil + \skvexpanded{\endgroup + \skv@provd\the\toks@ + }% +} + +%% Toggle keys: +% +% \skvtogkey[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>} +% \skvtogkey+[<pref>]{<fam>}[<mp>]{<key>}[<defa>]{<callback>}{<fallback>} +% +\skvrobustdef*\skvtogkey{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +\skvrobustdef*\skvztogkey{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@boolkey{}}% +} +\skvrobustdef*\skvtogkeys{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +\skvrobustdef*\skvztogkeys{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@boolkeys{}}% +} +% \skvbitogkeys[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>] +% {<callback.for.key1>}{<callback.for.key2>} +% \skvbitogkeys+[<pref>]{<fam>}[<mp>]{<key1,key2>}[<defa>] +% {<callback.for.key1>}{<callback.for.key2>}{<fallback>} +\skvrobustdef*\skvbitogkeys{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{01}% + \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}% +} +\skvrobustdef*\skvzbitogkeys{% + \def\skv@typetogkey{00}% + \def\skv@zapsw{00}% + \skv@t@stopta{\skv@testoptd\skv@biboolkeys{}}% +} +\skvrobustdef*\skvSignalkeyPresetException#1{% + \edef\skv@signalpreseterr{\unexpanded{#1}}% +} +\skvSignalkeyPresetException{% + \@latex@error{% + Key '\skvcurrentfullkey' + \MessageBreak is a signal key. It shouldn't have + \MessageBreak been preset. Presetting signal keys + \MessageBreak does lead to cyclic key setting + }\skv@ehd +} + +% Declare prefix and family and then define/set keys on them (without +% the need to give prefix and family). This can be handy if all the keys +% in a project have the same prefix and family. +% +% Example: +% +% \skvsetprefix{KV} +% \skvsetfamily{fam} +% \skvdefineordkey{key1}[default]{\def\x##1{##1*#1}} +% \skvprocesskeys{key1=value1} +% +\skvrobustdef*\skvsetprefix{\def\skv@currpref} +\skvrobustdef*\skvsetfamily{\def\skv@currfam} +\skvrobustdef*\skv@defineonmeta#1{% + \ifdefined\skv@currpref\else + \let\skv@currpref\skvdefaultprefix + \fi + \skvexpanded{\noexpand#1[\skv@currpref]{\skv@currfam}}% +} +\skvrobustdef*\skvdefineordkey{\skv@defineonmeta\skvordkey} +\skvrobustdef*\skvdefineordkeys{\skv@defineonmeta\skvordkeys} +\skvrobustdef*\skvdefinecmdkey{\skv@defineonmeta\skvcmdkey} +\skvrobustdef*\skvdefinecmdkeys{\skv@defineonmeta\skvcmdkeys} +\skvrobustdef*\skvdefineboolkey{\skv@defineonmeta\skvboolkey} +\skvrobustdef*\skvdefineboolkeys{\skv@defineonmeta\skvboolkeys} +\skvrobustdef*\skvdefinebiboolkeys{\skv@defineonmeta\skvbiboolkeys} +\skvrobustdef*\skvdefinetogkey{\skv@defineonmeta\skvtogkey} +\skvrobustdef*\skvdefinetogkeys{\skv@defineonmeta\skvtogkeys} +\skvrobustdef*\skvdefinechoicekey{\skv@defineonmeta\skvchoicekey} +\skvrobustdef*\skvdefinechoicekeys{\skv@defineonmeta\skvchoicekeys} +\skvrobustdef*\skvprocesskeys{\skv@defineonmeta\skvsetkeys} + +% Signal-slot keys: +% +% \skvappendslots[<pref>]{<fam>}{<signal.keys>}{<slots>} +% [<signal default>]{<signal callback>} +% \skvprependslots[<pref>]{<fam>}{<signal.keys>}{<slots>} +% [<signal default>]{<signal callback>} +% +% 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. <slots> must pre-exist. If slot key is undefined, then the signal-slot +% system isn't useful here since the slot will serve no purpose. +% +% 2. If desired, use pointers/handlers .expanded, .expand once, .expand twice +% on values of <slots>. +% +% 3. Here, it helps to consider <signal.keys> as parents (or signals/ +% subjects/observables) and <slots> as children (or slots/observers). +% +% 4. <signal.keys> can't be preset, to avoid cyclic setting of keys. +% Consider the following, for a signal key in the preset list: +% +% set any key on a path -> preset 'signal' +% -> set 'slot' -> preset 'signal'. +% +% In the attempt to set a slot key, the preset list (that includes +% the signal key) will be instantiated. +% +\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{<signal>}{<slots>}{<signal.callback>} +\skvrobustdef*\skv@appendslots@b#1#2#3{% + \def\skv@slotlist{}% + \skvkvparse{#2}\skv@prova{% + \skv@okvsplit\skv@prova{% + \skvxifstrcmpTF{#1}{##1}{% + \skv@err{Linking key '##1' to itself}\skv@ehd + }{% + % If slot key is undefined, then the signal-slot system isn't + % useful, since the slot won't be able to do anything with the + % emitted signal. + \skvifcsdefTF{\skv@header##1.@cbk}{}{% + \skv@err{Slot key '\skvcurrentpath/##1' + \MessageBreak is undefined}\skv@ehd + }% + }% + \edef\skv@slotlist{% + \skvaddlist,\skv@slotlist\unexpanded{##1}=% + \skvexpanderpointer{\unexpanded{##2}}% + }% + }% + }% + \edef\skv@elt{% + % Signal keys shouldn't be preset, to avoid cyclic setting of keys. + % See note above. + \noexpand\skvifdefboolTF{skv@inprepo}{% + \noexpand\skv@signalpreseterr + }{% + \noexpand\skvsetkeys[\skvcurrentprefix]% + {\skvcurrentfamily}{\skvexpandonce\skv@slotlist}% + }% + }% + % The signal key might already exists, especially when 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[<pref>]{<fam>}{<signals>}{<prepended.slots>} +% {<appended.slots>}[<signal.default>]{<signal.callback>} +% +% Slot keys are observer keys that receive signals from signal +% (ie, subject) keys. +% +% Examples: +% +% \skvordkeys[KV]{fam}{keya1,keyb1,keya2,keyb2}{\def\x##1{#1*##1}} +% \skvaddslots[KV]{fam}{keyd,keye}{keya1=vala,keyb1=valb} +% {keya2=vala,keyb2=.expanded{\valb}}[def-d&e]{\def\y##1{#1*##1}} +% +% \skvordkeys{graph}{avertices,bvertices}[6]{\def\x##1{#1+##1}} +% \skvaddslots{graph}{vertices}{avertices=4}{bvertices=8}[6] +% {\def\y##1{#1*##1}} +% \skvsetkeys{graph}{vertices=6} +% +\skvrobustdef*\skvaddslots{% + \let\skvexpanderpointer\@iden + \skv@testoptb\skv@addslots +} +\skvrobustdef*\skv@addslots#1#2#3{% + \skv@testopt{\skv@addslots@a{#1}{#2}{#3}}{^skv^}% +} +% \skv@addslots@a{<signals>}{<prepended slots>}{<appended slots>} +% [<signal default>]{<signal callback>} +\skvrobustdef*\skv@addslots@a#1#2#3[#4]#5{% + \skvexpanded{% + \skvprependslots[\skvcurrentprefix]{\skvcurrentfamily}% + \unexpanded{{#1}{#2}[#4]{#5}}% + % 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[<prefs>]{<fams>}{key}{<true>}{<false>} +% +% 1. The search will stop as soon as the key is found in one combination +% of prefixes/families. +% 2. The prefix and header created here may be needed in #5 and #6. Hence +% we put entry prefix and header on stack before commencing the search. +% +\skvrobustdef*\skvifkeydefTF{\skv@testopt{\skv@ifkeyundef0}\skvdefaultprefix} +\skvrobustdef*\skvifkeydefFT{\skv@testopt{\skv@ifkeyundef1}\skvdefaultprefix} +\skvrobustdef*\skv@ifkeyundef#1[#2]#3#4#5#6{% + \skvpushstate\skv@keystate\skv@keydepth + \edef\skv@prefs{#2}% + \edef\skv@fams{#3}% + \skvdespace{#4}\skvcurrentkey + \skv@kffalse + % Don't use \skvcurrentprefix in place of \skv@prova here. + % We use \skv@prova here because \skv@tempa, \skvcurrentprefix and + % \skvcurrentfamily might be in use already. + \skvcommaparse*\skv@prefs\skv@prova{% + \skv@makeprefix\skv@prova + \skv@ifk@yundef + % Break outer loop as needed: + \ifskv@kf\skvbreakloop\fi + }% + \skvexpanded{% + \skvifboolTF{skv@kf}{% + \if0#1% + \noexpand\@firstoftwo + \else + \noexpand\@secondoftwo + \fi + }{% + \if0#1% + \noexpand\@secondoftwo + \else + \noexpand\@firstoftwo + \fi + }% + }{#5}{#6}% + \skvpopstate\skv@keystate\skv@keydepth +} +\skvrobustdef*\skv@ifk@yundef{% + \skvcommaparse*\skv@fams\skv@prova{% + \skv@makeheader\skv@prova + \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{}{% + \skv@kftrue\skvbreakloop + }% + }% +} +% \skvifkeyinfamiliesTF[<prefs>]{<fams>}{key}{<true>}{<false>} +% +% The search will be done on all combinations of prefixes/families. The +% callback <true> or <false> will be executed for ALL the combinations. +% +\skvrobustdef*\skvifkeyinfamiliesTF{% + \skv@testopt{\skv@ifkeyinfamilies0}\skvdefaultprefix +} +% \skvdoonallpathsfound[<prefs>]{<fams>}{key}{<true>} +\skvrobustdef*\skvdoonallpathsfound{% + \skv@testopt\skv@doonallpathsfound\skvdefaultprefix +} +\skvrobustdef*\skv@doonallpathsfound[#1]#2#3#4{% + \skv@ifkeyinfamilies0[#1]{#2}{#3}{#4}{}% +} +% \skvdoonallpathsnotfound[<prefs>]{<fams>}{key}{<false>} +\skvrobustdef*\skvdoonallpathsnotfound{% + \skv@testopt\skv@doonallpathsnotfound\skvdefaultprefix +} +\skvrobustdef*\skv@doonallpathsnotfound[#1]#2#3#4{% + \skv@ifkeyinfamilies0[#1]{#2}{#3}{}{#4}% +} +\skvrobustdef*\skvifkeyinfamiliesFT{% + \skv@testopt{\skv@ifkeyinfamilies1}\skvdefaultprefix +} +\skvrobustdef*\skv@ifkeyinfamilies#1[#2]#3#4#5#6{% + \skvpushstate\skv@keystate\skv@keydepth + \edef\skv@prefs{#2}% + \edef\skv@fams{#3}% + \skvdespace{#4}\skvcurrentkey + \skvcommaparse*\skv@prefs\skv@prova{% + \skv@makeprefix\skv@prova + \skv@ifk@yinfamilies{#1}{#5}{#6}% + }% +} +\skvrobustdef*\skv@ifk@yinfamilies#1#2#3{% + \skvcommaparse*\skv@fams\skv@prova{% + \skv@makeheader\skv@prova + \skvexpanded{% + \skvifcsdefTF{\skv@header\skvcurrentkey.@cbk}{% + \if0#1% + \noexpand\@firstoftwo + \else + \noexpand\@secondoftwo + \fi + }{% + \if0#1% + \noexpand\@secondoftwo + \else + \noexpand\@firstoftwo + \fi + }% + }{#2}{#3}% + }% + \skvpopstate\skv@keystate\skv@keydepth +} +% \skvifkeyinfamilyTF{<prefix>/<family>}{<key>} +\skvnewdef*\skvifkeyinfamilyTF#1#2{% + \skvifcsdefTF{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}% +} +% \skvifkeyincurrentfamilyTF{<key>} +\skvnewdef*\skvifkeyincurrentfamilyTF#1{% + \skvifcsdefTF{\skvcurrentpath/\skvtrimspace{#1}.@cbk}% +} +\skvnewlet\skvifkeyoncurrentpathTF\skvifkeyincurrentfamilyTF + +% \skvifonekeyinfamilyTF{<prefix>/<family>}{<keys>} +% +% Given a list of keys, test if at least one of them is defined. +% +% Example: +% +% \edef\x{\skvifonekeyinfamilyTF{KV/fam}{keya,keyb,keyc}{T}{F}}. +% +\skvnewdef*\skvifonekeyinfamilyTF#1#2{% + \ifnum\numexpr0\skv@ifonekeydef{#1}#2,\@nnil,>\skvz@ + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvnewdef*\skv@ifonekeydef#1#2,{% + \ifx\@nnil#2% + \expandafter\@gobble + \else + \skvifcsdefTF{\skvtrimspace{#1}/\skvtrimspace{#2}.@cbk}{+1}{+0}% + \expandafter\skv@ifonekeydef + \fi + {#1}% +} + +% \skvgetdefinedkeys[<pref>]{<fams>}{<keys>} +% +% 1. Get keys defined on the given paths. +% 2. Remove any redundant star (*) and plus (+) suffix on +% \skvgetdefinedkeys. +% +\skvrobustdef*\skvgetdefinedkeys{% + \skv@testopta{\skv@testopt\skv@getdefinedkeys\skvdefaultprefix}% +} +\skvrobustdef*\skv@getdefinedkeys[#1]#2#3{% + \begingroup + \skv@makeprefix{#1}% + \def\skv@tempa{}% + \skvcommaparse{#2}\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + % Prepare to take defined keys outside the group: + \skvxifinTF{\skvoxdetok\skv@header}{\skvoxdetok\skv@tempa}{}{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \noexpand\skvcsexit{\skvcurrentpath.@definedkeys}% + }% + }% + \skvcommaparse{#3}\skvcurrentkey{% + \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{}{% + \skvletcs\skv@prova{\skvcurrentpath.@definedkeys}% + \skvifdefTF\skv@prova{% + \skvxifinTF{,\skvoxdetok\skvcurrentkey,}{,\skvoxdetok\skv@prova,}{}{% + \skvcsedef{\skvcurrentpath.@definedkeys}{\skv@prova,\skvcurrentkey}% + }% + }{% + \skvcsedef{\skvcurrentpath.@definedkeys}{\skvcurrentkey}% + }% + }% + }% + }% + \skvexpanded{\endgroup\skv@tempa}% +} + +% \skvvalueof{<pref>}{<family>}{<key>} +\skvnewdef*\skvvalueof#1#2#3{\skvcsuse{#1/#2/#3.@value}} +% \skvvalueofkey{<pref>/<family>/<key>} +\skvnewdef*\skvvalueofkey#1{\skvcsuse{#1.@value}} + +% When <pref> isn't given, the following will use 'default KV' for <pref>. +% \skvprintvalue[<pref>]{<family>}{<key>} +\skvrobustdef*\skvprintvalue{\skv@testoptb\skv@printvalue} +\skvrobustdef*\skv@printvalue#1{\skvcsuse{\skv@header#1.@value}} + +% \skvgetvalue{<pref>}{<family>}{<key>}<cmd> +\skvrobustdef*\skvgetvalue#1#2#3#4{\skvletcs#4{#1/#2/#3.@value}} + +% \skvconveyvalue[<pref>]{<family>}{<key>}<cmd> +\skvrobustdef*\skvconveyvalue{\skv@testoptb\skv@conveyvalue} +\skvrobustdef*\skv@conveyvalue#1#2{\skvletcs#2{\skv@header#1.@value}} + +% \skvgetinikv{<pref>}{<family>}<cmd> +\skvrobustdef*\skvgetinikv#1#2#3{\skvletcs#3{#1/#2.@inikv}} + +% \skvconveyinikv[<pref>]{<family>}<cmd> +\skvrobustdef*\skvconveyinikv{\skv@testoptb\skv@conveyinikv} +\skvrobustdef*\skv@conveyinikv#1{\skvletcs#1{\skvcurrentpath.@inikv}} + +% \skvkeyslet[<pref>]{<family>}{<list>} +% +% <list> -> {<newkey1>,<newkey2>,...,<newkey-n>}={<oldkey>} +% +% Note: <oldkey> must be only one key, to avoid letting one newkey +% to two oldkeys. +% +\skvrobustdef*\skvkeyslet{\skv@testoptb\skv@keyslet} +\skvnewlet\skvkeyinherit\skvkeyslet +\skvrobustdef*\skv@keyslet#1{% + \skvletcs\skv@inikv{\skvcurrentpath.@inikv}% + \skvifdefFT\skv@inikv{}{% + \skvexpbracenext\skv@getnamesofkeys\skv@inikv\skv@inikeys + }% + \def\skv@keyslet@do@a##1=##2=##3\skv@nil{% + \skvifinTF{,}{##2}{% + \skv@err{There more than one old key:\MessageBreak '##2'}\skv@ehd + }{% + \skvcommaparse{##1}\skv@prova{% + \skvexpbracenext\skv@keyslet@do@b\skv@prova{##2}% + }% + }% + }% + \def\skv@keyslet@do@b##1##2{% + \skvifnamedefFT{\skv@header##2.@cbk}{% + \skv@err{Key '\skvcurrentpath/##2'\MessageBreak is undefined}\skv@ehd + }{% + \skvifnamedefTF{\skv@header##1.@cbk}{% + \skv@err{Key '\skvcurrentpath/##1' is already defined}\skv@ehd + }{}% + }% + \skvcommaloop{cbk,ndv,gnv,fbv,gfv}\skv@prova{% + \skvcsletcs{\skv@header##1.@\skv@prova}{\skv@header##2.@\skv@prova}% + }% + \skvifnamedefTF{\skv@header##2.@defa}{% + \edef\skvcurrentkey{##2}% + \skv@getdefault\skvcurrentprefix\skvcurrentfamily\skvcurrentkey + \skvcurrentvalue + \skvcsedef{\skv@header##1.@defa}{% + \skvnoexpandcs{\skv@header##1}{\skvexpandonce\skvcurrentvalue}% + }% + \skv@swatrue + }{% + \skv@swafalse + }% + \skvifdefFT\skv@inikv{}{% + \skvxifinFT{,\detokenize{##2},}{,\skvoxdetok\skv@inikeys,}{}{% + \skvcsedef{\skvcurrentpath.@inikv}{% + \skvcsaddlist,{\skvcurrentpath.@inikv}% + ##1\ifskv@swa=\skvexpandonce\skvcurrentvalue\fi + }% + }% + }% + \skvcommaloop{ignoredkeys,needini}\skv@prova{% + \skvletcs\skv@tempa{\skvcurrentpath.@\skv@prova}% + \skvifdefFT\skv@tempa{}{% + \skvxifinFT{,\detokenize{##2},}{,\skvoxdetok\skv@tempa,}{}{% + \skvcsedef{\skvcurrentpath.@\skv@prova}{% + \skvaddlist,\skv@tempa##1% + }% + }% + }% + }% + }% + \skvkvparse{#1}\skv@prova{% + \expandafter\skv@keyslet@do@a\skv@prova==\skv@nil + }% + \let\do\skvundef + \do\skv@keyslet@do\do\skv@inikv\do\skv@inikeys +} + +\skvrobustdef*\skv@disabledkeywarning#1{% + \skv@warn{Key '\skvcurrentpath/#1' has been disabled + \MessageBreak You can't set or reset key '#1' at this + \MessageBreak late stage. Perhaps you should have set it + \MessageBreak earlier \ifskv@latex in \noexpand\documentclass or + \string\usepackage\fi}% +} +\skvrobustdef*\skv@disabledkeyerror#1{% + \skv@err{Key '\skvcurrentpath/#1' has been disabled}% + {You can't set or reset key '#1' at this + \MessageBreak late stage. Perhaps you should have set it + \MessageBreak earlier \ifskv@latex in \noexpand\documentclass or + \string\usepackage\fi}% +} +% A key may be disabled long before \skvmessagetypefordisabledkey +% is called. +\skvrobustdef*\skvmessagetypefordisabledkey#1{% + \edef\skv@prova{\skvtrimspace{#1}}% + \def\skv@provb##1{% + \def\skv@provb####1##1####2####3\@nil{% + \ifcase####2\relax + \let\skv@disabledkeymsg\skv@disabledkeywarning + \or + \let\skv@disabledkeymsg\skv@disabledkeywarning + \or + \let\skv@disabledkeymsg\skv@disabledkeyerror + \or + \let\skv@disabledkeymsg\@gobble + \else + \skv@err{Invalid message type '#1' for disabled keys}\skv@ehd + \fi + }% + \skv@provb warn{0}warning{1}error{2}nothing{3}##1{10}\@nil + }% + \expandafter\skv@provb\expandafter{\skv@prova}% +} +\skvnewlet\skvdisabledkeysmessagetype\skvmessagetypefordisabledkey +\skvdisabledkeysmessagetype{warn} + +\skvrobustdef*\skvdisablekeys{% + \def\skv@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[<pref>]{<fam>}{<keys>} +% \skvgpresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvpresetkeys{\skv@testopt\skv@presetkeys\skvdefaultprefix} +\skvrobustdef*\skv@presetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{#3}{}} +\skvrobustdef*\skvgpresetkeys{\skv@testopt\skv@gpresetkeys\skvdefaultprefix} +\skvrobustdef*\skv@gpresetkeys[#1]#2#3{\skvgpreposetkeys[#1]{#2}{#3}{}} +\skvrobustdef*\skvpostsetkeys{\skv@testopt\skv@postsetkeys\skvdefaultprefix} +\skvrobustdef*\skv@postsetkeys[#1]#2#3{\skvpreposetkeys[#1]{#2}{}{#3}} +\skvrobustdef*\skvgpostsetkeys{\skv@testopt\skv@gpostsetkeys\skvdefaultprefix} +\skvrobustdef*\skv@gpostsetkeys[#1]#2#3{\skvgpreposetkeys[#1]{#2}{}{#3}} +\skvrobustdef*\skvpreposetkeys{\skv@clfalse\skv@testoptb\skv@preposetkeys} +\skvrobustdef*\skvgpreposetkeys{\skv@cltrue\skv@testoptb\skv@preposetkeys} +\skvrobustdef*\skv@preposetkeys#1#2{% + \skvifblankTF{#1}{}{\skv@pr@posetkeys{#1}{preset}}% + \skvifblankTF{#2}{}{\skv@pr@posetkeys{#2}{postset}}% +} +\skvrobustdef*\skv@pr@posetkeys#1#2{% + \skvifcsdefFT{\skvcurrentpath.@#2}{% + \skvnormalize{csv}{,}{#1}\skv@tempa + \ifskv@cl\expandafter\global\fi + \expandafter\def\csname\skvcurrentpath.@#2\expandafter + \endcsname\expandafter{\skv@tempa}% + }{% + % Don't change \skv@filtermergelist to \skvfiltermergelist: + \expandafter\skv@filtermergelist\csname\skv@header + .@#2\endcsname{#1}\skv@getkeyname + }% +} +% \skvremovepresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepresetkeys{% + \skv@clfalse\skv@testoptb\skv@removepresetkeys +} +% \skvgremovepresetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepresetkeys{% + \skv@cltrue\skv@testoptb\skv@removepresetkeys +} +\skvrobustdef*\skv@removepresetkeys#1{% + \skvifcsdefTF{\skvcurrentpath.@preset}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@preset\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd + }% +} +% \skvundefpresetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpresetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpresetkeys +} +\skvrobustdef*\skvgundefpresetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpresetkeys +} +\skvrobustdef*\skv@undefpresetkeys{% + \skvifcsdefFT{\skvcurrentpath.@preset}{% + \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skvcurrentpath.@preset}% + }% +} + +% \skvremovepostsetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepostsetkeys{% + \skv@clfalse\skv@testoptb\skv@removepostsetkeys +} +% \skvgremovepostsetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepostsetkeys{% + \skv@cltrue\skv@testoptb\skv@removepostsetkeys +} +\skvrobustdef*\skv@removepostsetkeys#1{% + \skvifcsdefTF{\skvcurrentpath.@postset}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@postset\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No postset keys defined for '\skvcurrentpath'}\skv@ehd + }% +} +% \skvundefpostsetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpostsetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpostsetkeys +} +\skvrobustdef*\skvgundefpostsetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpostsetkeys +} +\skvrobustdef*\skv@undefpostsetkeys{% + \skvifcsdefFT{\skvcurrentpath.@postset}{% + \skv@err{No postset keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skvcurrentpath.@postset}% + }% +} + +% \skvremovepreposetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovepreposetkeys{% + \skv@clfalse\skv@testoptb\skv@removepreposetkeys +} +% \skvgremovepreposetkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovepreposetkeys{% + \skv@cltrue\skv@testoptb\skv@removepreposetkeys +} +\skvrobustdef*\skv@removepreposetkeys#1#2{% + \skv@r@movepreposetkeys{#1}{preset}% + \skv@r@movepreposetkeys{#2}{postset}% +} +\skvrobustdef*\skv@r@movepreposetkeys#1#2{% + \skvifcsdefTF{\skvcurrentpath.@#2}{% + \expandafter\skv@filterremoveelements\csname\skv@header + .@#2\endcsname{#1}\skv@getkeyname + }{% + \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd + }% +} +% \skvundefpreposetkeys[<pref>]{<fam>} +\skvrobustdef*\skvundefpreposetkeys{% + \skv@clfalse\skv@testoptb\skv@undefpreposetkeys +} +\skvrobustdef*\skvgundefpreposetkeys{% + \skv@cltrue\skv@testoptb\skv@undefpreposetkeys +} +\skvrobustdef*\skv@undefpreposetkeys{% + \skvifcsdefFT{\skvcurrentpath.@preset}{% + \skv@err{No preset keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skvcurrentpath.@preset}% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skvcurrentpath.@postset}% + }% +} + +%% Note about the absence of \skvsavevaluekeys (save-value-keys): +% +% All keys have their values saved in the macro '<header>.@value'. +% The value can be accessed via \skvgetvalue of \skvconveyvalue. When +% needed, the value can be taken outside a local group by using \global +% on the macro that has inherited the key's value via these commands. + +% \skvneedvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvneedvaluekeys{% + \skv@clfalse\skv@testoptb\skv@needvaluekeys +} +\skvrobustdef*\skvgneedvaluekeys{% + \skv@cltrue\skv@testoptb\skv@needvaluekeys +} +\skvrobustdef*\skv@needvaluekeys#1{% + \skvcommaparse{#1}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsdef{\skv@header\skv@prova.@ndv}{}% + }% +} +% \skvremoveneedvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveneedvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removeneedvaluekeys +} +\skvrobustdef*\skvgremoveneedvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removeneedvaluekeys +} +\skvrobustdef*\skv@removeneedvaluekeys#1{% + \skvcommaparse{#1}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header\skv@prova.@ndv}% + }% +} + +% \skvforbidvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvforbidvaluekeys{% + \skv@clfalse\skv@testoptb\skv@forbidvaluekeys +} +\skvrobustdef*\skvgforbidvaluekeys{% + \skv@cltrue\skv@testoptb\skv@forbidvaluekeys +} +\skvrobustdef*\skv@forbidvaluekeys#1{% + \skvcommaparse{#1}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsdef{\skv@header\skv@prova.@fbv}{}% + }% +} +% \skvremoveforbidvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveforbidvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removeforbidvaluekeys +} +\skvrobustdef*\skvgremoveforbidvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removeforbidvaluekeys +} +\skvrobustdef*\skv@removeforbidvaluekeys#1{% + \skvcommaparse{#1}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header\skv@prova.@fbv}% + }% +} +% \skvoptionkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvoptionkeys{% + \skv@clfalse\skv@testoptb{\skv@optionkeys{}}% +} +\skvrobustdef*\skvgoptionkeys{% + \skv@cltrue\skv@testoptb{\skv@optionkeys{}}% +} +% \skvnonoptionkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvnonoptionkeys{% + \skv@clfalse\skv@testoptb{\skv@optionkeys{non}}% +} +\skvrobustdef*\skvgnonoptionkeys{% + \skv@cltrue\skv@testoptb{\skv@optionkeys{non}}% +} +\skvrobustdef*\skv@optionkeys#1#2{% + \skvcommaparse{#2}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsdef{\skv@header\skv@prova.@#1opt}{}% + }% +} +% \skvremoveoptionkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveoptionkeys{% + \skv@clfalse\skv@testoptb{\skv@removeoptionkeys{}}% +} +\skvrobustdef*\skvgremoveoptionkeys{% + \skv@cltrue\skv@testoptb{\skv@removeoptionkeys{}}% +} +% \skvremovenonoptionkeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovenonoptionkeys{% + \skv@clfalse\skv@testoptb{\skv@removenonoptionkeys{non}}% +} +\skvrobustdef*\skvgremovenonoptionkeys{% + \skv@cltrue\skv@testoptb{\skv@removenonoptionkeys{non}}% +} +\skvrobustdef*\skv@removeoptionkeys#1#2{% + \skvcommaparse{#2}\skv@prova{% + \ifskv@cl\expandafter\global\fi + \skvcsundef{\skv@header\skv@prova.@#1opt}% + }% +} +% Save the default/initial values of keys: +% \skvsaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvsaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@saveinitialvaluekeys +} +% \skvgsaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgsaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@saveinitialvaluekeys +} +\skvrobustdef*\skv@saveinitialvaluekeys#1{% + \skvifcsdefFT{\skvcurrentpath.@needini}{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize\skv@tempa + \ifskv@cl\expandafter\global\fi + \skvcsedef{\skvcurrentpath.@needini}{\skvexpandonce\skv@tempa}% + }{% + \expandafter\skv@filtermergelist\csname + \skvcurrentpath.@needini\endcsname{#1}\skv@getkeyname + }% +} +% \skvremovesaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremovesaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@removesaveinitialvaluekeys +} +% \skvgremovesaveinitialvaluekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremovesaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@removesaveinitialvaluekeys +} +\skvrobustdef*\skv@removesaveinitialvaluekeys#1{% + \skvifcsdefFT{\skvcurrentpath.@needini}{% + \skv@err{No save-initial keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \expandafter\skv@filterremoveelements\csname + \skvcurrentpath.@needini\endcsname{#1}\skv@getkeyname + }% +} +% \skvundefsaveinitialvaluekeys[<pref>]{<fam>} +\skvrobustdef*\skvundefsaveinitialvaluekeys{% + \skv@clfalse\skv@testoptb\skv@undefsaveinitialvaluekeys +} +% \skvgundefsaveinitialvaluekeys[<pref>]{<fam>} +\skvrobustdef*\skvgundefsaveinitialvaluekeys{% + \skv@cltrue\skv@testoptb\skv@undefsaveinitialvaluekeys +} +\skvrobustdef*\skv@undefsaveinitialvaluekeys{% + \skvifcsdefFT{\skvcurrentpath.@needini}{% + \skv@err{No save-initial keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \csname skv\ifskv@cl g\fi csundef\endcsname{\skvcurrentpath.@needini}% + }% +} + +% While setting keys, ignore the listed keys. +% \skvignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvignorekeys{\skv@clfalse\skv@testoptb\skv@ignorekeys} +\skvrobustdef*\skvgignorekeys{\skv@cltrue\skv@testoptb\skv@ignorekeys} +\skvrobustdef*\skv@ignorekeys#1{% + \skvifcsdefTF{\skvcurrentpath.@ignoredkeys}{% + \expandafter\skv@filtermergelist\csname + \skvcurrentpath.@ignoredkeys\endcsname{#1}\skv@getkeyname + }{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[,]\skv@tempa + \ifskv@cl\expandafter\global\fi + \skvcsedef{\skvcurrentpath.@ignoredkeys}{\skvexpandonce\skv@tempa}% + }% +} +% \skvremoveignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvremoveignorekeys{% + \skv@clfalse\skv@testoptb\skv@removeignorekeys +} +% \skvgremoveignorekeys[<pref>]{<fam>}{<keys>} +\skvrobustdef*\skvgremoveignorekeys{% + \skv@cltrue\skv@testoptb\skv@removeignorekeys +} +\skvrobustdef*\skv@removeignorekeys#1{% + \skvifcsdefFT{\skvcurrentpath.@ignoredkeys}{% + \skv@err{No ignore keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \expandafter\skv@filterremoveelements\csname + \skvcurrentpath.@ignoredkeys\endcsname{#1}\skv@getkeyname + }% +} +% \skvundefignorekeys[<pref>]{<fam>} +\skvrobustdef*\skvundefignorekeys{% + \skv@clfalse\skv@testoptb\skv@undefignorekeys +} +% \skvgundefignorekeys[<pref>]{<fam>} +\skvrobustdef*\skvgundefignorekeys{% + \skv@cltrue\skv@testoptb\skv@undefignorekeys +} +\skvrobustdef*\skv@undefignorekeys{% + \skvifcsdefFT{\skvcurrentpath.@ignoredkeys}{% + \skv@err{No ignore keys defined for '\skvcurrentpath'}\skv@ehd + }{% + \csname skv\ifskv@cl g\fi csundef\endcsname + {\skvcurrentpath.@ignoredkeys}% + }% +} +% +% Adding to the callback of existing keys. +% +% \skvaddkeycode[<pref>]{<fam>}{<key>}{<precode>}{<postcode>} +\skvrobustdef*\skvaddkeycode{\skv@testoptb\skv@addkeycode} +\skvrobustdef*\skv@addkeycode#1#2#3{% + \skv@prependkeycode{prep}{#1}{#2}% + \skv@prependkeycode{app}{#1}{#3}% +} +\skvnewlet\skvaddtocallback\skvaddkeycode +% \skvprependkeycode[<pref>]{<fam>}{<key>}{<code>} +\skvrobustdef*\skvprependkeycode{\skv@testoptb{\skv@prependkeycode{prep}}} +\skvrobustdef*\skvappendkeycode{\skv@testoptb{\skv@prependkeycode{app}}} +\skvnewlet\skvprependtocallback\skvprependkeycode +\skvnewlet\skvapendtocallback\skvappendkeycode +\skvrobustdef*\skv@prependkeycode#1#2#3{% + \edef\skvcurrentkey{\skvtrimspace{#2}}% + \skvifcsdefTF{\skv@header\skvcurrentkey.@cbk}{% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{% + \skvifstrcmpTF{#1}{prep}{% + \skvtrimspace{#3}% + \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}% + }{% + \skvexpandcsonce{\skv@header\skvcurrentkey.@cbk}% + \skvtrimspace{#3}% + }% + }% + }{% + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{\skvtrimspace{#3}}% + }% +} +\skvrobustdef*\skvsetkeys{% + \skv@viarootofsetkeystrue + \skv@testopta{\skv@testoptc\skv@setkeys@a}% +} +\skvrobustdef*\skvsetrmkeys{% + \skv@viarootofsetkeystrue + \skv@testopta{\skv@testoptc\skv@setrmkeys}% +} +\skvrobustdef*\skv@setrmkeys[#1]{% + % Only one prefix is allowed here. \skv@testoptc has created + % \skvcurrentprefix and \skv@fams. + \skvifdefTF\skv@rmkeys{% + \skvexpbracenext{\skv@setkeys@a[#1]}\skv@rmkeys + }{}% +} +% \skv@setkeys@a[<ignored.list>]{<kvlist>} +\skvrobustdef*\skv@setkeys@a[#1]#2{% + % \skvexecuteoptions calls \skv@setkeys@a. So this is the place for + % \skv@insettrue: + \skv@insettrue + % \skv@testoptc has created \skvcurrentprefix and \skv@fams. + \skvifdefboolTF{skv@intry}{}{% + \skv@getnamesofkeys{#2}\skv@currentnames + }% + % Both #1 and #2 will be normalized in \skv@setkeys@b. + \skv@setprepokeys{#1}{preset}% + \skv@setkeys@b{#2}{#1}% + \skv@setprepokeys{#1}{postset}% + \ifnum\skv@setkeysdepth=\skvz@ + \skv@insetfalse + \skv@viarootofsetkeysfalse + \fi + % Don't do \let\CurrentOption\@empty here. +} +\skvnewlet\skv@sav@setkeys@a\skv@setkeys@a +\skvrobustdef*\skv@badsetkeysinpreset[#1]#2{% + \skv@err{% + \noexpand\skvsetkeys or \noexpand\skvsetrmkeys can't be nested in a preset + \MessageBreak key's callback: this will cause a cyclic call + \MessageBreak to \noexpand\skvsetkeys or \noexpand\skvsetrmkeys + }\skv@ehd +} +\skvrobustdef*\skv@setprepokeys#1#2{% + \skv@inprepotrue + \let\skv@setkeys@a\skv@badsetkeysinpreset + % Preposet keys doesn't use \skv@fams in \skv@setkeys@d; it + % branches off to \skv@setkeys@e. Hence the following loop is in + % order. + \skvecommaloop*\skv@fams\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + \skvletcs\reserved@a{\skvcurrentpath.@#2}% + \skvifdefFT\reserved@a{}{% + \skvexpandtwoargs\skv@setkeys@b{\skvexpandonce\reserved@a}% + {\skvaddlist,\skv@currentnames\unexpanded{#1}}% + }% + }% + \let\skv@setkeys@a\skv@sav@setkeys@a + \skv@inprepofalse +} +% \skv@setkeys@b{<kvlist>}{<ignored.list>} +% +% \skv@keydepth is called in places other than by \skvsetkeys. Hence we +% need an independent \skv@setkeysdepth here. +% +\skvrobustdef*\skv@setkeys@b#1#2{% + % \skv@setprepokeys calls \skv@setkeys@b. So this is the place to + % advance \skv@setkeysdepth. + \skvgadvanceno\skv@setkeysdepth\@ne + \ifnum\skv@setkeysdepth>\skv@stackdepthlimit\relax + \skv@err{\noexpand\skvsetkeys is nested too deeply. + \MessageBreak Maybe there has been infinite reentry}\skv@ehd + \fi + % \skv@na can't be anywhere else, not in \skv@setkeys@a. + \edef\skv@na{\unexpanded{#2}}% + \skvifemptyTF\skv@na{}{\skvcsvnormalize\skv@na}% + \skvkvparse{#1}\CurrentOption{% + \skv@processvalueaslistfalse + % Expand the key name twice, in case the key and its value + % are hidden in a macro. A maverick might do just that. + \expandafter\expandafter\expandafter + \skv@setkeys@c\CurrentOption=\skv@novalue=\skv@setkeys@nil + }% + \skvgadvanceno\skv@setkeysdepth\m@ne +} +\skvrobustdef*\skv@setkeys@c#1={% + \skvxifinTF{(}{\detokenize{#1}}{% + \skvxifinTF{)}{\detokenize{#1}}{% + % A pntkey can't carry pointers: + \edef\skvcurrentkey{\unexpanded{#1}}% + \let\skv@tkey\skvcurrentkey + \skv@strippointersfromkey*\skvcurrentkey + \skv@strippointersfromvalue*\skvcurrentkey + \ifx\skvcurrentvalue\skv@tkey + \skv@extractpntkey@atsetkey{#1}% + \def\skv@ispntkey{00}% + \ifx\skvcurrentvalue\@empty + \skvnovaluetrue + \else + \skvnovaluefalse + \fi + \else + \skv@err{A parenthesized key can't have pointers}\skv@ehd + \fi + }{% + \skv@err{Key value has '(' but no ')'} + {The value of a parenthesized key must have both opening + \MessageBreak parenthesis '(' and closing ')'.}% + }% + }{% + \def\skv@ispntkey{01}% + \skv@g@tkeyname#1=\skv@getname@nil\skvcurrentkey + }% + % ##1=value: + \def\skv@prova##1=##2\skv@setkeys@nil{% + \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 <return> to proceed. + }% + \fi + }% + }% + }{% + \skvifdefboolTF{skv@st}{% + \skv@setkeys@f + }{% + \skvifcsdefTF{\skvcurrentpath.@famhandler}{% + \begingroup + \let\elt\skvexpandonce + \skvexpanded{\endgroup + \skvnoexpandcs{\skvcurrentpath.@famhandler}% + {\elt\skvcurrentprefix}{\elt\skvcurrentfamily}% + {\elt\skvcurrentkey}{\elt\skvcurrentvalue}% + }% + }{% + % \ifskv@viarootofsetkeys is true, then \skvsetkeys is nested + % in the callback of a .try key. In that case, throw the exception: + \ifskv@intry + \ifskv@viarootofsetkeys + \skv@err{Key '\skvcurrentfullkey' + \MessageBreak is undefined}\skv@ehd + \fi + \else + \skv@err{Key '\skvcurrentfullkey' + \MessageBreak is undefined}\skv@ehd + \fi + }% + }% + }% + }% + }% +} +\skvrobustdef*\skv@setkeys@e{% + \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{% + \ifskv@intry\skv@successfalse\fi + }{% + \skv@kftrue + \ifskv@intry\skv@successtrue\fi + \ifskv@inpox + \skvifcsname\skv@header\skvcurrentkey.@nonopt\then + \skv@err{Key '\skvcurrenttriple' + \MessageBreak is a non-option key but it has appeared + \MessageBreak as a class or package option}\skv@ehd + \fi + \else + \skvifcsname\skv@header\skvcurrentkey.@opt\then + \skv@err{Key '\skvcurrenttriple' + \MessageBreak is an option key but it has appeared + \MessageBreak outside class or package option list}\skv@ehd + \fi + \fi + \ifskvnovalue + \skvifcsdefTF{\skv@header\skvcurrentkey.@ndv}{% + \skv@err{Key '\skvcurrenttriple' + \MessageBreak requires a user value. + \MessageBreak Maybe it's a signal key}\skv@ehd + }{% + \skvifcsdefTF{\skv@header\skvcurrentkey.@defa}{% + \skv@getdefault\skvcurrentprefix\skvcurrentfamily + \skvcurrentkey\skvcurrentvalue + % Check if the default value is braced: + \skvexpbracenext\skvifbracedFT\skvcurrentvalue{}{% + \skvvaluebracedtrue + }% + }{% + \skv@err{No value specified for key + \MessageBreak'\skvcurrenttriple' + \MessageBreak and no default value}\skv@ehd + }% + }% + \else + \skvifcsdefTF{\skv@header\skvcurrentkey.@fbv}{% + \skv@err{Key '\skvcurrenttriple' doesn't + \MessageBreak accept a user value: key value forbidden.}\skv@ehd + }{}% + \fi + \skvifemptyTF\skvcurrentvalue{}{% + \expandafter\skv@replacepointers\expandafter{\skvcurrentvalue}% + }% + \skvletcs\skv@callback{\skv@header\skvcurrentkey.@cbk}% + \skv@getargpattern + \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{<pref>}{<fam>}{<key>}{<return.macro>} +\skvrobustdef*\skv@getdefault#1#2#3#4{% + \begingroup + \let\elt\relax + \edef\skv@prova{\elt{#1}/\elt{#2}/\elt{#3}}% + \let\elt\skvtrimspace + \edef\skv@prova{\skv@prova}% + \def\skv@g@tdefault##1##2\skv@nil{% + \edef\skv@provb{\skvremovescape{##1}}% + \@onelevel@sanitize\skv@prova + \ifx\skv@prova\skv@provb + \skvcsdef{\skv@prova}####1{\edef#4{\unexpanded{####1}}}% + \csname\skv@prova.@defa\endcsname + \else + \skv@err{The default value syntax of key + \MessageBreak '\skv@prova' doesn't conform to + \MessageBreak skeyval format. Maybe another package + \MessageBreak has abused the format} + {I can't extract the default value of key '\skv@prova'.}% + \fi + }% + % <default.macro><key.macro> + \expandafter\expandafter\expandafter\skv@g@tdefault + \csname\skv@prova.@defa\endcsname\skv@nil + \skvaftergroupdef#4\endgroup +} +% Set keys using their default values: +% \skvinitializekeys[<pref>]{<fams>}[<ignored.keys>] +\skvrobustdef*\skvinitializekeys{\skv@testoptc\skv@initializekeys} +% #1: ignored keys +\skvrobustdef*\skv@initializekeys[#1]{% + \begingroup + \def\skv@tempe{}% + % \skv@testoptc has created \skvcurrentprefix and \skv@fams. + \skvcommaloop*\skv@fams\skv@tempa{% + \skv@makeheader\skv@tempa + \skvletcs\skv@tempa{\skvcurrentpath.@inikv}% + \skvifdefFT\skv@tempa{% + \skv@err{No 'save initial value' keys defined in family + \MessageBreak'\skvcurrentpath'}\skv@ehd + }{% + % '\skvcurrentpath.@poxkeys' is the list of keys that have been + % instantiated as current pacakge options. + \skvletcs\skv@tempc{\skvcurrentpath.@poxkeys}% + \skvifdefFT\skv@tempc{% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe + \skvsetkeys[\skvcurrentprefix]{\skvcurrentfamily}% + [#1]{\skvexpandonce\skv@tempa}% + }% + }{% + % Filter pox (instantiated package/article options) list members. + % 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*+[<pref>]{<fams>}[<hp>]{<list>} +% * -> define only new keys. +% + -> define in all the given families. +% +% The only difference between \skvedefinekeys and \skvdefinekeys is the +% value of \endlinechar. +% +% Examples: +% +% \skvdefinekeys*+[KVA]{fam1,fam2}[thp@]{% +% .initialize keys after define=true, +% .save initial values, +% .prepend to every key name=X@, +% .ord/{keya,keyb}/{default-a}, +% .cmd/{keyc,keyd}/, +% .zcmd/key e/\def\cmde##1{##1}/ +% \edef\y{\detokenize\expandafter{\thp@keye}}\def\x##1{#1*key e*##1}, +% .choice/keyf/center/{center.do=\def\x##1{#1*##1},left,right}/ +% \def\f##1{#1*##1}/\@@warning{Invalid value for keyf}, +% .zbool/show center/true/\edef\cmd{\ifthp@showcenter Yes\else No\fi}/ +% \@@warning{Invalid value for show center}, +% .arg/{key1,key2}/{#1/#2}, +% .arg/key3/{#1+#2}, +% .exec/\def\x##1{##1}, +% .exec code=\def\y##1{##1}, +% } +% \skvshowcs{KVA/fam1/keyb.@defa} +% \def\keybval{xx} +% \skvsetkeys[KVA]{fam1}{keyb=.expand once{\keybval}} +% +\skvnewbools[skvdfk@]{saveinitialvalues,initialize} +% '.initialize' means set the keys with their default values after +% they've been defined. Leave this as ordinary keys, not zbool keys, +% since the key names contain dot (.). Zapping the spaces in the +% key name will not lead to a boolean of the \ifhp@key: + +\skvordkey[SKV]{definekeys}{.initialize}[true]{% + \skvifboolvalT{#1}{% + \csname skvdfk@initialize#1\endcsname + }% +} + +% Save default values as a kv list in macro <header>.@needini: +\skvordkey[SKV]{definekeys}{.save initial values}[true]{% + \skvifboolvalT{#1}{% + \csname skvdfk@saveinitialvalues#1\endcsname + }% +} +\skvordkey[SKV]{definekeys}{.parser}[,]{% + \skvifntypeTF{#1}{% + \def\skvdfk@parser{#1}% + \skvstripouterbraces{2}\skvdfk@parser + }{% + \skv@err{Parser '#1' is invalid; must be a single character}\@ehd + }% +} +\skvrobustdef*\skvsetdefinekeysparser{\def\skvdfk@parser} +\skvsetdefinekeysparser{,} +\skvrobustdef*\skvsetupdefinekeys#1{\skvsetkeys[SKV]{definekeys}{#1}} + +\skvsetupdefinekeys{.initialize=false,.save initial values=false} + +% 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{<nr>}{<toks.cmd>}{<result.cmd>} +\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[<prefixes>]{<families>}{<handler>} +% +% Note: See skeyval.sty for \skvunknownoptionhandler. +% +% Eg, for keya undefined in KV/fam: +% +% \skvunknownkeyhandler[KV]{fam}{% +% \skvordkey[#1]{#2}{#3}[#4]{\def\x##1{##1xx#4}}% +% } +% \skvsetkeys[KV]{fam}{unknownkey=val} +% +\skvrobustdef*\skvunknownkeyhandler{% + \skv@testopt\skv@unknownkeyhandler\skvdefaultprefix +} +% For each prefix and family, this will define the functions +% \<prefix>/<family>/.@famhandler of 4 parameters (prefix, family, +% key name, current value). +\skvrobustdef*\skv@unknownkeyhandler[#1]#2#3{% + \skvcommaloop{#1}\skv@tempa{% + \skv@makeprefix\skv@tempa + \skvcommaloop{#2}\skv@tempb{% + \skv@makeheader\skv@tempb + \skvcsdef{\skvcurrentpath.@famhandler}##1##2##3##4{#3}% + }% + }% +} + +%%++++++++++++++++++++++ Keys for \skvusekeys: +++++++++++++++++++++++%% +% Example: +% \skvusekeys[ +% .prefix=KVA,.families={fam1,fam2},.set in all families, +% .save rm keys +% ]{% +% key1=value1, key2=value2 +% } +\skvmakekeys[ + .prefix=SKV,.family=usekeys,.hp=skvuk@,.endlinechar=-1 +]{% + .ord/{.save rm,.save rm keys,.save unknown keys}/true/ + \edef\skvuk@saverm{\skvifstrcmpTF{#1}{true}{00}{01}} + , + .ord/{.set in all families,.in all families}/true/ + \edef\skvuk@inallfams{\skvifstrcmpTF{#1}{true}{00}{01}} + , + .ord/.prefix/\skvdefaultprefix/ + \skviflacus#1\then + \let\skvuk@pref\skvdefaultprefix + \else + \edef\skvuk@pref{#1}% + \skvstripouterbraces{2}\skvuk@pref + \fi + , + .ord/{.family,.families}/\skv@nil/ + \skviflacus#1\then + \skv@err{Key family is empty\on@line}\skv@ehd + \else + \edef\skvuk@fams{#1}% + \skvstripouterbraces{2}\skvuk@fams + \fi + , + .ord/{.path,paths}/\skv@nil/ + \skvifblankTF{#1}{% + \skv@err{Key family is empty\on@line}\skv@ehd + }{% + \edef\skv@tempa{#1}% + \ifx\skv@tempa\skv@nnil + \skv@err{No path specified for \string\skvusekeys}\skv@ehd + \else + \skvstripouterbraces{2}\skv@tempa + \skv@splitpath@a\skv@tempa\skvuk@pref\skvuk@fams + \fi + }% + , +} + +% Default settings for keys of \skvusekeys: +\skvsetkeys[SKV]{usekeys}{% + .prefix,.family,.set in all families=false,.save rm=false% +} + +\skvrobustdef*\skvusekeys{\skv@testopt\skv@usekeys{}} +\skvrobustdef*\skv@usekeys[#1]#2{% + \skvpushfunctions\skvusekeys{% + \do\skvuk@fams\do\skvuk@pref\do\skvuk@inallfams\do\skvuk@saverm + }\skv@usekeysdepth + \skvsetkeys[SKV]{usekeys}{#1}% + \ifx\skvuk@fams\skv@nnil + \skv@err{No family specified for \string\skvusekeys}\skv@ehd + \fi + \skvexpanded{% + \noexpand\skvsetkeys\if\skvuk@saverm*\fi\if\skvuk@inallfams+\fi + [\skvuk@pref]{\skvuk@fams}{\unexpanded{#2}}% + }% + \skvpopfunctions\skvusekeys\skv@usekeysdepth +} + +%++++++++++ Utilities for handlers of the macro \directkeys +++++++++++% + +\skvnewlet\skvhandlereov\relax +% Undefine \dirkeys@handler@, in case a handler function is called +% without the handler name. Handler functions usually require +% 'dirkeys@handler@<handler.name>'. +\skvundef\dirkeys@handler@ + +\skvrobustdef*\skvsetdirectkeysparser{\def\dirkeys@parser} +\skvsetdirectkeysparser{,} +\skvrobustdef*\directkeys{\skv@testst\skv@dirkeys@parse@a} +\skvrobustdef*\skv@dirkeys@parse@a{% + \begingroup + \ifskv@tempst\endlinechar\m@ne\fi + \skv@dirkeys@parse@b +} +\skvrobustdef*\skv@dirkeys@parse@b#1{% + \endgroup + \skvpushstate\skv@dirkeys@state\dirkeys@depth + \indirectkeystrue + \def\dirkeys@pathlist{}% + \def\dirkeys@holderprefixtoks{}% + \dirkeys@saveunknownkeysfalse + \skvdfk@initializefalse + \skvdfk@saveinitialvaluesfalse + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + % Normalize with respect to equality (=) and comma (,), since + % \skvkvnormalize can't normalize with respect to \dirkeys@parser + % that isn't comma (,). \skvparselist will normalize with respect + % to \dirkeys@parser: + \skvkvnormalize\skv@tempa + \def\skv@dirkeys@split##1=##2=##3\dirkeys@nil{% + \edef\skv@tempa{\unexpanded{##2}}% + \skvstripouterbraces{2}\skv@tempa + \skvdespacecontent\skv@tempa + % Get the first token, to test if it's \directkeys. If it is, then + % we have a nested \dirkeys: + \expandafter\skvifxTF\skv@car##1x\car@nil\directkeys{% + \skvifemptyTF\skv@tempa{% + ##1\relax + }{% + \skv@err{Something wrong with use of \string\dirkeys: + \MessageBreak equals ('=') found after \string\dirkeys}\skv@ehd + }% + }{% + \skv@ensurehandlerdot{##1}% + \skvifcsdefTF{dirkeys@handler@##1}{% + \csname dirkeys@handler@##1\expandafter\endcsname + \skv@tempa\skvhandlereov + }{% + \skv@err{Unknown handler '\detokenize{##1}' + \MessageBreak in command \string\directkeys, or no '=' sign + \MessageBreak after handler name}\skv@ehd + }% + }% + }% + % Normalize with respect to \dirkeys@parser: + \skvexpanded{\skvparselist*{\dirkeys@parser}}\skv@tempa\skv@tempa{% + \expandafter\skv@dirkeys@split\skv@tempa==\dirkeys@nil + }% + \indirectkeysfalse + \skvpopstate\skv@dirkeys@state\dirkeys@depth +} +\skvrobustdef*\dirkeys@setdefaultpath{% + \ifx\dirkeys@pathlist\@empty + \ifskv@inopt + \edef\dirkeys@pathlist{% + \noexpand\skv@pathdo{KV}{\@currname.\@currext}% + }% + \else + \skv@err{No key can have empty or nil path/family}\skv@ehd + \fi + \fi + \ifx\dirkeys@holderprefixtoks\@empty + \def\dirkeys@holderprefixtoks{user@}% + \fi +} +\skvnewnumbers[skv@]{pathdepth} +\skvrobustdef*\skv@pushpathdo{% + \edef\skv@pathdepth{\the\numexpr\skv@pathdepth+1}% + \skvcslet{skv@pathdo@\romannumeral\skv@pathdepth}\skv@pathdo +} +\skvrobustdef*\skv@poppathdo{% + \skvletcs\skv@pathdo{skv@pathdo@\romannumeral\skv@pathdepth}% + \edef\skv@pathdepth{\the\numexpr\skv@pathdepth-1}% +} +\skvrobustdef*\dirkeys@dodefinekeys#1#2{% + \begingroup + % #2 may contain parameter characters that may be confused with + % ##1 and ##2 below. Hence we put it in the macro \skv@tempb. + % Note: Ignored keys are omitted by \skvdefinekeys. + \edef\skv@tempb{\unexpanded{#2}}% + \def\skv@tempa{}% + \def\skv@pathdo##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \skvdefinekeys#1[##1]{##2}% + [\dirkeys@holderprefixtoks]{\skvexpandonce\skv@tempb}% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempa +} +% \dirkeys@definetypekeys{<type>}{<star.or.empty>}{<keylist>} +\skvrobustdef*\dirkeys@definetypekeys#1#2#3{% + \begingroup + \def\skv@tempa{}% + \skvexpanded{\skvparselist{\skvdfk@parser}}{#3}\skv@prova{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \ifx\skv@tempa\@empty\else\skvdfk@parser\fi + .#1/\skvexpandonce\skv@prova + }% + }% + \skvexpanded{\endgroup + \dirkeys@dodefinekeys{#2}{\skvexpandonce\skv@tempa}% + }% +} +\skvrobustdef*\dirkeys@setkeys#1#2#3#4{% + \begingroup + % #1 may contain parameter characters that may be confused with + % ##1 and ##2 below. Hence we put it in the macro \skv@tempb: + \edef\skv@tempb{\unexpanded{#4}}% + \def\skv@tempa{}% + \def\skv@pathdo##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + #3\ifdirkeys@saveunknownkeys*\else#1\fi + [##1]{##2}[\skvcsuse{##1/##2/.@ignoredkeys}]% + \skvifstrcmpTF{#2}{rm}{}{{\skvexpandonce\skv@tempb}}% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempa +} + +% This is used by prepostsetkeys, etc. +% \dirkeys@dolistedkeys@a{<key.list>}{<callback>} +\skvrobustdef*\dirkeys@dolistedkeys@a#1#2{% + \begingroup + \def\skv@tempd{}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \def\skv@tempb{}% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaparse{#1}\skv@tempa{% + \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@igkeys,}{}{% + \edef\skv@tempb{\skvaddlist,\skv@tempb\skvexpandonce\skv@tempa}% + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvifemptyTF\skv@tempb{}{% + \noexpand#2[##1]{##2}{\skvexpandonce\skv@tempb}% + }% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempd +} + +% \dirkeys@dolistedkeys@b{<callback>} +\skvrobustdef*\dirkeys@dolistedkeys@b#1{% + \begingroup + \def\skv@tempa{}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \edef\skv@tempa{% + \skvexpandonce\skv@tempa + \unexpanded{\skv@makeprefix{##1}\skv@makeheader{##2}#1}% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempa +} +\skvrobustdef*\skv@getmaintoks#1#2{% + \def\skv@prova##1#2##2#2##3\skv@nil{% + \edef\skvcurrentmaintoks{\skvtrimspace{##1}}% + \skvstripouterbraces{2}\skvcurrentmaintoks + \ifx\skvcurrentmaintoks\@empty + \skv@err{No main key in '\detokenize{#1}'}\skv@ehd + \fi + \edef\skvcurrentsub{\unexpanded{##2}}% + \ifx\skvcurrentsub\skv@novaluetoks + \def\skvcurrentsub{}% + \else\skvafterfi + \skvdespacecontent\skvcurrentsub + \skvstripouterbraces{2}\skvcurrentsub + \fi + }% + \skv@prova#1#2\skv@novalue#2\skv@nil +} + +% Pushing and popping path or holder prefix. +% +\skvnewnumbers[dirkeys@meta]{pathdepth,hpdepth} +\skvrobustdef*\dirkeys@pushmeta#1{% + \skvifcasse{#1} + case{path}{\def\@elt{pathlist}} + case{hp}{\def\@elt{holderprefixtoks}} + \elsedo + \skv@err{Unknown meta type '#1'}\skv@ehd + \endif + \skvaftercs\skvgadvanceno{dirkeys@meta#1depth}\@ne + \edef\skv@elt{\skvrom\csname dirkeys@meta#1depth\endcsname}% + \skvcsletcs{dirkeys@\@elt @meta@\skv@elt}{dirkeys@\@elt}% +} +\skvrobustdef*\dirkeys@popmeta#1{% + \skvifcase\skvifstrcmpTF{#1}% + {path}{\def\@elt{pathlist}}% + {hp}{\def\@elt{holderprefixtoks}}% + \elsedo + \skv@err{Unknown meta type '#1'}\skv@ehd + \endif + \edef\skv@elt{\skvrom\csname dirkeys@meta#1depth\endcsname}% + \skvcsletcs{dirkeys@\@elt}{dirkeys@\@elt @meta@\skv@elt}% + \skvaftercs\skvgadvanceno{dirkeys@meta#1depth}\m@ne +} + +% Assigning admissible values to keys. The handlers here are: +% +% .state pattern, .state pattern expanded, .state pattern expand once, +% .state pattern expand twice. +% +% \dirkeys@savechoice{<list>}{<expander.type>} +% +% 1. <list> has the form +% +% {<key1,...,keyn>}/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}} +% ,..., +% <keyx-n>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}} +% +% 2. If it didn't exist, the main key (eg, the above <key1>) will be +% defined with an empty default value and an empty callback. +% +\skvrobustdef*\dirkeys@savechoice#1#2{% + \begingroup + \def\skv@tempd{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\dirkeys@s@vechoice\skv@tempa{#2}% + }% + \expandafter\endgroup\skv@tempd +} +\skvrobustdef*\dirkeys@s@vechoice#1#2{% + \skv@getmaintoks{#1}{/}% + \skvkvnormalize\skvcurrentsub + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaparse*\skvcurrentmaintoks\skv@tempb{% + \skvxifinTF{,\skv@tempb,}{,\skv@igkeys,}{}{% + \skvifcsdefTF{##1/##2/\skv@tempb.@cbk}{}{% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvordkey[##1]{##2}{\skv@tempb}[]{}% + }% + }% + \def\skv@choicelist{}% + \skvcommaloop*\skvcurrentsub\skv@tempa{% + \def\skv@prova####1.do=####2.do=####3\skv@nil{% + \edef\skv@choicelist{% + \skvexpandonce\skv@choicelist + {\skvtrimspace{####1}}{\skvtrimspace{####2}}% + }% + }% + \expandafter\skv@prova\skv@tempa.do=.do=\skv@nil + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvappendkeycode[##1]{##2}{\skv@tempb}% + {\dirkeys@executechoice{#2}{########1}% + {\skvexpandonce\skv@choicelist}}% + }% + }% + }% + \dirkeys@pathlist +} +% \dirkeys@executechoice{<expansion.type>}{<curr.value>}{<choices.and.cbks>} +\skvrobustdef*\dirkeys@executechoice#1#2#3{% + \skv@getexpander{#1}% + \edef\skv@prova{\skvexpander{#2}}% + \skvdespacecontent\skv@prova + \def\do##1##2##3\skv@nil{% + \edef\skv@provb{\unexpanded{##1}}% + \skvifxTF\skv@provb\skv@prova{% + ##2% + }{% + \skvifblankTF{##3}{% + \skv@err{No choice match found for key '\skvcurrentkey'}\skv@ehd + }{% + \do##3\skv@nil + }% + }% + }% + \do#3\skv@nil +} + +% Slot/style keys represent a limited signal-slot (or subject-observer) +% concept from Java. +% +% The handlers here are: +% +% .slot, .slot value expanded, .slot expand once, .slot expand twice, +% .prepend slot, .prepend slot value expanded, .prepend slot expand once, +% .prepend slot expand twice, .append slot, .append slot value expanded, +% .append slot expand once, .append slot expand twice +% +% Note that the handlers are preceded by 'append' or 'prepend'. +% +% \dirkeys@saveslots{<list>}{<append.or.prepend>}{<expander.type>} +% +% 1. <list> has the form +% +% {<signal key1>,...,<signal key-n>}/{<slot1=val1>,...,<slot-n=val-n>} +% ,..., +% {<signal key1>,...,<signal key-n>/{<slot1=val1>,...,<slot-n=val-n>} +% +% 2. Here the slots are linked with each signal key. When the signal key is +% set, the associated slots will be set with the given values. +% +% 3. Examples: +% +% a) The following example means that keyc and keyd are signals +% (representations for the actions on their right, ie, for setting +% the slots 'keya=\someright, keyb=\keybval' when keyc or keyd is set). +% The values of keya and keyb will be expanded twice before the +% keys are set: +% +% .append slot expand twice={keyc,keyd}/{keya=\someright,keyb=\keybval}. +% +% b) In the following example, #1 will be the value of keye and/or keyf +% when they're set. The values of keye and keyf will be fully expanded +% before they're assigned to keyc and keyd: +% +% .prepend slot value expanded={keye,keyf}/{keyc=#1,keyd=#1} +% +\skvrobustdef*\dirkeys@saveslots#1#2#3{% + \begingroup + \skv@getexpander{#3}% + \def\skv@tempd{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\dirkeys@s@veslots\skv@tempa{#2}% + }% + \expandafter\endgroup\skv@tempd +} +\skvrobustdef*\dirkeys@s@veslots#1#2{% + \skv@getmaintoks{#1}{/}% + \skvcsvnormalize\skvcurrentmaintoks + \skvkvnormalize\skvcurrentsub + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaloop*\skvcurrentmaintoks\skv@signalkey{% + \skvxifinTF{,\skv@signalkey,}{,\skv@igkeys,}{}{% + \skvifcsdefTF{##1/##2/\skv@signalkey.@cbk}{}{% + % Signal keys should always be set with a value. If they were + % undefined before being designated as a signal key, then + % clearly they should have no default. The empty callback will + % later be replaced by signal emitters to the slots. + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvordkey[##1]{##2}{.need value{\skv@signalkey}}{}% + }% + }% + % Insert expanders in the sub list: + \def\skv@slotlist{}% + \skvcommaloop*\skvcurrentsub\skv@tempa{% + \skv@okvsplit\skv@tempa{% + \skvxifstrcmpTF{\skv@signalkey}{####1}{% + \skv@err{Linking key '####1' to itself}\skv@ehd + }{% + % Slot keys must always be predefined, otherwise the signal + % will be useless: + \skvifcsdefTF{##1/##2/####1.@cbk}{}{% + \skv@err{Slot key '####1' is undefined}\skv@ehd + }% + }% + \skvxifinTF{,####1,}{,\skv@igkeys,}{}{% + \edef\skv@slotlist{% + \skvaddlist,\skv@slotlist\unexpanded{####1}=% + \skvexpandonce\skvexpander{\unexpanded{####2}}% + }% + }% + }% + }% + }% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + % The slots contain value expanders \@firstofone, \unexpanded, etc. + % They will be expanded by the next \skvexpanded when the main key + % is set. + \skvifstrcmpTF{#2}{app}{\skvappendkeycode}{\skvprependkeycode}% + [##1]{##2}{\skv@signalkey}{% + % Signal keys shouldn't be preset, to avoid cyclic setting + % of keys. See note above. + \noexpand\skvifdefboolTF{skv@inprepo}{% + \noexpand\skv@signalpreseterr + }{% + % The following \skvexpanded works on expanders of slot values: + \skvexpanded{\skvsetkeys[##1]{##2}{\skvexpandonce\skv@slotlist}}% + }% + }% + }% + }% + }% + \dirkeys@pathlist +} + +%% +++++++++++++++ Linking keys: +% +% The handlers here are: +% +% .link, .link expanded, .link expand once, .link expand twice, +% .prepend link, .prepend link expanded, .prepend link expand once, +% .prepend link expand twice, +% .append link, .append link expanded, .append link expand once, +% .append link expand twice +% +% \dirkeys@savelinks{<list>}{<append.or.prepend>}{<expander.type>} +% +% 1. <list> has the form +% +% {<linkkey-1>,..,<linkkey-n>}/{<parentkey-1>,...,<parentkey-n>} +% +% 2. The parent key will be set (with the value of link key) whenever the +% link key is set. +% 3. If the link key didn't exist, it will be defined as an ordinary key +% with the default value of the parent key. In this way, setting a +% link key without value won't give rise to a 'no value and no default +% error'. +% 4. Example: Link keyb and keyc to existing keya: +% +% .link={keyb,keyc}/keya +% +% This is equivalent to: +% +% .slot={{keyb,keyc}/keya=#1} OR .style={{keyb,keyc}/keya=#1} +% +% This will link keyb and keyc to keya, such that when keyb and +% keyc are set, keya too will be set with their values. +% +\skvrobustdef*\dirkeys@savelinks#1#2#3{% + \begingroup + \def\skv@tempe{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skvexpbracenext\skv@getmaintoks\skv@tempa{/}% + \skvcsvnormalize\skvcurrentmaintoks + \def\skv@tempd{}% + \skvcommaparse*\skvcurrentsub\skv@tempb{% + \edef\skv@tempd{% + \skvaddlist,\skv@tempd\skvexpandonce\skv@tempb=####1% + }% + }% + \edef\skv@tempe{% + \skvaddlist,\skv@tempe + {\skvexpandonce\skvcurrentmaintoks}/{\skvexpandonce\skv@tempd}% + }% + }% + \expandafter\endgroup\expandafter\dirkeys@saveslots + \expandafter{\skv@tempe}{#2}{#3}% +} +% ++++++++++++ Storing values of keys: +% +% The handlers here are: +% +% .store value in, .store expanded value in, .store expanded once value in, +% .store expanded twice value in. +% +% \dirkeys@savestores{<list>}{<expander.type>} +% +% 1. <list> has the form +% +% <key-1>/<cmd-1>,...,<key-n>/<cmd-n> +% +% 2. Examples: +% +% .store expanded twice value in={keyc/\valc,keyd/\vald} +% +\skvrobustdef*\dirkeys@savestores#1#2{% + \begingroup + \skv@getexpander{#2}% + \def\skv@tempd{}% + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[/]\skv@tempa + \skvcommaparse*\skv@tempa\skv@tempa{% + \skv@oslashsplit\skv@tempa{% + \skvifstrcmpTF{##2}{^skv^}{% + \skv@err{No holder commands for key(s) '\detokenize{##1}'}\skv@ehd + }{% + \skvcommaparse{##1}\skv@tempa{% + \skvexpbracenext\dirkeys@s@vestore\skv@tempa{##2}% + }% + }% + }% + }% + \expandafter\endgroup\skv@tempd +} +\skvrobustdef*\dirkeys@s@vestore#1#2{% + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvxifinTF{,#1,}{,\skv@igkeys,}{}{% + \skvifcsdefFT{##1/##2/#1.@cbk}{% + \skv@err{Key '#1' isn't defined + \MessageBreak on path '##1/##2'}\skv@ehd + }{% + \edef\skv@tempd{% + \skvexpandonce\skv@tempd + \skvprependkeycode[##1]{##2}{#1}{% + \skvletcs\noexpand\reserved@a{##1/##2/#1.@value}% + \edef\noexpand#2{% + % Don't use \skvcurrentvalue here, since this + % changes when .setkeys is nested: + \skvexpandonce\skvexpander{\noexpand\reserved@a}% + }% + }% + }% + }% + }% + }% + \dirkeys@pathlist +} + +\skvrobustdef*\dirkeys@keyslet#1{% + \def\skv@pathdo##1##2{% + \skv@makeprefix{##1}% + \skv@makeheader{##2}% + \skv@keyslet{#1}% + }% + \dirkeys@pathlist +} + +% #1: <key-1>=<arg-1>,...,<key-n>=<arg-n> +% #2: <arg.expander.type> +% +\skvrobustdef*\dirkeys@assignarg#1#2{% + \begingroup + % Don't take #1 inside \skv@pathdo because it contains parameter + % characters: + \edef\skv@tempd{\unexpanded{#1}}% + \def\skv@tempe{}% + \skvkvnormalize\skv@tempd + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvcommaloop*\skv@tempd\skv@tempa{% + \skv@okvsplit\skv@tempa{% + \skvxifinTF{,####1,}{,\skv@igkeys,}{}{% + \skvifcsdefTF{##1/##2/####1.@cbk}{% + \skvifblankTF{####2}{}{% + \edef\skv@tempe{% + \skvexpandonce\skv@tempe\unexpanded{% + \skvcsedef{##1/##2/####1.@#2arg}{\unexpanded{####2}}% + }% + }% + }% + }{% + \skv@err{Key '####1' is undefined + \MessageBreak in family '##1/##2'}\skv@ehd + }% + }% + }% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempe +} + +% Use the default values of <keys> to set the keys. +% \dirkeys@setwithdefaults[<pref>]{<fam>}{<keys>} +\skvrobustdef*\dirkeys@setwithdefaults[#1]#2#3{% + \begingroup + \def\skv@defaultlist{}% + \skvcommaparse{#3}\skvcurrentkey{% + \skvletcs\skv@prova{#1/#2/\skvcurrentkey.@defa}% + \skvifdefTF\skv@prova{% + \skv@getdefault{#1}{#2}\skvcurrentkey\skvcurrentvalue + \edef\skv@defaultlist{% + \skvaddlist,\skv@defaultlist + \skvcurrentkey=\skvexpandonce\skvcurrentvalue + }% + }{% + \skv@err{No default value for key '\skvcurrentkey' + \MessageBreak in family '#1/#2'}\skv@ehd + }% + }% + \skvexpanded{\endgroup + \skvsetkeys*+[#1]{#2}{\skvexpandonce\skv@defaultlist}% + }% +} +\skvrobustdef*\dirkeys@setunknownkeyhandler#1{% + \begingroup + \edef\skv@tempa{\unexpanded{#1}}% + \def\skv@tempb{}% + \toks@{##1##2##3##4}% + \def\skv@pathdo##1##2{% + \skv@makeprefix{##1}% + \skv@makeheader{##2}% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb\protected + \skvcsdef{\skvcurrentpath.@famhandler}\the\toks@{% + \skvexpandonce\skv@tempa + }% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempb +} + +%%++++++++++++++++++++++++++ Try setting keys +++++++++++++++++++++++%% +% +% \skvtrysetkeys[<paths>]{<keyvals>} +% +% \directkeys{ +% .try set keys={<options>}, where +% <options> includes <paths> and key-value list (see below). +% } +% +% Try to set a given key-value list on the paths given by the independent +% <paths> of \skvtrysetkeys. If called within \directkeys (via the handler +% '.try set keys'), \skvtrysetkeys will ignore the paths that are currently +% prevailing within \directkeys. That is, within \directkeys, the handler +% '.try set keys' acts independent of local circumstances of \directkeys +% and uses its own paths from <options>. +% +% Save the known/found keys in the macro suggested by the option +% <success list macro>. The user can inspect this macro to see the found +% keys. When many families are given by the user, the success list is likely +% to be smaller than failed list. +% +% Raise an error if the number of successful families is less than +% the minimum threshold <min success>. The default value of <min success> +% is 1. The options <min success> and <lower goal> are synonymous. +% +% \skvtrysetkeys is similar to \skvsetkeys*+ (ie, \skvsetkeys with the +% options *+), but in this case we have <try> (ie, the maximum total number +% of attempts - families to search for a given key) before backing off, +% and <upper goal> (the maximum number of successful families in which a key +% should be set). +% +% \skvtrysetkeys too will set existing preset and postset keys in the +% given families. +% +% The options of \skvtrysetkeys are: +% +% .try: +% The maximum number of the sum of successful and unsuccessful +% attempts that should be made to set a key in the given SFs. The +% default value of <try> is 100. +% .upper goal/.max success: +% The max. number of search families (SF) in which a given key should +% be set/executed, if the key existed in those families. The number of +% SFs may be more than <upper goal>, but the key may not need to be +% executed in all of them. The default value of <upper goal> is 1. +% .lower goal/.min success: +% The min. number of search families (SF) in which a given key should +% be set/executed. For any key, if <hits> is less than <lower goal> +% an error will be flagged. The default of this is 0, meaning that +% by default no error message will be returned if a key isn't found +% on any of the paths. +% .path/.paths: +% Paths to search <pref1>/<fam1>,...,<pref-n>/<fam-n>. +% .prefix +% The prefix to search (default: KV). The number of prefixes can't +% be more than 1. If you have multiple prefixes, then use the +% .paths key. The given prefix will be prepended to all the given +% families. +% .families/.family +% The families to search <fam1>,...,<fam-n>. +% .ignore keys: +% Ignored keys. These keys will not be set even if they are found. +% .save known keys: +% The boolean that directs that known/found keys should be saved +% in a macro. +% .save known keys in/success list macro/success list: +% The macro into which to save known keys. Each known key is +% saved in the format <pref>/<fam>/<key>. The user can +% inspect this macro to see the found keys. The default macro is +% \skvsuccesslist. +% .save unknown keys: +% The boolean that directs that unknown keys should be saved +% in a macro. +% .save unknown keys in/fail list macro/fail list: +% The macro into which to save unknown keys. Each unknown key is +% saved in the format <pref>/<fam>/<key>. The user can +% inspect this macro to see the unknown keys. The default macro is +% \skvfaillist. +% .path exception +% The user-supplied action (code) to take when a key hasn't been found +% on the curent path of the given paths. The default path exception +% is nil. +% .hits exception +% The user-supplied action (code) to take when the number of hits is +% less than preferred minimum success. This may specify retrying on +% some other paths. The default exception is an error message. +% +% NOTES: +% +% 1. When the paths are many, the user has to be right in choosing the +% options '.try', '.upper goal' and '.lower goal', otherwise not +% enough paths will be searched. For example, if you want some 'keya' +% to be set on paths KV1/fam1, KV1/fam2, KV2/fam1, KV2/fam2, and you +% set '.try=2' or '.upper goal=2', then only 2 paths will be traversed; +% not the desired 4. + +\skvedefinekeys[SKV]{skvtryset}[skvtry@]{% + .initialize keys after define, + .ord/{.try,.max attempt,.max attempts,.total}/100/ + \skvensureinteger{.max attempt}{#1}% + \def\skvtry@maxattempts{#1}% + \skvstripouterbraces{2}\skvtry@maxattempts + \edef\skvtry@maxattempts{\number\skvtry@maxattempts}% + \ifnum\skvtry@maxattempts<\@ne + \skv@err{The max. number of attempts ('try') + \MessageBreak can't be less than 1}\skv@ehd + \fi + , + .ord/{.upper goal,.max success,.max hits}/1/ + \skvensureinteger{upper goal}{#1}% + \def\skvtry@maxsuccess{#1}% + \skvstripouterbraces{2}\skvtry@maxsuccess + \edef\skvtry@maxsuccess{\number\skvtry@maxsuccess}% + \ifnum\skvtry@maxsuccess<\@ne + \skv@err{The max. number of successful attempts + \MessageBreak ('upper goal') can't be less than 1}\skv@ehd + \fi + , + .ord/{.min success,.lower goal,.goal}/0/ + \skvensureinteger{lower goal}{#1}% + \def\skvtry@minsuccess{#1}% + \skvstripouterbraces{2}\skvtry@minsuccess + \edef\skvtry@minsuccess{\number\skvtry@minsuccess}% + \ifnum\skvtry@minsuccess<\skvz@ + \skv@err{The min. number of successful attempts + \MessageBreak ('lower goal') can't be less than 0}\skv@ehd + \fi + , + % #1=<pref1>/<fam1>,...,<pref-n>/<fam-n> + .ord/{.path,.paths,.add path,.add paths}/\skv@nil/{ + \edef\skv@tempa{#1}% + \skvstripouterbraces{2}\skv@tempa + \ifx\skv@tempa\skv@nnil\def\skv@tempa{}\fi + \ifx\skv@tempa\@empty\else + \skvcommaparse*\skv@tempa\skv@prova{% + \skv@splitpath@a\skv@prova\skv@tempa\skv@tempb + \edef\skv@prova{{\skv@tempa}{\skv@tempb}}% + \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok\skvtry@pathlist}{}{% + \edef\skvtry@pathlist{% + \skvexpandonce\skvtry@pathlist\noexpand\skvtry@pathdo\skv@prova + }% + }% + }% + \ifskv@insa + \begingroup + \def\skv@pathdo##1##2{% + \edef\skv@prova{\detokenize{{##1}{##2}}}% + \skvxifinTF{\skv@prova}{\skvoxdetok\skvtry@pathlist}{}{% + \edef\skvtry@pathlist{% + \skvexpandonce\skvtry@pathlist + \noexpand\skvtry@pathdo{##1}{##2}% + }% + }% + }% + \dirkeys@pathlist + \skvaftergroupdef\skvtry@pathlist\endgroup + \fi + \fi + }, + % Prefix is used in making \skvtry@pathlist in .family: + .ord/{.prefix}/\skvdefaultprefix/ + \skv@ifoneprefix{#1}{% + \def\skvtry@pref{#1} + \skvstripouterbraces{2}\skvtry@pref + }{% + \skv@err{Only one prefix is allowed in '.try set keys'}\skv@ehd + } + , + .ord/.prefixes// + \ifskvindef\else + \skv@err{Key '.prefixes' not defined; try '.paths'}\skv@ehd + \fi + , + .ord/{.family,.families}/\skv@nil/ + \edef\skvtry@fams{#1} + \ifskvindef\else + \ifx\skvtry@fams\skv@nnil + \skv@err{No family specified for command + \MessageBreak\string\skvtrysetkeys or handler '.try set keys'}\skv@ehd + \fi + \fi + \skvstripouterbraces{2}\skvtry@fams + \skv@makepaths\skvtry@pref\skvtry@fams\skvtry@pathlist\skvtry@pathdo + , + .ord/{.ignore keys}// + \def\skvtry@igkeys{#1} + \ifx\skvtry@igkeys\@empty\else + \skvstripouterbraces{2}\skvtry@igkeys + \fi + , + .ord/{.save known keys in,.success list,.success list macro}/ + \skvsuccesslist/ + \skviflacus#1\then + \def\skvtry@knownkeyslistmacro{\skvsuccesslist}% + \else + \def\skvtry@knownkeyslistmacro{#1} + \fi + \skvstripouterbraces{2}\skvtry@knownkeyslistmacro + , + .ord/{.save unknown keys in,.fail list,.fail list macro}/ + \skvfaillist/ + \skviflacus#1\then + \def\skvtry@unknownkeyslistmacro{\skvfaillist}% + \else + \def\skvtry@unknownkeyslistmacro{#1} + \fi + \skvstripouterbraces{2}\skvtry@unknownkeyslistmacro + , + % Exception on each path, if key isn't found: + .ord/{.if any path fails,.if a path fails,.if path fails}// + \edef\skvtry@pathexception{\unexpanded{#1}} + \skvstripouterbraces{1}\skvtry@pathexception + , + % Exception to be triggered after all paths have been searched and the key + % isn't found: + .ord/{.if hits less than target,.if hits less than goal, + .if hits are less than goal}// + \edef\skvtry@hitsexception{\unexpanded{#1}} + \skvstripouterbraces{1}\skvtry@hitsexception + , +} + +% \skvtrysetkeys[<options>]{<kvlist>} +\skvrobustdef*\skvtrysetkeys{\skv@testopt\skv@trysetkeys{}} +\skvrobustdef*\skv@trysetkeys[#1]#2{% + \skvpushfunctions\skvtrysetkeys{% + \do\skvtry@maxattempts\do\skvtry@hitsexception\do\skvtry@pathexception + \do\skvtry@minsuccess\do\skvtry@maxsuccess\do\skvtry@igkeys + \do\skvtry@knownkeyslistmacro\do\skv@tempknownkeys + \do\skvtry@unknownkeyslistmacro\do\skv@tempunknownkeys + \do\skv@triedlist\do\CurrentOption\do\skvtry@pref\do\skvtry@fams + \do\skvtry@pathlist\do\skv@pathdo\do\skvcurrentkey\do\skvcurrentpath + \do\skvcurrentprefix\do\skvcurrentfamily + }\skv@trysetkeysdepth + % Initialize \skvtry@pathlist; see the definition of the keys of + % \skvtrysetkeys above. + \def\skvtry@pathlist{}% + \skvsetkeys[SKV]{skvtryset}{#1}% + \ifx\skvtry@pathlist\@empty + \skv@err{No families in which to 'try' to set keys}\skv@ehd + \fi + % To avoid repeated computation of \skv@currentnames in \skv@setkeys@a + % for each key in the next loop: + \skv@getnamesofkeys{#2}\skv@currentnames + \expandafter\def\skvtry@knownkeyslistmacro{}% + \expandafter\def\skvtry@unknownkeyslistmacro{}% + \skvemptify{\skv@tempknownkeys,\skv@tempunknownkeys,\skv@triedlist}% + \skv@intrytrue + \skvcommaparse{#2}\CurrentOption{% + \expandafter\skv@g@tkeyname\CurrentOption=\skv@getname@nil\skvcurrentkey + \skvifemptyTF\skvtry@igkeys{% + \@firstofone + }{% + \skvxifinTF{,\skvcurrentkey,}{,\skvtry@igkeys,}{}% + }{% + % \skv@triedlist saves non-ignored list: + \edef\skv@triedlist{\skvaddlist,\skv@triedlist\skvcurrentkey}% + \skvpushfunctions\skv@trysetkeys@istate{% + \do\CurrentOption\do\skvcurrentkey\do\skv@attempts + \do\skv@hits\do\skv@failedattempts\do\ifskv@success\do\skvtry@pathdo + }\skv@trysetkeysidepth + \skv@successfalse + \skvinizero{\skv@attempts,\skv@hits,\skv@failedattempts}% + \def\skvtry@pathdo##1##2{% + \edef\skv@fams{##2}% + \skv@makeprefix{##1}% + % \ifskv@st is 'false' because we always save unknown keys while + % in .try; and the branch for \ifskv@st is never taken when + % in .try (see \skv@setkeys@d). + \skv@stfalse\skv@plfalse\skv@viarootofsetkeysfalse + \skvexpbracenext{\skv@setkeys@a[]}\CurrentOption + \edef\skv@attempts{\the\numexpr\skv@attempts+1}% + \ifskv@success + \edef\skv@hits{\the\numexpr\skv@hits+1}% + \edef\skv@tempknownkeys{% + \skvaddlist,\skv@tempknownkeys\skvcurrenttriple + }% + \else + % \skv@failedattempts is currently unused. + \edef\skv@failedattempts{\the\numexpr\skv@failedattempts+1}% + \edef\skv@tempunknownkeys{% + \skvaddlist,\skv@tempunknownkeys\skvcurrenttriple + }% + \expandafter\expandafter\expandafter\skvtry@pathexception + \fi + \ifnum\skv@hits<\skvtry@maxsuccess\relax + \ifnum\skv@attempts<\skvtry@maxattempts\relax\else + \let\skvtry@pathdo\@gobbletwo + \fi + \else + \let\skvtry@pathdo\@gobbletwo + \fi + }% + \skvtry@pathlist + \ifnum\skv@hits<\skvtry@minsuccess\relax + \ifx\skv@triedlist\@empty + \skv@err{Empty try list; all keys were probably ignored}\skv@ehd + \else + \ifx\skvtry@hitsexception\@empty + \skv@err{Success target '\skvtry@minsuccess' wasn't reached + \MessageBreak for key '\skvcurrentkey' by + \ifskv@insa'.search also set'\else + '.try set keys' or \noexpand\skvtrysetkeys\fi. + \MessageBreak Actual hits were '\skv@hits'}\skv@ehd + \else + \expandafter\expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\skvtry@hitsexception + \fi + \fi + \fi + \skvpopfunctions\skv@trysetkeys@istate\skv@trysetkeysidepth + }% + }% + \ifx\skv@triedlist\@empty + \skv@warn{Empty try list in '.try set keys'; + \MessageBreak all keys were probably ignored}\skv@ehd + \fi + \expandafter\let\skvtry@knownkeyslistmacro\skv@tempknownkeys + \expandafter\let\skvtry@unknownkeyslistmacro\skv@tempunknownkeys + \ifnum\skv@trysetkeysdepth=\@ne + \skv@intryfalse\skv@insafalse + \let\do\skvundef + \do\skv@triedlist\do\skv@attempts\do\skv@hits\do\skv@failedattempts + \do\skv@tempknownkeys\do\skv@tempunknownkeys + \fi + \skvpopfunctions\skvtrysetkeys\skv@trysetkeysdepth +} +% In \directkeys, \dirkeys@trysetkeys will be called instead of \skvtrysetkeys: +\skvrobustdef*\dirkeys@trysetkeys#1{% + \begingroup + \@tempcnta\skvz@ + \def\skv@tempc{}\def\skv@tempd{}% + % Prepare to preserve outer brace in key value: + \def\skv@tempa##1={% + \begingroup + \let\bgroup\relax + \@ifnextchar\skv@orig@bgroup{% + \endgroup\@tempswatrue\skv@tempb##1=% + }{% + \endgroup\@tempswafalse\skv@tempb##1=% + }% + }% + % Because of the need to preserve outer braces in key values, don't + % use \skv@kvsplit here. + \def\skv@tempb##1=##2=##3\skv@nil{% + \skvxifinTF{,##1,}{,.key values,.kv,.kv list,}{% + \advance\@tempcnta\@ne + \skvifnumTF\@tempcnta>\@ne{% + \skv@err{Multiple entries for key + \MessageBreak '.key values' or '.kv' or '.kv list'}\skv@ehd + }{% + \edef\skv@tempd{\skvifstrcmpTF{##2}{^skv^}{}{\unexpanded{##2}}}% + }% + }{% + \edef\skv@tempc{% + \skvaddlist,\skv@tempc##1% + \skvifstrcmpTF{##2}{^skv^}{}{% + =\if@tempswa{\fi\unexpanded{##2}\if@tempswa}\fi + }% + }% + }% + }% + \skvkvparse{#1}\skv@prova{% + \expandafter\skv@tempa\skv@prova=^skv^=\skv@nil + }% + \ifx\skv@tempd\@empty + \skv@err{No key-value pairs for handler '.try set keys'}\skv@ehd + \fi + \skvexpanded{\endgroup + \noexpand\skv@trysetkeys[\skvexpandonce\skv@tempc]% + {\skvexpandonce\skv@tempd}% + }% +} + +%%+++++++++++++++++++++++++ 'search also' set keys +++++++++++++++++++%% +% +% \skvsasetkeys[<options>]{<keyvals>} +% .sa set keys={<options>} +% +% When using '.sa set keys', the key '.key values' must be part of +% <options>. +% +% 1. The only difference between \skvsasetkeys and \skvtrysetkeys is that, +% when in \directkeys, the values of the option 'paths' of \skvsasetkeys +% are added to the current active paths from the lists +% \dirkeys@pathlist. Outside of \directkeys, the commands +% \skvsasetkeys and \skvtrysetkeys are the same. +% +% 2. All the options of \skvtrysetkeys apply to \skvsasetkeys. + +\skvnewlet\skvsasetkeys\skvtrysetkeys +\skvrobustdef*\dirkeys@sasetkeys#1{% + \skv@insatrue + \dirkeys@trysetkeys{#1}% +} +% \dirkeys@addpaths{<paths>} +% Elements of <path> will have the form '{<pref>}/{<fam>}' +\skvrobustdef*\dirkeys@addpaths#1{% + \skv@formatpaths{#1}\dirkeys@pathlist +} +% \dirkeys@removepaths{<list>} +\skvrobustdef*\dirkeys@removepaths#1{% + \begingroup + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize[,]\skv@tempa + \skvcsvnormalize[/]\skv@tempa + \def\skv@tempb{}% + \def\skv@pathdo##1##2{% + \skvxifinTF{,\detokenize{##1/##2},}{,\skvoxdetok\skv@tempa,}{}{% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skv@pathdo{##1}{##2}% + }% + }% + }% + \dirkeys@pathlist + \let\dirkeys@pathlist\skv@tempb + \skvaftergroupdef\dirkeys@pathlist\endgroup +} +% \dirkeys@removefams{<list>} +\skvrobustdef*\dirkeys@removefams#1{% + \begingroup + \edef\skv@tempa{\unexpanded{#1}}% + \skvcsvnormalize\skv@tempa + \def\skv@tempb{}% + \def\skv@pathdo##1##2{% + \skvxifinTF{,\detokenize{##2},}{,\skvoxdetok\skv@tempa,}{}{% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skv@pathdo{##1}{##2}% + }% + }% + }% + \dirkeys@pathlist + \let\dirkeys@pathlist\skv@tempb + \skvaftergroupdef\dirkeys@pathlist\endgroup +} +% \dirkeys@collectpaths{<pref.macro>}{<fam.macro>}{<ig.macro>} +% Collect current prefixes in <pref.macro>, families in <fam.macro> and +% ignored keys in <ig.macro>. +\skvrobustdef*\dirkeys@collectpaths#1#2#3{% + \begingroup + \skvemptify{#1,#2,#3}% + \def\skv@pathdo##1##2{% + \skvxifinTF{,##1,}{,#1,}{}{\edef#1{\skvaddlist,#1##1}}% + \skvxifinTF{,##2,}{,#2,}{}{\edef#2{\skvaddlist,#2##2}}% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \edef#3{\skvaddlist,#3\skv@igkeys}% + }% + \dirkeys@pathlist + \let\do\skvcmdexit + \skvexpanded{\endgroup\do#1\do#2\do#3}% +} +% Remove leading and trailing slashes in #1. +\skvrobustdef*\skv@trimslashes#1{% + \begingroup + \def\skv@tempa{% + \skvxifinFT{\skvrelax/}{\skvrelax#1}{}{% + \def\do/##1\skv@nil{\def#1{##1}}% + \expandafter\do#1\skv@nil + \skv@tempa + }% + }% + \skv@tempa + \def\skv@tempa{% + \skvxifinFT{/\skvrelax}{#1\skvrelax}{}{% + \def\do##1/\skv@nil{\def#1{##1}}% + \expandafter\do#1\skv@nil + \skv@tempa + }% + }% + \skv@tempa + \skvaftergroupdef#1\endgroup +} + +% \dirkeys@addtocodeorvalue{app.or.pre}{cbk.or.value}{<list>} +% <list> has the form <key1>={<value1>},...,<key-n>={<value-n>} +\skvrobustdef*\dirkeys@addtocodeorvalue#1#2#3{% + \begingroup + % Hid the list from the parameters of \skv@pathdo: + \edef\skv@tempa{\unexpanded{#3}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \skv@okvsplit\skv@tempa{% + \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvletcs\skv@prova{##1/##2/####1.@#2}% + \skvifdefFT\skv@prova{% + \skv@err{Key '####1' isn't in family + \MessageBreak '##1/##2'}\skv@ehd + }{% + \skvcsedef{##1/##2/####1.@#2}{% + \skvifstrcmpTF{#1}{pre}{% + \skvtrimspace{####2}\skvexpandonce\skv@prova + }{% + \skvexpandonce\skv@prova\skvtrimspace{####2}% + }% + }% + % To take the new macros outside the group: + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@#2}% + }% + }% + }% + }% + }% + }% + \dirkeys@pathlist + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@assigndefault{<kvlist>} +\skvrobustdef*\dirkeys@assigndefault#1{% + \begingroup + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#1}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@kvsplit\expandafter{\skv@tempa}{% + \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvifcsdefTF{##1/##2/####1.@cbk}{% + \skvcsedef{##1/##2/####1.@defa}{% + \skvnoexpandcs{##1/##2/####1}{\skvtrimspace{####2}}% + }% + % To take the new defaults outside the group: + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@defa}% + }% + }{% + \skv@err{Key '####1' is not in family '##1/##2'}\skv@ehd + }% + }% + }% + }% + }% + \dirkeys@pathlist + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@assignvalue{<kvlist>} +\skvrobustdef*\dirkeys@assignvalue#1{% + \begingroup + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#1}}% + \def\skv@tempb{}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \expandafter\skv@kvsplit\expandafter{\skv@tempa}{% + \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \skvifcsdefTF{##1/##2/####1.@cbk}{% + \skvcsedef{##1/##2/####1.@value}{\skvtrimspace{####2}}% + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \noexpand\skvcsexit{##1/##2/####1.@value}% + }% + }{% + \skv@err{Key '####1' isn't in family '##1/##2'}\skv@ehd + }% + }% + }% + }% + }% + \dirkeys@pathlist + \skvexpanded{\endgroup\skv@tempb}% +} + +% \dirkeys@inheritproperty{value.or.cbk.or.defa}{<list>} +\skvrobustdef*\dirkeys@inheritproperty#1#2{% + \begingroup + \def\skv@tempb{}% + % Hid the list from the parameters of \do: + \edef\skv@tempa{\unexpanded{#2}}% + % ##1=prefix, ##2=family + \def\skv@pathdo##1##2{% + \skvletcs\skv@igkeys{##1/##2/.@ignoredkeys}% + \skvifdefTF\skv@igkeys{}{\def\skv@igkeys{}}% + \skvkvparse*\skv@tempa\skv@tempa{% + \skv@oslashsplit\skv@tempa{% + % \skv@oslashsplit may return ^skv^ for ####1 or ####2, but + % this doesn't matter here, since they will show that the keys + % aren't defined. + \skvxifinTF{,\detokenize{####1},}{,\skvoxdetok\skv@igkeys,}{}{% + \ifcase\skvifcsdefTF{##1/##2/####1.@cbk}{0}{1}% + \skvifcsdefTF{##1/##2/####2.@cbk}{0}{1}\relax + \edef\skv@tempb{% + \skvexpandonce\skv@tempb + \skvcsletcs{##1/##2/####1.@#1}{##1/##2/####2.@#1}% + }% + \else + \skv@err{Key '####1' or '####2' isn't in family '##1/##2'}\skv@ehd + \fi + }% + }% + }% + }% + \dirkeys@pathlist + \expandafter\endgroup\skv@tempb +} + +% \dirkeys@setbooleanhandler{<handler>}{<boolean>}{<bool.value>} +\skvrobustdef*\dirkeys@setbooleanhandler#1#2#3{% + \skvifcsdefTF{if#2}{% + \skvifblankTF{#3}{% + \csname#2true\endcsname + }{% + \skvifboolvalTF{#3}{% + \csname#2#3\endcsname + }{% + \skv@err{Invalid value '\detokenize{#3}' for handler '#1': + \MessageBreak its expects either 'true' or 'false'}\skv@ehd + }% + }% + }{% + \skv@err{No boolean '\detokenize{#2}'}\skv@ehd + }% +} + +\skvrobustdef*\skv@ensurehandlerdot#1{% + \if.\skv@car@detok#1x\car@nil\else + \skv@err{A dot (.) must lead handler name '#1'}\skv@ehd + \fi +} +\skvrobustdef*\dirkeys@handlerloop#1{% + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + \skvkvparse*\skv@tempa +} +\skvnewdef*\dirkeys@reservedhandlers{} +\skvrobustdef*\skv@reservedhandlererr{% + \skv@err{Handler '##1' is reserved. + \MessageBreak Reserved handlers can't be redefined whatsoever}\skv@ehd +} +\skvrobustdef*\dirkeys@defhandlers@aux#1{% + \skvxifinTF{,#1,}{,\dirkeys@reservedhandlers,}{% + \skv@reservedhandlererr + }{% + \ifskv@inreservedhandler + \edef\dirkeys@reservedhandlers{% + \dirkeys@reservedhandlers + \ifx\dirkeys@reservedhandlers\@empty\else,\fi#1% + }% + \fi + \edef\skv@collection{% + \skvexpandonce\skv@collection + \protected\skvcsdef{dirkeys@handler@#1}\skvxonce\skv@param{% + % We can call \skvcurrenthandler in \skv@body, but when the + % handler is cast into another handler, the inheriting handler + % will not be the one in \skvcurrenthandler. + \def\noexpand\skvcurrenthandler{#1}% + \skvexpandonce\skv@body + }% + }% + }% +} +% \dirkeys@defhandlers{<flag>}{<kvlist>} +% +% <flag> indicates if we're in <new> (01) or <define> (00) handler. +% +% <kvlist> -> {<handlers-1>}={<defn>},...,{<handlers-n>}={<defn>} +% +\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{<flag>}{<list>} +% +% Define handlers of more than one argument. This is slightly more +% expensive than '.new handlers': +% +% <flag> indicates whether we're in <new> (01) or <define> (00) handler. +% +\skvrobustdef*\dirkeys@defnarghandlers#1#2{% + \begingroup + \def\skv@collection{}% + \def\skv@defhandlers@do##1{% + \skv@ensurehandlerdot{##1}% + \def\skv@prova####1####2\handler@nil{% + \edef\skv@param{\skvgenerateparameters{1}{####1}\skvhandlereov}% + \edef\skv@body{\unexpanded{####2}}% + \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{<flag>}{<kvlist>} +% +% 1. <flag> is either 00 (for define) or 01 (for new handler). +% 2. <kvlist> has the form +% +% {<child handler list>}={<parent handler>} +% +\skvrobustdef*\dirkeys@handlerlet#1#2{% + \begingroup + \def\skv@collection{}% + \def\skv@handlerlet@do##1##2{% + \skvifblankTF{##1}{}{% + \skvxifinTF{,##1,}{,\dirkeys@reservedhandlers,}{% + \skv@reservedhandlererr + }{% + \ifskv@inreservedhandler + \edef\dirkeys@reservedhandlers{% + \dirkeys@reservedhandlers + \ifx\dirkeys@reservedhandlers\@empty\else,\fi##1% + }% + \fi + \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={<options>} + {.try set keys,.try set,.try}={ + \dirkeys@trysetkeys{#1} + }, + % 'search also' set keys. + % .sa set keys={<options>} + {.search also set keys,.sa set keys}={ + \dirkeys@sasetkeys{#1} + }, + {.presetkeys,.preset keys}={ + \dirkeys@dolistedkeys@a{#1}\skv@presetkeys + }, + .gpreset keys={ + \dirkeys@dolistedkeys@a{#1}\skv@gpresetkeys + }, + .remove preset keys={ + \dirkeys@dolistedkeys@a{#1}\skvremovepresetkeys + }, + .gremove preset keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremovepresetkeys + }, + .undefine preset keys={ + \skv@clfalse\dirkeys@dolistedkeys@b\skv@undefpresetkeys + }, + .gundefine preset keys={ + \skv@cltrue\dirkeys@dolistedkeys@b\skv@undefpresetkeys + }, + {.postsetkeys,.postset keys}={ + \dirkeys@dolistedkeys@a{#1}\skv@postsetkeys + }, + .gpostset keys={ + \dirkeys@dolistedkeys@a{#1}\skv@gpostsetkeys + }, + .remove postset keys={ + \dirkeys@dolistedkeys@a{#1}\skvremovepostsetkeys + }, + .gremove postset keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremovepostsetkeys + }, + .undefine postset keys={ + \skv@clfalse\dirkeys@dolistedkeys@b\skv@undefpostsetkeys + }, + .gundefine postset keys={ + \skv@cltrue\dirkeys@dolistedkeys@b\skv@undefpresetkeys + }, + .option keys={ + \dirkeys@dolistedkeys@a{#1}\skvoptionkeys + }, + .goption keys={ + \dirkeys@dolistedkeys@a{#1}\skvgoptionkeys + }, + .remove option keys={ + \dirkeys@dolistedkeys@a{#1}\skvremoveoptionkeys + }, + .gremove option keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremoveoptionkeys + }, + .nonoption keys={ + \dirkeys@dolistedkeys@a{#1}\skvnonoptionkeys + }, + .gnonoption keys={ + \dirkeys@dolistedkeys@a{#1}\skvgnonoptionkeys + }, + .remove nonoption keys={ + \dirkeys@dolistedkeys@a{#1}\skvremovenonoptionkeys + }, + .gremove nonoption keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremovenonoptionkeys + }, + .need value keys={ + \dirkeys@dolistedkeys@a{#1}\skvneedvaluekeys + }, + .gneed value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgneedvaluekeys + }, + .remove need value keys={ + \dirkeys@dolistedkeys@a{#1}\skvremoveneedvaluekeys + }, + .gremove need value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremoveneedvaluekeys + }, + .forbid value keys={ + \dirkeys@dolistedkeys@a{#1}\skvforbidvaluekeys + }, + .gforbid value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgforbidvaluekeys + }, + .remove forbid value keys={ + \dirkeys@dolistedkeys@a{#1}\skvremoveforbidvaluekeys + }, + .gremove forbid value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremoveforbidvaluekeys + }, + .save initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvsaveinitialvaluekeys + }, + .gsave initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgsaveinitialvaluekeys + }, + .remove save initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvremovesaveinitialvaluekeys + }, + .gremove save initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgremovesaveinitialvaluekeys + }, + .undefine save initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvundefsaveinitialvaluekeys + }, + .gundefine save initial value keys={ + \dirkeys@dolistedkeys@a{#1}\skvgundefsaveinitialvaluekeys + }, + {.disable keys,.disable key}={ + \dirkeys@dolistedkeys@a{#1}\skvdisablekeys + }, + {.disabled key message type,.disabled key exception type}={ + \skvdisabledkeysmessagetype{#1} + }, + % Choices are always appended; never prepended, since the parent + % key must first be set before choices are executed. + % <key1>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}},..., + % <key-n>/{<ch1>.do={<act1>},...,<ch-n>.do={<act-n>}} + {.state pattern,.choice,.nominations}={ + \dirkeys@savechoice{#1}{x0} + }, + .state pattern expand once={ + \dirkeys@savechoice{#1}{x1} + }, + .state pattern expand twice={ + \dirkeys@savechoice{#1}{x2} + }, + {.state pattern expanded,.estate pattern,.enominations,.echoice}={ + \dirkeys@savechoice{#1}{xx} + }, + % {<key-i>,...,<key-j>}/{<slotlist-ij>},..., + % {<key-n>,...,<key-m>}/{<slotlist-nm>}: + {.append slot,.append slots,.slot,.slots,.style,.styles,.observers}={ + \dirkeys@saveslots{#1}{app}{x0} + }, + {.prepend slot,.prepend slots}={ + \dirkeys@saveslots{#1}{prep}{x0} + }, + {.append slot value expanded,.append slot expanded, + .append slots value expanded,.append slots expanded, + .eslot,.eslots,.estyle,.estyles}={ + \dirkeys@saveslots{#1}{app}{xx} + }, + {.prepend slot value expanded,.prepend slot expanded, + .prepend slots value expanded,.prepend slots expanded}={ + \dirkeys@saveslots{#1}{prep}{xx} + }, + {.append slot expand value once,.append slot expand once, + .append slots expand value once,.append slots expand once, + .slot expand once,.slots expand once,.style expand once, + .styles expand once}={ + \dirkeys@saveslots{#1}{app}{x1} + }, + {.prepend slot expand value once,.prepend slot expand once, + .prepend slots expand value once,.prepend slots expand once}={ + \dirkeys@saveslots{#1}{prep}{x1} + }, + {.append slot expand value twice,.append slot expand twice, + .append slots expand value twice,.append slots expand twice, + .slot expand twice,.slots expand twice,.style expand twice, + .styles expand twice}={ + \dirkeys@saveslots{#1}{app}{x2} + }, + {.prepend slot expand value twice,.prepend slot expand twice, + .prepend slots expand value twice,.prepend slots expand twice}={ + \dirkeys@saveslots{#1}{prep}{x2} + }, + % {<linklist-1>}/<mainkeylist-1>,...,{<linklist-n>}/<mainkeylist-n>: + {.append link,.append links,.link,.links}={ + \dirkeys@savelinks{#1}{app}{x0} + }, + {.prepend link,.prepend links}={ + \dirkeys@savelinks{#1}{prep}{x0} + }, + {.append link expanded value,.append link expanded, + .append links expanded value,.append links expanded,.elink,.elinks}={ + \dirkeys@savelinks{#1}{app}{xx} + }, + {.prepend link expanded value,.prepend link expanded, + .prepend links expanded value,.prepend links expanded}={ + \dirkeys@savelinks{#1}{prep}{xx} + }, + {.append link expand value once,.append link expand once, + .append links expand value once,.append links expand once, + .link expand once,.links expand once}={ + \dirkeys@savelinks{#1}{app}{x1} + }, + {.prepend link expand value once,.prepend link expand once, + .prepend links expand value once,.prepend links expand once}={ + \dirkeys@savelinks{#1}{prep}{x1} + }, + {.append link expand value twice,.append link expand twice, + .append links expand value twice,.append links expand twice, + .link expand twice,.links expand twice}={ + \dirkeys@savelinks{#1}{app}{x2} + }, + {.prepend link expand value twice,.prepend link expand twice, + .prepend links expand value twice,.prepend links expand twice}={ + \dirkeys@savelinks{#1}{prep}{x2} + }, + % .store value in={key1,key2}/<cmd>: + .store value in={ + \dirkeys@savestores{#1}{x0} + }, + {.store expanded value in,.store evalue in}={ + \dirkeys@savestores{#1}{xx} + }, + .store expanded once value in={ + \dirkeys@savestores{#1}{x1} + }, + .store expanded twice value in={ + \dirkeys@savestores{#1}{x2} + }, + {.list break,.listbreak,.break list}={ + \skvbreaklooptrue + }, + {.arg,.args}={ + \dirkeys@assignarg{#1}{x0} + }, + % Expand arguments of keys at key setting time, not at key definition: + {.arg expanded,.args expanded}={ + \dirkeys@assignarg{#1}{xx} + }, + {.arg expand once,.args expand once}={ + \dirkeys@assignarg{#1}{x1} + }, + {.arg expand twice,.args expand twice}={ + \dirkeys@assignarg{#1}{x2} + }, + {.use defaults,.set with defaults}={ + \dirkeys@dolistedkeys@a{#1}\dirkeys@setwithdefaults + }, + {.unknown key handler,.unknown option handler}={ + \dirkeys@setunknownkeyhandler{#1} + }, + % Redefine the default value macros of keys. + % .default values={keya=vala,...,keyn=valn} + {.default value,.default values,.default,.defaults}={ + \dirkeys@assigndefault{#1} + }, + % Change the current values of keys. + % .assign values={keya=vala,...,keyn=valn} + {.assign value,.assign values}={ + \dirkeys@assignvalue{#1} + }, + {.inherit,.keys let,.key let}={ + \dirkeys@keyslet{#1} + }, + % A key inherits value from a key. + % .inherit values={keya1/keya2,...,keyn1/keyn2} + {.inherit value,.inherit values}={ + \dirkeys@inheritproperty{value}{#1} + }, + {.inherit callback,.inherit callbacks,.inherit code}={ + \dirkeys@inheritproperty{cbk}{#1} + }, + {.inherit default,.inherit defaults}={ + \dirkeys@inheritproperty{defa}{#1} + }, + % .prepend values={key1=val1,...,keyn=valn} + {.prepend value,.prepend values}={ + \dirkeys@addtocodeorvalue{pre}{value}{#1} + }, + {.append value,.append values}={ + \dirkeys@addtocodeorvalue{app}{value}{#1} + }, + % .prepend code={key1=code1,...,keyn=coden} + {.prepend code,.prepend codes}={ + \dirkeys@addtocodeorvalue{pre}{cbk}{#1} + }, + {.append code,.append codes}={ + \dirkeys@addtocodeorvalue{app}{cbk}{#1} + }, + }, + .new narg handlers={ + % All the parameters would have been despaced when '.no such key' + % is called. #1=prefix, #2=family, #3=key, #4=value. + .no such key={4}{ + \skvifcsdefTF{#1@#2@handler}{ + \csname#1@#2@handler\endcsname{#1}{#2}{#3}{#4} + }{ + \skv@err{Key '#3' is not in family '#1/#2'}\skv@ehd + } + }, + .add slot={2}{ + \skvusehandler{.prepend slot}{#1} + \skvusehandler{.append slot}{#2} + }, + .add code={2}{ + \skvusehandler{.prepend code}{#1} + \skvusehandler{.append code}{#2} + }, + .add value={2}{ + \skvusehandler{.prepend value}{#1} + \skvusehandler{.append value}{#2} + }, + % .get value={<pref>}{<fam>}{<key>}<cmd> + .get value={4}{ + \skvifnamedefTF{#1/#2/#3.@value}{ + \skvifescapedTF{#4}{ + \skvletcs#4{#1/#2/#3.@value} + }{ + \skv@err{Token '\detokenize{#4}' isn't escaped}\skv@ehd + } + }{ + \skv@err{Key '#3' isn't defined or has + \MessageBreak no value in '#1/#2'}\skv@ehd + } + }, + % .set boolean handler={<handler>}{<boolean>}{<boolean.value>} + % See example use above. + .set boolean handler={3}{ + \dirkeys@setbooleanhandler{#1}{#2}{#3} + }, + }, + .handlers let reserved={ + {.disabled option message type, + .message type when disabled key is invoked, + .message type when disabled option is invoked + }=.disabled key message type, + }, +} + +% \skvpatchcmd[<prefix>]{<cmd>}{<search>}{<replace>}{<success>}{<fail>} +% +\skvrobustdef*\skvpatchcmd{% + \begingroup + \@makeother{\#}\endlinechar\m@ne + \skv@testopt\skv@patchcmd{####1}% +} +\skvrobustdef*\skv@patchcmd[#1]#2#3#4{% + \ifskv@tracingkeys + \typeout{^^J** Debugging patches: command '\string#2'}% + \fi + \skv@p@tchcmd{#1}{#2}{#3}{#4}% +} +\skvrobustdef*\skv@p@tchcmd#1#2#3#4{% + \skvifpatchableTF{#2}{#3}{#4}{% + \skv@patchdebug{++}{Command is patchable}% + \skv@patchdebug{==}{Patching has begun}% + \begingroup + \edef\skv@tempa##1##2{% + \def##1####1\detokenize{macro:}####2->####3\skvrelax{% + #1\def\string#2####2{##2####3\skvrelax}% + }% + \def##2####1\detokenize{#3}####2\skvrelax{####1\detokenize{#4}####2}% + \edef##1{##1\meaning#2\skvrelax}% + }% + \skv@tempa\skv@tempa\skv@tempb + \skv@scantoksd\endgroup\skv@tempa + \skv@patchdebug{==}{Patching completed successfully}% + \@firstoftwo + }{% + \skv@patchdebug{--}{Patching couldn't be completed}% + \@secondoftwo + }% +} +\skvrobustdef*\skv@patchdebug#1#2{% + \ifskv@tracingkeys\typeout{[debug] #1 #2.}\fi +} +\skvrobustdef*\skvifpatchableTF#1#2#3{% + \endgroup + \skvifdefFT{#1}{% + \skv@patchdebug{--}{Command not defined}% + \@secondoftwo + }{% + \skv@patchdebug{++}{Command is defined}% + \skvifmacroTF{#1}{% + \skv@patchdebug{++}{Command is a macro}% + \skvifscannableTF{#1}{% + \skv@patchdebug{++}{Macro can be retokenized safely after patching}% + \skvifpatternTF{#2}{#1}{% + \skv@patchdebug{++}{Requested search pattern found}% + \@firstoftwo + }{% + \skv@patchdebug{--}{Requested search pattern not found}% + \@secondoftwo + }% + }{% + \skv@patchdebug{--}{Nested commands or parameters}% + \@secondoftwo + }% + }{% + \skv@patchdebug{--}{Macro can't be retokenized safely after patching}% + \@secondoftwo + }% + }% +} +\skvrobustdef*\skveveryscan{% + \everyeof{\noexpand}% + \endlinechar\m@ne + \makeatletter + \catcode`\ =10 + \catcode`\\=0 + \catcode`\{\@ne + \catcode`\}\tw@ +} +\skvrobustdef*\skv@scantoksd#1#2{% + \skvexpandsecond{#1\skveveryscan\scantokens}{% + {#2}\everyeof{\the\everyeof}% + \noexpand\endlinechar\the\endlinechar\relax + \catcode`\noexpand\@=\the\catcode`\@\relax + \catcode`\noexpand\ =\the\catcode`\ \relax + \catcode`\noexpand\\=\the\catcode`\\\relax + \catcode`\noexpand\{=\the\catcode`\{\relax + \catcode`\noexpand\}=\the\catcode`\}\relax + }% +} +\skvrobustdef*\skvifscannableTF#1{% + % Fails if the content of #1 is already detokenized. + \begingroup + \edef\skv@tempa##1{% + \def##1####1\detokenize{macro}:####2->####3\skvrelax{% + ####1\def\string\skv@tempa####2{####3}% + }% + \edef##1{##1\meaning#1\skvrelax}% + }% + \skv@tempa\skv@tempa + \makeatletter\everyeof{\noexpand}\endlinechar\m@ne + \scantokens\expandafter{\skv@tempa}\relax + \expandafter\endgroup + \ifx#1\skv@tempa + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +\skvrobustdef\skvifpatternTF#1#2{% + \begingroup + \edef\skv@tempa##1{\def##1####1\detokenize{#1}####2\skvrelax}% + \skv@tempa\skv@tempa{% + \expandafter\endgroup\ifx\\##2\\% + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi + }% + \edef\skv@tempb##1{##1\detokenize{#1}\skvrelax}% + \skv@tempb{\expandafter\skv@tempa\meaning#2}% +} +\edef\skv@begindoctoks{\string\begin\string{\string\document\string}} +\skvrobustdef*\skvAtBeginEnvironment#1{% + \skvaftercs\skvappendtomacro{lps@atbegin@#1@hook}% +} +\skvrobustdef*\skvAtEndEnvironment#1{% + \skvaftercs\skvappendtomacro{lps@atend@#1@hook}% +} + +\skvpatchcmd\begin + {\csname#1\endcsname} + {\skvcsuse{lps@atbegin@#1@hook}\csname#1\endcsname} + {} + {\skv@err{Patching '\string\begin' failed!\MessageBreak + '\string\skvAtBeginEnvironment' will not work\@gobble}\@ehd + } + +\skvpatchcmd\end + {\csname end#1\endcsname} + {\skvcsuse{lps@atend@#1@hook}\csname end#1\endcsname} + {} + {\skv@err{Patching '\string\end' failed!\MessageBreak + '\string\skvAtEndEnvironment' will not work\@gobble}\@ehd + } + +\skv@core@restorecodes + +\endinput + +%% End of file 'skeyval-core.tex'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval-for.tex b/macros/latex/contrib/skeyval/tex/skeyval-for.tex new file mode 100644 index 0000000000..205c019dc6 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-for.tex @@ -0,0 +1,2862 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-for.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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\begingroup% +\let\e\endlinechar\edef\p{\the\e}\e13\edef\n{\the\catcode\e}% +\xdef\skv@for@restorecatcodes{% + \let\noexpand\skv@for@restorecatcodes\relax% + \endlinechar=\p\relax\catcode\number\e=\n\relax% + \catcode\string`\noexpand`\string=\the\catcode\string``\relax% + \catcode\string`\noexpand=\string=\the\catcode\string`=\relax% +}% +\endgroup% +\endlinechar\string=13\relax% +\catcode13\string=5\relax% +\catcode\string``\string=12\relax +\catcode\string`=\string=12\relax +\def\skvgobble#1{} +\def\do#1{\catcode`\expandafter\skvgobble\string#1=\the\catcode`#1\relax} +\edef\skv@for@restorecatcodes{% + \unexpanded\expandafter{\skv@for@restorecatcodes}% + \do\ \do\%\do\@\do\(\do\)\do\[\do\]\do\|\do\:\do\;\do\,\do\.% + \do\>\do\<\do\!\do\*\do\+\do\-\do\^\do\_\do\&\do\$\do\~\do\'% + \do\"\do\ +} +\catcode`\ =10\relax +\catcode`\@=11 \catcode`\(=12 \catcode`\)=12 \catcode`\[=12 +\catcode`\]=12 \catcode`\|=12 \catcode`\:=12 \catcode`\;=12 +\catcode`\,=12 \catcode`\*=12 \catcode`\+=12 \catcode`\-=12 +\catcode`\<=12 \catcode`\>=12 \catcode`\!=12 \catcode`\.=12 +\catcode`\^=07 \catcode`\_=08 \catcode`\&=04 \catcode`\$=03 +\catcode`\~=13 \catcode`\%=14 \catcode`\'=12 \catcode`\"=12 + +\csname skeyval-for-loaded\endcsname +\expandafter\let\csname skeyval-for-loaded\endcsname\endinput + +\ifx\ProvidesFile\@undefined + \message{File 'skeyval-for.tex' 2013/05/15 v1.3 + Loops for skeyval package (AM)} +\else + \ProvidesFile{skeyval-for.tex} + [2013/05/15 v1.3 Loops for skeyval package (AM)] + \@addtofilelist{skeyval-for.tex} +\fi + +\skvrobustdef*\skv@nostackerr{% + \skv@err{You've called an empty stack}\skv@ehd +} +% \skvsavestate<base-fn>{<fn-list>}<counter-number> +% +% See file skeyval-core.tex for \skvpushfunctions and \skvpopfunctions. +% +\skvrobustdef*\skvsavestate#1#2#3{% + \skvgadvanceno#3\@ne + \begingroup + \let\s\string\escapechar\m@ne + \def\skv@prova##1{\s##1@skv@\romannumeral#3}% + \def\do##1{\let\noexpand##1\skvnoexpandcs{\skv@prova{##1}}}% + \skvifcsdefTF{\s#1@skv@stack}{}{\skvcsdef{\s#1@skv@stack}{\nostack}}% + \skvcsxdef{\s#1@skv@stack}{#2{\skvexpandcsonce{\s#1@skv@stack}}}% + \def\do##1{\let\skvnoexpandcs{\skv@prova{##1}}\noexpand##1}% + \edef\reserved@a{\endgroup#2}\reserved@a +} +% \skvrestorestate<base-fn><counter-number> +\skvrobustdef*\skvrestorestate#1#2{% + \begingroup + \let\nostack\skv@nostackerr + \edef\skv@elt{\skvnoexpandcs{\skvremovescape#1@skv@stack}}% + \expandafter\ifx\skv@elt\relax + \skv@err{Stack of '\noexpand#1' is undefined}\skv@ehd + \fi + \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 +} +\skvnewregisters\bool{% + \ifbreakallforeachloops,\ifskvbreakloop,\ifforeachlastitem, + \ifskvprocessempty,\ifskv@foreach@swa,\ifskv@foreach@swb, + \ifskv@foreach@parenthesis,\ifskv@useemptylist,\ifskv@inforeach, +} +\skvnewlet\skvbreakloop\skvbreaklooptrue +\skvnewlet\breaknewforeach\skvbreaklooptrue +% \breakallforeachloops will break all current loops (inner and outer): +\skvnewdef*\breakallforeachloops{% + \skvbreaklooptrue\breakallforeachloopstrue +} + +\skvnewnumbers[skv@]{csvdepth,dolistdepth} +% +% \skvdolist{<parser>}{<list>}<holdercmd>{<callback>} +% \skvdolist*{<parser>}{<listcmd>}<holdercmd>{<callback>} +% \skvparselist{<parser>}{<list>}<holdercmd>{<callback>} +% \skvparselist*{<parser>}{<listcmd>}<holdercmd>{<callback>} +% \skvprocesslist{<parser>}{<list>}<holdercmd>{<callback>} +% \skvprocesslist*{<parser>}{<listcmd>}<holdercmd>{<callback>} +% +% 1. These macros can process both csv and tsv/nsv lists. Use empty +% <parser> for tsv lists. +% 2. These macros can be nested to any depth, with different parsers +% on each level. +% 3. \skvdolist and \skvparselist will not process an empty list, but +% \skvprocesslist will do so. +% 4. For comma lists, \skvcommaloop and \skvcommaparse are faster. +% See later. +% 5. For processing kv lists, use \skvparsekvlist. +% 6. The preservation of outer braces isn't a priority in this loop. +% +\skvrobustdef*\skvdolist{\skv@useemptylistfalse\skv@testst\skv@dolist} +\skvrobustdef*\skv@dolist#1#2#3#4{% + \skv@usetempst{#2}#3% + % \skvlistcount is used by listkeys. + \skvsavestate\skvdolist{% + \do\skv@dolist@callback + \do\skv@dolist@a\do\skvlistcount\do\ifskv@useemptylist + }\skv@dolistdepth + \edef\skv@dolist@callback{\unexpanded{#4}}% + \def\skvlistcount{0}% + \def\skv@dolist@a##1##2#1{% + \skvifstrcmpTF{##2}{\skv@dolist@nil}{% + % To allow nesting of loop: + \skvbreakloopfalse + }{% + \edef##1{\unexpanded{##2}}% + \edef\skvlistcount{\the\numexpr\skvlistcount+1}% + \skv@dolist@callback\relax + \ifskvbreakloop + \skvbreakloopfalse + \begingroup + \def\skv@dolist@a####1####2\skv@dolist@nil#1{% + \endgroup + \edef\skvremainder{\unexpanded{####2}}% + }% + \fi + \skv@dolist@a{##1}% + }% + }% + \ifskv@useemptylist + \def\reserved@a##1##2##3{##3}% + \else + \let\reserved@a\skvifemptyTF + \fi + \reserved@a#3{}{% + \expandafter\skv@dolist@a\expandafter#3#3#1\skv@dolist@nil#1% + }% + \skvrestorestate\skvdolist\skv@dolistdepth +} +% First normalize the list before parsing. +\skvrobustdef*\skvparselist{\skv@useemptylistfalse\skv@testst\skv@parselist} +\skvrobustdef*\skvprocesslist{\skv@useemptylisttrue\skv@testst\skv@parselist} +\skvrobustdef*\skv@parselist#1#2#3{% + \skv@usetempst{#2}#3% + \skvcsvnormalize[#1]#3% + \skv@tempsttrue + \skv@dolist{#1}#3#3% +} + +\skvnewlet\foreachlistbreaker\relax +\skvnewlet\foreachlistpauser\relax +\skvnewdef*\skv@listbreakertoks{\foreachlistbreaker} +\skvnewdef*\skv@listpausertoks{\foreachlistpauser} +\skvnewlet\skvprocessempty\skvprocessemptytrue + +% Reversing tokens: +\skvnewdef*\skvreverse#1{\skv@reverse#1\skv@reverse@stop} +\skvnewdef*\skv@reverse#1#2\skv@reverse@stop{% + \if\relax\detokenize{#2}\relax\skv@reverse@stop\fi + \skv@reverse#2\skv@reverse@stop\skvreversedo{#1}% +} +\skvnewdef*\skv@reverse@stop#1\skv@reverse@stop{\fi} +\let\skvreversedo\@iden + +\skvrobustdef*\skv@removetrailingparser#1#2{% + \skvxifinTF{\detokenize{#1}\relax}{\skvoxdetok#2\relax}{% + \def\skv@prova##1#1\skv@trim@nil{\unexpanded{##1}}% + \edef#2{\expandafter\skv@prova#2\skv@trim@nil}% + }{}% +} + +% \skvrecurse<body>\while<\if...>\fi +% +% On entry, the condition is checked before the body is executed. +% +% \@tempcnta\z@ +% \skvrecurse{ +% \advance\@tempcnta\@ne +% \@tempcntb\z@ +% \skvrecurse +% \advance\@tempcntb\@ne +% \typeout{Doing \the\@tempcnta,\the\@tempcntb} +% \while +% \ifnum\@tempcntb<3 +% \fi +% } +% \while +% \ifnum\@tempcnta<4 +% \fi +% +\skvnewcounts{skv@recurse@cnt} +\skvrobustdef\skvrecurse#1\while#2\fi{% + \skv@recurse@cnt\skvz@ + \@expandtwoargs\skv@dobody{\skvtrimspace{#2}}{\skvtrimspace{#1}}% +} +\skvrobustdef\skv@dobody#1#2{% + \advance\skv@recurse@cnt\@ne + \ifnum\skv@recurse@cnt>10000\relax + \@latex@error{Possibly an infinite loop in \string\skvrecurse} + {Maybe you didn't increment some counter or + \MessageBreak the termination condition was never satisfied.}% + \fi + #1\relax\expandafter\@iden\else\expandafter\@gobble\fi + {#2\relax\skv@dobody{#1}{#2}}% +} + +% A version of LaTeX's \@tfor that can parse undoubled parameter +% characters and terminate the loop by using \skvbreakloop. +% +% \skvtfor<holder.cmd>:={<list>}\do{<callback>} +% \skvtfor*<holder.cmd>:={<listcmd>}\do{<callback>} +% +% Example: +% +% \skvnewregister\count{\nr} +% \skvtfor\x:=a \ifx {\def\x#1{#1}} \fi\do{% +% \nr\z@ +% \skvtfor\y:=12345\do{% +% \typeout{Doing: \detokenize\expandafter{\x}--\y}% +% \advance\nr\@ne +% \ifnum\nr>\tw@\skvbreakloop\fi +% } +% } +% +\skvnewdef*\skv@tfor@nil{\skv@tfor@nil} +\skvrobustdef*\skvtfor{\skv@testst\skv@tfor} +\skvrobustdef*\skv@tfor#1:={\skv@tf@r#1 } +\skvrobustdef\skv@tf@r#1#2\do#3{% + \edef#1{\skvtrimspace{#2}}% + \edef#1{\skvifdefboolTF{skv@tempst}\skvexpandtwice\skvexpandonce{#1}}% + \skvifemptyTF#1{}{% + \skvbreakloopfalse + \expandafter\skv@tforloop#1\skv@tfor@nil\skv@tfor#1{#3}% + }% +} +\skvrobustdef\skv@tforloop#1#2\skv@tfor#3#4{% + \edef#3{\unexpanded{#1}}% + \skvifxTF#3\skv@tfor@nil{% + \skvbreakloopfalse + }{% + #4\relax + \ifskvbreakloop + \skvbreakloopfalse + \begingroup + \def\skv@tforloop##1\skv@tfor@nil\skv@tfor##2##3{% + \endgroup + \edef\skvremainder{\unexpanded{##1}}% + }% + \fi + \skv@tforloop#2\skv@tfor#3{#4}% + }% +} + +%+++++++++++++++ Non-expandable comma loops ++++++++++++++%% +% +% 1. These follow from \skvdolist and \skvparselist, but they are +% specialized for comma lists and they are faster, since they don't +% stack any state. +% 2. There is no fear of parameter characters in <callback> being mixed +% with those of \skv@commaloop@picknext. +% 3. These loops can be nested to any depth. +% 4. The preservation of outer braces isn't a priority in these loops. +% +% \skvcommaloop{<list>}<holder-cmd>{<callback>} +% \skvcommaloop*{<listcmd>}<holder-cmd>{<callback>} +% +% Example: +% +% \skvcommaloop{x#1,y#1}\tempa{\edef\xx#1{#1\tempa}} +% +\skvrobustdef*\skvcommaloop{\skv@testst\skv@commaloop} +\skvrobustdef\skv@commaloop#1#2#3{% + \skv@usetempst{#1}#2% + % Don't put \skvcsvnormalize#2 here. Use \skvcommaparse instead. + % Some callers of \skvcommaloop already have their lists normalized. + \skvifxTF#2\@empty{}{% + \skvbreakloopfalse + \skvexpandnext{\skv@commaloop@picknext#2{#3}}#2,\skv@commaloop@nil,% + }% +} +\skvrobustdef\skv@commaloop@picknext#1#2#3,{% + \skvifstrcmpTF{#3}{\skv@commaloop@nil}{% + \skvbreakloopfalse + }{% + \edef#1{\unexpanded{#3}}% + #2\relax + \ifskvbreakloop + \skvbreakloopfalse + \begingroup + \long\def\skv@commaloop@picknext##1##2##3\skv@commaloop@nil,{% + \endgroup + \edef\skvremainder{\unexpanded{##3}}% + }% + \fi + \skv@commaloop@picknext#1{#2}% + }% +} +% +% \skvecommaloop{<list>}<cmd>{<callback>} +% \skvecommaloop*{<listcmd>}<cmd>{<callback>} +% +% \skvecommaloop is as \skvcommaloop but will execute <callback> +% once when <list> is empty. This is needed for processing empty +% families. +% +\skvrobustdef\skvecommaloop{\skv@testst\skv@ecommaloop} +\skvrobustdef\skv@ecommaloop#1#2#3{% + \edef#2{\unexpanded{\skv@commaloop@picknext#2{#3}}}% + \ifskv@tempst\expandafter\expandafter\fi#2#1,\skv@commaloop@nil,% +} +% +% \skvcommaparse{<list>}<cmd>{<callback>} +% \skvcommaparse*{<listcmd>}<cmd>{<callback>} +% +% \skvcommaparse is as \skvcommaloop but will first normalize <list> +% using \skvcsvnormalize. +% +\skvrobustdef*\skvcommaparse{\skv@testst\skv@commaparse} +\skvrobustdef\skv@commaparse#1#2#3{% + \skv@usetempst{#1}#2% + \skvcsvnormalize#2% + \skvcommaloop*#2#2{#3}% +} +% \skvkvparse{<list>}<cmd>{<callback>} +% \skvkvparse*{<listcmd>}<cmd>{<callback>} +% +% 1. \skvkvparse is as \skvcommaloop but will first normalize <list> using +% \skvkvnormalize. +% 2. It is meant for processing key-value lists. +% +\skvrobustdef*\skvkvparse{\skv@testst\skv@kvparse} +\skvnewlet\skvparsekvlist\skvkvparse +\skvrobustdef\skv@kvparse#1#2#3{% + \skv@usetempst{#1}#2% + \skvkvnormalize#2% + \skvcommaloop*#2#2{#3}% +} +% \skvcountelements[<parser>]{<list>}{<return.cmd>} +% \skvcountelements*[<parser>]{<listcmd>}{<return.cmd>} +\skvrobustdef*\skvcountelements{\skv@teststopt\skv@countelements,} +\skvrobustdef*\skv@countelements[#1]#2#3{% + \begingroup + \skv@usetempst{#2}\skv@tempa + \@tempcnta\skvz@ + \skvdolist*{#1}\skv@tempa\skv@prova{% + \advance\@tempcnta\@ne + }% + \edef#3{\the\@tempcnta}% + \skvaftergroupdef#3\endgroup +} +% If the number of elements in a list is equal or greater than <nr>. +% \skvifelementcount[<parser>]{<list>}{<nr>}{<true>}{<false>} +% \skvifelementcount*[<parser>]{<listcmd>}{<nr>}{<true>}{<false>} +\skvrobustdef*\skvifelementcount{\skv@teststopt\skv@ifelementcount,} +\skvrobustdef*\skv@ifelementcount[#1]#2#3{% + \begingroup + \skv@usetempst{#2}\skv@tempa + \@tempcnta\skvz@ + \skv@swafalse + \skvdolist*{#1}\skv@tempa\skv@prova{% + \advance\@tempcnta\@ne + \ifnum\@tempcnta=#3\relax + \skv@swatrue\skvbreakloop + \fi + }% + \expandafter\endgroup\ifskv@swa + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} + +% \skv@foreach@normalize[<parser>]<listcmd> +\skvrobustdef*\skv@foreach@normalize{\skv@testopt\skv@foreach@norm@lize,} +\skvrobustdef*\skv@foreach@norm@lize[#1]#2{% + \skvxifntypeTF{\detokenize{#1}}\skvcsvnormalize\skvmacnormalize[#1]#2% +} + +% \newforeach[<options>] <holders> {<list>} {<callback>} +% \newforeach*[<options>] <holders> {<listcmd>} {<callback>} +% \newforeach[<options>] <holders> in {<list>} do {<callback>} +% \newforeach*[<options>] <holders> in {<listcmd>} do {<callback>} +% \newforeach[<options>] <holders> in {<list>} <callback> ; +% \newforeach*[<options>] <holders> in {<listcmd>} <callback> ; +% +% 1. <callback> is the code that will be executed for each element +% of the list. It will refer to the holder macros <holders>. +% +% 3. Elements of the form (a,b) and [a,b] are grabbed together. +% +% 4. Use \breaknewforeach to break out of the loop prematurely. +% +% 5. \newforeach can be nested. +% +% 6. The star (*) variant of \newforeach is equivalent to using the +% key 'list is a macro' (or *) in <options>. +% +% \skv@foreach@holderdepth must be different from \foreachnestdepth, +% to avoid confusing two different stacks. +\skvnewnumbers{foreachnestdepth,skv@foreach@holderdepth,% + skv@foreach@nra,skv@warnsubparser} +% \newforeach will not accept \skvifstreq or \skvifstrcmpTF because +% they don't start with '\if'. So we introduce \ifforeachstrcmp: +\skvnewlet\ifforeachstrcmp\skvifstreq +\skvnewdef*\skv@foreach@onlyinitially{} +\skvrobustdef*\skvforeachonlyinitially#1{% + \edef\skv@foreach@onlyinitially{% + \skvexpandonce\skv@foreach@onlyinitially + \unexpanded{#1}% + }% +} +% \skv@foreach@addtocallback{<pre.or.post>}{<code.to.add>} +\skvrobustdef*\skv@foreach@addtocallback#1#2{% + \skvcsedef{skv@foreach@#1callback}{% + \skvifcsdefFT{skv@foreach@#1callback}{}{% + \skvexpandcsonce{skv@foreach@#1callback}% + }% + % No \unexpanded for #2 here, since #2 is to be expanded: + #2% + }% +} + +\skvnewlet\skv@foreach@codemark\relax +\skvrobustdef*\skv@foreach@addtohook#1#2#3{% + \begingroup + \skvifdefTF#2{}{\def#2{}}% + \edef\x{\detokenize{\skv@foreach@codemark#3\skv@foreach@codemark}}% + \skvxifinTF\x{\skvoxdetok#2}{}{% + \edef#2{% + \skvifstrcmpTF{#1}{pre}{% + \skv@foreach@codemark\unexpanded{#3}\skv@foreach@codemark + \skvexpandonce{#2}% + }{% + \skvexpandonce{#2}% + \skv@foreach@codemark\unexpanded{#3}\skv@foreach@codemark + }% + }% + }% + \skvaftergroupdef#2\endgroup +} + +% \prependtobeginforeach[<depths>]{<code>} +\skvrobustdef*\prependtobeginforeach{% + \skv@testopt{\skv@atbeginforeach{pre}}{}% +} +% \appendtobeginforeach[<depths>]{<code>} +\skvrobustdef*\appendtobeginforeach{% + \skv@testopt{\skv@atbeginforeach{app}}{}% +} +\skvnewlet\atbeginforeach\appendtobeginforeach +\skvrobustdef*\skv@atbeginforeach#1[#2]#3{% + \skvifblankTF{#2}{% + \skvaftercsname{\skv@foreach@addtohook{#1}}% + {skv@foreach@atbeginhook@\skvrom\@ne}{#3}% + }{% + \skvcommaloop{#2}\skv@tempa{% + \skvaftercsname{\skv@foreach@addtohook{#1}}% + {skv@foreach@atbeginhook@\skvrom\number\skv@tempa}{#3}% + }% + }% +} +% \prependtoendforeach[<depths>]{<code>} +\skvrobustdef*\prependtoendforeach{% + \skv@testopt{\skv@atendforeach{pre}}{}% +} +% \appendtoendforeach[<depths>]{<code>} +\skvrobustdef*\appendtoendforeach{% + \skv@testopt{\skv@atendforeach{app}}{}% +} +\skvnewlet\atendforeach\appendtoendforeach +\skvrobustdef*\skv@atendforeach#1[#2]#3{% + \skvifblankTF{#2}{% + \skvaftercsname{\skv@foreach@addtohook{#1}}% + {skv@foreach@atendhook@\skvrom\@ne}{#3}% + }{% + \skvcommaloop{#2}\skv@tempa{% + \skvaftercsname{\skv@foreach@addtohook{#1}}% + {skv@foreach@atendhook@\skvrom\number\skv@tempa}{#3}% + }% + }% +} +% \atbeginforeachondepth{<depths>}{<code>} +\skvrobustdef*\atbeginforeachondepth#1#2{% + \appendtobeginforeach[#1]{#2}% +} +% \atendforeachondepth{<depths>}{<code>} +\skvrobustdef*\atendforeachondepth#1#2{% + \appendtoendforeach[#1]{#2}% +} + +% \skv@foreach@fi@toks must use \detokenize. Don't change to \string! +\skvnewedef*\skv@foreach@fi@toks{\detokenize{\fi}} +\skvnewedef*\skv@foreach@if@toks{\string\if} + +% \skv@foreach@ifacceptkv and \skv@foreach@maketester must come +% before the initialization of keys: +\skvrobustdef*\skv@foreach@ifacceptkv#1#2{% + \def\next{}% + \ifskvindef\else + \edef\skv@prova{\unexpanded{#1}}% + \ifnum0>\numexpr0\ifx\skv@prova\@empty-1\else + \ifx\skv@prova\@nnil-1\else+0\fi\fi\relax\relax + \skv@err{Invalid syntax '\detokenize{#1}'}\skv@ehd + \else + \edef\next{\unexpanded{#2}}% + \fi + \fi + \next +} +\skvrobustdef*\skv@foreach@ifholderisonecmd#1#2#3{% + \skvifntypeTF{#2}{% + \skvifescapedTF{#2}{% + #3% + }{% + \skv@err{Value of key '#1' must + \MessageBreak be escaped: the holder macro + \MessageBreak to be evaluated}\skv@ehd + }% + }{% + \skv@err{Value of key '#1' must + \MessageBreak be only one token: the holder + \MessageBreak macro to be evaluated}\skv@ehd + }% +} +% \skv@foreach@maketester{<cond>}<cond.tester.cmd>{<flag>} +% +% 1. Only balanced conditionals beginning with \if... and ending with \fi +% are allowed. Restricting the type of allowed conditionals is +% necessary for auto syntax checking. +% 2. <flag> is either 0 or 1, signifying whether or not the condition +% should be entered in postcallback. This is because +% \skv@foreach@maketester is called (by key 'loop stopper') for +% formating the premature exit condition and (in +% \skv@foreach@parsecountin and \skv@foreachfox@parsecountin) for the +% condition for counting an item of the list. In the latter case, +% there is no need to enter the formated +% condition in the postcallback, since it it is entered in the +% 'afterprecallback' or (in the case of \foreachfox) in 'precallback'. +% +% Examples: \ifnum\x>10\fi, \ifx\x\cmd\fi. +% +\skvrobustdef*\skv@foreach@maketester#1#2#3{% + % Make '#1' innocent in a macro, in case it contains parameter + % characters. \skv@prova will appear only in the error message. + \begingroup + \edef\skv@prova{\detokenize{#1}}% + \skvifblankTF{#1}{% + \skv@err{A blank conditional has been given in + \MessageBreak\string\newforeach or a similar command}\skv@ehd + }{% + \skvxifinTF{\skv@foreach@fi@toks\relax}{\detokenize{#1}\relax}{% + \skvxifinTF{\relax\skv@foreach@if@toks}{\relax\detokenize{#1}}{% + \edef#2{\noexpand\skvifconditionTF\unexpanded{#1}}% + \ifnum#3=\@ne + \skv@foreach@addtocallback{post}{% + \unexpanded{\skvifconditionTF#1}{\noexpand\skvbreakloop}{}% + }% + \fi + }{% + \skv@err{Invalid conditional '\skv@prova'} + {Invalid, possibly unbalanced, conditional '\skv@prova'}% + }% + }{% + \skv@err{Invalid conditional '\skv@prova'}\skv@ehd + }% + }% + \skvexpanded{\endgroup + \skvcmdexit#2% + \ifnum#3=\@ne + \skvcmdexit\skv@foreach@postcallback + \fi + }% +} +\skvquickkeys.define{skeyval/foreach}{% + .exec/ + \def\skv@foreach@parser{,} + \def\skv@foreach@subparser{/} + ; + parser/{,}/ + \edef\skv@foreach@parser{\unexpanded{#1}} + \skvstripouterbraces{2}\skv@foreach@parser + \skvxifntypeTF{\detokenize{#1}}{% + \begingroup + % It is assumed that every n-type parser and subparser + % must have catcode 12: + \skvexpandnext{\catcode`}\skv@foreach@parser=12\relax + \skvrescan\skv@foreach@parser + \skvaftergroupdef\skv@foreach@parser\endgroup + }{} + ; + subparser/{/}/ + \edef\skv@foreach@subparser{\unexpanded{#1}} + \skvstripouterbraces{2}\skv@foreach@subparser + \skvxifntypeTF{\detokenize{#1}}{ + \begingroup + \skvexpandnext{\catcode`}\skv@foreach@subparser=12\relax + \skvrescan\skv@foreach@subparser + \skvaftergroupdef\skv@foreach@subparser\endgroup + }{} + ; + terminator,callback terminator/{;}/ + \edef\skv@foreach@terminator{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@foreach@terminator + ; + % process up to = <nr> + % Maximum number of items to process: + process up to,process up to number// + \skviflacus#1\then\else + \skvensureinteger{process up to}{#1}% + \def\skv@foreach@processupto{#1}% + \skvstripouterbraces{2}\skv@foreach@processupto + \fi + ; + process empty list/true/ + \edef\skv@foreach@processempty{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + % 'break=<condition>' + % Break list processing when <condition> is satisfied. <condition> must + % be a balanced plain TeX conditional. Eg, 'exit when={\ifnum\x=20\fi}'. + % Notice the use of outer braces in '{\ifnum\x=20\fi}'. This avoids + % conflicting the two equality signs: for the key-value and for number + % testing. + .exec/\let\skv@foreach@exittester\@secondoftwo; + break,loop stopper,exit when/\if01\fi/ + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + \skvexpbracenext\skv@foreach@maketester\skv@tempa + \skv@foreach@exittester\@ne + ; + % Java-type of continue: If the user wants to ignore the callback for + % some items, he can instantiate the key 'condition=<condition>'. + % <condition> must be a balanced plain TeX conditional. Eg, + % continue={\ifnum\x=20\fi}. + .exec/\let\skv@foreach@continuetester\@secondoftwo; + continue,ignore callback/\if01\fi/ + \edef\skv@tempa{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@tempa + \skvexpbracenext\skv@foreach@maketester\skv@tempa + \skv@foreach@continuetester\skvz@ + ; + % Eg, 'evaluate = \x as \z using \numexpr\x*2'. 'as' and 'using', and the + % tokens after them, can be missing, but the formula must be complete. + evaluate/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@parseevaluate{#1}% + }% + ; + % The evaluator \numexpr will invariably be used internally for this key. + % 'as' and 'using' are illegal here. This is only a shortcut for 'evaluate'. + evaluate num,evaluate number/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@ifholderisonecmd{evaluate number}{#1}{% + \skv@foreach@parseevaluate{#1as#1using\numexpr#1}% + }% + }% + ; + % The evaluator \dimexpr will invariably be used internally for this key. + % 'as' and 'using' are illegal here. This is only a shortcut for 'evaluate'. + evaluate dim,evaluate dimension/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@ifholderisonecmd{evaluate dimension}{#1}{% + \skv@foreach@parseevaluate{#1as#1using\dimexpr#1}% + }% + }% + ; + % Eg, 'expand=\x'. + % Fully expand holder macro. This is equivalent to, eg, edef\x{\x}. + % 'as' and 'using' are illegal here. This is only a shortcut for 'evaluate'. + expand,expanded/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@ifholderisonecmd{expand}{#1}{% + \skv@foreach@parseevaluate{#1as#1using#1}% + }% + }% + ; + % Eg, 'pgf parse = \x as \xe using \x*3'. 'as' and 'using', and the + % tokens after them, can be present or missing. + % \pgfmathparse is always used for this key. + pgf evaluate,pgf parse,pgfmath parse/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@parsepgfmathparse{#1}% + }% + ; + % count in = \y all \x satisfying <condition> initially <value> + % Eg, + % count in = \p all \x satisfying \ifnum\x>4\fi, or + % count in = \p all \x satisfying \skvifnumTF\x>4 + % The counting is not additive, meaning that the counter is initialized + % to zero on each depth. + count in,count/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@parsecountin{#1}% + }% + ; + % Eg, 'remember=\x as \y initially A': + remember/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@parseremember{#1}% + }% + ; + % Eg, 'recall=\x initially A'. + % 'recall' is as 'remember', but will save the value of the holder macro + % in \<foreachlast><holder>. 'as' is invalid/illegal here, since a + % retainer macro is built internally: + retain,recall,call up/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@foreach@parserecall{#1}% + }% + ; + item counter,counter/\foreachdefaultitemcounter/ + \skvifescapedTF{#1}{% + \def\skv@foreach@itemcounter{#1}% + }{% + \skv@err{Non-escaped value '\detokenize{#1}' + \MessageBreak for key 'item counter'}\skv@ehd + }% + ; + list breaker/\foreachlistbreaker/ + \edef\skv@prova{\unexpanded{#1}}% + \ifx\skv@prova\@empty + \skv@err{Invalid empty value for key 'list breaker'}\skv@ehd + \else + \let\skv@listbreakertoks\skv@prova + \fi + ; + list pauser/\foreachlistpauser/ + \edef\skv@prova{\unexpanded{#1}}% + \ifx\skv@prova\@empty + \skv@err{Invalid empty value for key 'list pauser'}\skv@ehd + \else + \let\skv@listpausertoks\skv@prova + \fi + ; + % Remember outside the loop the holder values. Of course, this + % means remembering only the latest values. + recover holder values,recover holder values after loop/true/ + \edef\skv@foreach@recoverholders{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + % This default value of 'link' is required by \skvautocompletelist: + .exec code/\def\skv@foreach@link{\@nil}; + link/\@nil/ + \edef\skv@prova{\unexpanded{#1}}% + \ifcase0% + \ifx\skv@prova\@empty 1\fi + \ifx\skv@prova\@nnil 1\fi\relax + \edef\skv@foreach@link{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@foreach@link + \fi + ; + grow/\@nil/ + \edef\skv@prova{\unexpanded{#1}}% + \ifx\skv@prova\@nnil\else + \skvcsedef{skv@foreach@grow#1}{00} + \edef\skv@prova{\skvifstrcmpTF{#1}{left}{right}{left}}% + \skvcsedef{skv@foreach@grow\skv@prova}{01}% + \fi + /left,right + ; + .exec/ + \def\skv@foreach@growleft{01} + \def\skv@foreach@growright{01} + ; + grow left/true/ + \edef\skv@foreach@growleft{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + grow right/true/ + \edef\skv@foreach@growright{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + % Keep the decimals in rounded numbers or dimensions when list is + % auto-completed. The value value of this key is 'true'. + keep decimals/true/ + \edef\skv@foreach@keepdecimals{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + keep high decimals,keep only high decimals/true/ + \edef\skv@foreach@keephighdecimals{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + % This key means that, eg, for \x/\y, do \let\y=\x if \y has no + % user value, ie, if the user gives as an item 'a' instead of + % 'a/b' or 'a/a'. + inherit,transfer macro values/true/ + \edef\skv@foreach@inherit{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + expand before fill,expand list before fill/true/ + \edef\skv@foreach@expandbeforefill{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + expand list once,x1,list is a macro,macro list/true/ + \edef\skv@foreach@expandlistonce{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + expand list twice,x2/true/ + \edef\skv@foreach@expandlisttwice{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + % Don't use 'expand' for this key, to avoid confusing it with + % the 'expand' for holder macros: + expand list,fully expand list,expand list fully,xx/true/ + \edef\skv@foreach@expandlistfully{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + reverse list,reverse,reflect/true/ + \edef\skv@foreach@reverselist{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + % 'hide parameters' can be used to avoid doubling the parameter + % characters in the callback. + hide parameters/true/ + \edef\skv@foreach@hideparameters{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; +} + +% The following keys don't need presetting. The user must supply +% values when they are called. In internal loops, their values will +% have to be revised from those of the outer loop. +% +% link,evaluate,,evaluate num,evaluate dim,expand,count in, +% process up to,grow +% +\skvquickkeys.preset{skeyval/foreach}{% + parser,subparser,loop stopper,callback terminator,process up to, + process empty list=false,item counter,list breaker,list pauser, + recover holder values=false,grow left=false,grow right=false, + keep decimals=true,keep high decimals=false,inherit=false, + expand before fill=false,reverse list=false,expand list once=false, + expand list twice=false,expand list fully=false,hide parameters=false +} + +\skvnewdef*\skv@foreach@kvlist{} +% \setupforeach{<kvlist>} +\skvrobustdef*\setupforeach#1{% + \skvifblankTF{#1}{}{% + \skvaddtolist\skv@foreach@kvlist{#1}% + }% +} +\skvrobustdef*\skv@foreach@setkeys#1{% + \def\reserved@a{\skvquickkeys.set{skeyval/foreach}}% + \expandafter\reserved@a\expandafter{\skv@foreach@kvlist,#1}% +} + +% evaluate=<holder.macro> as <child.macro> using <formula> +% +% 1. Evaluation is done with \edef unless \pgfmathparse is present +% in <formula> or key 'pgf evaluate' is called. +% +% 2. <formula> must be a valid formula, eg, +% +% \pgfmathparse\x*2 +% \pgfmathparse{exp(50)*\x*2+sin(.5*pi)*60} +% \numexpr\x*2 +% \skvexpandonce\x +% +% or simply +% +% 'evaluate \x as \x using <empty>' +% +% The latter is equivalent to asking for full expansion. See also +% the keys 'evaluate num', 'evaluate dim', and 'expand'. +% +% 3. This allows <formula> to be generic. +% +\skvrobustdef*\skv@foreach@parseevaluate#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% + }% + \skv@foreach@swafalse + \def\skv@tempd{}% + \def\skv@tempa##1as##2using##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'evaluate' has no holder macro}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@prova}{% + \expandafter\skvifntypeTF\expandafter{\skv@prova}{}{% + \skv@err{Value of key 'evaluate': more than 1 + \MessageBreak token \skvoxdetok\skv@prova. + \MessageBreak I can't find holder macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'evaluate': + \MessageBreak holder macro \skvoxdetok\skv@prova not escaped}\skv@ehd + }% + \fi + \ifx\skv@provb\@empty + \skv@err{Value of key 'evaluate' has no child macro}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@provb}{% + \expandafter\skvifntypeTF\expandafter{\skv@provb}{}{% + \skv@err{Value of key 'evaluate': more than 1 + \MessageBreak token \skvoxdetok\skv@provb. + \MessageBreak I can't find child macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'evaluate': + \MessageBreak child \skvoxdetok\skv@provb not escaped}\skv@ehd + }% + \fi + \ifx\skv@provc\@empty + \skv@err{Value of key 'evaluate' has no formula or + \MessageBreak holder macro to be expanded}\skv@ehd + \fi + \skv@tempxifin{\pgfmathparse}{##3}{% + \def\skv@tempc####1\pgfmathparse####2\skv@nil{% + \ifx\@nnil####1\@nnil + \skv@foreach@swatrue + \else + \skv@err{Invalid token \detokenize{####1} + \MessageBreak before \string\pgfmathparse}\skv@ehd + \fi + }% + \skv@tempc##3\skv@nil + }{% + \skv@tempxifin{\numexpr}{##3}{% + \def\skv@tempd{\relax}% + }{% + \skv@tempxifin{\dimexpr}{##3}{% + \def\skv@tempd{\relax}% + }{% + \def\skv@tempd{}% + }% + }% + }% + }% + \skv@tempxifin{using}{#1}{% + \skv@tempxifin{as}{#1}{% + % 'as' and 'using' are present: maybe there is a formula. + \skv@foreach@swbtrue + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'evaluate': + \MessageBreak there is 'using', but no 'as'}\skv@ehd + }% + }{% + % No formula given. Maybe the key is 'expand' or still 'evaluate' + % but without formula. + \skv@foreach@swbfalse + \skv@tempxifin{as}{#1}{% + \def\skv@tempb##1as##2##3\skv@nil{% + \ifx\@nnil##1\@nnil + \skv@err{Value of key 'evaluate' has no holder macro}\skv@ehd + \else + \ifx\@nnil##2% + \skv@err{Value of key 'evaluate' has no child macro}\skv@ehd + \else + \skv@tempa##1as##2using##1\skv@nil + \fi + \fi + }% + \skv@tempb#1{\@nnil}\skv@nil + }{% + % No 'as' or 'using': #1 should be only the holder macro: + \ifx\@nnil#1\@nnil + \skv@err{Value of key 'evaluate' has no holder macro}\skv@ehd + \else + \skvifntypeTF{#1}{% + % Expand the holder macro: + \skv@tempa#1as#1using#1\skv@nil + }{% + \skv@err{Value of key 'evaluate': more than 1 + \MessageBreak token '\detokenize{#1}'. + \MessageBreak I can't find holder macro}\skv@ehd + }% + \fi + }% + }% + \skv@foreach@addtocallback{afterpre}{% + \ifskv@foreach@swa + % <formula> has the syntax \pgfmathparse{\x}. + \skvexpandonce\skv@provc + \let\skvexpandonce\skv@provb\noexpand\pgfmathresult + \else + % <formula> has the syntax \numexpr\x*2 or \dimexpr\x*2. + % \ifskv@foreach@swb is true when a formula has been given. + \edef\skvexpandonce\skv@provb{% + \ifskv@foreach@swb\noexpand\the\fi + \skvexpandonce\skv@provc\skv@tempd + }% + \fi + }% + \skvaftergroupdef\skv@foreach@afterprecallback\endgroup +} + +% pgfmath parse=<holder.macro> as <child.macro> using <formula> +% +% 1. Evaluation is always done with \pgfmathparse. +% +% 2. <formula> must be \pgfmathparse-able. +% +\skvrobustdef*\skv@foreach@parsepgfmathparse#1{% + \begingroup + \ifdefined\pgfmathparse\else + \skv@err{No \string\pgfmathparse; 'pgfmath' module not loaded}\skv@ehd + \fi + \def\skv@tempxifin##1##2{% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% + }% + \skv@foreach@swafalse + \def\skv@tempa##1as##2using##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'pgfmath parse' has no holder macro}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@prova}{% + \expandafter\skvifntypeTF\expandafter{\skv@prova}{}{% + \skv@err{Value of key 'pgfmath parse': more than 1 + \MessageBreak token \skvoxdetok\skv@prova. + \MessageBreak I can't find holder macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'pgfmath parse': + \MessageBreak holder macro \skvoxdetok\skv@prova not escaped}\skv@ehd + }% + \fi + \ifx\skv@provb\@empty + \skv@err{Value of key 'pgfmath parse' has no child macro}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@provb}{% + \expandafter\skvifntypeTF\expandafter{\skv@provb}{}{% + \skv@err{Value of key 'pgfmath parse': more than 1 + \MessageBreak token \skvoxdetok\skv@provb. + \MessageBreak I can't find child macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'pgfmath parse': + \MessageBreak child \skvoxdetok\skv@provb not escaped}\skv@ehd + }% + \fi + \ifx\skv@provc\@empty + \skv@err{Value of key 'pgfmath parse' has no formula or + \MessageBreak holder macro to be expanded}\skv@ehd + \fi + \skv@tempxifin{\pgfmathparse}{##3}{% + \def\skv@tempc####1\pgfmathparse####2\skv@nil{% + \ifx\@nnil####1\@nnil + \skv@foreach@swatrue + \else + \skv@err{Invalid token \detokenize{####1} + \MessageBreak before \string\pgfmathparse}\skv@ehd + \fi + }% + \expandafter\skv@tempc\skv@provc\skv@nil + }{% + \skv@tempxifin{\numexpr}{##3}{% + \skv@err{Value of key 'pgfmath parse': + \MessageBreak\noexpand\numexpr not allowed}\skv@ehd + }{% + \skv@tempxifin{\dimexpr}{##3}{% + \skv@err{Value of key 'pgfmath parse': + \MessageBreak\noexpand\dimexpr not allowed}\skv@ehd + }{% + \skv@foreach@swatrue + \edef\skv@provc{\noexpand\pgfmathparse{\skvexpandonce\skv@provc}}% + }% + }% + }% + }% + \skv@tempxifin{using}{#1}{% + \skv@tempxifin{as}{#1}{% + % 'as' and 'using' are present: maybe there is a formula. + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'pgfmath parse': + \MessageBreak there is 'using', but no 'as'}\skv@ehd + }% + }{% + \skv@tempxifin{as}{#1}{% + \def\skv@tempb##1as##2##3\skv@nil{% + \ifx\@nnil##1\@nnil + \skv@err{Value of key 'pgfmath parse' has no holder macro}\skv@ehd + \else + \ifx\@nnil##2% + \skv@err{Value of key 'pgfmath parse' has no child macro}\skv@ehd + \else + \skv@tempa##1as##2using##1\skv@nil + \fi + \fi + }% + \skv@tempb#1{\@nnil}\skv@nil + }{% + % No 'as' or 'using': #1 should be only the holder macro: + \ifx\@nnil#1\@nnil + \skv@err{Value of key 'pgfmath parse' has no holder macro}\skv@ehd + \else + \skvifntypeTF{#1}{% + % Evaluate the holder macro: + \skv@tempa#1as#1using#1\skv@nil + }{% + \skv@err{Value of key 'pgfmath parse': more than 1 + \MessageBreak token '\detokenize{#1}'. + \MessageBreak I can't find holder macro}\skv@ehd + }% + \fi + }% + }% + \skv@foreach@addtocallback{afterpre}{% + \ifskv@foreach@swa + \skvexpandonce\skv@provc + \let\skvexpandonce\skv@provb\noexpand\pgfmathresult + \fi + }% + \skvaftergroupdef\skv@foreach@afterprecallback\endgroup +} + +% count in = \y all \x satisfying <condition> initially <value> +% +% 1. <condition> must be a balanced and valid TeX conditional, like +% +% \ifnum\x>2\fi, \ifx\x\@empty\fi +% +% 2. The aim is to allow general conditions (not only those involving +% numerals) to be submitted. +% +% 3. PGF's \foreach doesn't have this feature; it has only a general +% counter, eg, +% +% '\foreach \x [count=\xi] in {a,...,e}'. +% +% Generic counting is available in \newforeach by default, without +% user request. The macros \foreachcurrentitem, \foreachitemcount, +% \foreachnextitem are always available on each nesting level. The +% boolean \ifforeachlastitem is also always available, to indicate when +% the last item of the list has been reached: +% +% \ifforeachlastitem \typeout{Processing last entry of the list} \fi +% +% 4. Counting is done from 1 (zero) unless <value> is given after +% 'initially'. +% +\skvrobustdef*\skv@foreach@parsecountin#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% + }% + \def\skv@tempa##1\skv@nil{% + \skv@tempxifin{initially}{##1}{% + \skv@tempb##1\skv@nil + }{% + \skv@tempb##1initially\skv@nil + }% + }% + \def\skv@tempb##1all##2satisfying##3initially##4\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \edef\skv@provd{\skvtrimspace{##4}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'count' has no counter macro}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@prova}{% + \expandafter\skvifntypeTF\expandafter{\skv@prova}{}{% + \skv@err{Value of key 'count in': more than 1 + \MessageBreak token \skvoxdetok\skv@prova. + \MessageBreak I can't find counter macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'count':\MessageBreak + counter macro \skvoxdetok\skv@prova not escaped}\skv@ehd + }% + \fi + \ifx\skv@provd\@empty + \expandafter\def\skv@prova{0}% + \else + \skvifintegerTF\skv@provd{% + \expandafter\let\skv@prova\skv@provd + }{% + \skv@err{Value of key 'count':\MessageBreak + initial \skvoxdetok\skv@provd not an integer}\skv@ehd + }% + \fi + \skv@foreach@swafalse + \ifx\skv@provc\@empty + % No tester but there may be a holder macro to test. The holder + % macro, on its own, isn't sufficient to conduct a test: + \let\skv@foreach@itemcounter\skv@prova + \else + \ifx\skv@provb\@empty\else + % If there isn't a holder macro or quantity to test, then no + % test is possible: + \expandafter\skvifescapedTF\expandafter{\skv@provb}{% + \expandafter\skvifntypeTF\expandafter{\skv@provb}{}{% + \skv@err{Value of key 'count in': more than 1 + \MessageBreak token \skvoxdetok\skv@provb. + \MessageBreak I can't find holder macro}\skv@ehd + }% + }{% + \skv@err{Value of key 'count':\MessageBreak + holder macro \skvoxdetok\skv@provb not escaped}\skv@ehd + }% + \skv@foreach@swatrue + % \skv@tester is the second argument of \skv@foreach@maketester. + % \skv@tester is used in pushing the item counter: + \skvexpbracenext\skv@foreach@maketester\skv@provc\skv@tester\skvz@ + \fi + \fi + }% + \skv@tempxifin{satisfying}{#1}{% + \skv@tempxifin{all}{#1}{% + % 'all' and 'satisfying' are present: + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'count': + \MessageBreak there is 'satisfying', but no 'all'}\skv@ehd + }% + }{% + \skv@tempxifin{all}{#1}{% + \skv@tempa#1satisfying\skv@nil + }{% + % No 'all' or 'satisfying': #1 should be only the counter macro: + \ifx\@nnil#1\@nnil + \skv@err{Value of key 'count' has no counter macro}\skv@ehd + \else + \skvifntypeTF{#1}{% + \skv@tempa#1all satisfying\skv@nil + }{% + \skv@err{Value of key 'count in': more than 1 + \MessageBreak token '\detokenize{#1}'. + \MessageBreak I can't find counter macro}\skv@ehd + }% + \fi + }% + }% + \ifskv@foreach@swa + \skv@foreach@addtocallback{afterpre}{% + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \def\skvexpandonce\skv@prova{\number\skv@prova}% + \noexpand\fi + \skvexpandonce\skv@tester{% + \noexpand\skvpushnumber\skvexpandonce\skv@prova + }{}% + }% + \fi + \skvexpanded{\endgroup + \ifskv@foreach@swa + \skvcmdexit\skv@foreach@afterprecallback + \else + \skvcmdexit\skv@foreach@itemcounter + \fi + }% +} + +% remember=<holder.macro> as <child.macro> initially <value> +% +\skvrobustdef*\skv@foreach@parseremember#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% + }% + \skv@tempxifin{(}{#1}{% + \skv@err{Value of key 'remember': '(' found. + \MessageBreak I don't accept this PGF format. + \MessageBreak Remove the parenthesis '(<your input>)'}\skv@ehd + }{}% + \def\skv@tempa##1as##2initially##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \skv@foreach@swafalse + \ifx\skv@prova\@empty + \skv@err{Value of key 'remember' has no holder macro: + \MessageBreak i.e., no macro that holds input}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@prova}{}{% + \skv@err{Value of key 'remember': + \MessageBreak Holder macro \skvoxdetok\skv@prova not escaped}\skv@ehd + }% + \fi + \ifx\skv@provb\@empty + \skv@err{Value of key 'remember' has no child macro; + \MessageBreak i.e., no macro after 'as'}\skv@ehd + \else + \expandafter\skvifescapedTF\expandafter{\skv@provb}{% + \skv@foreach@swatrue + }{% + \skv@err{Value of key 'remember': + \MessageBreak Child \skvoxdetok\skv@provb not escaped}\skv@ehd + }% + \fi + }% + \skv@tempxifin{initially}{#1}{% + \skv@tempxifin{as}{#1}{% + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'remember': + \MessageBreak There is 'initially', but no 'as'}\skv@ehd + }% + }{% + \skv@tempxifin{as}{#1}{% + \skv@tempa#1initially\skv@nil + }{% + % No 'as' and 'initially': + \edef\skv@prova{\skvtrimspace{#1}}% + \ifx\skv@prova\@empty\else + % Go and raise error in \skv@tempa: + \skv@tempa#1as initially\skv@nil + \fi + }% + }% + \ifskv@foreach@swa + % Initially: + \skv@foreach@addtocallback{pre}{% + % Don't remove this \ifnum, otherwise the initial assignment + % will be overwritten on subsequent returns: + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \edef\skvexpandonce\skv@provb{% + \noexpand\unexpanded{\skvexpandonce\skv@provc}% + }% + \noexpand\fi + }% + \skv@foreach@addtocallback{post}{% + \let\skvexpandonce\skv@provb\skvexpandonce\skv@prova + }% + \fi + \skvexpanded{\endgroup + \ifskv@foreach@swa + \skvcmdexit\skv@foreach@precallback + \skvcmdexit\skv@foreach@postcallback + \fi + }% +} + +% recall=<holder.macro> initially <value>. +% +% This builds a retainer macro of the form \<foreachlast><holder.name>. +% +% 'as' is invalid/illegal here, since a retainer macro is built internally. +% +\skvrobustdef*\skv@foreach@parserecall#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% + }% + \skv@tempxifin{(}{#1}{% + \skv@err{Value of key 'recall': '(' found. + \MessageBreak I don't accept this PGF format. + \MessageBreak remove the parenthesis '()'}\skv@ehd + }{}% + \skv@foreach@swafalse + \def\skv@tempa##1initially##2\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'recall' has no holder macro: + \MessageBreak i.e., no macro that holds input}\skv@ehd + \else + \expandafter\skvifntypeTF\expandafter{\skv@prova}{% + \expandafter\skvifescapedTF\expandafter{\skv@prova}{% + \edef\skv@elt{\expandafter\skvremovescape\skv@prova}% + \skv@foreach@swatrue + % Initially: + \skv@foreach@addtocallback{pre}{% + % Don't remove this \ifnum, otherwise the initial assignment + % will be overwritten on subsequent returns: + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \skvcsedef{foreachlast\skv@elt}{% + \noexpand\unexpanded{\skvexpandonce\skv@provb}% + }% + \noexpand\fi + }% + \skv@foreach@addtocallback{post}{% + \skvcslet{foreachlast\skv@elt}\skvexpandonce\skv@prova + }% + }{% + \skv@err{Value of key 'recall': + \MessageBreak \detokenize{#1} is not escaped}\skv@ehd + }% + }{% + \skv@err{Value of key 'recall': + \MessageBreak \detokenize{#1} is not one token}\skv@ehd + }% + \fi + }% + \skv@tempxifin{as}{#1}{% + \skv@err{Value of key 'recall': invalid 'as' found. + \MessageBreak This key doesn't expect 'as' since + \MessageBreak it automatically builds a retainer macro + \MessageBreak of the form \<foreachlast><holder.name>}\skv@ehd + }{% + \skv@tempxifin{initially}{#1}{% + \skv@tempa#1\skv@nil + }{% + \ifx\@nnil#1\@nnil\else + \skv@tempa#1initially\skv@nil + \fi + }% + }% + \skvexpanded{\endgroup + \ifskv@foreach@swa + \skvcmdexit\skv@foreach@precallback + \skvcmdexit\skv@foreach@postcallback + \fi + }% +} +% \skv@foreach@doreverselist{<parser>}{<listcmd>} +\skvnewdef*\skv@foreach@doreverselist#1#2{% + \begingroup + \def\skv@tempb{}% + \def\skv@tempa##1#1##2\skv@reverse@stop{% + \if\relax\detokenize{##2}\relax\skv@reverse@stop\fi + \skv@tempa##2\skv@reverse@stop + \edef\skv@tempb{% + \skvexpandonce\skv@tempb\ifx\skv@tempb\@empty\else#1\fi + \unexpanded{##1}% + }% + }% + \def\skv@reverse@stop##1\skv@reverse@stop{\fi}% + \expandafter\skv@tempa#2#1\skv@reverse@stop + \let#2\skv@tempb + \skvaftergroupdef#2\endgroup +} + +% Eg, \skv@dots@in<value>\skv@nil...\skv@nnil\skv@stop +\skvnewdef*\skv@dots@in#1...#2#3\skv@stop{% + \ifx\skv@nnil#2\in@false\else\in@true\fi +} + +\skvrobustdef*\skv@foreach@latehookerr{% + \def\skv@foreach@latehook{% + \begingroup + \let\s\string + \skv@err{A \noexpand\newforeach hook (\s\prependtobeginforeach, + \MessageBreak\s\appendtobeginforeach, \s\atbeginforeach, + \MessageBreak\s\prependtoendforeach, \s\appendtoendforeach, + \MessageBreak or \s\atendforeach) has been illegally + \MessageBreak issued within \s\newforeach} + {\s\newforeach\space hook command shouldn't be issued + within \s\newforeach.}% + \endgroup + }% + \skvletmanytocmd{% + \prependtobeginforeach,\appendtobeginforeach,\atbeginforeach, + \prependtoendforeach,\appendtoendforeach,\atendforeach + }\skv@foreach@latehook +} + +\skvrobustdef*\skv@foreach@pushstate{% + \skvsavestate\skv@foreach@stack{% + \do\skv@foreach@starform\do\skv@foreach@parser + \do\skv@foreach@subparser\do\skv@foreach@listisamacro + \do\skv@foreach@processupto\do\skv@foreach@processempty + \do\skv@foreach@exittester\do\skv@foreach@continuetester + \do\skv@foreach@grow\do\skv@foreach@growleft\do\skv@foreach@growright + \do\skv@foreach@terminator\do\skv@foreach@recoverholders + \do\skv@foreach@normalizelist\do\skv@foreach@callback + \do\skv@foreach@hideparameters\do\skv@foreach@callback@hiddenparameters + \do\skv@foreach@auxcallback\do\skv@foreach@precallback + \do\skv@foreach@afterprecallback\do\skv@foreach@postcallback + \do\skv@foreach@onlyinitially + \do\foreachlistremainder\do\skv@foreach@mainloop + \do\skv@foreach@getnext\do\skv@foreach@picknext\do\skv@foreach@getrm + \do\skv@foreach@do@a\do\skv@foreach@do@b + % Macros '...lookahead...' need stacking in \foreachfox and when + % \newforeach is nested with \foreachfox: + \do\skv@foreach@lookahead@a\do\skv@foreach@lookahead@b + \do\skv@foreach@lookahead@c\do\skv@foreach@lookahead@d + \do\skv@foreach@paramlist\do\skv@foreach@holderlist + \do\skv@foreach@userlist\do\skv@foreach@inherit + \do\skv@foreach@keepdecimals\do\skv@foreach@keephighdecimals + \do\skv@foreach@itemcounter\do\skv@foreach@link + \do\skv@foreach@expandbeforefill\do\skv@foreach@expandlistonce + \do\skv@foreach@expandlisttwice\do\skv@foreach@expandlistfully + \do\skv@listbreakertoks\do\skv@listpausertoks + \do\skv@foreach@reverselist\do\foreachcurrentitem + \do\foreachnextitem\do\foreachitemcount\do\foreachprevitem + % The following commands are disabled on entry into the loop. + % Hence they need to be stacked here: + \do\prependtobeginforeach\do\appendtobeginforeach\do\atbeginforeach + \do\prependtoendforeach\do\appendtoendforeach\do\atendforeach + }\foreachnestdepth +} +\skvrobustdef*\skv@foreach@popstate{% + \skvrestorestate\skv@foreach@stack\foreachnestdepth +} +\skvnewlet\skv@foreach@nil\relax +\skvnewdef*\skv@foreach@nnil{\skv@foreach@nil} +\skvrobustdef*\newforeach{\skv@testst\skv@newforeach@beginprescan} +\skvrobustdef*\skv@newforeach@beginprescan{% + \skv@inforeachtrue + \edef\skv@foreach@starform{0\ifskv@tempst0\else1\fi}% + \begingroup + \def\skv@foreach@holderlist{}% + \def\skv@tempa{% + \skvexpanded{\endgroup + \noexpand\skv@foreach@endprescan + {\skvexpandonce\skv@foreach@holderlist}% + }% + }% + \let\bgroup\relax + \skvifnextchar[{% + \endgroup\skv@foreach@scanarg + }{% + \skvifnextchar i{% + \endgroup + \skv@err{\string\newforeach: no holder macro seen}\skv@ehd + }{% + \skvifnextchar\skv@orig@bgroup{% + \endgroup + \skv@err{\string\newforeach: no holder macro seen}\skv@ehd + }{% + \skv@foreach@collectholders + }% + }% + }% +} +\skvrobustdef*\skv@foreach@collectholders#1{% + \edef\skv@foreach@holderlist{% + \skvexpandonce\skv@foreach@holderlist\skvtrimspace{#1}% + }% + \skvifnextchar[{% + \skv@tempa + }{% + \skvifnextchar i{% + \skv@tempa[]% + }{% + \skvifnextchar\skv@orig@bgroup{% + \skv@tempa[]% + }{% + \skv@foreach@collectholders + }% + }% + }% +} +\skvrobustdef*\skv@foreach@endprescan#1[#2]{% + \skvifblankTF{#1}{% + \skv@err{\string\newforeach: no holder macros seen}\skv@ehd + }{% + \skv@foreach@scanarg[#2]#1% + }% +} +\skvrobustdef*\skv@foreach@scanarg{\skv@testopt\skv@foreach@scanarg@a{}} +\skvrobustdef*\skv@foreach@scanarg@a[#1]{% + \skv@foreach@pushstate + \skv@foreach@latehookerr + % \foreachitemcount must be a macro, not a counter, because it is stacked: + \def\foreachitemcount{0}% + % Don't confuse macros of various depths; reset them here: + \def\do##1{\def##1{}}% + \do\skv@foreach@itemcounter\do\skv@foreach@processupto + \do\skv@foreach@callback\do\skv@foreach@auxcallback + \do\skv@foreach@callback@hiddenparameters + \do\skv@foreach@precallback\do\skv@foreach@afterprecallback + \do\skv@foreach@postcallback\do\skv@foreach@onlyinitially + % The keys have to be set here, to determine if the list is a macro, etc. + % Since \skv@foreach@precallback are initialized in each nest, the + % keys have to be reset here. Reset all keys: they might have been + % fixed by the outer loop and the inner loop might assume the default + % values of the keys are those being used. + \skv@foreach@setkeys{#1}% + % We've set the keys; let's overrule \skv@foreach@expandlistonce if + % we had seen a star suffix. Don't overrule \skv@foreach@expandlisttwice + % and \skv@foreach@expandlistfully. + \if\skv@foreach@starform + \def\skv@foreach@expandlistonce{00}% + \fi + \ifcase0% + % Take only one case: + \if\skv@foreach@expandlistonce\else + \if\skv@foreach@expandlisttwice\else + \if\skv@foreach@expandlistfully\else 1\fi\fi\fi\relax + \def\skv@foreach@listisamacro{00}% + \else + \def\skv@foreach@listisamacro{01}% + \fi + \begingroup + % Collect item holders. Don't confuse explicit \bgroup for left brace: + \skv@cnta\skvz@ + \def\skv@foreach@holderlist{}% + \let\bgroup\relax + \skvifnextchar i\skv@foreach@scanarg@c{% + \skvifnextchar\skv@orig@bgroup + \skv@foreach@scanarg@d\skv@foreach@scanarg@b + }% +} +% #1: one holer macro or subparser. +\skvrobustdef*\skv@foreach@scanarg@b#1{% + % Odd items must be subparser: + \ifodd\skv@cnta + \ifnum\skv@cnta=\@ne + \skvifescapedTF{#1}{% + \skv@err{Token '\noexpand#1' cannot be a subparser; + \MessageBreak it seems to be a holder macro. I expected + \MessageBreak the subparser to be the token here.}\skv@ehd + }{% + \edef\skv@prova{\skvtrimspace{#1}}% + \let\elt\skvexpandonce + \let\skv@elt\skv@foreach@subparser + \if\elt\skv@prova\elt\skv@elt\else + % Don't stack \skv@warnsubparser, so that the following + % warning isn't called more than once in the inner loop. + \ifnum\skv@warnsubparser=\skvz@ + \skvgadvanceno\skv@warnsubparser\@ne + \skv@warn{Scanned subparser '\elt\skv@prova' is different from + \MessageBreak the default or user-supplied subparser '\elt\skv@elt'. + \MessageBreak I have used '\elt\skv@prova', but please submit + \MessageBreak '\elt\skv@prova' as the value of the key + 'subparser' + \MessageBreak so that I can stop giving this warning. + }% + \fi + \let\skv@foreach@subparser\skv@prova + \fi + }% + \fi + \else + \skvifescapedTF{#1}{}{% + \skv@err{Token '\noexpand#1' is not escaped + \MessageBreak and can't be a holder macro}\skv@ehd + }% + \fi + \advance\skv@cnta\@ne + \edef\skv@foreach@holderlist{% + \skvexpandonce\skv@foreach@holderlist\skvtrimspace{#1}% + }% + \skvifnextchar i\skv@foreach@scanarg@c{% + \skvifnextchar\skv@orig@bgroup + \skv@foreach@scanarg@d\skv@foreach@scanarg@b + }% +} +\skvrobustdef*\skv@foreach@scanarg@c#1#2{% + \skvifstrcmpTF{#1#2}{in}{% + \skv@foreach@scanarg@d + }{% + \skv@err{I have seen 'i' but no expected 'in'}\skv@ehd + }% +} +\skvrobustdef*\skv@foreach@scanarg@d{% + \ifx\skv@foreach@holderlist\@empty + \skv@err{No item holder macros before 'in' + \MessageBreak or before left brace}\skv@ehd + \fi + \skvifnextchar\skv@orig@bgroup{% + \skv@foreach@scanarg@e + }{% + \if\skv@foreach@listisamacro + \expandafter\skv@foreach@scanarg@e + \else + \def\reserved@a##1{% + \skvifmacroTF{##1}{% + \def\skv@tempehd{Maybe a star (*) suffix after + \noexpand\newforeach or key 'expand list once', etc., + \MessageBreak is missing.}% + }{% + \let\skv@tempehd\skv@ehd + }% + \skv@err{I expected a left brace for beginning of list, + \MessageBreak since you haven't told me that your + \MessageBreak list is a macro. Instead I found '\detokenize{##1}'. + \MessageBreak If your list is indeed a macro, you can + \MessageBreak either put star (*) after \noexpand\newforeach + \MessageBreak or use the key 'expand list once' + \MessageBreak or 'expand list twice' or 'list is a macro', etc.% + }\skv@tempehd + }% + \expandafter\reserved@a + \fi + }% +} +% #1: <list> or <listcmd> +\skvrobustdef*\skv@foreach@scanarg@e#1{% + \edef\skv@foreach@userlist{\unexpanded{#1}}% + \skvifnextchar\skv@orig@bgroup\skv@foreach@scanarg@h{% + \skvifnextchar d\skv@foreach@scanarg@f\skv@foreach@scanarg@g + }% +} +\skvrobustdef*\skv@foreach@scanarg@f#1#2{% + \skvifstrcmpTF{#1#2}{do}{% + \skvifnextchar\skv@orig@bgroup + \skv@foreach@scanarg@h\skv@foreach@scanarg@g + }{% + \skv@err{I have seen 'd' but no the expected 'do'}\skv@ehd + }% +} +% We've exhausted all options for grabbing the callback, so now +% look for terminator (default ;). +% +% 1. If there is no terminator ahead, the use of \skv@prova will trigger +% 'the use of ... does not match its definition' or +% 'runaway argument ...'. +% 2. This covers the case when \newforeach or \foreach comes next in +% the callback. +\skvrobustdef*\skv@foreach@scanarg@g{% + \def\skv@prova##1{% + \def\skv@prova####1##1{% + % Look out for a case like 'parser=<parser>'. + % Eg, \newforeach[parser=;]. In this case, the value ';' of + % key 'parser' will mistakenly be assumed to be the callback + % terminator. It should be enclosed in braces. + \skvxifinTF{\detokenize{parser=}\relax}{\detokenize{####1}\relax}{% + \skvxifinTF{[}{\detokenize{####1}}{% + \skv@err{Put '##1' in braces for key 'parser=##1'. + \MessageBreak Try [parser={##1}]} + {Maybe parser '##1' is the value of a key/option of + \noexpand\newforeach and needs to be enclosed in braces. + The '##1' I have just encountered doesn't seem to be the + one ending a TikZ object. Maybe you need to do [parser={##1}].}% + }{% + % Otherwise, let's try continue: + \skvexpanded{% + \noexpand\skv@foreach@scanarg@h{\skvtrimspace{####1}##1}% + }% + }% + }{% + \skvexpanded{% + \noexpand\skv@foreach@scanarg@h{\skvtrimspace{####1}##1}% + }% + }% + }% + \skv@prova + }% + % In case callback terminator is currently active, rescan it. + \skvrescan\skv@foreach@terminator + \expandafter\skv@prova\expandafter{\skv@foreach@terminator}% +} +% #1: callback +\skvrobustdef*\skv@foreach@scanarg@h#1{% + \skvexpanded{\endgroup + % The subparser may have changed along the way + % (see \skv@foreach@scanarg@b): + \skvcmdexit\skv@foreach@subparser + \skv@foreach@mainprocess + {\skvexpandonce\skv@foreach@holderlist}% + {\skvexpandonce\skv@foreach@userlist}% + {\skvtrimspace{#1}}% + }% +} +% #1: holder macros +% #2: List or list command +% #3: Callback +\skvrobustdef*\skv@foreach@mainprocess#1#2#3{% + \edef\skv@foreach@userlist{% + \if\skv@foreach@expandlistonce + \expandafter\skvexpandonce + \else + \if\skv@foreach@expandlisttwice + \expandafter\expandafter\expandafter\skvexpandtwice + \else + \if\skv@foreach@expandlistfully + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\@iden + \else + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\unexpanded + \fi + \fi + \fi + {#2}% + }% + % Initial code, if any: + \skvcsuse{skv@foreach@atbeginhook@\skvrom\foreachnestdepth}% + \skvifdefTF\foreachprevitem{}{\def\foreachprevitem{}}% + \expandafter\skv@foreach@normalize\expandafter[\skv@foreach@parser]% + \skv@foreach@userlist + % Why 'keep double parser' here? Think of a list like 32//13,11/23/,... + \skvexpbracenext\skvdefinekeepdoubleparsernormalizer + \skv@foreach@subparser\skv@tempa + \edef\skv@foreach@userlist{\skvexpbracenext\skv@tempa\skv@foreach@userlist}% + \ifnum\foreachnestdepth=\skvz@ + \breakallforeachloopsfalse + \fi + \skvbreakloopfalse + \foreachlastitemfalse + \def\skv@foreach@lookahead@a##1{% + \def\skv@foreach@picknext{##1.}% + \futurelet\skv@foreach@next\skv@foreach@lookahead@b + }% + \def\skv@foreach@lookahead@b{% + \ifx\skv@foreach@next[% + \expandafter\skv@foreach@lookahead@c + \else + \ifx\skv@foreach@next(% + \expandafter\expandafter\expandafter\skv@foreach@lookahead@d + \else + \expandafter\expandafter\expandafter\skv@foreach@picknext + \fi + \fi + }% + \def\skv@foreach@lookahead@c[##1]{\skv@foreach@picknext[{##1}]}% + \def\skv@foreach@lookahead@d(##1){\skv@foreach@picknext({##1})}% + \def\skv@foreach@mainloop##1{% + \def\skv@foreach@getnext####1##1####2\skv@foreach@stop{% + \edef\foreachnextitem{\skvexpandonce{\@gobble####1}}% + \ifx\foreachnextitem\skv@foreach@nnil + \foreachlastitemtrue + \def\foreachnextitem{}% + \fi + }% + \def\skv@foreach@do@a####1##1####2\skv@foreach@stop{% + \edef\foreachcurrentitem{\skvexpandonce{\@gobble####1}}% + \skvifxTF\foreachcurrentitem\skv@foreach@nnil{% + \skvbreakloopfalse + \foreachlastitemfalse + }{% + \skvifxTF\foreachcurrentitem\skv@listbreakertoks{% + \skv@foreach@getrm{####2}% + }{% + \skvifxTF\foreachcurrentitem\skv@listpausertoks{% + \message{^^J! List pause: + ^^JType x or X to quit, or <RETURN> to proceed^^J}% + \begingroup + \endlinechar\m@ne\global\read-1 to\@gtempa + \endgroup + \skvexpandarg\lowercase{\def\noexpand\@gtempa{\@gtempa}}% + \skvxifstrcmpTF\@gtempa{x}{% + \skv@foreach@getrm{####2}% + }{% + \skv@foreach@do@b{####2}% + }% + }{% + \skv@foreach@do@b{####2}% + }% + }% + }% + }% + \def\skv@foreach@do@b####1{% + \edef\foreachitemcount{\the\numexpr\foreachitemcount+1}% + \ifx\skv@foreach@itemcounter\@empty\else + \expandafter\let\skv@foreach@itemcounter\foreachitemcount + \fi + \skv@foreach@lookahead@a\skv@foreach@getnext####1\skv@foreach@stop + % If the user decides to continue after \foreachlistpauser is picked up, + % don't use \foreachlistpauser as an item: + \skvifxTF\foreachcurrentitem\skv@listpausertoks{}{% + \skvexpbracenext\skv@foreach@callback\foreachcurrentitem + \let\foreachprevitem\foreachcurrentitem + }% + \ifx\skv@foreach@processupto\@empty\else + \ifnum\foreachitemcount<\skv@foreach@processupto\relax\else + \skvbreakloop + \fi + \fi + \skvifdefboolTF{skvbreakloop}{% + \skv@foreach@getrm{####1}% + }{% + \skv@foreach@lookahead@a\skv@foreach@do@a####1\skv@foreach@stop + }% + }% + \def\skv@foreach@getrm####1{% + \ifbreakallforeachloops\else + \skvbreakloopfalse + \fi + \foreachlastitemfalse + \def\skv@prova########1\skv@foreach@nil##1{% + \edef\foreachlistremainder{\unexpanded{########1}}% + \skv@removetrailingparser{##1}\foreachlistremainder + }% + \skv@prova####1% + }% + \skvifxTF\skv@foreach@userlist\@empty{% + \if\skv@foreach@processempty\skvafterfi + \skv@foreach@callback{}% + \fi + }{% + \def\do####1{\if####1true\else false\fi}% + \skvexpandsecond{\skvautocompletelist*}{[% + % Note: the value of 'expand list once', etc., can't be + % transferred automatically to \skvautocompletelist since they + % have been used above to expand list. + parser={\skv@foreach@parser}, + link={\skvexpandonce\skv@foreach@link}, + grow left=\do\skv@foreach@growleft, + grow right=\do\skv@foreach@growright, + keep decimals=\do\skv@foreach@keepdecimals, + keep high decimals=\do\skv@foreach@keephighdecimals, + expand before fill=\do\skv@foreach@expandbeforefill + ]}\skv@foreach@userlist\skv@foreach@userlist + \if\skv@foreach@reverselist\skvafterfi + \expandafter\skv@foreach@doreverselist\expandafter + {\skv@foreach@parser}\skv@foreach@userlist + \fi + \expandafter\skv@foreach@lookahead@a\expandafter\skv@foreach@do@a + \skv@foreach@userlist##1\skv@foreach@nil##1\skv@foreach@stop + }% + }% + \skv@foreach@makeprecode{#1}% + \if\skv@foreach@recoverholders\else + \expandafter\skvsavestate\expandafter\skv@foreach@holderstate + \expandafter{\skv@foreach@holderlist}\skv@foreach@holderdepth + \fi + \skv@foreach@makeauxcallback{#3}% + \skvexpandbracenext\skv@foreach@mainloop\skv@foreach@parser + \skvcsuse{skv@foreach@atendhook@\skvrom\foreachnestdepth}% + \if\skv@foreach@recoverholders\else + \skvrestorestate\skv@foreach@holderstate\skv@foreach@holderdepth + \fi + \skv@foreach@popstate + \ifnum\foreachnestdepth=\skvz@ + \def\skv@warnsubparser{0}% + \fi + \skv@inforeachfalse +} + +% \skv@foreach@makeprecode{<holder.list>} +% +% 'Holder macro' here means, eg, \x and \y in +% +% \newforeach \x/\y in {1/a,2/b,3/c}{<code>} +% +\skvrobustdef*\skv@foreach@makeprecode#1{% + \begingroup + \skv@cnta\skvz@ + \def\skv@foreach@holderlist{}% + \def\skv@foreach@paramlist{}% + \def\skv@prevholder{}% + \def\skv@tempa{#1}% + \expandafter\skv@foreach@normalize\expandafter + [\skv@foreach@subparser]\skv@tempa + \skvtfor*\skv@prova:=\skv@tempa\do{% + \ifx\skv@prova\skv@foreach@subparser + \ifnum\skv@cnta=\skvz@ + \skv@err{Subparser '\skvoxdetok\skv@foreach@subparser' can't come + first in the list\MessageBreak of holder macros}\skv@ehd + \else + % Eg, if subparser is slash (/), \skv@foreach@paramlist will have + % the syntax: ##1/##2/...: + \edef\skv@foreach@paramlist{% + \skvexpandonce\skv@foreach@paramlist\skvexpandonce\skv@prova + }% + \fi + \else + \advance\skv@cnta\@ne + \edef\skv@elt{\expandafter\skvremovescape\skv@prova}% + \edef\skv@foreach@precallback{% + \skvexpandonce\skv@foreach@precallback + % Each given item-holder macro is now defined in terms of + % parameters of skv@foreach@auxcallback: + \edef\skvexpandonce\skv@prova{% + \noexpand\unexpanded{####\number\skv@cnta}% + }% + % If the current holder has no value, use the value of last holder: + \ifnum\skv@cnta>\@ne + \if\skv@foreach@inherit + \noexpand\ifx\skvexpandonce\skv@prova\noexpand\@empty + \let\skvexpandonce\skv@prova\skvexpandonce\skv@prevholder + \noexpand\fi + \fi + \fi + }% + \edef\skv@foreach@holderlist{% + \skvexpandonce\skv@foreach@holderlist + % The \do is for the individual entries of the stack of + % \skv@foreach@holderlist. It is required for stacking the + % holder list: + \noexpand\do\skvexpandonce\skv@prova + }% + \edef\skv@foreach@paramlist{% + \skvexpandonce\skv@foreach@paramlist########\number\skv@cnta + }% + \let\skv@prevholder\skv@prova + \fi + }% + % Add one extra parameter character to absorb extra subparsers that are + % added for incomplete list items: + \edef\skv@foreach@paramlist{% + \skvexpandonce\skv@foreach@paramlist + \skvexpandonce\skv@foreach@subparser + ########\number\numexpr\skv@cnta+1\relax + }% + \skvexpanded{\endgroup + \skvcmdexit\skv@foreach@holderlist + \skvcmdexit\skv@foreach@paramlist + \skvcmdexit\skv@foreach@precallback + }% +} +% Example (*): +% +% This example demonstrates why \skv@foreach@continuetester must be +% located delicately, at where it is currently. +% +% \def\ditem{D} +% \newforeach [ +% continue=\ifx\x\ditem\fi, remember=\x as \rx initially A +% ] \x in { +% B,(C1),D,E,[F1],G,H +% }{% +% $\overrightarrow{\rx\x}$\ifforeachlastitem.\else,\space\fi +% } +% +% #1: callback +\skvrobustdef*\skv@foreach@makeauxcallback#1{% + \begingroup + \def\skv@generatesubparser##1{% + \ifnum##1<10 \skvexpandonce\skv@foreach@subparser + \expandafter\skv@generatesubparser + \expandafter{\number\numexpr##1+1\expandafter}% + \fi + }% + % Add extra subparsers in case some items aren't subparser-complete: + \edef\skv@tempa{\skv@generatesubparser{1}}% + \edef\skv@foreach@callback@hiddenparameters{\unexpanded{#1}}% + \let\foreacheov\relax + \let\do\noexpand + \skvexpanded{\endgroup + \skvcmdexit\skv@foreach@callback@hiddenparameters + % Define auxiliary callback: + \def\do\skv@foreach@auxcallback\skv@foreach@paramlist\foreacheov{% + % Holder macro assignments: + \skvexpandonce\skv@foreach@precallback + % User tests/tasks, eg, if requested from 'evaluate' key: + \skvexpandonce\skv@foreach@afterprecallback + \do\ifnum\do\foreachitemcount=\@ne + \do\skv@foreach@onlyinitially + \do\fi + \ifx\skv@foreach@continuetester\@secondoftwo + \noexpand\skv@foreach@continuetester + \else + % Expose the parameter characters in \skv@foreach@continuetester. + % If the user wants to ignore the callback for some items, he can + % use the key 'continue=<condition>': + \skvexpandonce\skv@foreach@continuetester + \fi + {% + \relax + }{% + \if\skv@foreach@hideparameters + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi + {% + \noexpand\skv@foreach@callback@hiddenparameters + }{% + \unexpanded{#1}% + }% + % User tests/tasks, eg, if requested from the keys 'remember', + % 'exit when', etc: + \skvexpandonce\skv@foreach@postcallback + }% + }% + % Define the real callback: + \def\do\skv@foreach@callback####1{% + \do\skv@foreach@auxcallback####1\skvxonce\skv@tempa\foreacheov + }% + }% +} +\skvnewdef*\skv@ucalpha#1{% + \ifcase#1\or A\or B\or C\or D\or E\or F\or G\or H\or I\or J\or + K\or L\or M\or N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or + X\or Y\or Z\else?\fi +} +\skvnewdef*\skv@lcalpha#1{% + \ifcase#1\or a\or b\or c\or d\or e\or f\or g\or h\or i\or j\or + k\or l\or m\or n\or o\or p\or q\or r\or s\or t\or u\or v\or w\or + x\or y\or z\else?\fi +} +\skvnewdef*\skv@alphamap{% + {A}{a}{1}{B}{b}{2}{C}{c}{3}{D}{d}{4}{E}{e}{5}{F}{f}{6}{G}{g}{7}{H}{h}{8}% + {I}{i}{9}{J}{j}{10}{K}{k}{11}{L}{l}{12}{M}{m}{13}{N}{n}{14}{O}{o}{15}% + {P}{p}{16}{Q}{q}{17}{R}{r}{18}{S}{s}{19}{T}{t}{20}{U}{u}{21}{V}{v}{22}% + {W}{w}{23}{X}{x}{24}{Y}{y}{25}{Z}{z}{26}% +} +\skvrobustdef*\skv@reverselumap#1{% + \def\skv@prova##1##2##3##4\skv@nil{% + \ifx\@nnil##1% + \expandafter\@gobble + \else + \expandafter\@iden + \fi + {\skv@prova##4\skv@nil{##1}{##2}{##3}}% + }% + \edef#1{\expandafter\skv@prova\skv@alphamap\@nnil{}{}\skv@nil} +} + + +\skvquickkeys.define{skeyval/autocomplete}{% + .exec/\def\skv@autocomplete@parser{,}; + parser/{,}/ + \edef\skv@autocomplete@parser{\unexpanded{#1}} + \skvstripouterbraces{2}\skv@autocomplete@parser + \skvxifntypeTF{\detokenize{#1}}{% + \begingroup + % It is assumed that every n-type parser and subparser + % must have catcode 12: + \skvexpandnext{\catcode`}\skv@autocomplete@parser=12\relax + \skvrescan\skv@autocomplete@parser + \skvaftergroupdef\skv@autocomplete@parser\endgroup + }{} + ; + % This default value of 'link' is required by \skvautocompletelist: + .exec code/\def\skv@autocomplete@link{\@nil}; + link/\@nil/ + \edef\skv@prova{\unexpanded{#1}}% + \ifcase0% + \ifx\skv@prova\@empty 1\fi + \ifx\skv@prova\@nnil 1\fi\relax + \edef\skv@autocomplete@link{\unexpanded{#1}}% + \skvstripouterbraces{2}\skv@autocomplete@link + \fi + ; + grow/\@nil/ + \edef\skv@prova{\unexpanded{#1}}% + \ifx\skv@prova\@nnil\else + \skvcsedef{skv@autocomplete@grow#1}{00} + \edef\skv@prova{\skvifstrcmpTF{#1}{left}{right}{left}}% + \skvcsedef{skv@autocomplete@grow\skv@prova}{01}% + \fi + /left,right + ; + .exec/ + \def\skv@autocomplete@growleft{01} + \def\skv@autocomplete@growright{01} + ; + grow left/true/ + \edef\skv@autocomplete@growleft{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + grow right/true/ + \edef\skv@autocomplete@growright{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + % Keep the decimals in rounded numbers or dimensions when list is + % auto-completed. The value value of this key is 'true'. + keep decimals/true/ + \edef\skv@autocomplete@keepdecimals{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + keep high decimals,keep only high decimals/true/ + \edef\skv@autocomplete@keephighdecimals{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; + expand before fill,expand list before fill/true/ + \edef\skv@autocomplete@expandbeforefill{0\skvifstrcmpTF{true}{#1}01} + /true,false + ; +} + +% The keys 'link,grow' don't need presetting. The user must supply +% values when they are called. In internal loops, their values will +% have to be revised from those of the outer loop. +% +\skvquickkeys.preset{skeyval/autocomplete}{% + parser,grow left=false,grow right=false, + keep decimals=true,keep high decimals=false, + expand before fill=false +} + +% \skv@autocomplete@addtolist<parser><add.new><listcmd> +\skvrobustdef*\skv@autocomplete@addtolist#1#2#3{% + \edef#3{\skvexpandonce#3\ifx#3\@empty\else\unexpanded{#1}\fi#2}% +} +% \skv@autocomplete@zerostepwarn{<lower>}{<upper>}{<pt.or.nil>} +\skvrobustdef*\skv@autocomplete@zerostepwarn#1#2#3{% + \skv@warn{The tokens '\detokenize{#1}', '\detokenize{#2}' + give nil step; '1#3' used.\MessageBreak + Maybe you used the font-dependent units\MessageBreak + 'ex' or 'em' \ifskv@latex before \skv@begindoctoks\fi}% +} +% \skvautocompletelist[<kvlist>]{<list>}{<returncmd>} +% \skvautocompletelist*[<kvlist>]{<listcmd>}{<returncmd>} +% +% 1. Parse list of type {1,2,...,n,1,...,m} for auto completion. +% Return outcome in <listcmd> or \skvcompletedlist. +% +\skvrobustdef*\skvautocompletelist{\skv@teststopt\skv@autocompletelist@a{}} +\skvrobustdef*\skv@autocompletelist@a[#1]#2#3{% + \skv@usetempst{#2}\skv@autocomplete@userlist + \skvquickkeys.set{skeyval/autocomplete}{#1}% + \def\skv@inalphabetic{01}% + \def\skv@isupperalpha{01}% + \def\skv@preandpostdotspresent{01}% + % If the list is alphabetic or numbers, original unit is nil: + \def\skv@autocomplete@origunit{}% + \def\skvcompletedlist{}% + \skvexpandtwoargsonce + \skv@autocompletelist@b\skv@autocomplete@parser\skv@autocomplete@userlist + \let#3\skvcompletedlist +} +% \skv@autocompletelist@b{<parser>}{<list>} +\skvrobustdef*\skv@autocompletelist@b#1#2{% + \edef\skv@tempa{\unexpanded{#2}}% + \skv@foreach@normalize[#1]\skv@tempa + \def\skv@prova##1{% + \skvxifinTF{...}{\detokenize{##1}}{% + \if\skv@autocomplete@expandbeforefill + \edef\skv@tempa{##1}% + \skv@foreach@normalize[#1]\skv@tempa + \else + \edef\skv@tempa{\unexpanded{##1}}% + \fi + \skvexpandargonce{\skv@autocompletelist@c{#1}}\skv@tempa + \skv@foreach@normalize[#1]\skvcompletedlist + }{% + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist\skvtrimspace{##1}% + }% + }% + }% + \expandafter\skv@prova\expandafter{\skv@tempa}% +} +% \skv@autocompletelist@c{<parser>}{<list>} +\skvrobustdef*\skv@autocompletelist@c#1#2{% + \skvxiffound{\relax...}\in{\relax\detokenize{#2}}\then + \skv@err{I can't find lower limit before '...'}\skv@ehd + \else + \skvxiffound{\relax\detokenize{#1}...}\in{\relax\detokenize{#2}}\then + \skv@err{I can't find lower limit before '...'}\skv@ehd + \fi + \fi + % \newforeach format is '<parser>...<parser>': + \skvxifinTF{\detokenize{#1...#1}}{\detokenize{#2}}{% + \skv@autocomplete@splitbeforedots{#1}{#2}% + \def\skv@prova##1#1##2#1...#1##3#1##4#1##5\skv@nil{% + \skvifblankTF{##3}{% + \skv@err{I can't find upper limit number or dimension, + \MessageBreak i.e., after symbool '...'}\skv@ehd + }{% + % \skv@autocompletelist@b may be reentered. Hence accumulate + % filled list: + \skviflacus{##1}\then\else + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist\unexpanded{##1}% + }% + \fi + % Complete the list: + \skv@autocompletelist@d{#1}{##1}{##2}{##3}% + % In case the list is folded on ellipsis (...): + \skvifblankTF{##4}{}{\skv@autocompletelist@b{#1}{##4#1##5}}% + }% + }% + \expandafter\skv@prova\skv@tempa#1#1\skv@nil + }{% + \skv@autocompletelist@e{#1}{#2}% + }% +} +% skv@autocompletelist@d{<parser>}{<lower1>}{<lower2>}{<upperbound>} +% +% 1. No <guide> here. +% +\skvrobustdef*\skv@autocompletelist@d#1#2#3#4{% + \skvifxTF\skv@autocomplete@link\@nnil{% + % No <link> has been given. The list has '...' but presumably no link. + % + % \skv@autocompletelist@h{<parser>}{<guide>}{<lower1>}{<lower2>}{<upper>} + \skv@autocompletelist@h{#1}{}{#2}{#3}{#4}% + }{% + % <link> has been given by the user: + \skv@autocomplete@splitandgrow{#1}{#2}{#3}{#4}% + }% +} +% +% \skv@autocompletelist@e{<parser>}{<list>} +% +% \skv@autocompletelist@e will auto-complete this list without +% knowledge of the values of the keys 'link' and 'grow'. The syntax at (*) +% above isn't recognized by the auto-completion scheme of skeyval. +% +% skeyval formats: +% +% {1pt,4pt,...pt,10pt} : no additional info. required +% {A_2pt,A_4pt,A_...,A_10pt} : no additional info. required +% {\pi0,\pi0.5,\pi...,\pi3} : no additional info. required +% {0\pi,0.5\pi,...\pi,3\pi} : no additional info. required +% {1pt,4pt,...,10pt} : link = pt, grow=left, or no addit. info. +% {A_2pt,A_4pt,...,A_10pt} : link =_, grow=right +% +% The general PGF format with left and right guides (given as 'A_' +% and 'pt') in +% +% {A_2pt,A_4pt,A_...pt,A_10pt} +% +% is still legal in skeyval. +% +\skvrobustdef*\skv@autocompletelist@e#1#2{% + \skv@autocomplete@splitbeforedots{#1}{#2}% + \def\skv@checkguideinbounds##1##2{% + \skv@foreach@swafalse + \def\do####1{% + \ifx\do####1\else + \skviflacus####1\then\else + \skvifinTF{##1}{####1}{}\skv@foreach@swatrue + \fi + \expandafter\do + \fi + }% + \do##2\do + \ifskv@foreach@swa + \skv@err{Guide token '\detokenize{#1}' not found in some + \MessageBreak of the elements: I can't complete + \MessageBreak the current list. This may be + \MessageBreak due to wrong direction of growth}\skv@ehd + \fi + }% + \def\skv@prova##1#1##2#1##3...##4#1##5#1##6#1##7\skv@nil{% + \skvifblankTF{##5}{% + \skv@err{I can't find upper limit number or dimension, + \MessageBreak i.e., after symbool '...'}\skv@ehd + }{% + % \skv@autocompletelist@b may be reentered. Hence + \skviflacus{##1}\then\else + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist + \skvtrimspace{##1}% + }% + \fi + \let\do\unexpanded + \skvifblankTF{##3}{% + \skvifblankTF{##4}{% + \skv@err{No direction of growth before and/or after + \MessageBreak symbol '...'}\skv@ehd + }{% + % Eg, {2pt_A,4pt_A,...pt_A,10pt_A}. Here ##3 is nil, grow left. + \def\skv@autocomplete@growleft{00}% + \def\skv@autocomplete@growright{01}% + \skvexpanded{% + \skv@autocompletelist@f + {\do{#1}}{\do{##4}}{\do{##1}}{\do{##2}}{\do{##5}}% + }% + }% + }{% + \skvifblankTF{##4}{% + % Eg, {A_2pt,A_4pt,A_...,A_10pt}. Here ##4 is nil, grow right. + \def\skv@autocomplete@growright{00}% + \def\skv@autocomplete@growleft{01}% + \skvexpanded{% + \skv@autocompletelist@f + {\do{#1}}{\do{##3}}{\do{##1}}{\do{##2}}{\do{##5}}% + }% + }{% + % Eg, {A_2pt,A_4pt,A_...pt,A_10pt}. + \skvexpanded{% + \skv@autocompletelist@g + {\do{#1}}{\do{##3}}{\do{##4}}{\do{##1}}{\do{##2}}{\do{##5}}% + }% + }% + }% + % In case the list is folded on ellipsis (...): + \skvifblankTF{##6}{}{\skv@autocompletelist@b{#1}{##6#1##7}}% + }% + }% + \expandafter\skv@prova\skv@tempa#1#1#1\skv@nil +} +% \skv@autocompletelist@f +% {<parser>}{<guide>}{<lower1>}{<lower2>}{<upper>} +% +% 1. Use the <guide> to get the constant and variable parts of the +% elements. +% 2. Use the variable parts to determine if the list is Alpah, alpha or +% number type. In this format, the unit in dimension type of list may +% appear in <guide>. +% 3. Use the variable parts to work out the <step>. +% 4. Call the relevant macro to fill the list. +% 5. <lower1> has already been saved in \skvcompletedlist. +% +\skvrobustdef*\skv@autocompletelist@f#1#2#3#4#5{% + \skv@checkguideinbounds{#2}{{#3}{#4}{#5}}% + % Use the 'link' (#2) to get from {<lower1>}, {<lower2>} and {<upper>} + % the left or right sides of the elements that should be grown: + \def\skv@prova##1#2##2\skv@nil##3{% + \edef##3{\if\skv@autocomplete@growleft##1\else##2\fi}% + }% + % <lower1> is empty when there is only one element before ellipsis: + \skviflacus#3\then + \def\skv@tempa{}% + \else + \skv@prova#3\skv@nil\skv@tempa + \fi + \skv@prova#4\skv@nil\skv@tempb + \skv@prova#5\skv@nil\skv@tempc + \let\do\skvexpandonce + \skvexpandsecond + {\skv@autocompletelist@h{#1}{#2}}% + {{\do\skv@tempa}{\do\skv@tempb}{\do\skv@tempc}}% +} +% \skv@autocompletelist@g +% {<parser>}{<predots>}{<postdots>}{<lower1>}{<lower2>}{<upper>} +\skvrobustdef*\skv@autocompletelist@g#1#2#3#4#5#6{% + \def\skv@preandpostdotspresent{00}% + \skv@checkguideinbounds{#2}{{#4}{#5}{#6}}% + \skv@checkguideinbounds{#3}{{#4}{#5}{#6}}% + % For <lower1>, <lower2> and <upper>, get the tokens between + % <predots> and <postdots>. They are the ones to be grown. + \def\skv@prova##1#2##2#3##3\skv@nil##4{% + \edef##4{##2}% + }% + \ifx\@nnil#4\@nnil + \let\skv@tempa\@empty + \else + \skv@prova#4\skv@nil\skv@tempa + \fi + \skv@prova#5\skv@nil\skv@tempb + \skv@prova#6\skv@nil\skv@tempc + % Pass <guide> as empty to \skv@autocompletelist@h. Through out, the only + % reason for passing <guide> to \skv@autocompletelist@h is to restore + % it in \skv@autocomplete@restore. But while + % \skv@preandpostdotspresent is true, we won't do anything in + % \skv@autocomplete@restore. + \skvexpandsecond + {\skv@autocompletelist@h{#1}{}}% + {{\skv@tempa}{\skv@tempb}{\skv@tempc}}% + % Restore <predots> and <postdots>: + \def\do##1#1{% + \ifx\do##1\else + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist + \unexpanded{#2}##1\unexpanded{#3}\skv@autocomplete@origunit + }% + \expandafter\do + \fi + }% + \expandafter\do\skv@completedlist@partial#1\do#1% +} +% \skv@autocompletelist@h{<parser>}{<guide>}{<lower1>}{<lower2>}{<upper>} +% +% 1. Use the variable parts to determine if the list is Alpah, alpha or +% number type. In this format, the unit in dimension type of list may +% appear in <guide>. +% 2. Use the variable parts to work out the <step>. +% 3. Call the relevant macro to fill the list. +% 4. <lower1> has already been saved in \skvcompletedlist. +% +\skvrobustdef*\skv@autocompletelist@h#1#2#3#4#5{% + \def\skv@completedlist@partial{}% + \edef\skv@lowerlower{\ifx\@nnil#3\@nnil\else\unexpanded{#3}, \fi}% + \skvifloweralpha{#4}{% + \skv@foreach@swatrue + \ifx\@nnil#3\@nnil\else + \skvifloweralpha{#3}{}\skv@foreach@swafalse + \fi + \skvifloweralpha{#5}{}\skv@foreach@swafalse + \ifskv@foreach@swa + \ifx\@nnil#3\@nnil + \def\skv@autocomplete@step{1}% + \else + \edef\skv@autocomplete@step{\the\numexpr\lccode`#4-\lccode`#3}% + \ifnum\skv@autocomplete@step=\skvz@ + \def\skv@autocomplete@step{1}% + \skv@autocomplete@zerostepwarn{#3}{#4}{}% + \fi + \fi + \def\skv@isupperalpha{01}% + % \skv@autocomplete@origunit remains nil on this branch. + \skv@autocomplete@fillalphabetic{#1}{#4}{#5}\skv@completedlist@partial + % Also update the items already entered in \skvcompletedlist. + \skv@autocomplete@restore{#1}{#2}\skv@completedlist@partial + \else + \skv@err{The tokens '\detokenize\expandafter{\skv@lowerlower#4, #5}' + \MessageBreak aren't of the same type}\skv@ehd + \fi + }{% + \skvifupperalpha{#4}{% + \skv@foreach@swatrue + \ifx\@nnil#3\@nnil\else + \skvifupperalpha{#3}{}\skv@foreach@swafalse + \fi + \skvifupperalpha{#5}{}\skv@foreach@swafalse + \ifskv@foreach@swa + \ifx\@nnil#3\@nnil + \def\skv@autocomplete@step{1}% + \else + \edef\skv@autocomplete@step{\the\numexpr\uccode`#4-\uccode`#3}% + \ifnum\skv@autocomplete@step=\skvz@ + \def\skv@autocomplete@step{1}% + \skv@autocomplete@zerostepwarn{#3}{#4}{}% + \fi + \fi + \def\skv@isupperalpha{00}% + % \skv@autocomplete@origunit remains nil on this branch. + \skv@autocomplete@fillalphabetic{#1}{#4}{#5}\skv@completedlist@partial + \skv@autocomplete@restore{#1}{#2}\skv@completedlist@partial + \else + \skv@err{The tokens '\detokenize\expandafter{\skv@lowerlower#4, #5}' + \MessageBreak aren't of the same type}\skv@ehd + \fi + }{% + \skv@foreach@swatrue + \ifx\@nnil#3\@nnil\else + \skvifdimensionable{#3}{}\skv@foreach@swafalse + \fi + \skvifdimensionable{#4}{}\skv@foreach@swafalse + \let\skv@autocomplete@origunit\skv@dimunit + \skvifdimensionable{#5}{}\skv@foreach@swafalse + \ifx\skv@autocomplete@origunit\skv@dimunit\else + \skv@err{The units on \detokenize{'#4', '#5'} are different}\skv@ehd + \fi + \ifskv@foreach@swa + \ifx\@nnil#3\@nnil + \def\skv@autocomplete@step{1pt}% + \else + \edef\skv@autocomplete@step{\the\dimexpr#4-#3\relax}% + \ifdim\skv@autocomplete@step=\skvz@ + \def\skv@autocomplete@step{1pt}% + \skv@autocomplete@zerostepwarn{#3}{#4}{pt}% + \fi + \fi + \skvexpandsecond + {\skv@autocomplete@fillnumordim{#1}}{{#4}{#5}}% + \skv@completedlist@partial + \skv@autocomplete@strippoint{#1}\skv@completedlist@partial + % Attach the constant part to the elements of the completed list. + % <lower1> was saved earlier in \skv@autocompletelist@e, and + % <lower2> and <upper> have been saved while filling the list. + % Always strip point and restore original unit here. + \skv@autocomplete@restore{#1}{#2}\skv@completedlist@partial + \else + \skv@foreach@swatrue + \ifx\@nnil#3\@nnil\else + \skvifdimensionable{#3pt}{}\skv@foreach@swafalse + \fi + \skvifdimensionable{#4pt}{}\skv@foreach@swafalse + \skvifdimensionable{#5pt}{}\skv@foreach@swafalse + \ifskv@foreach@swa + \ifx\@nnil#3\@nnil + \def\skv@autocomplete@step{1pt}% + \else + \edef\skv@autocomplete@step{\the\dimexpr#4pt-#3pt\relax}% + \ifdim\skv@autocomplete@step=\skvz@ + \def\skv@autocomplete@step{1pt}% + \skv@autocomplete@zerostepwarn{#3}{#4}{pt}% + \fi + \fi + % \skv@autocomplete@origunit remains nil on this branch. + \skvexpandsecondonce + {\skv@autocomplete@fillnumordim{#1}}{{#4pt}{#5pt}}% + \skv@completedlist@partial + \skv@autocomplete@strippoint{#1}\skv@completedlist@partial + \skv@autocomplete@restore{#1}{#2}\skv@completedlist@partial + \else + \skv@err{Sorry, the tokens + '\detokenize\expandafter{\skv@lowerlower#4, #5}' + \MessageBreak are unparsable. I have given up on them. + \MessageBreak Did you miss something syntactic? + \MessageBreak See manual for acceptable syntaxes}\skv@ehd + \fi + \fi + }% + }% +} +% \skv@autocomplete@fillnumordim<parser><lower><upper><listcmd> +\skvrobustdef*\skv@autocomplete@fillnumordim#1#2#3#4{% + \begingroup + \skv@autocomplete@getcomparator + \if\skv@autocomplete@stepisneg + \ifdim#3\skv@autocomplete@unit>#2\skv@autocomplete@unit\relax + \skv@err{Step '\skv@autocomplete@step' is negative but upperbound + \MessageBreak '\detokenize{#3}' is greater than lowerbound + '\detokenize{#2}'}\skv@ehd + \fi + \else + \ifdim#3\skv@autocomplete@unit<#2\skv@autocomplete@unit\relax + \skv@err{Step '\skv@autocomplete@step' is nonnegative but upperbound + \MessageBreak '\detokenize{#3}' is less than lowerbound + '\detokenize{#2}'}\skv@ehd + \fi + \fi + % The bounds may have decimal places. So the calculation has to be + % done with \dimexpr: + \edef\skv@upperlimit{\the\dimexpr#3\skv@autocomplete@unit}% + \edef\skv@lowerlimit{\the\dimexpr#2\skv@autocomplete@unit}% + \skv@autocomplete@addtolist{#1}\skv@lowerlimit#4% + \ifdim\skv@lowerlimit\skv@comparator\skv@upperlimit\relax + \def\do{% + \edef\skv@lowerlimit{% + \the\dimexpr\skv@lowerlimit + +\skv@autocomplete@step\skv@autocomplete@unit\relax + }% + \ifdim\skv@lowerlimit\skv@comparator\skv@upperlimit\relax + \skv@autocomplete@addtolist{#1}\skv@lowerlimit#4% + \expandafter\do + \fi + }% + \do + % The last item has to in 'pt', so that 'pt' can be stripped. + % Hence \dimexpr. + \skv@autocomplete@addtolist{#1}\skv@upperlimit#4% + \fi + \skvaftergroupdef#4\endgroup +} +% Fill Alpha or alpha. +% \skv@autocomplete@fillalphabetic<parser><lower><upper><listcmd> +\skvrobustdef*\skv@autocomplete@fillalphabetic#1#2#3#4{% + \begingroup + \def\skv@inalphabetic{00}% + \skv@autocomplete@getcomparator + \edef\skv@type{% + \if\skv@isupperalpha Upper\else Lower\fi case alphabetic% + }% + \edef\reserved@a{\if-\skv@autocomplete@step\else\skv@autocomplete@step\fi}% + \skvoifinteger\reserved@a{}{% + \skv@err{List is '\skv@type' type but\MessageBreak + step '\skv@autocomplete@step' isn't an integer}\skv@ehd + }% + \skv@autocomplete@addtolist{#1}{#2}#4% + \skvifstrcmpTF{#2}{#3}{}{% + \edef\skv@lowerlimit{#2}% + \edef\skv@upperlimit{#3}% + \skvexpandnext{\chardef\skv@lowerlimit`}\skv@lowerlimit\relax + \skvexpandnext{\chardef\skv@upperlimit`}\skv@upperlimit\relax + \edef\skv@lowerlimit{% + \the\numexpr\skv@lowerlimit-\if\skv@isupperalpha 64\else 96\fi + }% + \edef\skv@upperlimit{% + \the\numexpr\skv@upperlimit-\if\skv@isupperalpha 64\else 96\fi + }% + \edef\skv@charcase{\the\numexpr\skv@lowerlimit+\skv@autocomplete@step}% + \skvrecurse + \if\skv@isupperalpha + \edef\skv@tempa{\skv@ucalpha\skv@charcase}% + \else + \edef\skv@tempa{\skv@lcalpha\skv@charcase}% + \fi + \skv@autocomplete@addtolist{#1}\skv@tempa#4% + \edef\skv@charcase{\the\numexpr\skv@charcase+\skv@autocomplete@step}% + \while + \ifnum\skv@charcase\skv@comparator\skv@upperlimit\relax + \fi + \skv@autocomplete@addtolist{#1}{#3}#4% + }% + \skvaftergroupdef#4\endgroup +} +% +% \skv@autocomplete@splitbeforedots{<parser>}{<list>} +% +% Get the one or two elements before '...' or before '<guide>,...'. +% +% {A_2pt,A_4pt,A_...pt,A_10pt} : no additional info. required +% {a^1,a^3,a^...,a^9} : no additional info. required +% {A_1,B_1,..._1,E_1} : no additional info. required +% {1--,4--,...--,16--} : no additional info. required +% {1pt,4pt,...pt,10pt} : no additional info. required +% {A_2pt,A_4pt,A_...,A_10pt} : no additional info. required +% +% {a^1,a^3,...,a^9} : link = ^, grow = right +% {A_1,B_1,...,E_1} : link = _, grow = left +% {1--,4--,...,16--} : link = --, grow = left +% {1pt,4pt,...,10pt} : link = pt, grow = left, or no addit. info. +% {A_2pt,A_4pt,...,A_10pt} : link = _, grow = right +% +% The items prior to the two before '<guide>...' or before '...' +% are called 'all-before-the-two' (AB2). Collect in \skv@tempa the two +% items before '<guide>...' or before '...' together with the rest of the +% list. Save the AB2 items in \skvcompletedlist. +% +% Return \skvcompletedlist and \skv@tempa. +% +\skvrobustdef*\skv@autocomplete@splitbeforedots#1#2{% + % First count all the elements before '<guide><parser>...' or '...'. + % We're already sure that argument #2 contains '<guide><parser>...' + % or '...'. \skvcompletedlist has been initialized before here. + \begingroup + \skv@cnta\skvz@ + \def\do##1#1{% + \skvifinTF{...}{##1}{% + % Gobble the rest: + \def\do####1\skv@nil{}% + }{% + \advance\skv@cnta\@ne + }% + \do + }% + \do#2#1\skv@nil + \ifnum\skv@cnta>\tw@ + \@tempcnta\m@ne + \def\do##1#1{% + \advance\@tempcnta\@ne + \ifnum\@tempcnta=\numexpr\skv@cnta-\tw@\relax + \def\do####1\skv@nil{\def\skv@tempa{##1#1####1}}% + \else + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist\unexpanded{##1}% + }% + \fi + \do + }% + \do#2#1\skv@nil + \else + \ifnum\skv@cnta=\@ne + % There is only one element before '<guide><parser>...' or '...'. + % Hence simply left-pad argument #2 with <parser> to give the + % impression of at least 2 elements: + \def\skv@tempa{#1#2}% + \else + \ifnum\skv@cnta=\tw@ + \def\skv@tempa{#2}% + \else + \ifnum\skv@cnta=\skvz@ + \skv@err{List <<\detokenize{#2}>>\MessageBreak + contains symbol '...' but not a lowerbound}\skv@ehd + \fi + \fi + \fi + \fi + \skvexpanded{\endgroup + \skvcmdexit\skvcompletedlist\skvcmdexit\skv@tempa + }% +} +% \skv@autocomplete@splitandgrow{<parser>}{<lower1>}{<lower2>}{<upperbound>} +% +% {0\pi,0.5\pi,...,3\pi} : link = \pi ; grow left +% {1--,4--,...,10--} : link = -- ; grow left +% {2pt,4pt,...,10pt} : link = pt ; grow left (or no additional info.) +% {A_1,C_1,...,Q_1} : link = _ ; grow left +% {2^a,2^c,...,2^q} : link = ^ ; grow right +% +% 1. Providing the link avoids the need to use the syntax of PGF's +% \foreach, such as {0\pi,0.5\pi,...\pi,3\pi}. But skeyval can also +% autocomplete lists like {0\pi,0.5\pi,...\pi,3\pi}. +% 2. We already know that the key 'link' will reject empty values. +% +\skvrobustdef*\skv@autocomplete@splitandgrow#1#2#3#4{% + \ifx\skv@autocomplete@growleft\skv@autocomplete@growright + \skv@err{I can't grow the list in both directions. + \MessageBreak Maybe you haven't indicated the + \MessageBreak direction of growth (left or right), + \MessageBreak or both directions are 'true'.} + {Try one of the keys 'grow=left or right', + \MessageBreak 'grow left=true or false', + 'grow right=true or false'}\skv@ehd + \fi + \begingroup + \def\skv@prova##1{% + \skv@foreach@swatrue + \skviflacus#2\then\else + \skvifinTF{##1}{#2}{}\skv@foreach@swafalse + \fi + \skvifinTF{##1}{#3}{}\skv@foreach@swafalse + \skvifinTF{##1}{#4}{}\skv@foreach@swafalse + \ifskv@foreach@swa\else + \edef\skv@provb{\ifx\@nnil#2\@nnil\else\unexpanded{#2}, \fi}% + \skv@err{No link '##1' in one of + '\detokenize\expandafter{\skv@provb#3, #4}'}\skv@ehd + \fi + \def\skv@prova####1##1####2\skv@nil{% + \if\skv@autocomplete@growright + \edef\skv@prova{\skvtrimspace{####1##1}}% + \else + \edef\skv@prova{\skvtrimspace{##1####2}}% + \fi + \def\do########1{{\skvtrimspace{########1}}}% + % \skv@autocompletelist@f<parser><guide><lower1><lower2><upper> + \skvexpanded{\endgroup + \noexpand\skv@autocompletelist@f + {\unexpanded{#1}}{\skvexpandonce\skv@prova}\do{#2}\do{#3}\do{#4}% + }% + }% + \skv@prova#3\skv@nil + }% + \expandafter\skv@prova\expandafter{\skv@autocomplete@link}% +} +\skvrobustdef*\skv@autocomplete@getcomparator{% + \begingroup + % <unit> isn't required in alphabetic sequences: + \if\skv@inalphabetic\else + \let\skvreversedo\@iden + \edef\skv@provb{\skvexpbracenext\skvreverse\skv@autocomplete@step}% + \def\skv@prova##1##2##3\skv@nil{% + \skvxifinTF{,\detokenize{##2##1},}{,\skvoxdetok\skv@validunit,}{% + % That is, the sequence is made up of dimensions. + \def\skv@autocomplete@unit{}% + }{% + \def\skv@autocomplete@unit{pt}% + }% + }% + \expandafter\skv@prova\skv@provb\skv@nil + \fi + % You can't use \ifnum or \ifdim here, since for alphanumeric sequences + % \skv@autocomplete@step isn't a dimension. + \def\skv@prova##1##2\skv@nil{% + \edef\skv@autocomplete@stepisneg{0\if-##10\else1\fi}% + }% + \expandafter\skv@prova\skv@autocomplete@step\skv@nil + \edef\skv@comparator{\if\skv@autocomplete@stepisneg>\else<\fi}% + \let\do\skvcmdexit + \skvexpanded{\endgroup + \do\skv@autocomplete@stepisneg\do\skv@comparator + \if\skv@inalphabetic\else\do\skv@autocomplete@unit\fi + }% +} +\begingroup +\catcode`P=12 \lccode`P=112 +\catcode`T=12 \lccode`T=116 +\lowercase{\endgroup + \def\skv@autocomplete@strippoint#1#2{% + \def\reserved@a##1#1\relax{\def#2{##1}}% + \def\do##1.##2PT#1{% + \ifx\do##1\else##1% + \if\skv@autocomplete@keephighdecimals + \ifdim.##2pt>.5pt\relax##2\fi + \else + \if\skv@autocomplete@keepdecimals + \ifnum##2>\skvz@.##2\fi + \fi + \fi + \unexpanded{#1}% + \expandafter\do + \fi + }% + \edef#2{\expandafter\do#2#1\do.PT#1}% + \expandafter\reserved@a#2\relax + } +} +% \skv@autocomplete@restore{<parser>}{<guide>}{<listcmd>} +% +% Restore guide and, in the case of dimension sequences, 'original unit'. +% +\skvrobustdef*\skv@autocomplete@restore#1#2#3{% + \if\skv@preandpostdotspresent + % Leave it: <predots> and <postdots> will be restored in + % \skv@autocompletelist@g. + \else + \def\do##1#1{% + \ifx\do##1\else + \edef\skvcompletedlist{% + \skvaddlist{#1}\skvcompletedlist + \if\skv@autocomplete@growleft + ##1\skv@autocomplete@origunit\unexpanded{#2}% + \else + \unexpanded{#2}##1\skv@autocomplete@origunit + \fi + }% + \expandafter\do + \fi + }% + \expandafter\do#3#1\do#1% + \fi +} + + +\skv@for@restorecatcodes +\endinput diff --git a/macros/latex/contrib/skeyval/tex/skeyval-ltxcmds.tex b/macros/latex/contrib/skeyval/tex/skeyval-ltxcmds.tex new file mode 100644 index 0000000000..30accb688a --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-ltxcmds.tex @@ -0,0 +1,316 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-ltxcmds.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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\begingroup% +\let\e\endlinechar\edef\p{\the\e}\e13\edef\n{\the\catcode\e}% +\xdef\skv@ltxcmds@restorecatcodes{% + \let\noexpand\skv@ltxcmds@restorecatcodes\relax% + \endlinechar=\p\relax\catcode\number\e=\n\relax% + \catcode\string`\noexpand`\string=\the\catcode\string``\relax% + \catcode\string`\noexpand=\string=\the\catcode\string`=\relax% +}% +\endgroup% +\endlinechar\string=13\relax% +\catcode13\string=5\relax% +\catcode\string``\string=12\relax +\catcode\string`=\string=12\relax +\long\def\skvgobble#1{} +\def\do#1{\catcode`\expandafter\skvgobble\string#1=\the\catcode`#1\relax} +\edef\skv@ltxcmds@restorecatcodes{% + \unexpanded\expandafter{\skv@ltxcmds@restorecatcodes}% + \do\ \do\%\do\@\do\(\do\)\do\[\do\]\do\|\do\:\do\;\do\,\do\.% + \do\>\do\<\do\!\do\*\do\+\do\-\do\^\do\_\do\&\do\$\do\~\do\'% + \do\"\do\ +} +\catcode`\ =10\relax +\catcode`\@=11 \catcode`\(=12 \catcode`\)=12 \catcode`\[=12 +\catcode`\]=12 \catcode`\|=12 \catcode`\:=12 \catcode`\;=12 +\catcode`\,=12 \catcode`\*=12 \catcode`\+=12 \catcode`\-=12 +\catcode`\<=12 \catcode`\>=12 \catcode`\!=12 \catcode`\.=12 +\catcode`\^=07 \catcode`\_=08 \catcode`\&=04 \catcode`\$=03 +\catcode`\~=13 \catcode`\%=14 \catcode`\'=12 \catcode`\"=12 + +\csname skeyval-ltxcmds-loaded\endcsname +\expandafter\let\csname skeyval-ltxcmds-loaded\endcsname\endinput + +\message{skeyval-ltxcmds 2013/05/15 v1.3 LaTeX header for skeyval (AM)} + +\countdef\m@ne=22 \m@ne=-1 +\def\wlog{\immediate\write\m@ne} +\countdef\count@=255 +\dimendef\dimen@=0 +\dimendef\dimen@i=1 +\dimendef\dimen@ii=2 +\skipdef\skip@=0 +\toksdef\toks@=0 +\newif\if@tempswa +\newcount\@tempcnta +\newcount\@tempcntb +\newdimen\@tempdima +\newdimen\@tempdimb +\newdimen\@tempdimc +\newbox\@tempboxa +\newskip\@tempskipa +\newskip\@tempskipb +\newtoks\@temptokena +\def\@nnil{\@nil} +\def\@empty{} +\def\space{ } +\def\@space{ } +\long\def\@firstofone#1{#1} +\let\@iden\@firstofone +\long\def\@firstoftwo#1#2{#1} +\long\def\@secondoftwo#1#2{#2} +\long\def\@gobble#1{} +\long\def\@gobbletwo#1#2{} +\lowercase{\def\@gobblespace} {} +\def\@nameuse#1{\csname#1\endcsname} +\def\@namedef#1{\expandafter\def\csname#1\endcsname} +\let\l@ngrel@x\relax +\def\loop#1\repeat{% + \def\iterate{#1\relax\expandafter\iterate\fi}% + \iterate\let\iterate\relax +} +\let\repeat\fi +\protected\def\newif#1{% + \count@\escapechar + \escapechar\m@ne + \let#1\iffalse + \ltx@booldef#1\iftrue + \ltx@booldef#1\iffalse + \escapechar\count@ +} +\protected\def\ltx@booldef#1#2{% + \expandafter\def\csname\expandafter\@gobbletwo\string#1% + \expandafter\@gobbletwo\string#2\endcsname{\let#1#2}% +} +\long\protected\def\@ifnextchar#1#2#3{% + \let\reserved@d=#1% + \def\reserved@a{#2}% + \def\reserved@b{#3}% + \futurelet\@let@token\@ifnch +} +\def\@ifnch{% + \ifx\@let@token\@sptoken + \let\reserved@c\@xifnch + \else + \ifx\@let@token\reserved@d + \let\reserved@c\reserved@a + \else + \let\reserved@c\reserved@b + \fi + \fi + \reserved@c +} +\lowercase{\lowercase{\let\@sptoken= } } +\lowercase{\def\@xifnch} {\futurelet\@let@token\@ifnch} +\let\kernel@ifnextchar\@ifnextchar +\long\def\@testopt#1#2{\kernel@ifnextchar[{#1}{#1[{#2}]}} +\protected\def\@expandtwoargs#1#2#3{% + \edef\reserved@a{\noexpand#1{#2}{#3}}\reserved@a +} +\edef\@backslashchar{\expandafter\@gobble\string\\} +\newif\ifin@ +\def\strip@prefix#1>{} +\protected\def\@onelevel@sanitize#1{% + \edef#1{\expandafter\strip@prefix\meaning#1}% +} +\long\protected\def\g@addto@macro#1#2{% + \begingroup + \toks@\expandafter{#1#2}% + \xdef#1{\the\toks@}% + \endgroup +} +% In case we call \@latex@error, etc, without LaTeX: +\newwrite\@unused +\def\typeout#1{% + \begingroup + \let\protect\string + \immediate\write\@unused{#1}% + \endgroup +} +\let\MessageBreak\relax +\protected\def\GenericInfo#1#2{% + \begingroup + \def\MessageBreak{^^J#1}% + \let\protect\string + \immediate\write\m@ne{#2\on@line.}% + \endgroup +} +\protected\def\GenericWarning#1#2{% + \begingroup + \def\MessageBreak{^^J#1}% + \let\protect\string + \immediate\write\@unused{^^J#2\on@line.^^J}% + \endgroup +} + +\begingroup +\lccode`\@=`\ % +\lccode`\~=`\ % +\lccode`\}=`\ % +\lccode`\{=`\ % +\lccode`\T=`\T% +\lccode`\H=`\H% +\catcode`\ =11\relax% +\lowercase{\endgroup% +\protected\def\GenericError#1#2#3#4{% +\begingroup% +\immediate\write\@unused{}% +\def\MessageBreak{^^J}% +\let\protect\string% +\edef% +\@err@ % \@err@ +{{#4}}% +\errhelp +\@err@ % \@err@ +\let +\@err@ % \@err@ +\@empty +\def\MessageBreak{^^J#1}% +\def~{\errmessage{% +#2.^^J^^J% +#3^^J% +Type H <return> for immediate help% +\@err@ % \@err@ +}}% +~% +\endgroup}}% + +\def\PackageError#1#2#3{% + \GenericError{% + (#1)\@spaces\@spaces\@spaces\@spaces + }{% + Package #1 Error: #2% + }{% + See the #1 package documentation for explanation.% + }{#3}% +} +\def\PackageWarning#1#2{% + \GenericWarning{% + (#1)\@spaces\@spaces\@spaces\@spaces + }{% + Package #1 Warning: #2% + }% +} +\def\PackageWarningNoLine#1#2{% + \PackageWarning{#1}{#2\@gobble}% +} +\def\PackageInfo#1#2{% + \GenericInfo{% + (#1) \@spaces\@spaces\@spaces + }{% + Package #1 Info: #2% + }% +} +\def\ClassError#1#2#3{% + \GenericError{% + (#1) \space\@spaces\@spaces\@spaces + }{% + Class #1 Error: #2% + }{% + See the #1 class documentation for explanation.% + }{#3}% +} +\def\ClassWarning#1#2{% + \GenericWarning{% + (#1) \space\@spaces\@spaces\@spaces + }{% + Class #1 Warning: #2% + }% +} +\def\ClassWarningNoLine#1#2{% + \ClassWarning{#1}{#2\@gobble}% +} +\def\ClassInfo#1#2{% + \GenericInfo{% + (#1) \space\space\@spaces\@spaces + }{% + Class #1 Info: #2% + }% +} +\def\@latex@error#1#2{% + \GenericError{% + \space\space\space\@spaces\@spaces\@spaces + }{% + LaTeX Error: #1% + }{% + See the LaTeX manual or LaTeX Companion for explanation.% + }{#2}% +} +\let\@latexerr\@latex@error +\def\@latex@warning#1{% + \GenericWarning{% + \space\space\space\@spaces\@spaces\@spaces + }{% + LaTeX Warning: #1% + }% +} +\let\@warning\@latex@warning +\def\@warningnoline#1{\@latex@warning{#1\@gobble}} +\def\@latex@info#1{% + \GenericInfo{% + \@spaces\@spaces\@spaces + }{% + LaTeX Info: #1% + }% +} +\def\@infonoline#1{\@latex@info{#1\@gobble}} +\errorcontextlines=\m@ne +\ifnum\inputlineno=\m@ne + \let\on@line\@empty +\else + \def\on@line{ on input line \the\inputlineno} +\fi + +\def\@spaces{\space\space\space\space} +\def\@ehd{% + There is a problem here. Investigate it before + \MessageBreak proceeding. Try typing <return> + to proceed.\MessageBreak If that doesn't work, + type X <return> to quit. +} +\def\showoverfull{\tracingonline\@ne} +\def\loggingoutput{% + \tracingoutput\@ne\showboxbreadth\maxdimen + \showboxdepth\maxdimen\errorstopmode +} +\def\loggingall{% + \tracingcommands\tw@\tracingstats\tw@ + \tracingpages\@ne\tracinglostchars\@ne + \tracingmacros\tw@\tracingparagraphs\@ne + \tracingrestores\@ne\errorcontextlines\maxdimen + \loggingoutput +} +\def\tracingall{\loggingall\showoverfull} + +\skv@ltxcmds@restorecatcodes +\endinput + +%% End of file 'skeyval-ltxcmds.tex'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval-ltxpatch.sty b/macros/latex/contrib/skeyval/tex/skeyval-ltxpatch.sty new file mode 100644 index 0000000000..fee051aa61 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-ltxpatch.sty @@ -0,0 +1,444 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-ltxpatch.sty', version 1.3, 2013/05/15. % +% % +% The rationale for this package: the patch in 'xkvltxp' package isn't % +% strong enough. % +% % +% 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\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@ltxp@restorecodes{\do35,64,123,125,61,59,13,\do,}% +} +\skv@prova + +\edef\skv@ltxp@restorecodes{% + \unexpanded\expandafter{\skv@ltxp@restorecodes}% + \endlinechar\the\endlinechar\relax +} +\endlinechar13 % +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx\do#1\else + \edef\skv@ltxp@restorecodes{% + \unexpanded\expandafter{\skv@ltxp@restorecodes}% + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\NeedsTeXFormat{LaTeX2e}[2011/06/27] +\ProvidesPackage{skeyval-ltxpatch} + [2013/05/15 v1.3 Patches for LaTeX2e kernel options processing (AM)] + +\ifdefined\skvz@\else\newdimen\skvz@\fi +\def\@space{ } +\protected\def\skvltxptracingall{% + \tracinggroups\@ne\tracingifs\@ne\loggingall\tracingassigns\@ne + \tracingonline\skvz@\tracingifs\skvz@ +} +\let\skvltxploggingall\skvltxptracingall + +\def\skvltxp@cmptwo#1#2\@@a#3#4\@@b#5{% + \csname @#5#1#3first\else second\fi oftwo\endcsname +} + +\begingroup +\lccode`\&=0 \catcode`\&=7 \lccode`\!=0 \catcode`\!=8 +\lowercase{\endgroup + \def\skvltxp@ifleadspace#1{\skvltxp@ifl@adspace!#1! &}% + \def\skvltxp@ifl@adspace#1! #2&{% + \skvltxp@ifnull{#2}\@secondoftwo\@firstoftwo + } + \def\skvltxp@ifnull#1{\skvltxp@cmptwo#1&\@@a&\@@b\ifx} + \def\skvltxp@ifx#1#2{\skvltxp@cmptwo#1&\@@a#2!\@@b\ifx} + \def\skvltxp@trimspace#1{\skvltxp@trimspace@a.#1& &} + \def\skvltxp@trimspace@a#1 &{\skvltxp@trimspace@b#1&} + \def\skvltxp@trimspace@b#1{% + \unexpanded\expandafter{% + \romannumeral0% + \expandafter\skvltxp@ifleadspace\expandafter{\@gobble#1}{% + \@gobble#1% + }{% + \expandafter\@space\@gobble#1% + }% + }% + } +} +\protected\def\skvltxp@despacecontent#1{% + \edef#1{\expandafter\skvltxp@trimspace\expandafter{#1}}% +} + +% \skvltxp@setupnormalizer{<parser>}<normalizer.cmd> +% <normalizer.cmd> should not be used for normalizing keyval lists. +\protected\def\skvltxp@setupnormalizer#1#2{% + \begingroup + \lccode`\~=`#1\relax + \lowercase{\endgroup + \def#2##1{% + \unexpanded\expandafter + {\romannumeral\skvltxp@activeparser#1##1#1~\skv@nil}% + }% + \def\skvltxp@activeparser##1~##2\skv@nil{% + \skvltxp@ifnull{##2} + {\skvltxp@spaceparser##1 #1\skv@nil} + {\skvltxp@activeparser##1#1##2\skv@nil}% + }% + }% + \def\skvltxp@spaceparser##1 #1##2\skv@nil{% + \skvltxp@ifnull{##2} + {\skvltxp@parserspace##1#1 \skv@nil} + {\skvltxp@spaceparser##1#1##2\skv@nil}% + }% + \def\skvltxp@parserspace##1#1 ##2\skv@nil{% + \skvltxp@ifnull{##2} + {\skvltxp@doubleparser##1#1#1\skv@nil} + {\skvltxp@parserspace##1#1##2\skv@nil}% + }% + \def\skvltxp@doubleparser##1#1#1##2\skv@nil{% + \skvltxp@ifnull{##2}{% + \skvltxp@ifnull{##1}{0 }{0\expandafter\@space\@gobble##1}% + }{% + \skvltxp@doubleparser##1#1##2\skv@nil + }% + }% +} +\skvltxp@setupnormalizer{,}\skvltxp@commanormalize + +\newif\ifbreakkerpaloop +\def\kerpaloopdepth{0} +% \kerpaloop{<parser>}{<list>}{<1.parameter.callback>} +% \kerpaloop*{<parser>}{<listcmd>}{<1.parameter.callback>} +\protected\def\kerpaloop{\@ifstar{\skv@kerpaloop1}{\skv@kerpaloop0}} +\def\skv@kerpaloop#1#2#3#4{% + \xdef\kerpaloopdepth{\the\numexpr\kerpaloopdepth+1}% + \def\skv@k@rpaloop{kerpaloop@\romannumeral\kerpaloopdepth}% + \breakkerpaloopfalse + \@namedef{\skv@k@rpaloop}##1#2{% + \skvltxp@ifx{##1}\kerpaloop{% + \breakkerpaloopfalse + }{% + #4\relax + \ifbreakkerpaloop + \breakkerpaloopfalse + \begingroup + \@namedef\skv@k@rpaloop####1\kerpaloop#2{\endgroup}% + \fi + \@nameuse\skv@k@rpaloop + }% + }% + \edef\reserved@a{% + \if0#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi + {\unexpanded{#3}}{\unexpanded\expandafter{#3}}% + }% + \ifx\reserved@a\@empty\expandafter\@gobble\else\expandafter\@iden\fi + {% + \csname\skv@k@rpaloop\expandafter\endcsname + \reserved@a#2\kerpaloop#2% + }% + \xdef\kerpaloopdepth{\the\numexpr\kerpaloopdepth-1}% +} +% \skvltxp@getoptionnames<cmd.in><cmd.out> +\def\skvltxp@getoptionnames#1#2{% + \def#2{}% + \edef#1{\expandafter\skvltxp@commanormalize\expandafter{#1}}% + \def\do##1=##2=##3\skv@nil{% + \edef#2{\ifx\@empty#2\else#2,\fi##1}% + }% + \kerpaloop*{,}#1{\do##1==\skv@nil}% +} +\def\@ptionlist#1{% + \@ifundefined{opt@#1}{}{\unexpanded\expandafter + \expandafter\expandafter{\csname opt@#1\endcsname}}% +} +\def\@if@ptions#1#2#3{% + \@expandtwoargs\@if@pti@ns{\@ptionlist{#2.#1}}{\unexpanded{#3}}% +} +\def\@if@pti@ns#1#2{% +% \skvltxp@setupnormalizer{,}% + \edef\skv@options@i{\skvltxp@commanormalize{#1}}% + \edef\skv@options@ii{\skvltxp@commanormalize{#2}}% + \ifx\skv@options@i\skv@options@ii + \def\skv@tempa{\expandafter\@firstoftwo\@gobbletwo}% + \else + \ifx\skv@options@ii\@empty + \ifx\skv@options@i\@empty + \def\skv@tempa{\expandafter\@firstoftwo\@gobbletwo}% + \else + \def\skv@tempa{\expandafter\@secondoftwo\@gobbletwo}% + \fi + \else + \let\skv@tempa\@iden + \fi + \fi + \skv@tempa{% + \let\skv@tempa\@firstoftwo + \def\do##1{% + \@expandtwoargs\in@{,\detokenize{##1},}% + {,\detokenize\expandafter{\skv@options@i},}% + \ifin@\else\let\skv@tempa\@secondoftwo\breakkerpalooptrue\fi + }% + \kerpaloop*{,}\skv@options@ii{\do{##1}}% + }% + \skv@tempa +} +\def\@onefilewithoptions#1[#2][#3]#4{% + \@pushfilename + \xdef\@currname{#1}% + \global\let\@currext#4% + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@empty + \let\CurrentOption\@empty + \@reset@ptions + \makeatletter + \@ifl@aded\@currext{#1}{% + \@if@ptions\@currext{#1}{#2}{}{% + % Merge the two options lists: + \let\skv@tempa\skv@options@i + \skvltxp@ifnull{#2}{}{% + \def\do##1{% + \@expandtwoargs\in@{,\detokenize{##1},}% + {,\detokenize\expandafter{\skv@tempa},}% + \ifin@\else + \edef\skv@tempa{% + \ifx\skv@tempa\@empty\else + \detokenize\expandafter{\skv@tempa},\fi + \detokenize{##1}% + }% + \fi + }% + \kerpaloop*{,}\skv@options@ii{\do{##1}}% + }% + \@onelevel@sanitize\skv@options@i + \@onelevel@sanitize\skv@options@ii + \@latex@error{Option clash for \@cls@pkg\@space #1}{% + The package '#1' has already been loaded + with \ifx\skv@options@i\@empty no options\MessageBreak\else + options:\MessageBreak\@space\@space[\skv@options@i]\fi. + \MessageBreak There has now been an attempt to load it with + \ifx\skv@options@ii\@empty no options\else options + \MessageBreak\@space\@space[\skv@options@ii]\fi. + \MessageBreak Adding the global options\MessageBreak + \@space\@space'\skv@tempa'\MessageBreak + to your \noexpand\documentclass declaration may fix this problem. + }% + }% + }{% + \@pass@ptions\@currext{#2}{#1}% + \global\expandafter + \let\csname ver@\@currname.\@currext\endcsname\@empty + \InputIfFileExists{\@currname.\@currext}{}{% + \@missingfileerror\@currname\@currext + }% + \let\@unprocessedoptions\@@unprocessedoptions + \csname\@currname.\@currext-h@@k\endcsname + \expandafter\let\csname\@currname.\@currext-h@@k\endcsname\@undefined + \@unprocessedoptions + }% + \def\skv@tempa##1 v##2 ##3\@nil##4{% + \edef##4{'##1\ifx\\##2\\\else\@space v##2\fi'}% + }% + \skv@tempa#3 v \@nil\skv@tempb + \expandafter\expandafter\expandafter\skv@tempa + \csname ver@#1.\@currext\expandafter\endcsname + \@space v \@nil\skv@tempc + \@ifl@ter\@currext{#1}{#3}{}{% + \@expandtwoargs\AtEndDocument{% + \noexpand\@latex@warning@no@line{% + You requested,\on@line, version\MessageBreak + \skv@tempb\@space of \@cls@pkg\@space #1,\MessageBreak + but only version \skv@tempc\MessageBreak is available + }% + }{}% + }% + \ifx\@currext\@clsextension + \let\LoadClass\@twoloadclasserror + \fi + \@popfilename + \@reset@ptions +} +\protected\def\ProcessOptions{% + \let\ds@\@empty + \edef\@curroptions{\@ptionlist{\@currname.\@currext}}% + \ifx\@classoptionslist\relax + \let\@classoptionslist@a\relax + \else + % LaTeX can't process options with values. So we filter out + % option values: + \skvltxp@getoptionnames\@classoptionslist\@classoptionslist@a + \fi + \skvltxp@getoptionnames\@curroptions\@curroptions@a + \@ifstar\@xprocess@ptions\@process@ptions +} +\def\@process@ptions{% + \@for\CurrentOption:=\@declaredoptions\do{% + \ifx\CurrentOption\@empty\else + \@expandtwoargs\in@{,\CurrentOption,}{% + ,\ifx\@currext\@clsextension\else + \ifx\@classoptionslist@a\relax\else\@classoptionslist@a,\fi\fi + \@curroptions@a,}% + \ifin@ + \@use@ption + \expandafter\let\csname ds@\CurrentOption\endcsname\@empty + \fi + \fi + }% + \@process@pti@ns +} +\def\@xprocess@ptions{% + \ifx\@currext\@clsextension\else + \@for\CurrentOption:=\@classoptionslist@a\do{% + \ifx\CurrentOption\@empty\else + \@expandtwoargs\in@{,\CurrentOption,}{,\@declaredoptions,}% + \ifin@ + \@use@ption + \expandafter\let\csname ds@\CurrentOption\endcsname\@empty + \fi + \fi + }% + \fi + \@process@pti@ns +} +\def\@process@pti@ns{% + \@for\CurrentOption:=\@curroptions@a\do{% + \@ifundefined{ds@\CurrentOption}{\@use@ption\default@ds}{\@use@ption}% + }% + \@for\CurrentOption:=\@declaredoptions\do{% + \expandafter\let\csname ds@\CurrentOption\endcsname\relax + }% + \let\CurrentOption\@empty + \let\@fileswith@pti@ns\@@fileswith@pti@ns + \AtEndOfPackage{\let\@unprocessedoptions\relax}% +} +\def\@pass@ptions#1#2#3{% + \def\reserved@a{#2}% + \def\reserved@b{\CurrentOption}% + \ifx\reserved@a\reserved@b + \@ifundefined{opt@#3.#1}{% + \@temptokena\expandafter{#2}% + }{% + \@temptokena\expandafter\expandafter\expandafter + {\csname opt@#3.#1\endcsname}% + \@temptokena\expandafter\expandafter\expandafter{% + \expandafter\the\expandafter\@temptokena\expandafter,#2}% + }% + \else + \@ifundefined{opt@#3.#1}{% + \@temptokena{#2}% + }{% + \@temptokena\expandafter\expandafter\expandafter + {\csname opt@#3.#1\endcsname}% + \@temptokena\expandafter{\the\@temptokena,#2}% + }% + \fi + \expandafter\xdef\csname opt@#3.#1\endcsname{\the\@temptokena}% +} +\def\OptionNotUsed{% + \ifx\@currext\@clsextension + \let\reserved@a\CurrentOption + \@onelevel@sanitize\reserved@a + \xdef\@unusedoptionlist{% + \@unusedoptionlist + \ifx\@unusedoptionlist\@empty\else,\fi + \reserved@a + }% + \fi +} + +\def\@use@ption{% + \begingroup + \skvltxp@despacecontent\CurrentOption + \@onelevel@sanitize\reserved@a + \@onelevel@sanitize\@unusedoptionlist + \def\skv@tempa{}% + \def\do##1=##2=##3\@nil{% + \edef\reserved@a{\skvltxp@trimspace{##1}}% + }% + \kerpaloop*{,}\@unusedoptionlist{% + \do##1==\@nil + \ifx\reserved@a\CurrentOption\else + \edef\skv@tempa{% + \skv@tempa\ifx\skv@tempa\@empty\else,\fi##1% + }% + \fi + }% + \global\let\@unusedoptionlist\skv@tempa + \endgroup + \csname ds@\CurrentOption\endcsname +} +\def\@fileswith@pti@ns#1[#2]#3[#4]{% + \ifx\@clsextension#1% + \ifx\@classoptionslist\relax + \@temptokena{#2}% + \xdef\@classoptionslist{\the\@temptokena}% + \def\reserved@a{% + \@onefilewithoptions#3[#2][#4]#1\@documentclasshook + }% + \else + \def\reserved@a{\@onefilewithoptions#3[#2][#4]#1}% + \fi + \else + \@temptokena{#2}% + \def\reserved@b##1,{% + \ifx\@nnil##1\relax\else + \ifx\relax##1\relax\else + \noexpand\@onefilewithoptions##1% + [\the\@temptokena][#4]\noexpand\@pkgextension + \fi + \expandafter\reserved@b + \fi + }% + \edef\reserved@a{\zap@space#3 \@empty}% + \edef\reserved@a{\expandafter\reserved@b\reserved@a,\@nnil,}% + \fi + \reserved@a +} +\let\@@fileswith@pti@ns\@fileswith@pti@ns + +\skv@ltxp@restorecodes +\endinput + +%% End of file `skeyval-ltxpatch.sty'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval-pstkey.sty b/macros/latex/contrib/skeyval/tex/skeyval-pstkey.sty new file mode 100644 index 0000000000..caa843aafe --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-pstkey.sty @@ -0,0 +1,87 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-pstkey.sty', 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\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@pst@restorecodes{\do35,64,123,125,61,59,13,\do,}% +} +\skv@prova + +\edef\skv@pst@restorecodes{% + \unexpanded\expandafter{\skv@pst@restorecodes}% + \endlinechar\the\endlinechar\relax +} +\endlinechar13 % +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx\do#1\else + \edef\skv@pst@restorecodes{% + \unexpanded\expandafter{\skv@pst@restorecodes}% + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\NeedsTeXFormat{LaTeX2e}[2011/06/27] +\ProvidesPackage{skeyval-pstkey} + [2013/05/15 v1.3 LaTeX/package wrapper for skeyval-pstkey.tex (AM)] + +\expandafter\ifx\csname skeyval-pstkey-loaded\endcsname +\endinput\else + \input skeyval-pstkey +\fi +\skvdeclareoption*{% + \PackageWarning{skeyval-pstkey} + {Unknown option '\CurrentOption' ignored}% +} +\skvprocessoptions +\skv@pst@restorecodes +\endinput + +%% End of file `skeyval-pstkey.sty'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval-pstkey.tex b/macros/latex/contrib/skeyval/tex/skeyval-pstkey.tex new file mode 100644 index 0000000000..18a97c4e72 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-pstkey.tex @@ -0,0 +1,94 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-pstkey.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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\begingroup% +\let\e\endlinechar\edef\p{\the\e}\e13\edef\n{\the\catcode\e}% +\xdef\skv@pst@restorecatcodes{% + \let\noexpand\skv@pst@restorecatcodes\relax% + \endlinechar=\p\relax\catcode\number\e=\n\relax% + \catcode\string`\noexpand`\string=\the\catcode\string``\relax% + \catcode\string`\noexpand=\string=\the\catcode\string`=\relax% +}% +\endgroup% +\endlinechar\string=13\relax% +\catcode13\string=5\relax% +\catcode\string``\string=12\relax +\catcode\string`=\string=12\relax +\def\skvgobble#1{} +\def\do#1{\catcode`\expandafter\skvgobble\string#1=\the\catcode`#1\relax} +\edef\skv@pst@restorecatcodes{% + \unexpanded\expandafter{\skv@pst@restorecatcodes}% + \do\ \do\%\do\@\do\(\do\)\do\[\do\]\do\|\do\:\do\;\do\,\do\.% + \do\>\do\<\do\!\do\*\do\+\do\-\do\^\do\_\do\&\do\$\do\~\do\'% + \do\"\do\ +} +\catcode`\ =10\relax +\catcode`\@=11 \catcode`\(=12 \catcode`\)=12 \catcode`\[=12 +\catcode`\]=12 \catcode`\|=12 \catcode`\:=12 \catcode`\;=12 +\catcode`\,=12 \catcode`\*=12 \catcode`\+=12 \catcode`\-=12 +\catcode`\<=12 \catcode`\>=12 \catcode`\!=12 \catcode`\.=12 +\catcode`\^=07 \catcode`\_=08 \catcode`\&=04 \catcode`\$=03 +\catcode`\~=13 \catcode`\%=14 \catcode`\'=12 \catcode`\"=12 + +\csname skeyval-pstkey-loaded\endcsname +\expandafter\let\csname skeyval-pstkey-loaded\endcsname\endinput + +\def\skv@pst@afterfi#1\fi{\fi#1} +\def\skv@pst@afterelse#1\else#2\fi{\fi#1} + +\ifx\ProvidesFile\@undefined\skv@pst@afterelse + \message{File 'skeyval-pstkey.tex' 2013/05/15 v1.3 + PSTricks specialization of skeyval package (AM)} + \input skeyval-core +\else\skv@pst@afterfi + \ProvidesFile{skeyval-pstkey.tex} + [2013/05/15 v1.3 PSTricks specialization of skeyval (AM)] + \@addtofilelist{skeyval-pstkey.tex} + \@ifpackageloaded{skeyval}{}{\RequirePackage{skeyval}} +\fi + +\def\pst@famlist{} +\def\pst@addfams#1{% + \skvcommaloop{#1}\skv@tempa{% + \skvxifin{,\skv@tempa,}{,\pst@famlist,}{}{% + \edef\pst@famlist{\pst@famlist,\skv@tempa}% + }% + }% +} +\def\psset{\skvexpandbracenext{\skv@testopt\pss@t}\pst@famlist} +\def\pss@t[#1]#2{\skvsetkeys+[psset]{#1}{#2}\ignorespaces} +\def\@psset#1,\@nil{% + \edef\skv@tempa{\skvsetkeys+[psset]{\pst@famlist}}% + \skv@tempa{#1}% +} + +\skv@pst@restorecatcodes +\endinput + +%% End of file 'skeyval-pstkey.tex'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval-testclass.cls b/macros/latex/contrib/skeyval/tex/skeyval-testclass.cls new file mode 100644 index 0000000000..771d60160f --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-testclass.cls @@ -0,0 +1,321 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-testclass.cls', 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\ProvidesClass{skeyval-testclass}[2013/05/15 v1.3 skeyval test class (AM)] +\NeedsTeXFormat{LaTeX2e}[2011/06/27] +\@ifpackageloaded{skeyval}{}{\RequirePackage{skeyval}} +\@ifpackageloaded{xcolor}{}{\RequirePackage{xcolor}} +\@ifpackageloaded{pifont}{}{\RequirePackage{pifont}} +\@ifpackageloaded{tikz}{}{\RequirePackage{tikz}} + +\ifdefined\skvtestcnt\else\newcount\skvtestcnt\fi +\skvatbegineverypage{\skvcl@maybewm} +\let\skvcl@maybewm\relax +\def\skvcl@watermark{% + \tikz[overlay,remember picture]{% + \node [\skvcl@watermarkcolor,inner sep=0pt,rotate=45,align=center, + scale=\skvcl@watermarkscale,font=\fontseries{b}\fontfamily{bch}% + \fontsize{\skvcl@fontsize@a}{\skvcl@fontsize@b}\selectfont] at + ([shift={(\skvcl@watermarkxshift,-\skvcl@watermarkyshift)}]% + current page.north west){% + \skvcl@watermarktext + }; + }% +} + +\skvrobustdef*\skvcl@titlecmds{% + \setcounter{footnote}{0}% + \def\do##1{\global\let##1\@empty}% + \do\@thanks\do\@author\do\@date\do\@title + \def\do##1{\global\let##1\relax}% + \do\thanks\do\maketitle\do\@maketitle\do\title\do\author\do + \date\do\and\let\do\relax + \let\@fnsymbol\orig@fnsymbol +} +\let\orig@fnsymbol\@fnsymbol + +\skvrobustredef*\skv@fnsymbol#1{% + \unskip\ifcase#1\or\ding{'106}\or\ding{'110}\or\ding{'111}\or + \ding{'112}\or\ding{'113}\or\ding{'114}\or\ding{'121}\or + \ding{'122}\else\@ctrerr\fi +} +\skvrobustdef*\skvcl@newfootnotestyle{% + \def\@makefnmark{% + \rlap{\@textsuperscript{\normalfont\skv@fnsymbol\@thefnmark}}% + }% + \long\def\@makefntext##1{% + \parindent1em\relax\noindent + \hb@xt@1.8em{\hss\@textsuperscript{% + \normalfont\skv@fnsymbol\@thefnmark}% + }% + ##1% + }% +} +\skvnewlet\skv@sav@footnote\footnote +\skvnewlet\skv@sav@thanks\thanks +\AtEndOfPackage{% + \skvdisablekeys[SKVC]{title}{insertwatermark}% +} +\AtBeginDocument{% + \def\footnote{\unskip\skv@sav@footnote}% + \def\thanks{\unskip\skv@sav@thanks}% + \skvdisablekeys[SKVC]{textemphasis}{dummy-option}% + \skvdisablekeys[SKVC]{title}{% + maketitlepage,firstpagenumber,pagenumberstyle + }% +} +\let\skvcl@settitlepageno\@gobble + +% Example of option and nonoption keys: +\skvordkeys[KV]{fam}{testkey1,testkey2}[value]{} +\skvoptionkeys[KV]{fam}{testkey1} +\skvnonoptionkeys[KV]{fam}{testkey2} + +% LaTeX will zap spaces in key name, anyway. So our options don't have +% spaces within themselves. + +\directkeys*{% + .every unknown option={ + \PassOptionsToClass{\CurrentOption}{article} + }, + .message type when disabled option is invoked=error, + .prefix=SKVC, + % Extended text emphasis: + .family=textemphasis, + .hp=xem@, + .new options={ + .initialize keys after define, + .ord/{testkey1,testkey2,testkey3}/default, + .bool/useitalic/true, + .bool/usebold/true, + .bool/usecolor/true, + .cmd/color/red, + .bool/make-textemphasis-inactive/true/ + \ifskvindef\else + \skvifbool xem@make-textemphasis-inactive\then + \PackageInfo{skeyval-testclass} + {Extended text emphasis has been made inactive} + \let\xem\emph + \fi + \fi + , + .ord/dummy-option/\@nil, + }, + .links={ + useitalics/useitalic, + usecolour/usecolor, + colour/color, + }, + .option keys = {testkey1}, + .nonoption keys = {testkey2,testkey3}, + % Modifying the article title: + .change family=title, + .hp=skvcl@, + .new options={ + .bool/putdate/true, + .cmd/datefont/\large, + .bool/clearpage/true, + .bool/maketitlepage/true/ + \skvifbool skvcl@maketitlepage\then + \ifskvindef\else + \PassOptionsToClass{titlepage}{article} + \fi + \fi + , + .cmd/firstpagenumber/\@nil/ + \ifx\skvcl@firstpagenumber\@nnil\else + \skvensureinteger{page number}\skvcl@firstpagenumber + \AtBeginDocument{% + \gdef\pagenumbering##1{% + \global\c@page\skvcl@firstpagenumber\relax + \gdef\thepage{\csname @##1\endcsname\c@page}% + }% + % Default: + \pagenumbering{arabic}% + \let\skvcl@settitlepageno\@iden + }% + \fi + , + .cmd/pagenumberstyle/\@nil/ + \ifx\skvcl@pagenumberstyle\@nnil\else + \skvafterbegindocument{\pagenumbering{#1}}% + \fi + , + .cmd/pretitleskip/10pt, + .cmd/posttitleskip/10pt, + .cmd/authorfont/\large, + .cmd/titlefont/\huge, + .bool/insertwatermark/true/ + \skvifbool skvcl@insertwatermark\then + \ifskvindef\else + \let\skvcl@maybewm\skvcl@watermark + \fi + \fi + , + .cmd/watermarkxshift/.5\paperwidth, + .cmd/watermarkyshift/.5\paperheight, + .cmd/watermarktext/DRAFT/ + \skvstripouterbraces{2}\skvcl@watermarktext + , + .cmd/fontsize/{14.4,17}/ + \def\skvcl@fontsize@a{#1}\def\skvcl@fontsize@b{#2} + , + .arg/fontsize/{#1,#2}, + .cmd/watermarkscale/5, + .cmd/watermarkcolor/gray!55, + }, + .slots={ + showdate/putdate=#1, + first-page-number/firstpagenumber=#1, + titlepage/maketitlepage=#1, + }, + % Ececute the options in the current family. If any of the options + % isn't found, report error (ie, don't save the key to the list of + % 'remaining keys'). Unknown options should be flagged immediately. + % Use '.execute options+' or '.execute options in all families' to + % execute options in multiple families. + .execute options={ + putdate,datefont,clearpage=false,pretitleskip,posttitleskip, + authorfont,titlefont,watermarkscale,firstpagenumber, + insertwatermark=false,watermarkxshift,watermarkyshift, + watermarktext,watermarkcolor,fontsize + }, + .families={textemphasis,title}, + .process options in all families, +} +%\show\skvcurrentpoxkeys + +% testkey1 is an option key: don't set it outside option list: +% \skvsetkeys[SKVC]{textemphasis}{testkey1} + +\skvrobustdef*\setupskvtestclass{% + \skvsetkeys*+[SKVC]{textemphasis,title}% +} + +% Setting up extended text emphasis: +\skvrobustdef*\setuptextemphasis#1{\skvsetkeys[SKVC]{textemphasis}{#1}} +\newcommand*\xem[1]{\xem@emph{\xem@bold{\xem@colortext{\xem@color}{#1}}}} +\newcommand*\xem@emph{% + \skvifbool xem@useitalic\then + \expandafter\emph + \else + \expandafter\@firstofone + \fi +} +\newcommand*\xem@bold{% + \skvifbool xem@usebold\then + \expandafter\textbf + \else + \expandafter\@firstofone + \fi +} +\newcommand*\xem@colortext{% + \skvifbool xem@usecolor\then + \expandafter\textcolor + \else + \expandafter\@secondoftwo + \fi +} + +\LoadClass{article} + +\if@titlepage + \renewcommand\maketitle{% + \ifskvcl@clearpage + \clearpage + \fi + \begin{titlepage}% + \skvcl@newfootnotestyle + \let\footnotesize\small + \let\footnoterule\relax + \let\footnote\thanks + \null\vfil + \vskip\skvcl@pretitleskip\relax + \begin{center}% + {\skvcl@titlefont\@title\par}% + \vskip\skvcl@posttitleskip\relax + {% + \skvcl@authorfont + \lineskip.75em\relax + \begin{tabular}[t]{c}% + \@author + \end{tabular}\par + }% + \vskip1.5em\relax + \skvifbool skvcl@putdate\then{{\skvcl@datefont\@date}}\fi + \end{center}\par + \@thanks + \vfil\null + \end{titlepage}% + \skvcl@settitlepageno{\c@page\skvcl@firstpagenumber\relax}% + \skvcl@titlecmds + } +\else + \renewcommand\maketitle{% + \par + \begingroup + \skvcl@newfootnotestyle + \if@twocolumn + \ifnum\col@number=\@ne + \@maketitle + \else + \twocolumn[\@maketitle]% + \fi + \else + \newpage + \global\@topnum\z@ + \@maketitle + \fi + \thispagestyle{plain}\@thanks + \endgroup + \skvcl@titlecmds + } + \def\@maketitle{% + \newpage + \null + \vskip\skvcl@pretitleskip\relax + \begin{center}% + \let\footnote\thanks + {\skvcl@titlefont\@title\par}% + \vskip\skvcl@posttitleskip\relax + {\skvcl@authorfont + \lineskip.5em\relax + \begin{tabular}[t]{c}% + \@author + \end{tabular}\par + }% + \vskip1em\relax + \skvifbool skvcl@putdate\then{\skvcl@datefont\@date}\fi + \end{center}% + \par + \vskip1.5em\relax + } +\fi + +\endinput diff --git a/macros/latex/contrib/skeyval/tex/skeyval-testpkg.sty b/macros/latex/contrib/skeyval/tex/skeyval-testpkg.sty new file mode 100644 index 0000000000..23a949e483 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-testpkg.sty @@ -0,0 +1,456 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-testpkg.sty', 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\ProvidesPackage{skeyval-testpkg} + [2013/05/15 v1.3 skeyval test package (AM)] +\NeedsTeXFormat{LaTeX2e} +\@ifpackageloaded{skeyval}{}{\RequirePackage{skeyval}} +\RequirePackage{graphicx} + +\skvdisabledkeysmessagetype{warning} + +%%++++++++++++++++ Passing options to packages +++++++++++++++++++%% + +\skvdeclarebooloption[SKVT]<testpkg>[skvtp@]{verbose}[true]{} + +% Declare options with default values that will be used when the options +% are called without user values. +% +\skvdeclarepassedoptionsloadpackage[SKVT]{testpkg}{% + hyperref={colorlinks,breaklinks},xcolor=svgnames +} + +%%+++++++++++++++++++ Producing arbitrary texts +++++++++++++++++%% + +% The key 'align' could have defined using \skvchoicekey, as in +% 'shadowbox' family below. +\newcommand*\justified{} +\skvordkey[SKVT]{abrtext}{align}[right]{% + \skvifcase\skvxifstrcmpTF{#1} + {left}{\def\mpfalign{flushleft}} + {right}{\def\mpfalign{flushright}} + {center}{\def\mpfalign{center}} + {justified}{\def\mpfalign{justified}} + \elsedo + \PackageError{skeyval-testpkg} + {Invalid value `#1' for align} + {You have issued an illegal value `#1' for option `align'.}% + \endif +} + +\skvzcmdkey[SKVT]{abrtext}[mpf]{text style}[\rmfamily]{} +\skvcmdkey[SKVT]{abrtext}[mpf]{author}[Karl Berry]{} +\skvzboolkey[SKVT]{abrtext}[mpf]{show author}[true]{} + +% Using pointers to expand values of keys while setting keys: +\def\alignval{right} +\def\alignvalb{\alignval} +\def\stextstyle{\sffamily} + +\skvsetkeys[SKVT]{abrtext}{ + align=.expand twice{\alignvalb}, + text style=.expand once{\stextstyle},author,show author +} + +%%+++++++++++++++++++++++++++ Citations +++++++++++++++++++++++++++%% + +% Keys defined by \skvdefinekeys are automatically initialiized when +% the option 'initialize' is true. In general, to save initialize +% key values at key definition, you need to call \skvsaveinitialvaluekeys +% before defining the keys. + +\skvsaveinitialvaluekeys[SKVT]{citation}{put frame,width,show author,author, + textcolor,fonttype,leftmargin,rightmargin,preskip,framecolor,shadecolor, + frame thickness} + +\newbox\skv@testbox +\skvdefinekeys*[SKVT]{citation}[ctt@]{% + .cmd/width/\hsize, + .zbool/show author/true, + .cmd/author//, % empty default + .zbool/put frame/true, + .cmd/framecolor/black, + .cmd/shadecolor/white, + .cmd/textcolor/black, + .zcmd/frame thickness/.4pt, + .cmd/fonttype/\normalfont, + .cmd/leftmargin/\leftmargin, + .cmd/rightmargin/\rightmargin, + .cmd/preskip/0ex, + .cmd/width/\textwidth, + .ord/dummy key/\@nil, +} +% Keys defined by \skvdefinekeys are automatically initialiized when +% the option 'initialize' is true. When \skvsaveinitialvaluekeys has been +% called for a set of keys, those keys can be initialized via +% \skvinitializekeys: +\skvinitializekeys[SKVT]{citation}[dummy key] + +\newcommand\newcitation[2][]{% + \skvsetkeys[SKVT]{citation}{#1}% + \sbox\skv@testbox{% + \ctt@fonttype + \textit{\textcolor{\ctt@textcolor}{\ctt@author}}% + }% + \list{}{% + \setlength\leftmargin{\ctt@leftmargin}% + \setlength\rightmargin{\ctt@leftmargin}% + }% + \vspace*{\ctt@preskip}% + \item\relax + \ifctt@putframe + \begingroup + \fboxrule\ctt@framethickness\relax + \fcolorbox{\ctt@framecolor}{\ctt@shadecolor}{% + \fi + \parbox{\ctt@width}{% + \ctt@fonttype\textcolor{\ctt@textcolor}{\ignorespaces#2}% + \ifctt@showauthor + \hspace*{\fill}\nolinebreak[1]% + \quad\hspace*{\fill}\finalhyphendemerits\z@\relax + \usebox\skv@testbox + \fi + }% + \ifctt@putframe}\endgroup\fi + \endlist + % Restore default values of keys: + \skvinitializekeys[SKVT]{citation}[] +} + + +%%++++++++++++++++++++++++ Shadow boxes ++++++++++++++++++++++%% + +\newdimen\shadowspread + +\skvdefinekeys*[SKVT]{shadowbox}[shb@]{% + .zbool/put frame/true, + .bool/shadow/true, + .cmd/framecolor/black, + .cmd/shadecolor/white, + .cmd/shadowcolor/gray!55, + .cmd/boxwidth/1.5cm, + .ord/framesize/.5pt/\def\currfboxrule{#1}, + .ord/shadowsize/2pt/\shadowspread=\dimexpr#1\relax, + .choice/align/center/{% + center.do=\def\curralign##1{\hfil##1\hfil}, + right.do=\def\curralign##1{\hfill##1}, + left.do=\def\curralign##1{##1\hfill}, + justified.do=\let\curralign\@iden + } +} + +\skvdefinekeys*[SKVT]{testfam}[shb@]{% + .zbool/put shadow/true,% Test zbool + .zcmd/shade color/gray!55, % Test .zcmd +} + +\skvdisablekeys[SKVT]{testfam}{put shadow, shade color} + +% Presetting and posetting keys. +% +% 1. 'Presetting keys' means set the following keys whenever +% \setkeys is called. These keys should be set BEFORE the keys +% listed in the current argument of \setkeys are set, provided +% that these keys aren't listed in the current argument of \setkeys. +% If these keys appear in the current argument of \setkeys, +% then obviously the user has new values for them or he wants +% the keys to be set with their default values. +% +% 2. 'Postsetting keys' means set the following keys whenever +% \setkeys is called. These keys should be set AFTER the keys +% listed in the current argument of \setkeys are set, provided +% that these keys aren't listed in the current argument of \setkeys. +% +% 3. The command \presetkeys of the xkeyval package expects both +% 'head' and 'tail'. The skeyval package splits \presetkeys into two, +% hopefully less confusing, commands: \skvpresetkeys and +% \skvpostsetkeys. +% +% 4. There is also command \skvpreposetkeys, which combines +% \skvpresetkeys and \skvpostsetkeys. This expects both head and +% tail and is equivalent to xkeyval's \presetkeys. + +\skvpresetkeys[SKVT]{shadowbox}{% + put frame,framecolor=black,framesize=.8pt,boxwidth=1.5cm,align=center, + shadecolor=gray!15 +} + +\skvpostsetkeys[SKVT]{shadowbox}{% + shadow=.use value{put frame},shadowcolor=.use value{framecolor}!40, + shadowsize=\dimexpr.use value{framesize}*5\relax +} + +\newcommand*\newshadowbox[2][]{% + \skvsetkeys[SKVT]{shadowbox}{#1}% + \begingroup + \ifshb@putframe + \fboxrule=\dimexpr\currfboxrule\relax + \else + \fboxrule=0pt + \fi + \ifshb@shadow\else\shadowspread=0pt \fi + \sbox\z@{\fcolorbox{\shb@framecolor}{\shb@shadecolor}{% + \hb@xt@\shb@boxwidth{\curralign{#2}}% + }}% + \hskip\shadowspread + \color{\shb@shadowcolor}% + \rule[-\dp0]{\wd0}{\dimexpr\ht0+\dp0\relax}% + \llap{\raisebox{\shadowspread}{\box0\hskip\shadowspread}}% + \endgroup +} + +%%++++++++++++++++++++++++ The \directkeys command ++++++++++++++++++++%% + +% The following are meant to demonstrate the use of the macro \directkeys: + +\ifdefined\skvtestcnt + \skvtestcnt\z@ +\else + \newcount\skvtestcnt +\fi +\def\logsetting{% + \advance\skvtestcnt\@ne + \typeout{'\skvcurrentkey' set: no. + \ifnum\skvtestcnt<10 0\fi\the\skvtestcnt, % + path: \skvcurrentprefix/\skvcurrentfamily}% +} + +\directkeys{% + % #1 will be the user-value of the handler 'some handler'. + .new handlers = { + .some handler=\def\x##1{##1*#1}, + .phantom handler=, + }, + .handler let = { + .another handler=.some handler, + .do nothing handler=.phantom handler + }, + .kill handlers={ + .another handler, + .do nothing handler + }, + .define handlers={ + .j handler=\def\x##1{##1*#1}, + .j handler=\def\y##1{##1*#1}, + }, + .define narg handlers={ + .j1 handler={2}{\def\x##1{##1*#1*#2}}, + .j2 handler={3}{\def\x##1{##1*#1*#2*#3}} + }, + .new narg handlers={ + % Error: handler j1 already exists: + % .j1 handler={2}{\def\x##1{##1*#1*#2}}, + .k handler={2}{\def\x##1{##1*#1*#2}}, + .l handler={3}{\def\x##1{##1*#1*#2*#3}} + }, + .reserve handlers={reservedx , reservedy}, + .holder prefix = mp@, + .prefix = KV, + .families = {fam1, fam2}, + .add family = {fam3}, + .add families = {fama,famb,famc}, + .ignore families = {fama,famb,famc}, + .restore families = {fama,famb}, + .restore paths = {KVA/fama,KVD/famc}, +% .ignore keys = {keyb,keyc}, + .new keys = { + .ord/keya/keya-default, + .cmd/keyb/keyb-default/\def\x##1{##1#1}, + .cmd/keyc/keyc-default, + .choice*/keyd/center + /{ right.do=\def\val@r{#1}, + left.do=\def\val@l{#1}, + center.do=\def\val@c{#1} + } + /\def\yes{value accepted} + /\def\no{value rejected}, + .bool/keye/true/ + \ifskvindef\else\logsetting\fi + \ifmp@keye\def\y##1{##1#1}\fi + , + .bool/keyf/true/, + .bool/keyg/true/ + }, + .push path, + .path = KV/fam1, + .set keys = {keyd}, + .pop path, + .ignore keys = {keyb,keyc}, + % Stop processing this list: + % .list break, + .restore keys = {keyb,keyc}, + .preset keys = {keyb,keyc}, + .postset keys = {keyb,keyc}, + % Save unknown keys in the default macro \<pref>@<fam>@rmkeys: + .save unknown keys = true, + .save unknown keys in = \macroa, + .set keys = {keya,keyb,keyc,keyd,keye}, + .set keys = {keya=keya-value,keyb=\def\y####1{####1}}, + .set keys* = {keya=keya-value,keyf}, + .set keys* = {keya=keya-value,keyf}, + .ignore keys = {keyf}, + .set rmkeys*, + % Execute arbitrary code: + .exec code=\def\x#1{#1}, + % Nesting of \directkeys is possible. Pre-entry and post-entry + % topologies are independent. At the end of the nest, + % prevailing variables of \directkeys revert to their previous + % (pre-door) values: + \directkeys{ + .paths = {KVA/fam3,KVB/fam3}, + .holder prefix = hp@, + .define keys = { + .cmd/keya/keya-default/\def\x##1{##1#1}, + .ord/keyb/keyb-default, + }, + .set keys = {keya=vala}, + }, + .exec code = \def\keycval{true}, + % Prepend slot keye to key1 and key2: + .prepend slot={key1/{keye=.expanded{\keycval}},key2/{keye=false}}, + % Multi-prepend slots: + .prepend slot={{key3,key4}/{keye=true,keyf=true},key5/{keye=false}}, + % Prepend slot keyd to key6: + .prepend slot={key6/{keyd=right}}, + % Append slots keye and keyf to key7 and key8. + % '.slots' and '.slot' are equivalent to '.append slots' + % and '.append slot'. + .append slot={key7/{keye=true},key8/{keyf=false}}, + % The next two link keyb and keyc to key9. #1 here refers to the + % current value of key9. The same effect can be achieved directly + % via the '.link' handler: + .prepend slot={key9/{keyb=#1}}, + .append slot={key9/{keyc=#1}}, + .set keys = {key9=val9}, + % Link keye and keyf to key10 and key11. The values of key10 and + % key11 are used to set keye and keyf. key10 and key11 are hence + % 'parent keys'. + .link = {key10/keye, {key11,key12}/{keyf,keyg}}, + .set keys = {key10=true}, + .ignore key = keyf, + .save unknown keys in = \macrob, + .setkeys = {keya,keyb,keyf}, + .disable keys = {keye,keyf}, +} + +%%++++++++++++++++++++++ Collecting body of environment +++++++++++++++++%% + +\skvmakekeys[ + .prefix=KV, + .family=collectbody, + .hp=mp, + .initialize, + .endlinechar=-1 +]{% + .cmd/title//, + .zcmd/title scale/1, + .zcmd/body scale/1, + .cmd/width/\textwidth, + .zcmd/title text style//, + .zcmd/body text style//, + .cmd/action// +} + +\newsavebox{\boxbody} +\newenvironment{collectbody}[1][]{% + \skvusekeys[.prefix=KV,.family=collectbody]{#1}% + \ifx\mptitle\@empty\else\skvafterfi + \begin{center}% + \scalebox{\mptitlescale}{\mptitletextstyle\mptitle}% + \end{center}% + \fi + \begin{lrbox}{\boxbody}% + \begin{minipage}{\mpwidth}% + \mpbodytextstyle\mpaction +}{% + \end{minipage}% + \end{lrbox}% + \scalebox{\mpbodyscale}{\usebox{\boxbody}}% +} + + +%%+++++++++++++++++++++ Changing the key parser ++++++++++++++++++++%% + +\setupskeyval{key parser={;}} +\skvafterdefinekeys{\setupskeyval{keyparser={,}}} +\skvdefinekeys*[SKVT]{example}[exm@]{% + .zbool/put frame/true; + .cmd/shadecolor/green; + .ord/framesize/.5pt/\def\currfboxrule{#1}; + .choice/align/center/{% + center.do=\def\curralign##1{\hfil##1\hfil}, + right.do=\def\curralign##1{\hfill##1}, + left.do=\def\curralign##1{##1\hfill}, + justified.do=\let\curralign\@iden + }; +} + +%%++++++++++++++++++++++++++ Options section ++++++++++++++++++++++%% + +\def\skvtp@unknownoptions{} + +\skvunknownoptionhandler[SKVT]<abrtext,shadowbox,citation,testpkg>{% + \edef\skvtp@unknownoptions{% + \skvtp@unknownoptions\ifx\skvtp@unknownoptions\@empty\else,\fi + #3% + }% + \AtEndDocument{% + \ifx\skvtp@unknownoptions\@empty\else + \PackageWarningNoLine{skeyval-testpkg} + {Unknown keys: \skvtp@unknownoptions}% + \fi + }% +} +\skvexecuteoptions[SKVT]<abrtext>{align=.expanded{\alignval}, + text style=.expand once{\stextstyle},author,show author +} + +\skvprocessoptions*+[SKVT]<abrtext,shadowbox,citation,testpkg>\relax +%\skvrestrictedprocessoptions*+[SKVT]<abrtext,shadowbox,citation,testpkg>\relax + +\skvcomment +Here you could also use: + +\directkeys{ + .mega process options*={ + .prefix=SKVT, + .families={abrtext,shadowbox,citation,testpkg}, + } +} + +or + +\directkeys{ + .prefix=SKVT, + .families={abrtext,shadowbox,citation,testpkg}, + .copy class options and process options in all families, +} +\endcomment + +\endinput diff --git a/macros/latex/contrib/skeyval/tex/skeyval-view.sty b/macros/latex/contrib/skeyval/tex/skeyval-view.sty new file mode 100644 index 0000000000..a09335b6b0 --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval-view.sty @@ -0,0 +1,425 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval-view.sty', 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\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@view@restorecodes{\do35,64,123,125,61,59,13,\do,}% +} +\skv@prova + +\edef\skv@view@restorecodes{% + \unexpanded\expandafter{\skv@view@restorecodes}% + \endlinechar\the\endlinechar\relax +} +\endlinechar13 % +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx\do#1\else + \edef\skv@view@restorecodes{% + \unexpanded\expandafter{\skv@view@restorecodes}% + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\NeedsTeXFormat{LaTeX2e}[2011/06/27] +\ProvidesPackage{skeyval-view} + [2013/05/15 v1.3 Viewer utility for skeyval package (AM)] +\@ifpackageloaded{skeyval}{}{\RequirePackage{skeyval}} +\directkeys*{% + .path=SKVV/skvview, + .holder prefix=skvv@, + .initialize keys after define=false, + .every unknown option={ + \PackageWarning{skeyval-view}{Unknown option '\CurrentOption' ignored}% + }, + .new options={ + % LaTeX's options processor will zap the option names. So these keys + % can't have spaces between their parts. + .ord/{xcolor-options,load-xcolor-with}// + \edef\skvv@xcoloroptions{\unexpanded{#1}} + \skvstripouterbraces{1}\skvv@xcoloroptions, + .bool/tabulate/true + }, + .execute options={ + tabulate=true,xcolor-options=false,load-xcolor-with=false + }, + .copy class options and process options +} +\skvnewregisters\toks{\skvv@toksa} +\AtEndOfPackage{% + \let\ifskvv@tempsw\ifskvv@tabulate + \skvv@tabulatefalse + \skvdisablekeys[SKVV]{skvview}{xcolor-options,load-xcolor-with}% + \let\ifskvv@tabulate\ifskvv@tempsw +} + +\@ifpackageloaded{longtable}{}{\RequirePackage{longtable}} +\@ifpackageloaded{xcolor}{}{\RequirePackage[\skvv@xcoloroptions]{xcolor}} +\skvnewbools[skvv@]{colii,coliii,coliv,colv} +\newwrite\skvv@out +\skvnewnumbers[skvv@]{depth,nra} + +\directkeys*{ + .path=SKVV/skvview, + .holder prefix=skvv@, + .initialize keys after define=false, + .define keys={ + .ord/{file,wcolsep,weol}/\@nil/ + \skvcsedef{skvv@\skvcurrentkey}{\unexpanded{#1}} + \skvaftercsname{\skvstripouterbraces{1}}{skvv@\skvcurrentkey} + \skvaftercsname\skvdespacecontent{skvv@\skvcurrentkey} + , + .ord/{prefix,family,type,default,columns}/\@nil/ + \skvcsedef{skvv@\skvcurrentkey}{\unexpanded{#1}} + \skvaftercsname{\skvstripouterbraces{1}}{skvv@\skvcurrentkey} + \skvaftercsname\skvcsvnormalize{skvv@\skvcurrentkey} + , + .bool/{view,vlabels,wlabels}/true, + .ord/length of default/20/\def\skvv@lengthofdefa{#1}, + .cmd/color/black, + }, + .set keys={ + prefix,family,type,default,file,columns,wcolsep=&,weol=\\, + view,vlabels=false,wlabels=false,color,length of default + }, + .links={ + show/view, + key type/type, + {prefixes,only those with prefix,only those with prefixes}/prefix, + {families,only those in family,only those in families}/family, + {defaults,only those with default,only those with defaults}/default, + view output/view, + write to file/file, + {view labels,show labels}/vlabels, + {view wlabels,show wlabels}/wlabels, + wend of line/weol, + wcolumn separation/wcolsep, + word length of default/length of default + }, + .style={ + only those without default/default=[none], + }, +} + +% Use \setupviewkeys to change viewing specifications without actually +% displaying (or writing to file) the key list: +% +\skvrobustdef*\setupviewkeys{\skvsetkeys[SKVV]{skvview}} + +\let\skvv@db\@empty +\skvrobustdef*\skvv@dotabulate#1#2#3{% + \ifskvv@tabulate + \begingroup + \edef\skv@tempa{\detokenize{#3}}% + \skv@getshortformofcontent\skvv@lengthofdefa\skv@tempa\skv@ival + \skvxaddtolist{,}\skvv@db{% + #1=\ifx\skvcurrentprefix\@empty\else + \skvcurrentprefix\fi=\skvcurrentfamily=#2=\skv@ival + }% + \skvaftergroupdef\skvv@db\endgroup + \fi +} +\protected\def\skv@ordkey@a#1#2[#3]#4{% + \skvifstrcmpTF{#3}{^skv^}{% + \skvv@dotabulate{#2}{ordinary}{[none]}% + }{% + \skv@definedefault{#2}{#3}% + \skvv@dotabulate{#2}{ordinary}{#3}% + }% + % Pointers on key #2 have been stripped in \skv@ordkey. + % 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 + }% +} +\protected\def\skv@cmdkey#1#2[#3]#4{% + \skv@strippointersfromkey{#2}% + \skvifdefboolTF{skv@st}{% + \skv@definedefault\skvcurrentkey{#3}% + \skvv@dotabulate\skvcurrentkey{command}{#3}% + }{% + \skvv@dotabulate\skvcurrentkey{command}{[none]}% + }% + % 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}% + }% +} +\protected\def\skv@choicekey@g{% + \skvcommaparse*\skv@currlist\skv@prova{% + \skvexpbracenext\skv@strippointersfromkey\skv@prova + \ifx\skv@default\skv@rej + \skvv@dotabulate\skvcurrentkey{choice}{[none]}% + \else + \skvexpanded{% + \noexpand\skvv@dotabulate + {\skvxonce\skvcurrentkey}{choice}{\skvxonce\skv@default}% + \noexpand\skv@definedefault + {\skvxonce\skvcurrentkey}{\skvxonce\skv@default}% + }% + \fi + \skvcsedef{\skv@header\skvcurrentkey.@cbk}{% + \skvcslet{\skv@hp\skvcurrentkey}\noexpand\skvcurrentvalue + \the\skv@temptoks + }% + }% +} +\protected\def\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}% + \skvv@dotabulate{#1}{\if\skv@typetogkey toggle\else bool\fi}{#3}% + }{% + \skvv@dotabulate{#1}{\if\skv@typetogkey toggle\else bool\fi}{[none]}% + }% + \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}% +} +% #2=styles, #3=default, #4=callback +\protected\def\skv@appendslots@a#1#2[#3]#4{% + \skvcommaparse{#1}\skv@prova{% + \skvexpbracenext\skv@strippointersfromkey\skv@prova + \skvexpanded{% + \skvifstrcmpTF{#3}{^skv^}{% + % When using \skvaddslots, signal keys will otherwise be added + % in the database twice (while treating prepended and appended + % slots). + \ifskv@addslots@appending\else + \skvv@dotabulate{\skvcurrentkey}{slot/style}{[none]}% + \fi + }{% + \skv@definedefault{\skvcurrentkey}{\unexpanded{#3}}% + \ifskv@addslots@appending\else + \skvv@dotabulate{\skvcurrentkey}{slot/style}{\unexpanded{#3}}% + \fi + }% + \skv@appendslots@b{\skvcurrentkey}\unexpanded{{#2}{#4}}% + }% + }% +} + +\skvbuildmacrostack\skvv@state{% + \do\skvv@prefix\do\skvv@family\do\skvv@type\do\skvv@default + \do\skvv@file\do\skvv@columns\do\skvv@wcolsep\do\skvv@weol + \do\skvv@color\do\ifskvv@view\do\ifskvv@vlabels\do\ifskvv@wlabels +}{2} + +\skvnewdef*\skvv@@view{% + \ifskvv@tabulate + 0\ifskvv@view 0\else 1\fi + \else + 01% + \fi +} +\skvrobustdef*\skvv@skvview#1{% + \skvsetkeys[SKVV]{skvview}{#1}% + \ifx\skvv@default\@nnil\else\@onelevel@sanitize\skvv@default\fi + \ifx\skvv@columns\@nnil + \skvsetnumber\skvv@nra5\relax + \skvv@coliitrue\skvv@coliiitrue\skvv@colivtrue\skvv@colvtrue + \else + \skvsetnumber\skvv@nra\@ne + \skvxifinTF{,prefix,}{,\skvv@columns,}{% + \skvadvanceno\skvv@nra\@ne\skvv@coliitrue + }{% + \skvv@coliifalse + }% + \skvxifinTF{,family,}{,\skvv@columns,}{% + \skvadvanceno\skvv@nra\@ne\skvv@coliiitrue + }{% + \skvv@coliiifalse + }% + \skvxifinTF{,type,}{,\skvv@columns,}{% + \skvadvanceno\skvv@nra\@ne\skvv@colivtrue + }{% + \skvxifinTF{,key type,}{,\skvv@columns,}{% + \skvadvanceno\skvv@nra\@ne\skvv@colivtrue + }{% + \skvv@colivfalse + }% + }% + \skvxifinTF{,default,}{,\skvv@columns,}{% + \skvadvanceno\skvv@nra\@ne\skvv@colvtrue + }{% + \skvv@colvfalse + }% + \fi + \if\skvv@@view + \protected@edef\skv@prova{% + \noexpand\begin{longtable}[l]{*\skvv@nra l}% + \\\noexpand\hline + \normalfont Key + \ifskvv@colii&\normalfont Prefix\fi + \ifskvv@coliii&\normalfont Family\fi + \ifskvv@coliv&\normalfont Type\fi + \ifskvv@colv&\normalfont Default\fi + \\\noexpand\hline + \noexpand\endfirsthead\noexpand\multicolumn{\skvv@nra}{l}{% + \normalfont\emph{Continued from previous page}}% + \\\noexpand\hline + \normalfont Key + \ifskvv@colii&\normalfont Prefix\fi + \ifskvv@coliii&\normalfont Family\fi + \ifskvv@coliv&\normalfont Type\fi + \ifskvv@colv&\normalfont Default\fi + \\\noexpand\hline\noexpand + \endhead\noexpand\hline + \noexpand\multicolumn{\skvv@nra}{r}{% + \normalfont\emph{Continued on next page}}% + \\\noexpand\endfoot\noexpand\hline + \noexpand\endlastfoot + }% + \skvv@toksa\expandafter{\skv@prova}% + \fi + \ifx\skvv@file\@nnil\else + \immediate\openout\skvv@out\skvv@file + \fi + \skvcommaloop*\skvv@db\skv@prova{% + \skv@swatrue + \expandafter\skvv@skvview@a\skv@prova\skv@nil + }% + \if\skvv@@view + \skvaddtotoks\skvv@toksa{\end{longtable}}% + \begingroup + \color{\skvv@color}% + \ttfamily\the\skvv@toksa + \endgroup + \fi + \ifx\skvv@file\@nnil\else\immediate\closeout\skvv@out\fi +} +\skvnewdef*\skvv@skvview@a#1=#2=#3=#4=#5\skv@nil{% + \skvv@ifin{#2}\skvv@prefix + \skvv@ifin{#3}\skvv@family + \skvv@ifin{#4}\skvv@type + \skvv@ifin{#5}\skvv@default + \ifskv@swa + \if\skvv@@view + \edef\skv@prova{% + #1\ifskvv@colii\fi\ifskvv@coliii\fi + \ifskvv@coliv\fi\ifskvv@colv\fi + \ifskvv@vlabels\noexpand\label{#2/#3/#1}\fi + }% + \skvoaddtotoks\skvv@toksa{\skv@prova\\}% + \fi + \ifx\skvv@file\@nnil\else + \immediate\write\skvv@out{% + #1\ifskvv@colii\skvv@wcolsep#2\fi + \ifskvv@coliii\skvv@wcolsep#3\fi + \ifskvv@coliv\skvv@wcolsep#4\fi + \ifskvv@colv\skvv@wcolsep#5\fi + \ifskvv@wlabels\string\label{#2/#3/#1}\fi + \skvexpandonce\skvv@weol + }% + \fi + \fi +} +\skvrobustdef*\skvv@ifin#1#2{% + \ifx#2\@nnil\else + \skvxifinTF{,\detokenize{#1},}{,\skvoxdetok{#2},}{}\skv@swafalse + \fi +} +\directkeys*{ + .path=SKVV/skvvshow, + .holder prefix=skvvs@, + .initialize keys after define, + .new keys={ + .ord/{.show,.show table}// + \skvifdefboolTF{skvindef}{}{ + \edef\skvvs@show{\unexpanded{#1}} + \skvstripouterbraces{2}\skvvs@show + \ifx\skvvs@show\@empty\else\skvafterfi + \skvpushstate\skvv@state\skvv@depth + \skvexpbracenext\skvv@skvview\skvvs@show + \skvpopstate\skvv@state\skvv@depth + \fi + }, + .ord/.exec code/\@nil/ + \edef\skvvs@execcode{\unexpanded{#1}} + \ifx\skvvs@execcode\@nnil\else + \expandafter\skvvs@execcode + \fi + }, +} + +\skvrobustdef*\skvviewkeys{\skvsetkeys[SKVV]{skvvshow}} +\skvnewlet\skvshowkeys\skvviewkeys + +\skv@view@restorecodes +\endinput + +%% End of file 'skeyval-view.sty'. diff --git a/macros/latex/contrib/skeyval/tex/skeyval.sty b/macros/latex/contrib/skeyval/tex/skeyval.sty new file mode 100644 index 0000000000..0bd5722fca --- /dev/null +++ b/macros/latex/contrib/skeyval/tex/skeyval.sty @@ -0,0 +1,971 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'skeyval.sty', version 1.3, 2013/05/15. % +% % +% NOTE % +% % +% Beginning from version 1.0, 2012/09/01, the skeyval package has changed % +% radically. Users of pre-version 1.0 of the package can load it now by % +% calling: % +% % +% \usepackage[compatibility]{skeyval} % +% \RequirePackage[compatibility]{skeyval} % +% or % +% \usepackage{skeyval-bc} % +% \RequirePackage{skeyval-bc} % +% % +% LICENSE % +% % +% 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. % +% % +% DISTRIBUTION % +% % +% 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). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\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@restorecodes{\do35,64,123,125,61,59,13,\do,}% +} +\skv@prova + +\edef\skv@restorecodes{% + \unexpanded\expandafter{\skv@restorecodes}% + \endlinechar\the\endlinechar\relax +} +\endlinechar13 % +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx\do#1\else + \edef\skv@restorecodes{% + \unexpanded\expandafter{\skv@restorecodes}% + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\ProvidesPackage{skeyval}[2013/05/15 v1.3 Robust key-value parser (AM)] +\NeedsTeXFormat{LaTeX2e}[2011/06/27] + +\input skeyval-core + +\skvrobustdef*\skvafterendpackage{% + \skvifcsdefTF{\@currname.\@currext-skv@endpkghook}{}{% + \@namedef{\@currname.\@currext-skv@endpkghook}{}% + }% + \expandafter\g@addto@macro + \csname\@currname.\@currext-skv@endpkghook\endcsname +} +\skvrobustdef*\skvafterpackage#1{% + \skvafterbegindocument{% + \skvifcsdefTF{ver@#1.\@pkgextension}{}{% + \skv@warn{Package '#1' was never loaded}% + }% + } + \skvifcsdefTF{ver@#1.\@pkgextension}{% + \@firstofone + }{% + \skvappendtomacro{#1.\@pkgextension-skv@endpkghook}% + }% +} +\xdef\@popfilename{% + \unexpanded{% + \csname\@currname.\@currext-skv@endpkghook\endcsname + \expandafter\let + \csname\@currname.\@currext-skv@endpkghook\endcsname\relax + }% + \unexpanded\expandafter{\@popfilename}% +} +\skvundef\skv@documentclass +\skvrobustdef*\skv@getdocumentclass{% + \ifcase0% + \ifx\@filelist\@undefined 1\fi + \ifx\@filelist\relax 1\fi + \ifx\@filelist\@gobble 1\fi\relax + \skvcommaparse*\@filelist\skv@tempa{% + \filename@parse\skv@tempa + \ifx\filename@ext\@clsextension + \skvifcsdefFT{opt@\filename@area\filename@base.\filename@ext}{}{% + \edef\skv@documentclass{% + \filename@area\filename@base.\filename@ext + }% + }% + \fi + \ifx\skv@documentclass\@undefined\else\skvbreakloop\fi + }% + \fi +} + +% The following packages need access to the original \@classoptionslist: +\skvnewdef*\skv@optionprocessorpackages{% + xkeyval,kvoptions,catoptions,biblatex,pgfopts,ltxkeys% +} +% Filter class options to remove those with '=': +\skvrobustdef*\skv@filterclassoptions{% + \ifcase0% + \ifx\@classoptionslist\@undefined 1\fi + \ifx\@classoptionslist\relax 1\fi\relax + \global\let\skv@filterclassoptions\relax + \skvkvnormalize\@classoptionslist + \let\skvoriginalclassoptionslist\@classoptionslist + \let\skv@classoptionslist\@classoptionslist + \let\@classoptionslist\@empty + \skvcommaloop*\skvoriginalclassoptionslist\skv@tempa{% + \skvxifinTF{=}{\skvexpandonce\skv@tempa}{}{% + \skvaddtolist*\@classoptionslist\skv@tempa + }% + }% + \def\@fileswith@ptions##1[##2]##3{% + \edef\skv@tempa{\unexpanded{##3}}% + \skvcsvnormalize\skv@tempa + \skvcommaloop*\skv@optionprocessorpackages\skv@prova{% + \skvxifinTF{,\skv@prova,}{,\skv@tempa,}{% + \let\@classoptionslist\skvoriginalclassoptionslist + \skvbreakloop + }{}% + }% + \skvifnextchar[{\@fileswith@pti@ns##1[##2]##3}% + {\@fileswith@pti@ns##1[##2]##3[]}% + }% + \fi +} +\skvrobustdef*\skvbeforebegindocument{% + \skvgappendtomacro\skv@beforebegindoc +} +\skvnewdef*\skv@beforebegindoc{} +\skvprependtomacro\document{% + \endgroup + \let\skvbeforebegindocument\@firstofone + \skv@beforebegindoc + \gdef\skvbeforebegindocument{\skv@notprerr\skvbeforebegindocument}% + \global\let\skv@beforebegindoc\relax + \begingroup +} +\skvrobustdef*\skvafterbegindocument{% + \skvgappendtomacro\skv@afterbegindoc +} +\skvnewdef*\skv@afterbegindoc{} +\skvgappendtomacro\document{% + \let\skvafterbegindocument\@firstofone + \skv@afterbegindoc + \gdef\skvafterbegindocument{\skv@notprerr\skvafterbegindocument}% + \global\let\skv@afterbegindoc\relax + \ignorespaces +} +\skvrobustdef*\skv@notprerr#1{% + \@latex@error{Command \detokenize{#1} should be used only + in preamble}\skv@ehd +} +\skvnewdef*\skv@everypagehook{} +\skvrobustdef*\skvatbegineverypage{\skvgappendtomacro\skv@everypagehook} +\skvafterbegindocument{% + \let\skv@savedoutputpage\@outputpage + \def\@outputpage{% + \let\skv@savedbegindvi\@begindvi + \def\@begindvi{% + \skv@everypagehook + \skv@savedbegindvi + }% + \skv@savedoutputpage + \let\@begindvi\skv@savedbegindvi + }% +} + +\skvnewdef*\skv@preamblecmdhook{} +\skvrobustdef*\skvonlypreamble#1{% + \def\skv@provb##1{% + \skvifntypeTF{##1}{}{% + \skv@err{More than one macro + \MessageBreak '\detokenize{##1}'. + \MessageBreak Maybe a comma is missing in the list}\skv@ehd + }% + \skvxifinTF{\detokenize{\do##1}}{\skvoxdetok\skv@preamblecmdhook}{% + \skv@warn{Command '\noexpand##1' multiply + \MessageBreak submitted to \string\skvonlypreamble: ignored}% + }{% + \xdef\skv@preamblecmdhook{% + \skvexpandonce\skv@preamblecmdhook\unexpanded{\do##1}% + }% + }% + }% + \skvcommaparse{#1}\skv@prova{% + \expandafter\skv@provb\expandafter{\skv@prova}% + }% +} +\AtEndOfPackage{% + \skvafterbegindocument{% + \def\do#1{% + \skvifdefTF#1{% + \gdef#1{\@latexerr{'\string#1' is a preamble command}\skv@ehd}% + }{% + \ifx\relax#1\else + \@@warning{Undefined command '\unexpanded{#1}' + \MessageBreak appeared in \string\skvonlypreamble. + \MessageBreak Only defined commands should be + \MessageBreak submitted to \string\skvonlypreamble.}% + \fi + }% + }% + \skv@preamblecmdhook + \let\skv@preamblecmdhook\relax + \let\do\relax + }% +} + +%%+++++++++++++++++ Utilities for handling options ++++++++++++++++%% + +% \skvunknownoptionhandler[<prefixes>]<<families>>{<handler>} +% +\skvrobustdef*\skvunknownoptionhandler{% + \skv@testopt\skv@unknownoptionhandler\skvdefaultprefix +} +\skvrobustdef*\skv@unknownoptionhandler[#1]{% + \skvifnextchar<{\skv@unkn@wnoptionhandler{#1}}% + {\skv@unkn@wnoptionhandler{#1}<\@currname.\@currext>}% +} +\skvrobustdef*\skv@unkn@wnoptionhandler#1<#2>{% + \skv@unknownkeyhandler[#1]{#2}% +} +\skvrobustdef*\skv@testopte#1{\skv@testopta{\skv@t@stopte{#1}}} +\skvrobustdef*\skv@t@stopte#1{\skv@testopt{\skv@t@st@pte{#1}}\skvdefaultprefix} +\skvrobustdef*\skv@t@st@pte#1[#2]{% + % Only one prefix is allowed: + \skvxifinTF{,}{\detokenize{#2}}{% + \skv@err{Only one prefix is allowed, but you gave '#2'}\skv@ehd + }{% + \skv@makeprefix{#2}% + \skvifnextchar<{\skv@@t@st@pte{#1}}% + {\skv@@t@st@pte{#1}<\@currname.\@currext>}% + }% +} +\skvrobustdef*\skv@@t@st@pte#1<#2>{% + \edef\skv@fams{#2}% + \skvxifinTF{,}{\detokenize{#2}}{% + \skvcsvnormalize\skv@fams + }{% + \skvdespacecontent\skv@fams + }% + \skv@testopt{#1}{}% +} + +\skvrobustdef*\skveveryunknownoption{\skvdeclareoption*} +% \skvdeclareoption[<pref>]<<fam>>{<key>}[<defa>]{<callback>} +% \skvdeclareoption*{<message>} +\skvrobustdef*\skvdeclareoption{% + \let\@fileswith@pti@ns\@badrequireerror + \let\skvdefkey\skvordkey + \skvifstar\skv@dox\skv@dox@a +} +% \skvdeclarevoidoption[<pref>]<<fam>>{<key>}[<defa>]{<callback>} +\skvrobustdef*\skvdeclarevoidoption{% + \let\@fileswith@pti@ns\@badrequireerror + \let\skvdefkey\skvvoidkey + \skv@dox@a +} +% \skvdeclareobsoleteoption +% [<pref>]{<fam>}{<obsolete key>}{<new key>}[<defa>]{<callback>} +\skvrobustdef*\skvdeclareobsoleteoption{% + \let\@fileswith@pti@ns\@badrequireerror + \let\skvdefkey\skvobsoletekey + \skv@dox@a +} + +% \skvdeclarebooloption[<pref>]<<fam>>[<mp>]{<key>}[<defa>]{<callback>} +\skvrobustdef*\skvdeclarebooloption{% + \let\@fileswith@pti@ns\@badrequireerror + \let\skvdefkey\skvboolkey + \skv@dox@a +} +% \skvdeclarecmdoption[<pref>]<<fam>>[<mp>]{<key>}[<defa>]{<callback>} +\skvrobustdef*\skvdeclarecmdoption{% + \let\@fileswith@pti@ns\@badrequireerror + \let\skvdefkey\skvcmdkey + \skv@dox@a +} + +% Our default action for unknown options is tied to \@currname +% and \@currext: +\skvrobustdef\skv@dox#1{% + \skvcsedef{skv@dox@\@currname.\@currext}{% + \unexpanded{% + \ifx\skvcurrentvalue\skv@novaluetoks + \def\skvcurrentvalue{no value}% + \fi + #1% + }% + }% +} +\skvrobustdef*\skv@dox@a{\skv@testopt\skv@dox@b\skvdefaultprefix} +\skvrobustdef*\skv@dox@b[#1]{% + \skvifnextchar<{\skv@dox@c{#1}}{\skv@dox@c{#1}<\@currname.\@currext>}% +} +\skvrobustdef*\skv@dox@c#1<#2>{\skvdefkey[#1]{#2}} + +% Declare options with default values that will be used when the options +% are called without user values. +% +% \skvdeclarepassedoptions[<pref>]{<fam>}{<option>=<default>,...} +% +% Example: +% +% \skvdeclarepassedoptions[KV]{fam}{% +% hyperref={colorlinks,breaklinks},xcolor=svgnames +% } +% +\skvrobustdef*\skvdeclarepassedoptions{% + \skv@testopt\skv@declarepassedoptions\skvdefaultprefix +} +\skvrobustdef*\skv@declarepassedoptions[#1]#2#3{% + \skvparsekvlist{#3}\skv@tempa{% + \skvexpbracenext\skv@kvsplit\skv@tempa{% + \skvdeclareoption[#1]<#2>{##1}[##2]{% + \skvifcsdefTF{ver@##1.sty}{% + \skv@warn{Package '##1' has been loaded and + \MessageBreak now you're passing options to it. + \MessageBreak Instruction ignored}% + }{% + \skvpassoptionstopackage{####1}{##1}% + \AtBeginDocument{% + \skvifpackageloadedTF{##1}{}{% + \skv@warn{Options were passed to package + \MessageBreak '##1' but it was never loaded}% + }% + }% + }% + }% + }% + }% +} +% \skvpassoptionstopackage{<opts>}{<pkg>} +\skvrobustdef*\skvpassoptionstopackage{\skv@passoptions{sty}} +\skvrobustdef*\skvpassoptionstoclass{\skv@passoptions{cls}} +\skvrobustdef*\skv@passoptions#1#2#3{% + \begingroup + \edef\@elt{opt@\skvtrimspace{#3}.#1}% + \skvcsxdef{\@elt}{% + \skvifcsdefFT{\@elt}{}{\@nameuse{\@elt},}% + \skvtrimspace{#2}% + }% + \endgroup +} + +% Pass options to package and load the package after processing options. +% +% \skvdeclarepassedoptionsloadpackage +% [<pref>]{<fam>}{<option>=<default>,...} +% +\skvrobustdef*\skvdeclarepassedoptionsloadpackage{% + \skv@testopt\skv@declarepassedoptions@load\skvdefaultprefix +} +\skvrobustdef*\skv@declarepassedoptions@load[#1]#2#3{% + \skvparsekvlist{#3}\skv@tempa{% + \skvexpbracenext\skv@kvsplit\skv@tempa{% + \skvdeclareoption[#1]<#2>{##1}[##2]{% + \skvifcsdefTF{ver@##1.sty}{% + \skv@warn{Package '##1' has been loaded and + \MessageBreak now you've asked for it to be + \MessageBreak loaded again. Instruction ignored}% + }{% + \skvafterprocessoptions{% + \RequirePackage[####1]{##1}% + }% + }% + }% + }% + }% +} + +% \skvexecuteoptions[<pref>]<fams>[<na>]{<kvlist>} +% \skvexecuteoptions+[<pref>]<fams>[<na>]{<kvlist>} +% +% This has no star (*) variant; any star suffix is quietly ignored. +% +\skvrobustdef*\skvexecuteoptions{% + \skv@testopte{\skv@stfalse\skv@setkeys@a}% +} + +% \skvprocessoptions*+[<pref>]{<fams>}[<na>] +% +% 1. If not using LaTeX, then no processing of options. +% 2. The plus (+) variant of \skvprocessoptions will process the options +% in all the listed families. The star (*) form of \skvprocessoptions +% -- like in LaTeX2e -- will also copy and use class options. +% 3. Class options are always processed first, but class options that ocurr +% also as local/package options are dropped from the list of options +% to be processed. That is, package options always take priority over +% class options. +% 4. The \ProcessOptionsX of xkeyval package doesn't expect any plus (+) +% suffix: it processes options only in a single family. +% +% 5. \skvpackagelist contains the list of loaded packages with their +% options that are found on the current paths. See +% <https://groups.google.com/forum/#!topic/comp.text.tex/XErNCMcT_jc> +% for the rationale. Example: +% +% \usepackage[option1,option2]{foobar} +% \show\skvpackagelist +% \expandafter\show\csname foobar.sty.poxkeys\endcsname +% +\skvnewlet\skvpackagelist\@empty +\skvrobustdef*\skvprocessoptions{% + \skv@getdocumentclass + \ifdefined\skv@documentclass + \global\let\skv@getdocumentclass\relax + \skv@filterclassoptions + \fi + \skv@testopte\skv@pox +} +\skvnewlet\skv@savprocessoptions\skvprocessoptions +\skvrobustdef*\skv@badprocessoptions{% + \skv@testopte{% + \skv@err{\noexpand\skvprocessoptions can't be nested}\skv@ehd + }% +} +\skvrobustdef*\skv@pox[#1]{% + \skv@inpoxtrue + \let\@fileswith@pti@ns\@badrequireerror + \let\skvprocessoptions\skv@badprocessoptions + \begingroup + \edef\skv@userclassorpackage{\@currname.\@currext}% + \let\skv@classoptionsfound\@empty + \let\skv@classorpackageoptions\@empty + % The list of pox keys for each family, simply for taking + % pox keys outside the current scope: + \let\skv@exitpoxkeys\@empty + % The list of pox keys in all familes: + \let\skvcurrentpoxkeys\@empty + \ifx\skv@userclassorpackage\skv@documentclass + \skv@inclasstrue + \let\@unusedoptionlist\skv@classoptionslist + \else + \skv@inclassfalse + \ifcase0% + \ifx\skv@classoptionslist\@undefined 1\fi + \ifx\skv@classoptionslist\@empty 1\fi + \ifx\skv@classoptionslist\relax 1\fi\relax + \ifskv@st + \def\ifkeyundef##1##2##3{\skvexpanded{\skvifkeydefFT[##1]{##2}{##3}}}% + \skvcommaloop*\skv@classoptionslist\CurrentOption{% + \expandafter\expandafter\expandafter + \skv@g@tkeyname\CurrentOption=\skv@getname@nil\skvcurrentkey + % Test if key is defined in one of the families. The search will + % stop as soon as the key is found in any of the families. + \ifkeyundef\skvcurrentprefix\skv@fams\skvcurrentkey{}{% + \edef\skv@classoptionsfound{% + \skvaddlist,\skv@classoptionsfound\skvexpandonce\CurrentOption + }% + \expandafter\skv@removeoption\expandafter{\CurrentOption}% + }% + }% + \ifx\skv@classoptionsfound\@empty\else + \skvexpbracenext\skv@getnamesofkeys\skv@classoptionsfound\skv@tempd + \skvexpbracenext\skv@getpoxkeys\skv@tempd + \fi + \fi + \fi + \fi + \skvletcs\skv@classorpackageoptions{opt@\@currname.\@currext}% + \ifx\skv@classorpackageoptions\relax + \def\skv@classorpackageoptions{}% + \fi + \skvifemptyTF\skv@classorpackageoptions{}{% + \skvexpbracenext\skv@getnamesofkeys\skv@classorpackageoptions\skv@tempd + \skvexpbracenext\skv@getpoxkeys\skv@tempd + % Package options have higher priority than class options. Hence + % filter class options \skv@classoptionsfound that are also present + % as package options \skv@classorpackageoptions: + \skvifxTF\skv@userclassorpackage\skv@documentclass{}{% + \skvifemptyTF\skv@classoptionsfound{}{% + \skvcommaloop*\skv@classoptionsfound\CurrentOption{% + \expandafter\skv@g@tkeyname\CurrentOption=\skv@getname@nil\skv@tempa + \skvxifinTF{,\skvoxdetok\skv@tempa,}{,\skvoxdetok\skv@tempd,}{}{% + \edef\skv@classorpackageoptions{% + \skvaddlist,\skv@classorpackageoptions + \skvexpandonce\CurrentOption + }% + }% + }% + }% + }% + }% + % Get document class options that are on the current paths: + \ifx\skv@userclassorpackage\skv@documentclass + \def\skvclassoptionsfound{}% + \skvcommaloop*\skv@classorpackageoptions\skv@prova{% + \expandafter\skv@g@tkeyname\skv@prova=\skv@getname@nil\skvcurrentkey + \edef\skv@prova{[\skvcurrentprefix]{\skv@fams}{\skvcurrentkey}}% + \expandafter\skvifkeydefTF\skv@prova{% + \edef\skvclassoptionsfound{% + \skvaddlist,\skvclassoptionsfound\skvcurrentkey + }% + }{}% + }% + \else + \let\skvclassoptionsfound\skv@classoptionsfound + \fi + % \skvpackagelist is populated by all calls to \skvprocessoptions. + \skvletcs\skv@prova{\@currname.\@currext.poxkeys}% + \edef\skvpackagelist{% + \skvaddlist,\skvpackagelist + \skvifdefFT\skv@prova{}{\@currname.\@currext{\skv@prova}}% + }% + \let\do\skvcmdexit + \skvexpanded{\endgroup + \do\skvclassoptionsfound\do\skvcurrentpoxkeys + \do\skvpackagelist\do\@unusedoptionlist + \skvboolexit\ifskv@inclass\skv@exitpoxkeys + % There may be preset keys even when \skv@classorpackageoptions is empty: + \skvsetkeys\ifskv@pl+\fi[\skvcurrentprefix]{\skv@fams}[#1]% + {\skvexpandonce\skv@classorpackageoptions}% + }% + \skvcslet{skv@dox@\@currname.\@currext}\relax + \skv@inpoxfalse + \let\@fileswith@pti@ns\@@fileswith@pti@ns + \let\skvprocessoptions\skv@savprocessoptions + \AtEndOfPackage{\let\@unprocessedoptions\relax}% + \csname skv@\@currname.\@currext @afterprocess@hook\endcsname + \skvcslet{skv@\@currname.\@currext @afterprocess@hook}\relax +} + +% \skvprocessoptionswithclassoptions*+[<pref>]{<fams>}[<na>] +% +% 1. The star (*) suffix is always assumed present, even when the +% user hasn't put it. +% +\skvrobustdef*\skvprocessoptionswithclassoptions{% + \skv@testopte\skv@processoptionswithclassoptions +} +\skvrobustdef*\skv@processoptionswithclassoptions[#1]{% + \skv@sttrue\skv@pox[#1]% +} + +% \skvprocessoptionswithoutclassoptions*+[<pref>]{<fams>}[<na>] +% +% 1. The star (*) suffix is always assumed absent, even when the +% user has put it. +% +\skvrobustdef*\skvprocessoptionswithoutclassoptions{% + \skv@testopte\skv@processoptionswithoutclassoptions +} +\skvrobustdef*\skv@processoptionswithoutclassoptions[#1]{% + \skv@stfalse\skv@pox[#1]% +} + +% \skv@getpoxkeys{<keys>} +% +% 1. Get pox keys (ie, keys processed as options) from package or +% class options. +% 2. This isn't a user command! See \skvgetdefinedkeys in file +% skeyval-core.tex. +\skvrobustdef*\skv@getpoxkeys#1{% + \begingroup + \skvcommaloop*\skv@fams\skvcurrentfamily{% + \skv@makeheader\skvcurrentfamily + % Prepare to take poxkeys outside the group: + \skvxifinTF{\skvoxdetok\skv@header}{\skvoxdetok\skv@exitpoxkeys}{}{% + \edef\skv@exitpoxkeys{% + \skvexpandonce\skv@exitpoxkeys + \noexpand\skvcsexit{\skvcurrentpath.@poxkeys}% + }% + }% + \edef\skv@prova{\@currname.\@currext.poxkeys}% + \skvxifinTF{\skvoxdetok\skv@prova}{\skvoxdetok\skv@exitpoxkeys}{}{% + \edef\skv@exitpoxkeys{% + \skvexpandonce\skv@exitpoxkeys\noexpand\skvcsexit{\skv@prova}% + }% + }% + \skvcommaloop{#1}\skvcurrentkey{% + \skvifcsdefFT{\skv@header\skvcurrentkey.@cbk}{}{% + \skvletcs\skv@prova{\skvcurrentpath.@poxkeys}% + \skvifdefTF\skv@prova{% + \skvxifinTF{,\skvcurrentkey,}{,\skv@prova,}{}{% + \skvcsedef{\skvcurrentpath.@poxkeys}{% + \skvaddlist,\skv@prova\skvcurrentkey + }% + }% + }{% + \skvcsedef{\skvcurrentpath.@poxkeys}{\skvcurrentkey}% + }% + \skvletcs\skv@prova{\@currname.\@currext.poxkeys}% + \skvifdefTF\skv@prova{% + \skvxifinTF{,\skvcurrentkey,}{,\skv@prova,}{}{% + \skvcsedef{\@currname.\@currext.poxkeys}{% + \skvaddlist,\skv@prova\skvcurrentkey + }% + }% + }{% + \skvcsedef{\@currname.\@currext.poxkeys}{\skvcurrentkey}% + }% + \skvxifinTF{,\skvcurrentkey,}{,\skvcurrentpoxkeys,}{}{% + \edef\skvcurrentpoxkeys{% + \skvaddlist,\skvcurrentpoxkeys\skvcurrentkey + }% + }% + }% + }% + }% + \let\do\skvcmdexit + \skvexpanded{\endgroup + \do\skvcurrentpoxkeys + % We need the list in \skv@exitpoxkeys outside the current scope. + % It will be used to take poxkeys (on each path) outside the scope + % in \skv@pox: + \do\skv@exitpoxkeys + % We need the poxkeys on each path outside the current scope: + \skv@exitpoxkeys + }% +} + +% +++++++++ Utilities for handling options via \directkeys +++++++++% + +% \dirkeys@declareoptions{<star>}{<list>} +% +% <star>, if present, means new options. +% +\skvrobustdef*\dirkeys@declareoptions#1#2{% + \let\@fileswith@pti@ns\@badrequireerror + \begingroup + % The boolean skv@inopt differs from skv@inpox. skv@inopt is only + % meant to set the default family in \dirkeys@setdefaultpath + % when dealing with options. + \skv@inopttrue + \def\skv@tempa{}% + \edef\skv@tempb{\unexpanded{#2}}% + \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@executeoptions{<plus sign or empty>}{<kvlist>} +% +% 1. <no-plus> = Execute options on ONLY the first path found. If an +% option isn't found on the given paths, report error (ie, +% don't save the option to the list of 'remaining keys'). Unknown +% options should be flagged by an error immediately. Hence no +% star (*) form of \dirkeys@executeoptions. +% 2. <plus> = Execute options on all the prevailing paths. If an +% option isn't found on the given paths, flag error. +% +\skvrobustdef*\dirkeys@executeoptions#1#2{% + \begingroup + \skv@inopttrue + % If the number of families is greater than one, gnored keys + % from different families will mixed. This is the case even in + % \skvsetkeys. + \skvemptify{\skv@igkeys,\skv@fams}% + \@tempcnta\skvz@ + \def\skv@pathdo##1##2{% + \advance\@tempcnta\@ne + \def\skv@pref{##1}% + \ifnum\@tempcnta=\@ne + \def\skv@firstpref{##1}% + \else + \ifx\skv@pref\skv@firstpref\else + \skv@err{You can't use multiple prefixes when + \MessageBreak calling the handler '.execute options'. + \MessageBreak Check your values of '.prefix' and '.paths'}\skv@ehd + \fi + \fi + \skvxifinTF{,##2,}{,\skv@fams,}{}{% + \edef\skv@fams{\skvaddlist,\skv@fams##2}% + }% + \skvletcs\skv@prova{##1/##2/.@ignoredkeys}% + \ifdefined\skv@prova + \edef\skv@igkeys{\skvaddlist,\skv@igkeys\skv@prova}% + \fi + }% + \dirkeys@pathlist + \ifx\skv@fams\@empty + \skv@err{No family before calling '.execute options'}\skv@ehd + \fi + \skvexpanded{\endgroup + \skvsetkeys#1[\skv@pref]{\skv@fams}[\skv@igkeys]% + }{#2}% +} +\skvrobustdef*\dirkeys@processoptions#1#2{% + \begingroup + \skv@inopttrue + % If the number of families is greater than one, gnored keys + % from different families will mixed. This is the case even in + % \skvprocessoptions. + \skvemptify{\skv@igkeys,\skv@fams}% + \@tempcnta\skvz@ + \def\skv@pathdo##1##2{% + \advance\@tempcnta\@ne + \def\skv@pref{##1}% + \ifnum\@tempcnta=\@ne + \def\skv@firstpref{##1}% + \else + \ifx\skv@pref\skv@firstpref\else + \skv@err{You can't use multiple prefixes when + \MessageBreak calling the handler '.process options'. + \MessageBreak Check your values of '.prefix' and '.paths'}\skv@ehd + \fi + \fi + \skvxifinTF{,##2,}{,\skv@fams,}{}{% + \edef\skv@fams{\skvaddlist,\skv@fams##2}% + }% + \skvletcs\skv@prova{##1/##2/.@ignoredkeys}% + \ifdefined\skv@prova + \edef\skv@igkeys{\skvaddlist,\skv@igkeys\skv@prova}% + \fi + }% + \dirkeys@pathlist + \ifx\skv@fams\@empty + \skv@err{No family before calling '.process options'}\skv@ehd + \fi + \skvexpanded{\endgroup + \skvprocessoptions#1#2[\skv@pref]<\skv@fams>[\skv@igkeys]% + }% +} + +% ++++++++ Keys for the handler <.mega process options> +++++++++ % + +\directkeys*{% + .path={SKV/megapox}, + .holder prefix=dirkeys@pox@, + .initialize keys after define=true, + .new keys={ + .ord/.ignore options// + \def\dirkeys@pox@igkeys{#1} + \skvstripouterbraces{2}\dirkeys@pox@igkeys + , + .ord/{.prefix,.options prefix}/\skvdefaultprefix/ + \skv@ifoneprefix{#1}{% + \edef\dirkeys@pox@pref{#1} + \skvstripouterbraces{2}\dirkeys@pox@pref + }{% + \skv@err{Only one prefix is allowed in + \MessageBreak processing options}\skv@ehd + } + , + .ord/{.family,.families,.options families,.options family}/ + \@currname.\@currext/ + \edef\dirkeys@pox@fams{#1} + \skvstripouterbraces{2}\dirkeys@pox@fams + \skv@makepaths\dirkeys@pox@pref\dirkeys@pox@fams + \dirkeys@megapox@pathlist\skv@pathdo + , + .ord/{path,.paths,.option path,.option paths}/ + {KV/\@currname.\@currext}/ + \skv@formatpaths{#1}\dirkeys@megapox@pathlist + , + .ord/{.copy class options,.include class options}/true/ + \edef\dirkeys@pox@copyclass{0\skvifstrcmpTF{#1}{true}{0}{1}} + , + }, +} + +\skvnewnumbers[skv@]{megapoxdepth} +\skvrobustdef*\dirkeys@megaprocessoptions#1{% + \skvpushfunctions\dirkeys@megapox{% + \do\dirkeys@pathlist\do\skv@pathdo\do\dirkeys@megapox@pathlist + \do\dirkeys@pox@igkeys\do\dirkeys@pox@copyclass + }\skv@megapoxdepth + \skvemptify{\dirkeys@megapox@pathlist,\dirkeys@pox@igkeys}% + \let\dirkeys@pox@pref\skvdefaultprefix + \def\dirkeys@pox@copyclass{01}% + \skvsetkeys[SKV]{megapox}{#1}% + \ifx\dirkeys@pox@igkeys\@empty + % So that all '...@ignoredkeys' will be undefined: + \let\dirkeys@pox@igkeys\undefined + \fi + \let\dirkeys@pathlist\dirkeys@megapox@pathlist + \def\skv@pathdo##1##2{% + \skvcslet{##1/##2/.@ignoredkeys}\dirkeys@pox@igkeys + }% + \dirkeys@pathlist + \skvexpanded{% + \dirkeys@processoptions\if\dirkeys@pox@copyclass*\fi{+}% + }% + \skvpopfunctions\dirkeys@megapox\skv@megapoxdepth +} + +% +++++++++++++ Handlers for executing and processing options ++++++++++++ % + +\directkeys*{% + .new handlers={ + {.action for unknown options,.action for unknown options}={ + \let\@fileswith@pti@ns\@badrequireerror + \skv@dox{#1} + }, + {.declare option,.declare options,.define option,.define options}={ + \dirkeys@declareoptions{}{#1} + }, + {.new option,.new options}={ + \dirkeys@declareoptions{*}{#1} + }, + {.execute options,.executeoptions,.execute options in one family}={ + \dirkeys@executeoptions{}{#1} + }, + {.execute options*,.executeoptions*}={ + \skv@err + {Handler '.execute options*' is undefined} + {Handler '.execute options' has no star (*) variant, + \MessageBreak since we don't want to ignore unknown + \MessageBreak options from within package or class file.}% + }, + {.execute options+,.executeoptions+,.execute options in all families}={ + \dirkeys@executeoptions{+}{#1} + }, + % 1. All <.process options> handlers will filter out ignored keys. + % 2. <.process options*> will copy class options. + {.process options,.processoptions,.process options in one family}={ + \dirkeys@processoptions{}{} + }, + {.process options*,.processoptions*, + .copy class options and process options, + .copy class options and process options in one family}={ + \dirkeys@processoptions{*}{} + }, + {.process options+,.processoptions+,.process options in all families}={ + \dirkeys@processoptions{}{+} + }, + {.process options*+,.processoptions*+, + .copy class options and process options in all families}={ + \dirkeys@processoptions{*}{+} + }, + % The handler <.mega process options> will: + % 1. Search all families in its key <paths>. + % 2. Not filter ignored paths and keys; in fact, it will not + % use active paths and keys. + % 3. The handler '.mega process options' has the keys <ignore options>, + % <options prefix>, <options families>, and <copy class options>. + % These keys have aliases. + % See an example later in this file. + {.mega process options,.megaprocessoptions}={ + \dirkeys@megaprocessoptions{#1} + }, + {.mega process options*,.megaprocessoptions*}={ + \dirkeys@megaprocessoptions{#1,copy class options} + }, + }, + .handler let={ + {.action for unknown option,.every unknown option}= + .action for unknown options, + }, +} + +% Loading a package while in options section is illegal. The command +% \skvafterprocessoptions can be used to defer the loading of packages +% till after the options section: +\skvrobustdef*\skvafterprocessoptions{% + \expandafter\skvgappendtomacro + \csname skv@\@currname.\@currext @afterprocess@hook\endcsname +} +\skvnewlet\skvifpackageloadedTF\@ifpackageloaded + +\skvnewdef*\skv@visiteduseoptions{01} +\skvrobustdef*\skv@removeoption#1{% + \ifx\@unusedoptionlist\@empty\else + \if\skv@visiteduseoptions\else + \skvcsvnormalize\@unusedoptionlist + \def\skv@visiteduseoptions{00}% + \fi + \begingroup + \skv@g@tkeyname#1=\skv@getname@nil\skv@tempb + \@onelevel@sanitize\skv@tempb + \def\skv@tempa{}% + \skvcommaloop*\@unusedoptionlist\skv@prova{% + \expandafter\skv@g@tkeyname\skv@prova=\skv@getname@nil\skv@provb + \@onelevel@sanitize\skv@provb + \ifx\skv@provb\skv@tempb\else + \edef\skv@tempa{\skvaddlist,\skv@tempa\skvexpandonce\skv@prova}% + \fi + }% + \let\@unusedoptionlist\skv@tempa + \skvaftergroupdef\@unusedoptionlist\endgroup + \fi +} +\skvonlypreamble{% + \skvdeclareoption,\skvexecuteoptions,\skvprocessoptions,\skv@removeoption, + \skveveryunknownoption,\dirkeys@declareoptions, + \dirkeys@megaprocessoptions,\dirkeys@processoptions,\dirkeys@executeoptions, + \skvprocessoptionswithclassoptions,\skvprocessoptionswithoutclassoptions +} + +%+++++++++++++++++ This package's options section ++++++++++++++++++% + +\skvrobustdef*\setupskeyval{\skvsetkeys[SKV]{skeyval}} +\skv@inoptionsectrue +\directkeys*{% + .every unknown option={ + \@onelevel@sanitize\CurrentOption + \PackageWarning{skeyval}{Unknown option '\CurrentOption' ignored}% + }, + .path=SKV/skeyval, + .holder prefix=skv@, + .new options={ + .bool/{verbose,tracingkeys}/true, + .ord/{keyparser,key parser}/{,}/ + \def\skv@keyparser{#1}% + \skvstripouterbraces{2}\skv@keyparser, + .bool/compatibility/true/ + \ifskv@compatibility + \skvafterprocessoptions{% + \skvifpackageloadedTF{skeyval-bc}{}{% + \RequirePackage{skeyval-bc}% + } + }% + \fi + , + }, + .execute options={ + tracingkeys=false,keyparser,verbose=false + }, + .mega process options={ + .options prefix=SKV, + .options families={skeyval,definekeys}, + .ignore options={}, + .copy class options, + }, +} +\skv@inoptionsecfalse + +\skv@restorecodes +\endinput |