//=============================================================================
//
//   File : libkvistr.cpp
//   Creation date : Thu Dec 27 2001 17:13:12 GMT by Szymon Stefanek
//
//   This str is part of the KVirc irc client distribution
//   Copyright (C) 2001-2005 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. ,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
//=============================================================================

//#warning: FIXME: Incomplete documentation ('seealso', 'example', etc)

#include "kvi_module.h"

#include "kvi_locale.h"
#include "kvi_mirccntrl.h"
#include "kvi_qstring.h"
#include "kvi_debug.h"
#include "kvi_settings.h"
#include "kvi_malloc.h"

#include "kvi_kvs_arraycast.h"
#include <qregexp.h>
#include <qclipboard.h>

#ifdef COMPILE_SSL_SUPPORT
	#include <openssl/evp.h>
#endif

/*
	@doc: str.section
	@type:
		function
	@title:
		$str.section
	@short:
		Returns a section of the string.
	@syntax:
		<string> $str.section(<string_to_split:string>,<separator:string>,<pos_from:int>,<pos_to:int>)
	@description:
		Returns a section of the string.
		The <string_to_split> is treated as a sequence of fields separated by <separator>.[br]
		The returned string consists of the fields from position start <pos_from> to position end <pos_to>.[br]
		Fields are numbered 0, 1, 2, etc., counting from the left, and -1, -2, etc., counting from right to left.[br]
	@examples:
		[example]
		%s = $str.section( "forename**middlename**surname**phone","**", 2, 2 );
		%s is "surname".
		[/example]
*/

static bool str_kvs_fnc_section(KviKvsModuleFunctionCall * c)
{
	QString szString, szSeparator,szSplittedString;
	kvs_int_t iPosFrom, iPosTo;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string_to_split",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("separator",KVS_PT_NONEMPTYSTRING,0,szSeparator)
		KVSM_PARAMETER("pos_from",KVS_PT_INT,0,iPosFrom)
		KVSM_PARAMETER("pos_to",KVS_PT_INT,0,iPosTo)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.section(szSeparator,iPosFrom,iPosTo));
	return true;
}

/*
	@doc: str.fromClipboard
	@type:
		function
	@title:
		$str.fromClipboard
	@short:
		Returns a string from clipboard.
	@syntax:
		<string> $str.fromClipboard()
	@description:
		Returns clipboard's contents.
	@examples:
		[example]
		%s = $str.fromClipboard();
		[/example]
	@seealso:
		[cmd]str.toClipboard[/cmd]
*/

static bool str_kvs_fnc_fromclipboard(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
	KVSM_PARAMETERS_END(c)
	QClipboard *cb = QApplication::clipboard();
        szString = cb->text(QClipboard::Clipboard);
	c->returnValue()->setString(szString);
	return true;
}

/*
	@doc: system.toClipboard
	@type:
		command
	@title:
		system.toClipboard
	@keyterms:
		OS clipboard
	@short:
		Sets the OS clipboard contents
	@syntax:
		system.setenv <value:string>
	@description:
		Sets the OS clipboard contents
	@seealso:
		[fnc]$str.fromClipboard[/fnc]
*/

static bool str_kvs_cmd_toClipboard(KviKvsModuleCommandCall * c)
{
	QString szValue;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("value",KVS_PT_STRING,KVS_PF_OPTIONAL,szValue)
	KVSM_PARAMETERS_END(c)
	QClipboard *cb = QApplication::clipboard();
	cb->setText(szValue, QClipboard::Clipboard );
	return true;
}

/*
	@doc: str.len
	@type:
		function
	@title:
		$str.len
	@short:
		Returns the lenght of the given string
	@syntax:
		<uint> $str.len(<data:string>)
	@description:
		Returns the lenght (that is, number of characters) of the given string.
		This function is internally aliased to [fnc]$str.length[/fnc]() too.
*/

/*
	@doc: str.length
	@type:
		function
	@title:
		$str.length
	@short:
		Returns the lenght of the given string
	@syntax:
		<uint> $str.length(<data:string>)
	@description:
		Returns the lenght (that is, number of characters) of the given string.
		This function is internally aliased to [fnc]$str.len[/fnc]() too.
*/

static bool str_kvs_fnc_len(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setInteger(szString.length());
	return true;
}

/*
	@doc: str.lowcase
	@type:
		function
	@title:
		$str.lowcase
	@short:
		Returns the given string with all characters turned to lower case
	@syntax:
		<string> $str.lowcase(<string_to_convert:string>)
	@description:
		Returns the <string_to_convert> with all characters turned to lower case.
		Warning: this function uses ISO-8859-1 locale to make the case translation.
		If you want to use a locale aware translation mapping then please
		use localelowcase.
*/

static bool str_kvs_fnc_lowcase(KviKvsModuleFunctionCall * c)
{
	QString  szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string_to_convert",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(KviQString::lowerISO88591(szString));
	return true;
}
/*
	@doc: str.upcase
	@type:
		function
	@title:
		$str.upcase
	@short:
		Returns the given string with all characters turned to upper case
	@syntax:
		<string> $str.upcase(<string_to_convert:string>)
	@description:
		Returns the given <string_to_convert> with all characters turned to lower case.
		Warning: this function uses ISO-8859-1 locale to make the case translation.
		If you want to use a locale aware translation mapping then please
		use $str.localeupcase.
*/

static bool str_kvs_fnc_upcase(KviKvsModuleFunctionCall * c)
{
	QString  szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string_to_convert",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(KviQString::upperISO88591(szString));
	return true;
}
/*
	@doc: str.localelowcase
	@type:
		function
	@title:
		$str.localelowcase
	@short:
		Returns the given string with all characters turned to lower case
	@syntax:
		<string> $str.localelowcase(<string_to_convert:string>)
	@description:
		Returns the given <string_to_convert> with all characters turned to lower case.
		Warning: this function is locale aware and it may produce unexpected
		results in locales that contain strange exceptions (like Turkish which maps
		i to Y with an accent). For IRC interaction you might prefer using $str.lowcase
*/

static bool str_kvs_fnc_localelowcase(KviKvsModuleFunctionCall * c)
{
	QString  szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string_to_convert",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.lower());
	return true;
}

/*
	@doc: str.localeupcase
	@type:
		function
	@title:
		$str.localeupcase
	@short:
		Returns the given string with all characters turned to upper case
	@syntax:
		<string> $str.localeupcase(<string_to_convert:string>)
	@description:
		Returns the given <string_to_convert> with all characters turned to lower case.
		Warning: this function is locale aware and it may produce unexpected
		results in locales that contain strange exceptions (like Turkish which maps
		i to Y with an accent). For IRC interaction you might prefer using $str.upcase
*/

static bool str_kvs_fnc_localeupcase(KviKvsModuleFunctionCall * c)
{
	QString  szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string_to_convert",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.upper());
	return true;
}

/*
	@doc: str.isnumber
	@type:
		function
	@title:
		$str.isnumber
	@short:
		Returns 1 if the given string represents a number
	@syntax:
		<bool> $str.isnumber(<givenstring:string>)
	@description:
		Returns 1 if the given string represents a number, 0 if not.
*/

static bool str_kvs_fnc_isnumber(KviKvsModuleFunctionCall * c)
{
	KviKvsVariant * v;
	KviKvsNumber num;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("givenstring",KVS_PT_VARIANT,0,v)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setBoolean(v->asNumber(num));
	return true;
}


/*
	@doc: str.isunsignednumber
	@type:
		function
	@title:
		$str.isunsignednumber
	@short:
		Returns 1 if the given string represents an unsigned number
	@syntax:
		<bool> $str.isunsignednumber(<givenstring:string>)
	@description:
		Returns 1 if the given string represents an unsigned number, 0 if not.
*/

static bool str_kvs_fnc_isunsignednumber(KviKvsModuleFunctionCall * c)
{
	KviKvsVariant * v;
	KviKvsNumber nNum;
	bool bRet;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("givenstring",KVS_PT_VARIANT,0,v)
	KVSM_PARAMETERS_END(c)
	if(!v->asNumber(nNum))  bRet = false;
	else
	{
	if(nNum.isInteger())
	{
		bRet = nNum.integer() >= 0;
	}
	else
	{
		bRet = nNum.real() >= 0.0;
	}
	}
	c->returnValue()->setBoolean(bRet);
	return true;
}

