Bei tiefen parse_filters with_objects mit prefix bauen.
[kivitendo-erp.git] / templates / f-tex / xstring.sty
1 %  __________________________________________________
2 % |                                                  |
3 % |                                                  |
4 % |                   xstring v1.0                   |
5 % |                                                  |
6 % |                  July, 4th 2008                  |
7 % |                                                  |
8 % |__________________________________________________|
9 %
10 % This is xtring.sty
11 %
12 % Christian Tellechea 2008
13 % email : unbonpetit@gmail.com
14 % -------------------------------------------------------------------
15 % This work may be distributed and/or modified under the
16 % conditions of the LaTeX Project Public License, either version 1.3
17 % of this license or (at your option) any later version.
18 % The latest version of this license is in
19 %
20 %     http://www.latex-project.org/lppl.txt
21 %
22 % and version 1.3 or later is part of all distributions of LaTeX
23 % version 2005/12/01 or later.
24 % -------------------------------------------------------------------
25 % This work has the LPPL maintenance status `maintained'.
26 %
27 % The Current Maintainer of this work is Christian Tellechea
28 %
29 % This work consists of the files
30 %   xstring.sty (this file)
31 %   readme.txt
32 %   xstring_doc_fr.tex, xstring_doc_fr.pdf (manual in french)
33 %   xstring_doc_en.tex, xstring_doc_en.pdf (manual in english)
34 %   xstring_test.tex, xstring_test.pdf (test file)
35 %
36 \NeedsTeXFormat{LaTeX2e}
37 \ProvidesPackage{xstring}[2008/07/04 v1.0 Extended macros for strings. C Tellechea]
38
39 % Messages pour situations particulières
40 \newcommand\@xs@BadNumArg   {\PackageInfo{xstring}{numerical argument out of range}}
41 \newcommand\@xs@ArgNotFound {\PackageInfo{xstring}{substring not found in string}}
42 \newcommand\@xs@ArgEmpty    {\PackageInfo{xstring}{empty argument}}
43
44 % Ouvre un groupe où les catcodes sont à 12 et à 10 pour les espaces
45 % ensuite, appelle \@xs@ReadVerb qui lit un argument entre délimiteurs verb
46 \newcommand\@xs@MakeVerb{% lit 1 argument et le transforme en verb
47        \begingroup% groupe où les catcodes sont à 12 pour la lecture suivante
48                \let\do\@makeother
49                \dospecials% on entre dans le mode verb
50                \obeyspaces% et on tient compte des espaces
51                \@xs@ReadVerb}% et on va lire l'argument
52
53 % Définit \@xs@ReadVerb qui lit un argument entre délimiteurs verb
54 \newcommand\setverbdelim[1]{% définit quel est le délimiteur de verb
55        \@xs@StrLen{#1}[\@len@verbdelim]%
56        \ifnum\@len@verbdelim=\@ne
57        \else
58                \PackageWarning{xstring}{verb delimiter is not a single char}%
59        \fi
60        \def\@xs@ReadVerb##1#1##2#1{% lit ##2 qui est entre les délimiteurs de verb
61                \endgroup% on ferme le groupe
62                \@xs@afterreadverb{##2}}}% on appelle l'exécution de fin
63
64 % Assigne l'argument entre délimiteur verb dans la sc #1
65 \newcommand\verbtocs[1]{%
66        \def\@xs@afterreadverb##1{\edef#1{##1}}%
67        \@xs@MakeVerb}
68
69 % A l'aide d'un pont d'\expandafter, développe d'un niveau toutes
70 % les sc de #1 puis et assigne le résultat à \result
71 \def\@xs@expandallonelevel#1#2\@nil{%
72        \expandafter\expandafter\expandafter\def% plein d'expandafter
73                \expandafter\expandafter\expandafter\result% pour développer #1 d'un niveau
74                        \expandafter\expandafter\expandafter{\expandafter\result#1}% et l'ajouter à \result
75        \ifx\@empty#2\@empty\else\@xs@expandallonelevel#2\@nil\fi}% s'il reste des sc, on boucle
76
77 % \scancs développe #3 autant de fois que spécifié en #1
78 % puis le développement obtenu est verbatimisé.
79 % On utilise \@xs@expandalltodepth autant de fois que #1 le spécifie
80 % puis on invoque \detokenize à la fin
81 \newcount\depth@scancs
82 \newcommand\scancs{%
83        \@ifstar%
84                {\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}%
85                 \def\@testornot{\@notestcs}%
86                 \@xs@scancs}%
87                {\def\@testornot{\@testcs}%
88                 \@xs@scancs}}
89 \newcommand\@xs@scancs[3][1]{%
90 % #1 : profondeur à développer avant de verbatimiser
91 % #2 : nom de la sc recevant la chaine verb
92 % #3 : nom de la sc à développer et à transformer en verb
93        \depth@scancs\z@
94        \def\@notestcs##1\@nil{\relax}%
95        \def\@testcs##1##2\@nil{%
96                \ifx\@empty##2\@empty
97                \else% si la séquence de contrôle n'est pas seule, on gueule
98                        \PackageError{xstring}{third argument or its expansion is not a single control sequence! Use \string\scancs* instead of \string\scancs (see manual).}%
99                \fi}%
100        \def\@xs@expandalltodepth##1\@nil{% développe de #1 niveaux toutes les sc de #3 et l'assigne à \the@result
101                \def\result{}%
102                \ifnum\depth@scancs<#1% tant que la profondeur de développement n'est pas atteinte
103                        \@testornot##1\@nil% on teste éventuellement si le développement comporte 1 seule sc
104                        \expandafter\@xs@expandallonelevel\the@result\@nil% on développe tout d'un niveau
105                        \expandafter\def\expandafter\the@result\expandafter{\result}%
106                        \advance\depth@scancs\@ne
107                        \expandafter\@xs@expandalltodepth\the@result\@nil% et on recommence
108                \fi}%
109        \def\the@result{#3}%
110        \@xs@expandalltodepth#3\@nil
111        \expandafter\def\expandafter#2\expandafter{\detokenize\expandafter{\the@result}}}
112
113 % Cette macro développe #2 au maximum (ce qui doit donner du texte),
114 % puis le transforme en token et l'assigne à #1
115 % On ouvre un groupe dans lequel un \@nil est inséré à la fin du fichier virtuel
116 % lu avec \scantokens. Ce \@nil sert à l'argument délimité de \@AssignResult
117 \newcommand\tokenize[2]{%
118        \begingroup
119                \expandafter\@xs@def\expandafter\the@text\expandafter{#2}% on développe en accord avec \fullexpandarg ou \noexpandarg
120                \def\@AssignResult##1\@nil{\def#1{##1}}% on assigne en tenant compte du \@nil qui vient de la fin du fichier virtuel
121                \everyeof{\@nil}% met un \@nil à la fin du fichier virtuel
122                \endlinechar\m@ne
123        \expandafter\expandafter\expandafter\endgroup% on retarde la fermeture du groupe
124        \expandafter\@AssignResult\scantokens\expandafter{\the@text}}% pout faire l'assignation
125
126 % Macro très simple qui assigne ou affiche le résultat, selon la présence
127 % ou non de #2 qui est l'argument optionnel venant en dernière position des macros
128 \newcommand\@xs@ReturnResult[2]{%
129        \expandafter\ifx\expandafter\@empty\detokenize{#2}\@empty% pas de nom ?
130                #1% on affiche #1
131        \else% il y a un nom donc
132                \edef#2{#1}% on met #1 dans #3 avec edef
133        \fi}
134
135 % Pas d'expansion des arguments
136 \newcommand\normalexpandarg{%
137        \let\@xs@def\def% on définit\@call avec \def
138        \let\@xs@expand\@empty}% et on neutralise \@xs@expand
139
140 % Expansion des arguments
141 \newcommand\fullexpandarg{%
142        \let\@xs@def\edef% on définit\@call avec \edef
143        \let\@xs@expand\noexpand}% et on utilise \noexpand
144
145 % Cette macro interne enlève le 1er caractère de la chaine passée en argument
146 \def\@xs@RemoveFirst#1[#2]{%
147        \ifx\@empty#1\@empty
148                \edef#2{}%
149        \else
150                \@xs@IfBeginWith{#1}{ }%
151                        {\@xs@StrBehind[1]{#1}{ }[#2]}%
152                        {\def\@xs@RemoveFirst@ii##1##2\@nil{\edef#2{##2}}%
153                         \@xs@RemoveFirst@ii#1\@nil}%
154        \fi}
155
156 % Cette macro interne renvoie le 1er caractère de la chaine passée en argument
157 \def\@xs@ReturnFirst#1[#2]{%
158        \ifx\@empty#1\@empty%
159                \edef#2{}%
160        \else
161                \@xs@IfBeginWith{#1}{ }%
162                        {\edef#2{ }}%
163                        {\def\@xs@ReturnFirst@ii##1##2\@nil{\edef#2{##1}}%
164                         \@xs@ReturnFirst@ii#1\@nil}%
165        \fi}
166
167 % Cette macro interne est utilisée dans les macros étoilées pour :
168 % 1) développer l'argument selon qu'on a choisit \fullexpandarg
169 %    ou \normalexpandarg, et ceci à l'aide de la macro \@xs@def
170 % 2) Ensuite, on détokenize ce développement de façon n'avoir plus que
171 %    des catcodes de 10 pour les espaces et 12 pour le reste.
172 \newcommand\@xs@expand@and@detokenize[2]{%
173        \@xs@def#1{#2}% on développe #2 selon \fullexpandarg ou \normalexpandarg
174        \edef#1{\detokenize\expandafter{#1}}}% puis on détokenize et on assigne à #1
175
176 % Cette macro teste si une chaine (#2) en contient au moins n(#1) fois une autre (#3)
177 % Pour cela, on teste "ce qu'il reste" après #2. On construit une récursivité sur le
178 % compteur d'occurences \Occurrence@IfSubStr. Si "ce qu'il reste" est vide avant d'atteindre
179 % l'occurence voulue, le test est négatif, sinon on poursuit par récursivité sur "ce qu'il reste"
180 % jusqu'à l'occurrence voulue. Si l'on atteint l'occurrence voulue, le test est positif.
181 % On utilise la séquence de contrôle non définie \@delimit pour éviter un effet
182 % indésirable qui ferait que \IfSubStr{a}{aa}{V}{F} renverrait V
183 \newcommand\IfSubStr{\@ifstar\@xs@IfSubStr@@\@xs@IfSubStr@}%
184 \newcommand\@xs@IfSubStr@@[3][1]{%
185        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
186        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
187        \edef\@call{\noexpand\@xs@IfSubStr[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}%
188        \@call}
189 \newcommand\@xs@IfSubStr@[3][1]{%
190        \@xs@def\@call{\@xs@expand\@xs@IfSubStr[#1]{#2}{#3}}%
191        \@call}
192 \newcount\Occurrence@IfSubStr
193 \def\@xs@IfSubStr[#1]#2#3{%
194 % est ce que la chaine #2 contient au moins #1 fois la sous chaine #3 ?
195        \ifnum#1<\@ne% on gère les cas spéciaux
196                \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% #1<1 -> on exécute #5
197        \else
198                \ifx\@empty#3\@empty%
199                        \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% si #3 est vide -> on écécute #5
200                \else
201                        \Occurrence@IfSubStr\z@
202                        \def\@xs@IfSubStr@i##1#3##2\@nil{%
203                                \advance\Occurrence@IfSubStr\@ne
204                                \ifx\@empty##2\@empty%% qu'y a t-il apès la sous chaine ?
205                                        \def\@xsresult@IfSubStr{\expandafter\@secondoftwo}% rien -> on exécute #5
206                                \else% si ce qu'il est reste n'est pas vide
207                                        \ifnum\Occurrence@IfSubStr<#1% tant que le nombre d'occurrence n'est pas atteint
208                                                \@xs@IfSubStr@i##2\@nil% on recommence
209                                        \else
210                                                \def\@xsresult@IfSubStr{\expandafter\@firstoftwo}% nombre d'occurrences atteint : on renvoie #4
211                                        \fi
212                                \fi}%
213                        \@xs@IfSubStr@i#2\@delimit#3\@nil% \@delimit pour éviter le cas de \IfSubStr{a}{aa}{V}{F} qui renverrait V
214                \fi
215        \fi
216        \@xsresult@IfSubStr}
217
218 \newcommand\IfBeginWith{\@ifstar\@xs@IfBeginWith@@\@xs@IfBeginWith@}%
219 \newcommand\@xs@IfBeginWith@@[2]{%
220        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
221        \@xs@expand@and@detokenize{\xsarg@b}{#2}%
222        \edef\@call{\noexpand\@xs@IfBeginWith{\xsarg@a}{\xsarg@b}}
223        \@call}
224 \newcommand\@xs@IfBeginWith@[2]{%
225        \@xs@def\@call{\@xs@expand\@xs@IfBeginWith{#1}{#2}}%
226        \@call}
227 \newcommand\@xs@IfBeginWith[2]{%
228        \@xs@IfSubStr[1]{#1}{#2}%
229                {\ifx\@empty#2\@empty%
230                        \def\@xs@IfBeginWith@i##1\@nil{\expandafter\@firstoftwo}% si #2 est vide, on renvoie vrai
231                 \else
232                        \def\@xs@IfBeginWith@i##1#2##2\@nil{% qu'y a t-il avant #2 ?
233                                \ifx\@empty##1\@empty
234                                        \expandafter\@firstoftwo% rien, donc #2 est au début, on renvoie vrai
235                                \else
236                                        \expandafter\@secondoftwo
237                                \fi}%
238                \fi}%
239                {\def\@xs@IfBeginWith@i##1\@nil{\expandafter\@secondoftwo}}% si #2 n'est pas dans #1, on renvoie faux
240        \@xs@IfBeginWith@i#1\@delimit#2\@nil}
241
242 \newcommand\IfEndWith{\@ifstar\@xs@IfEndWith@@\@xs@IfEndWith@}%
243 \newcommand\@xs@IfEndWith@@[2]{%
244        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
245        \@xs@expand@and@detokenize{\xsarg@b}{#2}%
246        \edef\@call{\noexpand\@xs@IfEndWith{\xsarg@a}{\xsarg@b}}
247        \@call}
248 \newcommand\@xs@IfEndWith@[2]{%
249        \@xs@def\@call{\@xs@expand\@xs@IfEndWith{#1}{#2}}%
250        \@call}
251 \newcommand*\@xs@IfEndWith[2]{%
252 % Est ce que la chaine #1 finit par la chaine #2 ?
253        \@xs@IfSubStr[1]{#1}{#2}%
254                {\ifx\@empty#2\@empty% si #2 est vide -> test vrai
255                        \def\@xs@remove@begining##1\@nil{\expandafter\@firstoftwo}%
256                 \else
257                        \def\@xs@remove@begining##1#2##2\@nil{% on isole ce qui est après #2, la chaine restante ##2
258                                \@xs@IfSubStr[1]{##2}{#2}% tant que #2 est dans la chaine restante..
259                                        {\@xs@remove@begining##2\@nil}% ...on recommence
260                                        {\ifx\@empty##2\@empty% sinon, on regarde ce qui reste
261                                                \expandafter\@firstoftwo% rien -> test vrai
262                                         \else
263                                                \expandafter\@secondoftwo% quelque chose -> test faux
264                                         \fi
265                                        }%
266                                }%
267                 \fi}%
268                {\def\@xs@remove@begining##1\@nil{\expandafter\@secondoftwo}}% si #2 n'est pas dans #1 -> test faux
269        \@xs@remove@begining#2#1\@nil}
270
271 \newcount\Occurrence@StrBefore
272 % Renvoie ce qui est à gauche de l'occurence n°#1 de la sous chaine #3 dans la chaine #2
273 \newcommand\StrBefore{\@ifstar\@xs@StrBefore@@\@xs@StrBefore@}
274 \newcommand\@xs@StrBefore@@[3][1]{%
275        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
276        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
277        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBefore[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}%
278        \@call}
279 \newcommand\@xs@StrBefore@[3][1]{%
280        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBefore[#1]{#2}{#3}}{}}%
281        \@call}
282 \def\@xs@StrBefore[#1]#2#3[#4]{%
283        \ifx\@empty#3\@empty%
284                \@xs@ArgNotFound% warning
285                \edef\@xs@result@StrBefore{}%
286        \else
287                \edef\@xs@result@StrBefore{}% resultat nul pour l'instant
288                \Occurrence@StrBefore\z@
289                \def\@xs@StrBefore@i##1#3##2\@nil{%
290                        \advance\Occurrence@StrBefore\@ne% on incrémente
291                        \ifx\@empty##2\@empty% pas trouvé ?
292                                \@xs@ArgNotFound% warning
293                                \edef\@xs@result@StrBefore{}% renvoie vide
294                        \else
295                                \ifnum\Occurrence@StrBefore<#1% tant que c'est pas la bonne occurrence
296                                        \edef\@xs@result@StrBefore{\@xs@result@StrBefore##1#3}% on ajoute le début au résultat final
297                                        \@xs@StrBefore@i##2\@nil% on recommence avec la chaine restante
298                                \else
299                                        \edef\@xs@result@StrBefore{\@xs@result@StrBefore##1}% on n'ajoute pas la chaine frontière #3 la dernière fois !
300                                \fi
301                        \fi}%
302                \@xs@StrBefore@i#2\@delimit#3\@nil%
303        \fi
304        \@xs@ReturnResult{\@xs@result@StrBefore}{#4}}
305
306 \newcount\Occurrence@StrBehind
307 % Renvoie ce qui est à droite de l'occurence n°#1 de la sous chaine #3 dans la chaine #2
308 \newcommand\StrBehind{\@ifstar\@xs@StrBehind@@\@xs@StrBehind@}
309 \newcommand\@xs@StrBehind@@[3][1]{%
310        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
311        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
312        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBehind[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}%
313        \@call}
314 \newcommand\@xs@StrBehind@[3][1]{%
315        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBehind[#1]{#2}{#3}}{}}%
316        \@call}
317 \def\@xs@StrBehind[#1]#2#3[#4]{%
318        \ifx\@empty#3\@empty%
319                \@xs@ArgNotFound% warning
320                \edef\@xs@result@StrBehind{}%
321        \else
322                \Occurrence@StrBehind\z@
323                \def\@xs@StrBehind@i##1#3##2\@nil{%
324                        \advance\Occurrence@StrBehind\@ne% on incrémente
325                        \ifnum\Occurrence@StrBehind<#1% tant que c'est pas la bonne occurrence
326                                \@xs@IfSubStr[1]{##2}{#3}% tant que la chaine restante contient #3
327                                        {\@xs@StrBehind@i##2\@nil}% on recommence avec la chaine restante
328                                        {\@xs@ArgNotFound% sinon, info
329                                         \edef\@xs@result@StrBehind{}}% et on renvoie vide
330                        \else
331                                \edef\@xs@result@StrBehind{##2}% on renvoie la chaine restante
332                        \fi}%
333                \@xs@IfSubStr[1]{#2}{#3}%
334                        {\@xs@StrBehind@i#2\@nil}%
335                        {\@xs@ArgNotFound% warning
336                         \edef\@xs@result@StrBehind{}}%
337        \fi
338        \@xs@ReturnResult{\@xs@result@StrBehind}{#4}}
339
340 \newcommand\IfSubStrBefore{\@ifstar\@xs@IfSubStrBefore@@\@xs@IfSubStrBefore@}
341 % est ce que l'occurrence #1 de la sous chaine #4 se trouve avant
342 % l'occurrence n°#2 de la sous chaine #5 dans la chaine #3?
343 \newcommand\@xs@IfSubStrBefore@@[4][1,1]{%
344        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
345        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
346        \@xs@expand@and@detokenize{\xsarg@c}{#4}%
347        \edef\@call{\noexpand\@xs@IfSubStrBefore[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}%
348        \@call}
349 \newcommand\@xs@IfSubStrBefore@[4][1,1]{%
350        \@xs@def\@call{\@xs@expand\@xs@IfSubStrBefore[#1]{#2}{#3}{#4}}%
351        \@call}
352 \def\@xs@IfSubStrBefore[#1,#2]#3#4#5{%
353        \@xs@StrPosition[#2]{#3}{#5}[\@xs@posMax@IfSubStrBefore]% on trouve les positions
354        \@xs@StrPosition[#1]{#3}{#4}[\@xs@posPattern@IfSubStrBefore]%
355        \ifnum\@xs@posMax@IfSubStrBefore<\@ne%
356                \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}% si pas trouvé #5 : 0 renvoie faux
357        \else
358                \ifnum\@xs@posPattern@IfSubStrBefore<\@ne%
359                        \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}% si pas trouvé #4 : 0 renvoie faux
360                \else
361                        \ifnum\@xs@posPattern@IfSubStrBefore<\@xs@posMax@IfSubStrBefore%
362                                \def\@xs@result@IfSubStrBefore{\expandafter\@firstoftwo}%
363                        \else
364                                \def\@xs@result@IfSubStrBefore{\expandafter\@secondoftwo}%
365                        \fi
366                \fi
367        \fi
368        \@xs@result@IfSubStrBefore}
369
370 % Est ce que l'occurrence #1 de la sous chaine #4 se trouve avant
371 % l'occurrence n°#2 de la sous chaine #5 dans la chaine #3?
372 \newcommand\IfSubStrBehind{\@ifstar\@xs@IfSubStrBehind@@\@xs@IfSubStrBehind@}
373 \newcommand\@xs@IfSubStrBehind@@[4][1,1]{%
374        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
375        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
376        \@xs@expand@and@detokenize{\xsarg@c}{#4}%
377        \edef\@call{\noexpand\@xs@IfSubStrBehind[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}%
378        \@call}
379 \newcommand\@xs@IfSubStrBehind@[4][1,1]{%
380        \@xs@def\@call{\@xs@expand\@xs@IfSubStrBehind[#1]{#2}{#3}{#4}}%
381        \@call}
382 \def\@xs@IfSubStrBehind[#1,#2]#3#4#5{%
383        \@xs@StrPosition[#2]{#3}{#5}[\@xs@posMax@IfSubStrBehind]% on trouve les positions
384        \@xs@StrPosition[#1]{#3}{#4}[\@xs@posPattern@IfSubStrBehind]%
385        \ifnum\@xs@posMax@IfSubStrBehind<1\relax%
386                \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}% si pas trouvé : 0 renvoie faux
387        \else
388                \ifnum\@xs@posPattern@IfSubStrBehind<1%
389                        \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}% si pas trouvé : 0 renvoie faux
390                \else
391                        \ifnum\@xs@posPattern@IfSubStrBehind>\@xs@posMax@IfSubStrBehind%
392                                \def\@xs@result@IfSubStrBehind{\expandafter\@firstoftwo}%
393                        \else
394                                \def\@xs@result@IfSubStrBehind{\expandafter\@secondoftwo}%
395                        \fi
396                \fi
397        \fi
398        \@xs@result@IfSubStrBehind}
399
400 \newcount\Gobble@StrBetween
401 % Renvoie ce qui est strictement compris entre les occurrences n°#1 et n°#2
402 % des chaines #4 et #5 dans la chaine #3
403 \newcommand\StrBetween{\@ifstar\@xs@StrBetween@@\@xs@StrBetween@}
404 \newcommand\@xs@StrBetween@@[4][1,1]{%
405        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
406        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
407        \@xs@expand@and@detokenize{\xsarg@c}{#4}%
408        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrBetween[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}{}}%
409        \@call}
410 \newcommand\@xs@StrBetween@[4][1,1]{%
411        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrBetween[#1]{#2}{#3}{#4}}{}}%
412        \@call}
413 \def\@xs@StrBetween[#1,#2]#3#4#5[#6]{%
414        \ifx\@empty#5\@empty%
415                \edef\@xs@StringBefore@StrBetween{}%
416        \else
417                \@xs@StrBefore[#2]{#3}{#5}[\@xs@StringBefore@StrBetween]% on extrait ce qui est à gauche de l'occurrence de #5
418                \@xs@StrPosition[#1]{#3}{#4}[\@xs@Pos@StrBetween]% position de l'occurrence de #3
419                \Gobble@StrBetween\@xs@Pos@StrBetween%
420                \@xs@StrLen{#4}[\@xs@Pos@StrBetween]%
421                \advance\Gobble@StrBetween\@xs@Pos@StrBetween% on lui ajoute la longueur de #3
422                \advance\Gobble@StrBetween\m@ne% et on enlève 1
423                \expandafter\expandafter\expandafter\@xs@StrGobbleLeft%
424                        \expandafter\expandafter\expandafter{\expandafter\@xs@StringBefore@StrBetween\expandafter}%
425                                        \expandafter{\Gobble@StrBetween}[\@xs@result@StrBetween]% pour manger ce nombre de caractère au début de \@xs@StringBefore@StrBetween
426        \fi
427        \@xs@ReturnResult{\@xs@result@StrBetween}{#6}}
428
429 \newcount\Occurrence@StrSubstitute
430 % Remplace les #1 premières occurences de la chaine #3
431 % par la chaine #4 dans la chaine #2
432 \newcommand\StrSubstitute{\@ifstar\@xs@StrSubstitute@@\@xs@StrSubstitute@}
433 \newcommand\@xs@StrSubstitute@@[4][0]{%
434        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
435        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
436        \@xs@expand@and@detokenize{\xsarg@c}{#4}%
437        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrSubstitute[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{\xsarg@c}}{}}%
438        \@call}
439 \newcommand\@xs@StrSubstitute@[4][0]{%
440        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrSubstitute[#1]{#2}{#3}{#4}}{}}%
441        \@call}
442 \def\@xs@StrSubstitute[#1]#2#3#4[#5]{%
443                \Occurrence@StrSubstitute\z@
444                \ifx\@empty#3\@empty% si #3 est vide, on ne fait rien
445                        \@xs@ArgEmpty% warning
446                        \edef\@result@StrSubstitute{#2}%
447                \else
448                        \edef\@result@StrSubstitute{}% on initialise à vide, histoire d'enlever le statut undefined
449                        \def\@xs@StrSubstitute@i##1#3##2\@nil{%
450                                \edef\@result@StrSubstitute{\@result@StrSubstitute##1#4}% on recopie jusqu'à la 1ere occurrence, et on remplace #3 par #4
451                                \ifnum#1>\z@% si on compte les occurrences
452                                        \advance\Occurrence@StrSubstitute\@ne% on incrémente le compteur
453                                        \@xs@IfSubStr[1]{##2}{#3}% tant que la chaine restante contient #3
454                                                {\ifnum\Occurrence@StrSubstitute<#1% tant qu'on n'a pas atteint la bonne occurrence,
455                                                        \@xs@StrSubstitute@i##2\@nil% on recommence
456                                                \else
457                                                        \edef\@result@StrSubstitute{\@result@StrSubstitute##2}% l'occurrence est atteinte : on rajoute ce qui reste
458                                                \fi}%
459                                                {\edef\@result@StrSubstitute{\@result@StrSubstitute##2}}% si on ne peut plus substituter, on rajoute ce qui reste
460                                \else% ici , on ne compte pas les occurrences, donc...
461                                        \@xs@IfSubStr[1]{##2}{#3}% ...tant que la chaine restante contient #3
462                                                {\@xs@StrSubstitute@i##2\@nil}% on recommence
463                                                {\edef\@result@StrSubstitute{\@result@StrSubstitute##2}}% si on ne peut plus substituter, on rajoute ce qui reste
464                                \fi}%
465                        \@xs@IfSubStr[1]{#2}{#3}%
466                                {\@xs@StrSubstitute@i#2\@nil}% si #2 contient #3, on exécute la macro
467                                {\edef\@result@StrSubstitute{#2}}% sinon, on renvoie la chaine #2
468                \fi
469        \@xs@ReturnResult{\@result@StrSubstitute}{#5}}
470
471 % Supprime les #1 premières occurrences de #3 dans #2
472 \newcommand\StrDel{\@ifstar\@xs@StrDel@@\@xs@StrDel@}
473 \newcommand\@xs@StrDel@@[3][0]{%
474        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
475        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
476        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrSubstitute[\@xs@expand#1]{\xsarg@a}{\xsarg@b}{}}{}}%
477        \@call}
478 \newcommand\@xs@StrDel@[3][0]{%
479        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrSubstitute[#1]{#2}{#3}{}}{}}%
480        \@call}
481
482 \newcount\Length@StrLen
483 % Compte combien de caractères contient la chaine #1 ?
484 \newcommand\StrLen{\@ifstar\@xs@StrLen@@\@xs@StrLen@}
485 \newcommand\@xs@StrLen@@[1]{%
486        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
487        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrLen{\xsarg@a}}{}}%
488        \@call}
489 \newcommand\@xs@StrLen@[1]{%
490        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrLen{#1}}{}}%
491        \@call}
492 % Macro qui compte le nombre de caractères de son argument #1
493 \def\@xs@StrLen@i#1\@nil{%
494        \ifx\@empty#1\@empty% tant que #1 n'est pas vide ...
495        \else
496                \advance\Length@StrLen\@ne% ...on ajoute 1 à la longueur
497                \@xs@IfBeginWith{#1}{ }% et on recommence après avoir enlevé le 1er caractère (on distingue le cas où c'est un espace)
498                        {\@xs@StrBehind[1]{#1}{ }[\@xs@result@StrLen]%
499                         \expandafter\@xs@StrLen@i\@xs@result@StrLen\@nil}%
500                        {\expandafter\@xs@StrLen@i\@xs@RemoveFirst@StrLen#1\@nil\@nil}%
501        \fi}%
502 % Macro qui enlève le 1er caractère de son argument
503 \def\@xs@RemoveFirst@StrLen#1#2\@nil{#2}%
504 % Macro principale
505 \def\@xs@StrLen#1[#2]{%
506 % Renvoie la longueur de la chaine #1
507        \Length@StrLen\z@
508        \@xs@StrLen@i#1\@nil%
509        \expandafter\@xs@ReturnResult\expandafter{\the\Length@StrLen}{#2}}
510
511 \newcount\Position@StrMid
512 \newcommand\StrMid{\@ifstar\@xs@StrMid@@\@xs@StrMid@}
513 \newcommand\@xs@StrMid@@[3]{%
514        \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#3}% position début et fin
515        \ifnum#2>#3\@xs@BadNumArg\fi
516        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
517        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrMid{\xsarg@a}}{}}%
518        \@call}
519 \newcommand\@xs@StrMid@[3]{%
520        \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#3}% position début et fin
521        \ifnum#2>#3\@xs@BadNumArg\fi
522        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrMid{#1}}{}}%
523        \@call}
524 % cette sous macro supprime le nombre de caractère nécessaires
525 % à la gauche de son argument, et renvoie le résultat dans \@result@Mid@Gobble
526 \def\@xs@Mid@Gobble#1\@nil{% enlève #2 caractères à gauche de ##1
527        \advance\Position@StrMid\@ne
528        \ifnum\Position@StrMid=\@StrMid@First% si on a atteint la position
529                \edef\@result@Mid@Gobble{#1}% on renvoie ce qui reste
530        \else% sinon
531                \@xs@RemoveFirst{#1}[\@Mid@remain]% on enlève un caractère
532                \expandafter\@xs@Mid@Gobble\@Mid@remain\@nil% et on recommence
533        \fi}%
534 % cette sous macro prend le nombre de caractères nécessaires
535 % à la gauche de son argument, et renvoie le résultat dans \@final@Result
536 \def\@xs@Mid@Concat#1\@nil{%
537        \@xs@ReturnFirst{#1}[\@Char@to@add]% prend le 1er caractère
538        \edef\@final@Result{\@final@Result\@Char@to@add}% l'ajoute au résultat final
539        \@xs@RemoveFirst{#1}[\@remain@StrMid]% puis l'enlève de la chaine
540        \ifnum\Position@StrMid<\@StrMid@Last% si pas encore fini
541                \advance\Position@StrMid\@ne% on incrémente et
542                \expandafter\@xs@Mid@Concat\@remain@StrMid\@nil% on recommence
543        \fi}%
544 \def\@xs@StrMid#1[#2]{%
545 % renvoie la sous chaine de #1 comprise entre les positions \@StrMid@First et \@StrMid@Last
546        \@xs@StrLen{#1}[\@len@StrMid]%
547        \ifnum\@StrMid@First>\@StrMid@Last% mauvais ordre ?
548                \@xs@ReturnResult{}{#2}% on renvoie rien
549        \else
550                \ifnum\@StrMid@Last<\@ne% si position fin trop petite
551                        \@xs@ReturnResult{}{#2}% on renvoie rien
552                \else
553                        \ifnum\@StrMid@First<\@ne% 1ère position trop petite ?
554                                \edef\@StrMid@First{1}% on limite à 1
555                                \@xs@BadNumArg% avertissement
556                        \fi
557                        \ifnum\@StrMid@Last>\@len@StrMid% dernière position trop grande ?
558                                \edef\@StrMid@Last{\@len@StrMid}% on limite à la longueur de la chaine
559                                \@xs@BadNumArg% avertissement
560                        \fi
561                        \Position@StrMid\z@
562                        \@xs@Mid@Gobble#1\@nil% on supprime ce qu'il faut à gauche
563                        \edef\@final@Result{}% rien dans le résultat final pour l'instant
564                        \expandafter\@xs@Mid@Concat\@result@Mid@Gobble\@nil% on prend les caractères qu'il faut à gauche
565                        \expandafter\@xs@ReturnResult\expandafter{\@final@Result}{#2}%
566                \fi
567        \fi}
568
569 \newcommand\StrGobbleLeft{\@ifstar\@xs@StrGobbleLeft@@\@xs@StrGobbleLeft@}
570 % supprime #2 caractères à gauche dans la chaine #1
571 \newcommand\@xs@StrGobbleLeft@@[2]{%
572        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
573        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrGobbleLeft{\xsarg@a}{\@xs@expand#2}}{}}%
574        \@call}
575 \newcommand\@xs@StrGobbleLeft@[2]{%
576        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrGobbleLeft{#1}{#2}}{}}%
577        \@call}
578 \def\@xs@StrGobbleLeft#1#2[#3]{%
579        \edef\@StrMid@First{\numexpr#2+1}%
580        \@xs@StrLen{#1}[\@StrMid@Last]%
581        \@xs@StrMid{#1}[\@xs@result@StrGobbleLeft]%
582        \@xs@ReturnResult{\@xs@result@StrGobbleLeft}{#3}}
583
584 % extrait de #1 la chaine à gauche de longueur #2
585 \newcommand\StrLeft{\@ifstar\@xs@StrLeft@@\@xs@StrLeft@}
586 \newcommand\@xs@StrLeft@@[2]{%
587        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
588        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrLeft{\xsarg@a}{\@xs@expand#2}}{}}%
589        \@call}
590 \newcommand\@xs@StrLeft@[2]{%
591        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrLeft{#1}{#2}}{}}%
592        \@call}
593 \def\@xs@StrLeft#1#2[#3]{%
594        \edef\@StrMid@First{1}%
595        \ifnum#2<0\@xs@BadNumArg\fi% avertissement
596        \edef\@StrMid@Last{#2}%
597        \@xs@StrMid{#1}[\@xs@result@@StrLeft]%
598        \@xs@ReturnResult{\@xs@result@@StrLeft}{#3}}
599
600 % supprime #2 caractères à droite dans la chaine #1
601 \newcommand\StrGobbleRight{\@ifstar\@xs@StrGobbleRight@@\@xs@StrGobbleRight@}
602 \newcommand\@xs@StrGobbleRight@@[2]{%
603        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
604        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrGobbleRight{\xsarg@a}{\@xs@expand#2}}{}}%
605        \@call}
606 \newcommand\@xs@StrGobbleRight@[2]{%
607        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrGobbleRight{#1}{#2}}{}}%
608        \@call}
609 \def\@xs@StrGobbleRight#1#2[#3]{%
610        \@xs@StrLen{#1}[\@Result@Length]%
611        \ifnum#2>\@Result@Length\@xs@BadNumArg\fi% avertissement
612        \edef\@StrMid@First{1}%
613        \edef\@StrMid@Last{\numexpr\@Result@Length-#2}%
614        \@xs@StrMid{#1}[\@xs@result@StrGobbleRight]%
615        \@xs@ReturnResult{\@xs@result@StrGobbleRight}{#3}}
616
617 % renvoie #2 caractères à la droite de la chaine #1
618 \newcommand\StrRight{\@ifstar\@xs@StrRight@@\@xs@StrRight@}
619 \newcommand\@xs@StrRight@@[2]{%
620        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
621        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrRight{\xsarg@a}{#2}}{}}%
622        \@call}
623 \newcommand\@xs@StrRight@[2]{%
624        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrRight{#1}{#2}}{}}%
625        \@call}
626 \def\@xs@StrRight#1#2[#3]{%
627        \ifnum#2<0\@xs@BadNumArg\fi% avertissement
628        \@xs@StrLen{#1}[\@StrMid@Last]%
629        \edef\@StrMid@First{\numexpr\@StrMid@Last+1-#2}%
630        \@xs@StrMid{#1}[\@xs@result@StrMid]%
631        \@xs@ReturnResult{\@xs@result@StrMid}{#3}}
632
633 \newcommand\StrChar{\@ifstar\@xs@StrChar@@\@xs@StrChar@}
634 \newcommand\@xs@StrChar@@[2]{%
635        \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#2}%
636        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
637        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrMid{\xsarg@a}}{}}%
638        \@call}
639 \newcommand\@xs@StrChar@[2]{%
640        \edef\@StrMid@First{\@xs@expand#2}\edef\@StrMid@Last{\@xs@expand#2}%
641        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrMid{#1}}{}}%
642        \@call}%
643
644 \newcount\Occurrence@StrCount
645 % Combien de fois compte t-on #2 dans #1 ?
646 \newcommand\StrCount{\@ifstar\@xs@StrCount@@\@xs@StrCount@}
647 \newcommand\@xs@StrCount@@[2]{%
648        \@xs@expand@and@detokenize{\xsarg@a}{#1}%
649        \@xs@expand@and@detokenize{\xsarg@b}{#2}%
650        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrCount{\xsarg@a}{\xsarg@b}}{}}%
651        \@call}
652 \newcommand\@xs@StrCount@[2]{%
653        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrCount{#1}{#2}}{}}%
654        \@call}
655 \def\@xs@StrCount#1#2[#3]{%
656        \Occurrence@StrCount\z@% on initialise à 0
657        \def\@xs@StrCount@i{%
658                \advance\Occurrence@StrCount\@ne% occurrence := occurrence +1
659                \@xs@StrPosition[\the\Occurrence@StrCount]{#1}{#2}[\@xs@Pos@StrCount]% position de l'occurrence ?
660                \ifnum\@xs@Pos@StrCount=\z@% si l'occurrence n'est pas trouvée
661                        \advance\Occurrence@StrCount\m@ne% on enlève 1 au compteur
662                        \@xs@ReturnResult{\the\Occurrence@StrCount}{#3}% et on renvoie ce résultat
663                \else
664                        \@xs@StrCount@i% si l'occurrence est trouvée, on recommence
665                \fi}%
666        \@xs@StrCount@i}
667
668 \newcount\Position@StrPosition
669 % renvoie la position de l'occurrence #1 de la sous chaine #3 dans la chaine #2
670 \newcommand\StrPosition{\@ifstar\@xs@StrPosition@@\@xs@StrPosition@}
671 \newcommand\@xs@StrPosition@@[3][1]{%
672        \@xs@expand@and@detokenize{\xsarg@a}{#2}%
673        \@xs@expand@and@detokenize{\xsarg@b}{#3}%
674        \edef\@call{\noexpand\@testopt{\noexpand\@xs@StrPosition[\@xs@expand#1]{\xsarg@a}{\xsarg@b}}{}}%
675        \@call}
676 \newcommand\@xs@StrPosition@[3][1]{%
677        \@xs@def\@call{\@xs@expand\@testopt{\@xs@expand\@xs@StrPosition[#1]{#2}{#3}}{}}%
678        \@call}
679 \def\@xs@StrPosition[#1]#2#3[#4]{%
680        \Position@StrPosition\z@
681        \ifx\@empty#3\@empty%
682        \else
683                \@xs@StrBefore[#1]{#2}{#3}[\@xs@result@StrPosition]% on extrait la chaine avant l'occurrence de #3
684                \expandafter\@xs@StrLen\expandafter{\@xs@result@StrPosition}[\@xs@len@StrPosition]%
685                \Position@StrPosition\@xs@len@StrPosition%
686                \advance\Position@StrPosition\@ne% \@xs@len@StrPosition+1 est le résultat à priori...
687                \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
688                        \Position@StrPosition\z@% on considère que l'occurrence de #3 n'est pas trouvée, mais...
689                        \ifnum#1=\@ne% si on teste la 1ère occurrence
690                                \@xs@IfBeginWith{#2}{#3}% et que le #2 commence par #3,
691                                        {\Position@StrPosition\@ne}% c'est le seul cas où on renvoie 1
692                                        {}%
693                        \fi
694                \fi
695        \fi
696        \expandafter\@xs@ReturnResult\expandafter{\the\Position@StrPosition}{#4}}
697
698 % Définit le délimiteur verb et le développement des arguments par défaut
699 \AtBeginDocument{\setverbdelim{|}\fullexpandarg}
700 \endinput
701 %
702 % Historique :
703 %
704 % 5/7/2008 : premiere sortie de ce package