include: Move ddk/imm.h to immdev.h.
[wine.git] / dlls / ntdll / locale.c
blobf18181210aa1f44cc2aac2364754ffe3d9550857
1 /*
2 * Locale functions
4 * Copyright 2004, 2019 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winnls.h"
32 #include "ntdll_misc.h"
33 #include "locale_private.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(nls);
38 UINT NlsAnsiCodePage = 0;
39 BYTE NlsMbCodePageTag = 0;
40 BYTE NlsMbOemCodePageTag = 0;
42 static LCID user_resource_lcid;
43 static LCID user_resource_neutral_lcid;
44 static LCID system_lcid;
45 static NLSTABLEINFO nls_info = { { CP_UTF8 }, { CP_UTF8 } };
46 static struct norm_table *norm_tables[16];
47 static const NLS_LOCALE_HEADER *locale_table;
48 static const WCHAR *locale_strings;
51 static WCHAR casemap( USHORT *table, WCHAR ch )
53 return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)];
57 static NTSTATUS load_norm_table( ULONG form, const struct norm_table **info )
59 unsigned int i;
60 USHORT *data, *tables;
61 SIZE_T size;
62 NTSTATUS status;
64 if (!form) return STATUS_INVALID_PARAMETER;
65 if (form >= ARRAY_SIZE(norm_tables)) return STATUS_OBJECT_NAME_NOT_FOUND;
67 if (!norm_tables[form])
69 if ((status = NtGetNlsSectionPtr( NLS_SECTION_NORMALIZE, form, NULL, (void **)&data, &size )))
70 return status;
72 /* sanity checks */
74 if (size <= 0x44) goto invalid;
75 if (data[0x14] != form) goto invalid;
76 tables = data + 0x1a;
77 for (i = 0; i < 8; i++)
79 if (tables[i] > size / sizeof(USHORT)) goto invalid;
80 if (i && tables[i] < tables[i-1]) goto invalid;
83 if (InterlockedCompareExchangePointer( (void **)&norm_tables[form], data, NULL ))
84 NtUnmapViewOfSection( GetCurrentProcess(), data );
86 *info = norm_tables[form];
87 return STATUS_SUCCESS;
89 invalid:
90 NtUnmapViewOfSection( GetCurrentProcess(), data );
91 return STATUS_INVALID_PARAMETER;
95 void locale_init(void)
97 const NLS_LOCALE_LCID_INDEX *entry;
98 USHORT utf8[2] = { 0, CP_UTF8 };
99 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
100 LARGE_INTEGER unused;
101 SIZE_T size;
102 UINT ansi_cp = 1252, oem_cp = 437;
103 void *ansi_ptr = utf8, *oem_ptr = utf8, *case_ptr;
104 NTSTATUS status;
105 const struct locale_nls_header *header;
107 status = RtlGetLocaleFileMappingAddress( (void **)&header, &system_lcid, &unused );
108 if (status)
110 ERR( "locale init failed %lx\n", status );
111 return;
113 locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales);
114 locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset);
116 entry = find_lcid_entry( locale_table, system_lcid );
117 ansi_cp = get_locale_data( locale_table, entry->idx )->idefaultansicodepage;
118 oem_cp = get_locale_data( locale_table, entry->idx )->idefaultcodepage;
120 NtQueryDefaultLocale( TRUE, &user_resource_lcid );
121 user_resource_neutral_lcid = PRIMARYLANGID( user_resource_lcid );
122 if (user_resource_lcid == LOCALE_CUSTOM_UNSPECIFIED)
124 const NLS_LOCALE_LCNAME_INDEX *entry;
125 const WCHAR *parent;
126 WCHAR bufferW[LOCALE_NAME_MAX_LENGTH];
127 SIZE_T len;
129 if (!RtlQueryEnvironmentVariable( NULL, L"WINEUSERLOCALE", 14, bufferW, ARRAY_SIZE(bufferW), &len )
130 && (entry = find_lcname_entry( locale_table, bufferW )))
132 user_resource_lcid = get_locale_data( locale_table, entry->idx )->unique_lcid;
133 parent = locale_strings + get_locale_data( locale_table, entry->idx )->sparent;
134 if (*parent && (entry = find_lcname_entry( locale_table, parent + 1 )))
135 user_resource_neutral_lcid = get_locale_data( locale_table, entry->idx )->unique_lcid;
138 TRACE( "resources: %04lx/%04lx/%04lx\n", user_resource_lcid, user_resource_neutral_lcid, system_lcid );
140 if (!RtlQueryActivationContextApplicationSettings( 0, NULL, L"http://schemas.microsoft.com/SMI/2019/WindowsSettings",
141 L"activeCodePage", locale, ARRAY_SIZE(locale), NULL ))
143 const NLS_LOCALE_LCNAME_INDEX *entry = find_lcname_entry( locale_table, locale );
145 if (!wcsicmp( locale, L"utf-8" ))
147 ansi_cp = oem_cp = CP_UTF8;
149 else if (!wcsicmp( locale, L"legacy" ))
151 if (ansi_cp == CP_UTF8) ansi_cp = 1252;
152 if (oem_cp == CP_UTF8) oem_cp = 437;
154 else if ((entry = find_lcname_entry( locale_table, locale )))
156 ansi_cp = get_locale_data( locale_table, entry->idx )->idefaultansicodepage;
157 oem_cp = get_locale_data( locale_table, entry->idx )->idefaultcodepage;
161 NtGetNlsSectionPtr( 10, 0, NULL, &case_ptr, &size );
162 NtCurrentTeb()->Peb->UnicodeCaseTableData = case_ptr;
163 if (ansi_cp != CP_UTF8)
165 NtGetNlsSectionPtr( 11, ansi_cp, NULL, &ansi_ptr, &size );
166 NtCurrentTeb()->Peb->AnsiCodePageData = ansi_ptr;
168 if (oem_cp != CP_UTF8)
170 NtGetNlsSectionPtr( 11, oem_cp, NULL, &oem_ptr, &size );
171 NtCurrentTeb()->Peb->OemCodePageData = oem_ptr;
173 RtlInitNlsTables( ansi_ptr, oem_ptr, case_ptr, &nls_info );
174 NlsAnsiCodePage = nls_info.AnsiTableInfo.CodePage;
175 NlsMbCodePageTag = nls_info.AnsiTableInfo.DBCSCodePage;
176 NlsMbOemCodePageTag = nls_info.OemTableInfo.DBCSCodePage;
180 /* return LCIDs to use for resource lookup */
181 void get_resource_lcids( LANGID *user, LANGID *user_neutral, LANGID *system )
183 *user = LANGIDFROMLCID( user_resource_lcid );
184 *user_neutral = LANGIDFROMLCID( user_resource_neutral_lcid );
185 *system = LANGIDFROMLCID( system_lcid );
189 static NTSTATUS get_dummy_preferred_ui_language( DWORD flags, LANGID lang, ULONG *count,
190 WCHAR *buffer, ULONG *size )
192 WCHAR name[LOCALE_NAME_MAX_LENGTH + 2];
193 NTSTATUS status;
194 ULONG len;
196 FIXME("(0x%lx %#x %p %p %p) returning a dummy value (current locale)\n", flags, lang, count, buffer, size);
198 if (flags & MUI_LANGUAGE_ID) swprintf( name, ARRAY_SIZE(name), L"%04lX", lang );
199 else
201 UNICODE_STRING str;
203 if (lang == LOCALE_CUSTOM_UNSPECIFIED)
204 NtQueryInstallUILanguage( &lang );
206 str.Buffer = name;
207 str.MaximumLength = sizeof(name);
208 status = RtlLcidToLocaleName( lang, &str, 0, FALSE );
209 if (status) return status;
212 len = wcslen( name ) + 2;
213 name[len - 1] = 0;
214 if (buffer)
216 if (len > *size)
218 *size = len;
219 return STATUS_BUFFER_TOO_SMALL;
221 memcpy( buffer, name, len * sizeof(WCHAR) );
223 *size = len;
224 *count = 1;
225 TRACE("returned variable content: %ld, \"%s\", %ld\n", *count, debugstr_w(buffer), *size);
226 return STATUS_SUCCESS;
230 /**************************************************************************
231 * RtlGetProcessPreferredUILanguages (NTDLL.@)
233 NTSTATUS WINAPI RtlGetProcessPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
235 LANGID ui_language;
237 FIXME( "%08lx, %p, %p %p\n", flags, count, buffer, size );
239 NtQueryDefaultUILanguage( &ui_language );
240 return get_dummy_preferred_ui_language( flags, ui_language, count, buffer, size );
244 /**************************************************************************
245 * RtlGetSystemPreferredUILanguages (NTDLL.@)
247 NTSTATUS WINAPI RtlGetSystemPreferredUILanguages( DWORD flags, ULONG unknown, ULONG *count,
248 WCHAR *buffer, ULONG *size )
250 LANGID ui_language;
252 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID | MUI_MACHINE_LANGUAGE_SETTINGS)) return STATUS_INVALID_PARAMETER;
253 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID)) return STATUS_INVALID_PARAMETER;
254 if (*size && !buffer) return STATUS_INVALID_PARAMETER;
256 NtQueryInstallUILanguage( &ui_language );
257 return get_dummy_preferred_ui_language( flags, ui_language, count, buffer, size );
261 /**************************************************************************
262 * RtlGetThreadPreferredUILanguages (NTDLL.@)
264 NTSTATUS WINAPI RtlGetThreadPreferredUILanguages( DWORD flags, ULONG *count, WCHAR *buffer, ULONG *size )
266 LANGID ui_language;
268 FIXME( "%08lx, %p, %p %p\n", flags, count, buffer, size );
270 NtQueryDefaultUILanguage( &ui_language );
271 return get_dummy_preferred_ui_language( flags, ui_language, count, buffer, size );
275 /**************************************************************************
276 * RtlGetUserPreferredUILanguages (NTDLL.@)
278 NTSTATUS WINAPI RtlGetUserPreferredUILanguages( DWORD flags, ULONG unknown, ULONG *count,
279 WCHAR *buffer, ULONG *size )
281 LANGID ui_language;
283 if (flags & ~(MUI_LANGUAGE_NAME | MUI_LANGUAGE_ID)) return STATUS_INVALID_PARAMETER;
284 if ((flags & MUI_LANGUAGE_NAME) && (flags & MUI_LANGUAGE_ID)) return STATUS_INVALID_PARAMETER;
285 if (*size && !buffer) return STATUS_INVALID_PARAMETER;
287 NtQueryDefaultUILanguage( &ui_language );
288 return get_dummy_preferred_ui_language( flags, ui_language, count, buffer, size );
292 /**************************************************************************
293 * RtlSetProcessPreferredUILanguages (NTDLL.@)
295 NTSTATUS WINAPI RtlSetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
297 FIXME( "%lu, %p, %p\n", flags, buffer, count );
298 return STATUS_SUCCESS;
302 /**************************************************************************
303 * RtlSetThreadPreferredUILanguages (NTDLL.@)
305 NTSTATUS WINAPI RtlSetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count )
307 FIXME( "%lu, %p, %p\n", flags, buffer, count );
308 return STATUS_SUCCESS;
312 /******************************************************************
313 * RtlInitCodePageTable (NTDLL.@)
315 void WINAPI RtlInitCodePageTable( USHORT *ptr, CPTABLEINFO *info )
317 static const CPTABLEINFO utf8_cpinfo = { CP_UTF8, 4, '?', 0xfffd, '?', '?' };
319 if (ptr[1] == CP_UTF8) *info = utf8_cpinfo;
320 else init_codepage_table( ptr, info );
324 /**************************************************************************
325 * RtlInitNlsTables (NTDLL.@)
327 void WINAPI RtlInitNlsTables( USHORT *ansi, USHORT *oem, USHORT *casetable, NLSTABLEINFO *info )
329 RtlInitCodePageTable( ansi, &info->AnsiTableInfo );
330 RtlInitCodePageTable( oem, &info->OemTableInfo );
331 info->UpperCaseTable = casetable + 2;
332 info->LowerCaseTable = casetable + casetable[1] + 2;
336 /**************************************************************************
337 * RtlResetRtlTranslations (NTDLL.@)
339 void WINAPI RtlResetRtlTranslations( const NLSTABLEINFO *info )
341 NlsAnsiCodePage = info->AnsiTableInfo.CodePage;
342 NlsMbCodePageTag = info->AnsiTableInfo.DBCSCodePage;
343 NlsMbOemCodePageTag = info->OemTableInfo.DBCSCodePage;
344 nls_info = *info;
348 /**************************************************************************
349 * RtlGetLocaleFileMappingAddress (NTDLL.@)
351 NTSTATUS WINAPI RtlGetLocaleFileMappingAddress( void **ptr, LCID *lcid, LARGE_INTEGER *size )
353 static void *cached_ptr;
354 static LCID cached_lcid;
356 if (!cached_ptr)
358 void *addr;
359 NTSTATUS status = NtInitializeNlsFiles( &addr, &cached_lcid, size );
361 if (status) return status;
362 if (InterlockedCompareExchangePointer( &cached_ptr, addr, NULL ))
363 NtUnmapViewOfSection( GetCurrentProcess(), addr );
365 *ptr = cached_ptr;
366 *lcid = cached_lcid;
367 return STATUS_SUCCESS;
371 /**************************************************************************
372 * RtlAnsiCharToUnicodeChar (NTDLL.@)
374 WCHAR WINAPI RtlAnsiCharToUnicodeChar( char **ansi )
376 unsigned char ch = *(*ansi)++;
378 if (nls_info.AnsiTableInfo.CodePage == CP_UTF8)
380 unsigned int res;
382 if (ch < 0x80) return ch;
383 if ((res = decode_utf8_char( ch, (const char **)ansi, *ansi + 3 )) > 0x10ffff) res = 0xfffd;
384 return res;
386 if (nls_info.AnsiTableInfo.DBCSOffsets)
388 USHORT off = nls_info.AnsiTableInfo.DBCSOffsets[ch];
389 if (off) return nls_info.AnsiTableInfo.DBCSOffsets[off + (unsigned char)*(*ansi)++];
391 return nls_info.AnsiTableInfo.MultiByteTable[ch];
395 /******************************************************************************
396 * RtlCompareUnicodeStrings (NTDLL.@)
398 LONG WINAPI RtlCompareUnicodeStrings( const WCHAR *s1, SIZE_T len1, const WCHAR *s2, SIZE_T len2,
399 BOOLEAN case_insensitive )
401 LONG ret = 0;
402 SIZE_T len = min( len1, len2 );
404 if (case_insensitive)
406 if (nls_info.UpperCaseTable)
408 while (!ret && len--) ret = casemap( nls_info.UpperCaseTable, *s1++ ) -
409 casemap( nls_info.UpperCaseTable, *s2++ );
411 else /* locale not setup yet */
413 while (!ret && len--) ret = casemap_ascii( *s1++ ) - casemap_ascii( *s2++ );
416 else
418 while (!ret && len--) ret = *s1++ - *s2++;
420 if (!ret) ret = len1 - len2;
421 return ret;
425 /**************************************************************************
426 * RtlPrefixUnicodeString (NTDLL.@)
428 BOOLEAN WINAPI RtlPrefixUnicodeString( const UNICODE_STRING *s1, const UNICODE_STRING *s2,
429 BOOLEAN ignore_case )
431 unsigned int i;
433 if (s1->Length > s2->Length) return FALSE;
434 if (ignore_case)
436 for (i = 0; i < s1->Length / sizeof(WCHAR); i++)
437 if (casemap( nls_info.UpperCaseTable, s1->Buffer[i] ) !=
438 casemap( nls_info.UpperCaseTable, s2->Buffer[i] )) return FALSE;
440 else
442 for (i = 0; i < s1->Length / sizeof(WCHAR); i++)
443 if (s1->Buffer[i] != s2->Buffer[i]) return FALSE;
445 return TRUE;
450 /******************************************************************************
451 * RtlHashUnicodeString (NTDLL.@)
453 NTSTATUS WINAPI RtlHashUnicodeString( const UNICODE_STRING *string, BOOLEAN case_insensitive,
454 ULONG alg, ULONG *hash )
456 unsigned int i;
458 if (!string || !hash) return STATUS_INVALID_PARAMETER;
460 switch (alg)
462 case HASH_STRING_ALGORITHM_DEFAULT:
463 case HASH_STRING_ALGORITHM_X65599:
464 break;
465 default:
466 return STATUS_INVALID_PARAMETER;
469 *hash = 0;
470 if (!case_insensitive)
471 for (i = 0; i < string->Length / sizeof(WCHAR); i++)
472 *hash = *hash * 65599 + string->Buffer[i];
473 else if (nls_info.UpperCaseTable)
474 for (i = 0; i < string->Length / sizeof(WCHAR); i++)
475 *hash = *hash * 65599 + casemap( nls_info.UpperCaseTable, string->Buffer[i] );
476 else /* locale not setup yet */
477 for (i = 0; i < string->Length / sizeof(WCHAR); i++)
478 *hash = *hash * 65599 + casemap_ascii( string->Buffer[i] );
479 return STATUS_SUCCESS;
483 /**************************************************************************
484 * RtlCustomCPToUnicodeN (NTDLL.@)
486 NTSTATUS WINAPI RtlCustomCPToUnicodeN( CPTABLEINFO *info, WCHAR *dst, DWORD dstlen, DWORD *reslen,
487 const char *src, DWORD srclen )
489 unsigned int ret = cp_mbstowcs( info, dst, dstlen / sizeof(WCHAR), src, srclen );
490 if (reslen) *reslen = ret * sizeof(WCHAR);
491 return STATUS_SUCCESS;
495 /**************************************************************************
496 * RtlUnicodeToCustomCPN (NTDLL.@)
498 NTSTATUS WINAPI RtlUnicodeToCustomCPN( CPTABLEINFO *info, char *dst, DWORD dstlen, DWORD *reslen,
499 const WCHAR *src, DWORD srclen )
501 unsigned int ret = cp_wcstombs( info, dst, dstlen, src, srclen / sizeof(WCHAR) );
502 if (reslen) *reslen = ret;
503 return STATUS_SUCCESS;
507 /**************************************************************************
508 * RtlMultiByteToUnicodeN (NTDLL.@)
510 NTSTATUS WINAPI RtlMultiByteToUnicodeN( WCHAR *dst, DWORD dstlen, DWORD *reslen,
511 const char *src, DWORD srclen )
513 unsigned int ret;
515 if (nls_info.AnsiTableInfo.CodePage != CP_UTF8)
516 ret = cp_mbstowcs( &nls_info.AnsiTableInfo, dst, dstlen / sizeof(WCHAR), src, srclen );
517 else
518 utf8_mbstowcs( dst, dstlen / sizeof(WCHAR), &ret, src, srclen );
520 if (reslen) *reslen = ret * sizeof(WCHAR);
521 return STATUS_SUCCESS;
525 /**************************************************************************
526 * RtlMultiByteToUnicodeSize (NTDLL.@)
528 NTSTATUS WINAPI RtlMultiByteToUnicodeSize( DWORD *size, const char *str, DWORD len )
530 unsigned int ret;
532 if (nls_info.AnsiTableInfo.CodePage != CP_UTF8)
533 ret = cp_mbstowcs_size( &nls_info.AnsiTableInfo, str, len );
534 else
535 utf8_mbstowcs_size( str, len, &ret );
537 *size = ret * sizeof(WCHAR);
538 return STATUS_SUCCESS;
542 /**************************************************************************
543 * RtlOemToUnicodeN (NTDLL.@)
545 NTSTATUS WINAPI RtlOemToUnicodeN( WCHAR *dst, DWORD dstlen, DWORD *reslen,
546 const char *src, DWORD srclen )
548 unsigned int ret;
550 if (nls_info.OemTableInfo.CodePage != CP_UTF8)
551 ret = cp_mbstowcs( &nls_info.OemTableInfo, dst, dstlen / sizeof(WCHAR), src, srclen );
552 else
553 utf8_mbstowcs( dst, dstlen / sizeof(WCHAR), &ret, src, srclen );
555 if (reslen) *reslen = ret * sizeof(WCHAR);
556 return STATUS_SUCCESS;
560 /**************************************************************************
561 * RtlOemStringToUnicodeSize (NTDLL.@)
562 * RtlxOemStringToUnicodeSize (NTDLL.@)
564 DWORD WINAPI RtlOemStringToUnicodeSize( const STRING *str )
566 unsigned int ret;
568 if (nls_info.OemTableInfo.CodePage != CP_UTF8)
569 ret = cp_mbstowcs_size( &nls_info.OemTableInfo, str->Buffer, str->Length );
570 else
571 utf8_mbstowcs_size( str->Buffer, str->Length, &ret );
573 return (ret + 1) * sizeof(WCHAR);
577 /**************************************************************************
578 * RtlUnicodeStringToOemSize (NTDLL.@)
579 * RtlxUnicodeStringToOemSize (NTDLL.@)
581 DWORD WINAPI RtlUnicodeStringToOemSize( const UNICODE_STRING *str )
583 unsigned int ret;
585 if (nls_info.OemTableInfo.CodePage != CP_UTF8)
586 ret = cp_wcstombs_size( &nls_info.OemTableInfo, str->Buffer, str->Length / sizeof(WCHAR) );
587 else
588 utf8_wcstombs_size( str->Buffer, str->Length / sizeof(WCHAR), &ret );
590 return ret + 1;
594 /**************************************************************************
595 * RtlUnicodeToMultiByteN (NTDLL.@)
597 NTSTATUS WINAPI RtlUnicodeToMultiByteN( char *dst, DWORD dstlen, DWORD *reslen,
598 const WCHAR *src, DWORD srclen )
600 unsigned int ret;
602 if (nls_info.AnsiTableInfo.CodePage != CP_UTF8)
603 ret = cp_wcstombs( &nls_info.AnsiTableInfo, dst, dstlen, src, srclen / sizeof(WCHAR) );
604 else
605 utf8_wcstombs( dst, dstlen, &ret, src, srclen / sizeof(WCHAR) );
607 if (reslen) *reslen = ret;
608 return STATUS_SUCCESS;
612 /**************************************************************************
613 * RtlUnicodeToMultiByteSize (NTDLL.@)
615 NTSTATUS WINAPI RtlUnicodeToMultiByteSize( DWORD *size, const WCHAR *str, DWORD len )
617 unsigned int ret;
619 if (nls_info.AnsiTableInfo.CodePage != CP_UTF8)
620 ret = cp_wcstombs_size( &nls_info.AnsiTableInfo, str, len / sizeof(WCHAR) );
621 else
622 utf8_wcstombs_size( str, len / sizeof(WCHAR), &ret );
624 *size = ret;
625 return STATUS_SUCCESS;
629 /**************************************************************************
630 * RtlUnicodeToOemN (NTDLL.@)
632 NTSTATUS WINAPI RtlUnicodeToOemN( char *dst, DWORD dstlen, DWORD *reslen,
633 const WCHAR *src, DWORD srclen )
635 unsigned int ret;
637 if (nls_info.OemTableInfo.CodePage != CP_UTF8)
638 ret = cp_wcstombs( &nls_info.OemTableInfo, dst, dstlen, src, srclen / sizeof(WCHAR) );
639 else
640 utf8_wcstombs( dst, dstlen, &ret, src, srclen / sizeof(WCHAR) );
642 if (reslen) *reslen = ret;
643 return STATUS_SUCCESS;
647 /**************************************************************************
648 * RtlDowncaseUnicodeChar (NTDLL.@)
650 WCHAR WINAPI RtlDowncaseUnicodeChar( WCHAR wch )
652 if (nls_info.LowerCaseTable) return casemap( nls_info.LowerCaseTable, wch );
653 if (wch >= 'A' && wch <= 'Z') wch += 'a' - 'A';
654 return wch;
658 /**************************************************************************
659 * RtlDowncaseUnicodeString (NTDLL.@)
661 NTSTATUS WINAPI RtlDowncaseUnicodeString( UNICODE_STRING *dest, const UNICODE_STRING *src,
662 BOOLEAN alloc )
664 DWORD i, len = src->Length;
666 if (alloc)
668 dest->MaximumLength = len;
669 if (!(dest->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len ))) return STATUS_NO_MEMORY;
671 else if (len > dest->MaximumLength) return STATUS_BUFFER_OVERFLOW;
673 for (i = 0; i < len / sizeof(WCHAR); i++)
674 dest->Buffer[i] = casemap( nls_info.LowerCaseTable, src->Buffer[i] );
675 dest->Length = len;
676 return STATUS_SUCCESS;
680 /**************************************************************************
681 * RtlUpcaseUnicodeChar (NTDLL.@)
683 WCHAR WINAPI RtlUpcaseUnicodeChar( WCHAR wch )
685 return casemap( nls_info.UpperCaseTable, wch );
689 /**************************************************************************
690 * RtlUpcaseUnicodeString (NTDLL.@)
692 NTSTATUS WINAPI RtlUpcaseUnicodeString( UNICODE_STRING *dest, const UNICODE_STRING *src,
693 BOOLEAN alloc )
695 DWORD i, len = src->Length;
697 if (alloc)
699 dest->MaximumLength = len;
700 if (!(dest->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len ))) return STATUS_NO_MEMORY;
702 else if (len > dest->MaximumLength) return STATUS_BUFFER_OVERFLOW;
704 for (i = 0; i < len / sizeof(WCHAR); i++)
705 dest->Buffer[i] = casemap( nls_info.UpperCaseTable, src->Buffer[i] );
706 dest->Length = len;
707 return STATUS_SUCCESS;
711 /**************************************************************************
712 * RtlUpcaseUnicodeToCustomCPN (NTDLL.@)
714 NTSTATUS WINAPI RtlUpcaseUnicodeToCustomCPN( CPTABLEINFO *info, char *dst, DWORD dstlen, DWORD *reslen,
715 const WCHAR *src, DWORD srclen )
717 DWORD i, ret;
719 srclen /= sizeof(WCHAR);
720 if (info->DBCSCodePage)
722 WCHAR *uni2cp = info->WideCharTable;
724 for (i = dstlen; srclen && i; i--, srclen--, src++)
726 WCHAR ch = casemap( nls_info.UpperCaseTable, *src );
727 if (uni2cp[ch] & 0xff00)
729 if (i == 1) break; /* do not output a partial char */
730 i--;
731 *dst++ = uni2cp[ch] >> 8;
733 *dst++ = (char)uni2cp[ch];
735 ret = dstlen - i;
737 else
739 char *uni2cp = info->WideCharTable;
740 ret = min( srclen, dstlen );
741 for (i = 0; i < ret; i++) dst[i] = uni2cp[casemap( nls_info.UpperCaseTable, src[i] )];
743 if (reslen) *reslen = ret;
744 return STATUS_SUCCESS;
748 static NTSTATUS upcase_unicode_to_utf8( char *dst, DWORD dstlen, DWORD *reslen,
749 const WCHAR *src, DWORD srclen )
751 char *end;
752 unsigned int val;
753 NTSTATUS status = STATUS_SUCCESS;
755 srclen /= sizeof(WCHAR);
757 for (end = dst + dstlen; srclen; srclen--, src++)
759 WCHAR ch = casemap( nls_info.UpperCaseTable, *src );
761 if (ch < 0x80) /* 0x00-0x7f: 1 byte */
763 if (dst > end - 1) break;
764 *dst++ = ch;
765 continue;
767 if (ch < 0x800) /* 0x80-0x7ff: 2 bytes */
769 if (dst > end - 2) break;
770 dst[1] = 0x80 | (ch & 0x3f);
771 ch >>= 6;
772 dst[0] = 0xc0 | ch;
773 dst += 2;
774 continue;
776 if (!get_utf16( src, srclen, &val ))
778 val = 0xfffd;
779 status = STATUS_SOME_NOT_MAPPED;
781 if (val < 0x10000) /* 0x800-0xffff: 3 bytes */
783 if (dst > end - 3) break;
784 dst[2] = 0x80 | (val & 0x3f);
785 val >>= 6;
786 dst[1] = 0x80 | (val & 0x3f);
787 val >>= 6;
788 dst[0] = 0xe0 | val;
789 dst += 3;
791 else /* 0x10000-0x10ffff: 4 bytes */
793 if (dst > end - 4) break;
794 dst[3] = 0x80 | (val & 0x3f);
795 val >>= 6;
796 dst[2] = 0x80 | (val & 0x3f);
797 val >>= 6;
798 dst[1] = 0x80 | (val & 0x3f);
799 val >>= 6;
800 dst[0] = 0xf0 | val;
801 dst += 4;
802 src++;
803 srclen--;
806 if (srclen) status = STATUS_BUFFER_TOO_SMALL;
807 if (reslen) *reslen = dstlen - (end - dst);
808 return status;
811 /**************************************************************************
812 * RtlUpcaseUnicodeToMultiByteN (NTDLL.@)
814 NTSTATUS WINAPI RtlUpcaseUnicodeToMultiByteN( char *dst, DWORD dstlen, DWORD *reslen,
815 const WCHAR *src, DWORD srclen )
817 if (nls_info.AnsiTableInfo.CodePage == CP_UTF8)
818 return upcase_unicode_to_utf8( dst, dstlen, reslen, src, srclen );
819 return RtlUpcaseUnicodeToCustomCPN( &nls_info.AnsiTableInfo, dst, dstlen, reslen, src, srclen );
823 /**************************************************************************
824 * RtlUpcaseUnicodeToOemN (NTDLL.@)
826 NTSTATUS WINAPI RtlUpcaseUnicodeToOemN( char *dst, DWORD dstlen, DWORD *reslen,
827 const WCHAR *src, DWORD srclen )
829 if (nls_info.OemTableInfo.CodePage == CP_UTF8)
830 return upcase_unicode_to_utf8( dst, dstlen, reslen, src, srclen );
831 return RtlUpcaseUnicodeToCustomCPN( &nls_info.OemTableInfo, dst, dstlen, reslen, src, srclen );
835 /*********************************************************************
836 * towlower (NTDLL.@)
838 WCHAR __cdecl towlower( WCHAR ch )
840 if (ch >= 0x100) return ch;
841 return casemap( nls_info.LowerCaseTable, ch );
845 /*********************************************************************
846 * towupper (NTDLL.@)
848 WCHAR __cdecl towupper( WCHAR ch )
850 if (nls_info.UpperCaseTable) return casemap( nls_info.UpperCaseTable, ch );
851 return casemap_ascii( ch );
855 /******************************************************************
856 * RtlIsValidLocaleName (NTDLL.@)
858 BOOLEAN WINAPI RtlIsValidLocaleName( const WCHAR *name, ULONG flags )
860 const NLS_LOCALE_LCNAME_INDEX *entry = find_lcname_entry( locale_table, name );
862 if (!entry) return FALSE;
863 /* reject neutral locale unless flag 2 is set */
864 if (!(flags & 2) && !get_locale_data( locale_table, entry->idx )->inotneutral) return FALSE;
865 return TRUE;
869 /******************************************************************
870 * RtlLcidToLocaleName (NTDLL.@)
872 NTSTATUS WINAPI RtlLcidToLocaleName( LCID lcid, UNICODE_STRING *str, ULONG flags, BOOLEAN alloc )
874 const NLS_LOCALE_LCID_INDEX *entry;
875 const WCHAR *name;
876 ULONG len;
878 if (!str) return STATUS_INVALID_PARAMETER_2;
880 switch (lcid)
882 case LOCALE_USER_DEFAULT:
883 NtQueryDefaultLocale( TRUE, &lcid );
884 break;
885 case LOCALE_SYSTEM_DEFAULT:
886 case LOCALE_CUSTOM_DEFAULT:
887 lcid = system_lcid;
888 break;
889 case LOCALE_CUSTOM_UI_DEFAULT:
890 return STATUS_UNSUCCESSFUL;
891 case LOCALE_CUSTOM_UNSPECIFIED:
892 return STATUS_INVALID_PARAMETER_1;
895 if (!(entry = find_lcid_entry( locale_table, lcid ))) return STATUS_INVALID_PARAMETER_1;
896 /* reject neutral locale unless flag 2 is set */
897 if (!(flags & 2) && !get_locale_data( locale_table, entry->idx )->inotneutral)
898 return STATUS_INVALID_PARAMETER_1;
900 name = locale_strings + entry->name;
901 len = *name++;
903 if (alloc)
905 if (!(str->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
906 return STATUS_NO_MEMORY;
907 str->MaximumLength = (len + 1) * sizeof(WCHAR);
909 else if (str->MaximumLength < (len + 1) * sizeof(WCHAR)) return STATUS_BUFFER_TOO_SMALL;
911 wcscpy( str->Buffer, name );
912 str->Length = len * sizeof(WCHAR);
913 TRACE( "%04lx -> %s\n", lcid, debugstr_us(str) );
914 return STATUS_SUCCESS;
918 /******************************************************************
919 * RtlLocaleNameToLcid (NTDLL.@)
921 NTSTATUS WINAPI RtlLocaleNameToLcid( const WCHAR *name, LCID *lcid, ULONG flags )
923 const NLS_LOCALE_LCNAME_INDEX *entry = find_lcname_entry( locale_table, name );
925 if (!entry) return STATUS_INVALID_PARAMETER_1;
926 /* reject neutral locale unless flag 2 is set */
927 if (!(flags & 2) && !get_locale_data( locale_table, entry->idx )->inotneutral)
928 return STATUS_INVALID_PARAMETER_1;
929 *lcid = entry->id;
930 TRACE( "%s -> %04lx\n", debugstr_w(name), *lcid );
931 return STATUS_SUCCESS;
935 /**************************************************************************
936 * RtlUTF8ToUnicodeN (NTDLL.@)
938 NTSTATUS WINAPI RtlUTF8ToUnicodeN( WCHAR *dst, DWORD dstlen, DWORD *reslen, const char *src, DWORD srclen )
940 unsigned int ret;
941 NTSTATUS status;
943 if (!src) return STATUS_INVALID_PARAMETER_4;
944 if (!reslen) return STATUS_INVALID_PARAMETER;
946 if (!dst)
947 status = utf8_mbstowcs_size( src, srclen, &ret );
948 else
949 status = utf8_mbstowcs( dst, dstlen / sizeof(WCHAR), &ret, src, srclen );
951 *reslen = ret * sizeof(WCHAR);
952 return status;
956 /**************************************************************************
957 * RtlUnicodeToUTF8N (NTDLL.@)
959 NTSTATUS WINAPI RtlUnicodeToUTF8N( char *dst, DWORD dstlen, DWORD *reslen, const WCHAR *src, DWORD srclen )
961 unsigned int ret;
962 NTSTATUS status;
964 if (!src) return STATUS_INVALID_PARAMETER_4;
965 if (!reslen) return STATUS_INVALID_PARAMETER;
966 if (dst && (srclen & 1)) return STATUS_INVALID_PARAMETER_5;
968 if (!dst)
969 status = utf8_wcstombs_size( src, srclen / sizeof(WCHAR), &ret );
970 else
971 status = utf8_wcstombs( dst, dstlen, &ret, src, srclen / sizeof(WCHAR) );
973 *reslen = ret;
974 return status;
978 /******************************************************************************
979 * RtlIsNormalizedString (NTDLL.@)
981 NTSTATUS WINAPI RtlIsNormalizedString( ULONG form, const WCHAR *str, INT len, BOOLEAN *res )
983 const struct norm_table *info;
984 NTSTATUS status;
985 BYTE props, class, last_class = 0;
986 unsigned int ch;
987 int i, r, result = 1;
989 if ((status = load_norm_table( form, &info ))) return status;
991 if (len == -1) len = wcslen( str );
993 for (i = 0; i < len && result; i += r)
995 if (!(r = get_utf16( str + i, len - i, &ch ))) return STATUS_NO_UNICODE_TRANSLATION;
996 if (info->comp_size)
998 if ((ch >= HANGUL_VBASE && ch < HANGUL_VBASE + HANGUL_VCOUNT) ||
999 (ch >= HANGUL_TBASE && ch < HANGUL_TBASE + HANGUL_TCOUNT))
1001 result = -1; /* QC=Maybe */
1002 continue;
1005 else if (ch >= HANGUL_SBASE && ch < HANGUL_SBASE + HANGUL_SCOUNT)
1007 result = 0; /* QC=No */
1008 break;
1010 props = get_char_props( info, ch );
1011 class = props & 0x3f;
1012 if (class == 0x3f)
1014 last_class = 0;
1015 if (props == 0xbf) result = 0; /* QC=No */
1016 else if (props == 0xff)
1018 /* ignore other chars in Hangul range */
1019 if (ch >= HANGUL_LBASE && ch < HANGUL_LBASE + 0x100) continue;
1020 if (ch >= HANGUL_SBASE && ch < HANGUL_SBASE + 0x2c00) continue;
1021 /* allow final null */
1022 if (!ch && i == len - 1) continue;
1023 return STATUS_NO_UNICODE_TRANSLATION;
1026 else if (props & 0x80)
1028 if ((props & 0xc0) == 0xc0) result = -1; /* QC=Maybe */
1029 if (class && class < last_class) result = 0; /* QC=No */
1030 last_class = class;
1032 else last_class = 0;
1035 if (result == -1)
1037 int dstlen = len * 4;
1038 NTSTATUS status;
1039 WCHAR *buffer = RtlAllocateHeap( GetProcessHeap(), 0, dstlen * sizeof(WCHAR) );
1040 if (!buffer) return STATUS_NO_MEMORY;
1041 status = RtlNormalizeString( form, str, len, buffer, &dstlen );
1042 result = !status && (dstlen == len) && !wcsncmp( buffer, str, len );
1043 RtlFreeHeap( GetProcessHeap(), 0, buffer );
1045 *res = result;
1046 return STATUS_SUCCESS;
1050 /******************************************************************************
1051 * RtlNormalizeString (NTDLL.@)
1053 NTSTATUS WINAPI RtlNormalizeString( ULONG form, const WCHAR *src, INT src_len, WCHAR *dst, INT *dst_len )
1055 int buf_len;
1056 WCHAR *buf = NULL;
1057 const struct norm_table *info;
1058 NTSTATUS status = STATUS_SUCCESS;
1060 TRACE( "%lx %s %d %p %d\n", form, debugstr_wn(src, src_len), src_len, dst, *dst_len );
1062 if ((status = load_norm_table( form, &info ))) return status;
1064 if (src_len == -1) src_len = wcslen(src) + 1;
1066 if (!*dst_len)
1068 *dst_len = src_len * info->len_factor;
1069 if (*dst_len > 64) *dst_len = max( 64, src_len + src_len / 8 );
1070 return STATUS_SUCCESS;
1072 if (!src_len)
1074 *dst_len = 0;
1075 return STATUS_SUCCESS;
1078 if (!info->comp_size) return decompose_string( info, src, src_len, dst, dst_len );
1080 buf_len = src_len * 4;
1081 for (;;)
1083 buf = RtlAllocateHeap( GetProcessHeap(), 0, buf_len * sizeof(WCHAR) );
1084 if (!buf) return STATUS_NO_MEMORY;
1085 status = decompose_string( info, src, src_len, buf, &buf_len );
1086 if (status != STATUS_BUFFER_TOO_SMALL) break;
1087 RtlFreeHeap( GetProcessHeap(), 0, buf );
1089 if (!status)
1091 buf_len = compose_string( info, buf, buf_len );
1092 if (*dst_len >= buf_len) memcpy( dst, buf, buf_len * sizeof(WCHAR) );
1093 else status = STATUS_BUFFER_TOO_SMALL;
1095 RtlFreeHeap( GetProcessHeap(), 0, buf );
1096 *dst_len = buf_len;
1097 return status;
1101 /* Punycode parameters */
1102 enum { BASE = 36, TMIN = 1, TMAX = 26, SKEW = 38, DAMP = 700 };
1104 static BOOL check_invalid_chars( const struct norm_table *info, DWORD flags,
1105 const unsigned int *buffer, int len )
1107 int i;
1109 for (i = 0; i < len; i++)
1111 switch (buffer[i])
1113 case 0x200c: /* zero-width non-joiner */
1114 case 0x200d: /* zero-width joiner */
1115 if (!i || get_combining_class( info, buffer[i - 1] ) != 9) return TRUE;
1116 break;
1117 case 0x2260: /* not equal to */
1118 case 0x226e: /* not less than */
1119 case 0x226f: /* not greater than */
1120 if (flags & IDN_USE_STD3_ASCII_RULES) return TRUE;
1121 break;
1123 switch (get_char_props( info, buffer[i] ))
1125 case 0xbf:
1126 return TRUE;
1127 case 0xff:
1128 if (buffer[i] >= HANGUL_SBASE && buffer[i] < HANGUL_SBASE + 0x2c00) break;
1129 return TRUE;
1130 case 0x7f:
1131 if (!(flags & IDN_ALLOW_UNASSIGNED)) return TRUE;
1132 break;
1136 if ((flags & IDN_USE_STD3_ASCII_RULES) && len && (buffer[0] == '-' || buffer[len - 1] == '-'))
1137 return TRUE;
1139 return FALSE;
1143 /******************************************************************************
1144 * RtlIdnToAscii (NTDLL.@)
1146 NTSTATUS WINAPI RtlIdnToAscii( DWORD flags, const WCHAR *src, INT srclen, WCHAR *dst, INT *dstlen )
1148 static const WCHAR prefixW[] = {'x','n','-','-'};
1149 const struct norm_table *info;
1150 NTSTATUS status;
1151 WCHAR normstr[256], res[256];
1152 unsigned int ch, buffer[64];
1153 int i, len, start, end, out_label, out = 0, normlen = ARRAY_SIZE(normstr);
1155 TRACE( "%lx %s %p %d\n", flags, debugstr_wn(src, srclen), dst, *dstlen );
1157 if ((status = load_norm_table( 13, &info ))) return status;
1159 if ((status = RtlIdnToNameprepUnicode( flags, src, srclen, normstr, &normlen ))) return status;
1161 /* implementation of Punycode based on RFC 3492 */
1163 for (start = 0; start < normlen; start = end + 1)
1165 int n = 0x80, bias = 72, delta = 0, b = 0, h, buflen = 0;
1167 out_label = out;
1168 for (i = start; i < normlen; i += len)
1170 if (!(len = get_utf16( normstr + i, normlen - i, &ch ))) break;
1171 if (!ch || ch == '.') break;
1172 if (ch < 0x80) b++;
1173 buffer[buflen++] = ch;
1175 end = i;
1177 if (b == end - start)
1179 if (end < normlen) b++;
1180 if (out + b > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
1181 memcpy( res + out, normstr + start, b * sizeof(WCHAR) );
1182 out += b;
1183 continue;
1186 if (buflen >= 4 && buffer[2] == '-' && buffer[3] == '-') return STATUS_INVALID_IDN_NORMALIZATION;
1187 if (check_invalid_chars( info, flags, buffer, buflen )) return STATUS_INVALID_IDN_NORMALIZATION;
1189 if (out + 5 + b > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
1190 memcpy( res + out, prefixW, sizeof(prefixW) );
1191 out += ARRAY_SIZE(prefixW);
1192 if (b)
1194 for (i = start; i < end; i++) if (normstr[i] < 0x80) res[out++] = normstr[i];
1195 res[out++] = '-';
1198 for (h = b; h < buflen; delta++, n++)
1200 int m = 0x10ffff, q, k;
1202 for (i = 0; i < buflen; i++) if (buffer[i] >= n && m > buffer[i]) m = buffer[i];
1203 delta += (m - n) * (h + 1);
1204 n = m;
1206 for (i = 0; i < buflen; i++)
1208 if (buffer[i] == n)
1210 for (q = delta, k = BASE; ; k += BASE)
1212 int t = k <= bias ? TMIN : k >= bias + TMAX ? TMAX : k - bias;
1213 int disp = q < t ? q : t + (q - t) % (BASE - t);
1214 if (out + 1 > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
1215 res[out++] = disp <= 25 ? 'a' + disp : '0' + disp - 26;
1216 if (q < t) break;
1217 q = (q - t) / (BASE - t);
1219 delta /= (h == b ? DAMP : 2);
1220 delta += delta / (h + 1);
1221 for (k = 0; delta > ((BASE - TMIN) * TMAX) / 2; k += BASE) delta /= BASE - TMIN;
1222 bias = k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
1223 delta = 0;
1224 h++;
1226 else if (buffer[i] < n) delta++;
1230 if (out - out_label > 63) return STATUS_INVALID_IDN_NORMALIZATION;
1232 if (end < normlen)
1234 if (out + 1 > ARRAY_SIZE(res)) return STATUS_INVALID_IDN_NORMALIZATION;
1235 res[out++] = normstr[end];
1239 if (*dstlen)
1241 if (out <= *dstlen) memcpy( dst, res, out * sizeof(WCHAR) );
1242 else status = STATUS_BUFFER_TOO_SMALL;
1244 *dstlen = out;
1245 return status;
1249 /******************************************************************************
1250 * RtlIdnToNameprepUnicode (NTDLL.@)
1252 NTSTATUS WINAPI RtlIdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen,
1253 WCHAR *dst, INT *dstlen )
1255 const struct norm_table *info;
1256 unsigned int ch;
1257 NTSTATUS status;
1258 WCHAR buf[256];
1259 int i, start, len, buflen = ARRAY_SIZE(buf);
1261 if (flags & ~(IDN_ALLOW_UNASSIGNED | IDN_USE_STD3_ASCII_RULES)) return STATUS_INVALID_PARAMETER;
1262 if (!src || srclen < -1) return STATUS_INVALID_PARAMETER;
1264 TRACE( "%lx %s %p %d\n", flags, debugstr_wn(src, srclen), dst, *dstlen );
1266 if ((status = load_norm_table( 13, &info ))) return status;
1268 if (srclen == -1) srclen = wcslen(src) + 1;
1270 for (i = 0; i < srclen; i++) if (src[i] < 0x20 || src[i] >= 0x7f) break;
1272 if (i == srclen || (i == srclen - 1 && !src[i])) /* ascii only */
1274 if (srclen > buflen) return STATUS_INVALID_IDN_NORMALIZATION;
1275 memcpy( buf, src, srclen * sizeof(WCHAR) );
1276 buflen = srclen;
1278 else if ((status = RtlNormalizeString( 13, src, srclen, buf, &buflen )))
1280 if (status == STATUS_NO_UNICODE_TRANSLATION) status = STATUS_INVALID_IDN_NORMALIZATION;
1281 return status;
1284 for (i = start = 0; i < buflen; i += len)
1286 if (!(len = get_utf16( buf + i, buflen - i, &ch ))) break;
1287 if (!ch) break;
1288 if (ch == '.')
1290 if (start == i) return STATUS_INVALID_IDN_NORMALIZATION;
1291 /* maximal label length is 63 characters */
1292 if (i - start > 63) return STATUS_INVALID_IDN_NORMALIZATION;
1293 if ((flags & IDN_USE_STD3_ASCII_RULES) && (buf[start] == '-' || buf[i-1] == '-'))
1294 return STATUS_INVALID_IDN_NORMALIZATION;
1295 start = i + 1;
1296 continue;
1298 if (flags & IDN_USE_STD3_ASCII_RULES)
1300 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1301 (ch >= '0' && ch <= '9') || ch == '-') continue;
1302 return STATUS_INVALID_IDN_NORMALIZATION;
1304 if (!(flags & IDN_ALLOW_UNASSIGNED))
1306 if (get_char_props( info, ch ) == 0x7f) return STATUS_INVALID_IDN_NORMALIZATION;
1309 if (!i || i - start > 63) return STATUS_INVALID_IDN_NORMALIZATION;
1310 if ((flags & IDN_USE_STD3_ASCII_RULES) && (buf[start] == '-' || buf[i-1] == '-'))
1311 return STATUS_INVALID_IDN_NORMALIZATION;
1313 if (*dstlen)
1315 if (buflen <= *dstlen) memcpy( dst, buf, buflen * sizeof(WCHAR) );
1316 else status = STATUS_BUFFER_TOO_SMALL;
1318 *dstlen = buflen;
1319 return status;
1323 /******************************************************************************
1324 * RtlIdnToUnicode (NTDLL.@)
1326 NTSTATUS WINAPI RtlIdnToUnicode( DWORD flags, const WCHAR *src, INT srclen, WCHAR *dst, INT *dstlen )
1328 const struct norm_table *info;
1329 int i, buflen, start, end, out_label, out = 0;
1330 NTSTATUS status;
1331 UINT buffer[64];
1332 WCHAR ch = 0;
1334 if (!src || srclen < -1) return STATUS_INVALID_PARAMETER;
1335 if (srclen == -1) srclen = wcslen( src ) + 1;
1337 TRACE( "%lx %s %p %d\n", flags, debugstr_wn(src, srclen), dst, *dstlen );
1339 if ((status = load_norm_table( 13, &info ))) return status;
1341 for (start = 0; start < srclen; )
1343 int n = 0x80, bias = 72, pos = 0, old_pos, w, k, t, delim = 0, digit, delta;
1345 out_label = out;
1346 for (i = start; i < srclen; i++)
1348 ch = src[i];
1349 if (ch > 0x7f || (i != srclen - 1 && !ch)) return STATUS_INVALID_IDN_NORMALIZATION;
1350 if (!ch || ch == '.') break;
1351 if (ch == '-') delim = i;
1353 if (!(flags & IDN_USE_STD3_ASCII_RULES)) continue;
1354 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1355 (ch >= '0' && ch <= '9') || ch == '-')
1356 continue;
1357 return STATUS_INVALID_IDN_NORMALIZATION;
1359 end = i;
1361 /* last label may be empty */
1362 if (start == end && ch) return STATUS_INVALID_IDN_NORMALIZATION;
1364 if (end - start < 4 ||
1365 (src[start] != 'x' && src[start] != 'X') ||
1366 (src[start + 1] != 'n' && src[start + 1] != 'N') ||
1367 src[start + 2] != '-' || src[start + 3] != '-')
1369 if (end - start > 63) return STATUS_INVALID_IDN_NORMALIZATION;
1371 if ((flags & IDN_USE_STD3_ASCII_RULES) && (src[start] == '-' || src[end - 1] == '-'))
1372 return STATUS_INVALID_IDN_NORMALIZATION;
1374 if (end < srclen) end++;
1375 if (*dstlen)
1377 if (out + end - start <= *dstlen)
1378 memcpy( dst + out, src + start, (end - start) * sizeof(WCHAR));
1379 else return STATUS_BUFFER_TOO_SMALL;
1381 out += end - start;
1382 start = end;
1383 continue;
1386 if (delim == start + 3) delim++;
1387 buflen = 0;
1388 for (i = start + 4; i < delim && buflen < ARRAY_SIZE(buffer); i++) buffer[buflen++] = src[i];
1389 if (buflen) i++;
1390 while (i < end)
1392 old_pos = pos;
1393 w = 1;
1394 for (k = BASE; ; k += BASE)
1396 if (i >= end) return STATUS_INVALID_IDN_NORMALIZATION;
1397 ch = src[i++];
1398 if (ch >= 'a' && ch <= 'z') digit = ch - 'a';
1399 else if (ch >= 'A' && ch <= 'Z') digit = ch - 'A';
1400 else if (ch >= '0' && ch <= '9') digit = ch - '0' + 26;
1401 else return STATUS_INVALID_IDN_NORMALIZATION;
1402 pos += digit * w;
1403 t = k <= bias ? TMIN : k >= bias + TMAX ? TMAX : k - bias;
1404 if (digit < t) break;
1405 w *= BASE - t;
1408 delta = (pos - old_pos) / (!old_pos ? DAMP : 2);
1409 delta += delta / (buflen + 1);
1410 for (k = 0; delta > ((BASE - TMIN) * TMAX) / 2; k += BASE) delta /= BASE - TMIN;
1411 bias = k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
1412 n += pos / (buflen + 1);
1413 pos %= buflen + 1;
1415 if (buflen >= ARRAY_SIZE(buffer) - 1) return STATUS_INVALID_IDN_NORMALIZATION;
1416 memmove( buffer + pos + 1, buffer + pos, (buflen - pos) * sizeof(*buffer) );
1417 buffer[pos++] = n;
1418 buflen++;
1421 if (check_invalid_chars( info, flags, buffer, buflen )) return STATUS_INVALID_IDN_NORMALIZATION;
1423 for (i = 0; i < buflen; i++)
1425 int len = 1 + (buffer[i] >= 0x10000);
1426 if (*dstlen)
1428 if (out + len <= *dstlen) put_utf16( dst + out, buffer[i] );
1429 else return STATUS_BUFFER_TOO_SMALL;
1431 out += len;
1434 if (out - out_label > 63) return STATUS_INVALID_IDN_NORMALIZATION;
1436 if (end < srclen)
1438 if (*dstlen)
1440 if (out + 1 <= *dstlen) dst[out] = src[end];
1441 else return STATUS_BUFFER_TOO_SMALL;
1443 out++;
1445 start = end + 1;
1447 *dstlen = out;
1448 return STATUS_SUCCESS;