24a44e62d0122ae185be090e8427139d43c11109
[reactos.git] / reactos / dll / win32 / kernel32 / misc / nls.c
1 /* $Id$
2 *
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
8 * Hartmut Birr
9 * Gunnar Andre Dalsnes
10 * Thomas Weidenmueller
11 * UPDATE HISTORY:
12 * Created 24/08/2004
13 */
14
15 /* INCLUDES *******************************************************************/
16
17 #include <k32.h>
18 #define NDEBUG
19 #include <debug.h>
20
21 /* GLOBAL VARIABLES ***********************************************************/
22
23 /* Sequence length based on the first character. */
24 static const char UTF8Length[128] =
25 {
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 */
34 };
35
36 /* First byte mask depending on UTF-8 sequence length. */
37 static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
38
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;
44
45 /* FORWARD DECLARATIONS *******************************************************/
46
47 BOOL WINAPI
48 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
49 LPSTR BaseName, LPSTR Result, ULONG ResultSize);
50
51 BOOL WINAPI
52 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
53
54 /* PRIVATE FUNCTIONS **********************************************************/
55
56 /**
57 * @name NlsInit
58 *
59 * Internal NLS related stuff initialization.
60 */
61
62 BOOL
63 FASTCALL
64 NlsInit()
65 {
66 UNICODE_STRING DirName;
67 OBJECT_ATTRIBUTES ObjectAttributes;
68 HANDLE Handle;
69
70 InitializeListHead(&CodePageListHead);
71 RtlInitializeCriticalSection(&CodePageListLock);
72
73 /*
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.
77 */
78 RtlInitUnicodeString(&DirName, L"\\Nls");
79
80 InitializeObjectAttributes(&ObjectAttributes,
81 &DirName,
82 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
83 NULL,
84 NULL);
85
86 if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes)))
87 {
88 NtClose(Handle);
89 }
90
91 /* Setup ANSI code page. */
92 AnsiCodePage.CodePage = CP_ACP;
93 AnsiCodePage.SectionHandle = NULL;
94 AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
95
96 RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
97 &AnsiCodePage.CodePageTable);
98 InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
99
100 /* Setup OEM code page. */
101 OemCodePage.CodePage = CP_OEMCP;
102 OemCodePage.SectionHandle = NULL;
103 OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
104
105 RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
106 &OemCodePage.CodePageTable);
107 InsertTailList(&CodePageListHead, &OemCodePage.Entry);
108
109 return TRUE;
110 }
111
112 /**
113 * @name NlsUninit
114 *
115 * Internal NLS related stuff uninitialization.
116 */
117
118 VOID
119 FASTCALL
120 NlsUninit()
121 {
122 PCODEPAGE_ENTRY Current;
123
124 /* Delete the code page list. */
125 while (!IsListEmpty(&CodePageListHead))
126 {
127 Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
128 if (Current->SectionHandle != NULL)
129 {
130 UnmapViewOfFile(Current->SectionMapping);
131 NtClose(Current->SectionHandle);
132 }
133 RemoveHeadList(&CodePageListHead);
134 }
135 RtlDeleteCriticalSection(&CodePageListLock);
136 }
137
138 /**
139 * @name IntGetLoadedCodePageEntry
140 *
141 * Internal function to get structure containing a code page information
142 * of code page that is already loaded.
143 *
144 * @param CodePage
145 * Number of the code page. Special values like CP_OEMCP, CP_ACP
146 * or CP_UTF8 aren't allowed.
147 *
148 * @return Code page entry or NULL if the specified code page hasn't
149 * been loaded yet.
150 */
151
152 PCODEPAGE_ENTRY
153 FASTCALL
154 IntGetLoadedCodePageEntry(UINT CodePage)
155 {
156 LIST_ENTRY *CurrentEntry;
157 PCODEPAGE_ENTRY Current;
158
159 RtlEnterCriticalSection(&CodePageListLock);
160 for (CurrentEntry = CodePageListHead.Flink;
161 CurrentEntry != &CodePageListHead;
162 CurrentEntry = CurrentEntry->Flink)
163 {
164 Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
165 if (Current->CodePage == CodePage)
166 {
167 RtlLeaveCriticalSection(&CodePageListLock);
168 return Current;
169 }
170 }
171 RtlLeaveCriticalSection(&CodePageListLock);
172
173 return NULL;
174 }
175
176 /**
177 * @name IntGetCodePageEntry
178 *
179 * Internal function to get structure containing a code page information.
180 *
181 * @param CodePage
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.
184 *
185 * @return Code page entry.
186 */
187
188 PCODEPAGE_ENTRY
189 FASTCALL
190 IntGetCodePageEntry(UINT CodePage)
191 {
192 CHAR SectionName[40];
193 NTSTATUS Status;
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];
200 UINT FileNamePos;
201 PCODEPAGE_ENTRY CodePageEntry;
202
203 if (CodePage == CP_THREAD_ACP)
204 {
205 if (!GetLocaleInfoW(GetThreadLocale(),
206 LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
207 (WCHAR *)&CodePage,
208 sizeof(CodePage) / sizeof(WCHAR)))
209 {
210 /* Last error is set by GetLocaleInfoW. */
211 return NULL;
212 }
213 }
214 else if (CodePage == CP_MACCP)
215 {
216 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT,
217 LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
218 (WCHAR *)&CodePage,
219 sizeof(CodePage) / sizeof(WCHAR)))
220 {
221 /* Last error is set by GetLocaleInfoW. */
222 return NULL;
223 }
224 }
225
226 /* Try searching for loaded page first. */
227 CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
228 if (CodePageEntry != NULL)
229 {
230 return CodePageEntry;
231 }
232
233 /*
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.
237 */
238 RtlEnterCriticalSection(&CodePageListLock);
239
240 /* Generate the section name. */
241 if (!GetNlsSectionName(CodePage,
242 10,
243 0,
244 "\\Nls\\NlsSectionCP",
245 SectionName,
246 sizeof(SectionName)))
247 {
248 RtlLeaveCriticalSection(&CodePageListLock);
249 return NULL;
250 }
251
252 RtlInitAnsiString(&AnsiName, SectionName);
253 RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
254
255 InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL);
256
257 /* Try to open the section first */
258 Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
259
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)
264 {
265 FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
266 if (GetCPFileNameFromRegistry(CodePage,
267 FileName + FileNamePos + 1,
268 MAX_PATH - FileNamePos - 1))
269 {
270 FileName[FileNamePos] = L'\\';
271 FileName[MAX_PATH] = 0;
272 FileHandle = CreateFileW(FileName,
273 FILE_GENERIC_READ,
274 FILE_SHARE_READ,
275 NULL,
276 OPEN_EXISTING,
277 0,
278 NULL);
279
280 Status = NtCreateSection(&SectionHandle,
281 SECTION_MAP_READ,
282 &ObjectAttributes,
283 NULL,
284 PAGE_READONLY,
285 SEC_FILE,
286 FileHandle);
287
288 /* HACK: Check if another process was faster
289 * and already created this section. See bug 3626 for details */
290 if (Status == STATUS_OBJECT_NAME_COLLISION)
291 {
292 /* Close the file then */
293 NtClose(FileHandle);
294
295 /* And open the section */
296 Status = NtOpenSection(&SectionHandle,
297 SECTION_MAP_READ,
298 &ObjectAttributes);
299 }
300 }
301 }
302 RtlFreeUnicodeString(&UnicodeName);
303
304 if (!NT_SUCCESS(Status))
305 {
306 RtlLeaveCriticalSection(&CodePageListLock);
307 return NULL;
308 }
309
310 SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
311 if (SectionMapping == NULL)
312 {
313 NtClose(SectionHandle);
314 RtlLeaveCriticalSection(&CodePageListLock);
315 return NULL;
316 }
317
318 CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
319 if (CodePageEntry == NULL)
320 {
321 NtClose(SectionHandle);
322 RtlLeaveCriticalSection(&CodePageListLock);
323 return NULL;
324 }
325
326 CodePageEntry->CodePage = CodePage;
327 CodePageEntry->SectionHandle = SectionHandle;
328 CodePageEntry->SectionMapping = SectionMapping;
329
330 RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
331
332 /* Insert the new entry to list and unlock. Uff. */
333 InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
334 RtlLeaveCriticalSection(&CodePageListLock);
335
336 return CodePageEntry;
337 }
338
339 /**
340 * @name IntMultiByteToWideCharUTF8
341 *
342 * Internal version of MultiByteToWideChar for UTF8.
343 *
344 * @see MultiByteToWideChar
345 * @todo Add UTF8 validity checks.
346 */
347
348 static
349 INT
350 WINAPI
351 IntMultiByteToWideCharUTF8(DWORD Flags,
352 LPCSTR MultiByteString,
353 INT MultiByteCount,
354 LPWSTR WideCharString,
355 INT WideCharCount)
356 {
357 LPCSTR MbsEnd;
358 UCHAR Char, Length;
359 WCHAR WideChar;
360 LONG Count;
361
362 if (Flags != 0)
363 {
364 SetLastError(ERROR_INVALID_FLAGS);
365 return 0;
366 }
367
368 /* Does caller query for output buffer size? */
369 if (WideCharCount == 0)
370 {
371 MbsEnd = MultiByteString + MultiByteCount;
372 for (; MultiByteString < MbsEnd; WideCharCount++)
373 {
374 Char = *MultiByteString++;
375 if (Char < 0xC0)
376 continue;
377 MultiByteString += UTF8Length[Char - 0x80];
378 }
379 return WideCharCount;
380 }
381
382 MbsEnd = MultiByteString + MultiByteCount;
383 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
384 {
385 Char = *MultiByteString++;
386 if (Char < 0x80)
387 {
388 *WideCharString++ = Char;
389 continue;
390 }
391 Length = UTF8Length[Char - 0x80];
392 WideChar = Char & UTF8Mask[Length];
393 while (Length && MultiByteString < MbsEnd)
394 {
395 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
396 Length--;
397 }
398 *WideCharString++ = WideChar;
399 }
400
401 if (MultiByteString < MbsEnd)
402 SetLastError(ERROR_INSUFFICIENT_BUFFER);
403
404 return Count;
405 }
406
407 /**
408 * @name IntMultiByteToWideCharCP
409 *
410 * Internal version of MultiByteToWideChar for code page tables.
411 *
412 * @see MultiByteToWideChar
413 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
414 * DBCS codepages.
415 */
416
417 static
418 INT
419 WINAPI
420 IntMultiByteToWideCharCP(UINT CodePage,
421 DWORD Flags,
422 LPCSTR MultiByteString,
423 INT MultiByteCount,
424 LPWSTR WideCharString,
425 INT WideCharCount)
426 {
427 PCODEPAGE_ENTRY CodePageEntry;
428 PCPTABLEINFO CodePageTable;
429 LPCSTR TempString;
430 INT TempLength;
431
432 /* Get code page table. */
433 CodePageEntry = IntGetCodePageEntry(CodePage);
434 if (CodePageEntry == NULL)
435 {
436 SetLastError(ERROR_INVALID_PARAMETER);
437 return 0;
438 }
439 CodePageTable = &CodePageEntry->CodePageTable;
440
441 /* Different handling for DBCS code pages. */
442 if (CodePageTable->MaximumCharacterSize > 1)
443 {
444 /* FIXME */
445
446 UCHAR Char;
447 USHORT DBCSOffset;
448 LPCSTR MbsEnd = MultiByteString + MultiByteCount;
449 INT Count;
450
451 /* Does caller query for output buffer size? */
452 if (WideCharCount == 0)
453 {
454 for (; MultiByteString < MbsEnd; WideCharCount++)
455 {
456 Char = *MultiByteString++;
457
458 if (Char < 0x80)
459 continue;
460
461 DBCSOffset = CodePageTable->DBCSOffsets[Char];
462
463 if (!DBCSOffset)
464 continue;
465
466 if (MultiByteString < MbsEnd)
467 MultiByteString++;
468 }
469
470 return WideCharCount;
471 }
472
473 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
474 {
475 Char = *MultiByteString++;
476
477 if (Char < 0x80)
478 {
479 *WideCharString++ = Char;
480 continue;
481 }
482
483 DBCSOffset = CodePageTable->DBCSOffsets[Char];
484
485 if (!DBCSOffset)
486 {
487 *WideCharString++ = CodePageTable->MultiByteTable[Char];
488 continue;
489 }
490
491 if (MultiByteString < MbsEnd)
492 *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + *(PUCHAR)MultiByteString++];
493 }
494
495 if (MultiByteString < MbsEnd)
496 {
497 SetLastError(ERROR_INSUFFICIENT_BUFFER);
498 return 0;
499 }
500
501 return Count;
502 }
503 else /* Not DBCS code page */
504 {
505 /* Check for invalid characters. */
506 if (Flags & MB_ERR_INVALID_CHARS)
507 {
508 for (TempString = MultiByteString, TempLength = MultiByteCount;
509 TempLength > 0;
510 TempString++, TempLength--)
511 {
512 if (CodePageTable->MultiByteTable[(UCHAR)*TempString] ==
513 CodePageTable->UniDefaultChar &&
514 *TempString != CodePageEntry->CodePageTable.DefaultChar)
515 {
516 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
517 return 0;
518 }
519 }
520 }
521
522 /* Does caller query for output buffer size? */
523 if (WideCharCount == 0)
524 return MultiByteCount;
525
526 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
527 for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount;
528 TempLength > 0;
529 MultiByteString++, TempLength--)
530 {
531 *WideCharString++ = CodePageTable->MultiByteTable[(UCHAR)*MultiByteString];
532 }
533
534 /* Adjust buffer size. Wine trick ;-) */
535 if (WideCharCount < MultiByteCount)
536 {
537 MultiByteCount = WideCharCount;
538 SetLastError(ERROR_INSUFFICIENT_BUFFER);
539 return 0;
540 }
541 return MultiByteCount;
542 }
543 }
544
545 /**
546 * @name IntMultiByteToWideCharSYMBOL
547 *
548 * Internal version of MultiByteToWideChar for SYMBOL.
549 *
550 * @see MultiByteToWideChar
551 */
552
553 static
554 INT
555 WINAPI
556 IntMultiByteToWideCharSYMBOL(DWORD Flags,
557 LPCSTR MultiByteString,
558 INT MultiByteCount,
559 LPWSTR WideCharString,
560 INT WideCharCount)
561 {
562 LONG Count;
563 UCHAR Char;
564 INT WideCharMaxLen;
565
566
567 if (Flags != 0)
568 {
569 SetLastError(ERROR_INVALID_FLAGS);
570 return 0;
571 }
572
573 if (WideCharCount == 0)
574 {
575 return MultiByteCount;
576 }
577
578 WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount;
579
580 for (Count = 0; Count < WideCharMaxLen; Count++)
581 {
582 Char = MultiByteString[Count];
583 if ( Char < 0x20 )
584 {
585 WideCharString[Count] = Char;
586 }
587 else
588 {
589 WideCharString[Count] = Char + 0xf000;
590 }
591 }
592 if (MultiByteCount > WideCharMaxLen)
593 {
594 SetLastError(ERROR_INSUFFICIENT_BUFFER);
595 return 0;
596 }
597
598 return WideCharMaxLen;
599 }
600
601 /**
602 * @name IntWideCharToMultiByteSYMBOL
603 *
604 * Internal version of WideCharToMultiByte for SYMBOL.
605 *
606 * @see WideCharToMultiByte
607 */
608
609 static INT
610 WINAPI
611 IntWideCharToMultiByteSYMBOL(DWORD Flags,
612 LPCWSTR WideCharString,
613 INT WideCharCount,
614 LPSTR MultiByteString,
615 INT MultiByteCount)
616 {
617 LONG Count;
618 INT MaxLen;
619 WCHAR Char;
620
621 if (Flags!=0)
622 {
623 SetLastError(ERROR_INVALID_PARAMETER);
624 return 0;
625 }
626
627
628 if (MultiByteCount == 0)
629 {
630 return WideCharCount;
631 }
632
633 MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount;
634 for (Count = 0; Count < MaxLen; Count++)
635 {
636 Char = WideCharString[Count];
637 if (Char < 0x20)
638 {
639 MultiByteString[Count] = Char;
640 }
641 else
642 {
643 if ((Char>=0xf020)&&(Char<0xf100))
644 {
645 MultiByteString[Count] = Char - 0xf000;
646 }
647 else
648 {
649 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
650 return 0;
651 }
652 }
653 }
654 if (WideCharCount > MaxLen)
655 {
656 SetLastError(ERROR_INSUFFICIENT_BUFFER);
657 return 0;
658 }
659 return MaxLen;
660 }
661
662 /**
663 * @name IntWideCharToMultiByteUTF8
664 *
665 * Internal version of WideCharToMultiByte for UTF8.
666 *
667 * @see WideCharToMultiByte
668 */
669
670 static INT
671 WINAPI
672 IntWideCharToMultiByteUTF8(UINT CodePage,
673 DWORD Flags,
674 LPCWSTR WideCharString,
675 INT WideCharCount,
676 LPSTR MultiByteString,
677 INT MultiByteCount,
678 LPCSTR DefaultChar,
679 LPBOOL UsedDefaultChar)
680 {
681 INT TempLength;
682 WCHAR Char;
683
684 /* Does caller query for output buffer size? */
685 if (MultiByteCount == 0)
686 {
687 for (TempLength = 0; WideCharCount;
688 WideCharCount--, WideCharString++)
689 {
690 TempLength++;
691 if (*WideCharString >= 0x80)
692 {
693 TempLength++;
694 if (*WideCharString >= 0x800)
695 TempLength++;
696 }
697 }
698 return TempLength;
699 }
700
701 for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++)
702 {
703 Char = *WideCharString;
704 if (Char < 0x80)
705 {
706 if (!TempLength)
707 {
708 SetLastError(ERROR_INSUFFICIENT_BUFFER);
709 break;
710 }
711 TempLength--;
712 *MultiByteString++ = (CHAR)Char;
713 continue;
714 }
715
716 if (Char < 0x800) /* 0x80-0x7ff: 2 bytes */
717 {
718 if (TempLength < 2)
719 {
720 SetLastError(ERROR_INSUFFICIENT_BUFFER);
721 break;
722 }
723 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
724 MultiByteString[0] = 0xc0 | Char;
725 MultiByteString += 2;
726 TempLength -= 2;
727 continue;
728 }
729
730 /* 0x800-0xffff: 3 bytes */
731 if (TempLength < 3)
732 {
733 SetLastError(ERROR_INSUFFICIENT_BUFFER);
734 break;
735 }
736 MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
737 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
738 MultiByteString[0] = 0xe0 | Char;
739 MultiByteString += 3;
740 TempLength -= 3;
741 }
742
743 return MultiByteCount - TempLength;
744 }
745
746 /**
747 * @name IsValidSBCSMapping
748 *
749 * Checks if ch (single-byte character) is a valid mapping for wch
750 *
751 * @see IntWideCharToMultiByteCP
752 */
753 static
754 inline
755 BOOL
756 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch)
757 {
758 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
759 if (Flags & WC_NO_BEST_FIT_CHARS)
760 return (CodePageTable->MultiByteTable[ch] == wch);
761
762 /* By default, all characters except TransDefaultChar apply as a valid mapping
763 for ch (so also "nearest" characters) */
764 if (ch != CodePageTable->TransDefaultChar)
765 return TRUE;
766
767 /* The only possible left valid mapping is the default character itself */
768 return (wch == CodePageTable->TransUniDefaultChar);
769 }
770
771 /**
772 * @name IsValidDBCSMapping
773 *
774 * Checks if ch (double-byte character) is a valid mapping for wch
775 *
776 * @see IntWideCharToMultiByteCP
777 */
778 static inline BOOL
779 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch)
780 {
781 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
782 if (ch == CodePageTable->TransDefaultChar && wch != CodePageTable->TransUniDefaultChar)
783 return FALSE;
784
785 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
786 if (Flags & WC_NO_BEST_FIT_CHARS)
787 {
788 if(ch & 0xff00)
789 {
790 USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8];
791 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
792 return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch);
793 }
794
795 return (CodePageTable->MultiByteTable[ch] == wch);
796 }
797
798 /* If we're still here, we have a valid mapping */
799 return TRUE;
800 }
801
802 /**
803 * @name IntWideCharToMultiByteCP
804 *
805 * Internal version of WideCharToMultiByte for code page tables.
806 *
807 * @see WideCharToMultiByte
808 * @todo Handle WC_COMPOSITECHECK
809 */
810 static
811 INT
812 WINAPI
813 IntWideCharToMultiByteCP(UINT CodePage,
814 DWORD Flags,
815 LPCWSTR WideCharString,
816 INT WideCharCount,
817 LPSTR MultiByteString,
818 INT MultiByteCount,
819 LPCSTR DefaultChar,
820 LPBOOL UsedDefaultChar)
821 {
822 PCODEPAGE_ENTRY CodePageEntry;
823 PCPTABLEINFO CodePageTable;
824 INT TempLength;
825
826 /* Get code page table. */
827 CodePageEntry = IntGetCodePageEntry(CodePage);
828 if (CodePageEntry == NULL)
829 {
830 SetLastError(ERROR_INVALID_PARAMETER);
831 return 0;
832 }
833 CodePageTable = &CodePageEntry->CodePageTable;
834
835
836 /* Different handling for DBCS code pages. */
837 if (CodePageTable->MaximumCharacterSize > 1)
838 {
839 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
840 if(Flags || DefaultChar || UsedDefaultChar)
841 {
842 BOOL TempUsedDefaultChar;
843 USHORT DefChar;
844
845 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
846 to check on every character */
847 if(!UsedDefaultChar)
848 UsedDefaultChar = &TempUsedDefaultChar;
849
850 *UsedDefaultChar = FALSE;
851
852 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
853 if(DefaultChar)
854 DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0];
855 else
856 DefChar = CodePageTable->TransDefaultChar;
857
858 /* Does caller query for output buffer size? */
859 if(!MultiByteCount)
860 {
861 for(TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
862 {
863 USHORT uChar;
864
865 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
866 {
867 /* FIXME: Handle WC_COMPOSITECHECK */
868 }
869
870 uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
871
872 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
873 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
874 {
875 uChar = DefChar;
876 *UsedDefaultChar = TRUE;
877 }
878
879 /* Increment TempLength again if this is a double-byte character */
880 if (uChar & 0xff00)
881 TempLength++;
882 }
883
884 return TempLength;
885 }
886
887 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
888 for(TempLength = MultiByteCount;
889 WideCharCount && TempLength;
890 TempLength--, WideCharString++, WideCharCount--)
891 {
892 USHORT uChar;
893
894 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
895 {
896 /* FIXME: Handle WC_COMPOSITECHECK */
897 }
898
899 uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString];
900
901 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
902 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
903 {
904 uChar = DefChar;
905 *UsedDefaultChar = TRUE;
906 }
907
908 /* Handle double-byte characters */
909 if (uChar & 0xff00)
910 {
911 /* Don't output a partial character */
912 if (TempLength == 1)
913 break;
914
915 TempLength--;
916 *MultiByteString++ = uChar >> 8;
917 }
918
919 *MultiByteString++ = (char)uChar;
920 }
921
922 /* WideCharCount should be 0 if all characters were converted */
923 if (WideCharCount)
924 {
925 SetLastError(ERROR_INSUFFICIENT_BUFFER);
926 return 0;
927 }
928
929 return MultiByteCount - TempLength;
930 }
931
932 /* Does caller query for output buffer size? */
933 if (!MultiByteCount)
934 {
935 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
936 {
937 /* Increment TempLength again if this is a double-byte character */
938 if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00)
939 TempLength++;
940 }
941
942 return TempLength;
943 }
944
945 /* Convert the WideCharString to the MultiByteString */
946 for (TempLength = MultiByteCount;
947 WideCharCount && TempLength;
948 TempLength--, WideCharString++, WideCharCount--)
949 {
950 USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
951
952 /* Is this a double-byte character? */
953 if (uChar & 0xff00)
954 {
955 /* Don't output a partial character */
956 if (TempLength == 1)
957 break;
958
959 TempLength--;
960 *MultiByteString++ = uChar >> 8;
961 }
962
963 *MultiByteString++ = (char)uChar;
964 }
965
966 /* WideCharCount should be 0 if all characters were converted */
967 if (WideCharCount)
968 {
969 SetLastError(ERROR_INSUFFICIENT_BUFFER);
970 return 0;
971 }
972
973 return MultiByteCount - TempLength;
974 }
975 else /* Not DBCS code page */
976 {
977 INT nReturn;
978
979 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
980 if (Flags || DefaultChar || UsedDefaultChar)
981 {
982 BOOL TempUsedDefaultChar;
983 CHAR DefChar;
984
985 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
986 to check on every character */
987 if (!UsedDefaultChar)
988 UsedDefaultChar = &TempUsedDefaultChar;
989
990 *UsedDefaultChar = FALSE;
991
992 /* Does caller query for output buffer size? */
993 if (!MultiByteCount)
994 {
995 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
996 for (TempLength = 0; WideCharCount; TempLength++, WideCharString++, WideCharCount--)
997 {
998 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
999 {
1000 /* FIXME: Handle WC_COMPOSITECHECK */
1001 }
1002
1003 if (!*UsedDefaultChar)
1004 *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable,
1005 Flags,
1006 *WideCharString,
1007 ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]);
1008 }
1009
1010 return TempLength;
1011 }
1012
1013 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1014 if (DefaultChar)
1015 DefChar = *DefaultChar;
1016 else
1017 DefChar = CodePageTable->TransDefaultChar;
1018
1019 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1020 for (TempLength = MultiByteCount;
1021 WideCharCount && TempLength;
1022 MultiByteString++, TempLength--, WideCharString++, WideCharCount--)
1023 {
1024 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1025 {
1026 /* FIXME: Handle WC_COMPOSITECHECK */
1027 }
1028
1029 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1030
1031 if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString))
1032 {
1033 *MultiByteString = DefChar;
1034 *UsedDefaultChar = TRUE;
1035 }
1036 }
1037
1038 /* WideCharCount should be 0 if all characters were converted */
1039 if (WideCharCount)
1040 {
1041 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1042 return 0;
1043 }
1044
1045 return MultiByteCount - TempLength;
1046 }
1047
1048 /* Does caller query for output buffer size? */
1049 if (!MultiByteCount)
1050 return WideCharCount;
1051
1052 /* Is the buffer large enough? */
1053 if (MultiByteCount < WideCharCount)
1054 {
1055 /* Convert the string up to MultiByteCount and return 0 */
1056 WideCharCount = MultiByteCount;
1057 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1058 nReturn = 0;
1059 }
1060 else
1061 {
1062 /* Otherwise WideCharCount will be the number of converted characters */
1063 nReturn = WideCharCount;
1064 }
1065
1066 /* Convert the WideCharString to the MultiByteString */
1067 for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++)
1068 {
1069 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1070 }
1071
1072 return nReturn;
1073 }
1074 }
1075
1076 /**
1077 * @name IntIsLeadByte
1078 *
1079 * Internal function to detect if byte is lead byte in specific character
1080 * table.
1081 */
1082
1083 static BOOL
1084 WINAPI
1085 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
1086 {
1087 UINT i;
1088
1089 if (TableInfo->MaximumCharacterSize == 2)
1090 {
1091 for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2)
1092 {
1093 if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1])
1094 return TRUE;
1095 }
1096 }
1097
1098 return FALSE;
1099 }
1100
1101 /* PUBLIC FUNCTIONS ***********************************************************/
1102
1103 /**
1104 * @name GetNlsSectionName
1105 *
1106 * Construct a name of NLS section.
1107 *
1108 * @param CodePage
1109 * Code page number.
1110 * @param Base
1111 * Integer base used for converting to string. Usually set to 10.
1112 * @param Unknown
1113 * As the name suggests the meaning of this parameter is unknown.
1114 * The native version of Kernel32 passes it as the third parameter
1115 * to NlsConvertIntegerToString function, which is used for the
1116 * actual conversion of the code page number.
1117 * @param BaseName
1118 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1119 * @param Result
1120 * Buffer that will hold the constructed name.
1121 * @param ResultSize
1122 * Size of the buffer for the result.
1123 *
1124 * @return TRUE if the buffer was large enough and was filled with
1125 * the requested information, FALSE otherwise.
1126 *
1127 * @implemented
1128 */
1129
1130 BOOL
1131 WINAPI
1132 GetNlsSectionName(UINT CodePage,
1133 UINT Base,
1134 ULONG Unknown,
1135 LPSTR BaseName,
1136 LPSTR Result,
1137 ULONG ResultSize)
1138 {
1139 CHAR Integer[11];
1140
1141 if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
1142 return FALSE;
1143
1144 /*
1145 * If the name including the terminating NULL character doesn't
1146 * fit in the output buffer then fail.
1147 */
1148 if (strlen(Integer) + strlen(BaseName) >= ResultSize)
1149 return FALSE;
1150
1151 lstrcpyA(Result, BaseName);
1152 lstrcatA(Result, Integer);
1153
1154 return TRUE;
1155 }
1156
1157 /**
1158 * @name GetCPFileNameFromRegistry
1159 *
1160 * Get file name of code page definition file.
1161 *
1162 * @param CodePage
1163 * Code page number to get file name of.
1164 * @param FileName
1165 * Buffer that is filled with file name of successful return. Can
1166 * be set to NULL.
1167 * @param FileNameSize
1168 * Size of the buffer to hold file name in WCHARs.
1169 *
1170 * @return TRUE if the file name was retrieved, FALSE otherwise.
1171 *
1172 * @implemented
1173 */
1174
1175 BOOL
1176 WINAPI
1177 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
1178 {
1179 WCHAR ValueNameBuffer[11];
1180 UNICODE_STRING KeyName, ValueName;
1181 OBJECT_ATTRIBUTES ObjectAttributes;
1182 NTSTATUS Status;
1183 HANDLE KeyHandle;
1184 PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
1185 DWORD KvpiSize;
1186 BOOL bRetValue;
1187
1188 bRetValue = FALSE;
1189
1190 /* Convert the codepage number to string. */
1191 ValueName.Buffer = ValueNameBuffer;
1192 ValueName.MaximumLength = sizeof(ValueNameBuffer);
1193
1194 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
1195 return bRetValue;
1196
1197 /* Open the registry key containing file name mappings. */
1198 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
1199 L"CurrentControlSet\\Control\\Nls\\CodePage");
1200 InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
1201 NULL, NULL);
1202 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1203 if (!NT_SUCCESS(Status))
1204 {
1205 return bRetValue;
1206 }
1207
1208 /* Allocate buffer that will be used to query the value data. */
1209 KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (MAX_PATH * sizeof(WCHAR));
1210 Kvpi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KvpiSize);
1211 if (Kvpi == NULL)
1212 {
1213 NtClose(KeyHandle);
1214 return bRetValue;
1215 }
1216
1217 /* Query the file name for our code page. */
1218 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
1219 Kvpi, KvpiSize, &KvpiSize);
1220
1221 NtClose(KeyHandle);
1222
1223 /* Check if we succeded and the value is non-empty string. */
1224 if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
1225 Kvpi->DataLength > sizeof(WCHAR))
1226 {
1227 bRetValue = TRUE;
1228 if (FileName != NULL)
1229 {
1230 lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
1231 min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
1232 }
1233 }
1234
1235 /* free temporary buffer */
1236 HeapFree(GetProcessHeap(),0,Kvpi);
1237 return bRetValue;
1238 }
1239
1240 /**
1241 * @name IsValidCodePage
1242 *
1243 * Detect if specified code page is valid and present in the system.
1244 *
1245 * @param CodePage
1246 * Code page number to query.
1247 *
1248 * @return TRUE if code page is present.
1249 */
1250
1251 BOOL
1252 WINAPI
1253 IsValidCodePage(UINT CodePage)
1254 {
1255 if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
1256 return TRUE;
1257 if (IntGetLoadedCodePageEntry(CodePage))
1258 return TRUE;
1259 return GetCPFileNameFromRegistry(CodePage, NULL, 0);
1260 }
1261
1262 /**
1263 * @name MultiByteToWideChar
1264 *
1265 * Convert a multi-byte string to wide-charater equivalent.
1266 *
1267 * @param CodePage
1268 * Code page to be used to perform the conversion. It can be also
1269 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1270 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1271 * for thread active code page, CP_UTF7 or CP_UTF8).
1272 * @param Flags
1273 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1274 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1275 * @param MultiByteString
1276 * Input buffer.
1277 * @param MultiByteCount
1278 * Size of MultiByteString, or -1 if MultiByteString is NULL
1279 * terminated.
1280 * @param WideCharString
1281 * Output buffer.
1282 * @param WideCharCount
1283 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1284 * to know how large WideCharString should be for a successful
1285 * conversion.
1286 *
1287 * @return Zero on error, otherwise the number of WCHARs written
1288 * in the WideCharString buffer.
1289 *
1290 * @implemented
1291 */
1292
1293 INT
1294 WINAPI
1295 MultiByteToWideChar(UINT CodePage,
1296 DWORD Flags,
1297 LPCSTR MultiByteString,
1298 INT MultiByteCount,
1299 LPWSTR WideCharString,
1300 INT WideCharCount)
1301 {
1302 /* Check the parameters. */
1303 if (MultiByteString == NULL ||
1304 (WideCharString == NULL && WideCharCount > 0) ||
1305 (PVOID)MultiByteString == (PVOID)WideCharString)
1306 {
1307 SetLastError(ERROR_INVALID_PARAMETER);
1308 return 0;
1309 }
1310
1311 /* Determine the input string length. */
1312 if (MultiByteCount < 0)
1313 {
1314 MultiByteCount = lstrlenA(MultiByteString) + 1;
1315 }
1316
1317 switch (CodePage)
1318 {
1319 case CP_UTF8:
1320 return IntMultiByteToWideCharUTF8(Flags,
1321 MultiByteString,
1322 MultiByteCount,
1323 WideCharString,
1324 WideCharCount);
1325
1326 case CP_UTF7:
1327 DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n");
1328 return 0;
1329
1330 case CP_SYMBOL:
1331 return IntMultiByteToWideCharSYMBOL(Flags,
1332 MultiByteString,
1333 MultiByteCount,
1334 WideCharString,
1335 WideCharCount);
1336 default:
1337 return IntMultiByteToWideCharCP(CodePage,
1338 Flags,
1339 MultiByteString,
1340 MultiByteCount,
1341 WideCharString,
1342 WideCharCount);
1343 }
1344 }
1345
1346 /**
1347 * @name WideCharToMultiByte
1348 *
1349 * Convert a wide-charater string to closest multi-byte equivalent.
1350 *
1351 * @param CodePage
1352 * Code page to be used to perform the conversion. It can be also
1353 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1354 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1355 * for thread active code page, CP_UTF7 or CP_UTF8).
1356 * @param Flags
1357 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
1358 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
1359 * @param WideCharString
1360 * Points to the wide-character string to be converted.
1361 * @param WideCharCount
1362 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
1363 * know how large WideCharString should be for a successful conversion.
1364 * @param MultiByteString
1365 * Points to the buffer to receive the translated string.
1366 * @param MultiByteCount
1367 * Specifies the size in bytes of the buffer pointed to by the
1368 * MultiByteString parameter. If this value is zero, the function
1369 * returns the number of bytes required for the buffer.
1370 * @param DefaultChar
1371 * Points to the character used if a wide character cannot be
1372 * represented in the specified code page. If this parameter is
1373 * NULL, a system default value is used.
1374 * @param UsedDefaultChar
1375 * Points to a flag that indicates whether a default character was
1376 * used. This parameter can be NULL.
1377 *
1378 * @return Zero on error, otherwise the number of bytes written in the
1379 * MultiByteString buffer. Or the number of bytes needed for
1380 * the MultiByteString buffer if MultiByteCount is zero.
1381 *
1382 * @implemented
1383 */
1384
1385 INT
1386 WINAPI
1387 WideCharToMultiByte(UINT CodePage,
1388 DWORD Flags,
1389 LPCWSTR WideCharString,
1390 INT WideCharCount,
1391 LPSTR MultiByteString,
1392 INT MultiByteCount,
1393 LPCSTR DefaultChar,
1394 LPBOOL UsedDefaultChar)
1395 {
1396 /* Check the parameters. */
1397 if (WideCharString == NULL ||
1398 (MultiByteString == NULL && MultiByteCount > 0) ||
1399 (PVOID)WideCharString == (PVOID)MultiByteString ||
1400 MultiByteCount < 0)
1401 {
1402 SetLastError(ERROR_INVALID_PARAMETER);
1403 return 0;
1404 }
1405
1406 /* Determine the input string length. */
1407 if (WideCharCount < 0)
1408 {
1409 WideCharCount = lstrlenW(WideCharString) + 1;
1410 }
1411
1412 switch (CodePage)
1413 {
1414 case CP_UTF8:
1415 return IntWideCharToMultiByteUTF8(CodePage,
1416 Flags,
1417 WideCharString,
1418 WideCharCount,
1419 MultiByteString,
1420 MultiByteCount,
1421 DefaultChar,
1422 UsedDefaultChar);
1423
1424 case CP_UTF7:
1425 DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n");
1426 return 0;
1427
1428 case CP_SYMBOL:
1429 if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL))
1430 {
1431 SetLastError(ERROR_INVALID_PARAMETER);
1432 return 0;
1433 }
1434 return IntWideCharToMultiByteSYMBOL(Flags,
1435 WideCharString,
1436 WideCharCount,
1437 MultiByteString,
1438 MultiByteCount);
1439
1440 default:
1441 return IntWideCharToMultiByteCP(CodePage,
1442 Flags,
1443 WideCharString,
1444 WideCharCount,
1445 MultiByteString,
1446 MultiByteCount,
1447 DefaultChar,
1448 UsedDefaultChar);
1449 }
1450 }
1451
1452 /**
1453 * @name GetACP
1454 *
1455 * Get active ANSI code page number.
1456 *
1457 * @implemented
1458 */
1459
1460 UINT
1461 WINAPI
1462 GetACP(VOID)
1463 {
1464 return AnsiCodePage.CodePageTable.CodePage;
1465 }
1466
1467 /**
1468 * @name GetOEMCP
1469 *
1470 * Get active OEM code page number.
1471 *
1472 * @implemented
1473 */
1474
1475 UINT
1476 WINAPI
1477 GetOEMCP(VOID)
1478 {
1479 return OemCodePage.CodePageTable.CodePage;
1480 }
1481
1482 /**
1483 * @name IsDBCSLeadByteEx
1484 *
1485 * Determine if passed byte is lead byte in specified code page.
1486 *
1487 * @implemented
1488 */
1489
1490 BOOL
1491 WINAPI
1492 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
1493 {
1494 PCODEPAGE_ENTRY CodePageEntry;
1495
1496 CodePageEntry = IntGetCodePageEntry(CodePage);
1497 if (CodePageEntry != NULL)
1498 return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
1499
1500 SetLastError(ERROR_INVALID_PARAMETER);
1501 return FALSE;
1502 }
1503
1504 /**
1505 * @name IsDBCSLeadByteEx
1506 *
1507 * Determine if passed byte is lead byte in current ANSI code page.
1508 *
1509 * @implemented
1510 */
1511
1512 BOOL
1513 WINAPI
1514 IsDBCSLeadByte(BYTE TestByte)
1515 {
1516 return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
1517 }
1518
1519 /* EOF */