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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 2467 BOOL WINAPI IsValidUILanguage(LANGID langid) 2468 { 2469 STUB; 2470 return 0; 2471 } 2472 2473 /* 2474 * @unimplemented 2475 */ 2476 VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2) 2477 { 2478 STUB; 2479 } 2480 2481 /* 2482 * @unimplemented 2483 */ 2484 UINT WINAPI SetCPGlobal(UINT CodePage) 2485 { 2486 STUB; 2487 return 0; 2488 } 2489 2490 /* 2491 * @unimplemented 2492 */ 2493 BOOL 2494 WINAPI 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 2506 NlsResetProcessLocale(VOID) 2507 { 2508 STUB; 2509 return TRUE; 2510 } 2511 2512 /* 2513 * @unimplemented 2514 */ 2515 VOID 2516 WINAPI 2517 GetDefaultSortkeySize(LPVOID lpUnknown) 2518 { 2519 STUB; 2520 lpUnknown = NULL; 2521 } 2522 2523 /* 2524 * @unimplemented 2525 */ 2526 VOID 2527 WINAPI 2528 GetLinguistLangSize(LPVOID lpUnknown) 2529 { 2530 STUB; 2531 lpUnknown = NULL; 2532 } 2533 2534 /* 2535 * @unimplemented 2536 */ 2537 BOOL 2538 WINAPI 2539 ValidateLocale(IN ULONG LocaleId) 2540 { 2541 STUB; 2542 return TRUE; 2543 } 2544 2545 /* 2546 * @unimplemented 2547 */ 2548 ULONG 2549 WINAPI 2550 NlsGetCacheUpdateCount(VOID) 2551 { 2552 STUB; 2553 return 0; 2554 } 2555 2556 /* 2557 * @unimplemented 2558 */ 2559 BOOL 2560 WINAPI 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 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 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