%%%============================================================================== %% Copyright 2023-present by Alceu Frigeri %% %% This work may be distributed and/or modified under the conditions of %% %% * The [LaTeX Project Public License](http://www.latex-project.org/lppl.txt), %% version 1.3c (or later), and/or %% * The [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html), %% version 3 (or later) %% %% This work has the LPPL maintenance status *maintained*. %% %% The Current Maintainer of this work is Alceu Frigeri %% %% This is version {1.5} {2025/11/06} %% %% The list of files that compose this work can be found in the README.md file at %% https://ctan.org/pkg/pgfkeysearch %% %%%============================================================================== \NeedsTeXFormat{LaTeX2e}[2022/06/01] \ProvidesExplPackage {pgfkeysearch} {2025/11/06} {1.5} {pgfkeys Search Extension} \ExplSyntaxOn %%%%%%% %%% %%% Just an attempt of having my packages info in a regular way %%% \Pkginfograb_set:nn {} { props} for each and all. %%% %%%%%%% \RequirePackage{pkginfograb} \pkginfograb_set:nn { pgfkeysearch } { name = {pgfkeysearch} , prefix = {pgfkeysearch} , date = {2025/11/06}, version = {1.5} , description = {pgfkeys~ search~ extension} } %%%%%%% %%% End of cut-n-paste %%%%%%% \msg_new:nnnn {pgfkeysearch} {deprecated} { (ID:#1)~The~ command~#2~is~ deprecated~ use~ #3 ~ instead } { You~tried~to~use~a~deprecated~ command:#2. Use~ #3~ instead. ~Error~Code~ ID:<#1>. } \cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNv \cs_set_eq:NN \__pgfkeysearch_tl_clear:N \tl_clear:N \cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \use_none:nn \keys_define:nn { pgfkeysearch } { root~ search .usage:n = general , root~ search .choices:nn = { {} , true , false } { \str_if_eq:VnTF \l_keys_choice_str {false} { \cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \use_none:nn } { \cs_set_eq:NN \__pgfkeysearch_if_root_search:Nn \seq_put_right:Nn } } , key .usage:n = general, key .choice:, key / value .code:n = { \cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNv } , key / macro .code:n = { \cs_set_eq:NN \__pgfkeysearch_exp_arg:NNw \exp_args:NNc } , key .value_required:n = true , settings .usage:n = general, settings .choice:, settings / old .code:n = { \cs_set_eq:NN \__pgfkeysearch_tl_clear:N \use_none:n \cs_set_eq:NN \pgfkeysearchvalueof \__pgfkeysearch_old_valueof:nnN \cs_set_eq:NN \pgfkeysearchvalueofTF \__pgfkeysearch_old_valueofTF:nnNTF \cs_set_eq:NN \pgfkeygetvalueof \__pgfkeysearch_old_getvalueof:nnN \cs_set_eq:NN \pgfkeygetvalueofTF \__pgfkeysearch_old_getvalueofTF:nnNTF \cs_set_eq:NN \pgfkeysearch \pgfkeysearchvalueof \cs_set_eq:NN \pgfkeysearchTF \pgfkeysearchvalueofTF \cs_set_eq:NN \pgfkeyget \pgfkeygetvalueof \cs_set_eq:NN \pgfkeygetTF \pgfkeygetvalueofTF } , settings / new .code:n = { \cs_set_eq:NN \__pgfkeysearch_tl_clear:N \tl_clear:N \cs_set_eq:NN \pgfkeysearchvalueof \pgfkeysearch_multipath_keysearch:nnN \cs_set_eq:NN \pgfkeysearchvalueofTF \pgfkeysearch_multipath_keysearch:nnNTF \cs_set_eq:NN \pgfkeygetvalueof \pgfkeysearch_keyget:nnN \cs_set_eq:NN \pgfkeygetvalueofTF \pgfkeysearch_keyget:nnNTF \cs_set_eq:NN \pgfkeysearch \pgfkeysearchvalueof \cs_set_eq:NN \pgfkeysearchTF \pgfkeysearchvalueofTF \cs_set_eq:NN \pgfkeyget \pgfkeygetvalueof \cs_set_eq:NN \pgfkeygetTF \pgfkeygetvalueofTF } , settings .value_required:n = true , } \cs_new_protected:Npn \pgfkeysearch_settings:n #1 { \keys_set:nn {pgfkeysearch}{#1} } %%%%%%%%%%%%%%% %%% %%% _keysearch allows to find a key, %%% if it's defined 'anywhere' in the tree. I mean, upwards until, likely, /some/ %%% if it isn't defined, returns nothing (no error triggered) %%% %%%%%%%%%%%%%%% \seq_new:N \l__pgfkeysearch_pathterms_seq \seq_new:N \l__pgfkeysearch_pathtree_seq \tl_new:N \l__pgfkeysearch_path_tl \cs_generate_variant:Nn \tl_set:Nn {Ne} %%%%%%%%%%%%%%% %%% %%% This will create a pair %%% *none* expandable %%% #cmd:signature# => cs %%% #cmd:signature#TF => prg %%% %%%%%%%%% %%% %%% #1 -> #cmd:signature# => base-name %%% #2 -> base-code %%% %%%%%%%%%%%%%%% \regex_const:Nn \c__pgfkeysearch_rtn_regex {\c{__pgfkeysearch_set_rtn_true:}|\c{__pgfkeysearch_set_rtn_false:}} \cs_new_protected:Npn \__pgfkeysearch_duo_new:Nn #1#2 { \tl_set:Nn \l__pgfkeysearch_tmpa_tl {#2} \regex_replace_all:NnN \c__pgfkeysearch_rtn_regex {} \l__pgfkeysearch_tmpa_tl \__pgfkeysearch_duo_aux:NVn #1 \l__pgfkeysearch_tmpa_tl {#2} } \cs_new_protected:Npn \__pgfkeysearch_duo_aux:Nnn #1#2#3 { \cs_new_protected:Nn #1 { #2 } \prg_new_protected_conditional:Nnn #1 {T , F , TF} { #3 \__pgfkeysearch_return: } } \cs_generate_variant:Nn \__pgfkeysearch_duo_aux:Nnn {NV} %%%%%%%%%%%%%%% %%% so that they can be easily removed (if needed) by _duo_new:Nn %%%%%%%%%%%%%%% \cs_new:Npn \__pgfkeysearch_set_rtn_true: { \cs_set_eq:NN \__pgfkeysearch_return: \prg_return_true: } \cs_new:Npn \__pgfkeysearch_set_rtn_false: { \cs_set_eq:NN \__pgfkeysearch_return: \prg_return_false: } %%%%%%%%%%%%%%% %%% %%% This is, basicaly, \pgfkeysvalueof %%% but, assigning the found value (if any) to #3 %%% %%%%%%%%%%%%%%% \__pgfkeysearch_duo_new:Nn \pgfkeysearch_keyget:nnN { \cs_if_exist:cTF {pgfk@#1/#2} { \__pgfkeysearch_exp_arg:NNw \tl_set:Nn #3 {pgfk@#1/#2} \__pgfkeysearch_set_rtn_true: } { \__pgfkeysearch_tl_clear:N #3 \__pgfkeysearch_set_rtn_false: } } %%%%%%%%%%%%%%% %%% %%% Given a single path /A/B/C/D it will look after %%% /A/B/C/D/ %%% /A/B/C/ %%% /A/B/ %%% /A/ %%% / (if "root search" set) %%% stoping at the first hit %%% %%%%%%%%%%%%%%% \__pgfkeysearch_duo_new:Nn \pgfkeysearch_keysearch:nnN { \seq_set_split:Nne \l__pgfkeysearch_pathterms_seq {/} {#1} \seq_remove_all:Nn \l__pgfkeysearch_pathterms_seq {} \tl_clear:N \l__pgfkeysearch_path_tl \seq_clear:N \l__pgfkeysearch_pathtree_seq \__pgfkeysearch_if_root_search:Nn \l__pgfkeysearch_pathtree_seq {} \seq_map_inline:Nn \l__pgfkeysearch_pathterms_seq { \tl_put_right:Nn \l__pgfkeysearch_path_tl {/##1} \seq_put_right:NV \l__pgfkeysearch_pathtree_seq \l__pgfkeysearch_path_tl } \seq_reverse:N \l__pgfkeysearch_pathtree_seq \__pgfkeysearch_set_rtn_false: \__pgfkeysearch_tl_clear:N #3 \seq_map_inline:Nn \l__pgfkeysearch_pathtree_seq { \cs_if_exist:cTF {pgfk@##1/#2} { \__pgfkeysearch_exp_arg:NNw \tl_set:Nn #3 {pgfk@##1/#2} \__pgfkeysearch_set_rtn_true: \seq_map_break: } { } } } \cs_new_protected:Npn \pgfkeysearch_keysearch:nnnTF { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnTF}{\cs_to_str:N \pgfkeysearch_keysearch:nnNTF} \pgfkeysearch_keysearch:nnNTF } \cs_new_protected:Npn \pgfkeysearch_keysearch:nnnT { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnT}{\cs_to_str:N \pgfkeysearch_keysearch:nnNT} \pgfkeysearch_keysearch:nnNT } \cs_new_protected:Npn \pgfkeysearch_keysearch:nnnF { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnnF}{\cs_to_str:N \pgfkeysearch_keysearch:nnNF} \pgfkeysearch_keysearch:nnNF } %%%%%%%%%%%%%%% %%% %%% #1 shall be a comma separated list of paths (can be a single one) %%% it searchs for a key in every path, stoping at the first hit. %%% %%%%%%%%%%%%%%% \__pgfkeysearch_duo_new:Nn \pgfkeysearch_multipath_keysearch:nnN { \seq_set_from_clist:Nn \l__pgfkeysearch_tmpa_seq {#1} \seq_map_inline:Nn \l__pgfkeysearch_tmpa_seq { \pgfkeysearch_keysearch:nnNTF {##1}{#2} #3 { \seq_map_break: } {} } } \cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnTF { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnTF}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNTF} \pgfkeysearch_multipath_keysearch:nnNTF } \cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnT { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnT}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNT} \pgfkeysearch_multipath_keysearch:nnNT } \cs_new_protected:Npn \pgfkeysearch_multipath_keysearch:nnnF { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnnF}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnNF} \pgfkeysearch_multipath_keysearch:nnNF } %%%%%%%%%%%%%%% %%% %%% The caveats: none of this is expandable. %%% %%%%%%%%%%%%%%% %%% %%% This is the more generic one. %%% #1 is the path (or list of paths) %%% #2 is the %%% #3 is the macro that will receive the key value (if any) %%% %%%%%%%%%%%%%%% %\NewDocumentCommand{\pgfkeysearchsettings}{m} % { \pgfkeysearch_settings:n {#1} } \cs_new_eq:NN \pgfkeysearchsettings \pgfkeysearch_settings:n \cs_new:Npn \__pgfkeysearch_old_valueof:nnN #1#2#3 { \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2}#3 { } { \tl_clear:N #3 } } \cs_new:Npn \__pgfkeysearch_old_valueofTF:nnNTF #1#2#3#4#5 { \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2}#3 { #4 } { \tl_clear:N #3 #5 } } \cs_new:Npn \__pgfkeysearch_old_getvalueof:nnN #1#2#3 { \pgfkeysearch_keyget:nnNTF {#1}{#2}#3 { } { \tl_clear:N #3 } } \cs_new:Npn \__pgfkeysearch_old_getvalueofTF:nnNTF #1#2#3#4#5 { \pgfkeysearch_keyget:nnNTF {#1}{#2}#3 { #4 } { \tl_clear:N #3 #5 } } \keys_set:nn {pgfkeysearch}{settings=new} \ProcessKeyOptions [pgfkeysearch]