8e5a8c0f22a365fe214da03db590417c8d83444a
[reactos.git] / reactos / lib / kernel32 / misc / profile.c
1 /* $Id: profile.c,v 1.12 2004/06/13 20:04:56 navaraf Exp $
2 *
3 * Imported from Wine
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
6 *
7 * PROJECT: ReactOS system libraries
8 * FILE: lib/kernel32/misc/profile.c
9 * PURPOSE: Profiles functions
10 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
11 * modified from WINE [ Onno Hovers, (onno@stack.urc.tue.nl) ]
12 * UPDATE HISTORY:
13 * Created 01/11/98
14 */
15
16 #include <k32.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 static CRITICAL_SECTION ProfileLock;
22
23 typedef struct tagPROFILEKEY
24 {
25 WCHAR *Value;
26 struct tagPROFILEKEY *Next;
27 WCHAR Name[1];
28 } PROFILEKEY;
29
30 typedef struct tagPROFILESECTION
31 {
32 struct tagPROFILEKEY *Key;
33 struct tagPROFILESECTION *Next;
34 WCHAR Name[1];
35 } PROFILESECTION;
36
37
38 typedef struct
39 {
40 BOOL Changed;
41 PROFILESECTION *Section;
42 WCHAR *FullName;
43 FILETIME LastWriteTime;
44 } PROFILE;
45
46
47 #define N_CACHED_PROFILES 10
48
49 /* Cached profile files */
50 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
51
52 #define CurProfile (MRUProfile[0])
53
54 #define PROFILE_MAX_LINE_LEN 1024
55
56 /* Check for comments in profile */
57 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
58
59 /* FUNCTIONS *****************************************************************/
60
61 /***********************************************************************
62 * PROFILE_CopyEntry
63 *
64 * Copy the content of an entry into a buffer, removing quotes, and possibly
65 * translating environment variables.
66 */
67 STATIC void FASTCALL
68 PROFILE_CopyEntry(LPWSTR Buffer, LPCWSTR Value, int Len, BOOL StripQuote)
69 {
70 WCHAR Quote = L'\0';
71
72 if (NULL == Buffer)
73 {
74 return;
75 }
76
77 if (StripQuote && (L'\'' == *Value || L'\"' == *Value))
78 {
79 if (L'\0' != Value[1] && (Value[wcslen(Value) - 1] == *Value))
80 {
81 Quote = *Value++;
82 }
83 }
84
85 lstrcpynW(Buffer, Value, Len);
86 if (L'\0' != Quote && wcslen(Value) <= Len)
87 {
88 Buffer[wcslen(Buffer) - 1] = L'\0';
89 }
90 }
91
92
93 /***********************************************************************
94 * PROFILE_Save
95 *
96 * Save a profile tree to a file.
97 */
98 STATIC void FASTCALL
99 PROFILE_Save(HANDLE File, PROFILESECTION *Section)
100 {
101 PROFILEKEY *Key;
102 char Buffer[PROFILE_MAX_LINE_LEN];
103 DWORD BytesWritten;
104
105 for ( ; NULL != Section; Section = Section->Next)
106 {
107 if (L'\0' != Section->Name[0])
108 {
109 strcpy(Buffer, "\r\n[");
110 WideCharToMultiByte(CP_ACP, 0, Section->Name, -1, Buffer + 3, sizeof(Buffer) - 6, NULL, NULL);
111 strcat(Buffer, "]\r\n");
112 WriteFile(File, Buffer, strlen(Buffer), &BytesWritten, NULL);
113 }
114 for (Key = Section->Key; NULL != Key; Key = Key->Next)
115 {
116 WideCharToMultiByte(CP_ACP, 0, Key->Name, -1, Buffer, sizeof(Buffer) - 3, NULL, NULL);
117 if (Key->Value)
118 {
119 strcat(Buffer, "=");
120 WideCharToMultiByte(CP_ACP, 0, Key->Value, -1, Buffer + strlen(Buffer),
121 sizeof(Buffer) - strlen(Buffer) - 2, NULL, NULL);
122 }
123 strcat(Buffer, "\r\n");
124 WriteFile(File, Buffer, strlen(Buffer), &BytesWritten, NULL);
125 }
126 }
127 }
128
129
130 /***********************************************************************
131 * PROFILE_Free
132 *
133 * Free a profile tree.
134 */
135 STATIC void FASTCALL
136 PROFILE_Free(PROFILESECTION *Section)
137 {
138 PROFILESECTION *NextSection;
139 PROFILEKEY *Key, *NextKey;
140
141 for ( ; NULL != Section; Section = NextSection)
142 {
143 for (Key = Section->Key; NULL != Key; Key = NextKey)
144 {
145 NextKey = Key->Next;
146 if (NULL != Key->Value)
147 {
148 HeapFree(GetProcessHeap(), 0, Key->Value);
149 }
150 HeapFree(GetProcessHeap(), 0, Key);
151 }
152 NextSection = Section->Next;
153 HeapFree(GetProcessHeap(), 0, Section);
154 }
155 }
156
157 STATIC inline int
158 PROFILE_isspace(char c)
159 {
160 if (isspace(c))
161 {
162 return 1;
163 }
164 if ('\r' == c || 0x1a == c)
165 {
166 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
167 return 1;
168 }
169 return 0;
170 }
171
172
173 /***********************************************************************
174 * PROFILE_Load
175 *
176 * Load a profile tree from a file.
177 */
178 static PROFILESECTION * FASTCALL
179 PROFILE_Load(HANDLE File)
180 {
181 char *p, *p2;
182 int Len;
183 PROFILESECTION *Section, *FirstSection;
184 PROFILESECTION **NextSection;
185 PROFILEKEY *Key, *PrevKey, **NextKey;
186 LARGE_INTEGER FileSize;
187 HANDLE Mapping;
188 PVOID BaseAddress;
189 char *Input, *End;
190
191 FileSize.u.LowPart = GetFileSize(File, &FileSize.u.HighPart);
192 if (INVALID_FILE_SIZE == FileSize.u.LowPart && 0 != GetLastError())
193 {
194 return NULL;
195 }
196
197 FirstSection = HeapAlloc(GetProcessHeap(), 0, sizeof(*Section));
198 if (NULL == FirstSection)
199 {
200 return NULL;
201 }
202 FirstSection->Name[0] = L'\0';
203 FirstSection->Key = NULL;
204 FirstSection->Next = NULL;
205 NextSection = &FirstSection->Next;
206 NextKey = &FirstSection->Key;
207 PrevKey = NULL;
208
209 if (0 == FileSize.QuadPart)
210 {
211 return FirstSection;
212 }
213 Mapping = CreateFileMappingW(File, NULL, PAGE_READONLY | SEC_COMMIT, 0, 0, NULL);
214 if (NULL == Mapping)
215 {
216 HeapFree(GetProcessHeap(), 0, FirstSection);
217 return NULL;
218 }
219 BaseAddress = MapViewOfFile(Mapping, FILE_MAP_READ, 0, 0, 0);
220 if (NULL == BaseAddress)
221 {
222 CloseHandle(Mapping);
223 HeapFree(GetProcessHeap(), 0, FirstSection);
224 return NULL;
225 }
226
227 Input = (char *) BaseAddress;
228 End = Input + FileSize.QuadPart;
229 while (Input < End)
230 {
231 while (Input < End && PROFILE_isspace(*Input))
232 {
233 Input++;
234 }
235 if (End <= Input)
236 {
237 break;
238 }
239 if ('[' == *Input) /* section start */
240 {
241 p = ++Input;
242 while (p < End && ']' != *p && '\n' != *p)
243 {
244 p++;
245 }
246 if (p < End && ']' == *p)
247 {
248 Len = p - Input;
249 if (NULL == (Section = HeapAlloc(GetProcessHeap(), 0,
250 sizeof(*Section) + Len * sizeof(WCHAR))))
251 {
252 break;
253 }
254 MultiByteToWideChar(CP_ACP, 0, Input, Len, Section->Name, Len);
255 Section->Name[Len] = L'\0';
256 Section->Key = NULL;
257 Section->Next = NULL;
258 *NextSection = Section;
259 NextSection = &Section->Next;
260 NextKey = &Section->Key;
261 PrevKey = NULL;
262
263 DPRINT("New section: %S\n", Section->Name);
264
265 }
266 Input = p;
267 while (Input < End && '\n' != *Input)
268 {
269 Input++;
270 }
271 Input++; /* Skip past the \n */
272 continue;
273 }
274
275 p = Input;
276 p2 = p;
277 while (p < End && '=' != *p && '\n' != *p)
278 {
279 if (! PROFILE_isspace(*p))
280 {
281 p2 = p;
282 }
283 p++;
284 }
285
286 if (p < End && '=' == *p)
287 {
288 Len = p2 - Input + 1;
289 if (NULL == (Key = HeapAlloc(GetProcessHeap(), 0,
290 sizeof(*Key) + Len * sizeof(WCHAR))))
291 {
292 break;
293 }
294 MultiByteToWideChar(CP_ACP, 0, Input, Len, Key->Name, Len);
295 Key->Name[Len] = L'\0';
296 Input = p + 1;
297 while (Input < End && '\n' != *Input && PROFILE_isspace(*Input))
298 {
299 Input++;
300 }
301 if (End <= Input || '\n' == *Input)
302 {
303 Key->Value = NULL;
304 }
305 else
306 {
307 p2 = Input;
308 p = Input + 1;
309 while (p < End && '\n' != *p)
310 {
311 if (! PROFILE_isspace(*p))
312 {
313 p2 = p;
314 }
315 p++;
316 }
317 Len = p2 - Input + 1;
318 if (NULL == (Key->Value = HeapAlloc(GetProcessHeap(), 0,
319 (Len + 1) * sizeof(WCHAR))))
320 {
321 break;
322 }
323 MultiByteToWideChar(CP_ACP, 0, Input, Len, Key->Value, Len);
324 Key->Value[Len] = L'\0';
325
326 Key->Next = NULL;
327 *NextKey = Key;
328 NextKey = &Key->Next;
329 PrevKey = Key;
330
331 DPRINT("New key: name=%S, value=%S\n",
332 Key->Name, NULL != Key->Value ? Key->Value : L"(none)");
333
334 Input = p;
335 }
336 }
337 while (Input < End && '\n' != *Input)
338 {
339 Input++;
340 }
341 Input++; /* Skip past the \n */
342 }
343
344 UnmapViewOfFile(BaseAddress);
345 CloseHandle(Mapping);
346
347 return FirstSection;
348 }
349
350
351 /***********************************************************************
352 * PROFILE_Find
353 *
354 * Find a key in a profile tree, optionally creating it.
355 */
356 static PROFILEKEY * FASTCALL
357 PROFILE_Find(PROFILESECTION **Section, LPCWSTR SectionName,
358 LPCWSTR KeyName, BOOL Create, BOOL CreateAlways)
359 {
360 LPCWSTR p;
361 int SecLen, KeyLen;
362
363 while (PROFILE_isspace(*SectionName))
364 {
365 SectionName++;
366 }
367 p = SectionName + wcslen(SectionName) - 1;
368 while (SectionName < p && PROFILE_isspace(*p))
369 {
370 p--;
371 }
372 SecLen = p - SectionName + 1;
373
374 while (PROFILE_isspace(*KeyName))
375 {
376 KeyName++;
377 }
378 p = KeyName + wcslen(KeyName) - 1;
379 while (KeyName < p && PROFILE_isspace(*p))
380 {
381 p--;
382 }
383 KeyLen = p - KeyName + 1;
384
385 while (NULL != *Section)
386 {
387 if (L'\0' != ((*Section)->Name[0])
388 && 0 == _wcsnicmp((*Section)->Name, SectionName, SecLen)
389 && L'\0' == ((*Section)->Name)[SecLen])
390 {
391 PROFILEKEY **Key = &(*Section)->Key;
392
393 while (NULL != *Key)
394 {
395 /* If create_always is FALSE then we check if the keyname
396 * already exists. Otherwise we add it regardless of its
397 * existence, to allow keys to be added more than once in
398 * some cases.
399 */
400 if (! CreateAlways)
401 {
402 if (0 == _wcsnicmp((*Key)->Name, KeyName, KeyLen)
403 && L'\0' == ((*Key)->Name)[KeyLen])
404 {
405 return *Key;
406 }
407 }
408 Key = &(*Key)->Next;
409 }
410 if (! Create)
411 {
412 return NULL;
413 }
414 if (NULL == (*Key = HeapAlloc(GetProcessHeap(), 0,
415 sizeof(PROFILEKEY) + wcslen(KeyName) * sizeof(WCHAR))))
416 {
417 return NULL;
418 }
419 wcscpy((*Key)->Name, KeyName);
420 (*Key)->Value = NULL;
421 (*Key)->Next = NULL;
422 return *Key;
423 }
424 Section = &(*Section)->Next;
425 }
426
427 if (! Create)
428 {
429 return NULL;
430 }
431
432 *Section = HeapAlloc(GetProcessHeap(), 0,
433 sizeof(PROFILESECTION) + wcslen(SectionName) * sizeof(WCHAR));
434 if (NULL == *Section)
435 {
436 return NULL;
437 }
438 wcscpy((*Section)->Name, SectionName);
439 (*Section)->Next = NULL;
440 if (NULL == ((*Section)->Key = HeapAlloc(GetProcessHeap(), 0,
441 sizeof(PROFILEKEY) + wcslen(KeyName) * sizeof(WCHAR))))
442 {
443 HeapFree(GetProcessHeap(), 0, *Section);
444 return NULL;
445 }
446 wcscpy((*Section)->Key->Name, KeyName );
447 (*Section)->Key->Value = NULL;
448 (*Section)->Key->Next = NULL;
449
450 return (*Section)->Key;
451 }
452
453
454 /***********************************************************************
455 * PROFILE_FlushFile
456 *
457 * Flush the current profile to disk if changed.
458 */
459 STATIC BOOL FASTCALL
460 PROFILE_FlushFile(void)
461 {
462 HANDLE File = INVALID_HANDLE_VALUE;
463 FILETIME LastWriteTime;
464
465 if (NULL == CurProfile)
466 {
467 DPRINT("No current profile!\n");
468 return FALSE;
469 }
470
471 if (! CurProfile->Changed || NULL == CurProfile->FullName)
472 {
473 return TRUE;
474 }
475
476 File = CreateFileW(CurProfile->FullName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
477 if (INVALID_HANDLE_VALUE == File)
478 {
479 DPRINT1("could not save profile file %s\n", CurProfile->FullName);
480 return FALSE;
481 }
482
483 DPRINT("Saving %S\n", CurProfile->FullName);
484 PROFILE_Save(File, CurProfile->Section);
485 if (GetFileTime(File, NULL, NULL, &LastWriteTime))
486 {
487 CurProfile->LastWriteTime.dwLowDateTime = LastWriteTime.dwLowDateTime;
488 CurProfile->LastWriteTime.dwHighDateTime = LastWriteTime.dwHighDateTime;
489 }
490 CurProfile->Changed = FALSE;
491 CloseHandle(File);
492
493 return TRUE;
494 }
495
496
497 /***********************************************************************
498 * PROFILE_ReleaseFile
499 *
500 * Flush the current profile to disk and remove it from the cache.
501 */
502 STATIC void FASTCALL
503 PROFILE_ReleaseFile(void)
504 {
505 PROFILE_FlushFile();
506 PROFILE_Free(CurProfile->Section);
507 if (NULL != CurProfile->FullName)
508 {
509 HeapFree(GetProcessHeap(), 0, CurProfile->FullName);
510 }
511 CurProfile->Changed = FALSE;
512 CurProfile->Section = NULL;
513 CurProfile->FullName = NULL;
514 CurProfile->LastWriteTime.dwLowDateTime = 0;
515 CurProfile->LastWriteTime.dwHighDateTime = 0;
516 }
517
518
519 /***********************************************************************
520 * PROFILE_Open
521 *
522 * Open a profile file, checking the cached file first.
523 */
524 STATIC BOOL FASTCALL
525 PROFILE_Open(LPCWSTR FileName)
526 {
527 WCHAR FullName[MAX_PATH];
528 HANDLE File = INVALID_HANDLE_VALUE;
529 int i, j;
530 PROFILE *TempProfile;
531 DWORD FullLen;
532 FILETIME LastWriteTime;
533
534 /* Build full path name */
535
536 if (wcschr(FileName, L'/') || wcschr(FileName, L'\\') ||
537 wcschr(FileName, L':'))
538 {
539 FullLen = GetFullPathNameW(FileName, MAX_PATH, FullName, NULL);
540 if (0 == FullLen || MAX_PATH < FullLen)
541 {
542 return FALSE;
543 }
544 }
545 else
546 {
547 FullLen = GetWindowsDirectoryW(FullName, MAX_PATH);
548 if (0 == FullLen || MAX_PATH < FullLen + 1 + wcslen(FileName))
549 {
550 return FALSE;
551 }
552 wcscat(FullName, L"\\");
553 wcscat(FullName, FileName);
554 }
555 #if 0 /* FIXME: Not yet implemented */
556 FullLen = GetLongPathNameW(FullName, FullName, MAX_PATH);
557 if (0 == FullLen || MAX_PATH < FullLen)
558 {
559 return FALSE;
560 }
561 #endif
562
563 /* Check for a match */
564
565 for (i = 0; i < N_CACHED_PROFILES; i++)
566 {
567 if (NULL != MRUProfile[i]->FullName && 0 == wcscmp(FullName, MRUProfile[i]->FullName))
568 {
569 if (0 != i)
570 {
571 PROFILE_FlushFile();
572 TempProfile = MRUProfile[i];
573 for(j = i; j > 0; j--)
574 {
575 MRUProfile[j] = MRUProfile[j - 1];
576 }
577 CurProfile = TempProfile;
578 }
579 File = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
580 NULL, OPEN_EXISTING, 0, NULL);
581 if (INVALID_HANDLE_VALUE == File)
582 {
583 return FALSE;
584 }
585 if (GetFileTime(File, NULL, NULL, &LastWriteTime) &&
586 LastWriteTime.dwLowDateTime == CurProfile->LastWriteTime.dwLowDateTime &&
587 LastWriteTime.dwHighDateTime == CurProfile->LastWriteTime.dwHighDateTime)
588 {
589 DPRINT("(%S): already opened (mru = %d)\n", FileName, i);
590 }
591 else
592 {
593 DPRINT("(%S): already opened, needs refreshing (mru = %d)\n", FileName, i);
594 }
595 CloseHandle(File);
596
597 return TRUE;
598 }
599 }
600
601 /* Flush the old current profile */
602 PROFILE_FlushFile();
603
604 /* Make the oldest profile the current one only in order to get rid of it */
605 if (N_CACHED_PROFILES == i)
606 {
607 TempProfile = MRUProfile[N_CACHED_PROFILES - 1];
608 for(i = N_CACHED_PROFILES - 1; i > 0; i--)
609 {
610 MRUProfile[i] = MRUProfile[i - 1];
611 }
612 CurProfile = TempProfile;
613 }
614 if (NULL != CurProfile->FullName)
615 {
616 PROFILE_ReleaseFile();
617 }
618
619 /* OK, now that CurProfile is definitely free we assign it our new file */
620 CurProfile->FullName = HeapAlloc(GetProcessHeap(), 0, (wcslen(FullName) + 1) * sizeof(WCHAR));
621 if (NULL != CurProfile->FullName)
622 {
623 wcscpy(CurProfile->FullName, FullName);
624 }
625
626 /* Try to open the profile file */
627 File = CreateFileW(FullName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
628 if (INVALID_HANDLE_VALUE != File)
629 {
630 CurProfile->Section = PROFILE_Load(File);
631 if (GetFileTime(File, NULL, NULL, &LastWriteTime))
632 {
633 CurProfile->LastWriteTime.dwLowDateTime = LastWriteTime.dwLowDateTime;
634 CurProfile->LastWriteTime.dwHighDateTime = LastWriteTime.dwHighDateTime;
635 }
636 CloseHandle(File);
637 }
638 else
639 {
640 /* Does not exist yet, we will create it in PROFILE_FlushFile */
641 DPRINT("profile file %S not found\n", FileName);
642 }
643
644 return TRUE;
645 }
646
647
648 /***********************************************************************
649 * PROFILE_GetSection
650 *
651 * Returns all keys of a section.
652 * If ReturnValues is TRUE, also include the corresponding values.
653 */
654 static INT FASTCALL
655 PROFILE_GetSection(PROFILESECTION *Section, LPCWSTR SectionName,
656 LPWSTR Buffer, UINT Len, BOOL ReturnValues )
657 {
658 PROFILEKEY *Key;
659
660 if (NULL == Buffer)
661 {
662 return 0;
663 }
664
665 DPRINT("%S,%p,%u\n", SectionName, Buffer, Len);
666
667 while (NULL != Section)
668 {
669 if (L'\0' != Section->Name[0] && 0 == _wcsicmp(Section->Name, SectionName))
670 {
671 UINT OldLen = Len;
672 for (Key = Section->Key; NULL != Key; Key = Key->Next)
673 {
674 if (Len <= 2)
675 {
676 break;
677 }
678 if (L'\0' == *Key->Name)
679 {
680 continue; /* Skip empty lines */
681 }
682 if (IS_ENTRY_COMMENT(Key->Name))
683 {
684 continue; /* Skip comments */
685 }
686 PROFILE_CopyEntry(Buffer, Key->Name, Len - 1, FALSE);
687 Len -= wcslen(Buffer) + 1;
688 Buffer += wcslen(Buffer) + 1;
689 if (Len < 2)
690 {
691 break;
692 }
693 if (ReturnValues && NULL != Key->Value)
694 {
695 Buffer[-1] = L'=';
696 PROFILE_CopyEntry(Buffer, Key->Value, Len - 1, 0);
697 Len -= wcslen(Buffer) + 1;
698 Buffer += wcslen(Buffer) + 1;
699 }
700 }
701 *Buffer = L'\0';
702 if (Len <= 1)
703 /*If either lpszSection or lpszKey is NULL and the supplied
704 destination buffer is too small to hold all the strings,
705 the last string is truncated and followed by two null characters.
706 In this case, the return value is equal to cchReturnBuffer
707 minus two. */
708 {
709 Buffer[-1] = L'\0';
710 return OldLen - 2;
711 }
712 return OldLen - Len;
713 }
714 Section = Section->Next;
715 }
716
717 Buffer[0] = Buffer[1] = L'\0';
718
719 return 0;
720 }
721
722 /* See GetPrivateProfileSectionNamesW for documentation */
723 STATIC INT FASTCALL
724 PROFILE_GetSectionNames(LPWSTR Buffer, UINT Len)
725 {
726 LPWSTR Buf;
727 UINT f,l;
728 PROFILESECTION *Section;
729
730 if (NULL == Buffer || 0 == Len)
731 {
732 return 0;
733 }
734
735 if (1 == Len)
736 {
737 *Buffer = L'\0';
738 return 0;
739 }
740
741 f = Len - 1;
742 Buf = Buffer;
743 Section = CurProfile->Section;
744 while (NULL != Section)
745 {
746 if (L'\0' != Section->Name[0])
747 {
748 l = wcslen(Section->Name) + 1;
749 if (f < l)
750 {
751 if (0 < f)
752 {
753 wcsncpy(Buf, Section->Name, f - 1);
754 Buf += f - 1;
755 *Buf++ = L'\0';
756 }
757 *Buf = L'\0';
758 return Len-2;
759 }
760 wcscpy(Buf, Section->Name);
761 Buf += l;
762 f -= l;
763 }
764 Section = Section->Next;
765 }
766
767 *Buf='\0';
768 return Buf - Buffer;
769 }
770
771
772 /***********************************************************************
773 * PROFILE_GetString
774 *
775 * Get a profile string.
776 */
777 static INT FASTCALL
778 PROFILE_GetString(LPCWSTR Section, LPCWSTR KeyName,
779 LPCWSTR DefVal, LPWSTR Buffer, UINT Len)
780 {
781 PROFILEKEY *Key = NULL;
782
783 if (NULL == Buffer)
784 {
785 return 0;
786 }
787
788 if (NULL == DefVal)
789 {
790 DefVal = L"";
791 }
792
793 if (NULL != KeyName)
794 {
795 if (L'\0' == KeyName[0])
796 {
797 return 0;
798 }
799 Key = PROFILE_Find(&CurProfile->Section, Section, KeyName, FALSE, FALSE);
800 PROFILE_CopyEntry(Buffer, (NULL != Key && NULL != Key->Value) ? Key->Value : DefVal,
801 Len, TRUE );
802 DPRINT("(%S,%S,%S): returning %S\n", Section, KeyName, DefVal, Buffer);
803 return wcslen(Buffer);
804 }
805
806 if (NULL != Section && L'\0' != Section[0])
807 {
808 INT Ret = PROFILE_GetSection(CurProfile->Section, Section, Buffer, Len, FALSE);
809 if (L'\0' == Buffer[0]) /* no luck -> def_val */
810 {
811 PROFILE_CopyEntry(Buffer, DefVal, Len, TRUE);
812 Ret = wcslen(Buffer);
813 }
814 return Ret;
815 }
816
817 Buffer[0] = '\0';
818
819 return 0;
820 }
821
822
823 /***********************************************************************
824 * PROFILE_DeleteSection
825 *
826 * Delete a section from a profile tree.
827 */
828 STATIC BOOL FASTCALL
829 PROFILE_DeleteSection(PROFILESECTION **Section, LPCWSTR Name)
830 {
831 while (NULL != *Section)
832 {
833 if (L'\0' != (*Section)->Name[0] && 0 == _wcsicmp((*Section)->Name, Name))
834 {
835 PROFILESECTION *ToDel = *Section;
836 *Section = ToDel->Next;
837 ToDel->Next = NULL;
838 PROFILE_Free(ToDel);
839 return TRUE;
840 }
841 Section = &(*Section)->Next;
842 }
843
844 return FALSE;
845 }
846
847
848 /***********************************************************************
849 * PROFILE_DeleteKey
850 *
851 * Delete a key from a profile tree.
852 */
853 STATIC BOOL FASTCALL
854 PROFILE_DeleteKey(PROFILESECTION **Section,
855 LPCWSTR SectionName, LPCWSTR KeyName)
856 {
857 while (*Section)
858 {
859 if (L'\0' != (*Section)->Name[0] && 0 == _wcsicmp((*Section)->Name, SectionName))
860 {
861 PROFILEKEY **Key = &(*Section)->Key;
862 while (NULL != *Key)
863 {
864 if (0 == _wcsicmp((*Key)->Name, KeyName))
865 {
866 PROFILEKEY *ToDel = *Key;
867 *Key = ToDel->Next;
868 if (NULL != ToDel->Value)
869 {
870 HeapFree(GetProcessHeap(), 0, ToDel->Value);
871 }
872 HeapFree(GetProcessHeap(), 0, ToDel);
873 return TRUE;
874 }
875 Key = &(*Key)->Next;
876 }
877 }
878 Section = &(*Section)->Next;
879 }
880
881 return FALSE;
882 }
883
884
885 /***********************************************************************
886 * PROFILE_SetString
887 *
888 * Set a profile string.
889 */
890 STATIC BOOL FASTCALL
891 PROFILE_SetString(LPCWSTR SectionName, LPCWSTR KeyName,
892 LPCWSTR Value, BOOL CreateAlways )
893 {
894 PROFILEKEY *Key;
895
896 if (NULL == KeyName) /* Delete a whole section */
897 {
898 DPRINT("(%S)\n", SectionName);
899 CurProfile->Changed |= PROFILE_DeleteSection(&CurProfile->Section,
900 SectionName);
901 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
902 this is not an error on application's level.*/
903 }
904
905 if (NULL == Value) /* Delete a key */
906 {
907 DPRINT("(%S,%S)\n", SectionName, KeyName);
908 CurProfile->Changed |= PROFILE_DeleteKey(&CurProfile->Section,
909 SectionName, KeyName);
910 return TRUE; /* same error handling as above */
911 }
912
913 /* Set the key value */
914 Key = PROFILE_Find(&CurProfile->Section, SectionName,
915 KeyName, TRUE, CreateAlways);
916 DPRINT("(%S,%S,%S):\n", SectionName, KeyName, Value);
917 if (NULL == Key)
918 {
919 return FALSE;
920 }
921 if (NULL != Key->Value)
922 {
923 /* strip the leading spaces. We can safely strip \n\r and
924 * friends too, they should not happen here anyway. */
925 while (PROFILE_isspace(*Value))
926 {
927 Value++;
928 }
929
930 if (0 == wcscmp(Key->Value, Value))
931 {
932 DPRINT(" no change needed\n");
933 return TRUE; /* No change needed */
934 }
935 DPRINT(" replacing %S\n", Key->Value);
936 HeapFree(GetProcessHeap(), 0, Key->Value);
937 }
938 else
939 {
940 DPRINT(" creating key\n" );
941 }
942 Key->Value = HeapAlloc(GetProcessHeap(), 0, (wcslen(Value) + 1) * sizeof(WCHAR));
943 wcscpy(Key->Value, Value);
944 CurProfile->Changed = TRUE;
945
946 return TRUE;
947 }
948
949 /*
950 * if AllowSectionNameCopy is TRUE, allow the copying :
951 * - of Section names if 'section' is NULL
952 * - of Keys in a Section if 'entry' is NULL
953 * (see MSDN doc for GetPrivateProfileString)
954 */
955 STATIC int FASTCALL
956 PROFILE_GetPrivateProfileString(LPCWSTR Section, LPCWSTR Entry,
957 LPCWSTR DefVal, LPWSTR Buffer,
958 UINT Len, LPCWSTR Filename,
959 BOOL AllowSectionNameCopy)
960 {
961 int Ret;
962 LPWSTR pDefVal = NULL;
963
964 if (NULL == Filename)
965 {
966 Filename = L"win.ini";
967 }
968
969 DPRINT("%S,%S,%S,%p,%u,%S\n", Section, Entry, DefVal, Buffer, Len, Filename);
970
971 /* strip any trailing ' ' of DefVal. */
972 if (NULL != DefVal)
973 {
974 LPCWSTR p = &DefVal[wcslen(DefVal)]; /* even "" works ! */
975
976 while (DefVal < p)
977 {
978 p--;
979 if (' ' != (*p))
980 {
981 break;
982 }
983 }
984 if (' ' == *p) /* ouch, contained trailing ' ' */
985 {
986 int NewLen = (int)(p - DefVal);
987 pDefVal = HeapAlloc(GetProcessHeap(), 0, (NewLen + 1) * sizeof(WCHAR));
988 wcsncpy(pDefVal, DefVal, NewLen);
989 pDefVal[Len] = '\0';
990 }
991 }
992
993 if (NULL == pDefVal)
994 {
995 pDefVal = (LPWSTR) DefVal;
996 }
997
998 RtlEnterCriticalSection(&ProfileLock);
999
1000 if (PROFILE_Open(Filename))
1001 {
1002 if ((AllowSectionNameCopy) && (NULL == Section))
1003 {
1004 Ret = PROFILE_GetSectionNames(Buffer, Len);
1005 }
1006 else
1007 {
1008 /* PROFILE_GetString already handles the 'entry == NULL' case */
1009 Ret = PROFILE_GetString(Section, Entry, pDefVal, Buffer, Len);
1010 }
1011 }
1012 else
1013 {
1014 lstrcpynW(Buffer, pDefVal, Len);
1015 Ret = wcslen(Buffer);
1016 }
1017
1018 RtlLeaveCriticalSection(&ProfileLock);
1019
1020 if (pDefVal != DefVal) /* allocated */
1021 {
1022 HeapFree(GetProcessHeap(), 0, pDefVal);
1023 }
1024
1025 DPRINT("returning %S, %d\n", Buffer, Ret);
1026
1027 return Ret;
1028 }
1029
1030
1031 BOOL FASTCALL
1032 PROFILE_Init()
1033 {
1034 unsigned i;
1035
1036 RtlInitializeCriticalSection(&ProfileLock);
1037
1038 for (i = 0; i < N_CACHED_PROFILES; i++)
1039 {
1040 MRUProfile[i] = HeapAlloc(GetProcessHeap(), 0, sizeof(PROFILE));
1041 if (NULL == MRUProfile[i])
1042 {
1043 return FALSE;
1044 }
1045 MRUProfile[i]->Changed = FALSE;
1046 MRUProfile[i]->Section = NULL;
1047 MRUProfile[i]->FullName = NULL;
1048 MRUProfile[i]->LastWriteTime.dwLowDateTime = 0;
1049 MRUProfile[i]->LastWriteTime.dwHighDateTime = 0;
1050 }
1051
1052 return TRUE;
1053 }
1054
1055 /*
1056 * @unimplemented
1057 */
1058 BOOL STDCALL
1059 CloseProfileUserMapping(VOID)
1060 {
1061 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1062 return FALSE;
1063 }
1064
1065
1066 /*
1067 * @implemented
1068 */
1069 UINT STDCALL
1070 GetPrivateProfileIntW(
1071 LPCWSTR AppName,
1072 LPCWSTR KeyName,
1073 INT Default,
1074 LPCWSTR FileName
1075 )
1076 {
1077 WCHAR Buffer[30];
1078 UNICODE_STRING BufferW;
1079 INT Len;
1080 ULONG Result;
1081
1082 if (0 == (Len = GetPrivateProfileStringW(AppName, KeyName, L"",
1083 Buffer, sizeof(Buffer)/sizeof(WCHAR),
1084 FileName)))
1085 {
1086 return (UINT) Default;
1087 }
1088
1089 if (Len + 1 == sizeof(Buffer) / sizeof(WCHAR))
1090 {
1091 DPRINT1("Result may be wrong!\n");
1092 }
1093
1094 /* FIXME: if entry can be found but it's empty, then Win16 is
1095 * supposed to return 0 instead of def_val ! Difficult/problematic
1096 * to implement (every other failure also returns zero buffer),
1097 * thus wait until testing framework avail for making sure nothing
1098 * else gets broken that way. */
1099 if (L'\0' == Buffer[0])
1100 {
1101 return (UINT) Default;
1102 }
1103
1104 RtlInitUnicodeString(&BufferW, Buffer);
1105 RtlUnicodeStringToInteger(&BufferW, 10, &Result);
1106
1107 return Result;
1108 }
1109
1110
1111 /*
1112 * @implemented
1113 */
1114 UINT STDCALL
1115 GetPrivateProfileIntA(
1116 LPCSTR AppName,
1117 LPCSTR KeyName,
1118 INT Default,
1119 LPCSTR FileName
1120 )
1121 {
1122 UNICODE_STRING KeyNameW, FileNameW, AppNameW;
1123 UINT Res;
1124
1125 if (NULL != KeyName)
1126 {
1127 RtlCreateUnicodeStringFromAsciiz(&KeyNameW, (PCSZ) KeyName);
1128 }
1129 else
1130 {
1131 KeyNameW.Buffer = NULL;
1132 }
1133 if (NULL != FileName)
1134 {
1135 RtlCreateUnicodeStringFromAsciiz(&FileNameW, (PCSZ) FileName);
1136 }
1137 else
1138 {
1139 FileNameW.Buffer = NULL;
1140 }
1141 if (NULL != AppName)
1142 {
1143 RtlCreateUnicodeStringFromAsciiz(&AppNameW, (PCSZ) AppName);
1144 }
1145 else
1146 {
1147 AppNameW.Buffer = NULL;
1148 }
1149
1150 Res = GetPrivateProfileIntW(AppNameW.Buffer, KeyNameW.Buffer, Default,
1151 FileNameW.Buffer);
1152
1153 RtlFreeUnicodeString(&AppNameW);
1154 RtlFreeUnicodeString(&FileNameW);
1155 RtlFreeUnicodeString(&KeyNameW);
1156
1157 return Res;
1158 }
1159
1160
1161 /*
1162 * @unimplemented
1163 */
1164 DWORD STDCALL
1165 GetPrivateProfileSectionW (
1166 LPCWSTR lpAppName,
1167 LPWSTR lpReturnedString,
1168 DWORD nSize,
1169 LPCWSTR lpFileName
1170 )
1171 {
1172 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1173 return 0;
1174 }
1175
1176
1177 /*
1178 * @unimplemented
1179 */
1180 DWORD STDCALL
1181 GetPrivateProfileSectionA (
1182 LPCSTR lpAppName,
1183 LPSTR lpReturnedString,
1184 DWORD nSize,
1185 LPCSTR lpFileName
1186 )
1187 {
1188 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1189 return 0;
1190 }
1191
1192
1193 /***********************************************************************
1194 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1195 *
1196 * Returns the section names contained in the specified file.
1197 * The section names are returned as a list of strings with an extra
1198 * '\0' to mark the end of the list.
1199 *
1200 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1201 * '\0' and the return value is 0
1202 * - otherwise if the buffer is too small then the first section name that
1203 * does not fit is truncated so that the string list can be terminated
1204 * correctly (double '\0')
1205 * - the return value is the number of characters written in the buffer
1206 * except for the trailing '\0'. If the buffer is too small, then the
1207 * return value is len-2
1208 * - Win2000 has a bug that triggers when the section names and the
1209 * trailing '\0' fit exactly in the buffer. In that case the trailing
1210 * '\0' is missing.
1211 *
1212 * Note that when the buffer is big enough then the return value may be any
1213 * value between 1 and len-1 , including len-2.
1214 *
1215 * @implemented
1216 */
1217 DWORD STDCALL
1218 GetPrivateProfileSectionNamesW(
1219 LPWSTR Buffer,
1220 DWORD Size,
1221 LPCWSTR FileName
1222 )
1223 {
1224 DWORD Ret = 0;
1225
1226 RtlEnterCriticalSection(&ProfileLock);
1227
1228 if (PROFILE_Open(FileName))
1229 {
1230 Ret = PROFILE_GetSectionNames(Buffer, Size);
1231 }
1232
1233 RtlLeaveCriticalSection(&ProfileLock);
1234
1235 return Ret;
1236 }
1237
1238
1239 /*
1240 * @implemented
1241 */
1242 DWORD STDCALL
1243 GetPrivateProfileSectionNamesA(
1244 LPSTR Buffer,
1245 DWORD Size,
1246 LPCSTR FileName
1247 )
1248 {
1249 UNICODE_STRING FileNameW;
1250 LPWSTR BufferW;
1251 INT RetW, Ret = 0;
1252
1253 BufferW = Buffer ? HeapAlloc(GetProcessHeap(), 0, Size * sizeof(WCHAR)) : NULL;
1254 if (NULL != FileName)
1255 {
1256 RtlCreateUnicodeStringFromAsciiz(&FileNameW, (PCSZ) FileName);
1257 }
1258 else
1259 {
1260 FileNameW.Buffer = NULL;
1261 }
1262
1263 RetW = GetPrivateProfileSectionNamesW(BufferW, Size, FileNameW.Buffer);
1264 if (0 != RetW && 0 != Size)
1265 {
1266 Ret = WideCharToMultiByte(CP_ACP, 0, BufferW, RetW, Buffer, Size, NULL, NULL);
1267 if (0 == Ret)
1268 {
1269 Ret = Size;
1270 Buffer[Size - 1] = '\0';
1271 }
1272 }
1273
1274 RtlFreeUnicodeString(&FileNameW);
1275 if (NULL != BufferW)
1276 {
1277 HeapFree(GetProcessHeap(), 0, BufferW);
1278 }
1279
1280 return Ret;
1281 }
1282
1283
1284 /*
1285 * @implemented
1286 */
1287 DWORD STDCALL
1288 GetPrivateProfileStringW(
1289 LPCWSTR AppName,
1290 LPCWSTR KeyName,
1291 LPCWSTR Default,
1292 LPWSTR ReturnedString,
1293 DWORD Size,
1294 LPCWSTR FileName
1295 )
1296 {
1297 return PROFILE_GetPrivateProfileString(AppName, KeyName, Default,
1298 ReturnedString, Size, FileName, TRUE);
1299 }
1300
1301
1302 /*
1303 * @implemented
1304 */
1305 DWORD STDCALL
1306 GetPrivateProfileStringA(
1307 LPCSTR AppName,
1308 LPCSTR KeyName,
1309 LPCSTR Default,
1310 LPSTR ReturnedString,
1311 DWORD Size,
1312 LPCSTR FileName
1313 )
1314 {
1315 UNICODE_STRING AppNameW, KeyNameW, DefaultW, FileNameW;
1316 LPWSTR ReturnedStringW;
1317 INT RetW, Ret = 0;
1318
1319 ReturnedStringW = (NULL != ReturnedString
1320 ? HeapAlloc(GetProcessHeap(), 0, Size * sizeof(WCHAR)) : NULL);
1321 if (NULL != AppName)
1322 {
1323 RtlCreateUnicodeStringFromAsciiz(&AppNameW, (PCSZ) AppName);
1324 }
1325 else
1326 {
1327 AppNameW.Buffer = NULL;
1328 }
1329 if (NULL != KeyName)
1330 {
1331 RtlCreateUnicodeStringFromAsciiz(&KeyNameW, (PCSZ) KeyName);
1332 }
1333 else
1334 {
1335 KeyNameW.Buffer = NULL;
1336 }
1337 if (NULL != Default)
1338 {
1339 RtlCreateUnicodeStringFromAsciiz(&DefaultW, (PCSZ) Default);
1340 }
1341 else
1342 {
1343 DefaultW.Buffer = NULL;
1344 }
1345 if (NULL != FileName)
1346 {
1347 RtlCreateUnicodeStringFromAsciiz(&FileNameW, (PCSZ) FileName);
1348 }
1349 else
1350 {
1351 FileNameW.Buffer = NULL;
1352 }
1353
1354 RetW = GetPrivateProfileStringW(AppNameW.Buffer, KeyNameW.Buffer,
1355 DefaultW.Buffer, ReturnedStringW, Size,
1356 FileNameW.Buffer);
1357 if (0 != Size)
1358 {
1359 Ret = WideCharToMultiByte(CP_ACP, 0, ReturnedStringW, RetW + 1, ReturnedString, Size, NULL, NULL);
1360 if (0 == Ret)
1361 {
1362 Ret = Size - 1;
1363 ReturnedString[Ret] = 0;
1364 }
1365 else
1366 {
1367 Ret--; /* strip terminating 0 */
1368 }
1369 }
1370
1371 RtlFreeUnicodeString(&AppNameW);
1372 RtlFreeUnicodeString(&KeyNameW);
1373 RtlFreeUnicodeString(&DefaultW);
1374 RtlFreeUnicodeString(&FileNameW);
1375 if (NULL != ReturnedStringW)
1376 {
1377 HeapFree(GetProcessHeap(), 0, ReturnedStringW);
1378 }
1379
1380 return Ret;
1381 }
1382
1383
1384 /*
1385 * @unimplemented
1386 */
1387 BOOL STDCALL
1388 GetPrivateProfileStructW (
1389 IN LPCWSTR Section,
1390 IN LPCWSTR Key,
1391 OUT LPVOID Struct,
1392 IN UINT StructSize,
1393 IN LPCWSTR File
1394 )
1395 {
1396 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1397 return 0;
1398 }
1399
1400
1401 /*
1402 * @unimplemented
1403 */
1404 BOOL STDCALL
1405 GetPrivateProfileStructA (
1406 IN LPCSTR Section,
1407 IN LPCSTR Key,
1408 OUT LPVOID Struct,
1409 IN UINT StructSize,
1410 IN LPCSTR File
1411 )
1412 {
1413 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1414 return 0;
1415 }
1416
1417
1418 /*
1419 * @implemented
1420 */
1421 UINT STDCALL
1422 GetProfileIntW(LPCWSTR lpAppName,
1423 LPCWSTR lpKeyName,
1424 INT nDefault)
1425 {
1426 return GetPrivateProfileIntW(lpAppName,
1427 lpKeyName,
1428 nDefault,
1429 L"win.ini");
1430 }
1431
1432
1433 /*
1434 * @implemented
1435 */
1436 UINT STDCALL
1437 GetProfileIntA(LPCSTR lpAppName,
1438 LPCSTR lpKeyName,
1439 INT nDefault)
1440 {
1441 return GetPrivateProfileIntA(lpAppName,
1442 lpKeyName,
1443 nDefault,
1444 "win.ini");
1445 }
1446
1447
1448 /*
1449 * @unimplemented
1450 */
1451 DWORD STDCALL
1452 GetProfileSectionW(LPCWSTR lpAppName,
1453 LPWSTR lpReturnedString,
1454 DWORD nSize)
1455 {
1456 return GetPrivateProfileSectionW(lpAppName,
1457 lpReturnedString,
1458 nSize,
1459 NULL);
1460 }
1461
1462
1463 /*
1464 * @unimplemented
1465 */
1466 DWORD STDCALL
1467 GetProfileSectionA(LPCSTR lpAppName,
1468 LPSTR lpReturnedString,
1469 DWORD nSize)
1470 {
1471 return GetPrivateProfileSectionA(lpAppName,
1472 lpReturnedString,
1473 nSize,
1474 NULL);
1475 }
1476
1477
1478 /*
1479 * @implemented
1480 */
1481 DWORD STDCALL
1482 GetProfileStringW(LPCWSTR lpAppName,
1483 LPCWSTR lpKeyName,
1484 LPCWSTR lpDefault,
1485 LPWSTR lpReturnedString,
1486 DWORD nSize)
1487 {
1488 return GetPrivateProfileStringW(lpAppName,
1489 lpKeyName,
1490 lpDefault,
1491 lpReturnedString,
1492 nSize,
1493 NULL);
1494 }
1495
1496
1497 /*
1498 * @implemented
1499 */
1500 DWORD STDCALL
1501 GetProfileStringA(LPCSTR lpAppName,
1502 LPCSTR lpKeyName,
1503 LPCSTR lpDefault,
1504 LPSTR lpReturnedString,
1505 DWORD nSize)
1506 {
1507 return GetPrivateProfileStringA(lpAppName,
1508 lpKeyName,
1509 lpDefault,
1510 lpReturnedString,
1511 nSize,
1512 NULL);
1513 }
1514
1515
1516 /*
1517 * @unimplemented
1518 */
1519 BOOL STDCALL
1520 OpenProfileUserMapping (VOID)
1521 {
1522 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1523 return 0;
1524 }
1525
1526
1527 /*
1528 * @unimplemented
1529 */
1530 BOOL STDCALL
1531 QueryWin31IniFilesMappedToRegistry (
1532 DWORD Unknown0,
1533 DWORD Unknown1,
1534 DWORD Unknown2,
1535 DWORD Unknown3
1536 )
1537 {
1538 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1539 return FALSE;
1540 }
1541
1542
1543 /*
1544 * @unimplemented
1545 */
1546 BOOL STDCALL
1547 WritePrivateProfileSectionA (
1548 LPCSTR lpAppName,
1549 LPCSTR lpString,
1550 LPCSTR lpFileName
1551 )
1552 {
1553 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1554 return FALSE;
1555 }
1556
1557
1558 /*
1559 * @unimplemented
1560 */
1561 BOOL STDCALL
1562 WritePrivateProfileSectionW (
1563 LPCWSTR lpAppName,
1564 LPCWSTR lpString,
1565 LPCWSTR lpFileName
1566 )
1567 {
1568 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1569 return FALSE;
1570 }
1571
1572
1573 /*
1574 * @implemented
1575 */
1576 BOOL STDCALL
1577 WritePrivateProfileStringA(LPCSTR AppName,
1578 LPCSTR KeyName,
1579 LPCSTR String,
1580 LPCSTR FileName)
1581 {
1582 UNICODE_STRING AppNameW, KeyNameW, StringW, FileNameW;
1583 BOOL Ret;
1584
1585 if (NULL != AppName)
1586 {
1587 RtlCreateUnicodeStringFromAsciiz(&AppNameW, (PCSZ) AppName);
1588 }
1589 else
1590 {
1591 AppNameW.Buffer = NULL;
1592 }
1593 if (NULL != KeyName)
1594 {
1595 RtlCreateUnicodeStringFromAsciiz(&KeyNameW, (PCSZ) KeyName);
1596 }
1597 else
1598 {
1599 KeyNameW.Buffer = NULL;
1600 }
1601 if (NULL != String)
1602 {
1603 RtlCreateUnicodeStringFromAsciiz(&StringW, (PCSZ) String);
1604 }
1605 else
1606 {
1607 StringW.Buffer = NULL;
1608 }
1609 if (NULL != FileName)
1610 {
1611 RtlCreateUnicodeStringFromAsciiz(&FileNameW, (PCSZ) FileName);
1612 }
1613 else
1614 {
1615 FileNameW.Buffer = NULL;
1616 }
1617
1618 Ret = WritePrivateProfileStringW(AppNameW.Buffer, KeyNameW.Buffer,
1619 StringW.Buffer, FileNameW.Buffer);
1620
1621 RtlFreeUnicodeString(&AppNameW);
1622 RtlFreeUnicodeString(&KeyNameW);
1623 RtlFreeUnicodeString(&StringW);
1624 RtlFreeUnicodeString(&FileNameW);
1625
1626 return Ret;
1627 }
1628
1629
1630 /*
1631 * @implemented
1632 */
1633 BOOL STDCALL
1634 WritePrivateProfileStringW(LPCWSTR AppName,
1635 LPCWSTR KeyName,
1636 LPCWSTR String,
1637 LPCWSTR FileName)
1638 {
1639 BOOL Ret = FALSE;
1640
1641 RtlEnterCriticalSection(&ProfileLock);
1642
1643 if (PROFILE_Open(FileName))
1644 {
1645 if (NULL == AppName && NULL == KeyName && NULL == String) /* documented "file flush" case */
1646 {
1647 PROFILE_FlushFile();
1648 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1649 }
1650 else
1651 {
1652 if (NULL == AppName)
1653 {
1654 DPRINT1("(NULL?,%s,%s,%s)?\n", KeyName, String, FileName);
1655 }
1656 else
1657 {
1658 Ret = PROFILE_SetString(AppName, KeyName, String, FALSE);
1659 PROFILE_FlushFile();
1660 }
1661 }
1662 }
1663
1664 RtlLeaveCriticalSection(&ProfileLock);
1665
1666 return Ret;
1667 }
1668
1669
1670 /*
1671 * @unimplemented
1672 */
1673 BOOL STDCALL
1674 WritePrivateProfileStructA (
1675 IN LPCSTR Section,
1676 IN LPCSTR Key,
1677 IN LPVOID Struct,
1678 IN UINT StructSize,
1679 IN LPCSTR File
1680 )
1681 {
1682 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1683 return FALSE;
1684 }
1685
1686
1687 /*
1688 * @unimplemented
1689 */
1690 BOOL STDCALL
1691 WritePrivateProfileStructW (
1692 IN LPCWSTR Section,
1693 IN LPCWSTR Key,
1694 IN LPVOID Struct,
1695 IN UINT StructSize,
1696 IN LPCWSTR File
1697 )
1698 {
1699 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1700 return FALSE;
1701 }
1702
1703
1704 /*
1705 * @unimplemented
1706 */
1707 BOOL STDCALL
1708 WriteProfileSectionA(LPCSTR lpAppName,
1709 LPCSTR lpString)
1710 {
1711 return WritePrivateProfileSectionA(lpAppName,
1712 lpString,
1713 NULL);
1714 }
1715
1716
1717 /*
1718 * @unimplemented
1719 */
1720 BOOL STDCALL
1721 WriteProfileSectionW(LPCWSTR lpAppName,
1722 LPCWSTR lpString)
1723 {
1724 return WritePrivateProfileSectionW(lpAppName,
1725 lpString,
1726 NULL);
1727 }
1728
1729
1730 /*
1731 * @implemented
1732 */
1733 BOOL STDCALL
1734 WriteProfileStringA(LPCSTR AppName,
1735 LPCSTR KeyName,
1736 LPCSTR String)
1737 {
1738 return WritePrivateProfileStringA(AppName,
1739 KeyName,
1740 String,
1741 NULL);
1742 }
1743
1744
1745 /*
1746 * @implemented
1747 */
1748 BOOL STDCALL
1749 WriteProfileStringW(LPCWSTR AppName,
1750 LPCWSTR KeyName,
1751 LPCWSTR String)
1752 {
1753 return WritePrivateProfileStringW(AppName,
1754 KeyName,
1755 String,
1756 NULL);
1757 }
1758
1759 /* EOF */