diff options
author | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2008-07-07 17:02:47 +0000 |
---|---|---|
committer | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2008-07-07 17:02:47 +0000 |
commit | 3e11740fbb02fefabd315dcd6e7c3d2dcb5ba499 (patch) | |
tree | 6d224f752c7b4cc8164f92c8faedb3c747f0fd6d /Master/texmf-dist/tex/latex/xstring | |
parent | 56debe23624f5565c111943964e5a74530e1216a (diff) |
new latex package xstring
git-svn-id: svn://tug.org/texlive/trunk@9337 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/xstring')
-rw-r--r-- | Master/texmf-dist/tex/latex/xstring/xstring.sty | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/xstring/xstring.sty b/Master/texmf-dist/tex/latex/xstring/xstring.sty new file mode 100644 index 00000000000..c9a7c45b588 --- /dev/null +++ b/Master/texmf-dist/tex/latex/xstring/xstring.sty @@ -0,0 +1,704 @@ +% __________________________________________________ +% | | +% | | +% | xstring v1.0 | +% | | +% | July, 4th 2008 | +% | | +% |__________________________________________________| +% +% This is xtring.sty +% +% Christian Tellechea 2008 +% email : unbonpetit@gmail.com +% ------------------------------------------------------------------- +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% ------------------------------------------------------------------- +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Christian Tellechea +% +% This work consists of the files +% xstring.sty (this file) +% readme.txt +% xstring_doc_fr.tex, xstring_doc_fr.pdf (manual in french) +% xstring_doc_en.tex, xstring_doc_en.pdf (manual in english) +% xstring_test.tex, xstring_test.pdf (test file) +% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{xstring}[2008/07/04 v1.0 Extended macros for strings. C Tellechea] + +% Messages pour situations particulières +\newcommand\@xs@BadNumArg {\PackageInfo{xstring}{numerical argument out of range}} +\newcommand\@xs@ArgNotFound {\PackageInfo{xstring}{substring not found in string}} +\newcommand\@xs@ArgEmpty {\PackageInfo{xstring}{empty argument}} + +% Ouvre un groupe où les catcodes sont à 12 et à 10 pour les espaces +% ensuite, appelle \@xs@ReadVerb qui lit un argument entre délimiteurs verb +\newcommand\@xs@MakeVerb{% lit 1 argument et le transforme en verb + \begingroup% groupe où les catcodes sont à 12 pour la lecture suivante + \let\do\@makeother + \dospecials% on entre dans le mode verb + \obeyspaces% et on tient compte des espaces + \@xs@ReadVerb}% et on va lire l'argument + +% Définit \@xs@ReadVerb qui lit un argument entre délimiteurs verb +\newcommand\setverbdelim[1]{% définit quel est le délimiteur de verb + \@xs@StrLen{#1}[\@len@verbdelim]% + \ifnum\@len@verbdelim=\@ne + \else + \PackageWarning{xstring}{verb delimiter is not a single char}% + \fi + \def\@xs@ReadVerb##1#1##2#1{% lit ##2 qui est entre les délimiteurs de verb + \endgroup% on ferme le groupe + \@xs@afterreadverb{##2}}}% on appelle l'exécution de fin + +% Assigne l'argument entre délimiteur verb dans la sc #1 +\newcommand\verbtocs[1]{% + \def\@xs@afterreadverb##1{\edef#1{##1}}% + \@xs@MakeVerb} + +% A l'aide d'un pont d'\expandafter, développe d'un niveau toutes +% les sc de #1 puis et assigne le résultat à \result +\def\@xs@expandallonelevel#1#2\@nil{% + \expandafter\expandafter\expandafter\def% plein d'expandafter + \expandafter\expandafter\expandafter\result% pour développer #1 d'un niveau + \expandafter\expandafter\expandafter{\expandafter\result#1}% et l'ajouter à \result + \ifx\@empty#2\@empty\else\@xs@expandallonelevel#2\@nil\fi}% s'il reste des sc, on boucle + +% \scancs développe #3 autant de fois que spécifié en #1 +% puis le développement obtenu est verbatimisé. +% On utilise \@xs@expandalltodepth autant de fois que #1 le spécifie +% puis on invoque \detokenize à la fin +\newcount\depth@scancs +\newcommand\scancs{% + \@ifstar% + {\PackageWarning{xstring}{if third argument or its expansion have braces or spaces, they will be removed when scanned! Use starred \string\scancs* macro with care}% + \def\@testornot{\@notestcs}% + \@xs@scancs}% + {\def\@testornot{\@testcs}% + \@xs@scancs}} +\newcommand\@xs@scancs[3][1]{% +% #1 : profondeur à développer avant de verbatimiser +% #2 : nom de la sc recevant la chaine verb +% #3 : nom de la sc à développer et à transformer en verb + \depth@scancs\z@ + \def\@notestcs##1\@nil{\relax}% + \def\@testcs##1##2\@nil{% + \ifx\@empty##2\@empty + \else% si la séquence de contrôle n'est pas seule, on gueule + \PackageError{xstring}{third argument or its expansion is not a single control sequence! Use \string\scancs* instead of \string\scancs (see manual).}% + \fi}% + \def\@xs@expandalltodepth##1\@nil{% développe de #1 niveaux toutes les sc de #3 et l'assigne à \the@result + \def\result{}% + \ifnum\depth@scancs<#1% tant que la profondeur de développement n'est pas atteinte + \@testornot##1\@nil% on teste éventuellement si le développement comporte 1 seule sc + \expandafter\@xs@expandallonelevel\the@result\@nil% on développe tout d'un niveau + \expandafter\def\expandafter\the@result\expandafter{\result}% + \advance\depth@scancs\@ne + \expandafter\@xs@expandalltodepth\the@result\@nil% et on recommence + \fi}% + \def\the@result{#3}% + \@xs@expandalltodepth#3\@nil + \expandafter\def\expandafter#2\expandafter{\detokenize\expandafter{\the@result}}} + +% Cette macro développe #2 au maximum (ce qui doit donner du texte), +% puis le transforme en token et l'assigne à #1 +% On ouvre un groupe dans lequel un \@nil est inséré à la fin du fichier virtuel +% lu avec \scantokens. Ce \@nil sert à l'argument délimité de \@AssignResult +\newcommand\tokenize[2]{% + \begingroup + \expandafter\@xs@def\expandafter\the@text\expandafter{#2}% on développe en accord avec \fullexpandarg ou \noexpandarg + \def\@AssignResult##1\@nil{\def#1{##1}}% on assigne en tenant compte du \@nil qui vient de la fin du fichier virtuel + \everyeof{\@nil}% met un \@nil à la fin du fichier virtuel + \endlinechar\m@ne + \expandafter\expandafter\expandafter\endgroup% on retarde la fermeture du groupe + \expandafter\@AssignResult\scantokens\expandafter{\the@text}}% pout faire l'assignation + +% Macro très simple qui assigne ou affiche le résultat, selon la présence +% ou non de #2 qui est l'argument optionnel venant en dernière position des macros +\newcommand\@xs@ReturnResult[2]{% + \expandafter\ifx\expandafter\@empty\detokenize{#2}\@empty% pas de nom ? + #1% on affiche #1 + \else% il y a un nom donc + \edef#2{#1}% on met #1 dans #3 avec edef + \fi} + +% Pas d'expansion des arguments +\newcommand\normalexpandarg{% + \let\@xs@def\def% on définit\@call avec \def + \let\@xs@expand\@empty}% et on neutralise \@xs@expand + +% Expansion des arguments +\newcommand\fullexpandarg{% + \let\@xs@def\edef% on définit\@call avec \edef + \let\@xs@expand\noexpand}% et on utilise \noexpand + +% Cette macro interne enlève le 1er caractère de la chaine passée en argument +\def\@xs@RemoveFirst#1[#2]{% + \ifx\@empty#1\@empty + \edef#2{}% + \else + \@xs@IfBeginWith{#1}{ }% + {\@xs@StrBehind[1]{#1}{ }[#2]}% + {\def\@xs@RemoveFirst@ii##1##2\@nil{\edef#2{##2}}% + \@xs@RemoveFirst@ii#1\@nil}% + \fi} + +% Cette macro interne renvoie le 1er caractère de la chaine passée en argument +\def\@xs@ReturnFirst#1[#2]{% + \ifx\@empty#1\@empty% + \edef#2{}% + \else + \@xs@IfBeginWith{#1}{ }% + {\edef#2{ }}% + {\def\@xs@ReturnFirst@ii##1##2\@nil{\edef#2{##1}}% + \@xs@ReturnFirst@ii#1\@nil}% + \fi} + +% 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. +\newcommand\@xs@expand@and@detokenize[2]{% + \@xs@def#1{#2}% on développe #2 selon \fullexpandarg ou \normalexpandarg + \edef#1{\detokenize\expandafter{#1}}}% puis on détokenize et on assigne à #1 + +% Cette macro teste si une chaine (#2) en contient au moins n(#1) fois une autre (#3) +% Pour cela, on teste "ce qu'il reste" après #2. On construit une récursivité sur le +% compteur d'occurences \Occurrence@IfSubStr. Si "ce qu'il reste" est vide avant d'atteindre +% l'occurence voulue, le test est négatif, sinon on poursuit par récursivité sur "ce qu'il reste" +% jusqu'à l'occurrence voulue. Si l'on atteint l'occurrence voulue, le test est positif. +% On utilise la séquence de contrôle non définie \@delimit pour éviter un effet +% indésirable qui ferait que \IfSubStr{a}{aa}{V}{F} renverrait V +\newcommand\IfSubStr{\@ifstar\@xs@IfSubStr@@\@xs@IfSubStr@}% +\newcommand\@xs@IfSubStr@@[3][1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \edef\@call{\noexpand\@xs@IfSubStr[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}% + \@call} +\newcommand\@xs@IfSubStr@[3][1]{% + \@xs@def\@call{\@xs@expand\@xs@IfSubStr[#1]{#2}{#3}}% + \@call} +\newcount\Occurrence@IfSubStr +\def\@xs@IfSubStr[#1]#2#3{% +% est ce que la chaine #2 contient au moins #1 fois la sous chaine #3 ? + \ifnum#1<\@ne% on gère les cas spéciaux + \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% #1<1 -> on exécute #5 + \else + \ifx\@empty#3\@empty% + \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% si #3 est vide -> on écécute #5 + \else + \Occurrence@IfSubStr\z@ + \def\@xs@IfSubStr@i##1#3##2\@nil{% + \advance\Occurrence@IfSubStr\@ne + \ifx\@empty##2\@empty%% qu'y a t-il apès la sous chaine ? + \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% rien -> on exécute #5 + \else% si ce qu'il est reste n'est pas vide + \ifnum\Occurrence@IfSubStr<#1% tant que le nombre d'occurrence n'est pas atteint + \@xs@IfSubStr@i##2\@nil% on recommence + \else + \def\@xsresult@IfSubStr{\expandafter\@firstoftwo}% nombre d'occurrences atteint : on renvoie #4 + \fi + \fi}% + \@xs@IfSubStr@i#2\@delimit#3\@nil% \@delimit pour éviter le cas de \IfSubStr{a}{aa}{V}{F} qui renverrait V + \fi + \fi + \@xsresult@IfSubStr} + +\newcommand\IfBeginWith{\@ifstar\@xs@IfBeginWith@@\@xs@IfBeginWith@}% +\newcommand\@xs@IfBeginWith@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \@xs@expand@and@detokenize{\xsarg@b}{#2}% + \edef\@call{\noexpand\@xs@IfBeginWith{\xsarg@a}{\xsarg@b}} + \@call} +\newcommand\@xs@IfBeginWith@[2]{% + \@xs@def\@call{\@xs@expand\@xs@IfBeginWith{#1}{#2}}% + \@call} +\newcommand\@xs@IfBeginWith[2]{% + \@xs@IfSubStr[1]{#1}{#2}% + {\ifx\@empty#2\@empty% + \def\@xs@IfBeginWith@i##1\@nil{\expandafter\@firstoftwo}% si #2 est vide, on renvoie vrai + \else + \def\@xs@IfBeginWith@i##1#2##2\@nil{% qu'y a t-il avant #2 ? + \ifx\@empty##1\@empty + \expandafter\@firstoftwo% rien, donc #2 est au début, on renvoie vrai + \else + \expandafter\@secondoftwo + \fi}% + \fi}% + {\def\@xs@IfBeginWith@i##1\@nil{\expandafter\@secondoftwo}}% si #2 n'est pas dans #1, on renvoie faux + \@xs@IfBeginWith@i#1\@delimit#2\@nil} + +\newcommand\IfEndWith{\@ifstar\@xs@IfEndWith@@\@xs@IfEndWith@}% +\newcommand\@xs@IfEndWith@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \@xs@expand@and@detokenize{\xsarg@b}{#2}% + \edef\@call{\noexpand\@xs@IfEndWith{\xsarg@a}{\xsarg@b}} + \@call} +\newcommand\@xs@IfEndWith@[2]{% + \@xs@def\@call{\@xs@expand\@xs@IfEndWith{#1}{#2}}% + \@call} +\newcommand*\@xs@IfEndWith[2]{% +% Est ce que la chaine #1 finit par la chaine #2 ? + \@xs@IfSubStr[1]{#1}{#2}% + {\ifx\@empty#2\@empty% si #2 est vide -> test vrai + \def\@xs@remove@begining##1\@nil{\expandafter\@firstoftwo}% + \else + \def\@xs@remove@begining##1#2##2\@nil{% on isole ce qui est après #2, la chaine restante ##2 + \@xs@IfSubStr[1]{##2}{#2}% tant que #2 est dans la chaine restante.. + {\@xs@remove@begining##2\@nil}% ...on recommence + {\ifx\@empty##2\@empty% sinon, on regarde ce qui reste + \expandafter\@firstoftwo% rien -> test vrai + \else + \expandafter\@secondoftwo% quelque chose -> test faux + \fi + }% + }% + \fi}% + {\def\@xs@remove@begining##1\@nil{\expandafter\@secondoftwo}}% si #2 n'est pas dans #1 -> test faux + \@xs@remove@begining#2#1\@nil} + +\newcount\Occurrence@StrBefore +% Renvoie ce qui est à gauche de l'occurence n°#1 de la sous chaine #3 dans la chaine #2 +\newcommand\StrBefore{\@ifstar\@xs@StrBefore@@\@xs@StrBefore@} +\newcommand\@xs@StrBefore@@[3][1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBefore[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}% + \@call} +\newcommand\@xs@StrBefore@[3][1]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBefore[#1]{#2}{#3}}{}}% + \@call} +\def\@xs@StrBefore[#1]#2#3[#4]{% + \ifx\@empty#3\@empty% + \@xs@ArgNotFound% warning + \edef\@xs@result@StrBefore{}% + \else + \edef\@xs@result@StrBefore{}% resultat nul pour l'instant + \Occurrence@StrBefore\z@ + \def\@xs@StrBefore@i##1#3##2\@nil{% + \advance\Occurrence@StrBefore\@ne% on incrémente + \ifx\@empty##2\@empty% pas trouvé ? + \@xs@ArgNotFound% warning + \edef\@xs@result@StrBefore{}% renvoie vide + \else + \ifnum\Occurrence@StrBefore<#1% tant que c'est pas la bonne occurrence + \edef\@xs@result@StrBefore{\@xs@result@StrBefore##1#3}% on ajoute le début au résultat final + \@xs@StrBefore@i##2\@nil% on recommence avec la chaine restante + \else + \edef\@xs@result@StrBefore{\@xs@result@StrBefore##1}% on n'ajoute pas la chaine frontière #3 la dernière fois ! + \fi + \fi}% + \@xs@StrBefore@i#2\@delimit#3\@nil% + \fi + \@xs@ReturnResult{\@xs@result@StrBefore}{#4}} + +\newcount\Occurrence@StrBehind +% Renvoie ce qui est à droite de l'occurence n°#1 de la sous chaine #3 dans la chaine #2 +\newcommand\StrBehind{\@ifstar\@xs@StrBehind@@\@xs@StrBehind@} +\newcommand\@xs@StrBehind@@[3][1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBehind[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}% + \@call} +\newcommand\@xs@StrBehind@[3][1]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBehind[#1]{#2}{#3}}{}}% + \@call} +\def\@xs@StrBehind[#1]#2#3[#4]{% + \ifx\@empty#3\@empty% + \@xs@ArgNotFound% warning + \edef\@xs@result@StrBehind{}% + \else + \Occurrence@StrBehind\z@ + \def\@xs@StrBehind@i##1#3##2\@nil{% + \advance\Occurrence@StrBehind\@ne% on incrémente + \ifnum\Occurrence@StrBehind<#1% tant que c'est pas la bonne occurrence + \@xs@IfSubStr[1]{##2}{#3}% tant que la chaine restante contient #3 + {\@xs@StrBehind@i##2\@nil}% on recommence avec la chaine restante + {\@xs@ArgNotFound% sinon, info + \edef\@xs@result@StrBehind{}}% et on renvoie vide + \else + \edef\@xs@result@StrBehind{##2}% on renvoie la chaine restante + \fi}% + \@xs@IfSubStr[1]{#2}{#3}% + {\@xs@StrBehind@i#2\@nil}% + {\@xs@ArgNotFound% warning + \edef\@xs@result@StrBehind{}}% + \fi + \@xs@ReturnResult{\@xs@result@StrBehind}{#4}} + +\newcommand\IfSubStrBefore{\@ifstar\@xs@IfSubStrBefore@@\@xs@IfSubStrBefore@} +% est ce que l'occurrence #1 de la sous chaine #4 se trouve avant +% l'occurrence n°#2 de la sous chaine #5 dans la chaine #3? +\newcommand\@xs@IfSubStrBefore@@[4][1,1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \@xs@expand@and@detokenize{\xsarg@c}{#4}% + \edef\@call{\noexpand\@xs@IfSubStrBefore[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}% + \@call} +\newcommand\@xs@IfSubStrBefore@[4][1,1]{% + \@xs@def\@call{\@xs@expand\@xs@IfSubStrBefore[#1]{#2}{#3}{#4}}% + \@call} +\def\@xs@IfSubStrBefore[#1,#2]#3#4#5{% + \@xs@StrPosition[#2]{#3}{#5}[\@xs@posMax@IfSubStrBefore]% on trouve les positions + \@xs@StrPosition[#1]{#3}{#4}[\@xs@posPattern@IfSubStrBefore]% + \ifnum\@xs@posMax@IfSubStrBefore<\@ne% + \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}% si pas trouvé #5 : 0 renvoie faux + \else + \ifnum\@xs@posPattern@IfSubStrBefore<\@ne% + \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}% si pas trouvé #4 : 0 renvoie faux + \else + \ifnum\@xs@posPattern@IfSubStrBefore<\@xs@posMax@IfSubStrBefore% + \def\@xs@result@IfSubStrBefore{\expandafter\@firstoftwo}% + \else + \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}% + \fi + \fi + \fi + \@xs@result@IfSubStrBefore} + +% Est ce que l'occurrence #1 de la sous chaine #4 se trouve avant +% l'occurrence n°#2 de la sous chaine #5 dans la chaine #3? +\newcommand\IfSubStrBehind{\@ifstar\@xs@IfSubStrBehind@@\@xs@IfSubStrBehind@} +\newcommand\@xs@IfSubStrBehind@@[4][1,1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \@xs@expand@and@detokenize{\xsarg@c}{#4}% + \edef\@call{\noexpand\@xs@IfSubStrBehind[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}% + \@call} +\newcommand\@xs@IfSubStrBehind@[4][1,1]{% + \@xs@def\@call{\@xs@expand\@xs@IfSubStrBehind[#1]{#2}{#3}{#4}}% + \@call} +\def\@xs@IfSubStrBehind[#1,#2]#3#4#5{% + \@xs@StrPosition[#2]{#3}{#5}[\@xs@posMax@IfSubStrBehind]% on trouve les positions + \@xs@StrPosition[#1]{#3}{#4}[\@xs@posPattern@IfSubStrBehind]% + \ifnum\@xs@posMax@IfSubStrBehind<1\relax% + \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}% si pas trouvé : 0 renvoie faux + \else + \ifnum\@xs@posPattern@IfSubStrBehind<1% + \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}% si pas trouvé : 0 renvoie faux + \else + \ifnum\@xs@posPattern@IfSubStrBehind>\@xs@posMax@IfSubStrBehind% + \def\@xs@result@IfSubStrBehind{\expandafter\@firstoftwo}% + \else + \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}% + \fi + \fi + \fi + \@xs@result@IfSubStrBehind} + +\newcount\Gobble@StrBetween +% Renvoie ce qui est strictement compris entre les occurrences n°#1 et n°#2 +% des chaines #4 et #5 dans la chaine #3 +\newcommand\StrBetween{\@ifstar\@xs@StrBetween@@\@xs@StrBetween@} +\newcommand\@xs@StrBetween@@[4][1,1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \@xs@expand@and@detokenize{\xsarg@c}{#4}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBetween[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}{}}% + \@call} +\newcommand\@xs@StrBetween@[4][1,1]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBetween[#1]{#2}{#3}{#4}}{}}% + \@call} +\def\@xs@StrBetween[#1,#2]#3#4#5[#6]{% + \ifx\@empty#5\@empty% + \edef\@xs@StringBefore@StrBetween{}% + \else + \@xs@StrBefore[#2]{#3}{#5}[\@xs@StringBefore@StrBetween]% on extrait ce qui est à gauche de l'occurrence de #5 + \@xs@StrPosition[#1]{#3}{#4}[\@xs@Pos@StrBetween]% position de l'occurrence de #3 + \Gobble@StrBetween\@xs@Pos@StrBetween% + \@xs@StrLen{#4}[\@xs@Pos@StrBetween]% + \advance\Gobble@StrBetween\@xs@Pos@StrBetween% on lui ajoute la longueur de #3 + \advance\Gobble@StrBetween\m@ne% et on enlève 1 + \expandafter\expandafter\expandafter\@xs@StrGobbleLeft% + \expandafter\expandafter\expandafter{\expandafter\@xs@StringBefore@StrBetween\expandafter}% + \expandafter{\Gobble@StrBetween}[\@xs@result@StrBetween]% pour manger ce nombre de caractère au début de \@xs@StringBefore@StrBetween + \fi + \@xs@ReturnResult{\@xs@result@StrBetween}{#6}} + +\newcount\Occurrence@StrSubstitute +% Remplace les #1 premières occurences de la chaine #3 +% par la chaine #4 dans la chaine #2 +\newcommand\StrSubstitute{\@ifstar\@xs@StrSubstitute@@\@xs@StrSubstitute@} +\newcommand\@xs@StrSubstitute@@[4][0]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \@xs@expand@and@detokenize{\xsarg@c}{#4}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrSubstitute[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}{}}% + \@call} +\newcommand\@xs@StrSubstitute@[4][0]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrSubstitute[#1]{#2}{#3}{#4}}{}}% + \@call} +\def\@xs@StrSubstitute[#1]#2#3#4[#5]{% + \Occurrence@StrSubstitute\z@ + \ifx\@empty#3\@empty% si #3 est vide, on ne fait rien + \@xs@ArgEmpty% warning + \edef\@result@StrSubstitute{#2}% + \else + \edef\@result@StrSubstitute{}% on initialise à vide, histoire d'enlever le statut undefined + \def\@xs@StrSubstitute@i##1#3##2\@nil{% + \edef\@result@StrSubstitute{\@result@StrSubstitute##1#4}% on recopie jusqu'à la 1ere occurrence, et on remplace #3 par #4 + \ifnum#1>\z@% si on compte les occurrences + \advance\Occurrence@StrSubstitute\@ne% on incrémente le compteur + \@xs@IfSubStr[1]{##2}{#3}% tant que la chaine restante contient #3 + {\ifnum\Occurrence@StrSubstitute<#1% tant qu'on n'a pas atteint la bonne occurrence, + \@xs@StrSubstitute@i##2\@nil% on recommence + \else + \edef\@result@StrSubstitute{\@result@StrSubstitute##2}% l'occurrence est atteinte : on rajoute ce qui reste + \fi}% + {\edef\@result@StrSubstitute{\@result@StrSubstitute##2}}% si on ne peut plus substituter, on rajoute ce qui reste + \else% ici , on ne compte pas les occurrences, donc... + \@xs@IfSubStr[1]{##2}{#3}% ...tant que la chaine restante contient #3 + {\@xs@StrSubstitute@i##2\@nil}% on recommence + {\edef\@result@StrSubstitute{\@result@StrSubstitute##2}}% si on ne peut plus substituter, on rajoute ce qui reste + \fi}% + \@xs@IfSubStr[1]{#2}{#3}% + {\@xs@StrSubstitute@i#2\@nil}% si #2 contient #3, on exécute la macro + {\edef\@result@StrSubstitute{#2}}% sinon, on renvoie la chaine #2 + \fi + \@xs@ReturnResult{\@result@StrSubstitute}{#5}} + +% Supprime les #1 premières occurrences de #3 dans #2 +\newcommand\StrDel{\@ifstar\@xs@StrDel@@\@xs@StrDel@} +\newcommand\@xs@StrDel@@[3][0]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrSubstitute[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{}}{}}% + \@call} +\newcommand\@xs@StrDel@[3][0]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrSubstitute[#1]{#2}{#3}{}}{}}% + \@call} + +\newcount\Length@StrLen +% Compte combien de caractères contient la chaine #1 ? +\newcommand\StrLen{\@ifstar\@xs@StrLen@@\@xs@StrLen@} +\newcommand\@xs@StrLen@@[1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrLen{\xsarg@a}}{}}% + \@call} +\newcommand\@xs@StrLen@[1]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrLen{#1}}{}}% + \@call} +% Macro qui compte le nombre de caractères de son argument #1 +\def\@xs@StrLen@i#1\@nil{% + \ifx\@empty#1\@empty% tant que #1 n'est pas vide ... + \else + \advance\Length@StrLen\@ne% ...on ajoute 1 à la longueur + \@xs@IfBeginWith{#1}{ }% et on recommence après avoir enlevé le 1er caractère (on distingue le cas où c'est un espace) + {\@xs@StrBehind[1]{#1}{ }[\@xs@result@StrLen]% + \expandafter\@xs@StrLen@i\@xs@result@StrLen\@nil}% + {\expandafter\@xs@StrLen@i\@xs@RemoveFirst@StrLen#1\@nil\@nil}% + \fi}% +% Macro qui enlève le 1er caractère de son argument +\def\@xs@RemoveFirst@StrLen#1#2\@nil{#2}% +% Macro principale +\def\@xs@StrLen#1[#2]{% +% Renvoie la longueur de la chaine #1 + \Length@StrLen\z@ + \@xs@StrLen@i#1\@nil% + \expandafter\@xs@ReturnResult\expandafter{\the\Length@StrLen}{#2}} + +\newcount\Position@StrMid +\newcommand\StrMid{\@ifstar\@xs@StrMid@@\@xs@StrMid@} +\newcommand\@xs@StrMid@@[3]{% + \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#3}% position début et fin + \ifnum#2>#3\@xs@BadNumArg\fi + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrMid{\xsarg@a}}{}}% + \@call} +\newcommand\@xs@StrMid@[3]{% + \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#3}% position début et fin + \ifnum#2>#3\@xs@BadNumArg\fi + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrMid{#1}}{}}% + \@call} +% cette sous macro supprime le nombre de caractère nécessaires +% à la gauche de son argument, et renvoie le résultat dans \@result@Mid@Gobble +\def\@xs@Mid@Gobble#1\@nil{% enlève #2 caractères à gauche de ##1 + \advance\Position@StrMid\@ne + \ifnum\Position@StrMid=\@StrMid@First% si on a atteint la position + \edef\@result@Mid@Gobble{#1}% on renvoie ce qui reste + \else% sinon + \@xs@RemoveFirst{#1}[\@Mid@remain]% on enlève un caractère + \expandafter\@xs@Mid@Gobble\@Mid@remain\@nil% et on recommence + \fi}% +% cette sous macro prend le nombre de caractères nécessaires +% à la gauche de son argument, et renvoie le résultat dans \@final@Result +\def\@xs@Mid@Concat#1\@nil{% + \@xs@ReturnFirst{#1}[\@Char@to@add]% prend le 1er caractère + \edef\@final@Result{\@final@Result\@Char@to@add}% l'ajoute au résultat final + \@xs@RemoveFirst{#1}[\@remain@StrMid]% puis l'enlève de la chaine + \ifnum\Position@StrMid<\@StrMid@Last% si pas encore fini + \advance\Position@StrMid\@ne% on incrémente et + \expandafter\@xs@Mid@Concat\@remain@StrMid\@nil% on recommence + \fi}% +\def\@xs@StrMid#1[#2]{% +% renvoie la sous chaine de #1 comprise entre les positions \@StrMid@First et \@StrMid@Last + \@xs@StrLen{#1}[\@len@StrMid]% + \ifnum\@StrMid@First>\@StrMid@Last% mauvais ordre ? + \@xs@ReturnResult{}{#2}% on renvoie rien + \else + \ifnum\@StrMid@Last<\@ne% si position fin trop petite + \@xs@ReturnResult{}{#2}% on renvoie rien + \else + \ifnum\@StrMid@First<\@ne% 1ère position trop petite ? + \edef\@StrMid@First{1}% on limite à 1 + \@xs@BadNumArg% avertissement + \fi + \ifnum\@StrMid@Last>\@len@StrMid% dernière position trop grande ? + \edef\@StrMid@Last{\@len@StrMid}% on limite à la longueur de la chaine + \@xs@BadNumArg% avertissement + \fi + \Position@StrMid\z@ + \@xs@Mid@Gobble#1\@nil% on supprime ce qu'il faut à gauche + \edef\@final@Result{}% rien dans le résultat final pour l'instant + \expandafter\@xs@Mid@Concat\@result@Mid@Gobble\@nil% on prend les caractères qu'il faut à gauche + \expandafter\@xs@ReturnResult\expandafter{\@final@Result}{#2}% + \fi + \fi} + +\newcommand\StrGobbleLeft{\@ifstar\@xs@StrGobbleLeft@@\@xs@StrGobbleLeft@} +% supprime #2 caractères à gauche dans la chaine #1 +\newcommand\@xs@StrGobbleLeft@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrGobbleLeft{\xsarg@a}{\@xs@expand#2}}{}}% + \@call} +\newcommand\@xs@StrGobbleLeft@[2]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrGobbleLeft{#1}{#2}}{}}% + \@call} +\def\@xs@StrGobbleLeft#1#2[#3]{% + \edef\@StrMid@First{\numexpr#2+1}% + \@xs@StrLen{#1}[\@StrMid@Last]% + \@xs@StrMid{#1}[\@xs@result@StrGobbleLeft]% + \@xs@ReturnResult{\@xs@result@StrGobbleLeft}{#3}} + +% extrait de #1 la chaine à gauche de longueur #2 +\newcommand\StrLeft{\@ifstar\@xs@StrLeft@@\@xs@StrLeft@} +\newcommand\@xs@StrLeft@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrLeft{\xsarg@a}{\@xs@expand#2}}{}}% + \@call} +\newcommand\@xs@StrLeft@[2]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrLeft{#1}{#2}}{}}% + \@call} +\def\@xs@StrLeft#1#2[#3]{% + \edef\@StrMid@First{1}% + \ifnum#2<0\@xs@BadNumArg\fi% avertissement + \edef\@StrMid@Last{#2}% + \@xs@StrMid{#1}[\@xs@result@@StrLeft]% + \@xs@ReturnResult{\@xs@result@@StrLeft}{#3}} + +% supprime #2 caractères à droite dans la chaine #1 +\newcommand\StrGobbleRight{\@ifstar\@xs@StrGobbleRight@@\@xs@StrGobbleRight@} +\newcommand\@xs@StrGobbleRight@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrGobbleRight{\xsarg@a}{\@xs@expand#2}}{}}% + \@call} +\newcommand\@xs@StrGobbleRight@[2]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrGobbleRight{#1}{#2}}{}}% + \@call} +\def\@xs@StrGobbleRight#1#2[#3]{% + \@xs@StrLen{#1}[\@Result@Length]% + \ifnum#2>\@Result@Length\@xs@BadNumArg\fi% avertissement + \edef\@StrMid@First{1}% + \edef\@StrMid@Last{\numexpr\@Result@Length-#2}% + \@xs@StrMid{#1}[\@xs@result@StrGobbleRight]% + \@xs@ReturnResult{\@xs@result@StrGobbleRight}{#3}} + +% renvoie #2 caractères à la droite de la chaine #1 +\newcommand\StrRight{\@ifstar\@xs@StrRight@@\@xs@StrRight@} +\newcommand\@xs@StrRight@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrRight{\xsarg@a}{#2}}{}}% + \@call} +\newcommand\@xs@StrRight@[2]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrRight{#1}{#2}}{}}% + \@call} +\def\@xs@StrRight#1#2[#3]{% + \ifnum#2<0\@xs@BadNumArg\fi% avertissement + \@xs@StrLen{#1}[\@StrMid@Last]% + \edef\@StrMid@First{\numexpr\@StrMid@Last+1-#2}% + \@xs@StrMid{#1}[\@xs@result@StrMid]% + \@xs@ReturnResult{\@xs@result@StrMid}{#3}} + +\newcommand\StrChar{\@ifstar\@xs@StrChar@@\@xs@StrChar@} +\newcommand\@xs@StrChar@@[2]{% + \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#2}% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrMid{\xsarg@a}}{}}% + \@call} +\newcommand\@xs@StrChar@[2]{% + \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#2}% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrMid{#1}}{}}% + \@call}% + +\newcount\Occurrence@StrCount +% Combien de fois compte t-on #2 dans #1 ? +\newcommand\StrCount{\@ifstar\@xs@StrCount@@\@xs@StrCount@} +\newcommand\@xs@StrCount@@[2]{% + \@xs@expand@and@detokenize{\xsarg@a}{#1}% + \@xs@expand@and@detokenize{\xsarg@b}{#2}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrCount{\xsarg@a}{\xsarg@b}}{}}% + \@call} +\newcommand\@xs@StrCount@[2]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrCount{#1}{#2}}{}}% + \@call} +\def\@xs@StrCount#1#2[#3]{% + \Occurrence@StrCount\z@% on initialise à 0 + \def\@xs@StrCount@i{% + \advance\Occurrence@StrCount\@ne% occurrence := occurrence +1 + \@xs@StrPosition[\the\Occurrence@StrCount]{#1}{#2}[\@xs@Pos@StrCount]% position de l'occurrence ? + \ifnum\@xs@Pos@StrCount=\z@% si l'occurrence n'est pas trouvée + \advance\Occurrence@StrCount\m@ne% on enlève 1 au compteur + \@xs@ReturnResult{\the\Occurrence@StrCount}{#3}% et on renvoie ce résultat + \else + \@xs@StrCount@i% si l'occurrence est trouvée, on recommence + \fi}% + \@xs@StrCount@i} + +\newcount\Position@StrPosition +% renvoie la position de l'occurrence #1 de la sous chaine #3 dans la chaine #2 +\newcommand\StrPosition{\@ifstar\@xs@StrPosition@@\@xs@StrPosition@} +\newcommand\@xs@StrPosition@@[3][1]{% + \@xs@expand@and@detokenize{\xsarg@a}{#2}% + \@xs@expand@and@detokenize{\xsarg@b}{#3}% + \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrPosition[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}% + \@call} +\newcommand\@xs@StrPosition@[3][1]{% + \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrPosition[#1]{#2}{#3}}{}}% + \@call} +\def\@xs@StrPosition[#1]#2#3[#4]{% + \Position@StrPosition\z@ + \ifx\@empty#3\@empty% + \else + \@xs@StrBefore[#1]{#2}{#3}[\@xs@result@StrPosition]% on extrait la chaine avant l'occurrence de #3 + \expandafter\@xs@StrLen\expandafter{\@xs@result@StrPosition}[\@xs@len@StrPosition]% + \Position@StrPosition\@xs@len@StrPosition% + \advance\Position@StrPosition\@ne% \@xs@len@StrPosition+1 est le résultat à priori... + \ifnum\@xs@len@StrPosition=\z@% ... mais si la position vaut 0, soit #3 est au début de #2, soit #3 n'est pas trouvé. On lève le doute ci dessous + \Position@StrPosition\z@% on considère que l'occurrence de #3 n'est pas trouvée, mais... + \ifnum#1=\@ne% si on teste la 1ère occurrence + \@xs@IfBeginWith{#2}{#3}% et que le #2 commence par #3, + {\Position@StrPosition\@ne}% c'est le seul cas où on renvoie 1 + {}% + \fi + \fi + \fi + \expandafter\@xs@ReturnResult\expandafter{\the\Position@StrPosition}{#4}} + +% Définit le délimiteur verb et le développement des arguments par défaut +\AtBeginDocument{\setverbdelim{|}\fullexpandarg} +\endinput +% +% Historique : +% +% 5/7/2008 : premiere sortie de ce package
\ No newline at end of file |