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