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"
36 #include "wine/unicode.h"
37 #include "wine/server.h"
38 #include "wine/library.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(profile);
43 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
53 typedef struct tagPROFILEKEY
56 struct tagPROFILEKEY *next;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY *key;
63 struct tagPROFILESECTION *next;
71 PROFILESECTION *section;
73 FILETIME LastWriteTime;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR emptystringW[] = {0};
91 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect;
94 static CRITICAL_SECTION_DEBUG critsect_debug =
96 0, 0, &PROFILE_CritSect,
97 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
98 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
100 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
102 static const char hex[16] = "0123456789ABCDEF";
104 /***********************************************************************
107 * Copy the content of an entry into a buffer, removing quotes, and possibly
108 * translating environment variables.
110 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
117 if (strip_quote && ((*value == '\'') || (*value == '\"')))
119 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
122 lstrcpynW( buffer, value, len );
123 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
126 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
127 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
130 USHORT * shortbuffer = (USHORT *)buffer;
131 for (i = 0; i < len; i++)
132 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
135 /* writes any necessary encoding marker to the file */
136 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
138 DWORD dwBytesWritten;
145 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
147 case ENCODING_UTF16LE:
149 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
151 case ENCODING_UTF16BE:
153 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
158 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
160 char write_buffer[PROFILE_MAX_LINE_LEN];
161 DWORD dwBytesWritten;
163 TRACE("writing: %s\n", debugstr_wn(szLine, len));
168 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
169 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
172 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
173 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
175 case ENCODING_UTF16LE:
176 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
178 case ENCODING_UTF16BE:
179 PROFILE_ByteSwapShortBuffer(szLine, len);
180 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
183 FIXME("encoding type %d not implemented\n", encoding);
187 /***********************************************************************
190 * Save a profile tree to a file.
192 static void PROFILE_Save( HANDLE hFile, PROFILESECTION *section, ENCODING encoding )
194 static const WCHAR wSectionFormat[] = {'\r','\n','[','%','s',']','\r','\n',0};
195 static const WCHAR wNameFormat[] = {'%','s',0};
196 static const WCHAR wValueFormat[] = {'=','%','s',0};
197 static const WCHAR wNewLine[] = {'\r','\n',0};
199 WCHAR szLine[PROFILE_MAX_LINE_LEN];
202 PROFILE_WriteMarker(hFile, encoding);
204 for ( ; section; section = section->next)
206 if (section->name[0])
208 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wSectionFormat, section->name );
209 PROFILE_WriteLine( hFile, szLine, len, encoding );
213 for (key = section->key; key; key = key->next)
215 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNameFormat, key->name );
217 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wValueFormat, key->value );
218 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNewLine );
219 PROFILE_WriteLine( hFile, szLine, len, encoding );
226 /***********************************************************************
229 * Free a profile tree.
231 static void PROFILE_Free( PROFILESECTION *section )
233 PROFILESECTION *next_section;
234 PROFILEKEY *key, *next_key;
236 for ( ; section; section = next_section)
238 for (key = section->key; key; key = next_key)
240 next_key = key->next;
241 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
242 HeapFree( GetProcessHeap(), 0, key );
244 next_section = section->next;
245 HeapFree( GetProcessHeap(), 0, section );
249 /* returns 1 if a character white space else 0 */
250 static inline int PROFILE_isspaceW(WCHAR c)
252 if (isspaceW(c)) return 1;
253 if (c=='\r' || c==0x1a) return 1;
254 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
258 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
260 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
261 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
262 IS_TEXT_UNICODE_ODD_LENGTH;
263 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
265 *len = sizeof(bom_utf8);
266 return ENCODING_UTF8;
268 RtlIsTextUnicode((void *)buffer, *len, &flags);
269 if (flags & IS_TEXT_UNICODE_SIGNATURE)
271 *len = sizeof(WCHAR);
272 return ENCODING_UTF16LE;
274 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
276 *len = sizeof(WCHAR);
277 return ENCODING_UTF16BE;
280 return ENCODING_ANSI;
283 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
285 return memchrW(szStart, '\n', szEnd - szStart);
288 /***********************************************************************
291 * Load a profile tree from a file.
293 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
297 const WCHAR *szLineStart, *szLineEnd;
298 const WCHAR *szValueStart, *szNameEnd, *szEnd;
300 PROFILESECTION *section, *first_section;
301 PROFILESECTION **next_section;
302 PROFILEKEY *key, *prev_key, **next_key;
305 TRACE("%p\n", hFile);
307 dwFileSize = GetFileSize(hFile, NULL);
308 if (dwFileSize == INVALID_FILE_SIZE)
311 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
312 if (!pBuffer) return NULL;
314 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
316 HeapFree(GetProcessHeap(), 0, pBuffer);
317 WARN("Error %ld reading file\n", GetLastError());
321 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
322 /* len is set to the number of bytes in the character marker.
323 * we want to skip these bytes */
324 pBuffer = (char *)pBuffer + len;
329 TRACE("ANSI encoding\n");
331 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
332 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
335 HeapFree(GetProcessHeap(), 0, pBuffer);
338 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
339 szEnd = szFile + len;
342 TRACE("UTF8 encoding\n");
344 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
345 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
348 HeapFree(GetProcessHeap(), 0, pBuffer);
351 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
352 szEnd = szFile + len;
354 case ENCODING_UTF16LE:
355 TRACE("UTF16 Little Endian encoding\n");
356 szFile = (WCHAR *)pBuffer + 1;
357 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
359 case ENCODING_UTF16BE:
360 TRACE("UTF16 Big Endian encoding\n");
361 szFile = (WCHAR *)pBuffer + 1;
362 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
363 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
366 FIXME("encoding type %d not implemented\n", *pEncoding);
367 HeapFree(GetProcessHeap(), 0, pBuffer);
371 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
372 if(first_section == NULL)
374 if (szFile != pBuffer)
375 HeapFree(GetProcessHeap(), 0, szFile);
376 HeapFree(GetProcessHeap(), 0, pBuffer);
379 first_section->name[0] = 0;
380 first_section->key = NULL;
381 first_section->next = NULL;
382 next_section = &first_section->next;
383 next_key = &first_section->key;
385 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
389 szLineStart = szLineEnd + 1;
390 if (szLineStart >= szEnd)
392 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
397 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
399 if (szLineStart >= szLineEnd) continue;
401 if (*szLineStart == '[') /* section start */
403 const WCHAR * szSectionEnd;
404 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
406 WARN("Invalid section header at line %d: %s\n",
407 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
412 len = (int)(szSectionEnd - szLineStart);
413 /* no need to allocate +1 for NULL terminating character as
414 * already included in structure */
415 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
417 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
418 section->name[len] = '\0';
420 section->next = NULL;
421 *next_section = section;
422 next_section = §ion->next;
423 next_key = §ion->key;
426 TRACE("New section: %s\n", debugstr_w(section->name));
432 /* get rid of white space at the end of the line */
433 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
435 /* line end should be pointing to character *after* the last wanted character */
438 /* get rid of white space after the name and before the start
440 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
442 szNameEnd = szValueStart - 1;
443 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
445 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
448 szNameEnd = szLineEnd - 1;
449 /* name end should be pointing to character *after* the last wanted character */
452 len = (int)(szNameEnd - szLineStart);
454 if (len || !prev_key || *prev_key->name)
456 /* no need to allocate +1 for NULL terminating character as
457 * already included in structure */
458 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
459 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
460 key->name[len] = '\0';
461 if (szValueStart && szValueStart < szLineEnd)
463 len = (int)(szLineEnd - szValueStart);
464 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
465 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
466 key->value[len] = '\0';
468 else key->value = NULL;
472 next_key = &key->next;
475 TRACE("New key: name=%s, value=%s\n",
476 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
479 if (szFile != pBuffer)
480 HeapFree(GetProcessHeap(), 0, szFile);
481 HeapFree(GetProcessHeap(), 0, pBuffer);
482 return first_section;
486 /***********************************************************************
487 * PROFILE_DeleteSection
489 * Delete a section from a profile tree.
491 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
495 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
497 PROFILESECTION *to_del = *section;
498 *section = to_del->next;
500 PROFILE_Free( to_del );
503 section = &(*section)->next;
509 /***********************************************************************
512 * Delete a key from a profile tree.
514 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
515 LPCWSTR section_name, LPCWSTR key_name )
519 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
521 PROFILEKEY **key = &(*section)->key;
524 if (!strcmpiW( (*key)->name, key_name ))
526 PROFILEKEY *to_del = *key;
528 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
529 HeapFree( GetProcessHeap(), 0, to_del );
535 section = &(*section)->next;
541 /***********************************************************************
542 * PROFILE_DeleteAllKeys
544 * Delete all keys from a profile tree.
546 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
548 PROFILESECTION **section= &CurProfile->section;
551 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
553 PROFILEKEY **key = &(*section)->key;
556 PROFILEKEY *to_del = *key;
558 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
559 HeapFree( GetProcessHeap(), 0, to_del );
560 CurProfile->changed =TRUE;
563 section = &(*section)->next;
568 /***********************************************************************
571 * Find a key in a profile tree, optionally creating it.
573 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
574 LPCWSTR key_name, BOOL create, BOOL create_always )
579 while (PROFILE_isspaceW(*section_name)) section_name++;
580 p = section_name + strlenW(section_name) - 1;
581 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
582 seclen = p - section_name + 1;
584 while (PROFILE_isspaceW(*key_name)) key_name++;
585 p = key_name + strlenW(key_name) - 1;
586 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
587 keylen = p - key_name + 1;
591 if ( ((*section)->name[0])
592 && (!(strncmpiW( (*section)->name, section_name, seclen )))
593 && (((*section)->name)[seclen] == '\0') )
595 PROFILEKEY **key = &(*section)->key;
599 /* If create_always is FALSE then we check if the keyname
600 * already exists. Otherwise we add it regardless of its
601 * existence, to allow keys to be added more than once in
606 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
607 && (((*key)->name)[keylen] == '\0') )
612 if (!create) return NULL;
613 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
615 strcpyW( (*key)->name, key_name );
616 (*key)->value = NULL;
620 section = &(*section)->next;
622 if (!create) return NULL;
623 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
624 if(*section == NULL) return NULL;
625 strcpyW( (*section)->name, section_name );
626 (*section)->next = NULL;
627 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
628 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
630 HeapFree(GetProcessHeap(), 0, *section);
633 strcpyW( (*section)->key->name, key_name );
634 (*section)->key->value = NULL;
635 (*section)->key->next = NULL;
636 return (*section)->key;
640 /***********************************************************************
643 * Flush the current profile to disk if changed.
645 static BOOL PROFILE_FlushFile(void)
648 FILETIME LastWriteTime;
652 WARN("No current profile!\n");
656 if (!CurProfile->changed) return TRUE;
658 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
660 if (hFile == INVALID_HANDLE_VALUE)
662 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
666 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
667 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
668 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
669 CurProfile->LastWriteTime=LastWriteTime;
670 CloseHandle( hFile );
671 CurProfile->changed = FALSE;
676 /***********************************************************************
677 * PROFILE_ReleaseFile
679 * Flush the current profile to disk and remove it from the cache.
681 static void PROFILE_ReleaseFile(void)
684 PROFILE_Free( CurProfile->section );
685 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
686 CurProfile->changed = FALSE;
687 CurProfile->section = NULL;
688 CurProfile->filename = NULL;
689 CurProfile->encoding = ENCODING_ANSI;
690 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
694 /***********************************************************************
697 * Open a profile file, checking the cached file first.
699 static BOOL PROFILE_Open( LPCWSTR filename )
701 WCHAR windirW[MAX_PATH];
702 WCHAR buffer[MAX_PATH];
703 HANDLE hFile = INVALID_HANDLE_VALUE;
704 FILETIME LastWriteTime;
706 PROFILE *tempProfile;
708 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
710 /* First time around */
713 for(i=0;i<N_CACHED_PROFILES;i++)
715 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
716 if(MRUProfile[i] == NULL) break;
717 MRUProfile[i]->changed=FALSE;
718 MRUProfile[i]->section=NULL;
719 MRUProfile[i]->filename=NULL;
720 MRUProfile[i]->encoding=ENCODING_ANSI;
721 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
724 GetWindowsDirectoryW( windirW, MAX_PATH );
726 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
727 !strchrW(filename, '\\') && !strchrW(filename, '/'))
729 static const WCHAR wszSeparator[] = {'\\', 0};
730 strcpyW(buffer, windirW);
731 strcatW(buffer, wszSeparator);
732 strcatW(buffer, filename);
737 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
740 TRACE("path: %s\n", debugstr_w(buffer));
742 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
744 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
746 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
750 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
752 for(i=0;i<N_CACHED_PROFILES;i++)
754 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
756 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
760 tempProfile=MRUProfile[i];
762 MRUProfile[j]=MRUProfile[j-1];
763 CurProfile=tempProfile;
765 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
766 TRACE("(%s): already opened (mru=%d)\n",
767 debugstr_w(buffer), i );
769 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
770 debugstr_w(buffer), i );
776 /* Flush the old current profile */
779 /* Make the oldest profile the current one only in order to get rid of it */
780 if(i==N_CACHED_PROFILES)
782 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
783 for(i=N_CACHED_PROFILES-1;i>0;i--)
784 MRUProfile[i]=MRUProfile[i-1];
785 CurProfile=tempProfile;
787 if(CurProfile->filename) PROFILE_ReleaseFile();
789 /* OK, now that CurProfile is definitely free we assign it our new file */
790 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
791 strcpyW( CurProfile->filename, buffer );
793 if (hFile != INVALID_HANDLE_VALUE)
795 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
797 memcpy(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME));
801 /* Does not exist yet, we will create it in PROFILE_FlushFile */
802 WARN("profile file %s not found\n", debugstr_w(buffer) );
808 /***********************************************************************
811 * Returns all keys of a section.
812 * If return_values is TRUE, also include the corresponding values.
814 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
815 LPWSTR buffer, UINT len, BOOL return_values )
819 if(!buffer) return 0;
821 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
825 if (section->name[0] && !strcmpiW( section->name, section_name ))
828 for (key = section->key; key; key = key->next)
831 if (!*key->name) continue; /* Skip empty lines */
832 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
833 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
834 len -= strlenW(buffer) + 1;
835 buffer += strlenW(buffer) + 1;
838 if (return_values && key->value) {
840 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
841 len -= strlenW(buffer) + 1;
842 buffer += strlenW(buffer) + 1;
847 /*If either lpszSection or lpszKey is NULL and the supplied
848 destination buffer is too small to hold all the strings,
849 the last string is truncated and followed by two null characters.
850 In this case, the return value is equal to cchReturnBuffer
858 section = section->next;
860 buffer[0] = buffer[1] = '\0';
864 /* See GetPrivateProfileSectionNamesA for documentation */
865 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
869 PROFILESECTION *section;
871 TRACE("(%p, %d)\n", buffer, len);
882 section = CurProfile->section;
883 while ((section!=NULL)) {
884 if (section->name[0]) {
885 l = strlenW(section->name)+1;
888 strncpyW(buf, section->name, f-1);
895 strcpyW(buf, section->name);
899 section = section->next;
906 /***********************************************************************
909 * Get a profile string.
911 * Tests with GetPrivateProfileString16, W95a,
912 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
913 * section key_name def_val res buffer
914 * "set1" "1" "x" 43 [data]
915 * "set1" "1 " "x" 43 [data] (!)
916 * "set1" " 1 "' "x" 43 [data] (!)
917 * "set1" "" "x" 1 "x"
918 * "set1" "" "x " 1 "x" (!)
919 * "set1" "" " x " 3 " x" (!)
920 * "set1" NULL "x" 6 "1\02\03\0\0"
921 * "set1" "" "x" 1 "x"
922 * NULL "1" "x" 0 "" (!)
928 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
929 LPCWSTR def_val, LPWSTR buffer, UINT len )
931 PROFILEKEY *key = NULL;
932 static const WCHAR empty_strW[] = { 0 };
934 if(!buffer) return 0;
936 if (!def_val) def_val = empty_strW;
941 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
944 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
945 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
947 TRACE("(%s,%s,%s): returning %s\n",
948 debugstr_w(section), debugstr_w(key_name),
949 debugstr_w(def_val), debugstr_w(buffer) );
950 return strlenW( buffer );
952 /* no "else" here ! */
953 if (section && section[0])
955 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
956 if (!buffer[0]) /* no luck -> def_val */
958 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
959 ret = strlenW(buffer);
968 /***********************************************************************
971 * Set a profile string.
973 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
974 LPCWSTR value, BOOL create_always )
976 if (!key_name) /* Delete a whole section */
978 TRACE("(%s)\n", debugstr_w(section_name));
979 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
981 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
982 this is not an error on application's level.*/
984 else if (!value) /* Delete a key */
986 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
987 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
988 section_name, key_name );
989 return TRUE; /* same error handling as above */
991 else /* Set the key value */
993 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
994 key_name, TRUE, create_always );
995 TRACE("(%s,%s,%s):\n",
996 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
997 if (!key) return FALSE;
1000 /* strip the leading spaces. We can safely strip \n\r and
1001 * friends too, they should not happen here anyway. */
1002 while (PROFILE_isspaceW(*value)) value++;
1004 if (!strcmpW( key->value, value ))
1006 TRACE(" no change needed\n" );
1007 return TRUE; /* No change needed */
1009 TRACE(" replacing %s\n", debugstr_w(key->value) );
1010 HeapFree( GetProcessHeap(), 0, key->value );
1012 else TRACE(" creating key\n" );
1013 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1014 strcpyW( key->value, value );
1015 CurProfile->changed = TRUE;
1021 /********************* API functions **********************************/
1024 /***********************************************************************
1025 * GetProfileIntA (KERNEL32.@)
1027 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1029 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1032 /***********************************************************************
1033 * GetProfileIntW (KERNEL32.@)
1035 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1037 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1041 * if allow_section_name_copy is TRUE, allow the copying :
1042 * - of Section names if 'section' is NULL
1043 * - of Keys in a Section if 'entry' is NULL
1044 * (see MSDN doc for GetPrivateProfileString)
1046 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1047 LPCWSTR def_val, LPWSTR buffer,
1048 UINT len, LPCWSTR filename,
1049 BOOL allow_section_name_copy )
1052 LPWSTR pDefVal = NULL;
1057 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1058 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1060 /* strip any trailing ' ' of def_val. */
1063 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1071 if (*p == ' ') /* ouch, contained trailing ' ' */
1073 int len = (int)(p - def_val);
1074 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1075 strncpyW(pDefVal, def_val, len);
1076 pDefVal[len] = '\0';
1080 pDefVal = (LPWSTR)def_val;
1082 RtlEnterCriticalSection( &PROFILE_CritSect );
1084 if (PROFILE_Open( filename )) {
1085 if ((allow_section_name_copy) && (section == NULL))
1086 ret = PROFILE_GetSectionNames(buffer, len);
1088 /* PROFILE_GetString already handles the 'entry == NULL' case */
1089 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1091 lstrcpynW( buffer, pDefVal, len );
1092 ret = strlenW( buffer );
1095 RtlLeaveCriticalSection( &PROFILE_CritSect );
1097 if (pDefVal != def_val) /* allocated */
1098 HeapFree(GetProcessHeap(), 0, pDefVal);
1100 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1105 /***********************************************************************
1106 * GetPrivateProfileString (KERNEL.128)
1108 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1109 LPCSTR def_val, LPSTR buffer,
1110 UINT16 len, LPCSTR filename )
1112 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1114 INT16 retW, ret = 0;
1116 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1117 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1118 else sectionW.Buffer = NULL;
1119 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1120 else entryW.Buffer = NULL;
1121 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1122 else def_valW.Buffer = NULL;
1123 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1124 else filenameW.Buffer = NULL;
1126 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1127 def_valW.Buffer, bufferW, len,
1128 filenameW.Buffer, FALSE );
1131 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1138 ret--; /* strip terminating 0 */
1141 RtlFreeUnicodeString(§ionW);
1142 RtlFreeUnicodeString(&entryW);
1143 RtlFreeUnicodeString(&def_valW);
1144 RtlFreeUnicodeString(&filenameW);
1145 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1149 /***********************************************************************
1150 * GetPrivateProfileStringA (KERNEL32.@)
1152 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1153 LPCSTR def_val, LPSTR buffer,
1154 UINT len, LPCSTR filename )
1156 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1160 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1161 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1162 else sectionW.Buffer = NULL;
1163 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1164 else entryW.Buffer = NULL;
1165 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1166 else def_valW.Buffer = NULL;
1167 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1168 else filenameW.Buffer = NULL;
1170 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1171 def_valW.Buffer, bufferW, len,
1175 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1182 ret--; /* strip terminating 0 */
1185 RtlFreeUnicodeString(§ionW);
1186 RtlFreeUnicodeString(&entryW);
1187 RtlFreeUnicodeString(&def_valW);
1188 RtlFreeUnicodeString(&filenameW);
1189 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1193 /***********************************************************************
1194 * GetPrivateProfileStringW (KERNEL32.@)
1196 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1197 LPCWSTR def_val, LPWSTR buffer,
1198 UINT len, LPCWSTR filename )
1200 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1202 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1203 buffer, len, filename, TRUE );
1206 /***********************************************************************
1207 * GetProfileStringA (KERNEL32.@)
1209 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1210 LPSTR buffer, UINT len )
1212 return GetPrivateProfileStringA( section, entry, def_val,
1213 buffer, len, "win.ini" );
1216 /***********************************************************************
1217 * GetProfileStringW (KERNEL32.@)
1219 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1220 LPCWSTR def_val, LPWSTR buffer, UINT len )
1222 return GetPrivateProfileStringW( section, entry, def_val,
1223 buffer, len, wininiW );
1226 /***********************************************************************
1227 * WriteProfileStringA (KERNEL32.@)
1229 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1232 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1235 /***********************************************************************
1236 * WriteProfileStringW (KERNEL32.@)
1238 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1241 return WritePrivateProfileStringW( section, entry, string, wininiW );
1245 /***********************************************************************
1246 * GetPrivateProfileIntW (KERNEL32.@)
1248 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1249 INT def_val, LPCWSTR filename )
1252 UNICODE_STRING bufferW;
1256 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1257 buffer, sizeof(buffer)/sizeof(WCHAR),
1261 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1263 /* FIXME: if entry can be found but it's empty, then Win16 is
1264 * supposed to return 0 instead of def_val ! Difficult/problematic
1265 * to implement (every other failure also returns zero buffer),
1266 * thus wait until testing framework avail for making sure nothing
1267 * else gets broken that way. */
1268 if (!buffer[0]) return (UINT)def_val;
1270 RtlInitUnicodeString( &bufferW, buffer );
1271 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1275 /***********************************************************************
1276 * GetPrivateProfileIntA (KERNEL32.@)
1278 * FIXME: rewrite using unicode
1280 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1281 INT def_val, LPCSTR filename )
1283 UNICODE_STRING entryW, filenameW, sectionW;
1285 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1286 else entryW.Buffer = NULL;
1287 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1288 else filenameW.Buffer = NULL;
1289 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1290 else sectionW.Buffer = NULL;
1291 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1293 RtlFreeUnicodeString(§ionW);
1294 RtlFreeUnicodeString(&filenameW);
1295 RtlFreeUnicodeString(&entryW);
1299 /***********************************************************************
1300 * GetPrivateProfileSectionW (KERNEL32.@)
1302 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1303 DWORD len, LPCWSTR filename )
1307 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1309 RtlEnterCriticalSection( &PROFILE_CritSect );
1311 if (PROFILE_Open( filename ))
1312 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1314 RtlLeaveCriticalSection( &PROFILE_CritSect );
1319 /***********************************************************************
1320 * GetPrivateProfileSectionA (KERNEL32.@)
1322 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1323 DWORD len, LPCSTR filename )
1325 UNICODE_STRING sectionW, filenameW;
1329 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1330 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1331 else sectionW.Buffer = NULL;
1332 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1333 else filenameW.Buffer = NULL;
1335 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1338 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1354 RtlFreeUnicodeString(§ionW);
1355 RtlFreeUnicodeString(&filenameW);
1356 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1360 /***********************************************************************
1361 * GetProfileSectionA (KERNEL32.@)
1363 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1365 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1368 /***********************************************************************
1369 * GetProfileSectionW (KERNEL32.@)
1371 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1373 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1377 /***********************************************************************
1378 * WritePrivateProfileStringW (KERNEL32.@)
1380 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1381 LPCWSTR string, LPCWSTR filename )
1385 RtlEnterCriticalSection( &PROFILE_CritSect );
1387 if (PROFILE_Open( filename ))
1389 if (!section && !entry && !string) /* documented "file flush" case */
1391 PROFILE_FlushFile();
1392 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1396 FIXME("(NULL?,%s,%s,%s)?\n",
1397 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1399 ret = PROFILE_SetString( section, entry, string, FALSE);
1400 PROFILE_FlushFile();
1405 RtlLeaveCriticalSection( &PROFILE_CritSect );
1409 /***********************************************************************
1410 * WritePrivateProfileStringA (KERNEL32.@)
1412 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1413 LPCSTR string, LPCSTR filename )
1415 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1418 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1419 else sectionW.Buffer = NULL;
1420 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1421 else entryW.Buffer = NULL;
1422 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1423 else stringW.Buffer = NULL;
1424 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1425 else filenameW.Buffer = NULL;
1427 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1428 stringW.Buffer, filenameW.Buffer);
1429 RtlFreeUnicodeString(§ionW);
1430 RtlFreeUnicodeString(&entryW);
1431 RtlFreeUnicodeString(&stringW);
1432 RtlFreeUnicodeString(&filenameW);
1436 /***********************************************************************
1437 * WritePrivateProfileSectionW (KERNEL32.@)
1439 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1440 LPCWSTR string, LPCWSTR filename )
1445 RtlEnterCriticalSection( &PROFILE_CritSect );
1447 if (PROFILE_Open( filename )) {
1448 if (!section && !string)
1449 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1450 else if (!string) {/* delete the named section*/
1451 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1452 PROFILE_FlushFile();
1454 PROFILE_DeleteAllKeys(section);
1457 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1458 strcpyW( buf, string );
1459 if((p = strchrW( buf, '='))) {
1461 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1463 HeapFree( GetProcessHeap(), 0, buf );
1464 string += strlenW(string)+1;
1466 PROFILE_FlushFile();
1470 RtlLeaveCriticalSection( &PROFILE_CritSect );
1474 /***********************************************************************
1475 * WritePrivateProfileSectionA (KERNEL32.@)
1477 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1478 LPCSTR string, LPCSTR filename)
1481 UNICODE_STRING sectionW, filenameW;
1490 while(*p) p += strlen(p) + 1;
1491 lenA = p - string + 1;
1492 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1493 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1494 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1496 else stringW = NULL;
1497 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1498 else sectionW.Buffer = NULL;
1499 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1500 else filenameW.Buffer = NULL;
1502 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1504 HeapFree(GetProcessHeap(), 0, stringW);
1505 RtlFreeUnicodeString(§ionW);
1506 RtlFreeUnicodeString(&filenameW);
1510 /***********************************************************************
1511 * WriteProfileSectionA (KERNEL32.@)
1513 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1516 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1519 /***********************************************************************
1520 * WriteProfileSectionW (KERNEL32.@)
1522 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1524 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1528 /***********************************************************************
1529 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1531 * Returns the section names contained in the specified file.
1532 * FIXME: Where do we find this file when the path is relative?
1533 * The section names are returned as a list of strings with an extra
1534 * '\0' to mark the end of the list. Except for that the behavior
1535 * depends on the Windows version.
1538 * - if the buffer is 0 or 1 character long then it is as if it was of
1540 * - otherwise, if the buffer is to small only the section names that fit
1542 * - note that this means if the buffer was to small to return even just
1543 * the first section name then a single '\0' will be returned.
1544 * - the return value is the number of characters written in the buffer,
1545 * except if the buffer was too smal in which case len-2 is returned
1548 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1549 * '\0' and the return value is 0
1550 * - otherwise if the buffer is too small then the first section name that
1551 * does not fit is truncated so that the string list can be terminated
1552 * correctly (double '\0')
1553 * - the return value is the number of characters written in the buffer
1554 * except for the trailing '\0'. If the buffer is too small, then the
1555 * return value is len-2
1556 * - Win2000 has a bug that triggers when the section names and the
1557 * trailing '\0' fit exactly in the buffer. In that case the trailing
1560 * Wine implements the observed Win2000 behavior (except for the bug).
1562 * Note that when the buffer is big enough then the return value may be any
1563 * value between 1 and len-1 (or len in Win95), including len-2.
1565 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1570 RtlEnterCriticalSection( &PROFILE_CritSect );
1572 if (PROFILE_Open( filename ))
1573 ret = PROFILE_GetSectionNames(buffer, size);
1575 RtlLeaveCriticalSection( &PROFILE_CritSect );
1581 /***********************************************************************
1582 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1584 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1587 UNICODE_STRING filenameW;
1591 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1592 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1593 else filenameW.Buffer = NULL;
1595 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1598 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1606 RtlFreeUnicodeString(&filenameW);
1607 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1611 /***********************************************************************
1612 * GetPrivateProfileStructW (KERNEL32.@)
1614 * Should match Win95's behaviour pretty much
1616 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1617 LPVOID buf, UINT len, LPCWSTR filename)
1621 RtlEnterCriticalSection( &PROFILE_CritSect );
1623 if (PROFILE_Open( filename )) {
1624 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1626 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1627 if (((strlenW(k->value) - 2) / 2) == len)
1634 end = k->value + strlenW(k->value); /* -> '\0' */
1635 /* check for invalid chars in ASCII coded hex string */
1636 for (p=k->value; p < end; p++)
1640 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1641 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1648 BOOL highnibble = TRUE;
1650 LPBYTE binbuf = (LPBYTE)buf;
1652 end -= 2; /* don't include checksum in output data */
1653 /* translate ASCII hex format into binary data */
1654 for (p=k->value; p < end; p++)
1658 (c - 'A' + 10) : (c - '0');
1665 *binbuf++ = b; /* feed binary data into output */
1666 chksum += b; /* calculate checksum */
1668 highnibble ^= 1; /* toggle */
1670 /* retrieve stored checksum value */
1672 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1674 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1675 if (b == (chksum & 0xff)) /* checksums match ? */
1681 RtlLeaveCriticalSection( &PROFILE_CritSect );
1686 /***********************************************************************
1687 * GetPrivateProfileStructA (KERNEL32.@)
1689 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1690 LPVOID buffer, UINT len, LPCSTR filename)
1692 UNICODE_STRING sectionW, keyW, filenameW;
1695 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1696 else sectionW.Buffer = NULL;
1697 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1698 else keyW.Buffer = NULL;
1699 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1700 else filenameW.Buffer = NULL;
1702 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1704 /* Do not translate binary data. */
1706 RtlFreeUnicodeString(§ionW);
1707 RtlFreeUnicodeString(&keyW);
1708 RtlFreeUnicodeString(&filenameW);
1714 /***********************************************************************
1715 * WritePrivateProfileStructW (KERNEL32.@)
1717 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1718 LPVOID buf, UINT bufsize, LPCWSTR filename)
1722 LPWSTR outstring, p;
1725 if (!section && !key && !buf) /* flush the cache */
1726 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1728 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1729 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1731 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1732 *p++ = hex[*binbuf >> 4];
1733 *p++ = hex[*binbuf & 0xf];
1736 /* checksum is sum & 0xff */
1737 *p++ = hex[(sum & 0xf0) >> 4];
1738 *p++ = hex[sum & 0xf];
1741 RtlEnterCriticalSection( &PROFILE_CritSect );
1743 if (PROFILE_Open( filename )) {
1744 ret = PROFILE_SetString( section, key, outstring, FALSE);
1745 PROFILE_FlushFile();
1748 RtlLeaveCriticalSection( &PROFILE_CritSect );
1750 HeapFree( GetProcessHeap(), 0, outstring );
1755 /***********************************************************************
1756 * WritePrivateProfileStructA (KERNEL32.@)
1758 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1759 LPVOID buf, UINT bufsize, LPCSTR filename)
1761 UNICODE_STRING sectionW, keyW, filenameW;
1764 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1765 else sectionW.Buffer = NULL;
1766 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1767 else keyW.Buffer = NULL;
1768 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1769 else filenameW.Buffer = NULL;
1771 /* Do not translate binary data. */
1772 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1775 RtlFreeUnicodeString(§ionW);
1776 RtlFreeUnicodeString(&keyW);
1777 RtlFreeUnicodeString(&filenameW);
1782 /***********************************************************************
1783 * WriteOutProfiles (KERNEL.315)
1785 void WINAPI WriteOutProfiles16(void)
1787 RtlEnterCriticalSection( &PROFILE_CritSect );
1788 PROFILE_FlushFile();
1789 RtlLeaveCriticalSection( &PROFILE_CritSect );
1792 /***********************************************************************
1793 * CloseProfileUserMapping (KERNEL32.@)
1795 BOOL WINAPI CloseProfileUserMapping(void) {
1796 FIXME("(), stub!\n");
1797 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);