/*
	@doc: str.isempty
	@type:
		function
	@title:
		$str.isempty
	@short:
		Returns 1 if the given string don't have any character.
	@syntax:
		<string> $str.isEmpty(<givenstring:string>)
	@description:
		Returns 1 if the given string don't have any character (that is, is empty).
		This function is almost useless since it is equivalent to the
		comparison with an empty string...
*/

static bool str_kvs_fnc_isempty(KviKvsModuleFunctionCall * c)
{
	QString v;
	bool bRet;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("datastring",KVS_PT_STRING,0,v)
	KVSM_PARAMETERS_END(c)
	if(v.isEmpty())  bRet = true;
	else bRet = false;
	c->returnValue()->setBoolean(bRet);
	return true;
}

/*
	@doc: str.contains
	@type:
		function
	@title:
		$str.contains
	@short:
		Returns 1 if the first parameter contains the second
	@syntax:
		<bool> $str.contains(<container:string>,<tofind:string>)
	@description:
		Returns 1 if the first string parameter contains the second string parameter.
		This function is case sensitive.
	@seealso:
		[fnc]$str.containsnocase[/fnc]()
*/


static bool str_kvs_fnc_contains(KviKvsModuleFunctionCall * c)
{
	QString szString,szSubString;
	bool bIs;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("container",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tofind",KVS_PT_STRING,0,szSubString)
	KVSM_PARAMETERS_END(c)
	bIs = szString.find(szSubString) != -1;
	c->returnValue()->setBoolean(bIs);
	return true;
}

/*
	@doc: str.containsnocase
	@type:
		function
	@title:
		$str.containsnocase
	@short:
		Returns 1 if the first parameter contains the second, case insensitive
	@syntax:
		<bool> $str.containsnocase(<container:string>,<tofind:string>)
	@description:
		Returns 1 if the first string parameter contains the second string parameter
		whithout taking in consideration the case of the characters in the string.
	@seealso:
		[fnc]$str.contains[/fnc]
*/

static bool str_kvs_fnc_containsnocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szSubString;
	bool bIs;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("container",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tofind",KVS_PT_STRING,0,szSubString)
	KVSM_PARAMETERS_END(c)
	bIs = szString.find(szSubString,0,false) != -1;
	c->returnValue()->setBoolean(bIs);
	return true;
}

/*
	@doc: str.equal
	@type:
		function
	@title:
		$str.equal
	@short:
		Returns 1 if the two string parameters are equal
	@syntax:
		<bool> $str.equal(<fromcompare:string>,<tocompare:string>)
	@description:
		Returns 1 if the two string parameters are equal. This function is case sensitive.
	@seealso:
		[fnc]$str.equalnocase[/fnc]()
*/

static bool str_kvs_fnc_equal(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	bool bIs;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("fromcompare",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tocompare",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	bIs =  KviQString::equalCS(szString,szString2);
	c->returnValue()->setBoolean(bIs);
	return true;
}
/*
	@doc: str.equalnocase
	@type:
		function
	@title:
		$str.equalnocase
	@short:
		Returns 1 if the two string parameters are equal, case insensitive
	@syntax:
		<bool> $str.equalnocase(<fromcompare:string>,<tocompare:string>)
	@description:
		Returns 1 if the two strngs parameters are equal, without taking the case of the
		characters in consideration.
	@seealso:
		[fnc]$str.equal[/fnc]()
*/

static bool str_kvs_fnc_equalnocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	bool bIs;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("fromcompare",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tocompare",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	bIs =  KviQString::equalCI(szString,szString2);
	c->returnValue()->setBoolean(bIs);
	return true;
}

/*
	@doc: str.cmp
	@type:
		function
	@title:
		$str.cmp
	@short:
		Compare two strings alphabetically
	@syntax:
		<bool> $str.cmp(<fromcompare:string>,<tocompare:string>)
	@description:
		This function compares two strings alphabetically. If the first string is 'greater'
		than the second, it will return a positive number, a negative number is the second is
		greater and 0 if the two strings are equal.
	@seealso:
		[fnc]$str.cmpnocase[/fnc]()
*/

static bool str_kvs_fnc_cmp(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	int iCmp;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("fromcompare",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tocompare",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	iCmp =  KviQString::cmpCS(szString,szString2);
	c->returnValue()->setInteger(iCmp);
	return true;
}

/*
	@doc: str.cmpnocase
	@type:
		function
	@title:
		$str.cmpnocase
	@short:
		Compare two strings alphabetically, case insensitive.
	@syntax:
		<bool> $str.cmpnocase(<fromcompare:string>,<tocompare:string>)
	@description:
		This function compares two strings alphabetically. If the first string is 'greater'
		than the second, it will return a positive number, a negative number is the second is
		greater and 0 if the two strings are equal. This function is case insensitive.
	@seealso:
		[fnc]$str.cmp[/fnc]()
*/
static bool str_kvs_fnc_cmpnocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	int iCmp;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("fromcompare",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("tocompare",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	iCmp =  KviQString::cmpCI(szString,szString2);
	c->returnValue()->setInteger(iCmp);
	return true;
}

/*
	@doc: str.find
	@type:
		function
	@title:
		$str.find
	@short:
		Find the index of the nth ocurrence of a substring in a string
	@syntax:
		<int> $str.find(<findIn:string>,<tofind:string>[,ocurrence:int])
	@description:
		This function search in the string given as the first parameter for the string
		given as his second parameter, and will return the index where the nth ocurrence
		given as the third parameter is found or -1 if it's not located. It starts
		counting at 0. If occurence is not specified then the first occurence
		is searched. WARNING: The occurente number starts from 1! (Yes, that's a bug, but
		for backward compatibility it must remain as it is :( ).[br]
		FIXME: The semantics of this function are totally broken :(
*/

static bool str_kvs_fnc_find(KviKvsModuleFunctionCall * c)
{
	QString szFindIn, szToFind;
	kvs_int_t iOcurence;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("findIn",KVS_PT_STRING,0,szFindIn)
		KVSM_PARAMETER("tofind",KVS_PT_STRING,0,szToFind)
		KVSM_PARAMETER("ocurrence",KVS_PT_INTEGER,KVS_PF_OPTIONAL,iOcurence)
	KVSM_PARAMETERS_END(c)
	int pos = 1;
	if(iOcurence!=0)
		pos = iOcurence;
	if(pos<1)
	{
		c->returnValue()->setInteger(-1);
		return true;
	}
	if(szFindIn.isEmpty())
	{
		c->returnValue()->setInteger(-1);
		return true;
	}
	if(szToFind.isEmpty())
	{
		c->returnValue()->setInteger(-1);
		return true;
	}
	int cnt = 1;
	int idx;
	int totalIdx = 0;

	while (cnt<=pos)
	{
	 	idx = szFindIn.right(szFindIn.length() - totalIdx).find(szToFind);
		if(idx == -1)
		{
			c->returnValue()->setInteger(-1);
			return true;
		}
		//Idx only gives the index until the pos _before_ the matched string so if this is
		//not the match we want (cont != 0) we skip it
		totalIdx += (idx + (cnt == pos ? 0 : szToFind.length()));
		++cnt;
	}
	c->returnValue()->setInteger(totalIdx);
	return true;
}
/*
	@doc: str.findfirst
	@type:
		function
	@title:
		$str.findfirst
	@short:
		Find the index of a substring in a string
	@syntax:
		<int> $str.findfirst(<findIn:string>,<toFind:string>)
	@description:
		This function search in the string given as the first parameter for the string
		given as his second parameter, and will return the index where is first located or
		-1 if it's not located. It starts counting at 0.
*/

static bool str_kvs_fnc_findfirst(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("findIn",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toFind",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setInteger(szString.find(szString2));
	return true;
}
/*
	@doc: str.findfirstnocase
	@type:
		function
	@title:
		$str.findfirstnocase
	@short:
		Find the index of a substring in a string, case insensitive
	@syntax:
		<int> $str.findfirstnocase(<findIn:string>,<toFind:string>)
	@description:
		This function search in the string given as the first parameter for the string
		given as his second parameter, and will return the index where is first located or
		-1 if it's not located. This function is case insensitive.
*/

static bool str_kvs_fnc_findfirstnocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("findIn",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toFind",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setInteger(szString.find(szString2,0,false));
	return true;
}
/*
	@doc: str.findlast
	@type:
		function
	@title:
		$str.findlast
	@short:
		Find the last index of a substring in a string
	@syntax:
		<int> $str.findlast(<findIn:string>,<toFind:string>)
	@description:
		This function search in the string given as the first parameter for the string
		given as his second parameter, and will return the index where is last located or
		-1 if it's not located.
*/

static bool str_kvs_fnc_findlast(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("findIn",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toFind",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setInteger(szString.findRev(szString2));
	return true;
}
/*
	@doc: str.findlastnocase
	@type:
		function
	@title:
		$str.findlastnocase
	@short:
		Find the last index of a substring in a string, case insensitive
	@syntax:
		<int> $str.findlastnocase(<findIn:string>,<toFind:string>)
	@description:
		This function search in the string given as the first parameter for the string
		given as his second parameter, and will return the index where is last located or
		-1 if it's not located. This function is case insensitive.
*/

static bool str_kvs_fnc_findlastnocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("findIn",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toFind",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setInteger(szString.findRev(szString2,-1,false));
	return true;
}
/*
	@doc: str.left
	@type:
		function
	@title:
		$str.left
	@short:
		Returns a substring starting from the left until the given index.
	@syntax:
		<string> $str.left(<data:string>,<index:int>)
	@description:
		This function returns a substring of the first string parameter which is the
		string starting from the left until the index specified in the second parameter.
*/

static bool str_kvs_fnc_left(KviKvsModuleFunctionCall * c)
{
	QString szString;
	kvs_int_t iIdx;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("index",KVS_PT_INTEGER,0,iIdx)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.left(iIdx));
	return true;
}
/*
	@doc: str.right
	@type:
		function
	@title:
		$str.right
	@short:
		Returns a substring starting from the right until the given index.
	@syntax:
		<string> $str.right(<data:string>,<index:int>)
	@description:
		This function returns a substring of the first string parameter which is the
		string starting from the right until the index specified in the second parameter.
		The index start counting at the last character and increase until the first.
*/

static bool str_kvs_fnc_right(KviKvsModuleFunctionCall * c)
{
	QString szString;
	kvs_int_t iIdx;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("index",KVS_PT_INTEGER,0,iIdx)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.right(iIdx));
	return true;
}
/*
	@doc: str.mid
	@type:
		function
	@title:
		$str.mid
	@short:
		Returns a substring starting from a given index.
	@syntax:
		<string> $str.mid(<data:string>,<startidx:int>,<nchars:int>)
	@description:
		This function returns a substring of the first string parameter wich is the
		string starting at the (numeric) index given with startidx and counting nchars
		forward.
*/

static bool str_kvs_fnc_mid(KviKvsModuleFunctionCall * c)
{
	QString szString;
	kvs_int_t iIdx,iNchars;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("startidx",KVS_PT_INTEGER,0,iIdx)
		KVSM_PARAMETER("nchars",KVS_PT_INTEGER,0,iNchars)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.mid(iIdx,iNchars));
	return true;
}
/*
	@doc: str.append
	@type:
		function
	@title:
		$str.append
	@short:
		Append one string to another.
	@syntax:
		<string> $str.append(<string:string>,<toappend:string>)
	@description:
		This function returns a string created appending the second string parameter
		to the end of the first string parameter.
*/

static bool str_kvs_fnc_append(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toappend",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.append(szString2));
	return true;
}
/*
	@doc: str.prepend
	@type:
		function
	@title:
		$str.prepend
	@short:
		Prepend one string to another.
	@syntax:
		<string> $str.prepend(<string:string>,<toprepend:string>)
	@description:
		This function returns a string created prepending the second string parameter
		to the start of the first string parameter.
*/

static bool str_kvs_fnc_prepend(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("toprepend",KVS_PT_STRING,0,szString2)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.prepend(szString2));
	return true;
}
/*
	@doc: str.insert
	@type:
		function
	@title:
		$str.insert
	@short:
		Inserts a substring in a string at a given index
	@syntax:
		<string> $str.insert(<string:string>,<substring:string>,<index:int>)
	@description:
		Inserts the substring given in the second parameter in the string given in the
		first parameter at the index given in the third parameter, then returns the
		resulting string.
*/

static bool str_kvs_fnc_insert(KviKvsModuleFunctionCall * c)
{
	QString szString,szString2;
	kvs_int_t iIdx;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("substring",KVS_PT_STRING,0,szString2)
		KVSM_PARAMETER("nchars",KVS_PT_INTEGER,0,iIdx)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.insert(iIdx,szString2));
	return true;
}
/*
	@doc: str.strip
	@type:
		function
	@title:
		$str.strip
	@short:
		Returns a whitespace stripped string
	@syntax:
		<string> $str.strip(<string:string>)
	@description:
		Returns a left and right whitespace stripped version of the string given as the
		first parameter.
*/

static bool str_kvs_fnc_strip(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(szString.stripWhiteSpace());
	return true;
}
/*
	@doc: str.stripleft
	@type:
		function
	@title:
		$str.stripleft
	@short:
		Returns a left whitespace stripped string
	@syntax:
		<string> $str.stripleft(<string:string>)
	@description:
		Returns a left whitespace stripped version of the string given as the
		first parameter.
*/

static bool str_kvs_fnc_stripleft(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	if(szString.length() > 0)
	{
		int idx = 0;
		while(szString[idx].isSpace())idx++;
		if(idx > 0)szString.remove(0,idx);
	}
	c->returnValue()->setString(szString);
	return true;
}
/*
	@doc: str.stripright
	@type:
		function
	@title:
		$str.stripright
	@short:
		Returns a right whitespace stripped string
	@syntax:
		<string> $str.stripright(<string:string>)
	@description:
		Returns a right whitespace stripped version of the string given as the
		first parameter.
*/

static bool str_kvs_fnc_stripright(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	int iIdx = 0;
	while(szString.at(szString.length() - (iIdx+1)).isSpace()) iIdx++;
	if(iIdx > 0)szString.remove(szString.length() - iIdx,iIdx);
	c->returnValue()->setString(szString);
	return true;
}

/*
	@doc: str.stripcolors
	@type:
		function
	@title:
		$str.stripcolors
	@short:
		Returns a mirc color codes stripped string
	@syntax:
		<string> $str.stripcolors(<string:string>)
	@description:
		Removes all mirc color codes from a string, including also bold, underline, reverse,
		icon, crypting and ctcp control codes.
*/

static bool str_kvs_fnc_stripcolors(KviKvsModuleFunctionCall * c)
{
	QString szString;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	c->returnValue()->setString(KviMircCntrl::stripControlBytes(szString));
	return true;
}

/*
	@doc: str.replace
	@type:
		function
	@title:
		$str.replace
	@short:
		Replace substrings in a string
	@syntax:
		<string> $str.replace(<string:string>,<replacewith:string>,<toreplace:string>)
	@description:
		This function returns a string created replacing all ocurrences of the third parameter
		('toreplace') in the string given as the first parameter ('string') with the string
		given as the second parameter ('replacewith').
		The string replacement is case sensitive!.
		FIXME: The order of the parameters in this function is illogical (and probably incompatible
		with any other scripting language) :D
	@examples:
		[example]
			echo $str.replace("I like big networks","neural","big")
		[/example]
*/

static bool str_kvs_fnc_replace(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr,szToreplace;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("newstr",KVS_PT_STRING,0,szNewstr)
		KVSM_PARAMETER("toreplace",KVS_PT_STRING,0,szToreplace)
	KVSM_PARAMETERS_END(c)
	szString.replace(szToreplace,szNewstr);
	c->returnValue()->setString(szString);
	return true;
}

/*
	@doc: str.replacenocase
	@type:
		function
	@title:
		$str.replacenocase
	@short:
		Replace substrings in a string ignoring case
	@syntax:
		<string> $str.replacenocase(<string:string>,<newstr:string>,<toreplace:string>)
	@description:
		This function returns a string created replacing all ocurrences of the third parameter
		('toreplace') in the string given as the first parameter ('string') with the string
		given as the second parameter ('newstr').[br]
		The replacement is case insensitive.[br]
		FIXME: The order of the parameters in this function is illogical (and probably incompatible
		with any other scripting language) :D
*/

static bool str_kvs_fnc_replacenocase(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr,szToreplace;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("newstr",KVS_PT_STRING,0,szNewstr)
		KVSM_PARAMETER("toreplace",KVS_PT_STRING,0,szToreplace)
	KVSM_PARAMETERS_END(c)
	szString.replace(szToreplace,szNewstr,false);
	c->returnValue()->setString(szString);
	return true;
}

/*
	@doc: str.urlencode
	@type:
		function
	@title:
		$str.urlencode
	@short:
		Returns a browser formatted string
	@syntax:
		<string> $str.urlencode(<string:string>)
	@description:
		This function returns a string created replacing all ocurrences in the parameter ('string') with their respective html entities.[br]
		The replacement is case insensitive.[br]
		FIXME: This function use the same replace order as $str.replace so it incompatible
		with any other scripting language) :D
*/

static bool str_kvs_fnc_urlencode(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	
	char * toReplace[]={" ", "#", "$",  "&", "/", ":", "<", "=", ">", "?", "@", "[", "\\", "]", "^", "`", "{", "|", "}", "~"};
	char * newStr[]={"%20", "%23", "%24", "&amp;", "%2F", "%3A", "&lt;", "%3D", "&gt;", "%3F", "%40", "%5B", "%5C", "%5D", "%5E", "%60", "%7B", "%7C", "%7D", "%7E"};

	/*
	for(int idx=0,idx<22,idx++)
		szNewstr=szString.replace(toReplace[idx],newStr[idx],false);
	*/

	int idx=0;
	while(idx<20){
		szNewstr=szString.replace(toReplace[idx],newStr[idx],false);
		idx++;
	}

	c->returnValue()->setString(szNewstr);
	return true;
}

/*
	@doc: str.lefttofirst
	@type:
		function
	@title:
		$str.lefttofirst
	@short:
		Returns the left part of a string until a given substring
	@syntax:
		<string> $str.lefttofirst(<string:string>,<substring:string>)
	@description:
		This function returns the left part of the string given as the first parameter
		from the start until the string given as the second parameter is found. It don't
		include the substring of the second parameter in the returned value. If the second
		parameter is not found, the entire string is returned.
		The match is case insensitive.
*/

static bool str_kvs_fnc_lefttofirst(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr;
	int where;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("substring",KVS_PT_STRING,0,szNewstr)
	KVSM_PARAMETERS_END(c)
	where = szString.find(szNewstr,false);
	if(where != -1) c->returnValue()->setString(szString.left(where));
	else c->returnValue()->setString(szString);
	return true;
}
/*
	@doc: str.lefttolast
	@type:
		function
	@title:
		$str.lefttolast
	@short:
		Returns the left part of a string until the last ocurrence of a given substring
	@syntax:
		<string> $str.lefttolast(<string:string>,<substring:string>)
	@description:
		This function returns the left part of the string given as the first parameter
		from the start until the last ocurrence of the string given as the second parameter
		is found. It don't include the substring of the second parameter in the returned value.
		If the second parameter is not found, the entire string is returned.
		The match is case insensitive
*/

static bool str_kvs_fnc_lefttolast(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("substring",KVS_PT_STRING,0,szNewstr)
	KVSM_PARAMETERS_END(c)
	int where = szString.findRev(szNewstr,-1,false);
	if(where != -1) c->returnValue()->setString(szString.left(where));
	else c->returnValue()->setString(szString);
	return true;
}
/*
	@doc: str.rightfromfirst
	@type:
		function
	@title:
		$str.rightfromfirst
	@short:
		Returns the right part of a string from the first ocurrence of a given substring
	@syntax:
		<string> $str.rightfromfirst(<string:string>,<substring:string>)
	@description:
		This function returns the right part of the string given as the first parameter
		from the position where the first ocurrence of the string given as the second parameter
		is found. It don't include the substring of the second parameter in the returned value.
		If the second parameter is not found, an empty string is returned..
		The match is case insensitive
*/

static bool str_kvs_fnc_rightfromfirst(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("substring",KVS_PT_STRING,0,szNewstr)
	KVSM_PARAMETERS_END(c)
	int idx = szString.find(szNewstr,false);
	if(idx != -1) c->returnValue()->setString(szString.right(szString.length()-(idx+szNewstr.length())));
	else c->returnValue()->setString("");
	return true;
}
/*
	@doc: str.rightfromlast
	@type:
		function
	@title:
		$str.rightfromlast
	@short:
		Returns the right part of a string from the last ocurrence of a given substring
	@syntax:
		<string> $str.rightfromlast(<string:string>,<substring:string>)
	@description:
		This function returns the right part of the string given as the first parameter
		from the position where the last ocurrence of the string given as the second parameter
		is found. It don't include the substring of the second parameter in the returned value.
		If the second parameter is not found, an empty string is returned..
		The match is case insensitive.
*/


static bool str_kvs_fnc_rightfromlast(KviKvsModuleFunctionCall * c)
{
	QString szString,szNewstr;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("substring",KVS_PT_STRING,0,szNewstr)
	KVSM_PARAMETERS_END(c)
	int idx = szString.findRev(szNewstr,-1,false);
	if(idx != -1) c->returnValue()->setString(szString.right(szString.length()-(idx+szNewstr.length())));
	else c->returnValue()->setString("");
	return true;
}

/*
	@doc: str.match
	@type:
		function
	@title:
		$str.match
	@short:
		Matches a fixed string against a wildcard expression
	@syntax:
		<bool> $str.match(<expression:string>,<string:string>[,<flags:string>])
	@description:
		Returns 1 if the fixed <string> matches the <expression>, 0 otherwise.[br]
		If <flags> contains the flag 'r' then <expression> is treated as a full
		regular expression otherwise it is treated as a simple wildcard expression containing
		the classic wildcards '*' and '?'.[br]
		If <flags> contains the flag 'e' then only an exact match is considered (e.g. the full
		<string> is exactly matched by <expression>), otherwise partial matches are allowed too (e.g.
		<expression> is found inside <string>).[br]
		The match is case sensitive.[br]
	@examples:
		[example]
			%test = "Hello! My nickname is Pragma"
			[cmd]if[/cmd]($str.match(Pragma*,%test))[cmd]echo[/cmd] "Matches Pragma*"
			[cmd]if[/cmd]($str.match(*Pragma,%test))[cmd]echo[/cmd] "Matches *Pragma"
			[cmd]if[/cmd]($str.match(H*y*i?k*a,%test))[cmd]echo[/cmd] "Matches H*y*i?k*a"
			[cmd]if[/cmd]($str.match(H*y*i?K*a,%test))[cmd]echo[/cmd] "Matches H*y*i?K*a"
		[/example]
	@seealso:
		[fnc]$str.matchnocase[/fnc]
*/

static bool str_kvs_fnc_match(KviKvsModuleFunctionCall * c)
{
	QString szWildcard,szString,szFlags;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("wildcard",KVS_PT_NONEMPTYSTRING,0,szWildcard)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("flags",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags)
	KVSM_PARAMETERS_END(c)
	bool bRegExp = (szFlags.find(QChar('r')) != -1) || (szFlags.find(QChar('R')) != -1);
	bool bExact = (szFlags.find(QChar('e')) != -1) || (szFlags.find(QChar('E')) != -1);
	c->returnValue()->setBoolean(KviQString::matchStringCS(szWildcard,szString,bRegExp,bExact));
	return true;
}

/*
	@doc: str.matchnocase
	@type:
		function
	@title:
		$str.matchnocase
	@short:
		Matches a fixed string against a wildcard expression (case insensitive)
	@syntax:
		<bool> $str.matchnocase(<expression>,<string:string>[,<flags:string>])
	@description:
		Returns 1 if the fixed <string> matches the <expression>, 0 otherwise.[br]
		If <flags> contains the flag 'r' then <expression> is treated as a full
		regular expression otherwise it is treated as a simple wildcard expression containing
		the classic wildcards '*' and '?'.[br]
		If <flags> contains the flag 'e' then only an exact match is considered (e.g. the full
		<string> is exactly matched by <expression>), otherwise partial matches are allowed too (e.g.
		<expression> is found inside <string>).[br]
		The match is case insensitive.[br]
	@examples:
		[example]
			%test = "Hello! My nickname is Pragma"
			[cmd]if[/cmd]($str.match(pragma*,%test))[cmd]echo[/cmd] "Matches pragma*"
			[cmd]if[/cmd]($str.match(*pragma,%test))[cmd]echo[/cmd] "Matches *pragma"
			[cmd]if[/cmd]($str.match(H*y*i?k*a,%test))[cmd]echo[/cmd] "Matches H*y*i?k*a"
			[cmd]if[/cmd]($str.match(H*y*i?K*a,%test))[cmd]echo[/cmd] "Matches H*y*i?K*a"
			[cmd]if[/cmd]($str.match(G*if?sx,%test))[cmd]echo[/cmd] "Matches G*if?sx"
		[/example]
	@seealso:
		[fnc]$str.match[/fnc]
*/

static bool str_kvs_fnc_matchnocase(KviKvsModuleFunctionCall * c)
{
	QString szWildcard,szString,szFlags;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("wildcard",KVS_PT_NONEMPTYSTRING,0,szWildcard)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("flags",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags)
	KVSM_PARAMETERS_END(c)
	bool bRegExp = (szFlags.find(QChar('r')) != -1) || (szFlags.find(QChar('R')) != -1);
	bool bExact = (szFlags.find(QChar('e')) != -1) || (szFlags.find(QChar('E')) != -1);
	c->returnValue()->setBoolean(KviQString::matchStringCI(szWildcard,szString,bRegExp,bExact));
	return true;
}

/*

	@doc: str.word
	@type:
		function
	@title:
		$str.word
	@short:
		Returns the nth word in a string
	@syntax:
		<string> $str.word(<n:int>,<string:string>)
	@description:
		Returns the nth word inside the <string> (with n starting from 0!)[br]
		A word is a substring not containing spaces (ASCII chars 32, carriage returns , tabs etc...).[br]
		If the string contains less than n+1 words then an empty string is returned.[br]
		This function is faster than a call to [fnc]split[/fnc]() and array indexing
		if you need a single word to be extracted from a complex string.[br]
		If you need to manage more than one word from the string then the [fnc]split[/fnc]()
		method is more efficient.[br]
		This function is a special case of [fnc]$str.token[/fnc]() and it runs a bit faster.
	@examples:
		[example]
			%test = "this is  a  string   full of  words"
			[cmd]echo[/cmd] $str.word(4,%test)
		[/example]
	@seealso:
		[fnc]$str.token[/fnc]
*/

static bool str_kvs_fnc_word(KviKvsModuleFunctionCall * c)
{
	QString szString;
	kvs_int_t iOccurence;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("occurence",KVS_PT_INT,0,iOccurence)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	int idx = 0;
	int cnt = 0;
	int begin;
	int len = szString.length();
	while (idx<len)
	{
		QChar szTmp = szString[idx].unicode();
		while (szTmp.isSpace())
		{
			idx++;
			 szTmp = szString[idx].unicode();
		}
		begin = idx;
		while (idx<len && !szTmp.isSpace())
		{
			idx++;
			szTmp = szString[idx].unicode();
		}
		if (iOccurence == (kvs_int_t)cnt)
		{
			c->returnValue()->setString(szString.mid(begin,idx-begin));
			return true;
		}
		cnt ++;
	}
	return true;

}
/*
	@doc: str.token
	@type:
		function
	@title:
		$str.token
	@short:
		Returns the nth token in a string
	@syntax:
		<string> $str.token(<n:int>,<separator:string>,<string:string>)
	@description:
		Returns the nth token inside the <string> (with n starting from 0!)[br]
		A token is a substring not containing the characters listed in <separators>.[br]
		If the string contains less than n+1 tokens then an empty string is returned.[br]
		[fnc]$str.word[/fnc]() is an optimized function dedicated to the special case
		in that <separators> is a string containing all the whitespace characters.[br]
		Please note that this function is SLOW. You might want to take a look at [fnc]$str.word[/fnc]()
		or even better to [fnc]$str.split[/fnc]().[br]
		This function is case sensitive: you need to specify both cases in the <separators> string
		if you want to do a case insensitive tokenization.[br]
	@examples:
		[example]
			%test = "21 hours 10 minutes 15 seconds"
			[cmd]echo[/cmd] $str.token(1," hoursmintecd",%test)
		[/example]
	@seealso:
		[fnc]$str.word[/fnc][br]
		[fnc]$str.split[/fnc][br]
*/

static bool str_kvs_fnc_token(KviKvsModuleFunctionCall * c)
{
	QString szString,sep;
	kvs_uint_t n;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("n",KVS_PT_UINT,0,n)
		KVSM_PARAMETER("separator",KVS_PT_STRING,0,sep)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szString)
	KVSM_PARAMETERS_END(c)
	if(sep.isEmpty())
	{
		c->returnValue()->setString(szString);
		return true;
	}

	int idx = 0;
	int cnt = 0;
	int begin;
	int len = szString.length();
	while (idx<len)
	{
		QChar szTmp = szString[idx].unicode();
//		while (szTmp==sep)
		while (sep.contains(szTmp))
		{
			idx++;
			 szTmp = szString[idx].unicode();
		}
		begin = idx;
		while (idx<len && !sep.contains(szTmp))
		{
			idx++;
			szTmp = szString[idx].unicode();
		}
		if (n == cnt)
		{
			c->returnValue()->setString(szString.mid(begin,idx-begin));
			return true;
		}
		cnt ++;
	}
	return true;
}

