/**************************************************
* unit:    strings           release 0.16         *
* purpose: general manipulation with text strings *
***************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef Streams
 #include <iostream.h>
#endif

#include <strings.h>


#define ALLIGN_ALLOC 2


#ifdef ALLIGN_ALLOC
inline size_t FIX_ALLOC(size_t size)
{
#if ALLIGN_ALLOC==2
  return (size+1) & ~1;
#else
 #if ALLIGN_ALLOC==4
  return (size+3) & ~3;
 #else
  return (size+7) & ~7;
 #endif
#endif
}
#else
 #define FIX_ALLOC(x) (x)
#endif


#ifndef ERROR_HANDLER
  #define RaiseError(ErrNo,Instance)	{}; /**/
#else
  void RaiseError(int ErrNo,void *Instance);
#endif

char string::Dummy=0;


//This function compares one char with string
int ChStrCmp(char c, const string & s)  //-1  c<s  0 c==s  1 c>s
{
 if(s.ch==NULL)
	{
	if(c==0) return(0);
	return(1);
	}
 if(c>*s.ch) return(1);
 if(c<*s.ch || s.size>1) return(-1);
 return(0);
}




//Constructors Of string class
//string::string(void)
//{
// ch=NULL;
// size=maxlen=0;
//}

string::string(char c)
{
 maxlen = FIX_ALLOC(2);
 if((ch=(char *)malloc(maxlen))==NULL)
	{
	maxlen = size = 0;
	RaiseError(StringsId|No_Memory,this); //Memory Exhausted
        return;
	}
 maxlen--;
 size=1;
 *ch=c;
 ch[1]=0;
}


string::string(unsigned len)
{
 maxlen = FIX_ALLOC(len+1);
 if( (ch=(char *)malloc(maxlen))==NULL )
   {
   maxlen = 0;
   RaiseError(StringsId|No_Memory,this); //Memory Exhausted
   }
 else 
   { 
   maxlen--;
   *ch = 0;
   }
 size=0;
}


string::string(const char *str)
{
 if(str==NULL)
            {
FastExit:   ch=NULL;
FastExit2:  size=maxlen=0;
            return;
            }

 if((size=strlen(str))==0) goto FastExit;
 maxlen = FIX_ALLOC(size+1);
 if((ch=(char *)malloc(maxlen))==NULL)
	      {
	      RaiseError(StringsId|No_Memory,this); //Memory Exhausted
              goto FastExit2;
              }
 maxlen--;
 memcpy(ch,str,size+1);	//strncpy(ch,str,maxlen+1);
}


string::string(const string &s)
{
 if(s.size==0)
	{
	ch=NULL;
	goto FastExit;
	}

 maxlen = FIX_ALLOC(s.size+1);
 if((ch=(char *)malloc(maxlen))==NULL)
	 {
	 RaiseError(StringsId|No_Memory,this); //Memory Exhausted
FastExit:size = maxlen = 0;
	 return;
	 }
 maxlen--;
 size = s.size;
 memcpy(ch,s.ch,size+1);	//strncpy(ch,s.ch,maxlen+1);
}


string::string(temp_string &s)
{
string *ps=&s;
 ch=ps->ch;size=ps->size;maxlen=ps->maxlen;
 ps->ch=NULL;ps->size=ps->maxlen=0;
}


string::string(const char *str,int i)
{
 maxlen = FIX_ALLOC(i+1);
 if((ch=(char *)malloc(maxlen))==NULL)
	{
	size = maxlen = 0;
	RaiseError(StringsId|No_Memory,this); //Memory Exhausted
        return;
        }
 maxlen--;
 ch[i]=0;
 strncpy(ch,str,i);
 size=strlen(ch);
}


void string::resize(unsigned int NewMaxLen)
{
 if(maxlen>=NewMaxLen) return;
 if(ch!=NULL)
 	{
        if(maxlen>0) {
		      ch=(char *)realloc(ch,NewMaxLen+1);
		      maxlen=NewMaxLen;
		      }
		 else {
		      if(size>0) ;//halt(1); //nevlastni retezce zatim neumim
			   else { //strange string has only 1 allocated byte for 0
				free(ch);
				ch=NULL;
				}
		      }
	}
 if(ch==NULL)
	{
	maxlen = FIX_ALLOC(NewMaxLen+1);
	ch = (char *)malloc(maxlen);
	if(ch!=NULL)
		{*ch=0; maxlen--;}		
	}
 if(ch==NULL)
	{
	size = maxlen = 0;
	if(NewMaxLen>0) RaiseError(StringsId|No_Memory,this); //Memory Exhausted
	}
 return;
}



