text: // SoftEther VPN Source Code - Stable Edition Repository
  // Mayaqua Kernel
  // 
  // SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
  // 
  // Copyright (c) Daiyuu Nobori.
  // Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
  // Copyright (c) SoftEther Corporation.
  // Copyright (c) all contributors on SoftEther VPN project in GitHub.
  // 
  // All Rights Reserved.
  // 
  // http://www.softether.org/
  // 
  // This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
  // Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
  // 
  // License: The Apache License, Version 2.0
  // https://www.apache.org/licenses/LICENSE-2.0
  // 
  // DISCLAIMER
  // ==========
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  // SOFTWARE.
  // 
  // THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
  // JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
  // DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
  // JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
  // AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
  // SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
  // OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
  // AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
  // CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
  // JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
  // ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
  // PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
  // LAW OR COURT RULE.
  // 
  // USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
  // A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
  // RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
  // COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
  // DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
  // CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
  // COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
  // WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
  // INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
  // AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
  // DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
  // AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
  // PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
  // PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
  // LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
  // RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
  // STATEMENT FOR WARNING AND DISCLAIMER.
  // 
  // READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
  // SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
  // LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
  // 
  // 
  // SOURCE CODE CONTRIBUTION
  // ------------------------
  // 
  // Your contribution to SoftEther VPN Project is much appreciated.
  // Please send patches to us through GitHub.
  // Read the SoftEther VPN Patch Acceptance Policy in advance:
  // http://www.softether.org/5-download/src/9.patch
  // 
  // 
  // DEAR SECURITY EXPERTS
  // ---------------------
  // 
  // If you find a bug or a security vulnerability please kindly inform us
  // about the problem immediately so that we can fix the security problem
  // to protect a lot of users around the world as soon as possible.
  // 
  // Our e-mail address for security reports is:
  // softether-vpn-security [at] softether.org
  // 
  // Please note that the above e-mail address is not a technical support
  // inquiry address. If you need technical assistance, please visit
  // http://www.softether.org/ and ask your question on the users forum.
  // 
  // Thank you for your cooperation.
  // 
  // 
  // NO MEMORY OR RESOURCE LEAKS
  // ---------------------------
  // 
  // The memory-leaks and resource-leaks verification under the stress
  // test has been passed before release this source code.
  
  
  // Internat.c
  // String conversion library for internationalization
  
  #include <GlobalConst.h>
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <wchar.h>
  #include <stdarg.h>
  #include <time.h>
  #include <errno.h>
  #include <Mayaqua/Mayaqua.h>
  
  extern LOCK *token_lock;
  static char charset[MAX_SIZE] = "EUCJP";
  static LOCK *iconv_lock = NULL;
  void *iconv_cache_wide_to_str = 0;
  void *iconv_cache_str_to_wide = 0;
  
  // Initialize the string
  void ClearUniStr(wchar_t *str, UINT str_size)
  {
  	UniStrCpy(str, str_size, L"");
  }
  
  // Examine whether the string contains the specified character
  bool UniInChar(wchar_t *string, wchar_t c)
  {
  	UINT i, len;
  	// Validate arguments
  	if (string == NULL)
  	{
  		return false;
  	}
  
  	len = UniStrLen(string);
  
  	for (i = 0;i < len;i++)
  	{
  		if (string[i] == c)
  		{
  			return true;
  		}
  	}
  
  	return false;
  }
  
  // Check whether the string is included
  bool UniInStr(wchar_t *str, wchar_t *keyword)
  {
  	return UniInStrEx(str, keyword, false);
  }
  bool UniInStrEx(wchar_t *str, wchar_t *keyword, bool case_sensitive)
  {
  	// Validate arguments
  	if (UniIsEmptyStr(str) || UniIsEmptyStr(keyword))
  	{
  		return false;
  	}
  
  	if (UniSearchStrEx(str, keyword, 0, case_sensitive) == INFINITE)
  	{
  		return false;
  	}
  
  	return true;
  }
  
  // Convert to binary data
  BUF *UniStrToBin(wchar_t *str)
  {
  	char *str_a = CopyUniToStr(str);
  	BUF *ret;
  
  	ret = StrToBin(str_a);
  
  	Free(str_a);
  
  	return ret;
  }
  
  // Generate a sequence of specified characters
  wchar_t *UniMakeCharArray(wchar_t c, UINT count)
  {
  	UINT i;
  	wchar_t *ret = Malloc(sizeof(wchar_t) * (count + 1));
  
  	for (i = 0;i < count;i++)
  	{
  		ret[i] = c;
  	}
  
  	ret[count] = 0;
  
  	return ret;
  }
  
  // Check whether the character is safe
  bool UniIsSafeChar(wchar_t c)
  {
  	UINT i, len;
  	wchar_t *check_str =
  		L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  		L"abcdefghijklmnopqrstuvwxyz"
  		L"0123456789"
  		L" ()-_#%&.";
  
  	len = UniStrLen(check_str);
  	for (i = 0;i < len;i++)
  	{
  		if (c == check_str[i])
  		{
  			return true;
  		}
  	}
  	return false;
  }
  
  // Convert the token list to a string list
  LIST *UniTokenListToList(UNI_TOKEN_LIST *t)
  {
  	UINT i;
  	LIST *o;
  	// Validate arguments
  	if (t == NULL)
  	{
  		return NULL;
  	}
  
  	o = NewListFast(NULL);
  	for (i = 0;i < t->NumTokens;i++)
  	{
  		Insert(o, UniCopyStr(t->Token[i]));
  	}
  
  	return o;
  }
  
  // Convert a string list to a token list
  UNI_TOKEN_LIST *UniListToTokenList(LIST *o)
  {
  	UINT i;
  	UNI_TOKEN_LIST *t;
  	// Validate arguments
  	if (o == NULL)
  	{
  		return NULL;
  	}
  
  	t = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  	t->NumTokens = LIST_NUM(o);
  	t->Token = ZeroMalloc(sizeof(wchar_t *) * t->NumTokens);
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		t->Token[i] = UniCopyStr(LIST_DATA(o, i));
  	}
  
  	return t;
  }
  
  // Free the string list
  void UniFreeStrList(LIST *o)
  {
  	UINT i;
  	// Validate arguments
  	if (o == NULL)
  	{
  		return;
  	}
  
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		wchar_t *s = LIST_DATA(o, i);
  		Free(s);
  	}
  
  	ReleaseList(o);
  }
  
  // Convert the string list to a string
  BUF *UniStrListToStr(LIST *o)
  {
  	BUF *b;
  	UINT i;
  	wchar_t c;
  	// Validate arguments
  	if (o == NULL)
  	{
  		return NULL;
  	}
  	b = NewBuf();
  
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		wchar_t *s = LIST_DATA(o, i);
  		WriteBuf(b, s, UniStrSize(s));
  	}
  
  	c = 0;
  	WriteBuf(b, &c, sizeof(c));
  
  	SeekBuf(b, 0, 0);
  
  	return b;
  }
  
  // Convert a (NULL delimited) string to list
  LIST *UniStrToStrList(wchar_t *str, UINT size)
  {
  	LIST *o;
  	wchar_t *tmp;
  	UINT tmp_size;
  	UINT i;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	o = NewListFast(NULL);
  
  	i = 0;
  	while (true)
  	{
  		if (i >= size)
  		{
  			break;
  		}
  		if (*str == 0)
  		{
  			break;
  		}
  
  		tmp_size = UniStrSize(str);
  		tmp = ZeroMalloc(tmp_size);
  		UniStrCpy(tmp, tmp_size, str);
  		Add(o, tmp);
  		str += UniStrLen(str) + 1;
  		i++;
  	}
  
  	return o;
  }
  
  // Normalize the line breaks
  wchar_t *UniNormalizeCrlf(wchar_t *str)
  {
  	wchar_t *ret;
  	UINT ret_size, i, len, wp;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	len = UniStrLen(str);
  	ret_size = sizeof(wchar_t) * (len + 32) * 2;
  	ret = Malloc(ret_size);
  
  	wp = 0;
  
  	for (i = 0;i < len;i++)
  	{
  		wchar_t c = str[i];
  
  		switch (c)
  		{
  		case L'\r':
  			if (str[i + 1] == L'\n')
  			{
  				i++;
  			}
  			ret[wp++] = L'\r';
  			ret[wp++] = L'\n';
  			break;
  
  		case L'\n':
  			ret[wp++] = L'\r';
  			ret[wp++] = L'\n';
  			break;
  
  		default:
  			ret[wp++] = c;
  			break;
  		}
  	}
  
  	ret[wp++] = 0;
  
  	return ret;
  }
  
  // Check whether str ends with the key
  bool UniEndWith(wchar_t *str, wchar_t *key)
  {
  	UINT str_len;
  	UINT key_len;
  	// Validate arguments
  	if (str == NULL || key == NULL)
  	{
  		return false;
  	}
  
  	// Comparison
  	str_len = UniStrLen(str);
  	key_len = UniStrLen(key);
  	if (str_len < key_len)
  	{
  		return false;
  	}
  
  	if (UniStrCmpi(str + (str_len - key_len), key) == 0)
  	{
  		return true;
  	}
  	else
  	{
  		return false;
  	}
  }
  
  // Check whether str starts with the key
  bool UniStartWith(wchar_t *str, wchar_t *key)
  {
  	UINT str_len;
  	UINT key_len;
  	wchar_t *tmp;
  	bool ret;
  	// Validate arguments
  	if (str == NULL || key == NULL)
  	{
  		return false;
  	}
  
  	// Comparison
  	str_len = UniStrLen(str);
  	key_len = UniStrLen(key);
  	if (str_len < key_len)
  	{
  		return false;
  	}
  	if (str_len == 0 || key_len == 0)
  	{
  		return false;
  	}
  	tmp = CopyUniStr(str);
  	tmp[key_len] = 0;
  
  	if (UniStrCmpi(tmp, key) == 0)
  	{
  		ret = true;
  	}
  	else
  	{
  		ret = false;
  	}
  
  	Free(tmp);
  
  	return ret;
  }
  
  // Convert the integer to a comma-separated string
  void UniToStr3(wchar_t *str, UINT size, UINT64 value)
  {
  	char tmp[MAX_SIZE];
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	ToStr3(tmp, sizeof(tmp), value);
  
  	StrToUni(str, size, tmp);
  }
  
  // Format of the string (internal function)
  wchar_t *InternalFormatArgs(wchar_t *fmt, va_list args, bool ansi_mode)
  {
  	UINT i, len;
  	wchar_t *tmp;
  	UINT tmp_size;
  	LIST *o;
  	UINT mode = 0;
  	UINT wp;
  	UINT total_size;
  	wchar_t *ret;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return NULL;
  	}
  
  	len = UniStrLen(fmt);
  	tmp_size = UniStrSize(fmt);
  	tmp = Malloc(tmp_size);
  
  	o = NewListFast(NULL);
  
  	mode = 0;
  
  	wp = 0;
  
  	for (i = 0;i < len;i++)
  	{
  		wchar_t c = fmt[i];
  
  		if (mode == 0)
  		{
  			// Normal character mode
  			switch (c)
  			{
  			case L'%':
  				// The start of the format specification
  				if (fmt[i + 1] == L'%')
  				{
  					// If the next character is also '%', output a '%' simply
  					i++;
  					tmp[wp++] = c;
  				}
  				else
  				{
  					// Shift the state if the next character is not a '%'
  					mode = 1;
  					tmp[wp++] = 0;
  					wp = 0;
  					Add(o, CopyUniStr(tmp));
  					tmp[wp++] = c;
  				}
  				break;
  			default:
  				// Ordinary character
  				tmp[wp++] = c;
  				break;
  			}
  		}
  		else
  		{
  			char *tag;
  			char dst[MAX_SIZE];
  			wchar_t *target_str;
  			wchar_t *padding_str;
  			bool left_padding;
  			UINT target_str_len;
  			UINT total_len;
  			wchar_t *output_str;
  			UINT padding;
  			// Formatting mode
  			switch (c)
  			{
  			case L'c':
  			case L'C':
  			case L'd':
  			case L'i':
  			case L'o':
  			case L'u':
  			case L'x':
  			case L'X':
  				// int type
  				tmp[wp++] = c;
  				tmp[wp++] = 0;
  				tag = CopyUniToStr(tmp);
  
  				#ifdef	OS_WIN32
  					ReplaceStrEx(tag, 0, tag, "ll", "I64", false);
  				#else	// OS_WIN32
  					ReplaceStrEx(tag, 0, tag, "I64", "ll", false);
  				#endif	// OS_WIN32
  
  				if ((UniStrLen(tmp) >= 5 && tmp[UniStrLen(tmp) - 4] == L'I' &&
  					tmp[UniStrLen(tmp) - 3] == L'6' &&
  					tmp[UniStrLen(tmp) - 2] == L'4') ||
  					(
  					UniStrLen(tmp) >= 4 && tmp[UniStrLen(tmp) - 3] == L'l' &&
  					tmp[UniStrLen(tmp) - 2] == L'l'))
  				{
  					#ifdef	OS_WIN32
  						_snprintf(dst, sizeof(dst), tag, va_arg(args, UINT64));
  					#else	// OS_WIN32
  						snprintf(dst, sizeof(dst), tag, va_arg(args, UINT64));
  					#endif	// OS_WIN32
  				}
  				else
  				{
  					#ifdef	OS_WIN32
  						_snprintf(dst, sizeof(dst), tag, va_arg(args, int));
  					#else	// OS_WIN32
  						snprintf(dst, sizeof(dst), tag, va_arg(args, int));
  					#endif	// OS_WIN32
  				}
  
  				Free(tag);
  				Add(o, CopyStrToUni(dst));
  
  				wp = 0;
  				mode = 0;
  				break;
  			case L'e':
  			case L'E':
  			case L'f':
  			case L'g':
  			case L'G':
  				// Double type
  				tmp[wp++] = c;
  				tmp[wp++] = 0;
  				tag = CopyUniToStr(tmp);
  
  				#ifdef	OS_WIN32
  					_snprintf(dst, sizeof(dst), tag, va_arg(args, double));
  				#else	// OS_WIN32
  					snprintf(dst, sizeof(dst), tag, va_arg(args, double));
  				#endif	// OS_WIN32
  
  				Free(tag);
  				Add(o, CopyStrToUni(dst));
  
  				wp = 0;
  				mode = 0;
  				break;
  			case L'n':
  			case L'p':
  				// Pointer type
  				tmp[wp++] = c;
  				tmp[wp++] = 0;
  				tag = ZeroMalloc(UniStrSize(tmp) + 32);
  				UniToStr(tag, 0, tmp);
  
  				#ifdef	OS_WIN32
  					_snprintf(dst, sizeof(dst), tag, va_arg(args, void *));
  				#else	// OS_WIN32
  					snprintf(dst, sizeof(dst), tag, va_arg(args, void *));
  				#endif	// OS_WIN32
  
  				Free(tag);
  				Add(o, CopyStrToUni(dst));
  
  				wp = 0;
  				mode = 0;
  				break;
  			case L'r':
  			case L'R':
  				// IP address type
  				tmp[wp++] = c;
  				tmp[wp++] = 0;
  
  				Zero(dst, sizeof(dst));
  				IPToStr(dst, sizeof(dst), va_arg(args, void *));
  
  				Add(o, CopyStrToUni(dst));
  
  				wp = 0;
  				mode = 0;
  				break;
  
  			case L's':
  			case L'S':
  				// String type
  				tmp[wp++] = c;
  				tmp[wp++] = 0;
  
  				if (ansi_mode == false)
  				{
  					if (c == L'S')
  					{
  						c = L's';
  					}
  					else
  					{
  						c = L'S';
  					}
  				}
  
  				if (c == L's')
  				{
  					target_str = CopyStrToUni(va_arg(args, char *));
  				}
  				else
  				{
  					target_str = CopyUniStr(va_arg(args, wchar_t *));
  				}
  
  				if (target_str == NULL)
  				{
  					target_str = CopyUniStr(L"(null)");
  				}
  
  				padding = 0;
  				left_padding = false;
  				if (tmp[1] == L'-')
  				{
  					// Left aligned
  					if (UniStrLen(tmp) >= 3)
  					{
  						padding = UniToInt(&tmp[2]);
  					}
  					left_padding = true;
  				}
  				else
  				{
  					// Right aligned
  					if (UniStrLen(tmp) >= 2)
  					{
  						padding = UniToInt(&tmp[1]);
  					}
  				}
  
  				target_str_len = UniStrWidth(target_str);
  
  				if (padding > target_str_len)
  				{
  					UINT len = padding - target_str_len;
  					UINT i;
  					padding_str = ZeroMalloc(sizeof(wchar_t) * (len + 1));
  					for (i = 0;i < len;i++)
  					{
  						padding_str[i] = L' ';
  					}
  				}
  				else
  				{
  					padding_str = ZeroMalloc(sizeof(wchar_t));
  				}
  
  				total_len = sizeof(wchar_t) * (UniStrLen(padding_str) + UniStrLen(target_str) + 1);
  				output_str = ZeroMalloc(total_len);
  				output_str[0] = 0;
  
  				if (left_padding == false)
  				{
  					UniStrCat(output_str, total_len, padding_str);
  				}
  				UniStrCat(output_str, total_len, target_str);
  				if (left_padding)
  				{
  					UniStrCat(output_str, total_len, padding_str);
  				}
  
  				Add(o, output_str);
  
  				Free(target_str);
  				Free(padding_str);
  
  				wp = 0;
  				mode = 0;
  				break;
  			default:
  				// Normal string
  				tmp[wp++] = c;
  				break;
  			}
  		}
  	}
  	tmp[wp++] = 0;
  	wp = 0;
  
  	if (UniStrLen(tmp) >= 1)
  	{
  		Add(o, CopyUniStr(tmp));
  	}
  
  	total_size = sizeof(wchar_t);
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		wchar_t *s = LIST_DATA(o, i);
  		total_size += UniStrLen(s) * sizeof(wchar_t);
  	}
  
  	ret = ZeroMalloc(total_size);
  	for (i = 0;i < LIST_NUM(o);i++)
  	{
  		wchar_t *s = LIST_DATA(o, i);
  		UniStrCat(ret, total_size, s);
  		Free(s);
  	}
  
  	ReleaseList(o);
  
  	Free(tmp);
  
  	return ret;
  }
  
  // Get the width of the string
  UINT UniStrWidth(wchar_t *str)
  {
  	UINT i, len, ret;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	ret = 0;
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		if (str[i] <= 255)
  		{
  			ret++;
  		}
  		else
  		{
  			ret += 2;
  		}
  	}
  	return ret;
  }
  
  // Display a dump of Unicode string
  void DumpUniStr(wchar_t *str)
  {
  	UINT i, len;
  	char *s;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	s = CopyUniToStr(str);
  
  	Print("DumpUniStr: %s\n  ", s);
  
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		Print("0x%04X ", str[i]);
  	}
  	Print("\n");
  
  	Free(s);
  }
  
  // Display the dump of the string
  void DumpStr(char *str)
  {
  	UINT i, len;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	Print("DumpStr: %s\n  ", str);
  
  	len = StrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		Print("0x%02X ", str[i]);
  	}
  	Print("\n");
  }
  
  // Convert string of 2 byte/character to wchar_t of 4 byte/character
  wchar_t *Utf16ToWide(USHORT *str)
  {
  	wchar_t *ret;
  	UINT len, i;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	len = 0;
  	while (true)
  	{
  		if (str[len] == 0)
  		{
  			break;
  		}
  		len++;
  	}
  
  	ret = Malloc((len + 1) * sizeof(wchar_t));
  	for (i = 0;i < len + 1;i++)
  	{
  		ret[i] = (wchar_t)str[i];
  	}
  
  	return ret;
  }
  
  // Convert wchar_t string of 4 byte/character to string of 2 byte/character
  USHORT *WideToUtf16(wchar_t *str)
  {
  	USHORT *ret;
  	UINT len;
  	UINT ret_size;
  	UINT i;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	len = UniStrLen(str);
  
  	ret_size = (len + 1) * 2;
  	ret = Malloc(ret_size);
  
  	for (i = 0;i < len + 1;i++)
  	{
  		ret[i] = (USHORT)str[i];
  	}
  
  	return ret;
  }
  
  // Initialization of the International Library
  void InitInternational()
  {
  #ifdef	OS_UNIX
  	void *d;
  
  	if (iconv_lock != NULL)
  	{
  		return;
  	}
  
  	GetCurrentCharSet(charset, sizeof(charset));
  	d = IconvWideToStrInternal();
  	if (d == (void *)-1)
  	{
  #ifdef	UNIX_MACOS
  		StrCpy(charset, sizeof(charset), "utf-8");
  #else	// UNIX_MACOS
  		StrCpy(charset, sizeof(charset), "EUCJP");
  #endif	// UNIX_MACOS
  		d = IconvWideToStrInternal();
  		if (d == (void *)-1)
  		{
  			StrCpy(charset, sizeof(charset), "US");
  		}
  		else
  		{
  			IconvFreeInternal(d);
  		}
  	}
  	else
  	{
  		IconvFreeInternal(d);
  	}
  
  	iconv_lock = NewLockMain();
  
  	iconv_cache_wide_to_str = IconvWideToStrInternal();
  	iconv_cache_str_to_wide = IconvStrToWideInternal();
  #endif	// OS_UNIX
  }
  
  // Release of the International Library
  void FreeInternational()
  {
  #ifdef	OS_UNIX
  #endif	// OS_UNIX
  }
  
  #ifdef	OS_UNIX
  
  // Calculate the size when the string converted to Unicode
  UINT UnixCalcStrToUni(char *str)
  {
  	wchar_t *tmp;
  	UINT len, tmp_size;
  	UINT ret;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	len = StrLen(str);
  	tmp_size = len * 5 + 10;
  	tmp = ZeroMalloc(tmp_size);
  	UnixStrToUni(tmp, tmp_size, str);
  	ret = UniStrLen(tmp);
  	Free(tmp);
  
  	return (ret + 1) * sizeof(wchar_t);
  }
  
  // Convert the strings to Unicode
  UINT UnixStrToUni(wchar_t *s, UINT size, char *str)
  {
  	void *d;
  	char *inbuf;
  	size_t insize;
  	char *outbuf;
  	char *outbuf_orig;
  	size_t outsize;
  	wchar_t *tmp;
  	// Validate arguments
  	if (s == NULL || str == NULL)
  	{
  		return 0;
  	}
  
  	d = IconvStrToWide();
  	if (d == (void *)-1)
  	{
  		UniStrCpy(s, size, L"");
  		return 0;
  	}
  
  	inbuf = (char *)str;
  	insize = StrLen(str) + 1;
  	outsize = insize * 5 + 10;
  	outbuf_orig = outbuf = ZeroMalloc(outsize);
  
  	if (iconv((iconv_t)d, (char **)&inbuf, (size_t *)&insize, (char **)&outbuf, (size_t *)&outsize) == (size_t)(-1))
  	{
  		Free(outbuf_orig);
  		UniStrCpy(s, size, L"");
  		IconvFree(d);
  		return 0;
  	}
  
  	tmp = Utf16ToWide((USHORT *)outbuf_orig);
  	Free(outbuf_orig);
  
  	UniStrCpy(s, size, tmp);
  	IconvFree(d);
  
  	Free(tmp);
  
  	return UniStrLen(s);
  }
  
  // Calculate the size when the Unicode converted to string
  UINT UnixCalcUniToStr(wchar_t *s)
  {
  	char *tmp;
  	UINT tmp_size;
  	UINT ret;
  	// Validate arguments
  	if (s == NULL)
  	{
  		return 0;
  	}
  
  	tmp_size = UniStrLen(s) * 5 + 10;
  	tmp = ZeroMalloc(tmp_size);
  	UnixUniToStr(tmp, tmp_size, s);
  
  	ret = StrSize(tmp);
  	Free(tmp);
  
  	return ret;
  }
  
  // Converted a Unicode string to a string
  UINT UnixUniToStr(char *str, UINT size, wchar_t *s)
  {
  	USHORT *tmp;
  	char *inbuf;
  	size_t insize;
  	char *outbuf;
  	char *outbuf_orig;
  	size_t outsize;
  	void *d;
  	// Validate arguments
  	if (str == NULL || s == NULL)
  	{
  		return 0;
  	}
  
  	// Convert a wchar_t string to sequence of 2-bytes first
  	tmp = WideToUtf16(s);
  	inbuf = (char *)tmp;
  	insize = (UniStrLen(s) + 1) * 2;
  	outsize = insize * 5 + 10;
  	outbuf_orig = outbuf = ZeroMalloc(outsize);
  
  	d = IconvWideToStr();
  	if (d == (void *)-1)
  	{
  		StrCpy(str, size, "");
  		Free(outbuf);
  		Free(tmp);
  		return 0;
  	}
  
  	if (iconv((iconv_t)d, (char **)&inbuf, (size_t *)&insize, (char **)&outbuf, (size_t *)&outsize) == (size_t)(-1))
  	{
  		Free(outbuf_orig);
  		IconvFree(d);
  		StrCpy(str, size, "");
  		Free(tmp);
  		return 0;
  	}
  
  	StrCpy(str, size, outbuf_orig);
  
  	Free(outbuf_orig);
  	IconvFree(d);
  	Free(tmp);
  
  	return StrLen(str);
  }
  
  // Converted the whcar_t to char
  void *IconvWideToStrInternal()
  {
  	return (void *)iconv_open(charset, IsBigEndian() ? "UTF-16BE" : "UTF-16LE");
  }
  
  // Convert the char to a wchar_t
  void *IconvStrToWideInternal()
  {
  	return (void *)iconv_open(IsBigEndian() ? "UTF-16BE" : "UTF-16LE", charset);
  }
  
  // Close the handle
  int IconvFreeInternal(void *d)
  {
  	iconv_close((iconv_t)d);
  	return 0;
  }
  
  void *IconvWideToStr()
  {
  	if (iconv_cache_wide_to_str == (void *)-1)
  	{
  		return (void *)-1;
  	}
  
  	Lock(iconv_lock);
  
  	return iconv_cache_wide_to_str;
  }
  
  void *IconvStrToWide()
  {
  	if (iconv_cache_str_to_wide == (void *)-1)
  	{
  		return (void *)-1;
  	}
  
  	Lock(iconv_lock);
  
  	return iconv_cache_str_to_wide;
  }
  
  int IconvFree(void *d)
  {
  	Unlock(iconv_lock);
  
  	return 0;
  }
  
  // Get the character set that is currently used from the environment variable
  void GetCurrentCharSet(char *name, UINT size)
  {
  	char tmp[MAX_SIZE];
  	TOKEN_LIST *t;
  	// Validate arguments
  	if (name == NULL)
  	{
  		return;
  	}
  
  	Zero(tmp, sizeof(tmp));
  	if (GetEnv("LANG", tmp, sizeof(tmp)) == false || IsEmptyStr(tmp))
  	{
  		Zero(tmp, sizeof(tmp));
  		if (GetEnv("LOCATION", tmp, sizeof(tmp)) == false || IsEmptyStr(tmp))
  		{
  			StrCpy(tmp, sizeof(tmp), "C");
  		}
  	}
  
  	Trim(tmp);
  
  	t = ParseToken(tmp, ".");
  	if (t->NumTokens >= 2)
  	{
  		StrCpy(name, size, t->Token[1]);
  	}
  	else
  	{
  		if (t->NumTokens == 1)
  		{
  			StrCpy(name, size, t->Token[0]);
  		}
  		else
  		{
  			StrCpy(name, size, "eucJP");
  		}
  	}
  	FreeToken(t);
  
  	StrUpper(name);
  }
  
  #endif	// OS_UNIX
  
  // Check whether the specified string is a space
  bool UniIsEmptyStr(wchar_t *str)
  {
  	return IsEmptyUniStr(str);
  }
  bool IsEmptyUniStr(wchar_t *str)
  {
  	bool ret;
  	wchar_t *s;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return true;
  	}
  
  	s = UniCopyStr(str);
  
  	UniTrim(s);
  	if (UniStrLen(s) == 0)
  	{
  		ret = true;
  	}
  	else
  	{
  		ret = false;
  	}
  
  	Free(s);
  
  	return ret;
  }
  
  // Check whether the specified string is a number
  bool UniIsNum(wchar_t *str)
  {
  	char tmp[MAX_SIZE];
  
  	// Validate arguments
  	if (str == NULL)
  	{
  		return false;
  	}
  
  	UniToStrForSingleChars(tmp, sizeof(tmp), str);
  
  	return IsNum(tmp);
  }
  
  
  // Empty Unicode token list
  UNI_TOKEN_LIST *UniNullToken()
  {
  	UNI_TOKEN_LIST *ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  	ret->Token = ZeroMalloc(0);
  
  	return ret;
  }
  
  // Empty Unicode token list (Alias)
  UNI_TOKEN_LIST *NullUniToken()
  {
  	return UniNullToken();
  }
  
  // Convert the token list to Unicode token list
  UNI_TOKEN_LIST *TokenListToUniTokenList(TOKEN_LIST *src)
  {
  	UNI_TOKEN_LIST *ret;
  	UINT i;
  	// Validate arguments
  	if (src == NULL)
  	{
  		return NULL;
  	}
  
  	ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  	ret->NumTokens = src->NumTokens;
  	ret->Token = ZeroMalloc(sizeof(wchar_t *) * ret->NumTokens);
  
  	for (i = 0;i < ret->NumTokens;i++)
  	{
  		ret->Token[i] = CopyStrToUni(src->Token[i]);
  	}
  
  	return ret;
  }
  
  // Convert a Unicode token list to a token list
  TOKEN_LIST *UniTokenListToTokenList(UNI_TOKEN_LIST *src)
  {
  	TOKEN_LIST *ret;
  	UINT i;
  	// Validate arguments
  	if (src == NULL)
  	{
  		return NULL;
  	}
  
  	ret = ZeroMalloc(sizeof(TOKEN_LIST));
  	ret->NumTokens = src->NumTokens;
  	ret->Token = ZeroMalloc(sizeof(char *) * ret->NumTokens);
  
  	for (i = 0;i < ret->NumTokens;i++)
  	{
  		ret->Token[i] = CopyUniToStr(src->Token[i]);
  	}
  
  	return ret;
  }
  
  // Unicode string copy
  wchar_t *UniCopyStr(wchar_t *str)
  {
  	return CopyUniStr(str);
  }
  
  // Copy the token list
  UNI_TOKEN_LIST *UniCopyToken(UNI_TOKEN_LIST *src)
  {
  	UNI_TOKEN_LIST *ret;
  	UINT i;
  	// Validate arguments
  	if (src == NULL)
  	{
  		return NULL;
  	}
  
  	ret = ZeroMalloc(sizeof(TOKEN_LIST));
  	ret->NumTokens = src->NumTokens;
  	ret->Token = ZeroMalloc(sizeof(wchar_t *) * ret->NumTokens);
  	for (i = 0;i < ret->NumTokens;i++)
  	{
  		ret->Token[i] = CopyUniStr(src->Token[i]);
  	}
  
  	return ret;
  }
  
  // Parse the command line string
  UNI_TOKEN_LIST *UniParseCmdLine(wchar_t *str)
  {
  	UNI_TOKEN_LIST *t;
  	LIST *o;
  	UINT i, len, wp, mode;
  	wchar_t c;
  	wchar_t *tmp;
  	bool ignore_space = false;
  	// Validate arguments
  	if (str == NULL)
  	{
  		// There is no token
  		return UniNullToken();
  	}
  
  	o = NewListFast(NULL);
  	tmp = Malloc(UniStrSize(str) + 32);
  
  	wp = 0;
  	mode = 0;
  
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		c = str[i];
  
  		switch (mode)
  		{
  		case 0:
  			// Mode to discover the next token
  			if (c == L' ' || c == L'\t')
  			{
  				// Advance to the next character
  			}
  			else
  			{
  				// Start of the token
  				if (c == L'\"')
  				{
  					if (str[i + 1] == L'\"')
  					{
  						// Regarded "" as a single " character
  						tmp[wp++] = L'\"';
  						i++;
  					}
  					else
  					{
  						// Single "(double-quote) enables the flag to ignore space
  					ignore_space = true;
  					}
  				}
  				else
  				{
  					tmp[wp++] = c;
  				}
  
  				mode = 1;
  			}
  			break;
  
  		case 1:
  			if (ignore_space == false && (c == L' ' || c == L'\t'))
  			{
  				// End of the token
  				tmp[wp++] = 0;
  				wp = 0;
  
  				Insert(o, UniCopyStr(tmp));
  				mode = 0;
  			}
  			else
  			{
  				if (c == L'\"')
  				{
  					if (str[i + 1] == L'\"')
  					{
  						// Regarded "" as a single " character
  						tmp[wp++] = L'\"';
  						i++;
  					}
  					else
  					{
  						if (ignore_space == false)
  						{
  							// Single "(double-quote) enables the flag to ignore space
  							ignore_space = true;
  						}
  						else
  						{
  							// Disable the flag to ignore space
  							ignore_space = false;
  						}
  					}
  				}
  				else
  				{
  					tmp[wp++] = c;
  				}
  			}
  			break;
  		}
  	}
  
  	if (wp != 0)
  	{
  		tmp[wp++] = 0;
  		Insert(o, UniCopyStr(tmp));
  	}
  
  	Free(tmp);
  
  	t = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  	t->NumTokens = LIST_NUM(o);
  	t->Token = ZeroMalloc(sizeof(wchar_t *) * t->NumTokens);
  	for (i = 0;i < t->NumTokens;i++)
  	{
  		t->Token[i] = LIST_DATA(o, i);
  	}
  
  	ReleaseList(o);
  
  	return t;
  }
  
  // Convert Unicode string to 64bit integer
  UINT64 UniToInt64(wchar_t *str)
  {
  	char tmp[MAX_SIZE];
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	UniToStrForSingleChars(tmp, sizeof(tmp), str);
  
  	return ToInt64(tmp);
  }
  
  // Convert a 64-bit integer to a Unicode string
  void UniToStr64(wchar_t *str, UINT64 value)
  {
  	char tmp[MAX_SIZE];
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	ToStr64(tmp, value);
  
  	StrToUni(str, 0, tmp);
  }
  
  // Convert an ANSI string to UTF
  UINT StrToUtf(char *utfstr, UINT size, char *str)
  {
  	char *tmp;
  	// Validate arguments
  	if (utfstr == NULL || str == NULL)
  	{
  		StrCpy(utfstr, size, "");
  		return 0;
  	}
  
  	tmp = CopyStrToUtf(str);
  
  	StrCpy(utfstr, size, tmp);
  
  	Free(tmp);
  
  	return StrLen(utfstr);
  }
  
  // Convert an UTF string to an ANSI string
  UINT UtfToStr(char *str, UINT size, char *utfstr)
  {
  	char *tmp;
  	// Validate arguments
  	if (str == NULL || utfstr == NULL)
  	{
  		StrCpy(str, size, "");
  		return 0;
  	}
  
  	tmp = CopyUtfToStr(utfstr);
  
  	StrCpy(str, size, tmp);
  
  	Free(tmp);
  
  	return StrLen(str);
  }
  
  // Convert the Unicode string to the UTF string
  UINT UniToUtf(char *utfstr, UINT size, wchar_t *unistr)
  {
  	char *tmp;
  	// Validate arguments
  	if (utfstr == NULL || unistr == NULL)
  	{
  		StrCpy(utfstr, size, "");
  		return 0;
  	}
  
  	tmp = CopyUniToStr(unistr);
  
  	StrCpy(utfstr, size, tmp);
  
  	Free(tmp);
  
  	return StrLen(utfstr);
  }
  
  // Convert the UTF string to a Unicode string
  UINT UtfToUni(wchar_t *unistr, UINT size, char *utfstr)
  {
  	wchar_t *tmp;
  	// Validate arguments
  	if (unistr == NULL || utfstr == NULL)
  	{
  		UniStrCpy(unistr, size, L"");
  		return 0;
  	}
  
  	tmp = CopyUtfToUni(utfstr);
  
  	UniStrCpy(unistr, size, tmp);
  
  	Free(tmp);
  
  	return UniStrLen(unistr);
  }
  
  // Copy the UTF-8 string to a Unicode string
  wchar_t *CopyUtfToUni(char *utfstr)
  {
  	UINT size;
  	wchar_t *ret;
  	UINT utfstr_len;
  	// Validate arguments
  	if (utfstr == NULL)
  	{
  		return NULL;
  	}
  
  	utfstr_len = StrLen(utfstr);
  
  	size = CalcUtf8ToUni((BYTE *)utfstr, utfstr_len);
  	ret = ZeroMalloc(size + sizeof(wchar_t));
  	Utf8ToUni(ret, size, (BYTE *)utfstr, utfstr_len);
  
  	return ret;
  }
  
  // Copy the UTF8 string to the ANSI string
  char *CopyUtfToStr(char *utfstr)
  {
  	wchar_t *uni;
  	char *ret;
  	// Validate arguments
  	if (utfstr == NULL)
  	{
  		return NULL;
  	}
  
  	uni = CopyUtfToUni(utfstr);
  	if (uni == NULL)
  	{
  		return CopyStr("");
  	}
  
  	ret = CopyUniToStr(uni);
  
  	Free(uni);
  
  	return ret;
  }
  
  // Copy a Unicode string to ANSI string
  char *CopyUniToStr(wchar_t *unistr)
  {
  	char *str;
  	UINT str_size;
  	// Validate arguments
  	if (unistr == NULL)
  	{
  		return NULL;
  	}
  
  	str_size = CalcUniToStr(unistr);
  	if (str_size == 0)
  	{
  		return CopyStr("");
  	}
  	str = Malloc(str_size);
  	UniToStr(str, str_size, unistr);
  
  	return str;
  }
  
  // Copy an ANSI string to a Unicode string
  wchar_t *CopyStrToUni(char *str)
  {
  	wchar_t *uni;
  	UINT uni_size;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	uni_size = CalcStrToUni(str);
  	if (uni_size == 0)
  	{
  		return CopyUniStr(L"");
  	}
  	uni = Malloc(uni_size);
  	StrToUni(uni, uni_size, str);
  
  	return uni;
  }
  
  // Copy a Unicode string to UTF-8 string
  char *CopyUniToUtf(wchar_t *unistr)
  {
  	UINT size;
  	char *ret;
  	// Validate arguments
  	if (unistr == NULL)
  	{
  		return NULL;
  	}
  
  	size = CalcUniToUtf8(unistr);
  	ret = ZeroMalloc(size + sizeof(char));
  
  	UniToUtf8((char *)ret, size, unistr);
  
  	return ret;
  }
  
  // Copy ANSI string to UTF8 string
  char *CopyStrToUtf(char *str)
  {
  	wchar_t *unistr;
  	char *ret;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	unistr = CopyStrToUni(str);
  	if (unistr == NULL)
  	{
  		return CopyStr("");
  	}
  
  	ret = CopyUniToUtf(unistr);
  
  	Free(unistr);
  
  	return ret;
  }
  
  // Copy the Unicode string
  wchar_t *CopyUniStr(wchar_t *str)
  {
  	UINT len;
  	wchar_t *dst;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return NULL;
  	}
  
  	len = UniStrLen(str);
  	dst = Malloc((len + 1) * sizeof(wchar_t));
  	UniStrCpy(dst, 0, str);
  
  	return dst;
  }
  
  // Check whether the string is safe
  bool IsSafeUniStr(wchar_t *str)
  {
  	UINT i, len;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return false;
  	}
  
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		if (IsSafeUniChar(str[i]) == false)
  		{
  			return false;
  		}
  	}
  	if (str[0] == L' ')
  	{
  		return false;
  	}
  	if (len != 0)
  	{
  		if (str[len - 1] == L' ')
  		{
  			return false;
  		}
  	}
  	return true;
  }
  
  // Check whether the character is safe
  bool IsSafeUniChar(wchar_t c)
  {
  	UINT i, len;
  	wchar_t *check_str =
  		L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  		L"abcdefghijklmnopqrstuvwxyz"
  		L"0123456789"
  		L" ()-_#%&.";
  
  	len = UniStrLen(check_str);
  	for (i = 0;i < len;i++)
  	{
  		if (c == check_str[i])
  		{
  			return true;
  		}
  	}
  	return false;
  }
  
  // Convert an UTF-8 string to an ANSI string
  UINT Utf8ToStr(char *str, UINT str_size, BYTE *u, UINT size)
  {
  	UINT ret, uni_size;
  	wchar_t *tmp;
  	// Validate arguments
  	if (u == NULL || str == NULL)
  	{
  		return 0;
  	}
  
  	// Convert to Unicode
  	uni_size = CalcUtf8ToUni(u, size);
  	if (uni_size == 0)
  	{
  		if (str_size >= 1)
  		{
  			StrCpy(str, 0, "");
  			return 0;
  		}
  	}
  	tmp = Malloc(uni_size);
  	Utf8ToUni(tmp, uni_size, u, size);
  
  	// Convert to ANSI
  	ret = UniToStr(str, str_size, tmp);
  	Free(tmp);
  
  	return ret;
  }
  
  // Get the size required when UTF-8 string is converted to ANSI string
  UINT CalcUtf8ToStr(BYTE *u, UINT size)
  {
  	UINT ret, uni_size;
  	wchar_t *tmp;
  	// Validate arguments
  	if (u == NULL)
  	{
  		return 0;
  	}
  
  	// Convert to Unicode
  	uni_size = CalcUtf8ToUni(u, size);
  	if (uni_size == 0)
  	{
  		return 0;
  	}
  	tmp = Malloc(uni_size);
  	Utf8ToUni(tmp, uni_size, u, size);
  
  	// Convert to ANSI
  	ret = CalcUniToStr(tmp);
  	Free(tmp);
  
  	return ret;
  }
  
  // Convert an ANSI string to UTF-8 string
  UINT StrToUtf8(BYTE *u, UINT size, char *str)
  {
  	UINT ret, uni_size;
  	wchar_t *tmp;
  	// Validate arguments
  	if (u == NULL || str == NULL)
  	{
  		return 0;
  	}
  
  	// Convert to Unicode
  	uni_size = CalcStrToUni(str);
  	if (uni_size == 0)
  	{
  		return 0;
  	}
  	tmp = Malloc(uni_size);
  	StrToUni(tmp, uni_size, str);
  
  	// Convert to UTF-8
  	ret = UniToUtf8(u, size, tmp);
  
  	Free(tmp);
  
  	return ret;
  }
  
  // Get the required buffer size to convert an ANSI string to an UTF-8 string
  UINT CalcStrToUtf8(char *str)
  {
  	UINT ret;
  	UINT uni_size;
  	wchar_t *tmp;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	// Convert to Unicode
  	uni_size = CalcStrToUni(str);
  	if (uni_size == 0)
  	{
  		return 0;
  	}
  	tmp = Malloc(uni_size);
  	StrToUni(tmp, uni_size, str);
  
  	// Get the size as it was converted to UTF-8
  	ret = CalcUniToUtf8(tmp);
  	Free(tmp);
  
  	return ret;
  }
  
  // Convert Unicode string to ANSI string
  UINT UniToStr(char *str, UINT size, wchar_t *s)
  {
  #ifdef	OS_WIN32
  	UINT ret;
  	char *tmp;
  	UINT new_size;
  	// Validate arguments
  	if (s == NULL || str == NULL)
  	{
  		return 0;
  	}
  
  	new_size = CalcUniToStr(s);
  	if (new_size == 0)
  	{
  		if (size >= 1)
  		{
  			StrCpy(str, 0, "");
  		}
  		return 0;
  	}
  	tmp = Malloc(new_size);
  	tmp[0] = 0;
  	wcstombs(tmp, s, new_size);
  	tmp[new_size - 1] = 0;
  	ret = StrCpy(str, size, tmp);
  	Free(tmp);
  
  	return ret;
  #else	// OS_WIN32
  	return UnixUniToStr(str, size, s);
  #endif	// OS_WIN32
  }
  
  // Get the required number of bytes to convert Unicode string to the ANSI string
  UINT CalcUniToStr(wchar_t *s)
  {
  #ifdef	OS_WIN32
  	UINT ret;
  	// Validate arguments
  	if (s == NULL)
  	{
  		return 0;
  	}
  
  	ret = (UINT)wcstombs(NULL, s, UniStrLen(s));
  	if (ret == (UINT)-1)
  	{
  		return 0;
  	}
  
  	return ret + 1;
  #else	// OS_WIN32
  	return UnixCalcUniToStr(s);
  #endif	// OS_WIN32
  }
  
  // Converted an ANSI string to a Unicode string
  UINT StrToUni(wchar_t *s, UINT size, char *str)
  {
  #ifdef	OS_WIN32
  	UINT ret;
  	wchar_t *tmp;
  	UINT new_size;
  	// Validate arguments
  	if (s == NULL || str == NULL)
  	{
  		return 0;
  	}
  
  	new_size = CalcStrToUni(str);
  	if (new_size == 0)
  	{
  		if (size >= 2)
  		{
  			UniStrCpy(s, 0, L"");
  		}
  		return 0;
  	}
  	tmp = Malloc(new_size);
  	tmp[0] = 0;
  	mbstowcs(tmp, str, StrLen(str));
  	tmp[(new_size - 1) / sizeof(wchar_t)] = 0;
  	ret = UniStrCpy(s, size, tmp);
  	Free(tmp);
  
  	return ret;
  #else	// OS_WIN32
  	return UnixStrToUni(s, size, str);
  #endif	// OS_WIN32
  }
  
  // Get the required buffer size for converting an ANSI string to an Unicode string
  UINT CalcStrToUni(char *str)
  {
  #ifdef	OS_WIN32
  	UINT ret;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	ret = (UINT)mbstowcs(NULL, str, StrLen(str));
  	if (ret == (UINT)-1)
  	{
  		return 0;
  	}
  
  	return (ret + 1) * sizeof(wchar_t);
  #else	// OS_WIN32
  	return UnixCalcStrToUni(str);
  #endif	// OS_WIN32
  }
  
  // Convert the UTF-8 strings to a Unicode string
  UINT Utf8ToUni(wchar_t *s, UINT size, BYTE *u, UINT u_size)
  {
  	UINT i, wp, num;
  	// Validate arguments
  	if (s == NULL || u == NULL)
  	{
  		return 0;
  	}
  	if (size == 0)
  	{
  		size = 0x3fffffff;
  	}
  	if (u_size == 0)
  	{
  		u_size = StrLen((char *)u);
  	}
  
  	i = 0;
  	wp = 0;
  	num = 0;
  	while (true)
  	{
  		UINT type;
  		wchar_t c;
  		BYTE c1, c2;
  
  		type = GetUtf8Type(u, u_size, i);
  		if (type == 0)
  		{
  			break;
  		}
  		switch (type)
  		{
  		case 1:
  			c1 = 0;
  			c2 = u[i];
  			break;
  		case 2:
  			c1 = (((u[i] & 0x1c) >> 2) & 0x07);
  			c2 = (((u[i] & 0x03) << 6) & 0xc0) | (u[i + 1] & 0x3f);
  			break;
  		case 3:
  			c1 = ((((u[i] & 0x0f) << 4) & 0xf0)) | (((u[i + 1] & 0x3c) >> 2) & 0x0f);
  			c2 = (((u[i + 1] & 0x03) << 6) & 0xc0) | (u[i + 2] & 0x3f);
  			break;
  		}
  		i += type;
  
  		c = 0;
  
  		if (IsBigEndian())
  		{
  			if (sizeof(wchar_t) == 2)
  			{
  				((BYTE *)&c)[0] = c1;
  				((BYTE *)&c)[1] = c2;
  			}
  			else
  			{
  				((BYTE *)&c)[2] = c1;
  				((BYTE *)&c)[3] = c2;
  			}
  		}
  		else
  		{
  			((BYTE *)&c)[0] = c2;
  			((BYTE *)&c)[1] = c1;
  		}
  
  		if (wp < ((size / sizeof(wchar_t)) - 1))
  		{
  			s[wp++] = c;
  			num++;
  		}
  		else
  		{
  			break;
  		}
  	}
  
  	if (wp < (size / sizeof(wchar_t)))
  	{
  		s[wp++] = 0;
  	}
  
  	return num;
  }
  
  // Get the buffer size when converted UTF-8 to Unicode
  UINT CalcUtf8ToUni(BYTE *u, UINT u_size)
  {
  	// Validate arguments
  	if (u == NULL)
  	{
  		return 0;
  	}
  	if (u_size == 0)
  	{
  		u_size = StrLen((char *)u);
  	}
  
  	return (Utf8Len(u, u_size) + 1) * sizeof(wchar_t);
  }
  
  // Get the number of characters in UTF-8 string
  UINT Utf8Len(BYTE *u, UINT size)
  {
  	UINT i, num;
  	// Validate arguments
  	if (u == NULL)
  	{
  		return 0;
  	}
  	if (size == 0)
  	{
  		size = StrLen((char *)u);
  	}
  
  	i = num = 0;
  	while (true)
  	{
  		UINT type;
  
  		type = GetUtf8Type(u, size, i);
  		if (type == 0)
  		{
  			break;
  		}
  		i += type;
  		num++;
  	}
  
  	return num;
  }
  
  // Convert an Unicode string to UTF-8 string
  UINT UniToUtf8(BYTE *u, UINT size, wchar_t *s)
  {
  	UINT i, len, type, wp;
  	// Validate arguments
  	if (u == NULL || s == NULL)
  	{
  		return 0;
  	}
  	if (size == 0)
  	{
  		size = 0x3fffffff;
  	}
  
  	len = UniStrLen(s);
  	wp = 0;
  	for (i = 0;i < len;i++)
  	{
  		BYTE c1, c2;
  		wchar_t c = s[i];
  
  		if (IsBigEndian())
  		{
  			if (sizeof(wchar_t) == 2)
  			{
  				c1 = ((BYTE *)&c)[0];
  				c2 = ((BYTE *)&c)[1];
  			}
  			else
  			{
  				c1 = ((BYTE *)&c)[2];
  				c2 = ((BYTE *)&c)[3];
  			}
  		}
  		else
  		{
  			c1 = ((BYTE *)&c)[1];
  			c2 = ((BYTE *)&c)[0];
  		}
  
  		type = GetUniType(s[i]);
  		switch (type)
  		{
  		case 1:
  			if (wp < size)
  			{
  				u[wp++] = c2;
  			}
  			break;
  		case 2:
  			if (wp < size)
  			{
  				u[wp++] = 0xc0 | (((((c1 & 0x07) << 2) & 0x1c)) | (((c2 & 0xc0) >> 6) & 0x03));
  			}
  			if (wp < size)
  			{
  				u[wp++] = 0x80 | (c2 & 0x3f);
  			}
  			break;
  		case 3:
  			if (wp < size)
  			{
  				u[wp++] = 0xe0 | (((c1 & 0xf0) >> 4) & 0x0f);
  			}
  			if (wp < size)
  			{
  				u[wp++] = 0x80 | (((c1 & 0x0f) << 2) & 0x3c) | (((c2 & 0xc0) >> 6) & 0x03);
  			}
  			if (wp < size)
  			{
  				u[wp++] = 0x80 | (c2 & 0x3f);
  			}
  			break;
  		}
  	}
  	if (wp < size)
  	{
  		u[wp] = 0;
  	}
  	return wp;
  }
  
  // Calculating the length of the string when converting Unicode string to UTF-8 string
  UINT CalcUniToUtf8(wchar_t *s)
  {
  	UINT i, len, size;
  	// Validate arguments
  	if (s == NULL)
  	{
  		return 0;
  	}
  
  	size = 0;
  	len = UniStrLen(s);
  	for (i = 0;i < len;i++)
  	{
  		size += GetUniType(s[i]);
  	}
  
  	return size;
  }
  
  // Get the number of bytes of a first character of the offset address of the UTF-8 string that starts with s
  UINT GetUtf8Type(BYTE *s, UINT size, UINT offset)
  {
  	// Validate arguments
  	if (s == NULL)
  	{
  		return 0;
  	}
  	if ((offset + 1) > size)
  	{
  		return 0;
  	}
  	if ((s[offset] & 0x80) == 0)
  	{
  		// 1 byte
  		return 1;
  	}
  	if ((s[offset] & 0x20) == 0)
  	{
  		// 2 bytes
  		if ((offset + 2) > size)
  		{
  			return 0;
  		}
  		return 2;
  	}
  	// 3 bytes
  	if ((offset + 3) > size)
  	{
  		return 0;
  	}
  	return 3;
  }
  
  // Type of the converted character 'c' to UTF-8 (in bytes)
  UINT GetUniType(wchar_t c)
  {
  	BYTE c1, c2;
  
  	if (IsBigEndian())
  	{
  		if (sizeof(wchar_t) == 2)
  		{
  			c1 = ((BYTE *)&c)[0];
  			c2 = ((BYTE *)&c)[1];
  		}
  		else
  		{
  			c1 = ((BYTE *)&c)[2];
  			c2 = ((BYTE *)&c)[3];
  		}
  	}
  	else
  	{
  		c1 = ((BYTE *)&c)[1];
  		c2 = ((BYTE *)&c)[0];
  	}
  
  	if (c1 == 0)
  	{
  		if (c2 <= 0x7f)
  		{
  			// 1 byte
  			return 1;
  		}
  		else
  		{
  			// 2 bytes
  			return 2;
  		}
  	}
  	if ((c1 & 0xf8) == 0)
  	{
  		// 2 bytes
  		return 2;
  	}
  	// 3 bytes
  	return 3;
  }
  
  // String replacing (case-insensitive)
  UINT UniReplaceStri(wchar_t *dst, UINT size, wchar_t *string, wchar_t *old_keyword, wchar_t *new_keyword)
  {
  	return UniReplaceStrEx(dst, size, string, old_keyword, new_keyword, false);
  }
  
  // String replacing (case-sensitive)
  UINT UniReplaceStr(wchar_t *dst, UINT size, wchar_t *string, wchar_t *old_keyword, wchar_t *new_keyword)
  {
  	return UniReplaceStrEx(dst, size, string, old_keyword, new_keyword, true);
  }
  
  // Replacement of string
  UINT UniReplaceStrEx(wchar_t *dst, UINT size, wchar_t *string, wchar_t *old_keyword, wchar_t *new_keyword, bool case_sensitive)
  {
  	UINT i, j, num, len_string, len_old, len_new, len_ret, wp;
  	wchar_t *ret;
  	// Validate arguments
  	if (string == NULL || old_keyword == NULL || new_keyword == NULL)
  	{
  		return 0;
  	}
  
  	// Get the length of the string
  	len_string = UniStrLen(string);
  	len_old = UniStrLen(old_keyword);
  	len_new = UniStrLen(new_keyword);
  
  	// Get the final string length
  	len_ret = UniCalcReplaceStrEx(string, old_keyword, new_keyword, case_sensitive);
  	// Memory allocation
  	ret = Malloc((len_ret + 1) * sizeof(wchar_t));
  	ret[len_ret] = 0;
  
  	// Search and Replace
  	i = j = num = wp = 0;
  	while (true)
  	{
  		i = UniSearchStrEx(string, old_keyword, i, case_sensitive);
  		if (i == INFINITE)
  		{
  			Copy(&ret[wp], &string[j], (len_string - j) * sizeof(wchar_t));
  			wp += len_string - j;
  			break;
  		}
  		num++;
  		Copy(&ret[wp], &string[j], (i - j) * sizeof(wchar_t));
  		wp += i - j;
  		Copy(&ret[wp], new_keyword, len_new * sizeof(wchar_t));
  		wp += len_new;
  		i += len_old;
  		j = i;
  	}
  
  	// Copy of the search results
  	UniStrCpy(dst, size, ret);
  
  	// Memory release
  	Free(ret);
  
  	return num;
  }
  
  // Calculate the length of the result of string replacement
  UINT UniCalcReplaceStrEx(wchar_t *string, wchar_t *old_keyword, wchar_t *new_keyword, bool case_sensitive)
  {
  	UINT i, num;
  	UINT len_string, len_old, len_new;
  	// Validate arguments
  	if (string == NULL || old_keyword == NULL || new_keyword == NULL)
  	{
  		return 0;
  	}
  
  	// Get the length of the string
  	len_string = UniStrLen(string);
  	len_old = UniStrLen(old_keyword);
  	len_new = UniStrLen(new_keyword);
  
  	if (len_old == len_new)
  	{
  		return len_string;
  	}
  
  	// Search process
  	num = 0;
  	i = 0;
  	while (true)
  	{
  		i = UniSearchStrEx(string, old_keyword, i, case_sensitive);
  		if (i == INFINITE)
  		{
  			break;
  		}
  		i += len_old;
  		num++;
  	}
  
  	// Calculation
  	return len_string + len_new * num - len_old * num;
  }
  
  // Search for a string (distinguish between upper / lower case)
  UINT UniSearchStr(wchar_t *string, wchar_t *keyword, UINT start)
  {
  	return UniSearchStrEx(string, keyword, start, true);
  }
  
  // Search for a string (Don't distinguish between upper / lower case)
  UINT UniSearchStri(wchar_t *string, wchar_t *keyword, UINT start)
  {
  	return UniSearchStrEx(string, keyword, start, false);
  }
  
  // Return the position of the first found of the keyword in the string 
  // (Found in first character: returns 0, Not found: returns INFINITE)
  UINT UniSearchStrEx(wchar_t *string, wchar_t *keyword, UINT start, bool case_sensitive)
  {
  	UINT len_string, len_keyword;
  	UINT i;
  	wchar_t *cmp_string, *cmp_keyword;
  	bool found;
  	// Validate arguments
  	if (string == NULL || keyword == NULL)
  	{
  		return INFINITE;
  	}
  
  	// Get the length of string
  	len_string = UniStrLen(string);
  	if (len_string <= start)
  	{
  		// Value of start is invalid
  		return INFINITE;
  	}
  
  	// Get the length of the keyword
  	len_keyword = UniStrLen(keyword);
  	if (len_keyword == 0)
  	{
  		// There is no keyword
  		return INFINITE;
  	}
  
  	if (len_string < len_keyword)
  	{
  		return INFINITE;
  	}
  
  	if (len_string == len_keyword)
  	{
  		if (case_sensitive)
  		{
  			if (UniStrCmp(string, keyword) == 0)
  			{
  				return 0;
  			}
  			else
  			{
  				return INFINITE;
  			}
  		}
  		else
  		{
  			if (UniStrCmpi(string, keyword) == 0)
  			{
  				return 0;
  			}
  			else
  			{
  				return INFINITE;
  			}
  		}
  	}
  
  	if (case_sensitive)
  	{
  		cmp_string = string;
  		cmp_keyword = keyword;
  	}
  	else
  	{
  		cmp_string = Malloc((len_string + 1) * sizeof(wchar_t));
  		UniStrCpy(cmp_string, (len_string + 1) * sizeof(wchar_t), string);
  		cmp_keyword = Malloc((len_keyword + 1) * sizeof(wchar_t));
  		UniStrCpy(cmp_keyword, (len_keyword + 1) * sizeof(wchar_t), keyword);
  		UniStrUpper(cmp_string);
  		UniStrUpper(cmp_keyword);
  	}
  
  	// Search
  	found = false;
  	for (i = start;i < (len_string - len_keyword + 1);i++)
  	{
  		// Compare
  		if (!wcsncmp(&cmp_string[i], cmp_keyword, len_keyword))
  		{
  			// Found
  			found = true;
  			break;
  		}
  	}
  
  	if (case_sensitive == false)
  	{
  		// Memory release
  		Free(cmp_keyword);
  		Free(cmp_string);
  	}
  
  	if (found == false)
  	{
  		return INFINITE;
  	}
  	return i;
  }
  
  // Release of the token list
  void UniFreeToken(UNI_TOKEN_LIST *tokens)
  {
  	UINT i;
  	if (tokens == NULL)
  	{
  		return;
  	}
  	for (i = 0;i < tokens->NumTokens;i++)
  	{
  		Free(tokens->Token[i]);
  	}
  	Free(tokens->Token);
  	Free(tokens);
  }
  
  // Parse token for UNIX
  UNI_TOKEN_LIST *UnixUniParseToken(wchar_t *src, wchar_t *separator)
  {
  	UNI_TOKEN_LIST *ret;
  	TOKEN_LIST *t;
  	char *src_s;
  	char *sep_s;
  
  	// Validate arguments
  	if (src == NULL || separator == NULL)
  	{
  		ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  		ret->Token = ZeroMalloc(0);
  		return ret;
  	}
  
  	src_s = CopyUniToStr(src);
  	sep_s = CopyUniToStr(separator);
  
  	t = ParseToken(src_s, sep_s);
  
  	ret = TokenListToUniTokenList(t);
  	FreeToken(t);
  
  	Free(src_s);
  	Free(sep_s);
  
  	return ret;
  }
  
  // Parse the token
  UNI_TOKEN_LIST *UniParseToken(wchar_t *src, wchar_t *separator)
  {
  #ifdef	OS_WIN32
  	UNI_TOKEN_LIST *ret;
  	wchar_t *tmp;
  	wchar_t *str1, *str2;
  	UINT len, num;
  
  #ifdef	OS_UNIX
  	wchar_t *state = NULL;
  #endif	// OS_UNIX
  
  	// Validate arguments
  	if (src == NULL)
  	{
  		ret = ZeroMalloc(sizeof(UNI_TOKEN_LIST));
  		ret->Token = ZeroMalloc(0);
  		return ret;
  	}
  	if (separator == NULL)
  	{
  		separator = L" .\t\r\n";
  	}
  	len = UniStrLen(src);
  	str1 = Malloc((len + 1) * sizeof(wchar_t));
  	str2 = Malloc((len + 1) * sizeof(wchar_t));
  	UniStrCpy(str1, 0, src);
  	UniStrCpy(str2, 0, src);
  
  	Lock(token_lock);
  	{
  		tmp = wcstok(str1, separator
  #ifdef	OS_UNIX
  			, &state
  #endif	// OS_UNIX
  			);
  		num = 0;
  		while (tmp != NULL)
  		{
  			num++;
  			tmp = wcstok(NULL, separator
  #ifdef	OS_UNIX
  				, &state
  #endif	// OS_UNIX
  				);
  		}
  		ret = Malloc(sizeof(UNI_TOKEN_LIST));
  		ret->NumTokens = num;
  		ret->Token = (wchar_t **)Malloc(sizeof(wchar_t *) * num);
  		num = 0;
  		tmp = wcstok(str2, separator
  #ifdef	OS_UNIX
  			, &state
  #endif	// OS_UNIX
  			);
  		while (tmp != NULL)
  		{
  			ret->Token[num] = (wchar_t *)Malloc((UniStrLen(tmp) + 1) * sizeof(wchar_t));
  			UniStrCpy(ret->Token[num], 0, tmp);
  			num++;
  			tmp = wcstok(NULL, separator
  #ifdef	OS_UNIX
  				, &state
  #endif	// OS_UNIX
  				);
  		}
  	}
  	Unlock(token_lock);
  
  	Free(str1);
  	Free(str2);
  	return ret;
  #else	// OS_WIN32
  	return UnixUniParseToken(src, separator);
  #endif	// OS_WIN32
  }
  
  // Get a line from standard input
  bool UniGetLine(wchar_t *str, UINT size)
  {
  #ifdef	OS_WIN32
  	return UniGetLineWin32(str, size);
  #else	// OS_WIN32
  	return UniGetLineUnix(str, size);
  #endif	// OS_WIN32
  }
  void AnsiGetLineUnix(char *str, UINT size)
  {
  	// Validate arguments
  	if (str == NULL)
  	{
  		char tmp[MAX_SIZE];
  		fgets(tmp, sizeof(tmp) - 1, stdin);
  		return;
  	}
  	if (size <= 1)
  	{
  		return;
  	}
  
  	// Read data from standard input
  	fgets(str, (int)(size - 1), stdin);
  
  	TrimCrlf(str);
  }
  bool UniGetLineUnix(wchar_t *str, UINT size)
  {
  	char *str_a;
  	UINT str_a_size = size;
  	if (str == NULL || size < sizeof(wchar_t))
  	{
  		return false;
  	}
  	if (str_a_size >= 0x7fffffff)
  	{
  		str_a_size = MAX_SIZE;
  	}
  	str_a_size *= 2;
  
  	str_a = ZeroMalloc(str_a_size);
  
  	AnsiGetLineUnix(str_a, str_a_size);
  
  	StrToUni(str, size, str_a);
  
  	Free(str_a);
  
  	return true;
  }
  bool UniGetLineWin32(wchar_t *str, UINT size)
  {
  	bool ret = false;
  
  #ifdef	OS_WIN32
  	ret = Win32InputW(str, size);
  #endif	// OS_WIN32
  
  	return ret;
  }
  
  // Remove '\r\n' at the end
  void UniTrimCrlf(wchar_t *str)
  {
  	UINT len;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  	len = UniStrLen(str);
  	if (len == 0)
  	{
  		return;
  	}
  
  	if (str[len - 1] == L'\n')
  	{
  		if (len >= 2 && str[len - 2] == L'\r')
  		{
  			str[len - 2] = 0;
  		}
  		str[len - 1] = 0;
  	}
  	else if(str[len - 1] == L'\r')
  	{
  		str[len - 1] = 0;
  	}
  }
  
  // Remove white space of the both side of the string
  void UniTrim(wchar_t *str)
  {
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	UniTrimLeft(str);
  	UniTrimRight(str);
  }
  
  // Remove white space on the right side of the string
  void UniTrimRight(wchar_t *str)
  {
  	wchar_t *buf, *tmp;
  	UINT len, i, wp, wp2;
  	bool flag;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  	len = UniStrLen(str);
  	if (len == 0)
  	{
  		return;
  	}
  	if (str[len - 1] != L' ' && str[len - 1] != L'\t')
  	{
  		return;
  	}
  
  	buf = Malloc((len + 1) * sizeof(wchar_t));
  	tmp = Malloc((len + 1) * sizeof(wchar_t));
  	flag = false;
  	wp = wp2 = 0;
  	for (i = 0;i < len;i++)
  	{
  		if (str[i] != L' ' && str[i] != L'\t')
  		{
  			Copy(&buf[wp], tmp, wp2 * sizeof(wchar_t));
  			wp += wp2;
  			wp2 = 0;
  			buf[wp++] = str[i];
  		}
  		else
  		{
  			tmp[wp2++] = str[i];
  		}
  	}
  	buf[wp] = 0;
  	UniStrCpy(str, 0, buf);
  	Free(buf);
  	Free(tmp);
  }
  
  // Remove white space from the left side of the string
  void UniTrimLeft(wchar_t *str)
  {
  	wchar_t *buf;
  	UINT len, i, wp;
  	bool flag;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  	len = UniStrLen(str);
  	if (len == 0)
  	{
  		return;
  	}
  	if (str[0] != L' ' && str[0] != L'\t')
  	{
  		return;
  	}
  
  	buf = Malloc((len + 1) * sizeof(wchar_t));
  	flag = false;
  	wp = 0;
  	for (i = 0;i < len;i++)
  	{
  		if (str[i] != L' ' && str[i] != L'\t')
  		{
  			flag = true;
  		}
  		if (flag)
  		{
  			buf[wp++] = str[i];
  		}
  	}
  	buf[wp] = 0;
  	UniStrCpy(str, 0, buf);
  	Free(buf);
  }
  
  // Convert an integer to a hexadecimal string (8-digit fixed)
  void UniToStrx8(wchar_t *str, UINT i)
  {
  	UniFormat(str, 0, L"0x%08x", i);
  }
  
  // Convert an integer to a hexadecimal string
  void UniToStrx(wchar_t *str, UINT i)
  {
  	UniFormat(str, 0, L"0x%02x", i);
  }
  
  // Convert a signed integer to a string
  void UniToStri(wchar_t *str, int i)
  {
  	UniFormat(str, 0, L"%i", i);
  }
  
  // Convert an integer to a string
  void UniToStru(wchar_t *str, UINT i)
  {
  	UniFormat(str, 0, L"%u", i);
  }
  
  // Convert the string to signed integer
  int UniToInti(wchar_t *str)
  {
  	char tmp[128];
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	UniToStrForSingleChars(tmp, sizeof(tmp), str);
  
  	return ToInt(tmp);
  }
  
  // Convert a string to an integer
  UINT UniToInt(wchar_t *str)
  {
  	char tmp[128];
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	UniToStrForSingleChars(tmp, sizeof(tmp), str);
  
  	return ToInti(tmp);
  }
  
  // Convert only single-byte characters in the Unicode string to a char string
  void UniToStrForSingleChars(char *dst, UINT dst_size, wchar_t *src)
  {
  	UINT i;
  	// Validate arguments
  	if (dst == NULL || src == NULL)
  	{
  		return;
  	}
  
  	for (i = 0;i < UniStrLen(src) + 1;i++)
  	{
  		wchar_t s = src[i];
  		char d;
  
  		if (s == 0)
  		{
  			d = 0;
  		}
  		else if (s <= 0xff)
  		{
  			d = (char)s;
  		}
  		else
  		{
  			d = ' ';
  		}
  
  		dst[i] = d;
  	}
  }
  
  // Format string replacement for 64-bit
  wchar_t *UniReplaceFormatStringFor64(wchar_t *fmt)
  {
  	wchar_t *tmp;
  	wchar_t *ret;
  	UINT tmp_size;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return NULL;
  	}
  
  	tmp_size = UniStrSize(fmt) * 2;
  	tmp = ZeroMalloc(tmp_size);
  
  #ifdef	OS_WIN32
  	UniReplaceStrEx(tmp, tmp_size, fmt, L"%ll", L"%I64", false);
  #else	// OS_WIN32
  	UniReplaceStrEx(tmp, tmp_size, fmt, L"%I64", L"%ll", false);
  
  	if (1)
  	{
  		UINT i, len;
  		bool f = false;
  		len = UniStrLen(tmp);
  		for (i = 0;i < len;i++)
  		{
  			if (tmp[i] == L'%')
  			{
  				f = true;
  			}
  
  			if (f)
  			{
  				switch (tmp[i])
  				{
  				case L'c':
  				case L'C':
  				case L'd':
  				case L'i':
  				case L'o':
  				case L'u':
  				case L'x':
  				case L'X':
  				case L'e':
  				case L'E':
  				case L'f':
  				case L'g':
  				case L'G':
  				case L'n':
  				case L'p':
  				case L's':
  				case L'S':
  					if (tmp[i] == L's')
  					{
  						tmp[i] = L'S';
  					}
  					else if (tmp[i] == L'S')
  					{
  						tmp[i] = L's';
  					}
  					f = false;
  					break;
  				}
  			}
  		}
  	}
  
  #endif	// OS_WIN32
  
  	ret = CopyUniStr(tmp);
  	Free(tmp);
  
  	return ret;
  }
  
  // Get lines from a string
  UNI_TOKEN_LIST *UniGetLines(wchar_t *str)
  {
  	UINT i, len;
  	BUF *b = NULL;
  	LIST *o;
  	UNI_TOKEN_LIST *ret;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return UniNullToken();
  	}
  
  	o = NewListFast(NULL);
  
  	len = UniStrLen(str);
  
  	b = NewBuf();
  
  	for (i = 0;i < len;i++)
  	{
  		wchar_t c = str[i];
  		bool f = false;
  
  		if (c == L'\r')
  		{
  			if (str[i + 1] == L'\n')
  			{
  				i++;
  			}
  			f = true;
  		}
  		else if (c == L'\n')
  		{
  			f = true;
  		}
  
  		if (f)
  		{
  			wchar_t zero = 0;
  			wchar_t *s;
  			WriteBuf(b, &zero, sizeof(wchar_t));
  
  			s = (wchar_t *)b->Buf;
  
  			Add(o, UniCopyStr(s));
  
  			ClearBuf(b);
  		}
  		else
  		{
  			WriteBuf(b, &c, sizeof(wchar_t));
  		}
  	}
  
  	if (true)
  	{
  		wchar_t zero = 0;
  		wchar_t *s;
  		WriteBuf(b, &zero, sizeof(wchar_t));
  
  		s = (wchar_t *)b->Buf;
  
  		Add(o, UniCopyStr(s));
  
  		ClearBuf(b);
  	}
  
  	FreeBuf(b);
  
  	ret = UniListToTokenList(o);
  
  	UniFreeStrList(o);
  
  	return ret;
  }
  
  // Display the string on the screen
  void UniPrintStr(wchar_t *string)
  {
  	// Validate arguments
  	if (string == NULL)
  	{
  		return;
  	}
  
  #ifdef	OS_UNIX
  	if (true)
  	{
  		char *str = CopyUniToStr(string);
  
  		if (str != NULL)
  		{
  			fputs(str, stdout);
  		}
  		else
  		{
  			fputs("", stdout);
  		}
  
  		Free(str);
  	}
  #else	// OS_UNIX
  	Win32PrintW(string);
  #endif	// OS_UNIX
  }
  
  // Display a string with arguments
  void UniPrintArgs(wchar_t *fmt, va_list args)
  {
  	wchar_t *str;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return;
  	}
  
  	str = InternalFormatArgs(fmt, args, false);
  
  	UniPrintStr(str);
  
  	Free(str);
  }
  
  // Display the string
  void UniPrint(wchar_t *fmt, ...)
  {
  	va_list args;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return;
  	}
  
  	va_start(args, fmt);
  	UniPrintArgs(fmt, args);
  	va_end(args);
  }
  
  // Display debug string with arguments
  void UniDebugArgs(wchar_t *fmt, va_list args)
  {
  	if (g_debug == false)
  	{
  		return;
  	}
  
  	UniPrintArgs(fmt, args);
  }
  
  // Display a debug string
  void UniDebug(wchar_t *fmt, ...)
  {
  	va_list args;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return;
  	}
  
  	va_start(args, fmt);
  	UniDebugArgs(fmt, args);
  	va_end(args);
  }
  
  // Format a string (argument list)
  void UniFormatArgs(wchar_t *buf, UINT size, wchar_t *fmt, va_list args)
  {
  	wchar_t *ret;
  	// Validate arguments
  	if (buf == NULL || fmt == NULL)
  	{
  		return;
  	}
  	if (size == 1)
  	{
  		return;
  	}
  
  	// KS
  	KS_INC(KS_FORMAT_COUNT);
  
  	ret = InternalFormatArgs(fmt, args, false);
  
  	UniStrCpy(buf, size, ret);
  
  	Free(ret);
  }
  
  // Format the string, and copy it
  wchar_t *CopyUniFormat(wchar_t *fmt, ...)
  {
  	wchar_t *ret, *str;
  	UINT size;
  	va_list args;
  	// Validate arguments
  	if (fmt == NULL)
  	{
  		return NULL;
  	}
  
  	size = MAX(UniStrSize(fmt) * 10, MAX_SIZE * 10);
  	str = Malloc(size);
  
  	va_start(args, fmt);
  	UniFormatArgs(str, size, fmt, args);
  
  	ret = UniCopyStr(str);
  	Free(str);
  	va_end(args);
  
  	return ret;
  }
  
  // Format the string
  void UniFormat(wchar_t *buf, UINT size, wchar_t *fmt, ...)
  {
  	va_list args;
  	// Validate arguments
  	if (buf == NULL || fmt == NULL)
  	{
  		return;
  	}
  
  	va_start(args, fmt);
  	UniFormatArgs(buf, size, fmt, args);
  	va_end(args);
  }
  
  // Flexible string comparison
  int UniSoftStrCmp(wchar_t *str1, wchar_t *str2)
  {
  	UINT ret;
  	wchar_t *tmp1, *tmp2;
  	// Validate arguments
  	if (str1 == NULL && str2 == NULL)
  	{
  		return 0;
  	}
  	if (str1 == NULL)
  	{
  		return 1;
  	}
  	if (str2 == NULL)
  	{
  		return -1;
  	}
  
  	tmp1 = CopyUniStr(str1);
  	tmp2 = CopyUniStr(str2);
  
  	UniTrim(tmp1);
  	UniTrim(tmp2);
  
  	ret = UniStrCmpi(tmp1, tmp2);
  
  	Free(tmp1);
  	Free(tmp2);
  
  	return ret;
  }
  
  // Compare the strings in case-insensitive mode
  int UniStrCmpi(wchar_t *str1, wchar_t *str2)
  {
  	UINT i;
  	// Validate arguments
  	if (str1 == NULL && str2 == NULL)
  	{
  		return 0;
  	}
  	if (str1 == NULL)
  	{
  		return 1;
  	}
  	if (str2 == NULL)
  	{
  		return -1;
  	}
  
  	// String comparison
  	i = 0;
  	while (true)
  	{
  		wchar_t c1, c2;
  		c1 = UniToUpper(str1[i]);
  		c2 = UniToUpper(str2[i]);
  		if (c1 > c2)
  		{
  			return 1;
  		}
  		else if (c1 < c2)
  		{
  			return -1;
  		}
  		if (str1[i] == 0 || str2[i] == 0)
  		{
  			return 0;
  		}
  		i++;
  	}
  }
  
  // Compare the string
  int UniStrCmp(wchar_t *str1, wchar_t *str2)
  {
  	// Validate arguments
  	if (str1 == NULL && str2 == NULL)
  	{
  		return 0;
  	}
  	if (str1 == NULL)
  	{
  		return 1;
  	}
  	if (str2 == NULL)
  	{
  		return -1;
  	}
  
  	return wcscmp(str1, str2);
  }
  
  // Uncapitalize the string
  void UniStrLower(wchar_t *str)
  {
  	UINT i, len;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		str[i] = UniToLower(str[i]);
  	}
  }
  
  // Capitalize the string
  void UniStrUpper(wchar_t *str)
  {
  	UINT i, len;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return;
  	}
  
  	len = UniStrLen(str);
  	for (i = 0;i < len;i++)
  	{
  		str[i] = UniToUpper(str[i]);
  	}
  }
  
  // Uncapitalize a character
  wchar_t UniToLower(wchar_t c)
  {
  	if (c >= L'A' && c <= L'Z')
  	{
  		c += L'a' - L'A';
  	}
  
  	return c;
  }
  
  // Capitalize a character
  wchar_t UniToUpper(wchar_t c)
  {
  	if (c >= L'a' && c <= L'z')
  	{
  		c -= L'a' - L'A';
  	}
  
  	return c;
  }
  
  // String concatenation
  UINT UniStrCat(wchar_t *dst, UINT size, wchar_t *src)
  {
  	UINT len1, len2, len_test;
  	// Validate arguments
  	if (dst == NULL || src == NULL)
  	{
  		return 0;
  	}
  	if (size != 0 && size < sizeof(wchar_t))
  	{
  		return 0;
  	}
  	if (size == sizeof(wchar_t))
  	{
  		wcscpy(dst, L"");
  		return 0;
  	}
  	if (size == 0)
  	{
  		// Ignore the length
  		size = 0x3fffffff;
  	}
  
  	len1 = UniStrLen(dst);
  	len2 = UniStrLen(src);
  	len_test = len1 + len2 + 1;
  	if (len_test > (size / sizeof(wchar_t)))
  	{
  		if (len2 <= (len_test - (size / sizeof(wchar_t))))
  		{
  			return 0;
  		}
  		len2 -= len_test - (size / sizeof(wchar_t));
  	}
  	Copy(&dst[len1], src, len2 * sizeof(wchar_t));
  	dst[len1 + len2] = 0;
  
  	return len1 + len2;
  }
  UINT UniStrCatLeft(wchar_t *dst, UINT size, wchar_t *src)
  {
  	wchar_t *s;
  	// Validate arguments
  	if (dst == NULL || src == NULL)
  	{
  		return 0;
  	}
  
  	s = UniCopyStr(dst);
  	UniStrCpy(dst, size, s);
  	UniStrCat(dst, size, src);
  	Free(s);
  
  	return UniStrLen(dst);
  }
  
  // String copy
  UINT UniStrCpy(wchar_t *dst, UINT size, wchar_t *src)
  {
  	UINT len;
  	// Validate arguments
  	if (dst == NULL || src == NULL)
  	{
  		if (src == NULL && dst != NULL)
  		{
  			if (size >= sizeof(wchar_t))
  			{
  				dst[0] = L'\0';
  			}
  		}
  		return 0;
  	}
  	if (dst == src)
  	{
  		return UniStrLen(src);
  	}
  	if (size != 0 && size < sizeof(wchar_t))
  	{
  		return 0;
  	}
  	if (size == sizeof(wchar_t))
  	{
  		wcscpy(dst, L"");
  		return 0;
  	}
  	if (size == 0)
  	{
  		// Ignore the length
  		size = 0x3fffffff;
  	}
  
  	// Check the length
  	len = UniStrLen(src);
  	if (len <= (size / sizeof(wchar_t) - 1))
  	{
  		Copy(dst, src, (len + 1) * sizeof(wchar_t));
  	}
  	else
  	{
  		len = size / sizeof(wchar_t) - 1;
  		Copy(dst, src, len * sizeof(wchar_t));
  		dst[len] = 0;
  	}
  
  	return len;
  }
  
  // Check whether the character is within specified buffer size
  bool UniCheckStrSize(wchar_t *str, UINT size)
  {
  	// Validate arguments
  	if (str == NULL || size <= 1)
  	{
  		return false;
  	}
  
  	return UniCheckStrLen(str, size / sizeof(wchar_t) - 1);
  }
  
  // Check whether the number of characters is within specified length
  bool UniCheckStrLen(wchar_t *str, UINT len)
  {
  	UINT count = 0;
  	UINT i;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return false;
  	}
  
  	for (i = 0;;i++)
  	{
  		if (str[i] == 0)
  		{
  			return true;
  		}
  		count++;
  		if (count > len)
  		{
  			return false;
  		}
  	}
  }
  
  // Get the buffer size needed to store the string
  UINT UniStrSize(wchar_t *str)
  {
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	return (UniStrLen(str) + 1) * sizeof(wchar_t);
  }
  
  // Get the length of the string
  UINT UniStrLen(wchar_t *str)
  {
  	UINT i;
  	// Validate arguments
  	if (str == NULL)
  	{
  		return 0;
  	}
  
  	i = 0;
  	while (true)
  	{
  		if (str[i] == 0)
  		{
  			break;
  		}
  		i++;
  	}
  
  	return i;
  }
  
  