/*
	@doc: str.charsum
	@type:
		function
	@title:
		$str.charsum
	@short:
		Returns the sum of the character codes of the string
	@syntax:
		<int> $str.charsum(<data:string>[,<bCaseInsensitive:boolean>])
	@description:
		Returns the sum of the character codes of the parameter <string>.
		The sum is suitable for implementing a simple hashing algorithm.[br]
		If <bCaseInsensitive> is specified and $true then the string
		will be converted to lowercase first.
*/


static bool str_kvs_fnc_charsum(KviKvsModuleFunctionCall * c)
{
	QString szString;
	bool bCaseInsensitive;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_STRING,0,szString)
		KVSM_PARAMETER("bCaseInsensitive",KVS_PT_BOOL,KVS_PF_OPTIONAL,bCaseInsensitive)
	KVSM_PARAMETERS_END(c)
	unsigned int sum = 0;
	int idx = 0;
	int len = szString.length();
	if(bCaseInsensitive)
	{
		while(idx < len)
		{
			sum += szString[idx].lower().unicode();
			idx++;
		}
	} else {
		while(idx < len)
		{
			sum += szString[idx].unicode();
			idx++;
		}
	}
	c->returnValue()->setInteger(sum);
	return true;
}


/*
	@doc: str.digest
	@type:
		function
	@title:
		$str.digest
	@short:
		Returns the sum of the character codes of the string
	@syntax:
		<string> $str.digest(<data:string>[,<algorythm:string>])
	@description:
		Calculates digest for given string using algorithm passed as 2nd argument.
		Currently supported: md5, md4, md2, sha1, mdc2, ripemd160, dss1
		Default is md5. Requires OpenSSL support
*/


