//
// Implements simple string class 'str'
//

# include "str.h"
# include <string.h>
# include <memory.h>
# if defined( _MSC_VER )
# pragma warning(disable:4505)
# endif

# define STDLEN 32

extern "C" void * malloc (unsigned sz);
extern "C" void free (void * ptr);

# if defined( PLACEMENT_NEW_BUG )

inline void *
operator new (unsigned sz, short allocsz)
{
    return malloc (sz + allocsz);
}

# else

void *
refstr::operator new (unsigned sz, short allocsz)
{
    return malloc (sz + allocsz);
}

# endif

void
str::_strinit (char const * s, short len, short siz)
{
    if (len < 0)
        len = (short) ((s) ? strlen (s) : 0);
    if (siz < 0)
        siz = STDLEN;
    if (siz < len + 1)
        siz = short(len + 1);
    strdata = new(siz) refstr(len, siz);
    if (s && len)
        memcpy (c_ptr(), s, len);
}

        // Called whenever string is to be modified or grown
int
str::_chksize (short sz)
{
    refstr * old = 0;
    if (strdata->_refs > 1) // Need to dup memory
        --strdata->_refs;   // Dec existing string reference
    else if (sz >= size())
        old = strdata;
    else
        return 0;
    _strinit (c_ptr(), length(), sz);
    delete old;
    return 1;
}

str &
str::operator= (str const & s)
{
    if (&s != this)
    {
        if (!--strdata->_refs)
            delete strdata;
        strdata = s.strdata;
        ++strdata->_refs;
    }
    return *this;
}

str &
str::operator= (char const * s)
{
    if (s != c_ptr())
    {
        short len = (short) strlen (s);
        _chksize (len);
        memcpy (c_ptr(), s, len + 1);
        strdata->_length = len;
    }
    return *this;
}

str &
str::operator= (char c)
{
    _chksize (1);
    *c_ptr() = c;
    strdata->_length = 1;
    return *this;
}

short
str::insert (short pos, char const * s, short len)
{
    if (len < 0)
        len = (short) strlen (s);
    if (len)
    {
        short leng = strdata->_length;
        if (pos < 0 || pos > leng)
            pos = leng;
        _chksize (short(leng + len));
        char * buf = c_ptr();
        if (pos < leng)
            memmove (buf + pos + len, buf + pos, leng - pos);
        memcpy (buf + pos, s, len);
        strdata->_length += len;
    }
    return length();
}

short
str::remove (short pos, short len)
{
    if (pos >= 0 && pos < length())
    {
        short leng = strdata->_length;
        if (len < 0 || (pos + len) > leng)
            len = short(leng - pos);
        if (len)
        {
            _chksize (0);
            char * buf = c_ptr();
            memcpy (buf + pos, buf + pos + len, leng - (pos + len));
            strdata->_length -= len;
        }
    }
    return length();
}

short
str::replace (short pos, char const * s, short clen, short len)
{
    if (pos >= 0)
    {
        short leng = strdata->_length;
        if (clen < 0 || (pos + clen) > leng)
            clen = short(leng - pos);
        if (len < 0)
            len = (short) strlen (s);
        if (pos > leng)
            pos = leng;
        _chksize (short(leng - clen + len));
        char * buf = c_ptr();
        if (clen != len && clen)
            memmove (buf + pos + len, buf + pos + clen,
                     leng - (pos + clen - len));
        if (len)
            memcpy (buf + pos, s, len);
        strdata->_length += short(len - clen);
    }
    return length();
}


str &
str::left (short len, char padch)
{
    if (len < 0)
        return right (short(-len), padch);
    short leng = strdata->_length;
    if (len != leng)
    {
        _chksize (len);
        if (len > leng)
            memset (strdata->ptr() + leng, padch, leng - len);
        strdata->_length = len;
    }
    return *this;
}

str &
str::right (short len, char padch)
{
    if (len < 0)
        return left(-1, padch);
    short leng = strdata->_length;
    if (len != leng)
    {
        _chksize (len);
        if (len > leng)
        {
            char * buf = strdata->ptr();
            memmove (buf + len - leng, buf, leng);
            memset (buf, padch, len - leng);
        }
        strdata->_length = len;
    }
    return *this;
}

str &
str::mid (short pos, short len, char padch)
{
    if (pos <= 0)
        return left(len, padch);
    short leng = strdata->_length;
    if (pos > leng)
        pos = leng;
    if (leng < len)         // Are we padding?
    {
        _chksize (len);
        char * buf = strdata->ptr();
        short nlen = short((len - (leng - pos)) / 2);
        if (nlen > 0)
        {
            memmove (buf, buf + pos, leng - pos);
            memset (buf + leng - pos, padch, nlen);
            strdata->_length -= short(pos - nlen);
        }
    }
    return right (len, padch);
}


int
str::_concat (char const * s, short len)
{
    if (len < 0)
        len = (short) strlen (s);
    if (len)
    {
        _chksize (short(len + length()));
        memcpy (c_ptr() + length(), s, len);
        strdata->_length += len;
    }
    return length();
}

short
str::removech (char const * clist)
{
    short result = 0;
    if (*clist)
    {
        char * buf, * sub;
        buf = sub = strdata->ptr();
        short nlen = strdata->_length;
        for (short i = 0; i < nlen; ++i)
        {
            if (strchr (clist, *buf) == 0)
            {
                if (result)
                    *sub = *buf;
                ++sub;
            }
            else
            {
                if (!result)
                    _chksize (0);
                ++result;
            }
            ++buf;
        }
        strdata->_length = short(nlen - result);
    }
    return result;
}

short
str::countch (char const * clist)
{
    short result = 0;
    if (*clist)
    {
        char * buf = strdata->ptr();
        short nlen = strdata->_length;
        for (short i = 0; i < nlen; ++i, ++buf)
            if (strchr (clist, *buf) != 0)
                ++result;
    }
    return result;
}


str
left (str const & s, short len, char padch)
{
    str n(s);
    return n.left(len, padch);
}

str
right (str const & s, short len, char padch)
{
    str n(s);
    return n.right(len, padch);
}

str
mid (str const & s, short pos, short len, char padch)
{
    str n(s);
    return n.mid(pos, len, padch);
}
