3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: dll/win32/kernel32/misc/nls.c
6 * PURPOSE: National Language Support
7 * PROGRAMMER: Filip Navara
10 * Thomas Weidenmueller
15 /* INCLUDES *******************************************************************/
21 /* GLOBAL VARIABLES ***********************************************************/
23 /* Sequence length based on the first character. */
24 static const char UTF8Length
[128] =
26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
29 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
31 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
32 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
33 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0 /* 0xF0 - 0xFF */
36 /* First byte mask depending on UTF-8 sequence length. */
37 static const unsigned char UTF8Mask
[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
39 /* FIXME: Change to HASH table or linear array. */
40 static LIST_ENTRY CodePageListHead
;
41 static CODEPAGE_ENTRY AnsiCodePage
;
42 static CODEPAGE_ENTRY OemCodePage
;
43 static RTL_CRITICAL_SECTION CodePageListLock
;
45 /* FORWARD DECLARATIONS *******************************************************/
48 GetNlsSectionName(UINT CodePage
, UINT Base
, ULONG Unknown
,
49 LPSTR BaseName
, LPSTR Result
, ULONG ResultSize
);
52 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
);
54 /* PRIVATE FUNCTIONS **********************************************************/
59 * Internal NLS related stuff initialization.
66 UNICODE_STRING DirName
;
67 OBJECT_ATTRIBUTES ObjectAttributes
;
70 InitializeListHead(&CodePageListHead
);
71 RtlInitializeCriticalSection(&CodePageListLock
);
74 * FIXME: Eventually this should be done only for the NLS Server
75 * process, but since we don't have anything like that (yet?) we
76 * always try to create the "\Nls" directory here.
78 RtlInitUnicodeString(&DirName
, L
"\\Nls");
80 InitializeObjectAttributes(&ObjectAttributes
,
82 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
86 if (NT_SUCCESS(NtCreateDirectoryObject(&Handle
, DIRECTORY_ALL_ACCESS
, &ObjectAttributes
)))
91 /* Setup ANSI code page. */
92 AnsiCodePage
.CodePage
= CP_ACP
;
93 AnsiCodePage
.SectionHandle
= NULL
;
94 AnsiCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->AnsiCodePageData
;
96 RtlInitCodePageTable((PUSHORT
)AnsiCodePage
.SectionMapping
,
97 &AnsiCodePage
.CodePageTable
);
98 InsertTailList(&CodePageListHead
, &AnsiCodePage
.Entry
);
100 /* Setup OEM code page. */
101 OemCodePage
.CodePage
= CP_OEMCP
;
102 OemCodePage
.SectionHandle
= NULL
;
103 OemCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->OemCodePageData
;
105 RtlInitCodePageTable((PUSHORT
)OemCodePage
.SectionMapping
,
106 &OemCodePage
.CodePageTable
);
107 InsertTailList(&CodePageListHead
, &OemCodePage
.Entry
);
115 * Internal NLS related stuff uninitialization.
122 PCODEPAGE_ENTRY Current
;
124 /* Delete the code page list. */
125 while (!IsListEmpty(&CodePageListHead
))
127 Current
= CONTAINING_RECORD(CodePageListHead
.Flink
, CODEPAGE_ENTRY
, Entry
);
128 if (Current
->SectionHandle
!= NULL
)
130 UnmapViewOfFile(Current
->SectionMapping
);
131 NtClose(Current
->SectionHandle
);
133 RemoveHeadList(&CodePageListHead
);
135 RtlDeleteCriticalSection(&CodePageListLock
);
139 * @name IntGetLoadedCodePageEntry
141 * Internal function to get structure containing a code page information
142 * of code page that is already loaded.
145 * Number of the code page. Special values like CP_OEMCP, CP_ACP
146 * or CP_UTF8 aren't allowed.
148 * @return Code page entry or NULL if the specified code page hasn't
154 IntGetLoadedCodePageEntry(UINT CodePage
)
156 LIST_ENTRY
*CurrentEntry
;
157 PCODEPAGE_ENTRY Current
;
159 RtlEnterCriticalSection(&CodePageListLock
);
160 for (CurrentEntry
= CodePageListHead
.Flink
;
161 CurrentEntry
!= &CodePageListHead
;
162 CurrentEntry
= CurrentEntry
->Flink
)
164 Current
= CONTAINING_RECORD(CurrentEntry
, CODEPAGE_ENTRY
, Entry
);
165 if (Current
->CodePage
== CodePage
)
167 RtlLeaveCriticalSection(&CodePageListLock
);
171 RtlLeaveCriticalSection(&CodePageListLock
);
177 * @name IntGetCodePageEntry
179 * Internal function to get structure containing a code page information.
182 * Number of the code page. Special values like CP_OEMCP, CP_ACP
183 * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
185 * @return Code page entry.
190 IntGetCodePageEntry(UINT CodePage
)
192 CHAR SectionName
[40];
194 HANDLE SectionHandle
= INVALID_HANDLE_VALUE
, FileHandle
;
195 PBYTE SectionMapping
;
196 OBJECT_ATTRIBUTES ObjectAttributes
;
197 ANSI_STRING AnsiName
;
198 UNICODE_STRING UnicodeName
;
199 WCHAR FileName
[MAX_PATH
+ 1];
201 PCODEPAGE_ENTRY CodePageEntry
;
203 if (CodePage
== CP_THREAD_ACP
)
205 if (!GetLocaleInfoW(GetThreadLocale(),
206 LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
208 sizeof(CodePage
) / sizeof(WCHAR
)))
210 /* Last error is set by GetLocaleInfoW. */
214 else if (CodePage
== CP_MACCP
)
216 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT
,
217 LOCALE_IDEFAULTMACCODEPAGE
| LOCALE_RETURN_NUMBER
,
219 sizeof(CodePage
) / sizeof(WCHAR
)))
221 /* Last error is set by GetLocaleInfoW. */
226 /* Try searching for loaded page first. */
227 CodePageEntry
= IntGetLoadedCodePageEntry(CodePage
);
228 if (CodePageEntry
!= NULL
)
230 return CodePageEntry
;
234 * Yes, we really want to lock here. Otherwise it can happen that
235 * two parallel requests will try to get the entry for the same
236 * code page and we would load it twice.
238 RtlEnterCriticalSection(&CodePageListLock
);
240 /* Generate the section name. */
241 if (!GetNlsSectionName(CodePage
,
244 "\\Nls\\NlsSectionCP",
246 sizeof(SectionName
)))
248 RtlLeaveCriticalSection(&CodePageListLock
);
252 RtlInitAnsiString(&AnsiName
, SectionName
);
253 RtlAnsiStringToUnicodeString(&UnicodeName
, &AnsiName
, TRUE
);
255 InitializeObjectAttributes(&ObjectAttributes
, &UnicodeName
, 0, NULL
, NULL
);
257 /* Try to open the section first */
258 Status
= NtOpenSection(&SectionHandle
, SECTION_MAP_READ
, &ObjectAttributes
);
260 /* If the section doesn't exist, try to create it. */
261 if (Status
== STATUS_UNSUCCESSFUL
||
262 Status
== STATUS_OBJECT_NAME_NOT_FOUND
||
263 Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
265 FileNamePos
= GetSystemDirectoryW(FileName
, MAX_PATH
);
266 if (GetCPFileNameFromRegistry(CodePage
,
267 FileName
+ FileNamePos
+ 1,
268 MAX_PATH
- FileNamePos
- 1))
270 FileName
[FileNamePos
] = L
'\\';
271 FileName
[MAX_PATH
] = 0;
272 FileHandle
= CreateFileW(FileName
,
280 Status
= NtCreateSection(&SectionHandle
,
288 /* HACK: Check if another process was faster
289 * and already created this section. See bug 3626 for details */
290 if (Status
== STATUS_OBJECT_NAME_COLLISION
)
292 /* Close the file then */
295 /* And open the section */
296 Status
= NtOpenSection(&SectionHandle
,
302 RtlFreeUnicodeString(&UnicodeName
);
304 if (!NT_SUCCESS(Status
))
306 RtlLeaveCriticalSection(&CodePageListLock
);
310 SectionMapping
= MapViewOfFile(SectionHandle
, FILE_MAP_READ
, 0, 0, 0);
311 if (SectionMapping
== NULL
)
313 NtClose(SectionHandle
);
314 RtlLeaveCriticalSection(&CodePageListLock
);
318 CodePageEntry
= HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY
));
319 if (CodePageEntry
== NULL
)
321 NtClose(SectionHandle
);
322 RtlLeaveCriticalSection(&CodePageListLock
);
326 CodePageEntry
->CodePage
= CodePage
;
327 CodePageEntry
->SectionHandle
= SectionHandle
;
328 CodePageEntry
->SectionMapping
= SectionMapping
;
330 RtlInitCodePageTable((PUSHORT
)SectionMapping
, &CodePageEntry
->CodePageTable
);
332 /* Insert the new entry to list and unlock. Uff. */
333 InsertTailList(&CodePageListHead
, &CodePageEntry
->Entry
);
334 RtlLeaveCriticalSection(&CodePageListLock
);
336 return CodePageEntry
;
340 * @name IntMultiByteToWideCharUTF8
342 * Internal version of MultiByteToWideChar for UTF8.
344 * @see MultiByteToWideChar
345 * @todo Add UTF8 validity checks.
351 IntMultiByteToWideCharUTF8(DWORD Flags
,
352 LPCSTR MultiByteString
,
354 LPWSTR WideCharString
,
364 SetLastError(ERROR_INVALID_FLAGS
);
368 /* Does caller query for output buffer size? */
369 if (WideCharCount
== 0)
371 MbsEnd
= MultiByteString
+ MultiByteCount
;
372 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
374 Char
= *MultiByteString
++;
377 MultiByteString
+= UTF8Length
[Char
- 0x80];
379 return WideCharCount
;
382 MbsEnd
= MultiByteString
+ MultiByteCount
;
383 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
385 Char
= *MultiByteString
++;
388 *WideCharString
++ = Char
;
391 Length
= UTF8Length
[Char
- 0x80];
392 WideChar
= Char
& UTF8Mask
[Length
];
393 while (Length
&& MultiByteString
< MbsEnd
)
395 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
398 *WideCharString
++ = WideChar
;
401 if (MultiByteString
< MbsEnd
)
402 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
408 * @name IntMultiByteToWideCharCP
410 * Internal version of MultiByteToWideChar for code page tables.
412 * @see MultiByteToWideChar
413 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
420 IntMultiByteToWideCharCP(UINT CodePage
,
422 LPCSTR MultiByteString
,
424 LPWSTR WideCharString
,
427 PCODEPAGE_ENTRY CodePageEntry
;
428 PCPTABLEINFO CodePageTable
;
432 /* Get code page table. */
433 CodePageEntry
= IntGetCodePageEntry(CodePage
);
434 if (CodePageEntry
== NULL
)
436 SetLastError(ERROR_INVALID_PARAMETER
);
439 CodePageTable
= &CodePageEntry
->CodePageTable
;
441 /* Different handling for DBCS code pages. */
442 if (CodePageTable
->MaximumCharacterSize
> 1)
448 LPCSTR MbsEnd
= MultiByteString
+ MultiByteCount
;
451 /* Does caller query for output buffer size? */
452 if (WideCharCount
== 0)
454 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
456 Char
= *MultiByteString
++;
461 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
466 if (MultiByteString
< MbsEnd
)
470 return WideCharCount
;
473 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
475 Char
= *MultiByteString
++;
479 *WideCharString
++ = Char
;
483 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
487 *WideCharString
++ = CodePageTable
->MultiByteTable
[Char
];
491 if (MultiByteString
< MbsEnd
)
492 *WideCharString
++ = CodePageTable
->DBCSOffsets
[DBCSOffset
+ *(PUCHAR
)MultiByteString
++];
495 if (MultiByteString
< MbsEnd
)
497 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
503 else /* Not DBCS code page */
505 /* Check for invalid characters. */
506 if (Flags
& MB_ERR_INVALID_CHARS
)
508 for (TempString
= MultiByteString
, TempLength
= MultiByteCount
;
510 TempString
++, TempLength
--)
512 if (CodePageTable
->MultiByteTable
[(UCHAR
)*TempString
] ==
513 CodePageTable
->UniDefaultChar
&&
514 *TempString
!= CodePageEntry
->CodePageTable
.DefaultChar
)
516 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
522 /* Does caller query for output buffer size? */
523 if (WideCharCount
== 0)
524 return MultiByteCount
;
526 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
527 for (TempLength
= (WideCharCount
< MultiByteCount
) ? WideCharCount
: MultiByteCount
;
529 MultiByteString
++, TempLength
--)
531 *WideCharString
++ = CodePageTable
->MultiByteTable
[(UCHAR
)*MultiByteString
];
534 /* Adjust buffer size. Wine trick ;-) */
535 if (WideCharCount
< MultiByteCount
)
537 MultiByteCount
= WideCharCount
;
538 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
541 return MultiByteCount
;
546 * @name IntMultiByteToWideCharSYMBOL
548 * Internal version of MultiByteToWideChar for SYMBOL.
550 * @see MultiByteToWideChar
556 IntMultiByteToWideCharSYMBOL(DWORD Flags
,
557 LPCSTR MultiByteString
,
559 LPWSTR WideCharString
,
569 SetLastError(ERROR_INVALID_FLAGS
);
573 if (WideCharCount
== 0)
575 return MultiByteCount
;
578 WideCharMaxLen
= WideCharCount
> MultiByteCount
? MultiByteCount
: WideCharCount
;
580 for (Count
= 0; Count
< WideCharMaxLen
; Count
++)
582 Char
= MultiByteString
[Count
];
585 WideCharString
[Count
] = Char
;
589 WideCharString
[Count
] = Char
+ 0xf000;
592 if (MultiByteCount
> WideCharMaxLen
)
594 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
598 return WideCharMaxLen
;
602 * @name IntWideCharToMultiByteSYMBOL
604 * Internal version of WideCharToMultiByte for SYMBOL.
606 * @see WideCharToMultiByte
611 IntWideCharToMultiByteSYMBOL(DWORD Flags
,
612 LPCWSTR WideCharString
,
614 LPSTR MultiByteString
,
623 SetLastError(ERROR_INVALID_PARAMETER
);
628 if (MultiByteCount
== 0)
630 return WideCharCount
;
633 MaxLen
= MultiByteCount
> WideCharCount
? WideCharCount
: MultiByteCount
;
634 for (Count
= 0; Count
< MaxLen
; Count
++)
636 Char
= WideCharString
[Count
];
639 MultiByteString
[Count
] = Char
;
643 if ((Char
>=0xf020)&&(Char
<0xf100))
645 MultiByteString
[Count
] = Char
- 0xf000;
649 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
654 if (WideCharCount
> MaxLen
)
656 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
663 * @name IntWideCharToMultiByteUTF8
665 * Internal version of WideCharToMultiByte for UTF8.
667 * @see WideCharToMultiByte
672 IntWideCharToMultiByteUTF8(UINT CodePage
,
674 LPCWSTR WideCharString
,
676 LPSTR MultiByteString
,
679 LPBOOL UsedDefaultChar
)
684 /* Does caller query for output buffer size? */
685 if (MultiByteCount
== 0)
687 for (TempLength
= 0; WideCharCount
;
688 WideCharCount
--, WideCharString
++)
691 if (*WideCharString
>= 0x80)
694 if (*WideCharString
>= 0x800)
701 for (TempLength
= MultiByteCount
; WideCharCount
; WideCharCount
--, WideCharString
++)
703 Char
= *WideCharString
;
708 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
712 *MultiByteString
++ = (CHAR
)Char
;
716 if (Char
< 0x800) /* 0x80-0x7ff: 2 bytes */
720 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
723 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
724 MultiByteString
[0] = 0xc0 | Char
;
725 MultiByteString
+= 2;
730 /* 0x800-0xffff: 3 bytes */
733 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
736 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
737 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
738 MultiByteString
[0] = 0xe0 | Char
;
739 MultiByteString
+= 3;
743 return MultiByteCount
- TempLength
;
747 * @name IsValidSBCSMapping
749 * Checks if ch (single-byte character) is a valid mapping for wch
751 * @see IntWideCharToMultiByteCP
756 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, UCHAR ch
)
758 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
759 if (Flags
& WC_NO_BEST_FIT_CHARS
)
760 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
762 /* By default, all characters except TransDefaultChar apply as a valid mapping
763 for ch (so also "nearest" characters) */
764 if (ch
!= CodePageTable
->TransDefaultChar
)
767 /* The only possible left valid mapping is the default character itself */
768 return (wch
== CodePageTable
->TransUniDefaultChar
);
772 * @name IsValidDBCSMapping
774 * Checks if ch (double-byte character) is a valid mapping for wch
776 * @see IntWideCharToMultiByteCP
779 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, USHORT ch
)
781 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
782 if (ch
== CodePageTable
->TransDefaultChar
&& wch
!= CodePageTable
->TransUniDefaultChar
)
785 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
786 if (Flags
& WC_NO_BEST_FIT_CHARS
)
790 USHORT uOffset
= CodePageTable
->DBCSOffsets
[ch
>> 8];
791 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
792 return (CodePageTable
->DBCSOffsets
[uOffset
+ (ch
& 0xff)] == wch
);
795 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
798 /* If we're still here, we have a valid mapping */
803 * @name IntWideCharToMultiByteCP
805 * Internal version of WideCharToMultiByte for code page tables.
807 * @see WideCharToMultiByte
808 * @todo Handle WC_COMPOSITECHECK
813 IntWideCharToMultiByteCP(UINT CodePage
,
815 LPCWSTR WideCharString
,
817 LPSTR MultiByteString
,
820 LPBOOL UsedDefaultChar
)
822 PCODEPAGE_ENTRY CodePageEntry
;
823 PCPTABLEINFO CodePageTable
;
826 /* Get code page table. */
827 CodePageEntry
= IntGetCodePageEntry(CodePage
);
828 if (CodePageEntry
== NULL
)
830 SetLastError(ERROR_INVALID_PARAMETER
);
833 CodePageTable
= &CodePageEntry
->CodePageTable
;
836 /* Different handling for DBCS code pages. */
837 if (CodePageTable
->MaximumCharacterSize
> 1)
839 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
840 if(Flags
|| DefaultChar
|| UsedDefaultChar
)
842 BOOL TempUsedDefaultChar
;
845 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
846 to check on every character */
848 UsedDefaultChar
= &TempUsedDefaultChar
;
850 *UsedDefaultChar
= FALSE
;
852 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
854 DefChar
= DefaultChar
[1] ? ((DefaultChar
[0] << 8) | DefaultChar
[1]) : DefaultChar
[0];
856 DefChar
= CodePageTable
->TransDefaultChar
;
858 /* Does caller query for output buffer size? */
861 for(TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
865 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
867 /* FIXME: Handle WC_COMPOSITECHECK */
870 uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
872 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
873 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
876 *UsedDefaultChar
= TRUE
;
879 /* Increment TempLength again if this is a double-byte character */
887 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
888 for(TempLength
= MultiByteCount
;
889 WideCharCount
&& TempLength
;
890 TempLength
--, WideCharString
++, WideCharCount
--)
894 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
896 /* FIXME: Handle WC_COMPOSITECHECK */
899 uChar
= ((PUSHORT
)CodePageTable
->WideCharTable
)[*WideCharString
];
901 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
902 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
905 *UsedDefaultChar
= TRUE
;
908 /* Handle double-byte characters */
911 /* Don't output a partial character */
916 *MultiByteString
++ = uChar
>> 8;
919 *MultiByteString
++ = (char)uChar
;
922 /* WideCharCount should be 0 if all characters were converted */
925 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
929 return MultiByteCount
- TempLength
;
932 /* Does caller query for output buffer size? */
935 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
937 /* Increment TempLength again if this is a double-byte character */
938 if (((PWCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
] & 0xff00)
945 /* Convert the WideCharString to the MultiByteString */
946 for (TempLength
= MultiByteCount
;
947 WideCharCount
&& TempLength
;
948 TempLength
--, WideCharString
++, WideCharCount
--)
950 USHORT uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
952 /* Is this a double-byte character? */
955 /* Don't output a partial character */
960 *MultiByteString
++ = uChar
>> 8;
963 *MultiByteString
++ = (char)uChar
;
966 /* WideCharCount should be 0 if all characters were converted */
969 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
973 return MultiByteCount
- TempLength
;
975 else /* Not DBCS code page */
979 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
980 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
982 BOOL TempUsedDefaultChar
;
985 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
986 to check on every character */
987 if (!UsedDefaultChar
)
988 UsedDefaultChar
= &TempUsedDefaultChar
;
990 *UsedDefaultChar
= FALSE
;
992 /* Does caller query for output buffer size? */
995 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
996 for (TempLength
= 0; WideCharCount
; TempLength
++, WideCharString
++, WideCharCount
--)
998 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1000 /* FIXME: Handle WC_COMPOSITECHECK */
1003 if (!*UsedDefaultChar
)
1004 *UsedDefaultChar
= !IntIsValidSBCSMapping(CodePageTable
,
1007 ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
]);
1013 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1015 DefChar
= *DefaultChar
;
1017 DefChar
= CodePageTable
->TransDefaultChar
;
1019 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1020 for (TempLength
= MultiByteCount
;
1021 WideCharCount
&& TempLength
;
1022 MultiByteString
++, TempLength
--, WideCharString
++, WideCharCount
--)
1024 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1026 /* FIXME: Handle WC_COMPOSITECHECK */
1029 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1031 if (!IntIsValidSBCSMapping(CodePageTable
, Flags
, *WideCharString
, *MultiByteString
))
1033 *MultiByteString
= DefChar
;
1034 *UsedDefaultChar
= TRUE
;
1038 /* WideCharCount should be 0 if all characters were converted */
1041 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1045 return MultiByteCount
- TempLength
;
1048 /* Does caller query for output buffer size? */
1049 if (!MultiByteCount
)
1050 return WideCharCount
;
1052 /* Is the buffer large enough? */
1053 if (MultiByteCount
< WideCharCount
)
1055 /* Convert the string up to MultiByteCount and return 0 */
1056 WideCharCount
= MultiByteCount
;
1057 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1062 /* Otherwise WideCharCount will be the number of converted characters */
1063 nReturn
= WideCharCount
;
1066 /* Convert the WideCharString to the MultiByteString */
1067 for (TempLength
= WideCharCount
; --TempLength
>= 0; WideCharString
++, MultiByteString
++)
1069 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1077 * @name IntIsLeadByte
1079 * Internal function to detect if byte is lead byte in specific character
1085 IntIsLeadByte(PCPTABLEINFO TableInfo
, BYTE Byte
)
1089 if (TableInfo
->MaximumCharacterSize
== 2)
1091 for (i
= 0; i
< MAXIMUM_LEADBYTES
&& TableInfo
->LeadByte
[i
]; i
+= 2)
1093 if (Byte
>= TableInfo
->LeadByte
[i
] && Byte
<= TableInfo
->LeadByte
[i
+1])
1101 /* PUBLIC FUNCTIONS ***********************************************************/
1104 * @name GetNlsSectionName
1106 * Construct a name of NLS section.
1111 * Integer base used for converting to string. Usually set to 10.
1113 * As the name suggests the meaning of this parameter is unknown.
1114 * The native version of Kernel32 passes it as the third parameter
1115 * to NlsConvertIntegerToString function, which is used for the
1116 * actual conversion of the code page number.
1118 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1120 * Buffer that will hold the constructed name.
1122 * Size of the buffer for the result.
1124 * @return TRUE if the buffer was large enough and was filled with
1125 * the requested information, FALSE otherwise.
1132 GetNlsSectionName(UINT CodePage
,
1141 if (!NT_SUCCESS(RtlIntegerToChar(CodePage
, Base
, sizeof(Integer
), Integer
)))
1145 * If the name including the terminating NULL character doesn't
1146 * fit in the output buffer then fail.
1148 if (strlen(Integer
) + strlen(BaseName
) >= ResultSize
)
1151 lstrcpyA(Result
, BaseName
);
1152 lstrcatA(Result
, Integer
);
1158 * @name GetCPFileNameFromRegistry
1160 * Get file name of code page definition file.
1163 * Code page number to get file name of.
1165 * Buffer that is filled with file name of successful return. Can
1167 * @param FileNameSize
1168 * Size of the buffer to hold file name in WCHARs.
1170 * @return TRUE if the file name was retrieved, FALSE otherwise.
1177 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
)
1179 WCHAR ValueNameBuffer
[11];
1180 UNICODE_STRING KeyName
, ValueName
;
1181 OBJECT_ATTRIBUTES ObjectAttributes
;
1184 PKEY_VALUE_PARTIAL_INFORMATION Kvpi
;
1190 /* Convert the codepage number to string. */
1191 ValueName
.Buffer
= ValueNameBuffer
;
1192 ValueName
.MaximumLength
= sizeof(ValueNameBuffer
);
1194 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage
, 10, &ValueName
)))
1197 /* Open the registry key containing file name mappings. */
1198 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System\\"
1199 L
"CurrentControlSet\\Control\\Nls\\CodePage");
1200 InitializeObjectAttributes(&ObjectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
,
1202 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1203 if (!NT_SUCCESS(Status
))
1208 /* Allocate buffer that will be used to query the value data. */
1209 KvpiSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + (MAX_PATH
* sizeof(WCHAR
));
1210 Kvpi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KvpiSize
);
1217 /* Query the file name for our code page. */
1218 Status
= NtQueryValueKey(KeyHandle
, &ValueName
, KeyValuePartialInformation
,
1219 Kvpi
, KvpiSize
, &KvpiSize
);
1223 /* Check if we succeded and the value is non-empty string. */
1224 if (NT_SUCCESS(Status
) && Kvpi
->Type
== REG_SZ
&&
1225 Kvpi
->DataLength
> sizeof(WCHAR
))
1228 if (FileName
!= NULL
)
1230 lstrcpynW(FileName
, (WCHAR
*)Kvpi
->Data
,
1231 min(Kvpi
->DataLength
/ sizeof(WCHAR
), FileNameSize
));
1235 /* free temporary buffer */
1236 HeapFree(GetProcessHeap(),0,Kvpi
);
1241 * @name IsValidCodePage
1243 * Detect if specified code page is valid and present in the system.
1246 * Code page number to query.
1248 * @return TRUE if code page is present.
1253 IsValidCodePage(UINT CodePage
)
1255 if (CodePage
== CP_UTF8
|| CodePage
== CP_UTF7
)
1257 if (IntGetLoadedCodePageEntry(CodePage
))
1259 return GetCPFileNameFromRegistry(CodePage
, NULL
, 0);
1263 * @name MultiByteToWideChar
1265 * Convert a multi-byte string to wide-charater equivalent.
1268 * Code page to be used to perform the conversion. It can be also
1269 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1270 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1271 * for thread active code page, CP_UTF7 or CP_UTF8).
1273 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1274 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1275 * @param MultiByteString
1277 * @param MultiByteCount
1278 * Size of MultiByteString, or -1 if MultiByteString is NULL
1280 * @param WideCharString
1282 * @param WideCharCount
1283 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1284 * to know how large WideCharString should be for a successful
1287 * @return Zero on error, otherwise the number of WCHARs written
1288 * in the WideCharString buffer.
1295 MultiByteToWideChar(UINT CodePage
,
1297 LPCSTR MultiByteString
,
1299 LPWSTR WideCharString
,
1302 /* Check the parameters. */
1303 if (MultiByteString
== NULL
||
1304 (WideCharString
== NULL
&& WideCharCount
> 0) ||
1305 (PVOID
)MultiByteString
== (PVOID
)WideCharString
)
1307 SetLastError(ERROR_INVALID_PARAMETER
);
1311 /* Determine the input string length. */
1312 if (MultiByteCount
< 0)
1314 MultiByteCount
= lstrlenA(MultiByteString
) + 1;
1320 return IntMultiByteToWideCharUTF8(Flags
,
1327 DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n");
1331 return IntMultiByteToWideCharSYMBOL(Flags
,
1337 return IntMultiByteToWideCharCP(CodePage
,
1347 * @name WideCharToMultiByte
1349 * Convert a wide-charater string to closest multi-byte equivalent.
1352 * Code page to be used to perform the conversion. It can be also
1353 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1354 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1355 * for thread active code page, CP_UTF7 or CP_UTF8).
1357 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
1358 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
1359 * @param WideCharString
1360 * Points to the wide-character string to be converted.
1361 * @param WideCharCount
1362 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
1363 * know how large WideCharString should be for a successful conversion.
1364 * @param MultiByteString
1365 * Points to the buffer to receive the translated string.
1366 * @param MultiByteCount
1367 * Specifies the size in bytes of the buffer pointed to by the
1368 * MultiByteString parameter. If this value is zero, the function
1369 * returns the number of bytes required for the buffer.
1370 * @param DefaultChar
1371 * Points to the character used if a wide character cannot be
1372 * represented in the specified code page. If this parameter is
1373 * NULL, a system default value is used.
1374 * @param UsedDefaultChar
1375 * Points to a flag that indicates whether a default character was
1376 * used. This parameter can be NULL.
1378 * @return Zero on error, otherwise the number of bytes written in the
1379 * MultiByteString buffer. Or the number of bytes needed for
1380 * the MultiByteString buffer if MultiByteCount is zero.
1387 WideCharToMultiByte(UINT CodePage
,
1389 LPCWSTR WideCharString
,
1391 LPSTR MultiByteString
,
1394 LPBOOL UsedDefaultChar
)
1396 /* Check the parameters. */
1397 if (WideCharString
== NULL
||
1398 (MultiByteString
== NULL
&& MultiByteCount
> 0) ||
1399 (PVOID
)WideCharString
== (PVOID
)MultiByteString
||
1402 SetLastError(ERROR_INVALID_PARAMETER
);
1406 /* Determine the input string length. */
1407 if (WideCharCount
< 0)
1409 WideCharCount
= lstrlenW(WideCharString
) + 1;
1415 return IntWideCharToMultiByteUTF8(CodePage
,
1425 DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n");
1429 if ((DefaultChar
!=NULL
) || (UsedDefaultChar
!=NULL
))
1431 SetLastError(ERROR_INVALID_PARAMETER
);
1434 return IntWideCharToMultiByteSYMBOL(Flags
,
1441 return IntWideCharToMultiByteCP(CodePage
,
1455 * Get active ANSI code page number.
1464 return AnsiCodePage
.CodePageTable
.CodePage
;
1470 * Get active OEM code page number.
1479 return OemCodePage
.CodePageTable
.CodePage
;
1483 * @name IsDBCSLeadByteEx
1485 * Determine if passed byte is lead byte in specified code page.
1492 IsDBCSLeadByteEx(UINT CodePage
, BYTE TestByte
)
1494 PCODEPAGE_ENTRY CodePageEntry
;
1496 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1497 if (CodePageEntry
!= NULL
)
1498 return IntIsLeadByte(&CodePageEntry
->CodePageTable
, TestByte
);
1500 SetLastError(ERROR_INVALID_PARAMETER
);
1505 * @name IsDBCSLeadByteEx
1507 * Determine if passed byte is lead byte in current ANSI code page.
1514 IsDBCSLeadByte(BYTE TestByte
)
1516 return IntIsLeadByte(&AnsiCodePage
.CodePageTable
, TestByte
);