#include <all_far.h>


#include "fstdlib.h"

/************************************
            String
 ************************************/
String::~String()                  { if(str) { free(str); str = NULL; } }

String::String(void)             { BeginSet(); }
String::String(const String& s)  { BeginSet(s.Length()); Alloc(s.c_str()); }
String::String(size_t def_size)  { BeginSet(def_size); }
String::String(LPCSTR fmt)
{
	if(!fmt || !fmt[0])
		BeginSet();
	else
	{
		int sz = (int)strlen(fmt);
		BeginSet(Max(DEF_STR_ALLOC, sz+1));
		StrCpy(str, fmt, maxchar);
		len = sz;
	}
}

int String::printf(LPCSTR fmt,...)
{
	va_list  a;
	int      res;
	va_start(a,fmt);
	res = vprintf(fmt,a);
	va_end(a);
	return res;
}

int String::vprintf(LPCSTR fmt,va_list list)
{
	char tmp;
	va_list list_copy;
	va_copy (list_copy, list);
	int sz = vsnprintf(&tmp,0,fmt,list_copy);
	Alloc(sz+1);
	va_end(list_copy);
	return len = vsnprintf(str, maxchar, fmt, list);
}

void String::BeginSet(size_t sz)
{
	str = (char*)malloc(maxchar = (int)Max((size_t)DEF_STR_ALLOC, sz));
	str[len = 0] = 0;
}

LPCSTR String::Alloc(LPCSTR s,int maxLen)
{
	if(maxLen == -1)
		maxLen = (int)strlen(s);

	Alloc(maxLen+1);

	if(!s || !maxLen)
	{
		str[len = 0] = 0;
		return c_str();
	}

	strncpy(str, s, maxLen);
	str[ maxLen ] = 0;
	len = (int)strlen(str);
	return c_str();
}

LPCSTR String::Alloc(int t)
{
	if(t < maxchar)
		return str;

	str = (char*)realloc(str,maxchar = Max(DEF_STR_ALLOC,t));
	return str;
}

String& String::Add(const String& s)
{
	Alloc(Length() + s.Length() + 1);
	strcpy(str+Length(), s.c_str());
	len += s.Length();
	return *this;
}

String& String::Add(LPCSTR s)
{
	int slen = (int)strlen(s);

	if(slen)
	{
		Alloc(Length() + slen + 1);
		strcpy(str+Length(),s);
		len += slen;
	}

	return *this;
}

String& String::Add(LPCSTR s, int from, int to /*-1*/)
{
	if(to <= from)
		return *this;

	if(to == -1)
		return Add(s+from);

	Alloc(len+(to-from+1));
	StrCpy(str+len, s+from, to-from);
	len = (int)strlen(str);
	return *this;
}

String& String::Add(char ch)
{
	if(ch == 0)
		return *this;

	if(len + 1 >= maxchar)
		Alloc(len + 10);

	str[len++] = ch;
	str[len] = 0;
	return *this;
}

void String::cat(LPCSTR s,...)
{
	va_list a;
	va_start(a, s);
	vcat(s,a);
	va_end(a);
}

void String::vcat(LPCSTR s,va_list a)
{
	if(!s || !s[0]) return;

	va_list a_copy;
	va_copy (a, a_copy);

	int slen = vsnprintf(nullptr,0,s,a_copy);
	va_end(a_copy);
	if(!slen) return;

	int clen = Length();

	if(!Alloc(clen + slen + 1)) return;

	vsprintf(str+clen,s,a);
	len += slen;
}


void String::InsCharPos(int pos,char ch)
{
	if(!str) pos = 0;

	pos = Max(Min(pos,len),0);
	Alloc(Length()+1);

	if(pos < len) memmove(str+pos+1,str+pos,len-pos);

	str[pos]   = ch;
	str[++len] = 0;
}

void String::Del(int pos, int cn)
{
	if(!cn || pos >= len) return;

	cn = Min(len-pos, cn);
	memmove(str+pos, str+pos+cn, len-pos-cn);
	len -= cn;
	str[len] = 0;
}

void String::DelChars(char ch)
{
	if(str)
	{
		for(int n = 0; str[n]; n++)
			if(str[n] == ch)
			{
				memmove(str+n,str+n+1,len-n);
				len--;
			}

		str[len] = 0;
	}
}

int String::RChr(char ch,int pos) const
{
	if(!str || !str[0])
		return -1;

	if(pos == -1)
		pos = Length()-1;

	for(pos = Max(0,Min(Length(),pos));
	        pos && str[pos] != ch;
	        pos--);

	return pos;
}

char String::SetChar(int num,char ch)
{
	if(!str || num < 0 || num > maxchar)
		return 0;

	str[num] = ch;

	if(ch && num == len)
		str[++len] = 0;
	else if(!ch && num < len)
		len = (int)strlen(str);

	return ch;
}

int String::Chr(LPCSTR ch,int pos) const
{
	if(pos >= len)
		return -1;

	for(int n = pos; n < len; n++)
		if(strchr(ch,str[n]) != NULL)
			return n;

	return -1;
}

int       String::Chr(char ch,int pos)         const { return StrPosChr(c_str(),ch,pos); }
int       String::Str(LPCSTR s,int pos)      const { return StrPosStr(c_str(),s,pos); }

String&   String::operator=(const String& s)         { Alloc(s.c_str()); return *this; }
String&   String::operator=(LPCSTR s)              { Alloc(s); return *this; }
BOOL      String::operator!=(const String& s)  const { return len != s.len || !Cmp(s.c_str()); }
BOOL      String::operator!=(LPCSTR s)       const { return !Cmp(s); }
BOOL      String::operator==(const String& s)  const { return len == s.len && Cmp(s.c_str()); }
BOOL      String::operator==(LPCSTR s)       const { return Cmp(s); }
char      String::operator[](int num)          const { return (num >= 0 && num <= len && str) ? str[num] : '\0'; }

void String::SetLength(int sz)
{
	if(sz >= 0 && sz < maxchar)
	{
		len     = sz;
		str[sz] = 0;
	}
}

LPCSTR String::Set(LPCSTR s, int from, int to)
{
	if(to == -1)
	{
		Alloc(s+from);
		return c_str();
	}
	else if(to <= from)
		return c_str();

	Alloc(to-from+1);
	StrCpy(str, s+from, to-from+1);
	len = (int)strlen(str);
	return c_str();
}

BOOL String::Cmp(LPCSTR s,int count, BOOL isCase) const
{
	if(!s)
		return FALSE;

	if(!s[0])
		return str[0] == 0;

	if(count < 0)
		return strcmp(c_str(),s) == 0;
	else
		return StrCmp(c_str(),s,count,isCase) == 0;
}

void String::LTrim(char ch)
{
	int      n;

	if(len == 0) return;

	for(n = 0; str[n] && str[n] == ch; n++);

	if(n) memmove(str,str+n,len-n);

	len-=n; str[len] = 0;
}

void String::RTrim(char ch)
{
	int      n;

	if(len == 0) return;

	for(n = len-1; n >= 0 && str[n] == ch; len--,n--)
		str[n] = 0;
}

void String::Trim(char ch)
{
	int      n;

	if(len == 0) return;

	for(n = 0; str[n] && str[n] == ch; n++);

	if(n) memmove(str,str+n,len-n);

	len-=n; str[len] = 0;

	for(n = len-1; n >= 0 && str[n] == ch; len--,n--)
		str[n] = 0;
}
