1 /* $Id: profile.c,v 1.13 2004/10/09 18:46:41 gvg Exp $
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
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) ]
21 static CRITICAL_SECTION ProfileLock
;
23 typedef struct tagPROFILEKEY
26 struct tagPROFILEKEY
*Next
;
30 typedef struct tagPROFILESECTION
32 struct tagPROFILEKEY
*Key
;
33 struct tagPROFILESECTION
*Next
;
41 PROFILESECTION
*Section
;
43 FILETIME LastWriteTime
;
47 #define N_CACHED_PROFILES 10
49 /* Cached profile files */
50 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
52 #define CurProfile (MRUProfile[0])
54 #define PROFILE_MAX_LINE_LEN 1024
56 /* Check for comments in profile */
57 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
59 /* FUNCTIONS *****************************************************************/
61 /***********************************************************************
64 * Copy the content of an entry into a buffer, removing quotes, and possibly
65 * translating environment variables.
68 PROFILE_CopyEntry(LPWSTR Buffer
, LPCWSTR Value
, int Len
, BOOL StripQuote
)
77 if (StripQuote
&& (L
'\'' == *Value
|| L
'\"' == *Value
))
79 if (L
'\0' != Value
[1] && (Value
[wcslen(Value
) - 1] == *Value
))
85 lstrcpynW(Buffer
, Value
, Len
);
86 if (L
'\0' != Quote
&& wcslen(Value
) <= Len
)
88 Buffer
[wcslen(Buffer
) - 1] = L
'\0';
93 /***********************************************************************
96 * Save a profile tree to a file.
99 PROFILE_Save(HANDLE File
, PROFILESECTION
*Section
)
102 char Buffer
[PROFILE_MAX_LINE_LEN
];
105 for ( ; NULL
!= Section
; Section
= Section
->Next
)
107 if (L
'\0' != Section
->Name
[0])
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
);
114 for (Key
= Section
->Key
; NULL
!= Key
; Key
= Key
->Next
)
116 WideCharToMultiByte(CP_ACP
, 0, Key
->Name
, -1, Buffer
, sizeof(Buffer
) - 3, NULL
, NULL
);
120 WideCharToMultiByte(CP_ACP
, 0, Key
->Value
, -1, Buffer
+ strlen(Buffer
),
121 sizeof(Buffer
) - strlen(Buffer
) - 2, NULL
, NULL
);
123 strcat(Buffer
, "\r\n");
124 WriteFile(File
, Buffer
, strlen(Buffer
), &BytesWritten
, NULL
);
130 /***********************************************************************
133 * Free a profile tree.
136 PROFILE_Free(PROFILESECTION
*Section
)
138 PROFILESECTION
*NextSection
;
139 PROFILEKEY
*Key
, *NextKey
;
141 for ( ; NULL
!= Section
; Section
= NextSection
)
143 for (Key
= Section
->Key
; NULL
!= Key
; Key
= NextKey
)
146 if (NULL
!= Key
->Value
)
148 HeapFree(GetProcessHeap(), 0, Key
->Value
);
150 HeapFree(GetProcessHeap(), 0, Key
);
152 NextSection
= Section
->Next
;
153 HeapFree(GetProcessHeap(), 0, Section
);
158 PROFILE_isspace(char c
)
164 if ('\r' == c
|| 0x1a == c
)
166 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
173 /***********************************************************************
176 * Load a profile tree from a file.
178 static PROFILESECTION
* FASTCALL
179 PROFILE_Load(HANDLE File
)
183 PROFILESECTION
*Section
, *FirstSection
;
184 PROFILESECTION
**NextSection
;
185 PROFILEKEY
*Key
, *PrevKey
, **NextKey
;
186 LARGE_INTEGER FileSize
;
189 char *Input
, *End
, *EndLine
, *LastNonSpace
;
191 FileSize
.u
.LowPart
= GetFileSize(File
, &FileSize
.u
.HighPart
);
192 if (INVALID_FILE_SIZE
== FileSize
.u
.LowPart
&& 0 != GetLastError())
197 FirstSection
= HeapAlloc(GetProcessHeap(), 0, sizeof(*Section
));
198 if (NULL
== FirstSection
)
202 FirstSection
->Name
[0] = L
'\0';
203 FirstSection
->Key
= NULL
;
204 FirstSection
->Next
= NULL
;
205 NextSection
= &FirstSection
->Next
;
206 NextKey
= &FirstSection
->Key
;
209 if (0 == FileSize
.QuadPart
)
213 Mapping
= CreateFileMappingW(File
, NULL
, PAGE_READONLY
| SEC_COMMIT
, 0, 0, NULL
);
216 HeapFree(GetProcessHeap(), 0, FirstSection
);
219 BaseAddress
= MapViewOfFile(Mapping
, FILE_MAP_READ
, 0, 0, 0);
220 if (NULL
== BaseAddress
)
222 CloseHandle(Mapping
);
223 HeapFree(GetProcessHeap(), 0, FirstSection
);
227 Input
= (char *) BaseAddress
;
228 End
= Input
+ FileSize
.QuadPart
;
233 while (EndLine
< End
&& '\n' != *EndLine
&& ';' != *EndLine
)
235 if (! PROFILE_isspace(*EndLine
))
237 LastNonSpace
= EndLine
;
241 if (NULL
!= LastNonSpace
)
243 EndLine
= LastNonSpace
+ 1;
245 while (Input
< EndLine
&& PROFILE_isspace(*Input
))
253 if ('[' == *Input
) /* section start */
256 while (p
< EndLine
&& ']' != *p
)
260 if (p
< EndLine
&& ']' == *p
)
263 if (NULL
== (Section
= HeapAlloc(GetProcessHeap(), 0,
264 sizeof(*Section
) + Len
* sizeof(WCHAR
))))
268 MultiByteToWideChar(CP_ACP
, 0, Input
, Len
, Section
->Name
, Len
);
269 Section
->Name
[Len
] = L
'\0';
271 Section
->Next
= NULL
;
272 *NextSection
= Section
;
273 NextSection
= &Section
->Next
;
274 NextKey
= &Section
->Key
;
277 DPRINT("New section: %S\n", Section
->Name
);
281 while (Input
< End
&& '\n' != *Input
)
285 Input
++; /* Skip past the \n */
291 while (p
< EndLine
&& '=' != *p
)
293 if (! PROFILE_isspace(*p
))
302 Len
= p2
- Input
+ 1;
303 if (NULL
== (Key
= HeapAlloc(GetProcessHeap(), 0,
304 sizeof(*Key
) + Len
* sizeof(WCHAR
))))
308 MultiByteToWideChar(CP_ACP
, 0, Input
, Len
, Key
->Name
, Len
);
309 Key
->Name
[Len
] = L
'\0';
311 while (Input
< EndLine
&& PROFILE_isspace(*Input
))
315 if (EndLine
<= Input
)
325 if (! PROFILE_isspace(*p
))
331 Len
= p2
- Input
+ 1;
332 if (NULL
== (Key
->Value
= HeapAlloc(GetProcessHeap(), 0,
333 (Len
+ 1) * sizeof(WCHAR
))))
337 MultiByteToWideChar(CP_ACP
, 0, Input
, Len
, Key
->Value
, Len
);
338 Key
->Value
[Len
] = L
'\0';
343 NextKey
= &Key
->Next
;
346 DPRINT("New key: name=%S, value=%S\n",
347 Key
->Name
, NULL
!= Key
->Value
? Key
->Value
: L
"(none)");
351 while (Input
< End
&& '\n' != *Input
)
355 Input
++; /* Skip past the \n */
358 UnmapViewOfFile(BaseAddress
);
359 CloseHandle(Mapping
);
365 /***********************************************************************
368 * Find a key in a profile tree, optionally creating it.
370 static PROFILEKEY
* FASTCALL
371 PROFILE_Find(PROFILESECTION
**Section
, LPCWSTR SectionName
,
372 LPCWSTR KeyName
, BOOL Create
, BOOL CreateAlways
)
377 while (PROFILE_isspace(*SectionName
))
381 p
= SectionName
+ wcslen(SectionName
) - 1;
382 while (SectionName
< p
&& PROFILE_isspace(*p
))
386 SecLen
= p
- SectionName
+ 1;
388 while (PROFILE_isspace(*KeyName
))
392 p
= KeyName
+ wcslen(KeyName
) - 1;
393 while (KeyName
< p
&& PROFILE_isspace(*p
))
397 KeyLen
= p
- KeyName
+ 1;
399 while (NULL
!= *Section
)
401 if (L
'\0' != ((*Section
)->Name
[0])
402 && 0 == _wcsnicmp((*Section
)->Name
, SectionName
, SecLen
)
403 && L
'\0' == ((*Section
)->Name
)[SecLen
])
405 PROFILEKEY
**Key
= &(*Section
)->Key
;
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
416 if (0 == _wcsnicmp((*Key
)->Name
, KeyName
, KeyLen
)
417 && L
'\0' == ((*Key
)->Name
)[KeyLen
])
428 if (NULL
== (*Key
= HeapAlloc(GetProcessHeap(), 0,
429 sizeof(PROFILEKEY
) + wcslen(KeyName
) * sizeof(WCHAR
))))
433 wcscpy((*Key
)->Name
, KeyName
);
434 (*Key
)->Value
= NULL
;
438 Section
= &(*Section
)->Next
;
446 *Section
= HeapAlloc(GetProcessHeap(), 0,
447 sizeof(PROFILESECTION
) + wcslen(SectionName
) * sizeof(WCHAR
));
448 if (NULL
== *Section
)
452 wcscpy((*Section
)->Name
, SectionName
);
453 (*Section
)->Next
= NULL
;
454 if (NULL
== ((*Section
)->Key
= HeapAlloc(GetProcessHeap(), 0,
455 sizeof(PROFILEKEY
) + wcslen(KeyName
) * sizeof(WCHAR
))))
457 HeapFree(GetProcessHeap(), 0, *Section
);
460 wcscpy((*Section
)->Key
->Name
, KeyName
);
461 (*Section
)->Key
->Value
= NULL
;
462 (*Section
)->Key
->Next
= NULL
;
464 return (*Section
)->Key
;
468 /***********************************************************************
471 * Flush the current profile to disk if changed.
474 PROFILE_FlushFile(void)
476 HANDLE File
= INVALID_HANDLE_VALUE
;
477 FILETIME LastWriteTime
;
479 if (NULL
== CurProfile
)
481 DPRINT("No current profile!\n");
485 if (! CurProfile
->Changed
|| NULL
== CurProfile
->FullName
)
490 File
= CreateFileW(CurProfile
->FullName
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
491 if (INVALID_HANDLE_VALUE
== File
)
493 DPRINT1("could not save profile file %s\n", CurProfile
->FullName
);
497 DPRINT("Saving %S\n", CurProfile
->FullName
);
498 PROFILE_Save(File
, CurProfile
->Section
);
499 if (GetFileTime(File
, NULL
, NULL
, &LastWriteTime
))
501 CurProfile
->LastWriteTime
.dwLowDateTime
= LastWriteTime
.dwLowDateTime
;
502 CurProfile
->LastWriteTime
.dwHighDateTime
= LastWriteTime
.dwHighDateTime
;
504 CurProfile
->Changed
= FALSE
;
511 /***********************************************************************
512 * PROFILE_ReleaseFile
514 * Flush the current profile to disk and remove it from the cache.
517 PROFILE_ReleaseFile(void)
520 PROFILE_Free(CurProfile
->Section
);
521 if (NULL
!= CurProfile
->FullName
)
523 HeapFree(GetProcessHeap(), 0, CurProfile
->FullName
);
525 CurProfile
->Changed
= FALSE
;
526 CurProfile
->Section
= NULL
;
527 CurProfile
->FullName
= NULL
;
528 CurProfile
->LastWriteTime
.dwLowDateTime
= 0;
529 CurProfile
->LastWriteTime
.dwHighDateTime
= 0;
533 /***********************************************************************
536 * Open a profile file, checking the cached file first.
539 PROFILE_Open(LPCWSTR FileName
)
541 WCHAR FullName
[MAX_PATH
];
542 HANDLE File
= INVALID_HANDLE_VALUE
;
544 PROFILE
*TempProfile
;
546 FILETIME LastWriteTime
;
548 /* Build full path name */
550 if (wcschr(FileName
, L
'/') || wcschr(FileName
, L
'\\') ||
551 wcschr(FileName
, L
':'))
553 FullLen
= GetFullPathNameW(FileName
, MAX_PATH
, FullName
, NULL
);
554 if (0 == FullLen
|| MAX_PATH
< FullLen
)
561 FullLen
= GetWindowsDirectoryW(FullName
, MAX_PATH
);
562 if (0 == FullLen
|| MAX_PATH
< FullLen
+ 1 + wcslen(FileName
))
566 wcscat(FullName
, L
"\\");
567 wcscat(FullName
, FileName
);
569 #if 0 /* FIXME: Not yet implemented */
570 FullLen
= GetLongPathNameW(FullName
, FullName
, MAX_PATH
);
571 if (0 == FullLen
|| MAX_PATH
< FullLen
)
577 /* Check for a match */
579 for (i
= 0; i
< N_CACHED_PROFILES
; i
++)
581 if (NULL
!= MRUProfile
[i
]->FullName
&& 0 == wcscmp(FullName
, MRUProfile
[i
]->FullName
))
586 TempProfile
= MRUProfile
[i
];
587 for(j
= i
; j
> 0; j
--)
589 MRUProfile
[j
] = MRUProfile
[j
- 1];
591 CurProfile
= TempProfile
;
593 File
= CreateFileW(FullName
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
594 NULL
, OPEN_EXISTING
, 0, NULL
);
595 if (INVALID_HANDLE_VALUE
== File
)
599 if (GetFileTime(File
, NULL
, NULL
, &LastWriteTime
) &&
600 LastWriteTime
.dwLowDateTime
== CurProfile
->LastWriteTime
.dwLowDateTime
&&
601 LastWriteTime
.dwHighDateTime
== CurProfile
->LastWriteTime
.dwHighDateTime
)
603 DPRINT("(%S): already opened (mru = %d)\n", FileName
, i
);
607 DPRINT("(%S): already opened, needs refreshing (mru = %d)\n", FileName
, i
);
615 /* Flush the old current profile */
618 /* Make the oldest profile the current one only in order to get rid of it */
619 if (N_CACHED_PROFILES
== i
)
621 TempProfile
= MRUProfile
[N_CACHED_PROFILES
- 1];
622 for(i
= N_CACHED_PROFILES
- 1; i
> 0; i
--)
624 MRUProfile
[i
] = MRUProfile
[i
- 1];
626 CurProfile
= TempProfile
;
628 if (NULL
!= CurProfile
->FullName
)
630 PROFILE_ReleaseFile();
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
)
637 wcscpy(CurProfile
->FullName
, FullName
);
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
)
644 CurProfile
->Section
= PROFILE_Load(File
);
645 if (GetFileTime(File
, NULL
, NULL
, &LastWriteTime
))
647 CurProfile
->LastWriteTime
.dwLowDateTime
= LastWriteTime
.dwLowDateTime
;
648 CurProfile
->LastWriteTime
.dwHighDateTime
= LastWriteTime
.dwHighDateTime
;
654 /* Does not exist yet, we will create it in PROFILE_FlushFile */
655 DPRINT("profile file %S not found\n", FileName
);
662 /***********************************************************************
665 * Returns all keys of a section.
666 * If ReturnValues is TRUE, also include the corresponding values.
669 PROFILE_GetSection(PROFILESECTION
*Section
, LPCWSTR SectionName
,
670 LPWSTR Buffer
, UINT Len
, BOOL ReturnValues
)
679 DPRINT("%S,%p,%u\n", SectionName
, Buffer
, Len
);
681 while (NULL
!= Section
)
683 if (L
'\0' != Section
->Name
[0] && 0 == _wcsicmp(Section
->Name
, SectionName
))
686 for (Key
= Section
->Key
; NULL
!= Key
; Key
= Key
->Next
)
692 if (L
'\0' == *Key
->Name
)
694 continue; /* Skip empty lines */
696 if (IS_ENTRY_COMMENT(Key
->Name
))
698 continue; /* Skip comments */
700 PROFILE_CopyEntry(Buffer
, Key
->Name
, Len
- 1, FALSE
);
701 Len
-= wcslen(Buffer
) + 1;
702 Buffer
+= wcslen(Buffer
) + 1;
707 if (ReturnValues
&& NULL
!= Key
->Value
)
710 PROFILE_CopyEntry(Buffer
, Key
->Value
, Len
- 1, 0);
711 Len
-= wcslen(Buffer
) + 1;
712 Buffer
+= wcslen(Buffer
) + 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
728 Section
= Section
->Next
;
731 Buffer
[0] = Buffer
[1] = L
'\0';
736 /* See GetPrivateProfileSectionNamesW for documentation */
738 PROFILE_GetSectionNames(LPWSTR Buffer
, UINT Len
)
742 PROFILESECTION
*Section
;
744 if (NULL
== Buffer
|| 0 == Len
)
757 Section
= CurProfile
->Section
;
758 while (NULL
!= Section
)
760 if (L
'\0' != Section
->Name
[0])
762 l
= wcslen(Section
->Name
) + 1;
767 wcsncpy(Buf
, Section
->Name
, f
- 1);
774 wcscpy(Buf
, Section
->Name
);
778 Section
= Section
->Next
;
786 /***********************************************************************
789 * Get a profile string.
792 PROFILE_GetString(LPCWSTR Section
, LPCWSTR KeyName
,
793 LPCWSTR DefVal
, LPWSTR Buffer
, UINT Len
)
795 PROFILEKEY
*Key
= NULL
;
809 if (L
'\0' == KeyName
[0])
813 Key
= PROFILE_Find(&CurProfile
->Section
, Section
, KeyName
, FALSE
, FALSE
);
814 PROFILE_CopyEntry(Buffer
, (NULL
!= Key
&& NULL
!= Key
->Value
) ? Key
->Value
: DefVal
,
816 DPRINT("(%S,%S,%S): returning %S\n", Section
, KeyName
, DefVal
, Buffer
);
817 return wcslen(Buffer
);
820 if (NULL
!= Section
&& L
'\0' != Section
[0])
822 INT Ret
= PROFILE_GetSection(CurProfile
->Section
, Section
, Buffer
, Len
, FALSE
);
823 if (L
'\0' == Buffer
[0]) /* no luck -> def_val */
825 PROFILE_CopyEntry(Buffer
, DefVal
, Len
, TRUE
);
826 Ret
= wcslen(Buffer
);
837 /***********************************************************************
838 * PROFILE_DeleteSection
840 * Delete a section from a profile tree.
843 PROFILE_DeleteSection(PROFILESECTION
**Section
, LPCWSTR Name
)
845 while (NULL
!= *Section
)
847 if (L
'\0' != (*Section
)->Name
[0] && 0 == _wcsicmp((*Section
)->Name
, Name
))
849 PROFILESECTION
*ToDel
= *Section
;
850 *Section
= ToDel
->Next
;
855 Section
= &(*Section
)->Next
;
862 /***********************************************************************
865 * Delete a key from a profile tree.
868 PROFILE_DeleteKey(PROFILESECTION
**Section
,
869 LPCWSTR SectionName
, LPCWSTR KeyName
)
873 if (L
'\0' != (*Section
)->Name
[0] && 0 == _wcsicmp((*Section
)->Name
, SectionName
))
875 PROFILEKEY
**Key
= &(*Section
)->Key
;
878 if (0 == _wcsicmp((*Key
)->Name
, KeyName
))
880 PROFILEKEY
*ToDel
= *Key
;
882 if (NULL
!= ToDel
->Value
)
884 HeapFree(GetProcessHeap(), 0, ToDel
->Value
);
886 HeapFree(GetProcessHeap(), 0, ToDel
);
892 Section
= &(*Section
)->Next
;
899 /***********************************************************************
902 * Set a profile string.
905 PROFILE_SetString(LPCWSTR SectionName
, LPCWSTR KeyName
,
906 LPCWSTR Value
, BOOL CreateAlways
)
910 if (NULL
== KeyName
) /* Delete a whole section */
912 DPRINT("(%S)\n", SectionName
);
913 CurProfile
->Changed
|= PROFILE_DeleteSection(&CurProfile
->Section
,
915 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
916 this is not an error on application's level.*/
919 if (NULL
== Value
) /* Delete a key */
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 */
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
);
935 if (NULL
!= Key
->Value
)
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
))
944 if (0 == wcscmp(Key
->Value
, Value
))
946 DPRINT(" no change needed\n");
947 return TRUE
; /* No change needed */
949 DPRINT(" replacing %S\n", Key
->Value
);
950 HeapFree(GetProcessHeap(), 0, Key
->Value
);
954 DPRINT(" creating key\n" );
956 Key
->Value
= HeapAlloc(GetProcessHeap(), 0, (wcslen(Value
) + 1) * sizeof(WCHAR
));
957 wcscpy(Key
->Value
, Value
);
958 CurProfile
->Changed
= TRUE
;
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)
970 PROFILE_GetPrivateProfileString(LPCWSTR Section
, LPCWSTR Entry
,
971 LPCWSTR DefVal
, LPWSTR Buffer
,
972 UINT Len
, LPCWSTR Filename
,
973 BOOL AllowSectionNameCopy
)
976 LPWSTR pDefVal
= NULL
;
978 if (NULL
== Filename
)
980 Filename
= L
"win.ini";
983 DPRINT("%S,%S,%S,%p,%u,%S\n", Section
, Entry
, DefVal
, Buffer
, Len
, Filename
);
985 /* strip any trailing ' ' of DefVal. */
988 LPCWSTR p
= &DefVal
[wcslen(DefVal
)]; /* even "" works ! */
998 if (' ' == *p
) /* ouch, contained trailing ' ' */
1000 int NewLen
= (int)(p
- DefVal
);
1001 pDefVal
= HeapAlloc(GetProcessHeap(), 0, (NewLen
+ 1) * sizeof(WCHAR
));
1002 wcsncpy(pDefVal
, DefVal
, NewLen
);
1003 pDefVal
[Len
] = '\0';
1007 if (NULL
== pDefVal
)
1009 pDefVal
= (LPWSTR
) DefVal
;
1012 RtlEnterCriticalSection(&ProfileLock
);
1014 if (PROFILE_Open(Filename
))
1016 if ((AllowSectionNameCopy
) && (NULL
== Section
))
1018 Ret
= PROFILE_GetSectionNames(Buffer
, Len
);
1022 /* PROFILE_GetString already handles the 'entry == NULL' case */
1023 Ret
= PROFILE_GetString(Section
, Entry
, pDefVal
, Buffer
, Len
);
1028 lstrcpynW(Buffer
, pDefVal
, Len
);
1029 Ret
= wcslen(Buffer
);
1032 RtlLeaveCriticalSection(&ProfileLock
);
1034 if (pDefVal
!= DefVal
) /* allocated */
1036 HeapFree(GetProcessHeap(), 0, pDefVal
);
1039 DPRINT("returning %S, %d\n", Buffer
, Ret
);
1050 RtlInitializeCriticalSection(&ProfileLock
);
1052 for (i
= 0; i
< N_CACHED_PROFILES
; i
++)
1054 MRUProfile
[i
] = HeapAlloc(GetProcessHeap(), 0, sizeof(PROFILE
));
1055 if (NULL
== MRUProfile
[i
])
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;
1073 CloseProfileUserMapping(VOID
)
1075 DPRINT1("CloseProfileUserMapping not implemented\n");
1076 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1085 GetPrivateProfileIntW(
1093 UNICODE_STRING BufferW
;
1097 if (0 == (Len
= GetPrivateProfileStringW(AppName
, KeyName
, L
"",
1098 Buffer
, sizeof(Buffer
)/sizeof(WCHAR
),
1101 return (UINT
) Default
;
1104 if (Len
+ 1 == sizeof(Buffer
) / sizeof(WCHAR
))
1106 DPRINT1("Result may be wrong!\n");
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])
1116 return (UINT
) Default
;
1119 RtlInitUnicodeString(&BufferW
, Buffer
);
1120 RtlUnicodeStringToInteger(&BufferW
, 10, &Result
);
1130 GetPrivateProfileIntA(
1137 UNICODE_STRING KeyNameW
, FileNameW
, AppNameW
;
1140 if (NULL
!= KeyName
)
1142 RtlCreateUnicodeStringFromAsciiz(&KeyNameW
, (PCSZ
) KeyName
);
1146 KeyNameW
.Buffer
= NULL
;
1148 if (NULL
!= FileName
)
1150 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, (PCSZ
) FileName
);
1154 FileNameW
.Buffer
= NULL
;
1156 if (NULL
!= AppName
)
1158 RtlCreateUnicodeStringFromAsciiz(&AppNameW
, (PCSZ
) AppName
);
1162 AppNameW
.Buffer
= NULL
;
1165 Res
= GetPrivateProfileIntW(AppNameW
.Buffer
, KeyNameW
.Buffer
, Default
,
1168 RtlFreeUnicodeString(&AppNameW
);
1169 RtlFreeUnicodeString(&FileNameW
);
1170 RtlFreeUnicodeString(&KeyNameW
);
1180 GetPrivateProfileSectionW (
1189 DPRINT("(%S, %p, %ld, %S)\n", Section
, Buffer
, Len
, FileName
);
1191 RtlEnterCriticalSection(&ProfileLock
);
1193 if (PROFILE_Open(FileName
))
1195 Ret
= PROFILE_GetSection(CurProfile
->Section
, Section
, Buffer
, Len
, TRUE
);
1198 RtlLeaveCriticalSection(&ProfileLock
);
1208 GetPrivateProfileSectionA (
1215 UNICODE_STRING SectionW
, FileNameW
;
1219 BufferW
= NULL
!= Buffer
? HeapAlloc(GetProcessHeap(), 0, Len
* sizeof(WCHAR
)) : NULL
;
1220 if (NULL
!= Section
)
1222 RtlCreateUnicodeStringFromAsciiz(&SectionW
, Section
);
1226 SectionW
.Buffer
= NULL
;
1228 if (NULL
!= FileName
)
1230 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, FileName
);
1234 FileNameW
.Buffer
= NULL
;
1237 RetW
= GetPrivateProfileSectionW(SectionW
.Buffer
, BufferW
, Len
, FileNameW
.Buffer
);
1241 Ret
= WideCharToMultiByte(CP_ACP
, 0, BufferW
, RetW
+ 2, Buffer
, Len
, NULL
, NULL
);
1249 Buffer
[Len
-2] = '\0';
1250 Buffer
[Len
-1] = '\0';
1259 RtlFreeUnicodeString(&SectionW
);
1260 RtlFreeUnicodeString(&FileNameW
);
1261 if (NULL
!= BufferW
)
1263 HeapFree(GetProcessHeap(), 0, BufferW
);
1270 /***********************************************************************
1271 * GetPrivateProfileSectionNamesW (KERNEL32.@)
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.
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
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.
1295 GetPrivateProfileSectionNamesW(
1303 RtlEnterCriticalSection(&ProfileLock
);
1305 if (PROFILE_Open(FileName
))
1307 Ret
= PROFILE_GetSectionNames(Buffer
, Size
);
1310 RtlLeaveCriticalSection(&ProfileLock
);
1320 GetPrivateProfileSectionNamesA(
1326 UNICODE_STRING FileNameW
;
1330 BufferW
= Buffer
? HeapAlloc(GetProcessHeap(), 0, Size
* sizeof(WCHAR
)) : NULL
;
1331 if (NULL
!= FileName
)
1333 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, (PCSZ
) FileName
);
1337 FileNameW
.Buffer
= NULL
;
1340 RetW
= GetPrivateProfileSectionNamesW(BufferW
, Size
, FileNameW
.Buffer
);
1341 if (0 != RetW
&& 0 != Size
)
1343 Ret
= WideCharToMultiByte(CP_ACP
, 0, BufferW
, RetW
, Buffer
, Size
, NULL
, NULL
);
1347 Buffer
[Size
- 1] = '\0';
1351 RtlFreeUnicodeString(&FileNameW
);
1352 if (NULL
!= BufferW
)
1354 HeapFree(GetProcessHeap(), 0, BufferW
);
1365 GetPrivateProfileStringW(
1369 LPWSTR ReturnedString
,
1374 return PROFILE_GetPrivateProfileString(AppName
, KeyName
, Default
,
1375 ReturnedString
, Size
, FileName
, TRUE
);
1383 GetPrivateProfileStringA(
1387 LPSTR ReturnedString
,
1392 UNICODE_STRING AppNameW
, KeyNameW
, DefaultW
, FileNameW
;
1393 LPWSTR ReturnedStringW
;
1396 ReturnedStringW
= (NULL
!= ReturnedString
1397 ? HeapAlloc(GetProcessHeap(), 0, Size
* sizeof(WCHAR
)) : NULL
);
1398 if (NULL
!= AppName
)
1400 RtlCreateUnicodeStringFromAsciiz(&AppNameW
, (PCSZ
) AppName
);
1404 AppNameW
.Buffer
= NULL
;
1406 if (NULL
!= KeyName
)
1408 RtlCreateUnicodeStringFromAsciiz(&KeyNameW
, (PCSZ
) KeyName
);
1412 KeyNameW
.Buffer
= NULL
;
1414 if (NULL
!= Default
)
1416 RtlCreateUnicodeStringFromAsciiz(&DefaultW
, (PCSZ
) Default
);
1420 DefaultW
.Buffer
= NULL
;
1422 if (NULL
!= FileName
)
1424 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, (PCSZ
) FileName
);
1428 FileNameW
.Buffer
= NULL
;
1431 RetW
= GetPrivateProfileStringW(AppNameW
.Buffer
, KeyNameW
.Buffer
,
1432 DefaultW
.Buffer
, ReturnedStringW
, Size
,
1436 Ret
= WideCharToMultiByte(CP_ACP
, 0, ReturnedStringW
, RetW
+ 1, ReturnedString
, Size
, NULL
, NULL
);
1440 ReturnedString
[Ret
] = 0;
1444 Ret
--; /* strip terminating 0 */
1448 RtlFreeUnicodeString(&AppNameW
);
1449 RtlFreeUnicodeString(&KeyNameW
);
1450 RtlFreeUnicodeString(&DefaultW
);
1451 RtlFreeUnicodeString(&FileNameW
);
1452 if (NULL
!= ReturnedStringW
)
1454 HeapFree(GetProcessHeap(), 0, ReturnedStringW
);
1465 GetPrivateProfileStructW (
1473 DPRINT1("GetPrivateProfileStructW not implemented\n");
1474 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1483 GetPrivateProfileStructA (
1491 DPRINT1("GetPrivateProfileStructA not implemented\n");
1492 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1501 GetProfileIntW(LPCWSTR lpAppName
,
1505 return GetPrivateProfileIntW(lpAppName
,
1516 GetProfileIntA(LPCSTR lpAppName
,
1520 return GetPrivateProfileIntA(lpAppName
,
1531 GetProfileSectionW(LPCWSTR lpAppName
,
1532 LPWSTR lpReturnedString
,
1535 return GetPrivateProfileSectionW(lpAppName
,
1546 GetProfileSectionA(LPCSTR lpAppName
,
1547 LPSTR lpReturnedString
,
1550 return GetPrivateProfileSectionA(lpAppName
,
1561 GetProfileStringW(LPCWSTR lpAppName
,
1564 LPWSTR lpReturnedString
,
1567 return GetPrivateProfileStringW(lpAppName
,
1580 GetProfileStringA(LPCSTR lpAppName
,
1583 LPSTR lpReturnedString
,
1586 return GetPrivateProfileStringA(lpAppName
,
1599 OpenProfileUserMapping (VOID
)
1601 DPRINT1("OpenProfileUserMapping not implemented\n");
1602 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1611 QueryWin31IniFilesMappedToRegistry (
1618 DPRINT1("QueryWin31IniFilesMappedToRegistry not implemented\n");
1619 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1628 WritePrivateProfileSectionA (
1634 DPRINT1("WritePrivateProfileSectionA not implemented\n");
1635 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1644 WritePrivateProfileSectionW (
1650 DPRINT1("WritePrivateProfileSectionW not implemented\n");
1651 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1660 WritePrivateProfileStringA(LPCSTR AppName
,
1665 UNICODE_STRING AppNameW
, KeyNameW
, StringW
, FileNameW
;
1668 if (NULL
!= AppName
)
1670 RtlCreateUnicodeStringFromAsciiz(&AppNameW
, (PCSZ
) AppName
);
1674 AppNameW
.Buffer
= NULL
;
1676 if (NULL
!= KeyName
)
1678 RtlCreateUnicodeStringFromAsciiz(&KeyNameW
, (PCSZ
) KeyName
);
1682 KeyNameW
.Buffer
= NULL
;
1686 RtlCreateUnicodeStringFromAsciiz(&StringW
, (PCSZ
) String
);
1690 StringW
.Buffer
= NULL
;
1692 if (NULL
!= FileName
)
1694 RtlCreateUnicodeStringFromAsciiz(&FileNameW
, (PCSZ
) FileName
);
1698 FileNameW
.Buffer
= NULL
;
1701 Ret
= WritePrivateProfileStringW(AppNameW
.Buffer
, KeyNameW
.Buffer
,
1702 StringW
.Buffer
, FileNameW
.Buffer
);
1704 RtlFreeUnicodeString(&AppNameW
);
1705 RtlFreeUnicodeString(&KeyNameW
);
1706 RtlFreeUnicodeString(&StringW
);
1707 RtlFreeUnicodeString(&FileNameW
);
1717 WritePrivateProfileStringW(LPCWSTR AppName
,
1724 RtlEnterCriticalSection(&ProfileLock
);
1726 if (PROFILE_Open(FileName
))
1728 if (NULL
== AppName
&& NULL
== KeyName
&& NULL
== String
) /* documented "file flush" case */
1730 PROFILE_FlushFile();
1731 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1735 if (NULL
== AppName
)
1737 DPRINT1("(NULL?,%s,%s,%s)?\n", KeyName
, String
, FileName
);
1741 Ret
= PROFILE_SetString(AppName
, KeyName
, String
, FALSE
);
1742 PROFILE_FlushFile();
1747 RtlLeaveCriticalSection(&ProfileLock
);
1757 WritePrivateProfileStructA (
1765 DPRINT1("WritePrivateProfileStructA not implemented\n");
1766 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1775 WritePrivateProfileStructW (
1783 DPRINT1("WritePrivateProfileStructW not implemented\n");
1784 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1793 WriteProfileSectionA(LPCSTR lpAppName
,
1796 return WritePrivateProfileSectionA(lpAppName
,
1806 WriteProfileSectionW(LPCWSTR lpAppName
,
1809 return WritePrivateProfileSectionW(lpAppName
,
1819 WriteProfileStringA(LPCSTR AppName
,
1823 return WritePrivateProfileStringA(AppName
,
1834 WriteProfileStringW(LPCWSTR AppName
,
1838 return WritePrivateProfileStringW(AppName
,