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"
33 #include "wine/winbase16.h"
34 #include "wine/unicode.h"
35 #include "wine/server.h"
36 #include "wine/library.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(profile);
41 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
51 typedef struct tagPROFILEKEY
54 struct tagPROFILEKEY *next;
58 typedef struct tagPROFILESECTION
60 struct tagPROFILEKEY *key;
61 struct tagPROFILESECTION *next;
69 PROFILESECTION *section;
71 FILETIME LastWriteTime;
76 #define N_CACHED_PROFILES 10
78 /* Cached profile files */
79 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
81 #define CurProfile (MRUProfile[0])
83 /* Check for comments in profile */
84 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
86 static const WCHAR emptystringW[] = {0};
87 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
89 static CRITICAL_SECTION PROFILE_CritSect;
90 static CRITICAL_SECTION_DEBUG critsect_debug =
92 0, 0, &PROFILE_CritSect,
93 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
94 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
96 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
98 static const char hex[16] = "0123456789ABCDEF";
100 /***********************************************************************
103 * Copy the content of an entry into a buffer, removing quotes, and possibly
104 * translating environment variables.
106 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
113 if (strip_quote && ((*value == '\'') || (*value == '\"')))
115 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
118 lstrcpynW( buffer, value, len );
119 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
122 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
123 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
126 USHORT * shortbuffer = (USHORT *)buffer;
127 for (i = 0; i < len; i++)
128 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
131 /* writes any necessary encoding marker to the file */
132 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
134 DWORD dwBytesWritten;
141 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
143 case ENCODING_UTF16LE:
145 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
147 case ENCODING_UTF16BE:
149 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
154 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
157 int write_buffer_len;
158 DWORD dwBytesWritten;
160 TRACE("writing: %s\n", debugstr_wn(szLine, len));
165 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
166 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
167 if (!write_buffer) return;
168 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
169 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
170 HeapFree(GetProcessHeap(), 0, write_buffer);
173 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
174 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
175 if (!write_buffer) return;
176 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
177 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
178 HeapFree(GetProcessHeap(), 0, write_buffer);
180 case ENCODING_UTF16LE:
181 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
183 case ENCODING_UTF16BE:
184 PROFILE_ByteSwapShortBuffer(szLine, len);
185 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
188 FIXME("encoding type %d not implemented\n", encoding);
192 /***********************************************************************
195 * Save a profile tree to a file.
197 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
202 PROFILE_WriteMarker(hFile, encoding);
204 for ( ; section; section = section->next)
208 if (section->name[0]) len += strlenW(section->name) + 6;
210 for (key = section->key; key; key = key->next)
212 len += strlenW(key->name) + 2;
213 if (key->value) len += strlenW(key->value) + 1;
216 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
220 if (section->name[0])
225 strcpyW( p, section->name );
231 for (key = section->key; key; key = key->next)
233 strcpyW( p, key->name );
238 strcpyW( p, key->value );
244 PROFILE_WriteLine( hFile, buffer, len, encoding );
245 HeapFree(GetProcessHeap(), 0, buffer);
250 /***********************************************************************
253 * Free a profile tree.
255 static void PROFILE_Free( PROFILESECTION *section )
257 PROFILESECTION *next_section;
258 PROFILEKEY *key, *next_key;
260 for ( ; section; section = next_section)
262 for (key = section->key; key; key = next_key)
264 next_key = key->next;
265 HeapFree( GetProcessHeap(), 0, key->value );
266 HeapFree( GetProcessHeap(), 0, key );
268 next_section = section->next;
269 HeapFree( GetProcessHeap(), 0, section );
273 /* returns 1 if a character white space else 0 */
274 static inline int PROFILE_isspaceW(WCHAR c)
276 if (isspaceW(c)) return 1;
277 if (c=='\r' || c==0x1a) return 1;
278 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
282 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
284 int flags = IS_TEXT_UNICODE_SIGNATURE |
285 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
286 IS_TEXT_UNICODE_ODD_LENGTH;
287 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
289 *len = sizeof(bom_utf8);
290 return ENCODING_UTF8;
292 RtlIsTextUnicode(buffer, *len, &flags);
293 if (flags & IS_TEXT_UNICODE_SIGNATURE)
295 *len = sizeof(WCHAR);
296 return ENCODING_UTF16LE;
298 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
300 *len = sizeof(WCHAR);
301 return ENCODING_UTF16BE;
304 return ENCODING_ANSI;
307 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
309 return memchrW(szStart, '\n', szEnd - szStart);
312 /***********************************************************************
315 * Load a profile tree from a file.
317 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
321 const WCHAR *szLineStart, *szLineEnd;
322 const WCHAR *szValueStart, *szNameEnd, *szEnd;
324 PROFILESECTION *section, *first_section;
325 PROFILESECTION **next_section;
326 PROFILEKEY *key, *prev_key, **next_key;
329 TRACE("%p\n", hFile);
331 dwFileSize = GetFileSize(hFile, NULL);
332 if (dwFileSize == INVALID_FILE_SIZE)
335 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
336 if (!pBuffer) return NULL;
338 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
340 HeapFree(GetProcessHeap(), 0, pBuffer);
341 WARN("Error %ld reading file\n", GetLastError());
345 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
346 /* len is set to the number of bytes in the character marker.
347 * we want to skip these bytes */
348 pBuffer = (char *)pBuffer + len;
353 TRACE("ANSI encoding\n");
355 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
356 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
359 HeapFree(GetProcessHeap(), 0, pBuffer);
362 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
363 szEnd = szFile + len;
366 TRACE("UTF8 encoding\n");
368 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
369 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
372 HeapFree(GetProcessHeap(), 0, pBuffer);
375 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
376 szEnd = szFile + len;
378 case ENCODING_UTF16LE:
379 TRACE("UTF16 Little Endian encoding\n");
380 szFile = (WCHAR *)pBuffer + 1;
381 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
383 case ENCODING_UTF16BE:
384 TRACE("UTF16 Big Endian encoding\n");
385 szFile = (WCHAR *)pBuffer + 1;
386 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
387 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
390 FIXME("encoding type %d not implemented\n", *pEncoding);
391 HeapFree(GetProcessHeap(), 0, pBuffer);
395 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
396 if(first_section == NULL)
398 if (szFile != pBuffer)
399 HeapFree(GetProcessHeap(), 0, szFile);
400 HeapFree(GetProcessHeap(), 0, pBuffer);
403 first_section->name[0] = 0;
404 first_section->key = NULL;
405 first_section->next = NULL;
406 next_section = &first_section->next;
407 next_key = &first_section->key;
409 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
413 szLineStart = szLineEnd + 1;
414 if (szLineStart >= szEnd)
416 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
421 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
423 if (szLineStart >= szLineEnd) continue;
425 if (*szLineStart == '[') /* section start */
427 const WCHAR * szSectionEnd;
428 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
430 WARN("Invalid section header at line %d: %s\n",
431 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
436 len = (int)(szSectionEnd - szLineStart);
437 /* no need to allocate +1 for NULL terminating character as
438 * already included in structure */
439 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
441 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
442 section->name[len] = '\0';
444 section->next = NULL;
445 *next_section = section;
446 next_section = §ion->next;
447 next_key = §ion->key;
450 TRACE("New section: %s\n", debugstr_w(section->name));
456 /* get rid of white space at the end of the line */
457 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
459 /* line end should be pointing to character *after* the last wanted character */
462 /* get rid of white space after the name and before the start
464 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
466 szNameEnd = szValueStart - 1;
467 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
469 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
472 szNameEnd = szLineEnd - 1;
473 /* name end should be pointing to character *after* the last wanted character */
476 len = (int)(szNameEnd - szLineStart);
478 if (len || !prev_key || *prev_key->name)
480 /* no need to allocate +1 for NULL terminating character as
481 * already included in structure */
482 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
483 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
484 key->name[len] = '\0';
487 len = (int)(szLineEnd - szValueStart);
488 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
489 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
490 key->value[len] = '\0';
492 else key->value = NULL;
496 next_key = &key->next;
499 TRACE("New key: name=%s, value=%s\n",
500 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
503 if (szFile != pBuffer)
504 HeapFree(GetProcessHeap(), 0, szFile);
505 HeapFree(GetProcessHeap(), 0, pBuffer);
506 return first_section;
510 /***********************************************************************
511 * PROFILE_DeleteSection
513 * Delete a section from a profile tree.
515 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
519 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
521 PROFILESECTION *to_del = *section;
522 *section = to_del->next;
524 PROFILE_Free( to_del );
527 section = &(*section)->next;
533 /***********************************************************************
536 * Delete a key from a profile tree.
538 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
539 LPCWSTR section_name, LPCWSTR key_name )
543 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
545 PROFILEKEY **key = &(*section)->key;
548 if (!strcmpiW( (*key)->name, key_name ))
550 PROFILEKEY *to_del = *key;
552 HeapFree( GetProcessHeap(), 0, to_del->value);
553 HeapFree( GetProcessHeap(), 0, to_del );
559 section = &(*section)->next;
565 /***********************************************************************
566 * PROFILE_DeleteAllKeys
568 * Delete all keys from a profile tree.
570 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
572 PROFILESECTION **section= &CurProfile->section;
575 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
577 PROFILEKEY **key = &(*section)->key;
580 PROFILEKEY *to_del = *key;
582 HeapFree( GetProcessHeap(), 0, to_del->value);
583 HeapFree( GetProcessHeap(), 0, to_del );
584 CurProfile->changed =TRUE;
587 section = &(*section)->next;
592 /***********************************************************************
595 * Find a key in a profile tree, optionally creating it.
597 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
598 LPCWSTR key_name, BOOL create, BOOL create_always )
603 while (PROFILE_isspaceW(*section_name)) section_name++;
604 p = section_name + strlenW(section_name) - 1;
605 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
606 seclen = p - section_name + 1;
608 while (PROFILE_isspaceW(*key_name)) key_name++;
609 p = key_name + strlenW(key_name) - 1;
610 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
611 keylen = p - key_name + 1;
615 if ( ((*section)->name[0])
616 && (!(strncmpiW( (*section)->name, section_name, seclen )))
617 && (((*section)->name)[seclen] == '\0') )
619 PROFILEKEY **key = &(*section)->key;
623 /* If create_always is FALSE then we check if the keyname
624 * already exists. Otherwise we add it regardless of its
625 * existence, to allow keys to be added more than once in
630 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
631 && (((*key)->name)[keylen] == '\0') )
636 if (!create) return NULL;
637 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
639 strcpyW( (*key)->name, key_name );
640 (*key)->value = NULL;
644 section = &(*section)->next;
646 if (!create) return NULL;
647 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
648 if(*section == NULL) return NULL;
649 strcpyW( (*section)->name, section_name );
650 (*section)->next = NULL;
651 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
652 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
654 HeapFree(GetProcessHeap(), 0, *section);
657 strcpyW( (*section)->key->name, key_name );
658 (*section)->key->value = NULL;
659 (*section)->key->next = NULL;
660 return (*section)->key;
664 /***********************************************************************
667 * Flush the current profile to disk if changed.
669 static BOOL PROFILE_FlushFile(void)
672 FILETIME LastWriteTime;
676 WARN("No current profile!\n");
680 if (!CurProfile->changed) return TRUE;
682 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
684 if (hFile == INVALID_HANDLE_VALUE)
686 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
690 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
691 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
692 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
693 CurProfile->LastWriteTime=LastWriteTime;
694 CloseHandle( hFile );
695 CurProfile->changed = FALSE;
700 /***********************************************************************
701 * PROFILE_ReleaseFile
703 * Flush the current profile to disk and remove it from the cache.
705 static void PROFILE_ReleaseFile(void)
708 PROFILE_Free( CurProfile->section );
709 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
710 CurProfile->changed = FALSE;
711 CurProfile->section = NULL;
712 CurProfile->filename = NULL;
713 CurProfile->encoding = ENCODING_ANSI;
714 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
718 /***********************************************************************
721 * Open a profile file, checking the cached file first.
723 static BOOL PROFILE_Open( LPCWSTR filename )
725 WCHAR windirW[MAX_PATH];
726 WCHAR buffer[MAX_PATH];
727 HANDLE hFile = INVALID_HANDLE_VALUE;
728 FILETIME LastWriteTime;
730 PROFILE *tempProfile;
732 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
734 /* First time around */
737 for(i=0;i<N_CACHED_PROFILES;i++)
739 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
740 if(MRUProfile[i] == NULL) break;
741 MRUProfile[i]->changed=FALSE;
742 MRUProfile[i]->section=NULL;
743 MRUProfile[i]->filename=NULL;
744 MRUProfile[i]->encoding=ENCODING_ANSI;
745 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
748 GetWindowsDirectoryW( windirW, MAX_PATH );
750 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
751 !strchrW(filename, '\\') && !strchrW(filename, '/'))
753 static const WCHAR wszSeparator[] = {'\\', 0};
754 strcpyW(buffer, windirW);
755 strcatW(buffer, wszSeparator);
756 strcatW(buffer, filename);
761 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
764 TRACE("path: %s\n", debugstr_w(buffer));
766 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
768 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
770 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
774 for(i=0;i<N_CACHED_PROFILES;i++)
776 if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
778 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
782 tempProfile=MRUProfile[i];
784 MRUProfile[j]=MRUProfile[j-1];
785 CurProfile=tempProfile;
788 if (hFile != INVALID_HANDLE_VALUE)
790 if (TRACE_ON(profile))
792 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
793 if (memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
794 TRACE("(%s): already opened (mru=%d)\n",
795 debugstr_w(buffer), i);
797 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
798 debugstr_w(buffer), i);
802 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
803 debugstr_w(buffer), i);
808 /* Flush the old current profile */
811 /* Make the oldest profile the current one only in order to get rid of it */
812 if(i==N_CACHED_PROFILES)
814 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
815 for(i=N_CACHED_PROFILES-1;i>0;i--)
816 MRUProfile[i]=MRUProfile[i-1];
817 CurProfile=tempProfile;
819 if(CurProfile->filename) PROFILE_ReleaseFile();
821 /* OK, now that CurProfile is definitely free we assign it our new file */
822 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
823 strcpyW( CurProfile->filename, buffer );
825 if (hFile != INVALID_HANDLE_VALUE)
827 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
828 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
833 /* Does not exist yet, we will create it in PROFILE_FlushFile */
834 WARN("profile file %s not found\n", debugstr_w(buffer) );
840 /***********************************************************************
843 * Returns all keys of a section.
844 * If return_values is TRUE, also include the corresponding values.
846 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
847 LPWSTR buffer, UINT len, BOOL return_values )
851 if(!buffer) return 0;
853 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
857 if (section->name[0] && !strcmpiW( section->name, section_name ))
860 for (key = section->key; key; key = key->next)
863 if (!*key->name) continue; /* Skip empty lines */
864 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
865 if (!return_values && !key->value) continue; /* Skip lines w.o. '=' */
866 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
867 len -= strlenW(buffer) + 1;
868 buffer += strlenW(buffer) + 1;
871 if (return_values && key->value) {
873 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
874 len -= strlenW(buffer) + 1;
875 buffer += strlenW(buffer) + 1;
880 /*If either lpszSection or lpszKey is NULL and the supplied
881 destination buffer is too small to hold all the strings,
882 the last string is truncated and followed by two null characters.
883 In this case, the return value is equal to cchReturnBuffer
891 section = section->next;
893 buffer[0] = buffer[1] = '\0';
897 /* See GetPrivateProfileSectionNamesA for documentation */
898 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
902 PROFILESECTION *section;
904 TRACE("(%p, %d)\n", buffer, len);
915 section = CurProfile->section;
916 while ((section!=NULL)) {
917 if (section->name[0]) {
918 tmplen = strlenW(section->name)+1;
919 if (tmplen > buflen) {
921 memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
928 memcpy(buf, section->name, tmplen * sizeof(WCHAR));
932 section = section->next;
939 /***********************************************************************
942 * Get a profile string.
944 * Tests with GetPrivateProfileString16, W95a,
945 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
946 * section key_name def_val res buffer
947 * "set1" "1" "x" 43 [data]
948 * "set1" "1 " "x" 43 [data] (!)
949 * "set1" " 1 "' "x" 43 [data] (!)
950 * "set1" "" "x" 1 "x"
951 * "set1" "" "x " 1 "x" (!)
952 * "set1" "" " x " 3 " x" (!)
953 * "set1" NULL "x" 6 "1\02\03\0\0"
954 * "set1" "" "x" 1 "x"
955 * NULL "1" "x" 0 "" (!)
961 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
962 LPCWSTR def_val, LPWSTR buffer, UINT len )
964 PROFILEKEY *key = NULL;
965 static const WCHAR empty_strW[] = { 0 };
967 if(!buffer) return 0;
969 if (!def_val) def_val = empty_strW;
974 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
977 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
978 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
980 TRACE("(%s,%s,%s): returning %s\n",
981 debugstr_w(section), debugstr_w(key_name),
982 debugstr_w(def_val), debugstr_w(buffer) );
983 return strlenW( buffer );
985 /* no "else" here ! */
986 if (section && section[0])
988 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
989 if (!buffer[0]) /* no luck -> def_val */
991 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
992 ret = strlenW(buffer);
1001 /***********************************************************************
1004 * Set a profile string.
1006 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
1007 LPCWSTR value, BOOL create_always )
1009 if (!key_name) /* Delete a whole section */
1011 TRACE("(%s)\n", debugstr_w(section_name));
1012 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1014 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1015 this is not an error on application's level.*/
1017 else if (!value) /* Delete a key */
1019 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1020 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1021 section_name, key_name );
1022 return TRUE; /* same error handling as above */
1024 else /* Set the key value */
1026 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1027 key_name, TRUE, create_always );
1028 TRACE("(%s,%s,%s):\n",
1029 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1030 if (!key) return FALSE;
1032 /* strip the leading spaces. We can safely strip \n\r and
1033 * friends too, they should not happen here anyway. */
1034 while (PROFILE_isspaceW(*value)) value++;
1038 if (!strcmpW( key->value, value ))
1040 TRACE(" no change needed\n" );
1041 return TRUE; /* No change needed */
1043 TRACE(" replacing %s\n", debugstr_w(key->value) );
1044 HeapFree( GetProcessHeap(), 0, key->value );
1046 else TRACE(" creating key\n" );
1047 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1048 strcpyW( key->value, value );
1049 CurProfile->changed = TRUE;
1055 /********************* API functions **********************************/
1058 /***********************************************************************
1059 * GetProfileIntA (KERNEL32.@)
1061 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1063 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1066 /***********************************************************************
1067 * GetProfileIntW (KERNEL32.@)
1069 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1071 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1075 * if allow_section_name_copy is TRUE, allow the copying :
1076 * - of Section names if 'section' is NULL
1077 * - of Keys in a Section if 'entry' is NULL
1078 * (see MSDN doc for GetPrivateProfileString)
1080 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1081 LPCWSTR def_val, LPWSTR buffer,
1082 UINT len, LPCWSTR filename,
1083 BOOL allow_section_name_copy )
1086 LPCWSTR pDefVal = NULL;
1091 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1092 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1094 /* strip any trailing ' ' of def_val. */
1097 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1105 if (*p == ' ') /* ouch, contained trailing ' ' */
1107 int len = (int)(p - def_val);
1110 p = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1111 memcpy(p, def_val, len * sizeof(WCHAR));
1117 pDefVal = (LPCWSTR)def_val;
1119 RtlEnterCriticalSection( &PROFILE_CritSect );
1121 if (PROFILE_Open( filename )) {
1122 if ((allow_section_name_copy) && (section == NULL))
1123 ret = PROFILE_GetSectionNames(buffer, len);
1125 /* PROFILE_GetString already handles the 'entry == NULL' case */
1126 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1128 lstrcpynW( buffer, pDefVal, len );
1129 ret = strlenW( buffer );
1132 RtlLeaveCriticalSection( &PROFILE_CritSect );
1134 if (pDefVal != def_val) /* allocated */
1135 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1137 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1142 /***********************************************************************
1143 * GetPrivateProfileString (KERNEL.128)
1145 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1146 LPCSTR def_val, LPSTR buffer,
1147 UINT16 len, LPCSTR filename )
1149 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1151 INT16 retW, ret = 0;
1153 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1154 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1155 else sectionW.Buffer = NULL;
1156 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1157 else entryW.Buffer = NULL;
1158 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1159 else def_valW.Buffer = NULL;
1160 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1161 else filenameW.Buffer = NULL;
1163 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1164 def_valW.Buffer, bufferW, len,
1165 filenameW.Buffer, FALSE );
1168 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1175 ret--; /* strip terminating 0 */
1178 RtlFreeUnicodeString(§ionW);
1179 RtlFreeUnicodeString(&entryW);
1180 RtlFreeUnicodeString(&def_valW);
1181 RtlFreeUnicodeString(&filenameW);
1182 HeapFree(GetProcessHeap(), 0, bufferW);
1186 /***********************************************************************
1187 * GetPrivateProfileStringA (KERNEL32.@)
1189 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1190 LPCSTR def_val, LPSTR buffer,
1191 UINT len, LPCSTR filename )
1193 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1197 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1198 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1199 else sectionW.Buffer = NULL;
1200 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1201 else entryW.Buffer = NULL;
1202 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1203 else def_valW.Buffer = NULL;
1204 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1205 else filenameW.Buffer = NULL;
1207 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1208 def_valW.Buffer, bufferW, len,
1212 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1219 ret--; /* strip terminating 0 */
1222 RtlFreeUnicodeString(§ionW);
1223 RtlFreeUnicodeString(&entryW);
1224 RtlFreeUnicodeString(&def_valW);
1225 RtlFreeUnicodeString(&filenameW);
1226 HeapFree(GetProcessHeap(), 0, bufferW);
1230 /***********************************************************************
1231 * GetPrivateProfileStringW (KERNEL32.@)
1233 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1234 LPCWSTR def_val, LPWSTR buffer,
1235 UINT len, LPCWSTR filename )
1237 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1239 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1240 buffer, len, filename, TRUE );
1243 /***********************************************************************
1244 * GetProfileStringA (KERNEL32.@)
1246 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1247 LPSTR buffer, UINT len )
1249 return GetPrivateProfileStringA( section, entry, def_val,
1250 buffer, len, "win.ini" );
1253 /***********************************************************************
1254 * GetProfileStringW (KERNEL32.@)
1256 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1257 LPCWSTR def_val, LPWSTR buffer, UINT len )
1259 return GetPrivateProfileStringW( section, entry, def_val,
1260 buffer, len, wininiW );
1263 /***********************************************************************
1264 * WriteProfileStringA (KERNEL32.@)
1266 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1269 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1272 /***********************************************************************
1273 * WriteProfileStringW (KERNEL32.@)
1275 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1278 return WritePrivateProfileStringW( section, entry, string, wininiW );
1282 /***********************************************************************
1283 * GetPrivateProfileIntW (KERNEL32.@)
1285 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1286 INT def_val, LPCWSTR filename )
1289 UNICODE_STRING bufferW;
1293 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1294 buffer, sizeof(buffer)/sizeof(WCHAR),
1298 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1300 /* FIXME: if entry can be found but it's empty, then Win16 is
1301 * supposed to return 0 instead of def_val ! Difficult/problematic
1302 * to implement (every other failure also returns zero buffer),
1303 * thus wait until testing framework avail for making sure nothing
1304 * else gets broken that way. */
1305 if (!buffer[0]) return (UINT)def_val;
1307 RtlInitUnicodeString( &bufferW, buffer );
1308 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1312 /***********************************************************************
1313 * GetPrivateProfileIntA (KERNEL32.@)
1315 * FIXME: rewrite using unicode
1317 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1318 INT def_val, LPCSTR filename )
1320 UNICODE_STRING entryW, filenameW, sectionW;
1322 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1323 else entryW.Buffer = NULL;
1324 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1325 else filenameW.Buffer = NULL;
1326 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1327 else sectionW.Buffer = NULL;
1328 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1330 RtlFreeUnicodeString(§ionW);
1331 RtlFreeUnicodeString(&filenameW);
1332 RtlFreeUnicodeString(&entryW);
1336 /***********************************************************************
1337 * GetPrivateProfileSectionW (KERNEL32.@)
1339 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1340 DWORD len, LPCWSTR filename )
1344 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1346 RtlEnterCriticalSection( &PROFILE_CritSect );
1348 if (PROFILE_Open( filename ))
1349 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1351 RtlLeaveCriticalSection( &PROFILE_CritSect );
1356 /***********************************************************************
1357 * GetPrivateProfileSectionA (KERNEL32.@)
1359 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1360 DWORD len, LPCSTR filename )
1362 UNICODE_STRING sectionW, filenameW;
1366 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1367 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1368 else sectionW.Buffer = NULL;
1369 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1370 else filenameW.Buffer = NULL;
1372 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1375 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1391 RtlFreeUnicodeString(§ionW);
1392 RtlFreeUnicodeString(&filenameW);
1393 HeapFree(GetProcessHeap(), 0, bufferW);
1397 /***********************************************************************
1398 * GetProfileSectionA (KERNEL32.@)
1400 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1402 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1405 /***********************************************************************
1406 * GetProfileSectionW (KERNEL32.@)
1408 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1410 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1414 /***********************************************************************
1415 * WritePrivateProfileStringW (KERNEL32.@)
1417 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1418 LPCWSTR string, LPCWSTR filename )
1422 RtlEnterCriticalSection( &PROFILE_CritSect );
1424 if (!section && !entry && !string) /* documented "file flush" case */
1426 if (!filename || PROFILE_Open( filename ))
1428 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1431 else if (PROFILE_Open( filename ))
1434 FIXME("(NULL?,%s,%s,%s)?\n",
1435 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1437 ret = PROFILE_SetString( section, entry, string, FALSE);
1438 PROFILE_FlushFile();
1442 RtlLeaveCriticalSection( &PROFILE_CritSect );
1446 /***********************************************************************
1447 * WritePrivateProfileStringA (KERNEL32.@)
1449 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1450 LPCSTR string, LPCSTR filename )
1452 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1455 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1456 else sectionW.Buffer = NULL;
1457 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1458 else entryW.Buffer = NULL;
1459 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1460 else stringW.Buffer = NULL;
1461 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1462 else filenameW.Buffer = NULL;
1464 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1465 stringW.Buffer, filenameW.Buffer);
1466 RtlFreeUnicodeString(§ionW);
1467 RtlFreeUnicodeString(&entryW);
1468 RtlFreeUnicodeString(&stringW);
1469 RtlFreeUnicodeString(&filenameW);
1473 /***********************************************************************
1474 * WritePrivateProfileSectionW (KERNEL32.@)
1476 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1477 LPCWSTR string, LPCWSTR filename )
1482 RtlEnterCriticalSection( &PROFILE_CritSect );
1484 if (!section && !string)
1486 if (!filename || PROFILE_Open( filename ))
1488 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1491 else if (PROFILE_Open( filename )) {
1492 if (!string) {/* delete the named section*/
1493 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1494 PROFILE_FlushFile();
1496 PROFILE_DeleteAllKeys(section);
1499 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1500 strcpyW( buf, string );
1501 if((p = strchrW( buf, '='))) {
1503 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1505 HeapFree( GetProcessHeap(), 0, buf );
1506 string += strlenW(string)+1;
1508 PROFILE_FlushFile();
1512 RtlLeaveCriticalSection( &PROFILE_CritSect );
1516 /***********************************************************************
1517 * WritePrivateProfileSectionA (KERNEL32.@)
1519 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1520 LPCSTR string, LPCSTR filename)
1523 UNICODE_STRING sectionW, filenameW;
1532 while(*p) p += strlen(p) + 1;
1533 lenA = p - string + 1;
1534 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1535 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1536 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1538 else stringW = NULL;
1539 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1540 else sectionW.Buffer = NULL;
1541 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1542 else filenameW.Buffer = NULL;
1544 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1546 HeapFree(GetProcessHeap(), 0, stringW);
1547 RtlFreeUnicodeString(§ionW);
1548 RtlFreeUnicodeString(&filenameW);
1552 /***********************************************************************
1553 * WriteProfileSectionA (KERNEL32.@)
1555 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1558 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1561 /***********************************************************************
1562 * WriteProfileSectionW (KERNEL32.@)
1564 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1566 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1570 /***********************************************************************
1571 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1573 * Returns the section names contained in the specified file.
1574 * FIXME: Where do we find this file when the path is relative?
1575 * The section names are returned as a list of strings with an extra
1576 * '\0' to mark the end of the list. Except for that the behavior
1577 * depends on the Windows version.
1580 * - if the buffer is 0 or 1 character long then it is as if it was of
1582 * - otherwise, if the buffer is too small only the section names that fit
1584 * - note that this means if the buffer was too small to return even just
1585 * the first section name then a single '\0' will be returned.
1586 * - the return value is the number of characters written in the buffer,
1587 * except if the buffer was too small in which case len-2 is returned
1590 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1591 * '\0' and the return value is 0
1592 * - otherwise if the buffer is too small then the first section name that
1593 * does not fit is truncated so that the string list can be terminated
1594 * correctly (double '\0')
1595 * - the return value is the number of characters written in the buffer
1596 * except for the trailing '\0'. If the buffer is too small, then the
1597 * return value is len-2
1598 * - Win2000 has a bug that triggers when the section names and the
1599 * trailing '\0' fit exactly in the buffer. In that case the trailing
1602 * Wine implements the observed Win2000 behavior (except for the bug).
1604 * Note that when the buffer is big enough then the return value may be any
1605 * value between 1 and len-1 (or len in Win95), including len-2.
1607 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1612 RtlEnterCriticalSection( &PROFILE_CritSect );
1614 if (PROFILE_Open( filename ))
1615 ret = PROFILE_GetSectionNames(buffer, size);
1617 RtlLeaveCriticalSection( &PROFILE_CritSect );
1623 /***********************************************************************
1624 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1626 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1629 UNICODE_STRING filenameW;
1633 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1634 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1635 else filenameW.Buffer = NULL;
1637 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1640 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1648 RtlFreeUnicodeString(&filenameW);
1649 HeapFree(GetProcessHeap(), 0, bufferW);
1653 /***********************************************************************
1654 * GetPrivateProfileStructW (KERNEL32.@)
1656 * Should match Win95's behaviour pretty much
1658 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1659 LPVOID buf, UINT len, LPCWSTR filename)
1663 RtlEnterCriticalSection( &PROFILE_CritSect );
1665 if (PROFILE_Open( filename )) {
1666 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1668 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1669 if (((strlenW(k->value) - 2) / 2) == len)
1676 end = k->value + strlenW(k->value); /* -> '\0' */
1677 /* check for invalid chars in ASCII coded hex string */
1678 for (p=k->value; p < end; p++)
1682 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1683 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1690 BOOL highnibble = TRUE;
1692 LPBYTE binbuf = (LPBYTE)buf;
1694 end -= 2; /* don't include checksum in output data */
1695 /* translate ASCII hex format into binary data */
1696 for (p=k->value; p < end; p++)
1700 (c - 'A' + 10) : (c - '0');
1707 *binbuf++ = b; /* feed binary data into output */
1708 chksum += b; /* calculate checksum */
1710 highnibble ^= 1; /* toggle */
1712 /* retrieve stored checksum value */
1714 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1716 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1717 if (b == (chksum & 0xff)) /* checksums match ? */
1723 RtlLeaveCriticalSection( &PROFILE_CritSect );
1728 /***********************************************************************
1729 * GetPrivateProfileStructA (KERNEL32.@)
1731 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1732 LPVOID buffer, UINT len, LPCSTR filename)
1734 UNICODE_STRING sectionW, keyW, filenameW;
1737 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1738 else sectionW.Buffer = NULL;
1739 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1740 else keyW.Buffer = NULL;
1741 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1742 else filenameW.Buffer = NULL;
1744 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1746 /* Do not translate binary data. */
1748 RtlFreeUnicodeString(§ionW);
1749 RtlFreeUnicodeString(&keyW);
1750 RtlFreeUnicodeString(&filenameW);
1756 /***********************************************************************
1757 * WritePrivateProfileStructW (KERNEL32.@)
1759 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1760 LPVOID buf, UINT bufsize, LPCWSTR filename)
1764 LPWSTR outstring, p;
1767 if (!section && !key && !buf) /* flush the cache */
1768 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1770 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1771 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1773 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1774 *p++ = hex[*binbuf >> 4];
1775 *p++ = hex[*binbuf & 0xf];
1778 /* checksum is sum & 0xff */
1779 *p++ = hex[(sum & 0xf0) >> 4];
1780 *p++ = hex[sum & 0xf];
1783 RtlEnterCriticalSection( &PROFILE_CritSect );
1785 if (PROFILE_Open( filename )) {
1786 ret = PROFILE_SetString( section, key, outstring, FALSE);
1787 PROFILE_FlushFile();
1790 RtlLeaveCriticalSection( &PROFILE_CritSect );
1792 HeapFree( GetProcessHeap(), 0, outstring );
1797 /***********************************************************************
1798 * WritePrivateProfileStructA (KERNEL32.@)
1800 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1801 LPVOID buf, UINT bufsize, LPCSTR filename)
1803 UNICODE_STRING sectionW, keyW, filenameW;
1806 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1807 else sectionW.Buffer = NULL;
1808 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1809 else keyW.Buffer = NULL;
1810 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1811 else filenameW.Buffer = NULL;
1813 /* Do not translate binary data. */
1814 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1817 RtlFreeUnicodeString(§ionW);
1818 RtlFreeUnicodeString(&keyW);
1819 RtlFreeUnicodeString(&filenameW);
1824 /***********************************************************************
1825 * WriteOutProfiles (KERNEL.315)
1827 void WINAPI WriteOutProfiles16(void)
1829 RtlEnterCriticalSection( &PROFILE_CritSect );
1830 PROFILE_FlushFile();
1831 RtlLeaveCriticalSection( &PROFILE_CritSect );
1834 /***********************************************************************
1835 * CloseProfileUserMapping (KERNEL32.@)
1837 BOOL WINAPI CloseProfileUserMapping(void) {
1838 FIXME("(), stub!\n");
1839 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);