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