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
28 DEBUG_CHANNEL(profile
);
30 #else /* __REACTOS__ */
33 #include "wine/port.h"
43 #include "wine/unicode.h"
44 #include "wine/library.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(profile
);
49 #endif /* __REACTOS__ */
51 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
61 typedef struct tagPROFILEKEY
64 struct tagPROFILEKEY
*next
;
68 typedef struct tagPROFILESECTION
70 struct tagPROFILEKEY
*key
;
71 struct tagPROFILESECTION
*next
;
79 PROFILESECTION
*section
;
81 FILETIME LastWriteTime
;
86 #define N_CACHED_PROFILES 10
88 /* Cached profile files */
89 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
91 #define CurProfile (MRUProfile[0])
93 /* Check for comments in profile */
94 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
96 static const WCHAR emptystringW
[] = {0};
97 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
101 static RTL_CRITICAL_SECTION PROFILE_CritSect
;
102 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
104 0, 0, &PROFILE_CritSect
,
105 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
108 static RTL_CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
110 #else /* __REACTOS__ */
112 static CRITICAL_SECTION PROFILE_CritSect
;
113 static CRITICAL_SECTION_DEBUG critsect_debug
=
115 0, 0, &PROFILE_CritSect
,
116 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
117 0, 0, { (DWORD_PTR
)(__FILE__
": PROFILE_CritSect") }
119 static CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
121 #endif /* __REACTOS__ */
123 static const char hex
[16] = "0123456789ABCDEF";
125 /***********************************************************************
128 * Copy the content of an entry into a buffer, removing quotes, and possibly
129 * translating environment variables.
131 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
138 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
140 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
143 lstrcpynW( buffer
, value
, len
);
144 if (quote
&& (len
>= lstrlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
147 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
148 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
151 USHORT
* shortbuffer
= buffer
;
152 for (i
= 0; i
< len
; i
++)
153 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
156 /* writes any necessary encoding marker to the file */
157 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
159 DWORD dwBytesWritten
;
166 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
168 case ENCODING_UTF16LE
:
170 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
172 case ENCODING_UTF16BE
:
174 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
179 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
182 int write_buffer_len
;
183 DWORD dwBytesWritten
;
185 TRACE("writing: %s\n", debugstr_wn(szLine
, len
));
190 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
191 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
192 if (!write_buffer
) return;
193 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
194 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
195 HeapFree(GetProcessHeap(), 0, write_buffer
);
198 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
199 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
200 if (!write_buffer
) return;
201 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
202 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
203 HeapFree(GetProcessHeap(), 0, write_buffer
);
205 case ENCODING_UTF16LE
:
206 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
208 case ENCODING_UTF16BE
:
209 PROFILE_ByteSwapShortBuffer(szLine
, len
);
210 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
213 FIXME("encoding type %d not implemented\n", encoding
);
217 /***********************************************************************
220 * Save a profile tree to a file.
222 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
227 PROFILE_WriteMarker(hFile
, encoding
);
229 for ( ; section
; section
= section
->next
)
233 if (section
->name
[0]) len
+= strlenW(section
->name
) + 4;
235 for (key
= section
->key
; key
; key
= key
->next
)
237 len
+= strlenW(key
->name
) + 2;
238 if (key
->value
) len
+= strlenW(key
->value
) + 1;
241 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
245 if (section
->name
[0])
248 strcpyW( p
, section
->name
);
255 for (key
= section
->key
; key
; key
= key
->next
)
257 strcpyW( p
, key
->name
);
262 strcpyW( p
, key
->value
);
268 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
269 HeapFree(GetProcessHeap(), 0, buffer
);
274 /***********************************************************************
277 * Free a profile tree.
279 static void PROFILE_Free( PROFILESECTION
*section
)
281 PROFILESECTION
*next_section
;
282 PROFILEKEY
*key
, *next_key
;
284 for ( ; section
; section
= next_section
)
286 for (key
= section
->key
; key
; key
= next_key
)
288 next_key
= key
->next
;
289 HeapFree( GetProcessHeap(), 0, key
->value
);
290 HeapFree( GetProcessHeap(), 0, key
);
292 next_section
= section
->next
;
293 HeapFree( GetProcessHeap(), 0, section
);
297 /* returns TRUE if a whitespace character, else FALSE */
298 static inline BOOL
PROFILE_isspaceW(WCHAR c
)
300 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
301 return isspaceW(c
) || c
== 0x1a;
304 static inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
306 int flags
= IS_TEXT_UNICODE_SIGNATURE
|
307 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
308 IS_TEXT_UNICODE_ODD_LENGTH
;
309 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
311 *len
= sizeof(bom_utf8
);
312 return ENCODING_UTF8
;
314 RtlIsTextUnicode(buffer
, *len
, &flags
);
315 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
317 *len
= sizeof(WCHAR
);
318 return ENCODING_UTF16LE
;
320 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
322 *len
= sizeof(WCHAR
);
323 return ENCODING_UTF16BE
;
326 return ENCODING_ANSI
;
330 /***********************************************************************
333 * Load a profile tree from a file.
335 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
337 void *buffer_base
, *pBuffer
;
339 const WCHAR
*szLineStart
, *szLineEnd
;
340 const WCHAR
*szValueStart
, *szEnd
, *next_line
;
342 PROFILESECTION
*section
, *first_section
;
343 PROFILESECTION
**next_section
;
344 PROFILEKEY
*key
, *prev_key
, **next_key
;
347 TRACE("%p\n", hFile
);
349 dwFileSize
= GetFileSize(hFile
, NULL
);
350 if (dwFileSize
== INVALID_FILE_SIZE
|| dwFileSize
== 0)
353 buffer_base
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
354 if (!buffer_base
) return NULL
;
356 if (!ReadFile(hFile
, buffer_base
, dwFileSize
, &dwFileSize
, NULL
))
358 HeapFree(GetProcessHeap(), 0, buffer_base
);
359 WARN("Error %d reading file\n", GetLastError());
363 *pEncoding
= PROFILE_DetectTextEncoding(buffer_base
, &len
);
364 /* len is set to the number of bytes in the character marker.
365 * we want to skip these bytes */
366 pBuffer
= (char *)buffer_base
+ len
;
371 TRACE("ANSI encoding\n");
373 len
= MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, NULL
, 0);
374 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
377 HeapFree(GetProcessHeap(), 0, buffer_base
);
380 MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, szFile
, len
);
381 szEnd
= szFile
+ len
;
384 TRACE("UTF8 encoding\n");
386 len
= MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, NULL
, 0);
387 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
390 HeapFree(GetProcessHeap(), 0, buffer_base
);
393 MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, szFile
, len
);
394 szEnd
= szFile
+ len
;
396 case ENCODING_UTF16LE
:
397 TRACE("UTF16 Little Endian encoding\n");
399 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
401 case ENCODING_UTF16BE
:
402 TRACE("UTF16 Big Endian encoding\n");
404 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
405 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
408 FIXME("encoding type %d not implemented\n", *pEncoding
);
409 HeapFree(GetProcessHeap(), 0, buffer_base
);
413 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
414 if(first_section
== NULL
)
416 if (szFile
!= pBuffer
)
417 HeapFree(GetProcessHeap(), 0, szFile
);
418 HeapFree(GetProcessHeap(), 0, buffer_base
);
421 first_section
->name
[0] = 0;
422 first_section
->key
= NULL
;
423 first_section
->next
= NULL
;
424 next_section
= &first_section
->next
;
425 next_key
= &first_section
->key
;
429 while (next_line
< szEnd
)
431 szLineStart
= next_line
;
432 next_line
= memchrW(szLineStart
, '\n', szEnd
- szLineStart
);
433 if (!next_line
) next_line
= memchrW(szLineStart
, '\r', szEnd
- szLineStart
);
434 if (!next_line
) next_line
= szEnd
;
436 szLineEnd
= next_line
;
440 /* get rid of white space */
441 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
)) szLineStart
++;
442 while ((szLineEnd
> szLineStart
) && PROFILE_isspaceW(szLineEnd
[-1])) szLineEnd
--;
444 if (szLineStart
>= szLineEnd
) continue;
446 if (*szLineStart
== '[') /* section start */
448 const WCHAR
* szSectionEnd
;
449 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
451 WARN("Invalid section header at line %d: %s\n",
452 line
, debugstr_wn(szLineStart
, (int)(szLineEnd
- szLineStart
)) );
457 len
= (int)(szSectionEnd
- szLineStart
);
458 /* no need to allocate +1 for NULL terminating character as
459 * already included in structure */
460 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
462 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
463 section
->name
[len
] = '\0';
465 section
->next
= NULL
;
466 *next_section
= section
;
467 next_section
= §ion
->next
;
468 next_key
= §ion
->key
;
471 TRACE("New section: %s\n", debugstr_w(section
->name
));
477 /* get rid of white space after the name and before the start
479 len
= szLineEnd
- szLineStart
;
480 if ((szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
482 const WCHAR
*szNameEnd
= szValueStart
;
483 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(szNameEnd
[-1])) szNameEnd
--;
484 len
= szNameEnd
- szLineStart
;
486 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
489 if (len
|| !prev_key
|| *prev_key
->name
)
491 /* no need to allocate +1 for NULL terminating character as
492 * already included in structure */
493 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
494 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
495 key
->name
[len
] = '\0';
498 len
= (int)(szLineEnd
- szValueStart
);
499 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
500 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
501 key
->value
[len
] = '\0';
503 else key
->value
= NULL
;
507 next_key
= &key
->next
;
511 TRACE("New key: name=%s, value=%s\n",
512 debugstr_w(key
->name
),
513 key
->value
? debugstr_w(key
->value
) : L
"(none)");
515 TRACE("New key: name=%s, value=%s\n",
516 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
520 if (szFile
!= pBuffer
)
521 HeapFree(GetProcessHeap(), 0, szFile
);
522 HeapFree(GetProcessHeap(), 0, buffer_base
);
523 return first_section
;
527 /***********************************************************************
528 * PROFILE_DeleteSection
530 * Delete a section from a profile tree.
532 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
536 if (!strcmpiW( (*section
)->name
, name
))
538 PROFILESECTION
*to_del
= *section
;
539 *section
= to_del
->next
;
541 PROFILE_Free( to_del
);
544 section
= &(*section
)->next
;
550 /***********************************************************************
553 * Delete a key from a profile tree.
555 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
556 LPCWSTR section_name
, LPCWSTR key_name
)
560 if (!strcmpiW( (*section
)->name
, section_name
))
562 PROFILEKEY
**key
= &(*section
)->key
;
565 if (!strcmpiW( (*key
)->name
, key_name
))
567 PROFILEKEY
*to_del
= *key
;
569 HeapFree( GetProcessHeap(), 0, to_del
->value
);
570 HeapFree( GetProcessHeap(), 0, to_del
);
576 section
= &(*section
)->next
;
582 /***********************************************************************
583 * PROFILE_DeleteAllKeys
585 * Delete all keys from a profile tree.
587 static void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
589 PROFILESECTION
**section
= &CurProfile
->section
;
592 if (!strcmpiW( (*section
)->name
, section_name
))
594 PROFILEKEY
**key
= &(*section
)->key
;
597 PROFILEKEY
*to_del
= *key
;
599 HeapFree( GetProcessHeap(), 0, to_del
->value
);
600 HeapFree( GetProcessHeap(), 0, to_del
);
601 CurProfile
->changed
=TRUE
;
604 section
= &(*section
)->next
;
609 /***********************************************************************
612 * Find a key in a profile tree, optionally creating it.
614 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
615 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
618 int seclen
= 0, keylen
= 0;
620 while (PROFILE_isspaceW(*section_name
)) section_name
++;
623 p
= section_name
+ strlenW(section_name
) - 1;
624 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
625 seclen
= p
- section_name
+ 1;
628 while (PROFILE_isspaceW(*key_name
)) key_name
++;
631 p
= key_name
+ strlenW(key_name
) - 1;
632 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
633 keylen
= p
- key_name
+ 1;
638 if (!strncmpiW((*section
)->name
, section_name
, seclen
) &&
639 ((*section
)->name
)[seclen
] == '\0')
641 PROFILEKEY
**key
= &(*section
)->key
;
645 /* If create_always is FALSE then we check if the keyname
646 * already exists. Otherwise we add it regardless of its
647 * existence, to allow keys to be added more than once in
652 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
653 && (((*key
)->name
)[keylen
] == '\0') )
658 if (!create
) return NULL
;
659 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
661 strcpyW( (*key
)->name
, key_name
);
662 (*key
)->value
= NULL
;
666 section
= &(*section
)->next
;
668 if (!create
) return NULL
;
669 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
670 if(*section
== NULL
) return NULL
;
671 strcpyW( (*section
)->name
, section_name
);
672 (*section
)->next
= NULL
;
673 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
674 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
676 HeapFree(GetProcessHeap(), 0, *section
);
679 strcpyW( (*section
)->key
->name
, key_name
);
680 (*section
)->key
->value
= NULL
;
681 (*section
)->key
->next
= NULL
;
682 return (*section
)->key
;
686 /***********************************************************************
689 * Flush the current profile to disk if changed.
691 static BOOL
PROFILE_FlushFile(void)
694 FILETIME LastWriteTime
;
698 WARN("No current profile!\n");
702 if (!CurProfile
->changed
) return TRUE
;
704 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
705 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
707 if (hFile
== INVALID_HANDLE_VALUE
)
709 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile
->filename
), GetLastError());
713 TRACE("Saving %s\n", debugstr_w(CurProfile
->filename
));
714 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
715 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
716 CurProfile
->LastWriteTime
=LastWriteTime
;
717 CloseHandle( hFile
);
718 CurProfile
->changed
= FALSE
;
723 /***********************************************************************
724 * PROFILE_ReleaseFile
726 * Flush the current profile to disk and remove it from the cache.
728 static void PROFILE_ReleaseFile(void)
731 PROFILE_Free( CurProfile
->section
);
732 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
733 CurProfile
->changed
= FALSE
;
734 CurProfile
->section
= NULL
;
735 CurProfile
->filename
= NULL
;
736 CurProfile
->encoding
= ENCODING_ANSI
;
737 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
740 /***********************************************************************
742 * Compares a file time with the current time. If the file time is
743 * at least 2.1 seconds in the past, return true.
745 * Intended as cache safety measure: The time resolution on FAT is
746 * two seconds, so files that are not at least two seconds old might
747 * keep their time even on modification, so don't cache them.
749 static BOOL
is_not_current(FILETIME
* ft
)
752 LONGLONG ftll
, nowll
;
753 GetSystemTimeAsFileTime(&Now
);
754 ftll
= ((LONGLONG
)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
;
755 nowll
= ((LONGLONG
)Now
.dwHighDateTime
<< 32) + Now
.dwLowDateTime
;
756 TRACE("%08x;%08x\n",(unsigned)ftll
+21000000,(unsigned)nowll
);
757 return ftll
+ 21000000 < nowll
;
760 /***********************************************************************
763 * Open a profile file, checking the cached file first.
765 static BOOL
PROFILE_Open( LPCWSTR filename
, BOOL write_access
)
767 WCHAR buffer
[MAX_PATH
];
768 HANDLE hFile
= INVALID_HANDLE_VALUE
;
769 FILETIME LastWriteTime
;
771 PROFILE
*tempProfile
;
773 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
775 /* First time around */
778 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
780 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
781 if(MRUProfile
[i
] == NULL
) break;
782 MRUProfile
[i
]->changed
=FALSE
;
783 MRUProfile
[i
]->section
=NULL
;
784 MRUProfile
[i
]->filename
=NULL
;
785 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
786 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
793 if ((RtlDetermineDosPathNameType_U(filename
) == RtlPathTypeRelative
) &&
795 if ((RtlDetermineDosPathNameType_U(filename
) == RELATIVE_PATH
) &&
797 !strchrW(filename
, '\\') && !strchrW(filename
, '/'))
799 static const WCHAR wszSeparator
[] = {'\\', 0};
800 WCHAR windirW
[MAX_PATH
];
801 GetWindowsDirectoryW( windirW
, MAX_PATH
);
802 strcpyW(buffer
, windirW
);
803 strcatW(buffer
, wszSeparator
);
804 strcatW(buffer
, filename
);
809 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
812 TRACE("path: %s\n", debugstr_w(buffer
));
814 hFile
= CreateFileW(buffer
, GENERIC_READ
| (write_access
? GENERIC_WRITE
: 0),
815 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
816 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
818 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
820 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer
));
824 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
826 if ((MRUProfile
[i
]->filename
&& !strcmpiW( buffer
, MRUProfile
[i
]->filename
)))
828 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile
[i
]->filename
), debugstr_w(buffer
));
832 tempProfile
=MRUProfile
[i
];
834 MRUProfile
[j
]=MRUProfile
[j
-1];
835 CurProfile
=tempProfile
;
838 if (hFile
!= INVALID_HANDLE_VALUE
)
840 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
841 if (!memcmp( &CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
) ) &&
842 is_not_current(&LastWriteTime
))
843 TRACE("(%s): already opened (mru=%d)\n",
844 debugstr_w(buffer
), i
);
847 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
848 debugstr_w(buffer
), i
);
849 PROFILE_Free(CurProfile
->section
);
850 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
851 CurProfile
->LastWriteTime
= LastWriteTime
;
856 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
857 debugstr_w(buffer
), i
);
861 /* Flush the old current profile */
864 /* Make the oldest profile the current one only in order to get rid of it */
865 if(i
==N_CACHED_PROFILES
)
867 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
868 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
869 MRUProfile
[i
]=MRUProfile
[i
-1];
870 CurProfile
=tempProfile
;
872 if(CurProfile
->filename
) PROFILE_ReleaseFile();
874 /* OK, now that CurProfile is definitely free we assign it our new file */
875 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer
)+1) * sizeof(WCHAR
) );
876 strcpyW( CurProfile
->filename
, buffer
);
878 if (hFile
!= INVALID_HANDLE_VALUE
)
880 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
881 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
886 /* Does not exist yet, we will create it in PROFILE_FlushFile */
887 WARN("profile file %s not found\n", debugstr_w(buffer
) );
893 /***********************************************************************
896 * Returns all keys of a section.
897 * If return_values is TRUE, also include the corresponding values.
899 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
901 LPWSTR buffer
, DWORD len
, BOOL return_values
)
903 LPWSTR buffer
, UINT len
, BOOL return_values
)
908 if(!buffer
) return 0;
910 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
914 if (!strcmpiW( section
->name
, section_name
))
917 for (key
= section
->key
; key
; key
= key
->next
)
920 if (!*key
->name
) continue; /* Skip empty lines */
921 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
922 if (!return_values
&& !key
->value
) continue; /* Skip lines w.o. '=' */
923 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
924 len
-= strlenW(buffer
) + 1;
925 buffer
+= strlenW(buffer
) + 1;
928 if (return_values
&& key
->value
) {
930 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
931 len
-= strlenW(buffer
) + 1;
932 buffer
+= strlenW(buffer
) + 1;
937 /*If either lpszSection or lpszKey is NULL and the supplied
938 destination buffer is too small to hold all the strings,
939 the last string is truncated and followed by two null characters.
940 In this case, the return value is equal to cchReturnBuffer
948 section
= section
->next
;
950 buffer
[0] = buffer
[1] = '\0';
954 /* See GetPrivateProfileSectionNamesA for documentation */
956 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, DWORD len
)
958 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
963 PROFILESECTION
*section
;
965 TRACE("(%p, %d)\n", buffer
, len
);
976 section
= CurProfile
->section
;
977 while ((section
!=NULL
)) {
978 if (section
->name
[0]) {
979 tmplen
= strlenW(section
->name
)+1;
980 if (tmplen
>= buflen
) {
982 memcpy(buf
, section
->name
, (buflen
-1) * sizeof(WCHAR
));
989 memcpy(buf
, section
->name
, tmplen
* sizeof(WCHAR
));
993 section
= section
->next
;
1000 /***********************************************************************
1003 * Get a profile string.
1005 * Tests with GetPrivateProfileString16, W95a,
1006 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
1007 * section key_name def_val res buffer
1008 * "set1" "1" "x" 43 [data]
1009 * "set1" "1 " "x" 43 [data] (!)
1010 * "set1" " 1 "' "x" 43 [data] (!)
1011 * "set1" "" "x" 1 "x"
1012 * "set1" "" "x " 1 "x" (!)
1013 * "set1" "" " x " 3 " x" (!)
1014 * "set1" NULL "x" 6 "1\02\03\0\0"
1015 * "set1" "" "x" 1 "x"
1016 * NULL "1" "x" 0 "" (!)
1022 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
1024 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
1026 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1029 PROFILEKEY
*key
= NULL
;
1030 static const WCHAR empty_strW
[] = { 0 };
1032 if(!buffer
|| !len
) return 0;
1034 if (!def_val
) def_val
= empty_strW
;
1037 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
1038 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
1040 TRACE("(%s,%s,%s): returning %s\n",
1041 debugstr_w(section
), debugstr_w(key_name
),
1042 debugstr_w(def_val
), debugstr_w(buffer
) );
1043 return strlenW( buffer
);
1045 /* no "else" here ! */
1048 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
);
1049 if (!buffer
[0]) /* no luck -> def_val */
1051 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
1052 ret
= strlenW(buffer
);
1061 /***********************************************************************
1064 * Set a profile string.
1066 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
1067 LPCWSTR value
, BOOL create_always
)
1069 if (!key_name
) /* Delete a whole section */
1071 TRACE("(%s)\n", debugstr_w(section_name
));
1072 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
1074 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
1075 this is not an error on application's level.*/
1077 else if (!value
) /* Delete a key */
1079 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
1080 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
1081 section_name
, key_name
);
1082 return TRUE
; /* same error handling as above */
1084 else /* Set the key value */
1086 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
1087 key_name
, TRUE
, create_always
);
1088 TRACE("(%s,%s,%s):\n",
1089 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
1090 if (!key
) return FALSE
;
1092 /* strip the leading spaces. We can safely strip \n\r and
1093 * friends too, they should not happen here anyway. */
1094 while (PROFILE_isspaceW(*value
)) value
++;
1098 if (!strcmpW( key
->value
, value
))
1100 TRACE(" no change needed\n" );
1101 return TRUE
; /* No change needed */
1103 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
1104 HeapFree( GetProcessHeap(), 0, key
->value
);
1106 else TRACE(" creating key\n" );
1107 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
1108 strcpyW( key
->value
, value
);
1109 CurProfile
->changed
= TRUE
;
1115 /********************* API functions **********************************/
1118 /***********************************************************************
1119 * GetProfileIntA (KERNEL32.@)
1121 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1123 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1126 /***********************************************************************
1127 * GetProfileIntW (KERNEL32.@)
1129 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1131 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1134 /***********************************************************************
1135 * GetPrivateProfileStringW (KERNEL32.@)
1138 DWORD WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1139 LPCWSTR def_val
, LPWSTR buffer
,
1140 DWORD len
, LPCWSTR filename
)
1142 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1143 LPCWSTR def_val
, LPWSTR buffer
,
1144 UINT len
, LPCWSTR filename
)
1148 LPWSTR defval_tmp
= NULL
;
1150 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1151 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1153 /* strip any trailing ' ' of def_val. */
1156 LPCWSTR p
= def_val
+ strlenW(def_val
) - 1;
1158 while (p
> def_val
&& *p
== ' ')
1163 int vlen
= (int)(p
- def_val
) + 1;
1165 defval_tmp
= HeapAlloc(GetProcessHeap(), 0, (vlen
+ 1) * sizeof(WCHAR
));
1166 memcpy(defval_tmp
, def_val
, vlen
* sizeof(WCHAR
));
1167 defval_tmp
[vlen
] = '\0';
1168 def_val
= defval_tmp
;
1172 RtlEnterCriticalSection( &PROFILE_CritSect
);
1174 if (PROFILE_Open( filename
, FALSE
)) {
1175 if (section
== NULL
)
1176 ret
= PROFILE_GetSectionNames(buffer
, len
);
1178 /* PROFILE_GetString can handle the 'entry == NULL' case */
1179 ret
= PROFILE_GetString( section
, entry
, def_val
, buffer
, len
);
1180 } else if (buffer
&& def_val
) {
1181 lstrcpynW( buffer
, def_val
, len
);
1182 ret
= strlenW( buffer
);
1187 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1189 HeapFree(GetProcessHeap(), 0, defval_tmp
);
1191 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1196 /***********************************************************************
1197 * GetPrivateProfileStringA (KERNEL32.@)
1200 DWORD WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1201 LPCSTR def_val
, LPSTR buffer
,
1202 DWORD len
, LPCSTR filename
)
1204 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1205 LPCSTR def_val
, LPSTR buffer
,
1206 UINT len
, LPCSTR filename
)
1209 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1213 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1214 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1215 else sectionW
.Buffer
= NULL
;
1216 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1217 else entryW
.Buffer
= NULL
;
1218 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1219 else def_valW
.Buffer
= NULL
;
1220 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1221 else filenameW
.Buffer
= NULL
;
1223 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1224 def_valW
.Buffer
, bufferW
, len
,
1230 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, len
- 1, NULL
, NULL
);
1237 RtlFreeUnicodeString(§ionW
);
1238 RtlFreeUnicodeString(&entryW
);
1239 RtlFreeUnicodeString(&def_valW
);
1240 RtlFreeUnicodeString(&filenameW
);
1241 HeapFree(GetProcessHeap(), 0, bufferW
);
1245 /***********************************************************************
1246 * GetProfileStringA (KERNEL32.@)
1249 DWORD WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1250 LPSTR buffer
, DWORD len
)
1252 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1253 LPSTR buffer
, UINT len
)
1256 return GetPrivateProfileStringA( section
, entry
, def_val
,
1257 buffer
, len
, "win.ini" );
1260 /***********************************************************************
1261 * GetProfileStringW (KERNEL32.@)
1264 DWORD WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1265 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
1267 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1268 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1271 return GetPrivateProfileStringW( section
, entry
, def_val
,
1272 buffer
, len
, wininiW
);
1275 /***********************************************************************
1276 * WriteProfileStringA (KERNEL32.@)
1278 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1281 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1284 /***********************************************************************
1285 * WriteProfileStringW (KERNEL32.@)
1287 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1290 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1294 /***********************************************************************
1295 * GetPrivateProfileIntW (KERNEL32.@)
1297 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1298 INT def_val
, LPCWSTR filename
)
1301 UNICODE_STRING bufferW
;
1304 if (GetPrivateProfileStringW( section
, entry
, emptystringW
,
1305 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1309 /* FIXME: if entry can be found but it's empty, then Win16 is
1310 * supposed to return 0 instead of def_val ! Difficult/problematic
1311 * to implement (every other failure also returns zero buffer),
1312 * thus wait until testing framework avail for making sure nothing
1313 * else gets broken that way. */
1314 if (!buffer
[0]) return (UINT
)def_val
;
1316 RtlInitUnicodeString( &bufferW
, buffer
);
1317 RtlUnicodeStringToInteger( &bufferW
, 0, &result
);
1321 /***********************************************************************
1322 * GetPrivateProfileIntA (KERNEL32.@)
1324 * FIXME: rewrite using unicode
1326 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1327 INT def_val
, LPCSTR filename
)
1329 UNICODE_STRING entryW
, filenameW
, sectionW
;
1331 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1332 else entryW
.Buffer
= NULL
;
1333 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1334 else filenameW
.Buffer
= NULL
;
1335 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1336 else sectionW
.Buffer
= NULL
;
1337 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1339 RtlFreeUnicodeString(§ionW
);
1340 RtlFreeUnicodeString(&filenameW
);
1341 RtlFreeUnicodeString(&entryW
);
1345 /***********************************************************************
1346 * GetPrivateProfileSectionW (KERNEL32.@)
1349 DWORD WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1351 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1353 DWORD len
, LPCWSTR filename
)
1357 if (!section
|| !buffer
)
1359 SetLastError(ERROR_INVALID_PARAMETER
);
1363 TRACE("(%s, %p, %d, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1365 RtlEnterCriticalSection( &PROFILE_CritSect
);
1367 if (PROFILE_Open( filename
, FALSE
))
1368 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1370 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1375 /***********************************************************************
1376 * GetPrivateProfileSectionA (KERNEL32.@)
1379 DWORD WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1381 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1383 DWORD len
, LPCSTR filename
)
1385 UNICODE_STRING sectionW
, filenameW
;
1389 if (!section
|| !buffer
)
1391 SetLastError(ERROR_INVALID_PARAMETER
);
1395 bufferW
= HeapAlloc(GetProcessHeap(), 0, len
* 2 * sizeof(WCHAR
));
1396 RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1397 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1398 else filenameW
.Buffer
= NULL
;
1400 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
* 2, filenameW
.Buffer
);
1403 if (retW
== len
* 2 - 2) retW
++; /* overflow */
1404 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1405 if (!ret
|| ret
== len
) /* overflow */
1419 RtlFreeUnicodeString(§ionW
);
1420 RtlFreeUnicodeString(&filenameW
);
1421 HeapFree(GetProcessHeap(), 0, bufferW
);
1425 /***********************************************************************
1426 * GetProfileSectionA (KERNEL32.@)
1429 DWORD WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1431 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1434 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1437 /***********************************************************************
1438 * GetProfileSectionW (KERNEL32.@)
1441 DWORD WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1443 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1446 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1450 /***********************************************************************
1451 * WritePrivateProfileStringW (KERNEL32.@)
1453 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1454 LPCWSTR string
, LPCWSTR filename
)
1458 RtlEnterCriticalSection( &PROFILE_CritSect
);
1460 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1462 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1464 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1467 else if (PROFILE_Open( filename
, TRUE
))
1470 SetLastError(ERROR_FILE_NOT_FOUND
);
1472 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1473 if (ret
) ret
= PROFILE_FlushFile();
1477 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1481 /***********************************************************************
1482 * WritePrivateProfileStringA (KERNEL32.@)
1484 BOOL WINAPI DECLSPEC_HOTPATCH
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1485 LPCSTR string
, LPCSTR filename
)
1487 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1490 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1491 else sectionW
.Buffer
= NULL
;
1492 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1493 else entryW
.Buffer
= NULL
;
1494 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1495 else stringW
.Buffer
= NULL
;
1496 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1497 else filenameW
.Buffer
= NULL
;
1499 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1500 stringW
.Buffer
, filenameW
.Buffer
);
1501 RtlFreeUnicodeString(§ionW
);
1502 RtlFreeUnicodeString(&entryW
);
1503 RtlFreeUnicodeString(&stringW
);
1504 RtlFreeUnicodeString(&filenameW
);
1508 /***********************************************************************
1509 * WritePrivateProfileSectionW (KERNEL32.@)
1511 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1512 LPCWSTR string
, LPCWSTR filename
)
1517 RtlEnterCriticalSection( &PROFILE_CritSect
);
1519 if (!section
&& !string
)
1521 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1523 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1526 else if (PROFILE_Open( filename
, TRUE
)) {
1527 if (!string
) {/* delete the named section*/
1528 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1530 PROFILE_DeleteAllKeys(section
);
1532 while(*string
&& ret
) {
1533 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1534 strcpyW( buf
, string
);
1535 if((p
= strchrW( buf
, '='))) {
1537 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1539 HeapFree( GetProcessHeap(), 0, buf
);
1540 string
+= strlenW(string
)+1;
1543 if (ret
) ret
= PROFILE_FlushFile();
1546 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1550 /***********************************************************************
1551 * WritePrivateProfileSectionA (KERNEL32.@)
1553 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1554 LPCSTR string
, LPCSTR filename
)
1557 UNICODE_STRING sectionW
, filenameW
;
1566 while(*p
) p
+= strlen(p
) + 1;
1567 lenA
= p
- string
+ 1;
1568 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1569 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1570 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1572 else stringW
= NULL
;
1573 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1574 else sectionW
.Buffer
= NULL
;
1575 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1576 else filenameW
.Buffer
= NULL
;
1578 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1580 HeapFree(GetProcessHeap(), 0, stringW
);
1581 RtlFreeUnicodeString(§ionW
);
1582 RtlFreeUnicodeString(&filenameW
);
1586 /***********************************************************************
1587 * WriteProfileSectionA (KERNEL32.@)
1589 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1592 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1595 /***********************************************************************
1596 * WriteProfileSectionW (KERNEL32.@)
1598 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1600 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1604 /***********************************************************************
1605 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1607 * Returns the section names contained in the specified file.
1608 * FIXME: Where do we find this file when the path is relative?
1609 * The section names are returned as a list of strings with an extra
1610 * '\0' to mark the end of the list. Except for that the behavior
1611 * depends on the Windows version.
1614 * - if the buffer is 0 or 1 character long then it is as if it was of
1616 * - otherwise, if the buffer is too small only the section names that fit
1618 * - note that this means if the buffer was too small to return even just
1619 * the first section name then a single '\0' will be returned.
1620 * - the return value is the number of characters written in the buffer,
1621 * except if the buffer was too small in which case len-2 is returned
1624 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1625 * '\0' and the return value is 0
1626 * - otherwise if the buffer is too small then the first section name that
1627 * does not fit is truncated so that the string list can be terminated
1628 * correctly (double '\0')
1629 * - the return value is the number of characters written in the buffer
1630 * except for the trailing '\0'. If the buffer is too small, then the
1631 * return value is len-2
1632 * - Win2000 has a bug that triggers when the section names and the
1633 * trailing '\0' fit exactly in the buffer. In that case the trailing
1636 * Wine implements the observed Win2000 behavior (except for the bug).
1638 * Note that when the buffer is big enough then the return value may be any
1639 * value between 1 and len-1 (or len in Win95), including len-2.
1641 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1646 RtlEnterCriticalSection( &PROFILE_CritSect
);
1648 if (PROFILE_Open( filename
, FALSE
))
1649 ret
= PROFILE_GetSectionNames(buffer
, size
);
1651 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1657 /***********************************************************************
1658 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1660 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1663 UNICODE_STRING filenameW
;
1667 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1668 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1669 else filenameW
.Buffer
= NULL
;
1671 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1674 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+1, buffer
, size
-1, NULL
, NULL
);
1686 RtlFreeUnicodeString(&filenameW
);
1687 HeapFree(GetProcessHeap(), 0, bufferW
);
1691 /***********************************************************************
1692 * GetPrivateProfileStructW (KERNEL32.@)
1694 * Should match Win95's behaviour pretty much
1696 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1697 LPVOID buf
, UINT len
, LPCWSTR filename
)
1701 RtlEnterCriticalSection( &PROFILE_CritSect
);
1703 if (PROFILE_Open( filename
, FALSE
)) {
1704 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1706 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1707 if (((strlenW(k
->value
) - 2) / 2) == len
)
1714 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1715 /* check for invalid chars in ASCII coded hex string */
1716 for (p
=k
->value
; p
< end
; p
++)
1720 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1721 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1728 BOOL highnibble
= TRUE
;
1730 LPBYTE binbuf
= buf
;
1732 end
-= 2; /* don't include checksum in output data */
1733 /* translate ASCII hex format into binary data */
1734 for (p
=k
->value
; p
< end
; p
++)
1738 (c
- 'A' + 10) : (c
- '0');
1745 *binbuf
++ = b
; /* feed binary data into output */
1746 chksum
+= b
; /* calculate checksum */
1748 highnibble
^= 1; /* toggle */
1750 /* retrieve stored checksum value */
1752 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1754 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1755 if (b
== (chksum
& 0xff)) /* checksums match ? */
1761 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1766 /***********************************************************************
1767 * GetPrivateProfileStructA (KERNEL32.@)
1769 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1770 LPVOID buffer
, UINT len
, LPCSTR filename
)
1772 UNICODE_STRING sectionW
, keyW
, filenameW
;
1775 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1776 else sectionW
.Buffer
= NULL
;
1777 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1778 else keyW
.Buffer
= NULL
;
1779 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1780 else filenameW
.Buffer
= NULL
;
1782 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1784 /* Do not translate binary data. */
1786 RtlFreeUnicodeString(§ionW
);
1787 RtlFreeUnicodeString(&keyW
);
1788 RtlFreeUnicodeString(&filenameW
);
1794 /***********************************************************************
1795 * WritePrivateProfileStructW (KERNEL32.@)
1797 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1798 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1802 LPWSTR outstring
, p
;
1805 if (!section
&& !key
&& !buf
) /* flush the cache */
1806 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1808 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1809 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1811 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1812 *p
++ = hex
[*binbuf
>> 4];
1813 *p
++ = hex
[*binbuf
& 0xf];
1816 /* checksum is sum & 0xff */
1817 *p
++ = hex
[(sum
& 0xf0) >> 4];
1818 *p
++ = hex
[sum
& 0xf];
1821 RtlEnterCriticalSection( &PROFILE_CritSect
);
1823 if (PROFILE_Open( filename
, TRUE
)) {
1824 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1825 if (ret
) ret
= PROFILE_FlushFile();
1828 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1830 HeapFree( GetProcessHeap(), 0, outstring
);
1835 /***********************************************************************
1836 * WritePrivateProfileStructA (KERNEL32.@)
1838 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1839 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1841 UNICODE_STRING sectionW
, keyW
, filenameW
;
1844 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1845 else sectionW
.Buffer
= NULL
;
1846 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1847 else keyW
.Buffer
= NULL
;
1848 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1849 else filenameW
.Buffer
= NULL
;
1851 /* Do not translate binary data. */
1852 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1855 RtlFreeUnicodeString(§ionW
);
1856 RtlFreeUnicodeString(&keyW
);
1857 RtlFreeUnicodeString(&filenameW
);
1862 /***********************************************************************
1863 * OpenProfileUserMapping (KERNEL32.@)
1865 BOOL WINAPI
OpenProfileUserMapping(void) {
1866 FIXME("(), stub!\n");
1867 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1871 /***********************************************************************
1872 * CloseProfileUserMapping (KERNEL32.@)
1874 BOOL WINAPI
CloseProfileUserMapping(void) {
1875 FIXME("(), stub!\n");
1876 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);