[KERNEL32] Fix multibyte conversion in default char (#2111)
[reactos.git] / dll / win32 / kernel32 / winnls / string / nls.c
1 /*
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
7 * Hartmut Birr
8 * Gunnar Andre Dalsnes
9 * Thomas Weidenmueller
10 * Katayama Hirofumi MZ
11 * UPDATE HISTORY:
12 * Created 24/08/2004
13 */
14
15 /* INCLUDES *******************************************************************/
16
17 #include <k32.h>
18
19 #define NDEBUG
20 #include <debug.h>
21
22 /* GLOBAL VARIABLES ***********************************************************/
23
24 /* Sequence length based on the first character. */
25 static const char UTF8Length[128] =
26 {
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 */
35 };
36
37 /* First byte mask depending on UTF-8 sequence length. */
38 static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
39
40 /* UTF-8 length to lower bound */
41 static const unsigned long UTF8LBound[] =
42 {0, 0x80, 0x800, 0x10000, 0x200000, 0x2000000, 0xFFFFFFFF};
43
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;
49
50 /* FORWARD DECLARATIONS *******************************************************/
51
52 BOOL WINAPI
53 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
54 LPSTR BaseName, LPSTR Result, ULONG ResultSize);
55
56 BOOL WINAPI
57 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
58
59 /* PRIVATE FUNCTIONS **********************************************************/
60
61 /**
62 * @name NlsInit
63 *
64 * Internal NLS related stuff initialization.
65 */
66
67 BOOL
68 FASTCALL
69 NlsInit(VOID)
70 {
71 UNICODE_STRING DirName;
72 OBJECT_ATTRIBUTES ObjectAttributes;
73 HANDLE Handle;
74
75 InitializeListHead(&CodePageListHead);
76 RtlInitializeCriticalSection(&CodePageListLock);
77
78 /*
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.
82 */
83 RtlInitUnicodeString(&DirName, L"\\Nls");
84
85 InitializeObjectAttributes(&ObjectAttributes,
86 &DirName,
87 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
88 NULL,
89 NULL);
90
91 if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes)))
92 {
93 NtClose(Handle);
94 }
95
96 /* Setup ANSI code page. */
97 AnsiCodePage.SectionHandle = NULL;
98 AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
99
100 RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
101 &AnsiCodePage.CodePageTable);
102 AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage;
103
104 InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
105
106 /* Setup OEM code page. */
107 OemCodePage.SectionHandle = NULL;
108 OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
109
110 RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
111 &OemCodePage.CodePageTable);
112 OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage;
113 InsertTailList(&CodePageListHead, &OemCodePage.Entry);
114
115 return TRUE;
116 }
117
118 /**
119 * @name NlsUninit
120 *
121 * Internal NLS related stuff uninitialization.
122 */
123
124 VOID
125 FASTCALL
126 NlsUninit(VOID)
127 {
128 PCODEPAGE_ENTRY Current;
129
130 /* Delete the code page list. */
131 while (!IsListEmpty(&CodePageListHead))
132 {
133 Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
134 if (Current->SectionHandle != NULL)
135 {
136 UnmapViewOfFile(Current->SectionMapping);
137 NtClose(Current->SectionHandle);
138 }
139 RemoveHeadList(&CodePageListHead);
140 }
141 RtlDeleteCriticalSection(&CodePageListLock);
142 }
143
144 /**
145 * @name IntGetLoadedCodePageEntry
146 *
147 * Internal function to get structure containing a code page information
148 * of code page that is already loaded.
149 *
150 * @param CodePage
151 * Number of the code page. Special values like CP_OEMCP, CP_ACP
152 * or CP_UTF8 aren't allowed.
153 *
154 * @return Code page entry or NULL if the specified code page hasn't
155 * been loaded yet.
156 */
157
158 PCODEPAGE_ENTRY
159 FASTCALL
160 IntGetLoadedCodePageEntry(UINT CodePage)
161 {
162 LIST_ENTRY *CurrentEntry;
163 PCODEPAGE_ENTRY Current;
164
165 RtlEnterCriticalSection(&CodePageListLock);
166 for (CurrentEntry = CodePageListHead.Flink;
167 CurrentEntry != &CodePageListHead;
168 CurrentEntry = CurrentEntry->Flink)
169 {
170 Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
171 if (Current->CodePage == CodePage)
172 {
173 RtlLeaveCriticalSection(&CodePageListLock);
174 return Current;
175 }
176 }
177 RtlLeaveCriticalSection(&CodePageListLock);
178
179 return NULL;
180 }
181
182 /**
183 * @name IntGetCodePageEntry
184 *
185 * Internal function to get structure containing a code page information.
186 *
187 * @param CodePage
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.
190 *
191 * @return Code page entry.
192 */
193
194 PCODEPAGE_ENTRY
195 FASTCALL
196 IntGetCodePageEntry(UINT CodePage)
197 {
198 CHAR SectionName[40];
199 NTSTATUS Status;
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];
206 UINT FileNamePos;
207 PCODEPAGE_ENTRY CodePageEntry;
208 if (CodePage == CP_ACP)
209 {
210 return &AnsiCodePage;
211 }
212 else if (CodePage == CP_OEMCP)
213 {
214 return &OemCodePage;
215 }
216 else if (CodePage == CP_THREAD_ACP)
217 {
218 if (!GetLocaleInfoW(GetThreadLocale(),
219 LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
220 (WCHAR *)&CodePage,
221 sizeof(CodePage) / sizeof(WCHAR)))
222 {
223 /* Last error is set by GetLocaleInfoW. */
224 return NULL;
225 }
226 if (CodePage == 0)
227 return &AnsiCodePage;
228 }
229 else if (CodePage == CP_MACCP)
230 {
231 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT,
232 LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
233 (WCHAR *)&CodePage,
234 sizeof(CodePage) / sizeof(WCHAR)))
235 {
236 /* Last error is set by GetLocaleInfoW. */
237 return NULL;
238 }
239 }
240
241 /* Try searching for loaded page first. */
242 CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
243 if (CodePageEntry != NULL)
244 {
245 return CodePageEntry;
246 }
247
248 /*
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.
252 */
253 RtlEnterCriticalSection(&CodePageListLock);
254
255 /* Generate the section name. */
256 if (!GetNlsSectionName(CodePage,
257 10,
258 0,
259 "\\Nls\\NlsSectionCP",
260 SectionName,
261 sizeof(SectionName)))
262 {
263 RtlLeaveCriticalSection(&CodePageListLock);
264 return NULL;
265 }
266
267 RtlInitAnsiString(&AnsiName, SectionName);
268 RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
269
270 InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL);
271
272 /* Try to open the section first */
273 Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
274
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)
279 {
280 FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
281 if (GetCPFileNameFromRegistry(CodePage,
282 FileName + FileNamePos + 1,
283 MAX_PATH - FileNamePos - 1))
284 {
285 FileName[FileNamePos] = L'\\';
286 FileName[MAX_PATH] = 0;
287 FileHandle = CreateFileW(FileName,
288 FILE_GENERIC_READ,
289 FILE_SHARE_READ,
290 NULL,
291 OPEN_EXISTING,
292 0,
293 NULL);
294
295 Status = NtCreateSection(&SectionHandle,
296 SECTION_MAP_READ,
297 &ObjectAttributes,
298 NULL,
299 PAGE_READONLY,
300 SEC_COMMIT,
301 FileHandle);
302
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)
306 {
307 /* Close the file then */
308 NtClose(FileHandle);
309
310 /* And open the section */
311 Status = NtOpenSection(&SectionHandle,
312 SECTION_MAP_READ,
313 &ObjectAttributes);
314 }
315 }
316 }
317 RtlFreeUnicodeString(&UnicodeName);
318
319 if (!NT_SUCCESS(Status))
320 {
321 RtlLeaveCriticalSection(&CodePageListLock);
322 return NULL;
323 }
324
325 SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
326 if (SectionMapping == NULL)
327 {
328 NtClose(SectionHandle);
329 RtlLeaveCriticalSection(&CodePageListLock);
330 return NULL;
331 }
332
333 CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
334 if (CodePageEntry == NULL)
335 {
336 NtClose(SectionHandle);
337 RtlLeaveCriticalSection(&CodePageListLock);
338 return NULL;
339 }
340
341 CodePageEntry->CodePage = CodePage;
342 CodePageEntry->SectionHandle = SectionHandle;
343 CodePageEntry->SectionMapping = SectionMapping;
344
345 RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
346
347 /* Insert the new entry to list and unlock. Uff. */
348 InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
349 RtlLeaveCriticalSection(&CodePageListLock);
350
351 return CodePageEntry;
352 }
353
354 /**
355 * @name IntMultiByteToWideCharUTF8
356 *
357 * Internal version of MultiByteToWideChar for UTF8.
358 *
359 * @see MultiByteToWideChar
360 */
361
362 static
363 INT
364 WINAPI
365 IntMultiByteToWideCharUTF8(DWORD Flags,
366 LPCSTR MultiByteString,
367 INT MultiByteCount,
368 LPWSTR WideCharString,
369 INT WideCharCount)
370 {
371 LPCSTR MbsEnd, MbsPtrSave;
372 UCHAR Char, TrailLength;
373 WCHAR WideChar;
374 LONG Count;
375 BOOL CharIsValid, StringIsValid = TRUE;
376 const WCHAR InvalidChar = 0xFFFD;
377
378 if (Flags != 0 && Flags != MB_ERR_INVALID_CHARS)
379 {
380 SetLastError(ERROR_INVALID_FLAGS);
381 return 0;
382 }
383
384 /* Does caller query for output buffer size? */
385 if (WideCharCount == 0)
386 {
387 /* validate and count the wide characters */
388 MbsEnd = MultiByteString + MultiByteCount;
389 for (; MultiByteString < MbsEnd; WideCharCount++)
390 {
391 Char = *MultiByteString++;
392 if (Char < 0x80)
393 {
394 TrailLength = 0;
395 continue;
396 }
397 if ((Char & 0xC0) == 0x80)
398 {
399 TrailLength = 0;
400 StringIsValid = FALSE;
401 continue;
402 }
403
404 TrailLength = UTF8Length[Char - 0x80];
405 if (TrailLength == 0)
406 {
407 StringIsValid = FALSE;
408 continue;
409 }
410
411 CharIsValid = TRUE;
412 MbsPtrSave = MultiByteString;
413 WideChar = Char & UTF8Mask[TrailLength];
414
415 while (TrailLength && MultiByteString < MbsEnd)
416 {
417 if ((*MultiByteString & 0xC0) != 0x80)
418 {
419 CharIsValid = StringIsValid = FALSE;
420 break;
421 }
422
423 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
424 TrailLength--;
425 }
426
427 if (!CharIsValid || WideChar < UTF8LBound[UTF8Length[Char - 0x80]])
428 {
429 MultiByteString = MbsPtrSave;
430 }
431 }
432
433 if (TrailLength)
434 {
435 WideCharCount++;
436 StringIsValid = FALSE;
437 }
438
439 if (Flags == MB_ERR_INVALID_CHARS && !StringIsValid)
440 {
441 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
442 return 0;
443 }
444
445 return WideCharCount;
446 }
447
448 /* convert */
449 MbsEnd = MultiByteString + MultiByteCount;
450 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
451 {
452 Char = *MultiByteString++;
453 if (Char < 0x80)
454 {
455 *WideCharString++ = Char;
456 TrailLength = 0;
457 continue;
458 }
459 if ((Char & 0xC0) == 0x80)
460 {
461 *WideCharString++ = InvalidChar;
462 TrailLength = 0;
463 StringIsValid = FALSE;
464 continue;
465 }
466
467 TrailLength = UTF8Length[Char - 0x80];
468 if (TrailLength == 0)
469 {
470 *WideCharString++ = InvalidChar;
471 StringIsValid = FALSE;
472 continue;
473 }
474
475 CharIsValid = TRUE;
476 MbsPtrSave = MultiByteString;
477 WideChar = Char & UTF8Mask[TrailLength];
478
479 while (TrailLength && MultiByteString < MbsEnd)
480 {
481 if ((*MultiByteString & 0xC0) != 0x80)
482 {
483 CharIsValid = StringIsValid = FALSE;
484 break;
485 }
486
487 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
488 TrailLength--;
489 }
490
491 if (CharIsValid && UTF8LBound[UTF8Length[Char - 0x80]] <= WideChar)
492 {
493 *WideCharString++ = WideChar;
494 }
495 else
496 {
497 *WideCharString++ = InvalidChar;
498 MultiByteString = MbsPtrSave;
499 StringIsValid = FALSE;
500 }
501 }
502
503 if (TrailLength && Count < WideCharCount && MultiByteString < MbsEnd)
504 {
505 *WideCharString = InvalidChar;
506 WideCharCount++;
507 }
508
509 if (MultiByteString < MbsEnd)
510 {
511 SetLastError(ERROR_INSUFFICIENT_BUFFER);
512 return 0;
513 }
514
515 if (Flags == MB_ERR_INVALID_CHARS && (!StringIsValid || TrailLength))
516 {
517 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
518 return 0;
519 }
520
521 return Count;
522 }
523
524 /**
525 * @name IntMultiByteToWideCharCP
526 *
527 * Internal version of MultiByteToWideChar for code page tables.
528 *
529 * @see MultiByteToWideChar
530 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
531 * DBCS codepages.
532 */
533
534 static
535 INT
536 WINAPI
537 IntMultiByteToWideCharCP(UINT CodePage,
538 DWORD Flags,
539 LPCSTR MultiByteString,
540 INT MultiByteCount,
541 LPWSTR WideCharString,
542 INT WideCharCount)
543 {
544 PCODEPAGE_ENTRY CodePageEntry;
545 PCPTABLEINFO CodePageTable;
546 PUSHORT MultiByteTable;
547 LPCSTR TempString;
548 INT TempLength;
549 USHORT WideChar;
550
551 /* Get code page table. */
552 CodePageEntry = IntGetCodePageEntry(CodePage);
553 if (CodePageEntry == NULL)
554 {
555 SetLastError(ERROR_INVALID_PARAMETER);
556 return 0;
557 }
558
559 CodePageTable = &CodePageEntry->CodePageTable;
560
561 /* If MB_USEGLYPHCHARS flag present and glyph table present */
562 if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256])
563 {
564 /* Use glyph table */
565 MultiByteTable = CodePageTable->MultiByteTable + 256 + 1;
566 }
567 else
568 {
569 MultiByteTable = CodePageTable->MultiByteTable;
570 }
571
572 /* Different handling for DBCS code pages. */
573 if (CodePageTable->DBCSCodePage)
574 {
575 UCHAR Char;
576 USHORT DBCSOffset;
577 LPCSTR MbsEnd = MultiByteString + MultiByteCount;
578 INT Count;
579
580 if (Flags & MB_ERR_INVALID_CHARS)
581 {
582 TempString = MultiByteString;
583
584 while (TempString < MbsEnd)
585 {
586 DBCSOffset = CodePageTable->DBCSOffsets[(UCHAR)*TempString];
587
588 if (DBCSOffset)
589 {
590 /* If lead byte is presented, but behind it there is no symbol */
591 if (((TempString + 1) == MbsEnd) || (*(TempString + 1) == 0))
592 {
593 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
594 return 0;
595 }
596
597 WideChar = CodePageTable->DBCSOffsets[DBCSOffset + *(TempString + 1)];
598
599 if (WideChar == CodePageTable->UniDefaultChar &&
600 MAKEWORD(*(TempString + 1), *TempString) != CodePageTable->TransUniDefaultChar)
601 {
602 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
603 return 0;
604 }
605
606 TempString++;
607 }
608 else
609 {
610 WideChar = MultiByteTable[(UCHAR)*TempString];
611
612 if ((WideChar == CodePageTable->UniDefaultChar &&
613 *TempString != CodePageTable->TransUniDefaultChar) ||
614 /* "Private Use" characters */
615 (WideChar >= 0xE000 && WideChar <= 0xF8FF))
616 {
617 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
618 return 0;
619 }
620 }
621
622 TempString++;
623 }
624 }
625
626 /* Does caller query for output buffer size? */
627 if (WideCharCount == 0)
628 {
629 for (; MultiByteString < MbsEnd; WideCharCount++)
630 {
631 Char = *MultiByteString++;
632
633 DBCSOffset = CodePageTable->DBCSOffsets[Char];
634
635 if (!DBCSOffset)
636 continue;
637
638 if (MultiByteString < MbsEnd)
639 MultiByteString++;
640 }
641
642 return WideCharCount;
643 }
644
645 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
646 {
647 Char = *MultiByteString++;
648
649 DBCSOffset = CodePageTable->DBCSOffsets[Char];
650
651 if (!DBCSOffset)
652 {
653 *WideCharString++ = MultiByteTable[Char];
654 continue;
655 }
656
657 if (MultiByteString == MbsEnd || *MultiByteString == 0)
658 {
659 *WideCharString++ = CodePageTable->UniDefaultChar;
660 }
661 else
662 {
663 *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + (UCHAR)*MultiByteString++];
664 }
665 }
666
667 if (MultiByteString < MbsEnd)
668 {
669 SetLastError(ERROR_INSUFFICIENT_BUFFER);
670 return 0;
671 }
672
673 return Count;
674 }
675 else /* SBCS code page */
676 {
677 /* Check for invalid characters. */
678 if (Flags & MB_ERR_INVALID_CHARS)
679 {
680 for (TempString = MultiByteString, TempLength = MultiByteCount;
681 TempLength > 0;
682 TempString++, TempLength--)
683 {
684 WideChar = MultiByteTable[(UCHAR)*TempString];
685
686 if ((WideChar == CodePageTable->UniDefaultChar &&
687 *TempString != CodePageTable->TransUniDefaultChar) ||
688 /* "Private Use" characters */
689 (WideChar >= 0xE000 && WideChar <= 0xF8FF))
690 {
691 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
692 return 0;
693 }
694 }
695 }
696
697 /* Does caller query for output buffer size? */
698 if (WideCharCount == 0)
699 return MultiByteCount;
700
701 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
702 for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount;
703 TempLength > 0;
704 MultiByteString++, TempLength--)
705 {
706 *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString];
707 }
708
709 /* Adjust buffer size. Wine trick ;-) */
710 if (WideCharCount < MultiByteCount)
711 {
712 MultiByteCount = WideCharCount;
713 SetLastError(ERROR_INSUFFICIENT_BUFFER);
714 return 0;
715 }
716 return MultiByteCount;
717 }
718 }
719
720 /**
721 * @name IntMultiByteToWideCharSYMBOL
722 *
723 * Internal version of MultiByteToWideChar for SYMBOL.
724 *
725 * @see MultiByteToWideChar
726 */
727
728 static
729 INT
730 WINAPI
731 IntMultiByteToWideCharSYMBOL(DWORD Flags,
732 LPCSTR MultiByteString,
733 INT MultiByteCount,
734 LPWSTR WideCharString,
735 INT WideCharCount)
736 {
737 LONG Count;
738 UCHAR Char;
739 INT WideCharMaxLen;
740
741
742 if (Flags != 0)
743 {
744 SetLastError(ERROR_INVALID_FLAGS);
745 return 0;
746 }
747
748 if (WideCharCount == 0)
749 {
750 return MultiByteCount;
751 }
752
753 WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount;
754
755 for (Count = 0; Count < WideCharMaxLen; Count++)
756 {
757 Char = MultiByteString[Count];
758 if ( Char < 0x20 )
759 {
760 WideCharString[Count] = Char;
761 }
762 else
763 {
764 WideCharString[Count] = Char + 0xf000;
765 }
766 }
767 if (MultiByteCount > WideCharMaxLen)
768 {
769 SetLastError(ERROR_INSUFFICIENT_BUFFER);
770 return 0;
771 }
772
773 return WideCharMaxLen;
774 }
775
776 /**
777 * @name IntWideCharToMultiByteSYMBOL
778 *
779 * Internal version of WideCharToMultiByte for SYMBOL.
780 *
781 * @see WideCharToMultiByte
782 */
783
784 static INT
785 WINAPI
786 IntWideCharToMultiByteSYMBOL(DWORD Flags,
787 LPCWSTR WideCharString,
788 INT WideCharCount,
789 LPSTR MultiByteString,
790 INT MultiByteCount)
791 {
792 LONG Count;
793 INT MaxLen;
794 WCHAR Char;
795
796 if (Flags!=0)
797 {
798 SetLastError(ERROR_INVALID_PARAMETER);
799 return 0;
800 }
801
802
803 if (MultiByteCount == 0)
804 {
805 return WideCharCount;
806 }
807
808 MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount;
809 for (Count = 0; Count < MaxLen; Count++)
810 {
811 Char = WideCharString[Count];
812 if (Char < 0x20)
813 {
814 MultiByteString[Count] = (CHAR)Char;
815 }
816 else
817 {
818 if ((Char >= 0xf020) && (Char < 0xf100))
819 {
820 MultiByteString[Count] = Char - 0xf000;
821 }
822 else
823 {
824 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
825 return 0;
826 }
827 }
828 }
829
830 if (WideCharCount > MaxLen)
831 {
832 SetLastError(ERROR_INSUFFICIENT_BUFFER);
833 return 0;
834 }
835 return MaxLen;
836 }
837
838 /**
839 * @name IntWideCharToMultiByteUTF8
840 *
841 * Internal version of WideCharToMultiByte for UTF8.
842 *
843 * @see WideCharToMultiByte
844 */
845
846 static INT
847 WINAPI
848 IntWideCharToMultiByteUTF8(UINT CodePage,
849 DWORD Flags,
850 LPCWSTR WideCharString,
851 INT WideCharCount,
852 LPSTR MultiByteString,
853 INT MultiByteCount,
854 LPCSTR DefaultChar,
855 LPBOOL UsedDefaultChar)
856 {
857 INT TempLength;
858 DWORD Char;
859
860 if (Flags)
861 {
862 SetLastError(ERROR_INVALID_FLAGS);
863 return 0;
864 }
865
866 /* Does caller query for output buffer size? */
867 if (MultiByteCount == 0)
868 {
869 for (TempLength = 0; WideCharCount;
870 WideCharCount--, WideCharString++)
871 {
872 TempLength++;
873 if (*WideCharString >= 0x80)
874 {
875 TempLength++;
876 if (*WideCharString >= 0x800)
877 {
878 TempLength++;
879 if (*WideCharString >= 0xd800 && *WideCharString < 0xdc00 &&
880 WideCharCount >= 1 &&
881 WideCharString[1] >= 0xdc00 && WideCharString[1] <= 0xe000)
882 {
883 WideCharCount--;
884 WideCharString++;
885 TempLength++;
886 }
887 }
888 }
889 }
890 return TempLength;
891 }
892
893 for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++)
894 {
895 Char = *WideCharString;
896 if (Char < 0x80)
897 {
898 if (!TempLength)
899 {
900 SetLastError(ERROR_INSUFFICIENT_BUFFER);
901 break;
902 }
903 TempLength--;
904 *MultiByteString++ = (CHAR)Char;
905 continue;
906 }
907
908 if (Char < 0x800) /* 0x80-0x7ff: 2 bytes */
909 {
910 if (TempLength < 2)
911 {
912 SetLastError(ERROR_INSUFFICIENT_BUFFER);
913 break;
914 }
915 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
916 MultiByteString[0] = 0xc0 | Char;
917 MultiByteString += 2;
918 TempLength -= 2;
919 continue;
920 }
921
922 /* surrogate pair 0x10000-0x10ffff: 4 bytes */
923 if (Char >= 0xd800 && Char < 0xdc00 &&
924 WideCharCount >= 1 &&
925 WideCharString[1] >= 0xdc00 && WideCharString[1] < 0xe000)
926 {
927 WideCharCount--;
928 WideCharString++;
929
930 if (TempLength < 4)
931 {
932 SetLastError(ERROR_INSUFFICIENT_BUFFER);
933 break;
934 }
935
936 Char = (Char - 0xd800) << 10;
937 Char |= *WideCharString - 0xdc00;
938 ASSERT(Char <= 0xfffff);
939 Char += 0x10000;
940 ASSERT(Char <= 0x10ffff);
941
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;
947 TempLength -= 4;
948 continue;
949 }
950
951 /* 0x800-0xffff: 3 bytes */
952 if (TempLength < 3)
953 {
954 SetLastError(ERROR_INSUFFICIENT_BUFFER);
955 break;
956 }
957 MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
958 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
959 MultiByteString[0] = 0xe0 | Char;
960 MultiByteString += 3;
961 TempLength -= 3;
962 }
963
964 return MultiByteCount - TempLength;
965 }
966
967 /**
968 * @name IsValidSBCSMapping
969 *
970 * Checks if ch (single-byte character) is a valid mapping for wch
971 *
972 * @see IntWideCharToMultiByteCP
973 */
974 static
975 inline
976 BOOL
977 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch)
978 {
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);
982
983 /* By default, all characters except TransDefaultChar apply as a valid mapping
984 for ch (so also "nearest" characters) */
985 if (ch != CodePageTable->TransDefaultChar)
986 return TRUE;
987
988 /* The only possible left valid mapping is the default character itself */
989 return (wch == CodePageTable->TransUniDefaultChar);
990 }
991
992 /**
993 * @name IsValidDBCSMapping
994 *
995 * Checks if ch (double-byte character) is a valid mapping for wch
996 *
997 * @see IntWideCharToMultiByteCP
998 */
999 static inline BOOL
1000 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch)
1001 {
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)
1004 return FALSE;
1005
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)
1008 {
1009 if(ch & 0xff00)
1010 {
1011 USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8];
1012 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1013 return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch);
1014 }
1015
1016 return (CodePageTable->MultiByteTable[ch] == wch);
1017 }
1018
1019 /* If we're still here, we have a valid mapping */
1020 return TRUE;
1021 }
1022
1023 /**
1024 * @name IntWideCharToMultiByteCP
1025 *
1026 * Internal version of WideCharToMultiByte for code page tables.
1027 *
1028 * @see WideCharToMultiByte
1029 * @todo Handle WC_COMPOSITECHECK
1030 */
1031 static
1032 INT
1033 WINAPI
1034 IntWideCharToMultiByteCP(UINT CodePage,
1035 DWORD Flags,
1036 LPCWSTR WideCharString,
1037 INT WideCharCount,
1038 LPSTR MultiByteString,
1039 INT MultiByteCount,
1040 LPCSTR DefaultChar,
1041 LPBOOL UsedDefaultChar)
1042 {
1043 PCODEPAGE_ENTRY CodePageEntry;
1044 PCPTABLEINFO CodePageTable;
1045 INT TempLength;
1046
1047 /* Get code page table. */
1048 CodePageEntry = IntGetCodePageEntry(CodePage);
1049 if (CodePageEntry == NULL)
1050 {
1051 SetLastError(ERROR_INVALID_PARAMETER);
1052 return 0;
1053 }
1054
1055 CodePageTable = &CodePageEntry->CodePageTable;
1056
1057
1058 /* Different handling for DBCS code pages. */
1059 if (CodePageTable->DBCSCodePage)
1060 {
1061 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1062 if (Flags || DefaultChar || UsedDefaultChar)
1063 {
1064 BOOL TempUsedDefaultChar;
1065 USHORT DefChar;
1066
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;
1071
1072 *UsedDefaultChar = FALSE;
1073
1074 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1075 if (DefaultChar)
1076 DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0];
1077 else
1078 DefChar = CodePageTable->TransDefaultChar;
1079
1080 /* Does caller query for output buffer size? */
1081 if (!MultiByteCount)
1082 {
1083 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1084 {
1085 USHORT uChar;
1086
1087 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1088 {
1089 /* FIXME: Handle WC_COMPOSITECHECK */
1090 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1091 }
1092
1093 uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1094
1095 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1096 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1097 {
1098 uChar = DefChar;
1099 *UsedDefaultChar = TRUE;
1100 }
1101
1102 /* Increment TempLength again if this is a double-byte character */
1103 if (uChar & 0xff00)
1104 TempLength++;
1105 }
1106
1107 return TempLength;
1108 }
1109
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--)
1114 {
1115 USHORT uChar;
1116
1117 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1118 {
1119 /* FIXME: Handle WC_COMPOSITECHECK */
1120 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1121 }
1122
1123 uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString];
1124
1125 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1126 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1127 {
1128 uChar = DefChar;
1129 *UsedDefaultChar = TRUE;
1130 }
1131
1132 /* Handle double-byte characters */
1133 if (uChar & 0xff00)
1134 {
1135 /* Don't output a partial character */
1136 if (TempLength == 1)
1137 break;
1138
1139 TempLength--;
1140 *MultiByteString++ = uChar >> 8;
1141 }
1142
1143 *MultiByteString++ = (char)uChar;
1144 }
1145
1146 /* WideCharCount should be 0 if all characters were converted */
1147 if (WideCharCount)
1148 {
1149 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1150 return 0;
1151 }
1152
1153 return MultiByteCount - TempLength;
1154 }
1155
1156 /* Does caller query for output buffer size? */
1157 if (!MultiByteCount)
1158 {
1159 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1160 {
1161 /* Increment TempLength again if this is a double-byte character */
1162 if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00)
1163 TempLength++;
1164 }
1165
1166 return TempLength;
1167 }
1168
1169 /* Convert the WideCharString to the MultiByteString */
1170 for (TempLength = MultiByteCount;
1171 WideCharCount && TempLength;
1172 TempLength--, WideCharString++, WideCharCount--)
1173 {
1174 USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1175
1176 /* Is this a double-byte character? */
1177 if (uChar & 0xff00)
1178 {
1179 /* Don't output a partial character */
1180 if (TempLength == 1)
1181 break;
1182
1183 TempLength--;
1184 *MultiByteString++ = uChar >> 8;
1185 }
1186
1187 *MultiByteString++ = (char)uChar;
1188 }
1189
1190 /* WideCharCount should be 0 if all characters were converted */
1191 if (WideCharCount)
1192 {
1193 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1194 return 0;
1195 }
1196
1197 return MultiByteCount - TempLength;
1198 }
1199 else /* SBCS code page */
1200 {
1201 INT nReturn;
1202
1203 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1204 if (Flags || DefaultChar || UsedDefaultChar)
1205 {
1206 BOOL TempUsedDefaultChar;
1207 CHAR DefChar;
1208
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;
1213
1214 *UsedDefaultChar = FALSE;
1215
1216 /* Does caller query for output buffer size? */
1217 if (!MultiByteCount)
1218 {
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--)
1221 {
1222 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1223 {
1224 /* FIXME: Handle WC_COMPOSITECHECK */
1225 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1226 }
1227
1228 if (!*UsedDefaultChar)
1229 *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable,
1230 Flags,
1231 *WideCharString,
1232 ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]);
1233 }
1234
1235 return TempLength;
1236 }
1237
1238 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1239 if (DefaultChar)
1240 DefChar = *DefaultChar;
1241 else
1242 DefChar = (CHAR)CodePageTable->TransDefaultChar;
1243
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--)
1248 {
1249 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1250 {
1251 /* FIXME: Handle WC_COMPOSITECHECK */
1252 DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1253 }
1254
1255 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1256
1257 if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString))
1258 {
1259 *MultiByteString = DefChar;
1260 *UsedDefaultChar = TRUE;
1261 }
1262 }
1263
1264 /* WideCharCount should be 0 if all characters were converted */
1265 if (WideCharCount)
1266 {
1267 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1268 return 0;
1269 }
1270
1271 return MultiByteCount - TempLength;
1272 }
1273
1274 /* Does caller query for output buffer size? */
1275 if (!MultiByteCount)
1276 return WideCharCount;
1277
1278 /* Is the buffer large enough? */
1279 if (MultiByteCount < WideCharCount)
1280 {
1281 /* Convert the string up to MultiByteCount and return 0 */
1282 WideCharCount = MultiByteCount;
1283 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1284 nReturn = 0;
1285 }
1286 else
1287 {
1288 /* Otherwise WideCharCount will be the number of converted characters */
1289 nReturn = WideCharCount;
1290 }
1291
1292 /* Convert the WideCharString to the MultiByteString */
1293 for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++)
1294 {
1295 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1296 }
1297
1298 return nReturn;
1299 }
1300 }
1301
1302 /**
1303 * @name IntIsLeadByte
1304 *
1305 * Internal function to detect if byte is lead byte in specific character
1306 * table.
1307 */
1308
1309 static BOOL
1310 WINAPI
1311 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
1312 {
1313 UINT i;
1314
1315 if (TableInfo->MaximumCharacterSize == 2)
1316 {
1317 for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2)
1318 {
1319 if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1])
1320 return TRUE;
1321 }
1322 }
1323
1324 return FALSE;
1325 }
1326
1327 /* PUBLIC FUNCTIONS ***********************************************************/
1328
1329 /**
1330 * @name GetNlsSectionName
1331 *
1332 * Construct a name of NLS section.
1333 *
1334 * @param CodePage
1335 * Code page number.
1336 * @param Base
1337 * Integer base used for converting to string. Usually set to 10.
1338 * @param Unknown
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.
1343 * @param BaseName
1344 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1345 * @param Result
1346 * Buffer that will hold the constructed name.
1347 * @param ResultSize
1348 * Size of the buffer for the result.
1349 *
1350 * @return TRUE if the buffer was large enough and was filled with
1351 * the requested information, FALSE otherwise.
1352 *
1353 * @implemented
1354 */
1355
1356 BOOL
1357 WINAPI
1358 GetNlsSectionName(UINT CodePage,
1359 UINT Base,
1360 ULONG Unknown,
1361 LPSTR BaseName,
1362 LPSTR Result,
1363 ULONG ResultSize)
1364 {
1365 CHAR Integer[11];
1366
1367 if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
1368 return FALSE;
1369
1370 /*
1371 * If the name including the terminating NULL character doesn't
1372 * fit in the output buffer then fail.
1373 */
1374 if (strlen(Integer) + strlen(BaseName) >= ResultSize)
1375 return FALSE;
1376
1377 lstrcpyA(Result, BaseName);
1378 lstrcatA(Result, Integer);
1379
1380 return TRUE;
1381 }
1382
1383 /**
1384 * @name GetCPFileNameFromRegistry
1385 *
1386 * Get file name of code page definition file.
1387 *
1388 * @param CodePage
1389 * Code page number to get file name of.
1390 * @param FileName
1391 * Buffer that is filled with file name of successful return. Can
1392 * be set to NULL.
1393 * @param FileNameSize
1394 * Size of the buffer to hold file name in WCHARs.
1395 *
1396 * @return TRUE if the file name was retrieved, FALSE otherwise.
1397 *
1398 * @implemented
1399 */
1400
1401 BOOL
1402 WINAPI
1403 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
1404 {
1405 WCHAR ValueNameBuffer[11];
1406 UNICODE_STRING KeyName, ValueName;
1407 OBJECT_ATTRIBUTES ObjectAttributes;
1408 NTSTATUS Status;
1409 HANDLE KeyHandle;
1410 PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
1411 DWORD KvpiSize;
1412 BOOL bRetValue;
1413
1414 bRetValue = FALSE;
1415
1416 /* Convert the codepage number to string. */
1417 ValueName.Buffer = ValueNameBuffer;
1418 ValueName.MaximumLength = sizeof(ValueNameBuffer);
1419
1420 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
1421 return bRetValue;
1422
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,
1427 NULL, NULL);
1428 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1429 if (!NT_SUCCESS(Status))
1430 {
1431 return bRetValue;
1432 }
1433
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);
1437 if (Kvpi == NULL)
1438 {
1439 NtClose(KeyHandle);
1440 return bRetValue;
1441 }
1442
1443 /* Query the file name for our code page. */
1444 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
1445 Kvpi, KvpiSize, &KvpiSize);
1446
1447 NtClose(KeyHandle);
1448
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))
1452 {
1453 bRetValue = TRUE;
1454 if (FileName != NULL)
1455 {
1456 lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
1457 min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
1458 }
1459 }
1460
1461 /* free temporary buffer */
1462 HeapFree(GetProcessHeap(),0,Kvpi);
1463 return bRetValue;
1464 }
1465
1466 /**
1467 * @name IsValidCodePage
1468 *
1469 * Detect if specified code page is valid and present in the system.
1470 *
1471 * @param CodePage
1472 * Code page number to query.
1473 *
1474 * @return TRUE if code page is present.
1475 */
1476
1477 BOOL
1478 WINAPI
1479 IsValidCodePage(UINT CodePage)
1480 {
1481 if (CodePage == 0) return FALSE;
1482 if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
1483 return TRUE;
1484 if (IntGetLoadedCodePageEntry(CodePage))
1485 return TRUE;
1486 return GetCPFileNameFromRegistry(CodePage, NULL, 0);
1487 }
1488
1489 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1490 {
1491 if (dstlen > 0)
1492 {
1493 if (*index >= dstlen)
1494 return FALSE;
1495
1496 dst[*index] = character;
1497 }
1498
1499 (*index)++;
1500
1501 return TRUE;
1502 }
1503
1504 static INT Utf7ToWideChar(const char *src, int srclen, WCHAR *dst, int dstlen)
1505 {
1506 static const signed char base64_decoding_table[] =
1507 {
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 */
1516 };
1517
1518 const char *source_end = src + srclen;
1519 int dest_index = 0;
1520
1521 DWORD byte_pair = 0;
1522 short offset = 0;
1523
1524 while (src < source_end)
1525 {
1526 if (*src == '+')
1527 {
1528 src++;
1529 if (src >= source_end)
1530 break;
1531
1532 if (*src == '-')
1533 {
1534 /* just a plus sign escaped as +- */
1535 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
1536 {
1537 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1538 return 0;
1539 }
1540 src++;
1541 continue;
1542 }
1543
1544 do
1545 {
1546 signed char sextet = *src;
1547 if (sextet == '-')
1548 {
1549 /* skip over the dash and end base64 decoding
1550 * the current, unfinished byte pair is discarded */
1551 src++;
1552 offset = 0;
1553 break;
1554 }
1555 if (sextet < 0)
1556 {
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 */
1560 break;
1561 }
1562
1563 sextet = base64_decoding_table[sextet];
1564 if (sextet == -1)
1565 {
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 */
1569 offset = 0;
1570 break;
1571 }
1572
1573 byte_pair = (byte_pair << 6) | sextet;
1574 offset += 6;
1575
1576 if (offset >= 16)
1577 {
1578 /* this byte pair is done */
1579 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
1580 {
1581 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1582 return 0;
1583 }
1584 offset -= 16;
1585 }
1586
1587 src++;
1588 }
1589 while (src < source_end);
1590 }
1591 else
1592 {
1593 /* we have to convert to unsigned char in case *src < 0 */
1594 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
1595 {
1596 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1597 return 0;
1598 }
1599 src++;
1600 }
1601 }
1602
1603 return dest_index;
1604 }
1605
1606 /**
1607 * @name MultiByteToWideChar
1608 *
1609 * Convert a multi-byte string to wide-charater equivalent.
1610 *
1611 * @param CodePage
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).
1616 * @param Flags
1617 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1618 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1619 * @param MultiByteString
1620 * Input buffer.
1621 * @param MultiByteCount
1622 * Size of MultiByteString, or -1 if MultiByteString is NULL
1623 * terminated.
1624 * @param WideCharString
1625 * Output buffer.
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
1629 * conversion.
1630 *
1631 * @return Zero on error, otherwise the number of WCHARs written
1632 * in the WideCharString buffer.
1633 *
1634 * @implemented
1635 */
1636
1637 INT
1638 WINAPI
1639 MultiByteToWideChar(UINT CodePage,
1640 DWORD Flags,
1641 LPCSTR MultiByteString,
1642 INT MultiByteCount,
1643 LPWSTR WideCharString,
1644 INT WideCharCount)
1645 {
1646 /* Check the parameters. */
1647 if (MultiByteString == NULL ||
1648 MultiByteCount == 0 || WideCharCount < 0 ||
1649 (WideCharCount && (WideCharString == NULL ||
1650 (PVOID)MultiByteString == (PVOID)WideCharString)))
1651 {
1652 SetLastError(ERROR_INVALID_PARAMETER);
1653 return 0;
1654 }
1655
1656 /* Determine the input string length. */
1657 if (MultiByteCount < 0)
1658 {
1659 MultiByteCount = lstrlenA(MultiByteString) + 1;
1660 }
1661
1662 switch (CodePage)
1663 {
1664 case CP_UTF8:
1665 return IntMultiByteToWideCharUTF8(Flags,
1666 MultiByteString,
1667 MultiByteCount,
1668 WideCharString,
1669 WideCharCount);
1670
1671 case CP_UTF7:
1672 if (Flags)
1673 {
1674 SetLastError(ERROR_INVALID_FLAGS);
1675 return 0;
1676 }
1677 return Utf7ToWideChar(MultiByteString, MultiByteCount,
1678 WideCharString, WideCharCount);
1679
1680 case CP_SYMBOL:
1681 return IntMultiByteToWideCharSYMBOL(Flags,
1682 MultiByteString,
1683 MultiByteCount,
1684 WideCharString,
1685 WideCharCount);
1686 default:
1687 return IntMultiByteToWideCharCP(CodePage,
1688 Flags,
1689 MultiByteString,
1690 MultiByteCount,
1691 WideCharString,
1692 WideCharCount);
1693 }
1694 }
1695
1696 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
1697 {
1698 static const BOOL directly_encodable_table[] =
1699 {
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 */
1708 };
1709
1710 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
1711 }
1712
1713 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
1714 {
1715 if (dstlen > 0)
1716 {
1717 if (*index >= dstlen)
1718 return FALSE;
1719
1720 dst[*index] = character;
1721 }
1722
1723 (*index)++;
1724
1725 return TRUE;
1726 }
1727
1728 static INT WideCharToUtf7(const WCHAR *src, int srclen, char *dst, int dstlen)
1729 {
1730 static const char base64_encoding_table[] =
1731 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1732
1733 const WCHAR *source_end = src + srclen;
1734 int dest_index = 0;
1735
1736 while (src < source_end)
1737 {
1738 if (*src == '+')
1739 {
1740 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1741 {
1742 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1743 return 0;
1744 }
1745 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1746 {
1747 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1748 return 0;
1749 }
1750 src++;
1751 }
1752 else if (utf7_can_directly_encode(*src))
1753 {
1754 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
1755 {
1756 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1757 return 0;
1758 }
1759 src++;
1760 }
1761 else
1762 {
1763 unsigned int offset = 0;
1764 DWORD byte_pair = 0;
1765
1766 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1767 {
1768 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1769 return 0;
1770 }
1771
1772 while (src < source_end && !utf7_can_directly_encode(*src))
1773 {
1774 byte_pair = (byte_pair << 16) | *src;
1775 offset += 16;
1776 while (offset >= 6)
1777 {
1778 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
1779 {
1780 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1781 return 0;
1782 }
1783 offset -= 6;
1784 }
1785 src++;
1786 }
1787
1788 if (offset)
1789 {
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)
1793 {
1794 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1795 return 0;
1796 }
1797
1798 byte_pair <<= (6 - offset);
1799 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
1800 {
1801 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1802 return 0;
1803 }
1804 }
1805
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, '-'))
1809 {
1810 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1811 return 0;
1812 }
1813 }
1814 }
1815
1816 return dest_index;
1817 }
1818
1819 /*
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.
1826 */
1827 UINT
1828 GetLocalisedText(
1829 IN UINT uID,
1830 IN LPWSTR lpszDest,
1831 IN UINT cchDest)
1832 {
1833 HRSRC hrsrc;
1834 HGLOBAL hmem;
1835 LCID lcid;
1836 LANGID langId;
1837 const WCHAR *p;
1838 UINT i;
1839
1840 /* See HACK in winnls/lang/xx-XX.rc files */
1841 if (uID == 37)
1842 uID = uID * 100;
1843
1844 lcid = GetUserDefaultLCID();
1845 lcid = ConvertDefaultLocale(lcid);
1846
1847 langId = LANGIDFROMLCID(lcid);
1848
1849 if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
1850 langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
1851
1852 hrsrc = FindResourceExW(hCurrentModule,
1853 (LPWSTR)RT_STRING,
1854 MAKEINTRESOURCEW((uID >> 4) + 1),
1855 langId);
1856
1857 /* English fallback */
1858 if (!hrsrc)
1859 {
1860 hrsrc = FindResourceExW(hCurrentModule,
1861 (LPWSTR)RT_STRING,
1862 MAKEINTRESOURCEW((uID >> 4) + 1),
1863 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
1864 }
1865
1866 if (!hrsrc)
1867 goto NotFound;
1868
1869 hmem = LoadResource(hCurrentModule, hrsrc);
1870 if (!hmem)
1871 goto NotFound;
1872
1873 p = LockResource(hmem);
1874
1875 for (i = 0; i < (uID & 0x0F); i++)
1876 p += *p + 1;
1877
1878 /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
1879 if (cchDest == 0)
1880 return *p + 1;
1881 /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
1882 if (*p + 1 > cchDest)
1883 {
1884 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1885 return 0;
1886 }
1887
1888 i = *p;
1889 if (i > 0)
1890 {
1891 memcpy(lpszDest, p + 1, i * sizeof(WCHAR));
1892 lpszDest[i] = L'\0';
1893 return i;
1894 }
1895 #if 0
1896 else
1897 {
1898 if (cchDest >= 1)
1899 lpszDest[0] = L'\0';
1900 /* Fall-back */
1901 }
1902 #endif
1903
1904 NotFound:
1905 DPRINT1("Resource not found: uID = %lu\n", uID);
1906 SetLastError(ERROR_INVALID_PARAMETER);
1907 return 0;
1908 }
1909
1910 /*
1911 * @implemented
1912 */
1913 BOOL
1914 WINAPI
1915 GetCPInfo(UINT CodePage,
1916 LPCPINFO CodePageInfo)
1917 {
1918 PCODEPAGE_ENTRY CodePageEntry;
1919
1920 if (!CodePageInfo)
1921 {
1922 SetLastError(ERROR_INVALID_PARAMETER);
1923 return FALSE;
1924 }
1925
1926 CodePageEntry = IntGetCodePageEntry(CodePage);
1927 if (CodePageEntry == NULL)
1928 {
1929 switch(CodePage)
1930 {
1931 case CP_UTF7:
1932 case CP_UTF8:
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;
1937 return TRUE;
1938 }
1939
1940 DPRINT1("Invalid CP!: %lx\n", CodePage);
1941 SetLastError( ERROR_INVALID_PARAMETER );
1942 return FALSE;
1943 }
1944
1945 if (CodePageEntry->CodePageTable.DefaultChar & 0xff00)
1946 {
1947 CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8;
1948 CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff;
1949 }
1950 else
1951 {
1952 CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff;
1953 CodePageInfo->DefaultChar[1] = 0;
1954 }
1955
1956 if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2)
1957 memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte));
1958 else
1959 CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1960
1961 return TRUE;
1962 }
1963
1964 /*
1965 * @implemented
1966 */
1967 BOOL
1968 WINAPI
1969 GetCPInfoExW(UINT CodePage,
1970 DWORD dwFlags,
1971 LPCPINFOEXW lpCPInfoEx)
1972 {
1973 if (!GetCPInfo(CodePage, (LPCPINFO)lpCPInfoEx))
1974 return FALSE;
1975
1976 switch(CodePage)
1977 {
1978 case CP_UTF7:
1979 {
1980 lpCPInfoEx->CodePage = CP_UTF7;
1981 lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1982 return GetLocalisedText(lpCPInfoEx->CodePage,
1983 lpCPInfoEx->CodePageName,
1984 ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0;
1985 }
1986 break;
1987
1988 case CP_UTF8:
1989 {
1990 lpCPInfoEx->CodePage = CP_UTF8;
1991 lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1992 return GetLocalisedText(lpCPInfoEx->CodePage,
1993 lpCPInfoEx->CodePageName,
1994 ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0;
1995 }
1996
1997 default:
1998 {
1999 PCODEPAGE_ENTRY CodePageEntry;
2000
2001 CodePageEntry = IntGetCodePageEntry(CodePage);
2002 if (CodePageEntry == NULL)
2003 {
2004 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
2005 SetLastError(ERROR_INVALID_PARAMETER);
2006 return FALSE;
2007 }
2008
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;
2014 }
2015 break;
2016 }
2017 }
2018
2019
2020 /*
2021 * @implemented
2022 */
2023 BOOL
2024 WINAPI
2025 GetCPInfoExA(UINT CodePage,
2026 DWORD dwFlags,
2027 LPCPINFOEXA lpCPInfoEx)
2028 {
2029 CPINFOEXW CPInfo;
2030
2031 if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo))
2032 return FALSE;
2033
2034 /* the layout is the same except for CodePageName */
2035 memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA));
2036
2037 WideCharToMultiByte(CP_ACP,
2038 0,
2039 CPInfo.CodePageName,
2040 -1,
2041 lpCPInfoEx->CodePageName,
2042 sizeof(lpCPInfoEx->CodePageName),
2043 NULL,
2044 NULL);
2045 return TRUE;
2046 }
2047
2048 /**
2049 * @name WideCharToMultiByte
2050 *
2051 * Convert a wide-charater string to closest multi-byte equivalent.
2052 *
2053 * @param CodePage
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).
2058 * @param Flags
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.
2079 *
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.
2083 *
2084 * @implemented
2085 */
2086
2087 INT
2088 WINAPI
2089 WideCharToMultiByte(UINT CodePage,
2090 DWORD Flags,
2091 LPCWSTR WideCharString,
2092 INT WideCharCount,
2093 LPSTR MultiByteString,
2094 INT MultiByteCount,
2095 LPCSTR DefaultChar,
2096 LPBOOL UsedDefaultChar)
2097 {
2098 /* Check the parameters. */
2099 if (WideCharString == NULL ||
2100 WideCharCount == 0 ||
2101 (MultiByteString == NULL && MultiByteCount > 0) ||
2102 (PVOID)WideCharString == (PVOID)MultiByteString ||
2103 MultiByteCount < 0)
2104 {
2105 SetLastError(ERROR_INVALID_PARAMETER);
2106 return 0;
2107 }
2108
2109 /* Determine the input string length. */
2110 if (WideCharCount < 0)
2111 {
2112 WideCharCount = lstrlenW(WideCharString) + 1;
2113 }
2114
2115 switch (CodePage)
2116 {
2117 case CP_UTF8:
2118 if (DefaultChar != NULL || UsedDefaultChar != NULL)
2119 {
2120 SetLastError(ERROR_INVALID_PARAMETER);
2121 return 0;
2122 }
2123 return IntWideCharToMultiByteUTF8(CodePage,
2124 Flags,
2125 WideCharString,
2126 WideCharCount,
2127 MultiByteString,
2128 MultiByteCount,
2129 DefaultChar,
2130 UsedDefaultChar);
2131
2132 case CP_UTF7:
2133 if (DefaultChar != NULL || UsedDefaultChar != NULL)
2134 {
2135 SetLastError(ERROR_INVALID_PARAMETER);
2136 return 0;
2137 }
2138 if (Flags)
2139 {
2140 SetLastError(ERROR_INVALID_FLAGS);
2141 return 0;
2142 }
2143 return WideCharToUtf7(WideCharString, WideCharCount,
2144 MultiByteString, MultiByteCount);
2145
2146 case CP_SYMBOL:
2147 if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL))
2148 {
2149 SetLastError(ERROR_INVALID_PARAMETER);
2150 return 0;
2151 }
2152 return IntWideCharToMultiByteSYMBOL(Flags,
2153 WideCharString,
2154 WideCharCount,
2155 MultiByteString,
2156 MultiByteCount);
2157
2158 default:
2159 return IntWideCharToMultiByteCP(CodePage,
2160 Flags,
2161 WideCharString,
2162 WideCharCount,
2163 MultiByteString,
2164 MultiByteCount,
2165 DefaultChar,
2166 UsedDefaultChar);
2167 }
2168 }
2169
2170 /**
2171 * @name GetACP
2172 *
2173 * Get active ANSI code page number.
2174 *
2175 * @implemented
2176 */
2177
2178 UINT
2179 WINAPI
2180 GetACP(VOID)
2181 {
2182 return AnsiCodePage.CodePageTable.CodePage;
2183 }
2184
2185 /**
2186 * @name GetOEMCP
2187 *
2188 * Get active OEM code page number.
2189 *
2190 * @implemented
2191 */
2192
2193 UINT
2194 WINAPI
2195 GetOEMCP(VOID)
2196 {
2197 return OemCodePage.CodePageTable.CodePage;
2198 }
2199
2200 /**
2201 * @name IsDBCSLeadByteEx
2202 *
2203 * Determine if passed byte is lead byte in specified code page.
2204 *
2205 * @implemented
2206 */
2207
2208 BOOL
2209 WINAPI
2210 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
2211 {
2212 PCODEPAGE_ENTRY CodePageEntry;
2213
2214 CodePageEntry = IntGetCodePageEntry(CodePage);
2215 if (CodePageEntry != NULL)
2216 return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
2217
2218 SetLastError(ERROR_INVALID_PARAMETER);
2219 return FALSE;
2220 }
2221
2222 /**
2223 * @name IsDBCSLeadByteEx
2224 *
2225 * Determine if passed byte is lead byte in current ANSI code page.
2226 *
2227 * @implemented
2228 */
2229
2230 BOOL
2231 WINAPI
2232 IsDBCSLeadByte(BYTE TestByte)
2233 {
2234 return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
2235 }
2236
2237 /*
2238 * @unimplemented
2239 */
2240 NTSTATUS WINAPI CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor,ULONG Size,ULONG AccessMask)
2241 {
2242 STUB;
2243 return 0;
2244 }
2245
2246 /*
2247 * @unimplemented
2248 */
2249 BOOL WINAPI IsValidUILanguage(LANGID langid)
2250 {
2251 STUB;
2252 return 0;
2253 }
2254
2255 /*
2256 * @unimplemented
2257 */
2258 VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2)
2259 {
2260 STUB;
2261 }
2262
2263 /*
2264 * @unimplemented
2265 */
2266 UINT WINAPI SetCPGlobal(UINT CodePage)
2267 {
2268 STUB;
2269 return 0;
2270 }
2271
2272 /*
2273 * @unimplemented
2274 */
2275 BOOL
2276 WINAPI
2277 ValidateLCType(int a1, unsigned int a2, int a3, int a4)
2278 {
2279 STUB;
2280 return FALSE;
2281 }
2282
2283 /*
2284 * @unimplemented
2285 */
2286 BOOL
2287 WINAPI
2288 NlsResetProcessLocale(VOID)
2289 {
2290 STUB;
2291 return TRUE;
2292 }
2293
2294 /*
2295 * @unimplemented
2296 */
2297 VOID
2298 WINAPI
2299 GetDefaultSortkeySize(LPVOID lpUnknown)
2300 {
2301 STUB;
2302 lpUnknown = NULL;
2303 }
2304
2305 /*
2306 * @unimplemented
2307 */
2308 VOID
2309 WINAPI
2310 GetLinguistLangSize(LPVOID lpUnknown)
2311 {
2312 STUB;
2313 lpUnknown = NULL;
2314 }
2315
2316 /*
2317 * @unimplemented
2318 */
2319 BOOL
2320 WINAPI
2321 ValidateLocale(IN ULONG LocaleId)
2322 {
2323 STUB;
2324 return TRUE;
2325 }
2326
2327 /*
2328 * @unimplemented
2329 */
2330 ULONG
2331 WINAPI
2332 NlsGetCacheUpdateCount(VOID)
2333 {
2334 STUB;
2335 return 0;
2336 }
2337
2338 /*
2339 * @unimplemented
2340 */
2341 BOOL
2342 WINAPI
2343 IsNLSDefinedString(IN NLS_FUNCTION Function,
2344 IN DWORD dwFlags,
2345 IN LPNLSVERSIONINFO lpVersionInformation,
2346 IN LPCWSTR lpString,
2347 IN INT cchStr)
2348 {
2349 STUB;
2350 return TRUE;
2351 }
2352
2353 /*
2354 * @unimplemented
2355 */
2356 BOOL
2357 WINAPI
2358 GetNLSVersion(IN NLS_FUNCTION Function,
2359 IN LCID Locale,
2360 IN OUT LPNLSVERSIONINFO lpVersionInformation)
2361 {
2362 STUB;
2363 return TRUE;
2364 }
2365
2366 /*
2367 * @unimplemented
2368 */
2369 BOOL
2370 WINAPI
2371 GetNLSVersionEx(IN NLS_FUNCTION function,
2372 IN LPCWSTR lpLocaleName,
2373 IN OUT LPNLSVERSIONINFOEX lpVersionInformation)
2374 {
2375 STUB;
2376 return TRUE;
2377 }
2378
2379 /* EOF */