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