% !TeX encoding = ISO-8859-1 % Ce fichier contient le code de l'extension "xstring" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % \def\xstringname {xstring} % \def\xstringversion {1.81} % % % \def\xstringdate {2018/12/09} % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % Author : Christian Tellechea % % Status : Maintained % % Maintainer : Christian Tellechea % % Email : unbonpetit@netc.fr % % Package URL: https://www.ctan.org/pkg/xstring % % Bug tracker: https://framagit.org/unbonpetit/xstring/issues % % Repository : https://framagit.org/unbonpetit/xstring/tree/master % % Copyright : Christian Tellechea 2008-2018 % % Licence : Released under the LaTeX Project Public License v1.3c % % or later, see http://www.latex-project.org/lppl.txt % % Files : 1) xstring.tex % % 2) xstring.sty % % 3) xstring-fr.tex % % 4) xstring-fr.pdf % % 5) xstring-en.tex % % 6) xstring-en.pdf % % 7) README % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ifdefined\usepackage\else \immediate\write-1 {Package: xstring \xstringdate\space v\xstringversion\space String manipulations (CT)}% \fi \newcount\integerpart \newcount\decimalpart% compteurs utilisés par xstring \edef\restorexstringcatcode{\catcode\number`\_ =\number\catcode`\_\relax} \catcode`\_=11 \long\def\xs_execfirst#1#2{#1} \long\def\xs_execsecond#1#2{#2} \long\def\xs_gobarg#1{} \expandafter\def\expandafter\xs_gobspace\space{} \def\xs_deftok#1#2{\let#1= #2\relax}\xs_deftok\xs_sptoken{ } \def\xs_ifnxttok#1#2#3{\xs_deftok\xs_toktotest{#1}\def\__truecode{#2}\def\__falsecode{#3}\xs_ifnxttok_a} \def\xs_ifnxttok_a{\futurelet\__futurtok\xs_ifnxttok_b} \def\xs_ifnxttok_b{% \xs_ifx{\__futurtok\xs_sptoken} {\afterassignment\xs_ifnxttok_a\let\__futurtok= } {\xs_ifx{\__futurtok\xs_toktotest}\__truecode\__falsecode}% } \def\xs_ifstar#1{\xs_ifnxttok*{\xs_execfirst{#1}}} \def\xs_testopt#1#2{\xs_ifnxttok[{#1}{#1[{#2}]}} \long\def\xs_exparg#1#2{\expandafter\xs_exparg_a\expandafter{#2}{#1}}% \long\def\xs_exparg_a#1#2{#2{#1}} \long\def\xs_exptwoargs#1#2#3{\xs_exparg{\xs_exparg{#1}{#2}}{#3}} \long\def\xs_eearg#1#2{\xs_exparg{\xs_exparg{#1}}{#2}} \long\def\xs_edefaddtomacro#1#2{\edef#1{\unexpanded\expandafter{#1}#2}} \long\def\xs_addtomacro#1#2{\xs_exparg{\def#1}{#1#2}} \long\def\xs_eaddtomacro#1#2{\xs_exparg{\xs_addtomacro#1}{#2}} \long\def\xs_eeaddtomacro#1#2{\xs_exparg{\xs_eaddtomacro#1}{#2}} \long\def\xs_endgroup_eaddtomacro#1#2{\xs_exparg{\endgroup\xs_addtomacro#1}{#2}} \long\def\xs_beforespace#1 #2\_nil{#1} \long\def\xs_afterspace#1 #2\_nil{#2} \long\def\xs_gobtonil#1\_nil{} \long\def\xs_gobtobrace#1#{} \long\def\xs_ifx#1{\ifx#1\expandafter\xs_execfirst\else\expandafter\xs_execsecond\fi} \def\xs_ifnum#1{\ifnum#1\expandafter\xs_execfirst\else\expandafter\xs_execsecond\fi} \long\def\xs_ifempty#1{\expandafter\xs_ifx\expandafter{\expandafter\relax\detokenize{#1}\relax}} \def\xs_toks_toverb{% lit 1 argument et le transforme en verb \begingroup \def\do##1{\catcode`##112 }% \dospecials \obeyspaces \xs_read_verb } \def\setverbdelim#1{% définit quel est le délimiteur de verb \xs_exparg\xs_ifempty{\xs_gobarg#1}% {} {\errmessage{Package xstring Warning: verb delimiter "\detokenize{#1}" is not a single token}}% \def\xs_read_verb##1#1##2#1{\endgroup\xs_assign_verb{##2}}% on appelle l'exécution de fin } % Assigne l'argument entre délimiteur verb dans la sc #1 \def\verbtocs#1{% \def\xs_assign_verb##1{\def#1{##1}}% \xs_toks_toverb } \begingroup \catcode0 3 \gdef\xs_twozeroschars{^^00}% \catcode0 7 \xdef\xs_twozeroschars{\xs_twozeroschars^^00}% \endgroup \edef\xs_reserved_A{\long\def\noexpand\xs_assigntokenizeresult##1\xs_twozeroschars} \xs_reserved_A#2{\endgroup\xs_exparg{\def#2}{\xs_gobarg#1}} \def\tokenize#1#2{% \begingroup \xs_def\xs_reserved_A{#2}% on développe en accord avec \fullexpandarg ou \noexpandarg \everyeof\expandafter{\xs_twozeroschars#1}% \endlinechar-1 \expandafter\xs_assigntokenizeresult\scantokens\expandafter{\expandafter\relax\xs_reserved_A}% on fait l'assignation }% \long\def\xs_assign_xstringresult#1#2{\xs_ifempty{#2}{#1}{\def#2{#1}}} \def\xs_ldef{\long\def} \def\xs_ledef{\long\edef} \def\normalexpandarg{% \let\xs_def\xs_ldef% on définit \xs_call avec \def \def\xs_expand##1{\unexpanded\expandafter{##1}}% } \let\noexpandarg\normalexpandarg% synonyme \def\expandarg{% \let\xs_def\xs_ldef% on définit \xs_call avec \def \def\xs_expand##1{\unexpanded\expandafter\expandafter\expandafter{##1}}% } \def\fullexpandarg{% \let\xs_def\xs_ledef% on définit\xs_call avec \edef \def\xs_expand##1{##1}% et on neutralise \xs_expand } \def\saveexpandmode{\let\xs_saved_def\xs_defarg\let\xs_saved_expand\xs_expand} \def\restoreexpandmode{\let\xs_defarg\xs_saved_def\let\xs_expand\xs_saved_expand} % Macro interne renvoyant #2 si son argument commence par une accolade ouvrante "{" % et #3 sinon (Ulrich Diez sur comp.text.tex) \long\def\xs_ifbeginwithbrace#1{% \csname xs_exec% \expandafter\xs_gobarg\string{% \expandafter\xs_gobarg\expandafter{\expandafter{\string#1}% \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\xs_execfirst \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\xs_gobarg \expandafter\expandafter\expandafter\xs_gobarg \expandafter\expandafter\expandafter{\expandafter\string\expandafter}\string}% \expandafter\xs_gobarg\string}% \xs_execsecond{first}{second}% \endcsname } % Cette macro interne prend la 1ere unité syntaxique de #1 et assigne le résultat à #2 \long\def\xs_returnfirstsyntaxunit#1#2{% \let\xs_toks\relax \xs_ifempty{#1} {\let#2\empty } {\xs_eearg\xs_ifempty{\expandafter\xs_beforespace\detokenize{#1} \_nil}% #1 commence par un espace ? {\def#2{ }} {\xs_ifbeginwithbrace{#1} {\def\xs_groupfound{\xs_exparg{\def#2}{\expandafter{#2}}\xs_gobtonil}% on met #2 dans des accolades et on finit \let\xs_toks\bgroup \afterassignment\xs_groupfound \def#2% } {\long\def\xs_assignfirstarg##1##2\_nil{\def#2{##1}}% \xs_assignfirstarg }% #1\_nil }% }% } \long\def\xs_removefirstsyntaxunit#1#2{% \xs_ifempty{#1} {\let#2\empty } {\xs_eearg\xs_ifempty{\expandafter\xs_beforespace\detokenize{#1} \_nil}% #1 commence par un espace ? {\xs_exparg\xs_ifbeginwithbrace{\xs_gobspace#1} {\xs_exparg{\def#2}{\xs_gobtobrace#1}}% {\xs_exparg{\def#2}{\xs_afterspace#1\_nil}}% }% {\xs_exparg{\def#2}{\xs_gobarg#1}}% }% } % Cette macro interne est utilisée dans les macros étoilées pour : % 1) développer l'argument selon qu'on a choisit \fullexpandarg % ou \normalexpandarg, et ceci à l'aide de la macro \xs_def % 2) Ensuite, on détokenize ce développement de façon n'avoir plus que % des catcodes de 10 pour les espaces et 12 pour le reste. \long\def\xs_expand_and_detokenize#1#2{% \long\def#1{#2}% \expandafter\edef\expandafter#1\expandafter{\xs_expand#1}% on développe #2 selon le mode de développement \long\edef#1{\detokenize\expandafter{#1}}% puis on détokenize et on assigne à #1 } \long\def\xs_expand_and_assign#1#2{\xs_def#1{#2}}% on développe #2 selon \fullexpandarg ou \normalexpandarg \def\xs_arglist{0########1########2########3########4########5########6########7########8########9} % \xs_assign_args{3} met dans la sc \xs_myarg les tokens "####1####2####3" \def\xs_assign_args#1{\def\xs_assign_args_i0##1#1##2\_nil{\def\xs_myarg{##1#1}}\expandafter\xs_assign_args_i\xs_arglist\_nil} % \xs_assign_opt_args{3} met dans la sc \xs_myarg les tokens "[####1]####2####3####4" \def\xs_assign_opt_args#1{\xs_exparg\xs_assign_opt_args_i{\number\numexpr#1+1}} \def\xs_assign_opt_args_i#1{\def\xs_assign_opt_args_ii0##11##2#1##3\_nil{\def\xs_myarg{[##11]##2#1}}\expandafter\xs_assign_opt_args_ii\xs_arglist\_nil} % \xs_assign_one_arg{3} met dans la sc \xs_myarg les tokens "####3" \def\xs_assign_one_arg#1{\xs_exparg\xs_assign_one_arg_i{\number\numexpr#1-1}{#1}} \def\xs_assign_one_arg_i#1#2{\def\xs_assign_one_arg_ii##1#1##2#2##3\_nil{\def\xs_myarg{##2#2}}\expandafter\xs_assign_one_arg_ii\xs_arglist\_nil} % #1 : numéro du 1er argument; #2 : nombre de lignes; #3 argument optionnel; #4 : dernier numéro de l'argument devant être détokenisé si étoile \def\xs_buildlines#1#2#3#4{% \let\xs_newlines\empty \let\xs_newargs\empty \def\xs_buildlines_i##1{% \xs_exparg\xs_assign_one_arg{\number\numexpr##1+#1-1}% \edef\xs_reserved_B{\noexpand\xs_expand\csname xs_arg_\romannumeral\numexpr##1\endcsname}% \xs_ifnum{##1=1 }% si c'est le premier argument {\xs_ifempty{#3}% {\xs_eaddtomacro\xs_newargs{\expandafter{\xs_reserved_B}}% \edef\xs_reserved_B{\ifnum##1>#4 xs_def\else xs_assign\fi}% }% et s'il y a un argument optionnel alors, on met des crochets {\xs_eaddtomacro\xs_newargs{\expandafter[\xs_reserved_B]}% \def\xs_reserved_B{xs_def}% ne pas détokéniser l'argument optionnel grace au \xs_def }% } {\xs_eaddtomacro\xs_newargs{\expandafter{\xs_reserved_B}}% \edef\xs_reserved_B{\ifnum##1>#4 xs_def\else xs_assign\fi}% }% \edef\xs_newlines{\unexpanded\expandafter{\xs_newlines}\expandafter\noexpand\csname\xs_reserved_B\endcsname\expandafter\noexpand\csname xs_arg_\romannumeral\numexpr##1\endcsname{\xs_myarg}}% \xs_ifnum{##1<#2 } {\xs_exparg\xs_buildlines_i{\number\numexpr##1+1}} {}% }% \xs_buildlines_i1% } \def\xs_newmacro{% \xs_ifstar {\let\xs_reserved_D\empty\xs_newmacro_} {\let\xs_reserved_D\relax\xs_newmacro_0}% } % #1 : dernier numéro de l'argument devant être détokenisé % #2 : nom de la macro publique % #3 : argument optionnel (vide si pas d'arg optionnel) % #4 : nombre d'arguments obligatoires % #5 : utilisation de testopt (1 si oui, 0 si non) % #6 : code de la macro \def\xs_newmacro_#1#2#3#4#5{% \edef\xs_reserved_A{xs_\expandafter\xs_gobarg\string#2}% \edef\xs_reserved_C{\expandafter\noexpand\csname\xs_reserved_A _\ifx\empty#3\empty _\fi\endcsname}% \edef\xs_reserved_B{% \long \ifx\empty\xs_reserved_D \def\noexpand#2{\noexpand\xs_ifstar {\let\noexpand\xs_assign\noexpand\xs_expand_and_detokenize\expandafter\noexpand\xs_reserved_C}% {\let\noexpand\xs_assign\noexpand\xs_expand_and_assign\expandafter\noexpand\xs_reserved_C}% }% \else \def\noexpand#2{\let\noexpand\xs_assign\noexpand\xs_expand_and_assign\expandafter\noexpand\xs_reserved_C}% \fi \ifx\empty#3\empty \else \def\expandafter\noexpand\xs_reserved_C{% \noexpand\xs_testopt{\expandafter\noexpand\csname\xs_reserved_A __\endcsname}{\ifx\xs_def\xs_ledef#3\else\unexpanded{#3}\fi}}% \fi }% % Que fait \xs_reserved_B ? Il définit : % si #3 est vide : \NOM{\xs_ifstar{\let\xs_assign\xs_expand_and_detokenize\xs_NOM__}{\let\xs_assign\xs_expand_and_assign\xs_NOM__}} % si #3 existe : \NOM{\xs_ifstar{\let\xs_assign\xs_expand_and_detokenize\xs_NOM_}{\let\xs_assign\xs_expand_and_assign\xs_NOM_}} % \xs_NOM_{\xs_testopt{\xs_NOM__}{#3}}} \xs_reserved_B \ifx\empty#3\empty \xs_buildlines1{#4}{#3}{#1}% \xs_assign_args{#4}% \else \xs_exparg{\xs_buildlines1}{\number\numexpr#4+1}{#3}{#1}% \xs_assign_opt_args{#4}% \fi \edef\xs_reserved_B{\def\expandafter\noexpand\csname\xs_reserved_A __\endcsname\xs_myarg}% \edef\xs_reserved_C{\unexpanded\expandafter{\xs_newlines}\edef\noexpand\xs_call}% \edef\xs_reserved_D{\noexpand\noexpand\expandafter\noexpand\csname\xs_reserved_A\endcsname\unexpanded\expandafter{\xs_newargs}}% \ifnum#5=1 \edef\xs_reserved_D{\noexpand\noexpand\noexpand\xs_testopt{\unexpanded\expandafter{\xs_reserved_D}}{}}\fi \xs_edefaddtomacro\xs_reserved_C{{\unexpanded\expandafter{\xs_reserved_D}}\noexpand\xs_call}% \xs_edefaddtomacro\xs_reserved_B{{\unexpanded\expandafter{\xs_reserved_C}}}% % Que fait \xs_reserved_B ? Il définit par exemple pour 3 arguments obligatoires et 1 facultatif : % \def\xs_NOM__[##2]##3##4##5{% % \xs_def\xs_arg_i{##2}\xs_assign\xs_arg_ii{##3}\xs_assign\xs_arg_iii{##4}\xs_asign\xs_arg_iv{##5}% % si #5=0: \edef\xs_call{\noexpand\xs_NOM[\xs_expand\xs_arg_i]{\xs_expand\xs_arg_ii}{\xs_expand\xs_arg_iii}{\xs_expand\xs_arg_iv}}% % si #5=1: \edef\xs_call{\noexpand\xs_testopt{\noexpand\xs_NOM[\xs_expand\xs_arg_i]{\xs_expand\xs_arg_i}{\xs_expand\xs_arg_ii}{\xs_expand\xs_arg_iii}{\xs_expand\xs_arg_iv}}{}}% % \xs_call} \long\xs_reserved_B \edef\xs_reserved_B{% \def\expandafter\noexpand\csname\xs_reserved_A\endcsname \xs_myarg\ifnum#5=1 [\unexpanded{##}\number\numexpr\ifx\empty#3\empty#4+1\else#4+2\fi]\fi }% % Que fait \xs_reserved_B ? Il définit par exemple pour 3 arguments obligatoires et 1 facultatif : % \def \xs_NOM[##2]##3##4##5[##6]{#6} \long\xs_reserved_B } % macro générique qui lit \xs_reserved_C us par us % 3 sous-routines sont appelées à des moments clé : % \xs_atendofgroup (un groupe se finit, appel récursif) % \xs_atbegingroup (un groupe vient d'être ouvert) % \xs_atnextsyntaxunit (la future US n'est pas un groupe) \def\xs_read_reserved_C{% \xs_exparg\xs_ifempty{\xs_reserved_C}% {\xs_ifnum{\xs_nestlevel=0 } {} {\xs_atendofgroup \xs_read_reserved_C }% } {\xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_C}\xs_reserved_A \xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_C}\xs_reserved_C \xs_exploregroups \xs_ifx{\bgroup\xs_toks} {\advance\integerpart1 \begingroup \expandafter\def\expandafter\xs_reserved_C\xs_reserved_A \xs_calc_grouIP \def\xs_nestlevel{1}% \integerpart0 \xs_atbegingroup } {\advance\decimalpart1 \xs_atnextsyntaxunit }% \xs_read_reserved_C }% } % macro générique qui lit \xs_reserved_D en procédant à des tests avec \IfBeginWith % 1 sous-routine est appelée : % \xs_atoccurfound (une occurrence a été trouvée) \def\xs_read_reserved_D{% \xs_exparg\xs_ifempty{\xs_reserved_D}% {\xs_ifnum{\xs_nestlevel=0 } {} {\edef\__temp{% \endgroup \noexpand\decimalpart=\number\decimalpart\relax \unexpanded{\xs_addtomacro\xs_reserved_C}{{\unexpanded\expandafter{\xs_reserved_C}}}% }% \__temp \xs_read_reserved_D }% }% {\xs_exptwoargs\xs_IfBeginWith_i\xs_reserved_D\xs_reserved_E {\advance\decimalpart1 \let\xs_reserved_D\xs_reserved_A \xs_atoccurfound }% {\xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_D}\xs_reserved_A \xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_D}\xs_reserved_D \xs_exploregroups \xs_ifx{\bgroup\xs_toks} {\advance\integerpart1 \begingroup \expandafter\def\expandafter\xs_reserved_D\xs_reserved_A \xs_calc_grouIP \let\xs_reserved_C\empty \def\xs_nestlevel{1}% \integerpart0 } {\xs_eaddtomacro\xs_reserved_C{\xs_reserved_A}% }% }% \xs_read_reserved_D }% } \xs_newmacro\StrRemoveBraces{}{1}{1}{% \def\xs_reserved_C{#1}% \let\xs_reserved_B\empty \def\xs_nestlevel{0}% \xs_StrRemoveBraces_i \xs_exparg\xs_assign_xstringresult{\xs_reserved_B}{#2}% } \def\xs_StrRemoveBraces_i{% \xs_exparg\xs_ifempty{\xs_reserved_C}% {\xs_ifnum{\xs_nestlevel=0 } {} {\xs_endgroup_eaddtomacro\xs_reserved_B\xs_reserved_B \xs_StrRemoveBraces_i }% } {\xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_C}\xs_reserved_A \xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_C}\xs_reserved_C \xs_ifx{\bgroup\xs_toks} {\xs_ifx{\xs_exploregroups\relax}% on explore les groupes ? {\begingroup \expandafter\def\expandafter\xs_reserved_C\xs_reserved_A \def\xs_nestlevel{1}% \let\xs_reserved_B\empty } {\expandafter\xs_addtomacro\expandafter\xs_reserved_B\xs_reserved_A }% } {\xs_eaddtomacro\xs_reserved_B{\xs_reserved_A}% }% \xs_StrRemoveBraces_i }% } % \xs_cutafteroccur coupe l'argument #1 après la #3ème occurrence de #2 % \xs_reserved_C : contient ce qui se trouve avant cette occurrence % \xs_reserved_D : contient ce qui se trouve avant cette occurrence y compris cette occurrence % \xs_reserved_E : contient ce qui se trouve après l'occurrence % si l'occurrence n'existe pas ou qu'un des arguments est vide, toutes les chaines renvoyées sont vides \long\def\xs_cutafteroccur#1#2#3{% \xs_ifnum{#3<1 } {\let\xs_reserved_C\empty\let\xs_reserved_E\empty\gdef\groupID{}} {\xs_cutafteroccur_i{#1}{#2}{#3}}% } \long\def\xs_cutafteroccur_i#1#2#3{% \def\xs_reserved_D{#1}\let\xs_reserved_C\empty\def\xs_reserved_E{#2}% \decimalpart0 \integerpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atoccurfound{% \xs_ifnum{\decimalpart=\numexpr(#3)\relax} {\edef\__temp{% \def\noexpand\xs_reserved_C{\unexpanded\expandafter{\xs_reserved_C}}% \def\noexpand\xs_reserved_D{\unexpanded\expandafter{\xs_reserved_D}}% \noexpand\decimalpart=\number\decimalpart\relax }% \expandafter\xs_exitallgroups\__temp \xs_gobarg% mange l'appel récursif \xs_read_reserved_D dans \xs_read_reserved_D } {\xs_eaddtomacro\xs_reserved_C{\xs_reserved_E}% }% }% \xs_read_reserved_D \xs_ifnum{\decimalpart=\numexpr(#3)\relax}% occurrence trouvée ? {\let\xs_reserved_E\xs_reserved_D \xs_exparg{\def\xs_reserved_D}{\xs_reserved_C#2}% } {\let\xs_reserved_C\empty \let\xs_reserved_E\empty \gdef\groupID{}% }% } \xs_newmacro*3\IfSubStr{1}{2}{0}{% \xs_cutafteroccur{#2}{#3}{#1}% \xs_exparg\xs_ifempty{\xs_reserved_D}\xs_execsecond\xs_execfirst } \xs_newmacro*2\IfBeginWith{}{2}{0}{% \xs_ifempty{#2}% {\xs_execsecond} {\xs_IfBeginWith_i{#1}{#2}}% } \long\def\xs_IfBeginWith_i#1#2{% \xs_ifempty{#2}% {\xs_execfirst% #2 est vide, tous les tests sont passés avec succès : on renvoie #3 } {\xs_ifempty{#1}% {\xs_execsecond% #1 est vide, c'est que #2 est + long que #1 : on renvoie #4 } {\xs_returnfirstsyntaxunit{#2}\xs_reserved_B \xs_returnfirstsyntaxunit{#1}\xs_reserved_A \xs_ifx{\xs_reserved_A\xs_reserved_B}% il y a égalité... {\xs_removefirstsyntaxunit{#2}\xs_reserved_B \xs_removefirstsyntaxunit{#1}\xs_reserved_A% on enlève les 1ere unités syntaxiques \xs_exptwoargs\xs_IfBeginWith_i\xs_reserved_A\xs_reserved_B } {\xs_execsecond }% }% }% } \xs_newmacro*2\IfEndWith{}{2}{0}{% \xs_ifempty{#2}% {\xs_execsecond } {\xs_StrCount{#1}{#2}[\xs_reserved_A]% \xs_ifnum{\xs_reserved_A=0 } {\xs_execsecond} {\xs_exparg\xs_ifempty{\xs_reserved_C}}% }% } \xs_newmacro*4\IfSubStrBefore{1,1}{3}{0}{\xs_IfSubStrBefore_i[#1]{#2}{#3}{#4}} \def\xs_IfSubStrBefore_i[#1,#2]#3#4#5{% \def\xs_reserved_C{#3}% \xs_ifx{\xs_exploregroups\relax}% si on explore les groupes {\let\xs_reserved_B\empty \def\xs_nestlevel{0}% \xs_StrRemoveBraces_i% on retire les accolades \let\xs_reserved_C\xs_reserved_B } {}% \xs_exparg\xs_cutafteroccur\xs_reserved_C{#5}{#2}% \xs_exparg\xs_cutafteroccur\xs_reserved_C{#4}{#1}% \gdef\groupID{}% \xs_exparg\xs_ifempty{\xs_reserved_C}% \xs_execsecond \xs_execfirst } \xs_newmacro*4\IfSubStrBehind{1,1}{3}{0}{\xs_IfSubStrBehind_i[#1]{#2}{#3}{#4}} \long\def\xs_IfSubStrBehind_i[#1,#2]#3#4#5{\xs_IfSubStrBefore_i[#2,#1]{#3}{#5}{#4}} \long\def\xs_formatnumber#1#2{% \xs_ifempty{#1}% {\def#2{0X}% si vide, renvoie 0X } {\xs_returnfirstsyntaxunit{#1}\xs_reserved_A \def\xs_reserved_B{+}% \xs_ifx{\xs_reserved_A\xs_reserved_B} {\xs_removefirstsyntaxunit{#1}\xs_reserved_C \xs_exparg\xs_ifempty{\xs_reserved_C}% {\def#2{+0X}} {\xs_exparg{\def#2}{\expandafter+\expandafter0\xs_reserved_C}}% } {\def\xs_reserved_B{-}% \xs_ifx{\xs_reserved_A\xs_reserved_B} {\xs_removefirstsyntaxunit{#1}\xs_reserved_A \xs_exparg\xs_ifempty{\xs_reserved_A}% {\def#2{-0X}}% {\xs_exparg{\def#2}{\expandafter-\expandafter0\xs_reserved_A}}% } {\def#2{0#1}% }% }% }% } \xs_newmacro\IfInteger{}{1}{0}{% \xs_formatnumber{#1}\xs_reserved_A \decimalpart0 \afterassignment\xs_defafterinteger\integerpart\xs_reserved_A\relax\_nil \let\xs_after_intpart\afterinteger \expandafter\xs_testdot\afterinteger\_nil \xs_ifx{\empty\afterdecimal} {\xs_ifnum{\decimalpart=0 } {\xs_execfirst% partie décimale constituée de 0 --> seul cas où on renvoie vrai } {\expandafter\xs_defafterinteger\xs_after_intpart\relax\_nil \xs_execsecond }% } {\expandafter\xs_defafterinteger\xs_after_intpart\relax\_nil \xs_execsecond }% } \xs_newmacro\IfDecimal{}{1}{0}{% \xs_formatnumber{#1}\xs_reserved_A \decimalpart0 \afterassignment\xs_defafterinteger\integerpart\xs_reserved_A\relax\_nil \expandafter\xs_testdot\afterinteger\_nil \xs_ifx{\empty\afterdecimal} } \begingroup\catcode`\@ 11 \long\gdef\xs_defafterinteger#1\relax\_nil{\def\@xs@afterinteger{#1}\let\afterinteger\@xs@afterinteger} \long\gdef\xs_defafterdecimal#1\_nil{\def\@xs@afterdecimal{#1}\let\afterdecimal\@xs@afterinteger} \endgroup \def\xs_testdot{% \let\xs_decsep\empty \xs_ifnxttok. {\def\xs_decsep{.}\xs_readdecimalpart}% {\xs_testcomma}% } \def\xs_testcomma{% \xs_ifnxttok, {\def\xs_dessep{,}\xs_readdecimalpart}% {\xs_defafterdecimal}% } \long\def\xs_readdecimalpart#1#2\_nil{% \xs_ifempty{#2} {\def\xs_reserved_A{0X}} {\def\xs_reserved_A{0#2}}% \afterassignment\xs_defafterinteger\decimalpart\xs_reserved_A\relax\_nil \expandafter\xs_defafterdecimal\afterinteger\_nil } % test d'égalité sur des chaines (et des chaines contenant des nombres) \long\def\xs_IfStrEqFalse_i#1#2{\let\xs_reserved_A\xs_execsecond} \long\def\xs_IfStrEqFalse_ii#1#2{% renvoie vrai si les 2 arg sont décimaux et s'ils sont égaux, faux sinon \xs_IfDecimal{#1}% {\xs_IfDecimal{#2}% {\ifdim#1pt=#2pt \let\xs_reserved_A\xs_execfirst \else \let\xs_reserved_A\xs_execsecond \fi% les 2 sont décimaux : on fait le test }% {\let\xs_reserved_A\xs_execsecond}% un seul est décimal }% {\let\xs_reserved_A\xs_execsecond}% #1 n'est pas décimal } \long\def\xs_TestEqual#1#2{% teste si les 2 arguments sont égaux \def\xs_reserved_A{#1}\def\xs_reserved_B{#2}% \xs_ifx{\xs_reserved_A\xs_reserved_B} {\let\xs_reserved_A\xs_execfirst}% égalité parfaite des 2 chaines {\xs_reserved_D{#1}{#2}}% \xs_reserved_A } \xs_newmacro*2\IfStrEq{}{2}{0}{% teste si les deux chaines sont égales \let\xs_reserved_D\xs_IfStrEqFalse_i \xs_TestEqual{#1}{#2}% } \xs_newmacro*2\IfEq{}{2}{0}{% teste si les 2 arguments (chaine ou nombre) sont égaux \let\xs_reserved_D\xs_IfStrEqFalse_ii \xs_TestEqual{#1}{#2}% } \def\IfStrEqCase{% \xs_ifstar {\def\xs_reserved_E{\IfStrEq*}\xs_IfStrCase}% {\def\xs_reserved_E{\IfStrEq}\xs_IfStrCase}% } \long\def\xs_IfStrCase#1#2{\xs_testopt{\xs_IfStringCase{#1}{#2}}{}} \def\IfEqCase{% \xs_ifstar {\def\xs_reserved_E{\IfEq*}\xs_IfEqCase}% {\def\xs_reserved_E{\IfEq}\xs_IfEqCase}% } \long\def\xs_IfEqCase#1#2{\xs_testopt{\xs_IfStringCase{#1}{#2}}{}} \long\def\xs_IfStringCase#1#2[#3]{% \long\def\xs_testcase##1##2##3\_nil{% lit les 2 premieres unités syntaxiques dans ##1 et ##2. Les autres dans ##3 \xs_reserved_E{#1}{##1}% {##2}% le test est positif, on exécute le code correspondant {\xs_ifempty{##3}% {#3}% s'il n'y a plus de cas, on exécute le code {\xs_testcase##3\_nil}% sinon, on recommence avec ce qui reste }% }% \xs_testcase#2\_nil } % Renvoie ce qui est à gauche de l'occurence n°#1 de la sous chaine #3 dans la chaine #2 \xs_newmacro*3\StrBefore{1}{2}{1}{% \xs_cutafteroccur{#2}{#3}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_C}{#4}% } % Renvoie ce qui est à droite de l'occurence n°#1 de la sous chaine #3 dans la chaine #2 \xs_newmacro*3\StrBehind{1}{2}{1}{% \xs_cutafteroccur{#2}{#3}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_E}{#4}% } % Renvoie ce qui est strictement compris entre les occurrences n°#1 et n°#2 % des chaines #4 et #5 dans la chaine #3 \xs_newmacro*4\StrBetween{1,1}{3}{1}{\xs_StrBetween_i[#1]{#2}{#3}{#4}[#5]} \long\def\xs_StrBetween_i[#1,#2]#3#4#5[#6]{% \begingroup \noexploregroups \xs_cutafteroccur{#3}{#5}{#2}% \xs_exparg\xs_cutafteroccur{\xs_reserved_C}{#4}{#1}% \expandafter \endgroup \expandafter\xs_assign_xstringresult\expandafter{\xs_reserved_E}{#6}% \gdef\groupID{}% } \def\exploregroups{\let\xs_exploregroups\relax} \def\noexploregroups{\def\xs_exploregroups{\let\xs_toks\relax}} \def\saveexploremode{\let\xs_saveexploremode\xs_exploregroups} \def\restoreexploremode{\let\xs_exploregroups\xs_saveexploremode} % Remplace les #1 premières occurences de la chaine #3 % par la chaine #4 dans la chaine #2 \xs_newmacro\StrSubstitute{0}{3}{1}{% \def\xs_reserved_D{#2}\let\xs_reserved_C\empty\def\xs_reserved_E{#3}% \decimalpart0 \integerpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atoccurfound{% \xs_ifnum{#1<1 } {\def\xs_reserved_A{#4}} {\xs_ifnum{\decimalpart>#1 } {\def\xs_reserved_A{#3}} {\def\xs_reserved_A{#4}}% }% \xs_eaddtomacro\xs_reserved_C{\xs_reserved_A}% }% \xs_ifempty{#3}% {\xs_exparg\xs_assign_xstringresult{\xs_reserved_D}{#5}% } {\xs_read_reserved_D \xs_exparg\xs_assign_xstringresult{\xs_reserved_C}{#5}% }% } % Supprime les #1 premières occurrences de #3 dans #2 \xs_newmacro\StrDel{0}{2}{1}{\xs_StrSubstitute[#1]{#2}{#3}{}[#4]} \def\xs_exitallgroups{\ifnum\xs_nestlevel>0 \endgroup\expandafter\xs_exitallgroups\fi} % Compte combien d'unités syntaxiques contient la chaine #1 ? \xs_newmacro\StrLen{}{1}{1}{% \def\xs_reserved_C{#1}\decimalpart0 \def\xs_nestlevel{0}\gdef\groupID{0}% \let\xs_atbegingroup\relax \def\xs_atendofgroup{\expandafter\endgroup\expandafter\decimalpart\number\decimalpart\relax}% \let\xs_atnextsyntaxunit\relax \xs_read_reserved_C \xs_exparg\xs_assign_xstringresult{\number\decimalpart}{#2}% } % Macro interne fermant autant de groupes que nécessaire pour trouver une unité syntaxique derrière \def\xs_continuetonext{% \xs_exparg\xs_ifempty{\xs_reserved_C}% {\ifnum\xs_nestlevel>0 \xs_endgroup_eaddtomacro\xs_reserved_B{\expandafter{\xs_reserved_B}}% on concatène \expandafter\xs_continuetonext% on recommence \fi} {}% }% \def\xs_calc_grouIP{% \begingroup\def\xs_reserved_A{0}% \xs_ifx{\xs_reserved_A\groupID}% si on arrive du groupe de niveau 0 {\endgroup\xdef\groupID{\number\integerpart}}% on met simplement le niveau {\endgroup\xs_eearg{\gdef\groupID}{\expandafter\groupID\expandafter,\number\integerpart}}% } % Cette macro coupe la chaine #1 après l'unité syntaxique n°#2 \def\StrSplit{% \xs_ifstar {\let\xs_reserved_E\xs_continuetonext\StrSplit_i}% {\let\xs_reserved_E\relax\StrSplit_i}% } \xs_newmacro\StrSplit_i{}{2}{0}{\xs_StrSplit_ii{#2}{#1}\xs_StrSplit_iii} % Cette macro interne coupe la chaine #2 après l'unité syntaxique n°#1 % Le début est assigné dans \xs_reserved_B et la fin dans \xs_reserved_C \long\def\xs_StrSplit_ii#1#2{% \let\xs_reserved_B\empty\def\xs_reserved_C{#2}\def\xs_reserved_D{#1}\gdef\groupID{}% \ifnum#1>0 \decimalpart0 \integerpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atendofgroup{% \edef\__temp{% \endgroup \noexpand\decimalpart=\number\decimalpart\relax \unexpanded{\xs_addtomacro\xs_reserved_B}{{\unexpanded\expandafter{\xs_reserved_B}}}% }% \__temp }% \def\xs_atbegingroup{\let\xs_reserved_B\empty}% \def\xs_atnextsyntaxunit{% \xs_eaddtomacro\xs_reserved_B{\xs_reserved_A}% \xs_ifnum{\decimalpart=\xs_reserved_D\relax} {\ifx\xs_reserved_C\empty\xs_reserved_E\fi \edef\__temp{\def\noexpand\xs_reserved_B{\unexpanded\expandafter{\xs_reserved_B}}\def\noexpand\xs_reserved_C{\unexpanded\expandafter{\xs_reserved_C}}}% \expandafter\xs_exitallgroups\__temp \xs_gobarg% mange l'appel récursif \xs_read_reserved_C dans \xs_read_reserved_C } {}% }% \xs_read_reserved_C \fi } \def\xs_StrSplit_iii#1#2{\let#1\xs_reserved_B\let#2\xs_reserved_C} % StrCut[n]{}{}\macroA\macroB % \macroA reçoit ce qui se trouve avant la n ème occurrence dans % \macroB reçoit ce qui est après cette n ème occurrence \xs_newmacro*3\StrCut{1}{2}{0}{% \xs_ifempty{#3}% {\gdef\groupID{}\let\xs_reserved_C\empty\let\xs_reserved_E\empty } {\xs_ifnum{#1<1 } {\xs_StrCut_ii{#2}{#3}1} {\xs_StrCut_ii{#2}{#3}{#1}}% }% \xs_StrCut_iii } \long\def\xs_StrCut_ii#1#2#3{% \let\xs_reserved_C\empty\def\xs_reserved_D{#1}\def\xs_reserved_E{#2}% \decimalpart0 \integerpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atoccurfound{% \xs_ifnum{\decimalpart=\numexpr(#3)\relax} {\edef\__temp{% \def\noexpand\xs_reserved_C{\unexpanded\expandafter{\xs_reserved_C}}% \def\noexpand\xs_reserved_D{\unexpanded\expandafter{\xs_reserved_D}}% \noexpand\decimalpart=\number\decimalpart\relax }% \expandafter\xs_exitallgroups\__temp \xs_gobarg% stoppe la récursivité dans \xs_read_reserved_D } {\xs_eaddtomacro\xs_reserved_C{\xs_reserved_E}% }% }% \xs_read_reserved_D \let\xs_reserved_E\xs_reserved_D \xs_exparg{\def\xs_reserved_D}{\xs_reserved_C#2}% } \long\def\xs_StrCut_iii#1#2{\let#1\xs_reserved_C\let#2\xs_reserved_E} % De la chaine #1, renvoie ce qui se trouve entre les positions % #2 et #3, unités syntaxiques aux positions compris ! \xs_newmacro\StrMid{}{3}{1}{% \begingroup \noexploregroups \let\xs_reserved_E\relax \xs_StrSplit_ii{#3}{#1}% \edef\xs_reserved_C{\number\numexpr#2-1}% \let\xs_reserved_E\relax \xs_exptwoargs\xs_StrSplit_ii\xs_reserved_C\xs_reserved_B \expandafter\endgroup \expandafter\xs_assign_xstringresult\expandafter{\xs_reserved_C}{#4}% \gdef\groupID{}% } % supprime #2 unités syntaxiques à gauche dans la chaine #1 \xs_newmacro\StrGobbleLeft{}{2}{1}{% \let\xs_reserved_E\relax \xs_StrSplit_ii{#2}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_C}{#3}% } % extrait de #1 la chaine à gauche de longueur #2 \xs_newmacro\StrLeft{}{2}{1}{% \let\xs_reserved_E\relax \xs_StrSplit_ii{#2}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_B}{#3}% } % supprime #2 unités syntaxiques à droite dans la chaine #1 \xs_newmacro\StrGobbleRight{}{2}{1}{% \xs_StrLen{#1}[\xs_reserved_D]% \let\xs_reserved_E\relax \xs_exparg\xs_StrSplit_ii{\number\numexpr\xs_reserved_D-#2}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_B}{#3}% } % renvoie #2 unités syntaxiques à la droite de la chaine #1 \xs_newmacro\StrRight{}{2}{1}{% \xs_StrLen{#1}[\xs_reserved_D]% \let\xs_reserved_E\relax \xs_exparg\xs_StrSplit_ii{\number\numexpr\xs_reserved_D-#2}{#1}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_C}{#3}% } \xs_newmacro\StrChar{}{2}{1}{% \let\xs_reserved_B\empty\def\xs_reserved_C{#1}\def\xs_reserved_D{#2}% \ifnum#2>0 \def\groupID{0}\def\xs_nestlevel{0}\integerpart0 \decimalpart0 \let\xs_atbegingroup\relax \def\xs_atendofgroup{\expandafter\endgroup\expandafter\decimalpart\number\decimalpart\relax}% \def\xs_atnextsyntaxunit{% \ifnum\decimalpart=\xs_reserved_D% la n ième US est atteinte ? \edef\__temp{\def\noexpand\xs_reserved_B{\unexpanded\expandafter{\xs_reserved_A}}}% on capture l'US en cours qui est celle cherchée \expandafter\xs_exitallgroups\__temp \expandafter\xs_gobarg% stoppe la récursivité dans \xs_read_reserved_C \fi }% \xs_read_reserved_C \fi \xs_exparg\xs_ifempty{\xs_reserved_B}% {\gdef\groupID{}} {}% \xs_exparg\xs_assign_xstringresult{\xs_reserved_B}{#3}% } % Combien de fois compte t-on #2 dans #1 ? \xs_newmacro\StrCount{}{2}{1}{% \def\xs_reserved_D{#1}\def\xs_reserved_E{#2}\let\xs_reserved_C\empty \xs_ifempty{#2}% {\xs_assign_xstringresult{0}{#3}% } {\decimalpart0 \integerpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atoccurfound{\let\xs_reserved_C\empty\xs_read_reserved_D}% \xs_read_reserved_D \xs_exparg\xs_assign_xstringresult{\number\decimalpart}{#3}% }% } % renvoie la position de l'occurrence #1 de la sous chaine #3 dans la chaine #2 \xs_newmacro\StrPosition{1}{2}{1}{% \xs_cutafteroccur{#2}{#3}{#1}% \let\xs_reserved_E\groupID \xs_ifx{\xs_reserved_C\xs_reserved_D} {\xs_assign_xstringresult{0}{#4}% \let\xs_reserved_E\empty } {\xs_exparg\xs_StrLen{\xs_reserved_C}[\xs_reserved_C]% \xs_exparg\xs_assign_xstringresult{\number\numexpr\xs_reserved_C+1}{#4}% }% \global\let\groupID\xs_reserved_E } \def\comparestrict{\def\xs_comparecoeff{1}} \def\comparenormal{\def\xs_comparecoeff{0}} \def\savecomparemode{\let\xs_saved_comparecoeff\xs_comparecoeff} \def\restorecomparemode{\let\xs_comparecoeff\xs_saved_comparecoeff} % Compare les deux arguments #1 et #2 % Renvoie 0 s'ils sont égaux et renvoie la % position de la premiere unité syntaxiques différente sinon \xs_newmacro*2\StrCompare{}{2}{1}{% \def\xs_reserved_A{#1}% \def\xs_reserved_B{#2}% \xs_ifx{\xs_reserved_B\xs_reserved_A} {\xs_assign_xstringresult{0}{#3}} {\xs_StrCompare_i{#1}{#2}{#3}}% } \long\def\xs_StrCompare_i#1#2#3{% \def\xs_StrCompare_iii##1{% \let\xs_reserved_A\empty \xs_exparg\xs_ifempty{\xs_reserved_C}% {\edef\xs_reserved_A{*\xs_comparecoeff}}% {\xs_exparg\xs_ifempty{\xs_reserved_D}% {\edef\xs_reserved_A{*\xs_comparecoeff}} {}% }% \def\xs_next{\xs_exparg\xs_assign_xstringresult{\number\numexpr##1\xs_reserved_A\relax}{#3}}% }% \def\xs_StrCompare_ii##1{% ##1 est la position \xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_C}\xs_reserved_A \xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_D}\xs_reserved_B \xs_ifx{\xs_reserved_B\xs_reserved_A} {\xs_exparg\xs_ifempty{\xs_reserved_A}% {\xs_StrCompare_iii{##1}% les 2 unités syntaxiques sont égales, on renvoie la position } {\def\xs_next{\xs_exparg\xs_StrCompare_ii{\number\numexpr##1+1}}% les 2 unités syntaxiques sont égales et non vides, on recommence \xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_C}\xs_reserved_C \xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_D}\xs_reserved_D }% }% les 2 unités syntaxiques sont différentes : on renvoie la position {\xs_StrCompare_iii{##1}% }% \xs_next }% \def\xs_reserved_C{#1}\def\xs_reserved_D{#2}% \xs_StrCompare_ii1% } \xs_newmacro\StrFindGroup{}{2}{1}{% \def\xs_reserved_A{#2}\def\xs_reserved_B{0}% \xs_ifx{\xs_reserved_A\xs_reserved_B} {\xs_assign_xstringresult{{#1}}{#3}} {\xs_StrFindGroup_i{#1}{#2}[#3]}% } \long\def\xs_StrFindGroup_i#1#2[#3]{% \def\xs_StrFindGroup_ii{% \xs_exparg\xs_ifempty{\xs_reserved_C}% {\xs_assign_xstringresult{}{#3}% s'il ne reste plus rien, on renvoie vide } {\xs_exparg\xs_returnfirstsyntaxunit{\xs_reserved_C}\xs_reserved_D \xs_ifx{\bgroup\xs_toks}% si la 1ère unité syntaxique est un groupe explicite {\advance\decimalpart1 % on augmente le compteur \xs_ifnum{\decimalpart=\xs_reserved_A\relax}% on est au groupe cherché lors de la profondeur courante ? {\xs_ifx{\empty\xs_reserved_B}% on est à la profondeur maximale ? {\xs_exparg\xs_assign_xstringresult{\xs_reserved_D}{#3}}% on renvoie ce groupe {\xs_exparg{\expandafter\xs_StrFindGroup_i\xs_reserved_D}{\xs_reserved_B}[#3]}% on recommence avec ce groupe et les profondeurs de recherche restantes } {\xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_C}\xs_reserved_C \xs_StrFindGroup_ii }% } {\xs_exparg\xs_removefirstsyntaxunit{\xs_reserved_C}\xs_reserved_C \xs_StrFindGroup_ii }% }% }% \xs_extractgroupnumber{#2}\xs_reserved_A\xs_reserved_B \decimalpart0 \xs_ifnum{\xs_reserved_A>0 } {\def\xs_reserved_C{#1}} {\let\xs_reserved_C\empty}% \xs_StrFindGroup_ii } \def\xs_extractgroupnumber#1#2#3{% \def\xs_extractgroupnumber_i##1,##2\_nil{\def#2{##1}\def#3{##2}}% \xs_extractgroupnumber_i#1,\_nil \ifx\empty#3\else\xs_extractgroupnumber_i#1\_nil\fi } \def\expandingroups{\let\xs_expandingroups\exploregroups} \def\noexpandingroups{\let\xs_expandingroups\noexploregroups} \def\StrExpand{\xs_testopt{\xs_StrExpand}{1}} \long\def\xs_StrExpand[#1]#2#3{% \begingroup \xs_expandingroups \xs_ifnum{#1>0 } {\integerpart#1 \decimalpart0 \gdef\groupID{0}\def\xs_nestlevel{0}% \def\xs_atendofgroup{\xs_endgroup_eaddtomacro\xs_reserved_B{\expandafter{\xs_reserved_B}}}% \def\xs_atbegingroup{\let\xs_reserved_B\empty}% \def\xs_atnextsyntaxunit{\xs_eeaddtomacro\xs_reserved_B{\xs_reserved_A}}% \def\xs_reserved_C{#2}% \xs_StrExpand_i{#1}% Appel de la macro récursive } {\def\xs_reserved_B{#2}% } \expandafter \endgroup \expandafter\def\expandafter#3\expandafter{\xs_reserved_B}% \gdef\groupID{}% } \long\def\xs_StrExpand_i#1{% \xs_ifnum{#1>0 } {\let\xs_reserved_B\empty \xs_read_reserved_C \let\xs_reserved_C\xs_reserved_B \def\xs_reserved_A{\xs_exparg\xs_StrExpand_i{\number\numexpr#1-1}}% } {\let\xs_reserved_A\relax }% \xs_reserved_A } \def\scancs{\xs_testopt{\xs_scancs}{1}} \long\def\xs_scancs[#1]#2#3{% \xs_StrExpand[#1]{#3}{#2}% \edef#2{\detokenize\expandafter{#2}}% } \restorexstringcatcode \setverbdelim{|}% \fullexpandarg\saveexpandmode \comparenormal\savecomparemode \noexploregroups\saveexploremode \expandingroups \endinput % % H I S T O R I Q U E %------------------------------------------------------------------------------ %% v1.0 4/7/2008 : - Package achevé. % 7/7/2008 : - Package publié sur CTAN. %------------------------------------------------------------------------------ % v1.1 15/8/2008 % - Le package ne nécessite plus LaTeX et est désormais utilisable sous % Plain eTeX. % - Ajout des macros \IfInteger et \IfDecimal. % Pour cette dernière, la partie entière et la partie décimale sont % récupérables dans les compteurs \integerpart et \decimalpart. Ce % qu'il reste après qu'un caractère non attendu soit rencontré est % dispo dans \xs_remainafterdecimal % - Correction d'un bug dans \tokenize : % suppression de l'expansion avec \normalexpandarg pour éviter le bug % avec les lettres accentuées dans l'exemple : \tokenize\aa{éù} % - Amélioration du code de la plupart des macros. % - écriture d'une routine \xs_newmacro définissant les macros. % - Suppression de tous les compteurs grace à \numexpr. % - Nouvelles fonctionnalités internes dans : % \IfSubStr % ajout des chaines se trouvant avant et après l'occurrence testée % dans les sc \xs_behind_IfSubStr et \xs_before_IfSubStr % \StrCount % a) ajout de ce qui se trouve après la dernière occurrence dans la % sc \xs_StrCount_afterlastocccur % b) la macro ne dépend plus d'aucune autre %------------------------------------------------------------------------------ % v1.2 23/8/2008 % - Amélioration du code de \StrMid \StrGobbleLeft, \StrLeft, % \StrGobbleRight et \StrRight à l'aide de la nouvelle macro % \xs_StrSplit_i % - Nouveau test \IfStrEq qui compare 2 chaines % - Nouveau test \IfEq qui compare 2 chaines (ou 2 nombres si les 2 chaines % contiennent un nombre) % - Correction d'un bug dans \xs_formatnumber. La macro n'envisageait pas le % cas où son argument était vide ce qui faiasait planter % \IfDecimal{}{V}{F} et \IfInteger{}{V}{F} %------------------------------------------------------------------------------ % v1.3 21/9/2008 % - Amélioration du code, mise en place de récursivités terminales dans % toutes les macros récursives, simplification de \xs_TestEqual en % utilisant \ifx % - Nouvelles macros \IfStrEqCase et \IfEqCase % Teste si une chaîne est égale à plusieurs chaines données et % exécute un code spécifique % - Création de la macro publique \StrSplit % Coupe la chaine en 2 à une position donnée % - Nouvelle macro \StrCompare % Compare 2 chaînes et renvoie la position du 1er caractère différent. % Renvoie 0 si elles sont égales. %------------------------------------------------------------------------------ % v1.3a 29/9/2008 % - Correction d'un bug : \expandafter#1 % dans les macros \IfSubStrBefore et \StrBetween qui buggue si #1 est un % nombre > 9 ! % - Ré écriture de \xs_returnfirstsyntaxunit et \xs_gobarg qui % deviennent purement développables. % - Ré écriture de \StrLen % - Nouvelle macro interne \xs_cutafteroccur % Coupe la chaine après la nième occurrence d'une sous chaine. % - Code amélioré de \StrMid, \StrLeft, \StrGobbleLeft, \StrRight, % \StrGobbleRight grace à \xs_cutafteroccur %------------------------------------------------------------------------------ % v1.4 4/11/2008 % - xstring traite ses arguments par unité syntaxique, et donc toutes les % macros internes ont été ré-écrites à cette fin. % - Le manuel a été ré-écrit, rendu plus clair et mieux structuré. % - Nouvelle fonctionnalité : \expandarg % le 1er lexème de chaque argument est 1-développé avant que la % macro ne soit appelée. Si un argument comporte plus qu'1 lexème, % il faut passer par \scancs*[1] pour 1-développer toutes les unités % syntaxiques. % - Correction de 2 erreurs dans le manuel anglais : 0 est la valeur par % défaut dans \StrSubstitute et \StrDel % - Cohérence dans les nom des séquences de contrôles temporaires qui % sont désormais : \xs_reserved_A à \xs_reserved_E %------------------------------------------------------------------------------ % v1.4a 12/11/2008 % - Correction d'un bug dans \xs_StrSplit dans le cas où l'argument % numérique est <1 : la 1ere coupure doit être vide et le seconde doit % être égale à l'argument. %------------------------------------------------------------------------------ % v1.4b 16/11/2008 % - Correction de 2 bugs dans \xs_returnfirstsyntaxunit %------------------------------------------------------------------------------ % v1.5 31/12/2008 % - Nouvelles fonctionnalités dans la recherche de groupes par un % identificateur caractérisant leur imbrication. Implémentation de : % 1) \StrFindGroup % 2) \groupID % - Ajout des nouvelles macros génériques \xs_read_reserved_C et % \xs_read_reserved_D % - Correction d'un bug dans \StrSplit : \StrSplit{{a}bc}{1}[\truc] faisait % que \truc contenait «a{a}». % - Correction d'un bug dans \xs_newmacro : l'argument optionnel était % détokénisé à tort dans les macros étoilées. % - Dans \xs_newmacro, on doit choisir le numéro du dernier argument devant % être détokénisé dans le cas d'une macro étoilée (sachant que % l'argument optionnel ne l'est jamais) %------------------------------------------------------------------------------ % v1.5a 09/2/2009 % - Nouvelle macro \StrExpand, sensible au mode d'exploration. La macro est % indépendante du mode d'exploration et dispose de ses propres commandes % d'exploration : \expandingroups et \noexpandingroups % - Ré-écriture de \scancs, qui devient triviale puisque dépendant de % \StrExpand. Il n'y a plus de version étoilée et désormais, \scancs % est sensible au mode d'exploration. % - Correction d'un bug dans \IfInteger : \IfInteger{2.0}{V}{F} affichait F % - Correction de bugs : mettre \let\xs_reserved_E\relax avant d'appeler % \xs_StrSplit_i % - Suppression des messages d'erreur, de warning et d'info. xstring devient % donc silencieux ;) % - Passage de 3 à 2 compteurs % - Le manuel est désormais plus coloré, en espérant qu'il sera un peu % plus agréable à lire ! %------------------------------------------------------------------------------ % v1.5b 13/3/2009 % - Modification d'un grand nombre de macros de telle sorte que vraiment % n'importe quel code peut désormais être placé dans les arguments des % macros (comme par exemple des \if, \ifx ou autre, même sans leur \fi, ce % qui n'était pas possible jusqu'alors) %------------------------------------------------------------------------------ % v1.5c 05/6/2009 % - Correction d'un bug dans \IfBeginWith : il faut appeler % \xs_returnfirstsyntaxunit pour l'argument #2 puis pour l'argument #1 et % non pas l'inverse pour que \xs_toks reflète ce qui se trouve dans #1 % - Correction d'un bug dans \xs_returnfirstsyntaxunit au cas où #1 commence % par un espace : mettre \xs_toks à 0 % - Correction d'un bug dans \xs_returnfirstsyntaxunit : % \xs_returnfirstsyntaxunit{{}}\truc % faisait que \truc se développait en un espace. % - Correction d'un bug dans \xs_removefirstsyntaxunit où le cas % \xs_removefirstsyntaxunit{ {b}}\truc % donnait \truc se développant en «b» au lieu de «{b}». %------------------------------------------------------------------------------ % v1.5d 28/3/2010 % - Correction d'un bug dans \IfInteger : \afterinteger ne donnait pas le % bon résultat. Par exemple avec 1.23 il renvoyait {} au lieu de {.23} % - \xs_formatnumber ne detokenize plus ses arguments (que m'est-il passé % par la tête pour que je code un truc comme ca ?), et donc % \afterinteger et \xs_afterdecimal ont des tokens ayant le bon % catcode %------------------------------------------------------------------------------ % v1.6 24/10/2012 % - Correction d'un bug dans \xs_cutafteroccur au cas où #3<1 % - \IfDecimal ne \detokenize plus l'argument. % - Nouvelle version de \tokenize qui enlevait les accolades si son argument % était entre accolades % - Nouvelle macro \StrCut % - \xs_cutafteroccur_i ne produit plus d'espace indésirable si #3 était % évalué par \numexpr %------------------------------------------------------------------------------ % v1.7 13/1/2013 % - Correction d'un manque de rigueur dans les assignations concernant % \groupID : elles sont toutes globales désormais % - Les macros de xstring sont \long %------------------------------------------------------------------------------ % v1.7a 28/2/2013 % - Correction d'un espace parasite dans \xs_removefirstsyntaxunit %------------------------------------------------------------------------------ % v1.7b 29/7/2013 % - La date contenue dans \xstringdate est désormais conforme %------------------------------------------------------------------------------ % v1.7c 13/10/2013 % - les macros impliquées dans \IfStrCase et \IfStrEqCase sont désormais \long %------------------------------------------------------------------------------ % v1.8 07/12/2018 % - affectations globales supprimées (sauf pour \groupID) afin de ne pas % surcharger la pile de sauvegarde % - nettoyage du code, corrections de lourdeurs et maladresses %------------------------------------------------------------------------------ % v1.81 09/12/2018 % - bug dans \IfInteger, c'est \xs_ifx{\empty\afterdecimal} et non pas % \xs_ifx{\empty\xs_afterdecimal}