%  __________________________________________________
% |                                                  |
% |                                                  |
% |                   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