4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
15 #include <sys/types.h>
23 #include "wine/winbase16.h"
27 #include "debugtools.h"
29 #include "wine/server.h"
31 DEFAULT_DEBUG_CHANNEL(profile);
33 typedef struct tagPROFILEKEY
36 struct tagPROFILEKEY *next;
40 typedef struct tagPROFILESECTION
42 struct tagPROFILEKEY *key;
43 struct tagPROFILESECTION *next;
51 PROFILESECTION *section;
59 #define N_CACHED_PROFILES 10
61 /* Cached profile files */
62 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
64 #define CurProfile (MRUProfile[0])
66 /* wine.ini config file registry root */
67 static HKEY wine_profile_key;
69 #define PROFILE_MAX_LINE_LEN 1024
71 /* Wine profile name in $HOME directory; must begin with slash */
72 static const char PROFILE_WineIniName[] = "/.winerc";
74 /* Wine profile: the profile file being used */
75 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
77 /* Check for comments in profile */
78 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
80 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
82 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
84 static const char hex[16] = "0123456789ABCDEF";
86 /***********************************************************************
89 * Copy the content of an entry into a buffer, removing quotes, and possibly
90 * translating environment variables.
92 static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
100 if ((*value == '\'') || (*value == '\"'))
102 if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
107 lstrcpynA( buffer, value, len );
108 if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
112 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
114 if ((*p == '$') && (p[1] == '{'))
118 const char *p2 = strchr( p, '}' );
119 if (!p2) continue; /* ignore it */
120 lstrcpynA(env_val, p + 2, min( sizeof(env_val), (int)(p2-p)-1 ));
121 if ((env_p = getenv( env_val )) != NULL)
124 lstrcpynA( buffer, env_p, len );
125 buffer_len = strlen( buffer );
126 buffer += buffer_len;
132 if (quote && (len > 1)) buffer--;
137 /***********************************************************************
140 * Save a profile tree to a file.
142 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
146 for ( ; section; section = section->next)
148 if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name );
149 for (key = section->key; key; key = key->next)
151 fprintf( file, "%s", key->name );
152 if (key->value) fprintf( file, "=%s", key->value );
153 fprintf( file, "\r\n" );
159 /***********************************************************************
162 * Free a profile tree.
164 static void PROFILE_Free( PROFILESECTION *section )
166 PROFILESECTION *next_section;
167 PROFILEKEY *key, *next_key;
169 for ( ; section; section = next_section)
171 for (key = section->key; key; key = next_key)
173 next_key = key->next;
174 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
175 HeapFree( GetProcessHeap(), 0, key );
177 next_section = section->next;
178 HeapFree( GetProcessHeap(), 0, section );
182 static inline int PROFILE_isspace(char c)
184 if (isspace(c)) return 1;
185 if (c=='\r' || c==0x1a) return 1;
186 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
191 /***********************************************************************
194 * Load a profile tree from a file.
196 static PROFILESECTION *PROFILE_Load( FILE *file )
198 char buffer[PROFILE_MAX_LINE_LEN];
201 PROFILESECTION *section, *first_section;
202 PROFILESECTION **next_section;
203 PROFILEKEY *key, *prev_key, **next_key;
205 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
206 if(first_section == NULL) return NULL;
207 first_section->name[0] = 0;
208 first_section->key = NULL;
209 first_section->next = NULL;
210 next_section = &first_section->next;
211 next_key = &first_section->key;
214 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
218 while (*p && PROFILE_isspace(*p)) p++;
219 if (*p == '[') /* section start */
221 if (!(p2 = strrchr( p, ']' )))
223 WARN("Invalid section header at line %d: '%s'\n",
230 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + strlen(p) )))
232 strcpy( section->name, p );
234 section->next = NULL;
235 *next_section = section;
236 next_section = §ion->next;
237 next_key = §ion->key;
240 TRACE("New section: '%s'\n",section->name);
247 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
249 if ((p2 = strchr( p, '=' )) != NULL)
252 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
254 while (*p2 && PROFILE_isspace(*p2)) p2++;
257 if(*p || !prev_key || *prev_key->name)
259 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + strlen(p) ))) break;
260 strcpy( key->name, p );
263 key->value = HeapAlloc( GetProcessHeap(), 0, strlen(p2)+1 );
264 strcpy( key->value, p2 );
266 else key->value = NULL;
270 next_key = &key->next;
273 TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)");
276 return first_section;
279 /* convert the .winerc file to the new format */
280 static void convert_config( FILE *in, const char *output_name )
282 char buffer[PROFILE_MAX_LINE_LEN];
286 /* create the output file, only if it doesn't exist already */
287 int fd = open( output_name, O_WRONLY|O_CREAT|O_EXCL, 0666 );
290 MESSAGE( "Could not create new config file '%s': %s\n", output_name, strerror(errno) );
294 out = fdopen( fd, "w" );
295 fprintf( out, "WINE REGISTRY Version 2\n" );
296 fprintf( out, ";; All keys relative to \\\\Machine\\\\Software\\\\Wine\\\\Wine\\\\Config\n\n" );
297 while (fgets( buffer, PROFILE_MAX_LINE_LEN, in ))
299 if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0;
301 while (*p && PROFILE_isspace(*p)) p++;
302 if (*p == '[') /* section start */
304 if ((p2 = strrchr( p, ']' )))
308 fprintf( out, "[%s]\n", p );
313 if (*p == ';' || *p == '#')
315 fprintf( out, "%s\n", p );
320 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
322 if ((p2 = strchr( p, '=' )) != NULL)
325 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
327 while (*p2 && PROFILE_isspace(*p2)) p2++;
332 fprintf( out, "\n" );
338 if (*p == '\\') fputc( '\\', out );
342 fprintf( out, "\" = \"" );
347 if (*p2 == '\\') fputc( '\\', out );
352 fprintf( out, "\"\n" );
358 /***********************************************************************
359 * PROFILE_DeleteSection
361 * Delete a section from a profile tree.
363 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
367 if ((*section)->name[0] && !strcasecmp( (*section)->name, name ))
369 PROFILESECTION *to_del = *section;
370 *section = to_del->next;
372 PROFILE_Free( to_del );
375 section = &(*section)->next;
381 /***********************************************************************
384 * Delete a key from a profile tree.
386 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
387 LPCSTR section_name, LPCSTR key_name )
391 if ((*section)->name[0] && !strcasecmp( (*section)->name, section_name ))
393 PROFILEKEY **key = &(*section)->key;
396 if (!strcasecmp( (*key)->name, key_name ))
398 PROFILEKEY *to_del = *key;
400 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
401 HeapFree( GetProcessHeap(), 0, to_del );
407 section = &(*section)->next;
413 /***********************************************************************
414 * PROFILE_DeleteAllKeys
416 * Delete all keys from a profile tree.
418 void PROFILE_DeleteAllKeys( LPCSTR section_name)
420 PROFILESECTION **section= &CurProfile->section;
423 if ((*section)->name[0] && !strcasecmp( (*section)->name, section_name ))
425 PROFILEKEY **key = &(*section)->key;
428 PROFILEKEY *to_del = *key;
430 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
431 HeapFree( GetProcessHeap(), 0, to_del );
432 CurProfile->changed =TRUE;
435 section = &(*section)->next;
440 /***********************************************************************
443 * Find a key in a profile tree, optionally creating it.
445 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
446 const char *section_name,
447 const char *key_name, int create )
452 while (PROFILE_isspace(*section_name)) section_name++;
453 p = section_name + strlen(section_name) - 1;
454 while ((p > section_name) && PROFILE_isspace(*p)) p--;
455 seclen = p - section_name + 1;
457 while (PROFILE_isspace(*key_name)) key_name++;
458 p = key_name + strlen(key_name) - 1;
459 while ((p > key_name) && PROFILE_isspace(*p)) p--;
460 keylen = p - key_name + 1;
464 if ( ((*section)->name[0])
465 && (!(strncasecmp( (*section)->name, section_name, seclen )))
466 && (((*section)->name)[seclen] == '\0') )
468 PROFILEKEY **key = &(*section)->key;
471 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
472 && (((*key)->name)[keylen] == '\0') )
476 if (!create) return NULL;
477 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlen(key_name) )))
479 strcpy( (*key)->name, key_name );
480 (*key)->value = NULL;
484 section = &(*section)->next;
486 if (!create) return NULL;
487 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlen(section_name) );
488 if(*section == NULL) return NULL;
489 strcpy( (*section)->name, section_name );
490 (*section)->next = NULL;
491 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
492 sizeof(PROFILEKEY) + strlen(key_name) )))
494 HeapFree(GetProcessHeap(), 0, *section);
497 strcpy( (*section)->key->name, key_name );
498 (*section)->key->value = NULL;
499 (*section)->key->next = NULL;
500 return (*section)->key;
504 /***********************************************************************
507 * Flush the current profile to disk if changed.
509 static BOOL PROFILE_FlushFile(void)
511 char *p, buffer[MAX_PATHNAME_LEN];
512 const char *unix_name;
518 WARN("No current profile!\n");
522 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
523 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
525 /* Try to create it in $HOME/.wine */
526 /* FIXME: this will need a more general solution */
527 strcpy( buffer, get_config_dir() );
528 p = buffer + strlen(buffer);
530 strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
532 file = fopen( buffer, "w" );
538 WARN("could not save profile file %s\n", CurProfile->dos_name);
542 TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
543 PROFILE_Save( file, CurProfile->section );
545 CurProfile->changed = FALSE;
546 if(!stat(unix_name,&buf))
547 CurProfile->mtime=buf.st_mtime;
552 /***********************************************************************
553 * PROFILE_ReleaseFile
555 * Flush the current profile to disk and remove it from the cache.
557 static void PROFILE_ReleaseFile(void)
560 PROFILE_Free( CurProfile->section );
561 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
562 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
563 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
564 CurProfile->changed = FALSE;
565 CurProfile->section = NULL;
566 CurProfile->dos_name = NULL;
567 CurProfile->unix_name = NULL;
568 CurProfile->filename = NULL;
569 CurProfile->mtime = 0;
573 /***********************************************************************
576 * Open a profile file, checking the cached file first.
578 static BOOL PROFILE_Open( LPCSTR filename )
580 DOS_FULL_NAME full_name;
581 char buffer[MAX_PATHNAME_LEN];
582 char *newdos_name, *p;
586 PROFILE *tempProfile;
588 /* First time around */
591 for(i=0;i<N_CACHED_PROFILES;i++)
593 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
594 if(MRUProfile[i] == NULL) break;
595 MRUProfile[i]->changed=FALSE;
596 MRUProfile[i]->section=NULL;
597 MRUProfile[i]->dos_name=NULL;
598 MRUProfile[i]->unix_name=NULL;
599 MRUProfile[i]->filename=NULL;
600 MRUProfile[i]->mtime=0;
603 /* Check for a match */
605 if (strchr( filename, '/' ) || strchr( filename, '\\' ) ||
606 strchr( filename, ':' ))
608 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
612 GetWindowsDirectoryA( buffer, sizeof(buffer) );
613 strcat( buffer, "\\" );
614 strcat( buffer, filename );
615 if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
618 for(i=0;i<N_CACHED_PROFILES;i++)
620 if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
621 (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
626 tempProfile=MRUProfile[i];
628 MRUProfile[j]=MRUProfile[j-1];
629 CurProfile=tempProfile;
631 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
632 TRACE("(%s): already opened (mru=%d)\n",
635 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
641 /* Flush the old current profile */
644 /* Make the oldest profile the current one only in order to get rid of it */
645 if(i==N_CACHED_PROFILES)
647 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
648 for(i=N_CACHED_PROFILES-1;i>0;i--)
649 MRUProfile[i]=MRUProfile[i-1];
650 CurProfile=tempProfile;
652 if(CurProfile->filename) PROFILE_ReleaseFile();
654 /* OK, now that CurProfile is definitely free we assign it our new file */
655 newdos_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.short_name)+1 );
656 strcpy( newdos_name, full_name.short_name );
657 CurProfile->dos_name = newdos_name;
658 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, strlen(filename)+1 );
659 strcpy( CurProfile->filename, filename );
661 /* Try to open the profile file, first in $HOME/.wine */
663 /* FIXME: this will need a more general solution */
664 strcpy( buffer, get_config_dir() );
665 p = buffer + strlen(buffer);
667 strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
669 if ((file = fopen( buffer, "r" )))
671 TRACE("(%s): found it in %s\n",
673 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
674 strcpy( CurProfile->unix_name, buffer );
679 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
680 strcpy( CurProfile->unix_name, full_name.long_name );
681 if ((file = fopen( full_name.long_name, "r" )))
682 TRACE("(%s): found it in %s\n",
683 filename, full_name.long_name );
688 CurProfile->section = PROFILE_Load( file );
690 if(!stat(CurProfile->unix_name,&buf))
691 CurProfile->mtime=buf.st_mtime;
695 /* Does not exist yet, we will create it in PROFILE_FlushFile */
696 WARN("profile file %s not found\n", newdos_name );
702 /***********************************************************************
705 * Returns all keys of a section.
706 * If return_values is TRUE, also include the corresponding values.
708 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
709 LPSTR buffer, UINT len, BOOL handle_env,
714 if(!buffer) return 0;
718 if (section->name[0] && !strcasecmp( section->name, section_name ))
721 for (key = section->key; key; key = key->next)
724 if (!*key->name) continue; /* Skip empty lines */
725 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
726 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
727 len -= strlen(buffer) + 1;
728 buffer += strlen(buffer) + 1;
731 if (return_values && key->value) {
733 PROFILE_CopyEntry ( buffer,
734 key->value, len - 1, handle_env );
735 len -= strlen(buffer) + 1;
736 buffer += strlen(buffer) + 1;
741 /*If either lpszSection or lpszKey is NULL and the supplied
742 destination buffer is too small to hold all the strings,
743 the last string is truncated and followed by two null characters.
744 In this case, the return value is equal to cchReturnBuffer
752 section = section->next;
754 buffer[0] = buffer[1] = '\0';
758 /* See GetPrivateProfileSectionNamesA for documentation */
759 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
763 PROFILESECTION *section;
774 section = CurProfile->section;
775 while ((section!=NULL)) {
776 if (section->name[0]) {
777 l = strlen(section->name)+1;
780 strncpy(buf, section->name, f-1);
787 strcpy(buf, section->name);
791 section = section->next;
798 /***********************************************************************
801 * Get a profile string.
803 * Tests with GetPrivateProfileString16, W95a,
804 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
805 * section key_name def_val res buffer
806 * "set1" "1" "x" 43 [data]
807 * "set1" "1 " "x" 43 [data] (!)
808 * "set1" " 1 "' "x" 43 [data] (!)
809 * "set1" "" "x" 1 "x"
810 * "set1" "" "x " 1 "x" (!)
811 * "set1" "" " x " 3 " x" (!)
812 * "set1" NULL "x" 6 "1\02\03\0\0"
813 * "set1" "" "x" 1 "x"
814 * NULL "1" "x" 0 "" (!)
820 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
821 LPCSTR def_val, LPSTR buffer, UINT len )
823 PROFILEKEY *key = NULL;
825 if(!buffer) return 0;
827 if (!def_val) def_val = "";
828 if (key_name && key_name[0])
830 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
831 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
833 TRACE("('%s','%s','%s'): returning '%s'\n",
834 section, key_name, def_val, buffer );
835 return strlen( buffer );
837 if (key_name && !(key_name[0]))
838 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
840 if (section && section[0])
841 return PROFILE_GetSection(CurProfile->section, section, buffer, len,
848 /***********************************************************************
851 * Set a profile string.
853 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
856 if (!key_name) /* Delete a whole section */
858 TRACE("('%s')\n", section_name);
859 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
861 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
862 this is not an error on application's level.*/
864 else if (!value) /* Delete a key */
866 TRACE("('%s','%s')\n",
867 section_name, key_name );
868 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
869 section_name, key_name );
870 return TRUE; /* same error handling as above */
872 else /* Set the key value */
874 PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
876 TRACE("('%s','%s','%s'): \n",
877 section_name, key_name, value );
878 if (!key) return FALSE;
881 /* strip the leading spaces. We can safely strip \n\r and
882 * friends too, they should not happen here anyway. */
883 while (PROFILE_isspace(*value)) value++;
885 if (!strcmp( key->value, value ))
887 TRACE(" no change needed\n" );
888 return TRUE; /* No change needed */
890 TRACE(" replacing '%s'\n", key->value );
891 HeapFree( GetProcessHeap(), 0, key->value );
893 else TRACE(" creating key\n" );
894 key->value = HeapAlloc( GetProcessHeap(), 0, strlen(value)+1 );
895 strcpy( key->value, value );
896 CurProfile->changed = TRUE;
902 /***********************************************************************
903 * PROFILE_GetWineIniString
905 * Get a config string from the wine.ini file.
907 int PROFILE_GetWineIniString( const char *section, const char *key_name,
908 const char *def, char *buffer, int len )
910 char tmp[PROFILE_MAX_LINE_LEN];
914 if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey )))
917 DWORD count = sizeof(tmp);
918 err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count );
921 PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE );
922 TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer );
923 return strlen(buffer);
927 /***********************************************************************
928 * PROFILE_EnumWineIniString
930 * Get a config string from the wine.ini file.
932 BOOL PROFILE_EnumWineIniString( const char *section, int index,
933 char *name, int name_len, char *buffer, int len )
935 char tmp[PROFILE_MAX_LINE_LEN];
938 DWORD count = sizeof(tmp);
940 if (RegOpenKeyA( wine_profile_key, section, &hkey )) return FALSE;
941 err = RegEnumValueA( hkey, index, name, (DWORD*)&name_len, NULL, &type, tmp, &count );
945 PROFILE_CopyEntry( buffer, tmp, len, TRUE );
946 TRACE( "('%s',%d): returning '%s'='%s'\n", section, index, name, buffer );
952 /***********************************************************************
953 * PROFILE_GetWineIniInt
955 * Get a config integer from the wine.ini file.
957 int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
963 PROFILE_GetWineIniString( section, key_name, "", buffer, sizeof(buffer) );
964 if (!buffer[0]) return def;
965 result = strtol( buffer, &p, 0 );
966 return (p == buffer) ? 0 /* No digits at all */ : (int)result;
970 /******************************************************************************
972 * int PROFILE_GetWineIniBool(
973 * char const *section,
974 * char const *key_name,
977 * Reads a boolean value from the wine.ini file. This function attempts to
978 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
979 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
980 * true. Anything else results in the return of the default value.
982 * This function uses 1 to indicate true, and 0 for false. You can check
983 * for existence by setting def to something other than 0 or 1 and
984 * examining the return value.
986 int PROFILE_GetWineIniBool(
988 char const *key_name,
994 PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
996 switch(key_value[0]) {
1017 TRACE("(\"%s\", \"%s\", %s), [%c], ret %s.\n", section, key_name,
1018 def ? "TRUE" : "FALSE", key_value[0],
1019 retval ? "TRUE" : "FALSE");
1025 /***********************************************************************
1026 * PROFILE_LoadWineIni
1028 * Load the old .winerc file.
1030 int PROFILE_LoadWineIni(void)
1032 OBJECT_ATTRIBUTES attr;
1033 UNICODE_STRING nameW;
1034 char buffer[MAX_PATHNAME_LEN];
1040 attr.Length = sizeof(attr);
1041 attr.RootDirectory = 0;
1042 attr.ObjectName = &nameW;
1043 attr.Attributes = 0;
1044 attr.SecurityDescriptor = NULL;
1045 attr.SecurityQualityOfService = NULL;
1047 /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1048 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine" ) ||
1049 NtCreateKey( &hKeySW, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp ))
1051 ERR("Cannot create config registry key\n" );
1054 RtlFreeUnicodeString( &nameW );
1057 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1058 NtCreateKey( &wine_profile_key, KEY_ALL_ACCESS, &attr, 0,
1059 NULL, REG_OPTION_VOLATILE, &disp ))
1061 ERR("Cannot create config registry key\n" );
1064 RtlFreeUnicodeString( &nameW );
1066 if (!CLIENT_IsBootThread()) return 1; /* already loaded */
1068 if ((p = getenv( "HOME" )) != NULL)
1070 lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
1071 strcat( buffer, PROFILE_WineIniName );
1072 if ((f = fopen( buffer, "r" )) != NULL)
1074 lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
1078 else WARN("could not get $HOME value for config file.\n" );
1080 if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
1082 MESSAGE( "Can't open configuration file %s/config\n",get_config_dir() );
1087 if (disp == REG_OPENED_EXISTING_KEY)
1089 MESSAGE( "Warning: configuration loaded by the server from '%s/config',\n"
1090 " file '%s' was ignored.\n", get_config_dir(), PROFILE_WineIniUsed );
1095 /* convert to the new format */
1096 sprintf( buffer, "%s/config", get_config_dir() );
1097 convert_config( f, buffer );
1100 MESSAGE( "The '%s' configuration file has been converted\n"
1101 "to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
1102 MESSAGE( "You should verify that the contents of the new file are correct,\n"
1103 "and then remove the old one and restart Wine.\n" );
1108 /***********************************************************************
1109 * PROFILE_UsageWineIni
1111 * Explain the wine.ini file to those who don't read documentation.
1112 * Keep below one screenful in length so that error messages above are
1115 void PROFILE_UsageWineIni(void)
1117 MESSAGE("Perhaps you have not properly edited or created "
1118 "your Wine configuration file.\n");
1119 MESSAGE("This is (supposed to be) '%s/config'\n", get_config_dir());
1120 /* RTFM, so to say */
1123 /***********************************************************************
1124 * PROFILE_GetStringItem
1126 * Convenience function that turns a string 'xxx, yyy, zzz' into
1127 * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1129 char* PROFILE_GetStringItem( char* start )
1133 for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
1137 if( lpch ) *lpch = '\0'; else *lpchX = '\0';
1139 if( !PROFILE_isspace(*lpchX) ) return lpchX;
1141 else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
1144 if( lpch ) *lpch = '\0';
1148 /********************* API functions **********************************/
1150 /***********************************************************************
1151 * GetProfileInt (KERNEL.57)
1153 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1155 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1159 /***********************************************************************
1160 * GetProfileIntA (KERNEL32.@)
1162 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1164 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1167 /***********************************************************************
1168 * GetProfileIntW (KERNEL32.@)
1170 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1172 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1176 * if allow_section_name_copy is TRUE, allow the copying :
1177 * - of Section names if 'section' is NULL
1178 * - of Keys in a Section if 'entry' is NULL
1179 * (see MSDN doc for GetPrivateProfileString)
1181 static int PROFILE_GetPrivateProfileString( LPCSTR section, LPCSTR entry,
1182 LPCSTR def_val, LPSTR buffer,
1183 UINT16 len, LPCSTR filename,
1184 BOOL allow_section_name_copy )
1187 LPSTR pDefVal = NULL;
1190 filename = "win.ini";
1192 /* strip any trailing ' ' of def_val. */
1195 LPSTR p = (LPSTR)&def_val[strlen(def_val)]; /* even "" works ! */
1203 if (*p == ' ') /* ouch, contained trailing ' ' */
1205 int len = (int)p - (int)def_val;
1206 pDefVal = HeapAlloc(GetProcessHeap(), 0, len + 1);
1207 strncpy(pDefVal, def_val, len);
1208 pDefVal[len] = '\0';
1212 pDefVal = (LPSTR)def_val;
1214 EnterCriticalSection( &PROFILE_CritSect );
1216 if (PROFILE_Open( filename )) {
1217 if ((allow_section_name_copy) && (section == NULL))
1218 ret = PROFILE_GetSectionNames(buffer, len);
1220 /* PROFILE_GetString already handles the 'entry == NULL' case */
1221 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1223 lstrcpynA( buffer, pDefVal, len );
1224 ret = strlen( buffer );
1227 LeaveCriticalSection( &PROFILE_CritSect );
1229 if (pDefVal != def_val) /* allocated */
1230 HeapFree(GetProcessHeap(), 0, pDefVal);
1235 /***********************************************************************
1236 * GetPrivateProfileString (KERNEL.128)
1238 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1239 LPCSTR def_val, LPSTR buffer,
1240 UINT16 len, LPCSTR filename )
1242 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1243 buffer, len, filename, FALSE );
1246 /***********************************************************************
1247 * GetPrivateProfileStringA (KERNEL32.@)
1249 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1250 LPCSTR def_val, LPSTR buffer,
1251 UINT len, LPCSTR filename )
1253 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1254 buffer, len, filename, TRUE );
1257 /***********************************************************************
1258 * GetPrivateProfileStringW (KERNEL32.@)
1260 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1261 LPCWSTR def_val, LPWSTR buffer,
1262 UINT len, LPCWSTR filename )
1264 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1265 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1266 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1267 LPSTR def_valA = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1268 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1269 INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1270 bufferA, len, filenameA );
1271 if (len > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, len ))
1273 HeapFree( GetProcessHeap(), 0, sectionA );
1274 HeapFree( GetProcessHeap(), 0, entryA );
1275 HeapFree( GetProcessHeap(), 0, filenameA );
1276 HeapFree( GetProcessHeap(), 0, def_valA );
1277 HeapFree( GetProcessHeap(), 0, bufferA);
1281 /***********************************************************************
1282 * GetProfileString (KERNEL.58)
1284 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1285 LPSTR buffer, UINT16 len )
1287 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1288 buffer, len, "win.ini", FALSE );
1291 /***********************************************************************
1292 * GetProfileStringA (KERNEL32.@)
1294 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1295 LPSTR buffer, UINT len )
1297 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1298 buffer, len, "win.ini", TRUE );
1301 /***********************************************************************
1302 * GetProfileStringW (KERNEL32.@)
1304 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1305 LPCWSTR def_val, LPWSTR buffer, UINT len )
1307 return GetPrivateProfileStringW( section, entry, def_val,
1308 buffer, len, wininiW );
1311 /***********************************************************************
1312 * WriteProfileString (KERNEL.59)
1314 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1317 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1320 /***********************************************************************
1321 * WriteProfileStringA (KERNEL32.@)
1323 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1326 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1329 /***********************************************************************
1330 * WriteProfileStringW (KERNEL32.@)
1332 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1335 return WritePrivateProfileStringW( section, entry, string, wininiW );
1339 /***********************************************************************
1340 * GetPrivateProfileInt (KERNEL.127)
1342 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1343 INT16 def_val, LPCSTR filename )
1345 long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename);
1347 if (result > 65535) return 65535;
1348 if (result >= 0) return (UINT16)result;
1349 if (result < -32768) return -32768;
1350 return (UINT16)(INT16)result;
1353 /***********************************************************************
1354 * GetPrivateProfileIntA (KERNEL32.@)
1356 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1357 INT def_val, LPCSTR filename )
1363 PROFILE_GetPrivateProfileString( section, entry, "",
1364 buffer, sizeof(buffer), filename, FALSE );
1365 if (!buffer[0]) return (UINT)def_val;
1366 result = strtol( buffer, &p, 0 );
1367 if (p == buffer) return 0; /* No digits at all */
1368 return (UINT)result;
1371 /***********************************************************************
1372 * GetPrivateProfileIntW (KERNEL32.@)
1374 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1375 INT def_val, LPCWSTR filename )
1377 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1378 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1379 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1380 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1381 HeapFree( GetProcessHeap(), 0, sectionA );
1382 HeapFree( GetProcessHeap(), 0, filenameA );
1383 HeapFree( GetProcessHeap(), 0, entryA );
1387 /***********************************************************************
1388 * GetPrivateProfileSection (KERNEL.418)
1390 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1391 UINT16 len, LPCSTR filename )
1393 return GetPrivateProfileSectionA( section, buffer, len, filename );
1396 /***********************************************************************
1397 * GetPrivateProfileSectionA (KERNEL32.@)
1399 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1400 DWORD len, LPCSTR filename )
1404 EnterCriticalSection( &PROFILE_CritSect );
1406 if (PROFILE_Open( filename ))
1407 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1410 LeaveCriticalSection( &PROFILE_CritSect );
1415 /***********************************************************************
1416 * GetPrivateProfileSectionW (KERNEL32.@)
1419 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1420 DWORD len, LPCWSTR filename )
1423 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1424 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1425 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1426 INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len,
1428 MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1429 HeapFree( GetProcessHeap(), 0, sectionA );
1430 HeapFree( GetProcessHeap(), 0, filenameA );
1431 HeapFree( GetProcessHeap(), 0, bufferA);
1435 /***********************************************************************
1436 * GetProfileSection (KERNEL.419)
1438 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1440 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1443 /***********************************************************************
1444 * GetProfileSectionA (KERNEL32.@)
1446 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1448 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1451 /***********************************************************************
1452 * GetProfileSectionW (KERNEL32.@)
1454 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1456 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1460 /***********************************************************************
1461 * WritePrivateProfileString (KERNEL.129)
1463 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1464 LPCSTR string, LPCSTR filename )
1466 return WritePrivateProfileStringA(section,entry,string,filename);
1469 /***********************************************************************
1470 * WritePrivateProfileStringA (KERNEL32.@)
1472 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1473 LPCSTR string, LPCSTR filename )
1477 EnterCriticalSection( &PROFILE_CritSect );
1479 if (PROFILE_Open( filename ))
1481 if (!section && !entry && !string) /* documented "file flush" case */
1482 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1484 ret = PROFILE_SetString( section, entry, string );
1487 LeaveCriticalSection( &PROFILE_CritSect );
1491 /***********************************************************************
1492 * WritePrivateProfileStringW (KERNEL32.@)
1494 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1495 LPCWSTR string, LPCWSTR filename )
1497 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1498 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1499 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1500 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1501 BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1502 stringA, filenameA );
1503 HeapFree( GetProcessHeap(), 0, sectionA );
1504 HeapFree( GetProcessHeap(), 0, entryA );
1505 HeapFree( GetProcessHeap(), 0, stringA );
1506 HeapFree( GetProcessHeap(), 0, filenameA );
1510 /***********************************************************************
1511 * WritePrivateProfileSection (KERNEL.416)
1513 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1514 LPCSTR string, LPCSTR filename )
1516 return WritePrivateProfileSectionA( section, string, filename );
1519 /***********************************************************************
1520 * WritePrivateProfileSectionA (KERNEL32.@)
1522 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1523 LPCSTR string, LPCSTR filename )
1528 EnterCriticalSection( &PROFILE_CritSect );
1530 if (PROFILE_Open( filename )) {
1531 if (!section && !string)
1532 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1533 else if (!string) /* delete the named section*/
1534 ret = PROFILE_SetString(section,NULL,NULL);
1536 PROFILE_DeleteAllKeys(section);
1539 LPSTR buf = HeapAlloc( GetProcessHeap(), 0, strlen(string)+1 );
1540 strcpy( buf, string );
1541 if((p=strchr( buf, '='))){
1543 ret = PROFILE_SetString( section, buf, p+1 );
1546 HeapFree( GetProcessHeap(), 0, buf );
1547 string += strlen(string)+1;
1553 LeaveCriticalSection( &PROFILE_CritSect );
1557 /***********************************************************************
1558 * WritePrivateProfileSectionW (KERNEL32.@)
1560 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1561 LPCWSTR string, LPCWSTR filename)
1564 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1565 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1566 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1567 BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1568 HeapFree( GetProcessHeap(), 0, sectionA );
1569 HeapFree( GetProcessHeap(), 0, stringA );
1570 HeapFree( GetProcessHeap(), 0, filenameA );
1574 /***********************************************************************
1575 * WriteProfileSection (KERNEL.417)
1577 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1579 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1582 /***********************************************************************
1583 * WriteProfileSectionA (KERNEL32.@)
1585 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1588 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1591 /***********************************************************************
1592 * WriteProfileSectionW (KERNEL32.@)
1594 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1596 return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1599 /***********************************************************************
1600 * GetPrivateProfileSectionNames (KERNEL.143)
1602 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1605 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1609 /***********************************************************************
1610 * GetProfileSectionNames (KERNEL.142)
1612 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1615 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1619 /***********************************************************************
1620 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1622 * Returns the section names contained in the specified file.
1623 * FIXME: Where do we find this file when the path is relative?
1624 * The section names are returned as a list of strings with an extra
1625 * '\0' to mark the end of the list. Except for that the behavior
1626 * depends on the Windows version.
1629 * - if the buffer is 0 or 1 character long then it is as if it was of
1631 * - otherwise, if the buffer is to small only the section names that fit
1633 * - note that this means if the buffer was to small to return even just
1634 * the first section name then a single '\0' will be returned.
1635 * - the return value is the number of characters written in the buffer,
1636 * except if the buffer was too smal in which case len-2 is returned
1639 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1640 * '\0' and the return value is 0
1641 * - otherwise if the buffer is too small then the first section name that
1642 * does not fit is truncated so that the string list can be terminated
1643 * correctly (double '\0')
1644 * - the return value is the number of characters written in the buffer
1645 * except for the trailing '\0'. If the buffer is too small, then the
1646 * return value is len-2
1647 * - Win2000 has a bug that triggers when the section names and the
1648 * trailing '\0' fit exactly in the buffer. In that case the trailing
1651 * Wine implements the observed Win2000 behavior (except for the bug).
1653 * Note that when the buffer is big enough then the return value may be any
1654 * value between 1 and len-1 (or len in Win95), including len-2.
1656 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1662 EnterCriticalSection( &PROFILE_CritSect );
1664 if (PROFILE_Open( filename ))
1665 ret = PROFILE_GetSectionNames(buffer, size);
1667 LeaveCriticalSection( &PROFILE_CritSect );
1673 /***********************************************************************
1674 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1676 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1680 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1681 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, size);
1683 INT ret = GetPrivateProfileSectionNamesA(bufferA, size, filenameA);
1684 if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, size ))
1686 HeapFree( GetProcessHeap(), 0, bufferA);
1687 HeapFree( GetProcessHeap(), 0, filenameA );
1692 /***********************************************************************
1693 * GetPrivateProfileStruct (KERNEL.407)
1695 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1696 LPVOID buf, UINT16 len, LPCSTR filename)
1698 return GetPrivateProfileStructA( section, key, buf, len, filename );
1701 /***********************************************************************
1702 * GetPrivateProfileStructA (KERNEL32.@)
1704 * Should match Win95's behaviour pretty much
1706 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1707 LPVOID buf, UINT len, LPCSTR filename)
1711 EnterCriticalSection( &PROFILE_CritSect );
1713 if (PROFILE_Open( filename )) {
1714 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1716 TRACE("value (at %p): '%s'\n", k->value, k->value);
1717 if (((strlen(k->value) - 2) / 2) == len)
1724 end = k->value + strlen(k->value); /* -> '\0' */
1725 /* check for invalid chars in ASCII coded hex string */
1726 for (p=k->value; p < end; p++)
1730 WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1731 *p, filename, section, key);
1738 BOOL highnibble = TRUE;
1740 LPBYTE binbuf = (LPBYTE)buf;
1742 end -= 2; /* don't include checksum in output data */
1743 /* translate ASCII hex format into binary data */
1744 for (p=k->value; p < end; p++)
1748 (c - 'A' + 10) : (c - '0');
1755 *binbuf++ = b; /* feed binary data into output */
1756 chksum += b; /* calculate checksum */
1758 highnibble ^= 1; /* toggle */
1760 /* retrieve stored checksum value */
1762 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1764 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1765 if (b == (chksum & 0xff)) /* checksums match ? */
1771 LeaveCriticalSection( &PROFILE_CritSect );
1776 /***********************************************************************
1777 * GetPrivateProfileStructW (KERNEL32.@)
1779 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1780 LPVOID buffer, UINT len, LPCWSTR filename)
1782 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1783 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1784 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1785 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1787 INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1789 if (len > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, len ))
1790 ((LPWSTR)buffer)[len-1] = 0;
1791 HeapFree( GetProcessHeap(), 0, bufferA);
1792 HeapFree( GetProcessHeap(), 0, sectionA );
1793 HeapFree( GetProcessHeap(), 0, keyA );
1794 HeapFree( GetProcessHeap(), 0, filenameA );
1801 /***********************************************************************
1802 * WritePrivateProfileStruct (KERNEL.406)
1804 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1805 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1807 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1810 /***********************************************************************
1811 * WritePrivateProfileStructA (KERNEL32.@)
1813 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1814 LPVOID buf, UINT bufsize, LPCSTR filename)
1821 if (!section && !key && !buf) /* flush the cache */
1822 return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1824 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1825 outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1);
1827 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1828 *p++ = hex[*binbuf >> 4];
1829 *p++ = hex[*binbuf & 0xf];
1832 /* checksum is sum & 0xff */
1833 *p++ = hex[(sum & 0xf0) >> 4];
1834 *p++ = hex[sum & 0xf];
1837 EnterCriticalSection( &PROFILE_CritSect );
1839 if (PROFILE_Open( filename ))
1840 ret = PROFILE_SetString( section, key, outstring );
1842 LeaveCriticalSection( &PROFILE_CritSect );
1844 HeapFree( GetProcessHeap(), 0, outstring );
1849 /***********************************************************************
1850 * WritePrivateProfileStructW (KERNEL32.@)
1852 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1853 LPVOID buf, UINT bufsize, LPCWSTR filename)
1855 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1856 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1857 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1858 INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1860 HeapFree( GetProcessHeap(), 0, sectionA );
1861 HeapFree( GetProcessHeap(), 0, keyA );
1862 HeapFree( GetProcessHeap(), 0, filenameA );
1868 /***********************************************************************
1869 * WriteOutProfiles (KERNEL.315)
1871 void WINAPI WriteOutProfiles16(void)
1873 EnterCriticalSection( &PROFILE_CritSect );
1874 PROFILE_FlushFile();
1875 LeaveCriticalSection( &PROFILE_CritSect );
1878 /***********************************************************************
1879 * CloseProfileUserMapping (KERNEL32.@)
1881 BOOL WINAPI CloseProfileUserMapping(void) {
1882 FIXME("(), stub!\n");
1883 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);