%---------------------------------------------------------------------------
% 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