static bool str_kvs_fnc_digest(KviKvsModuleFunctionCall * c)
{
#ifdef COMPILE_SSL_SUPPORT
	QString szString,szType,szResult;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("data",KVS_PT_NONEMPTYSTRING,0,szString)
		KVSM_PARAMETER("algorythm",KVS_PT_NONEMPTYSTRING,KVS_PF_OPTIONAL,szType)
	KVSM_PARAMETERS_END(c)

	if(szType.isEmpty()) szType="md5";

	EVP_MD_CTX mdctx;
	const EVP_MD *md;
	unsigned char md_value[EVP_MAX_MD_SIZE];
	unsigned int md_len, i;
	char buff[3];
	OpenSSL_add_all_digests();

	md = EVP_get_digestbyname(szType.utf8().data());
	if(!md) {
		c->warning(__tr2qs("%Q algorytm is not supported"),&szType);
		return true;
	}

	EVP_MD_CTX_init(&mdctx);
	EVP_DigestInit_ex(&mdctx, md, NULL);
	EVP_DigestUpdate(&mdctx, szString.utf8().data(), szString.utf8().length());
	EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
	EVP_MD_CTX_cleanup(&mdctx);

	for(i = 0; i < md_len; i++)
	{
#ifdef COMPILE_ON_WINDOWS
		_snprintf(buff,3,"%02x",md_value[i]);
#else
		snprintf(buff,3,"%02x",md_value[i]);
#endif
		szResult.append(buff);
	}

	c->returnValue()->setString(szResult);
#else
	c->warning(__tr2qs("KVIrc is compiled without OpenSSL support. $str.digest function disabled"));
#endif

	return true;
}

