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
,
289 RtlFreeUnicodeString(&UnicodeName
);
291 if (!NT_SUCCESS(Status
))
293 RtlLeaveCriticalSection(&CodePageListLock
);
297 SectionMapping
= MapViewOfFile(SectionHandle
, FILE_MAP_READ
, 0, 0, 0);
298 if (SectionMapping
== NULL
)
300 NtClose(SectionHandle
);
301 RtlLeaveCriticalSection(&CodePageListLock
);
305 CodePageEntry
= HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY
));
306 if (CodePageEntry
== NULL
)
308 NtClose(SectionHandle
);
309 RtlLeaveCriticalSection(&CodePageListLock
);
313 CodePageEntry
->CodePage
= CodePage
;
314 CodePageEntry
->SectionHandle
= SectionHandle
;
315 CodePageEntry
->SectionMapping
= SectionMapping
;
317 RtlInitCodePageTable((PUSHORT
)SectionMapping
, &CodePageEntry
->CodePageTable
);
319 /* Insert the new entry to list and unlock. Uff. */
320 InsertTailList(&CodePageListHead
, &CodePageEntry
->Entry
);
321 RtlLeaveCriticalSection(&CodePageListLock
);
323 return CodePageEntry
;
327 * @name IntMultiByteToWideCharUTF8
329 * Internal version of MultiByteToWideChar for UTF8.
331 * @see MultiByteToWideChar
332 * @todo Add UTF8 validity checks.
338 IntMultiByteToWideCharUTF8(DWORD Flags
,
339 LPCSTR MultiByteString
,
341 LPWSTR WideCharString
,
351 SetLastError(ERROR_INVALID_FLAGS
);
355 /* Does caller query for output buffer size? */
356 if (WideCharCount
== 0)
358 MbsEnd
= MultiByteString
+ MultiByteCount
;
359 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
361 Char
= *MultiByteString
++;
364 MultiByteString
+= UTF8Length
[Char
- 0x80];
366 return WideCharCount
;
369 MbsEnd
= MultiByteString
+ MultiByteCount
;
370 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
372 Char
= *MultiByteString
++;
375 *WideCharString
++ = Char
;
378 Length
= UTF8Length
[Char
- 0x80];
379 WideChar
= Char
& UTF8Mask
[Length
];
380 while (Length
&& MultiByteString
< MbsEnd
)
382 WideChar
= (WideChar
<< 6) | *MultiByteString
++;
385 *WideCharString
++ = WideChar
;
388 if (MultiByteString
< MbsEnd
)
389 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
395 * @name IntMultiByteToWideCharCP
397 * Internal version of MultiByteToWideChar for code page tables.
399 * @see MultiByteToWideChar
400 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
407 IntMultiByteToWideCharCP(UINT CodePage
,
409 LPCSTR MultiByteString
,
411 LPWSTR WideCharString
,
414 PCODEPAGE_ENTRY CodePageEntry
;
415 PCPTABLEINFO CodePageTable
;
419 /* Get code page table. */
420 CodePageEntry
= IntGetCodePageEntry(CodePage
);
421 if (CodePageEntry
== NULL
)
423 SetLastError(ERROR_INVALID_PARAMETER
);
426 CodePageTable
= &CodePageEntry
->CodePageTable
;
428 /* Different handling for DBCS code pages. */
429 if (CodePageTable
->MaximumCharacterSize
> 1)
435 LPCSTR MbsEnd
= MultiByteString
+ MultiByteCount
;
438 /* Does caller query for output buffer size? */
439 if (WideCharCount
== 0)
441 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
443 Char
= *MultiByteString
++;
448 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
453 if (MultiByteString
< MbsEnd
)
457 return WideCharCount
;
460 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
462 Char
= *MultiByteString
++;
466 *WideCharString
++ = Char
;
470 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
474 *WideCharString
++ = CodePageTable
->MultiByteTable
[Char
];
478 if (MultiByteString
< MbsEnd
)
479 *WideCharString
++ = CodePageTable
->DBCSOffsets
[DBCSOffset
+ *(PUCHAR
)MultiByteString
++];
482 if (MultiByteString
< MbsEnd
)
483 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
487 else /* Not DBCS code page */
489 /* Check for invalid characters. */
490 if (Flags
& MB_ERR_INVALID_CHARS
)
492 for (TempString
= MultiByteString
, TempLength
= MultiByteCount
;
494 TempString
++, TempLength
--)
496 if (CodePageTable
->MultiByteTable
[(UCHAR
)*TempString
] ==
497 CodePageTable
->UniDefaultChar
&&
498 *TempString
!= CodePageEntry
->CodePageTable
.DefaultChar
)
500 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
506 /* Does caller query for output buffer size? */
507 if (WideCharCount
== 0)
508 return MultiByteCount
;
510 /* Adjust buffer size. Wine trick ;-) */
511 if (WideCharCount
< MultiByteCount
)
513 MultiByteCount
= WideCharCount
;
514 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
517 for (TempLength
= MultiByteCount
;
519 MultiByteString
++, TempLength
--)
521 *WideCharString
++ = CodePageTable
->MultiByteTable
[(UCHAR
)*MultiByteString
];
524 return MultiByteCount
;
529 * @name IntMultiByteToWideCharSYMBOL
531 * Internal version of MultiByteToWideChar for SYMBOL.
533 * @see MultiByteToWideChar
539 IntMultiByteToWideCharSYMBOL(DWORD Flags
,
540 LPCSTR MultiByteString
,
542 LPWSTR WideCharString
,
552 SetLastError(ERROR_INVALID_FLAGS
);
556 if (WideCharCount
== 0)
558 return MultiByteCount
;
561 WideCharMaxLen
= WideCharCount
> MultiByteCount
? MultiByteCount
: WideCharCount
;
563 for (Count
= 0; Count
< WideCharMaxLen
; Count
++)
565 Char
= MultiByteString
[Count
];
568 WideCharString
[Count
] = Char
;
572 WideCharString
[Count
] = Char
+ 0xf000;
575 if (MultiByteCount
> WideCharMaxLen
)
577 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
581 return WideCharMaxLen
;
585 * @name IntWideCharToMultiByteSYMBOL
587 * Internal version of WideCharToMultiByte for SYMBOL.
589 * @see WideCharToMultiByte
594 IntWideCharToMultiByteSYMBOL(DWORD Flags
,
595 LPCWSTR WideCharString
,
597 LPSTR MultiByteString
,
606 SetLastError(ERROR_INVALID_PARAMETER
);
611 if (MultiByteCount
== 0)
613 return WideCharCount
;
616 MaxLen
= MultiByteCount
> WideCharCount
? WideCharCount
: MultiByteCount
;
617 for (Count
= 0; Count
< MaxLen
; Count
++)
619 Char
= WideCharString
[Count
];
622 MultiByteString
[Count
] = Char
;
626 if ((Char
>=0xf020)&&(Char
<0xf100))
628 MultiByteString
[Count
] = Char
- 0xf000;
632 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
637 if (WideCharCount
> MaxLen
)
639 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
646 * @name IntWideCharToMultiByteUTF8
648 * Internal version of WideCharToMultiByte for UTF8.
650 * @see WideCharToMultiByte
655 IntWideCharToMultiByteUTF8(UINT CodePage
,
657 LPCWSTR WideCharString
,
659 LPSTR MultiByteString
,
662 LPBOOL UsedDefaultChar
)
667 /* Does caller query for output buffer size? */
668 if (MultiByteCount
== 0)
670 for (TempLength
= 0; WideCharCount
;
671 WideCharCount
--, WideCharString
++)
674 if (*WideCharString
>= 0x80)
677 if (*WideCharString
>= 0x800)
684 for (TempLength
= MultiByteCount
; WideCharCount
; WideCharCount
--, WideCharString
++)
686 Char
= *WideCharString
;
691 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
695 *MultiByteString
++ = (CHAR
)Char
;
699 if (Char
< 0x800) /* 0x80-0x7ff: 2 bytes */
703 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
706 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
707 MultiByteString
[0] = 0xc0 | Char
;
708 MultiByteString
+= 2;
713 /* 0x800-0xffff: 3 bytes */
716 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
719 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
720 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
721 MultiByteString
[0] = 0xe0 | Char
;
722 MultiByteString
+= 3;
726 return MultiByteCount
- TempLength
;
730 * @name IsValidSBCSMapping
732 * Checks if ch (single-byte character) is a valid mapping for wch
734 * @see IntWideCharToMultiByteCP
739 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, UCHAR ch
)
741 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
742 if (Flags
& WC_NO_BEST_FIT_CHARS
)
743 return (CodePageTable
->MultiByteTable
[ch
] != wch
);
745 /* By default, all characters except TransDefaultChar apply as a valid mapping
746 for ch (so also "nearest" characters) */
747 if (ch
!= CodePageTable
->TransDefaultChar
)
750 /* The only possible left valid mapping is the default character itself */
751 return (wch
== CodePageTable
->TransUniDefaultChar
);
755 * @name IsValidDBCSMapping
757 * Checks if ch (double-byte character) is a valid mapping for wch
759 * @see IntWideCharToMultiByteCP
762 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, USHORT ch
)
764 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
765 if (ch
== CodePageTable
->TransDefaultChar
&& wch
!= CodePageTable
->TransUniDefaultChar
)
768 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
769 if (Flags
& WC_NO_BEST_FIT_CHARS
)
773 UCHAR uOffset
= CodePageTable
->DBCSOffsets
[ch
>> 8];
774 return (CodePageTable
->MultiByteTable
[(uOffset
<< 8) + (ch
& 0xff)] == wch
);
777 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
780 /* If we're still here, we have a valid mapping */
785 * @name IntWideCharToMultiByteCP
787 * Internal version of WideCharToMultiByte for code page tables.
789 * @see WideCharToMultiByte
790 * @todo Handle WC_COMPOSITECHECK
795 IntWideCharToMultiByteCP(UINT CodePage
,
797 LPCWSTR WideCharString
,
799 LPSTR MultiByteString
,
802 LPBOOL UsedDefaultChar
)
804 PCODEPAGE_ENTRY CodePageEntry
;
805 PCPTABLEINFO CodePageTable
;
808 /* Get code page table. */
809 CodePageEntry
= IntGetCodePageEntry(CodePage
);
810 if (CodePageEntry
== NULL
)
812 SetLastError(ERROR_INVALID_PARAMETER
);
815 CodePageTable
= &CodePageEntry
->CodePageTable
;
818 /* Different handling for DBCS code pages. */
819 if (CodePageTable
->MaximumCharacterSize
> 1)
821 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
822 if(Flags
|| DefaultChar
|| UsedDefaultChar
)
824 BOOL TempUsedDefaultChar
;
827 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
828 to check on every character */
830 UsedDefaultChar
= &TempUsedDefaultChar
;
832 *UsedDefaultChar
= FALSE
;
834 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
836 DefChar
= DefaultChar
[1] ? ((DefaultChar
[0] << 8) | DefaultChar
[1]) : DefaultChar
[0];
838 DefChar
= CodePageTable
->TransDefaultChar
;
840 /* Does caller query for output buffer size? */
843 for(TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
847 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
849 /* FIXME: Handle WC_COMPOSITECHECK */
852 uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
854 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
855 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
858 *UsedDefaultChar
= TRUE
;
861 /* Increment TempLength again if this is a double-byte character */
869 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
870 for(TempLength
= MultiByteCount
;
871 WideCharCount
&& TempLength
;
872 TempLength
--, WideCharString
++, WideCharCount
--)
876 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
878 /* FIXME: Handle WC_COMPOSITECHECK */
881 uChar
= ((PUSHORT
)CodePageTable
->WideCharTable
)[*WideCharString
];
883 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
884 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
887 *UsedDefaultChar
= TRUE
;
890 /* Handle double-byte characters */
893 /* Don't output a partial character */
898 *MultiByteString
++ = uChar
>> 8;
901 *MultiByteString
++ = (char)uChar
;
904 /* WideCharCount should be 0 if all characters were converted */
907 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
911 return MultiByteCount
- TempLength
;
914 /* Does caller query for output buffer size? */
917 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
919 /* Increment TempLength again if this is a double-byte character */
920 if (((PWCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
] & 0xff00)
927 /* Convert the WideCharString to the MultiByteString */
928 for (TempLength
= MultiByteCount
;
929 WideCharCount
&& TempLength
;
930 TempLength
--, WideCharString
++, WideCharCount
--)
932 USHORT uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
934 /* Is this a double-byte character? */
937 /* Don't output a partial character */
942 *MultiByteString
++ = uChar
>> 8;
945 *MultiByteString
++ = (char)uChar
;
948 /* WideCharCount should be 0 if all characters were converted */
951 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
955 return MultiByteCount
- TempLength
;
957 else /* Not DBCS code page */
961 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
962 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
964 BOOL TempUsedDefaultChar
;
967 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
968 to check on every character */
969 if (!UsedDefaultChar
)
970 UsedDefaultChar
= &TempUsedDefaultChar
;
972 *UsedDefaultChar
= FALSE
;
974 /* Does caller query for output buffer size? */
977 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
978 for (TempLength
= 0; WideCharCount
; TempLength
++, WideCharString
++, WideCharCount
--)
980 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
982 /* FIXME: Handle WC_COMPOSITECHECK */
985 if (!*UsedDefaultChar
)
986 *UsedDefaultChar
= !IntIsValidSBCSMapping(CodePageTable
,
989 ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
]);
995 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
997 DefChar
= *DefaultChar
;
999 DefChar
= CodePageTable
->TransDefaultChar
;
1001 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1002 for (TempLength
= MultiByteCount
;
1003 WideCharCount
&& TempLength
;
1004 MultiByteString
++, TempLength
--, WideCharString
++, WideCharCount
--)
1006 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1008 /* FIXME: Handle WC_COMPOSITECHECK */
1011 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1013 if (!IntIsValidSBCSMapping(CodePageTable
, Flags
, *WideCharString
, *MultiByteString
))
1015 *MultiByteString
= DefChar
;
1016 *UsedDefaultChar
= TRUE
;
1020 /* WideCharCount should be 0 if all characters were converted */
1023 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1027 return MultiByteCount
- TempLength
;
1030 /* Does caller query for output buffer size? */
1031 if (!MultiByteCount
)
1032 return WideCharCount
;
1034 /* Is the buffer large enough? */
1035 if (MultiByteCount
< WideCharCount
)
1037 /* Convert the string up to MultiByteCount and return 0 */
1038 WideCharCount
= MultiByteCount
;
1039 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1044 /* Otherwise WideCharCount will be the number of converted characters */
1045 nReturn
= WideCharCount
;
1048 /* Convert the WideCharString to the MultiByteString */
1049 for (TempLength
= WideCharCount
; --TempLength
>= 0; WideCharString
++, MultiByteString
++)
1051 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1059 * @name IntIsLeadByte
1061 * Internal function to detect if byte is lead byte in specific character
1067 IntIsLeadByte(PCPTABLEINFO TableInfo
, BYTE Byte
)
1071 if (TableInfo
->MaximumCharacterSize
== 2)
1073 for (LeadByteNo
= 0; LeadByteNo
< MAXIMUM_LEADBYTES
; LeadByteNo
++)
1075 if (TableInfo
->LeadByte
[LeadByteNo
] == Byte
)
1083 /* PUBLIC FUNCTIONS ***********************************************************/
1086 * @name GetNlsSectionName
1088 * Construct a name of NLS section.
1093 * Integer base used for converting to string. Usually set to 10.
1095 * As the name suggests the meaning of this parameter is unknown.
1096 * The native version of Kernel32 passes it as the third parameter
1097 * to NlsConvertIntegerToString function, which is used for the
1098 * actual conversion of the code page number.
1100 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1102 * Buffer that will hold the constructed name.
1104 * Size of the buffer for the result.
1106 * @return TRUE if the buffer was large enough and was filled with
1107 * the requested information, FALSE otherwise.
1114 GetNlsSectionName(UINT CodePage
,
1123 if (!NT_SUCCESS(RtlIntegerToChar(CodePage
, Base
, sizeof(Integer
), Integer
)))
1127 * If the name including the terminating NULL character doesn't
1128 * fit in the output buffer then fail.
1130 if (strlen(Integer
) + strlen(BaseName
) >= ResultSize
)
1133 lstrcpyA(Result
, BaseName
);
1134 lstrcatA(Result
, Integer
);
1140 * @name GetCPFileNameFromRegistry
1142 * Get file name of code page definition file.
1145 * Code page number to get file name of.
1147 * Buffer that is filled with file name of successful return. Can
1149 * @param FileNameSize
1150 * Size of the buffer to hold file name in WCHARs.
1152 * @return TRUE if the file name was retrieved, FALSE otherwise.
1159 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
)
1161 WCHAR ValueNameBuffer
[11];
1162 UNICODE_STRING KeyName
, ValueName
;
1163 OBJECT_ATTRIBUTES ObjectAttributes
;
1166 PKEY_VALUE_PARTIAL_INFORMATION Kvpi
;
1172 /* Convert the codepage number to string. */
1173 ValueName
.Buffer
= ValueNameBuffer
;
1174 ValueName
.MaximumLength
= sizeof(ValueNameBuffer
);
1176 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage
, 10, &ValueName
)))
1179 /* Open the registry key containing file name mappings. */
1180 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System\\"
1181 L
"CurrentControlSet\\Control\\Nls\\CodePage");
1182 InitializeObjectAttributes(&ObjectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
,
1184 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1185 if (!NT_SUCCESS(Status
))
1190 /* Allocate buffer that will be used to query the value data. */
1191 KvpiSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + (MAX_PATH
* sizeof(WCHAR
));
1192 Kvpi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KvpiSize
);
1199 /* Query the file name for our code page. */
1200 Status
= NtQueryValueKey(KeyHandle
, &ValueName
, KeyValuePartialInformation
,
1201 Kvpi
, KvpiSize
, &KvpiSize
);
1205 /* Check if we succeded and the value is non-empty string. */
1206 if (NT_SUCCESS(Status
) && Kvpi
->Type
== REG_SZ
&&
1207 Kvpi
->DataLength
> sizeof(WCHAR
))
1210 if (FileName
!= NULL
)
1212 lstrcpynW(FileName
, (WCHAR
*)Kvpi
->Data
,
1213 min(Kvpi
->DataLength
/ sizeof(WCHAR
), FileNameSize
));
1217 /* free temporary buffer */
1218 HeapFree(GetProcessHeap(),0,Kvpi
);
1223 * @name IsValidCodePage
1225 * Detect if specified code page is valid and present in the system.
1228 * Code page number to query.
1230 * @return TRUE if code page is present.
1235 IsValidCodePage(UINT CodePage
)
1237 if (CodePage
== CP_UTF8
|| CodePage
== CP_UTF7
)
1239 if (IntGetLoadedCodePageEntry(CodePage
))
1241 return GetCPFileNameFromRegistry(CodePage
, NULL
, 0);
1245 * @name MultiByteToWideChar
1247 * Convert a multi-byte string to wide-charater equivalent.
1250 * Code page to be used to perform the conversion. It can be also
1251 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1252 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1253 * for thread active code page, CP_UTF7 or CP_UTF8).
1255 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1256 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1257 * @param MultiByteString
1259 * @param MultiByteCount
1260 * Size of MultiByteString, or -1 if MultiByteString is NULL
1262 * @param WideCharString
1264 * @param WideCharCount
1265 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1266 * to know how large WideCharString should be for a successful
1269 * @return Zero on error, otherwise the number of WCHARs written
1270 * in the WideCharString buffer.
1277 MultiByteToWideChar(UINT CodePage
,
1279 LPCSTR MultiByteString
,
1281 LPWSTR WideCharString
,
1284 /* Check the parameters. */
1285 if (MultiByteString
== NULL
||
1286 (WideCharString
== NULL
&& WideCharCount
> 0) ||
1287 (PVOID
)MultiByteString
== (PVOID
)WideCharString
)
1289 SetLastError(ERROR_INVALID_PARAMETER
);
1293 /* Determine the input string length. */
1294 if (MultiByteCount
< 0)
1296 MultiByteCount
= lstrlenA(MultiByteString
) + 1;
1302 return IntMultiByteToWideCharUTF8(Flags
,
1309 DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n");
1313 return IntMultiByteToWideCharSYMBOL(Flags
,
1319 return IntMultiByteToWideCharCP(CodePage
,
1329 * @name WideCharToMultiByte
1331 * Convert a wide-charater string to closest multi-byte equivalent.
1334 * Code page to be used to perform the conversion. It can be also
1335 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1336 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1337 * for thread active code page, CP_UTF7 or CP_UTF8).
1339 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
1340 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
1341 * @param WideCharString
1342 * Points to the wide-character string to be converted.
1343 * @param WideCharCount
1344 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
1345 * know how large WideCharString should be for a successful conversion.
1346 * @param MultiByteString
1347 * Points to the buffer to receive the translated string.
1348 * @param MultiByteCount
1349 * Specifies the size in bytes of the buffer pointed to by the
1350 * MultiByteString parameter. If this value is zero, the function
1351 * returns the number of bytes required for the buffer.
1352 * @param DefaultChar
1353 * Points to the character used if a wide character cannot be
1354 * represented in the specified code page. If this parameter is
1355 * NULL, a system default value is used.
1356 * @param UsedDefaultChar
1357 * Points to a flag that indicates whether a default character was
1358 * used. This parameter can be NULL.
1360 * @return Zero on error, otherwise the number of bytes written in the
1361 * MultiByteString buffer. Or the number of bytes needed for
1362 * the MultiByteString buffer if MultiByteCount is zero.
1369 WideCharToMultiByte(UINT CodePage
,
1371 LPCWSTR WideCharString
,
1373 LPSTR MultiByteString
,
1376 LPBOOL UsedDefaultChar
)
1378 /* Check the parameters. */
1379 if (WideCharString
== NULL
||
1380 (MultiByteString
== NULL
&& MultiByteCount
> 0) ||
1381 (PVOID
)WideCharString
== (PVOID
)MultiByteString
||
1384 SetLastError(ERROR_INVALID_PARAMETER
);
1388 /* Determine the input string length. */
1389 if (WideCharCount
< 0)
1391 WideCharCount
= lstrlenW(WideCharString
) + 1;
1397 return IntWideCharToMultiByteUTF8(CodePage
,
1407 DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n");
1411 if ((DefaultChar
!=NULL
) || (UsedDefaultChar
!=NULL
))
1413 SetLastError(ERROR_INVALID_PARAMETER
);
1416 return IntWideCharToMultiByteSYMBOL(Flags
,
1423 return IntWideCharToMultiByteCP(CodePage
,
1437 * Get active ANSI code page number.
1446 return AnsiCodePage
.CodePageTable
.CodePage
;
1452 * Get active OEM code page number.
1461 return OemCodePage
.CodePageTable
.CodePage
;
1465 * @name IsDBCSLeadByteEx
1467 * Determine if passed byte is lead byte in specified code page.
1474 IsDBCSLeadByteEx(UINT CodePage
, BYTE TestByte
)
1476 PCODEPAGE_ENTRY CodePageEntry
;
1478 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1479 if (CodePageEntry
!= NULL
)
1480 return IntIsLeadByte(&CodePageEntry
->CodePageTable
, TestByte
);
1482 SetLastError(ERROR_INVALID_PARAMETER
);
1487 * @name IsDBCSLeadByteEx
1489 * Determine if passed byte is lead byte in current ANSI code page.
1496 IsDBCSLeadByte(BYTE TestByte
)
1498 return IntIsLeadByte(&AnsiCodePage
.CodePageTable
, TestByte
);