4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
34 #include "wine/winbase16.h"
35 #include "wine/unicode.h"
36 #include "wine/server.h"
37 #include "wine/library.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(profile);
42 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
52 typedef struct tagPROFILEKEY
55 struct tagPROFILEKEY *next;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
70 PROFILESECTION *section;
72 FILETIME LastWriteTime;
77 #define N_CACHED_PROFILES 10
79 /* Cached profile files */
80 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
82 #define CurProfile (MRUProfile[0])
84 #define PROFILE_MAX_LINE_LEN 1024
86 /* Check for comments in profile */
87 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
89 static const WCHAR emptystringW[] = {0};
90 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
92 static CRITICAL_SECTION PROFILE_CritSect;
93 static CRITICAL_SECTION_DEBUG critsect_debug =
95 0, 0, &PROFILE_CritSect,
96 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
97 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
99 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
101 static const char hex[16] = "0123456789ABCDEF";
103 /***********************************************************************
106 * Copy the content of an entry into a buffer, removing quotes, and possibly
107 * translating environment variables.
109 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
116 if (strip_quote && ((*value == '\'') || (*value == '\"')))
118 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
121 lstrcpynW( buffer, value, len );
122 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
125 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
126 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
129 USHORT * shortbuffer = (USHORT *)buffer;
130 for (i = 0; i < len; i++)
131 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
134 /* writes any necessary encoding marker to the file */
135 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
137 DWORD dwBytesWritten;
144 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
146 case ENCODING_UTF16LE:
148 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
150 case ENCODING_UTF16BE:
152 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
157 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
159 char write_buffer[PROFILE_MAX_LINE_LEN];
160 DWORD dwBytesWritten;
162 TRACE("writing: %s\n", debugstr_wn(szLine, len));
167 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
168 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
171 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
172 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
174 case ENCODING_UTF16LE:
175 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
177 case ENCODING_UTF16BE:
178 PROFILE_ByteSwapShortBuffer(szLine, len);
179 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
182 FIXME("encoding type %d not implemented\n", encoding);
186 /***********************************************************************
189 * Save a profile tree to a file.
191 static void PROFILE_Save( HANDLE hFile, PROFILESECTION *section, ENCODING encoding )
193 static const WCHAR wSectionFormat[] = {'\r','\n','[','%','s',']','\r','\n',0};
194 static const WCHAR wNameFormat[] = {'%','s',0};
195 static const WCHAR wValueFormat[] = {'=','%','s',0};
196 static const WCHAR wNewLine[] = {'\r','\n',0};
198 WCHAR szLine[PROFILE_MAX_LINE_LEN];
201 PROFILE_WriteMarker(hFile, encoding);
203 for ( ; section; section = section->next)
205 if (section->name[0])
207 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wSectionFormat, section->name );
208 PROFILE_WriteLine( hFile, szLine, len, encoding );
212 for (key = section->key; key; key = key->next)
214 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNameFormat, key->name );
216 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wValueFormat, key->value );
217 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNewLine );
218 PROFILE_WriteLine( hFile, szLine, len, encoding );
225 /***********************************************************************
228 * Free a profile tree.
230 static void PROFILE_Free( PROFILESECTION *section )
232 PROFILESECTION *next_section;
233 PROFILEKEY *key, *next_key;
235 for ( ; section; section = next_section)
237 for (key = section->key; key; key = next_key)
239 next_key = key->next;
240 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
241 HeapFree( GetProcessHeap(), 0, key );
243 next_section = section->next;
244 HeapFree( GetProcessHeap(), 0, section );
248 /* returns 1 if a character white space else 0 */
249 static inline int PROFILE_isspaceW(WCHAR c)
251 if (isspaceW(c)) return 1;
252 if (c=='\r' || c==0x1a) return 1;
253 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
257 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
259 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
260 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
261 IS_TEXT_UNICODE_ODD_LENGTH;
262 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
264 *len = sizeof(bom_utf8);
265 return ENCODING_UTF8;
267 RtlIsTextUnicode((void *)buffer, *len, &flags);
268 if (flags & IS_TEXT_UNICODE_SIGNATURE)
270 *len = sizeof(WCHAR);
271 return ENCODING_UTF16LE;
273 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
275 *len = sizeof(WCHAR);
276 return ENCODING_UTF16BE;
279 return ENCODING_ANSI;
282 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
284 return memchrW(szStart, '\n', szEnd - szStart);
287 /***********************************************************************
290 * Load a profile tree from a file.
292 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
296 const WCHAR *szLineStart, *szLineEnd;
297 const WCHAR *szValueStart, *szNameEnd, *szEnd;
299 PROFILESECTION *section, *first_section;
300 PROFILESECTION **next_section;
301 PROFILEKEY *key, *prev_key, **next_key;
304 TRACE("%p\n", hFile);
306 dwFileSize = GetFileSize(hFile, NULL);
307 if (dwFileSize == INVALID_FILE_SIZE)
310 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
311 if (!pBuffer) return NULL;
313 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
315 HeapFree(GetProcessHeap(), 0, pBuffer);
316 WARN("Error %ld reading file\n", GetLastError());
320 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
321 /* len is set to the number of bytes in the character marker.
322 * we want to skip these bytes */
323 pBuffer = (char *)pBuffer + len;
328 TRACE("ANSI encoding\n");
330 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
331 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
334 HeapFree(GetProcessHeap(), 0, pBuffer);
337 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
338 szEnd = szFile + len;
341 TRACE("UTF8 encoding\n");
343 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
344 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
347 HeapFree(GetProcessHeap(), 0, pBuffer);
350 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
351 szEnd = szFile + len;
353 case ENCODING_UTF16LE:
354 TRACE("UTF16 Little Endian encoding\n");
355 szFile = (WCHAR *)pBuffer + 1;
356 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
358 case ENCODING_UTF16BE:
359 TRACE("UTF16 Big Endian encoding\n");
360 szFile = (WCHAR *)pBuffer + 1;
361 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
362 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
365 FIXME("encoding type %d not implemented\n", *pEncoding);
366 HeapFree(GetProcessHeap(), 0, pBuffer);
370 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
371 if(first_section == NULL)
373 if (szFile != pBuffer)
374 HeapFree(GetProcessHeap(), 0, szFile);
375 HeapFree(GetProcessHeap(), 0, pBuffer);
378 first_section->name[0] = 0;
379 first_section->key = NULL;
380 first_section->next = NULL;
381 next_section = &first_section->next;
382 next_key = &first_section->key;
384 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
388 szLineStart = szLineEnd + 1;
389 if (szLineStart >= szEnd)
391 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
396 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
398 if (szLineStart >= szLineEnd) continue;
400 if (*szLineStart == '[') /* section start */
402 const WCHAR * szSectionEnd;
403 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
405 WARN("Invalid section header at line %d: %s\n",
406 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
411 len = (int)(szSectionEnd - szLineStart);
412 /* no need to allocate +1 for NULL terminating character as
413 * already included in structure */
414 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
416 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
417 section->name[len] = '\0';
419 section->next = NULL;
420 *next_section = section;
421 next_section = §ion->next;
422 next_key = §ion->key;
425 TRACE("New section: %s\n", debugstr_w(section->name));
431 /* get rid of white space at the end of the line */
432 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
434 /* line end should be pointing to character *after* the last wanted character */
437 /* get rid of white space after the name and before the start
439 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
441 szNameEnd = szValueStart - 1;
442 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
444 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
447 szNameEnd = szLineEnd - 1;
448 /* name end should be pointing to character *after* the last wanted character */
451 len = (int)(szNameEnd - szLineStart);
453 if (len || !prev_key || *prev_key->name)
455 /* no need to allocate +1 for NULL terminating character as
456 * already included in structure */
457 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
458 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
459 key->name[len] = '\0';
462 len = (int)(szLineEnd - szValueStart);
463 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
464 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
465 key->value[len] = '\0';
467 else key->value = NULL;
471 next_key = &key->next;
474 TRACE("New key: name=%s, value=%s\n",
475 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
478 if (szFile != pBuffer)
479 HeapFree(GetProcessHeap(), 0, szFile);
480 HeapFree(GetProcessHeap(), 0, pBuffer);
481 return first_section;
485 /***********************************************************************
486 * PROFILE_DeleteSection
488 * Delete a section from a profile tree.
490 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
494 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
496 PROFILESECTION *to_del = *section;
497 *section = to_del->next;
499 PROFILE_Free( to_del );
502 section = &(*section)->next;
508 /***********************************************************************
511 * Delete a key from a profile tree.
513 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
514 LPCWSTR section_name, LPCWSTR key_name )
518 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
520 PROFILEKEY **key = &(*section)->key;
523 if (!strcmpiW( (*key)->name, key_name ))
525 PROFILEKEY *to_del = *key;
527 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
528 HeapFree( GetProcessHeap(), 0, to_del );
534 section = &(*section)->next;
540 /***********************************************************************
541 * PROFILE_DeleteAllKeys
543 * Delete all keys from a profile tree.
545 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
547 PROFILESECTION **section= &CurProfile->section;
550 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
552 PROFILEKEY **key = &(*section)->key;
555 PROFILEKEY *to_del = *key;
557 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
558 HeapFree( GetProcessHeap(), 0, to_del );
559 CurProfile->changed =TRUE;
562 section = &(*section)->next;
567 /***********************************************************************
570 * Find a key in a profile tree, optionally creating it.
572 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
573 LPCWSTR key_name, BOOL create, BOOL create_always )
578 while (PROFILE_isspaceW(*section_name)) section_name++;
579 p = section_name + strlenW(section_name) - 1;
580 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
581 seclen = p - section_name + 1;
583 while (PROFILE_isspaceW(*key_name)) key_name++;
584 p = key_name + strlenW(key_name) - 1;
585 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
586 keylen = p - key_name + 1;
590 if ( ((*section)->name[0])
591 && (!(strncmpiW( (*section)->name, section_name, seclen )))
592 && (((*section)->name)[seclen] == '\0') )
594 PROFILEKEY **key = &(*section)->key;
598 /* If create_always is FALSE then we check if the keyname
599 * already exists. Otherwise we add it regardless of its
600 * existence, to allow keys to be added more than once in
605 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
606 && (((*key)->name)[keylen] == '\0') )
611 if (!create) return NULL;
612 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
614 strcpyW( (*key)->name, key_name );
615 (*key)->value = NULL;
619 section = &(*section)->next;
621 if (!create) return NULL;
622 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
623 if(*section == NULL) return NULL;
624 strcpyW( (*section)->name, section_name );
625 (*section)->next = NULL;
626 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
627 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
629 HeapFree(GetProcessHeap(), 0, *section);
632 strcpyW( (*section)->key->name, key_name );
633 (*section)->key->value = NULL;
634 (*section)->key->next = NULL;
635 return (*section)->key;
639 /***********************************************************************
642 * Flush the current profile to disk if changed.
644 static BOOL PROFILE_FlushFile(void)
647 FILETIME LastWriteTime;
651 WARN("No current profile!\n");
655 if (!CurProfile->changed) return TRUE;
657 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
659 if (hFile == INVALID_HANDLE_VALUE)
661 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
665 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
666 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
667 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
668 CurProfile->LastWriteTime=LastWriteTime;
669 CloseHandle( hFile );
670 CurProfile->changed = FALSE;
675 /***********************************************************************
676 * PROFILE_ReleaseFile
678 * Flush the current profile to disk and remove it from the cache.
680 static void PROFILE_ReleaseFile(void)
683 PROFILE_Free( CurProfile->section );
684 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
685 CurProfile->changed = FALSE;
686 CurProfile->section = NULL;
687 CurProfile->filename = NULL;
688 CurProfile->encoding = ENCODING_ANSI;
689 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
693 /***********************************************************************
696 * Open a profile file, checking the cached file first.
698 static BOOL PROFILE_Open( LPCWSTR filename )
700 WCHAR windirW[MAX_PATH];
701 WCHAR buffer[MAX_PATH];
702 HANDLE hFile = INVALID_HANDLE_VALUE;
703 FILETIME LastWriteTime;
705 PROFILE *tempProfile;
707 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
709 /* First time around */
712 for(i=0;i<N_CACHED_PROFILES;i++)
714 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
715 if(MRUProfile[i] == NULL) break;
716 MRUProfile[i]->changed=FALSE;
717 MRUProfile[i]->section=NULL;
718 MRUProfile[i]->filename=NULL;
719 MRUProfile[i]->encoding=ENCODING_ANSI;
720 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
723 GetWindowsDirectoryW( windirW, MAX_PATH );
725 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
726 !strchrW(filename, '\\') && !strchrW(filename, '/'))
728 static const WCHAR wszSeparator[] = {'\\', 0};
729 strcpyW(buffer, windirW);
730 strcatW(buffer, wszSeparator);
731 strcatW(buffer, filename);
736 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
739 TRACE("path: %s\n", debugstr_w(buffer));
741 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
743 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
745 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
749 for(i=0;i<N_CACHED_PROFILES;i++)
751 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
753 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
757 tempProfile=MRUProfile[i];
759 MRUProfile[j]=MRUProfile[j-1];
760 CurProfile=tempProfile;
762 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
763 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
764 TRACE("(%s): already opened (mru=%d)\n",
765 debugstr_w(buffer), i );
767 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
768 debugstr_w(buffer), i );
774 /* Flush the old current profile */
777 /* Make the oldest profile the current one only in order to get rid of it */
778 if(i==N_CACHED_PROFILES)
780 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
781 for(i=N_CACHED_PROFILES-1;i>0;i--)
782 MRUProfile[i]=MRUProfile[i-1];
783 CurProfile=tempProfile;
785 if(CurProfile->filename) PROFILE_ReleaseFile();
787 /* OK, now that CurProfile is definitely free we assign it our new file */
788 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
789 strcpyW( CurProfile->filename, buffer );
791 if (hFile != INVALID_HANDLE_VALUE)
793 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
794 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
799 /* Does not exist yet, we will create it in PROFILE_FlushFile */
800 WARN("profile file %s not found\n", debugstr_w(buffer) );
806 /***********************************************************************
809 * Returns all keys of a section.
810 * If return_values is TRUE, also include the corresponding values.
812 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
813 LPWSTR buffer, UINT len, BOOL return_values )
817 if(!buffer) return 0;
819 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
823 if (section->name[0] && !strcmpiW( section->name, section_name ))
826 for (key = section->key; key; key = key->next)
829 if (!*key->name) continue; /* Skip empty lines */
830 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
831 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
832 len -= strlenW(buffer) + 1;
833 buffer += strlenW(buffer) + 1;
836 if (return_values && key->value) {
838 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
839 len -= strlenW(buffer) + 1;
840 buffer += strlenW(buffer) + 1;
845 /*If either lpszSection or lpszKey is NULL and the supplied
846 destination buffer is too small to hold all the strings,
847 the last string is truncated and followed by two null characters.
848 In this case, the return value is equal to cchReturnBuffer
856 section = section->next;
858 buffer[0] = buffer[1] = '\0';
862 /* See GetPrivateProfileSectionNamesA for documentation */
863 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
867 PROFILESECTION *section;
869 TRACE("(%p, %d)\n", buffer, len);
880 section = CurProfile->section;
881 while ((section!=NULL)) {
882 if (section->name[0]) {
883 l = strlenW(section->name)+1;
886 strncpyW(buf, section->name, f-1);
893 strcpyW(buf, section->name);
897 section = section->next;
904 /***********************************************************************
907 * Get a profile string.
909 * Tests with GetPrivateProfileString16, W95a,
910 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
911 * section key_name def_val res buffer
912 * "set1" "1" "x" 43 [data]
913 * "set1" "1 " "x" 43 [data] (!)
914 * "set1" " 1 "' "x" 43 [data] (!)
915 * "set1" "" "x" 1 "x"
916 * "set1" "" "x " 1 "x" (!)
917 * "set1" "" " x " 3 " x" (!)
918 * "set1" NULL "x" 6 "1\02\03\0\0"
919 * "set1" "" "x" 1 "x"
920 * NULL "1" "x" 0 "" (!)
926 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
927 LPCWSTR def_val, LPWSTR buffer, UINT len )
929 PROFILEKEY *key = NULL;
930 static const WCHAR empty_strW[] = { 0 };
932 if(!buffer) return 0;
934 if (!def_val) def_val = empty_strW;
939 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
942 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
943 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
945 TRACE("(%s,%s,%s): returning %s\n",
946 debugstr_w(section), debugstr_w(key_name),
947 debugstr_w(def_val), debugstr_w(buffer) );
948 return strlenW( buffer );
950 /* no "else" here ! */
951 if (section && section[0])
953 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
954 if (!buffer[0]) /* no luck -> def_val */
956 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
957 ret = strlenW(buffer);
966 /***********************************************************************
969 * Set a profile string.
971 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
972 LPCWSTR value, BOOL create_always )
974 if (!key_name) /* Delete a whole section */
976 TRACE("(%s)\n", debugstr_w(section_name));
977 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
979 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
980 this is not an error on application's level.*/
982 else if (!value) /* Delete a key */
984 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
985 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
986 section_name, key_name );
987 return TRUE; /* same error handling as above */
989 else /* Set the key value */
991 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
992 key_name, TRUE, create_always );
993 TRACE("(%s,%s,%s):\n",
994 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
995 if (!key) return FALSE;
997 /* strip the leading spaces. We can safely strip \n\r and
998 * friends too, they should not happen here anyway. */
999 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);