#include "stdafx.h"
#include "aplAggr.h"

#ifdef ENABLE_APL_STRING

#include <stdarg.h>  // Для Format

#ifdef _MFC_VER
	#ifdef _DEBUG
		#define new DEBUG_NEW
	#endif
#endif


#ifdef _MFC_VER

inline void aplUpperChar(char &c0) // Это не будет работать с UTF-8
{
	unsigned char &c=(unsigned char &)c0;
	if('\0'==c) return;
	if( (c>='a') && (c<='z')) c-= ('z' - 'Z');
	else if( (c>=(unsigned char)'а') && (c<=(unsigned char)'я')) c-= ('я' - 'Я');
	else if( c==(unsigned char)'ё') c='Ё';
};

inline void aplLowerChar(char &c0) // Это не будет работать с UTF-8
{
	unsigned char &c=(unsigned char &)c0;
	if('\0'==c) return;
	if( (c>='A') && (c<='Z')) c+= ('z' - 'Z');
	else if( (c>=(unsigned char)'А') && (c<=(unsigned char)'Я')) c+= ('я' - 'Я');
	else if(c==(unsigned char)'Ё') c='ё';
};
#endif

extern int aplUtf8StrLen(const char *str);

bool bHasStringMultiBYTE(const char *str) // Возвращает true, если есть символы больше 1 байта
{
	if(0==str) return false;
	size_t i=0;
	while(true)
	{
		char c=str[i];
		if('\0'==c) return false;
		if(c&0x80) return true;
		i++;
	}
}

// Возвращает указатель на N-ый символ в UTF-8 строке и 0 в случае ошибки
char *FindNSymbolInUff8String(const char *str, int num)
{
    if(0==str) return 0;
    if(0==num) return (char*)str;
    if(num <0) return 0;
    if('\0'==str[0])return 0;

    int i=0;
    int iCount=0;
    while(iCount<num)
    {
        char c=str[i];
        if('\0'==c) return 0;
        if(0==(c&0x80)) i++; // англ. буква 1 байт
        else if(0xc0==(c&0xe0)) i+=2; // тут 2 байта
        else if(0xe0==(c&0xf0)) i+=3; // тут 3 байта
        else if(0xf0==(c&0xf8)) i+=4; // тут 4 байта
        else {APL_STOP_IF_DEBUG return 0;} // это кривой символ

        iCount++;
    }
    return (char*)str+i;
}

const unsigned char* sRusLow = (unsigned char*) "апряё";  // диапазоны букв для удобства проверки при UTF-8
const unsigned char* sRusUp  = (unsigned char*) "АПРЯЁ";

void aplUpperUtf8Symbol (unsigned char &c1, unsigned char &c2, int &nBYTEs)
{
    nBYTEs=1;
    if(0==c1) return;

    if(0==(c1&0x80)) // англ. буква: 1 байтовый символ
    {
        if(c1>='a' &&c1<='z') c1-='z'-'Z';
    }
    else if(0xc0==(c1&0xe0))  // тут 2 байта
    {
        nBYTEs=2;

        if(c1==sRusLow[0] && (c2>=sRusLow[1] && c2<=sRusLow[3]))
        {
            c1= sRusUp[0];
            c2-= sRusLow[1]-sRusUp[1];
        }
        else if(c1==sRusLow[4] && (c2>=sRusLow[5] && c2<=sRusLow[7]))
        {
            c1= sRusUp[4];
            c2-= sRusLow[5]-sRusUp[5];
        }
        else if(c1==sRusLow[8] && c2==sRusLow[9] )
        {
            c1= sRusUp[8];
            c2= sRusUp[9];
        }
    }
    else if(0xe0==(c1&0xf0)) nBYTEs=3; // тут 3 байта
    else if(0xf0==(c1&0xf8)) nBYTEs=4; // тут 4 байта
    else {} // это кривой символ
}

int aplCompareUtf8StringIgnoreCase(const char *s1, const char *s2)
{
    if(0==s1)
    {
        if(0==s2)  return 0;
        if(s2[0]=='\0')  return 0;
        return -1;
    }
    if(0==s2)  return 1;

    int i=-1;
    int nBYTEs1=1,nBYTEs2=1;
    while(true)
    {
        i+=nBYTEs1;
        nBYTEs1=1;

        unsigned char c11=((unsigned char*)s1)[i];
        unsigned char c21=((unsigned char*)s2)[i];

        if(c11=='\0')
        {
            if(c21=='\0')   return 0;
            return -1;
        }
        if(c21=='\0') return 1;

        if( (c11==c21) && (!(c11&0x80)) ) continue; // 2 одинаковых однобайтных символа (для ускорения)

        unsigned char c12=((unsigned char*)s1)[i+1];
        unsigned char c22=((unsigned char*)s2)[i+1];

        aplUpperUtf8Symbol(c11,c12,nBYTEs1);
        aplUpperUtf8Symbol(c21,c22,nBYTEs2);

        if(c11==c21)
        {
            if(!(c11&0x80)) continue;  // 2 одинаковых однобайтных символа
            if(c12==c22) continue;
            if(c12<c22) return -1;
            return 1;
        }
        if(c11<c21) return -1;
        return 1;
    }
    return 0;
};

