summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/amsrefs/rkeyval.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/amsrefs/rkeyval.dtx')
-rw-r--r--macros/latex/contrib/amsrefs/rkeyval.dtx1127
1 files changed, 1127 insertions, 0 deletions
diff --git a/macros/latex/contrib/amsrefs/rkeyval.dtx b/macros/latex/contrib/amsrefs/rkeyval.dtx
new file mode 100644
index 0000000000..271c0694a8
--- /dev/null
+++ b/macros/latex/contrib/amsrefs/rkeyval.dtx
@@ -0,0 +1,1127 @@
+% \def\filename{rkeyval.dtx}
+% \def\fileversion{2.00}
+% \def\filedate{2004/06/28}
+%
+% \iffalse meta-comment
+%
+% American Mathematical Society
+% Technical Support
+% Publications Technical Group
+% 201 Charles Street
+% Providence, RI 02904
+% USA
+% tel: (401) 455-4080
+% (800) 321-4267 (USA and Canada only)
+% fax: (401) 331-3842
+% email: tech-support@ams.org
+%
+% Copyright 2001, 2010 American Mathematical Society.
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is the American Mathematical
+% Society.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\documentclass[draft,oneside]{amsdtx}
+\pagestyle{myheadings}
+
+\makeatletter
+\DeclareRobustCommand{\fld}{\@category@index{field}}
+\makeatother
+
+\DoNotIndex{\@emptytoks,\@temptokenb}
+
+\CodelineIndex
+
+\begin{document}
+\title{The \pkg{rkeyval} package:\protect\linebreak[1]
+ Syntactically restricted key-value scanning}
+\author{Michael Downes and David M. Jones\\American Mathematical Society}
+\date{Version \fileversion, \filedate}
+\DocInput{rkeyval.dtx}
+\PrintIndex
+\end{document}
+%</driver>
+% \fi
+%
+% \MakeShortVerb{\|}
+%
+% \maketitle
+% \markboth{The \protect\pkg{rkeyval} package}
+% {Version \protect\fileversion, \protect\filedate}
+%
+% \tableofcontents
+%
+% \section{Introduction}
+%
+% The \pkg{rkeyval} package provides functions for scanning key-value
+% notation similar to the kind of scanning supported by the standard
+% \pkg{keyval} package. However, the syntax is more restrictive in
+% order to make some improved error-checking possible. In particular,
+% if a comma is omitted between two instances of |key={value}|
+% form, the \cn{RestrictedSetKeys} command will spot the missing
+% comma and issue a suitable error message (and it will be given at
+% the point where the missing comma is detected, before reading any
+% further in the \tex/ file). The standard \cn{setkeys} command, by
+% contrast, will append the second key name to the value of the first
+% key and discard the second value, without any notification to the
+% user that anything has gone wrong. But that is partly because the
+% standard \cn{setkeys} command allows implied values and does not
+% require braces around explicit values (except when necessary to
+% hide material that has a syntactic resemblance to a key-value
+% pair). With \cn{RestrictedSetKeys} the value must always be present
+% and it must be enclosed in braces.
+%
+%^^A Maybe commas should be optional.
+%
+% Further restrictions of the \cn{RestrictedSetKeys} command and its
+% companion commands reduce memory consumption in certain ways.
+% Defining a key creates only one control sequence, a container for
+% holding the value. Processing of key values is normally limited to
+% storing a value given by the user; any additional processing must
+% be supplied separately by the programmer.
+%
+% Generally speaking, the error-checking done by
+% \cn{RestrictedSetKeys} is better for applications where all the
+% keys are expected to have textual values, while \cn{setkeys} is
+% better when one wants to silently recover as far as possible from
+% syntactic errors, instead of notifying the user of the errors; or
+% when keys have nontrivial default values (i.e., not empty) or other
+% kinds of special processing.
+%
+% \begin{verbatim}
+% \RestrictedSetKeys{setup-code}{group}{code}{key={val}, ...}
+%\end{verbatim}
+% Normally \cn{RestrictedSetKeys} simply carries out the following
+% assignment for each key-value pair:
+% \begin{verbatim}
+% \def\group'key{val}
+%\end{verbatim}
+% The first argument is normally empty, but the exact nature of the
+% warnings given and other aspects of the processing can be affected
+% by putting extra setup code there. The \pkg{amsrefs} package uses
+% this to implement a copying operation where field name and value
+% are written out immediately to another file instead of being stored
+% in the usual way.
+%
+% Some examples for defining the key names associated with a given
+% group. This defines ``title'' as a recognized key for the \fn{bib}
+% group:
+% \begin{verbatim}
+% \DefineSimpleKey{bib}{title}
+%\end{verbatim}
+% If a key is defined with \cs{DefineSimpleKey}, the result of using
+% the same key more than once in a single entry will be an error
+% message.
+%
+% This defines ``title'' to be a repeatable key:
+% \begin{verbatim}
+% \DefineSupersedingKey{bib}{title}
+%\end{verbatim}
+% If it occurs more than once, the last value supersedes the earlier
+% ones, instead of getting an error. This variation is not needed for
+% simple usage, but in more complicated situations where key values
+% are combined from multiple sources, it may be useful.
+%
+% This defines ``author'' to be a repeatable key, with each value
+% being appended to a list of values:
+% \begin{verbatim}
+% \DefineAdditiveKey{bib}{author}{\name}
+%\end{verbatim}
+% The third argument specifies a wrapper function that should be
+% applied to each item in the list. I.e., suppose that two author
+% names are given:
+% \begin{verbatim}
+% author={Smith, J. Q.},
+% author={Taylor, X. Y.},
+%\end{verbatim}
+% Then they will be stored in the form
+% \begin{verbatim}
+% \name{Smith, J. Q.}\name{Taylor, X. Y.}
+%\end{verbatim}
+%
+% This defines ``transition'' to be a dummy key with a value that is
+% superficially nonempty but effectly empty:
+% \begin{verbatim}
+% \DefineDummyKey{bib}{transition}
+%\end{verbatim}
+% Defining a dummy key like this can be useful in dealing with
+% certain boundary situations that sometimes arise.
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+% Standard declaration of package name and date.
+% \begin{macrocode}
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{rkeyval}[2004/05/05 v1.08]
+% \end{macrocode}
+%
+% \begin{macro}{\@xp}
+% \begin{macro}{\@nx}
+% \begin{macrocode}
+\let\@xp\expandafter
+\let\@nx\noexpand
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@gobblethree}
+% \begin{macro}{\@nilgobble}
+% Not in the \latex/ kernel yet.
+% \begin{macrocode}
+\long\def\@gobblethree#1#2#3{}
+\long\def\@nilgobble#1\@nil{}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@emptytoks}
+% Using \cs{@ifundefined} here avoids problems with really old
+% versions of \latex/ that choke on \cs{newtoks} if it is written
+% directly in the false branch of a conditional.
+% \begin{macrocode}
+\@ifundefined{@emptytoks}{\csname newtoks\endcsname\@emptytoks}{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@temptokenb}
+% \begin{macrocode}
+\@ifundefined{@temptokenb}{\csname newtoks\endcsname\@temptokenb}{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@append}
+% \begin{macrocode}
+\def\@append#1#2#3{\@xp\def\@xp#2\@xp{#2#1{#3}}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\star@}
+% Test for a trailing option marked by a star. Usage:
+% \begin{verbatim}
+% \newcommand{\blub}[1]{\star@{\blubaux{#1}}{default}}
+%\end{verbatim}
+% Arg 1 of \cs{star@} is the code to
+% be run, arg 2 is the default value of the option (could be empty).
+% If arg 1 is \ncn{moo}, this test discards a star and expands to
+% \ncn{moo} if a star is found, or expands to |\moo{#2}| if
+% not. As the example shows, arg 1 need not be a single token.
+% \begin{macrocode}
+\def\star@#1#2{%
+ \def\star@a##1{#1}%
+ \def\star@b{#1{#2}}%
+ \futurelet\@let@token\star@test
+}
+
+\def\star@test{\ifx*\@let@token \let\star@b\star@a\fi \star@b}
+% \end{macrocode}
+%
+% Please note: If there is a space before the star, then the star is
+% not treated as an option char.
+%
+% Why use a star? Since it's already part of standard \latex/
+% command syntax, it's unlikely to suffer from catcode changes.
+%
+% Why not just put the star at the beginning in the usual way? It
+% seemed to me that the lack of a trailing option
+% feature was a deficiency in current \latex/ and could be given an
+% experimental implementation in a package like this without any
+% adverse effect on existing documents.
+% \end{macro}
+%
+% Ensure non-weird catcode for relevant characters.
+% \begin{macrocode}
+\@ifundefined{NormalCatcodes}{\RequirePackage{pcatcode}\relax}{}
+\PushCatcodes\NormalCatcodes
+% \end{macrocode}
+%
+% \begin{macro}{\extract@group}
+% Extracts ``group'' from |\group'field|.
+% \begin{macrocode}
+\def\extract@group#1{%
+ \@xp\extract@group@a\string#1\@nil
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\extract@group@a}
+% \begin{macrocode}
+\def\extract@group@a#1#2'{#2\@nilgobble}
+% \end{macrocode}
+% \end{macro}
+%
+% \section{Data structures}
+%
+% The result of scanning the key/value pairs is an assignment statement
+% for \cs{rsk@toks}. For example, consider the entry
+% \begin{verbatim}
+% \bib{LelekZ1962}{article}{
+% author={Lelek, A.},
+% author={Zaremba, D.},
+% title={Dimensions of irreducible ...},
+% journal={Fund. Math.},
+% date={1962/63},
+% }
+%\end{verbatim}
+% The scanned result is to assign
+% \begin{verbatim}
+% \global\rsk@toks{%
+% \set:bib'author{Lelek, A.}{}%
+% \set:bib'author{Zaremba, D.}{}%
+% \set:bib'title{Dimensions of irreducible ...}{}%
+% \set:bib'journal{Fund. Math.}{}%
+% \set:bib'date{1962/63}{}%
+% }
+%\end{verbatim}
+% The extra empty arguments on each line are for auxiliary
+% properties (see below).
+% What happens thereafter with \cs{rsk@toks} depends on the code in
+% the last arg of \cs{RestrictedSetKeys}.
+%
+% \section{Auxiliary properties}
+%
+% Unfortunately, the previous section isn't the entire story. In
+% addition to the values of each field, we need to store a set of
+% auxiliary properties associated with those values. Note that
+% properties are explicitly associated with \emph{values}, not with
+% keys, because each value of an additive key could have different
+% properties.
+%
+% All such extra data will be stored in a special field named
+% ``aux'', with embedded tags to indicate which field each piece of
+% the \fld{aux} field is associated with. The extra bits can be
+% extracted on demand using standard techniques, and the primary
+% value of each field is not burdened with any attachments, so that
+% comparisons or scanning of the field contents can remain as
+% simple as possible.
+%
+% Thus in practice there is at least one bit of auxiliary information
+% in every bib item, and our previous example would have the title
+% language indicated:
+% \begin{verbatim}
+% \DSK@def\bib'title{Eine Bemerkung zur Darstellung von Polynomen
+% \"{u}ber Verb\"{a}nden}%
+% \@append\bib'title\bib'aux{\selectlanguage{german}}%
+%\end{verbatim}
+%
+% \begin{macro}{\set@property}
+% \begin{macrocode}
+\def\set@property#1{%
+ \begingroup
+ \edef\@tempa{\extract@group#1}%
+ \edef\@tempa{%
+ \@nx\@append\@nx#1\@xp\@nx\csname \@tempa,aux\endcsname
+ }%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\get@property}
+% \begin{macrocode}
+% \get@property\destination\bib'title
+\def\get@property#1#2{%
+ \get@nth@property#1#2\m@ne
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\get@nth@property}
+% \begin{macrocode}
+% \get@nth@property\destination\bib'title N
+\def\get@nth@property#1#2#3{%
+ \begingroup
+ \edef\@tempa{\extract@group#2}%
+ \@tempcnta#3\relax
+ \@tempcntb\z@
+ \@xp\scan@properties\@xp#2\csname \@tempa,aux\endcsname
+ \edef\@tempa{\def\@nx#1{\@tempa}}%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\scan@properties}
+% \begin{macrocode}
+\def\scan@properties#1#2{%
+ \begingroup
+ \def\@tempa{#1}%
+ \let\@tempc\@empty
+ \@xp\find@property #2 \@nil\@nil
+ \edef\@tempa{\def\@nx\@tempa{\@tempc}}%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\find@property}
+% \begin{macrocode}
+\def\find@property#1#2{%
+ \ifx\@nil#1%
+ \else
+ \def\@tempb{#1}%
+ \ifx\@tempa\@tempb
+ \ifnum\@tempcnta<\z@
+ \def\@tempc{#2}%
+ \else
+ \advance\@tempcntb\@ne
+ \ifnum\@tempcntb=\@tempcnta
+ \def\@tempc{#2}%
+ \fi
+ \fi
+ \fi
+ \@xp\find@property
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\reset@property}
+% \begin{macrocode}
+\def\reset@property#1#2{%
+ \reset@nth@property#1\m@ne{#2}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\reset@nth@property}
+% \begin{macrocode}
+% \reset@nth@property\bib'title N VALUE
+\def\reset@nth@property#1#2#3{%
+ \begingroup
+ \edef\@tempa{\extract@group#1}%
+ \@tempcnta#2\relax
+ \@temptokena{#3}%
+ \toks@\@emptytoks
+ \@tempcntb\z@
+ \@xp\reset@scan\@xp#1\csname \@tempa,aux\endcsname
+ \edef\@tempa{%
+ \def\@xp\@nx\csname \@tempa,aux\endcsname{\the\toks@}%
+ }%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\reset@scan}
+% \begin{macrocode}
+\def\reset@scan#1#2{%
+ \begingroup
+ \def\@tempa{#1}%
+ \@xp\reset@scan@a #2 \@nil\@nil
+ \edef\@tempa{\toks@{\the\toks@}}%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\find@property}
+% \begin{macrocode}
+\def\reset@scan@a#1#2{%
+ \ifx\@nil#1%
+ \else
+ \def\@tempb{#1}%
+ \ifx\@tempa\@tempb
+ \ifnum\@tempcnta<\z@
+ \@temptokenb\@temptokena
+ \else
+ \advance\@tempcntb\@ne
+ \ifnum\@tempcntb=\@tempcnta
+ \@temptokenb\@temptokena
+ \fi
+ \fi
+ \else
+ \@temptokenb{#2}%
+ \fi
+ \edef\@tempb{%
+ \toks@{\the\toks@ \@nx#1{\the\@temptokenb}}%
+ }%
+ \@tempb
+ \@xp\reset@scan@a
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \section{Some machinery for finite state automata}
+%
+% Coincidentally I needed to write two finite state automaton parsers
+% for two related packages, so after writing them separately I spent
+% some time analyzing the code fragments they shared in common and
+% abstracted them so that the cs names could be shared.
+%
+% \begin{macro}{\fsa@l}
+% FSA lookahead.
+% \begin{macrocode}
+\def\fsa@l{\futurelet\@let@token\fsa@t}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fsa@b}
+% FSA bypass a token. Don't delete the space at the end!
+% \begin{macrocode}
+\def\fsa@b{\afterassignment\fsa@l \let\@let@token= }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fsa@c}
+% FSA copy a token (not space, bgroup, egroup).
+% \begin{macrocode}
+\def\fsa@c#1{\aftergroup#1\fsa@l}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fsa@n}
+% FSA next action. This is just a placeholder definition.
+% \begin{macrocode}
+\let\fsa@n\@empty
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fsa@t}
+% FSA test. This is just a placeholder definition.
+% \begin{macrocode}
+\let\fsa@t\@empty
+% \end{macrocode}
+% \end{macro}
+%
+% \section{Now some of the real work}
+%
+% \begin{macro}{\rsk@toks}
+% \begin{macrocode}
+\newtoks\rsk@toks
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rkvIfEmpty}
+% Beginning here.
+% \begin{macrocode}
+\def\rkvIfEmpty#1#2{%
+ \@xp\ifx\csname#1'#2\endcsname\@empty
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rkvIfAdditive}
+% \begin{macrocode}
+\def\rkvIfAdditive#1{%
+ \@xp\let\@xp\@let@token \csname \rkv@setter#1\endcsname
+ \afterassignment\@nilgobble
+ \@xp\let\@xp\@let@token \@let@token \@empty\@empty\@nil
+ \ifx\@let@token\DSK@append
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rkv@setter}
+% It irritates me that I can't embed the \ncn{csname} and
+% \ncn{endcsname} in here.
+% \begin{macrocode}
+\def\rkv@setter#1{set:\@xp\@gobble\string#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rkv@DSAK}
+% Define a simple, superseding, or additive key.
+% \begin{macrocode}
+\def\rkv@DSAK#1#2{%
+ \addto@group@reset#1{\let#1\@empty}%
+ \edef\@tempa{\def\csname \rkv@setter#1\endcsname}%
+ \@tempa{#2#1}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rkv@DDK}
+% This function is used for a dummy key whose value (expansion)
+% should be empty but that should appear non-empty to
+% \cs{rkvIfEmpty}.
+% \begin{macrocode}
+\def\rkv@DDK#1{%
+ \addto@group@reset#1{\def#1{\@empty}}%
+ \@xp\let\csname \rkv@setter#1\endcsname\@gobble
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DSK@def}
+% \begin{macrocode}
+\def\DSK@def#1{%
+ \ifx#1\@empty\else
+ \PackageWarningNoLine{rkeyval}%
+ {Key \string#1 should not be repeated}%
+ \fi
+ \DSK@redef#1%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DSK@redef}
+% We weed out empty field values for consistency with
+% \cs{DSK@append}.
+% \begin{macrocode}
+\def\DSK@redef#1#2{%
+ \@ifempty{#2}{\@gobble}{%
+ \def#1{#2}%
+ \set@property#1
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\init@group@reset}
+% \begin{macrocode}
+\def\init@group@reset#1{%
+ \begingroup
+ \edef\@tempb{\@xp\@nx\csname #1@reset\endcsname}%
+ \@xp\ifx\@tempb\relax
+ \@xp\xdef\@tempb{\let \csname #1,aux\endcsname\@nx\@empty}
+ \fi
+ \endgroup
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\addto@group@reset}
+% \begin{macrocode}
+\def\addto@group@reset#1{%
+ \begingroup
+ \edef\@tempa{\extract@group#1}%
+ \init@group@reset\@tempa
+ \edef\@tempa{%
+ \@nx\g@addto@macro\@xp\@nx\csname\@tempa @reset\endcsname
+ }%
+ \@xp\endgroup
+ \@tempa
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DefineSimpleKey}
+% \begin{macrocode}
+\newcommand{\DefineSimpleKey}[2]{%
+ \@xp\rkv@DSAK
+ \csname #1'#2\endcsname
+ {\DSK@def}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DefineSupersedingKey}
+% \begin{macrocode}
+\newcommand{\DefineSupersedingKey}[2]{%
+ \@xp\rkv@DSAK
+ \csname #1'#2\endcsname
+ {\DSK@redef}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DefineAdditiveKey}
+% \begin{macrocode}
+\newcommand{\DefineAdditiveKey}[3]{%
+ \@xp\rkv@DSAK
+ \csname #1'#2\endcsname
+ {\DSK@append#3}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DSK@append}
+% We weed out empty field values (e.g., |editor={}| or
+% \verb*|editor={ }|) because otherwise an additive field could
+% end up with a value like |\name{}| which appears non-empty to
+% \cs{rkvIfEmpty} but produces no output on the page.
+% \begin{macrocode}
+\def\DSK@append#1#2#3{%
+ \@ifempty{#3}{\@gobble}{%
+ \@append#1#2{#3}%
+ \set@property#2
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DefineDummyKey}
+% \begin{macrocode}
+\newcommand{\DefineDummyKey}[2]{%
+ \@xp\rkv@DDK \csname #1'#2\endcsname
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\RestrictedSetKeys}
+% \begin{macrocode}
+\newcommand{\RestrictedSetKeys}[3]{%
+ \global\rsk@toks\@xp{\csname #2@reset\endcsname}%
+ \def\rsk@finish{#3}%
+ \gdef\rsk@set{\@xp\rsk@set@a\csname#2'}%
+ #1\relax
+ \begingroup
+ \rsk@changecase
+ \aftergroup\rsk@set
+% \end{macrocode}
+% Start by removing the opening brace.
+% \begin{macrocode}
+ \let\fsa@t\rsk@z
+ \fsa@l
+}
+% \end{macrocode}
+% \end{macro}
+%
+% The aftergroup tokens end up looking like this:
+% \begin{verbatim}
+% \lowercase{\rsk@set FIELDNAME\endcsname}
+% --> \@xp\rsk@set@a\csname bib'fieldname\endcsname
+% --> \rsk@set@a\bib'abcdef
+%\end{verbatim}
+%
+% \begin{macro}{\rsk@unknown@key}
+% \begin{macrocode}
+\def\rsk@unknown@key#1{%
+ \PackageWarning{rkeyval}{Unknown key: \string#1}%
+ \@xp\def\csname\rkv@setter#1\endcsname {\DSK@redef#1}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \section{The state machine}
+%
+%\begin{verbatim}
+% State 0: Skip opening brace (\rsk@z).
+% space -> 0
+% { -> 2
+% other -> error "Missing open brace"
+%
+% State 1: Skip comma (\rsk@a).
+% space -> 1
+% \par -> 1
+% comma -> 2
+% @ -> read optional arg; 1
+% } -> 6
+% other -> error "Missing comma"; 2
+%
+% State 2: Find field name (\rsk@b).
+% space -> 2
+% \par -> 2
+% comma -> 2
+% letter -> 3
+% { -> error "Missing key name"; 4
+% } -> 6
+% other -> error "Invalid key name character"; 2
+%
+% State 3: Scan field name (\rsk@c).
+% letter -> 3
+% comma -> error "Invalid key name character"; 3
+% equal -> 4
+% other punct -> 3
+% space -> 4
+% { -> error "Missing equal sign"; 4
+% } -> error "Missing equal sign"; 4
+% other -> error "Invalid key name character"; 3
+%
+% State 4: Skip equals (\rsk@d).
+% space -> 4
+% equal -> 4
+% { -> 5
+% other -> error "Missing { for value of current key"; 5
+%
+% State 5: Read field value (\rsk@set@a).
+% any -> 1
+%
+% State 6: Done (\rsk@end).
+%\end{verbatim}
+%
+% \begin{macro}{\rsk@z}
+% State 0: Skip opening brace.
+% \begin{macrocode}
+\def\rsk@z{%
+ \ifx\bgroup\@let@token
+ \let\fsa@t\rsk@b
+ \let\fsa@n\fsa@b
+ \else
+ \ifx\@sptoken\@let@token
+ \let\fsa@n\fsa@b
+ \else
+ \rsk@errf
+ \fi
+ \fi
+ \fsa@n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@a}
+% State 1: Skip comma.
+% \begin{macrocode}
+\def\rsk@a{%
+ \ifx\@let@token\@sptoken
+ \let\fsa@n\fsa@b
+ \else
+ \ifx\@let@token\par
+ \let\fsa@n\fsa@b
+ \else
+ \ifx,\@let@token
+ \endgroup
+ \let\fsa@t\rsk@b
+ \let\fsa@n\fsa@b
+ \else
+ \ifx\egroup\@let@token
+ \endgroup
+ \let\fsa@n\rsk@end
+ \else
+ \endgroup
+ \let\fsa@n\rsk@erraa
+ \fi
+ \fi
+ \fi
+ \fi
+ \fsa@n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@b}
+% State 2: Find field name.
+%
+% Allow \cs{par} here to permit a blank line after
+% the end of one key-val pair and the start of the next (perhaps to
+% break up a long list into sections).
+% \begin{macrocode}
+\def\rsk@b{%
+ \ifcat\@nx\@let@token A%
+ \let\fsa@t\rsk@c
+ \let\fsa@n\fsa@c
+ \else
+ \ifx\@sptoken\@let@token
+ \let\fsa@n\fsa@b
+ \else
+ \rsk@bb
+ \fi
+ \fi
+ \fsa@n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@bb}
+% \begin{macrocode}
+\def\rsk@bb{%
+ \ifx,\@let@token
+ \let\fsa@n\fsa@b
+ \else
+ \ifx\bgroup\@let@token
+ \let\fsa@n\rsk@errb
+ \else
+ \ifx\egroup\@let@token
+ \let\fsa@n\rsk@end
+ \else
+ \ifx\par\@let@token
+ \let\fsa@n\fsa@b
+ \else
+ \let\fsa@n\rsk@errc
+ \fi
+ \fi
+ \fi
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@c}
+% State 3: Scan field name.
+% \begin{macrocode}
+\def\rsk@c{%
+ \ifcat\@nx\@let@token A%
+ \let\fsa@n\fsa@c
+ \else
+ \ifx\@sptoken\@let@token
+ \let\fsa@t\rsk@d
+ \let\fsa@n\fsa@b
+ \else
+ \ifx=\@let@token
+ \let\saw@equal T%
+ \let\fsa@t\rsk@d
+ \let\fsa@n\fsa@b
+ \else
+ \rsk@cb
+ \fi
+ \fi
+ \fi
+ \fsa@n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@cb}
+% \begin{macrocode}
+\def\rsk@cb{%
+ \ifx,\@let@token
+ \let\fsa@n\rsk@errc
+ \else
+ \ifcat\@nx\@let@token .%
+ \let\fsa@n\fsa@c
+ \else
+ \ifx\bgroup\@let@token
+ \let\fsa@n\rsk@noequal
+ \else
+ \ifx\egroup\@let@token
+ \let\fsa@n\rsk@noequal
+ \else
+ \let\fsa@n\rsk@errc
+ \fi
+ \fi
+ \fi
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\saw@equal}
+% \begin{macrocode}
+\let\saw@equal=F
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@d}
+% State 4: Skip equals.
+%
+% If no equal sign ever came
+% along, then give a warning about it and set \cs{saw@equal} to true
+% so that when \cs{rsk@noequal} cycles through again it will take the
+% other branch.
+% \begin{macrocode}
+\def\rsk@d{%
+ \ifx\bgroup\@let@token
+ \ifx\saw@equal T%
+ \aftergroup\endcsname
+ \rsk@endcase
+ \let\fsa@n\endgroup
+ \else
+ \let\saw@equal T%
+ \let\fsa@n\rsk@noequal
+ \fi
+ \else
+ \ifx\@sptoken\@let@token
+ \let\fsa@n\fsa@b
+ \else
+ \ifx=\@let@token
+ \let\saw@equal T%
+ \let\fsa@n\fsa@b
+ \else
+ \let\fsa@n\rsk@erre
+ \fi
+ \fi
+ \fi
+ \fsa@n
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@casesensitive}
+% \begin{macrocode}
+\def\rsk@casesensitive{%
+ \let\rsk@changecase\@empty
+ \let\rsk@endcase\@empty
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@startlc}
+% \begin{macrocode}
+\def\rsk@startlc{\aftergroup\lowercase\aftergroup{\iffalse}\fi}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@endlc}
+% \begin{macrocode}
+\def\rsk@endlc{\iffalse{\fi\aftergroup}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@lowercase}
+% \begin{macrocode}
+\def\rsk@lowercase{%
+ \let\rsk@changecase\rsk@startlc
+ \let\rsk@endcase\rsk@endlc
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\rsk@lowercase
+% \end{macrocode}
+%
+% \begin{macro}{\rsk@resume}
+% Here we get improved reporting of error context by changing
+% end-of-line to be different from normal space. If we don't find a
+% comma on the current line, assume it is an error.
+% \begin{macrocode}
+\def\rsk@resume{%
+ \begingroup
+ \rsk@changecase
+ \aftergroup\rsk@set
+ \let\fsa@t\rsk@a
+ \begingroup
+ \catcode\endlinechar=\active
+ \lccode`\~=\endlinechar
+ \lowercase{\let~\par}%
+ \fsa@l
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@set@a}
+% State 5: Read field value.
+% \begin{macrocode}
+\def\rsk@set@a#1#2{%
+ \star@{\rsk@set@b#1{#2}}{}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@set@b}
+% \begin{macrocode}
+\def\rsk@set@b#1#2#3{%
+ \@xp\ifx \csname\rkv@setter#1\endcsname \relax
+ \rsk@unknown@key#1%
+ \fi
+ \edef\@tempa{\@xp\@nx\csname \rkv@setter#1\endcsname}%
+ \toks@\@xp{\@tempa{#2}{#3}}%
+ \edef\@tempa{%
+ \global\rsk@toks{\the\rsk@toks \the\toks@}%
+ }%
+ \@tempa
+ \rsk@resume
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@end}
+% State 6: Done.
+%
+% Lets see, now why did I add this?
+% \begin{macrocode}
+\def\rsk@end{%
+ \global\let\rsk@set\rsk@terminate
+ \rsk@endcase
+ \endgroup
+ \endcsname
+ \afterassignment\rsk@finish
+ \toks@\bgroup
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@terminate}
+% \begin{macrocode}
+\def\rsk@terminate{\@xp\@gobble\csname}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\NoCommaWarning}
+% \begin{macrocode}
+\def\NoCommaWarning{\PackageWarning{rkeyval}{Missing comma}}%
+% \end{macrocode}
+% \end{macro}
+%
+%% % \begin{macro}{\NoCommaError}
+%% % \begin{macrocode}
+%% \def\NoCommaError{\rsk@err{Missing comma}\@ehc}
+%% % \end{macrocode}
+%% % \end{macro}
+%
+% \begin{macro}{\rsk@nocomma}
+% \begin{macrocode}
+\def\rsk@nocomma{\NoCommaWarning}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@err}
+% \begin{macrocode}
+\def\rsk@err{\PackageError{rkeyval}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@errf}
+% \begin{macrocode}
+\def\rsk@errf{\rsk@err{Missing open brace}\@ehc\rsk@b}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@erraa}
+% \begin{macrocode}
+\long\def\rsk@erraa{\rsk@nocomma \let\fsa@t\rsk@b \fsa@l}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@errb}
+% \begin{macrocode}
+\def\rsk@errb{\rsk@err{Missing key name}\@ehc\rsk@d}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@errc}
+% \begin{macrocode}
+\def\rsk@errc{\rsk@err{Invalid key name character}\@ehc\fsa@b}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\rsk@noequal}
+% \begin{macrocode}
+\def\rsk@noequal{\rsk@err{Missing equal sign}\@ehc\rsk@d}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\rsk@erre}
+% In this case we guess that the value is without braces but probably
+% terminated with a comma. We want to scan up to the comma in order
+% to get back in synch.
+% \begin{macrocode}
+\def\rsk@erre#1,{%
+ \rsk@err{Missing open brace for key value}\@ehc
+ \iffalse{\fi
+ \endgroup
+ \endcsname
+ \rsk@endcase }{#1},%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\PopCatcodes
+% \end{macrocode}
+%
+% The usual \cs{endinput} to ensure that random garbage at the end of
+% the file doesn't get copied by \fn{docstrip}.
+% \begin{macrocode}
+\endinput
+% \end{macrocode}
+%
+% \CheckSum{871}
+% \Finale