//=============================================================================
//
//   File : kvi_qstring.cpp
//   Creation date : Mon Aug 04 2003 13:36:33 CEST by Szymon Stefanek
//
//   This file is part of the KVirc irc client distribution
//   Copyright (C) 2003-2006 Szymon Stefanek (pragma at kvirc dot net)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

//=============================================================================
//
// Helper functions for the TQString class
//
//=============================================================================

#define __KVILIB__


#include "kvi_qstring.h"
#include "kvi_string.h"
#include "kvi_malloc.h"
#include "kvi_locale.h"

#include <ctype.h> // for tolower()
#include <stdio.h> // for sprintf()
#include <tqregexp.h> 

// kvi_string.cpp
extern unsigned char iso88591_toLower_map[256];
extern unsigned char iso88591_toUpper_map[256];

#define MY_MIN(a,b) (((a) < (b)) ? (a) : (b))

namespace KviTQString
{
	// The global empty (and null) string
	const TQString empty;

	KVILIB_API bool equalCSN(const TQString &sz1,const TQString &sz2,unsigned int len)
	{
		if(len == 0)return true; // assume equal
		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		unsigned int lmin = MY_MIN(sz1.length(),sz2.length());
		if(lmin < len)return false;
		const TQChar * c1e = c1 + len;
		
		if(!c1 || !c2)return (c1 == c2);
		
		while(c1 < c1e)
		{
			if(c1->unicode() != c2->unicode())return false;
			c1++;
			c2++;
		}
		return (c1 == c1e);
	}

	KVILIB_API bool equalCIN(const TQString &sz1,const TQString &sz2,unsigned int len)
	{
		if(len == 0)return true; // assume equal
		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		unsigned int lmin = MY_MIN(sz1.length(),sz2.length());
		if(lmin < len)return false;
		const TQChar * c1e = c1 + len;

		if(!c1 || !c2)return (c1 == c2);

		while(c1 < c1e)
		{
			if(c1->lower().unicode() != c2->lower().unicode())return false;
			c1++;
			c2++;
		}
		return (c1 == c1e);
	}

	KVILIB_API bool equalCSN(const TQString &sz1,const char * sz2,unsigned int len)
	{
		if(len == 0)return true; // assume equal
		const TQChar * c1 = sz1.unicode();
		if(sz1.length() < len)return false;
		const TQChar * c1e = c1 + len;

		if(!sz2)return !c1;
		if(!c1)return !sz2;

		while((c1 < c1e) && (*sz2))
		{
			if(c1->unicode() != *sz2)return false;
			c1++;
			sz2++;
		}
		return (c1 == c1e);
	}

	KVILIB_API bool equalCIN(const TQString &sz1,const char * sz2,unsigned int len)
	{
		if(len == 0)return true; // assume equal
		const TQChar * c1 = sz1.unicode();
		if(sz1.length() < len)return false;
		const TQChar * c1e = c1 + len;

		if(!sz2)return !c1;
		if(!c1)return !(*sz2);

		while((c1 < c1e) && (*sz2))
		{
			if(c1->lower().unicode() != tolower(*sz2))return false;
			c1++;
			sz2++;
		}
		return (c1 == c1e);
	}

	// sz2 is assumed to be null terminated, sz1 is not!
	KVILIB_API bool equalCIN(const TQString &sz1,const TQChar *sz2,unsigned int len)
	{
		if(len == 0)return true; // assume equal
		const TQChar * c1 = sz1.unicode();
		if(sz1.length() < len)return false;
		const TQChar * c1e = c1 + len;

		if(!sz2)return !c1;
		if(!c1)return !(sz2->unicode());

		while((c1 < c1e) && (sz2->unicode()))
		{
			if(c1->lower().unicode() != sz2->lower().unicode())return false;
			c1++;
			sz2++;
		}
		return (c1 == c1e);
	}

