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