text: /*	File: D:\WACKER\tdll\tchar.c (Created: 08-Dec-1993)
   *
   *	Copyright 1994 by Hilgraeve Inc. -- Monroe, MI
   *	All rights reserved
   *
   *	$Revision: 19 $
   *	$Date: 7/08/02 6:49p $
   */
  
  #include <windows.h>
  #pragma hdrstop
  
  #include <tchar.h>
  
  #include "stdtyp.h"
  #include "tdll.h"
  #include "assert.h"
  #include "htchar.h"
  #include "mc.h"
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	TCHAR_Fill
   *
   * DESCRIPTION:
   *	Fills a TCHAR string with the specified TCHAR.
   *
   * ARGUMENTS:
   *	dest	- string to fill.
   *	c		- character to fill string with.
   *	size_t	- number of TCHAR units to copy.
   *
   * RETURNS:
   *	pointer to string.
   *
   */
  TCHAR *TCHAR_Fill(TCHAR *dest, TCHAR c, size_t count)
  	{
  #if defined(UNICODE)
  	int i;
  
  	for (i = 0 ; i < count ; ++i)
  		dest[i] = c;
  
  	return dest;
  
  #else
  
  	return (TCHAR *)memset(dest, c, count);
  
  #endif
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	TCHAR_Trim
   *
   * DESCRIPTION:
   *	This function is called to clean up user input.  It strips all white
   *	space from the front and rear of a string.  Sometimes nothing is left.
   *
   *	NOTE: This won't work on strings > 512 bytes
   *
   * ARGUEMENTS:
   *	pszStr     -- the string to trim
   *
   * RETURNS:
   *	pointer to the string
   */
  TCHAR *TCHAR_Trim(TCHAR *pszStr)
  	{
  	int nExit;
  	TCHAR *pszPtr;
  	TCHAR *pszLast;
  	TCHAR acBuf[512];
  
  	/* Skip the leading white space */
  	for (nExit = FALSE, pszPtr = pszStr; nExit == FALSE; )
  		{
  		switch (*pszPtr)
  			{
  			/* Anything here is considered white space */
  			case 0x20:
  			case 0x9:
  			case 0xA:
  			case 0xB:
  			case 0xC:
  			case 0xD:
  				pszPtr += 1;		/* Skip the white space */
  				break;
  			default:
  				nExit = TRUE;
  				break;
  			}
  		}
  
  	if ((unsigned int)lstrlen(pszPtr) > sizeof(acBuf))
  		{
  		return NULL;
  		}
  
  	lstrcpy(acBuf, pszPtr);
  
  	/* Find the last non white space character */
  	pszPtr = pszLast = acBuf;
  	while (*pszPtr != TEXT('\0'))
  		{
  		switch (*pszPtr)
  			{
  			/* Anything here is considered white space */
  			case 0x20:
  			case 0x9:
  			case 0xA:
  			case 0xB:
  			case 0xC:
  			case 0xD:
  				break;
  			default:
  				pszLast = pszPtr;
  				break;
  			}
  		pszPtr += 1;
  		}
  	pszLast += 1;
  	*pszLast = TEXT('\0');
  
  	lstrcpy(pszStr, acBuf);
  
  	return pszStr;
  	}
  
  #if 0 // Thought I needed this but I didn't.  May be useful someday however.
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	TCHAR_Trunc
   *
   * DESCRIPTION:
   *	Removes trailing space from a character array.	Does not assume
   *
   * ARGUMENTS:
   *	psz - string of characters (null terminated).
   *
   * RETURNS:
   *	Length of truncated string
   *
   */
  int TCHAR_Trunc(const LPTSTR psz)
  	{
  	int i;
  
  	for (i = lstrlen(psz) - 1 ; i > 0 ; --i)
  		{
  		switch (psz[i])
  			{
  		/* Whitespace characters */
  		case TEXT(' '):
  		case TEXT('\t'):
  		case TEXT('\n'):
  		case TEXT('\v'):
  		case TEXT('\f'):
  		case TEXT('\r'):
  			break;
  
  		default:
  			psz[i+1] = TEXT('\0');
  			return i;
  			}
  		}
  
  	return i;
  	}
  #endif
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharNext
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharNext(LPCTSTR pszStr)
  	{
  	LPTSTR pszRet = (LPTSTR)NULL;
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  #if defined(CHAR_MIXED)
  		/* Could be done with 'IsDBCSLeadByte' etc. */
  		pszRet = CharNextExA(0, pszStr, 0);
  #else
  		pszRet = (LPTSTR)pszStr + 1;
  #endif
  		}
  	return pszRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharPrev
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharPrev(LPCTSTR pszStart, LPCTSTR pszStr)
  	{
  	LPTSTR pszRet = (LPTSTR)NULL;
  
  	if ((pszStart != (LPTSTR)NULL) && (pszStr != (LPTSTR)NULL))
  		{
  #if defined(CHAR_MIXED)
  		pszRet = CharPrev(pszStart, pszStr);
  #else
  		if (pszStr > pszStart)
  			pszRet = (LPTSTR)pszStr - 1;
  		else
  			pszRet = (LPTSTR)pszStart;
  #endif
  		}
  
  	return pszRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharLast
   *
   * DESCRIPTION:
   *	Returns a pointer to the last character in a string
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharLast(LPCTSTR pszStr)
  	{
  	LPTSTR pszRet = (LPTSTR)NULL;
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  #if defined(CHAR_MIXED)
  		while (*pszStr != TEXT('\0'))
  			{
  			pszRet = (LPTSTR)pszStr;
  			pszStr = CharNextExA(0, pszStr, 0);
  			}
  #else
  		/* It might be possible to use 'strlen' here. Then again... */
  		// pszRet = pszStr + StrCharGetByteCount(pszStr) - 1;
  		pszRet = (LPTSTR)pszStr + lstrlen(pszStr) - 1;
  #endif
  		}
  	return pszRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharEnd
   *
   * DESCRIPTION:
   *	Returns a pointer to the NULL terminating a string
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharEnd(LPCTSTR pszStr)
  	{
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  #if defined(CHAR_MIXED)
  		while (*pszStr != TEXT('\0'))
  			{
  			pszStr = StrCharNext(pszStr);
  			pszStr += 1;
  			}
  #else
  		pszStr = pszStr + lstrlen(pszStr);
  #endif
  		}
  	return (LPTSTR)pszStr;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharFindFirst
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharFindFirst(LPCTSTR pszStr, int nChar)
  	{
  #if defined(CHAR_MIXED)
  	WORD *pszW;
  #endif
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  #if defined(CHAR_MIXED)
  		while (*pszStr != TEXT('\0'))
  			{
  			/*
  			 * NOTE: this may not work for UNICODE
  			 */
  			if (nChar > 0xFF)
  				{
  				/* Two byte character */
  				if (IsDBCSLeadByte(*pszStr))
  					{
  					pszW = (WORD *)pszStr;
  					if (*pszW == (WORD)nChar)
  						return (LPTSTR)pszStr;
  					}
  				}
  			else
  				{
  				/* Single byte character */
  				if (*pszStr == (TCHAR)nChar)
  					return (LPTSTR)pszStr;
  				}
  			pszStr = CharNextExA(0, pszStr, 0);
  			}
  #else
  		while (pszStr && (*pszStr != TEXT('\0')) )
  			{
  			if (*pszStr == (TCHAR)nChar)
  				return (LPTSTR)pszStr;
  
  			pszStr = StrCharNext(pszStr);
  			}
  #endif
  		}
  	return (LPTSTR)NULL;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharFindLast
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharFindLast(LPCTSTR pszStr, int nChar)
  	{
  	LPTSTR pszRet = (LPTSTR)NULL;
  #if defined(CHAR_MIXED)
  	WORD *pszW;
  #else
  	LPTSTR pszEnd;
  #endif
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  #if defined(CHAR_MIXED)
  		while (*pszStr != TEXT('\0'))
  			{
  			/*
  			 * NOTE: this may not work for UNICODE
  			 */
  			if (nChar > 0xFF)
  				{
  				/* Two byte character */
  				if (IsDBCSLeadByte(*pszStr))
  					{
  					pszW = (WORD *)pszStr;
  					if (*pszW == (WORD)nChar)
  						pszRet = (LPTSTR)pszStr;
  					}
  				}
  			else
  				{
  				/* Single byte character */
  				if (*pszStr == (TCHAR)nChar)
  					pszRet = (LPTSTR)pszStr;
  				}
  			pszStr = CharNextExA(0, pszStr, 0);
  			}
  #else
  		pszEnd = StrCharLast(pszStr);
  		while (pszEnd && (pszEnd > pszStr) )
  			{
  			if (*pszEnd == (TCHAR)nChar)
  				{
  				pszRet = (LPTSTR)pszEnd;
  				break;
  				}
  			pszEnd = StrCharPrev(pszStr, pszEnd);
  			}
  #endif
  		}
  	return pszRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharGetStrLength
   *
   * DESCRIPTION:
   *	This function returns the number of characters in a string.  A two byte
   *	character counts as one.
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharGetStrLength(LPCTSTR pszStr)
  	{
  	int nRet = 0;
  
  #if DEADWOOD
  #if defined(CHAR_MIXED)
  	if (pszStr != (LPTSTR)NULL)
  		{
  		while (*pszStr != TEXT('\0'))
  			{
  			nRet++;
  			pszStr = CharNextExA(0, pszStr, 0);
  			}
  		}
  #else
  	if (pszStr != (LPTSTR)NULL)
  		{
  		nRet = lstrlen(pszStr);
  		}
  #endif
  #else // DEADWOOD
  	if (pszStr != (LPTSTR)NULL)
  		{
  		nRet = lstrlen(pszStr);
  		}
  #endif // DEADWOOD
  	return nRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharGetByteCount
   *
   * DESCRIPTION:
   *	This function returns the number of bytes in a string.  A two byte char
   *	counts as two.
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharGetByteCount(LPCTSTR pszStr)
  	{
  	int nRet = 0;
  #if defined(CHAR_MIXED)
  	LPCTSTR pszFoo;
  
  	if (pszStr != (LPTSTR)NULL)
  		{
  		pszFoo = pszStr;
  		while (*pszFoo != TEXT('\0'))
  			{
  			pszFoo = CharNextExA(0, pszFoo, 0);
  			}
  		nRet = (int)(pszFoo - pszStr);
  		}
  #else
  	if (pszStr != (LPTSTR)NULL)
  		{
  		nRet = lstrlen(pszStr);
  		}
  #endif
  	return nRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharCopy
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharCopy(LPTSTR pszDst, LPCTSTR pszSrc)
  	{
  	return lstrcpy(pszDst, pszSrc);
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharCat
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharCat(LPTSTR pszDst, LPCTSTR pszSrc)
  	{
  	return lstrcat(pszDst, pszSrc);
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharCmp
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharCmp(LPCTSTR pszA, LPCTSTR pszB)
  	{
  	return lstrcmp(pszA, pszB);
  	}
  int StrCharCmpi(LPCTSTR pszA, LPCTSTR pszB)
  	{
  	return lstrcmpi(pszA, pszB);
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharCmpN
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharCmpN(LPCTSTR pszA, LPCTSTR pszB, size_t iLen)
  	{
  	return _tcsncmp(pszA, pszB, iLen);
  	}
  int StrCharCmpiN(LPCTSTR pszA, LPCTSTR pszB, size_t iLen)
  	{
  	return _tcsnicmp(pszA, pszB, iLen);
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharStrStr
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  LPTSTR StrCharStrStr(LPCTSTR pszA, LPCTSTR pszB)
  	{
  	LPTSTR pszRet = (LPTSTR)NULL;
  	int nSize;
  	int nRemaining;
  	LPTSTR pszPtr;
  
  	/*
  	 * We need to write a version of 'strstr' that will work.
  	 * Do we really know what the problems are ?
  	 */
  	nSize = StrCharGetByteCount(pszB);
  
  	pszPtr = (LPTSTR)pszA;
  	while ((nRemaining = StrCharGetByteCount(pszPtr)) >= nSize)
  		{
  		if (memcmp(pszPtr, pszB, (size_t)nSize) == 0)
  			return pszPtr;
  		pszPtr = StrCharNext(pszPtr);
  		}
  	return pszRet;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	StrCharCopyN
   *
   * DESCRIPTION:
   *  Basically do a lstrcpy of n bytes, with one exception, we make sure that
   *	the copied string does not end in a lead-byte of a double-byte character.
   *
   * ARGUMENTS:
   *  pszDst - pointer to the copy target string.
   *	pszSrc - pointer to the copy source string.
   *	iLen   - the maximum number of TCHARs to copy.  Like strcpyn, the
  			 string may not be null terminated if the buffer is exceeded.
   *
   * RETURNS:
   *	0=error, else pszDst
   *
   */
  LPTSTR StrCharCopyN(LPTSTR pszDst, LPCTSTR pszSrc, int iLen)
  	{
  	int    i = 0;
      int    iCounter = iLen * sizeof(TCHAR);    // Use a temporary character counter.
  	LPCTSTR psz = pszSrc;
  
  	if (pszDst == 0 || pszSrc == 0 || iLen == 0 || iCounter == 0)
  		return 0;
  
  	while (1)
  		{
  		i = (int)(StrCharNext(psz) - psz);
  		iCounter -= i;
  
  		if (iCounter <= 0)
  			break;
  
  		if (*psz == TEXT('\0'))
  			{
              //
              // Since StrCharNext() will return the pointer to the
              // terminating null character if at the end of the string,
              // so just increment to the next address location so we
              // have the correct number of bytes to copy (excluding
              // the terminating NULL character).  We NULL terminate
              // the string at the end of this function, so we don't
              // have to copy the NULL character. REV: 12/28/2000.
              //
  			psz += 1;	// still need to increment
  			break;
  			}
  
  		psz += i;
  		}
  
      //
      // Make sure we don't overwrite memory. REV: 12/28/2000.
      //
      i = min((LONG)((psz - pszSrc) + sizeof(TCHAR)), iLen * (int)sizeof(TCHAR));
  
  	MemCopy(pszDst, pszSrc, i);
  
      //
      // Make sure the string is null terminated. REV: 12/28/2000.
      //
      pszDst[(i / sizeof(TCHAR)) - 1] = TEXT('\0');
  
  	return pszDst;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharPBrk
   *
   * DESCRIPTION:
   *	Searches a string for the first occurrence of a character contained in a
   *	specified buffer. This search does not include the null terminator. 
   *
   * PARAMETERS:
   *	pszStr - Address of the string to be searched. 
   *	pszSet - Address of a null-terminated character buffer that contains the 
   *		characters for which to search. 
   *
   * RETURNS:
   *	Returns the address in psz of the first occurrence of a character 
   *	contained in the buffer at pszSet, or NULL if no match is found. 
   */
  LPTSTR StrCharPBrk(LPCTSTR pszStr, LPCTSTR pszSet)
  	{
  	LPCTSTR psz = pszSet;
  	LPTSTR pszRetVal = NULL;
  	
  	while (*psz != TEXT('\0'))
  		{
  		if ((pszRetVal = StrCharFindFirst(pszStr, (int)(*psz))) != NULL)
  			break;
  		psz++;
  		}
  
  	return pszRetVal;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	CnvrtMBCStoECHAR
   *
   * DESCRIPTION:
   *	Converts a DBCS (mixed byte) string into an ECHAR (double byte) string.
   *
   * PARAMETERS:
   *	tchrSource   - Source String
   *  ulDestSize   - Length of Destination String in Bytes
   *  echrDest     - Destination String
   *  ulSourceSize - Length of Destination String in Bytes
   *
   * RETURNS:
   *	0 - Success
   *  1 - Error
   */
  int CnvrtMBCStoECHAR(ECHAR * echrDest, const unsigned long ulDestSize, const TCHAR * const tchrSource, const unsigned long ulSourceSize)
  	{
  	ULONG ulLoop      = 0;
  	ULONG ulDestCount = 0;
  	ULONG ulDestEChars = ulDestSize / sizeof(ECHAR);
  	BOOL fLeadByteFound = FALSE;
  
  	if ((echrDest == NULL) || (tchrSource == NULL))
  		{
  		assert(FALSE);
  		return TRUE;					
  		}
  
  	// Make sure that the destination string is big enough to handle to source string
  	if (ulDestEChars < ulSourceSize)
  		{
  		assert(FALSE);
  		return 1;
  		}
  
  
  #if defined(CHAR_MIXED)
  	// because we do a strcpy in the NARROW version of this function,
  	// and we want the behavior to be the save between the two.  We
  	// clear out the string, just like strcpy does
      memset(echrDest, 0, ulDestSize);
  
  	for (ulLoop = 0; ulLoop < ulSourceSize; ulLoop++)
  		{
  		if ((IsDBCSLeadByte(tchrSource[ulLoop])) && (!fLeadByteFound))
  			// If we found a lead byte, and the last one was not a lead
  			// byte.  We load the byte into the top half of the ECHAR
  			{
  			echrDest[ulDestCount] = (tchrSource[ulLoop] & 0x00FF);
  			echrDest[ulDestCount] = (ECHAR)(echrDest[ulDestCount] << 8);
  			fLeadByteFound = TRUE;
  			}
  		else if (fLeadByteFound)
  			{
  			// If the last byte was a lead byte, we or it into the
  			// bottom half of the ECHAR
  			echrDest[ulDestCount] |= (tchrSource[ulLoop] & 0x00FF);
  			fLeadByteFound = FALSE;
  			ulDestCount++;
  			}
  		else
  			{
  			// Otherwise we load the byte into the bottom half of the
  			// ECHAR and clear the top half.
  			echrDest[ulDestCount] = (tchrSource[ulLoop] & 0x00FF);
  			ulDestCount++;
  			}
  		}
  #else
  	// ECHAR is only a byte, so do a straight string copy.
      if (ulSourceSize)
          MemCopy(echrDest, tchrSource, ulSourceSize);
  #endif
  	return 0;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	CnvrtECHARtoMBCS
   *
   * DESCRIPTION:
   *	Converts an ECHAR (double byte) string into a DBCS (mixed byte) string.
   *
   * PARAMETERS:
   *	echrSource   - Source String
   *  ulDestSize   - Length of Destination String in Bytes
   *  tchrDest     - Destination String
   *
   * RETURNS:
   *	Number of bytes in the converted string
   *  1 - Error
   */
  int CnvrtECHARtoMBCS(TCHAR * tchrDest, const unsigned long ulDestSize, const ECHAR * const echrSource, const unsigned long ulSourceSize)
  	{
  	ULONG ulLoop      = 0;
  	ULONG ulDestCount = 0;
  	ULONG ulSourceEChars = ulSourceSize / sizeof(ECHAR);
      #if defined(INCL_VTUTF8)
      extern BOOL DoUTF8;
      #endif
  
  	if ((tchrDest == NULL) || (echrSource == NULL))
  		{
  		assert(FALSE);
  		return TRUE;
  		}
  
      #if defined(CHAR_MIXED)
  	// because we do a strcpy in the NARROW version of this function,
  	// and we want the behavior to be the save between the two.  We
  	// clear out the string, just like strcpy does
      memset(tchrDest, 0, ulDestSize);
  
  	// We can't do a strlen of an ECHAR string, so we loop
  	// until we hit NULL or we are over the size of the destination.
  	while ((ulLoop < ulSourceEChars) && (ulDestCount <= ulDestSize))
  		{
  		if (echrSource[ulLoop] & 0xFF00)
  			// Lead byte in this character, load the lead byte into one
  			// TCHAR and the lower byte into a second TCHAR.
  			{
  			tchrDest[ulDestCount] = (TCHAR)((echrSource[ulLoop] & 0xFF00) >> 8);
  			ulDestCount++;
  			tchrDest[ulDestCount] = (TCHAR)(echrSource[ulLoop] & 0x00FF);
  			}
  		else
  			// No lead byte in this ECHAR, just load the lower half into
  			// the TCHAR.
  			{
  			tchrDest[ulDestCount] = (TCHAR)(echrSource[ulLoop] & 0x00FF);
  			}
  		ulDestCount++;
  		ulLoop++;
  		if(ulDestCount > ulDestSize)
  			assert(FALSE);
  		}
  
  	return ulDestCount;
      #else
  	// ECHAR is only a byte, so do a straight string copy.
      if (ulSourceSize)
  	    MemCopy(tchrDest, echrSource, ulSourceSize);
  	return ulSourceSize;
      #endif
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharGetEcharLen
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharGetEcharLen(const ECHAR * const pszA)
  	{
  	int nReturn = 0;
  
  	if (pszA == NULL)
  		{
  		assert(FALSE);
  		return nReturn;
  		}
  
  #if defined(CHAR_MIXED)
  	while (pszA[nReturn] != ETEXT('\0'))
  		{
  		nReturn++;
  		}
  
  #else
  	nReturn = strlen(pszA);
  #endif
  
      return nReturn;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharGetEcharByteCount
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharGetEcharByteCount(const ECHAR * const pszA)
  	{
  #if defined(CHAR_MIXED)
  	int nLoop = 0;
  #endif
  	if (pszA == NULL)
  		{
  		assert(FALSE);
  		return 0;
  		}
  
  #if defined(CHAR_MIXED)
  	while (pszA[nLoop] != 0)
  		{
  		nLoop++;
  		}
  
  	nLoop *= (int)sizeof(ECHAR);
  	return nLoop;
  #else
  	return (int)strlen(pszA);
  #endif
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
   * FUNCTION:
   *	StrCharCmpEtoT
   *
   * DESCRIPTION:
   *
   * PARAMETERS:
   *
   * RETURNS:
   */
  int StrCharCmpEtoT(const ECHAR * const pszA, const TCHAR * const pszB)
  	{
  
  #if defined(CHAR_MIXED)
  
  	TCHAR *tpszA = NULL;
  	int    nLenA = StrCharGetEcharLen(pszA);
  
  	tpszA = (TCHAR *)malloc((unsigned int)nLenA * sizeof(ECHAR));
  	if (tpszA == NULL)
  		{
  		assert(FALSE);
  		return 0;
  		}
  
  	CnvrtECHARtoMBCS(tpszA, (unsigned long)(nLenA * (int)sizeof(ECHAR)), pszA, StrCharGetEcharByteCount(pszA));
  
  	return StrCharCmp(tpszA, pszB);
  #else
  	return strcmp(pszA, pszB);
  #endif
  	}
  
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	ECHAR_Fill
   *
   * DESCRIPTION:
   *	Fills a ECHAR string with the specified ECHAR.
   *
   * ARGUMENTS:
   *	dest	- string to fill.
   *	c		- character to fill string with.
   *	size_t	- number of ECHAR units to copy.
   *
   * RETURNS:
   *	pointer to string.
   *
   */
  ECHAR *ECHAR_Fill(ECHAR *dest, ECHAR c, size_t count)
  	{
  #if defined(CHAR_NARROW)
  
  	return (TCHAR *)memset(dest, c, count);
  
  #else
  	unsigned int i;
  
  	if (dest == NULL)
  		{
  		assert(FALSE);
  		return 0;
  		}
  
  	for (i = 0 ; i < count ; ++i)
  		dest[i] = c;
  
  	return dest;
  #endif
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	ECHAR_Fill
   *
   * DESCRIPTION:
   *	
   *
   * ARGUMENTS:
   *	pszDest	- string to fill.
   *	cchDest	- number of TCHAR units to copy.
   *	eChar	- character to fill string with.
   *
   * RETURNS:
   *	pointer to string.
   *
   */
  int CnvrtECHARtoTCHAR(LPTSTR pszDest, int cchDest, ECHAR eChar)
  	{
      #if defined(CHAR_NARROW)
  	pszDest[0] = eChar;
  	pszDest[1] = ETEXT('\0');
  	
      #else
  	memset(pszDest, 0, cchDest*sizeof(*pszDest));
  	// This is the only place where we convert a single ECHAR to TCHAR's
  	// so as of right now we will not make this into a function.
  	if (eChar & 0xFF00)
  		// Lead byte in this character, load the lead byte into one
  		// TCHAR and the lower byte into a second TCHAR.
  		{
  		if (cchDest >= 2)
  			{
  			pszDest[0] = (TCHAR)((eChar & 0xFF00) >> 8);
  			pszDest[1] = (TCHAR)(eChar & 0x00FF);
  			}
  		else
  			{
  			return 1;
  			}
  		}
  	else
  		// No lead byte in this ECHAR, just load the lower half into
  		// the TCHAR.
  		{
  		pszDest[0] = (TCHAR)(eChar & 0x00FF);
  		}
      #endif
  
  	return 0;
  	}
  
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	isDBCSChar
   *
   * DESCRIPTION:
   *	Determines if the Character is Double Byte or not
   *	
   *
   * ARGUMENTS:
   *	c		- character to test.
   *
   * RETURNS:
   *	int 	TRUE  - if DBCS
   *			FALSE - if SBCS
   *
   */
  int isDBCSChar(unsigned int Char)
  	{
  	int rtn = 0;
  #if defined(CHAR_NARROW)
  	rtn = 0;
  
  #else
  	ECHAR ech = 0;
  	char ch;
  
  	if (Char == 0)
  		{
  		// assert(FALSE);
  		return FALSE;
  		}
  
  	ech = ETEXT(Char);
  
  	if (ech & 0xFF00)
  		{
  		ch = (char)(ech >> 8);
  		if (IsDBCSLeadByte(ch))
  			{
  			rtn = 1;
  			}
  
  		}
  #endif
  	return rtn;
  	}
  
  /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   * FUNCTION:
   *	StrCharStripDBCSString
   *
   * DESCRIPTION:
   *	Strips out Left/Right pairs of wide characters and leaves a single wide character
   *	in it's place
   *	
   *
   * ARGUMENTS:
   *	aech		- String to be stripped
   *
   * RETURNS:
   *	int - number of characters striped out of string
   *
   */
  int StrCharStripDBCSString(ECHAR *aechDest, const long lDestSize,
      ECHAR *aechSource)
  	{
  	int nCount = 0;
  #if !defined(CHAR_NARROW)
  	ECHAR *pechTmpS;
  	ECHAR *pechTmpD;
  	long j;
  	long lDLen = lDestSize / sizeof(ECHAR);;
  
  	if ((aechSource == NULL) || (aechDest == NULL))
  		{
  		assert(FALSE);
  		return nCount;
  		}
  
          pechTmpS = aechSource;
          pechTmpD = aechDest;
  
  	for (j = 0; (*pechTmpS != '\0') && (j < lDLen); j++)
  		{
  		*pechTmpD = *pechTmpS;
  
  		if ((isDBCSChar(*pechTmpS)) && (*(pechTmpS + 1) != '\0'))
  			{
  			if (*pechTmpS == *(pechTmpS + 1))
  				{
  				pechTmpS++;
  				nCount++;
  				}
  			}
  		pechTmpS++;
                  pechTmpD++;
  		}
  
  	*pechTmpD = ETEXT('\0');
  #endif
  	return nCount;
  	}
  
  #if defined(DEADWOOD)
  #if defined(INCL_VTUTF8)
  //******************************************************************************
  // Function:	TranslateUTF8ToDBCS
  //
  // Description: 
  //    This function will convert a UTF-8 character to a DBCS character.  If the
  //    character passed is not a full description of a UTF-8 character, then the
  //    character is appended to the UNICODE buffer.
  // Arguments: 
  //    IncomingByte
  //    pUTF8Buffer
  //    iUTF8BufferLength
  //    pUNICODEBuffer
  //    iUNICODEBufferLength
  //    pDBCSBuffer
  //    iDBCSBufferLength
  //
  // Returns: 
  //	
  // Throws:
  //	
  // Author: Ron E. Vorndam,  03/06/2001
  //
  
  BOOLEAN TranslateUTF8ToDBCS(UCHAR  IncomingByte,
                              UCHAR *pUTF8Buffer,
                              int    iUTF8BufferLength,
                              WCHAR *pUNICODEBuffer,
                              int    iUNICODEBufferLength,
                              TCHAR *pDBCSBuffer,
                              int    iDBCSBufferLength)
      {
      BOOLEAN bReturn = FALSE;
      int     iLength = 0;
  
      if (pUTF8Buffer != NULL && iUTF8BufferLength > 0 &&
          pUNICODEBuffer != NULL && iUNICODEBufferLength > 0 &&
          pDBCSBuffer != NULL && iDBCSBufferLength > 0)
          {
          //
          // Translate from UTF8 to UNICODE.
          //
          if (TranslateUtf8ToUnicode(IncomingByte,
                                     pUTF8Buffer,
                                     pUNICODEBuffer) == TRUE)
              {
              //
              // Now Translate the UNICODE to DBCS characters.
              //
              iLength = WideCharToMultiByte(CP_OEMCP,
              //iLength = WideCharToMultiByte(CP_ACP,
                                            0, //WC_COMPOSITECHECK | WC_SEPCHARS,
                                            pUNICODEBuffer, -1, 
                                            NULL, 0, NULL, NULL );
  
              if (iLength > 0 && iDBCSBufferLength >= iLength)
                  {
                  WideCharToMultiByte(CP_OEMCP,
                  //WideCharToMultiByte(CP_ACP,
                                      0, //WC_COMPOSITECHECK | WC_SEPCHARS,
                                      pUNICODEBuffer, -1,
                                      pDBCSBuffer, iLength, NULL, NULL); 
  
                  if (iLength > 0)
                      {
                      bReturn = TRUE;
                      }
                  }
              else
                  {
                  //
                  // Return an error and report the number of bytes required to
                  // make the data conversion.
                  //
                  iDBCSBufferLength = iLength * -1;
                  }
              }
          }
  
      return bReturn;
      }
  
  BOOLEAN TranslateDBCSToUTF8(const TCHAR *pDBCSBuffer,
                                    int    iDBCSBufferLength,
                                    WCHAR *pUNICODEBuffer,
                                    int    iUNICODEBufferLength,
                                    UCHAR *pUTF8Buffer,
                                    int    iUTF8BufferLength)
      {
      BOOLEAN   bReturn = FALSE;
      int       iLength = 0;
  
      //iLength = WideCharToMultiByte(CP_OEMCP,
      iLength = MultiByteToWideChar(CP_ACP,
                                    0, //WC_COMPOSITECHECK | WC_SEPCHARS,
                                    pDBCSBuffer, -1, 
                                    pUNICODEBuffer, iLength);
  
      if (iLength > 0 && iDBCSBufferLength > 0)
          {
          if (pUNICODEBuffer != NULL && iUNICODEBufferLength >= iLength)
              {
              //
              // Translate the DBCS to UNICODE characters.
              //
              //WideCharToMultiByte(CP_OEMCP,
              MultiByteToWideChar(CP_ACP,
                                  0, //MB_COMPOSITE,
                                  pDBCSBuffer, -1,
                                  pUNICODEBuffer, iLength);
  
              if (iLength > 0 && iLength <= iUTF8BufferLength)
                  {
                  //
                  // Translate from UNICODE to UTF8.
                  //
                  bReturn = TranslateUnicodeToUtf8(pUNICODEBuffer,
                                                   pUTF8Buffer);
                  }
              }
          }
  
      return bReturn;
      }
  
  //
  // The following functions are from code obtained directly from
  // Microsoft for converting Unicode to UTF-8 and UTF-8 to unicode
  // buffers. REV: 03/02/2001
  //
  
  BOOLEAN TranslateUnicodeToUtf8(PCWSTR SourceBuffer,
                                 UCHAR  *DestinationBuffer) 
  /*++
  
  Routine Description:
  
      translates a unicode buffer into a UTF8 version.
  
  Arguments:
  
      SourceBuffer - unicode buffer to be translated.
      DestinationBuffer - receives UTF8 version of same buffer.
  
  Return Value:
  
      TRUE - We successfully translated the Unicode value into its
             corresponding UTF8 encoding.
     
      FALSE - The translation failed.
  
  
  --*/
  
  {
      ULONG Count = 0;
  
      //
      // convert into UTF8 for actual transmission
      //
      // UTF-8 encodes 2-byte Unicode characters as follows:
      // If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
      // If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
      // Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
      //
      DestinationBuffer[Count] = (UCHAR)'\0';
      while (*SourceBuffer) {
  
          if( (*SourceBuffer & 0xFF80) == 0 ) {
              //
              // if the top 9 bits are zero, then just
              // encode as 1 byte.  (ASCII passes through unchanged).
              //
              DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0x7F);
          } else if( (*SourceBuffer & 0xF800) == 0 ) {
              //
              // if the top 5 bits are zero, then encode as 2 bytes
              //
              DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 6) & 0x1F) | 0xC0;
              DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
          } else {
              //
              // encode as 3 bytes
              //
              DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 12) & 0xF) | 0xE0;
              DestinationBuffer[Count++] = (UCHAR)((*SourceBuffer >> 6) & 0x3F) | 0x80;
              DestinationBuffer[Count++] = (UCHAR)(*SourceBuffer & 0xBF) | 0x80;
          }
          SourceBuffer += 1;
      }
  
      DestinationBuffer[Count] = (UCHAR)'\0';
  
      return(TRUE);
  
  }
  
  
  
  BOOLEAN TranslateUtf8ToUnicode(UCHAR  IncomingByte,
                                 UCHAR  *ExistingUtf8Buffer,
                                 WCHAR  *DestinationUnicodeVal) 
  /*++
  
  Routine Description:
  
      Takes IncomingByte and concatenates it onto ExistingUtf8Buffer.
      Then attempts to decode the new contents of ExistingUtf8Buffer.
  
  Arguments:
  
      IncomingByte -          New character to be appended onto 
                              ExistingUtf8Buffer.
      
      
      ExistingUtf8Buffer -    running buffer containing incomplete UTF8
                              encoded unicode value.  When it gets full,
                              we'll decode the value and return the
                              corresponding Unicode value.
                              
                              Note that if we *do* detect a completed UTF8
                              buffer and actually do a decode and return a
                              Unicode value, then we will zero-fill the
                              contents of ExistingUtf8Buffer.
      
      
      DestinationUnicodeVal - receives Unicode version of the UTF8 buffer.
  
                              Note that if we do *not* detect a completed
                              UTF8 buffer and thus can not return any data
                              in DestinationUnicodeValue, then we will
                              zero-fill the contents of DestinationUnicodeVal.
  
  
  Return Value:
  
      TRUE - We received a terminating character for our UTF8 buffer and will
             return a decoded Unicode value in DestinationUnicode.
     
      FALSE - We haven't yet received a terminating character for our UTF8
              buffer.
  
  --*/
  
  {
  //    ULONG Count = 0;
      ULONG i = 0;
      BOOLEAN ReturnValue = FALSE;
  
  
      
      //
      // Insert our byte into ExistingUtf8Buffer.
      //
      i = 0;
      do {
          if( ExistingUtf8Buffer[i] == 0 ) {
              ExistingUtf8Buffer[i] = IncomingByte;
              break;
          }
  
          i++;
      } while( i < 3 );
  
      //
      // If we didn't get to actually insert our IncomingByte,
      // then someone sent us a fully-qualified UTF8 buffer.
      // This means we're about to drop IncomingByte.
      //
      // Drop the zero-th byte, shift everything over by one
      // and insert our new character.
      //
      // This implies that we should *never* need to zero out
      // the contents of ExistingUtf8Buffer unless we detect
      // a completed UTF8 packet.  Otherwise, assume one of
      // these cases:
      // 1. We started listening mid-stream, so we caught the
      //    last half of a UTF8 packet.  In this case, we'll
      //    end up shifting the contents of ExistingUtf8Buffer
      //    until we detect a proper UTF8 start byte in the zero-th
      //    position.
      // 2. We got some garbage character, which would invalidate
      //    a UTF8 packet.  By using the logic below, we would
      //    end up disregarding that packet and waiting for
      //    the next UTF8 packet to come in.
      if( i >= 3 ) {
          ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
          ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
          ExistingUtf8Buffer[2] = IncomingByte;
      }
  
  
  
  
  
      //
      // Attempt to convert the UTF8 buffer 
      //
      // UTF8 decodes to Unicode in the following fashion:
      // If the high-order bit is 0 in the first byte:
      //      0xxxxxxx yyyyyyyy zzzzzzzz decodes to a Unicode value of 00000000 0xxxxxxx
      //
      // If the high-order 3 bits in the first byte == 6:
      //      110xxxxx 10yyyyyy zzzzzzzz decodes to a Unicode value of 00000xxx xxyyyyyy
      //
      // If the high-order 3 bits in the first byte == 7:
      //      1110xxxx 10yyyyyy 10zzzzzz decodes to a Unicode value of xxxxyyyy yyzzzzzz
      // 
      
      if( (ExistingUtf8Buffer[0] & 0x80) == 0 ) {        
  
          //
          // First case described above.  Just return the first byte
          // of our UTF8 buffer.
          //
          *DestinationUnicodeVal = (WCHAR)(ExistingUtf8Buffer[0]);
  
          
          //
          // We used 1 byte.  Discard that byte and shift everything
          // in our buffer over by 1.
          //
          ExistingUtf8Buffer[0] = ExistingUtf8Buffer[1];
          ExistingUtf8Buffer[1] = ExistingUtf8Buffer[2];
          ExistingUtf8Buffer[2] = 0;
  
          ReturnValue = TRUE;
  
      } else if( (ExistingUtf8Buffer[0] & 0xE0) == 0xC0 ) {
  
          
          //
          // Second case described above.  Decode the first 2 bytes of
          // of our UTF8 buffer.
          //
          if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
  
              // upper byte: 00000xxx
              *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] >> 2) & 0x07);
              *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  
              // high bits of lower byte: xx000000
              *DestinationUnicodeVal |= ((ExistingUtf8Buffer[0] & 0x03) << 6);
  
              // low bits of lower byte: 00yyyyyy
              *DestinationUnicodeVal |= (ExistingUtf8Buffer[1] & 0x3F);
                                       
  
              //
              // We used 2 bytes.  Discard those bytes and shift everything
              // in our buffer over by 2.
              //
              ExistingUtf8Buffer[0] = ExistingUtf8Buffer[2];
              ExistingUtf8Buffer[1] = 0;
              ExistingUtf8Buffer[2] = 0;
          
              ReturnValue = TRUE;
  
          }
      } else if( (ExistingUtf8Buffer[0] & 0xF0) == 0xE0 ) {
          
          //
          // Third case described above.  Decode the all 3 bytes of
          // of our UTF8 buffer.
          //
  
          if( (ExistingUtf8Buffer[1] & 0xC0) == 0x80 ) {
              
              if( (ExistingUtf8Buffer[2] & 0xC0) == 0x80 ) {
                  
                  // upper byte: xxxx0000
                  *DestinationUnicodeVal = ((ExistingUtf8Buffer[0] << 4) & 0xF0);
  
                  // upper byte: 0000yyyy
                  *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] >> 2) & 0x0F);
  
                  *DestinationUnicodeVal = *DestinationUnicodeVal << 8;
  
                  // lower byte: yy000000
                  *DestinationUnicodeVal |= ((ExistingUtf8Buffer[1] << 6) & 0xC0);
  
                  // lower byte: 00zzzzzz
                  *DestinationUnicodeVal |= (ExistingUtf8Buffer[2] & 0x3F);
              
                  //
                  // We used all 3 bytes.  Zero out the buffer.
                  //
                  ExistingUtf8Buffer[0] = 0;
                  ExistingUtf8Buffer[1] = 0;
                  ExistingUtf8Buffer[2] = 0;
              
                  ReturnValue = TRUE;
  
              }
          }
      }
  
      return ReturnValue;
  }
  #endif //INCL_VTUTF8
  #endif // defined(DEADWOOD)
  