/* _GNU_SOURCE required for compilation with obsolete 2.95.x GCC */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */

#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>

// templates
#include "flist.h"
#include "fstring.h"

//---------------------------------------------------------------------------
//                                                                                  !\" $%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~
static const unsigned char TO_LOWER_TBL_CZ[]=    "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~szlaszlraaalcceeidnooouutr ";
static const unsigned char TO_UPPER_TBL_CZ[]=    "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~LDOOUT ";
static const unsigned char TO_7BIT_TBL[] =       "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~SSTZzsstzzLASZlasllzRAAAALCCCEEEEIIDDNNOOOORUUUUYTraaaalccceeeeiiddnnooooruuuuyt ";
static const unsigned char TO_7BIT_LOWER_TBL[] = "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~sstzzsstzzlaszlasllzraaaalccceeeeiiddnnooooruuuuytraaaalccceeeeiiddnnooooruuuuyt ";
//---------------------------------------------------------------------------
//inline char ToSame(char ch) {return ch;}
char To7Bit(char ch) {return TO_7BIT_TBL[(unsigned char)ch];}
char To7BitLower(char ch) {return TO_7BIT_LOWER_TBL[(unsigned char)ch];}
char ToUpperCZ(char ch) {return (char)TO_UPPER_TBL_CZ[(unsigned char)ch];}
char ToLowerCZ(char ch) {return (char)TO_LOWER_TBL_CZ[(unsigned char)ch];}
//---------------------------------------------------------------------------
//                                               "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~
static const unsigned char CZ_SORT_TBL[]=        "\0                                !\"#$%&'()*+,-./0123456789:;<=>?@ABCEGHIJLMNOPQSTUVXZ\\]^_`acdefghABCEGHIJLMNOPQSTUVXZ\\]^_`aijklY[bY[bAADGGGLFRSSW\\\\\\`AADGGGLFRSSW\\\\\\`";
//priradi ceskym znakum hodnoty tak, aby je slo srovnat podle abecedy
//nerozlisuje mala/velka a carky krouzky
//hacky rozlisuje
//---------------------------------------------------------------------------
byte CzSortValue(byte ch) { return CZ_SORT_TBL[ch];}
//---------------------------------------------------------------------------
int StrMatch(const char *str, const char *pattern, pfnTranslateChar tr, int len)
{
    const char *s = str;
    const char *p = pattern;
    int i, token_len;
    bool asterisk;

    if(!tr) goto NotFound;
    if(!s || !p) goto NotFound;

    while(*p) {
        asterisk = false;
        if(*p == '*') {
            while(*p == '*') p++;   // skupina * je jako jedna *
            if(!*p) break; // ukoncujici *
            // * znamena, ze mam libovolny pocet pokusu najit to, co je za ni
            asterisk = true;
        }
        for(token_len=0; p[token_len] && p[token_len]!='*'; token_len++);
        //hledej token
        for(;;) {
            for(i=0; i<token_len && s[i] && s+i-str<len; i++) {
                if(p[i] != '?') if(tr(s[i]) != tr(p[i])) break;
            }
            if(i == token_len) {
                //neco nasel
                if(!p[i] && (!s[i] || s+i-str>=len)) {
                    // token je posledni a string je prozkouman dokonce uspech
                    p += i; s += i;
                    break;
                }
                else if(p[i]) {
                    // je dalsi token, zkoumej dalsi token
                    s += i;
                    // nastav p na dalsi token
                    p += i;
                    break;
                }
                // neni dalsi token a string neni prozkouman dokonce zkoumej dal string
            }
            // pokud token nezacinal * tak smula
            if(!asterisk) goto NotFound;
            // zkus dalsi posici, kdyz je to mozny
            if(*s && s-str<len) s++;
            else goto NotFound;
        }
    }
    return s - str;
NotFound:
    return -1;
}
//---------------------------------------------------------------------------
int StrCaseMatch(const char *str, const char *pattern, int len)
{
    return StrMatch(str, pattern, ToSame, len);
}
//---------------------------------------------------------------------------
int StrICaseMatch(const char *str, const char *pattern, int len)
{
    return StrMatch(str, pattern, ToLowerCZ, len);
}
//---------------------------------------------------------------------------
int StrCaseMatchCZ(const char *str, const char *pattern, int len)
{
    return StrMatch(str, pattern, To7Bit, len);
}
//---------------------------------------------------------------------------
int StrICaseMatchCZ(const char *str, const char *pattern, int len)
{
    return StrMatch(str, pattern, To7BitLower, len);
}
//---------------------------------------------------------------------------
static int cmpTrivial(const char *str1, const char *str2, int len1, int len2)
{
    if(!str1) len1 = 0;
    else if(!*str1) len1 = 0;
    if(!str2) len2 = 0;
    else if(!*str2) len2 = 0;

    if(len1 == 0 && len2 == 0) return 0;
    if(len1 == 0) return -1;
    if(len2 == 0) return 1;

    return 100;
}
//---------------------------------------------------------------------------
int StrICaseCZCmp(const char *str1, const char *str2, int len1, int len2)
{
    int ret = cmpTrivial(str1, str2, len1, len2);
    if(ret <= 1) return ret;

    unsigned char *pc1=(unsigned char*)str1;
    unsigned char *pc2=(unsigned char*)str2;
    unsigned char c1,c2;

    while(1) {
        if((*pc1 == 'c' || *pc1 == 'C') && (*(pc1+1) == 'h' || *(pc1+1) == 'H')) {//mame tu ch
            c1 = CZ_SORT_TBL[(int)'h'] + 1;
            pc1++;
        } else c1 = CZ_SORT_TBL[*pc1];

        if((*pc2 == 'c' || *pc2 == 'C') && (*(pc2+1) == 'h' || *(pc2+1) == 'H')) {//mame tu ch
            c2 = CZ_SORT_TBL[(int)'h'] + 1;
            pc2++;
        } else c2 = CZ_SORT_TBL[*pc2];

        if((pc1 - (byte*)str1) >= len1 || c1 == 0) {
            if((pc2 - (byte*)str2) >= len2 || c2 == 0) return 0;
            else        return -1;
        }
        if((pc2 - (byte*)str2) >= len2 || c2 == 0) return 1;
        if(c1 < c2) return -1;
        if(c1 > c2) return 1;
        pc1++;pc2++;
    }
}
//---------------------------------------------------------------------------
int StrICaseCmp(const char *str1, const char *str2, int len1, int len2)
{
    int ret = cmpTrivial(str1, str2, len1, len2);
    if(ret <= 1) return ret;

    unsigned char *pc1=(unsigned char*)str1;
    unsigned char *pc2=(unsigned char*)str2;
    unsigned char c1,c2;
    while(1) {
      c1 = TO_LOWER_TBL_CZ[*pc1];
      c2 = TO_LOWER_TBL_CZ[*pc2];
      if((pc1 - (byte*)str1) >= len1 || c1 == 0) {
        if((pc2 - (byte*)str2) >= len2 || c2 == 0) return 0;
        else        return -1;
      }
      if((pc2 - (byte*)str2) >= len2 || c2 == 0) return 1;
      if(c1 < c2) return -1;
      if(c1 > c2) return 1;
      pc1++;pc2++;
    }
}
//---------------------------------------------------------------------------
int StrCaseCmp(const char *str1, const char *str2, int len1, int len2)
{
    int ret = cmpTrivial(str1, str2, len1, len2);
    if(ret <= 1) return ret;

    byte *pc1=(byte*)str1;
    byte *pc2=(byte*)str2;
    while(1) {
      if((pc1 - (byte*)str1) >= len1 || *pc1 == 0) {
        if((pc2 - (byte*)str2) >= len2 || *pc2 == 0) return 0;
        else          return -1;
      }
      if((pc2 - (byte*)str2) >= len2 || *pc2 == 0) return 1;
      if(*pc1 < *pc2) return -1;
      if(*pc1 > *pc2) return 1;
      pc1++;pc2++;
    }
}
//---------------------------------------------------------------------------
char *StrUpperCZ(char *str)
{
  for(byte *pc=(byte*)str; *pc; pc++) if(*pc > ' ') *pc = ToUpperCZ(*pc);
  return str;
}
//---------------------------------------------------------------------------
/*
static char SHIFT_NUMS[] = {41,33,64,35,36,37,94,38,42,40};
word VKey2Char(word Key, bool shift, bool ctrl, bool alt)
{
int k = Key;
  if(k>='A' && k<='Z') {
    if(shift && !ctrl && !alt) k += 'a' - 'A';
  }
  else if(k>='0' && k<='9') {
    if(shift && !ctrl && !alt) k = SHIFT_NUMS[k-'0'];
  }
  else if(k>=0x60 && k<=0x6E) k = k - 0x60 + '0';    //num keypad
  else if(k==0x6E) k = '.';
  else if(k==0xBA) k = ';';
  else if(k==0xBB) k = '=';
  else if(k==0xBC) k = ',';
  else if(k==0xBD) k = '-';
  else if(k==0xBE) k = '.';
  else if(k==0xBF) k = '/';
  else {
    if(shift) k |= 0x100;
    if(ctrl) k |= 0x200;
    if(alt) k |= 0x400;
    k |= 0x1000;
  }
  return (word)k;
}
*/
//---------------------------------------------------------------------------
/*
int StrFindDelimited(char *str, int pos, char delim, int *pfldlen)
{
char *pc;
int i,j;
int fieldstart = -1;
    for(i=0, pc=str; ; pc++) {
    if(*pc == delim) i++;
    if((pos==0 && pc==str) || i==pos) {
      if(pc > str) pc++;
      fieldstart = pc - str;
      for(j=0; pc[j] && pc[j]!=delim; j++);
      if(pfldlen) *pfldlen = j;
      return fieldstart;
    }
    if(!*pc) break;
  }
  return -(i+1);
}
*/
//-------------------------------------------------------
/*
unsigned Bin2Gray(unsigned bin)
{
	unsigned ret = bin;
	unsigned pos = 0x80000000;
	int bit = 0;
	for(int i=0; i<32; i++) {
		if(bit) ret ^= pos;
		bit = bin & pos;
		pos >>= 1;
	}
	return ret;
}
*/
//=================================================================
//============================== FString ==========================
//=================================================================
FString::FString(const FString& fs)
{
    data = fs.data;
    data->IncrRefCnt();
    fOffset = fs.Offset();;
    fLength = fs.Len();
    ZeroTermStr = NULL;
}
//---------------------------------------------------------------------------
FString::FString(const char *str)
{
    Init();
    Set(str);
}
//---------------------------------------------------------------------------
FString::FString(char c)
{
    Init();
    Append(c);
}
//---------------------------------------------------------------------------
FString::FString(int i)
{
    Init();
    setInt(i);
}
//---------------------------------------------------------------------------
FString::FString(unsigned int u)
{
    Init();
    setUInt(u);
}
//---------------------------------------------------------------------------
FString::FString(double d, int dec)
{
    Init();
    *this = setFloat(d, dec);
}
//---------------------------------------------------------------------------
static FVector<byte> emptyStringData;
void FString::Init()
{
    data = &emptyStringData;
    emptyStringData.IncrRefCnt();
    //data = new FVector<byte>;
    fOffset = 0;
    fLength = data->Cnt();
    ZeroTermStr = NULL;
}
//---------------------------------------------------------------------------
FString::~FString()
{
    data->DecrRefCnt();
    if(RefCnt() < 0) {
            throw FStringException("FString::operator= : RefCnt() < 0, internal check error");
    }
    else if(RefCnt() == 0) {
            delete data;
    }
    if(ZeroTermStr) delete[] ZeroTermStr;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
const char *FString::Str() const
{
    if(Capacity() > Offset() + Len() && StrBuff()[Len()] == '\0') {
        // kdyz string konci na '\0' nedelej nic
        return StrBuff();
    }
    else {
        if(RefCnt() == 1 && Capacity() > Len() + Offset()) {
            /// jen jedna reference a v buffru je misto pro '\0'
            StrBuff()[Len()] = '\0';
            return StrBuff();
        }
        else {
            if(ZeroTermStr) delete[] ZeroTermStr;
            char *pc = new char[Len() + 1];
            int i;
            for(i=0; i<Len(); i++) pc[i] = StrBuff()[i];
            pc[i] = '\0';
            ((FString*)this)->ZeroTermStr = pc;
            // ((FString*)this)->ZeroTermStr proto, abych mohl zmenit const pointer this
        }
    }
    return ZeroTermStr;
}
//---------------------------------------------------------------------------
const char *FString::CStr()
{
    if(BuffLen() > Offset()+Len() && StrBuff()[Len()] == '\0') {
        // kdyz string konci na '\0' nedelej nic
    }
    else {
        Touch();
        Append('\0');   // nejdriv pridam '\0', ale tim se mi i natahne string
        setLen(Len() - 1); // zkratim string na puvodni delku
/*
        if(Capacity() > Offset() + Len()) {
            StrBuff()[Len()] = '\0';
        }
        else if(BuffLen() == Offset() + Len()) {
            data->Append('\0');
        }
        else
            throw FStringException("CStr() - internal data inconsistency.");
*/
    }
    return StrBuff();
}
//---------------------------------------------------------------------------
void FString::Touch()
{
    if(RefCnt() > 1) {
        data->DecrRefCnt();
        byte *o = data->Vector();
        data = new FVector<byte>(Len()+1);  // +1 kvuli funkci Str(), aby se nemusela alokovat pamet
        data->setLen(Len());
        if(o) {
            o += Offset();
            byte *n = data->Vector();
            for(int i=0; i<Len(); i++) n[i] = o[i];
        }
        fOffset = 0;
    }
}
//---------------------------------------------------------------------------
void FString::setLen(int new_len)
{
    if(new_len < 0)
        throw FStringException("SetLen(%i) new_len < 0", new_len);

    if(new_len <= Len()) fLength = new_len;
    else if(new_len > Len()) {
        Touch();
        // copy string data to pos 0, because FVector::setLen() calls realloc()
        // and do not know anything about fOffset
        if(fOffset > 0) {
            byte *b1 = Buff(), *b2 = b1 + fOffset;
            for(int i=0; i<fLength; i++) b1[i] = b2[i];
        }
        data->setLen(new_len);
        fOffset = 0;
        fLength = new_len;
    }
}
//---------------------------------------------------------------------------
int FString::strlenf(const char *str)
{
    int l;
    for(l=0; str[l]; l++);
    return l;
}
//---------------------------------------------------------------------------
void FString::CheckStrLen()
{
    int l = strlenf(StrBuff());
    if(l <= BuffLen() - Offset()) fLength = l;
}
//---------------------------------------------------------------------------
int FString::Cmp(const char *str, enumCmpMethod cmp) const
{
    switch(cmp) {
        case cmpCase:   return StrCaseCmp(StrBuff(), str, Len());
        //?case cmpICaseCZ:return StrICaseCZCmp(StrBuff(), str, Len());
        case cmpICase:
        default:        return StrICaseCmp(StrBuff(), str, Len());
    }
}
//---------------------------------------------------------------------------
int FString::Cmp(const FString& fstr, enumCmpMethod cmp) const
{
    switch(cmp) {
        case cmpCase:   return StrCaseCmp(StrBuff(), fstr.StrBuff(), Len(), fstr.Len());
        case cmpICaseCZ:return StrICaseCZCmp(StrBuff(), fstr.StrBuff(), Len(), fstr.Len());
        case cmpICase:
        default:        return StrICaseCmp(StrBuff(), fstr.StrBuff(), Len(), fstr.Len());
    }
}
//---------------------------------------------------------------------------
const FString& FString::operator=(const char *str)
{
    Copy(str);
    return *this;
}
//---------------------------------------------------------------------------
const FString& FString::operator=(const FString& fstr)
{
    fstr.data->IncrRefCnt();
    // nejdriv inkrementuju reference pro fstr
    // kdybych to delal na konci nefungovalo by 's = s;'
    data->DecrRefCnt();
    if(RefCnt() < 0) {
            throw FStringException("FString::operator= : RefCnt() < 0, internal check error");
    }
    else if(RefCnt() == 0) {
            delete data;
    }
    data = fstr.data;
    fOffset = fstr.Offset();
    fLength = fstr.Len();
    return *this;
}
//---------------------------------------------------------------------------
char& FString::getRef(int index)
{
    if(index < 0) index += Len();
    if(index == Len())
        throw FStringException("FString::operator[] try to get reference of terminating '\\0'");
    if(index > Len())
        throw FStringException("FString::operator[%i] index too big", index);
    if(index < 0)
        throw FStringException("FString::operator[%i] index too small", index);
    Touch();
    // kvuli tomuto Touch nepouzivam operator[], pak by se totiz i pri cteni znaku
    // tvorila kopie, pokud by RefCnt>1
    return (char&)(*data)[Offset() + index];
}
//---------------------------------------------------------------------------
char FString::operator[](int index) const
{
    if(index < 0) index += Len();
    if(index < 0 || index >= Len()) {
        return '\0';
        // mimo string jsou vsude '\0'
//        dummyChar = 0;
//        return dummyChar;
        // predchozi reseni je zdrojem chyb pri prepisovani 0
        // proto jsem od neho upustil
//        throw FStringException("operator[%i] - index out of range Len()==%i", index, Len());
    }
    return CharBuff()[Offset() + index];
}
//---------------------------------------------------------------------------
int FString::Pos(char findchar, int count, bool backward) const
{
//    return strcposfb(StrBuff(), c, n, backward, Len());
    const char *str = StrBuff();
    const char *pc = str;
    int len = Len();
    int l;
    for(l=0; l<len && str[l]; l++);
    int i = (backward)? l-1: 0;
    int dir = (backward)? -1: 1;
    for(; i<l && i>=0; i+=dir) {
        if(pc[i] == findchar) count--;
        if(count <= 0) return i;
    }
    return -1;
}
//---------------------------------------------------------------------------
int FString::QuotedPos(const char *str, char quote, bool icase) const
{
    int l = Len();
    char *pc = StrBuff();
    char c1, c2;
    bool inquotes = false;
    for(int i=0; i<l; i++) {
        if(pc[i] == quote) inquotes = !inquotes;
        else if(inquotes) continue;
        else {
            c1 = (icase)? toupper(pc[i]): pc[i];
            c2 = (icase)? toupper(str[0]): str[0];
            if(c1 == c2) {
                int j;
                for(j=1; str[j] && pc[i+j]; j++) {
                    c1 = (icase)? toupper(pc[i+j]): pc[i+j];
                    c2 = (icase)? toupper(str[j]): str[j];
                    if(c1 != c2) break;
                }
                if(!str[j]) return i;
            }
        }
    }
    return -1;
}
//---------------------------------------------------------------------------
int FString::Pos(const char *str, int count, int start) const
{
    int i, j;
    for(j=start; j<Len(); j++) {
        for(i=j; i<Len() && (*this)[i]==str[i-j]; i++);
        if(!str[i-j]) count--;
        if(count <= 0) return j;
    }
    return -1;
}
//---------------------------------------------------------------------------
int FString::IPos(const char *str, int count, int start) const
{
    int i, j;
    for(j=start; j<Len(); j++) {
        for(i=j; i<Len() && toupper((*this)[i])==toupper(str[i-j]); i++);
        if(!str[i-j]) count--;
        if(count <= 0) return j;
    }
    return -1;
}
//---------------------------------------------------------------------------
int FString::MatchingBracketPos(char open_bracket, char close_bracket, char quote) const
{
    bool inquotes = false;
    int opens = 0;
    char c;
    for(int i=0; (c=(*this)[i])!='\0'; i++) {
        if(quote) {
            if(c == quote) inquotes = !inquotes;
        }
        if(inquotes) continue;
        if(c == open_bracket) opens++;
        if(c == close_bracket) opens--;
        if(opens < 0) return i;
    }
    return -1;
}
//---------------------------------------------------------------------------
FString FString::Slice(int start, int end) const
{
    FString fs = *this;

    int l = Len();
    if(start < 0) start += l;
    if(end < 0) end += l;
    if(start < 0) start = 0;
    if(end < 0) end = 0;
    if(end > l) end = l;
    if(start > end) start = end;
    fs.fOffset += start;
    fs.fLength = end - start;

    return fs;
}
//---------------------------------------------------------------------------
FString FString::CutSlice(int len)
{
    FString fs = Slice(0, len);
    *this = Slice(len);
    return fs;
}
//---------------------------------------------------------------------------
int FString::getToken(const FString& pattern, bool icase)
{
    int i,j;
    bool esc = false;
    bool neg = false;
    //fprintf(stderr, "getToken: '%s' pattern: '%s'\n", Str(), pattern.Str());
    for(j=0, i=0; pattern[j]; j++) {
        //char c1, c2;
        //c1 = (*this)[i], c2 = pattern[j];
        //fprintf(stderr, "[1]\tesc:%i neg:%i this[%i]:'%c' pattern[%i]:'%c'\n", esc, neg, i, c1, j, c2);
        esc = neg = false;
        if(pattern[j] == '\\') {
            j++;
            esc = true;
        }
        if(pattern[j] == '!') {
            if(!esc) {
                j++;
                neg = true;
            }
        }
        //c1 = (*this)[i]; c2 = pattern[j];
        //fprintf(stderr, "\tesc:%i neg:%i this[%i]:'%c' pattern[%i]:'%c'\n", esc, neg, i, c1, j, c2);
        if(!esc) {
            if(pattern[j] == '~') { // one or more blanks (or nonblanks if neg)
                if(neg) {
                    if((*this)[i] <= ' ') break;
                    while((*this)[i] > ' ') i++;
                }
                else {
                    if((*this)[i] > ' ') break;
                    while((*this)[i] <= ' ') i++;
                }
                continue;
            }
        }
        bool cmp;
        if(icase) cmp = (To7BitLower((*this)[i]) == To7BitLower(pattern[j]));
        else      cmp = ((*this)[i] == pattern[j]);
        if(neg) cmp = !cmp;
        if(!cmp) break;
        i++;
    }
    if(j < pattern.Len()) return -(i + 1);
    return i;
}
/*?
FString FString::CutMatch(const char* pattern)
{
    FString fs;
    int l = Match(pattern);
    if(l > 0) {
        fs = Slice(0, l);
        *this = Slice(l);
    }
    return fs;
}
?*/
//---------------------------------------------------------------------------
FString FString::CutPos(const char* findstr, bool ignore_case)
{
    FString fs;
    int l;
    if(ignore_case) l = IPos(findstr);
    else            l = Pos(findstr);
    if(l >= 0) {
        fs = Slice(0, l);
        *this = Slice(l + strlenf(findstr));
    }
    else {
        fs = Slice(0);
        *this = Slice(0, 0);
    }
    return fs;
}
//---------------------------------------------------------------------------
FString FString::Cut(char delim, char quote)
{
    bool inquotes = false;
    int i;
    char *pc = StrBuff(), c;
    for(i=0; i<Len(); i++) {
        c = pc[i];
        if(c == quote) inquotes = !inquotes;
        else {
            if(inquotes) continue;
            if(c == delim) break;
        }
    }
    FString fs = Slice(0, i);
    *this = Slice(i);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::CutDelimited(char *delims, char quote)
{
    bool inquotes = false;
    int i,j;
    char *pc = StrBuff();
    for(i=0; i<Len(); i++) {
        if(pc[i] == quote) inquotes = !inquotes;
        else {
            if(inquotes) continue;
            for(j=0; delims[j]; j++) if(pc[i] == delims[j]) break;
            if(delims[j]) break;
        }
    }
    FString fs = Slice(0, i);
    *this = Slice(i);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::CutInt()
{
    int ix = 0;
    bool sgn = false;
    while((unsigned char)(*this)[ix] <= ' ') ix++;
    if((*this)[ix] == '+' || (*this)[ix] == '-') {sgn = true; ix++;}
    while((*this)[ix] >= '0' && (*this)[ix] <= '9') ix++;
    if(ix == 1 && sgn) ix = 0;  // + nebo - neni cislo
    FString fs = Slice(0, ix);
    *this = Slice(ix);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::CutHex()
{
    int ix = 0;
    while((unsigned char)(*this)[ix] <= ' ') ix++;
    //if((*this)[ix] == '+') {ix++;}
    while(true) {
        unsigned char c = (unsigned char)(*this)[ix];
        if(c>='0' && c<='9' || c>='a' && c<='f' || c>='A' && c<='F') {ix++; continue;}
        break;
    }
    FString fs = Slice(0, ix);
    *this = Slice(ix);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::Replace(const char* findstr, const char* replstr, char quote) const
{
    FString fs, fs1 = *this;
    int pos;
    int l = strlenf(findstr);

    while(!fs1.isEmpty()) {
        pos = fs1.QuotedPos(findstr);
        if(pos < 0) {
            // nenasel
            fs += fs1;
            fs1.setLen(0);
            break;
        }
        else {
            //nasel a muze vymenovat
            fs += fs1.Slice(0, pos);
            fs += replstr;
            fs1 = fs1.Slice(pos+l);
        }
    }
    return fs;
}
//---------------------------------------------------------------------------
void FString::Append(char c)
{
    setLen(Len() + 1);
    getRef(-1) = c;
}
//---------------------------------------------------------------------------
void FString::Set(const char *str, int maxlen)
{
    int l = 0;
    if(str) l = strlenf(str);
    l = (l>maxlen)? maxlen: l;
    if(l>0) {
        Touch();
        setCapacity((unsigned)l+1); // +1 je kvuli Str() aby nedochazelo k prealokovani
        char *pc = StrBuff();
        for(int i=0; i<l; i++) pc[i] = str[i];
    }
    setLen(l);
}
//---------------------------------------------------------------------------
void FString::setCapacity(unsigned newcapacity) 
{
    Touch();
    data->setCapacity(newcapacity);
}
//---------------------------------------------------------------------------
void FString::Copy(const char *str, int maxlen)
{
    setLen(0);
    if(str) for(int i=0; i<maxlen && str[i]; i++) Append(str[i]);
}
//---------------------------------------------------------------------------
void FString::Cat(const char *src, int maxsrclen)
{
    if(src) for(int i=0; i<maxsrclen && src[i]; i++) Append(src[i]);
}
//---------------------------------------------------------------------------
void FString::MemCpy(const void *buff, unsigned len)
{
    Touch();
    setLen(len);
    byte *pb = Buff();
    if(buff) for(unsigned i=0; i<len; i++) pb[i] = ((byte*)buff)[i];
}
//---------------------------------------------------------------------------
void FString::MemCat(const void *buff, unsigned len)
{
    int oldlen = Len();
    setLen(oldlen + len);
    byte *pb = Buff() + oldlen;
    if(buff) for(unsigned i=0; i<len; i++) pb[i] = ((byte*)buff)[i];
}
//---------------------------------------------------------------------------
FString FString::NoSpaces() const
{
    FString fs = *this;
    fs.Touch();
    unsigned char *str = (unsigned char*)fs.StrBuff();
    int len = Len();
    unsigned char *pc,*pd;
    for(pc=(unsigned char*)str, pd=(unsigned char*)str; *pc && (pc-str) < len; pc++) {
        if(*pc > ' ') *pd++ = *pc;
    }
    fs.setLen(pd - str);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::OneSpace() const
{
    FString fs = this->Trim();
    fs.Touch();

    unsigned char *str = (unsigned char*)fs.StrBuff();
    int len = Len();
    unsigned char *pc1 = (unsigned char*)str;
    unsigned char *pc2 = (unsigned char*)str;
    bool spc;

    while(*pc2 && (pc2-str)<len) {
        spc = (*pc2 <= ' ');
        if(spc) {
            *pc1++ = ' ';
            while(*pc2 && *pc2 <= ' ' && (pc2-str)<len) pc2++;
        }
        else *pc1++ = *pc2++;
    }
    fs.setLen(pc1 - str);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::White2Space() const
{
    FString fs = *this;
    fs.Touch();
    char *pc = fs.StrBuff();
    int len = Len();
    for(int i=0; i<len; i++) {
        if(pc[i]>0 && pc[i]<' ') pc[i] = ' ';
    }
    return fs;
}
//---------------------------------------------------------------------------
FString FString::LTrim() const
{
    FString fs = *this;
    int i;
    for(i=0; i<fs.Len() && fs.Buff()[fs.Offset() + i]<=' '; i++);
    fs.fOffset += i;
    fs.fLength -= i;
    return fs;
}
//---------------------------------------------------------------------------
FString FString::RTrim() const
{
    FString fs = *this;
    int i;
    for(i=fs.Len(); i>0 && fs.Buff()[fs.Offset() + i - 1]<=' '; i--);
    fs.fLength = i;
    return fs;
}
//---------------------------------------------------------------------------
FString FString::Trim() const
{
    FString fs = LTrim();
    fs = fs.RTrim();
    return fs;
}
//---------------------------------------------------------------------------
FString FString::LPad(int newlen, char padchar) const
{
    FString fs = *this;
	int l = fs.Len();
    if(newlen == l) return fs;
    if(newlen < l) {fs = fs.Slice(l-newlen); return fs;}
	fs.setLen(newlen);
	char *pc = fs.StrBuff();
    int ni = newlen-1;
	for(int i=l-1; i>=0; i--) pc[ni--] = pc[i];
	for(; ni>=0; ni--) pc[ni] = padchar;
	return fs;
}
//---------------------------------------------------------------------------
FString FString::RPad(int newlen, char padchar) const
{
    FString fs = *this;
	int l = fs.Len();
	fs.setLen(newlen);
	char *pc = fs.StrBuff();
	for(int i=l; i<newlen; i++) pc[i] = padchar;
	return fs;
}
//---------------------------------------------------------------------------
FString FString::CPad(int newlen, char padchar) const
{
    FString fs = *this;
	int l = (newlen - Len()) / 2;
	if(l < 0) l = 0;
	fs = fs.LPad(l + Len(), padchar);
	fs = fs.RPad(newlen, padchar);
	return fs;
}
//---------------------------------------------------------------------------
FString FString::Rep(char c, int cnt)
{
    FString fs;
    return fs.RPad(cnt, c);
}
//---------------------------------------------------------------------------
/*?
FString& FString::Crypt()
{
    FString fs = *this;
    int l = 2*(Len() + 1); // zakrypteny string muze byt delsi
    setLen(l+1); // +1 na ukoncovaci '\0'
    l = CryptStr(fs.Str(), StrBuff(), l);
    setLen(l);
    return *this;
}
?*/
//---------------------------------------------------------------------------
/*?
FString& FString::UnCrypt()
{
    if(!isEmpty()) {
        FString fs = *this;
        Touch();
        int l = UnCryptStr(fs.Str(), StrBuff(), Len()-1);
        setLen(l);
    }
    return *this;
}
?*/
//---------------------------------------------------------------------------
FString FString::printf(const char * format, ...)
{
    FString fs;
	va_list arg;
	va_start(arg, format);
    fs = FString::vprintf(format, arg);
	va_end(arg);
    return fs;
}
FString FString::vprintf(const char *format, va_list arg)
{
    FString fs;
#if defined __BORLANDC__ || defined __GNUC__
    // borlandi _vsnprintf nevraci pocet zapsanych znaku, kdyz je n==0
    int len = vsnprintf(NULL, 0, format, arg);
#else
    // microsoft neimplementuje vsnprintf
    int len = _vsnprintf(NULL, 0, format, arg);
#endif
	if(len < 0) len = 0; // kdyz se vsnprintf nepovede
	fs.setLen(len);
#if defined __GNUC__
	vsnprintf(fs.StrBuff(), len, format, arg);
#else
	_vsnprintf(fs.StrBuff(), len, format, arg);
#endif
    return fs;
}
//---------------------------------------------------------------------------
FString FString::toUpper() const
{
    FString fs = *this;
    fs.Touch();
    char *str = fs.StrBuff();
    for(int i=0; i<Len(); i++) str[i] = ToUpperCZ(str[i]);
//    strupperf(fs.StrBuff(), fs.Len());
    return fs;
}
//---------------------------------------------------------------------------
FString FString::toLower() const
{
    FString fs = *this;
    fs.Touch();
    char *str = fs.StrBuff();
    for(int i=0; i<Len(); i++) str[i] = ToLowerCZ(str[i]);
//    strlowerf(fs.StrBuff(), fs.Len());
    return fs;
}
//---------------------------------------------------------------------------
FString FString::toAscii7() const
{
    FString fs = *this;
    fs.Touch();
    char *str = fs.StrBuff();
    for(int i=0; i<Len(); i++) str[i] = To7Bit(str[i]);
    return fs;
}
//---------------------------------------------------------------------------
bool FString::toBool() const
{
    FString fs = (*this).Trim();
    if(Cmp("y", cmpICase) == 0) return true;
    if(Cmp("a", cmpICase) == 0) return true;
    if(Cmp("yes", cmpICase) == 0) return true;
    if(Cmp("ano", cmpICase) == 0) return true;
    if(toInt()) return true;
    return false;
}
//---------------------------------------------------------------------------
int FString::toInt(int base) const
{
    int ret = strtol(Str(), NULL, base);
    return ret;
}
//---------------------------------------------------------------------------
unsigned FString::toUInt(int base) const
{
    int ret = strtoul(Str(), NULL, base);
    return ret;
}
//---------------------------------------------------------------------------
const FString& FString::setInt(int n)
{
    if(n<0) setUInt(-n);
    else    setUInt(n);
    if(n<0) *this = "-" + *this;

    return *this;
}
//---------------------------------------------------------------------------
const FString& FString::setUInt(unsigned int n)
{
    char ch;
    char *pc1, *pc2;

    setLen(32); 
    int bufflen = Len(), l = 0;
    char *buff = StrBuff();
    
    if(n == 0) {
        buff[l++] = '0';
        setLen(l); 
        return *this;
    }

    while(n>0 && l<bufflen) {
      buff[l++] = (char)(n % 10) + '0';
      n /= 10;
    }

    // reverse digits order
    pc1 = buff;
    pc2 = buff + l - 1;
    while(pc2>pc1) {
      ch = *pc1; *pc1 = *pc2; *pc2 = ch;
      pc1++; pc2--;
    }
    setLen(l);
    return *this;
}
//---------------------------------------------------------------------------
const FString& FString::setFloat(double val, int dec)
{
    int decpos, sign;
    bool neg = false;
    if(val < 0) {neg = true; val = -val;}

    //zaokrouhlime
//    double epsilon = 5 * pow10(-(dec + 1));
    double mult = pow10(dec);
    val = floor(mult * val + 0.5);
    val /= mult;

    // double by mel mit 16 vyznamnych cislic
    FString fs = ecvt(val, 16, &decpos, &sign);
    if(decpos <= 0) {
        fs = "0." + FString::Rep('0', -decpos) + fs;
    }
    else if(decpos < fs.Len()) {
        fs = fs.Slice(0, decpos) + "." + fs.Slice(decpos);
    }
    else {
        fs += FString::Rep('0', decpos - fs.Len()) + ".";
    }
    decpos = fs.Pos('.');
    fs = fs.RPad(decpos + dec + 1, '0');

    if(neg) fs = "-" + fs;

    *this = fs;
    return *this;
}
//---------------------------------------------------------------------------
FString FString::fromBool(bool b)
{
    FString fs;
    fs = (b)? "Yes": "No";
    return fs;
}
//---------------------------------------------------------------------------
FString FString::fromInt(int i)
{
    FString fs;
    fs.setInt(i);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::fromUInt(unsigned u)
{
    FString fs;
    fs.setUInt(u);
    return fs;
}
//---------------------------------------------------------------------------
FString FString::fromFloat(double val, int dec)
{
    FString fs;
    fs.setFloat(val, dec);
    return fs;
}
//---------------------------------------------------------------------------
const FString& FString::operator+=(const FString& fstr) 
{
    MemCat(fstr.StrBuff(), fstr.Len());
    return *this;
}
//---------------------------------------------------------------------------
FString FString::operator+(char c) const
{
        FString fs(*this);
        return fs.operator+=(c);
}
//---------------------------------------------------------------------------
FString FString::operator+(int i) const
{
        FString fs(*this);
        return fs.operator+=(FString(i));
}
//---------------------------------------------------------------------------
FString FString::operator+(const char *str) const
{
        FString fs(*this);
        return fs.operator+=(str);
}
//---------------------------------------------------------------------------
FString FString::operator+(const FString& fstr) const
{
        FString fs(*this);
        return fs.operator+=(fstr);
}
//-------------------------------- friends ----------------------------------
//---------------------------------------------------------------------------
FString operator+(const char* str, const  FString &fstr)
{
        FString fs = str;
        fs += fstr;
        return fs;
}
//---------------------------------------------------------------------------
FString operator+(char c, const  FString &fstr)
{
        char str[] = {c, '\0'};
        FString fs = str;
        fs += fstr;
        return fs;
}
//---------------------------------------------------------------------------
const char *FString::escapecharf(byte c)
{
    static char EscBuff[5];
    byte b;
    if(c < ' ' || c == '\\') {
        EscBuff[0] = '\\';
        switch(c) {
          case '\\':
            EscBuff[1] = '\\'; EscBuff[2] = '\0';
            break;
          case '\a':
            EscBuff[1] = 'a'; EscBuff[2] = '\0';
            break;
          case '\b':
            EscBuff[1] = 'b'; EscBuff[2] = '\0';
            break;
          case '\f':
            EscBuff[1] = 'f'; EscBuff[2] = '\0';
            break;
          case '\n':
            EscBuff[1] = 'n'; EscBuff[2] = '\0';
            break;
          case '\r':
            EscBuff[1] = 'r'; EscBuff[2] = '\0';
            break;
          case '\t':
            EscBuff[1] = 't'; EscBuff[2] = '\0';
            break;
          case '\v':
            EscBuff[1] = 'v'; EscBuff[2] = '\0';
            break;
          case '\0':
            EscBuff[1] = '0'; EscBuff[2] = '\0';
            break;
          default:
            b = c/16; EscBuff[1] = (b < 0x0A)? b + '0': b - 0x0A + 'A';
            b = c%16; EscBuff[2] = (b < 0x0A)? b + '0': b - 0x0A + 'A';
            EscBuff[3] = 0;
            break;
        }
    }
    else {
        EscBuff[0] = c;
        EscBuff[1] = 0;
    }
    return EscBuff;
}
//---------------------------------------------------------------------------
int FString::unescapecharf(const char *str, byte *ret)
{
    byte *pc = (byte*)str,b;
    int c;
    *ret = 0;
    if(!*pc) return 0;
    if(*pc == '\\') {
        pc++;
        switch(*pc) {
          case '\\':
            b = '\\'; pc++; break;
          case 'a':
            b = '\a'; pc++; break;
          case 'b':
            b = 'b'; pc++; break;
          case 'f':
            b = '\f'; pc++; break;
          case 'n':
            b = '\n'; pc++; break;
          case 'r':
            b = '\r'; pc++; break;
          case 't':
            b = '\t'; pc++; break;
          case 'v':
            b = '\v'; pc++; break;
          default:
            if(!*pc) {b = 0; break;}
            c = *pc++;
            if(!isxdigit(c)) {b = (byte)c; break;}
            c = toupper(c);
            b = (byte)(c < 'A')? c - '0': c - 'A' + 0x0A;
            if(!*pc) break;
            c = *pc;
            if(!isxdigit(c)) break;
            pc++;
            c = toupper(c);
            b *= 16;
            b += (byte)(c < 'A')? c - '0': c - 'A' + 0x0A;
            break;
        }
    }
    else b = *pc++;

    *ret = b;
    return pc - (byte*)str;
}
//---------------------------------------------------------------------------
FString FString::Escape(const byte *data, int len)
{
    FString fs;
    const char *pc;
    for(int i=0; i<len; i++) {
        pc = escapecharf(data[i]);
        if(!*pc) break;
        for(; *pc; pc++) fs.Append(*pc);
    }
    return fs;
}
//---------------------------------------------------------------------------
FString FString::Escape() const
{
    return Escape((const byte*)StrBuff(), Len());
}
//---------------------------------------------------------------------------
FString FString::UnEscape() const
{
    int len = Len();
    const char *str = StrBuff();
    FString fs;
    int escpos;
    byte b;

    for(escpos = 0; escpos < len; ) {
        escpos += unescapecharf(str + escpos, &b);
        fs += (char)b;
    }
    return fs;
}
//---------------------------------------------------------------------------
FString FString::eval() const
{
    int pos = QuotedPos("==", '\'');
    if(pos < 0) return *this;
    FString fs = Slice(0, pos).Trim();
    FString fs1 = Slice(pos + 2).Trim();
    if(fs == fs1) return "1";
    return "0";
}
//---------------------------------------------------------------------------
int FString::fromStream(FILE *f)
{
    if(f) {
        fseek(f, 0, SEEK_END);
        int f_len = (int)ftell(f);
        setLen((int)f_len);
        Touch();
        fseek(f, 0, SEEK_SET);
        fread((void*)StrBuff(), 1, f_len, f);
        return Len();
    }
    else setLen(0);
    return -1;
}
//---------------------------------------------------------------------------
int FString::toStream(FILE *f) const
{
    if(f) {
        fwrite((void*)StrBuff(), 1, Len(), f);
        return Len();
    }
    return -1;
}

//=================================================================
//=========================== FStringList =========================
//=================================================================
void FStringList::setLen(flist_ix_t newlen)
{
    flist_ix_t oldlen = Len();
    FVector<FString*>::setLen(newlen);
    if(newlen > oldlen) {
        for(flist_ix_t u=oldlen; u<Cnt(); u++) vector[u] = new FString();
//        for(unsigned u=oldcnt; u<Cnt(); u++) vector[u] = new T;
    }
    else if(newlen < oldlen) {
        for(flist_ix_t i=newlen; i<oldlen; i++) {
            FString *o = vector[i];
            if(o) {
                if(DeleteOnRemove()) delete o;
                vector[i] = NULL;
            }
        }
    }
}
//---------------------------------------------------------------------------
void FStringList::Insert(const char *str, unsigned before_ix)
{
    FString *pfs = new FString(str);
    FList<FString>::Insert(pfs, before_ix);
}
//---------------------------------------------------------------------------
void FStringList::Insert(const FString &fs, unsigned before_ix)
{
    FString *pfs = new FString(fs);
    FList<FString>::Insert(pfs, before_ix);
}
//---------------------------------------------------------------------------
void FStringList::fromString(const FString &s, char delim, char quote, bool strip_it)
{
    FString fs = s;
    Clear();
    if(!!fs) while(true) {
        Append(fs.Cut(delim, quote));
        if(!fs) break;
        fs = fs.Slice(1);
    }
    if(strip_it) strip(quote);
    /*
    byte c = '.';
    FString fs;
    char q = text_delim;
    bool quoted = !(q == 0);
    bool in_quotes = false;

    Clear();
    if(!s) return;
    for(int i=0; c != '\0'; i++) {
        c = s[i];
        if(c == delim || c == '\0') {
            // pokud je delim v uvozovkach je soucasti retezce
            if(quoted && in_quotes && c) {
            }
            else {
                fs = fs.Trim();
                if(quoted) {
                    if(fs[0] == q) {
                        if(fs[-1] == q) fs.Slice(1, -1);
                    }
                }
                Append(fs);
                fs = "";
                in_quotes = false;
                continue;
            }
        }
        if(c == q) {
            in_quotes = !in_quotes;
            continue;
        }
        //if(c < ' ' && c != '\t' && c != '\n') continue;
        fs += (char)c;
    }
    */
}
//---------------------------------------------------------------------------
void FStringList::strip(char quote)
{
    FString fs;
    for(int i=0; i<Cnt(); i++) {
        fs = operator[](i).Trim();
        if(quote) if(fs[0] == quote && fs[-1] == quote) fs = fs.Slice(1, -1);
        operator[](i) = fs;
    }
}
//---------------------------------------------------------------------------
FString FStringList::toString(char delim, char text_delim)
{
    FString fs;
    char q = text_delim;
    bool quoted = !(q == 0);

    for(int ui=0; ui<Cnt(); ui++) {
        if(ui>0) fs += delim;
        if(quoted) {
            if((*this)[ui].Pos(delim) >= 0) {
                // je treba uvozovek
                fs += q;
                fs += (*this)[ui];
                fs += q;
                continue;
            }
        }
        fs += (*this)[ui];
    }
    return fs;
}
//---------------------------------------------------------------------------
void FStringList::LoadFromFile(const char *filename, char text_delim,
                               int skiplines, int linescnt)
{
    FILE *in = fopen(filename, "rb");
    if(!in) throw FStringListException("Cann't open file %s !", filename);

    FString fs;
    int c;
    char q = text_delim;
    bool quoted = !(q == 0);
    bool in_quotes = false;
    int lcnt = 0;

    Clear();
    while(1) {
        c = fgetc(in);
        if((c == '\n' && !in_quotes) || c == EOF) {
            lcnt++;
            if(lcnt > skiplines) Append(fs);
            if(c == EOF || Cnt() >= linescnt) break;
            fs = "";
            continue;
        }
        if(quoted) {
            if(c == q) in_quotes = !in_quotes;
        }
        if(c < ' ' && c != '\t' && c != '\n') continue;
        fs += (char)c;
    }

    fclose(in);
}
//---------------------------------------------------------------------------
void FStringList::SaveToFile(const char *filename, bool overwrite)
{
    FILE *out = fopen(filename, "rb");
    if(out && !overwrite) {
        fclose(out);
        throw FStringListException("File %s already exists!", filename);
    }
    out = fopen(filename, "wb");
    if(!out) throw FStringListException("Cann't open file %s for writing!", filename);

    for(int ui=0; ui<Cnt(); ui++) {
        if(ui>0) fputs("\r\n", out);
        fputs((*this)[ui].Str(), out);
    }
    fclose(out);
}

//=================================================================
//========================== FStringTable =========================
//=================================================================
FString FStringTable::dummyStr = "";
//---------------------------------------------------------------------------
void FStringTable::InsertRow(unsigned before_ix, FStringList *str_src)
{
    FStringList *sl = new FStringList(ColCnt());
    for(unsigned u=0; u<(unsigned)ColCnt(); u++)
        if(!str_src) sl->Append(FStringTable::dummyStr);
        else         sl->Append((*str_src)[u]);
    Insert(sl, before_ix);
}
//---------------------------------------------------------------------------
FString FStringTable::toString(char delim, char quote)
{
    FString fs;
    for(flist_ix_t r=0; r<RowCnt(); r++) {
        for(int c=0; c<ColCnt(); c++) {
            if(c > 0) fs += delim;
            if(quote) fs += quote;
            fs += AsCell(c, r);
            if(quote) fs += quote;
        }
        fs += F_EOLN;
    }
    return fs;
}
//=================================================================



