Logo Search packages:      
Sourcecode: vbaexpress version File versions  Download package

astring.cpp

/*
 *   This file is part of VBA Express.
 *
 *   Copyright (c) 2005-2006 Achraf cherti <achrafcherti@gmail.com>
 * 
 *   VBA Express is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   VBA Express is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with VBA Express; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*********************************************************************
 * Achraf's String
 *
 * Fonctions dynamiques et optimisées pour la gestion
 * de strings en C++.
 *
 * Achraf cherti 2005-2006
 * Email: achrafcherti@gmail.com
 *
 *************************************************************
 * Licence:
 * Cette source est distribuée sous Licence GNU General
 * Public licence version 2 (ou ultérieure).
 *************************************************************
 *==============
 *Idées
 *==============
 * - substr pos pos2
 *************************************************************
 *==============
 * Historique:
 *==============
 * 
 * -----------------------------------
 * * Version 0.4 (Novembre 2005):
 * -----------------------------------
 * - Correction d'un petit bug dans la version DEBUG
 *   d'AStringList. Il donnait une information 
 *   de débogage erronée quand on a fait aucune
 *   opération sur le AStringList.
 * - Elimination de tous les warnings AINSI-C (-pedantic)
 *  
 * -----------------------------------
 * * Version 0.3 (Octobre 2005):
 * -----------------------------------
 * - Mise à jour de la fonction join_cat d'AString pour l'optimiser.
 *   elle est 8x plus rapide.
 * - Ajout d'un nouveau type de compilation: RECALC_LEN
 *   qui recalcule le len à chaque fois
 * - Ajout de deux variables pour calculer le nombre de
 *   strings alloqués puis désalloqués. Utile pour le DEBUG.
 * - Correction d'un bug dans insert. Maintenant on peux inclure
 *   une chaine dans elle même.
 * - Correction d'un bug dans operator*= qui ne calculait pas le 
 *   len à la fin.
 * - AString::operator= peut maintenant accueillir le même str.
 * - Correction de la fonction move() d'AStringList qui déplaçait
 *   l'élement après pos2 (au lieu d'avant pos2)
 * - Correction d'un bug dans la fonction replace() qui ne
 *   modifiait que le premier caractère de la chaine (lors du
 *   remplacement)
 *
 * -----------------------------------
 * * Version 0.2: Septembre 2005
 * -----------------------------------
 * - Ajout du define TEST_ZERO dans tout le code source
 * - Ajout de plusieurs commentaires aux fonctions
 * - Correction de la fonction split_cat() qui ne
 *   splittait pas une chaine ou il y a une seule
 *   division.
 * - Remplacement de strcpy() par un remplacement
 *   manuel dans la fonction remove() d'Astring.
 *   car cela cause une erreur mémoire.
 * - Correction d'un bug dans la fonction remove()
 *   qui ne mettait pas à jour la variable _len
 * - Correction d'un bug strcpy() dans operator<<char *
 *   et remplacement de strcpy() par memmove()
 * - Correction de la fonction insert() qui ne faisait
 *   qu'un déplacement de string (sans insertion)
 * - Elimination d'une instruction inutile dans replace()
 * - Correction du calcul du len dans replace()
 * - Correction d'un petit bug dans replace() quand la
 *   la nouvelle chaine était plus petite que l'ancienne.
 * - Correction des fonctions ltrim() pour un souci
 *   d'accès mémoire. Rempl de strcpy by memmove()
 * - Correction de la fonction substr(). Même chose que
 *   ltrim(). strcpy() par memmove()
 * - Réécriture de la fonction operator*=
 * - Correction d'un petit bug dans la fonction insert()
 *   qui causait une erreur mem quand on voulait insérer
 *   une liste dans la même liste.
 * - Corection d'un bug dans AString operator=AString&
 *   qui vidait le string quand celui ci était le pointeur
 *   même de la classe.
 * - Correction de AString operator<<AString&
 *   qui causait une errmem quand le AString était la même 
 *   classe mère.
 * - Ajout d'une nouvelle fonction: instr
 * - Correction de la fonction replace() de 'char' qui
 *   ne faisait pas de remplacement du tout.
 * - Ajout de 2 nouvelles fonctions: rinstr(char,size_t) rinstr(char)
 *
 * ------------------------------
 * * Version 0.1: Avril 2005
 * ------------------------------
 * - Première version
 *********************************************************************/

 //TODO tout ce qui est affectation doit tester si null
 
#include "astring.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef DEBUG
int AString_num_alloc=0;
int AStringList_num_alloc=0;
#endif
 
/************************************************************
 * TESTS de defines
 ************************************************************/
//s'il y a un debug
#ifdef DEBUG

//si on ne doit pas recalculer le len
#ifndef RECALC_LEN
#define TEST_LEN() {if(strlen(_ptr)!=_len) { fprintf(stderr,"ERREUR: %s:%i\nLe vrai len(%i) != _len(%i)\n",__FILE__,__LINE__,strlen(_ptr),_len); exit(1);} if(_len>_realloc_size) { fprintf(stderr,"Le len(%i) est plus grand que ce qui est alloué (%i)...\n",_len,_realloc_size); exit(1);} } 
#else
#define TEST_LEN()
#endif

//s'il n'y a pas de debug
#else
#define TEST_LEN()
#endif

/************************************************************
 * Combien il y a d'extra?
 *
 * Un extra c'est une allocation mémoire qui dépasse
 * la taille réélle de la chaine.
 *
 * NOTE: Le fonction retourne l'extra en incluant le 
 *       zero final dans le calcul
 ************************************************************/
size_t AString::extra()
{
      static size_t true_len;
      true_len=length();
      if(true_len+1>=_realloc_size) return 0;
      return _realloc_size-(true_len+1);
}

