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