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"
32 #include <sys/types.h>
43 #include "wine/winbase16.h"
47 #include "wine/unicode.h"
48 #include "wine/server.h"
49 #include "wine/library.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(profile);
54 typedef struct tagPROFILEKEY
57 struct tagPROFILEKEY *next;
61 typedef struct tagPROFILESECTION
63 struct tagPROFILEKEY *key;
64 struct tagPROFILESECTION *next;
72 PROFILESECTION *section;
80 #define N_CACHED_PROFILES 10
82 /* Cached profile files */
83 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
85 #define CurProfile (MRUProfile[0])
87 #define PROFILE_MAX_LINE_LEN 1024
89 /* Check for comments in profile */
90 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
92 static const WCHAR emptystringW[] = {0};
93 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
95 static CRITICAL_SECTION PROFILE_CritSect;
96 static CRITICAL_SECTION_DEBUG critsect_debug =
98 0, 0, &PROFILE_CritSect,
99 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
100 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
102 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
104 static const char hex[16] = "0123456789ABCDEF";
106 /***********************************************************************
109 * Copy the content of an entry into a buffer, removing quotes, and possibly
110 * translating environment variables.
112 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
119 if (strip_quote && ((*value == '\'') || (*value == '\"')))
121 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
124 lstrcpynW( buffer, value, len );
125 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
129 /***********************************************************************
132 * Save a profile tree to a file.
134 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
137 char buffer[PROFILE_MAX_LINE_LEN];
139 for ( ; section; section = section->next)
141 if (section->name[0])
143 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
144 fprintf( file, "\r\n[%s]\r\n", buffer );
146 for (key = section->key; key; key = key->next)
148 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
149 fprintf( file, "%s", buffer );
152 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
153 fprintf( file, "=%s", buffer );
155 fprintf( file, "\r\n" );
161 /***********************************************************************
164 * Free a profile tree.
166 static void PROFILE_Free( PROFILESECTION *section )
168 PROFILESECTION *next_section;
169 PROFILEKEY *key, *next_key;
171 for ( ; section; section = next_section)
173 for (key = section->key; key; key = next_key)
175 next_key = key->next;
176 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
177 HeapFree( GetProcessHeap(), 0, key );
179 next_section = section->next;
180 HeapFree( GetProcessHeap(), 0, section );
184 static inline int PROFILE_isspace(char c)
186 if (isspace(c)) return 1;
187 if (c=='\r' || c==0x1a) return 1;
188 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
193 /***********************************************************************
196 * Load a profile tree from a file.
198 static PROFILESECTION *PROFILE_Load( FILE *file )
200 char buffer[PROFILE_MAX_LINE_LEN];
203 PROFILESECTION *section, *first_section;
204 PROFILESECTION **next_section;
205 PROFILEKEY *key, *prev_key, **next_key;
207 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
208 if(first_section == NULL) return NULL;
209 first_section->name[0] = 0;
210 first_section->key = NULL;
211 first_section->next = NULL;
212 next_section = &first_section->next;
213 next_key = &first_section->key;
216 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
220 while (*p && PROFILE_isspace(*p)) p++;
221 if (*p == '[') /* section start */
223 if (!(p2 = strrchr( p, ']' )))
225 WARN("Invalid section header at line %d: '%s'\n",
233 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
235 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
237 section->next = NULL;
238 *next_section = section;
239 next_section = §ion->next;
240 next_key = §ion->key;
243 TRACE("New section: %s\n", debugstr_w(section->name));
250 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
252 if ((p2 = strchr( p, '=' )) != NULL)
255 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
257 while (*p2 && PROFILE_isspace(*p2)) p2++;
260 if(*p || !prev_key || *prev_key->name)
263 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
264 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
267 len = strlen(p2) + 1;
268 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
269 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
271 else key->value = NULL;
275 next_key = &key->next;
278 TRACE("New key: name=%s, value=%s\n",
279 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
282 return first_section;
286 /***********************************************************************
287 * PROFILE_DeleteSection
289 * Delete a section from a profile tree.
291 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
295 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
297 PROFILESECTION *to_del = *section;
298 *section = to_del->next;
300 PROFILE_Free( to_del );
303 section = &(*section)->next;
309 /***********************************************************************
312 * Delete a key from a profile tree.
314 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
315 LPCWSTR section_name, LPCWSTR key_name )
319 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
321 PROFILEKEY **key = &(*section)->key;
324 if (!strcmpiW( (*key)->name, key_name ))
326 PROFILEKEY *to_del = *key;
328 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
329 HeapFree( GetProcessHeap(), 0, to_del );
335 section = &(*section)->next;
341 /***********************************************************************
342 * PROFILE_DeleteAllKeys
344 * Delete all keys from a profile tree.
346 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
348 PROFILESECTION **section= &CurProfile->section;
351 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
353 PROFILEKEY **key = &(*section)->key;
356 PROFILEKEY *to_del = *key;
358 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
359 HeapFree( GetProcessHeap(), 0, to_del );
360 CurProfile->changed =TRUE;
363 section = &(*section)->next;
368 /***********************************************************************
371 * Find a key in a profile tree, optionally creating it.
373 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
374 LPCWSTR key_name, BOOL create, BOOL create_always )
379 while (PROFILE_isspace(*section_name)) section_name++;
380 p = section_name + strlenW(section_name) - 1;
381 while ((p > section_name) && PROFILE_isspace(*p)) p--;
382 seclen = p - section_name + 1;
384 while (PROFILE_isspace(*key_name)) key_name++;
385 p = key_name + strlenW(key_name) - 1;
386 while ((p > key_name) && PROFILE_isspace(*p)) p--;
387 keylen = p - key_name + 1;
391 if ( ((*section)->name[0])
392 && (!(strncmpiW( (*section)->name, section_name, seclen )))
393 && (((*section)->name)[seclen] == '\0') )
395 PROFILEKEY **key = &(*section)->key;
399 /* If create_always is FALSE then we check if the keyname
400 * already exists. Otherwise we add it regardless of its
401 * existence, to allow keys to be added more than once in
406 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
407 && (((*key)->name)[keylen] == '\0') )
412 if (!create) return NULL;
413 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
415 strcpyW( (*key)->name, key_name );
416 (*key)->value = NULL;
420 section = &(*section)->next;
422 if (!create) return NULL;
423 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
424 if(*section == NULL) return NULL;
425 strcpyW( (*section)->name, section_name );
426 (*section)->next = NULL;
427 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
428 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
430 HeapFree(GetProcessHeap(), 0, *section);
433 strcpyW( (*section)->key->name, key_name );
434 (*section)->key->value = NULL;
435 (*section)->key->next = NULL;
436 return (*section)->key;
440 /***********************************************************************
443 * Flush the current profile to disk if changed.
445 static BOOL PROFILE_FlushFile(void)
447 char *p, buffer[MAX_PATHNAME_LEN];
448 const char *unix_name;
454 WARN("No current profile!\n");
458 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
459 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
461 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
462 WCHAR *name, *name_lwr;
463 /* Try to create it in $HOME/.wine */
464 /* FIXME: this will need a more general solution */
465 strcpy( buffer, wine_get_config_dir() );
466 p = buffer + strlen(buffer);
468 *p = 0; /* make strlen() below happy */
469 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
471 /* create a lower cased version of the name */
472 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
473 strcpyW(name_lwr, name);
475 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
476 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
477 HeapFree(GetProcessHeap(), 0, name_lwr);
479 file = fopen( buffer, "w" );
485 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
489 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
490 PROFILE_Save( file, CurProfile->section );
492 CurProfile->changed = FALSE;
493 if(!stat(unix_name,&buf))
494 CurProfile->mtime=buf.st_mtime;
499 /***********************************************************************
500 * PROFILE_ReleaseFile
502 * Flush the current profile to disk and remove it from the cache.
504 static void PROFILE_ReleaseFile(void)
507 PROFILE_Free( CurProfile->section );
508 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
509 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
510 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
511 CurProfile->changed = FALSE;
512 CurProfile->section = NULL;
513 CurProfile->dos_name = NULL;
514 CurProfile->unix_name = NULL;
515 CurProfile->filename = NULL;
516 CurProfile->mtime = 0;
520 /***********************************************************************
523 * Open a profile file, checking the cached file first.
525 static BOOL PROFILE_Open( LPCWSTR filename )
527 DOS_FULL_NAME full_name;
528 char buffer[MAX_PATHNAME_LEN];
530 WCHAR *name, *name_lwr;
535 PROFILE *tempProfile;
537 /* First time around */
540 for(i=0;i<N_CACHED_PROFILES;i++)
542 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
543 if(MRUProfile[i] == NULL) break;
544 MRUProfile[i]->changed=FALSE;
545 MRUProfile[i]->section=NULL;
546 MRUProfile[i]->dos_name=NULL;
547 MRUProfile[i]->unix_name=NULL;
548 MRUProfile[i]->filename=NULL;
549 MRUProfile[i]->mtime=0;
552 /* Check for a match */
554 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
555 strchrW( filename, ':' ))
557 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
561 static const WCHAR bkslashW[] = {'\\',0};
562 WCHAR windirW[MAX_PATH];
564 GetWindowsDirectoryW( windirW, MAX_PATH );
565 strcatW( windirW, bkslashW );
566 strcatW( windirW, filename );
567 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
570 for(i=0;i<N_CACHED_PROFILES;i++)
572 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
573 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
578 tempProfile=MRUProfile[i];
580 MRUProfile[j]=MRUProfile[j-1];
581 CurProfile=tempProfile;
583 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
584 TRACE("(%s): already opened (mru=%d)\n",
585 debugstr_w(filename), i );
587 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
588 debugstr_w(filename), i );
593 /* Flush the old current profile */
596 /* Make the oldest profile the current one only in order to get rid of it */
597 if(i==N_CACHED_PROFILES)
599 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
600 for(i=N_CACHED_PROFILES-1;i>0;i--)
601 MRUProfile[i]=MRUProfile[i-1];
602 CurProfile=tempProfile;
604 if(CurProfile->filename) PROFILE_ReleaseFile();
606 /* OK, now that CurProfile is definitely free we assign it our new file */
607 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
608 strcpyW( newdos_name, full_name.short_name );
609 CurProfile->dos_name = newdos_name;
610 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
611 strcpyW( CurProfile->filename, filename );
613 /* Try to open the profile file, first in $HOME/.wine */
615 /* FIXME: this will need a more general solution */
616 strcpy( buffer, wine_get_config_dir() );
617 p = buffer + strlen(buffer);
619 *p = 0; /* make strlen() below happy */
620 name = strrchrW( newdos_name, '\\' ) + 1;
622 /* create a lower cased version of the name */
623 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
624 strcpyW(name_lwr, name);
626 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
627 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
628 HeapFree(GetProcessHeap(), 0, name_lwr);
630 if ((file = fopen( buffer, "r" )))
632 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
633 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
634 strcpy( CurProfile->unix_name, buffer );
638 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
639 strcpy( CurProfile->unix_name, full_name.long_name );
640 if ((file = fopen( full_name.long_name, "r" )))
641 TRACE("(%s): found it in %s\n",
642 debugstr_w(filename), full_name.long_name );
647 CurProfile->section = PROFILE_Load( file );
649 if(!stat(CurProfile->unix_name,&buf))
650 CurProfile->mtime=buf.st_mtime;
654 /* Does not exist yet, we will create it in PROFILE_FlushFile */
655 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
661 /***********************************************************************
664 * Returns all keys of a section.
665 * If return_values is TRUE, also include the corresponding values.
667 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
668 LPWSTR buffer, UINT len, BOOL return_values )
672 if(!buffer) return 0;
674 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
678 if (section->name[0] && !strcmpiW( section->name, section_name ))
681 for (key = section->key; key; key = key->next)
684 if (!*key->name) continue; /* Skip empty lines */
685 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
686 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
687 len -= strlenW(buffer) + 1;
688 buffer += strlenW(buffer) + 1;
691 if (return_values && key->value) {
693 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
694 len -= strlenW(buffer) + 1;
695 buffer += strlenW(buffer) + 1;
700 /*If either lpszSection or lpszKey is NULL and the supplied
701 destination buffer is too small to hold all the strings,
702 the last string is truncated and followed by two null characters.
703 In this case, the return value is equal to cchReturnBuffer
711 section = section->next;
713 buffer[0] = buffer[1] = '\0';
717 /* See GetPrivateProfileSectionNamesA for documentation */
718 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
722 PROFILESECTION *section;
733 section = CurProfile->section;
734 while ((section!=NULL)) {
735 if (section->name[0]) {
736 l = strlenW(section->name)+1;
739 strncpyW(buf, section->name, f-1);
746 strcpyW(buf, section->name);
750 section = section->next;
757 /***********************************************************************
760 * Get a profile string.
762 * Tests with GetPrivateProfileString16, W95a,
763 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
764 * section key_name def_val res buffer
765 * "set1" "1" "x" 43 [data]
766 * "set1" "1 " "x" 43 [data] (!)
767 * "set1" " 1 "' "x" 43 [data] (!)
768 * "set1" "" "x" 1 "x"
769 * "set1" "" "x " 1 "x" (!)
770 * "set1" "" " x " 3 " x" (!)
771 * "set1" NULL "x" 6 "1\02\03\0\0"
772 * "set1" "" "x" 1 "x"
773 * NULL "1" "x" 0 "" (!)
779 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
780 LPCWSTR def_val, LPWSTR buffer, UINT len )
782 PROFILEKEY *key = NULL;
783 static const WCHAR empty_strW[] = { 0 };
785 if(!buffer) return 0;
787 if (!def_val) def_val = empty_strW;
792 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
795 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
796 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
798 TRACE("(%s,%s,%s): returning %s\n",
799 debugstr_w(section), debugstr_w(key_name),
800 debugstr_w(def_val), debugstr_w(buffer) );
801 return strlenW( buffer );
803 /* no "else" here ! */
804 if (section && section[0])
806 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
807 if (!buffer[0]) /* no luck -> def_val */
809 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
810 ret = strlenW(buffer);
819 /***********************************************************************
822 * Set a profile string.
824 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
825 LPCWSTR value, BOOL create_always )
827 if (!key_name) /* Delete a whole section */
829 TRACE("(%s)\n", debugstr_w(section_name));
830 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
832 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
833 this is not an error on application's level.*/
835 else if (!value) /* Delete a key */
837 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
838 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
839 section_name, key_name );
840 return TRUE; /* same error handling as above */
842 else /* Set the key value */
844 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
845 key_name, TRUE, create_always );
846 TRACE("(%s,%s,%s):\n",
847 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
848 if (!key) return FALSE;
851 /* strip the leading spaces. We can safely strip \n\r and
852 * friends too, they should not happen here anyway. */
853 while (PROFILE_isspace(*value)) value++;
855 if (!strcmpW( key->value, value ))
857 TRACE(" no change needed\n" );
858 return TRUE; /* No change needed */
860 TRACE(" replacing %s\n", debugstr_w(key->value) );
861 HeapFree( GetProcessHeap(), 0, key->value );
863 else TRACE(" creating key\n" );
864 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
865 strcpyW( key->value, value );
866 CurProfile->changed = TRUE;
872 /********************* API functions **********************************/
874 /***********************************************************************
875 * GetProfileInt (KERNEL.57)
877 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
879 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
883 /***********************************************************************
884 * GetProfileIntA (KERNEL32.@)
886 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
888 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
891 /***********************************************************************
892 * GetProfileIntW (KERNEL32.@)
894 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
896 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
900 * if allow_section_name_copy is TRUE, allow the copying :
901 * - of Section names if 'section' is NULL
902 * - of Keys in a Section if 'entry' is NULL
903 * (see MSDN doc for GetPrivateProfileString)
905 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
906 LPCWSTR def_val, LPWSTR buffer,
907 UINT len, LPCWSTR filename,
908 BOOL allow_section_name_copy )
911 LPWSTR pDefVal = NULL;
916 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
917 debugstr_w(def_val), buffer, len, debugstr_w(filename));
919 /* strip any trailing ' ' of def_val. */
922 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
930 if (*p == ' ') /* ouch, contained trailing ' ' */
932 int len = (int)(p - def_val);
933 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
934 strncpyW(pDefVal, def_val, len);
939 pDefVal = (LPWSTR)def_val;
941 RtlEnterCriticalSection( &PROFILE_CritSect );
943 if (PROFILE_Open( filename )) {
944 if ((allow_section_name_copy) && (section == NULL))
945 ret = PROFILE_GetSectionNames(buffer, len);
947 /* PROFILE_GetString already handles the 'entry == NULL' case */
948 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
950 lstrcpynW( buffer, pDefVal, len );
951 ret = strlenW( buffer );
954 RtlLeaveCriticalSection( &PROFILE_CritSect );
956 if (pDefVal != def_val) /* allocated */
957 HeapFree(GetProcessHeap(), 0, pDefVal);
959 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
964 /***********************************************************************
965 * GetPrivateProfileString (KERNEL.128)
967 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
968 LPCSTR def_val, LPSTR buffer,
969 UINT16 len, LPCSTR filename )
971 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
975 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
976 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
977 else sectionW.Buffer = NULL;
978 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
979 else entryW.Buffer = NULL;
980 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
981 else def_valW.Buffer = NULL;
982 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
983 else filenameW.Buffer = NULL;
985 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
986 def_valW.Buffer, bufferW, len,
987 filenameW.Buffer, FALSE );
990 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
997 ret--; /* strip terminating 0 */
1000 RtlFreeUnicodeString(§ionW);
1001 RtlFreeUnicodeString(&entryW);
1002 RtlFreeUnicodeString(&def_valW);
1003 RtlFreeUnicodeString(&filenameW);
1004 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1008 /***********************************************************************
1009 * GetPrivateProfileStringA (KERNEL32.@)
1011 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1012 LPCSTR def_val, LPSTR buffer,
1013 UINT len, LPCSTR filename )
1015 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1019 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1020 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1021 else sectionW.Buffer = NULL;
1022 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1023 else entryW.Buffer = NULL;
1024 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1025 else def_valW.Buffer = NULL;
1026 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1027 else filenameW.Buffer = NULL;
1029 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1030 def_valW.Buffer, bufferW, len,
1034 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1041 ret--; /* strip terminating 0 */
1044 RtlFreeUnicodeString(§ionW);
1045 RtlFreeUnicodeString(&entryW);
1046 RtlFreeUnicodeString(&def_valW);
1047 RtlFreeUnicodeString(&filenameW);
1048 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1052 /***********************************************************************
1053 * GetPrivateProfileStringW (KERNEL32.@)
1055 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1056 LPCWSTR def_val, LPWSTR buffer,
1057 UINT len, LPCWSTR filename )
1059 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1060 buffer, len, filename, TRUE );
1063 /***********************************************************************
1064 * GetProfileString (KERNEL.58)
1066 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1067 LPSTR buffer, UINT16 len )
1069 return GetPrivateProfileString16( section, entry, def_val,
1070 buffer, len, "win.ini" );
1073 /***********************************************************************
1074 * GetProfileStringA (KERNEL32.@)
1076 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1077 LPSTR buffer, UINT len )
1079 return GetPrivateProfileStringA( section, entry, def_val,
1080 buffer, len, "win.ini" );
1083 /***********************************************************************
1084 * GetProfileStringW (KERNEL32.@)
1086 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1087 LPCWSTR def_val, LPWSTR buffer, UINT len )
1089 return GetPrivateProfileStringW( section, entry, def_val,
1090 buffer, len, wininiW );
1093 /***********************************************************************
1094 * WriteProfileString (KERNEL.59)
1096 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1099 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1102 /***********************************************************************
1103 * WriteProfileStringA (KERNEL32.@)
1105 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1108 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1111 /***********************************************************************
1112 * WriteProfileStringW (KERNEL32.@)
1114 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1117 return WritePrivateProfileStringW( section, entry, string, wininiW );
1121 /***********************************************************************
1122 * GetPrivateProfileInt (KERNEL.127)
1124 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1125 INT16 def_val, LPCSTR filename )
1127 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1128 * here, but Win98SE doesn't care about this at all, so I deleted it.
1129 * AFAIR versions prior to Win9x had these limits, though. */
1130 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1133 /***********************************************************************
1134 * GetPrivateProfileIntW (KERNEL32.@)
1136 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1137 INT def_val, LPCWSTR filename )
1140 UNICODE_STRING bufferW;
1144 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1145 buffer, sizeof(buffer)/sizeof(WCHAR),
1149 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1151 /* FIXME: if entry can be found but it's empty, then Win16 is
1152 * supposed to return 0 instead of def_val ! Difficult/problematic
1153 * to implement (every other failure also returns zero buffer),
1154 * thus wait until testing framework avail for making sure nothing
1155 * else gets broken that way. */
1156 if (!buffer[0]) return (UINT)def_val;
1158 RtlInitUnicodeString( &bufferW, buffer );
1159 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1163 /***********************************************************************
1164 * GetPrivateProfileIntA (KERNEL32.@)
1166 * FIXME: rewrite using unicode
1168 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1169 INT def_val, LPCSTR filename )
1171 UNICODE_STRING entryW, filenameW, sectionW;
1173 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1174 else entryW.Buffer = NULL;
1175 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1176 else filenameW.Buffer = NULL;
1177 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1178 else sectionW.Buffer = NULL;
1179 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1181 RtlFreeUnicodeString(§ionW);
1182 RtlFreeUnicodeString(&filenameW);
1183 RtlFreeUnicodeString(&entryW);
1187 /***********************************************************************
1188 * GetPrivateProfileSection (KERNEL.418)
1190 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1191 UINT16 len, LPCSTR filename )
1193 return GetPrivateProfileSectionA( section, buffer, len, filename );
1196 /***********************************************************************
1197 * GetPrivateProfileSectionW (KERNEL32.@)
1199 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1200 DWORD len, LPCWSTR filename )
1204 RtlEnterCriticalSection( &PROFILE_CritSect );
1206 if (PROFILE_Open( filename ))
1207 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1209 RtlLeaveCriticalSection( &PROFILE_CritSect );
1214 /***********************************************************************
1215 * GetPrivateProfileSectionA (KERNEL32.@)
1217 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1218 DWORD len, LPCSTR filename )
1220 UNICODE_STRING sectionW, filenameW;
1224 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1225 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1226 else sectionW.Buffer = NULL;
1227 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1228 else filenameW.Buffer = NULL;
1230 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1233 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1249 RtlFreeUnicodeString(§ionW);
1250 RtlFreeUnicodeString(&filenameW);
1251 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1255 /***********************************************************************
1256 * GetProfileSection (KERNEL.419)
1258 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1260 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1263 /***********************************************************************
1264 * GetProfileSectionA (KERNEL32.@)
1266 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1268 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1271 /***********************************************************************
1272 * GetProfileSectionW (KERNEL32.@)
1274 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1276 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1280 /***********************************************************************
1281 * WritePrivateProfileString (KERNEL.129)
1283 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1284 LPCSTR string, LPCSTR filename )
1286 return WritePrivateProfileStringA(section,entry,string,filename);
1289 /***********************************************************************
1290 * WritePrivateProfileStringW (KERNEL32.@)
1292 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1293 LPCWSTR string, LPCWSTR filename )
1297 RtlEnterCriticalSection( &PROFILE_CritSect );
1299 if (PROFILE_Open( filename ))
1301 if (!section && !entry && !string) /* documented "file flush" case */
1303 PROFILE_FlushFile();
1304 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1308 FIXME("(NULL?,%s,%s,%s)?\n",
1309 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1311 ret = PROFILE_SetString( section, entry, string, FALSE);
1312 PROFILE_FlushFile();
1317 RtlLeaveCriticalSection( &PROFILE_CritSect );
1321 /***********************************************************************
1322 * WritePrivateProfileStringA (KERNEL32.@)
1324 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1325 LPCSTR string, LPCSTR filename )
1327 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1330 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1331 else sectionW.Buffer = NULL;
1332 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1333 else entryW.Buffer = NULL;
1334 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1335 else stringW.Buffer = NULL;
1336 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1337 else filenameW.Buffer = NULL;
1339 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1340 stringW.Buffer, filenameW.Buffer);
1341 RtlFreeUnicodeString(§ionW);
1342 RtlFreeUnicodeString(&entryW);
1343 RtlFreeUnicodeString(&stringW);
1344 RtlFreeUnicodeString(&filenameW);
1348 /***********************************************************************
1349 * WritePrivateProfileSection (KERNEL.416)
1351 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1352 LPCSTR string, LPCSTR filename )
1354 return WritePrivateProfileSectionA( section, string, filename );
1357 /***********************************************************************
1358 * WritePrivateProfileSectionW (KERNEL32.@)
1360 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1361 LPCWSTR string, LPCWSTR filename )
1366 RtlEnterCriticalSection( &PROFILE_CritSect );
1368 if (PROFILE_Open( filename )) {
1369 if (!section && !string)
1370 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1371 else if (!string) {/* delete the named section*/
1372 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1373 PROFILE_FlushFile();
1375 PROFILE_DeleteAllKeys(section);
1378 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1379 strcpyW( buf, string );
1380 if((p = strchrW( buf, '='))) {
1382 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1384 HeapFree( GetProcessHeap(), 0, buf );
1385 string += strlenW(string)+1;
1387 PROFILE_FlushFile();
1391 RtlLeaveCriticalSection( &PROFILE_CritSect );
1395 /***********************************************************************
1396 * WritePrivateProfileSectionA (KERNEL32.@)
1398 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1399 LPCSTR string, LPCSTR filename)
1402 UNICODE_STRING sectionW, filenameW;
1411 while(*p) p += strlen(p) + 1;
1412 lenA = p - string + 1;
1413 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1414 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1415 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1417 else stringW = NULL;
1418 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1419 else sectionW.Buffer = NULL;
1420 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1421 else filenameW.Buffer = NULL;
1423 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1425 HeapFree(GetProcessHeap(), 0, stringW);
1426 RtlFreeUnicodeString(§ionW);
1427 RtlFreeUnicodeString(&filenameW);
1431 /***********************************************************************
1432 * WriteProfileSection (KERNEL.417)
1434 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1436 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1439 /***********************************************************************
1440 * WriteProfileSectionA (KERNEL32.@)
1442 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1445 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1448 /***********************************************************************
1449 * WriteProfileSectionW (KERNEL32.@)
1451 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1453 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1456 /***********************************************************************
1457 * GetPrivateProfileSectionNames (KERNEL.143)
1459 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1462 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1466 /***********************************************************************
1467 * GetProfileSectionNames (KERNEL.142)
1469 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1472 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1476 /***********************************************************************
1477 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1479 * Returns the section names contained in the specified file.
1480 * FIXME: Where do we find this file when the path is relative?
1481 * The section names are returned as a list of strings with an extra
1482 * '\0' to mark the end of the list. Except for that the behavior
1483 * depends on the Windows version.
1486 * - if the buffer is 0 or 1 character long then it is as if it was of
1488 * - otherwise, if the buffer is to small only the section names that fit
1490 * - note that this means if the buffer was to small to return even just
1491 * the first section name then a single '\0' will be returned.
1492 * - the return value is the number of characters written in the buffer,
1493 * except if the buffer was too smal in which case len-2 is returned
1496 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1497 * '\0' and the return value is 0
1498 * - otherwise if the buffer is too small then the first section name that
1499 * does not fit is truncated so that the string list can be terminated
1500 * correctly (double '\0')
1501 * - the return value is the number of characters written in the buffer
1502 * except for the trailing '\0'. If the buffer is too small, then the
1503 * return value is len-2
1504 * - Win2000 has a bug that triggers when the section names and the
1505 * trailing '\0' fit exactly in the buffer. In that case the trailing
1508 * Wine implements the observed Win2000 behavior (except for the bug).
1510 * Note that when the buffer is big enough then the return value may be any
1511 * value between 1 and len-1 (or len in Win95), including len-2.
1513 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1518 RtlEnterCriticalSection( &PROFILE_CritSect );
1520 if (PROFILE_Open( filename ))
1521 ret = PROFILE_GetSectionNames(buffer, size);
1523 RtlLeaveCriticalSection( &PROFILE_CritSect );
1529 /***********************************************************************
1530 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1532 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1535 UNICODE_STRING filenameW;
1539 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1540 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1541 else filenameW.Buffer = NULL;
1543 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1546 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1554 RtlFreeUnicodeString(&filenameW);
1555 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1559 /***********************************************************************
1560 * GetPrivateProfileStruct (KERNEL.407)
1562 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1563 LPVOID buf, UINT16 len, LPCSTR filename)
1565 return GetPrivateProfileStructA( section, key, buf, len, filename );
1568 /***********************************************************************
1569 * GetPrivateProfileStructW (KERNEL32.@)
1571 * Should match Win95's behaviour pretty much
1573 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1574 LPVOID buf, UINT len, LPCWSTR filename)
1578 RtlEnterCriticalSection( &PROFILE_CritSect );
1580 if (PROFILE_Open( filename )) {
1581 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1583 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1584 if (((strlenW(k->value) - 2) / 2) == len)
1591 end = k->value + strlenW(k->value); /* -> '\0' */
1592 /* check for invalid chars in ASCII coded hex string */
1593 for (p=k->value; p < end; p++)
1597 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1598 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1605 BOOL highnibble = TRUE;
1607 LPBYTE binbuf = (LPBYTE)buf;
1609 end -= 2; /* don't include checksum in output data */
1610 /* translate ASCII hex format into binary data */
1611 for (p=k->value; p < end; p++)
1615 (c - 'A' + 10) : (c - '0');
1622 *binbuf++ = b; /* feed binary data into output */
1623 chksum += b; /* calculate checksum */
1625 highnibble ^= 1; /* toggle */
1627 /* retrieve stored checksum value */
1629 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1631 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1632 if (b == (chksum & 0xff)) /* checksums match ? */
1638 RtlLeaveCriticalSection( &PROFILE_CritSect );
1643 /***********************************************************************
1644 * GetPrivateProfileStructA (KERNEL32.@)
1646 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1647 LPVOID buffer, UINT len, LPCSTR filename)
1649 UNICODE_STRING sectionW, keyW, filenameW;
1652 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1653 else sectionW.Buffer = NULL;
1654 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1655 else keyW.Buffer = NULL;
1656 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1657 else filenameW.Buffer = NULL;
1659 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1661 /* Do not translate binary data. */
1663 RtlFreeUnicodeString(§ionW);
1664 RtlFreeUnicodeString(&keyW);
1665 RtlFreeUnicodeString(&filenameW);
1671 /***********************************************************************
1672 * WritePrivateProfileStruct (KERNEL.406)
1674 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1675 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1677 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1680 /***********************************************************************
1681 * WritePrivateProfileStructW (KERNEL32.@)
1683 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1684 LPVOID buf, UINT bufsize, LPCWSTR filename)
1688 LPWSTR outstring, p;
1691 if (!section && !key && !buf) /* flush the cache */
1692 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1694 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1695 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1697 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1698 *p++ = hex[*binbuf >> 4];
1699 *p++ = hex[*binbuf & 0xf];
1702 /* checksum is sum & 0xff */
1703 *p++ = hex[(sum & 0xf0) >> 4];
1704 *p++ = hex[sum & 0xf];
1707 RtlEnterCriticalSection( &PROFILE_CritSect );
1709 if (PROFILE_Open( filename )) {
1710 ret = PROFILE_SetString( section, key, outstring, FALSE);
1711 PROFILE_FlushFile();
1714 RtlLeaveCriticalSection( &PROFILE_CritSect );
1716 HeapFree( GetProcessHeap(), 0, outstring );
1721 /***********************************************************************
1722 * WritePrivateProfileStructA (KERNEL32.@)
1724 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1725 LPVOID buf, UINT bufsize, LPCSTR filename)
1727 UNICODE_STRING sectionW, keyW, filenameW;
1730 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1731 else sectionW.Buffer = NULL;
1732 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1733 else keyW.Buffer = NULL;
1734 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1735 else filenameW.Buffer = NULL;
1737 /* Do not translate binary data. */
1738 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1741 RtlFreeUnicodeString(§ionW);
1742 RtlFreeUnicodeString(&keyW);
1743 RtlFreeUnicodeString(&filenameW);
1748 /***********************************************************************
1749 * WriteOutProfiles (KERNEL.315)
1751 void WINAPI WriteOutProfiles16(void)
1753 RtlEnterCriticalSection( &PROFILE_CritSect );
1754 PROFILE_FlushFile();
1755 RtlLeaveCriticalSection( &PROFILE_CritSect );
1758 /***********************************************************************
1759 * CloseProfileUserMapping (KERNEL32.@)
1761 BOOL WINAPI CloseProfileUserMapping(void) {
1762 FIXME("(), stub!\n");
1763 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);