summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/options/options.sty
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2015-12-06 23:51:36 +0000
committerKarl Berry <karl@freefriends.org>2015-12-06 23:51:36 +0000
commitf68189f71d0a9a9a67cba3d61a6d3a1ce35ec832 (patch)
tree78bad79915ce0174ea790faf40d4b643d7684c07 /Master/texmf-dist/tex/latex/options/options.sty
parent25bc28958d2f77d9fcaa69cc06575b6b1ff6ad0f (diff)
options (6dec15)
git-svn-id: svn://tug.org/texlive/trunk@39030 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/options/options.sty')
-rw-r--r--Master/texmf-dist/tex/latex/options/options.sty1387
1 files changed, 1387 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/options/options.sty b/Master/texmf-dist/tex/latex/options/options.sty
new file mode 100644
index 00000000000..ebd0311e27d
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/options/options.sty
@@ -0,0 +1,1387 @@
+%---------------------------------------------------------------------------
+% Copyright 2015 Daan Leijen, Microsoft Corporation.
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) 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.
+%---------------------------------------------------------------------------
+\NeedsTeXFormat{LaTeX2e}[1995/12/01]
+
+\ProvidesPackage{options}[2015/03/01, Daan Leijen, Provides convenient path-value (or key-value) options]
+\RequirePackage{etoolbox}
+\RequirePackage{xcolor}
+
+\newif\ifopt@debug
+\DeclareOption{debug}{\opt@debugtrue}
+\ProcessOptions\relax
+
+% --------------------------------------------------------
+% make the package robust agains catcode changes of = and ,
+% --------------------------------------------------------
+\edef\opt@restore@catcodes{%
+ \catcode`\noexpand\=\the\catcode`\=\relax
+ \catcode`\noexpand\,\the\catcode`\,\relax
+ \let\noexpand\opt@catcodes\relax
+}
+\catcode`\=12\relax
+\catcode`\,12\relax
+
+
+% --------------------------------------------------------
+% Extensions to etoolbox
+% --------------------------------------------------------
+
+\providecommand*\@swaparg[2]{#2{#1}}
+\providecommand*\expandnext[2]{\expandafter\@swaparg\expandafter{#2}{#1}}
+
+\providecommand*\@swaptwo[2]{#2#1}
+\providecommand*\expandnextcmds[2]{\expandafter\@swaptwo\expandafter{#2}{#1}}
+
+% unsafe expand next if the command is a single token..
+\providecommand*\expandnextsingle[2]{\expandafter#1\expandafter{#2}}
+
+\providecommand*\eifstrequal[1]{\expandnextsingle\ifstrequal{#1}}
+\providecommand*\eifblank[1]{\expandnextsingle\ifblank{#1}}
+
+\providecommand*\ontoggle[2]{\iftoggle{#1}{#2}{}}
+
+\providerobustcmd*\providelength[1]{\ifdef{#1}{}{\newlength#1}}
+\providerobustcmd*\csnewlength[1]{\expandafter\newlength\csname #1\endcsname}
+\providerobustcmd*\csprovidelength[1]{\ifcsdef{#1}{}{\expandafter\newlength\csname #1\endcsname}}
+\providerobustcmd*\cssetlength[2]{\expandafter\setlength\csname #1\endcsname{#2}}
+
+% --------------------------------------------------------
+% Basic option commands
+% \option{<path>} returns the value of a path
+% \letoption{<path>}\cmd let's \cmd to the value of <path>
+% --------------------------------------------------------
+
+\newcommand*\optionname[1]{optk@#1}
+\newcommand*\option[1]{\csname optk@#1\endcsname}
+\newcommand*\letoption[2]{\expandafter\let\expandafter#2\csname optk@#1\endcsname}
+
+% \letoption@ will not be redefined for debugging and can be used to test if an option exists
+\let\letoption@\letoption
+
+% --------------------------------------------------------
+% options, optionsalso
+% main path setting routine
+% --------------------------------------------------------
+
+\newif\ifopt@allowsearch % allow searching along search-also handlers
+\newif\ifopt@ignoreunknown % ignore unknown options?
+\newif\ifopt@unknownwarnonly % issue just a warning on an unknown option? (instead of an error)
+
+% process options together with remaining ones from a previous run
+\newrobustcmd*\optionswithremaining[1]{%
+ \letoptionlist{/options/remaining}\opt@savedremaining
+ \options{#1}%
+ %\typeout{now process: \meaning\opt@savedremaining, in default: \option@defaultpath}%
+ \expandnextsingle\optionsalso{\opt@savedremaining}%
+}
+
+% process options
+\newrobustcmd*\options{%
+ \def\option@defaultpath{}%
+ \opt@allowsearchtrue%
+ \opt@ignoreunknownfalse%
+ \opt@unknownwarnonlyfalse%
+ \optionsalso%
+}
+
+% track nesting level of options (due to calls to options in an argument)
+\newif\ifopt@nested
+\newcount\opt@nesting
+
+% Process options in the context of an other options call
+% For efficiency, only save state if nested
+\newrobustcmd*\optionsalso[1]{%
+ \ifopt@nested
+ \optionsalso@save{#1}% save state in a nested context for chained handlers
+ \else
+ \opt@nestedtrue
+ \opt@parse#1,\opt@stop,%
+ \opt@nestedfalse
+ \fi
+}
+
+% this saves and restores any nested state: for now only about chained handlers
+\newrobustcmd*\optionsalso@save[1]{%
+ \opt@savestate{\the\opt@nesting}%
+ \advance\opt@nesting 1\relax
+ \opt@parse#1,\opt@stop,%
+ \advance\opt@nesting -1\relax
+ \opt@restorestate{\the\opt@nesting}%
+}
+
+\newrobustcmd*\opt@savestate[1]{%
+ \cslet{option@handlernext#1}\option@handlernext%
+ \cslet{option@handlerpath#1}\option@handlerpath%
+ \cslet{optionvalue#1}\optionvalue%
+ \def\option@handlernext{}%
+ \def\option@handlerpath{}%
+}
+
+\newrobustcmd*\opt@restorestate[1]{%
+ \letcs\option@handlernext{option@handlernext#1}%
+ \letcs\option@handlerpath{option@handlerpath#1}%
+ \letcs\optionvalue{optionvalue#1}%
+}
+
+% Qualified options -- mostly for here for compatibility with pgfqkeys
+\newrobustcmd*\qoptions[2]{%
+ \def\option@defaultpath{#1}%
+ \opt@allowsearchtrue%
+ \opt@ignoreunknownfalse%
+ \optionsalso{#2}%
+}
+
+% Qualified optionsalso
+\newrobustcmd*\qoptionsalso[2]{%
+ \def\option@defaultpath{#1}%
+ \optionsalso{#2}%
+}
+
+%-----------------------------------------------------------------------
+% The main option processing
+%
+% For an option <option> we may have these values:
+% <option> : the value read by \option{<option>}
+% /@code : the code invoked on assignment (use option@invoke to invoke explicitly), gets \optionvalue
+% /@cmdN : command handlers invoked by code with arguments from \optionvalue
+% /@type : the type of value (not set for pure code values)
+% /@unknown : an unknown handler for this path
+% /@code : taking 2 arguments: path and value
+% /@searchalso: the other paths needed to be search for this path
+% /@code
+%
+% Handlers start with a "." in their name; the handlers are found in: /handlers/<handlername>
+% Special handlers are defined in: /handlers/special/<char>
+% If a path starts with <char> it is invoked; just like first-char-syntax in pgfkeys.
+%-----------------------------------------------------------------------
+
+% this is used if no argument is given
+% define as relax, this makes it unique but also safe against infinite expansion
+\def\optionnovalue{\relax}
+
+% opt@stop is used to stop argument processing in tex \def's with patterns
+\let\opt@stop\relax
+
+% The main parsing of options
+% This code is quite expanded to make it as efficient as possible.
+% It is based on the original code by David Carlisle in the standard latex "keyval" package.
+\def\opt@parse#1,{%
+ \ifx\opt@stop#1\@empty\else
+ \opt@parse@arg#1==\opt@stop
+ \expandafter\opt@parse\fi}
+
+\def\opt@parse@arg#1=#2=#3\opt@stop{%
+ \opt@trimdef\option@rawpath{#1}%
+ \ifx\@empty#3\@empty%
+ \let\optionvalue\optionnovalue
+ \else
+ \opt@trimdef\optionvalue{#2}%
+ \fi
+ \ifx\option@rawpath\@empty
+ \ifx\optionvalue\optionnovalue\else\optionerror@nopath{#2}\fi
+ \else
+ % try path directly
+ \expandafter\opt@setcodepath@\option@rawpath\opt@stop%
+ \ifx\opt@codepath\relax
+ \opt@handle{#1}{#2}% not directly found, try handlers, search etc.
+ \fi
+ % now \optionvalue is defined; invoke the /@code
+ \opt@codepath%
+ \ifx\option@handlernext\@empty\else\optionerror@handler\fi
+ \fi
+}
+
+% Phase 2: look for a handler or search along the path
+\newrobustcmd*\opt@handle[2]{%
+ % not directly found..
+ \opt@setoptionpath{\option@rawpath}% create full option@path applying the default path
+ \option@gethandler\option@path% defines option@handlerpath & option@handler
+ \ifx\option@handler\@empty
+ % search path
+ \ifopt@allowsearch
+ \opt@searchalso{\option@path}%
+ \fi
+ \else
+ % handler
+ \letoption@{/handlers/\option@handler/@code}\opt@codepath
+ \fi
+ \ifx\opt@codepath\relax
+ \opt@handle@specials{#1}{#2}%
+ \fi
+}
+
+% Set opt@codepath from a raw path taking the default path into account
+\newcommand\opt@setcodepath[1]{\expandafter\opt@setcodepath@#1\opt@stop}
+\def\opt@setcodepath@#1#2\opt@stop{\ifx#1/\relax\letoption@{#1#2/@code}\opt@codepath\else\letoption@{\option@defaultpath/#1#2/@code}\opt@codepath\fi}
+
+% Set option@path from a raw path taking the default path into account
+\newcommand\opt@setoptionpath[1]{\expandafter\opt@setoptionpath@#1\opt@stop}
+\def\opt@setoptionpath@#1#2\opt@stop{\ifx#1/\relax\edef\option@path{#1#2}\else\edef\option@path{\option@defaultpath/#1#2}\fi}
+
+
+% Phase 3: look for a special syntax handler, or invoke the unknown handlers
+\newrobustcmd*\opt@handle@specials[2]{%
+ % special handler?
+ \edef\opt@first{\expandafter\option@firstletterof@\option@rawpath\opt@stop}%
+ \letoption@{/handlers/special/\opt@first/@code}\opt@codepath
+ \ifx\opt@codepath\relax
+ % still not found: invoke unknown handler
+ \ifopt@ignoreunknown
+ \opt@collectunknown{#1}{#2}% expects \optionvalue to be defined
+ \else
+ \opt@unknown{\option@path}%
+ \fi
+ \else
+ % it is a special handler
+ \ifx\optionvalue\optionnovalue
+ \let\optionvalue\option@rawpath%
+ \else
+ \optionerror{\option@rawpath}{special options cannot take an argument ("\meaning\optionvalue")}%
+ \fi
+ \fi
+}
+
+% Space gobbling; basically unchanged from the original definition in the keyval package by David Carlisle
+% note: takes off one layer of braces
+\newrobustcmd\opt@define@trimdef[1]{%
+ \long\def\opt@trimdef##1##2{%
+ \futurelet\opt@firsttoken\opt@trim@##2\opt@stop\opt@stop#1\opt@stop\relax##1}%
+ \def\opt@trim@{%
+ \ifx\opt@firsttoken\opt@sptoken%
+ \expandafter\opt@trim@left%
+ \else%
+ \expandafter\opt@trim@left\expandafter#1% add space token since sp@b removes one
+ \fi}%
+ \long\def\opt@trim@left#1##1 \opt@stop{\opt@trim@right##1}%
+}
+\opt@define@trimdef{ }% define trimdef with a space as argument
+\long\def\opt@trim@right#1\opt@stop#2\relax#3{\edef#3{\unexpanded{#1}}}
+{\def\:{\global\let\opt@sptoken= } \: }
+
+
+% --------------------------------------------------------
+% Searching
+% --------------------------------------------------------
+
+% search a relative path: <default path><search path>
+\newrobustcmd*\opt@searchforpath[2]{%
+ \option@ifisabsolute{#2}%
+ {\letoption@{#2/@code}\opt@codepath}%
+ {\letoption@{#1#2/@code}\opt@codepath% use default path
+ \ifx\opt@codepath\relax
+ \ifopt@allowsearch\else
+ \opt@searchalso{#1#2}%
+ \fi
+ \fi}%
+}
+
+% search an absolute path
+% this is called by search-also handlers.
+\newrobustcmd*\opt@searchforabspath[1]{%
+ \letoption@{#1/@code}\opt@codepath
+ \ifx\opt@codepath\relax
+ \ifopt@allowsearch\else
+ \opt@searchalso{#1}%
+ \fi
+ \fi
+}
+
+% \opt@searchalso: invoked to search for a path: set \opt@codepath if a match is found
+%
+% note: we can nest searches without grouping
+% because they all use the same "\opt@do@search" (option@searchalsodo) and
+% all need to break once the inner one breaks
+\newrobustcmd*\opt@searchalso[1]{%
+ \option@foreachparent\opt@do@search{#1}%
+}
+\newrobustcmd*\opt@do@search[3]{% {<root>}{<subpath>}{<name>}
+ \letoption@{#1/@searchalso/@code}\opt@temp
+ \ifx\opt@temp\relax\else
+ \edef\option@searchpath{#2#3}% subpath
+ \opt@temp% invoke search routine
+ \ifx\opt@codepath\relax\else\opt@break\fi% on found: break loop
+ \fi
+}
+
+% --------------------------------------------------------
+% Unknown handler search
+% --------------------------------------------------------
+
+% invoked on unknown path: set \opt@codepath to the unknown handler (with option@unknownpath)
+\newrobustcmd*\opt@unknown[1]{%
+ %\typeout{found unknown: #1}%
+ \def\opt@do@unknown##1##2##3{%
+ \letoption@{##1/@unknown/@code}\opt@codepath
+ \ifx\opt@codepath\relax\else
+ \edef\option@unknownpath{#1}% full path
+ \edef\option@unknownsubpath{##2##3}% subpath
+ \expandnext{\opt@addarg{#1}}{\optionvalue}% set optionvalue to two arguments: the path and the original value
+ \opt@break%
+ \fi%
+ }%
+ \option@foreachparent\opt@do@unknown{#1}%
+}
+\newrobustcmd*\opt@addarg[2]{\edef\optionvalue{\unexpanded{{#1}{#2}}}}%
+
+% invoked when unknown option needs to be saved (and ignored)
+\newrobustcmd*\opt@collectunknown[2]{%
+ \ifx\optionvalue\optionnovalue
+ \option@push{/options/remaining}{#1}%
+ \else
+ \option@push{/options/remaining}{#1={#2}}%
+ \fi
+}
+
+% --------------------------------------------------------
+% Handlers' splitting and chaining
+% --------------------------------------------------------
+
+% \option@gethandler{path}: split off the part after the "/.", sets option@handlerpath and option@handler and option@handlernext
+\def\option@gethandler#1{\expandafter\option@gethandler@#1/././.\opt@stop}
+\def\option@gethandler@#1/.#2/.#3/.#4\opt@stop{%
+ \def\option@handler{#2}%
+ \ifx\option@handler\@empty\else
+ \def\option@handlerpath{#1}%
+ \def\option@handler{#2}%
+ \def\option@handlernext{#3}%
+ \fi
+}
+\def\option@handlerpath{}
+\def\option@handlernext{}
+
+\newrobustcmd*\option@chainempty{\let\optionvalue\optionnovalue\option@chainonly}
+\newrobustcmd*\option@chainonly{\ifx\option@handlernext\@empty\else\option@chain\fi}
+\newrobustcmd*\option@chain{%
+ \ifx\option@handlernext\@empty
+ % we are done, invoke the option itself
+ \letoption@{\option@handlerpath/@code}\opt@codepath
+ \ifx\opt@codepath\relax
+ \opt@unknown{\option@handlerpath}%
+ \fi
+ \else
+ % invoke the next handler
+ %\typeout{call next handler: \option@handlerpath, \option@handlernext, \optionvalue}%
+ \edef\opt@temp{\option@handlerpath/.\option@handlernext}%
+ \option@gethandler{\opt@temp}%
+ \letoption@{/handlers/\option@handler/@code}\opt@codepath
+ \ifx\opt@codepath\relax
+ \opt@unknown{/handlers/\option@handler}%
+ \fi
+ \fi
+ \opt@codepath
+}
+
+% --------------------------------------------------------
+% Path splitting
+% --------------------------------------------------------
+
+% \option@isabsolute{path}: check if path starts with /
+\def\ifoptionisabsolute#1{\expandafter\ifoptionisabsolute@#1\opt@stop}
+\def\ifoptionisabsolute@#1#2\opt@stop{\ifx#1/\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}%
+
+% \option@splitroot{path}: split off the first directory of a path into \option@root
+% option@splitroot{/foo/bar} -> foo
+% option@splitroot{/foo} -> foo
+\newrobustcmd*\option@splitroot[1]{\expandafter\option@splitroot@#1//\opt@stop}
+\def\option@splitroot@#1/#2/#3\opt@stop{%
+ \def\option@root{#1}%
+ \ifx\option@root\@empty
+ \def\option@root{#2}%
+ \fi
+}
+
+% \option@splitpath{path}: \option@path and \option@name
+% \option@splitpath{/foo/bar/baz} -> /foo/bar baz
+\newrobustcmd*\option@splitpath[1]{%
+ \let\option@path\@empty
+ \ifoptionisabsolute{#1}{%
+ \def\opt@do@split##1##2##3{%
+ \edef\option@path{##1/}%
+ \edef\option@name{##3}%
+ \opt@break
+ }%
+ \option@foreachparent\opt@do@split{#1}%
+ }{\edef\option@name{#1}}%
+}
+
+
+% \option@foreachparent\<do>{<path>} basic routine for iterating over path components
+% applies \<do> to each parent path. todo: can we optimize this routine more for the searchalso?
+% /foo/bar/baz -> \<do>{/foo/bar}{}{baz} \<do>{/foo}{bar/}{baz} \<do>{}{foo/bar/}{baz}
+\newrobustcmd*\option@foreachparent[2]{%
+ \let\opt@donext#1%
+ \expandafter\opt@foreachparent@#2/\opt@stop\opt@stop%
+ \opt@donext{}{\option@subpath}{\option@name}%
+}
+
+\newrobustcmd*\opt@break{\def\opt@donext##1##2##3{}}
+
+\def\opt@foreachparent@/#1/#2\opt@stop#3\opt@stop{% #3 is the current root
+ \def\opt@temp{#2}%
+ \ifx\opt@temp\@empty% final path
+ \def\option@name{#1}%
+ \def\option@subpath{}%
+ \else
+ \opt@foreachparent@/#2\opt@stop#3/#1\opt@stop% recurse first
+ \opt@donext{#3/#1}{\option@subpath}{\option@name}%
+ \edef\option@subpath{#1/\option@subpath}%
+ \fi
+}
+
+
+% --------------------------------------------------------
+% Errors
+% --------------------------------------------------------
+\newrobustcmd*\optionwarning[2]{%
+ \eifblank{\option@handler}{\def\opt@temp{}}{\def\opt@temp{(in ".\option@handler") }}%
+ \PackageWarning{options}{option "#1": \opt@temp #2}{}}
+
+\newrobustcmd*\optionerror[2]{%
+ \eifblank{\option@handler}{\def\opt@temp{}}{\def\opt@temp{(in ".\option@handler") }}%
+ \PackageError{options}{option "#1": \opt@temp #2}{}}
+
+\newrobustcmd*\optionerror@expect[2]{\optionerror{#1}{unexpected "\optionvalue" (expecting #2)}}
+\newrobustcmd*\optionerror@toomany[2]{\optionerror{#1}{too many arguments (expecting #2)}}
+\newrobustcmd*\optionerror@noarg[1]{\optionerror{#1}{does not take an argument}}
+\newrobustcmd*\optionerror@needarg[1]{\optionerror{#1}{requires an argument}}
+
+\newrobustcmd*\optionerror@nopath[1]{\optionerror{?}{value without option path: "#1"}}
+\newrobustcmd*\optionerror@handler[1]{\optionerror{\option@rawpath}{handler ".\option@handlernext" is not allowed to follow the previous handlers}}%
+
+\newrobustcmd*\option@ensurefree[1]{\ifoptiondefined{#1}{\optionerror{#1}{already defined}}{}}
+\newrobustcmd*\option@ensuredefined[1]{\ifoptiondefined{#1}{}{\optionerror{#1}{unknown option}}}
+
+
+% --------------------------------------------------------
+% Basic option setting
+% --------------------------------------------------------
+
+\newrobustcmd*\edefoption[2]{\protected@edef#2{\option{#1}}}%
+
+\newrobustcmd*\option@set[2]{\csdef{optk@#1}{#2}}
+\newrobustcmd*\option@let[2]{\cslet{optk@#1}#2}
+
+\newrobustcmd*\option@def[1]{\csdef{optk@#1}}
+\newrobustcmd*\option@undef[1]{\csundef{optk@#1}}
+
+\newrobustcmd*\option@eset[2]{\expandnext{\option@set{#1}}{#2}}
+\newrobustcmd*\option@xset[2]{\protected@edef\opt@temp{#2}\option@let{#1}\opt@temp}
+
+\newrobustcmd*\option@appto[2]{\csappto{optk@#1}{#2}}
+\newrobustcmd*\option@preto[2]{\cspreto{optk@#1}{#2}}
+
+\newcommand*\ifoptiondefined[1]{\ifcsdef{optk@#1}}
+\newcommand*\ifoptionvoid[1]{\ifcsvoid{optk@#1}}
+\newcommand*\ifoptioncmd[1]{\ifcsparam{optk@#1}}
+
+\newrobustcmd*\ifoptionequal[2]{%
+ \edefoption{#1}\opt@temp
+ \eifstrequal{\opt@temp}{#2}%
+}
+
+\newrobustcmd*\ifoptionblank[2]{%
+ \edefoption{#1}\opt@temp
+ \eifblank{\opt@temp}{#2}%
+}
+
+\newcommand*\ifoptiontype[2]{%
+ \letoptiontype{#1}\opt@type
+ \eifstrequal{\opt@type}{#2}%
+}
+\newcommand*\ifoptioniscode[1]{%
+ \let\opt@next\@secondoftwo%
+ \letoption@{#1/@code}\opt@code
+ \letoption@{#1}\opt@value
+ \ifx\opt@code\opt@value
+ \let\opt@next\@firstoftwo
+ \else
+ \letoptiontype{#1}\opt@type
+ \eifstrequal{\opt@type}{code}%
+ {\let\opt@next\@firstoftwo}{}%
+ \fi
+ \opt@next
+}
+
+\newcommand*\letoptiontype[2]{%
+ \letoption@{#1/@type}#2%
+ \ifx#2\relax\def#2{code}\fi
+}%
+
+\newcommand*\optionparamcount[1]{%
+ \ifoptiondefined{#1/@cmd1}{1}{%
+ \ifoptiondefined{#1/@cmd2}{2}{%
+ \ifoptiondefined{#1/@cmd3}{3}{%
+ \ifoptiondefined{#1/@cmd4}{4}{%
+ \ifoptiondefined{#1/@cmd5}{5}{%
+ \ifoptiondefined{#1/@cmdx}{x}{%
+ 0%
+ }}}}}}%
+}
+
+\newcommand*\optionshowtype[1]{%
+ \letoptiontype{#1}\opt@type%
+ \eifstrequal{\opt@type}{code}%
+ {\option@font@special{cmd}$_\optionparamcount{#1}$}%
+ {\option@font@special{\opt@type}}%
+}
+
+% Set the current color from a color options.
+% Keeps color unchanged if the color name was blank.
+\newcommand*\optioncolor[1]{\ifoptioncolortransparent{#1}{}{\color{#1}}}
+\newcommand*\optiontextcolor[2]{\ifoptioncolortransparent{#1}{#2}{\textcolor{#1}{#2}}}
+\newcommand*\optioncolorbox[2]{\ifoptioncolortransparent{#1}{#2}{\colorbox{#1}{#2}}}
+
+\newcommand*\ifoptioncolortransparent[3]{%
+ \ifoptionblank{#1}{#2}{%
+ \ifoptiontype{#1}{color expr}{%
+ \edefoption{#1}\opt@color
+ \expandnext{\opt@colorlet{#1}}{\opt@color}%
+ }{}%
+ #3
+ }%
+}
+
+
+% convert dimension option into number in pt for use in picture
+\newcommand*\opt@unit[1]{\strip@pt\dimexpr#1\relax}
+\newcommand*\optionunit[1]{\opt@unit{\option{#1}}}
+
+% --------------------------------------------------------
+% Comma seperated values
+% --------------------------------------------------------
+
+\newcommand\option@headof[1]{\option@headof@#1,\opt@stop}%
+\def\option@headof@#1,#2\opt@stop{#1}%
+
+\providerobustcmd*\option@ifanyof[2]{%
+ \let\opt@do@next\@secondoftwo
+ \edef\opt@temp{#1}%
+ \def\opt@do@anyof##1{\eifstrequal{\opt@temp}{##1}{\let\opt@do@next\@firstoftwo\listbreak}{}}%
+ \expandnext{\forcsvlist\opt@do@anyof}{#2}%
+ \opt@do@next
+}
+
+\newrobustcmd*\ifoptionanyof[2]{\option@ifanyof{\option{#1}}{#2}}
+
+\newcount\opt@idx
+\providerobustcmd*\option@findidx[3]{%
+ \opt@idx=0\relax
+ \def#3{-1}%
+ \edef\opt@temp{#1}%
+ \def\opt@do@findidx##1{\eifstrequal{\opt@temp}{##1}{\edef#3{\the\opt@idx}\listbreak}{\advance\opt@idx 1\relax}}%
+ \expandnext{\forcsvlist\opt@do@findidx}{#2}%
+}
+
+% return the first letter of the argument or ' ' (space) if the input was empty. Does not expand the argument.
+\providecommand*\option@firstletterof[1]{\eifblank{#1}{ }{\expandafter\option@firstletterof@#1\opt@stop}}
+\def\option@firstletterof@#1#2\opt@stop{#1}
+
+
+% --------------------------------------------------------
+% Lists
+% --------------------------------------------------------
+
+\newrobustcmd*\option@setnil[1]{\option@set{#1}{}}
+\newrobustcmd*\option@push[2]{\listcsadd{optk@#1}{#2}}
+\newrobustcmd*\option@epush[2]{\listcseadd{optk@#1}{#2}}%
+\newrobustcmd\optionlistdo[2]{\def\opt@@do##1{#2}\forlistcsloop{\opt@@do}{optk@#1}}
+
+\newrobustcmd*\option@setlist[2]{\option@setnil{#1}\option@concat{#1}{#2}}
+\newrobustcmd*\option@concat[2]{%
+ \def\opt@do@concat##1{\option@push{#1}{##1}}%
+ \expandnext{\forcsvlist\opt@do@concat}{#2}%
+}
+\newcommand*\letoptionlist[2]{%
+ \def#2{}%
+ \optionlistdo{#1}{%
+ \ifdefempty#2%
+ {\def#2{##1}}%
+ {\appto#2{,##1}}%
+ }%
+}
+\newcommand*\optionlist[1]{%
+ \let\opt@comma\@empty%
+ \optionlistdo{#1}{%
+ \opt@comma##1%
+ \ifx\opt@comma\@empty
+ \def\opt@comma{,}%
+ \fi
+ }%
+}
+
+\let\ifoptionnil\ifoptionvoid
+\newrobustcmd*\ifoptioncontains[2]{\xifinlistcs{#2}{optk@#1}}
+\newrobustcmd*\option@pushifnew[2]{\ifoptioncontains{#1}{#2}{}{\option@push{#1}{#2}}}
+
+
+% --------------------------------------------------------
+% Choices: these functions are used for the choice type
+% --------------------------------------------------------
+
+% find a choice: sets opt@choicename, opt@choicevalue, and opt@choiceord
+\newcount\opt@choiceord
+\newcommand*\opt@findchoice[2]{% {<name>}{<option>}
+ \opt@choiceord=0\relax
+ \def\opt@choicevalue{}%
+ \def\opt@choicename{}%
+ \def\opt@do@choice##1{\opt@do@choice@##1\opt@stop}%
+ \def\opt@do@choice@##1=##2\opt@stop{%
+ \ifstrequal{##1}{#1}{%
+ \def\opt@choicename{##1}%
+ \def\opt@choicevalue{##2}%
+ \listbreak
+ }%
+ {\advance\opt@choiceord 1\relax}%
+ }%
+ \forlistcsloop\opt@do@choice{\optionname{#2/@choices}}%
+ \ifx\opt@choicename\@empty%
+ \opt@choiceord=-1\relax
+ \fi
+}
+
+% get choice list as comma separated name list
+\newcommand\letoptionchoices[2]{% {<option>}{\<macro>}
+ \def#2{}%
+ \def\opt@do@choice##1{\opt@do@choice@##1\opt@stop}%
+ \def\opt@do@choice@##1=##2\opt@stop{%
+ \ifx#2\@empty
+ \def#2{##1}%
+ \else
+ \appto#2{,##1}%
+ \fi
+ }%
+ \forlistcsloop\opt@do@choice{\optionname{#1/@choices}}%
+}%
+
+% parse choices into a list of elements "choice=value". Set \opt@choices and \opt@firstchoicename
+\newcommand*\opt@choices@parse[2]{% {<option>}{<user list>}
+ \def\opt@choices{}%
+ \def\opt@firstchoicename{}%
+ \edef\opt@choicepath{#1}%
+ \opt@choice@foreach#2,\opt@stop,\relax
+ %\typeout{choice list: \opt@choices}%
+}
+
+\def\opt@choice@foreach#1,{%
+ \ifx\opt@stop#1\@empty\else
+ \opt@choice@item#1==\opt@stop
+ \expandafter\opt@choice@foreach\fi}
+
+\def\opt@choice@item#1=#2=#3\opt@stop{%
+ \opt@trimdef\opt@choicename{#1}%
+ \ifx\@empty#3\@empty%
+ \let\opt@choicevalue\opt@choicename% default is the name itself
+ \else
+ \opt@trimdef\opt@choicevalue{#2}%
+ \fi
+ \expandnext{\expandnextsingle\opt@choice@push{\opt@choicename}}{\opt@choicevalue}%
+}
+
+\newcommand*\opt@choice@push[2]{%
+ %\typeout{parse choice: #1={#2}}%
+ \listadd\opt@choices{#1={#2}}%
+ \ifx\opt@firstchoicename\@empty
+ \def\opt@firstchoicename{#1}%
+ \fi
+}
+
+
+% --------------------------------------------------------
+% Invoke
+% --------------------------------------------------------
+
+\newcommand*\option@xinvoke[2]{%
+ \protected@edef\opt@temp{#2}%
+ \expandnext{\option@invoke{#1}}{\opt@temp}%
+}%
+\newcommand*\option@einvoke[2]{%
+ \expandnext{\option@invoke{#1}}{#2}%
+}%
+\newcommand*\option@invoke[2]{%
+ \option@ensuredefined{#1}%
+ \def\optionvalue{#2}%
+ \option{#1/@code}%
+}%
+\newcommand*\option@invokedefault[1]{%
+ \option@ensuredefined{#1}%
+ \let\optionvalue\optionnovalue%
+ \option{#1/@code}%
+}%
+
+
+% --------------------------------------------------------
+% Record metadata
+% --------------------------------------------------------
+
+\newrobustcmd*\option@addpathinfo[1]{%
+ \def\opt@do@add##1##2##3{%
+ \ifoptiondefined{##1/@names}{}{%
+ \option@setnil{##1/@names}\option@setnil{##1/@paths}%
+ }%
+ \eifblank{##2}%
+ {\expandnext{\option@pushifnew{##1/@names}}{##3}%
+ %\typeout{path "##1/@names": add "##3": \option{##1/@names}}%
+ }%
+ {\option@splitroot{##2}%
+ \expandnext{\option@pushifnew{##1/@paths}}{\option@root}%
+ %\typeout{path "##1/@paths": add "\option@root"; \option{##1/@paths}}%
+ }%
+ }%
+ \option@foreachparent\opt@do@add{#1}%
+}
+
+% --------------------------------------------------------
+% Basic definitions of options
+% --------------------------------------------------------
+\def\option@patch@pre{}
+\def\option@patch@post{}
+
+\newrobustcmd*\optionnewcode{\@ifstar{\optionnewcode@{true}}{\optionnewcode@{false}}}
+\newrobustcmd*\optionnewcode@[3]{%*[]
+ %\typeout{define cmd: #2}%
+ \option@ensurefree{#2}%
+ \option@splitpath{#2}% sets option@path, and option@name
+ \eifblank{\option@path}{%
+ \optionerror{#2}{no valid option name specified. Option definitions must start with a forward slash (i.e. "/path/<name>")}%
+ }{}%
+ \option@addpathinfo{#2}%
+ \ifbool{#1}% noarg? expand out for efficiency
+ {\csdef{optk@#2/@code}{%
+ \ifx\optionvalue\optionnovalue\else\optionerror@noarg{#2}\fi
+ \option@patch@pre% hook to patch in extra checking code
+ #3%
+ \option@patch@post% hook for post code.. generally unsafe due to chained handlers
+ }}%
+ {\csdef{optk@#2/@code}{%
+ \ifx\optionvalue\optionnovalue
+ \letoption@{#2/@def}\optionvalue%
+ \ifx\optionvalue\relax\optionerror@needarg{#2}\fi
+ \fi
+ \option@patch@pre
+ #3%
+ \option@patch@post
+ }}%
+ \csletcs{optk@#2}{optk@#2/@code}% point at first to the cmd
+}
+
+% define handler code; such code takes the expanded option@handlerpath and optionvalue as arguments
+\newrobustcmd*\optionnewhandler{\@ifstar{\optionnewhandler@{true}}{\optionnewhandler@{false}}}
+\newrobustcmd*\optionnewhandler@[3]{%
+ \option@ensurefree{#2}%
+ \option@def{#2/@cmd2}##1##2{#3}%
+ \optionnewcode@{#1}{#2}{\expandnext{\expandnext{\option{#2/@cmd2}}{\option@handlerpath}}{\optionvalue}}\relax%
+}
+
+
+% --------------------------------------------------------
+% Add pre/post processing to an option
+% note: patchcmd leaves some spaces behind.. can we switch to appto?
+% --------------------------------------------------------
+
+\newrobustcmd*\optionprependcode[2]{%
+ \option@ensuredefined{#1}%
+ \expandafter\patchcmd\expandafter{\csname optk@#1/@code\endcsname}{\option@patch@pre}{#2\option@patch@pre}{}{\optionerror{#1}{unable to prepend code}}%
+}
+
+\newrobustcmd*\optionappendcode[2]{%
+ \option@ensuredefined{#1}%
+ \expandafter\patchcmd\expandafter{\csname optk@#1/@code\endcsname}{\option@patch@post}{\option@patch@post #2}{}{\optionerror{#2}{unable to append code}}%
+}
+
+
+% --------------------------------------------------------
+% Bootstrap and use our options themselves to define everything else
+% --------------------------------------------------------
+
+% start bootstrap with the 'new handler' handler
+\optionnewhandler{/handlers/new handler}{\optionnewhandler{#1}{#2\option@chainempty}\option@chainempty}
+
+\options{
+ %
+ /handlers/new transformer/.new handler =\optionnewhandler{#1}{#2\option@chain},
+ /handlers/new operation/.new handler =\optionnewhandler{#1}{#2\option@chainonly},
+ /handlers/new handler*/.new handler =\optionnewhandler*{#1}{#2\option@chainempty},
+ /handlers/new operation*/.new handler =\optionnewhandler*{#1}{#2\option@chainonly},
+ %
+ /handlers/new cmd transformer/.new handler=\optionsalso{%
+ #1/.new operation* = {\expandnextsingle\optionprependcode{##1}{#2}}% prepend code to the option it is handling (##1)
+ },
+ % command transformers
+ /handlers/expands/.new cmd transformer ={\protected@edef\optionvalue{\optionvalue}},
+ /handlers/defaults/.new cmd transformer ={\option@default@transform},
+ % the "new handler" can be provided with defaults.
+ /handlers/new handler/.defaults,
+ % value transformers
+ /handlers/expand once/.new transformer =\edef\optionvalue{\expandafter\expandafter\expandafter\noexpand\optionvalue},
+ /handlers/expand twice/.new transformer =\edef\optionvalue{\expandafter\expandafter\expandafter\noexpand\optionvalue}%
+ \edef\optionvalue{\expandafter\expandafter\expandafter\noexpand\optionvalue},
+ /handlers/expanded/.new transformer =\protected@edef\optionvalue{\optionvalue},
+}
+
+% For values of the form "[default]value" we assign "default" as the default, and continue with "value"
+\newrobustcmd\option@default@transform{\expandafter\option@default@transform@\optionvalue\opt@stop}
+\def\option@default@transform@{\@ifnextchar[{\option@default@transform@opt}{\option@default@transform@end}}
+\def\option@default@transform@end#1\opt@stop{}
+\def\option@default@transform@opt[#1]#2\opt@stop{%
+ \option@set{\option@handlerpath/@def}{#1}%
+ \edef\optionvalue{\unexpanded{#2}}% preserve # arguments in optionvalue
+}
+
+
+% --------------------------------------------------------
+% command actions take arguments
+% --------------------------------------------------------
+
+% provide command pattern; defined separately so we can expand the option@handler and pass as an argument
+\newrobustcmd*\optionnewcmdx[4]{%
+ \option@def{#1/@cmdx}#2\opt@stop{#4}%
+ \optionnewcode{#1}{\expandnextcmds{\option{#1/@cmdx}}{\optionvalue#3\opt@stop}\relax}%
+}
+
+\options{
+ /handlers/new code/.new handler/.defaults = \optionnewcode{#1}{#2}\option@chainempty,
+ /handlers/new cmd*/.new handler = \optionnewcode*{#1}{#2}\option@chainempty,
+ /options/exec/.new code =\optionvalue,
+ % commands
+ /handlers/new cmd/.new handler/.defaults=\optionsalso{%
+ /options/exec={\option@def{#1/@cmd1}##1{#2}},%
+ #1/.new code={\expandnext{\option{#1/@cmd1}}{\optionvalue}},%
+ },%
+ /handlers/new cmd 2/.new handler/.defaults = \optionsalso{
+ /options/exec = {\option@def{#1/@cmd2}##1##2##3{\ifblank{##3}{#2}{\optionerror@toomany{#1}{2 (but got: ##1,##2,##3)}}}},
+ #1/.new code = {\expandnextcmds{\option{#1/@cmd2}}{\optionvalue}{}{}{}\relax},
+ },
+ /handlers/new cmd 3/.new handler/.defaults = \optionsalso{
+ /options/exec = {\option@def{#1/@cmd3}##1##2##3##4{\ifblank{##4}{#2}{\option@toomany{#1}{3 (but got: ##1,##2,##3,##4)}}}},
+ #1/.new code = {\expandnextcmds{\option{#1/@cmd3}}{\optionvalue}{}{}{}{}\relax},
+ },
+ /handlers/new cmd 4/.new handler/.defaults = \optionsalso{
+ /options/exec = \option@def{#1/@cmd4}##1##2##3##4##5{\ifblank{##5}{#2}{\optionerror@toomany{#1}{4}}},
+ #1/.new code = \expandnextcmds{\option{#1/@cmd4}}{\optionvalue}{}{}{}{}{}\relax,
+ },
+ % tuples and triples take exactly 2 or 3 arguments
+ /handlers/new cmd tuple*/.new handler/.defaults = \optionsalso{
+ #1/.new cmdx = {##1,##2,##3}{,,}%
+ {\ifstrequal{##3}{,}{#2}{\optionerror{#1}{expecting a 2 argument tuple \{_,_\}, but got "\optionvalue"}}},
+ #1/.type=cmd tuple*,
+ },
+ /handlers/new cmd tuple/.new handler/.defaults = \optionsalso{
+ /options/exec = {\option@def{#1/@cmd2}##1##2{#2}},
+ #1/.new cmdx = {##1,##2}{,}%
+ {\ifblank{##2}{\option{#1/@cmd2}{##1}{##1}}{\option{#1/@cmd2}{##1}{##2}}},
+ #1/.type=cmd tuple,
+ },
+ /handlers/new cmd triple/.new handler/.defaults = \optionsalso{
+ #1/.new cmdx = {##1,##2,##3,##4}{,,,}%
+ {\ifstrequal{##4}{,,}{#2}{\optionerror{#1}{expecting a 3 argument triple \{_,_,_\}, but got "\optionvalue"}}},
+ #1/.type=cmd triple,
+ },
+ % aliases
+ /handlers/new cmdx/.new cmd 3/.defaults = \expandnextsingle\optionnewcmdx{\option@handlerpath}{#1}{#2}{#3},
+ /handlers/new code alias/.new handler = \option@set{#1/@code}{\option{#2/@code}},
+ /handlers/new cmd 0/.new code alias = /handlers/new cmd*,
+ /handlers/new cmd 1/.new code alias = /handlers/new cmd,
+}
+
+
+% --------------------------------------------------------
+% Provide unknown here so we get a proper error for unknown options
+% --------------------------------------------------------
+
+\options{
+ /@unknown/.new cmd 2 = {%
+ \ifopt@unknownwarnonly
+ \optionwarning{#1}{unknown option}%
+ \else
+ \optionerror{#1}{unknown option}%
+ \fi%
+ }%
+}
+
+
+% --------------------------------------------------------
+% Convenience handlers
+% --------------------------------------------------------
+
+% debugging
+\options{
+ /handlers/show/.new handler* = {\option@font@name{#1}=\optionshow{#1}},
+ /handlers/typeout/.new handler* = {\optiontypeout{#1}},
+}
+
+% family options
+\options{
+ /handlers/new style/.new handler/.defaults = \optionsalso{
+ #1/.new cmd = \optionsalso{#2},
+ #1/.type = style,
+ },
+ /handlers/new style*/.new handler = \optionsalso{
+ #1/.new cmd* = \optionsalso{#2},
+ #1/.type = style,
+ },
+ /handlers/new alias/.new handler ={%
+ \option@ensuredefined{#2}%
+ \optionsalso{%
+ #1/.new cmd = \optionsalso{#2={##1}},
+ #1/.type = alias,
+ }%
+ \option@xset{#1}{\option{#2}}%
+ %\option@xset{#1/@ini}{\option{#2/@ini}}%
+ },
+ /handlers/new alias*/.new handler = \optionsalso{
+ \option@ensuredefined{#2}%
+ \optionsalso{
+ #1/.new cmd = \optionsalso{#2},
+ #1/.type = alias,
+ }%
+ \option@xset{#1}{\option{#2}}%
+ %\option@xset{#1/@ini}{\option{#2/@ini}}%
+ },
+ /handlers/show/style/.new cmd = {\option@font@special{style}},
+ %
+ /handlers/search also/.new handler =\optionaddsearch{#1}{#2},
+ /handlers/cd/.new handler* =\edef\option@defaultpath{#1},
+ /handlers/new family/.new handler =[]{\optionsalso{
+ #1/.new cmd* = {\edef\option@defaultpath{#1}},
+ #1/.search also = {#2},
+ #1/.type = family,
+ #1/.cd
+ }},
+ /handlers/show/family/.new cmd = {\option@font@special{family}, searches: \option@showvalue{\optionlist{#1/@searchalso}}},
+}
+
+% compatibility
+\options{
+ /handlers/code/.new handler = \optionsalso{#1/.new cmd = {#2}},
+ /handlers/style/.new handler = \optionsalso{#1/.new style= {#2}},
+ /handlers/is family/.new handler* = \optionsalso{#1/.new family},
+}
+
+% defining new option types
+\options{%
+ /handlers/undef/.new handler = {%
+ \ifoptiondefined{#1}{%
+ \option@undef{#1}%
+ \option@undef{#1/@type}%
+ \option@undef{#1/@code}%
+ \option@undef{#1/@def}%
+ \option@undef{#1/@ini}%
+ % may leave other values but the above ones are surely required to be removed
+ }%
+ },
+ /handlers/type/.new handler = \option@ensuredefined{#1}\option@set{#1/@type}{#2},
+ /handlers/default/.new handler = \option@ensuredefined{#1}\option@set{#1/@def}{#2},
+ /handlers/defaulted/.new handler= {%
+ \option@ensuredefined{#1}%
+ \option@default@transform% changes optionvalue and sets /@def if provided
+ },
+ /handlers/initial/.new handler = {%
+ \option@ensuredefined{#1}%
+ \option@default@transform% changes optionvalue and sets /@def if provided
+ \option@eset{#1/@ini}{\optionvalue}%
+ \option@einvoke{#1}{\optionvalue}%
+ },
+ /handlers/reset/.new operation* = {%
+ \letoption@{#1/@ini}\opt@temp
+ \ifx\opt@temp\relax
+ \optionerror{#1}{cannot reset an option that has no initial value}%
+ \else
+ \option@einvoke{#1}{\opt@temp}%
+ \fi
+ },
+}
+
+% \optionaddsearch{<path>}{<search paths>}
+% add new search paths to a path.
+% for efficiency we expand out the search code inline instead of iterating
+\newrobustcmd*\optionaddsearch[2]{%
+ \ifoptiondefined{#1/@searchalso/@code}%
+ {\option@concat{#1/@searchalso}{#2}}%
+ {\option@setlist{#1/@searchalso}{#2}%
+ \option@set{#1/@searchalso/@type}{searchalso}%
+ \option@def{#1/@searchalso/@code}{}}%
+ \def\opt@do@addsearch##1{%
+ \edef\opt@temp{%
+ \noexpand\ifx\noexpand\opt@codepath\noexpand\relax
+ \noexpand\opt@searchforabspath{##1/\noexpand\option@searchpath}%
+ \noexpand\fi
+ }%
+ \expandnext{\option@appto{#1/@searchalso/@code}}{\opt@temp}%
+ }%
+ \forcsvlist\opt@do@addsearch{#2}%
+}
+
+
+% --------------------------------------------------------
+% Basic data type handlers
+% --------------------------------------------------------
+
+\options{
+ /handlers/new value/.new handler = []\optionsalso{%
+ #1/.new code = \option@let{#1}\optionvalue,
+ #1/.type = value,
+ #1/.initial = {#2},
+ },
+ %
+ /handlers/new num/.new handler = [0]\optionsalso{%
+ #1/.new code = \option@eset{#1}{\the\numexpr\optionvalue\relax},
+ #1/.type = num,
+ #1/.initial = {#2},
+ },
+ %
+ /handlers/new glue/.new handler = [0pt]\optionsalso{%
+ #1/.new code = \option@eset{#1}{\the\glueexpr\optionvalue\relax},
+ #1/.type = glue,
+ #1/.initial = {#2},
+ },
+ %
+ /handlers/new length/.new handler = [0pt]\optionsalso{%
+ #1/.new code = \cssetlength{#1}{\dimexpr\optionvalue\relax},
+ #1/.type = length,
+ /options/exec = \csnewlength{#1}\option@eset{#1}{\csname #1\endcsname},
+ #1/.initial = {#2},
+ },
+ /handlers/show/length/.new cmd = {\option@showvalue{\the\option{#1}}},
+ %
+ /handlers/new dim/.new handler = [0pt]\optionsalso{%
+ #1/.new code = {\option@eset{#1/@dimexpr}{\optionvalue}},
+ #1/.type = dim,
+ /options/exec = {\option@eset{#1}{\dimexpr\option{#1/@dimexpr}\relax}},% read as dimexpr
+ #1/.initial = {#2},
+ },
+ /handlers/show/dim/.new cmd ={\option@showvalue{\the\option{#1}}\ (=\optionshowliteral{#1/@dimexpr})},
+ %
+ /handlers/new toggle/.new handler = [false]\optionsalso{%
+ #1/.new code = {[true]{%
+ \ifcsdef{toggle\optionvalue}%
+ {\csuse{toggle\optionvalue}{#1}}%
+ {\eifstrequal\optionvalue{True}%
+ {\toggletrue{#1}}%
+ {\eifstrequal\optionvalue{False}%
+ {\togglefalse{#1}}%
+ {\optionerror@expect{#1}{true or false}}}}%
+ }},
+ #1/.type = toggle,
+ /options/exec = {\newtoggle{#1}\option@set{#1}{\iftoggle{#1}{true}{false}}},
+ #1/.initial = {#2},
+ },
+ /handlers/flip/.new operation = \iftoggle{#1}{\togglefalse{#1}}{\toggletrue{#1}},
+ %
+ /handlers/new list/.new handler = []\optionsalso{%
+ #1/.new code = \expandnext{\option@setlist{#1}}{\optionvalue},
+ #1/.type = list,
+ #1/.initial = {#2},
+ },
+ /handlers/show/list/.new cmd = \letoptionlist{#1}\opt@list\option@showvalue{\opt@list},
+ /handlers/push/.new operation = \option@push{#1}{#2},
+ /handlers/concat/.new operation = \option@concat{#1}{#2},
+ %
+ /handlers/new choice/.new handler={%
+ \opt@choices@parse{#1}{#2}% sets \opt@choices & opt@firstchoicename
+ \option@let{#1/@choices}{\opt@choices}%
+ \optionsalso{%
+ #1/.new cmd/.expands = {%
+ \opt@findchoice{##1}{#1}%
+ \ifnum\opt@choiceord<0%
+ \letoptionchoices{#1}\opt@choices
+ \optionerror@expect{#1}{one of "\opt@choices"}%
+ \else
+ \option@eset{#1}{\opt@choicevalue}%
+ \option@eset{#1/@ord}{\the\opt@choiceord}%
+ \option@eset{#1/@name}{\opt@choicename}%
+ \fi
+ },
+ #1/.type = choice,
+ #1/.expanded/.initial = \opt@firstchoicename,%
+ }%
+ },
+ /handlers/show/choice/.new cmd = {%
+ \option@showvalue{\option{#1}} %
+ (\option@font@name{@ord}=\option{#1/@ord}), %
+ \letoptionchoices{#1}\opt@choices
+ choices=\option@showvalue{\opt@choices}%
+ },
+ %
+ /handlers/new color/.new handler=[black]{%
+ %\opt@ensurexcolor{#1}%
+ \optionsalso{%
+ #1/.new cmd/.expands = {%
+ \option@set{#1}{##1}%
+ \eifblank{##1}{}{\opt@colorlet{#1}{##1}}%
+ },
+ #1/.type = color,
+ #1/.initial = {#2},
+ }%
+ },
+ /handlers/new color expr/.new handler=[black]{%
+ %\opt@ensurexcolor{#1}%
+ \optionsalso{%
+ #1/.new value,
+ #1/.type = color expr,
+ #1/.initial = {#2},
+ }%
+ },
+}
+
+\providecommand\colorlet[2]{\optionerror{#1}{color options require package "xcolor"; please include this package}}%
+\providecommand\definecolor[3]{\optionerror{#1}{color options require package "xcolor"; please include this package}}%
+
+% opt@colorlet{<color name>}{<color>} lets color be a color name or \#XXXXXX HTML color
+\def\opt@colorlet@html#1\opt@stop#2\opt@stop{\definecolor{#2}{HTML}{#1}}
+\def\opt@colorlet@name#1\opt@stop#2\opt@stop{\colorlet{#2}{#1}}
+\def\opt@colorlet@{\@ifnextchar \#{\@firstoftwo{\opt@colorlet@html}}{\opt@colorlet@name}}
+\newrobustcmd*\opt@colorlet[2]{%
+ \ifdef{\colorlet}%
+ {\opt@colorlet@#2\opt@stop#1\opt@stop}%
+ {\definecolor{#1}{named}{#2}}% if no xcolor, only support named colors
+}
+
+
+
+
+% --------------------------------------------------------
+% Hook into existing definitions
+% --------------------------------------------------------
+
+\options{
+ /handlers/is if/.new handler = \optionsalso{%
+ /options/exec = {\ifcsdef{if#2}{}{\optionerror{#1}{no `if` with name "#2" is defined}}},
+ #1/.new code = {[true]\ifcsdef{#2\optionvalue}{\csuse{#2\optionvalue}}{\optionerror@expect{#1}{true or false}}},
+ #1/.type = if,
+ /options/exec = {\option@set{#1}{#2}},% read name of the if
+ },
+ /handlers/show/if/.new cmd = {\option@showvalue{\option{#1}} (=\option@showvalue{\ifbool{\option{#1}}{true}{false})}},
+ %
+ /handlers/is def/.new handler = \optionsalso{%
+ /options/exec = \ifdef{#2}{}{\optionerror{#1}{no definition "\detokenize{#2}" is found}},
+ #1/.new code = \expandafter\def\expandafter#2\expandafter{\optionvalue},
+ #1/.type = def,
+ /options/exec =\option@set{#1}{#2},% read returns definition
+ %#1/.initial = {#2},
+ },
+ %
+ /handlers/is edef/.new handler = \optionsalso{%
+ /options/exec = \ifdef{#2}{}{\optionerror{#1}{no definition "\detokenize{#2}" is found}},
+ #1/.new code = \protected@edef#2{\optionvalue},
+ #1/.type = edef,
+ /options/exec =\option@set{#1}{#2},% read returns definition
+ %#1/.initial = {#2},
+ },
+ %
+ /handlers/is counter/.new handler = \optionsalso{%
+ /options/exec = \ifltxcounter{#2}{}{\optionerror{#1}{no definition "\detokenize{#2}" is found}},
+ #1/.new code = \setcounter{#2}{\optionvalue},
+ #1/.type = counter,
+ /options/exec =\option@set{#1}{#2},% read returns definition
+ %#1/.initial = {#2},
+ },
+ /handlers/show/counter/.new cmd = {\option@showvalue{\option{#1}} (=\option@font@value{\arabic{\option{#1}}})},
+ /handlers/inc/.new operation = [1]{%
+ \ifoptiontype{#1}{counter}% check if this is a counter or a number
+ {\addtocounter{\option{#1}}{#2}}%
+ {\option@xinvoke{#1}{\option{#1} + #2}}%
+ },
+ /handlers/step/.new operation* = {%
+ \ifoptiontype{#1}{counter}% check if this is a counter
+ {\stepcounter{\option{#1}}}%
+ {\optionerror{#1}{cannot "step" a non-counter}}%
+ },
+ /handlers/refstep/.new operation* = {%
+ \ifoptiontype{#1}{counter}% check if this is a counter
+ {\refstepcounter{\option{#1}}}%
+ {\optionerror{#1}{cannot "refstep" a non-counter}}%
+ },
+}
+
+
+% --------------------------------------------------------
+% Initial options for our library
+% --------------------------------------------------------
+\options{
+ /options/ignoreunknown/.is if = opt@ignoreunknown,
+ /options/unknownwarnonly/.is if = opt@unknownwarnonly,
+ /options/allowsearch/.is if = opt@allowsearch,
+ /options/collectunknown/.new style* = {/options/ignoreunknown,/options/remaining={}},
+ /options/showbuiltin/.new toggle,
+}
+
+\csundef{optk@/options/remaining}% tricksy, but we want to define it officially here :-)
+\optionsalso{
+ /options/remaining/.new list,
+}
+
+
+% --------------------------------------------------------
+% Show definitions
+% --------------------------------------------------------
+
+\newrobustcmd*\optionshowall[1][false]{\options{/options/showbuiltin=#1}\optionshowpath{}}
+
+\newcommand*\option@font@name[1]{\textsf{#1}}
+\newcommand*\option@font@path[1]{\textsf{#1}}
+\newcommand*\option@font@value[1]{\textsf{#1}}
+\newcommand*\option@font@special[1]{\textit{#1}}
+
+\newcommand*\option@showvalue[1]{$\langle$\option@font@value{#1}$\rangle$}
+
+\newrobustcmd*\optionshowpath[2][]{\option@showpath{#1#2}{#2}}
+\newrobustcmd*\option@showpath[2]{%
+ \noindent\option@font@path{#2/}%
+ \option@showitems{/@names}{\option@showname}{#1}%
+ \option@showitems{/@paths}{\option@showpath}{#1}%
+}
+
+\newrobustcmd*\option@showitems[3]{%
+ \ifoptionvoid{#3#1}{}{%
+ \begin{opt@marginlr}{1em}{0em}%
+ \optionlistdo{#3#1}{%
+ \iftoggle{/options/showbuiltin}{}%
+ {\option@ifanyof{#3/##1}{/handlers,/options}{\@gobble}%
+ {\if\option@firstletterof{##1}.\relax\expandafter\@gobble\fi}}%
+ {%
+ \noindent\hspace*{-0.5em}#2{#3/##1}{##1}%
+ \par%
+ }%
+ }%
+ \end{opt@marginlr}%
+ }%
+}
+
+\newrobustcmd*\option@showname[2]{%
+ \option@font@name{#2}%
+ =\optionshow{#1}%
+ \ifoptiondefined{#1/@ini}{%
+ , initial=\optionshowliteral{#1/@ini}%
+ }{}%
+ \ifoptiondefined{#1/@def}{%
+ , default=\optionshowliteral{#1/@def}%
+ }{}%
+}
+
+\newrobustcmd*\optionshowliteral[1]{\letoption{#1}\opt@temp\option@showvalue{\texttt{\expandnextsingle\detokenize{\opt@temp}}}}
+
+\newcommand*\optiontypeout[1]{%
+ \def\option@font@special##1{##1}%
+ \def\option@font@name##1{##1}%
+ \def\option@font@path##1{##1}%
+ \def\option@font@value##1{##1}%
+ \def\option@showvalue##1{##1}%
+ \typeout{#1=\optionshow{#1}}%
+}
+
+\newcommand*\optionshow[1]{%
+ \ifoptiondefined{#1/@type}%
+ {\ifoptiondefined{/handlers/show/\option{#1/@type}}{\@firstoftwo}{\@secondoftwo}}%
+ {\@secondoftwo}%
+ {\option@invoke{/handlers/show/\option{#1/@type}}{#1}}%
+ {\ifoptiondefined{#1}%
+ {\ifoptioniscode{#1}%
+ {\optionshowtype{#1}}%
+ {\option@showvalue{\option{#1}}}}%
+ {\option@font@special{undefined}}%
+ }%
+}
+
+\newenvironment{opt@marginlr}[2]{%
+ \topsep\z@
+ \partopsep\z@
+ \trivlist
+ \rightmargin=\dimexpr#2\relax%
+ \leftmargin=\dimexpr#1\relax%
+ \advance\linewidth -\rightmargin
+ \advance\linewidth -\leftmargin
+ \advance\@totalleftmargin \leftmargin
+ \parshape \@ne \@totalleftmargin \linewidth
+ \item[]%
+}%
+{\endtrivlist}
+
+
+% --------------------------------------------------------
+% Handling Package and Class options
+% --------------------------------------------------------
+
+% Process the 'global' class options in a package file
+% Just take care of known options and adjust the unusedoptionslist accordingly
+\newrobustcmd*\option@ProcessGlobalOptions[1]{%
+ \ifdefvoid{\@classoptionslist}{}{%
+ % process known options, and collect the rest in the remaining list
+ \options{/options/collectunknown,#1}\expandnext\optionsalso{\@classoptionslist}%
+ % remove processed options from the latex @unusedoptionlist
+ \ifx\@unusedoptionlist\@empty\relax\else
+ \def\opt@do##1{%
+ \ifoptioncontains{/options/remaining}{##1}{}{%
+ \expandnext{\@removeelement{##1}}{\@unusedoptionlist}{\@unusedoptionlist}%
+ }%
+ }%
+ \expandnext{\forcsvlist\opt@do}{\@classoptionslist}%
+ \fi
+ }%
+}
+
+% Process the local option in a .sty file, raise an error on unknown flags
+\newrobustcmd*\option@ProcessPackageOptions[1]{%
+ \letcs\opt@localoptions{opt@\@currname.\@currext}%
+ \ifdefvoid{\opt@localoptions}{}{%
+ \options{#1}\expandnext\optionsalso{\opt@localoptions}%
+ }%
+ \let\CurrentOption\@empty
+ \AtEndOfPackage{\let\@unprocessedoptions\relax}%
+}
+
+% Process the local options in a .cls file
+\newrobustcmd*\option@ProcessClassOptions[1]{%
+ \letcs\opt@localoptions{opt@\@currname.\@currext}%
+ \ifdefvoid{\opt@localoptions}{}{%
+ \options{/options/collectunknown,#1}\expandnext\optionsalso{\opt@localoptions}%
+ \letoptionlist{/options/remaining}\@unusedoptionlist% set the unused option list
+ }%
+ \let\CurrentOption\@empty
+ \AtEndOfPackage{\let\@unprocessedoptions\relax}%
+}
+
+% Process class- or package options.
+% A package will take global class options into account
+\newrobustcmd*\options@ProcessOptions[1]{%
+ \ifx\@currext\@clsextension
+ \option@ProcessClassOptions{#1}%
+ \else
+ \option@ProcessGlobalOptions{#1}%
+ \option@ProcessPackageOptions{#1}%
+ \fi
+}
+\@onlypreamble\options@ProcessOptions
+\@onlypreamble\options@ProcessPackageOptions
+\@onlypreamble\options@ProcessClassOptions
+\@onlypreamble\options@ProcessGlobalOptions
+
+
+% --------------------------------------------------------
+% Enable debugging
+% --------------------------------------------------------
+\ifopt@debug
+ \let\option@fast\option
+ \renewcommand*\option[1]{%
+ \ifcsdef{optk@#1}{}{\z@\optionerror{#1}{debug option: undefined option}}%
+ \option@fast{#1}%
+ }
+ \let\letoption@fast\letoption
+ \renewcommand*\letoption[2]{%
+ \ifcsdef{optk@#1}{}{\z@\optionerror{#1}{debug letoption: undefined option}}%
+ \letoption@fast{#1}#2%
+ }
+\fi
+
+% --------------------------------------------------------
+% restore cat codes
+% --------------------------------------------------------
+\opt@restore@catcodes