	KVILIB_API TQString makeSizeReadable(size_t bytes)
	{
		double size = bytes;
		if(size<900)
			return TQString(__tr2qs("%1 bytes")).arg(size,0,'f',3);

		size/=1024;
		if(size<900)
			return TQString(__tr2qs("%1 KB")).arg(size,0,'f',3);

		size/=1024;
		if(size<900)
			return TQString(__tr2qs("%1 MB")).arg(size,0,'f',3);

		//Pirated DVD?;)
		size/=1024;
		if(size<900)
			return TQString(__tr2qs("%1 GB")).arg(size,0,'f',3);

		//Uhm.. We are downloading a whole internet:)))
		size/=1024;
		return TQString(__tr2qs("%1 TB")).arg(size,0,'f',3);
	}

	KVILIB_API bool equalCS(const TQString &sz1,const TQString &sz2)
	{
		if(sz1.length() != sz2.length())return false;

		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		const TQChar * c1e = c1 + sz1.length();
		
		if(!c1 || !c2)return (c1 == c2);
		
		while(c1 < c1e)
		{
			if(c1->unicode() != c2->unicode())return false;
			c1++;
			c2++;
		}
		return (c1 == c1e);
	}

	KVILIB_API bool equalCI(const TQString &sz1,const TQString &sz2)
	{
		if(sz1.length() != sz2.length())return false;

		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		const TQChar * c1e = c1 + sz1.length();

		if(!c1 || !c2)return (c1 == c2);

		while(c1 < c1e)
		{
			if(c1->lower().unicode() != c2->lower().unicode())return false;
			c1++;
			c2++;
		}
		return (c1 == c1e);
	}

	// sz2 is assumed to be null terminated, sz1 is not!
	KVILIB_API bool equalCI(const TQString &sz1,const TQChar *sz2)
	{
		const TQChar * c1 = sz1.unicode();
		const TQChar * c1e = c1 + sz1.length();

		if(!c1 || !sz2)return (c1 == sz2);

		while(c1 < c1e)
		{
			if(!sz2->unicode())return false; // sz1 has at least another character
			if(c1->lower().unicode() != sz2->lower().unicode())return false;
			c1++;
			sz2++;
		}
		return (c1 == c1e) && (!sz2->unicode());
	}

	KVILIB_API bool equalCS(const TQString &sz1,const char * sz2)
	{
		const TQChar * c1 = sz1.unicode();
		const TQChar * c1e = c1 + sz1.length();

		if(!c1)return !sz2;

		while((c1 < c1e) && (*sz2))
		{
			if(c1->unicode() != *sz2)return false;
			c1++;
			sz2++;
		}
		return ((c1 == c1e) && (*sz2 == '\0'));
	}

	KVILIB_API bool equalCI(const TQString &sz1,const char * sz2)
	{
		const TQChar * c1 = sz1.unicode();
		const TQChar * c1e = c1 + sz1.length();

		if(!c1)return !sz2;

		while((c1 < c1e) && (*sz2))
		{
			if(c1->lower().unicode() != tolower(*sz2))return false;
			c1++;
			sz2++;
		}
		return ((c1 == c1e) && (*sz2 == '\0'));
	}

	KVILIB_API int cmpCS(const TQString &sz1,const TQString &sz2)
	{
		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		const TQChar * c1e = c1 + sz1.length();
		const TQChar * c2e = c2 + sz2.length();

		if(!c1)
		{
			if(!c2)return 0;
			return -1;
		}
		if(!c2)return 1;


		for(;;)
		{
			if(c1 >= c1e)
			{
				if(c2 < c2e)return /* 0 */ - (c2->unicode());
				return 0;
			}
			if(c2 >= c2e)return c1->unicode() /* - 0 */;

			int diff = c1->unicode() - c2->unicode();
			if(diff)return diff;

			c1++;
			c2++;
		}

		return 0; // never here
	}

	KVILIB_API int cmpCI(const TQString &sz1,const TQString &sz2)
	{
		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		const TQChar * c1e = c1 + sz1.length();
		const TQChar * c2e = c2 + sz2.length();

		if(!c1)
		{
			if(!c2)return 0;
			return -1;
		}
		if(!c2)return 1;

		for(;;)
		{
			if(c1 >= c1e)
			{
				if(c2 < c2e)return /* 0 */ - (c2->lower().unicode());
				return 0;
			}
			if(c2 >= c2e)return c1->lower().unicode() /* - 0 */;

			int diff = c1->lower().unicode() - c2->lower().unicode();
			if(diff)return diff;

			c1++;
			c2++;
		}

		return 0; // never here
	}

