///**********************  string2.cc  ***************************///
/*  This is a fairly poweful string class, complete with socket IO
    based on TCP/IP protocol (read() write())  */

#include "string2.h"
#include <stdarg.h>
#include <errno.h>
#include <signal.h>

extern int do_shutdown; //hack for ScryMUD

                      /* class functions */

void String::parse_show(short after) { 
  String tmp(cur_len + 100); //doubt more that 100 newlines
  int i;
  char chr;

  for (i = 0; i<cur_len; i++) {
    if ((chr = string[i]) == '\n') {
      tmp.Append("\r");
    }//if
    tmp.Append(chr);
  }//for

  cur_len = tmp.cur_len;
  delete[] string;
  string = tmp.string;
  tmp.string = NULL;
  max_len = tmp.max_len;
}//parse_show


/* these inserts don't work right */
void String::InsertBefore(char a, int posn) {

   if (cur_len + 1 > max_len) {
      char* tmp = string;
      string = new char[1 + max_len + 10];
      max_len += 10; //cur_len remains the same here
      strcpy(string, tmp);
      delete[] tmp;
   }//if

   
   int j = posn;
   char tmp = a;
   char tmp2;

   while (TRUE) {
      tmp2 = string[j];
      string[j] = tmp;
      if (!tmp) {
	 break;
      }//if
      tmp = tmp2;
      j++;
   }//while

   cur_len++;
}//InsertBefore


void String::InsertAfter(char a, int posn) {

   if (cur_len + 1 > max_len) {
      char* tmp = string;
      string = new char[1 + max_len + 10];
      max_len += 10; //cur_len remains the same here
      strcpy(string, tmp);
      delete[] tmp;
   }//if

   
   int j = posn + 1;
   char tmp = a;
   char tmp2;

   while (TRUE) {
      tmp2 = string[j];
      string[j] = tmp;
      if (!tmp) {
	 break;
      }//if
      tmp = tmp2;
      j++;
   }//while

   cur_len++;
}//InsertAfter

   

 
      String::String() { //default constructor
         //log("In default const.\n");
         string = new char[2];
         string[0] = '\0';
         cur_len = 0;
         max_len = 1;
      } // constructor


      String::String(const char* S) {
         //log("In char* const, here is S:  ");
	 if (S) {
            int i = strlen(S);
            string = new char[i + 1];
            cur_len = i;
            max_len = i;
            strcpy(string, S);
         }//if
	 else {
	    string = new char[2];
	    string[0] = '\0';
	    cur_len = 0; max_len = 1;
	 }//else
      } // constructor


      String::String(const String& S) {
         //log("In String const, here is source:");
         string = new char[S.cur_len + 1];
         strcpy(string, S.string);
         cur_len = S.cur_len;
         max_len = S.cur_len;
      } // constructor


      String::String(const int m_len) {
	 if (m_len > 1) {
            string = new char[m_len + 1];
            cur_len = 0;
            max_len = m_len;
            string[0] = '\0';
	 }//if
	 else {
	    string = new char[2];
	    cur_len = 0; max_len = 1;
	    string[0] = '\0';
	 }//else
      } // constructor


      String::~String() {
         delete[] string;
	 string = NULL;
      } //destructor

void String::Report() const {
#ifdef GRRMUD
   char buf[strlen(string) + 100];
   log("Report of String:");

   sprintf(buf, "cur_len: %i, max_len: %i, string: %s", cur_len, 
           max_len, string);
   log(buf);
#endif
   return;
}//Report
 

String String::Look_Command() const {
   String temp(100);
   int i = 0; 
   char a;

   
   if (max_len == 0) { //trivial case...
      return temp;
   }//if

   a = string[i];
   while ((isspace(a)) || (a == '.')) {
      i++;
      a = string[i];
   }//while

   //log("Cleared spaces:");
   //log(string);

   if (a) {			//wasn't all spaces
      //log("In if, wasn't all spaces.\n");
      if (a == '\'')  { // in quotes
         i++;
         a = string[i];
         while (a && !((a == '\'') || (a == '\n'))) {
            temp += a;
            i++;
            a = string[i];
         }//while
         if (a == '\'') { //closing quote
            i++; //move past it
         }//if
      }//if
      else {
         while (!(isspace(a) || a == '.')) {
            temp += a;
            i++;
            a = string[i];
         }//while
      }//else
   }//if
   return temp;

}//look_command


