/*--------------------------------------------------------------------*/
/*       U s t r . c p p                                              */
/*                                                                    */
/*       Implementation of "Str" class                                */
/*--------------------------------------------------------------------*/
/*       Copyright (c) 2002 by Fyodor Ustinov                         */
/*                             FIDONet 2:5020/79                      */
/*                                                                    */
/*       All rights reserved.                                         */
/*--------------------------------------------------------------------*/

// #include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include "ustr.hpp"

#define CHUNK_SIZE 512

/*--------------------------------------------------------------------*/
/*                Constructors & destructor                           */
/*--------------------------------------------------------------------*/

Str::Str() {
   MemLen = 0;
   Len = 0;
   Ptr = NULL;
   _Base = 10;
   alloc(1);
}

Str::Str(char const *Nstr) {
   MemLen = 0;
   _Base = 10;
   Ptr = NULL;
   if (Nstr == NULL) {
      Len = 0;
      alloc(1);
   } else {
      Len = strlen(Nstr);
      alloc(Len+1);
      strncpy(Ptr,Nstr,Len);
   }
}

Str::Str(const Str &s) {
   Len = s.Len;
   _Base = 10;
   MemLen = 0;
   Ptr = NULL;
   alloc(Len+1);
   strncpy(Ptr,s.Ptr,Len+1);
}

Str & Str::operator=(const Str &s) {
   if (&s != this) {
      free();
      alloc(s.Len+1);
      Len = s.Len;
      memcpy(Ptr,s.Ptr,Len);
   }
   return *this;
}

Str & Str::operator=(const char *s) {

   free();
   alloc(1);
   if (s == NULL) return *this;
   if (*s == '\0') return *this;
   Len = strlen(s);
   alloc(Len+1);
   strncpy(Ptr,s,Len);
   return *this;
}

Str & Str::operator=(const char c) {

   free();
   alloc(1);
   *Ptr = c;
   Len = 1;
   return *this;
}


Str::~Str() {
   free();
}

/*--------------------------------------------------------------------*/
/*                Allocate and free memory                            */
/*--------------------------------------------------------------------*/

void Str::alloc(size_t sz) {
char *tmt;
size_t rsz;

   if (sz == 0) throw "Str: Attempt to allocate 0 bytes";
// cout << "MemLen == " << MemLen << " sz == " << sz << "\n";

   if (MemLen != 0 && sz < MemLen && sz >= MemLen-CHUNK_SIZE) return;
   tmt = Ptr;
   rsz = (((sz / CHUNK_SIZE) + 1) * CHUNK_SIZE) + 1;
   Ptr = (char *) malloc(rsz);
   if (Ptr == NULL) throw "Str: Unable to allocate memory";

   if (tmt != NULL) {
      if (rsz > MemLen) {
         memmove(Ptr,tmt,MemLen);
      } else {
         memmove(Ptr,tmt,rsz);
         Len = rsz-1;
      }
      ::free(tmt);
   }   
   MemLen = rsz;
// cout << "at end MemLen == " << MemLen << " Len == " << Len << "\n";
}

void Str::free(void) {
   if (Ptr != NULL) {
      ::free(Ptr);
      Ptr = NULL;
      Len = 0;
      MemLen = 0;
   }
}
/*--------------------------------------------------------------------*/
/*                Return pointers to c string                         */
/*--------------------------------------------------------------------*/

Str::operator char const *(){
   Ptr[Len] = '\0';
   return Ptr;
}

Str::operator char const *() const {
   Ptr[Len] = '\0';
   return Ptr;
}
Str::operator char*() {
   Ptr[Len] = '\0';
   return Ptr;
}

/*--------------------------------------------------------------------*/
/*                Add to string                                       */
/*--------------------------------------------------------------------*/

void Str::AddChar(char const c) {
   alloc(Len + 1);
   Ptr[Len++] = c;
}

static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";

void Str::ParseDig(signed long n) {
int i;
char s[128];

   if (n < 0L) {
      n = -n;
      AddChar('-');
   }
   i = 0;
   do {
      s[i++] = digits[(int)(n % _Base)];
   } while (n /= _Base);
   do {
     AddChar(s[--i]);
   } while (i != 0);
}

void Str::ParseDig(unsigned long n) {
int i;
char s[128];

   i = 0;
   do {
      s[i++] = digits[(int)(n % _Base)];
   } while (n /= _Base);
   do {
     i--;
     AddChar(s[i]);
   } while (i != 0);
}
 

Str & Str::operator += (Str const &s) {
   alloc(Len + s.Len + 1);
   memcpy(Ptr+Len,s.Ptr,s.Len);
   Len += s.Len;
   return *this;
}