/*
	@doc: str.join
	@type:
		function
	@title:
		$str.join
	@short:
		Returns a string joined from several strings
	@syntax:
		<string> $str.join(<separator:string>,<data:array>[,<flags:string>])
	@description:
		Joins all the string in the <data> array by using
		the specified <separator> and returns the result.
		If <flags> contains the character "n" then empty strings in teh <data>
		array are skipped.

*/


static bool str_kvs_fnc_join(KviKvsModuleFunctionCall * c)
{
	QString szSep;
	KviKvsArrayCast ac;
	QString szFlags;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("separator",KVS_PT_STRING,0,szSep)
		KVSM_PARAMETER("data",KVS_PT_ARRAYCAST,0,ac)
		KVSM_PARAMETER("flags",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags)
	KVSM_PARAMETERS_END(c)

	QString szRet;
	bool bSkipEmpty = szFlags.find('n',0,false) != -1;

	bool bFirst = true;

	if(KviKvsArray * a = ac.array())
	{
		kvs_uint_t uIdx = 0;
		kvs_uint_t uSize = a->size();
		while(uIdx < uSize)
		{
			KviKvsVariant * v = a->at(uIdx);
			if(v)
			{
				QString tmp;
				v->asString(tmp);
				if(bFirst)
				{
					szRet = tmp;
					bFirst = false;
				} else {
					szRet += szSep;
					szRet += tmp;
				}
			} else {
				if(!bSkipEmpty)
				{
					if(bFirst)
					{
						bFirst = false;
					} else {
						szRet += szSep;
					}
				}
			}
			uIdx++;
		}
	}

	c->returnValue()->setString(szRet);
	return true;
}


