# \@ifpackagewith doesn't behave as I expected with global options

When I compile the following code, I expect a "Yes" but I get a "No" instead. Is there something subtle going on or is my method for checking wrong? If so, how can I see which options have been passed to babel? (It's for a class I'm writing.)

\@ifpackageloaded{babel} gives a "Yes", as does \usepackage[english]{babel}.

\documentclass[english]{article}
\usepackage{babel}
\newcommand{\control}{undefined}
\makeatletter
\@ifpackagewith{babel}{english}
{\renewcommand{\control}{Yes}}
{\renewcommand{\control}{No}}
\makeatother

\begin{document}
\control
\end{document}

• You aren't loading babel with the english option. ;-) Indeed, global options don't appear in the list associated to babel. – egreg Jan 14 '16 at 16:48
• @egreg Yes but why? Shouldn't global options be passed to every package, babel included? – Arch Stanton Jan 14 '16 at 16:50
• They are passed, but the macro \opt@babel.sty isn't updated with the global options: it only contains the “local” options. – egreg Jan 14 '16 at 16:50
• @egreg Is this a subtlety of babel only? I mean, should I always repeat the relevant options when I call babel, even if I declared them in the class too? – Arch Stanton Jan 14 '16 at 16:55
• No, it's for all packages. – egreg Jan 14 '16 at 17:07

When you load a package with

\usepackage[a,b,c]{package}


the macro \opt@package.sty is defined to expand to a,b,c; such a macro is used for \@ifpackagewith. However, global options are not included in the list. So, if the package know the option g and you do

\documentclass[g]{article}

\usepackage[a,b,c]{package}


the option g will be passed to package before the options a, b and c. However the code

\@ifpackagewith{g}{package}{YES}{NO}


will follow the false branch. Unfortunate? Yes.

Can you do something about this? Of course you can, because the global options are stored in \@classoptionslist, but there's no explicit interface for it.

The babel package relies on \date<language> being defined as a check that the language has been loaded, either as a global or a local option.

Here's an expl3 version of \@ifpackagewith that also checks the global options.

\documentclass[english]{article}
\usepackage{babel}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\packageoptionsTF}{mmmm}
{
\stanton_package_options:nnTF { #1 } { #2 } { #3 } { #4 }
}

\cs_new_protected:Nn \stanton_package_options:nnTF
{
\clist_map_inline:nn { #2 }
{
\clist_if_in:cnTF { opt@#1.sty } { ##1 }
{ #3 } % it's a local option
{
\clist_if_in:cnTF { @classoptionslist } { ##1 }
{ #3 } % it's a global option
{ #4 }
}
}
}
\ExplSyntaxOff

\newcommand{\control}{undefined}
\newcommand{\ita}{}

\packageoptionsTF{babel}{english}{%
\renewcommand{\control}{Yes}%
}{%
\renewcommand{\control}{No}%
}
\packageoptionsTF{babel}{italian}{%
\renewcommand{\ita}{Yes}%
}{%
\renewcommand{\ita}{No}%
}

\begin{document}

\control

\ita

\end{document}


The output is

Yes
No

• Thanks egreg. This ranks among the best pieces of code I've ever stolen from here :-) – Arch Stanton Jan 17 '16 at 0:13
• @ArchStanton You're welcome! I have even better code on the site, notwithstanding what a fellow chat resident might say. ;-) – egreg Jan 17 '16 at 0:16
• egreg, I don't understand the code, but I think it only checks if the options are given but not if the package is actually loaded, judging by how it behaves. – Arch Stanton Jan 17 '16 at 20:26
• @ArchStanton You're right: \@ifpackagewith doesn't balk if the package has not been actually loaded. It's the same for the code I proposed. Probably you want to add the check whether the package has been loaded. – egreg Jan 17 '16 at 20:28
• Sorry, I thought it did. – Arch Stanton Jan 17 '16 at 20:30