summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2009-06-11 00:13:25 +0000
committerKarl Berry <karl@freefriends.org>2009-06-11 00:13:25 +0000
commita9f41a29daefaf04b27040f25cd0f63f41d290a6 (patch)
treebb3b723d64aa7cd49b7dc9a24de50804295b4606
parentb5132ffc519f2695644d078951baf6a558eea289 (diff)
new latex3 package keys3 (10jun09)
git-svn-id: svn://tug.org/texlive/trunk@13703 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/latex/keys3/README38
-rw-r--r--Master/texmf-dist/doc/latex/keys3/keys3-test.tex394
-rw-r--r--Master/texmf-dist/doc/latex/keys3/keys3.pdfbin0 -> 444041 bytes
-rw-r--r--Master/texmf-dist/doc/latex/keys3/keys3opts2.pdfbin0 -> 313237 bytes
-rw-r--r--Master/texmf-dist/source/latex/keys3/keys3.dtx2267
-rw-r--r--Master/texmf-dist/source/latex/keys3/keys3.ins89
-rw-r--r--Master/texmf-dist/source/latex/keys3/keys3opts2.dtx256
-rw-r--r--Master/texmf-dist/source/latex/keys3/keys3opts2.ins89
-rw-r--r--Master/texmf-dist/tex/latex/keys3/keys3.sty686
-rw-r--r--Master/texmf-dist/tex/latex/keys3/keys3opts2.sty90
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check3
-rw-r--r--Master/tlpkg/tlpsrc/collection-latex3.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/keys3.tlpsrc0
13 files changed, 3912 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/latex/keys3/README b/Master/texmf-dist/doc/latex/keys3/README
new file mode 100644
index 00000000000..b79a089d7dc
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/keys3/README
@@ -0,0 +1,38 @@
+---------------------------------------------------------------
+The keys3 package --- Key management for LaTeX3
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+The keys3 package is an experimental implementation of key
+management for LaTeX3. The entire package is written in
+expl3 code rather than in traditional TeX/LaTeX. The key
+module used by keys3 is based on that introduced by the
+pgfkeys package from the pgf/TiKZ bundle. Keys are thus
+addressed in a path-like manner, and are controlled by key
+properties:
+
+/module/key/.code:n = <code>,
+/module/key/.value_required:,
+/module/key = <value>
+
+The keys3 package is intended as a method for testing how key
+management might be implemented in LaTeX3. Both the methods
+used inside the package and the interface provided are intended
+to be discussed. Feedback is welcomed either by e-mail
+(joseph.wright@morningstar2.co.uk) or at the package website
+(http://keys3.berlios.de).
+
+The experimental nature of keys3 and the underlying expl3
+system means that it should not be used in production
+documents. However, programmers testing expl3, or keen to work
+on improved key management systems, are encouraged to try the
+package and provide feedback.
+
+keys3 is *not* an official LaTeX3 team package. Whether any of
+the ideas explored here will be taken up by the team is
+therefore unknown. However, the keys3 package should provide
+ideas on what needs to be available for key management and how
+it can be implemented.
diff --git a/Master/texmf-dist/doc/latex/keys3/keys3-test.tex b/Master/texmf-dist/doc/latex/keys3/keys3-test.tex
new file mode 100644
index 00000000000..2715d0f8a91
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/keys3/keys3-test.tex
@@ -0,0 +1,394 @@
+% !TEX program = pdflatex
+%
+% This is a test file demonstrating the keys3 experimental LaTeX3
+% package. This file illustrates the methods made available by keys3
+% for managing key-value input. It also acts as a check, as almost all
+% of the keys3 methods are used (with known output) and because a number
+% of errors are deliberately raised.
+
+\documentclass{article}
+\usepackage{keys3,xparse}
+\usepackage[parfill]{parskip}
+
+% The document commands for creating keys is very simple. A generic
+% one is created to set any modules keys, and one specific to the module
+% used here.
+\ExplSyntaxOn
+
+\DeclareDocumentCommand{\SetModuleKeys}{m}{
+ \keys_manage:n{
+ module~name/.cd:,
+ #1}
+}
+
+\DeclareDocumentCommand{\SetKeys}{m}{
+ \keys_manage:n{#1}
+}
+
+\ExplSyntaxOff
+
+\begin{document}
+
+% Each test starts with a short piece of text explaining the property
+% being used. Under this, the expected output is given, followed
+% if relevant by the text of any error messages expected.
+
+% Test one: Simply creating a key with one argument and then using it.
+% Notice that the text length determines the limits of the argument:
+% spaces at either end are ignored. Literal spaces between text are
+% counted, as are those enclosed in braces.
+% ----------------------------------------------------------------------
+% You said: `Hello World'
+% You said: ` Hello World '
+% You said: `'
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key/.code:n = {You said: `#1'\\},
+ key = Hello World ,
+ key = { Hello World },
+ key
+}
+
+% Test two: Creating a multiple-argument key. Here, the arguments
+% given need to be at least as many as those required by the code!
+% ----------------------------------------------------------------------
+% You said: `A', `B', `C', D'.
+% You said: `AAA', `BBB', `CCC', ` DDD'.
+% You said: `', `', `', `'.
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key/.code:Nn = 4 {You said: `#1', `#2', `#3', `#4'.\\},
+ key = ABCD,
+ key = {AAA}{BBB} {CCC} { DDD},
+ key =,
+}
+
+% Test three: The ability to change the key path is available with the
+% .cd: property. The following moves from the key root to two separate
+% paths, creating keys with the same names.
+% ----------------------------------------------------------------------
+% A key in path one
+% A key in path two
+% A key in path two
+% A key in path one
+% ----------------------------------------------------------------------
+\SetKeys{
+ /path one/.cd:,
+ key/.code:n = A key in path one\\,
+ /path two/.cd:,
+ key/.code:n = A key in path two\\,
+ /path one/key,
+ /path two/key,
+ key,
+ /path one/.cd:,
+ key
+}
+
+% Test four: Variants of .code:n and .code:Nn exist which carry out a
+% full expansion on code definition. First the :x variant is tested.
+% ----------------------------------------------------------------------
+% Temp holds: ABC
+% Temp hold: 123
+% ----------------------------------------------------------------------
+\newcommand*{\temp}{123}
+\SetModuleKeys{
+ key one/.code:n = Temp holds: \temp\\,
+ key two/.code:x = Temp holds: \temp,
+}
+\renewcommand*{\temp}{ABC}
+\SetModuleKeys{
+ key one,
+ key two
+}
+% Now the :Nx version
+% ----------------------------------------------------------------------
+% Arguments: A, B. Temp holds: ABC
+% Arguments: 1, 2. Temp hold: 123
+% ----------------------------------------------------------------------
+\renewcommand*{\temp}{123}
+\SetModuleKeys{
+ key one/.code:Nn = 2 {Arguments: #1, #2. Temp holds: \temp\\},
+ key two/.code:Nx = 2 {Arguments: #1, #2. Temp holds: \temp},
+}
+
+\renewcommand*{\temp}{ABC}
+\SetModuleKeys{
+ key one = {A} {B},
+ key two = {1} {2}
+}
+
+% Test five: Values can be required and forbidden with the
+% .value_required: and .value_forbidden: properties.
+% ----------------------------------------------------------------------
+% All okay
+% All okay
+% ----------------------------------------------------------------------
+% The key `/module name/key' cannot taken a value:
+% the given input `Not allowed' is being ignored.
+%
+% The key `/module name/key' requires a value
+% and is being ignored.
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key/.code:n = {All okay\\},
+ key/.value_forbidden:,
+ key,
+ key = Not allowed,
+ key/.code:n = #1,
+ key/.value_required:,
+ key = {All okay},
+ key
+}
+
+% Test six: Keys can be given a default value, to be used if nothing is
+% specified by the user.
+% ----------------------------------------------------------------------
+% Default
+% Real
+% Default
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key/.code:n = {#1\\},
+ key/.default:n = Default,
+ key,
+ key = Real,
+ key
+}
+
+% Test seven: Keys can be created to store data tl variable both
+% locally and globally.
+% ----------------------------------------------------------------------
+% Some content
+% Outside
+% Some content
+% Some content
+% ----------------------------------------------------------------------
+\renewcommand*{\temp}{Outside}
+\SetModuleKeys{
+ key one/.tl_set:N = \temp,
+}
+\begingroup
+\SetModuleKeys{
+ key = Some content
+}
+\temp\\
+\endgroup
+\temp\\
+\SetModuleKeys{
+ key/.tl_gset:N = \temp
+}
+\begingroup
+\SetModuleKeys{
+ key = Some content
+}
+\temp\\
+\endgroup
+\temp\\
+
+% Test eight: An expanded version of .tkp_set:N is available.
+% ----------------------------------------------------------------------
+% Unexpanded
+% Expanded
+% ----------------------------------------------------------------------
+\renewcommand*{\temp}{Expanded}
+\newcommand*{\tempa}{}
+\newcommand*{\tempb}{}
+\SetModuleKeys{
+ key one/.tl_set:N = \tempa,
+ key two/.tl_set_x:N = \tempb,
+ key one = \temp,
+ key two = \temp,
+}
+\renewcommand*{\temp}{Unexpanded}
+\tempa\\
+\tempb\\
+
+% Test nine: Storage functions for int, skip and toks variables are also
+% available. Both local and global setting is possible, although there
+% are no "x" versions.
+% ----------------------------------------------------------------------
+% 12345
+% 10.00002pt
+% abc
+% ----------------------------------------------------------------------
+\newcount\tempint
+\newskip\tempskip
+\newtoks\temptoks
+\SetModuleKeys{
+ key/.int_set:N = \tempint,
+ key = 12345,
+ key/.skip_set:N = \tempskip,
+ key = 1 em,
+ key/.toks_set:N = \temptoks,
+ key = abc
+}
+\the\tempint\\
+\the\tempskip\\
+\the\temptoks
+
+% Test ten: Variables can be identified only by the unique part of their
+% name, provided that /keys/current_module:n has been set with the
+% appropriate information. Both local and global names can be used in
+% this way.
+% ----------------------------------------------------------------------
+% Local
+% Global
+% ----------------------------------------------------------------------
+\ExplSyntaxOn
+\tl_new:N \l_module_local_tl
+\tl_new:N \g_module_global_tl
+\ExplSyntaxOff
+\SetModuleKeys{
+ /keys/current_module:n = module,
+ key one/.tl_set:n = local,
+ key two/.tl_gset:n = global,
+ key one = Local,
+ key two = Global
+}
+\ExplSyntaxOn
+\l_module_local_tl \\
+\g_module_global_tl
+\ExplSyntaxOff
+
+% Test eleven: Boolean keys are created in the same way as other storage
+% keys. However, the key will then only accept "true" and "false" as
+% input. The key name can be given alone, resulting in the switch being
+% set "true".
+% ----------------------------------------------------------------------
+% Local switch true
+% Global switch true
+% ----------------------------------------------------------------------
+% Key `/module name/key one' takes the Boolean values
+% `true' and `false' only.
+% The given value `rubbish' is being ignored.
+% ----------------------------------------------------------------------
+\ExplSyntaxOn
+\bool_new:N \localbool
+\bool_new:N \g_module_global_bool
+\ExplSyntaxOff
+\SetModuleKeys{
+ /keys/current_module:n = module,
+ key one/.bool_set:N = \localbool,
+ key two/.bool_gset:n = global,
+ key one = true,
+ key two = false,
+ key two,
+ key one = rubbish
+}
+\ExplSyntaxOn
+\bool_if:NTF \localbool { Local~switch~true } { Local~switch~false } \\
+\bool_if:NTF \g_module_global_bool
+ { Global~switch~true } { Global~switch~false }
+\ExplSyntaxOff
+
+% Test twelve: The logic for Boolean keys can be reversed using the
+% .bool_set_inverse:N and related properties. This can be used with
+% in concert with .bool_set:N to create complementary keys.
+% ----------------------------------------------------------------------
+% Hello World
+% Hello User
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ /keys/current_module:n = module,
+ key one/.bool_set_inverse:N = \localbool,
+ key two/.bool_gset_inverse:n = global,
+ key one,
+ key two = true,
+}
+\ExplSyntaxOn
+\bool_if:NTF \localbool { Local~switch~true } { Local~switch~false } \\
+\bool_if:NTF \g_module_global_bool
+ { Global~switch~true } { Global~switch~false }
+\ExplSyntaxOff
+
+% Test thirteen: Choices can be created either from a list of options or
+% one by one. Both are tested here by creating a list then adding to it
+% with an extra key. Unknown choices lead to an error message.
+% ----------------------------------------------------------------------
+% Choice `a' is number 1
+% Choice `b' is number 2
+% Choice `c' is number 3
+% An extra choice
+% ----------------------------------------------------------------------
+% Choice `e' unknown for key `/module name/key/e':
+% the key is being ignored.
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key/.create_choices:nn = {a,b,c}
+ {Choice `\csname l_keys_current_choice_tl\endcsname' is number
+ \the\csname l_keys_current_choice_int\endcsname \\},
+ key/d/.code:n = {An extra choice \\},
+ key = a,
+ key = b,
+ key = c,
+ key = d,
+ key = e
+}
+
+% Test fourteen: Keys can be used to set other keys. One or more
+% arguments can be accepted when this happens, allowing complex key
+% setting to be carried out easily. There are expanded versions of these
+% properties, which work similarly.
+% ----------------------------------------------------------------------
+% Hello Fred.
+% How are you, Fred?
+% Hello Fred.
+% How are you, Ginger?
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key one/.code:n = {Hello #1.\\},
+ key two/.code:n = {How are you, #1?\\},
+ key three/.use_keys:n = { key one = #1, key two = #1},
+ key three = Fred,
+ key three/.use_keys:Nn = 2 { key one = #1, key two = #2},
+ key three = {Fred} {Ginger}
+}
+
+% Test fifteen: The .try:w and .retry:w properties attempt to set a key
+% only if it exists. The .try:w property does this for every key it
+% is given, whereas .retry: only works if the previous .try:w failed.
+% ----------------------------------------------------------------------
+% Hello
+% World
+% Seen
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key one/.code:n = {#1\\},
+ key two/.code:n = {#1\\},
+ key one/.try:n = Hello,
+ key ten/.try:n = Rubbish,
+ key two/.try:n = World,
+ key two/.retry:n = Not seen,
+ key ten/.try:n = Rubbish,
+ key two/.retry:n = Seen,
+}
+
+% Test sixteen: Two programmers tools, .show_code: and .show_key:, do
+% pretty much what might be expected. .show_key: is mainly useful for
+% internal keys.
+% ----------------------------------------------------------------------
+% a or b
+% ----------------------------------------------------------------------
+% > \keys-root/module name/key one/._cmd:w=\long macro:
+% #1#2->#1 or #2.
+%
+% > \keys-root/module name/key one/._num_args_tl=macro:
+% ->2.
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ key one/.code:Nn = 2 {#1 or #2},
+ key one = ab,
+ key one/.show_code:,
+ key one/._num_args_tl/.show_key:,
+}
+
+% Test seventeen: A completely unknown key should cause an error.
+% ----------------------------------------------------------------------
+% The key `/module name/unknown key' is unknown and is being ignored
+% ----------------------------------------------------------------------
+\SetModuleKeys{
+ unknown key = some value
+}
+
+\end{document}
+
diff --git a/Master/texmf-dist/doc/latex/keys3/keys3.pdf b/Master/texmf-dist/doc/latex/keys3/keys3.pdf
new file mode 100644
index 00000000000..3d26fd7fdfe
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/keys3/keys3.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/keys3/keys3opts2.pdf b/Master/texmf-dist/doc/latex/keys3/keys3opts2.pdf
new file mode 100644
index 00000000000..cdc4783125a
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/keys3/keys3opts2.pdf
Binary files differ
diff --git a/Master/texmf-dist/source/latex/keys3/keys3.dtx b/Master/texmf-dist/source/latex/keys3/keys3.dtx
new file mode 100644
index 00000000000..6932050bb8c
--- /dev/null
+++ b/Master/texmf-dist/source/latex/keys3/keys3.dtx
@@ -0,0 +1,2267 @@
+% \iffalse meta-comment
+% !TEX program = pdflatex
+% !TEX encoding = ISO-8859-1
+%<*internal>
+\iffalse
+%</internal>
+%<*readme>
+---------------------------------------------------------------
+The keys3 package --- Key management for LaTeX3
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+The keys3 package is an experimental implementation of key
+management for LaTeX3. The entire package is written in
+expl3 code rather than in traditional TeX/LaTeX. The key
+module used by keys3 is based on that introduced by the
+pgfkeys package from the pgf/TiKZ bundle. Keys are thus
+addressed in a path-like manner, and are controlled by key
+properties:
+
+/module/key/.code:n = <code>,
+/module/key/.value_required:,
+/module/key = <value>
+
+The keys3 package is intended as a method for testing how key
+management might be implemented in LaTeX3. Both the methods
+used inside the package and the interface provided are intended
+to be discussed. Feedback is welcomed either by e-mail
+(joseph.wright@morningstar2.co.uk) or at the package website
+(http://keys3.berlios.de).
+
+The experimental nature of keys3 and the underlying expl3
+system means that it should not be used in production
+documents. However, programmers testing expl3, or keen to work
+on improved key management systems, are encouraged to try the
+package and provide feedback.
+
+keys3 is *not* an official LaTeX3 team package. Whether any of
+the ideas explored here will be taken up by the team is
+therefore unknown. However, the keys3 package should provide
+ideas on what needs to be available for key management and how
+it can be implemented.
+%</readme>
+%<*internal>
+\fi
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+%</internal>
+%<*install>
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+---------------------------------------------------------------
+The keys3 package --- Key management for LaTeX3
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+EXPERIMENTAL CODE
+
+Do not distribute this file without also distributing the
+source files specified above.
+
+Do not distribute a modified version of this file.
+
+\endpreamble
+\postamble
+
+Copyright (C) 2008-2009 by
+ Joseph Wright <joseph.wright@morningstar2.co.uk>
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Joseph Wright.
+
+This work consists of the file keys3.dtx
+ and the derived files keys3.pdf,
+ keys3.sty and
+ keys3.ins.
+
+\endpostamble
+\usedir{tex/latex/keys3}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usedir{source/latex/keys3}
+\generate{
+ \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\nopreamble\nopostamble
+\usedir{doc/latex/keys3}
+\generate{
+ \file{README.txt}{\from{\jobname.dtx}{readme}}
+}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%</internal>
+%<*driver|package>
+\RequirePackage{expl3}
+\GetIdInfo$Id: keys3.dtx 101 2009-06-10 12:16:20Z josephwright $
+ {Key management for LaTeX3}
+%</driver|package>
+%\fi
+%\iffalse
+%<*driver>
+%\fi
+\ProvidesFile{\filename.\filenameext}
+ [\filedate\space v\fileversion\space\filedescription]
+%\iffalse
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\filename.\filenameext}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \pkg{keys3} package^^A
+% \thanks{This file has version number \fileversion, last
+% revised \filedate.}\\
+% Key management for \LaTeX3}
+% \author{Joseph Wright^^A
+% \thanks{E-mail: joseph.wright@morningstar2.co.uk}}
+% \date{\filedate}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+%\section{Key management}
+%
+% The key--value method is a popular system for creating large numbers
+% of settings for controlling macro or package behaviour. For the
+% user, the system normally results in input of the form
+%\begin{verbatim}
+% \PackageControlMacro{
+% key = value,
+% key two = value two
+% }
+%\end{verbatim}
+% or
+%\begin{verbatim}
+% \PackageMacro[
+% key = value,
+% key two = value two
+% ]{argument}.
+%\end{verbatim}
+% For the programmer, the original \pkg{keyval} package gives only
+% the most basic interface for this work. All key macros have to be
+% created one at a time, and as a result the \pkg{kvoptions} and
+% \pkg{xkeyval} packages have been written to extend the ease of
+% creating keys. However, the underlying model is rather inflexible.
+%
+% As an alternative, \pkg{pgfkeys} from the \pkg{pgf}/Ti\emph{k}z
+% bundle uses a ``file-like'' model for keys. In this model, each key
+% has one or more functions attached to define its action. In
+% \pkg{pgfkeys} terminology, these are key \emph{handlers}. Keys can
+% be created and used using a unified interface:
+%\begin{verbatim}
+% \pgfkeys{
+% /path/key/.handler = code,
+% /path/key = value
+% }
+%\end{verbatim}
+%
+% The \pkg{keys3} package is aimed at creating a high-level
+% programmers interface for key--value controls in \LaTeX3. Key
+% creation and control follows the \pkg{pgfkeys} closely, although
+% changes have been made to adhere to the new coding ideas of \LaTeX3,
+% and new functions have been added. The \pkg{keys3} package is
+% \emph{not} a simple translation of \pkg{pgfkeys} to the new syntax.
+% In package internals have been written from the ground up, to better
+% enforce variable typing and to act in a know manner under failing
+% circumstances.
+%
+% In the \pkg{keys3} model, each key belongs to a module,
+% which may have one or more subdivisions. Each key then has
+% one or more properties which define how the key acts. The idea
+% of a single function for all key control is retained:
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key/.property = code,
+% /module/sub/key/.property = more code,
+% /module/key = value
+% }
+%\end{verbatim}
+% The combination of all \m{module} parts up to the \m{key} is referred
+% to as the \emph{path} of the key.
+%
+% Keys are created by assigning one or more properties to the key,
+% most commonly a piece of code to execute when the key is used (this
+% is the \texttt{.code:n} property).
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key/.code:n = <code>
+% }
+%\end{verbatim}
+% Keys are then used in the normal way, by assigning a value to the
+% key.
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key = <value>
+% }
+%\end{verbatim}
+% Here, \meta{value} is passed to the \meta{code} defined |#1|. The
+% \meta{code} can then process it in whatever way is appropriate.
+%
+% At a document level, it is likely that \cs{keys_manage:n} would be
+% wrapped up into a user macro or optional argument. For \LaTeXe, a
+% generic set up macro might be created with:
+%\begin{verbatim}
+% \newcommand*\SomePackageSetup[1]{%
+% \@nameuse{keys_manage:n}{%
+% /package name/.cd:,
+% #1
+% }%
+% }
+%\end{verbatim}
+% or to use key--value input as the optional argument for a macro:
+%\begin{verbatim}
+% \newcommand*\SomePackageMacro[2][]{%
+% \begingroup
+% \@nameuse{keys_manage:n}{
+% /package name/.cd:,
+% #1
+% }%
+% % Main code for \SomePackageMacro
+% \endgroup
+% }
+%\end{verbatim}
+% The same concepts using \pkg{xparse} for \LaTeX3 use:
+%\begin{verbatim}
+% \DeclareDocumentCommand \SomePackageSetup { m } {
+% \keys_manage:n {
+% /package name/.cd:,
+% #1
+% }
+% }
+% \DeclareDocumentCommand \SomePackageMacro { o m } {
+% \group_begin:
+% \keys_manage:n {
+% /package name/.cd:,
+% #1
+% }
+% % Main code for \SomePackageMacro
+% \group_end:
+% }
+%\end{verbatim}
+%
+%\subsection{Creating, retrieving and setting keys}
+%
+% The main interface for key management is the the \cs{keys_manage:n}
+% macro. This can be used to create, retrieve and set keys, and is
+% therefore the preferred interface for the package.
+%
+% \begin{function}{
+% \keys_manage:n|
+% \keys_manage_quick:n
+% }
+% \begin{syntax}
+% "\keys_manage:n" <keyval list>
+% \end{syntax}
+% The main key management function, which parses over the <keyval
+% list> and processes each key. The standard version removes leading
+% and trailing spaces and checks catcodes for ungrouped ``|,|'' and
+% ``|=|'' tokens. The |_quick| version omits these tests for speed,
+% and is therefore more suited to creating keys inside code blocks.
+% \end{function}
+%
+% \begin{function}{
+% \keys_manage:nn|
+% \keys_manage_quick:nn|
+% }
+% \begin{syntax}
+% "\keys_manage:nn" <path> <keyval list>
+% \end{syntax}
+% Versions of the above which include a <path> as an argument. These
+% are faster versions of the |:n| macros when setting lots of keys
+% separately and with known paths.
+% \end{function}
+%
+% Setting up and altering keys is carried out using one or more
+% properties (in \pkg{pgfkeys}, these are called handlers). In all
+% cases, \m{key} may be a full-qualified key with a path, or a partial
+% key to which the default path will be added. Notice that when a
+% single argument is required, the braces around the argument may be
+% omitted without any error arising.
+%
+%\subsubsection{Storing values}
+%
+% A common use of key--value input is to store the values given in a
+% variable for later use. \pkg{keys3} provides pre-defined
+% properties for storing in integer, skip, token list variables and
+% token registers. The method is similar in all cases:
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key~one/.int_set:N = \l_mod_data_int,
+% /module/key~two/.skip_set:N = \l_mod_data_skip,
+% /module/key~three/.tl_set:N = \l_mod_data_tl,
+% /module/key~four/.toks_set:N = \l_mod_data_toks
+% }
+%\end{verbatim}
+% These assignments are local: in each case, a global variant is
+% available, with \texttt{set} replaced by \texttt{gset}. For example,
+% \texttt{.tl_get:N} globally stores values in a token list variable.
+%
+% When using keys to store a value, it is often convenient to give only
+% the unique part of the variable name. This is particularly true when
+% creating a large number of related storage areas. To achieve this,
+% \pkg{keys3} requires that the module prefix to be used is defined
+% first. The special key \texttt{/keys/current_module:n} can be set with
+% the name of the current module.
+%\begin{verbatim}
+% \keys_manage:n{
+% /keys/current_module:n = module,
+% /module/key/.tl_set:n = my_data,
+% }
+%\end{verbatim}
+% This will use a variable called \cs{l_module_my_data_tl} to store the
+% input. Thus the preceding code achieves the same effect as
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key/.tl_set:N = \l_module_my_data_tl
+% }
+%\end{verbatim}
+% The same method applies to int, skip and toks variables. The
+% appropriate prefix (\cs{l_} or \cs{g_}) and suffix (|_int|, |_skip|,
+% |_tl| or |_toks|) is always added.
+%
+%\subsubsection{Multiple choice keys}
+%
+% Multiple choice keys are created in \pkg{keys3} using the
+% \texttt{.expects_choice:} property. Each choice is then a sub-key of
+% the choice key.
+%\begin{verbatim}
+% \keys_manage:n{
+% /module/key/.expects_choice:,
+% /module/key/choice~a/.code:n = Some code,
+% /module/key/choice~b/.code:n = Some other code,
+% }
+%\end{verbatim}
+% In this way, choices which execute arbitrary code can be created.
+% Notice that the \m{code} should \emph{not} include a parameter (|#1|).
+%
+% Often it is desirable to create a family of similar choices, which
+% only require either the text of the choice, or the position of the
+% choice in a list, to be used. To create this type of simple choice,
+% \pkg{keys3} provides the \texttt{.create_choices:nn} property. This
+% applies the same code to a list of choice text. Inside the code,
+% the name of the choice given is available as
+% \cs{l_keys_current_choice_tl}. The position of the choice in the
+% lists is also available, as \cs{l_keys_current_choice_int}.
+%\begin{verbatim}
+% \keys_manage:n {
+% /module/key/.create_choices:nn = {choice~a, choice~b, choice~c} {
+% You~gave~choice~``\l_keys_current_choice_tl'',~which~is~in~
+% position~\l_keys_current_choice_int\~in~the~list.
+% }
+% }
+%\end{verbatim}
+%
+%\subsection{Properties}
+%
+% \begin{function}{
+% .bool_set:N|
+% .bool_gset:N|
+% }
+% \begin{syntax}
+% <key>/.bool_set:N = <bool>
+% \end{syntax}
+% Defines <key> to set <bool> to <value> (which must be either
+% \texttt{true} or \texttt{false}).
+% \end{function}
+%
+% \begin{function}{
+% .bool_set:n|
+% .bool_gset:n|
+% }
+% \begin{syntax}
+% <key>/.bool_set:n = <name>
+% \end{syntax}
+% Defines <key> to set switch with unique <name> to <value> (which
+% must be either \texttt{true} or \texttt{false}). The switch
+% name will be constructed using the current <module> (if any),
+% prefixed by |\l_| and ending with |_int|.
+% \end{function}
+%
+% \begin{function}{
+% .bool_set_inverse:N|
+% .bool_gset_inverse:N|
+% }
+% \begin{syntax}
+% <key>/.bool_set_inverse:N = <bool>
+% \end{syntax}
+% Defines <key> to set <bool> to <value> (which must be either
+% \texttt{true} or \texttt{false}), with reversed logic.
+% \end{function}
+%
+% \begin{function}{
+% .bool_set_inverse:n|
+% .bool_gset_inverse:n|
+% }
+% \begin{syntax}
+% <key>/.bool_set_inverse:n = <name>
+% \end{syntax}
+% Defines <key> to set switch with unique <name> to <value> (which
+% must be either \texttt{true} or \texttt{false}), with reversed logic.
+% The switch name will be constructed using the current <module> (if
+% any), prefixed by |\l_| and ending with |_int|.
+% \end{function}
+%
+% \begin{function}{.cd:}
+% \begin{syntax}
+% <key>/.cd:
+% \end{syntax}
+% Changes path to <key>.
+% \end{function}
+%
+% \begin{function}{
+% .code:n|
+% .code:x
+% }
+% \begin{syntax}
+% <key>/.code:n = <code>
+% \end{syntax}
+% Stores the <code> for execution when <key> is called. The <code> can
+% include one parameter (|#1|).
+% \end{function}
+%
+% \begin{function}{
+% .code:Nn|
+% .code:Nx
+% }
+% \begin{syntax}
+% <key>/.code:Nn = <number> <code>
+% \end{syntax}
+% Stores the <code> for execution when <key> is called. The <code> can
+% include <number> parameters, which can be in the range 0--9.
+% \end{function}
+%
+% \begin{function}{
+% .create_choices:nn|
+% .create_choices:nx|
+% }
+% \begin{syntax}
+% <key>/.create_choices:nn = <list> <code>
+% \end{syntax}
+% Creates a sub-key of <key> for each <choice> in the comma-separated
+% <list>. Each <choice> will have <code> associated with it. The
+% current choice text is available as \cs{l_keys_current_choice_tl},
+% and its position in the <list> as \cs{l_keys_current_choice_int}.
+% \end{function}
+%
+% \begin{function}{.default:n}
+% \begin{syntax}
+% <key>/.default:n = <content>
+% \end{syntax}
+% Creates a default value for <key>, which is used if no value is
+% given. The <content> is stored as a token list variable, and so
+% must be compatible with this variable type.
+% \end{function}
+%
+% \begin{function}{.expects_choice:}
+% \begin{syntax}
+% <key>/.expects_choice:
+% \end{syntax}
+% Indicates that the <value> given for <key> should be a sub-key of
+% <key>. In this way, <key> accepts one of a limited range of choices.
+% \end{function}
+%
+% \begin{function}{
+% .int_set:N|
+% .int_gset:N|
+% }
+% \begin{syntax}
+% <key>/.int_set:N = <int>
+% \end{syntax}
+% Defines <key> to store <value> in the <int> named.
+% \end{function}
+%
+% \begin{function}{
+% .int_set:n|
+% .int_gset:n|
+% }
+% \begin{syntax}
+% <key>/.int_set:n = <name>
+% \end{syntax}
+% Defines <key> to store <value> in a int with unique name <name>. The
+% int name will be constructed using the current <module> (if any),
+% prefixed by |\l_| and ending with |_int|.
+% \end{function}
+%
+% \begin{function}{.retry:n}
+% \begin{syntax}
+% <key>/.retry:n = <data>
+% \end{syntax}
+% Executes <key> if it exists and if the previous \texttt{.try:n}
+% failed. The <data> is passed to <key>, if successful.
+% \end{function}
+%
+% \begin{function}{
+% .show_code:|
+% .show_key:|
+% }
+% \begin{syntax}
+% <key>/.show_code:
+% <key>/.show_key:
+% \end{syntax}
+% Shows the function for the current key or the code associated with
+% the current key.
+% \end{function}
+%
+% \begin{function}{
+% .skip_set:N|
+% .skip_gset:N|
+% }
+% \begin{syntax}
+% <key>/.skip_set:N = <skip>
+% \end{syntax}
+% Defines <key> to store <value> in the <skip> named.
+% \end{function}
+%
+% \begin{function}{
+% .skip_set:n|
+% .skip_gset:n|
+% }
+% \begin{syntax}
+% <key>/.skip_set:n = <name>
+% \end{syntax}
+% Defines <key> to store <value> in a skip with unique name <name>. The
+% skip name will be constructed using the current <module> (if any),
+% prefixed by |\l_| and ending with |_skip|.
+% \end{function}
+%
+% \begin{function}{
+% .tl_set:N|
+% .tl_set_x:N|
+% .tl_gset:N|
+% .tl_gset_x:N|
+% }
+% \begin{syntax}
+% <key>/.tl_set:N = <tl>
+% \end{syntax}
+% Defines <key> to store <value> in the <tl> named. The \texttt{x}
+% versions use \cs{tl_(g)set:Nx} for this process, the standard
+% version \cs{tl_(g)set:Nn}.
+% \end{function}
+%
+% \begin{function}{
+% .tl_set:n|
+% .tl_set_x:n|
+% .tl_gset:n|
+% .tl_gset_x:n|
+% }
+% \begin{syntax}
+% <key>/.tl_set:n = <name>
+% \end{syntax}
+% Defines <key> to store <value> in a tl with unique name <name>. The
+% tl name will be constructed using the current <module> (if any),
+% prefixed by |\l_| and ending with |_tl|.
+% \end{function}
+%
+% \begin{function}{
+% .toks_set:N|
+% .toks_gset:N|
+% }
+% \begin{syntax}
+% <key>/.toks_set:N = <toks>
+% \end{syntax}
+% Defines <key> to store <value> in the <toks> named.
+% \end{function}
+%
+% \begin{function}{
+% .toks_set:n|
+% .toks_gset:n|
+% }
+% \begin{syntax}
+% <key>/.toks_set:n = <name>
+% \end{syntax}
+% Defines <key> to store <value> in a toks with unique name <name>. The
+% rest of the toks name will be constructed using the current <module>
+% (if any), prefixes by |\l_| and ending with |_toks|.
+% \end{function}
+%
+% \begin{function}{.try:n}
+% \begin{syntax}
+% <key>/.try:n = <data>
+% \end{syntax}
+% Executes <key> if defined, and does nothing otherwise. The <data> is
+% passed to <key>, if successful.
+% \end{function}
+%
+% \begin{function}{
+% .use_keys:n|
+% .use_keys:x|
+% }
+% \begin{syntax}
+% <key>/.use_keys:n = <keyval list>
+% \end{syntax}
+% Calling <key> applies the <keyval list> as a block. Thus one <key>
+% can make many related settings. As usual, |#1| is available in the
+% <keyval list> as <key> will take one argument.
+% \end{function}
+%
+% \begin{function}{
+% .use_keys:Nn|
+% .use_keys:Nx|
+% }
+% \begin{syntax}
+% <key>/.use_keys:Nn = <number> <keyval list>
+% \end{syntax}
+% Calling <key> applies the <keyval list> as a block. Thus one <key>
+% can make many related settings. The <key> will take <number> of
+% arguments, which can be used inside the <keyval list>.
+% \end{function}
+%
+% \begin{function}{
+% .value_forbidden:|
+% .value_required:|
+% }
+% \begin{syntax}
+% <key>/.value_forbidden:
+% \end{syntax}
+% Flags for forbidding and requiring a <value> for <key>.
+% \end{function}
+%
+%\subsection{Variables and constants}
+%
+% \begin{variable}{
+% \c_keys_0_empty_tl|
+% \c_keys_1_empty_tl|
+% \c_keys_2_empty_tl|
+% \c_keys_3_empty_tl|
+% \c_keys_4_empty_tl|
+% \c_keys_5_empty_tl|
+% \c_keys_6_empty_tl|
+% \c_keys_7_empty_tl|
+% \c_keys_8_empty_tl|
+% \c_keys_9_empty_tl|
+% }
+% A set of token list variables containing empty groups.
+% \end{variable}
+%
+% \begin{variable}{\c_keys_cs_prefix_tl}
+% The prefix added to the fully-qualified key when saving them.
+% \end{variable}
+%
+% \begin{variable}{
+% \c_keys_errors_path_tl|
+% \c_keys_properties_path_tl|
+% \c_keys_utilities_path_tl|
+% }
+% Paths for properties used by \pkg{l3keys} itself.
+% \end{variable}
+%
+% \begin{variable}{\c_keys_root_tl}
+% The root path for keys.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_choice_code_tl}
+% The code to execute for each multiple choice when created \emph{en
+% masse}.
+% \end{variable}
+%
+% \begin{variable}{
+% \l_keys_current_choice_tl|
+% \l_keys_current_choice_int|
+% }
+% Information on multiple choices.
+% \end{variable}
+%
+% \begin{variable}{
+% \l_keys_current_key_full_tl|
+% \l_keys_current_key_name_tl|
+% }
+% The current key name is stored both with and without a path.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_current_module_tl}
+% Current module name used when creating csnames.
+% \end{variable}
+%
+% \begin{variable}{
+% \l_keys_current_path_tl|
+% \l_keys_default_path_tl|
+% \l_keys_choice_path_tl|
+% }
+% Various key paths need to be stored.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_current_value_toks}
+% The value given for the current key, stored as a token register.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_no_value_bool}
+% A marker for ``no value'' as key input.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_success_bool}
+% A marker used when trying keys without raising errors.
+% \end{variable}
+%
+% \begin{variable}{\l_keys_tmpa_tl}
+% A scratch variable.
+% \end{variable}
+%
+%\subsection{Internal functions}
+%
+% Notice that everything should be done using the keys system. Only
+% \cs{keys_manage:n} and so on are intended for external use. In all
+% cases, \m{key} is a fully-qualified key name. Functions created will
+% be prefixed with \cs{c_keys_cs_prefix_tl}.
+%
+% \begin{function}{\keys_bool_new:n}
+% \begin{syntax}
+% "\keys_bool_new:n" <key>
+% \end{syntax}
+% Creates a switch <key>, which will be set to \texttt{true}.
+% \end{function}
+%
+% \begin{function}{
+% \keys_bool_set:nN|
+% \keys_bool_set:nnn|
+% }
+% \begin{syntax}
+% "\keys_bool_set:nN" <function> <boolean>
+% "\keys_bool_set:nnn" <function> <prefix> <name>
+% \end{syntax}
+% Uses <function> (either \texttt{set} or \texttt{gset}) to use
+% <value> in setting <boolean>. If a <name> is given, it is used to
+% construct a csname for the switch including the current <module>.
+% The <prefix> should be |l_| or |g_|.
+% \end{function}
+%
+% \begin{function}{
+% \keys_bool_set_inverse:n|
+% \keys_bool_set_inverse:nN|
+% \keys_bool_set_inverse:nnn|
+% }
+% \begin{syntax}
+% "\keys_bool_set:n" <switch>
+% "\keys_bool_set:nN" <function> <boolean>
+% "\keys_bool_set:nnn" <function> <prefix> <name>
+% \end{syntax}
+% Uses <function> (either \texttt{set} or \texttt{gset}) to use
+% <value> in setting <boolean>. If a <name> is given, it is used to
+% construct a csname for the switch including the current <module>.
+% The <prefix> should be |l_| or |g_|. The logic of the setting is
+% reversed compared to the \cs{keys_bool_set:} functions.
+% \end{function}
+%
+% \begin{function}{
+% \keys_check_exists:NN|
+% \keys_check_exists:cN
+%}
+% \begin{syntax}
+% "\keys_check_exists:NN" <var> <new cmd>
+% \end{syntax}
+% Checks that <var> exists and if not used <new cmd> to create it.
+% \end{function}
+%
+% \begin{function}{\keys_choice_create:n}
+% \begin{syntax}
+% "\keys_choice_create:n" <choice>
+% \end{syntax}
+% Creates <choice> as a sub-key of <key>.
+% \end{function}
+%
+% \begin{function}{\keys_choices_create:Nnn}
+% \begin{syntax}
+% "\keys_choices_create:Nnn" <expansion> <list> <code>
+% \end{syntax}
+% Takes a comma separated <list> and makes a <choice> as a sub-key of
+% the current <key> for each. The <choice> will execute <code>, which
+% is processed according to <expansion> (either \texttt{n} or
+% \texttt{x}). The <code> has access to \cs{l_keys_current_choice_tl}
+% and \cs{l_keys_current_choice_int}, which indicate the choice given.
+% \end{function}
+%
+%
+% \begin{function}{\keys_clear_properties:n}
+% \begin{syntax}
+% "\keys_clear_properties:n" <key>
+% \end{syntax}
+% Clears the internal properties of <key>.
+% \end{function}
+%
+% \begin{function}{\keys_default_add:}
+% \begin{syntax}
+% "\keys_default_add:"
+% \end{syntax}
+% If no value was given for the current key, and a default value is
+% available, copies the default into \cs{l_keys_current_value_toks}.
+% \end{function}
+%
+% \begin{function}{\keys_msg_new:nn}
+% \begin{syntax}
+% "\keys_msg_new:nn" <name> <text>
+% \end{syntax}
+% Creates a new message with <name> which produces <text>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_error:nn|
+% \keys_error:n|
+% }
+% \begin{syntax}
+% "\keys_error:nn" <name> <arg>
+% "\keys_error:n" <name>
+% \end{syntax}
+% Issues error <name>, with or without <argument>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_find_code_full:|
+% \keys_find_code_name:
+% }
+% \begin{syntax}
+% "\keys_find_code_full:"
+% "\keys_find_code_name:"
+% \end{syntax}
+% Find code to execute for the current fully qualified key
+% (\texttt{full}) or key name only (\texttt{name}).
+% \end{function}
+%
+% \begin{function}{\keys_if_cmd_exist:n / (TF)}
+% \begin{syntax}
+% "\keys_if_cmd_exist:nTF" <key> <true code>
+% ~~~~<false code>
+% \end{syntax}
+% Checks for the existence of a |._cmd:w| function for <key>.
+% \end{function}
+%
+% \begin{function}{\keys_if_exist:n / (TF)}
+% \begin{syntax}
+% "\keys_if_exist:nTF" <key> <true code>
+% ~~~~<false code>
+% \end{syntax}
+% Checks for the existence of a <key>.
+% \end{function}
+%
+% \begin{function}{\keys_if_value:n / (TF)}
+% \begin{syntax}
+% "\keys_if_value:nTF" <property> <true code> <false code>
+% \end{syntax}
+% Checks for the <property> switch of a <key> (typically
+% \texttt{required} or \texttt{forbidden}).
+% \end{function}
+%
+% \begin{function}{\keys_manage_internal:n}
+% \begin{syntax}
+% "\keys_manage_internal:n" <keyval list>
+% \end{syntax}
+% An internal ``recycling'' version of \cs{keys_manage_quick:n}, which
+% should not be called from outside.
+% \end{function}
+%
+% \begin{function}{
+% \keys_no_value_elt:n|
+% \keys_value_elt:nn
+% }
+% \begin{syntax}
+% "\keys_no_value_elt:n" <elt>
+% "\keys_value_elt:nn" <elt> <value>
+% \end{syntax}
+% Functions used by \pkg{l3keyval} for each <elt> of the <keyval
+% list> being processed.
+% \end{function}
+%
+% \begin{function}{\keys_parse:n}
+% \begin{syntax}
+% "\keys_parse:n" <keyval list>
+% \end{syntax}
+% Functions which actually parses <keyval list>.
+% \end{function}
+%
+% \begin{function}{\keys_parse_list:n}
+% \begin{syntax}
+% "\keys_parse_list:n" <keyval list>
+% \end{syntax}
+% Set up macro for \cs{keys_parse:n}.
+% \end{function}
+%
+% \begin{function}{
+% \keys_path_add:N|
+% \keys_path_add:w
+% }
+% \begin{syntax}
+% "\keys_path_add:N" <tl>
+% "\keys_path_add:w" <tlist> "\q_stop"
+% \end{syntax}
+% Adds a full path to key name stored in <tl> or given as <tlist>.
+% \end{function}
+%
+% \begin{function}{\keys_process_elt:nn}
+% \begin{syntax}
+% "\keys_process_elt:nn" <elt> <value>
+% \end{syntax}
+% Lead-off processor for converting <elt> into a fully-qualified
+% <key> and checking validity of <value>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_separate_path:|
+% \keys_separate_path:w
+% }
+% \begin{syntax}
+% "\keys_separate_path:"
+% "\keys_separate_path:w" / <path 1> / <path 2> "\q_stop"
+% \end{syntax}
+% Separates key into <path> and <key name>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_set:NN|
+% \keys_set:Nnn|
+% }
+% \begin{syntax}
+% "\keys_set:NN" <function> <variable>
+% "\keys_set:Nnn" <function> <prefix> <name>
+% \end{syntax}
+% Uses <function> to store <value> in <variable>. If a <name> is
+% given, it is used to construct a csname for the variable including
+% the current <module>. The <prefix> should be |l_| or |g_|.
+% \end{function}
+%
+% \begin{function}{
+% \keys_set_cmd:nn|
+% \keys_set_cmd:nx|
+% \keys_set_cmd:nNn|
+% \keys_set_cmd:nNx|
+% }
+% \begin{syntax}
+% "\keys_set_cmd:nn" <key> <code>
+% "\keys_set_cmd:nNn" <key> <number> <code>
+% \end{syntax}
+% Creates a |._cmd:w| function for <key>, with definition <code>. The
+% \texttt{N} variant can include <number> parameters.
+% \end{function}
+%
+% \begin{function}{
+% \keys_store:nn|
+% \keys_store:nx
+% }
+% \begin{syntax}
+% "\keys_store:nn" <key> <data>
+% \end{syntax}
+% Stores <data> in <key> function.
+% \end{function}
+%
+% \begin{function}{\keys_toks_set:Nn}
+% \begin{syntax}
+% "\keys_toks_set:Nn" <toks> <key>
+% \end{syntax}
+% Sets <toks> equal to the content of <key>
+% \end{function}
+%
+% \begin{function}{\keys_try:}
+% \begin{syntax}
+% "\keys_try:"
+% \end{syntax}
+% Attempt to execute a key, with no error if the key is unknown.
+% \end{function}
+%
+% \begin{function}{\keys_undefine:n}
+% \begin{syntax}
+% "\keys_undefine:n" <key>
+% \end{syntax}
+% Delete definition of <key>.
+% \end{function}
+%
+% \begin{function}{\keys_use:n}
+% \begin{syntax}
+% "\keys_use:n" <key>
+% \end{syntax}
+% Use definition of <key>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_use_cmd:n|
+% \keys_use_cmd:nn
+% }
+% \begin{syntax}
+% "\keys_use_cmd:n" <key>
+% "\keys_use_cmd:nn" <key> <arg>
+% \end{syntax}
+% Uses the |._cmd:w| function for <key>, passing <arg> if needed.
+% \end{function}
+%
+%\subsubsection{Internal properties}
+%
+% The internal key properties should not be accessed directly.
+%
+% \begin{function}{._cmd:w}
+% \begin{syntax}
+% \end{syntax}
+% The function which is executed for a key with \texttt{.code}.
+% \end{function}
+%
+% \begin{function}{._default_tl}
+% \begin{syntax}
+% \end{syntax}
+% Holds the default value for a key.
+% \end{function}
+%
+% \begin{function}{._forbidden_bool}
+% \begin{syntax}
+% \end{syntax}
+% Indicates that a value cannot be given for <key>.
+% \end{function}
+%
+% \begin{function}{._num_args_tl}
+% \begin{syntax}
+% \end{syntax}
+% For functions defined with \texttt{.code:Nn} and \texttt{.code:Nx},
+% contains the number of arguments the associated |._cmd:w| function
+% takes.
+% \end{function}
+%
+% \begin{function}{._required_bool}
+% \begin{syntax}
+% \end{syntax}
+% Indicates that <key> must have a value provided.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+%\subsection{Implementation}
+%
+% The usual preliminaries.
+% \begin{macrocode}
+%<*package>
+\ProvidesExplPackage
+ {\filename}{\filedate}{\fileversion}{\filedescription}
+% \end{macrocode}
+%
+%\subsubsection{Variables and contrasts}
+%
+%\begin{macro}{\c_keys_0_empty_tl}
+%\begin{macro}{\c_keys_1_empty_tl}
+%\begin{macro}{\c_keys_2_empty_tl}
+%\begin{macro}{\c_keys_3_empty_tl}
+%\begin{macro}{\c_keys_4_empty_tl}
+%\begin{macro}{\c_keys_5_empty_tl}
+%\begin{macro}{\c_keys_6_empty_tl}
+%\begin{macro}{\c_keys_7_empty_tl}
+%\begin{macro}{\c_keys_8_empty_tl}
+%\begin{macro}{\c_keys_9_empty_tl}
+% A set of empty arguments.
+% \begin{macrocode}
+\tl_new:cn { c_keys_0_empty_tl } {}
+\tl_new:cn { c_keys_1_empty_tl } { {} }
+\tl_new:cn { c_keys_2_empty_tl } { {} {} }
+\tl_new:cn { c_keys_3_empty_tl } { {} {} {} }
+\tl_new:cn { c_keys_4_empty_tl } { {} {} {} {} }
+\tl_new:cn { c_keys_5_empty_tl } { {} {} {} {} {} }
+\tl_new:cn { c_keys_6_empty_tl } { {} {} {} {} {} {} }
+\tl_new:cn { c_keys_7_empty_tl } { {} {} {} {} {} {} {} }
+\tl_new:cn { c_keys_8_empty_tl } { {} {} {} {} {} {} {} {} }
+\tl_new:cn { c_keys_9_empty_tl } { {} {} {} {} {} {} {} {} {} }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\c_keys_cs_prefix_tl}
+% First, the small number of constants needed are created. A prefix is
+% used to keep all of the actual key macros in one place.
+% \begin{macrocode}
+\tl_new:Nn \c_keys_cs_prefix_tl { keys-root }
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\c_keys_errors_path_tl}
+%\begin{macro}{\c_keys_properties_path_tl}
+%\begin{macro}{\c_keys_utilities_path_tl}
+% The locations of all of the keys used by \pkg{keys3} itself.
+% \begin{macrocode}
+\tl_new:Nn \c_keys_errors_path_tl { /keys/errors }
+\tl_new:Nn \c_keys_properties_path_tl { /keys/properties }
+\tl_new:Nn \c_keys_utilities_path_tl { /keys }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\c_keys_root_tl}
+% The key root should have a clear name; like all of the key macros,
+% this does not include the prefix.
+% \begin{macrocode}
+\tl_new:Nn \c_keys_root_tl { / }
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_choice_code_tl}
+% When making choices, the code for each key has to be stored.
+% \begin{macrocode}
+\tl_new:N \l_keys_choice_code_tl
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_current_choice_tl}
+%\begin{macro}{\l_keys_current_choice_int}
+% Multiple choices need some storage.
+% \begin{macrocode}
+\tl_new:N \l_keys_current_choice_tl
+\int_new:N \l_keys_current_choice_int
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_current_key_full_tl}
+%\begin{macro}{\l_keys_current_key_name_tl}
+% The current key name and the fully-qualified key are stored.
+% \begin{macrocode}
+\tl_new:N \l_keys_current_key_full_tl
+\tl_new:N \l_keys_current_default_tl
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_current_module_tl}
+% The module name of the current module is stored here.
+% \begin{macrocode}
+\tl_new:N \l_keys_current_module_tl
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_current_path_tl}
+%\begin{macro}{\l_keys_default_path_tl}
+%\begin{macro}{\l_keys_choice_path_tl}
+% The current and default paths can be stored as tls. The default path
+% is then initialised as the key root.
+% \begin{macrocode}
+\tl_new:N \l_keys_current_path_tl
+\tl_new:N \l_keys_default_path_tl
+\tl_set_eq:NN \l_keys_default_path_tl \c_keys_root_tl
+\tl_new:N \l_keys_choice_path_tl
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_current_value_toks}
+% The current value is stored in a token register.
+% \begin{macrocode}
+\toks_new:N \l_keys_current_value_toks
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_no_value_bool}
+% To indicate that no value has been given.
+% \begin{macrocode}
+\bool_new:N \l_keys_no_value_bool
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_success_bool}
+% A switch for trying keys.
+% \begin{macrocode}
+\bool_new:N \l_keys_success_bool
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\l_keys_tmpa_tl}
+% A scratch area.
+% \begin{macrocode}
+\tl_new:N \l_keys_tmpa_tl
+% \end{macrocode}
+%\end{macro}
+%
+%\subsubsection{Functions}
+%
+%\begin{macro}{\keys_manage:n}
+%\begin{macro}{\keys_manage_quick:n}
+%\begin{macro}{\keys_manage_internal:n}
+%\begin{macro}[aux]{\keys_manage_aux:nn}
+%\begin{macro}[aux]{\keys_manage_aux:Vn}
+% The main key management macros both call the auxiliary function after
+% setting up the parser. The expansion trick means a literal path is
+% sent to the later function, and so the default path can be redefined.
+% \begin{macrocode}
+\cs_new:Nn \keys_manage:n {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_removal_sanitize:n
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_manage_internal:n {#1}
+}
+\cs_new:Nn \keys_manage_quick:n {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_no_space_removal_no_sanitize:n
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_manage_internal:n {#1}
+}
+\cs_new:Nn \keys_manage_internal:n {
+ \keys_manage_aux:Vn \l_keys_default_path_tl {#1}
+}
+\cs_new:Nn \keys_manage_aux:nn {
+ \tl_set_eq:NN \l_keys_default_path_tl \c_keys_root_tl
+ \keys_parse_list:n {#2}
+ \tl_set:Nn \l_keys_default_path_tl {#1}
+}
+\cs_generate_variant:Nn \keys_manage_aux:nn { Vn }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\begin{macro}{\keys_manage:nn}
+%\begin{macro}{\keys_manage_quick:nn}
+%\begin{macro}[aux]{\keys_manage_aux:nnn}
+%\begin{macro}[aux]{\keys_manage_aux:Vnn}
+% This version uses the same tricks but includes the path as a second
+% argument. When setting lots of keys separately, this is a little
+% faster than the key-based method.
+% \begin{macrocode}
+\cs_new:Nn \keys_manage:nn {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_removal_sanitize:n
+ \keys_manage_aux:Vnn \l_keys_default_path_tl {#1} {#2}
+}
+\cs_new:Nn \keys_manage_quick:nn {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_no_removal_no_sanitize:n
+ \keys_manage_aux:Vnn \l_keys_default_path_tl {#1} {#2}
+}
+\cs_new:Nn \keys_manage_aux:nnn {
+ \tl_set:Nn \l_keys_default_path_tl {#2}
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_parse_list:n {#3}
+ \tl_set:Nn \l_keys_default_path_tl {#1}
+}
+\cs_generate_variant:Nn \keys_manage_aux:nnn { Vnn }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\subsubsection{Internal functions}
+%
+%\begin{macro}{\keys_bool_new:n}
+% To create a new switch, which will be true (as the existence of the
+% switch is the flag here).
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_bool_new:n {
+ \bool_new:c { \c_keys_cs_prefix_tl #1 }
+ \bool_set_true:c { \c_keys_cs_prefix_tl #1 }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_bool_set:nN}
+%\begin{macro}{\keys_bool_set:nnn}
+% Boolean keys are created by using the fact that only \texttt{true} and
+% \texttt{false} give the right result when looking for a setting
+% function. A default is also set, so that the key name alone can be
+% given.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_bool_set:nN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ \cs_if_exist:cTF { bool_ #1 _ ##1 :N } {
+ \use:c { bool_ #1 _ ##1 :N } #2
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_bool_set:nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N \cs_if_exist:cTF { bool_ #1 _ ##1 :c } {
+ \exp_not:N \use:c { bool_ #1 _ ##1 :c }
+ { #2 \l_keys_current_module_tl #3 _bool }
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_bool_set_inverse:n}
+%\begin{macro}{\keys_bool_set_inverse:nN}
+%\begin{macro}{\keys_bool_set_inverse:nnn}
+% To set keys with reversed logic, the basics can be done in the same
+% way as for the standard switches. The only thing needed is a
+% reversal of \texttt{true}/\texttt{false}.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_bool_set_inverse:n {
+ \tl_if_eq:nnTF {#1} { true } { false } { true }
+}
+\cs_new_nopar:Nn \keys_bool_set_inverse:nN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ \cs_if_exist:cTF { bool_ #1 _ ##1 :N } {
+ \use:c { bool_ #1 _ \keys_bool_set_inverse:n {##1} :N } #2
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_bool_set_inverse:nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N \cs_if_exist:cTF { bool_ #1 _ ##1 :c } {
+ \exp_not:N \use:c {
+ bool_ #1 _ \exp_not:N \keys_bool_set_inverse:n {##1} :c
+ } { #2 \l_keys_current_module_tl #3 _bool }
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_check_exists:NN}
+%\begin{macro}{\keys_check_exists:cN}
+% A check function to avoid problems later.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_check_exists:NN {
+ \cs_if_exist:NF #1 {
+ #2 #1
+ }
+}
+\cs_generate_variant:Nn \keys_check_exists:NN { cN }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_choices_create:Nnn}
+%\begin{macro}[aux]{\keys_choices_create_aux:n}
+%\begin{macro}[aux]{\keys_choices_create_aux:x}
+%\begin{macro}{\keys_choice_create:n}
+% When making multiple choices, the code for each choice is the same.
+% Only the path and counter need to be altered.
+% \begin{macrocode}
+\cs_new:Nn \keys_choices_create:Nnn {
+ \tl_set_eq:NN \l_keys_choice_path_tl \l_keys_current_path_tl
+ \int_zero:N \l_keys_current_choice_int
+ \use:c { keys_choices_create_aux: #1 } {#3}
+ \clist_map_function:nN {#2} \keys_choice_create:n
+ \keys_manage_internal:n { \l_keys_choice_path_tl /.expects_choice: }
+}
+\cs_new:Nn \keys_choices_create_aux:n {
+ \tl_set:Nn \l_keys_choice_code_tl { \exp_not:n {#1} }
+}
+\cs_new:Nn \keys_choices_create_aux:x {
+ \tl_set:Nn \l_keys_choice_code_tl {#1}
+}
+\cs_new_nopar:Nn \keys_choice_create:n {
+ \int_incr:N \l_keys_current_choice_int
+ \keys_parse_list:n {
+ \l_keys_choice_path_tl / #1 /.code:x = {
+ \exp_not:n { \int_set:Nn \l_keys_current_choice_int }
+ { \int_use:N \l_keys_current_choice_int }
+ \exp_not:n { \tl_set:Nn \l_keys_current_choice_tl } {#1}
+ \l_keys_choice_code_tl
+ }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_clear_properties:n}
+% To avoid problem on redefinition, all properties are removed.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_clear_properties:n {
+ \keys_undefine:n { #1 /._cmd:w }
+ \keys_undefine:n { #1 /._default_tl }
+ \keys_undefine:n { #1 /._forbidden_bool }
+ \keys_undefine:n { #1 /._num_args_tl }
+ \keys_undefine:n { #1 /._required_bool }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_default_add:}
+% Copies the default value to the current one if needed.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_default_add: {
+ \bool_if:NT \l_keys_no_value_bool {
+ \keys_if_exist:nT
+ { \l_keys_current_key_full_tl /._default_tl } {
+ \keys_toks_set:Nn \l_keys_current_value_toks
+ { \l_keys_current_key_full_tl /._default_tl }
+ \bool_set_false:N \l_keys_no_value_bool
+ }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_find_code_full:}
+%\begin{macro}{\keys_find_code_name:}
+% Two functions to find something to process the key value given. First,
+% a search is made for either a command property for the key, or a
+% function for the key itself. If that fails, the generic handlers are
+% used after separating out the key name and key path.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_find_code_full: {
+ \keys_if_cmd_exist:nTF { \l_keys_current_key_full_tl } {
+ \keys_use_cmd:n { \l_keys_current_key_full_tl }
+ }{
+ \keys_if_exist:nTF { \l_keys_current_key_full_tl } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_use:n { \l_keys_current_key_full_tl }
+ }{
+ \keys_store:nx { \l_keys_current_key_full_tl }
+ { \toks_use:N \l_keys_current_value_toks }
+ }
+ }{
+ \keys_find_code_name:
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_find_code_name: {
+ \keys_separate_path:
+ \keys_if_cmd_exist:nTF
+ { \c_keys_properties_path_tl / \l_keys_current_key_name_tl } {
+ \keys_use_cmd:n
+ {\c_keys_properties_path_tl / \l_keys_current_key_name_tl}
+ }{
+ \keys_if_cmd_exist:nTF
+ { \l_keys_current_path_tl / unknown } {
+ \keys_use_cmd:n { \l_keys_current_path_tl / unknown }
+ }{
+ \keys_error:n { unknown~key }
+ }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}[TF]{\keys_if_cmd_exist:n}
+% A dedicated check for the |._cmd:w| property key.
+% \begin{macrocode}
+\prg_new_conditional:Nnn \keys_if_cmd_exist:n {TF,T,F} {
+ \if_cs_exist:w \c_keys_cs_prefix_tl #1 /._cmd:w \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}[TF]{\keys_if_exist:n}
+% Check if a key exists without adding to the hash table.
+% \begin{macrocode}
+\prg_new_conditional:Nnn \keys_if_exist:n {TF,T,F} {
+ \if_cs_exist:w \c_keys_cs_prefix_tl #1 \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}[TF]{\keys_if_value:n}
+% Used for required and forbidden values.
+% \begin{macrocode}
+\prg_new_conditional:Nnn \keys_if_value:n {TF,T,F} {
+ \if_cs_exist:w
+ \c_keys_cs_prefix_tl \l_keys_current_key_full_tl /._ #1 _bool
+ \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_no_value_elt:n}
+%\begin{macro}{\keys_value_elt:nn}
+% The two functions passed to \pkg{l3keyval} to actually act on each
+% key found.
+% \begin{macrocode}
+\cs_new:Nn \keys_no_value_elt:n {
+ \bool_set_true:N \l_keys_no_value_bool
+ \keys_process_elt:nn {#1} { }
+}
+\cs_new:Nn \keys_value_elt:nn {
+ \bool_set_false:N \l_keys_no_value_bool
+ \keys_process_elt:nn {#1} {#2}
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_parse:n}
+% The macro used to actually process the key--value input is taken from
+% \pkg{l3keyval}. There are two possible options, and so at this
+% stage the macro is simply reserved.
+% \begin{macrocode}
+\cs_new:Nn \keys_parse:n { \ERROR }
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_parse_list:n}
+% All of the management macros call this common parser. First, the key
+% processing macros are defined, then the appropriate parser is called.
+% \begin{macrocode}
+\cs_new:Nn \keys_parse_list:n {
+ \cs_set_eq:NN \KV_key_value_elt:nn \keys_value_elt:nn
+ \cs_set_eq:NN \KV_key_no_value_elt:n \keys_no_value_elt:n
+ \keys_parse:n {#1}
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_path_add:N}
+%\begin{macro}{\keys_path_add:w}
+%\begin{macro}[aux]{\keys_path_add_aux:w}
+% The code to check for a path looks for a |/| at the start of the key.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_path_add:N {
+ \exp_after:wN \keys_path_add:w #1 \q_stop
+}
+\cs_new_nopar:Npn \keys_path_add:w {
+ \exp_after:wN \peek_meaning:NTF \c_keys_root_tl {
+ \use_none_delimit_by_q_stop:w
+ }{
+ \keys_path_add_aux:w
+ }
+}
+\cs_new_nopar:Npn \keys_path_add_aux:w #1 \q_stop {
+ \tl_set:Nx \l_keys_current_key_full_tl {\l_keys_default_path_tl #1}
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_process_elt:nn}
+%\begin{macro}[aux]{\keys_process_elt_aux:}
+% The key processor starts by storing the given key name and value, and
+% adding a path to the former if necessary. There is then potentially a
+% need to fill in a default value before checking for required or
+% forbidden values.
+% \begin{macrocode}
+\cs_new:Nn \keys_process_elt:nn {
+ \tl_set:Nx \l_keys_current_key_full_tl {#1}
+ \toks_set:Nn \l_keys_current_value_toks {#2}
+ \keys_path_add:N \l_keys_current_key_full_tl
+ \keys_default_add:
+ \keys_if_value:nTF { required } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_error:n { value~required }
+ }{
+ \keys_process_elt_aux:
+ }
+ }{
+ \keys_process_elt_aux:
+ }
+}
+\cs_new:Nn \keys_process_elt_aux: {
+ \keys_if_value:nTF { forbidden } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_find_code_full:
+ }{
+ \keys_error:nn { value~forbidden }
+ { \toks_use:N \l_keys_current_value_toks }
+ }
+ }{
+ \keys_find_code_full:
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_separate_path:}
+%\begin{macro}{\keys_separate_path:w}
+% A simple piece of recursion to find the key name and path.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_separate_path: {
+ \tl_clear:N \l_keys_current_path_tl
+ \exp_after:wN \keys_separate_path:w \l_keys_current_key_full_tl
+ / \q_stop
+}
+\cs_new_nopar:Npn \keys_separate_path:w / #1 / #2 \q_stop {
+ \tl_if_empty:nTF {#2} {
+ \tl_set:Nn \l_keys_current_key_name_tl {#1}
+ }{
+ \tl_put_right:Nn \l_keys_current_path_tl { / #1 }
+ \keys_separate_path:w /#2 \q_stop
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_set:NN}
+%\begin{macro}{\keys_set:Nnn}
+% Storage of data in outside of keys.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_set:NN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ #1 #2 {##1}
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_set:Nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N #1 { #2 \l_keys_current_module_tl #3 } {##1}
+ }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_set_cmd:nn}
+%\begin{macro}{\keys_set_cmd:nx}
+%\begin{macro}{\keys_set_cmd:nNn}
+%\begin{macro}{\keys_set_cmd:nNx}
+%\begin{macro}{._cmd:w}
+%\begin{macro}{._num_args_tl}
+% Creation of the key |._cmd:w| macros happens here. For the
+% multiple-argument versions, the number of arguments is stored for
+% use later on.
+% \begin{macrocode}
+\cs_new:Nn \keys_set_cmd:nn {
+ \keys_clear_properties:n {#1}
+ \cs_set:cpn { \c_keys_cs_prefix_tl #1 /._cmd:w } ##1 {#2}
+}
+\cs_new:Nn \keys_set_cmd:nx {
+ \keys_clear_properties:n {#1}
+ \cs_set:cpx { \c_keys_cs_prefix_tl #1 /._cmd:w } ##1 {#2}
+}
+\cs_new:Nn \keys_set_cmd:nNn {
+ \keys_clear_properties:n {#1}
+ \cs_generate_from_arg_count:cNnn { \c_keys_cs_prefix_tl #1 /._cmd:w }
+ \cs_set:Npn #2 {#3}
+ \keys_store:nn { #1 /._num_args_tl} {#2}
+}
+\cs_new:Nn \keys_set_cmd:nNx {
+ \keys_clear_properties:n {#1}
+ \cs_generate_from_arg_count:cNnn { \c_keys_cs_prefix_tl #1 /._cmd:w }
+ \cs_set:Npx #2 {#3}
+ \keys_store:nn { #1 /._num_args_tl} {#2}
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_store:nn}
+%\begin{macro}{\keys_store:nx}
+% Direct storage of data in keys.
+% \begin{macrocode}
+\cs_new:Nn \keys_store:nn {
+ \tl_set:cn { \c_keys_cs_prefix_tl #1 } {#2}
+}
+\cs_new:Nn \keys_store:nx {
+ \tl_set:cx { \c_keys_cs_prefix_tl #1 } {#2}
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{\keys_toks_set:Nn}
+% Sets a toks to the content of a key.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_toks_set:Nn {
+ \exp_args:NNv \toks_set:Nn #1 { \c_keys_cs_prefix_tl #2 }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_try:}
+% A function to look for the |._cmd:w| property of a key, and executes
+% it if found.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_try: {
+ \bool_set_false:N \l_keys_success_bool
+ \keys_if_cmd_exist:nT { \l_keys_current_path_tl } {
+ \bool_set_true:N \l_keys_success_bool
+ \bool_if:NT \l_keys_no_value_bool {
+ \keys_if_exist:nT
+ { \l_keys_current_path_tl /._default_tl } {
+ \keys_toks_set:Nn \l_keys_current_value_toks
+ { \l_keys_current_path_tl /._default_tl }
+ }
+ }
+ \cs_set_eq:NN \l_keys_current_key_full_tl \l_keys_current_path_tl
+ \keys_use_cmd:n { \l_keys_current_path_tl }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_undefine:n}
+% To remove a key.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_undefine:n {
+ \cs_set_eq:cN { \c_keys_cs_prefix_tl #1 } \c_undefined
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_use:n}
+% Use whatever is stored in a key.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_use:n {
+ \use:c { \c_keys_cs_prefix_tl #1 }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_use_cmd:n}
+%\begin{macro}{\keys_use_cmd:nn}
+%\begin{macro}[aux]{\keys_use_cmd_aux:nn}
+%\begin{macro}[aux]{\keys_use_cmd_aux:w}
+% Some care is needed when using command keys. For commands with
+% multiple arguments a check is made in case none were given, and if
+% so a series of empty values is given instead.
+% \begin{macrocode}
+\cs_new_nopar:Nn \keys_use_cmd:n {
+ \exp_args:Nno \keys_use_cmd:nn {#1}
+ { \toks_use:N \l_keys_current_value_toks }
+}
+\cs_new_nopar:Nn \keys_use_cmd:nn {
+ \keys_if_exist:nTF { #1 /._num_args_tl } {
+ \keys_use_cmd_aux:nn {#1} {#2}
+ }{
+ \keys_use:n {#1/._cmd:w} {#2}
+ }
+}
+\cs_new_nopar:Nn \keys_use_cmd_aux:nn {
+ \tl_if_empty:nTF {#2} {
+ \cs_set:Npn \keys_use_cmd_aux:w {
+ \keys_use:n { #1 /._cmd:w }
+ }
+ \exp_after:wN \exp_after:wN \exp_after:wN \keys_use_cmd_aux:w
+ \cs:w
+ c_keys_ \keys_use:n { #1 /._num_args_tl } _empty_tl
+ \cs_end:
+ }{
+ \keys_use:n { #1 /._cmd:w } #2
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\subsubsection{Error handling code}
+%
+% The \LaTeX3 approach is to have named errors called separately and
+% defined separately. To make life a little easier here, some custom
+% functions are used to keep repetition down.
+%
+%\begin{macro}{\keys_msg_new:nn}
+% To create new error messages, a utility function is created.
+% \begin{macrocode}
+\cs_new_nopar:Npn \keys_msg_new:nn #1 {
+ \msg_new:nnn { keys } {#1}
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_error:n}
+%\begin{macro}{\keys_error:nn}
+% Utilities for using errors, which always have
+% \cs{l_keys_current_key_full_tl} as the first argument.
+% \begin{macrocode}
+\cs_new_protected_nopar:Nn \keys_error:nn {
+ \msg_error:nnxx { keys } {#1} { \l_keys_current_key_full_tl } {#2}
+}
+\cs_new_protected_nopar:Nn \keys_error:n {
+ \msg_error:nnx { keys } {#1} { \l_keys_current_key_full_tl }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+% \begin{macrocode}
+\keys_msg_new:nn
+ { unknown~key }
+ {The key `#1' is unknown and is being ignored.}
+\keys_msg_new:nn
+ { value~required }
+ {The key `#1' requires a value\\%
+ and is being ignored.}
+\keys_msg_new:nn
+ { value~forbidden }
+ {The key `#1' cannot taken a value:\\%
+ the given input `#2' is being ignored.}
+\keys_msg_new:nn
+ { Boolean~expected }
+ {Key `#1' takes the Boolean values\\%
+ `true' and `false'~only.\\%
+ The given value `#2' is being ignored.}
+\keys_msg_new:nn
+ { not_boolean }
+ {Key `#1' is not a Boolean key: you cannot create a complement.}
+\keys_msg_new:nn
+ { unknown~choice }
+ {Choice `#2' unknown for key `#1':\\%
+ the key is being ignored.}
+% \end{macrocode}
+%
+%\subsubsection{Property definitions}
+%
+%\begin{macro}{.code:n}
+%\begin{macro}{.code:Nn}
+% The \texttt{.code:n} and \texttt{.code:Nn} properties have to be
+% defined directly.
+% \begin{macrocode}
+\keys_set_cmd:nn { \c_keys_properties_path_tl /.code:n } {
+ \keys_set_cmd:nn { \l_keys_current_path_tl } {#1}
+}
+\keys_set_cmd:nNn { \c_keys_properties_path_tl /.code:Nn } 2 {
+ \keys_set_cmd:nNn { \l_keys_current_path_tl } #1 {#2}
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+% The remaining definitions can all be carried out using the package
+% itself. As category codes and spaces are not an issue here, the
+% |_quick| version of \cs{keys_manage} is used.
+%
+% First, an error is created for unknown keys: this is done early to
+% catch any internal errors.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_errors_path_tl /unknown/.code:n = {
+ \keys_error:n { unknown~key }
+ }
+}
+% \end{macrocode}
+%
+%\begin{macro}{.code:x}
+%\begin{macro}{.code:Nx}
+% Fully-expanded versions of the basic \texttt{.code} properties.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.code:x/.code:n = {
+ \keys_set_cmd:nx { \l_keys_current_path_tl } {#1}
+ },
+ \c_keys_properties_path_tl /.code:Nx/.code:Nn = 2 {
+ \keys_set_cmd:nNx { \l_keys_current_path_tl } {#1} {#2}
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.cd:}
+% The change-directory property simply alters the value of the default
+% path.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.cd:/.code:n = {
+ \tl_set:Nx \l_keys_default_path_tl { \l_keys_current_path_tl / }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{.value_forbidden:}
+%\begin{macro}{._required_bool}
+%\begin{macro}{.value_required:}
+%\begin{macro}{._forbidden_bool}
+% Values are required or forbidden by creating the appropriate flags.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.value_required:/.code:n = {
+ \keys_bool_new:n { \l_keys_current_path_tl /._required_bool }
+ \keys_undefine:n { \l_keys_current_path_tl /._forbidden_bool }
+ },
+ \c_keys_properties_path_tl /.value_forbidden:/.code:n = {
+ \keys_bool_new:n { \l_keys_current_path_tl /._forbidden_bool }
+ \keys_undefine:n { \l_keys_current_path_tl /._required_bool }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.default:n}
+%\begin{macro}{._default_tl}
+% The default value for a key is stored in the |._default_tl|
+% private property.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.default:n/.code:n = {
+ \keys_store:nn { \l_keys_current_path_tl /._default_tl } {#1}
+ \keys_undefine:n { \l_keys_current_path_tl /._required_bool }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.tl_set:N}
+%\begin{macro}{.tl_set_x:N}
+%\begin{macro}{.tl_gset:N}
+%\begin{macro}{.tl_gset_x:N}
+% Storage of the value in a tl, as given or expanded, local or global.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.tl_set:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.tl_set_x:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_set:Nx #1
+ },
+ \c_keys_properties_path_tl /.tl_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.tl_gset_x:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_gset:Nx #1
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.int_set:N}
+%\begin{macro}{.int_gset:N}
+%\begin{macro}{.skip_set:N}
+%\begin{macro}{.skip_gset:N}
+%\begin{macro}{.toks_set:N}
+%\begin{macro}{.toks_gset:N}
+% For int, skip and toks storage, no expansion to worry about.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.int_set:N/.code:n = {
+ \keys_check_exists:NN #1 \int_new:N
+ \keys_set:NN \int_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.int_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \int_new:N
+ \keys_set:NN \int_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.skip_set:N/.code:n = {
+ \keys_check_exists:NN #1 \skip_new:N
+ \keys_set:NN \skip_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.skip_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \skip_new:N
+ \keys_set:NN \skip_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.toks_set:N/.code:n = {
+ \keys_check_exists:NN #1 \toks_new:N
+ \keys_set:NN \toks_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.toks_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \toks_new:N
+ \keys_set:NN \toks_gset:Nn #1
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+% The prefix for the current module is needed when storing material
+% by csname.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_utilities_path_tl /current_module:n/.code:n = {
+ \tl_if_empty:nTF {#1} {
+ \tl_clear:N \l_keys_current_module_tl
+ }{
+ \tl_set:Nn \l_keys_current_module_tl { #1 _ }
+ }
+ }
+}
+% \end{macrocode}
+%
+%\begin{macro}{.tl_set:n}
+%\begin{macro}{.tl_set_x:n}
+%\begin{macro}{.tl_gset:n}
+%\begin{macro}{.tl_gset_x:n}
+%\begin{macro}{.int_set:n}
+%\begin{macro}{.int_gset:n}
+%\begin{macro}{.skip_set:n}
+%\begin{macro}{.skip_gset:n}
+%\begin{macro}{.toks_set:n}
+%\begin{macro}{.toks_gset:n}
+% With the module available, storage properties that only need the
+% unique part of the variable name are created.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.tl_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_set:cn { l_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_set_x:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_set:cx { l_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_gset:cn { g_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_gset_x:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_gset:cx { g_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.int_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _int } \int_new:N
+ \keys_set:Nnn \int_set:cn { l_ } { #1 _int }
+ },
+ \c_keys_properties_path_tl /.int_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _int } \int_new:N
+ \keys_set:Nnn \int_gset:cn { g_ } { #1 _int }
+ },
+ \c_keys_properties_path_tl /.skip_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _skip } \skip_new:N
+ \keys_set:Nnn \skip_set:cn { l_ } { #1 _skip }
+ },
+ \c_keys_properties_path_tl /.skip_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _skip } \skip_new:N
+ \keys_set:Nnn \skip_gset:cn { g_ } { #1 _skip }
+ },
+ \c_keys_properties_path_tl /.toks_set:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _toks } \toks_new:N
+ \keys_set:Nnn \toks_set:cn { l_ } { #1 _toks }
+ },
+ \c_keys_properties_path_tl /.toks_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _toks } \toks_new:N
+ \keys_set:Nnn \toks_gset:cn { g_ } { #1 _toks }
+ },
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.bool_set:N}
+%\begin{macro}{.bool_gset:N}
+%\begin{macro}{.bool_set:n}
+%\begin{macro}{.bool_gset:n}
+% The properties for switches look similar, although internally things
+% are rather different.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.bool_set:N/.code:n = {
+ \keys_bool_set:nN { set } #1
+ },
+ \c_keys_properties_path_tl /.bool_gset:N/.code:n = {
+ \keys_bool_set:nN { gset } #1
+ },
+ \c_keys_properties_path_tl /.bool_set:n/.code:n = {
+ \keys_bool_set:nnn { set } { l_ } { #1 }
+ },
+ \c_keys_properties_path_tl /.bool_gset:n/.code:n = {
+ \keys_bool_set:nnn { gset } { g_ } { #1 }
+ },
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.bool_set_inverse:N}
+%\begin{macro}{.bool_gset_inverse:N}
+%\begin{macro}{.bool_set_inverse:n}
+%\begin{macro}{.bool_gset_inverse:n}
+% The inverse versions of the switches are handled in the same way
+% here.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.bool_set_inverse:N/.code:n = {
+ \keys_bool_set_inverse:nN { set } #1
+ },
+ \c_keys_properties_path_tl /.bool_gset_inverse:N/.code:n = {
+ \keys_bool_set_inverse:nN { gset } #1
+ },
+ \c_keys_properties_path_tl /.bool_set_inverse:n/.code:n = {
+ \keys_bool_set_inverse:nnn { set } { l_ } { #1 }
+ },
+ \c_keys_properties_path_tl /.bool_gset_inverse:n/.code:n = {
+ \keys_bool_set_inverse:nnn { gset } { g_ } { #1 }
+ },
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.expects_choice:}
+% Multiple choice keys are created by searching sub-keys. So the
+% code to make a key into a multiple choice is quite simple.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.expects_choice:/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.cd:,
+ .code:n = {
+ \tl_set:Nn \l_keys_current_choice_tl {##1}
+ \int_zero:N \l_keys_current_choice_int
+ \exp_args:No \keys_parse_list:n
+ { \l_keys_current_key_full_tl / ##1 }
+ },
+ unknown/.code:n = {
+ \keys_error:nn { unknown~choice }
+ { \l_keys_current_key_name_tl }
+ }
+ }
+ },
+}
+% \end{macrocode}
+%\end{macro}
+%\begin{macro}{.create_choices:nn}
+%\begin{macro}{.create_choices:nx}
+% Creating choices as a block.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.create_choices:nn/.code:Nn = 2 {
+ \keys_choices_create:Nnn n {#1} {#2}
+ },
+ \c_keys_properties_path_tl /.create_choices:nx/.code:Nn = 2 {
+ \keys_choices_create:Nnn x {#1} {#2}
+ },
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.use_keys:n}
+%\begin{macro}{.use_keys:x}
+%\begin{macro}{.use_keys:Nn}
+%\begin{macro}{.use_keys:Nx}
+% Keys calling other keys is actually quite easy.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.use_keys:n/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:n = { \keys_parse_list:n {#1} }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:x/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:x = {
+ \exp_not:N \keys_parse_list:n {#1}
+ }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:Nn/.code:Nn = 2 {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:Nn = #1 { \keys_parse_list:n {#2} }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:Nx/.code:Nn = 2 {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:Nx =
+ #1 { \exp_not:N \keys_parse_list:n {#2} }
+ }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.try:n}
+%\begin{macro}{.retry:n}
+% For attempting to set keys without assuming they exist.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.try:n/.code:n = { \keys_try: },
+ \c_keys_properties_path_tl /.retry:n/.code:n = {
+ \bool_if:NF \l_keys_success_bool { \keys_try: }
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+%\begin{macro}{.show_code:}
+%\begin{macro}{.show_key:}
+% Finally, two keys for debugging problems.
+% \begin{macrocode}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.show_code:/.code:n = {
+ \cs_show:c {
+ \c_keys_cs_prefix_tl \l_keys_current_path_tl /._cmd:w
+ }
+ },
+ \c_keys_properties_path_tl /.show_key:/.code:n = {
+ \cs_show:c { \c_keys_cs_prefix_tl \l_keys_current_path_tl }
+ }
+}
+%</package>
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+% \end{implementation}
diff --git a/Master/texmf-dist/source/latex/keys3/keys3.ins b/Master/texmf-dist/source/latex/keys3/keys3.ins
new file mode 100644
index 00000000000..30b04080e3a
--- /dev/null
+++ b/Master/texmf-dist/source/latex/keys3/keys3.ins
@@ -0,0 +1,89 @@
+%%
+%% This is file `keys3.ins',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% keys3.dtx (with options: `install')
+%% ---------------------------------------------------------------
+%% The keys3 package --- Key management for LaTeX3
+%% Maintained by Joseph Wright
+%% E-mail: joseph.wright@morningstar2.co.uk
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% ---------------------------------------------------------------
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+---------------------------------------------------------------
+The keys3 package --- Key management for LaTeX3
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+EXPERIMENTAL CODE
+
+Do not distribute this file without also distributing the
+source files specified above.
+
+Do not distribute a modified version of this file.
+
+\endpreamble
+\postamble
+
+Copyright (C) 2008-2009 by
+ Joseph Wright <joseph.wright@morningstar2.co.uk>
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Joseph Wright.
+
+This work consists of the file keys3.dtx
+ and the derived files keys3.pdf,
+ keys3.sty and
+ keys3.ins.
+
+\endpostamble
+\usedir{tex/latex/keys3}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+\endbatchfile
+%%
+%% Copyright (C) 2008-2009 by
+%% Joseph Wright <joseph.wright@morningstar2.co.uk>
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This work is "maintained" (as per LPPL maintenance status) by
+%% Joseph Wright.
+%%
+%% This work consists of the file keys3.dtx
+%% and the derived files keys3.pdf,
+%% keys3.sty and
+%% keys3.ins.
+%%
+%%
+%% End of file `keys3.ins'.
diff --git a/Master/texmf-dist/source/latex/keys3/keys3opts2.dtx b/Master/texmf-dist/source/latex/keys3/keys3opts2.dtx
new file mode 100644
index 00000000000..5aa9e8cce08
--- /dev/null
+++ b/Master/texmf-dist/source/latex/keys3/keys3opts2.dtx
@@ -0,0 +1,256 @@
+%\iffalse
+% !TEX program = pdflatex
+% !TEX encoding = ISO-8859-1
+%<*internal>
+\iffalse
+%</internal>
+%<*readme>
+---------------------------------------------------------------
+The keys3opts2 package --- Using keys3 for LaTeX2e options
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+The keys3 package is an experimental implementation of key
+management for LaTeX3. The entire package is written in
+expl3 code rather than in traditional TeX/LaTeX. The
+keys3opts2 package provides a mechanism to allow load-time
+option processed using keys3 in LaTeX2e.
+
+%</readme>
+%<*internal>
+\fi
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+%</internal>
+%<*install>
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+---------------------------------------------------------------
+The keys3opts2 package --- Using keys3 for LaTeX2e options
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+EXPERIMENTAL CODE
+
+Do not distribute this file without also distributing the
+source files specified above.
+
+Do not distribute a modified version of this file.
+
+\endpreamble
+\postamble
+
+Copyright (C) 2008-2009 by
+ Joseph Wright <joseph.wright@morningstar2.co.uk>
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Joseph Wright.
+
+This work consists of the file keys3opts2.dtx
+ and the derived files keys3opts2.pdf,
+ keys3opts2.sty and
+ keys3opts2.ins.
+
+\endpostamble
+\usedir{tex/latex/keys3}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usedir{source/latex/keys3}
+\generate{
+ \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%</internal>
+%<*driver|package>
+\RequirePackage{l3names}
+\GetIdInfo$Id: keys3opts2.dtx 93 2009-05-03 20:34:46Z josephwright $
+ {Using keys3 for LaTeX2e options}
+%</driver|package>
+%\fi
+%\iffalse
+%<*driver>
+%\fi
+\ProvidesFile{\filename.\filenameext}
+ [\filedate\space v\fileversion\space\filedescription]
+%\iffalse
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\filename.\filenameext}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \textsf{keys3opts2} package^^A
+% \thanks{This file has version number \fileversion, last
+% revised \filedate.}\\
+% Using \textsf{keys3} for \LaTeXe\ options}
+% \author{Joseph Wright^^A
+% \thanks{E-mail: joseph.wright@morningstar2.co.uk}}
+% \date{\filedate}
+% \maketitle
+%
+%\section{Package options}
+%
+% The methods that \LaTeX3 will use to process package options are not
+% clear at the present time. However, in order to fully test the
+% \pkg{expl3} bundle fully, it is useful to be able to work with
+% the \LaTeXe\ system but using \LaTeX3 methods as far as possible.
+%
+% The \pkg{keys3} package provides an experimental method for
+% managing key--value input using \pkg{expl3} syntax. It is
+% self-contained and does not use any \LaTeXe methods. On the other
+% hand, \pkg{keys3opts2} is intended as a ``bridge'' between the
+% \LaTeXe\ kernel and \textsf{keys3}. It allows keys to be created
+% using \pkg{keys3} and processed by \LaTeXe\ as package or class
+% options.
+%
+%\subsection{functions}
+%
+% \begin{function}{\keys_options_ltx:n}
+% \begin{syntax}
+% "\keys_options_ltx:n" <module>
+% \end{syntax}
+% The only macro for outside use. Processes the current option list
+% using the currently defined keys, checking if they exist for
+% <module>.
+% \end{function}
+%
+% \begin{function}{
+% \keys_options_ltx_cls:n|
+% \keys_options_ltx_pkg:n
+% }
+% \begin{syntax}
+% "\keys_options_ltx_cls:n" <option>
+% \end{syntax}
+% Helper macros for processing class and package options,
+% respectively.
+% \end{function}
+%
+% \begin{function}{\keys_options_ltx_strip_eq:w}
+% \begin{syntax}
+% "\keys_options_ltx_strip_eq:w" <option> "\q_stop"
+% \end{syntax}
+% Used to strip ``\texttt{=}'' out of options.
+% \end{function}
+%
+% \begin{variable}{\l_keys_options_ltx_clist}
+% Storage area for option list.
+% \end{variable}
+%
+%\subsection{Implementation}
+%
+% Almost everything here is based heavily on \pkg{pgfopts}, which is
+% itself a port of the appropriate code from \pkg{kvoptions} to work
+% with \pkg{pgfkeys}.
+%
+% As this package is an add-on to \pkg{keys3}, the same internal
+% module name (|\keys_|) is used here as in the main package. The lead
+% off as usual.
+% \begin{macrocode}
+%<*package>
+\ProvidesExplPackage
+ {\filename}{\filedate}{\fileversion}{\filedescription}
+\RequirePackage{keys3}
+% \end{macrocode}
+%
+%\begin{macro}{\l_keys_options_ltx_clist}
+% A simple storage area.
+% \begin{macrocode}
+\clist_new:N \l_keys_options_ltx_clist
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_options_ltx:n}
+% As this is a minimal testing package, a lot of the complexity of the
+% normal \LaTeX\ options is ignored. Here, the package insists a path
+% is given for the keys. First, a path may need to be added to the
+% argument given. The path we need to process is then stored in
+% \cs{l_keys_current_key_tl}.
+% \begin{macrocode}
+\cs_new:Nn \keys_options_ltx:n {
+ \tl_set:Nx \l_keys_current_key_tl {#1}
+ \keys_path_add:N \l_keys_current_key_tl
+ \clist_clear:N \l_keys_options_ltx_clist
+ \cs_if_eq:NNTF \@currext \@clsextension {
+ \cs_if_free:cF { opt@ \@currname . \@currext } {
+ \clist_map_function:cN { opt@ \@currname . \@currext }
+ \keys_options_ltx_cls:n
+ }
+ }{
+ \cs_if_free:NF \@classoptionslist {
+ \clist_map_function:NN \@classoptionslist \keys_options_ltx_pkg:n
+ }
+ \cs_if_free:cF { opt@ \@currname . \@currext } {
+ \exp_args:NNc \clist_concat:NNN \l_keys_options_ltx_clist
+ { opt@ \@currname . \@currext } \l_keys_options_ltx_clist
+ }
+ }
+ \clist_put_left:Nn \l_keys_options_ltx_clist { #1 /.cd: }
+ \exp_args:NV \keys_manage:n \l_keys_options_ltx_clist
+ \AtEndOfPackage { \cs_set_eq:NN \@unprocessedoptions \scan_stop: }
+}
+% \end{macrocode}
+%\end{macro}
+%\begin{macro}{\keys_options_ltx_cls:n}
+% This macro checks if the current class option is a known key. To do
+% that, any ``\texttt{=}'' part has to be removed first.
+% \begin{macrocode}
+\cs_new:Nn \keys_options_ltx_cls:n {
+ \keys_if_cmd_exist:nTF {
+ \l_keys_current_key_tl / \keys_options_ltx_strip_eq:w #1 = \q_stop
+ }{
+ \clist_put_right:Nn \l_keys_options_ltx_clist {#1}
+ }{
+ \clist_put_right:Nn \@unusedoptionlist {#1}
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%\begin{macro}{\keys_options_ltx_pkg:n}
+% For packages, options need to be removed from the global list.
+% \begin{macrocode}
+\cs_new:Nn \keys_options_ltx_pkg:n {
+ \keys_if_cmd_really_exist:nT {
+ \l_keys_current_key_tl / \keys_options_ltx_strip_eq:w #1 = \q_stop
+ }{
+ \clist_put_right:Nn \l_keys_options_ltx_clist {#1}
+ \clist_remove_element:Nn \@unusedoptionlist {#1}
+ }
+}
+% \end{macrocode}
+%\end{macro}
+%
+%\begin{macro}{\keys_options_ltx_strip_eq:w}
+% A simple method to remove an equals sign plus any trailing data.
+% \begin{macrocode}
+\cs_new:Npn \keys_options_ltx_strip_eq:w #1 = #2 \q_stop {#1}
+%</package>
+% \end{macrocode}
+%\end{macro}
+%
+%\Finale
diff --git a/Master/texmf-dist/source/latex/keys3/keys3opts2.ins b/Master/texmf-dist/source/latex/keys3/keys3opts2.ins
new file mode 100644
index 00000000000..b49c3a57e4f
--- /dev/null
+++ b/Master/texmf-dist/source/latex/keys3/keys3opts2.ins
@@ -0,0 +1,89 @@
+%%
+%% This is file `keys3opts2.ins',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% keys3opts2.dtx (with options: `install')
+%% ---------------------------------------------------------------
+%% The keys3opts2 package --- Using keys3 for LaTeX2e options
+%% Maintained by Joseph Wright
+%% E-mail: joseph.wright@morningstar2.co.uk
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% ---------------------------------------------------------------
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+\input docstrip.tex
+\keepsilent
+\askforoverwritefalse
+\preamble
+---------------------------------------------------------------
+The keys3opts2 package --- Using keys3 for LaTeX2e options
+Maintained by Joseph Wright
+E-mail: joseph.wright@morningstar2.co.uk
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+---------------------------------------------------------------
+
+EXPERIMENTAL CODE
+
+Do not distribute this file without also distributing the
+source files specified above.
+
+Do not distribute a modified version of this file.
+
+\endpreamble
+\postamble
+
+Copyright (C) 2008-2009 by
+ Joseph Wright <joseph.wright@morningstar2.co.uk>
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Joseph Wright.
+
+This work consists of the file keys3opts2.dtx
+ and the derived files keys3opts2.pdf,
+ keys3opts2.sty and
+ keys3opts2.ins.
+
+\endpostamble
+\usedir{tex/latex/keys3}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+\endbatchfile
+%%
+%% Copyright (C) 2008-2009 by
+%% Joseph Wright <joseph.wright@morningstar2.co.uk>
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This work is "maintained" (as per LPPL maintenance status) by
+%% Joseph Wright.
+%%
+%% This work consists of the file keys3opts2.dtx
+%% and the derived files keys3opts2.pdf,
+%% keys3opts2.sty and
+%% keys3opts2.ins.
+%%
+%%
+%% End of file `keys3opts2.ins'.
diff --git a/Master/texmf-dist/tex/latex/keys3/keys3.sty b/Master/texmf-dist/tex/latex/keys3/keys3.sty
new file mode 100644
index 00000000000..140e038463e
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/keys3/keys3.sty
@@ -0,0 +1,686 @@
+%%
+%% This is file `keys3.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% keys3.dtx (with options: `package')
+%% ---------------------------------------------------------------
+%% The keys3 package --- Key management for LaTeX3
+%% Maintained by Joseph Wright
+%% E-mail: joseph.wright@morningstar2.co.uk
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% ---------------------------------------------------------------
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+\RequirePackage{expl3}
+\GetIdInfo$Id: keys3.dtx 101 2009-06-10 12:16:20Z josephwright $
+ {Key management for LaTeX3}
+\ProvidesExplPackage
+ {\filename}{\filedate}{\fileversion}{\filedescription}
+\tl_new:cn { c_keys_0_empty_tl } {}
+\tl_new:cn { c_keys_1_empty_tl } { {} }
+\tl_new:cn { c_keys_2_empty_tl } { {} {} }
+\tl_new:cn { c_keys_3_empty_tl } { {} {} {} }
+\tl_new:cn { c_keys_4_empty_tl } { {} {} {} {} }
+\tl_new:cn { c_keys_5_empty_tl } { {} {} {} {} {} }
+\tl_new:cn { c_keys_6_empty_tl } { {} {} {} {} {} {} }
+\tl_new:cn { c_keys_7_empty_tl } { {} {} {} {} {} {} {} }
+\tl_new:cn { c_keys_8_empty_tl } { {} {} {} {} {} {} {} {} }
+\tl_new:cn { c_keys_9_empty_tl } { {} {} {} {} {} {} {} {} {} }
+\tl_new:Nn \c_keys_cs_prefix_tl { keys-root }
+\tl_new:Nn \c_keys_errors_path_tl { /keys/errors }
+\tl_new:Nn \c_keys_properties_path_tl { /keys/properties }
+\tl_new:Nn \c_keys_utilities_path_tl { /keys }
+\tl_new:Nn \c_keys_root_tl { / }
+\tl_new:N \l_keys_choice_code_tl
+\tl_new:N \l_keys_current_choice_tl
+\int_new:N \l_keys_current_choice_int
+\tl_new:N \l_keys_current_key_full_tl
+\tl_new:N \l_keys_current_default_tl
+\tl_new:N \l_keys_current_module_tl
+\tl_new:N \l_keys_current_path_tl
+\tl_new:N \l_keys_default_path_tl
+\tl_set_eq:NN \l_keys_default_path_tl \c_keys_root_tl
+\tl_new:N \l_keys_choice_path_tl
+\toks_new:N \l_keys_current_value_toks
+\bool_new:N \l_keys_no_value_bool
+\bool_new:N \l_keys_success_bool
+\tl_new:N \l_keys_tmpa_tl
+\cs_new:Nn \keys_manage:n {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_removal_sanitize:n
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_manage_internal:n {#1}
+}
+\cs_new:Nn \keys_manage_quick:n {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_no_space_removal_no_sanitize:n
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_manage_internal:n {#1}
+}
+\cs_new:Nn \keys_manage_internal:n {
+ \keys_manage_aux:Vn \l_keys_default_path_tl {#1}
+}
+\cs_new:Nn \keys_manage_aux:nn {
+ \tl_set_eq:NN \l_keys_default_path_tl \c_keys_root_tl
+ \keys_parse_list:n {#2}
+ \tl_set:Nn \l_keys_default_path_tl {#1}
+}
+\cs_generate_variant:Nn \keys_manage_aux:nn { Vn }
+\cs_new:Nn \keys_manage:nn {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_removal_sanitize:n
+ \keys_manage_aux:Vnn \l_keys_default_path_tl {#1} {#2}
+}
+\cs_new:Nn \keys_manage_quick:nn {
+ \cs_set_eq:NN \keys_parse:n \KV_parse_space_no_removal_no_sanitize:n
+ \keys_manage_aux:Vnn \l_keys_default_path_tl {#1} {#2}
+}
+\cs_new:Nn \keys_manage_aux:nnn {
+ \tl_set:Nn \l_keys_default_path_tl {#2}
+ \tl_clear:N \l_keys_current_module_tl
+ \keys_parse_list:n {#3}
+ \tl_set:Nn \l_keys_default_path_tl {#1}
+}
+\cs_generate_variant:Nn \keys_manage_aux:nnn { Vnn }
+\cs_new_nopar:Nn \keys_bool_new:n {
+ \bool_new:c { \c_keys_cs_prefix_tl #1 }
+ \bool_set_true:c { \c_keys_cs_prefix_tl #1 }
+}
+\cs_new_nopar:Nn \keys_bool_set:nN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ \cs_if_exist:cTF { bool_ #1 _ ##1 :N } {
+ \use:c { bool_ #1 _ ##1 :N } #2
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_bool_set:nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N \cs_if_exist:cTF { bool_ #1 _ ##1 :c } {
+ \exp_not:N \use:c { bool_ #1 _ ##1 :c }
+ { #2 \l_keys_current_module_tl #3 _bool }
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_bool_set_inverse:n {
+ \tl_if_eq:nnTF {#1} { true } { false } { true }
+}
+\cs_new_nopar:Nn \keys_bool_set_inverse:nN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ \cs_if_exist:cTF { bool_ #1 _ ##1 :N } {
+ \use:c { bool_ #1 _ \keys_bool_set_inverse:n {##1} :N } #2
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_bool_set_inverse:nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N \cs_if_exist:cTF { bool_ #1 _ ##1 :c } {
+ \exp_not:N \use:c {
+ bool_ #1 _ \exp_not:N \keys_bool_set_inverse:n {##1} :c
+ } { #2 \l_keys_current_module_tl #3 _bool }
+ }{
+ \keys_error:nn { Boolean~expected } {##1}
+ }
+ },
+ \l_keys_current_path_tl /.default:n = true
+ }
+}
+\cs_new_nopar:Nn \keys_check_exists:NN {
+ \cs_if_exist:NF #1 {
+ #2 #1
+ }
+}
+\cs_generate_variant:Nn \keys_check_exists:NN { cN }
+\cs_new:Nn \keys_choices_create:Nnn {
+ \tl_set_eq:NN \l_keys_choice_path_tl \l_keys_current_path_tl
+ \int_zero:N \l_keys_current_choice_int
+ \use:c { keys_choices_create_aux: #1 } {#3}
+ \clist_map_function:nN {#2} \keys_choice_create:n
+ \keys_manage_internal:n { \l_keys_choice_path_tl /.expects_choice: }
+}
+\cs_new:Nn \keys_choices_create_aux:n {
+ \tl_set:Nn \l_keys_choice_code_tl { \exp_not:n {#1} }
+}
+\cs_new:Nn \keys_choices_create_aux:x {
+ \tl_set:Nn \l_keys_choice_code_tl {#1}
+}
+\cs_new_nopar:Nn \keys_choice_create:n {
+ \int_incr:N \l_keys_current_choice_int
+ \keys_parse_list:n {
+ \l_keys_choice_path_tl / #1 /.code:x = {
+ \exp_not:n { \int_set:Nn \l_keys_current_choice_int }
+ { \int_use:N \l_keys_current_choice_int }
+ \exp_not:n { \tl_set:Nn \l_keys_current_choice_tl } {#1}
+ \l_keys_choice_code_tl
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_clear_properties:n {
+ \keys_undefine:n { #1 /._cmd:w }
+ \keys_undefine:n { #1 /._default_tl }
+ \keys_undefine:n { #1 /._forbidden_bool }
+ \keys_undefine:n { #1 /._num_args_tl }
+ \keys_undefine:n { #1 /._required_bool }
+}
+\cs_new_nopar:Nn \keys_default_add: {
+ \bool_if:NT \l_keys_no_value_bool {
+ \keys_if_exist:nT
+ { \l_keys_current_key_full_tl /._default_tl } {
+ \keys_toks_set:Nn \l_keys_current_value_toks
+ { \l_keys_current_key_full_tl /._default_tl }
+ \bool_set_false:N \l_keys_no_value_bool
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_find_code_full: {
+ \keys_if_cmd_exist:nTF { \l_keys_current_key_full_tl } {
+ \keys_use_cmd:n { \l_keys_current_key_full_tl }
+ }{
+ \keys_if_exist:nTF { \l_keys_current_key_full_tl } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_use:n { \l_keys_current_key_full_tl }
+ }{
+ \keys_store:nx { \l_keys_current_key_full_tl }
+ { \toks_use:N \l_keys_current_value_toks }
+ }
+ }{
+ \keys_find_code_name:
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_find_code_name: {
+ \keys_separate_path:
+ \keys_if_cmd_exist:nTF
+ { \c_keys_properties_path_tl / \l_keys_current_key_name_tl } {
+ \keys_use_cmd:n
+ {\c_keys_properties_path_tl / \l_keys_current_key_name_tl}
+ }{
+ \keys_if_cmd_exist:nTF
+ { \l_keys_current_path_tl / unknown } {
+ \keys_use_cmd:n { \l_keys_current_path_tl / unknown }
+ }{
+ \keys_error:n { unknown~key }
+ }
+ }
+}
+\prg_new_conditional:Nnn \keys_if_cmd_exist:n {TF,T,F} {
+ \if_cs_exist:w \c_keys_cs_prefix_tl #1 /._cmd:w \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+\prg_new_conditional:Nnn \keys_if_exist:n {TF,T,F} {
+ \if_cs_exist:w \c_keys_cs_prefix_tl #1 \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+\prg_new_conditional:Nnn \keys_if_value:n {TF,T,F} {
+ \if_cs_exist:w
+ \c_keys_cs_prefix_tl \l_keys_current_key_full_tl /._ #1 _bool
+ \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+}
+\cs_new:Nn \keys_no_value_elt:n {
+ \bool_set_true:N \l_keys_no_value_bool
+ \keys_process_elt:nn {#1} { }
+}
+\cs_new:Nn \keys_value_elt:nn {
+ \bool_set_false:N \l_keys_no_value_bool
+ \keys_process_elt:nn {#1} {#2}
+}
+\cs_new:Nn \keys_parse:n { \ERROR }
+\cs_new:Nn \keys_parse_list:n {
+ \cs_set_eq:NN \KV_key_value_elt:nn \keys_value_elt:nn
+ \cs_set_eq:NN \KV_key_no_value_elt:n \keys_no_value_elt:n
+ \keys_parse:n {#1}
+}
+\cs_new_nopar:Nn \keys_path_add:N {
+ \exp_after:wN \keys_path_add:w #1 \q_stop
+}
+\cs_new_nopar:Npn \keys_path_add:w {
+ \exp_after:wN \peek_meaning:NTF \c_keys_root_tl {
+ \use_none_delimit_by_q_stop:w
+ }{
+ \keys_path_add_aux:w
+ }
+}
+\cs_new_nopar:Npn \keys_path_add_aux:w #1 \q_stop {
+ \tl_set:Nx \l_keys_current_key_full_tl {\l_keys_default_path_tl #1}
+}
+\cs_new:Nn \keys_process_elt:nn {
+ \tl_set:Nx \l_keys_current_key_full_tl {#1}
+ \toks_set:Nn \l_keys_current_value_toks {#2}
+ \keys_path_add:N \l_keys_current_key_full_tl
+ \keys_default_add:
+ \keys_if_value:nTF { required } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_error:n { value~required }
+ }{
+ \keys_process_elt_aux:
+ }
+ }{
+ \keys_process_elt_aux:
+ }
+}
+\cs_new:Nn \keys_process_elt_aux: {
+ \keys_if_value:nTF { forbidden } {
+ \bool_if:NTF \l_keys_no_value_bool {
+ \keys_find_code_full:
+ }{
+ \keys_error:nn { value~forbidden }
+ { \toks_use:N \l_keys_current_value_toks }
+ }
+ }{
+ \keys_find_code_full:
+ }
+}
+\cs_new_nopar:Nn \keys_separate_path: {
+ \tl_clear:N \l_keys_current_path_tl
+ \exp_after:wN \keys_separate_path:w \l_keys_current_key_full_tl
+ / \q_stop
+}
+\cs_new_nopar:Npn \keys_separate_path:w / #1 / #2 \q_stop {
+ \tl_if_empty:nTF {#2} {
+ \tl_set:Nn \l_keys_current_key_name_tl {#1}
+ }{
+ \tl_put_right:Nn \l_keys_current_path_tl { / #1 }
+ \keys_separate_path:w /#2 \q_stop
+ }
+}
+\cs_new_nopar:Nn \keys_set:NN {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:n = {
+ #1 #2 {##1}
+ }
+ }
+}
+\cs_new_nopar:Nn \keys_set:Nnn {
+ \keys_parse_list:n {
+ \c_keys_properties_path_tl /.code:x = {
+ \exp_not:N #1 { #2 \l_keys_current_module_tl #3 } {##1}
+ }
+ }
+}
+\cs_new:Nn \keys_set_cmd:nn {
+ \keys_clear_properties:n {#1}
+ \cs_set:cpn { \c_keys_cs_prefix_tl #1 /._cmd:w } ##1 {#2}
+}
+\cs_new:Nn \keys_set_cmd:nx {
+ \keys_clear_properties:n {#1}
+ \cs_set:cpx { \c_keys_cs_prefix_tl #1 /._cmd:w } ##1 {#2}
+}
+\cs_new:Nn \keys_set_cmd:nNn {
+ \keys_clear_properties:n {#1}
+ \cs_generate_from_arg_count:cNnn { \c_keys_cs_prefix_tl #1 /._cmd:w }
+ \cs_set:Npn #2 {#3}
+ \keys_store:nn { #1 /._num_args_tl} {#2}
+}
+\cs_new:Nn \keys_set_cmd:nNx {
+ \keys_clear_properties:n {#1}
+ \cs_generate_from_arg_count:cNnn { \c_keys_cs_prefix_tl #1 /._cmd:w }
+ \cs_set:Npx #2 {#3}
+ \keys_store:nn { #1 /._num_args_tl} {#2}
+}
+\cs_new:Nn \keys_store:nn {
+ \tl_set:cn { \c_keys_cs_prefix_tl #1 } {#2}
+}
+\cs_new:Nn \keys_store:nx {
+ \tl_set:cx { \c_keys_cs_prefix_tl #1 } {#2}
+}
+\cs_new_nopar:Nn \keys_toks_set:Nn {
+ \exp_args:NNv \toks_set:Nn #1 { \c_keys_cs_prefix_tl #2 }
+}
+\cs_new_nopar:Nn \keys_try: {
+ \bool_set_false:N \l_keys_success_bool
+ \keys_if_cmd_exist:nT { \l_keys_current_path_tl } {
+ \bool_set_true:N \l_keys_success_bool
+ \bool_if:NT \l_keys_no_value_bool {
+ \keys_if_exist:nT
+ { \l_keys_current_path_tl /._default_tl } {
+ \keys_toks_set:Nn \l_keys_current_value_toks
+ { \l_keys_current_path_tl /._default_tl }
+ }
+ }
+ \cs_set_eq:NN \l_keys_current_key_full_tl \l_keys_current_path_tl
+ \keys_use_cmd:n { \l_keys_current_path_tl }
+ }
+}
+\cs_new_nopar:Nn \keys_undefine:n {
+ \cs_set_eq:cN { \c_keys_cs_prefix_tl #1 } \c_undefined
+}
+\cs_new_nopar:Nn \keys_use:n {
+ \use:c { \c_keys_cs_prefix_tl #1 }
+}
+\cs_new_nopar:Nn \keys_use_cmd:n {
+ \exp_args:Nno \keys_use_cmd:nn {#1}
+ { \toks_use:N \l_keys_current_value_toks }
+}
+\cs_new_nopar:Nn \keys_use_cmd:nn {
+ \keys_if_exist:nTF { #1 /._num_args_tl } {
+ \keys_use_cmd_aux:nn {#1} {#2}
+ }{
+ \keys_use:n {#1/._cmd:w} {#2}
+ }
+}
+\cs_new_nopar:Nn \keys_use_cmd_aux:nn {
+ \tl_if_empty:nTF {#2} {
+ \cs_set:Npn \keys_use_cmd_aux:w {
+ \keys_use:n { #1 /._cmd:w }
+ }
+ \exp_after:wN \exp_after:wN \exp_after:wN \keys_use_cmd_aux:w
+ \cs:w
+ c_keys_ \keys_use:n { #1 /._num_args_tl } _empty_tl
+ \cs_end:
+ }{
+ \keys_use:n { #1 /._cmd:w } #2
+ }
+}
+\cs_new_nopar:Npn \keys_msg_new:nn #1 {
+ \msg_new:nnn { keys } {#1}
+}
+\cs_new_protected_nopar:Nn \keys_error:nn {
+ \msg_error:nnxx { keys } {#1} { \l_keys_current_key_full_tl } {#2}
+}
+\cs_new_protected_nopar:Nn \keys_error:n {
+ \msg_error:nnx { keys } {#1} { \l_keys_current_key_full_tl }
+}
+\keys_msg_new:nn
+ { unknown~key }
+ {The key `#1' is unknown and is being ignored.}
+\keys_msg_new:nn
+ { value~required }
+ {The key `#1' requires a value\\%
+ and is being ignored.}
+\keys_msg_new:nn
+ { value~forbidden }
+ {The key `#1' cannot taken a value:\\%
+ the given input `#2' is being ignored.}
+\keys_msg_new:nn
+ { Boolean~expected }
+ {Key `#1' takes the Boolean values\\%
+ `true' and `false'~only.\\%
+ The given value `#2' is being ignored.}
+\keys_msg_new:nn
+ { not_boolean }
+ {Key `#1' is not a Boolean key: you cannot create a complement.}
+\keys_msg_new:nn
+ { unknown~choice }
+ {Choice `#2' unknown for key `#1':\\%
+ the key is being ignored.}
+\keys_set_cmd:nn { \c_keys_properties_path_tl /.code:n } {
+ \keys_set_cmd:nn { \l_keys_current_path_tl } {#1}
+}
+\keys_set_cmd:nNn { \c_keys_properties_path_tl /.code:Nn } 2 {
+ \keys_set_cmd:nNn { \l_keys_current_path_tl } #1 {#2}
+}
+\keys_manage_quick:n {
+ \c_keys_errors_path_tl /unknown/.code:n = {
+ \keys_error:n { unknown~key }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.code:x/.code:n = {
+ \keys_set_cmd:nx { \l_keys_current_path_tl } {#1}
+ },
+ \c_keys_properties_path_tl /.code:Nx/.code:Nn = 2 {
+ \keys_set_cmd:nNx { \l_keys_current_path_tl } {#1} {#2}
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.cd:/.code:n = {
+ \tl_set:Nx \l_keys_default_path_tl { \l_keys_current_path_tl / }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.value_required:/.code:n = {
+ \keys_bool_new:n { \l_keys_current_path_tl /._required_bool }
+ \keys_undefine:n { \l_keys_current_path_tl /._forbidden_bool }
+ },
+ \c_keys_properties_path_tl /.value_forbidden:/.code:n = {
+ \keys_bool_new:n { \l_keys_current_path_tl /._forbidden_bool }
+ \keys_undefine:n { \l_keys_current_path_tl /._required_bool }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.default:n/.code:n = {
+ \keys_store:nn { \l_keys_current_path_tl /._default_tl } {#1}
+ \keys_undefine:n { \l_keys_current_path_tl /._required_bool }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.tl_set:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.tl_set_x:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_set:Nx #1
+ },
+ \c_keys_properties_path_tl /.tl_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.tl_gset_x:N/.code:n = {
+ \keys_check_exists:NN #1 \tl_new:N
+ \keys_set:NN \tl_gset:Nx #1
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.int_set:N/.code:n = {
+ \keys_check_exists:NN #1 \int_new:N
+ \keys_set:NN \int_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.int_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \int_new:N
+ \keys_set:NN \int_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.skip_set:N/.code:n = {
+ \keys_check_exists:NN #1 \skip_new:N
+ \keys_set:NN \skip_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.skip_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \skip_new:N
+ \keys_set:NN \skip_gset:Nn #1
+ },
+ \c_keys_properties_path_tl /.toks_set:N/.code:n = {
+ \keys_check_exists:NN #1 \toks_new:N
+ \keys_set:NN \toks_set:Nn #1
+ },
+ \c_keys_properties_path_tl /.toks_gset:N/.code:n = {
+ \keys_check_exists:NN #1 \toks_new:N
+ \keys_set:NN \toks_gset:Nn #1
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_utilities_path_tl /current_module:n/.code:n = {
+ \tl_if_empty:nTF {#1} {
+ \tl_clear:N \l_keys_current_module_tl
+ }{
+ \tl_set:Nn \l_keys_current_module_tl { #1 _ }
+ }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.tl_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_set:cn { l_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_set_x:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_set:cx { l_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_gset:cn { g_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.tl_gset_x:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _tl } \tl_new:N
+ \keys_set:Nnn \tl_gset:cx { g_ } { #1 _tl }
+ },
+ \c_keys_properties_path_tl /.int_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _int } \int_new:N
+ \keys_set:Nnn \int_set:cn { l_ } { #1 _int }
+ },
+ \c_keys_properties_path_tl /.int_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _int } \int_new:N
+ \keys_set:Nnn \int_gset:cn { g_ } { #1 _int }
+ },
+ \c_keys_properties_path_tl /.skip_set:n/.code:n = {
+ \keys_check_exists:cN { l_ #1 _skip } \skip_new:N
+ \keys_set:Nnn \skip_set:cn { l_ } { #1 _skip }
+ },
+ \c_keys_properties_path_tl /.skip_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _skip } \skip_new:N
+ \keys_set:Nnn \skip_gset:cn { g_ } { #1 _skip }
+ },
+ \c_keys_properties_path_tl /.toks_set:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _toks } \toks_new:N
+ \keys_set:Nnn \toks_set:cn { l_ } { #1 _toks }
+ },
+ \c_keys_properties_path_tl /.toks_gset:n/.code:n = {
+ \keys_check_exists:cN { g_ #1 _toks } \toks_new:N
+ \keys_set:Nnn \toks_gset:cn { g_ } { #1 _toks }
+ },
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.bool_set:N/.code:n = {
+ \keys_bool_set:nN { set } #1
+ },
+ \c_keys_properties_path_tl /.bool_gset:N/.code:n = {
+ \keys_bool_set:nN { gset } #1
+ },
+ \c_keys_properties_path_tl /.bool_set:n/.code:n = {
+ \keys_bool_set:nnn { set } { l_ } { #1 }
+ },
+ \c_keys_properties_path_tl /.bool_gset:n/.code:n = {
+ \keys_bool_set:nnn { gset } { g_ } { #1 }
+ },
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.bool_set_inverse:N/.code:n = {
+ \keys_bool_set_inverse:nN { set } #1
+ },
+ \c_keys_properties_path_tl /.bool_gset_inverse:N/.code:n = {
+ \keys_bool_set_inverse:nN { gset } #1
+ },
+ \c_keys_properties_path_tl /.bool_set_inverse:n/.code:n = {
+ \keys_bool_set_inverse:nnn { set } { l_ } { #1 }
+ },
+ \c_keys_properties_path_tl /.bool_gset_inverse:n/.code:n = {
+ \keys_bool_set_inverse:nnn { gset } { g_ } { #1 }
+ },
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.expects_choice:/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.cd:,
+ .code:n = {
+ \tl_set:Nn \l_keys_current_choice_tl {##1}
+ \int_zero:N \l_keys_current_choice_int
+ \exp_args:No \keys_parse_list:n
+ { \l_keys_current_key_full_tl / ##1 }
+ },
+ unknown/.code:n = {
+ \keys_error:nn { unknown~choice }
+ { \l_keys_current_key_name_tl }
+ }
+ }
+ },
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.create_choices:nn/.code:Nn = 2 {
+ \keys_choices_create:Nnn n {#1} {#2}
+ },
+ \c_keys_properties_path_tl /.create_choices:nx/.code:Nn = 2 {
+ \keys_choices_create:Nnn x {#1} {#2}
+ },
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.use_keys:n/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:n = { \keys_parse_list:n {#1} }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:x/.code:n = {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:x = {
+ \exp_not:N \keys_parse_list:n {#1}
+ }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:Nn/.code:Nn = 2 {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:Nn = #1 { \keys_parse_list:n {#2} }
+ }
+ },
+ \c_keys_properties_path_tl /.use_keys:Nx/.code:Nn = 2 {
+ \keys_manage_internal:n {
+ \l_keys_current_path_tl /.code:Nx =
+ #1 { \exp_not:N \keys_parse_list:n {#2} }
+ }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.try:n/.code:n = { \keys_try: },
+ \c_keys_properties_path_tl /.retry:n/.code:n = {
+ \bool_if:NF \l_keys_success_bool { \keys_try: }
+ }
+}
+\keys_manage_quick:n {
+ \c_keys_properties_path_tl /.show_code:/.code:n = {
+ \cs_show:c {
+ \c_keys_cs_prefix_tl \l_keys_current_path_tl /._cmd:w
+ }
+ },
+ \c_keys_properties_path_tl /.show_key:/.code:n = {
+ \cs_show:c { \c_keys_cs_prefix_tl \l_keys_current_path_tl }
+ }
+}
+%%
+%% Copyright (C) 2008-2009 by
+%% Joseph Wright <joseph.wright@morningstar2.co.uk>
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This work is "maintained" (as per LPPL maintenance status) by
+%% Joseph Wright.
+%%
+%% This work consists of the file keys3.dtx
+%% and the derived files keys3.pdf,
+%% keys3.sty and
+%% keys3.ins.
+%%
+%%
+%% End of file `keys3.sty'.
diff --git a/Master/texmf-dist/tex/latex/keys3/keys3opts2.sty b/Master/texmf-dist/tex/latex/keys3/keys3opts2.sty
new file mode 100644
index 00000000000..4a2cd7eb090
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/keys3/keys3opts2.sty
@@ -0,0 +1,90 @@
+%%
+%% This is file `keys3opts2.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% keys3opts2.dtx (with options: `package')
+%% ---------------------------------------------------------------
+%% The keys3opts2 package --- Using keys3 for LaTeX2e options
+%% Maintained by Joseph Wright
+%% E-mail: joseph.wright@morningstar2.co.uk
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% ---------------------------------------------------------------
+%%
+%% EXPERIMENTAL CODE
+%%
+%% Do not distribute this file without also distributing the
+%% source files specified above.
+%%
+%% Do not distribute a modified version of this file.
+%%
+\RequirePackage{l3names}
+\GetIdInfo$Id: keys3opts2.dtx 93 2009-05-03 20:34:46Z josephwright $
+ {Using keys3 for LaTeX2e options}
+\ProvidesExplPackage
+ {\filename}{\filedate}{\fileversion}{\filedescription}
+\RequirePackage{keys3}
+\clist_new:N \l_keys_options_ltx_clist
+\cs_new:Nn \keys_options_ltx:n {
+ \tl_set:Nx \l_keys_current_key_tl {#1}
+ \keys_path_add:N \l_keys_current_key_tl
+ \clist_clear:N \l_keys_options_ltx_clist
+ \cs_if_eq:NNTF \@currext \@clsextension {
+ \cs_if_free:cF { opt@ \@currname . \@currext } {
+ \clist_map_function:cN { opt@ \@currname . \@currext }
+ \keys_options_ltx_cls:n
+ }
+ }{
+ \cs_if_free:NF \@classoptionslist {
+ \clist_map_function:NN \@classoptionslist \keys_options_ltx_pkg:n
+ }
+ \cs_if_free:cF { opt@ \@currname . \@currext } {
+ \exp_args:NNc \clist_concat:NNN \l_keys_options_ltx_clist
+ { opt@ \@currname . \@currext } \l_keys_options_ltx_clist
+ }
+ }
+ \clist_put_left:Nn \l_keys_options_ltx_clist { #1 /.cd: }
+ \exp_args:NV \keys_manage:n \l_keys_options_ltx_clist
+ \AtEndOfPackage { \cs_set_eq:NN \@unprocessedoptions \scan_stop: }
+}
+\cs_new:Nn \keys_options_ltx_cls:n {
+ \keys_if_cmd_exist:nTF {
+ \l_keys_current_key_tl / \keys_options_ltx_strip_eq:w #1 = \q_stop
+ }{
+ \clist_put_right:Nn \l_keys_options_ltx_clist {#1}
+ }{
+ \clist_put_right:Nn \@unusedoptionlist {#1}
+ }
+}
+\cs_new:Nn \keys_options_ltx_pkg:n {
+ \keys_if_cmd_really_exist:nT {
+ \l_keys_current_key_tl / \keys_options_ltx_strip_eq:w #1 = \q_stop
+ }{
+ \clist_put_right:Nn \l_keys_options_ltx_clist {#1}
+ \clist_remove_element:Nn \@unusedoptionlist {#1}
+ }
+}
+\cs_new:Npn \keys_options_ltx_strip_eq:w #1 = #2 \q_stop {#1}
+%%
+%% Copyright (C) 2008-2009 by
+%% Joseph Wright <joseph.wright@morningstar2.co.uk>
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This work is "maintained" (as per LPPL maintenance status) by
+%% Joseph Wright.
+%%
+%% This work consists of the file keys3opts2.dtx
+%% and the derived files keys3opts2.pdf,
+%% keys3opts2.sty and
+%% keys3opts2.ins.
+%%
+%%
+%% End of file `keys3opts2.sty'.
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index 811440d1332..a6390f71e8d 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -130,7 +130,8 @@ my @WorkingTLP = qw(
iso10303 isodate isodoc isomath isonums isorot isotope itnumpar iwona
jeopardy jknapltx jneurosci jpsj junicode
jura juraabbrev jurabib juramisc jurarsp
- kalender kastrup kerkis kerntest keystroke knuth koma-script kpfonts
+ kalender kastrup kerkis kerntest keys3
+ keystroke knuth koma-script kpfonts
l2picfaq l2tabu-english l2tabu-french l2tabu-it l2tabu-spanish
labbook labelcas labels lastpage
latex latex-doc-ptr latex2e-help-texinfo latex2man
diff --git a/Master/tlpkg/tlpsrc/collection-latex3.tlpsrc b/Master/tlpkg/tlpsrc/collection-latex3.tlpsrc
index 31ce4e920ab..642fea7d105 100644
--- a/Master/tlpkg/tlpsrc/collection-latex3.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-latex3.tlpsrc
@@ -2,5 +2,6 @@ name collection-latex3
category Collection
shortdesc LaTeX3 packages
depend expl3
+depend keys3
depend xpackages
depend collection-latex
diff --git a/Master/tlpkg/tlpsrc/keys3.tlpsrc b/Master/tlpkg/tlpsrc/keys3.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/keys3.tlpsrc