
// Modified version of the String class (c) Scott Robert Ladd 1991,1992
// Modifications by Brad Pitzel

#include "iomanip.h"
#include "string.h"
#include "stdio.h"
#include "stdarg.h"
#include "ctype.h"
#include "limits.h"
#include "str.h"

// class-global constant initialization
size_t String::AllocIncr = 8;

// calculate the allocation size for a string
inline size_t String::CalcSiz(size_t needed)
    {
    size_t x;

    x = ((needed + AllocIncr) / AllocIncr) * AllocIncr;

    return x;
    }

// constructor
String::String() : Len(0), Siz(0), Txt(NULL)
    {
    }

String::String(const String & str) : Len(str.Len), Siz(str.Siz)
    {
    if (str.Txt == NULL)
        Txt = NULL;
    else
        {
        Txt = new char[Siz];

        if (Txt == NULL)
            	cerr << "String:1 error\n";
	else
        	memcpy(Txt,str.Txt,Len + 1);
        }
    }

String::String(const char * cstr)
    {
    if ((cstr == NULL) || (cstr[0] == '\x00'))
        {
        Len = 0;
        Siz = 0;
        Txt = NULL;
        }
    else
        {
        Len = strlen(cstr);
        Siz = CalcSiz(Len);

        Txt = new char [Siz];

        if (Txt == NULL)
            	cerr << "String:2 error\n";
	else
        	memcpy(Txt,cstr,Len + 1);
        }
    }

String::String(size_t count, char fillCh)
    {
    if (count <= 0)
    	{
	Len = Siz = 0;
	Txt = NULL;
	return;
	}

    Siz = CalcSiz(count);
    Len = count;

    Txt = new char[Siz];

    if (Txt == NULL)
            cerr << "String:4 error \n";
    else
    	{
    	memset(Txt,fillCh,count);
	Txt[count] = '\x00';
	}
    }

String::String(size_t maxsize, const char * format, ... )
    {
    // allocate temporary buffer
    char * buffer = new char[maxsize];

    if (buffer == NULL)
    	    {
            cerr << "String:5 error \n";
            return;
            }

    // initialize argument list
    va_list args;
    
    va_start(args,format);

    // format items into buffer based on format
    Len = vsprintf(buffer,format,args);

    // end argument list processing
    va_end(args);

    // calculate require Txt length
    Siz = CalcSiz(Len);

    // allocate Txt
    Txt = new char[Siz];

    if (Txt == NULL)
        cerr << "String:6 error \n";
    else
    	{
   	 // duplicate data from buffer
    	strcpy(Txt,buffer);
    	}

    // delete buffer
    delete buffer;
    }

// destructor
String::~String()
    {
    if (Txt != NULL)
        delete Txt;
    }

// assignment method
String String::operator = (const String & str)
    {
    Len = str.Len;
    Siz = str.Siz;

    if (Txt != NULL)
        delete Txt;

    if (Siz == 0)
        Txt = NULL;
    else
        {
        Txt = new char[Siz];

        if (Txt == NULL)
            cerr << "String:7 error \n";

        memcpy(Txt,str.Txt,Len + 1);
        }

    return *this;
    }

// concatenation methods
String operator + (const String & str1,
                   const String & str2)
    {
    String temp;

    unsigned long totalLen = str1.Len + str2.Len;

    if (totalLen == 0)
        return temp;

    if (totalLen > UINT_MAX)
            cerr << "String:8 error \n";

    temp.Len = 0;
    temp.Siz = String::CalcSiz((size_t)totalLen);
    temp.Txt = new char[temp.Siz];

    if (temp.Txt == NULL)
            cerr << "String:9 error \n";

    temp.Txt[0] = '\000';

    if (str1.Txt != NULL)
        {
        memcpy(temp.Txt,str1.Txt,str1.Len);
        temp.Len = str1.Len;
        }

    if (str2.Txt != NULL)
        {
        memcpy(&temp.Txt[temp.Len],str2.Txt,str2.Len + 1);
        temp.Len += str2.Len;
        }

    return temp;
    }

// concatenation methods
String operator + (const String & str, char ch)
    {
    String temp;

    if (str.Txt == NULL)
        {
        temp.Len = 1;
        temp.Siz = String::AllocIncr;
        temp.Txt = new char [temp.Siz];

        if (temp.Txt == NULL)
            cerr << "String:10 error \n";

        temp.Txt[0] = ch;
        temp.Txt[1] = '\000';
        }
    else
        {
        if (str.Len == UINT_MAX)
            cerr << "String:11 error \n";

        temp.Len = str.Len + 1;

        if (temp.Len == str.Siz)
            temp.Siz = str.Siz + String::AllocIncr;
        else
            temp.Siz = str.Siz;

        temp.Txt = new char[temp.Siz];

        if (temp.Txt == NULL)
            cerr << "String:12 error \n";

        memcpy(temp.Txt,str.Txt,str.Len);

        temp.Txt[str.Len]  = ch;
        temp.Txt[temp.Len] = '\000';
        }

    return temp;
    }

