/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: string2.cc,v 1.12.2.2 2000/03/10 04:11:58 greear Exp $
// $Revision: 1.12.2.2 $  $Author: greear $ $Date: 2000/03/10 04:11:58 $

//
//ScryMUD Server Code
//Copyright (C) 1998  Ben Greear
//
//This program 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.
//
//This program 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 this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// To contact the Author, Ben Greear:  greear@cyberhighway.net, (preferred)
//                                     greearb@agcs.com
//

///**********************  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>
#include <string.h>

extern int core_dump(const char* msg); //misc2.cc

                      /* class functions */


LogStream* String::logfile = NULL;
int String::string_cnt = 0;
int String::total_bytes = 0;

// return a value that should be pretty unique.
// This is basically lame, do some research if you want a real
// hash function!! --BLG
unsigned int String::hash() {
   unsigned int retval = 0;

   for (int i = 0; i<cur_len; i++) {
      retval += (int)(string[i]);
   }
   retval += cur_len;
   return retval;
}

int String::Assert(int boolean_val, const char* msg) const {
   if (!boolean_val) {
      core_dump(msg);
   }
   return TRUE;
}

void String::setLogFile(LogStream* dafile) { logfile = dafile; }

LogStream* String::getLogFile() { return logfile; }

String String::sterilizeForHtml() const {
   String retval(Strlen() + 20);
   
   char ch;
   for (int i = 0; i<cur_len; i++) {
      ch = string[i];
      if (ch == '<') {
         retval += "&lt;";
      }
      else if (ch == '>') {
         retval += "&gt;";
      }
      else if (ch == '&') {
         retval += "&amp;";
      }
      else {
         retval += ch;
      }
   }//for
   return retval;
}//sterilizeForHtml

void String::Strip() {
   int i;

   if (cur_len == 0)
      return;

   for (i = 0; i<cur_len; i++) {
      if (!isspace(string[i]))
         break;
   }
   
   if (i == cur_len) {
      Clear();
      return;
   }
   
   int end;
   for (end = cur_len - 1; end >= i; end--) {
      if (!isspace(string[end]))
         break;
   }//for

   if (end <= i) {
      Clear();
      return;
   }

   char buf[cur_len + 1];
   memcpy(buf, string + i, (end - i) + 1);
   buf[(end - i) + 1] = 0;

   *this = buf;
}//Strip

//drop a few off the end of the string
void String::dropFromEnd(int num_chars) {
   if (cur_len > num_chars) {
      string[cur_len - num_chars] = 0;
      cur_len -= num_chars;
   }
   else {
      *this = "";
   }
}

/** Starts inserting in the string[i+1] position. */
void String::insertAfter(int i, const char* str) {
   if (!str || (i < 0))
      return;

   if (i > cur_len) {
      this->Append(str);
      return;
   }

   ensureCapacity(cur_len + strlen(str));
   String tmp(string + i+1);
   strcpy(string + i+1, str);

   cur_len = strlen(string);

   this->Append(tmp);
}//insertAfter

void String::ensureCapacity(int max_length) {
   //LOGFILE << "In ensure Capacity...  max_length:  "
   //        << max_length << "  max_len:  " << max_len
   //        << endl << flush;
   if (max_len < max_length) {
      char* tmp = string;
      string = new char[max_length + max_len + 1];
      total_bytes += max_length; //keep our static statistics
      max_len += max_length; //cur_len remains the same here
      strcpy(string, tmp);
      delete[] tmp;
   }//if
   //LOGFILE << "In ensure Capacity...  done" << endl
   //        << flush;
}//ensureCapacity
   
