| /* | 
 |  * Profile functions | 
 |  * | 
 |  * Copyright 1993 Miguel de Icaza | 
 |  * Copyright 1996 Alexandre Julliard | 
 |  */ | 
 |  | 
 | #include <ctype.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <pwd.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "winbase.h" | 
 | #include "winerror.h" | 
 | #include "wine/winbase16.h" | 
 | #include "windef.h" | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 | #include "winnls.h" | 
 | #include "winreg.h" | 
 | #include "file.h" | 
 | #include "heap.h" | 
 | #include "debugtools.h" | 
 | #include "options.h" | 
 | #include "server.h" | 
 |  | 
 | DEFAULT_DEBUG_CHANNEL(profile); | 
 |  | 
 | typedef struct tagPROFILEKEY | 
 | { | 
 |     char                  *name; | 
 |     char                  *value; | 
 |     struct tagPROFILEKEY  *next; | 
 | } PROFILEKEY; | 
 |  | 
 | typedef struct tagPROFILESECTION | 
 | { | 
 |     char                       *name; | 
 |     struct tagPROFILEKEY       *key; | 
 |     struct tagPROFILESECTION   *next; | 
 | } PROFILESECTION;  | 
 |  | 
 |  | 
 | typedef struct | 
 | { | 
 |     BOOL             changed; | 
 |     PROFILESECTION  *section; | 
 |     char            *dos_name; | 
 |     char            *unix_name; | 
 |     char            *filename; | 
 |     time_t           mtime; | 
 | } PROFILE; | 
 |  | 
 |  | 
 | #define N_CACHED_PROFILES 10 | 
 |  | 
 | /* Cached profile files */ | 
 | static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL}; | 
 |  | 
 | #define CurProfile (MRUProfile[0]) | 
 |  | 
 | /* wine.ini config file registry root */ | 
 | static HKEY wine_profile_key; | 
 |  | 
 | #define PROFILE_MAX_LINE_LEN   1024 | 
 |  | 
 | /* Wine profile name in $HOME directory; must begin with slash */ | 
 | static const char PROFILE_WineIniName[] = "/.winerc"; | 
 |  | 
 | /* Wine profile: the profile file being used */ | 
 | static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = ""; | 
 |  | 
 | /* Check for comments in profile */ | 
 | #define IS_ENTRY_COMMENT(str)  ((str)[0] == ';') | 
 |  | 
 | #define WINE_INI_GLOBAL ETCDIR "/wine.conf" | 
 |  | 
 | static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 }; | 
 |  | 
 | static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT; | 
 |  | 
 | static const char hex[16] = "0123456789ABCDEF"; | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_CopyEntry | 
 |  * | 
 |  * Copy the content of an entry into a buffer, removing quotes, and possibly | 
 |  * translating environment variables. | 
 |  */ | 
 | static void PROFILE_CopyEntry( char *buffer, const char *value, int len, | 
 |                                int handle_env ) | 
 | { | 
 |     char quote = '\0'; | 
 |     const char *p; | 
 |  | 
 |     if ((*value == '\'') || (*value == '\"')) | 
 |     { | 
 |         if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++; | 
 |     } | 
 |  | 
 |     if (!handle_env) | 
 |     { | 
 |         lstrcpynA( buffer, value, len ); | 
 |         if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0'; | 
 |         return; | 
 |     } | 
 |  | 
 |     for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- ) | 
 |     { | 
 |         if ((*p == '$') && (p[1] == '{')) | 
 |         { | 
 |             char env_val[1024]; | 
 |             const char *env_p; | 
 |             const char *p2 = strchr( p, '}' ); | 
 |             if (!p2) continue;  /* ignore it */ | 
 |             lstrcpynA(env_val, p + 2, min( sizeof(env_val), (int)(p2-p)-1 )); | 
 |             if ((env_p = getenv( env_val )) != NULL) | 
 |             { | 
 |                 lstrcpynA( buffer, env_p, len ); | 
 |                 buffer += strlen( buffer ); | 
 |                 len -= strlen( buffer ); | 
 |             } | 
 |             p = p2 + 1; | 
 |         } | 
 |     } | 
 |     if (quote && (len > 1)) buffer--; | 
 |     *buffer = '\0'; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_Save | 
 |  * | 
 |  * Save a profile tree to a file. | 
 |  */ | 
 | static void PROFILE_Save( FILE *file, PROFILESECTION *section ) | 
 | { | 
 |     PROFILEKEY *key; | 
 |  | 
 |     for ( ; section; section = section->next) | 
 |     { | 
 |         if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name ); | 
 |         for (key = section->key; key; key = key->next) | 
 |         { | 
 |             fprintf( file, "%s", key->name ); | 
 |             if (key->value) fprintf( file, "=%s", key->value ); | 
 |             fprintf( file, "\r\n" ); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_Free | 
 |  * | 
 |  * Free a profile tree. | 
 |  */ | 
 | static void PROFILE_Free( PROFILESECTION *section ) | 
 | { | 
 |     PROFILESECTION *next_section; | 
 |     PROFILEKEY *key, *next_key; | 
 |  | 
 |     for ( ; section; section = next_section) | 
 |     { | 
 |         if (section->name) HeapFree( GetProcessHeap(), 0, section->name ); | 
 |         for (key = section->key; key; key = next_key) | 
 |         { | 
 |             next_key = key->next; | 
 |             if (key->name) HeapFree( GetProcessHeap(), 0, key->name ); | 
 |             if (key->value) HeapFree( GetProcessHeap(), 0, key->value ); | 
 |             HeapFree( GetProcessHeap(), 0, key ); | 
 |         } | 
 |         next_section = section->next; | 
 |         HeapFree( GetProcessHeap(), 0, section ); | 
 |     } | 
 | } | 
 |  | 
 | static inline int PROFILE_isspace(char c) | 
 | { | 
 | 	if (isspace(c)) return 1; | 
 | 	if (c=='\r' || c==0x1a) return 1; | 
 | 	/* CR and ^Z (DOS EOF) are spaces too  (found on CD-ROMs) */ | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_Load | 
 |  * | 
 |  * Load a profile tree from a file. | 
 |  */ | 
 | static PROFILESECTION *PROFILE_Load( FILE *file ) | 
 | { | 
 |     char buffer[PROFILE_MAX_LINE_LEN]; | 
 |     char *p, *p2; | 
 |     int line = 0; | 
 |     PROFILESECTION *section, *first_section; | 
 |     PROFILESECTION **next_section; | 
 |     PROFILEKEY *key, *prev_key, **next_key; | 
 |  | 
 |     first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ); | 
 |     if(first_section == NULL) return NULL;  | 
 |     first_section->name = NULL; | 
 |     first_section->key  = NULL; | 
 |     first_section->next = NULL; | 
 |     next_section = &first_section->next; | 
 |     next_key     = &first_section->key; | 
 |     prev_key     = NULL; | 
 |  | 
 |     while (fgets( buffer, PROFILE_MAX_LINE_LEN, file )) | 
 |     { | 
 |         line++; | 
 |         p = buffer; | 
 |         while (*p && PROFILE_isspace(*p)) p++; | 
 |         if (*p == '[')  /* section start */ | 
 |         { | 
 |             if (!(p2 = strrchr( p, ']' ))) | 
 |             { | 
 |                 WARN("Invalid section header at line %d: '%s'\n", | 
 | 		     line, p ); | 
 |             } | 
 |             else | 
 |             { | 
 |                 *p2 = '\0'; | 
 |                 p++; | 
 |                 section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ); | 
 | 		if(section == NULL) break; | 
 |                 section->name = HEAP_strdupA( GetProcessHeap(), 0, p ); | 
 |                 section->key  = NULL; | 
 |                 section->next = NULL; | 
 |                 *next_section = section; | 
 |                 next_section  = §ion->next; | 
 |                 next_key      = §ion->key; | 
 |                 prev_key      = NULL; | 
 |  | 
 |                 TRACE("New section: '%s'\n",section->name); | 
 |  | 
 |                 continue; | 
 |             } | 
 |         } | 
 |  | 
 |         p2=p+strlen(p) - 1; | 
 |         while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0'; | 
 |  | 
 |         if ((p2 = strchr( p, '=' )) != NULL) | 
 |         { | 
 |             char *p3 = p2 - 1; | 
 |             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0'; | 
 |             *p2++ = '\0'; | 
 |             while (*p2 && PROFILE_isspace(*p2)) p2++; | 
 |         } | 
 |  | 
 |         if(*p || !prev_key || *prev_key->name) | 
 |         { | 
 |            key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) ); | 
 | 	   if(key == NULL) break; | 
 |            key->name  = HEAP_strdupA( GetProcessHeap(), 0, p ); | 
 |            key->value = p2 ? HEAP_strdupA( GetProcessHeap(), 0, p2 ) : NULL; | 
 |            key->next  = NULL; | 
 |            *next_key  = key; | 
 |            next_key   = &key->next; | 
 |            prev_key   = key; | 
 |  | 
 |            TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)"); | 
 |         } | 
 |     } | 
 |     return first_section; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_RegistryLoad | 
 |  * | 
 |  * Load a profile tree from a file into a registry key. | 
 |  */ | 
 | static DWORD PROFILE_RegistryLoad( HKEY root, FILE *file ) | 
 | { | 
 |     HKEY hkey = 0; | 
 |     DWORD err = 0; | 
 |     char buffer[PROFILE_MAX_LINE_LEN]; | 
 |     char *p, *p2; | 
 |     int line = 0; | 
 |  | 
 |     while (fgets( buffer, PROFILE_MAX_LINE_LEN, file )) | 
 |     { | 
 |         line++; | 
 |         p = buffer; | 
 |         while (*p && PROFILE_isspace(*p)) p++; | 
 |         if (*p == '[')  /* section start */ | 
 |         { | 
 |             if (!(p2 = strrchr( p, ']' ))) | 
 |             { | 
 |                 WARN("Invalid section header at line %d: '%s'\n", | 
 | 		     line, p ); | 
 |             } | 
 |             else | 
 |             { | 
 |                 *p2 = '\0'; | 
 |                 p++; | 
 |                 if (hkey) RegCloseKey( hkey ); | 
 |                 if ((err = RegCreateKeyExA( root, p, 0, NULL, REG_OPTION_VOLATILE, | 
 |                                             KEY_ALL_ACCESS, NULL, &hkey, NULL ))) return err; | 
 |                 TRACE("New section: '%s'\n",p); | 
 |                 continue; | 
 |             } | 
 |         } | 
 |  | 
 |         p2=p+strlen(p) - 1; | 
 |         while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0'; | 
 |  | 
 |         if ((p2 = strchr( p, '=' )) != NULL) | 
 |         { | 
 |             char *p3 = p2 - 1; | 
 |             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0'; | 
 |             *p2++ = '\0'; | 
 |             while (*p2 && PROFILE_isspace(*p2)) p2++; | 
 |         } | 
 |  | 
 |         if (*p && hkey && !IS_ENTRY_COMMENT(p)) | 
 |         { | 
 |             if (!p2) p2 = ""; | 
 |             if ((err = RegSetValueExA( hkey, p, 0, REG_SZ, p2, strlen(p2)+1 ))) | 
 |             { | 
 |                 RegCloseKey( hkey ); | 
 |                 return err; | 
 |             } | 
 |             TRACE("New key: name='%s', value='%s'\n",p,p2); | 
 |         } | 
 |     } | 
 |     if (hkey) RegCloseKey( hkey ); | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_DeleteSection | 
 |  * | 
 |  * Delete a section from a profile tree. | 
 |  */ | 
 | static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name ) | 
 | { | 
 |     while (*section) | 
 |     { | 
 |         if ((*section)->name && !strcasecmp( (*section)->name, name )) | 
 |         { | 
 |             PROFILESECTION *to_del = *section; | 
 |             *section = to_del->next; | 
 |             to_del->next = NULL; | 
 |             PROFILE_Free( to_del ); | 
 |             return TRUE; | 
 |         } | 
 |         section = &(*section)->next; | 
 |     } | 
 |     return FALSE; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_DeleteKey | 
 |  * | 
 |  * Delete a key from a profile tree. | 
 |  */ | 
 | static BOOL PROFILE_DeleteKey( PROFILESECTION **section, | 
 | 			       LPCSTR section_name, LPCSTR key_name ) | 
 | { | 
 |     while (*section) | 
 |     { | 
 |         if ((*section)->name && !strcasecmp( (*section)->name, section_name )) | 
 |         { | 
 |             PROFILEKEY **key = &(*section)->key; | 
 |             while (*key) | 
 |             { | 
 |                 if (!strcasecmp( (*key)->name, key_name )) | 
 |                 { | 
 |                     PROFILEKEY *to_del = *key; | 
 |                     *key = to_del->next; | 
 |                     if (to_del->name) HeapFree( GetProcessHeap(), 0, to_del->name ); | 
 |                     if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value); | 
 |                     HeapFree( GetProcessHeap(), 0, to_del ); | 
 |                     return TRUE; | 
 |                 } | 
 |                 key = &(*key)->next; | 
 |             } | 
 |         } | 
 |         section = &(*section)->next; | 
 |     } | 
 |     return FALSE; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_Find | 
 |  * | 
 |  * Find a key in a profile tree, optionally creating it. | 
 |  */ | 
 | static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, | 
 |                                  const char *section_name, | 
 |                                  const char *key_name, int create ) | 
 | { | 
 |     const char *p; | 
 |     int seclen, keylen; | 
 |  | 
 |     while (PROFILE_isspace(*section_name)) section_name++; | 
 |     p = section_name + strlen(section_name) - 1; | 
 |     while ((p > section_name) && PROFILE_isspace(*p)) p--; | 
 |     seclen = p - section_name + 1; | 
 |      | 
 |     while (PROFILE_isspace(*key_name)) key_name++; | 
 |     p = key_name + strlen(key_name) - 1; | 
 |     while ((p > key_name) && PROFILE_isspace(*p)) p--; | 
 |     keylen = p - key_name + 1; | 
 |  | 
 |     while (*section) | 
 |     { | 
 |         if ( ((*section)->name) | 
 | 	  && (!(strncasecmp( (*section)->name, section_name, seclen ))) | 
 | 	  && (((*section)->name)[seclen] == '\0') ) | 
 |         { | 
 |             PROFILEKEY **key = &(*section)->key; | 
 |             while (*key) | 
 |             { | 
 |                 if ( (!(strncasecmp( (*key)->name, key_name, keylen ))) | 
 | 		  && (((*key)->name)[keylen] == '\0') ) | 
 | 		    return *key; | 
 |                 key = &(*key)->next; | 
 |             } | 
 |             if (!create) return NULL; | 
 |             *key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) ); | 
 | 	    if(*key == NULL) return NULL; | 
 |             (*key)->name  = HEAP_strdupA( GetProcessHeap(), 0, key_name ); | 
 |             (*key)->value = NULL; | 
 |             (*key)->next  = NULL; | 
 |             return *key; | 
 |         } | 
 |         section = &(*section)->next; | 
 |     } | 
 |     if (!create) return NULL; | 
 |     *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) ); | 
 |     if(*section == NULL) return NULL; | 
 |     (*section)->name = HEAP_strdupA( GetProcessHeap(), 0, section_name ); | 
 |     (*section)->next = NULL; | 
 |     (*section)->key  = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) ); | 
 |     if((*section)->key  == NULL) | 
 |     { | 
 | 	    HeapFree(GetProcessHeap(), 0, *section); | 
 | 	    return NULL; | 
 |     } | 
 |     (*section)->key->name  = HEAP_strdupA( GetProcessHeap(), 0, key_name ); | 
 |     (*section)->key->value = NULL; | 
 |     (*section)->key->next  = NULL; | 
 |     return (*section)->key; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_FlushFile | 
 |  * | 
 |  * Flush the current profile to disk if changed. | 
 |  */ | 
 | static BOOL PROFILE_FlushFile(void) | 
 | { | 
 |     char *p, buffer[MAX_PATHNAME_LEN]; | 
 |     const char *unix_name; | 
 |     FILE *file = NULL; | 
 |     struct stat buf; | 
 |  | 
 |     if(!CurProfile) | 
 |     { | 
 |         WARN("No current profile!\n"); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     if (!CurProfile->changed || !CurProfile->dos_name) return TRUE; | 
 |     if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w"))) | 
 |     { | 
 |         /* Try to create it in $HOME/.wine */ | 
 |         /* FIXME: this will need a more general solution */ | 
 |         strcpy( buffer, get_config_dir() ); | 
 |         p = buffer + strlen(buffer); | 
 |         *p++ = '/'; | 
 |         strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 ); | 
 |         CharLowerA( p ); | 
 |         file = fopen( buffer, "w" ); | 
 |         unix_name = buffer; | 
 |     } | 
 |      | 
 |     if (!file) | 
 |     { | 
 |         WARN("could not save profile file %s\n", CurProfile->dos_name); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name ); | 
 |     PROFILE_Save( file, CurProfile->section ); | 
 |     fclose( file ); | 
 |     CurProfile->changed = FALSE; | 
 |     if(!stat(unix_name,&buf)) | 
 |        CurProfile->mtime=buf.st_mtime; | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_ReleaseFile | 
 |  * | 
 |  * Flush the current profile to disk and remove it from the cache. | 
 |  */ | 
 | static void PROFILE_ReleaseFile(void) | 
 | { | 
 |     PROFILE_FlushFile(); | 
 |     PROFILE_Free( CurProfile->section ); | 
 |     if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name ); | 
 |     if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name ); | 
 |     if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename ); | 
 |     CurProfile->changed   = FALSE; | 
 |     CurProfile->section   = NULL; | 
 |     CurProfile->dos_name  = NULL; | 
 |     CurProfile->unix_name = NULL; | 
 |     CurProfile->filename  = NULL; | 
 |     CurProfile->mtime     = 0; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_Open | 
 |  * | 
 |  * Open a profile file, checking the cached file first. | 
 |  */ | 
 | static BOOL PROFILE_Open( LPCSTR filename ) | 
 | { | 
 |     DOS_FULL_NAME full_name; | 
 |     char buffer[MAX_PATHNAME_LEN]; | 
 |     char *newdos_name, *p; | 
 |     FILE *file = NULL; | 
 |     int i,j; | 
 |     struct stat buf; | 
 |     PROFILE *tempProfile; | 
 |  | 
 |     /* First time around */ | 
 |  | 
 |     if(!CurProfile) | 
 |        for(i=0;i<N_CACHED_PROFILES;i++) | 
 |          { | 
 |           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) ); | 
 | 	  if(MRUProfile[i] == NULL) break; | 
 |           MRUProfile[i]->changed=FALSE; | 
 |           MRUProfile[i]->section=NULL; | 
 |           MRUProfile[i]->dos_name=NULL; | 
 |           MRUProfile[i]->unix_name=NULL; | 
 |           MRUProfile[i]->filename=NULL; | 
 |           MRUProfile[i]->mtime=0; | 
 |          } | 
 |  | 
 |     /* Check for a match */ | 
 |  | 
 |     if (strchr( filename, '/' ) || strchr( filename, '\\' ) ||  | 
 |         strchr( filename, ':' )) | 
 |     { | 
 |         if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE; | 
 |     } | 
 |     else | 
 |     { | 
 |         GetWindowsDirectoryA( buffer, sizeof(buffer) ); | 
 |         strcat( buffer, "\\" ); | 
 |         strcat( buffer, filename ); | 
 |         if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE; | 
 |     } | 
 |  | 
 |     for(i=0;i<N_CACHED_PROFILES;i++) | 
 |       { | 
 |        if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) || | 
 |            (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name ))) | 
 |          { | 
 |           if(i) | 
 |             { | 
 |              PROFILE_FlushFile(); | 
 |              tempProfile=MRUProfile[i]; | 
 |              for(j=i;j>0;j--) | 
 |                 MRUProfile[j]=MRUProfile[j-1]; | 
 |              CurProfile=tempProfile; | 
 |             } | 
 |           if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime) | 
 |              TRACE("(%s): already opened (mru=%d)\n", | 
 |                               filename, i ); | 
 |           else | 
 |               TRACE("(%s): already opened, needs refreshing (mru=%d)\n", | 
 |                              filename, i ); | 
 | 	  return TRUE; | 
 |          } | 
 |       } | 
 |  | 
 |     /* Flush the old current profile */ | 
 |     PROFILE_FlushFile(); | 
 |  | 
 |     /* Make the oldest profile the current one only in order to get rid of it */ | 
 |     if(i==N_CACHED_PROFILES) | 
 |       { | 
 |        tempProfile=MRUProfile[N_CACHED_PROFILES-1]; | 
 |        for(i=N_CACHED_PROFILES-1;i>0;i--) | 
 |           MRUProfile[i]=MRUProfile[i-1]; | 
 |        CurProfile=tempProfile; | 
 |       } | 
 |     if(CurProfile->filename) PROFILE_ReleaseFile(); | 
 |  | 
 |     /* OK, now that CurProfile is definitely free we assign it our new file */ | 
 |     newdos_name = HEAP_strdupA( GetProcessHeap(), 0, full_name.short_name ); | 
 |     CurProfile->dos_name  = newdos_name; | 
 |     CurProfile->filename  = HEAP_strdupA( GetProcessHeap(), 0, filename ); | 
 |  | 
 |     /* Try to open the profile file, first in $HOME/.wine */ | 
 |  | 
 |     /* FIXME: this will need a more general solution */ | 
 |     strcpy( buffer, get_config_dir() ); | 
 |     p = buffer + strlen(buffer); | 
 |     *p++ = '/'; | 
 |     strcpy( p, strrchr( newdos_name, '\\' ) + 1 ); | 
 |     CharLowerA( p ); | 
 |     if ((file = fopen( buffer, "r" ))) | 
 |     { | 
 |         TRACE("(%s): found it in %s\n", | 
 |               filename, buffer ); | 
 |         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0, buffer ); | 
 |     } | 
 |  | 
 |     if (!file) | 
 |     { | 
 |         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0, | 
 |                                              full_name.long_name ); | 
 |         if ((file = fopen( full_name.long_name, "r" ))) | 
 |             TRACE("(%s): found it in %s\n", | 
 |                              filename, full_name.long_name ); | 
 |     } | 
 |  | 
 |     if (file) | 
 |     { | 
 |         CurProfile->section = PROFILE_Load( file ); | 
 |         fclose( file ); | 
 |         if(!stat(CurProfile->unix_name,&buf)) | 
 |            CurProfile->mtime=buf.st_mtime; | 
 |     } | 
 |     else | 
 |     { | 
 |         /* Does not exist yet, we will create it in PROFILE_FlushFile */ | 
 |         WARN("profile file %s not found\n", newdos_name ); | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_GetSection | 
 |  * | 
 |  * Returns all keys of a section. | 
 |  * If return_values is TRUE, also include the corresponding values. | 
 |  */ | 
 | static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name, | 
 | 			       LPSTR buffer, UINT len, BOOL handle_env, | 
 | 			       BOOL return_values ) | 
 | { | 
 |     PROFILEKEY *key; | 
 |     while (section) | 
 |     { | 
 |         if (section->name && !strcasecmp( section->name, section_name )) | 
 |         { | 
 |             UINT oldlen = len; | 
 |             for (key = section->key; key; key = key->next) | 
 |             { | 
 |                 if (len <= 2) break; | 
 |                 if (!*key->name) continue;  /* Skip empty lines */ | 
 |                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */ | 
 |                 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env ); | 
 |                 len -= strlen(buffer) + 1; | 
 |                 buffer += strlen(buffer) + 1; | 
 | 		if (return_values && key->value) { | 
 | 			buffer[-1] = '='; | 
 | 			PROFILE_CopyEntry ( buffer, | 
 | 				key->value, len - 1, handle_env ); | 
 | 			len -= strlen(buffer) + 1; | 
 | 			buffer += strlen(buffer) + 1; | 
 |                 } | 
 |             } | 
 |             *buffer = '\0'; | 
 |             if (len <= 1) | 
 |                 /*If either lpszSection or lpszKey is NULL and the supplied | 
 |                   destination buffer is too small to hold all the strings,  | 
 |                   the last string is truncated and followed by two null characters. | 
 |                   In this case, the return value is equal to cchReturnBuffer | 
 |                   minus two. */ | 
 |             { | 
 | 		buffer[-1] = '\0'; | 
 |                 return oldlen - 2; | 
 |             } | 
 |             return oldlen - len; | 
 |         } | 
 |         section = section->next; | 
 |     } | 
 |     buffer[0] = buffer[1] = '\0'; | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len ) | 
 | { | 
 |     LPSTR buf = buffer; | 
 |     WORD l, cursize = 0; | 
 |     PROFILESECTION *section; | 
 |  | 
 |     for (section = CurProfile->section; section; section = section->next) | 
 | 	if (section->name) { | 
 | 	    l = strlen(section->name); | 
 | 	    cursize += l+1; | 
 | 	    if (cursize > len+1) | 
 | 		return len-2; | 
 |  | 
 | 	    strcpy(buf, section->name); | 
 | 	    buf += l+1; | 
 | 	} | 
 |  | 
 |     *buf=0; | 
 |     buf++; | 
 |     return buf-buffer; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_GetString | 
 |  * | 
 |  * Get a profile string. | 
 |  */ | 
 | static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name, | 
 | 			      LPCSTR def_val, LPSTR buffer, UINT len ) | 
 | { | 
 |     PROFILEKEY *key = NULL; | 
 |  | 
 |     if (!def_val) def_val = ""; | 
 |     if (key_name && key_name[0]) | 
 |     { | 
 |         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE ); | 
 |         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val, | 
 |                            len, FALSE ); | 
 |         TRACE("('%s','%s','%s'): returning '%s'\n", | 
 |                          section, key_name, def_val, buffer ); | 
 |         return strlen( buffer ); | 
 |     } | 
 |     if (key_name && !(key_name[0])) | 
 |       /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227*/ | 
 |       return 0; | 
 |     if (section && section[0]) | 
 |         return PROFILE_GetSection(CurProfile->section, section, buffer, len, | 
 | 				FALSE, FALSE); | 
 |     /* undocumented; both section and key_name are NULL */ | 
 |     return PROFILE_GetSectionNames(buffer, len); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_SetString | 
 |  * | 
 |  * Set a profile string. | 
 |  */ | 
 | static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name, | 
 | 			       LPCSTR value ) | 
 | { | 
 |     if (!key_name)  /* Delete a whole section */ | 
 |     { | 
 |         TRACE("('%s')\n", section_name); | 
 |         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section, | 
 |                                                       section_name ); | 
 |         return TRUE;         /* Even if PROFILE_DeleteSection() has failed, | 
 |                                 this is not an error on application's level.*/ | 
 |     } | 
 |     else if (!value)  /* Delete a key */ | 
 |     { | 
 |         TRACE("('%s','%s')\n", | 
 |                          section_name, key_name ); | 
 |         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section, | 
 |                                                   section_name, key_name ); | 
 |         return TRUE;          /* same error handling as above */ | 
 |     } | 
 |     else  /* Set the key value */ | 
 |     { | 
 |         PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name, | 
 |                                         key_name, TRUE ); | 
 |         TRACE("('%s','%s','%s'): \n", | 
 |                          section_name, key_name, value ); | 
 |         if (!key) return FALSE; | 
 |         if (key->value) | 
 |         { | 
 |             if (!strcmp( key->value, value )) | 
 |             { | 
 |                 TRACE("  no change needed\n" ); | 
 |                 return TRUE;  /* No change needed */ | 
 |             } | 
 |             TRACE("  replacing '%s'\n", key->value ); | 
 |             HeapFree( GetProcessHeap(), 0, key->value ); | 
 |         } | 
 |         else TRACE("  creating key\n" ); | 
 |         key->value = HEAP_strdupA( GetProcessHeap(), 0, value ); | 
 |         CurProfile->changed = TRUE; | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_GetWineIniString | 
 |  * | 
 |  * Get a config string from the wine.ini file. | 
 |  */ | 
 | int PROFILE_GetWineIniString( const char *section, const char *key_name, | 
 |                               const char *def, char *buffer, int len ) | 
 | { | 
 |     char tmp[PROFILE_MAX_LINE_LEN]; | 
 |     HKEY hkey; | 
 |     DWORD err; | 
 |  | 
 |     if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey ))) | 
 |     { | 
 |         DWORD type; | 
 |         DWORD count = sizeof(tmp); | 
 |         err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count ); | 
 |         RegCloseKey( hkey ); | 
 |     } | 
 |     PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE ); | 
 |     TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer ); | 
 |     return strlen(buffer); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_EnumWineIniString | 
 |  * | 
 |  * Get a config string from the wine.ini file. | 
 |  */ | 
 | BOOL PROFILE_EnumWineIniString( const char *section, int index, | 
 |                                 char *name, int name_len, char *buffer, int len ) | 
 | { | 
 |     char tmp[PROFILE_MAX_LINE_LEN]; | 
 |     HKEY hkey; | 
 |     DWORD err, type; | 
 |     DWORD count = sizeof(tmp); | 
 |  | 
 |     if (RegOpenKeyA( wine_profile_key, section, &hkey )) return FALSE; | 
 |     err = RegEnumValueA( hkey, index, name, (DWORD*)&name_len, NULL, &type, tmp, &count ); | 
 |     RegCloseKey( hkey ); | 
 |     if (!err) | 
 |     { | 
 |         PROFILE_CopyEntry( buffer, tmp, len, TRUE ); | 
 |         TRACE( "('%s',%d): returning '%s'='%s'\n", section, index, name, buffer ); | 
 |     } | 
 |     return !err; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_GetWineIniInt | 
 |  * | 
 |  * Get a config integer from the wine.ini file. | 
 |  */ | 
 | int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def ) | 
 | { | 
 |     char buffer[20]; | 
 |     char *p; | 
 |     long result; | 
 |  | 
 |     PROFILE_GetWineIniString( section, key_name, "", buffer, sizeof(buffer) ); | 
 |     if (!buffer[0]) return def; | 
 |     result = strtol( buffer, &p, 0 ); | 
 |     return (p == buffer) ? 0  /* No digits at all */ : (int)result; | 
 | } | 
 |  | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *   int  PROFILE_GetWineIniBool( | 
 |  *      char const  *section, | 
 |  *      char const  *key_name, | 
 |  *      int  def ) | 
 |  * | 
 |  *   Reads a boolean value from the wine.ini file.  This function attempts to | 
 |  *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0' | 
 |  *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for | 
 |  *   true.  Anything else results in the return of the default value. | 
 |  * | 
 |  *   This function uses 1 to indicate true, and 0 for false.  You can check | 
 |  *   for existence by setting def to something other than 0 or 1 and | 
 |  *   examining the return value. | 
 |  */ | 
 | int  PROFILE_GetWineIniBool( | 
 |     char const  *section, | 
 |     char const  *key_name, | 
 |     int  def ) | 
 | { | 
 |     char  key_value[2]; | 
 |     int  retval; | 
 |  | 
 |     PROFILE_GetWineIniString(section, key_name, "~", key_value, 2); | 
 |  | 
 |     switch(key_value[0]) { | 
 |     case 'n': | 
 |     case 'N': | 
 |     case 'f': | 
 |     case 'F': | 
 |     case '0': | 
 | 	retval = 0; | 
 | 	break; | 
 |  | 
 |     case 'y': | 
 |     case 'Y': | 
 |     case 't': | 
 |     case 'T': | 
 |     case '1': | 
 | 	retval = 1; | 
 | 	break; | 
 |  | 
 |     default: | 
 | 	retval = def; | 
 |     } | 
 |  | 
 |     TRACE("(\"%s\", \"%s\", %s), " | 
 | 		    "[%c], ret %s.\n", section, key_name, | 
 | 		    def ? "TRUE" : "FALSE", key_value[0], | 
 | 		    retval ? "TRUE" : "FALSE"); | 
 |  | 
 |     return retval; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_LoadWineIni | 
 |  * | 
 |  * Load the wine.ini file. | 
 |  */ | 
 | int PROFILE_LoadWineIni(void) | 
 | { | 
 |     char buffer[MAX_PATHNAME_LEN]; | 
 |     const char *p; | 
 |     FILE *f; | 
 |     HKEY hKeySW; | 
 |  | 
 |     /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */ | 
 |     if (RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine", &hKeySW )) | 
 |     { | 
 |         ERR("Cannot create config registry key\n" ); | 
 |         return 0; | 
 |     } | 
 |     RegCloseKey( hKeySW ); | 
 |     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", 0, NULL, | 
 |                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &wine_profile_key, NULL )) | 
 |     { | 
 |         ERR("Cannot create config registry key\n" ); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     if (!CLIENT_IsBootThread()) return 1;  /* already loaded */ | 
 |  | 
 |     if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) ) | 
 |     { | 
 |       /* Open -config specified file */ | 
 |       lstrcpynA(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN); | 
 |       goto found; | 
 |     } | 
 |  | 
 |     if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) ) | 
 |     { | 
 | 	lstrcpynA(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN); | 
 | 	goto found; | 
 |     } | 
 |     if ((p = getenv( "HOME" )) != NULL) | 
 |     { | 
 |         lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName)); | 
 |         strcat( buffer, PROFILE_WineIniName ); | 
 |         if ((f = fopen( buffer, "r" )) != NULL) | 
 |         { | 
 | 	    lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN); | 
 |             goto found; | 
 |         } | 
 |     } | 
 |     else WARN("could not get $HOME value for config file.\n" ); | 
 |  | 
 |     /* Try global file */ | 
 |  | 
 |     if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL) | 
 |     { | 
 | 	lstrcpynA(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN); | 
 |         goto found; | 
 |     } | 
 |     MESSAGE( "Can't open configuration file %s or $HOME%s\n", | 
 | 	     WINE_INI_GLOBAL, PROFILE_WineIniName ); | 
 |     return 0; | 
 |  | 
 |  found: | 
 |     PROFILE_RegistryLoad( wine_profile_key, f ); | 
 |     fclose( f ); | 
 |     return 1; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_UsageWineIni | 
 |  * | 
 |  * Explain the wine.ini file to those who don't read documentation. | 
 |  * Keep below one screenful in length so that error messages above are | 
 |  * noticed. | 
 |  */ | 
 | void PROFILE_UsageWineIni(void) | 
 | { | 
 |     MESSAGE("Perhaps you have not properly edited or created " | 
 | 	"your Wine configuration file.\n"); | 
 |     MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName); | 
 |     MESSAGE("  or it is determined by the -config option or from\n" | 
 |         "  the WINE_INI environment variable.\n"); | 
 |     if (*PROFILE_WineIniUsed) | 
 | 	MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed); | 
 |     /* RTFM, so to say */ | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           PROFILE_GetStringItem | 
 |  * | 
 |  *  Convenience function that turns a string 'xxx, yyy, zzz' into  | 
 |  *  the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'. | 
 |  */ | 
 | char* PROFILE_GetStringItem( char* start ) | 
 | { | 
 |     char* lpchX, *lpch; | 
 |  | 
 |     for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ ) | 
 |     { | 
 |         if( *lpchX == ',' ) | 
 |         { | 
 |             if( lpch ) *lpch = '\0'; else *lpchX = '\0'; | 
 |             while( *(++lpchX) ) | 
 |                 if( !PROFILE_isspace(*lpchX) ) return lpchX; | 
 |         } | 
 | 	else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX; | 
 | 	     else lpch = NULL; | 
 |     } | 
 |     if( lpch ) *lpch = '\0'; | 
 |     return NULL; | 
 | } | 
 |  | 
 | /********************* API functions **********************************/ | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileInt16   (KERNEL.57) | 
 |  */ | 
 | UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val ) | 
 | { | 
 |     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" ); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileIntA   (KERNEL32.264) | 
 |  */ | 
 | UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val ) | 
 | { | 
 |     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileIntW   (KERNEL32.264) | 
 |  */ | 
 | UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val ) | 
 | { | 
 |     return GetPrivateProfileIntW( section, entry, def_val, wininiW ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileString16   (KERNEL.58) | 
 |  */ | 
 | INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val, | 
 |                                  LPSTR buffer, UINT16 len ) | 
 | { | 
 |     return GetPrivateProfileString16( section, entry, def_val, | 
 |                                       buffer, len, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileStringA   (KERNEL32.268) | 
 |  */ | 
 | INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val, | 
 | 			      LPSTR buffer, UINT len ) | 
 | { | 
 |     return GetPrivateProfileStringA( section, entry, def_val, | 
 | 				     buffer, len, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileStringW   (KERNEL32.269) | 
 |  */ | 
 | INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry, | 
 | 			      LPCWSTR def_val, LPWSTR buffer, UINT len ) | 
 | { | 
 |     return GetPrivateProfileStringW( section, entry, def_val, | 
 | 				     buffer, len, wininiW ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileString16   (KERNEL.59) | 
 |  */ | 
 | BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry, | 
 |                                     LPCSTR string ) | 
 | { | 
 |     return WritePrivateProfileString16( section, entry, string, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileStringA   (KERNEL32.587) | 
 |  */ | 
 | BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry, | 
 | 				 LPCSTR string ) | 
 | { | 
 |     return WritePrivateProfileStringA( section, entry, string, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileStringW   (KERNEL32.588) | 
 |  */ | 
 | BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry, | 
 |                                      LPCWSTR string ) | 
 | { | 
 |     return WritePrivateProfileStringW( section, entry, string, wininiW ); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileInt16   (KERNEL.127) | 
 |  */ | 
 | UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry, | 
 |                                       INT16 def_val, LPCSTR filename ) | 
 | { | 
 |     long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename); | 
 |  | 
 |     if (result > 65535) return 65535; | 
 |     if (result >= 0) return (UINT16)result; | 
 |     if (result < -32768) return -32768; | 
 |     return (UINT16)(INT16)result; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileIntA   (KERNEL32.251) | 
 |  */ | 
 | UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry, | 
 | 				   INT def_val, LPCSTR filename ) | 
 | { | 
 |     char buffer[20]; | 
 |     char *p; | 
 |     long result; | 
 |  | 
 |     GetPrivateProfileStringA( section, entry, "", | 
 |                                 buffer, sizeof(buffer), filename ); | 
 |     if (!buffer[0]) return (UINT)def_val; | 
 |     result = strtol( buffer, &p, 0 ); | 
 |     if (p == buffer) return 0;  /* No digits at all */ | 
 |     return (UINT)result; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileIntW   (KERNEL32.252) | 
 |  */ | 
 | UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry, | 
 | 				   INT def_val, LPCWSTR filename ) | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry ); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, entryA ); | 
 |     return res; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileString16   (KERNEL.128) | 
 |  */ | 
 | INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry, | 
 |                                         LPCSTR def_val, LPSTR buffer, | 
 |                                         UINT16 len, LPCSTR filename ) | 
 | { | 
 |     return GetPrivateProfileStringA(section,entry,def_val,buffer,len,filename); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileStringA   (KERNEL32.255) | 
 |  */ | 
 | INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry, | 
 | 				     LPCSTR def_val, LPSTR buffer, | 
 | 				     UINT len, LPCSTR filename ) | 
 | { | 
 |     int		ret; | 
 |  | 
 |     if (!filename)  | 
 | 	filename = "win.ini"; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) { | 
 |         ret = PROFILE_GetString( section, entry, def_val, buffer, len ); | 
 |     } else { | 
 |        lstrcpynA( buffer, def_val, len ); | 
 |        ret = strlen( buffer ); | 
 |     } | 
 |  | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |      | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileStringW   (KERNEL32.256) | 
 |  */ | 
 | INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry, | 
 | 				     LPCWSTR def_val, LPWSTR buffer, | 
 | 				     UINT len, LPCWSTR filename ) | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry ); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     LPSTR def_valA  = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val ); | 
 |     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len ); | 
 |     INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA, | 
 |                                             bufferA, len, filenameA ); | 
 |     lstrcpynAtoW( buffer, bufferA, len ); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, entryA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, def_valA ); | 
 |     HeapFree( GetProcessHeap(), 0, bufferA); | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSection16   (KERNEL.418) | 
 |  */ | 
 | INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer, | 
 | 				        UINT16 len, LPCSTR filename ) | 
 | { | 
 |     return GetPrivateProfileSectionA( section, buffer, len, filename ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSectionA   (KERNEL32.255) | 
 |  */ | 
 | INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer, | 
 | 				      DWORD len, LPCSTR filename ) | 
 | { | 
 |     int		ret = 0; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) | 
 |         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, | 
 | 				 FALSE, TRUE); | 
 |      | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSectionW   (KERNEL32.256) | 
 |  */ | 
 |  | 
 | INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer, | 
 | 				      DWORD len, LPCWSTR filename ) | 
 |  | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len ); | 
 |     INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len,  | 
 | 						filenameA ); | 
 |     MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, bufferA); | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileSection16   (KERNEL.419) | 
 |  */ | 
 | INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len ) | 
 | { | 
 |     return GetPrivateProfileSection16( section, buffer, len, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileSectionA   (KERNEL32.268) | 
 |  */ | 
 | INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len ) | 
 | { | 
 |     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileSectionW   (KERNEL32) | 
 |  */ | 
 | INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len ) | 
 | { | 
 |     return GetPrivateProfileSectionW( section, buffer, len, wininiW ); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileString16   (KERNEL.129) | 
 |  */ | 
 | BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry, | 
 |                                            LPCSTR string, LPCSTR filename ) | 
 | { | 
 |     return WritePrivateProfileStringA(section,entry,string,filename); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileStringA   (KERNEL32.582) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry, | 
 | 					LPCSTR string, LPCSTR filename ) | 
 | { | 
 |     BOOL ret = FALSE; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) | 
 |     { | 
 |         if (!section && !entry && !string) | 
 |             PROFILE_ReleaseFile();  /* always return FALSE in this case */ | 
 |         else | 
 |             ret = PROFILE_SetString( section, entry, string ); | 
 |     } | 
 |  | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileStringW   (KERNEL32.583) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry, | 
 | 					LPCWSTR string, LPCWSTR filename ) | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry ); | 
 |     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string ); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     BOOL res = WritePrivateProfileStringA( sectionA, entryA, | 
 | 					   stringA, filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, entryA ); | 
 |     HeapFree( GetProcessHeap(), 0, stringA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |     return res; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileSection16   (KERNEL.416) | 
 |  */ | 
 | BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,  | 
 | 				 	    LPCSTR string, LPCSTR filename ) | 
 | { | 
 |     return WritePrivateProfileSectionA( section, string, filename ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileSectionA   (KERNEL32) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,  | 
 | 					 LPCSTR string, LPCSTR filename ) | 
 | { | 
 |     BOOL ret = FALSE; | 
 |     LPSTR p ; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) { | 
 |         if (!section && !string && !filename) | 
 |             PROFILE_ReleaseFile();  /* always return FALSE in this case */ | 
 |         else { | 
 |             while(*string){ | 
 |                 LPSTR buf=HEAP_strdupA( GetProcessHeap(), 0, string ); | 
 |                 if((p=strchr( buf, '='))){ | 
 |                     *p='\0'; | 
 |                     ret = PROFILE_SetString( section, buf, p+1 ); | 
 |                      | 
 |                 } | 
 |                 HeapFree( GetProcessHeap(), 0, buf ); | 
 |                 string += strlen(string)+1; | 
 |             } | 
 |              | 
 |         } | 
 |     } | 
 |  | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileSectionW   (KERNEL32) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section, | 
 | 					 LPCWSTR string, LPCWSTR filename) | 
 |  | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string ); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, stringA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |     return res; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileSection16   (KERNEL.417) | 
 |  */ | 
 | BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values) | 
 | { | 
 |     return WritePrivateProfileSection16( section, keys_n_values, "win.ini"); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileSectionA   (KERNEL32.747) | 
 |  */ | 
 | BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values) | 
 |                                       | 
 | { | 
 |     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini"); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteProfileSectionW   (KERNEL32.748) | 
 |  */ | 
 | BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values) | 
 | { | 
 |    return (WritePrivateProfileSectionW (section,keys_n_values, wininiW)); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSectionNames16   (KERNEL.143) | 
 |  */ | 
 | WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size, | 
 |                                              LPCSTR filename ) | 
 | { | 
 |     WORD ret = 0; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) | 
 | 	ret = PROFILE_GetSectionNames(buffer, size); | 
 |  | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetProfileSectionNames16   (KERNEL.142) | 
 |  */ | 
 | WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size) | 
 |  | 
 | { | 
 |     return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini")); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSectionNamesA  (KERNEL32.365) | 
 |  */ | 
 | DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size, | 
 | 					     LPCSTR filename) | 
 |  | 
 | { | 
 |  return (GetPrivateProfileSectionNames16 (buffer,size,filename)); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileSectionNamesW  (KERNEL32.366) | 
 |  */ | 
 | DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size, | 
 | 					     LPCWSTR filename) | 
 |  | 
 | { | 
 |    LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |    LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size); | 
 |  | 
 |    INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA); | 
 |    lstrcpynAtoW( buffer, bufferA, size);    | 
 |    HeapFree( GetProcessHeap(), 0, bufferA); | 
 |    HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |  | 
 |    return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileStruct16 (KERNEL.407) | 
 |  */ | 
 | BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,  | 
 |  				        LPVOID buf, UINT16 len, LPCSTR filename) | 
 | { | 
 |     return GetPrivateProfileStructA( section, key, buf, len, filename ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileStructA (KERNEL32.370) | 
 |  * | 
 |  * Should match Win95's behaviour pretty much | 
 |  */ | 
 | BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,  | 
 | 				      LPVOID buf, UINT len, LPCSTR filename) | 
 | { | 
 |     BOOL	ret = FALSE; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename )) { | 
 |         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE); | 
 | 	if (k) { | 
 | 	    TRACE("value (at %p): '%s'\n", k->value, k->value); | 
 | 	    if (((strlen(k->value) - 2) / 2) == len) | 
 | 	    { | 
 | 		LPSTR end, p; | 
 | 		BOOL valid = TRUE; | 
 | 		CHAR c; | 
 | 		DWORD chksum = 0; | 
 |  | 
 | 	        end  = k->value + strlen(k->value); /* -> '\0' */ | 
 | 	        /* check for invalid chars in ASCII coded hex string */ | 
 | 	        for (p=k->value; p < end; p++) | 
 | 		{ | 
 |                     if (!isxdigit(*p)) | 
 | 		    { | 
 | 			WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n", | 
 |                              *p, filename, section, key); | 
 | 		        valid = FALSE; | 
 | 		        break; | 
 | 		    } | 
 | 		} | 
 | 		if (valid) | 
 | 		{ | 
 | 		    BOOL highnibble = TRUE; | 
 | 		    BYTE b = 0, val; | 
 | 		    LPBYTE binbuf = (LPBYTE)buf; | 
 | 		     | 
 | 	            end -= 2; /* don't include checksum in output data */ | 
 | 	            /* translate ASCII hex format into binary data */ | 
 |                     for (p=k->value; p < end; p++) | 
 |             	    { | 
 | 	        	c = toupper(*p); | 
 | 			val = (c > '9') ? | 
 | 				(c - 'A' + 10) : (c - '0'); | 
 |  | 
 | 			if (highnibble) | 
 | 		    	    b = val << 4; | 
 | 			else | 
 | 			{ | 
 | 		    	    b += val; | 
 | 		    	    *binbuf++ = b; /* feed binary data into output */ | 
 | 		    	    chksum += b; /* calculate checksum */ | 
 | 			} | 
 | 			highnibble ^= 1; /* toggle */ | 
 |             	    } | 
 | 		    /* retrieve stored checksum value */ | 
 | 		    c = toupper(*p++); | 
 | 		    b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4; | 
 | 		    c = toupper(*p); | 
 | 		    b +=  (c > '9') ? (c - 'A' + 10) : (c - '0'); | 
 | 	            if (b == (chksum & 0xff)) /* checksums match ? */ | 
 |                         ret = TRUE; | 
 |                 } | 
 |             } | 
 | 	} | 
 |     } | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GetPrivateProfileStructW (KERNEL32.543) | 
 |  */ | 
 | BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key, | 
 | 				      LPVOID buffer, UINT len, LPCWSTR filename) | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len ); | 
 |  | 
 |     INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA, | 
 | 					len, filenameA ); | 
 |     lstrcpynAtoW( buffer, bufferA, len ); | 
 |     HeapFree( GetProcessHeap(), 0, bufferA); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, keyA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileStruct16 (KERNEL.406) | 
 |  */ | 
 | BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,  | 
 | 	LPVOID buf, UINT16 bufsize, LPCSTR filename) | 
 | { | 
 |     return WritePrivateProfileStructA( section, key, buf, bufsize, filename ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileStructA (KERNEL32.744) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,  | 
 |                                         LPVOID buf, UINT bufsize, LPCSTR filename) | 
 | { | 
 |     BOOL ret = FALSE; | 
 |     LPBYTE binbuf; | 
 |     LPSTR outstring, p; | 
 |     DWORD sum = 0; | 
 |  | 
 |     if (!section && !key && !buf)  /* flush the cache */ | 
 |         return WritePrivateProfileStringA( NULL, NULL, NULL, filename ); | 
 |  | 
 |     /* allocate string buffer for hex chars + checksum hex char + '\0' */ | 
 |     outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1); | 
 |     p = outstring; | 
 |     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) { | 
 |       *p++ = hex[*binbuf >> 4]; | 
 |       *p++ = hex[*binbuf & 0xf]; | 
 |       sum += *binbuf; | 
 |     } | 
 |     /* checksum is sum & 0xff */ | 
 |     *p++ = hex[(sum & 0xf0) >> 4]; | 
 |     *p++ = hex[sum & 0xf]; | 
 |     *p++ = '\0'; | 
 |  | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     if (PROFILE_Open( filename ))  | 
 |         ret = PROFILE_SetString( section, key, outstring ); | 
 |  | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 |  | 
 |     HeapFree( GetProcessHeap(), 0, outstring ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           WritePrivateProfileStructW (KERNEL32.544) | 
 |  */ | 
 | BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key, | 
 | 					LPVOID buf, UINT bufsize, LPCWSTR filename) | 
 | { | 
 |     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section ); | 
 |     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key); | 
 |     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); | 
 |     INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize, | 
 | 					  filenameA ); | 
 |     HeapFree( GetProcessHeap(), 0, sectionA ); | 
 |     HeapFree( GetProcessHeap(), 0, keyA ); | 
 |     HeapFree( GetProcessHeap(), 0, filenameA ); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  *           WriteOutProfiles   (KERNEL.315) | 
 |  */ | 
 | void WINAPI WriteOutProfiles16(void) | 
 | { | 
 |     EnterCriticalSection( &PROFILE_CritSect ); | 
 |     PROFILE_FlushFile(); | 
 |     LeaveCriticalSection( &PROFILE_CritSect ); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           CloseProfileUserMapping   (KERNEL.138) | 
 |  */ | 
 | BOOL WINAPI CloseProfileUserMapping(void) { | 
 |     FIXME("(), stub!\n"); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return FALSE; | 
 | } |