String String::Get_Command(short& eos, short& term_by_period) {
   String temp(100);
   int i = 0, k = 0;
   eos = term_by_period = FALSE;
   char a;
   String buf(300);

   a = string[i];
   while ((isspace(a)) || (a == '.')) {
      i++;
      a = string[i];
   }//while

   if (a) {			//wasn't all spaces
      if (a == '\'')  { // in quotes
         i++;
         a = string[i];
         while (a && !((a == '\'') || (a == '\n'))) {
            temp += a;
            i++;
            a = string[i];
         }//while
         if (a == '\'') { //closing quote
            i++; //move past it
         }//if
      }//if
      else {
         while (!(isspace(a) || a == '.' || !a)) {
            temp += a;
            i++;
            a = string[i];
         }//while
         if (a == '.')
            term_by_period = TRUE;
      }//else
   }//if
   else {
      string[0] = '\0';  //null it out, was empty.. I think :)
      cur_len = 0;
      eos = TRUE;
      return temp;		//it was only spaces i spose
   }//else

/*    This gives a very detailed look at the string in question..for debugin
   for (int ss = 0; string[ss]; ss++) {
      buf.Append(ss); buf += "\t-=|"; 
      buf += string[ss]; buf += "|=-\n";
   }//for
   log(buf);

   buf = "Here is i:  ";
   buf.Append(i);
   log(buf);

   buf = "Here is cur_len:  ";
   buf.Append(cur_len);
   log(buf);
*/

   if (string[i] == '\n') {
      //log("String[i] was a newline.\n");
      eos = TRUE;
      i++;				//to get past newline char
   }//if

   k = 0;
   while (i < cur_len) {
      string[k] = string[i];
      i++; 
      k++;
   }//while
   string[k] = '\0';
   cur_len = k;

/*        more debugging stuff
   log("After adjustment...\n");
   buf = '\0';
   for (int ss = 0; string[ss]; ss++) {
      buf.Append(ss); buf += "\t-=|"; 
      buf += string[ss]; buf += "|=-\n";
   }//for
   log(buf);
*/

#ifdef GRRMUD   
   log("Here is string returned by get_command:");
   log(temp);
#endif
   return temp;
}//get_command
	 

String String::Get_Rest(short destruct = TRUE) {
   String temp(300);
   int i = 0, k = 0;
   char a;

   //   log("In get_rest\n");

   if (string[0] == ' ')
      i++;	//junk leading space

   while (((a = string[i]) != '\n') && (a)) {
      temp += a;
      i++;
   }//while
   if (a == '\n')
      i++;	//advance past newline
   
   if (destruct) {
      while (i < cur_len) {
         string[k] = string[i];
         i++; 
         k++;
      }//while
      string[k] = '\0';
      cur_len = k;
   }//if should destruct

   return temp;
}//get_rest         



/*  This function is book keeping for when you change the string with the 
    [] operator.  This is a bit messy, but I need the power, and
    hopefully, this here will allow some security as well, if
    used correctly.  */

void String::Refigure_strlen() {
   cur_len = strlen(string);
   if (cur_len > max_len) {
#ifdef GRRMUD
      log("ERROR:  cur_len is greater than max_len, end of string was\n");
      log("over-written.  Good luck, you'll need it!\n");
#endif
      cur_len = max_len;
      string[cur_len] = '\0';  //try to salvage the situation..
   }//if
}//Refigure_strlen


void String::Termed_Read(ifstream& da_file) { //reads lines untill it finds
					      // a line containing only "~"
   char buf[81];
   short test;

   *this = "\0";  
   da_file.getline(buf, 80);
   test = strcmp(buf, "~");
   while (test) {
      (*this) += buf;
      da_file.getline(buf, 80);
      if (strcmp(buf, "~") != 0) {
         (*this) += "\n";
      }//while
      else {
         test = FALSE;
      }//else
   }//while
}//Termed_Read


