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