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