aboutsummaryrefslogtreecommitdiffstats
//=============================================================================
//
//   File : KviQString.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-2010 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 option) 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 QString class
//

#include "KviQString.h"
#include "KviCString.h"
#include "KviMemory.h"
#include "KviLocale.h"
#include "KviRegExp.h"

#include <cctype> // for tolower()
#include <cstdio> // for sprintf()

// 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 KviQString
{
	// The global empty (and null) string
	const QString Empty;

	static char cHexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

	bool equalCSN(const QString & sz1, const QString & sz2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return true;
		}
		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		unsigned int uLMin = MY_MIN(sz1.length(), sz2.length());
		if(uLMin < uLen)
		{
			return false;
		}
		const QChar * pC1e = pC1 + uLen;

		if(!pC1 || !pC2)
		{
			return (pC1 == pC2);
		}

		while(pC1 < pC1e)
		{
			if(pC1->unicode() != pC2->unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e);
	}

	bool equalCIN(const QString & sz1, const QString & sz2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return true;
		}
		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		unsigned int uLMin = MY_MIN(sz1.length(), sz2.length());
		if(uLMin < uLen)
		{
			return false;
		}
		const QChar * pC1e = pC1 + uLen;

		if(!pC1 || !pC2)
		{
			return (pC1 == pC2);
		}

		while(pC1 < pC1e)
		{
			if(pC1->toLower().unicode() != pC2->toLower().unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e);
	}

	bool equalCSN(const QString & sz1, const char * pc2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return true;
		}
		const QChar * pC1 = sz1.unicode();
		if((unsigned)sz1.length() < uLen)
		{
			return false;
		}
		const QChar * pC1e = pC1 + uLen;

		if(!pc2)
		{
			return !pC1;
		}
		if(!pC1)
		{
			return !pc2;
		}

		while((pC1 < pC1e) && (*pc2))
		{
			if(pC1->unicode() != *pc2)
			{
				return false;
			}
			pC1++;
			pc2++;
		}
		return (pC1 == pC1e);
	}

	bool equalCIN(const QString & sz1, const char * pc2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return true;
		}
		const QChar * pC1 = sz1.unicode();
		if((unsigned)sz1.length() < uLen)
		{
			return false;
		}
		const QChar * pC1e = pC1 + uLen;

		if(!pc2)
		{
			return !pC1;
		}
		if(!pC1)
		{
			return !(*pc2);
		}

		while((pC1 < pC1e) && (*pc2))
		{
			if(pC1->toLower().unicode() != tolower(*pc2))
			{
				return false;
			}
			pC1++;
			pc2++;
		}
		return (pC1 == pC1e);
	}

	// pC2 is assumed to be null terminated, sz1 is not!
	bool equalCIN(const QString & sz1, const QChar * pC2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return true;
		}
		const QChar * pC1 = sz1.unicode();
		if((unsigned)sz1.length() < uLen)
		{
			return false;
		}
		const QChar * pC1e = pC1 + uLen;

		if(!pC2)
		{
			return !pC1;
		}
		if(!pC1)
		{
			return !(pC2->unicode());
		}

		while((pC1 < pC1e) && (pC2->unicode()))
		{
			if(pC1->toLower().unicode() != pC2->toLower().unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e);
	}

	QString makeSizeReadable(quint64 bytes)
	{
		double dSize = bytes;
		if(dSize < 1024)
		{
			return QString(__tr2qs("%1 bytes")).arg(dSize, 0, 'f', 0);
		}

		dSize /= 1024;
		if(dSize < 1024)
		{
			return QString(__tr2qs("%1 KiB")).arg(dSize, 0, 'f', 3);
		}

		dSize /= 1024;
		if(dSize < 1024)
		{
			return QString(__tr2qs("%1 MiB")).arg(dSize, 0, 'f', 3);
		}

		//Pirated DVD?;)
		dSize /= 1024;
		if(dSize < 1024)
		{
			return QString(__tr2qs("%1 GiB")).arg(dSize, 0, 'f', 3);
		}

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

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

		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		const QChar * pC1e = pC1 + sz1.length();

		if(!pC1 || !pC2)
		{
			return (pC1 == pC2);
		}

		while(pC1 < pC1e)
		{
			if(pC1->unicode() != pC2->unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e);
	}

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

		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		const QChar * pC1e = pC1 + sz1.length();

		if(!pC1 || !pC2)
		{
			return (pC1 == pC2);
		}

		while(pC1 < pC1e)
		{
			if(pC1->toLower().unicode() != pC2->toLower().unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e);
	}

	// sz2 is assumed to be null terminated, sz1 is not!
	bool equalCI(const QString & sz1, const QChar * pC2)
	{
		const QChar * pC1 = sz1.unicode();
		const QChar * pC1e = pC1 + sz1.length();

		if(!pC1 || !pC2)
		{
			return (pC1 == pC2);
		}

		while(pC1 < pC1e)
		{
			if(!pC2->unicode())
			{
				// sz1 has at least another character
				return false;
			}
			if(pC1->toLower().unicode() != pC2->toLower().unicode())
			{
				return false;
			}
			pC1++;
			pC2++;
		}
		return (pC1 == pC1e) && (!pC2->unicode());
	}

	bool equalCS(const QString & sz1, const char * pc2)
	{
		const QChar * pC1 = sz1.unicode();
		const QChar * pC1e = pC1 + sz1.length();

		if(!pC1)
		{
			return !pc2;
		}

		while((pC1 < pC1e) && (*pc2))
		{
			if(pC1->unicode() != *pc2)
			{
				return false;
			}
			pC1++;
			pc2++;
		}
		return ((pC1 == pC1e) && (*pc2 == '\0'));
	}

	bool equalCI(const QString & sz1, const char * pc2)
	{
		const QChar * pC1 = sz1.unicode();
		const QChar * pC1e = pC1 + sz1.length();

		if(!pC1)
		{
			return !pc2;
		}

		while((pC1 < pC1e) && (*pc2))
		{
			if(pC1->toLower().unicode() != tolower(*pc2))
			{
				return false;
			}
			pC1++;
			pc2++;
		}
		return ((pC1 == pC1e) && (*pc2 == '\0'));
	}

	int cmpCI(const QString & sz1, const QString & sz2, bool bNonAlphaGreater)
	{
		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		const QChar * pC1e = pC1 + sz1.length();
		const QChar * pC2e = pC2 + sz2.length();

		if(!pC1)
		{
			if(!pC2)
			{
				return 0;
			}
			return -1;
		}
		if(!pC2)
		{
			return 1;
		}

		for(;;)
		{
			if(bNonAlphaGreater)
			{
				if(pC1->isLetterOrNumber() && !pC2->isLetterOrNumber())
				{
					return -1;
				}
				else if(!pC1->isLetterOrNumber() && pC2->isLetterOrNumber())
				{
					return 1;
				}
			}

			if(pC1 >= pC1e)
			{
				if(pC2 < pC2e)
				{
					return /* 0 */ -(pC2->toLower().unicode());
				}
				return 0;
			}

			if(pC2 >= pC2e)
			{
				return pC1->toLower().unicode() /* - 0 */;
			}

			int iDiff = pC1->toLower().unicode() - pC2->toLower().unicode();

			if(iDiff)
			{
				return iDiff;
			}

			pC1++;
			pC2++;
		}

		return 0; // never here
	}

	int cmpCIN(const QString & sz1, const QString & sz2, unsigned int uLen)
	{
		if(uLen == 0)
		{
			// assume equal
			return 0;
		}
		unsigned int u1 = MY_MIN(uLen, (unsigned)sz1.length());
		unsigned int u2 = MY_MIN(u1, (unsigned)sz2.length()); // FIXME: THIS IS NOT OK

		const QChar * pC1 = sz1.unicode();
		const QChar * pC2 = sz2.unicode();
		const QChar * pC1e = pC1 + u2;

		if(!pC1)
		{
			if(!pC2)
			{
				return 0;
			}
			return -1;
		}
		if(!pC2)
		{
			return 1;
		}

		int iDiff = 0;

		while((pC1 < pC1e) && !(iDiff = (pC1->toLower().unicode() - pC2->toLower().unicode())))
		{
			pC1++;
			pC2++;
		}

		return iDiff;
	}

	void ensureLastCharIs(QString & szSrc, const QChar & c)
	{
		if(!szSrc.endsWith(c))
		{
			szSrc.append(c);
		}
	}

	QString getToken(QString & szSrc, const QChar & sep)
	{
		int i = 0;
		while(i < szSrc.length())
		{
			if(szSrc[i] == sep)
			{
				break;
			}
			i++;
		}
		QString szRet;
		if(i == szSrc.length())
		{
			szRet = szSrc;
			szSrc = "";
		}
		else
		{
			szRet = szSrc.left(i);
			while(i < szSrc.length())
			{
				if(szSrc[i] != sep)
				{
					break;
				}
				i++;
			}
			if(i == szSrc.length())
			{
				szSrc = "";
			}
			else
			{
				szSrc.remove(0, i);
			}
		}
		return szRet;
	}

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

	void stripRight(QString & szSrc, const QChar & c)
	{
		int iRemove = 0;
		while(iRemove < szSrc.length())
		{
			if(szSrc.at(szSrc.length() - (iRemove + 1)) == c)
			{
				iRemove++;
			}
			else
			{
				break;
			}
		}
		if(iRemove > 0)
		{
			szSrc.remove(szSrc.length() - iRemove, iRemove);
		}
	}

	void stripLeft(QString & szSrc, const QChar & c)
	{
		int iRemove = 0;
		while(iRemove < szSrc.length())
		{
			if(szSrc[iRemove] == c)
			{
				iRemove++;
			}
			else
			{
				break;
			}
		}
		if(iRemove > 0)
		{
			szSrc.remove(0, iRemove);
		}
	}

	void appendNumber(QString & szSrc, double dReal)
	{
#ifdef COMPILE_ON_MINGW
		//workaround for #801
		QString cBuffer;
		cBuffer.setNum(dReal, 'f');
#else
		char cBuffer[512];
		::sprintf(cBuffer, "%f", dReal);
#endif
		szSrc.append(cBuffer);
	}

	void appendNumber(QString & szSrc, int iInteger)
	{
		char cBuffer[64];
		::sprintf(cBuffer, "%d", iInteger);
		szSrc.append(cBuffer);
	}

	void appendNumber(QString & szSrc, kvi_i64_t iInteger)
	{
		char cBuffer[64];
		::sprintf(cBuffer, "%lld", (long long int)iInteger);
		szSrc.append(cBuffer);
	}

	void appendNumber(QString & szSrc, kvi_u64_t uInteger)
	{
		char cBuffer[64];
		::sprintf(cBuffer, "%llu", (long long unsigned int)uInteger);
		szSrc.append(cBuffer);
	}

	void appendNumber(QString & szSrc, unsigned int uInteger)
	{
		char cBuffer[64];
		::sprintf(cBuffer, "%u", uInteger);
		szSrc.append(cBuffer);
	}

	void vsprintf(QString & szSrc, const QString & szFmt, kvi_va_list list)
	{
#define MEMINCREMENT 32

		int iRealLen = 0;
		int iAllocSize = MEMINCREMENT;

		//szSrc.resize(allocsize);

		const QChar * pFmt = szFmt.constData();
		if(!pFmt)
		{
			szSrc = QString();
			return;
		}

		QChar * pBuffer = (QChar *)KviMemory::allocate(sizeof(QChar) * iAllocSize);
		//QChar * p = (QChar *)s.unicode();

		char * pcArgString;
		long lArgValue;
		unsigned long ulArgValue;

		//9999999999999999999999999999999\0
		char cNumberBuffer[32]; //enough ? 10 is enough for 32bit unsigned int...
		char * pcNumBuf;
		unsigned int iTmp;

		QChar * p = pBuffer;

#define INCREMENT_MEM                                                                  \
	{                                                                                  \
		iAllocSize += MEMINCREMENT;                                                    \
		pBuffer = (QChar *)KviMemory::reallocate(pBuffer, sizeof(QChar) * iAllocSize); \
		p = pBuffer + iRealLen;                                                        \
	}

#define INCREMENT_MEM_BY(numchars)                                                     \
	{                                                                                  \
		iAllocSize += numchars + MEMINCREMENT;                                         \
		pBuffer = (QChar *)KviMemory::reallocate(pBuffer, sizeof(QChar) * iAllocSize); \
		p = pBuffer + iRealLen;                                                        \
	}

		for(; pFmt->unicode(); ++pFmt)
		{
			if(iRealLen == iAllocSize)
				INCREMENT_MEM

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

			++pFmt; //skip this '%'
			switch(pFmt->unicode())
			{
				case 's': // char * string
				{
					pcArgString = kvi_va_arg(list, char *);
					if(!pcArgString)
					{
						pcArgString = (char *)"[!NULL!]";
					}
					QString szStr(pcArgString);
					if(szStr.isEmpty())
					{
						continue;
					}
					int iLen = szStr.length();
					const QChar * pCh = szStr.unicode();
					if(!pCh)
					{
						continue;
					}
					if((iAllocSize - iRealLen) < iLen)
					{
						INCREMENT_MEM_BY(iLen)
					}
					while(iLen--)
					{
						*p++ = *pCh++;
					}
					iRealLen += szStr.length();
					continue;
				}
				case 'S': // KviCString * string
				{
					KviCString * szStr = kvi_va_arg(list, KviCString *);
					if(!szStr)
					{
						continue;
					}
					if((iAllocSize - iRealLen) < szStr->len())
					{
						INCREMENT_MEM_BY(szStr->len())
					}
					pcArgString = szStr->ptr();
					while(*pcArgString)
					{
						*p++ = QChar(*pcArgString++);
					}
					iRealLen += szStr->len();
					continue;
				}
				case 'Q': // QString * string
				{
					QString * szStr = kvi_va_arg(list, QString *);
					if(!szStr)
					{
						continue;
					}
					if(szStr->isEmpty())
					{
						continue;
					}
					int iLen = szStr->length();
					const QChar * pCh = szStr->unicode();
					if(!pCh)
					{
						continue;
					}
					if((iAllocSize - iRealLen) < iLen)
					{
						INCREMENT_MEM_BY(iLen)
					}
					while(iLen--)
					{
						*p++ = *pCh++;
					}
					iRealLen += szStr->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);
					iRealLen++;
					continue;
				}
				case 'q': // QChar *
				{
					//
					// 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++ = *((QChar *)kvi_va_arg(list, QChar *));
					iRealLen++;
					continue;
				}
				case 'd': //signed integer
				{
					lArgValue = kvi_va_arg(list, int);
					if(lArgValue < 0)
					{
						//negative integer
						*p++ = '-';
						iRealLen++;
						//need to have it positive. most negative integer exception (avoid completely senseless (non digit) responses)
						lArgValue = -lArgValue;
						if(lArgValue < 0)
						{
							//we get -0 here
							lArgValue = 0;
						}
					}
					//write the number in a temporary buffer
					pcNumBuf = cNumberBuffer;
					do
					{
						iTmp = lArgValue / 10;
						*pcNumBuf++ = lArgValue - (iTmp * 10) + '0';
					} while((lArgValue = iTmp));
					//copy now....
					ulArgValue = pcNumBuf - cNumberBuffer; //length of the number string
					if((iAllocSize - iRealLen) < (int)ulArgValue)
					{
						INCREMENT_MEM_BY(ulArgValue)
					}
					do
					{
						*p++ = QChar(*--pcNumBuf);
					} while(pcNumBuf != cNumberBuffer);
					iRealLen += ulArgValue;
					continue;
				}
				case 'u': //unsigned integer
				{
					ulArgValue = kvi_va_arg(list, unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pcNumBuf = cNumberBuffer;
					do
					{
						iTmp = ulArgValue / 10;
						*pcNumBuf++ = ulArgValue - (iTmp * 10) + '0';
					} while((ulArgValue = iTmp));
					//copy now....
					lArgValue = pcNumBuf - cNumberBuffer; //length of the number string
					if((iAllocSize - iRealLen) < lArgValue)
					{
						INCREMENT_MEM_BY(lArgValue)
					}
					do
					{
						*p++ = *--pcNumBuf;
					} while(pcNumBuf != cNumberBuffer);
					iRealLen += lArgValue;
					continue;
				}
				case 'h':
				case 'x': // hexadecimal unsigned integer
				{
					static char cHexSmallDigits[] = "0123456789abcdef";
					ulArgValue = kvi_va_arg(list, unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pcNumBuf = cNumberBuffer;
					do
					{
						iTmp = ulArgValue / 16;
						*pcNumBuf++ = cHexSmallDigits[ulArgValue - (iTmp * 16)];
					} while((ulArgValue = iTmp));
					//copy now....
					lArgValue = pcNumBuf - cNumberBuffer; //length of the number string
					if((iAllocSize - iRealLen) < lArgValue)
					{
						INCREMENT_MEM_BY(lArgValue)
					}
					do
					{
						*p++ = *--pcNumBuf;
					} while(pcNumBuf != cNumberBuffer);
					iRealLen += lArgValue;
					continue;
				}
				case 'H':
				case 'X': // hexadecimal unsigned integer
				{
					static char cHexBigDigits[] = "0123456789ABCDEF";
					ulArgValue = kvi_va_arg(list, unsigned int); //many implementations place int here
					//write the number in a temporary buffer
					pcNumBuf = cNumberBuffer;
					do
					{
						iTmp = ulArgValue / 16;
						*pcNumBuf++ = cHexBigDigits[ulArgValue - (iTmp * 16)];
					} while((ulArgValue = iTmp));
					//copy now....
					lArgValue = pcNumBuf - cNumberBuffer; //length of the number string
					if((iAllocSize - iRealLen) < lArgValue)
					{
						INCREMENT_MEM_BY(lArgValue)
					}
					do
					{
						*p++ = *--pcNumBuf;
					} while(pcNumBuf != cNumberBuffer);
					iRealLen += lArgValue;
					continue;
				}
				default:
				{
					// a normal percent followed by some char
					*p++ = '%'; //write it
					iRealLen++;
					if(pFmt->unicode())
					{
						if(iRealLen == iAllocSize)
						{
							INCREMENT_MEM
						}
						*p++ = *pFmt;
						iRealLen++;
					}
					continue;
				}
			}
		}

		szSrc.setUnicode(pBuffer, iRealLen);
		KviMemory::free(pBuffer);
		//szSrc.squeeze();
	}

	void appendFormatted(QString & szSrc, QString szFmt, ...)
	{
		QString szTmp;
		kvi_va_list list;
		kvi_va_start(list, szFmt);
		//print...with max 256 chars
		KviQString::vsprintf(szTmp, szFmt, list);
		kvi_va_end(list);
		szSrc.append(szTmp);
	}

	bool matchWildExpressions(const QString & szM1, const QString & szM2)
	{
		//          s1
		//          pM1
		// mask1 : *xor
		// mask2 : xorand*xor
		//         m2
		//          s2

		//                        s2
		//                       m2
		//                       |
		// XorT!xor@111.111.111.11
		//
		// *!*@*.net
		//      |
		//      pM1
		//      s1
		//

		const QChar * pM1 = (const QChar *)szM1.constData();
		const QChar * pM2 = (const QChar *)szM2.constData();

		if(!(pM1 && pM2 && (pM1->unicode())))
		{
			return false;
		}

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

	bool matchString(const QString & szExp, const QString & szStr, bool bIsRegExp, bool bExact, bool bCs)
	{
		QString szWildcard;

		if(!bIsRegExp)
		{
			// In wildcard matching mode, KviRegExp not only interprets * and ?, but also [ ]
			// This workaround embeds square brackes in square brackets (ticket #1264)

			QChar * pPtr = (QChar *)szExp.constData();

			if(!pPtr)
				return false;

			while(pPtr->unicode())
			{
				if((pPtr->unicode() == '[') || (pPtr->unicode() == ']'))
				{
					szWildcard.append("[");
					szWildcard.append(*pPtr);
					szWildcard.append("]");
				}
				else
				{
					szWildcard.append(*pPtr);
				}
				pPtr++;
			}
		}
		else
		{
			szWildcard = szExp;
		}

		KviRegExp re(szWildcard, bCs ? KviRegExp::CaseSensitive : KviRegExp::CaseInsensitive, bIsRegExp ? KviRegExp::RegExp : KviRegExp::Wildcard);

		if(bExact)
			return re.exactMatch(szStr);
		return re.indexIn(szStr) != -1;
	}

	void cutFromFirst(QString & szSrc, const QChar & c, bool bIncluded)
	{
		int iIdx = szSrc.indexOf(c);

		if(iIdx == -1)
			return;
		szSrc.truncate(bIncluded ? iIdx : iIdx + 1);
	}

	void cutFromFirst(QString & szSrc, const QString & szFind, bool bIncluded)
	{
		int iIdx = szSrc.indexOf(szFind);
		if(iIdx == -1)
			return;
		szSrc.truncate(bIncluded ? iIdx : iIdx + szFind.length());
	}

	void cutFromLast(QString & szSrc, const QChar & c, bool bIncluded)
	{
		int iIdx = szSrc.lastIndexOf(c);

		if(iIdx == -1)
			return;
		szSrc.truncate(bIncluded ? iIdx : iIdx + 1);
	}

	void cutFromLast(QString & szSrc, const QString & szFind, bool bIncluded)
	{
		int iIdx = szSrc.lastIndexOf(szFind);
		if(iIdx == -1)
			return;
		szSrc.truncate(bIncluded ? iIdx : iIdx + szFind.length());
	}

	void cutToFirst(QString & szSrc, const QChar & c, bool bIncluded, bool bClearIfNotFound)
	{
		int iIdx = szSrc.indexOf(c);
		if(iIdx == -1)
		{
			if(bClearIfNotFound)
				szSrc = "";
			return;
		}
		szSrc.remove(0, bIncluded ? iIdx + 1 : iIdx);
	}

	void cutToFirst(QString & szSrc, const QString & szFind, bool bIncluded, bool bClearIfNotFound)
	{
		int iIdx = szSrc.indexOf(szFind);
		if(iIdx == -1)
		{
			if(bClearIfNotFound)
				szSrc = "";
			return;
		}
		szSrc.remove(0, bIncluded ? iIdx + szFind.length() : iIdx);
	}

	void cutToLast(QString & szSrc, const QChar & c, bool bIncluded, bool bClearIfNotFound)
	{
		int iIdx = szSrc.lastIndexOf(c);
		if(iIdx == -1)
		{
			if(bClearIfNotFound)
				szSrc = "";
			return;
		}
		szSrc.remove(0, bIncluded ? iIdx + 1 : iIdx);
	}

	void cutToLast(QString & szSrc, const QString & szFind, bool bIncluded, bool bClearIfNotFound)
	{
		int iIdx = szSrc.lastIndexOf(szFind);
		if(iIdx == -1)
		{
			if(bClearIfNotFound)
				szSrc = "";
			return;
		}
		szSrc.remove(0, bIncluded ? iIdx + szFind.length() : iIdx);
	}

	QString leftToFirst(QString & szSrc, const QChar & c, bool bIncluded, bool bReturnFullStringIfNotFound)
	{
		int iIdx = szSrc.indexOf(c);
		if(iIdx == -1)
			return bReturnFullStringIfNotFound ? szSrc : QString();
		return szSrc.left(bIncluded ? iIdx + 1 : iIdx);
	}

	QString leftToFirst(QString & szSrc, const QString & szFind, bool bIncluded, bool bReturnFullStringIfNotFound)
	{
		int iIdx = szSrc.indexOf(szFind);
		if(iIdx == -1)
			return bReturnFullStringIfNotFound ? szSrc : QString();
		return szSrc.left(bIncluded ? iIdx + 1 : iIdx);
	}

	QString leftToLast(QString & szSrc, const QChar & c, bool bIncluded, bool bReturnFullStringIfNotFound)
	{
		int iIdx = szSrc.lastIndexOf(c);
		if(iIdx == -1)
			return bReturnFullStringIfNotFound ? szSrc : QString();
		return szSrc.left(bIncluded ? iIdx + 1 : iIdx);
	}

	QString leftToLast(QString & szSrc, const QString & szFind, bool bIncluded, bool bReturnFullStringIfNotFound)
	{
		int iIdx = szSrc.lastIndexOf(szFind);
		if(iIdx == -1)
			return bReturnFullStringIfNotFound ? szSrc : QString();
		return szSrc.left(bIncluded ? iIdx + 1 : iIdx);
	}

	QString upperISO88591(const QString & szSrc)
	{
		const QChar * pC = szSrc.constData();
		if(!pC)
		{
			QString szRet;
			return szRet;
		}
		QChar * pBuffer = (QChar *)KviMemory::allocate(sizeof(QChar) * szSrc.length());
		QChar * pB = pBuffer;
		unsigned short usX = pC->unicode();
		while(usX)
		{
			if(usX < 256)
				*pB = QChar((unsigned short)iso88591_toUpper_map[usX]);
			else
				*pB = *pC;
			pC++;
			pB++;
			usX = pC->unicode();
		}
		QString szRet(pBuffer, szSrc.length());
		KviMemory::free(pBuffer);
		return szRet;
	}

	QString lowerISO88591(const QString & szSrc)
	{
		const QChar * pC = szSrc.constData();
		if(!pC)
		{
			QString szRet;
			return szRet;
		}
		QChar * pBuffer = (QChar *)KviMemory::allocate(sizeof(QChar) * szSrc.length());
		QChar * pB = pBuffer;
		unsigned short usX = pC->unicode();
		while(usX)
		{
			if(usX < 256)
			{
				*pB = QChar((unsigned short)iso88591_toLower_map[usX]);
			}
			else
				*pB = *pC;
			pC++;
			pB++;
			usX = pC->unicode();
		}
		QString szRet(pBuffer, szSrc.length());
		KviMemory::free(pBuffer);
		return szRet;
	}

	void transliterate(QString & szSrc, const QString & szToFind, const QString & szReplacement)
	{
		int i = 0;
		int il = MY_MIN(szToFind.length(), szReplacement.length());
		while(i < il)
		{
			int k = 0;
			int kl = szSrc.length();
			while(k < kl)
			{
				if(szSrc[k] == szToFind[i])
				{
					szSrc[k] = szReplacement[i];
				}
				k++;
			}
			i++;
		}
	}

	void bufferToHex(QString & szRetBuffer, const unsigned char * pcBuffer, unsigned int uLen)
	{
		szRetBuffer.resize(uLen * 2);
		unsigned int u = 0;
		while(u < (uLen * 2))
		{
			szRetBuffer[int(u)] = QChar((unsigned int)cHexDigits[(*pcBuffer) / 16]);
			u++;
			szRetBuffer[int(u)] = QChar((unsigned int)cHexDigits[(*pcBuffer) % 16]);
			u++;
			pcBuffer++;
		}
	}

	void escapeKvs(QString * szData, uint uFlags)
	{
		// escape any $;\%
		szData->replace("\\", "\\\\");
		szData->replace("\"", "\\\"");
		szData->replace(";", "\\;");
		if(!(uFlags & PermitFunctions))
			szData->replace("$", "\\$");
		if(!(uFlags & PermitVariables))
			szData->replace("%", "\\%");
		if(!(uFlags & PermitMultiLine))
		{
			szData->replace("\r", " ");
			szData->replace("\n", " ");
		}
		if(uFlags & EscapeSpace)
			szData->replace(" ", "\\ ");

		if(uFlags & EscapeParenthesis)
		{
			szData->replace("(", "\\(");
			szData->replace(")", "\\)");
		}
	}

	QString toHtmlEscaped(QString szData)
	{
		return szData.toHtmlEscaped();
	}
}