//Destructor Of class string
//string::~string(void)
//{
// if(ch!=NULL)
//   if (maxlen>0)||(len==0) free(ch);
// ch=NULL;
// size=maxlen=0;
//}


//-------------------------------------
//Family of Operator =

//Oparator for filling string from char
string &string::operator=(char c)
{
if(c==0)
      {
      if(ch!=NULL) *ch=0;
      size=0;
      return(*this);
      }

if(maxlen<1)
	{
	if(maxlen!=0) free(ch);
	maxlen = FIX_ALLOC(2);
	ch = (char *)malloc(maxlen);
	maxlen--;
	}

if(ch!=NULL)
	{
	size=1;
	ch[0]=c;
	ch[1]=0;
	return(*this);
	}

maxlen=size=0;
RaiseError(StringsId|No_Memory,this); //Memory Exhausted
return(*this);
}


string &string::operator=(long l)
{
char str[12];
 sprintf(str,"%ld",l);
 return(*this=str);
}


string &string::operator=(double d)
{
char str[24];
 sprintf(str,"%g",d);
 return(*this=str);
}


//Oparator for filling string from char *
string &string::operator=(const char *str)
{
int len;

 if((len=StrLen(str))==0)
	{
	if(maxlen>0) *ch=0;
	size=0;
	return(*this);
	}
 if(len>=maxlen)
	{
	if(maxlen!=0) free(ch);
	maxlen = FIX_ALLOC(len+1);
	ch = (char *)malloc(maxlen);
	maxlen--;
	}
 if(ch!=NULL)
	{
	size = len;
	memcpy(ch,str,len+1); //strcpy(ch,str);
	return(*this);
	}

 size=maxlen=0;
 RaiseError(StringsId|No_Memory,this); //Memory Exhausted
return(*this);
}


//Oparator for filling string from string
string &string::operator=(const string & s)
{
 if(s.ch==NULL)
	{
	if(maxlen>0) *ch=0;
Empty:	size = 0;
	return(*this);
	}
 if(s.ch==ch) return(*this); //same pointers

 if(s.size>maxlen)
	{
	if(maxlen!=0) free(ch);

	maxlen = FIX_ALLOC(s.size+1);
	if( (ch=(char *)malloc(maxlen))==NULL )
	   {
	   maxlen = 0;
	   RaiseError(StringsId|No_Memory,this); //Memory Exhausted
	   goto Empty;
	   }
	maxlen--;	
	}

 size = s.size;
 if(ch!=NULL) memcpy(ch,s.ch,size+1);

return(*this);
}



//Receiving one character from String
//inline char string::operator[](const int i) const
//{
// if(i>=size) return(0);
// return(ch[i]);
//}

//Looking for substring in the String
//char *string::operator IN(const string &s) const
//{
// if(s.ch==NULL) return(NULL);
// return(strstr(s.ch, ch));
//}


//--- family of     Addind Strings   -------
temp_string operator+(const string & s1, const string & s2)
{
 if(s2.size==0) return(s1);
 if(s1.size==0) return(s2);

 temp_string tmp(s1.size+s2.size);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return(tmp);	//no memory, zero or wrong size
	}
 if(((long)s1.size+(long)s2.size)>(long)tmp.maxlen)
	{
	return(tmp);	//nasty int overflow
	}

 memcpy(tmp.ch,s1.ch,s1.size);
 memcpy(tmp.ch+s1.size,s2.ch,s2.size+1);
 tmp.size = s1.size + s2.size;
return(tmp);
}


temp_string operator+(const string & s1, const char *str)
{
unsigned len;
 if(str==NULL) return(s1);
 len=strlen(str);	//NULL is fixed above
 if(len==0) return(s1);

 temp_string tmp(s1.size+len);
 if(tmp.ch==NULL || ((long)s1.size+(long)len)>(long)tmp.maxlen)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return(tmp);	//no memory, zero or wrong size
	}

 if(s1.size>0) memcpy(tmp.ch,s1.ch,s1.size);
 memcpy(tmp.ch+s1.size,str,len+1);
 tmp.size = s1.size+len;
return(tmp);
}

temp_string operator+ (const char *str, const string & s2)
{
unsigned len;
 if(str==NULL) return(s2);
 len=strlen(str);
 if(len==0) return(s2);
 if(s2.size<=0) return(str);

 temp_string tmp(s2.size+len);
 if(tmp.ch==NULL || ((long)s2.size+(long)len)>(long)tmp.maxlen)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return(tmp);	//no memory, zero or wrong size
	}

 memcpy(tmp.ch,str,len+1);
 memcpy(tmp.ch+len,s2.ch,s2.size+1);
 tmp.size = len + s2.size;
 return(tmp);
}