/*
	@doc: str.grep
	@type:
		function
	@title:
		$str.grep
	@short:
		Emulates the GNU Regular Expression Parser
	@syntax:
		<array> $str.grep(<match:string>,<strings:array>[,<flags:string>])
	@description:
		Returns an array with the elements of <strings> which match the string <match>.
		<flags> can be any combination of the characters 's','w' and 'r'.[br]
		If the  flag 'w' is specified then <match> is assumed to be a wildcard regular
		expression (with * and ? wildcards). If the flag 'r' is specified
		then <match> is assumed to be a standard regular expression. If none of
		'w' and 'r' is specified then <match> is treated as a simple string to be
		searched in each element of the <strings> array. 'r' takes precedence over 'w'.
		If the flag 's' is specified the matches are case sensitive.[br]
		Note that since almost any other variable type can be automatically cast
		to an array, then you can use this function also on scalars or hashes.
	@examples:
		[example]
			[comment]# Find all the nicknames starting with the letter A or a[/comment]
			[cmd]echo[/cmd] $str.grep("^a",[fnc]$chan.users[/fnc],"r")
			[comment]# Find the current CPU speed (on UNIX like machines only)[/comment]
			[cmd]echo[/cmd] $str.grep("MHz",[fnc]$str.split[/fnc]([fnc]$lf[/fnc],[fnc]$file.read[/fnc]("/proc/cpuinfo")))
			[comment]# simply check if the specified string matches a regular expression[/comment]
			[comment]# (this in fact is a little tricky, but you will probably not notice it :D)[/comment]
			[cmd]if[/cmd]($str.grep("[st]+","test string","r"))[cmd]echo[/cmd] "Yeah, it matches!"
		[/example]
	@seealso:
		[fnc]$array[/fnc]
*/

static bool str_kvs_fnc_grep(KviKvsModuleFunctionCall * c)
{
	KviKvsArrayCast ac;
	QString szMatch,szFlags;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("match",KVS_PT_STRING,0,szMatch)
		KVSM_PARAMETER("strings",KVS_PT_ARRAYCAST,0,ac)
		KVSM_PARAMETER("flags",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags)
	KVSM_PARAMETERS_END(c)

	KviKvsArray * n = new KviKvsArray();
	c->returnValue()->setArray(n);

	KviKvsArray * a = ac.array();

	bool bCaseSensitive = szFlags.find('s',0,false) != -1;
	bool bRegexp = szFlags.find('r',0,false) != -1;
	bool bWild = szFlags.find('w',0,false) != -1;
	int idx = 0;
	int cnt = a->size();

	int i = 0;
	if(bRegexp || bWild)
	{
		QRegExp re(szMatch,bCaseSensitive,bWild);
		while(idx < cnt)
		{
			KviKvsVariant * v = a->at(idx);
			if(v)
			{
				QString sz;
				v->asString(sz);
				if(re.search(sz) != -1)
				{
					n->set(i,new KviKvsVariant(sz));
					i++;
				}
			}
			idx++;
		}
	} else {
		while(idx < cnt)
		{
			KviKvsVariant * v = a->at(idx);
			if(v)
			{
				QString sz;
				v->asString(sz);
				if(sz.find(szMatch,0,bCaseSensitive) != -1)
				{
					n->set(i,new KviKvsVariant(sz));
					i++;
				}
			}
			idx++;
		}
	}

	return true;
}


/*
	@doc: str.split
	@type:
		function
	@title:
		$str.split
	@short:
		Splits a string to an array
	@syntax:
		<array> $str.split(<separator:string>,<data:string>[,<flags:string>[,<maxfields:integer>]])
	@description:
		Splits the <data> string by <separator> and returns an array of substrings.[br]
		<flags> may be a combination of the characters 's', 'w', 'r' and 'n'.[br]
		If s is specified, <separator> matching is case sensitive, otherwise is case insensitive.[br]
		If w is specified, <separator> is treated as a wildcard-type regular expression
		(with * and ? wildcars).[br]
		If r is specified, <separator> is treated as a extended-type regular expression
		(with character classes, special escapes etc..).[br]
		If both w and r are specified w takes precedence.[br]
		If none of w and r are specified <separator> is treated as a simple string to be matched.[br]
		If 'n' is specified then eventual empty fields are discarded.[br]
		If <maxfield> is specified then at most <maxfields> items are returned in the array (i.e. the last
		item may be not splitted completly).
	@examples:
		[example]
			[comment]# Split the fields[/comment]
			%test[] = $str.split(!,"Field0!Field1!Field2!Field3!!Field5")
			echo %test[]
			%i = 0
			[cmd]while[/cmd](%i < %test[]#)
			{
				[cmd]echo[/cmd] "Field %i: %test[%i]"
				%i++;
			}
		[/example]
		Regexp splitting:
		[example]
			%Test[] = $str.split("[ ]*[0-9][0-9]*-","AllOfThem: 1-Balboy 2-Pragma 3-Iakkolo 4-Crocodile",r)
			echo %Test[]
			%Test[] = $str.split("Y*H","hihiYeaHhohohoyeahYepYEAHhi",sw)
			echo %Test[]
		[/example]
		If used in "non-array" context it returns just a comma separated list of substrings:[br]
		[example]
			[cmd]echo[/cmd] $str.split("[ ]*","Condense spaces and change &nbsp; &nbsp; all &nbsp; &nbsp; &nbsp; it in commas",r)
		[/example]
*/

