Commit 85d42816 authored by Dmitry Timoshkov's avatar Dmitry Timoshkov Committed by Alexandre Julliard

Implement LCMapString using unicode collation tables.

Move CompareString and LCMapString to dlls/kernel/locale.c.
parent 4cb21206
......@@ -16344,8 +16344,6 @@ esac
ac_config_commands="$ac_config_commands objects"
ac_config_commands="$ac_config_commands ole"
ac_config_commands="$ac_config_commands programs/regapi/tests"
ac_config_commands="$ac_config_commands programs/regedit/tests"
......@@ -17118,7 +17116,6 @@ do
"misc" ) CONFIG_COMMANDS="$CONFIG_COMMANDS misc" ;;
"msdos" ) CONFIG_COMMANDS="$CONFIG_COMMANDS msdos" ;;
"objects" ) CONFIG_COMMANDS="$CONFIG_COMMANDS objects" ;;
"ole" ) CONFIG_COMMANDS="$CONFIG_COMMANDS ole" ;;
"programs/regapi/tests" ) CONFIG_COMMANDS="$CONFIG_COMMANDS programs/regapi/tests" ;;
"programs/regedit/tests" ) CONFIG_COMMANDS="$CONFIG_COMMANDS programs/regedit/tests" ;;
"relay32" ) CONFIG_COMMANDS="$CONFIG_COMMANDS relay32" ;;
......@@ -17857,8 +17854,6 @@ echo "$as_me: creating misc" >&6;} && mkdir "misc") ;;
echo "$as_me: creating msdos" >&6;} && mkdir "msdos") ;;
objects ) test -d "objects" || ({ echo "$as_me:$LINENO: creating objects" >&5
echo "$as_me: creating objects" >&6;} && mkdir "objects") ;;
ole ) test -d "ole" || ({ echo "$as_me:$LINENO: creating ole" >&5
echo "$as_me: creating ole" >&6;} && mkdir "ole") ;;
programs/regapi/tests ) test -d "programs/regapi/tests" || ({ echo "$as_me:$LINENO: creating programs/regapi/tests" >&5
echo "$as_me: creating programs/regapi/tests" >&6;} && mkdir "programs/regapi/tests") ;;
programs/regedit/tests ) test -d "programs/regedit/tests" || ({ echo "$as_me:$LINENO: creating programs/regedit/tests" >&5
......
......@@ -1351,7 +1351,6 @@ WINE_CONFIG_EXTRA_DIR(memory)
WINE_CONFIG_EXTRA_DIR(misc)
WINE_CONFIG_EXTRA_DIR(msdos)
WINE_CONFIG_EXTRA_DIR(objects)
WINE_CONFIG_EXTRA_DIR(ole)
WINE_CONFIG_EXTRA_DIR(programs/regapi/tests)
WINE_CONFIG_EXTRA_DIR(programs/regedit/tests)
WINE_CONFIG_EXTRA_DIR(relay32)
......
......@@ -20,7 +20,6 @@ SPEC_SRCS16 = \
windebug.spec
C_SRCS = \
$(TOPOBJDIR)/ole/ole2nls.c \
atom.c \
change.c \
comm.c \
......@@ -65,7 +64,7 @@ MC_SRCS = \
messages/winerr_enu.mc
SUBDIRS = tests
EXTRASUBDIRS = messages nls $(TOPOBJDIR)/ole
EXTRASUBDIRS = messages nls
@MAKE_DLL_RULES@
......
......@@ -535,7 +535,7 @@ static INT get_registry_locale_info( LPCWSTR value, LPWSTR buffer, INT len )
* GetLocaleInfoA (KERNEL32.@)
*
* NOTES
* LANG_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
* LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
*
* MS online documentation states that the string returned is NULL terminated
* except for LOCALE_FONTSIGNATURE which "will return a non-NULL
......@@ -593,7 +593,7 @@ INT WINAPI GetLocaleInfoA( LCID lcid, LCTYPE lctype, LPSTR buffer, INT len )
* GetLocaleInfoW (KERNEL32.@)
*
* NOTES
* LANG_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
* LOCALE_NEUTRAL is equal to LOCALE_SYSTEM_DEFAULT
*
* MS online documentation states that the string returned is NULL terminated
* except for LOCALE_FONTSIGNATURE which "will return a non-NULL
......@@ -617,8 +617,8 @@ INT WINAPI GetLocaleInfoW( LCID lcid, LCTYPE lctype, LPWSTR buffer, INT len )
}
if (!len) buffer = NULL;
if (lcid == LOCALE_NEUTRAL || lcid == LANG_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LANG_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
lcflags = lctype & LOCALE_LOCALEINFOFLAGSMASK;
lctype &= ~LOCALE_LOCALEINFOFLAGSMASK;
......@@ -705,8 +705,8 @@ BOOL WINAPI SetLocaleInfoA(LCID lcid, LCTYPE lctype, LPCSTR data)
DWORD len;
BOOL ret;
if (lcid == LOCALE_NEUTRAL || lcid == LANG_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LANG_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (!(lctype & LOCALE_USE_CP_ACP)) codepage = get_lcid_codepage( lcid );
len = MultiByteToWideChar( codepage, 0, data, -1, NULL, 0 );
......@@ -733,8 +733,8 @@ BOOL WINAPI SetLocaleInfoW( LCID lcid, LCTYPE lctype, LPCWSTR data )
NTSTATUS status;
HKEY hkey;
if (lcid == LOCALE_NEUTRAL || lcid == LANG_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LANG_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (!(value = get_locale_value_name( lctype )))
{
......@@ -780,8 +780,8 @@ LCID WINAPI GetThreadLocale(void)
*/
BOOL WINAPI SetThreadLocale( LCID lcid ) /* [in] Locale identifier */
{
if (lcid == LOCALE_NEUTRAL || lcid == LANG_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LANG_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
NtCurrentTeb()->CurrentLocale = lcid;
NtCurrentTeb()->code_page = get_lcid_codepage( lcid );
......@@ -994,6 +994,319 @@ BOOL WINAPI GetStringTypeExA( LCID locale, DWORD type, LPCSTR src, INT count, LP
}
/*************************************************************************
* LCMapStringW (KERNEL32.@)
*/
INT WINAPI LCMapStringW(LCID lcid, DWORD flags, LPCWSTR src, INT srclen,
LPWSTR dst, INT dstlen)
{
LPWSTR dst_ptr;
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))
{
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
if (!dstlen) dst = NULL;
if (lcid == LOCALE_NEUTRAL || lcid == LOCALE_SYSTEM_DEFAULT) lcid = GetSystemDefaultLCID();
else if (lcid == LOCALE_USER_DEFAULT) lcid = GetUserDefaultLCID();
if (flags & LCMAP_SORTKEY)
{
if (src == dst)
{
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
if (srclen < 0) srclen = strlenW(src);
TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
return wine_get_sortkey(flags, src, srclen, (char *)dst, dstlen);
}
/* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
if (flags & SORT_STRINGSORT)
{
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
if (srclen < 0) srclen = strlenW(src) + 1;
TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
lcid, flags, debugstr_wn(src, srclen), srclen, dst, dstlen);
if (!dst) /* return required string length */
{
INT len;
for (len = 0; srclen; src++, srclen--)
{
WCHAR wch = *src;
/* tests show that win2k just ignores NORM_IGNORENONSPACE,
* and skips white space and punctuation characters for
* NORM_IGNORESYMBOLS.
*/
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
len++;
}
return len;
}
if (flags & LCMAP_UPPERCASE)
{
for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
{
WCHAR wch = *src;
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
*dst_ptr++ = toupperW(wch);
dstlen--;
}
}
else if (flags & LCMAP_LOWERCASE)
{
for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
{
WCHAR wch = *src;
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
*dst_ptr++ = tolowerW(wch);
dstlen--;
}
}
else
{
if (src == dst)
{
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
for (dst_ptr = dst; srclen && dstlen; src++, srclen--)
{
WCHAR wch = *src;
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
*dst_ptr++ = wch;
dstlen--;
}
}
return dst_ptr - dst;
}
/*************************************************************************
* LCMapStringA (KERNEL32.@)
*/
INT WINAPI LCMapStringA(LCID lcid, DWORD flags, LPCSTR src, INT srclen,
LPSTR dst, INT dstlen)
{
WCHAR bufW[128];
LPWSTR srcW, dstW;
INT ret = 0, srclenW, dstlenW;
UINT locale_cp;
if (!src || !srclen || dstlen < 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&locale_cp, (sizeof(locale_cp)/sizeof(WCHAR)));
srclenW = MultiByteToWideChar(locale_cp, 0, src, srclen, bufW, 128);
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 map_string_exit;
}
ret = wine_get_sortkey(flags, srcW, srclenW, dst, dstlen);
}
if (flags & SORT_STRINGSORT)
{
SetLastError(ERROR_INVALID_FLAGS);
goto map_string_exit;
}
dstlenW = LCMapStringW(lcid, flags, srcW, srclenW, NULL, 0);
dstW = HeapAlloc(GetProcessHeap(), 0, dstlenW * sizeof(WCHAR));
if (!dstW)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto map_string_exit;
}
LCMapStringW(lcid, flags, srcW, srclenW, dstW, dstlenW);
ret = WideCharToMultiByte(locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL);
HeapFree(GetProcessHeap(), 0, dstW);
map_string_exit:
if (srcW != bufW) HeapFree(GetProcessHeap(), 0, srcW);
return ret;
}
/******************************************************************************
* CompareStringW (KERNEL32.@)
*/
INT WINAPI CompareStringW(LCID lcid, DWORD style,
LPCWSTR str1, INT len1, LPCWSTR str2, INT len2)
{
INT ret, len;
if (!str1 || !str2)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (len1 < 0) len1 = lstrlenW(str1);
if (len2 < 0) len2 = lstrlenW(str2);
len = (len1 < len2) ? len1 : len2;
ret = (style & NORM_IGNORECASE) ? strncmpiW(str1, str2, len) :
strncmpW(str1, str2, len);
if (ret) /* need to translate result */
return (ret < 0) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
if (len1 == len2) return CSTR_EQUAL;
/* the longer one is lexically greater */
return (len1 < len2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
}
/******************************************************************************
* CompareStringA (KERNEL32.@)
*/
INT WINAPI CompareStringA(LCID lcid, DWORD style,
LPCSTR str1, INT len1, LPCSTR str2, INT len2)
{
WCHAR buf1W[128], buf2W[128];
LPWSTR str1W, str2W;
INT len1W, len2W, ret;
UINT locale_cp;
if (!str1 || !str2)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(WCHAR *)&locale_cp, (sizeof(locale_cp)/sizeof(WCHAR)));
len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, buf1W, 128);
if (len1W)
str1W = buf1W;
else
{
len1W = MultiByteToWideChar(locale_cp, 0, str1, len1, NULL, 0);
str1W = HeapAlloc(GetProcessHeap(), 0, len1W * sizeof(WCHAR));
if (!str1W)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
MultiByteToWideChar(locale_cp, 0, str1, len1, str1W, len1W);
}
len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, buf2W, 128);
if (len2W)
str2W = buf2W;
else
{
len2W = MultiByteToWideChar(locale_cp, 0, str2, len2, NULL, 0);
str2W = HeapAlloc(GetProcessHeap(), 0, len2W * sizeof(WCHAR));
if (!str2W)
{
if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
MultiByteToWideChar(locale_cp, 0, str2, len2, str2W, len2W);
}
ret = CompareStringW(lcid, style, str1W, len1W, str2W, len2W);
if (str1W != buf1W) HeapFree(GetProcessHeap(), 0, str1W);
if (str2W != buf2W) HeapFree(GetProcessHeap(), 0, str2W);
return ret;
}
/*************************************************************************
* lstrcmp (KERNEL32.@)
* lstrcmpA (KERNEL32.@)
*/
int WINAPI lstrcmpA(LPCSTR str1, LPCSTR str2)
{
int ret = CompareStringA(GetThreadLocale(), 0, str1, -1, str2, -1);
if (ret) ret -= 2;
return ret;
}
/*************************************************************************
* lstrcmpi (KERNEL32.@)
* lstrcmpiA (KERNEL32.@)
*/
int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2)
{
int ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
if (ret) ret -= 2;
return ret;
}
/*************************************************************************
* lstrcmpW (KERNEL32.@)
*/
int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
{
int ret = CompareStringW(GetThreadLocale(), 0, str1, -1, str2, -1);
if (ret) ret -= 2;
return ret;
}
/*************************************************************************
* lstrcmpiW (KERNEL32.@)
*/
int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
{
int ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1);
if (ret) ret -= 2;
return ret;
}
/******************************************************************************
* LOCALE_Init
*/
......
......@@ -3,6 +3,7 @@
* Test run on win2K (French)
*
* Copyright (c) 2002 YASAR Mehmet
* Copyright 2003 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -696,30 +697,282 @@ char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
strcpy(buffer1, "Salut"); strcpy(buffer2, "Salute");
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, -1);
ok (ret== 1, "CompareStringA (st1=%s str2=%s) expected result=1", buffer1, buffer2);
ok(ret == 1, "CompareStringA (st1=%s str2=%s) expected result=1, got %d", buffer1, buffer2, ret);
strcpy(buffer1, "Salut"); strcpy(buffer2, "saLuT");
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, -1);
ok (ret== 2, "CompareStringA (st1=%s str2=%s) expected result=2", buffer1, buffer2);
ok(ret == 2, "CompareStringA (st1=%s str2=%s) expected result=2, got %d", buffer1, buffer2, ret);
strcpy(buffer1, "Salut"); strcpy(buffer2, "hola");
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, -1);
ok (ret== 3, "CompareStringA (st1=%s str2=%s) expected result=3", buffer1, buffer2);
ok(ret == 3, "CompareStringA (st1=%s str2=%s) expected result=3, got %d", buffer1, buffer2, ret);
strcpy(buffer1, "hh"); strcpy(buffer2, "hh");
strcpy(buffer1, "haha"); strcpy(buffer2, "hoho");
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, -1);
ok (ret== 1, "CompareStringA (st1=%s str2=%s) expected result=1", buffer1, buffer2);
ok (ret == 1, "CompareStringA (st1=%s str2=%s) expected result=1, got %d", buffer1, buffer2, ret);
lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT );
strcpy(buffer1, "hh"); strcpy(buffer2, "hh");
strcpy(buffer1, "haha"); strcpy(buffer2, "hoho");
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, -1);
ok (ret== 1, "CompareStringA (st1=%s str2=%s) expected result=1", buffer1, buffer2);
ok (ret == 1, "CompareStringA (st1=%s str2=%s) expected result=1, got %d", buffer1, buffer2, ret);
ret = CompareStringA(lcid, NORM_IGNORECASE, buffer1, -1, buffer2, 0);
ok (ret== 3, "CompareStringA (st1=%s str2=%s) expected result=3", buffer1, buffer2);
ok (ret == 3, "CompareStringA (st1=%s str2=%s) expected result=3, got %d", buffer1, buffer2, ret);
}
void test_LCMapStringA(void)
{
int ret, ret2;
char buf[256], buf2[256];
static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
static const char symbols_stripped[] = "justateststring1";
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
upper_case, -1, buf, sizeof(buf));
ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
upper_case, -1, buf, sizeof(buf));
ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
upper_case, -1, buf, sizeof(buf));
ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
upper_case, -1, buf, sizeof(buf));
ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
/* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
SetLastError(0xdeadbeef);
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
upper_case, -1, buf, sizeof(buf));
ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
/* test LCMAP_LOWERCASE */
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
upper_case, -1, buf, sizeof(buf));
ok(ret == lstrlenA(upper_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenA(upper_case) + 1);
ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
/* test LCMAP_UPPERCASE */
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
lower_case, -1, buf, sizeof(buf));
ok(ret == lstrlenA(lower_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenA(lower_case) + 1);
ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
/* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
lstrcpyA(buf, lower_case);
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
buf, -1, buf, sizeof(buf));
if (!ret) /* Win9x */
trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
else
{
ok(ret == lstrlenA(lower_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenA(lower_case) + 1);
ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
}
lstrcpyA(buf, upper_case);
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
buf, -1, buf, sizeof(buf));
if (!ret) /* Win9x */
trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
else
{
ok(ret == lstrlenA(upper_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenA(lower_case) + 1);
ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
}
/* otherwise src == dst should fail */
SetLastError(0xdeadbeef);
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
buf, 10, buf, sizeof(buf));
ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
"unexpected error code %ld\n", GetLastError());
ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
/* test whether '\0' is always appended */
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
upper_case, -1, buf, sizeof(buf));
ok(ret, "LCMapStringA must succeed\n");
ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
ok(ret, "LCMapStringA must succeed\n");
ok(ret == ret2, "lengths of sort keys must be equal\n");
ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
/* test LCMAP_SORTKEY | NORM_IGNORECASE */
ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
upper_case, -1, buf, sizeof(buf));
ok(ret, "LCMapStringA must succeed\n");
ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
lower_case, -1, buf2, sizeof(buf2));
ok(ret2, "LCMapStringA must succeed\n");
ok(ret == ret2, "lengths of sort keys must be equal\n");
ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
/* test NORM_IGNORENONSPACE */
lstrcpyA(buf, "foo");
ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
lower_case, -1, buf, sizeof(buf));
ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
lstrlenA(lower_case) + 1, ret);
ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
/* test NORM_IGNORESYMBOLS */
lstrcpyA(buf, "foo");
ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
lower_case, -1, buf, sizeof(buf));
ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
lstrlenA(symbols_stripped) + 1, ret);
ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
}
void test_LCMapStringW(void)
{
int ret, ret2;
WCHAR buf[256], buf2[256];
char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
static const WCHAR fooW[] = {'f','o','o',0};
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
{
trace("Skipping LCMapStringW tests on Win9x\n");
return;
}
ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
ok(GetLastError() == ERROR_INVALID_FLAGS,
"unexpected error code %ld\n", GetLastError());
/* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
SetLastError(0xdeadbeef);
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
/* test LCMAP_LOWERCASE */
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(upper_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenW(upper_case) + 1);
ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
/* test LCMAP_UPPERCASE */
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(lower_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenW(lower_case) + 1);
ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
/* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
lstrcpyW(buf, lower_case);
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(lower_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenW(lower_case) + 1);
ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
lstrcpyW(buf, upper_case);
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(upper_case) + 1,
"ret %d, error %ld, expected value %d\n",
ret, GetLastError(), lstrlenW(lower_case) + 1);
ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
/* otherwise src == dst should fail */
SetLastError(0xdeadbeef);
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
buf, 10, buf, sizeof(buf));
ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
"unexpected error code %ld\n", GetLastError());
ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
/* test whether '\0' is always appended */
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
upper_case, -1, buf, sizeof(buf));
ok(ret, "LCMapStringW must succeed\n");
ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
ok(ret, "LCMapStringW must succeed\n");
ok(ret == ret2, "lengths of sort keys must be equal\n");
ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
/* test LCMAP_SORTKEY | NORM_IGNORECASE */
ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
upper_case, -1, buf, sizeof(buf));
ok(ret, "LCMapStringW must succeed\n");
ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
lower_case, -1, buf2, sizeof(buf2));
ok(ret2, "LCMapStringW must succeed\n");
ok(ret == ret2, "lengths of sort keys must be equal\n");
ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
/* test NORM_IGNORENONSPACE */
lstrcpyW(buf, fooW);
ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(lower_case) + 1, "LCMapStringW should return %d, ret = %d\n",
lstrlenW(lower_case) + 1, ret);
ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n");
/* test NORM_IGNORESYMBOLS */
lstrcpyW(buf, fooW);
ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
ok(ret == lstrlenW(symbols_stripped) + 1, "LCMapStringW should return %d, ret = %d\n",
lstrlenW(symbols_stripped) + 1, ret);
ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n");
}
START_TEST(locale)
{
......@@ -733,4 +986,7 @@ START_TEST(locale)
TestGetNumberFormat();
TestGetCurrencyFormat();
TestCompareStringA();
test_LCMapStringW();
test_LCMapStringA();
}
......@@ -100,6 +100,7 @@ static inline void release( void *ptr )
/* put an ASCII string into the debug buffer */
inline static char *put_string_a( const char *src, int n )
{
static const char hex[16] = "0123456789abcdef";
char *dst, *res;
if (n == -1) n = strlen(src);
......@@ -123,9 +124,9 @@ inline static char *put_string_a( const char *src, int n )
else
{
*dst++ = '\\';
*dst++ = '0' + ((c >> 6) & 7);
*dst++ = '0' + ((c >> 3) & 7);
*dst++ = '0' + ((c >> 0) & 7);
*dst++ = 'x';
*dst++ = hex[(c >> 4) & 0x0f];
*dst++ = hex[c & 0x0f];
}
}
}
......
......@@ -21,6 +21,8 @@
#ifndef __WINE_UNICODE_H
#define __WINE_UNICODE_H
#include <stdarg.h>
#include "windef.h"
#include "winnls.h"
......@@ -71,6 +73,8 @@ extern int wine_cp_wcstombs( const union cptable *table, int flags,
extern int wine_utf8_wcstombs( const WCHAR *src, int srclen, char *dst, int dstlen );
extern int wine_utf8_mbstowcs( int flags, const char *src, int srclen, WCHAR *dst, int dstlen );
extern int wine_get_sortkey( int flags, const WCHAR *src, int srclen, char *dst, int dstlen );
extern int strcmpiW( const WCHAR *str1, const WCHAR *str2 );
extern int strncmpiW( const WCHAR *str1, const WCHAR *str2, int n );
extern WCHAR *strstrW( const WCHAR *str, const WCHAR *sub );
......
......@@ -196,6 +196,10 @@ extern "C" {
#define LCMAP_HALFWIDTH 0x00400000 /* map double byte to single byte */
#define LCMAP_FULLWIDTH 0x00800000 /* map single byte to double byte */
#define LCMAP_LINGUISTIC_CASING 0x01000000 /* use linguistic rules for casing */
#define LCMAP_SIMPLIFIED_CHINESE 0x02000000 /* map traditional chinese to simplified chinese */
#define LCMAP_TRADITIONAL_CHINESE 0x04000000 /* map simplified chinese to traditional chinese */
/* Date Flags for GetDateFormat. */
#define DATE_SHORTDATE 0x00000001 /* use short date picture */
......
......@@ -70,9 +70,11 @@ CODEPAGES = \
C_SRCS = \
casemap.c \
collation.c \
compose.c \
cptable.c \
mbtowc.c \
sortkey.c \
string.c \
utf8.c \
wctomb.c \
......
......@@ -20,11 +20,10 @@
#include <string.h>
#include "winnls.h"
#include "wine/unicode.h"
/* get the decomposition of a Unicode char */
static int get_decomposition( WCHAR src, WCHAR *dst, unsigned int dstlen )
int get_decomposition( WCHAR src, WCHAR *dst, unsigned int dstlen )
{
extern const WCHAR unicode_decompose_table[];
const WCHAR *ptr = unicode_decompose_table;
......
/*
* Unicode sort key generation
*
* Copyright 2003 Dmitry Timoshkov
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wine/unicode.h"
extern int get_decomposition(WCHAR src, WCHAR *dst, unsigned int dstlen);
/*
* flags - normalization NORM_* flags
*
* FIXME: 'variable' flag not handled
*/
int wine_get_sortkey(int flags, const WCHAR *src, int srclen, char *dst, int dstlen)
{
extern const unsigned int collation_table[];
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++)
{
int decomposed_len = get_decomposition(*src, dummy, 4);
if (decomposed_len)
{
int i;
for (i = 0; i < decomposed_len; i++)
{
WCHAR wch = dummy[i];
unsigned int ce;
/* tests show that win2k just ignores NORM_IGNORENONSPACE,
* and skips white space and punctuation characters for
* NORM_IGNORESYMBOLS.
*/
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
if (flags & NORM_IGNORECASE) wch = tolowerW(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' + 1 * '\0' + key length */
return key_len[0] + key_len[1] + key_len[2] + key_len[3] + 4 + 1;
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++)
{
int decomposed_len = get_decomposition(*src, dummy, 4);
if (decomposed_len)
{
int i;
for (i = 0; i < decomposed_len; i++)
{
WCHAR wch = dummy[i];
unsigned int ce;
/* tests show that win2k just ignores NORM_IGNORENONSPACE,
* and skips white space and punctuation characters for
* NORM_IGNORESYMBOLS.
*/
if ((flags & NORM_IGNORESYMBOLS) && (get_char_typeW(wch) & (C1_PUNCT | C1_SPACE)))
continue;
if (flags & NORM_IGNORECASE) wch = tolowerW(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;
}
......@@ -20,7 +20,6 @@
#include <string.h>
#include "winnls.h"
#include "wine/unicode.h"
/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */
......
......@@ -20,7 +20,6 @@
#include <string.h>
#include "winnls.h"
#include "wine/unicode.h"
/* search for a character in the unicode_compose_table; helper for compose() */
......
......@@ -16,6 +16,7 @@ EXPORTS
wine_cp_get_table
wine_cp_mbstowcs
wine_cp_wcstombs
wine_get_sortkey
wine_utf8_mbstowcs
wine_utf8_wcstombs
wine_wctype_table
/*
* National Language Support library
*
* Copyright 1995 Martin von Loewis
* Copyright 1998 David Lee Lambert
* Copyright 2000 Julio Csar Gzquez
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "wine/port.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <locale.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winternl.h"
#include "wine/unicode.h"
#include "winver.h"
#include "winnls.h"
#include "winreg.h"
#include "winerror.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(nls);
static const unsigned char LCM_Unicode_LUT[] = {
6 , 3, /* - 1 */
6 , 4, /* - 2 */
6 , 5, /* - 3 */
6 , 6, /* - 4 */
6 , 7, /* - 5 */
6 , 8, /* - 6 */
6 , 9, /* - 7 */
6 , 10, /* - 8 */
7 , 5, /* - 9 */
7 , 6, /* - 10 */
7 , 7, /* - 11 */
7 , 8, /* - 12 */
7 , 9, /* - 13 */
6 , 11, /* - 14 */
6 , 12, /* - 15 */
6 , 13, /* - 16 */
6 , 14, /* - 17 */
6 , 15, /* - 18 */
6 , 16, /* - 19 */
6 , 17, /* - 20 */
6 , 18, /* - 21 */
6 , 19, /* - 22 */
6 , 20, /* - 23 */
6 , 21, /* - 24 */
6 , 22, /* - 25 */
6 , 23, /* - 26 */
6 , 24, /* - 27 */
6 , 25, /* - 28 */
6 , 26, /* - 29 */
6 , 27, /* - 30 */
6 , 28, /* - 31 */
7 , 2, /* - 32 */
7 , 28, /* ! - 33 */
7 , 29, /* " - 34 */ /* " */
7 , 31, /* # - 35 */
7 , 33, /* $ - 36 */
7 , 35, /* % - 37 */
7 , 37, /* & - 38 */
6 , 128, /* ' - 39 */
7 , 39, /* ( - 40 */
7 , 42, /* ) - 41 */
7 , 45, /* * - 42 */
8 , 3, /* + - 43 */
7 , 47, /* , - 44 */
6 , 130, /* - - 45 */
7 , 51, /* . - 46 */
7 , 53, /* / - 47 */
12 , 3, /* 0 - 48 */
12 , 33, /* 1 - 49 */
12 , 51, /* 2 - 50 */
12 , 70, /* 3 - 51 */
12 , 88, /* 4 - 52 */
12 , 106, /* 5 - 53 */
12 , 125, /* 6 - 54 */
12 , 144, /* 7 - 55 */
12 , 162, /* 8 - 56 */
12 , 180, /* 9 - 57 */
7 , 55, /* : - 58 */
7 , 58, /* ; - 59 */
8 , 14, /* < - 60 */
8 , 18, /* = - 61 */
8 , 20, /* > - 62 */
7 , 60, /* ? - 63 */
7 , 62, /* @ - 64 */
14 , 2, /* A - 65 */
14 , 9, /* B - 66 */
14 , 10, /* C - 67 */
14 , 26, /* D - 68 */
14 , 33, /* E - 69 */
14 , 35, /* F - 70 */
14 , 37, /* G - 71 */
14 , 44, /* H - 72 */
14 , 50, /* I - 73 */
14 , 53, /* J - 74 */
14 , 54, /* K - 75 */
14 , 72, /* L - 76 */
14 , 81, /* M - 77 */
14 , 112, /* N - 78 */
14 , 124, /* O - 79 */
14 , 126, /* P - 80 */
14 , 137, /* Q - 81 */
14 , 138, /* R - 82 */
14 , 145, /* S - 83 */
14 , 153, /* T - 84 */
14 , 159, /* U - 85 */
14 , 162, /* V - 86 */
14 , 164, /* W - 87 */
14 , 166, /* X - 88 */
14 , 167, /* Y - 89 */
14 , 169, /* Z - 90 */
7 , 63, /* [ - 91 */
7 , 65, /* \ - 92 */
7 , 66, /* ] - 93 */
7 , 67, /* ^ - 94 */
7 , 68, /* _ - 95 */
7 , 72, /* ` - 96 */
14 , 2, /* a - 97 */
14 , 9, /* b - 98 */
14 , 10, /* c - 99 */
14 , 26, /* d - 100 */
14 , 33, /* e - 101 */
14 , 35, /* f - 102 */
14 , 37, /* g - 103 */
14 , 44, /* h - 104 */
14 , 50, /* i - 105 */
14 , 53, /* j - 106 */
14 , 54, /* k - 107 */
14 , 72, /* l - 108 */
14 , 81, /* m - 109 */
14 , 112, /* n - 110 */
14 , 124, /* o - 111 */
14 , 126, /* p - 112 */
14 , 137, /* q - 113 */
14 , 138, /* r - 114 */
14 , 145, /* s - 115 */
14 , 153, /* t - 116 */
14 , 159, /* u - 117 */
14 , 162, /* v - 118 */
14 , 164, /* w - 119 */
14 , 166, /* x - 120 */
14 , 167, /* y - 121 */
14 , 169, /* z - 122 */
7 , 74, /* { - 123 */
7 , 76, /* | - 124 */
7 , 78, /* } - 125 */
7 , 80, /* ~ - 126 */
6 , 29, /*  - 127 */
6 , 30, /* - 128 */
6 , 31, /* - 129 */
7 , 123, /* - 130 */
14 , 35, /* - 131 */
7 , 127, /* - 132 */
10 , 21, /* - 133 */
10 , 15, /* - 134 */
10 , 16, /* - 135 */
7 , 67, /* - 136 */
10 , 22, /* - 137 */
14 , 145, /* - 138 */
7 , 136, /* - 139 */
14 + 16 , 124, /* - 140 */
6 , 43, /* - 141 */
6 , 44, /* - 142 */
6 , 45, /* - 143 */
6 , 46, /* - 144 */
7 , 121, /* - 145 */
7 , 122, /* - 146 */
7 , 125, /* - 147 */
7 , 126, /* - 148 */
10 , 17, /* - 149 */
6 , 137, /* - 150 */
6 , 139, /* - 151 */
7 , 93, /* - 152 */
14 , 156, /* - 153 */
14 , 145, /* - 154 */
7 , 137, /* - 155 */
14 + 16 , 124, /* - 156 */
6 , 59, /* - 157 */
6 , 60, /* - 158 */
14 , 167, /* - 159 */
7 , 4, /* - 160 */
7 , 81, /* - 161 */
10 , 2, /* - 162 */
10 , 3, /* - 163 */
10 , 4, /* - 164 */
10 , 5, /* - 165 */
7 , 82, /* - 166 */
10 , 6, /* - 167 */
7 , 83, /* - 168 */
10 , 7, /* - 169 */
14 , 2, /* - 170 */
8 , 24, /* - 171 */
10 , 8, /* - 172 */
6 , 131, /* - 173 */
10 , 9, /* - 174 */
7 , 84, /* - 175 */
10 , 10, /* - 176 */
8 , 23, /* - 177 */
12 , 51, /* - 178 */
12 , 70, /* - 179 */
7 , 85, /* - 180 */
10 , 11, /* - 181 */
10 , 12, /* - 182 */
10 , 13, /* - 183 */
7 , 86, /* - 184 */
12 , 33, /* - 185 */
14 , 124, /* - 186 */
8 , 26, /* - 187 */
12 , 21, /* - 188 */
12 , 25, /* - 189 */
12 , 29, /* - 190 */
7 , 87, /* - 191 */
14 , 2, /* - 192 */
14 , 2, /* - 193 */
14 , 2, /* - 194 */
14 , 2, /* - 195 */
14 , 2, /* - 196 */
14 , 2, /* - 197 */
14 + 16 , 2, /* - 198 */
14 , 10, /* - 199 */
14 , 33, /* - 200 */
14 , 33, /* - 201 */
14 , 33, /* - 202 */
14 , 33, /* - 203 */
14 , 50, /* - 204 */
14 , 50, /* - 205 */
14 , 50, /* - 206 */
14 , 50, /* - 207 */
14 , 26, /* - 208 */
14 , 112, /* - 209 */
14 , 124, /* - 210 */
14 , 124, /* - 211 */
14 , 124, /* - 212 */
14 , 124, /* - 213 */
14 , 124, /* - 214 */
8 , 28, /* - 215 */
14 , 124, /* - 216 */
14 , 159, /* - 217 */
14 , 159, /* - 218 */
14 , 159, /* - 219 */
14 , 159, /* - 220 */
14 , 167, /* - 221 */
14 + 32 , 153, /* - 222 */
14 + 48 , 145, /* - 223 */
14 , 2, /* - 224 */
14 , 2, /* - 225 */
14 , 2, /* - 226 */
14 , 2, /* - 227 */
14 , 2, /* - 228 */
14 , 2, /* - 229 */
14 + 16 , 2, /* - 230 */
14 , 10, /* - 231 */
14 , 33, /* - 232 */
14 , 33, /* - 233 */
14 , 33, /* - 234 */
14 , 33, /* - 235 */
14 , 50, /* - 236 */
14 , 50, /* - 237 */
14 , 50, /* - 238 */
14 , 50, /* - 239 */
14 , 26, /* - 240 */
14 , 112, /* - 241 */
14 , 124, /* - 242 */
14 , 124, /* - 243 */
14 , 124, /* - 244 */
14 , 124, /* - 245 */
14 , 124, /* - 246 */
8 , 29, /* - 247 */
14 , 124, /* - 248 */
14 , 159, /* - 249 */
14 , 159, /* - 250 */
14 , 159, /* - 251 */
14 , 159, /* - 252 */
14 , 167, /* - 253 */
14 + 32 , 153, /* - 254 */
14 , 167 /* - 255 */ };
static const unsigned char LCM_Unicode_LUT_2[] = { 33, 44, 145 };
#define LCM_Diacritic_Start 131
static const unsigned char LCM_Diacritic_LUT[] = {
123, /* - 131 */
2, /* - 132 */
2, /* - 133 */
2, /* - 134 */
2, /* - 135 */
3, /* - 136 */
2, /* - 137 */
20, /* - 138 */
2, /* - 139 */
2, /* - 140 */
2, /* - 141 */
2, /* - 142 */
2, /* - 143 */
2, /* - 144 */
2, /* - 145 */
2, /* - 146 */
2, /* - 147 */
2, /* - 148 */
2, /* - 149 */
2, /* - 150 */
2, /* - 151 */
2, /* - 152 */
2, /* - 153 */
20, /* - 154 */
2, /* - 155 */
2, /* - 156 */
2, /* - 157 */
2, /* - 158 */
19, /* - 159 */
2, /* - 160 */
2, /* - 161 */
2, /* - 162 */
2, /* - 163 */
2, /* - 164 */
2, /* - 165 */
2, /* - 166 */
2, /* - 167 */
2, /* - 168 */
2, /* - 169 */
3, /* - 170 */
2, /* - 171 */
2, /* - 172 */
2, /* - 173 */
2, /* - 174 */
2, /* - 175 */
2, /* - 176 */
2, /* - 177 */
2, /* - 178 */
2, /* - 179 */
2, /* - 180 */
2, /* - 181 */
2, /* - 182 */
2, /* - 183 */
2, /* - 184 */
2, /* - 185 */
3, /* - 186 */
2, /* - 187 */
2, /* - 188 */
2, /* - 189 */
2, /* - 190 */
2, /* - 191 */
15, /* - 192 */
14, /* - 193 */
18, /* - 194 */
25, /* - 195 */
19, /* - 196 */
26, /* - 197 */
2, /* - 198 */
28, /* - 199 */
15, /* - 200 */
14, /* - 201 */
18, /* - 202 */
19, /* - 203 */
15, /* - 204 */
14, /* - 205 */
18, /* - 206 */
19, /* - 207 */
104, /* - 208 */
25, /* - 209 */
15, /* - 210 */
14, /* - 211 */
18, /* - 212 */
25, /* - 213 */
19, /* - 214 */
2, /* - 215 */
33, /* - 216 */
15, /* - 217 */
14, /* - 218 */
18, /* - 219 */
19, /* - 220 */
14, /* - 221 */
2, /* - 222 */
2, /* - 223 */
15, /* - 224 */
14, /* - 225 */
18, /* - 226 */
25, /* - 227 */
19, /* - 228 */
26, /* - 229 */
2, /* - 230 */
28, /* - 231 */
15, /* - 232 */
14, /* - 233 */
18, /* - 234 */
19, /* - 235 */
15, /* - 236 */
14, /* - 237 */
18, /* - 238 */
19, /* - 239 */
104, /* - 240 */
25, /* - 241 */
15, /* - 242 */
14, /* - 243 */
18, /* - 244 */
25, /* - 245 */
19, /* - 246 */
2, /* - 247 */
33, /* - 248 */
15, /* - 249 */
14, /* - 250 */
18, /* - 251 */
19, /* - 252 */
14, /* - 253 */
2, /* - 254 */
19, /* - 255 */
} ;
/******************************************************************************
* OLE2NLS_isPunctuation [INTERNAL]
*/
static int OLE2NLS_isPunctuation(unsigned char c)
{
/* "punctuation character" in this context is a character which is
considered "less important" during word sort comparison.
See LCMapString implementation for the precise definition
of "less important". */
return (LCM_Unicode_LUT[-2+2*c]==6);
}
/******************************************************************************
* OLE2NLS_isNonSpacing [INTERNAL]
*/
static int OLE2NLS_isNonSpacing(unsigned char c)
{
/* This function is used by LCMapStringA. Characters
for which it returns true are ignored when mapping a
string with NORM_IGNORENONSPACE */
return ((c==136) || (c==170) || (c==186));
}
/******************************************************************************
* OLE2NLS_isSymbol [INTERNAL]
* FIXME: handle current locale
*/
static int OLE2NLS_isSymbol(unsigned char c)
{
/* This function is used by LCMapStringA. Characters
for which it returns true are ignored when mapping a
string with NORM_IGNORESYMBOLS */
return ( (c!=0) && !(isalpha(c) || isdigit(c)) );
}
/******************************************************************************
* identity [Internal]
*/
static int identity(int c)
{
return c;
}
/*************************************************************************
* LCMapStringA [KERNEL32.@]
*
* Convert a string, or generate a sort key from it.
*
* RETURNS
* Success: The length of the string written to dststr.
* Failure: 0.
*
* NOTES
* -If mapflags includes LCMAP_SORTKEY, the function will generate a
* sort key for srcstr. Otherwise, srcstr is converted according to
* mapflags.
* -If scrlen is -1, the function will compute the length of strsrc
* (which must be NUL terminated) itself.
* -If dstlen is 0, The return value is the buffer length that is needed.
* -NORM_IGNOREWIDTH means to compare ASCII and wide characters
* as if they are equal.
* In the only code page implemented so far, there may not be
* wide characters in strings passed to this function,
* so there is nothing to be done for this flag.
*/
INT WINAPI LCMapStringA(
LCID lcid, /* [in] Locale Id */
DWORD mapflags, /* [in] Flags */
LPCSTR srcstr, /* [in] Source buffer */
INT srclen, /* [in] Length of srcstr */
LPSTR dststr, /* [out] Destination buffer */
INT dstlen) /* [in] Length of dststr */
{
int i;
TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
lcid,mapflags,debugstr_an(srcstr,srclen),srclen,dststr,dstlen);
if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
{
ERR("(src=%s,dest=%s): Invalid NULL string\n",
debugstr_an(srcstr,srclen), dststr);
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (srclen == -1)
srclen = strlen(srcstr) + 1 ; /* (include final '\0') */
#define LCMAPSTRINGA_SUPPORTED_FLAGS (LCMAP_UPPERCASE | \
LCMAP_LOWERCASE | \
LCMAP_SORTKEY | \
NORM_IGNORECASE | \
NORM_IGNORENONSPACE | \
SORT_STRINGSORT | \
NORM_IGNOREWIDTH | \
NORM_IGNOREKANATYPE)
/* FIXME: as long as we don't support Katakana nor Hiragana
* characters, we can support NORM_IGNOREKANATYPE
*/
if (mapflags & ~LCMAPSTRINGA_SUPPORTED_FLAGS)
{
FIXME("(0x%04lx,0x%08lx,%p,%d,%p,%d): "
"unimplemented flags: 0x%08lx\n",
lcid,
mapflags,
srcstr,
srclen,
dststr,
dstlen,
mapflags & ~LCMAPSTRINGA_SUPPORTED_FLAGS
);
}
if ( !(mapflags & LCMAP_SORTKEY) )
{
int i,j;
int (*f)(int) = identity;
int flag_ignorenonspace = mapflags & NORM_IGNORENONSPACE;
int flag_ignoresymbols = mapflags & NORM_IGNORESYMBOLS;
if (flag_ignorenonspace || flag_ignoresymbols)
{
/* For some values of mapflags, the length of the resulting
string is not known at this point. Windows does map the string
and does not SetLastError ERROR_INSUFFICIENT_BUFFER in
these cases. */
if (dstlen==0)
{
/* Compute required length */
for (i=j=0; i < srclen; i++)
{
if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
&& !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
j++;
}
return j;
}
}
else
{
if (dstlen==0)
return srclen;
if (dstlen<srclen)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
}
if (mapflags & LCMAP_UPPERCASE)
f = toupper;
else if (mapflags & LCMAP_LOWERCASE)
f = tolower;
/* FIXME: NORM_IGNORENONSPACE requires another conversion */
for (i=j=0; (i<srclen) && (j<dstlen) ; i++)
{
if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
&& !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
{
dststr[j] = (CHAR) f(srcstr[i]);
j++;
}
}
return j;
}
/* FIXME: This function completely ignores the "lcid" parameter. */
/* else ... (mapflags & LCMAP_SORTKEY) */
{
int unicode_len=0;
int case_len=0;
int diacritic_len=0;
int delayed_punctuation_len=0;
char *case_component;
char *diacritic_component;
char *delayed_punctuation_component;
int room,count;
int flag_stringsort = mapflags & SORT_STRINGSORT;
/* compute how much room we will need */
for (i=0;i<srclen;i++)
{
int ofs;
unsigned char source_char = srcstr[i];
if (source_char!='\0')
{
if (flag_stringsort || !OLE2NLS_isPunctuation(source_char))
{
unicode_len++;
if ( LCM_Unicode_LUT[-2+2*source_char] & ~15 )
unicode_len++; /* double letter */
}
else
{
delayed_punctuation_len++;
}
}
if (isupper(source_char))
case_len=unicode_len;
ofs = source_char - LCM_Diacritic_Start;
if ((ofs>=0) && (LCM_Diacritic_LUT[ofs]!=2))
diacritic_len=unicode_len;
}
if (mapflags & NORM_IGNORECASE)
case_len=0;
if (mapflags & NORM_IGNORENONSPACE)
diacritic_len=0;
room = 2 * unicode_len /* "unicode" component */
+ diacritic_len /* "diacritic" component */
+ case_len /* "case" component */
+ 4 * delayed_punctuation_len /* punctuation in word sort mode */
+ 4 /* four '\1' separators */
+ 1 ; /* terminal '\0' */
if (dstlen==0)
return room;
else if (dstlen<room)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
#if 0
/*FIXME the Pointercheck should not be nessesary */
if (IsBadWritePtr (dststr,room))
{ ERR("bad destination buffer (dststr) : %p,%d\n",dststr,dstlen);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
#endif
/* locate each component, write separators */
diacritic_component = dststr + 2*unicode_len ;
*diacritic_component++ = '\1';
case_component = diacritic_component + diacritic_len ;
*case_component++ = '\1';
delayed_punctuation_component = case_component + case_len ;
*delayed_punctuation_component++ = '\1';
*delayed_punctuation_component++ = '\1';
/* read source string char by char, write
corresponding weight in each component. */
for (i=0,count=0;i<srclen;i++)
{
unsigned char source_char=srcstr[i];
if (source_char!='\0')
{
int type,longcode;
type = LCM_Unicode_LUT[-2+2*source_char];
longcode = type >> 4;
type &= 15;
if (!flag_stringsort && OLE2NLS_isPunctuation(source_char))
{
WORD encrypted_location = (1<<15) + 7 + 4*count;
*delayed_punctuation_component++ = (unsigned char) (encrypted_location>>8);
*delayed_punctuation_component++ = (unsigned char) (encrypted_location&255);
/* big-endian is used here because it lets string comparison be
compatible with numerical comparison */
*delayed_punctuation_component++ = type;
*delayed_punctuation_component++ = LCM_Unicode_LUT[-1+2*source_char];
/* assumption : a punctuation character is never a
double or accented letter */
}
else
{
dststr[2*count] = type;
dststr[2*count+1] = LCM_Unicode_LUT[-1+2*source_char];
if (longcode)
{
if (count<case_len)
case_component[count] = ( isupper(source_char) ? 18 : 2 ) ;
if (count<diacritic_len)
diacritic_component[count] = 2; /* assumption: a double letter
is never accented */
count++;
dststr[2*count] = type;
dststr[2*count+1] = *(LCM_Unicode_LUT_2 - 1 + longcode);
/* 16 in the first column of LCM_Unicode_LUT --> longcode = 1
32 in the first column of LCM_Unicode_LUT --> longcode = 2
48 in the first column of LCM_Unicode_LUT --> longcode = 3 */
}
if (count<case_len)
case_component[count] = ( isupper(source_char) ? 18 : 2 ) ;
if (count<diacritic_len)
{
int ofs = source_char - LCM_Diacritic_Start;
diacritic_component[count] = (ofs>=0 ? LCM_Diacritic_LUT[ofs] : 2);
}
count++;
}
}
}
dststr[room-1] = '\0';
return room;
}
}
/*************************************************************************
* LCMapStringW [KERNEL32.@]
*
* See LCMapStringA.
*/
INT WINAPI LCMapStringW(
LCID lcid,DWORD mapflags,LPCWSTR srcstr,INT srclen,LPWSTR dststr,
INT dstlen)
{
int i;
TRACE("(0x%04lx,0x%08lx,%p,%d,%p,%d)\n",
lcid, mapflags, srcstr, srclen, dststr, dstlen);
if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
{
ERR("(src=%p,dst=%p): Invalid NULL string\n", srcstr, dststr);
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (srclen==-1)
srclen = strlenW(srcstr)+1;
/* FIXME: Both this function and it's companion LCMapStringA()
* completely ignore the "lcid" parameter. In place of the "lcid"
* parameter the application must set the "LC_COLLATE" or "LC_ALL"
* environment variable prior to invoking this function. */
if (mapflags & LCMAP_SORTKEY)
{
/* Possible values of LC_COLLATE. */
char *lc_collate_default = 0; /* value prior to this function */
char *lc_collate_env = 0; /* value retrieved from the environment */
/* General purpose index into strings of any type. */
int str_idx = 0;
/* Lengths of various strings where the length is measured in
* wide characters for wide character strings and in bytes for
* native strings. The lengths include the NULL terminator. */
size_t returned_len = 0;
size_t src_native_len = 0;
size_t dst_native_len = 0;
size_t dststr_libc_len = 0;
/* Native (character set determined by locale) versions of the
* strings source and destination strings. */
LPSTR src_native = 0;
LPSTR dst_native = 0;
/* Version of the source and destination strings using the
* "wchar_t" Unicode data type needed by various libc functions. */
wchar_t *srcstr_libc = 0;
wchar_t *dststr_libc = 0;
if(!(srcstr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
srclen * sizeof(wchar_t))))
{
ERR("Unable to allocate %d bytes for srcstr_libc\n",
srclen * sizeof(wchar_t));
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
/* Convert source string to a libc Unicode string. */
for(str_idx = 0; str_idx < srclen; str_idx++)
{
srcstr_libc[str_idx] = srcstr[str_idx];
}
/* src_native should contain at most 3 bytes for each
* multibyte characters in the original srcstr string. */
src_native_len = 3 * srclen;
if(!(src_native = (LPSTR)HeapAlloc(GetProcessHeap(), 0,
src_native_len)))
{
ERR("Unable to allocate %d bytes for src_native\n", src_native_len);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
return 0;
}
/* FIXME: Prior to to setting the LC_COLLATE locale category the
* current value is backed up so it can be restored after the
* last LC_COLLATE sensitive function returns.
*
* Even though the locale is adjusted for a minimum amount of
* time a race condition exists where other threads may be
* affected if they invoke LC_COLLATE sensitive functions. One
* possible solution is to wrap all LC_COLLATE sensitive Wine
* functions, like LCMapStringW(), in a mutex.
*
* Another enhancement to the following would be to set the
* LC_COLLATE locale category as a function of the "lcid"
* parameter instead of the "LC_COLLATE" environment variable. */
if(!(lc_collate_default = setlocale(LC_COLLATE, NULL)))
{
ERR("Unable to query the LC_COLLATE catagory\n");
SetLastError(ERROR_INVALID_PARAMETER);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
return 0;
}
if(!(lc_collate_env = setlocale(LC_COLLATE, "")))
{
ERR("Unable to inherit the LC_COLLATE locale category from the "
"environment. The \"LC_COLLATE\" environment variable is "
"\"%s\".\n", getenv("LC_COLLATE") ?
getenv("LC_COLLATE") : "<unset>");
SetLastError(ERROR_INVALID_PARAMETER);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
return 0;
}
TRACE("lc_collate_default = %s\n", lc_collate_default);
TRACE("lc_collate_env = %s\n", lc_collate_env);
/* Convert the libc Unicode string to a native multibyte character
* string. */
returned_len = wcstombs(src_native, srcstr_libc, src_native_len) + 1;
if(returned_len == 0)
{
ERR("wcstombs failed. The string specified (%s) may contain an invalid character.\n",
debugstr_w(srcstr));
SetLastError(ERROR_INVALID_PARAMETER);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
setlocale(LC_COLLATE, lc_collate_default);
return 0;
}
else if(returned_len > src_native_len)
{
src_native[src_native_len - 1] = 0;
ERR("wcstombs returned a string (%s) that was longer (%d bytes) "
"than expected (%d bytes).\n", src_native, returned_len,
dst_native_len);
/* Since this is an internal error I'm not sure what the correct
* error code is. */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
setlocale(LC_COLLATE, lc_collate_default);
return 0;
}
src_native_len = returned_len;
TRACE("src_native = %s src_native_len = %d\n",
src_native, src_native_len);
/* dst_native seems to contain at most 4 bytes for each byte in
* the original src_native string. Change if need be since this
* isn't documented by the strxfrm() man page. */
dst_native_len = 4 * src_native_len;
if(!(dst_native = (LPSTR)HeapAlloc(GetProcessHeap(), 0, dst_native_len)))
{
ERR("Unable to allocate %d bytes for dst_native\n", dst_native_len);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
setlocale(LC_COLLATE, lc_collate_default);
return 0;
}
/* The actual translation is done by the following call to
* strxfrm(). The surrounding code could have been simplified
* by calling wcsxfrm() instead except that wcsxfrm() is not
* available on older Linux systems (RedHat 4.1 with
* libc-5.3.12-17).
*
* Also, it is possible that the translation could be done by
* various tables as it is done in LCMapStringA(). However, I'm
* not sure what those tables are. */
returned_len = strxfrm(dst_native, src_native, dst_native_len) + 1;
if(returned_len > dst_native_len)
{
dst_native[dst_native_len - 1] = 0;
ERR("strxfrm returned a string (%s) that was longer (%d bytes) "
"than expected (%d bytes).\n", dst_native, returned_len,
dst_native_len);
/* Since this is an internal error I'm not sure what the correct
* error code is. */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
setlocale(LC_COLLATE, lc_collate_default);
return 0;
}
dst_native_len = returned_len;
TRACE("dst_native = %s dst_native_len = %d\n",
dst_native, dst_native_len);
dststr_libc_len = dst_native_len;
if(!(dststr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
dststr_libc_len * sizeof(wchar_t))))
{
ERR("Unable to allocate %d bytes for dststr_libc\n",
dststr_libc_len * sizeof(wchar_t));
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
setlocale(LC_COLLATE, lc_collate_default);
return 0;
}
/* Convert the native multibyte string to a libc Unicode string. */
returned_len = mbstowcs(dststr_libc, dst_native, dst_native_len) + 1;
/* Restore LC_COLLATE now that the last LC_COLLATE sensitive
* function has returned. */
setlocale(LC_COLLATE, lc_collate_default);
if(returned_len == 0)
{
ERR("mbstowcs failed. The native version of the translated string "
"(%s) may contain an invalid character.\n", dst_native);
SetLastError(ERROR_INVALID_PARAMETER);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
return 0;
}
if(dstlen)
{
if(returned_len > dstlen)
{
ERR("mbstowcs returned a string that was longer (%d chars) "
"than the buffer provided (%d chars).\n", returned_len,
dstlen);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
return 0;
}
dstlen = returned_len;
/* Convert a libc Unicode string to the destination string. */
for(str_idx = 0; str_idx < dstlen; str_idx++)
{
dststr[str_idx] = dststr_libc[str_idx];
}
TRACE("1st 4 int sized chunks of dststr = %x %x %x %x\n",
*(((int *)dststr) + 0),
*(((int *)dststr) + 1),
*(((int *)dststr) + 2),
*(((int *)dststr) + 3));
}
else
{
dstlen = returned_len;
}
TRACE("dstlen (return) = %d\n", dstlen);
if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
return dstlen;
}
else
{
int (*f)(int)=identity;
if (dstlen==0)
return srclen;
if (dstlen<srclen)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
if (mapflags & LCMAP_UPPERCASE)
f = toupper;
else if (mapflags & LCMAP_LOWERCASE)
f = tolower;
for (i=0; i < srclen; i++)
dststr[i] = (WCHAR) f(srcstr[i]);
return srclen;
}
}
/***********************************************************************
* OLE2NLS_EstimateMappingLength
*
* Estimates the number of characters required to hold the string
* computed by LCMapStringA.
*
* The size is always over-estimated, with a fixed limit on the
* amount of estimation error.
*
* Note that len == -1 is not permitted.
*/
static inline int OLE2NLS_EstimateMappingLength(LCID lcid, DWORD dwMapFlags,
LPCSTR str, DWORD len)
{
/* Estimate only for small strings to keep the estimation error from
* becoming too large. */
if (len < 128) return len * 8 + 5;
else return LCMapStringA(lcid, dwMapFlags, str, len, NULL, 0);
}
/******************************************************************************
* CompareStringA [KERNEL32.@]
* Compares two strings using locale
*
* RETURNS
*
* success: CSTR_LESS_THAN, CSTR_EQUAL, CSTR_GREATER_THAN
* failure: 0
*
* NOTES
*
* Defaults to a word sort, but uses a string sort if
* SORT_STRINGSORT is set.
* Calls SetLastError for ERROR_INVALID_FLAGS, ERROR_INVALID_PARAMETER.
*
* BUGS
*
* This implementation ignores the locale
*
* FIXME
*
* Quite inefficient.
*/
int WINAPI CompareStringA(
LCID lcid, /* [in] locale ID */
DWORD fdwStyle, /* [in] comparison-style options */
LPCSTR s1, /* [in] first string */
int l1, /* [in] length of first string */
LPCSTR s2, /* [in] second string */
int l2) /* [in] length of second string */
{
int mapstring_flags;
int len1,len2;
int result;
LPSTR sk1,sk2;
TRACE("%s and %s\n",
debugstr_an (s1,l1), debugstr_an (s2,l2));
if ( (s1==NULL) || (s2==NULL) )
{
ERR("(s1=%s,s2=%s): Invalid NULL string\n",
debugstr_an(s1,l1), debugstr_an(s2,l2));
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if(fdwStyle & NORM_IGNORESYMBOLS)
FIXME("IGNORESYMBOLS not supported\n");
if (l1 == -1) l1 = strlen(s1);
if (l2 == -1) l2 = strlen(s2);
mapstring_flags = LCMAP_SORTKEY | fdwStyle ;
len1 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s1, l1);
len2 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s2, l2);
if ((len1==0)||(len2==0))
return 0; /* something wrong happened */
sk1 = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len1 + len2);
sk2 = sk1 + len1;
if ( (!LCMapStringA(lcid,mapstring_flags,s1,l1,sk1,len1))
|| (!LCMapStringA(lcid,mapstring_flags,s2,l2,sk2,len2)) )
{
ERR("Bug in LCmapStringA.\n");
result = 0;
}
else
{
/* strcmp doesn't necessarily return -1, 0, or 1 */
result = strcmp(sk1,sk2);
}
HeapFree(GetProcessHeap(),0,sk1);
if (result < 0)
return 1;
if (result == 0)
return 2;
/* must be greater, if we reach this point */
return 3;
}
/******************************************************************************
* CompareStringW [KERNEL32.@]
* This implementation ignores the locale
* FIXME : Does only string sort. Should
* be reimplemented the same way as CompareStringA.
*/
int WINAPI CompareStringW(LCID lcid, DWORD fdwStyle,
LPCWSTR s1, int l1, LPCWSTR s2, int l2)
{
int len,ret;
if(fdwStyle & NORM_IGNORENONSPACE)
FIXME("IGNORENONSPACE not supported\n");
if(fdwStyle & NORM_IGNORESYMBOLS)
FIXME("IGNORESYMBOLS not supported\n");
if(s1==NULL || s2==NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/* Is strcmp defaulting to string sort or to word sort?? */
/* FIXME: Handle NORM_STRINGSORT */
l1 = (l1==-1)?strlenW(s1):l1;
l2 = (l2==-1)?strlenW(s2):l2;
len = l1<l2 ? l1:l2;
ret = (fdwStyle & NORM_IGNORECASE) ? strncmpiW(s1,s2,len) : strncmpW(s1,s2,len);
/* not equal, return 1 or 3 */
if(ret!=0) {
/* need to translate result */
return ((int)ret < 0) ? 1 : 3;
}
/* same len, return 2 */
if(l1==l2) return 2;
/* the longer one is lexically greater */
return (l1<l2)? 1 : 3;
}
/***********************************************************************
* lstrcmp (KERNEL32.@)
* lstrcmpA (KERNEL32.@)
*/
INT WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 )
{
return CompareStringA(LOCALE_SYSTEM_DEFAULT,0,str1,-1,str2,-1) - 2 ;
}
/***********************************************************************
* lstrcmpW (KERNEL32.@)
* FIXME : should call CompareStringW, when it is implemented.
* This implementation is not "word sort", as it should.
*/
INT WINAPI lstrcmpW( LPCWSTR str1, LPCWSTR str2 )
{
TRACE("%s and %s\n",
debugstr_w (str1), debugstr_w (str2));
if (!str1 || !str2) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
while (*str1 && (*str1 == *str2)) { str1++; str2++; }
return (INT)(*str1 - *str2);
}
/***********************************************************************
* lstrcmpi (KERNEL32.@)
* lstrcmpiA (KERNEL32.@)
*/
INT WINAPI lstrcmpiA( LPCSTR str1, LPCSTR str2 )
{ TRACE("strcmpi %s and %s\n",
debugstr_a (str1), debugstr_a (str2));
return CompareStringA(LOCALE_SYSTEM_DEFAULT,NORM_IGNORECASE,str1,-1,str2,-1)-2;
}
/***********************************************************************
* lstrcmpiW (KERNEL32.@)
*/
INT WINAPI lstrcmpiW( LPCWSTR str1, LPCWSTR str2 )
{
if (!str1 || !str2) {
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
return strcmpiW( str1, str2 );
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment