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
29 #include <sys/types.h>
37 #include "wine/winbase16.h"
41 #include "wine/server.h"
42 #include "wine/library.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(profile);
47 typedef struct tagPROFILEKEY
50 struct tagPROFILEKEY *next;
54 typedef struct tagPROFILESECTION
56 struct tagPROFILEKEY *key;
57 struct tagPROFILESECTION *next;
65 PROFILESECTION *section;
73 #define N_CACHED_PROFILES 10
75 /* Cached profile files */
76 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
78 #define CurProfile (MRUProfile[0])
80 /* wine.ini config file registry root */
81 static HKEY wine_profile_key;
83 #define PROFILE_MAX_LINE_LEN 1024
85 /* Wine profile name in $HOME directory; must begin with slash */
86 static const char PROFILE_WineIniName[] = "/.winerc";
88 /* Wine profile: the profile file being used */
89 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
91 /* Check for comments in profile */
92 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
94 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
96 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
98 static const char hex[16] = "0123456789ABCDEF";
100 /***********************************************************************
103 * Copy the content of an entry into a buffer, removing quotes, and possibly
104 * translating environment variables.
106 static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
114 if ((*value == '\'') || (*value == '\"'))
116 if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
121 lstrcpynA( buffer, value, len );
122 if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
126 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
128 if ((*p == '$') && (p[1] == '{'))
132 const char *p2 = strchr( p, '}' );
133 if (!p2) continue; /* ignore it */
134 lstrcpynA(env_val, p + 2, min( sizeof(env_val), (int)(p2-p)-1 ));
135 if ((env_p = getenv( env_val )) != NULL)
138 lstrcpynA( buffer, env_p, len );
139 buffer_len = strlen( buffer );
140 buffer += buffer_len;
146 if (quote && (len > 1)) buffer--;
151 /***********************************************************************
154 * Save a profile tree to a file.
156 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
160 for ( ; section; section = section->next)
162 if (section->name[0]) fprintf( file, "\r\n[%s]\r\n", section->name );
163 for (key = section->key; key; key = key->next)
165 fprintf( file, "%s", key->name );
166 if (key->value) fprintf( file, "=%s", key->value );
167 fprintf( file, "\r\n" );
173 /***********************************************************************
176 * Free a profile tree.
178 static void PROFILE_Free( PROFILESECTION *section )
180 PROFILESECTION *next_section;
181 PROFILEKEY *key, *next_key;
183 for ( ; section; section = next_section)
185 for (key = section->key; key; key = next_key)
187 next_key = key->next;
188 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
189 HeapFree( GetProcessHeap(), 0, key );
191 next_section = section->next;
192 HeapFree( GetProcessHeap(), 0, section );
196 static inline int PROFILE_isspace(char c)
198 if (isspace(c)) return 1;
199 if (c=='\r' || c==0x1a) return 1;
200 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
205 /***********************************************************************
208 * Load a profile tree from a file.
210 static PROFILESECTION *PROFILE_Load( FILE *file )
212 char buffer[PROFILE_MAX_LINE_LEN];
215 PROFILESECTION *section, *first_section;
216 PROFILESECTION **next_section;
217 PROFILEKEY *key, *prev_key, **next_key;
219 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
220 if(first_section == NULL) return NULL;
221 first_section->name[0] = 0;
222 first_section->key = NULL;
223 first_section->next = NULL;
224 next_section = &first_section->next;
225 next_key = &first_section->key;
228 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
232 while (*p && PROFILE_isspace(*p)) p++;
233 if (*p == '[') /* section start */
235 if (!(p2 = strrchr( p, ']' )))
237 WARN("Invalid section header at line %d: '%s'\n",
244 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + strlen(p) )))
246 strcpy( section->name, p );
248 section->next = NULL;
249 *next_section = section;
250 next_section = §ion->next;
251 next_key = §ion->key;
254 TRACE("New section: '%s'\n",section->name);
261 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
263 if ((p2 = strchr( p, '=' )) != NULL)
266 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
268 while (*p2 && PROFILE_isspace(*p2)) p2++;
271 if(*p || !prev_key || *prev_key->name)
273 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + strlen(p) ))) break;
274 strcpy( key->name, p );
277 key->value = HeapAlloc( GetProcessHeap(), 0, strlen(p2)+1 );
278 strcpy( key->value, p2 );
280 else key->value = NULL;
284 next_key = &key->next;
287 TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)");
290 return first_section;
293 /* convert the .winerc file to the new format */
294 static void convert_config( FILE *in, const char *output_name )
296 char buffer[PROFILE_MAX_LINE_LEN];
300 /* create the output file, only if it doesn't exist already */
301 int fd = open( output_name, O_WRONLY|O_CREAT|O_EXCL, 0666 );
304 MESSAGE( "Could not create new config file '%s': %s\n", output_name, strerror(errno) );
308 out = fdopen( fd, "w" );
309 fprintf( out, "WINE REGISTRY Version 2\n" );
310 fprintf( out, ";; All keys relative to \\\\Machine\\\\Software\\\\Wine\\\\Wine\\\\Config\n\n" );
311 while (fgets( buffer, PROFILE_MAX_LINE_LEN, in ))
313 if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0;
315 while (*p && PROFILE_isspace(*p)) p++;
316 if (*p == '[') /* section start */
318 if ((p2 = strrchr( p, ']' )))
322 fprintf( out, "[%s]\n", p );
327 if (*p == ';' || *p == '#')
329 fprintf( out, "%s\n", p );
334 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
336 if ((p2 = strchr( p, '=' )) != NULL)
339 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
341 while (*p2 && PROFILE_isspace(*p2)) p2++;
346 fprintf( out, "\n" );
352 if (*p == '\\') fputc( '\\', out );
356 fprintf( out, "\" = \"" );
361 if (*p2 == '\\') fputc( '\\', out );
366 fprintf( out, "\"\n" );
372 /***********************************************************************
373 * PROFILE_DeleteSection
375 * Delete a section from a profile tree.
377 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
381 if ((*section)->name[0] && !strcasecmp( (*section)->name, name ))
383 PROFILESECTION *to_del = *section;
384 *section = to_del->next;
386 PROFILE_Free( to_del );
389 section = &(*section)->next;
395 /***********************************************************************
398 * Delete a key from a profile tree.
400 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
401 LPCSTR section_name, LPCSTR key_name )
405 if ((*section)->name[0] && !strcasecmp( (*section)->name, section_name ))
407 PROFILEKEY **key = &(*section)->key;
410 if (!strcasecmp( (*key)->name, key_name ))
412 PROFILEKEY *to_del = *key;
414 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
415 HeapFree( GetProcessHeap(), 0, to_del );
421 section = &(*section)->next;
427 /***********************************************************************
428 * PROFILE_DeleteAllKeys
430 * Delete all keys from a profile tree.
432 void PROFILE_DeleteAllKeys( LPCSTR section_name)
434 PROFILESECTION **section= &CurProfile->section;
437 if ((*section)->name[0] && !strcasecmp( (*section)->name, section_name ))
439 PROFILEKEY **key = &(*section)->key;
442 PROFILEKEY *to_del = *key;
444 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
445 HeapFree( GetProcessHeap(), 0, to_del );
446 CurProfile->changed =TRUE;
449 section = &(*section)->next;
454 /***********************************************************************
457 * Find a key in a profile tree, optionally creating it.
459 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, const char *section_name,
460 const char *key_name, BOOL create, BOOL create_always )
465 while (PROFILE_isspace(*section_name)) section_name++;
466 p = section_name + strlen(section_name) - 1;
467 while ((p > section_name) && PROFILE_isspace(*p)) p--;
468 seclen = p - section_name + 1;
470 while (PROFILE_isspace(*key_name)) key_name++;
471 p = key_name + strlen(key_name) - 1;
472 while ((p > key_name) && PROFILE_isspace(*p)) p--;
473 keylen = p - key_name + 1;
477 if ( ((*section)->name[0])
478 && (!(strncasecmp( (*section)->name, section_name, seclen )))
479 && (((*section)->name)[seclen] == '\0') )
481 PROFILEKEY **key = &(*section)->key;
485 /* If create_always is FALSE then we check if the keyname already exists.
486 * Otherwise we add it regardless of its existence, to allow
487 * keys to be added more then once in some cases.
491 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
492 && (((*key)->name)[keylen] == '\0') )
497 if (!create) return NULL;
498 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlen(key_name) )))
500 strcpy( (*key)->name, key_name );
501 (*key)->value = NULL;
505 section = &(*section)->next;
507 if (!create) return NULL;
508 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlen(section_name) );
509 if(*section == NULL) return NULL;
510 strcpy( (*section)->name, section_name );
511 (*section)->next = NULL;
512 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
513 sizeof(PROFILEKEY) + strlen(key_name) )))
515 HeapFree(GetProcessHeap(), 0, *section);
518 strcpy( (*section)->key->name, key_name );
519 (*section)->key->value = NULL;
520 (*section)->key->next = NULL;
521 return (*section)->key;
525 /***********************************************************************
528 * Flush the current profile to disk if changed.
530 static BOOL PROFILE_FlushFile(void)
532 char *p, buffer[MAX_PATHNAME_LEN];
533 const char *unix_name;
539 WARN("No current profile!\n");
543 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
544 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
546 /* Try to create it in $HOME/.wine */
547 /* FIXME: this will need a more general solution */
548 strcpy( buffer, wine_get_config_dir() );
549 p = buffer + strlen(buffer);
551 strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
553 file = fopen( buffer, "w" );
559 WARN("could not save profile file %s\n", CurProfile->dos_name);
563 TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
564 PROFILE_Save( file, CurProfile->section );
566 CurProfile->changed = FALSE;
567 if(!stat(unix_name,&buf))
568 CurProfile->mtime=buf.st_mtime;
573 /***********************************************************************
574 * PROFILE_ReleaseFile
576 * Flush the current profile to disk and remove it from the cache.
578 static void PROFILE_ReleaseFile(void)
581 PROFILE_Free( CurProfile->section );
582 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
583 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
584 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
585 CurProfile->changed = FALSE;
586 CurProfile->section = NULL;
587 CurProfile->dos_name = NULL;
588 CurProfile->unix_name = NULL;
589 CurProfile->filename = NULL;
590 CurProfile->mtime = 0;
594 /***********************************************************************
597 * Open a profile file, checking the cached file first.
599 static BOOL PROFILE_Open( LPCSTR filename )
601 DOS_FULL_NAME full_name;
602 char buffer[MAX_PATHNAME_LEN];
603 char *newdos_name, *p;
607 PROFILE *tempProfile;
609 /* First time around */
612 for(i=0;i<N_CACHED_PROFILES;i++)
614 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
615 if(MRUProfile[i] == NULL) break;
616 MRUProfile[i]->changed=FALSE;
617 MRUProfile[i]->section=NULL;
618 MRUProfile[i]->dos_name=NULL;
619 MRUProfile[i]->unix_name=NULL;
620 MRUProfile[i]->filename=NULL;
621 MRUProfile[i]->mtime=0;
624 /* Check for a match */
626 if (strchr( filename, '/' ) || strchr( filename, '\\' ) ||
627 strchr( filename, ':' ))
629 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
633 GetWindowsDirectoryA( buffer, sizeof(buffer) );
634 strcat( buffer, "\\" );
635 strcat( buffer, filename );
636 if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
639 for(i=0;i<N_CACHED_PROFILES;i++)
641 if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
642 (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
647 tempProfile=MRUProfile[i];
649 MRUProfile[j]=MRUProfile[j-1];
650 CurProfile=tempProfile;
652 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
653 TRACE("(%s): already opened (mru=%d)\n",
656 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
662 /* Flush the old current profile */
665 /* Make the oldest profile the current one only in order to get rid of it */
666 if(i==N_CACHED_PROFILES)
668 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
669 for(i=N_CACHED_PROFILES-1;i>0;i--)
670 MRUProfile[i]=MRUProfile[i-1];
671 CurProfile=tempProfile;
673 if(CurProfile->filename) PROFILE_ReleaseFile();
675 /* OK, now that CurProfile is definitely free we assign it our new file */
676 newdos_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.short_name)+1 );
677 strcpy( newdos_name, full_name.short_name );
678 CurProfile->dos_name = newdos_name;
679 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, strlen(filename)+1 );
680 strcpy( CurProfile->filename, filename );
682 /* Try to open the profile file, first in $HOME/.wine */
684 /* FIXME: this will need a more general solution */
685 strcpy( buffer, wine_get_config_dir() );
686 p = buffer + strlen(buffer);
688 strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
690 if ((file = fopen( buffer, "r" )))
692 TRACE("(%s): found it in %s\n",
694 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
695 strcpy( CurProfile->unix_name, buffer );
700 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
701 strcpy( CurProfile->unix_name, full_name.long_name );
702 if ((file = fopen( full_name.long_name, "r" )))
703 TRACE("(%s): found it in %s\n",
704 filename, full_name.long_name );
709 CurProfile->section = PROFILE_Load( file );
711 if(!stat(CurProfile->unix_name,&buf))
712 CurProfile->mtime=buf.st_mtime;
716 /* Does not exist yet, we will create it in PROFILE_FlushFile */
717 WARN("profile file %s not found\n", newdos_name );
723 /***********************************************************************
726 * Returns all keys of a section.
727 * If return_values is TRUE, also include the corresponding values.
729 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
730 LPSTR buffer, UINT len, BOOL handle_env,
735 if(!buffer) return 0;
739 if (section->name[0] && !strcasecmp( section->name, section_name ))
742 for (key = section->key; key; key = key->next)
745 if (!*key->name) continue; /* Skip empty lines */
746 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
747 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
748 len -= strlen(buffer) + 1;
749 buffer += strlen(buffer) + 1;
752 if (return_values && key->value) {
754 PROFILE_CopyEntry ( buffer,
755 key->value, len - 1, handle_env );
756 len -= strlen(buffer) + 1;
757 buffer += strlen(buffer) + 1;
762 /*If either lpszSection or lpszKey is NULL and the supplied
763 destination buffer is too small to hold all the strings,
764 the last string is truncated and followed by two null characters.
765 In this case, the return value is equal to cchReturnBuffer
773 section = section->next;
775 buffer[0] = buffer[1] = '\0';
779 /* See GetPrivateProfileSectionNamesA for documentation */
780 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
784 PROFILESECTION *section;
795 section = CurProfile->section;
796 while ((section!=NULL)) {
797 if (section->name[0]) {
798 l = strlen(section->name)+1;
801 strncpy(buf, section->name, f-1);
808 strcpy(buf, section->name);
812 section = section->next;
819 /***********************************************************************
822 * Get a profile string.
824 * Tests with GetPrivateProfileString16, W95a,
825 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
826 * section key_name def_val res buffer
827 * "set1" "1" "x" 43 [data]
828 * "set1" "1 " "x" 43 [data] (!)
829 * "set1" " 1 "' "x" 43 [data] (!)
830 * "set1" "" "x" 1 "x"
831 * "set1" "" "x " 1 "x" (!)
832 * "set1" "" " x " 3 " x" (!)
833 * "set1" NULL "x" 6 "1\02\03\0\0"
834 * "set1" "" "x" 1 "x"
835 * NULL "1" "x" 0 "" (!)
841 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
842 LPCSTR def_val, LPSTR buffer, UINT len )
844 PROFILEKEY *key = NULL;
846 if(!buffer) return 0;
848 if (!def_val) def_val = "";
852 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
854 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
855 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
857 TRACE("('%s','%s','%s'): returning '%s'\n",
858 section, key_name, def_val, buffer );
859 return strlen( buffer );
861 /* no "else" here ! */
862 if (section && section[0])
864 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
865 if (!buffer[0]) /* no luck -> def_val */
867 PROFILE_CopyEntry(buffer, def_val, len, FALSE);
868 ret = strlen(buffer);
877 /***********************************************************************
880 * Set a profile string.
882 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
883 LPCSTR value, BOOL create_always )
885 if (!key_name) /* Delete a whole section */
887 TRACE("('%s')\n", section_name);
888 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
890 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
891 this is not an error on application's level.*/
893 else if (!value) /* Delete a key */
895 TRACE("('%s','%s')\n",
896 section_name, key_name );
897 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
898 section_name, key_name );
899 return TRUE; /* same error handling as above */
901 else /* Set the key value */
903 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
904 key_name, TRUE, create_always );
905 TRACE("('%s','%s','%s'): \n",
906 section_name, key_name, value );
907 if (!key) return FALSE;
910 /* strip the leading spaces. We can safely strip \n\r and
911 * friends too, they should not happen here anyway. */
912 while (PROFILE_isspace(*value)) value++;
914 if (!strcmp( key->value, value ))
916 TRACE(" no change needed\n" );
917 return TRUE; /* No change needed */
919 TRACE(" replacing '%s'\n", key->value );
920 HeapFree( GetProcessHeap(), 0, key->value );
922 else TRACE(" creating key\n" );
923 key->value = HeapAlloc( GetProcessHeap(), 0, strlen(value)+1 );
924 strcpy( key->value, value );
925 CurProfile->changed = TRUE;
931 /***********************************************************************
932 * PROFILE_GetWineIniString
934 * Get a config string from the wine.ini file.
936 int PROFILE_GetWineIniString( const char *section, const char *key_name,
937 const char *def, char *buffer, int len )
939 char tmp[PROFILE_MAX_LINE_LEN];
943 if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey )))
946 DWORD count = sizeof(tmp);
947 err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count );
950 PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE );
951 TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer );
952 return strlen(buffer);
956 /******************************************************************************
958 * int PROFILE_GetWineIniBool(
959 * char const *section,
960 * char const *key_name,
963 * Reads a boolean value from the wine.ini file. This function attempts to
964 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
965 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
966 * true. Anything else results in the return of the default value.
968 * This function uses 1 to indicate true, and 0 for false. You can check
969 * for existence by setting def to something other than 0 or 1 and
970 * examining the return value.
972 int PROFILE_GetWineIniBool(
974 char const *key_name,
980 PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
982 switch(key_value[0]) {
1003 TRACE("(\"%s\", \"%s\", %s), [%c], ret %s.\n", section, key_name,
1004 def ? "TRUE" : "FALSE", key_value[0],
1005 retval ? "TRUE" : "FALSE");
1011 /***********************************************************************
1012 * PROFILE_LoadWineIni
1014 * Load the old .winerc file.
1016 int PROFILE_LoadWineIni(void)
1018 OBJECT_ATTRIBUTES attr;
1019 UNICODE_STRING nameW;
1020 char buffer[MAX_PATHNAME_LEN];
1026 attr.Length = sizeof(attr);
1027 attr.RootDirectory = 0;
1028 attr.ObjectName = &nameW;
1029 attr.Attributes = 0;
1030 attr.SecurityDescriptor = NULL;
1031 attr.SecurityQualityOfService = NULL;
1033 /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1034 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine" ) ||
1035 NtCreateKey( &hKeySW, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp ))
1037 ERR("Cannot create config registry key\n" );
1040 RtlFreeUnicodeString( &nameW );
1043 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1044 NtCreateKey( &wine_profile_key, KEY_ALL_ACCESS, &attr, 0,
1045 NULL, REG_OPTION_VOLATILE, &disp ))
1047 ERR("Cannot create config registry key\n" );
1050 RtlFreeUnicodeString( &nameW );
1052 if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
1054 if ((p = getenv( "HOME" )) != NULL)
1056 lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
1057 strcat( buffer, PROFILE_WineIniName );
1058 if ((f = fopen( buffer, "r" )) != NULL)
1060 lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
1062 /* convert to the new format */
1063 sprintf( buffer, "%s/config", wine_get_config_dir() );
1064 convert_config( f, buffer );
1067 MESSAGE( "The '%s' configuration file has been converted\n"
1068 "to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
1069 MESSAGE( "You should verify that the contents of the new file are correct,\n"
1070 "and then remove the old one and restart Wine.\n" );
1074 else WARN("could not get $HOME value for config file.\n" );
1076 MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
1081 /***********************************************************************
1082 * PROFILE_UsageWineIni
1084 * Explain the wine.ini file to those who don't read documentation.
1085 * Keep below one screenful in length so that error messages above are
1088 void PROFILE_UsageWineIni(void)
1090 MESSAGE("Perhaps you have not properly edited or created "
1091 "your Wine configuration file.\n");
1092 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1093 /* RTFM, so to say */
1097 /********************* API functions **********************************/
1099 /***********************************************************************
1100 * GetProfileInt (KERNEL.57)
1102 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1104 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1108 /***********************************************************************
1109 * GetProfileIntA (KERNEL32.@)
1111 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1113 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1116 /***********************************************************************
1117 * GetProfileIntW (KERNEL32.@)
1119 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1121 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1125 * if allow_section_name_copy is TRUE, allow the copying :
1126 * - of Section names if 'section' is NULL
1127 * - of Keys in a Section if 'entry' is NULL
1128 * (see MSDN doc for GetPrivateProfileString)
1130 static int PROFILE_GetPrivateProfileString( LPCSTR section, LPCSTR entry,
1131 LPCSTR def_val, LPSTR buffer,
1132 UINT16 len, LPCSTR filename,
1133 BOOL allow_section_name_copy )
1136 LPSTR pDefVal = NULL;
1139 filename = "win.ini";
1141 /* strip any trailing ' ' of def_val. */
1144 LPSTR p = (LPSTR)&def_val[strlen(def_val)]; /* even "" works ! */
1152 if (*p == ' ') /* ouch, contained trailing ' ' */
1154 int len = (int)p - (int)def_val;
1155 pDefVal = HeapAlloc(GetProcessHeap(), 0, len + 1);
1156 strncpy(pDefVal, def_val, len);
1157 pDefVal[len] = '\0';
1161 pDefVal = (LPSTR)def_val;
1163 EnterCriticalSection( &PROFILE_CritSect );
1165 if (PROFILE_Open( filename )) {
1166 if ((allow_section_name_copy) && (section == NULL))
1167 ret = PROFILE_GetSectionNames(buffer, len);
1169 /* PROFILE_GetString already handles the 'entry == NULL' case */
1170 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1172 lstrcpynA( buffer, pDefVal, len );
1173 ret = strlen( buffer );
1176 LeaveCriticalSection( &PROFILE_CritSect );
1178 if (pDefVal != def_val) /* allocated */
1179 HeapFree(GetProcessHeap(), 0, pDefVal);
1184 /***********************************************************************
1185 * GetPrivateProfileString (KERNEL.128)
1187 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1188 LPCSTR def_val, LPSTR buffer,
1189 UINT16 len, LPCSTR filename )
1191 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1192 buffer, len, filename, FALSE );
1195 /***********************************************************************
1196 * GetPrivateProfileStringA (KERNEL32.@)
1198 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1199 LPCSTR def_val, LPSTR buffer,
1200 UINT len, LPCSTR filename )
1202 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1203 buffer, len, filename, TRUE );
1206 /***********************************************************************
1207 * GetPrivateProfileStringW (KERNEL32.@)
1209 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1210 LPCWSTR def_val, LPWSTR buffer,
1211 UINT len, LPCWSTR filename )
1213 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1214 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1215 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1216 LPSTR def_valA = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1217 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1218 INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1219 bufferA, len, filenameA );
1220 if (len > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, len ))
1222 HeapFree( GetProcessHeap(), 0, sectionA );
1223 HeapFree( GetProcessHeap(), 0, entryA );
1224 HeapFree( GetProcessHeap(), 0, filenameA );
1225 HeapFree( GetProcessHeap(), 0, def_valA );
1226 HeapFree( GetProcessHeap(), 0, bufferA);
1230 /***********************************************************************
1231 * GetProfileString (KERNEL.58)
1233 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1234 LPSTR buffer, UINT16 len )
1236 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1237 buffer, len, "win.ini", FALSE );
1240 /***********************************************************************
1241 * GetProfileStringA (KERNEL32.@)
1243 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1244 LPSTR buffer, UINT len )
1246 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1247 buffer, len, "win.ini", TRUE );
1250 /***********************************************************************
1251 * GetProfileStringW (KERNEL32.@)
1253 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1254 LPCWSTR def_val, LPWSTR buffer, UINT len )
1256 return GetPrivateProfileStringW( section, entry, def_val,
1257 buffer, len, wininiW );
1260 /***********************************************************************
1261 * WriteProfileString (KERNEL.59)
1263 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1266 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1269 /***********************************************************************
1270 * WriteProfileStringA (KERNEL32.@)
1272 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1275 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1278 /***********************************************************************
1279 * WriteProfileStringW (KERNEL32.@)
1281 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1284 return WritePrivateProfileStringW( section, entry, string, wininiW );
1288 /***********************************************************************
1289 * GetPrivateProfileInt (KERNEL.127)
1291 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1292 INT16 def_val, LPCSTR filename )
1294 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1295 * here, but Win98SE doesn't care about this at all, so I deleted it.
1296 * AFAIR versions prior to Win9x had these limits, though. */
1297 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1300 /***********************************************************************
1301 * GetPrivateProfileIntA (KERNEL32.@)
1303 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1304 INT def_val, LPCSTR filename )
1309 if (!PROFILE_GetPrivateProfileString( section, entry, "",
1310 buffer, sizeof(buffer), filename, FALSE ))
1312 /* FIXME: if entry can be found but it's empty, then Win16 is
1313 * supposed to return 0 instead of def_val ! Difficult/problematic
1314 * to implement (every other failure also returns zero buffer),
1315 * thus wait until testing framework avail for making sure nothing
1316 * else gets broken that way. */
1317 if (!buffer[0]) return (UINT)def_val;
1319 /* Don't use strtol() here !
1320 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1321 YES, scan for unsigned format ! (otherwise compatibility error) */
1322 if (!sscanf(buffer, "%lu", &result)) return 0;
1323 return (UINT)result;
1326 /***********************************************************************
1327 * GetPrivateProfileIntW (KERNEL32.@)
1329 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1330 INT def_val, LPCWSTR filename )
1332 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1333 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1334 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1335 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1336 HeapFree( GetProcessHeap(), 0, sectionA );
1337 HeapFree( GetProcessHeap(), 0, filenameA );
1338 HeapFree( GetProcessHeap(), 0, entryA );
1342 /***********************************************************************
1343 * GetPrivateProfileSection (KERNEL.418)
1345 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1346 UINT16 len, LPCSTR filename )
1348 return GetPrivateProfileSectionA( section, buffer, len, filename );
1351 /***********************************************************************
1352 * GetPrivateProfileSectionA (KERNEL32.@)
1354 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1355 DWORD len, LPCSTR filename )
1359 EnterCriticalSection( &PROFILE_CritSect );
1361 if (PROFILE_Open( filename ))
1362 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1365 LeaveCriticalSection( &PROFILE_CritSect );
1370 /***********************************************************************
1371 * GetPrivateProfileSectionW (KERNEL32.@)
1374 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1375 DWORD len, LPCWSTR filename )
1378 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1379 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1380 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1381 INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len,
1383 MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1384 HeapFree( GetProcessHeap(), 0, sectionA );
1385 HeapFree( GetProcessHeap(), 0, filenameA );
1386 HeapFree( GetProcessHeap(), 0, bufferA);
1390 /***********************************************************************
1391 * GetProfileSection (KERNEL.419)
1393 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1395 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1398 /***********************************************************************
1399 * GetProfileSectionA (KERNEL32.@)
1401 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1403 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1406 /***********************************************************************
1407 * GetProfileSectionW (KERNEL32.@)
1409 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1411 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1415 /***********************************************************************
1416 * WritePrivateProfileString (KERNEL.129)
1418 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1419 LPCSTR string, LPCSTR filename )
1421 return WritePrivateProfileStringA(section,entry,string,filename);
1424 /***********************************************************************
1425 * WritePrivateProfileStringA (KERNEL32.@)
1427 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1428 LPCSTR string, LPCSTR filename )
1432 EnterCriticalSection( &PROFILE_CritSect );
1434 if (PROFILE_Open( filename ))
1436 if (!section && !entry && !string) /* documented "file flush" case */
1437 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1440 FIXME("(NULL?,%s,%s,%s)? \n",entry,string,filename);
1442 ret = PROFILE_SetString( section, entry, string, FALSE);
1447 LeaveCriticalSection( &PROFILE_CritSect );
1451 /***********************************************************************
1452 * WritePrivateProfileStringW (KERNEL32.@)
1454 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1455 LPCWSTR string, LPCWSTR filename )
1457 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1458 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1459 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1460 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1461 BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1462 stringA, filenameA );
1463 HeapFree( GetProcessHeap(), 0, sectionA );
1464 HeapFree( GetProcessHeap(), 0, entryA );
1465 HeapFree( GetProcessHeap(), 0, stringA );
1466 HeapFree( GetProcessHeap(), 0, filenameA );
1470 /***********************************************************************
1471 * WritePrivateProfileSection (KERNEL.416)
1473 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1474 LPCSTR string, LPCSTR filename )
1476 return WritePrivateProfileSectionA( section, string, filename );
1479 /***********************************************************************
1480 * WritePrivateProfileSectionA (KERNEL32.@)
1482 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1483 LPCSTR string, LPCSTR filename )
1488 EnterCriticalSection( &PROFILE_CritSect );
1490 if (PROFILE_Open( filename )) {
1491 if (!section && !string)
1492 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1493 else if (!string) /* delete the named section*/
1494 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1496 PROFILE_DeleteAllKeys(section);
1499 LPSTR buf = HeapAlloc( GetProcessHeap(), 0, strlen(string)+1 );
1500 strcpy( buf, string );
1501 if((p=strchr( buf, '='))){
1503 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1505 HeapFree( GetProcessHeap(), 0, buf );
1506 string += strlen(string)+1;
1511 LeaveCriticalSection( &PROFILE_CritSect );
1515 /***********************************************************************
1516 * WritePrivateProfileSectionW (KERNEL32.@)
1518 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1519 LPCWSTR string, LPCWSTR filename)
1522 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1523 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1524 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1525 BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1526 HeapFree( GetProcessHeap(), 0, sectionA );
1527 HeapFree( GetProcessHeap(), 0, stringA );
1528 HeapFree( GetProcessHeap(), 0, filenameA );
1532 /***********************************************************************
1533 * WriteProfileSection (KERNEL.417)
1535 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1537 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1540 /***********************************************************************
1541 * WriteProfileSectionA (KERNEL32.@)
1543 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1546 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1549 /***********************************************************************
1550 * WriteProfileSectionW (KERNEL32.@)
1552 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1554 return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1557 /***********************************************************************
1558 * GetPrivateProfileSectionNames (KERNEL.143)
1560 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1563 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1567 /***********************************************************************
1568 * GetProfileSectionNames (KERNEL.142)
1570 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1573 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1577 /***********************************************************************
1578 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1580 * Returns the section names contained in the specified file.
1581 * FIXME: Where do we find this file when the path is relative?
1582 * The section names are returned as a list of strings with an extra
1583 * '\0' to mark the end of the list. Except for that the behavior
1584 * depends on the Windows version.
1587 * - if the buffer is 0 or 1 character long then it is as if it was of
1589 * - otherwise, if the buffer is to small only the section names that fit
1591 * - note that this means if the buffer was to small to return even just
1592 * the first section name then a single '\0' will be returned.
1593 * - the return value is the number of characters written in the buffer,
1594 * except if the buffer was too smal in which case len-2 is returned
1597 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1598 * '\0' and the return value is 0
1599 * - otherwise if the buffer is too small then the first section name that
1600 * does not fit is truncated so that the string list can be terminated
1601 * correctly (double '\0')
1602 * - the return value is the number of characters written in the buffer
1603 * except for the trailing '\0'. If the buffer is too small, then the
1604 * return value is len-2
1605 * - Win2000 has a bug that triggers when the section names and the
1606 * trailing '\0' fit exactly in the buffer. In that case the trailing
1609 * Wine implements the observed Win2000 behavior (except for the bug).
1611 * Note that when the buffer is big enough then the return value may be any
1612 * value between 1 and len-1 (or len in Win95), including len-2.
1614 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1620 EnterCriticalSection( &PROFILE_CritSect );
1622 if (PROFILE_Open( filename ))
1623 ret = PROFILE_GetSectionNames(buffer, size);
1625 LeaveCriticalSection( &PROFILE_CritSect );
1631 /***********************************************************************
1632 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1634 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1638 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1639 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, size);
1641 INT ret = GetPrivateProfileSectionNamesA(bufferA, size, filenameA);
1642 if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, size ))
1644 HeapFree( GetProcessHeap(), 0, bufferA);
1645 HeapFree( GetProcessHeap(), 0, filenameA );
1650 /***********************************************************************
1651 * GetPrivateProfileStruct (KERNEL.407)
1653 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1654 LPVOID buf, UINT16 len, LPCSTR filename)
1656 return GetPrivateProfileStructA( section, key, buf, len, filename );
1659 /***********************************************************************
1660 * GetPrivateProfileStructA (KERNEL32.@)
1662 * Should match Win95's behaviour pretty much
1664 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1665 LPVOID buf, UINT len, LPCSTR filename)
1669 EnterCriticalSection( &PROFILE_CritSect );
1671 if (PROFILE_Open( filename )) {
1672 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1674 TRACE("value (at %p): '%s'\n", k->value, k->value);
1675 if (((strlen(k->value) - 2) / 2) == len)
1682 end = k->value + strlen(k->value); /* -> '\0' */
1683 /* check for invalid chars in ASCII coded hex string */
1684 for (p=k->value; p < end; p++)
1688 WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1689 *p, filename, section, key);
1696 BOOL highnibble = TRUE;
1698 LPBYTE binbuf = (LPBYTE)buf;
1700 end -= 2; /* don't include checksum in output data */
1701 /* translate ASCII hex format into binary data */
1702 for (p=k->value; p < end; p++)
1706 (c - 'A' + 10) : (c - '0');
1713 *binbuf++ = b; /* feed binary data into output */
1714 chksum += b; /* calculate checksum */
1716 highnibble ^= 1; /* toggle */
1718 /* retrieve stored checksum value */
1720 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1722 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1723 if (b == (chksum & 0xff)) /* checksums match ? */
1729 LeaveCriticalSection( &PROFILE_CritSect );
1734 /***********************************************************************
1735 * GetPrivateProfileStructW (KERNEL32.@)
1737 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1738 LPVOID buffer, UINT len, LPCWSTR filename)
1740 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1741 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1742 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1743 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1745 INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1747 if (len > 0 && !MultiByteToWideChar( CP_ACP, 0, bufferA, -1, buffer, len ))
1748 ((LPWSTR)buffer)[len-1] = 0;
1749 HeapFree( GetProcessHeap(), 0, bufferA);
1750 HeapFree( GetProcessHeap(), 0, sectionA );
1751 HeapFree( GetProcessHeap(), 0, keyA );
1752 HeapFree( GetProcessHeap(), 0, filenameA );
1759 /***********************************************************************
1760 * WritePrivateProfileStruct (KERNEL.406)
1762 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1763 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1765 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1768 /***********************************************************************
1769 * WritePrivateProfileStructA (KERNEL32.@)
1771 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1772 LPVOID buf, UINT bufsize, LPCSTR filename)
1779 if (!section && !key && !buf) /* flush the cache */
1780 return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1782 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1783 outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1);
1785 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1786 *p++ = hex[*binbuf >> 4];
1787 *p++ = hex[*binbuf & 0xf];
1790 /* checksum is sum & 0xff */
1791 *p++ = hex[(sum & 0xf0) >> 4];
1792 *p++ = hex[sum & 0xf];
1795 EnterCriticalSection( &PROFILE_CritSect );
1797 if (PROFILE_Open( filename ))
1798 ret = PROFILE_SetString( section, key, outstring, FALSE);
1800 LeaveCriticalSection( &PROFILE_CritSect );
1802 HeapFree( GetProcessHeap(), 0, outstring );
1807 /***********************************************************************
1808 * WritePrivateProfileStructW (KERNEL32.@)
1810 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1811 LPVOID buf, UINT bufsize, LPCWSTR filename)
1813 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1814 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1815 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1816 INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1818 HeapFree( GetProcessHeap(), 0, sectionA );
1819 HeapFree( GetProcessHeap(), 0, keyA );
1820 HeapFree( GetProcessHeap(), 0, filenameA );
1826 /***********************************************************************
1827 * WriteOutProfiles (KERNEL.315)
1829 void WINAPI WriteOutProfiles16(void)
1831 EnterCriticalSection( &PROFILE_CritSect );
1832 PROFILE_FlushFile();
1833 LeaveCriticalSection( &PROFILE_CritSect );
1836 /***********************************************************************
1837 * CloseProfileUserMapping (KERNEL32.@)
1839 BOOL WINAPI CloseProfileUserMapping(void) {
1840 FIXME("(), stub!\n");
1841 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);