static bool str_kvs_fnc_split(KviKvsModuleFunctionCall * c)
{
	QString szSep,szStr,szFla;
	kvs_int_t iMaxItems;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("separator",KVS_PT_STRING,0,szSep)
		KVSM_PARAMETER("string",KVS_PT_STRING,0,szStr)
		KVSM_PARAMETER("flags",KVS_PT_STRING,KVS_PF_OPTIONAL,szFla)
		KVSM_PARAMETER("maxitems",KVS_PT_INTEGER,KVS_PF_OPTIONAL,iMaxItems)
	KVSM_PARAMETERS_END(c)

	if(c->params()->count() < 4)iMaxItems = -1;

	KviKvsArray * a = new KviKvsArray();
	c->returnValue()->setArray(a);

	if(szSep.isEmpty())
	{
		a->set(0,new KviKvsVariant(szStr));
		return true;
	}

	if(iMaxItems == 0)return true;

	bool bWild = szFla.find('w',0,false) != -1;
	bool bContainsR = szFla.find('r',0,false) != -1;
	bool bCaseSensitive = szFla.find('s',0,false) != -1;
	bool bNoEmpty = szFla.find('n',0,false) != -1;

	int id = 0;

	int iMatch = 0;
	int iStrLen = szStr.length();
	int iBegin = 0;

	if(bContainsR || bWild)
	{
		QRegExp re(szSep,bCaseSensitive,bWild);

		while((iMatch != -1) && (iMatch < iStrLen) && ((id < (iMaxItems-1)) || (iMaxItems < 0)))
		{
			iMatch = re.search(szStr,iBegin);
			if(iMatch != -1)
			{
				int len = re.matchedLength();
				if((len == 0) && (iBegin == iMatch))iMatch++; // safety measure for empty string matching

				QString tmp = szStr.mid(iBegin,iMatch - iBegin);
				if(bNoEmpty)
				{
					if(!tmp.isEmpty())
					{
						a->set(id,new KviKvsVariant(tmp));
						id++;
					}
				} else {
					a->set(id,new KviKvsVariant(tmp));
					id++;
				}

				iMatch += len;
				iBegin = iMatch;
			}
		}
	} else {
		while((iMatch != -1) && (iMatch < iStrLen) && ((id < (iMaxItems-1)) || (iMaxItems < 0)))
		{
			iMatch = szStr.find(szSep,iBegin,bCaseSensitive);
			if(iMatch != -1)
			{
				QString tmp = szStr.mid(iBegin,iMatch - iBegin);
				if(bNoEmpty)
				{
					if(!tmp.isEmpty())
					{
						a->set(id,new KviKvsVariant(tmp));
						id++;
					}
				} else {
					a->set(id,new KviKvsVariant(tmp));
					id++;
				}

				iMatch += szSep.length();
				iBegin = iMatch;
			}
		}
	}

	if(iBegin < iStrLen)
	{
		QString tmpx = szStr.right(iStrLen-iBegin);
		if(bNoEmpty)
		{
			if(!tmpx.isEmpty())
				a->set(id,new KviKvsVariant(tmpx));
		} else {
			a->set(id,new KviKvsVariant(tmpx));
		}
	} else {
		if(!bNoEmpty)
			a->set(id,new KviKvsVariant(QString::null)); // empty string at the end
	}

	return true;
}

/*
	@doc: str.printf
	@type:
		function
	@title:
		$str.printf
	@short:
		Returns a formatted string in a C sprintf-like fashion.
	@syntax:
		<string> $str.printf(<format:string>[,<parameter:variant>[...]]);
	@description:
		This function acts like the C sprintf(): it returns
		a string formatted by following the specification in <format>
		and by using the following corresponding variadic parameters.
		Since the percent sign is used as variable prefix in KVIrc,
		this function uses the question mark '?' instead.[br]
		The <format> string can contain the following escape sequences:[br]
		[br]
		[table]
		[tr][td][b]?s[/b][/td][td]The next parameter is evaluated as a string and substituted in place of ?s[/td][/tr]
		[tr][td][b]?d[/b][/td][td]The next parameter is evaluated as a signed integer and substituted in place of ?d[/td][/tr]
		[tr][td][b]?i[/b][/td][td]Same as ?d[/td][/tr]
		[tr][td][b]?u[/b][/td][td]The next parameter is evaluated as an unsigned signed integer and substituted in place of ?d[/td][/tr]
		[tr][td][b]?x[/b][/td][td]The next parameter is evaluated as an unsigned integer and its hexadecimal rappresentation
			is substituted in place of ?x[/td][/tr]
		[tr][td][b]?h[/b][/td][td]Same as ?x[/td][/tr]
		[tr][td][b]?X[/b][/td][td]Same as ?x but uppercase hexadecimal digits are used[/td][/tr]
		[tr][td][b]?H[/b][/td][td]Same as ?X[/td][/tr]
		[tr][td][b]??[/b][/td][td]A literal question mark[/td][/tr]
		[tr][td][b]?[.N]f[/b][/td][td]The next parameter is evaluated as a real floating point value
			and its rappresentation substituted in place of ?f. The optional [.N] modifier,
			where N is an unsigned integer, rappresents the desired precision.[/td][/tr]
		[tr][td][b]?[.N]e[/b][/td][td]The next parameter is evaluated as a real floating point value
			and its scientific rappresentation substituted in place of ?e. The optional [.N] modifier,
			where N is an unsigned integer, rappresents the desired precision.[/td][/tr]
		[tr][td][b]?[.N]E[/b][/td][td]Same as ?e but an uppercase E is used as the exponent prefix[/td][/tr]
		[/table]
	@examples:
		[example]
			%val = $(1.0 / 3.0);
			$str.printf("1/3 with a precision of 3 digits is ?.3f, while in scientific notation it's ?e",%val,%val)
		[/example]
*/


static bool str_kvs_fnc_printf(KviKvsModuleFunctionCall * c)
{
	QString szFormat;
	KviKvsVariantList vArgs;
	KVSM_PARAMETERS_BEGIN(c)
		KVSM_PARAMETER("format",KVS_PT_STRING,0,szFormat)
		KVSM_PARAMETER("vArgs",KVS_PT_VARIANTLIST,0,vArgs)
	KVSM_PARAMETERS_END(c)

	QString s;

#define MEMINCREMENT 32

	int reallen = 0;
	int allocsize = MEMINCREMENT;

	//s.setLength(allocsize);

	const QChar * fmt = KviQString::nullTerminatedArray(szFormat);

	if(fmt)
	{
		QChar * buffer = (QChar *)kvi_malloc(sizeof(QChar) * allocsize);
		//QChar * p = (QChar *)s.unicode();
	
		//9999999999999999999999999999999\0
		char numberBuffer[1024];
		char *pNumBuf;
		kvs_uint_t tmp;
		kvs_int_t argValue;
		kvs_uint_t argUValue;
		kvs_real_t argRValue;
	
		QChar * p = buffer;
	
	#define INCREMENT_MEM \
		{ \
			allocsize += MEMINCREMENT; \
			buffer = (QChar *)kvi_realloc(buffer,sizeof(QChar) * allocsize); \
			p = buffer + reallen; \
		}
	
	#define INCREMENT_MEM_BY(numchars) \
		{ \
			allocsize += numchars + MEMINCREMENT; \
			buffer = (QChar *)kvi_realloc(buffer,sizeof(QChar) * allocsize); \
			p = buffer + reallen; \
		}
	
		KviKvsVariant * pVar;
	
		pVar = vArgs.first();

		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':
				{
					QString sz;
					if(pVar)pVar->asString(sz);
					if(sz.isEmpty())continue;
					int len = sz.length();
					if((allocsize - reallen) < len)INCREMENT_MEM_BY(len)
					const QChar * ch = sz.unicode();
					while(len--)*p++ = *ch++;
					reallen += sz.length();
					pVar = vArgs.next();
					continue;
				}
				case 'd': //signed integer
				{
					if(pVar)
					{
						if(!pVar->asInteger(argValue))
						{
							c->warning(__tr2qs("Invalid argument for ?d escape sequence, 0 assumed"));
							argValue = 0;
						}
					} else {
						c->warning(__tr2qs("Missing argument for ?d escape sequence, 0 assumed"));
						argValue = 0;
					}
					argValue = 0;
					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++ = QChar(*--pNumBuf); } while(pNumBuf != numberBuffer);
					reallen += argUValue;
					pVar = vArgs.next();
					continue;
				}
				case 'u': //unsigned integer
				{
					if(pVar)
					{
						if(!pVar->asInteger(argValue))
						{
							c->warning(__tr2qs("Invalid argument for ?u escape sequence, 0 assumed"));
							argValue = 0;
						}
					} else {
						c->warning(__tr2qs("Missing argument for ?u escape sequence, 0 assumed"));
						argValue = 0;
					}
					argUValue = (kvs_uint_t)argValue;
					//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;
					pVar = vArgs.next();
					continue;
				}
				case 'h':
				case 'x': // hexadecimal unsigned integer
				{
					static char hexsmalldigits[]="0123456789abcdef";
	
					if(pVar)
					{
						if(!pVar->asInteger(argValue))
						{
							c->warning(__tr2qs("Invalid argument for ?x escape sequence, 0 assumed"));
							argValue = 0;
						}
					} else {
						c->warning(__tr2qs("Missing argument for ?x escape sequence, 0 assumed"));
						argValue = 0;
					}
					argUValue = (kvs_uint_t)argValue;
	
					//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;
					pVar = vArgs.next();
					continue;
				}
				case 'H':
				case 'X': // hexadecimal unsigned integer
				{
					static char hexbigdigits[]="0123456789ABCDEF";
					if(pVar)
					{
						if(!pVar->asInteger(argValue))
						{
							c->warning(__tr2qs("Invalid argument for ?X escape sequence, 0 assumed"));
							argValue = 0;
						}
					} else {
						c->warning(__tr2qs("Missing argument for ?X escape sequence, 0 assumed"));
						argValue = 0;
					}
					argUValue = (kvs_uint_t)argValue;
					//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;
					pVar = vArgs.next();
					continue;
				}
				case '?':
				{
					if(fmt->unicode())
					{
						if(reallen == allocsize)INCREMENT_MEM
						*p++ = *fmt;
						reallen++;
					}
					continue;
				}
				break;
				case '.':
				{
					// precision mark
					const QChar * save = fmt;
					fmt++;
					unsigned int uPrecision = 0;
					
					char fmtbuffer[8];
					fmtbuffer[0] = '%';
					fmtbuffer[1] = '.';
	
					int idx = 2;
					
					while((fmt->unicode() >= '0') && (fmt->unicode() <= '9') && (idx < 6))
					{
						uPrecision *= 10;
						fmtbuffer[idx] = fmt->unicode();
						uPrecision += fmtbuffer[idx] - '0';
						fmt++;
						idx++;
					}
					fmtbuffer[idx] = fmt->unicode();
					fmtbuffer[idx+1] = 0;
	
					if(pVar)
					{
						if(!pVar->asReal(argRValue))
						{
							c->warning(__tr2qs("Invalid argument for a floating point escape sequence, 0.0 assumed"));
							argRValue = 0.0;
						}
					} else {
						c->warning(__tr2qs("Missing argument for a floating point escape sequence, 0.0 assumed"));
						argRValue = 0;
					}
					switch(fmt->unicode())
					{
						case 'e':
						case 'E':
						case 'F':
						case 'f':
							::sprintf(numberBuffer,fmtbuffer,argRValue);
							//copy now....
							argValue = kvi_strLen(numberBuffer);
							if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue)
							pNumBuf = numberBuffer;
							while(*pNumBuf){ *p++ = *pNumBuf++; }
							reallen += argValue;
						break;
						default:
							// anything else is crap.. invalid format
							fmt = save;
							*p++ = '?';  //write it
							reallen++;
							if(fmt->unicode())
							{
								if(reallen == allocsize)INCREMENT_MEM
								*p++ = *fmt;
								reallen++;
							}
						break;
					}
					pVar = vArgs.next();
					continue;
				}
				break;
				default: //a normal ? 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);
	}

	c->returnValue()->setString(s);
	return true;
}