void String::Tolower() {
   for (int i = 0; i < cur_len; i++) {
      string[i] = tolower(string[i]);
   }//for
}//Tolower


int String::Strlen() const {
   return cur_len;
}//Strlen


int String::Write(const int desc, const int max_to_write) {
   int sofar = 0, this_round;
   String buf;

   if (desc == -1) {  //cleans up an error message or two.
      return -1;
   }//if
   if (max_to_write > cur_len) {
#ifdef GRRMUD
      log("ERROR:  trying to write more than exists...cya!\n");
      do_shutdown = TRUE;
#endif
      exit(3);
   }//if
   else {
      do {
         //Sprintf(buf, "In Write, desc:  %i\n", desc);
         //log(buf);
         this_round = write(desc, (string + sofar), 
                            max_to_write - sofar);
         if (this_round < 0) { //some error happened
            perror("String.Write() err");
#ifdef GRRMUD
	    log("ERROR:  String.Write() err.\n");
#endif
            string[0] = '\0';
            cur_len = 0;
            return -1;
         }//if
         else if (this_round == 0) {
            string[0] = '\0';
            cur_len = 0;
            return sofar;
         }//if
         else if ((sofar += this_round) >= max_to_write) {
            string[0] = '\0';
            cur_len = 0;
            return sofar;
         }//if
      } while (TRUE);
   }//else
}//Write (to a descriptor)


int String::Read(const int desc, const int max_to_read) {
   int begin = strlen(string);
   int sofar = 0;
   int this_round = 0;
   int i, j;
   String tmp(100);
#ifdef GRRMUD
   Sprintf(tmp, "begin: %i, sofar: %i, input now: %S", 
	   begin, sofar, this);
   log(tmp);
#endif

   if (max_to_read > max_len) {
      log("Increased size in Read.\n");
      char* holder = string;
      string = new char[max_to_read + 1]; //ensure no overflow
      strcpy(string, holder);
      begin = cur_len = strlen(string);
      max_len = max_to_read;
      delete[] holder;
   }//if

#ifdef GRRMUD
      log("Beginning of Read do, here is input so far:");
      log(*this);
#endif
      do {
         this_round = read(desc, (string + sofar + begin), 
                           max_to_read - (begin + sofar) - 1);

         if (this_round > 0) {
            sofar += this_round;
         }//if
         else {
            if (this_round < 0) { //some error happened
               if (errno != EAGAIN) { //== EWOULDBLOCK
                  perror("String.Read() err, not EAGAIn");
                  string[sofar] = '\0';
                  cur_len = sofar;
#ifdef GRRMUD
                  log("ERROR:  String::Read exitted on error.\n");
                  log(*this);
#endif
                  return -1;
               }//if
               else  //this means it read all in buffer
                  break;
            }//if
            else {
#ifdef GRRMUD
               log("ERROR:  EOF encountered on socket read.\n");
#endif
               string[sofar] = '\0';
               cur_len = sofar;
               return -1;
            }//else
         }//else
      } while (TRUE); //*(string + begin + sofar -1) != '\n'); //newline


      *(string + begin + sofar) = 0;
      cur_len = begin + sofar;

/*		This is for debugging
      tmp = '\0';
      for (int ss = 0; string[ss]; ss++) {
         tmp.Append(ss); tmp += "\t-=|"; 
         tmp += string[ss]; tmp += "|=-\n";
      }//for
      log(tmp);
*/

		/* get rid of DOS's carriage return */

      j = 0;
      char a;
      for (i = 0; a = string[i]; i++) {
         if (a == 8) {  //deal with backspace
	    if (j > 0)
	       j--;
	 }//if
	 else if (a == ';') {
	    if (string[i+1] == ';') { //should escape it
	       string[j] = ';';
	       j++;
	       i++; //move past second semicolon
	    }//if
	    else {
	       string[j] = '\n';
	       j++;
	    }//else
	 }//if should deal with semicolon
   	 else if (a != 13) { //delete DOS's CR
	    string[j] = a;
	    j++;
         }//if
      }//for
      string[j] = 0;
      cur_len = j;

      char chr;
      for (i = begin; ((chr = string[i]) != '\n'); i++) {
         if (!chr) {
            return 0;	//do nothing..has no newline in it
         }//if
      }//for

      return 1;
}//Read (from a descriptor)


