wine-wine/dlls/kernelbase/locale.c

3986 lines
123 KiB
C
Raw Normal View History

/*
* Locale support
*
* Copyright 1995 Martin von Loewis
* Copyright 1998 David Lee Lambert
* Copyright 2000 Julio César Gázquez
* Copyright 2003 Jon Griffiths
* Copyright 2005 Dmitry Timoshkov
* Copyright 2002, 2019 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include <stdlib.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "winuser.h"
#include "winternl.h"
#include "kernelbase.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(nls);
#define CALINFO_MAX_YEAR 2029
extern UINT CDECL __wine_get_unix_codepage(void);
extern unsigned int wine_decompose( int flags, WCHAR ch, WCHAR *dst, unsigned int dstlen ) DECLSPEC_HIDDEN;
extern WCHAR wine_compose( const WCHAR *str ) DECLSPEC_HIDDEN;
extern const unsigned short wctype_table[] DECLSPEC_HIDDEN;
extern const unsigned int collation_table[] DECLSPEC_HIDDEN;
static HANDLE kernel32_handle;
static const struct registry_value
{
DWORD lctype;
const WCHAR *name;
} registry_values[] =
{
{ LOCALE_ICALENDARTYPE, L"iCalendarType" },
{ LOCALE_ICURRDIGITS, L"iCurrDigits" },
{ LOCALE_ICURRENCY, L"iCurrency" },
{ LOCALE_IDIGITS, L"iDigits" },
{ LOCALE_IFIRSTDAYOFWEEK, L"iFirstDayOfWeek" },
{ LOCALE_IFIRSTWEEKOFYEAR, L"iFirstWeekOfYear" },
{ LOCALE_ILZERO, L"iLZero" },
{ LOCALE_IMEASURE, L"iMeasure" },
{ LOCALE_INEGCURR, L"iNegCurr" },
{ LOCALE_INEGNUMBER, L"iNegNumber" },
{ LOCALE_IPAPERSIZE, L"iPaperSize" },
{ LOCALE_ITIME, L"iTime" },
{ LOCALE_S1159, L"s1159" },
{ LOCALE_S2359, L"s2359" },
{ LOCALE_SCURRENCY, L"sCurrency" },
{ LOCALE_SDATE, L"sDate" },
{ LOCALE_SDECIMAL, L"sDecimal" },
{ LOCALE_SGROUPING, L"sGrouping" },
{ LOCALE_SLIST, L"sList" },
{ LOCALE_SLONGDATE, L"sLongDate" },
{ LOCALE_SMONDECIMALSEP, L"sMonDecimalSep" },
{ LOCALE_SMONGROUPING, L"sMonGrouping" },
{ LOCALE_SMONTHOUSANDSEP, L"sMonThousandSep" },
{ LOCALE_SNEGATIVESIGN, L"sNegativeSign" },
{ LOCALE_SPOSITIVESIGN, L"sPositiveSign" },
{ LOCALE_SSHORTDATE, L"sShortDate" },
{ LOCALE_STHOUSAND, L"sThousand" },
{ LOCALE_STIME, L"sTime" },
{ LOCALE_STIMEFORMAT, L"sTimeFormat" },
{ LOCALE_SYEARMONTH, L"sYearMonth" },
/* The following are not listed under MSDN as supported,
* but seem to be used and also stored in the registry.
*/
{ LOCALE_SNAME, L"LocaleName" },
{ LOCALE_ICOUNTRY, L"iCountry" },
{ LOCALE_IDATE, L"iDate" },
{ LOCALE_ILDATE, L"iLDate" },
{ LOCALE_ITLZERO, L"iTLZero" },
{ LOCALE_SCOUNTRY, L"sCountry" },
{ LOCALE_SABBREVLANGNAME, L"sLanguage" },
{ LOCALE_IDIGITSUBSTITUTION, L"Numshape" },
{ LOCALE_SNATIVEDIGITS, L"sNativeDigits" },
{ LOCALE_ITIMEMARKPOSN, L"iTimePrefix" },
};
static WCHAR *registry_cache[ARRAY_SIZE(registry_values)];
static const struct { UINT cp; const WCHAR *name; } codepage_names[] =
{
{ 37, L"IBM EBCDIC US Canada" },
{ 424, L"IBM EBCDIC Hebrew" },
{ 437, L"OEM United States" },
{ 500, L"IBM EBCDIC International" },
{ 737, L"OEM Greek 437G" },
{ 775, L"OEM Baltic" },
{ 850, L"OEM Multilingual Latin 1" },
{ 852, L"OEM Slovak Latin 2" },
{ 855, L"OEM Cyrillic" },
{ 856, L"Hebrew PC" },
{ 857, L"OEM Turkish" },
{ 860, L"OEM Portuguese" },
{ 861, L"OEM Icelandic" },
{ 862, L"OEM Hebrew" },
{ 863, L"OEM Canadian French" },
{ 864, L"OEM Arabic" },
{ 865, L"OEM Nordic" },
{ 866, L"OEM Russian" },
{ 869, L"OEM Greek" },
{ 874, L"ANSI/OEM Thai" },
{ 875, L"IBM EBCDIC Greek" },
{ 878, L"Russian KOI8" },
{ 932, L"ANSI/OEM Japanese Shift-JIS" },
{ 936, L"ANSI/OEM Simplified Chinese GBK" },
{ 949, L"ANSI/OEM Korean Unified Hangul" },
{ 950, L"ANSI/OEM Traditional Chinese Big5" },
{ 1006, L"IBM Arabic" },
{ 1026, L"IBM EBCDIC Latin 5 Turkish" },
{ 1250, L"ANSI Eastern Europe" },
{ 1251, L"ANSI Cyrillic" },
{ 1252, L"ANSI Latin 1" },
{ 1253, L"ANSI Greek" },
{ 1254, L"ANSI Turkish" },
{ 1255, L"ANSI Hebrew" },
{ 1256, L"ANSI Arabic" },
{ 1257, L"ANSI Baltic" },
{ 1258, L"ANSI/OEM Viet Nam" },
{ 1361, L"Korean Johab" },
{ 10000, L"Mac Roman" },
{ 10001, L"Mac Japanese" },
{ 10002, L"Mac Traditional Chinese" },
{ 10003, L"Mac Korean" },
{ 10004, L"Mac Arabic" },
{ 10005, L"Mac Hebrew" },
{ 10006, L"Mac Greek" },
{ 10007, L"Mac Cyrillic" },
{ 10008, L"Mac Simplified Chinese" },
{ 10010, L"Mac Romanian" },
{ 10017, L"Mac Ukrainian" },
{ 10021, L"Mac Thai" },
{ 10029, L"Mac Latin 2" },
{ 10079, L"Mac Icelandic" },
{ 10081, L"Mac Turkish" },
{ 10082, L"Mac Croatian" },
{ 20127, L"US-ASCII (7bit)" },
{ 20866, L"Russian KOI8" },
{ 20932, L"EUC-JP" },
{ 21866, L"Ukrainian KOI8" },
{ 28591, L"ISO 8859-1 Latin 1" },
{ 28592, L"ISO 8859-2 Latin 2 (East European)" },
{ 28593, L"ISO 8859-3 Latin 3 (South European)" },
{ 28594, L"ISO 8859-4 Latin 4 (Baltic old)" },
{ 28595, L"ISO 8859-5 Cyrillic" },
{ 28596, L"ISO 8859-6 Arabic" },
{ 28597, L"ISO 8859-7 Greek" },
{ 28598, L"ISO 8859-8 Hebrew" },
{ 28599, L"ISO 8859-9 Latin 5 (Turkish)" },
{ 28600, L"ISO 8859-10 Latin 6 (Nordic)" },
{ 28601, L"ISO 8859-11 Latin (Thai)" },
{ 28603, L"ISO 8859-13 Latin 7 (Baltic)" },
{ 28604, L"ISO 8859-14 Latin 8 (Celtic)" },
{ 28605, L"ISO 8859-15 Latin 9 (Euro)" },
{ 28606, L"ISO 8859-16 Latin 10 (Balkan)" },
{ 65000, L"Unicode (UTF-7)" },
{ 65001, L"Unicode (UTF-8)" }
};
/* Unicode expanded ligatures */
static const WCHAR ligatures[][5] =
{
{ 0x00c6, 'A','E',0 },
{ 0x00de, 'T','H',0 },
{ 0x00df, 's','s',0 },
{ 0x00e6, 'a','e',0 },
{ 0x00fe, 't','h',0 },
{ 0x0132, 'I','J',0 },
{ 0x0133, 'i','j',0 },
{ 0x0152, 'O','E',0 },
{ 0x0153, 'o','e',0 },
{ 0x01c4, 'D',0x017d,0 },
{ 0x01c5, 'D',0x017e,0 },
{ 0x01c6, 'd',0x017e,0 },
{ 0x01c7, 'L','J',0 },
{ 0x01c8, 'L','j',0 },
{ 0x01c9, 'l','j',0 },
{ 0x01ca, 'N','J',0 },
{ 0x01cb, 'N','j',0 },
{ 0x01cc, 'n','j',0 },
{ 0x01e2, 0x0100,0x0112,0 },
{ 0x01e3, 0x0101,0x0113,0 },
{ 0x01f1, 'D','Z',0 },
{ 0x01f2, 'D','z',0 },
{ 0x01f3, 'd','z',0 },
{ 0x01fc, 0x00c1,0x00c9,0 },
{ 0x01fd, 0x00e1,0x00e9,0 },
{ 0x05f0, 0x05d5,0x05d5,0 },
{ 0x05f1, 0x05d5,0x05d9,0 },
{ 0x05f2, 0x05d9,0x05d9,0 },
{ 0xfb00, 'f','f',0 },
{ 0xfb01, 'f','i',0 },
{ 0xfb02, 'f','l',0 },
{ 0xfb03, 'f','f','i',0 },
{ 0xfb04, 'f','f','l',0 },
{ 0xfb05, 0x017f,'t',0 },
{ 0xfb06, 's','t',0 },
};
static NLSTABLEINFO nls_info;
static UINT mac_cp = 10000;
static HKEY intl_key;
static HKEY nls_key;
static CPTABLEINFO codepages[128];
static unsigned int nb_codepages;
static CRITICAL_SECTION locale_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &locale_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") }
};
static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 };
/***********************************************************************
* init_locale
*/
void init_locale(void)
{
UINT ansi_cp = 0, oem_cp = 0;
USHORT *ansi_ptr, *oem_ptr, *casemap_ptr;
LCID lcid = GetUserDefaultLCID();
WCHAR bufferW[80];
GEOID geoid = GEOID_NOT_AVAILABLE;
DWORD count, dispos, i;
SIZE_T size;
HKEY hkey;
kernel32_handle = GetModuleHandleW( L"kernel32.dll" );
GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR) );
GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&mac_cp, sizeof(mac_cp)/sizeof(WCHAR) );
GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR) );
NtGetNlsSectionPtr( 10, 0, 0, (void **)&casemap_ptr, &size );
if (!ansi_cp || NtGetNlsSectionPtr( 11, ansi_cp, NULL, (void **)&ansi_ptr, &size ))
NtGetNlsSectionPtr( 11, 1252, NULL, (void **)&ansi_ptr, &size );
if (!oem_cp || NtGetNlsSectionPtr( 11, oem_cp, 0, (void **)&oem_ptr, &size ))
NtGetNlsSectionPtr( 11, 437, NULL, (void **)&oem_ptr, &size );
NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
NtCurrentTeb()->Peb->UnicodeCaseTableData = casemap_ptr;
RtlInitNlsTables( ansi_ptr, oem_ptr, casemap_ptr, &nls_info );
RtlResetRtlTranslations( &nls_info );
RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls",
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL );
RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International",
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL );
if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &dispos ))
{
if (dispos == REG_CREATED_NEW_KEY)
{
GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
(WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
SetUserGeoID( geoid );
}
RegCloseKey( hkey );
}
/* Update registry contents if the user locale has changed.
* This simulates the action of the Windows control panel. */
count = sizeof(bufferW);
if (!RegQueryValueExW( intl_key, L"Locale", NULL, NULL, (BYTE *)bufferW, &count ))
{
if (wcstoul( bufferW, NULL, 16 ) == lcid) return; /* already set correctly */
TRACE( "updating registry, locale changed %s -> %08x\n", debugstr_w(bufferW), lcid );
}
else TRACE( "updating registry, locale changed none -> %08x\n", lcid );
swprintf( bufferW, ARRAY_SIZE(bufferW), L"%08x", lcid );
RegSetValueExW( intl_key, L"Locale", 0, REG_SZ,
(BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
for (i = 0; i < ARRAY_SIZE(registry_values); i++)
{
GetLocaleInfoW( LOCALE_USER_DEFAULT, registry_values[i].lctype | LOCALE_NOUSEROVERRIDE,
bufferW, ARRAY_SIZE( bufferW ));
RegSetValueExW( intl_key, registry_values[i].name, 0, REG_SZ,
(BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) );
}
if (geoid == GEOID_NOT_AVAILABLE)
{
GetLocaleInfoW( LOCALE_USER_DEFAULT, LOCALE_IGEOID | LOCALE_RETURN_NUMBER,
(WCHAR *)&geoid, sizeof(geoid) / sizeof(WCHAR) );
SetUserGeoID( geoid );
}
if (!RegCreateKeyExW( nls_key, L"Codepage",
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
{
count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", ansi_cp );
RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", oem_cp );
RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", mac_cp );
RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) );
RegCloseKey( hkey );
}
}
static inline USHORT get_table_entry( const USHORT *table, WCHAR ch )
{
return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
}
static inline WCHAR casemap( const USHORT *table, WCHAR ch )
{
return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
}
static UINT get_lcid_codepage( LCID lcid, ULONG flags )
{
UINT ret = GetACP();
if (!(flags & LOCALE_USE_CP_ACP) && lcid != GetSystemDefaultLCID())
GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&ret, sizeof(ret)/sizeof(WCHAR) );
return ret;
}
static BOOL is_genitive_name_supported( LCTYPE lctype )
{
switch (LOWORD(lctype))
{
case LOCALE_SMONTHNAME1:
case LOCALE_SMONTHNAME2:
case LOCALE_SMONTHNAME3:
case LOCALE_SMONTHNAME4:
case LOCALE_SMONTHNAME5:
case LOCALE_SMONTHNAME6:
case LOCALE_SMONTHNAME7:
case LOCALE_SMONTHNAME8:
case LOCALE_SMONTHNAME9:
case LOCALE_SMONTHNAME10:
case LOCALE_SMONTHNAME11:
case LOCALE_SMONTHNAME12:
case LOCALE_SMONTHNAME13:
return TRUE;
default:
return FALSE;
}
}
static int get_value_base_by_lctype( LCTYPE lctype )
{
return lctype == LOCALE_ILANGUAGE || lctype == LOCALE_IDEFAULTLANGUAGE ? 16 : 10;
}
static const struct registry_value *get_locale_registry_value( DWORD lctype )
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE( registry_values ); i++)
if (registry_values[i].lctype == lctype) return &registry_values[i];
return NULL;
}
static INT get_registry_locale_info( const struct registry_value *registry_value, LPWSTR buffer, INT len )
{
DWORD size, index = registry_value - registry_values;
INT ret;
RtlEnterCriticalSection( &locale_section );
if (!registry_cache[index])
{
size = len * sizeof(WCHAR);
ret = RegQueryValueExW( intl_key, registry_value->name, NULL, NULL, (BYTE *)buffer, &size );
if (!ret)
{
if (buffer && (registry_cache[index] = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) )))
{
memcpy( registry_cache[index], buffer, size );
registry_cache[index][size / sizeof(WCHAR)] = 0;
}
RtlLeaveCriticalSection( &locale_section );
return size / sizeof(WCHAR);
}
else
{
RtlLeaveCriticalSection( &locale_section );
if (ret == ERROR_FILE_NOT_FOUND) return -1;
if (ret == ERROR_MORE_DATA) SetLastError( ERROR_INSUFFICIENT_BUFFER );
else SetLastError( ret );
return 0;
}
}
ret = lstrlenW( registry_cache[index] ) + 1;
if (buffer)
{
if (ret > len)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
ret = 0;
}
else lstrcpyW( buffer, registry_cache[index] );
}
RtlLeaveCriticalSection( &locale_section );
return ret;
}
static const CPTABLEINFO *get_codepage_table( UINT codepage )
{
unsigned int i;
USHORT *ptr;
SIZE_T size;
switch (codepage)
{
case CP_ACP:
return &nls_info.AnsiTableInfo;
case CP_OEMCP:
return &nls_info.OemTableInfo;
case CP_MACCP:
codepage = mac_cp;
break;
case CP_THREAD_ACP:
if (NtCurrentTeb()->CurrentLocale == GetUserDefaultLCID()) return &nls_info.AnsiTableInfo;
codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 );
if (!codepage) return &nls_info.AnsiTableInfo;
break;
default:
if (codepage == nls_info.AnsiTableInfo.CodePage) return &nls_info.AnsiTableInfo;
if (codepage == nls_info.OemTableInfo.CodePage) return &nls_info.OemTableInfo;
break;
}
RtlEnterCriticalSection( &locale_section );
for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done;
if (i == ARRAY_SIZE( codepages ))
{
RtlLeaveCriticalSection( &locale_section );
ERR( "too many codepages\n" );
return NULL;
}
if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size ))
{
RtlLeaveCriticalSection( &locale_section );
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
RtlInitCodePageTable( ptr, &codepages[i] );
nb_codepages++;
done:
RtlLeaveCriticalSection( &locale_section );
return &codepages[i];
}
static const WCHAR *get_ligature( WCHAR wc )
{
int low = 0, high = ARRAY_SIZE( ligatures ) -1;
while (low <= high)
{
int pos = (low + high) / 2;
if (ligatures[pos][0] < wc) low = pos + 1;
else if (ligatures[pos][0] > wc) high = pos - 1;
else return ligatures[pos] + 1;
}
return NULL;
}
static int expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
{
int i, len, pos = 0;
const WCHAR *expand;
for (i = 0; i < srclen; i++)
{
if (!(expand = get_ligature( src[i] )))
{
expand = src + i;
len = 1;
}
else len = lstrlenW( expand );
if (dstlen)
{
if (pos + len > dstlen) break;
memcpy( dst + pos, expand, len * sizeof(WCHAR) );
}
pos += len;
}
return pos;
}
static int fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int dstlen )
{
extern const WCHAR wine_digitmap[] DECLSPEC_HIDDEN;
int i;
if (!dstlen) return srclen;
if (srclen > dstlen) return 0;
for (i = 0; i < srclen; i++)
dst[i] = src[i] + wine_digitmap[wine_digitmap[src[i] >> 8] + (src[i] & 0xff)];
return srclen;
}
static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
{
int len, i;
if (flags)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!dstlen) return srclen;
len = min( srclen, dstlen );
for (i = 0; i < len; i++)
{
unsigned char c = src[i];
dst[i] = (c < 0x20) ? c : c + 0xf000;
}
if (len < srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return len;
}
static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
{
static const signed char base64_decoding_table[] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
};
const char *source_end = src + srclen;
int offset = 0, pos = 0;
DWORD byte_pair = 0;
if (flags)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
#define OUTPUT(ch) \
do { \
if (dstlen > 0) \
{ \
if (pos >= dstlen) goto overflow; \
dst[pos] = (ch); \
} \
pos++; \
} while(0)
while (src < source_end)
{
if (*src == '+')
{
src++;
if (src >= source_end) break;
if (*src == '-')
{
/* just a plus sign escaped as +- */
OUTPUT( '+' );
src++;
continue;
}
do
{
signed char sextet = *src;
if (sextet == '-')
{
/* skip over the dash and end base64 decoding
* the current, unfinished byte pair is discarded */
src++;
offset = 0;
break;
}
if (sextet < 0)
{
/* the next character of src is < 0 and therefore not part of a base64 sequence
* the current, unfinished byte pair is NOT discarded in this case
* this is probably a bug in Windows */
break;
}
sextet = base64_decoding_table[sextet];
if (sextet == -1)
{
/* -1 means that the next character of src is not part of a base64 sequence
* in other words, all sextets in this base64 sequence have been processed
* the current, unfinished byte pair is discarded */
offset = 0;
break;
}
byte_pair = (byte_pair << 6) | sextet;
offset += 6;
if (offset >= 16)
{
/* this byte pair is done */
OUTPUT( byte_pair >> (offset - 16) );
offset -= 16;
}
src++;
}
while (src < source_end);
}
else
{
OUTPUT( (unsigned char)*src );
src++;
}
}
return pos;
overflow:
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
#undef OUTPUT
}
static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen )
{
DWORD reslen;
NTSTATUS status;
if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!dstlen) dst = NULL;
status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen );
if (status == STATUS_SOME_NOT_MAPPED)
{
if (flags & MB_ERR_INVALID_CHARS)
{
SetLastError( ERROR_NO_UNICODE_TRANSLATION );
return 0;
}
}
else if (!set_ntstatus( status )) reslen = 0;
return reslen / sizeof(WCHAR);
}
static inline int is_private_use_area_char( WCHAR code )
{
return (code >= 0xe000 && code <= 0xf8ff);
}
static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen )
{
if (info->DBCSOffsets)
{
for ( ; srclen; src++, srclen-- )
{
USHORT off = info->DBCSOffsets[*src];
if (off)
{
if (srclen == 1) break; /* partial char, error */
if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar &&
((src[0] << 8) | src[1]) != info->TransDefaultChar) break;
src++;
srclen--;
continue;
}
if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransDefaultChar)
break;
if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
}
}
else
{
for ( ; srclen; src++, srclen-- )
{
if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransDefaultChar)
break;
if (is_private_use_area_char( info->MultiByteTable[*src] )) break;
}
}
return !!srclen;
}
static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen,
WCHAR *dst, int dstlen )
{
WCHAR ch, dummy[4]; /* no decomposition is larger than 4 chars */
USHORT off;
int len, res;
if (info->DBCSOffsets)
{
if (!dstlen) /* compute length */
{
for (len = 0; srclen; srclen--, src++)
{
if ((off = info->DBCSOffsets[*src]))
{
if (srclen > 1 && src[1])
{
src++;
srclen--;
ch = info->DBCSOffsets[off + *src];
}
else ch = info->UniDefaultChar;
}
else ch = info->MultiByteTable[*src];
len += wine_decompose( 0, ch, dummy, 4 );
}
return len;
}
for (len = dstlen; srclen && len; srclen--, src++)
{
if ((off = info->DBCSOffsets[*src]))
{
if (srclen > 1 && src[1])
{
src++;
srclen--;
ch = info->DBCSOffsets[off + *src];
}
else ch = info->UniDefaultChar;
}
else ch = info->MultiByteTable[*src];
if (!(res = wine_decompose( 0, ch, dst, len ))) break;
dst += res;
len -= res;
}
}
else
{
if (!dstlen) /* compute length */
{
for (len = 0; srclen; srclen--, src++)
len += wine_decompose( 0, info->MultiByteTable[*src], dummy, 4 );
return len;
}
for (len = dstlen; srclen && len; srclen--, src++)
{
if (!(res = wine_decompose( 0, info->MultiByteTable[*src], dst, len ))) break;
len -= res;
dst += res;
}
}
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dstlen - len;
}
static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
WCHAR *dst, int dstlen )
{
const USHORT *table = info->MultiByteTable;
int ret = srclen;
if (!dstlen) return srclen;
if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */
{
srclen = dstlen;
SetLastError( ERROR_INSUFFICIENT_BUFFER );
ret = 0;
}
while (srclen >= 16)
{
dst[0] = table[src[0]];
dst[1] = table[src[1]];
dst[2] = table[src[2]];
dst[3] = table[src[3]];
dst[4] = table[src[4]];
dst[5] = table[src[5]];
dst[6] = table[src[6]];
dst[7] = table[src[7]];
dst[8] = table[src[8]];
dst[9] = table[src[9]];
dst[10] = table[src[10]];
dst[11] = table[src[11]];
dst[12] = table[src[12]];
dst[13] = table[src[13]];
dst[14] = table[src[14]];
dst[15] = table[src[15]];
src += 16;
dst += 16;
srclen -= 16;
}
/* now handle the remaining characters */
src += srclen;
dst += srclen;
switch (srclen)
{
case 15: dst[-15] = table[src[-15]];
case 14: dst[-14] = table[src[-14]];
case 13: dst[-13] = table[src[-13]];
case 12: dst[-12] = table[src[-12]];
case 11: dst[-11] = table[src[-11]];
case 10: dst[-10] = table[src[-10]];
case 9: dst[-9] = table[src[-9]];
case 8: dst[-8] = table[src[-8]];
case 7: dst[-7] = table[src[-7]];
case 6: dst[-6] = table[src[-6]];
case 5: dst[-5] = table[src[-5]];
case 4: dst[-4] = table[src[-4]];
case 3: dst[-3] = table[src[-3]];
case 2: dst[-2] = table[src[-2]];
case 1: dst[-1] = table[src[-1]];
case 0: break;
}
return ret;
}
static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen,
WCHAR *dst, int dstlen )
{
USHORT off;
int i;
if (!dstlen)
{
for (i = 0; srclen; i++, src++, srclen--)
if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; }
return i;
}
for (i = dstlen; srclen && i; i--, srclen--, src++, dst++)
{
if ((off = info->DBCSOffsets[*src]))
{
if (srclen > 1 && src[1])
{
src++;
srclen--;
*dst = info->DBCSOffsets[off + *src];
}
else *dst = info->UniDefaultChar;
}
else *dst = info->MultiByteTable[*src];
}
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dstlen - i;
}
static int mbstowcs_codepage( UINT codepage, DWORD flags, const char *src, int srclen,
WCHAR *dst, int dstlen )
{
CPTABLEINFO local_info;
const CPTABLEINFO *info = get_codepage_table( codepage );
const unsigned char *str = (const unsigned char *)src;
if (!info)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256)
{
local_info = *info;
local_info.MultiByteTable += 257;
info = &local_info;
}
if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen ))
{
SetLastError( ERROR_NO_UNICODE_TRANSLATION );
return 0;
}
if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen );
if (info->DBCSOffsets)
return mbstowcs_dbcs( info, str, srclen, dst, dstlen );
else
return mbstowcs_sbcs( info, str, srclen, dst, dstlen );
}
static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
const char *defchar, BOOL *used )
{
int len, i;
if (flags)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (defchar || used)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (!dstlen) return srclen;
len = min( srclen, dstlen );
for (i = 0; i < len; i++)
{
if (src[i] < 0x20) dst[i] = src[i];
else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000;
else
{
SetLastError( ERROR_NO_UNICODE_TRANSLATION );
return 0;
}
}
if (srclen > len)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return len;
}
static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
const char *defchar, BOOL *used )
{
static const char directly_encodable[] =
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */
};
#define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)])
static const char base64_encoding_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const WCHAR *source_end = src + srclen;
int pos = 0;
if (defchar || used)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (flags)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
#define OUTPUT(ch) \
do { \
if (dstlen > 0) \
{ \
if (pos >= dstlen) goto overflow; \
dst[pos] = (ch); \
} \
pos++; \
} while (0)
while (src < source_end)
{
if (*src == '+')
{
OUTPUT( '+' );
OUTPUT( '-' );
src++;
}
else if (ENCODABLE(*src))
{
OUTPUT( *src );
src++;
}
else
{
unsigned int offset = 0, byte_pair = 0;
OUTPUT( '+' );
while (src < source_end && !ENCODABLE(*src))
{
byte_pair = (byte_pair << 16) | *src;
offset += 16;
while (offset >= 6)
{
offset -= 6;
OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] );
}
src++;
}
if (offset)
{
/* Windows won't create a padded base64 character if there's no room for the - sign
* as well ; this is probably a bug in Windows */
if (dstlen > 0 && pos + 1 >= dstlen) goto overflow;
byte_pair <<= (6 - offset);
OUTPUT( base64_encoding_table[byte_pair & 0x3f] );
}
/* Windows always explicitly terminates the base64 sequence
even though RFC 2152 (page 3, rule 2) does not require this */
OUTPUT( '-' );
}
}
return pos;
overflow:
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
#undef OUTPUT
#undef ENCODABLE
}
static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen,
const char *defchar, BOOL *used )
{
DWORD reslen;
NTSTATUS status;
if (defchar || used)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!dstlen) dst = NULL;
status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) );
if (status == STATUS_SOME_NOT_MAPPED)
{
if (flags & WC_ERR_INVALID_CHARS)
{
SetLastError( ERROR_NO_UNICODE_TRANSLATION );
return 0;
}
}
else if (!set_ntstatus( status )) reslen = 0;
return reslen;
}
static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
char *dst, unsigned int dstlen )
{
const char *table = info->WideCharTable;
int ret = srclen;
if (!dstlen) return srclen;
if (dstlen < srclen)
{
/* buffer too small: fill it up to dstlen and return error */
srclen = dstlen;
SetLastError( ERROR_INSUFFICIENT_BUFFER );
ret = 0;
}
while (srclen >= 16)
{
dst[0] = table[src[0]];
dst[1] = table[src[1]];
dst[2] = table[src[2]];
dst[3] = table[src[3]];
dst[4] = table[src[4]];
dst[5] = table[src[5]];
dst[6] = table[src[6]];
dst[7] = table[src[7]];
dst[8] = table[src[8]];
dst[9] = table[src[9]];
dst[10] = table[src[10]];
dst[11] = table[src[11]];
dst[12] = table[src[12]];
dst[13] = table[src[13]];
dst[14] = table[src[14]];
dst[15] = table[src[15]];
src += 16;
dst += 16;
srclen -= 16;
}
/* now handle remaining characters */
src += srclen;
dst += srclen;
switch(srclen)
{
case 15: dst[-15] = table[src[-15]];
case 14: dst[-14] = table[src[-14]];
case 13: dst[-13] = table[src[-13]];
case 12: dst[-12] = table[src[-12]];
case 11: dst[-11] = table[src[-11]];
case 10: dst[-10] = table[src[-10]];
case 9: dst[-9] = table[src[-9]];
case 8: dst[-8] = table[src[-8]];
case 7: dst[-7] = table[src[-7]];
case 6: dst[-6] = table[src[-6]];
case 5: dst[-5] = table[src[-5]];
case 4: dst[-4] = table[src[-4]];
case 3: dst[-3] = table[src[-3]];
case 2: dst[-2] = table[src[-2]];
case 1: dst[-1] = table[src[-1]];
case 0: break;
}
return ret;
}
static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen,
char *dst, unsigned int dstlen )
{
const USHORT *table = info->WideCharTable;
int i;
if (!dstlen)
{
for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++;
return i;
}
for (i = dstlen; srclen && i; i--, srclen--, src++)
{
if (table[*src] & 0xff00)
{
if (i == 1) break; /* do not output a partial char */
i--;
*dst++ = table[*src] >> 8;
}
*dst++ = (char)table[*src];
}
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dstlen - i;
}
static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags,
WCHAR wch, unsigned char ch )
{
if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
return (info->MultiByteTable[ch] == wch);
return 1;
}
static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags,
WCHAR wch, unsigned short ch )
{
if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar)
{
if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch;
return info->MultiByteTable[ch] == wch;
}
return 1;
}
static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
{
const char *table = info->WideCharTable;
const char def = defchar ? *defchar : (char)info->DefaultChar;
int i;
BOOL tmp;
WCHAR wch, composed;
if (!used) used = &tmp; /* avoid checking on every char */
*used = FALSE;
if (!dstlen)
{
for (i = 0; srclen; i++, src++, srclen--)
{
wch = *src;
if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = wine_compose( src )))
{
/* now check if we can use the composed char */
if (is_valid_sbcs_mapping( info, flags, composed, table[composed] ))
{
/* we have a good mapping, use it */
src++;
srclen--;
continue;
}
/* no mapping for the composed char, check the other flags */
if (flags & WC_DEFAULTCHAR) /* use the default char instead */
{
*used = TRUE;
src++; /* skip the non-spacing char */
srclen--;
continue;
}
if (flags & WC_DISCARDNS) /* skip the second char of the composition */
{
src++;
srclen--;
}
/* WC_SEPCHARS is the default */
}
if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch, table[wch] );
}
return i;
}
for (i = dstlen; srclen && i; dst++, i--, src++, srclen--)
{
wch = *src;
if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = wine_compose( src )))
{
/* now check if we can use the composed char */
*dst = table[composed];
if (is_valid_sbcs_mapping( info, flags, composed, table[composed] ))
{
/* we have a good mapping, use it */
src++;
srclen--;
continue;
}
/* no mapping for the composed char, check the other flags */
if (flags & WC_DEFAULTCHAR) /* use the default char instead */
{
*dst = def;
*used = TRUE;
src++; /* skip the non-spacing char */
srclen--;
continue;
}
if (flags & WC_DISCARDNS) /* skip the second char of the composition */
{
src++;
srclen--;
}
/* WC_SEPCHARS is the default */
}
*dst = table[wch];
if (!is_valid_sbcs_mapping( info, flags, wch, table[wch] ))
{
*dst = def;
*used = TRUE;
}
}
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dstlen - i;
}
static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen,
char *dst, unsigned int dstlen, const char *defchar, BOOL *used )
{
const USHORT *table = info->WideCharTable;
WCHAR wch, composed, defchar_value;
unsigned short res;
BOOL tmp;
int i;
if (!defchar[1]) defchar_value = (unsigned char)defchar[0];
else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1];
if (!used) used = &tmp; /* avoid checking on every char */
*used = FALSE;
if (!dstlen)
{
if (!defchar && !used && !(flags & WC_COMPOSITECHECK))
{
for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++;
return i;
}
for (i = 0; srclen; srclen--, src++, i++)
{
wch = *src;
if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = wine_compose( src )))
{
/* now check if we can use the composed char */
res = table[composed];
if (is_valid_dbcs_mapping( info, flags, composed, res ))
{
/* we have a good mapping for the composed char, use it */
if (res & 0xff00) i++;
src++;
srclen--;
continue;
}
/* no mapping for the composed char, check the other flags */
if (flags & WC_DEFAULTCHAR) /* use the default char instead */
{
if (defchar_value & 0xff00) i++;
*used = TRUE;
src++; /* skip the non-spacing char */
srclen--;
continue;
}
if (flags & WC_DISCARDNS) /* skip the second char of the composition */
{
src++;
srclen--;
}
/* WC_SEPCHARS is the default */
}
res = table[wch];
if (!is_valid_dbcs_mapping( info, flags, wch, res ))
{
res = defchar_value;
*used = TRUE;
}
if (res & 0xff00) i++;
}
return i;
}
for (i = dstlen; srclen && i; i--, srclen--, src++)
{
wch = *src;
if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = wine_compose( src )))
{
/* now check if we can use the composed char */
res = table[composed];
if (is_valid_dbcs_mapping( info, flags, composed, res ))
{
/* we have a good mapping for the composed char, use it */
src++;
srclen--;
goto output_char;
}
/* no mapping for the composed char, check the other flags */
if (flags & WC_DEFAULTCHAR) /* use the default char instead */
{
res = defchar_value;
*used = TRUE;
src++; /* skip the non-spacing char */
srclen--;
goto output_char;
}
if (flags & WC_DISCARDNS) /* skip the second char of the composition */
{
src++;
srclen--;
}
/* WC_SEPCHARS is the default */
}
res = table[wch];
if (!is_valid_dbcs_mapping( info, flags, wch, res ))
{
res = defchar_value;
*used = TRUE;
}
output_char:
if (res & 0xff00)
{
if (i == 1) break; /* do not output a partial char */
i--;
*dst++ = res >> 8;
}
*dst++ = (char)res;
}
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dstlen - i;
}
static int wcstombs_codepage( UINT codepage, DWORD flags, const WCHAR *src, int srclen,
char *dst, int dstlen, const char *defchar, BOOL *used )
{
const CPTABLEINFO *info = get_codepage_table( codepage );
if (!info)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS |
WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (flags || defchar || used)
{
if (!defchar) defchar = (const char *)&info->DefaultChar;
if (info->DBCSOffsets)
return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
else
return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used );
}
if (info->DBCSOffsets)
return wcstombs_dbcs( info, src, srclen, dst, dstlen );
else
return wcstombs_sbcs( info, src, srclen, dst, dstlen );
}
static int get_sortkey( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen )
{
WCHAR dummy[4]; /* no decomposition is larger than 4 chars */
int key_len[4];
char *key_ptr[4];
const WCHAR *src_save = src;
int srclen_save = srclen;
key_len[0] = key_len[1] = key_len[2] = key_len[3] = 0;
for (; srclen; srclen--, src++)
{
unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
dummy[0] = *src;
if (decomposed_len)
{
for (i = 0; i < decomposed_len; i++)
{
WCHAR wch = dummy[i];
unsigned int ce;
if ((flags & NORM_IGNORESYMBOLS) &&
(get_table_entry( wctype_table, wch ) & (C1_PUNCT | C1_SPACE)))
continue;
if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
ce = collation_table[collation_table[wch >> 8] + (wch & 0xff)];
if (ce != (unsigned int)-1)
{
if (ce >> 16) key_len[0] += 2;
if ((ce >> 8) & 0xff) key_len[1]++;
if ((ce >> 4) & 0x0f) key_len[2]++;
if (ce & 1)
{
if (wch >> 8) key_len[3]++;
key_len[3]++;
}
}
else
{
key_len[0] += 2;
if (wch >> 8) key_len[0]++;
if (wch & 0xff) key_len[0]++;
}
}
}
}
if (!dstlen) /* compute length */
/* 4 * '\1' + key length */
return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4;
if (dstlen < key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1)
return 0; /* overflow */
src = src_save;
srclen = srclen_save;
key_ptr[0] = dst;
key_ptr[1] = key_ptr[0] + key_len[0] + 1;
key_ptr[2] = key_ptr[1] + key_len[1] + 1;
key_ptr[3] = key_ptr[2] + key_len[2] + 1;
for (; srclen; srclen--, src++)
{
unsigned int i, decomposed_len = 1;/*wine_decompose(*src, dummy, 4);*/
dummy[0] = *src;
if (decomposed_len)
{
for (i = 0; i < decomposed_len; i++)
{
WCHAR wch = dummy[i];
unsigned int ce;
if ((flags & NORM_IGNORESYMBOLS) &&
(get_table_entry( wctype_table, wch ) & (C1_PUNCT | C1_SPACE)))
continue;
if (flags & NORM_IGNORECASE) wch = casemap( nls_info.LowerCaseTable, wch );
ce = collation_table[collation_table[wch >> 8] + (wch & 0xff)];
if (ce != (unsigned int)-1)
{
WCHAR key;
if ((key = ce >> 16))
{
*key_ptr[0]++ = key >> 8;
*key_ptr[0]++ = key & 0xff;
}
/* make key 1 start from 2 */
if ((key = (ce >> 8) & 0xff)) *key_ptr[1]++ = key + 1;
/* make key 2 start from 2 */
if ((key = (ce >> 4) & 0x0f)) *key_ptr[2]++ = key + 1;
/* key 3 is always a character code */
if (ce & 1)
{
if (wch >> 8) *key_ptr[3]++ = wch >> 8;
if (wch & 0xff) *key_ptr[3]++ = wch & 0xff;
}
}
else
{
*key_ptr[0]++ = 0xff;
*key_ptr[0]++ = 0xfe;
if (wch >> 8) *key_ptr[0]++ = wch >> 8;
if (wch & 0xff) *key_ptr[0]++ = wch & 0xff;
}
}
}
}
*key_ptr[0] = 1;
*key_ptr[1] = 1;
*key_ptr[2] = 1;
*key_ptr[3]++ = 1;
*key_ptr[3] = 0;
return key_ptr[3] - dst;
}
/* compose a full-width katakana. return consumed source characters. */
static int compose_katakana( const WCHAR *src, int srclen, WCHAR *dst )
{
static const BYTE katakana_map[] =
{
/* */ 0x02, 0x0c, 0x0d, 0x01, 0xfb, 0xf2, 0xa1, /* U+FF61- */
0xa3, 0xa5, 0xa7, 0xa9, 0xe3, 0xe5, 0xe7, 0xc3, /* U+FF68- */
0xfc, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, /* U+FF70- */
0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, /* U+FF78- */
0xbf, 0xc1, 0xc4, 0xc6, 0xc8, 0xca, 0xcb, 0xcc, /* U+FF80- */
0xcd, 0xce, 0xcf, 0xd2, 0xd5, 0xd8, 0xdb, 0xde, /* U+FF88- */
0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe8, 0xe9, /* U+FF90- */
0xea, 0xeb, 0xec, 0xed, 0xef, 0xf3, 0x99, 0x9a, /* U+FF98- */
};
WCHAR dummy;
int shift;
if (!dst) dst = &dummy;
switch (*src)
{
case 0x309b:
case 0x309c:
*dst = *src - 2;
return 1;
case 0x30f0:
case 0x30f1:
case 0x30fd:
*dst = *src;
break;
default:
shift = *src - 0xff61;
if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
*dst = katakana_map[shift] | 0x3000;
break;
}
if (srclen <= 1) return 1;
switch (src[1])
{
case 0xff9e: /* datakuten (voiced sound) */
if ((*src >= 0xff76 && *src <= 0xff84) || (*src >= 0xff8a && *src <= 0xff8e) || *src == 0x30fd)
*dst += 1;
else if (*src == 0xff73)
*dst = 0x30f4; /* KATAKANA LETTER VU */
else if (*src == 0xff9c)
*dst = 0x30f7; /* KATAKANA LETTER VA */
else if (*src == 0x30f0)
*dst = 0x30f8; /* KATAKANA LETTER VI */
else if (*src == 0x30f1)
*dst = 0x30f9; /* KATAKANA LETTER VE */
else if (*src == 0xff66)
*dst = 0x30fa; /* KATAKANA LETTER VO */
else
return 1;
break;
case 0xff9f: /* handakuten (semi-voiced sound) */
if (*src >= 0xff8a && *src <= 0xff8e)
*dst += 2;
else
return 1;
break;
default:
return 1;
}
return 2;
}
/* map one or two half-width characters to one full-width character */
static int map_to_fullwidth( const WCHAR *src, int srclen, WCHAR *dst )
{
INT n;
if (*src <= '~' && *src > ' ' && *src != '\\')
*dst = *src - 0x20 + 0xff00;
else if (*src == ' ')
*dst = 0x3000;
else if (*src <= 0x00af && *src >= 0x00a2)
{
static const BYTE misc_symbols_table[] =
{
0xe0, 0xe1, 0x00, 0xe5, 0xe4, 0x00, 0x00, /* U+00A2- */
0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0xe3 /* U+00A9- */
};
if (misc_symbols_table[*src - 0x00a2])
*dst = misc_symbols_table[*src - 0x00a2] | 0xff00;
else
*dst = *src;
}
else if (*src == 0x20a9) /* WON SIGN */
*dst = 0xffe6;
else if ((n = compose_katakana(src, srclen, dst)) > 0)
return n;
else if (*src >= 0xffa0 && *src <= 0xffdc)
{
static const BYTE hangul_mapping_table[] =
{
0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* U+FFA0- */
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* U+FFA8- */
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* U+FFB0- */
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x00, /* U+FFB8- */
0x00, 0x00, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, /* U+FFC0- */
0x00, 0x00, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* U+FFC8- */
0x00, 0x00, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, /* U+FFD0- */
0x00, 0x00, 0x61, 0x62, 0x63 /* U+FFD8- */
};
if (hangul_mapping_table[*src - 0xffa0])
*dst = hangul_mapping_table[*src - 0xffa0] | 0x3100;
else
*dst = *src;
}
else
*dst = *src;
return 1;
}
/* decompose a full-width katakana character into one or two half-width characters. */
static int decompose_katakana( WCHAR c, WCHAR *dst, int dstlen )
{
static const BYTE katakana_map[] =
{
/* */ 0x9e, 0x9f, 0x9e, 0x9f, 0x00, 0x00, 0x00, /* U+3099- */
0x00, 0x67, 0x71, 0x68, 0x72, 0x69, 0x73, 0x6a, /* U+30a1- */
0x74, 0x6b, 0x75, 0x76, 0x01, 0x77, 0x01, 0x78, /* U+30a8- */
0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, /* U+30b0- */
0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, /* U+30b8- */
0x01, 0x81, 0x01, 0x6f, 0x82, 0x01, 0x83, 0x01, /* U+30c0- */
0x84, 0x01, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, /* U+30c8- */
0x01, 0x02, 0x8b, 0x01, 0x02, 0x8c, 0x01, 0x02, /* U+30d0- */
0x8d, 0x01, 0x02, 0x8e, 0x01, 0x02, 0x8f, 0x90, /* U+30d8- */
0x91, 0x92, 0x93, 0x6c, 0x94, 0x6d, 0x95, 0x6e, /* U+30e0- */
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x00, 0x9c, /* U+30e8- */
0x00, 0x00, 0x66, 0x9d, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */
0x58, 0x58, 0x08, 0x65, 0x70, 0x00, 0x51 /* U+30f8- */
};
int len = 0, shift = c - 0x3099;
BYTE k;
if (shift < 0 || shift >= ARRAY_SIZE( katakana_map )) return 0;
k = katakana_map[shift];
if (!k)
{
if (dstlen > 0) *dst = c;
len++;
}
else if (k > 0x60)
{
if (dstlen > 0) *dst = k | 0xff00;
len++;
}
else
{
if (dstlen >= 2)
{
dst[0] = (k > 0x50) ? (c - (k & 0xf)) : (katakana_map[shift - k] | 0xff00);
dst[1] = (k == 2) ? 0xff9f : 0xff9e;
}
len += 2;
}
return len;
}
/* map single full-width character to single or double half-width characters. */
static int map_to_halfwidth( WCHAR c, WCHAR *dst, int dstlen )
{
int n = decompose_katakana( c, dst, dstlen );
if (n > 0) return n;
if (c == 0x3000)
*dst = ' ';
else if (c == 0x3001)
*dst = 0xff64;
else if (c == 0x3002)
*dst = 0xff61;
else if (c == 0x300c || c == 0x300d)
*dst = (c - 0x300c) + 0xff62;
else if (c >= 0x3131 && c <= 0x3163)
{
*dst = c - 0x3131 + 0xffa1;
if (*dst >= 0xffbf) *dst += 3;
if (*dst >= 0xffc8) *dst += 2;
if (*dst >= 0xffd0) *dst += 2;
if (*dst >= 0xffd8) *dst += 2;
}
else if (c == 0x3164)
*dst = 0xffa0;
else if (c == 0x2019)
*dst = '\'';
else if (c == 0x201d)
*dst = '"';
else if (c > 0xff00 && c < 0xff5f && c != 0xff3c)
*dst = c - 0xff00 + 0x20;
else if (c >= 0xffe0 && c <= 0xffe6)
{
static const WCHAR misc_symbol_map[] = { 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9 };
*dst = misc_symbol_map[c - 0xffe0];
}
else
*dst = c;
return 1;
}
/* Note: the Internal_ functions are not documented. The number of parameters
* should be correct, but their exact meaning may not.
*/
/******************************************************************************
* Internal_EnumCalendarInfo (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc, LCID lcid, CALID id,
CALTYPE type, BOOL unicode, BOOL ex,
BOOL exex, LPARAM lparam )
{
WCHAR buffer[256];
DWORD optional = 0;
INT ret;
if (!proc)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (id == ENUM_ALL_CALENDARS)
{
if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
(WCHAR *)&id, sizeof(id) / sizeof(WCHAR) )) return FALSE;
if (!GetLocaleInfoW( lcid, LOCALE_IOPTIONALCALENDAR | LOCALE_RETURN_NUMBER,
(WCHAR *)&optional, sizeof(optional) / sizeof(WCHAR) )) optional = 0;
}
for (;;)
{
if (type & CAL_RETURN_NUMBER)
ret = GetCalendarInfoW( lcid, id, type, NULL, 0, (LPDWORD)buffer );
else if (unicode)
ret = GetCalendarInfoW( lcid, id, type, buffer, ARRAY_SIZE(buffer), NULL );
else
{
WCHAR bufW[256];
ret = GetCalendarInfoW( lcid, id, type, bufW, ARRAY_SIZE(bufW), NULL );
if (ret) WideCharToMultiByte( CP_ACP, 0, bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL );
}
if (ret)
{
if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam );
else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id );
else ret = proc( buffer );
}
if (!ret) break;
if (!optional) break;
id = optional;
}
return TRUE;
}
/**************************************************************************
* Internal_EnumDateFormats (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam )
{
WCHAR buffer[256];
LCTYPE lctype;
CALID cal_id;
INT ret;
if (!proc)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (!GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER,
(LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR) ))
return FALSE;
switch (flags & ~LOCALE_USE_CP_ACP)
{
case 0:
case DATE_SHORTDATE:
lctype = LOCALE_SSHORTDATE;
break;
case DATE_LONGDATE:
lctype = LOCALE_SLONGDATE;
break;
case DATE_YEARMONTH:
lctype = LOCALE_SYEARMONTH;
break;
default:
FIXME( "unknown date format 0x%08x\n", flags );
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
lctype |= flags & LOCALE_USE_CP_ACP;
if (unicode)
ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
else
ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
if (ret)
{
if (exex) ((DATEFMT_ENUMPROCEXEX)proc)( buffer, cal_id, lparam );
else if (ex) ((DATEFMT_ENUMPROCEXW)proc)( buffer, cal_id );
else proc( buffer );
}
return TRUE;
}
/******************************************************************************
* Internal_EnumLanguageGroupLocales (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
DWORD flags, LONG_PTR param, BOOL unicode )
{
WCHAR name[10], value[10];
DWORD name_len, value_len, type, index = 0, alt = 0;
HKEY key, altkey;
LCID lcid;
if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
for (;;)
{
name_len = ARRAY_SIZE(name);
value_len = sizeof(value);
if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL,
&type, (BYTE *)value, &value_len ))
{
if (alt++) break;
index = 0;
continue;
}
if (type != REG_SZ) continue;
if (id != wcstoul( value, NULL, 16 )) continue;
lcid = wcstoul( name, NULL, 16 );
if (!unicode)
{
char nameA[10];
WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break;
}
else if (!proc( id, lcid, name, param )) break;
}
RegCloseKey( altkey );
RegCloseKey( key );
return TRUE;
}
/***********************************************************************
* Internal_EnumSystemCodePages (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags,
BOOL unicode )
{
WCHAR name[10];
DWORD name_len, type, index = 0;
HKEY key;
if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE;
for (;;)
{
name_len = ARRAY_SIZE(name);
if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
if (type != REG_SZ) continue;
if (!wcstoul( name, NULL, 10 )) continue;
if (!unicode)
{
char nameA[10];
WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break;
}
else if (!proc( name )) break;
}
RegCloseKey( key );
return TRUE;
}
/******************************************************************************
* Internal_EnumSystemLanguageGroups (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc,
DWORD flags, LONG_PTR param, BOOL unicode )
{
WCHAR name[10], value[10], descr[80];
DWORD name_len, value_len, type, index = 0;
HKEY key;
LGRPID id;
if (!proc)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (flags)
{
case 0:
flags = LGRPID_INSTALLED;
break;
case LGRPID_INSTALLED:
case LGRPID_SUPPORTED:
break;
default:
SetLastError( ERROR_INVALID_FLAGS );
return FALSE;
}
if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
for (;;)
{
name_len = ARRAY_SIZE(name);
value_len = sizeof(value);
if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break;
if (type != REG_SZ) continue;
id = wcstoul( name, NULL, 16 );
if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue;
if (!LoadStringW( kernel32_handle, 0x2000 + id, descr, ARRAY_SIZE(descr) )) descr[0] = 0;
TRACE( "%p: %u %s %s %x %lx\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param );
if (!unicode)
{
char nameA[10], descrA[80];
WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL );
if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break;
}
else if (!proc( id, name, descr, flags, param )) break;
}
RegCloseKey( key );
return TRUE;
}
/**************************************************************************
* Internal_EnumTimeFormats (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags,
BOOL unicode, BOOL ex, LPARAM lparam )
{
WCHAR buffer[256];
LCTYPE lctype;
INT ret;
if (!proc)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (flags & ~LOCALE_USE_CP_ACP)
{
case 0:
lctype = LOCALE_STIMEFORMAT;
break;
case TIME_NOSECONDS:
lctype = LOCALE_SSHORTTIME;
break;
default:
FIXME( "Unknown time format %x\n", flags );
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
lctype |= flags & LOCALE_USE_CP_ACP;
if (unicode)
ret = GetLocaleInfoW( lcid, lctype, buffer, ARRAY_SIZE(buffer) );
else
ret = GetLocaleInfoA( lcid, lctype, (char *)buffer, sizeof(buffer) );
if (ret)
{
if (ex) ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam );
else proc( buffer );
}
return TRUE;
}
/******************************************************************************
* Internal_EnumUILanguages (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags,
LONG_PTR param, BOOL unicode )
{
WCHAR name[10];
DWORD name_len, type, index = 0;
HKEY key;
if (!proc)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (flags & ~MUI_LANGUAGE_ID)
{
SetLastError( ERROR_INVALID_FLAGS );
return FALSE;
}
if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
for (;;)
{
name_len = ARRAY_SIZE(name);
if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
if (type != REG_SZ) continue;
if (!wcstoul( name, NULL, 16 )) continue;
if (!unicode)
{
char nameA[10];
WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL );
if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break;
}
else if (!proc( name, param )) break;
}
RegCloseKey( key );
return TRUE;
}
/******************************************************************************
* CompareStringOrdinal (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1,
const WCHAR *str2, INT len2, BOOL ignore_case )
{
int ret;
if (!str1 || !str2)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (len1 < 0) len1 = lstrlenW( str1 );
if (len2 < 0) len2 = lstrlenW( str2 );
ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case );
if (ret < 0) return CSTR_LESS_THAN;
if (ret > 0) return CSTR_GREATER_THAN;
return CSTR_EQUAL;
}
/******************************************************************************
* ConvertDefaultLocale (kernelbase.@)
*/
LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid )
{
switch (lcid)
{
case LOCALE_INVARIANT:
return lcid; /* keep as-is */
case LOCALE_SYSTEM_DEFAULT:
return GetSystemDefaultLCID();
case LOCALE_USER_DEFAULT:
case LOCALE_NEUTRAL:
return GetUserDefaultLCID();
case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
case MAKELANGID( LANG_CHINESE, 0x1e ):
return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
case MAKELANGID( LANG_CHINESE, 0x1f ):
return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
case MAKELANGID( LANG_IRISH, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_IRISH, SUBLANG_IRISH_IRELAND );
case MAKELANGID( LANG_BENGALI, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH );
case MAKELANGID( LANG_SINDHI, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_SINDHI, SUBLANG_SINDHI_AFGHANISTAN );
case MAKELANGID( LANG_INUKTITUT, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN );
case MAKELANGID( LANG_TAMAZIGHT, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN );
case MAKELANGID( LANG_FULAH, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_FULAH, SUBLANG_FULAH_SENEGAL );
case MAKELANGID( LANG_TIGRINYA, SUBLANG_NEUTRAL ):
return MAKELANGID( LANG_TIGRINYA, SUBLANG_TIGRINYA_ERITREA );
default:
/* Replace SUBLANG_NEUTRAL with SUBLANG_DEFAULT */
if (SUBLANGID(lcid) == SUBLANG_NEUTRAL && SORTIDFROMLCID(lcid) == SORT_DEFAULT)
lcid = MAKELANGID( PRIMARYLANGID(lcid), SUBLANG_DEFAULT );
break;
}
return lcid;
}
/******************************************************************************
* EnumCalendarInfoW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid,
CALID id, CALTYPE type )
{
return Internal_EnumCalendarInfo( proc, lcid, id, type, TRUE, FALSE, FALSE, 0 );
}
/******************************************************************************
* EnumCalendarInfoExW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid,
CALID id, CALTYPE type )
{
return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, FALSE, 0 );
}
/******************************************************************************
* EnumCalendarInfoExEx (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id,
LPCWSTR reserved, CALTYPE type, LPARAM lparam )
{
LCID lcid = LocaleNameToLCID( locale, 0 );
return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, lcid, id, type, TRUE, TRUE, TRUE, lparam );
}
/**************************************************************************
* EnumDateFormatsW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
{
return Internal_EnumDateFormats( proc, lcid, flags, TRUE, FALSE, FALSE, 0 );
}
/**************************************************************************
* EnumDateFormatsExW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags )
{
return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, FALSE, 0 );
}
/**************************************************************************
* EnumDateFormatsExEx (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale,
DWORD flags, LPARAM lparam )
{
LCID lcid = LocaleNameToLCID( locale, 0 );
return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, TRUE, lparam );
}
/******************************************************************************
* EnumLanguageGroupLocalesW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id,
DWORD flags, LONG_PTR param )
{
return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE );
}
/******************************************************************************
* EnumUILanguagesW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param )
{
return Internal_EnumUILanguages( proc, flags, param, TRUE );
}
/***********************************************************************
* EnumSystemCodePagesW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags )
{
return Internal_EnumSystemCodePages( proc, flags, TRUE );
}
/******************************************************************************
* EnumSystemLanguageGroupsW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc,
DWORD flags, LONG_PTR param )
{
return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE );
}
/******************************************************************************
* EnumSystemLocalesA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags )
{
char name[10];
DWORD name_len, type, index = 0;
HKEY key;
if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
for (;;)
{
name_len = ARRAY_SIZE(name);
if (RegEnumValueA( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
if (type != REG_SZ) continue;
if (!strtoul( name, NULL, 16 )) continue;
if (!proc( name )) break;
}
RegCloseKey( key );
return TRUE;
}
/******************************************************************************
* EnumSystemLocalesW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags )
{
WCHAR name[10];
DWORD name_len, type, index = 0;
HKEY key;
if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
for (;;)
{
name_len = ARRAY_SIZE(name);
if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break;
if (type != REG_SZ) continue;
if (!wcstoul( name, NULL, 16 )) continue;
if (!proc( name )) break;
}
RegCloseKey( key );
return TRUE;
}
/******************************************************************************
* EnumSystemLocalesEx (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags,
LPARAM param, void *reserved )
{
WCHAR buffer[256], name[10];
DWORD name_len, type, neutral, flags, index = 0, alt = 0;
HKEY key, altkey;
LCID lcid;
if (reserved)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE;
if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0;
for (;;)
{
name_len = ARRAY_SIZE(name);
if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL, &type, NULL, NULL ))
{
if (alt++) break;
index = 0;
continue;
}
if (type != REG_SZ) continue;
if (!(lcid = wcstoul( name, NULL, 16 ))) continue;
GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, buffer, ARRAY_SIZE( buffer ));
if (!GetLocaleInfoW( lcid, LOCALE_INEUTRAL | LOCALE_NOUSEROVERRIDE | LOCALE_RETURN_NUMBER,
(LPWSTR)&neutral, sizeof(neutral) / sizeof(WCHAR) ))
neutral = 0;
if (alt)
flags = LOCALE_ALTERNATE_SORTS;
else
flags = LOCALE_WINDOWS | (neutral ? LOCALE_NEUTRALDATA : LOCALE_SPECIFICDATA);
if (wanted_flags && !(flags & wanted_flags)) continue;
if (!proc( buffer, flags, param )) break;
}
RegCloseKey( altkey );
RegCloseKey( key );
return TRUE;
}
/**************************************************************************
* EnumTimeFormatsW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags )
{
return Internal_EnumTimeFormats( proc, lcid, flags, TRUE, FALSE, 0 );
}
/**************************************************************************
* EnumTimeFormatsEx (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale,
DWORD flags, LPARAM lparam )
{
LCID lcid = LocaleNameToLCID( locale, 0 );
return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, lcid, flags, TRUE, TRUE, lparam );
}
/******************************************************************************
* FindStringOrdinal (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size,
const WCHAR *val, INT val_size, BOOL ignore_case )
{
INT offset, inc, count;
TRACE( "%#x %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size,
wine_dbgstr_w(val), val_size, ignore_case );
if (!src || !val)
{
SetLastError( ERROR_INVALID_PARAMETER );
return -1;
}
if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH)
{
SetLastError( ERROR_INVALID_FLAGS );
return -1;
}
if (src_size == -1) src_size = lstrlenW( src );
if (val_size == -1) val_size = lstrlenW( val );
SetLastError( ERROR_SUCCESS );
src_size -= val_size;
if (src_size < 0) return -1;
count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1;
offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size;
inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1;
while (count--)
{
if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL)
return offset;
offset += inc;
}
return -1;
}
/******************************************************************************
* FoldStringW (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen )
{
WCHAR *tmp;
int ret;
if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (srclen == -1) srclen = lstrlenW(src) + 1;
switch (flags)
{
case MAP_PRECOMPOSED:
return NormalizeString( NormalizationC, src, srclen, dst, dstlen );
case MAP_FOLDCZONE:
case MAP_PRECOMPOSED | MAP_FOLDCZONE:
return NormalizeString( NormalizationKC, src, srclen, dst, dstlen );
case MAP_COMPOSITE:
return NormalizeString( NormalizationD, src, srclen, dst, dstlen );
case MAP_COMPOSITE | MAP_FOLDCZONE:
return NormalizeString( NormalizationKD, src, srclen, dst, dstlen );
case MAP_FOLDDIGITS:
return fold_digits( src, srclen, dst, dstlen );
case MAP_EXPAND_LIGATURES:
case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE:
return expand_ligatures( src, srclen, dst, dstlen );
case MAP_FOLDDIGITS | MAP_PRECOMPOSED:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationC, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_FOLDCZONE:
case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationKC, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_COMPOSITE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationD, tmp, srclen, dst, dstlen );
break;
case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = NormalizeString( NormalizationKD, tmp, srclen, dst, dstlen );
break;
case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS:
case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE:
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) break;
if (!(ret = fold_digits( src, srclen, tmp, srclen ))) break;
ret = expand_ligatures( tmp, srclen, dst, dstlen );
break;
default:
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!tmp)
{
SetLastError( ERROR_OUTOFMEMORY );
return 0;
}
RtlFreeHeap( GetProcessHeap(), 0, tmp );
return ret;
}
/******************************************************************************
* GetACP (kernelbase.@)
*/
UINT WINAPI GetACP(void)
{
return nls_info.AnsiTableInfo.CodePage;
}
/***********************************************************************
* GetCPInfo (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo )
{
const CPTABLEINFO *table;
if (!cpinfo)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (codepage)
{
case CP_UTF7:
case CP_UTF8:
cpinfo->DefaultChar[0] = 0x3f;
cpinfo->DefaultChar[1] = 0;
cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
cpinfo->MaxCharSize = (codepage == CP_UTF7) ? 5 : 4;
break;
default:
if (!(table = get_codepage_table( codepage ))) return FALSE;
cpinfo->MaxCharSize = table->MaximumCharacterSize;
memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
break;
}
return TRUE;
}
/***********************************************************************
* GetCPInfoExW (kernelbase.@)
*/
BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo )
{
const CPTABLEINFO *table;
int min, max, pos;
if (!cpinfo)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
switch (codepage)
{
case CP_UTF7:
cpinfo->DefaultChar[0] = 0x3f;
cpinfo->DefaultChar[1] = 0;
cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
cpinfo->MaxCharSize = 5;
cpinfo->CodePage = CP_UTF7;
cpinfo->UnicodeDefaultChar = 0x3f;
break;
case CP_UTF8:
cpinfo->DefaultChar[0] = 0x3f;
cpinfo->DefaultChar[1] = 0;
cpinfo->LeadByte[0] = cpinfo->LeadByte[1] = 0;
cpinfo->MaxCharSize = 4;
cpinfo->CodePage = CP_UTF8;
cpinfo->UnicodeDefaultChar = 0x3f;
break;
default:
if (!(table = get_codepage_table( codepage ))) return FALSE;
cpinfo->MaxCharSize = table->MaximumCharacterSize;
memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) );
memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) );
cpinfo->CodePage = table->CodePage;
cpinfo->UnicodeDefaultChar = table->UniDefaultChar;
break;
}
min = 0;
max = ARRAY_SIZE(codepage_names) - 1;
cpinfo->CodePageName[0] = 0;
while (min <= max)
{
pos = (min + max) / 2;
if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1;
else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1;
else
{
wcscpy( cpinfo->CodePageName, codepage_names[pos].name );
break;
}
}
return TRUE;
}
/***********************************************************************
* GetCalendarInfoW (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type,
WCHAR *data, INT count, DWORD *value )
{
static const LCTYPE lctype_map[] =
{
0, /* not used */
0, /* CAL_ICALINTVALUE */
0, /* CAL_SCALNAME */
0, /* CAL_IYEAROFFSETRANGE */
0, /* CAL_SERASTRING */
LOCALE_SSHORTDATE,
LOCALE_SLONGDATE,
LOCALE_SDAYNAME1,
LOCALE_SDAYNAME2,
LOCALE_SDAYNAME3,
LOCALE_SDAYNAME4,
LOCALE_SDAYNAME5,
LOCALE_SDAYNAME6,
LOCALE_SDAYNAME7,
LOCALE_SABBREVDAYNAME1,
LOCALE_SABBREVDAYNAME2,
LOCALE_SABBREVDAYNAME3,
LOCALE_SABBREVDAYNAME4,
LOCALE_SABBREVDAYNAME5,
LOCALE_SABBREVDAYNAME6,
LOCALE_SABBREVDAYNAME7,
LOCALE_SMONTHNAME1,
LOCALE_SMONTHNAME2,
LOCALE_SMONTHNAME3,
LOCALE_SMONTHNAME4,
LOCALE_SMONTHNAME5,
LOCALE_SMONTHNAME6,
LOCALE_SMONTHNAME7,
LOCALE_SMONTHNAME8,
LOCALE_SMONTHNAME9,
LOCALE_SMONTHNAME10,
LOCALE_SMONTHNAME11,
LOCALE_SMONTHNAME12,
LOCALE_SMONTHNAME13,
LOCALE_SABBREVMONTHNAME1,
LOCALE_SABBREVMONTHNAME2,
LOCALE_SABBREVMONTHNAME3,
LOCALE_SABBREVMONTHNAME4,
LOCALE_SABBREVMONTHNAME5,
LOCALE_SABBREVMONTHNAME6,
LOCALE_SABBREVMONTHNAME7,
LOCALE_SABBREVMONTHNAME8,
LOCALE_SABBREVMONTHNAME9,
LOCALE_SABBREVMONTHNAME10,
LOCALE_SABBREVMONTHNAME11,
LOCALE_SABBREVMONTHNAME12,
LOCALE_SABBREVMONTHNAME13,
LOCALE_SYEARMONTH,
0, /* CAL_ITWODIGITYEARMAX */
LOCALE_SSHORTESTDAYNAME1,
LOCALE_SSHORTESTDAYNAME2,
LOCALE_SSHORTESTDAYNAME3,
LOCALE_SSHORTESTDAYNAME4,
LOCALE_SSHORTESTDAYNAME5,
LOCALE_SSHORTESTDAYNAME6,
LOCALE_SSHORTESTDAYNAME7,
LOCALE_SMONTHDAY,
0, /* CAL_SABBREVERASTRING */
};
DWORD flags = 0;
CALTYPE calinfo = type & 0xffff;
if (type & CAL_NOUSEROVERRIDE) FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
if (type & CAL_USE_CP_ACP) FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
if ((type & CAL_RETURN_NUMBER) && !value)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (type & CAL_RETURN_GENITIVE_NAMES) flags |= LOCALE_RETURN_GENITIVE_NAMES;
switch (calinfo)
{
case CAL_ICALINTVALUE:
if (type & CAL_RETURN_NUMBER)
return GetLocaleInfoW( lcid, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE,
(WCHAR *)value, sizeof(*value) / sizeof(WCHAR) );
return GetLocaleInfoW( lcid, LOCALE_ICALENDARTYPE, data, count );
case CAL_SCALNAME:
FIXME( "Unimplemented caltype %d\n", calinfo );
if (data) *data = 0;
return 1;
case CAL_IYEAROFFSETRANGE:
case CAL_SERASTRING:
case CAL_SABBREVERASTRING:
FIXME( "Unimplemented caltype %d\n", calinfo );
return 0;
case CAL_SSHORTDATE:
case CAL_SLONGDATE:
case CAL_SDAYNAME1:
case CAL_SDAYNAME2:
case CAL_SDAYNAME3:
case CAL_SDAYNAME4:
case CAL_SDAYNAME5:
case CAL_SDAYNAME6:
case CAL_SDAYNAME7:
case CAL_SABBREVDAYNAME1:
case CAL_SABBREVDAYNAME2:
case CAL_SABBREVDAYNAME3:
case CAL_SABBREVDAYNAME4:
case CAL_SABBREVDAYNAME5:
case CAL_SABBREVDAYNAME6:
case CAL_SABBREVDAYNAME7:
case CAL_SMONTHNAME1:
case CAL_SMONTHNAME2:
case CAL_SMONTHNAME3:
case CAL_SMONTHNAME4:
case CAL_SMONTHNAME5:
case CAL_SMONTHNAME6:
case CAL_SMONTHNAME7:
case CAL_SMONTHNAME8:
case CAL_SMONTHNAME9:
case CAL_SMONTHNAME10:
case CAL_SMONTHNAME11:
case CAL_SMONTHNAME12:
case CAL_SMONTHNAME13:
case CAL_SABBREVMONTHNAME1:
case CAL_SABBREVMONTHNAME2:
case CAL_SABBREVMONTHNAME3:
case CAL_SABBREVMONTHNAME4:
case CAL_SABBREVMONTHNAME5:
case CAL_SABBREVMONTHNAME6:
case CAL_SABBREVMONTHNAME7:
case CAL_SABBREVMONTHNAME8:
case CAL_SABBREVMONTHNAME9:
case CAL_SABBREVMONTHNAME10:
case CAL_SABBREVMONTHNAME11:
case CAL_SABBREVMONTHNAME12:
case CAL_SABBREVMONTHNAME13:
case CAL_SMONTHDAY:
case CAL_SYEARMONTH:
case CAL_SSHORTESTDAYNAME1:
case CAL_SSHORTESTDAYNAME2:
case CAL_SSHORTESTDAYNAME3:
case CAL_SSHORTESTDAYNAME4:
case CAL_SSHORTESTDAYNAME5:
case CAL_SSHORTESTDAYNAME6:
case CAL_SSHORTESTDAYNAME7:
return GetLocaleInfoW( lcid, lctype_map[calinfo] | flags, data, count );
case CAL_ITWODIGITYEARMAX:
if (type & CAL_RETURN_NUMBER)
{
*value = CALINFO_MAX_YEAR;
return sizeof(DWORD) / sizeof(WCHAR);
}
else
{
WCHAR buffer[10];
int ret = swprintf( buffer, ARRAY_SIZE(buffer), L"%u", CALINFO_MAX_YEAR ) + 1;
if (!data) return ret;
if (ret <= count)
{
lstrcpyW( data, buffer );
return ret;
}
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
break;
default:
FIXME( "Unknown caltype %d\n", calinfo );
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
return 0;
}
/***********************************************************************
* GetCalendarInfoEx (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *locale, CALID calendar, const WCHAR *reserved,
CALTYPE type, WCHAR *data, INT count, DWORD *value )
{
LCID lcid = LocaleNameToLCID( locale, 0 );
return GetCalendarInfoW( lcid, calendar, type, data, count, value );
}
/******************************************************************************
* GetLocaleInfoA (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len )
{
WCHAR *bufferW;
INT lenW, ret;
TRACE( "lcid=0x%x lctype=0x%x %p %d\n", lcid, lctype, buffer, len );
if (len < 0 || (len && !buffer))
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER))
return GetLocaleInfoW( lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) ) * sizeof(WCHAR);
if (!(lenW = GetLocaleInfoW( lcid, lctype, NULL, 0 ))) return 0;
if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) )))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return 0;
}
ret = GetLocaleInfoW( lcid, lctype, bufferW, lenW );
if (ret) ret = WideCharToMultiByte( get_lcid_codepage( lcid, lctype ), 0,
bufferW, ret, buffer, len, NULL, NULL );
RtlFreeHeap( GetProcessHeap(), 0, bufferW );
return ret;
}
/******************************************************************************
* GetLocaleInfoW (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len )
{
HRSRC hrsrc;
HGLOBAL hmem;
INT ret;
UINT lcflags = lctype;
const WCHAR *p;
unsigned int i;
if (len < 0 || (len && !buffer))
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (lctype & LOCALE_RETURN_GENITIVE_NAMES && !is_genitive_name_supported( lctype ))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!len) buffer = NULL;
lcid = ConvertDefaultLocale( lcid );
lctype = LOWORD(lctype);
TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d)\n", lcid, lctype, buffer, len );
/* first check for overrides in the registry */
if (!(lcflags & LOCALE_NOUSEROVERRIDE) && lcid == ConvertDefaultLocale( LOCALE_USER_DEFAULT ))
{
const struct registry_value *value = get_locale_registry_value( lctype );
if (value)
{
if (lcflags & LOCALE_RETURN_NUMBER)
{
WCHAR tmp[16];
ret = get_registry_locale_info( value, tmp, ARRAY_SIZE( tmp ));
if (ret > 0)
{
WCHAR *end;
UINT number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
if (*end) /* invalid number */
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
ret = sizeof(UINT) / sizeof(WCHAR);
if (!len) return ret;
if (ret > len)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
memcpy( buffer, &number, sizeof(number) );
}
}
else ret = get_registry_locale_info( value, buffer, len );
if (ret != -1) return ret;
}
}
/* now load it from kernel resources */
if (!(hrsrc = FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
ULongToPtr((lctype >> 4) + 1), lcid )))
{
SetLastError( ERROR_INVALID_FLAGS ); /* no such lctype */
return 0;
}
if (!(hmem = LoadResource( kernel32_handle, hrsrc ))) return 0;
p = LockResource( hmem );
for (i = 0; i < (lctype & 0x0f); i++) p += *p + 1;
if (lcflags & LOCALE_RETURN_NUMBER) ret = sizeof(UINT) / sizeof(WCHAR);
else if (is_genitive_name_supported( lctype ) && *p)
{
/* genitive form is stored after a null separator from a nominative */
for (i = 1; i <= *p; i++) if (!p[i]) break;
if (i <= *p && (lcflags & LOCALE_RETURN_GENITIVE_NAMES))
{
ret = *p - i + 1;
p += i;
}
else ret = i;
}
else
ret = (lctype == LOCALE_FONTSIGNATURE) ? *p : *p + 1;
if (!len) return ret;
if (ret > len)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
if (lcflags & LOCALE_RETURN_NUMBER)
{
UINT number;
WCHAR *end, *tmp = HeapAlloc( GetProcessHeap(), 0, (*p + 1) * sizeof(WCHAR) );
if (!tmp) return 0;
memcpy( tmp, p + 1, *p * sizeof(WCHAR) );
tmp[*p] = 0;
number = wcstol( tmp, &end, get_value_base_by_lctype( lctype ) );
if (!*end)
memcpy( buffer, &number, sizeof(number) );
else /* invalid number */
{
SetLastError( ERROR_INVALID_FLAGS );
ret = 0;
}
HeapFree( GetProcessHeap(), 0, tmp );
TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning number %d\n",
lcid, lctype, buffer, len, number );
}
else
{
memcpy( buffer, p + 1, ret * sizeof(WCHAR) );
if (lctype != LOCALE_FONTSIGNATURE) buffer[ret-1] = 0;
TRACE( "(lcid=0x%x,lctype=0x%x,%p,%d) returning %d %s\n",
lcid, lctype, buffer, len, ret, debugstr_w(buffer) );
}
return ret;
}
/******************************************************************************
* GetLocaleInfoEx (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *locale, LCTYPE info, WCHAR *buffer, INT len )
{
LCID lcid = LocaleNameToLCID( locale, 0 );
TRACE( "%s lcid=0x%x 0x%x\n", debugstr_w(locale), lcid, info );
if (!lcid) return 0;
/* special handling for neutral locale names */
if (locale && lstrlenW( locale ) == 2)
{
switch (LOWORD( info ))
{
case LOCALE_SNAME:
if (len && len < 3)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
if (len) lstrcpyW( buffer, locale );
return 3;
case LOCALE_SPARENT:
if (len) buffer[0] = 0;
return 1;
}
}
return GetLocaleInfoW( lcid, info, buffer, len );
}
/******************************************************************************
* GetOEMCP (kernelbase.@)
*/
UINT WINAPI GetOEMCP(void)
{
return nls_info.OemTableInfo.CodePage;
}
/***********************************************************************
* GetStringTypeA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count,
WORD *chartype )
{
UINT cp;
INT countW;
LPWSTR srcW;
BOOL ret = FALSE;
if (count == -1) count = strlen(src) + 1;
cp = get_lcid_codepage( locale, 0 );
countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0);
if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
{
MultiByteToWideChar(cp, 0, src, count, srcW, countW);
/*
* NOTE: the target buffer has 1 word for each CHARACTER in the source
* string, with multibyte characters there maybe be more bytes in count
* than character space in the buffer!
*/
ret = GetStringTypeW(type, srcW, countW, chartype);
HeapFree(GetProcessHeap(), 0, srcW);
}
return ret;
}
/***********************************************************************
* GetStringTypeW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype )
{
if (!src)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (count == -1) count = lstrlenW(src) + 1;
switch (type)
{
case CT_CTYPE1:
while (count--) *chartype++ = get_table_entry( wctype_table, *src++ ) & 0xfff;
break;
case CT_CTYPE2:
while (count--) *chartype++ = get_table_entry( wctype_table, *src++ ) >> 12;
break;
case CT_CTYPE3:
{
WARN("CT_CTYPE3: semi-stub.\n");
while (count--)
{
int c = *src;
WORD type1, type3 = 0; /* C3_NOTAPPLICABLE */
type1 = get_table_entry( wctype_table, *src++ ) & 0xfff;
/* try to construct type3 from type1 */
if(type1 & C1_SPACE) type3 |= C3_SYMBOL;
if(type1 & C1_ALPHA) type3 |= C3_ALPHA;
if ((c>=0x30A0)&&(c<=0x30FF)) type3 |= C3_KATAKANA;
if ((c>=0x3040)&&(c<=0x309F)) type3 |= C3_HIRAGANA;
if ((c>=0x4E00)&&(c<=0x9FAF)) type3 |= C3_IDEOGRAPH;
if (c == 0x0640) type3 |= C3_KASHIDA;
if ((c>=0x3000)&&(c<=0x303F)) type3 |= C3_SYMBOL;
if ((c>=0xD800)&&(c<=0xDBFF)) type3 |= C3_HIGHSURROGATE;
if ((c>=0xDC00)&&(c<=0xDFFF)) type3 |= C3_LOWSURROGATE;
if ((c>=0xFF00)&&(c<=0xFF60)) type3 |= C3_FULLWIDTH;
if ((c>=0xFF00)&&(c<=0xFF20)) type3 |= C3_SYMBOL;
if ((c>=0xFF3B)&&(c<=0xFF40)) type3 |= C3_SYMBOL;
if ((c>=0xFF5B)&&(c<=0xFF60)) type3 |= C3_SYMBOL;
if ((c>=0xFF21)&&(c<=0xFF3A)) type3 |= C3_ALPHA;
if ((c>=0xFF41)&&(c<=0xFF5A)) type3 |= C3_ALPHA;
if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_FULLWIDTH;
if ((c>=0xFFE0)&&(c<=0xFFE6)) type3 |= C3_SYMBOL;
if ((c>=0xFF61)&&(c<=0xFFDC)) type3 |= C3_HALFWIDTH;
if ((c>=0xFF61)&&(c<=0xFF64)) type3 |= C3_SYMBOL;
if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_KATAKANA;
if ((c>=0xFF65)&&(c<=0xFF9F)) type3 |= C3_ALPHA;
if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_HALFWIDTH;
if ((c>=0xFFE8)&&(c<=0xFFEE)) type3 |= C3_SYMBOL;
*chartype++ = type3;
}
break;
}
default:
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
return TRUE;
}
/***********************************************************************
* GetStringTypeExW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count,
WORD *chartype )
{
/* locale is ignored for Unicode */
return GetStringTypeW( type, src, count, chartype );
}
/***********************************************************************
* GetSystemDefaultLCID (kernelbase.@)
*/
LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void)
{
LCID lcid;
NtQueryDefaultLocale( FALSE, &lcid );
return lcid;
}
/***********************************************************************
* GetSystemDefaultLangID (kernelbase.@)
*/
LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void)
{
return LANGIDFROMLCID( GetSystemDefaultLCID() );
}
/***********************************************************************
* GetSystemDefaultLocaleName (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT len )
{
return LCIDToLocaleName( GetSystemDefaultLCID(), name, len, 0 );
}
/***********************************************************************
* GetSystemDefaultUILanguage (kernelbase.@)
*/
LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void)
{
LANGID lang;
NtQueryInstallUILanguage( &lang );
return lang;
}
/***********************************************************************
* GetUserDefaultLCID (kernelbase.@)
*/
LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void)
{
LCID lcid;
NtQueryDefaultLocale( TRUE, &lcid );
return lcid;
}
/***********************************************************************
* GetUserDefaultLangID (kernelbase.@)
*/
LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void)
{
return LANGIDFROMLCID( GetUserDefaultLCID() );
}
/***********************************************************************
* GetUserDefaultLocaleName (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len )
{
return LCIDToLocaleName( GetUserDefaultLCID(), name, len, 0 );
}
/***********************************************************************
* GetUserDefaultUILanguage (kernelbase.@)
*/
LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void)
{
LANGID lang;
NtQueryDefaultUILanguage( &lang );
return lang;
}
/******************************************************************************
* IsCharAlphaA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c )
{
WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
return !!(get_table_entry( wctype_table, wc ) & C1_ALPHA);
}
/******************************************************************************
* IsCharAlphaW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_ALPHA);
}
/******************************************************************************
* IsCharAlphaNumericA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c )
{
WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
return !!(get_table_entry( wctype_table, wc ) & (C1_ALPHA | C1_DIGIT));
}
/******************************************************************************
* IsCharAlphaNumericW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & (C1_ALPHA | C1_DIGIT));
}
/******************************************************************************
* IsCharBlankW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_BLANK);
}
/******************************************************************************
* IsCharCntrlW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_CNTRL);
}
/******************************************************************************
* IsCharDigitW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_DIGIT);
}
/******************************************************************************
* IsCharLowerA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c )
{
WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
return !!(get_table_entry( wctype_table, wc ) & C1_LOWER);
}
/******************************************************************************
* IsCharLowerW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_LOWER);
}
/******************************************************************************
* IsCharPunctW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_PUNCT);
}
/******************************************************************************
* IsCharSpaceA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c )
{
WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
return !!(get_table_entry( wctype_table, wc ) & C1_SPACE);
}
/******************************************************************************
* IsCharSpaceW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_SPACE);
}
/******************************************************************************
* IsCharUpperA (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c )
{
WCHAR wc = nls_info.AnsiTableInfo.MultiByteTable[(unsigned char)c];
return !!(get_table_entry( wctype_table, wc ) & C1_UPPER);
}
/******************************************************************************
* IsCharUpperW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_UPPER);
}
/******************************************************************************
* IsCharXDigitW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc )
{
return !!(get_table_entry( wctype_table, wc ) & C1_XDIGIT);
}
/******************************************************************************
* IsDBCSLeadByte (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar )
{
return nls_info.AnsiTableInfo.DBCSCodePage && nls_info.AnsiTableInfo.DBCSOffsets[testchar];
}
/******************************************************************************
* IsDBCSLeadByteEx (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar )
{
const CPTABLEINFO *table = get_codepage_table( codepage );
return table && table->DBCSCodePage && table->DBCSOffsets[testchar];
}
/******************************************************************************
* IsNormalizedString (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len )
{
BOOLEAN res;
if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE;
return res;
}
/******************************************************************************
* IsValidCodePage (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage )
{
switch (codepage)
{
case CP_ACP:
case CP_OEMCP:
case CP_MACCP:
case CP_THREAD_ACP:
return FALSE;
case CP_UTF7:
case CP_UTF8:
return TRUE;
default:
return get_codepage_table( codepage ) != NULL;
}
}
/******************************************************************************
* IsValidLanguageGroup (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags )
{
WCHAR name[10], value[10];
DWORD type, value_len = sizeof(value);
BOOL ret = FALSE;
HKEY key;
if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE;
swprintf( name, ARRAY_SIZE(name), L"%x", id );
if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ)
ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 );
RegCloseKey( key );
return ret;
}
/******************************************************************************
* IsValidLocale (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags )
{
/* check if language is registered in the kernel32 resources */
return FindResourceExW( kernel32_handle, (LPWSTR)RT_STRING,
ULongToPtr( (LOCALE_ILANGUAGE >> 4) + 1 ), LANGIDFROMLCID(lcid)) != 0;
}
/******************************************************************************
* IsValidLocaleName (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale )
{
LCID lcid;
return !RtlLocaleNameToLcid( locale, &lcid, 2 );
}
/***********************************************************************
* LCIDToLocaleName (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags )
{
static int once;
if (flags && !once++) FIXME( "unsupported flags %x\n", flags );
return GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, name, count );
}
/***********************************************************************
* LCMapStringEx (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen,
WCHAR *dst, int dstlen, NLSVERSIONINFO *version,
void *reserved, LPARAM handle )
{
LPWSTR dst_ptr;
INT len;
if (version) FIXME( "unsupported version structure %p\n", version );
if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved );
if (handle)
{
static int once;
if (!once++) FIXME( "unsupported lparam %lx\n", handle );
}
if (!src || !srclen || dstlen < 0)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
/* mutually exclusive flags */
if ((flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) == (LCMAP_LOWERCASE | LCMAP_UPPERCASE) ||
(flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA)) == (LCMAP_HIRAGANA | LCMAP_KATAKANA) ||
(flags & (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) == (LCMAP_HALFWIDTH | LCMAP_FULLWIDTH) ||
(flags & (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE)) == (LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE) ||
!flags)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (!dstlen) dst = NULL;
if (flags & LCMAP_SORTKEY)
{
INT ret;
if (src == dst)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (srclen < 0) srclen = lstrlenW(src);
TRACE( "(%s,0x%08x,%s,%d,%p,%d)\n",
debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
if ((ret = get_sortkey( flags, src, srclen, (char *)dst, dstlen ))) ret++;
else SetLastError( ERROR_INSUFFICIENT_BUFFER );
return ret;
}
/* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
if (flags & SORT_STRINGSORT)
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (((flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS)) &&
(flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))) ||
((flags & (LCMAP_HIRAGANA | LCMAP_KATAKANA | LCMAP_HALFWIDTH | LCMAP_FULLWIDTH)) &&
(flags & (LCMAP_SIMPLIFIED_CHINESE | LCMAP_TRADITIONAL_CHINESE))))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (srclen < 0) srclen = lstrlenW(src) + 1;
TRACE( "(%s,0x%08x,%s,%d,%p,%d)\n",
debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen );
if (!dst) /* return required string length */
{
if (flags & NORM_IGNORESYMBOLS)
{
for (len = 0; srclen; src++, srclen--)
if (!(get_table_entry( wctype_table, *src ) & (C1_PUNCT | C1_SPACE))) len++;
}
else if (flags & LCMAP_FULLWIDTH)
{
for (len = 0; srclen; src++, srclen--, len++)
{
if (compose_katakana( src, srclen, NULL ) == 2)
{
src++;
srclen--;
}
}
}
else if (flags & LCMAP_HALFWIDTH)
{
for (len = 0; srclen; src++, srclen--, len++)
{
WCHAR wch = *src;
/* map Hiragana to Katakana before decomposition if needed */
if ((flags & LCMAP_KATAKANA) &&
((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E))
wch += 0x60;
if (decompose_katakana( wch, NULL, 0 ) == 2) len++;
}
}
else len = srclen;
return len;
}
if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE)))
{
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
if (flags & (NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS))
{
for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--)
{
if ((flags & NORM_IGNORESYMBOLS) && (get_table_entry( wctype_table, *src ) & (C1_PUNCT | C1_SPACE)))
continue;
*dst_ptr++ = *src;
len--;
}
goto done;
}
if (flags & (LCMAP_FULLWIDTH | LCMAP_HALFWIDTH | LCMAP_HIRAGANA | LCMAP_KATAKANA))
{
for (len = dstlen, dst_ptr = dst; len && srclen; src++, srclen--, len--, dst_ptr++)
{
WCHAR wch;
if (flags & LCMAP_FULLWIDTH)
{
/* map half-width character to full-width one,
e.g. U+FF71 -> U+30A2, U+FF8C U+FF9F -> U+30D7. */
if (map_to_fullwidth( src, srclen, &wch ) == 2)
{
src++;
srclen--;
}
}
else wch = *src;
if (flags & LCMAP_KATAKANA)
{
/* map hiragana to katakana, e.g. U+3041 -> U+30A1.
we can't use C3_HIRAGANA as some characters can't map to katakana */
if ((wch >= 0x3041 && wch <= 0x3096) || wch == 0x309D || wch == 0x309E) wch += 0x60;
}
else if (flags & LCMAP_HIRAGANA)
{
/* map katakana to hiragana, e.g. U+30A1 -> U+3041.
we can't use C3_KATAKANA as some characters can't map to hiragana */
if ((wch >= 0x30A1 && wch <= 0x30F6) || wch == 0x30FD || wch == 0x30FE) wch -= 0x60;
}
if (flags & LCMAP_HALFWIDTH)
{
/* map full-width character to half-width one,
e.g. U+30A2 -> U+FF71, U+30D7 -> U+FF8C U+FF9F. */
if (map_to_halfwidth(wch, dst_ptr, len) == 2)
{
len--;
dst_ptr++;
if (!len) break;
}
}
else *dst_ptr = wch;
}
if (!(flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) || srclen) goto done;
srclen = dst_ptr - dst;
src = dst;
}
if (flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
{
USHORT *table = (flags & LCMAP_LOWERCASE) ? nls_info.LowerCaseTable : nls_info.UpperCaseTable;
for (len = dstlen, dst_ptr = dst; srclen && len; src++, srclen--, len--)
*dst_ptr++ = casemap( table, *src );
}
else
{
len = min( srclen, dstlen );
memcpy( dst, src, len * sizeof(WCHAR) );
dst_ptr = dst + len;
srclen -= len;
}
done:
if (srclen)
{
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return 0;
}
return dst_ptr - dst;
}
/***********************************************************************
* LCMapStringA (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen,
char *dst, int dstlen )
{
WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer;
LPWSTR srcW, dstW;
INT ret = 0, srclenW, dstlenW;
UINT locale_cp = CP_ACP;
if (!src || !srclen || dstlen < 0)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
locale_cp = get_lcid_codepage( lcid, flags );
srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 );
if (srclenW) srcW = bufW;
else
{
srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 );
srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) );
if (!srcW)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return 0;
}
MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW );
}
if (flags & LCMAP_SORTKEY)
{
if (src == dst)
{
SetLastError( ERROR_INVALID_FLAGS );
goto done;
}
ret = LCMapStringEx( NULL, flags, srcW, srclenW, (WCHAR *)dst, dstlen, NULL, NULL, 0 );
goto done;
}
if (flags & SORT_STRINGSORT)
{
SetLastError( ERROR_INVALID_FLAGS );
goto done;
}
dstlenW = LCMapStringEx( NULL, flags, srcW, srclenW, NULL, 0, NULL, NULL, 0 );
if (!dstlenW) goto done;
dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) );
if (!dstW)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
goto done;
}
LCMapStringEx( NULL, flags, srcW, srclenW, dstW, dstlenW, NULL, NULL, 0 );
ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL );
HeapFree( GetProcessHeap(), 0, dstW );
done:
if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW );
return ret;
}
/***********************************************************************
* LCMapStringW (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen,
WCHAR *dst, int dstlen )
{
return LCMapStringEx( NULL, flags, src, srclen, dst, dstlen, NULL, NULL, 0 );
}
/***********************************************************************
* LocaleNameToLCID (kernelbase.@)
*/
LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags )
{
LCID lcid;
if (!name) return GetUserDefaultLCID();
if (!set_ntstatus( RtlLocaleNameToLcid( name, &lcid, 2 ))) return 0;
if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES)) lcid = ConvertDefaultLocale( lcid );
return lcid;
}
/******************************************************************************
* MultiByteToWideChar (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen,
WCHAR *dst, INT dstlen )
{
int ret;
if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (srclen < 0) srclen = strlen(src) + 1;
switch (codepage)
{
case CP_SYMBOL:
ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen );
break;
case CP_UTF7:
ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen );
break;
case CP_UTF8:
ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
break;
case CP_UNIXCP:
codepage = __wine_get_unix_codepage();
if (codepage == CP_UTF8)
{
ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen );
#ifdef __APPLE__ /* work around broken Mac OS X filesystem that enforces decomposed Unicode */
if (ret && dstlen) RtlNormalizeString( NormalizationC, dst, ret, dst, &ret );
#endif
break;
}
/* fall through */
default:
ret = mbstowcs_codepage( codepage, flags, src, srclen, dst, dstlen );
break;
}
TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret );
return ret;
}
/******************************************************************************
* NormalizeString (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len,
WCHAR *dst, INT dst_len)
{
set_ntstatus( RtlNormalizeString( form, src, src_len, dst, &dst_len ));
return dst_len;
}
/******************************************************************************
* ResolveLocaleName (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len )
{
FIXME( "stub: %s, %p, %d\n", wine_dbgstr_w(name), buffer, len );
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
return 0;
}
/******************************************************************************
* SetLocaleInfoW (kernelbase.@)
*/
BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data )
{
const struct registry_value *value;
DWORD index;
LSTATUS status;
lctype = LOWORD(lctype);
value = get_locale_registry_value( lctype );
if (!data || !value)
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if (lctype == LOCALE_IDATE || lctype == LOCALE_ILDATE)
{
SetLastError( ERROR_INVALID_FLAGS );
return FALSE;
}
TRACE( "setting %x (%s) to %s\n", lctype, debugstr_w(value->name), debugstr_w(data) );
/* FIXME: should check that data to set is sane */
status = RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)data, (lstrlenW(data)+1)*sizeof(WCHAR) );
index = value - registry_values;
RtlEnterCriticalSection( &locale_section );
HeapFree( GetProcessHeap(), 0, registry_cache[index] );
registry_cache[index] = NULL;
RtlLeaveCriticalSection( &locale_section );
if (lctype == LOCALE_SSHORTDATE || lctype == LOCALE_SLONGDATE)
{
/* Set I-value from S value */
WCHAR *pD, *pM, *pY, buf[2];
pD = wcschr( data, 'd' );
pM = wcschr( data, 'M' );
pY = wcschr( data, 'y' );
if (pD <= pM) buf[0] = '1'; /* D-M-Y */
else if (pY <= pM) buf[0] = '2'; /* Y-M-D */
else buf[0] = '0'; /* M-D-Y */
buf[1] = 0;
lctype = (lctype == LOCALE_SSHORTDATE) ? LOCALE_IDATE : LOCALE_ILDATE;
value = get_locale_registry_value( lctype );
index = value - registry_values;
RegSetValueExW( intl_key, value->name, 0, REG_SZ, (BYTE *)buf, sizeof(buf) );
RtlEnterCriticalSection( &locale_section );
HeapFree( GetProcessHeap(), 0, registry_cache[index] );
registry_cache[index] = NULL;
RtlLeaveCriticalSection( &locale_section );
}
return set_ntstatus( status );
}
/***********************************************************************
* SetCalendarInfoW (kernelbase.@)
*/
INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data )
{
FIXME( "(%08x,%08x,%08x,%s): stub\n", lcid, calendar, type, debugstr_w(data) );
return 0;
}
/***********************************************************************
* VerLanguageNameA (kernelbase.@)
*/
DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size )
{
return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
}
/***********************************************************************
* VerLanguageNameW (kernelbase.@)
*/
DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size )
{
return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size );
}
/***********************************************************************
* WideCharToMultiByte (kernelbase.@)
*/
INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen,
LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used )
{
int ret;
if (!src || !srclen || (!dst && dstlen) || dstlen < 0)
{
SetLastError( ERROR_INVALID_PARAMETER );
return 0;
}
if (srclen < 0) srclen = lstrlenW(src) + 1;
switch (codepage)
{
case CP_SYMBOL:
ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used );
break;
case CP_UTF7:
ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used );
break;
case CP_UTF8:
ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used );
break;
case CP_UNIXCP:
codepage = __wine_get_unix_codepage();
if (codepage == CP_UTF8)
{
if (used) *used = FALSE;
ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, NULL, NULL );
break;
}
/* fall through */
default:
ret = wcstombs_codepage( codepage, flags, src, srclen, dst, dstlen, defchar, used );
break;
}
TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret );
return ret;
}