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"
46 #include "wine/unicode.h"
47 #include "wine/server.h"
48 #include "wine/library.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(profile);
53 typedef struct tagPROFILEKEY
56 struct tagPROFILEKEY *next;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY *key;
63 struct tagPROFILESECTION *next;
71 PROFILESECTION *section;
79 #define N_CACHED_PROFILES 10
81 /* Cached profile files */
82 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
84 #define CurProfile (MRUProfile[0])
86 #define PROFILE_MAX_LINE_LEN 1024
88 /* Check for comments in profile */
89 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
91 static const WCHAR emptystringW[] = {0};
92 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
94 static CRITICAL_SECTION PROFILE_CritSect;
95 static CRITICAL_SECTION_DEBUG critsect_debug =
97 0, 0, &PROFILE_CritSect,
98 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
99 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
101 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
103 static const char hex[16] = "0123456789ABCDEF";
105 /***********************************************************************
108 * Copy the content of an entry into a buffer, removing quotes, and possibly
109 * translating environment variables.
111 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
118 if (strip_quote && ((*value == '\'') || (*value == '\"')))
120 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
123 lstrcpynW( buffer, value, len );
124 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
128 /***********************************************************************
131 * Save a profile tree to a file.
133 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
136 char buffer[PROFILE_MAX_LINE_LEN];
138 for ( ; section; section = section->next)
140 if (section->name[0])
142 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
143 fprintf( file, "\r\n[%s]\r\n", buffer );
145 for (key = section->key; key; key = key->next)
147 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
148 fprintf( file, "%s", buffer );
151 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
152 fprintf( file, "=%s", buffer );
154 fprintf( file, "\r\n" );
160 /***********************************************************************
163 * Free a profile tree.
165 static void PROFILE_Free( PROFILESECTION *section )
167 PROFILESECTION *next_section;
168 PROFILEKEY *key, *next_key;
170 for ( ; section; section = next_section)
172 for (key = section->key; key; key = next_key)
174 next_key = key->next;
175 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
176 HeapFree( GetProcessHeap(), 0, key );
178 next_section = section->next;
179 HeapFree( GetProcessHeap(), 0, section );
183 static inline int PROFILE_isspace(char c)
185 if (isspace(c)) return 1;
186 if (c=='\r' || c==0x1a) return 1;
187 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
192 /***********************************************************************
195 * Load a profile tree from a file.
197 static PROFILESECTION *PROFILE_Load( FILE *file )
199 char buffer[PROFILE_MAX_LINE_LEN];
202 PROFILESECTION *section, *first_section;
203 PROFILESECTION **next_section;
204 PROFILEKEY *key, *prev_key, **next_key;
206 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
207 if(first_section == NULL) return NULL;
208 first_section->name[0] = 0;
209 first_section->key = NULL;
210 first_section->next = NULL;
211 next_section = &first_section->next;
212 next_key = &first_section->key;
215 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
219 while (*p && PROFILE_isspace(*p)) p++;
220 if (*p == '[') /* section start */
222 if (!(p2 = strrchr( p, ']' )))
224 WARN("Invalid section header at line %d: '%s'\n",
232 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
234 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
236 section->next = NULL;
237 *next_section = section;
238 next_section = §ion->next;
239 next_key = §ion->key;
242 TRACE("New section: %s\n", debugstr_w(section->name));
249 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
251 if ((p2 = strchr( p, '=' )) != NULL)
254 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
256 while (*p2 && PROFILE_isspace(*p2)) p2++;
259 if(*p || !prev_key || *prev_key->name)
262 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
263 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
266 len = strlen(p2) + 1;
267 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
268 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
270 else key->value = NULL;
274 next_key = &key->next;
277 TRACE("New key: name=%s, value=%s\n",
278 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
281 return first_section;
285 /***********************************************************************
286 * PROFILE_DeleteSection
288 * Delete a section from a profile tree.
290 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
294 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
296 PROFILESECTION *to_del = *section;
297 *section = to_del->next;
299 PROFILE_Free( to_del );
302 section = &(*section)->next;
308 /***********************************************************************
311 * Delete a key from a profile tree.
313 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
314 LPCWSTR section_name, LPCWSTR key_name )
318 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
320 PROFILEKEY **key = &(*section)->key;
323 if (!strcmpiW( (*key)->name, key_name ))
325 PROFILEKEY *to_del = *key;
327 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
328 HeapFree( GetProcessHeap(), 0, to_del );
334 section = &(*section)->next;
340 /***********************************************************************
341 * PROFILE_DeleteAllKeys
343 * Delete all keys from a profile tree.
345 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
347 PROFILESECTION **section= &CurProfile->section;
350 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
352 PROFILEKEY **key = &(*section)->key;
355 PROFILEKEY *to_del = *key;
357 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
358 HeapFree( GetProcessHeap(), 0, to_del );
359 CurProfile->changed =TRUE;
362 section = &(*section)->next;
367 /***********************************************************************
370 * Find a key in a profile tree, optionally creating it.
372 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
373 LPCWSTR key_name, BOOL create, BOOL create_always )
378 while (PROFILE_isspace(*section_name)) section_name++;
379 p = section_name + strlenW(section_name) - 1;
380 while ((p > section_name) && PROFILE_isspace(*p)) p--;
381 seclen = p - section_name + 1;
383 while (PROFILE_isspace(*key_name)) key_name++;
384 p = key_name + strlenW(key_name) - 1;
385 while ((p > key_name) && PROFILE_isspace(*p)) p--;
386 keylen = p - key_name + 1;
390 if ( ((*section)->name[0])
391 && (!(strncmpiW( (*section)->name, section_name, seclen )))
392 && (((*section)->name)[seclen] == '\0') )
394 PROFILEKEY **key = &(*section)->key;
398 /* If create_always is FALSE then we check if the keyname
399 * already exists. Otherwise we add it regardless of its
400 * existence, to allow keys to be added more than once in
405 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
406 && (((*key)->name)[keylen] == '\0') )
411 if (!create) return NULL;
412 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
414 strcpyW( (*key)->name, key_name );
415 (*key)->value = NULL;
419 section = &(*section)->next;
421 if (!create) return NULL;
422 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
423 if(*section == NULL) return NULL;
424 strcpyW( (*section)->name, section_name );
425 (*section)->next = NULL;
426 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
427 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
429 HeapFree(GetProcessHeap(), 0, *section);
432 strcpyW( (*section)->key->name, key_name );
433 (*section)->key->value = NULL;
434 (*section)->key->next = NULL;
435 return (*section)->key;
439 /***********************************************************************
442 * Flush the current profile to disk if changed.
444 static BOOL PROFILE_FlushFile(void)
446 char *p, buffer[MAX_PATHNAME_LEN];
447 const char *unix_name;
453 WARN("No current profile!\n");
457 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
458 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
460 WCHAR *name, *name_lwr;
461 /* Try to create it in $HOME/.wine */
462 /* FIXME: this will need a more general solution */
463 strcpy( buffer, wine_get_config_dir() );
464 p = buffer + strlen(buffer);
466 *p = 0; /* make strlen() below happy */
467 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
469 /* create a lower cased version of the name */
470 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
471 strcpyW(name_lwr, name);
473 WideCharToMultiByte(CP_UNIXCP, 0, name_lwr, -1,
474 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
475 HeapFree(GetProcessHeap(), 0, name_lwr);
477 file = fopen( buffer, "w" );
483 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
487 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
488 PROFILE_Save( file, CurProfile->section );
490 CurProfile->changed = FALSE;
491 if(!stat(unix_name,&buf))
492 CurProfile->mtime=buf.st_mtime;
497 /***********************************************************************
498 * PROFILE_ReleaseFile
500 * Flush the current profile to disk and remove it from the cache.
502 static void PROFILE_ReleaseFile(void)
505 PROFILE_Free( CurProfile->section );
506 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
507 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
508 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
509 CurProfile->changed = FALSE;
510 CurProfile->section = NULL;
511 CurProfile->dos_name = NULL;
512 CurProfile->unix_name = NULL;
513 CurProfile->filename = NULL;
514 CurProfile->mtime = 0;
518 /***********************************************************************
521 * Open a profile file, checking the cached file first.
523 static BOOL PROFILE_Open( LPCWSTR filename )
525 DOS_FULL_NAME full_name;
526 char buffer[MAX_PATHNAME_LEN];
528 WCHAR *name, *name_lwr;
533 PROFILE *tempProfile;
535 /* First time around */
538 for(i=0;i<N_CACHED_PROFILES;i++)
540 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
541 if(MRUProfile[i] == NULL) break;
542 MRUProfile[i]->changed=FALSE;
543 MRUProfile[i]->section=NULL;
544 MRUProfile[i]->dos_name=NULL;
545 MRUProfile[i]->unix_name=NULL;
546 MRUProfile[i]->filename=NULL;
547 MRUProfile[i]->mtime=0;
550 /* Check for a match */
552 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
553 strchrW( filename, ':' ))
555 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
559 static const WCHAR bkslashW[] = {'\\',0};
560 WCHAR windirW[MAX_PATH];
562 GetWindowsDirectoryW( windirW, MAX_PATH );
563 strcatW( windirW, bkslashW );
564 strcatW( windirW, filename );
565 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
568 for(i=0;i<N_CACHED_PROFILES;i++)
570 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
571 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
576 tempProfile=MRUProfile[i];
578 MRUProfile[j]=MRUProfile[j-1];
579 CurProfile=tempProfile;
581 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
582 TRACE("(%s): already opened (mru=%d)\n",
583 debugstr_w(filename), i );
585 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
586 debugstr_w(filename), i );
591 /* Flush the old current profile */
594 /* Make the oldest profile the current one only in order to get rid of it */
595 if(i==N_CACHED_PROFILES)
597 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
598 for(i=N_CACHED_PROFILES-1;i>0;i--)
599 MRUProfile[i]=MRUProfile[i-1];
600 CurProfile=tempProfile;
602 if(CurProfile->filename) PROFILE_ReleaseFile();
604 /* OK, now that CurProfile is definitely free we assign it our new file */
605 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
606 strcpyW( newdos_name, full_name.short_name );
607 CurProfile->dos_name = newdos_name;
608 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
609 strcpyW( CurProfile->filename, filename );
611 /* Try to open the profile file, first in $HOME/.wine */
613 /* FIXME: this will need a more general solution */
614 strcpy( buffer, wine_get_config_dir() );
615 p = buffer + strlen(buffer);
617 *p = 0; /* make strlen() below happy */
618 name = strrchrW( newdos_name, '\\' ) + 1;
620 /* create a lower cased version of the name */
621 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
622 strcpyW(name_lwr, name);
624 WideCharToMultiByte(CP_UNIXCP, 0, name_lwr, -1,
625 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
626 HeapFree(GetProcessHeap(), 0, name_lwr);
628 if ((file = fopen( buffer, "r" )))
630 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
631 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
632 strcpy( CurProfile->unix_name, buffer );
636 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
637 strcpy( CurProfile->unix_name, full_name.long_name );
638 if ((file = fopen( full_name.long_name, "r" )))
639 TRACE("(%s): found it in %s\n",
640 debugstr_w(filename), full_name.long_name );
645 CurProfile->section = PROFILE_Load( file );
647 if(!stat(CurProfile->unix_name,&buf))
648 CurProfile->mtime=buf.st_mtime;
652 /* Does not exist yet, we will create it in PROFILE_FlushFile */
653 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
659 /***********************************************************************
662 * Returns all keys of a section.
663 * If return_values is TRUE, also include the corresponding values.
665 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
666 LPWSTR buffer, UINT len, BOOL return_values )
670 if(!buffer) return 0;
672 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
676 if (section->name[0] && !strcmpiW( section->name, section_name ))
679 for (key = section->key; key; key = key->next)
682 if (!*key->name) continue; /* Skip empty lines */
683 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
684 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
685 len -= strlenW(buffer) + 1;
686 buffer += strlenW(buffer) + 1;
689 if (return_values && key->value) {
691 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
692 len -= strlenW(buffer) + 1;
693 buffer += strlenW(buffer) + 1;
698 /*If either lpszSection or lpszKey is NULL and the supplied
699 destination buffer is too small to hold all the strings,
700 the last string is truncated and followed by two null characters.
701 In this case, the return value is equal to cchReturnBuffer
709 section = section->next;
711 buffer[0] = buffer[1] = '\0';
715 /* See GetPrivateProfileSectionNamesA for documentation */
716 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
720 PROFILESECTION *section;
731 section = CurProfile->section;
732 while ((section!=NULL)) {
733 if (section->name[0]) {
734 l = strlenW(section->name)+1;
737 strncpyW(buf, section->name, f-1);
744 strcpyW(buf, section->name);
748 section = section->next;
755 /***********************************************************************
758 * Get a profile string.
760 * Tests with GetPrivateProfileString16, W95a,
761 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
762 * section key_name def_val res buffer
763 * "set1" "1" "x" 43 [data]
764 * "set1" "1 " "x" 43 [data] (!)
765 * "set1" " 1 "' "x" 43 [data] (!)
766 * "set1" "" "x" 1 "x"
767 * "set1" "" "x " 1 "x" (!)
768 * "set1" "" " x " 3 " x" (!)
769 * "set1" NULL "x" 6 "1\02\03\0\0"
770 * "set1" "" "x" 1 "x"
771 * NULL "1" "x" 0 "" (!)
777 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
778 LPCWSTR def_val, LPWSTR buffer, UINT len )
780 PROFILEKEY *key = NULL;
781 static const WCHAR empty_strW[] = { 0 };
783 if(!buffer) return 0;
785 if (!def_val) def_val = empty_strW;
790 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
793 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
794 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
796 TRACE("(%s,%s,%s): returning %s\n",
797 debugstr_w(section), debugstr_w(key_name),
798 debugstr_w(def_val), debugstr_w(buffer) );
799 return strlenW( buffer );
801 /* no "else" here ! */
802 if (section && section[0])
804 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
805 if (!buffer[0]) /* no luck -> def_val */
807 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
808 ret = strlenW(buffer);
817 /***********************************************************************
820 * Set a profile string.
822 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
823 LPCWSTR value, BOOL create_always )
825 if (!key_name) /* Delete a whole section */
827 TRACE("(%s)\n", debugstr_w(section_name));
828 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
830 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
831 this is not an error on application's level.*/
833 else if (!value) /* Delete a key */
835 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
836 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
837 section_name, key_name );
838 return TRUE; /* same error handling as above */
840 else /* Set the key value */
842 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
843 key_name, TRUE, create_always );
844 TRACE("(%s,%s,%s):\n",
845 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
846 if (!key) return FALSE;
849 /* strip the leading spaces. We can safely strip \n\r and
850 * friends too, they should not happen here anyway. */
851 while (PROFILE_isspace(*value)) value++;
853 if (!strcmpW( key->value, value ))
855 TRACE(" no change needed\n" );
856 return TRUE; /* No change needed */
858 TRACE(" replacing %s\n", debugstr_w(key->value) );
859 HeapFree( GetProcessHeap(), 0, key->value );
861 else TRACE(" creating key\n" );
862 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
863 strcpyW( key->value, value );
864 CurProfile->changed = TRUE;
870 /********************* API functions **********************************/
872 /***********************************************************************
873 * GetProfileInt (KERNEL.57)
875 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
877 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
881 /***********************************************************************
882 * GetProfileIntA (KERNEL32.@)
884 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
886 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
889 /***********************************************************************
890 * GetProfileIntW (KERNEL32.@)
892 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
894 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
898 * if allow_section_name_copy is TRUE, allow the copying :
899 * - of Section names if 'section' is NULL
900 * - of Keys in a Section if 'entry' is NULL
901 * (see MSDN doc for GetPrivateProfileString)
903 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
904 LPCWSTR def_val, LPWSTR buffer,
905 UINT len, LPCWSTR filename,
906 BOOL allow_section_name_copy )
909 LPWSTR pDefVal = NULL;
914 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
915 debugstr_w(def_val), buffer, len, debugstr_w(filename));
917 /* strip any trailing ' ' of def_val. */
920 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
928 if (*p == ' ') /* ouch, contained trailing ' ' */
930 int len = (int)(p - def_val);
931 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
932 strncpyW(pDefVal, def_val, len);
937 pDefVal = (LPWSTR)def_val;
939 RtlEnterCriticalSection( &PROFILE_CritSect );
941 if (PROFILE_Open( filename )) {
942 if ((allow_section_name_copy) && (section == NULL))
943 ret = PROFILE_GetSectionNames(buffer, len);
945 /* PROFILE_GetString already handles the 'entry == NULL' case */
946 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
948 lstrcpynW( buffer, pDefVal, len );
949 ret = strlenW( buffer );
952 RtlLeaveCriticalSection( &PROFILE_CritSect );
954 if (pDefVal != def_val) /* allocated */
955 HeapFree(GetProcessHeap(), 0, pDefVal);
957 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
962 /***********************************************************************
963 * GetPrivateProfileString (KERNEL.128)
965 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
966 LPCSTR def_val, LPSTR buffer,
967 UINT16 len, LPCSTR filename )
969 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
973 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
974 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
975 else sectionW.Buffer = NULL;
976 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
977 else entryW.Buffer = NULL;
978 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
979 else def_valW.Buffer = NULL;
980 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
981 else filenameW.Buffer = NULL;
983 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
984 def_valW.Buffer, bufferW, len,
985 filenameW.Buffer, FALSE );
988 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
995 ret--; /* strip terminating 0 */
998 RtlFreeUnicodeString(§ionW);
999 RtlFreeUnicodeString(&entryW);
1000 RtlFreeUnicodeString(&def_valW);
1001 RtlFreeUnicodeString(&filenameW);
1002 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1006 /***********************************************************************
1007 * GetPrivateProfileStringA (KERNEL32.@)
1009 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1010 LPCSTR def_val, LPSTR buffer,
1011 UINT len, LPCSTR filename )
1013 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1017 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1018 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1019 else sectionW.Buffer = NULL;
1020 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1021 else entryW.Buffer = NULL;
1022 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1023 else def_valW.Buffer = NULL;
1024 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1025 else filenameW.Buffer = NULL;
1027 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1028 def_valW.Buffer, bufferW, len,
1032 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1039 ret--; /* strip terminating 0 */
1042 RtlFreeUnicodeString(§ionW);
1043 RtlFreeUnicodeString(&entryW);
1044 RtlFreeUnicodeString(&def_valW);
1045 RtlFreeUnicodeString(&filenameW);
1046 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1050 /***********************************************************************
1051 * GetPrivateProfileStringW (KERNEL32.@)
1053 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1054 LPCWSTR def_val, LPWSTR buffer,
1055 UINT len, LPCWSTR filename )
1057 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1058 buffer, len, filename, TRUE );
1061 /***********************************************************************
1062 * GetProfileString (KERNEL.58)
1064 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1065 LPSTR buffer, UINT16 len )
1067 return GetPrivateProfileString16( section, entry, def_val,
1068 buffer, len, "win.ini" );
1071 /***********************************************************************
1072 * GetProfileStringA (KERNEL32.@)
1074 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1075 LPSTR buffer, UINT len )
1077 return GetPrivateProfileStringA( section, entry, def_val,
1078 buffer, len, "win.ini" );
1081 /***********************************************************************
1082 * GetProfileStringW (KERNEL32.@)
1084 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1085 LPCWSTR def_val, LPWSTR buffer, UINT len )
1087 return GetPrivateProfileStringW( section, entry, def_val,
1088 buffer, len, wininiW );
1091 /***********************************************************************
1092 * WriteProfileString (KERNEL.59)
1094 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1097 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1100 /***********************************************************************
1101 * WriteProfileStringA (KERNEL32.@)
1103 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1106 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1109 /***********************************************************************
1110 * WriteProfileStringW (KERNEL32.@)
1112 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1115 return WritePrivateProfileStringW( section, entry, string, wininiW );
1119 /***********************************************************************
1120 * GetPrivateProfileInt (KERNEL.127)
1122 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1123 INT16 def_val, LPCSTR filename )
1125 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1126 * here, but Win98SE doesn't care about this at all, so I deleted it.
1127 * AFAIR versions prior to Win9x had these limits, though. */
1128 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1131 /***********************************************************************
1132 * GetPrivateProfileIntW (KERNEL32.@)
1134 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1135 INT def_val, LPCWSTR filename )
1138 UNICODE_STRING bufferW;
1142 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1143 buffer, sizeof(buffer)/sizeof(WCHAR),
1147 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1149 /* FIXME: if entry can be found but it's empty, then Win16 is
1150 * supposed to return 0 instead of def_val ! Difficult/problematic
1151 * to implement (every other failure also returns zero buffer),
1152 * thus wait until testing framework avail for making sure nothing
1153 * else gets broken that way. */
1154 if (!buffer[0]) return (UINT)def_val;
1156 RtlInitUnicodeString( &bufferW, buffer );
1157 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1161 /***********************************************************************
1162 * GetPrivateProfileIntA (KERNEL32.@)
1164 * FIXME: rewrite using unicode
1166 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1167 INT def_val, LPCSTR filename )
1169 UNICODE_STRING entryW, filenameW, sectionW;
1171 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1172 else entryW.Buffer = NULL;
1173 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1174 else filenameW.Buffer = NULL;
1175 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1176 else sectionW.Buffer = NULL;
1177 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1179 RtlFreeUnicodeString(§ionW);
1180 RtlFreeUnicodeString(&filenameW);
1181 RtlFreeUnicodeString(&entryW);
1185 /***********************************************************************
1186 * GetPrivateProfileSection (KERNEL.418)
1188 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1189 UINT16 len, LPCSTR filename )
1191 return GetPrivateProfileSectionA( section, buffer, len, filename );
1194 /***********************************************************************
1195 * GetPrivateProfileSectionW (KERNEL32.@)
1197 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1198 DWORD len, LPCWSTR filename )
1202 RtlEnterCriticalSection( &PROFILE_CritSect );
1204 if (PROFILE_Open( filename ))
1205 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1207 RtlLeaveCriticalSection( &PROFILE_CritSect );
1212 /***********************************************************************
1213 * GetPrivateProfileSectionA (KERNEL32.@)
1215 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1216 DWORD len, LPCSTR filename )
1218 UNICODE_STRING sectionW, filenameW;
1222 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1223 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1224 else sectionW.Buffer = NULL;
1225 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1226 else filenameW.Buffer = NULL;
1228 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1231 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1247 RtlFreeUnicodeString(§ionW);
1248 RtlFreeUnicodeString(&filenameW);
1249 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1253 /***********************************************************************
1254 * GetProfileSection (KERNEL.419)
1256 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1258 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1261 /***********************************************************************
1262 * GetProfileSectionA (KERNEL32.@)
1264 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1266 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1269 /***********************************************************************
1270 * GetProfileSectionW (KERNEL32.@)
1272 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1274 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1278 /***********************************************************************
1279 * WritePrivateProfileString (KERNEL.129)
1281 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1282 LPCSTR string, LPCSTR filename )
1284 return WritePrivateProfileStringA(section,entry,string,filename);
1287 /***********************************************************************
1288 * WritePrivateProfileStringW (KERNEL32.@)
1290 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1291 LPCWSTR string, LPCWSTR filename )
1295 RtlEnterCriticalSection( &PROFILE_CritSect );
1297 if (PROFILE_Open( filename ))
1299 if (!section && !entry && !string) /* documented "file flush" case */
1301 PROFILE_FlushFile();
1302 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1306 FIXME("(NULL?,%s,%s,%s)?\n",
1307 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1309 ret = PROFILE_SetString( section, entry, string, FALSE);
1310 PROFILE_FlushFile();
1315 RtlLeaveCriticalSection( &PROFILE_CritSect );
1319 /***********************************************************************
1320 * WritePrivateProfileStringA (KERNEL32.@)
1322 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1323 LPCSTR string, LPCSTR filename )
1325 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1328 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1329 else sectionW.Buffer = NULL;
1330 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1331 else entryW.Buffer = NULL;
1332 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1333 else stringW.Buffer = NULL;
1334 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1335 else filenameW.Buffer = NULL;
1337 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1338 stringW.Buffer, filenameW.Buffer);
1339 RtlFreeUnicodeString(§ionW);
1340 RtlFreeUnicodeString(&entryW);
1341 RtlFreeUnicodeString(&stringW);
1342 RtlFreeUnicodeString(&filenameW);
1346 /***********************************************************************
1347 * WritePrivateProfileSection (KERNEL.416)
1349 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1350 LPCSTR string, LPCSTR filename )
1352 return WritePrivateProfileSectionA( section, string, filename );
1355 /***********************************************************************
1356 * WritePrivateProfileSectionW (KERNEL32.@)
1358 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1359 LPCWSTR string, LPCWSTR filename )
1364 RtlEnterCriticalSection( &PROFILE_CritSect );
1366 if (PROFILE_Open( filename )) {
1367 if (!section && !string)
1368 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1369 else if (!string) {/* delete the named section*/
1370 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1371 PROFILE_FlushFile();
1373 PROFILE_DeleteAllKeys(section);
1376 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1377 strcpyW( buf, string );
1378 if((p = strchrW( buf, '='))) {
1380 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1382 HeapFree( GetProcessHeap(), 0, buf );
1383 string += strlenW(string)+1;
1385 PROFILE_FlushFile();
1389 RtlLeaveCriticalSection( &PROFILE_CritSect );
1393 /***********************************************************************
1394 * WritePrivateProfileSectionA (KERNEL32.@)
1396 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1397 LPCSTR string, LPCSTR filename)
1400 UNICODE_STRING sectionW, filenameW;
1409 while(*p) p += strlen(p) + 1;
1410 lenA = p - string + 1;
1411 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1412 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1413 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1415 else stringW = NULL;
1416 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1417 else sectionW.Buffer = NULL;
1418 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1419 else filenameW.Buffer = NULL;
1421 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1423 HeapFree(GetProcessHeap(), 0, stringW);
1424 RtlFreeUnicodeString(§ionW);
1425 RtlFreeUnicodeString(&filenameW);
1429 /***********************************************************************
1430 * WriteProfileSection (KERNEL.417)
1432 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1434 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1437 /***********************************************************************
1438 * WriteProfileSectionA (KERNEL32.@)
1440 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1443 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1446 /***********************************************************************
1447 * WriteProfileSectionW (KERNEL32.@)
1449 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1451 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1454 /***********************************************************************
1455 * GetPrivateProfileSectionNames (KERNEL.143)
1457 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1460 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1464 /***********************************************************************
1465 * GetProfileSectionNames (KERNEL.142)
1467 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1470 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1474 /***********************************************************************
1475 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1477 * Returns the section names contained in the specified file.
1478 * FIXME: Where do we find this file when the path is relative?
1479 * The section names are returned as a list of strings with an extra
1480 * '\0' to mark the end of the list. Except for that the behavior
1481 * depends on the Windows version.
1484 * - if the buffer is 0 or 1 character long then it is as if it was of
1486 * - otherwise, if the buffer is to small only the section names that fit
1488 * - note that this means if the buffer was to small to return even just
1489 * the first section name then a single '\0' will be returned.
1490 * - the return value is the number of characters written in the buffer,
1491 * except if the buffer was too smal in which case len-2 is returned
1494 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1495 * '\0' and the return value is 0
1496 * - otherwise if the buffer is too small then the first section name that
1497 * does not fit is truncated so that the string list can be terminated
1498 * correctly (double '\0')
1499 * - the return value is the number of characters written in the buffer
1500 * except for the trailing '\0'. If the buffer is too small, then the
1501 * return value is len-2
1502 * - Win2000 has a bug that triggers when the section names and the
1503 * trailing '\0' fit exactly in the buffer. In that case the trailing
1506 * Wine implements the observed Win2000 behavior (except for the bug).
1508 * Note that when the buffer is big enough then the return value may be any
1509 * value between 1 and len-1 (or len in Win95), including len-2.
1511 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1516 RtlEnterCriticalSection( &PROFILE_CritSect );
1518 if (PROFILE_Open( filename ))
1519 ret = PROFILE_GetSectionNames(buffer, size);
1521 RtlLeaveCriticalSection( &PROFILE_CritSect );
1527 /***********************************************************************
1528 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1530 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1533 UNICODE_STRING filenameW;
1537 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1538 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1539 else filenameW.Buffer = NULL;
1541 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1544 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1552 RtlFreeUnicodeString(&filenameW);
1553 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1557 /***********************************************************************
1558 * GetPrivateProfileStruct (KERNEL.407)
1560 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1561 LPVOID buf, UINT16 len, LPCSTR filename)
1563 return GetPrivateProfileStructA( section, key, buf, len, filename );
1566 /***********************************************************************
1567 * GetPrivateProfileStructW (KERNEL32.@)
1569 * Should match Win95's behaviour pretty much
1571 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1572 LPVOID buf, UINT len, LPCWSTR filename)
1576 RtlEnterCriticalSection( &PROFILE_CritSect );
1578 if (PROFILE_Open( filename )) {
1579 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1581 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1582 if (((strlenW(k->value) - 2) / 2) == len)
1589 end = k->value + strlenW(k->value); /* -> '\0' */
1590 /* check for invalid chars in ASCII coded hex string */
1591 for (p=k->value; p < end; p++)
1595 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1596 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1603 BOOL highnibble = TRUE;
1605 LPBYTE binbuf = (LPBYTE)buf;
1607 end -= 2; /* don't include checksum in output data */
1608 /* translate ASCII hex format into binary data */
1609 for (p=k->value; p < end; p++)
1613 (c - 'A' + 10) : (c - '0');
1620 *binbuf++ = b; /* feed binary data into output */
1621 chksum += b; /* calculate checksum */
1623 highnibble ^= 1; /* toggle */
1625 /* retrieve stored checksum value */
1627 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1629 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1630 if (b == (chksum & 0xff)) /* checksums match ? */
1636 RtlLeaveCriticalSection( &PROFILE_CritSect );
1641 /***********************************************************************
1642 * GetPrivateProfileStructA (KERNEL32.@)
1644 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1645 LPVOID buffer, UINT len, LPCSTR filename)
1647 UNICODE_STRING sectionW, keyW, filenameW;
1650 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1651 else sectionW.Buffer = NULL;
1652 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1653 else keyW.Buffer = NULL;
1654 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1655 else filenameW.Buffer = NULL;
1657 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1659 /* Do not translate binary data. */
1661 RtlFreeUnicodeString(§ionW);
1662 RtlFreeUnicodeString(&keyW);
1663 RtlFreeUnicodeString(&filenameW);
1669 /***********************************************************************
1670 * WritePrivateProfileStruct (KERNEL.406)
1672 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1673 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1675 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1678 /***********************************************************************
1679 * WritePrivateProfileStructW (KERNEL32.@)
1681 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1682 LPVOID buf, UINT bufsize, LPCWSTR filename)
1686 LPWSTR outstring, p;
1689 if (!section && !key && !buf) /* flush the cache */
1690 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1692 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1693 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1695 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1696 *p++ = hex[*binbuf >> 4];
1697 *p++ = hex[*binbuf & 0xf];
1700 /* checksum is sum & 0xff */
1701 *p++ = hex[(sum & 0xf0) >> 4];
1702 *p++ = hex[sum & 0xf];
1705 RtlEnterCriticalSection( &PROFILE_CritSect );
1707 if (PROFILE_Open( filename )) {
1708 ret = PROFILE_SetString( section, key, outstring, FALSE);
1709 PROFILE_FlushFile();
1712 RtlLeaveCriticalSection( &PROFILE_CritSect );
1714 HeapFree( GetProcessHeap(), 0, outstring );
1719 /***********************************************************************
1720 * WritePrivateProfileStructA (KERNEL32.@)
1722 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1723 LPVOID buf, UINT bufsize, LPCSTR filename)
1725 UNICODE_STRING sectionW, keyW, filenameW;
1728 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1729 else sectionW.Buffer = NULL;
1730 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1731 else keyW.Buffer = NULL;
1732 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1733 else filenameW.Buffer = NULL;
1735 /* Do not translate binary data. */
1736 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1739 RtlFreeUnicodeString(§ionW);
1740 RtlFreeUnicodeString(&keyW);
1741 RtlFreeUnicodeString(&filenameW);
1746 /***********************************************************************
1747 * WriteOutProfiles (KERNEL.315)
1749 void WINAPI WriteOutProfiles16(void)
1751 RtlEnterCriticalSection( &PROFILE_CritSect );
1752 PROFILE_FlushFile();
1753 RtlLeaveCriticalSection( &PROFILE_CritSect );
1756 /***********************************************************************
1757 * CloseProfileUserMapping (KERNEL32.@)
1759 BOOL WINAPI CloseProfileUserMapping(void) {
1760 FIXME("(), stub!\n");
1761 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);