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