% \iffalse meta-comment
%
%% File: l3keys2e.dtx
%
% Copyright (C) 2009-2025 The LaTeX Project
%
% 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
%
%    https://www.latex-project.org/lppl.txt
%
% This file is part of the "l3packages bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/latex3/latex3
%
% for those people who are interested.
%
%<*driver|package>
% The version of expl3 required is tested as early as possible, as
% some really old versions do not define \ProvidesExplPackage.
\RequirePackage{expl3}[2020/02/08]
%<package>\@ifpackagelater{expl3}{2020/02/08}
%<package>  {}
%<package>  {%
%<package>    \PackageError{l3keys2e}{Support package l3kernel too old}
%<package>      {%
%<package>        Please install an up to date version of l3kernel\MessageBreak
%<package>        using your TeX package manager or from CTAN.\MessageBreak
%<package>        \MessageBreak
%<package>        Loading l3keys2e will abort!%
%<package>      }%
%<package>    \endinput
%<package>  }
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\usepackage{amstext}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3keys2e} package\\
%     \LaTeXe{} option processing using \LaTeX3 keys^^A
% }
%
% \author{^^A
%  The \LaTeX{} Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2025-10-09}
%
% \maketitle
%
% \begin{documentation}
%
% \section{Introduction}
% 
% \begin{center}
%   \bfseries This package is obsolete with the June 2022 \LaTeX{} release.
% \end{center}
%
% With new formats, \cs{ProcessKeyOptions} is available as a more powerful
% version of \cs{ProcessKeysOptions}. In particular, it is built directly
% into the \LaTeX{} kernel, uses `raw' options (and therefore does not
% suffer from space stripping and expansion issues), and can avoid
% option clash warnings. Package authors should transition their
% code to the new approach.
%
% \section{Old introduction}
%
% The key--value method for optional arguments is very popular, as it
% allows the class or package author to define a large number of
% options with a simple interface. The \pkg{expl3} bundle of \LaTeX3
% base code includes the module \pkg{l3keys} for defining keys, but
% to use these when loading \LaTeXe\ packages and classes requires
% extra support. That support is provided by this small package, which
% is intended to enable \LaTeXe\ packages to benefit from the power of
% the \LaTeX3 key--value system.
%
% \subsection{Creating and using keyval options}
%
% As with any key--value input, using key--value pairs as package or
% class options has two parts. The first stage is to define one or
% more keys, using the \cs{keys_define:nn} function. For example, an
% option which simply stores a value would be created using:
% \begin{verbatim}
%  \keys_define:nn { module }
%    { option .tl_set:N = \l_module_variable_tl }
% \end{verbatim}
% On its own, this will not make the key an option for the package or
% class containing the definition. The second stage is therefore to
% process the current options, searching for applicable keys.
%
% \begin{function}{\ProcessKeysOptions}
%   \begin{syntax}
%     \cs{ProcessKeysOptions} \Arg{module}
%   \end{syntax}
%   The \cs{ProcessKeysOptions} function is used to check the current
%   option list against the keys defined for \Arg{module}. Global (class)
%   options and local (package) options are checked when this function
%   is called in a package. Each option which does match a key name is
%   then used to attempt to set the appropriate key using
%   \cs{keys_set:nn}. For example, the option defined earlier would be
%   processed by the line
%   \begin{verbatim}
%     \ProcessKeysOptions { module }
%   \end{verbatim}
% \end{function}
%
% \begin{function}{\ProcessKeysPackageOptions}
%   \begin{syntax}
%     \cs{ProcessKeysPackageOptions} \Arg{module}
%   \end{syntax}
%   This function works in a similar manner to \cs{ProcessKeysOptions}.
%   When used in a \LaTeXe{} package, \cs{ProcessKeysPackageOptions}
%   will not examine any class options available. In contrast,
%   \cs{ProcessKeysOptions} does parse class options (in common with the
%   \LaTeXe{} kernel function \cs{ProcessOptions}).
% \end{function}
%
% When passing unknown keys to other packages, the standard \LaTeX{}
% \tn{CurrentOption} command is available and should be used. In contrast
% to \cs{l_keys_key_str}, \cs{CurrentOption} is a token list and thus retains
% category code information. Depending on how options are used by third-party
% packages, this may be essential for the option to be recognized.
%
%\end{documentation}
%
%\begin{implementation}
%
%\subsection{\pkg{l3keys2e} implementation}
%
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=keys>
%    \end{macrocode}
%
%    \begin{macrocode}
\ProvidesExplPackage{l3keys2e}{2025-10-09}{}
  {LaTeX2e option processing using LaTeX3 keys}
%    \end{macrocode}
%
% Allow for a kernel-based implementation
%    \begin{macrocode}
\cs_if_exist:NT \ProcessKeysOptions
  { \file_input_stop: }
%    \end{macrocode}
%
% Non-standard variants.
%    \begin{macrocode}
\cs_generate_variant:Nn \clist_put_right:Nn { Nv }
\cs_generate_variant:Nn \keys_if_exist:nnT  { nx }
\cs_generate_variant:Nn \keys_if_exist:nnTF { nx }
%    \end{macrocode}
%
% \begin{macro}{\l_@@_latexe_options_clist}
%   A single list is used for all options, into which they are collected
%   before processing.
%    \begin{macrocode}
\clist_new:N \l_@@_latexe_options_clist
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_process_class_bool}
%   A flag to indicate that class options should be processed for
%   packages.
%    \begin{macrocode}
\bool_new:N \l_@@_process_class_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_options:n}
%   The main function calls functions to collect up the global and local
%   options into \cs{l_@@_latexe_options_clist} before calling the
%   underlying functions to actually do the processing. So that a suitable
%   message is produced if the option is unknown, the special
%   \texttt{unknown} key is set if it does not already exist for the
%   current module.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_latexe_options:n #1
  {
    \clist_clear:N \l_@@_latexe_options_clist
    \@@_latexe_options_global:n {#1}
    \@@_latexe_options_local:
    \keys_if_exist:nnF {#1} { unknown }
      {
        \keys_define:nn {#1}
          {
            unknown .code:n =
              {
                \msg_error:nnee { keyvalue } { option-unknown }
                  { \l_keys_key_str } { \@currname }
              }
          }
        \AtEndOfPackage
          { \keys_define:nn {#1} { unknown .undefine: } }
      }
    \clist_map_inline:Nn \l_@@_latexe_options_clist
      {
        \tl_set:Nn \CurrentOption {##1}
        \keys_set:nn {#1} {##1}
      }
    \AtEndOfPackage { \cs_set_eq:NN \@unprocessedoptions \scan_stop: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_options_global:n}
%   Global (class) options are handled differently for \LaTeXe{} packages
%   and classes. Hence this function is essentially a check on the current
%  file type. The initial test is needed as \LaTeXe{} allows variables to
%   be equal to \cs{scan_stop:}, which is forbidden in \LaTeX3 code.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_latexe_options_global:n #1
  {
    \cs_if_eq:NNF \@classoptionslist \scan_stop:
      {
        \cs_if_eq:NNTF \@currext \@clsextension
          { \@@_latexe_options_class:n {#1} }
          {
            \bool_if:NT \l_@@_process_class_bool
             { \@@_latexe_options_package:n {#1} }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_options_class:n}
%   For classes, each option (stripped of any content after |=|)
%   is checked for existence as a key. If found, the option is added to
%   the combined list for processing. On the other hand, unused options
%   are stored up in \cs{@unusedoptionlist}. Before any of that, though,
%   there is a simple check to see if there is an |unknown| key. If there
%   is, then \emph{everything} will match and the mapping can be skipped.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_latexe_options_class:n #1
  {
    \cs_if_free:cF { opt@ \@currname . \@currext }
      {
        \keys_if_exist:nnTF {#1} { unknown }
          {
            \clist_put_right:Nv \l_@@_latexe_options_clist
              { opt@ \@currname . \@currext }
          }
          {
            \clist_map_inline:cn { opt@ \@currname . \@currext }
              {
                \keys_if_exist:neTF {#1} { \@@_latexe_remove_equals:n {##1} }
                  { \clist_put_right:Nn \l_@@_latexe_options_clist {##1} }
                  { \clist_put_right:Nn \@unusedoptionlist {##1} }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_options_package:n}
%   For global options when processing a package, the tasks are slightly
%   different from those for a class. The check is the same, but here
%   there is nothing to do if the option is not applicable. Each valid
%   option also needs to be removed from \cs{@unusedoptionlist}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_latexe_options_package:n #1
  {
    \clist_map_inline:Nn \@classoptionslist
      {
        \keys_if_exist:neT {#1} { \@@_latexe_remove_equals:n {##1} }
          {
            \clist_put_right:Nn \l_@@_latexe_options_clist {##1}
            \clist_remove_all:Nn \@unusedoptionlist {##1}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_options_local:}
%   If local options are found, the are added to the processing list.
%   \LaTeXe{} stores options for each file in a macro which may or may not
%   exist, hence the need to use \cs{cs_if_exist:c}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_latexe_options_local:
  {
    \cs_if_eq:NNF \@currext \@clsextension
      {
        \cs_if_exist:cT { opt@ \@currname . \@currext }
          {
            \exp_args:NNc \clist_put_right:NV \l_@@_latexe_options_clist
              { opt@ \@currname . \@currext }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_latexe_remove_equals:n}
% \begin{macro}{\@@_latexe_remove_equals:w}
%   As the name suggests, this is a simple function to remove an equals
%   sign from the input. This is all wrapped up in an \texttt{n} function
%   so that there will always be a sign available.
%    \begin{macrocode}
\cs_new:Npn \@@_latexe_remove_equals:n #1
  { \@@_latexe_remove_equals:w #1 = \s_@@_stop }
\cs_new:Npn \@@_latexe_remove_equals:w #1 = #2 \s_@@_stop { \exp_not:n {#1} }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ProcessKeysOptions}
% \begin{macro}{\ProcessKeysPackageOptions}
%   The user macro are simply wrappers around the internal process. In
%   contrast to other similar packages, the module name is always required
%   here.
%    \begin{macrocode}
\cs_new_protected:Npn \ProcessKeysOptions #1
  {
    \bool_set_true:N \l_@@_process_class_bool
    \@@_latexe_options:n {#1}
  }
\cs_new_protected:Npn \ProcessKeysPackageOptions #1
  {
    \bool_set_false:N \l_@@_process_class_bool
    \@@_latexe_options:n {#1}
  }
\@onlypreamble \ProcessKeysOptions
\@onlypreamble \ProcessKeysPackageOptions
%    \end{macrocode}
%\end{macro}
%\end{macro}
%
% One message to give.
%    \begin{macrocode}
\msg_new:nnnn { keyvalue } { option-unknown }
  { Unknown~option~'#1'~for~package~#2. }
  {
    LaTeX~has~been~asked~to~set~an~option~called~'#1'~
    but~the~#2~package~has~not~created~an~option~with~this~name.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