StrCompVal String::Compare(const String & str,
                           StrCompMode caseChk) const
    {
    // handle special cases where one string is empty
    if (Txt == NULL)
        if (str.Txt == NULL)
            return SC_EQUAL;
        else
            return SC_LESS;

    if (str.Txt == NULL)
        return SC_GREATER;

    // compare the # of characters in the shorter string
    size_t count;

    if (str.Len < Len)
        count = str.Len;
    else
        count = Len;

    // working variables
    char   c1, c2;
    size_t i;

    if (caseChk == SM_IGNORE)
        {
        // case insensitive comparison
        for (i = 0; i < count; ++i)
            {
            c1 = (char)tolower(Txt[i]);
            c2 = (char)tolower(str.Txt[i]);

            // if characters differ
            if (c1 != c2)
                {
                // select appropriate result
                if (c1 < c2)
                    return SC_LESS;
                else
                    return SC_GREATER;
                }
            }
        }
    else
        {
        for (i = 0; i < count; ++i)
            {
            c1 = Txt[i];
            c2 = str.Txt[i];

            // if characters differ
            if (c1 != c2)
                {
                // select appropriate result
                if (c1 < c2)
                    return SC_LESS;
                else
                    return SC_GREATER;
                }
            }
        }

    // at this point, no differences were found
    if (Len == str.Len)
        return SC_EQUAL;
    else
        {
        // is lengths differ, shorter string < longer one
        if (Len < str.Len)
            return SC_LESS;
        else
            return SC_GREATER;
        }
    }

void String::InsertAfter( char search, String replace )
{
int i,j,k;

k = replace.Length();
j = Len;

i=0;
while(i<j)
	{
	if (Txt[i]==search)
		{
		Delete(i,1);
		Insert(i,replace);
		j += (k-1);
		i += (k-2);
		}
	i++;
	}
}


char String::Find(const String & str,
                     size_t & pos,
                     StrCompMode caseChk) const
    {
    // uses the brute force method
    if (Len < str.Len)
        return 0;

    // duplicate buffers
    char * target = new char[Len + 1];

    if (target == NULL)
            cerr << "String:13 error \n";

    strcpy(target,Txt);

    char * pattern = new char[str.Len + 1];

    if (pattern == NULL)
            cerr << "String:14 error \n";

    strcpy(pattern,str.Txt);

    // create return value variable
    char result;

    // convert to all lowercase if case-insensitive search
    if (caseChk == SM_IGNORE)
        {
        strlwr(target);
        strlwr(pattern);
        }

    // calculate last position in *this where str could be
    size_t end = Len - str.Len;
    size_t p, t;

    // start at the beginning of ttarget
    pos = 0;

    for (;;)
        {
        p = 0;   // beginning of pattern
        t = pos; // beginning of search position in target

        // while characters match 
        // and we're not at the end of the strings

        while ((pattern[p] == target[t])
            && (pattern[p] != 0) 
            && (target[t]  != 0))
            {
            // move to next character
            ++t;
            ++p;
            }

        // if we've reached the end of pattern
        //     we've found pattern in target
 
        if (pattern[p] == 0)
            {
            result = 1;
            break;
            }

        // if we've reached the end of target
        // or we've searched far enough
        //     pattern has not been found

        if ((target[t] == 0) || (pos >= end))
            {
            result = 0;
            break;
            }

        // keep looking, starting at the mismatch

        ++pos;
        }

    // delete temporary buffers
    delete target;
    delete pattern;

    // outa here
    return result;
    }

// substring deletion method
void String::Delete(size_t pos, size_t count)
    {
    if (Txt == NULL)
        return;

    size_t newLen, i;

    // error if deleting outside of string
    if ((pos + count - 1) > Len)
            fprintf(stderr,"String::Delete, pos=%d count=%d Len=%d", pos,count,Len );

    // length of new string
    newLen = Len - count;

    if ((Siz - newLen) > AllocIncr)
        {
        // allocation size has changed
        // calculate new size

        Siz = CalcSiz(newLen);

        // create new buffer

        char * temp = new char[Siz];

        if (temp == NULL)
            cerr << "String:15 error \n";

        // copy characters into new buffer
        char * tptr = temp;

        for (i = 0; i <= Len; ++i)
            {
            // when count is reached, skip deleted characters
            if (i == pos)
                i += count;

            *tptr = Txt[i];
            ++tptr;
            }

        // delete old buffer
        delete Txt;

        // assign new buffer
        Txt = temp;
        }
    else
        {
        // just "slide" characters down
        for (i = pos + count - 1; i < Len; ++i)
            Txt[i] = Txt[i + count];
        }

    Len = newLen;
    }

