2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/winnls/string/nls.c
5 * PURPOSE: National Language Support
6 * PROGRAMMER: Filip Navara
10 * Katayama Hirofumi MZ
15 /* INCLUDES *******************************************************************/
22 /* GLOBAL VARIABLES ***********************************************************/
24 /* Sequence length based on the first character. */
25 static const char UTF8Length
[128] =
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
29 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
31 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
32 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
33 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
34 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xF0 - 0xFF */
37 /* First byte mask depending on UTF-8 sequence length. */
38 static const unsigned char UTF8Mask
[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
40 /* UTF-8 length to lower bound */
41 static const unsigned long UTF8LBound
[] =
42 {0, 0x80, 0x800, 0x10000, 0x200000, 0x2000000, 0xFFFFFFFF};
44 /* FIXME: Change to HASH table or linear array. */
45 static LIST_ENTRY CodePageListHead
;
46 static CODEPAGE_ENTRY AnsiCodePage
;
47 static CODEPAGE_ENTRY OemCodePage
;
48 static RTL_CRITICAL_SECTION CodePageListLock
;
50 /* FORWARD DECLARATIONS *******************************************************/
53 GetNlsSectionName(UINT CodePage
, UINT Base
, ULONG Unknown
,
54 LPSTR BaseName
, LPSTR Result
, ULONG ResultSize
);
57 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
);
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
& 0xC0) == 0x80)
400 StringIsValid
= FALSE
;
404 TrailLength
= UTF8Length
[Char
- 0x80];
405 if (TrailLength
== 0)
407 StringIsValid
= FALSE
;
412 MbsPtrSave
= MultiByteString
;
413 WideChar
= Char
& UTF8Mask
[TrailLength
];
415 while (TrailLength
&& MultiByteString
< MbsEnd
)
417 if ((*MultiByteString
& 0xC0) != 0x80)
419 CharIsValid
= StringIsValid
= FALSE
;
423 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
427 if (!CharIsValid
|| WideChar
< UTF8LBound
[UTF8Length
[Char
- 0x80]])
429 MultiByteString
= MbsPtrSave
;
436 StringIsValid
= FALSE
;
439 if (Flags
== MB_ERR_INVALID_CHARS
&& !StringIsValid
)
441 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
445 return WideCharCount
;
449 MbsEnd
= MultiByteString
+ MultiByteCount
;
450 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
452 Char
= *MultiByteString
++;
455 *WideCharString
++ = Char
;
459 if ((Char
& 0xC0) == 0x80)
461 *WideCharString
++ = InvalidChar
;
463 StringIsValid
= FALSE
;
467 TrailLength
= UTF8Length
[Char
- 0x80];
468 if (TrailLength
== 0)
470 *WideCharString
++ = InvalidChar
;
471 StringIsValid
= FALSE
;
476 MbsPtrSave
= MultiByteString
;
477 WideChar
= Char
& UTF8Mask
[TrailLength
];
479 while (TrailLength
&& MultiByteString
< MbsEnd
)
481 if ((*MultiByteString
& 0xC0) != 0x80)
483 CharIsValid
= StringIsValid
= FALSE
;
487 WideChar
= (WideChar
<< 6) | (*MultiByteString
++ & 0x7f);
491 if (CharIsValid
&& UTF8LBound
[UTF8Length
[Char
- 0x80]] <= WideChar
)
493 *WideCharString
++ = WideChar
;
497 *WideCharString
++ = InvalidChar
;
498 MultiByteString
= MbsPtrSave
;
499 StringIsValid
= FALSE
;
503 if (TrailLength
&& Count
< WideCharCount
&& MultiByteString
< MbsEnd
)
505 *WideCharString
= InvalidChar
;
509 if (MultiByteString
< MbsEnd
)
511 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
515 if (Flags
== MB_ERR_INVALID_CHARS
&& (!StringIsValid
|| TrailLength
))
517 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
525 * @name IntMultiByteToWideCharCP
527 * Internal version of MultiByteToWideChar for code page tables.
529 * @see MultiByteToWideChar
530 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
537 IntMultiByteToWideCharCP(UINT CodePage
,
539 LPCSTR MultiByteString
,
541 LPWSTR WideCharString
,
544 PCODEPAGE_ENTRY CodePageEntry
;
545 PCPTABLEINFO CodePageTable
;
546 PUSHORT MultiByteTable
;
551 /* Get code page table. */
552 CodePageEntry
= IntGetCodePageEntry(CodePage
);
553 if (CodePageEntry
== NULL
)
555 SetLastError(ERROR_INVALID_PARAMETER
);
559 CodePageTable
= &CodePageEntry
->CodePageTable
;
561 /* If MB_USEGLYPHCHARS flag present and glyph table present */
562 if ((Flags
& MB_USEGLYPHCHARS
) && CodePageTable
->MultiByteTable
[256])
564 /* Use glyph table */
565 MultiByteTable
= CodePageTable
->MultiByteTable
+ 256 + 1;
569 MultiByteTable
= CodePageTable
->MultiByteTable
;
572 /* Different handling for DBCS code pages. */
573 if (CodePageTable
->DBCSCodePage
)
577 LPCSTR MbsEnd
= MultiByteString
+ MultiByteCount
;
580 if (Flags
& MB_ERR_INVALID_CHARS
)
582 TempString
= MultiByteString
;
584 while (TempString
< MbsEnd
)
586 DBCSOffset
= CodePageTable
->DBCSOffsets
[(UCHAR
)*TempString
];
590 /* If lead byte is presented, but behind it there is no symbol */
591 if (((TempString
+ 1) == MbsEnd
) || (*(TempString
+ 1) == 0))
593 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
597 WideChar
= CodePageTable
->DBCSOffsets
[DBCSOffset
+ *(TempString
+ 1)];
599 if (WideChar
== CodePageTable
->UniDefaultChar
&&
600 MAKEWORD(*(TempString
+ 1), *TempString
) != CodePageTable
->TransUniDefaultChar
)
602 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
610 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
612 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
613 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
614 /* "Private Use" characters */
615 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
617 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
626 /* Does caller query for output buffer size? */
627 if (WideCharCount
== 0)
629 for (; MultiByteString
< MbsEnd
; WideCharCount
++)
631 Char
= *MultiByteString
++;
633 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
638 if (MultiByteString
< MbsEnd
)
642 return WideCharCount
;
645 for (Count
= 0; Count
< WideCharCount
&& MultiByteString
< MbsEnd
; Count
++)
647 Char
= *MultiByteString
++;
649 DBCSOffset
= CodePageTable
->DBCSOffsets
[Char
];
653 *WideCharString
++ = MultiByteTable
[Char
];
657 if (MultiByteString
== MbsEnd
|| *MultiByteString
== 0)
659 *WideCharString
++ = CodePageTable
->UniDefaultChar
;
663 *WideCharString
++ = CodePageTable
->DBCSOffsets
[DBCSOffset
+ (UCHAR
)*MultiByteString
++];
667 if (MultiByteString
< MbsEnd
)
669 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
675 else /* SBCS code page */
677 /* Check for invalid characters. */
678 if (Flags
& MB_ERR_INVALID_CHARS
)
680 for (TempString
= MultiByteString
, TempLength
= MultiByteCount
;
682 TempString
++, TempLength
--)
684 WideChar
= MultiByteTable
[(UCHAR
)*TempString
];
686 if ((WideChar
== CodePageTable
->UniDefaultChar
&&
687 *TempString
!= CodePageTable
->TransUniDefaultChar
) ||
688 /* "Private Use" characters */
689 (WideChar
>= 0xE000 && WideChar
<= 0xF8FF))
691 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
697 /* Does caller query for output buffer size? */
698 if (WideCharCount
== 0)
699 return MultiByteCount
;
701 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
702 for (TempLength
= (WideCharCount
< MultiByteCount
) ? WideCharCount
: MultiByteCount
;
704 MultiByteString
++, TempLength
--)
706 *WideCharString
++ = MultiByteTable
[(UCHAR
)*MultiByteString
];
709 /* Adjust buffer size. Wine trick ;-) */
710 if (WideCharCount
< MultiByteCount
)
712 MultiByteCount
= WideCharCount
;
713 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
716 return MultiByteCount
;
721 * @name IntMultiByteToWideCharSYMBOL
723 * Internal version of MultiByteToWideChar for SYMBOL.
725 * @see MultiByteToWideChar
731 IntMultiByteToWideCharSYMBOL(DWORD Flags
,
732 LPCSTR MultiByteString
,
734 LPWSTR WideCharString
,
744 SetLastError(ERROR_INVALID_FLAGS
);
748 if (WideCharCount
== 0)
750 return MultiByteCount
;
753 WideCharMaxLen
= WideCharCount
> MultiByteCount
? MultiByteCount
: WideCharCount
;
755 for (Count
= 0; Count
< WideCharMaxLen
; Count
++)
757 Char
= MultiByteString
[Count
];
760 WideCharString
[Count
] = Char
;
764 WideCharString
[Count
] = Char
+ 0xf000;
767 if (MultiByteCount
> WideCharMaxLen
)
769 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
773 return WideCharMaxLen
;
777 * @name IntWideCharToMultiByteSYMBOL
779 * Internal version of WideCharToMultiByte for SYMBOL.
781 * @see WideCharToMultiByte
786 IntWideCharToMultiByteSYMBOL(DWORD Flags
,
787 LPCWSTR WideCharString
,
789 LPSTR MultiByteString
,
798 SetLastError(ERROR_INVALID_PARAMETER
);
803 if (MultiByteCount
== 0)
805 return WideCharCount
;
808 MaxLen
= MultiByteCount
> WideCharCount
? WideCharCount
: MultiByteCount
;
809 for (Count
= 0; Count
< MaxLen
; Count
++)
811 Char
= WideCharString
[Count
];
814 MultiByteString
[Count
] = (CHAR
)Char
;
818 if ((Char
>= 0xf020) && (Char
< 0xf100))
820 MultiByteString
[Count
] = Char
- 0xf000;
824 SetLastError(ERROR_NO_UNICODE_TRANSLATION
);
830 if (WideCharCount
> MaxLen
)
832 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
839 * @name IntWideCharToMultiByteUTF8
841 * Internal version of WideCharToMultiByte for UTF8.
843 * @see WideCharToMultiByte
848 IntWideCharToMultiByteUTF8(UINT CodePage
,
850 LPCWSTR WideCharString
,
852 LPSTR MultiByteString
,
855 LPBOOL UsedDefaultChar
)
862 SetLastError(ERROR_INVALID_FLAGS
);
866 /* Does caller query for output buffer size? */
867 if (MultiByteCount
== 0)
869 for (TempLength
= 0; WideCharCount
;
870 WideCharCount
--, WideCharString
++)
873 if (*WideCharString
>= 0x80)
876 if (*WideCharString
>= 0x800)
879 if (*WideCharString
>= 0xd800 && *WideCharString
< 0xdc00 &&
880 WideCharCount
>= 1 &&
881 WideCharString
[1] >= 0xdc00 && WideCharString
[1] <= 0xe000)
893 for (TempLength
= MultiByteCount
; WideCharCount
; WideCharCount
--, WideCharString
++)
895 Char
= *WideCharString
;
900 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
904 *MultiByteString
++ = (CHAR
)Char
;
908 if (Char
< 0x800) /* 0x80-0x7ff: 2 bytes */
912 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
915 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
916 MultiByteString
[0] = 0xc0 | Char
;
917 MultiByteString
+= 2;
922 /* surrogate pair 0x10000-0x10ffff: 4 bytes */
923 if (Char
>= 0xd800 && Char
< 0xdc00 &&
924 WideCharCount
>= 1 &&
925 WideCharString
[1] >= 0xdc00 && WideCharString
[1] < 0xe000)
932 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
936 Char
= (Char
- 0xd800) << 10;
937 Char
|= *WideCharString
- 0xdc00;
938 ASSERT(Char
<= 0xfffff);
940 ASSERT(Char
<= 0x10ffff);
942 MultiByteString
[3] = 0x80 | (Char
& 0x3f); Char
>>= 6;
943 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
944 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
945 MultiByteString
[0] = 0xf0 | Char
;
946 MultiByteString
+= 4;
951 /* 0x800-0xffff: 3 bytes */
954 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
957 MultiByteString
[2] = 0x80 | (Char
& 0x3f); Char
>>= 6;
958 MultiByteString
[1] = 0x80 | (Char
& 0x3f); Char
>>= 6;
959 MultiByteString
[0] = 0xe0 | Char
;
960 MultiByteString
+= 3;
964 return MultiByteCount
- TempLength
;
968 * @name IsValidSBCSMapping
970 * Checks if ch (single-byte character) is a valid mapping for wch
972 * @see IntWideCharToMultiByteCP
977 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, UCHAR ch
)
979 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
980 if (Flags
& WC_NO_BEST_FIT_CHARS
)
981 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
983 /* By default, all characters except TransDefaultChar apply as a valid mapping
984 for ch (so also "nearest" characters) */
985 if (ch
!= CodePageTable
->TransDefaultChar
)
988 /* The only possible left valid mapping is the default character itself */
989 return (wch
== CodePageTable
->TransUniDefaultChar
);
993 * @name IsValidDBCSMapping
995 * Checks if ch (double-byte character) is a valid mapping for wch
997 * @see IntWideCharToMultiByteCP
1000 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable
, DWORD Flags
, WCHAR wch
, USHORT ch
)
1002 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
1003 if (ch
== CodePageTable
->TransDefaultChar
&& wch
!= CodePageTable
->TransUniDefaultChar
)
1006 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
1007 if (Flags
& WC_NO_BEST_FIT_CHARS
)
1011 USHORT uOffset
= CodePageTable
->DBCSOffsets
[ch
>> 8];
1012 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1013 return (CodePageTable
->DBCSOffsets
[uOffset
+ (ch
& 0xff)] == wch
);
1016 return (CodePageTable
->MultiByteTable
[ch
] == wch
);
1019 /* If we're still here, we have a valid mapping */
1024 * @name IntWideCharToMultiByteCP
1026 * Internal version of WideCharToMultiByte for code page tables.
1028 * @see WideCharToMultiByte
1029 * @todo Handle WC_COMPOSITECHECK
1034 IntWideCharToMultiByteCP(UINT CodePage
,
1036 LPCWSTR WideCharString
,
1038 LPSTR MultiByteString
,
1041 LPBOOL UsedDefaultChar
)
1043 PCODEPAGE_ENTRY CodePageEntry
;
1044 PCPTABLEINFO CodePageTable
;
1047 /* Get code page table. */
1048 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1049 if (CodePageEntry
== NULL
)
1051 SetLastError(ERROR_INVALID_PARAMETER
);
1055 CodePageTable
= &CodePageEntry
->CodePageTable
;
1058 /* Different handling for DBCS code pages. */
1059 if (CodePageTable
->DBCSCodePage
)
1061 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1062 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1064 BOOL TempUsedDefaultChar
;
1067 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1068 to check on every character */
1069 if (!UsedDefaultChar
)
1070 UsedDefaultChar
= &TempUsedDefaultChar
;
1072 *UsedDefaultChar
= FALSE
;
1074 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1076 DefChar
= DefaultChar
[1] ? ((DefaultChar
[0] << 8) | DefaultChar
[1]) : DefaultChar
[0];
1078 DefChar
= CodePageTable
->TransDefaultChar
;
1080 /* Does caller query for output buffer size? */
1081 if (!MultiByteCount
)
1083 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1087 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1089 /* FIXME: Handle WC_COMPOSITECHECK */
1090 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1093 uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1095 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1096 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1099 *UsedDefaultChar
= TRUE
;
1102 /* Increment TempLength again if this is a double-byte character */
1110 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1111 for (TempLength
= MultiByteCount
;
1112 WideCharCount
&& TempLength
;
1113 TempLength
--, WideCharString
++, WideCharCount
--)
1117 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1119 /* FIXME: Handle WC_COMPOSITECHECK */
1120 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1123 uChar
= ((PUSHORT
)CodePageTable
->WideCharTable
)[*WideCharString
];
1125 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1126 if (!IntIsValidDBCSMapping(CodePageTable
, Flags
, *WideCharString
, uChar
))
1129 *UsedDefaultChar
= TRUE
;
1132 /* Handle double-byte characters */
1135 /* Don't output a partial character */
1136 if (TempLength
== 1)
1140 *MultiByteString
++ = uChar
>> 8;
1143 *MultiByteString
++ = (char)uChar
;
1146 /* WideCharCount should be 0 if all characters were converted */
1149 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1153 return MultiByteCount
- TempLength
;
1156 /* Does caller query for output buffer size? */
1157 if (!MultiByteCount
)
1159 for (TempLength
= 0; WideCharCount
; WideCharCount
--, WideCharString
++, TempLength
++)
1161 /* Increment TempLength again if this is a double-byte character */
1162 if (((PWCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
] & 0xff00)
1169 /* Convert the WideCharString to the MultiByteString */
1170 for (TempLength
= MultiByteCount
;
1171 WideCharCount
&& TempLength
;
1172 TempLength
--, WideCharString
++, WideCharCount
--)
1174 USHORT uChar
= ((PUSHORT
) CodePageTable
->WideCharTable
)[*WideCharString
];
1176 /* Is this a double-byte character? */
1179 /* Don't output a partial character */
1180 if (TempLength
== 1)
1184 *MultiByteString
++ = uChar
>> 8;
1187 *MultiByteString
++ = (char)uChar
;
1190 /* WideCharCount should be 0 if all characters were converted */
1193 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1197 return MultiByteCount
- TempLength
;
1199 else /* SBCS code page */
1203 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1204 if (Flags
|| DefaultChar
|| UsedDefaultChar
)
1206 BOOL TempUsedDefaultChar
;
1209 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1210 to check on every character */
1211 if (!UsedDefaultChar
)
1212 UsedDefaultChar
= &TempUsedDefaultChar
;
1214 *UsedDefaultChar
= FALSE
;
1216 /* Does caller query for output buffer size? */
1217 if (!MultiByteCount
)
1219 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1220 for (TempLength
= 0; WideCharCount
; TempLength
++, WideCharString
++, WideCharCount
--)
1222 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1224 /* FIXME: Handle WC_COMPOSITECHECK */
1225 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1228 if (!*UsedDefaultChar
)
1229 *UsedDefaultChar
= !IntIsValidSBCSMapping(CodePageTable
,
1232 ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
]);
1238 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1240 DefChar
= *DefaultChar
;
1242 DefChar
= (CHAR
)CodePageTable
->TransDefaultChar
;
1244 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1245 for (TempLength
= MultiByteCount
;
1246 WideCharCount
&& TempLength
;
1247 MultiByteString
++, TempLength
--, WideCharString
++, WideCharCount
--)
1249 if ((Flags
& WC_COMPOSITECHECK
) && WideCharCount
> 1)
1251 /* FIXME: Handle WC_COMPOSITECHECK */
1252 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1255 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1257 if (!IntIsValidSBCSMapping(CodePageTable
, Flags
, *WideCharString
, *MultiByteString
))
1259 *MultiByteString
= DefChar
;
1260 *UsedDefaultChar
= TRUE
;
1264 /* WideCharCount should be 0 if all characters were converted */
1267 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1271 return MultiByteCount
- TempLength
;
1274 /* Does caller query for output buffer size? */
1275 if (!MultiByteCount
)
1276 return WideCharCount
;
1278 /* Is the buffer large enough? */
1279 if (MultiByteCount
< WideCharCount
)
1281 /* Convert the string up to MultiByteCount and return 0 */
1282 WideCharCount
= MultiByteCount
;
1283 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1288 /* Otherwise WideCharCount will be the number of converted characters */
1289 nReturn
= WideCharCount
;
1292 /* Convert the WideCharString to the MultiByteString */
1293 for (TempLength
= WideCharCount
; --TempLength
>= 0; WideCharString
++, MultiByteString
++)
1295 *MultiByteString
= ((PCHAR
)CodePageTable
->WideCharTable
)[*WideCharString
];
1303 * @name IntIsLeadByte
1305 * Internal function to detect if byte is lead byte in specific character
1311 IntIsLeadByte(PCPTABLEINFO TableInfo
, BYTE Byte
)
1315 if (TableInfo
->MaximumCharacterSize
== 2)
1317 for (i
= 0; i
< MAXIMUM_LEADBYTES
&& TableInfo
->LeadByte
[i
]; i
+= 2)
1319 if (Byte
>= TableInfo
->LeadByte
[i
] && Byte
<= TableInfo
->LeadByte
[i
+1])
1327 /* PUBLIC FUNCTIONS ***********************************************************/
1330 * @name GetNlsSectionName
1332 * Construct a name of NLS section.
1337 * Integer base used for converting to string. Usually set to 10.
1339 * As the name suggests the meaning of this parameter is unknown.
1340 * The native version of Kernel32 passes it as the third parameter
1341 * to NlsConvertIntegerToString function, which is used for the
1342 * actual conversion of the code page number.
1344 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1346 * Buffer that will hold the constructed name.
1348 * Size of the buffer for the result.
1350 * @return TRUE if the buffer was large enough and was filled with
1351 * the requested information, FALSE otherwise.
1358 GetNlsSectionName(UINT CodePage
,
1367 if (!NT_SUCCESS(RtlIntegerToChar(CodePage
, Base
, sizeof(Integer
), Integer
)))
1371 * If the name including the terminating NULL character doesn't
1372 * fit in the output buffer then fail.
1374 if (strlen(Integer
) + strlen(BaseName
) >= ResultSize
)
1377 lstrcpyA(Result
, BaseName
);
1378 lstrcatA(Result
, Integer
);
1384 * @name GetCPFileNameFromRegistry
1386 * Get file name of code page definition file.
1389 * Code page number to get file name of.
1391 * Buffer that is filled with file name of successful return. Can
1393 * @param FileNameSize
1394 * Size of the buffer to hold file name in WCHARs.
1396 * @return TRUE if the file name was retrieved, FALSE otherwise.
1403 GetCPFileNameFromRegistry(UINT CodePage
, LPWSTR FileName
, ULONG FileNameSize
)
1405 WCHAR ValueNameBuffer
[11];
1406 UNICODE_STRING KeyName
, ValueName
;
1407 OBJECT_ATTRIBUTES ObjectAttributes
;
1410 PKEY_VALUE_PARTIAL_INFORMATION Kvpi
;
1416 /* Convert the codepage number to string. */
1417 ValueName
.Buffer
= ValueNameBuffer
;
1418 ValueName
.MaximumLength
= sizeof(ValueNameBuffer
);
1420 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage
, 10, &ValueName
)))
1423 /* Open the registry key containing file name mappings. */
1424 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\System\\"
1425 L
"CurrentControlSet\\Control\\Nls\\CodePage");
1426 InitializeObjectAttributes(&ObjectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
,
1428 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
1429 if (!NT_SUCCESS(Status
))
1434 /* Allocate buffer that will be used to query the value data. */
1435 KvpiSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + (MAX_PATH
* sizeof(WCHAR
));
1436 Kvpi
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, KvpiSize
);
1443 /* Query the file name for our code page. */
1444 Status
= NtQueryValueKey(KeyHandle
, &ValueName
, KeyValuePartialInformation
,
1445 Kvpi
, KvpiSize
, &KvpiSize
);
1449 /* Check if we succeded and the value is non-empty string. */
1450 if (NT_SUCCESS(Status
) && Kvpi
->Type
== REG_SZ
&&
1451 Kvpi
->DataLength
> sizeof(WCHAR
))
1454 if (FileName
!= NULL
)
1456 lstrcpynW(FileName
, (WCHAR
*)Kvpi
->Data
,
1457 min(Kvpi
->DataLength
/ sizeof(WCHAR
), FileNameSize
));
1461 /* free temporary buffer */
1462 HeapFree(GetProcessHeap(),0,Kvpi
);
1467 * @name IsValidCodePage
1469 * Detect if specified code page is valid and present in the system.
1472 * Code page number to query.
1474 * @return TRUE if code page is present.
1479 IsValidCodePage(UINT CodePage
)
1481 if (CodePage
== 0) return FALSE
;
1482 if (CodePage
== CP_UTF8
|| CodePage
== CP_UTF7
)
1484 if (IntGetLoadedCodePageEntry(CodePage
))
1486 return GetCPFileNameFromRegistry(CodePage
, NULL
, 0);
1489 static inline BOOL
utf7_write_w(WCHAR
*dst
, int dstlen
, int *index
, WCHAR character
)
1493 if (*index
>= dstlen
)
1496 dst
[*index
] = character
;
1504 static INT
Utf7ToWideChar(const char *src
, int srclen
, WCHAR
*dst
, int dstlen
)
1506 static const signed char base64_decoding_table
[] =
1508 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1509 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1510 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1511 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1512 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1513 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1514 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1515 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1518 const char *source_end
= src
+ srclen
;
1521 DWORD byte_pair
= 0;
1524 while (src
< source_end
)
1529 if (src
>= source_end
)
1534 /* just a plus sign escaped as +- */
1535 if (!utf7_write_w(dst
, dstlen
, &dest_index
, '+'))
1537 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1546 signed char sextet
= *src
;
1549 /* skip over the dash and end base64 decoding
1550 * the current, unfinished byte pair is discarded */
1557 /* the next character of src is < 0 and therefore not part of a base64 sequence
1558 * the current, unfinished byte pair is NOT discarded in this case
1559 * this is probably a bug in Windows */
1563 sextet
= base64_decoding_table
[sextet
];
1566 /* -1 means that the next character of src is not part of a base64 sequence
1567 * in other words, all sextets in this base64 sequence have been processed
1568 * the current, unfinished byte pair is discarded */
1573 byte_pair
= (byte_pair
<< 6) | sextet
;
1578 /* this byte pair is done */
1579 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (byte_pair
>> (offset
- 16)) & 0xFFFF))
1581 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1589 while (src
< source_end
);
1593 /* we have to convert to unsigned char in case *src < 0 */
1594 if (!utf7_write_w(dst
, dstlen
, &dest_index
, (unsigned char)*src
))
1596 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1607 * @name MultiByteToWideChar
1609 * Convert a multi-byte string to wide-charater equivalent.
1612 * Code page to be used to perform the conversion. It can be also
1613 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1614 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1615 * for thread active code page, CP_UTF7 or CP_UTF8).
1617 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1618 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1619 * @param MultiByteString
1621 * @param MultiByteCount
1622 * Size of MultiByteString, or -1 if MultiByteString is NULL
1624 * @param WideCharString
1626 * @param WideCharCount
1627 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1628 * to know how large WideCharString should be for a successful
1631 * @return Zero on error, otherwise the number of WCHARs written
1632 * in the WideCharString buffer.
1639 MultiByteToWideChar(UINT CodePage
,
1641 LPCSTR MultiByteString
,
1643 LPWSTR WideCharString
,
1646 /* Check the parameters. */
1647 if (MultiByteString
== NULL
||
1648 MultiByteCount
== 0 || WideCharCount
< 0 ||
1649 (WideCharCount
&& (WideCharString
== NULL
||
1650 (PVOID
)MultiByteString
== (PVOID
)WideCharString
)))
1652 SetLastError(ERROR_INVALID_PARAMETER
);
1656 /* Determine the input string length. */
1657 if (MultiByteCount
< 0)
1659 MultiByteCount
= lstrlenA(MultiByteString
) + 1;
1665 return IntMultiByteToWideCharUTF8(Flags
,
1674 SetLastError(ERROR_INVALID_FLAGS
);
1677 return Utf7ToWideChar(MultiByteString
, MultiByteCount
,
1678 WideCharString
, WideCharCount
);
1681 return IntMultiByteToWideCharSYMBOL(Flags
,
1687 return IntMultiByteToWideCharCP(CodePage
,
1696 static inline BOOL
utf7_can_directly_encode(WCHAR codepoint
)
1698 static const BOOL directly_encodable_table
[] =
1700 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1701 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1702 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1703 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1704 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1705 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1706 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1707 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
1710 return codepoint
<= 0x7A ? directly_encodable_table
[codepoint
] : FALSE
;
1713 static inline BOOL
utf7_write_c(char *dst
, int dstlen
, int *index
, char character
)
1717 if (*index
>= dstlen
)
1720 dst
[*index
] = character
;
1728 static INT
WideCharToUtf7(const WCHAR
*src
, int srclen
, char *dst
, int dstlen
)
1730 static const char base64_encoding_table
[] =
1731 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1733 const WCHAR
*source_end
= src
+ srclen
;
1736 while (src
< source_end
)
1740 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1742 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1745 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1747 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1752 else if (utf7_can_directly_encode(*src
))
1754 if (!utf7_write_c(dst
, dstlen
, &dest_index
, *src
))
1756 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1763 unsigned int offset
= 0;
1764 DWORD byte_pair
= 0;
1766 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '+'))
1768 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1772 while (src
< source_end
&& !utf7_can_directly_encode(*src
))
1774 byte_pair
= (byte_pair
<< 16) | *src
;
1778 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[(byte_pair
>> (offset
- 6)) & 0x3F]))
1780 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1790 /* Windows won't create a padded base64 character if there's no room for the - sign
1791 * as well ; this is probably a bug in Windows */
1792 if (dstlen
> 0 && dest_index
+ 1 >= dstlen
)
1794 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1798 byte_pair
<<= (6 - offset
);
1799 if (!utf7_write_c(dst
, dstlen
, &dest_index
, base64_encoding_table
[byte_pair
& 0x3F]))
1801 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1806 /* Windows always explicitly terminates the base64 sequence
1807 even though RFC 2152 (page 3, rule 2) does not require this */
1808 if (!utf7_write_c(dst
, dstlen
, &dest_index
, '-'))
1810 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1820 * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW
1821 * and GetGeoInfoW. It uses the current user localization, otherwise falls back
1822 * to English (US). Contrary to LoadStringW which always saves the loaded string
1823 * into the user-given buffer, truncating the string if needed, this function
1824 * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer
1825 * is not large enough.
1840 /* See HACK in winnls/lang/xx-XX.rc files */
1844 lcid
= GetUserDefaultLCID();
1845 lcid
= ConvertDefaultLocale(lcid
);
1847 langId
= LANGIDFROMLCID(lcid
);
1849 if (PRIMARYLANGID(langId
) == LANG_NEUTRAL
)
1850 langId
= MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
);
1852 hrsrc
= FindResourceExW(hCurrentModule
,
1854 MAKEINTRESOURCEW((uID
>> 4) + 1),
1857 /* English fallback */
1860 hrsrc
= FindResourceExW(hCurrentModule
,
1862 MAKEINTRESOURCEW((uID
>> 4) + 1),
1863 MAKELANGID(LANG_ENGLISH
, SUBLANG_ENGLISH_US
));
1869 hmem
= LoadResource(hCurrentModule
, hrsrc
);
1873 p
= LockResource(hmem
);
1875 for (i
= 0; i
< (uID
& 0x0F); i
++)
1878 /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
1881 /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
1882 if (*p
+ 1 > cchDest
)
1884 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1891 memcpy(lpszDest
, p
+ 1, i
* sizeof(WCHAR
));
1892 lpszDest
[i
] = L
'\0';
1899 lpszDest
[0] = L
'\0';
1905 DPRINT1("Resource not found: uID = %lu\n", uID
);
1906 SetLastError(ERROR_INVALID_PARAMETER
);
1915 GetCPInfo(UINT CodePage
,
1916 LPCPINFO CodePageInfo
)
1918 PCODEPAGE_ENTRY CodePageEntry
;
1922 SetLastError(ERROR_INVALID_PARAMETER
);
1926 CodePageEntry
= IntGetCodePageEntry(CodePage
);
1927 if (CodePageEntry
== NULL
)
1933 CodePageInfo
->DefaultChar
[0] = 0x3f;
1934 CodePageInfo
->DefaultChar
[1] = 0;
1935 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
1936 CodePageInfo
->MaxCharSize
= (CodePage
== CP_UTF7
) ? 5 : 4;
1940 DPRINT1("Invalid CP!: %lx\n", CodePage
);
1941 SetLastError( ERROR_INVALID_PARAMETER
);
1945 if (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00)
1947 CodePageInfo
->DefaultChar
[0] = (CodePageEntry
->CodePageTable
.DefaultChar
& 0xff00) >> 8;
1948 CodePageInfo
->DefaultChar
[1] = CodePageEntry
->CodePageTable
.DefaultChar
& 0x00ff;
1952 CodePageInfo
->DefaultChar
[0] = CodePageEntry
->CodePageTable
.DefaultChar
& 0xff;
1953 CodePageInfo
->DefaultChar
[1] = 0;
1956 if ((CodePageInfo
->MaxCharSize
= CodePageEntry
->CodePageTable
.MaximumCharacterSize
) == 2)
1957 memcpy(CodePageInfo
->LeadByte
, CodePageEntry
->CodePageTable
.LeadByte
, sizeof(CodePageInfo
->LeadByte
));
1959 CodePageInfo
->LeadByte
[0] = CodePageInfo
->LeadByte
[1] = 0;
1969 GetCPInfoExW(UINT CodePage
,
1971 LPCPINFOEXW lpCPInfoEx
)
1973 if (!GetCPInfo(CodePage
, (LPCPINFO
)lpCPInfoEx
))
1980 lpCPInfoEx
->CodePage
= CP_UTF7
;
1981 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
1982 return GetLocalisedText(lpCPInfoEx
->CodePage
,
1983 lpCPInfoEx
->CodePageName
,
1984 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
1990 lpCPInfoEx
->CodePage
= CP_UTF8
;
1991 lpCPInfoEx
->UnicodeDefaultChar
= 0x3f;
1992 return GetLocalisedText(lpCPInfoEx
->CodePage
,
1993 lpCPInfoEx
->CodePageName
,
1994 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
1999 PCODEPAGE_ENTRY CodePageEntry
;
2001 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2002 if (CodePageEntry
== NULL
)
2004 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
2005 SetLastError(ERROR_INVALID_PARAMETER
);
2009 lpCPInfoEx
->CodePage
= CodePageEntry
->CodePageTable
.CodePage
;
2010 lpCPInfoEx
->UnicodeDefaultChar
= CodePageEntry
->CodePageTable
.UniDefaultChar
;
2011 return GetLocalisedText(lpCPInfoEx
->CodePage
,
2012 lpCPInfoEx
->CodePageName
,
2013 ARRAYSIZE(lpCPInfoEx
->CodePageName
)) != 0;
2025 GetCPInfoExA(UINT CodePage
,
2027 LPCPINFOEXA lpCPInfoEx
)
2031 if (!GetCPInfoExW(CodePage
, dwFlags
, &CPInfo
))
2034 /* the layout is the same except for CodePageName */
2035 memcpy(lpCPInfoEx
, &CPInfo
, sizeof(CPINFOEXA
));
2037 WideCharToMultiByte(CP_ACP
,
2039 CPInfo
.CodePageName
,
2041 lpCPInfoEx
->CodePageName
,
2042 sizeof(lpCPInfoEx
->CodePageName
),
2049 * @name WideCharToMultiByte
2051 * Convert a wide-charater string to closest multi-byte equivalent.
2054 * Code page to be used to perform the conversion. It can be also
2055 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
2056 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
2057 * for thread active code page, CP_UTF7 or CP_UTF8).
2059 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
2060 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
2061 * @param WideCharString
2062 * Points to the wide-character string to be converted.
2063 * @param WideCharCount
2064 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
2065 * know how large WideCharString should be for a successful conversion.
2066 * @param MultiByteString
2067 * Points to the buffer to receive the translated string.
2068 * @param MultiByteCount
2069 * Specifies the size in bytes of the buffer pointed to by the
2070 * MultiByteString parameter. If this value is zero, the function
2071 * returns the number of bytes required for the buffer.
2072 * @param DefaultChar
2073 * Points to the character used if a wide character cannot be
2074 * represented in the specified code page. If this parameter is
2075 * NULL, a system default value is used.
2076 * @param UsedDefaultChar
2077 * Points to a flag that indicates whether a default character was
2078 * used. This parameter can be NULL.
2080 * @return Zero on error, otherwise the number of bytes written in the
2081 * MultiByteString buffer. Or the number of bytes needed for
2082 * the MultiByteString buffer if MultiByteCount is zero.
2089 WideCharToMultiByte(UINT CodePage
,
2091 LPCWSTR WideCharString
,
2093 LPSTR MultiByteString
,
2096 LPBOOL UsedDefaultChar
)
2098 /* Check the parameters. */
2099 if (WideCharString
== NULL
||
2100 WideCharCount
== 0 ||
2101 (MultiByteString
== NULL
&& MultiByteCount
> 0) ||
2102 (PVOID
)WideCharString
== (PVOID
)MultiByteString
||
2105 SetLastError(ERROR_INVALID_PARAMETER
);
2109 /* Determine the input string length. */
2110 if (WideCharCount
< 0)
2112 WideCharCount
= lstrlenW(WideCharString
) + 1;
2118 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2120 SetLastError(ERROR_INVALID_PARAMETER
);
2123 return IntWideCharToMultiByteUTF8(CodePage
,
2133 if (DefaultChar
!= NULL
|| UsedDefaultChar
!= NULL
)
2135 SetLastError(ERROR_INVALID_PARAMETER
);
2140 SetLastError(ERROR_INVALID_FLAGS
);
2143 return WideCharToUtf7(WideCharString
, WideCharCount
,
2144 MultiByteString
, MultiByteCount
);
2147 if ((DefaultChar
!=NULL
) || (UsedDefaultChar
!=NULL
))
2149 SetLastError(ERROR_INVALID_PARAMETER
);
2152 return IntWideCharToMultiByteSYMBOL(Flags
,
2159 return IntWideCharToMultiByteCP(CodePage
,
2173 * Get active ANSI code page number.
2182 return AnsiCodePage
.CodePageTable
.CodePage
;
2188 * Get active OEM code page number.
2197 return OemCodePage
.CodePageTable
.CodePage
;
2201 * @name IsDBCSLeadByteEx
2203 * Determine if passed byte is lead byte in specified code page.
2210 IsDBCSLeadByteEx(UINT CodePage
, BYTE TestByte
)
2212 PCODEPAGE_ENTRY CodePageEntry
;
2214 CodePageEntry
= IntGetCodePageEntry(CodePage
);
2215 if (CodePageEntry
!= NULL
)
2216 return IntIsLeadByte(&CodePageEntry
->CodePageTable
, TestByte
);
2218 SetLastError(ERROR_INVALID_PARAMETER
);
2223 * @name IsDBCSLeadByteEx
2225 * Determine if passed byte is lead byte in current ANSI code page.
2232 IsDBCSLeadByte(BYTE TestByte
)
2234 return IntIsLeadByte(&AnsiCodePage
.CodePageTable
, TestByte
);
2240 NTSTATUS WINAPI
CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor
,ULONG Size
,ULONG AccessMask
)
2249 BOOL WINAPI
IsValidUILanguage(LANGID langid
)
2258 VOID WINAPI
NlsConvertIntegerToString(ULONG Value
,ULONG Base
,ULONG strsize
, LPWSTR str
, ULONG strsize2
)
2266 UINT WINAPI
SetCPGlobal(UINT CodePage
)
2277 ValidateLCType(int a1
, unsigned int a2
, int a3
, int a4
)
2288 NlsResetProcessLocale(VOID
)
2299 GetDefaultSortkeySize(LPVOID lpUnknown
)
2310 GetLinguistLangSize(LPVOID lpUnknown
)
2321 ValidateLocale(IN ULONG LocaleId
)
2332 NlsGetCacheUpdateCount(VOID
)
2343 IsNLSDefinedString(IN NLS_FUNCTION Function
,
2345 IN LPNLSVERSIONINFO lpVersionInformation
,
2346 IN LPCWSTR lpString
,
2358 GetNLSVersion(IN NLS_FUNCTION Function
,
2360 IN OUT LPNLSVERSIONINFO lpVersionInformation
)
2371 GetNLSVersionEx(IN NLS_FUNCTION function
,
2372 IN LPCWSTR lpLocaleName
,
2373 IN OUT LPNLSVERSIONINFOEX lpVersionInformation
)