/****************************************************
* unit:    strings           release 0.30           *
* purpose: general manipulation with text strings   *
* Licency: GPL or LGPL                              *
* Copyright: (c) 1998-2025 Jaroslav Fojtik          *
*****************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "stringa.h"


/// Allocation tunning.
/// The optimal value on AMD Win32 seems to be 2
/// From benchmark ... Addition loop time: 1:1.049294us 2:0.946128us 4:0.954430us
#ifndef ALLIGN_ALLOC
 #define ALLIGN_ALLOC 2
#endif


#ifdef ALLIGN_ALLOC
inline size_t FIX_ALLOC(const size_t size)
{
#if ALLIGN_ALLOC<=1
  return size;
#else
 #if ALLIGN_ALLOC==2
  return (size+1) & ~1;
#else
 #if ALLIGN_ALLOC==4
  return (size+3) & ~3;
 #else
  return (size+7) & ~7;
 #endif
#endif
#endif
}
#else
 #define FIX_ALLOC(x) (x)
#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;
}


/// Allocate empty string with len+1 bytes.
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);
}


#ifdef USE_TEMP_STRINGS
/// Create a new string and steal the content of the donor.
string::string(temp_string &s)
{
 ch=s.ch; size=s.size; maxlen=s.maxlen;
 s.ch=NULL; s.size=s.maxlen=0;
}


/// Create a new string and steal the content of the donor.
/// Use pointer is tricky. You have function say:
/// temp_string GetString(void)
///......
/// string result_fast(&GetString());	// This will extract contents
/// string result_slow(GetString());	// This will duplicate temporary object
string::string(temp_string *s)
{
 ch=s->ch; size=s->size; maxlen=s->maxlen;
 s->ch=NULL; s->size=s->maxlen=0;
}
#endif


/// Allocates 'i+1' bytes and copy maximally i characters from str.
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--;
 memcpy(ch,str,i);
 ch[i]=0;
 size=strlen(ch);
}

/** Resizes string buffer and preservesd contents when possible. */
void string::resize(unsigned int NewMaxLen)
{
 if(maxlen>=NewMaxLen) return;
 if(ch!=NULL)
   {
   if(maxlen>0) 
     {
     void *cp_ch = realloc(ch,NewMaxLen+1);
     if(cp_ch==NULL) 
     {
       RaiseError(StringsId|No_Memory,this); //Memory Exhausted
       free(ch);		// 'maxlen' will be cleared at the end.
     }
     ch = (char *)cp_ch;
     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 =

/// Operator 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 could be 0 only here
	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);
}


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

 if((len=::StrLen(str))==0)
	{
	if(maxlen>0)
        {
          if(ch==NULL) maxlen=0;
                 else *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);
}


/// Operator for filling string from string
string &string::operator=(const string & s)
{
 if(this==&s) return *this;	// check for self assignment
 if(s.ch==NULL)
	{
	if(maxlen>0)
          {
          if(ch==NULL) maxlen=0;
                 else *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(temp_string(s1.ch,s1.size));
 if(s1.size==0) return(temp_string(s2.ch,s2.size));

 string tmp(s1.size+s2.size);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return temp_string();		//no memory, zero or wrong size
	}
 if(((long)s1.size+(long)s2.size)>(long)tmp.maxlen)
	{
	return(temp_string(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(temp_string(tmp));
}


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

 string tmp(s1.size+len);
 if(tmp.ch==NULL || ((long)s1.size+(long)len)>(long)tmp.maxlen)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return temp_string();	//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(temp_string(tmp));
}

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

 string tmp(s2.size+len);
 if(tmp.ch==NULL || ((long)s2.size+(long)len)>(long)tmp.maxlen)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return temp_string();	//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(temp_string(tmp));
}


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

 string tmp(s.size+1);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return temp_string();	//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(temp_string(tmp));
}


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

 string tmp(s.size+1);
 if(tmp.ch==NULL)
	{
	RaiseError(StringsId|No_Memory,&tmp);
	return temp_string();	//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(temp_string(tmp));
}


string & append(string & s1, const char *str)
{
if(str!=NULL)
  {
  unsigned StrSize=strlen(str);
  if(StrSize>0)
    {
    unsigned NewSize = s1.size + StrSize;
    s1.resize(NewSize);
    if(s1.ch==NULL || 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)	// Check for int overflow.
	{
	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)
{
 string tmp(n*length(s));
 if(tmp.ch==NULL) return temp_string();	    //memory exhausted or n==0

 while(n-->0)
	tmp += s;
 return temp_string(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(size==maxlen && c!=0)
     ret = StringsId | Unterminated;
return(ret);
}



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

#ifdef USE_TEMP_STRINGS
temp_string::temp_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;
}


temp_string::temp_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);
}


/// Allocates 'i+1' bytes and copy maximally i characters from str.
temp_string::temp_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);
}


string &string::operator=(temp_string &s)
{
 if(this != (string*)&s)		// Do not assign to self.
 {
   if(s.size==0)
   {
     size = 0;
     if(maxlen>0) *ch=0;
     return(*this);
   }
   if(ch)
     if(maxlen>0 || size==0) free(ch);
   ch=s.ch; size=s.size; maxlen=s.maxlen;
   s.ch=NULL; s.size=s.maxlen=0;
 }
return(*this);
}


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

#endif

////////////////////////////////////////////////////////////////
//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;
string s(len);

 buff = s();
 if(buff!=NULL)
   {
   while(*str)
	{
	*buff++ = toupper((unsigned char)*str++);
	}
   s.size = len;
   s.ch[len] = 0;
   }
return(temp_string(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;
string s(len);

  buff = s();
  if(buff!=NULL)
    {
    while(*str)
	{
	*buff++ = tolower((unsigned char)*str++);
	}
    s.size = len;
    s.ch[len] = 0;
    }
return(temp_string(s));
}


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

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


temp_string copy(const char *str, int from, int len)
{
 if(str==NULL || *str==0) return temp_string();
 int str_len = strlen(str);
 if(from>=str_len) return temp_string();

return temp_string(str+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();
	}

 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(temp_string(tmp));
}


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

 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(temp_string(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(temp_string(s.ch,s.size));

 ptr = s();
 newptr = StrStr(ptr,substring());
 if(newptr==NULL) return(temp_string(s.ch,s.size));

 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(temp_string(tmp));
}


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

 if(substring==NULL) return(temp_string(s.ch,s.size));
 substrlen = strlen(substring);
 if(substrlen==0 || length(s)<substrlen) return(temp_string(s.ch,s.size));

 ptr = s();
 newptr = StrStr(ptr,substring);
 if(newptr==NULL) return(temp_string(s.ch,s.size));

 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(temp_string(tmp));
}


/** Cut all leading and trailing spaces on a current string instance. */
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);
}


