/*
 *   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;
}