//********************************************************************************************
//********************************************************************************************
//********************************************************************************************

CaplStringA::CaplStringA() :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	SetSize(m_length + 1);
	m_buffer[m_length] = '\0';
}

CaplStringA::CaplStringA(const CaplStringA& other) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	*this = other;
}

CaplStringA::CaplStringA(const char* c_str) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
	*this = c_str;
}

CaplStringA::CaplStringA(const wchar_t* w_str) :
	m_buffer(NULL)
	, m_length(0)
	, m_size(0)
{
	*this = w_str;
}

CaplStringA::CaplStringA(char ch, size_t nLength /*= 1*/) :
	m_buffer(NULL)
,	m_length(0)
,	m_size(0)
{
    SetSize(nLength + 1); 
	for (size_t i = 0; i < nLength; i++) m_buffer[i] = ch;
	m_length = nLength;
    m_buffer[m_length] = '\0';
}

/*
CaplStringA::CaplStringA(int iValue)
{
	Format("%d", iValue);
}

CaplStringA::CaplStringA(long lValue)
{
	Format("%d", lValue);
}
*/

CaplStringA::~CaplStringA()
{
	if (m_buffer)
	{
		delete[] m_buffer;
		m_buffer=0;
	}
}

LPSTR CaplStringA::GetBuffer(int nMinBufferLength /*= -1*/)
{
	if (nMinBufferLength > (int)m_size)
		SetSize(nMinBufferLength);

	return m_buffer;
}

LPSTR CaplStringA::GetBufferSetLength(int nNewLength)
{
	GetBuffer(nNewLength+1);
	SetLength(nNewLength);
	return m_buffer;
}

void CaplStringA::ReleaseBuffer(int nNewLength /* = -1 */)
{
	if (nNewLength > -1)
		SetLength(nNewLength);
}

void CaplStringA::SetLength(size_t new_length)
{
    if(m_length==new_length) return;
	if ((new_length+1)  > m_size) SetSize(new_length+1);
	m_length = new_length;
    m_buffer[m_length] = '\0';;
}

size_t CaplStringA::SetSize(size_t new_size)
{
	if (new_size == 0) // удаление строки
	{
		if(0==m_length && 1==m_size) return m_size;
		if (m_buffer) delete[] m_buffer;
		m_length=0;
		m_buffer = new char[1];
		m_size=1;
		m_buffer[m_length] = '\0';
		return m_size;
	}

	if(new_size==m_size) return m_size;
	if(new_size<(m_length+1)) new_size=(m_length+1);  

	if(new_size<=m_size) return m_size; // Оставляем буффер, если оно больше

	m_size = new_size;
    char* new_buffer = new char[m_size];
    if (m_buffer)
	{
        if(0!=m_length) memcpy(new_buffer,m_buffer,m_length*sizeof(char));
		new_buffer[m_length] = '\0';
		delete[] m_buffer;
	}
	m_buffer = new_buffer;
	return m_size;
}

void CaplStringA::Empty()
{
	SetSize(0);
}

CaplStringA& CaplStringA::operator=(const CaplStringA& other)
{
	if(this==&other) return *this; // На случай присваивания себя себя
	*this=other.GetCString();
	return *this;
}

CaplStringA& CaplStringA::operator=(const char* c_str)
{
	if(0==c_str || 0==c_str[0]) {Empty(); return *this;}

	if(m_buffer==c_str) return *this;  // На случай присваивания себя себя
	
    m_length = 0;
    size_t l=strlen(c_str);
    SetSize(l + 1);
    memcpy(m_buffer, c_str, l*sizeof(char));
    m_length=l;
	m_buffer[m_length] = _T('\0');
	return *this;
}

CaplStringA& CaplStringA::operator=(char ch)
{
    m_length = 0;
    SetSize(2);
    m_length = 1;
    m_buffer[0] = ch;
	m_buffer[m_length] = '\0';
	return *this;
}

CaplStringA& CaplStringA::operator=(const wchar_t* w_str)
{
    if(0==w_str || L'\0'==w_str[0]) {Empty(); return *this;}

	//*this=LPSTR(CaplStringAdapter(w_str));  это двойное копирование сконвертированной строчки

#ifndef __GNUC__

    int la = ::WideCharToMultiByte(CP_ACP, 0, w_str, -1, NULL, 0, NULL, NULL);

    if(la<=0) Empty();
    else
    {
        SetSize(la + 1);
        int len2= ::WideCharToMultiByte(CP_ACP, 0,  w_str, -1, m_buffer, m_size,  NULL, NULL);

        if(len2<=0) Empty();
        else
        {
            m_length=len2-1;
            m_buffer[m_length] = '\0'; // На всякий случай
        }
    }

#else

    int lw = wcslen(w_str);
    int sw = sizeof(wchar_t)*(lw+1); // Будем считать, что все буквы русские, а >2 байт в UTF-8 не будет

	BYTE *out_buf=0;
	int out_buf_len=0, out_buf_size=0;
	bConvertTextWithIconv(aplCurWcharCode,(BYTE*)w_str,sw,aplCurCharCode,out_buf,out_buf_len, &out_buf_size);

	if(0!=out_buf)
	{
		if(m_buffer)delete [] m_buffer;
		m_buffer=(char*)out_buf;
		m_size=(int)out_buf_size;
		m_length=strlen(m_buffer); // в принципе, это out_buf_len-1, но так надёжнее
	}
	else Empty();

#endif

    return *this;
}

