Implement LCMapString using unicode collation tables.

Move CompareString and LCMapString to dlls/kernel/locale.c.
This commit is contained in:
Dmitry Timoshkov 2003-06-27 19:02:23 +00:00 committed by Alexandre Julliard
parent 4cb212063b
commit 85d4281616
15 changed files with 758 additions and 1251 deletions

5
configure vendored
View file

@ -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

View file

@ -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)

View file

@ -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@

View file

@ -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
*/

View file

@ -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, "héhé"); strcpy(buffer2, "hèhè");
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, "héhé"); strcpy(buffer2, "hèhè");
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();
}

View file

@ -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];
}
}
}

View file

@ -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 );

View file

@ -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 */

View file

@ -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 \

View file

@ -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;

154
libs/unicode/sortkey.c Normal file
View file

@ -0,0 +1,154 @@
/*
* 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;
}

View file

@ -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) */

View file

@ -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() */

View file

@ -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

File diff suppressed because it is too large Load diff