/************************************************************
 * Les fonction split*() permettent de diviser l'AString
 * en utilisant le séparateur "sep" puis en l'ajoutant
 * dans la liste 'list'
 *
 * Exemple:
 * -------
 *  AString string("bonjour-tout-le-monde");
 *  AStringList list;
 *  string.split(list,"-")
 *  //divise avec le séparateur tiret
 *  //pour avoir bonjour tout le monde séparés
 *
 * Détails:
 * -------
 *  split()     - vident la liste avant d'ajouter
 *  split_cat() - Ajoutent à la liste sans la vider
 *
 * Valeur de retour:
 * -----------------
 *  La fonction retourne la référence à this.
 ************************************************************/
AString &AString::split(AStringList &list,const char *sep)
{ list.clear(); return split_cat(list,sep);}

AString &AString::split(AStringList &list,AString &sep)
{ list.clear(); return split_cat(list,sep.ptr()); }

AString &AString::split_cat(AStringList &list,AString &sep)
{ return split_cat(list,sep.ptr()); }

AString &AString::split_cat(AStringList &list,const char *sep)
{
      char *p=_ptr, *precedent=_ptr;
      size_t len_sep;
      bool separateur_trouve=false; //quand le séparateur est trouvé précédement
#ifdef TEST_ZERO
      if(!sep) return *this;
#endif
      len_sep=strlen(sep);
      while(*p) {
            //si séparateur
            if(strncmp(sep,p,len_sep)==0) {
                  //Ajoute dans la liste...
                  char save=*p; *p=0;
                  list << precedent;
                  *p=save;
                  //dépasse séparateur dans le string
                  p+=len_sep;
                  precedent=p;
                  separateur_trouve=true;
            }
            else {
                  separateur_trouve=false;
                  p++;
            }
      }
      //y a deux choses qui peuvent déclancher l'ajout d'une
      //chaine à la fin
      //soit il reste quelque chose dans le string (non zero)
      //ou il y a un séparateur qui a été trouvé précédement
      //alors qu'après la chaine est vide.
      if(*precedent || separateur_trouve) list << precedent;
      return *this;
}

/***********************************************************
 * Fonction générique pour transformer les
 * caractères d'une chaine un par un en utilisant
 * la fonction char *fonc(char)
 *
 * Valeur de retour:
 * -----------------
 * - *this
 ***********************************************************/
AString &AString::transform(char (*fonc)(char))
{
      static char *p;
      if(!fonc) return *this;
      p=_ptr;
      while(*p) { *p=(*fonc)(*p); p++; }
      return *this;
}

/***********************************************************
 * fonctions statiques pour faire la conversion
 * maj/min
 ***********************************************************/
static char my_tolower(char c)
{
      static char diff='A'-'a';
      if(c>='A' && c<='Z') return c-diff;
      return c;
}

static char my_toupper(char c)
{ 
      static char diff='A'-'a';
      if(c>='a' && c<='z') return c+diff;
      return c;
}

/***********************************************************
 * Fonction pour convertir un caractère en minuscule
 * et en majuscule.
 ***********************************************************/

char AString::c_tolower(char c)
{ return (char)my_tolower(c); }

char AString::c_toupper(char c)
{ return (char)my_toupper(c); }

/***********************************************************
 * chomp() permet d'enlever le \n à la fin de la chaine
 *
 * chop() permet d'enlever le dernier caractère de la 
 * chaine
 ***********************************************************/
AString &AString::chomp()
{
      static size_t true_len;
      true_len = length();
      if(!true_len) return *this;
      if(_ptr[true_len-1]=='\n') this->chop();
      return *this;
}

char AString::chop()
{
      static char *p;
      char ret;
      static size_t true_len;
      true_len=length();
      if(!true_len) return 0;
      true_len--;
      //si le len n'est po recalculé alors mettre à jour _len
#ifndef RECALC_LEN
      _len=true_len; // CHANGELEN
#endif
      ret=*(p=(_ptr+true_len));
      *(p)=0;
      return ret;
}

/***********************************************************
 * push AStringList
 ***********************************************************/
void AString::operator>>(AStringList &list)
{ list.push(this->ptr()); }

/***********************************************************
 * Ajouter AStringList
 ***********************************************************/
AString &AString::join_cat(AStringList &list, const char *sep)
{
      size_t i;
      size_t true_len=0;
      size_t len_sep; //le len du separateur

      TEST_LEN();

      //si pas de sep alors " "
#ifdef TEST_ZERO
      if(!sep) sep=" ";
#endif

      //si la liste est vide alors on ne va rien ajouter
      if(!list.size()) return *this;

      //calcule le len du separateur
      len_sep=strlen(sep);

      //calcule de tout le len
      true_len=list[0].length();
      for(i=1;i<list.size();i++) true_len+=list[i].length()+len_sep;

      //recallocation
      _realloc(true_len+1); // +1 du zero final

      //on ajout le premier
      char *p=_ptr;
      strcpy(p,list[0].ptr()); p+=list[i].length();
      
      //copie les autres avec separateur
      for(i=1;i<list.size();i++) {
            strcpy(p,sep); p+=len_sep;
            strcpy(p,list[i].ptr()); p+=list[i].length();
      }

      //recalcule le len
#ifndef RECALC_LEN
      _len=true_len;
#endif

      TEST_LEN();
      return *this;
}

AString &AString::join_cat(AStringList &list, AString &sep)
{ return join_cat(list,sep.ptr()); }

AString &AString::operator<<(AStringList &list)
{ return join_cat(list," "); }

/***********************************************************
 * Joindre AStringList
 ***********************************************************/
AString &AString::join(AStringList &list, const char *sep)
{
      *this="";
      return join_cat(list,sep);
}

AString &AString::join(AStringList &list, AString &str)
{ return join(list,str.ptr()); }

AString &AString::operator=(AStringList &list)
{ return join(list," "); }

/***********************************************************
 * begin/end
 ***********************************************************/
size_t AString::end()
{
      static size_t true_len;
      true_len=length();
      return (true_len)?true_len-1:0;
}

/***********************************************************
 * Supprimer un caractère dans la chaine.
 ***********************************************************/
AString &AString::remove(size_t i)
{
      char *p;
      static size_t true_len;
      
      true_len=length(); //calcule le len

      //Un petit test DEBUG
      TEST_LEN();

      //un petit test
      if(i>=true_len) return *this;
      
      //positionne le pointeur dans le caractère à supprimer
      p=_ptr+i;

      //et là il commence la suppression des caractères
      for(;;) {
            *p=*(p+1);
            if(!*p) break;
            p++;
      }

      //mise à jour du len
      true_len--;
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif

      //Un petit test DEBUG
      TEST_LEN();

      //la référence à this... (au cas ou c'est utile!)
      return *this;
}

AString &AString::remove(size_t i, size_t len_i)
{
      char *p;
      size_t end;
      static size_t true_len;

      true_len=length();

      //si i dépasse len! là pas la peine... @+
      if(i>=true_len) return *this;

      //de même si len est zero
      if(!len_i) return *this;

      //pour savoir si i+len dépasse la chaine
      end=i+len_i;
      if(end>=true_len) {
            //dans ce cas là, il l'adapte à la chaine actuelle
            len_i=end-i;
            //dans le cas ou le len c'est 0...
            //pas la peine de continuer puisque ça dépasse
            //la chaine
            if(len_i<=0) return *this;
      }

      //il met p
      p=_ptr+i;

      //maintenant, il procède à la suppression
      for(;;) {
            *p=*(p+len_i);
            if(!*p) break;
            p++;
      }

      //met à jour le len
      true_len-=len_i;
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif

      //Un petit test DEBUG
      TEST_LEN();

      //valeur de retour
      return *this;
}

/***********************************************************
 * Faire sortir du string
 ***********************************************************/
void AString::operator>>(long &l)
{ l=atol(_ptr); }

void AString::operator>>(int &i)
{ i=atoi(_ptr); }

void AString::operator>>(double &d)
{ d=atof(_ptr); }

void AString::operator>>(float &f)
{ f=(float)atof(_ptr); }

//retourne EOF s'il échoue...
int AString::operator>>(FILE *handle)
{ return fputs(_ptr,handle); }

/***********************************************************
 * Insertion dans le string
 ***********************************************************/
AString &AString::insert(size_t index, const char *string)
{
      size_t len_new_string;
      static size_t true_len;
      AString *dup=0; 
      
      /* DEBUG */ TEST_LEN();
      
#ifdef TEST_ZERO
      if(!string) return *this;
#endif

      //si c'est le même str alors faire un dup
      if(string==_ptr) {
            dup=new AString(string);
            string=dup->ptr();
      }

      //calcule le len
      true_len=length();

      //un petit test si index > len
      //ça veut dire > fin+1 (cela inclut
      //le zero de fin)
      if(index>true_len){
            // inutile if(!len) return *this;
            index=true_len;
      }

      //calcule le len du nouveau string et realloque le tout
      len_new_string=strlen(string);
      _realloc(len_new_string+true_len+1);
      
      //strcpy autre part !
      memmove(_ptr+index+len_new_string,_ptr+index,true_len-index+1); //+1 pour le zero

      //fait maintenant notre petite insertion
      memmove(_ptr+index,string,len_new_string);
      
      //Incrémente le len!
      true_len+=len_new_string; //mise à jour :-)
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif

      /* DEBUG */ TEST_LEN();

      //DESALLOCATION (ne pas oublier)
      delete dup;

      return *this;
}

AString &AString::insert(size_t index, AString &str)
{
      return insert(index,str.ptr());
}

AString &AString::insert(size_t index, long l)
{
      AString temp;
      temp<<l;
      return insert(index,temp);
}

AString &AString::insert(size_t index, int i)
{
      AString temp;
      temp<<i;
      return insert(index,temp);
}

AString &AString::insert(size_t index, double d)
{
      AString temp;
      temp<<d;
      return insert(index,temp);
}

AString &AString::insert(size_t index, FILE *handle)
{
      AString temp;
      temp<<handle;
      return insert(index,temp);
}

/***********************************************************
 * Ajouter dans le string
 ***********************************************************/
AString &AString::operator<<(FILE *handle)
{ 
      static char buffer[1024];
      size_t len;
      char *p;

      /* DEBUG */ TEST_LEN();
      
      while(!feof(handle)) {
            *buffer=0;
            p=fgets(buffer,1024,handle);
            *this << buffer;

            //fin?
            len=strlen(buffer);

            //si \n alors c'est parfait! on a notre ligne :-)
            if(len!=0 && buffer[len-1]=='\n') break;
      }

      /* DEBUG */ TEST_LEN();

      return *this;
}

AString &AString::operator<<(AString &str) 
{ 
      if(this!=&str)
            return (*this)<<str.ptr(); 
      
      //si c'est un ajout du string sur lui même
      AString temp = str;
      *this<<temp;
      return *this;
}

AString &AString::operator<<(const char *str)
{ 
      static size_t len;
      static size_t true_len;
      
      TEST_LEN();

#ifdef TEST_ZERO
      if(!str) return *this;
#endif

      true_len=length();

      //important d'être détaché de 'test_zero'
      if(!*str) return *this;

      //calcul du len + reallocation
      len=strlen(str);
      _realloc(true_len+len+1);

      //là il va commencer le ++
      //un peu comme ça:
      //(mais cela ne copie pas le zero final)
      memmove(_ptr+true_len,str,len+1);

      true_len+=len;
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif

      _ptr[true_len]=0; //met le zero final (exception qui se produit quand _str=_ptr)

      //un petit test
      TEST_LEN();
      return *this;
}

