summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/kvsetkeys
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-12-18 03:01:19 +0000
committerNorbert Preining <norbert@preining.info>2019-12-18 03:01:19 +0000
commit6d3d0706f9b7085f8bc6efa81398dbfe551272db (patch)
tree46f604191e8b84eb8f939a14f2bfc5fd795dddd9 /macros/latex/contrib/kvsetkeys
parent6cbf49c3ed1a2881b7fccbc67eccb5532d196b74 (diff)
CTAN sync 201912180301
Diffstat (limited to 'macros/latex/contrib/kvsetkeys')
-rw-r--r--macros/latex/contrib/kvsetkeys/README.md11
-rw-r--r--macros/latex/contrib/kvsetkeys/kvsetkeys.dtx1676
-rw-r--r--macros/latex/contrib/kvsetkeys/kvsetkeys.pdfbin0 -> 374946 bytes
3 files changed, 1687 insertions, 0 deletions
diff --git a/macros/latex/contrib/kvsetkeys/README.md b/macros/latex/contrib/kvsetkeys/README.md
new file mode 100644
index 0000000000..a7e822fa0f
--- /dev/null
+++ b/macros/latex/contrib/kvsetkeys/README.md
@@ -0,0 +1,11 @@
+# kvsetkeys
+
+kvsetkeys package for LaTeX
+
+
+Package kvsetkeys provides kvsetkeys, a variant
+of package keyval's \cs{setkeys}. It allows to specify
+a handler that deals with unknown options. Active commas and equal
+signs may be used (e.g. see babel's shorthands) and
+only one level of curly braces is removed from the values.
+
diff --git a/macros/latex/contrib/kvsetkeys/kvsetkeys.dtx b/macros/latex/contrib/kvsetkeys/kvsetkeys.dtx
new file mode 100644
index 0000000000..c04507c297
--- /dev/null
+++ b/macros/latex/contrib/kvsetkeys/kvsetkeys.dtx
@@ -0,0 +1,1676 @@
+% \iffalse meta-comment
+%
+% File: kvsetkeys.dtx
+% Version: 2019/12/15 v1.18
+% Info: Key value parser
+%
+% Copyright (C)
+% 2006, 2007, 2009-2012 Heiko Oberdiek
+% 2016-2019 Oberdiek Package Support Group
+% https://github.com/ho-tex/kvsetkeys/issues
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either
+% version 1.3c of this license or (at your option) any later
+% version. This version of this license is in
+% https://www.latex-project.org/lppl/lppl-1-3c.txt
+% and the latest version of this license is in
+% https://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.
+%
+% This work has the LPPL maintenance status "maintained".
+%
+% The Current Maintainers of this work are
+% Heiko Oberdiek and the Oberdiek Package Support Group
+% https://github.com/ho-tex/kvsetkeys/issues
+%
+% The Base Interpreter refers to any `TeX-Format',
+% because some files are installed in TDS:tex/generic//.
+%
+% This work consists of the main source file kvsetkeys.dtx
+% and the derived files
+% kvsetkeys.sty, kvsetkeys.pdf, kvsetkeys.ins, kvsetkeys.drv,
+% kvsetkeys-example.tex, kvsetkeys-test1.tex,
+% kvsetkeys-test2.tex, kvsetkeys-test3.tex,
+% kvsetkeys-test4.tex.
+%
+% Distribution:
+% CTAN:macros/latex/contrib/kvsetkeys/kvsetkeys.dtx
+% CTAN:macros/latex/contrib/kvsetkeys/kvsetkeys.pdf
+%
+% Unpacking:
+% (a) If kvsetkeys.ins is present:
+% tex kvsetkeys.ins
+% (b) Without kvsetkeys.ins:
+% tex kvsetkeys.dtx
+% (c) If you insist on using LaTeX
+% latex \let\install=y\input{kvsetkeys.dtx}
+% (quote the arguments according to the demands of your shell)
+%
+% Documentation:
+% (a) If kvsetkeys.drv is present:
+% latex kvsetkeys.drv
+% (b) Without kvsetkeys.drv:
+% latex kvsetkeys.dtx; ...
+% The class ltxdoc loads the configuration file ltxdoc.cfg
+% if available. Here you can specify further options, e.g.
+% use A4 as paper format:
+% \PassOptionsToClass{a4paper}{article}
+%
+% Programm calls to get the documentation (example):
+% pdflatex kvsetkeys.dtx
+% makeindex -s gind.ist kvsetkeys.idx
+% pdflatex kvsetkeys.dtx
+% makeindex -s gind.ist kvsetkeys.idx
+% pdflatex kvsetkeys.dtx
+%
+% Installation:
+% TDS:tex/generic/kvsetkeys/kvsetkeys.sty
+% TDS:doc/latex/kvsetkeys/kvsetkeys.pdf
+% TDS:doc/latex/kvsetkeys/kvsetkeys-example.tex
+% TDS:source/latex/kvsetkeys/kvsetkeys.dtx
+%
+%<*ignore>
+\begingroup
+ \catcode123=1 %
+ \catcode125=2 %
+ \def\x{LaTeX2e}%
+\expandafter\endgroup
+\ifcase 0\ifx\install y1\fi\expandafter
+ \ifx\csname processbatchFile\endcsname\relax\else1\fi
+ \ifx\fmtname\x\else 1\fi\relax
+\else\csname fi\endcsname
+%</ignore>
+%<*install>
+\input docstrip.tex
+\Msg{************************************************************************}
+\Msg{* Installation}
+\Msg{* Package: kvsetkeys 2019/12/15 v1.18 Key value parser (HO)}
+\Msg{************************************************************************}
+
+\keepsilent
+\askforoverwritefalse
+
+\let\MetaPrefix\relax
+\preamble
+
+This is a generated file.
+
+Project: kvsetkeys
+Version: 2019/12/15 v1.18
+
+Copyright (C)
+ 2006, 2007, 2009-2012 Heiko Oberdiek
+ 2016-2019 Oberdiek Package Support Group
+
+This work may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either
+version 1.3c of this license or (at your option) any later
+version. This version of this license is in
+ https://www.latex-project.org/lppl/lppl-1-3c.txt
+and the latest version of this license is in
+ https://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.
+
+This work has the LPPL maintenance status "maintained".
+
+The Current Maintainers of this work are
+Heiko Oberdiek and the Oberdiek Package Support Group
+https://github.com/ho-tex/kvsetkeys/issues
+
+
+The Base Interpreter refers to any `TeX-Format',
+because some files are installed in TDS:tex/generic//.
+
+This work consists of the main source file kvsetkeys.dtx
+and the derived files
+ kvsetkeys.sty, kvsetkeys.pdf, kvsetkeys.ins, kvsetkeys.drv,
+ kvsetkeys-example.tex, kvsetkeys-test1.tex,
+ kvsetkeys-test2.tex, kvsetkeys-test3.tex,
+ kvsetkeys-test4.tex.
+
+\endpreamble
+\let\MetaPrefix\DoubleperCent
+
+\generate{%
+ \file{kvsetkeys.ins}{\from{kvsetkeys.dtx}{install}}%
+ \file{kvsetkeys.drv}{\from{kvsetkeys.dtx}{driver}}%
+ \usedir{tex/generic/kvsetkeys}%
+ \file{kvsetkeys.sty}{\from{kvsetkeys.dtx}{package}}%
+ \usedir{doc/latex/kvsetkeys}%
+ \file{kvsetkeys-example.tex}{\from{kvsetkeys.dtx}{example}}%
+}
+
+\catcode32=13\relax% active space
+\let =\space%
+\Msg{************************************************************************}
+\Msg{*}
+\Msg{* To finish the installation you have to move the following}
+\Msg{* file into a directory searched by TeX:}
+\Msg{*}
+\Msg{* kvsetkeys.sty}
+\Msg{*}
+\Msg{* To produce the documentation run the file `kvsetkeys.drv'}
+\Msg{* through LaTeX.}
+\Msg{*}
+\Msg{* Happy TeXing!}
+\Msg{*}
+\Msg{************************************************************************}
+
+\endbatchfile
+%</install>
+%<*ignore>
+\fi
+%</ignore>
+%<*driver>
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesFile{kvsetkeys.drv}%
+ [2019/12/15 v1.18 Key value parser (HO)]%
+\documentclass{ltxdoc}
+\usepackage{holtxdoc}[2011/11/22]
+\begin{document}
+ \DocInput{kvsetkeys.dtx}%
+\end{document}
+%</driver>
+% \fi
+%
+%
+%
+% \GetFileInfo{kvsetkeys.drv}
+%
+% \title{The \xpackage{kvsetkeys} package}
+% \date{2019/12/15 v1.18}
+% \author{Heiko Oberdiek\thanks
+% {Please report any issues at \url{https://github.com/ho-tex/kvsetkeys/issues}}}
+%
+% \maketitle
+%
+% \begin{abstract}
+% Package \xpackage{kvsetkeys} provides \cs{kvsetkeys}, a variant
+% of package \xpackage{keyval}'s \cs{setkeys}. It allows to specify
+% a handler that deals with unknown options. Active commas and equal
+% signs may be used (e.g. see \xpackage{babel}'s shorthands) and
+% only one level of curly braces is removed from the values.
+% \end{abstract}
+%
+% \tableofcontents
+%
+% \def\M#1{\texttt{\{}\meta{#1}\texttt{\}}}
+%
+% \section{Documentation}
+%
+% First I want to recommend the very good review article
+% ``A guide to key-value methods'' by Joseph Wright \cite{tb94wright}.
+% It introduces the different key-value packages and compares them.
+%
+% \subsection{Motivation}
+%
+% \cs{kvsetkeys} serves as replacement for \xpackage{keyval}'s
+% \cs{setkeys}. It basically uses the same syntax. But the
+% implementation is more robust and predictable:
+% \begin{description}
+% \item[Active syntax characters:]
+% Comma `|,|' and the equals sign `|=|' are used inside
+% key value lists as syntax characters. Package \xpackage{keyval}
+% uses the catcode of the characters that is active during
+% package loading, usually this is catcode 12 (other).
+% But it can happen that the catcode setting of the syntax characters
+% changes. Especially active characters are of interest, because
+% some language adaptations uses them. For example, option \xoption{turkish}
+% of package \xpackage{babel} uses the equals sign as active shorthand
+% character. Therefore package \xpackage{kvsetkeys} deals with
+% both catcode settings 12 (other) and 13 (active).
+% \item[Brace removal:]
+% Package \xpackage{keyval}'s \cs{setkeys} removes up to two
+% levels of curly braces around the value in some unpredictable way:
+%\begin{quote}
+%|\setkeys{fam}{key={{value}}} || || | $\rightarrow$ |value|\\
+%|\setkeys{fam}{key={{{value}}}} | $\rightarrow$ |{value}|\\
+%|\setkeys{fam}{key= {{{value}}}}| $\rightarrow$ |{{value}}|
+%\end{quote}
+% This package \xpackage{kvsetkeys} follows a much stronger rule:
+% Exactly one level of braces are removed from an item, if the
+% item is surrounded by curly braces. An item can be a
+% the key value pair, the key or the value.
+%\begin{quote}
+%|\kvsetkeys{fam}{key={value}} || | $\rightarrow$ |value|\\
+%|\kvsetkeys{fam}{key={{value}} | $\rightarrow$ |{value}|\\
+%|\kvsetkeys{fam}{key= {{value}}| $\rightarrow$ |{value}|
+%\end{quote}
+% \item[Arbitrary values:] Unmatched conditionals are supported.
+% \end{description}
+%
+% Before I describe
+% \cs{kvsetkeys} in more detail, first I want to explain, how
+% this package deals with key value lists. For the package also
+% provides low level interfaces that can be used by package authors.
+%
+% \subsection{Normalizing key value lists}
+%
+% \begin{declcs}{kv@normalize}\,\M{key value list}
+% \end{declcs}
+% If the user specifies key value lists, he usually prefers
+% nice formatted source code, e.g.:
+% \begin{quote}
+%\begin{verbatim}
+%\hypersetup{
+% pdftitle = {...},
+% pdfsubject = {...},
+% pdfauthor = {...},
+% pdfkeywords = {...},
+% ...
+%}
+%\end{verbatim}
+% \end{quote}
+% Thus there can be spaces around keys, around |=| or around the value.
+% Also empty entries are possible by too many commas. Therefore these
+% spaces and empty entries are silently removed by package \xpackage{keyval}
+% and this package. Whereas the contents of the value can be protected
+% by curly braces, especially if spaces or commas are used inside,
+% a key name must not use spaces or other syntax characters.
+%
+% \cs{kv@normalize} takes a key value list and performs the cleanup:
+% \begin{itemize}
+% \item Spaces are removed.
+% \item Syntax characters (comma and equal sign) that are active
+% are replaced by the same characters with standard catcode.
+% (Example: \xpackage{babel}'s language option \xoption{turkish}
+% uses the equal sign as active shorthand character.)
+% \end{itemize}
+% The result is stored in \cs{kv@list}, e.g.:
+% \begin{quote}
+% |\kv@list| $\rightarrow$ |,pdftitle={...},pdfsubject={...},...,|
+% \end{quote}
+% Curly braces around values (or keys) remain untouched.
+% \begin{description}
+% \item[v1.3+:]
+% One comma is added in front of the list and each pair ends with
+% a comma. Thus an empty list consists of one comma, otherwise
+% two commas encloses the list. Empty entries other than the first
+% are removed.
+% \item[v1.0 -- v1.2:]
+% Empty entries are removed later. In fact it adds a comma at the begin
+% and end to protect the last value and an easier implementation.
+% \end{description}
+%
+% \subsection{Parsing key value lists}
+%
+% \begin{declcs}{kv@parse}\,\M{key value list}\,\M{processor}
+% \end{declcs}
+% It is easier to parse a normalized list, thus \cs{kv@parse}
+% normalizes the list and calls \cs{kv@parse@normalized}.
+%
+% \begin{declcs}{kv@parse@normalized}\,\M{key value list}%
+% \,\M{processor}
+% \end{declcs}
+% Now the key value list is split into single key value pairs.
+% For further processing the key and value are given as arguments
+% for the \meta{processor}:
+% \begin{quote}
+% \meta{processor}\,\M{key}\,\M{value}
+% \end{quote}
+% Also key and value are stored in macro names:
+% \begin{itemize}
+% \item \cs{kv@key} stores the key.
+% \item \cs{kv@value} stores the value or if the value was not
+% specified it has the meaning \cs{relax}.
+% \end{itemize}
+% The behaviour in pseudo code:
+% \begin{quote}
+% foreach (\meta{key}, \meta{value}) in (\meta{key value list})\\
+% \hspace*{1.5em}\cs{kv@key} := \meta{key}\\
+% \hspace*{1.5em}\cs{kv@value} := \meta{value}\\
+% \hspace*{1.5em}\meta{processor}\,\M{key}\,\M{value}
+% \end{quote}
+%
+% \begin{declcs}{kv@break}
+% \end{declcs}
+% Since version 2011/03/03 v1.11 \cs{kv@break} can be
+% called inside the \meta{processor}
+% of \cs{kv@parse} or \cs{kv@parse@normalized}, then
+% the processing is stopped and the following entries discarded.
+%
+% \subsection{Processing key value pairs}
+%
+% Key value pairs can be processed in many different ways.
+% For example, the processor for \cs{kvsetkeys} works similar
+% to \cs{setkeys} of package \xpackage{keyval}. There unknown
+% keys raise an error.
+%
+% Package \xpackage{xkeyval} also knows a star form of \cs{setkeys}
+% that stores unknown keys in an internal macro for further processing
+% with \cs{setrmkeys} and similar macros. This feature is covered
+% by processor \cs{kv@processor@known}.
+%
+% \subsubsection{Processing similar to \xpackage{keyval}}
+%
+% \begin{declcs}{kv@processor@default}\,\M{family}\,\M{key}\,\M{value}
+% \end{declcs}
+% There are many possiblities to process key value pairs.
+% \cs{kv@processor@default} is the processor used in \cs{kvsetkeys}.
+% It reimplements and extends the behaviour of
+% \xpackage{keyval}'s \cs{setkeys}.
+% In case of unknown keys \cs{setkeys} raise an error.
+% This processer, however, calls a handler instead, if it
+% is provided by the family. Both \meta{family} and \meta{key}
+% may contain package \xpackage{babel}'s shorthands
+% (since 2011/04/07 v1.13).
+%
+% Since 2011/10/18 v1.15 the family handler can reject the
+% successful handling of a key by calling \cs{kv@handled@false}.
+%
+% Since 2019/12/15 v1.18 \cs{kv@processor@default} also defines
+% macro \cs{kv@fam} with meaning \meta{family} for convenience.
+%
+% \subsubsection{Processing similar to \cs{setkeys*} of package \xpackage{xkeyval}}
+%
+% \begin{declcs}{kv@processor@known}\,\M{family}\,^^A
+% \M{cmd}\,\M{key}\,\M{value}
+% \end{declcs}
+% The key value processor \cs{kv@processor@known} behaves similar
+% to \cs{kv@processor@default}. If the \meta{key} exists in the
+% \meta{family} its code is called, otherwise the family handler
+% is tried. If the family handler is not set or cannot handle the
+% key, the unknown key value pair is added to the macro \meta{cmd}.
+% Since 2011/10/18 v1.15.
+%
+% The behaviour in pseudo code:
+% \begin{quote}
+% if \meta{key} exists\\
+% \hspace*{1.5em}call the keyval code of \meta{key}\\
+% else\\
+% \hspace*{1.5em}if \meta{handler} for \meta{family} exists\\
+% \hspace*{3em}handled = true\\
+% \hspace*{3em}\meta{handler}\,\M{key}\,\M{value}\\
+% \hspace*{3em}if handled\\
+% \hspace*{3em}else\\
+% \hspace*{4.5em}add \texttt{"}\M{key}\texttt{=}\M{value}\texttt{"}
+% to \M{cmd}\\
+% \hspace*{3em}fi\\
+% \hspace*{1.5em}else\\
+% \hspace*{3em}add \texttt{"}\M{key}\texttt{=}\M{value}\texttt{"}
+% to \M{cmd}\\
+% \hspace*{3em}raise unknown key error\\
+% \hspace*{1.5em}fi\\
+% fi
+% \end{quote}
+%
+% Since 2019/12/15 v1.18 \cs{kv@processor@known} also defines
+% macro \cs{kv@fam} with meaning \meta{family} for convenience.
+%
+% \subsection{Default family handler}
+%
+% \cs{kv@processor@default} calls \meta{handler}, the default
+% handler for the family, if the key does not exist in the family.
+% The handler is called with two arguments, the key and the value.
+% It can be defined with \cs{kv@set@family@hander}:
+%
+% \begin{declcs}{kv@set@family@handler}\,\M{family}\,\M{handler definition}
+% \end{declcs}
+% This sets the default family handler for the keyval family
+% \meta{family}. Inside \meta{handler definition} |#1| stands for
+% the key and |#2| is the value. Also \cs{kv@key} and \cs{kv@value}
+% can be used for the key and the value. If the value is not given,
+% \cs{kv@value} has the meaning \cs{relax}.
+%
+% \begin{declcs}{kv@unset@family@handler}\,\M{family}
+% \end{declcs}
+% It removes the family handler for \meta{family}.
+% Since 2011/10/18 v1.15.
+%
+% \subsection{Put it all together}
+%
+% \begin{declcs}{kvsetkeys}\,\M{family}\,\M{key value list}
+% \end{declcs}
+% Macro \cs{kvsetkeys} processes the \meta{key value list} with
+% the standard processor \cs{kv@processor@default}:
+% \begin{quote}
+% \cs{kv@parse}\,\M{key value list}^^A
+% \texttt{\{}\cs{kv@processor@default}\,\M{family}\texttt{\}}
+% \end{quote}
+%
+% \begin{declcs}{kvsetknownkeys}\,\M{family}\,\M{cmd}\,\M{key value list}
+% \end{declcs}
+% Macro \cs{kvsetknownkeys} processes the \meta{key value list}
+% with processor \cs{kv@processor@known}. All key value pairs
+% with keys that are not known in \meta{family} are stored
+% in macro \meta{cmd}. A previous contents of macro \meta{cmd}
+% will be overwritten. If all keys can be handled, \meta{cmd}
+% will be empty, otherwise it contains a key value list of
+% unhandled key value pairs.
+% Since 2011/10/18 v1.15.
+%
+% Pseudo code:
+% \begin{quote}
+% create macro \meta{cmdaux} with unique name (inside the current group)\\
+% \cs{def}\meta{cmdaux}\{\}\\
+% \cs{kv@parse}\,\M{key value list}^^A
+% \texttt{\{}\cs{kv@processor@known}\,\M{family}\,\M{cmdaux}\texttt{\}}\\
+% \cs{let}\meta{cmd}=\meta{cmdaux}
+% \end{quote}
+%
+% \begin{declcs}{kvsetkeys@expandafter}\,\M{family}\,\M{list cmd}\\
+% \cs{kvsetknownkeys@expandafter}\,\M{family}\,%
+% \M{cmd}\,\M{list cmd}
+% \end{declcs}
+% Both macros behave like the counterparts without suffix
+% |@expandafter|. The difference is that the key value list is
+% given as macro that is expanded once.
+% Since 2011/10/18 v1.15.
+%
+% Thus you can replace \cs{setkeys} of package \xpackage{keyval}
+% by the key value parser of this package:
+% \begin{quote}
+% |\renewcommand*{\setkeys}{\kvsetkeys}|\\
+% or\\
+% |\let\setkeys\kvsetkeys|
+% \end{quote}
+%
+% \subsection{Comma separated lists}
+%
+% Since version 2007/09/29 v1.3 this package also supports the normalizing
+% and parsing of general comma separated lists.
+%
+% \begin{declcs}{comma@normalize}\,\M{comma list}
+% \end{declcs}
+% Macro \cs{comma@normalize} normalizes the comma separated list,
+% removes spaces around commas. The result is put in macro \cs{comma@list}.
+%
+% \begin{declcs}{comma@parse}\,\M{comma list}\,\M{processor}
+% \end{declcs}
+% Macro \cs{comma@parse} first normalizes the comma separated list
+% and then parses the list by calling \cs{comma@parse@normalized}.
+%
+% \begin{declcs}{comma@parse@normalized}\,\M{normalized comma list}^^A
+% \,\M{processor}
+% \end{declcs}
+% The list is parsed. Empty entries are ignored. \meta{processor}
+% is called for each non-empty entry with the entry as argument:
+% \begin{quote}
+% \meta{processor}|{|\meta{entry}|}|
+% \end{quote}
+% Also the entry is stored in the macro \cs{comma@entry}.
+%
+% \begin{declcs}{comma@break}
+% \end{declcs}
+% Since version 2011/03/03 v1.11 \cs{comma@break} can be
+% called inside the \meta{processor}
+% of \cs{comma@parse} or \cs{comma@parse@normalized}, then
+% the processing is stopped and the following entries discarded.
+%
+% \section{Example}
+%
+% The following example prints a short piece of HTML code using
+% the tabbing environment for indenting purpose and a key value
+% syntax for specifying the attributes of an HTML tag.
+% The example illustrates the use of a default family handler.
+% \begin{macrocode}
+%<*example>
+\documentclass{article}
+\usepackage[T1]{fontenc}
+\usepackage{kvsetkeys}
+\usepackage{keyval}
+
+\makeatletter
+\newcommand*{\tag}[2][]{%
+ % #1: attributes
+ % #2: tag name
+ \begingroup
+ \toks@={}%
+ \let\@endslash\@empty
+ \kvsetkeys{tag}{#1}%
+ \texttt{%
+ \textless #2\the\toks@\@endslash\textgreater
+ }%
+ \endgroup
+}
+\kv@set@family@handler{tag}{%
+ % #1: key
+ % #2: value
+ \toks@\expandafter{%
+ \the\toks@
+ \space
+ #1=\string"#2\string"%
+ }%
+}
+\define@key{tag}{/}[]{%
+ \def\@endslash{/}%
+}
+\makeatother
+
+\begin{document}
+\begin{tabbing}
+ \mbox{}\qquad\=\qquad\=\kill
+ \tag{html}\\
+ \>\dots\\
+ \>\tag[border=1]{table}\\
+ \>\>\tag[width=200, span=3, /]{colgroup}\\
+ \>\>\dots\\
+ \>\tag{/table}\\
+ \>\dots\\
+ \tag{/html}\\
+\end{tabbing}
+\end{document}
+%</example>
+% \end{macrocode}
+%
+% \StopEventually{
+% }
+%
+% \section{Implementation}
+%
+% \subsection{Identification}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+% Reload check, especially if the package is not used with \LaTeX.
+% \begin{macrocode}
+\begingroup\catcode61\catcode48\catcode32=10\relax%
+ \catcode13=5 % ^^M
+ \endlinechar=13 %
+ \catcode35=6 % #
+ \catcode39=12 % '
+ \catcode44=12 % ,
+ \catcode45=12 % -
+ \catcode46=12 % .
+ \catcode58=12 % :
+ \catcode64=11 % @
+ \catcode123=1 % {
+ \catcode125=2 % }
+ \expandafter\let\expandafter\x\csname ver@kvsetkeys.sty\endcsname
+ \ifx\x\relax % plain-TeX, first loading
+ \else
+ \def\empty{}%
+ \ifx\x\empty % LaTeX, first loading,
+ % variable is initialized, but \ProvidesPackage not yet seen
+ \else
+ \expandafter\ifx\csname PackageInfo\endcsname\relax
+ \def\x#1#2{%
+ \immediate\write-1{Package #1 Info: #2.}%
+ }%
+ \else
+ \def\x#1#2{\PackageInfo{#1}{#2, stopped}}%
+ \fi
+ \x{kvsetkeys}{The package is already loaded}%
+ \aftergroup\endinput
+ \fi
+ \fi
+\endgroup%
+% \end{macrocode}
+% Package identification:
+% \begin{macrocode}
+\begingroup\catcode61\catcode48\catcode32=10\relax%
+ \catcode13=5 % ^^M
+ \endlinechar=13 %
+ \catcode35=6 % #
+ \catcode39=12 % '
+ \catcode40=12 % (
+ \catcode41=12 % )
+ \catcode44=12 % ,
+ \catcode45=12 % -
+ \catcode46=12 % .
+ \catcode47=12 % /
+ \catcode58=12 % :
+ \catcode64=11 % @
+ \catcode91=12 % [
+ \catcode93=12 % ]
+ \catcode123=1 % {
+ \catcode125=2 % }
+ \expandafter\ifx\csname ProvidesPackage\endcsname\relax
+ \def\x#1#2#3[#4]{\endgroup
+ \immediate\write-1{Package: #3 #4}%
+ \xdef#1{#4}%
+ }%
+ \else
+ \def\x#1#2[#3]{\endgroup
+ #2[{#3}]%
+ \ifx#1\@undefined
+ \xdef#1{#3}%
+ \fi
+ \ifx#1\relax
+ \xdef#1{#3}%
+ \fi
+ }%
+ \fi
+\expandafter\x\csname ver@kvsetkeys.sty\endcsname
+\ProvidesPackage{kvsetkeys}%
+ [2019/12/15 v1.18 Key value parser (HO)]%
+% \end{macrocode}
+%
+% \begin{macrocode}
+\begingroup\catcode61\catcode48\catcode32=10\relax%
+ \catcode13=5 % ^^M
+ \endlinechar=13 %
+ \catcode123=1 % {
+ \catcode125=2 % }
+ \catcode64=11 % @
+ \def\x{\endgroup
+ \expandafter\edef\csname KVS@AtEnd\endcsname{%
+ \endlinechar=\the\endlinechar\relax
+ \catcode13=\the\catcode13\relax
+ \catcode32=\the\catcode32\relax
+ \catcode35=\the\catcode35\relax
+ \catcode61=\the\catcode61\relax
+ \catcode64=\the\catcode64\relax
+ \catcode123=\the\catcode123\relax
+ \catcode125=\the\catcode125\relax
+ }%
+ }%
+\x\catcode61\catcode48\catcode32=10\relax%
+\catcode13=5 % ^^M
+\endlinechar=13 %
+\catcode35=6 % #
+\catcode64=11 % @
+\catcode123=1 % {
+\catcode125=2 % }
+\def\TMP@EnsureCode#1#2{%
+ \edef\KVS@AtEnd{%
+ \KVS@AtEnd
+ \catcode#1=\the\catcode#1\relax
+ }%
+ \catcode#1=#2\relax
+}
+\TMP@EnsureCode{36}{3}% $
+\TMP@EnsureCode{38}{4}% &
+\TMP@EnsureCode{39}{12}% '
+\TMP@EnsureCode{43}{12}% +
+\TMP@EnsureCode{44}{12}% ,
+\TMP@EnsureCode{45}{12}% -
+\TMP@EnsureCode{46}{12}% .
+\TMP@EnsureCode{47}{12}% /
+\TMP@EnsureCode{91}{12}% [
+\TMP@EnsureCode{93}{12}% ]
+\TMP@EnsureCode{94}{7}% ^ (superscript)
+\TMP@EnsureCode{96}{12}% `
+\TMP@EnsureCode{126}{13}% ~ (active)
+\edef\KVS@AtEnd{\KVS@AtEnd\noexpand\endinput}
+% \end{macrocode}
+%
+% \subsection{Package loading}
+%
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname RequirePackage\endcsname\relax
+ \def\TMP@RequirePackage#1[#2]{%
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname ver@#1.sty\endcsname\relax
+ \input #1.sty\relax
+ \fi
+ }%
+ \TMP@RequirePackage{infwarerr}[2007/09/09]%
+ \let\PackageError\@PackageError
+\else
+\fi
+% \end{macrocode}
+%
+% \begin{macrocode}
+\expandafter\ifx\csname toks@\endcsname\relax
+ \toksdef\toks@=0 %
+\fi
+% \end{macrocode}
+%
+% \subsection{Check for \eTeX}
+%
+% \cs{unexpanded}, \cs{ifcsname}, and \cs{unless} are used if found.
+% \begin{macrocode}
+\ifx\numexpr\@undefined
+ \catcode`\$=9 % ignore
+ \catcode`\&=14 % comment
+\else % e-TeX
+ \catcode`\$=14 % comment
+ \catcode`\&=9 % ignore
+\fi
+% \end{macrocode}
+%
+% \subsection{Generic help macros}
+%
+% \begin{macro}{\KVS@Empty}
+% \begin{macrocode}
+\def\KVS@Empty{}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@FirstOfTwo}
+% \begin{macrocode}
+\long\def\KVS@FirstOfTwo#1#2{#1}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@SecondOfTwo}
+% \begin{macrocode}
+\long\def\KVS@SecondOfTwo#1#2{#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\KVS@IfEmpty}
+% \begin{macrocode}
+\long\def\KVS@IfEmpty#1{%
+& \edef\KVS@Temp{\unexpanded{#1}}%
+$ \begingroup
+$ \toks@{#1}%
+$ \edef\KVS@Temp{\the\toks@}%
+$ \expandafter\endgroup
+ \ifx\KVS@Temp\KVS@Empty
+ \expandafter\KVS@FirstOfTwo
+ \else
+ \expandafter\KVS@SecondOfTwo
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Normalizing}
+%
+% \begin{macro}{\kv@normalize}
+% \begin{macrocode}
+\long\def\kv@normalize#1{%
+ \begingroup
+ \toks@{,#1,}%
+ \KVS@Comma
+ \KVS@SpaceComma
+ \KVS@CommaSpace
+ \KVS@CommaComma
+ \KVS@Equals
+ \KVS@SpaceEquals
+ \KVS@EqualsSpace
+ \xdef\KVS@Global{\the\toks@}%
+ \endgroup
+ \let\kv@list\KVS@Global
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\comma@normalize}
+% \begin{macrocode}
+\def\comma@normalize#1{%
+ \begingroup
+ \toks@{,#1,}%
+ \KVS@Comma
+ \KVS@SpaceComma
+ \KVS@CommaSpace
+ \KVS@CommaComma
+ \xdef\KVS@Global{\the\toks@}%
+ \endgroup
+ \let\comma@list\KVS@Global
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@Comma}
+% Converts active commas into comma with catcode other.
+% Also adds a comma at the end to protect the last value
+% for next cleanup steps.
+% \begin{macrocode}
+\begingroup
+ \lccode`\,=`\,%
+ \lccode`\~=`\,%
+\lowercase{\endgroup
+ \def\KVS@Comma{%
+ \toks@\expandafter{\expandafter}\expandafter
+ \KVS@@Comma\the\toks@~\KVS@Nil
+ }%
+ \long\def\KVS@@Comma#1~#2\KVS@Nil{%
+ \toks@\expandafter{\the\toks@#1}%
+ \KVS@IfEmpty{#2}{%
+ }{%
+ \KVS@@Comma,#2\KVS@Nil
+ }%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@SpaceComma}
+% Removes spaces before the comma, may add commas at the end.
+% \begin{macrocode}
+\def\KVS@SpaceComma#1{%
+ \def\KVS@SpaceComma{%
+ \expandafter\KVS@@SpaceComma\the\toks@#1,\KVS@Nil
+ }%
+}
+\KVS@SpaceComma{ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@SpaceComma}
+% \begin{macrocode}
+\long\def\KVS@@SpaceComma#1 ,#2\KVS@Nil{%
+ \KVS@IfEmpty{#2}{%
+ \toks@{#1}%
+ }{%
+ \KVS@@SpaceComma#1,#2\KVS@Nil
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@CommaSpace}
+% Removes spaces after the comma, may add commas at the end.
+% \begin{macrocode}
+\def\KVS@CommaSpace{%
+ \expandafter\KVS@@CommaSpace\the\toks@, \KVS@Nil
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@CommaSpace}
+% \begin{macrocode}
+\long\def\KVS@@CommaSpace#1, #2\KVS@Nil{%
+ \KVS@IfEmpty{#2}{%
+ \toks@{#1}%
+ }{%
+ \KVS@@CommaSpace#1,#2\KVS@Nil
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@CommaComma}
+% Replaces multiple commas by one comma.
+% \begin{macrocode}
+\def\KVS@CommaComma{%
+ \expandafter\KVS@@CommaComma\the\toks@,\KVS@Nil
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@CommaComma}
+% \begin{macrocode}
+\long\def\KVS@@CommaComma#1,,#2\KVS@Nil{%
+ \KVS@IfEmpty{#2}{%
+ \toks@{#1,}% (!)
+ }{%
+ \KVS@@CommaComma#1,#2\KVS@Nil
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\KVS@Equals}
+% Converts active equals signs into catcode other characters.
+% \begin{macrocode}
+\begingroup
+ \lccode`\==`\=%
+ \lccode`\~=`\=%
+\lowercase{\endgroup
+ \def\KVS@Equals{%
+ \toks@\expandafter{\expandafter}\expandafter
+ \KVS@@Equals\the\toks@~\KVS@Nil
+ }%
+ \long\def\KVS@@Equals#1~#2\KVS@Nil{%
+ \edef\KVS@Temp{\the\toks@}%
+ \ifx\KVS@Temp\KVS@Empty
+ \expandafter\KVS@FirstOfTwo
+ \else
+ \expandafter\KVS@SecondOfTwo
+ \fi
+ {%
+ \toks@{#1}%
+ }{%
+ \toks@\expandafter{\the\toks@=#1}%
+ }%
+ \KVS@IfEmpty{#2}{%
+ }{%
+ \KVS@@Equals#2\KVS@Nil
+ }%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@SpaceEquals}
+% Removes spaces before the equals sign.
+% \begin{macrocode}
+\def\KVS@SpaceEquals#1{%
+ \def\KVS@SpaceEquals{%
+ \expandafter\KVS@@SpaceEquals\the\toks@#1=\KVS@Nil
+ }%
+}
+\KVS@SpaceEquals{ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@SpaceEquals}
+% \begin{macrocode}
+\long\def\KVS@@SpaceEquals#1 =#2\KVS@Nil{%
+ \KVS@IfEmpty{#2}{%
+ \toks@{#1}%
+ }{%
+ \KVS@@SpaceEquals#1=#2\KVS@Nil
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@EqualsSpace}
+% Removes spaces after the equals sign.
+% \begin{macrocode}
+\def\KVS@EqualsSpace{%
+ \expandafter\KVS@@EqualsSpace\the\toks@= \KVS@Nil
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@EqualsSpace}
+% \begin{macrocode}
+\long\def\KVS@@EqualsSpace#1= #2\KVS@Nil{%
+ \KVS@IfEmpty{#2}{%
+ \toks@{#1}%
+ }{%
+ \KVS@@EqualsSpace#1=#2\KVS@Nil
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Parsing key value lists}
+%
+% \begin{macro}{\kv@parse}
+% Normalizes and parses the key value list. Also sets \cs{kv@list}.
+% \begin{macrocode}
+\long\def\kv@parse#1{%
+ \kv@normalize{#1}%
+ \expandafter\kv@parse@normalized\expandafter{\kv@list}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\kv@parse@normalized}
+% |#1|: key value list\\
+% |#2|: processor
+% \begin{macrocode}
+\long\def\kv@parse@normalized#1#2{%
+ \KVS@Parse#1,\KVS@Nil{#2}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@Parse}
+% |#1,#2|: key value list\\
+% |#3|: processor
+% \begin{macrocode}
+\long\def\KVS@Parse#1,#2\KVS@Nil#3{%
+ \KVS@IfEmpty{#1}{%
+ }{%
+ \KVS@Process#1=\KVS@Nil{#3}%
+ }%
+ \KVS@MaybeBreak
+ \KVS@IfEmpty{#2}{%
+ }{%
+ \KVS@Parse#2\KVS@Nil{#3}%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@Process}
+% |#1|: key\\
+% |#2|: value, |=|\\
+% |#3|: processor
+% \begin{macrocode}
+\long\def\KVS@Process#1=#2\KVS@Nil#3{%
+ \let\KVS@MaybeBreak\relax
+ \def\kv@key{#1}%
+ \KVS@IfEmpty{#2}{%
+ \let\kv@value\relax
+ #3{#1}{}%
+ }{%
+ \KVS@@Process{#1}#2\KVS@Nil{#3}%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@@Process}
+% |#1|: key\\
+% |#2|: value\\
+% |#3|: processor
+% \begin{macrocode}
+\long\def\KVS@@Process#1#2=\KVS@Nil#3{%
+& \edef\kv@value{\unexpanded{#2}}%
+$ \begingroup
+$ \toks@{#2}%
+$ \xdef\KVS@Global{\the\toks@}%
+$ \endgroup
+$ \let\kv@value\KVS@Global
+ #3{#1}{#2}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@MaybeBreak}
+% \begin{macrocode}
+\let\KVS@MaybeBreak\relax
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@break}
+% \begin{macrocode}
+\def\KVS@break#1#2#3#4{%
+ \let\KVS@MaybeBreak\relax
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\kv@break}
+% \begin{macrocode}
+\def\kv@break{%
+ \let\KVS@MaybeBreak\KVS@break
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Parsing comma lists}
+%
+% \begin{macro}{\comma@parse}
+% Normalizes and parses the key value list. Also sets \cs{comma@list}.
+% \begin{macrocode}
+\def\comma@parse#1{%
+ \comma@normalize{#1}%
+ \expandafter\comma@parse@normalized\expandafter{\comma@list}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\comma@parse@normalized}
+% |#1|: comma list\\
+% |#2|: processor
+% \begin{macrocode}
+\def\comma@parse@normalized#1#2{%
+ \KVS@CommaParse#1,\KVS@Nil{#2}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@CommaParse}
+% |#1,#2|: comma list\\
+% |#3|: processor
+% \begin{macrocode}
+\def\KVS@CommaParse#1,#2\KVS@Nil#3{%
+ \KVS@IfEmpty{#1}{%
+ }{%
+ \def\comma@entry{#1}%
+ #3{#1}%
+ }%
+ \KVS@MaybeBreak
+ \KVS@IfEmpty{#2}{%
+ }{%
+ \KVS@CommaParse#2\KVS@Nil{#3}%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\comma@break}
+% \begin{macrocode}
+\def\comma@break{%
+ \let\KVS@MaybeBreak\KVS@break
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Processing key value pairs}
+%
+% \begin{macro}{\kv@handled@false}
+% The handler can call \cs{kv@handled@false} or
+% \cs{kv@handled@true} so report failure or success.
+% The default is success (compatibility for versions
+% before 2011/10/18 v1.15).
+% \begin{macrocode}
+\def\kv@handled@false{%
+ \let\ifkv@handled@\iffalse
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\kv@handled@true}
+% \begin{macrocode}
+\def\kv@handled@true{%
+ \let\ifkv@handled@\iftrue
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ifkv@handled@}
+% \begin{macrocode}
+\kv@handled@true
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kv@processor@default}
+% \begin{macrocode}
+\def\kv@processor@default#1#2{%
+ \begingroup
+ \csname @safe@activestrue\endcsname
+ \let\ifincsname\iftrue
+ \edef\KVS@temp{\endgroup
+ \noexpand\KVS@ProcessorDefault{#1}{#2}%
+ }%
+ \KVS@temp
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@ProcessorDefault}
+% \begin{macrocode}
+\long\def\KVS@ProcessorDefault#1#2#3{%
+ \def\kv@fam{#1}%
+& \unless\ifcsname KV@#1@#2\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KV@#1@#2\endcsname\relax
+& \unless\ifcsname KVS@#1@handler\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KVS@#1@handler\endcsname\relax
+ \kv@error@unknownkey{#1}{#2}%
+ \else
+ \kv@handled@true
+ \csname KVS@#1@handler\endcsname{#2}{#3}%
+ \relax
+ \ifkv@handled@
+ \else
+ \kv@error@unknownkey{#1}{#2}%
+ \fi
+ \fi
+ \else
+ \ifx\kv@value\relax
+& \unless\ifcsname KV@#1@#2@default\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KV@#1@#2@default\endcsname\relax
+ \kv@error@novalue{#1}{#2}%
+ \else
+ \csname KV@#1@#2@default\endcsname
+ \relax
+ \fi
+ \else
+ \csname KV@#1@#2\endcsname{#3}%
+ \fi
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kv@processor@known}
+% \begin{macrocode}
+\def\kv@processor@known#1#2#3{%
+ \begingroup
+ \csname @safe@activestrue\endcsname
+ \let\ifincsname\iftrue
+ \edef\KVS@temp{\endgroup
+ \noexpand\KVS@ProcessorKnown{#1}\noexpand#2{#3}%
+ }%
+ \KVS@temp
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@ProcessorKnown}
+% \begin{macrocode}
+\long\def\KVS@ProcessorKnown#1#2#3#4{%
+ \def\kv@fam{#1}%
+& \unless\ifcsname KV@#1@#3\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KV@#1@#3\endcsname\relax
+& \unless\ifcsname KVS@#1@handler\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KVS@#1@handler\endcsname\relax
+ \KVS@AddUnhandled#2{#3}{#4}%
+ \else
+ \kv@handled@true
+ \csname KVS@#1@handler\endcsname{#3}{#4}%
+ \relax
+ \ifkv@handled@
+ \else
+ \KVS@AddUnhandled#2{#3}{#4}%
+ \fi
+ \fi
+ \else
+ \ifx\kv@value\relax
+& \unless\ifcsname KV@#1@#2@default\endcsname
+$ \begingroup\expandafter\expandafter\expandafter\endgroup
+$ \expandafter\ifx\csname KV@#1@#3@default\endcsname\relax
+ \kv@error@novalue{#1}{#3}%
+ \else
+ \csname KV@#1@#3@default\endcsname
+ \relax
+ \fi
+ \else
+ \csname KV@#1@#3\endcsname{#4}%
+ \fi
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@AddUnhandled}
+% \begin{macrocode}
+\long\def\KVS@AddUnhandled#1#2#3{%
+& \edef#1{%
+& \ifx#1\KVS@empty
+& \else
+& \unexpanded{#1},%
+& \fi
+& \unexpanded{{#2}={#3}}%
+& }%
+$ \begingroup
+$ \ifx#1\KVS@empty
+$ \toks@{{#2}={#3}}%
+$ \else
+$ \toks@\expandafter{#1,{#2}={#3}}%
+$ \fi
+$ \xdef\KVS@Global{\the\toks@}%
+$ \endgroup
+$ \let#1\KVS@Global
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kv@set@family@handler}
+% \begin{macrocode}
+\long\def\kv@set@family@handler#1#2{%
+ \begingroup
+ \csname @safe@activestrue\endcsname
+ \let\ifincsname\iftrue
+ \expandafter\endgroup
+ \expandafter\def\csname KVS@#1@handler\endcsname##1##2{#2}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kv@unset@family@handler}
+% \begin{macrocode}
+\long\def\kv@unset@family@handler#1#2{%
+ \begingroup
+ \csname @safe@activestrue\endcsname
+ \let\ifincsname\iftrue
+ \expandafter\endgroup
+ \expandafter\let\csname KVS@#1@handler\endcsname\@UnDeFiNeD
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Error handling}
+%
+% \begin{macro}{\kv@error@novalue}
+% \begin{macrocode}
+\def\kv@error@novalue{%
+ \kv@error@generic{No value specified for}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\kv@error@unknownkey}
+% \begin{macrocode}
+\def\kv@error@unknownkey{%
+ \kv@error@generic{Undefined}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\kv@error@generic}
+% \begin{macrocode}
+\def\kv@error@generic#1#2#3{%
+ \PackageError{kvsetkeys}{%
+ #1 key `#3'%
+ }{%
+ The keyval family of the key `#3' is `#2'.\MessageBreak
+ The setting of the key is ignored because of the error.\MessageBreak
+ \MessageBreak
+ \@ehc
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Do it all}
+%
+% \begin{macro}{\kvsetkeys}
+% \begin{macrocode}
+\long\def\kvsetkeys#1#2{%
+ \kv@parse{#2}{\kv@processor@default{#1}}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kvsetkeys@expandafter}
+% \begin{macrocode}
+\def\kvsetkeys@expandafter#1#2{%
+ \expandafter\kv@parse\expandafter{#2}{%
+ \kv@processor@default{#1}%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\KVS@cmd}
+% \begin{macrocode}
+\def\KVS@cmd{0}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@cmd@inc}
+% \begin{macrocode}
+\def\KVS@cmd@inc{%
+& \edef\KVS@cmd{\the\numexpr\KVS@cmd+1}%
+$ \begingroup
+$ \count255=\KVS@cmd\relax
+$ \advance\count255 by 1\relax
+$ \edef\x{\endgroup
+$ \noexpand\def\noexpand\KVS@cmd{\number\count255}%
+$ }%
+$ \x
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@cmd@dec}
+% \begin{macrocode}
+\def\KVS@cmd@dec{%
+& \edef\KVS@cmd{\the\numexpr\KVS@cmd-1}%
+$ \begingroup
+$ \count255=\KVS@cmd\relax
+$ \advance\count255 by -1\relax
+$ \edef\x{\endgroup
+$ \noexpand\def\noexpand\KVS@cmd{\number\count255}%
+$ }%
+$ \x
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@empty}
+% \begin{macrocode}
+\def\KVS@empty{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kvsetknownkeys}
+% \begin{macrocode}
+\def\kvsetknownkeys{%
+ \expandafter
+ \KVS@setknownkeys\csname KVS@cmd\KVS@cmd\endcsname{}%
+}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\KVS@setknownkeys}
+% \begin{macrocode}
+\long\def\KVS@setknownkeys#1#2#3#4#5{%
+ \let#1\KVS@empty
+ \KVS@cmd@inc
+ #2\kv@parse#2{#5}{\kv@processor@known{#3}#1}%
+ \KVS@cmd@dec
+ \let#4=#1%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\kvsetknownkeys@expandafter}
+% \begin{macrocode}
+\def\kvsetknownkeys@expandafter{%
+ \expandafter
+ \KVS@setknownkeys
+ \csname KVS@cmd\KVS@cmd\endcsname\expandafter
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\KVS@AtEnd%
+%</package>
+% \end{macrocode}
+%% \section{Installation}
+%
+% \subsection{Download}
+%
+% \paragraph{Package.} This package is available on
+% CTAN\footnote{\CTANpkg{kvsetkeys}}:
+% \begin{description}
+% \item[\CTAN{macros/latex/contrib/kvsetkeys/kvsetkeys.dtx}] The source file.
+% \item[\CTAN{macros/latex/contrib/kvsetkeys/kvsetkeys.pdf}] Documentation.
+% \end{description}
+%
+%
+% \paragraph{Bundle.} All the packages of the bundle `kvsetkeys'
+% are also available in a TDS compliant ZIP archive. There
+% the packages are already unpacked and the documentation files
+% are generated. The files and directories obey the TDS standard.
+% \begin{description}
+% \item[\CTANinstall{install/macros/latex/contrib/kvsetkeys.tds.zip}]
+% \end{description}
+% \emph{TDS} refers to the standard ``A Directory Structure
+% for \TeX\ Files'' (\CTANpkg{tds}). Directories
+% with \xfile{texmf} in their name are usually organized this way.
+%
+% \subsection{Bundle installation}
+%
+% \paragraph{Unpacking.} Unpack the \xfile{kvsetkeys.tds.zip} in the
+% TDS tree (also known as \xfile{texmf} tree) of your choice.
+% Example (linux):
+% \begin{quote}
+% |unzip kvsetkeys.tds.zip -d ~/texmf|
+% \end{quote}
+%
+% \subsection{Package installation}
+%
+% \paragraph{Unpacking.} The \xfile{.dtx} file is a self-extracting
+% \docstrip\ archive. The files are extracted by running the
+% \xfile{.dtx} through \plainTeX:
+% \begin{quote}
+% \verb|tex kvsetkeys.dtx|
+% \end{quote}
+%
+% \paragraph{TDS.} Now the different files must be moved into
+% the different directories in your installation TDS tree
+% (also known as \xfile{texmf} tree):
+% \begin{quote}
+% \def\t{^^A
+% \begin{tabular}{@{}>{\ttfamily}l@{ $\rightarrow$ }>{\ttfamily}l@{}}
+% kvsetkeys.sty & tex/generic/kvsetkeys/kvsetkeys.sty\\
+% kvsetkeys.pdf & doc/latex/kvsetkeys/kvsetkeys.pdf\\
+% kvsetkeys-example.tex & doc/latex/kvsetkeys/kvsetkeys-example.tex\\
+% kvsetkeys.dtx & source/latex/kvsetkeys/kvsetkeys.dtx\\
+% \end{tabular}^^A
+% }^^A
+% \sbox0{\t}^^A
+% \ifdim\wd0>\linewidth
+% \begingroup
+% \advance\linewidth by\leftmargin
+% \advance\linewidth by\rightmargin
+% \edef\x{\endgroup
+% \def\noexpand\lw{\the\linewidth}^^A
+% }\x
+% \def\lwbox{^^A
+% \leavevmode
+% \hbox to \linewidth{^^A
+% \kern-\leftmargin\relax
+% \hss
+% \usebox0
+% \hss
+% \kern-\rightmargin\relax
+% }^^A
+% }^^A
+% \ifdim\wd0>\lw
+% \sbox0{\small\t}^^A
+% \ifdim\wd0>\linewidth
+% \ifdim\wd0>\lw
+% \sbox0{\footnotesize\t}^^A
+% \ifdim\wd0>\linewidth
+% \ifdim\wd0>\lw
+% \sbox0{\scriptsize\t}^^A
+% \ifdim\wd0>\linewidth
+% \ifdim\wd0>\lw
+% \sbox0{\tiny\t}^^A
+% \ifdim\wd0>\linewidth
+% \lwbox
+% \else
+% \usebox0
+% \fi
+% \else
+% \lwbox
+% \fi
+% \else
+% \usebox0
+% \fi
+% \else
+% \lwbox
+% \fi
+% \else
+% \usebox0
+% \fi
+% \else
+% \lwbox
+% \fi
+% \else
+% \usebox0
+% \fi
+% \else
+% \lwbox
+% \fi
+% \else
+% \usebox0
+% \fi
+% \end{quote}
+% If you have a \xfile{docstrip.cfg} that configures and enables \docstrip's
+% TDS installing feature, then some files can already be in the right
+% place, see the documentation of \docstrip.
+%
+% \subsection{Refresh file name databases}
+%
+% If your \TeX~distribution
+% (\TeX\,Live, \mikTeX, \dots) relies on file name databases, you must refresh
+% these. For example, \TeX\,Live\ users run \verb|texhash| or
+% \verb|mktexlsr|.
+%
+% \subsection{Some details for the interested}
+%
+% \paragraph{Unpacking with \LaTeX.}
+% The \xfile{.dtx} chooses its action depending on the format:
+% \begin{description}
+% \item[\plainTeX:] Run \docstrip\ and extract the files.
+% \item[\LaTeX:] Generate the documentation.
+% \end{description}
+% If you insist on using \LaTeX\ for \docstrip\ (really,
+% \docstrip\ does not need \LaTeX), then inform the autodetect routine
+% about your intention:
+% \begin{quote}
+% \verb|latex \let\install=y\input{kvsetkeys.dtx}|
+% \end{quote}
+% Do not forget to quote the argument according to the demands
+% of your shell.
+%
+% \paragraph{Generating the documentation.}
+% You can use both the \xfile{.dtx} or the \xfile{.drv} to generate
+% the documentation. The process can be configured by the
+% configuration file \xfile{ltxdoc.cfg}. For instance, put this
+% line into this file, if you want to have A4 as paper format:
+% \begin{quote}
+% \verb|\PassOptionsToClass{a4paper}{article}|
+% \end{quote}
+% An example follows how to generate the
+% documentation with pdf\LaTeX:
+% \begin{quote}
+%\begin{verbatim}
+%pdflatex kvsetkeys.dtx
+%makeindex -s gind.ist kvsetkeys.idx
+%pdflatex kvsetkeys.dtx
+%makeindex -s gind.ist kvsetkeys.idx
+%pdflatex kvsetkeys.dtx
+%\end{verbatim}
+% \end{quote}
+%
+% \begin{thebibliography}{9}
+% \bibitem{tb94wright}
+% A guide to key-value methods, Joseph Wright, second draft for
+% \href{https://www.tug.org/tugboat}{TUGBoat}, 2009-03-17.
+% \url{https://www.texdev.net/uploads/2009/03/keyval.pdf}
+%
+% \bibitem{keyval}
+% David Carlisle:
+% \textit{The \xpackage{keyval} package};
+% 1999/03/16 v1.13;
+% \CTANpkg{keyval}.
+%
+% \end{thebibliography}
+%
+% \begin{History}
+% \begin{Version}{2006/03/06 v1.0}
+% \item
+% First version.
+% \end{Version}
+% \begin{Version}{2006/10/19 v1.1}
+% \item
+% Fix of \cs{kv@set@family@handler}.
+% \item
+% Example added.
+% \end{Version}
+% \begin{Version}{2007/09/09 v1.2}
+% \item
+% Using package \xpackage{infwarerr} for error messages.
+% \item
+% Catcode section rewritten.
+% \end{Version}
+% \begin{Version}{2007/09/29 v1.3}
+% \item
+% Normalizing and parsing of comma separated lists added.
+% \item
+% \cs{kv@normalize} rewritten.
+% \item
+% Robustness increased for normalizing and parsing,
+% e.g. for values with unmatched conditionals.
+% \item
+% \eTeX\ is used if available.
+% \item
+% Tests added for normalizing and parsing.
+% \end{Version}
+% \begin{Version}{2009/07/19 v1.4}
+% \item
+% Bug fix for \cs{kv@normalize}: unwanted space removed (Florent Chervet).
+% \end{Version}
+% \begin{Version}{2009/07/30 v1.5}
+% \item
+% Documentation addition: recommendation for Joseph Wright's
+% review article.
+% \end{Version}
+% \begin{Version}{2009/12/12 v1.6}
+% \item
+% Short info shortened.
+% \end{Version}
+% \begin{Version}{2009/12/22 v1.7}
+% \item
+% Internal optimization (\cs{KVS@CommaSpace}, \dots, \cs{KVS@EqualsSpace}).
+% \end{Version}
+% \begin{Version}{2010/01/28 v1.8}
+% \item
+% Compatibility to ini\TeX\ added.
+% \end{Version}
+% \begin{Version}{2010/03/01 v1.9}
+% \item
+% Support of \cs{par} inside values.
+% \end{Version}
+% \begin{Version}{2011/01/30 v1.10}
+% \item
+% Already loaded package files are not input in \hologo{plainTeX}.
+% \end{Version}
+% \begin{Version}{2011/03/03 v1.11}
+% \item
+% \cs{kv@break} and \cs{comma@break} added.
+% \end{Version}
+% \begin{Version}{2011/04/05 v1.12}
+% \item
+% Error message with recovery action in help message
+% (request by GL).
+% \end{Version}
+% \begin{Version}{2011/04/07 v1.13}
+% \item
+% \cs{kv@processor@default} supports package \xpackage{babel}'s
+% shorthands.
+% \item
+% \cs{kv@set@family@handler} with shorthand support.
+% \end{Version}
+% \begin{Version}{2011/06/15 v1.14}
+% \item
+% Some optimizations in token register uses (GL, HO).
+% \end{Version}
+% \begin{Version}{2011/10/18 v1.15}
+% \item
+% \cs{kv@processor@known} and \cs{kvsetknownkeys} added.
+% \item
+% \cs{kvsetkeys@expandafter} and \cs{kvsetknownkeys@expandafter} added.
+% \item
+% Family handler can report success or failure by \cs{kv@handled@true}
+% or \cs{kv@handled@false}.
+% \item
+% \cs{kv@unset@family@handler} added.
+% \end{Version}
+% \begin{Version}{2012/04/25 v1.16}
+% \item
+% \cs{kv@processor@default} and \cs{kv@processor@known} define
+% macro \cs{kv@fam} for convenience.
+% \item
+% Catcode section: Catcode setting for \texttt{+} added for \hologo{eTeX}.
+% \end{Version}
+% \begin{Version}{2016/05/16 v1.17}
+% \item
+% Documentation updates.
+% \end{Version}
+% \begin{Version}{2019/12/15 v1.18}
+% \item
+% Documentation updates.
+% \item
+% Avoid \xpackage{etexcmds} and \xpackage{infwarwerr} in \LaTeX.
+% \end{Version}
+% \end{History}
+%
+% \PrintIndex
+%
+% \Finale
+\endinput
diff --git a/macros/latex/contrib/kvsetkeys/kvsetkeys.pdf b/macros/latex/contrib/kvsetkeys/kvsetkeys.pdf
new file mode 100644
index 0000000000..7d4a72c04f
--- /dev/null
+++ b/macros/latex/contrib/kvsetkeys/kvsetkeys.pdf
Binary files differ