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