/*++

Copyright (c) 1991-2000,  Microsoft Corporation  All rights reserved.

Module Name:

    ansi.c

Abstract:

    This file contains the ANSI versions of the NLS API functions.

    APIs found in this file:
      CompareStringA
      LCMapStringA
      GetLocaleInfoA
      SetLocaleInfoA
      GetCalendarInfoA
      SetCalendarInfoA
      GetTimeFormatA
      GetDateFormatA
      GetNumberFormatA
      GetCurrencyFormatA
      EnumCalendarInfoA
      EnumCalendarInfoExA
      EnumTimeFormatsA
      EnumDateFormatsA
      EnumDateFormatsExA
      GetStringTypeExA
      GetStringTypeA
      FoldStringA
      EnumSystemLanguageGroupsA
      EnumLanguageGroupLocalesA
      EnumUILanguagesA
      EnumSystemLocalesA
      EnumSystemCodePagesA
      GetCPInfoExA
      GetGeoInfoA


Revision History:

    11-10-93    JulieB    Created.
    07-03-00    lguindon  Began GEO API port

--*/



//
//  Include Files.
//

#include "nls.h"




//
//  Forward Declarations.
//

PCP_HASH
NlsGetACPFromLocale(
    LCID Locale,
    DWORD dwFlags);

BOOL
NlsAnsiToUnicode(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPCSTR pAnsiBuffer,
    int AnsiLength,
    LPWSTR *ppUnicodeBuffer,
    int *pUnicodeLength);

int
NlsUnicodeToAnsi(
    PCP_HASH pHashN,
    LPCWSTR pUnicodeBuffer,
    int UnicodeLength,
    LPSTR pAnsiBuffer,
    int AnsiLength);

BOOL
NlsEnumUnicodeToAnsi(
    PCP_HASH pHashN,
    LPCWSTR pUnicodeBuffer,
    LPSTR *ppAnsiBuffer);





