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 emptystringW[] = {0};
91 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
95 static const char hex[16] = "0123456789ABCDEF";
97 /***********************************************************************
100 * Copy the content of an entry into a buffer, removing quotes, and possibly
101 * translating environment variables.
103 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
104 int handle_env, BOOL strip_quote )
111 if (strip_quote && ((*value == '\'') || (*value == '\"')))
113 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
118 lstrcpynW( buffer, value, len );
119 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
124 while (*p && (len > 1))
126 if ((*p == '$') && (p[1] == '{'))
129 LPCWSTR p2 = strchrW( p, '}' );
131 if (!p2) continue; /* ignore it */
132 copy_len = min( 1024, (int)(p2-p)-1 );
133 strncpyW(env_val, p + 2, copy_len );
134 env_val[copy_len - 1] = 0; /* ensure 0 termination */
136 if (GetEnvironmentVariableW( env_val, buffer, len))
138 copy_len = strlenW( buffer );
150 if (quote && (len > 1)) buffer--;
155 /***********************************************************************
158 * Save a profile tree to a file.
160 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
163 char buffer[PROFILE_MAX_LINE_LEN];
165 for ( ; section; section = section->next)
167 if (section->name[0])
169 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
170 fprintf( file, "\r\n[%s]\r\n", buffer );
172 for (key = section->key; key; key = key->next)
174 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
175 fprintf( file, "%s", buffer );
178 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
179 fprintf( file, "=%s", buffer );
181 fprintf( file, "\r\n" );
187 /***********************************************************************
190 * Free a profile tree.
192 static void PROFILE_Free( PROFILESECTION *section )
194 PROFILESECTION *next_section;
195 PROFILEKEY *key, *next_key;
197 for ( ; section; section = next_section)
199 for (key = section->key; key; key = next_key)
201 next_key = key->next;
202 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
203 HeapFree( GetProcessHeap(), 0, key );
205 next_section = section->next;
206 HeapFree( GetProcessHeap(), 0, section );
210 static inline int PROFILE_isspace(char c)
212 if (isspace(c)) return 1;
213 if (c=='\r' || c==0x1a) return 1;
214 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
219 /***********************************************************************
222 * Load a profile tree from a file.
224 static PROFILESECTION *PROFILE_Load( FILE *file )
226 char buffer[PROFILE_MAX_LINE_LEN];
229 PROFILESECTION *section, *first_section;
230 PROFILESECTION **next_section;
231 PROFILEKEY *key, *prev_key, **next_key;
233 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
234 if(first_section == NULL) return NULL;
235 first_section->name[0] = 0;
236 first_section->key = NULL;
237 first_section->next = NULL;
238 next_section = &first_section->next;
239 next_key = &first_section->key;
242 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
246 while (*p && PROFILE_isspace(*p)) p++;
247 if (*p == '[') /* section start */
249 if (!(p2 = strrchr( p, ']' )))
251 WARN("Invalid section header at line %d: '%s'\n",
259 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
261 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
263 section->next = NULL;
264 *next_section = section;
265 next_section = §ion->next;
266 next_key = §ion->key;
269 TRACE("New section: %s\n", debugstr_w(section->name));
276 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
278 if ((p2 = strchr( p, '=' )) != NULL)
281 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
283 while (*p2 && PROFILE_isspace(*p2)) p2++;
286 if(*p || !prev_key || *prev_key->name)
289 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
290 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
293 len = strlen(p2) + 1;
294 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
295 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
297 else key->value = NULL;
301 next_key = &key->next;
304 TRACE("New key: name=%s, value=%s\n",
305 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
308 return first_section;
312 /***********************************************************************
313 * PROFILE_DeleteSection
315 * Delete a section from a profile tree.
317 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
321 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
323 PROFILESECTION *to_del = *section;
324 *section = to_del->next;
326 PROFILE_Free( to_del );
329 section = &(*section)->next;
335 /***********************************************************************
338 * Delete a key from a profile tree.
340 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
341 LPCWSTR section_name, LPCWSTR key_name )
345 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
347 PROFILEKEY **key = &(*section)->key;
350 if (!strcmpiW( (*key)->name, key_name ))
352 PROFILEKEY *to_del = *key;
354 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
355 HeapFree( GetProcessHeap(), 0, to_del );
361 section = &(*section)->next;
367 /***********************************************************************
368 * PROFILE_DeleteAllKeys
370 * Delete all keys from a profile tree.
372 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
374 PROFILESECTION **section= &CurProfile->section;
377 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
379 PROFILEKEY **key = &(*section)->key;
382 PROFILEKEY *to_del = *key;
384 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
385 HeapFree( GetProcessHeap(), 0, to_del );
386 CurProfile->changed =TRUE;
389 section = &(*section)->next;
394 /***********************************************************************
397 * Find a key in a profile tree, optionally creating it.
399 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
400 LPCWSTR key_name, BOOL create, BOOL create_always )
405 while (PROFILE_isspace(*section_name)) section_name++;
406 p = section_name + strlenW(section_name) - 1;
407 while ((p > section_name) && PROFILE_isspace(*p)) p--;
408 seclen = p - section_name + 1;
410 while (PROFILE_isspace(*key_name)) key_name++;
411 p = key_name + strlenW(key_name) - 1;
412 while ((p > key_name) && PROFILE_isspace(*p)) p--;
413 keylen = p - key_name + 1;
417 if ( ((*section)->name[0])
418 && (!(strncmpiW( (*section)->name, section_name, seclen )))
419 && (((*section)->name)[seclen] == '\0') )
421 PROFILEKEY **key = &(*section)->key;
425 /* If create_always is FALSE then we check if the keyname already exists.
426 * Otherwise we add it regardless of its existence, to allow
427 * keys to be added more then once in some cases.
431 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
432 && (((*key)->name)[keylen] == '\0') )
437 if (!create) return NULL;
438 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
440 strcpyW( (*key)->name, key_name );
441 (*key)->value = NULL;
445 section = &(*section)->next;
447 if (!create) return NULL;
448 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
449 if(*section == NULL) return NULL;
450 strcpyW( (*section)->name, section_name );
451 (*section)->next = NULL;
452 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
453 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
455 HeapFree(GetProcessHeap(), 0, *section);
458 strcpyW( (*section)->key->name, key_name );
459 (*section)->key->value = NULL;
460 (*section)->key->next = NULL;
461 return (*section)->key;
465 /***********************************************************************
468 * Flush the current profile to disk if changed.
470 static BOOL PROFILE_FlushFile(void)
472 char *p, buffer[MAX_PATHNAME_LEN];
473 const char *unix_name;
479 WARN("No current profile!\n");
483 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
484 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
486 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
487 WCHAR *name, *name_lwr;
488 /* Try to create it in $HOME/.wine */
489 /* FIXME: this will need a more general solution */
490 strcpy( buffer, wine_get_config_dir() );
491 p = buffer + strlen(buffer);
493 *p = 0; /* make strlen() below happy */
494 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
496 /* create a lower cased version of the name */
497 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
498 strcpyW(name_lwr, name);
500 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
501 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
502 HeapFree(GetProcessHeap(), 0, name_lwr);
504 file = fopen( buffer, "w" );
510 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
514 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
515 PROFILE_Save( file, CurProfile->section );
517 CurProfile->changed = FALSE;
518 if(!stat(unix_name,&buf))
519 CurProfile->mtime=buf.st_mtime;
524 /***********************************************************************
525 * PROFILE_ReleaseFile
527 * Flush the current profile to disk and remove it from the cache.
529 static void PROFILE_ReleaseFile(void)
532 PROFILE_Free( CurProfile->section );
533 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
534 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
535 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
536 CurProfile->changed = FALSE;
537 CurProfile->section = NULL;
538 CurProfile->dos_name = NULL;
539 CurProfile->unix_name = NULL;
540 CurProfile->filename = NULL;
541 CurProfile->mtime = 0;
545 /***********************************************************************
548 * Open a profile file, checking the cached file first.
550 static BOOL PROFILE_Open( LPCWSTR filename )
552 DOS_FULL_NAME full_name;
553 char buffer[MAX_PATHNAME_LEN];
555 WCHAR *name, *name_lwr;
560 PROFILE *tempProfile;
562 /* First time around */
565 for(i=0;i<N_CACHED_PROFILES;i++)
567 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
568 if(MRUProfile[i] == NULL) break;
569 MRUProfile[i]->changed=FALSE;
570 MRUProfile[i]->section=NULL;
571 MRUProfile[i]->dos_name=NULL;
572 MRUProfile[i]->unix_name=NULL;
573 MRUProfile[i]->filename=NULL;
574 MRUProfile[i]->mtime=0;
577 /* Check for a match */
579 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
580 strchrW( filename, ':' ))
582 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
586 static const WCHAR bkslashW[] = {'\\',0};
587 WCHAR windirW[MAX_PATH];
589 GetWindowsDirectoryW( windirW, MAX_PATH );
590 strcatW( windirW, bkslashW );
591 strcatW( windirW, filename );
592 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
595 for(i=0;i<N_CACHED_PROFILES;i++)
597 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
598 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
603 tempProfile=MRUProfile[i];
605 MRUProfile[j]=MRUProfile[j-1];
606 CurProfile=tempProfile;
608 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
609 TRACE("(%s): already opened (mru=%d)\n",
610 debugstr_w(filename), i );
612 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
613 debugstr_w(filename), i );
618 /* Flush the old current profile */
621 /* Make the oldest profile the current one only in order to get rid of it */
622 if(i==N_CACHED_PROFILES)
624 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
625 for(i=N_CACHED_PROFILES-1;i>0;i--)
626 MRUProfile[i]=MRUProfile[i-1];
627 CurProfile=tempProfile;
629 if(CurProfile->filename) PROFILE_ReleaseFile();
631 /* OK, now that CurProfile is definitely free we assign it our new file */
632 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
633 strcpyW( newdos_name, full_name.short_name );
634 CurProfile->dos_name = newdos_name;
635 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
636 strcpyW( CurProfile->filename, filename );
638 /* Try to open the profile file, first in $HOME/.wine */
640 /* FIXME: this will need a more general solution */
641 strcpy( buffer, wine_get_config_dir() );
642 p = buffer + strlen(buffer);
644 *p = 0; /* make strlen() below happy */
645 name = strrchrW( newdos_name, '\\' ) + 1;
647 /* create a lower cased version of the name */
648 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
649 strcpyW(name_lwr, name);
651 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
652 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
653 HeapFree(GetProcessHeap(), 0, name_lwr);
655 if ((file = fopen( buffer, "r" )))
657 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
658 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
659 strcpy( CurProfile->unix_name, buffer );
663 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
664 strcpy( CurProfile->unix_name, full_name.long_name );
665 if ((file = fopen( full_name.long_name, "r" )))
666 TRACE("(%s): found it in %s\n",
667 debugstr_w(filename), full_name.long_name );
672 CurProfile->section = PROFILE_Load( file );
674 if(!stat(CurProfile->unix_name,&buf))
675 CurProfile->mtime=buf.st_mtime;
679 /* Does not exist yet, we will create it in PROFILE_FlushFile */
680 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
686 /***********************************************************************
689 * Returns all keys of a section.
690 * If return_values is TRUE, also include the corresponding values.
692 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
693 LPWSTR buffer, UINT len, BOOL handle_env,
698 if(!buffer) return 0;
700 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
704 if (section->name[0] && !strcmpiW( section->name, section_name ))
707 for (key = section->key; key; key = key->next)
710 if (!*key->name) continue; /* Skip empty lines */
711 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
712 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
713 len -= strlenW(buffer) + 1;
714 buffer += strlenW(buffer) + 1;
717 if (return_values && key->value) {
719 PROFILE_CopyEntry ( buffer,
720 key->value, len - 1, handle_env, 0 );
721 len -= strlenW(buffer) + 1;
722 buffer += strlenW(buffer) + 1;
727 /*If either lpszSection or lpszKey is NULL and the supplied
728 destination buffer is too small to hold all the strings,
729 the last string is truncated and followed by two null characters.
730 In this case, the return value is equal to cchReturnBuffer
738 section = section->next;
740 buffer[0] = buffer[1] = '\0';
744 /* See GetPrivateProfileSectionNamesA for documentation */
745 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
749 PROFILESECTION *section;
760 section = CurProfile->section;
761 while ((section!=NULL)) {
762 if (section->name[0]) {
763 l = strlenW(section->name)+1;
766 strncpyW(buf, section->name, f-1);
773 strcpyW(buf, section->name);
777 section = section->next;
784 /***********************************************************************
787 * Get a profile string.
789 * Tests with GetPrivateProfileString16, W95a,
790 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
791 * section key_name def_val res buffer
792 * "set1" "1" "x" 43 [data]
793 * "set1" "1 " "x" 43 [data] (!)
794 * "set1" " 1 "' "x" 43 [data] (!)
795 * "set1" "" "x" 1 "x"
796 * "set1" "" "x " 1 "x" (!)
797 * "set1" "" " x " 3 " x" (!)
798 * "set1" NULL "x" 6 "1\02\03\0\0"
799 * "set1" "" "x" 1 "x"
800 * NULL "1" "x" 0 "" (!)
806 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
807 LPCWSTR def_val, LPWSTR buffer, UINT len )
809 PROFILEKEY *key = NULL;
810 static const WCHAR empty_strW[] = { 0 };
812 if(!buffer) return 0;
814 if (!def_val) def_val = empty_strW;
819 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
822 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
823 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
825 TRACE("(%s,%s,%s): returning %s\n",
826 debugstr_w(section), debugstr_w(key_name),
827 debugstr_w(def_val), debugstr_w(buffer) );
828 return strlenW( buffer );
830 /* no "else" here ! */
831 if (section && section[0])
833 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
834 if (!buffer[0]) /* no luck -> def_val */
836 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
837 ret = strlenW(buffer);
846 /***********************************************************************
849 * Set a profile string.
851 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
852 LPCWSTR value, BOOL create_always )
854 if (!key_name) /* Delete a whole section */
856 TRACE("(%s)\n", debugstr_w(section_name));
857 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
859 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
860 this is not an error on application's level.*/
862 else if (!value) /* Delete a key */
864 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
865 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
866 section_name, key_name );
867 return TRUE; /* same error handling as above */
869 else /* Set the key value */
871 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
872 key_name, TRUE, create_always );
873 TRACE("(%s,%s,%s):\n",
874 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
875 if (!key) return FALSE;
878 /* strip the leading spaces. We can safely strip \n\r and
879 * friends too, they should not happen here anyway. */
880 while (PROFILE_isspace(*value)) value++;
882 if (!strcmpW( key->value, value ))
884 TRACE(" no change needed\n" );
885 return TRUE; /* No change needed */
887 TRACE(" replacing %s\n", debugstr_w(key->value) );
888 HeapFree( GetProcessHeap(), 0, key->value );
890 else TRACE(" creating key\n" );
891 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
892 strcpyW( key->value, value );
893 CurProfile->changed = TRUE;
899 /***********************************************************************
902 static HKEY get_profile_key(void)
904 static HKEY profile_key;
908 OBJECT_ATTRIBUTES attr;
909 UNICODE_STRING nameW;
912 attr.Length = sizeof(attr);
913 attr.RootDirectory = 0;
914 attr.ObjectName = &nameW;
916 attr.SecurityDescriptor = NULL;
917 attr.SecurityQualityOfService = NULL;
919 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
920 NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL ))
922 ERR("Cannot create config registry key\n" );
925 RtlFreeUnicodeString( &nameW );
927 if (InterlockedCompareExchangePointer( (void **)&profile_key, hkey, 0 ))
928 NtClose( hkey ); /* somebody beat us to it */
934 /***********************************************************************
935 * PROFILE_GetWineIniString
937 * Get a config string from the wine.ini file.
939 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
940 LPCWSTR def, LPWSTR buffer, int len )
944 OBJECT_ATTRIBUTES attr;
945 UNICODE_STRING nameW;
947 attr.Length = sizeof(attr);
948 attr.RootDirectory = get_profile_key();
949 attr.ObjectName = &nameW;
951 attr.SecurityDescriptor = NULL;
952 attr.SecurityQualityOfService = NULL;
953 RtlInitUnicodeString( &nameW, section );
954 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
956 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
959 RtlInitUnicodeString( &nameW, key_name );
960 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
961 tmp, sizeof(tmp), &count )))
963 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
964 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
969 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
970 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
971 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
972 return strlenW(buffer);
976 /******************************************************************************
978 * PROFILE_GetWineIniBool
980 * Reads a boolean value from the wine.ini file. This function attempts to
981 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
982 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
983 * true. Anything else results in the return of the default value.
985 * This function uses 1 to indicate true, and 0 for false. You can check
986 * for existence by setting def to something other than 0 or 1 and
987 * examining the return value.
989 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
991 static const WCHAR def_valueW[] = {'~',0};
995 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
997 switch(key_value[0]) {
1018 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1019 def ? "TRUE" : "FALSE", key_value[0],
1020 retval ? "TRUE" : "FALSE");
1026 /***********************************************************************
1027 * PROFILE_UsageWineIni
1029 * Explain the wine.ini file to those who don't read documentation.
1030 * Keep below one screenful in length so that error messages above are
1033 void PROFILE_UsageWineIni(void)
1035 MESSAGE("Perhaps you have not properly edited or created "
1036 "your Wine configuration file,\n");
1037 MESSAGE("which is (supposed to be) '%s/config'.\n", wine_get_config_dir());
1041 /********************* API functions **********************************/
1043 /***********************************************************************
1044 * GetProfileInt (KERNEL.57)
1046 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1048 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1052 /***********************************************************************
1053 * GetProfileIntA (KERNEL32.@)
1055 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1057 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1060 /***********************************************************************
1061 * GetProfileIntW (KERNEL32.@)
1063 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1065 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1069 * if allow_section_name_copy is TRUE, allow the copying :
1070 * - of Section names if 'section' is NULL
1071 * - of Keys in a Section if 'entry' is NULL
1072 * (see MSDN doc for GetPrivateProfileString)
1074 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1075 LPCWSTR def_val, LPWSTR buffer,
1076 UINT len, LPCWSTR filename,
1077 BOOL allow_section_name_copy )
1080 LPWSTR pDefVal = NULL;
1085 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1086 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1088 /* strip any trailing ' ' of def_val. */
1091 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1099 if (*p == ' ') /* ouch, contained trailing ' ' */
1101 int len = (int)(p - def_val);
1102 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1103 strncpyW(pDefVal, def_val, len);
1104 pDefVal[len] = '\0';
1108 pDefVal = (LPWSTR)def_val;
1110 EnterCriticalSection( &PROFILE_CritSect );
1112 if (PROFILE_Open( filename )) {
1113 if ((allow_section_name_copy) && (section == NULL))
1114 ret = PROFILE_GetSectionNames(buffer, len);
1116 /* PROFILE_GetString already handles the 'entry == NULL' case */
1117 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1119 lstrcpynW( buffer, pDefVal, len );
1120 ret = strlenW( buffer );
1123 LeaveCriticalSection( &PROFILE_CritSect );
1125 if (pDefVal != def_val) /* allocated */
1126 HeapFree(GetProcessHeap(), 0, pDefVal);
1128 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1133 /***********************************************************************
1134 * GetPrivateProfileString (KERNEL.128)
1136 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1137 LPCSTR def_val, LPSTR buffer,
1138 UINT16 len, LPCSTR filename )
1140 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1142 INT16 retW, ret = 0;
1144 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1145 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1146 else sectionW.Buffer = NULL;
1147 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1148 else entryW.Buffer = NULL;
1149 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1150 else def_valW.Buffer = NULL;
1151 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1152 else filenameW.Buffer = NULL;
1154 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1155 def_valW.Buffer, bufferW, len,
1156 filenameW.Buffer, FALSE );
1159 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1166 ret--; /* strip terminating 0 */
1169 RtlFreeUnicodeString(§ionW);
1170 RtlFreeUnicodeString(&entryW);
1171 RtlFreeUnicodeString(&def_valW);
1172 RtlFreeUnicodeString(&filenameW);
1173 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1177 /***********************************************************************
1178 * GetPrivateProfileStringA (KERNEL32.@)
1180 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1181 LPCSTR def_val, LPSTR buffer,
1182 UINT len, LPCSTR filename )
1184 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1188 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1189 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1190 else sectionW.Buffer = NULL;
1191 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1192 else entryW.Buffer = NULL;
1193 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1194 else def_valW.Buffer = NULL;
1195 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1196 else filenameW.Buffer = NULL;
1198 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1199 def_valW.Buffer, bufferW, len,
1203 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1210 ret--; /* strip terminating 0 */
1213 RtlFreeUnicodeString(§ionW);
1214 RtlFreeUnicodeString(&entryW);
1215 RtlFreeUnicodeString(&def_valW);
1216 RtlFreeUnicodeString(&filenameW);
1217 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1221 /***********************************************************************
1222 * GetPrivateProfileStringW (KERNEL32.@)
1224 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1225 LPCWSTR def_val, LPWSTR buffer,
1226 UINT len, LPCWSTR filename )
1228 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1229 buffer, len, filename, TRUE );
1232 /***********************************************************************
1233 * GetProfileString (KERNEL.58)
1235 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1236 LPSTR buffer, UINT16 len )
1238 return GetPrivateProfileString16( section, entry, def_val,
1239 buffer, len, "win.ini" );
1242 /***********************************************************************
1243 * GetProfileStringA (KERNEL32.@)
1245 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1246 LPSTR buffer, UINT len )
1248 return GetPrivateProfileStringA( section, entry, def_val,
1249 buffer, len, "win.ini" );
1252 /***********************************************************************
1253 * GetProfileStringW (KERNEL32.@)
1255 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1256 LPCWSTR def_val, LPWSTR buffer, UINT len )
1258 return GetPrivateProfileStringW( section, entry, def_val,
1259 buffer, len, wininiW );
1262 /***********************************************************************
1263 * WriteProfileString (KERNEL.59)
1265 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1268 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1271 /***********************************************************************
1272 * WriteProfileStringA (KERNEL32.@)
1274 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1277 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1280 /***********************************************************************
1281 * WriteProfileStringW (KERNEL32.@)
1283 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1286 return WritePrivateProfileStringW( section, entry, string, wininiW );
1290 /***********************************************************************
1291 * GetPrivateProfileInt (KERNEL.127)
1293 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1294 INT16 def_val, LPCSTR filename )
1296 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1297 * here, but Win98SE doesn't care about this at all, so I deleted it.
1298 * AFAIR versions prior to Win9x had these limits, though. */
1299 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1302 /***********************************************************************
1303 * GetPrivateProfileIntW (KERNEL32.@)
1305 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1306 INT def_val, LPCWSTR filename )
1309 UNICODE_STRING bufferW;
1313 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1314 buffer, sizeof(buffer)/sizeof(WCHAR),
1318 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!");
1320 /* FIXME: if entry can be found but it's empty, then Win16 is
1321 * supposed to return 0 instead of def_val ! Difficult/problematic
1322 * to implement (every other failure also returns zero buffer),
1323 * thus wait until testing framework avail for making sure nothing
1324 * else gets broken that way. */
1325 if (!buffer[0]) return (UINT)def_val;
1327 RtlInitUnicodeString( &bufferW, buffer );
1328 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1332 /***********************************************************************
1333 * GetPrivateProfileIntA (KERNEL32.@)
1335 * FIXME: rewrite using unicode
1337 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1338 INT def_val, LPCSTR filename )
1340 UNICODE_STRING entryW, filenameW, sectionW;
1342 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1343 else entryW.Buffer = NULL;
1344 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1345 else filenameW.Buffer = NULL;
1346 if(section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1347 else sectionW.Buffer = NULL;
1348 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1350 RtlFreeUnicodeString(§ionW);
1351 RtlFreeUnicodeString(&filenameW);
1352 RtlFreeUnicodeString(&entryW);
1356 /***********************************************************************
1357 * GetPrivateProfileSection (KERNEL.418)
1359 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1360 UINT16 len, LPCSTR filename )
1362 return GetPrivateProfileSectionA( section, buffer, len, filename );
1365 /***********************************************************************
1366 * GetPrivateProfileSectionW (KERNEL32.@)
1368 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1369 DWORD len, LPCWSTR filename )
1373 EnterCriticalSection( &PROFILE_CritSect );
1375 if (PROFILE_Open( filename ))
1376 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1379 LeaveCriticalSection( &PROFILE_CritSect );
1384 /***********************************************************************
1385 * GetPrivateProfileSectionA (KERNEL32.@)
1387 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1388 DWORD len, LPCSTR filename )
1390 UNICODE_STRING sectionW, filenameW;
1394 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1395 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1396 else sectionW.Buffer = NULL;
1397 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1398 else filenameW.Buffer = NULL;
1400 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1403 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1419 RtlFreeUnicodeString(§ionW);
1420 RtlFreeUnicodeString(&filenameW);
1421 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1425 /***********************************************************************
1426 * GetProfileSection (KERNEL.419)
1428 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1430 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1433 /***********************************************************************
1434 * GetProfileSectionA (KERNEL32.@)
1436 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1438 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1441 /***********************************************************************
1442 * GetProfileSectionW (KERNEL32.@)
1444 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1446 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1450 /***********************************************************************
1451 * WritePrivateProfileString (KERNEL.129)
1453 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1454 LPCSTR string, LPCSTR filename )
1456 return WritePrivateProfileStringA(section,entry,string,filename);
1459 /***********************************************************************
1460 * WritePrivateProfileStringW (KERNEL32.@)
1462 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1463 LPCWSTR string, LPCWSTR filename )
1467 EnterCriticalSection( &PROFILE_CritSect );
1469 if (PROFILE_Open( filename ))
1471 if (!section && !entry && !string) /* documented "file flush" case */
1473 PROFILE_FlushFile();
1474 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1478 FIXME("(NULL?,%s,%s,%s)?\n",
1479 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1481 ret = PROFILE_SetString( section, entry, string, FALSE);
1482 PROFILE_FlushFile();
1487 LeaveCriticalSection( &PROFILE_CritSect );
1491 /***********************************************************************
1492 * WritePrivateProfileStringA (KERNEL32.@)
1494 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1495 LPCSTR string, LPCSTR filename )
1497 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1500 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1501 else sectionW.Buffer = NULL;
1502 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1503 else entryW.Buffer = NULL;
1504 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1505 else stringW.Buffer = NULL;
1506 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1507 else filenameW.Buffer = NULL;
1509 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1510 stringW.Buffer, filenameW.Buffer);
1511 RtlFreeUnicodeString(§ionW);
1512 RtlFreeUnicodeString(&entryW);
1513 RtlFreeUnicodeString(&stringW);
1514 RtlFreeUnicodeString(&filenameW);
1518 /***********************************************************************
1519 * WritePrivateProfileSection (KERNEL.416)
1521 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1522 LPCSTR string, LPCSTR filename )
1524 return WritePrivateProfileSectionA( section, string, filename );
1527 /***********************************************************************
1528 * WritePrivateProfileSectionW (KERNEL32.@)
1530 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1531 LPCWSTR string, LPCWSTR filename )
1536 EnterCriticalSection( &PROFILE_CritSect );
1538 if (PROFILE_Open( filename )) {
1539 if (!section && !string)
1540 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1541 else if (!string) {/* delete the named section*/
1542 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1543 PROFILE_FlushFile();
1545 PROFILE_DeleteAllKeys(section);
1548 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1549 strcpyW( buf, string );
1550 if((p = strchrW( buf, '='))) {
1552 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1554 HeapFree( GetProcessHeap(), 0, buf );
1555 string += strlenW(string)+1;
1557 PROFILE_FlushFile();
1561 LeaveCriticalSection( &PROFILE_CritSect );
1565 /***********************************************************************
1566 * WritePrivateProfileSectionA (KERNEL32.@)
1568 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1569 LPCSTR string, LPCSTR filename)
1572 UNICODE_STRING sectionW, filenameW;
1581 while(*p) p += strlen(p) + 1;
1582 lenA = p - string + 1;
1583 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1584 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1585 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1587 else stringW = NULL;
1588 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1589 else sectionW.Buffer = NULL;
1590 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1591 else filenameW.Buffer = NULL;
1593 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1595 HeapFree(GetProcessHeap(), 0, stringW);
1596 RtlFreeUnicodeString(§ionW);
1597 RtlFreeUnicodeString(&filenameW);
1601 /***********************************************************************
1602 * WriteProfileSection (KERNEL.417)
1604 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1606 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1609 /***********************************************************************
1610 * WriteProfileSectionA (KERNEL32.@)
1612 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1615 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1618 /***********************************************************************
1619 * WriteProfileSectionW (KERNEL32.@)
1621 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1623 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1626 /***********************************************************************
1627 * GetPrivateProfileSectionNames (KERNEL.143)
1629 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1632 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1636 /***********************************************************************
1637 * GetProfileSectionNames (KERNEL.142)
1639 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1642 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1646 /***********************************************************************
1647 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1649 * Returns the section names contained in the specified file.
1650 * FIXME: Where do we find this file when the path is relative?
1651 * The section names are returned as a list of strings with an extra
1652 * '\0' to mark the end of the list. Except for that the behavior
1653 * depends on the Windows version.
1656 * - if the buffer is 0 or 1 character long then it is as if it was of
1658 * - otherwise, if the buffer is to small only the section names that fit
1660 * - note that this means if the buffer was to small to return even just
1661 * the first section name then a single '\0' will be returned.
1662 * - the return value is the number of characters written in the buffer,
1663 * except if the buffer was too smal in which case len-2 is returned
1666 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1667 * '\0' and the return value is 0
1668 * - otherwise if the buffer is too small then the first section name that
1669 * does not fit is truncated so that the string list can be terminated
1670 * correctly (double '\0')
1671 * - the return value is the number of characters written in the buffer
1672 * except for the trailing '\0'. If the buffer is too small, then the
1673 * return value is len-2
1674 * - Win2000 has a bug that triggers when the section names and the
1675 * trailing '\0' fit exactly in the buffer. In that case the trailing
1678 * Wine implements the observed Win2000 behavior (except for the bug).
1680 * Note that when the buffer is big enough then the return value may be any
1681 * value between 1 and len-1 (or len in Win95), including len-2.
1683 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1688 EnterCriticalSection( &PROFILE_CritSect );
1690 if (PROFILE_Open( filename ))
1691 ret = PROFILE_GetSectionNames(buffer, size);
1693 LeaveCriticalSection( &PROFILE_CritSect );
1699 /***********************************************************************
1700 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1702 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1705 UNICODE_STRING filenameW;
1709 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1710 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1711 else filenameW.Buffer = NULL;
1713 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1716 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1724 RtlFreeUnicodeString(&filenameW);
1725 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1729 /***********************************************************************
1730 * GetPrivateProfileStruct (KERNEL.407)
1732 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1733 LPVOID buf, UINT16 len, LPCSTR filename)
1735 return GetPrivateProfileStructA( section, key, buf, len, filename );
1738 /***********************************************************************
1739 * GetPrivateProfileStructW (KERNEL32.@)
1741 * Should match Win95's behaviour pretty much
1743 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1744 LPVOID buf, UINT len, LPCWSTR filename)
1748 EnterCriticalSection( &PROFILE_CritSect );
1750 if (PROFILE_Open( filename )) {
1751 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1753 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1754 if (((strlenW(k->value) - 2) / 2) == len)
1761 end = k->value + strlenW(k->value); /* -> '\0' */
1762 /* check for invalid chars in ASCII coded hex string */
1763 for (p=k->value; p < end; p++)
1767 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1768 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1775 BOOL highnibble = TRUE;
1777 LPBYTE binbuf = (LPBYTE)buf;
1779 end -= 2; /* don't include checksum in output data */
1780 /* translate ASCII hex format into binary data */
1781 for (p=k->value; p < end; p++)
1785 (c - 'A' + 10) : (c - '0');
1792 *binbuf++ = b; /* feed binary data into output */
1793 chksum += b; /* calculate checksum */
1795 highnibble ^= 1; /* toggle */
1797 /* retrieve stored checksum value */
1799 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1801 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1802 if (b == (chksum & 0xff)) /* checksums match ? */
1808 LeaveCriticalSection( &PROFILE_CritSect );
1813 /***********************************************************************
1814 * GetPrivateProfileStructA (KERNEL32.@)
1816 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1817 LPVOID buffer, UINT len, LPCSTR filename)
1819 UNICODE_STRING sectionW, keyW, filenameW;
1822 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1823 else sectionW.Buffer = NULL;
1824 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1825 else keyW.Buffer = NULL;
1826 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1827 else filenameW.Buffer = NULL;
1829 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1831 /* Do not translate binary data. */
1833 RtlFreeUnicodeString(§ionW);
1834 RtlFreeUnicodeString(&keyW);
1835 RtlFreeUnicodeString(&filenameW);
1841 /***********************************************************************
1842 * WritePrivateProfileStruct (KERNEL.406)
1844 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1845 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1847 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1850 /***********************************************************************
1851 * WritePrivateProfileStructW (KERNEL32.@)
1853 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1854 LPVOID buf, UINT bufsize, LPCWSTR filename)
1858 LPWSTR outstring, p;
1861 if (!section && !key && !buf) /* flush the cache */
1862 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1864 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1865 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1867 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1868 *p++ = hex[*binbuf >> 4];
1869 *p++ = hex[*binbuf & 0xf];
1872 /* checksum is sum & 0xff */
1873 *p++ = hex[(sum & 0xf0) >> 4];
1874 *p++ = hex[sum & 0xf];
1877 EnterCriticalSection( &PROFILE_CritSect );
1879 if (PROFILE_Open( filename )) {
1880 ret = PROFILE_SetString( section, key, outstring, FALSE);
1881 PROFILE_FlushFile();
1884 LeaveCriticalSection( &PROFILE_CritSect );
1886 HeapFree( GetProcessHeap(), 0, outstring );
1891 /***********************************************************************
1892 * WritePrivateProfileStructA (KERNEL32.@)
1894 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1895 LPVOID buf, UINT bufsize, LPCSTR filename)
1897 UNICODE_STRING sectionW, keyW, filenameW;
1900 if (section) RtlCreateUnicodeStringFromAsciiz(§ionW, section);
1901 else sectionW.Buffer = NULL;
1902 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1903 else keyW.Buffer = NULL;
1904 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1905 else filenameW.Buffer = NULL;
1907 /* Do not translate binary data. */
1908 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1911 RtlFreeUnicodeString(§ionW);
1912 RtlFreeUnicodeString(&keyW);
1913 RtlFreeUnicodeString(&filenameW);
1918 /***********************************************************************
1919 * WriteOutProfiles (KERNEL.315)
1921 void WINAPI WriteOutProfiles16(void)
1923 EnterCriticalSection( &PROFILE_CritSect );
1924 PROFILE_FlushFile();
1925 LeaveCriticalSection( &PROFILE_CritSect );
1928 /***********************************************************************
1929 * CloseProfileUserMapping (KERNEL32.@)
1931 BOOL WINAPI CloseProfileUserMapping(void) {
1932 FIXME("(), stub!\n");
1933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);