AString &AString::operator<<(int i)
{
      static char var[20];
      sprintf(var,"%i",i);
      return *this<<var;
}
AString &AString::operator<<(double d)
{
      static char var[30];
      sprintf(var,"%f",d);
      return *this<<var;
}

AString &AString::operator<<(long l)
{
      static char var[30];
      sprintf(var,"%li",l);
      return *this<<var;
}

/***********************************************************
 * Fonctions de recherche
 ***********************************************************/

//recherche d'un char d'une manière inversée
//la fonction retourne 0 si aucun string n'a été trouvé
//1 ou plus si c'est ok
size_t AString::rinstr(char c, size_t pos) 
{
      size_t i;
      static size_t true_len;
      TEST_LEN(); //seulement au début puisque cette fonc ne modifie pas le len
      
      //calcul len
      true_len=length();
      
      if(!c) return true_len;
      //dans le cas ou la position dépasse ou égale la fin
      //la position égalera la fin (pour éviter segfault)
      if(pos>=true_len) return pos=true_len;
      //et puis commence l'opération de recherche
      for(i=0;i<true_len;i++) {
            if(_ptr[i]==c) return pos+1;//eh oui +1
            pos--;
      }
      return 0; //zero c pas bon
}

//recherche d'un char d'une manière inversée
//la fonction retourne 0 si aucun string n'a été trouvé
//1 ou plus si c'est ok
size_t AString::rinstr(char c) 
{
      size_t pos,i;
      static size_t true_len;
      TEST_LEN(); //seulement au début puisque cette fonc ne modifie pas le len
      true_len=length();
      if(!c) return true_len;
      if(!true_len) return 0; //si le len est vide ce n'est pas trouvé d'avance
      pos=true_len-1;
      //et puis commence l'opération de recherche
      for(i=0;i<true_len;i++) {
            if(_ptr[pos]==c) return pos+1;//eh oui +1
            pos--;
      }
      return 0; //zero c pas bon
}

//recherche d'un char
//la fonction retourne 0 si aucun string n'a été trouvé
//1 ou plus si c'est ok
size_t AString::instr(char c, size_t pos) 
{
      char *p=_ptr;
      static size_t true_len;

      TEST_LEN(); //seulement au début puisque cette fonc ne modifie pas le len

      true_len=length();
      if(!c) return true_len;
      
      //dans le cas ou la position dépasse ou égale la fin
      //pas la peine de chercher puisque c'est déjà introuvable
      if(pos>=true_len) return 0;
      
      //et puis commence l'opération de recherche
      while(*p) {
            if(*p==c) return pos+1;//eh oui +1
            p++; pos++;
      }
      return 0; //zero c pas bon
}

/***********************************************************
 * remplacement
 * si c1 ou c2 NULL alors bye
 ***********************************************************/

//remplacement d'un seul caractère
AString &AString::replace(char c1, char c2) 
{
      char *p=_ptr;
      if(!c1 || !c2) return *this;
      while(*p) {
            if(*p==c1) *p=c2;
            p++;
      }
      return *this;
}

//retourne true si s1 = s2 sachant que le len pour tester c'est 'len'
bool AString::cmp(const char *s1,const char *s2, size_t len)
{ if(strncmp(s1,s2,len)) return false; return true; }

//retourne true si s1 = s2 sachant que le len pour tester c'est 'len'
//cette fonction ne prends pas en compte la casse
bool AString::cmp_nocase(const char *s1,const char *s2, size_t len)
{ if(strncasecmp(s1,s2,len)) return false; return true; }
            
//fonction pour faire un remplacement de strings
AString &AString::replace(const char *ancien, const char *nouveau, AString_cmp_func (*equal))
{ return replace(0,ancien,nouveau); }

//OPTIMIZ chercher tt occu puis après new str et cpy cpy cpy -->fin
AString &AString::replace(size_t index, const char *ancien, const char *nouveau, AString_cmp_func (*equal))
{
      char *p; size_t len_ancien,len_nouveau;
      static size_t true_len;
      TEST_LEN();

#ifdef TEST_ZERO
      if(!ancien || !nouveau || !equal) return *this;
#endif

      true_len = length();

      //s'il dépasse le len ou encore s'il est à la fin de strings
      //(juste sur le zero final)
      if(index>=true_len) return *this;

      //mets les informations...
      p=_ptr+index;
      len_ancien=strlen(ancien);
      len_nouveau=strlen(nouveau);

      //si ancien vide... pas la peine de continuer :-)
      if(!len_ancien) return *this;
      
      //la plus rapide... l'ancien et le nouveau sont égaux!
      if(len_ancien==len_nouveau) {
            while(*p) {
                  if((*equal)(p,ancien,len_ancien)) { 
                        strncpy(p,nouveau,len_nouveau); 
                        p+=len_ancien; 
                  }
                  else
                        p++;
            }
            TEST_LEN();
            return *this;
      }

      //n'a pas besoin de realloc !!
      else if(len_nouveau<len_ancien) {
            while(*p) {
                  if((*equal)(p,ancien,len_ancien)) {
                        static size_t diff;
                        static char *s;
                        
                        if(*nouveau) strcpy(p,nouveau);

                        //commence le déplacement
                        //un peu commme ça: strcpy(p+len_nouveau,p+len_ancien);
                        s = p;
                        diff = len_ancien-len_nouveau; //la différence entre les deux
                        s+=len_nouveau;
                        for(;;) {
                              *s = *(s+diff);
                              if(!*s) break;
                              s++;
                        }

                        //maintenant, il avance
                        //si len_nouveau = 0 il reste en place car le prochaine caractère
                        //sera l'actuel (comme le nouveau c'est un vide "")
                        p+=len_nouveau;

                        //mise à jour du len interne
                        true_len-=diff;
#ifndef RECALC_LEN
                        _len=true_len; //CHANGELEN
#endif
                  }
                  else
                        p++;
            }
            TEST_LEN();
            return *this;
      }

      //la plus lente :-)
      else /* if(len_nouveau>len_ancien) */ {
            size_t new_len;

            //calcule combiens il y a d'anciens...
            size_t nombre_ancien = 0;

            while(*p) {
                  if((*equal)(p,ancien,len_ancien)) {
                        nombre_ancien++;
                        p+=len_ancien;
                  }
                  else
                        p++;
            }

            //si aucun "ancien" trouvé... pas la peine de continuer.
            if(!nombre_ancien) return *this;

            //save
            AString save=*this;

            //reallocation en enlevant le len de tous les anciens puis en ajoutant
            //le len de tous les nouveaux
            new_len = true_len-(nombre_ancien*len_ancien)+(nombre_ancien*len_nouveau)+1;
            _realloc(new_len);

            //mise à jour du len
            true_len=new_len-1; 
#ifndef RECALC_LEN
            _len=true_len; //CHANGELEN
#endif

            //le contenu...
            const char *str=save.ptr()+index;
            p=_ptr+index;
            
            while(*str) {
                  if((*equal)(str,ancien,len_ancien)) {
                        *p=0; strcat(p,nouveau);
                        //incremente
                        str+=len_ancien;
                        p+=len_nouveau;
                  }
                  else {
                        *p=*str; //met le caractère normalement...
                        //incremente
                        str++; p++;
                  }
            }

            //ne pas oublier le NULL de fin
            *p=0;
      }
      TEST_LEN();
      return *this;
}

