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, { 0, (DWORD
)(__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
+ 1;
377 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
379 case ENCODING_UTF16BE
:
380 TRACE("UTF16 Big Endian encoding\n");
381 szFile
= (WCHAR
*)pBuffer
+ 1;
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
)
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_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
)
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
);
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
);
1063 * if allow_section_name_copy is TRUE, allow the copying :
1064 * - of Section names if 'section' is NULL
1065 * - of 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
,
1071 BOOL allow_section_name_copy
)
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
));
1105 pDefVal
= (LPCWSTR
)def_val
;
1107 RtlEnterCriticalSection( &PROFILE_CritSect
);
1109 if (PROFILE_Open( filename
)) {
1110 if ((allow_section_name_copy
) && (section
== NULL
))
1111 ret
= PROFILE_GetSectionNames(buffer
, len
);
1113 /* PROFILE_GetString already handles the 'entry == NULL' case */
1114 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1116 lstrcpynW( buffer
, pDefVal
, len
);
1117 ret
= strlenW( buffer
);
1120 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1122 if (pDefVal
!= def_val
) /* allocated */
1123 HeapFree(GetProcessHeap(), 0, (void*)pDefVal
);
1125 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1130 /***********************************************************************
1131 * GetPrivateProfileString (KERNEL.128)
1133 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1134 LPCSTR def_val
, LPSTR buffer
,
1135 UINT16 len
, LPCSTR filename
)
1137 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1139 INT16 retW
, ret
= 0;
1141 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1142 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1143 else sectionW
.Buffer
= NULL
;
1144 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1145 else entryW
.Buffer
= NULL
;
1146 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1147 else def_valW
.Buffer
= NULL
;
1148 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1149 else filenameW
.Buffer
= NULL
;
1151 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1152 def_valW
.Buffer
, bufferW
, len
,
1153 filenameW
.Buffer
, FALSE
);
1156 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1163 ret
--; /* strip terminating 0 */
1166 RtlFreeUnicodeString(§ionW
);
1167 RtlFreeUnicodeString(&entryW
);
1168 RtlFreeUnicodeString(&def_valW
);
1169 RtlFreeUnicodeString(&filenameW
);
1170 HeapFree(GetProcessHeap(), 0, bufferW
);
1174 /***********************************************************************
1175 * GetPrivateProfileStringA (KERNEL32.@)
1177 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1178 LPCSTR def_val
, LPSTR buffer
,
1179 UINT len
, LPCSTR filename
)
1181 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1185 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1186 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1187 else sectionW
.Buffer
= NULL
;
1188 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1189 else entryW
.Buffer
= NULL
;
1190 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1191 else def_valW
.Buffer
= NULL
;
1192 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1193 else filenameW
.Buffer
= NULL
;
1195 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1196 def_valW
.Buffer
, bufferW
, len
,
1200 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1207 ret
--; /* strip terminating 0 */
1210 RtlFreeUnicodeString(§ionW
);
1211 RtlFreeUnicodeString(&entryW
);
1212 RtlFreeUnicodeString(&def_valW
);
1213 RtlFreeUnicodeString(&filenameW
);
1214 HeapFree(GetProcessHeap(), 0, bufferW
);
1218 /***********************************************************************
1219 * GetPrivateProfileStringW (KERNEL32.@)
1221 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1222 LPCWSTR def_val
, LPWSTR buffer
,
1223 UINT len
, LPCWSTR filename
)
1225 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section
), debugstr_w(entry
), debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1227 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1228 buffer
, len
, filename
, TRUE
);
1231 /***********************************************************************
1232 * GetProfileStringA (KERNEL32.@)
1234 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1235 LPSTR buffer
, UINT len
)
1237 return GetPrivateProfileStringA( section
, entry
, def_val
,
1238 buffer
, len
, "win.ini" );
1241 /***********************************************************************
1242 * GetProfileStringW (KERNEL32.@)
1244 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1245 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1247 return GetPrivateProfileStringW( section
, entry
, def_val
,
1248 buffer
, len
, wininiW
);
1251 /***********************************************************************
1252 * WriteProfileStringA (KERNEL32.@)
1254 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1257 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1260 /***********************************************************************
1261 * WriteProfileStringW (KERNEL32.@)
1263 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1266 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1270 /***********************************************************************
1271 * GetPrivateProfileIntW (KERNEL32.@)
1273 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1274 INT def_val
, LPCWSTR filename
)
1277 UNICODE_STRING bufferW
;
1281 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1282 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1286 if (len
+1 == sizeof(buffer
)/sizeof(WCHAR
)) FIXME("result may be wrong!\n");
1288 /* FIXME: if entry can be found but it's empty, then Win16 is
1289 * supposed to return 0 instead of def_val ! Difficult/problematic
1290 * to implement (every other failure also returns zero buffer),
1291 * thus wait until testing framework avail for making sure nothing
1292 * else gets broken that way. */
1293 if (!buffer
[0]) return (UINT
)def_val
;
1295 RtlInitUnicodeString( &bufferW
, buffer
);
1296 RtlUnicodeStringToInteger( &bufferW
, 10, &result
);
1300 /***********************************************************************
1301 * GetPrivateProfileIntA (KERNEL32.@)
1303 * FIXME: rewrite using unicode
1305 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1306 INT def_val
, LPCSTR filename
)
1308 UNICODE_STRING entryW
, filenameW
, sectionW
;
1310 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1311 else entryW
.Buffer
= NULL
;
1312 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1313 else filenameW
.Buffer
= NULL
;
1314 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1315 else sectionW
.Buffer
= NULL
;
1316 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1318 RtlFreeUnicodeString(§ionW
);
1319 RtlFreeUnicodeString(&filenameW
);
1320 RtlFreeUnicodeString(&entryW
);
1324 /***********************************************************************
1325 * GetPrivateProfileSectionW (KERNEL32.@)
1327 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1328 DWORD len
, LPCWSTR filename
)
1332 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1334 RtlEnterCriticalSection( &PROFILE_CritSect
);
1336 if (PROFILE_Open( filename
))
1337 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1339 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1344 /***********************************************************************
1345 * GetPrivateProfileSectionA (KERNEL32.@)
1347 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1348 DWORD len
, LPCSTR filename
)
1350 UNICODE_STRING sectionW
, filenameW
;
1354 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1355 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1356 else sectionW
.Buffer
= NULL
;
1357 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1358 else filenameW
.Buffer
= NULL
;
1360 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1363 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1379 RtlFreeUnicodeString(§ionW
);
1380 RtlFreeUnicodeString(&filenameW
);
1381 HeapFree(GetProcessHeap(), 0, bufferW
);
1385 /***********************************************************************
1386 * GetProfileSectionA (KERNEL32.@)
1388 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1390 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1393 /***********************************************************************
1394 * GetProfileSectionW (KERNEL32.@)
1396 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1398 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1402 /***********************************************************************
1403 * WritePrivateProfileStringW (KERNEL32.@)
1405 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1406 LPCWSTR string
, LPCWSTR filename
)
1410 RtlEnterCriticalSection( &PROFILE_CritSect
);
1412 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1414 if (!filename
|| PROFILE_Open( filename
))
1416 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1419 else if (PROFILE_Open( filename
))
1422 FIXME("(NULL?,%s,%s,%s)?\n",
1423 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1425 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1426 PROFILE_FlushFile();
1430 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1434 /***********************************************************************
1435 * WritePrivateProfileStringA (KERNEL32.@)
1437 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1438 LPCSTR string
, LPCSTR filename
)
1440 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1443 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1444 else sectionW
.Buffer
= NULL
;
1445 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1446 else entryW
.Buffer
= NULL
;
1447 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1448 else stringW
.Buffer
= NULL
;
1449 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1450 else filenameW
.Buffer
= NULL
;
1452 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1453 stringW
.Buffer
, filenameW
.Buffer
);
1454 RtlFreeUnicodeString(§ionW
);
1455 RtlFreeUnicodeString(&entryW
);
1456 RtlFreeUnicodeString(&stringW
);
1457 RtlFreeUnicodeString(&filenameW
);
1461 /***********************************************************************
1462 * WritePrivateProfileSectionW (KERNEL32.@)
1464 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1465 LPCWSTR string
, LPCWSTR filename
)
1470 RtlEnterCriticalSection( &PROFILE_CritSect
);
1472 if (!section
&& !string
)
1474 if (!filename
|| PROFILE_Open( filename
))
1476 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1479 else if (PROFILE_Open( filename
)) {
1480 if (!string
) {/* delete the named section*/
1481 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1482 PROFILE_FlushFile();
1484 PROFILE_DeleteAllKeys(section
);
1487 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1488 strcpyW( buf
, string
);
1489 if((p
= strchrW( buf
, '='))) {
1491 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1493 HeapFree( GetProcessHeap(), 0, buf
);
1494 string
+= strlenW(string
)+1;
1496 PROFILE_FlushFile();
1500 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1504 /***********************************************************************
1505 * WritePrivateProfileSectionA (KERNEL32.@)
1507 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1508 LPCSTR string
, LPCSTR filename
)
1511 UNICODE_STRING sectionW
, filenameW
;
1520 while(*p
) p
+= strlen(p
) + 1;
1521 lenA
= p
- string
+ 1;
1522 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1523 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1524 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1526 else stringW
= NULL
;
1527 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1528 else sectionW
.Buffer
= NULL
;
1529 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1530 else filenameW
.Buffer
= NULL
;
1532 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1534 HeapFree(GetProcessHeap(), 0, stringW
);
1535 RtlFreeUnicodeString(§ionW
);
1536 RtlFreeUnicodeString(&filenameW
);
1540 /***********************************************************************
1541 * WriteProfileSectionA (KERNEL32.@)
1543 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1546 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1549 /***********************************************************************
1550 * WriteProfileSectionW (KERNEL32.@)
1552 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1554 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1558 /***********************************************************************
1559 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1561 * Returns the section names contained in the specified file.
1562 * FIXME: Where do we find this file when the path is relative?
1563 * The section names are returned as a list of strings with an extra
1564 * '\0' to mark the end of the list. Except for that the behavior
1565 * depends on the Windows version.
1568 * - if the buffer is 0 or 1 character long then it is as if it was of
1570 * - otherwise, if the buffer is too small only the section names that fit
1572 * - note that this means if the buffer was too small to return even just
1573 * the first section name then a single '\0' will be returned.
1574 * - the return value is the number of characters written in the buffer,
1575 * except if the buffer was too small in which case len-2 is returned
1578 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1579 * '\0' and the return value is 0
1580 * - otherwise if the buffer is too small then the first section name that
1581 * does not fit is truncated so that the string list can be terminated
1582 * correctly (double '\0')
1583 * - the return value is the number of characters written in the buffer
1584 * except for the trailing '\0'. If the buffer is too small, then the
1585 * return value is len-2
1586 * - Win2000 has a bug that triggers when the section names and the
1587 * trailing '\0' fit exactly in the buffer. In that case the trailing
1590 * Wine implements the observed Win2000 behavior (except for the bug).
1592 * Note that when the buffer is big enough then the return value may be any
1593 * value between 1 and len-1 (or len in Win95), including len-2.
1595 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1600 RtlEnterCriticalSection( &PROFILE_CritSect
);
1602 if (PROFILE_Open( filename
))
1603 ret
= PROFILE_GetSectionNames(buffer
, size
);
1605 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1611 /***********************************************************************
1612 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1614 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1617 UNICODE_STRING filenameW
;
1621 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1622 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1623 else filenameW
.Buffer
= NULL
;
1625 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1628 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1636 RtlFreeUnicodeString(&filenameW
);
1637 HeapFree(GetProcessHeap(), 0, bufferW
);
1641 /***********************************************************************
1642 * GetPrivateProfileStructW (KERNEL32.@)
1644 * Should match Win95's behaviour pretty much
1646 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1647 LPVOID buf
, UINT len
, LPCWSTR filename
)
1651 RtlEnterCriticalSection( &PROFILE_CritSect
);
1653 if (PROFILE_Open( filename
)) {
1654 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1656 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1657 if (((strlenW(k
->value
) - 2) / 2) == len
)
1664 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1665 /* check for invalid chars in ASCII coded hex string */
1666 for (p
=k
->value
; p
< end
; p
++)
1670 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1671 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1678 BOOL highnibble
= TRUE
;
1680 LPBYTE binbuf
= (LPBYTE
)buf
;
1682 end
-= 2; /* don't include checksum in output data */
1683 /* translate ASCII hex format into binary data */
1684 for (p
=k
->value
; p
< end
; p
++)
1688 (c
- 'A' + 10) : (c
- '0');
1695 *binbuf
++ = b
; /* feed binary data into output */
1696 chksum
+= b
; /* calculate checksum */
1698 highnibble
^= 1; /* toggle */
1700 /* retrieve stored checksum value */
1702 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1704 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1705 if (b
== (chksum
& 0xff)) /* checksums match ? */
1711 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1716 /***********************************************************************
1717 * GetPrivateProfileStructA (KERNEL32.@)
1719 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1720 LPVOID buffer
, UINT len
, LPCSTR filename
)
1722 UNICODE_STRING sectionW
, keyW
, filenameW
;
1725 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1726 else sectionW
.Buffer
= NULL
;
1727 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1728 else keyW
.Buffer
= NULL
;
1729 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1730 else filenameW
.Buffer
= NULL
;
1732 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1734 /* Do not translate binary data. */
1736 RtlFreeUnicodeString(§ionW
);
1737 RtlFreeUnicodeString(&keyW
);
1738 RtlFreeUnicodeString(&filenameW
);
1744 /***********************************************************************
1745 * WritePrivateProfileStructW (KERNEL32.@)
1747 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1748 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1752 LPWSTR outstring
, p
;
1755 if (!section
&& !key
&& !buf
) /* flush the cache */
1756 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1758 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1759 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1761 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1762 *p
++ = hex
[*binbuf
>> 4];
1763 *p
++ = hex
[*binbuf
& 0xf];
1766 /* checksum is sum & 0xff */
1767 *p
++ = hex
[(sum
& 0xf0) >> 4];
1768 *p
++ = hex
[sum
& 0xf];
1771 RtlEnterCriticalSection( &PROFILE_CritSect
);
1773 if (PROFILE_Open( filename
)) {
1774 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1775 PROFILE_FlushFile();
1778 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1780 HeapFree( GetProcessHeap(), 0, outstring
);
1785 /***********************************************************************
1786 * WritePrivateProfileStructA (KERNEL32.@)
1788 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1789 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1791 UNICODE_STRING sectionW
, keyW
, filenameW
;
1794 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1795 else sectionW
.Buffer
= NULL
;
1796 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1797 else keyW
.Buffer
= NULL
;
1798 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1799 else filenameW
.Buffer
= NULL
;
1801 /* Do not translate binary data. */
1802 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1805 RtlFreeUnicodeString(§ionW
);
1806 RtlFreeUnicodeString(&keyW
);
1807 RtlFreeUnicodeString(&filenameW
);
1812 /***********************************************************************
1813 * WriteOutProfiles (KERNEL.315)
1815 void WINAPI
WriteOutProfiles16(void)
1817 RtlEnterCriticalSection( &PROFILE_CritSect
);
1818 PROFILE_FlushFile();
1819 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1822 /***********************************************************************
1823 * CloseProfileUserMapping (KERNEL32.@)
1825 BOOL WINAPI
CloseProfileUserMapping(void) {
1826 FIXME("(), stub!\n");
1827 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);