CaplStringA& CaplStringA::operator+=(const CaplStringA& other)
{
	return *this += other.GetCString();
}

CaplStringA& CaplStringA::Append(LPCSTR other, int nCount /* = -1 */)
{
    size_t length_other = strlen(other);
	size_t nC = nCount < 0 ? length_other : nCount;
	
	if (nC > length_other)	nC = length_other;
	if (nC > 0)
	{
		size_t old_length = m_length;
		SetSize(m_length + nC +1);
		m_length += nC;
        LPSTR pNewStr = m_buffer + old_length;
        memcpy(pNewStr, other, nC*sizeof(char));
		m_buffer[m_length] = '\0';
	}
	return *this;
}

CaplStringA& CaplStringA::operator+=(LPCSTR other)
{
	if(0==other) return *this;
	if('\0'==other[0]) return *this;

	if(other==m_buffer) // Добавление себя к себе
	{
		// Ситуация редкая, поэтому с оптимальностью не заморачиваемся
		CaplStringA sTmp(*this);
		return (*this)+=sTmp;
	}

    size_t length_other = strlen(other);
	if (length_other > 0)
	{
		size_t old_length = m_length;

		SetSize(m_length + length_other +1);
		m_length += length_other;
		LPSTR pNewStr = m_buffer + old_length;
        memcpy(pNewStr, other, length_other*sizeof(char));
		m_buffer[m_length] = '\0';
	}
	return *this;
}

CaplStringA& CaplStringA::operator+=(char ch)
{

#ifdef __GNUC__
    // Это многобайтовый символ UTF-8 его так просто добавлять нельзя
    if(ch&0x80) {  APL_STOP_IF_DEBUG    return *this; }
#endif

    // Чаще всего эта функция используется при большом кол-ве добавлений.
	// Поэтому лучше сразу выделить буффер побольше
	if((m_length + 2)>m_size) SetSize(m_length + 16);

	m_buffer[m_length] = ch;
	m_length++;
	m_buffer[m_length] = '\0';
	return *this;
}

int CaplStringA::Format(LPCSTR _format, ...)
{
    int bufLen = APL_MAX_STRING_LENGTH;
	LPSTR pBuf = 0;
    int iRes = -1;
#ifdef _MFC_VER
    int maxBufLen = MAXLONG32/2;
#else
    int maxBufLen = MAX_INT32/2;
#endif
    while(true)
    {
		if(pBuf != 0) delete[] pBuf;
        pBuf = new char[bufLen];

        va_list marker;
        va_start(marker, _format);	// устанавливаем маркер на 1-й неименованный параметр
        // передаем список неименованных параметров аналогу функции sprintf и пишем в наш буфер
		iRes = vsnprintf(pBuf, bufLen, _format, marker);
        va_end(marker);				// очищаем

        if(iRes == -1)
        {
#ifdef _MFC_VER
			// Микрософтовский vsnprintf возвращает -1 при любой ошибке, даже если буфера не хватило. Увеличиваем буфер.
			bufLen *= 4;
            if((UINT)bufLen > (UINT)maxBufLen)
			{
                *this = _T("<Error: arguments too long for memory.>");
				break;
			}
#else
			// GNUшный vsnprintf возвращает -1 только при неустранимой ошибке 
#ifdef __linux__
            char buf[256];
            *this = strerror_r(errno, buf, sizeof(buf));
#else
            *this =  strerror(errno);

#endif
			break;
#endif
        }
        else if(iRes + (int)sizeof(char) <= bufLen)
        {
            *this = pBuf;
            break;
        }
#ifndef _MFC_VER
		// Размер буфера возвращает только GNUшный vsnprintf
        else
        {
            bufLen = iRes + sizeof(char);
            if((UINT)bufLen > (UINT)maxBufLen)
            {
                *this = _T("<Error: arguments too long for memory.>");
                break;
            }
        }
#endif
    }
	if(pBuf != 0) delete[] pBuf;
	return iRes;
}

int CaplStringA::Compare(LPCSTR other, bool bCaseSensitive /*= true*/) const
{
	if(0==m_buffer && 0==other) return 0;
	if(0!=m_buffer && 0==other) return 1;
	if(0==m_buffer && 0!=other) return -1;

	if (bCaseSensitive) return strcmp(m_buffer, other);
		
	// ЯАИ _stricmp и strcasecmp некорректно работают с русскими буквами
	// нормально работающего варианта найти не осилил
	// Сделал 'в лоб'. (Аналогично MakeLower и MakeUpper)

	if(0==m_length)
	{
		if(0==other)  return 0;
		if(other[0]=='\0')  return 0;
		return -1;
	}

	if(0==other)  return 1;

#ifdef _MFC_VER

	size_t i;
	for (i = 0; i < m_length; ++i)
	{
		char c1=m_buffer[i];
		char c2=other[i];

		if(c1==c2)
		{
			if(c1=='\0') return 0;
			continue;
		}
		if(c2=='\0') return 1; // other короче

		aplUpperChar(c1);
		aplUpperChar(c2);

		if(c1==c2) continue;
		if(c1<c2) return -1;
		return 1;
	}
	if(other[i]!='\0') return -1; // other длиннее
#else

    return aplCompareUtf8StringIgnoreCase(m_buffer, (const char*)other);

#endif

	return 0;
}

