diff options
author | Norbert Preining <norbert@preining.info> | 2023-01-24 03:02:57 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-01-24 03:02:57 +0000 |
commit | b06c6695162b493aeec3b594c6beb518c15c98ba (patch) | |
tree | 0c1dc3cfc73ea789d6bdc4673d776f3ef652d6d8 /macros/generic/expkv-bundle/expkv-opt.dtx | |
parent | 4451a2090261307c963209bc65fea9fde4eceeea (diff) |
CTAN sync 202301240302
Diffstat (limited to 'macros/generic/expkv-bundle/expkv-opt.dtx')
-rw-r--r-- | macros/generic/expkv-bundle/expkv-opt.dtx | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/expkv-opt.dtx b/macros/generic/expkv-bundle/expkv-opt.dtx new file mode 100644 index 0000000000..d2b51d0cb0 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-opt.dtx @@ -0,0 +1,796 @@ +% \iffalse meta-comment +% +% File: expkv-opt.dtx Copyright (C) 2020-2023 Jonathan P. Spratte +% +% This work may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this license or +% (at your option) any later version. The latest version of this license is in +% the file: +% +% http://www.latex-project.org/lppl.txt +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\expkvDocNoGenerate{} +\input expkv-bundle.ins +\generate{\file{expkv-opt.sty}{\from{expkv-opt.dtx}{pkg}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvo} +% +% \gobbledocstriptag +%<*pkg> +% +% First we check whether the \LaTeXe\ kernel supports raw options. If it doesn't +% we check whether a specific version was requested, and if that's not the case +% we manually run |\pkgcls@parse@date@arg| with the last version that supported +% non-raw options. +% \begin{macrocode} +\IfFormatAtLeastTF{2021/05/01} + {} + {% + \ifx\pkgcls@targetlabel\@empty + \ifnum\requestedLaTeXdate=\pkgcls@targetdate + \pkgcls@parse@date@arg{=v0.1}% + \fi + \fi + } +% \end{macrocode} +% Then we tell \LaTeXe\ where to find which release so that the package rollback +% code of \LaTeXe\ can do its thing. +% \begin{macrocode} +\DeclareRelease{v0.1}{2020/10/10}{expkv-opt-2020-10-10.sty} +\DeclareCurrentRelease{v0.2}{2021/04/04} +% \end{macrocode} +% +% Start the package with the typical \LaTeX\ standards. +% +% \begin{macro}{\ekvoVersion,\ekvoDate} +% Store the package's version and date in two macros. +% \begin{macrocode} +\newcommand*\ekvoVersion{1.0} +\newcommand*\ekvoDate{2023-01-23} +% \end{macrocode} +% \end{macro} +% And we report who we are and what we need. +% \begin{macrocode} +\ProvidesPackage{expkv-opt} + [% + \ekvoDate\space v\ekvoVersion\space + parse class and package options with expkv% + ] +\RequirePackage{expkv} +% \end{macrocode} +% +% +% \subsection{Loop} +% +% \begin{macro}[internal] +% {\ekvo@CurrentOption@loop,\ekvo@CurrentOption@loop@,\ekvo@end@loop} +% We'll need some loop which can iterate over a comma separated list. The loop +% is very basic and only works for commas of category~12. First we insert the +% delimiters for the actual loop. The |\ekv@set@other| is necessary to get a +% functional |\ekvmorekv| in this loop. +% \begin{macrocode} +\protected\long\def\ekvo@CurrentOption@loop#1#2% + {% + \ekvo@CurrentOption@loop@#2\ekv@set@other\ekv@mark#1,\ekv@stop,\ekvo@tail + } +% \end{macrocode} +% The actual loop checks whether the final element has been read and if so +% ends the loop. Else blank elements are ignored, |\CurrentOption| is set and +% the macro which parses the list elements called. Then call the next +% iteration. +% \begin{macrocode} +\long\def\ekvo@CurrentOption@loop@#1\ekv@set@other#2,% + {% + \ekv@gobble@from@mark@to@stop#2\ekvo@end@loop\ekv@stop + \ekv@ifblank{#2}% + {}% + {% + \edef\CurrentOption{\unexpanded\expandafter{\ekv@gobble@mark#2}}% + #1{#2}% + }% + \ekvo@CurrentOption@loop@#1\ekv@set@other\ekv@mark + } +\long\def\ekvo@end@loop#1\ekvo@tail{} +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Tests} +% +% \begin{macro}[internal]{\ekvo@ifx@TF,\ekvo@ifx@T,\ekvo@ifx@F} +% We'll need branching |\ifx| tests so that user input containing unbalanced +% \TeX\ ifs doesn't break (at least not because of us, everything else is the +% fault of \LaTeXe). +% \begin{macrocode} +\def\ekvo@ifx@TF#1#2{\ifx#1#2\ekv@fi@firstoftwo\fi\@secondoftwo} +\def\ekvo@ifx@T#1#2{\ifx#1#2\ekv@fi@firstofone\fi\@gobble} +\def\ekvo@ifx@F#1#2{\ifx#1#2\ekv@fi@gobble\fi\@firstofone} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@do@with@set,\ekvo@name,\ekvo@setname} +% This test checks whether the \set\ is defined. If it is we store it in +% |\ekvo@setname| and set |\ekvo@name| to a short cut to get the \key's +% callback name. Next we execute the code in |#2|, if the \set\ isn't defined +% |#2| is gobbled. +% \begin{macrocode} +\protected\def\ekvo@do@with@set#1#2% + {% + \ekvifdefinedset{#1}% + {% + \expandafter + \let\expandafter\ekvo@name\csname\ekv@undefined@set{#1}\endcsname + \def\ekvo@setname{#1}% + #2% + }% + {\ekvo@err@undefined@set{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Key handlers} +% +% \expkvo\ uses handlers specifying what happens if a parsed \key\ is defined or +% undefined. +% +% \begin{macro}[internal] +% {\ekvo@handle@undefined@k@pkg,\ekvo@handle@undefined@kv@pkg} +% The case for undefined keys in a local list of a package is easy, just throw +% appropriate errors. +% \begin{macrocode} +\protected\long\def\ekvo@handle@undefined@k@pkg#1% + {% + \ekv@ifdefined{\ekvo@name{#1}}% + {\ekvo@err@value@required{#1}}% + {\ekvo@err@undefined@key{#1}}% + } +\def\ekvo@handle@undefined@kv@pkg#1#2% + {% + \ekv@ifdefined{\ekvo@name{#1}N}% + {\ekvo@err@value@forbidden{#1}}% + {\ekvo@err@undefined@key{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@addto@unused@one,\ekvo@addto@unused@two, +% \ekvo@rmfrom@unused@one,\ekvo@rmfrom@unused@two +% } +% These macros will add or remove the |\CurrentOption| to or from the list of +% unused global options. Since |\ekvo@do@unusedoptionlist| will have some +% overhead before calling the list changing macro in filtering the current +% option, we use an optimization here in that we check whether the list is +% empty before calling the |rmfrom| function. +% \begin{macrocode} +\long\def\ekvo@addto@unused@one#1{\ekvo@do@unusedoptionlist\ekvo@addnewto@list} +\long\def\ekvo@addto@unused@two#1#2{\ekvo@do@unusedoptionlist\ekvo@addnewto@list} +\long\def\ekvo@rmfrom@unused@one#1% + {% + \ekvo@ifx@F\@unusedoptionlist\@empty + {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% + } +\long\def\ekvo@rmfrom@unused@two#1#2% + {% + \ekvo@ifx@F\@unusedoptionlist\@empty + {\ekvo@do@unusedoptionlist\ekvo@rmfrom@list}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@do@unusedoptionlist, +% \ekvo@prepare@unusedoption, +% \ekvo@prepare@unusedoption@a, +% \ekvo@prepare@unusedoption@b, +% \ekvo@prepare@unusedoption@c +% } +% The way the new \LaTeXe\ kernel handles the unused option list changed. Now +% not the entire |\CurrentOption| is listed, but just everything up to the +% first equals sign, and spaces got zapped, doesn't matter whether the raw +% option list gets used or not. So we have to zap spaces and remove everything +% from the first equals sign onwards. The code used here will fail if the +% current option contains an |\ekv@mark| or |\ekv@stop| before the first +% equals sign (this seems rather unlikely). +% \begin{macrocode} +\protected\def\ekvo@do@unusedoptionlist#1% + {% + \let\ekvo@unpreparedCurrentOption\CurrentOption + \edef\CurrentOption + {\expandafter\ekvo@prepare@unusedoption\CurrentOption=\ekv@mark}% + #1\@unusedoptionlist + \let\CurrentOption\ekvo@unpreparedCurrentOption + } +\def\ekvo@prepare@unusedoption{\ekvo@prepare@unusedoption@a\@empty} +\def\ekvo@prepare@unusedoption@a#1% + {% + \long\def\ekvo@prepare@unusedoption@a##1=##2\ekv@mark + {% + \ekvo@prepare@unusedoption@b##1\ekv@stop + \ekv@mark\ekvo@prepare@unusedoption@b + #1\ekv@mark\ekvo@prepare@unusedoption@c + }% + } +\ekvo@prepare@unusedoption@a{ } +\long\def\ekvo@prepare@unusedoption@b#1 #2\ekv@mark#3{#3#1#2\ekv@mark#3} +\long\def\ekvo@prepare@unusedoption@c + #1\ekv@stop + \ekv@mark\ekvo@prepare@unusedoption@b\ekv@mark\ekvo@prepare@unusedoption@c + {\unexpanded\expandafter{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@set@handlers@local,\ekvo@set@handlers@global, +% \ekvo@set@handlers@list +% } +% These macros are boring. They just set up the handlers to respect the rules +% documented earlier. +% \begin{macrocode} +\protected\def\ekvo@set@handlers@local + {% + \ekvo@if@need@handlers + {% + \ifx\@currext\@clsextension + \ifx\@classoptionslist\relax + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + \else + \expandafter + \ifx + \csname @raw@opt@\@currname.\@currext\endcsname + \@raw@classoptionslist + \let\ekvo@handle@undefined@k\ekvo@addto@unused@one + \let\ekvo@handle@undefined@kv\ekvo@addto@unused@two + \unless\ifx\@unusedoptionlist\@empty + \let\ekvo@handle@defined@k\ekvo@rmfrom@unused@one + \let\ekvo@handle@defined@kv\ekvo@rmfrom@unused@two + \fi + \else + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + \fi + \fi + \else + \let\ekvo@handle@undefined@k\ekvo@handle@undefined@k@pkg + \let\ekvo@handle@undefined@kv\ekvo@handle@undefined@kv@pkg + \fi + }% + } +\protected\def\ekvo@set@handlers@global + {% + \unless\ifx\@unusedoptionlist\@empty + \let\ekvo@handle@defined@k\ekvo@rmfrom@unused@one + \let\ekvo@handle@defined@kv\ekvo@rmfrom@unused@two + \fi + \ekvo@if@need@handlers + {% + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + }% + } +\protected\def\ekvo@set@handlers@list + {% + \ekvo@if@need@handlers + {% + \let\ekvo@handle@undefined@k\@gobble + \let\ekvo@handle@undefined@kv\@gobbletwo + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@if@need@handlers,\ekvo@dont@need@handlers} +% If the user specifies handlers this macro will be let to +% |\ekvo@dont@need@handlers|, which will act like |\@gobble| and also let it +% to |\@firstofone| afterwards. +% \begin{macrocode} +\let\ekvo@if@need@handlers\@firstofone +\protected\long\def\ekvo@dont@need@handlers#1% + {% + \let\ekvo@if@need@handlers\@firstofone + }% +% \end{macrocode} +% \end{macro} +% +% We have to set the default for the handlers of defined keys, because they +% don't necessarily get defined before a list is parsed. +% \begin{macrocode} +\let\ekvo@handle@defined@k\@gobble +\let\ekvo@handle@defined@kv\@gobbletwo +% \end{macrocode} +% +% +% \subsection{Processing list elements} +% +% \begin{macro}[internal]{\ekvo@process@common} +% All the key processing frontend macros use the same basic structure. |#1| +% will be a simple test, deciding whether the list will really be parsed or +% not, |#3| will be the \set, and |#2| will be the individual code of the +% frontend macro which should be executed if both the test in |#1| is true and +% the \set\ is defined. +% \begin{macrocode} +\protected\def\ekvo@process@common#1#2#3% + {% + #1{\ekvo@do@with@set{#3}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@process@list} +% This macro only expands the list holding macro and forwards it to the +% loop macro. +% \begin{macrocode} +\protected\def\ekvo@process@list#1% + {% + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@parse + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvo@parse, +% \ekvo@parse@kv,\ekvo@parse@kv@,\ekvo@expansion@kv,\ekvo@expansion@kv@, +% \ekvo@parse@k, \ekvo@parse@k@, \ekvo@expansion@k, \ekvo@expansion@k@ +% } +% This macro calls internals of \expkv\ such that the input is split at an +% equals sign of category other. +% \begin{macrocode} +\long\def\ekvo@parse#1% + {% + \ekv@eq@other#1\ekv@nil\ekv@mark\ekvo@parse@kv + =\ekv@mark\ekvo@parse@k + } +% \end{macrocode} +% If there was an equals sign, this will be called and remove the remainder of +% the split. Afterwards the \expnotation\ is checked and possibly executed, +% and the \kv\ pair is set. While reinsertion works via the |Rr| +% \expansion-rule, it might affect the unused global option list. +% \begin{macrocode} +\long\def\ekvo@parse@kv#1\ekv@stop#2\ekv@nil=\ekv@mark\ekvo@parse@k + {\ekv@strip{#2}{\ekv@strip{#1}\ekvo@parse@kv@}} +\long\def\ekvo@parse@kv@#1#2% + {% + \ekv@ifexp{#1}% + {\ekvo@expansion@kv{#2}}% + {\ekvo@set@kv{#1}{#2}}% + } +% \end{macrocode} +% Check for \expansion-rules was true, now we need to execute them, and use +% the result in |\ekvo@set@kv|. Also we have to handle the key as if it was a +% defined key if the |\r| \expansion-rule was found (which potentially removes +% it from the list of global unused options). +% \begin{macrocode} +\long\def\ekvo@expansion@kv#1#2#3#4% + {% + \ekv@expansion@rule@{#1}#2\ekv@mark\ekv@stop{#3}{}% + {\ekvo@handle@defined@kv{#3}{#1}\ekvmorekv}% + \ekvo@expansion@kv@ + } +\long\def\ekvo@expansion@kv@#1#2{\ekvo@set@kv{#1}{#2}} +% \end{macrocode} +% And basically the same two biggish steps, but for the case that no equals +% sign is found. +% \begin{macrocode} +\long\def\ekvo@parse@k#1\ekv@nil\ekv@mark\ekvo@parse@kv\ekv@stop\ekv@mark + {\ekv@strip{#1}\ekvo@parse@k@} +\long\def\ekvo@parse@k@#1% + {% + \ekv@ifexp{#1}% + \ekvo@expansion@k + {\ekvo@set@k{#1}}% + } +\long\def\ekvo@expansion@k#1#2#3% + {% + \ekv@expansion@rule@{#2}#1\ekv@mark\ekv@stop{}{}% + {\ekvo@handle@defined@k{#2}\ekvmorekv}% + \ekvo@expansion@k@ + } +\long\def\ekvo@expansion@k@#1#2{\ekvo@set@k{#2}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@set@k,\ekvo@set@kv} +% These two macros check whether the key is defined and if so call the handler +% for defined keys and execute the key, else the handler for undefined keys is +% called. +% \begin{macrocode} +\protected\def\ekvo@set@k#1% + {% + \ekv@ifdefined{\ekvo@name{#1}N}% + {% + \ekvo@handle@defined@k{#1}% + \csname\ekvo@name{#1}N\endcsname + }% + {\ekvo@handle@undefined@k{#1}}% + } +\protected\def\ekvo@set@kv#1#2% + {% + \ekv@ifdefined{\ekvo@name{#1}}% + {% + \ekvo@handle@defined@kv{#1}{#2}% + \csname\ekvo@name{#1}\endcsname{#2}% + }% + {\ekvo@handle@undefined@kv{#1}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{List variable helpers} +% +% \begin{macro}[internal]{\ekvo@addto@list} +% This macro is rather simple. If the list to which the |\CurrentOption| +% should be added is empty we can just let the list to the |\CurrentOption|. +% Else we have to expand the list once and the |\CurrentOption| once. +% \begin{macrocode} +\protected\def\ekvo@addto@list#1% + {% + \ekvo@ifx@TF#1\@empty + {\let#1\CurrentOption}% + {% + \edef#1% + {% + \unexpanded\expandafter{#1},% + \unexpanded\expandafter{\CurrentOption}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@addnewto@list} +% This works just like |\ekvo@addto@list|, but it only adds elements which are +% not yet part of the list, hence we check if the option is already in the +% list. +% \begin{macrocode} +\protected\def\ekvo@addnewto@list#1% + {% + \ekvo@ifx@TF#1\@empty + {\let#1\CurrentOption}% + {% + \ekvo@if@in@list#1% + {}% + {% + \edef#1% + {% + \unexpanded\expandafter{#1},% + \unexpanded\expandafter{\CurrentOption}% + }% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvo@if@in@list,\ekvo@if@in@list@,\ekvo@if@in@list@result} +% This is a slow but robust test whether some option is already contained in a +% list. Using argument gobbling logic would be faster, but that way wouldn't +% be robust for elements containing |{|, |}|, or |#|. Just loop over the code +% and compare each element, end early if a match is found. +% \begin{macrocode} +\protected\def\ekvo@if@in@list#1% + {% + \ekvo@ifx@TF#1\@empty + \@secondoftwo + {% + \let\ekvo@curropt\CurrentOption + \let\ekvo@if@in@list@result\@secondoftwo + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@if@in@list@ + \let\CurrentOption\ekvo@curropt + \ekvo@if@in@list@result + }% + } +\protected\long\def\ekvo@if@in@list@#1% + {% + \ekvo@ifx@T\ekvo@curropt\CurrentOption + {% + \let\ekvo@if@in@list@result\@firstoftwo + \ekvo@end@loop + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvo@rmfrom@list,\ekvo@rmfrom@list@} +% This works by looping over every list item and comparing it to +% |\ekvo@curropt| which stores the real |\CurrentOption|. This is +% comparatively slow, but works for items containing braces unlike what +% \LaTeXe\ does. We could be faster for items not containing braces, though. +% \begin{macrocode} +\protected\def\ekvo@rmfrom@list#1% + {% + \ekvo@ifx@F#1\@empty + {% + \let\ekvo@tmp@list\@empty + \let\ekvo@curropt\CurrentOption + \expandafter\ekvo@CurrentOption@loop\expandafter{#1}\ekvo@rmfrom@list@ + \let\CurrentOption\ekvo@curropt + \let#1\ekvo@tmp@list + }% + } +\protected\long\def\ekvo@rmfrom@list@#1% + {% + \ekvo@ifx@F\CurrentOption\ekvo@curropt + {\ekvo@addto@list\ekvo@tmp@list}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Errors} +% +% \begin{macro}[internal] +% { +% \ekvo@err@undefined@key,\ekvo@err@value@required, +% \ekvo@err@value@forbidden,\ekvo@err@undefined@set +% } +% Just some macros to throw errors in the few cases an error has to be thrown. +% \begin{macrocode} +\protected\def\ekvo@err@undefined@key#1% + {% + \ekvo@pkg@cls@error + {Undefined option `\detokenize{#1}'}% + {The used option was not defined. Perhaps you misspelled it?}% + } +\protected\def\ekvo@err@value@required#1% + {% + \ekvo@pkg@cls@error + {Missing value for option `\detokenize{#1}'}% + {The used option requires a value or you misspelled its name.}% + } +\protected\def\ekvo@err@value@forbidden#1% + {% + \ekvo@pkg@cls@error + {Unwanted value for option `\detokenize{#1}'}% + {The used option doesn't support a value or you misspelled its name.}% + } +\protected\def\ekvo@err@undefined@set#1% + {% + \PackageError{expkv-opt}% + {Undefined set `#1'}% + {The set for which you try to parse options isn't defined in expkv.}% + } +\protected\def\ekvo@pkg@cls@error + {% + \ekvo@ifx@TF\@currext\@clsextension + \ClassError + \PackageError + \@currname + } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{User Interface} +% +% The user interface macros just put together the bits and pieces. +% +% \begin{macro}{\ekvoProcessOptions} +% First we check if user-defined handlers were used. If that's the case we +% need to store them and restore them for each list processor. Else just call +% them sequentially. +% \begin{macrocode} +\protected\def\ekvoProcessOptions#1% + {% + \ekvo@if@need@handlers + {% + \ekvoProcessGlobalOptions{#1}% + \ekvoProcessLocalOptions {#1}% + \ekvoProcessFutureOptions{#1}% + \@gobbletwo + }% + \@firstofone + {% + \let\ekvo@tmpa\ekvo@handle@undefined@k + \let\ekvo@tmpb\ekvo@handle@undefined@kv + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessGlobalOptions{#1}% + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessLocalOptions {#1}% + \ekvoUseUnknownHandlers\ekvo@tmpa\ekvo@tmpb + \ekvoProcessFutureOptions{#1}% + \let\ekvo@tmpa\ekvo@undefined + \let\ekvo@tmpb\ekvo@undefined + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessLocalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessLocalOptions + {% + \ekvo@process@common + {\ekv@ifdefined{@raw@opt@\@currname.\@currext}\@firstofone\@gobble}% + {% + \ekvo@set@handlers@local + \expandafter + \ekvo@process@list\csname @raw@opt@\@currname.\@currext\endcsname + \AtEndOfPackage{\let\@unprocessedoptions\relax}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessGlobalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessGlobalOptions + {% + \ekvo@process@common{\ekvo@ifx@F\@classoptionslist\relax}% + {% + \ekvo@set@handlers@global + \ekvo@process@list\@raw@classoptionslist + \let\ekvo@handle@defined@k\@gobble + \let\ekvo@handle@defined@kv\@gobbletwo + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessUnusedGlobalOptions} +% \begin{macrocode} +\protected\def\ekvoProcessUnusedGlobalOptions#1% + {% + \PackageError{expkv-opt}% + {% + \noexpand + \ekvoProcessUnusedGlobalOptions no longer works because of changes in% + \MessageBreak + the LaTeX2e kernel% + }% + {}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessFutureOptions} +% Parsing future options (without patching kernel internals) is only possible +% with kernel versions after 2022-11-01. +% The user macro needs to store the user-set handlers for later if +% |\ekvoUseUnknownHandlers| was used. Then it stores the correct code to +% process future options in the |opt@handler@| variable. +% \begin{macrocode} +\IfFormatAtLeastTF{2022/11/01} + {% + \protected\def\ekvoProcessFutureOptions#1% + {% + \ekvo@if@need@handlers\@gobbletwo + \@firstofone + {% + \expandafter\let + \csname ekvo@future@undefined@k@\@currname.\@currext\endcsname + \ekvo@handle@undefined@k + \expandafter\let + \csname ekvo@future@undefined@kv@\@currname.\@currext\endcsname + \ekvo@handle@undefined@kv + }% + \protected\expandafter + \xdef\csname opt@handler@\@currname.\@currext\endcsname + {\ekv@unexpanded{\ekvo@future{#1}}}% + } +% \end{macrocode} +% \begin{macro}[internal]{\ekvo@future,\ekvo@set@handlers@future} +% Key parsing is pretty similar to |\ekvoProcessLocalOptions|, only the +% handlers are defined differently, namely it is checked whether they are +% user-specified, if not we reuse the undefined handlers for packages (which +% will throw an error if an unknown key is found). +% \begin{macrocode} + \protected\def\ekvo@future + {% + \ekvo@process@common + {\ekv@ifdefined{@raw@opt@\@currname.\@currext}\@firstofone\@gobble}% + {% + \ekvo@set@handlers@future + \expandafter\ekvo@process@list + \csname @raw@opt@\@currname.\@currext\endcsname + \AtEndOfPackage{\let\@unprocessedoptions\relax}% + }% + } + \protected\def\ekvo@set@handlers@future + {% + \expandafter\let\expandafter + \ekvo@handle@undefined@k + \csname + \ekv@ifdefined{ekvo@future@undefined@k@\@currname.\@currext}% + {ekvo@future@undefined@k@\@currname.\@currext}% + {ekvo@handle@undefined@k@pkg}% + \endcsname + \expandafter\let\expandafter + \ekvo@handle@undefined@kv + \csname + \ekv@ifdefined{ekvo@future@undefined@kv@\@currname.\@currext}% + {ekvo@future@undefined@kv@\@currname.\@currext}% + {ekvo@handle@undefined@kv@pkg}% + \endcsname + } +% \end{macrocode} +% \end{macro} +% \begin{macrocode} + } + {% + \protected\def\ekvoProcessFutureOptions#1% + {% + \PackageWarning{expkv-opt}% + {LaTeX-kernel is too old to process future options.\@gobbletwo}% + \protected\gdef\ekvoProcessFutureOptions##1{}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoProcessOptionsList} +% \begin{macrocode} +\protected\def\ekvoProcessOptionsList#1% + {% + \ekvo@process@common{\ekvo@ifx@F#1\@empty}% + {% + \ekvo@set@handlers@list + \ekvo@process@list#1% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvoUseUnknownHandlers} +% \begin{macro}[internal]{\ekvoUseUnknownHandlers@n,\ekvoUseUnknownHandlers@s} +% \begin{macrocode} +\protected\def\ekvoUseUnknownHandlers + {% + \let\ekvo@if@need@handlers\ekvo@dont@need@handlers + \@ifstar\ekvoUseUnknownHandlers@s\ekvoUseUnknownHandlers@n + } +\protected\def\ekvoUseUnknownHandlers@s + {% + \long\def\ekvo@handle@undefined@k##1% + {% + \ekv@ifdefined{\ekvo@name{}uN}% + {% + \csname\ekvo@name{}uN\expandafter\endcsname\expandafter + {\detokenize{##1}}{##1}% + }% + {\@gobble}% + }% + \long\def\ekvo@handle@undefined@kv##1##2% + {% + \ekv@ifdefined{\ekvo@name{}u}% + {% + \csname\ekvo@name{}u\ekv@expanded{\endcsname + {\ekv@unexpanded{##2}}{\detokenize{##1}}}{##1}% + }% + {}% + }% + } +\protected\def\ekvoUseUnknownHandlers@n#1#2% + {% + \let\ekvo@handle@undefined@k#1\relax + \let\ekvo@handle@undefined@kv#2\relax + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% All user interface macros should be only used in the preamble. +% \begin{macrocode} +\@onlypreamble\ekvoProcessOptions +\@onlypreamble\ekvoProcessLocalOptions +\@onlypreamble\ekvoProcessGlobalOptions +\@onlypreamble\ekvoProcessFutureOptions +\@onlypreamble\ekvoProcessOptionsList +\@onlypreamble\ekvoUseUnknownHandlers +% \end{macrocode} +% +% \gobbledocstriptag +%</pkg> |