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
23 #include "wine/port.h"
33 #include "wine/winbase16.h"
34 #include "wine/unicode.h"
35 #include "wine/library.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(profile);
40 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
50 typedef struct tagPROFILEKEY
53 struct tagPROFILEKEY *next;
57 typedef struct tagPROFILESECTION
59 struct tagPROFILEKEY *key;
60 struct tagPROFILESECTION *next;
68 PROFILESECTION *section;
70 FILETIME LastWriteTime;
75 #define N_CACHED_PROFILES 10
77 /* Cached profile files */
78 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
80 #define CurProfile (MRUProfile[0])
82 /* Check for comments in profile */
83 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
85 static const WCHAR emptystringW[] = {0};
86 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
88 static CRITICAL_SECTION PROFILE_CritSect;
89 static CRITICAL_SECTION_DEBUG critsect_debug =
91 0, 0, &PROFILE_CritSect,
92 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
93 0, 0, { (DWORD_PTR)(__FILE__ ": PROFILE_CritSect") }
95 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
97 static const char hex[16] = "0123456789ABCDEF";
99 /***********************************************************************
102 * Copy the content of an entry into a buffer, removing quotes, and possibly
103 * translating environment variables.
105 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
112 if (strip_quote && ((*value == '\'') || (*value == '\"')))
114 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
117 lstrcpynW( buffer, value, len );
118 if (quote && (len >= lstrlenW(value))) buffer[strlenW(buffer)-1] = '\0';
121 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
122 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
125 USHORT * shortbuffer = buffer;
126 for (i = 0; i < len; i++)
127 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
130 /* writes any necessary encoding marker to the file */
131 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
133 DWORD dwBytesWritten;
140 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
142 case ENCODING_UTF16LE:
144 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
146 case ENCODING_UTF16BE:
148 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
153 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
156 int write_buffer_len;
157 DWORD dwBytesWritten;
159 TRACE("writing: %s\n", debugstr_wn(szLine, len));
164 write_buffer_len = WideCharToMultiByte(CP_ACP, 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_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
168 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
169 HeapFree(GetProcessHeap(), 0, write_buffer);
172 write_buffer_len = WideCharToMultiByte(CP_UTF8, 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_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
176 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
177 HeapFree(GetProcessHeap(), 0, write_buffer);
179 case ENCODING_UTF16LE:
180 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
182 case ENCODING_UTF16BE:
183 PROFILE_ByteSwapShortBuffer(szLine, len);
184 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
187 FIXME("encoding type %d not implemented\n", encoding);
191 /***********************************************************************
194 * Save a profile tree to a file.
196 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
201 PROFILE_WriteMarker(hFile, encoding);
203 for ( ; section; section = section->next)
207 if (section->name[0]) len += strlenW(section->name) + 4;
209 for (key = section->key; key; key = key->next)
211 len += strlenW(key->name) + 2;
212 if (key->value) len += strlenW(key->value) + 1;
215 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
219 if (section->name[0])
222 strcpyW( p, section->name );
229 for (key = section->key; key; key = key->next)
231 strcpyW( p, key->name );
236 strcpyW( p, key->value );
242 PROFILE_WriteLine( hFile, buffer, len, encoding );
243 HeapFree(GetProcessHeap(), 0, buffer);
248 /***********************************************************************
251 * Free a profile tree.
253 static void PROFILE_Free( PROFILESECTION *section )
255 PROFILESECTION *next_section;
256 PROFILEKEY *key, *next_key;
258 for ( ; section; section = next_section)
260 for (key = section->key; key; key = next_key)
262 next_key = key->next;
263 HeapFree( GetProcessHeap(), 0, key->value );
264 HeapFree( GetProcessHeap(), 0, key );
266 next_section = section->next;
267 HeapFree( GetProcessHeap(), 0, section );
271 /* returns 1 if a character white space else 0 */
272 static inline int PROFILE_isspaceW(WCHAR c)
274 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
275 return isspaceW(c) || c == 0x1a;
278 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
280 int flags = IS_TEXT_UNICODE_SIGNATURE |
281 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
282 IS_TEXT_UNICODE_ODD_LENGTH;
283 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
285 *len = sizeof(bom_utf8);
286 return ENCODING_UTF8;
288 RtlIsTextUnicode(buffer, *len, &flags);
289 if (flags & IS_TEXT_UNICODE_SIGNATURE)
291 *len = sizeof(WCHAR);
292 return ENCODING_UTF16LE;
294 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
296 *len = sizeof(WCHAR);
297 return ENCODING_UTF16BE;
300 return ENCODING_ANSI;
304 /***********************************************************************
307 * Load a profile tree from a file.
309 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
311 void *buffer_base, *pBuffer;
313 const WCHAR *szLineStart, *szLineEnd;
314 const WCHAR *szValueStart, *szEnd, *next_line;
316 PROFILESECTION *section, *first_section;
317 PROFILESECTION **next_section;
318 PROFILEKEY *key, *prev_key, **next_key;
321 TRACE("%p\n", hFile);
323 dwFileSize = GetFileSize(hFile, NULL);
324 if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
327 buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
328 if (!buffer_base) return NULL;
330 if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
332 HeapFree(GetProcessHeap(), 0, buffer_base);
333 WARN("Error %d reading file\n", GetLastError());
337 *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
338 /* len is set to the number of bytes in the character marker.
339 * we want to skip these bytes */
340 pBuffer = (char *)buffer_base + len;
345 TRACE("ANSI encoding\n");
347 len = MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, NULL, 0);
348 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
351 HeapFree(GetProcessHeap(), 0, buffer_base);
354 MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, szFile, len);
355 szEnd = szFile + len;
358 TRACE("UTF8 encoding\n");
360 len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
361 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
364 HeapFree(GetProcessHeap(), 0, buffer_base);
367 MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
368 szEnd = szFile + len;
370 case ENCODING_UTF16LE:
371 TRACE("UTF16 Little Endian encoding\n");
373 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
375 case ENCODING_UTF16BE:
376 TRACE("UTF16 Big Endian encoding\n");
378 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
379 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
382 FIXME("encoding type %d not implemented\n", *pEncoding);
383 HeapFree(GetProcessHeap(), 0, buffer_base);
387 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
388 if(first_section == NULL)
390 if (szFile != pBuffer)
391 HeapFree(GetProcessHeap(), 0, szFile);
392 HeapFree(GetProcessHeap(), 0, buffer_base);
395 first_section->name[0] = 0;
396 first_section->key = NULL;
397 first_section->next = NULL;
398 next_section = &first_section->next;
399 next_key = &first_section->key;
403 while (next_line < szEnd)
405 szLineStart = next_line;
406 next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
407 if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart);
408 if (!next_line) next_line = szEnd;
410 szLineEnd = next_line;
414 /* get rid of white space */
415 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
416 while ((szLineEnd > szLineStart) && PROFILE_isspaceW(szLineEnd[-1])) szLineEnd--;
418 if (szLineStart >= szLineEnd) continue;
420 if (*szLineStart == '[') /* section start */
422 const WCHAR * szSectionEnd;
423 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
425 WARN("Invalid section header at line %d: %s\n",
426 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
431 len = (int)(szSectionEnd - szLineStart);
432 /* no need to allocate +1 for NULL terminating character as
433 * already included in structure */
434 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
436 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
437 section->name[len] = '\0';
439 section->next = NULL;
440 *next_section = section;
441 next_section = §ion->next;
442 next_key = §ion->key;
445 TRACE("New section: %s\n", debugstr_w(section->name));
451 /* get rid of white space after the name and before the start
453 len = szLineEnd - szLineStart;
454 if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
456 const WCHAR *szNameEnd = szValueStart;
457 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
458 len = szNameEnd - szLineStart;
460 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
463 if (len || !prev_key || *prev_key->name)
465 /* no need to allocate +1 for NULL terminating character as
466 * already included in structure */
467 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
468 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
469 key->name[len] = '\0';
472 len = (int)(szLineEnd - szValueStart);
473 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
474 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
475 key->value[len] = '\0';
477 else key->value = NULL;
481 next_key = &key->next;
484 TRACE("New key: name=%s, value=%s\n",
485 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
488 if (szFile != pBuffer)
489 HeapFree(GetProcessHeap(), 0, szFile);
490 HeapFree(GetProcessHeap(), 0, buffer_base);
491 return first_section;
495 /***********************************************************************
496 * PROFILE_DeleteSection
498 * Delete a section from a profile tree.
500 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
504 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
506 PROFILESECTION *to_del = *section;
507 *section = to_del->next;
509 PROFILE_Free( to_del );
512 section = &(*section)->next;
518 /***********************************************************************
521 * Delete a key from a profile tree.
523 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
524 LPCWSTR section_name, LPCWSTR key_name )
528 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
530 PROFILEKEY **key = &(*section)->key;
533 if (!strcmpiW( (*key)->name, key_name ))
535 PROFILEKEY *to_del = *key;
537 HeapFree( GetProcessHeap(), 0, to_del->value);
538 HeapFree( GetProcessHeap(), 0, to_del );
544 section = &(*section)->next;
550 /***********************************************************************
551 * PROFILE_DeleteAllKeys
553 * Delete all keys from a profile tree.
555 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
557 PROFILESECTION **section= &CurProfile->section;
560 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
562 PROFILEKEY **key = &(*section)->key;
565 PROFILEKEY *to_del = *key;
567 HeapFree( GetProcessHeap(), 0, to_del->value);
568 HeapFree( GetProcessHeap(), 0, to_del );
569 CurProfile->changed =TRUE;
572 section = &(*section)->next;
577 /***********************************************************************
580 * Find a key in a profile tree, optionally creating it.
582 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
583 LPCWSTR key_name, BOOL create, BOOL create_always )
588 while (PROFILE_isspaceW(*section_name)) section_name++;
590 p = section_name + strlenW(section_name) - 1;
594 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
595 seclen = p - section_name + 1;
597 while (PROFILE_isspaceW(*key_name)) key_name++;
599 p = key_name + strlenW(key_name) - 1;
603 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
604 keylen = p - key_name + 1;
608 if ( ((*section)->name[0])
609 && (!(strncmpiW( (*section)->name, section_name, seclen )))
610 && (((*section)->name)[seclen] == '\0') )
612 PROFILEKEY **key = &(*section)->key;
616 /* If create_always is FALSE then we check if the keyname
617 * already exists. Otherwise we add it regardless of its
618 * existence, to allow keys to be added more than once in
623 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
624 && (((*key)->name)[keylen] == '\0') )
629 if (!create) return NULL;
630 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
632 strcpyW( (*key)->name, key_name );
633 (*key)->value = NULL;
637 section = &(*section)->next;
639 if (!create) return NULL;
640 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
641 if(*section == NULL) return NULL;
642 strcpyW( (*section)->name, section_name );
643 (*section)->next = NULL;
644 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
645 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
647 HeapFree(GetProcessHeap(), 0, *section);
650 strcpyW( (*section)->key->name, key_name );
651 (*section)->key->value = NULL;
652 (*section)->key->next = NULL;
653 return (*section)->key;
657 /***********************************************************************
660 * Flush the current profile to disk if changed.
662 static BOOL PROFILE_FlushFile(void)
665 FILETIME LastWriteTime;
669 WARN("No current profile!\n");
673 if (!CurProfile->changed) return TRUE;
675 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
676 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
678 if (hFile == INVALID_HANDLE_VALUE)
680 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile->filename), GetLastError());
684 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
685 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
686 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
687 CurProfile->LastWriteTime=LastWriteTime;
688 CloseHandle( hFile );
689 CurProfile->changed = FALSE;
694 /***********************************************************************
695 * PROFILE_ReleaseFile
697 * Flush the current profile to disk and remove it from the cache.
699 static void PROFILE_ReleaseFile(void)
702 PROFILE_Free( CurProfile->section );
703 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
704 CurProfile->changed = FALSE;
705 CurProfile->section = NULL;
706 CurProfile->filename = NULL;
707 CurProfile->encoding = ENCODING_ANSI;
708 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
711 /***********************************************************************
713 * Compares a file time with the current time. If the file time is
714 * at least 2.1 seconds in the past, return true.
716 * Intended as cache safety measure: The time resolution on FAT is
717 * two seconds, so files that are not at least two seconds old might
718 * keep their time even on modification, so don't cache them.
720 static BOOL is_not_current(FILETIME * ft)
723 LONGLONG ftll, nowll;
724 GetSystemTimeAsFileTime(&Now);
725 ftll = ((LONGLONG)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
726 nowll = ((LONGLONG)Now.dwHighDateTime << 32) + Now.dwLowDateTime;
727 TRACE("%08x;%08x\n",(unsigned)ftll+21000000,(unsigned)nowll);
728 return ftll + 21000000 < nowll;
731 /***********************************************************************
734 * Open a profile file, checking the cached file first.
736 static BOOL PROFILE_Open( LPCWSTR filename, BOOL write_access )
738 WCHAR windirW[MAX_PATH];
739 WCHAR buffer[MAX_PATH];
740 HANDLE hFile = INVALID_HANDLE_VALUE;
741 FILETIME LastWriteTime;
743 PROFILE *tempProfile;
745 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
747 /* First time around */
750 for(i=0;i<N_CACHED_PROFILES;i++)
752 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
753 if(MRUProfile[i] == NULL) break;
754 MRUProfile[i]->changed=FALSE;
755 MRUProfile[i]->section=NULL;
756 MRUProfile[i]->filename=NULL;
757 MRUProfile[i]->encoding=ENCODING_ANSI;
758 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
761 GetWindowsDirectoryW( windirW, MAX_PATH );
766 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
767 !strchrW(filename, '\\') && !strchrW(filename, '/'))
769 static const WCHAR wszSeparator[] = {'\\', 0};
770 strcpyW(buffer, windirW);
771 strcatW(buffer, wszSeparator);
772 strcatW(buffer, filename);
777 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
780 TRACE("path: %s\n", debugstr_w(buffer));
782 hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
783 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
784 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
786 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
788 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
792 for(i=0;i<N_CACHED_PROFILES;i++)
794 if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
796 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
800 tempProfile=MRUProfile[i];
802 MRUProfile[j]=MRUProfile[j-1];
803 CurProfile=tempProfile;
806 if (hFile != INVALID_HANDLE_VALUE)
808 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
809 if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
810 is_not_current(&LastWriteTime))
811 TRACE("(%s): already opened (mru=%d)\n",
812 debugstr_w(buffer), i);
815 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
816 debugstr_w(buffer), i);
817 PROFILE_Free(CurProfile->section);
818 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
819 CurProfile->LastWriteTime = LastWriteTime;
823 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
824 debugstr_w(buffer), i);
829 /* Flush the old current profile */
832 /* Make the oldest profile the current one only in order to get rid of it */
833 if(i==N_CACHED_PROFILES)
835 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
836 for(i=N_CACHED_PROFILES-1;i>0;i--)
837 MRUProfile[i]=MRUProfile[i-1];
838 CurProfile=tempProfile;
840 if(CurProfile->filename) PROFILE_ReleaseFile();
842 /* OK, now that CurProfile is definitely free we assign it our new file */
843 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
844 strcpyW( CurProfile->filename, buffer );
846 if (hFile != INVALID_HANDLE_VALUE)
848 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
849 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
854 /* Does not exist yet, we will create it in PROFILE_FlushFile */
855 WARN("profile file %s not found\n", debugstr_w(buffer) );
861 /***********************************************************************
864 * Returns all keys of a section.
865 * If return_values is TRUE, also include the corresponding values.
867 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
868 LPWSTR buffer, UINT len, BOOL return_values, BOOL return_noequalkeys )
872 if(!buffer) return 0;
874 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
878 if (section->name[0] && !strcmpiW( section->name, section_name ))
881 for (key = section->key; key; key = key->next)
884 if (!*key->name) continue; /* Skip empty lines */
885 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
886 if (!return_noequalkeys && !return_values && !key->value) continue; /* Skip lines w.o. '=' */
887 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
888 len -= strlenW(buffer) + 1;
889 buffer += strlenW(buffer) + 1;
892 if (return_values && key->value) {
894 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
895 len -= strlenW(buffer) + 1;
896 buffer += strlenW(buffer) + 1;
901 /*If either lpszSection or lpszKey is NULL and the supplied
902 destination buffer is too small to hold all the strings,
903 the last string is truncated and followed by two null characters.
904 In this case, the return value is equal to cchReturnBuffer
912 section = section->next;
914 buffer[0] = buffer[1] = '\0';
918 /* See GetPrivateProfileSectionNamesA for documentation */
919 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
923 PROFILESECTION *section;
925 TRACE("(%p, %d)\n", buffer, len);
936 section = CurProfile->section;
937 while ((section!=NULL)) {
938 if (section->name[0]) {
939 tmplen = strlenW(section->name)+1;
940 if (tmplen >= buflen) {
942 memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
949 memcpy(buf, section->name, tmplen * sizeof(WCHAR));
953 section = section->next;
960 /***********************************************************************
963 * Get a profile string.
965 * Tests with GetPrivateProfileString16, W95a,
966 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
967 * section key_name def_val res buffer
968 * "set1" "1" "x" 43 [data]
969 * "set1" "1 " "x" 43 [data] (!)
970 * "set1" " 1 "' "x" 43 [data] (!)
971 * "set1" "" "x" 1 "x"
972 * "set1" "" "x " 1 "x" (!)
973 * "set1" "" " x " 3 " x" (!)
974 * "set1" NULL "x" 6 "1\02\03\0\0"
975 * "set1" "" "x" 1 "x"
976 * NULL "1" "x" 0 "" (!)
982 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
983 LPCWSTR def_val, LPWSTR buffer, UINT len, BOOL win32 )
985 PROFILEKEY *key = NULL;
986 static const WCHAR empty_strW[] = { 0 };
988 if(!buffer || !len) return 0;
990 if (!def_val) def_val = empty_strW;
995 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
996 return strlenW(buffer);
998 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
999 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
1001 TRACE("(%s,%s,%s): returning %s\n",
1002 debugstr_w(section), debugstr_w(key_name),
1003 debugstr_w(def_val), debugstr_w(buffer) );
1004 return strlenW( buffer );
1006 /* no "else" here ! */
1007 if (section && section[0])
1009 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, !win32);
1010 if (!buffer[0]) /* no luck -> def_val */
1012 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
1013 ret = strlenW(buffer);
1022 /***********************************************************************
1025 * Set a profile string.
1027 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
1028 LPCWSTR value, BOOL create_always )
1030 if (!key_name) /* Delete a whole section */
1032 TRACE("(%s)\n", debugstr_w(section_name));
1033 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1035 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1036 this is not an error on application's level.*/
1038 else if (!value) /* Delete a key */
1040 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1041 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1042 section_name, key_name );
1043 return TRUE; /* same error handling as above */
1045 else /* Set the key value */
1047 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1048 key_name, TRUE, create_always );
1049 TRACE("(%s,%s,%s):\n",
1050 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1051 if (!key) return FALSE;
1053 /* strip the leading spaces. We can safely strip \n\r and
1054 * friends too, they should not happen here anyway. */
1055 while (PROFILE_isspaceW(*value)) value++;
1059 if (!strcmpW( key->value, value ))
1061 TRACE(" no change needed\n" );
1062 return TRUE; /* No change needed */
1064 TRACE(" replacing %s\n", debugstr_w(key->value) );
1065 HeapFree( GetProcessHeap(), 0, key->value );
1067 else TRACE(" creating key\n" );
1068 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1069 strcpyW( key->value, value );
1070 CurProfile->changed = TRUE;
1076 /********************* API functions **********************************/
1079 /***********************************************************************
1080 * GetProfileIntA (KERNEL32.@)
1082 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1084 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1087 /***********************************************************************
1088 * GetProfileIntW (KERNEL32.@)
1090 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1092 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1097 * - Section names if 'section' is NULL
1098 * - Keys in a Section if 'entry' is NULL
1099 * (see MSDN doc for GetPrivateProfileString)
1101 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1102 LPCWSTR def_val, LPWSTR buffer,
1103 UINT len, LPCWSTR filename,
1107 LPWSTR defval_tmp = NULL;
1109 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1110 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1112 /* strip any trailing ' ' of def_val. */
1115 LPCWSTR p = def_val + strlenW(def_val) - 1;
1117 while (p > def_val && *p == ' ')
1122 int len = (int)(p - def_val) + 1;
1124 defval_tmp = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1125 memcpy(defval_tmp, def_val, len * sizeof(WCHAR));
1126 defval_tmp[len] = '\0';
1127 def_val = defval_tmp;
1131 RtlEnterCriticalSection( &PROFILE_CritSect );
1133 if (PROFILE_Open( filename, FALSE )) {
1134 if (win32 && (section == NULL))
1135 ret = PROFILE_GetSectionNames(buffer, len);
1137 /* PROFILE_GetString can handle the 'entry == NULL' case */
1138 ret = PROFILE_GetString( section, entry, def_val, buffer, len, win32 );
1139 } else if (buffer && def_val) {
1140 lstrcpynW( buffer, def_val, len );
1141 ret = strlenW( buffer );
1146 RtlLeaveCriticalSection( &PROFILE_CritSect );
1148 HeapFree(GetProcessHeap(), 0, defval_tmp);
1150 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1155 /***********************************************************************
1156 * GetPrivateProfileString (KERNEL.128)
1158 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1159 LPCSTR def_val, LPSTR buffer,
1160 UINT16 len, LPCSTR filename )
1162 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1164 INT16 retW, ret = 0;
1166 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1167 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1168 else sectionW.Buffer = NULL;
1169 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1170 else entryW.Buffer = NULL;
1171 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1172 else def_valW.Buffer = NULL;
1173 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1174 else filenameW.Buffer = NULL;
1176 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1177 def_valW.Buffer, bufferW, len,
1178 filenameW.Buffer, FALSE );
1181 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1188 ret--; /* strip terminating 0 */
1191 RtlFreeUnicodeString(§ionW);
1192 RtlFreeUnicodeString(&entryW);
1193 RtlFreeUnicodeString(&def_valW);
1194 RtlFreeUnicodeString(&filenameW);
1195 HeapFree(GetProcessHeap(), 0, bufferW);
1199 /***********************************************************************
1200 * GetPrivateProfileStringA (KERNEL32.@)
1202 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1203 LPCSTR def_val, LPSTR buffer,
1204 UINT len, LPCSTR filename )
1206 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1210 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1211 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1212 else sectionW.Buffer = NULL;
1213 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1214 else entryW.Buffer = NULL;
1215 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1216 else def_valW.Buffer = NULL;
1217 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1218 else filenameW.Buffer = NULL;
1220 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1221 def_valW.Buffer, bufferW, len,
1225 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1232 ret--; /* strip terminating 0 */
1235 RtlFreeUnicodeString(§ionW);
1236 RtlFreeUnicodeString(&entryW);
1237 RtlFreeUnicodeString(&def_valW);
1238 RtlFreeUnicodeString(&filenameW);
1239 HeapFree(GetProcessHeap(), 0, bufferW);
1243 /***********************************************************************
1244 * GetPrivateProfileStringW (KERNEL32.@)
1246 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1247 LPCWSTR def_val, LPWSTR buffer,
1248 UINT len, LPCWSTR filename )
1250 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1252 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1253 buffer, len, filename, TRUE );
1256 /***********************************************************************
1257 * GetProfileStringA (KERNEL32.@)
1259 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1260 LPSTR buffer, UINT len )
1262 return GetPrivateProfileStringA( section, entry, def_val,
1263 buffer, len, "win.ini" );
1266 /***********************************************************************
1267 * GetProfileStringW (KERNEL32.@)
1269 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1270 LPCWSTR def_val, LPWSTR buffer, UINT len )
1272 return GetPrivateProfileStringW( section, entry, def_val,
1273 buffer, len, wininiW );
1276 /***********************************************************************
1277 * WriteProfileStringA (KERNEL32.@)
1279 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1282 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1285 /***********************************************************************
1286 * WriteProfileStringW (KERNEL32.@)
1288 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1291 return WritePrivateProfileStringW( section, entry, string, wininiW );
1295 /***********************************************************************
1296 * GetPrivateProfileIntW (KERNEL32.@)
1298 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1299 INT def_val, LPCWSTR filename )
1302 UNICODE_STRING bufferW;
1306 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1307 buffer, sizeof(buffer)/sizeof(WCHAR),
1311 /* FIXME: if entry can be found but it's empty, then Win16 is
1312 * supposed to return 0 instead of def_val ! Difficult/problematic
1313 * to implement (every other failure also returns zero buffer),
1314 * thus wait until testing framework avail for making sure nothing
1315 * else gets broken that way. */
1316 if (!buffer[0]) return (UINT)def_val;
1318 RtlInitUnicodeString( &bufferW, buffer );
1319 RtlUnicodeStringToInteger( &bufferW, 0, &result);
1323 /***********************************************************************
1324 * GetPrivateProfileIntA (KERNEL32.@)
1326 * FIXME: rewrite using unicode
1328 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1329 INT def_val, LPCSTR filename )
1331 UNICODE_STRING entryW, filenameW, sectionW;
1333 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1334 else entryW.Buffer = NULL;
1335 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1336 else filenameW.Buffer = NULL;
1337 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1338 else sectionW.Buffer = NULL;
1339 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1341 RtlFreeUnicodeString(§ionW);
1342 RtlFreeUnicodeString(&filenameW);
1343 RtlFreeUnicodeString(&entryW);
1347 /***********************************************************************
1348 * GetPrivateProfileSectionW (KERNEL32.@)
1350 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1351 DWORD len, LPCWSTR filename )
1355 if (!section || !buffer)
1357 SetLastError(ERROR_INVALID_PARAMETER);
1361 TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1363 RtlEnterCriticalSection( &PROFILE_CritSect );
1365 if (PROFILE_Open( filename, FALSE ))
1366 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE, FALSE);
1368 RtlLeaveCriticalSection( &PROFILE_CritSect );
1373 /***********************************************************************
1374 * GetPrivateProfileSectionA (KERNEL32.@)
1376 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1377 DWORD len, LPCSTR filename )
1379 UNICODE_STRING sectionW, filenameW;
1383 if (!section || !buffer)
1385 SetLastError(ERROR_INVALID_PARAMETER);
1389 bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1390 RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1391 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1392 else filenameW.Buffer = NULL;
1394 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1397 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1413 RtlFreeUnicodeString(§ionW);
1414 RtlFreeUnicodeString(&filenameW);
1415 HeapFree(GetProcessHeap(), 0, bufferW);
1419 /***********************************************************************
1420 * GetProfileSectionA (KERNEL32.@)
1422 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1424 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1427 /***********************************************************************
1428 * GetProfileSectionW (KERNEL32.@)
1430 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1432 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1436 /***********************************************************************
1437 * WritePrivateProfileStringW (KERNEL32.@)
1439 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1440 LPCWSTR string, LPCWSTR filename )
1444 RtlEnterCriticalSection( &PROFILE_CritSect );
1446 if (!section && !entry && !string) /* documented "file flush" case */
1448 if (!filename || PROFILE_Open( filename, TRUE ))
1450 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1453 else if (PROFILE_Open( filename, TRUE ))
1456 SetLastError(ERROR_FILE_NOT_FOUND);
1458 ret = PROFILE_SetString( section, entry, string, FALSE);
1459 PROFILE_FlushFile();
1463 RtlLeaveCriticalSection( &PROFILE_CritSect );
1467 /***********************************************************************
1468 * WritePrivateProfileStringA (KERNEL32.@)
1470 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1471 LPCSTR string, LPCSTR filename )
1473 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1476 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1477 else sectionW.Buffer = NULL;
1478 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1479 else entryW.Buffer = NULL;
1480 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1481 else stringW.Buffer = NULL;
1482 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1483 else filenameW.Buffer = NULL;
1485 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1486 stringW.Buffer, filenameW.Buffer);
1487 RtlFreeUnicodeString(§ionW);
1488 RtlFreeUnicodeString(&entryW);
1489 RtlFreeUnicodeString(&stringW);
1490 RtlFreeUnicodeString(&filenameW);
1494 /***********************************************************************
1495 * WritePrivateProfileSectionW (KERNEL32.@)
1497 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1498 LPCWSTR string, LPCWSTR filename )
1503 RtlEnterCriticalSection( &PROFILE_CritSect );
1505 if (!section && !string)
1507 if (!filename || PROFILE_Open( filename, TRUE ))
1509 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1512 else if (PROFILE_Open( filename, TRUE )) {
1513 if (!string) {/* delete the named section*/
1514 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1515 PROFILE_FlushFile();
1517 PROFILE_DeleteAllKeys(section);
1520 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1521 strcpyW( buf, string );
1522 if((p = strchrW( buf, '='))) {
1524 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1526 HeapFree( GetProcessHeap(), 0, buf );
1527 string += strlenW(string)+1;
1529 PROFILE_FlushFile();
1533 RtlLeaveCriticalSection( &PROFILE_CritSect );
1537 /***********************************************************************
1538 * WritePrivateProfileSectionA (KERNEL32.@)
1540 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1541 LPCSTR string, LPCSTR filename)
1544 UNICODE_STRING sectionW, filenameW;
1553 while(*p) p += strlen(p) + 1;
1554 lenA = p - string + 1;
1555 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1556 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1557 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1559 else stringW = NULL;
1560 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1561 else sectionW.Buffer = NULL;
1562 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1563 else filenameW.Buffer = NULL;
1565 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1567 HeapFree(GetProcessHeap(), 0, stringW);
1568 RtlFreeUnicodeString(§ionW);
1569 RtlFreeUnicodeString(&filenameW);
1573 /***********************************************************************
1574 * WriteProfileSectionA (KERNEL32.@)
1576 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1579 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1582 /***********************************************************************
1583 * WriteProfileSectionW (KERNEL32.@)
1585 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1587 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1591 /***********************************************************************
1592 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1594 * Returns the section names contained in the specified file.
1595 * FIXME: Where do we find this file when the path is relative?
1596 * The section names are returned as a list of strings with an extra
1597 * '\0' to mark the end of the list. Except for that the behavior
1598 * depends on the Windows version.
1601 * - if the buffer is 0 or 1 character long then it is as if it was of
1603 * - otherwise, if the buffer is too small only the section names that fit
1605 * - note that this means if the buffer was too small to return even just
1606 * the first section name then a single '\0' will be returned.
1607 * - the return value is the number of characters written in the buffer,
1608 * except if the buffer was too small in which case len-2 is returned
1611 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1612 * '\0' and the return value is 0
1613 * - otherwise if the buffer is too small then the first section name that
1614 * does not fit is truncated so that the string list can be terminated
1615 * correctly (double '\0')
1616 * - the return value is the number of characters written in the buffer
1617 * except for the trailing '\0'. If the buffer is too small, then the
1618 * return value is len-2
1619 * - Win2000 has a bug that triggers when the section names and the
1620 * trailing '\0' fit exactly in the buffer. In that case the trailing
1623 * Wine implements the observed Win2000 behavior (except for the bug).
1625 * Note that when the buffer is big enough then the return value may be any
1626 * value between 1 and len-1 (or len in Win95), including len-2.
1628 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1633 RtlEnterCriticalSection( &PROFILE_CritSect );
1635 if (PROFILE_Open( filename, FALSE ))
1636 ret = PROFILE_GetSectionNames(buffer, size);
1638 RtlLeaveCriticalSection( &PROFILE_CritSect );
1644 /***********************************************************************
1645 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1647 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1650 UNICODE_STRING filenameW;
1654 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1655 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1656 else filenameW.Buffer = NULL;
1658 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1661 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1673 RtlFreeUnicodeString(&filenameW);
1674 HeapFree(GetProcessHeap(), 0, bufferW);
1678 /***********************************************************************
1679 * GetPrivateProfileStructW (KERNEL32.@)
1681 * Should match Win95's behaviour pretty much
1683 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1684 LPVOID buf, UINT len, LPCWSTR filename)
1688 RtlEnterCriticalSection( &PROFILE_CritSect );
1690 if (PROFILE_Open( filename, FALSE )) {
1691 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1693 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1694 if (((strlenW(k->value) - 2) / 2) == len)
1701 end = k->value + strlenW(k->value); /* -> '\0' */
1702 /* check for invalid chars in ASCII coded hex string */
1703 for (p=k->value; p < end; p++)
1707 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1708 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1715 BOOL highnibble = TRUE;
1717 LPBYTE binbuf = buf;
1719 end -= 2; /* don't include checksum in output data */
1720 /* translate ASCII hex format into binary data */
1721 for (p=k->value; p < end; p++)
1725 (c - 'A' + 10) : (c - '0');
1732 *binbuf++ = b; /* feed binary data into output */
1733 chksum += b; /* calculate checksum */
1735 highnibble ^= 1; /* toggle */
1737 /* retrieve stored checksum value */
1739 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1741 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1742 if (b == (chksum & 0xff)) /* checksums match ? */
1748 RtlLeaveCriticalSection( &PROFILE_CritSect );
1753 /***********************************************************************
1754 * GetPrivateProfileStructA (KERNEL32.@)
1756 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1757 LPVOID buffer, UINT len, LPCSTR filename)
1759 UNICODE_STRING sectionW, keyW, filenameW;
1762 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1763 else sectionW.Buffer = NULL;
1764 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1765 else keyW.Buffer = NULL;
1766 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1767 else filenameW.Buffer = NULL;
1769 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1771 /* Do not translate binary data. */
1773 RtlFreeUnicodeString(§ionW);
1774 RtlFreeUnicodeString(&keyW);
1775 RtlFreeUnicodeString(&filenameW);
1781 /***********************************************************************
1782 * WritePrivateProfileStructW (KERNEL32.@)
1784 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1785 LPVOID buf, UINT bufsize, LPCWSTR filename)
1789 LPWSTR outstring, p;
1792 if (!section && !key && !buf) /* flush the cache */
1793 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1795 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1796 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1798 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1799 *p++ = hex[*binbuf >> 4];
1800 *p++ = hex[*binbuf & 0xf];
1803 /* checksum is sum & 0xff */
1804 *p++ = hex[(sum & 0xf0) >> 4];
1805 *p++ = hex[sum & 0xf];
1808 RtlEnterCriticalSection( &PROFILE_CritSect );
1810 if (PROFILE_Open( filename, TRUE )) {
1811 ret = PROFILE_SetString( section, key, outstring, FALSE);
1812 PROFILE_FlushFile();
1815 RtlLeaveCriticalSection( &PROFILE_CritSect );
1817 HeapFree( GetProcessHeap(), 0, outstring );
1822 /***********************************************************************
1823 * WritePrivateProfileStructA (KERNEL32.@)
1825 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1826 LPVOID buf, UINT bufsize, LPCSTR filename)
1828 UNICODE_STRING sectionW, keyW, filenameW;
1831 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1832 else sectionW.Buffer = NULL;
1833 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1834 else keyW.Buffer = NULL;
1835 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1836 else filenameW.Buffer = NULL;
1838 /* Do not translate binary data. */
1839 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1842 RtlFreeUnicodeString(§ionW);
1843 RtlFreeUnicodeString(&keyW);
1844 RtlFreeUnicodeString(&filenameW);
1849 /***********************************************************************
1850 * WriteOutProfiles (KERNEL.315)
1852 void WINAPI WriteOutProfiles16(void)
1854 RtlEnterCriticalSection( &PROFILE_CritSect );
1855 PROFILE_FlushFile();
1856 RtlLeaveCriticalSection( &PROFILE_CritSect );
1859 /***********************************************************************
1860 * OpenProfileUserMapping (KERNEL32.@)
1862 BOOL WINAPI OpenProfileUserMapping(void) {
1863 FIXME("(), stub!\n");
1864 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1868 /***********************************************************************
1869 * CloseProfileUserMapping (KERNEL32.@)
1871 BOOL WINAPI CloseProfileUserMapping(void) {
1872 FIXME("(), stub!\n");
1873 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);