//grabs everything untill that character is found.  It removes all that
// from the string, so successive calls iterate through it...
// Returned pointer is to a String allocated off of the Heap..ie delete it
// eventually if that makes sense.
String* String::getUntil(char dlm) {
   String* retval = new String(cur_len);
   
   int i;
   int found_dlm = FALSE;
   char ch;
   for (i = 0; i<cur_len; i++) {
      ch = *(string + i);
      if (ch == dlm) {
         found_dlm = TRUE;
         break;
      }
      else
         retval->Append(ch);
   }//for

   //now, move the string over
   if ((!found_dlm) || (i == cur_len)) {
      Clear();
   }
   else {
      String tmp((char*)(string + i + 1));
      *this = tmp;
   }

   if (retval->Strlen() > 0)
      return retval;
   else {
      delete retval;
      return NULL;
   }
}//getUntil


// After is supposed to be a boolean telling us to put it before
// or after... 
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') {
       if (after)
          tmp.Append("\n\r");
       else
          tmp.Append("\r\n");
       continue;
    }//if
    tmp.Append(chr);
  }//for

  *this = tmp;
}//parse_show


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

   ensureCapacity(cur_len + 1);
   
   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) {

   ensureCapacity(cur_len + 1);
   
   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_cnt++;
   string = new char[2];
   string[0] = '\0';
   cur_len = 0;
   max_len = 1;

   total_bytes += 2;
} // constructor


String::String(const char* S) {
   //log("In char* const, here is S:  ");
   string_cnt++;
   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
   total_bytes += (max_len + 1);
} // constructor


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

   total_bytes += (max_len + 1);
} // constructor


String::String(const int my_len) {
   int m_len = my_len;
   string_cnt++;

   if (m_len > 100000) {
      LOGFILE << "WARNING:  making string of size 100000" << endl;
      m_len = 100000;
   }

   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

   total_bytes += (max_len + 1);
} // constructor


String::~String() {
   string_cnt--;
   total_bytes -= (max_len + 1);
   delete[] string;
   string = NULL;
} //destructor

void String::Report() const {
   char buf[strlen(string) + 100];
   sprintf(buf, "cur_len: %i, max_len: %i, string: %s", cur_len, 
           max_len, string);
   LOGFILE << "Report of String -:" << buf << ":-" << endl;
}//Report
 

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

   //LOGFILE << "In look_command -:" << *this << ":-" << endl;
   
   if (max_len == 0) { //trivial case...
      return temp;
   }//if

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

   int in_quotes = FALSE;

   if (a == '\'') {
      if (is_first) {
         temp = '\'';
         return temp;
      }
      if (string[i+1] == '\'') {
         temp.Append(a);
         i += 2;
         a = string[i];
      }//if
      else {
         in_quotes = TRUE;
         i++;
         a = string[i];
      }//else
   }//if

   while (TRUE) {
      if (!a || a == '\n')
         break; //done

      if (in_quotes) {
         if (a == '\'') {
            if (string[i+1] == '\'') { //escaped the quote
               temp.Append(a);
            }//if
            else {
               break; //all done
            }//else
         }//if
      }//if
      else {
         if (isspace(a) || (a == '.')) {
            //then all done
            break;
         }//if
         else {
            temp.Append(a);
         }//else
      }//else

      i++;
      a = string[i];
   }//while

   return temp;

}//look_command


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

   //LOGFILE << "In Get_command -:" << *this << ":-" << endl;

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

   if (a) {			//wasn't all spaces

      //LOGFILE << "a -:" << a << ":-  i:" << i << endl;

      if (is_first && (a == '\'')) {
         temp = '\'';
         i++; //move to the next one
      }
      else {
         if ((a == '\'') && (string[i+1] != '\''))  { // in quotes

            //LOGFILE << "In quotes." << endl;
            
            i++;
            a = string[i];
            while (a && (a != '\n')) {
               if (a == '\'') {
                  if (string[i+1] == '\'') { //then escape it.
                     temp += a;
                     i += 2;
                  }//if
                  else {
                     break;
                  }
               }//if
               else {
                  temp += a;
                  i++;
               }//else
               a = string[i];
            }//while
            if (a == '\'') { //closing quote
               i++; //move past it
            }//if
         }//if
         else {
            if ((a == '\'') && (string[i+1] == '\'')) {
               temp.Append(a);
               i += 2;
               a = string[i];
            }//if
            
            while (!(isspace(a) || a == '.' || !a)) {
               temp += a;
               i++;
               a = string[i];
            }//while
            if (a == '.')
               term_by_period = TRUE;
         }//else
      }//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);
*/
   
   //log("Here is string returned by get_command:");
   //log(temp);
   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) {
      LOGFILE.log(ERROR, "ERROR:  cur_len is > max_len, end of string was\n");
      //log("over-written.  Good luck, you'll need it!\n");
      cur_len = max_len;
      string[cur_len] = '\0';  //try to salvage the situation..
   }//if
}//Refigure_strlen