	KVILIB_API int cmpCIN(const TQString &sz1,const TQString &sz2,unsigned int len)
	{
		if(len == 0)return 0; // assume equal
		unsigned int l1 = MY_MIN(len,sz1.length());
		unsigned int l = MY_MIN(l1,sz2.length()); // FIXME: THIS IS NOT OK

		const TQChar * c1 = sz1.unicode();
		const TQChar * c2 = sz2.unicode();
		const TQChar * c1e = c1 + l;

		if(!c1)
		{
			if(!c2)return 0;
			return -1;
		}
		if(!c2)return 1;

		int diff = 0;

		while((c1 < c1e) && !(diff = (c1->lower().unicode() - c2->lower().unicode())))
		{
			c1++;
			c2++;
		}

		return diff;
	}

	KVILIB_API void ensureLastCharIs(TQString &szString,const TQChar &c)
	{
		if(!lastCharIs(szString,c))szString.append(c);
	}

	KVILIB_API TQString getToken(TQString &szString,const TQChar &sep)
	{
		unsigned int i=0;
		while(i < szString.length())
		{
			if(szString[i] == sep)break;
			i++;
		}
		TQString ret;
		if(i == szString.length())
		{
			ret = szString;
			szString = "";
		} else {
			ret = szString.left(i);
			while(i < szString.length())
			{
				if(szString[i] != sep)break;
				i++;
			}
			if(i == szString.length())szString = "";
			else szString.remove(0,i);
		}
		return ret;
	}

	KVILIB_API void stripRightWhiteSpace(TQString &s)
	{
		unsigned int iRemove = 0;
		while(iRemove < s.length())
		{
			if(s.at(s.length() - (iRemove + 1)).isSpace())iRemove++;
			else break;
		}
		if(iRemove > 0)s.remove(s.length() - iRemove,iRemove);
	}

	KVILIB_API void stripRight(TQString &s,const TQChar &c)
	{
		unsigned int iRemove = 0;
		while(iRemove < s.length())
		{
			if(s.at(s.length() - (iRemove + 1)) == c)iRemove++;
			else break;
		}
		if(iRemove > 0)s.remove(s.length() - iRemove,iRemove);
	}

	KVILIB_API void stripLeft(TQString &s,const TQChar &c)
	{
		unsigned int iRemove = 0;
		while(iRemove < s.length())
		{
			if(s[iRemove] == c)
				iRemove++;
			else
				break;
		}
		if(iRemove > 0)s.remove(0,iRemove);
	}

	KVILIB_API void detach(TQString &sz)
	{
		sz.setLength(sz.length());
	}

	KVILIB_API const TQChar * nullTerminatedArray(const TQString &sz)
	{
		//sz.setLength(sz.length()); // detach!
		return (const TQChar *)sz.ucs2(); // MAY BE NULL!
	}
	
	KVILIB_API void appendNumber(TQString &s,double dReal)
	{
		char buffer[512];
		::sprintf(buffer,"%f",dReal);
		s.append(buffer);
	}
	
	KVILIB_API void appendNumber(TQString &s,int iInteger)
	{
		char buffer[64];
		::sprintf(buffer,"%d",iInteger);
		s.append(buffer);
	}

	KVILIB_API void appendNumber(TQString &s,kvi_i64_t iInteger)
	{
		char buffer[64];
		::sprintf(buffer,"%ld",iInteger);
		s.append(buffer);
	}
	
	KVILIB_API void appendNumber(TQString &s,kvi_u64_t uInteger)
	{
		char buffer[64];
		::sprintf(buffer,"%lu",uInteger);
		s.append(buffer);
	}
	
	KVILIB_API void appendNumber(TQString &s,unsigned int uInteger)
	{
		char buffer[64];
		::sprintf(buffer,"%u",uInteger);
		s.append(buffer);
	}

