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
26 DEBUG_CHANNEL(profile
);
28 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
38 typedef struct tagPROFILEKEY
41 struct tagPROFILEKEY
*next
;
45 typedef struct tagPROFILESECTION
47 struct tagPROFILEKEY
*key
;
48 struct tagPROFILESECTION
*next
;
56 PROFILESECTION
*section
;
58 FILETIME LastWriteTime
;
63 #define N_CACHED_PROFILES 10
65 /* Cached profile files */
66 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
68 #define CurProfile (MRUProfile[0])
70 /* Check for comments in profile */
71 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
73 static const WCHAR emptystringW
[] = {0};
74 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
76 static RTL_CRITICAL_SECTION PROFILE_CritSect
;
77 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
79 0, 0, &PROFILE_CritSect
,
80 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
83 static RTL_CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
85 static const char hex
[16] = "0123456789ABCDEF";
87 /***********************************************************************
90 * Copy the content of an entry into a buffer, removing quotes, and possibly
91 * translating environment variables.
93 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
100 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
102 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
105 lstrcpynW( buffer
, value
, len
);
106 if (quote
&& (len
>= lstrlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
109 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
110 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
113 USHORT
* shortbuffer
= buffer
;
114 for (i
= 0; i
< len
; i
++)
115 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
118 /* writes any necessary encoding marker to the file */
119 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
121 DWORD dwBytesWritten
;
128 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
130 case ENCODING_UTF16LE
:
132 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
134 case ENCODING_UTF16BE
:
136 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
141 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
144 int write_buffer_len
;
145 DWORD dwBytesWritten
;
147 TRACE("writing: %s\n", debugstr_wn(szLine
, len
));
152 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
153 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
154 if (!write_buffer
) return;
155 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
156 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
157 HeapFree(GetProcessHeap(), 0, write_buffer
);
160 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
161 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
162 if (!write_buffer
) return;
163 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
164 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
165 HeapFree(GetProcessHeap(), 0, write_buffer
);
167 case ENCODING_UTF16LE
:
168 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
170 case ENCODING_UTF16BE
:
171 PROFILE_ByteSwapShortBuffer(szLine
, len
);
172 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
175 FIXME("encoding type %d not implemented\n", encoding
);
179 /***********************************************************************
182 * Save a profile tree to a file.
184 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
189 PROFILE_WriteMarker(hFile
, encoding
);
191 for ( ; section
; section
= section
->next
)
195 if (section
->name
[0]) len
+= strlenW(section
->name
) + 4;
197 for (key
= section
->key
; key
; key
= key
->next
)
199 len
+= strlenW(key
->name
) + 2;
200 if (key
->value
) len
+= strlenW(key
->value
) + 1;
203 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
207 if (section
->name
[0])
210 strcpyW( p
, section
->name
);
217 for (key
= section
->key
; key
; key
= key
->next
)
219 strcpyW( p
, key
->name
);
224 strcpyW( p
, key
->value
);
230 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
231 HeapFree(GetProcessHeap(), 0, buffer
);
236 /***********************************************************************
239 * Free a profile tree.
241 static void PROFILE_Free( PROFILESECTION
*section
)
243 PROFILESECTION
*next_section
;
244 PROFILEKEY
*key
, *next_key
;
246 for ( ; section
; section
= next_section
)
248 for (key
= section
->key
; key
; key
= next_key
)
250 next_key
= key
->next
;
251 HeapFree( GetProcessHeap(), 0, key
->value
);
252 HeapFree( GetProcessHeap(), 0, key
);
254 next_section
= section
->next
;
255 HeapFree( GetProcessHeap(), 0, section
);
259 /* returns 1 if a character white space else 0 */
260 static inline int PROFILE_isspaceW(WCHAR c
)
262 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
263 return isspaceW(c
) || c
== 0x1a;
266 static inline ENCODING
PROFILE_DetectTextEncoding(void * buffer
, int * len
)
268 int flags
= IS_TEXT_UNICODE_SIGNATURE
|
269 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
270 IS_TEXT_UNICODE_ODD_LENGTH
;
271 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
273 *len
= sizeof(bom_utf8
);
274 return ENCODING_UTF8
;
276 RtlIsTextUnicode(buffer
, *len
, &flags
);
277 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
279 *len
= sizeof(WCHAR
);
280 return ENCODING_UTF16LE
;
282 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
284 *len
= sizeof(WCHAR
);
285 return ENCODING_UTF16BE
;
288 return ENCODING_ANSI
;
292 /***********************************************************************
295 * Load a profile tree from a file.
297 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
299 void *buffer_base
, *pBuffer
;
301 const WCHAR
*szLineStart
, *szLineEnd
;
302 const WCHAR
*szValueStart
, *szEnd
, *next_line
;
304 PROFILESECTION
*section
, *first_section
;
305 PROFILESECTION
**next_section
;
306 PROFILEKEY
*key
, *prev_key
, **next_key
;
309 TRACE("%p\n", hFile
);
311 dwFileSize
= GetFileSize(hFile
, NULL
);
312 if (dwFileSize
== INVALID_FILE_SIZE
|| dwFileSize
== 0)
315 buffer_base
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
316 if (!buffer_base
) return NULL
;
318 if (!ReadFile(hFile
, buffer_base
, dwFileSize
, &dwFileSize
, NULL
))
320 HeapFree(GetProcessHeap(), 0, buffer_base
);
321 WARN("Error %d reading file\n", GetLastError());
325 *pEncoding
= PROFILE_DetectTextEncoding(buffer_base
, &len
);
326 /* len is set to the number of bytes in the character marker.
327 * we want to skip these bytes */
328 pBuffer
= (char *)buffer_base
+ len
;
333 TRACE("ANSI encoding\n");
335 len
= MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, NULL
, 0);
336 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
339 HeapFree(GetProcessHeap(), 0, buffer_base
);
342 MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, szFile
, len
);
343 szEnd
= szFile
+ len
;
346 TRACE("UTF8 encoding\n");
348 len
= MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, NULL
, 0);
349 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
352 HeapFree(GetProcessHeap(), 0, buffer_base
);
355 MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, szFile
, len
);
356 szEnd
= szFile
+ len
;
358 case ENCODING_UTF16LE
:
359 TRACE("UTF16 Little Endian encoding\n");
361 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
363 case ENCODING_UTF16BE
:
364 TRACE("UTF16 Big Endian encoding\n");
366 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
367 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
370 FIXME("encoding type %d not implemented\n", *pEncoding
);
371 HeapFree(GetProcessHeap(), 0, buffer_base
);
375 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
376 if(first_section
== NULL
)
378 if (szFile
!= pBuffer
)
379 HeapFree(GetProcessHeap(), 0, szFile
);
380 HeapFree(GetProcessHeap(), 0, buffer_base
);
383 first_section
->name
[0] = 0;
384 first_section
->key
= NULL
;
385 first_section
->next
= NULL
;
386 next_section
= &first_section
->next
;
387 next_key
= &first_section
->key
;
391 while (next_line
< szEnd
)
393 szLineStart
= next_line
;
394 next_line
= memchrW(szLineStart
, '\n', szEnd
- szLineStart
);
395 if (!next_line
) next_line
= memchrW(szLineStart
, '\r', szEnd
- szLineStart
);
396 if (!next_line
) next_line
= szEnd
;
398 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 const WCHAR
* szSectionEnd
;
411 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
413 WARN("Invalid section header at line %d: %s\n",
414 line
, debugstr_wn(szLineStart
, (int)(szLineEnd
- szLineStart
)) );
419 len
= (int)(szSectionEnd
- 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 if ((szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
444 const WCHAR
*szNameEnd
= szValueStart
;
445 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(szNameEnd
[-1])) szNameEnd
--;
446 len
= szNameEnd
- szLineStart
;
448 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
451 if (len
|| !prev_key
|| *prev_key
->name
)
453 /* no need to allocate +1 for NULL terminating character as
454 * already included in structure */
455 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
456 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
457 key
->name
[len
] = '\0';
460 len
= (int)(szLineEnd
- szValueStart
);
461 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
462 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
463 key
->value
[len
] = '\0';
465 else key
->value
= NULL
;
469 next_key
= &key
->next
;
472 TRACE("New key: name=%s, value=%s\n",
473 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : L
"(none)");
476 if (szFile
!= pBuffer
)
477 HeapFree(GetProcessHeap(), 0, szFile
);
478 HeapFree(GetProcessHeap(), 0, buffer_base
);
479 return first_section
;
483 /***********************************************************************
484 * PROFILE_DeleteSection
486 * Delete a section from a profile tree.
488 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
492 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
494 PROFILESECTION
*to_del
= *section
;
495 *section
= to_del
->next
;
497 PROFILE_Free( to_del
);
500 section
= &(*section
)->next
;
506 /***********************************************************************
509 * Delete a key from a profile tree.
511 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
512 LPCWSTR section_name
, LPCWSTR key_name
)
516 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
518 PROFILEKEY
**key
= &(*section
)->key
;
521 if (!strcmpiW( (*key
)->name
, key_name
))
523 PROFILEKEY
*to_del
= *key
;
525 HeapFree( GetProcessHeap(), 0, to_del
->value
);
526 HeapFree( GetProcessHeap(), 0, to_del
);
532 section
= &(*section
)->next
;
538 /***********************************************************************
539 * PROFILE_DeleteAllKeys
541 * Delete all keys from a profile tree.
543 static void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
545 PROFILESECTION
**section
= &CurProfile
->section
;
548 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
550 PROFILEKEY
**key
= &(*section
)->key
;
553 PROFILEKEY
*to_del
= *key
;
555 HeapFree( GetProcessHeap(), 0, to_del
->value
);
556 HeapFree( GetProcessHeap(), 0, to_del
);
557 CurProfile
->changed
=TRUE
;
560 section
= &(*section
)->next
;
565 /***********************************************************************
568 * Find a key in a profile tree, optionally creating it.
570 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
571 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
576 while (PROFILE_isspaceW(*section_name
)) section_name
++;
578 p
= section_name
+ strlenW(section_name
) - 1;
582 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
583 seclen
= p
- section_name
+ 1;
585 while (PROFILE_isspaceW(*key_name
)) key_name
++;
587 p
= key_name
+ strlenW(key_name
) - 1;
591 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
592 keylen
= p
- key_name
+ 1;
596 if ( ((*section
)->name
[0])
597 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
598 && (((*section
)->name
)[seclen
] == '\0') )
600 PROFILEKEY
**key
= &(*section
)->key
;
604 /* If create_always is FALSE then we check if the keyname
605 * already exists. Otherwise we add it regardless of its
606 * existence, to allow keys to be added more than once in
611 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
612 && (((*key
)->name
)[keylen
] == '\0') )
617 if (!create
) return NULL
;
618 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
620 strcpyW( (*key
)->name
, key_name
);
621 (*key
)->value
= NULL
;
625 section
= &(*section
)->next
;
627 if (!create
) return NULL
;
628 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
629 if(*section
== NULL
) return NULL
;
630 strcpyW( (*section
)->name
, section_name
);
631 (*section
)->next
= NULL
;
632 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
633 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
635 HeapFree(GetProcessHeap(), 0, *section
);
638 strcpyW( (*section
)->key
->name
, key_name
);
639 (*section
)->key
->value
= NULL
;
640 (*section
)->key
->next
= NULL
;
641 return (*section
)->key
;
645 /***********************************************************************
648 * Flush the current profile to disk if changed.
650 static BOOL
PROFILE_FlushFile(void)
653 FILETIME LastWriteTime
;
657 WARN("No current profile!\n");
661 if (!CurProfile
->changed
) return TRUE
;
663 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
664 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
666 if (hFile
== INVALID_HANDLE_VALUE
)
668 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile
->filename
), GetLastError());
672 TRACE("Saving %s\n", debugstr_w(CurProfile
->filename
));
673 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
674 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
675 CurProfile
->LastWriteTime
=LastWriteTime
;
676 CloseHandle( hFile
);
677 CurProfile
->changed
= FALSE
;
682 /***********************************************************************
683 * PROFILE_ReleaseFile
685 * Flush the current profile to disk and remove it from the cache.
687 static void PROFILE_ReleaseFile(void)
690 PROFILE_Free( CurProfile
->section
);
691 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
692 CurProfile
->changed
= FALSE
;
693 CurProfile
->section
= NULL
;
694 CurProfile
->filename
= NULL
;
695 CurProfile
->encoding
= ENCODING_ANSI
;
696 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
699 /***********************************************************************
701 * Compares a file time with the current time. If the file time is
702 * at least 2.1 seconds in the past, return true.
704 * Intended as cache safety measure: The time resolution on FAT is
705 * two seconds, so files that are not at least two seconds old might
706 * keep their time even on modification, so don't cache them.
708 static BOOL
is_not_current(FILETIME
* ft
)
711 LONGLONG ftll
, nowll
;
712 GetSystemTimeAsFileTime(&Now
);
713 ftll
= ((LONGLONG
)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
;
714 nowll
= ((LONGLONG
)Now
.dwHighDateTime
<< 32) + Now
.dwLowDateTime
;
715 TRACE("%08x;%08x\n",(unsigned)ftll
+21000000,(unsigned)nowll
);
716 return ftll
+ 21000000 < nowll
;
719 /***********************************************************************
722 * Open a profile file, checking the cached file first.
724 static BOOL
PROFILE_Open( LPCWSTR filename
, BOOL write_access
)
726 WCHAR buffer
[MAX_PATH
];
727 HANDLE hFile
= INVALID_HANDLE_VALUE
;
728 FILETIME LastWriteTime
;
730 PROFILE
*tempProfile
;
732 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
734 /* First time around */
737 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
739 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
740 if(MRUProfile
[i
] == NULL
) break;
741 MRUProfile
[i
]->changed
=FALSE
;
742 MRUProfile
[i
]->section
=NULL
;
743 MRUProfile
[i
]->filename
=NULL
;
744 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
745 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
751 if ((RtlDetermineDosPathNameType_U(filename
) == RtlPathTypeRelative
) &&
752 !strchrW(filename
, '\\') && !strchrW(filename
, '/'))
754 static const WCHAR wszSeparator
[] = {'\\', 0};
755 WCHAR windirW
[MAX_PATH
];
756 GetWindowsDirectoryW( windirW
, MAX_PATH
);
757 strcpyW(buffer
, windirW
);
758 strcatW(buffer
, wszSeparator
);
759 strcatW(buffer
, filename
);
764 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
767 TRACE("path: %s\n", debugstr_w(buffer
));
769 hFile
= CreateFileW(buffer
, GENERIC_READ
| (write_access
? GENERIC_WRITE
: 0),
770 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
771 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
773 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
775 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer
));
779 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
781 if ((MRUProfile
[i
]->filename
&& !strcmpiW( buffer
, MRUProfile
[i
]->filename
)))
783 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile
[i
]->filename
), debugstr_w(buffer
));
787 tempProfile
=MRUProfile
[i
];
789 MRUProfile
[j
]=MRUProfile
[j
-1];
790 CurProfile
=tempProfile
;
793 if (hFile
!= INVALID_HANDLE_VALUE
)
795 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
796 if (!memcmp( &CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
) ) &&
797 is_not_current(&LastWriteTime
))
798 TRACE("(%s): already opened (mru=%d)\n",
799 debugstr_w(buffer
), i
);
802 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
803 debugstr_w(buffer
), i
);
804 PROFILE_Free(CurProfile
->section
);
805 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
806 CurProfile
->LastWriteTime
= LastWriteTime
;
811 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
812 debugstr_w(buffer
), i
);
816 /* Flush the old current profile */
819 /* Make the oldest profile the current one only in order to get rid of it */
820 if(i
==N_CACHED_PROFILES
)
822 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
823 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
824 MRUProfile
[i
]=MRUProfile
[i
-1];
825 CurProfile
=tempProfile
;
827 if(CurProfile
->filename
) PROFILE_ReleaseFile();
829 /* OK, now that CurProfile is definitely free we assign it our new file */
830 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer
)+1) * sizeof(WCHAR
) );
831 strcpyW( CurProfile
->filename
, buffer
);
833 if (hFile
!= INVALID_HANDLE_VALUE
)
835 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
836 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
841 /* Does not exist yet, we will create it in PROFILE_FlushFile */
842 WARN("profile file %s not found\n", debugstr_w(buffer
) );
848 /***********************************************************************
851 * Returns all keys of a section.
852 * If return_values is TRUE, also include the corresponding values.
854 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
855 LPWSTR buffer
, DWORD len
, BOOL return_values
)
859 if(!buffer
) return 0;
861 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
865 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
868 for (key
= section
->key
; key
; key
= key
->next
)
871 if (!*key
->name
) continue; /* Skip empty lines */
872 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
873 if (!return_values
&& !key
->value
) continue; /* Skip lines w.o. '=' */
874 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
875 len
-= strlenW(buffer
) + 1;
876 buffer
+= strlenW(buffer
) + 1;
879 if (return_values
&& key
->value
) {
881 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
882 len
-= strlenW(buffer
) + 1;
883 buffer
+= strlenW(buffer
) + 1;
888 /*If either lpszSection or lpszKey is NULL and the supplied
889 destination buffer is too small to hold all the strings,
890 the last string is truncated and followed by two null characters.
891 In this case, the return value is equal to cchReturnBuffer
899 section
= section
->next
;
901 buffer
[0] = buffer
[1] = '\0';
905 /* See GetPrivateProfileSectionNamesA for documentation */
906 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, DWORD len
)
910 PROFILESECTION
*section
;
912 TRACE("(%p, %d)\n", buffer
, len
);
923 section
= CurProfile
->section
;
924 while ((section
!=NULL
)) {
925 if (section
->name
[0]) {
926 tmplen
= strlenW(section
->name
)+1;
927 if (tmplen
>= buflen
) {
929 memcpy(buf
, section
->name
, (buflen
-1) * sizeof(WCHAR
));
936 memcpy(buf
, section
->name
, tmplen
* sizeof(WCHAR
));
940 section
= section
->next
;
947 /***********************************************************************
950 * Get a profile string.
952 * Tests with GetPrivateProfileString16, W95a,
953 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
954 * section key_name def_val res buffer
955 * "set1" "1" "x" 43 [data]
956 * "set1" "1 " "x" 43 [data] (!)
957 * "set1" " 1 "' "x" 43 [data] (!)
958 * "set1" "" "x" 1 "x"
959 * "set1" "" "x " 1 "x" (!)
960 * "set1" "" " x " 3 " x" (!)
961 * "set1" NULL "x" 6 "1\02\03\0\0"
962 * "set1" "" "x" 1 "x"
963 * NULL "1" "x" 0 "" (!)
969 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
970 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
972 PROFILEKEY
*key
= NULL
;
973 static const WCHAR empty_strW
[] = { 0 };
975 if(!buffer
|| !len
) return 0;
977 if (!def_val
) def_val
= empty_strW
;
982 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
983 return strlenW(buffer
);
985 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
986 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
988 TRACE("(%s,%s,%s): returning %s\n",
989 debugstr_w(section
), debugstr_w(key_name
),
990 debugstr_w(def_val
), debugstr_w(buffer
) );
991 return strlenW( buffer
);
993 /* no "else" here ! */
994 if (section
&& section
[0])
996 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
);
997 if (!buffer
[0]) /* no luck -> def_val */
999 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
1000 ret
= strlenW(buffer
);
1009 /***********************************************************************
1012 * Set a profile string.
1014 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
1015 LPCWSTR value
, BOOL create_always
)
1017 if (!key_name
) /* Delete a whole section */
1019 TRACE("(%s)\n", debugstr_w(section_name
));
1020 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
1022 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
1023 this is not an error on application's level.*/
1025 else if (!value
) /* Delete a key */
1027 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
1028 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
1029 section_name
, key_name
);
1030 return TRUE
; /* same error handling as above */
1032 else /* Set the key value */
1034 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
1035 key_name
, TRUE
, create_always
);
1036 TRACE("(%s,%s,%s):\n",
1037 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
1038 if (!key
) return FALSE
;
1040 /* strip the leading spaces. We can safely strip \n\r and
1041 * friends too, they should not happen here anyway. */
1042 while (PROFILE_isspaceW(*value
)) value
++;
1046 if (!strcmpW( key
->value
, value
))
1048 TRACE(" no change needed\n" );
1049 return TRUE
; /* No change needed */
1051 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
1052 HeapFree( GetProcessHeap(), 0, key
->value
);
1054 else TRACE(" creating key\n" );
1055 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
1056 strcpyW( key
->value
, value
);
1057 CurProfile
->changed
= TRUE
;
1063 /********************* API functions **********************************/
1066 /***********************************************************************
1067 * GetProfileIntA (KERNEL32.@)
1069 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1071 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1074 /***********************************************************************
1075 * GetProfileIntW (KERNEL32.@)
1077 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1079 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1082 /***********************************************************************
1083 * GetPrivateProfileStringW (KERNEL32.@)
1085 DWORD WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1086 LPCWSTR def_val
, LPWSTR buffer
,
1087 DWORD len
, LPCWSTR filename
)
1090 LPWSTR defval_tmp
= NULL
;
1092 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1093 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1095 /* strip any trailing ' ' of def_val. */
1098 LPCWSTR p
= def_val
+ strlenW(def_val
) - 1;
1100 while (p
> def_val
&& *p
== ' ')
1105 int len
= (int)(p
- def_val
) + 1;
1107 defval_tmp
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1108 memcpy(defval_tmp
, def_val
, len
* sizeof(WCHAR
));
1109 defval_tmp
[len
] = '\0';
1110 def_val
= defval_tmp
;
1114 RtlEnterCriticalSection( &PROFILE_CritSect
);
1116 if (PROFILE_Open( filename
, FALSE
)) {
1117 if (section
== NULL
)
1118 ret
= PROFILE_GetSectionNames(buffer
, len
);
1120 /* PROFILE_GetString can handle the 'entry == NULL' case */
1121 ret
= PROFILE_GetString( section
, entry
, def_val
, buffer
, len
);
1122 } else if (buffer
&& def_val
) {
1123 lstrcpynW( buffer
, def_val
, len
);
1124 ret
= strlenW( buffer
);
1129 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1131 HeapFree(GetProcessHeap(), 0, defval_tmp
);
1133 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1138 /***********************************************************************
1139 * GetPrivateProfileStringA (KERNEL32.@)
1141 DWORD WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1142 LPCSTR def_val
, LPSTR buffer
,
1143 DWORD len
, LPCSTR filename
)
1145 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1149 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1150 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1151 else sectionW
.Buffer
= NULL
;
1152 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1153 else entryW
.Buffer
= NULL
;
1154 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1155 else def_valW
.Buffer
= NULL
;
1156 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1157 else filenameW
.Buffer
= NULL
;
1159 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1160 def_valW
.Buffer
, bufferW
, len
,
1166 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, len
- 1, NULL
, NULL
);
1173 RtlFreeUnicodeString(§ionW
);
1174 RtlFreeUnicodeString(&entryW
);
1175 RtlFreeUnicodeString(&def_valW
);
1176 RtlFreeUnicodeString(&filenameW
);
1177 HeapFree(GetProcessHeap(), 0, bufferW
);
1181 /***********************************************************************
1182 * GetProfileStringA (KERNEL32.@)
1184 DWORD WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1185 LPSTR buffer
, DWORD len
)
1187 return GetPrivateProfileStringA( section
, entry
, def_val
,
1188 buffer
, len
, "win.ini" );
1191 /***********************************************************************
1192 * GetProfileStringW (KERNEL32.@)
1194 DWORD WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1195 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
1197 return GetPrivateProfileStringW( section
, entry
, def_val
,
1198 buffer
, len
, wininiW
);
1201 /***********************************************************************
1202 * WriteProfileStringA (KERNEL32.@)
1204 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1207 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1210 /***********************************************************************
1211 * WriteProfileStringW (KERNEL32.@)
1213 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1216 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1220 /***********************************************************************
1221 * GetPrivateProfileIntW (KERNEL32.@)
1223 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1224 INT def_val
, LPCWSTR filename
)
1227 UNICODE_STRING bufferW
;
1230 if (GetPrivateProfileStringW( section
, entry
, emptystringW
,
1231 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1235 /* FIXME: if entry can be found but it's empty, then Win16 is
1236 * supposed to return 0 instead of def_val ! Difficult/problematic
1237 * to implement (every other failure also returns zero buffer),
1238 * thus wait until testing framework avail for making sure nothing
1239 * else gets broken that way. */
1240 if (!buffer
[0]) return (UINT
)def_val
;
1242 RtlInitUnicodeString( &bufferW
, buffer
);
1243 RtlUnicodeStringToInteger( &bufferW
, 0, &result
);
1247 /***********************************************************************
1248 * GetPrivateProfileIntA (KERNEL32.@)
1250 * FIXME: rewrite using unicode
1252 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1253 INT def_val
, LPCSTR filename
)
1255 UNICODE_STRING entryW
, filenameW
, sectionW
;
1257 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1258 else entryW
.Buffer
= NULL
;
1259 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1260 else filenameW
.Buffer
= NULL
;
1261 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1262 else sectionW
.Buffer
= NULL
;
1263 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1265 RtlFreeUnicodeString(§ionW
);
1266 RtlFreeUnicodeString(&filenameW
);
1267 RtlFreeUnicodeString(&entryW
);
1271 /***********************************************************************
1272 * GetPrivateProfileSectionW (KERNEL32.@)
1274 DWORD WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1275 DWORD len
, LPCWSTR filename
)
1279 if (!section
|| !buffer
)
1281 SetLastError(ERROR_INVALID_PARAMETER
);
1285 TRACE("(%s, %p, %d, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1287 RtlEnterCriticalSection( &PROFILE_CritSect
);
1289 if (PROFILE_Open( filename
, FALSE
))
1290 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1292 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1297 /***********************************************************************
1298 * GetPrivateProfileSectionA (KERNEL32.@)
1300 DWORD WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1301 DWORD len
, LPCSTR filename
)
1303 UNICODE_STRING sectionW
, filenameW
;
1307 if (!section
|| !buffer
)
1309 SetLastError(ERROR_INVALID_PARAMETER
);
1313 bufferW
= HeapAlloc(GetProcessHeap(), 0, len
* 2 * sizeof(WCHAR
));
1314 RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1315 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1316 else filenameW
.Buffer
= NULL
;
1318 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
* 2, filenameW
.Buffer
);
1321 if (retW
== len
* 2 - 2) retW
++; /* overflow */
1322 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1323 if (!ret
|| ret
== len
) /* overflow */
1337 RtlFreeUnicodeString(§ionW
);
1338 RtlFreeUnicodeString(&filenameW
);
1339 HeapFree(GetProcessHeap(), 0, bufferW
);
1343 /***********************************************************************
1344 * GetProfileSectionA (KERNEL32.@)
1346 DWORD WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1348 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1351 /***********************************************************************
1352 * GetProfileSectionW (KERNEL32.@)
1354 DWORD WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1356 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1360 /***********************************************************************
1361 * WritePrivateProfileStringW (KERNEL32.@)
1363 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1364 LPCWSTR string
, LPCWSTR filename
)
1368 RtlEnterCriticalSection( &PROFILE_CritSect
);
1370 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1372 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1374 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1377 else if (PROFILE_Open( filename
, TRUE
))
1380 SetLastError(ERROR_FILE_NOT_FOUND
);
1382 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1383 PROFILE_FlushFile();
1387 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1391 /***********************************************************************
1392 * WritePrivateProfileStringA (KERNEL32.@)
1394 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1395 LPCSTR string
, LPCSTR filename
)
1397 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1400 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1401 else sectionW
.Buffer
= NULL
;
1402 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1403 else entryW
.Buffer
= NULL
;
1404 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1405 else stringW
.Buffer
= NULL
;
1406 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1407 else filenameW
.Buffer
= NULL
;
1409 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1410 stringW
.Buffer
, filenameW
.Buffer
);
1411 RtlFreeUnicodeString(§ionW
);
1412 RtlFreeUnicodeString(&entryW
);
1413 RtlFreeUnicodeString(&stringW
);
1414 RtlFreeUnicodeString(&filenameW
);
1418 /***********************************************************************
1419 * WritePrivateProfileSectionW (KERNEL32.@)
1421 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1422 LPCWSTR string
, LPCWSTR filename
)
1427 RtlEnterCriticalSection( &PROFILE_CritSect
);
1429 if (!section
&& !string
)
1431 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1433 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1436 else if (PROFILE_Open( filename
, TRUE
)) {
1437 if (!string
) {/* delete the named section*/
1438 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1439 PROFILE_FlushFile();
1441 PROFILE_DeleteAllKeys(section
);
1444 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1445 strcpyW( buf
, string
);
1446 if((p
= strchrW( buf
, '='))) {
1448 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1450 HeapFree( GetProcessHeap(), 0, buf
);
1451 string
+= strlenW(string
)+1;
1453 PROFILE_FlushFile();
1457 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1461 /***********************************************************************
1462 * WritePrivateProfileSectionA (KERNEL32.@)
1464 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1465 LPCSTR string
, LPCSTR filename
)
1468 UNICODE_STRING sectionW
, filenameW
;
1477 while(*p
) p
+= strlen(p
) + 1;
1478 lenA
= p
- string
+ 1;
1479 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1480 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1481 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1483 else stringW
= NULL
;
1484 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1485 else sectionW
.Buffer
= NULL
;
1486 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1487 else filenameW
.Buffer
= NULL
;
1489 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1491 HeapFree(GetProcessHeap(), 0, stringW
);
1492 RtlFreeUnicodeString(§ionW
);
1493 RtlFreeUnicodeString(&filenameW
);
1497 /***********************************************************************
1498 * WriteProfileSectionA (KERNEL32.@)
1500 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1503 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1506 /***********************************************************************
1507 * WriteProfileSectionW (KERNEL32.@)
1509 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1511 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1515 /***********************************************************************
1516 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1518 * Returns the section names contained in the specified file.
1519 * FIXME: Where do we find this file when the path is relative?
1520 * The section names are returned as a list of strings with an extra
1521 * '\0' to mark the end of the list. Except for that the behavior
1522 * depends on the Windows version.
1525 * - if the buffer is 0 or 1 character long then it is as if it was of
1527 * - otherwise, if the buffer is too small only the section names that fit
1529 * - note that this means if the buffer was too small to return even just
1530 * the first section name then a single '\0' will be returned.
1531 * - the return value is the number of characters written in the buffer,
1532 * except if the buffer was too small in which case len-2 is returned
1535 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1536 * '\0' and the return value is 0
1537 * - otherwise if the buffer is too small then the first section name that
1538 * does not fit is truncated so that the string list can be terminated
1539 * correctly (double '\0')
1540 * - the return value is the number of characters written in the buffer
1541 * except for the trailing '\0'. If the buffer is too small, then the
1542 * return value is len-2
1543 * - Win2000 has a bug that triggers when the section names and the
1544 * trailing '\0' fit exactly in the buffer. In that case the trailing
1547 * Wine implements the observed Win2000 behavior (except for the bug).
1549 * Note that when the buffer is big enough then the return value may be any
1550 * value between 1 and len-1 (or len in Win95), including len-2.
1552 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1557 RtlEnterCriticalSection( &PROFILE_CritSect
);
1559 if (PROFILE_Open( filename
, FALSE
))
1560 ret
= PROFILE_GetSectionNames(buffer
, size
);
1562 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1568 /***********************************************************************
1569 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1571 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1574 UNICODE_STRING filenameW
;
1578 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1579 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1580 else filenameW
.Buffer
= NULL
;
1582 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1585 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+1, buffer
, size
-1, NULL
, NULL
);
1597 RtlFreeUnicodeString(&filenameW
);
1598 HeapFree(GetProcessHeap(), 0, bufferW
);
1602 /***********************************************************************
1603 * GetPrivateProfileStructW (KERNEL32.@)
1605 * Should match Win95's behaviour pretty much
1607 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1608 LPVOID buf
, UINT len
, LPCWSTR filename
)
1612 RtlEnterCriticalSection( &PROFILE_CritSect
);
1614 if (PROFILE_Open( filename
, FALSE
)) {
1615 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1617 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1618 if (((strlenW(k
->value
) - 2) / 2) == len
)
1625 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1626 /* check for invalid chars in ASCII coded hex string */
1627 for (p
=k
->value
; p
< end
; p
++)
1631 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1632 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1639 BOOL highnibble
= TRUE
;
1641 LPBYTE binbuf
= buf
;
1643 end
-= 2; /* don't include checksum in output data */
1644 /* translate ASCII hex format into binary data */
1645 for (p
=k
->value
; p
< end
; p
++)
1649 (c
- 'A' + 10) : (c
- '0');
1656 *binbuf
++ = b
; /* feed binary data into output */
1657 chksum
+= b
; /* calculate checksum */
1659 highnibble
^= 1; /* toggle */
1661 /* retrieve stored checksum value */
1663 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1665 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1666 if (b
== (chksum
& 0xff)) /* checksums match ? */
1672 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1677 /***********************************************************************
1678 * GetPrivateProfileStructA (KERNEL32.@)
1680 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1681 LPVOID buffer
, UINT len
, LPCSTR filename
)
1683 UNICODE_STRING sectionW
, keyW
, filenameW
;
1686 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1687 else sectionW
.Buffer
= NULL
;
1688 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1689 else keyW
.Buffer
= NULL
;
1690 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1691 else filenameW
.Buffer
= NULL
;
1693 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1695 /* Do not translate binary data. */
1697 RtlFreeUnicodeString(§ionW
);
1698 RtlFreeUnicodeString(&keyW
);
1699 RtlFreeUnicodeString(&filenameW
);
1705 /***********************************************************************
1706 * WritePrivateProfileStructW (KERNEL32.@)
1708 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1709 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1713 LPWSTR outstring
, p
;
1716 if (!section
&& !key
&& !buf
) /* flush the cache */
1717 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1719 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1720 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1722 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1723 *p
++ = hex
[*binbuf
>> 4];
1724 *p
++ = hex
[*binbuf
& 0xf];
1727 /* checksum is sum & 0xff */
1728 *p
++ = hex
[(sum
& 0xf0) >> 4];
1729 *p
++ = hex
[sum
& 0xf];
1732 RtlEnterCriticalSection( &PROFILE_CritSect
);
1734 if (PROFILE_Open( filename
, TRUE
)) {
1735 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1736 PROFILE_FlushFile();
1739 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1741 HeapFree( GetProcessHeap(), 0, outstring
);
1746 /***********************************************************************
1747 * WritePrivateProfileStructA (KERNEL32.@)
1749 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1750 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1752 UNICODE_STRING sectionW
, keyW
, filenameW
;
1755 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1756 else sectionW
.Buffer
= NULL
;
1757 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1758 else keyW
.Buffer
= NULL
;
1759 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1760 else filenameW
.Buffer
= NULL
;
1762 /* Do not translate binary data. */
1763 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1766 RtlFreeUnicodeString(§ionW
);
1767 RtlFreeUnicodeString(&keyW
);
1768 RtlFreeUnicodeString(&filenameW
);
1773 /***********************************************************************
1774 * OpenProfileUserMapping (KERNEL32.@)
1776 BOOL WINAPI
OpenProfileUserMapping(void) {
1777 FIXME("(), stub!\n");
1778 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1782 /***********************************************************************
1783 * CloseProfileUserMapping (KERNEL32.@)
1785 BOOL WINAPI
CloseProfileUserMapping(void) {
1786 FIXME("(), stub!\n");
1787 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);