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