% \iffalse
%<*copyright>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% conv-xkv package                                      %%
%% Copyright (C) 2016--2017  D. P. Story                 %%
%%   dpstory@uakron.edu                                  %%
%%                                                       %%
%% This program can redistributed and/or modified under  %%
%% the terms of the LaTeX Project Public License         %%
%% Distributed from CTAN archives in directory           %%
%% macros/latex/base/lppl.txt; either version 1.2 of the %%
%% License, or (at your option) any later version.       %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%</copyright>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{conv-xkv}
%<package> [2017/01/09 v1.1b convert xkeyval format (dps)]
%<*driver>
\documentclass{ltxdoc}
\usepackage[colorlinks,hyperindex=false]{hyperref}
\OnlyDescription  % comment out for implementation details
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\InputIfFileExists{aebdocfmt.def}{\PackageInfo{web}{Inputting aebdocfmt.def}}
    {\def\IndexOpt{\DescribeMacro}\def\IndexKey{\DescribeMacro}\let\setupFullwidth\relax
     \PackageInfo{web}{aebdocfmt.def cannot be found}}
\begin{document}
\def\ameta#1{\ensuremath{\langle\textit{\texttt{#1}}\rangle}}
\def\meta#1{\textsl{\texttt{#1}}}
\def\darg#1{{\ttfamily\char123\relax#1\char125\relax}}
\def\CMD#1{\textbackslash#1}
\let\pkg\textsf
\let\opt\texttt
  \GetFileInfo{conv-xkv.sty}
  \title{\textsf{conv-xkv}: Convert \textsf{xkeyval} format style}
  \author{D. P. Story\\
    Email: \texttt{dpstory@uakron.edu}}
  \date{processed \today}
  \maketitle
  \tableofcontents
  \DocInput{conv-xkv.dtx}
\IfFileExists{\jobname.ind}{\newpage\setupFullwidth\par\PrintIndex}{\paragraph*{Index} The index goes here.\\Execute
    \texttt{makeindex -s gind.ist -o conv-xkv.ind conv-xkv.idx} on the command line and recompile
    \texttt{conv-xkv.dtx}.}
\IfFileExists{\jobname.gls}{\PrintChanges}{\paragraph*{Change History} The list of changes goes here.\\Execute
    \texttt{makeindex -s gglo.ist -o conv-xkv.gls conv-xkv.glo} on the command line and recompile
    \texttt{conv-xkv.dtx}.}
