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"
34 #include "wine/winbase16.h"
36 #include "wine/unicode.h"
37 #include "wine/server.h"
38 #include "wine/library.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(profile
);
43 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
53 typedef struct tagPROFILEKEY
56 struct tagPROFILEKEY
*next
;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY
*key
;
63 struct tagPROFILESECTION
*next
;
71 PROFILESECTION
*section
;
73 FILETIME LastWriteTime
;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR emptystringW
[] = {0};
91 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect
;
94 static CRITICAL_SECTION_DEBUG critsect_debug
=
96 0, 0, &PROFILE_CritSect
,
97 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
98 0, 0, { 0, (DWORD
)(__FILE__
": PROFILE_CritSect") }
100 static CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
102 static const char hex
[16] = "0123456789ABCDEF";
104 /***********************************************************************
107 * Copy the content of an entry into a buffer, removing quotes, and possibly
108 * translating environment variables.
110 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
117 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
119 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
122 lstrcpynW( buffer
, value
, len
);
123 if (quote
&& (len
>= strlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
126 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
127 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
130 USHORT
* shortbuffer
= (USHORT
*)buffer
;
131 for (i
= 0; i
< len
; i
++)
132 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
135 /* writes any necessary encoding marker to the file */
136 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
138 DWORD dwBytesWritten
;
145 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
147 case ENCODING_UTF16LE
:
149 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
151 case ENCODING_UTF16BE
:
153 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
158 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
160 char write_buffer
[PROFILE_MAX_LINE_LEN
];
161 DWORD dwBytesWritten
;
163 TRACE("writing: %s\n", debugstr_wn(szLine
, len
));
168 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, sizeof(write_buffer
), NULL
, NULL
);
169 WriteFile(hFile
, write_buffer
, len
* sizeof(char), &dwBytesWritten
, NULL
);
172 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, sizeof(write_buffer
), NULL
, NULL
);
173 WriteFile(hFile
, write_buffer
, len
* sizeof(char), &dwBytesWritten
, NULL
);
175 case ENCODING_UTF16LE
:
176 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
178 case ENCODING_UTF16BE
:
179 PROFILE_ByteSwapShortBuffer(szLine
, len
);
180 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
183 FIXME("encoding type %d not implemented\n", encoding
);
187 /***********************************************************************
190 * Save a profile tree to a file.
192 static void PROFILE_Save( HANDLE hFile
, PROFILESECTION
*section
, ENCODING encoding
)
194 static const WCHAR wSectionFormat
[] = {'\r','\n','[','%','s',']','\r','\n',0};
195 static const WCHAR wNameFormat
[] = {'%','s',0};
196 static const WCHAR wValueFormat
[] = {'=','%','s',0};
197 static const WCHAR wNewLine
[] = {'\r','\n',0};
199 WCHAR szLine
[PROFILE_MAX_LINE_LEN
];
202 PROFILE_WriteMarker(hFile
, encoding
);
204 for ( ; section
; section
= section
->next
)
206 if (section
->name
[0])
208 len
+= snprintfW( szLine
+ len
, PROFILE_MAX_LINE_LEN
- len
, wSectionFormat
, section
->name
);
209 PROFILE_WriteLine( hFile
, szLine
, len
, encoding
);
213 for (key
= section
->key
; key
; key
= key
->next
)
215 len
+= snprintfW( szLine
+ len
, PROFILE_MAX_LINE_LEN
- len
, wNameFormat
, key
->name
);
217 len
+= snprintfW( szLine
+ len
, PROFILE_MAX_LINE_LEN
- len
, wValueFormat
, key
->value
);
218 len
+= snprintfW( szLine
+ len
, PROFILE_MAX_LINE_LEN
- len
, wNewLine
);
219 PROFILE_WriteLine( hFile
, szLine
, len
, encoding
);
226 /***********************************************************************
229 * Free a profile tree.
231 static void PROFILE_Free( PROFILESECTION
*section
)
233 PROFILESECTION
*next_section
;
234 PROFILEKEY
*key
, *next_key
;
236 for ( ; section
; section
= next_section
)
238 for (key
= section
->key
; key
; key
= next_key
)
240 next_key
= key
->next
;
241 if (key
->value
) HeapFree( GetProcessHeap(), 0, key
->value
);
242 HeapFree( GetProcessHeap(), 0, key
);
244 next_section
= section
->next
;
245 HeapFree( GetProcessHeap(), 0, section
);
249 /* returns 1 if a character white space else 0 */
250 static inline int PROFILE_isspaceW(WCHAR c
)
252 if (isspaceW(c
)) return 1;
253 if (c
=='\r' || c
==0x1a) return 1;
254 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
258 static inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
260 DWORD flags
= IS_TEXT_UNICODE_SIGNATURE
|
261 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
262 IS_TEXT_UNICODE_ODD_LENGTH
;
263 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
265 *len
= sizeof(bom_utf8
);
266 return ENCODING_UTF8
;
268 RtlIsTextUnicode((void *)buffer
, *len
, &flags
);
269 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
271 *len
= sizeof(WCHAR
);
272 return ENCODING_UTF16LE
;
274 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
276 *len
= sizeof(WCHAR
);
277 return ENCODING_UTF16BE
;
280 return ENCODING_ANSI
;
283 static const WCHAR
* PROFILE_GetLine(const WCHAR
* szStart
, const WCHAR
* szEnd
)
285 return memchrW(szStart
, '\n', szEnd
- szStart
);
288 /***********************************************************************
291 * Load a profile tree from a file.
293 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
297 const WCHAR
*szLineStart
, *szLineEnd
;
298 const WCHAR
*szValueStart
, *szNameEnd
, *szEnd
;
300 PROFILESECTION
*section
, *first_section
;
301 PROFILESECTION
**next_section
;
302 PROFILEKEY
*key
, *prev_key
, **next_key
;
305 TRACE("%p\n", hFile
);
307 dwFileSize
= GetFileSize(hFile
, NULL
);
308 if (dwFileSize
== INVALID_FILE_SIZE
)
311 pBuffer
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
312 if (!pBuffer
) return NULL
;
314 if (!ReadFile(hFile
, pBuffer
, dwFileSize
, &dwFileSize
, NULL
))
316 HeapFree(GetProcessHeap(), 0, pBuffer
);
317 WARN("Error %ld reading file\n", GetLastError());
321 *pEncoding
= PROFILE_DetectTextEncoding(pBuffer
, &len
);
322 /* len is set to the number of bytes in the character marker.
323 * we want to skip these bytes */
324 pBuffer
= (char *)pBuffer
+ len
;
329 TRACE("ANSI encoding\n");
331 len
= MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
332 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
335 HeapFree(GetProcessHeap(), 0, pBuffer
);
338 MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
339 szEnd
= szFile
+ len
;
342 TRACE("UTF8 encoding\n");
344 len
= MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
345 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
348 HeapFree(GetProcessHeap(), 0, pBuffer
);
351 MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
352 szEnd
= szFile
+ len
;
354 case ENCODING_UTF16LE
:
355 TRACE("UTF16 Little Endian encoding\n");
356 szFile
= (WCHAR
*)pBuffer
+ 1;
357 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
359 case ENCODING_UTF16BE
:
360 TRACE("UTF16 Big Endian encoding\n");
361 szFile
= (WCHAR
*)pBuffer
+ 1;
362 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
363 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
366 FIXME("encoding type %d not implemented\n", *pEncoding
);
367 HeapFree(GetProcessHeap(), 0, pBuffer
);
371 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
372 if(first_section
== NULL
)
374 if (szFile
!= pBuffer
)
375 HeapFree(GetProcessHeap(), 0, szFile
);
376 HeapFree(GetProcessHeap(), 0, pBuffer
);
379 first_section
->name
[0] = 0;
380 first_section
->key
= NULL
;
381 first_section
->next
= NULL
;
382 next_section
= &first_section
->next
;
383 next_key
= &first_section
->key
;
385 szLineEnd
= szFile
- 1; /* will be increased to correct value in loop */
389 szLineStart
= szLineEnd
+ 1;
390 if (szLineStart
>= szEnd
)
392 szLineEnd
= PROFILE_GetLine(szLineStart
, szEnd
);
397 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
)) szLineStart
++;
399 if (szLineStart
>= szLineEnd
) continue;
401 if (*szLineStart
== '[') /* section start */
403 const WCHAR
* szSectionEnd
;
404 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
406 WARN("Invalid section header at line %d: %s\n",
407 line
, debugstr_wn(szLineStart
, (int)(szLineEnd
- szLineStart
)) );
412 len
= (int)(szSectionEnd
- szLineStart
);
413 /* no need to allocate +1 for NULL terminating character as
414 * already included in structure */
415 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
417 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
418 section
->name
[len
] = '\0';
420 section
->next
= NULL
;
421 *next_section
= section
;
422 next_section
= §ion
->next
;
423 next_key
= §ion
->key
;
426 TRACE("New section: %s\n", debugstr_w(section
->name
));
432 /* get rid of white space at the end of the line */
433 while ((szLineEnd
> szLineStart
) && ((*szLineEnd
== '\n') || PROFILE_isspaceW(*szLineEnd
))) szLineEnd
--;
435 /* line end should be pointing to character *after* the last wanted character */
438 /* get rid of white space after the name and before the start
440 if ((szNameEnd
= szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
442 szNameEnd
= szValueStart
- 1;
443 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(*szNameEnd
)) szNameEnd
--;
445 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
448 szNameEnd
= szLineEnd
- 1;
449 /* name end should be pointing to character *after* the last wanted character */
452 len
= (int)(szNameEnd
- szLineStart
);
454 if (len
|| !prev_key
|| *prev_key
->name
)
456 /* no need to allocate +1 for NULL terminating character as
457 * already included in structure */
458 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
459 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
460 key
->name
[len
] = '\0';
461 if (szValueStart
&& szValueStart
< szLineEnd
)
463 len
= (int)(szLineEnd
- szValueStart
);
464 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
465 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
466 key
->value
[len
] = '\0';
468 else key
->value
= NULL
;
472 next_key
= &key
->next
;
475 TRACE("New key: name=%s, value=%s\n",
476 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
479 if (szFile
!= pBuffer
)
480 HeapFree(GetProcessHeap(), 0, szFile
);
481 HeapFree(GetProcessHeap(), 0, pBuffer
);
482 return first_section
;
486 /***********************************************************************
487 * PROFILE_DeleteSection
489 * Delete a section from a profile tree.
491 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
495 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
497 PROFILESECTION
*to_del
= *section
;
498 *section
= to_del
->next
;
500 PROFILE_Free( to_del
);
503 section
= &(*section
)->next
;
509 /***********************************************************************
512 * Delete a key from a profile tree.
514 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
515 LPCWSTR section_name
, LPCWSTR key_name
)
519 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
521 PROFILEKEY
**key
= &(*section
)->key
;
524 if (!strcmpiW( (*key
)->name
, key_name
))
526 PROFILEKEY
*to_del
= *key
;
528 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
529 HeapFree( GetProcessHeap(), 0, to_del
);
535 section
= &(*section
)->next
;
541 /***********************************************************************
542 * PROFILE_DeleteAllKeys
544 * Delete all keys from a profile tree.
546 void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
548 PROFILESECTION
**section
= &CurProfile
->section
;
551 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
553 PROFILEKEY
**key
= &(*section
)->key
;
556 PROFILEKEY
*to_del
= *key
;
558 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
559 HeapFree( GetProcessHeap(), 0, to_del
);
560 CurProfile
->changed
=TRUE
;
563 section
= &(*section
)->next
;
568 /***********************************************************************
571 * Find a key in a profile tree, optionally creating it.
573 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
574 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
579 while (PROFILE_isspaceW(*section_name
)) section_name
++;
580 p
= section_name
+ strlenW(section_name
) - 1;
581 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
582 seclen
= p
- section_name
+ 1;
584 while (PROFILE_isspaceW(*key_name
)) key_name
++;
585 p
= key_name
+ strlenW(key_name
) - 1;
586 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
587 keylen
= p
- key_name
+ 1;
591 if ( ((*section
)->name
[0])
592 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
593 && (((*section
)->name
)[seclen
] == '\0') )
595 PROFILEKEY
**key
= &(*section
)->key
;
599 /* If create_always is FALSE then we check if the keyname
600 * already exists. Otherwise we add it regardless of its
601 * existence, to allow keys to be added more than once in
606 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
607 && (((*key
)->name
)[keylen
] == '\0') )
612 if (!create
) return NULL
;
613 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
615 strcpyW( (*key
)->name
, key_name
);
616 (*key
)->value
= NULL
;
620 section
= &(*section
)->next
;
622 if (!create
) return NULL
;
623 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
624 if(*section
== NULL
) return NULL
;
625 strcpyW( (*section
)->name
, section_name
);
626 (*section
)->next
= NULL
;
627 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
628 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
630 HeapFree(GetProcessHeap(), 0, *section
);
633 strcpyW( (*section
)->key
->name
, key_name
);
634 (*section
)->key
->value
= NULL
;
635 (*section
)->key
->next
= NULL
;
636 return (*section
)->key
;
640 /***********************************************************************
643 * Flush the current profile to disk if changed.
645 static BOOL
PROFILE_FlushFile(void)
648 FILETIME LastWriteTime
;
652 WARN("No current profile!\n");
656 if (!CurProfile
->changed
) return TRUE
;
658 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
660 if (hFile
== INVALID_HANDLE_VALUE
)
662 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile
->filename
), GetLastError());
666 TRACE("Saving %s\n", debugstr_w(CurProfile
->filename
));
667 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
668 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
669 CurProfile
->LastWriteTime
=LastWriteTime
;
670 CloseHandle( hFile
);
671 CurProfile
->changed
= FALSE
;
676 /***********************************************************************
677 * PROFILE_ReleaseFile
679 * Flush the current profile to disk and remove it from the cache.
681 static void PROFILE_ReleaseFile(void)
684 PROFILE_Free( CurProfile
->section
);
685 if (CurProfile
->filename
) HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
686 CurProfile
->changed
= FALSE
;
687 CurProfile
->section
= NULL
;
688 CurProfile
->filename
= NULL
;
689 CurProfile
->encoding
= ENCODING_ANSI
;
690 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
694 /***********************************************************************
697 * Open a profile file, checking the cached file first.
699 static BOOL
PROFILE_Open( LPCWSTR filename
)
701 WCHAR windirW
[MAX_PATH
];
702 WCHAR buffer
[MAX_PATH
];
703 HANDLE hFile
= INVALID_HANDLE_VALUE
;
704 FILETIME LastWriteTime
;
706 PROFILE
*tempProfile
;
708 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
710 /* First time around */
713 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
715 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
716 if(MRUProfile
[i
] == NULL
) break;
717 MRUProfile
[i
]->changed
=FALSE
;
718 MRUProfile
[i
]->section
=NULL
;
719 MRUProfile
[i
]->filename
=NULL
;
720 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
721 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
724 GetWindowsDirectoryW( windirW
, MAX_PATH
);
726 if ((RtlDetermineDosPathNameType_U(filename
) == RELATIVE_PATH
) &&
727 !strchrW(filename
, '\\') && !strchrW(filename
, '/'))
729 static const WCHAR wszSeparator
[] = {'\\', 0};
730 strcpyW(buffer
, windirW
);
731 strcatW(buffer
, wszSeparator
);
732 strcatW(buffer
, filename
);
737 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
740 TRACE("path: %s\n", debugstr_w(buffer
));
742 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
744 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
746 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer
));
750 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
752 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
754 if ((MRUProfile
[i
]->filename
&& !strcmpW( buffer
, MRUProfile
[i
]->filename
)))
756 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile
[i
]->filename
), debugstr_w(buffer
));
760 tempProfile
=MRUProfile
[i
];
762 MRUProfile
[j
]=MRUProfile
[j
-1];
763 CurProfile
=tempProfile
;
765 if(memcmp(&CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
)))
766 TRACE("(%s): already opened (mru=%d)\n",
767 debugstr_w(buffer
), i
);
769 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
770 debugstr_w(buffer
), i
);
776 /* Flush the old current profile */
779 /* Make the oldest profile the current one only in order to get rid of it */
780 if(i
==N_CACHED_PROFILES
)
782 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
783 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
784 MRUProfile
[i
]=MRUProfile
[i
-1];
785 CurProfile
=tempProfile
;
787 if(CurProfile
->filename
) PROFILE_ReleaseFile();
789 /* OK, now that CurProfile is definitely free we assign it our new file */
790 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer
)+1) * sizeof(WCHAR
) );
791 strcpyW( CurProfile
->filename
, buffer
);
793 if (hFile
!= INVALID_HANDLE_VALUE
)
795 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
797 memcpy(&CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
));
801 /* Does not exist yet, we will create it in PROFILE_FlushFile */
802 WARN("profile file %s not found\n", debugstr_w(buffer
) );
808 /***********************************************************************
811 * Returns all keys of a section.
812 * If return_values is TRUE, also include the corresponding values.
814 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
815 LPWSTR buffer
, UINT len
, BOOL return_values
)
819 if(!buffer
) return 0;
821 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
825 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
828 for (key
= section
->key
; key
; key
= key
->next
)
831 if (!*key
->name
) continue; /* Skip empty lines */
832 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
833 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
834 len
-= strlenW(buffer
) + 1;
835 buffer
+= strlenW(buffer
) + 1;
838 if (return_values
&& key
->value
) {
840 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
841 len
-= strlenW(buffer
) + 1;
842 buffer
+= strlenW(buffer
) + 1;
847 /*If either lpszSection or lpszKey is NULL and the supplied
848 destination buffer is too small to hold all the strings,
849 the last string is truncated and followed by two null characters.
850 In this case, the return value is equal to cchReturnBuffer
858 section
= section
->next
;
860 buffer
[0] = buffer
[1] = '\0';
864 /* See GetPrivateProfileSectionNamesA for documentation */
865 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
869 PROFILESECTION
*section
;
871 TRACE("(%p, %d)\n", buffer
, len
);
882 section
= CurProfile
->section
;
883 while ((section
!=NULL
)) {
884 if (section
->name
[0]) {
885 l
= strlenW(section
->name
)+1;
888 strncpyW(buf
, section
->name
, f
-1);
895 strcpyW(buf
, section
->name
);
899 section
= section
->next
;
906 /***********************************************************************
909 * Get a profile string.
911 * Tests with GetPrivateProfileString16, W95a,
912 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
913 * section key_name def_val res buffer
914 * "set1" "1" "x" 43 [data]
915 * "set1" "1 " "x" 43 [data] (!)
916 * "set1" " 1 "' "x" 43 [data] (!)
917 * "set1" "" "x" 1 "x"
918 * "set1" "" "x " 1 "x" (!)
919 * "set1" "" " x " 3 " x" (!)
920 * "set1" NULL "x" 6 "1\02\03\0\0"
921 * "set1" "" "x" 1 "x"
922 * NULL "1" "x" 0 "" (!)
928 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
929 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
931 PROFILEKEY
*key
= NULL
;
932 static const WCHAR empty_strW
[] = { 0 };
934 if(!buffer
) return 0;
936 if (!def_val
) def_val
= empty_strW
;
941 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
944 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
945 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
947 TRACE("(%s,%s,%s): returning %s\n",
948 debugstr_w(section
), debugstr_w(key_name
),
949 debugstr_w(def_val
), debugstr_w(buffer
) );
950 return strlenW( buffer
);
952 /* no "else" here ! */
953 if (section
&& section
[0])
955 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
);
956 if (!buffer
[0]) /* no luck -> def_val */
958 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
959 ret
= strlenW(buffer
);
968 /***********************************************************************
971 * Set a profile string.
973 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
974 LPCWSTR value
, BOOL create_always
)
976 if (!key_name
) /* Delete a whole section */
978 TRACE("(%s)\n", debugstr_w(section_name
));
979 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
981 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
982 this is not an error on application's level.*/
984 else if (!value
) /* Delete a key */
986 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
987 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
988 section_name
, key_name
);
989 return TRUE
; /* same error handling as above */
991 else /* Set the key value */
993 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
994 key_name
, TRUE
, create_always
);
995 TRACE("(%s,%s,%s):\n",
996 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
997 if (!key
) return FALSE
;
1000 /* strip the leading spaces. We can safely strip \n\r and
1001 * friends too, they should not happen here anyway. */
1002 while (PROFILE_isspaceW(*value
)) value
++;
1004 if (!strcmpW( key
->value
, value
))
1006 TRACE(" no change needed\n" );
1007 return TRUE
; /* No change needed */
1009 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
1010 HeapFree( GetProcessHeap(), 0, key
->value
);
1012 else TRACE(" creating key\n" );
1013 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
1014 strcpyW( key
->value
, value
);
1015 CurProfile
->changed
= TRUE
;
1021 /********************* API functions **********************************/
1023 /***********************************************************************
1024 * GetProfileInt (KERNEL.57)
1026 UINT16 WINAPI
GetProfileInt16( LPCSTR section
, LPCSTR entry
, INT16 def_val
)
1028 return GetPrivateProfileInt16( section
, entry
, def_val
, "win.ini" );
1032 /***********************************************************************
1033 * GetProfileIntA (KERNEL32.@)
1035 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1037 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1040 /***********************************************************************
1041 * GetProfileIntW (KERNEL32.@)
1043 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1045 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1049 * if allow_section_name_copy is TRUE, allow the copying :
1050 * - of Section names if 'section' is NULL
1051 * - of Keys in a Section if 'entry' is NULL
1052 * (see MSDN doc for GetPrivateProfileString)
1054 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1055 LPCWSTR def_val
, LPWSTR buffer
,
1056 UINT len
, LPCWSTR filename
,
1057 BOOL allow_section_name_copy
)
1060 LPWSTR pDefVal
= NULL
;
1065 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1066 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1068 /* strip any trailing ' ' of def_val. */
1071 LPCWSTR p
= &def_val
[strlenW(def_val
)]; /* even "" works ! */
1079 if (*p
== ' ') /* ouch, contained trailing ' ' */
1081 int len
= (int)(p
- def_val
);
1082 pDefVal
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1083 strncpyW(pDefVal
, def_val
, len
);
1084 pDefVal
[len
] = '\0';
1088 pDefVal
= (LPWSTR
)def_val
;
1090 RtlEnterCriticalSection( &PROFILE_CritSect
);
1092 if (PROFILE_Open( filename
)) {
1093 if ((allow_section_name_copy
) && (section
== NULL
))
1094 ret
= PROFILE_GetSectionNames(buffer
, len
);
1096 /* PROFILE_GetString already handles the 'entry == NULL' case */
1097 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1099 lstrcpynW( buffer
, pDefVal
, len
);
1100 ret
= strlenW( buffer
);
1103 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1105 if (pDefVal
!= def_val
) /* allocated */
1106 HeapFree(GetProcessHeap(), 0, pDefVal
);
1108 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1113 /***********************************************************************
1114 * GetPrivateProfileString (KERNEL.128)
1116 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1117 LPCSTR def_val
, LPSTR buffer
,
1118 UINT16 len
, LPCSTR filename
)
1120 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1122 INT16 retW
, ret
= 0;
1124 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1125 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1126 else sectionW
.Buffer
= NULL
;
1127 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1128 else entryW
.Buffer
= NULL
;
1129 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1130 else def_valW
.Buffer
= NULL
;
1131 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1132 else filenameW
.Buffer
= NULL
;
1134 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1135 def_valW
.Buffer
, bufferW
, len
,
1136 filenameW
.Buffer
, FALSE
);
1139 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1146 ret
--; /* strip terminating 0 */
1149 RtlFreeUnicodeString(§ionW
);
1150 RtlFreeUnicodeString(&entryW
);
1151 RtlFreeUnicodeString(&def_valW
);
1152 RtlFreeUnicodeString(&filenameW
);
1153 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1157 /***********************************************************************
1158 * GetPrivateProfileStringA (KERNEL32.@)
1160 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1161 LPCSTR def_val
, LPSTR buffer
,
1162 UINT len
, LPCSTR filename
)
1164 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1168 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1169 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1170 else sectionW
.Buffer
= NULL
;
1171 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1172 else entryW
.Buffer
= NULL
;
1173 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1174 else def_valW
.Buffer
= NULL
;
1175 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1176 else filenameW
.Buffer
= NULL
;
1178 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1179 def_valW
.Buffer
, bufferW
, len
,
1183 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1190 ret
--; /* strip terminating 0 */
1193 RtlFreeUnicodeString(§ionW
);
1194 RtlFreeUnicodeString(&entryW
);
1195 RtlFreeUnicodeString(&def_valW
);
1196 RtlFreeUnicodeString(&filenameW
);
1197 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1201 /***********************************************************************
1202 * GetPrivateProfileStringW (KERNEL32.@)
1204 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1205 LPCWSTR def_val
, LPWSTR buffer
,
1206 UINT len
, LPCWSTR filename
)
1208 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section
), debugstr_w(entry
), debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1210 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1211 buffer
, len
, filename
, TRUE
);
1214 /***********************************************************************
1215 * GetProfileString (KERNEL.58)
1217 INT16 WINAPI
GetProfileString16( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1218 LPSTR buffer
, UINT16 len
)
1220 return GetPrivateProfileString16( section
, entry
, def_val
,
1221 buffer
, len
, "win.ini" );
1224 /***********************************************************************
1225 * GetProfileStringA (KERNEL32.@)
1227 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1228 LPSTR buffer
, UINT len
)
1230 return GetPrivateProfileStringA( section
, entry
, def_val
,
1231 buffer
, len
, "win.ini" );
1234 /***********************************************************************
1235 * GetProfileStringW (KERNEL32.@)
1237 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1238 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1240 return GetPrivateProfileStringW( section
, entry
, def_val
,
1241 buffer
, len
, wininiW
);
1244 /***********************************************************************
1245 * WriteProfileString (KERNEL.59)
1247 BOOL16 WINAPI
WriteProfileString16( LPCSTR section
, LPCSTR entry
,
1250 return WritePrivateProfileString16( section
, entry
, string
, "win.ini" );
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 * GetPrivateProfileInt (KERNEL.127)
1275 UINT16 WINAPI
GetPrivateProfileInt16( LPCSTR section
, LPCSTR entry
,
1276 INT16 def_val
, LPCSTR filename
)
1278 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1279 * here, but Win98SE doesn't care about this at all, so I deleted it.
1280 * AFAIR versions prior to Win9x had these limits, though. */
1281 return (INT16
)GetPrivateProfileIntA(section
,entry
,def_val
,filename
);
1284 /***********************************************************************
1285 * GetPrivateProfileIntW (KERNEL32.@)
1287 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1288 INT def_val
, LPCWSTR filename
)
1291 UNICODE_STRING bufferW
;
1295 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1296 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1300 if (len
+1 == sizeof(buffer
)/sizeof(WCHAR
)) FIXME("result may be wrong!\n");
1302 /* FIXME: if entry can be found but it's empty, then Win16 is
1303 * supposed to return 0 instead of def_val ! Difficult/problematic
1304 * to implement (every other failure also returns zero buffer),
1305 * thus wait until testing framework avail for making sure nothing
1306 * else gets broken that way. */
1307 if (!buffer
[0]) return (UINT
)def_val
;
1309 RtlInitUnicodeString( &bufferW
, buffer
);
1310 RtlUnicodeStringToInteger( &bufferW
, 10, &result
);
1314 /***********************************************************************
1315 * GetPrivateProfileIntA (KERNEL32.@)
1317 * FIXME: rewrite using unicode
1319 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1320 INT def_val
, LPCSTR filename
)
1322 UNICODE_STRING entryW
, filenameW
, sectionW
;
1324 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1325 else entryW
.Buffer
= NULL
;
1326 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1327 else filenameW
.Buffer
= NULL
;
1328 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1329 else sectionW
.Buffer
= NULL
;
1330 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1332 RtlFreeUnicodeString(§ionW
);
1333 RtlFreeUnicodeString(&filenameW
);
1334 RtlFreeUnicodeString(&entryW
);
1338 /***********************************************************************
1339 * GetPrivateProfileSection (KERNEL.418)
1341 INT16 WINAPI
GetPrivateProfileSection16( LPCSTR section
, LPSTR buffer
,
1342 UINT16 len
, LPCSTR filename
)
1344 return GetPrivateProfileSectionA( section
, buffer
, len
, filename
);
1347 /***********************************************************************
1348 * GetPrivateProfileSectionW (KERNEL32.@)
1350 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1351 DWORD len
, LPCWSTR filename
)
1355 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section
), buffer
, len
, debugstr_w(filename
));
1357 RtlEnterCriticalSection( &PROFILE_CritSect
);
1359 if (PROFILE_Open( filename
))
1360 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1362 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1367 /***********************************************************************
1368 * GetPrivateProfileSectionA (KERNEL32.@)
1370 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1371 DWORD len
, LPCSTR filename
)
1373 UNICODE_STRING sectionW
, filenameW
;
1377 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1378 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1379 else sectionW
.Buffer
= NULL
;
1380 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1381 else filenameW
.Buffer
= NULL
;
1383 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1386 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1402 RtlFreeUnicodeString(§ionW
);
1403 RtlFreeUnicodeString(&filenameW
);
1404 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1408 /***********************************************************************
1409 * GetProfileSection (KERNEL.419)
1411 INT16 WINAPI
GetProfileSection16( LPCSTR section
, LPSTR buffer
, UINT16 len
)
1413 return GetPrivateProfileSection16( section
, buffer
, len
, "win.ini" );
1416 /***********************************************************************
1417 * GetProfileSectionA (KERNEL32.@)
1419 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1421 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1424 /***********************************************************************
1425 * GetProfileSectionW (KERNEL32.@)
1427 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1429 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1433 /***********************************************************************
1434 * WritePrivateProfileString (KERNEL.129)
1436 BOOL16 WINAPI
WritePrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1437 LPCSTR string
, LPCSTR filename
)
1439 return WritePrivateProfileStringA(section
,entry
,string
,filename
);
1442 /***********************************************************************
1443 * WritePrivateProfileStringW (KERNEL32.@)
1445 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1446 LPCWSTR string
, LPCWSTR filename
)
1450 RtlEnterCriticalSection( &PROFILE_CritSect
);
1452 if (PROFILE_Open( filename
))
1454 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1456 PROFILE_FlushFile();
1457 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1461 FIXME("(NULL?,%s,%s,%s)?\n",
1462 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1464 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1465 PROFILE_FlushFile();
1470 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1474 /***********************************************************************
1475 * WritePrivateProfileStringA (KERNEL32.@)
1477 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1478 LPCSTR string
, LPCSTR filename
)
1480 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1483 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1484 else sectionW
.Buffer
= NULL
;
1485 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1486 else entryW
.Buffer
= NULL
;
1487 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1488 else stringW
.Buffer
= NULL
;
1489 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1490 else filenameW
.Buffer
= NULL
;
1492 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1493 stringW
.Buffer
, filenameW
.Buffer
);
1494 RtlFreeUnicodeString(§ionW
);
1495 RtlFreeUnicodeString(&entryW
);
1496 RtlFreeUnicodeString(&stringW
);
1497 RtlFreeUnicodeString(&filenameW
);
1501 /***********************************************************************
1502 * WritePrivateProfileSection (KERNEL.416)
1504 BOOL16 WINAPI
WritePrivateProfileSection16( LPCSTR section
,
1505 LPCSTR string
, LPCSTR filename
)
1507 return WritePrivateProfileSectionA( section
, string
, filename
);
1510 /***********************************************************************
1511 * WritePrivateProfileSectionW (KERNEL32.@)
1513 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1514 LPCWSTR string
, LPCWSTR filename
)
1519 RtlEnterCriticalSection( &PROFILE_CritSect
);
1521 if (PROFILE_Open( filename
)) {
1522 if (!section
&& !string
)
1523 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1524 else if (!string
) {/* delete the named section*/
1525 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1526 PROFILE_FlushFile();
1528 PROFILE_DeleteAllKeys(section
);
1531 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1532 strcpyW( buf
, string
);
1533 if((p
= strchrW( buf
, '='))) {
1535 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1537 HeapFree( GetProcessHeap(), 0, buf
);
1538 string
+= strlenW(string
)+1;
1540 PROFILE_FlushFile();
1544 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1548 /***********************************************************************
1549 * WritePrivateProfileSectionA (KERNEL32.@)
1551 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1552 LPCSTR string
, LPCSTR filename
)
1555 UNICODE_STRING sectionW
, filenameW
;
1564 while(*p
) p
+= strlen(p
) + 1;
1565 lenA
= p
- string
+ 1;
1566 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1567 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1568 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1570 else stringW
= NULL
;
1571 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1572 else sectionW
.Buffer
= NULL
;
1573 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1574 else filenameW
.Buffer
= NULL
;
1576 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1578 HeapFree(GetProcessHeap(), 0, stringW
);
1579 RtlFreeUnicodeString(§ionW
);
1580 RtlFreeUnicodeString(&filenameW
);
1584 /***********************************************************************
1585 * WriteProfileSection (KERNEL.417)
1587 BOOL16 WINAPI
WriteProfileSection16( LPCSTR section
, LPCSTR keys_n_values
)
1589 return WritePrivateProfileSection16( section
, keys_n_values
, "win.ini");
1592 /***********************************************************************
1593 * WriteProfileSectionA (KERNEL32.@)
1595 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1598 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1601 /***********************************************************************
1602 * WriteProfileSectionW (KERNEL32.@)
1604 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1606 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1609 /***********************************************************************
1610 * GetPrivateProfileSectionNames (KERNEL.143)
1612 WORD WINAPI
GetPrivateProfileSectionNames16( LPSTR buffer
, WORD size
,
1615 return GetPrivateProfileSectionNamesA(buffer
,size
,filename
);
1619 /***********************************************************************
1620 * GetProfileSectionNames (KERNEL.142)
1622 WORD WINAPI
GetProfileSectionNames16(LPSTR buffer
, WORD size
)
1625 return GetPrivateProfileSectionNamesA(buffer
,size
,"win.ini");
1629 /***********************************************************************
1630 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1632 * Returns the section names contained in the specified file.
1633 * FIXME: Where do we find this file when the path is relative?
1634 * The section names are returned as a list of strings with an extra
1635 * '\0' to mark the end of the list. Except for that the behavior
1636 * depends on the Windows version.
1639 * - if the buffer is 0 or 1 character long then it is as if it was of
1641 * - otherwise, if the buffer is to small only the section names that fit
1643 * - note that this means if the buffer was to small to return even just
1644 * the first section name then a single '\0' will be returned.
1645 * - the return value is the number of characters written in the buffer,
1646 * except if the buffer was too smal in which case len-2 is returned
1649 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1650 * '\0' and the return value is 0
1651 * - otherwise if the buffer is too small then the first section name that
1652 * does not fit is truncated so that the string list can be terminated
1653 * correctly (double '\0')
1654 * - the return value is the number of characters written in the buffer
1655 * except for the trailing '\0'. If the buffer is too small, then the
1656 * return value is len-2
1657 * - Win2000 has a bug that triggers when the section names and the
1658 * trailing '\0' fit exactly in the buffer. In that case the trailing
1661 * Wine implements the observed Win2000 behavior (except for the bug).
1663 * Note that when the buffer is big enough then the return value may be any
1664 * value between 1 and len-1 (or len in Win95), including len-2.
1666 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1671 RtlEnterCriticalSection( &PROFILE_CritSect
);
1673 if (PROFILE_Open( filename
))
1674 ret
= PROFILE_GetSectionNames(buffer
, size
);
1676 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1682 /***********************************************************************
1683 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1685 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1688 UNICODE_STRING filenameW
;
1692 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1693 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1694 else filenameW
.Buffer
= NULL
;
1696 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1699 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1707 RtlFreeUnicodeString(&filenameW
);
1708 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1712 /***********************************************************************
1713 * GetPrivateProfileStruct (KERNEL.407)
1715 BOOL16 WINAPI
GetPrivateProfileStruct16(LPCSTR section
, LPCSTR key
,
1716 LPVOID buf
, UINT16 len
, LPCSTR filename
)
1718 return GetPrivateProfileStructA( section
, key
, buf
, len
, filename
);
1721 /***********************************************************************
1722 * GetPrivateProfileStructW (KERNEL32.@)
1724 * Should match Win95's behaviour pretty much
1726 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1727 LPVOID buf
, UINT len
, LPCWSTR filename
)
1731 RtlEnterCriticalSection( &PROFILE_CritSect
);
1733 if (PROFILE_Open( filename
)) {
1734 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1736 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1737 if (((strlenW(k
->value
) - 2) / 2) == len
)
1744 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1745 /* check for invalid chars in ASCII coded hex string */
1746 for (p
=k
->value
; p
< end
; p
++)
1750 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1751 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1758 BOOL highnibble
= TRUE
;
1760 LPBYTE binbuf
= (LPBYTE
)buf
;
1762 end
-= 2; /* don't include checksum in output data */
1763 /* translate ASCII hex format into binary data */
1764 for (p
=k
->value
; p
< end
; p
++)
1768 (c
- 'A' + 10) : (c
- '0');
1775 *binbuf
++ = b
; /* feed binary data into output */
1776 chksum
+= b
; /* calculate checksum */
1778 highnibble
^= 1; /* toggle */
1780 /* retrieve stored checksum value */
1782 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1784 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1785 if (b
== (chksum
& 0xff)) /* checksums match ? */
1791 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1796 /***********************************************************************
1797 * GetPrivateProfileStructA (KERNEL32.@)
1799 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1800 LPVOID buffer
, UINT len
, LPCSTR filename
)
1802 UNICODE_STRING sectionW
, keyW
, filenameW
;
1805 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1806 else sectionW
.Buffer
= NULL
;
1807 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1808 else keyW
.Buffer
= NULL
;
1809 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1810 else filenameW
.Buffer
= NULL
;
1812 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1814 /* Do not translate binary data. */
1816 RtlFreeUnicodeString(§ionW
);
1817 RtlFreeUnicodeString(&keyW
);
1818 RtlFreeUnicodeString(&filenameW
);
1824 /***********************************************************************
1825 * WritePrivateProfileStruct (KERNEL.406)
1827 BOOL16 WINAPI
WritePrivateProfileStruct16 (LPCSTR section
, LPCSTR key
,
1828 LPVOID buf
, UINT16 bufsize
, LPCSTR filename
)
1830 return WritePrivateProfileStructA( section
, key
, buf
, bufsize
, filename
);
1833 /***********************************************************************
1834 * WritePrivateProfileStructW (KERNEL32.@)
1836 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1837 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1841 LPWSTR outstring
, p
;
1844 if (!section
&& !key
&& !buf
) /* flush the cache */
1845 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1847 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1848 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1850 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1851 *p
++ = hex
[*binbuf
>> 4];
1852 *p
++ = hex
[*binbuf
& 0xf];
1855 /* checksum is sum & 0xff */
1856 *p
++ = hex
[(sum
& 0xf0) >> 4];
1857 *p
++ = hex
[sum
& 0xf];
1860 RtlEnterCriticalSection( &PROFILE_CritSect
);
1862 if (PROFILE_Open( filename
)) {
1863 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1864 PROFILE_FlushFile();
1867 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1869 HeapFree( GetProcessHeap(), 0, outstring
);
1874 /***********************************************************************
1875 * WritePrivateProfileStructA (KERNEL32.@)
1877 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1878 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1880 UNICODE_STRING sectionW
, keyW
, filenameW
;
1883 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1884 else sectionW
.Buffer
= NULL
;
1885 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1886 else keyW
.Buffer
= NULL
;
1887 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1888 else filenameW
.Buffer
= NULL
;
1890 /* Do not translate binary data. */
1891 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1894 RtlFreeUnicodeString(§ionW
);
1895 RtlFreeUnicodeString(&keyW
);
1896 RtlFreeUnicodeString(&filenameW
);
1901 /***********************************************************************
1902 * WriteOutProfiles (KERNEL.315)
1904 void WINAPI
WriteOutProfiles16(void)
1906 RtlEnterCriticalSection( &PROFILE_CritSect
);
1907 PROFILE_FlushFile();
1908 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1911 /***********************************************************************
1912 * CloseProfileUserMapping (KERNEL32.@)
1914 BOOL WINAPI
CloseProfileUserMapping(void) {
1915 FIXME("(), stub!\n");
1916 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);