int CaplStringA::Delete(size_t iIndex, size_t nCount /* = 1 */)
{
	if ((int)iIndex<0) iIndex=0;
	if ( 0==nCount || 0==m_length || iIndex >= m_length ) return m_length;

#ifdef __GNUC__
	// Функция не доработана для работы с русским языком в UTF-8
	// Если в строке будут русские символы, то отладчик тут остановитсяж
    //if(bHasStringMultiBYTE(m_buffer)) {  APL_STOP_IF_DEBUG    return m_length;  }

    char *start=m_buffer;
    if(iIndex>0) start=FindNSymbolInUff8String(start,iIndex);
    if(0==start) return m_length; // начало больше длинны

    char *end=0;
    if(nCount>0) end=FindNSymbolInUff8String(start,nCount);

    if(0==end)
    {
        m_length=start-m_buffer;
        m_buffer[m_length]='\0';
    }
    else
    {
        int dn=end-start;
		int ds=start-m_buffer;
		for(size_t i=0; (ds+i+dn)<=m_length; i++)
        {
            start[i] = start[i + dn];
        }
        m_length-=dn;
    }
    //int g=strlen(m_buffer);
    //if(g!=m_length) APL_STOP_IF_DEBUG
    return m_length;

#else

    if(iIndex + nCount > m_length) nCount =m_length - iIndex;

	for(size_t i=iIndex; i<=(m_length-nCount); i++)
	{
		m_buffer[i] = m_buffer[i + nCount];
	}
	m_length -= nCount;
	m_buffer[m_length] ='\0';  //На всякий случай
	return nCount;

#endif
}

// конкатенация двух строк
CaplStringA operator+(const CaplStringA& str1, const CaplStringA& str2)
{
	CaplStringA sResult = str1;
	sResult += str2;
	return sResult;
}

CaplStringA operator+(const CaplStringA& str, char ch)
{
	CaplStringA sResult = str;
	sResult += ch;
	return sResult;
}

CaplStringA operator+(char ch, const CaplStringA& str)
{
	CaplStringA sResult = ch;
	sResult += str;
	return sResult;
}

int CaplStringA::Find(LPCSTR psSub, size_t iStart /* = 0 */) const
{
    if(0==m_length) return -1;
    if (iStart >= m_length)	return -1;

    if(0==psSub) return 0;
    if('\0'==psSub[0]) return 0;

    if ((int)iStart<0) iStart=0;

	size_t iSubLength = strlen(psSub);
	size_t nLimit = m_length - iSubLength + 1;	// последний возможный индекс в строке, чтобы можно было вместить искомую подстроку

    size_t i = iStart;
    char cf=psSub[0];

    while (i<nLimit)
    {
        char c0=m_buffer[i];
        if (cf == c0)
        {
           bool bEqu=true;
           for(size_t j=1; j<iSubLength;j++)
           {
               if(m_buffer[i+j]!=psSub[j]) {bEqu=false; break;}
           }
           if(bEqu) return i;
        }

#ifdef __GNUC__
        if(c0&0x80) // Это многобайтовый символ UTF-8.
        {
            if(0xc0==(c0&0xe0)) i+=1; // тут 2 байта
            else if(0xe0==(c0&0xf0)) i+=2; // тут 3 байта
            else if(0xf0==(c0&0xf8)) i+=3; // тут 4 байта
            else {APL_STOP_IF_DEBUG return -1;} // это кривой символ
        }
#endif

        i++;
    }
    return -1;
}

int CaplStringA::Find(char ch, size_t iStart /* = 0 */) const
{
#ifdef __GNUC__
    // Это многобайтовый символ UTF-8. Значит вызов был некорректен
    if( ch&0x80) {  APL_STOP_IF_DEBUG    return -1;  }
#endif

    if(0==m_length) return -1;
    if (iStart >= m_length)	return -1;
	if ((int)iStart<0) iStart=0;

	for (size_t i = iStart; i < m_length; i++)
	{
        char c0=m_buffer[i];
        if (ch == c0) return i;
#ifdef __GNUC__
        if(c0&0x80) // Это многобайтовый символ UTF-8.
        {
            if(0xc0==(c0&0xe0)) i+=1; // тут 2 байта
            else if(0xe0==(c0&0xf0)) i+=2; // тут 3 байта
            else if(0xf0==(c0&0xf8)) i+=3; // тут 4 байта
            else {APL_STOP_IF_DEBUG return 0;} // это кривой символ
        }
#endif
    }
	return -1;
}