/** Cut all leading and trailing spaces on a given string instance. */
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));
}


#include <stdarg.h>
#include <stdio.h>
int string::printf(const char* format, ...)
{
  va_list paramList;
  va_start(paramList, format);
#ifdef _HAVE_VSNPRINTF
  size = vsnprintf(0, 0, format, paramList);
#else
  size = vsprintf(NULL, format, paramList);
#endif
  va_end(paramList);

  if(size>0)
    {
    resize(size+1);
    va_start(paramList, format);
#ifdef _HAVE_VSNPRINTF
    size = vsnprintf(ch, maxlen, format, paramList);
#else
    size = vsprintf(ch, format, paramList);
#endif
    va_end(paramList);
    }
  else
    erase();

  return size;
}


int string::cat_printf(const char* format, ...)
{
  va_list paramList;
  va_start(paramList, format);
#ifdef _HAVE_VSNPRINTF
  int size2 = vsnprintf(0, 0, format, paramList);
#else
  int size2 = vsprintf(NULL, format, paramList);
#endif
  va_end(paramList);

  if(size2>0)
    {
    resize(size+size2+1);	// Keep one BYTE place for trailing 0.
    va_start(paramList, format);
#ifdef _HAVE_VSNPRINTF
    size2 = vsnprintf(ch+size, size2+1, format, paramList);
#else
    size2 = vsprintf(ch+size, format, paramList);
#endif
    va_end(paramList);
    size += size2;
    }

  return size2;
}


#ifdef Streams

#ifdef IOSTREAM_H_ONLY
ostream &operator<<(ostream & xout, const string &s)
#else
std::ostream &operator<<(std::ostream & xout, const string &s)
#endif
{
 return xout<<s();
}

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