reshuffling of dlls
[reactos.git] / reactos / dll / win32 / kernel32 / misc / nls.c
1 /*
2 * ReactOS Kernel
3 * Copyright (C) 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /**
20 * @brief National Language Support.
21 * @author Filip Navara
22 */
23
24 /* INCLUDES *******************************************************************/
25
26 #include <k32.h>
27 #define NDEBUG
28 #include "../include/debug.h"
29
30 /* GLOBAL VARIABLES ***********************************************************/
31
32 typedef struct _CODEPAGE_ENTRY
33 {
34 LIST_ENTRY Entry;
35 UINT CodePage;
36 HANDLE SectionHandle;
37 PBYTE SectionMapping;
38 CPTABLEINFO CodePageTable;
39 } CODEPAGE_ENTRY, *PCODEPAGE_ENTRY;
40
41 /* Sequence length based on the first character. */
42 static const char UTF8Length[128] =
43 {
44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
46 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
48 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
49 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
50 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
51 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0 /* 0xF0 - 0xFF */
52 };
53
54 /* First byte mask depending on UTF-8 sequence length. */
55 static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
56
57 /* FIXME: Change to HASH table or linear array. */
58 static LIST_ENTRY CodePageListHead;
59 static CODEPAGE_ENTRY AnsiCodePage;
60 static CODEPAGE_ENTRY OemCodePage;
61 static RTL_CRITICAL_SECTION CodePageListLock;
62
63 /* FORWARD DECLARATIONS *******************************************************/
64
65 BOOL STDCALL
66 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
67 LPSTR BaseName, LPSTR Result, ULONG ResultSize);
68
69 BOOL STDCALL
70 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
71
72 /* PRIVATE FUNCTIONS **********************************************************/
73
74 /**
75 * @name NlsInit
76 *
77 * Internal NLS related stuff initialization.
78 */
79
80 BOOL FASTCALL
81 NlsInit()
82 {
83 UNICODE_STRING DirName;
84 OBJECT_ATTRIBUTES ObjectAttributes;
85 HANDLE Handle;
86
87 InitializeListHead(&CodePageListHead);
88 RtlInitializeCriticalSection(&CodePageListLock);
89
90 /*
91 * FIXME: Eventually this should be done only for the NLS Server
92 * process, but since we don't have anything like that (yet?) we
93 * always try to create the "\Nls" directory here.
94 */
95 RtlInitUnicodeString(&DirName, L"\\Nls");
96 InitializeObjectAttributes(&ObjectAttributes, &DirName,
97 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
98 NULL, NULL);
99 if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes)))
100 {
101 NtClose(Handle);
102 }
103
104 /* Setup ANSI code page. */
105 AnsiCodePage.CodePage = CP_ACP;
106 AnsiCodePage.SectionHandle = NULL;
107 AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
108 RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
109 &AnsiCodePage.CodePageTable);
110 InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
111
112 /* Setup OEM code page. */
113 OemCodePage.CodePage = CP_OEMCP;
114 OemCodePage.SectionHandle = NULL;
115 OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
116 RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
117 &OemCodePage.CodePageTable);
118 InsertTailList(&CodePageListHead, &OemCodePage.Entry);
119
120 return TRUE;
121 }
122
123 /**
124 * @name NlsUninit
125 *
126 * Internal NLS related stuff uninitialization.
127 */
128
129 VOID FASTCALL
130 NlsUninit()
131 {
132 PCODEPAGE_ENTRY Current;
133
134 /* Delete the code page list. */
135 while (!IsListEmpty(&CodePageListHead))
136 {
137 Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
138 if (Current->SectionHandle != NULL)
139 {
140 UnmapViewOfFile(Current->SectionMapping);
141 NtClose(Current->SectionHandle);
142 }
143 RemoveHeadList(&CodePageListHead);
144 }
145 RtlDeleteCriticalSection(&CodePageListLock);
146 }
147
148 /**
149 * @name IntGetLoadedCodePageEntry
150 *
151 * Internal function to get structure containing a code page information
152 * of code page that is already loaded.
153 *
154 * @param CodePage
155 * Number of the code page. Special values like CP_OEMCP, CP_ACP
156 * or CP_UTF8 aren't allowed.
157 *
158 * @return Code page entry or NULL if the specified code page hasn't
159 * been loaded yet.
160 */
161
162 PCODEPAGE_ENTRY FASTCALL
163 IntGetLoadedCodePageEntry(UINT CodePage)
164 {
165 LIST_ENTRY *CurrentEntry;
166 PCODEPAGE_ENTRY Current;
167
168 RtlEnterCriticalSection(&CodePageListLock);
169 for (CurrentEntry = CodePageListHead.Flink;
170 CurrentEntry != &CodePageListHead;
171 CurrentEntry = CurrentEntry->Flink)
172 {
173 Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
174 if (Current->CodePage == CodePage)
175 {
176 RtlLeaveCriticalSection(&CodePageListLock);
177 return Current;
178 }
179 }
180 RtlLeaveCriticalSection(&CodePageListLock);
181
182 return NULL;
183 }
184
185 /**
186 * @name IntGetCodePageEntry
187 *
188 * Internal function to get structure containing a code page information.
189 *
190 * @param CodePage
191 * Number of the code page. Special values like CP_OEMCP, CP_ACP
192 * or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
193 *
194 * @return Code page entry.
195 */
196
197 static PCODEPAGE_ENTRY FASTCALL
198 IntGetCodePageEntry(UINT CodePage)
199 {
200 CHAR SectionName[40];
201 NTSTATUS Status;
202 HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle;
203 PBYTE SectionMapping;
204 OBJECT_ATTRIBUTES ObjectAttributes;
205 ANSI_STRING AnsiName;
206 UNICODE_STRING UnicodeName;
207 WCHAR FileName[MAX_PATH + 1];
208 UINT FileNamePos;
209 PCODEPAGE_ENTRY CodePageEntry;
210
211 if (CodePage == CP_THREAD_ACP)
212 {
213 if (!GetLocaleInfoW(GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE |
214 LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage,
215 sizeof(CodePage) / sizeof(WCHAR)))
216 {
217 /* Last error is set by GetLocaleInfoW. */
218 return 0;
219 }
220 }
221 else if (CodePage == CP_MACCP)
222 {
223 if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE |
224 LOCALE_RETURN_NUMBER, (WCHAR *)&CodePage,
225 sizeof(CodePage) / sizeof(WCHAR)))
226 {
227 /* Last error is set by GetLocaleInfoW. */
228 return 0;
229 }
230 }
231
232 /* Try searching for loaded page first. */
233 CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
234 if (CodePageEntry != NULL)
235 {
236 return CodePageEntry;
237 }
238
239 /*
240 * Yes, we really want to lock here. Otherwise it can happen that
241 * two parallel requests will try to get the entry for the same
242 * code page and we would load it twice.
243 */
244 RtlEnterCriticalSection(&CodePageListLock);
245
246 /* Generate the section name. */
247 if (!GetNlsSectionName(CodePage, 10, 0, "\\Nls\\NlsSectionCP",
248 SectionName, sizeof(SectionName)))
249 {
250 RtlLeaveCriticalSection(&CodePageListLock);
251 return NULL;
252 }
253 RtlInitAnsiString(&AnsiName, SectionName);
254 RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
255 InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0,
256 NULL, NULL);
257
258 /* Try to open the section first */
259 Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
260
261 /* If the section doesn't exist, try to create it. */
262 if (Status == STATUS_UNSUCCESSFUL ||
263 Status == STATUS_OBJECT_NAME_NOT_FOUND ||
264 Status == STATUS_OBJECT_PATH_NOT_FOUND)
265 {
266 FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
267 if (GetCPFileNameFromRegistry(CodePage, FileName + FileNamePos + 1,
268 MAX_PATH - FileNamePos - 1))
269 {
270 FileName[FileNamePos] = L'\\';
271 FileName[MAX_PATH] = 0;
272 FileHandle = CreateFileW(FileName, FILE_GENERIC_READ, FILE_SHARE_READ,
273 NULL, OPEN_EXISTING, 0, NULL);
274 Status = NtCreateSection(&SectionHandle, SECTION_MAP_READ,
275 &ObjectAttributes, NULL, PAGE_READONLY,
276 SEC_FILE, FileHandle);
277 }
278 }
279 RtlFreeUnicodeString(&UnicodeName);
280
281 if (!NT_SUCCESS(Status))
282 {
283 RtlLeaveCriticalSection(&CodePageListLock);
284 return NULL;
285 }
286
287 SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
288 if (SectionMapping == NULL)
289 {
290 NtClose(SectionHandle);
291 RtlLeaveCriticalSection(&CodePageListLock);
292 return NULL;
293 }
294
295 CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
296 if (CodePageEntry == NULL)
297 {
298 NtClose(SectionHandle);
299 RtlLeaveCriticalSection(&CodePageListLock);
300 return NULL;
301 }
302
303 CodePageEntry->CodePage = CodePage;
304 CodePageEntry->SectionHandle = SectionHandle;
305 CodePageEntry->SectionMapping = SectionMapping;
306 RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
307
308 /* Insert the new entry to list and unlock. Uff. */
309 InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
310 RtlLeaveCriticalSection(&CodePageListLock);
311
312 return CodePageEntry;
313 }
314
315 /**
316 * @name IntMultiByteToWideCharUTF8
317 *
318 * Internal version of MultiByteToWideChar for UTF8.
319 *
320 * @see MultiByteToWideChar
321 * @todo Add UTF8 validity checks.
322 */
323
324 static INT STDCALL
325 IntMultiByteToWideCharUTF8(DWORD Flags,
326 LPCSTR MultiByteString, INT MultiByteCount,
327 LPWSTR WideCharString, INT WideCharCount)
328 {
329 LPCSTR MbsEnd;
330 UCHAR Char, Length;
331 WCHAR WideChar;
332 LONG Count;
333
334 if (Flags != 0)
335 {
336 SetLastError(ERROR_INVALID_FLAGS);
337 return 0;
338 }
339
340 /* Does caller query for output buffer size? */
341 if (WideCharCount == 0)
342 {
343 MbsEnd = MultiByteString + MultiByteCount;
344 for (; MultiByteString < MbsEnd; WideCharCount++)
345 {
346 Char = *MultiByteString++;
347 if (Char < 0xC0)
348 continue;
349 MultiByteString += UTF8Length[Char - 0x80];
350 }
351 return WideCharCount;
352 }
353
354 MbsEnd = MultiByteString + MultiByteCount;
355 for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
356 {
357 Char = *MultiByteString++;
358 if (Char < 0x80)
359 {
360 *WideCharString++ = Char;
361 continue;
362 }
363 Length = UTF8Length[Char - 0x80];
364 WideChar = Char & UTF8Mask[Length];
365 while (Length && MultiByteString < MbsEnd)
366 {
367 WideChar = (WideChar << 6) | *MultiByteString++;
368 Length--;
369 }
370 *WideCharString++ = WideChar;
371 }
372
373 if (MultiByteString < MbsEnd)
374 SetLastError(ERROR_INSUFFICIENT_BUFFER);
375
376 return Count;
377 }
378
379 /**
380 * @name IntMultiByteToWideCharCP
381 *
382 * Internal version of MultiByteToWideChar for code page tables.
383 *
384 * @see MultiByteToWideChar
385 * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
386 * DBCS codepages.
387 */
388
389 static INT STDCALL
390 IntMultiByteToWideCharCP(UINT CodePage, DWORD Flags,
391 LPCSTR MultiByteString, INT MultiByteCount,
392 LPWSTR WideCharString, INT WideCharCount)
393 {
394 PCODEPAGE_ENTRY CodePageEntry;
395 PCPTABLEINFO CodePageTable;
396 LPCSTR TempString;
397 INT TempLength;
398
399 /* Get code page table. */
400 CodePageEntry = IntGetCodePageEntry(CodePage);
401 if (CodePageEntry == NULL)
402 {
403 SetLastError(ERROR_INVALID_PARAMETER);
404 return 0;
405 }
406 CodePageTable = &CodePageEntry->CodePageTable;
407
408 /* Different handling for DBCS code pages. */
409 if (CodePageTable->MaximumCharacterSize > 1)
410 {
411 /* UNIMPLEMENTED */
412 return 0;
413 }
414 else
415 {
416 /* Check for invalid characters. */
417 if (Flags & MB_ERR_INVALID_CHARS)
418 {
419 for (TempString = MultiByteString, TempLength = MultiByteCount;
420 TempLength > 0;
421 TempString++, TempLength--)
422 {
423 if (CodePageTable->MultiByteTable[(UCHAR)*TempString] ==
424 CodePageTable->UniDefaultChar &&
425 *TempString != CodePageEntry->CodePageTable.DefaultChar)
426 {
427 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
428 return 0;
429 }
430 }
431 }
432
433 /* Does caller query for output buffer size? */
434 if (WideCharCount == 0)
435 return MultiByteCount;
436
437 /* Adjust buffer size. Wine trick ;-) */
438 if (WideCharCount < MultiByteCount)
439 {
440 MultiByteCount = WideCharCount;
441 SetLastError(ERROR_INSUFFICIENT_BUFFER);
442 }
443
444 for (TempLength = MultiByteCount;
445 TempLength > 0;
446 MultiByteString++, TempLength--)
447 {
448 *WideCharString++ = CodePageTable->MultiByteTable[(UCHAR)*MultiByteString];
449 }
450
451 return MultiByteCount;
452 }
453 }
454
455 /**
456 * @name IntWideCharToMultiByteUTF8
457 *
458 * Internal version of WideCharToMultiByte for UTF8.
459 *
460 * @see WideCharToMultiByte
461 */
462
463 static INT STDCALL
464 IntWideCharToMultiByteUTF8(UINT CodePage, DWORD Flags,
465 LPCWSTR WideCharString, INT WideCharCount,
466 LPSTR MultiByteString, INT MultiByteCount,
467 LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
468 {
469 INT TempLength;
470 WCHAR Char;
471
472 /* Does caller query for output buffer size? */
473 if (MultiByteCount == 0)
474 {
475 for (TempLength = 0; WideCharCount;
476 WideCharCount--, WideCharString++)
477 {
478 TempLength++;
479 if (*WideCharString >= 0x80)
480 {
481 TempLength++;
482 if (*WideCharString >= 0x800)
483 TempLength++;
484 }
485 }
486 return TempLength;
487 }
488
489 for (TempLength = MultiByteCount; WideCharCount;
490 WideCharCount--, WideCharString++)
491 {
492 Char = *WideCharString;
493 if (Char < 0x80)
494 {
495 if (!TempLength)
496 {
497 SetLastError(ERROR_INSUFFICIENT_BUFFER);
498 break;
499 }
500 TempLength--;
501 *MultiByteString++ = Char;
502 continue;
503 }
504
505 if (Char < 0x800) /* 0x80-0x7ff: 2 bytes */
506 {
507 if (TempLength < 2)
508 {
509 SetLastError(ERROR_INSUFFICIENT_BUFFER);
510 break;
511 }
512 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
513 MultiByteString[0] = 0xc0 | Char;
514 MultiByteString += 2;
515 TempLength -= 2;
516 continue;
517 }
518
519 /* 0x800-0xffff: 3 bytes */
520 if (TempLength < 3)
521 {
522 SetLastError(ERROR_INSUFFICIENT_BUFFER);
523 break;
524 }
525 MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
526 MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
527 MultiByteString[0] = 0xe0 | Char;
528 MultiByteString += 3;
529 TempLength -= 3;
530 }
531
532 return MultiByteCount - TempLength;
533 }
534
535 /**
536 * @name IntWideCharToMultiByteCP
537 *
538 * Internal version of WideCharToMultiByte for code page tables.
539 *
540 * @see WideCharToMultiByte
541 * @todo Handle default characters and flags.
542 */
543
544 static INT STDCALL
545 IntWideCharToMultiByteCP(UINT CodePage, DWORD Flags,
546 LPCWSTR WideCharString, INT WideCharCount,
547 LPSTR MultiByteString, INT MultiByteCount,
548 LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
549 {
550 PCODEPAGE_ENTRY CodePageEntry;
551 PCPTABLEINFO CodePageTable;
552 INT TempLength;
553
554 /* Get code page table. */
555 CodePageEntry = IntGetCodePageEntry(CodePage);
556 if (CodePageEntry == NULL)
557 {
558 SetLastError(ERROR_INVALID_PARAMETER);
559 return 0;
560 }
561 CodePageTable = &CodePageEntry->CodePageTable;
562
563 /* Different handling for DBCS code pages. */
564 if (CodePageTable->MaximumCharacterSize > 1)
565 {
566 DPRINT1("WideCharToMultiByte for DBCS codepages is not implemented!\n");
567 return 0;
568 }
569 else
570 {
571 /* Does caller query for output buffer size? */
572 if (MultiByteCount == 0)
573 return WideCharCount;
574
575 /* Adjust buffer size. Wine trick ;-) */
576 if (MultiByteCount < WideCharCount)
577 {
578 WideCharCount = MultiByteCount;
579 SetLastError(ERROR_INSUFFICIENT_BUFFER);
580 }
581
582 for (TempLength = WideCharCount;
583 TempLength > 0;
584 WideCharString++, TempLength--)
585 {
586 *MultiByteString++ = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
587 }
588
589 /* FIXME */
590 if (UsedDefaultChar != NULL)
591 *UsedDefaultChar = FALSE;
592
593 return WideCharCount;
594 }
595 }
596
597 /**
598 * @name IntIsLeadByte
599 *
600 * Internal function to detect if byte is lead byte in specific character
601 * table.
602 */
603
604 static BOOL STDCALL
605 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
606 {
607 UINT LeadByteNo;
608
609 if (TableInfo->MaximumCharacterSize == 2)
610 {
611 for (LeadByteNo = 0; LeadByteNo < MAXIMUM_LEADBYTES; LeadByteNo++)
612 {
613 if (TableInfo->LeadByte[LeadByteNo] == Byte)
614 return TRUE;
615 }
616 }
617
618 return FALSE;
619 }
620
621 /* PUBLIC FUNCTIONS ***********************************************************/
622
623 /**
624 * @name GetNlsSectionName
625 *
626 * Construct a name of NLS section.
627 *
628 * @param CodePage
629 * Code page number.
630 * @param Base
631 * Integer base used for converting to string. Usually set to 10.
632 * @param Unknown
633 * As the name suggests the meaning of this parameter is unknown.
634 * The native version of Kernel32 passes it as the third parameter
635 * to NlsConvertIntegerToString function, which is used for the
636 * actual conversion of the code page number.
637 * @param BaseName
638 * Base name of the section. (ex. "\\Nls\\NlsSectionCP")
639 * @param Result
640 * Buffer that will hold the constructed name.
641 * @param ResultSize
642 * Size of the buffer for the result.
643 *
644 * @return TRUE if the buffer was large enough and was filled with
645 * the requested information, FALSE otherwise.
646 *
647 * @implemented
648 */
649
650 BOOL STDCALL
651 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
652 LPSTR BaseName, LPSTR Result, ULONG ResultSize)
653 {
654 CHAR Integer[11];
655
656 if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
657 return FALSE;
658
659 /*
660 * If the name including the terminating NULL character doesn't
661 * fit in the output buffer then fail.
662 */
663 if (strlen(Integer) + strlen(BaseName) >= ResultSize)
664 return FALSE;
665
666 lstrcpyA(Result, BaseName);
667 lstrcatA(Result, Integer);
668
669 return TRUE;
670 }
671
672 /**
673 * @name GetCPFileNameFromRegistry
674 *
675 * Get file name of code page definition file.
676 *
677 * @param CodePage
678 * Code page number to get file name of.
679 * @param FileName
680 * Buffer that is filled with file name of successful return. Can
681 * be set to NULL.
682 * @param FileNameSize
683 * Size of the buffer to hold file name in WCHARs.
684 *
685 * @return TRUE if the file name was retrieved, FALSE otherwise.
686 *
687 * @implemented
688 */
689
690 BOOL STDCALL
691 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
692 {
693 WCHAR ValueNameBuffer[11];
694 UNICODE_STRING KeyName, ValueName;
695 OBJECT_ATTRIBUTES ObjectAttributes;
696 NTSTATUS Status;
697 HANDLE KeyHandle;
698 PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
699 DWORD KvpiSize;
700
701 /* Convert the codepage number to string. */
702 ValueName.Buffer = ValueNameBuffer;
703 ValueName.MaximumLength = sizeof(ValueNameBuffer);
704 if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
705 return FALSE;
706
707 /* Open the registry key containing file name mappings. */
708 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
709 L"CurrentControlSet\\Control\\Nls\\CodePage");
710 InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
711 NULL, NULL);
712 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
713 if (!NT_SUCCESS(Status))
714 {
715 return FALSE;
716 }
717
718 /* Allocate buffer that will be used to query the value data. */
719 KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
720 (MAX_PATH * sizeof(WCHAR));
721 Kvpi = HeapAlloc(GetProcessHeap(), 0, KvpiSize);
722 if (Kvpi == NULL)
723 {
724 NtClose(KeyHandle);
725 return FALSE;
726 }
727
728 /* Query the file name for our code page. */
729 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
730 Kvpi, KvpiSize, &KvpiSize);
731
732 NtClose(KeyHandle);
733
734 /* Check if we succeded and the value is non-empty string. */
735 if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
736 Kvpi->DataLength > sizeof(WCHAR))
737 {
738 if (FileName != NULL)
739 {
740 lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
741 min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
742 }
743 return TRUE;
744 }
745
746 return FALSE;
747 }
748
749 /**
750 * @name IsValidCodePage
751 *
752 * Detect if specified code page is valid and present in the system.
753 *
754 * @param CodePage
755 * Code page number to query.
756 *
757 * @return TRUE if code page is present.
758 */
759
760 BOOL STDCALL
761 IsValidCodePage(UINT CodePage)
762 {
763 if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
764 return TRUE;
765 if (IntGetLoadedCodePageEntry(CodePage))
766 return TRUE;
767 return GetCPFileNameFromRegistry(CodePage, NULL, 0);
768 }
769
770 /**
771 * @name MultiByteToWideChar
772 *
773 * Convert a multi-byte string to wide-charater equivalent.
774 *
775 * @param CodePage
776 * Code page to be used to perform the conversion. It can be also
777 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
778 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
779 * for thread active code page, CP_UTF7 or CP_UTF8).
780 * @param Flags
781 * Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
782 * MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
783 * @param MultiByteString
784 * Input buffer.
785 * @param MultiByteCount
786 * Size of MultiByteString, or -1 if MultiByteString is NULL
787 * terminated.
788 * @param WideCharString
789 * Output buffer.
790 * @param WideCharCount
791 * Size in WCHARs of WideCharString, or 0 if the caller just wants
792 * to know how large WideCharString should be for a successful
793 * conversion.
794 *
795 * @return Zero on error, otherwise the number of WCHARs written
796 * in the WideCharString buffer.
797 *
798 * @implemented
799 */
800
801 INT STDCALL
802 MultiByteToWideChar(UINT CodePage, DWORD Flags,
803 LPCSTR MultiByteString, INT MultiByteCount,
804 LPWSTR WideCharString, INT WideCharCount)
805 {
806 /* Check the parameters. */
807 if (MultiByteString == NULL ||
808 (WideCharString == NULL && WideCharCount > 0) ||
809 (PVOID)MultiByteString == (PVOID)WideCharString)
810 {
811 SetLastError(ERROR_INVALID_PARAMETER);
812 return 0;
813 }
814
815 /* Determine the input string length. */
816 if (MultiByteCount < 0)
817 {
818 MultiByteCount = lstrlenA(MultiByteString) + 1;
819 }
820
821 switch (CodePage)
822 {
823 case CP_UTF8:
824 return IntMultiByteToWideCharUTF8(
825 Flags, MultiByteString, MultiByteCount,
826 WideCharString, WideCharCount);
827
828 case CP_UTF7:
829 DPRINT1("MultiByteToWideChar for CP_UTF7 is not implemented!\n");
830 return 0;
831
832 case CP_SYMBOL:
833 DPRINT1("MultiByteToWideChar for CP_SYMBOL is not implemented!\n");
834 return 0;
835
836 default:
837 return IntMultiByteToWideCharCP(
838 CodePage, Flags, MultiByteString, MultiByteCount,
839 WideCharString, WideCharCount);
840 }
841 }
842
843 /**
844 * @name WideCharToMultiByte
845 *
846 * Convert a wide-charater string to closest multi-byte equivalent.
847 *
848 * @param CodePage
849 * Code page to be used to perform the conversion. It can be also
850 * one of the special values (CP_ACP for ANSI code page, CP_MACCP
851 * for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
852 * for thread active code page, CP_UTF7 or CP_UTF8).
853 * @param Flags
854 * Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
855 * WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
856 * @param WideCharString
857 * Points to the wide-character string to be converted.
858 * @param WideCharCount
859 * Size in WCHARs of WideCharStr, or 0 if the caller just wants to
860 * know how large WideCharString should be for a successful conversion.
861 * @param MultiByteString
862 * Points to the buffer to receive the translated string.
863 * @param MultiByteCount
864 * Specifies the size in bytes of the buffer pointed to by the
865 * MultiByteString parameter. If this value is zero, the function
866 * returns the number of bytes required for the buffer.
867 * @param DefaultChar
868 * Points to the character used if a wide character cannot be
869 * represented in the specified code page. If this parameter is
870 * NULL, a system default value is used.
871 * @param UsedDefaultChar
872 * Points to a flag that indicates whether a default character was
873 * used. This parameter can be NULL.
874 *
875 * @return Zero on error, otherwise the number of bytes written in the
876 * MultiByteString buffer. Or the number of bytes needed for
877 * the MultiByteString buffer if MultiByteCount is zero.
878 *
879 * @implemented
880 */
881
882 INT STDCALL
883 WideCharToMultiByte(UINT CodePage, DWORD Flags,
884 LPCWSTR WideCharString, INT WideCharCount,
885 LPSTR MultiByteString, INT MultiByteCount,
886 LPCSTR DefaultChar, LPBOOL UsedDefaultChar)
887 {
888 /* Check the parameters. */
889 if (WideCharString == NULL ||
890 (MultiByteString == NULL && MultiByteCount > 0) ||
891 (PVOID)WideCharString == (PVOID)MultiByteString)
892 {
893 SetLastError(ERROR_INVALID_PARAMETER);
894 return 0;
895 }
896
897 /* Determine the input string length. */
898 if (WideCharCount < 0)
899 {
900 WideCharCount = lstrlenW(WideCharString) + 1;
901 }
902
903 switch (CodePage)
904 {
905 case CP_UTF8:
906 return IntWideCharToMultiByteUTF8(
907 CodePage, Flags, WideCharString, WideCharCount,
908 MultiByteString, MultiByteCount, DefaultChar,
909 UsedDefaultChar);
910
911 case CP_UTF7:
912 DPRINT1("WideCharToMultiByte for CP_UTF7 is not implemented!\n");
913 return 0;
914
915 case CP_SYMBOL:
916 DPRINT1("WideCharToMultiByte for CP_SYMBOL is not implemented!\n");
917 return 0;
918
919 default:
920 return IntWideCharToMultiByteCP(
921 CodePage, Flags, WideCharString, WideCharCount,
922 MultiByteString, MultiByteCount, DefaultChar,
923 UsedDefaultChar);
924 }
925 }
926
927 /**
928 * @name GetACP
929 *
930 * Get active ANSI code page number.
931 *
932 * @implemented
933 */
934
935 UINT STDCALL
936 GetACP(VOID)
937 {
938 return AnsiCodePage.CodePageTable.CodePage;
939 }
940
941 /**
942 * @name GetOEMCP
943 *
944 * Get active OEM code page number.
945 *
946 * @implemented
947 */
948
949 UINT STDCALL
950 GetOEMCP(VOID)
951 {
952 return OemCodePage.CodePageTable.CodePage;
953 }
954
955 /**
956 * @name IsDBCSLeadByteEx
957 *
958 * Determine if passed byte is lead byte in specified code page.
959 *
960 * @implemented
961 */
962
963 BOOL STDCALL
964 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
965 {
966 PCODEPAGE_ENTRY CodePageEntry;
967
968 CodePageEntry = IntGetCodePageEntry(CodePage);
969 if (CodePageEntry != NULL)
970 return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
971
972 SetLastError(ERROR_INVALID_PARAMETER);
973 return FALSE;
974 }
975
976 /**
977 * @name IsDBCSLeadByteEx
978 *
979 * Determine if passed byte is lead byte in current ANSI code page.
980 *
981 * @implemented
982 */
983
984 BOOL STDCALL
985 IsDBCSLeadByte(BYTE TestByte)
986 {
987 return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
988 }
989
990 /* EOF */