AString &AString::replace(size_t index,AString &ancien, AString &nouveau, AString_cmp_func (*equal))
{ return replace(index,ancien.ptr(),nouveau.ptr(),equal); }

AString &AString::replace(AString &ancien, AString &nouveau, AString_cmp_func (*equal))
{ return replace(0,ancien.ptr(),nouveau.ptr(),equal); }

AString &AString::replace(size_t index,AString &ancien, const char *nouveau, AString_cmp_func (*equal))
{ return replace(index,ancien.ptr(),nouveau,equal);}

AString &AString::replace(AString &ancien, const char *nouveau, AString_cmp_func (*equal))
{ return replace(0,ancien.ptr(),nouveau,equal);}

AString &AString::replace(size_t index,const char *ancien, AString &nouveau, AString_cmp_func (*equal))
{ return replace(index,ancien,nouveau.ptr(),equal); }
AString &AString::replace(const char *ancien, AString &nouveau, AString_cmp_func (*equal))
{ return replace(0,ancien,nouveau.ptr(),equal); }

/***********************************************************
 * lire une ligne dans un fichier
 * avec le \n de la fin...
 ***********************************************************/
AString &AString::getline(FILE *handle)
{
      *this="";
      *this << handle;
      return *this;
}

/***********************************************************
 * ltrim/rtrim/trim
 ***********************************************************/
AString &AString::trim(const char *reject)
{
      return rtrim(reject).ltrim(reject);
}

