4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
34 #include "wine/winbase16.h"
35 #include "wine/unicode.h"
36 #include "wine/server.h"
37 #include "wine/library.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(profile);
42 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
52 typedef struct tagPROFILEKEY
55 struct tagPROFILEKEY *next;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
70 PROFILESECTION *section;
72 FILETIME LastWriteTime;
77 #define N_CACHED_PROFILES 10
79 /* Cached profile files */
80 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
82 #define CurProfile (MRUProfile[0])
84 /* Check for comments in profile */
85 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
87 static const WCHAR emptystringW[] = {0};
88 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
90 static CRITICAL_SECTION PROFILE_CritSect;
91 static CRITICAL_SECTION_DEBUG critsect_debug =
93 0, 0, &PROFILE_CritSect,
94 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
95 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
97 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
99 static const char hex[16] = "0123456789ABCDEF";
101 /***********************************************************************
104 * Copy the content of an entry into a buffer, removing quotes, and possibly
105 * translating environment variables.
107 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
114 if (strip_quote && ((*value == '\'') || (*value == '\"')))
116 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
119 lstrcpynW( buffer, value, len );
120 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
123 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
124 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
127 USHORT * shortbuffer = (USHORT *)buffer;
128 for (i = 0; i < len; i++)
129 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
132 /* writes any necessary encoding marker to the file */
133 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
135 DWORD dwBytesWritten;
142 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
144 case ENCODING_UTF16LE:
146 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
148 case ENCODING_UTF16BE:
150 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
155 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
158 int write_buffer_len;
159 DWORD dwBytesWritten;
161 TRACE("writing: %s\n", debugstr_wn(szLine, len));
166 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
167 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
168 if (!write_buffer) return;
169 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
170 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
171 HeapFree(GetProcessHeap(), 0, write_buffer);
174 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
175 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
176 if (!write_buffer) return;
177 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
178 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
179 HeapFree(GetProcessHeap(), 0, write_buffer);
181 case ENCODING_UTF16LE:
182 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
184 case ENCODING_UTF16BE:
185 PROFILE_ByteSwapShortBuffer(szLine, len);
186 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
189 FIXME("encoding type %d not implemented\n", encoding);
193 /***********************************************************************
196 * Save a profile tree to a file.
198 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
203 PROFILE_WriteMarker(hFile, encoding);
205 for ( ; section; section = section->next)
209 if (section->name[0]) len += strlenW(section->name) + 6;
211 for (key = section->key; key; key = key->next)
213 len += strlenW(key->name) + 2;
214 if (key->value) len += strlenW(key->value) + 1;
217 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
221 if (section->name[0])
226 strcpyW( p, section->name );
232 for (key = section->key; key; key = key->next)
234 strcpyW( p, key->name );
239 strcpyW( p, key->value );
245 PROFILE_WriteLine( hFile, buffer, len, encoding );
246 HeapFree(GetProcessHeap(), 0, buffer);
251 /***********************************************************************
254 * Free a profile tree.
256 static void PROFILE_Free( PROFILESECTION *section )
258 PROFILESECTION *next_section;
259 PROFILEKEY *key, *next_key;
261 for ( ; section; section = next_section)
263 for (key = section->key; key; key = next_key)
265 next_key = key->next;
266 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
267 HeapFree( GetProcessHeap(), 0, key );
269 next_section = section->next;
270 HeapFree( GetProcessHeap(), 0, section );
274 /* returns 1 if a character white space else 0 */
275 static inline int PROFILE_isspaceW(WCHAR c)
277 if (isspaceW(c)) return 1;
278 if (c=='\r' || c==0x1a) return 1;
279 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
283 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
285 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
286 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
287 IS_TEXT_UNICODE_ODD_LENGTH;
288 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
290 *len = sizeof(bom_utf8);
291 return ENCODING_UTF8;
293 RtlIsTextUnicode((void *)buffer, *len, &flags);
294 if (flags & IS_TEXT_UNICODE_SIGNATURE)
296 *len = sizeof(WCHAR);
297 return ENCODING_UTF16LE;
299 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
301 *len = sizeof(WCHAR);
302 return ENCODING_UTF16BE;
305 return ENCODING_ANSI;
308 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
310 return memchrW(szStart, '\n', szEnd - szStart);
313 /***********************************************************************
316 * Load a profile tree from a file.
318 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
322 const WCHAR *szLineStart, *szLineEnd;
323 const WCHAR *szValueStart, *szNameEnd, *szEnd;
325 PROFILESECTION *section, *first_section;
326 PROFILESECTION **next_section;
327 PROFILEKEY *key, *prev_key, **next_key;
330 TRACE("%p\n", hFile);
332 dwFileSize = GetFileSize(hFile, NULL);
333 if (dwFileSize == INVALID_FILE_SIZE)
336 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
337 if (!pBuffer) return NULL;
339 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
341 HeapFree(GetProcessHeap(), 0, pBuffer);
342 WARN("Error %ld reading file\n", GetLastError());
346 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
347 /* len is set to the number of bytes in the character marker.
348 * we want to skip these bytes */
349 pBuffer = (char *)pBuffer + len;
354 TRACE("ANSI encoding\n");
356 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
357 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
360 HeapFree(GetProcessHeap(), 0, pBuffer);
363 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
364 szEnd = szFile + len;
367 TRACE("UTF8 encoding\n");
369 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
370 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
373 HeapFree(GetProcessHeap(), 0, pBuffer);
376 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
377 szEnd = szFile + len;
379 case ENCODING_UTF16LE:
380 TRACE("UTF16 Little Endian encoding\n");
381 szFile = (WCHAR *)pBuffer + 1;
382 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
384 case ENCODING_UTF16BE:
385 TRACE("UTF16 Big Endian encoding\n");
386 szFile = (WCHAR *)pBuffer + 1;
387 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
388 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
391 FIXME("encoding type %d not implemented\n", *pEncoding);
392 HeapFree(GetProcessHeap(), 0, pBuffer);
396 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
397 if(first_section == NULL)
399 if (szFile != pBuffer)
400 HeapFree(GetProcessHeap(), 0, szFile);
401 HeapFree(GetProcessHeap(), 0, pBuffer);
404 first_section->name[0] = 0;
405 first_section->key = NULL;
406 first_section->next = NULL;
407 next_section = &first_section->next;
408 next_key = &first_section->key;
410 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
414 szLineStart = szLineEnd + 1;
415 if (szLineStart >= szEnd)
417 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
422 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
424 if (szLineStart >= szLineEnd) continue;
426 if (*szLineStart == '[') /* section start */
428 const WCHAR * szSectionEnd;
429 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
431 WARN("Invalid section header at line %d: %s\n",
432 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
437 len = (int)(szSectionEnd - szLineStart);
438 /* no need to allocate +1 for NULL terminating character as
439 * already included in structure */
440 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
442 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
443 section->name[len] = '\0';
445 section->next = NULL;
446 *next_section = section;
447 next_section = §ion->next;
448 next_key = §ion->key;
451 TRACE("New section: %s\n", debugstr_w(section->name));
457 /* get rid of white space at the end of the line */
458 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
460 /* line end should be pointing to character *after* the last wanted character */
463 /* get rid of white space after the name and before the start
465 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
467 szNameEnd = szValueStart - 1;
468 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
470 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
473 szNameEnd = szLineEnd - 1;
474 /* name end should be pointing to character *after* the last wanted character */
477 len = (int)(szNameEnd - szLineStart);
479 if (len || !prev_key || *prev_key->name)
481 /* no need to allocate +1 for NULL terminating character as
482 * already included in structure */
483 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
484 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
485 key->name[len] = '\0';
488 len = (int)(szLineEnd - szValueStart);
489 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
490 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
491 key->value[len] = '\0';
493 else key->value = NULL;
497 next_key = &key->next;
500 TRACE("New key: name=%s, value=%s\n",
501 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
504 if (szFile != pBuffer)
505 HeapFree(GetProcessHeap(), 0, szFile);
506 HeapFree(GetProcessHeap(), 0, pBuffer);
507 return first_section;
511 /***********************************************************************
512 * PROFILE_DeleteSection
514 * Delete a section from a profile tree.
516 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
520 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
522 PROFILESECTION *to_del = *section;
523 *section = to_del->next;
525 PROFILE_Free( to_del );
528 section = &(*section)->next;
534 /***********************************************************************
537 * Delete a key from a profile tree.
539 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
540 LPCWSTR section_name, LPCWSTR key_name )
544 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
546 PROFILEKEY **key = &(*section)->key;
549 if (!strcmpiW( (*key)->name, key_name ))
551 PROFILEKEY *to_del = *key;
553 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
554 HeapFree( GetProcessHeap(), 0, to_del );
560 section = &(*section)->next;
566 /***********************************************************************
567 * PROFILE_DeleteAllKeys
569 * Delete all keys from a profile tree.
571 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
573 PROFILESECTION **section= &CurProfile->section;
576 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
578 PROFILEKEY **key = &(*section)->key;
581 PROFILEKEY *to_del = *key;
583 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
584 HeapFree( GetProcessHeap(), 0, to_del );
585 CurProfile->changed =TRUE;
588 section = &(*section)->next;
593 /***********************************************************************
596 * Find a key in a profile tree, optionally creating it.
598 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
599 LPCWSTR key_name, BOOL create, BOOL create_always )
604 while (PROFILE_isspaceW(*section_name)) section_name++;
605 p = section_name + strlenW(section_name) - 1;
606 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
607 seclen = p - section_name + 1;
609 while (PROFILE_isspaceW(*key_name)) key_name++;
610 p = key_name + strlenW(key_name) - 1;
611 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
612 keylen = p - key_name + 1;
616 if ( ((*section)->name[0])
617 && (!(strncmpiW( (*section)->name, section_name, seclen )))
618 && (((*section)->name)[seclen] == '\0') )
620 PROFILEKEY **key = &(*section)->key;
624 /* If create_always is FALSE then we check if the keyname
625 * already exists. Otherwise we add it regardless of its
626 * existence, to allow keys to be added more than once in
631 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
632 && (((*key)->name)[keylen] == '\0') )
637 if (!create) return NULL;
638 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
640 strcpyW( (*key)->name, key_name );
641 (*key)->value = NULL;
645 section = &(*section)->next;
647 if (!create) return NULL;
648 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
649 if(*section == NULL) return NULL;
650 strcpyW( (*section)->name, section_name );
651 (*section)->next = NULL;
652 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
653 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
655 HeapFree(GetProcessHeap(), 0, *section);
658 strcpyW( (*section)->key->name, key_name );
659 (*section)->key->value = NULL;
660 (*section)->key->next = NULL;
661 return (*section)->key;
665 /***********************************************************************
668 * Flush the current profile to disk if changed.
670 static BOOL PROFILE_FlushFile(void)
673 FILETIME LastWriteTime;
677 WARN("No current profile!\n");
681 if (!CurProfile->changed) return TRUE;
683 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
685 if (hFile == INVALID_HANDLE_VALUE)
687 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
691 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
692 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
693 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
694 CurProfile->LastWriteTime=LastWriteTime;
695 CloseHandle( hFile );
696 CurProfile->changed = FALSE;
701 /***********************************************************************
702 * PROFILE_ReleaseFile
704 * Flush the current profile to disk and remove it from the cache.
706 static void PROFILE_ReleaseFile(void)
709 PROFILE_Free( CurProfile->section );
710 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
711 CurProfile->changed = FALSE;
712 CurProfile->section = NULL;
713 CurProfile->filename = NULL;
714 CurProfile->encoding = ENCODING_ANSI;
715 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
719 /***********************************************************************
722 * Open a profile file, checking the cached file first.
724 static BOOL PROFILE_Open( LPCWSTR filename )
726 WCHAR windirW[MAX_PATH];
727 WCHAR buffer[MAX_PATH];
728 HANDLE hFile = INVALID_HANDLE_VALUE;
729 FILETIME LastWriteTime;
731 PROFILE *tempProfile;
733 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
735 /* First time around */
738 for(i=0;i<N_CACHED_PROFILES;i++)
740 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
741 if(MRUProfile[i] == NULL) break;
742 MRUProfile[i]->changed=FALSE;
743 MRUProfile[i]->section=NULL;
744 MRUProfile[i]->filename=NULL;
745 MRUProfile[i]->encoding=ENCODING_ANSI;
746 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
749 GetWindowsDirectoryW( windirW, MAX_PATH );
751 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
752 !strchrW(filename, '\\') && !strchrW(filename, '/'))
754 static const WCHAR wszSeparator[] = {'\\', 0};
755 strcpyW(buffer, windirW);
756 strcatW(buffer, wszSeparator);
757 strcatW(buffer, filename);
762 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
765 TRACE("path: %s\n", debugstr_w(buffer));
767 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
769 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
771 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
775 for(i=0;i<N_CACHED_PROFILES;i++)
777 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
779 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
783 tempProfile=MRUProfile[i];
785 MRUProfile[j]=MRUProfile[j-1];
786 CurProfile=tempProfile;
788 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
789 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
790 TRACE("(%s): already opened (mru=%d)\n",
791 debugstr_w(buffer), i );
793 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
794 debugstr_w(buffer), i );
800 /* Flush the old current profile */
803 /* Make the oldest profile the current one only in order to get rid of it */
804 if(i==N_CACHED_PROFILES)
806 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
807 for(i=N_CACHED_PROFILES-1;i>0;i--)
808 MRUProfile[i]=MRUProfile[i-1];
809 CurProfile=tempProfile;
811 if(CurProfile->filename) PROFILE_ReleaseFile();
813 /* OK, now that CurProfile is definitely free we assign it our new file */
814 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
815 strcpyW( CurProfile->filename, buffer );
817 if (hFile != INVALID_HANDLE_VALUE)
819 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
820 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
825 /* Does not exist yet, we will create it in PROFILE_FlushFile */
826 WARN("profile file %s not found\n", debugstr_w(buffer) );
832 /***********************************************************************
835 * Returns all keys of a section.
836 * If return_values is TRUE, also include the corresponding values.
838 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
839 LPWSTR buffer, UINT len, BOOL return_values )
843 if(!buffer) return 0;
845 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
849 if (section->name[0] && !strcmpiW( section->name, section_name ))
852 for (key = section->key; key; key = key->next)
855 if (!*key->name) continue; /* Skip empty lines */
856 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
857 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
858 len -= strlenW(buffer) + 1;
859 buffer += strlenW(buffer) + 1;
862 if (return_values && key->value) {
864 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
865 len -= strlenW(buffer) + 1;
866 buffer += strlenW(buffer) + 1;
871 /*If either lpszSection or lpszKey is NULL and the supplied
872 destination buffer is too small to hold all the strings,
873 the last string is truncated and followed by two null characters.
874 In this case, the return value is equal to cchReturnBuffer
882 section = section->next;
884 buffer[0] = buffer[1] = '\0';
888 /* See GetPrivateProfileSectionNamesA for documentation */
889 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
893 PROFILESECTION *section;
895 TRACE("(%p, %d)\n", buffer, len);
906 section = CurProfile->section;
907 while ((section!=NULL)) {
908 if (section->name[0]) {
909 l = strlenW(section->name)+1;
912 strncpyW(buf, section->name, f-1);
919 strcpyW(buf, section->name);
923 section = section->next;
930 /***********************************************************************
933 * Get a profile string.
935 * Tests with GetPrivateProfileString16, W95a,
936 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
937 * section key_name def_val res buffer
938 * "set1" "1" "x" 43 [data]
939 * "set1" "1 " "x" 43 [data] (!)
940 * "set1" " 1 "' "x" 43 [data] (!)
941 * "set1" "" "x" 1 "x"
942 * "set1" "" "x " 1 "x" (!)
943 * "set1" "" " x " 3 " x" (!)
944 * "set1" NULL "x" 6 "1\02\03\0\0"
945 * "set1" "" "x" 1 "x"
946 * NULL "1" "x" 0 "" (!)
952 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
953 LPCWSTR def_val, LPWSTR buffer, UINT len )
955 PROFILEKEY *key = NULL;
956 static const WCHAR empty_strW[] = { 0 };
958 if(!buffer) return 0;
960 if (!def_val) def_val = empty_strW;
965 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
968 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
969 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
971 TRACE("(%s,%s,%s): returning %s\n",
972 debugstr_w(section), debugstr_w(key_name),
973 debugstr_w(def_val), debugstr_w(buffer) );
974 return strlenW( buffer );
976 /* no "else" here ! */
977 if (section && section[0])
979 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
980 if (!buffer[0]) /* no luck -> def_val */
982 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
983 ret = strlenW(buffer);
992 /***********************************************************************
995 * Set a profile string.
997 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
998 LPCWSTR value, BOOL create_always )
1000 if (!key_name) /* Delete a whole section */
1002 TRACE("(%s)\n", debugstr_w(section_name));
1003 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1005 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1006 this is not an error on application's level.*/
1008 else if (!value) /* Delete a key */
1010 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1011 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1012 section_name, key_name );
1013 return TRUE; /* same error handling as above */
1015 else /* Set the key value */
1017 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1018 key_name, TRUE, create_always );
1019 TRACE("(%s,%s,%s):\n",
1020 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1021 if (!key) return FALSE;
1023 /* strip the leading spaces. We can safely strip \n\r and
1024 * friends too, they should not happen here anyway. */
1025 while (PROFILE_isspaceW(*value)) value++;
1029 if (!strcmpW( key->value, value ))
1031 TRACE(" no change needed\n" );
1032 return TRUE; /* No change needed */
1034 TRACE(" replacing %s\n", debugstr_w(key->value) );
1035 HeapFree( GetProcessHeap(), 0, key->value );
1037 else TRACE(" creating key\n" );
1038 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1039 strcpyW( key->value, value );
1040 CurProfile->changed = TRUE;
1046 /********************* API functions **********************************/
1049 /***********************************************************************
1050 * GetProfileIntA (KERNEL32.@)
1052 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1054 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1057 /***********************************************************************
1058 * GetProfileIntW (KERNEL32.@)
1060 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1062 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1066 * if allow_section_name_copy is TRUE, allow the copying :
1067 * - of Section names if 'section' is NULL
1068 * - of Keys in a Section if 'entry' is NULL
1069 * (see MSDN doc for GetPrivateProfileString)
1071 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1072 LPCWSTR def_val, LPWSTR buffer,
1073 UINT len, LPCWSTR filename,
1074 BOOL allow_section_name_copy )
1077 LPCWSTR pDefVal = NULL;
1082 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1083 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1085 /* strip any trailing ' ' of def_val. */
1088 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1096 if (*p == ' ') /* ouch, contained trailing ' ' */
1098 int len = (int)(p - def_val);
1101 p = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1102 strncpyW(p, def_val, len);
1108 pDefVal = (LPCWSTR)def_val;
1110 RtlEnterCriticalSection( &PROFILE_CritSect );
1112 if (PROFILE_Open( filename )) {
1113 if ((allow_section_name_copy) && (section == NULL))
1114 ret = PROFILE_GetSectionNames(buffer, len);
1116 /* PROFILE_GetString already handles the 'entry == NULL' case */
1117 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1119 lstrcpynW( buffer, pDefVal, len );
1120 ret = strlenW( buffer );
1123 RtlLeaveCriticalSection( &PROFILE_CritSect );
1125 if (pDefVal != def_val) /* allocated */
1126 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1128 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1133 /***********************************************************************
1134 * GetPrivateProfileString (KERNEL.128)
1136 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1137 LPCSTR def_val, LPSTR buffer,
1138 UINT16 len, LPCSTR filename )
1140 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1142 INT16 retW, ret = 0;
1144 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1145 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1146 else sectionW.Buffer = NULL;
1147 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1148 else entryW.Buffer = NULL;
1149 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1150 else def_valW.Buffer = NULL;
1151 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1152 else filenameW.Buffer = NULL;
1154 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1155 def_valW.Buffer, bufferW, len,
1156 filenameW.Buffer, FALSE );
1159 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1166 ret--; /* strip terminating 0 */
1169 RtlFreeUnicodeString(§ionW);
1170 RtlFreeUnicodeString(&entryW);
1171 RtlFreeUnicodeString(&def_valW);
1172 RtlFreeUnicodeString(&filenameW);
1173 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1177 /***********************************************************************
1178 * GetPrivateProfileStringA (KERNEL32.@)
1180 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1181 LPCSTR def_val, LPSTR buffer,
1182 UINT len, LPCSTR filename )
1184 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1188 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1189 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1190 else sectionW.Buffer = NULL;
1191 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1192 else entryW.Buffer = NULL;
1193 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1194 else def_valW.Buffer = NULL;
1195 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1196 else filenameW.Buffer = NULL;
1198 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1199 def_valW.Buffer, bufferW, len,
1203 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1210 ret--; /* strip terminating 0 */
1213 RtlFreeUnicodeString(§ionW);
1214 RtlFreeUnicodeString(&entryW);
1215 RtlFreeUnicodeString(&def_valW);
1216 RtlFreeUnicodeString(&filenameW);
1217 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1221 /***********************************************************************
1222 * GetPrivateProfileStringW (KERNEL32.@)
1224 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1225 LPCWSTR def_val, LPWSTR buffer,
1226 UINT len, LPCWSTR filename )
1228 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1230 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1231 buffer, len, filename, TRUE );
1234 /***********************************************************************
1235 * GetProfileStringA (KERNEL32.@)
1237 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1238 LPSTR buffer, UINT len )
1240 return GetPrivateProfileStringA( section, entry, def_val,
1241 buffer, len, "win.ini" );
1244 /***********************************************************************
1245 * GetProfileStringW (KERNEL32.@)
1247 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1248 LPCWSTR def_val, LPWSTR buffer, UINT len )
1250 return GetPrivateProfileStringW( section, entry, def_val,
1251 buffer, len, wininiW );
1254 /***********************************************************************
1255 * WriteProfileStringA (KERNEL32.@)
1257 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1260 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1263 /***********************************************************************
1264 * WriteProfileStringW (KERNEL32.@)
1266 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1269 return WritePrivateProfileStringW( section, entry, string, wininiW );
1273 /***********************************************************************
1274 * GetPrivateProfileIntW (KERNEL32.@)
1276 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1277 INT def_val, LPCWSTR filename )
1280 UNICODE_STRING bufferW;
1284 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1285 buffer, sizeof(buffer)/sizeof(WCHAR),
1289 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1291 /* FIXME: if entry can be found but it's empty, then Win16 is
1292 * supposed to return 0 instead of def_val ! Difficult/problematic
1293 * to implement (every other failure also returns zero buffer),
1294 * thus wait until testing framework avail for making sure nothing
1295 * else gets broken that way. */
1296 if (!buffer[0]) return (UINT)def_val;
1298 RtlInitUnicodeString( &bufferW, buffer );
1299 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1303 /***********************************************************************
1304 * GetPrivateProfileIntA (KERNEL32.@)
1306 * FIXME: rewrite using unicode
1308 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1309 INT def_val, LPCSTR filename )
1311 UNICODE_STRING entryW, filenameW, sectionW;
1313 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1314 else entryW.Buffer = NULL;
1315 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1316 else filenameW.Buffer = NULL;
1317 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1318 else sectionW.Buffer = NULL;
1319 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1321 RtlFreeUnicodeString(§ionW);
1322 RtlFreeUnicodeString(&filenameW);
1323 RtlFreeUnicodeString(&entryW);
1327 /***********************************************************************
1328 * GetPrivateProfileSectionW (KERNEL32.@)
1330 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1331 DWORD len, LPCWSTR filename )
1335 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1337 RtlEnterCriticalSection( &PROFILE_CritSect );
1339 if (PROFILE_Open( filename ))
1340 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1342 RtlLeaveCriticalSection( &PROFILE_CritSect );
1347 /***********************************************************************
1348 * GetPrivateProfileSectionA (KERNEL32.@)
1350 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1351 DWORD len, LPCSTR filename )
1353 UNICODE_STRING sectionW, filenameW;
1357 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1358 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1359 else sectionW.Buffer = NULL;
1360 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1361 else filenameW.Buffer = NULL;
1363 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1366 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1382 RtlFreeUnicodeString(§ionW);
1383 RtlFreeUnicodeString(&filenameW);
1384 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1388 /***********************************************************************
1389 * GetProfileSectionA (KERNEL32.@)
1391 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1393 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1396 /***********************************************************************
1397 * GetProfileSectionW (KERNEL32.@)
1399 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1401 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1405 /***********************************************************************
1406 * WritePrivateProfileStringW (KERNEL32.@)
1408 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1409 LPCWSTR string, LPCWSTR filename )
1413 RtlEnterCriticalSection( &PROFILE_CritSect );
1415 if (PROFILE_Open( filename ))
1417 if (!section && !entry && !string) /* documented "file flush" case */
1419 PROFILE_FlushFile();
1420 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1424 FIXME("(NULL?,%s,%s,%s)?\n",
1425 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1427 ret = PROFILE_SetString( section, entry, string, FALSE);
1428 PROFILE_FlushFile();
1433 RtlLeaveCriticalSection( &PROFILE_CritSect );
1437 /***********************************************************************
1438 * WritePrivateProfileStringA (KERNEL32.@)
1440 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1441 LPCSTR string, LPCSTR filename )
1443 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1446 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1447 else sectionW.Buffer = NULL;
1448 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1449 else entryW.Buffer = NULL;
1450 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1451 else stringW.Buffer = NULL;
1452 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1453 else filenameW.Buffer = NULL;
1455 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1456 stringW.Buffer, filenameW.Buffer);
1457 RtlFreeUnicodeString(§ionW);
1458 RtlFreeUnicodeString(&entryW);
1459 RtlFreeUnicodeString(&stringW);
1460 RtlFreeUnicodeString(&filenameW);
1464 /***********************************************************************
1465 * WritePrivateProfileSectionW (KERNEL32.@)
1467 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1468 LPCWSTR string, LPCWSTR filename )
1473 RtlEnterCriticalSection( &PROFILE_CritSect );
1475 if (PROFILE_Open( filename )) {
1476 if (!section && !string)
1477 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1478 else if (!string) {/* delete the named section*/
1479 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1480 PROFILE_FlushFile();
1482 PROFILE_DeleteAllKeys(section);
1485 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1486 strcpyW( buf, string );
1487 if((p = strchrW( buf, '='))) {
1489 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1491 HeapFree( GetProcessHeap(), 0, buf );
1492 string += strlenW(string)+1;
1494 PROFILE_FlushFile();
1498 RtlLeaveCriticalSection( &PROFILE_CritSect );
1502 /***********************************************************************
1503 * WritePrivateProfileSectionA (KERNEL32.@)
1505 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1506 LPCSTR string, LPCSTR filename)
1509 UNICODE_STRING sectionW, filenameW;
1518 while(*p) p += strlen(p) + 1;
1519 lenA = p - string + 1;
1520 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1521 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1522 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1524 else stringW = NULL;
1525 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1526 else sectionW.Buffer = NULL;
1527 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1528 else filenameW.Buffer = NULL;
1530 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1532 HeapFree(GetProcessHeap(), 0, stringW);
1533 RtlFreeUnicodeString(§ionW);
1534 RtlFreeUnicodeString(&filenameW);
1538 /***********************************************************************
1539 * WriteProfileSectionA (KERNEL32.@)
1541 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1544 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1547 /***********************************************************************
1548 * WriteProfileSectionW (KERNEL32.@)
1550 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1552 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1556 /***********************************************************************
1557 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1559 * Returns the section names contained in the specified file.
1560 * FIXME: Where do we find this file when the path is relative?
1561 * The section names are returned as a list of strings with an extra
1562 * '\0' to mark the end of the list. Except for that the behavior
1563 * depends on the Windows version.
1566 * - if the buffer is 0 or 1 character long then it is as if it was of
1568 * - otherwise, if the buffer is to small only the section names that fit
1570 * - note that this means if the buffer was to small to return even just
1571 * the first section name then a single '\0' will be returned.
1572 * - the return value is the number of characters written in the buffer,
1573 * except if the buffer was too smal in which case len-2 is returned
1576 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1577 * '\0' and the return value is 0
1578 * - otherwise if the buffer is too small then the first section name that
1579 * does not fit is truncated so that the string list can be terminated
1580 * correctly (double '\0')
1581 * - the return value is the number of characters written in the buffer
1582 * except for the trailing '\0'. If the buffer is too small, then the
1583 * return value is len-2
1584 * - Win2000 has a bug that triggers when the section names and the
1585 * trailing '\0' fit exactly in the buffer. In that case the trailing
1588 * Wine implements the observed Win2000 behavior (except for the bug).
1590 * Note that when the buffer is big enough then the return value may be any
1591 * value between 1 and len-1 (or len in Win95), including len-2.
1593 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1598 RtlEnterCriticalSection( &PROFILE_CritSect );
1600 if (PROFILE_Open( filename ))
1601 ret = PROFILE_GetSectionNames(buffer, size);
1603 RtlLeaveCriticalSection( &PROFILE_CritSect );
1609 /***********************************************************************
1610 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1612 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1615 UNICODE_STRING filenameW;
1619 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1620 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1621 else filenameW.Buffer = NULL;
1623 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1626 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1634 RtlFreeUnicodeString(&filenameW);
1635 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1639 /***********************************************************************
1640 * GetPrivateProfileStructW (KERNEL32.@)
1642 * Should match Win95's behaviour pretty much
1644 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1645 LPVOID buf, UINT len, LPCWSTR filename)
1649 RtlEnterCriticalSection( &PROFILE_CritSect );
1651 if (PROFILE_Open( filename )) {
1652 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1654 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1655 if (((strlenW(k->value) - 2) / 2) == len)
1662 end = k->value + strlenW(k->value); /* -> '\0' */
1663 /* check for invalid chars in ASCII coded hex string */
1664 for (p=k->value; p < end; p++)
1668 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1669 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1676 BOOL highnibble = TRUE;
1678 LPBYTE binbuf = (LPBYTE)buf;
1680 end -= 2; /* don't include checksum in output data */
1681 /* translate ASCII hex format into binary data */
1682 for (p=k->value; p < end; p++)
1686 (c - 'A' + 10) : (c - '0');
1693 *binbuf++ = b; /* feed binary data into output */
1694 chksum += b; /* calculate checksum */
1696 highnibble ^= 1; /* toggle */
1698 /* retrieve stored checksum value */
1700 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1702 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1703 if (b == (chksum & 0xff)) /* checksums match ? */
1709 RtlLeaveCriticalSection( &PROFILE_CritSect );
1714 /***********************************************************************
1715 * GetPrivateProfileStructA (KERNEL32.@)
1717 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1718 LPVOID buffer, UINT len, LPCSTR filename)
1720 UNICODE_STRING sectionW, keyW, filenameW;
1723 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1724 else sectionW.Buffer = NULL;
1725 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1726 else keyW.Buffer = NULL;
1727 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1728 else filenameW.Buffer = NULL;
1730 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1732 /* Do not translate binary data. */
1734 RtlFreeUnicodeString(§ionW);
1735 RtlFreeUnicodeString(&keyW);
1736 RtlFreeUnicodeString(&filenameW);
1742 /***********************************************************************
1743 * WritePrivateProfileStructW (KERNEL32.@)
1745 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1746 LPVOID buf, UINT bufsize, LPCWSTR filename)
1750 LPWSTR outstring, p;
1753 if (!section && !key && !buf) /* flush the cache */
1754 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1756 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1757 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1759 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1760 *p++ = hex[*binbuf >> 4];
1761 *p++ = hex[*binbuf & 0xf];
1764 /* checksum is sum & 0xff */
1765 *p++ = hex[(sum & 0xf0) >> 4];
1766 *p++ = hex[sum & 0xf];
1769 RtlEnterCriticalSection( &PROFILE_CritSect );
1771 if (PROFILE_Open( filename )) {
1772 ret = PROFILE_SetString( section, key, outstring, FALSE);
1773 PROFILE_FlushFile();
1776 RtlLeaveCriticalSection( &PROFILE_CritSect );
1778 HeapFree( GetProcessHeap(), 0, outstring );
1783 /***********************************************************************
1784 * WritePrivateProfileStructA (KERNEL32.@)
1786 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1787 LPVOID buf, UINT bufsize, LPCSTR filename)
1789 UNICODE_STRING sectionW, keyW, filenameW;
1792 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1793 else sectionW.Buffer = NULL;
1794 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1795 else keyW.Buffer = NULL;
1796 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1797 else filenameW.Buffer = NULL;
1799 /* Do not translate binary data. */
1800 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1803 RtlFreeUnicodeString(§ionW);
1804 RtlFreeUnicodeString(&keyW);
1805 RtlFreeUnicodeString(&filenameW);
1810 /***********************************************************************
1811 * WriteOutProfiles (KERNEL.315)
1813 void WINAPI WriteOutProfiles16(void)
1815 RtlEnterCriticalSection( &PROFILE_CritSect );
1816 PROFILE_FlushFile();
1817 RtlLeaveCriticalSection( &PROFILE_CritSect );
1820 /***********************************************************************
1821 * CloseProfileUserMapping (KERNEL32.@)
1823 BOOL WINAPI CloseProfileUserMapping(void) {
1824 FIXME("(), stub!\n");
1825 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);