[KERNEL32] Implement MB_USEGLYPHCHARS flag for MultiByteToWideChar. Add checks for...
[reactos.git] / reactos / 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 * UPDATE HISTORY:
11 * Created 24/08/2004
12 */
13
14 /* INCLUDES *******************************************************************/
15
16 #include <k32.h>
17
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(VOID)
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.SectionHandle = NULL;
93 AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
94
95 RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
96 &AnsiCodePage.CodePageTable);
97 AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage;
98
99 InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
100
101 /* Setup OEM code page. */
102 OemCodePage.SectionHandle = NULL;
103 OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
104
105 RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
106 &OemCodePage.CodePageTable);
107 OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage;
108 InsertTailList(&CodePageListHead, &OemCodePage.Entry);
109
110 return TRUE;
111 }
112
113 /**
114 * @name NlsUninit
115 *
116 * Internal NLS related stuff uninitialization.
117 */
118
119 VOID
120 FASTCALL
121 NlsUninit(VOID)
122 {
123 PCODEPAGE_ENTRY Current;
124
125 /* Delete the code page list. */
126 while (!IsListEmpty(&CodePageListHead))
127 {
128 Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
129 if (Current->SectionHandle != NULL)
130 {
131 UnmapViewOfFile(Current->SectionMapping);
132 NtClose(Current->SectionHandle);
133 }
134 RemoveHeadList(&CodePageListHead);
135 }
136 RtlDeleteCriticalSection(&CodePageListLock);
137 }
138
139 /**
140 * @name IntGetLoadedCodePageEntry
141 *
142 * Internal function to get structure containing a code page information
143 * of code page that is already loaded.
144 *
145 * @param CodePage
146 * Number of the code page. Special values like CP_OEMCP, CP_ACP
147 * or CP_UTF8 aren't allowed.
148 *
149 * @return Code page entry or NULL if the specified code page hasn't
150 * been loaded yet.
151 */
152
153 PCODEPAGE_ENTRY
154 FASTCALL
155 IntGetLoadedCodePageEntry(UINT CodePage)
156 {
157 LIST_ENTRY *CurrentEntry;
158 PCODEPAGE_ENTRY Current;
159
160 RtlEnterCriticalSection(&CodePageListLock);
161 for (CurrentEntry = CodePageListHead.Flink;
162 CurrentEntry != &CodePageListHead;
163 CurrentEntry = CurrentEntry->Flink)
164 {
165 Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
166 if (Current->CodePage == CodePage)
167 {
168 RtlLeaveCriticalSection(&CodePageListLock);
169 return Current;
170 }
171 }
172 RtlLeaveCriticalSection(&CodePageListLock);
173
174 return NULL;
175 }
176
177 /**
178 * @name IntGetCodePageEntry
179 *
180 * Internal function to get structure containing a code page information.
181 *
182 * @param CodePage
183 * Number of the code page. Special values like CP_OEMCP, CP_ACP
184 * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
185 *
186 * @return Code page entry.
187 */
188
189 PCODEPAGE_ENTRY
190 FASTCALL
191 IntGetCodePageEntry(UINT CodePage)
192 {
193 CHAR SectionName[40];
194 NTSTATUS Status;
195 HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle;
196 PBYTE SectionMapping;
197 OBJECT_ATTRIBUTES ObjectAttributes;
198 ANSI_STRING AnsiName;
199 UNICODE_STRING UnicodeName;
200 WCHAR FileName[MAX_PATH + 1];
201 UINT FileNamePos;
202 PCODEPAGE_ENTRY CodePageEntry;
203 if (CodePage == CP_ACP)
204 {
205 return &AnsiCodePage;
206 }
207 else if (CodePage == CP_OEMCP)
208 {
209 return &OemCodePage;
210 }
211 else if (CodePage == CP_THREAD_ACP)
212 {
213 if (!GetLocaleInfoW(GetThreadLocale(),
214 LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
215 (WCHAR *)&CodePage,
216 sizeof(CodePage) / sizeof(WCHAR)))
217 {
218 /* Last error is set by GetLocaleInfoW. */
219 return NULL;
220 }
221 if (CodePage == 0)
222 return &AnsiCodePage;
223 }
224 else if (CodePage == CP_MACCP)
225 {
226 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT,
227 LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
228 (WCHAR *)&CodePage,
229 sizeof(CodePage) / sizeof(WCHAR)))
230 {
231 /* Last error is set by GetLocaleInfoW. */
232 return NULL;
233 }
234 }
235
236 /* Try searching for loaded page first. */
237 CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
238 if (CodePageEntry != NULL)
239 {
240 return CodePageEntry;
241 }
242
243 /*
244 * Yes, we really want to lock here. Otherwise it can happen that
245 * two parallel requests will try to get the entry for the same
246 * code page and we would load it twice.
247 */
248 RtlEnterCriticalSection(&CodePageListLock);
249
250 /* Generate the section name. */
251 if (!GetNlsSectionName(CodePage,
252 10,
253 0,
254 "\\Nls\\NlsSectionCP",
255 SectionName,
256 sizeof(SectionName)))
257 {
258 RtlLeaveCriticalSection(&CodePageListLock);
259 return NULL;
260 }
261
262 RtlInitAnsiString(&AnsiName, SectionName);
263 RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
264
265 InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL);
266
267 /* Try to open the section first */
268 Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
269
270 /* If the section doesn't exist, try to create it. */
271 if (Status == STATUS_UNSUCCESSFUL ||
272 Status == STATUS_OBJECT_NAME_NOT_FOUND ||
273 Status == STATUS_OBJECT_PATH_NOT_FOUND)
274 {
275 FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
276 if (GetCPFileNameFromRegistry(CodePage,
277 FileName + FileNamePos + 1,
278 MAX_PATH - FileNamePos - 1))
279 {
280 FileName[FileNamePos] = L'\\';
281 FileName[MAX_PATH] = 0;
282 FileHandle = CreateFileW(FileName,
283 FILE_GENERIC_READ,
284 FILE_SHARE_READ,
285 NULL,
286 OPEN_EXISTING,
287 0,
288 NULL);
289
290 Status = NtCreateSection(&SectionHandle,
291 SECTION_MAP_READ,
292 &ObjectAttributes,
293 NULL,
294 PAGE_READONLY,
295 SEC_COMMIT,
296 FileHandle);
297
298 /* HACK: Check if another process was faster
299 * and already created this section. See bug 3626 for details */
300 if (Status == STATUS_OBJECT_NAME_COLLISION)
301 {
302 /* Close the file then */
303 NtClose(FileHandle);
304
305 /* And open the section */
306 Status = NtOpenSection(&SectionHandle,
307 SECTION_MAP_READ,
308 &ObjectAttributes);
309 }
310 }
311 }
312 RtlFreeUnicodeString(&UnicodeName);
313
314 if (!NT_SUCCESS(Status))
315 {
316 RtlLeaveCriticalSection(&CodePageListLock);
317 return NULL;
318 }
319
320 SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
321 if (SectionMapping == NULL)
322 {
323 NtClose(SectionHandle);
324 RtlLeaveCriticalSection(&CodePageListLock);
325 return NULL;
326 }
327
328 CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
329 if (CodePageEntry == NULL)
330 {
331 NtClose(SectionHandle);
332 RtlLeaveCriticalSection(&CodePageListLock);
333 return NULL;
334 }
335
336 CodePageEntry->CodePage = CodePage;
337 CodePageEntry->SectionHandle = SectionHandle;
338 CodePageEntry->SectionMapping = SectionMapping;
339
340 RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
341
342 /* Insert the new entry to list and unlock. Uff. */
343 InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
344 RtlLeaveCriticalSection(&CodePageListLock);
345
346 return CodePageEntry;
347 }
348
349 /**
350 * @name IntMultiByteToWideCharUTF8
351 *
352 * Internal version of MultiByteToWideChar for UTF8.
353 *
354 * @see MultiByteToWideChar
355 * @todo Add UTF8 validity checks.
356 */
357
358 static
359 INT
360 WINAPI
361 IntMultiByteToWideCharUTF8(DWORD Flags,
362 LPCSTR MultiByteString,
363 INT MultiByteCount,
364 LPWSTR WideCharString,
365 INT WideCharCount)
366 {
367 LPCSTR MbsEnd;
368 UCHAR Char, Length;
369 WCHAR WideChar;
370 LONG Count;
371
372 if (Flags != 0 && Flags != MB_ERR_INVALID_CHARS)
373 {
374 SetLastError(ERROR_INVALID_FLAGS);
375 return 0;
376 }
377
378 /* Does caller query for output buffer size? */
379 if (WideCharCount == 0)
380 {
381 MbsEnd = MultiByteString + MultiByteCount;
382 for (; MultiByteString < MbsEnd; WideCharCount++)
383 {
384 Char = *MultiByteString++;
385 if (Char < 0xC0)
386 continue;
387 MultiByteString += UTF8Length[Char - 0x80];
388 }
389 return WideCharCount;
390 }
391
392 MbsEnd = MultiByteString + MultiByteCount;
393 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
394 {
395 Char = *MultiByteString++;
396 if (Char < 0x80)
397 {
398 *WideCharString++ = Char;
399 continue;
400 }
401 Length = UTF8Length[Char - 0x80];
402 WideChar = Char & UTF8Mask[Length];
403 while (Length && MultiByteString < MbsEnd)
404 {
405 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
406 Length--;
407 }
408 *WideCharString++ = WideChar;
409 }
410
411 if (MultiByteString < MbsEnd)
412 SetLastError(ERROR_INSUFFICIENT_BUFFER);
413
414 return Count;
415 }
416
417 /**
418 * @name IntMultiByteToWideCharCP
419 *
420 * Internal version of MultiByteToWideChar for code page tables.
421 *
422 * @see MultiByteToWideChar
423 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
424 * DBCS codepages.
425 */
426
427 static
428 INT
429 WINAPI
430 IntMultiByteToWideCharCP(UINT CodePage,
431 DWORD Flags,
432 LPCSTR MultiByteString,
433 INT MultiByteCount,
434 LPWSTR WideCharString,
435 INT WideCharCount)
436 {
437 PCODEPAGE_ENTRY CodePageEntry;
438 PCPTABLEINFO CodePageTable;
439 PUSHORT MultiByteTable;
440 LPCSTR TempString;
441 INT TempLength;
442
443 /* Get code page table. */
444 CodePageEntry = IntGetCodePageEntry(CodePage);
445 if (CodePageEntry == NULL)
446 {
447 SetLastError(ERROR_INVALID_PARAMETER);
448 return 0;
449 }
450
451 CodePageTable = &CodePageEntry->CodePageTable;
452
453 /* If MB_USEGLYPHCHARS flag present and glyph table present */
454 if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256])
455 {
456 /* Use glyph table */
457 MultiByteTable = CodePageTable->MultiByteTable + 256 + 1;
458 }
459 else
460 {
461 MultiByteTable = CodePageTable->MultiByteTable;
462 }
463
464 /* Different handling for DBCS code pages. */
465 if (CodePageTable->DBCSCodePage)
466 {
467 UCHAR Char;
468 USHORT DBCSOffset;
469 LPCSTR MbsEnd = MultiByteString + MultiByteCount;
470 INT Count;
471
472 if (Flags & MB_ERR_INVALID_CHARS)
473 {
474 /* FIXME */
475 DPRINT1("IntMultiByteToWideCharCP: MB_ERR_INVALID_CHARS case not implemented!\n");
476 }
477
478 /* Does caller query for output buffer size? */
479 if (WideCharCount == 0)
480 {
481 for (; MultiByteString < MbsEnd; WideCharCount++)
482 {
483 Char = *MultiByteString++;
484
485 if (Char < 0x80)
486 continue;
487
488 DBCSOffset = CodePageTable->DBCSOffsets[Char];
489
490 if (!DBCSOffset)
491 continue;
492
493 if (MultiByteString < MbsEnd)
494 MultiByteString++;
495 }
496
497 return WideCharCount;
498 }
499
500 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
501 {
502 Char = *MultiByteString++;
503
504 if (Char < 0x80)
505 {
506 *WideCharString++ = Char;
507 continue;
508 }
509
510 DBCSOffset = CodePageTable->DBCSOffsets[Char];
511
512 if (!DBCSOffset)
513 {
514 *WideCharString++ = MultiByteTable[Char];
515 continue;
516 }
517
518 if (MultiByteString < MbsEnd)
519 *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + *(PUCHAR)MultiByteString++];
520 }
521
522 if (MultiByteString < MbsEnd)
523 {
524 SetLastError(ERROR_INSUFFICIENT_BUFFER);
525 return 0;
526 }
527
528 return Count;
529 }
530 else /* Not DBCS code page */
531 {
532 /* Check for invalid characters. */
533 if (Flags & MB_ERR_INVALID_CHARS)
534 {
535 for (TempString = MultiByteString, TempLength = MultiByteCount;
536 TempLength > 0;
537 TempString++, TempLength--)
538 {
539 USHORT WideChar = MultiByteTable[(UCHAR)*TempString];
540
541 if ((WideChar == CodePageTable->UniDefaultChar &&
542 *TempString != CodePageEntry->CodePageTable.TransUniDefaultChar) ||
543 /* "Private Use" characters */
544 (WideChar >= 0xE000 && WideChar <= 0xF8FF))
545 {
546 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
547 return 0;
548 }
549 }
550 }
551
552 /* Does caller query for output buffer size? */
553 if (WideCharCount == 0)
554 return MultiByteCount;
555
556 /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
557 for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount;
558 TempLength > 0;
559 MultiByteString++, TempLength--)
560 {
561 *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString];
562 }
563
564 /* Adjust buffer size. Wine trick ;-) */
565 if (WideCharCount < MultiByteCount)
566 {
567 MultiByteCount = WideCharCount;
568 SetLastError(ERROR_INSUFFICIENT_BUFFER);
569 return 0;
570 }
571 return MultiByteCount;
572 }
573 }
574
575 /**
576 * @name IntMultiByteToWideCharSYMBOL
577 *
578 * Internal version of MultiByteToWideChar for SYMBOL.
579 *
580 * @see MultiByteToWideChar
581 */
582
583 static
584 INT
585 WINAPI
586 IntMultiByteToWideCharSYMBOL(DWORD Flags,
587 LPCSTR MultiByteString,
588 INT MultiByteCount,
589 LPWSTR WideCharString,
590 INT WideCharCount)
591 {
592 LONG Count;
593 UCHAR Char;
594 INT WideCharMaxLen;
595
596
597 if (Flags != 0)
598 {
599 SetLastError(ERROR_INVALID_FLAGS);
600 return 0;
601 }
602
603 if (WideCharCount == 0)
604 {
605 return MultiByteCount;
606 }
607
608 WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount;
609
610 for (Count = 0; Count < WideCharMaxLen; Count++)
611 {
612 Char = MultiByteString[Count];
613 if ( Char < 0x20 )
614 {
615 WideCharString[Count] = Char;
616 }
617 else
618 {
619 WideCharString[Count] = Char + 0xf000;
620 }
621 }
622 if (MultiByteCount > WideCharMaxLen)
623 {
624 SetLastError(ERROR_INSUFFICIENT_BUFFER);
625 return 0;
626 }
627
628 return WideCharMaxLen;
629 }
630
631 /**
632 * @name IntWideCharToMultiByteSYMBOL
633 *
634 * Internal version of WideCharToMultiByte for SYMBOL.
635 *
636 * @see WideCharToMultiByte
637 */
638
639 static INT
640 WINAPI
641 IntWideCharToMultiByteSYMBOL(DWORD Flags,
642 LPCWSTR WideCharString,
643 INT WideCharCount,
644 LPSTR MultiByteString,
645 INT MultiByteCount)
646 {
647 LONG Count;
648 INT MaxLen;
649 WCHAR Char;
650
651 if (Flags!=0)
652 {
653 SetLastError(ERROR_INVALID_PARAMETER);
654 return 0;
655 }
656
657
658 if (MultiByteCount == 0)
659 {
660 return WideCharCount;
661 }
662
663 MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount;
664 for (Count = 0; Count < MaxLen; Count++)
665 {
666 Char = WideCharString[Count];
667 if (Char < 0x20)
668 {
669 MultiByteString[Count] = (CHAR)Char;
670 }
671 else
672 {
673 if ((Char >= 0xf020) && (Char < 0xf100))
674 {
675 MultiByteString[Count] = Char - 0xf000;
676 }
677 else
678 {
679 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
680 return 0;
681 }
682 }
683 }
684
685 if (WideCharCount > MaxLen)
686 {
687 SetLastError(ERROR_INSUFFICIENT_BUFFER);
688 return 0;
689 }
690 return MaxLen;
691 }
692
693 /**
694 * @name IntWideCharToMultiByteUTF8
695 *
696 * Internal version of WideCharToMultiByte for UTF8.
697 *
698 * @see WideCharToMultiByte
699 */
700
701 static INT
702 WINAPI
703 IntWideCharToMultiByteUTF8(UINT CodePage,
704 DWORD Flags,
705 LPCWSTR WideCharString,
706 INT WideCharCount,
707 LPSTR MultiByteString,
708 INT MultiByteCount,
709 LPCSTR DefaultChar,
710 LPBOOL UsedDefaultChar)
711 {
712 INT TempLength;
713 WCHAR Char;
714
715 /* Does caller query for output buffer size? */
716 if (MultiByteCount == 0)
717 {
718 for (TempLength = 0; WideCharCount;
719 WideCharCount--, WideCharString++)
720 {
721 TempLength++;
722 if (*WideCharString >= 0x80)
723 {
724 TempLength++;
725 if (*WideCharString >= 0x800)
726 TempLength++;
727 }
728 }
729 return TempLength;
730 }
731
732 for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++)
733 {
734 Char = *WideCharString;
735 if (Char < 0x80)
736 {
737 if (!TempLength)
738 {
739 SetLastError(ERROR_INSUFFICIENT_BUFFER);
740 break;
741 }
742 TempLength--;
743 *MultiByteString++ = (CHAR)Char;
744 continue;
745 }
746
747 if (Char < 0x800) /* 0x80-0x7ff: 2 bytes */
748 {
749 if (TempLength < 2)
750 {
751 SetLastError(ERROR_INSUFFICIENT_BUFFER);
752 break;
753 }
754 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
755 MultiByteString[0] = 0xc0 | Char;
756 MultiByteString += 2;
757 TempLength -= 2;
758 continue;
759 }
760
761 /* 0x800-0xffff: 3 bytes */
762 if (TempLength < 3)
763 {
764 SetLastError(ERROR_INSUFFICIENT_BUFFER);
765 break;
766 }
767 MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
768 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
769 MultiByteString[0] = 0xe0 | Char;
770 MultiByteString += 3;
771 TempLength -= 3;
772 }
773
774 return MultiByteCount - TempLength;
775 }
776
777 /**
778 * @name IsValidSBCSMapping
779 *
780 * Checks if ch (single-byte character) is a valid mapping for wch
781 *
782 * @see IntWideCharToMultiByteCP
783 */
784 static
785 inline
786 BOOL
787 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch)
788 {
789 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
790 if (Flags & WC_NO_BEST_FIT_CHARS)
791 return (CodePageTable->MultiByteTable[ch] == wch);
792
793 /* By default, all characters except TransDefaultChar apply as a valid mapping
794 for ch (so also "nearest" characters) */
795 if (ch != CodePageTable->TransDefaultChar)
796 return TRUE;
797
798 /* The only possible left valid mapping is the default character itself */
799 return (wch == CodePageTable->TransUniDefaultChar);
800 }
801
802 /**
803 * @name IsValidDBCSMapping
804 *
805 * Checks if ch (double-byte character) is a valid mapping for wch
806 *
807 * @see IntWideCharToMultiByteCP
808 */
809 static inline BOOL
810 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch)
811 {
812 /* If ch is the default character, but the wch is not, it can't be a valid mapping */
813 if (ch == CodePageTable->TransDefaultChar && wch != CodePageTable->TransUniDefaultChar)
814 return FALSE;
815
816 /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
817 if (Flags & WC_NO_BEST_FIT_CHARS)
818 {
819 if(ch & 0xff00)
820 {
821 USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8];
822 /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
823 return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch);
824 }
825
826 return (CodePageTable->MultiByteTable[ch] == wch);
827 }
828
829 /* If we're still here, we have a valid mapping */
830 return TRUE;
831 }
832
833 /**
834 * @name IntWideCharToMultiByteCP
835 *
836 * Internal version of WideCharToMultiByte for code page tables.
837 *
838 * @see WideCharToMultiByte
839 * @todo Handle WC_COMPOSITECHECK
840 */
841 static
842 INT
843 WINAPI
844 IntWideCharToMultiByteCP(UINT CodePage,
845 DWORD Flags,
846 LPCWSTR WideCharString,
847 INT WideCharCount,
848 LPSTR MultiByteString,
849 INT MultiByteCount,
850 LPCSTR DefaultChar,
851 LPBOOL UsedDefaultChar)
852 {
853 PCODEPAGE_ENTRY CodePageEntry;
854 PCPTABLEINFO CodePageTable;
855 INT TempLength;
856
857 /* Get code page table. */
858 CodePageEntry = IntGetCodePageEntry(CodePage);
859 if (CodePageEntry == NULL)
860 {
861 SetLastError(ERROR_INVALID_PARAMETER);
862 return 0;
863 }
864 CodePageTable = &CodePageEntry->CodePageTable;
865
866
867 /* Different handling for DBCS code pages. */
868 if (CodePageTable->MaximumCharacterSize > 1)
869 {
870 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
871 if(Flags || DefaultChar || UsedDefaultChar)
872 {
873 BOOL TempUsedDefaultChar;
874 USHORT DefChar;
875
876 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
877 to check on every character */
878 if(!UsedDefaultChar)
879 UsedDefaultChar = &TempUsedDefaultChar;
880
881 *UsedDefaultChar = FALSE;
882
883 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
884 if(DefaultChar)
885 DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0];
886 else
887 DefChar = CodePageTable->TransDefaultChar;
888
889 /* Does caller query for output buffer size? */
890 if(!MultiByteCount)
891 {
892 for(TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
893 {
894 USHORT uChar;
895
896 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
897 {
898 /* FIXME: Handle WC_COMPOSITECHECK */
899 }
900
901 uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
902
903 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
904 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
905 {
906 uChar = DefChar;
907 *UsedDefaultChar = TRUE;
908 }
909
910 /* Increment TempLength again if this is a double-byte character */
911 if (uChar & 0xff00)
912 TempLength++;
913 }
914
915 return TempLength;
916 }
917
918 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
919 for(TempLength = MultiByteCount;
920 WideCharCount && TempLength;
921 TempLength--, WideCharString++, WideCharCount--)
922 {
923 USHORT uChar;
924
925 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
926 {
927 /* FIXME: Handle WC_COMPOSITECHECK */
928 }
929
930 uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString];
931
932 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
933 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
934 {
935 uChar = DefChar;
936 *UsedDefaultChar = TRUE;
937 }
938
939 /* Handle double-byte characters */
940 if (uChar & 0xff00)
941 {
942 /* Don't output a partial character */
943 if (TempLength == 1)
944 break;
945
946 TempLength--;
947 *MultiByteString++ = uChar >> 8;
948 }
949
950 *MultiByteString++ = (char)uChar;
951 }
952
953 /* WideCharCount should be 0 if all characters were converted */
954 if (WideCharCount)
955 {
956 SetLastError(ERROR_INSUFFICIENT_BUFFER);
957 return 0;
958 }
959
960 return MultiByteCount - TempLength;
961 }
962
963 /* Does caller query for output buffer size? */
964 if (!MultiByteCount)
965 {
966 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
967 {
968 /* Increment TempLength again if this is a double-byte character */
969 if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00)
970 TempLength++;
971 }
972
973 return TempLength;
974 }
975
976 /* Convert the WideCharString to the MultiByteString */
977 for (TempLength = MultiByteCount;
978 WideCharCount && TempLength;
979 TempLength--, WideCharString++, WideCharCount--)
980 {
981 USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
982
983 /* Is this a double-byte character? */
984 if (uChar & 0xff00)
985 {
986 /* Don't output a partial character */
987 if (TempLength == 1)
988 break;
989
990 TempLength--;
991 *MultiByteString++ = uChar >> 8;
992 }
993
994 *MultiByteString++ = (char)uChar;
995 }
996
997 /* WideCharCount should be 0 if all characters were converted */
998 if (WideCharCount)
999 {
1000 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1001 return 0;
1002 }
1003
1004 return MultiByteCount - TempLength;
1005 }
1006 else /* Not DBCS code page */
1007 {
1008 INT nReturn;
1009
1010 /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1011 if (Flags || DefaultChar || UsedDefaultChar)
1012 {
1013 BOOL TempUsedDefaultChar;
1014 CHAR DefChar;
1015
1016 /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1017 to check on every character */
1018 if (!UsedDefaultChar)
1019 UsedDefaultChar = &TempUsedDefaultChar;
1020
1021 *UsedDefaultChar = FALSE;
1022
1023 /* Does caller query for output buffer size? */
1024 if (!MultiByteCount)
1025 {
1026 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1027 for (TempLength = 0; WideCharCount; TempLength++, WideCharString++, WideCharCount--)
1028 {
1029 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1030 {
1031 /* FIXME: Handle WC_COMPOSITECHECK */
1032 }
1033
1034 if (!*UsedDefaultChar)
1035 *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable,
1036 Flags,
1037 *WideCharString,
1038 ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]);
1039 }
1040
1041 return TempLength;
1042 }
1043
1044 /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1045 if (DefaultChar)
1046 DefChar = *DefaultChar;
1047 else
1048 DefChar = (CHAR)CodePageTable->TransDefaultChar;
1049
1050 /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1051 for (TempLength = MultiByteCount;
1052 WideCharCount && TempLength;
1053 MultiByteString++, TempLength--, WideCharString++, WideCharCount--)
1054 {
1055 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1056 {
1057 /* FIXME: Handle WC_COMPOSITECHECK */
1058 }
1059
1060 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1061
1062 if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString))
1063 {
1064 *MultiByteString = DefChar;
1065 *UsedDefaultChar = TRUE;
1066 }
1067 }
1068
1069 /* WideCharCount should be 0 if all characters were converted */
1070 if (WideCharCount)
1071 {
1072 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1073 return 0;
1074 }
1075
1076 return MultiByteCount - TempLength;
1077 }
1078
1079 /* Does caller query for output buffer size? */
1080 if (!MultiByteCount)
1081 return WideCharCount;
1082
1083 /* Is the buffer large enough? */
1084 if (MultiByteCount < WideCharCount)
1085 {
1086 /* Convert the string up to MultiByteCount and return 0 */
1087 WideCharCount = MultiByteCount;
1088 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1089 nReturn = 0;
1090 }
1091 else
1092 {
1093 /* Otherwise WideCharCount will be the number of converted characters */
1094 nReturn = WideCharCount;
1095 }
1096
1097 /* Convert the WideCharString to the MultiByteString */
1098 for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++)
1099 {
1100 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1101 }
1102
1103 return nReturn;
1104 }
1105 }
1106
1107 /**
1108 * @name IntIsLeadByte
1109 *
1110 * Internal function to detect if byte is lead byte in specific character
1111 * table.
1112 */
1113
1114 static BOOL
1115 WINAPI
1116 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
1117 {
1118 UINT i;
1119
1120 if (TableInfo->MaximumCharacterSize == 2)
1121 {
1122 for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2)
1123 {
1124 if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1])
1125 return TRUE;
1126 }
1127 }
1128
1129 return FALSE;
1130 }
1131
1132 /* PUBLIC FUNCTIONS ***********************************************************/
1133
1134 /**
1135 * @name GetNlsSectionName
1136 *
1137 * Construct a name of NLS section.
1138 *
1139 * @param CodePage
1140 * Code page number.
1141 * @param Base
1142 * Integer base used for converting to string. Usually set to 10.
1143 * @param Unknown
1144 * As the name suggests the meaning of this parameter is unknown.
1145 * The native version of Kernel32 passes it as the third parameter
1146 * to NlsConvertIntegerToString function, which is used for the
1147 * actual conversion of the code page number.
1148 * @param BaseName
1149 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1150 * @param Result
1151 * Buffer that will hold the constructed name.
1152 * @param ResultSize
1153 * Size of the buffer for the result.
1154 *
1155 * @return TRUE if the buffer was large enough and was filled with
1156 * the requested information, FALSE otherwise.
1157 *
1158 * @implemented
1159 */
1160
1161 BOOL
1162 WINAPI
1163 GetNlsSectionName(UINT CodePage,
1164 UINT Base,
1165 ULONG Unknown,
1166 LPSTR BaseName,
1167 LPSTR Result,
1168 ULONG ResultSize)
1169 {
1170 CHAR Integer[11];
1171
1172 if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
1173 return FALSE;
1174
1175 /*
1176 * If the name including the terminating NULL character doesn't
1177 * fit in the output buffer then fail.
1178 */
1179 if (strlen(Integer) + strlen(BaseName) >= ResultSize)
1180 return FALSE;
1181
1182 lstrcpyA(Result, BaseName);
1183 lstrcatA(Result, Integer);
1184
1185 return TRUE;
1186 }
1187
1188 /**
1189 * @name GetCPFileNameFromRegistry
1190 *
1191 * Get file name of code page definition file.
1192 *
1193 * @param CodePage
1194 * Code page number to get file name of.
1195 * @param FileName
1196 * Buffer that is filled with file name of successful return. Can
1197 * be set to NULL.
1198 * @param FileNameSize
1199 * Size of the buffer to hold file name in WCHARs.
1200 *
1201 * @return TRUE if the file name was retrieved, FALSE otherwise.
1202 *
1203 * @implemented
1204 */
1205
1206 BOOL
1207 WINAPI
1208 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
1209 {
1210 WCHAR ValueNameBuffer[11];
1211 UNICODE_STRING KeyName, ValueName;
1212 OBJECT_ATTRIBUTES ObjectAttributes;
1213 NTSTATUS Status;
1214 HANDLE KeyHandle;
1215 PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
1216 DWORD KvpiSize;
1217 BOOL bRetValue;
1218
1219 bRetValue = FALSE;
1220
1221 /* Convert the codepage number to string. */
1222 ValueName.Buffer = ValueNameBuffer;
1223 ValueName.MaximumLength = sizeof(ValueNameBuffer);
1224
1225 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
1226 return bRetValue;
1227
1228 /* Open the registry key containing file name mappings. */
1229 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
1230 L"CurrentControlSet\\Control\\Nls\\CodePage");
1231 InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
1232 NULL, NULL);
1233 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1234 if (!NT_SUCCESS(Status))
1235 {
1236 return bRetValue;
1237 }
1238
1239 /* Allocate buffer that will be used to query the value data. */
1240 KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (MAX_PATH * sizeof(WCHAR));
1241 Kvpi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KvpiSize);
1242 if (Kvpi == NULL)
1243 {
1244 NtClose(KeyHandle);
1245 return bRetValue;
1246 }
1247
1248 /* Query the file name for our code page. */
1249 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
1250 Kvpi, KvpiSize, &KvpiSize);
1251
1252 NtClose(KeyHandle);
1253
1254 /* Check if we succeded and the value is non-empty string. */
1255 if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
1256 Kvpi->DataLength > sizeof(WCHAR))
1257 {
1258 bRetValue = TRUE;
1259 if (FileName != NULL)
1260 {
1261 lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
1262 min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
1263 }
1264 }
1265
1266 /* free temporary buffer */
1267 HeapFree(GetProcessHeap(),0,Kvpi);
1268 return bRetValue;
1269 }
1270
1271 /**
1272 * @name IsValidCodePage
1273 *
1274 * Detect if specified code page is valid and present in the system.
1275 *
1276 * @param CodePage
1277 * Code page number to query.
1278 *
1279 * @return TRUE if code page is present.
1280 */
1281
1282 BOOL
1283 WINAPI
1284 IsValidCodePage(UINT CodePage)
1285 {
1286 if (CodePage == 0) return FALSE;
1287 if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
1288 return TRUE;
1289 if (IntGetLoadedCodePageEntry(CodePage))
1290 return TRUE;
1291 return GetCPFileNameFromRegistry(CodePage, NULL, 0);
1292 }
1293
1294 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1295 {
1296 if (dstlen > 0)
1297 {
1298 if (*index >= dstlen)
1299 return FALSE;
1300
1301 dst[*index] = character;
1302 }
1303
1304 (*index)++;
1305
1306 return TRUE;
1307 }
1308
1309 static INT Utf7ToWideChar(const char *src, int srclen, WCHAR *dst, int dstlen)
1310 {
1311 static const signed char base64_decoding_table[] =
1312 {
1313 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1314 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1315 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1316 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1317 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1318 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1319 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1320 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
1321 };
1322
1323 const char *source_end = src + srclen;
1324 int dest_index = 0;
1325
1326 DWORD byte_pair = 0;
1327 short offset = 0;
1328
1329 while (src < source_end)
1330 {
1331 if (*src == '+')
1332 {
1333 src++;
1334 if (src >= source_end)
1335 break;
1336
1337 if (*src == '-')
1338 {
1339 /* just a plus sign escaped as +- */
1340 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
1341 {
1342 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1343 return 0;
1344 }
1345 src++;
1346 continue;
1347 }
1348
1349 do
1350 {
1351 signed char sextet = *src;
1352 if (sextet == '-')
1353 {
1354 /* skip over the dash and end base64 decoding
1355 * the current, unfinished byte pair is discarded */
1356 src++;
1357 offset = 0;
1358 break;
1359 }
1360 if (sextet < 0)
1361 {
1362 /* the next character of src is < 0 and therefore not part of a base64 sequence
1363 * the current, unfinished byte pair is NOT discarded in this case
1364 * this is probably a bug in Windows */
1365 break;
1366 }
1367
1368 sextet = base64_decoding_table[sextet];
1369 if (sextet == -1)
1370 {
1371 /* -1 means that the next character of src is not part of a base64 sequence
1372 * in other words, all sextets in this base64 sequence have been processed
1373 * the current, unfinished byte pair is discarded */
1374 offset = 0;
1375 break;
1376 }
1377
1378 byte_pair = (byte_pair << 6) | sextet;
1379 offset += 6;
1380
1381 if (offset >= 16)
1382 {
1383 /* this byte pair is done */
1384 if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
1385 {
1386 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1387 return 0;
1388 }
1389 offset -= 16;
1390 }
1391
1392 src++;
1393 }
1394 while (src < source_end);
1395 }
1396 else
1397 {
1398 /* we have to convert to unsigned char in case *src < 0 */
1399 if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
1400 {
1401 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1402 return 0;
1403 }
1404 src++;
1405 }
1406 }
1407
1408 return dest_index;
1409 }
1410
1411 /**
1412 * @name MultiByteToWideChar
1413 *
1414 * Convert a multi-byte string to wide-charater equivalent.
1415 *
1416 * @param CodePage
1417 * Code page to be used to perform the conversion. It can be also
1418 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1419 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1420 * for thread active code page, CP_UTF7 or CP_UTF8).
1421 * @param Flags
1422 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1423 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1424 * @param MultiByteString
1425 * Input buffer.
1426 * @param MultiByteCount
1427 * Size of MultiByteString, or -1 if MultiByteString is NULL
1428 * terminated.
1429 * @param WideCharString
1430 * Output buffer.
1431 * @param WideCharCount
1432 * Size in WCHARs of WideCharString, or 0 if the caller just wants
1433 * to know how large WideCharString should be for a successful
1434 * conversion.
1435 *
1436 * @return Zero on error, otherwise the number of WCHARs written
1437 * in the WideCharString buffer.
1438 *
1439 * @implemented
1440 */
1441
1442 INT
1443 WINAPI
1444 MultiByteToWideChar(UINT CodePage,
1445 DWORD Flags,
1446 LPCSTR MultiByteString,
1447 INT MultiByteCount,
1448 LPWSTR WideCharString,
1449 INT WideCharCount)
1450 {
1451 /* Check the parameters. */
1452 if (MultiByteString == NULL ||
1453 MultiByteCount == 0 || WideCharCount < 0 ||
1454 (WideCharCount && (WideCharString == NULL ||
1455 (PVOID)MultiByteString == (PVOID)WideCharString)))
1456 {
1457 SetLastError(ERROR_INVALID_PARAMETER);
1458 return 0;
1459 }
1460
1461 /* Determine the input string length. */
1462 if (MultiByteCount < 0)
1463 {
1464 MultiByteCount = lstrlenA(MultiByteString) + 1;
1465 }
1466
1467 switch (CodePage)
1468 {
1469 case CP_UTF8:
1470 return IntMultiByteToWideCharUTF8(Flags,
1471 MultiByteString,
1472 MultiByteCount,
1473 WideCharString,
1474 WideCharCount);
1475
1476 case CP_UTF7:
1477 if (Flags)
1478 {
1479 SetLastError(ERROR_INVALID_FLAGS);
1480 return 0;
1481 }
1482 return Utf7ToWideChar(MultiByteString, MultiByteCount,
1483 WideCharString, WideCharCount);
1484
1485 case CP_SYMBOL:
1486 return IntMultiByteToWideCharSYMBOL(Flags,
1487 MultiByteString,
1488 MultiByteCount,
1489 WideCharString,
1490 WideCharCount);
1491 default:
1492 return IntMultiByteToWideCharCP(CodePage,
1493 Flags,
1494 MultiByteString,
1495 MultiByteCount,
1496 WideCharString,
1497 WideCharCount);
1498 }
1499 }
1500
1501 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
1502 {
1503 static const BOOL directly_encodable_table[] =
1504 {
1505 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1507 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1508 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1509 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1510 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1511 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1512 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7A */
1513 };
1514
1515 return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
1516 }
1517
1518 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
1519 {
1520 if (dstlen > 0)
1521 {
1522 if (*index >= dstlen)
1523 return FALSE;
1524
1525 dst[*index] = character;
1526 }
1527
1528 (*index)++;
1529
1530 return TRUE;
1531 }
1532
1533 static INT WideCharToUtf7(const WCHAR *src, int srclen, char *dst, int dstlen)
1534 {
1535 static const char base64_encoding_table[] =
1536 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1537
1538 const WCHAR *source_end = src + srclen;
1539 int dest_index = 0;
1540
1541 while (src < source_end)
1542 {
1543 if (*src == '+')
1544 {
1545 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1546 {
1547 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1548 return 0;
1549 }
1550 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1551 {
1552 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1553 return 0;
1554 }
1555 src++;
1556 }
1557 else if (utf7_can_directly_encode(*src))
1558 {
1559 if (!utf7_write_c(dst, dstlen, &dest_index, *src))
1560 {
1561 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1562 return 0;
1563 }
1564 src++;
1565 }
1566 else
1567 {
1568 unsigned int offset = 0;
1569 DWORD byte_pair = 0;
1570
1571 if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1572 {
1573 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1574 return 0;
1575 }
1576
1577 while (src < source_end && !utf7_can_directly_encode(*src))
1578 {
1579 byte_pair = (byte_pair << 16) | *src;
1580 offset += 16;
1581 while (offset >= 6)
1582 {
1583 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
1584 {
1585 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1586 return 0;
1587 }
1588 offset -= 6;
1589 }
1590 src++;
1591 }
1592
1593 if (offset)
1594 {
1595 /* Windows won't create a padded base64 character if there's no room for the - sign
1596 * as well ; this is probably a bug in Windows */
1597 if (dstlen > 0 && dest_index + 1 >= dstlen)
1598 {
1599 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1600 return 0;
1601 }
1602
1603 byte_pair <<= (6 - offset);
1604 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
1605 {
1606 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1607 return 0;
1608 }
1609 }
1610
1611 /* Windows always explicitly terminates the base64 sequence
1612 even though RFC 2152 (page 3, rule 2) does not require this */
1613 if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1614 {
1615 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1616 return 0;
1617 }
1618 }
1619 }
1620
1621 return dest_index;
1622 }
1623
1624 DWORD
1625 GetLocalisedText(DWORD dwResId, WCHAR *lpszDest, DWORD dwDestSize)
1626 {
1627 HRSRC hrsrc;
1628 LCID lcid;
1629 LANGID langId;
1630 DWORD dwId;
1631
1632 if (dwResId == 37)
1633 dwId = dwResId * 100;
1634 else
1635 dwId = dwResId;
1636
1637 lcid = GetUserDefaultLCID();
1638 lcid = ConvertDefaultLocale(lcid);
1639
1640 langId = LANGIDFROMLCID(lcid);
1641
1642 if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
1643 langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
1644
1645 hrsrc = FindResourceExW(hCurrentModule,
1646 (LPWSTR)RT_STRING,
1647 MAKEINTRESOURCEW((dwId >> 4) + 1),
1648 langId);
1649
1650 /* english fallback */
1651 if(!hrsrc)
1652 {
1653 hrsrc = FindResourceExW(hCurrentModule,
1654 (LPWSTR)RT_STRING,
1655 MAKEINTRESOURCEW((dwId >> 4) + 1),
1656 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
1657 }
1658
1659 if (hrsrc)
1660 {
1661 HGLOBAL hmem = LoadResource(hCurrentModule, hrsrc);
1662
1663 if (hmem)
1664 {
1665 const WCHAR *p;
1666 unsigned int i;
1667 unsigned int len;
1668
1669 p = LockResource(hmem);
1670
1671 for (i = 0; i < (dwId & 0x0f); i++) p += *p + 1;
1672
1673 if(dwDestSize == 0)
1674 return *p + 1;
1675
1676 len = *p * sizeof(WCHAR);
1677
1678 if(len + sizeof(WCHAR) > dwDestSize)
1679 {
1680 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1681 return FALSE;
1682 }
1683
1684 memcpy(lpszDest, p + 1, len);
1685 lpszDest[*p] = '\0';
1686
1687 return TRUE;
1688 }
1689 }
1690
1691 DPRINT1("Resource not found: dwResId = %lu\n", dwResId);
1692 SetLastError(ERROR_INVALID_PARAMETER);
1693 return FALSE;
1694 }
1695
1696 /*
1697 * @implemented
1698 */
1699 BOOL
1700 WINAPI
1701 GetCPInfo(UINT CodePage,
1702 LPCPINFO CodePageInfo)
1703 {
1704 PCODEPAGE_ENTRY CodePageEntry;
1705
1706 if (!CodePageInfo)
1707 {
1708 SetLastError(ERROR_INVALID_PARAMETER);
1709 return FALSE;
1710 }
1711
1712 CodePageEntry = IntGetCodePageEntry(CodePage);
1713 if (CodePageEntry == NULL)
1714 {
1715 switch(CodePage)
1716 {
1717 case CP_UTF7:
1718 case CP_UTF8:
1719 CodePageInfo->DefaultChar[0] = 0x3f;
1720 CodePageInfo->DefaultChar[1] = 0;
1721 CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1722 CodePageInfo->MaxCharSize = (CodePage == CP_UTF7) ? 5 : 4;
1723 return TRUE;
1724 }
1725
1726 DPRINT1("Invalid CP!: %lx\n", CodePage);
1727 SetLastError( ERROR_INVALID_PARAMETER );
1728 return FALSE;
1729 }
1730
1731 if (CodePageEntry->CodePageTable.DefaultChar & 0xff00)
1732 {
1733 CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8;
1734 CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff;
1735 }
1736 else
1737 {
1738 CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff;
1739 CodePageInfo->DefaultChar[1] = 0;
1740 }
1741
1742 if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2)
1743 memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte));
1744 else
1745 CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1746
1747 return TRUE;
1748 }
1749
1750 /*
1751 * @implemented
1752 */
1753 BOOL
1754 WINAPI
1755 GetCPInfoExW(UINT CodePage,
1756 DWORD dwFlags,
1757 LPCPINFOEXW lpCPInfoEx)
1758 {
1759 if (!GetCPInfo(CodePage, (LPCPINFO) lpCPInfoEx))
1760 return FALSE;
1761
1762 switch(CodePage)
1763 {
1764 case CP_UTF7:
1765 {
1766 lpCPInfoEx->CodePage = CP_UTF7;
1767 lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1768 return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
1769 }
1770 break;
1771
1772 case CP_UTF8:
1773 {
1774 lpCPInfoEx->CodePage = CP_UTF8;
1775 lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1776 return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
1777 }
1778
1779 default:
1780 {
1781 PCODEPAGE_ENTRY CodePageEntry;
1782
1783 CodePageEntry = IntGetCodePageEntry(CodePage);
1784 if (CodePageEntry == NULL)
1785 {
1786 DPRINT1("Could not get CodePage Entry! CodePageEntry = 0\n");
1787 SetLastError(ERROR_INVALID_PARAMETER);
1788 return FALSE;
1789 }
1790
1791 lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage;
1792 lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar;
1793 return GetLocalisedText(CodePageEntry->CodePageTable.CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
1794 }
1795 break;
1796 }
1797 }
1798
1799
1800 /*
1801 * @implemented
1802 */
1803 BOOL
1804 WINAPI
1805 GetCPInfoExA(UINT CodePage,
1806 DWORD dwFlags,
1807 LPCPINFOEXA lpCPInfoEx)
1808 {
1809 CPINFOEXW CPInfo;
1810
1811 if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo))
1812 return FALSE;
1813
1814 /* the layout is the same except for CodePageName */
1815 memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA));
1816
1817 WideCharToMultiByte(CP_ACP,
1818 0,
1819 CPInfo.CodePageName,
1820 -1,
1821 lpCPInfoEx->CodePageName,
1822 sizeof(lpCPInfoEx->CodePageName),
1823 NULL,
1824 NULL);
1825 return TRUE;
1826 }
1827
1828 /**
1829 * @name WideCharToMultiByte
1830 *
1831 * Convert a wide-charater string to closest multi-byte equivalent.
1832 *
1833 * @param CodePage
1834 * Code page to be used to perform the conversion. It can be also
1835 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
1836 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1837 * for thread active code page, CP_UTF7 or CP_UTF8).
1838 * @param Flags
1839 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
1840 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
1841 * @param WideCharString
1842 * Points to the wide-character string to be converted.
1843 * @param WideCharCount
1844 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
1845 * know how large WideCharString should be for a successful conversion.
1846 * @param MultiByteString
1847 * Points to the buffer to receive the translated string.
1848 * @param MultiByteCount
1849 * Specifies the size in bytes of the buffer pointed to by the
1850 * MultiByteString parameter. If this value is zero, the function
1851 * returns the number of bytes required for the buffer.
1852 * @param DefaultChar
1853 * Points to the character used if a wide character cannot be
1854 * represented in the specified code page. If this parameter is
1855 * NULL, a system default value is used.
1856 * @param UsedDefaultChar
1857 * Points to a flag that indicates whether a default character was
1858 * used. This parameter can be NULL.
1859 *
1860 * @return Zero on error, otherwise the number of bytes written in the
1861 * MultiByteString buffer. Or the number of bytes needed for
1862 * the MultiByteString buffer if MultiByteCount is zero.
1863 *
1864 * @implemented
1865 */
1866
1867 INT
1868 WINAPI
1869 WideCharToMultiByte(UINT CodePage,
1870 DWORD Flags,
1871 LPCWSTR WideCharString,
1872 INT WideCharCount,
1873 LPSTR MultiByteString,
1874 INT MultiByteCount,
1875 LPCSTR DefaultChar,
1876 LPBOOL UsedDefaultChar)
1877 {
1878 /* Check the parameters. */
1879 if (WideCharString == NULL ||
1880 WideCharCount == 0 ||
1881 (MultiByteString == NULL && MultiByteCount > 0) ||
1882 (PVOID)WideCharString == (PVOID)MultiByteString ||
1883 MultiByteCount < 0)
1884 {
1885 SetLastError(ERROR_INVALID_PARAMETER);
1886 return 0;
1887 }
1888
1889 /* Determine the input string length. */
1890 if (WideCharCount < 0)
1891 {
1892 WideCharCount = lstrlenW(WideCharString) + 1;
1893 }
1894
1895 switch (CodePage)
1896 {
1897 case CP_UTF8:
1898 if (DefaultChar != NULL || UsedDefaultChar != NULL)
1899 {
1900 SetLastError(ERROR_INVALID_PARAMETER);
1901 return 0;
1902 }
1903 return IntWideCharToMultiByteUTF8(CodePage,
1904 Flags,
1905 WideCharString,
1906 WideCharCount,
1907 MultiByteString,
1908 MultiByteCount,
1909 DefaultChar,
1910 UsedDefaultChar);
1911
1912 case CP_UTF7:
1913 if (DefaultChar != NULL || UsedDefaultChar != NULL)
1914 {
1915 SetLastError(ERROR_INVALID_PARAMETER);
1916 return 0;
1917 }
1918 if (Flags)
1919 {
1920 SetLastError(ERROR_INVALID_FLAGS);
1921 return 0;
1922 }
1923 return WideCharToUtf7(WideCharString, WideCharCount,
1924 MultiByteString, MultiByteCount);
1925
1926 case CP_SYMBOL:
1927 if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL))
1928 {
1929 SetLastError(ERROR_INVALID_PARAMETER);
1930 return 0;
1931 }
1932 return IntWideCharToMultiByteSYMBOL(Flags,
1933 WideCharString,
1934 WideCharCount,
1935 MultiByteString,
1936 MultiByteCount);
1937
1938 default:
1939 return IntWideCharToMultiByteCP(CodePage,
1940 Flags,
1941 WideCharString,
1942 WideCharCount,
1943 MultiByteString,
1944 MultiByteCount,
1945 DefaultChar,
1946 UsedDefaultChar);
1947 }
1948 }
1949
1950 /**
1951 * @name GetACP
1952 *
1953 * Get active ANSI code page number.
1954 *
1955 * @implemented
1956 */
1957
1958 UINT
1959 WINAPI
1960 GetACP(VOID)
1961 {
1962 return AnsiCodePage.CodePageTable.CodePage;
1963 }
1964
1965 /**
1966 * @name GetOEMCP
1967 *
1968 * Get active OEM code page number.
1969 *
1970 * @implemented
1971 */
1972
1973 UINT
1974 WINAPI
1975 GetOEMCP(VOID)
1976 {
1977 return OemCodePage.CodePageTable.CodePage;
1978 }
1979
1980 /**
1981 * @name IsDBCSLeadByteEx
1982 *
1983 * Determine if passed byte is lead byte in specified code page.
1984 *
1985 * @implemented
1986 */
1987
1988 BOOL
1989 WINAPI
1990 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
1991 {
1992 PCODEPAGE_ENTRY CodePageEntry;
1993
1994 CodePageEntry = IntGetCodePageEntry(CodePage);
1995 if (CodePageEntry != NULL)
1996 return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
1997
1998 SetLastError(ERROR_INVALID_PARAMETER);
1999 return FALSE;
2000 }
2001
2002 /**
2003 * @name IsDBCSLeadByteEx
2004 *
2005 * Determine if passed byte is lead byte in current ANSI code page.
2006 *
2007 * @implemented
2008 */
2009
2010 BOOL
2011 WINAPI
2012 IsDBCSLeadByte(BYTE TestByte)
2013 {
2014 return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
2015 }
2016
2017 /*
2018 * @unimplemented
2019 */
2020 NTSTATUS WINAPI CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor,ULONG Size,ULONG AccessMask)
2021 {
2022 STUB;
2023 return 0;
2024 }
2025
2026 /*
2027 * @unimplemented
2028 */
2029 BOOL WINAPI IsValidUILanguage(LANGID langid)
2030 {
2031 STUB;
2032 return 0;
2033 }
2034
2035 /*
2036 * @unimplemented
2037 */
2038 VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2)
2039 {
2040 STUB;
2041 }
2042
2043 /*
2044 * @unimplemented
2045 */
2046 UINT WINAPI SetCPGlobal(UINT CodePage)
2047 {
2048 STUB;
2049 return 0;
2050 }
2051
2052 /*
2053 * @unimplemented
2054 */
2055 BOOL
2056 WINAPI
2057 ValidateLCType(int a1, unsigned int a2, int a3, int a4)
2058 {
2059 STUB;
2060 return FALSE;
2061 }
2062
2063 /*
2064 * @unimplemented
2065 */
2066 BOOL
2067 WINAPI
2068 NlsResetProcessLocale(VOID)
2069 {
2070 STUB;
2071 return TRUE;
2072 }
2073
2074 /*
2075 * @unimplemented
2076 */
2077 VOID
2078 WINAPI
2079 GetDefaultSortkeySize(LPVOID lpUnknown)
2080 {
2081 STUB;
2082 lpUnknown = NULL;
2083 }
2084
2085 /*
2086 * @unimplemented
2087 */
2088 VOID
2089 WINAPI
2090 GetLinguistLangSize(LPVOID lpUnknown)
2091 {
2092 STUB;
2093 lpUnknown = NULL;
2094 }
2095
2096 /*
2097 * @unimplemented
2098 */
2099 BOOL
2100 WINAPI
2101 ValidateLocale(IN ULONG LocaleId)
2102 {
2103 STUB;
2104 return TRUE;
2105 }
2106
2107 /*
2108 * @unimplemented
2109 */
2110 ULONG
2111 WINAPI
2112 NlsGetCacheUpdateCount(VOID)
2113 {
2114 STUB;
2115 return 0;
2116 }
2117
2118 /*
2119 * @unimplemented
2120 */
2121 BOOL
2122 WINAPI
2123 IsNLSDefinedString(IN NLS_FUNCTION Function,
2124 IN DWORD dwFlags,
2125 IN LPNLSVERSIONINFO lpVersionInformation,
2126 IN LPCWSTR lpString,
2127 IN INT cchStr)
2128 {
2129 STUB;
2130 return TRUE;
2131 }
2132
2133 /*
2134 * @unimplemented
2135 */
2136 BOOL
2137 WINAPI
2138 GetNLSVersion(IN NLS_FUNCTION Function,
2139 IN LCID Locale,
2140 IN OUT LPNLSVERSIONINFO lpVersionInformation)
2141 {
2142 STUB;
2143 return TRUE;
2144 }
2145
2146 /* EOF */