//-------------------------------------------------------------------------//
//                             API ROUTINES                                //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  CompareStringA
//
//  Compares two wide character strings of the same locale according to the
//  supplied locale handle.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI CompareStringA(
    LCID Locale,
    DWORD dwCmpFlags,
    LPCSTR lpString1,
    int cchCount1,
    LPCSTR lpString2,
    int cchCount2)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    WCHAR pSTmp1[MAX_STRING_LEN]; // tmp Unicode buffer (string 1)
    WCHAR pSTmp2[MAX_STRING_LEN]; // tmp Unicode buffer (string 2)
    LPWSTR pUnicode1;             // ptr to unicode string 1
    LPWSTR pUnicode2;             // ptr to unicode string 2
    int UnicodeLength1;           // length of Unicode string 1
    int UnicodeLength2;           // length of Unicode string 2
    int ResultLen;                // result length
    BOOL fUseNegCounts = (cchCount1 < 0 && cchCount2 < 0);    // flag to use negative counts


    //
    //  Invalid Parameter Check:
    //    - Get the code page hash node for the given locale.
    //    - either string is null
    //
    pHashN = NlsGetACPFromLocale(Locale, dwCmpFlags);
    if ((pHashN == NULL) ||
        (lpString1 == NULL) || (lpString2 == NULL))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Invalid Flags Check:
    //    - invalid flags
    //
    if (dwCmpFlags & CS_INVALID_FLAG)
    {
        SetLastError(ERROR_INVALID_FLAGS);
        return (0);
    }

    //
    //  Convert Ansi string 1 to Unicode.
    //
    pUnicode1 = pSTmp1;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpString1,
                           cchCount1,
                           &pUnicode1,
                           &UnicodeLength1 ))
    {
        return (0);
    }

    //
    //  Convert Ansi string 2 to Unicode.
    //
    pUnicode2 = pSTmp2;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpString2,
                           cchCount2,
                           &pUnicode2,
                           &UnicodeLength2 ))
    {
        NLS_FREE_TMP_BUFFER(pUnicode1, pSTmp1);
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    ResultLen = CompareStringW( Locale,
                                dwCmpFlags,
                                pUnicode1,
                                (fUseNegCounts) ? -1 : UnicodeLength1,
                                pUnicode2,
                                (fUseNegCounts) ? -1 : UnicodeLength2 );

    //
    //  Free the allocated source buffers (if they were allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode1, pSTmp1);
    NLS_FREE_TMP_BUFFER(pUnicode2, pSTmp2);

    //
    //  Return the result of the call to CompareStringW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  LCMapStringA
//
//  Maps one wide character string to another performing the specified
//  translation.  This mapping routine only takes flags that are locale
//  dependent.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI LCMapStringA(
    LCID Locale,
    DWORD dwMapFlags,
    LPCSTR lpSrcStr,
    int cchSrc,
    LPSTR lpDestStr,
    int cchDest)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwMapFlags);

    //
    //  Invalid Parameter Check:
    //     - valid code page
    //     - destination buffer size is negative
    //     - length of dest string is NOT zero AND dest string is NULL
    //     - same buffer - src = destination
    //              if not UPPER or LOWER or
    //              UPPER or LOWER used with Japanese flags
    //
    if ((pHashN == NULL) ||
        (cchDest < 0) ||
        ((cchDest != 0) && (lpDestStr == NULL)) ||
        ((lpSrcStr == lpDestStr) &&
         ((!(dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))) ||
          (dwMapFlags & (LCMAP_HIRAGANA | LCMAP_KATAKANA |
                         LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)))))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpSrcStr,
                           cchSrc,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  Special case the sortkey flag, since the Unicode buffer does
    //  NOT need to be converted back to Ansi.
    //
    if (dwMapFlags & LCMAP_SORTKEY)
    {
        //
        //  Call the W version of the API.
        //
        ResultLen = LCMapStringW( Locale,
                                  dwMapFlags,
                                  pUnicode,
                                  UnicodeLength,
                                  (LPWSTR)lpDestStr,
                                  cchDest );

        //
        //  Free the allocated source buffer (if one was allocated).
        //
        NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

        //
        //  Return the result of LCMapStringW.
        //
        return (ResultLen);
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = LCMapStringW( Locale,
                                  dwMapFlags,
                                  pUnicode,
                                  UnicodeLength,
                                  pBuf,
                                  ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = LCMapStringW( Locale,
                                      dwMapFlags,
                                      pUnicode,
                                      UnicodeLength,
                                      NULL,
                                      0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( pHashN,
                                      pBuf,
                                      ResultLen,
                                      lpDestStr,
                                      cchDest );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to LCMapStringW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetLocaleInfoA
//
//  Returns one of the various pieces of information about a particular
//  locale by querying the configuration registry.  This call also indicates
//  how much memory is necessary to contain the desired information.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetLocaleInfoA(
    LCID Locale,
    LCTYPE LCType,
    LPSTR lpLCData,
    int cchData)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Invalid Parameter Check:
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //
    if ((cchData < 0) ||
        (lpLCData == NULL) && (cchData != 0))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetLocaleInfoW( Locale,
                                    LCType,
                                    pBuf,
                                    ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetLocaleInfoW( Locale,
                                        LCType,
                                        NULL,
                                        0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        if ((LCType & LOCALE_RETURN_NUMBER) ||
            (NLS_GET_LCTYPE_VALUE(LCType) == LOCALE_FONTSIGNATURE))
        {
            //
            //  For the font signature and number value, the result length
            //  will actually be twice the amount of the wide char version.
            //
            ResultLen *= 2;

            //
            //  Make sure we can use the buffer.
            //
            if (cchData)
            {
                //
                //  Make sure the buffer is large enough.
                //
                if (cchData < ResultLen)
                {
                    //
                    //  The buffer is too small.
                    //
                    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);
                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
                    return (0);
                }

                //
                //  Convert the font signature or number value to its byte
                //  form.  Since it's already byte reversed, just do a move
                //  memory.
                //
                RtlMoveMemory(lpLCData, pBuf, ResultLen);
            }
        }
        else
        {
            //
            // If this is LCTYPE == LOCALE_SLANGUAGE, then use the
            // CP_ACP of the system. This is what Win9x has done,
            // and we need to be compatible with this.
            //
            if (NLS_GET_LCTYPE_VALUE(LCType) == LOCALE_SLANGUAGE)
            {
                LCType |= LOCALE_USE_CP_ACP;
            }

            //
            //  Get the code page hash node for the given locale.
            //
            pHashN = NlsGetACPFromLocale(Locale, LCType);
            if (pHashN == NULL)
            {
                ResultLen = 0;
            }
            else
            {
                //
                //  Convert to Ansi.
                //
                ResultLen = NlsUnicodeToAnsi( pHashN,
                                              pBuf,
                                              ResultLen,
                                              lpLCData,
                                              cchData );
            }
        }
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetLocaleInfoW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  SetLocaleInfoA
//
//  Sets one of the various pieces of information about a particular
//  locale by making an entry in the user's portion of the configuration
//  registry.  This will only affect the user override portion of the locale
//  settings.  The system defaults will never be reset.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI SetLocaleInfoA(
    LCID Locale,
    LCTYPE LCType,
    LPCSTR lpLCData)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    BOOL Result;                  // result


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, LCType);
    if (pHashN == NULL)
    {
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpLCData,
                           -1,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (FALSE);
    }

    //
    //  Call the W version of the API.
    //
    Result = SetLocaleInfoW( Locale,
                             LCType,
                             pUnicode );

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Return the result of the call to SetLocaleInfoW.
    //
    return (Result);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetCalendarInfoA
//
//  Returns one of the various pieces of information about a particular
//  calendar by querying the configuration registry.  This call also indicates
//  how much memory is necessary to contain the desired information.
//
//  12-17-97    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetCalendarInfoA(
    LCID Locale,
    CALID Calendar,
    CALTYPE CalType,
    LPSTR lpCalData,
    int cchData,
    LPDWORD lpValue)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Invalid Parameter Check:
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //
    if ((cchData < 0) ||
        ((lpCalData == NULL) && (cchData != 0)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Need to check the parameters based on the CAL_RETURN_NUMBER
    //  CalType.
    //
    if (CalType & CAL_RETURN_NUMBER)
    {
        if ((lpCalData != NULL) || (cchData != 0) || (lpValue == NULL))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return (0);
        }
    }
    else
    {
        if ((lpValue != NULL) ||
            (cchData < 0) ||
            ((lpCalData == NULL) && (cchData != 0)))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return (0);
        }
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetCalendarInfoW( Locale,
                                      Calendar,
                                      CalType,
                                      lpCalData ? pBuf : NULL,
                                      lpCalData ? ResultLen : 0,
                                      lpValue );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) ||
            (lpValue != NULL) ||
            (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetCalendarInfoW( Locale,
                                          Calendar,
                                          CalType,
                                          NULL,
                                          0,
                                          NULL ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        if (CalType & CAL_RETURN_NUMBER)
        {
            //
            //  For the number value, the result length will actually be
            //  twice the amount of the wide char version.
            //
            ResultLen *= 2;

            //
            //  There is nothing else to do in this case, since the value
            //  has already been stored in lpValue.
            //
        }
        else
        {
            //
            //  Get the code page hash node for the given locale.
            //
            pHashN = NlsGetACPFromLocale(Locale, CalType);
            if (pHashN == NULL)
            {
                ResultLen = 0;
            }
            else
            {
                //
                //  Convert to Ansi.
                //
                ResultLen = NlsUnicodeToAnsi( pHashN,
                                              pBuf,
                                              ResultLen,
                                              lpCalData,
                                              cchData );
            }
        }
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetCalendarInfoW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  SetCalendarInfoA
//
//  Sets one of the various pieces of information about a particular
//  calendar by making an entry in the user's portion of the configuration
//  registry.  This will only affect the user override portion of the
//  calendar settings.  The system defaults will never be reset.
//
//  12-17-97    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI SetCalendarInfoA(
    LCID Locale,
    CALID Calendar,
    CALTYPE CalType,
    LPCSTR lpCalData)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    BOOL Result;                  // result


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, CalType);
    if (pHashN == NULL)
    {
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpCalData,
                           -1,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (FALSE);
    }

    //
    //  Call the W version of the API.
    //
    Result = SetCalendarInfoW( Locale,
                               Calendar,
                               CalType,
                               pUnicode );

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Return the result of the call to SetCalendarInfoW.
    //
    return (Result);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetTimeFormatA
//
//  Returns a properly formatted time string for the given locale.  It uses
//  either the system time or the specified time.  This call also indicates
//  how much memory is necessary to contain the desired information.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetTimeFormatA(
    LCID Locale,
    DWORD dwFlags,
    CONST SYSTEMTIME *lpTime,
    LPCSTR lpFormat,
    LPSTR lpTimeStr,
    int cchTime)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwFlags);

    //
    //  Invalid Parameter Check:
    //    - valid code page
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //
    if ((pHashN == NULL) ||
        (cchTime < 0) ||
        ((lpTimeStr == NULL) && (cchTime != 0)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpFormat,
                           -1,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetTimeFormatW( Locale,
                                    dwFlags,
                                    lpTime,
                                    pUnicode,
                                    pBuf,
                                    ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetTimeFormatW( Locale,
                                        dwFlags,
                                        lpTime,
                                        pUnicode,
                                        NULL,
                                        0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( pHashN,
                                      pBuf,
                                      ResultLen,
                                      lpTimeStr,
                                      cchTime );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetTimeFormatW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetDateFormatA
//
//  Returns a properly formatted date string for the given locale.  It uses
//  either the system date or the specified date.  The user may specify
//  the short date format, the long date format, or the year/month format.
//  This call also indicates how much memory is necessary to contain the
//  desired information.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetDateFormatA(
    LCID Locale,
    DWORD dwFlags,
    CONST SYSTEMTIME *lpDate,
    LPCSTR lpFormat,
    LPSTR lpDateStr,
    int cchDate)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwFlags);

    //
    //  Invalid Parameter Check:
    //    - valid code page
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //
    if ((pHashN == NULL) ||
        (cchDate < 0) ||
        ((lpDateStr == NULL) && (cchDate != 0)))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Invalid Flags Check:
    //    - flags other than valid ones
    //    - more than one of either ltr reading or rtl reading
    //    - using LTR or RTL flag and not using cp 1255 or 1256
    //
    if ((dwFlags & GDF_INVALID_FLAG) ||
        (MORE_THAN_ONE(dwFlags, GDF_SINGLE_FLAG)) ||
        ((dwFlags & (DATE_LTRREADING | DATE_RTLREADING)) &&
         (pHashN->CodePage != 1255) && (pHashN->CodePage != 1256)))
    {
        SetLastError(ERROR_INVALID_FLAGS);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpFormat,
                           -1,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetDateFormatW( Locale,
                                    dwFlags,
                                    lpDate,
                                    pUnicode,
                                    pBuf,
                                    ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetDateFormatW( Locale,
                                        dwFlags,
                                        lpDate,
                                        pUnicode,
                                        NULL,
                                        0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( pHashN,
                                      pBuf,
                                      ResultLen,
                                      lpDateStr,
                                      cchDate );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetDateFormatW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetNumberFormatA
//
//  Returns a properly formatted number string for the given locale.
//  This call also indicates how much memory is necessary to contain
//  the desired information.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetNumberFormatA(
    LCID Locale,
    DWORD dwFlags,
    LPCSTR lpValue,
    CONST NUMBERFMTA *lpFormat,
    LPSTR lpNumberStr,
    int cchNumber)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPWSTR pValueU;               // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length
    NUMBERFMTW FormatU;           // Unicode number format
    LPNUMBERFMTW pFormatU = NULL; // ptr to Unicode number format


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwFlags);

    //
    //  Invalid Parameter Check:
    //    - valid code page
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //    - ptrs to string buffers same
    //
    if ((pHashN == NULL) ||
        (cchNumber < 0) ||
        ((lpNumberStr == NULL) && (cchNumber != 0)) ||
        (lpValue == lpNumberStr))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pValueU = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpValue,
                           -1,
                           &pValueU,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  If the format structure exists, convert the strings
    //  in the structure.
    //
    if (lpFormat)
    {
        //
        //  Copy Ansi structure to Unicode structure.
        //
        FormatU = *(NUMBERFMTW *)lpFormat;
        FormatU.lpDecimalSep = NULL;
        FormatU.lpThousandSep = NULL;

        //
        //  Convert Ansi strings in structure to Unicode strings.
        //
        if (!NlsAnsiToUnicode( pHashN,
                               0,
                               lpFormat->lpDecimalSep,
                               -1,
                               &(FormatU.lpDecimalSep),
                               &UnicodeLength ) ||
            !NlsAnsiToUnicode( pHashN,
                               0,
                               lpFormat->lpThousandSep,
                               -1,
                               &(FormatU.lpThousandSep),
                               &UnicodeLength ))
        {
            NLS_FREE_TMP_BUFFER(pValueU, pSTmp);
            NLS_FREE_MEM(FormatU.lpDecimalSep);
            return (0);
        }

        pFormatU = &FormatU;
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetNumberFormatW( Locale,
                                      dwFlags,
                                      pValueU,
                                      pFormatU,
                                      pBuf,
                                      ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetNumberFormatW( Locale,
                                          dwFlags,
                                          pValueU,
                                          pFormatU,
                                          NULL,
                                          0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                ResultLen = 0;
                break;
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pValueU, pSTmp);
    if (lpFormat)
    {
        NLS_FREE_MEM(FormatU.lpDecimalSep);
        NLS_FREE_MEM(FormatU.lpThousandSep);
    }

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( pHashN,
                                      pBuf,
                                      ResultLen,
                                      lpNumberStr,
                                      cchNumber );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetNumberFormatW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetCurrencyFormatA
//
//  Returns a properly formatted currency string for the given locale.
//  This call also indicates how much memory is necessary to contain
//  the desired information.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI GetCurrencyFormatA(
    LCID Locale,
    DWORD dwFlags,
    LPCSTR lpValue,
    CONST CURRENCYFMTA *lpFormat,
    LPSTR lpCurrencyStr,
    int cchCurrency)
{
    PCP_HASH pHashN;                   // ptr to CP hash node
    LPWSTR pValueU;                    // ptr to unicode string
    int UnicodeLength;                 // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];       // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];       // tmp Unicode buffer (destination)
    LPWSTR pBuf;                       // ptr to destination buffer
    int ResultLen;                     // result length
    CURRENCYFMTW FormatU;              // Unicode currency format
    LPCURRENCYFMTW pFormatU = NULL;    // ptr to Unicode currency format


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwFlags);

    //
    //  Invalid Parameter Check:
    //    - count is negative
    //    - NULL data pointer AND count is not zero
    //    - ptrs to string buffers same
    //
    if ((pHashN == NULL) ||
        (cchCurrency < 0) ||
        ((lpCurrencyStr == NULL) && (cchCurrency != 0)) ||
        (lpValue == lpCurrencyStr))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pValueU = pSTmp;
    if (!NlsAnsiToUnicode( pHashN,
                           0,
                           lpValue,
                           -1,
                           &pValueU,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  If the format structure exists, convert the strings
    //  in the structure.
    //
    if (lpFormat)
    {
        //
        //  Copy Ansi structure to Unicode structure.
        //
        FormatU = *(CURRENCYFMTW *)lpFormat;
        FormatU.lpDecimalSep = NULL;
        FormatU.lpThousandSep = NULL;
        FormatU.lpCurrencySymbol = NULL;

        //
        //  Convert Ansi strings in structure to Unicode strings.
        //
        if (!NlsAnsiToUnicode( pHashN,
                               0,
                               lpFormat->lpDecimalSep,
                               -1,
                               &(FormatU.lpDecimalSep),
                               &UnicodeLength ) ||
            !NlsAnsiToUnicode( pHashN,
                               0,
                               lpFormat->lpThousandSep,
                               -1,
                               &(FormatU.lpThousandSep),
                               &UnicodeLength ) ||
            !NlsAnsiToUnicode( pHashN,
                               0,
                               lpFormat->lpCurrencySymbol,
                               -1,
                               &(FormatU.lpCurrencySymbol),
                               &UnicodeLength ))
        {
            NLS_FREE_TMP_BUFFER(pValueU, pSTmp);
            NLS_FREE_MEM(FormatU.lpDecimalSep);
            NLS_FREE_MEM(FormatU.lpThousandSep);
            return (0);
        }

        pFormatU = &FormatU;
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = GetCurrencyFormatW( Locale,
                                        dwFlags,
                                        pValueU,
                                        pFormatU,
                                        pBuf,
                                        ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = GetCurrencyFormatW( Locale,
                                            dwFlags,
                                            pValueU,
                                            pFormatU,
                                            NULL,
                                            0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                ResultLen = 0;
                break;
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pValueU, pSTmp);
    if (lpFormat)
    {
        NLS_FREE_MEM(FormatU.lpDecimalSep);
        NLS_FREE_MEM(FormatU.lpThousandSep);
        NLS_FREE_MEM(FormatU.lpCurrencySymbol);
    }

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( pHashN,
                                      pBuf,
                                      ResultLen,
                                      lpCurrencyStr,
                                      cchCurrency );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to GetCurrencyFormatW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumCalendarInfoA
//
//  Enumerates the specified calendar information that is available for the
//  specified locale, based on the CalType parameter.  It does so by
//  passing the pointer to the string buffer containing the calendar info
//  to an application-defined callback function.  It continues until the
//  last calendar info is found or the callback function returns FALSE.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumCalendarInfoA(
    CALINFO_ENUMPROCA lpCalInfoEnumProc,
    LCID Locale,
    CALID Calendar,
    CALTYPE CalType)
{
    return (Internal_EnumCalendarInfo( (NLS_ENUMPROC)lpCalInfoEnumProc,
                                        Locale,
                                        Calendar,
                                        CalType,
                                        FALSE,
                                        FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumCalendarInfoExA
//
//  Enumerates the specified calendar information that is available for the
//  specified locale, based on the CalType parameter.  It does so by
//  passing the pointer to the string buffer containing the calendar info
//  and the calendar id to an application-defined callback function.  It
//  continues until the last calendar info is found or the callback function
//  returns FALSE.
//
//  10-14-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumCalendarInfoExA(
    CALINFO_ENUMPROCEXA lpCalInfoEnumProcEx,
    LCID Locale,
    CALID Calendar,
    CALTYPE CalType)
{
    return (Internal_EnumCalendarInfo( (NLS_ENUMPROC)lpCalInfoEnumProcEx,
                                        Locale,
                                        Calendar,
                                        CalType,
                                        FALSE,
                                        TRUE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumTimeFormatsA
//
//  Enumerates the time formats that are available for the
//  specified locale, based on the dwFlags parameter.  It does so by
//  passing the pointer to the string buffer containing the time format
//  to an application-defined callback function.  It continues until the
//  last time format is found or the callback function returns FALSE.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumTimeFormatsA(
    TIMEFMT_ENUMPROCA lpTimeFmtEnumProc,
    LCID Locale,
    DWORD dwFlags)
{
    return (Internal_EnumTimeFormats( (NLS_ENUMPROC)lpTimeFmtEnumProc,
                                       Locale,
                                       dwFlags,
                                       FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumDateFormatsA
//
//  Enumerates the short date, long date, or year/month formats that are
//  available for the specified locale, based on the dwFlags parameter.
//  It does so by passing the pointer to the string buffer containing the
//  date format to an application-defined callback function.  It continues
//  until the last date format is found or the callback function returns
//  FALSE.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumDateFormatsA(
    DATEFMT_ENUMPROCA lpDateFmtEnumProc,
    LCID Locale,
    DWORD dwFlags)
{
    return (Internal_EnumDateFormats( (NLS_ENUMPROC)lpDateFmtEnumProc,
                                       Locale,
                                       dwFlags,
                                       FALSE,
                                       FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumDateFormatsExA
//
//  Enumerates the short date, long date, or year/month formats that are
//  available for the specified locale, based on the dwFlags parameter.
//  It does so by passing the pointer to the string buffer containing the
//  date format and the calendar id to an application-defined callback
//  function.  It continues until the last date format is found or the
//  callback function returns FALSE.
//
//  10-14-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumDateFormatsExA(
    DATEFMT_ENUMPROCEXA lpDateFmtEnumProcEx,
    LCID Locale,
    DWORD dwFlags)
{
    return (Internal_EnumDateFormats( (NLS_ENUMPROC)lpDateFmtEnumProcEx,
                                       Locale,
                                       dwFlags,
                                       FALSE,
                                       TRUE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetStringTypeExA
//
//  Returns character type information about a particular Ansi string.
//
//  01-18-94    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI GetStringTypeExA(
    LCID Locale,
    DWORD dwInfoType,
    LPCSTR lpSrcStr,
    int cchSrc,
    LPWORD lpCharType)
{
    return (GetStringTypeA( Locale,
                            dwInfoType,
                            lpSrcStr,
                            cchSrc,
                            lpCharType));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetStringTypeA
//
//  Returns character type information about a particular Ansi string.
//
//  NOTE:  The number of parameters is different from GetStringTypeW.
//         The 16-bit OLE product shipped this routine with the wrong
//         parameters (ported from Chicago) and now we must support it.
//
//         Use GetStringTypeEx to get the same set of parameters between
//         the A and W version.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI GetStringTypeA(
    LCID Locale,
    DWORD dwInfoType,
    LPCSTR lpSrcStr,
    int cchSrc,
    LPWORD lpCharType)
{
    PCP_HASH pHashCP;             // ptr to CP hash node
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    BOOL Result;                  // result


    //
    //  Get the code page hash node for the given locale.
    //  This will also return an error if the locale id is invalid,
    //  so there is no need to check the locale id separately.
    //
    pHashCP = NlsGetACPFromLocale(Locale, 0);

    //
    //  Invalid Parameter Check:
    //    - Validate LCID
    //    - valid code page
    //    - same buffer - src and destination
    //
    if ((pHashCP == NULL) ||
        (lpSrcStr == (LPSTR)lpCharType))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }

    //
    //  Convert Ansi string to Unicode.
    //
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( pHashCP,
                           MB_INVALID_CHAR_CHECK,
                           lpSrcStr,
                           cchSrc,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    Result = GetStringTypeW( dwInfoType,
                             pUnicode,
                             UnicodeLength,
                             lpCharType );

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Return the result of the call to GetStringTypeW.
    //
    return (Result);
}


////////////////////////////////////////////////////////////////////////////
//
//  FoldStringA
//
//  Maps one wide character string to another performing the specified
//  translation.  This mapping routine only takes flags that are locale
//  independent.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int WINAPI FoldStringA(
    DWORD dwMapFlags,
    LPCSTR lpSrcStr,
    int cchSrc,
    LPSTR lpDestStr,
    int cchDest)
{
    LPWSTR pUnicode;              // ptr to unicode string
    int UnicodeLength;            // length of Unicode string
    WCHAR pSTmp[MAX_STRING_LEN];  // tmp Unicode buffer (source)
    WCHAR pDTmp[MAX_STRING_LEN];  // tmp Unicode buffer (destination)
    LPWSTR pBuf;                  // ptr to destination buffer
    int ResultLen;                // result length


    //
    //  Invalid Parameter Check:
    //     - dest buffer size is negative
    //     - length of dest string is NOT zero AND dest string is NULL
    //     - same buffer - src = destination
    //
    if ((cchDest < 0) ||
        ((cchDest != 0) && (lpDestStr == NULL)) ||
        (lpSrcStr == lpDestStr))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (0);
    }


    //
    //  Convert Ansi string to Unicode.
    //
    RtlZeroMemory(pSTmp, sizeof(pSTmp));
    pUnicode = pSTmp;
    if (!NlsAnsiToUnicode( gpACPHashN,
                           0,
                           lpSrcStr,
                           cchSrc,
                           &pUnicode,
                           &UnicodeLength ))
    {
        return (0);
    }

    //
    //  Call the W version of the API.
    //
    pBuf = pDTmp;
    ResultLen = MAX_STRING_LEN;
    while (1)
    {
        ResultLen = FoldStringW( dwMapFlags,
                                 pUnicode,
                                 UnicodeLength,
                                 pBuf,
                                 ResultLen );

        //
        //  Make sure the static buffer was large enough.
        //
        if ((ResultLen != 0) || (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        {
            break;
        }

        //
        //  Get the size of the buffer needed for the mapping.
        //
        if (ResultLen = FoldStringW( dwMapFlags,
                                     pUnicode,
                                     UnicodeLength,
                                     NULL,
                                     0 ))
        {
            //
            //  Allocate a buffer of the appropriate size.
            //
            if ((pBuf = (LPWSTR)NLS_ALLOC_MEM(ResultLen * sizeof(WCHAR))) == NULL)
            {
                NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);
                SetLastError(ERROR_OUTOFMEMORY);
                return (0);
            }
        }
    }

    //
    //  Free the allocated source buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pUnicode, pSTmp);

    //
    //  Convert the destination Unicode buffer to the given Ansi buffer.
    //
    if (ResultLen > 0)
    {
        ResultLen = NlsUnicodeToAnsi( gpACPHashN,
                                      pBuf,
                                      ResultLen,
                                      lpDestStr,
                                      cchDest );
    }

    //
    //  Free the allocated destination buffer (if one was allocated).
    //
    NLS_FREE_TMP_BUFFER(pBuf, pDTmp);

    //
    //  Return the result of the call to FoldStringW.
    //
    return (ResultLen);
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumSystemLanguageGroupsA
//
//  Enumerates the system language groups that are installed or supported,
//  based on the dwFlags parameter.  It does so by passing the pointer to
//  the string buffer containing the language group id to an
//  application-defined callback function.  It continues until the last
//  language group id is found or the callback function returns FALSE.
//
//  03-10-98    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumSystemLanguageGroupsA(
    LANGUAGEGROUP_ENUMPROCA lpLanguageGroupEnumProc,
    DWORD dwFlags,
    LONG_PTR lParam)
{
    return (Internal_EnumSystemLanguageGroups(
                                       (NLS_ENUMPROC)lpLanguageGroupEnumProc,
                                       dwFlags,
                                       lParam,
                                       FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumLanguageGroupLocalesA
//
//  Enumerates the locales in a given language group.  It does so by
//  passing the appropriate information to an application-defined
//  callback function.  It continues until the last locale in the language
//  group is found or the callback function returns FALSE.
//
//  03-10-98    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumLanguageGroupLocalesA(
    LANGGROUPLOCALE_ENUMPROCA lpLangGroupLocaleEnumProc,
    LGRPID LanguageGroup,
    DWORD dwFlags,
    LONG_PTR lParam)
{
    return (Internal_EnumLanguageGroupLocales(
                                       (NLS_ENUMPROC)lpLangGroupLocaleEnumProc,
                                       LanguageGroup,
                                       dwFlags,
                                       lParam,
                                       FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumUILanguagesA
//
//  Enumerates the system UI languages that are installed.  It does so by
//  passing the pointer to the string buffer containing the UI language id
//  to an application-defined callback function.  It continues until the
//  last UI language id is found or the callback function returns FALSE.
//
//  03-10-98    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumUILanguagesA(
    UILANGUAGE_ENUMPROCA lpUILanguageEnumProc,
    DWORD dwFlags,
    LONG_PTR lParam)
{
    return (Internal_EnumUILanguages( (NLS_ENUMPROC)lpUILanguageEnumProc,
                                      dwFlags,
                                      lParam,
                                      FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumSystemLocalesA
//
//  Enumerates the system locales that are installed or supported, based on
//  the dwFlags parameter.  It does so by passing the pointer to the string
//  buffer containing the locale id to an application-defined callback
//  function.  It continues until the last locale id is found or the
//  callback function returns FALSE.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumSystemLocalesA(
    LOCALE_ENUMPROCA lpLocaleEnumProc,
    DWORD dwFlags)
{
    return (Internal_EnumSystemLocales( (NLS_ENUMPROC)lpLocaleEnumProc,
                                         dwFlags,
                                         FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  EnumSystemCodePagesA
//
//  Enumerates the system code pages that are installed or supported, based on
//  the dwFlags parameter.  It does so by passing the pointer to the string
//  buffer containing the code page id to an application-defined callback
//  function.  It continues until the last code page is found or the
//  callback function returns FALSE.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI EnumSystemCodePagesA(
    CODEPAGE_ENUMPROCA lpCodePageEnumProc,
    DWORD dwFlags)
{
    return (Internal_EnumSystemCodePages( (NLS_ENUMPROC)lpCodePageEnumProc,
                                          dwFlags,
                                          FALSE ));
}


////////////////////////////////////////////////////////////////////////////
//
//  GetCPInfoExA
//
//  Returns information about a given code page.
//
//  11-15-96    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL WINAPI GetCPInfoExA(
    UINT CodePage,
    DWORD dwFlags,
    LPCPINFOEXA lpCPInfoEx)
{
    CPINFOEXW lpCPInfoExW;
    BOOL rc;


    //
    //  Invalid Parameter Check:
    //     - lpCPInfoEx is NULL
    //
    if (lpCPInfoEx == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (FALSE);
    }

    //
    //  Call the W version of the API.
    //
    rc = GetCPInfoExW(CodePage, dwFlags, &lpCPInfoExW);

    //
    //  Convert the code page name from Unicode to Ansi.
    //
    if (rc == TRUE)
    {
        if (!NlsUnicodeToAnsi( gpACPHashN,
                               lpCPInfoExW.CodePageName,
                               -1,
                               lpCPInfoEx->CodePageName,
                               MAX_PATH ))
        {
            return (FALSE);
        }
    }

    //
    //  Copy the rest of the information from the Unicode buffer to the
    //  Ansi buffer.
    //
    RtlMoveMemory( lpCPInfoEx,
                   &lpCPInfoExW,
                   FIELD_OFFSET(CPINFOEXW, CodePageName) );

    //
    //  Return the result.
    //
    return (rc);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetGeoInfoA
//
//  Wrapper funtion of GetGeoInfoW for ANSI. This function return information
//  about a geographical region.
//
//  11-20-99    WeiWu     Created
//  07-03-00    lguindon  Began GEO API port
////////////////////////////////////////////////////////////////////////////

int WINAPI GetGeoInfoA(
    GEOID GeoId,
    DWORD GeoType,
    LPSTR lpGeoData,
    int cchData,
    LANGID LangId)
{
    int iRet = 0;

    //
    //  Create buffer initialized to zero.
    //
    WCHAR wszBuffer[MAX_REG_VAL_SIZE] = {0};

    //
    //  Sanity check.
    //
    if ((lpGeoData == NULL) && (cchData > 0))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (iRet);
    }

    //
    //  Call the unicode version.
    //
    iRet = GetGeoInfoW( GeoId,
                        GeoType,
                        wszBuffer,
                        sizeof(wszBuffer) / sizeof(WCHAR),
                        LangId );

    //
    //  Convert to ANSI if we get something.
    //
    if (iRet)
    {
        iRet = WideCharToMultiByte( CP_ACP,
                                    0,
                                    wszBuffer,
                                    iRet,
                                    lpGeoData,
                                    cchData,
                                    NULL,
                                    NULL );
    }

    return (iRet);
}





//-------------------------------------------------------------------------//
//                           INTERNAL ROUTINES                             //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  NlsGetACPFromLocale
//
//  Gets the CP hash node for the default ACP of the given locale.  If
//  either the locale or the code page are invalid, then NULL is returned.
//
//  01-19-94    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

PCP_HASH NlsGetACPFromLocale(
    LCID Locale,
    DWORD dwFlags)
{
    PLOC_HASH pHashN;                  // ptr to LOC hash node
    PCP_HASH pHashCP;                  // ptr to CP hash node
    UNICODE_STRING ObUnicodeStr;       // value string
    UINT CodePage;                     // code page value


    //
    //  See if the system ACP should be used.
    //
    if (dwFlags & (LOCALE_USE_CP_ACP | LOCALE_RETURN_NUMBER))
    {
        return (gpACPHashN);
    }

    //
    //  Get the locale hash node.
    //
    VALIDATE_LOCALE(Locale, pHashN, FALSE);
    if (pHashN == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return (NULL);
    }

    //
    //  Get the ACP code page.  If it's equal to CP_ACP (0), then return
    //  the system ACP hash node.
    //
    CodePage = pHashN->pLocaleFixed->DefaultACP;
    if (CodePage == CP_ACP)
    {
        return (gpACPHashN);
    }

    //
    //  Get the CP hash node for the code page.
    //
    pHashCP = GetCPHashNode(CodePage);
    if (pHashCP == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
    }

    //
    //  Return the code page hash node.
    //
    return (pHashCP);
}


////////////////////////////////////////////////////////////////////////////
//
//  NlsAnsiToUnicode
//
//  Converts an Ansi string to a Unicode string.
//
//  NOTE:  The Unicode buffer is allocated if the routine succeeds, so the
//         caller will need to free the buffer when it is no longer needed.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL NlsAnsiToUnicode(
    PCP_HASH pHashN,
    DWORD dwFlags,
    LPCSTR pAnsiBuffer,
    int AnsiLength,
    LPWSTR *ppUnicodeBuffer,
    int *pUnicodeLength)
{
    LPWSTR pUnicode;              // ptr to Unicode buffer
    ULONG UnicodeLength;          // length of the Unicode string
    int ResultLength;             // result length of Unicode string


    //
    //  Make sure the pointer passed in is not null.
    //
    if (pAnsiBuffer == NULL)
    {
        *ppUnicodeBuffer = NULL;
        *pUnicodeLength = 0;
        return (TRUE);
    }

    //
    //  Make sure the Ansi length is set properly (in bytes).
    //
    if (AnsiLength < 0)
    {
        AnsiLength = strlen(pAnsiBuffer) + 1;
    }

    //
    //  See if the static buffer is big enough.
    //
    if ((*ppUnicodeBuffer == NULL) || (AnsiLength > (MAX_STRING_LEN - 1)))
    {
        //
        //  Get the size of the Unicode string, including the
        //  null terminator.
        //
        UnicodeLength = AnsiLength;

        //
        //  Allocate the Unicode buffer.
        //
        if ((pUnicode = (LPWSTR)NLS_ALLOC_MEM(
                            (UnicodeLength + 1) * sizeof(WCHAR) )) == NULL)
        {
            SetLastError(ERROR_OUTOFMEMORY);
            return (FALSE);
        }
    }
    else
    {
        UnicodeLength = MAX_STRING_LEN - 1;
        pUnicode = *ppUnicodeBuffer;
    }

    //
    //  Make sure the length of the Ansi string is not zero.
    //
    if (AnsiLength == 0)
    {
        pUnicode[0] = 0;
        *ppUnicodeBuffer = pUnicode;
        *pUnicodeLength = 0;
        return (TRUE);
    }

    //
    //  Convert the Ansi string to a Unicode string.
    //
    ResultLength = SpecialMBToWC( pHashN,
                                  dwFlags,
                                  pAnsiBuffer,
                                  AnsiLength,
                                  pUnicode,
                                  UnicodeLength );
    if (ResultLength == 0)
    {
        //
        //  Free the allocated Unicode buffer (if one was allocated).
        //
        NLS_FREE_TMP_BUFFER(pUnicode, *ppUnicodeBuffer);

        //
        //  See if the failure was due to insufficient buffer size.
        //
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            //  Get the size of the buffer needed to hold the
            //  Unicode string.
            //
            UnicodeLength = SpecialMBToWC( pHashN,
                                           dwFlags,
                                           pAnsiBuffer,
                                           AnsiLength,
                                           NULL,
                                           0 );
            //
            //  Allocate the Unicode buffer.
            //
            if ((pUnicode = (LPWSTR)NLS_ALLOC_MEM(
                                (UnicodeLength + 1) * sizeof(WCHAR) )) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                return (FALSE);
            }

            //
            //  Try the translation again.
            //
            ResultLength = SpecialMBToWC( pHashN,
                                          dwFlags,
                                          pAnsiBuffer,
                                          AnsiLength,
                                          pUnicode,
                                          UnicodeLength );
        }

        //
        //  If there was still an error, return failure.
        //
        if (ResultLength == 0)
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return (FALSE);
        }
    }

    //
    //  Make sure there is room in the buffer for the null terminator.
    //
    ASSERT(ResultLength <= (int)UnicodeLength);

    //
    //  Null terminate the string.
    //
    pUnicode[ResultLength] = UNICODE_NULL;

    //
    //  Return the Unicode buffer and success.
    //
    *ppUnicodeBuffer = pUnicode;
    *pUnicodeLength = ResultLength;
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  NlsUnicodeToAnsi
//
//  Converts a Unicode string to an Ansi string.
//
//  This routine does NOT allocate the Ansi buffer.  Instead, it uses the
//  Ansi buffer passed in (unless AnsiLength is 0) and checks for buffer
//  overflow.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

int NlsUnicodeToAnsi(
    PCP_HASH pHashN,
    LPCWSTR pUnicodeBuffer,
    int UnicodeLength,
    LPSTR pAnsiBuffer,
    int AnsiLength)
{
    //
    //  Convert the Unicode string to an Ansi string and return the
    //  result.  The last error will be set appropriately by
    //  WideCharToMultiByte.
    //
    return (WideCharToMultiByte( pHashN->CodePage,
                                 0,
                                 pUnicodeBuffer,
                                 UnicodeLength,
                                 pAnsiBuffer,
                                 AnsiLength,
                                 NULL,
                                 NULL ));
}


////////////////////////////////////////////////////////////////////////////
//
//  NlsEnumUnicodeToAnsi
//
//  Converts a Unicode string to an Ansi string.
//
//  NOTE:  The Ansi buffer is allocated if the routine succeeds, so the
//         caller will need to free the buffer when it is no longer needed.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL NlsEnumUnicodeToAnsi(
    PCP_HASH pHashN,
    LPCWSTR pUnicodeBuffer,
    LPSTR *ppAnsiBuffer)
{
    LPSTR pAnsi;                  // ptr to Ansi buffer
    ULONG AnsiLength;             // length of the Ansi string
    ULONG UnicodeLength;          // length of the Unicode string
    ULONG ResultLength;           // result length of Ansi string


    //
    //  Get the length of the Unicode string (in bytes), including the
    //  null terminator.
    //
    UnicodeLength = NlsStrLenW(pUnicodeBuffer) + 1;

    //
    //  Get the size of the Ansi string (in bytes), including the
    //  null terminator.
    //
    AnsiLength = UnicodeLength * sizeof(WCHAR);

    //
    //  Allocate the Ansi buffer.
    //
    if ((pAnsi = (LPSTR)NLS_ALLOC_MEM(AnsiLength)) == NULL)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        return (FALSE);
    }

    //
    //  Convert the Unicode string to an Ansi string.
    //  It will already be null terminated.
    //
    ResultLength = WideCharToMultiByte( pHashN->CodePage,
                                        0,
                                        pUnicodeBuffer,
                                        UnicodeLength,
                                        pAnsi,
                                        AnsiLength,
                                        NULL,
                                        NULL );
    if (ResultLength == 0)
    {
        //
        //  Free the allocated Ansi buffer.
        //
        NLS_FREE_MEM(pAnsi);

        //
        //  See if the failure was due to insufficient buffer size.
        //
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
            //
            //  Get the size of the buffer needed to hold the
            //  ansi string.
            //
            AnsiLength = WideCharToMultiByte( pHashN->CodePage,
                                              0,
                                              pUnicodeBuffer,
                                              UnicodeLength,
                                              0,
                                              0,
                                              NULL,
                                              NULL );
            //
            //  Allocate the Ansi buffer.
            //
            if ((pAnsi = (LPSTR)NLS_ALLOC_MEM(AnsiLength)) == NULL)
            {
                SetLastError(ERROR_OUTOFMEMORY);
                return (FALSE);
            }

            //
            //  Try the translation again.
            //
            ResultLength = WideCharToMultiByte( pHashN->CodePage,
                                                0,
                                                pUnicodeBuffer,
                                                UnicodeLength,
                                                pAnsi,
                                                AnsiLength,
                                                NULL,
                                                NULL );
        }

        //
        //  If there was still an error, return failure.
        //
        if (ResultLength == 0)
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return (FALSE);
        }
    }

    //
    //  Return the Ansi buffer and success.
    //
    *ppAnsiBuffer = pAnsi;
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  NlsDispatchAnsiEnumProc
//
//  Converts a Unicode string to an Ansi string.
//
//  11-10-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

BOOL NlsDispatchAnsiEnumProc(
    LCID Locale,
    NLS_ENUMPROC pNlsEnumProc,
    DWORD dwFlags,
    LPWSTR pUnicodeBuffer1,
    LPWSTR pUnicodeBuffer2,
    DWORD dwValue1,
    DWORD dwValue2,
    LONG_PTR lParam,
    BOOL fVersion)
{
    PCP_HASH pHashN;              // ptr to CP hash node
    LPSTR pAnsiBuffer1 = NULL;    // ptr to ansi buffer
    LPSTR pAnsiBuffer2 = NULL;    // ptr to ansi buffer
    BOOL rc = FALSE;              // return code


    //
    //  Get the code page hash node for the given locale.
    //
    pHashN = NlsGetACPFromLocale(Locale, dwFlags);
    if (pHashN == NULL)
    {
        return (0);
    }

    //
    //  Convert the null-terminated Unicode string to a
    //  null-terminated Ansi string.
    //
    if (!NlsEnumUnicodeToAnsi( pHashN,
                               pUnicodeBuffer1,
                               &pAnsiBuffer1 ))
    {
        return (FALSE);
    }

    if ((pUnicodeBuffer2 != NULL) &&
        (!NlsEnumUnicodeToAnsi( pHashN,
                                pUnicodeBuffer2,
                                &pAnsiBuffer2 )))
    {
        NLS_FREE_MEM(pAnsiBuffer1);
        return (FALSE);
    }

    //
    //  Call the callback function.
    //
    switch (fVersion)
    {
        case ( 0 ) :
        {
            rc = (*pNlsEnumProc)(pAnsiBuffer1);
            break;
        }
        case ( 1 ) :
        {
            rc = (*((NLS_ENUMPROCEX)pNlsEnumProc))(pAnsiBuffer1, dwValue1);
            break;
        }
        case ( 2 ) :
        {
            rc = (*((NLS_ENUMPROC2)pNlsEnumProc))( dwValue1,
                                                   dwValue2,
                                                   pAnsiBuffer1,
                                                   lParam );
            break;
        }
        case ( 3 ) :
        {
            rc = (*((NLS_ENUMPROC3)pNlsEnumProc))( dwValue1,
                                                   pAnsiBuffer1,
                                                   pAnsiBuffer2,
                                                   dwValue2,
                                                   lParam );
            break;
        }
        case  ( 4 ) :
        {
            rc = (*((NLS_ENUMPROC4)pNlsEnumProc))( pAnsiBuffer1,
                                                   lParam );
            break;

        }
    }

    //
    //  Free any allocated memory.
    //
    NLS_FREE_MEM(pAnsiBuffer1);
    if (pAnsiBuffer2)
    {
        NLS_FREE_MEM(pAnsiBuffer2);
    }

    //
    //  Return the result.
    //
    return (rc);
}
