comctl32/tests: Flush events before testing edit control IME messages.
[wine.git] / dlls / kernel32 / profile.c
blobbf1a91708968750d5758a6c6cfd3e13181ac8086
1 /*
2 * Profile functions
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
22 #include <string.h>
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winnls.h"
28 #include "winerror.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "shlwapi.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(profile);
36 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
38 typedef enum
40 ENCODING_ANSI = 1,
41 ENCODING_UTF8,
42 ENCODING_UTF16LE,
43 ENCODING_UTF16BE
44 } ENCODING;
46 typedef struct tagPROFILEKEY
48 WCHAR *value;
49 struct tagPROFILEKEY *next;
50 WCHAR name[1];
51 } PROFILEKEY;
53 typedef struct tagPROFILESECTION
55 struct tagPROFILEKEY *key;
56 struct tagPROFILESECTION *next;
57 WCHAR name[1];
58 } PROFILESECTION;
61 typedef struct
63 BOOL changed;
64 PROFILESECTION *section;
65 WCHAR *filename;
66 FILETIME LastWriteTime;
67 ENCODING encoding;
68 } PROFILE;
71 #define N_CACHED_PROFILES 10
73 /* Cached profile files */
74 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
76 #define CurProfile (MRUProfile[0])
78 /* Check for comments in profile */
79 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
81 static CRITICAL_SECTION PROFILE_CritSect;
82 static CRITICAL_SECTION_DEBUG critsect_debug =
84 0, 0, &PROFILE_CritSect,
85 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
86 0, 0, { (DWORD_PTR)(__FILE__ ": PROFILE_CritSect") }
88 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
90 static const char hex[16] = "0123456789ABCDEF";
92 /***********************************************************************
93 * PROFILE_CopyEntry
95 * Copy the content of an entry into a buffer, removing quotes, and possibly
96 * translating environment variables.
98 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len )
100 WCHAR quote = '\0';
102 if(!buffer) return;
104 if (*value == '\'' || *value == '\"')
106 if (value[1] && (value[lstrlenW(value)-1] == *value)) quote = *value++;
109 lstrcpynW( buffer, value, len );
110 if (quote && (len >= lstrlenW(value))) buffer[lstrlenW(buffer)-1] = '\0';
113 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
114 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
116 int i;
117 USHORT * shortbuffer = buffer;
118 for (i = 0; i < len; i++)
119 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
122 /* writes any necessary encoding marker to the file */
123 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
125 DWORD dwBytesWritten;
126 WCHAR bom;
127 switch (encoding)
129 case ENCODING_ANSI:
130 break;
131 case ENCODING_UTF8:
132 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
133 break;
134 case ENCODING_UTF16LE:
135 bom = 0xFEFF;
136 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
137 break;
138 case ENCODING_UTF16BE:
139 bom = 0xFFFE;
140 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
141 break;
145 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
147 char * write_buffer;
148 int write_buffer_len;
149 DWORD dwBytesWritten;
151 TRACE("writing: %s\n", debugstr_wn(szLine, len));
153 switch (encoding)
155 case ENCODING_ANSI:
156 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
157 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
158 if (!write_buffer) return;
159 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
160 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
161 HeapFree(GetProcessHeap(), 0, write_buffer);
162 break;
163 case ENCODING_UTF8:
164 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
165 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
166 if (!write_buffer) return;
167 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
168 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
169 HeapFree(GetProcessHeap(), 0, write_buffer);
170 break;
171 case ENCODING_UTF16LE:
172 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
173 break;
174 case ENCODING_UTF16BE:
175 PROFILE_ByteSwapShortBuffer(szLine, len);
176 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
177 break;
178 default:
179 FIXME("encoding type %d not implemented\n", encoding);
183 /***********************************************************************
184 * PROFILE_Save
186 * Save a profile tree to a file.
188 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
190 PROFILEKEY *key;
191 WCHAR *buffer, *p;
193 PROFILE_WriteMarker(hFile, encoding);
195 for ( ; section; section = section->next)
197 int len = 0;
199 if (section->name[0]) len += lstrlenW(section->name) + 4;
201 for (key = section->key; key; key = key->next)
203 len += lstrlenW(key->name) + 2;
204 if (key->value) len += lstrlenW(key->value) + 1;
207 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
208 if (!buffer) return;
210 p = buffer;
211 if (section->name[0])
213 *p++ = '[';
214 lstrcpyW( p, section->name );
215 p += lstrlenW(p);
216 *p++ = ']';
217 *p++ = '\r';
218 *p++ = '\n';
221 for (key = section->key; key; key = key->next)
223 lstrcpyW( p, key->name );
224 p += lstrlenW(p);
225 if (key->value)
227 *p++ = '=';
228 lstrcpyW( p, key->value );
229 p += lstrlenW(p);
231 *p++ = '\r';
232 *p++ = '\n';
234 PROFILE_WriteLine( hFile, buffer, len, encoding );
235 HeapFree(GetProcessHeap(), 0, buffer);
240 /***********************************************************************
241 * PROFILE_Free
243 * Free a profile tree.
245 static void PROFILE_Free( PROFILESECTION *section )
247 PROFILESECTION *next_section;
248 PROFILEKEY *key, *next_key;
250 for ( ; section; section = next_section)
252 for (key = section->key; key; key = next_key)
254 next_key = key->next;
255 HeapFree( GetProcessHeap(), 0, key->value );
256 HeapFree( GetProcessHeap(), 0, key );
258 next_section = section->next;
259 HeapFree( GetProcessHeap(), 0, section );
263 /* returns TRUE if a whitespace character, else FALSE */
264 static inline BOOL PROFILE_isspaceW(WCHAR c)
266 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
267 return (c >= 0x09 && c <= 0x0d) || c == 0x1a || c == 0x20;
270 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
272 int flags = IS_TEXT_UNICODE_SIGNATURE |
273 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
274 IS_TEXT_UNICODE_ODD_LENGTH;
275 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
277 *len = sizeof(bom_utf8);
278 return ENCODING_UTF8;
280 RtlIsTextUnicode(buffer, *len, &flags);
281 if (flags & IS_TEXT_UNICODE_SIGNATURE)
283 *len = sizeof(WCHAR);
284 return ENCODING_UTF16LE;
286 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
288 *len = sizeof(WCHAR);
289 return ENCODING_UTF16BE;
291 *len = 0;
292 return ENCODING_ANSI;
296 /***********************************************************************
297 * PROFILE_Load
299 * Load a profile tree from a file.
301 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
303 void *buffer_base, *pBuffer;
304 WCHAR * szFile;
305 const WCHAR *szLineStart, *szLineEnd;
306 const WCHAR *szValueStart, *szEnd, *next_line;
307 int len;
308 PROFILESECTION *section, *first_section;
309 PROFILESECTION **next_section;
310 PROFILEKEY *key, *prev_key, **next_key;
311 DWORD dwFileSize;
313 TRACE("%p\n", hFile);
315 dwFileSize = GetFileSize(hFile, NULL);
316 if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
317 return NULL;
319 buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
320 if (!buffer_base) return NULL;
322 if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
324 HeapFree(GetProcessHeap(), 0, buffer_base);
325 WARN("Error %ld reading file\n", GetLastError());
326 return NULL;
328 len = dwFileSize;
329 *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
330 /* len is set to the number of bytes in the character marker.
331 * we want to skip these bytes */
332 pBuffer = (char *)buffer_base + len;
333 dwFileSize -= len;
334 switch (*pEncoding)
336 case ENCODING_ANSI:
337 TRACE("ANSI encoding\n");
339 len = MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, NULL, 0);
340 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
341 if (!szFile)
343 HeapFree(GetProcessHeap(), 0, buffer_base);
344 return NULL;
346 MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, szFile, len);
347 szEnd = szFile + len;
348 break;
349 case ENCODING_UTF8:
350 TRACE("UTF8 encoding\n");
352 len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
353 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
354 if (!szFile)
356 HeapFree(GetProcessHeap(), 0, buffer_base);
357 return NULL;
359 MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
360 szEnd = szFile + len;
361 break;
362 case ENCODING_UTF16LE:
363 TRACE("UTF16 Little Endian encoding\n");
364 szFile = pBuffer;
365 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
366 break;
367 case ENCODING_UTF16BE:
368 TRACE("UTF16 Big Endian encoding\n");
369 szFile = pBuffer;
370 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
371 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
372 break;
373 default:
374 FIXME("encoding type %d not implemented\n", *pEncoding);
375 HeapFree(GetProcessHeap(), 0, buffer_base);
376 return NULL;
379 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
380 if(first_section == NULL)
382 if (szFile != pBuffer)
383 HeapFree(GetProcessHeap(), 0, szFile);
384 HeapFree(GetProcessHeap(), 0, buffer_base);
385 return NULL;
387 first_section->name[0] = 0;
388 first_section->key = NULL;
389 first_section->next = NULL;
390 next_section = &first_section->next;
391 next_key = &first_section->key;
392 prev_key = NULL;
393 next_line = szFile;
395 while (next_line < szEnd)
397 szLineStart = next_line;
398 while (next_line < szEnd && *next_line != '\n' && *next_line != '\r') next_line++;
399 while (next_line < szEnd && (*next_line == '\n' || *next_line == '\r')) next_line++;
400 szLineEnd = next_line;
402 /* get rid of white space */
403 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
404 while ((szLineEnd > szLineStart) && PROFILE_isspaceW(szLineEnd[-1])) szLineEnd--;
406 if (szLineStart >= szLineEnd) continue;
408 if (*szLineStart == '[') /* section start */
410 for (len = szLineEnd - szLineStart; len > 0; len--) if (szLineStart[len - 1] == ']') break;
411 if (!len)
413 WARN("Invalid section header: %s\n",
414 debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
416 else
418 szLineStart++;
419 len -= 2;
420 /* no need to allocate +1 for NULL terminating character as
421 * already included in structure */
422 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
423 break;
424 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
425 section->name[len] = '\0';
426 section->key = NULL;
427 section->next = NULL;
428 *next_section = section;
429 next_section = &section->next;
430 next_key = &section->key;
431 prev_key = NULL;
433 TRACE("New section: %s\n", debugstr_w(section->name));
435 continue;
439 /* get rid of white space after the name and before the start
440 * of the value */
441 len = szLineEnd - szLineStart;
442 for (szValueStart = szLineStart; szValueStart < szLineEnd; szValueStart++) if (*szValueStart == '=') break;
443 if (szValueStart < szLineEnd)
445 const WCHAR *szNameEnd = szValueStart;
446 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
447 len = szNameEnd - szLineStart;
448 szValueStart++;
449 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
451 else szValueStart = NULL;
453 if (len || !prev_key || *prev_key->name)
455 /* no need to allocate +1 for NULL terminating character as
456 * already included in structure */
457 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
458 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
459 key->name[len] = '\0';
460 if (szValueStart)
462 len = (int)(szLineEnd - szValueStart);
463 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
464 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
465 key->value[len] = '\0';
467 else key->value = NULL;
469 key->next = NULL;
470 *next_key = key;
471 next_key = &key->next;
472 prev_key = key;
474 TRACE("New key: name=%s, value=%s\n",
475 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
478 if (szFile != pBuffer)
479 HeapFree(GetProcessHeap(), 0, szFile);
480 HeapFree(GetProcessHeap(), 0, buffer_base);
481 return first_section;
485 /***********************************************************************
486 * PROFILE_DeleteKey
488 * Delete a key from a profile tree.
490 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
491 LPCWSTR section_name, LPCWSTR key_name )
493 while (*section)
495 if (!wcsicmp( (*section)->name, section_name ))
497 PROFILEKEY **key = &(*section)->key;
498 while (*key)
500 if (!wcsicmp( (*key)->name, key_name ))
502 PROFILEKEY *to_del = *key;
503 *key = to_del->next;
504 HeapFree( GetProcessHeap(), 0, to_del->value);
505 HeapFree( GetProcessHeap(), 0, to_del );
506 return TRUE;
508 key = &(*key)->next;
511 section = &(*section)->next;
513 return FALSE;
517 /***********************************************************************
518 * PROFILE_DeleteAllKeys
520 * Delete all keys from a profile tree.
522 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
524 PROFILESECTION **section= &CurProfile->section;
525 while (*section)
527 if (!wcsicmp( (*section)->name, section_name ))
529 PROFILEKEY **key = &(*section)->key;
530 while (*key)
532 PROFILEKEY *to_del = *key;
533 *key = to_del->next;
534 HeapFree( GetProcessHeap(), 0, to_del->value);
535 HeapFree( GetProcessHeap(), 0, to_del );
536 CurProfile->changed =TRUE;
539 section = &(*section)->next;
544 /***********************************************************************
545 * PROFILE_Find
547 * Find a key in a profile tree, optionally creating it.
549 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
550 LPCWSTR key_name, BOOL create, BOOL create_always )
552 LPCWSTR p;
553 int seclen = 0, keylen = 0;
555 while (PROFILE_isspaceW(*section_name)) section_name++;
556 if (*section_name)
558 p = section_name + lstrlenW(section_name) - 1;
559 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
560 seclen = p - section_name + 1;
563 while (PROFILE_isspaceW(*key_name)) key_name++;
564 if (*key_name)
566 p = key_name + lstrlenW(key_name) - 1;
567 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
568 keylen = p - key_name + 1;
571 while (*section)
573 if (!wcsnicmp((*section)->name, section_name, seclen) &&
574 ((*section)->name)[seclen] == '\0')
576 PROFILEKEY **key = &(*section)->key;
578 while (*key)
580 /* If create_always is FALSE then we check if the keyname
581 * already exists. Otherwise we add it regardless of its
582 * existence, to allow keys to be added more than once in
583 * some cases.
585 if(!create_always)
587 if ( (!(wcsnicmp( (*key)->name, key_name, keylen )))
588 && (((*key)->name)[keylen] == '\0') )
589 return *key;
591 key = &(*key)->next;
593 if (!create) return NULL;
594 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + lstrlenW(key_name) * sizeof(WCHAR) )))
595 return NULL;
596 lstrcpyW( (*key)->name, key_name );
597 (*key)->value = NULL;
598 (*key)->next = NULL;
599 return *key;
601 section = &(*section)->next;
603 if (!create) return NULL;
604 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + lstrlenW(section_name) * sizeof(WCHAR) );
605 if(*section == NULL) return NULL;
606 lstrcpyW( (*section)->name, section_name );
607 (*section)->next = NULL;
608 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
609 sizeof(PROFILEKEY) + lstrlenW(key_name) * sizeof(WCHAR) )))
611 HeapFree(GetProcessHeap(), 0, *section);
612 return NULL;
614 lstrcpyW( (*section)->key->name, key_name );
615 (*section)->key->value = NULL;
616 (*section)->key->next = NULL;
617 return (*section)->key;
621 /***********************************************************************
622 * PROFILE_FlushFile
624 * Flush the current profile to disk if changed.
626 static BOOL PROFILE_FlushFile(void)
628 HANDLE hFile = NULL;
629 FILETIME LastWriteTime;
631 if(!CurProfile)
633 WARN("No current profile!\n");
634 return FALSE;
637 if (!CurProfile->changed) return TRUE;
639 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
640 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
642 if (hFile == INVALID_HANDLE_VALUE)
644 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
645 return FALSE;
648 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
649 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
650 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
651 CurProfile->LastWriteTime=LastWriteTime;
652 CloseHandle( hFile );
653 CurProfile->changed = FALSE;
654 return TRUE;
658 /***********************************************************************
659 * PROFILE_ReleaseFile
661 * Flush the current profile to disk and remove it from the cache.
663 static void PROFILE_ReleaseFile(void)
665 PROFILE_FlushFile();
666 PROFILE_Free( CurProfile->section );
667 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
668 CurProfile->changed = FALSE;
669 CurProfile->section = NULL;
670 CurProfile->filename = NULL;
671 CurProfile->encoding = ENCODING_ANSI;
672 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
675 /***********************************************************************
677 * Compares a file time with the current time. If the file time is
678 * at least 2.1 seconds in the past, return true.
680 * Intended as cache safety measure: The time resolution on FAT is
681 * two seconds, so files that are not at least two seconds old might
682 * keep their time even on modification, so don't cache them.
684 static BOOL is_not_current(FILETIME *ft)
686 LARGE_INTEGER now;
687 LONGLONG ftll;
689 NtQuerySystemTime( &now );
690 ftll = ((LONGLONG)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
691 TRACE("%s; %s\n", wine_dbgstr_longlong(ftll), wine_dbgstr_longlong(now.QuadPart));
692 return ftll + 21000000 < now.QuadPart;
695 /***********************************************************************
696 * PROFILE_Open
698 * Open a profile file, checking the cached file first.
700 static BOOL PROFILE_Open( LPCWSTR filename, BOOL write_access )
702 WCHAR buffer[MAX_PATH];
703 HANDLE hFile = INVALID_HANDLE_VALUE;
704 FILETIME LastWriteTime;
705 int i,j;
706 PROFILE *tempProfile;
708 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
710 /* First time around */
712 if(!CurProfile)
713 for(i=0;i<N_CACHED_PROFILES;i++)
715 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
716 if(MRUProfile[i] == NULL) break;
717 MRUProfile[i]->changed=FALSE;
718 MRUProfile[i]->section=NULL;
719 MRUProfile[i]->filename=NULL;
720 MRUProfile[i]->encoding=ENCODING_ANSI;
721 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
724 if (!filename)
725 filename = L"win.ini";
727 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
728 !wcschr(filename, '\\') && !wcschr(filename, '/'))
730 WCHAR windirW[MAX_PATH];
731 GetWindowsDirectoryW( windirW, MAX_PATH );
732 lstrcpyW(buffer, windirW);
733 lstrcatW(buffer, L"\\");
734 lstrcatW(buffer, filename);
736 else
738 LPWSTR dummy;
739 GetFullPathNameW(filename, ARRAY_SIZE(buffer), buffer, &dummy);
742 TRACE("path: %s\n", debugstr_w(buffer));
744 hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
745 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
746 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
748 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
750 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
751 return FALSE;
754 for(i=0;i<N_CACHED_PROFILES;i++)
756 if ((MRUProfile[i]->filename && !wcsicmp( buffer, MRUProfile[i]->filename )))
758 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
759 if(i)
761 PROFILE_FlushFile();
762 tempProfile=MRUProfile[i];
763 for(j=i;j>0;j--)
764 MRUProfile[j]=MRUProfile[j-1];
765 CurProfile=tempProfile;
768 if (hFile != INVALID_HANDLE_VALUE)
770 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
771 if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
772 is_not_current(&LastWriteTime))
773 TRACE("(%s): already opened (mru=%d)\n",
774 debugstr_w(buffer), i);
775 else
777 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
778 debugstr_w(buffer), i);
779 PROFILE_Free(CurProfile->section);
780 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
781 CurProfile->LastWriteTime = LastWriteTime;
783 CloseHandle(hFile);
784 return TRUE;
786 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
787 debugstr_w(buffer), i);
791 /* Flush the old current profile */
792 PROFILE_FlushFile();
794 /* Make the oldest profile the current one only in order to get rid of it */
795 if(i==N_CACHED_PROFILES)
797 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
798 for(i=N_CACHED_PROFILES-1;i>0;i--)
799 MRUProfile[i]=MRUProfile[i-1];
800 CurProfile=tempProfile;
802 if(CurProfile->filename) PROFILE_ReleaseFile();
804 /* OK, now that CurProfile is definitely free we assign it our new file */
805 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(buffer)+1) * sizeof(WCHAR) );
806 lstrcpyW( CurProfile->filename, buffer );
808 if (hFile != INVALID_HANDLE_VALUE)
810 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
811 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
812 CloseHandle(hFile);
814 else
816 /* Does not exist yet, we will create it in PROFILE_FlushFile */
817 WARN("profile file %s not found\n", debugstr_w(buffer) );
819 return TRUE;
823 /***********************************************************************
824 * PROFILE_GetSection
826 * Returns all keys of a section.
827 * If return_values is TRUE, also include the corresponding values.
829 static INT PROFILE_GetSection( const WCHAR *filename, LPCWSTR section_name,
830 LPWSTR buffer, UINT len, BOOL return_values )
832 PROFILESECTION *section;
833 PROFILEKEY *key;
835 if(!buffer) return 0;
837 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
839 EnterCriticalSection( &PROFILE_CritSect );
841 if (!PROFILE_Open( filename, FALSE ))
843 LeaveCriticalSection( &PROFILE_CritSect );
844 buffer[0] = 0;
845 return 0;
848 for (section = CurProfile->section; section; section = section->next)
850 if (!wcsicmp( section->name, section_name ))
852 UINT oldlen = len;
853 for (key = section->key; key; key = key->next)
855 if (len <= 2) break;
856 if (!*key->name && !key->value) continue; /* Skip empty lines */
857 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
858 if (!return_values && !key->value) continue; /* Skip lines w.o. '=' */
859 lstrcpynW( buffer, key->name, len - 1 );
860 len -= lstrlenW(buffer) + 1;
861 buffer += lstrlenW(buffer) + 1;
862 if (len < 2)
863 break;
864 if (return_values && key->value) {
865 buffer[-1] = '=';
866 lstrcpynW( buffer, key->value, len - 1 );
867 len -= lstrlenW(buffer) + 1;
868 buffer += lstrlenW(buffer) + 1;
871 *buffer = '\0';
873 LeaveCriticalSection( &PROFILE_CritSect );
875 if (len <= 1)
876 /*If either lpszSection or lpszKey is NULL and the supplied
877 destination buffer is too small to hold all the strings,
878 the last string is truncated and followed by two null characters.
879 In this case, the return value is equal to cchReturnBuffer
880 minus two. */
882 buffer[-1] = '\0';
883 return oldlen - 2;
885 return oldlen - len;
888 buffer[0] = buffer[1] = '\0';
890 LeaveCriticalSection( &PROFILE_CritSect );
892 return 0;
895 static BOOL PROFILE_DeleteSection( const WCHAR *filename, const WCHAR *name )
897 PROFILESECTION **section;
899 EnterCriticalSection( &PROFILE_CritSect );
901 if (!PROFILE_Open( filename, TRUE ))
903 LeaveCriticalSection( &PROFILE_CritSect );
904 return FALSE;
907 for (section = &CurProfile->section; *section; section = &(*section)->next)
909 if (!wcsicmp( (*section)->name, name ))
911 PROFILESECTION *to_del = *section;
912 *section = to_del->next;
913 to_del->next = NULL;
914 PROFILE_Free( to_del );
915 CurProfile->changed = TRUE;
916 PROFILE_FlushFile();
917 break;
921 LeaveCriticalSection( &PROFILE_CritSect );
922 return TRUE;
926 /* See GetPrivateProfileSectionNamesA for documentation */
927 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
929 LPWSTR buf;
930 UINT buflen,tmplen;
931 PROFILESECTION *section;
933 TRACE("(%p, %d)\n", buffer, len);
935 if (!buffer || !len)
936 return 0;
937 if (len==1) {
938 *buffer='\0';
939 return 0;
942 buflen=len-1;
943 buf=buffer;
944 section = CurProfile->section;
945 while ((section!=NULL)) {
946 if (section->name[0]) {
947 tmplen = lstrlenW(section->name)+1;
948 if (tmplen >= buflen) {
949 if (buflen > 0) {
950 memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
951 buf += buflen-1;
952 *buf++='\0';
954 *buf='\0';
955 return len-2;
957 memcpy(buf, section->name, tmplen * sizeof(WCHAR));
958 buf += tmplen;
959 buflen -= tmplen;
961 section = section->next;
963 *buf='\0';
964 return buf-buffer;
967 /***********************************************************************
968 * PROFILE_SetString
970 * Set a profile string.
972 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
973 LPCWSTR value, BOOL create_always )
975 if (!value) /* Delete a key */
977 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
978 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
979 section_name, key_name );
980 return TRUE; /* same error handling as above */
982 else /* Set the key value */
984 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
985 key_name, TRUE, create_always );
986 TRACE("(%s,%s,%s):\n",
987 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
988 if (!key) return FALSE;
990 /* strip the leading spaces. We can safely strip \n\r and
991 * friends too, they should not happen here anyway. */
992 while (PROFILE_isspaceW(*value)) value++;
994 if (key->value)
996 if (!wcscmp( key->value, value ))
998 TRACE(" no change needed\n" );
999 return TRUE; /* No change needed */
1001 TRACE(" replacing %s\n", debugstr_w(key->value) );
1002 HeapFree( GetProcessHeap(), 0, key->value );
1004 else TRACE(" creating key\n" );
1005 key->value = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(value)+1) * sizeof(WCHAR) );
1006 lstrcpyW( key->value, value );
1007 CurProfile->changed = TRUE;
1009 return TRUE;
1012 static HKEY open_file_mapping_key( const WCHAR *filename )
1014 static HKEY mapping_key;
1015 HKEY key;
1017 EnterCriticalSection( &PROFILE_CritSect );
1019 if (!mapping_key && RegOpenKeyExW( HKEY_LOCAL_MACHINE,
1020 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping",
1021 0, KEY_WOW64_64KEY, &mapping_key ))
1022 mapping_key = NULL;
1024 LeaveCriticalSection( &PROFILE_CritSect );
1026 if (mapping_key && !RegOpenKeyExW( mapping_key, PathFindFileNameW( filename ), 0, KEY_READ, &key ))
1027 return key;
1028 return NULL;
1031 static WCHAR *enum_key( HKEY key, DWORD i )
1033 WCHAR *value, *new_value;
1034 DWORD max = 256, len;
1035 LSTATUS res;
1037 if (!(value = HeapAlloc( GetProcessHeap(), 0, max * sizeof(WCHAR) ))) return NULL;
1038 len = max;
1039 while ((res = RegEnumValueW( key, i, value, &len, NULL, NULL, NULL, NULL )) == ERROR_MORE_DATA)
1041 max *= 2;
1042 if (!(new_value = HeapReAlloc( GetProcessHeap(), 0, value, max * sizeof(WCHAR) )))
1044 HeapFree( GetProcessHeap(), 0, value );
1045 return NULL;
1047 value = new_value;
1048 len = max;
1050 if (!res) return value;
1051 HeapFree( GetProcessHeap(), 0, value );
1052 return NULL;
1055 static WCHAR *get_key_value( HKEY key, const WCHAR *value )
1057 DWORD size = 0;
1058 WCHAR *data;
1060 if (RegGetValueW( key, NULL, value, RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, NULL, &size )) return NULL;
1061 if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
1062 if (!RegGetValueW( key, NULL, value, RRF_RT_REG_SZ | RRF_NOEXPAND, NULL, (BYTE *)data, &size )) return data;
1063 HeapFree( GetProcessHeap(), 0, data );
1064 return NULL;
1067 static HKEY open_mapped_key( const WCHAR *path, BOOL write )
1069 static const WCHAR usrW[] = {'U','S','R',':'};
1070 static const WCHAR sysW[] = {'S','Y','S',':'};
1071 WCHAR *combined_path;
1072 const WCHAR *p;
1073 LSTATUS res;
1074 HKEY key;
1076 TRACE("%s\n", debugstr_w( path ));
1078 for (p = path; strchr("!#@", *p); p++)
1079 FIXME("ignoring %c modifier\n", *p);
1081 if (!wcsncmp( p, usrW, ARRAY_SIZE( usrW ) ))
1083 if (write)
1084 res = RegCreateKeyExW( HKEY_CURRENT_USER, p + 4, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key, NULL );
1085 else
1086 res = RegOpenKeyExW( HKEY_CURRENT_USER, p + 4, 0, KEY_READ, &key );
1087 return res ? NULL : key;
1090 if (!wcsncmp( p, sysW, ARRAY_SIZE( sysW ) ))
1092 p += 4;
1093 if (!(combined_path = HeapAlloc( GetProcessHeap(), 0,
1094 (ARRAY_SIZE( L"Software\\" ) + lstrlenW( p )) * sizeof(WCHAR) )))
1095 return NULL;
1096 lstrcpyW( combined_path, L"Software\\" );
1097 lstrcatW( combined_path, p );
1098 if (write)
1099 res = RegCreateKeyExW( HKEY_LOCAL_MACHINE, combined_path, 0, NULL,
1100 0, KEY_READ | KEY_WRITE, NULL, &key, NULL );
1101 else
1102 res = RegOpenKeyExW( HKEY_LOCAL_MACHINE, combined_path, 0, KEY_READ, &key );
1103 HeapFree( GetProcessHeap(), 0, combined_path );
1104 return res ? NULL : key;
1107 FIXME("unhandled path syntax %s\n", debugstr_w( path ));
1108 return NULL;
1111 /* returns TRUE if the given section + name is mapped */
1112 static BOOL get_mapped_section_key( const WCHAR *filename, const WCHAR *section,
1113 const WCHAR *name, BOOL write, HKEY *ret_key )
1115 WCHAR *path = NULL, *combined_path;
1116 HKEY key, subkey = NULL;
1118 if (!(key = open_file_mapping_key( filename )))
1119 return FALSE;
1121 if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey ))
1123 if (!(path = get_key_value( subkey, name )))
1124 path = get_key_value( subkey, NULL );
1125 RegCloseKey( subkey );
1126 RegCloseKey( key );
1127 if (!path) return FALSE;
1129 else
1131 if (!(path = get_key_value( key, section )))
1133 if ((path = get_key_value( key, NULL )))
1135 if ((combined_path = HeapAlloc( GetProcessHeap(), 0,
1136 (lstrlenW( path ) + lstrlenW( section ) + 2) * sizeof(WCHAR) )))
1138 lstrcpyW( combined_path, path );
1139 lstrcatW( combined_path, L"\\" );
1140 lstrcatW( combined_path, section );
1142 HeapFree( GetProcessHeap(), 0, path );
1143 path = combined_path;
1146 RegCloseKey( key );
1147 if (!path) return FALSE;
1150 *ret_key = open_mapped_key( path, write );
1151 HeapFree( GetProcessHeap(), 0, path );
1152 return TRUE;
1155 static DWORD get_mapped_section( HKEY key, WCHAR *buffer, DWORD size, BOOL return_values )
1157 WCHAR *entry, *value;
1158 DWORD i, ret = 0;
1160 for (i = 0; (entry = enum_key( key, i )); ++i)
1162 lstrcpynW( buffer + ret, entry, size - ret - 1 );
1163 ret = min( ret + lstrlenW( entry ) + 1, size - 1 );
1164 if (return_values && ret < size - 1 && (value = get_key_value( key, entry )))
1166 buffer[ret - 1] = '=';
1167 lstrcpynW( buffer + ret, value, size - ret - 1 );
1168 ret = min( ret + lstrlenW( value ) + 1, size - 1 );
1169 HeapFree( GetProcessHeap(), 0, value );
1171 HeapFree( GetProcessHeap(), 0, entry );
1174 return ret;
1177 static DWORD get_section( const WCHAR *filename, const WCHAR *section,
1178 WCHAR *buffer, DWORD size, BOOL return_values )
1180 HKEY key, subkey, section_key;
1181 BOOL use_ini = TRUE;
1182 DWORD ret = 0;
1183 WCHAR *path;
1185 if ((key = open_file_mapping_key( filename )))
1187 if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey ))
1189 WCHAR *entry, *value;
1190 HKEY entry_key;
1191 DWORD i;
1193 for (i = 0; (entry = enum_key( subkey, i )); ++i)
1195 if (!(path = get_key_value( subkey, entry )))
1197 HeapFree( GetProcessHeap(), 0, entry );
1198 continue;
1201 entry_key = open_mapped_key( path, FALSE );
1202 HeapFree( GetProcessHeap(), 0, path );
1203 if (!entry_key)
1205 HeapFree( GetProcessHeap(), 0, entry );
1206 continue;
1209 if (entry[0])
1211 if ((value = get_key_value( entry_key, entry )))
1213 lstrcpynW( buffer + ret, entry, size - ret - 1 );
1214 ret = min( ret + lstrlenW( entry ) + 1, size - 1 );
1215 if (return_values && ret < size - 1)
1217 buffer[ret - 1] = '=';
1218 lstrcpynW( buffer + ret, value, size - ret - 1 );
1219 ret = min( ret + lstrlenW( value ) + 1, size - 1 );
1221 HeapFree( GetProcessHeap(), 0, value );
1224 else
1226 ret = get_mapped_section( entry_key, buffer, size, return_values );
1227 use_ini = FALSE;
1230 HeapFree( GetProcessHeap(), 0, entry );
1231 RegCloseKey( entry_key );
1234 RegCloseKey( subkey );
1236 else if (get_mapped_section_key( filename, section, NULL, FALSE, &section_key ))
1238 ret = get_mapped_section( section_key, buffer, size, return_values );
1239 use_ini = FALSE;
1240 RegCloseKey( section_key );
1243 RegCloseKey( key );
1246 if (use_ini)
1247 ret += PROFILE_GetSection( filename, section, buffer + ret, size - ret, return_values );
1249 return ret;
1252 static void delete_key_values( HKEY key )
1254 WCHAR *entry;
1256 while ((entry = enum_key( key, 0 )))
1258 RegDeleteValueW( key, entry );
1259 HeapFree( GetProcessHeap(), 0, entry );
1263 static BOOL delete_section( const WCHAR *filename, const WCHAR *section )
1265 HKEY key, subkey, section_key;
1267 if ((key = open_file_mapping_key( filename )))
1269 if (!RegOpenKeyExW( key, section, 0, KEY_READ, &subkey ))
1271 WCHAR *entry, *path;
1272 HKEY entry_key;
1273 DWORD i;
1275 for (i = 0; (entry = enum_key( subkey, i )); ++i)
1277 if (!(path = get_key_value( subkey, entry )))
1279 HeapFree( GetProcessHeap(), 0, entry );
1280 continue;
1283 entry_key = open_mapped_key( path, TRUE );
1284 HeapFree( GetProcessHeap(), 0, path );
1285 if (!entry_key)
1287 HeapFree( GetProcessHeap(), 0, entry );
1288 continue;
1291 if (entry[0])
1292 RegDeleteValueW( entry_key, entry );
1293 else
1294 delete_key_values( entry_key );
1296 HeapFree( GetProcessHeap(), 0, entry );
1297 RegCloseKey( entry_key );
1300 RegCloseKey( subkey );
1302 else if (get_mapped_section_key( filename, section, NULL, TRUE, &section_key ))
1304 delete_key_values( section_key );
1305 RegCloseKey( section_key );
1308 RegCloseKey( key );
1311 return PROFILE_DeleteSection( filename, section );
1314 /********************* API functions **********************************/
1317 /***********************************************************************
1318 * GetProfileIntA (KERNEL32.@)
1320 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1322 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1325 /***********************************************************************
1326 * GetProfileIntW (KERNEL32.@)
1328 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1330 return GetPrivateProfileIntW( section, entry, def_val, L"win.ini" );
1333 /***********************************************************************
1334 * GetPrivateProfileStringW (KERNEL32.@)
1336 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1337 LPCWSTR def_val, LPWSTR buffer,
1338 UINT len, LPCWSTR filename )
1340 int ret;
1341 LPWSTR defval_tmp = NULL;
1342 const WCHAR *p;
1343 HKEY key;
1345 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1346 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1348 if (!buffer || !len) return 0;
1349 if (!def_val) def_val = L"";
1350 if (!section) return GetPrivateProfileSectionNamesW( buffer, len, filename );
1351 if (!entry)
1353 ret = get_section( filename, section, buffer, len, FALSE );
1354 if (!buffer[0])
1356 PROFILE_CopyEntry( buffer, def_val, len );
1357 ret = lstrlenW( buffer );
1359 return ret;
1362 /* strip any trailing ' ' of def_val. */
1363 p = def_val + lstrlenW(def_val) - 1;
1365 while (p > def_val && *p == ' ') p--;
1367 if (p >= def_val)
1369 int vlen = (int)(p - def_val) + 1;
1371 defval_tmp = HeapAlloc(GetProcessHeap(), 0, (vlen + 1) * sizeof(WCHAR));
1372 memcpy(defval_tmp, def_val, vlen * sizeof(WCHAR));
1373 defval_tmp[vlen] = '\0';
1374 def_val = defval_tmp;
1377 if (get_mapped_section_key( filename, section, entry, FALSE, &key ))
1379 if (key)
1381 WCHAR *value;
1383 if ((value = get_key_value( key, entry )))
1385 lstrcpynW( buffer, value, len );
1386 HeapFree( GetProcessHeap(), 0, value );
1388 else
1389 lstrcpynW( buffer, def_val, len );
1391 RegCloseKey( key );
1393 else
1394 lstrcpynW( buffer, def_val, len );
1396 ret = lstrlenW( buffer );
1398 else
1400 EnterCriticalSection( &PROFILE_CritSect );
1402 if (PROFILE_Open( filename, FALSE ))
1404 PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section, entry, FALSE, FALSE );
1405 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val, len );
1406 TRACE("-> %s\n", debugstr_w( buffer ));
1407 ret = lstrlenW( buffer );
1409 else
1411 lstrcpynW( buffer, def_val, len );
1412 ret = lstrlenW( buffer );
1415 LeaveCriticalSection( &PROFILE_CritSect );
1418 HeapFree(GetProcessHeap(), 0, defval_tmp);
1420 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1422 return ret;
1425 /***********************************************************************
1426 * GetPrivateProfileStringA (KERNEL32.@)
1428 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1429 LPCSTR def_val, LPSTR buffer,
1430 UINT len, LPCSTR filename )
1432 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1433 LPWSTR bufferW;
1434 INT retW, ret = 0;
1436 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1437 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1438 else sectionW.Buffer = NULL;
1439 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1440 else entryW.Buffer = NULL;
1441 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1442 else def_valW.Buffer = NULL;
1443 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1444 else filenameW.Buffer = NULL;
1446 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1447 def_valW.Buffer, bufferW, len,
1448 filenameW.Buffer);
1449 if (len && buffer)
1451 if (retW)
1453 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, len - 1, NULL, NULL);
1454 if (!ret)
1455 ret = len - 1;
1457 buffer[ret] = 0;
1460 RtlFreeUnicodeString(&sectionW);
1461 RtlFreeUnicodeString(&entryW);
1462 RtlFreeUnicodeString(&def_valW);
1463 RtlFreeUnicodeString(&filenameW);
1464 HeapFree(GetProcessHeap(), 0, bufferW);
1465 return ret;
1468 /***********************************************************************
1469 * GetProfileStringA (KERNEL32.@)
1471 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1472 LPSTR buffer, UINT len )
1474 return GetPrivateProfileStringA( section, entry, def_val,
1475 buffer, len, "win.ini" );
1478 /***********************************************************************
1479 * GetProfileStringW (KERNEL32.@)
1481 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1482 LPCWSTR def_val, LPWSTR buffer, UINT len )
1484 return GetPrivateProfileStringW( section, entry, def_val, buffer, len, L"win.ini" );
1487 /***********************************************************************
1488 * WriteProfileStringA (KERNEL32.@)
1490 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1491 LPCSTR string )
1493 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1496 /***********************************************************************
1497 * WriteProfileStringW (KERNEL32.@)
1499 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1500 LPCWSTR string )
1502 return WritePrivateProfileStringW( section, entry, string, L"win.ini" );
1506 /***********************************************************************
1507 * GetPrivateProfileIntW (KERNEL32.@)
1509 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1510 INT def_val, LPCWSTR filename )
1512 WCHAR buffer[30];
1513 UNICODE_STRING bufferW;
1514 ULONG result;
1516 if (GetPrivateProfileStringW( section, entry, L"", buffer, ARRAY_SIZE( buffer ),
1517 filename ) == 0)
1518 return def_val;
1520 /* FIXME: if entry can be found but it's empty, then Win16 is
1521 * supposed to return 0 instead of def_val ! Difficult/problematic
1522 * to implement (every other failure also returns zero buffer),
1523 * thus wait until testing framework avail for making sure nothing
1524 * else gets broken that way. */
1525 if (!buffer[0]) return (UINT)def_val;
1527 RtlInitUnicodeString( &bufferW, buffer );
1528 RtlUnicodeStringToInteger( &bufferW, 0, &result);
1529 return result;
1532 /***********************************************************************
1533 * GetPrivateProfileIntA (KERNEL32.@)
1535 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1536 INT def_val, LPCSTR filename )
1538 UNICODE_STRING entryW, filenameW, sectionW;
1539 UINT res;
1540 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1541 else entryW.Buffer = NULL;
1542 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1543 else filenameW.Buffer = NULL;
1544 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1545 else sectionW.Buffer = NULL;
1546 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1547 filenameW.Buffer);
1548 RtlFreeUnicodeString(&sectionW);
1549 RtlFreeUnicodeString(&filenameW);
1550 RtlFreeUnicodeString(&entryW);
1551 return res;
1554 /***********************************************************************
1555 * GetPrivateProfileSectionW (KERNEL32.@)
1557 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1558 DWORD len, LPCWSTR filename )
1560 if (!section || !buffer)
1562 SetLastError(ERROR_INVALID_PARAMETER);
1563 return 0;
1566 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1568 return get_section( filename, section, buffer, len, TRUE );
1571 /***********************************************************************
1572 * GetPrivateProfileSectionA (KERNEL32.@)
1574 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1575 DWORD len, LPCSTR filename )
1577 UNICODE_STRING sectionW, filenameW;
1578 LPWSTR bufferW;
1579 INT retW, ret = 0;
1581 if (!section || !buffer)
1583 SetLastError(ERROR_INVALID_PARAMETER);
1584 return 0;
1587 bufferW = HeapAlloc(GetProcessHeap(), 0, len * 2 * sizeof(WCHAR));
1588 RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1589 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1590 else filenameW.Buffer = NULL;
1592 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len * 2, filenameW.Buffer);
1593 if (retW)
1595 if (retW == len * 2 - 2) retW++; /* overflow */
1596 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1597 if (!ret || ret == len) /* overflow */
1599 ret = len - 2;
1600 buffer[len-2] = 0;
1601 buffer[len-1] = 0;
1603 else ret--;
1605 else
1607 buffer[0] = 0;
1608 buffer[1] = 0;
1611 RtlFreeUnicodeString(&sectionW);
1612 RtlFreeUnicodeString(&filenameW);
1613 HeapFree(GetProcessHeap(), 0, bufferW);
1614 return ret;
1617 /***********************************************************************
1618 * GetProfileSectionA (KERNEL32.@)
1620 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1622 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1625 /***********************************************************************
1626 * GetProfileSectionW (KERNEL32.@)
1628 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1630 return GetPrivateProfileSectionW( section, buffer, len, L"win.ini" );
1634 /***********************************************************************
1635 * WritePrivateProfileStringW (KERNEL32.@)
1637 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1638 LPCWSTR string, LPCWSTR filename )
1640 BOOL ret = FALSE;
1641 HKEY key;
1643 TRACE("(%s, %s, %s, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1645 if (!section && !entry && !string) /* documented "file flush" case */
1647 EnterCriticalSection( &PROFILE_CritSect );
1648 if (!filename || PROFILE_Open( filename, TRUE ))
1650 if (CurProfile) PROFILE_ReleaseFile();
1652 LeaveCriticalSection( &PROFILE_CritSect );
1653 return FALSE;
1655 if (!entry) return delete_section( filename, section );
1657 if (get_mapped_section_key( filename, section, entry, TRUE, &key ))
1659 LSTATUS res;
1661 if (string)
1662 res = RegSetValueExW( key, entry, 0, REG_SZ, (const BYTE *)string,
1663 (lstrlenW( string ) + 1) * sizeof(WCHAR) );
1664 else
1665 res = RegDeleteValueW( key, entry );
1666 RegCloseKey( key );
1667 if (res) SetLastError( res );
1668 return !res;
1671 EnterCriticalSection( &PROFILE_CritSect );
1673 if (PROFILE_Open( filename, TRUE ))
1675 if (!section)
1676 SetLastError(ERROR_FILE_NOT_FOUND);
1677 else
1678 ret = PROFILE_SetString( section, entry, string, FALSE);
1679 if (ret) ret = PROFILE_FlushFile();
1682 LeaveCriticalSection( &PROFILE_CritSect );
1683 return ret;
1686 /***********************************************************************
1687 * WritePrivateProfileStringA (KERNEL32.@)
1689 BOOL WINAPI DECLSPEC_HOTPATCH WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1690 LPCSTR string, LPCSTR filename )
1692 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1693 BOOL ret;
1695 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1696 else sectionW.Buffer = NULL;
1697 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1698 else entryW.Buffer = NULL;
1699 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1700 else stringW.Buffer = NULL;
1701 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1702 else filenameW.Buffer = NULL;
1704 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1705 stringW.Buffer, filenameW.Buffer);
1706 RtlFreeUnicodeString(&sectionW);
1707 RtlFreeUnicodeString(&entryW);
1708 RtlFreeUnicodeString(&stringW);
1709 RtlFreeUnicodeString(&filenameW);
1710 return ret;
1713 /***********************************************************************
1714 * WritePrivateProfileSectionW (KERNEL32.@)
1716 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1717 LPCWSTR string, LPCWSTR filename )
1719 BOOL ret = FALSE;
1720 LPWSTR p;
1721 HKEY key, section_key;
1723 if (!section && !string)
1725 EnterCriticalSection( &PROFILE_CritSect );
1726 if (!filename || PROFILE_Open( filename, TRUE ))
1728 if (CurProfile) PROFILE_ReleaseFile();
1730 LeaveCriticalSection( &PROFILE_CritSect );
1731 return FALSE;
1733 if (!string) return delete_section( filename, section );
1735 if ((key = open_file_mapping_key( filename )))
1737 /* replace existing entries, but only if they are mapped, and do not
1738 * delete any keys */
1740 const WCHAR *entry, *p;
1742 for (entry = string; *entry; entry += lstrlenW( entry ) + 1)
1744 if ((p = wcschr( entry, '=' )))
1746 WCHAR *entry_copy;
1747 p++;
1748 if (!(entry_copy = HeapAlloc( GetProcessHeap(), 0, (p - entry) * sizeof(WCHAR) )))
1750 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1751 RegCloseKey( key );
1752 return FALSE;
1754 lstrcpynW( entry_copy, entry, p - entry );
1755 if (get_mapped_section_key( filename, section, entry_copy, TRUE, &section_key ))
1757 LSTATUS res = RegSetValueExW( section_key, entry_copy, 0, REG_SZ, (const BYTE *)p,
1758 (lstrlenW( p ) + 1) * sizeof(WCHAR) );
1759 RegCloseKey( section_key );
1760 if (res)
1762 HeapFree( GetProcessHeap(), 0, entry_copy );
1763 SetLastError( res );
1764 RegCloseKey( key );
1765 return FALSE;
1768 HeapFree( GetProcessHeap(), 0, entry_copy );
1771 RegCloseKey( key );
1772 return TRUE;
1775 EnterCriticalSection( &PROFILE_CritSect );
1777 if (PROFILE_Open( filename, TRUE ))
1779 PROFILE_DeleteAllKeys(section);
1780 ret = TRUE;
1781 while (*string && ret)
1783 WCHAR *buf = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( string ) + 1) * sizeof(WCHAR) );
1784 lstrcpyW( buf, string );
1785 if ((p = wcschr( buf, '=')))
1787 *p = '\0';
1788 ret = PROFILE_SetString( section, buf, p+1, TRUE );
1790 HeapFree( GetProcessHeap(), 0, buf );
1791 string += lstrlenW( string ) + 1;
1793 if (ret) ret = PROFILE_FlushFile();
1796 LeaveCriticalSection( &PROFILE_CritSect );
1797 return ret;
1800 /***********************************************************************
1801 * WritePrivateProfileSectionA (KERNEL32.@)
1803 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1804 LPCSTR string, LPCSTR filename)
1807 UNICODE_STRING sectionW, filenameW;
1808 LPWSTR stringW;
1809 BOOL ret;
1811 if (string)
1813 INT lenA, lenW;
1814 LPCSTR p = string;
1816 while(*p) p += strlen(p) + 1;
1817 lenA = p - string + 1;
1818 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1819 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1820 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1822 else stringW = NULL;
1823 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1824 else sectionW.Buffer = NULL;
1825 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1826 else filenameW.Buffer = NULL;
1828 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1830 HeapFree(GetProcessHeap(), 0, stringW);
1831 RtlFreeUnicodeString(&sectionW);
1832 RtlFreeUnicodeString(&filenameW);
1833 return ret;
1836 /***********************************************************************
1837 * WriteProfileSectionA (KERNEL32.@)
1839 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1842 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1845 /***********************************************************************
1846 * WriteProfileSectionW (KERNEL32.@)
1848 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1850 return WritePrivateProfileSectionW(section, keys_n_values, L"win.ini");
1854 /***********************************************************************
1855 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1857 * Returns the section names contained in the specified file.
1858 * FIXME: Where do we find this file when the path is relative?
1859 * The section names are returned as a list of strings with an extra
1860 * '\0' to mark the end of the list. Except for that the behavior
1861 * depends on the Windows version.
1863 * Win95:
1864 * - if the buffer is 0 or 1 character long then it is as if it was of
1865 * infinite length.
1866 * - otherwise, if the buffer is too small only the section names that fit
1867 * are returned.
1868 * - note that this means if the buffer was too small to return even just
1869 * the first section name then a single '\0' will be returned.
1870 * - the return value is the number of characters written in the buffer,
1871 * except if the buffer was too small in which case len-2 is returned
1873 * Win2000:
1874 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1875 * '\0' and the return value is 0
1876 * - otherwise if the buffer is too small then the first section name that
1877 * does not fit is truncated so that the string list can be terminated
1878 * correctly (double '\0')
1879 * - the return value is the number of characters written in the buffer
1880 * except for the trailing '\0'. If the buffer is too small, then the
1881 * return value is len-2
1882 * - Win2000 has a bug that triggers when the section names and the
1883 * trailing '\0' fit exactly in the buffer. In that case the trailing
1884 * '\0' is missing.
1886 * Wine implements the observed Win2000 behavior (except for the bug).
1888 * Note that when the buffer is big enough then the return value may be any
1889 * value between 1 and len-1 (or len in Win95), including len-2.
1891 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1892 LPCWSTR filename)
1894 DWORD ret = 0;
1895 HKEY key;
1897 if ((key = open_file_mapping_key( filename )))
1899 WCHAR *section;
1900 DWORD i;
1902 for (i = 0; (section = enum_key( key, i )); ++i)
1904 lstrcpynW( buffer + ret, section, size - ret - 1 );
1905 ret = min( ret + lstrlenW( section ) + 1, size - 1 );
1906 HeapFree( GetProcessHeap(), 0, section );
1909 RegCloseKey( key );
1912 RtlEnterCriticalSection( &PROFILE_CritSect );
1914 if (PROFILE_Open( filename, FALSE ))
1915 ret += PROFILE_GetSectionNames( buffer + ret, size - ret );
1917 RtlLeaveCriticalSection( &PROFILE_CritSect );
1919 return ret;
1923 /***********************************************************************
1924 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1926 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1927 LPCSTR filename)
1929 UNICODE_STRING filenameW;
1930 LPWSTR bufferW;
1931 INT retW, ret = 0;
1933 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1934 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1935 else filenameW.Buffer = NULL;
1937 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1938 if (retW && size)
1940 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1941 if (!ret)
1943 ret = size-2;
1944 buffer[size-1] = 0;
1946 else
1947 ret = ret-1;
1949 else if(size)
1950 buffer[0] = '\0';
1952 RtlFreeUnicodeString(&filenameW);
1953 HeapFree(GetProcessHeap(), 0, bufferW);
1954 return ret;
1957 static int get_hex_byte( const WCHAR *p )
1959 int val;
1961 if (*p >= '0' && *p <= '9') val = *p - '0';
1962 else if (*p >= 'A' && *p <= 'Z') val = *p - 'A' + 10;
1963 else if (*p >= 'a' && *p <= 'z') val = *p - 'a' + 10;
1964 else return -1;
1965 val <<= 4;
1966 p++;
1967 if (*p >= '0' && *p <= '9') val += *p - '0';
1968 else if (*p >= 'A' && *p <= 'Z') val += *p - 'A' + 10;
1969 else if (*p >= 'a' && *p <= 'z') val += *p - 'a' + 10;
1970 else return -1;
1971 return val;
1974 /***********************************************************************
1975 * GetPrivateProfileStructW (KERNEL32.@)
1977 * Should match Win95's behaviour pretty much
1979 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1980 LPVOID buf, UINT len, LPCWSTR filename)
1982 BOOL ret = FALSE;
1983 LPBYTE data = buf;
1984 BYTE chksum = 0;
1985 int val;
1986 WCHAR *p, *buffer;
1988 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (2 * len + 3) * sizeof(WCHAR) ))) return FALSE;
1990 if (GetPrivateProfileStringW( section, key, NULL, buffer, 2 * len + 3, filename ) != 2 * len + 2)
1991 goto done;
1993 for (p = buffer; len; p += 2, len--)
1995 if ((val = get_hex_byte( p )) == -1) goto done;
1996 *data++ = val;
1997 chksum += val;
1999 /* retrieve stored checksum value */
2000 if ((val = get_hex_byte( p )) == -1) goto done;
2001 ret = ((BYTE)val == chksum);
2003 done:
2004 HeapFree( GetProcessHeap(), 0, buffer );
2005 return ret;
2008 /***********************************************************************
2009 * GetPrivateProfileStructA (KERNEL32.@)
2011 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
2012 LPVOID buffer, UINT len, LPCSTR filename)
2014 UNICODE_STRING sectionW, keyW, filenameW;
2015 INT ret;
2017 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
2018 else sectionW.Buffer = NULL;
2019 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
2020 else keyW.Buffer = NULL;
2021 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
2022 else filenameW.Buffer = NULL;
2024 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
2025 filenameW.Buffer);
2026 /* Do not translate binary data. */
2028 RtlFreeUnicodeString(&sectionW);
2029 RtlFreeUnicodeString(&keyW);
2030 RtlFreeUnicodeString(&filenameW);
2031 return ret;
2036 /***********************************************************************
2037 * WritePrivateProfileStructW (KERNEL32.@)
2039 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
2040 LPVOID buf, UINT bufsize, LPCWSTR filename)
2042 BOOL ret = FALSE;
2043 LPBYTE binbuf;
2044 LPWSTR outstring, p;
2045 DWORD sum = 0;
2047 TRACE("(%s %s %p %u %s)\n", debugstr_w(section), debugstr_w(key), buf, bufsize, debugstr_w(filename));
2049 if (!section && !key && !buf) /* flush the cache */
2050 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
2052 if (!buf)
2053 return WritePrivateProfileStringW(section, key, NULL, filename);
2055 /* allocate string buffer for hex chars + checksum hex char + '\0' */
2056 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
2057 p = outstring;
2058 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
2059 *p++ = hex[*binbuf >> 4];
2060 *p++ = hex[*binbuf & 0xf];
2061 sum += *binbuf;
2063 /* checksum is sum & 0xff */
2064 *p++ = hex[(sum & 0xf0) >> 4];
2065 *p++ = hex[sum & 0xf];
2066 *p++ = '\0';
2068 ret = WritePrivateProfileStringW( section, key, outstring, filename );
2069 HeapFree( GetProcessHeap(), 0, outstring );
2070 return ret;
2073 /***********************************************************************
2074 * WritePrivateProfileStructA (KERNEL32.@)
2076 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
2077 LPVOID buf, UINT bufsize, LPCSTR filename)
2079 UNICODE_STRING sectionW, keyW, filenameW;
2080 INT ret;
2082 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
2083 else sectionW.Buffer = NULL;
2084 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
2085 else keyW.Buffer = NULL;
2086 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
2087 else filenameW.Buffer = NULL;
2089 /* Do not translate binary data. */
2090 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
2091 filenameW.Buffer);
2093 RtlFreeUnicodeString(&sectionW);
2094 RtlFreeUnicodeString(&keyW);
2095 RtlFreeUnicodeString(&filenameW);
2096 return ret;
2100 /***********************************************************************
2101 * OpenProfileUserMapping (KERNEL32.@)
2103 BOOL WINAPI OpenProfileUserMapping(void) {
2104 FIXME("(), stub!\n");
2105 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2106 return FALSE;
2109 /***********************************************************************
2110 * CloseProfileUserMapping (KERNEL32.@)
2112 BOOL WINAPI CloseProfileUserMapping(void) {
2113 FIXME("(), stub!\n");
2114 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2115 return FALSE;