	KVILIB_API void vsprintf(TQString &s,const TQString &szFmt,kvi_va_list list)
	{
#define MEMINCREMENT 32

		int reallen = 0;
		int allocsize = MEMINCREMENT;

		//s.setLength(allocsize);

		const TQChar * fmt = nullTerminatedArray(szFmt);
		if(!fmt)
		{
			s = TQString();
			return;
		}

		TQChar * buffer = (TQChar *)kvi_malloc(sizeof(TQChar) * allocsize);
		//TQChar * p = (TQChar *)s.unicode();

		const char *argString;
		long argValue;
		unsigned long argUValue;
	
		//9999999999999999999999999999999\0
		char numberBuffer[32]; //enough ? 10 is enough for 32bit unsigned int...
		char *pNumBuf;
		unsigned int tmp;

		TQChar * p = buffer;

#define INCREMENT_MEM \
		{ \
			allocsize += MEMINCREMENT; \
			buffer = (TQChar *)kvi_realloc(buffer,sizeof(TQChar) * allocsize); \
			p = buffer + reallen; \
		}

#define INCREMENT_MEM_BY(numchars) \
		{ \
			allocsize += numchars + MEMINCREMENT; \
			buffer = (TQChar *)kvi_realloc(buffer,sizeof(TQChar) * allocsize); \
			p = buffer + reallen; \
		}


		for(; fmt->unicode() ; ++fmt)
		{
			if(reallen == allocsize)INCREMENT_MEM

			//copy up to a '%'
			if(fmt->unicode() != '%')
			{
				*p++ = *fmt;
				reallen++;
				continue;
			}

			++fmt; //skip this '%'
			switch(fmt->unicode())
			{
				case 's': // char * string
				{
					argString = kvi_va_arg(list,char *);
					if(!argString)argString = "[!NULL!]";
					TQString str(argString);
					if(str.isEmpty())continue;
					int len = str.length();
					const TQChar * ch = str.unicode();
					if(!ch)continue;
					if((allocsize - reallen) < len)INCREMENT_MEM_BY(len)
					while(len--)*p++ = *ch++;
					reallen += str.length();
					continue;
				}
				case 'S': // KviStr * string
				{
					KviStr * str = kvi_va_arg(list,KviStr *);
					if(!str)continue;
					if((allocsize - reallen) < str->len())INCREMENT_MEM_BY(str->len())
					argString = str->ptr();
					while(*argString)*p++ = TQChar(*argString++);
					reallen += str->len();
					continue;
				}
				case 'Q': // TQString * string
				{
					TQString * str = kvi_va_arg(list,TQString *);
					if(!str)continue;
					if(str->isEmpty())continue;
					int len = str->length();
					const TQChar * ch = str->unicode();
					if(!ch)continue;
					if((allocsize - reallen) < len)INCREMENT_MEM_BY(len)
					while(len--)*p++ = *ch++;
					reallen += str->length();
					continue;
				}
				case 'c': //char
				{
					//
					// I'm not sure about this...
					// In the linux kernel source the
					// unsigned char is extracted from an integer type.
					// We assume that gcc stacks a char argument
					// as sizeof(int) bytes value.
					// Is this always true ?
					//
					*p++ = (char)kvi_va_arg(list,int);
					reallen++;
					continue;
				}
				case 'q': // TQChar *
				{
					//
					// I'm not sure about this...
					// In the linux kernel source the
					// unsigned char is extracted from an integer type.
					// We assume that gcc stacks a char argument
					// as sizeof(int) bytes value.
					// Is this always true ?
					//
					*p++ = *((TQChar *)kvi_va_arg(list,TQChar *));
					reallen++;
					continue;
				}
				case 'd': //signed integer
				{
					argValue = kvi_va_arg(list,int);
					if(argValue < 0)
					{ //negative integer
						*p++ = '-';
						reallen++;
						argValue = -argValue; //need to have it positive
						// most negative integer exception (avoid completely senseless (non digit) responses)
						if(argValue < 0)argValue = 0;  //we get -0 here
					}
					//write the number in a temporary buffer
					pNumBuf = numberBuffer;
					do {
						tmp = argValue / 10;
						*pNumBuf++ = argValue - (tmp * 10) + '0';
					} while((argValue = tmp));
					//copy now....
					argUValue = pNumBuf - numberBuffer; //length of the number string
					if((allocsize - reallen) < (int)argUValue)INCREMENT_MEM_BY(argUValue)
					do { *p++ = TQChar(*--pNumBuf); } while(pNumBuf != numberBuffer);
					reallen += argUValue;
					continue;
				}
				case 'u': //unsigned integer
				{
					argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pNumBuf = numberBuffer;
					do {
						tmp = argUValue / 10;
						*pNumBuf++ = argUValue - (tmp * 10) + '0';
					} while((argUValue = tmp));
					//copy now....
					argValue = pNumBuf - numberBuffer; //length of the number string
					if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue)
					do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer);
					reallen += argValue;
					continue;
				}
				case 'h':
				case 'x': // hexadecimal unsigned integer
				{
					static char hexsmalldigits[]="0123456789abcdef";
					argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pNumBuf = numberBuffer;
					do {
						tmp = argUValue / 16;
						*pNumBuf++ = hexsmalldigits[argUValue - (tmp * 16)];
					} while((argUValue = tmp));
					//copy now....
					argValue = pNumBuf - numberBuffer; //length of the number string
					if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue)
					do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer);
					reallen += argValue;
					continue;
				}
				case 'H':
				case 'X': // hexadecimal unsigned integer
				{
					static char hexbigdigits[]="0123456789ABCDEF";
					argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pNumBuf = numberBuffer;
					do {
						tmp = argUValue / 16;
						*pNumBuf++ = hexbigdigits[argUValue - (tmp * 16)];
					} while((argUValue = tmp));
					//copy now....
					argValue = pNumBuf - numberBuffer; //length of the number string
					if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue)
					do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer);
					reallen += argValue;
					continue;
				}
				default: //a normal percent followed by some char
				{
					*p++ = '%';  //write it
					reallen++;
					if(fmt->unicode())
					{
						if(reallen == allocsize)INCREMENT_MEM
						*p++ = *fmt;
						reallen++;
					}
					continue;
				}
			}
		}

		s.setUnicode(buffer,reallen);
		kvi_free(buffer);
		//s.squeeze();
	}


	KVILIB_API TQString & sprintf(TQString &s,const TQString &szFmt,...)
	{
		kvi_va_list list;
		kvi_va_start_by_reference(list,szFmt);
		//print...with max 256 chars
		KviTQString::vsprintf(s,szFmt,list);
		kvi_va_end(list);
		return s;
	}

	KVILIB_API void appendFormatted(TQString &s,const TQString &szFmt,...)
	{
		TQString tmp;
		kvi_va_list list;
		kvi_va_start_by_reference(list,szFmt);
		//print...with max 256 chars
		KviTQString::vsprintf(tmp,szFmt,list);
		kvi_va_end(list);
		s.append(tmp);
	}

	KVILIB_API bool matchWildExpressionsCI(const TQString &szM1,const TQString &szM2)
	{
		//Matches two regular expressions containging wildcards (* and ?)
	
		//          s1
		//          m1
		// mask1 : *xor
		// mask2 : xorand*xor
		//         m2
		//          s2
	
		//                        s2
		//                       m2
		//                       | 
		// XorT!xor@111.111.111.11
		//
		// *!*@*.net
		//      |
		//      m1
		//      s1
		//

		const TQChar * m1 = (const TQChar *)szM1.ucs2();
		const TQChar * m2 = (const TQChar *)szM2.ucs2();

		if(!(m1 && m2 && (m1->unicode())))return false;
		const TQChar * savePos1 = 0;
		const TQChar * savePos2 = m2;
		while(m1->unicode())
		{
			//loop managed by m1 (initially first mask)
			if(m1->unicode()=='*')
			{
				//Found a wildcard in m1
				savePos1 = ++m1;            //move to the next char and save the position...this is our jolly
				if(!savePos1->unicode())return true;  //last was a wildcard , matches everything ahead...
				savePos2 = m2+1;            //next return state for the second string
				continue;                   //and return
			}
			if(!m2->unicode())return false;         //m2 finished and we had something to match here!
			if(m1->lower()==m2->lower())
			{
				//chars matched
				m1++;                       //Go ahead in the two strings
				m2++;                       //
				if((!(m1->unicode())) && m2->unicode() && savePos1)
				{
					//m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)...
					//retry matching the string following the * from the savePos2 (one char ahead last time)
					m1 = savePos1;          //back to char after wildcard
					m2 = savePos2;          //back to last savePos2
					savePos2++;             //next savePos2 will be next char
				}
			} else {
				if(m2->unicode() == '*')
				{
					//A wlidcard in the second string
					//Invert the game : mask1 <-> mask2
					//mask2 now leads the game...
					savePos1 = m1;          //aux
					m1 = m2;                //...swap
					m2 = savePos1;          //...swap
					savePos1 = m1;          //sync save pos1
					savePos2 = m2 + 1;      //sync save pos2
					continue;               //...and again
				}
				// m1 != m2 , m1 != * , m2 != *
				if((m1->unicode() == '?') || (m2->unicode() == '?'))
				{
					m1++;
					m2++;
					if((!(m1->unicode())) && m2->unicode() && savePos1)
					{
						//m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)...
						//retry matching the string following the * from the savePos2 (one char ahead last time)
						m1 = savePos1;          //back to char after wildcard
						m2 = savePos2;          //back to last savePos2
						savePos2++;             //next savePos2 will be next char
					}
				} else {
					if(savePos1)
					{
						//Have a jolly man...allow not matching...
						m1 = savePos1;          //go back to char after wildcard...need to rematch...
						m2 = savePos2;          //back to last savePos2
						savePos2++;             //and set next savePos2
					} else return false;        //No previous wildcards...not matched!
				}
			}
		}
		return (!(m2->unicode()));           //m1 surely finished , so for the match , m2 must be finished too
	}

	KVILIB_API bool matchStringCI(const TQString &szExp,const TQString &szStr,bool bIsRegExp,bool bExact)
	{
		TQString szWildcard;
		TQChar* ptr=(TQChar*)szExp.ucs2();
		if(!ptr) return 0;
		while(ptr->unicode())
		{
			if((ptr->unicode()=='[') || (ptr->unicode()==']'))
			{
				szWildcard.append("[");
				szWildcard.append(*ptr);
				szWildcard.append("]");
			} else {
				szWildcard.append(*ptr);
			}
			ptr++;
		}
		TQRegExp re(szWildcard,false,!bIsRegExp);
		if(bExact) return re.exactMatch(szStr);
		return re.search(szStr) != -1;
	}

	KVILIB_API bool matchStringCS(const TQString &szExp,const TQString &szStr,bool bIsRegExp,bool bExact)
	{
		TQString szWildcard;
		TQChar* ptr=(TQChar*)szExp.ucs2();
		if(!ptr) return 0;
		while(ptr->unicode())
		{
			if((ptr->unicode()=='[')) // <-- hum ?
			{
				szWildcard.append("[");
				szWildcard.append(*ptr);
				szWildcard.append("]");
			} else {
				szWildcard.append(*ptr);
			}
			ptr++;
		}
		TQRegExp re(szWildcard,true,!bIsRegExp);
		if(bExact) return re.exactMatch(szStr);
		return re.search(szStr) != -1;
	}

	KVILIB_API void cutFromFirst(TQString &s,const TQChar &c,bool bIncluded)
	{
		int idx = s.find(c);
		if(idx == -1)return;
		s.truncate(bIncluded ? idx : idx + 1);
	}

	KVILIB_API void cutFromLast(TQString &s,const TQChar &c,bool bIncluded)
	{
		int idx = s.findRev(c);
		if(idx == -1)return;
		s.truncate(bIncluded ? idx : idx + 1);
	}
	
	KVILIB_API void cutToFirst(TQString &s,const TQChar &c,bool bIncluded,bool bClearIfNotFound)
	{
		int idx = s.find(c);
		if(idx == -1)
		{
			if(bClearIfNotFound)s = "";
			return;
		}
		s.remove(0,bIncluded ? idx + 1 : idx);
	}
	
	KVILIB_API void cutToLast(TQString &s,const TQChar &c,bool bIncluded,bool bClearIfNotFound)
	{
		int idx = s.findRev(c);
		if(idx == -1)
		{
			if(bClearIfNotFound)s = "";
			return;
		}
		s.remove(0,bIncluded ? idx + 1 : idx);
	}

	KVILIB_API void cutFromFirst(TQString &s,const TQString &c,bool bIncluded)
	{
		int idx = s.find(c);
		if(idx == -1)return;
		s.truncate(bIncluded ? idx : idx + c.length());
	}

	KVILIB_API void cutFromLast(TQString &s,const TQString &c,bool bIncluded)
	{
		int idx = s.findRev(c);
		if(idx == -1)return;
		s.truncate(bIncluded ? idx : idx + c.length());
	}
	
	KVILIB_API void cutToFirst(TQString &s,const TQString &c,bool bIncluded,bool bClearIfNotFound)
	{
		int idx = s.find(c);
		if(idx == -1)
		{
			if(bClearIfNotFound)s = "";
			return;
		}
		s.remove(0,bIncluded ? idx + c.length() : idx);
	}
	
	KVILIB_API void cutToLast(TQString &s,const TQString &c,bool bIncluded,bool bClearIfNotFound)
	{
		int idx = s.findRev(c);
		if(idx == -1)
		{
			if(bClearIfNotFound)s = "";
			return;
		}
		s.remove(0,bIncluded ? idx + c.length() : idx);
	}

	KVILIB_API TQString upperISO88591(const TQString &szSrc)
	{
		const TQChar * c = nullTerminatedArray(szSrc);
		if(!c)
		{
			TQString ret;
			return ret;
		}
		TQChar * buffer = (TQChar *)kvi_malloc(sizeof(TQChar) * szSrc.length());
		TQChar * b = buffer;
		unsigned short us = c->unicode();
		while(us)
		{
			if(us < 256)
				*b=TQChar((unsigned short)iso88591_toUpper_map[us]);
			else
				*b = *c;
			c++;
			b++;
			us = c->unicode();
		}
		TQString ret(buffer,szSrc.length());
		kvi_free(buffer);
		return ret;
	}
	
	KVILIB_API TQString lowerISO88591(const TQString &szSrc)
	{
		const TQChar * c = nullTerminatedArray(szSrc);
		if(!c)
		{
			TQString ret;
			return ret;
		}
		TQChar * buffer = (TQChar *)kvi_malloc(sizeof(TQChar) * szSrc.length());
		TQChar * b = buffer;
		unsigned short us = c->unicode();
		while(us)
		{
			if(us < 256)
			{
				*b=TQChar((unsigned short)iso88591_toLower_map[us]);
			} else
				*b = *c;
			c++;
			b++;
			us = c->unicode();
		}
		TQString ret(buffer,szSrc.length());
		kvi_free(buffer);
		return ret;
	}

	KVILIB_API void transliterate(TQString &s,const TQString &szToFind,const TQString &szReplacement)
	{
		int i=0;
		int il = MY_MIN(szToFind.length(),szReplacement.length());
		while(i < il)
		{
			int k=0;
			int kl = s.length();
			while(k < kl)
			{
				if(s[k] == szToFind[i])s[k] = szReplacement[i];
				k++;
			}
			i++;
		}
	}
	
	static char hexdigits[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
	
	KVILIB_API void bufferToHex(TQString &szRetBuffer,const unsigned char * buffer,unsigned int len)
	{
		szRetBuffer.setLength(len * 2);
		unsigned int i=0;
		while(i < (len*2))
		{
			szRetBuffer[int(i)] = TQChar( (unsigned int) hexdigits[(*buffer) / 16] );
			i++;
			szRetBuffer[int(i)] = TQChar( (unsigned int)hexdigits[(*buffer) % 16] );
			i++;
			buffer++;
		}
	}
};
