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 LPCWSTR 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);
1075 p = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1076 strncpyW(p, def_val, len);
1082 pDefVal = (LPCWSTR)def_val;
1084 RtlEnterCriticalSection( &PROFILE_CritSect );
1086 if (PROFILE_Open( filename )) {
1087 if ((allow_section_name_copy) && (section == NULL))
1088 ret = PROFILE_GetSectionNames(buffer, len);
1090 /* PROFILE_GetString already handles the 'entry == NULL' case */
1091 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1093 lstrcpynW( buffer, pDefVal, len );
1094 ret = strlenW( buffer );
1097 RtlLeaveCriticalSection( &PROFILE_CritSect );
1099 if (pDefVal != def_val) /* allocated */
1100 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1102 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1107 /***********************************************************************
1108 * GetPrivateProfileString (KERNEL.128)
1110 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1111 LPCSTR def_val, LPSTR buffer,
1112 UINT16 len, LPCSTR filename )
1114 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1116 INT16 retW, ret = 0;
1118 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1119 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1120 else sectionW.Buffer = NULL;
1121 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1122 else entryW.Buffer = NULL;
1123 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1124 else def_valW.Buffer = NULL;
1125 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1126 else filenameW.Buffer = NULL;
1128 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1129 def_valW.Buffer, bufferW, len,
1130 filenameW.Buffer, FALSE );
1133 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1140 ret--; /* strip terminating 0 */
1143 RtlFreeUnicodeString(§ionW);
1144 RtlFreeUnicodeString(&entryW);
1145 RtlFreeUnicodeString(&def_valW);
1146 RtlFreeUnicodeString(&filenameW);
1147 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1151 /***********************************************************************
1152 * GetPrivateProfileStringA (KERNEL32.@)
1154 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1155 LPCSTR def_val, LPSTR buffer,
1156 UINT len, LPCSTR filename )
1158 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1162 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1163 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1164 else sectionW.Buffer = NULL;
1165 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1166 else entryW.Buffer = NULL;
1167 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1168 else def_valW.Buffer = NULL;
1169 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1170 else filenameW.Buffer = NULL;
1172 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1173 def_valW.Buffer, bufferW, len,
1177 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1184 ret--; /* strip terminating 0 */
1187 RtlFreeUnicodeString(§ionW);
1188 RtlFreeUnicodeString(&entryW);
1189 RtlFreeUnicodeString(&def_valW);
1190 RtlFreeUnicodeString(&filenameW);
1191 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1195 /***********************************************************************
1196 * GetPrivateProfileStringW (KERNEL32.@)
1198 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1199 LPCWSTR def_val, LPWSTR buffer,
1200 UINT len, LPCWSTR filename )
1202 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1204 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1205 buffer, len, filename, TRUE );
1208 /***********************************************************************
1209 * GetProfileStringA (KERNEL32.@)
1211 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1212 LPSTR buffer, UINT len )
1214 return GetPrivateProfileStringA( section, entry, def_val,
1215 buffer, len, "win.ini" );
1218 /***********************************************************************
1219 * GetProfileStringW (KERNEL32.@)
1221 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1222 LPCWSTR def_val, LPWSTR buffer, UINT len )
1224 return GetPrivateProfileStringW( section, entry, def_val,
1225 buffer, len, wininiW );
1228 /***********************************************************************
1229 * WriteProfileStringA (KERNEL32.@)
1231 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1234 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1237 /***********************************************************************
1238 * WriteProfileStringW (KERNEL32.@)
1240 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1243 return WritePrivateProfileStringW( section, entry, string, wininiW );
1247 /***********************************************************************
1248 * GetPrivateProfileIntW (KERNEL32.@)
1250 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1251 INT def_val, LPCWSTR filename )
1254 UNICODE_STRING bufferW;
1258 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1259 buffer, sizeof(buffer)/sizeof(WCHAR),
1263 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1265 /* FIXME: if entry can be found but it's empty, then Win16 is
1266 * supposed to return 0 instead of def_val ! Difficult/problematic
1267 * to implement (every other failure also returns zero buffer),
1268 * thus wait until testing framework avail for making sure nothing
1269 * else gets broken that way. */
1270 if (!buffer[0]) return (UINT)def_val;
1272 RtlInitUnicodeString( &bufferW, buffer );
1273 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1277 /***********************************************************************
1278 * GetPrivateProfileIntA (KERNEL32.@)
1280 * FIXME: rewrite using unicode
1282 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1283 INT def_val, LPCSTR filename )
1285 UNICODE_STRING entryW, filenameW, sectionW;
1287 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1288 else entryW.Buffer = NULL;
1289 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1290 else filenameW.Buffer = NULL;
1291 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1292 else sectionW.Buffer = NULL;
1293 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1295 RtlFreeUnicodeString(§ionW);
1296 RtlFreeUnicodeString(&filenameW);
1297 RtlFreeUnicodeString(&entryW);
1301 /***********************************************************************
1302 * GetPrivateProfileSectionW (KERNEL32.@)
1304 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1305 DWORD len, LPCWSTR filename )
1309 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1311 RtlEnterCriticalSection( &PROFILE_CritSect );
1313 if (PROFILE_Open( filename ))
1314 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1316 RtlLeaveCriticalSection( &PROFILE_CritSect );
1321 /***********************************************************************
1322 * GetPrivateProfileSectionA (KERNEL32.@)
1324 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1325 DWORD len, LPCSTR filename )
1327 UNICODE_STRING sectionW, filenameW;
1331 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1332 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1333 else sectionW.Buffer = NULL;
1334 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1335 else filenameW.Buffer = NULL;
1337 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1340 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1356 RtlFreeUnicodeString(§ionW);
1357 RtlFreeUnicodeString(&filenameW);
1358 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1362 /***********************************************************************
1363 * GetProfileSectionA (KERNEL32.@)
1365 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1367 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1370 /***********************************************************************
1371 * GetProfileSectionW (KERNEL32.@)
1373 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1375 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1379 /***********************************************************************
1380 * WritePrivateProfileStringW (KERNEL32.@)
1382 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1383 LPCWSTR string, LPCWSTR filename )
1387 RtlEnterCriticalSection( &PROFILE_CritSect );
1389 if (PROFILE_Open( filename ))
1391 if (!section && !entry && !string) /* documented "file flush" case */
1393 PROFILE_FlushFile();
1394 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1398 FIXME("(NULL?,%s,%s,%s)?\n",
1399 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1401 ret = PROFILE_SetString( section, entry, string, FALSE);
1402 PROFILE_FlushFile();
1407 RtlLeaveCriticalSection( &PROFILE_CritSect );
1411 /***********************************************************************
1412 * WritePrivateProfileStringA (KERNEL32.@)
1414 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1415 LPCSTR string, LPCSTR filename )
1417 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1420 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1421 else sectionW.Buffer = NULL;
1422 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1423 else entryW.Buffer = NULL;
1424 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1425 else stringW.Buffer = NULL;
1426 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1427 else filenameW.Buffer = NULL;
1429 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1430 stringW.Buffer, filenameW.Buffer);
1431 RtlFreeUnicodeString(§ionW);
1432 RtlFreeUnicodeString(&entryW);
1433 RtlFreeUnicodeString(&stringW);
1434 RtlFreeUnicodeString(&filenameW);
1438 /***********************************************************************
1439 * WritePrivateProfileSectionW (KERNEL32.@)
1441 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1442 LPCWSTR string, LPCWSTR filename )
1447 RtlEnterCriticalSection( &PROFILE_CritSect );
1449 if (PROFILE_Open( filename )) {
1450 if (!section && !string)
1451 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1452 else if (!string) {/* delete the named section*/
1453 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1454 PROFILE_FlushFile();
1456 PROFILE_DeleteAllKeys(section);
1459 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1460 strcpyW( buf, string );
1461 if((p = strchrW( buf, '='))) {
1463 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1465 HeapFree( GetProcessHeap(), 0, buf );
1466 string += strlenW(string)+1;
1468 PROFILE_FlushFile();
1472 RtlLeaveCriticalSection( &PROFILE_CritSect );
1476 /***********************************************************************
1477 * WritePrivateProfileSectionA (KERNEL32.@)
1479 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1480 LPCSTR string, LPCSTR filename)
1483 UNICODE_STRING sectionW, filenameW;
1492 while(*p) p += strlen(p) + 1;
1493 lenA = p - string + 1;
1494 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1495 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1496 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1498 else stringW = NULL;
1499 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1500 else sectionW.Buffer = NULL;
1501 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1502 else filenameW.Buffer = NULL;
1504 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1506 HeapFree(GetProcessHeap(), 0, stringW);
1507 RtlFreeUnicodeString(§ionW);
1508 RtlFreeUnicodeString(&filenameW);
1512 /***********************************************************************
1513 * WriteProfileSectionA (KERNEL32.@)
1515 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1518 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1521 /***********************************************************************
1522 * WriteProfileSectionW (KERNEL32.@)
1524 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1526 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1530 /***********************************************************************
1531 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1533 * Returns the section names contained in the specified file.
1534 * FIXME: Where do we find this file when the path is relative?
1535 * The section names are returned as a list of strings with an extra
1536 * '\0' to mark the end of the list. Except for that the behavior
1537 * depends on the Windows version.
1540 * - if the buffer is 0 or 1 character long then it is as if it was of
1542 * - otherwise, if the buffer is to small only the section names that fit
1544 * - note that this means if the buffer was to small to return even just
1545 * the first section name then a single '\0' will be returned.
1546 * - the return value is the number of characters written in the buffer,
1547 * except if the buffer was too smal in which case len-2 is returned
1550 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1551 * '\0' and the return value is 0
1552 * - otherwise if the buffer is too small then the first section name that
1553 * does not fit is truncated so that the string list can be terminated
1554 * correctly (double '\0')
1555 * - the return value is the number of characters written in the buffer
1556 * except for the trailing '\0'. If the buffer is too small, then the
1557 * return value is len-2
1558 * - Win2000 has a bug that triggers when the section names and the
1559 * trailing '\0' fit exactly in the buffer. In that case the trailing
1562 * Wine implements the observed Win2000 behavior (except for the bug).
1564 * Note that when the buffer is big enough then the return value may be any
1565 * value between 1 and len-1 (or len in Win95), including len-2.
1567 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1572 RtlEnterCriticalSection( &PROFILE_CritSect );
1574 if (PROFILE_Open( filename ))
1575 ret = PROFILE_GetSectionNames(buffer, size);
1577 RtlLeaveCriticalSection( &PROFILE_CritSect );
1583 /***********************************************************************
1584 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1586 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1589 UNICODE_STRING filenameW;
1593 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1594 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1595 else filenameW.Buffer = NULL;
1597 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1600 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1608 RtlFreeUnicodeString(&filenameW);
1609 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1613 /***********************************************************************
1614 * GetPrivateProfileStructW (KERNEL32.@)
1616 * Should match Win95's behaviour pretty much
1618 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1619 LPVOID buf, UINT len, LPCWSTR filename)
1623 RtlEnterCriticalSection( &PROFILE_CritSect );
1625 if (PROFILE_Open( filename )) {
1626 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1628 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1629 if (((strlenW(k->value) - 2) / 2) == len)
1636 end = k->value + strlenW(k->value); /* -> '\0' */
1637 /* check for invalid chars in ASCII coded hex string */
1638 for (p=k->value; p < end; p++)
1642 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1643 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1650 BOOL highnibble = TRUE;
1652 LPBYTE binbuf = (LPBYTE)buf;
1654 end -= 2; /* don't include checksum in output data */
1655 /* translate ASCII hex format into binary data */
1656 for (p=k->value; p < end; p++)
1660 (c - 'A' + 10) : (c - '0');
1667 *binbuf++ = b; /* feed binary data into output */
1668 chksum += b; /* calculate checksum */
1670 highnibble ^= 1; /* toggle */
1672 /* retrieve stored checksum value */
1674 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1676 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1677 if (b == (chksum & 0xff)) /* checksums match ? */
1683 RtlLeaveCriticalSection( &PROFILE_CritSect );
1688 /***********************************************************************
1689 * GetPrivateProfileStructA (KERNEL32.@)
1691 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1692 LPVOID buffer, UINT len, LPCSTR filename)
1694 UNICODE_STRING sectionW, keyW, filenameW;
1697 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1698 else sectionW.Buffer = NULL;
1699 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1700 else keyW.Buffer = NULL;
1701 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1702 else filenameW.Buffer = NULL;
1704 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1706 /* Do not translate binary data. */
1708 RtlFreeUnicodeString(§ionW);
1709 RtlFreeUnicodeString(&keyW);
1710 RtlFreeUnicodeString(&filenameW);
1716 /***********************************************************************
1717 * WritePrivateProfileStructW (KERNEL32.@)
1719 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1720 LPVOID buf, UINT bufsize, LPCWSTR filename)
1724 LPWSTR outstring, p;
1727 if (!section && !key && !buf) /* flush the cache */
1728 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1730 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1731 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1733 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1734 *p++ = hex[*binbuf >> 4];
1735 *p++ = hex[*binbuf & 0xf];
1738 /* checksum is sum & 0xff */
1739 *p++ = hex[(sum & 0xf0) >> 4];
1740 *p++ = hex[sum & 0xf];
1743 RtlEnterCriticalSection( &PROFILE_CritSect );
1745 if (PROFILE_Open( filename )) {
1746 ret = PROFILE_SetString( section, key, outstring, FALSE);
1747 PROFILE_FlushFile();
1750 RtlLeaveCriticalSection( &PROFILE_CritSect );
1752 HeapFree( GetProcessHeap(), 0, outstring );
1757 /***********************************************************************
1758 * WritePrivateProfileStructA (KERNEL32.@)
1760 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1761 LPVOID buf, UINT bufsize, LPCSTR filename)
1763 UNICODE_STRING sectionW, keyW, filenameW;
1766 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1767 else sectionW.Buffer = NULL;
1768 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1769 else keyW.Buffer = NULL;
1770 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1771 else filenameW.Buffer = NULL;
1773 /* Do not translate binary data. */
1774 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1777 RtlFreeUnicodeString(§ionW);
1778 RtlFreeUnicodeString(&keyW);
1779 RtlFreeUnicodeString(&filenameW);
1784 /***********************************************************************
1785 * WriteOutProfiles (KERNEL.315)
1787 void WINAPI WriteOutProfiles16(void)
1789 RtlEnterCriticalSection( &PROFILE_CritSect );
1790 PROFILE_FlushFile();
1791 RtlLeaveCriticalSection( &PROFILE_CritSect );
1794 /***********************************************************************
1795 * CloseProfileUserMapping (KERNEL32.@)
1797 BOOL WINAPI CloseProfileUserMapping(void) {
1798 FIXME("(), stub!\n");
1799 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);