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';
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 for(i=0;i<N_CACHED_PROFILES;i++)
752 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
754 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
758 tempProfile=MRUProfile[i];
760 MRUProfile[j]=MRUProfile[j-1];
761 CurProfile=tempProfile;
763 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
764 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
765 TRACE("(%s): already opened (mru=%d)\n",
766 debugstr_w(buffer), i );
768 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
769 debugstr_w(buffer), i );
775 /* Flush the old current profile */
778 /* Make the oldest profile the current one only in order to get rid of it */
779 if(i==N_CACHED_PROFILES)
781 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
782 for(i=N_CACHED_PROFILES-1;i>0;i--)
783 MRUProfile[i]=MRUProfile[i-1];
784 CurProfile=tempProfile;
786 if(CurProfile->filename) PROFILE_ReleaseFile();
788 /* OK, now that CurProfile is definitely free we assign it our new file */
789 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
790 strcpyW( CurProfile->filename, buffer );
792 if (hFile != INVALID_HANDLE_VALUE)
794 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
795 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
800 /* Does not exist yet, we will create it in PROFILE_FlushFile */
801 WARN("profile file %s not found\n", debugstr_w(buffer) );
807 /***********************************************************************
810 * Returns all keys of a section.
811 * If return_values is TRUE, also include the corresponding values.
813 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
814 LPWSTR buffer, UINT len, BOOL return_values )
818 if(!buffer) return 0;
820 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
824 if (section->name[0] && !strcmpiW( section->name, section_name ))
827 for (key = section->key; key; key = key->next)
830 if (!*key->name) continue; /* Skip empty lines */
831 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
832 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
833 len -= strlenW(buffer) + 1;
834 buffer += strlenW(buffer) + 1;
837 if (return_values && key->value) {
839 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
840 len -= strlenW(buffer) + 1;
841 buffer += strlenW(buffer) + 1;
846 /*If either lpszSection or lpszKey is NULL and the supplied
847 destination buffer is too small to hold all the strings,
848 the last string is truncated and followed by two null characters.
849 In this case, the return value is equal to cchReturnBuffer
857 section = section->next;
859 buffer[0] = buffer[1] = '\0';
863 /* See GetPrivateProfileSectionNamesA for documentation */
864 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
868 PROFILESECTION *section;
870 TRACE("(%p, %d)\n", buffer, len);
881 section = CurProfile->section;
882 while ((section!=NULL)) {
883 if (section->name[0]) {
884 l = strlenW(section->name)+1;
887 strncpyW(buf, section->name, f-1);
894 strcpyW(buf, section->name);
898 section = section->next;
905 /***********************************************************************
908 * Get a profile string.
910 * Tests with GetPrivateProfileString16, W95a,
911 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
912 * section key_name def_val res buffer
913 * "set1" "1" "x" 43 [data]
914 * "set1" "1 " "x" 43 [data] (!)
915 * "set1" " 1 "' "x" 43 [data] (!)
916 * "set1" "" "x" 1 "x"
917 * "set1" "" "x " 1 "x" (!)
918 * "set1" "" " x " 3 " x" (!)
919 * "set1" NULL "x" 6 "1\02\03\0\0"
920 * "set1" "" "x" 1 "x"
921 * NULL "1" "x" 0 "" (!)
927 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
928 LPCWSTR def_val, LPWSTR buffer, UINT len )
930 PROFILEKEY *key = NULL;
931 static const WCHAR empty_strW[] = { 0 };
933 if(!buffer) return 0;
935 if (!def_val) def_val = empty_strW;
940 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
943 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
944 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
946 TRACE("(%s,%s,%s): returning %s\n",
947 debugstr_w(section), debugstr_w(key_name),
948 debugstr_w(def_val), debugstr_w(buffer) );
949 return strlenW( buffer );
951 /* no "else" here ! */
952 if (section && section[0])
954 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
955 if (!buffer[0]) /* no luck -> def_val */
957 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
958 ret = strlenW(buffer);
967 /***********************************************************************
970 * Set a profile string.
972 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
973 LPCWSTR value, BOOL create_always )
975 if (!key_name) /* Delete a whole section */
977 TRACE("(%s)\n", debugstr_w(section_name));
978 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
980 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
981 this is not an error on application's level.*/
983 else if (!value) /* Delete a key */
985 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
986 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
987 section_name, key_name );
988 return TRUE; /* same error handling as above */
990 else /* Set the key value */
992 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
993 key_name, TRUE, create_always );
994 TRACE("(%s,%s,%s):\n",
995 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
996 if (!key) return FALSE;
999 /* strip the leading spaces. We can safely strip \n\r and
1000 * friends too, they should not happen here anyway. */
1001 while (PROFILE_isspaceW(*value)) value++;
1003 if (!strcmpW( key->value, value ))
1005 TRACE(" no change needed\n" );
1006 return TRUE; /* No change needed */
1008 TRACE(" replacing %s\n", debugstr_w(key->value) );
1009 HeapFree( GetProcessHeap(), 0, key->value );
1011 else TRACE(" creating key\n" );
1012 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1013 strcpyW( key->value, value );
1014 CurProfile->changed = TRUE;
1020 /********************* API functions **********************************/
1023 /***********************************************************************
1024 * GetProfileIntA (KERNEL32.@)
1026 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1028 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1031 /***********************************************************************
1032 * GetProfileIntW (KERNEL32.@)
1034 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1036 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1040 * if allow_section_name_copy is TRUE, allow the copying :
1041 * - of Section names if 'section' is NULL
1042 * - of Keys in a Section if 'entry' is NULL
1043 * (see MSDN doc for GetPrivateProfileString)
1045 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1046 LPCWSTR def_val, LPWSTR buffer,
1047 UINT len, LPCWSTR filename,
1048 BOOL allow_section_name_copy )
1051 LPWSTR pDefVal = NULL;
1056 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1057 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1059 /* strip any trailing ' ' of def_val. */
1062 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1070 if (*p == ' ') /* ouch, contained trailing ' ' */
1072 int len = (int)(p - def_val);
1073 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1074 strncpyW(pDefVal, def_val, len);
1075 pDefVal[len] = '\0';
1079 pDefVal = (LPWSTR)def_val;
1081 RtlEnterCriticalSection( &PROFILE_CritSect );
1083 if (PROFILE_Open( filename )) {
1084 if ((allow_section_name_copy) && (section == NULL))
1085 ret = PROFILE_GetSectionNames(buffer, len);
1087 /* PROFILE_GetString already handles the 'entry == NULL' case */
1088 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1090 lstrcpynW( buffer, pDefVal, len );
1091 ret = strlenW( buffer );
1094 RtlLeaveCriticalSection( &PROFILE_CritSect );
1096 if (pDefVal != def_val) /* allocated */
1097 HeapFree(GetProcessHeap(), 0, pDefVal);
1099 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1104 /***********************************************************************
1105 * GetPrivateProfileString (KERNEL.128)
1107 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1108 LPCSTR def_val, LPSTR buffer,
1109 UINT16 len, LPCSTR filename )
1111 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1113 INT16 retW, ret = 0;
1115 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1116 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1117 else sectionW.Buffer = NULL;
1118 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1119 else entryW.Buffer = NULL;
1120 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1121 else def_valW.Buffer = NULL;
1122 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1123 else filenameW.Buffer = NULL;
1125 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1126 def_valW.Buffer, bufferW, len,
1127 filenameW.Buffer, FALSE );
1130 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1137 ret--; /* strip terminating 0 */
1140 RtlFreeUnicodeString(§ionW);
1141 RtlFreeUnicodeString(&entryW);
1142 RtlFreeUnicodeString(&def_valW);
1143 RtlFreeUnicodeString(&filenameW);
1144 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1148 /***********************************************************************
1149 * GetPrivateProfileStringA (KERNEL32.@)
1151 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1152 LPCSTR def_val, LPSTR buffer,
1153 UINT len, LPCSTR filename )
1155 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1159 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1160 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1161 else sectionW.Buffer = NULL;
1162 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1163 else entryW.Buffer = NULL;
1164 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1165 else def_valW.Buffer = NULL;
1166 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1167 else filenameW.Buffer = NULL;
1169 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1170 def_valW.Buffer, bufferW, len,
1174 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1181 ret--; /* strip terminating 0 */
1184 RtlFreeUnicodeString(§ionW);
1185 RtlFreeUnicodeString(&entryW);
1186 RtlFreeUnicodeString(&def_valW);
1187 RtlFreeUnicodeString(&filenameW);
1188 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1192 /***********************************************************************
1193 * GetPrivateProfileStringW (KERNEL32.@)
1195 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1196 LPCWSTR def_val, LPWSTR buffer,
1197 UINT len, LPCWSTR filename )
1199 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1201 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1202 buffer, len, filename, TRUE );
1205 /***********************************************************************
1206 * GetProfileStringA (KERNEL32.@)
1208 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1209 LPSTR buffer, UINT len )
1211 return GetPrivateProfileStringA( section, entry, def_val,
1212 buffer, len, "win.ini" );
1215 /***********************************************************************
1216 * GetProfileStringW (KERNEL32.@)
1218 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1219 LPCWSTR def_val, LPWSTR buffer, UINT len )
1221 return GetPrivateProfileStringW( section, entry, def_val,
1222 buffer, len, wininiW );
1225 /***********************************************************************
1226 * WriteProfileStringA (KERNEL32.@)
1228 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1231 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1234 /***********************************************************************
1235 * WriteProfileStringW (KERNEL32.@)
1237 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1240 return WritePrivateProfileStringW( section, entry, string, wininiW );
1244 /***********************************************************************
1245 * GetPrivateProfileIntW (KERNEL32.@)
1247 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1248 INT def_val, LPCWSTR filename )
1251 UNICODE_STRING bufferW;
1255 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1256 buffer, sizeof(buffer)/sizeof(WCHAR),
1260 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1262 /* FIXME: if entry can be found but it's empty, then Win16 is
1263 * supposed to return 0 instead of def_val ! Difficult/problematic
1264 * to implement (every other failure also returns zero buffer),
1265 * thus wait until testing framework avail for making sure nothing
1266 * else gets broken that way. */
1267 if (!buffer[0]) return (UINT)def_val;
1269 RtlInitUnicodeString( &bufferW, buffer );
1270 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1274 /***********************************************************************
1275 * GetPrivateProfileIntA (KERNEL32.@)
1277 * FIXME: rewrite using unicode
1279 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1280 INT def_val, LPCSTR filename )
1282 UNICODE_STRING entryW, filenameW, sectionW;
1284 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1285 else entryW.Buffer = NULL;
1286 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1287 else filenameW.Buffer = NULL;
1288 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1289 else sectionW.Buffer = NULL;
1290 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1292 RtlFreeUnicodeString(§ionW);
1293 RtlFreeUnicodeString(&filenameW);
1294 RtlFreeUnicodeString(&entryW);
1298 /***********************************************************************
1299 * GetPrivateProfileSectionW (KERNEL32.@)
1301 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1302 DWORD len, LPCWSTR filename )
1306 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1308 RtlEnterCriticalSection( &PROFILE_CritSect );
1310 if (PROFILE_Open( filename ))
1311 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1313 RtlLeaveCriticalSection( &PROFILE_CritSect );
1318 /***********************************************************************
1319 * GetPrivateProfileSectionA (KERNEL32.@)
1321 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1322 DWORD len, LPCSTR filename )
1324 UNICODE_STRING sectionW, filenameW;
1328 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1329 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1330 else sectionW.Buffer = NULL;
1331 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1332 else filenameW.Buffer = NULL;
1334 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1337 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1353 RtlFreeUnicodeString(§ionW);
1354 RtlFreeUnicodeString(&filenameW);
1355 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1359 /***********************************************************************
1360 * GetProfileSectionA (KERNEL32.@)
1362 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1364 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1367 /***********************************************************************
1368 * GetProfileSectionW (KERNEL32.@)
1370 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1372 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1376 /***********************************************************************
1377 * WritePrivateProfileStringW (KERNEL32.@)
1379 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1380 LPCWSTR string, LPCWSTR filename )
1384 RtlEnterCriticalSection( &PROFILE_CritSect );
1386 if (PROFILE_Open( filename ))
1388 if (!section && !entry && !string) /* documented "file flush" case */
1390 PROFILE_FlushFile();
1391 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1395 FIXME("(NULL?,%s,%s,%s)?\n",
1396 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1398 ret = PROFILE_SetString( section, entry, string, FALSE);
1399 PROFILE_FlushFile();
1404 RtlLeaveCriticalSection( &PROFILE_CritSect );
1408 /***********************************************************************
1409 * WritePrivateProfileStringA (KERNEL32.@)
1411 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1412 LPCSTR string, LPCSTR filename )
1414 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1417 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1418 else sectionW.Buffer = NULL;
1419 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1420 else entryW.Buffer = NULL;
1421 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1422 else stringW.Buffer = NULL;
1423 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1424 else filenameW.Buffer = NULL;
1426 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1427 stringW.Buffer, filenameW.Buffer);
1428 RtlFreeUnicodeString(§ionW);
1429 RtlFreeUnicodeString(&entryW);
1430 RtlFreeUnicodeString(&stringW);
1431 RtlFreeUnicodeString(&filenameW);
1435 /***********************************************************************
1436 * WritePrivateProfileSectionW (KERNEL32.@)
1438 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1439 LPCWSTR string, LPCWSTR filename )
1444 RtlEnterCriticalSection( &PROFILE_CritSect );
1446 if (PROFILE_Open( filename )) {
1447 if (!section && !string)
1448 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1449 else if (!string) {/* delete the named section*/
1450 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1451 PROFILE_FlushFile();
1453 PROFILE_DeleteAllKeys(section);
1456 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1457 strcpyW( buf, string );
1458 if((p = strchrW( buf, '='))) {
1460 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1462 HeapFree( GetProcessHeap(), 0, buf );
1463 string += strlenW(string)+1;
1465 PROFILE_FlushFile();
1469 RtlLeaveCriticalSection( &PROFILE_CritSect );
1473 /***********************************************************************
1474 * WritePrivateProfileSectionA (KERNEL32.@)
1476 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1477 LPCSTR string, LPCSTR filename)
1480 UNICODE_STRING sectionW, filenameW;
1489 while(*p) p += strlen(p) + 1;
1490 lenA = p - string + 1;
1491 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1492 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1493 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1495 else stringW = NULL;
1496 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1497 else sectionW.Buffer = NULL;
1498 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1499 else filenameW.Buffer = NULL;
1501 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1503 HeapFree(GetProcessHeap(), 0, stringW);
1504 RtlFreeUnicodeString(§ionW);
1505 RtlFreeUnicodeString(&filenameW);
1509 /***********************************************************************
1510 * WriteProfileSectionA (KERNEL32.@)
1512 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1515 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1518 /***********************************************************************
1519 * WriteProfileSectionW (KERNEL32.@)
1521 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1523 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1527 /***********************************************************************
1528 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1530 * Returns the section names contained in the specified file.
1531 * FIXME: Where do we find this file when the path is relative?
1532 * The section names are returned as a list of strings with an extra
1533 * '\0' to mark the end of the list. Except for that the behavior
1534 * depends on the Windows version.
1537 * - if the buffer is 0 or 1 character long then it is as if it was of
1539 * - otherwise, if the buffer is to small only the section names that fit
1541 * - note that this means if the buffer was to small to return even just
1542 * the first section name then a single '\0' will be returned.
1543 * - the return value is the number of characters written in the buffer,
1544 * except if the buffer was too smal in which case len-2 is returned
1547 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1548 * '\0' and the return value is 0
1549 * - otherwise if the buffer is too small then the first section name that
1550 * does not fit is truncated so that the string list can be terminated
1551 * correctly (double '\0')
1552 * - the return value is the number of characters written in the buffer
1553 * except for the trailing '\0'. If the buffer is too small, then the
1554 * return value is len-2
1555 * - Win2000 has a bug that triggers when the section names and the
1556 * trailing '\0' fit exactly in the buffer. In that case the trailing
1559 * Wine implements the observed Win2000 behavior (except for the bug).
1561 * Note that when the buffer is big enough then the return value may be any
1562 * value between 1 and len-1 (or len in Win95), including len-2.
1564 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1569 RtlEnterCriticalSection( &PROFILE_CritSect );
1571 if (PROFILE_Open( filename ))
1572 ret = PROFILE_GetSectionNames(buffer, size);
1574 RtlLeaveCriticalSection( &PROFILE_CritSect );
1580 /***********************************************************************
1581 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1583 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1586 UNICODE_STRING filenameW;
1590 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1591 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1592 else filenameW.Buffer = NULL;
1594 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1597 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1605 RtlFreeUnicodeString(&filenameW);
1606 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1610 /***********************************************************************
1611 * GetPrivateProfileStructW (KERNEL32.@)
1613 * Should match Win95's behaviour pretty much
1615 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1616 LPVOID buf, UINT len, LPCWSTR filename)
1620 RtlEnterCriticalSection( &PROFILE_CritSect );
1622 if (PROFILE_Open( filename )) {
1623 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1625 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1626 if (((strlenW(k->value) - 2) / 2) == len)
1633 end = k->value + strlenW(k->value); /* -> '\0' */
1634 /* check for invalid chars in ASCII coded hex string */
1635 for (p=k->value; p < end; p++)
1639 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1640 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1647 BOOL highnibble = TRUE;
1649 LPBYTE binbuf = (LPBYTE)buf;
1651 end -= 2; /* don't include checksum in output data */
1652 /* translate ASCII hex format into binary data */
1653 for (p=k->value; p < end; p++)
1657 (c - 'A' + 10) : (c - '0');
1664 *binbuf++ = b; /* feed binary data into output */
1665 chksum += b; /* calculate checksum */
1667 highnibble ^= 1; /* toggle */
1669 /* retrieve stored checksum value */
1671 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1673 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1674 if (b == (chksum & 0xff)) /* checksums match ? */
1680 RtlLeaveCriticalSection( &PROFILE_CritSect );
1685 /***********************************************************************
1686 * GetPrivateProfileStructA (KERNEL32.@)
1688 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1689 LPVOID buffer, UINT len, LPCSTR filename)
1691 UNICODE_STRING sectionW, keyW, filenameW;
1694 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1695 else sectionW.Buffer = NULL;
1696 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1697 else keyW.Buffer = NULL;
1698 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1699 else filenameW.Buffer = NULL;
1701 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1703 /* Do not translate binary data. */
1705 RtlFreeUnicodeString(§ionW);
1706 RtlFreeUnicodeString(&keyW);
1707 RtlFreeUnicodeString(&filenameW);
1713 /***********************************************************************
1714 * WritePrivateProfileStructW (KERNEL32.@)
1716 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1717 LPVOID buf, UINT bufsize, LPCWSTR filename)
1721 LPWSTR outstring, p;
1724 if (!section && !key && !buf) /* flush the cache */
1725 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1727 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1728 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1730 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1731 *p++ = hex[*binbuf >> 4];
1732 *p++ = hex[*binbuf & 0xf];
1735 /* checksum is sum & 0xff */
1736 *p++ = hex[(sum & 0xf0) >> 4];
1737 *p++ = hex[sum & 0xf];
1740 RtlEnterCriticalSection( &PROFILE_CritSect );
1742 if (PROFILE_Open( filename )) {
1743 ret = PROFILE_SetString( section, key, outstring, FALSE);
1744 PROFILE_FlushFile();
1747 RtlLeaveCriticalSection( &PROFILE_CritSect );
1749 HeapFree( GetProcessHeap(), 0, outstring );
1754 /***********************************************************************
1755 * WritePrivateProfileStructA (KERNEL32.@)
1757 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1758 LPVOID buf, UINT bufsize, LPCSTR filename)
1760 UNICODE_STRING sectionW, keyW, filenameW;
1763 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1764 else sectionW.Buffer = NULL;
1765 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1766 else keyW.Buffer = NULL;
1767 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1768 else filenameW.Buffer = NULL;
1770 /* Do not translate binary data. */
1771 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1774 RtlFreeUnicodeString(§ionW);
1775 RtlFreeUnicodeString(&keyW);
1776 RtlFreeUnicodeString(&filenameW);
1781 /***********************************************************************
1782 * WriteOutProfiles (KERNEL.315)
1784 void WINAPI WriteOutProfiles16(void)
1786 RtlEnterCriticalSection( &PROFILE_CritSect );
1787 PROFILE_FlushFile();
1788 RtlLeaveCriticalSection( &PROFILE_CritSect );
1791 /***********************************************************************
1792 * CloseProfileUserMapping (KERNEL32.@)
1794 BOOL WINAPI CloseProfileUserMapping(void) {
1795 FIXME("(), stub!\n");
1796 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);