AString &AString::ltrim(const char *reject)
{
      char *p=_ptr;
      size_t i=0;
      static size_t true_len;
      
      /* DEBUG */ TEST_LEN();
      
      //init
      p=_ptr; i=0;

      //cherche là ou il n'y a pas des 'reject'
      while(*p && strchr(reject,*p)) { p++; i++; }

      //calcule le true len
      true_len=length();

      //maintenant il applique le ltrim
      if(i) { 
            true_len-=i; 
#ifndef RECALC_LEN
            _len=true_len; //CHANGELEN
#endif
            memmove(_ptr,p,true_len+1);
      } 

      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::rtrim(const char *reject)
{
      char *p;
      size_t i=0;
      static size_t true_len;

#ifdef TEST_ZERO
      if(!reject) return *this;
#endif

      /* DEBUG */ TEST_LEN();

      true_len = length();
      
      //teste le len qui doit être positif
      //très important pour ce qui suit
      if(!true_len) return *this;
      
      //p va pointer vers la fin de la chaine
      //(avant le zero final)
      p=_ptr+true_len-1;
      
      //applique maintenant le rtrim
      while(p>=_ptr && strchr(reject,*p)) { 
            p--; 
            i++; 
      }

      //maintenant il met un zero pour faire un rtrim rapidement
      if(i) { 
            //met le null
            *(p+1)=0; 
            //il calcule le len selon les données
            true_len-=i; 
#ifndef RECALC_LEN
            _len=true_len; //CHANGELEN
#endif
      }
      
      /* DEBUG */ TEST_LEN();

      return *this;
}

/***********************************************************
 * lcase/ucase
 ***********************************************************/
AString &AString::lcase()
{
      unsigned char *p=(unsigned char*)_ptr;
      /* DEBUG */ TEST_LEN();
      while(*p) {
            *p=(unsigned char)tolower(*p);
            p++;
      }
      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::ucase()
{
      unsigned char *p=(unsigned char *)_ptr;
      /* DEBUG */ TEST_LEN();
      while(*p) {
            *p=(unsigned char)toupper(*p);
            p++;
      }
      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::lcase(size_t debut, size_t len)
{
      unsigned char *p;
      static size_t true_len;
      
      /* DEBUG */ TEST_LEN();

      true_len=length();
      
      if(debut>=true_len) return *this;
      
      p=(unsigned char*)_ptr+debut;

      while(*p && (len--)!=0) {
            *p=(unsigned char)tolower(*p);
            p++;
      }

      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::ucase(size_t debut, size_t len)
{
      unsigned char *p;
      static size_t true_len;

      /* DEBUG */ TEST_LEN();

      true_len=length();
      
      if(debut>=true_len) return *this;
      
      p=(unsigned char*)_ptr+debut;

      while(*p && (len--)!=0) {
            *p=(unsigned char)toupper(*p);
            p++;
      }
      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::ucase(size_t pos)
{ return ucase(pos,1); }

AString &AString::lcase(size_t pos)
{ return lcase(pos,1); }

/***********************************************************
 * substr
 ***********************************************************/
AString &AString::substr(size_t begin, size_t len)
{
      /* DEBUG */ TEST_LEN();

      //si le len est vide alors il faudra simplement
      //vider la chaine!
      if(!len) {
#ifndef RECALC_LEN
            _len=0;  //vider aussi! CHANGELEN
#endif
            *_ptr=0; //vider!
            return *this;
      }

      //var true len
      static size_t true_len;
      true_len = length();

      //teste le begin
      if(begin>=true_len) {
#ifndef RECALC_LEN
            _len=0;  //CHANGELEN
#endif
            *_ptr=0;
            return *this;
      }

      //recal du len
      true_len-=begin;
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif
      
      //applique le substr
      memmove(_ptr,_ptr+begin,true_len+1); //len déjà appliqué précédemment
      
      //quitte la fonc si le len de la division
      //dépasse le vrai len
      //c'est logique, on ne peux dépasser le string
      if(len>true_len) return *this;

      //dans le cas ou le len de la division est 
      //moins grand
      //on va mettre un zero, histoire de limiter
      //la chaine et d'appliquer le substr de cette
      //fonction
      _ptr[len]=0;
#ifndef RECALC_LEN
      _len=len; //CHANGELEN
#endif
      
      /* DEBUG */ TEST_LEN();
      return *this;
}

AString &AString::substr(size_t begin)
{
      static size_t true_len;
      /* DEBUG */ TEST_LEN();

      true_len = length();
      
      if(begin>=true_len) {
#ifndef RECALC_LEN
            _len=0;  //CHANGELEN
#endif
            *_ptr=0;
            return *this;
      }

      //recalcul du len
      true_len-=begin;
#ifndef RECALC_LEN
      _len=true_len; //CHANGELEN
#endif
      
      //applique le substr
      memmove(_ptr,_ptr+begin,true_len+1); //len déjà appliqué précédemment
      
      /* DEBUG */ TEST_LEN();
      return *this;
}

/***********************************************************
 * ==
 ***********************************************************/
bool AString::operator==(AString &str)
{
      return !strcmp(_ptr,str.ptr());
}

bool AString::operator==(const char *str)
{
      return !strcmp(_ptr,str);
}

bool AString::operator!=(const char *str)
{
      return strcmp(_ptr,str);
}

bool AString::operator!=(AString &str)
{
      return strcmp(_ptr,str.ptr());
}

/***********************************************************
 * Duplication...
 *
 * La chaine se duplique elle même d'après son contenu.
 *
 * exemple:
 * s="hello"
 *
 * s*=3;
 *
 * donc s="hellohellohello"
 ***********************************************************/
AString &AString::operator*=(size_t dup)
{
      static size_t l;
      static char *p;
      static size_t true_len;

      /* DEBUG */ TEST_LEN();

      if(!dup) return *this;

      true_len = length();

      //maintenant il va faire un petit calcul
      l = true_len * dup; //le future len
      _realloc(l+1);

      //se positionne après le premier dup
      p = _ptr+true_len;
      
      //applique maintenant le dup
      dup--; //le premier est déjà là!
      while(dup--) {
            //il fait un: 
            memmove(p,_ptr,true_len);
            p+=true_len;
      }
      
      //met à jour le len
      _ptr[true_len = l]=0;
#ifndef RECALC_LEN
      _len=l;
#endif

      /* DEBUG */ TEST_LEN();
      return *this;
}

/***********************************************************
 * [] 
 * pour avoir un caractère de la chaine. 
 ***********************************************************/
char AString::operator[] (size_t p)
{
      if(p>=length()) return 0;
      return _ptr[p];
}

/***********************************************************
 * opérateur =
 ***********************************************************/
AString &AString::operator=(int i)
{
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0; //vider le string
      return (*this)<<i;
}

AString &AString::operator=(long l)
{
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0;
      return (*this)<<l;
}

AString & AString::operator=(const char *str)
{
      if(str==_ptr) return *this;
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0;
      return (*this)<<str;
}

AString &AString::operator=(AString &str)
{
      if(this==&str) return *this;
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0;
      return (*this)<<str;
}

AString &AString::operator=(FILE *handle)
{
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0;
      return (*this)<<handle;
}

AString &AString::operator=(double d)
{
#ifndef RECALC_LEN
      _len=0;
#endif
      *_ptr=0;
      return (*this)<<d;
}

/***********************************************************
 * Infos
 ***********************************************************/
const char *AString::ptr() 
{ return _ptr; }

size_t AString::length()
{ 
#ifndef RECALC_LEN
      return _len; 
#else
      return strlen(_ptr); //le recalc..
#endif
}

size_t AString::size()
{ return length(); }

void AString::_realloc(size_t size, bool perm_inferieur) //perm_inferieur=peut régreser
{
      static char *save;

      //la place mem n'a pas le droit d'être vide.
      if(size<1) size=1;

      // s'il ne peut pas regresser et qu'il y a assez de place
      // alors pas le peine de rester ici!
      if(!perm_inferieur && _realloc_size>=size) return; //pas de realloc au cas ou inutile (Optimiz)

      //mais... sinon... le realloc est très important.
      _ptr=(char *)realloc(save=_ptr,size);
      if(!_ptr) {
            free(save);
            _ptr=0; 
#ifndef RECALC_LEN
            _len=0;
#endif
            _realloc_size=0;
            fprintf(stderr,"Pas assez de memoire...\n");
            perror("realloc");
            exit(1);
      }

      //sinon il applique tout ça!
      _realloc_size=size;
}

/***********************************************************
 * Constructeur/Destructeur
 **********************************************************/
void AString :: _init()
{
#ifndef RECALC_LEN
      _len=0;
#endif
      _realloc_size=0;
      _ptr=0;
      _realloc(1);
      *_ptr=0;
#ifdef DEBUG
      //si c'est le premier appel. alors calculer un ++ pour le DEBUG
      AString_num_alloc++;
#endif
}

AString :: AString(long l)
{
      _init();
      *this<<l;
}

AString :: AString(int i)
{
      _init();
      *this<<i;
}

AString :: AString(double d)
{
      _init();
      *this<<d;
}

AString :: AString(FILE *handle)
{
      _init();
      *this<<handle;
}

AString :: AString(const char *str)
{
#ifdef TEST_ZERO
      if(!str) return;
#endif
      _init();
      *this<<str;
}

AString :: AString(AString &str)
{
      _init();
      *this<<str;
}

AString :: AString()
{
      _init();
}

AString :: ~AString()
{
      free(_ptr);
#ifdef DEBUG
      AString_num_alloc--;
#endif
}

void AString::free_extra()
{
#ifndef RECALC_LEN
      if(_len+1<_realloc_size) _realloc(_len+1,true);
#else
      static size_t len;
      len=length();
      if(len+1<_realloc_size) _realloc(len+1,true);
#endif
}

/***********************************************************
 * AStringList
 *
 * Gestion d'une liste de chaines
 ***********************************************************/

//================================
// Shift
//
// ajouter une liste dans une 
// autre. shift ajoute l'autre 
// liste au début de la première.
//================================
AStringList &AStringList::shift(AStringList &list)
{
      size_t i;
      _realloc(_size+list.size());
      memmove(_list+list.size(),_list,_size*sizeof(AString *));
      _size+=list.size();
      for(i=0;i<list.size();i++) 
                  _list[i]=new AString(list[i]);
      return *this;
}

//ajouter un string au début de la liste!
AStringList &AStringList::shift(const char *str)
{
#ifdef TEST_ZERO
      if(!str) return *this;
#endif
      _realloc(_size+1);
      memmove(_list+1,_list,_size*sizeof(AString *));
      _size++;
      _list[0]=new AString(str);
      return *this;
}

//idem. Mais c'est un AString.
AStringList &AStringList::shift(AString &str)
{
      return shift(str.ptr());
}

//================================
// pop
//================================
void AStringList::operator>>(AString &str)
{
      str= (*this)[size()-1];
      remove(size()-1);
}

//================================
// Push
//================================
AStringList &AStringList::operator<<(AStringList &list)
{ return push(list); }
AStringList &AStringList::operator<<(AString &str)
{ return push(str); }
AStringList &AStringList::operator<<(const char *str)
{ return push(str); }

AStringList &AStringList::push(AStringList &list)
{
      size_t begin=_size;
      if(!list.size()) return *this;
      _realloc(_size+list.size()); //je l'ai mis ici pour éviter segfault
      _size+=list.size();
      for(size_t i=0;i<list.size();i++) 
            _list[begin+i]=new AString(list[i]);
      return *this;
}

AStringList &AStringList::push(AString &str)
{
      return push(str.ptr());
}

AStringList &AStringList::push(const char *str)
{
#ifdef TEST_ZERO
      if(!str) return *this;
#endif
      _realloc(_size+1);
      _list[_size] = new AString(str);
      _size++;
      return *this;
}

//================================
//realloc
//================================
void AStringList::_realloc(size_t new_size)
{
      AString **save;

      //si new size est un zero alors mettre 1
      if(!new_size) new_size=1;
      new_size*=sizeof(AString *);

      //bon on teste si au moins le nouveau size dépasse l'actuel
      if(_realloc_size>=new_size) return; //optimisation...

      //là on commence la réallocation
      _list=(AString **)realloc(save=_list,new_size);

      //on teste s'il y a eu erreur...
      if(!_list) {
            free(save);
            perror("realloc");
            exit(1);
      }

      //on sauvegarde le nouveau size afin qu'il soit pris en compte ultérieurement
      _realloc_size=new_size;
}

//================================
// FREE EXTRA
// au cas ou l'on fait un
// super gros remove
// de 1000 par exemple
// on peux faire un free_extra 
// afin de libérer
//================================
void AStringList::free_extra()
{
      if(_size<_realloc_size) _realloc(_size);
}

//================================
// Constr/Destruct
//================================
AStringList::AStringList(AStringList &list)
{
      _list=0; _size=0;
      _realloc_size=0;
      push(list);
      //une petite fonction de debug
      //elle permet de tester s'il y a eu une allocation ou désallocation...
#ifdef DEBUG
      AStringList_num_alloc++;
#endif

}

AStringList::AStringList()
{
      _list=0;
      _size=0;
      _realloc_size=0;
      //une petite fonction de debug
      //elle permet de tester s'il y a eu une allocation ou désallocation...
#ifdef DEBUG
      AStringList_num_alloc++;
#endif

}

AStringList::~AStringList()
{
      if(_list) {
            for(size_t i=0;i<_size;i++) delete _list[i];
            free(_list);
            _list=0;
      }
      _size=0;
      _realloc_size=0;

#ifdef DEBUG
      AStringList_num_alloc--;
#endif
}

//infos
size_t AStringList::size()
{ return _size; }

//ref
//si dépassement alors retourne le dernier
//si aucun string alors retourne le temp vide
AString &AStringList::operator[](size_t index)
{
      //si dépassement alors il retourne toujours le dernier element
      if(index>=_size) index=_size-1;
      //retourne le string
      return *_list[index];
}

//================================
// move permet de déplacer
// un élément dans pos1 vers
// pos2
//
// ex:
//
// el1 el2 el3 fin
//  0   1   2   3
//  
// si move(1,3)
//
// el1     el3 [ el2 ] fin
//================================
AStringList &AStringList::move(size_t pos1, size_t pos2)
{
      AString *p;
      size_t len;

      //le premier test qui limite pos1 et pos2
      if(pos1>_size) pos1=_size;
      if(pos2>_size) pos2=_size;

      //pos1 doit à tout prix être < pos2
      //sinon swap!
      if(pos1>pos2) {
            //un deuxième test pour la fin
            if(pos1==_size) pos1=_size-1;

            //calcule le len
            len=pos1-pos2;
            if(!len) return *this; //au moins un!

            p=_list[pos1];
            memmove(_list+pos2+1,_list+pos2,(len)*sizeof(AString *)); //-1 pour l'inserer avant la pos
            _list[pos2]=p;
      }
      //si pos1<pos2
      else {
            //test pour la fin
            if(pos1==_size) pos1=_size-1;

            //calcule le len entre pos1 et pos2
            len=pos2-pos1;
            if(!len) return *this; //au moins un!
            
            //fait un petit move!
            p=_list[pos1];
            memmove(_list+pos1,_list+pos1+1,(len-1)*sizeof(AString *)); //-1 pour l'inserer avant la pos
            _list[pos2-1]=p;
      }

      return *this;
}

AStringList &AStringList::swap(size_t pos1, size_t pos2)
{
      AString *p;
      if(pos1>=_size) return *this;
      if(pos2>=_size) return *this;
      p=_list[pos1];
      _list[pos1]=_list[pos2];
      _list[pos2]=p;
      return *this;
}

//================================
// suppression
//================================
AStringList &AStringList::remove(size_t index, size_t len)
{
      size_t i;
      size_t end=index+len;

      //dans le cas ou index dépasse size
      if(index>=size()) return *this;

      //dans le cas ou end (index+len) dépasse size
      if(end>size()) {
            end=size();
            len=end-index;
      }

      //delete...
      for(i=index;i<end;i++) 
            delete _list[i];

      //resize...
      memmove(_list+index,_list+index+len,(_size-index-len)*sizeof(AString *));
      _size-=len;
      return *this;
}

AStringList &AStringList::remove(size_t index)
{
      if(index>=_size) return *this;
      delete _list[index];
      memmove(_list+index,_list+index+1,(_size-index-1)*sizeof(AString *));
      _size--;
      return *this;
}

//================================
// Insertion
//================================
AStringList &AStringList::insert(size_t index, AStringList &list)
{
      size_t i;
      //ça dépasse la fin?
      //alors ajuste ça à la fin
      if(index>_size) index=_size;

      //si c'est la même liste
      if(&list==this) {
            //il va faire une copie
            AString **save_list = (AString **)malloc(sizeof(AString *)*list.size());
            if(!save_list) {
                  perror("malloc");
                  exit(1);
            }

            //fait une petite copie
            memcpy(save_list,_list,sizeof(AString *)*list.size());

            _realloc(_size+list.size()); 
            memmove(_list+index+list.size(),_list+index, (_size-index)*sizeof(AString *));

            //ajoute les strings dans une boucle
            for(i=0;i<list.size();i++) 
                  _list[i+index]=new AString(save_list[i]->ptr());

            //inc le size
            _size+=list.size();

            //chose à ne pas oublier!
            free(save_list);
      }
      //si c'est pas la même liste
      else {
            _realloc(_size+list.size()); 
            memmove(_list+index+list.size(),_list+index, (_size-index)*sizeof(AString *));
            _size+=list.size();

            //ajoute les strings dans une boucle
            for(i=0;i<list.size();i++) 
                  _list[i+index]=new AString(list[i].ptr());
      }

      return *this;
}

AStringList &AStringList::insert(size_t index, const char *str)
{
      if(index>_size) return *this;
      _realloc(_size+1); 
      memmove(_list+index+1,_list+index,(_size-index)*sizeof(AString *));
      _size++;
      _list[index]=new AString(str);
      return *this;
}

AStringList &AStringList::insert(size_t index, AString &str)
{ return insert(index,str.ptr()); }

//================================
// Vider la liste!
// delete toutes les chaines
// et _size=0.
// mais ne pas realloc (rapidité
// de la _list... il faut faire
// free_extra
//================================
AStringList &AStringList::clear()
{
      static size_t i;
      for(i=0;i<_size;i++) 
                  delete _list[i];
      _size=0;
      return *this;
}

//================================
// dup la liste
//================================
void AStringList::operator*=(size_t dup)
{
      size_t init_size=_size;
      static size_t i,j;
      // *0 alors vider!
      if(!dup) { clear(); return; }

      //pour faire multiplication il faut enlever le premier!
      dup--; 

      //refaire l'option 'dup' fois...
      for(i=0;i<dup;i++) {
            //reparcourt la liste plusieurs fois...
            for(j=0;j<init_size;j++) 
                  *this << *_list[j];
      }
}


//================================
//Assignation
//================================
AStringList &AStringList::copy(AStringList &new_astringlist)
{
      if(&new_astringlist==this) return *this;
      clear();
      *this<<new_astringlist;
      return *this;
}

//================================
// IO dans FILE *
//
//
// true si erreur
//================================

//false si error load/save
bool AStringList::load(const char *filename)
{
      FILE *handle = fopen(filename,"r");
      if(!handle) return true;
      *this << handle;
      fclose(handle);
      return false;
}

bool AStringList::save(const char *filename)
{
      FILE *handle = fopen(filename,"w");
      if(!handle) return true;
      *this >> handle;
      fclose(handle);
      return false;
}

bool AStringList::save(AString &filename)
{ return save(filename.ptr()); }

bool AStringList::load(AString &filename)
{ return save(filename.ptr()); }

AStringList &AStringList::operator<<(FILE *handle)
{
      if(!handle) return *this;
      while(!feof(handle)) {
            *this << ""; //ajoute à la liste
            ((*this)[size()-1]<<handle).chomp();
      }
      return *this;
}

AStringList &AStringList::operator>>(FILE *handle)
{
      size_t s=0;
      if(!handle) return *this;
      while(s<_size) {
            (*this)[s] >> handle;
            fputs("\n",handle);
            s++;
      }
      return *this;
}


Generated by  Doxygen 1.6.0   Back to index