xref: /reactos/dll/win32/kernel32/winnls/string/nls.c (revision d2aeaba5)
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  *                  Katayama Hirofumi MZ
11  * UPDATE HISTORY:
12  *                  Created 24/08/2004
13  */
14 
15 /* INCLUDES *******************************************************************/
16 
17 #include <k32.h>
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 /* GLOBAL VARIABLES ***********************************************************/
23 
24 /* Sequence length based on the first character. */
25 static const char UTF8Length[128] =
26 {
27    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
28    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
29    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
30    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
31    0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
32    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
33    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
34    3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0  /* 0xF0 - 0xFF */
35 };
36 
37 /* First byte mask depending on UTF-8 sequence length. */
38 static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
39 
40 /* UTF-8 length to lower bound */
41 static const unsigned long UTF8LBound[] =
42     {0, 0x80, 0x800, 0x10000, 0x200000, 0x2000000, 0xFFFFFFFF};
43 
44 /* FIXME: Change to HASH table or linear array. */
45 static LIST_ENTRY CodePageListHead;
46 static CODEPAGE_ENTRY AnsiCodePage;
47 static CODEPAGE_ENTRY OemCodePage;
48 static RTL_CRITICAL_SECTION CodePageListLock;
49 
50 /* FORWARD DECLARATIONS *******************************************************/
51 
52 BOOL WINAPI
53 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
54                   LPSTR BaseName, LPSTR Result, ULONG ResultSize);
55 
56 BOOL WINAPI
57 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
58 
59 NTSTATUS
60 CreateNlsDirectorySecurity(_Out_ PSECURITY_DESCRIPTOR *NlsSecurityDescriptor);
61 
62 NTSTATUS WINAPI
63 CreateNlsSecurityDescriptor(_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, _In_ SIZE_T DescriptorSize, _In_ ULONG AccessMask);
64 
65 /* PRIVATE FUNCTIONS **********************************************************/
66 
67 /**
68  * @name NlsInit
69  *
70  * Internal NLS related stuff initialization.
71  */
72 
73 BOOL
74 FASTCALL
75 NlsInit(VOID)
76 {
77     UNICODE_STRING DirName;
78     OBJECT_ATTRIBUTES ObjectAttributes;
79     PSECURITY_DESCRIPTOR NlsDirSd;
80     HANDLE Handle;
81     NTSTATUS Status;
82 
83     InitializeListHead(&CodePageListHead);
84     RtlInitializeCriticalSection(&CodePageListLock);
85 
86     /*
87      * FIXME: Eventually this should be done only for the NLS Server
88      * process, but since we don't have anything like that (yet?) we
89      * always try to create the "\Nls" directory here.
90      */
91     RtlInitUnicodeString(&DirName, L"\\Nls");
92 
93     /* Create a security descriptor for NLS directory */
94     Status = CreateNlsDirectorySecurity(&NlsDirSd);
95     if (!NT_SUCCESS(Status))
96     {
97         DPRINT1("Failed to create NLS directory security (Status 0x%08x)\n", Status);
98         return FALSE;
99     }
100 
101     InitializeObjectAttributes(&ObjectAttributes,
102                                &DirName,
103                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
104                                NULL,
105                                NlsDirSd);
106 
107     if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT, &ObjectAttributes)))
108     {
109         NtClose(Handle);
110     }
111 
112     /* Setup ANSI code page. */
113     AnsiCodePage.SectionHandle = NULL;
114     AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
115 
116     RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
117                          &AnsiCodePage.CodePageTable);
118     AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage;
119 
120     InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
121 
122     /* Setup OEM code page. */
123     OemCodePage.SectionHandle = NULL;
124     OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
125 
126     RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
127                          &OemCodePage.CodePageTable);
128     OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage;
129     InsertTailList(&CodePageListHead, &OemCodePage.Entry);
130 
131     RtlFreeHeap(RtlGetProcessHeap(), 0, NlsDirSd);
132     return TRUE;
133 }
134 
135 /**
136  * @name NlsUninit
137  *
138  * Internal NLS related stuff uninitialization.
139  */
140 
141 VOID
142 FASTCALL
143 NlsUninit(VOID)
144 {
145     PCODEPAGE_ENTRY Current;
146 
147     /* Delete the code page list. */
148     while (!IsListEmpty(&CodePageListHead))
149     {
150         Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
151         if (Current->SectionHandle != NULL)
152         {
153             UnmapViewOfFile(Current->SectionMapping);
154             NtClose(Current->SectionHandle);
155         }
156         RemoveHeadList(&CodePageListHead);
157     }
158     RtlDeleteCriticalSection(&CodePageListLock);
159 }
160 
161 /**
162  * @name IntGetLoadedCodePageEntry
163  *
164  * Internal function to get structure containing a code page information
165  * of code page that is already loaded.
166  *
167  * @param CodePage
168  *        Number of the code page. Special values like CP_OEMCP, CP_ACP
169  *        or CP_UTF8 aren't allowed.
170  *
171  * @return Code page entry or NULL if the specified code page hasn't
172  *         been loaded yet.
173  */
174 
175 PCODEPAGE_ENTRY
176 FASTCALL
177 IntGetLoadedCodePageEntry(UINT CodePage)
178 {
179     LIST_ENTRY *CurrentEntry;
180     PCODEPAGE_ENTRY Current;
181 
182     RtlEnterCriticalSection(&CodePageListLock);
183     for (CurrentEntry = CodePageListHead.Flink;
184          CurrentEntry != &CodePageListHead;
185          CurrentEntry = CurrentEntry->Flink)
186     {
187         Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
188         if (Current->CodePage == CodePage)
189         {
190             RtlLeaveCriticalSection(&CodePageListLock);
191             return Current;
192         }
193     }
194     RtlLeaveCriticalSection(&CodePageListLock);
195 
196     return NULL;
197 }
198 
199 /**
200  * @name IntGetCodePageEntry
201  *
202  * Internal function to get structure containing a code page information.
203  *
204  * @param CodePage
205  *        Number of the code page. Special values like CP_OEMCP, CP_ACP
206  *        or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
207  *
208  * @return Code page entry.
209  */
210 
211 PCODEPAGE_ENTRY
212 FASTCALL
213 IntGetCodePageEntry(UINT CodePage)
214 {
215     CHAR SectionName[40];
216     NTSTATUS Status;
217     HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle;
218     PBYTE SectionMapping;
219     OBJECT_ATTRIBUTES ObjectAttributes;
220     ANSI_STRING AnsiName;
221     UNICODE_STRING UnicodeName;
222     WCHAR FileName[MAX_PATH + 1];
223     UINT FileNamePos;
224     PCODEPAGE_ENTRY CodePageEntry;
225     PSECURITY_DESCRIPTOR NlsSd;
226     if (CodePage == CP_ACP)
227     {
228         return &AnsiCodePage;
229     }
230     else if (CodePage == CP_OEMCP)
231     {
232         return &OemCodePage;
233     }
234     else if (CodePage == CP_THREAD_ACP)
235     {
236         if (!GetLocaleInfoW(GetThreadLocale(),
237                             LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
238                             (WCHAR *)&CodePage,
239                             sizeof(CodePage) / sizeof(WCHAR)))
240         {
241             /* Last error is set by GetLocaleInfoW. */
242             return NULL;
243         }
244         if (CodePage == 0)
245             return &AnsiCodePage;
246     }
247     else if (CodePage == CP_MACCP)
248     {
249         if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT,
250                             LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
251                             (WCHAR *)&CodePage,
252                             sizeof(CodePage) / sizeof(WCHAR)))
253         {
254             /* Last error is set by GetLocaleInfoW. */
255             return NULL;
256         }
257     }
258 
259     /* Try searching for loaded page first. */
260     CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
261     if (CodePageEntry != NULL)
262     {
263         return CodePageEntry;
264     }
265 
266     /*
267      * Yes, we really want to lock here. Otherwise it can happen that
268      * two parallel requests will try to get the entry for the same
269      * code page and we would load it twice.
270      */
271     RtlEnterCriticalSection(&CodePageListLock);
272 
273     /* Generate the section name. */
274     if (!GetNlsSectionName(CodePage,
275                            10,
276                            0,
277                            "\\Nls\\NlsSectionCP",
278                            SectionName,
279                            sizeof(SectionName)))
280     {
281         RtlLeaveCriticalSection(&CodePageListLock);
282         return NULL;
283     }
284 
285     RtlInitAnsiString(&AnsiName, SectionName);
286     RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
287 
288     /*
289      * FIXME: IntGetCodePageEntry should not create any security
290      * descriptor here but instead this responsibility should be
291      * assigned to Base Server API (aka basesrv.dll). That is,
292      * kernel32 must instruct basesrv.dll on creating NLS section
293      * names that do not exist through API message communication.
294      * However since we do not do that, let the kernel32 do the job
295      * by assigning security to NLS section names for the time being...
296      */
297     Status = CreateNlsSecurityDescriptor(&NlsSd, sizeof(SECURITY_DESCRIPTOR), SECTION_MAP_READ);
298     if (!NT_SUCCESS(Status))
299     {
300         RtlLeaveCriticalSection(&CodePageListLock);
301         return NULL;
302     }
303 
304     InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NlsSd);
305 
306     /* Try to open the section first */
307     Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
308 
309     /* If the section doesn't exist, try to create it. */
310     if (Status == STATUS_UNSUCCESSFUL ||
311         Status == STATUS_OBJECT_NAME_NOT_FOUND ||
312         Status == STATUS_OBJECT_PATH_NOT_FOUND)
313     {
314         FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
315         if (GetCPFileNameFromRegistry(CodePage,
316                                       FileName + FileNamePos + 1,
317                                       MAX_PATH - FileNamePos - 1))
318         {
319             FileName[FileNamePos] = L'\\';
320             FileName[MAX_PATH] = 0;
321             FileHandle = CreateFileW(FileName,
322                                      FILE_GENERIC_READ,
323                                      FILE_SHARE_READ,
324                                      NULL,
325                                      OPEN_EXISTING,
326                                      0,
327                                      NULL);
328 
329             Status = NtCreateSection(&SectionHandle,
330                                      SECTION_MAP_READ,
331                                      &ObjectAttributes,
332                                      NULL,
333                                      PAGE_READONLY,
334                                      SEC_COMMIT,
335                                      FileHandle);
336 
337             /* HACK: Check if another process was faster
338              * and already created this section. See bug 3626 for details */
339             if (Status == STATUS_OBJECT_NAME_COLLISION)
340             {
341                 /* Close the file then */
342                 NtClose(FileHandle);
343 
344                 /* And open the section */
345                 Status = NtOpenSection(&SectionHandle,
346                                        SECTION_MAP_READ,
347                                        &ObjectAttributes);
348             }
349         }
350     }
351     RtlFreeUnicodeString(&UnicodeName);
352     HeapFree(GetProcessHeap(), 0, NlsSd);
353 
354     if (!NT_SUCCESS(Status))
355     {
356         RtlLeaveCriticalSection(&CodePageListLock);
357         return NULL;
358     }
359 
360     SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
361     if (SectionMapping == NULL)
362     {
363         NtClose(SectionHandle);
364         RtlLeaveCriticalSection(&CodePageListLock);
365         return NULL;
366     }
367 
368     CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
369     if (CodePageEntry == NULL)
370     {
371         NtClose(SectionHandle);
372         RtlLeaveCriticalSection(&CodePageListLock);
373         return NULL;
374     }
375 
376     CodePageEntry->CodePage = CodePage;
377     CodePageEntry->SectionHandle = SectionHandle;
378     CodePageEntry->SectionMapping = SectionMapping;
379 
380     RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
381 
382     /* Insert the new entry to list and unlock. Uff. */
383     InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
384     RtlLeaveCriticalSection(&CodePageListLock);
385 
386     return CodePageEntry;
387 }
388 
389 /**
390  * @name IntMultiByteToWideCharUTF8
391  *
392  * Internal version of MultiByteToWideChar for UTF8.
393  *
394  * @note We use Win10's behaviour due to security reason.
395  *
396  * @see MultiByteToWideChar
397  */
398 static
399 INT
400 WINAPI
401 IntMultiByteToWideCharUTF8(DWORD Flags,
402                            LPCSTR MultiByteString,
403                            INT MultiByteCount,
404                            LPWSTR WideCharString,
405                            INT WideCharCount)
406 {
407     LPCSTR MbsEnd, MbsPtrSave;
408     UCHAR Char, TrailLength;
409     WCHAR WideChar;
410     LONG Count;
411     BOOL CharIsValid, StringIsValid = TRUE;
412     const WCHAR InvalidChar = 0xFFFD;
413 
414     if (Flags != 0 && Flags != MB_ERR_INVALID_CHARS)
415     {
416         SetLastError(ERROR_INVALID_FLAGS);
417         return 0;
418     }
419 
420     /* Does caller query for output buffer size? */
421     if (WideCharCount == 0)
422     {
423         /* validate and count the wide characters */
424         MbsEnd = MultiByteString + MultiByteCount;
425         for (; MultiByteString < MbsEnd; WideCharCount++)
426         {
427             Char = *MultiByteString++;
428             if (Char < 0x80)
429             {
430                 TrailLength = 0;
431                 continue;
432             }
433             if ((Char & 0xC0) == 0x80)
434             {
435                 TrailLength = 0;
436                 StringIsValid = FALSE;
437                 continue;
438             }
439 
440             TrailLength = UTF8Length[Char - 0x80];
441             if (TrailLength == 0)
442             {
443                 StringIsValid = FALSE;
444                 continue;
445             }
446 
447             CharIsValid = TRUE;
448             MbsPtrSave = MultiByteString;
449             WideChar = Char & UTF8Mask[TrailLength];
450 
451             while (TrailLength && MultiByteString < MbsEnd)
452             {
453                 if ((*MultiByteString & 0xC0) != 0x80)
454                 {
455                     CharIsValid = StringIsValid = FALSE;
456                     break;
457                 }
458 
459                 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
460                 TrailLength--;
461             }
462 
463             if (!CharIsValid || WideChar < UTF8LBound[UTF8Length[Char - 0x80]])
464             {
465                 MultiByteString = MbsPtrSave;
466             }
467         }
468 
469         if (TrailLength)
470         {
471             WideCharCount++;
472             StringIsValid = FALSE;
473         }
474 
475         if (Flags == MB_ERR_INVALID_CHARS && !StringIsValid)
476         {
477             SetLastError(ERROR_NO_UNICODE_TRANSLATION);
478             return 0;
479         }
480 
481         return WideCharCount;
482     }
483 
484     /* convert */
485     MbsEnd = MultiByteString + MultiByteCount;
486     for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
487     {
488         Char = *MultiByteString++;
489         if (Char < 0x80)
490         {
491             *WideCharString++ = Char;
492             TrailLength = 0;
493             continue;
494         }
495         if ((Char & 0xC0) == 0x80)
496         {
497             *WideCharString++ = InvalidChar;
498             TrailLength = 0;
499             StringIsValid = FALSE;
500             continue;
501         }
502 
503         TrailLength = UTF8Length[Char - 0x80];
504         if (TrailLength == 0)
505         {
506             *WideCharString++ = InvalidChar;
507             StringIsValid = FALSE;
508             continue;
509         }
510 
511         CharIsValid = TRUE;
512         MbsPtrSave = MultiByteString;
513         WideChar = Char & UTF8Mask[TrailLength];
514 
515         while (TrailLength && MultiByteString < MbsEnd)
516         {
517             if ((*MultiByteString & 0xC0) != 0x80)
518             {
519                 CharIsValid = StringIsValid = FALSE;
520                 break;
521             }
522 
523             WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
524             TrailLength--;
525         }
526 
527         if (CharIsValid && UTF8LBound[UTF8Length[Char - 0x80]] <= WideChar)
528         {
529             *WideCharString++ = WideChar;
530         }
531         else
532         {
533             *WideCharString++ = InvalidChar;
534             MultiByteString = MbsPtrSave;
535             StringIsValid = FALSE;
536         }
537     }
538 
539     if (TrailLength && Count < WideCharCount && MultiByteString < MbsEnd)
540     {
541         *WideCharString = InvalidChar;
542         WideCharCount++;
543     }
544 
545     if (MultiByteString < MbsEnd)
546     {
547         SetLastError(ERROR_INSUFFICIENT_BUFFER);
548         return 0;
549     }
550 
551     if (Flags == MB_ERR_INVALID_CHARS && (!StringIsValid || TrailLength))
552     {
553         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
554         return 0;
555     }
556 
557     return Count;
558 }
559 
560 /**
561  * @name IntMultiByteToWideCharCP
562  *
563  * Internal version of MultiByteToWideChar for code page tables.
564  *
565  * @see MultiByteToWideChar
566  * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
567  *       DBCS codepages.
568  */
569 
570 static
571 INT
572 WINAPI
573 IntMultiByteToWideCharCP(UINT CodePage,
574                          DWORD Flags,
575                          LPCSTR MultiByteString,
576                          INT MultiByteCount,
577                          LPWSTR WideCharString,
578                          INT WideCharCount)
579 {
580     PCODEPAGE_ENTRY CodePageEntry;
581     PCPTABLEINFO CodePageTable;
582     PUSHORT MultiByteTable;
583     LPCSTR TempString;
584     INT TempLength;
585     USHORT WideChar;
586 
587     /* Get code page table. */
588     CodePageEntry = IntGetCodePageEntry(CodePage);
589     if (CodePageEntry == NULL)
590     {
591         SetLastError(ERROR_INVALID_PARAMETER);
592         return 0;
593     }
594 
595     CodePageTable = &CodePageEntry->CodePageTable;
596 
597     /* If MB_USEGLYPHCHARS flag present and glyph table present */
598     if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256])
599     {
600         /* Use glyph table */
601         MultiByteTable = CodePageTable->MultiByteTable + 256 + 1;
602     }
603     else
604     {
605         MultiByteTable = CodePageTable->MultiByteTable;
606     }
607 
608     /* Different handling for DBCS code pages. */
609     if (CodePageTable->DBCSCodePage)
610     {
611         UCHAR Char;
612         USHORT DBCSOffset;
613         LPCSTR MbsEnd = MultiByteString + MultiByteCount;
614         INT Count;
615 
616         if (Flags & MB_ERR_INVALID_CHARS)
617         {
618             TempString = MultiByteString;
619 
620             while (TempString < MbsEnd)
621             {
622                 DBCSOffset = CodePageTable->DBCSOffsets[(UCHAR)*TempString];
623 
624                 if (DBCSOffset)
625                 {
626                     /* If lead byte is presented, but behind it there is no symbol */
627                     if (((TempString + 1) == MbsEnd) || (*(TempString + 1) == 0))
628                     {
629                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
630                         return 0;
631                     }
632 
633                     WideChar = CodePageTable->DBCSOffsets[DBCSOffset + *(TempString + 1)];
634 
635                     if (WideChar == CodePageTable->UniDefaultChar &&
636                         MAKEWORD(*(TempString + 1), *TempString) != CodePageTable->TransUniDefaultChar)
637                     {
638                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
639                         return 0;
640                     }
641 
642                     TempString++;
643                 }
644                 else
645                 {
646                     WideChar = MultiByteTable[(UCHAR)*TempString];
647 
648                     if ((WideChar == CodePageTable->UniDefaultChar &&
649                         *TempString != CodePageTable->TransUniDefaultChar) ||
650                         /* "Private Use" characters */
651                         (WideChar >= 0xE000 && WideChar <= 0xF8FF))
652                     {
653                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
654                         return 0;
655                     }
656                 }
657 
658                 TempString++;
659             }
660         }
661 
662         /* Does caller query for output buffer size? */
663         if (WideCharCount == 0)
664         {
665             for (; MultiByteString < MbsEnd; WideCharCount++)
666             {
667                 Char = *MultiByteString++;
668 
669                 DBCSOffset = CodePageTable->DBCSOffsets[Char];
670 
671                 if (!DBCSOffset)
672                     continue;
673 
674                 if (MultiByteString < MbsEnd)
675                     MultiByteString++;
676             }
677 
678             return WideCharCount;
679         }
680 
681         for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
682         {
683             Char = *MultiByteString++;
684 
685             DBCSOffset = CodePageTable->DBCSOffsets[Char];
686 
687             if (!DBCSOffset)
688             {
689                 *WideCharString++ = MultiByteTable[Char];
690                 continue;
691             }
692 
693             if (MultiByteString == MbsEnd || *MultiByteString == 0)
694             {
695                 *WideCharString++ = CodePageTable->UniDefaultChar;
696             }
697             else
698             {
699                 *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + (UCHAR)*MultiByteString++];
700             }
701         }
702 
703         if (MultiByteString < MbsEnd)
704         {
705             SetLastError(ERROR_INSUFFICIENT_BUFFER);
706             return 0;
707         }
708 
709         return Count;
710     }
711     else /* SBCS code page */
712     {
713         /* Check for invalid characters. */
714         if (Flags & MB_ERR_INVALID_CHARS)
715         {
716             for (TempString = MultiByteString, TempLength = MultiByteCount;
717                  TempLength > 0;
718                  TempString++, TempLength--)
719             {
720                 WideChar = MultiByteTable[(UCHAR)*TempString];
721 
722                 if ((WideChar == CodePageTable->UniDefaultChar &&
723                     *TempString != CodePageTable->TransUniDefaultChar) ||
724                     /* "Private Use" characters */
725                     (WideChar >= 0xE000 && WideChar <= 0xF8FF))
726                 {
727                     SetLastError(ERROR_NO_UNICODE_TRANSLATION);
728                     return 0;
729                 }
730             }
731         }
732 
733         /* Does caller query for output buffer size? */
734         if (WideCharCount == 0)
735             return MultiByteCount;
736 
737         /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
738         for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount;
739             TempLength > 0;
740             MultiByteString++, TempLength--)
741         {
742             *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString];
743         }
744 
745         /* Adjust buffer size. Wine trick ;-) */
746         if (WideCharCount < MultiByteCount)
747         {
748             MultiByteCount = WideCharCount;
749             SetLastError(ERROR_INSUFFICIENT_BUFFER);
750             return 0;
751         }
752         return MultiByteCount;
753     }
754 }
755 
756 /**
757  * @name IntMultiByteToWideCharSYMBOL
758  *
759  * Internal version of MultiByteToWideChar for SYMBOL.
760  *
761  * @see MultiByteToWideChar
762  */
763 
764 static
765 INT
766 WINAPI
767 IntMultiByteToWideCharSYMBOL(DWORD Flags,
768                              LPCSTR MultiByteString,
769                              INT MultiByteCount,
770                              LPWSTR WideCharString,
771                              INT WideCharCount)
772 {
773     LONG Count;
774     UCHAR Char;
775     INT WideCharMaxLen;
776 
777 
778     if (Flags != 0)
779     {
780         SetLastError(ERROR_INVALID_FLAGS);
781         return 0;
782     }
783 
784     if (WideCharCount == 0)
785     {
786         return MultiByteCount;
787     }
788 
789     WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount;
790 
791     for (Count = 0; Count < WideCharMaxLen; Count++)
792     {
793         Char = MultiByteString[Count];
794         if ( Char < 0x20 )
795         {
796             WideCharString[Count] = Char;
797         }
798         else
799         {
800             WideCharString[Count] = Char + 0xf000;
801         }
802     }
803     if (MultiByteCount > WideCharMaxLen)
804     {
805         SetLastError(ERROR_INSUFFICIENT_BUFFER);
806         return 0;
807     }
808 
809     return WideCharMaxLen;
810 }
811 
812 /**
813  * @name IntWideCharToMultiByteSYMBOL
814  *
815  * Internal version of WideCharToMultiByte for SYMBOL.
816  *
817  * @see WideCharToMultiByte
818  */
819 
820 static INT
821 WINAPI
822 IntWideCharToMultiByteSYMBOL(DWORD Flags,
823                              LPCWSTR WideCharString,
824                              INT WideCharCount,
825                              LPSTR MultiByteString,
826                              INT MultiByteCount)
827 {
828     LONG Count;
829     INT MaxLen;
830     WCHAR Char;
831 
832     if (Flags!=0)
833     {
834         SetLastError(ERROR_INVALID_PARAMETER);
835         return 0;
836     }
837 
838 
839     if (MultiByteCount == 0)
840     {
841         return WideCharCount;
842     }
843 
844     MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount;
845     for (Count = 0; Count < MaxLen; Count++)
846     {
847         Char = WideCharString[Count];
848         if (Char < 0x20)
849         {
850             MultiByteString[Count] = (CHAR)Char;
851         }
852         else
853         {
854             if ((Char >= 0xf020) && (Char < 0xf100))
855             {
856                 MultiByteString[Count] = Char - 0xf000;
857             }
858             else
859             {
860                 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
861                 return 0;
862             }
863         }
864     }
865 
866     if (WideCharCount > MaxLen)
867     {
868         SetLastError(ERROR_INSUFFICIENT_BUFFER);
869         return 0;
870     }
871     return MaxLen;
872 }
873 
874 /**
875  * @name IntWideCharToMultiByteUTF8
876  *
877  * Internal version of WideCharToMultiByte for UTF8.
878  *
879  * @see WideCharToMultiByte
880  */
881 
882 static INT
883 WINAPI
884 IntWideCharToMultiByteUTF8(UINT CodePage,
885                            DWORD Flags,
886                            LPCWSTR WideCharString,
887                            INT WideCharCount,
888                            LPSTR MultiByteString,
889                            INT MultiByteCount,
890                            LPCSTR DefaultChar,
891                            LPBOOL UsedDefaultChar)
892 {
893     INT TempLength;
894     DWORD Char;
895 
896     if (Flags)
897     {
898         SetLastError(ERROR_INVALID_FLAGS);
899         return 0;
900     }
901 
902     /* Does caller query for output buffer size? */
903     if (MultiByteCount == 0)
904     {
905         for (TempLength = 0; WideCharCount;
906             WideCharCount--, WideCharString++)
907         {
908             TempLength++;
909             if (*WideCharString >= 0x80)
910             {
911                 TempLength++;
912                 if (*WideCharString >= 0x800)
913                 {
914                     TempLength++;
915                     if (*WideCharString >= 0xd800 && *WideCharString < 0xdc00 &&
916                         WideCharCount >= 1 &&
917                         WideCharString[1] >= 0xdc00 && WideCharString[1] <= 0xe000)
918                     {
919                         WideCharCount--;
920                         WideCharString++;
921                         TempLength++;
922                     }
923                 }
924             }
925         }
926         return TempLength;
927     }
928 
929     for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++)
930     {
931         Char = *WideCharString;
932         if (Char < 0x80)
933         {
934             if (!TempLength)
935             {
936                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
937                 break;
938             }
939             TempLength--;
940             *MultiByteString++ = (CHAR)Char;
941             continue;
942         }
943 
944         if (Char < 0x800)  /* 0x80-0x7ff: 2 bytes */
945         {
946             if (TempLength < 2)
947             {
948                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
949                 break;
950             }
951             MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
952             MultiByteString[0] = 0xc0 | Char;
953             MultiByteString += 2;
954             TempLength -= 2;
955             continue;
956         }
957 
958         /* surrogate pair 0x10000-0x10ffff: 4 bytes */
959         if (Char >= 0xd800 && Char < 0xdc00 &&
960             WideCharCount >= 1 &&
961             WideCharString[1] >= 0xdc00 && WideCharString[1] < 0xe000)
962         {
963             WideCharCount--;
964             WideCharString++;
965 
966             if (TempLength < 4)
967             {
968                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
969                 break;
970             }
971 
972             Char = (Char - 0xd800) << 10;
973             Char |= *WideCharString - 0xdc00;
974             ASSERT(Char <= 0xfffff);
975             Char += 0x10000;
976             ASSERT(Char <= 0x10ffff);
977 
978             MultiByteString[3] = 0x80 | (Char & 0x3f); Char >>= 6;
979             MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
980             MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
981             MultiByteString[0] = 0xf0 | Char;
982             MultiByteString += 4;
983             TempLength -= 4;
984             continue;
985         }
986 
987         /* 0x800-0xffff: 3 bytes */
988         if (TempLength < 3)
989         {
990             SetLastError(ERROR_INSUFFICIENT_BUFFER);
991             break;
992         }
993         MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
994         MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
995         MultiByteString[0] = 0xe0 | Char;
996         MultiByteString += 3;
997         TempLength -= 3;
998     }
999 
1000     return MultiByteCount - TempLength;
1001 }
1002 
1003 /**
1004  * @name IsValidSBCSMapping
1005  *
1006  * Checks if ch (single-byte character) is a valid mapping for wch
1007  *
1008  * @see IntWideCharToMultiByteCP
1009  */
1010 static
1011 inline
1012 BOOL
1013 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch)
1014 {
1015     /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
1016     if (Flags & WC_NO_BEST_FIT_CHARS)
1017         return (CodePageTable->MultiByteTable[ch] == wch);
1018 
1019     /* By default, all characters except TransDefaultChar apply as a valid mapping
1020        for ch (so also "nearest" characters) */
1021     if (ch != CodePageTable->TransDefaultChar)
1022         return TRUE;
1023 
1024     /* The only possible left valid mapping is the default character itself */
1025     return (wch == CodePageTable->TransUniDefaultChar);
1026 }
1027 
1028 /**
1029  * @name IsValidDBCSMapping
1030  *
1031  * Checks if ch (double-byte character) is a valid mapping for wch
1032  *
1033  * @see IntWideCharToMultiByteCP
1034  */
1035 static inline BOOL
1036 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch)
1037 {
1038     /* If ch is the default character, but the wch is not, it can't be a valid mapping */
1039     if (ch == CodePageTable->TransDefaultChar && wch != CodePageTable->TransUniDefaultChar)
1040         return FALSE;
1041 
1042     /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
1043     if (Flags & WC_NO_BEST_FIT_CHARS)
1044     {
1045         if(ch & 0xff00)
1046         {
1047             USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8];
1048             /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1049             return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch);
1050         }
1051 
1052         return (CodePageTable->MultiByteTable[ch] == wch);
1053     }
1054 
1055     /* If we're still here, we have a valid mapping */
1056     return TRUE;
1057 }
1058 
1059 /**
1060  * @name IntWideCharToMultiByteCP
1061  *
1062  * Internal version of WideCharToMultiByte for code page tables.
1063  *
1064  * @see WideCharToMultiByte
1065  * @todo Handle WC_COMPOSITECHECK
1066  */
1067 static
1068 INT
1069 WINAPI
1070 IntWideCharToMultiByteCP(UINT CodePage,
1071                          DWORD Flags,
1072                          LPCWSTR WideCharString,
1073                          INT WideCharCount,
1074                          LPSTR MultiByteString,
1075                          INT MultiByteCount,
1076                          LPCSTR DefaultChar,
1077                          LPBOOL UsedDefaultChar)
1078 {
1079     PCODEPAGE_ENTRY CodePageEntry;
1080     PCPTABLEINFO CodePageTable;
1081     INT TempLength;
1082 
1083     /* Get code page table. */
1084     CodePageEntry = IntGetCodePageEntry(CodePage);
1085     if (CodePageEntry == NULL)
1086     {
1087         SetLastError(ERROR_INVALID_PARAMETER);
1088         return 0;
1089     }
1090 
1091     CodePageTable = &CodePageEntry->CodePageTable;
1092 
1093 
1094     /* Different handling for DBCS code pages. */
1095     if (CodePageTable->DBCSCodePage)
1096     {
1097         /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1098         if (Flags || DefaultChar || UsedDefaultChar)
1099         {
1100             BOOL TempUsedDefaultChar;
1101             USHORT DefChar;
1102 
1103             /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1104                to check on every character */
1105             if (!UsedDefaultChar)
1106                 UsedDefaultChar = &TempUsedDefaultChar;
1107 
1108             *UsedDefaultChar = FALSE;
1109 
1110             /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1111             if (DefaultChar)
1112                 DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0];
1113             else
1114                 DefChar = CodePageTable->TransDefaultChar;
1115 
1116             /* Does caller query for output buffer size? */
1117             if (!MultiByteCount)
1118             {
1119                 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1120                 {
1121                     USHORT uChar;
1122 
1123                     if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1124                     {
1125                         /* FIXME: Handle WC_COMPOSITECHECK */
1126                         DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1127                     }
1128 
1129                     uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1130 
1131                     /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1132                     if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1133                     {
1134                         uChar = DefChar;
1135                         *UsedDefaultChar = TRUE;
1136                     }
1137 
1138                     /* Increment TempLength again if this is a double-byte character */
1139                     if (uChar & 0xff00)
1140                         TempLength++;
1141                 }
1142 
1143                 return TempLength;
1144             }
1145 
1146             /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1147             for (TempLength = MultiByteCount;
1148                  WideCharCount && TempLength;
1149                  TempLength--, WideCharString++, WideCharCount--)
1150             {
1151                 USHORT uChar;
1152 
1153                 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1154                 {
1155                     /* FIXME: Handle WC_COMPOSITECHECK */
1156                     DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1157                 }
1158 
1159                 uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString];
1160 
1161                 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1162                 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1163                 {
1164                     uChar = DefChar;
1165                     *UsedDefaultChar = TRUE;
1166                 }
1167 
1168                 /* Handle double-byte characters */
1169                 if (uChar & 0xff00)
1170                 {
1171                     /* Don't output a partial character */
1172                     if (TempLength == 1)
1173                         break;
1174 
1175                     TempLength--;
1176                     *MultiByteString++ = uChar >> 8;
1177                 }
1178 
1179                 *MultiByteString++ = (char)uChar;
1180             }
1181 
1182             /* WideCharCount should be 0 if all characters were converted */
1183             if (WideCharCount)
1184             {
1185                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1186                 return 0;
1187             }
1188 
1189             return MultiByteCount - TempLength;
1190         }
1191 
1192         /* Does caller query for output buffer size? */
1193         if (!MultiByteCount)
1194         {
1195             for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1196             {
1197                 /* Increment TempLength again if this is a double-byte character */
1198                 if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00)
1199                     TempLength++;
1200             }
1201 
1202             return TempLength;
1203         }
1204 
1205         /* Convert the WideCharString to the MultiByteString */
1206         for (TempLength = MultiByteCount;
1207              WideCharCount && TempLength;
1208              TempLength--, WideCharString++, WideCharCount--)
1209         {
1210             USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1211 
1212             /* Is this a double-byte character? */
1213             if (uChar & 0xff00)
1214             {
1215                 /* Don't output a partial character */
1216                 if (TempLength == 1)
1217                     break;
1218 
1219                 TempLength--;
1220                 *MultiByteString++ = uChar >> 8;
1221             }
1222 
1223             *MultiByteString++ = (char)uChar;
1224         }
1225 
1226         /* WideCharCount should be 0 if all characters were converted */
1227         if (WideCharCount)
1228         {
1229             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1230             return 0;
1231         }
1232 
1233         return MultiByteCount - TempLength;
1234     }
1235     else /* SBCS code page */
1236     {
1237         INT nReturn;
1238 
1239         /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1240         if (Flags || DefaultChar || UsedDefaultChar)
1241         {
1242             BOOL TempUsedDefaultChar;
1243             CHAR DefChar;
1244 
1245             /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1246                to check on every character */
1247             if (!UsedDefaultChar)
1248                 UsedDefaultChar = &TempUsedDefaultChar;
1249 
1250             *UsedDefaultChar = FALSE;
1251 
1252             /* Does caller query for output buffer size? */
1253             if (!MultiByteCount)
1254             {
1255                 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1256                 for (TempLength = 0; WideCharCount; TempLength++, WideCharString++, WideCharCount--)
1257                 {
1258                     if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1259                     {
1260                         /* FIXME: Handle WC_COMPOSITECHECK */
1261                         DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1262                     }
1263 
1264                     if (!*UsedDefaultChar)
1265                         *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable,
1266                                                                   Flags,
1267                                                                   *WideCharString,
1268                                                                   ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]);
1269                 }
1270 
1271                 return TempLength;
1272             }
1273 
1274             /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1275             if (DefaultChar)
1276                 DefChar = *DefaultChar;
1277             else
1278                 DefChar = (CHAR)CodePageTable->TransDefaultChar;
1279 
1280             /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1281             for (TempLength = MultiByteCount;
1282                  WideCharCount && TempLength;
1283                  MultiByteString++, TempLength--, WideCharString++, WideCharCount--)
1284             {
1285                 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1286                 {
1287                     /* FIXME: Handle WC_COMPOSITECHECK */
1288                     DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1289                 }
1290 
1291                 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1292 
1293                 if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString))
1294                 {
1295                     *MultiByteString = DefChar;
1296                     *UsedDefaultChar = TRUE;
1297                 }
1298             }
1299 
1300             /* WideCharCount should be 0 if all characters were converted */
1301             if (WideCharCount)
1302             {
1303                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1304                 return 0;
1305             }
1306 
1307             return MultiByteCount - TempLength;
1308         }
1309 
1310         /* Does caller query for output buffer size? */
1311         if (!MultiByteCount)
1312             return WideCharCount;
1313 
1314         /* Is the buffer large enough? */
1315         if (MultiByteCount < WideCharCount)
1316         {
1317             /* Convert the string up to MultiByteCount and return 0 */
1318             WideCharCount = MultiByteCount;
1319             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1320             nReturn = 0;
1321         }
1322         else
1323         {
1324             /* Otherwise WideCharCount will be the number of converted characters */
1325             nReturn = WideCharCount;
1326         }
1327 
1328         /* Convert the WideCharString to the MultiByteString */
1329         for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++)
1330         {
1331             *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1332         }
1333 
1334         return nReturn;
1335     }
1336 }
1337 
1338 /**
1339  * @name IntIsLeadByte
1340  *
1341  * Internal function to detect if byte is lead byte in specific character
1342  * table.
1343  */
1344 
1345 static BOOL
1346 WINAPI
1347 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
1348 {
1349     UINT i;
1350 
1351     if (TableInfo->MaximumCharacterSize == 2)
1352     {
1353         for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2)
1354         {
1355             if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1])
1356                 return TRUE;
1357         }
1358     }
1359 
1360     return FALSE;
1361 }
1362 
1363 /* PUBLIC FUNCTIONS ***********************************************************/
1364 
1365 /**
1366  * @name GetNlsSectionName
1367  *
1368  * Construct a name of NLS section.
1369  *
1370  * @param CodePage
1371  *        Code page number.
1372  * @param Base
1373  *        Integer base used for converting to string. Usually set to 10.
1374  * @param Unknown
1375  *        As the name suggests the meaning of this parameter is unknown.
1376  *        The native version of Kernel32 passes it as the third parameter
1377  *        to NlsConvertIntegerToString function, which is used for the
1378  *        actual conversion of the code page number.
1379  * @param BaseName
1380  *        Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1381  * @param Result
1382  *        Buffer that will hold the constructed name.
1383  * @param ResultSize
1384  *        Size of the buffer for the result.
1385  *
1386  * @return TRUE if the buffer was large enough and was filled with
1387  *         the requested information, FALSE otherwise.
1388  *
1389  * @implemented
1390  */
1391 
1392 BOOL
1393 WINAPI
1394 GetNlsSectionName(UINT CodePage,
1395                   UINT Base,
1396                   ULONG Unknown,
1397                   LPSTR BaseName,
1398                   LPSTR Result,
1399                   ULONG ResultSize)
1400 {
1401     CHAR Integer[11];
1402 
1403     if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
1404         return FALSE;
1405 
1406     /*
1407      * If the name including the terminating NULL character doesn't
1408      * fit in the output buffer then fail.
1409      */
1410     if (strlen(Integer) + strlen(BaseName) >= ResultSize)
1411         return FALSE;
1412 
1413     lstrcpyA(Result, BaseName);
1414     lstrcatA(Result, Integer);
1415 
1416     return TRUE;
1417 }
1418 
1419 /**
1420  * @name GetCPFileNameFromRegistry
1421  *
1422  * Get file name of code page definition file.
1423  *
1424  * @param CodePage
1425  *        Code page number to get file name of.
1426  * @param FileName
1427  *        Buffer that is filled with file name of successful return. Can
1428  *        be set to NULL.
1429  * @param FileNameSize
1430  *        Size of the buffer to hold file name in WCHARs.
1431  *
1432  * @return TRUE if the file name was retrieved, FALSE otherwise.
1433  *
1434  * @implemented
1435  */
1436 
1437 BOOL
1438 WINAPI
1439 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
1440 {
1441     WCHAR ValueNameBuffer[11];
1442     UNICODE_STRING KeyName, ValueName;
1443     OBJECT_ATTRIBUTES ObjectAttributes;
1444     NTSTATUS Status;
1445     HANDLE KeyHandle;
1446     PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
1447     DWORD KvpiSize;
1448     BOOL bRetValue;
1449 
1450     bRetValue = FALSE;
1451 
1452     /* Convert the codepage number to string. */
1453     ValueName.Buffer = ValueNameBuffer;
1454     ValueName.MaximumLength = sizeof(ValueNameBuffer);
1455 
1456     if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
1457         return bRetValue;
1458 
1459     /* Open the registry key containing file name mappings. */
1460     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
1461                          L"CurrentControlSet\\Control\\Nls\\CodePage");
1462     InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
1463                                NULL, NULL);
1464     Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1465     if (!NT_SUCCESS(Status))
1466     {
1467         return bRetValue;
1468     }
1469 
1470     /* Allocate buffer that will be used to query the value data. */
1471     KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (MAX_PATH * sizeof(WCHAR));
1472     Kvpi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KvpiSize);
1473     if (Kvpi == NULL)
1474     {
1475         NtClose(KeyHandle);
1476         return bRetValue;
1477     }
1478 
1479     /* Query the file name for our code page. */
1480     Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
1481                              Kvpi, KvpiSize, &KvpiSize);
1482 
1483     NtClose(KeyHandle);
1484 
1485     /* Check if we succeded and the value is non-empty string. */
1486     if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
1487         Kvpi->DataLength > sizeof(WCHAR))
1488     {
1489         bRetValue = TRUE;
1490         if (FileName != NULL)
1491         {
1492             lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
1493                       min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
1494         }
1495     }
1496 
1497     /* free temporary buffer */
1498     HeapFree(GetProcessHeap(),0,Kvpi);
1499     return bRetValue;
1500 }
1501 
1502 /**
1503  * @name IsValidCodePage
1504  *
1505  * Detect if specified code page is valid and present in the system.
1506  *
1507  * @param CodePage
1508  *        Code page number to query.
1509  *
1510  * @return TRUE if code page is present.
1511  */
1512 
1513 BOOL
1514 WINAPI
1515 IsValidCodePage(UINT CodePage)
1516 {
1517     if (CodePage == 0) return FALSE;
1518     if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
1519         return TRUE;
1520     if (IntGetLoadedCodePageEntry(CodePage))
1521         return TRUE;
1522     return GetCPFileNameFromRegistry(CodePage, NULL, 0);
1523 }
1524 
1525 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1526 {
1527     if (dstlen > 0)
1528     {
1529         if (*index >= dstlen)
1530             return FALSE;
1531 
1532         dst[*index] = character;
1533     }
1534 
1535     (*index)++;
1536 
1537     return TRUE;
1538 }
1539 
1540 static INT Utf7ToWideChar(const char *src, int srclen, WCHAR *dst, int dstlen)
1541 {
1542     static const signed char base64_decoding_table[] =
1543     {
1544         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1545         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1546         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1547         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1548         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1549         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1550         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1551         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70-0x7F */
1552     };
1553 
1554     const char *source_end = src + srclen;
1555     int dest_index = 0;
1556 
1557     DWORD byte_pair = 0;
1558     short offset = 0;
1559 
1560     while (src < source_end)
1561     {
1562         if (*src == '+')
1563         {
1564             src++;
1565             if (src >= source_end)
1566                 break;
1567 
1568             if (*src == '-')
1569             {
1570                 /* just a plus sign escaped as +- */
1571                 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
1572                 {
1573                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1574                     return 0;
1575                 }
1576                 src++;
1577                 continue;
1578             }
1579 
1580             do
1581             {
1582                 signed char sextet = *src;
1583                 if (sextet == '-')
1584                 {
1585                     /* skip over the dash and end base64 decoding
1586                      * the current, unfinished byte pair is discarded */
1587                     src++;
1588                     offset = 0;
1589                     break;
1590                 }
1591                 if (sextet < 0)
1592                 {
1593                     /* the next character of src is < 0 and therefore not part of a base64 sequence
1594                      * the current, unfinished byte pair is NOT discarded in this case
1595                      * this is probably a bug in Windows */
1596                     break;
1597                 }
1598 
1599                 sextet = base64_decoding_table[sextet];
1600                 if (sextet == -1)
1601                 {
1602                     /* -1 means that the next character of src is not part of a base64 sequence
1603                      * in other words, all sextets in this base64 sequence have been processed
1604                      * the current, unfinished byte pair is discarded */
1605                     offset = 0;
1606                     break;
1607                 }
1608 
1609                 byte_pair = (byte_pair << 6) | sextet;
1610                 offset += 6;
1611 
1612                 if (offset >= 16)
1613                 {
1614                     /* this byte pair is done */
1615                     if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
1616                     {
1617                         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1618                         return 0;
1619                     }
1620                     offset -= 16;
1621                 }
1622 
1623                 src++;
1624             }
1625             while (src < source_end);
1626         }
1627         else
1628         {
1629             /* we have to convert to unsigned char in case *src < 0 */
1630             if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
1631             {
1632                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1633                 return 0;
1634             }
1635             src++;
1636         }
1637     }
1638 
1639     return dest_index;
1640 }
1641 
1642 /**
1643  * @name MultiByteToWideChar
1644  *
1645  * Convert a multi-byte string to wide-charater equivalent.
1646  *
1647  * @param CodePage
1648  *        Code page to be used to perform the conversion. It can be also
1649  *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
1650  *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1651  *        for thread active code page, CP_UTF7 or CP_UTF8).
1652  * @param Flags
1653  *        Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1654  *        MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1655  * @param MultiByteString
1656  *        Input buffer.
1657  * @param MultiByteCount
1658  *        Size of MultiByteString, or -1 if MultiByteString is NULL
1659  *        terminated.
1660  * @param WideCharString
1661  *        Output buffer.
1662  * @param WideCharCount
1663  *        Size in WCHARs of WideCharString, or 0 if the caller just wants
1664  *        to know how large WideCharString should be for a successful
1665  *        conversion.
1666  *
1667  * @return Zero on error, otherwise the number of WCHARs written
1668  *         in the WideCharString buffer.
1669  *
1670  * @implemented
1671  */
1672 INT
1673 WINAPI
1674 MultiByteToWideChar(UINT CodePage,
1675                     DWORD Flags,
1676                     LPCSTR MultiByteString,
1677                     INT MultiByteCount,
1678                     LPWSTR WideCharString,
1679                     INT WideCharCount)
1680 {
1681     /* Check the parameters. */
1682     if (MultiByteString == NULL ||
1683         MultiByteCount == 0 || WideCharCount < 0 ||
1684         (WideCharCount && (WideCharString == NULL ||
1685         (PVOID)MultiByteString == (PVOID)WideCharString)))
1686     {
1687         SetLastError(ERROR_INVALID_PARAMETER);
1688         return 0;
1689     }
1690 
1691     /* Determine the input string length. */
1692     if (MultiByteCount < 0)
1693     {
1694         MultiByteCount = lstrlenA(MultiByteString) + 1;
1695     }
1696 
1697     switch (CodePage)
1698     {
1699         case CP_UTF8:
1700             return IntMultiByteToWideCharUTF8(Flags,
1701                                               MultiByteString,
1702                                               MultiByteCount,
1703                                               WideCharString,
1704                                               WideCharCount);
1705 
1706         case CP_UTF7:
1707             if (Flags)
1708             {
1709                 SetLastError(ERROR_INVALID_FLAGS);
1710                 return 0;
1711             }
1712             return Utf7ToWideChar(MultiByteString, MultiByteCount,
1713                                   WideCharString, WideCharCount);
1714 
1715         case CP_SYMBOL:
1716             return IntMultiByteToWideCharSYMBOL(Flags,
1717                                                 MultiByteString,
1718                                                 MultiByteCount,
1719                                                 WideCharString,
1720                                                 WideCharCount);
1721         default:
1722             return IntMultiByteToWideCharCP(CodePage,
1723                                             Flags,
1724                                             MultiByteString,
1725                                             MultiByteCount,
1726                                             WideCharString,
1727                                             WideCharCount);
1728     }
1729 }
1730 
1731 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
1732 {
1733     static const BOOL directly_encodable_table[] =
1734     {
1735         1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1736         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1737         1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1738         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1739         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1740         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1741         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1742         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1                 /* 0x70 - 0x7A */
1743     };
1744 
1745     return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
1746 }
1747 
1748 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
1749 {
1750     if (dstlen > 0)
1751     {
1752         if (*index >= dstlen)
1753             return FALSE;
1754 
1755         dst[*index] = character;
1756     }
1757 
1758     (*index)++;
1759 
1760     return TRUE;
1761 }
1762 
1763 static INT WideCharToUtf7(const WCHAR *src, int srclen, char *dst, int dstlen)
1764 {
1765     static const char base64_encoding_table[] =
1766         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1767 
1768     const WCHAR *source_end = src + srclen;
1769     int dest_index = 0;
1770 
1771     while (src < source_end)
1772     {
1773         if (*src == '+')
1774         {
1775             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1776             {
1777                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1778                 return 0;
1779             }
1780             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1781             {
1782                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1783                 return 0;
1784             }
1785             src++;
1786         }
1787         else if (utf7_can_directly_encode(*src))
1788         {
1789             if (!utf7_write_c(dst, dstlen, &dest_index, *src))
1790             {
1791                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1792                 return 0;
1793             }
1794             src++;
1795         }
1796         else
1797         {
1798             unsigned int offset = 0;
1799             DWORD byte_pair = 0;
1800 
1801             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1802             {
1803                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1804                 return 0;
1805             }
1806 
1807             while (src < source_end && !utf7_can_directly_encode(*src))
1808             {
1809                 byte_pair = (byte_pair << 16) | *src;
1810                 offset += 16;
1811                 while (offset >= 6)
1812                 {
1813                     if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
1814                     {
1815                         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1816                         return 0;
1817                     }
1818                     offset -= 6;
1819                 }
1820                 src++;
1821             }
1822 
1823             if (offset)
1824             {
1825                 /* Windows won't create a padded base64 character if there's no room for the - sign
1826                  * as well ; this is probably a bug in Windows */
1827                 if (dstlen > 0 && dest_index + 1 >= dstlen)
1828                 {
1829                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1830                     return 0;
1831                 }
1832 
1833                 byte_pair <<= (6 - offset);
1834                 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
1835                 {
1836                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1837                     return 0;
1838                 }
1839             }
1840 
1841             /* Windows always explicitly terminates the base64 sequence
1842                even though RFC 2152 (page 3, rule 2) does not require this */
1843             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1844             {
1845                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1846                 return 0;
1847             }
1848         }
1849     }
1850 
1851     return dest_index;
1852 }
1853 
1854 /*
1855  * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW
1856  * and GetGeoInfoW. It uses the current user localization, otherwise falls back
1857  * to English (US). Contrary to LoadStringW which always saves the loaded string
1858  * into the user-given buffer, truncating the string if needed, this function
1859  * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer
1860  * is not large enough.
1861  */
1862 UINT
1863 GetLocalisedText(
1864     IN UINT uID,
1865     IN LPWSTR lpszDest,
1866     IN UINT cchDest,
1867     IN LANGID lang)
1868 {
1869     HRSRC hrsrc;
1870     HGLOBAL hmem;
1871     LCID lcid;
1872     LANGID langId;
1873     const WCHAR *p;
1874     UINT i;
1875 
1876     /* See HACK in winnls/lang/xx-XX.rc files */
1877     if (uID == 37)
1878         uID = uID * 100;
1879 
1880     lcid = ConvertDefaultLocale(lang);
1881 
1882     langId = LANGIDFROMLCID(lcid);
1883 
1884     if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
1885         langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
1886 
1887     hrsrc = FindResourceExW(hCurrentModule,
1888                             (LPWSTR)RT_STRING,
1889                             MAKEINTRESOURCEW((uID >> 4) + 1),
1890                             langId);
1891 
1892     /* English fallback */
1893     if (!hrsrc)
1894     {
1895         hrsrc = FindResourceExW(hCurrentModule,
1896                                 (LPWSTR)RT_STRING,
1897                                 MAKEINTRESOURCEW((uID >> 4) + 1),
1898                                 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
1899     }
1900 
1901     if (!hrsrc)
1902         goto NotFound;
1903 
1904     hmem = LoadResource(hCurrentModule, hrsrc);
1905     if (!hmem)
1906         goto NotFound;
1907 
1908     p = LockResource(hmem);
1909 
1910     for (i = 0; i < (uID & 0x0F); i++)
1911         p += *p + 1;
1912 
1913     /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
1914     if (cchDest == 0)
1915         return *p + 1;
1916     /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
1917     if (*p + 1 > cchDest)
1918     {
1919         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1920         return 0;
1921     }
1922 
1923     i = *p;
1924     if (i > 0)
1925     {
1926         memcpy(lpszDest, p + 1, i * sizeof(WCHAR));
1927         lpszDest[i] = L'\0';
1928         return i;
1929     }
1930 #if 0
1931     else
1932     {
1933         if (cchDest >= 1)
1934             lpszDest[0] = L'\0';
1935         /* Fall-back */
1936     }
1937 #endif
1938 
1939 NotFound:
1940     DPRINT1("Resource not found: uID = %lu\n", uID);
1941     SetLastError(ERROR_INVALID_PARAMETER);
1942     return 0;
1943 }
1944 
1945 /*
1946  * @implemented
1947  */
1948 BOOL
1949 WINAPI
1950 GetCPInfo(UINT CodePage,
1951           LPCPINFO CodePageInfo)
1952 {
1953     PCODEPAGE_ENTRY CodePageEntry;
1954 
1955     if (!CodePageInfo)
1956     {
1957         SetLastError(ERROR_INVALID_PARAMETER);
1958         return FALSE;
1959     }
1960 
1961     CodePageEntry = IntGetCodePageEntry(CodePage);
1962     if (CodePageEntry == NULL)
1963     {
1964         switch(CodePage)
1965         {
1966             case CP_UTF7:
1967             case CP_UTF8:
1968                 CodePageInfo->DefaultChar[0] = 0x3f;
1969                 CodePageInfo->DefaultChar[1] = 0;
1970                 CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1971                 CodePageInfo->MaxCharSize = (CodePage == CP_UTF7) ? 5 : 4;
1972                 return TRUE;
1973         }
1974 
1975         DPRINT1("Invalid CP!: %lx\n", CodePage);
1976         SetLastError( ERROR_INVALID_PARAMETER );
1977         return FALSE;
1978     }
1979 
1980     if (CodePageEntry->CodePageTable.DefaultChar & 0xff00)
1981     {
1982         CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8;
1983         CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff;
1984     }
1985     else
1986     {
1987         CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff;
1988         CodePageInfo->DefaultChar[1] = 0;
1989     }
1990 
1991     if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2)
1992         memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte));
1993     else
1994         CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1995 
1996     return TRUE;
1997 }
1998 
1999 /*
2000  * @implemented
2001  */
2002 BOOL
2003 WINAPI
2004 GetCPInfoExW(UINT CodePage,
2005              DWORD dwFlags,
2006              LPCPINFOEXW lpCPInfoEx)
2007 {
2008     if (!GetCPInfo(CodePage, (LPCPINFO)lpCPInfoEx))
2009         return FALSE;
2010 
2011     switch(CodePage)
2012     {
2013         case CP_UTF7:
2014         {
2015             lpCPInfoEx->CodePage = CP_UTF7;
2016             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
2017             return GetLocalisedText(lpCPInfoEx->CodePage,
2018                                     lpCPInfoEx->CodePageName,
2019                                     ARRAYSIZE(lpCPInfoEx->CodePageName),
2020                                     GetThreadLocale()) != 0;
2021         }
2022         break;
2023 
2024         case CP_UTF8:
2025         {
2026             lpCPInfoEx->CodePage = CP_UTF8;
2027             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
2028             return GetLocalisedText(lpCPInfoEx->CodePage,
2029                                     lpCPInfoEx->CodePageName,
2030                                     ARRAYSIZE(lpCPInfoEx->CodePageName),
2031                                     GetThreadLocale()) != 0;
2032         }
2033 
2034         default:
2035         {
2036             PCODEPAGE_ENTRY CodePageEntry;
2037 
2038             CodePageEntry = IntGetCodePageEntry(CodePage);
2039             if (CodePageEntry == NULL)
2040             {
2041                 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
2042                 SetLastError(ERROR_INVALID_PARAMETER);
2043                 return FALSE;
2044             }
2045 
2046             lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage;
2047             lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar;
2048             return GetLocalisedText(lpCPInfoEx->CodePage,
2049                                     lpCPInfoEx->CodePageName,
2050                                     ARRAYSIZE(lpCPInfoEx->CodePageName),
2051                                     GetThreadLocale()) != 0;
2052         }
2053         break;
2054     }
2055 }
2056 
2057 
2058 /*
2059  * @implemented
2060  */
2061 BOOL
2062 WINAPI
2063 GetCPInfoExA(UINT CodePage,
2064              DWORD dwFlags,
2065              LPCPINFOEXA lpCPInfoEx)
2066 {
2067     CPINFOEXW CPInfo;
2068 
2069     if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo))
2070         return FALSE;
2071 
2072     /* the layout is the same except for CodePageName */
2073     memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA));
2074 
2075     WideCharToMultiByte(CP_ACP,
2076                         0,
2077                         CPInfo.CodePageName,
2078                         -1,
2079                         lpCPInfoEx->CodePageName,
2080                         sizeof(lpCPInfoEx->CodePageName),
2081                         NULL,
2082                         NULL);
2083     return TRUE;
2084 }
2085 
2086 /**
2087  * @name WideCharToMultiByte
2088  *
2089  * Convert a wide-charater string to closest multi-byte equivalent.
2090  *
2091  * @param CodePage
2092  *        Code page to be used to perform the conversion. It can be also
2093  *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
2094  *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
2095  *        for thread active code page, CP_UTF7 or CP_UTF8).
2096  * @param Flags
2097  *        Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
2098  *        WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
2099  * @param WideCharString
2100  *        Points to the wide-character string to be converted.
2101  * @param WideCharCount
2102  *        Size in WCHARs of WideCharStr, or 0 if the caller just wants to
2103  *        know how large WideCharString should be for a successful conversion.
2104  * @param MultiByteString
2105  *        Points to the buffer to receive the translated string.
2106  * @param MultiByteCount
2107  *        Specifies the size in bytes of the buffer pointed to by the
2108  *        MultiByteString parameter. If this value is zero, the function
2109  *        returns the number of bytes required for the buffer.
2110  * @param DefaultChar
2111  *        Points to the character used if a wide character cannot be
2112  *        represented in the specified code page. If this parameter is
2113  *        NULL, a system default value is used.
2114  * @param UsedDefaultChar
2115  *        Points to a flag that indicates whether a default character was
2116  *        used. This parameter can be NULL.
2117  *
2118  * @return Zero on error, otherwise the number of bytes written in the
2119  *         MultiByteString buffer. Or the number of bytes needed for
2120  *         the MultiByteString buffer if MultiByteCount is zero.
2121  *
2122  * @implemented
2123  */
2124 
2125 INT
2126 WINAPI
2127 WideCharToMultiByte(UINT CodePage,
2128                     DWORD Flags,
2129                     LPCWSTR WideCharString,
2130                     INT WideCharCount,
2131                     LPSTR MultiByteString,
2132                     INT MultiByteCount,
2133                     LPCSTR DefaultChar,
2134                     LPBOOL UsedDefaultChar)
2135 {
2136     /* Check the parameters. */
2137     if (WideCharString == NULL ||
2138         WideCharCount == 0 ||
2139         (MultiByteString == NULL && MultiByteCount > 0) ||
2140         (PVOID)WideCharString == (PVOID)MultiByteString ||
2141         MultiByteCount < 0)
2142     {
2143         SetLastError(ERROR_INVALID_PARAMETER);
2144         return 0;
2145     }
2146 
2147     /* Determine the input string length. */
2148     if (WideCharCount < 0)
2149     {
2150         WideCharCount = lstrlenW(WideCharString) + 1;
2151     }
2152 
2153     switch (CodePage)
2154     {
2155         case CP_UTF8:
2156             if (DefaultChar != NULL || UsedDefaultChar != NULL)
2157             {
2158                 SetLastError(ERROR_INVALID_PARAMETER);
2159                 return 0;
2160             }
2161             return IntWideCharToMultiByteUTF8(CodePage,
2162                                               Flags,
2163                                               WideCharString,
2164                                               WideCharCount,
2165                                               MultiByteString,
2166                                               MultiByteCount,
2167                                               DefaultChar,
2168                                               UsedDefaultChar);
2169 
2170         case CP_UTF7:
2171             if (DefaultChar != NULL || UsedDefaultChar != NULL)
2172             {
2173                 SetLastError(ERROR_INVALID_PARAMETER);
2174                 return 0;
2175             }
2176             if (Flags)
2177             {
2178                 SetLastError(ERROR_INVALID_FLAGS);
2179                 return 0;
2180             }
2181             return WideCharToUtf7(WideCharString, WideCharCount,
2182                                   MultiByteString, MultiByteCount);
2183 
2184         case CP_SYMBOL:
2185             if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL))
2186             {
2187                 SetLastError(ERROR_INVALID_PARAMETER);
2188                 return 0;
2189             }
2190             return IntWideCharToMultiByteSYMBOL(Flags,
2191                                                 WideCharString,
2192                                                 WideCharCount,
2193                                                 MultiByteString,
2194                                                 MultiByteCount);
2195 
2196         default:
2197             return IntWideCharToMultiByteCP(CodePage,
2198                                             Flags,
2199                                             WideCharString,
2200                                             WideCharCount,
2201                                             MultiByteString,
2202                                             MultiByteCount,
2203                                             DefaultChar,
2204                                             UsedDefaultChar);
2205    }
2206 }
2207 
2208 /**
2209  * @name GetACP
2210  *
2211  * Get active ANSI code page number.
2212  *
2213  * @implemented
2214  */
2215 
2216 UINT
2217 WINAPI
2218 GetACP(VOID)
2219 {
2220     return AnsiCodePage.CodePageTable.CodePage;
2221 }
2222 
2223 /**
2224  * @name GetOEMCP
2225  *
2226  * Get active OEM code page number.
2227  *
2228  * @implemented
2229  */
2230 
2231 UINT
2232 WINAPI
2233 GetOEMCP(VOID)
2234 {
2235     return OemCodePage.CodePageTable.CodePage;
2236 }
2237 
2238 /**
2239  * @name IsDBCSLeadByteEx
2240  *
2241  * Determine if passed byte is lead byte in specified code page.
2242  *
2243  * @implemented
2244  */
2245 
2246 BOOL
2247 WINAPI
2248 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
2249 {
2250     PCODEPAGE_ENTRY CodePageEntry;
2251 
2252     CodePageEntry = IntGetCodePageEntry(CodePage);
2253     if (CodePageEntry != NULL)
2254         return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
2255 
2256     SetLastError(ERROR_INVALID_PARAMETER);
2257     return FALSE;
2258 }
2259 
2260 /**
2261  * @name IsDBCSLeadByteEx
2262  *
2263  * Determine if passed byte is lead byte in current ANSI code page.
2264  *
2265  * @implemented
2266  */
2267 
2268 BOOL
2269 WINAPI
2270 IsDBCSLeadByte(BYTE TestByte)
2271 {
2272     return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
2273 }
2274 
2275 /**
2276  * @brief
2277  * Creates a security descriptor for the NLS object directory
2278  * name.
2279  *
2280  * @param[out] SecurityDescriptor
2281  * A pointer to an allocated and created security descriptor
2282  * that is given to the caller.
2283  *
2284  * @return
2285  * STATUS_SUCCESS is returned if the function has successfully
2286  * created a security descriptor for a NLS section name. Otherwise
2287  * a NTSTATUS failure code is returned.
2288  *
2289  * @remarks
2290  * Everyone (aka World SID) is given read access to the NLS directory
2291  * whereas admins are given full power.
2292  */
2293 NTSTATUS
2294 CreateNlsDirectorySecurity(_Out_ PSECURITY_DESCRIPTOR *NlsSecurityDescriptor)
2295 {
2296     NTSTATUS Status;
2297     PACL Dacl;
2298     PSID WorldSid = NULL, AdminsSid = NULL;
2299     ULONG DaclSize, RelSdSize = 0;
2300     PSECURITY_DESCRIPTOR RelativeSd = NULL;
2301     SECURITY_DESCRIPTOR AbsoluteSd;
2302     static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
2303     static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
2304 
2305     /* Create the World SID */
2306     Status = RtlAllocateAndInitializeSid(&WorldAuthority,
2307                                          1,
2308                                          SECURITY_WORLD_RID,
2309                                          0, 0, 0, 0, 0, 0, 0,
2310                                          &WorldSid);
2311     if (!NT_SUCCESS(Status))
2312     {
2313         DPRINT1("CreateNlsDirectorySecurity(): Failed to create world SID (Status 0x%08x)\n", Status);
2314         return Status;
2315     }
2316 
2317     /* Create the admins SID */
2318     Status = RtlAllocateAndInitializeSid(&NtAuthority,
2319                                          2,
2320                                          SECURITY_BUILTIN_DOMAIN_RID,
2321                                          DOMAIN_ALIAS_RID_ADMINS,
2322                                          0, 0, 0, 0, 0, 0,
2323                                          &AdminsSid);
2324     if (!NT_SUCCESS(Status))
2325     {
2326         DPRINT1("CreateNlsDirectorySecurity(): Failed to create admins SID (Status 0x%08x)\n", Status);
2327         goto Quit;
2328     }
2329 
2330     /* Build up the size of our DACL, including the World and admins SIDs */
2331     DaclSize = sizeof(ACL) +
2332                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(WorldSid) +
2333                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(AdminsSid);
2334 
2335     /* Allocate memory for our DACL */
2336     Dacl = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, DaclSize);
2337     if (Dacl == NULL)
2338     {
2339         DPRINT1("CreateNlsDirectorySecurity(): Could not allocate memory for DACL, not enough memory!\n");
2340         Status = STATUS_INSUFFICIENT_RESOURCES;
2341         goto Quit;
2342     }
2343 
2344     /* Create the DACL */
2345     Status = RtlCreateAcl(Dacl, DaclSize, ACL_REVISION);
2346     if (!NT_SUCCESS(Status))
2347     {
2348         DPRINT1("CreateNlsDirectorySecurity(): Failed to create the DACL (Status 0x%08x)\n", Status);
2349         goto Quit;
2350     }
2351 
2352     /* Give everyone basic directory access */
2353     Status = RtlAddAccessAllowedAce(Dacl,
2354                                     ACL_REVISION,
2355                                     DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT,
2356                                     WorldSid);
2357     if (!NT_SUCCESS(Status))
2358     {
2359         DPRINT1("CreateNlsDirectorySecurity(): Failed to insert allowed access ACE to DACL for World SID (Status 0x%08x)\n", Status);
2360         goto Quit;
2361     }
2362 
2363     /* Give admins full power */
2364     Status = RtlAddAccessAllowedAce(Dacl,
2365                                     ACL_REVISION,
2366                                     DIRECTORY_ALL_ACCESS,
2367                                     AdminsSid);
2368     if (!NT_SUCCESS(Status))
2369     {
2370         DPRINT1("CreateNlsDirectorySecurity(): Failed to insert allowed access ACE to DACL for admins SID (Status 0x%08x)\n", Status);
2371         goto Quit;
2372     }
2373 
2374     /* Initialize the security descriptor */
2375     Status = RtlCreateSecurityDescriptor(&AbsoluteSd,
2376                                          SECURITY_DESCRIPTOR_REVISION);
2377     if (!NT_SUCCESS(Status))
2378     {
2379         DPRINT1("CreateNlsDirectorySecurity(): Failed to initialize the security descriptor (Status 0x%08x)\n", Status);
2380         goto Quit;
2381     }
2382 
2383     /* Set the DACL to descriptor */
2384     Status = RtlSetDaclSecurityDescriptor(&AbsoluteSd,
2385                                           TRUE,
2386                                           Dacl,
2387                                           FALSE);
2388     if (!NT_SUCCESS(Status))
2389     {
2390         DPRINT1("CreateNlsDirectorySecurity(): Failed to insert DACL into the descriptor (Status 0x%08x)\n", Status);
2391         goto Quit;
2392     }
2393 
2394     /* Determine how much size is needed to convert the absolute SD into self-relative one */
2395     Status = RtlAbsoluteToSelfRelativeSD(&AbsoluteSd,
2396                                          NULL,
2397                                          &RelSdSize);
2398     if (Status != STATUS_BUFFER_TOO_SMALL)
2399     {
2400         DPRINT1("CreateNlsDirectorySecurity(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08x)\n", Status);
2401         goto Quit;
2402     }
2403 
2404     /* Allocate buffer for relative SD */
2405     RelativeSd = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, RelSdSize);
2406     if (RelativeSd == NULL)
2407     {
2408         DPRINT1("CreateNlsDirectorySecurity(): Could not allocate memory for relative SD, not enough memory!\n");
2409         Status = STATUS_INSUFFICIENT_RESOURCES;
2410         goto Quit;
2411     }
2412 
2413     /* Convert it now */
2414     Status = RtlAbsoluteToSelfRelativeSD(&AbsoluteSd,
2415                                          RelativeSd,
2416                                          &RelSdSize);
2417     if (!NT_SUCCESS(Status))
2418     {
2419         DPRINT1("CreateNlsDirectorySecurity(): Failed to convert absolute SD to self-relative format (Status 0x%08x)\n", Status);
2420         goto Quit;
2421     }
2422 
2423     /* Give the security descriptor to the caller */
2424     *NlsSecurityDescriptor = RelativeSd;
2425 
2426 Quit:
2427     if (WorldSid != NULL)
2428     {
2429         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
2430     }
2431 
2432     if (AdminsSid != NULL)
2433     {
2434         RtlFreeHeap(RtlGetProcessHeap(), 0, AdminsSid);
2435     }
2436 
2437     if (Dacl != NULL)
2438     {
2439         RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
2440     }
2441 
2442     if (!NT_SUCCESS(Status))
2443     {
2444         if (RelativeSd != NULL)
2445         {
2446             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSd);
2447         }
2448     }
2449 
2450     return Status;
2451 }
2452 
2453 /**
2454  * @brief
2455  * Creates a security descriptor for each NLS section
2456  * name.
2457  *
2458  * @param[in] AccessMask
2459  * An access mask bit to supply to the function. This
2460  * access mask grants everyone access specific to that
2461  * bit mask.
2462  *
2463  * @param[out] SecurityDescriptor
2464  * A pointer to an allocated and created security descriptor
2465  * that is given to the caller.
2466  *
2467  * @return
2468  * STATUS_SUCCESS is returned if the function has successfully
2469  * created a security descriptor for a NLS section name. Otherwise
2470  * a NTSTATUS failure code is returned.
2471  *
2472  * @remarks
2473  * The implementation of CreateNlsSecurityDescriptor on Windows Server
2474  * 2003 is slightly different compared to ours. The second parameter
2475  * takes the size of a security descriptor, in bytes. This is implied
2476  * that on Windows the caller is responsible to submit the exact
2477  * size of the descriptor. On ReactOS we're going to do it different,
2478  * let the function be responsible for security descriptor creation
2479  * and its size. DescriptorSize will act like a dummy parameter for us
2480  * in this case. Everyone (aka World SID) is given read access to each
2481  * NLS section name.
2482  */
2483 NTSTATUS WINAPI CreateNlsSecurityDescriptor(_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor, _In_ SIZE_T DescriptorSize, _In_ ULONG AccessMask)
2484 {
2485     NTSTATUS Status;
2486     PACL Dacl;
2487     PSID WorldSid = NULL;
2488     ULONG DaclSize, RelSdSize = 0;
2489     PSECURITY_DESCRIPTOR RelativeSd = NULL;
2490     SECURITY_DESCRIPTOR AbsoluteSd;
2491     static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
2492 
2493     /* DescriptorSize is just a dummy parameter */
2494     UNREFERENCED_PARAMETER(DescriptorSize);
2495 
2496     /* Create the World SID */
2497     Status = RtlAllocateAndInitializeSid(&WorldAuthority,
2498                                          1,
2499                                          SECURITY_WORLD_RID,
2500                                          0, 0, 0, 0, 0, 0, 0,
2501                                          &WorldSid);
2502     if (!NT_SUCCESS(Status))
2503     {
2504         DPRINT1("CreateNlsSecurityDescriptor(): Failed to create world SID (Status 0x%08x)\n", Status);
2505         return Status;
2506     }
2507 
2508     /* Build up the size of our DACL, including the World SID */
2509     DaclSize = sizeof(ACL) +
2510                sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(WorldSid);
2511 
2512     /* Allocate memory for our DACL */
2513     Dacl = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, DaclSize);
2514     if (Dacl == NULL)
2515     {
2516         DPRINT1("CreateNlsSecurityDescriptor(): Could not allocate memory for DACL, not enough memory!\n");
2517         Status = STATUS_INSUFFICIENT_RESOURCES;
2518         goto Quit;
2519     }
2520 
2521     /* Create the DACL */
2522     Status = RtlCreateAcl(Dacl, DaclSize, ACL_REVISION);
2523     if (!NT_SUCCESS(Status))
2524     {
2525         DPRINT1("CreateNlsSecurityDescriptor(): Failed to create the DACL (Status 0x%08x)\n", Status);
2526         goto Quit;
2527     }
2528 
2529     /* Add a ACE with allow access to the World SID */
2530     Status = RtlAddAccessAllowedAce(Dacl,
2531                                     ACL_REVISION,
2532                                     AccessMask,
2533                                     WorldSid);
2534     if (!NT_SUCCESS(Status))
2535     {
2536         DPRINT1("CreateNlsSecurityDescriptor(): Failed to insert allowed access ACE to DACL for World SID (Status 0x%08x)\n", Status);
2537         goto Quit;
2538     }
2539 
2540     /* Initialize the security descriptor */
2541     Status = RtlCreateSecurityDescriptor(&AbsoluteSd,
2542                                          SECURITY_DESCRIPTOR_REVISION);
2543     if (!NT_SUCCESS(Status))
2544     {
2545         DPRINT1("CreateNlsSecurityDescriptor(): Failed to insert allowed access ACE to DACL for World SID (Status 0x%08x)\n", Status);
2546         goto Quit;
2547     }
2548 
2549     /* Set the DACL to descriptor */
2550     Status = RtlSetDaclSecurityDescriptor(&AbsoluteSd,
2551                                           TRUE,
2552                                           Dacl,
2553                                           FALSE);
2554     if (!NT_SUCCESS(Status))
2555     {
2556         DPRINT1("CreateNlsSecurityDescriptor(): Failed to insert DACL into the descriptor (Status 0x%08x)\n", Status);
2557         goto Quit;
2558     }
2559 
2560     /* Determine how much size is needed to convert the absolute SD into self-relative one */
2561     Status = RtlAbsoluteToSelfRelativeSD(&AbsoluteSd,
2562                                          NULL,
2563                                          &RelSdSize);
2564     if (Status != STATUS_BUFFER_TOO_SMALL)
2565     {
2566         DPRINT1("CreateNlsSecurityDescriptor(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08x)\n", Status);
2567         goto Quit;
2568     }
2569 
2570     /* Allocate buffer for relative SD */
2571     RelativeSd = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, RelSdSize);
2572     if (RelativeSd == NULL)
2573     {
2574         DPRINT1("CreateNlsSecurityDescriptor(): Could not allocate memory for relative SD, not enough memory!\n");
2575         Status = STATUS_INSUFFICIENT_RESOURCES;
2576         goto Quit;
2577     }
2578 
2579     /* Convert it now */
2580     Status = RtlAbsoluteToSelfRelativeSD(&AbsoluteSd,
2581                                          RelativeSd,
2582                                          &RelSdSize);
2583     if (!NT_SUCCESS(Status))
2584     {
2585         DPRINT1("CreateNlsSecurityDescriptor(): Failed to convert absolute SD to self-relative format (Status 0x%08x)\n", Status);
2586         goto Quit;
2587     }
2588 
2589     /* Give the security descriptor to the caller */
2590     *SecurityDescriptor = RelativeSd;
2591 
2592 Quit:
2593     if (WorldSid != NULL)
2594     {
2595         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
2596     }
2597 
2598     if (Dacl != NULL)
2599     {
2600         RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
2601     }
2602 
2603     if (!NT_SUCCESS(Status))
2604     {
2605         if (RelativeSd != NULL)
2606         {
2607             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSd);
2608         }
2609     }
2610 
2611     return Status;
2612 }
2613 
2614 /*
2615  * @unimplemented
2616  */
2617 BOOL WINAPI IsValidUILanguage(LANGID langid)
2618 {
2619     STUB;
2620     return 0;
2621 }
2622 
2623 /*
2624  * @unimplemented
2625  */
2626 VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2)
2627 {
2628     STUB;
2629 }
2630 
2631 /*
2632  * @unimplemented
2633  */
2634 UINT WINAPI SetCPGlobal(UINT CodePage)
2635 {
2636     STUB;
2637     return 0;
2638 }
2639 
2640 /*
2641  * @unimplemented
2642  */
2643 BOOL
2644 WINAPI
2645 ValidateLCType(int a1, unsigned int a2, int a3, int a4)
2646 {
2647     STUB;
2648     return FALSE;
2649 }
2650 
2651 /*
2652  * @unimplemented
2653  */
2654 BOOL
2655 WINAPI
2656 NlsResetProcessLocale(VOID)
2657 {
2658     STUB;
2659     return TRUE;
2660 }
2661 
2662 /*
2663  * @unimplemented
2664  */
2665 VOID
2666 WINAPI
2667 GetDefaultSortkeySize(LPVOID lpUnknown)
2668 {
2669     STUB;
2670     lpUnknown = NULL;
2671 }
2672 
2673 /*
2674  * @unimplemented
2675  */
2676 VOID
2677 WINAPI
2678 GetLinguistLangSize(LPVOID lpUnknown)
2679 {
2680     STUB;
2681     lpUnknown = NULL;
2682 }
2683 
2684 /*
2685  * @unimplemented
2686  */
2687 BOOL
2688 WINAPI
2689 ValidateLocale(IN ULONG LocaleId)
2690 {
2691     STUB;
2692     return TRUE;
2693 }
2694 
2695 /*
2696  * @unimplemented
2697  */
2698 ULONG
2699 WINAPI
2700 NlsGetCacheUpdateCount(VOID)
2701 {
2702     STUB;
2703     return 0;
2704 }
2705 
2706 /*
2707  * @unimplemented
2708  */
2709 BOOL
2710 WINAPI
2711 IsNLSDefinedString(IN NLS_FUNCTION Function,
2712                    IN DWORD dwFlags,
2713                    IN LPNLSVERSIONINFO lpVersionInformation,
2714                    IN LPCWSTR lpString,
2715                    IN INT cchStr)
2716 {
2717     STUB;
2718     return TRUE;
2719 }
2720 
2721 /*
2722  * @unimplemented
2723  */
2724 BOOL
2725 WINAPI
2726 GetNLSVersion(IN NLS_FUNCTION Function,
2727               IN LCID Locale,
2728               IN OUT LPNLSVERSIONINFO lpVersionInformation)
2729 {
2730     STUB;
2731     return TRUE;
2732 }
2733 
2734 /*
2735  * @unimplemented
2736  */
2737 BOOL
2738 WINAPI
2739 GetNLSVersionEx(IN NLS_FUNCTION function,
2740                 IN LPCWSTR lpLocaleName,
2741                 IN OUT LPNLSVERSIONINFOEX lpVersionInformation)
2742 {
2743     STUB;
2744     return TRUE;
2745 }
2746 
2747 /* EOF */
2748