int String::Contains(const char ch) const {
   int retval = 0;
   for (int i = 0; i<cur_len; i++) {
      if (string[i] == ch) {
         retval++;
      }
   }//for

   return retval;
}


/** Reads untill it finds the delim, and then reads untill
 * it finds another.  Escape the delim with a \ (backslash).
 * This is not too efficient, as it reads one character at
 * a time, btw.
 */
int String::readToken(char delim, ifstream& dafile, int include_delim) {
   char ch;
   int is_escaped = FALSE;

   Vclear();

   // Get to the beginning
   while (dafile) {
      dafile.get(ch);
      if (ch == '\\') {
         if (is_escaped) {
            is_escaped = FALSE;
         }
         else {
            is_escaped = TRUE;
         }
      }
      else if (ch == delim) {
         if (is_escaped) {
            is_escaped = FALSE;
         }
         else {
            break;
         }
      }
      else {
         is_escaped = FALSE;
      }
   }//while

   if (!dafile)
      return -1;

   if (include_delim)
      *this += delim;

   while (dafile) {
      dafile.get(ch);
      if (ch == '\\') {
         if (is_escaped) {
            is_escaped = FALSE;
            *this += ch;
         }
         else {
            is_escaped = TRUE;
         }
      }
      else if (ch == delim) {
         if (is_escaped) {
            is_escaped = FALSE;
            *this += ch;
         }
         else {
            break;
         }
      }
      else {
         // Only escape the delimiter at this time.
         if (is_escaped) {
            *this += '\\';
         }
         *this += ch;
         is_escaped = FALSE;
      }
   }//while

   if (!dafile)
      return -1;

   if (include_delim)
      *this += delim;

   return 0;
}//readToken


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

   *this = "";  

   if (!da_file) {
      LOGFILE.log(WRN, "WARNING:  String::Termed_Read, da_file is null\n");
      return;
   }

   da_file.getline(buf, 180);

   if (LOGFILE.ofLevel(DB)) {
      LOGFILE << "String::Termed_Read, buf -:" << buf << ":-" << endl;
   }

   test = strcmp(buf, "~");
   while (test) {
      if (!da_file) {
         LOGFILE.log(WRN,
                     "WARNING:  String::Termed_Read, in while da_file null\n");
         break;
      }//if

      (*this) += buf;
      da_file.getline(buf, 180);

      if (LOGFILE.ofLevel(DB)) {
         LOGFILE << "String::Termed_Read, in while buf -:" 
                 << buf << ":-" << endl;
      }

      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

void String::toUpper() {
   for (int i = 0; i < cur_len; i++) {
      string[i] = toupper(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

   Assert((max_to_write <= cur_len),
          "ERROR:  trying to write more than exists...cya!\n");

   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");
         LOGFILE.log(ERROR, "ERROR:  String.Write() err.\n");
         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) {
         if (max_to_write == cur_len) { //wrote it all
            string[0] = '\0';
            cur_len = 0;
         }
         else {
            // These can overlap, the +1 is for the null terminator.
            memmove(string, string + sofar, (cur_len - sofar + 1));
            cur_len = strlen(string);
         }//else
         return sofar;
      }//if
   } while (TRUE);
}//Write (to a descriptor)


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

   ensureCapacity(max_to_read);
   begin = Strlen();

   //log("Beginning of Read do, here is input so far:");
   //log(*this);

   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;
               //log("ERROR:  String::Read exitted on error.\n");
               //log(*this);
               return -1;
            }//if
            else  //this means it read all in buffer
               break;
         }//if
         else {
            //log("ERROR:  EOF encountered on socket read.\n");
            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 = begin;
   char a;
   for (i = begin; (a = string[i]); i++) {
      if (a == 8) {  //deal with backspace
         if (j > begin)
            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::purge() {
   total_bytes -= (max_len + 1);
   delete[] string;
   string = new char[2];
   cur_len = 0;
   max_len = 1;
   total_bytes += (max_len + 1);
   string[0] = '\0';
}//Clear      


void String::Clear() {
   *this = "\0";
}//Clear


void String::Cap() {
   for (int i = 0; i<cur_len; i++) {
      if (isalpha(string[i])) {
         string[i] = toupper(string[i]);
         return;
      }//if
   }//for
}//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);

   ensureCapacity(cur_len + k);

   /*  string + cur_len suggested by Nevyn.  Good idea, should
    *   make it faster. --Ben
    */
   strcat(string + cur_len, source);
   cur_len += k;
}//Append      


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

void String::appendHex(const long i) {
   char source[25];
   sprintf(source, "%lx", 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) {
   ensureCapacity(cur_len + S.cur_len);
   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", (int)(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);
   return *this;
}//+= operator

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


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


String& String::operator+= (const int i) {
   this->Append(i);
   return *this;
}

String& String::operator+= (const long l) {
   this->Append((int)l); //bah, same thing in a real computer
   return *this;
}

String& String::operator= (const int i) {
   Clear();
   this->Append(i);
   return *this;
}

String& String::operator= (const long l) {
   Clear();
   Append((int)l); //same thing in a real computer, for now at least
   return *this;
}

String& String::operator= (const char c) {
   Clear();
   Append(c);
   return *this;
}


String& String::operator= (const String &S) {
   ensureCapacity(S.cur_len);
   strcpy(string, S.string);
   cur_len = S.cur_len;
   return *this;
}//assignment operator


String& String::operator= (const char* S) {
   if (S == NULL) {
      Clear();
      return *this;
   }//if
   int k = strlen(S);
   ensureCapacity(k);
   strcpy(string, S);
   cur_len = k;
   return *this;
}//assignment operator

char String::charAt(int i) const {
   Assert(((i >= 0) && (i <= max_len)), "ERROR:  Index out of range.\n");
   return string[i];
}

void String::setCharAt(int idx, const char val) {
   Assert(((idx >= 0) && (idx < cur_len)), "ERROR: Index out of range.\n");   
   Assert(val, "ERROR:  setCharAt:  trying to set a NULL value.\n");
   string[idx] = val;
}
      
char String::operator[] (const unsigned int i) const {
   Assert(((int)(i) < max_len), "ERROR:  index to high, String::operator[]\n");

   return string[i];

}//[] 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;
   va_start(ap, string);

   vSprintf(targ, string, ap);
   va_end(ap);
   return;
}

void vSprintf(String& targ, const char* string, va_list ap) {
   int i = 0;
   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 'I':    /* ignore */
                  (void)va_arg(ap, void*);
                  // Do nothing with it!!
                  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 'p':    /* pointer */
                  targ.appendHex(va_arg(ap, long));
                  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 {
                        LOGFILE.log(ERROR, 
                                    "ERROR:  Sprintf, case 'P', first not an int\n");
                     }
                  }//if two digits
                  else {
                     LOGFILE.log(ERROR,
                                 "ERROR:  Sprintf, case 'P', second not an int\n");
                  }
                  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:
                  targ.Assert(FALSE, "ERROR: default called in Sprintf.\n");
            }//switch
            i++;
         }//if
         else { //
            i++;
            targ.Append("%");
         }//else
      }//if
      else {
         targ.Append(string[i]);
         i++;
      }//else
   }//for
}//vSprintf

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

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


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