temp_string operator+ (const string & s, char c)
{
 if(s.size==0) return(temp_string(c));

 temp_string tmp(s.size+1);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return(tmp);	//no memory or zero size
	}

 memcpy(tmp.ch,s.ch,s.size);
 tmp.ch[s.size]=c;
 tmp.size = s.size+1;
 tmp.ch[tmp.size]=0;

 return(tmp);
}


temp_string operator+ (char c, const string & s)
{
 if(c==0) return(s);
 if(length(s)==0) return(temp_string(c));

 temp_string tmp(s.size+1);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return(tmp);	//no memory or zero size
	}

 tmp.ch[0]=c;
 tmp.ch[1]=0;
 tmp.size = s.size+1;
 memcpy(tmp.ch+1,s.ch,tmp.size);
 return(tmp);
}


string & append(string & s1, const char *str)
{
if(str!=NULL)
  {
  unsigned StrSize=strlen(str);
  if(StrSize>0)
    {
    long NewSize=s1.size+StrSize;
    s1.resize(NewSize);
    if(s1.ch==NULL || (long)s1.maxlen<NewSize)
	{
	RaiseError(StringsId|IntOverflow,&s1);	//Size overflow
	return(s1);	//overflow!!!
	}
    memmove(s1.ch+s1.size,str,StrSize+1); //memmove is faster than: strcat(s1.ch,str);
    s1.size+=StrSize;
    }
  }
return(s1);
}


string & append(string & s1, const string & s2)
{
if(s2.size>0)
  {
  s1.resize(s1.size+s2.size);
  if(s1.ch==NULL)
	{
	RaiseError(StringsId|IntOverflow,&s1);	//Size overflow
	return(s1);	//overflow!!!
	}
  memmove(s1.ch+s1.size,s2.ch,s2.size+1);
  s1.size+=s2.size;
  }
return(s1);
}


string & append(string & s1, char c)
{
if(c!=0)
  {
  if(s1.size+1<s1.size)
	{
	RaiseError(StringsId|IntOverflow,&s1);	//Size overflow
	return(s1);	//overflow!!!
	}
  if(s1.size+1>s1.maxlen)
	{
	s1.resize(s1.size+1);
	}
  if(s1.ch!=NULL)
	{
	s1.ch[s1.size]=c;
	s1.ch[++s1.size]=0;
	}
  }
return(s1);
}

string & append(string & s1, long l)
{
char str[12];
 sprintf(str,"%ld",l);
 return append(s1,str);
}


string & append(string & s1, double d)
{
char str[24];
 sprintf(str,"%g",d);
 return append(s1,str);
}



temp_string multiply(const string& s, unsigned n)
{
 temp_string tmp(n*length(s));
 if(tmp.ch==NULL) return(tmp);	    //memory exhausted or n==0

 while(n-->0)
	tmp += s;
 return tmp;
}



//-----   Miscelaneous functions -----
void string::erase(void)
{
 if(ch!=NULL)
   {    
   if ((maxlen>0)||(size==0)) free(ch);
   ch=NULL;
   }
 size=maxlen=0;
}


/**this procedure checks consistency of the variable string*/
int string::check(void)
{
int ret=0;
char c;
unsigned int Length;

if(ch==NULL)
	{
	if(size!=0)   ret=StringsId | Bad_Length;
	if(maxlen!=0) ret=StringsId | Bad_MaxLen;
	size=maxlen=0;
        return(ret);
        }

if(size>maxlen)
	{
        ret=StringsId | Wrong_String;
	size=maxlen;
        }

c=ch[maxlen];
Length=size;

ch[maxlen]=0;
if((size=StrLen(ch))!=Length)
  ret = StringsId | Bad_Length;
if((c!=0)&&(size==maxlen))
  ret = StringsId | Unterminated;
return(ret);
}



///////////Speedup class temp_string////////////////

string &string::operator=(temp_string &s)
{
string *ps=&s;
 if(ps->size==0)
	{
	size=0;
	if(maxlen>0) *ch=0;
	return(*this);
	}
 if(ch)
   if ((maxlen>0)||(size==0)) free(ch);
 ch=ps->ch;size=ps->size;maxlen=ps->maxlen;
 ps->ch=NULL;ps->size=ps->maxlen=0;
return(*this);
}


////////////////////////////////////////////////////////////////
//Related procedures for class String

string & string::ToUpper(void)
{
char *str;
if(ch!=NULL)
  for(str=ch;*str!=0;str++)
	{
	*str = toupper((unsigned char)*str);
	}
return(*this);
}