int CaplStringA::ReverseFind(char ch) const
{
#ifdef __GNUC__
    // Это многобайтовый символ UTF-8. Значит вызов был некорректен
    if( ch&0x80) {  APL_STOP_IF_DEBUG    return -1;  }
#endif

    if(0==m_length) return -1;

#ifdef __GNUC__
    // Функция не доработана для работы с русским языком в UTF-8
    // Если в строке будут русские символы, то отладчик тут остановитсяж
    //if(bHasStringMultiBYTE(m_buffer)) {  APL_STOP_IF_DEBUG    return 0;  }
#endif

	for (int i = m_length - 1; i >= 0; i--)
	{
        if (ch == m_buffer[i])
        {
#ifdef __GNUC__
            if(i>0)
            {
				if(0!=((m_buffer[i-1])&0x80)) continue; // Это многобайтовый символ UTF-8, ищем явно не его
            }
#endif
            return i;
        }
	}
	return -1;
}

CaplStringA CaplStringA::Left(int nCount) const
{
    if(0==m_length) return *this;

#ifdef __GNUC__

    CaplStringA sLeft;
    if(nCount>0)
    {
        char *strn= FindNSymbolInUff8String(m_buffer,nCount);
        if(0==strn) return *this; // не нашлось столько символов
        sLeft.Append(m_buffer, strn-m_buffer);
    }
    return sLeft;

#else
    if(nCount>=(int)m_length) return *this;
    CaplStringA sLeft;
    if (nCount>0)  sLeft.Append(m_buffer, nCount);
    return sLeft;
#endif
}

CaplStringA CaplStringA::Right(int nCount) const
{
    if(0==m_length) return *this;

#ifdef __GNUC__

    CaplStringA sRight;
    if(nCount>0)
    {
        int sLen=aplUtf8StrLen(m_buffer);
        if(nCount>=sLen) return *this;
        char *strn= FindNSymbolInUff8String(m_buffer,sLen-nCount);
        if(0==strn) return *this; // не нашлось столько символов
        sRight.Append(strn, m_length-(strn-m_buffer));
    }
    return sRight;

#else

    if(nCount>=(int)m_length) return *this;
    CaplStringA sRight;
    if (nCount>0)  sRight.Append((m_buffer + (m_length - nCount)), nCount);
	return sRight;

#endif
}
CaplStringA CaplStringA::Mid(int iFirst) const
{
	return( Mid( iFirst, m_length-iFirst) );
}


CaplStringA CaplStringA::Mid(int iFirst, int nCount) const
{
    if(0==m_length) return *this;
    if (iFirst < 0)  iFirst = 0;
    
	CaplStringA sMid; 
	if (nCount <= 0)  return sMid;

#ifdef __GNUC__

    if(0==nCount) return sMid;

    char *start=m_buffer;
    if(iFirst>0) start=FindNSymbolInUff8String(start,iFirst);
    if(0==start) return sMid; // начало больше длинны

    char *end=0;
    if(nCount>0) end=FindNSymbolInUff8String(start,nCount);
    if(0==end) end=m_buffer+m_length; // конец больше длинны
    sMid.Append(start, end-start);
    return sMid;

#else

    if( iFirst >= (int)m_length ) nCount = 0;
    if( (iFirst+nCount) > (int) m_length ) nCount = (int)m_length-iFirst;
    if(0==iFirst && nCount == (int)m_length) return( *this );

    if(nCount>0)
    {
        LPCSTR pMidBuffer = m_buffer + iFirst;
        sMid.Append(pMidBuffer, nCount);
    }
    return sMid;

#endif

}
#ifdef _MFC_VER
CaplStringA& CaplStringA::MakeReverse()
{
	if (!m_buffer || !*m_buffer) return  *this;

#ifdef __GNUC__
	// Функция не доработана для работы с русским языком в UTF-8
	// Если в строке будут русские символы, то отладчик тут остановитсяж
	if(bHasStringMultiBYTE(m_buffer)) {  APL_STOP_IF_DEBUG    return *this;  }
#endif

	char *p1, *p2;
	for (p1 = m_buffer, p2 = m_buffer + strlen(m_buffer) - 1; p2 > p1; ++p1, --p2)
	{
		*p1 ^= *p2;
		*p2 ^= *p1;
		*p1 ^= *p2;
	}
	return *this;
}
#endif

CaplStringA& CaplStringA::MakeLower()
{
	if(0==m_length) return *this;
	// ЯАИ: под QT Creator не работает с русским языком
	//for (size_t i = 0; i < m_length; ++i)		m_buffer[i] = tolower(m_buffer[i]);
	// Нормальной реализации не нашел. сделал в лоб  (аналогично и в MakeUpper())

#ifdef _MFC_VER
	for (size_t i = 0; i < m_length; i++) aplLowerChar(m_buffer[i]);
#else

	for (size_t i = 0; i < m_length; i++)
	{
		unsigned char c=m_buffer[i];
		if(0==c) break;
		if(0==(c&0x80)) // англ. буква
		{
			if(c>='A' &&c<='Z') m_buffer[i]+='z'-'Z';
		}
		else if(0xc0==(c&0xe0)) // тут 2 байта
		{
			unsigned char c1= m_buffer[i+1];

			if(c==sRusUp[0] && (c1>=sRusUp[1] && c1<=sRusUp[3]))
			{
				m_buffer[i]= sRusLow[0];
				m_buffer[i+1]+= sRusLow[1]-sRusUp[1];
			}
			else if(c==sRusUp[4] && (c1>=sRusUp[5] && c1<=sRusUp[7]))
			{
				m_buffer[i]= sRusLow[4];
				m_buffer[i+1]+= sRusLow[5]-sRusUp[5];
			}
			else if(c==sRusUp[8] && c1==sRusUp[9] )
			{
				m_buffer[i]= sRusLow[8];
				m_buffer[i+1]= sRusLow[9];
			}

			i++;
		}
		else if(0xe0==(c&0xf0)) i+=2; // тут 3 байта
		else if(0xf0==(c&0xf8)) i+=3; // тут 4 байта
		else {} // это кривой символ
	}
#endif
	return *this;
}