// substring insertion methods
void String::Insert(size_t pos, char ch)
    {
    if (pos > Len)
            cerr << "String:16 error \n";

    if (Txt == NULL)
        {
        // an empty string == ch
        Len = 1;
        Siz = AllocIncr;

        Txt = new char [Siz];

        if (Txt == NULL)
            cerr << "String:17 error \n";

        Txt[0] = ch;
        Txt[1] = '\000';
        }
    else
        {
        size_t newLen = Len + 1;
        size_t i;

        if (newLen == Siz)
            {
            // need a larger buffer
            Siz += AllocIncr;

            // create temporary buffer
            char * temp = new char[Siz];
            char * tptr = temp;

            if (temp == NULL)
            cerr << "String:18 error \n";

            // copy in old buffer, inserting ch when needed
            for (i = 0; i <= Len; ++i)
                {
                if (i == pos)
                    {
                    *tptr = ch;
                    ++tptr;
                    }

                *tptr = Txt[i];
                ++tptr;
                }

            // delete old buffer
            delete Txt;

            // assign new buffer and length
            Txt = temp;
            Len = newLen;
            }
        else
            {
            // slide characters right
            for (i = newLen; i > pos; --i)
                Txt[i] = Txt[i-1];

            // insert character
            Txt[pos] = ch;

            // adjust length
            Len = newLen;
            }
        }
    }

void String::Insert(size_t pos, const String & str)
    {
    if (str.Txt == NULL)
        return;

    if (pos > Len)
            cerr << "String:19 error \n";

    if (Txt == NULL)
        {
        // empty string = str
        *this = str;
        }
    else
        {
        // calculate new length
        unsigned long totalLen = str.Len + Len;

        if (totalLen > UINT_MAX)
            cerr << "String:20 error \n";

        size_t i, j;

        // if new  length > current size
        if (totalLen >= Siz)
            {
            // allocate new buffer
            Siz = CalcSiz((size_t)totalLen);

            char * temp = new char [Siz];
            char * tptr = temp;

            // copy buffers from source strings
            for (i = 0; i <= Len; ++i)
                {
                if (i == pos)
                    {
                    for (j = 0; j < str.Len; ++j)
                        {
                        *tptr = str.Txt[j];
                        ++tptr;
                        }
                    }

                *tptr = Txt[i];
                ++tptr;
                }

            // delete old buffer
            delete Txt;

            // assign new buffer
            Txt = temp;
            }
        else
            {
            // slide section old buffer to right
            for (i = Len + str.Len; i >= pos + str.Len; --i)
                Txt[i] = Txt[i - str.Len];

            // insert new string
            for (i = 0; i < str.Len; ++i)
                Txt[pos + i] = str.Txt[i];
            }

        Len = (size_t)totalLen;
        }
    }

// substring retrieval method
String String::Cut(size_t start, size_t count) const
    {
    if ((start + count) > Len)
            cerr << "String:21 error \n";

    String temp;

    if ((start < Len) && (count > 0))
        {
        temp.Len = count;
        temp.Siz = CalcSiz(count);
        temp.Txt = new char[temp.Siz];

        if (temp.Txt == NULL)
            cerr << "String:22 error \n";

        memcpy(temp.Txt,&Txt[start],count);

        temp.Txt[count] = '\000';
        }

    return temp;
    }

String String::CutHead(size_t count) const
    {
    if (count > Len)
            cerr << "String:22 error \n";

    String temp;

    if (count > 0)
        {
        temp.Len = count;
        temp.Siz = CalcSiz(count);
        temp.Txt = new char[temp.Siz];

        if (temp.Txt == NULL)
            cerr << "String:23 error \n";

        memcpy(temp.Txt,Txt,count);

        temp.Txt[count] = '\000';
        }

    return temp;
    }

String String::CutTail(size_t count) const
    {
    if (count > Len)
            cerr << "String:24 error \n";

    String temp;

    if (count > 0)
        {
        temp.Len = count;
        temp.Siz = CalcSiz(count);
        temp.Txt = new char[temp.Siz];

        if (temp.Txt == NULL)
            cerr << "String:25 error \n";

        memcpy(temp.Txt,&Txt[Len - count - 1],count);

        temp.Txt[count] = '\000';
        }

    return temp;
    }

// case-modification methods
void String::ToUpper()
    {
    if (Txt != NULL)
        strupr(Txt);
    }

void String::ToLower()
    {
    if (Txt != NULL)
        strlwr(Txt);
    }

String String::AsUpper() const
    {
    String temp = *this;

    if (temp.Txt != NULL)
        strupr(temp.Txt);

    return temp;
    }

String String::AsLower() const
    {
    String temp = *this;

    if (temp.Txt != NULL)
        strlwr(temp.Txt);

    return temp;
    }
// stream I/O functions

istream & operator >> (istream & strm, String & str)
    {
    static char buf[128];

    if ((str.Txt == NULL) || (str.Len == 0))
        {
        strm >> setw(128) >> buf;
//        strm >> buf;

        #ifdef __ZTC__
            str = (const char *)buf;
        #else
            str = buf;
        #endif
        }
    else
//        strm >> str.Txt;
        strm >> setw(str.Len) >> str.Txt;


    return strm;
    }


//convert a string to upper case
char *strupr( char *str )
{
char *temp = str;
while (*temp) { 
      *temp = toupper( *temp );
      temp++;
      }

return str;
}

//convert a string to lower case
char *strlwr( char *str )
{
char *temp = str;

while (*temp) { 
      *temp = tolower( *temp );
      temp++;
      }
return str;
}
