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"
31 #include <sys/types.h>
41 #include "wine/winbase16.h"
45 #include "wine/unicode.h"
46 #include "wine/server.h"
47 #include "wine/library.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(profile);
52 typedef struct tagPROFILEKEY
55 struct tagPROFILEKEY *next;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
70 PROFILESECTION *section;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
92 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
94 static const char hex[16] = "0123456789ABCDEF";
96 /***********************************************************************
99 * Copy the content of an entry into a buffer, removing quotes, and possibly
100 * translating environment variables.
102 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
103 int handle_env, BOOL strip_quote )
110 if (strip_quote && ((*value == '\'') || (*value == '\"')))
112 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
117 lstrcpynW( buffer, value, len );
118 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
122 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
124 if ((*p == '$') && (p[1] == '{'))
127 LPCWSTR p2 = strchrW( p, '}' );
129 if (!p2) continue; /* ignore it */
130 copy_len = min( 1024, (int)(p2-p)-1 );
131 strncpyW(env_val, p + 2, copy_len );
132 env_val[copy_len - 1] = 0; /* ensure 0 termination */
134 if (GetEnvironmentVariableW( env_val, buffer, len))
136 copy_len = strlenW( buffer );
143 if (quote && (len > 1)) buffer--;
148 /***********************************************************************
151 * Save a profile tree to a file.
153 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
156 char buffer[PROFILE_MAX_LINE_LEN];
158 for ( ; section; section = section->next)
160 if (section->name[0])
162 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
163 fprintf( file, "\r\n[%s]\r\n", buffer );
165 for (key = section->key; key; key = key->next)
167 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
168 fprintf( file, "%s", buffer );
171 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
172 fprintf( file, "=%s", buffer );
174 fprintf( file, "\r\n" );
180 /***********************************************************************
183 * Free a profile tree.
185 static void PROFILE_Free( PROFILESECTION *section )
187 PROFILESECTION *next_section;
188 PROFILEKEY *key, *next_key;
190 for ( ; section; section = next_section)
192 for (key = section->key; key; key = next_key)
194 next_key = key->next;
195 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
196 HeapFree( GetProcessHeap(), 0, key );
198 next_section = section->next;
199 HeapFree( GetProcessHeap(), 0, section );
203 static inline int PROFILE_isspace(char c)
205 if (isspace(c)) return 1;
206 if (c=='\r' || c==0x1a) return 1;
207 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
212 /***********************************************************************
215 * Load a profile tree from a file.
217 static PROFILESECTION *PROFILE_Load( FILE *file )
219 char buffer[PROFILE_MAX_LINE_LEN];
222 PROFILESECTION *section, *first_section;
223 PROFILESECTION **next_section;
224 PROFILEKEY *key, *prev_key, **next_key;
226 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
227 if(first_section == NULL) return NULL;
228 first_section->name[0] = 0;
229 first_section->key = NULL;
230 first_section->next = NULL;
231 next_section = &first_section->next;
232 next_key = &first_section->key;
235 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
239 while (*p && PROFILE_isspace(*p)) p++;
240 if (*p == '[') /* section start */
242 if (!(p2 = strrchr( p, ']' )))
244 WARN("Invalid section header at line %d: '%s'\n",
252 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
254 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
256 section->next = NULL;
257 *next_section = section;
258 next_section = §ion->next;
259 next_key = §ion->key;
262 TRACE("New section: %s\n", debugstr_w(section->name));
269 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
271 if ((p2 = strchr( p, '=' )) != NULL)
274 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
276 while (*p2 && PROFILE_isspace(*p2)) p2++;
279 if(*p || !prev_key || *prev_key->name)
282 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
283 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
286 len = strlen(p2) + 1;
287 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
288 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
290 else key->value = NULL;
294 next_key = &key->next;
297 TRACE("New key: name=%s, value=%s\n",
298 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
301 return first_section;
305 /***********************************************************************
306 * PROFILE_DeleteSection
308 * Delete a section from a profile tree.
310 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
314 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
316 PROFILESECTION *to_del = *section;
317 *section = to_del->next;
319 PROFILE_Free( to_del );
322 section = &(*section)->next;
328 /***********************************************************************
331 * Delete a key from a profile tree.
333 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
334 LPCWSTR section_name, LPCWSTR key_name )
338 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
340 PROFILEKEY **key = &(*section)->key;
343 if (!strcmpiW( (*key)->name, key_name ))
345 PROFILEKEY *to_del = *key;
347 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
348 HeapFree( GetProcessHeap(), 0, to_del );
354 section = &(*section)->next;
360 /***********************************************************************
361 * PROFILE_DeleteAllKeys
363 * Delete all keys from a profile tree.
365 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
367 PROFILESECTION **section= &CurProfile->section;
370 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
372 PROFILEKEY **key = &(*section)->key;
375 PROFILEKEY *to_del = *key;
377 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
378 HeapFree( GetProcessHeap(), 0, to_del );
379 CurProfile->changed =TRUE;
382 section = &(*section)->next;
387 /***********************************************************************
390 * Find a key in a profile tree, optionally creating it.
392 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
393 LPCWSTR key_name, BOOL create, BOOL create_always )
398 while (PROFILE_isspace(*section_name)) section_name++;
399 p = section_name + strlenW(section_name) - 1;
400 while ((p > section_name) && PROFILE_isspace(*p)) p--;
401 seclen = p - section_name + 1;
403 while (PROFILE_isspace(*key_name)) key_name++;
404 p = key_name + strlenW(key_name) - 1;
405 while ((p > key_name) && PROFILE_isspace(*p)) p--;
406 keylen = p - key_name + 1;
410 if ( ((*section)->name[0])
411 && (!(strncmpiW( (*section)->name, section_name, seclen )))
412 && (((*section)->name)[seclen] == '\0') )
414 PROFILEKEY **key = &(*section)->key;
418 /* If create_always is FALSE then we check if the keyname already exists.
419 * Otherwise we add it regardless of its existence, to allow
420 * keys to be added more then once in some cases.
424 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
425 && (((*key)->name)[keylen] == '\0') )
430 if (!create) return NULL;
431 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
433 strcpyW( (*key)->name, key_name );
434 (*key)->value = NULL;
438 section = &(*section)->next;
440 if (!create) return NULL;
441 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
442 if(*section == NULL) return NULL;
443 strcpyW( (*section)->name, section_name );
444 (*section)->next = NULL;
445 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
446 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
448 HeapFree(GetProcessHeap(), 0, *section);
451 strcpyW( (*section)->key->name, key_name );
452 (*section)->key->value = NULL;
453 (*section)->key->next = NULL;
454 return (*section)->key;
458 /***********************************************************************
461 * Flush the current profile to disk if changed.
463 static BOOL PROFILE_FlushFile(void)
465 char *p, buffer[MAX_PATHNAME_LEN];
466 const char *unix_name;
472 WARN("No current profile!\n");
476 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
477 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
479 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
480 WCHAR *name, *name_lwr;
481 /* Try to create it in $HOME/.wine */
482 /* FIXME: this will need a more general solution */
483 strcpy( buffer, wine_get_config_dir() );
484 p = buffer + strlen(buffer);
486 *p = 0; /* make strlen() below happy */
487 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
489 /* create a lower cased version of the name */
490 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
491 strcpyW(name_lwr, name);
493 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
494 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
495 HeapFree(GetProcessHeap(), 0, name_lwr);
497 file = fopen( buffer, "w" );
503 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
507 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
508 PROFILE_Save( file, CurProfile->section );
510 CurProfile->changed = FALSE;
511 if(!stat(unix_name,&buf))
512 CurProfile->mtime=buf.st_mtime;
517 /***********************************************************************
518 * PROFILE_ReleaseFile
520 * Flush the current profile to disk and remove it from the cache.
522 static void PROFILE_ReleaseFile(void)
525 PROFILE_Free( CurProfile->section );
526 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
527 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
528 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
529 CurProfile->changed = FALSE;
530 CurProfile->section = NULL;
531 CurProfile->dos_name = NULL;
532 CurProfile->unix_name = NULL;
533 CurProfile->filename = NULL;
534 CurProfile->mtime = 0;
538 /***********************************************************************
541 * Open a profile file, checking the cached file first.
543 static BOOL PROFILE_Open( LPCWSTR filename )
545 DOS_FULL_NAME full_name;
546 char buffer[MAX_PATHNAME_LEN];
548 WCHAR *name, *name_lwr;
553 PROFILE *tempProfile;
555 /* First time around */
558 for(i=0;i<N_CACHED_PROFILES;i++)
560 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
561 if(MRUProfile[i] == NULL) break;
562 MRUProfile[i]->changed=FALSE;
563 MRUProfile[i]->section=NULL;
564 MRUProfile[i]->dos_name=NULL;
565 MRUProfile[i]->unix_name=NULL;
566 MRUProfile[i]->filename=NULL;
567 MRUProfile[i]->mtime=0;
570 /* Check for a match */
572 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
573 strchrW( filename, ':' ))
575 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
579 static const WCHAR bkslashW[] = {'\\',0};
580 WCHAR windirW[MAX_PATH];
582 GetWindowsDirectoryW( windirW, MAX_PATH );
583 strcatW( windirW, bkslashW );
584 strcatW( windirW, filename );
585 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
588 for(i=0;i<N_CACHED_PROFILES;i++)
590 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
591 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
596 tempProfile=MRUProfile[i];
598 MRUProfile[j]=MRUProfile[j-1];
599 CurProfile=tempProfile;
601 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
602 TRACE("(%s): already opened (mru=%d)\n",
603 debugstr_w(filename), i );
605 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
606 debugstr_w(filename), i );
611 /* Flush the old current profile */
614 /* Make the oldest profile the current one only in order to get rid of it */
615 if(i==N_CACHED_PROFILES)
617 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
618 for(i=N_CACHED_PROFILES-1;i>0;i--)
619 MRUProfile[i]=MRUProfile[i-1];
620 CurProfile=tempProfile;
622 if(CurProfile->filename) PROFILE_ReleaseFile();
624 /* OK, now that CurProfile is definitely free we assign it our new file */
625 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
626 strcpyW( newdos_name, full_name.short_name );
627 CurProfile->dos_name = newdos_name;
628 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
629 strcpyW( CurProfile->filename, filename );
631 /* Try to open the profile file, first in $HOME/.wine */
633 /* FIXME: this will need a more general solution */
634 strcpy( buffer, wine_get_config_dir() );
635 p = buffer + strlen(buffer);
637 *p = 0; /* make strlen() below happy */
638 name = strrchrW( newdos_name, '\\' ) + 1;
640 /* create a lower cased version of the name */
641 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
642 strcpyW(name_lwr, name);
644 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
645 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
646 HeapFree(GetProcessHeap(), 0, name_lwr);
648 if ((file = fopen( buffer, "r" )))
650 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
651 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
652 strcpy( CurProfile->unix_name, buffer );
656 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
657 strcpy( CurProfile->unix_name, full_name.long_name );
658 if ((file = fopen( full_name.long_name, "r" )))
659 TRACE("(%s): found it in %s\n",
660 debugstr_w(filename), full_name.long_name );
665 CurProfile->section = PROFILE_Load( file );
667 if(!stat(CurProfile->unix_name,&buf))
668 CurProfile->mtime=buf.st_mtime;
672 /* Does not exist yet, we will create it in PROFILE_FlushFile */
673 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
679 /***********************************************************************
682 * Returns all keys of a section.
683 * If return_values is TRUE, also include the corresponding values.
685 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
686 LPWSTR buffer, UINT len, BOOL handle_env,
691 if(!buffer) return 0;
693 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
697 if (section->name[0] && !strcmpiW( section->name, section_name ))
700 for (key = section->key; key; key = key->next)
703 if (!*key->name) continue; /* Skip empty lines */
704 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
705 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
706 len -= strlenW(buffer) + 1;
707 buffer += strlenW(buffer) + 1;
710 if (return_values && key->value) {
712 PROFILE_CopyEntry ( buffer,
713 key->value, len - 1, handle_env, 0 );
714 len -= strlenW(buffer) + 1;
715 buffer += strlenW(buffer) + 1;
720 /*If either lpszSection or lpszKey is NULL and the supplied
721 destination buffer is too small to hold all the strings,
722 the last string is truncated and followed by two null characters.
723 In this case, the return value is equal to cchReturnBuffer
731 section = section->next;
733 buffer[0] = buffer[1] = '\0';
737 /* See GetPrivateProfileSectionNamesA for documentation */
738 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
742 PROFILESECTION *section;
753 section = CurProfile->section;
754 while ((section!=NULL)) {
755 if (section->name[0]) {
756 l = strlenW(section->name)+1;
759 strncpyW(buf, section->name, f-1);
766 strcpyW(buf, section->name);
770 section = section->next;
777 /***********************************************************************
780 * Get a profile string.
782 * Tests with GetPrivateProfileString16, W95a,
783 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
784 * section key_name def_val res buffer
785 * "set1" "1" "x" 43 [data]
786 * "set1" "1 " "x" 43 [data] (!)
787 * "set1" " 1 "' "x" 43 [data] (!)
788 * "set1" "" "x" 1 "x"
789 * "set1" "" "x " 1 "x" (!)
790 * "set1" "" " x " 3 " x" (!)
791 * "set1" NULL "x" 6 "1\02\03\0\0"
792 * "set1" "" "x" 1 "x"
793 * NULL "1" "x" 0 "" (!)
799 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
800 LPCWSTR def_val, LPWSTR buffer, UINT len )
802 PROFILEKEY *key = NULL;
803 static const WCHAR empty_strW[] = { 0 };
805 if(!buffer) return 0;
807 if (!def_val) def_val = empty_strW;
812 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
815 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
816 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
818 TRACE("(%s,%s,%s): returning %s\n",
819 debugstr_w(section), debugstr_w(key_name),
820 debugstr_w(def_val), debugstr_w(buffer) );
821 return strlenW( buffer );
823 /* no "else" here ! */
824 if (section && section[0])
826 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
827 if (!buffer[0]) /* no luck -> def_val */
829 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
830 ret = strlenW(buffer);
839 /***********************************************************************
842 * Set a profile string.
844 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
845 LPCWSTR value, BOOL create_always )
847 if (!key_name) /* Delete a whole section */
849 TRACE("(%s)\n", debugstr_w(section_name));
850 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
852 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
853 this is not an error on application's level.*/
855 else if (!value) /* Delete a key */
857 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
858 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
859 section_name, key_name );
860 return TRUE; /* same error handling as above */
862 else /* Set the key value */
864 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
865 key_name, TRUE, create_always );
866 TRACE("(%s,%s,%s):\n",
867 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
868 if (!key) return FALSE;
871 /* strip the leading spaces. We can safely strip \n\r and
872 * friends too, they should not happen here anyway. */
873 while (PROFILE_isspace(*value)) value++;
875 if (!strcmpW( key->value, value ))
877 TRACE(" no change needed\n" );
878 return TRUE; /* No change needed */
880 TRACE(" replacing %s\n", debugstr_w(key->value) );
881 HeapFree( GetProcessHeap(), 0, key->value );
883 else TRACE(" creating key\n" );
884 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
885 strcpyW( key->value, value );
886 CurProfile->changed = TRUE;
892 /***********************************************************************
895 static HKEY get_profile_key(void)
897 static HKEY profile_key;
901 OBJECT_ATTRIBUTES attr;
902 UNICODE_STRING nameW;
905 attr.Length = sizeof(attr);
906 attr.RootDirectory = 0;
907 attr.ObjectName = &nameW;
909 attr.SecurityDescriptor = NULL;
910 attr.SecurityQualityOfService = NULL;
912 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
913 NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL ))
915 ERR("Cannot create config registry key\n" );
918 RtlFreeUnicodeString( &nameW );
920 if (InterlockedCompareExchangePointer( (void **)&profile_key, hkey, 0 ))
921 NtClose( hkey ); /* somebody beat us to it */
927 /***********************************************************************
928 * PROFILE_GetWineIniString
930 * Get a config string from the wine.ini file.
932 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
933 LPCWSTR def, LPWSTR buffer, int len )
937 OBJECT_ATTRIBUTES attr;
938 UNICODE_STRING nameW;
940 attr.Length = sizeof(attr);
941 attr.RootDirectory = get_profile_key();
942 attr.ObjectName = &nameW;
944 attr.SecurityDescriptor = NULL;
945 attr.SecurityQualityOfService = NULL;
946 RtlInitUnicodeString( &nameW, section );
947 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
949 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
952 RtlInitUnicodeString( &nameW, key_name );
953 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
954 tmp, sizeof(tmp), &count )))
956 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
957 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
962 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
963 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
964 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
965 return strlenW(buffer);
969 /******************************************************************************
971 * PROFILE_GetWineIniBool
973 * Reads a boolean value from the wine.ini file. This function attempts to
974 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
975 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
976 * true. Anything else results in the return of the default value.
978 * This function uses 1 to indicate true, and 0 for false. You can check
979 * for existence by setting def to something other than 0 or 1 and
980 * examining the return value.
982 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
984 static const WCHAR def_valueW[] = {'~',0};
988 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
990 switch(key_value[0]) {
1011 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1012 def ? "TRUE" : "FALSE", key_value[0],
1013 retval ? "TRUE" : "FALSE");
1019 /***********************************************************************
1020 * PROFILE_UsageWineIni
1022 * Explain the wine.ini file to those who don't read documentation.
1023 * Keep below one screenful in length so that error messages above are
1026 void PROFILE_UsageWineIni(void)
1028 MESSAGE("Perhaps you have not properly edited or created "
1029 "your Wine configuration file.\n");
1030 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1031 /* RTFM, so to say */
1035 /********************* API functions **********************************/
1037 /***********************************************************************
1038 * GetProfileInt (KERNEL.57)
1040 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1042 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1046 /***********************************************************************
1047 * GetProfileIntA (KERNEL32.@)
1049 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1051 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1054 /***********************************************************************
1055 * GetProfileIntW (KERNEL32.@)
1057 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1059 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1063 * if allow_section_name_copy is TRUE, allow the copying :
1064 * - of Section names if 'section' is NULL
1065 * - of Keys in a Section if 'entry' is NULL
1066 * (see MSDN doc for GetPrivateProfileString)
1068 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1069 LPCWSTR def_val, LPWSTR buffer,
1070 UINT len, LPCWSTR filename,
1071 BOOL allow_section_name_copy )
1074 LPWSTR pDefVal = NULL;
1079 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1080 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1082 /* strip any trailing ' ' of def_val. */
1085 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1093 if (*p == ' ') /* ouch, contained trailing ' ' */
1095 int len = (int)(p - def_val);
1096 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1097 strncpyW(pDefVal, def_val, len);
1098 pDefVal[len] = '\0';
1102 pDefVal = (LPWSTR)def_val;
1104 EnterCriticalSection( &PROFILE_CritSect );
1106 if (PROFILE_Open( filename )) {
1107 if ((allow_section_name_copy) && (section == NULL))
1108 ret = PROFILE_GetSectionNames(buffer, len);
1110 /* PROFILE_GetString already handles the 'entry == NULL' case */
1111 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1113 lstrcpynW( buffer, pDefVal, len );
1114 ret = strlenW( buffer );
1117 LeaveCriticalSection( &PROFILE_CritSect );
1119 if (pDefVal != def_val) /* allocated */
1120 HeapFree(GetProcessHeap(), 0, pDefVal);
1122 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1127 /***********************************************************************
1128 * GetPrivateProfileString (KERNEL.128)
1130 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1131 LPCSTR def_val, LPSTR buffer,
1132 UINT16 len, LPCSTR filename )
1134 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1136 INT16 retW, ret = 0;
1138 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1139 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1140 else sectionW.Buffer = NULL;
1141 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1142 else entryW.Buffer = NULL;
1143 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1144 else def_valW.Buffer = NULL;
1145 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1146 else filenameW.Buffer = NULL;
1148 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1149 def_valW.Buffer, bufferW, len,
1150 filenameW.Buffer, FALSE );
1153 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1160 ret--; /* strip terminating 0 */
1163 RtlFreeUnicodeString(§ionW);
1164 RtlFreeUnicodeString(&entryW);
1165 RtlFreeUnicodeString(&def_valW);
1166 RtlFreeUnicodeString(&filenameW);
1167 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1171 /***********************************************************************
1172 * GetPrivateProfileStringA (KERNEL32.@)
1174 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1175 LPCSTR def_val, LPSTR buffer,
1176 UINT len, LPCSTR filename )
1178 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1182 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1183 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1184 else sectionW.Buffer = NULL;
1185 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1186 else entryW.Buffer = NULL;
1187 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1188 else def_valW.Buffer = NULL;
1189 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1190 else filenameW.Buffer = NULL;
1192 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1193 def_valW.Buffer, bufferW, len,
1197 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1204 ret--; /* strip terminating 0 */
1207 RtlFreeUnicodeString(§ionW);
1208 RtlFreeUnicodeString(&entryW);
1209 RtlFreeUnicodeString(&def_valW);
1210 RtlFreeUnicodeString(&filenameW);
1211 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1215 /***********************************************************************
1216 * GetPrivateProfileStringW (KERNEL32.@)
1218 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1219 LPCWSTR def_val, LPWSTR buffer,
1220 UINT len, LPCWSTR filename )
1222 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1223 buffer, len, filename, TRUE );
1226 /***********************************************************************
1227 * GetProfileString (KERNEL.58)
1229 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1230 LPSTR buffer, UINT16 len )
1232 return GetPrivateProfileString16( section, entry, def_val,
1233 buffer, len, "win.ini" );
1236 /***********************************************************************
1237 * GetProfileStringA (KERNEL32.@)
1239 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1240 LPSTR buffer, UINT len )
1242 return GetPrivateProfileStringA( section, entry, def_val,
1243 buffer, len, "win.ini" );
1246 /***********************************************************************
1247 * GetProfileStringW (KERNEL32.@)
1249 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1250 LPCWSTR def_val, LPWSTR buffer, UINT len )
1252 return GetPrivateProfileStringW( section, entry, def_val,
1253 buffer, len, wininiW );
1256 /***********************************************************************
1257 * WriteProfileString (KERNEL.59)
1259 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1262 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1265 /***********************************************************************
1266 * WriteProfileStringA (KERNEL32.@)
1268 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1271 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1274 /***********************************************************************
1275 * WriteProfileStringW (KERNEL32.@)
1277 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1280 return WritePrivateProfileStringW( section, entry, string, wininiW );
1284 /***********************************************************************
1285 * GetPrivateProfileInt (KERNEL.127)
1287 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1288 INT16 def_val, LPCSTR filename )
1290 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1291 * here, but Win98SE doesn't care about this at all, so I deleted it.
1292 * AFAIR versions prior to Win9x had these limits, though. */
1293 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1296 /***********************************************************************
1297 * GetPrivateProfileIntA (KERNEL32.@)
1299 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1300 INT def_val, LPCSTR filename )
1305 if (!GetPrivateProfileStringA( section, entry, "",
1306 buffer, sizeof(buffer), filename ))
1308 /* FIXME: if entry can be found but it's empty, then Win16 is
1309 * supposed to return 0 instead of def_val ! Difficult/problematic
1310 * to implement (every other failure also returns zero buffer),
1311 * thus wait until testing framework avail for making sure nothing
1312 * else gets broken that way. */
1313 if (!buffer[0]) return (UINT)def_val;
1315 /* Don't use strtol() here !
1316 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1317 YES, scan for unsigned format ! (otherwise compatibility error) */
1318 if (!sscanf(buffer, "%lu", &result)) return 0;
1319 return (UINT)result;
1322 /***********************************************************************
1323 * GetPrivateProfileIntW (KERNEL32.@)
1325 * FIXME: rewrite using unicode
1327 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1328 INT def_val, LPCWSTR filename )
1330 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1331 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1332 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1333 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1334 HeapFree( GetProcessHeap(), 0, sectionA );
1335 HeapFree( GetProcessHeap(), 0, filenameA );
1336 HeapFree( GetProcessHeap(), 0, entryA );
1340 /***********************************************************************
1341 * GetPrivateProfileSection (KERNEL.418)
1343 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1344 UINT16 len, LPCSTR filename )
1346 return GetPrivateProfileSectionA( section, buffer, len, filename );
1349 /***********************************************************************
1350 * GetPrivateProfileSectionW (KERNEL32.@)
1352 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1353 DWORD len, LPCWSTR filename )
1357 EnterCriticalSection( &PROFILE_CritSect );
1359 if (PROFILE_Open( filename ))
1360 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1363 LeaveCriticalSection( &PROFILE_CritSect );
1368 /***********************************************************************
1369 * GetPrivateProfileSectionA (KERNEL32.@)
1371 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1372 DWORD len, LPCSTR filename )
1374 UNICODE_STRING sectionW, filenameW;
1378 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1379 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1380 else sectionW.Buffer = NULL;
1381 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1382 else filenameW.Buffer = NULL;
1384 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1387 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1403 RtlFreeUnicodeString(§ionW);
1404 RtlFreeUnicodeString(&filenameW);
1405 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1409 /***********************************************************************
1410 * GetProfileSection (KERNEL.419)
1412 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1414 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1417 /***********************************************************************
1418 * GetProfileSectionA (KERNEL32.@)
1420 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1422 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1425 /***********************************************************************
1426 * GetProfileSectionW (KERNEL32.@)
1428 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1430 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1434 /***********************************************************************
1435 * WritePrivateProfileString (KERNEL.129)
1437 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1438 LPCSTR string, LPCSTR filename )
1440 return WritePrivateProfileStringA(section,entry,string,filename);
1443 /***********************************************************************
1444 * WritePrivateProfileStringW (KERNEL32.@)
1446 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1447 LPCWSTR string, LPCWSTR filename )
1451 EnterCriticalSection( &PROFILE_CritSect );
1453 if (PROFILE_Open( filename ))
1455 if (!section && !entry && !string) /* documented "file flush" case */
1457 PROFILE_FlushFile();
1458 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1462 FIXME("(NULL?,%s,%s,%s)?\n",
1463 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1465 ret = PROFILE_SetString( section, entry, string, FALSE);
1466 PROFILE_FlushFile();
1471 LeaveCriticalSection( &PROFILE_CritSect );
1475 /***********************************************************************
1476 * WritePrivateProfileStringA (KERNEL32.@)
1478 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1479 LPCSTR string, LPCSTR filename )
1481 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1484 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1485 else sectionW.Buffer = NULL;
1486 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1487 else entryW.Buffer = NULL;
1488 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1489 else stringW.Buffer = NULL;
1490 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1491 else filenameW.Buffer = NULL;
1493 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1494 stringW.Buffer, filenameW.Buffer);
1495 RtlFreeUnicodeString(§ionW);
1496 RtlFreeUnicodeString(&entryW);
1497 RtlFreeUnicodeString(&stringW);
1498 RtlFreeUnicodeString(&filenameW);
1502 /***********************************************************************
1503 * WritePrivateProfileSection (KERNEL.416)
1505 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1506 LPCSTR string, LPCSTR filename )
1508 return WritePrivateProfileSectionA( section, string, filename );
1511 /***********************************************************************
1512 * WritePrivateProfileSectionW (KERNEL32.@)
1514 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1515 LPCWSTR string, LPCWSTR filename )
1520 EnterCriticalSection( &PROFILE_CritSect );
1522 if (PROFILE_Open( filename )) {
1523 if (!section && !string)
1524 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1525 else if (!string) {/* delete the named section*/
1526 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1527 PROFILE_FlushFile();
1529 PROFILE_DeleteAllKeys(section);
1532 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1533 strcpyW( buf, string );
1534 if((p = strchrW( buf, '='))) {
1536 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1538 HeapFree( GetProcessHeap(), 0, buf );
1539 string += strlenW(string)+1;
1541 PROFILE_FlushFile();
1545 LeaveCriticalSection( &PROFILE_CritSect );
1549 /***********************************************************************
1550 * WritePrivateProfileSectionA (KERNEL32.@)
1552 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1553 LPCSTR string, LPCSTR filename)
1556 UNICODE_STRING sectionW, filenameW;
1565 while(*p) p += strlen(p) + 1;
1566 lenA = p - string + 1;
1567 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1568 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1569 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1571 else stringW = NULL;
1572 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1573 else sectionW.Buffer = NULL;
1574 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1575 else filenameW.Buffer = NULL;
1577 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1579 HeapFree(GetProcessHeap(), 0, stringW);
1580 RtlFreeUnicodeString(§ionW);
1581 RtlFreeUnicodeString(&filenameW);
1585 /***********************************************************************
1586 * WriteProfileSection (KERNEL.417)
1588 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1590 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1593 /***********************************************************************
1594 * WriteProfileSectionA (KERNEL32.@)
1596 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1599 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1602 /***********************************************************************
1603 * WriteProfileSectionW (KERNEL32.@)
1605 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1607 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1610 /***********************************************************************
1611 * GetPrivateProfileSectionNames (KERNEL.143)
1613 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1616 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1620 /***********************************************************************
1621 * GetProfileSectionNames (KERNEL.142)
1623 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1626 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1630 /***********************************************************************
1631 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1633 * Returns the section names contained in the specified file.
1634 * FIXME: Where do we find this file when the path is relative?
1635 * The section names are returned as a list of strings with an extra
1636 * '\0' to mark the end of the list. Except for that the behavior
1637 * depends on the Windows version.
1640 * - if the buffer is 0 or 1 character long then it is as if it was of
1642 * - otherwise, if the buffer is to small only the section names that fit
1644 * - note that this means if the buffer was to small to return even just
1645 * the first section name then a single '\0' will be returned.
1646 * - the return value is the number of characters written in the buffer,
1647 * except if the buffer was too smal in which case len-2 is returned
1650 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1651 * '\0' and the return value is 0
1652 * - otherwise if the buffer is too small then the first section name that
1653 * does not fit is truncated so that the string list can be terminated
1654 * correctly (double '\0')
1655 * - the return value is the number of characters written in the buffer
1656 * except for the trailing '\0'. If the buffer is too small, then the
1657 * return value is len-2
1658 * - Win2000 has a bug that triggers when the section names and the
1659 * trailing '\0' fit exactly in the buffer. In that case the trailing
1662 * Wine implements the observed Win2000 behavior (except for the bug).
1664 * Note that when the buffer is big enough then the return value may be any
1665 * value between 1 and len-1 (or len in Win95), including len-2.
1667 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1672 EnterCriticalSection( &PROFILE_CritSect );
1674 if (PROFILE_Open( filename ))
1675 ret = PROFILE_GetSectionNames(buffer, size);
1677 LeaveCriticalSection( &PROFILE_CritSect );
1683 /***********************************************************************
1684 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1686 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1689 UNICODE_STRING filenameW;
1693 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1694 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1695 else filenameW.Buffer = NULL;
1697 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1700 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1708 RtlFreeUnicodeString(&filenameW);
1709 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1713 /***********************************************************************
1714 * GetPrivateProfileStruct (KERNEL.407)
1716 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1717 LPVOID buf, UINT16 len, LPCSTR filename)
1719 return GetPrivateProfileStructA( section, key, buf, len, filename );
1722 /***********************************************************************
1723 * GetPrivateProfileStructW (KERNEL32.@)
1725 * Should match Win95's behaviour pretty much
1727 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1728 LPVOID buf, UINT len, LPCWSTR filename)
1732 EnterCriticalSection( &PROFILE_CritSect );
1734 if (PROFILE_Open( filename )) {
1735 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1737 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1738 if (((strlenW(k->value) - 2) / 2) == len)
1745 end = k->value + strlenW(k->value); /* -> '\0' */
1746 /* check for invalid chars in ASCII coded hex string */
1747 for (p=k->value; p < end; p++)
1751 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1752 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1759 BOOL highnibble = TRUE;
1761 LPBYTE binbuf = (LPBYTE)buf;
1763 end -= 2; /* don't include checksum in output data */
1764 /* translate ASCII hex format into binary data */
1765 for (p=k->value; p < end; p++)
1769 (c - 'A' + 10) : (c - '0');
1776 *binbuf++ = b; /* feed binary data into output */
1777 chksum += b; /* calculate checksum */
1779 highnibble ^= 1; /* toggle */
1781 /* retrieve stored checksum value */
1783 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1785 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1786 if (b == (chksum & 0xff)) /* checksums match ? */
1792 LeaveCriticalSection( &PROFILE_CritSect );
1797 /***********************************************************************
1798 * GetPrivateProfileStructA (KERNEL32.@)
1800 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1801 LPVOID buffer, UINT len, 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 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1815 /* Do not translate binary data. */
1817 RtlFreeUnicodeString(§ionW);
1818 RtlFreeUnicodeString(&keyW);
1819 RtlFreeUnicodeString(&filenameW);
1825 /***********************************************************************
1826 * WritePrivateProfileStruct (KERNEL.406)
1828 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1829 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1831 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1834 /***********************************************************************
1835 * WritePrivateProfileStructW (KERNEL32.@)
1837 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1838 LPVOID buf, UINT bufsize, LPCWSTR filename)
1842 LPWSTR outstring, p;
1845 if (!section && !key && !buf) /* flush the cache */
1846 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1848 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1849 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1851 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1852 *p++ = hex[*binbuf >> 4];
1853 *p++ = hex[*binbuf & 0xf];
1856 /* checksum is sum & 0xff */
1857 *p++ = hex[(sum & 0xf0) >> 4];
1858 *p++ = hex[sum & 0xf];
1861 EnterCriticalSection( &PROFILE_CritSect );
1863 if (PROFILE_Open( filename )) {
1864 ret = PROFILE_SetString( section, key, outstring, FALSE);
1865 PROFILE_FlushFile();
1868 LeaveCriticalSection( &PROFILE_CritSect );
1870 HeapFree( GetProcessHeap(), 0, outstring );
1875 /***********************************************************************
1876 * WritePrivateProfileStructA (KERNEL32.@)
1878 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1879 LPVOID buf, UINT bufsize, LPCSTR filename)
1881 UNICODE_STRING sectionW, keyW, filenameW;
1884 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1885 else sectionW.Buffer = NULL;
1886 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1887 else keyW.Buffer = NULL;
1888 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1889 else filenameW.Buffer = NULL;
1891 /* Do not translate binary data. */
1892 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1895 RtlFreeUnicodeString(§ionW);
1896 RtlFreeUnicodeString(&keyW);
1897 RtlFreeUnicodeString(&filenameW);
1902 /***********************************************************************
1903 * WriteOutProfiles (KERNEL.315)
1905 void WINAPI WriteOutProfiles16(void)
1907 EnterCriticalSection( &PROFILE_CritSect );
1908 PROFILE_FlushFile();
1909 LeaveCriticalSection( &PROFILE_CritSect );
1912 /***********************************************************************
1913 * CloseProfileUserMapping (KERNEL32.@)
1915 BOOL WINAPI CloseProfileUserMapping(void) {
1916 FIXME("(), stub!\n");
1917 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);