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 1, 1, 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, 4, 4, 4, 4, 5, 5, 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
);
59 /* PRIVATE FUNCTIONS **********************************************************/
64 * Internal NLS related stuff initialization.
71 UNICODE_STRING DirName
;
72 OBJECT_ATTRIBUTES ObjectAttributes
;
75 InitializeListHead(&CodePageListHead
);
76 RtlInitializeCriticalSection(&CodePageListLock
);
79 * FIXME: Eventually this should be done only for the NLS Server
80 * process, but since we don't have anything like that (yet?) we
81 * always try to create the "\Nls" directory here.
83 RtlInitUnicodeString(&DirName
, L
"\\Nls");
85 InitializeObjectAttributes(&ObjectAttributes
,
87 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
91 if (NT_SUCCESS(NtCreateDirectoryObject(&Handle
, DIRECTORY_ALL_ACCESS
, &ObjectAttributes
)))
96 /* Setup ANSI code page. */
97 AnsiCodePage
.SectionHandle
= NULL
;
98 AnsiCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->AnsiCodePageData
;
100 RtlInitCodePageTable((PUSHORT
)AnsiCodePage
.SectionMapping
,
101 &AnsiCodePage
.CodePageTable
);
102 AnsiCodePage
.CodePage
= AnsiCodePage
.CodePageTable
.CodePage
;
104 InsertTailList(&CodePageListHead
, &AnsiCodePage
.Entry
);
106 /* Setup OEM code page. */
107 OemCodePage
.SectionHandle
= NULL
;
108 OemCodePage
.SectionMapping
= NtCurrentTeb()->ProcessEnvironmentBlock
->OemCodePageData
;
110 RtlInitCodePageTable((PUSHORT
)OemCodePage
.SectionMapping
,
111 &OemCodePage
.CodePageTable
);
112 OemCodePage
.CodePage
= OemCodePage
.CodePageTable
.CodePage
;
113 InsertTailList(&CodePageListHead
, &OemCodePage
.Entry
);
121 * Internal NLS related stuff uninitialization.
128 PCODEPAGE_ENTRY Current
;
130 /* Delete the code page list. */
131 while (!IsListEmpty(&CodePageListHead
))
133 Current
= CONTAINING_RECORD(CodePageListHead
.Flink
, CODEPAGE_ENTRY
, Entry
);
134 if (Current
->SectionHandle
!= NULL
)
136 UnmapViewOfFile(Current
->SectionMapping
);
137 NtClose(Current
->SectionHandle
);
139 RemoveHeadList(&CodePageListHead
);
141 RtlDeleteCriticalSection(&CodePageListLock
);
145 * @name IntGetLoadedCodePageEntry
147 * Internal function to get structure containing a code page information
148 * of code page that is already loaded.
151 * Number of the code page. Special values like CP_OEMCP, CP_ACP
152 * or CP_UTF8 aren't allowed.
154 * @return Code page entry or NULL if the specified code page hasn't
160 IntGetLoadedCodePageEntry(UINT CodePage
)
162 LIST_ENTRY
*CurrentEntry
;
163 PCODEPAGE_ENTRY Current
;
165 RtlEnterCriticalSection(&CodePageListLock
);
166 for (CurrentEntry
= CodePageListHead
.Flink
;
167 CurrentEntry
!= &CodePageListHead
;
168 CurrentEntry
= CurrentEntry
->Flink
)
170 Current
= CONTAINING_RECORD(CurrentEntry
, CODEPAGE_ENTRY
, Entry
);
171 if (Current
->CodePage
== CodePage
)
173 RtlLeaveCriticalSection(&CodePageListLock
);
177 RtlLeaveCriticalSection(&CodePageListLock
);
183 * @name IntGetCodePageEntry
185 * Internal function to get structure containing a code page information.
188 * Number of the code page. Special values like CP_OEMCP, CP_ACP
189 * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
191 * @return Code page entry.
196 IntGetCodePageEntry(UINT CodePage
)
198 CHAR SectionName
[40];
200 HANDLE SectionHandle
= INVALID_HANDLE_VALUE
, FileHandle
;
201 PBYTE SectionMapping
;
202 OBJECT_ATTRIBUTES ObjectAttributes
;
203 ANSI_STRING AnsiName
;
204 UNICODE_STRING UnicodeName
;
205 WCHAR FileName
[MAX_PATH
+ 1];
207 PCODEPAGE_ENTRY CodePageEntry
;
208 if (CodePage
== CP_ACP
)
210 return &AnsiCodePage
;
212 else if (CodePage
== CP_OEMCP
)
216 else if (CodePage
== CP_THREAD_ACP
)
218 if (!GetLocaleInfoW(GetThreadLocale(),
219 LOCALE_IDEFAULTANSICODEPAGE
| LOCALE_RETURN_NUMBER
,
221 sizeof(CodePage
) / sizeof(WCHAR
)))
223 /* Last error is set by GetLocaleInfoW. */
227 return &AnsiCodePage
;
229 else if (CodePage
== CP_MACCP
)
231 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT
,
232 LOCALE_IDEFAULTMACCODEPAGE
| LOCALE_RETURN_NUMBER
,
234 sizeof(CodePage
) / sizeof(WCHAR
)))
236 /* Last error is set by GetLocaleInfoW. */
241 /* Try searching for loaded page first. */
242 CodePageEntry
= IntGetLoadedCodePageEntry(CodePage
);
243 if (CodePageEntry
!= NULL
)
245 return CodePageEntry
;
249 * Yes, we really want to lock here. Otherwise it can happen that
250 * two parallel requests will try to get the entry for the same
251 * code page and we would load it twice.
253 RtlEnterCriticalSection(&CodePageListLock
);
255 /* Generate the section name. */
256 if (!GetNlsSectionName(CodePage
,
259 "\\Nls\\NlsSectionCP",
261 sizeof(SectionName
)))
263 RtlLeaveCriticalSection(&CodePageListLock
);
267 RtlInitAnsiString(&AnsiName
, SectionName
);
268 RtlAnsiStringToUnicodeString(&UnicodeName
, &AnsiName
, TRUE
);
270 InitializeObjectAttributes(&ObjectAttributes
, &UnicodeName
, 0, NULL
, NULL
);
272 /* Try to open the section first */
273 Status
= NtOpenSection(&SectionHandle
, SECTION_MAP_READ
, &ObjectAttributes
);
275 /* If the section doesn't exist, try to create it. */
276 if (Status
== STATUS_UNSUCCESSFUL
||
277 Status
== STATUS_OBJECT_NAME_NOT_FOUND
||
278 Status
== STATUS_OBJECT_PATH_NOT_FOUND
)
280 FileNamePos
= GetSystemDirectoryW(FileName
, MAX_PATH
);
281 if (GetCPFileNameFromRegistry(CodePage
,
282 FileName
+ FileNamePos
+ 1,
283 MAX_PATH
- FileNamePos
- 1))
285 FileName
[FileNamePos
] = L
'\\';
286 FileName
[MAX_PATH
] = 0;
287 FileHandle
= CreateFileW(FileName
,
295 Status
= NtCreateSection(&SectionHandle
,
303 /* HACK: Check if another process was faster
304 * and already created this section. See bug 3626 for details */
305 if (Status
== STATUS_OBJECT_NAME_COLLISION
)
307 /* Close the file then */
310 /* And open the section */
311 Status
= NtOpenSection(&SectionHandle
,
317 RtlFreeUnicodeString(&UnicodeName
);
319 if (!NT_SUCCESS(Status
))
321 RtlLeaveCriticalSection(&CodePageListLock
);
325 SectionMapping
= MapViewOfFile(SectionHandle
, FILE_MAP_READ
, 0, 0, 0);
326 if (SectionMapping
== NULL
)
328 NtClose(SectionHandle
);
329 RtlLeaveCriticalSection(&CodePageListLock
);
333 CodePageEntry
= HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY
));
334 if (CodePageEntry
== NULL
)
336 NtClose(SectionHandle
);
337 RtlLeaveCriticalSection(&CodePageListLock
);
341 CodePageEntry
->CodePage
= CodePage
;
342 CodePageEntry
->SectionHandle
= SectionHandle
;
343 CodePageEntry
->SectionMapping
= SectionMapping
;
345 RtlInitCodePageTable((PUSHORT
)SectionMapping
, &CodePageEntry
->CodePageTable
);
347 /* Insert the new entry to list and unlock. Uff. */
348 InsertTailList(&CodePageListHead
, &CodePageEntry
->Entry
);
349 RtlLeaveCriticalSection(&CodePageListLock
);
351 return CodePageEntry
;
355 * @name IntMultiByteToWideCharUTF8
357 * Internal version of MultiByteToWideChar for UTF8.
359 * @see MultiByteToWideChar
365 IntMultiByteToWideCharUTF8(DWORD Flags
,
366 LPCSTR MultiByteString
,
368 LPWSTR WideCharString
,
371 LPCSTR MbsEnd
, MbsPtrSave
;
372 UCHAR Char
, TrailLength
;
375 BOOL CharIsValid
, StringIsValid
= TRUE
;
376 const WCHAR InvalidChar
= 0xFFFD;
378 if (Flags
!= 0 && Flags
!= MB_ERR_INVALID_CHARS
)
380 SetLastError(ERROR_INVALID_FLAGS
);
384 /* Does caller query for output buffer size? */
385 if (WideCharCount
== 0)
387 /* validate and count the wide characters */
388 MbsEnd
= MultiByteString
+ MultiByteCount
;
389 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
391 Char
= *MultiByteString
++;
397 if (Char
>= 0xF8 || (Char
& 0xC0) == 0x80)
400 StringIsValid
= FALSE
;
405 MbsPtrSave
= MultiByteString
;
406 TrailLength
= UTF8Length
[Char
- 0x80];
407 WideChar
= Char
& UTF8Mask
[TrailLength
];
409 while (TrailLength
&& MultiByteString
< MbsEnd
)
411 if ((*MultiByteString
& 0xC0) != 0x80)
413 CharIsValid
= StringIsValid
= FALSE
;
417 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
421 if (!CharIsValid
|| WideChar
< UTF8LBound
[UTF8Length
[Char
- 0x80]])
423 MultiByteString
= MbsPtrSave
;
432 if (Flags
== MB_ERR_INVALID_CHARS
&& (!StringIsValid
|| TrailLength
))
434 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
438 return WideCharCount
;
442 MbsEnd
= MultiByteString
+ MultiByteCount
;
443 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
445 Char
= *MultiByteString
++;
448 *WideCharString
++ = Char
;
452 if (Char
>= 0xF8 || Char
== 0x80 || (Char
& 0xC0) == 0x80)
454 *WideCharString
++ = InvalidChar
;
460 MbsPtrSave
= MultiByteString
;
461 TrailLength
= UTF8Length
[Char
- 0x80];
462 WideChar
= Char
& UTF8Mask
[TrailLength
];
464 while (TrailLength
&& MultiByteString
< MbsEnd
)
466 if ((*MultiByteString
& 0xC0) != 0x80)
468 CharIsValid
= StringIsValid
= FALSE
;
472 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
476 if (CharIsValid
&& UTF8LBound
[UTF8Length
[Char
- 0x80]] <= WideChar
)
478 *WideCharString
++ = WideChar
;
482 *WideCharString
++ = InvalidChar
;
483 MultiByteString
= MbsPtrSave
;
487 if (TrailLength
&& Count
< WideCharCount
&& MultiByteString
< MbsEnd
)
489 *WideCharString
= InvalidChar
;
493 if (MultiByteString
< MbsEnd
)
495 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
499 if (Flags
== MB_ERR_INVALID_CHARS
&& (!StringIsValid
|| TrailLength
))
501 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
509 * @name IntMultiByteToWideCharCP
511 * Internal version of MultiByteToWideChar for code page tables.
513 * @see MultiByteToWideChar
514 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
521 IntMultiByteToWideCharCP(UINT CodePage
,
523 LPCSTR MultiByteString
,
525 LPWSTR WideCharString
,
528 PCODEPAGE_ENTRY CodePageEntry
;
529 PCPTABLEINFO CodePageTable
;
530 PUSHORT MultiByteTable
;
535 /* Get code page table. */
536 CodePageEntry
= IntGetCodePageEntry(CodePage
);
537 if (CodePageEntry
== NULL
)
539 SetLastError(ERROR_INVALID_PARAMETER
);
543 CodePageTable
= &CodePageEntry
->CodePageTable
;
545 /* If MB_USEGLYPHCHARS flag present and glyph table present */
546 if ((Flags
& MB_USEGLYPHCHARS
) && CodePageTable
->MultiByteTable
[256])
548 /* Use glyph table */
549 MultiByteTable
= CodePageTable
->MultiByteTable
+ 256 + 1;
553 MultiByteTable
= CodePageTable
->MultiByteTable
;
556 /* Different handling for DBCS code pages. */
557 if (CodePageTable
->DBCSCodePage
)
561 LPCSTR MbsEnd
= MultiByteString
+ MultiByteCount
;
564 if (Flags
& MB_ERR_INVALID_CHARS
)
566 TempString
= MultiByteString
;
568 while (TempString
< MbsEnd
)
570 DBCSOffset
= CodePageTable
->DBCSOffsets
[(UCHAR
)*TempString
];
574 /* If lead byte is presented, but behind it there is no symbol */
575 if (((TempString
+ 1) == MbsEnd
) || (*(TempString
+ 1) == 0))
577 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
581 WideChar
= CodePageTable
->DBCSOffsets
[DBCSOffset
+ *(TempString
+ 1)];
583 if (WideChar
== CodePageTable
->UniDefaultChar
&&
584 MAKEWORD(*(TempString
+ 1), *TempString
) != CodePageTable
->TransUniDefaultChar
)
586 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
594 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
596 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
597 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
598 /* "Private Use" characters */
599 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
601 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
610 /* Does caller query for output buffer size? */
611 if (WideCharCount
== 0)
613 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
615 Char
= *MultiByteString
++;
617 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
622 if (MultiByteString
< MbsEnd
)
626 return WideCharCount
;
629 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
631 Char
= *MultiByteString
++;
633 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
637 *WideCharString
++ = MultiByteTable
[Char
];
641 if (MultiByteString
== MbsEnd
)
643 *WideCharString
++ = MultiByteTable
[Char
];
645 else if (*MultiByteString
== 0)
647 *WideCharString
++ = UNICODE_NULL
;
652 *WideCharString
++ = CodePageTable
->DBCSOffsets
[DBCSOffset
+ (UCHAR
)*MultiByteString
++];
656 if (MultiByteString
< MbsEnd
)
658 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
664 else /* SBCS code page */
666 /* Check for invalid characters. */
667 if (Flags
& MB_ERR_INVALID_CHARS
)
669 for (TempString
= MultiByteString
, TempLength
= MultiByteCount
;
671 TempString
++, TempLength
--)
673 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
675 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
676 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
677 /* "Private Use" characters */
678 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
680 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
686 /* Does caller query for output buffer size? */
687 if (WideCharCount
== 0)
688 return MultiByteCount
;
690 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
691 for (TempLength
= (WideCharCount
< MultiByteCount
) ? WideCharCount
: MultiByteCount
;
693 MultiByteString
++, TempLength
--)
695 *WideCharString
++ = MultiByteTable
[(UCHAR
)*MultiByteString
];
698 /* Adjust buffer size. Wine trick ;-) */
699 if (WideCharCount
< MultiByteCount
)
701 MultiByteCount
= WideCharCount
;
702 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
705 return MultiByteCount
;
710 * @name IntMultiByteToWideCharSYMBOL
712 * Internal version of MultiByteToWideChar for SYMBOL.
714 * @see MultiByteToWideChar
720 IntMultiByteToWideCharSYMBOL(DWORD Flags
,
721 LPCSTR MultiByteString
,
723 LPWSTR WideCharString
,
733 SetLastError(ERROR_INVALID_FLAGS
);
737 if (WideCharCount
== 0)
739 return MultiByteCount
;
742 WideCharMaxLen
= WideCharCount
> MultiByteCount
? MultiByteCount
: WideCharCount
;
744 for (Count
= 0; Count
< WideCharMaxLen
; Count
++)
746 Char
= MultiByteString
[Count
];
749 WideCharString
[Count
] = Char
;
753 WideCharString
[Count
] = Char
+ 0xf000;
756 if (MultiByteCount
> WideCharMaxLen
)
758 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
762 return WideCharMaxLen
;
766 * @name IntWideCharToMultiByteSYMBOL
768 * Internal version of WideCharToMultiByte for SYMBOL.
770 * @see WideCharToMultiByte
775 IntWideCharToMultiByteSYMBOL(DWORD Flags
,
776 LPCWSTR WideCharString
,
778 LPSTR MultiByteString
,
787 SetLastError(ERROR_INVALID_PARAMETER
);
792 if (MultiByteCount
== 0)
794 return WideCharCount
;
797 MaxLen
= MultiByteCount
> WideCharCount
? WideCharCount
: MultiByteCount
;
798 for (Count
= 0; Count
< MaxLen
; Count
++)
800 Char
= WideCharString
[Count
];
803 MultiByteString
[Count
] = (CHAR
)Char
;
807 if ((Char
>= 0xf020) && (Char
< 0xf100))
809 MultiByteString
[Count
] = Char
- 0xf000;
813 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
819 if (WideCharCount
> MaxLen
)
821 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
828 * @name IntWideCharToMultiByteUTF8
830 * Internal version of WideCharToMultiByte for UTF8.
832 * @see WideCharToMultiByte
837 IntWideCharToMultiByteUTF8(UINT CodePage
,
839 LPCWSTR WideCharString
,
841 LPSTR MultiByteString
,
844 LPBOOL UsedDefaultChar
)
851 SetLastError(ERROR_INVALID_FLAGS
);
855 /* Does caller query for output buffer size? */
856 if (MultiByteCount
== 0)
858 for (TempLength
= 0; WideCharCount
;
859 WideCharCount
--, WideCharString
++)
862 if (*WideCharString
>= 0x80)
865 if (*WideCharString
>= 0x800)
868 if (*WideCharString
>= 0xd800 && *WideCharString
< 0xdc00 &&
869 WideCharCount
>= 1 &&
870 WideCharString
[1] >= 0xdc00 && WideCharString
[1] <= 0xe000)
882 for (TempLength
= MultiByteCount
; WideCharCount
; WideCharCount
--, WideCharString
++)
884 Char
= *WideCharString
;
889 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
893 *MultiByteString
++ = (CHAR
)Char
;
897 if (Char
< 0x800) /* 0x80-0x7ff: 2 bytes */
901 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
904 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
905 MultiByteString
[0] = 0xc0 | Char
;
906 MultiByteString
+= 2;
911 /* surrogate pair 0x10000-0x10ffff: 4 bytes */
912 if (Char
>= 0xd800 && Char
< 0xdc00 &&
913 WideCharCount
>= 1 &&
914 WideCharString
[1] >= 0xdc00 && WideCharString
[1] < 0xe000)
921 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
925 Char
= (Char
- 0xd800) << 10;
926 Char
|= *WideCharString
- 0xdc00;
927 ASSERT(Char
<= 0xfffff);
929 ASSERT(Char
<= 0x10ffff);
931 MultiByteString
[3] = 0x80 | (Char
& 0x3f); Char
>>= 6;
932 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
933 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
934 MultiByteString
[0] = 0xf0 | Char
;
935 MultiByteString
+= 4;
940 /* 0x800-0xffff: 3 bytes */
943 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
946 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
947 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
948 MultiByteString
[0] = 0xe0 | Char
;
949 MultiByteString
+= 3;
953 return MultiByteCount
- TempLength
;
957 * @name IsValidSBCSMapping
959 * Checks if ch (single-byte character) is a valid mapping for wch
961 * @see IntWideCharToMultiByteCP
966 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, UCHAR ch
)
968 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
969 if (Flags
& WC_NO_BEST_FIT_CHARS
)
970 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
972 /* By default, all characters except TransDefaultChar apply as a valid mapping
973 for ch (so also "nearest" characters) */
974 if (ch
!= CodePageTable
->TransDefaultChar
)
977 /* The only possible left valid mapping is the default character itself */
978 return (wch
== CodePageTable
->TransUniDefaultChar
);
982 * @name IsValidDBCSMapping
984 * Checks if ch (double-byte character) is a valid mapping for wch
986 * @see IntWideCharToMultiByteCP
989 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, USHORT ch
)
991 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
992 if (ch
== CodePageTable
->TransDefaultChar
&& wch
!= CodePageTable
->TransUniDefaultChar
)
995 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
996 if (Flags
& WC_NO_BEST_FIT_CHARS
)
1000 USHORT uOffset
= CodePageTable
->DBCSOffsets
[ch
>> 8];
1001 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1002 return (CodePageTable
->DBCSOffsets
[uOffset
+ (ch
& 0xff)] == wch
);
1005 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
1008 /* If we're still here, we have a valid mapping */
1013 * @name IntWideCharToMultiByteCP
1015 * Internal version of WideCharToMultiByte for code page tables.
1017 * @see WideCharToMultiByte
1018 * @todo Handle WC_COMPOSITECHECK
1023 IntWideCharToMultiByteCP(UINT CodePage
,
1025 LPCWSTR WideCharString
,
1027 LPSTR MultiByteString
,
1030 LPBOOL UsedDefaultChar
)
1032 PCODEPAGE_ENTRY CodePageEntry
;
1033 PCPTABLEINFO CodePageTable
;
1036 /* Get code page table. */
1037 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1038 if (CodePageEntry
== NULL
)
1040 SetLastError(ERROR_INVALID_PARAMETER
);
1044 CodePageTable
= &CodePageEntry
->CodePageTable
;
1047 /* Different handling for DBCS code pages. */
1048 if (CodePageTable
->DBCSCodePage
)
1050 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1051 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1053 BOOL TempUsedDefaultChar
;
1056 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1057 to check on every character */
1058 if (!UsedDefaultChar
)
1059 UsedDefaultChar
= &TempUsedDefaultChar
;
1061 *UsedDefaultChar
= FALSE
;
1063 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1065 DefChar
= DefaultChar
[1] ? ((DefaultChar
[0] << 8) | DefaultChar
[1]) : DefaultChar
[0];
1067 DefChar
= CodePageTable
->TransDefaultChar
;
1069 /* Does caller query for output buffer size? */
1070 if (!MultiByteCount
)
1072 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1076 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1078 /* FIXME: Handle WC_COMPOSITECHECK */
1079 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1082 uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1084 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1085 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1088 *UsedDefaultChar
= TRUE
;
1091 /* Increment TempLength again if this is a double-byte character */
1099 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1100 for (TempLength
= MultiByteCount
;
1101 WideCharCount
&& TempLength
;
1102 TempLength
--, WideCharString
++, WideCharCount
--)
1106 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1108 /* FIXME: Handle WC_COMPOSITECHECK */
1109 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1112 uChar
= ((PUSHORT
)CodePageTable
->WideCharTable
)[*WideCharString
];
1114 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1115 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1118 *UsedDefaultChar
= TRUE
;
1121 /* Handle double-byte characters */
1124 /* Don't output a partial character */
1125 if (TempLength
== 1)
1129 *MultiByteString
++ = uChar
>> 8;
1132 *MultiByteString
++ = (char)uChar
;
1135 /* WideCharCount should be 0 if all characters were converted */
1138 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1142 return MultiByteCount
- TempLength
;
1145 /* Does caller query for output buffer size? */
1146 if (!MultiByteCount
)
1148 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1150 /* Increment TempLength again if this is a double-byte character */
1151 if (((PWCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
] & 0xff00)
1158 /* Convert the WideCharString to the MultiByteString */
1159 for (TempLength
= MultiByteCount
;
1160 WideCharCount
&& TempLength
;
1161 TempLength
--, WideCharString
++, WideCharCount
--)
1163 USHORT uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1165 /* Is this a double-byte character? */
1168 /* Don't output a partial character */
1169 if (TempLength
== 1)
1173 *MultiByteString
++ = uChar
>> 8;
1176 *MultiByteString
++ = (char)uChar
;
1179 /* WideCharCount should be 0 if all characters were converted */
1182 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1186 return MultiByteCount
- TempLength
;
1188 else /* SBCS code page */
1192 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1193 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1195 BOOL TempUsedDefaultChar
;
1198 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1199 to check on every character */
1200 if (!UsedDefaultChar
)
1201 UsedDefaultChar
= &TempUsedDefaultChar
;
1203 *UsedDefaultChar
= FALSE
;
1205 /* Does caller query for output buffer size? */
1206 if (!MultiByteCount
)
1208 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1209 for (TempLength
= 0; WideCharCount
; TempLength
++, WideCharString
++, WideCharCount
--)
1211 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1213 /* FIXME: Handle WC_COMPOSITECHECK */
1214 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1217 if (!*UsedDefaultChar
)
1218 *UsedDefaultChar
= !IntIsValidSBCSMapping(CodePageTable
,
1221 ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
]);
1227 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1229 DefChar
= *DefaultChar
;
1231 DefChar
= (CHAR
)CodePageTable
->TransDefaultChar
;
1233 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1234 for (TempLength
= MultiByteCount
;
1235 WideCharCount
&& TempLength
;
1236 MultiByteString
++, TempLength
--, WideCharString
++, WideCharCount
--)
1238 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1240 /* FIXME: Handle WC_COMPOSITECHECK */
1241 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1244 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1246 if (!IntIsValidSBCSMapping(CodePageTable
, Flags
, *WideCharString
, *MultiByteString
))
1248 *MultiByteString
= DefChar
;
1249 *UsedDefaultChar
= TRUE
;
1253 /* WideCharCount should be 0 if all characters were converted */
1256 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1260 return MultiByteCount
- TempLength
;
1263 /* Does caller query for output buffer size? */
1264 if (!MultiByteCount
)
1265 return WideCharCount
;
1267 /* Is the buffer large enough? */
1268 if (MultiByteCount
< WideCharCount
)
1270 /* Convert the string up to MultiByteCount and return 0 */
1271 WideCharCount
= MultiByteCount
;
1272 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1277 /* Otherwise WideCharCount will be the number of converted characters */
1278 nReturn
= WideCharCount
;
1281 /* Convert the WideCharString to the MultiByteString */
1282 for (TempLength
= WideCharCount
; --TempLength
>= 0; WideCharString
++, MultiByteString
++)
1284 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1292 * @name IntIsLeadByte
1294 * Internal function to detect if byte is lead byte in specific character
1300 IntIsLeadByte(PCPTABLEINFO TableInfo
, BYTE Byte
)
1304 if (TableInfo
->MaximumCharacterSize
== 2)
1306 for (i
= 0; i
< MAXIMUM_LEADBYTES
&& TableInfo
->LeadByte
[i
]; i
+= 2)
1308 if (Byte
>= TableInfo
->LeadByte
[i
] && Byte
<= TableInfo
->LeadByte
[i
+1])
1316 /* PUBLIC FUNCTIONS ***********************************************************/
1319 * @name GetNlsSectionName
1321 * Construct a name of NLS section.
1326 * Integer base used for converting to string. Usually set to 10.
1328 * As the name suggests the meaning of this parameter is unknown.
1329 * The native version of Kernel32 passes it as the third parameter
1330 * to NlsConvertIntegerToString function, which is used for the
1331 * actual conversion of the code page number.
1333 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1335 * Buffer that will hold the constructed name.
1337 * Size of the buffer for the result.
1339 * @return TRUE if the buffer was large enough and was filled with
1340 * the requested information, FALSE otherwise.
1347 GetNlsSectionName(UINT CodePage
,
1356 if (!NT_SUCCESS(RtlIntegerToChar(CodePage
, Base
, sizeof(Integer
), Integer
)))
1360 * If the name including the terminating NULL character doesn't
1361 * fit in the output buffer then fail.
1363 if (strlen(Integer
) + strlen(BaseName
) >= ResultSize
)
1366 lstrcpyA(Result
, BaseName
);
1367 lstrcatA(Result
, Integer
);
1373 * @name GetCPFileNameFromRegistry
1375 * Get file name of code page definition file.
1378 * Code page number to get file name of.
1380 * Buffer that is filled with file name of successful return. Can
1382 * @param FileNameSize
1383 * Size of the buffer to hold file name in WCHARs.
1385 * @return TRUE if the file name was retrieved, FALSE otherwise.
1392 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
)
1394 WCHAR ValueNameBuffer
[11];
1395 UNICODE_STRING KeyName
, ValueName
;
1396 OBJECT_ATTRIBUTES ObjectAttributes
;
1399 PKEY_VALUE_PARTIAL_INFORMATION Kvpi
;
1405 /* Convert the codepage number to string. */
1406 ValueName
.Buffer
= ValueNameBuffer
;
1407 ValueName
.MaximumLength
= sizeof(ValueNameBuffer
);
1409 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage
, 10, &ValueName
)))
1412 /* Open the registry key containing file name mappings. */
1413 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System\\"
1414 L
"CurrentControlSet\\Control\\Nls\\CodePage");
1415 InitializeObjectAttributes(&ObjectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
,
1417 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1418 if (!NT_SUCCESS(Status
))
1423 /* Allocate buffer that will be used to query the value data. */
1424 KvpiSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + (MAX_PATH
* sizeof(WCHAR
));
1425 Kvpi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KvpiSize
);
1432 /* Query the file name for our code page. */
1433 Status
= NtQueryValueKey(KeyHandle
, &ValueName
, KeyValuePartialInformation
,
1434 Kvpi
, KvpiSize
, &KvpiSize
);
1438 /* Check if we succeded and the value is non-empty string. */
1439 if (NT_SUCCESS(Status
) && Kvpi
->Type
== REG_SZ
&&
1440 Kvpi
->DataLength
> sizeof(WCHAR
))
1443 if (FileName
!= NULL
)
1445 lstrcpynW(FileName
, (WCHAR
*)Kvpi
->Data
,
1446 min(Kvpi
->DataLength
/ sizeof(WCHAR
), FileNameSize
));
1450 /* free temporary buffer */
1451 HeapFree(GetProcessHeap(),0,Kvpi
);
1456 * @name IsValidCodePage
1458 * Detect if specified code page is valid and present in the system.
1461 * Code page number to query.
1463 * @return TRUE if code page is present.
1468 IsValidCodePage(UINT CodePage
)
1470 if (CodePage
== 0) return FALSE
;
1471 if (CodePage
== CP_UTF8
|| CodePage
== CP_UTF7
)
1473 if (IntGetLoadedCodePageEntry(CodePage
))
1475 return GetCPFileNameFromRegistry(CodePage
, NULL
, 0);
1478 static inline BOOL
utf7_write_w(WCHAR
*dst
, int dstlen
, int *index
, WCHAR character
)
1482 if (*index
>= dstlen
)
1485 dst
[*index
] = character
;
1493 static INT
Utf7ToWideChar(const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
1495 static const signed char base64_decoding_table
[] =
1497 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1498 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1499 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1500 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1501 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1502 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1503 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1504 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1507 const char *source_end
= src
+ srclen
;
1510 DWORD byte_pair
= 0;
1513 while (src
< source_end
)
1518 if (src
>= source_end
)
1523 /* just a plus sign escaped as +- */
1524 if (!utf7_write_w(dst
, dstlen
, &dest_index
, '+'))
1526 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1535 signed char sextet
= *src
;
1538 /* skip over the dash and end base64 decoding
1539 * the current, unfinished byte pair is discarded */
1546 /* the next character of src is < 0 and therefore not part of a base64 sequence
1547 * the current, unfinished byte pair is NOT discarded in this case
1548 * this is probably a bug in Windows */
1552 sextet
= base64_decoding_table
[sextet
];
1555 /* -1 means that the next character of src is not part of a base64 sequence
1556 * in other words, all sextets in this base64 sequence have been processed
1557 * the current, unfinished byte pair is discarded */
1562 byte_pair
= (byte_pair
<< 6) | sextet
;
1567 /* this byte pair is done */
1568 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (byte_pair
>> (offset
- 16)) & 0xFFFF))
1570 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1578 while (src
< source_end
);
1582 /* we have to convert to unsigned char in case *src < 0 */
1583 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (unsigned char)*src
))
1585 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1596 * @name MultiByteToWideChar
1598 * Convert a multi-byte string to wide-charater equivalent.
1601 * Code page to be used to perform the conversion. It can be also
1602 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1603 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1604 * for thread active code page, CP_UTF7 or CP_UTF8).
1606 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1607 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1608 * @param MultiByteString
1610 * @param MultiByteCount
1611 * Size of MultiByteString, or -1 if MultiByteString is NULL
1613 * @param WideCharString
1615 * @param WideCharCount
1616 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1617 * to know how large WideCharString should be for a successful
1620 * @return Zero on error, otherwise the number of WCHARs written
1621 * in the WideCharString buffer.
1628 MultiByteToWideChar(UINT CodePage
,
1630 LPCSTR MultiByteString
,
1632 LPWSTR WideCharString
,
1635 /* Check the parameters. */
1636 if (MultiByteString
== NULL
||
1637 MultiByteCount
== 0 || WideCharCount
< 0 ||
1638 (WideCharCount
&& (WideCharString
== NULL
||
1639 (PVOID
)MultiByteString
== (PVOID
)WideCharString
)))
1641 SetLastError(ERROR_INVALID_PARAMETER
);
1645 /* Determine the input string length. */
1646 if (MultiByteCount
< 0)
1648 MultiByteCount
= lstrlenA(MultiByteString
) + 1;
1654 return IntMultiByteToWideCharUTF8(Flags
,
1663 SetLastError(ERROR_INVALID_FLAGS
);
1666 return Utf7ToWideChar(MultiByteString
, MultiByteCount
,
1667 WideCharString
, WideCharCount
);
1670 return IntMultiByteToWideCharSYMBOL(Flags
,
1676 return IntMultiByteToWideCharCP(CodePage
,
1685 static inline BOOL
utf7_can_directly_encode(WCHAR codepoint
)
1687 static const BOOL directly_encodable_table
[] =
1689 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1690 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1691 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1692 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1693 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1694 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1695 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1696 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
1699 return codepoint
<= 0x7A ? directly_encodable_table
[codepoint
] : FALSE
;
1702 static inline BOOL
utf7_write_c(char *dst
, int dstlen
, int *index
, char character
)
1706 if (*index
>= dstlen
)
1709 dst
[*index
] = character
;
1717 static INT
WideCharToUtf7(const WCHAR
*src
, int srclen
, char *dst
, int dstlen
)
1719 static const char base64_encoding_table
[] =
1720 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1722 const WCHAR
*source_end
= src
+ srclen
;
1725 while (src
< source_end
)
1729 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1731 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1734 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1736 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1741 else if (utf7_can_directly_encode(*src
))
1743 if (!utf7_write_c(dst
, dstlen
, &dest_index
, *src
))
1745 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1752 unsigned int offset
= 0;
1753 DWORD byte_pair
= 0;
1755 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1757 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1761 while (src
< source_end
&& !utf7_can_directly_encode(*src
))
1763 byte_pair
= (byte_pair
<< 16) | *src
;
1767 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[(byte_pair
>> (offset
- 6)) & 0x3F]))
1769 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1779 /* Windows won't create a padded base64 character if there's no room for the - sign
1780 * as well ; this is probably a bug in Windows */
1781 if (dstlen
> 0 && dest_index
+ 1 >= dstlen
)
1783 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1787 byte_pair
<<= (6 - offset
);
1788 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[byte_pair
& 0x3F]))
1790 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1795 /* Windows always explicitly terminates the base64 sequence
1796 even though RFC 2152 (page 3, rule 2) does not require this */
1797 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1799 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1809 * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW
1810 * and GetGeoInfoW. It uses the current user localization, otherwise falls back
1811 * to English (US). Contrary to LoadStringW which always saves the loaded string
1812 * into the user-given buffer, truncating the string if needed, this function
1813 * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer
1814 * is not large enough.
1829 /* See HACK in winnls/lang/xx-XX.rc files */
1833 lcid
= GetUserDefaultLCID();
1834 lcid
= ConvertDefaultLocale(lcid
);
1836 langId
= LANGIDFROMLCID(lcid
);
1838 if (PRIMARYLANGID(langId
) == LANG_NEUTRAL
)
1839 langId
= MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
);
1841 hrsrc
= FindResourceExW(hCurrentModule
,
1843 MAKEINTRESOURCEW((uID
>> 4) + 1),
1846 /* English fallback */
1849 hrsrc
= FindResourceExW(hCurrentModule
,
1851 MAKEINTRESOURCEW((uID
>> 4) + 1),
1852 MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
));
1858 hmem
= LoadResource(hCurrentModule
, hrsrc
);
1862 p
= LockResource(hmem
);
1864 for (i
= 0; i
< (uID
& 0x0F); i
++)
1867 /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
1870 /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
1871 if (*p
+ 1 > cchDest
)
1873 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1880 memcpy(lpszDest
, p
+ 1, i
* sizeof(WCHAR
));
1881 lpszDest
[i
] = L
'\0';
1888 lpszDest
[0] = L
'\0';
1894 DPRINT1("Resource not found: uID = %lu\n", uID
);
1895 SetLastError(ERROR_INVALID_PARAMETER
);
1904 GetCPInfo(UINT CodePage
,
1905 LPCPINFO CodePageInfo
)
1907 PCODEPAGE_ENTRY CodePageEntry
;
1911 SetLastError(ERROR_INVALID_PARAMETER
);
1915 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1916 if (CodePageEntry
== NULL
)
1922 CodePageInfo
->DefaultChar
[0] = 0x3f;
1923 CodePageInfo
->DefaultChar
[1] = 0;
1924 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
1925 CodePageInfo
->MaxCharSize
= (CodePage
== CP_UTF7
) ? 5 : 4;
1929 DPRINT1("Invalid CP!: %lx\n", CodePage
);
1930 SetLastError( ERROR_INVALID_PARAMETER
);
1934 if (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00)
1936 CodePageInfo
->DefaultChar
[0] = (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00) >> 8;
1937 CodePageInfo
->DefaultChar
[1] = CodePageEntry
->CodePageTable
.DefaultChar
& 0x00ff;
1941 CodePageInfo
->DefaultChar
[0] = CodePageEntry
->CodePageTable
.DefaultChar
& 0xff;
1942 CodePageInfo
->DefaultChar
[1] = 0;
1945 if ((CodePageInfo
->MaxCharSize
= CodePageEntry
->CodePageTable
.MaximumCharacterSize
) == 2)
1946 memcpy(CodePageInfo
->LeadByte
, CodePageEntry
->CodePageTable
.LeadByte
, sizeof(CodePageInfo
->LeadByte
));
1948 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
1958 GetCPInfoExW(UINT CodePage
,
1960 LPCPINFOEXW lpCPInfoEx
)
1962 if (!GetCPInfo(CodePage
, (LPCPINFO
)lpCPInfoEx
))
1969 lpCPInfoEx
->CodePage
= CP_UTF7
;
1970 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
1971 return GetLocalisedText(lpCPInfoEx
->CodePage
,
1972 lpCPInfoEx
->CodePageName
,
1973 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
1979 lpCPInfoEx
->CodePage
= CP_UTF8
;
1980 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
1981 return GetLocalisedText(lpCPInfoEx
->CodePage
,
1982 lpCPInfoEx
->CodePageName
,
1983 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
1988 PCODEPAGE_ENTRY CodePageEntry
;
1990 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1991 if (CodePageEntry
== NULL
)
1993 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
1994 SetLastError(ERROR_INVALID_PARAMETER
);
1998 lpCPInfoEx
->CodePage
= CodePageEntry
->CodePageTable
.CodePage
;
1999 lpCPInfoEx
->UnicodeDefaultChar
= CodePageEntry
->CodePageTable
.UniDefaultChar
;
2000 return GetLocalisedText(lpCPInfoEx
->CodePage
,
2001 lpCPInfoEx
->CodePageName
,
2002 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
2014 GetCPInfoExA(UINT CodePage
,
2016 LPCPINFOEXA lpCPInfoEx
)
2020 if (!GetCPInfoExW(CodePage
, dwFlags
, &CPInfo
))
2023 /* the layout is the same except for CodePageName */
2024 memcpy(lpCPInfoEx
, &CPInfo
, sizeof(CPINFOEXA
));
2026 WideCharToMultiByte(CP_ACP
,
2028 CPInfo
.CodePageName
,
2030 lpCPInfoEx
->CodePageName
,
2031 sizeof(lpCPInfoEx
->CodePageName
),
2038 * @name WideCharToMultiByte
2040 * Convert a wide-charater string to closest multi-byte equivalent.
2043 * Code page to be used to perform the conversion. It can be also
2044 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
2045 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
2046 * for thread active code page, CP_UTF7 or CP_UTF8).
2048 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
2049 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
2050 * @param WideCharString
2051 * Points to the wide-character string to be converted.
2052 * @param WideCharCount
2053 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
2054 * know how large WideCharString should be for a successful conversion.
2055 * @param MultiByteString
2056 * Points to the buffer to receive the translated string.
2057 * @param MultiByteCount
2058 * Specifies the size in bytes of the buffer pointed to by the
2059 * MultiByteString parameter. If this value is zero, the function
2060 * returns the number of bytes required for the buffer.
2061 * @param DefaultChar
2062 * Points to the character used if a wide character cannot be
2063 * represented in the specified code page. If this parameter is
2064 * NULL, a system default value is used.
2065 * @param UsedDefaultChar
2066 * Points to a flag that indicates whether a default character was
2067 * used. This parameter can be NULL.
2069 * @return Zero on error, otherwise the number of bytes written in the
2070 * MultiByteString buffer. Or the number of bytes needed for
2071 * the MultiByteString buffer if MultiByteCount is zero.
2078 WideCharToMultiByte(UINT CodePage
,
2080 LPCWSTR WideCharString
,
2082 LPSTR MultiByteString
,
2085 LPBOOL UsedDefaultChar
)
2087 /* Check the parameters. */
2088 if (WideCharString
== NULL
||
2089 WideCharCount
== 0 ||
2090 (MultiByteString
== NULL
&& MultiByteCount
> 0) ||
2091 (PVOID
)WideCharString
== (PVOID
)MultiByteString
||
2094 SetLastError(ERROR_INVALID_PARAMETER
);
2098 /* Determine the input string length. */
2099 if (WideCharCount
< 0)
2101 WideCharCount
= lstrlenW(WideCharString
) + 1;
2107 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2109 SetLastError(ERROR_INVALID_PARAMETER
);
2112 return IntWideCharToMultiByteUTF8(CodePage
,
2122 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2124 SetLastError(ERROR_INVALID_PARAMETER
);
2129 SetLastError(ERROR_INVALID_FLAGS
);
2132 return WideCharToUtf7(WideCharString
, WideCharCount
,
2133 MultiByteString
, MultiByteCount
);
2136 if ((DefaultChar
!=NULL
) || (UsedDefaultChar
!=NULL
))
2138 SetLastError(ERROR_INVALID_PARAMETER
);
2141 return IntWideCharToMultiByteSYMBOL(Flags
,
2148 return IntWideCharToMultiByteCP(CodePage
,
2162 * Get active ANSI code page number.
2171 return AnsiCodePage
.CodePageTable
.CodePage
;
2177 * Get active OEM code page number.
2186 return OemCodePage
.CodePageTable
.CodePage
;
2190 * @name IsDBCSLeadByteEx
2192 * Determine if passed byte is lead byte in specified code page.
2199 IsDBCSLeadByteEx(UINT CodePage
, BYTE TestByte
)
2201 PCODEPAGE_ENTRY CodePageEntry
;
2203 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2204 if (CodePageEntry
!= NULL
)
2205 return IntIsLeadByte(&CodePageEntry
->CodePageTable
, TestByte
);
2207 SetLastError(ERROR_INVALID_PARAMETER
);
2212 * @name IsDBCSLeadByteEx
2214 * Determine if passed byte is lead byte in current ANSI code page.
2221 IsDBCSLeadByte(BYTE TestByte
)
2223 return IntIsLeadByte(&AnsiCodePage
.CodePageTable
, TestByte
);
2229 NTSTATUS WINAPI
CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor
,ULONG Size
,ULONG AccessMask
)
2238 BOOL WINAPI
IsValidUILanguage(LANGID langid
)
2247 VOID WINAPI
NlsConvertIntegerToString(ULONG Value
,ULONG Base
,ULONG strsize
, LPWSTR str
, ULONG strsize2
)
2255 UINT WINAPI
SetCPGlobal(UINT CodePage
)
2266 ValidateLCType(int a1
, unsigned int a2
, int a3
, int a4
)
2277 NlsResetProcessLocale(VOID
)
2288 GetDefaultSortkeySize(LPVOID lpUnknown
)
2299 GetLinguistLangSize(LPVOID lpUnknown
)
2310 ValidateLocale(IN ULONG LocaleId
)
2321 NlsGetCacheUpdateCount(VOID
)
2332 IsNLSDefinedString(IN NLS_FUNCTION Function
,
2334 IN LPNLSVERSIONINFO lpVersionInformation
,
2335 IN LPCWSTR lpString
,
2347 GetNLSVersion(IN NLS_FUNCTION Function
,
2349 IN OUT LPNLSVERSIONINFO lpVersionInformation
)
2360 GetNLSVersionEx(IN NLS_FUNCTION function
,
2361 IN LPCWSTR lpLocaleName
,
2362 IN OUT LPNLSVERSIONINFOEX lpVersionInformation
)