void String::Clear() {
   delete[] string;
   string = new char[2];
   cur_len = 0;
   max_len = 1;
   string[0] = 0;
}//Clear      

void String::Vclear() {
   *this = "";   // = overload
}//Vclear      

void String::Cap() {
   if (max_len > 0)
      string[0] = toupper(string[0]);
}//Cap      

void String::Getline(istream& stream, int max_len) {
   char buf[max_len + 1];
   stream.getline(buf, max_len);
   *this = buf;
}//Getline      


void String::Append(const char* source) {
   if (!source)
      return;
   int k = strlen(source);
   if ((cur_len + k) > max_len) {
      char* holder = string;
      string = new char[cur_len+k+1];
      max_len = cur_len + k;
      strcpy(string, holder);
      delete[] holder;
   }//if
   strcat(string, source);
   cur_len += k;
}//Append      


void String::Append(const int i) {
   char source[25];
   sprintf(source, "%i", i);
   this->Append(source);
}//Append      


void String::Append(const char c) {
   char buf[2];
   sprintf(buf, "%c", c);
   this->Append(buf);
}//Append      


void String::Append(const String& S) {
   if ((cur_len + S.cur_len) > max_len) {
#ifdef GRRMUD
      log("STREAMLINE:  Increasing len in Append(String&)\n");
#endif
      char* holder = string;
      string = new char[cur_len + S.cur_len + 1];
      max_len = (cur_len + S.cur_len);
      strcpy(string, holder);
      delete[] holder;
   }//if
   strcat(string, S.string);
   cur_len += S.cur_len;
}//Append      


void String::Prepend(const int i) {
   char source[25];
   sprintf(source, "%i", i);
   this->Prepend(source);
}//Prepend      


void String::Prepend(const long i) {
   char source[25];
   sprintf(source, "%d", i);
   this->Prepend(source);
}//Prepend      


void String::Prepend(const char* source) {
   String tmp(*this); //keep a copy handy
   *this = source;
   this->Append(tmp);
}//Prepend      


void String::Prepend(const String& source) {
   this->Prepend(source.string);
}//Prepend      


   
     ///*********************  OPERATORS  ******************************///

      String::operator const char*() const {
         return string;
      }//to char* operator overload


      String& String::operator+= (const String &S) {
         this->Append(S);
      }//+= operator

      String& String::operator+= (const char s) {
         this->Append(s);
      }//+= operator


      String& String::operator+= (const char* S) {
         this->Append(S);
      }//+= operator


      String& String::operator= (const String &S) {
         if (S.cur_len > max_len) {
            delete[] string;
            string = new char[S.cur_len + 1];
            max_len = (S.cur_len);
         }//if
         strcpy(string, S.string);
         cur_len = S.cur_len;
         return *this;
      }//assignment operator


      String& String::operator= (const char* S) {
         if (S == NULL) {
	    Vclear();
            return *this;
         }//if
         int k = strlen(S);
         if (k > max_len) {
            delete[] string;
            string = new char[k + 1];
            max_len = (k);
         }//if
         strcpy(string, S);
         cur_len = k;
         return *this;
      }//assignment operator


      char& String::operator[] (const unsigned int i) const {
         if (i < 0) {
#ifdef GRRMUD
	    log("ERROR:  String::operator[], i < 0.\n");
#endif
	    //kill(getpid(), SIGSEGV); //dump core

	    if (max_len == 0)
               return string[0]; //return last char
            else
               return string[max_len - 1]; //return last char

	 }//if
         else if (i < max_len)
            return string[i];
         else { //i is out of range, too high
            String str(100);
#ifdef GRRMUD
            log("WARNING:  tried to go past end of string, operator[]");
            Sprintf(str, "cur_len: %i, max_len: %i, i: %i", cur_len, max_len,
                    i);
            log(str);
            log(*this);
#endif
	    //kill(getpid(), SIGSEGV); //dump core, fer debugging
	    // never get here!!
	    if (max_len == 0)
               return string[0]; //return last char
            else
               return string[max_len - 1]; //return last char
         }//else
      }//[] operator


      String String::operator+ (const char* S) const {
         String tmp(*this);
         tmp.Append(S);
         return tmp;
      }//assignment operator


      String String::operator+ (const String& S) const {
         String tmp(*this);
         tmp.Append(S);
         return tmp;
      }//assignment operator


