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