Str & Str::operator += (char const *s) {
int slen;
   if (s == NULL) return *this;
   if (*s == '\0') return *this;
   slen = strlen(s);
   alloc(Len + slen + 1);
   strncpy(Ptr+Len,s,slen);
   Len += slen;
   return *this;
}

/*--------------------------------------------------------------------*/
/*                Substrings                                          */
/*--------------------------------------------------------------------*/

Str Str::operator () ( size_t _p, size_t _l) const {
Str s;
   if (_p == 0 || _p > Len || (_p + _l - 1) > Len) throw "Diapason out of range";
   if (_l == 0) {
      s.Len = Len - _p + 1;
   } else {
      s.Len = _l;
   }
   if (s.Len != 0) {
      s.alloc(s.Len + 1);
      memcpy(s.Ptr,Ptr+_p-1,s.Len);
   }
   return s;
}

/*--------------------------------------------------------------------*/
/*                Comparasion                                         */
/*--------------------------------------------------------------------*/

int operator == (Str const &s1, Str const &s2) {
   if (s1.Len != s2.Len) return 0;
   if (memcmp(s1.Ptr,s2.Ptr,s1.Len) != 0) return 0;
   return -1;
}

int operator == (Str const &s1, char const *s2) {
   if (s2 == NULL) return 0;
   if (s1.Len != strlen(s2)) return 0;
   if (memcmp(s1.Ptr,s2,s1.Len) != 0) return 0;
   return -1;
}

int operator == (char const *s2, Str const &s1) {
   if (s2 == NULL) return 0;
   if (s1.Len != strlen(s2)) return 0;
   if (memcmp(s1.Ptr,s2,s1.Len) != 0) return 0;
   return -1;
}


/*--------------------------------------------------------------------*/
/*                Characters                                          */
/*--------------------------------------------------------------------*/

char & Str::operator [] (size_t _p) {
   if (_p == 0) throw "Index is 0";
   if (_p > Len) throw "Index out of range";
   return *(Ptr+_p-1);
}

char const & Str::operator [] (size_t _p) const {
   if (Ptr == NULL) throw "Str is NULL";
   if (_p == 0) throw "Index is 0";
   if (_p > Len) throw "Index out of range";
   return *(Ptr+_p-1);
}

Str &Str::Trim(char const *Del) {
char *tmt;

   if (Del == NULL) return *this;
   while (Len != 0 && strchr(Del,Ptr[Len-1]) != NULL) Len--;
   if (Len == 0) return *this;
   for (tmt = Ptr; strchr(Del,*tmt) != NULL && Len != 0; Len--,tmt++);
   if (Len == 0) return *this;
   memmove(Ptr,tmt,Len);
   return *this;
}

size_t Str::Pos(char const *s) {
char *tmt;
   if (s == NULL) return 0;
   Ptr[Len] = '\0';
   if ((tmt = strstr(Ptr,s)) == NULL) return 0;
   return (tmt-Ptr+1);
}

Str &Str::Cut(size_t _p, size_t _l) {
   if (_p == 0 || _p > Len || (_p + _l - 1) > Len) throw "Diapason out of range";
   if (_l == 0) {
      Len = _p - 1;
   } else {
      Len -= _l;
      memmove(Ptr+_p-1,Ptr+_p+_l-1,Len-_p+1);
   }
   return *this;
}

Str &Str::Upper(void) {
   for (unsigned int i = 0; i < Len; i++) {
      Ptr[i] = toupper(Ptr[i]);
   }
   return *this;
}

/*--------------------------------------------------------------------*/
/*                In/Out                                              */
/*--------------------------------------------------------------------*/

ostream & operator << (ostream &io, Str const &s) {
   if (s.Len != 0) io.write(s.Ptr,s.Len);
   return io;
}


/*--------------------------------------------------------------------*/
#if 0
void main(void) {
   try {
      Str a;
      Str b("123456");
      b = b;
      a = b;

      cout << b << "\n";
      b[2] = 'c';

      a += b;
      a += " This is a test!";
      cout << b << "\n";
      cout << a << "\n";

      a = NULL;
      cout << a << "\n";

      a = "Test";
      cout << a << "\n";

      a.Base(16);
      a += 'c';
      a += 'd';
      a += ' ';
      a += 255;
      a += ' ';
      a += -255;
      a += ' ';
      a += 255L;
      a += ' ';
      a += -255L;
      cout << a << "\n";

      for (int i = 1; i < 4; i++) {
         cout << "i == " << i << " c == " << b[i] << "\n";
      }
      cout << "5" << "\n";
      cout <<"->"<< b(5,2) << "<-\n";
   }
   catch (char *s) {
     cout << "\nException: " << s << "\n";
   }
   catch (...) {
     cout << "\nException: "  << "\n";
   }
}
#endif

syntax highlighted by Code2HTML, v. 0.9.1