CaplStringA& CaplStringA::MakeUpper()
{
	if(0==m_length) return *this;

	// ЯАИ: под QT Creator не работает с русским языком
	// for (size_t i = 0; i < m_length; ++i)	m_buffer[i] = toupper(m_buffer[i]);
	// Нормальной реализации не нашел. сделал в лоб  (аналогично и в MakeLower())

#ifdef _MFC_VER
	for (size_t i = 0; i < m_length; i++) aplUpperChar(m_buffer[i]);
#else
    /* Это рабочий вариант, оставлен как контрольный
    for (size_t i = 0; i < m_length; i++)
    {
        unsigned char c=m_buffer[i];
        if(0==c) break;
        if(0==(c&0x80)) // англ. буква
        {
            if(c>='a' &&c<='z') m_buffer[i]-='z'-'Z';
        }
        else if(0xc0==(c&0xe0)) // тут 2 байта
        {
            unsigned char c1= m_buffer[i+1];

            if(c==sRusLow[0] && (c1>=sRusLow[1] && c1<=sRusLow[3]))
            {
                m_buffer[i]= sRusUp[0];
                m_buffer[i+1]-= sRusLow[1]-sRusUp[1];
            }
            else if(c==sRusLow[4] && (c1>=sRusLow[5] && c1<=sRusLow[7]))
            {
                m_buffer[i]= sRusUp[4];
                m_buffer[i+1]-= sRusLow[5]-sRusUp[5];
            }
            else if(c==sRusLow[8] && c1==sRusLow[9] )
            {
                m_buffer[i]= sRusUp[8];
                m_buffer[i+1]= sRusUp[9];
            }

            i++;
        }
        else if(0xe0==(c&0xf0)) i+=2; // тут 3 байта
        else if(0xf0==(c&0xf8)) i+=3; // тут 4 байта
        else {} // это кривой символ
    }
    */

    // Это вариант c общей функцией
    for (size_t i = 0; i < m_length; i++)
    {
        if(0==m_buffer[i]) break;
        int n=0;
        aplUpperUtf8Symbol(((unsigned char*)m_buffer)[i], ((unsigned char*)m_buffer)[i+1],n);
        if(n>1) i+=(n-1);

    }

#endif
	return *this;
}



int CaplStringA::Remove(char chRemove)
{
#ifdef __GNUC__
	// Функция не доработана для работы с русским языком в UTF-8
	// Если в строке будут русские символы, то отладчик тут остановитсяж
	if(bHasStringMultiBYTE(m_buffer)) {  APL_STOP_IF_DEBUG    return 0;  }
#endif

	int iFind(0), nCount(0);
	while (-1 < (iFind = Find(chRemove, iFind)))
	{
		Delete(iFind);
		++nCount;
	}

	return nCount;
}

int CaplStringA::Insert(int iIndex, LPCSTR pcInsert)
{
    if(0==pcInsert) return m_length;
    if('\0'==pcInsert[0]) return m_length;
    if(iIndex<0) iIndex=0;
    else if(iIndex>(int)m_length)iIndex=m_length;

    int len2=strlen(pcInsert);
    SetSize(m_length+len2+1);

    int i,iStart;

#ifdef _MFC_VER
    iStart=iIndex;
#else
    if(0==iIndex) iStart=0;
    else
    {
        char *sStart=FindNSymbolInUff8String(m_buffer, iIndex);
        if(0!=sStart)iStart=sStart-m_buffer;
        else iStart=m_length; // Не нашлось столько символов - добавляем в конец
    }
#endif

    for(i=m_length; i>=iStart; i--)m_buffer[i+len2]=m_buffer[i];

    for(i=0;i<len2;i++) m_buffer[i+iStart]=pcInsert[i];

    m_length+=len2;
    //m_buffer[m_length]='\0';
	return m_length;
}

int CaplStringA::Insert(int iIndex, char chInsert)
{
#ifdef __GNUC__
    // Это многобайтовый символ UTF-8. Значит вызов был некорректен
    if( chInsert&0x80) {  APL_STOP_IF_DEBUG    return m_length;  }
#endif

    if('\0'==chInsert) return m_length;
    if(iIndex<0) iIndex=0;
    else if(iIndex>(int)m_length)iIndex=m_length;

    SetSize(m_length+2);

    int i,iStart;

#ifdef _MFC_VER
    iStart=iIndex;
#else
    if(0==iIndex) iStart=0;
    else
    {
        char *sStart=FindNSymbolInUff8String(m_buffer, iIndex);
        if(0!=sStart)iStart=sStart-m_buffer;
        else iStart=m_length; // Не нашлось столько символов - добавляем в конец
    }
#endif

    for(i=m_length; i>=iStart; i--) m_buffer[i+1]=m_buffer[i];

    m_buffer[iStart]=chInsert;

    m_length++;
    //m_buffer[m_length]='\0';
    return m_length;
}