\end{document}
%</driver>
% \fi
%
% \MakeShortVerb{|}
% \DoNotIndex{\w,\x,\y,\z}
% \InputIfFileExists{aebdonotindex.def}{\PackageInfo{web}{Inputting aebdonotindex.def}}
%    {\PackageInfo{web}{cannot find aebdonotindex.def}}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%    \section{Introduction}
%    This is a intellectual exercise for creating \emph{alternate key-value} notation. The standard {\LaTeX}
%    notation is \texttt{\ameta{key}=\ameta{value}}. To change to the JavaScript object style of
%    key-values (\texttt{\ameta{key}:\ameta{value}}), use \cs{cxkvsetkeys} as you would \cs{setkeys}:
%\begin{quote}
%    \cs{cxkvsetkeys}\darg{\ameta{family}}\darg{\ameta{KV-pairs}}
%\end{quote}
%    to convert \texttt{\ameta{key}:\ameta{value}} to \texttt{\ameta{key}=\ameta{value}} and \pkg{xkeyval} processes the keys as it
%    normally does. The comma (,) separates sets of key-value pairs and must not, therefore, be used as the delimiter
%    that separates the \ameta{key} from the \ameta{value}.
%
%    The package is more general than what is described above. You can define several key-value delimiters, for whatever reason,
%    in your document or package. Declare a \textit{named} delimiter:
%\begin{quote}\ttfamily
%\cs{DeclareDelimiter}\darg{\ameta{name}}\darg{\ameta{delimiter}}
%\end{quote}
%Use the newly declared delimited as follows:
%\begin{quote}
%\cs{cxkvsetkeys(\ameta{name})}\darg{\ameta{family}}\darg{\ameta{KV-pairs}}
%\end{quote}
%The case of using a colon (:) for the delimiter is already defined, its name is `\texttt{colon}' and need not be declared.
%
%\paragraph*{Important change in syntax} With version dated 2017/01/03 or later, the optional argument \ameta{name} is now delimited
%by \textbf{parentheses}, rather than the standard brackets. This is to be able to detect \ameta{name} when the full syntax
%of \cs{setkeys} is used:
%\changes{v1.1a}{2017/01/03}{Change in syntax, use parentheses rather than brackets}
%\begin{quote}
%   \cs{setkeys*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}}
%\end{quote}
%The syntax for \cs{cxkvsetkeys} shall be
%\begin{quote}
%   \cs{cxkvsetkeys(\ameta{name})*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}}
%\end{quote}
% The \pkg{conv-xkv} package does nothing with \pkg{xkeyval} arguments \texttt{*[\ameta{prefix}]} and
% \texttt{[\ameta{na}]} other than to collect them and pass them on to \cs{setkeys} at the appropriate time.
% The \pkg{conv-xkv} is concerned only with converting a new notation \texttt{\ameta{key}\ameta{delim}\ameta{value}}
% to \texttt{\ameta{key}=\ameta{value}}.
%
% If the key-values do not contain the designated delimiter, \pkg{conv-xkv} simply passes everything on to \cs{setkeys}.
% What this means is that, for example, both \cs{cxkvsetkeys\darg{myfam}\darg{fname:Don,lname:Story}} works as does
% \cs{cxkvsetkeys\darg{myfam}\darg{fname=Don,lname=Story}}. One then has the option of using the standard notation
% or an alternate notation.
%
%
% \paragraph*{Demo file} The example file is \texttt{convert2xkeyval.tex}, use it to explore the possibilities and is found
%    in the \texttt{examples} folder of this distribution.
%
%    \section{Preliminaries}
%    We require the \pkg{xkeyval} package.
%    \changes{v1.0}{2016/12/20}{Date of first upload to CTAN}
%     \begin{macrocode}
\RequirePackage{xkeyval}
%    \end{macrocode}
%    The code below is taken from \pkg{hyperref}, and set and restore commands are renamed. This hopefully makes
%    a number of special characters available to act as a delimiter.
%    \begin{macrocode}
\begingroup
  \@makeother\`%
  \@makeother\=%
  \edef\x{%
    \edef\noexpand\x{%
      \endgroup
      \noexpand\toks@{%
        \catcode 96=\noexpand\the\catcode`\noexpand\`\relax
        \catcode 61=\noexpand\the\catcode`\noexpand\=\relax
      }%
    }%
    \noexpand\x
  }%
\x
\@makeother\`
\@makeother\=
\def\ckv@SetCatcodes{%
  \@makeother\`%
  \@makeother\=%
  \@makeother\~%
  \catcode`\$=3 %
  \catcode`\&=4 %
  \catcode`\^=7 %
  \catcode`\_=8 %
  \@makeother\|%
  \@makeother\:%
  \@makeother\(%
  \@makeother\)%
  \@makeother\[%
  \@makeother\]%
  \@makeother\/%
  \@makeother\!%
  \@makeother\<%
  \@makeother\>%
  \@makeother\.%
  \@makeother\;%
  \@makeother\+%
  \@makeother\-%
  \@makeother\"%
  \@makeother\'%
}
\begingroup
  \def\x#1{\catcode`\noexpand#1=\the\catcode`#1\relax}%
  \xdef\ckv@RestoreCatcodes{%
    \the\toks@
    \x\~%
    \x\$%
    \x\&%
    \x\^%
    \x\_%
    \x\|%
    \x\:%
    \x\(%
    \x\)%
    \x\[%
    \x\]%
    \x\/%
    \x\!%
    \x\<%
    \x\>%
    \x\.%
    \x\;%
    \x\+%
    \x\-%
    \x\"%
    \x\'%
  }%