/*********************************************************************/
//              Module stuff
/********************************************************************/

static bool str_module_init(KviModule * m)
{
	KVSM_REGISTER_FUNCTION(m,"split",str_kvs_fnc_split);
	KVSM_REGISTER_FUNCTION(m,"grep",str_kvs_fnc_grep);
	KVSM_REGISTER_FUNCTION(m,"charsum",str_kvs_fnc_charsum);
	KVSM_REGISTER_FUNCTION(m,"len",str_kvs_fnc_len);
	KVSM_REGISTER_FUNCTION(m,"length",str_kvs_fnc_len);
	KVSM_REGISTER_FUNCTION(m,"isempty",str_kvs_fnc_isempty);
	KVSM_REGISTER_FUNCTION(m,"join",str_kvs_fnc_join);

	KVSM_REGISTER_FUNCTION(m,"section",str_kvs_fnc_section);
	KVSM_REGISTER_FUNCTION(m,"lowcase",str_kvs_fnc_lowcase);
	KVSM_REGISTER_FUNCTION(m,"upcase",str_kvs_fnc_upcase);
	KVSM_REGISTER_FUNCTION(m,"localeupcase",str_kvs_fnc_localeupcase);
	KVSM_REGISTER_FUNCTION(m,"localelowcase",str_kvs_fnc_localelowcase);
	KVSM_REGISTER_FUNCTION(m,"isnumber",str_kvs_fnc_isnumber);
	KVSM_REGISTER_FUNCTION(m,"isunsignednumber",str_kvs_fnc_isunsignednumber);
	KVSM_REGISTER_FUNCTION(m,"contains",str_kvs_fnc_contains);
	KVSM_REGISTER_FUNCTION(m,"containsnocase",str_kvs_fnc_containsnocase);
	KVSM_REGISTER_FUNCTION(m,"equal",str_kvs_fnc_equal);
	KVSM_REGISTER_FUNCTION(m,"equalnocase",str_kvs_fnc_equalnocase);
	KVSM_REGISTER_FUNCTION(m,"cmp",str_kvs_fnc_cmp);
	KVSM_REGISTER_FUNCTION(m,"cmpnocase",str_kvs_fnc_cmpnocase);
	KVSM_REGISTER_FUNCTION(m,"find",str_kvs_fnc_find);
	KVSM_REGISTER_FUNCTION(m,"findfirst",str_kvs_fnc_findfirst);
	KVSM_REGISTER_FUNCTION(m,"findfirstnocase",str_kvs_fnc_findfirstnocase);
	KVSM_REGISTER_FUNCTION(m,"findlast",str_kvs_fnc_findlast);
	KVSM_REGISTER_FUNCTION(m,"findlastnocase",str_kvs_fnc_findlastnocase);
	KVSM_REGISTER_FUNCTION(m,"left",str_kvs_fnc_left);
	KVSM_REGISTER_FUNCTION(m,"right",str_kvs_fnc_right);
	KVSM_REGISTER_FUNCTION(m,"mid",str_kvs_fnc_mid);
	KVSM_REGISTER_FUNCTION(m,"append",str_kvs_fnc_append);
	KVSM_REGISTER_FUNCTION(m,"prepend",str_kvs_fnc_prepend);
	KVSM_REGISTER_FUNCTION(m,"insert",str_kvs_fnc_insert);
	KVSM_REGISTER_FUNCTION(m,"strip",str_kvs_fnc_strip);
	KVSM_REGISTER_FUNCTION(m,"stripright",str_kvs_fnc_stripright);
	KVSM_REGISTER_FUNCTION(m,"stripleft",str_kvs_fnc_stripleft);
	KVSM_REGISTER_FUNCTION(m,"stripcolors",str_kvs_fnc_stripcolors);
	KVSM_REGISTER_FUNCTION(m,"replace",str_kvs_fnc_replace);
	KVSM_REGISTER_FUNCTION(m,"replacenocase",str_kvs_fnc_replacenocase);
	KVSM_REGISTER_FUNCTION(m,"urlencode",str_kvs_fnc_urlencode);
	KVSM_REGISTER_FUNCTION(m,"lefttolast",str_kvs_fnc_lefttolast);
	KVSM_REGISTER_FUNCTION(m,"lefttofirst",str_kvs_fnc_lefttofirst);
	KVSM_REGISTER_FUNCTION(m,"rightfromfirst",str_kvs_fnc_rightfromfirst);
	KVSM_REGISTER_FUNCTION(m,"rightfromlast",str_kvs_fnc_rightfromlast);
	KVSM_REGISTER_FUNCTION(m,"match",str_kvs_fnc_match);
	KVSM_REGISTER_FUNCTION(m,"matchnocase",str_kvs_fnc_matchnocase);
	KVSM_REGISTER_FUNCTION(m,"word",str_kvs_fnc_word);
	KVSM_REGISTER_FUNCTION(m,"token",str_kvs_fnc_token);
	KVSM_REGISTER_FUNCTION(m,"fromClipboard",str_kvs_fnc_fromclipboard);
	KVSM_REGISTER_FUNCTION(m,"digest",str_kvs_fnc_digest);
	KVSM_REGISTER_FUNCTION(m,"printf",str_kvs_fnc_printf);

	KVSM_REGISTER_SIMPLE_COMMAND(m,"toClipboard",str_kvs_cmd_toClipboard);
	return true;
}

static bool str_module_cleanup(KviModule *m)
{
	return true;
}

KVIRC_MODULE(
	"File",                                                 // module name
	"1.0.0",                                                // module version
	"Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)"\
	"          (C) 2002 Juanjo Alvarez (juanjux@yahoo.es)" \
	"          (C) 2005 Tonino Imbesi (grifisx at barmes dot org)" \
	"          (C) 2005 Alessandro Carbone (noldor at barmes dot org)", // author & (C)
	"Interface to the str system",
	str_module_init,
	0,
	0,
	str_module_cleanup
)
