4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 static const char bom_utf8
[] = {0xEF,0xBB,0xBF};
37 typedef struct tagPROFILEKEY
40 struct tagPROFILEKEY
*next
;
44 typedef struct tagPROFILESECTION
46 struct tagPROFILEKEY
*key
;
47 struct tagPROFILESECTION
*next
;
55 PROFILESECTION
*section
;
57 FILETIME LastWriteTime
;
62 #define N_CACHED_PROFILES 10
64 /* Cached profile files */
65 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
67 #define CurProfile (MRUProfile[0])
69 /* Check for comments in profile */
70 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
72 static const WCHAR emptystringW
[] = {0};
73 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',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
},
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
++)
92 return (WCHAR
*)(ULONG_PTR
)ptr
;
96 static __inline WCHAR
*memrchrW( const WCHAR
*ptr
, WCHAR ch
, size_t n
)
100 for (end
= ptr
+ n
; ptr
< end
; ptr
++)
102 ret
= (WCHAR
*)(ULONG_PTR
)ptr
;
106 /***********************************************************************
109 * Copy the content of an entry into a buffer, removing quotes, and possibly
110 * translating environment variables.
112 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 lstrcpynW( buffer
, value
, len
);
126 if (quote
&& (len
>= (int)wcslen(value
))) buffer
[wcslen(buffer
)-1] = '\0';
129 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
130 static __inline
void PROFILE_ByteSwapShortBuffer(WCHAR
* buffer
, int len
)
133 USHORT
* shortbuffer
= buffer
;
134 for (i
= 0; i
< len
; i
++)
135 shortbuffer
[i
] = RtlUshortByteSwap(shortbuffer
[i
]);
138 /* writes any necessary encoding marker to the file */
139 static __inline
void PROFILE_WriteMarker(HANDLE hFile
, ENCODING encoding
)
141 DWORD dwBytesWritten
;
148 WriteFile(hFile
, bom_utf8
, sizeof(bom_utf8
), &dwBytesWritten
, NULL
);
150 case ENCODING_UTF16LE
:
152 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
154 case ENCODING_UTF16BE
:
156 WriteFile(hFile
, &bom
, sizeof(bom
), &dwBytesWritten
, NULL
);
161 static void PROFILE_WriteLine( HANDLE hFile
, WCHAR
* szLine
, int len
, ENCODING encoding
)
164 int write_buffer_len
;
165 DWORD dwBytesWritten
;
167 DPRINT("writing: %.*S\n", len
, szLine
);
172 write_buffer_len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
173 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
174 if (!write_buffer
) return;
175 len
= WideCharToMultiByte(CP_ACP
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
176 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
177 HeapFree(GetProcessHeap(), 0, write_buffer
);
180 write_buffer_len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, NULL
, 0, NULL
, NULL
);
181 write_buffer
= HeapAlloc(GetProcessHeap(), 0, write_buffer_len
);
182 if (!write_buffer
) return;
183 len
= WideCharToMultiByte(CP_UTF8
, 0, szLine
, len
, write_buffer
, write_buffer_len
, NULL
, NULL
);
184 WriteFile(hFile
, write_buffer
, len
, &dwBytesWritten
, NULL
);
185 HeapFree(GetProcessHeap(), 0, write_buffer
);
187 case ENCODING_UTF16LE
:
188 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
190 case ENCODING_UTF16BE
:
191 PROFILE_ByteSwapShortBuffer(szLine
, len
);
192 WriteFile(hFile
, szLine
, len
* sizeof(WCHAR
), &dwBytesWritten
, NULL
);
195 DPRINT1("encoding type %d not implemented\n", encoding
);
199 /***********************************************************************
202 * Save a profile tree to a file.
204 static void PROFILE_Save( HANDLE hFile
, const PROFILESECTION
*section
, ENCODING encoding
)
209 PROFILE_WriteMarker(hFile
, encoding
);
211 for ( ; section
; section
= section
->next
)
215 if (section
->name
[0]) len
+= wcslen(section
->name
) + 4;
217 for (key
= section
->key
; key
; key
= key
->next
)
219 len
+= wcslen(key
->name
) + 2;
220 if (key
->value
) len
+= wcslen(key
->value
) + 1;
223 buffer
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
227 if (section
->name
[0])
230 wcscpy( p
, section
->name
);
237 for (key
= section
->key
; key
; key
= key
->next
)
239 wcscpy( p
, key
->name
);
244 wcscpy( p
, key
->value
);
250 PROFILE_WriteLine( hFile
, buffer
, len
, encoding
);
251 HeapFree(GetProcessHeap(), 0, buffer
);
256 /***********************************************************************
259 * Free a profile tree.
261 static void PROFILE_Free( PROFILESECTION
*section
)
263 PROFILESECTION
*next_section
;
264 PROFILEKEY
*key
, *next_key
;
266 for ( ; section
; section
= next_section
)
268 for (key
= section
->key
; key
; key
= next_key
)
270 next_key
= key
->next
;
271 HeapFree( GetProcessHeap(), 0, key
->value
);
272 HeapFree( GetProcessHeap(), 0, key
);
274 next_section
= section
->next
;
275 HeapFree( GetProcessHeap(), 0, section
);
279 /* returns 1 if a character white space else 0 */
280 static __inline
int PROFILE_isspaceW(WCHAR c
)
282 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
283 return isspace(c
) || c
== 0x1a;
286 static __inline ENCODING
PROFILE_DetectTextEncoding(const void * buffer
, int * len
)
288 INT flags
= IS_TEXT_UNICODE_SIGNATURE
|
289 IS_TEXT_UNICODE_REVERSE_SIGNATURE
|
290 IS_TEXT_UNICODE_ODD_LENGTH
;
291 if (*len
>= sizeof(bom_utf8
) && !memcmp(buffer
, bom_utf8
, sizeof(bom_utf8
)))
293 *len
= sizeof(bom_utf8
);
294 return ENCODING_UTF8
;
296 RtlIsTextUnicode((void *)buffer
, *len
, &flags
);
297 if (flags
& IS_TEXT_UNICODE_SIGNATURE
)
299 *len
= sizeof(WCHAR
);
300 return ENCODING_UTF16LE
;
302 if (flags
& IS_TEXT_UNICODE_REVERSE_SIGNATURE
)
304 *len
= sizeof(WCHAR
);
305 return ENCODING_UTF16BE
;
308 return ENCODING_ANSI
;
312 /***********************************************************************
315 * Load a profile tree from a file.
317 static PROFILESECTION
*PROFILE_Load(HANDLE hFile
, ENCODING
* pEncoding
)
319 void *buffer_base
, *pBuffer
;
321 const WCHAR
*szLineStart
, *szLineEnd
;
322 const WCHAR
*szValueStart
, *szEnd
, *next_line
;
324 PROFILESECTION
*section
, *first_section
;
325 PROFILESECTION
**next_section
;
326 PROFILEKEY
*key
, *prev_key
, **next_key
;
329 DPRINT("%p\n", hFile
);
331 dwFileSize
= GetFileSize(hFile
, NULL
);
332 if (dwFileSize
== INVALID_FILE_SIZE
|| dwFileSize
== 0)
335 buffer_base
= HeapAlloc(GetProcessHeap(), 0 , dwFileSize
);
339 if (!ReadFile(hFile
, buffer_base
, dwFileSize
, &dwFileSize
, NULL
))
341 HeapFree(GetProcessHeap(), 0, buffer_base
);
342 DPRINT("Error %ld reading file\n", GetLastError());
346 *pEncoding
= PROFILE_DetectTextEncoding(buffer_base
, &len
);
347 /* len is set to the number of bytes in the character marker.
348 * we want to skip these bytes */
349 pBuffer
= (char *)buffer_base
+ len
;
354 DPRINT("ANSI encoding\n");
356 len
= MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, NULL
, 0);
357 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
360 HeapFree(GetProcessHeap(), 0, buffer_base
);
363 MultiByteToWideChar(CP_ACP
, 0, pBuffer
, dwFileSize
, szFile
, len
);
364 szEnd
= szFile
+ len
;
368 DPRINT("UTF8 encoding\n");
370 len
= MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, NULL
, 0);
371 szFile
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
374 HeapFree(GetProcessHeap(), 0, buffer_base
);
377 MultiByteToWideChar(CP_UTF8
, 0, pBuffer
, dwFileSize
, szFile
, len
);
378 szEnd
= szFile
+ len
;
381 case ENCODING_UTF16LE
:
382 DPRINT("UTF16 Little Endian encoding\n");
384 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
387 case ENCODING_UTF16BE
:
388 DPRINT("UTF16 Big Endian encoding\n");
390 szEnd
= (WCHAR
*)((char *)pBuffer
+ dwFileSize
);
391 PROFILE_ByteSwapShortBuffer(szFile
, dwFileSize
/ sizeof(WCHAR
));
395 DPRINT("encoding type %d not implemented\n", *pEncoding
);
396 HeapFree(GetProcessHeap(), 0, buffer_base
);
400 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
401 if (first_section
== NULL
)
403 if (szFile
!= pBuffer
)
404 HeapFree(GetProcessHeap(), 0, szFile
);
405 HeapFree(GetProcessHeap(), 0, buffer_base
);
408 first_section
->name
[0] = 0;
409 first_section
->key
= NULL
;
410 first_section
->next
= NULL
;
411 next_section
= &first_section
->next
;
412 next_key
= &first_section
->key
;
416 while (next_line
< szEnd
)
418 szLineStart
= next_line
;
419 next_line
= memchrW(szLineStart
, '\n', szEnd
- szLineStart
);
420 if (!next_line
) next_line
= memchrW(szLineStart
, '\r', szEnd
- szLineStart
);
421 if (!next_line
) next_line
= szEnd
;
423 szLineEnd
= next_line
;
427 /* get rid of white space */
428 while (szLineStart
< szLineEnd
&& PROFILE_isspaceW(*szLineStart
)) szLineStart
++;
429 while ((szLineEnd
> szLineStart
) && PROFILE_isspaceW(szLineEnd
[-1])) szLineEnd
--;
431 if (szLineStart
>= szLineEnd
) continue;
433 if (*szLineStart
== '[') /* section start */
435 const WCHAR
* szSectionEnd
;
436 if (!(szSectionEnd
= memrchrW( szLineStart
, ']', szLineEnd
- szLineStart
)))
438 DPRINT("Invalid section header at line %d: %.*S\n",
439 line
, (int)(szLineEnd
- szLineStart
), szLineStart
);
444 len
= (int)(szSectionEnd
- szLineStart
);
445 /* no need to allocate +1 for NULL terminating character as
446 * already included in structure */
447 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
449 memcpy(section
->name
, szLineStart
, len
* sizeof(WCHAR
));
450 section
->name
[len
] = '\0';
452 section
->next
= NULL
;
453 *next_section
= section
;
454 next_section
= §ion
->next
;
455 next_key
= §ion
->key
;
458 DPRINT("New section: %S\n", section
->name
);
464 /* get rid of white space after the name and before the start
466 len
= szLineEnd
- szLineStart
;
467 if ((szValueStart
= memchrW( szLineStart
, '=', szLineEnd
- szLineStart
)) != NULL
)
469 const WCHAR
*szNameEnd
= szValueStart
;
470 while ((szNameEnd
> szLineStart
) && PROFILE_isspaceW(szNameEnd
[-1])) szNameEnd
--;
471 len
= szNameEnd
- szLineStart
;
473 while (szValueStart
< szLineEnd
&& PROFILE_isspaceW(*szValueStart
)) szValueStart
++;
476 if (len
|| !prev_key
|| *prev_key
->name
)
478 /* no need to allocate +1 for NULL terminating character as
479 * already included in structure */
480 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
481 memcpy(key
->name
, szLineStart
, len
* sizeof(WCHAR
));
482 key
->name
[len
] = '\0';
485 len
= (int)(szLineEnd
- szValueStart
);
486 key
->value
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
487 if (!key
->value
) break;
488 memcpy(key
->value
, szValueStart
, len
* sizeof(WCHAR
));
489 key
->value
[len
] = '\0';
491 else key
->value
= NULL
;
495 next_key
= &key
->next
;
498 DPRINT("New key: name=%S, value=%S\n",
499 key
->name
, key
->value
?key
->value
: L
"(none)");
502 if (szFile
!= pBuffer
)
503 HeapFree(GetProcessHeap(), 0, szFile
);
504 HeapFree(GetProcessHeap(), 0, buffer_base
);
505 return first_section
;
509 /***********************************************************************
510 * PROFILE_DeleteSection
512 * Delete a section from a profile tree.
514 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
518 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, name
))
520 PROFILESECTION
*to_del
= *section
;
521 *section
= to_del
->next
;
523 PROFILE_Free( to_del
);
526 section
= &(*section
)->next
;
532 /***********************************************************************
535 * Delete a key from a profile tree.
537 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
538 LPCWSTR section_name
, LPCWSTR key_name
)
542 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, section_name
))
544 PROFILEKEY
**key
= &(*section
)->key
;
547 if (!_wcsicmp( (*key
)->name
, key_name
))
549 PROFILEKEY
*to_del
= *key
;
551 HeapFree( GetProcessHeap(), 0, to_del
->value
);
552 HeapFree( GetProcessHeap(), 0, to_del
);
558 section
= &(*section
)->next
;
564 /***********************************************************************
565 * PROFILE_DeleteAllKeys
567 * Delete all keys from a profile tree.
569 static void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
571 PROFILESECTION
**section
= &CurProfile
->section
;
574 if ((*section
)->name
[0] && !_wcsicmp( (*section
)->name
, section_name
))
576 PROFILEKEY
**key
= &(*section
)->key
;
579 PROFILEKEY
*to_del
= *key
;
581 HeapFree( GetProcessHeap(), 0, to_del
->value
);
582 HeapFree( GetProcessHeap(), 0, to_del
);
583 CurProfile
->changed
=TRUE
;
586 section
= &(*section
)->next
;
591 /***********************************************************************
594 * Find a key in a profile tree, optionally creating it.
596 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
597 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
602 while (PROFILE_isspaceW(*section_name
)) section_name
++;
604 p
= section_name
+ wcslen(section_name
) - 1;
608 while ((p
> section_name
) && PROFILE_isspaceW(*p
)) p
--;
609 seclen
= p
- section_name
+ 1;
611 while (PROFILE_isspaceW(*key_name
)) key_name
++;
613 p
= key_name
+ wcslen(key_name
) - 1;
617 while ((p
> key_name
) && PROFILE_isspaceW(*p
)) p
--;
618 keylen
= p
- key_name
+ 1;
622 if ( ((*section
)->name
[0])
623 && (!(_wcsnicmp( (*section
)->name
, section_name
, seclen
)))
624 && (((*section
)->name
)[seclen
] == '\0') )
626 PROFILEKEY
**key
= &(*section
)->key
;
630 /* If create_always is FALSE then we check if the keyname
631 * already exists. Otherwise we add it regardless of its
632 * existence, to allow keys to be added more than once in
637 if ( (!(_wcsnicmp( (*key
)->name
, key_name
, keylen
)))
638 && (((*key
)->name
)[keylen
] == '\0') )
645 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + wcslen(key_name
) * sizeof(WCHAR
) )))
647 wcscpy( (*key
)->name
, key_name
);
648 (*key
)->value
= NULL
;
652 section
= &(*section
)->next
;
654 if (!create
) return NULL
;
655 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + wcslen(section_name
) * sizeof(WCHAR
) );
656 if(*section
== NULL
) return NULL
;
657 wcscpy( (*section
)->name
, section_name
);
658 (*section
)->next
= NULL
;
659 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
660 sizeof(PROFILEKEY
) + wcslen(key_name
) * sizeof(WCHAR
) )))
662 HeapFree(GetProcessHeap(), 0, *section
);
665 wcscpy( (*section
)->key
->name
, key_name
);
666 (*section
)->key
->value
= NULL
;
667 (*section
)->key
->next
= NULL
;
668 return (*section
)->key
;
672 /***********************************************************************
675 * Flush the current profile to disk if changed.
677 static BOOL
PROFILE_FlushFile(void)
680 FILETIME LastWriteTime
;
684 DPRINT("No current profile!\n");
688 if (!CurProfile
->changed
) return TRUE
;
690 hFile
= CreateFileW(CurProfile
->filename
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
691 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
693 if (hFile
== INVALID_HANDLE_VALUE
)
695 DPRINT("could not save profile file %S (error was %ld)\n", CurProfile
->filename
, GetLastError());
699 DPRINT("Saving %S\n", CurProfile
->filename
);
700 PROFILE_Save( hFile
, CurProfile
->section
, CurProfile
->encoding
);
701 if(GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
))
702 CurProfile
->LastWriteTime
=LastWriteTime
;
703 CloseHandle( hFile
);
704 CurProfile
->changed
= FALSE
;
709 /***********************************************************************
710 * PROFILE_ReleaseFile
712 * Flush the current profile to disk and remove it from the cache.
714 static void PROFILE_ReleaseFile(void)
717 PROFILE_Free( CurProfile
->section
);
718 HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
719 CurProfile
->changed
= FALSE
;
720 CurProfile
->section
= NULL
;
721 CurProfile
->filename
= NULL
;
722 CurProfile
->encoding
= ENCODING_ANSI
;
723 ZeroMemory(&CurProfile
->LastWriteTime
, sizeof(CurProfile
->LastWriteTime
));
726 /***********************************************************************
728 * Compares a file time with the current time. If the file time is
729 * at least 2.1 seconds in the past, return true.
731 * Intended as cache safety measure: The time resolution on FAT is
732 * two seconds, so files that are not at least two seconds old might
733 * keep their time even on modification, so don't cache them.
735 static BOOL
is_not_current(FILETIME
* ft
)
738 LONGLONG ftll
, nowll
;
739 GetSystemTimeAsFileTime(&Now
);
740 ftll
= ((LONGLONG
)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
;
741 nowll
= ((LONGLONG
)Now
.dwHighDateTime
<< 32) + Now
.dwLowDateTime
;
742 DPRINT("%08x;%08x\n",(unsigned)ftll
+21000000,(unsigned)nowll
);
743 return ftll
+ 21000000 < nowll
;
746 /***********************************************************************
749 * Open a profile file, checking the cached file first.
751 static BOOL
PROFILE_Open( LPCWSTR filename
, BOOL write_access
)
753 WCHAR windirW
[MAX_PATH
];
754 WCHAR buffer
[MAX_PATH
];
755 HANDLE hFile
= INVALID_HANDLE_VALUE
;
756 FILETIME LastWriteTime
;
758 PROFILE
*tempProfile
;
760 ZeroMemory(&LastWriteTime
, sizeof(LastWriteTime
));
762 /* First time around */
765 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
767 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
768 if(MRUProfile
[i
] == NULL
) break;
769 MRUProfile
[i
]->changed
=FALSE
;
770 MRUProfile
[i
]->section
=NULL
;
771 MRUProfile
[i
]->filename
=NULL
;
772 MRUProfile
[i
]->encoding
=ENCODING_ANSI
;
773 ZeroMemory(&MRUProfile
[i
]->LastWriteTime
, sizeof(FILETIME
));
776 GetWindowsDirectoryW( windirW
, MAX_PATH
);
781 if ((RtlDetermineDosPathNameType_U(filename
) == RtlPathTypeRelative
) &&
782 !wcschr(filename
, '\\') && !wcschr(filename
, '/'))
784 static const WCHAR wszSeparator
[] = {'\\', 0};
785 wcscpy(buffer
, windirW
);
786 wcscat(buffer
, wszSeparator
);
787 wcscat(buffer
, filename
);
792 GetFullPathNameW(filename
, sizeof(buffer
)/sizeof(buffer
[0]), buffer
, &dummy
);
795 DPRINT("path: %S\n", buffer
);
797 hFile
= CreateFileW(buffer
, GENERIC_READ
| (write_access
? GENERIC_WRITE
: 0),
798 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
799 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
801 if ((hFile
== INVALID_HANDLE_VALUE
) && (GetLastError() != ERROR_FILE_NOT_FOUND
))
803 DPRINT("Error %ld opening file %S\n", GetLastError(), buffer
);
807 for(i
= 0; i
< N_CACHED_PROFILES
; i
++)
809 if ((MRUProfile
[i
]->filename
&& !wcscmp( buffer
, MRUProfile
[i
]->filename
)))
811 DPRINT("MRU Filename: %S, new filename: %S\n", MRUProfile
[i
]->filename
, buffer
);
815 tempProfile
=MRUProfile
[i
];
816 for (j
= i
; j
> 0; j
--)
817 MRUProfile
[j
] = MRUProfile
[j
-1];
818 CurProfile
=tempProfile
;
820 if (hFile
!= INVALID_HANDLE_VALUE
)
822 GetFileTime(hFile
, NULL
, NULL
, &LastWriteTime
);
823 if (!memcmp( &CurProfile
->LastWriteTime
, &LastWriteTime
, sizeof(FILETIME
) ) &&
824 is_not_current(&LastWriteTime
))
825 DPRINT("(%S): already opened (mru=%d)\n", buffer
, i
);
828 DPRINT("(%S): already opened, needs refreshing (mru=%d)\n", buffer
, i
);
829 PROFILE_Free(CurProfile
->section
);
830 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
831 CurProfile
->LastWriteTime
= LastWriteTime
;
835 else DPRINT("(%S): already opened (mru = %d)\n", buffer
, i
);
841 /* Flush the old current profile */
844 /* Make the oldest profile the current one only in order to get rid of it */
845 if(i
== N_CACHED_PROFILES
)
847 tempProfile
= MRUProfile
[N_CACHED_PROFILES
-1];
848 for (i
= N_CACHED_PROFILES
- 1; i
> 0; i
--)
849 MRUProfile
[i
] = MRUProfile
[i
-1];
850 CurProfile
=tempProfile
;
853 if (CurProfile
->filename
)
854 PROFILE_ReleaseFile();
856 /* OK, now that CurProfile is definitely free we assign it our new file */
857 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (wcslen(buffer
)+1) * sizeof(WCHAR
) );
858 if(CurProfile
->filename
== NULL
)
860 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
863 wcscpy( CurProfile
->filename
, buffer
);
865 if (hFile
!= INVALID_HANDLE_VALUE
)
867 CurProfile
->section
= PROFILE_Load(hFile
, &CurProfile
->encoding
);
868 GetFileTime(hFile
, NULL
, NULL
, &CurProfile
->LastWriteTime
);
873 /* Does not exist yet, we will create it in PROFILE_FlushFile */
874 DPRINT("profile file %S not found\n", buffer
);
880 /***********************************************************************
883 * Returns all keys of a section.
884 * If return_values is TRUE, also include the corresponding values.
886 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
887 LPWSTR buffer
, UINT len
, BOOL return_values
)
891 if(!buffer
) return 0;
893 DPRINT("%S,%p,%u\n", section_name
, buffer
, len
);
897 if (section
->name
[0] && !_wcsicmp( section
->name
, section_name
))
900 for (key
= section
->key
; key
; key
= key
->next
)
903 if (!*key
->name
) continue; /* Skip empty lines */
904 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
905 if (!return_values
&& !key
->value
) continue; /* Skip lines w.o. '=' */
906 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, 0 );
907 len
-= wcslen(buffer
) + 1;
908 buffer
+= wcslen(buffer
) + 1;
911 if (return_values
&& key
->value
)
914 PROFILE_CopyEntry ( buffer
, key
->value
, len
- 1, 0 );
915 len
-= wcslen(buffer
) + 1;
916 buffer
+= wcslen(buffer
) + 1;
922 /*If either lpszSection or lpszKey is NULL and the supplied
923 destination buffer is too small to hold all the strings,
924 the last string is truncated and followed by two null characters.
925 In this case, the return value is equal to cchReturnBuffer
932 section
= section
->next
;
934 buffer
[0] = buffer
[1] = '\0';
938 /* See GetPrivateProfileSectionNamesA for documentation */
939 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
943 PROFILESECTION
*section
;
945 DPRINT("(%p, %d)\n", buffer
, len
);
956 section
= CurProfile
->section
;
957 while ((section
!=NULL
)) {
958 if (section
->name
[0]) {
959 tmplen
= wcslen(section
->name
)+1;
960 if (tmplen
>= buflen
) {
962 memcpy(buf
, section
->name
, (buflen
- 1) * sizeof(WCHAR
));
969 memcpy(buf
, section
->name
, tmplen
* sizeof(WCHAR
));
973 section
= section
->next
;
980 /***********************************************************************
983 * Get a profile string.
985 * Tests with GetPrivateProfileString16, W95a,
986 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
987 * section key_name def_val res buffer
988 * "set1" "1" "x" 43 [data]
989 * "set1" "1 " "x" 43 [data] (!)
990 * "set1" " 1 "' "x" 43 [data] (!)
991 * "set1" "" "x" 1 "x"
992 * "set1" "" "x " 1 "x" (!)
993 * "set1" "" " x " 3 " x" (!)
994 * "set1" NULL "x" 6 "1\02\03\0\0"
995 * "set1" "" "x" 1 "x"
996 * NULL "1" "x" 0 "" (!)
1002 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
1003 LPCWSTR def_val
, LPWSTR buffer
, UINT len
, BOOL win32
)
1005 PROFILEKEY
*key
= NULL
;
1006 static const WCHAR empty_strW
[] = { 0 };
1008 if (!buffer
|| !len
) return 0;
1010 if (!def_val
) def_val
= empty_strW
;
1015 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
1016 return wcslen(buffer
);
1018 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
1019 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
1021 DPRINT("(%S, %S, %S): returning %S\n",
1024 return wcslen(buffer
);
1027 /* no "else" here ! */
1028 if (section
&& section
[0])
1030 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
);
1031 if (!buffer
[0]) /* no luck -> def_val */
1033 PROFILE_CopyEntry(buffer
, def_val
, len
, TRUE
);
1034 ret
= wcslen(buffer
);
1043 /***********************************************************************
1046 * Set a profile string.
1048 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
1049 LPCWSTR value
, BOOL create_always
)
1051 if (!key_name
) /* Delete a whole section */
1053 DPRINT("(%S)\n", section_name
);
1054 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
1056 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
1057 this is not an error on application's level.*/
1059 else if (!value
) /* Delete a key */
1061 DPRINT("(%S, %S)\n", section_name
, key_name
);
1062 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
1063 section_name
, key_name
);
1064 return TRUE
; /* same error handling as above */
1066 else /* Set the key value */
1068 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
1069 key_name
, TRUE
, create_always
);
1070 DPRINT("(%S, %S, %S):\n",
1071 section_name
, key_name
, value
);
1072 if (!key
) return FALSE
;
1074 /* strip the leading spaces. We can safely strip \n\r and
1075 * friends too, they should not happen here anyway. */
1076 while (PROFILE_isspaceW(*value
)) value
++;
1080 if (!wcscmp( key
->value
, value
))
1082 DPRINT(" no change needed\n" );
1083 return TRUE
; /* No change needed */
1085 DPRINT(" replacing %S\n", key
->value
);
1086 HeapFree( GetProcessHeap(), 0, key
->value
);
1090 DPRINT(" creating key\n");
1092 key
->value
= HeapAlloc( GetProcessHeap(), 0, (wcslen(value
) + 1) * sizeof(WCHAR
) );
1093 if(key
->value
== NULL
)
1095 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1098 wcscpy( key
->value
, value
);
1099 CurProfile
->changed
= TRUE
;
1105 /********************* API functions **********************************/
1108 /***********************************************************************
1109 * GetProfileIntA (KERNEL32.@)
1111 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1113 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1116 /***********************************************************************
1117 * GetProfileIntW (KERNEL32.@)
1119 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1121 return GetPrivateProfileIntW( section
, entry
, def_val
, L
"win.ini" );
1126 * - Section names if 'section' is NULL
1127 * - Keys in a Section if 'entry' is NULL
1128 * (see MSDN doc for GetPrivateProfileString)
1130 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1131 LPCWSTR def_val
, LPWSTR buffer
,
1132 UINT len
, LPCWSTR filename
,
1136 LPWSTR defval_tmp
= NULL
;
1138 DPRINT("%S, %S, %S, %p, %u, %S\n",
1139 section
, entry
, def_val
, buffer
, len
, filename
);
1141 /* strip any trailing ' ' of def_val. */
1144 LPCWSTR p
= def_val
+ wcslen(def_val
) - 1;
1146 while (p
> def_val
&& *p
== ' ')
1151 int len
= (int)(p
- def_val
) + 1;
1153 defval_tmp
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1154 if (!defval_tmp
) return 0;
1155 memcpy(defval_tmp
, def_val
, len
* sizeof(WCHAR
));
1156 defval_tmp
[len
] = '\0';
1157 def_val
= defval_tmp
;
1161 RtlEnterCriticalSection( &PROFILE_CritSect
);
1163 if (PROFILE_Open( filename
, FALSE
)) {
1164 if (win32
&& (section
== NULL
))
1165 ret
= PROFILE_GetSectionNames(buffer
, len
);
1167 /* PROFILE_GetString can handle the 'entry == NULL' case */
1168 ret
= PROFILE_GetString( section
, entry
, def_val
, buffer
, len
, win32
);
1169 } else if (buffer
&& def_val
) {
1170 lstrcpynW( buffer
, def_val
, len
);
1171 ret
= wcslen( buffer
);
1176 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1178 HeapFree(GetProcessHeap(), 0, defval_tmp
);
1180 DPRINT("returning %S, %d\n", buffer
, ret
);
1186 /***********************************************************************
1187 * GetPrivateProfileStringA (KERNEL32.@)
1189 DWORD WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1190 LPCSTR def_val
, LPSTR buffer
,
1191 DWORD len
, LPCSTR filename
)
1193 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1197 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1198 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1199 else sectionW
.Buffer
= NULL
;
1200 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1201 else entryW
.Buffer
= NULL
;
1202 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1203 else def_valW
.Buffer
= NULL
;
1204 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1205 else filenameW
.Buffer
= NULL
;
1207 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1208 def_valW
.Buffer
, bufferW
, len
,
1212 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1219 ret
--; /* strip terminating 0 */
1222 RtlFreeUnicodeString(§ionW
);
1223 RtlFreeUnicodeString(&entryW
);
1224 RtlFreeUnicodeString(&def_valW
);
1225 RtlFreeUnicodeString(&filenameW
);
1226 HeapFree(GetProcessHeap(), 0, bufferW
);
1230 /***********************************************************************
1231 * GetPrivateProfileStringW (KERNEL32.@)
1233 DWORD WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1234 LPCWSTR def_val
, LPWSTR buffer
,
1235 DWORD len
, LPCWSTR filename
)
1237 DPRINT("(%S, %S, %S, %p, %d, %S)\n",
1238 section
, entry
, def_val
, buffer
, len
, filename
);
1240 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1241 buffer
, len
, filename
, TRUE
);
1245 /***********************************************************************
1246 * GetProfileStringA (KERNEL32.@)
1248 DWORD WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1249 LPSTR buffer
, DWORD len
)
1251 return GetPrivateProfileStringA( section
, entry
, def_val
,
1252 buffer
, len
, "win.ini" );
1256 /***********************************************************************
1257 * GetProfileStringW (KERNEL32.@)
1259 DWORD WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1260 LPCWSTR def_val
, LPWSTR buffer
, DWORD len
)
1262 return GetPrivateProfileStringW( section
, entry
, def_val
,
1263 buffer
, len
, L
"win.ini" );
1266 /***********************************************************************
1267 * WriteProfileStringA (KERNEL32.@)
1269 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1272 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1275 /***********************************************************************
1276 * WriteProfileStringW (KERNEL32.@)
1278 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1281 return WritePrivateProfileStringW( section
, entry
, string
, L
"win.ini" );
1285 /***********************************************************************
1286 * GetPrivateProfileIntW (KERNEL32.@)
1288 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1289 INT def_val
, LPCWSTR filename
)
1292 UNICODE_STRING bufferW
;
1296 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1297 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1301 /* FIXME: if entry can be found but it's empty, then Win16 is
1302 * supposed to return 0 instead of def_val ! Difficult/problematic
1303 * to implement (every other failure also returns zero buffer),
1304 * thus wait until testing framework avail for making sure nothing
1305 * else gets broken that way. */
1306 if (!buffer
[0]) return (UINT
)def_val
;
1308 RtlInitUnicodeString( &bufferW
, buffer
);
1309 RtlUnicodeStringToInteger( &bufferW
, 0, &result
);
1313 /***********************************************************************
1314 * GetPrivateProfileIntA (KERNEL32.@)
1316 * FIXME: rewrite using unicode
1318 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1319 INT def_val
, LPCSTR filename
)
1321 UNICODE_STRING entryW
, filenameW
, sectionW
;
1323 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1324 else entryW
.Buffer
= NULL
;
1325 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1326 else filenameW
.Buffer
= NULL
;
1327 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1328 else sectionW
.Buffer
= NULL
;
1329 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1331 RtlFreeUnicodeString(§ionW
);
1332 RtlFreeUnicodeString(&filenameW
);
1333 RtlFreeUnicodeString(&entryW
);
1337 /***********************************************************************
1338 * GetPrivateProfileSectionW (KERNEL32.@)
1340 DWORD WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1341 DWORD len
, LPCWSTR filename
)
1345 if (!section
|| !buffer
)
1347 SetLastError(ERROR_INVALID_PARAMETER
);
1351 DPRINT("(%S, %p, %ld, %S)\n", section
, buffer
, len
, filename
);
1353 RtlEnterCriticalSection( &PROFILE_CritSect
);
1355 if (PROFILE_Open( filename
, FALSE
))
1356 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, TRUE
);
1358 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1363 /***********************************************************************
1364 * GetPrivateProfileSectionA (KERNEL32.@)
1366 DWORD WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1367 DWORD len
, LPCSTR filename
)
1369 UNICODE_STRING sectionW
, filenameW
;
1373 if (!section
|| !buffer
)
1375 SetLastError(ERROR_INVALID_PARAMETER
);
1379 bufferW
= HeapAlloc(GetProcessHeap(), 0, len
* 2 * sizeof(WCHAR
));
1382 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1386 RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1387 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1388 else filenameW
.Buffer
= NULL
;
1390 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
* 2, filenameW
.Buffer
);
1393 if (retW
== len
* 2 - 2) retW
++; /* overflow */
1394 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1395 if (!ret
|| ret
== len
) /* overflow */
1409 RtlFreeUnicodeString(§ionW
);
1410 RtlFreeUnicodeString(&filenameW
);
1411 HeapFree(GetProcessHeap(), 0, bufferW
);
1415 /***********************************************************************
1416 * GetProfileSectionA (KERNEL32.@)
1418 DWORD WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1420 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1423 /***********************************************************************
1424 * GetProfileSectionW (KERNEL32.@)
1426 DWORD WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1428 return GetPrivateProfileSectionW( section
, buffer
, len
, L
"win.ini" );
1432 /***********************************************************************
1433 * WritePrivateProfileStringW (KERNEL32.@)
1435 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1436 LPCWSTR string
, LPCWSTR filename
)
1440 RtlEnterCriticalSection( &PROFILE_CritSect
);
1442 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1444 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1446 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1449 else if (PROFILE_Open( filename
, TRUE
))
1452 SetLastError(ERROR_FILE_NOT_FOUND
);
1454 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1455 PROFILE_FlushFile();
1459 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1463 /***********************************************************************
1464 * WritePrivateProfileStringA (KERNEL32.@)
1466 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1467 LPCSTR string
, LPCSTR filename
)
1469 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1472 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1473 else sectionW
.Buffer
= NULL
;
1474 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1475 else entryW
.Buffer
= NULL
;
1476 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1477 else stringW
.Buffer
= NULL
;
1478 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1479 else filenameW
.Buffer
= NULL
;
1481 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1482 stringW
.Buffer
, filenameW
.Buffer
);
1483 RtlFreeUnicodeString(§ionW
);
1484 RtlFreeUnicodeString(&entryW
);
1485 RtlFreeUnicodeString(&stringW
);
1486 RtlFreeUnicodeString(&filenameW
);
1490 /***********************************************************************
1491 * WritePrivateProfileSectionW (KERNEL32.@)
1493 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1494 LPCWSTR string
, LPCWSTR filename
)
1499 RtlEnterCriticalSection( &PROFILE_CritSect
);
1501 if (!section
&& !string
)
1503 if (!filename
|| PROFILE_Open( filename
, TRUE
))
1505 if (CurProfile
) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1508 else if (PROFILE_Open( filename
, TRUE
)) {
1509 if (!string
) {/* delete the named section*/
1510 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1511 PROFILE_FlushFile();
1513 PROFILE_DeleteAllKeys(section
);
1516 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (wcslen(string
)+1) * sizeof(WCHAR
) );
1519 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1520 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1523 wcscpy( buf
, string
);
1524 if((p
= wcschr( buf
, '='))) {
1526 ret
= PROFILE_SetString( section
, buf
, p
+ 1, TRUE
);
1528 HeapFree( GetProcessHeap(), 0, buf
);
1529 string
+= wcslen(string
) + 1;
1531 PROFILE_FlushFile();
1535 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1539 /***********************************************************************
1540 * WritePrivateProfileSectionA (KERNEL32.@)
1542 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1543 LPCSTR string
, LPCSTR filename
)
1546 UNICODE_STRING sectionW
, filenameW
;
1555 while(*p
) p
+= strlen(p
) + 1;
1556 lenA
= p
- string
+ 1;
1557 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1558 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1559 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1561 else stringW
= NULL
;
1562 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1563 else sectionW
.Buffer
= NULL
;
1564 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1565 else filenameW
.Buffer
= NULL
;
1567 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1569 HeapFree(GetProcessHeap(), 0, stringW
);
1570 RtlFreeUnicodeString(§ionW
);
1571 RtlFreeUnicodeString(&filenameW
);
1575 /***********************************************************************
1576 * WriteProfileSectionA (KERNEL32.@)
1578 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1581 return WritePrivateProfileSectionA(section
, keys_n_values
, "win.ini");
1584 /***********************************************************************
1585 * WriteProfileSectionW (KERNEL32.@)
1587 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1589 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1593 /***********************************************************************
1594 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1596 * Returns the section names contained in the specified file.
1597 * FIXME: Where do we find this file when the path is relative?
1598 * The section names are returned as a list of strings with an extra
1599 * '\0' to mark the end of the list. Except for that the behavior
1600 * depends on the Windows version.
1603 * - if the buffer is 0 or 1 character long then it is as if it was of
1605 * - otherwise, if the buffer is too small only the section names that fit
1607 * - note that this means if the buffer was too small to return even just
1608 * the first section name then a single '\0' will be returned.
1609 * - the return value is the number of characters written in the buffer,
1610 * except if the buffer was too small in which case len-2 is returned
1613 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1614 * '\0' and the return value is 0
1615 * - otherwise if the buffer is too small then the first section name that
1616 * does not fit is truncated so that the string list can be terminated
1617 * correctly (double '\0')
1618 * - the return value is the number of characters written in the buffer
1619 * except for the trailing '\0'. If the buffer is too small, then the
1620 * return value is len-2
1621 * - Win2000 has a bug that triggers when the section names and the
1622 * trailing '\0' fit exactly in the buffer. In that case the trailing
1625 * Wine implements the observed Win2000 behavior (except for the bug).
1627 * Note that when the buffer is big enough then the return value may be any
1628 * value between 1 and len-1 (or len in Win95), including len-2.
1630 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1635 RtlEnterCriticalSection( &PROFILE_CritSect
);
1637 if (PROFILE_Open( filename
, FALSE
))
1638 ret
= PROFILE_GetSectionNames(buffer
, size
);
1640 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1646 /***********************************************************************
1647 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1649 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1652 UNICODE_STRING filenameW
;
1656 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1657 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1658 else filenameW
.Buffer
= NULL
;
1660 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1663 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+1, buffer
, size
-1, NULL
, NULL
);
1675 RtlFreeUnicodeString(&filenameW
);
1676 HeapFree(GetProcessHeap(), 0, bufferW
);
1680 /***********************************************************************
1681 * GetPrivateProfileStructW (KERNEL32.@)
1683 * Should match Win95's behaviour pretty much
1685 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1686 LPVOID buf
, UINT len
, LPCWSTR filename
)
1690 RtlEnterCriticalSection( &PROFILE_CritSect
);
1692 if (PROFILE_Open( filename
, FALSE
))
1694 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1697 DPRINT("value (at %p): %S\n", k
->value
, k
->value
);
1698 if (((wcslen(k
->value
) - 2) / 2) == len
)
1705 end
= k
->value
+ wcslen(k
->value
); /* -> '\0' */
1707 /* check for invalid chars in ASCII coded hex string */
1708 for (p
= k
->value
; p
< end
; p
++)
1712 DPRINT("invalid char '%x' in file %S->[%S]->%S !\n",
1713 *p
, filename
, section
, key
);
1721 BOOL highnibble
= TRUE
;
1723 LPBYTE binbuf
= buf
;
1725 end
-= 2; /* don't include checksum in output data */
1726 /* translate ASCII hex format into binary data */
1727 for (p
= k
->value
; p
< end
; p
++)
1730 val
= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1737 *binbuf
++ = b
; /* feed binary data into output */
1738 chksum
+= b
; /* calculate checksum */
1740 highnibble
^= 1; /* toggle */
1743 /* retrieve stored checksum value */
1745 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1747 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1748 if (b
== (chksum
& 0xff)) /* checksums match ? */
1754 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1759 /***********************************************************************
1760 * GetPrivateProfileStructA (KERNEL32.@)
1762 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1763 LPVOID buffer
, UINT len
, LPCSTR filename
)
1765 UNICODE_STRING sectionW
, keyW
, filenameW
;
1768 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1769 else sectionW
.Buffer
= NULL
;
1770 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1771 else keyW
.Buffer
= NULL
;
1772 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1773 else filenameW
.Buffer
= NULL
;
1775 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1777 /* Do not translate binary data. */
1779 RtlFreeUnicodeString(§ionW
);
1780 RtlFreeUnicodeString(&keyW
);
1781 RtlFreeUnicodeString(&filenameW
);
1787 /***********************************************************************
1788 * WritePrivateProfileStructW (KERNEL32.@)
1790 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1791 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1795 LPWSTR outstring
, p
;
1798 if (!section
&& !key
&& !buf
) /* flush the cache */
1799 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1801 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1802 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1803 if(outstring
== NULL
)
1805 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1809 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++)
1811 *p
++ = hex
[*binbuf
>> 4];
1812 *p
++ = hex
[*binbuf
& 0xf];
1815 /* checksum is sum & 0xff */
1816 *p
++ = hex
[(sum
& 0xf0) >> 4];
1817 *p
++ = hex
[sum
& 0xf];
1820 RtlEnterCriticalSection( &PROFILE_CritSect
);
1822 if (PROFILE_Open( filename
, TRUE
))
1824 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1825 PROFILE_FlushFile();
1828 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1830 HeapFree( GetProcessHeap(), 0, outstring
);
1835 /***********************************************************************
1836 * WritePrivateProfileStructA (KERNEL32.@)
1839 WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1840 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1842 UNICODE_STRING sectionW
, keyW
, filenameW
;
1845 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1846 else sectionW
.Buffer
= NULL
;
1847 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1848 else keyW
.Buffer
= NULL
;
1849 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1850 else filenameW
.Buffer
= NULL
;
1852 /* Do not translate binary data. */
1853 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1856 RtlFreeUnicodeString(§ionW
);
1857 RtlFreeUnicodeString(&keyW
);
1858 RtlFreeUnicodeString(&filenameW
);
1863 /***********************************************************************
1864 * CloseProfileUserMapping
1867 CloseProfileUserMapping(VOID
)
1869 DPRINT1("(), stub!\n");
1870 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1879 QueryWin31IniFilesMappedToRegistry(DWORD Unknown0
,
1884 DPRINT1("QueryWin31IniFilesMappedToRegistry not implemented\n");
1885 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);