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
28 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
38 typedef struct tagPROFILEKEY
41 struct tagPROFILEKEY
*next
;
45 typedef struct tagPROFILESECTION
47 struct tagPROFILEKEY
*key
;
48 struct tagPROFILESECTION
*next
;
56 PROFILESECTION
*section
;
58 FILETIME LastWriteTime
;
63 #define N_CACHED_PROFILES 10
65 /* Cached profile files */
66 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
68 #define CurProfile (MRUProfile[0])
70 /* Check for comments in profile */
71 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
73 static const WCHAR emptystringW
[] = {0};
75 static RTL_CRITICAL_SECTION PROFILE_CritSect
;
76 static RTL_CRITICAL_SECTION_DEBUG critsect_debug
=
78 0, 0, &PROFILE_CritSect
,
79 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
80 0, 0, { 0, (DWORD
)(__FILE__
": PROFILE_CritSect") }
82 static RTL_CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
84 static const char hex
[16] = "0123456789ABCDEF";
87 static inline WCHAR
*memchrW( const WCHAR
*ptr
, WCHAR ch
, size_t n
)
90 for (end
= ptr
+ n
; ptr
< end
; ptr
++)
96 static inline WCHAR
*memrchrW( const WCHAR
*ptr
, WCHAR ch
, size_t n
)
98 const WCHAR
*end
, *ret
= NULL
;
99 for (end
= ptr
+ n
; ptr
< end
; ptr
++)
105 /***********************************************************************
108 * Copy the content of an entry into a buffer, removing quotes, and possibly
109 * translating environment variables.
111 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
119 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
121 if (value
[1] && (value
[wcslen(value
)-1] == *value
))
125 wcsncpy( buffer
, value
, len
);
126 if (quote
&& ((size_t)len
>= wcslen(value
)))
127 buffer
[wcslen(buffer
) - 1] = '\0';
131 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
132 static inline void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
135 USHORT
* shortbuffer
= (USHORT
*)buffer
;
136 for (i
= 0; i
< len
; i
++)
137 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
141 /* writes any necessary encoding marker to the file */
142 static inline void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
144 DWORD dwBytesWritten
;
151 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
153 case ENCODING_UTF16LE
:
155 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
157 case ENCODING_UTF16BE
:
159 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
165 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
168 int write_buffer_len
;
169 DWORD dwBytesWritten
;
171 DPRINT("writing: %.*S\n", len
, szLine
);
176 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
177 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
178 if (!write_buffer
) return;
179 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
180 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
181 HeapFree(GetProcessHeap(), 0, write_buffer
);
184 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
185 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
186 if (!write_buffer
) return;
187 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
188 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
189 HeapFree(GetProcessHeap(), 0, write_buffer
);
191 case ENCODING_UTF16LE
:
192 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
194 case ENCODING_UTF16BE
:
195 PROFILE_ByteSwapShortBuffer(szLine
, len
);
196 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
199 DPRINT1("encoding type %d not implemented\n", encoding
);
204 /***********************************************************************
207 * Save a profile tree to a file.
209 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
214 PROFILE_WriteMarker(hFile
, encoding
);
216 for ( ; section
; section
= section
->next
)
220 if (section
->name
[0])
221 len
+= wcslen(section
->name
) + 6;
223 for (key
= section
->key
; key
; key
= key
->next
)
225 len
+= wcslen(key
->name
) + 2;
227 len
+= wcslen(key
->value
) + 1;
230 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
235 if (section
->name
[0])
240 wcscpy( p
, section
->name
);
246 for (key
= section
->key
; key
; key
= key
->next
)
248 wcscpy( p
, key
->name
);
253 wcscpy( p
, key
->value
);
259 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
260 HeapFree(GetProcessHeap(), 0, buffer
);
265 /***********************************************************************
268 * Free a profile tree.
270 static void PROFILE_Free( PROFILESECTION
*section
)
272 PROFILESECTION
*next_section
;
273 PROFILEKEY
*key
, *next_key
;
275 for ( ; section
; section
= next_section
)
277 for (key
= section
->key
; key
; key
= next_key
)
279 next_key
= key
->next
;
281 HeapFree( GetProcessHeap(), 0, key
->value
);
282 HeapFree( GetProcessHeap(), 0, key
);
284 next_section
= section
->next
;
285 HeapFree( GetProcessHeap(), 0, section
);
290 /* returns 1 if a character white space else 0 */
291 static inline int PROFILE_isspaceW(WCHAR c
)
295 if (c
=='\r' || c
==0x1a)
297 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
302 static inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
304 DWORD flags
= IS_TEXT_UNICODE_SIGNATURE
|
305 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
306 IS_TEXT_UNICODE_ODD_LENGTH
;
307 if (*len
>= (int)sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
309 *len
= sizeof(bom_utf8
);
310 return ENCODING_UTF8
;
312 RtlIsTextUnicode((void *)buffer
, *len
, &flags
);
313 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
315 *len
= sizeof(WCHAR
);
316 return ENCODING_UTF16LE
;
318 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
320 *len
= sizeof(WCHAR
);
321 return ENCODING_UTF16BE
;
324 return ENCODING_ANSI
;
327 static const WCHAR
* PROFILE_GetLine(const WCHAR
* szStart
, const WCHAR
* szEnd
)
329 return memchrW(szStart
, '\n', szEnd
- szStart
);
332 /***********************************************************************
335 * Load a profile tree from a file.
337 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
341 const WCHAR
*szLineStart
, *szLineEnd
;
342 const WCHAR
*szValueStart
, *szNameEnd
, *szEnd
;
344 PROFILESECTION
*section
, *first_section
;
345 PROFILESECTION
**next_section
;
346 PROFILEKEY
*key
, *prev_key
, **next_key
;
349 DPRINT("%p\n", hFile
);
351 dwFileSize
= GetFileSize(hFile
, NULL
);
352 if (dwFileSize
== INVALID_FILE_SIZE
)
355 pBuffer
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
359 if (!ReadFile(hFile
, pBuffer
, dwFileSize
, &dwFileSize
, NULL
))
361 HeapFree(GetProcessHeap(), 0, pBuffer
);
362 DPRINT("Error %ld reading file\n", GetLastError());
367 *pEncoding
= PROFILE_DetectTextEncoding(pBuffer
, &len
);
368 /* len is set to the number of bytes in the character marker.
369 * we want to skip these bytes */
370 pBuffer
= (char *)pBuffer
+ len
;
375 DPRINT("ANSI encoding\n");
377 len
= MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
378 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
381 HeapFree(GetProcessHeap(), 0, pBuffer
);
384 MultiByteToWideChar(CP_ACP
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
385 szEnd
= szFile
+ len
;
389 DPRINT("UTF8 encoding\n");
391 len
= MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, NULL
, 0);
392 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
395 HeapFree(GetProcessHeap(), 0, pBuffer
);
398 MultiByteToWideChar(CP_UTF8
, 0, (char *)pBuffer
, dwFileSize
, szFile
, len
);
399 szEnd
= szFile
+ len
;
402 case ENCODING_UTF16LE
:
403 DPRINT("UTF16 Little Endian encoding\n");
404 szFile
= (WCHAR
*)pBuffer
+ 1;
405 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
408 case ENCODING_UTF16BE
:
409 DPRINT("UTF16 Big Endian encoding\n");
410 szFile
= (WCHAR
*)pBuffer
+ 1;
411 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
412 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
416 DPRINT("encoding type %d not implemented\n", *pEncoding
);
417 HeapFree(GetProcessHeap(), 0, pBuffer
);
421 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
422 if (first_section
== NULL
)
424 if (szFile
!= pBuffer
)
425 HeapFree(GetProcessHeap(), 0, szFile
);
426 HeapFree(GetProcessHeap(), 0, pBuffer
);
429 first_section
->name
[0] = 0;
430 first_section
->key
= NULL
;
431 first_section
->next
= NULL
;
432 next_section
= &first_section
->next
;
433 next_key
= &first_section
->key
;
435 szLineEnd
= szFile
- 1; /* will be increased to correct value in loop */
439 szLineStart
= szLineEnd
+ 1;
440 if (szLineStart
>= szEnd
)
442 szLineEnd
= PROFILE_GetLine(szLineStart
, szEnd
);
447 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
))
450 if (szLineStart
>= szLineEnd
)
453 if (*szLineStart
== '[') /* section start */
455 const WCHAR
* szSectionEnd
;
456 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
458 DPRINT("Invalid section header at line %d: %.*S\n",
459 line
, (int)(szLineEnd
- szLineStart
), szLineStart
);
464 len
= (int)(szSectionEnd
- szLineStart
);
465 /* no need to allocate +1 for NULL terminating character as
466 * already included in structure */
467 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
469 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
470 section
->name
[len
] = '\0';
472 section
->next
= NULL
;
473 *next_section
= section
;
474 next_section
= §ion
->next
;
475 next_key
= §ion
->key
;
478 DPRINT("New section: %S\n", section
->name
);
484 /* get rid of white space at the end of the line */
485 while ((szLineEnd
> szLineStart
) && ((*szLineEnd
== '\n') || PROFILE_isspaceW(*szLineEnd
)))
488 /* line end should be pointing to character *after* the last wanted character */
491 /* get rid of white space after the name and before the start
493 if ((szNameEnd
= szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
495 szNameEnd
= szValueStart
- 1;
496 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(*szNameEnd
))
499 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
))
503 szNameEnd
= szLineEnd
- 1;
504 /* name end should be pointing to character *after* the last wanted character */
507 len
= (int)(szNameEnd
- szLineStart
);
509 if (len
|| !prev_key
|| *prev_key
->name
)
511 /* no need to allocate +1 for NULL terminating character as
512 * already included in structure */
513 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
514 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
515 key
->name
[len
] = '\0';
518 len
= (int)(szLineEnd
- szValueStart
);
519 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
520 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
521 key
->value
[len
] = '\0';
523 else key
->value
= NULL
;
527 next_key
= &key
->next
;
530 DPRINT("New key: name=%S, value=%S\n",
531 key
->name
, key
->value
?key
->value
: L
"(none)");
534 if (szFile
!= pBuffer
)
535 HeapFree(GetProcessHeap(), 0, szFile
);
536 HeapFree(GetProcessHeap(), 0, pBuffer
);
537 return first_section
;
541 /***********************************************************************
542 * PROFILE_DeleteSection
544 * Delete a section from a profile tree.
546 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
550 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, name
))
552 PROFILESECTION
*to_del
= *section
;
553 *section
= to_del
->next
;
555 PROFILE_Free( to_del
);
558 section
= &(*section
)->next
;
564 /***********************************************************************
567 * Delete a key from a profile tree.
569 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
570 LPCWSTR section_name
, LPCWSTR key_name
)
574 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, section_name
))
576 PROFILEKEY
**key
= &(*section
)->key
;
579 if (!_wcsicmp( (*key
)->name
, key_name
))
581 PROFILEKEY
*to_del
= *key
;
584 HeapFree( GetProcessHeap(), 0, to_del
->value
);
585 HeapFree( GetProcessHeap(), 0, to_del
);
591 section
= &(*section
)->next
;
597 /***********************************************************************
598 * PROFILE_DeleteAllKeys
600 * Delete all keys from a profile tree.
602 void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
604 PROFILESECTION
**section
= &CurProfile
->section
;
607 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, section_name
))
609 PROFILEKEY
**key
= &(*section
)->key
;
612 PROFILEKEY
*to_del
= *key
;
615 HeapFree( GetProcessHeap(), 0, to_del
->value
);
616 HeapFree( GetProcessHeap(), 0, to_del
);
617 CurProfile
->changed
=TRUE
;
620 section
= &(*section
)->next
;
625 /***********************************************************************
628 * Find a key in a profile tree, optionally creating it.
630 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
631 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
636 while (PROFILE_isspaceW(*section_name
)) section_name
++;
637 p
= section_name
+ wcslen(section_name
) - 1;
638 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
639 seclen
= p
- section_name
+ 1;
641 while (PROFILE_isspaceW(*key_name
)) key_name
++;
642 p
= key_name
+ wcslen(key_name
) - 1;
643 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
644 keylen
= p
- key_name
+ 1;
648 if ( ((*section
)->name
[0])
649 && (!(_wcsnicmp( (*section
)->name
, section_name
, seclen
)))
650 && (((*section
)->name
)[seclen
] == '\0') )
652 PROFILEKEY
**key
= &(*section
)->key
;
656 /* If create_always is FALSE then we check if the keyname
657 * already exists. Otherwise we add it regardless of its
658 * existence, to allow keys to be added more than once in
663 if ( (!(_wcsnicmp( (*key
)->name
, key_name
, keylen
)))
664 && (((*key
)->name
)[keylen
] == '\0') )
671 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + wcslen(key_name
) * sizeof(WCHAR
) )))
673 wcscpy( (*key
)->name
, key_name
);
674 (*key
)->value
= NULL
;
678 section
= &(*section
)->next
;
680 if (!create
) return NULL
;
681 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + wcslen(section_name
) * sizeof(WCHAR
) );
682 if(*section
== NULL
) return NULL
;
683 wcscpy( (*section
)->name
, section_name
);
684 (*section
)->next
= NULL
;
685 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
686 sizeof(PROFILEKEY
) + wcslen(key_name
) * sizeof(WCHAR
) )))
688 HeapFree(GetProcessHeap(), 0, *section
);
691 wcscpy( (*section
)->key
->name
, key_name
);
692 (*section
)->key
->value
= NULL
;
693 (*section
)->key
->next
= NULL
;
694 return (*section
)->key
;
698 /***********************************************************************
701 * Flush the current profile to disk if changed.
703 static BOOL
PROFILE_FlushFile(void)
706 FILETIME LastWriteTime
;
710 DPRINT("No current profile!\n");
714 if (!CurProfile
->changed
) return TRUE
;
716 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
718 if (hFile
== INVALID_HANDLE_VALUE
)
720 DPRINT("could not save profile file %S (error was %ld)\n", CurProfile
->filename
, GetLastError());
724 DPRINT("Saving %S\n", CurProfile
->filename
);
725 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
726 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
727 CurProfile
->LastWriteTime
=LastWriteTime
;
728 CloseHandle( hFile
);
729 CurProfile
->changed
= FALSE
;
734 /***********************************************************************
735 * PROFILE_ReleaseFile
737 * Flush the current profile to disk and remove it from the cache.
739 static void PROFILE_ReleaseFile(void)
742 PROFILE_Free( CurProfile
->section
);
743 if (CurProfile
->filename
)
744 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
745 CurProfile
->changed
= FALSE
;
746 CurProfile
->section
= NULL
;
747 CurProfile
->filename
= NULL
;
748 CurProfile
->encoding
= ENCODING_ANSI
;
749 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
753 /***********************************************************************
756 * Open a profile file, checking the cached file first.
758 static BOOL
PROFILE_Open( LPCWSTR filename
)
760 WCHAR windirW
[MAX_PATH
];
761 WCHAR buffer
[MAX_PATH
];
762 HANDLE hFile
= INVALID_HANDLE_VALUE
;
763 FILETIME LastWriteTime
;
765 PROFILE
*tempProfile
;
767 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
769 /* First time around */
772 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
774 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
775 if(MRUProfile
[i
] == NULL
) break;
776 MRUProfile
[i
]->changed
=FALSE
;
777 MRUProfile
[i
]->section
=NULL
;
778 MRUProfile
[i
]->filename
=NULL
;
779 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
780 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
783 GetWindowsDirectoryW( windirW
, MAX_PATH
);
785 if ((RtlDetermineDosPathNameType_U(filename
) == RELATIVE_PATH
) &&
786 !wcschr(filename
, '\\') && !wcschr(filename
, '/'))
788 static const WCHAR wszSeparator
[] = {'\\', 0};
789 wcscpy(buffer
, windirW
);
790 wcscat(buffer
, wszSeparator
);
791 wcscat(buffer
, filename
);
796 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
799 DPRINT("path: %S\n", buffer
);
801 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
803 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
805 DPRINT("Error %ld opening file %S\n", GetLastError(), buffer
);
809 for(i
= 0; i
< N_CACHED_PROFILES
; i
++)
811 if ((MRUProfile
[i
]->filename
&& !wcscmp( buffer
, MRUProfile
[i
]->filename
)))
813 DPRINT("MRU Filename: %S, new filename: %S\n", MRUProfile
[i
]->filename
, buffer
);
817 tempProfile
=MRUProfile
[i
];
818 for (j
= i
; j
> 0; j
--)
819 MRUProfile
[j
] = MRUProfile
[j
-1];
820 CurProfile
=tempProfile
;
822 if (hFile
!= INVALID_HANDLE_VALUE
)
823 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
825 LastWriteTime
.dwHighDateTime
= LastWriteTime
.dwLowDateTime
= 0;
826 if (memcmp(&CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
)))
828 DPRINT("(%S): already opened (mru = %d)\n",
833 DPRINT("(%S): already opened, needs refreshing (mru = %d)\n",
836 if (hFile
!= INVALID_HANDLE_VALUE
)
842 /* Flush the old current profile */
845 /* Make the oldest profile the current one only in order to get rid of it */
846 if(i
== N_CACHED_PROFILES
)
848 tempProfile
= MRUProfile
[N_CACHED_PROFILES
-1];
849 for (i
= N_CACHED_PROFILES
- 1; i
> 0; i
--)
850 MRUProfile
[i
] = MRUProfile
[i
-1];
851 CurProfile
=tempProfile
;
854 if (CurProfile
->filename
)
855 PROFILE_ReleaseFile();
857 /* OK, now that CurProfile is definitely free we assign it our new file */
858 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (wcslen(buffer
)+1) * sizeof(WCHAR
) );
859 wcscpy( CurProfile
->filename
, buffer
);
861 if (hFile
!= INVALID_HANDLE_VALUE
)
863 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
864 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
869 /* Does not exist yet, we will create it in PROFILE_FlushFile */
870 DPRINT("profile file %S not found\n", buffer
);
876 /***********************************************************************
879 * Returns all keys of a section.
880 * If return_values is TRUE, also include the corresponding values.
882 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
883 LPWSTR buffer
, UINT len
, BOOL return_values
)
890 DPRINT("%S,%p,%u\n", section_name
, buffer
, len
);
894 if (section
->name
[0] && !_wcsicmp( section
->name
, section_name
))
897 for (key
= section
->key
; key
; key
= key
->next
)
902 continue; /* Skip empty lines */
903 if (IS_ENTRY_COMMENT(key
->name
))
904 continue; /* Skip comments */
905 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
906 len
-= wcslen(buffer
) + 1;
907 buffer
+= wcslen(buffer
) + 1;
910 if (return_values
&& key
->value
)
913 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
914 len
-= wcslen(buffer
) + 1;
915 buffer
+= wcslen(buffer
) + 1;
921 /*If either lpszSection or lpszKey is NULL and the supplied
922 destination buffer is too small to hold all the strings,
923 the last string is truncated and followed by two null characters.
924 In this case, the return value is equal to cchReturnBuffer
931 section
= section
->next
;
933 buffer
[0] = buffer
[1] = '\0';
937 /* See GetPrivateProfileSectionNamesA for documentation */
938 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
942 PROFILESECTION
*section
;
944 DPRINT("(%p, %d)\n", buffer
, len
);
956 section
= CurProfile
->section
;
957 while ((section
!=NULL
))
959 if (section
->name
[0])
961 l
= wcslen(section
->name
)+1;
966 wcsncpy(buf
, section
->name
, f
- 1);
973 wcscpy(buf
, section
->name
);
977 section
= section
->next
;
984 /***********************************************************************
987 * Get a profile string.
989 * Tests with GetPrivateProfileString16, W95a,
990 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
991 * section key_name def_val res buffer
992 * "set1" "1" "x" 43 [data]
993 * "set1" "1 " "x" 43 [data] (!)
994 * "set1" " 1 "' "x" 43 [data] (!)
995 * "set1" "" "x" 1 "x"
996 * "set1" "" "x " 1 "x" (!)
997 * "set1" "" " x " 3 " x" (!)
998 * "set1" NULL "x" 6 "1\02\03\0\0"
999 * "set1" "" "x" 1 "x"
1000 * NULL "1" "x" 0 "" (!)
1006 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
1007 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1009 PROFILEKEY
*key
= NULL
;
1010 static const WCHAR empty_strW
[] = { 0 };
1016 def_val
= empty_strW
;
1022 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
1025 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
1026 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
1028 DPRINT("(%S, %S, %S): returning %S\n",
1031 return wcslen(buffer
);
1034 /* no "else" here ! */
1035 if (section
&& section
[0])
1037 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
);
1038 if (!buffer
[0]) /* no luck -> def_val */
1040 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
1041 ret
= wcslen(buffer
);
1050 /***********************************************************************
1053 * Set a profile string.
1055 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
1056 LPCWSTR value
, BOOL create_always
)
1058 if (!key_name
) /* Delete a whole section */
1060 DPRINT("(%S)\n", section_name
);
1061 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
1063 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
1064 this is not an error on application's level.*/
1066 else if (!value
) /* Delete a key */
1068 DPRINT("(%S, %S)\n", section_name
, key_name
);
1069 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
1070 section_name
, key_name
);
1071 return TRUE
; /* same error handling as above */
1073 else /* Set the key value */
1075 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
1076 key_name
, TRUE
, create_always
);
1077 DPRINT("(%S, %S, %S):\n",
1078 section_name
, key_name
, value
);
1082 /* strip the leading spaces. We can safely strip \n\r and
1083 * friends too, they should not happen here anyway. */
1084 while (PROFILE_isspaceW(*value
))
1089 if (!wcscmp( key
->value
, value
))
1091 DPRINT(" no change needed\n" );
1092 return TRUE
; /* No change needed */
1094 DPRINT(" replacing %S\n", key
->value
);
1095 HeapFree( GetProcessHeap(), 0, key
->value
);
1099 DPRINT(" creating key\n");
1101 key
->value
= HeapAlloc( GetProcessHeap(), 0, (wcslen(value
) + 1) * sizeof(WCHAR
) );
1102 wcscpy( key
->value
, value
);
1103 CurProfile
->changed
= TRUE
;
1109 /********************* API functions **********************************/
1112 /***********************************************************************
1113 * GetProfileIntA (KERNEL32.@)
1115 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1117 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1120 /***********************************************************************
1121 * GetProfileIntW (KERNEL32.@)
1123 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1125 return GetPrivateProfileIntW( section
, entry
, def_val
, L
"win.ini" );
1129 * if allow_section_name_copy is TRUE, allow the copying :
1130 * - of Section names if 'section' is NULL
1131 * - of Keys in a Section if 'entry' is NULL
1132 * (see MSDN doc for GetPrivateProfileString)
1134 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1135 LPCWSTR def_val
, LPWSTR buffer
,
1136 UINT len
, LPCWSTR filename
,
1137 BOOL allow_section_name_copy
)
1140 LPCWSTR pDefVal
= NULL
;
1143 filename
= L
"win.ini";
1145 DPRINT("%S, %S, %S, %p, %u, %S\n",
1147 def_val
, buffer
, len
, filename
);
1149 /* strip any trailing ' ' of def_val. */
1152 LPCWSTR p
= &def_val
[wcslen(def_val
)]; /* even "" works ! */
1161 if (*p
== ' ') /* ouch, contained trailing ' ' */
1163 int len
= (int)(p
- def_val
);
1166 p
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1167 wcsncpy(p
, def_val
, len
);
1174 pDefVal
= (LPCWSTR
)def_val
;
1176 RtlEnterCriticalSection( &PROFILE_CritSect
);
1178 if (PROFILE_Open( filename
))
1180 if ((allow_section_name_copy
) && (section
== NULL
))
1181 ret
= PROFILE_GetSectionNames(buffer
, len
);
1183 /* PROFILE_GetString already handles the 'entry == NULL' case */
1184 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1188 wcsncpy( buffer
, pDefVal
, len
);
1189 ret
= wcslen( buffer
);
1192 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1194 if (pDefVal
!= def_val
) /* allocated */
1195 HeapFree(GetProcessHeap(), 0, (void*)pDefVal
);
1197 DPRINT("returning %S, %d\n", buffer
, ret
);
1203 /***********************************************************************
1204 * GetPrivateProfileStringA (KERNEL32.@)
1206 DWORD WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1207 LPCSTR def_val
, LPSTR buffer
,
1208 DWORD len
, LPCSTR filename
)
1210 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1214 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1215 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1216 else sectionW
.Buffer
= NULL
;
1217 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1218 else entryW
.Buffer
= NULL
;
1219 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1220 else def_valW
.Buffer
= NULL
;
1221 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1222 else filenameW
.Buffer
= NULL
;
1224 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1225 def_valW
.Buffer
, bufferW
, len
,
1229 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1236 ret
--; /* strip terminating 0 */
1239 RtlFreeUnicodeString(§ionW
);
1240 RtlFreeUnicodeString(&entryW
);
1241 RtlFreeUnicodeString(&def_valW
);
1242 RtlFreeUnicodeString(&filenameW
);
1243 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1248 /***********************************************************************
1249 * GetPrivateProfileStringW (KERNEL32.@)
1251 DWORD WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1252 LPCWSTR def_val
, LPWSTR buffer
,
1253 DWORD len
, LPCWSTR filename
)
1255 DPRINT("(%S, %S, %S, %p, %d, %S)\n",
1256 section
, entry
, def_val
, buffer
, len
, filename
);
1258 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1259 buffer
, len
, filename
, TRUE
);
1263 /***********************************************************************
1264 * GetProfileStringA (KERNEL32.@)
1266 DWORD WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1267 LPSTR buffer
, DWORD len
)
1269 return GetPrivateProfileStringA( section
, entry
, def_val
,
1270 buffer
, len
, "win.ini" );
1274 /***********************************************************************
1275 * GetProfileStringW (KERNEL32.@)
1277 DWORD WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1278 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
1280 return GetPrivateProfileStringW( section
, entry
, def_val
,
1281 buffer
, len
, L
"win.ini" );
1284 /***********************************************************************
1285 * WriteProfileStringA (KERNEL32.@)
1287 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1290 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1293 /***********************************************************************
1294 * WriteProfileStringW (KERNEL32.@)
1296 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1299 return WritePrivateProfileStringW( section
, entry
, string
, L
"win.ini" );
1303 /***********************************************************************
1304 * GetPrivateProfileIntW (KERNEL32.@)
1306 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1307 INT def_val
, LPCWSTR filename
)
1310 UNICODE_STRING bufferW
;
1314 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1315 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1319 if (len
+1 == sizeof(buffer
)/sizeof(WCHAR
))
1320 DPRINT1("result may be wrong!\n");
1322 /* FIXME: if entry can be found but it's empty, then Win16 is
1323 * supposed to return 0 instead of def_val ! Difficult/problematic
1324 * to implement (every other failure also returns zero buffer),
1325 * thus wait until testing framework avail for making sure nothing
1326 * else gets broken that way. */
1328 return (UINT
)def_val
;
1330 RtlInitUnicodeString( &bufferW
, buffer
);
1331 RtlUnicodeStringToInteger( &bufferW
, 10, &result
);
1335 /***********************************************************************
1336 * GetPrivateProfileIntA (KERNEL32.@)
1338 * FIXME: rewrite using unicode
1340 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1341 INT def_val
, LPCSTR filename
)
1343 UNICODE_STRING entryW
, filenameW
, sectionW
;
1345 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1346 else entryW
.Buffer
= NULL
;
1347 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1348 else filenameW
.Buffer
= NULL
;
1349 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1350 else sectionW
.Buffer
= NULL
;
1351 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1353 RtlFreeUnicodeString(§ionW
);
1354 RtlFreeUnicodeString(&filenameW
);
1355 RtlFreeUnicodeString(&entryW
);
1360 /***********************************************************************
1361 * GetPrivateProfileSectionW (KERNEL32.@)
1363 DWORD WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1364 DWORD len
, LPCWSTR filename
)
1368 DPRINT("(%S, %p, %ld, %S)\n",
1369 section
, buffer
, len
, filename
);
1371 RtlEnterCriticalSection( &PROFILE_CritSect
);
1373 if (PROFILE_Open( filename
))
1374 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1376 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1382 /***********************************************************************
1383 * GetPrivateProfileSectionA (KERNEL32.@)
1385 DWORD WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1386 DWORD len
, LPCSTR filename
)
1388 UNICODE_STRING sectionW
, filenameW
;
1392 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1393 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1394 else sectionW
.Buffer
= NULL
;
1395 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1396 else filenameW
.Buffer
= NULL
;
1398 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1401 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1417 RtlFreeUnicodeString(§ionW
);
1418 RtlFreeUnicodeString(&filenameW
);
1419 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1423 /***********************************************************************
1424 * GetProfileSectionA (KERNEL32.@)
1426 DWORD WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1428 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1431 /***********************************************************************
1432 * GetProfileSectionW (KERNEL32.@)
1434 DWORD WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1436 return GetPrivateProfileSectionW( section
, buffer
, len
, L
"win.ini" );
1440 /***********************************************************************
1441 * WritePrivateProfileStringW (KERNEL32.@)
1443 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1444 LPCWSTR string
, LPCWSTR filename
)
1448 RtlEnterCriticalSection( &PROFILE_CritSect
);
1450 if (PROFILE_Open( filename
))
1452 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1454 PROFILE_FlushFile();
1455 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1461 DPRINT1("(NULL?, %S, %S, %S)?\n",
1462 entry
, string
, filename
);
1466 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1467 PROFILE_FlushFile();
1472 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1476 /***********************************************************************
1477 * WritePrivateProfileStringA (KERNEL32.@)
1479 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1480 LPCSTR string
, LPCSTR filename
)
1482 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1485 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1486 else sectionW
.Buffer
= NULL
;
1487 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1488 else entryW
.Buffer
= NULL
;
1489 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1490 else stringW
.Buffer
= NULL
;
1491 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1492 else filenameW
.Buffer
= NULL
;
1494 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1495 stringW
.Buffer
, filenameW
.Buffer
);
1496 RtlFreeUnicodeString(§ionW
);
1497 RtlFreeUnicodeString(&entryW
);
1498 RtlFreeUnicodeString(&stringW
);
1499 RtlFreeUnicodeString(&filenameW
);
1503 /***********************************************************************
1504 * WritePrivateProfileSectionW (KERNEL32.@)
1506 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1507 LPCWSTR string
, LPCWSTR filename
)
1512 RtlEnterCriticalSection( &PROFILE_CritSect
);
1514 if (PROFILE_Open( filename
))
1516 if (!section
&& !string
)
1518 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1522 /* delete the named section*/
1523 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1524 PROFILE_FlushFile();
1528 PROFILE_DeleteAllKeys(section
);
1532 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (wcslen(string
) + 1) * sizeof(WCHAR
) );
1533 wcscpy( buf
, string
);
1534 if ((p
= wcschr( buf
, L
'=')))
1537 ret
= PROFILE_SetString( section
, buf
, p
+ 1, TRUE
);
1539 HeapFree( GetProcessHeap(), 0, buf
);
1540 string
+= wcslen(string
) + 1;
1542 PROFILE_FlushFile();
1546 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1550 /***********************************************************************
1551 * WritePrivateProfileSectionA (KERNEL32.@)
1553 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1554 LPCSTR string
, LPCSTR filename
)
1557 UNICODE_STRING sectionW
, filenameW
;
1568 lenA
= p
- string
+ 1;
1569 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1570 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1571 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1573 else stringW
= NULL
;
1574 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1575 else sectionW
.Buffer
= NULL
;
1576 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1577 else filenameW
.Buffer
= NULL
;
1579 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1581 HeapFree(GetProcessHeap(), 0, stringW
);
1582 RtlFreeUnicodeString(§ionW
);
1583 RtlFreeUnicodeString(&filenameW
);
1587 /***********************************************************************
1588 * WriteProfileSectionA (KERNEL32.@)
1590 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1593 return WritePrivateProfileSectionA(section
, keys_n_values
, "win.ini");
1596 /***********************************************************************
1597 * WriteProfileSectionW (KERNEL32.@)
1599 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1601 return WritePrivateProfileSectionW(section
, keys_n_values
, L
"win.ini");
1605 /***********************************************************************
1606 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1608 * Returns the section names contained in the specified file.
1609 * FIXME: Where do we find this file when the path is relative?
1610 * The section names are returned as a list of strings with an extra
1611 * '\0' to mark the end of the list. Except for that the behavior
1612 * depends on the Windows version.
1615 * - if the buffer is 0 or 1 character long then it is as if it was of
1617 * - otherwise, if the buffer is to small only the section names that fit
1619 * - note that this means if the buffer was to small to return even just
1620 * the first section name then a single '\0' will be returned.
1621 * - the return value is the number of characters written in the buffer,
1622 * except if the buffer was too smal in which case len-2 is returned
1625 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1626 * '\0' and the return value is 0
1627 * - otherwise if the buffer is too small then the first section name that
1628 * does not fit is truncated so that the string list can be terminated
1629 * correctly (double '\0')
1630 * - the return value is the number of characters written in the buffer
1631 * except for the trailing '\0'. If the buffer is too small, then the
1632 * return value is len-2
1633 * - Win2000 has a bug that triggers when the section names and the
1634 * trailing '\0' fit exactly in the buffer. In that case the trailing
1637 * Wine implements the observed Win2000 behavior (except for the bug).
1639 * Note that when the buffer is big enough then the return value may be any
1640 * value between 1 and len-1 (or len in Win95), including len-2.
1642 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1647 RtlEnterCriticalSection( &PROFILE_CritSect
);
1649 if (PROFILE_Open( filename
))
1650 ret
= PROFILE_GetSectionNames(buffer
, size
);
1652 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1658 /***********************************************************************
1659 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1661 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1664 UNICODE_STRING filenameW
;
1668 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1670 RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1672 filenameW
.Buffer
= NULL
;
1674 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1677 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1685 RtlFreeUnicodeString(&filenameW
);
1686 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1690 /***********************************************************************
1691 * GetPrivateProfileStructW (KERNEL32.@)
1693 * Should match Win95's behaviour pretty much
1695 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1696 LPVOID buf
, UINT len
, LPCWSTR filename
)
1700 RtlEnterCriticalSection( &PROFILE_CritSect
);
1702 if (PROFILE_Open( filename
))
1704 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1707 DPRINT("value (at %p): %S\n", k
->value
, k
->value
);
1708 if (((wcslen(k
->value
) - 2) / 2) == len
)
1715 end
= k
->value
+ wcslen(k
->value
); /* -> '\0' */
1717 /* check for invalid chars in ASCII coded hex string */
1718 for (p
= k
->value
; p
< end
; p
++)
1722 DPRINT("invalid char '%x' in file %S->[%S]->%S !\n",
1723 *p
, filename
, section
, key
);
1731 BOOL highnibble
= TRUE
;
1733 LPBYTE binbuf
= (LPBYTE
)buf
;
1735 end
-= 2; /* don't include checksum in output data */
1736 /* translate ASCII hex format into binary data */
1737 for (p
= k
->value
; p
< end
; p
++)
1740 val
= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1747 *binbuf
++ = b
; /* feed binary data into output */
1748 chksum
+= b
; /* calculate checksum */
1750 highnibble
^= 1; /* toggle */
1753 /* retrieve stored checksum value */
1755 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1757 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1758 if (b
== (chksum
& 0xff)) /* checksums match ? */
1764 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1769 /***********************************************************************
1770 * GetPrivateProfileStructA (KERNEL32.@)
1772 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1773 LPVOID buffer
, UINT len
, LPCSTR filename
)
1775 UNICODE_STRING sectionW
, keyW
, filenameW
;
1778 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1779 else sectionW
.Buffer
= NULL
;
1780 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1781 else keyW
.Buffer
= NULL
;
1782 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1783 else filenameW
.Buffer
= NULL
;
1785 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1787 /* Do not translate binary data. */
1789 RtlFreeUnicodeString(§ionW
);
1790 RtlFreeUnicodeString(&keyW
);
1791 RtlFreeUnicodeString(&filenameW
);
1796 /***********************************************************************
1797 * WritePrivateProfileStructW (KERNEL32.@)
1799 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1800 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1804 LPWSTR outstring
, p
;
1807 if (!section
&& !key
&& !buf
) /* flush the cache */
1808 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1810 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1811 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1813 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++)
1815 *p
++ = hex
[*binbuf
>> 4];
1816 *p
++ = hex
[*binbuf
& 0xf];
1819 /* checksum is sum & 0xff */
1820 *p
++ = hex
[(sum
& 0xf0) >> 4];
1821 *p
++ = hex
[sum
& 0xf];
1824 RtlEnterCriticalSection( &PROFILE_CritSect
);
1826 if (PROFILE_Open( filename
))
1828 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1829 PROFILE_FlushFile();
1832 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1834 HeapFree( GetProcessHeap(), 0, outstring
);
1840 /***********************************************************************
1841 * WritePrivateProfileStructA (KERNEL32.@)
1844 WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1845 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1847 UNICODE_STRING sectionW
, keyW
, filenameW
;
1850 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1851 else sectionW
.Buffer
= NULL
;
1852 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1853 else keyW
.Buffer
= NULL
;
1854 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1855 else filenameW
.Buffer
= NULL
;
1857 /* Do not translate binary data. */
1858 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1861 RtlFreeUnicodeString(§ionW
);
1862 RtlFreeUnicodeString(&keyW
);
1863 RtlFreeUnicodeString(&filenameW
);
1868 /***********************************************************************
1869 * CloseProfileUserMapping
1872 CloseProfileUserMapping(VOID
)
1874 DPRINT1("(), stub!\n");
1875 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);