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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
33 #include "wine/winbase16.h"
34 #include "wine/unicode.h"
35 #include "wine/server.h"
36 #include "wine/library.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(profile
);
41 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
51 typedef struct tagPROFILEKEY
54 struct tagPROFILEKEY
*next
;
58 typedef struct tagPROFILESECTION
60 struct tagPROFILEKEY
*key
;
61 struct tagPROFILESECTION
*next
;
69 PROFILESECTION
*section
;
71 FILETIME LastWriteTime
;
76 #define N_CACHED_PROFILES 10
78 /* Cached profile files */
79 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
81 #define CurProfile (MRUProfile[0])
83 /* Check for comments in profile */
84 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
86 static const WCHAR emptystringW
[] = {0};
87 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
89 static CRITICAL_SECTION PROFILE_CritSect
;
90 static CRITICAL_SECTION_DEBUG critsect_debug
=
92 0, 0, &PROFILE_CritSect
,
93 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
94 0, 0, { (DWORD_PTR
)(__FILE__
": PROFILE_CritSect") }
96 static CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
98 static const char hex
[16] = "0123456789ABCDEF";
100 /***********************************************************************
103 * Copy the content of an entry into a buffer, removing quotes, and possibly
104 * translating environment variables.
106 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
113 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
115 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
118 lstrcpynW( buffer
, value
, len
);
119 if (quote
&& (len
>= strlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
122 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
123 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
126 USHORT
* shortbuffer
= (USHORT
*)buffer
;
127 for (i
= 0; i
< len
; i
++)
128 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
131 /* writes any necessary encoding marker to the file */
132 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
134 DWORD dwBytesWritten
;
141 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
143 case ENCODING_UTF16LE
:
145 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
147 case ENCODING_UTF16BE
:
149 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
154 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
157 int write_buffer_len
;
158 DWORD dwBytesWritten
;
160 TRACE("writing: %s\n", debugstr_wn(szLine
, len
));
165 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
166 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
167 if (!write_buffer
) return;
168 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
169 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
170 HeapFree(GetProcessHeap(), 0, write_buffer
);
173 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
174 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
175 if (!write_buffer
) return;
176 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
177 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
178 HeapFree(GetProcessHeap(), 0, write_buffer
);
180 case ENCODING_UTF16LE
:
181 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
183 case ENCODING_UTF16BE
:
184 PROFILE_ByteSwapShortBuffer(szLine
, len
);
185 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
188 FIXME("encoding type %d not implemented\n", encoding
);
192 /***********************************************************************
195 * Save a profile tree to a file.
197 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
202 PROFILE_WriteMarker(hFile
, encoding
);
204 for ( ; section
; section
= section
->next
)
208 if (section
->name
[0]) len
+= strlenW(section
->name
) + 6;
210 for (key
= section
->key
; key
; key
= key
->next
)
212 len
+= strlenW(key
->name
) + 2;
213 if (key
->value
) len
+= strlenW(key
->value
) + 1;
216 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
220 if (section
->name
[0])
225 strcpyW( p
, section
->name
);
231 for (key
= section
->key
; key
; key
= key
->next
)
233 strcpyW( p
, key
->name
);
238 strcpyW( p
, key
->value
);
244 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
245 HeapFree(GetProcessHeap(), 0, buffer
);
250 /***********************************************************************
253 * Free a profile tree.
255 static void PROFILE_Free( PROFILESECTION
*section
)
257 PROFILESECTION
*next_section
;
258 PROFILEKEY
*key
, *next_key
;
260 for ( ; section
; section
= next_section
)
262 for (key
= section
->key
; key
; key
= next_key
)
264 next_key
= key
->next
;
265 HeapFree( GetProcessHeap(), 0, key
->value
);
266 HeapFree( GetProcessHeap(), 0, key
);
268 next_section
= section
->next
;
269 HeapFree( GetProcessHeap(), 0, section
);
273 /* returns 1 if a character white space else 0 */
274 static inline int PROFILE_isspaceW(WCHAR c
)
276 if (isspaceW(c
)) return 1;
277 if (c
=='\r' || c
==0x1a) return 1;
278 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
282 static inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
284 int flags
= IS_TEXT_UNICODE_SIGNATURE
|
285 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
286 IS_TEXT_UNICODE_ODD_LENGTH
;
287 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
289 *len
= sizeof(bom_utf8
);
290 return ENCODING_UTF8
;
292 RtlIsTextUnicode(buffer
, *len
, &flags
);
293 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
295 *len
= sizeof(WCHAR
);
296 return ENCODING_UTF16LE
;
298 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
300 *len
= sizeof(WCHAR
);
301 return ENCODING_UTF16BE
;
304 return ENCODING_ANSI
;
308 /***********************************************************************
311 * Load a profile tree from a file.
313 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
317 const WCHAR
*szLineStart
, *szLineEnd
;
318 const WCHAR
*szValueStart
, *szEnd
, *next_line
;
320 PROFILESECTION
*section
, *first_section
;
321 PROFILESECTION
**next_section
;
322 PROFILEKEY
*key
, *prev_key
, **next_key
;
325 TRACE("%p\n", hFile
);
327 dwFileSize
= GetFileSize(hFile
, NULL
);
328 if (dwFileSize
== INVALID_FILE_SIZE
)
331 pBuffer
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
332 if (!pBuffer
) return NULL
;
334 if (!ReadFile(hFile
, pBuffer
, dwFileSize
, &dwFileSize
, NULL
))
336 HeapFree(GetProcessHeap(), 0, pBuffer
);
337 WARN("Error %ld reading file\n", GetLastError());
341 *pEncoding
= PROFILE_DetectTextEncoding(pBuffer
, &len
);
342 /* len is set to the number of bytes in the character marker.
343 * we want to skip these bytes */
344 pBuffer
= (char *)pBuffer
+ len
;
349 TRACE("ANSI encoding\n");
351 len
= MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
352 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
355 HeapFree(GetProcessHeap(), 0, pBuffer
);
358 MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
359 szEnd
= szFile
+ len
;
362 TRACE("UTF8 encoding\n");
364 len
= MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
365 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
368 HeapFree(GetProcessHeap(), 0, pBuffer
);
371 MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
372 szEnd
= szFile
+ len
;
374 case ENCODING_UTF16LE
:
375 TRACE("UTF16 Little Endian encoding\n");
376 szFile
= (WCHAR
*)pBuffer
;
377 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
379 case ENCODING_UTF16BE
:
380 TRACE("UTF16 Big Endian encoding\n");
381 szFile
= (WCHAR
*)pBuffer
;
382 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
383 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
386 FIXME("encoding type %d not implemented\n", *pEncoding
);
387 HeapFree(GetProcessHeap(), 0, pBuffer
);
391 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
392 if(first_section
== NULL
)
394 if (szFile
!= pBuffer
)
395 HeapFree(GetProcessHeap(), 0, szFile
);
396 HeapFree(GetProcessHeap(), 0, pBuffer
);
399 first_section
->name
[0] = 0;
400 first_section
->key
= NULL
;
401 first_section
->next
= NULL
;
402 next_section
= &first_section
->next
;
403 next_key
= &first_section
->key
;
407 while (next_line
< szEnd
)
409 szLineStart
= next_line
;
410 next_line
= memchrW(szLineStart
, '\n', szEnd
- szLineStart
);
411 if (!next_line
) next_line
= szEnd
;
413 szLineEnd
= next_line
;
417 /* get rid of white space */
418 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
)) szLineStart
++;
419 while ((szLineEnd
> szLineStart
) && ((szLineEnd
[-1] == '\n') || PROFILE_isspaceW(szLineEnd
[-1]))) szLineEnd
--;
421 if (szLineStart
>= szLineEnd
) continue;
423 if (*szLineStart
== '[') /* section start */
425 const WCHAR
* szSectionEnd
;
426 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
428 WARN("Invalid section header at line %d: %s\n",
429 line
, debugstr_wn(szLineStart
, (int)(szLineEnd
- szLineStart
)) );
434 len
= (int)(szSectionEnd
- szLineStart
);
435 /* no need to allocate +1 for NULL terminating character as
436 * already included in structure */
437 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
439 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
440 section
->name
[len
] = '\0';
442 section
->next
= NULL
;
443 *next_section
= section
;
444 next_section
= §ion
->next
;
445 next_key
= §ion
->key
;
448 TRACE("New section: %s\n", debugstr_w(section
->name
));
454 /* get rid of white space after the name and before the start
456 len
= szLineEnd
- szLineStart
;
457 if ((szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
459 const WCHAR
*szNameEnd
= szValueStart
;
460 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(szNameEnd
[-1])) szNameEnd
--;
461 len
= szNameEnd
- szLineStart
;
463 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
466 if (len
|| !prev_key
|| *prev_key
->name
)
468 /* no need to allocate +1 for NULL terminating character as
469 * already included in structure */
470 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
471 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
472 key
->name
[len
] = '\0';
475 len
= (int)(szLineEnd
- szValueStart
);
476 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
477 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
478 key
->value
[len
] = '\0';
480 else key
->value
= NULL
;
484 next_key
= &key
->next
;
487 TRACE("New key: name=%s, value=%s\n",
488 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
491 if (szFile
!= pBuffer
)
492 HeapFree(GetProcessHeap(), 0, szFile
);
493 HeapFree(GetProcessHeap(), 0, pBuffer
);
494 return first_section
;
498 /***********************************************************************
499 * PROFILE_DeleteSection
501 * Delete a section from a profile tree.
503 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
507 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
509 PROFILESECTION
*to_del
= *section
;
510 *section
= to_del
->next
;
512 PROFILE_Free( to_del
);
515 section
= &(*section
)->next
;
521 /***********************************************************************
524 * Delete a key from a profile tree.
526 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
527 LPCWSTR section_name
, LPCWSTR key_name
)
531 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
533 PROFILEKEY
**key
= &(*section
)->key
;
536 if (!strcmpiW( (*key
)->name
, key_name
))
538 PROFILEKEY
*to_del
= *key
;
540 HeapFree( GetProcessHeap(), 0, to_del
->value
);
541 HeapFree( GetProcessHeap(), 0, to_del
);
547 section
= &(*section
)->next
;
553 /***********************************************************************
554 * PROFILE_DeleteAllKeys
556 * Delete all keys from a profile tree.
558 static void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
560 PROFILESECTION
**section
= &CurProfile
->section
;
563 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
565 PROFILEKEY
**key
= &(*section
)->key
;
568 PROFILEKEY
*to_del
= *key
;
570 HeapFree( GetProcessHeap(), 0, to_del
->value
);
571 HeapFree( GetProcessHeap(), 0, to_del
);
572 CurProfile
->changed
=TRUE
;
575 section
= &(*section
)->next
;
580 /***********************************************************************
583 * Find a key in a profile tree, optionally creating it.
585 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
586 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
591 while (PROFILE_isspaceW(*section_name
)) section_name
++;
592 p
= section_name
+ strlenW(section_name
) - 1;
593 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
594 seclen
= p
- section_name
+ 1;
596 while (PROFILE_isspaceW(*key_name
)) key_name
++;
597 p
= key_name
+ strlenW(key_name
) - 1;
598 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
599 keylen
= p
- key_name
+ 1;
603 if ( ((*section
)->name
[0])
604 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
605 && (((*section
)->name
)[seclen
] == '\0') )
607 PROFILEKEY
**key
= &(*section
)->key
;
611 /* If create_always is FALSE then we check if the keyname
612 * already exists. Otherwise we add it regardless of its
613 * existence, to allow keys to be added more than once in
618 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
619 && (((*key
)->name
)[keylen
] == '\0') )
624 if (!create
) return NULL
;
625 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
627 strcpyW( (*key
)->name
, key_name
);
628 (*key
)->value
= NULL
;
632 section
= &(*section
)->next
;
634 if (!create
) return NULL
;
635 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
636 if(*section
== NULL
) return NULL
;
637 strcpyW( (*section
)->name
, section_name
);
638 (*section
)->next
= NULL
;
639 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
640 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
642 HeapFree(GetProcessHeap(), 0, *section
);
645 strcpyW( (*section
)->key
->name
, key_name
);
646 (*section
)->key
->value
= NULL
;
647 (*section
)->key
->next
= NULL
;
648 return (*section
)->key
;
652 /***********************************************************************
655 * Flush the current profile to disk if changed.
657 static BOOL
PROFILE_FlushFile(void)
660 FILETIME LastWriteTime
;
664 WARN("No current profile!\n");
668 if (!CurProfile
->changed
) return TRUE
;
670 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
672 if (hFile
== INVALID_HANDLE_VALUE
)
674 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile
->filename
), GetLastError());
678 TRACE("Saving %s\n", debugstr_w(CurProfile
->filename
));
679 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
680 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
681 CurProfile
->LastWriteTime
=LastWriteTime
;
682 CloseHandle( hFile
);
683 CurProfile
->changed
= FALSE
;
688 /***********************************************************************
689 * PROFILE_ReleaseFile
691 * Flush the current profile to disk and remove it from the cache.
693 static void PROFILE_ReleaseFile(void)
696 PROFILE_Free( CurProfile
->section
);
697 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
698 CurProfile
->changed
= FALSE
;
699 CurProfile
->section
= NULL
;
700 CurProfile
->filename
= NULL
;
701 CurProfile
->encoding
= ENCODING_ANSI
;
702 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
706 /***********************************************************************
709 * Open a profile file, checking the cached file first.
711 static BOOL
PROFILE_Open( LPCWSTR filename
)
713 WCHAR windirW
[MAX_PATH
];
714 WCHAR buffer
[MAX_PATH
];
715 HANDLE hFile
= INVALID_HANDLE_VALUE
;
716 FILETIME LastWriteTime
;
718 PROFILE
*tempProfile
;
720 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
722 /* First time around */
725 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
727 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
728 if(MRUProfile
[i
] == NULL
) break;
729 MRUProfile
[i
]->changed
=FALSE
;
730 MRUProfile
[i
]->section
=NULL
;
731 MRUProfile
[i
]->filename
=NULL
;
732 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
733 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
736 GetWindowsDirectoryW( windirW
, MAX_PATH
);
738 if ((RtlDetermineDosPathNameType_U(filename
) == RELATIVE_PATH
) &&
739 !strchrW(filename
, '\\') && !strchrW(filename
, '/'))
741 static const WCHAR wszSeparator
[] = {'\\', 0};
742 strcpyW(buffer
, windirW
);
743 strcatW(buffer
, wszSeparator
);
744 strcatW(buffer
, filename
);
749 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
752 TRACE("path: %s\n", debugstr_w(buffer
));
754 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
756 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
758 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer
));
762 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
764 if ((MRUProfile
[i
]->filename
&& !strcmpiW( buffer
, MRUProfile
[i
]->filename
)))
766 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile
[i
]->filename
), debugstr_w(buffer
));
770 tempProfile
=MRUProfile
[i
];
772 MRUProfile
[j
]=MRUProfile
[j
-1];
773 CurProfile
=tempProfile
;
776 if (hFile
!= INVALID_HANDLE_VALUE
)
778 if (TRACE_ON(profile
))
780 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
781 if (memcmp(&CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
)))
782 TRACE("(%s): already opened (mru=%d)\n",
783 debugstr_w(buffer
), i
);
785 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
786 debugstr_w(buffer
), i
);
790 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
791 debugstr_w(buffer
), i
);
796 /* Flush the old current profile */
799 /* Make the oldest profile the current one only in order to get rid of it */
800 if(i
==N_CACHED_PROFILES
)
802 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
803 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
804 MRUProfile
[i
]=MRUProfile
[i
-1];
805 CurProfile
=tempProfile
;
807 if(CurProfile
->filename
) PROFILE_ReleaseFile();
809 /* OK, now that CurProfile is definitely free we assign it our new file */
810 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer
)+1) * sizeof(WCHAR
) );
811 strcpyW( CurProfile
->filename
, buffer
);
813 if (hFile
!= INVALID_HANDLE_VALUE
)
815 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
816 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
821 /* Does not exist yet, we will create it in PROFILE_FlushFile */
822 WARN("profile file %s not found\n", debugstr_w(buffer
) );
828 /***********************************************************************
831 * Returns all keys of a section.
832 * If return_values is TRUE, also include the corresponding values.
834 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
835 LPWSTR buffer
, UINT len
, BOOL return_values
, BOOL return_noequalkeys
)
839 if(!buffer
) return 0;
841 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
845 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
848 for (key
= section
->key
; key
; key
= key
->next
)
851 if (!*key
->name
) continue; /* Skip empty lines */
852 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
853 if (!return_noequalkeys
&& !return_values
&& !key
->value
) continue; /* Skip lines w.o. '=' */
854 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
855 len
-= strlenW(buffer
) + 1;
856 buffer
+= strlenW(buffer
) + 1;
859 if (return_values
&& key
->value
) {
861 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
862 len
-= strlenW(buffer
) + 1;
863 buffer
+= strlenW(buffer
) + 1;
868 /*If either lpszSection or lpszKey is NULL and the supplied
869 destination buffer is too small to hold all the strings,
870 the last string is truncated and followed by two null characters.
871 In this case, the return value is equal to cchReturnBuffer
879 section
= section
->next
;
881 buffer
[0] = buffer
[1] = '\0';
885 /* See GetPrivateProfileSectionNamesA for documentation */
886 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
890 PROFILESECTION
*section
;
892 TRACE("(%p, %d)\n", buffer
, len
);
903 section
= CurProfile
->section
;
904 while ((section
!=NULL
)) {
905 if (section
->name
[0]) {
906 tmplen
= strlenW(section
->name
)+1;
907 if (tmplen
> buflen
) {
909 memcpy(buf
, section
->name
, (buflen
-1) * sizeof(WCHAR
));
916 memcpy(buf
, section
->name
, tmplen
* sizeof(WCHAR
));
920 section
= section
->next
;
927 /***********************************************************************
930 * Get a profile string.
932 * Tests with GetPrivateProfileString16, W95a,
933 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
934 * section key_name def_val res buffer
935 * "set1" "1" "x" 43 [data]
936 * "set1" "1 " "x" 43 [data] (!)
937 * "set1" " 1 "' "x" 43 [data] (!)
938 * "set1" "" "x" 1 "x"
939 * "set1" "" "x " 1 "x" (!)
940 * "set1" "" " x " 3 " x" (!)
941 * "set1" NULL "x" 6 "1\02\03\0\0"
942 * "set1" "" "x" 1 "x"
943 * NULL "1" "x" 0 "" (!)
949 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
950 LPCWSTR def_val
, LPWSTR buffer
, UINT len
, BOOL win32
)
952 PROFILEKEY
*key
= NULL
;
953 static const WCHAR empty_strW
[] = { 0 };
955 if(!buffer
) return 0;
957 if (!def_val
) def_val
= empty_strW
;
962 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
965 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
966 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
968 TRACE("(%s,%s,%s): returning %s\n",
969 debugstr_w(section
), debugstr_w(key_name
),
970 debugstr_w(def_val
), debugstr_w(buffer
) );
971 return strlenW( buffer
);
973 /* no "else" here ! */
974 if (section
&& section
[0])
976 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
, !win32
);
977 if (!buffer
[0]) /* no luck -> def_val */
979 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
980 ret
= strlenW(buffer
);
989 /***********************************************************************
992 * Set a profile string.
994 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
995 LPCWSTR value
, BOOL create_always
)
997 if (!key_name
) /* Delete a whole section */
999 TRACE("(%s)\n", debugstr_w(section_name
));
1000 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
1002 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
1003 this is not an error on application's level.*/
1005 else if (!value
) /* Delete a key */
1007 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
1008 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
1009 section_name
, key_name
);
1010 return TRUE
; /* same error handling as above */
1012 else /* Set the key value */
1014 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
1015 key_name
, TRUE
, create_always
);
1016 TRACE("(%s,%s,%s):\n",
1017 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
1018 if (!key
) return FALSE
;
1020 /* strip the leading spaces. We can safely strip \n\r and
1021 * friends too, they should not happen here anyway. */
1022 while (PROFILE_isspaceW(*value
)) value
++;
1026 if (!strcmpW( key
->value
, value
))
1028 TRACE(" no change needed\n" );
1029 return TRUE
; /* No change needed */
1031 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
1032 HeapFree( GetProcessHeap(), 0, key
->value
);
1034 else TRACE(" creating key\n" );
1035 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
1036 strcpyW( key
->value
, value
);
1037 CurProfile
->changed
= TRUE
;
1043 /********************* API functions **********************************/
1046 /***********************************************************************
1047 * GetProfileIntA (KERNEL32.@)
1049 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1051 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1054 /***********************************************************************
1055 * GetProfileIntW (KERNEL32.@)
1057 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1059 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1064 * - Section names if 'section' is NULL
1065 * - Keys in a Section if 'entry' is NULL
1066 * (see MSDN doc for GetPrivateProfileString)
1068 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1069 LPCWSTR def_val
, LPWSTR buffer
,
1070 UINT len
, LPCWSTR filename
,
1074 LPCWSTR pDefVal
= NULL
;
1079 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1080 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1082 /* strip any trailing ' ' of def_val. */
1085 LPCWSTR p
= &def_val
[strlenW(def_val
)]; /* even "" works ! */
1093 if (*p
== ' ') /* ouch, contained trailing ' ' */
1095 int len
= (int)(p
- def_val
);
1098 p
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1099 memcpy(p
, def_val
, len
* sizeof(WCHAR
));
1107 RtlEnterCriticalSection( &PROFILE_CritSect
);
1109 if (PROFILE_Open( filename
)) {
1110 if (win32
&& (section
== NULL
))
1111 ret
= PROFILE_GetSectionNames(buffer
, len
);
1113 /* PROFILE_GetString can handle the 'entry == NULL' case */
1114 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
, win32
);
1115 } else if (buffer
&& pDefVal
) {
1116 lstrcpynW( buffer
, pDefVal
, len
);
1117 ret
= strlenW( buffer
);
1122 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1124 if (pDefVal
!= def_val
) /* allocated */
1125 HeapFree(GetProcessHeap(), 0, (void*)pDefVal
);
1127 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1132 /***********************************************************************
1133 * GetPrivateProfileString (KERNEL.128)
1135 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1136 LPCSTR def_val
, LPSTR buffer
,
1137 UINT16 len
, LPCSTR filename
)
1139 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1141 INT16 retW
, ret
= 0;
1143 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1144 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1145 else sectionW
.Buffer
= NULL
;
1146 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1147 else entryW
.Buffer
= NULL
;
1148 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1149 else def_valW
.Buffer
= NULL
;
1150 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1151 else filenameW
.Buffer
= NULL
;
1153 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1154 def_valW
.Buffer
, bufferW
, len
,
1155 filenameW
.Buffer
, FALSE
);
1158 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1165 ret
--; /* strip terminating 0 */
1168 RtlFreeUnicodeString(§ionW
);
1169 RtlFreeUnicodeString(&entryW
);
1170 RtlFreeUnicodeString(&def_valW
);
1171 RtlFreeUnicodeString(&filenameW
);
1172 HeapFree(GetProcessHeap(), 0, bufferW
);
1176 /***********************************************************************
1177 * GetPrivateProfileStringA (KERNEL32.@)
1179 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1180 LPCSTR def_val
, LPSTR buffer
,
1181 UINT len
, LPCSTR filename
)
1183 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1187 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1188 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1189 else sectionW
.Buffer
= NULL
;
1190 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1191 else entryW
.Buffer
= NULL
;
1192 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1193 else def_valW
.Buffer
= NULL
;
1194 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1195 else filenameW
.Buffer
= NULL
;
1197 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1198 def_valW
.Buffer
, bufferW
, len
,
1202 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1209 ret
--; /* strip terminating 0 */
1212 RtlFreeUnicodeString(§ionW
);
1213 RtlFreeUnicodeString(&entryW
);
1214 RtlFreeUnicodeString(&def_valW
);
1215 RtlFreeUnicodeString(&filenameW
);
1216 HeapFree(GetProcessHeap(), 0, bufferW
);
1220 /***********************************************************************
1221 * GetPrivateProfileStringW (KERNEL32.@)
1223 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1224 LPCWSTR def_val
, LPWSTR buffer
,
1225 UINT len
, LPCWSTR filename
)
1227 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section
), debugstr_w(entry
), debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1229 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1230 buffer
, len
, filename
, TRUE
);
1233 /***********************************************************************
1234 * GetProfileStringA (KERNEL32.@)
1236 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1237 LPSTR buffer
, UINT len
)
1239 return GetPrivateProfileStringA( section
, entry
, def_val
,
1240 buffer
, len
, "win.ini" );
1243 /***********************************************************************
1244 * GetProfileStringW (KERNEL32.@)
1246 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1247 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1249 return GetPrivateProfileStringW( section
, entry
, def_val
,
1250 buffer
, len
, wininiW
);
1253 /***********************************************************************
1254 * WriteProfileStringA (KERNEL32.@)
1256 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1259 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1262 /***********************************************************************
1263 * WriteProfileStringW (KERNEL32.@)
1265 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1268 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1272 /***********************************************************************
1273 * GetPrivateProfileIntW (KERNEL32.@)
1275 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1276 INT def_val
, LPCWSTR filename
)
1279 UNICODE_STRING bufferW
;
1283 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1284 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1288 if (len
+1 == sizeof(buffer
)/sizeof(WCHAR
)) FIXME("result may be wrong!\n");
1290 /* FIXME: if entry can be found but it's empty, then Win16 is
1291 * supposed to return 0 instead of def_val ! Difficult/problematic
1292 * to implement (every other failure also returns zero buffer),
1293 * thus wait until testing framework avail for making sure nothing
1294 * else gets broken that way. */
1295 if (!buffer
[0]) return (UINT
)def_val
;
1297 RtlInitUnicodeString( &bufferW
, buffer
);
1298 RtlUnicodeStringToInteger( &bufferW
, 10, &result
);
1302 /***********************************************************************
1303 * GetPrivateProfileIntA (KERNEL32.@)
1305 * FIXME: rewrite using unicode
1307 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1308 INT def_val
, LPCSTR filename
)
1310 UNICODE_STRING entryW
, filenameW
, sectionW
;
1312 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1313 else entryW
.Buffer
= NULL
;
1314 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1315 else filenameW
.Buffer
= NULL
;
1316 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1317 else sectionW
.Buffer
= NULL
;
1318 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1320 RtlFreeUnicodeString(§ionW
);
1321 RtlFreeUnicodeString(&filenameW
);
1322 RtlFreeUnicodeString(&entryW
);
1326 /***********************************************************************
1327 * GetPrivateProfileSectionW (KERNEL32.@)
1329 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1330 DWORD len
, LPCWSTR filename
)
1334 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1336 RtlEnterCriticalSection( &PROFILE_CritSect
);
1338 if (PROFILE_Open( filename
))
1339 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
, FALSE
);
1341 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1346 /***********************************************************************
1347 * GetPrivateProfileSectionA (KERNEL32.@)
1349 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1350 DWORD len
, LPCSTR filename
)
1352 UNICODE_STRING sectionW
, filenameW
;
1356 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1357 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1358 else sectionW
.Buffer
= NULL
;
1359 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1360 else filenameW
.Buffer
= NULL
;
1362 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1365 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1381 RtlFreeUnicodeString(§ionW
);
1382 RtlFreeUnicodeString(&filenameW
);
1383 HeapFree(GetProcessHeap(), 0, bufferW
);
1387 /***********************************************************************
1388 * GetProfileSectionA (KERNEL32.@)
1390 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1392 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1395 /***********************************************************************
1396 * GetProfileSectionW (KERNEL32.@)
1398 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1400 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1404 /***********************************************************************
1405 * WritePrivateProfileStringW (KERNEL32.@)
1407 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1408 LPCWSTR string
, LPCWSTR filename
)
1412 RtlEnterCriticalSection( &PROFILE_CritSect
);
1414 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1416 if (!filename
|| PROFILE_Open( filename
))
1418 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1421 else if (PROFILE_Open( filename
))
1424 FIXME("(NULL?,%s,%s,%s)?\n",
1425 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1427 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1428 PROFILE_FlushFile();
1432 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1436 /***********************************************************************
1437 * WritePrivateProfileStringA (KERNEL32.@)
1439 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1440 LPCSTR string
, LPCSTR filename
)
1442 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1445 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1446 else sectionW
.Buffer
= NULL
;
1447 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1448 else entryW
.Buffer
= NULL
;
1449 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1450 else stringW
.Buffer
= NULL
;
1451 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1452 else filenameW
.Buffer
= NULL
;
1454 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1455 stringW
.Buffer
, filenameW
.Buffer
);
1456 RtlFreeUnicodeString(§ionW
);
1457 RtlFreeUnicodeString(&entryW
);
1458 RtlFreeUnicodeString(&stringW
);
1459 RtlFreeUnicodeString(&filenameW
);
1463 /***********************************************************************
1464 * WritePrivateProfileSectionW (KERNEL32.@)
1466 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1467 LPCWSTR string
, LPCWSTR filename
)
1472 RtlEnterCriticalSection( &PROFILE_CritSect
);
1474 if (!section
&& !string
)
1476 if (!filename
|| PROFILE_Open( filename
))
1478 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1481 else if (PROFILE_Open( filename
)) {
1482 if (!string
) {/* delete the named section*/
1483 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1484 PROFILE_FlushFile();
1486 PROFILE_DeleteAllKeys(section
);
1489 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1490 strcpyW( buf
, string
);
1491 if((p
= strchrW( buf
, '='))) {
1493 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1495 HeapFree( GetProcessHeap(), 0, buf
);
1496 string
+= strlenW(string
)+1;
1498 PROFILE_FlushFile();
1502 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1506 /***********************************************************************
1507 * WritePrivateProfileSectionA (KERNEL32.@)
1509 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1510 LPCSTR string
, LPCSTR filename
)
1513 UNICODE_STRING sectionW
, filenameW
;
1522 while(*p
) p
+= strlen(p
) + 1;
1523 lenA
= p
- string
+ 1;
1524 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1525 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1526 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1528 else stringW
= NULL
;
1529 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1530 else sectionW
.Buffer
= NULL
;
1531 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1532 else filenameW
.Buffer
= NULL
;
1534 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1536 HeapFree(GetProcessHeap(), 0, stringW
);
1537 RtlFreeUnicodeString(§ionW
);
1538 RtlFreeUnicodeString(&filenameW
);
1542 /***********************************************************************
1543 * WriteProfileSectionA (KERNEL32.@)
1545 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1548 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1551 /***********************************************************************
1552 * WriteProfileSectionW (KERNEL32.@)
1554 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1556 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1560 /***********************************************************************
1561 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1563 * Returns the section names contained in the specified file.
1564 * FIXME: Where do we find this file when the path is relative?
1565 * The section names are returned as a list of strings with an extra
1566 * '\0' to mark the end of the list. Except for that the behavior
1567 * depends on the Windows version.
1570 * - if the buffer is 0 or 1 character long then it is as if it was of
1572 * - otherwise, if the buffer is too small only the section names that fit
1574 * - note that this means if the buffer was too small to return even just
1575 * the first section name then a single '\0' will be returned.
1576 * - the return value is the number of characters written in the buffer,
1577 * except if the buffer was too small in which case len-2 is returned
1580 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1581 * '\0' and the return value is 0
1582 * - otherwise if the buffer is too small then the first section name that
1583 * does not fit is truncated so that the string list can be terminated
1584 * correctly (double '\0')
1585 * - the return value is the number of characters written in the buffer
1586 * except for the trailing '\0'. If the buffer is too small, then the
1587 * return value is len-2
1588 * - Win2000 has a bug that triggers when the section names and the
1589 * trailing '\0' fit exactly in the buffer. In that case the trailing
1592 * Wine implements the observed Win2000 behavior (except for the bug).
1594 * Note that when the buffer is big enough then the return value may be any
1595 * value between 1 and len-1 (or len in Win95), including len-2.
1597 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1602 RtlEnterCriticalSection( &PROFILE_CritSect
);
1604 if (PROFILE_Open( filename
))
1605 ret
= PROFILE_GetSectionNames(buffer
, size
);
1607 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1613 /***********************************************************************
1614 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1616 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1619 UNICODE_STRING filenameW
;
1623 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1624 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1625 else filenameW
.Buffer
= NULL
;
1627 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1630 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1638 RtlFreeUnicodeString(&filenameW
);
1639 HeapFree(GetProcessHeap(), 0, bufferW
);
1643 /***********************************************************************
1644 * GetPrivateProfileStructW (KERNEL32.@)
1646 * Should match Win95's behaviour pretty much
1648 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1649 LPVOID buf
, UINT len
, LPCWSTR filename
)
1653 RtlEnterCriticalSection( &PROFILE_CritSect
);
1655 if (PROFILE_Open( filename
)) {
1656 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1658 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1659 if (((strlenW(k
->value
) - 2) / 2) == len
)
1666 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1667 /* check for invalid chars in ASCII coded hex string */
1668 for (p
=k
->value
; p
< end
; p
++)
1672 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1673 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1680 BOOL highnibble
= TRUE
;
1682 LPBYTE binbuf
= (LPBYTE
)buf
;
1684 end
-= 2; /* don't include checksum in output data */
1685 /* translate ASCII hex format into binary data */
1686 for (p
=k
->value
; p
< end
; p
++)
1690 (c
- 'A' + 10) : (c
- '0');
1697 *binbuf
++ = b
; /* feed binary data into output */
1698 chksum
+= b
; /* calculate checksum */
1700 highnibble
^= 1; /* toggle */
1702 /* retrieve stored checksum value */
1704 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1706 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1707 if (b
== (chksum
& 0xff)) /* checksums match ? */
1713 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1718 /***********************************************************************
1719 * GetPrivateProfileStructA (KERNEL32.@)
1721 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1722 LPVOID buffer
, UINT len
, LPCSTR filename
)
1724 UNICODE_STRING sectionW
, keyW
, filenameW
;
1727 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1728 else sectionW
.Buffer
= NULL
;
1729 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1730 else keyW
.Buffer
= NULL
;
1731 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1732 else filenameW
.Buffer
= NULL
;
1734 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1736 /* Do not translate binary data. */
1738 RtlFreeUnicodeString(§ionW
);
1739 RtlFreeUnicodeString(&keyW
);
1740 RtlFreeUnicodeString(&filenameW
);
1746 /***********************************************************************
1747 * WritePrivateProfileStructW (KERNEL32.@)
1749 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1750 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1754 LPWSTR outstring
, p
;
1757 if (!section
&& !key
&& !buf
) /* flush the cache */
1758 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1760 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1761 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1763 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1764 *p
++ = hex
[*binbuf
>> 4];
1765 *p
++ = hex
[*binbuf
& 0xf];
1768 /* checksum is sum & 0xff */
1769 *p
++ = hex
[(sum
& 0xf0) >> 4];
1770 *p
++ = hex
[sum
& 0xf];
1773 RtlEnterCriticalSection( &PROFILE_CritSect
);
1775 if (PROFILE_Open( filename
)) {
1776 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1777 PROFILE_FlushFile();
1780 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1782 HeapFree( GetProcessHeap(), 0, outstring
);
1787 /***********************************************************************
1788 * WritePrivateProfileStructA (KERNEL32.@)
1790 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1791 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1793 UNICODE_STRING sectionW
, keyW
, filenameW
;
1796 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1797 else sectionW
.Buffer
= NULL
;
1798 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1799 else keyW
.Buffer
= NULL
;
1800 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1801 else filenameW
.Buffer
= NULL
;
1803 /* Do not translate binary data. */
1804 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1807 RtlFreeUnicodeString(§ionW
);
1808 RtlFreeUnicodeString(&keyW
);
1809 RtlFreeUnicodeString(&filenameW
);
1814 /***********************************************************************
1815 * WriteOutProfiles (KERNEL.315)
1817 void WINAPI
WriteOutProfiles16(void)
1819 RtlEnterCriticalSection( &PROFILE_CritSect
);
1820 PROFILE_FlushFile();
1821 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1824 /***********************************************************************
1825 * CloseProfileUserMapping (KERNEL32.@)
1827 BOOL WINAPI
CloseProfileUserMapping(void) {
1828 FIXME("(), stub!\n");
1829 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);