int CaplStringA::Replace(LPCSTR pcOld, LPCSTR pcNew)
{
	if(0==m_length) return 0;
    int nOldLength = strlen(pcOld);
    if(0==nOldLength) return 0;
    int nNewLength = strlen(pcNew);
    int iFind(0), nCount(0);
	while (-1 < (iFind = Find(pcOld, iFind)))
	{
        if ( nNewLength == nOldLength)
		{
			for (int i = 0; i < nOldLength; ++i)
			{
				m_buffer[iFind + i] = pcNew[i];
			}
		}
		else
		{
            //Delete(iFind, nOldLength); // Надо в байтах, а не в символах
            int i;
            for(i=iFind; i<=((int)m_length-nOldLength); i++) m_buffer[i] = m_buffer[i + nOldLength];
            m_length -= nOldLength;

            //Insert(iFind, pcNew); // Надо в байтах, а не в символах
            SetSize(m_length+nNewLength+1);
            for(i=m_length; i>=iFind; i--)m_buffer[i+nNewLength]=m_buffer[i];
            for(i=0;i<nNewLength;i++) m_buffer[i+iFind]=pcNew[i];

            m_length+=nNewLength;
		}
		iFind += nNewLength;
		++nCount;
	}

	return nCount;
}

int CaplStringA::Replace(char chOld, char chNew)
{
	if(0==m_length) return 0;
#ifdef __GNUC__
    // Это многобайтовые символы UTF-8. Значит вызов был некорректен
    if( chOld&0x80  ||  chNew&0x80 ) { APL_STOP_IF_DEBUG return 0;  }
#endif

	int nCount=0;
	for(size_t i=0; i<m_length; i++) {
        char c0=m_buffer[i];
        if( chOld==c0) { m_buffer[i]=chNew; nCount++; }

#ifdef __GNUC__
        if(c0&0x80) // Это многобайтовый символ UTF-8.
        {
            if(0xc0==(c0&0xe0)) i+=1; // тут 2 байта
            else if(0xe0==(c0&0xf0)) i+=2; // тут 3 байта
            else if(0xf0==(c0&0xf8)) i+=3; // тут 4 байта
            else {APL_STOP_IF_DEBUG return -1;} // это кривой символ
        }
#endif
	}
	return nCount;
}

#ifdef _MFC_VER

bool CaplStringA::LoadString(void * hInstance, unsigned int nId)
{
    if(0==hInstance || 0==nId) return false;

#ifdef __GNUC__
    // Функция работает только в VisualStudio
    APL_STOP_IF_DEBUG
    return false;
#endif

	CMFCString sMFCStr;
	bool bRet = sMFCStr.LoadString((HINSTANCE)hInstance, nId)?true:false;
	if (bRet)
		*this = sMFCStr;

	return false;
}
#endif

char CaplStringA::operator[](int index)
{
	if (index < 0 || (size_t)index >= m_length)
	{
		APL_STOP_IF_DEBUG // Выход за пределы массива
		APL_WRITE_TO_LOG(_T("CaplStringA::operator[] -> Выход за пределы массива"))
		return '\0';
	}
	return m_buffer[index];
}

char CaplStringA::GetAt(int iChar ) const
{
	if (iChar < 0 || (size_t)iChar >= m_length)
	{
		APL_STOP_IF_DEBUG // Выход за пределы массива
			APL_WRITE_TO_LOG(_T("CaplStringA::operator[] -> Выход за пределы массива"))
			return '\0';
	}
	return m_buffer[iChar];
}

CaplStringA& CaplStringA::Trim(char chTarget)
{
	return (TrimRight(chTarget).TrimLeft(chTarget));
}

CaplStringA& CaplStringA::Trim(LPCSTR pszTargets)
{
	return (TrimRight(pszTargets).TrimLeft(pszTargets));
}

CaplStringA& CaplStringA::Trim()
{
	return (TrimRight().TrimLeft());
}

CaplStringA& CaplStringA::TrimLeft(char chTarget)
{
	if(0==m_length) return (*this);

#ifdef __GNUC__
    // Это многобайтовый символ UTF-8. Значит вызов был некорректен
    if( chTarget&0x80) {  APL_STOP_IF_DEBUG    return (*this); }
#endif

	size_t i;
	for(i=0; i<=m_length; i++)
	{
		if( m_buffer[i]!=chTarget) break;
	}
	if(0==i) return (*this);
	if(i==m_length) { Empty(); return (*this);}

	for(size_t j=i; j<=m_length; j++)
		m_buffer[j-i] = m_buffer[j];

	m_length-=i;
	return (*this);
}

