4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(profile
);
36 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
46 typedef struct tagPROFILEKEY
49 struct tagPROFILEKEY
*next
;
53 typedef struct tagPROFILESECTION
55 struct tagPROFILEKEY
*key
;
56 struct tagPROFILESECTION
*next
;
64 PROFILESECTION
*section
;
66 FILETIME LastWriteTime
;
71 #define N_CACHED_PROFILES 10
73 /* Cached profile files */
74 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
76 #define CurProfile (MRUProfile[0])
78 /* Check for comments in profile */
79 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
81 static CRITICAL_SECTION PROFILE_CritSect
;
82 static CRITICAL_SECTION_DEBUG critsect_debug
=
84 0, 0, &PROFILE_CritSect
,
85 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
86 0, 0, { (DWORD_PTR
)(__FILE__
": PROFILE_CritSect") }
88 static CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
90 static const char hex
[16] = "0123456789ABCDEF";
92 /***********************************************************************
95 * Copy the content of an entry into a buffer, removing quotes, and possibly
96 * translating environment variables.
98 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
)
104 if (*value
== '\'' || *value
== '\"')
106 if (value
[1] && (value
[lstrlenW(value
)-1] == *value
)) quote
= *value
++;
109 lstrcpynW( buffer
, value
, len
);
110 if (quote
&& (len
>= lstrlenW(value
))) buffer
[lstrlenW(buffer
)-1] = '\0';
113 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
114 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
117 USHORT
* shortbuffer
= buffer
;
118 for (i
= 0; i
< len
; i
++)
119 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
122 /* writes any necessary encoding marker to the file */
123 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
125 DWORD dwBytesWritten
;
132 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
134 case ENCODING_UTF16LE
:
136 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
138 case ENCODING_UTF16BE
:
140 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
145 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
148 int write_buffer_len
;
149 DWORD dwBytesWritten
;
151 TRACE("writing: %s\n", debugstr_wn(szLine
, len
));
156 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
157 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
158 if (!write_buffer
) return;
159 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
160 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
161 HeapFree(GetProcessHeap(), 0, write_buffer
);
164 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
165 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
166 if (!write_buffer
) return;
167 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
168 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
169 HeapFree(GetProcessHeap(), 0, write_buffer
);
171 case ENCODING_UTF16LE
:
172 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
174 case ENCODING_UTF16BE
:
175 PROFILE_ByteSwapShortBuffer(szLine
, len
);
176 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
179 FIXME("encoding type %d not implemented\n", encoding
);
183 /***********************************************************************
186 * Save a profile tree to a file.
188 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
193 PROFILE_WriteMarker(hFile
, encoding
);
195 for ( ; section
; section
= section
->next
)
199 if (section
->name
[0]) len
+= lstrlenW(section
->name
) + 4;
201 for (key
= section
->key
; key
; key
= key
->next
)
203 len
+= lstrlenW(key
->name
) + 2;
204 if (key
->value
) len
+= lstrlenW(key
->value
) + 1;
207 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
211 if (section
->name
[0])
214 lstrcpyW( p
, section
->name
);
221 for (key
= section
->key
; key
; key
= key
->next
)
223 lstrcpyW( p
, key
->name
);
228 lstrcpyW( p
, key
->value
);
234 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
235 HeapFree(GetProcessHeap(), 0, buffer
);
240 /***********************************************************************
243 * Free a profile tree.
245 static void PROFILE_Free( PROFILESECTION
*section
)
247 PROFILESECTION
*next_section
;
248 PROFILEKEY
*key
, *next_key
;
250 for ( ; section
; section
= next_section
)
252 for (key
= section
->key
; key
; key
= next_key
)
254 next_key
= key
->next
;
255 HeapFree( GetProcessHeap(), 0, key
->value
);
256 HeapFree( GetProcessHeap(), 0, key
);
258 next_section
= section
->next
;
259 HeapFree( GetProcessHeap(), 0, section
);
263 /* returns TRUE if a whitespace character, else FALSE */
264 static inline BOOL
PROFILE_isspaceW(WCHAR c
)
266 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
267 return (c
>= 0x09 && c
<= 0x0d) || c
== 0x1a || c
== 0x20;
270 static inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
272 int flags
= IS_TEXT_UNICODE_SIGNATURE
|
273 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
274 IS_TEXT_UNICODE_ODD_LENGTH
;
275 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
277 *len
= sizeof(bom_utf8
);
278 return ENCODING_UTF8
;
280 RtlIsTextUnicode(buffer
, *len
, &flags
);
281 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
283 *len
= sizeof(WCHAR
);
284 return ENCODING_UTF16LE
;
286 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
288 *len
= sizeof(WCHAR
);
289 return ENCODING_UTF16BE
;
292 return ENCODING_ANSI
;
296 /***********************************************************************
299 * Load a profile tree from a file.
301 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
303 void *buffer_base
, *pBuffer
;
305 const WCHAR
*szLineStart
, *szLineEnd
;
306 const WCHAR
*szValueStart
, *szEnd
, *next_line
;
308 PROFILESECTION
*section
, *first_section
;
309 PROFILESECTION
**next_section
;
310 PROFILEKEY
*key
, *prev_key
, **next_key
;
313 TRACE("%p\n", hFile
);
315 dwFileSize
= GetFileSize(hFile
, NULL
);
316 if (dwFileSize
== INVALID_FILE_SIZE
|| dwFileSize
== 0)
319 buffer_base
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
320 if (!buffer_base
) return NULL
;
322 if (!ReadFile(hFile
, buffer_base
, dwFileSize
, &dwFileSize
, NULL
))
324 HeapFree(GetProcessHeap(), 0, buffer_base
);
325 WARN("Error %d reading file\n", GetLastError());
329 *pEncoding
= PROFILE_DetectTextEncoding(buffer_base
, &len
);
330 /* len is set to the number of bytes in the character marker.
331 * we want to skip these bytes */
332 pBuffer
= (char *)buffer_base
+ len
;
337 TRACE("ANSI encoding\n");
339 len
= MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, NULL
, 0);
340 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
343 HeapFree(GetProcessHeap(), 0, buffer_base
);
346 MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, szFile
, len
);
347 szEnd
= szFile
+ len
;
350 TRACE("UTF8 encoding\n");
352 len
= MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, NULL
, 0);
353 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
356 HeapFree(GetProcessHeap(), 0, buffer_base
);
359 MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, szFile
, len
);
360 szEnd
= szFile
+ len
;
362 case ENCODING_UTF16LE
:
363 TRACE("UTF16 Little Endian encoding\n");
365 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
367 case ENCODING_UTF16BE
:
368 TRACE("UTF16 Big Endian encoding\n");
370 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
371 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
374 FIXME("encoding type %d not implemented\n", *pEncoding
);
375 HeapFree(GetProcessHeap(), 0, buffer_base
);
379 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
380 if(first_section
== NULL
)
382 if (szFile
!= pBuffer
)
383 HeapFree(GetProcessHeap(), 0, szFile
);
384 HeapFree(GetProcessHeap(), 0, buffer_base
);
387 first_section
->name
[0] = 0;
388 first_section
->key
= NULL
;
389 first_section
->next
= NULL
;
390 next_section
= &first_section
->next
;
391 next_key
= &first_section
->key
;
395 while (next_line
< szEnd
)
397 szLineStart
= next_line
;
398 while (next_line
< szEnd
&& *next_line
!= '\n' && *next_line
!= '\r') next_line
++;
399 while (next_line
< szEnd
&& (*next_line
== '\n' || *next_line
== '\r')) next_line
++;
400 szLineEnd
= next_line
;
402 /* get rid of white space */
403 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
)) szLineStart
++;
404 while ((szLineEnd
> szLineStart
) && PROFILE_isspaceW(szLineEnd
[-1])) szLineEnd
--;
406 if (szLineStart
>= szLineEnd
) continue;
408 if (*szLineStart
== '[') /* section start */
410 for (len
= szLineEnd
- szLineStart
; len
> 0; len
--) if (szLineStart
[len
- 1] == ']') break;
413 WARN("Invalid section header: %s\n",
414 debugstr_wn(szLineStart
, (int)(szLineEnd
- szLineStart
)) );
420 /* no need to allocate +1 for NULL terminating character as
421 * already included in structure */
422 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
424 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
425 section
->name
[len
] = '\0';
427 section
->next
= NULL
;
428 *next_section
= section
;
429 next_section
= §ion
->next
;
430 next_key
= §ion
->key
;
433 TRACE("New section: %s\n", debugstr_w(section
->name
));
439 /* get rid of white space after the name and before the start
441 len
= szLineEnd
- szLineStart
;
442 for (szValueStart
= szLineStart
; szValueStart
< szLineEnd
; szValueStart
++) if (*szValueStart
== '=') break;
443 if (szValueStart
< szLineEnd
)
445 const WCHAR
*szNameEnd
= szValueStart
;
446 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(szNameEnd
[-1])) szNameEnd
--;
447 len
= szNameEnd
- szLineStart
;
449 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
451 else szValueStart
= NULL
;
453 if (len
|| !prev_key
|| *prev_key
->name
)
455 /* no need to allocate +1 for NULL terminating character as
456 * already included in structure */
457 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
458 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
459 key
->name
[len
] = '\0';
462 len
= (int)(szLineEnd
- szValueStart
);
463 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
464 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
465 key
->value
[len
] = '\0';
467 else key
->value
= NULL
;
471 next_key
= &key
->next
;
474 TRACE("New key: name=%s, value=%s\n",
475 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
478 if (szFile
!= pBuffer
)
479 HeapFree(GetProcessHeap(), 0, szFile
);
480 HeapFree(GetProcessHeap(), 0, buffer_base
);
481 return first_section
;
485 /***********************************************************************
488 * Delete a key from a profile tree.
490 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
491 LPCWSTR section_name
, LPCWSTR key_name
)
495 if (!wcsicmp( (*section
)->name
, section_name
))
497 PROFILEKEY
**key
= &(*section
)->key
;
500 if (!wcsicmp( (*key
)->name
, key_name
))
502 PROFILEKEY
*to_del
= *key
;
504 HeapFree( GetProcessHeap(), 0, to_del
->value
);
505 HeapFree( GetProcessHeap(), 0, to_del
);
511 section
= &(*section
)->next
;
517 /***********************************************************************
518 * PROFILE_DeleteAllKeys
520 * Delete all keys from a profile tree.
522 static void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
524 PROFILESECTION
**section
= &CurProfile
->section
;
527 if (!wcsicmp( (*section
)->name
, section_name
))
529 PROFILEKEY
**key
= &(*section
)->key
;
532 PROFILEKEY
*to_del
= *key
;
534 HeapFree( GetProcessHeap(), 0, to_del
->value
);
535 HeapFree( GetProcessHeap(), 0, to_del
);
536 CurProfile
->changed
=TRUE
;
539 section
= &(*section
)->next
;
544 /***********************************************************************
547 * Find a key in a profile tree, optionally creating it.
549 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
550 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
553 int seclen
= 0, keylen
= 0;
555 while (PROFILE_isspaceW(*section_name
)) section_name
++;
558 p
= section_name
+ lstrlenW(section_name
) - 1;
559 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
560 seclen
= p
- section_name
+ 1;
563 while (PROFILE_isspaceW(*key_name
)) key_name
++;
566 p
= key_name
+ lstrlenW(key_name
) - 1;
567 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
568 keylen
= p
- key_name
+ 1;
573 if (!wcsnicmp((*section
)->name
, section_name
, seclen
) &&
574 ((*section
)->name
)[seclen
] == '\0')
576 PROFILEKEY
**key
= &(*section
)->key
;
580 /* If create_always is FALSE then we check if the keyname
581 * already exists. Otherwise we add it regardless of its
582 * existence, to allow keys to be added more than once in
587 if ( (!(wcsnicmp( (*key
)->name
, key_name
, keylen
)))
588 && (((*key
)->name
)[keylen
] == '\0') )
593 if (!create
) return NULL
;
594 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + lstrlenW(key_name
) * sizeof(WCHAR
) )))
596 lstrcpyW( (*key
)->name
, key_name
);
597 (*key
)->value
= NULL
;
601 section
= &(*section
)->next
;
603 if (!create
) return NULL
;
604 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + lstrlenW(section_name
) * sizeof(WCHAR
) );
605 if(*section
== NULL
) return NULL
;
606 lstrcpyW( (*section
)->name
, section_name
);
607 (*section
)->next
= NULL
;
608 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
609 sizeof(PROFILEKEY
) + lstrlenW(key_name
) * sizeof(WCHAR
) )))
611 HeapFree(GetProcessHeap(), 0, *section
);
614 lstrcpyW( (*section
)->key
->name
, key_name
);
615 (*section
)->key
->value
= NULL
;
616 (*section
)->key
->next
= NULL
;
617 return (*section
)->key
;
621 /***********************************************************************
624 * Flush the current profile to disk if changed.
626 static BOOL
PROFILE_FlushFile(void)
629 FILETIME LastWriteTime
;
633 WARN("No current profile!\n");
637 if (!CurProfile
->changed
) return TRUE
;
639 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
640 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
642 if (hFile
== INVALID_HANDLE_VALUE
)
644 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile
->filename
), GetLastError());
648 TRACE("Saving %s\n", debugstr_w(CurProfile
->filename
));
649 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
650 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
651 CurProfile
->LastWriteTime
=LastWriteTime
;
652 CloseHandle( hFile
);
653 CurProfile
->changed
= FALSE
;
658 /***********************************************************************
659 * PROFILE_ReleaseFile
661 * Flush the current profile to disk and remove it from the cache.
663 static void PROFILE_ReleaseFile(void)
666 PROFILE_Free( CurProfile
->section
);
667 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
668 CurProfile
->changed
= FALSE
;
669 CurProfile
->section
= NULL
;
670 CurProfile
->filename
= NULL
;
671 CurProfile
->encoding
= ENCODING_ANSI
;
672 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
675 /***********************************************************************
677 * Compares a file time with the current time. If the file time is
678 * at least 2.1 seconds in the past, return true.
680 * Intended as cache safety measure: The time resolution on FAT is
681 * two seconds, so files that are not at least two seconds old might
682 * keep their time even on modification, so don't cache them.
684 static BOOL
is_not_current(FILETIME
*ft
)
689 NtQuerySystemTime( &now
);
690 ftll
= ((LONGLONG
)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
;
691 TRACE("%s; %s\n", wine_dbgstr_longlong(ftll
), wine_dbgstr_longlong(now
.QuadPart
));
692 return ftll
+ 21000000 < now
.QuadPart
;
695 /***********************************************************************
698 * Open a profile file, checking the cached file first.
700 static BOOL
PROFILE_Open( LPCWSTR filename
, BOOL write_access
)
702 WCHAR buffer
[MAX_PATH
];
703 HANDLE hFile
= INVALID_HANDLE_VALUE
;
704 FILETIME LastWriteTime
;
706 PROFILE
*tempProfile
;
708 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
710 /* First time around */
713 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
715 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
716 if(MRUProfile
[i
] == NULL
) break;
717 MRUProfile
[i
]->changed
=FALSE
;
718 MRUProfile
[i
]->section
=NULL
;
719 MRUProfile
[i
]->filename
=NULL
;
720 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
721 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
725 filename
= L
"win.ini";
727 if ((RtlDetermineDosPathNameType_U(filename
) == RELATIVE_PATH
) &&
728 !wcschr(filename
, '\\') && !wcschr(filename
, '/'))
730 WCHAR windirW
[MAX_PATH
];
731 GetWindowsDirectoryW( windirW
, MAX_PATH
);
732 lstrcpyW(buffer
, windirW
);
733 lstrcatW(buffer
, L
"\\");
734 lstrcatW(buffer
, filename
);
739 GetFullPathNameW(filename
, ARRAY_SIZE(buffer
), buffer
, &dummy
);
742 TRACE("path: %s\n", debugstr_w(buffer
));
744 hFile
= CreateFileW(buffer
, GENERIC_READ
| (write_access
? GENERIC_WRITE
: 0),
745 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
746 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
748 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
750 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer
));
754 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
756 if ((MRUProfile
[i
]->filename
&& !wcsicmp( buffer
, MRUProfile
[i
]->filename
)))
758 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile
[i
]->filename
), debugstr_w(buffer
));
762 tempProfile
=MRUProfile
[i
];
764 MRUProfile
[j
]=MRUProfile
[j
-1];
765 CurProfile
=tempProfile
;
768 if (hFile
!= INVALID_HANDLE_VALUE
)
770 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
771 if (!memcmp( &CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
) ) &&
772 is_not_current(&LastWriteTime
))
773 TRACE("(%s): already opened (mru=%d)\n",
774 debugstr_w(buffer
), i
);
777 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
778 debugstr_w(buffer
), i
);
779 PROFILE_Free(CurProfile
->section
);
780 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
781 CurProfile
->LastWriteTime
= LastWriteTime
;
786 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
787 debugstr_w(buffer
), i
);
791 /* Flush the old current profile */
794 /* Make the oldest profile the current one only in order to get rid of it */
795 if(i
==N_CACHED_PROFILES
)
797 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
798 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
799 MRUProfile
[i
]=MRUProfile
[i
-1];
800 CurProfile
=tempProfile
;
802 if(CurProfile
->filename
) PROFILE_ReleaseFile();
804 /* OK, now that CurProfile is definitely free we assign it our new file */
805 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(buffer
)+1) * sizeof(WCHAR
) );
806 lstrcpyW( CurProfile
->filename
, buffer
);
808 if (hFile
!= INVALID_HANDLE_VALUE
)
810 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
811 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
816 /* Does not exist yet, we will create it in PROFILE_FlushFile */
817 WARN("profile file %s not found\n", debugstr_w(buffer
) );
823 /***********************************************************************
826 * Returns all keys of a section.
827 * If return_values is TRUE, also include the corresponding values.
829 static INT
PROFILE_GetSection( const WCHAR
*filename
, LPCWSTR section_name
,
830 LPWSTR buffer
, UINT len
, BOOL return_values
)
832 PROFILESECTION
*section
;
835 if(!buffer
) return 0;
837 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
839 EnterCriticalSection( &PROFILE_CritSect
);
841 if (!PROFILE_Open( filename
, FALSE
))
843 LeaveCriticalSection( &PROFILE_CritSect
);
848 for (section
= CurProfile
->section
; section
; section
= section
->next
)
850 if (!wcsicmp( section
->name
, section_name
))
853 for (key
= section
->key
; key
; key
= key
->next
)
856 if (!*key
->name
&& !key
->value
) continue; /* Skip empty lines */
857 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
858 if (!return_values
&& !key
->value
) continue; /* Skip lines w.o. '=' */
859 lstrcpynW( buffer
, key
->name
, len
- 1 );
860 len
-= lstrlenW(buffer
) + 1;
861 buffer
+= lstrlenW(buffer
) + 1;
864 if (return_values
&& key
->value
) {
866 lstrcpynW( buffer
, key
->value
, len
- 1 );
867 len
-= lstrlenW(buffer
) + 1;
868 buffer
+= lstrlenW(buffer
) + 1;
873 LeaveCriticalSection( &PROFILE_CritSect
);
876 /*If either lpszSection or lpszKey is NULL and the supplied
877 destination buffer is too small to hold all the strings,
878 the last string is truncated and followed by two null characters.
879 In this case, the return value is equal to cchReturnBuffer
888 buffer
[0] = buffer
[1] = '\0';
890 LeaveCriticalSection( &PROFILE_CritSect
);
895 static BOOL
PROFILE_DeleteSection( const WCHAR
*filename
, const WCHAR
*name
)
897 PROFILESECTION
**section
;
899 EnterCriticalSection( &PROFILE_CritSect
);
901 if (!PROFILE_Open( filename
, TRUE
))
903 LeaveCriticalSection( &PROFILE_CritSect
);
907 for (section
= &CurProfile
->section
; *section
; section
= &(*section
)->next
)
909 if (!wcsicmp( (*section
)->name
, name
))
911 PROFILESECTION
*to_del
= *section
;
912 *section
= to_del
->next
;
914 PROFILE_Free( to_del
);
915 CurProfile
->changed
= TRUE
;
921 LeaveCriticalSection( &PROFILE_CritSect
);
926 /* See GetPrivateProfileSectionNamesA for documentation */
927 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
931 PROFILESECTION
*section
;
933 TRACE("(%p, %d)\n", buffer
, len
);
944 section
= CurProfile
->section
;
945 while ((section
!=NULL
)) {
946 if (section
->name
[0]) {
947 tmplen
= lstrlenW(section
->name
)+1;
948 if (tmplen
>= buflen
) {
950 memcpy(buf
, section
->name
, (buflen
-1) * sizeof(WCHAR
));
957 memcpy(buf
, section
->name
, tmplen
* sizeof(WCHAR
));
961 section
= section
->next
;
967 /***********************************************************************
970 * Set a profile string.
972 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
973 LPCWSTR value
, BOOL create_always
)
975 if (!value
) /* Delete a key */
977 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
978 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
979 section_name
, key_name
);
980 return TRUE
; /* same error handling as above */
982 else /* Set the key value */
984 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
985 key_name
, TRUE
, create_always
);
986 TRACE("(%s,%s,%s):\n",
987 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
988 if (!key
) return FALSE
;
990 /* strip the leading spaces. We can safely strip \n\r and
991 * friends too, they should not happen here anyway. */
992 while (PROFILE_isspaceW(*value
)) value
++;
996 if (!wcscmp( key
->value
, value
))
998 TRACE(" no change needed\n" );
999 return TRUE
; /* No change needed */
1001 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
1002 HeapFree( GetProcessHeap(), 0, key
->value
);
1004 else TRACE(" creating key\n" );
1005 key
->value
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(value
)+1) * sizeof(WCHAR
) );
1006 lstrcpyW( key
->value
, value
);
1007 CurProfile
->changed
= TRUE
;
1012 static HKEY
open_file_mapping_key( const WCHAR
*filename
)
1014 static HKEY mapping_key
;
1017 EnterCriticalSection( &PROFILE_CritSect
);
1019 if (!mapping_key
&& RegOpenKeyExW( HKEY_LOCAL_MACHINE
,
1020 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping",
1021 0, KEY_WOW64_64KEY
, &mapping_key
))
1024 LeaveCriticalSection( &PROFILE_CritSect
);
1026 if (mapping_key
&& !RegOpenKeyExW( mapping_key
, PathFindFileNameW( filename
), 0, KEY_READ
, &key
))
1031 static WCHAR
*enum_key( HKEY key
, DWORD i
)
1033 WCHAR
*value
, *new_value
;
1034 DWORD max
= 256, len
;
1037 if (!(value
= HeapAlloc( GetProcessHeap(), 0, max
* sizeof(WCHAR
) ))) return NULL
;
1039 while ((res
= RegEnumValueW( key
, i
, value
, &len
, NULL
, NULL
, NULL
, NULL
)) == ERROR_MORE_DATA
)
1042 if (!(new_value
= HeapReAlloc( GetProcessHeap(), 0, value
, max
* sizeof(WCHAR
) )))
1044 HeapFree( GetProcessHeap(), 0, value
);
1050 if (!res
) return value
;
1051 HeapFree( GetProcessHeap(), 0, value
);
1055 static WCHAR
*get_key_value( HKEY key
, const WCHAR
*value
)
1060 if (RegGetValueW( key
, NULL
, value
, RRF_RT_REG_SZ
| RRF_NOEXPAND
, NULL
, NULL
, &size
)) return NULL
;
1061 if (!(data
= HeapAlloc( GetProcessHeap(), 0, size
))) return NULL
;
1062 if (!RegGetValueW( key
, NULL
, value
, RRF_RT_REG_SZ
| RRF_NOEXPAND
, NULL
, (BYTE
*)data
, &size
)) return data
;
1063 HeapFree( GetProcessHeap(), 0, data
);
1067 static HKEY
open_mapped_key( const WCHAR
*path
, BOOL write
)
1069 static const WCHAR usrW
[] = {'U','S','R',':'};
1070 static const WCHAR sysW
[] = {'S','Y','S',':'};
1071 WCHAR
*combined_path
;
1076 TRACE("%s\n", debugstr_w( path
));
1078 for (p
= path
; strchr("!#@", *p
); p
++)
1079 FIXME("ignoring %c modifier\n", *p
);
1081 if (!wcsncmp( p
, usrW
, ARRAY_SIZE( usrW
) ))
1084 res
= RegCreateKeyExW( HKEY_CURRENT_USER
, p
+ 4, 0, NULL
, 0, KEY_READ
| KEY_WRITE
, NULL
, &key
, NULL
);
1086 res
= RegOpenKeyExW( HKEY_CURRENT_USER
, p
+ 4, 0, KEY_READ
, &key
);
1087 return res
? NULL
: key
;
1090 if (!wcsncmp( p
, sysW
, ARRAY_SIZE( sysW
) ))
1093 if (!(combined_path
= HeapAlloc( GetProcessHeap(), 0,
1094 (ARRAY_SIZE( L
"Software\\" ) + lstrlenW( p
)) * sizeof(WCHAR
) )))
1096 lstrcpyW( combined_path
, L
"Software\\" );
1097 lstrcatW( combined_path
, p
);
1099 res
= RegCreateKeyExW( HKEY_LOCAL_MACHINE
, combined_path
, 0, NULL
,
1100 0, KEY_READ
| KEY_WRITE
, NULL
, &key
, NULL
);
1102 res
= RegOpenKeyExW( HKEY_LOCAL_MACHINE
, combined_path
, 0, KEY_READ
, &key
);
1103 HeapFree( GetProcessHeap(), 0, combined_path
);
1104 return res
? NULL
: key
;
1107 FIXME("unhandled path syntax %s\n", debugstr_w( path
));
1111 /* returns TRUE if the given section + name is mapped */
1112 static BOOL
get_mapped_section_key( const WCHAR
*filename
, const WCHAR
*section
,
1113 const WCHAR
*name
, BOOL write
, HKEY
*ret_key
)
1115 WCHAR
*path
= NULL
, *combined_path
;
1116 HKEY key
, subkey
= NULL
;
1118 if (!(key
= open_file_mapping_key( filename
)))
1121 if (!RegOpenKeyExW( key
, section
, 0, KEY_READ
, &subkey
))
1123 if (!(path
= get_key_value( subkey
, name
)))
1124 path
= get_key_value( subkey
, NULL
);
1125 RegCloseKey( subkey
);
1127 if (!path
) return FALSE
;
1131 if (!(path
= get_key_value( key
, section
)))
1133 if ((path
= get_key_value( key
, NULL
)))
1135 if ((combined_path
= HeapAlloc( GetProcessHeap(), 0,
1136 (lstrlenW( path
) + lstrlenW( section
) + 2) * sizeof(WCHAR
) )))
1138 lstrcpyW( combined_path
, path
);
1139 lstrcatW( combined_path
, L
"\\" );
1140 lstrcatW( combined_path
, section
);
1142 HeapFree( GetProcessHeap(), 0, path
);
1143 path
= combined_path
;
1147 if (!path
) return FALSE
;
1150 *ret_key
= open_mapped_key( path
, write
);
1151 HeapFree( GetProcessHeap(), 0, path
);
1155 static DWORD
get_mapped_section( HKEY key
, WCHAR
*buffer
, DWORD size
, BOOL return_values
)
1157 WCHAR
*entry
, *value
;
1160 for (i
= 0; (entry
= enum_key( key
, i
)); ++i
)
1162 lstrcpynW( buffer
+ ret
, entry
, size
- ret
- 1 );
1163 ret
= min( ret
+ lstrlenW( entry
) + 1, size
- 1 );
1164 if (return_values
&& ret
< size
- 1 && (value
= get_key_value( key
, entry
)))
1166 buffer
[ret
- 1] = '=';
1167 lstrcpynW( buffer
+ ret
, value
, size
- ret
- 1 );
1168 ret
= min( ret
+ lstrlenW( value
) + 1, size
- 1 );
1169 HeapFree( GetProcessHeap(), 0, value
);
1171 HeapFree( GetProcessHeap(), 0, entry
);
1177 static DWORD
get_section( const WCHAR
*filename
, const WCHAR
*section
,
1178 WCHAR
*buffer
, DWORD size
, BOOL return_values
)
1180 HKEY key
, subkey
, section_key
;
1181 BOOL use_ini
= TRUE
;
1185 if ((key
= open_file_mapping_key( filename
)))
1187 if (!RegOpenKeyExW( key
, section
, 0, KEY_READ
, &subkey
))
1189 WCHAR
*entry
, *value
;
1193 for (i
= 0; (entry
= enum_key( subkey
, i
)); ++i
)
1195 if (!(path
= get_key_value( subkey
, entry
)))
1197 HeapFree( GetProcessHeap(), 0, entry
);
1201 entry_key
= open_mapped_key( path
, FALSE
);
1202 HeapFree( GetProcessHeap(), 0, path
);
1205 HeapFree( GetProcessHeap(), 0, entry
);
1211 if ((value
= get_key_value( entry_key
, entry
)))
1213 lstrcpynW( buffer
+ ret
, entry
, size
- ret
- 1 );
1214 ret
= min( ret
+ lstrlenW( entry
) + 1, size
- 1 );
1215 if (return_values
&& ret
< size
- 1)
1217 buffer
[ret
- 1] = '=';
1218 lstrcpynW( buffer
+ ret
, value
, size
- ret
- 1 );
1219 ret
= min( ret
+ lstrlenW( value
) + 1, size
- 1 );
1221 HeapFree( GetProcessHeap(), 0, value
);
1226 ret
= get_mapped_section( entry_key
, buffer
, size
, return_values
);
1230 HeapFree( GetProcessHeap(), 0, entry
);
1231 RegCloseKey( entry_key
);
1234 RegCloseKey( subkey
);
1236 else if (get_mapped_section_key( filename
, section
, NULL
, FALSE
, §ion_key
))
1238 ret
= get_mapped_section( section_key
, buffer
, size
, return_values
);
1240 RegCloseKey( section_key
);
1247 ret
+= PROFILE_GetSection( filename
, section
, buffer
+ ret
, size
- ret
, return_values
);
1252 static void delete_key_values( HKEY key
)
1256 while ((entry
= enum_key( key
, 0 )))
1258 RegDeleteValueW( key
, entry
);
1259 HeapFree( GetProcessHeap(), 0, entry
);
1263 static BOOL
delete_section( const WCHAR
*filename
, const WCHAR
*section
)
1265 HKEY key
, subkey
, section_key
;
1267 if ((key
= open_file_mapping_key( filename
)))
1269 if (!RegOpenKeyExW( key
, section
, 0, KEY_READ
, &subkey
))
1271 WCHAR
*entry
, *path
;
1275 for (i
= 0; (entry
= enum_key( subkey
, i
)); ++i
)
1277 if (!(path
= get_key_value( subkey
, entry
)))
1279 HeapFree( GetProcessHeap(), 0, entry
);
1283 entry_key
= open_mapped_key( path
, TRUE
);
1284 HeapFree( GetProcessHeap(), 0, path
);
1287 HeapFree( GetProcessHeap(), 0, entry
);
1292 RegDeleteValueW( entry_key
, entry
);
1294 delete_key_values( entry_key
);
1296 HeapFree( GetProcessHeap(), 0, entry
);
1297 RegCloseKey( entry_key
);
1300 RegCloseKey( subkey
);
1302 else if (get_mapped_section_key( filename
, section
, NULL
, TRUE
, §ion_key
))
1304 delete_key_values( section_key
);
1305 RegCloseKey( section_key
);
1311 return PROFILE_DeleteSection( filename
, section
);
1314 /********************* API functions **********************************/
1317 /***********************************************************************
1318 * GetProfileIntA (KERNEL32.@)
1320 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1322 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1325 /***********************************************************************
1326 * GetProfileIntW (KERNEL32.@)
1328 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1330 return GetPrivateProfileIntW( section
, entry
, def_val
, L
"win.ini" );
1333 /***********************************************************************
1334 * GetPrivateProfileStringW (KERNEL32.@)
1336 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1337 LPCWSTR def_val
, LPWSTR buffer
,
1338 UINT len
, LPCWSTR filename
)
1341 LPWSTR defval_tmp
= NULL
;
1345 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1346 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1348 if (!buffer
|| !len
) return 0;
1349 if (!def_val
) def_val
= L
"";
1350 if (!section
) return GetPrivateProfileSectionNamesW( buffer
, len
, filename
);
1353 ret
= get_section( filename
, section
, buffer
, len
, FALSE
);
1356 PROFILE_CopyEntry( buffer
, def_val
, len
);
1357 ret
= lstrlenW( buffer
);
1362 /* strip any trailing ' ' of def_val. */
1363 p
= def_val
+ lstrlenW(def_val
) - 1;
1365 while (p
> def_val
&& *p
== ' ') p
--;
1369 int vlen
= (int)(p
- def_val
) + 1;
1371 defval_tmp
= HeapAlloc(GetProcessHeap(), 0, (vlen
+ 1) * sizeof(WCHAR
));
1372 memcpy(defval_tmp
, def_val
, vlen
* sizeof(WCHAR
));
1373 defval_tmp
[vlen
] = '\0';
1374 def_val
= defval_tmp
;
1377 if (get_mapped_section_key( filename
, section
, entry
, FALSE
, &key
))
1383 if ((value
= get_key_value( key
, entry
)))
1385 lstrcpynW( buffer
, value
, len
);
1386 HeapFree( GetProcessHeap(), 0, value
);
1389 lstrcpynW( buffer
, def_val
, len
);
1394 lstrcpynW( buffer
, def_val
, len
);
1396 ret
= lstrlenW( buffer
);
1400 EnterCriticalSection( &PROFILE_CritSect
);
1402 if (PROFILE_Open( filename
, FALSE
))
1404 PROFILEKEY
*key
= PROFILE_Find( &CurProfile
->section
, section
, entry
, FALSE
, FALSE
);
1405 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
, len
);
1406 TRACE("-> %s\n", debugstr_w( buffer
));
1407 ret
= lstrlenW( buffer
);
1411 lstrcpynW( buffer
, def_val
, len
);
1412 ret
= lstrlenW( buffer
);
1415 LeaveCriticalSection( &PROFILE_CritSect
);
1418 HeapFree(GetProcessHeap(), 0, defval_tmp
);
1420 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1425 /***********************************************************************
1426 * GetPrivateProfileStringA (KERNEL32.@)
1428 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1429 LPCSTR def_val
, LPSTR buffer
,
1430 UINT len
, LPCSTR filename
)
1432 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1436 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1437 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1438 else sectionW
.Buffer
= NULL
;
1439 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1440 else entryW
.Buffer
= NULL
;
1441 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1442 else def_valW
.Buffer
= NULL
;
1443 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1444 else filenameW
.Buffer
= NULL
;
1446 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1447 def_valW
.Buffer
, bufferW
, len
,
1453 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, len
- 1, NULL
, NULL
);
1460 RtlFreeUnicodeString(§ionW
);
1461 RtlFreeUnicodeString(&entryW
);
1462 RtlFreeUnicodeString(&def_valW
);
1463 RtlFreeUnicodeString(&filenameW
);
1464 HeapFree(GetProcessHeap(), 0, bufferW
);
1468 /***********************************************************************
1469 * GetProfileStringA (KERNEL32.@)
1471 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1472 LPSTR buffer
, UINT len
)
1474 return GetPrivateProfileStringA( section
, entry
, def_val
,
1475 buffer
, len
, "win.ini" );
1478 /***********************************************************************
1479 * GetProfileStringW (KERNEL32.@)
1481 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1482 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1484 return GetPrivateProfileStringW( section
, entry
, def_val
, buffer
, len
, L
"win.ini" );
1487 /***********************************************************************
1488 * WriteProfileStringA (KERNEL32.@)
1490 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1493 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1496 /***********************************************************************
1497 * WriteProfileStringW (KERNEL32.@)
1499 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1502 return WritePrivateProfileStringW( section
, entry
, string
, L
"win.ini" );
1506 /***********************************************************************
1507 * GetPrivateProfileIntW (KERNEL32.@)
1509 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1510 INT def_val
, LPCWSTR filename
)
1513 UNICODE_STRING bufferW
;
1516 if (GetPrivateProfileStringW( section
, entry
, L
"", buffer
, ARRAY_SIZE( buffer
),
1520 /* FIXME: if entry can be found but it's empty, then Win16 is
1521 * supposed to return 0 instead of def_val ! Difficult/problematic
1522 * to implement (every other failure also returns zero buffer),
1523 * thus wait until testing framework avail for making sure nothing
1524 * else gets broken that way. */
1525 if (!buffer
[0]) return (UINT
)def_val
;
1527 RtlInitUnicodeString( &bufferW
, buffer
);
1528 RtlUnicodeStringToInteger( &bufferW
, 0, &result
);
1532 /***********************************************************************
1533 * GetPrivateProfileIntA (KERNEL32.@)
1535 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1536 INT def_val
, LPCSTR filename
)
1538 UNICODE_STRING entryW
, filenameW
, sectionW
;
1540 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1541 else entryW
.Buffer
= NULL
;
1542 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1543 else filenameW
.Buffer
= NULL
;
1544 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1545 else sectionW
.Buffer
= NULL
;
1546 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1548 RtlFreeUnicodeString(§ionW
);
1549 RtlFreeUnicodeString(&filenameW
);
1550 RtlFreeUnicodeString(&entryW
);
1554 /***********************************************************************
1555 * GetPrivateProfileSectionW (KERNEL32.@)
1557 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1558 DWORD len
, LPCWSTR filename
)
1560 if (!section
|| !buffer
)
1562 SetLastError(ERROR_INVALID_PARAMETER
);
1566 TRACE("(%s, %p, %d, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1568 return get_section( filename
, section
, buffer
, len
, TRUE
);
1571 /***********************************************************************
1572 * GetPrivateProfileSectionA (KERNEL32.@)
1574 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1575 DWORD len
, LPCSTR filename
)
1577 UNICODE_STRING sectionW
, filenameW
;
1581 if (!section
|| !buffer
)
1583 SetLastError(ERROR_INVALID_PARAMETER
);
1587 bufferW
= HeapAlloc(GetProcessHeap(), 0, len
* 2 * sizeof(WCHAR
));
1588 RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1589 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1590 else filenameW
.Buffer
= NULL
;
1592 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
* 2, filenameW
.Buffer
);
1595 if (retW
== len
* 2 - 2) retW
++; /* overflow */
1596 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1597 if (!ret
|| ret
== len
) /* overflow */
1611 RtlFreeUnicodeString(§ionW
);
1612 RtlFreeUnicodeString(&filenameW
);
1613 HeapFree(GetProcessHeap(), 0, bufferW
);
1617 /***********************************************************************
1618 * GetProfileSectionA (KERNEL32.@)
1620 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1622 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1625 /***********************************************************************
1626 * GetProfileSectionW (KERNEL32.@)
1628 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1630 return GetPrivateProfileSectionW( section
, buffer
, len
, L
"win.ini" );
1634 /***********************************************************************
1635 * WritePrivateProfileStringW (KERNEL32.@)
1637 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1638 LPCWSTR string
, LPCWSTR filename
)
1643 TRACE("(%s, %s, %s, %s)\n", debugstr_w(section
), debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1645 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1647 EnterCriticalSection( &PROFILE_CritSect
);
1648 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1650 if (CurProfile
) PROFILE_ReleaseFile();
1652 LeaveCriticalSection( &PROFILE_CritSect
);
1655 if (!entry
) return delete_section( filename
, section
);
1657 if (get_mapped_section_key( filename
, section
, entry
, TRUE
, &key
))
1662 res
= RegSetValueExW( key
, entry
, 0, REG_SZ
, (const BYTE
*)string
,
1663 (lstrlenW( string
) + 1) * sizeof(WCHAR
) );
1665 res
= RegDeleteValueW( key
, entry
);
1667 if (res
) SetLastError( res
);
1671 EnterCriticalSection( &PROFILE_CritSect
);
1673 if (PROFILE_Open( filename
, TRUE
))
1676 SetLastError(ERROR_FILE_NOT_FOUND
);
1678 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1679 if (ret
) ret
= PROFILE_FlushFile();
1682 LeaveCriticalSection( &PROFILE_CritSect
);
1686 /***********************************************************************
1687 * WritePrivateProfileStringA (KERNEL32.@)
1689 BOOL WINAPI DECLSPEC_HOTPATCH
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1690 LPCSTR string
, LPCSTR filename
)
1692 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1695 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1696 else sectionW
.Buffer
= NULL
;
1697 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1698 else entryW
.Buffer
= NULL
;
1699 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1700 else stringW
.Buffer
= NULL
;
1701 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1702 else filenameW
.Buffer
= NULL
;
1704 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1705 stringW
.Buffer
, filenameW
.Buffer
);
1706 RtlFreeUnicodeString(§ionW
);
1707 RtlFreeUnicodeString(&entryW
);
1708 RtlFreeUnicodeString(&stringW
);
1709 RtlFreeUnicodeString(&filenameW
);
1713 /***********************************************************************
1714 * WritePrivateProfileSectionW (KERNEL32.@)
1716 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1717 LPCWSTR string
, LPCWSTR filename
)
1721 HKEY key
, section_key
;
1723 if (!section
&& !string
)
1725 EnterCriticalSection( &PROFILE_CritSect
);
1726 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1728 if (CurProfile
) PROFILE_ReleaseFile();
1730 LeaveCriticalSection( &PROFILE_CritSect
);
1733 if (!string
) return delete_section( filename
, section
);
1735 if ((key
= open_file_mapping_key( filename
)))
1737 /* replace existing entries, but only if they are mapped, and do not
1738 * delete any keys */
1740 const WCHAR
*entry
, *p
;
1742 for (entry
= string
; *entry
; entry
+= lstrlenW( entry
) + 1)
1744 if ((p
= wcschr( entry
, '=' )))
1748 if (!(entry_copy
= HeapAlloc( GetProcessHeap(), 0, (p
- entry
) * sizeof(WCHAR
) )))
1750 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1754 lstrcpynW( entry_copy
, entry
, p
- entry
);
1755 if (get_mapped_section_key( filename
, section
, entry_copy
, TRUE
, §ion_key
))
1757 LSTATUS res
= RegSetValueExW( section_key
, entry_copy
, 0, REG_SZ
, (const BYTE
*)p
,
1758 (lstrlenW( p
) + 1) * sizeof(WCHAR
) );
1759 RegCloseKey( section_key
);
1762 HeapFree( GetProcessHeap(), 0, entry_copy
);
1763 SetLastError( res
);
1768 HeapFree( GetProcessHeap(), 0, entry_copy
);
1775 EnterCriticalSection( &PROFILE_CritSect
);
1777 if (PROFILE_Open( filename
, TRUE
))
1779 PROFILE_DeleteAllKeys(section
);
1781 while (*string
&& ret
)
1783 WCHAR
*buf
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( string
) + 1) * sizeof(WCHAR
) );
1784 lstrcpyW( buf
, string
);
1785 if ((p
= wcschr( buf
, '=')))
1788 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1790 HeapFree( GetProcessHeap(), 0, buf
);
1791 string
+= lstrlenW( string
) + 1;
1793 if (ret
) ret
= PROFILE_FlushFile();
1796 LeaveCriticalSection( &PROFILE_CritSect
);
1800 /***********************************************************************
1801 * WritePrivateProfileSectionA (KERNEL32.@)
1803 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1804 LPCSTR string
, LPCSTR filename
)
1807 UNICODE_STRING sectionW
, filenameW
;
1816 while(*p
) p
+= strlen(p
) + 1;
1817 lenA
= p
- string
+ 1;
1818 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1819 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1820 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1822 else stringW
= NULL
;
1823 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1824 else sectionW
.Buffer
= NULL
;
1825 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1826 else filenameW
.Buffer
= NULL
;
1828 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1830 HeapFree(GetProcessHeap(), 0, stringW
);
1831 RtlFreeUnicodeString(§ionW
);
1832 RtlFreeUnicodeString(&filenameW
);
1836 /***********************************************************************
1837 * WriteProfileSectionA (KERNEL32.@)
1839 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1842 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1845 /***********************************************************************
1846 * WriteProfileSectionW (KERNEL32.@)
1848 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1850 return WritePrivateProfileSectionW(section
, keys_n_values
, L
"win.ini");
1854 /***********************************************************************
1855 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1857 * Returns the section names contained in the specified file.
1858 * FIXME: Where do we find this file when the path is relative?
1859 * The section names are returned as a list of strings with an extra
1860 * '\0' to mark the end of the list. Except for that the behavior
1861 * depends on the Windows version.
1864 * - if the buffer is 0 or 1 character long then it is as if it was of
1866 * - otherwise, if the buffer is too small only the section names that fit
1868 * - note that this means if the buffer was too small to return even just
1869 * the first section name then a single '\0' will be returned.
1870 * - the return value is the number of characters written in the buffer,
1871 * except if the buffer was too small in which case len-2 is returned
1874 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1875 * '\0' and the return value is 0
1876 * - otherwise if the buffer is too small then the first section name that
1877 * does not fit is truncated so that the string list can be terminated
1878 * correctly (double '\0')
1879 * - the return value is the number of characters written in the buffer
1880 * except for the trailing '\0'. If the buffer is too small, then the
1881 * return value is len-2
1882 * - Win2000 has a bug that triggers when the section names and the
1883 * trailing '\0' fit exactly in the buffer. In that case the trailing
1886 * Wine implements the observed Win2000 behavior (except for the bug).
1888 * Note that when the buffer is big enough then the return value may be any
1889 * value between 1 and len-1 (or len in Win95), including len-2.
1891 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1897 if ((key
= open_file_mapping_key( filename
)))
1902 for (i
= 0; (section
= enum_key( key
, i
)); ++i
)
1904 lstrcpynW( buffer
+ ret
, section
, size
- ret
- 1 );
1905 ret
= min( ret
+ lstrlenW( section
) + 1, size
- 1 );
1906 HeapFree( GetProcessHeap(), 0, section
);
1912 RtlEnterCriticalSection( &PROFILE_CritSect
);
1914 if (PROFILE_Open( filename
, FALSE
))
1915 ret
+= PROFILE_GetSectionNames( buffer
+ ret
, size
- ret
);
1917 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1923 /***********************************************************************
1924 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1926 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1929 UNICODE_STRING filenameW
;
1933 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1934 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1935 else filenameW
.Buffer
= NULL
;
1937 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1940 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+1, buffer
, size
-1, NULL
, NULL
);
1952 RtlFreeUnicodeString(&filenameW
);
1953 HeapFree(GetProcessHeap(), 0, bufferW
);
1957 static int get_hex_byte( const WCHAR
*p
)
1961 if (*p
>= '0' && *p
<= '9') val
= *p
- '0';
1962 else if (*p
>= 'A' && *p
<= 'Z') val
= *p
- 'A' + 10;
1963 else if (*p
>= 'a' && *p
<= 'z') val
= *p
- 'a' + 10;
1967 if (*p
>= '0' && *p
<= '9') val
+= *p
- '0';
1968 else if (*p
>= 'A' && *p
<= 'Z') val
+= *p
- 'A' + 10;
1969 else if (*p
>= 'a' && *p
<= 'z') val
+= *p
- 'a' + 10;
1974 /***********************************************************************
1975 * GetPrivateProfileStructW (KERNEL32.@)
1977 * Should match Win95's behaviour pretty much
1979 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1980 LPVOID buf
, UINT len
, LPCWSTR filename
)
1988 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, (2 * len
+ 3) * sizeof(WCHAR
) ))) return FALSE
;
1990 if (GetPrivateProfileStringW( section
, key
, NULL
, buffer
, 2 * len
+ 3, filename
) != 2 * len
+ 2)
1993 for (p
= buffer
; len
; p
+= 2, len
--)
1995 if ((val
= get_hex_byte( p
)) == -1) goto done
;
1999 /* retrieve stored checksum value */
2000 if ((val
= get_hex_byte( p
)) == -1) goto done
;
2001 ret
= ((BYTE
)val
== chksum
);
2004 HeapFree( GetProcessHeap(), 0, buffer
);
2008 /***********************************************************************
2009 * GetPrivateProfileStructA (KERNEL32.@)
2011 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
2012 LPVOID buffer
, UINT len
, LPCSTR filename
)
2014 UNICODE_STRING sectionW
, keyW
, filenameW
;
2017 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
2018 else sectionW
.Buffer
= NULL
;
2019 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
2020 else keyW
.Buffer
= NULL
;
2021 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
2022 else filenameW
.Buffer
= NULL
;
2024 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
2026 /* Do not translate binary data. */
2028 RtlFreeUnicodeString(§ionW
);
2029 RtlFreeUnicodeString(&keyW
);
2030 RtlFreeUnicodeString(&filenameW
);
2036 /***********************************************************************
2037 * WritePrivateProfileStructW (KERNEL32.@)
2039 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
2040 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
2044 LPWSTR outstring
, p
;
2047 if (!section
&& !key
&& !buf
) /* flush the cache */
2048 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
2050 /* allocate string buffer for hex chars + checksum hex char + '\0' */
2051 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
2053 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
2054 *p
++ = hex
[*binbuf
>> 4];
2055 *p
++ = hex
[*binbuf
& 0xf];
2058 /* checksum is sum & 0xff */
2059 *p
++ = hex
[(sum
& 0xf0) >> 4];
2060 *p
++ = hex
[sum
& 0xf];
2063 ret
= WritePrivateProfileStringW( section
, key
, outstring
, filename
);
2064 HeapFree( GetProcessHeap(), 0, outstring
);
2068 /***********************************************************************
2069 * WritePrivateProfileStructA (KERNEL32.@)
2071 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
2072 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
2074 UNICODE_STRING sectionW
, keyW
, filenameW
;
2077 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
2078 else sectionW
.Buffer
= NULL
;
2079 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
2080 else keyW
.Buffer
= NULL
;
2081 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
2082 else filenameW
.Buffer
= NULL
;
2084 /* Do not translate binary data. */
2085 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
2088 RtlFreeUnicodeString(§ionW
);
2089 RtlFreeUnicodeString(&keyW
);
2090 RtlFreeUnicodeString(&filenameW
);
2095 /***********************************************************************
2096 * OpenProfileUserMapping (KERNEL32.@)
2098 BOOL WINAPI
OpenProfileUserMapping(void) {
2099 FIXME("(), stub!\n");
2100 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2104 /***********************************************************************
2105 * CloseProfileUserMapping (KERNEL32.@)
2107 BOOL WINAPI
CloseProfileUserMapping(void) {
2108 FIXME("(), stub!\n");
2109 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);