# keyval: pass unknown options to a command

Consider the following MWE:

\documentclass[a4paper]{article}
\usepackage{keyval}

\makeatletter
\define@key{my}{foo}[]{foo is enabled\par}
\define@key{my}{bar}[0]{bar is set to #1\par}
\makeatother

\newcommand{\somecommand}[2][none]{arg 1: #1, arg 2: #2}
\newcommand{\othercommand}[2][]{\setkeys{my}{#1} \somecommand{#2}}

\begin{document}

\othercommand[foo,bar=9]{baz}

\end{document}


This works fine as long as the only optional arguments passed to \othercommand are either foo or bar. Since these are optional arguments, they may be omitted as well. But what I would like to do now is the following: if there is another optional argument given, not known to the \othercommand, it shall be passed to the \somecommand. Example: if I write

\othercommand[foo, bar=29, zap=12]{baz}

I want the options foo and bar still be processed by the \othercommand. But the zap=12 option is not known, so it shall be passed to \somecommand without modification, such that I can write

\newcommand{\othercommand}[2][]{\setkeys{my}{#1} \somecommand[pass unknown options here]{#2}}

For documentclasses, there is a command like "pass options to class" which passes unknown documentclass options to the underlying document class. I wonder whether such a mechanism can be used here as well.

• Does \somecommand also accept foo and bar as options? – Werner Jan 31 '19 at 20:19
• keyval has a command \KV@errx that you could redefine to handle unknown keys, but imho it would be better to use a newer keyval package like l3keys from expl3 which has built-in handlers for this case. – Ulrike Fischer Jan 31 '19 at 20:41
• Werner, the \somecommand only accepts zap as optional argument, but neither foo nor bar. So these should not be passed to the \somecommand. – T. Pluess Feb 2 '19 at 17:38
• Ulrike, could you show a sample? I have absolutely no idea about this l3 topic. I have never used it before and the syntax seems even more complicated to me than the "old" syntax does. Perhaps I am not smart enough :-) – T. Pluess Feb 2 '19 at 17:39

The l3keys module of LaTeX3 has the function \keys_set_known:nnN which stores every unknown key in a token list.

You could use it in the following way:

\documentclass[]{article}

% Inside of \ExplSyntaxOn ... \ExplSyntaxOff spaces are ignored and you have to
% use ~ to insert a space instead. Also _ and : can be part of macro names which
% is used to add some structure.
\ExplSyntaxOn
\keys_define:nn { my }
{
foo .code:n = { foo ~ is ~ enabled \par }
,foo .value_forbidden:n = { true }
,bar .code:n = { bar ~ is ~ set ~ to ~ #1\par }
,bar .default:n = { 0 }
}
\tl_new:N \l__my_unknown_keys_tl
\NewDocumentCommand \othercommand { O{} m }
{
\group_begin:
\keys_set_known:nnN { my } {#1} \l__my_unknown_keys_tl
\exp_args:NNo \somecommand [ \l__my_unknown_keys_tl ] {#2}
\group_end:
}
\NewDocumentCommand \somecommand { O{none} m }
{
arg ~ 1: ~ #1, ~ arg ~ 2: ~ #2\par
}
\ExplSyntaxOff

\begin{document}
\othercommand{baz}
\othercommand[foo, bar=9]{baz}
\othercommand[foo, bar=9, zip=12]{baz}
\end{document}


If you want to only use the optional argument of \somecommand if there were any unknown keys and else stick to its default, you could use:

\documentclass[]{article}

% Inside of \ExplSyntaxOn ... \ExplSyntaxOff spaces are ignored and you have to
% use ~ to insert a space instead. Also _ and : can be part of macro names which
% is used to add some structure.
\ExplSyntaxOn
\keys_define:nn { my }
{
foo .code:n = { foo ~ is ~ enabled \par }
,foo .value_forbidden:n = { true }
,bar .code:n = { bar ~ is ~ set ~ to ~ #1\par }
,bar .default:n = { 0 }
}
\tl_new:N \l__my_unknown_keys_tl
\NewDocumentCommand \othercommand { O{} m }
{
\group_begin:
\keys_set_known:nnN { my } {#1} \l__my_unknown_keys_tl
\tl_if_empty:NTF \l__my_unknown_keys_tl
{ \somecommand {#2} }
{ \exp_args:NNo \somecommand [ \l__my_unknown_keys_tl ] {#2} }
\group_end:
}
\NewDocumentCommand \somecommand { O{none} m }
{
arg ~ 1: ~ #1, ~ arg ~ 2: ~ #2\par
}
\ExplSyntaxOff

\begin{document}
\othercommand{baz}
\othercommand[foo, bar=9]{baz}
\othercommand[foo, bar=9, zip=12]{baz}
\end{document}


The following is an example how this could be done with expkv. Note that the unknown key isn't forwarded exactly like it was passed in (that information is lost when expkv has parsed the current key=val pair), but in a way in which most key=value packages would parse an equivalent value of pair (I say most because some of the wide spread packages have some issues regarding brace stripping, most notably it is possible that pgfkeys doesn't parse correctly something which is passed in as key= {value}).

\documentclass[]{article}

\usepackage{expkv}

\makeatletter
\ekvdefNoVal{my}{foo}{foo is enabled\par}
\ekvdef{my}{bar}{bar is set to #1\par}
\ekvdefNoVal{my}{bar}{bar is set to 0\par}
{%
\ifx\@empty#1%
\def#1{#2}%
\else
\edef#1{\unexpanded\expandafter{#1,#2}}%
\fi
}
\newcommand*\pluess@my@unknown@list{}
\ekvsetdef\my@set{my}

\newcommand\othercommand[2][]
{%
\begingroup
\let\pluess@my@unknown@list\@empty
\my@set{#1}%
\expandafter\somecommand\expandafter
[\expandafter{\pluess@my@unknown@list}]%
{#2}%
\endgroup
}
\newcommand\somecommand[2][none]{arg 1: #1, arg 2: #2\par}
\makeatother

\begin{document}
\othercommand{baz}
\othercommand[foo, bar=9]{baz}
\othercommand[foo, bar=9, zip=12]{baz}
\end{document}


If you want to only use the optional argument of \somecommand if there were any unknown keys and else stick to its default, you could use:

\documentclass[]{article}

\usepackage{expkv}

\makeatletter
\ekvdefNoVal{my}{foo}{foo is enabled\par}
\ekvdef{my}{bar}{bar is set to #1\par}
\ekvdefNoVal{my}{bar}{bar is set to 0\par}
{%
\ifx\@empty#1%
\def#1{#2}%
\else
\edef#1{\unexpanded\expandafter{#1,#2}}%
\fi
}
\newcommand*\pluess@my@unknown@list{}
\ekvsetdef\my@set{my}

\newcommand\othercommand[2][]
{%
\begingroup
\let\pluess@my@unknown@list\@empty
\my@set{#1}%
\ifx\pluess@my@unknown@list\@empty
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\somecommand}%
{%
\expandafter\somecommand\expandafter
[\expandafter{\pluess@my@unknown@list}]%
}%
{#2}%
\endgroup
}
\newcommand\somecommand[2][none]{arg 1: #1, arg 2: #2\par}
\makeatother

\begin{document}
\othercommand{baz}
\othercommand[foo, bar=9]{baz}
\othercommand[foo, bar=9, zip=12]{baz}
\end{document}