bool aplFindCharInString(LPCSTR pszTargets, char chFind)
{
	bool bFind(false);
	for(size_t i=0; i<strlen(pszTargets); ++i)
	{
		if (pszTargets[i]==chFind)
		{
			bFind = true;
			break;
		}
	}
	return bFind;
}


CaplStringA& CaplStringA::TrimLeft(LPCSTR pszTargets)
{
    if(0==pszTargets) return (*this);
    if(0==pszTargets[0]) return (*this);

#ifdef __GNUC__
    // Функция не доработана для работы с русским языком в UTF-8
    if(bHasStringMultiBYTE(m_buffer)) { APL_STOP_IF_DEBUG return (*this); }
    if(bHasStringMultiBYTE(pszTargets)) { APL_STOP_IF_DEBUG return (*this); }
    APL_STOP_IF_DEBUG
#else

	while(m_length>0 && aplFindCharInString(pszTargets, m_buffer[0]))
	{
		for(size_t i=0; i<(m_length-1); ++i)
			m_buffer[i] = m_buffer[i+1];
		--m_length;
	}

#endif
	return (*this);
}

CaplStringA& CaplStringA::TrimLeft()
{
	return TrimLeft(' ');
}

CaplStringA& CaplStringA::TrimRight(char chTarget)
{
	if(0==m_length) return (*this);

	int i;
	for(i=m_length-1; i>=0; i--)
	{
		if( m_buffer[i]!=chTarget) break;

#ifdef __GNUC__

        if(i>0)  { if(0x80&(m_buffer[i-1])) break;  } // Если символ многобайтовый ...
#endif

	}

	if(i<0) Empty();
	else if(i<(int)(m_length-1))
	{
		i++;
		m_buffer[i]='\0';
		m_length=(size_t)i;
	}
	return (*this);
}

CaplStringA& CaplStringA::TrimRight(LPCSTR pszTargets)
{
#ifdef __GNUC__
	if(bHasStringMultiBYTE(m_buffer))
	{
		//Функция не доработана для работы с русским языком в UTF-8"
		APL_STOP_IF_DEBUG
		APL_WRITE_TO_LOG(_T("Вызов CaplStringA::TrimRight для UTF-8 c русскими буквами"));
		return (*this);
	}
#endif

	while(m_length>0 && aplFindCharInString(pszTargets, m_buffer[m_length-1]))
	{
		m_buffer[m_length-1] = '\0';
		--m_length;
	}

	return (*this);
}

CaplStringA& CaplStringA::TrimRight()
{
	return TrimRight(' ');
}

int aplStringSpanIncludingA( LPCSTR pszPlace, LPCSTR pszTokens,  bool bInclude)
{
	int i=0;
	while(true)
	{
		char ci=pszPlace[i];
		if(ci=='\0') return i;
		int j=0;
		bool bfound =false;
		while(true) 
		{
			char cj=pszTokens[j];
			if(cj=='\0') break;
			if(ci==cj) {bfound=true; break;}
			j++;
		}
		if(bInclude)
		{
			if(!bfound) return i;
		}
		else
		{
			if(bfound) return i;
		}
		i++;
	} 
	ASSERT(FALSE);
	return -1; // По идее, этого быть не должно
}

CaplStringA CaplStringA::Tokenize(LPCSTR pszTokens, int& iStart)
{
	if(0==m_buffer) {iStart=-1; return "";}
	if(iStart < 0) return "";

	ASSERT( iStart >= 0 );

	if( (pszTokens == NULL) || (*pszTokens == (TCHAR)0) )
	{
		if (iStart < (int)GetLength()) return Right(GetLength()-iStart);
	}
	else
	{
		char* pszPlace = m_buffer + iStart;
		char* pszEnd = m_buffer + GetLength();
		if( pszPlace < pszEnd )
		{
			//int nIncluding = StringTraits::StringSpanIncluding( pszPlace,	pszTokens );
			int nIncluding = aplStringSpanIncludingA( pszPlace,	pszTokens,true );

			if( (pszPlace+nIncluding) < pszEnd )
			{
				pszPlace += nIncluding;
				//int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );
				int nExcluding = aplStringSpanIncludingA( pszPlace, pszTokens,false );

				int iFrom = iStart+nIncluding;
				int nUntil = nExcluding;
				iStart = iFrom+nUntil+1;

				return Mid( iFrom, nUntil );
			}
		}
	}

	// return empty string, done tokenizing
	iStart = -1;
	return("");
}


BOOL CaplStringA::GetEnvironmentVariable(LPCSTR pszVar )
{
	Empty();
	if(0==pszVar) return FALSE;
	if('\0'==pszVar[0]) return FALSE;

#ifdef _MFC_VER   

	size_t len=0;
	getenv_s(&len,0,0,pszVar);
	if(len<=0) return FALSE;
	len++;
	SetSize(len);
	getenv_s(&len,m_buffer,len,pszVar); 
	if(len>0)
	{
		m_length=len-1;
		m_buffer[m_length]='\0';
	}

#else 

	char *env=getenv(pszVar); // В Астре нет  _wgetenv
	if(0==env) return FALSE;
	if('\0'==env[0]) return FALSE;
	*this=env;

#endif

	return TRUE;
}


#endif
