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