temp_string ToUpper(const char *str)
{
unsigned int len=StrLen(str);
char *buff;
temp_string s(len);

 buff=s();
 if(buff!=NULL)
   {
   while(*str)
	{
	*buff++ = toupper((unsigned char)*str++);
	}
   s.SetSize(len);
   }
return(s);
}



string & string::ToLower(void)
{
char *str;
if(ch!=NULL)
  for(str=ch;*str!=0;str++)
	{
	*str = tolower((unsigned char)*str);
	}
return(*this);
}


temp_string ToLower(const char *str)
{
unsigned int len=StrLen(str);
char *buff;
temp_string s(len);

  buff=s();
  if(buff!=NULL)
    {
    while(*str)
	{
	*buff++ = tolower((unsigned char)*str++);
	}
    s.SetSize(len);
    }
return(s);
}



temp_string copy(const string &s,int from, int len)
{
if((from>length(s)) || (len<=0) || (from<0) || (length(s)==0))
	{
	return temp_string();
	}

return temp_string(s()+from,len);
}


temp_string del(const string &s,int from, int len)
{
if(length(s)<from+len) len=length(s)-from;

if((from>=length(s))||(len<0)||(from<0)||(length(s)==0))
	{
	return temp_string();
	}

temp_string tmp((unsigned)(length(s)-len));
memmove(tmp.ch,s.ch,from);
memmove(tmp.ch+from,s.ch+from+len,length(s)-from-len+1);
tmp.size=length(s)-len;
return(tmp);
}


temp_string insert(const string &s,const string &s2,int position)
{
if((position>length(s))||(position<0))
	{
	return temp_string();
	}
if(length(s)==0) return(s2);
if(length(s2)==0) return(s);

temp_string tmp((unsigned)(length(s)+length(s2)));
memmove(tmp.ch,s.ch,position);
memmove(tmp.ch+position,s2.ch,length(s2));
memmove(tmp.ch+position+length(s2),s.ch+position,length(s)-position+1);
tmp.size=length(s)+length(s2);
return(tmp);
}



/* This is higher level procedure for replacing one substring to another
   substring */
temp_string replacesubstring(const string & s, const string & substring, const string & newsubstring )
{
char *newptr,*ptr;
char c;

 if(substring=="" || length(s)<length(substring)) return(s);

 ptr=s();
 newptr=StrStr(ptr,substring());
 if(newptr==NULL) return(s);

 temp_string tmp;
 do {
    c=*newptr;
    *newptr=0;

    tmp.resize(length(tmp)+length(newsubstring)+(newptr-ptr));
    tmp+=ptr;
    tmp+=newsubstring;

    *newptr=c;
    ptr=newptr+length(substring);
    } while((newptr=StrStr(ptr,substring())) != NULL);
 tmp += ptr;

return(tmp);
}


temp_string replacesubstring(const string & s, const char *substring, const char *newsubstring )
{
char *newptr,*ptr;
char c;
int substrlen;

 if(substring==NULL) return(s);
 substrlen=strlen(substring);
 if(substrlen==0 || length(s)<substrlen) return(s);

 ptr=s();
 newptr=StrStr(ptr,substring);
 if(newptr==NULL) return(s);

 temp_string tmp;
 do {
    c=*newptr;
    *newptr=0;

    tmp.resize(length(tmp)+substrlen+(newptr-ptr));
    tmp+=ptr;
    tmp+=newsubstring;

    *newptr=c;
    ptr=newptr+substrlen;
    } while((newptr=StrStr(ptr,substring)) != NULL);
 tmp += ptr;

return(tmp);
}


string & string::trim(void)
{
char *From,*To;

 if(size==0 || ch==NULL) return *this;

 From=ch;
 To=From+size-1;

 while(isspace(*From))
	{
	From++;
	if(*From==0)
	   {
	   size=0;
	   *ch=0;
	   return(*this);
	   }
	}

 while(isspace(*To) && (To>From))
	{
	*To--=0;
	}

 if(From>ch)
	{
	memmove(ch,From,To-From+2);
	//size=strlen(ch);
	}
 size=To-From+1;

return(*this);
}



temp_string trim(const string & s)
{
char *From,*To;

 From=s();
 if(length(s)==0 || From==NULL) return temp_string();
 To=From+length(s)-1;

 while(isspace(*From))
	{
	From++;
	if(*From==0)
		return temp_string();
	}

 while(isspace(*To) && (To>From))
	{
	To--;
	}

return(temp_string(From,To-From+1));
}


#ifdef Streams
ostream &operator<<(ostream & xout, const string &s)
{
 return xout<<s();
}

/*istream &operator>>(istream &, string &)
{
} */
#endif