\endgroup
\ckv@SetCatcodes
%    \end{macrocode}
%    \section{Core commands for this package}
%    The default delimiter is the colon (:).
%    \begin{macrocode}
\def\csarg#1#2{\expandafter#1\csname#2\endcsname}
\csarg\def{kvdelim-colon}{:}
%    \end{macrocode}
%    Use \DescribeMacro{\usekvdelim}\cs{usekvdelim} to display delimiter, as associated with
%    the argument \texttt{\#1}.
%    \begin{macrocode}
\def\usekvdelim#1{\@nameuse{kvdelim-#1}}
%    \end{macrocode}
%    \begin{macro}{\DeclareDelimiter}
%    In the preamble, we declare the delimiter to be used. The command takes on argument, which
%    is the delimiter to be used, for example `\texttt{:}' or `\texttt{->}'. If this declaration
%    does not appear in the preamble, the delimited is taken to be `\texttt{:}'.
%    \begin{macrocode}
\def\DeclareDelimiter{\ckv@SetCatcodes\DeclareDelimiter@i}
\def\DeclareDelimiter@i#1#2{\@ifundefined{kvdelim-#1}
    {\csarg\def{kvdelim-#1}{#2}\ckv@RestoreCatcodes\cxkvSetup{#1}}
    {\ckv@RestoreCatcodes}}
\@onlypreamble\DeclareDelimiter
%    \end{macrocode}
%    \end{macro}
%    \cs{cxkv@tmptoks} is used to hold the converted key-values, the contents of
%    this token register is passed to \cs{setkeys} in \cs{cxkv@cnvrtDelimniiEquali}
%    \begin{macrocode}
\newtoks\cxkv@tmptoks \cxkv@tmptoks={}
\def\cxkv@dummy{dummy}
\def\cxkv@dummyc{dummy,}
\bgroup
    \catcode`\#=12\relax\gdef\cxkvarg{#}
    \obeyspaces\gdef\cxkv@TAB{    }
\egroup
%    \end{macrocode}
%    \begin{macro}{\cxkvsetkeys}
%    This is the default definition, setup for using the colon (:) as the key-value
%    delimiter. But these next two commands are redefined by the
%    \cs{DeclareDelimiter} command in the preamble. The syntax is
%\begin{quote}
%\cs{cxkvsetkeys[\ameta{name}]}\darg{\ameta{family}}\darg{\ameta{KV-pairs}}
%\end{quote}
%where \ameta{KV-pairs} are the key-value pairs using the declared delimiter.
%\begin{quote}\ttfamily
%\cs{cxkvsetkeys}\darg{myfam}\{fname:\,Fred,lname:\,Flintstone\}
%\end{quote}
%The family \texttt{myfam} and keys \texttt{fname} and \texttt{lname} must have been defined
%earlier: If the optional argument is not specified, then it is assumed the \ameta{name} argument
%is \texttt{colon}, a reserved word for this package for this argument.
%\begin{verbatim}
%   \define@key{myfam}{\def\fname{#1}}
%   \define@key{myfam}{\def\lname{#1}}
%\end{verbatim}
%    \begin{macrocode}
\def\cxkv@colon{colon}
%    \end{macrocode}
%    The general form for \cs{setkeys} is
%\begin{quote}
%   \cs{setkeys*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}}
%\end{quote}
%    \begin{macro}{\cxkvsetkeys}
%The syntax for \cs{cxkvsetkeys} shall be
%\begin{quote}
%   \cs{cxkvsetkeys(\ameta{name})*[\ameta{prefix}]\darg{\ameta{families}}[\ameta{na}]\darg{\ameta{keys}}}
%\end{quote}
%The process to pick up the full parameter set of \cs{setkeys} is lengthy.
%    \begin{macrocode}
\newcommand\cxkvsetkeys{%
    \@ifnextchar({\cxkvsetkeys@i}{\cxkvsetkeys@i(colon)}}
\def\cxkvsetkeys@i(#1){\cxkvsetkeys@ii{#1}}
\def\cxkvsetkeys@ii#1{\def\cxkv@delimname{#1}\@ifstar
    {\def\cxkv@skOpts{*}\cxkvsetkeys@iii}
    {\def\cxkv@skOpts{}\cxkvsetkeys@iii}}
\newcommand\cxkvsetkeys@iii[2][]{\def\@rgi{#1}\ifx\@rgi\@empty
    \expandafter\def\expandafter\cxkv@skOpts
        \expandafter{\cxkv@skOpts{#2}}\else
    \expandafter\def\expandafter
        \cxkv@skOpts\expandafter{\cxkv@skOpts[#1]{#2}}\fi
    \def\thisxkvF@mily{#2}\cxkvsetkeys@iv}
\newcommand\cxkvsetkeys@iv[2][]{\def\@rgi{#1}\ifx\@rgi\@empty\else
    \expandafter\def\expandafter\cxkv@skOpts
        \expandafter{\cxkv@skOpts[#1]}\fi
    \expandafter\cxkvsetkeys@v\expandafter{\thisxkvF@mily}{#2}}
\def\cxkvsetkeys@v#1#2{\cxkv@skipfalse
    \ifx\cxkv@delimname\cxkv@colon\else
        \InputIfFileExists{xkv-\cxkv@delimname.cut}
        {\PackageInfo{conv-xkv}{Inputting xkv-\cxkv@delimname.cut}}
        {\PackageInfo{conv-xkv}{Cannot find xkv-\cxkv@delimname.cut}}\fi
    \@nameuse{cxkvsetkeys-\cxkv@delimname}{#1}{#2}}
%    \end{macrocode}
%    \end{macro}
%    \begin{macrocode}
\csarg\def{cxkvsetkeys-colon}#1#2{%
    \def\thisxkvF@mily{#1}\def\thisxkvV@lues{#2}\def\cxkv@scratch{}%
    \cxkv@tmptoks={}%
    \@nameuse{cxkv@cnvrtDelimniiEqual-colon}#2,dummy:dummy,\@nil}
\csarg\def{cxkv@cnvrtDelimniiEqual-colon}#1:#2,#3\@nil{%
    \cxkv@cnvrtDelimniiEquali{colon}{#1}{#2}{#3}}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{\cxkvSetup}
%    Write the definitions of \cs{cxkvsetkeys} and \cs{cxkv@cnvrtDelimniiEqual} to
%    the file \texttt{conv-xkv.cut} then input this file back in.
%    \begin{macrocode}
\def\cxkvSetup#1{\bgroup
\IfFileExists{xkv-#1.cut}{\PackageInfo{conv-xkv}{xkv-#1.cut
    already exists,\MessageBreak will not create another one}}{%
    \PackageInfo{conv-xkv}{Creating the file xkv-#1.cut
        containing\MessageBreak required definitions}%
    \newwrite \cxkv@write
    \uccode`c=`\%
    \def\w{#1}\def\x{cxkvsetkeys-#1}%
    \def\y{cxkv@cnvrtDelimniiEqual-#1}%
    \def\z{kvdelim-#1}%
    \immediate\openout \cxkv@write xkv-#1.cut
    \immediate\write\cxkv@write{\string\makeatletter}%
    \uppercase{\immediate\write\cxkv@write{\string
        \csarg\string\def{\y}\cxkvarg1\@nameuse{\z}%
        \cxkvarg2,\cxkvarg3\string\@nil{c^^J\cxkv@TAB
        \string\cxkv@cnvrtDelimniiEquali{\w}{\cxkvarg1}%
        {\cxkvarg2}{\cxkvarg3}}}}
    \uppercase{\immediate\write\cxkv@write{\string\csarg\string\def
        {\x}\cxkvarg1\cxkvarg2{c^^J\cxkv@TAB
        \string\def\string\thisxkvF@mily{\cxkvarg1}\string
        \def\string\thisxkvV@lues{\cxkvarg2}\string
        \let\string\cxkv@scratch\string\@empty\string
        \cxkv@tmptoks={}c^^J\cxkv@TAB
        \string\@nameuse{\y}\cxkvarg2,%
        \cxkv@dummy\@nameuse{\z}\cxkv@dummy,\string\@nil}}}
    \immediate\write\cxkv@write{\string\makeatother}%
    \immediate\closeout \cxkv@write
}%
\egroup}
%    \end{macrocode}
%    \end{macro}
%    \cs{cxkv@cnvrtDelimniiEquali} continues \cs{cxkv@cnvrtDelimniiEqual}. It is
%    the part that does not need to be redefined.
%    \begin{macrocode}
\newif\ifcxkv@keyonly \cxkv@keyonlyfalse
\def\cxkv@comma{,}
\def\cxkv@removecomma#1,\@nil{\def\cxkv@key{#1}}
\def\cxkv@parsecomma#1,#2\@nil{\def\@rgi{#1}\def\@rgii{#2}%
    \ifx\@rgii\@empty\cxkv@keyonlyfalse\else
        \cxkv@keyonlytrue\cxkv@removecomma#2\@nil\fi}
\newif\ifcxkv@skip \cxkv@skipfalse
\def\cxkv@cnvrtDelimniiEquali#1#2#3#4{%
    \def\cxkv@rgiii{#3}\def\cxkv@rgiv{#4}%
%    \end{macrocode}
%    If the fourth argument is empty, that means there were no delimiters in the argument,
%    so we pass the original argument \cs{thisxkvF@mily} to \cs{setkeys}.
%    \changes{v1.1}{2017/01/03}{Try to detect if the expected delimiter is present at all}
%    \begin{macrocode}
    \ifx\thisxkvV@lues\@empty\else
        \ifx\cxkv@rgiv\@empty
            \edef\cxkv@next{\noexpand
                \setkeys\cxkv@skOpts{\thisxkvV@lues}}%
            \cxkv@skiptrue
        \fi
    \fi
    \let\thisxkvV@lues\@empty
    \ifcxkv@skip\else
    \ifx\cxkv@rgiii\cxkv@dummy
        \cxkv@parsecomma#2,\@nil
        \ifcxkv@keyonly
            \edef\cxkv@tmp{\the\cxkv@tmptoks,\@rgi}%
            \cxkv@tmptoks=\expandafter{\cxkv@tmp}%
            \edef\cxkv@scratch{\the\cxkv@tmptoks}%
            \edef\cxkv@next{\noexpand
                \setkeys\cxkv@skOpts{\the\cxkv@tmptoks}}%
        \else
            \edef\cxkv@next{\noexpand
                \setkeys\cxkv@skOpts{\the\cxkv@tmptoks}}%
        \fi
    \else
        \cxkv@parsecomma#2,\@nil
        \ifcxkv@keyonly
            \edef\cxkv@tmp{\the\cxkv@tmptoks,\@rgi}%
            \cxkv@tmptoks=\expandafter{\cxkv@tmp}%
            \edef\cxkv@scratch{\the\cxkv@tmptoks}%
            \edef\cxkv@next{\noexpand
                \@nameuse{cxkv@cnvrtDelimniiEqual-#1}\cxkv@key
                \@nameuse{kvdelim-#1}#3,#4\noexpand\@nil}
        \else
            \cxkv@tmptoks=\expandafter{\cxkv@scratch,#2=#3}%
            \edef\cxkv@scratch{\the\cxkv@tmptoks}%
            \def\cxkv@next{%
                \@nameuse{cxkv@cnvrtDelimniiEqual-#1}#4\@nil}\fi
    \fi\fi\cxkv@next
}
\ckv@RestoreCatcodes
%    \end{macrocode}
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%  \Finale
\endinput