/* equality operators, case insensitive */
      int String::operator!= (const char* S) const {
         return strcasecmp(string, S);
      }//assignment operator


      int String::operator== (const char* S) const {
         return (!(strcasecmp(string, S)));
      }//assignment operator

      int String::operator< (const char* S) const {
         return (strcasecmp(string, S) < 0);
      }//assignment operator

      int String::operator> (const char* S) const {
         return (strcasecmp(string, S) > 0);
      }//assignment operator

      int String::operator> (const String& S) const {
         return (strcasecmp(string, S.string) > 0);
      }//assignment operator

      int String::operator< (const String& S) const {
         return (strcasecmp(string, S.string) < 0);
      }//assignment operator

      int String::operator<= (const char* S) const {
         return (strcasecmp(string, S) <= 0);
      }//assignment operator

      int String::operator>= (const char* S) const {
         return (strcasecmp(string, S) >= 0);
      }//assignment operator




              /* friend functions */

void Sprintf(String& targ, const char* string, ... ) {
   va_list ap;
   int i = 0;
   va_start(ap, string);
   int num = 0;
   String buf(100);
   int s_len;
   char* tmp_chr_str;
   String* tmp_str;

   targ = "\0"; //initialize targ

   for (i = 0; string[i]; ) {
      if (string[i] == '%') {
         i++;
         if (string[i] != '%') {
            switch (string[i]) {
               case 'i':    /* int */  
                  targ.Append(va_arg(ap, int));
                  break;
               case 'd':     /* long, turn it into an int */
                  targ.Append(va_arg(ap, int));
                  break;
               case 's':      /* char * */
                  if (tmp_chr_str = va_arg(ap, char *)) {
                     targ.Append(tmp_chr_str);
                  }//if
                  break;
               case 'S':      /* String */
                  if (tmp_str = va_arg(ap, String *)) {
                     targ.Append(*tmp_str);
                  }//if
                  break;
               case 'P':      /* pad w/spaces */
                  i++; //looking at first number now
                  if (isdigit(string[i+1])) { //two digits?
                     if (isdigit(string[i])) { //it should be
                        num = 10 * ((int)(string[i]) - 48);
                        num += (int)(string[i+1]) - 48;
                     }//if
                     else {
#ifdef GRRMUD
                        log("ERROR:  Sprintf, case 'P', first not an int\n");
#endif
		     }//else
                  }//if two digits
                  else {
#ifdef GRRMUD		    
                     log("ERROR:  Sprintf, case 'P', second not an int\n");
#endif
		  }//else
                  i += 2; //now pointing to next valid letter
                  
                  s_len = targ.Strlen();
                  while (s_len < num) {
                     targ.Append(" ");  //pad another space
                     s_len++;
                  }//while
                  i--;  //slight hack
                  break;
               default:
#ifdef GRRMUD
                  log("ERROR:  default called in Sprintf.\n");
                  do_shutdown = TRUE;
#endif
                  exit(1);
            }//switch
            i++;
         }//if
         else { //
            i++;
            targ.Append("%");
         }//else
      }//if
      else {
         targ.Append(string[i]);
         i++;
      }//else
   }//for
   va_end(ap);
}//Sprintf

		/* WARNING:  this will fail if input is > 99 chars. */

      istream& operator>> (istream& stream, String& str) {
         char buf[100];
         stream >> buf;
         str = buf;
      }//write_out operator


      ostream& operator<< (ostream& stream, const String& str) {
         stream << str.string;
      }//write_out operator
