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