1
/************************************************************
2
* Librairie pour la gestion de chaine de caractères
5
* Auteur: Achraf cherti
6
* Email: achrafcherti@gmail.com
7
************************************************************
8
* NOTE: les commentaires de ce code source sont
9
* écrit au format utf8.
10
************************************************************
14
* Cette source est distribuée sous licence
15
* GNU General Public Licence version 2.
17
* Elle n'est distribuée sans aucune garantie.
18
* Vous avez le droit de la distribuer,
19
* l'étudier ou l'améliorer tant que le
20
* code source reste libre et conforme à
22
************************************************************
27
* * Création de toutes les fonctions de base
28
* * Version pas complètement testée
30
* * Elimination de tous les warning critiques
31
* * Correction de quelque bugs trouvés grâce à
32
* certaines programmes qui utilisent gstr.c
33
* (comme Jargon Informatique)
35
* * Relecture de toutes les fonctions une à une
36
* et réécriture de certaines (pour optimisation)
37
* * Réécriture de presque tous les commentaires
38
* incorrectement écrits pour avoir mieux de
39
* détails sur l'utilisation des fonctions.
40
* * Changement du nom gs_joker() par gs_wildcard()
41
* * Correction de certains bugs dans gs_wildcard
42
* * Réécriture de gs_is_affix() et gs_is_caseaffix()
43
* gs_indexof() gs_last_indexof().
44
* * Remplacement dans gs_sdel() strcpy() par
45
* memmove() plus adaptée à cela et valgrind
46
* provoque quelques erreurs.
47
* * Changement de gs_join() pour qu'il alloque
48
* le tout d'un coup au lieu de faire des realloc
50
* * Elimination de l'allocation mémoire dans
52
* * Correction de tous les malloc/realloc/free
53
* par des MALLOC/REALLOC/FREE (les defines
54
* pour faciliter l'intégration avec un
55
* autre type d'allocations)
56
* * correction de gs_strcat_r ou dest n'était
57
* pas désalloqué quand il n'y avait pas assez
59
* * Correction d'un bug dans gs_replace_m() et
60
* gs_replace_r() qui n'alloque pas la bonne
62
* * Correction d'un bug dans gs_replace_m()
63
* qui ne teste pas le NULL dans la bonne
65
* * Petite optimisation dans gs_nstr() au lieu
66
* de recalculer strlen rech, il la stocke dans
67
* une variable au début.
68
* * Optimisation de gs_replace() pour qu'il
69
* test le type de remplacement au début...
70
* * gs_*case, remplacement do{}while par while{}
72
* * Correction d'un petit bug dans _gs_substr()
73
* qui ne retournait pas str + pas de memmove
74
* * Création de fonction gs_change pour être le
75
* pilier de gs_change_m et gs_change_r
76
* * Correction d'un bug dans gs_r_accept() qui
77
* ignore le premier caractère lors de la recherche.
78
* * Correction de gs_r_reject() - même bug de
81
************************************************************/
87
#include <ctype.h> // toupper/tolower
91
/*********************************************
92
* Compare 'str' avec 'joker'.
93
* Permet de mettre un wilcard dans la
95
* Comme par exemple: *.exe
96
*********************************************/
99
int gs_wildcard(const char *p, const char *joker)
101
while(*p && *joker) {
104
//chercher prochain caractère
105
//dans joker qui est diff '?' et '*'
107
while(*joker=='*' || *joker=='?') joker++;
109
//cherche le caractère qui égale
110
//celui demandé par le joker.
111
while(*p && *p!=*joker) p++;
113
//pas trouvé?? alors erreur...
114
//comme par exemple acha = ach*raf
115
if(*p!=*joker) return 0;
117
//si fin mais égalité
118
if(!*p && !*joker) return -1;
121
// S'il n'y a pas d'étoile... Il va soit
122
// tester avec un joker '?' soit faire un
123
// vrai test pour voir si le *joker actuel
125
else if(*joker!='?' && *joker!=*p) return 0;
127
//incrémente les deux... Comme toujours...
131
//si l'on finit avant l'autre...
133
//si le joker a déjà fini... donc c pas bon.
134
if(!*joker) return 0;
135
//cherche s'il n'y a que des wildcard
136
//à la fin de joker car si c'est le cas
137
//ça veut dire que c'est parfait.
138
//par exemple: gs_wildcard("achra","achra*")
139
//p va finir avant le dernier 'a' de achra
140
//et puis la fonction va croire que c faux.
141
//mais maintenant après cette boucle il saura
142
//qu'il n'y a "que" des wildcard
143
while(*joker && (*joker=='*' || *joker=='?')) joker++;
144
//si joker est arrivé à la fin!! ça veut dire que c ok
145
if(!*joker) return -1;
149
//et sinon... cela veut dire que c'est pas bon :-)
153
/************************************************************
154
* Teste si 'affix' est l'affixe de 'string'.
155
* Si c'est oui, il retourne une valeur différente
160
* string et affix doivent être de vrai pointeurs non NULL.
161
************************************************************/
163
int gs_is_affix(const char *string, const char *affix)
166
if(*string!=*affix) return 0;
172
/************************************************************
173
* Teste si 'affix' est l'affixe de 'string'.
174
* Si c'est oui, il retourne une valeur différente
177
* Ce test est différent de gs_is_affix car il ignore
178
* les majuscules ou minuscules.
182
* string et affix doivent être de vrai pointeurs non NULL.
183
************************************************************/
185
int gs_is_caseaffix(const char *string, const char *affix)
188
if(tolower(*string)!=tolower(*affix)) return 0;
194
/************************************************************
195
* Recheche dans 'string' si 'rech' existe.
196
* Il retourne l'index de ce qui concorde à 'rech'
199
* isaffix: marche de la même façon que gs_is_affix()
200
************************************************************/
202
char *gs_indexof(const char *string, const char *rech, int (*isaffix)(const char *, const char *))
204
if(!isaffix) isaffix=gs_is_affix;
206
if((*isaffix)(string,rech)) return (char *)string;
209
return NULL; //pas trouvé!
213
char *gs_last_indexof(const char *string, const char *rech, int (*isaffix)(const char *, const char *))
215
const char *s = string+strlen(string)-strlen(rech);
216
if(!isaffix) isaffix=gs_is_affix;
219
if((*isaffix)(s,rech)) return (char *)s;
225
/************************************************************
226
* Supprime 'len' caractères depuis la position 'pos'
227
* dans la chaine 'string'.
228
************************************************************/
230
char *gs_sdel(char *string, size_t pos, size_t len)
234
//fix les dépassements
236
if(pos>=slen) return string;
237
if(pos+len>=slen) len=slen-pos;
240
memmove(string+pos,string+pos+len, slen-pos-len+1);
245
// Même chose que gs_sdel mais pour un seul caractère
247
char *gs_cdel(char *string, size_t pos)
249
return gs_sdel(string,pos,1);
252
/************************************************************
253
* Cette fonction prend toutes les chaines qui sont
254
* dans 'list' pour les joindre en utilisant 'separator'.
256
* Par exemple la liste contient:
261
* Si on utilise cette fonction avec separator=":"
262
* Alors le résultat (alloqué) sera:
265
************************************************************/
267
char *gs_join(GS_LIST *list, const char *separator)
271
static size_t len_general;
273
//teste si list est vide. très important
274
//pour ce qui est en bas...
275
if(!list->size) return strdup("");
277
//calcul du len général (toutes les chaines de 'list' + separateurs)
278
len_general=strlen(separator)*(list->size-1); //y a toujours un séparateur en moins
280
while(i<list->size) { len_general+=strlen(list->ptr[i]); i++; }
282
//allocation du tout...
283
s = (char *)MALLOC(len_general+1);
285
*s=0; //vide la chaine
287
//maintenant il va copier le contenu de toutes
288
//les chaines de 'list' + separateur dans s
290
strcpy(s,list->ptr[0]);
291
while(i<list->size) { strcat(s,separator); strcat(s,list->ptr[i]); i++; }
297
//j'ai préféré la remplacer pour éviter
298
//la réallcation à chaque fois...
300
for(i=0;i<list->size;i++) {
301
if(i!=0) s=gs_strcat_r(s,separator);
302
s=gs_strcat_r(s,list->ptr[i]);
309
/************************************************************
310
* Cette fonction découpe 'string' en morceaux en
311
* utilisant le délimiteur 'delim'
313
* _strstr(chaine,delim)
314
* =====================
315
* La fonction _strstr() se charge de trouver
316
* 'delim' dans 'chaine'
317
************************************************************/
319
GS_LIST *gs_split_ex(const char *string, const char *delim, char *(*_strstr)(const char *,const char *), size_t delim_len)
321
static const char *suivant, *precedent, *cp /*const p*/;
325
//Création d'une liste
326
GS_LIST *list = gs_list_new();
327
if(!list) return list;
331
while((suivant=(*_strstr)(precedent,delim))) {
332
//calcule le len de 'precedent' à 'len'
334
while(cp<suivant) { len++; cp++; }
336
//allocation d'une nouvelle chaine
337
p = (char *)MALLOC(len+1); //le 1 du zero
342
strncpy(p,precedent,len);
343
p[len]=0; //le zero de la fin...
346
if(!gs_list_push(list,p)) {
351
precedent=suivant+delim_len;
354
//si oui alors on l'ajoute!
356
if(!gs_list_push(list,p)) {
361
//et enfin, il retourne le résultat!
365
GS_LIST *gs_split(const char *string, const char *delim)
367
return gs_split_ex(string,delim,strstr,strlen(delim));
370
GS_LIST *gs_csplit(const char *string, const char *delim)
372
return gs_split_ex(string,delim,strpbrk,1);
375
/************************************************************
376
* Création d'une nouvelle liste GS_LIST.
378
* Cette alloque le contenu, mais aussi la variable GS_LIST
379
* (qui est d'ailleurs retournée).
383
* La liste 'list->ptr' est une char**, elle est compatible
384
* avec les chaines qui finissent avec un zero.
385
* C'est pour cette raison qu'un zero est toujours ajouté
387
************************************************************/
389
GS_LIST *gs_list_new()
391
//création d'une liste vide
392
GS_LIST *list = (GS_LIST *)MALLOC(sizeof(GS_LIST));
393
memset(list,0,sizeof(GS_LIST));
395
// créer un élément contenant zero
396
list->ptr=(char **)MALLOC(sizeof(char *));
397
if(!list->ptr) { FREE(list); return NULL; }
400
//retourne le résultat
404
/************************************************************
405
* Ajoute un élément dans la liste 'list'.
407
* NOTE: str doit être un pointeur malloc! car gs_list_push
408
* n'ajoute que le pointeur 'str'
409
* Utilisez gs_list_push_m() pour avoir une fonction
410
* qui copie str avec strdup()
412
* NOTE: 'list' doit être alloquée avec gs_list_new()
416
* S'il n'y a pas assez de mémoire pour ajouter un
417
* élément dans 'list', cette fonction retourne NULL.
418
* Sinon, elle retourne str.
419
************************************************************/
421
char *gs_list_push(GS_LIST *list, char *str)
426
list->ptr = (char **)REALLOC(save=list->ptr, (sizeof(GS_LIST)*(list->size+1))+1);
433
list->ptr[list->size] = str;
434
list->ptr[list->size+1] = NULL; // le dernier contiens toujours NULL
441
/************************************************************
442
* Ajoute une copie malloc de str dans list.
443
* Voir l'explication dans gs_list_push() pour avoir plus
445
************************************************************/
447
char *gs_list_push_m(GS_LIST *list, const char *str)
449
return gs_list_push(list,strdup(str));
452
/************************************************************
453
* Désalloque la liste de strings contenue dans le pointeur
454
* du pointeur 'list'.
456
* NOTE: Cette fonction met le pointeur 'list' à 0.
457
************************************************************/
459
void gs_list_free(GS_LIST **list)
461
size_t list_size=(*list)->size,i;
462
//free tous les pointeurs dans la liste
465
FREE((*list)->ptr[i]);
474
/************************************************************
475
* Retourne une nouvelle allocation mémoire contenant
478
* Note: Si dest/src=0 alors ret NULL
480
************************************************************/
482
char *gs_strcat_m(const char *dest, const char *src)
485
if(!dest || !src) return NULL;
486
ptr=(char *)MALLOC(strlen(dest)+strlen(src)+1);
493
/************************************************************
494
* Même chose que gs_strcat_m() mais cette fois-ci c'est
495
* 'dest' qui est realloqué pour contenir dest+src
499
* Quand il n'y a pas assez de mémoire, il retourne NULL
500
* et free 'dest' automatiquement.
501
* Il ne pas réutiliser dest après un NULL
502
* retourné par cette fonction.
506
* Si dest ou src sont NULL elle retourne systématiquement
507
* NULL et désallque dest si celui-ci est non NULL.
511
* SI: !dest ret strdup(src)
514
* Si pas assez de mémoire retourne
515
* 0 mais le pointeur on doit faire
516
* free(dest) après (comme realloc)
517
************************************************************/
519
char *gs_strcat_r(char *dest, const char *src)
522
if(!dest || !src) { free(dest); return NULL; }
523
dest = (char *)REALLOC(save=dest, strlen(dest)+strlen(src)+1);
524
if(!dest) {free(save); return dest;}
525
return strcat(dest,src);
528
/************************************************************
529
* Cette fonction remplace le caractère 'src' par 'repl'
530
* dans la chaine 'string'.
532
* Elle retourne un simple pointeur vers 'string'.
533
************************************************************/
535
char *gs_creplace(char *string, char src, char repl)
539
if(*string==src) *string=repl;
545
/************************************************************
546
* Remplacement de 'src' par 'repl' dans
551
* Retourne un nouveau pointeur "malloc"
552
* contenant le résultat (remplacement).
554
* ATTENTION: !str alors ret NULL.
555
************************************************************/
557
char *gs_replace_m(const char *str, const char *src, const char *repl)
559
char *str_m; //string alloqué
562
if(!str) return NULL;
564
// calcule le nombre de repl dans src
565
len=gs_nstr(str,src);
566
if(!len) return strdup(str);
567
len=(strlen(str)-(len*strlen(src)))+(len*strlen(repl))+1;
570
str_m = (char *)MALLOC(len); // src fois * len2
571
if(!str_m) return NULL;
575
return gs_replace(str_m,src,repl);
578
/************************************************************
579
* Remplacement de 'src' par 'repl'
580
* 'src' est realloqué et contient
581
* le résultat du remplacement.
585
* La fonc retourne un ptr realloqué de
586
* str contenant le remplacement...
588
* Si pas assez de mémoire retourne NULL.
590
* ATTENTION: !str alors ret NULL.
592
************************************************************/
594
char *gs_replace_r(char *str, const char *src, const char *repl)
599
if(!str) return NULL;
601
len=gs_nstr(str,src);
603
len=strlen(str) - (len*strlen(src)) + (len*strlen(repl)) + 1;
605
str = (char *)REALLOC(save=str,len); // src fois * len2
611
return gs_replace(str,src,repl);
614
/************************************************************
615
* Calcule le nombre de fois que 'rech'
616
* est trouvée dans 'str'.
617
************************************************************/
619
size_t gs_nstr(const char *str, const char *rech)
623
size_t len_rech = strlen(rech);
635
/************************************************************
636
* remplace 'src' par 'repl' sans aucune
641
* La fonction un pointeur vers 'str'.
645
* Il faut être sûre que 'str' peut contenir
646
* le remplacement de tous les 'src' par les
648
************************************************************/
650
char *gs_replace(char *str, const char *src, const char *repl)
652
static size_t len,len2;
653
static int difference;
661
//ici, le type de remplacement quand il ont la même taille
663
while((str=strstr(str,src))) {
664
strncpy(str,repl,len2);
669
//Donc il met ce qu'il y a dans 'repl' à la place de 'src'
670
//puis il déplace tout ce qui reste remplit le vide laissé
671
//par 'repl' (puisqu'il est petit) par ce qui reste de str.
672
else if(difference>0) {
673
while((str=strstr(str,src))) {
674
strncpy(str,repl,len2);
676
memmove(str,str+difference,strlen(str+difference)+1);//+1=zero
680
//Alors il va laisser de la place à repl pour qu'elle
681
//puisse se permettre de s'installer là ou 'str' est logée.
683
while((str=strstr(str,src))) {
684
memmove(str+len2,str+len,strlen(str+len)+1); //+1 = null final
685
strncpy(str,repl,len2);
693
/************************************************************
694
* Cette fonction concertit tous les caractères de 'str'
696
* Elle retourne le pointeur 'str'.
697
************************************************************/
699
char *gs_lcase(char *str)
703
*str=gs_clcase(*str);
709
/************************************************************
710
* Cette fonction concertit tous les caractères de 'str'
712
* Elle retourne le pointeur 'str'.
713
************************************************************/
715
char *gs_ucase(char *str)
719
*str=gs_cucase(*str);
726
char gs_clcase(char c)
727
{ return (char)tolower(c); }
728
char gs_cucase(char c)
729
{ return (char)toupper(c); }
731
/************************************************************
732
* _gs_substr fait un substring. Cela veut dire que
733
* le string contient ce qu'il y a à la position 'pos'
734
* à la longueur 'len'.
738
* Elle ne teste pas si pos->len sont dans le string
739
* ce qui fait que si vous appelez cette fonction il
740
* faut être sûre de cette donnée.
741
************************************************************/
743
char *_gs_substr(char *str, int pos, int len)
751
for(n_len=len;n_len;n_len--) {
755
str[len]=0; //le zero de la fin
760
/************************************************************
761
* La même chose que _gs_substr() mais cette fonction est
762
* plus haut-niveau puisqu'elle teste au moins si 'pos'
763
* et 'len' existent dans 'str' et essaye de les ajuster
768
* En plus elle permet de prendre une position de
770
* Si pos est négatif cela veut dire
772
* position depuis la fin!
774
* p.ex: -1 c le dernier caractère
775
************************************************************/
777
char *gs_substr(char *str, int pos, int len)
779
size_t str_size = strlen(str);
782
if(pos<0) pos=str_size+pos;
783
if(!len) {*str=0; return str; } //un string vide si pas de len
785
//il teste si len dépasse
786
if(pos+len>str_size) len=str_size-pos;
788
//fait maintenant un substr
789
return _gs_substr(str,pos,len);
792
/************************************************************
793
* Retourne le substr du pointeur 'str' en version malloc.
794
************************************************************/
796
char *gs_substr_m(char *str, int pos, int len)
799
if(!str) return NULL;
802
if(!ptr) return NULL;
804
return gs_substr(ptr,pos,len);
807
/************************************************************
808
* Change une partie d'un string
811
* il faut être sûre qu'il y a assez d'espace mém
812
* pour remplacer le 'len' par strlen'value'.
813
************************************************************/
815
char *gs_change(char *str, int pos, int len, const char *value)
818
if(!str) return NULL;
820
value_len = strlen(value);
824
memmove(str+pos,value,value_len);
830
dest = str+pos+value_len;
832
memmove(dest,src,strlen(src)+1);
833
memmove(str+pos,value,value_len);
839
/************************************************************
840
* Change une partie d'un string par une valeur
842
* Cette version _m retourne le résultat en malloc (ce
843
* qui veut dire que cette fonction ne touche pas à str)
844
************************************************************/
846
char *gs_change_m(const char *str, int pos, int len, const char *value)
849
if(!str) return NULL;
850
p=(char *)malloc(strlen(str)+strlen(value)-len+1);
852
return gs_change(p,pos,len,value);
855
/************************************************************
856
* Changer une partie d'un string en y mettant une valeur.
858
* si realloc fail. alors free old str
860
************************************************************/
862
char *gs_change_r(char *str, int pos, int len, const char *value)
864
if(!str) return NULL;
865
str=(char *)realloc(str,strlen(str)+strlen(value)-len+1);
867
return gs_change(str,pos,len,value);
870
/************************************************************
871
* Retourne le pointeur vers le premier caractère dans
872
* str qui appartient à 'accept'.
873
************************************************************/
875
char *gs_accept(char *str, const char *accept)
878
if(strchr(accept,*str))
885
/************************************************************
886
* La même chose que gs_accept() mais à l'envert.
887
************************************************************/
889
char *gs_r_accept(char *str, const char *accept)
896
if(strchr(accept,*end)) return end;
902
/************************************************************
903
* Retourne le pointeur vers le premier caractère qui
904
* n'appartient pas à la chaine reject.
905
************************************************************/
907
char *gs_reject(char *str, const char *reject)
910
if(!strchr(reject,*str)) return str;
917
char *gs_r_reject(char *str, const char *reject)
919
char *end=str+strlen(str);
922
if(!strchr(reject,*end)) return end;
928
/************************************************************
929
* Supprime l'emsemble des "reject"
930
* de droite et de gauche de la chaine 'str.
931
************************************************************/
933
char *gs_c_trim(char *str, const char *reject)
934
{ return gs_c_ltrim(gs_c_rtrim(str,reject),reject); }
936
/************************************************************
937
* Supprime l'ensemble des "reject" de gauche.
938
************************************************************/
940
char *gs_c_ltrim(char *str, const char *reject)
942
char *s = gs_reject(str,reject);
947
if(s==str) return str;
949
memmove(str,s,strlen(s)+1);
953
/************************************************************
954
* Supprime l'ensemble des "reject" de droite.
955
************************************************************/
957
char *gs_c_rtrim(char *str, const char *reject)
959
char *s=gs_r_reject(str,reject);
968
#define TRIM_REJECT " \t\v\r\n\f"
969
// \t tabulation horizontale
970
// \v tabulation verticale
971
// \r carriage return
972
// \n line feed (saut de ligne)
973
// \f form feed (saut de page)
975
/************************************************************
976
* Supprime les espaces de gauche.
977
************************************************************/
979
char *gs_ltrim(char *str)
981
return gs_c_ltrim(str,TRIM_REJECT);
984
/************************************************************
985
* Supprime les espaces de gauche.
986
************************************************************/
988
char *gs_rtrim(char *str)
990
return gs_c_rtrim(str,TRIM_REJECT);
993
/************************************************************
994
* Supprime les espaces de gauche et de droite
995
************************************************************/
997
char *gs_trim(char *str)
999
return gs_ltrim(gs_rtrim(str));
1002
/************************************************************
1003
* realloque la chaine 'str' selon son len.
1004
************************************************************/
1006
char *gs_fix_realloc(char *str)
1009
return (char *)REALLOC(str,strlen(str)+1);