2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/winnls/string/nls.c
5 * PURPOSE: National Language Support
6 * PROGRAMMER: Filip Navara
10 * Katayama Hirofumi MZ
15 /* INCLUDES *******************************************************************/
22 /* GLOBAL VARIABLES ***********************************************************/
24 /* Sequence length based on the first character. */
25 static const char UTF8Length
[128] =
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
29 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
31 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
32 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
33 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
34 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xF0 - 0xFF */
37 /* First byte mask depending on UTF-8 sequence length. */
38 static const unsigned char UTF8Mask
[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
40 /* UTF-8 length to lower bound */
41 static const unsigned long UTF8LBound
[] =
42 {0, 0x80, 0x800, 0x10000, 0x200000, 0x2000000, 0xFFFFFFFF};
44 /* FIXME: Change to HASH table or linear array. */
45 static LIST_ENTRY CodePageListHead
;
46 static CODEPAGE_ENTRY AnsiCodePage
;
47 static CODEPAGE_ENTRY OemCodePage
;
48 static RTL_CRITICAL_SECTION CodePageListLock
;
50 /* FORWARD DECLARATIONS *******************************************************/
53 GetNlsSectionName(UINT CodePage
, UINT Base
, ULONG Unknown
,
54 LPSTR BaseName
, LPSTR Result
, ULONG ResultSize
);
57 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
);
61 CreateNlsSecurityDescriptor(
62 _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor
,
63 _In_ SIZE_T DescriptorSize
,
64 _In_ ULONG AccessMask
);
66 /* PRIVATE FUNCTIONS **********************************************************/
70 * Creates a security descriptor for the NLS object directory.
72 * @param[out] SecurityDescriptor
73 * @param[in] DescriptorSize
74 * Same parameters as for CreateNlsSecurityDescriptor().
77 * Everyone (World SID) is given read access to the NLS directory,
78 * whereas Admins are given full access.
81 CreateNlsDirectorySecurity(
82 _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor
,
83 _In_ SIZE_T DescriptorSize
)
85 static SID_IDENTIFIER_AUTHORITY NtAuthority
= {SECURITY_NT_AUTHORITY
};
89 BOOLEAN DaclPresent
, DaclDefaulted
;
91 /* Give everyone basic directory access */
92 Status
= CreateNlsSecurityDescriptor(SecurityDescriptor
,
94 DIRECTORY_TRAVERSE
| DIRECTORY_CREATE_OBJECT
);
95 if (!NT_SUCCESS(Status
))
97 DPRINT1("Failed to create basic NLS SD (Status 0x%08x)\n", Status
);
101 /* Create the Admins SID */
102 // NOTE: Win <= 2k3 uses SYSTEM instead (SECURITY_LOCAL_SYSTEM_RID with one SubAuthority)
103 Status
= RtlAllocateAndInitializeSid(&NtAuthority
,
105 SECURITY_BUILTIN_DOMAIN_RID
,
106 DOMAIN_ALIAS_RID_ADMINS
,
109 if (!NT_SUCCESS(Status
))
111 DPRINT1("Failed to create Admins SID (Status 0x%08x)\n", Status
);
115 /* Retrieve the DACL from the descriptor */
116 Status
= RtlGetDaclSecurityDescriptor(SecurityDescriptor
,
120 if (!NT_SUCCESS(Status
) || !DaclPresent
|| !Dacl
)
122 DPRINT1("Failed to get DACL from descriptor (Status 0x%08x)\n", Status
);
126 /* Add an allowed access ACE to the Admins SID with full access.
127 * The function verifies the DACL is large enough to accommodate it. */
128 Status
= RtlAddAccessAllowedAce(Dacl
,
130 DIRECTORY_ALL_ACCESS
,
132 if (!NT_SUCCESS(Status
))
134 DPRINT1("Failed to add allowed access ACE for Admins SID (Status 0x%08x)\n", Status
);
139 RtlFreeSid(AdminsSid
);
146 * Internal NLS related stuff initialization.
154 UNICODE_STRING DirName
;
155 OBJECT_ATTRIBUTES ObjectAttributes
;
157 UCHAR SecurityDescriptor
[NLS_SECTION_SECURITY_DESCRIPTOR_SIZE
+
158 NLS_SIZEOF_ACE_AND_SIDS(2)];
160 InitializeListHead(&CodePageListHead
);
161 RtlInitializeCriticalSection(&CodePageListLock
);
164 * FIXME: Eventually this should be done only for the NLS Server
165 * process, but since we don't have anything like that (yet?) we
166 * always try to create the "\NLS" directory here.
168 RtlInitUnicodeString(&DirName
, L
"\\NLS");
170 /* Create a security descriptor for the NLS directory */
171 Status
= CreateNlsDirectorySecurity(&SecurityDescriptor
,
172 sizeof(SecurityDescriptor
));
173 if (!NT_SUCCESS(Status
))
175 DPRINT1("Failed to create NLS directory security (Status 0x%08x)\n", Status
);
179 InitializeObjectAttributes(&ObjectAttributes
,
181 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
183 &SecurityDescriptor
);
185 Status
= NtCreateDirectoryObject(&Handle
,
186 DIRECTORY_TRAVERSE
| DIRECTORY_CREATE_OBJECT
,
188 if (NT_SUCCESS(Status
))
193 /* Setup ANSI code page. */
194 AnsiCodePage
.SectionHandle
= NULL
;
195 AnsiCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->AnsiCodePageData
;
197 RtlInitCodePageTable((PUSHORT
)AnsiCodePage
.SectionMapping
,
198 &AnsiCodePage
.CodePageTable
);
199 AnsiCodePage
.CodePage
= AnsiCodePage
.CodePageTable
.CodePage
;
201 InsertTailList(&CodePageListHead
, &AnsiCodePage
.Entry
);
203 /* Setup OEM code page. */
204 OemCodePage
.SectionHandle
= NULL
;
205 OemCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->OemCodePageData
;
207 RtlInitCodePageTable((PUSHORT
)OemCodePage
.SectionMapping
,
208 &OemCodePage
.CodePageTable
);
209 OemCodePage
.CodePage
= OemCodePage
.CodePageTable
.CodePage
;
210 InsertTailList(&CodePageListHead
, &OemCodePage
.Entry
);
218 * Internal NLS related stuff uninitialization.
225 PCODEPAGE_ENTRY Current
;
227 /* Delete the code page list. */
228 while (!IsListEmpty(&CodePageListHead
))
230 Current
= CONTAINING_RECORD(CodePageListHead
.Flink
, CODEPAGE_ENTRY
, Entry
);
231 if (Current
->SectionHandle
!= NULL
)
233 UnmapViewOfFile(Current
->SectionMapping
);
234 NtClose(Current
->SectionHandle
);
236 RemoveHeadList(&CodePageListHead
);
238 RtlDeleteCriticalSection(&CodePageListLock
);
242 * @name IntGetLoadedCodePageEntry
244 * Internal function to get structure containing a code page information
245 * of code page that is already loaded.
248 * Number of the code page. Special values like CP_OEMCP, CP_ACP
249 * or CP_UTF8 aren't allowed.
251 * @return Code page entry or NULL if the specified code page hasn't
257 IntGetLoadedCodePageEntry(UINT CodePage
)
259 LIST_ENTRY
*CurrentEntry
;
260 PCODEPAGE_ENTRY Current
;
262 RtlEnterCriticalSection(&CodePageListLock
);
263 for (CurrentEntry
= CodePageListHead
.Flink
;
264 CurrentEntry
!= &CodePageListHead
;
265 CurrentEntry
= CurrentEntry
->Flink
)
267 Current
= CONTAINING_RECORD(CurrentEntry
, CODEPAGE_ENTRY
, Entry
);
268 if (Current
->CodePage
== CodePage
)
270 RtlLeaveCriticalSection(&CodePageListLock
);
274 RtlLeaveCriticalSection(&CodePageListLock
);
280 * @name IntGetCodePageEntry
282 * Internal function to get structure containing a code page information.
285 * Number of the code page. Special values like CP_OEMCP, CP_ACP
286 * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
288 * @return Code page entry.
293 IntGetCodePageEntry(UINT CodePage
)
296 CHAR SectionName
[40];
297 HANDLE SectionHandle
= INVALID_HANDLE_VALUE
, FileHandle
;
298 PBYTE SectionMapping
;
299 OBJECT_ATTRIBUTES ObjectAttributes
;
300 UCHAR SecurityDescriptor
[NLS_SECTION_SECURITY_DESCRIPTOR_SIZE
];
301 ANSI_STRING AnsiName
;
302 UNICODE_STRING UnicodeName
;
303 WCHAR FileName
[MAX_PATH
+ 1];
305 PCODEPAGE_ENTRY CodePageEntry
;
307 if (CodePage
== CP_ACP
)
309 return &AnsiCodePage
;
311 else if (CodePage
== CP_OEMCP
)
315 else if (CodePage
== CP_THREAD_ACP
)
317 if (!GetLocaleInfoW(GetThreadLocale(),
318 LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
320 sizeof(CodePage
) / sizeof(WCHAR
)))
322 /* Last error is set by GetLocaleInfoW. */
326 return &AnsiCodePage
;
328 else if (CodePage
== CP_MACCP
)
330 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT
,
331 LOCALE_IDEFAULTMACCODEPAGE
| LOCALE_RETURN_NUMBER
,
333 sizeof(CodePage
) / sizeof(WCHAR
)))
335 /* Last error is set by GetLocaleInfoW. */
340 /* Try searching for loaded page first. */
341 CodePageEntry
= IntGetLoadedCodePageEntry(CodePage
);
342 if (CodePageEntry
!= NULL
)
344 return CodePageEntry
;
348 * Yes, we really want to lock here. Otherwise it can happen that
349 * two parallel requests will try to get the entry for the same
350 * code page and we would load it twice.
352 RtlEnterCriticalSection(&CodePageListLock
);
354 /* Generate the section name. */
355 if (!GetNlsSectionName(CodePage
,
358 "\\Nls\\NlsSectionCP",
360 sizeof(SectionName
)))
362 RtlLeaveCriticalSection(&CodePageListLock
);
366 RtlInitAnsiString(&AnsiName
, SectionName
);
367 RtlAnsiStringToUnicodeString(&UnicodeName
, &AnsiName
, TRUE
);
370 * FIXME: IntGetCodePageEntry should not create any security
371 * descriptor here but instead this responsibility should be
372 * assigned to Base Server API (aka basesrv.dll). That is,
373 * kernel32 must instruct basesrv.dll on creating NLS section
374 * names that do not exist through API message communication.
375 * However since we do not do that, let the kernel32 do the job
376 * by assigning security to NLS section names for the time being...
378 Status
= CreateNlsSecurityDescriptor(&SecurityDescriptor
,
379 sizeof(SecurityDescriptor
),
381 if (!NT_SUCCESS(Status
))
383 DPRINT1("CreateNlsSecurityDescriptor FAILED! (Status 0x%08x)\n", Status
);
384 RtlLeaveCriticalSection(&CodePageListLock
);
388 InitializeObjectAttributes(&ObjectAttributes
,
390 OBJ_CASE_INSENSITIVE
,
394 /* Try to open the section first */
395 Status
= NtOpenSection(&SectionHandle
,
399 /* If the section doesn't exist, try to create it. */
400 if (Status
== STATUS_UNSUCCESSFUL
||
401 Status
== STATUS_OBJECT_NAME_NOT_FOUND
||
402 Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
404 FileNamePos
= GetSystemDirectoryW(FileName
, MAX_PATH
);
405 if (GetCPFileNameFromRegistry(CodePage
,
406 FileName
+ FileNamePos
+ 1,
407 MAX_PATH
- FileNamePos
- 1))
409 FileName
[FileNamePos
] = L
'\\';
410 FileName
[MAX_PATH
] = 0;
411 FileHandle
= CreateFileW(FileName
,
419 Status
= NtCreateSection(&SectionHandle
,
427 /* HACK: Check if another process was faster
428 * and already created this section. See bug 3626 for details */
429 if (Status
== STATUS_OBJECT_NAME_COLLISION
)
431 /* Close the file then */
434 /* And open the section */
435 Status
= NtOpenSection(&SectionHandle
,
441 RtlFreeUnicodeString(&UnicodeName
);
443 if (!NT_SUCCESS(Status
))
445 RtlLeaveCriticalSection(&CodePageListLock
);
449 SectionMapping
= MapViewOfFile(SectionHandle
, FILE_MAP_READ
, 0, 0, 0);
450 if (SectionMapping
== NULL
)
452 NtClose(SectionHandle
);
453 RtlLeaveCriticalSection(&CodePageListLock
);
457 CodePageEntry
= HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY
));
458 if (CodePageEntry
== NULL
)
460 NtClose(SectionHandle
);
461 RtlLeaveCriticalSection(&CodePageListLock
);
465 CodePageEntry
->CodePage
= CodePage
;
466 CodePageEntry
->SectionHandle
= SectionHandle
;
467 CodePageEntry
->SectionMapping
= SectionMapping
;
469 RtlInitCodePageTable((PUSHORT
)SectionMapping
, &CodePageEntry
->CodePageTable
);
471 /* Insert the new entry to list and unlock. Uff. */
472 InsertTailList(&CodePageListHead
, &CodePageEntry
->Entry
);
473 RtlLeaveCriticalSection(&CodePageListLock
);
475 return CodePageEntry
;
479 * @name IntMultiByteToWideCharUTF8
481 * Internal version of MultiByteToWideChar for UTF8.
483 * @note We use Win10's behaviour due to security reason.
485 * @see MultiByteToWideChar
490 IntMultiByteToWideCharUTF8(DWORD Flags
,
491 LPCSTR MultiByteString
,
493 LPWSTR WideCharString
,
496 LPCSTR MbsEnd
, MbsPtrSave
;
497 UCHAR Char
, TrailLength
;
500 BOOL CharIsValid
, StringIsValid
= TRUE
;
501 const WCHAR InvalidChar
= 0xFFFD;
503 if (Flags
!= 0 && Flags
!= MB_ERR_INVALID_CHARS
)
505 SetLastError(ERROR_INVALID_FLAGS
);
509 /* Does caller query for output buffer size? */
510 if (WideCharCount
== 0)
512 /* validate and count the wide characters */
513 MbsEnd
= MultiByteString
+ MultiByteCount
;
514 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
516 Char
= *MultiByteString
++;
522 if ((Char
& 0xC0) == 0x80)
525 StringIsValid
= FALSE
;
529 TrailLength
= UTF8Length
[Char
- 0x80];
530 if (TrailLength
== 0)
532 StringIsValid
= FALSE
;
537 MbsPtrSave
= MultiByteString
;
538 WideChar
= Char
& UTF8Mask
[TrailLength
];
540 while (TrailLength
&& MultiByteString
< MbsEnd
)
542 if ((*MultiByteString
& 0xC0) != 0x80)
544 CharIsValid
= StringIsValid
= FALSE
;
548 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
552 if (!CharIsValid
|| WideChar
< UTF8LBound
[UTF8Length
[Char
- 0x80]])
554 MultiByteString
= MbsPtrSave
;
561 StringIsValid
= FALSE
;
564 if (Flags
== MB_ERR_INVALID_CHARS
&& !StringIsValid
)
566 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
570 return WideCharCount
;
574 MbsEnd
= MultiByteString
+ MultiByteCount
;
575 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
577 Char
= *MultiByteString
++;
580 *WideCharString
++ = Char
;
584 if ((Char
& 0xC0) == 0x80)
586 *WideCharString
++ = InvalidChar
;
588 StringIsValid
= FALSE
;
592 TrailLength
= UTF8Length
[Char
- 0x80];
593 if (TrailLength
== 0)
595 *WideCharString
++ = InvalidChar
;
596 StringIsValid
= FALSE
;
601 MbsPtrSave
= MultiByteString
;
602 WideChar
= Char
& UTF8Mask
[TrailLength
];
604 while (TrailLength
&& MultiByteString
< MbsEnd
)
606 if ((*MultiByteString
& 0xC0) != 0x80)
608 CharIsValid
= StringIsValid
= FALSE
;
612 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
616 if (CharIsValid
&& UTF8LBound
[UTF8Length
[Char
- 0x80]] <= WideChar
)
618 *WideCharString
++ = WideChar
;
622 *WideCharString
++ = InvalidChar
;
623 MultiByteString
= MbsPtrSave
;
624 StringIsValid
= FALSE
;
628 if (TrailLength
&& Count
< WideCharCount
&& MultiByteString
< MbsEnd
)
630 *WideCharString
= InvalidChar
;
634 if (MultiByteString
< MbsEnd
)
636 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
640 if (Flags
== MB_ERR_INVALID_CHARS
&& (!StringIsValid
|| TrailLength
))
642 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
650 * @name IntMultiByteToWideCharCP
652 * Internal version of MultiByteToWideChar for code page tables.
654 * @see MultiByteToWideChar
655 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
662 IntMultiByteToWideCharCP(UINT CodePage
,
664 LPCSTR MultiByteString
,
666 LPWSTR WideCharString
,
669 PCODEPAGE_ENTRY CodePageEntry
;
670 PCPTABLEINFO CodePageTable
;
671 PUSHORT MultiByteTable
;
676 /* Get code page table. */
677 CodePageEntry
= IntGetCodePageEntry(CodePage
);
678 if (CodePageEntry
== NULL
)
680 SetLastError(ERROR_INVALID_PARAMETER
);
684 CodePageTable
= &CodePageEntry
->CodePageTable
;
686 /* If MB_USEGLYPHCHARS flag present and glyph table present */
687 if ((Flags
& MB_USEGLYPHCHARS
) && CodePageTable
->MultiByteTable
[256])
689 /* Use glyph table */
690 MultiByteTable
= CodePageTable
->MultiByteTable
+ 256 + 1;
694 MultiByteTable
= CodePageTable
->MultiByteTable
;
697 /* Different handling for DBCS code pages. */
698 if (CodePageTable
->DBCSCodePage
)
702 LPCSTR MbsEnd
= MultiByteString
+ MultiByteCount
;
705 if (Flags
& MB_ERR_INVALID_CHARS
)
707 TempString
= MultiByteString
;
709 while (TempString
< MbsEnd
)
711 DBCSOffset
= CodePageTable
->DBCSOffsets
[(UCHAR
)*TempString
];
715 /* If lead byte is presented, but behind it there is no symbol */
716 if (((TempString
+ 1) == MbsEnd
) || (*(TempString
+ 1) == 0))
718 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
722 WideChar
= CodePageTable
->DBCSOffsets
[DBCSOffset
+ *(TempString
+ 1)];
724 if (WideChar
== CodePageTable
->UniDefaultChar
&&
725 MAKEWORD(*(TempString
+ 1), *TempString
) != CodePageTable
->TransUniDefaultChar
)
727 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
735 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
737 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
738 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
739 /* "Private Use" characters */
740 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
742 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
751 /* Does caller query for output buffer size? */
752 if (WideCharCount
== 0)
754 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
756 Char
= *MultiByteString
++;
758 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
763 if (MultiByteString
< MbsEnd
)
767 return WideCharCount
;
770 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
772 Char
= *MultiByteString
++;
774 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
778 *WideCharString
++ = MultiByteTable
[Char
];
782 if (MultiByteString
== MbsEnd
|| *MultiByteString
== 0)
784 *WideCharString
++ = CodePageTable
->UniDefaultChar
;
788 *WideCharString
++ = CodePageTable
->DBCSOffsets
[DBCSOffset
+ (UCHAR
)*MultiByteString
++];
792 if (MultiByteString
< MbsEnd
)
794 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
800 else /* SBCS code page */
802 /* Check for invalid characters. */
803 if (Flags
& MB_ERR_INVALID_CHARS
)
805 for (TempString
= MultiByteString
, TempLength
= MultiByteCount
;
807 TempString
++, TempLength
--)
809 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
811 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
812 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
813 /* "Private Use" characters */
814 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
816 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
822 /* Does caller query for output buffer size? */
823 if (WideCharCount
== 0)
824 return MultiByteCount
;
826 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
827 for (TempLength
= (WideCharCount
< MultiByteCount
) ? WideCharCount
: MultiByteCount
;
829 MultiByteString
++, TempLength
--)
831 *WideCharString
++ = MultiByteTable
[(UCHAR
)*MultiByteString
];
834 /* Adjust buffer size. Wine trick ;-) */
835 if (WideCharCount
< MultiByteCount
)
837 MultiByteCount
= WideCharCount
;
838 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
841 return MultiByteCount
;
846 * @name IntMultiByteToWideCharSYMBOL
848 * Internal version of MultiByteToWideChar for SYMBOL.
850 * @see MultiByteToWideChar
856 IntMultiByteToWideCharSYMBOL(DWORD Flags
,
857 LPCSTR MultiByteString
,
859 LPWSTR WideCharString
,
869 SetLastError(ERROR_INVALID_FLAGS
);
873 if (WideCharCount
== 0)
875 return MultiByteCount
;
878 WideCharMaxLen
= WideCharCount
> MultiByteCount
? MultiByteCount
: WideCharCount
;
880 for (Count
= 0; Count
< WideCharMaxLen
; Count
++)
882 Char
= MultiByteString
[Count
];
885 WideCharString
[Count
] = Char
;
889 WideCharString
[Count
] = Char
+ 0xf000;
892 if (MultiByteCount
> WideCharMaxLen
)
894 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
898 return WideCharMaxLen
;
902 * @name IntWideCharToMultiByteSYMBOL
904 * Internal version of WideCharToMultiByte for SYMBOL.
906 * @see WideCharToMultiByte
911 IntWideCharToMultiByteSYMBOL(DWORD Flags
,
912 LPCWSTR WideCharString
,
914 LPSTR MultiByteString
,
923 SetLastError(ERROR_INVALID_PARAMETER
);
928 if (MultiByteCount
== 0)
930 return WideCharCount
;
933 MaxLen
= MultiByteCount
> WideCharCount
? WideCharCount
: MultiByteCount
;
934 for (Count
= 0; Count
< MaxLen
; Count
++)
936 Char
= WideCharString
[Count
];
939 MultiByteString
[Count
] = (CHAR
)Char
;
943 if ((Char
>= 0xf020) && (Char
< 0xf100))
945 MultiByteString
[Count
] = Char
- 0xf000;
949 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
955 if (WideCharCount
> MaxLen
)
957 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
964 * @name IntWideCharToMultiByteUTF8
966 * Internal version of WideCharToMultiByte for UTF8.
968 * @see WideCharToMultiByte
973 IntWideCharToMultiByteUTF8(UINT CodePage
,
975 LPCWSTR WideCharString
,
977 LPSTR MultiByteString
,
980 LPBOOL UsedDefaultChar
)
987 SetLastError(ERROR_INVALID_FLAGS
);
991 /* Does caller query for output buffer size? */
992 if (MultiByteCount
== 0)
994 for (TempLength
= 0; WideCharCount
;
995 WideCharCount
--, WideCharString
++)
998 if (*WideCharString
>= 0x80)
1001 if (*WideCharString
>= 0x800)
1004 if (*WideCharString
>= 0xd800 && *WideCharString
< 0xdc00 &&
1005 WideCharCount
>= 1 &&
1006 WideCharString
[1] >= 0xdc00 && WideCharString
[1] <= 0xe000)
1018 for (TempLength
= MultiByteCount
; WideCharCount
; WideCharCount
--, WideCharString
++)
1020 Char
= *WideCharString
;
1025 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1029 *MultiByteString
++ = (CHAR
)Char
;
1033 if (Char
< 0x800) /* 0x80-0x7ff: 2 bytes */
1037 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1040 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1041 MultiByteString
[0] = 0xc0 | Char
;
1042 MultiByteString
+= 2;
1047 /* surrogate pair 0x10000-0x10ffff: 4 bytes */
1048 if (Char
>= 0xd800 && Char
< 0xdc00 &&
1049 WideCharCount
>= 1 &&
1050 WideCharString
[1] >= 0xdc00 && WideCharString
[1] < 0xe000)
1057 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1061 Char
= (Char
- 0xd800) << 10;
1062 Char
|= *WideCharString
- 0xdc00;
1063 ASSERT(Char
<= 0xfffff);
1065 ASSERT(Char
<= 0x10ffff);
1067 MultiByteString
[3] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1068 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1069 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1070 MultiByteString
[0] = 0xf0 | Char
;
1071 MultiByteString
+= 4;
1076 /* 0x800-0xffff: 3 bytes */
1079 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1082 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1083 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
1084 MultiByteString
[0] = 0xe0 | Char
;
1085 MultiByteString
+= 3;
1089 return MultiByteCount
- TempLength
;
1093 * @name IsValidSBCSMapping
1095 * Checks if ch (single-byte character) is a valid mapping for wch
1097 * @see IntWideCharToMultiByteCP
1102 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, UCHAR ch
)
1104 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
1105 if (Flags
& WC_NO_BEST_FIT_CHARS
)
1106 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
1108 /* By default, all characters except TransDefaultChar apply as a valid mapping
1109 for ch (so also "nearest" characters) */
1110 if (ch
!= CodePageTable
->TransDefaultChar
)
1113 /* The only possible left valid mapping is the default character itself */
1114 return (wch
== CodePageTable
->TransUniDefaultChar
);
1118 * @name IsValidDBCSMapping
1120 * Checks if ch (double-byte character) is a valid mapping for wch
1122 * @see IntWideCharToMultiByteCP
1125 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, USHORT ch
)
1127 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
1128 if (ch
== CodePageTable
->TransDefaultChar
&& wch
!= CodePageTable
->TransUniDefaultChar
)
1131 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
1132 if (Flags
& WC_NO_BEST_FIT_CHARS
)
1136 USHORT uOffset
= CodePageTable
->DBCSOffsets
[ch
>> 8];
1137 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1138 return (CodePageTable
->DBCSOffsets
[uOffset
+ (ch
& 0xff)] == wch
);
1141 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
1144 /* If we're still here, we have a valid mapping */
1149 * @name IntWideCharToMultiByteCP
1151 * Internal version of WideCharToMultiByte for code page tables.
1153 * @see WideCharToMultiByte
1154 * @todo Handle WC_COMPOSITECHECK
1159 IntWideCharToMultiByteCP(UINT CodePage
,
1161 LPCWSTR WideCharString
,
1163 LPSTR MultiByteString
,
1166 LPBOOL UsedDefaultChar
)
1168 PCODEPAGE_ENTRY CodePageEntry
;
1169 PCPTABLEINFO CodePageTable
;
1172 /* Get code page table. */
1173 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1174 if (CodePageEntry
== NULL
)
1176 SetLastError(ERROR_INVALID_PARAMETER
);
1180 CodePageTable
= &CodePageEntry
->CodePageTable
;
1183 /* Different handling for DBCS code pages. */
1184 if (CodePageTable
->DBCSCodePage
)
1186 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1187 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1189 BOOL TempUsedDefaultChar
;
1192 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1193 to check on every character */
1194 if (!UsedDefaultChar
)
1195 UsedDefaultChar
= &TempUsedDefaultChar
;
1197 *UsedDefaultChar
= FALSE
;
1199 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1201 DefChar
= DefaultChar
[1] ? ((DefaultChar
[0] << 8) | DefaultChar
[1]) : DefaultChar
[0];
1203 DefChar
= CodePageTable
->TransDefaultChar
;
1205 /* Does caller query for output buffer size? */
1206 if (!MultiByteCount
)
1208 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1212 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1214 /* FIXME: Handle WC_COMPOSITECHECK */
1215 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1218 uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1220 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1221 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1224 *UsedDefaultChar
= TRUE
;
1227 /* Increment TempLength again if this is a double-byte character */
1235 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1236 for (TempLength
= MultiByteCount
;
1237 WideCharCount
&& TempLength
;
1238 TempLength
--, WideCharString
++, WideCharCount
--)
1242 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1244 /* FIXME: Handle WC_COMPOSITECHECK */
1245 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1248 uChar
= ((PUSHORT
)CodePageTable
->WideCharTable
)[*WideCharString
];
1250 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1251 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1254 *UsedDefaultChar
= TRUE
;
1257 /* Handle double-byte characters */
1260 /* Don't output a partial character */
1261 if (TempLength
== 1)
1265 *MultiByteString
++ = uChar
>> 8;
1268 *MultiByteString
++ = (char)uChar
;
1271 /* WideCharCount should be 0 if all characters were converted */
1274 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1278 return MultiByteCount
- TempLength
;
1281 /* Does caller query for output buffer size? */
1282 if (!MultiByteCount
)
1284 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1286 /* Increment TempLength again if this is a double-byte character */
1287 if (((PWCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
] & 0xff00)
1294 /* Convert the WideCharString to the MultiByteString */
1295 for (TempLength
= MultiByteCount
;
1296 WideCharCount
&& TempLength
;
1297 TempLength
--, WideCharString
++, WideCharCount
--)
1299 USHORT uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1301 /* Is this a double-byte character? */
1304 /* Don't output a partial character */
1305 if (TempLength
== 1)
1309 *MultiByteString
++ = uChar
>> 8;
1312 *MultiByteString
++ = (char)uChar
;
1315 /* WideCharCount should be 0 if all characters were converted */
1318 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1322 return MultiByteCount
- TempLength
;
1324 else /* SBCS code page */
1328 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1329 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1331 BOOL TempUsedDefaultChar
;
1334 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1335 to check on every character */
1336 if (!UsedDefaultChar
)
1337 UsedDefaultChar
= &TempUsedDefaultChar
;
1339 *UsedDefaultChar
= FALSE
;
1341 /* Does caller query for output buffer size? */
1342 if (!MultiByteCount
)
1344 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1345 for (TempLength
= 0; WideCharCount
; TempLength
++, WideCharString
++, WideCharCount
--)
1347 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1349 /* FIXME: Handle WC_COMPOSITECHECK */
1350 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1353 if (!*UsedDefaultChar
)
1354 *UsedDefaultChar
= !IntIsValidSBCSMapping(CodePageTable
,
1357 ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
]);
1363 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1365 DefChar
= *DefaultChar
;
1367 DefChar
= (CHAR
)CodePageTable
->TransDefaultChar
;
1369 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1370 for (TempLength
= MultiByteCount
;
1371 WideCharCount
&& TempLength
;
1372 MultiByteString
++, TempLength
--, WideCharString
++, WideCharCount
--)
1374 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1376 /* FIXME: Handle WC_COMPOSITECHECK */
1377 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1380 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1382 if (!IntIsValidSBCSMapping(CodePageTable
, Flags
, *WideCharString
, *MultiByteString
))
1384 *MultiByteString
= DefChar
;
1385 *UsedDefaultChar
= TRUE
;
1389 /* WideCharCount should be 0 if all characters were converted */
1392 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1396 return MultiByteCount
- TempLength
;
1399 /* Does caller query for output buffer size? */
1400 if (!MultiByteCount
)
1401 return WideCharCount
;
1403 /* Is the buffer large enough? */
1404 if (MultiByteCount
< WideCharCount
)
1406 /* Convert the string up to MultiByteCount and return 0 */
1407 WideCharCount
= MultiByteCount
;
1408 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1413 /* Otherwise WideCharCount will be the number of converted characters */
1414 nReturn
= WideCharCount
;
1417 /* Convert the WideCharString to the MultiByteString */
1418 for (TempLength
= WideCharCount
; --TempLength
>= 0; WideCharString
++, MultiByteString
++)
1420 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1428 * @name IntIsLeadByte
1430 * Internal function to detect if byte is lead byte in specific character
1436 IntIsLeadByte(PCPTABLEINFO TableInfo
, BYTE Byte
)
1440 if (TableInfo
->MaximumCharacterSize
== 2)
1442 for (i
= 0; i
< MAXIMUM_LEADBYTES
&& TableInfo
->LeadByte
[i
]; i
+= 2)
1444 if (Byte
>= TableInfo
->LeadByte
[i
] && Byte
<= TableInfo
->LeadByte
[i
+1])
1452 /* PUBLIC FUNCTIONS ***********************************************************/
1455 * @name GetNlsSectionName
1457 * Construct a name of NLS section.
1462 * Integer base used for converting to string. Usually set to 10.
1464 * As the name suggests the meaning of this parameter is unknown.
1465 * The native version of Kernel32 passes it as the third parameter
1466 * to NlsConvertIntegerToString function, which is used for the
1467 * actual conversion of the code page number.
1469 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1471 * Buffer that will hold the constructed name.
1473 * Size of the buffer for the result.
1475 * @return TRUE if the buffer was large enough and was filled with
1476 * the requested information, FALSE otherwise.
1483 GetNlsSectionName(UINT CodePage
,
1492 if (!NT_SUCCESS(RtlIntegerToChar(CodePage
, Base
, sizeof(Integer
), Integer
)))
1496 * If the name including the terminating NULL character doesn't
1497 * fit in the output buffer then fail.
1499 if (strlen(Integer
) + strlen(BaseName
) >= ResultSize
)
1502 lstrcpyA(Result
, BaseName
);
1503 lstrcatA(Result
, Integer
);
1509 * @name GetCPFileNameFromRegistry
1511 * Get file name of code page definition file.
1514 * Code page number to get file name of.
1516 * Buffer that is filled with file name of successful return. Can
1518 * @param FileNameSize
1519 * Size of the buffer to hold file name in WCHARs.
1521 * @return TRUE if the file name was retrieved, FALSE otherwise.
1528 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
)
1530 WCHAR ValueNameBuffer
[11];
1531 UNICODE_STRING KeyName
, ValueName
;
1532 OBJECT_ATTRIBUTES ObjectAttributes
;
1535 PKEY_VALUE_PARTIAL_INFORMATION Kvpi
;
1541 /* Convert the codepage number to string. */
1542 ValueName
.Buffer
= ValueNameBuffer
;
1543 ValueName
.MaximumLength
= sizeof(ValueNameBuffer
);
1545 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage
, 10, &ValueName
)))
1548 /* Open the registry key containing file name mappings. */
1549 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System\\"
1550 L
"CurrentControlSet\\Control\\Nls\\CodePage");
1551 InitializeObjectAttributes(&ObjectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
,
1553 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1554 if (!NT_SUCCESS(Status
))
1559 /* Allocate buffer that will be used to query the value data. */
1560 KvpiSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + (MAX_PATH
* sizeof(WCHAR
));
1561 Kvpi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KvpiSize
);
1568 /* Query the file name for our code page. */
1569 Status
= NtQueryValueKey(KeyHandle
, &ValueName
, KeyValuePartialInformation
,
1570 Kvpi
, KvpiSize
, &KvpiSize
);
1574 /* Check if we succeded and the value is non-empty string. */
1575 if (NT_SUCCESS(Status
) && Kvpi
->Type
== REG_SZ
&&
1576 Kvpi
->DataLength
> sizeof(WCHAR
))
1579 if (FileName
!= NULL
)
1581 lstrcpynW(FileName
, (WCHAR
*)Kvpi
->Data
,
1582 min(Kvpi
->DataLength
/ sizeof(WCHAR
), FileNameSize
));
1586 /* free temporary buffer */
1587 HeapFree(GetProcessHeap(),0,Kvpi
);
1592 * @name IsValidCodePage
1594 * Detect if specified code page is valid and present in the system.
1597 * Code page number to query.
1599 * @return TRUE if code page is present.
1604 IsValidCodePage(UINT CodePage
)
1606 if (CodePage
== 0) return FALSE
;
1607 if (CodePage
== CP_UTF8
|| CodePage
== CP_UTF7
)
1609 if (IntGetLoadedCodePageEntry(CodePage
))
1611 return GetCPFileNameFromRegistry(CodePage
, NULL
, 0);
1614 static inline BOOL
utf7_write_w(WCHAR
*dst
, int dstlen
, int *index
, WCHAR character
)
1618 if (*index
>= dstlen
)
1621 dst
[*index
] = character
;
1629 static INT
Utf7ToWideChar(const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
1631 static const signed char base64_decoding_table
[] =
1633 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1634 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1635 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1636 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1637 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1638 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1639 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1640 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1643 const char *source_end
= src
+ srclen
;
1646 DWORD byte_pair
= 0;
1649 while (src
< source_end
)
1654 if (src
>= source_end
)
1659 /* just a plus sign escaped as +- */
1660 if (!utf7_write_w(dst
, dstlen
, &dest_index
, '+'))
1662 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1671 signed char sextet
= *src
;
1674 /* skip over the dash and end base64 decoding
1675 * the current, unfinished byte pair is discarded */
1682 /* the next character of src is < 0 and therefore not part of a base64 sequence
1683 * the current, unfinished byte pair is NOT discarded in this case
1684 * this is probably a bug in Windows */
1688 sextet
= base64_decoding_table
[sextet
];
1691 /* -1 means that the next character of src is not part of a base64 sequence
1692 * in other words, all sextets in this base64 sequence have been processed
1693 * the current, unfinished byte pair is discarded */
1698 byte_pair
= (byte_pair
<< 6) | sextet
;
1703 /* this byte pair is done */
1704 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (byte_pair
>> (offset
- 16)) & 0xFFFF))
1706 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1714 while (src
< source_end
);
1718 /* we have to convert to unsigned char in case *src < 0 */
1719 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (unsigned char)*src
))
1721 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1732 * @name MultiByteToWideChar
1734 * Convert a multi-byte string to wide-charater equivalent.
1737 * Code page to be used to perform the conversion. It can be also
1738 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1739 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1740 * for thread active code page, CP_UTF7 or CP_UTF8).
1742 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1743 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1744 * @param MultiByteString
1746 * @param MultiByteCount
1747 * Size of MultiByteString, or -1 if MultiByteString is NULL
1749 * @param WideCharString
1751 * @param WideCharCount
1752 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1753 * to know how large WideCharString should be for a successful
1756 * @return Zero on error, otherwise the number of WCHARs written
1757 * in the WideCharString buffer.
1763 MultiByteToWideChar(UINT CodePage
,
1765 LPCSTR MultiByteString
,
1767 LPWSTR WideCharString
,
1770 /* Check the parameters. */
1771 if (MultiByteString
== NULL
||
1772 MultiByteCount
== 0 || WideCharCount
< 0 ||
1773 (WideCharCount
&& (WideCharString
== NULL
||
1774 (PVOID
)MultiByteString
== (PVOID
)WideCharString
)))
1776 SetLastError(ERROR_INVALID_PARAMETER
);
1780 /* Determine the input string length. */
1781 if (MultiByteCount
< 0)
1783 MultiByteCount
= lstrlenA(MultiByteString
) + 1;
1789 return IntMultiByteToWideCharUTF8(Flags
,
1798 SetLastError(ERROR_INVALID_FLAGS
);
1801 return Utf7ToWideChar(MultiByteString
, MultiByteCount
,
1802 WideCharString
, WideCharCount
);
1805 return IntMultiByteToWideCharSYMBOL(Flags
,
1811 return IntMultiByteToWideCharCP(CodePage
,
1820 static inline BOOL
utf7_can_directly_encode(WCHAR codepoint
)
1822 static const BOOL directly_encodable_table
[] =
1824 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1825 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1826 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1827 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1828 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1829 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1830 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1831 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
1834 return codepoint
<= 0x7A ? directly_encodable_table
[codepoint
] : FALSE
;
1837 static inline BOOL
utf7_write_c(char *dst
, int dstlen
, int *index
, char character
)
1841 if (*index
>= dstlen
)
1844 dst
[*index
] = character
;
1852 static INT
WideCharToUtf7(const WCHAR
*src
, int srclen
, char *dst
, int dstlen
)
1854 static const char base64_encoding_table
[] =
1855 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1857 const WCHAR
*source_end
= src
+ srclen
;
1860 while (src
< source_end
)
1864 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1866 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1869 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1871 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1876 else if (utf7_can_directly_encode(*src
))
1878 if (!utf7_write_c(dst
, dstlen
, &dest_index
, *src
))
1880 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1887 unsigned int offset
= 0;
1888 DWORD byte_pair
= 0;
1890 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1892 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1896 while (src
< source_end
&& !utf7_can_directly_encode(*src
))
1898 byte_pair
= (byte_pair
<< 16) | *src
;
1902 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[(byte_pair
>> (offset
- 6)) & 0x3F]))
1904 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1914 /* Windows won't create a padded base64 character if there's no room for the - sign
1915 * as well ; this is probably a bug in Windows */
1916 if (dstlen
> 0 && dest_index
+ 1 >= dstlen
)
1918 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1922 byte_pair
<<= (6 - offset
);
1923 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[byte_pair
& 0x3F]))
1925 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1930 /* Windows always explicitly terminates the base64 sequence
1931 even though RFC 2152 (page 3, rule 2) does not require this */
1932 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1934 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1944 * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW
1945 * and GetGeoInfoW. It uses the current user localization, otherwise falls back
1946 * to English (US). Contrary to LoadStringW which always saves the loaded string
1947 * into the user-given buffer, truncating the string if needed, this function
1948 * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer
1949 * is not large enough.
1965 /* See HACK in winnls/lang/xx-XX.rc files */
1969 lcid
= ConvertDefaultLocale(lang
);
1971 langId
= LANGIDFROMLCID(lcid
);
1973 if (PRIMARYLANGID(langId
) == LANG_NEUTRAL
)
1974 langId
= MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
);
1976 hrsrc
= FindResourceExW(hCurrentModule
,
1978 MAKEINTRESOURCEW((uID
>> 4) + 1),
1981 /* English fallback */
1984 hrsrc
= FindResourceExW(hCurrentModule
,
1986 MAKEINTRESOURCEW((uID
>> 4) + 1),
1987 MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
));
1993 hmem
= LoadResource(hCurrentModule
, hrsrc
);
1997 p
= LockResource(hmem
);
1999 for (i
= 0; i
< (uID
& 0x0F); i
++)
2002 /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
2005 /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
2006 if (*p
+ 1 > cchDest
)
2008 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2015 memcpy(lpszDest
, p
+ 1, i
* sizeof(WCHAR
));
2016 lpszDest
[i
] = L
'\0';
2023 lpszDest
[0] = L
'\0';
2029 DPRINT1("Resource not found: uID = %lu\n", uID
);
2030 SetLastError(ERROR_INVALID_PARAMETER
);
2039 GetCPInfo(UINT CodePage
,
2040 LPCPINFO CodePageInfo
)
2042 PCODEPAGE_ENTRY CodePageEntry
;
2046 SetLastError(ERROR_INVALID_PARAMETER
);
2050 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2051 if (CodePageEntry
== NULL
)
2057 CodePageInfo
->DefaultChar
[0] = 0x3f;
2058 CodePageInfo
->DefaultChar
[1] = 0;
2059 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
2060 CodePageInfo
->MaxCharSize
= (CodePage
== CP_UTF7
) ? 5 : 4;
2064 DPRINT1("Invalid CP!: %lx\n", CodePage
);
2065 SetLastError( ERROR_INVALID_PARAMETER
);
2069 if (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00)
2071 CodePageInfo
->DefaultChar
[0] = (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00) >> 8;
2072 CodePageInfo
->DefaultChar
[1] = CodePageEntry
->CodePageTable
.DefaultChar
& 0x00ff;
2076 CodePageInfo
->DefaultChar
[0] = CodePageEntry
->CodePageTable
.DefaultChar
& 0xff;
2077 CodePageInfo
->DefaultChar
[1] = 0;
2080 if ((CodePageInfo
->MaxCharSize
= CodePageEntry
->CodePageTable
.MaximumCharacterSize
) == 2)
2081 memcpy(CodePageInfo
->LeadByte
, CodePageEntry
->CodePageTable
.LeadByte
, sizeof(CodePageInfo
->LeadByte
));
2083 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
2093 GetCPInfoExW(UINT CodePage
,
2095 LPCPINFOEXW lpCPInfoEx
)
2097 if (!GetCPInfo(CodePage
, (LPCPINFO
)lpCPInfoEx
))
2104 lpCPInfoEx
->CodePage
= CP_UTF7
;
2105 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
2106 return GetLocalisedText(lpCPInfoEx
->CodePage
,
2107 lpCPInfoEx
->CodePageName
,
2108 ARRAYSIZE(lpCPInfoEx
->CodePageName
),
2109 GetThreadLocale()) != 0;
2115 lpCPInfoEx
->CodePage
= CP_UTF8
;
2116 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
2117 return GetLocalisedText(lpCPInfoEx
->CodePage
,
2118 lpCPInfoEx
->CodePageName
,
2119 ARRAYSIZE(lpCPInfoEx
->CodePageName
),
2120 GetThreadLocale()) != 0;
2125 PCODEPAGE_ENTRY CodePageEntry
;
2127 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2128 if (CodePageEntry
== NULL
)
2130 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
2131 SetLastError(ERROR_INVALID_PARAMETER
);
2135 lpCPInfoEx
->CodePage
= CodePageEntry
->CodePageTable
.CodePage
;
2136 lpCPInfoEx
->UnicodeDefaultChar
= CodePageEntry
->CodePageTable
.UniDefaultChar
;
2137 return GetLocalisedText(lpCPInfoEx
->CodePage
,
2138 lpCPInfoEx
->CodePageName
,
2139 ARRAYSIZE(lpCPInfoEx
->CodePageName
),
2140 GetThreadLocale()) != 0;
2152 GetCPInfoExA(UINT CodePage
,
2154 LPCPINFOEXA lpCPInfoEx
)
2158 if (!GetCPInfoExW(CodePage
, dwFlags
, &CPInfo
))
2161 /* the layout is the same except for CodePageName */
2162 memcpy(lpCPInfoEx
, &CPInfo
, sizeof(CPINFOEXA
));
2164 WideCharToMultiByte(CP_ACP
,
2166 CPInfo
.CodePageName
,
2168 lpCPInfoEx
->CodePageName
,
2169 sizeof(lpCPInfoEx
->CodePageName
),
2176 * @name WideCharToMultiByte
2178 * Convert a wide-charater string to closest multi-byte equivalent.
2181 * Code page to be used to perform the conversion. It can be also
2182 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
2183 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
2184 * for thread active code page, CP_UTF7 or CP_UTF8).
2186 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
2187 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
2188 * @param WideCharString
2189 * Points to the wide-character string to be converted.
2190 * @param WideCharCount
2191 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
2192 * know how large WideCharString should be for a successful conversion.
2193 * @param MultiByteString
2194 * Points to the buffer to receive the translated string.
2195 * @param MultiByteCount
2196 * Specifies the size in bytes of the buffer pointed to by the
2197 * MultiByteString parameter. If this value is zero, the function
2198 * returns the number of bytes required for the buffer.
2199 * @param DefaultChar
2200 * Points to the character used if a wide character cannot be
2201 * represented in the specified code page. If this parameter is
2202 * NULL, a system default value is used.
2203 * @param UsedDefaultChar
2204 * Points to a flag that indicates whether a default character was
2205 * used. This parameter can be NULL.
2207 * @return Zero on error, otherwise the number of bytes written in the
2208 * MultiByteString buffer. Or the number of bytes needed for
2209 * the MultiByteString buffer if MultiByteCount is zero.
2216 WideCharToMultiByte(UINT CodePage
,
2218 LPCWSTR WideCharString
,
2220 LPSTR MultiByteString
,
2223 LPBOOL UsedDefaultChar
)
2225 /* Check the parameters. */
2226 if (WideCharString
== NULL
||
2227 WideCharCount
== 0 ||
2228 (MultiByteString
== NULL
&& MultiByteCount
> 0) ||
2229 (PVOID
)WideCharString
== (PVOID
)MultiByteString
||
2232 SetLastError(ERROR_INVALID_PARAMETER
);
2236 /* Determine the input string length. */
2237 if (WideCharCount
< 0)
2239 WideCharCount
= lstrlenW(WideCharString
) + 1;
2245 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2247 SetLastError(ERROR_INVALID_PARAMETER
);
2250 return IntWideCharToMultiByteUTF8(CodePage
,
2260 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2262 SetLastError(ERROR_INVALID_PARAMETER
);
2267 SetLastError(ERROR_INVALID_FLAGS
);
2270 return WideCharToUtf7(WideCharString
, WideCharCount
,
2271 MultiByteString
, MultiByteCount
);
2274 if ((DefaultChar
!=NULL
) || (UsedDefaultChar
!=NULL
))
2276 SetLastError(ERROR_INVALID_PARAMETER
);
2279 return IntWideCharToMultiByteSYMBOL(Flags
,
2286 return IntWideCharToMultiByteCP(CodePage
,
2300 * Get active ANSI code page number.
2309 return AnsiCodePage
.CodePageTable
.CodePage
;
2315 * Get active OEM code page number.
2324 return OemCodePage
.CodePageTable
.CodePage
;
2328 * @name IsDBCSLeadByteEx
2330 * Determine if passed byte is lead byte in specified code page.
2337 IsDBCSLeadByteEx(UINT CodePage
, BYTE TestByte
)
2339 PCODEPAGE_ENTRY CodePageEntry
;
2341 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2342 if (CodePageEntry
!= NULL
)
2343 return IntIsLeadByte(&CodePageEntry
->CodePageTable
, TestByte
);
2345 SetLastError(ERROR_INVALID_PARAMETER
);
2350 * @name IsDBCSLeadByteEx
2352 * Determine if passed byte is lead byte in current ANSI code page.
2359 IsDBCSLeadByte(BYTE TestByte
)
2361 return IntIsLeadByte(&AnsiCodePage
.CodePageTable
, TestByte
);
2366 * Creates a security descriptor for each NLS section. Typically used by
2367 * BASESRV to give Everyone (World SID) read access to the sections.
2369 * @param[out] SecurityDescriptor
2370 * A pointer to a correctly sized user-allocated buffer, that receives
2371 * a security descriptor containing one ACL with one World SID.
2372 * Its size should be at least equal to NLS_SECTION_SECURITY_DESCRIPTOR_SIZE.
2374 * @param[in] DescriptorSize
2375 * Size (in bytes) of the user-provided SecurityDescriptor buffer.
2377 * @param[in] AccessMask
2378 * An access mask that grants Everyone an access specific to that mask.
2381 * STATUS_SUCCESS is returned if the function has successfully
2382 * created a security descriptor for a NLS section name. Otherwise
2383 * a NTSTATUS failure code is returned.
2386 * This implementation has to be made compatible with NT <= 5.2 in order
2387 * to inter-operate with BASESRV. In particular, the security descriptor
2388 * is a user-provided buffer correctly sized. The caller is responsible
2389 * to submit the exact size of the descriptor.
2393 CreateNlsSecurityDescriptor(
2394 _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor
,
2395 _In_ SIZE_T DescriptorSize
,
2396 _In_ ULONG AccessMask
)
2398 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= {SECURITY_WORLD_SID_AUTHORITY
};
2404 if (DescriptorSize
< NLS_SECTION_SECURITY_DESCRIPTOR_SIZE
)
2406 DPRINT1("Security descriptor size too small\n");
2407 return STATUS_BUFFER_TOO_SMALL
;
2410 /* Create the World SID */
2411 Status
= RtlAllocateAndInitializeSid(&WorldAuthority
,
2414 0, 0, 0, 0, 0, 0, 0,
2416 if (!NT_SUCCESS(Status
))
2418 DPRINT1("Failed to create World SID (Status 0x%08x)\n", Status
);
2422 /* Initialize the security descriptor */
2423 Status
= RtlCreateSecurityDescriptor(SecurityDescriptor
,
2424 SECURITY_DESCRIPTOR_REVISION
);
2425 if (!NT_SUCCESS(Status
))
2427 DPRINT1("Failed to create security descriptor (Status 0x%08x)\n", Status
);
2431 /* The DACL follows the security descriptor, and includes the World SID */
2432 Dacl
= (PACL
)((ULONG_PTR
)SecurityDescriptor
+ sizeof(SECURITY_DESCRIPTOR
));
2433 DaclSize
= DescriptorSize
- sizeof(SECURITY_DESCRIPTOR
);
2435 /* Create the DACL */
2436 Status
= RtlCreateAcl(Dacl
, DaclSize
, ACL_REVISION
);
2437 if (!NT_SUCCESS(Status
))
2439 DPRINT1("Failed to create DACL (Status 0x%08x)\n", Status
);
2443 /* Add an allowed access ACE to the World SID */
2444 Status
= RtlAddAccessAllowedAce(Dacl
, ACL_REVISION
, AccessMask
, WorldSid
);
2445 if (!NT_SUCCESS(Status
))
2447 DPRINT1("Failed to add allowed access ACE for World SID (Status 0x%08x)\n", Status
);
2451 /* Set the DACL to the descriptor */
2452 Status
= RtlSetDaclSecurityDescriptor(SecurityDescriptor
, TRUE
, Dacl
, FALSE
);
2453 if (!NT_SUCCESS(Status
))
2455 DPRINT1("Failed to set DACL into descriptor (Status 0x%08x)\n", Status
);
2460 RtlFreeSid(WorldSid
);
2467 BOOL WINAPI
IsValidUILanguage(LANGID langid
)
2476 VOID WINAPI
NlsConvertIntegerToString(ULONG Value
,ULONG Base
,ULONG strsize
, LPWSTR str
, ULONG strsize2
)
2484 UINT WINAPI
SetCPGlobal(UINT CodePage
)
2495 ValidateLCType(int a1
, unsigned int a2
, int a3
, int a4
)
2506 NlsResetProcessLocale(VOID
)
2517 GetDefaultSortkeySize(LPVOID lpUnknown
)
2528 GetLinguistLangSize(LPVOID lpUnknown
)
2539 ValidateLocale(IN ULONG LocaleId
)
2550 NlsGetCacheUpdateCount(VOID
)
2561 IsNLSDefinedString(IN NLS_FUNCTION Function
,
2563 IN LPNLSVERSIONINFO lpVersionInformation
,
2564 IN LPCWSTR lpString
,
2576 GetNLSVersion(IN NLS_FUNCTION Function
,
2578 IN OUT LPNLSVERSIONINFO lpVersionInformation
)
2589 GetNLSVersionEx(IN NLS_FUNCTION function
,
2590 IN LPCWSTR lpLocaleName
,
2591 IN OUT LPNLSVERSIONINFOEX lpVersionInformation
)