Initialize the NONCLIENTMETRICS with 0 (helps moorhuhn.exe).
[wine] / files / profile.c
1 /*
2  * Profile functions
3  *
4  * Copyright 1993 Miguel de Icaza
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <pwd.h>
15 #include <unistd.h>
16
17 #include "winbase.h"
18 #include "winerror.h"
19 #include "wine/winbase16.h"
20 #include "windef.h"
21 #include "wingdi.h"
22 #include "winuser.h"
23 #include "winnls.h"
24 #include "file.h"
25 #include "heap.h"
26 #include "debugtools.h"
27 #include "xmalloc.h"
28 #include "options.h"
29
30 DEFAULT_DEBUG_CHANNEL(profile);
31
32 typedef struct tagPROFILEKEY
33 {
34     char                  *name;
35     char                  *value;
36     struct tagPROFILEKEY  *next;
37 } PROFILEKEY;
38
39 typedef struct tagPROFILESECTION
40 {
41     char                       *name;
42     struct tagPROFILEKEY       *key;
43     struct tagPROFILESECTION   *next;
44 } PROFILESECTION; 
45
46
47 typedef struct
48 {
49     BOOL             changed;
50     PROFILESECTION  *section;
51     char            *dos_name;
52     char            *unix_name;
53     char            *filename;
54     time_t           mtime;
55 } PROFILE;
56
57
58 #define N_CACHED_PROFILES 10
59
60 /* Cached profile files */
61 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
62
63 #define CurProfile (MRUProfile[0])
64
65 /* wine.ini profile content */
66 static PROFILESECTION *PROFILE_WineProfile;
67
68 #define PROFILE_MAX_LINE_LEN   1024
69
70 /* Wine profile name in $HOME directory; must begin with slash */
71 static const char PROFILE_WineIniName[] = "/.winerc";
72
73 /* Wine profile: the profile file being used */
74 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
75
76 /* Check for comments in profile */
77 #define IS_ENTRY_COMMENT(str)  ((str)[0] == ';')
78
79 #define WINE_INI_GLOBAL ETCDIR "/wine.conf"
80 #define WINE_CONFIG_DIR "/.wine"   /* config dir inside $HOME */
81
82 static LPCWSTR wininiW = NULL;
83
84 static CRITICAL_SECTION PROFILE_CritSect;
85
86 static const char hex[16] = "0123456789ABCDEF";
87
88 /***********************************************************************
89  *           PROFILE_GetConfigDir
90  *
91  * Return the name of the configuration directory ($HOME/.wine)
92  */
93 const char *PROFILE_GetConfigDir(void)
94 {
95     static char *confdir;
96     if (!confdir)
97     {
98         const char *home = getenv( "HOME" );
99         if (!home)
100         {
101             struct passwd *pwd = getpwuid( getuid() );
102             if (!pwd)
103             {
104                 fprintf( stderr, "wine: could not find your home directory\n" );
105                 exit(1);
106             }
107             home = pwd->pw_dir;
108         }
109         confdir = xmalloc( strlen(home) + strlen(WINE_CONFIG_DIR) + 1 );
110         strcpy( confdir, home );
111         strcat( confdir, WINE_CONFIG_DIR );
112         mkdir( confdir, 0755 );  /* create it just in case */
113     }
114     return confdir;
115 }
116
117
118 /***********************************************************************
119  *           PROFILE_CopyEntry
120  *
121  * Copy the content of an entry into a buffer, removing quotes, and possibly
122  * translating environment variables.
123  */
124 static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
125                                int handle_env )
126 {
127     char quote = '\0';
128     const char *p;
129
130     if ((*value == '\'') || (*value == '\"'))
131     {
132         if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
133     }
134
135     if (!handle_env)
136     {
137         lstrcpynA( buffer, value, len );
138         if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
139         return;
140     }
141
142     for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
143     {
144         if ((*p == '$') && (p[1] == '{'))
145         {
146             char env_val[1024];
147             const char *env_p;
148             const char *p2 = strchr( p, '}' );
149             if (!p2) continue;  /* ignore it */
150             lstrcpynA(env_val, p + 2, MIN( sizeof(env_val), (int)(p2-p)-1 ));
151             if ((env_p = getenv( env_val )) != NULL)
152             {
153                 lstrcpynA( buffer, env_p, len );
154                 buffer += strlen( buffer );
155                 len -= strlen( buffer );
156             }
157             p = p2 + 1;
158         }
159     }
160     if (quote && (len > 1)) buffer--;
161     *buffer = '\0';
162 }
163
164
165 /***********************************************************************
166  *           PROFILE_Save
167  *
168  * Save a profile tree to a file.
169  */
170 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
171 {
172     PROFILEKEY *key;
173
174     for ( ; section; section = section->next)
175     {
176         if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name );
177         for (key = section->key; key; key = key->next)
178         {
179             fprintf( file, "%s", key->name );
180             if (key->value) fprintf( file, "=%s", key->value );
181             fprintf( file, "\r\n" );
182         }
183     }
184 }
185
186
187 /***********************************************************************
188  *           PROFILE_Free
189  *
190  * Free a profile tree.
191  */
192 static void PROFILE_Free( PROFILESECTION *section )
193 {
194     PROFILESECTION *next_section;
195     PROFILEKEY *key, *next_key;
196
197     for ( ; section; section = next_section)
198     {
199         if (section->name) HeapFree( SystemHeap, 0, section->name );
200         for (key = section->key; key; key = next_key)
201         {
202             next_key = key->next;
203             if (key->name) HeapFree( SystemHeap, 0, key->name );
204             if (key->value) HeapFree( SystemHeap, 0, key->value );
205             HeapFree( SystemHeap, 0, key );
206         }
207         next_section = section->next;
208         HeapFree( SystemHeap, 0, section );
209     }
210 }
211
212 static inline int PROFILE_isspace(char c)
213 {
214         if (isspace(c)) return 1;
215         if (c=='\r' || c==0x1a) return 1;
216         /* CR and ^Z (DOS EOF) are spaces too  (found on CD-ROMs) */
217         return 0;
218 }
219
220
221 /***********************************************************************
222  *           PROFILE_Load
223  *
224  * Load a profile tree from a file.
225  */
226 static PROFILESECTION *PROFILE_Load( FILE *file )
227 {
228     char buffer[PROFILE_MAX_LINE_LEN];
229     char *p, *p2;
230     int line = 0;
231     PROFILESECTION *section, *first_section;
232     PROFILESECTION **next_section;
233     PROFILEKEY *key, *prev_key, **next_key;
234
235     first_section = HEAP_xalloc( SystemHeap, 0, sizeof(*section) );
236     first_section->name = NULL;
237     first_section->key  = NULL;
238     first_section->next = NULL;
239     next_section = &first_section->next;
240     next_key     = &first_section->key;
241     prev_key     = NULL;
242
243     while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
244     {
245         line++;
246         p = buffer;
247         while (*p && PROFILE_isspace(*p)) p++;
248         if (*p == '[')  /* section start */
249         {
250             if (!(p2 = strrchr( p, ']' )))
251             {
252                 WARN("Invalid section header at line %d: '%s'\n",
253                      line, p );
254             }
255             else
256             {
257                 *p2 = '\0';
258                 p++;
259                 section = HEAP_xalloc( SystemHeap, 0, sizeof(*section) );
260                 section->name = HEAP_strdupA( SystemHeap, 0, p );
261                 section->key  = NULL;
262                 section->next = NULL;
263                 *next_section = section;
264                 next_section  = &section->next;
265                 next_key      = &section->key;
266                 prev_key      = NULL;
267
268                 TRACE("New section: '%s'\n",section->name);
269
270                 continue;
271             }
272         }
273
274         p2=p+strlen(p) - 1;
275         while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
276
277         if ((p2 = strchr( p, '=' )) != NULL)
278         {
279             char *p3 = p2 - 1;
280             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
281             *p2++ = '\0';
282             while (*p2 && PROFILE_isspace(*p2)) p2++;
283         }
284
285         if(*p || !prev_key || *prev_key->name)
286           {
287            key = HEAP_xalloc( SystemHeap, 0, sizeof(*key) );
288            key->name  = HEAP_strdupA( SystemHeap, 0, p );
289            key->value = p2 ? HEAP_strdupA( SystemHeap, 0, p2 ) : NULL;
290            key->next  = NULL;
291            *next_key  = key;
292            next_key   = &key->next;
293            prev_key   = key;
294
295            TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)");
296           }
297     }
298     return first_section;
299 }
300
301
302 /***********************************************************************
303  *           PROFILE_DeleteSection
304  *
305  * Delete a section from a profile tree.
306  */
307 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
308 {
309     while (*section)
310     {
311         if ((*section)->name && !strcasecmp( (*section)->name, name ))
312         {
313             PROFILESECTION *to_del = *section;
314             *section = to_del->next;
315             to_del->next = NULL;
316             PROFILE_Free( to_del );
317             return TRUE;
318         }
319         section = &(*section)->next;
320     }
321     return FALSE;
322 }
323
324
325 /***********************************************************************
326  *           PROFILE_DeleteKey
327  *
328  * Delete a key from a profile tree.
329  */
330 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
331                                LPCSTR section_name, LPCSTR key_name )
332 {
333     while (*section)
334     {
335         if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
336         {
337             PROFILEKEY **key = &(*section)->key;
338             while (*key)
339             {
340                 if (!strcasecmp( (*key)->name, key_name ))
341                 {
342                     PROFILEKEY *to_del = *key;
343                     *key = to_del->next;
344                     if (to_del->name) HeapFree( SystemHeap, 0, to_del->name );
345                     if (to_del->value) HeapFree( SystemHeap, 0, to_del->value);
346                     HeapFree( SystemHeap, 0, to_del );
347                     return TRUE;
348                 }
349                 key = &(*key)->next;
350             }
351         }
352         section = &(*section)->next;
353     }
354     return FALSE;
355 }
356
357
358 /***********************************************************************
359  *           PROFILE_Find
360  *
361  * Find a key in a profile tree, optionally creating it.
362  */
363 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
364                                  const char *section_name,
365                                  const char *key_name, int create )
366 {
367     const char *p;
368     int seclen, keylen;
369
370     while (PROFILE_isspace(*section_name)) section_name++;
371     p = section_name + strlen(section_name) - 1;
372     while ((p > section_name) && PROFILE_isspace(*p)) p--;
373     seclen = p - section_name + 1;
374     
375     while (PROFILE_isspace(*key_name)) key_name++;
376     p = key_name + strlen(key_name) - 1;
377     while ((p > key_name) && PROFILE_isspace(*p)) p--;
378     keylen = p - key_name + 1;
379
380     while (*section)
381     {
382         if ( ((*section)->name)
383           && (!(strncasecmp( (*section)->name, section_name, seclen )))
384           && (((*section)->name)[seclen] == '\0') )
385         {
386             PROFILEKEY **key = &(*section)->key;
387             while (*key)
388             {
389                 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
390                   && (((*key)->name)[keylen] == '\0') )
391                     return *key;
392                 key = &(*key)->next;
393             }
394             if (!create) return NULL;
395             *key = HEAP_xalloc( SystemHeap, 0, sizeof(PROFILEKEY) );
396             (*key)->name  = HEAP_strdupA( SystemHeap, 0, key_name );
397             (*key)->value = NULL;
398             (*key)->next  = NULL;
399             return *key;
400         }
401         section = &(*section)->next;
402     }
403     if (!create) return NULL;
404     *section = HEAP_xalloc( SystemHeap, 0, sizeof(PROFILESECTION) );
405     (*section)->name = HEAP_strdupA( SystemHeap, 0, section_name );
406     (*section)->next = NULL;
407     (*section)->key  = HEAP_xalloc( SystemHeap, 0, sizeof(PROFILEKEY) );
408     (*section)->key->name  = HEAP_strdupA( SystemHeap, 0, key_name );
409     (*section)->key->value = NULL;
410     (*section)->key->next  = NULL;
411     return (*section)->key;
412 }
413
414
415 /***********************************************************************
416  *           PROFILE_FlushFile
417  *
418  * Flush the current profile to disk if changed.
419  */
420 static BOOL PROFILE_FlushFile(void)
421 {
422     char *p, buffer[MAX_PATHNAME_LEN];
423     const char *unix_name;
424     FILE *file = NULL;
425     struct stat buf;
426
427     if(!CurProfile)
428     {
429         WARN("No current profile!\n");
430         return FALSE;
431     }
432
433     if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
434     if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
435     {
436         /* Try to create it in $HOME/.wine */
437         /* FIXME: this will need a more general solution */
438         strcpy( buffer, PROFILE_GetConfigDir() );
439         p = buffer + strlen(buffer);
440         *p++ = '/';
441         strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
442         CharLowerA( p );
443         file = fopen( buffer, "w" );
444         unix_name = buffer;
445     }
446     
447     if (!file)
448     {
449         WARN("could not save profile file %s\n", CurProfile->dos_name);
450         return FALSE;
451     }
452
453     TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
454     PROFILE_Save( file, CurProfile->section );
455     fclose( file );
456     CurProfile->changed = FALSE;
457     if(!stat(unix_name,&buf))
458        CurProfile->mtime=buf.st_mtime;
459     return TRUE;
460 }
461
462
463 /***********************************************************************
464  *           PROFILE_ReleaseFile
465  *
466  * Flush the current profile to disk and remove it from the cache.
467  */
468 static void PROFILE_ReleaseFile(void)
469 {
470     PROFILE_FlushFile();
471     PROFILE_Free( CurProfile->section );
472     if (CurProfile->dos_name) HeapFree( SystemHeap, 0, CurProfile->dos_name );
473     if (CurProfile->unix_name) HeapFree( SystemHeap, 0, CurProfile->unix_name );
474     if (CurProfile->filename) HeapFree( SystemHeap, 0, CurProfile->filename );
475     CurProfile->changed   = FALSE;
476     CurProfile->section   = NULL;
477     CurProfile->dos_name  = NULL;
478     CurProfile->unix_name = NULL;
479     CurProfile->filename  = NULL;
480     CurProfile->mtime     = 0;
481 }
482
483
484 /***********************************************************************
485  *           PROFILE_Open
486  *
487  * Open a profile file, checking the cached file first.
488  */
489 static BOOL PROFILE_Open( LPCSTR filename )
490 {
491     DOS_FULL_NAME full_name;
492     char buffer[MAX_PATHNAME_LEN];
493     char *newdos_name, *p;
494     FILE *file = NULL;
495     int i,j;
496     struct stat buf;
497     PROFILE *tempProfile;
498
499     /* First time around */
500
501     if(!CurProfile)
502        for(i=0;i<N_CACHED_PROFILES;i++)
503          {
504           MRUProfile[i]=HEAP_xalloc( SystemHeap, 0, sizeof(PROFILE) );
505           MRUProfile[i]->changed=FALSE;
506           MRUProfile[i]->section=NULL;
507           MRUProfile[i]->dos_name=NULL;
508           MRUProfile[i]->unix_name=NULL;
509           MRUProfile[i]->filename=NULL;
510           MRUProfile[i]->mtime=0;
511          }
512
513     /* Check for a match */
514
515     if (strchr( filename, '/' ) || strchr( filename, '\\' ) || 
516         strchr( filename, ':' ))
517     {
518         if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
519     }
520     else
521     {
522         GetWindowsDirectoryA( buffer, sizeof(buffer) );
523         strcat( buffer, "\\" );
524         strcat( buffer, filename );
525         if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
526     }
527
528     for(i=0;i<N_CACHED_PROFILES;i++)
529       {
530        if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
531            (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
532          {
533           if(i)
534             {
535              PROFILE_FlushFile();
536              tempProfile=MRUProfile[i];
537              for(j=i;j>0;j--)
538                 MRUProfile[j]=MRUProfile[j-1];
539              CurProfile=tempProfile;
540             }
541           if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
542              TRACE("(%s): already opened (mru=%d)\n",
543                               filename, i );
544           else
545               TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
546                              filename, i );
547           return TRUE;
548          }
549       }
550
551     /* Flush the old current profile */
552     PROFILE_FlushFile();
553
554     /* Make the oldest profile the current one only in order to get rid of it */
555     if(i==N_CACHED_PROFILES)
556       {
557        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
558        for(i=N_CACHED_PROFILES-1;i>0;i--)
559           MRUProfile[i]=MRUProfile[i-1];
560        CurProfile=tempProfile;
561       }
562     if(CurProfile->filename) PROFILE_ReleaseFile();
563
564     /* OK, now that CurProfile is definitely free we assign it our new file */
565     newdos_name = HEAP_strdupA( SystemHeap, 0, full_name.short_name );
566     CurProfile->dos_name  = newdos_name;
567     CurProfile->filename  = HEAP_strdupA( SystemHeap, 0, filename );
568
569     /* Try to open the profile file, first in $HOME/.wine */
570
571     /* FIXME: this will need a more general solution */
572     strcpy( buffer, PROFILE_GetConfigDir() );
573     p = buffer + strlen(buffer);
574     *p++ = '/';
575     strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
576     CharLowerA( p );
577     if ((file = fopen( buffer, "r" )))
578     {
579         TRACE("(%s): found it in %s\n",
580               filename, buffer );
581         CurProfile->unix_name = HEAP_strdupA( SystemHeap, 0, buffer );
582     }
583
584     if (!file)
585     {
586         CurProfile->unix_name = HEAP_strdupA( SystemHeap, 0,
587                                              full_name.long_name );
588         if ((file = fopen( full_name.long_name, "r" )))
589             TRACE("(%s): found it in %s\n",
590                              filename, full_name.long_name );
591     }
592
593     if (file)
594     {
595         CurProfile->section = PROFILE_Load( file );
596         fclose( file );
597         if(!stat(CurProfile->unix_name,&buf))
598            CurProfile->mtime=buf.st_mtime;
599     }
600     else
601     {
602         /* Does not exist yet, we will create it in PROFILE_FlushFile */
603         WARN("profile file %s not found\n", newdos_name );
604     }
605     return TRUE;
606 }
607
608
609 /***********************************************************************
610  *           PROFILE_GetSection
611  *
612  * Returns all keys of a section.
613  * If return_values is TRUE, also include the corresponding values.
614  */
615 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
616                                LPSTR buffer, UINT len, BOOL handle_env,
617                                BOOL return_values )
618 {
619     PROFILEKEY *key;
620     while (section)
621     {
622         if (section->name && !strcasecmp( section->name, section_name ))
623         {
624             UINT oldlen = len;
625             for (key = section->key; key; key = key->next)
626             {
627                 if (len <= 2) break;
628                 if (!*key->name) continue;  /* Skip empty lines */
629                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
630                 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
631                 len -= strlen(buffer) + 1;
632                 buffer += strlen(buffer) + 1;
633                 if (return_values && key->value) {
634                         buffer[-1] = '=';
635                         PROFILE_CopyEntry ( buffer,
636                                 key->value, len - 1, handle_env );
637                         len -= strlen(buffer) + 1;
638                         buffer += strlen(buffer) + 1;
639                 }
640             }
641             *buffer = '\0';
642             if (len <= 1)
643                 /*If either lpszSection or lpszKey is NULL and the supplied
644                   destination buffer is too small to hold all the strings, 
645                   the last string is truncated and followed by two null characters.
646                   In this case, the return value is equal to cchReturnBuffer
647                   minus two. */
648             {
649                 buffer[-1] = '\0';
650                 return oldlen - 2;
651             }
652             return oldlen - len;
653         }
654         section = section->next;
655     }
656     buffer[0] = buffer[1] = '\0';
657     return 0;
658 }
659
660
661 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
662 {
663     LPSTR buf = buffer;
664     WORD l, cursize = 0;
665     PROFILESECTION *section;
666
667     for (section = CurProfile->section; section; section = section->next)
668         if (section->name) {
669             l = strlen(section->name);
670             cursize += l+1;
671             if (cursize > len+1)
672                 return len-2;
673
674             strcpy(buf, section->name);
675             buf += l+1;
676         }
677
678     *buf=0;
679     buf++;
680     return buf-buffer;
681 }
682
683
684 /***********************************************************************
685  *           PROFILE_GetString
686  *
687  * Get a profile string.
688  */
689 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
690                               LPCSTR def_val, LPSTR buffer, UINT len )
691 {
692     PROFILEKEY *key = NULL;
693
694     if (!def_val) def_val = "";
695     if (key_name && key_name[0])
696     {
697         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
698         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
699                            len, FALSE );
700         TRACE("('%s','%s','%s'): returning '%s'\n",
701                          section, key_name, def_val, buffer );
702         return strlen( buffer );
703     }
704     if (section && section[0])
705         return PROFILE_GetSection(CurProfile->section, section, buffer, len,
706                                 FALSE, FALSE);
707     /* undocumented; both section and key_name are NULL */
708     return PROFILE_GetSectionNames(buffer, len);
709 }
710
711
712 /***********************************************************************
713  *           PROFILE_SetString
714  *
715  * Set a profile string.
716  */
717 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
718                                LPCSTR value )
719 {
720     if (!key_name)  /* Delete a whole section */
721     {
722         TRACE("('%s')\n", section_name);
723         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
724                                                       section_name );
725         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
726                                 this is not an error on application's level.*/
727     }
728     else if (!value)  /* Delete a key */
729     {
730         TRACE("('%s','%s')\n",
731                          section_name, key_name );
732         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
733                                                   section_name, key_name );
734         return TRUE;          /* same error handling as above */
735     }
736     else  /* Set the key value */
737     {
738         PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
739                                         key_name, TRUE );
740         TRACE("('%s','%s','%s'): \n",
741                          section_name, key_name, value );
742         if (!key) return FALSE;
743         if (key->value)
744         {
745             if (!strcmp( key->value, value ))
746             {
747                 TRACE("  no change needed\n" );
748                 return TRUE;  /* No change needed */
749             }
750             TRACE("  replacing '%s'\n", key->value );
751             HeapFree( SystemHeap, 0, key->value );
752         }
753         else TRACE("  creating key\n" );
754         key->value = HEAP_strdupA( SystemHeap, 0, value );
755         CurProfile->changed = TRUE;
756     }
757     return TRUE;
758 }
759
760
761 /***********************************************************************
762  *           PROFILE_GetWineIniString
763  *
764  * Get a config string from the wine.ini file.
765  */
766 int PROFILE_GetWineIniString( const char *section, const char *key_name,
767                               const char *def, char *buffer, int len )
768 {
769     int ret;
770
771     EnterCriticalSection( &PROFILE_CritSect );
772
773     if (key_name)
774     {
775         PROFILEKEY *key = PROFILE_Find(&PROFILE_WineProfile, section, key_name, FALSE);
776         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def,
777                            len, TRUE );
778         TRACE("('%s','%s','%s'): returning '%s'\n",
779                          section, key_name, def, buffer );
780         ret = strlen( buffer );
781     } 
782     else 
783     {
784        ret = PROFILE_GetSection( PROFILE_WineProfile, section, buffer, len, TRUE, FALSE );
785     }
786     LeaveCriticalSection( &PROFILE_CritSect );
787
788     return ret;
789 }
790
791
792 /***********************************************************************
793  *           PROFILE_GetWineIniInt
794  *
795  * Get a config integer from the wine.ini file.
796  */
797 int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
798 {
799     char buffer[20];
800     char *p;
801     long result;
802     PROFILEKEY *key;
803     int ret;
804
805     EnterCriticalSection( &PROFILE_CritSect );
806
807     key = PROFILE_Find( &PROFILE_WineProfile, section, key_name, FALSE );
808     if (!key || !key->value) {
809        ret = def;
810     } else {
811        PROFILE_CopyEntry( buffer, key->value, sizeof(buffer), TRUE );
812        result = strtol( buffer, &p, 0 );
813        ret = (p == buffer) ? 0  /* No digits at all */ : (int)result;
814     }
815
816     LeaveCriticalSection( &PROFILE_CritSect );
817
818     return ret;
819 }
820
821
822 /******************************************************************************
823  *
824  *   int  PROFILE_EnumerateWineIniSection(
825  *     char const  *section,  #Name of the section to enumerate
826  *     void  (*cbfn)(char const *key, char const *value, void *user),
827  *                            # Address of the callback function 
828  *     void  *user )          # User-specified pointer.
829  *
830  *   For each entry in a section in the wine.conf file, this function will
831  *   call the specified callback function, informing it of each key and
832  *   value.  An optional user pointer may be passed to it (if this is not
833  *   needed, pass NULL through it and ignore the value in the callback
834  *   function).
835  *
836  *   The callback function must accept three parameters:
837  *      The name of the key (char const *)
838  *      The value of the key (char const *)
839  *      A user-specified parameter (void *)
840  *   Note that the first two are char CONST *'s, not char *'s!  The callback
841  *   MUST not modify these strings!
842  *
843  *   The return value indicates the number of times the callback function
844  *   was called.
845  */
846 int  PROFILE_EnumerateWineIniSection(
847     char const  *section,
848     void  (*cbfn)(char const *, char const *, void *),
849     void  *userptr )
850 {
851     PROFILESECTION  *scansect;
852     PROFILEKEY  *scankey;
853     int  calls = 0;
854
855     EnterCriticalSection( &PROFILE_CritSect );
856
857     /* Search for the correct section */
858     for(scansect = PROFILE_WineProfile; scansect; scansect = scansect->next) {
859         if(scansect->name && !strcasecmp(scansect->name, section)) {
860
861             /* Enumerate each key with the callback */
862             for(scankey = scansect->key; scankey; scankey = scankey->next) {
863
864                 /* Ignore blank entries -- these shouldn't exist, but let's
865                    be extra careful */
866                 if (!scankey->name[0]) continue;
867                 if (!scankey->value) cbfn(scankey->name, NULL, userptr);
868                 else
869                 {
870                     char value[1024];
871                     PROFILE_CopyEntry(value, scankey->value, sizeof(value), TRUE);
872                     cbfn(scankey->name, value, userptr);
873                 }
874                 ++calls;
875             }
876         
877             break;
878         }
879     }
880     LeaveCriticalSection( &PROFILE_CritSect );
881
882     return calls;
883 }
884
885
886 /******************************************************************************
887  *
888  *   int  PROFILE_GetWineIniBool(
889  *      char const  *section,
890  *      char const  *key_name,
891  *      int  def )
892  *
893  *   Reads a boolean value from the wine.ini file.  This function attempts to
894  *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
895  *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
896  *   true.  Anything else results in the return of the default value.
897  *
898  *   This function uses 1 to indicate true, and 0 for false.  You can check
899  *   for existence by setting def to something other than 0 or 1 and
900  *   examining the return value.
901  */
902 int  PROFILE_GetWineIniBool(
903     char const  *section,
904     char const  *key_name,
905     int  def )
906 {
907     char  key_value[2];
908     int  retval;
909
910     PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
911
912     switch(key_value[0]) {
913     case 'n':
914     case 'N':
915     case 'f':
916     case 'F':
917     case '0':
918         retval = 0;
919         break;
920
921     case 'y':
922     case 'Y':
923     case 't':
924     case 'T':
925     case '1':
926         retval = 1;
927         break;
928
929     default:
930         retval = def;
931     }
932
933     TRACE("(\"%s\", \"%s\", %s), "
934                     "[%c], ret %s.\n", section, key_name,
935                     def ? "TRUE" : "FALSE", key_value[0],
936                     retval ? "TRUE" : "FALSE");
937
938     return retval;
939 }
940
941
942 /***********************************************************************
943  *           PROFILE_LoadWineIni
944  *
945  * Load the wine.ini file.
946  */
947 int PROFILE_LoadWineIni(void)
948 {
949     char buffer[MAX_PATHNAME_LEN];
950     const char *p;
951     FILE *f;
952
953     InitializeCriticalSection( &PROFILE_CritSect );
954     MakeCriticalSectionGlobal( &PROFILE_CritSect );
955
956     if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) )
957     {
958       /* Open -config specified file */
959       PROFILE_WineProfile = PROFILE_Load ( f);
960       fclose ( f );
961       lstrcpynA(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN);
962       return 1;
963     }
964
965     if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) )
966     {
967         PROFILE_WineProfile = PROFILE_Load( f );
968         fclose( f );
969         lstrcpynA(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN);
970         return 1;
971     }
972     if ((p = getenv( "HOME" )) != NULL)
973     {
974         lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
975         strcat( buffer, PROFILE_WineIniName );
976         if ((f = fopen( buffer, "r" )) != NULL)
977         {
978             PROFILE_WineProfile = PROFILE_Load( f );
979             fclose( f );
980             lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
981             return 1;
982         }
983     }
984     else WARN("could not get $HOME value for config file.\n" );
985
986     /* Try global file */
987
988     if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
989     {
990         PROFILE_WineProfile = PROFILE_Load( f );
991         fclose( f );
992         lstrcpynA(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN);
993         return 1;
994     }
995     MESSAGE( "Can't open configuration file %s or $HOME%s\n",
996          WINE_INI_GLOBAL, PROFILE_WineIniName );
997     return 0;
998 }
999
1000
1001 /***********************************************************************
1002  *           PROFILE_UsageWineIni
1003  *
1004  * Explain the wine.ini file to those who don't read documentation.
1005  * Keep below one screenful in length so that error messages above are
1006  * noticed.
1007  */
1008 void PROFILE_UsageWineIni(void)
1009 {
1010     MESSAGE("Perhaps you have not properly edited or created "
1011         "your Wine configuration file.\n");
1012     MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName);
1013     MESSAGE("  or it is determined by the -config option or from\n"
1014         "  the WINE_INI environment variable.\n");
1015     if (*PROFILE_WineIniUsed)
1016         MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed);
1017     /* RTFM, so to say */
1018 }
1019
1020 /***********************************************************************
1021  *           PROFILE_GetStringItem
1022  *
1023  *  Convenience function that turns a string 'xxx, yyy, zzz' into 
1024  *  the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1025  */
1026 char* PROFILE_GetStringItem( char* start )
1027 {
1028     char* lpchX, *lpch;
1029
1030     for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
1031     {
1032         if( *lpchX == ',' )
1033         {
1034             if( lpch ) *lpch = '\0'; else *lpchX = '\0';
1035             while( *(++lpchX) )
1036                 if( !PROFILE_isspace(*lpchX) ) return lpchX;
1037         }
1038         else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
1039              else lpch = NULL;
1040     }
1041     if( lpch ) *lpch = '\0';
1042     return NULL;
1043 }
1044
1045 /********************* API functions **********************************/
1046
1047 /***********************************************************************
1048  *           GetProfileInt16   (KERNEL.57)
1049  */
1050 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1051 {
1052     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1053 }
1054
1055
1056 /***********************************************************************
1057  *           GetProfileInt32A   (KERNEL32.264)
1058  */
1059 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1060 {
1061     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1062 }
1063
1064 /***********************************************************************
1065  *           GetProfileInt32W   (KERNEL32.264)
1066  */
1067 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1068 {
1069     if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1070     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1071 }
1072
1073 /***********************************************************************
1074  *           GetProfileString16   (KERNEL.58)
1075  */
1076 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1077                                  LPSTR buffer, UINT16 len )
1078 {
1079     return GetPrivateProfileString16( section, entry, def_val,
1080                                       buffer, len, "win.ini" );
1081 }
1082
1083 /***********************************************************************
1084  *           GetProfileString32A   (KERNEL32.268)
1085  */
1086 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1087                               LPSTR buffer, UINT len )
1088 {
1089     return GetPrivateProfileStringA( section, entry, def_val,
1090                                      buffer, len, "win.ini" );
1091 }
1092
1093 /***********************************************************************
1094  *           GetProfileString32W   (KERNEL32.269)
1095  */
1096 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1097                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1098 {
1099     if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1100     return GetPrivateProfileStringW( section, entry, def_val,
1101                                      buffer, len, wininiW );
1102 }
1103
1104 /***********************************************************************
1105  *           WriteProfileString16   (KERNEL.59)
1106  */
1107 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1108                                     LPCSTR string )
1109 {
1110     return WritePrivateProfileString16( section, entry, string, "win.ini" );
1111 }
1112
1113 /***********************************************************************
1114  *           WriteProfileString32A   (KERNEL32.587)
1115  */
1116 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1117                                  LPCSTR string )
1118 {
1119     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1120 }
1121
1122 /***********************************************************************
1123  *           WriteProfileString32W   (KERNEL32.588)
1124  */
1125 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1126                                      LPCWSTR string )
1127 {
1128     if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1129     return WritePrivateProfileStringW( section, entry, string, wininiW );
1130 }
1131
1132
1133 /***********************************************************************
1134  *           GetPrivateProfileInt16   (KERNEL.127)
1135  */
1136 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1137                                       INT16 def_val, LPCSTR filename )
1138 {
1139     long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename);
1140
1141     if (result > 65535) return 65535;
1142     if (result >= 0) return (UINT16)result;
1143     if (result < -32768) return -32768;
1144     return (UINT16)(INT16)result;
1145 }
1146
1147 /***********************************************************************
1148  *           GetPrivateProfileInt32A   (KERNEL32.251)
1149  */
1150 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1151                                    INT def_val, LPCSTR filename )
1152 {
1153     char buffer[20];
1154     char *p;
1155     long result;
1156
1157     GetPrivateProfileStringA( section, entry, "",
1158                                 buffer, sizeof(buffer), filename );
1159     if (!buffer[0]) return (UINT)def_val;
1160     result = strtol( buffer, &p, 0 );
1161     if (p == buffer) return 0;  /* No digits at all */
1162     return (UINT)result;
1163 }
1164
1165 /***********************************************************************
1166  *           GetPrivateProfileInt32W   (KERNEL32.252)
1167  */
1168 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1169                                    INT def_val, LPCWSTR filename )
1170 {
1171     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1172     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1173     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1174     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1175     HeapFree( GetProcessHeap(), 0, sectionA );
1176     HeapFree( GetProcessHeap(), 0, filenameA );
1177     HeapFree( GetProcessHeap(), 0, entryA );
1178     return res;
1179 }
1180
1181 /***********************************************************************
1182  *           GetPrivateProfileString16   (KERNEL.128)
1183  */
1184 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1185                                         LPCSTR def_val, LPSTR buffer,
1186                                         UINT16 len, LPCSTR filename )
1187 {
1188     return GetPrivateProfileStringA(section,entry,def_val,buffer,len,filename);
1189 }
1190
1191 /***********************************************************************
1192  *           GetPrivateProfileString32A   (KERNEL32.255)
1193  */
1194 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1195                                      LPCSTR def_val, LPSTR buffer,
1196                                      UINT len, LPCSTR filename )
1197 {
1198     int         ret;
1199
1200     if (!filename) 
1201         filename = "win.ini";
1202
1203     EnterCriticalSection( &PROFILE_CritSect );
1204
1205     if (PROFILE_Open( filename )) {
1206         ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1207     } else {
1208        lstrcpynA( buffer, def_val, len );
1209        ret = strlen( buffer );
1210     }
1211
1212     LeaveCriticalSection( &PROFILE_CritSect );
1213     
1214     return ret;
1215 }
1216
1217 /***********************************************************************
1218  *           GetPrivateProfileString32W   (KERNEL32.256)
1219  */
1220 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1221                                      LPCWSTR def_val, LPWSTR buffer,
1222                                      UINT len, LPCWSTR filename )
1223 {
1224     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1225     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1226     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1227     LPSTR def_valA  = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1228     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1229     INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1230                                             bufferA, len, filenameA );
1231     lstrcpynAtoW( buffer, bufferA, len );
1232     HeapFree( GetProcessHeap(), 0, sectionA );
1233     HeapFree( GetProcessHeap(), 0, entryA );
1234     HeapFree( GetProcessHeap(), 0, filenameA );
1235     HeapFree( GetProcessHeap(), 0, def_valA );
1236     HeapFree( GetProcessHeap(), 0, bufferA);
1237     return ret;
1238 }
1239
1240 /***********************************************************************
1241  *           GetPrivateProfileSection16   (KERNEL.418)
1242  */
1243 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1244                                         UINT16 len, LPCSTR filename )
1245 {
1246     return GetPrivateProfileSectionA( section, buffer, len, filename );
1247 }
1248
1249 /***********************************************************************
1250  *           GetPrivateProfileSection32A   (KERNEL32.255)
1251  */
1252 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1253                                       DWORD len, LPCSTR filename )
1254 {
1255     int         ret = 0;
1256
1257     EnterCriticalSection( &PROFILE_CritSect );
1258
1259     if (PROFILE_Open( filename ))
1260         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1261                                  FALSE, TRUE);
1262     
1263     LeaveCriticalSection( &PROFILE_CritSect );
1264
1265     return ret;
1266 }
1267
1268 /***********************************************************************
1269  *           GetPrivateProfileSection32W   (KERNEL32.256)
1270  */
1271
1272 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1273                                       DWORD len, LPCWSTR filename )
1274
1275 {
1276     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1277     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1278     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1279     INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len, 
1280                                                 filenameA );
1281     MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1282     HeapFree( GetProcessHeap(), 0, sectionA );
1283     HeapFree( GetProcessHeap(), 0, filenameA );
1284     HeapFree( GetProcessHeap(), 0, bufferA);
1285     return ret;
1286 }
1287
1288 /***********************************************************************
1289  *           GetProfileSection16   (KERNEL.419)
1290  */
1291 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1292 {
1293     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1294 }
1295
1296 /***********************************************************************
1297  *           GetProfileSection32A   (KERNEL32.268)
1298  */
1299 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1300 {
1301     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1302 }
1303
1304 /***********************************************************************
1305  *           GetProfileSection32W   (KERNEL32)
1306  */
1307 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1308 {
1309     if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1310     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1311 }
1312
1313
1314 /***********************************************************************
1315  *           WritePrivateProfileString16   (KERNEL.129)
1316  */
1317 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1318                                            LPCSTR string, LPCSTR filename )
1319 {
1320     return WritePrivateProfileStringA(section,entry,string,filename);
1321 }
1322
1323 /***********************************************************************
1324  *           WritePrivateProfileString32A   (KERNEL32.582)
1325  */
1326 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1327                                         LPCSTR string, LPCSTR filename )
1328 {
1329     BOOL ret = FALSE;
1330
1331     EnterCriticalSection( &PROFILE_CritSect );
1332
1333     if (PROFILE_Open( filename ))
1334     {
1335         if (!section && !entry && !string)
1336             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1337         else
1338             ret = PROFILE_SetString( section, entry, string );
1339     }
1340
1341     LeaveCriticalSection( &PROFILE_CritSect );
1342     return ret;
1343 }
1344
1345 /***********************************************************************
1346  *           WritePrivateProfileString32W   (KERNEL32.583)
1347  */
1348 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1349                                         LPCWSTR string, LPCWSTR filename )
1350 {
1351     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1352     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1353     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1354     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1355     BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1356                                            stringA, filenameA );
1357     HeapFree( GetProcessHeap(), 0, sectionA );
1358     HeapFree( GetProcessHeap(), 0, entryA );
1359     HeapFree( GetProcessHeap(), 0, stringA );
1360     HeapFree( GetProcessHeap(), 0, filenameA );
1361     return res;
1362 }
1363
1364 /***********************************************************************
1365  *           WritePrivateProfileSection16   (KERNEL.416)
1366  */
1367 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section, 
1368                                             LPCSTR string, LPCSTR filename )
1369 {
1370     return WritePrivateProfileSectionA( section, string, filename );
1371 }
1372
1373 /***********************************************************************
1374  *           WritePrivateProfileSectionA   (KERNEL32)
1375  */
1376 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section, 
1377                                          LPCSTR string, LPCSTR filename )
1378 {
1379     BOOL ret = FALSE;
1380     LPSTR p ;
1381
1382     EnterCriticalSection( &PROFILE_CritSect );
1383
1384     if (PROFILE_Open( filename )) {
1385         if (!section && !string && !filename)
1386             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1387         else {
1388             while(*string){
1389                 LPSTR buf=HEAP_strdupA( GetProcessHeap(), 0, string );
1390                 if((p=strchr( buf, '='))){
1391                     *p='\0';
1392                     ret = PROFILE_SetString( section, buf, p+1 );
1393                     
1394                 }
1395                 HeapFree( GetProcessHeap(), 0, buf );
1396                 string += strlen(string)+1;
1397             }
1398             
1399         }
1400     }
1401
1402     LeaveCriticalSection( &PROFILE_CritSect );
1403     return ret;
1404 }
1405
1406 /***********************************************************************
1407  *           WritePrivateProfileSection32W   (KERNEL32)
1408  */
1409 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1410                                          LPCWSTR string, LPCWSTR filename)
1411
1412 {
1413     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1414     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1415     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1416     BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1417     HeapFree( GetProcessHeap(), 0, sectionA );
1418     HeapFree( GetProcessHeap(), 0, stringA );
1419     HeapFree( GetProcessHeap(), 0, filenameA );
1420     return res;
1421 }
1422
1423 /***********************************************************************
1424  *           WriteProfileSection16   (KERNEL.417)
1425  */
1426 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1427 {
1428     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1429 }
1430
1431 /***********************************************************************
1432  *           WriteProfileSection32A   (KERNEL32.747)
1433  */
1434 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1435                                      
1436 {
1437     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1438 }
1439
1440 /***********************************************************************
1441  *           WriteProfileSection32W   (KERNEL32.748)
1442  */
1443 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1444 {
1445    if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini");
1446    
1447    return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1448 }
1449
1450 /***********************************************************************
1451  *           GetPrivateProfileSectionNames16   (KERNEL.143)
1452  */
1453 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1454                                              LPCSTR filename )
1455 {
1456     WORD ret = 0;
1457
1458     EnterCriticalSection( &PROFILE_CritSect );
1459
1460     if (PROFILE_Open( filename ))
1461         ret = PROFILE_GetSectionNames(buffer, size);
1462
1463     LeaveCriticalSection( &PROFILE_CritSect );
1464
1465     return ret;
1466 }
1467
1468
1469 /***********************************************************************
1470  *           GetProfileSectionNames16   (KERNEL.142)
1471  */
1472 WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size)
1473
1474 {
1475     return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini"));
1476 }
1477
1478
1479 /***********************************************************************
1480  *           GetPrivateProfileSectionNames32A  (KERNEL32.365)
1481  */
1482 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1483                                              LPCSTR filename)
1484
1485 {
1486  return (GetPrivateProfileSectionNames16 (buffer,size,filename));
1487 }
1488
1489
1490 /***********************************************************************
1491  *           GetPrivateProfileSectionNames32W  (KERNEL32.366)
1492  */
1493 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1494                                              LPCWSTR filename)
1495
1496 {
1497    LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1498    LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size);
1499
1500    INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
1501    lstrcpynAtoW( buffer, bufferA, size);   
1502    HeapFree( GetProcessHeap(), 0, bufferA);
1503    HeapFree( GetProcessHeap(), 0, filenameA );
1504
1505    return ret;
1506 }
1507
1508 /***********************************************************************
1509  *           GetPrivateProfileStruct16 (KERNEL.407)
1510  */
1511 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key, 
1512                                         LPVOID buf, UINT16 len, LPCSTR filename)
1513 {
1514     return GetPrivateProfileStructA( section, key, buf, len, filename );
1515 }
1516
1517 /***********************************************************************
1518  *           GetPrivateProfileStruct32A (KERNEL32.370)
1519  *
1520  * Should match Win95's behaviour pretty much
1521  */
1522 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key, 
1523                                       LPVOID buf, UINT len, LPCSTR filename)
1524 {
1525     BOOL        ret = FALSE;
1526
1527     EnterCriticalSection( &PROFILE_CritSect );
1528
1529     if (PROFILE_Open( filename )) {
1530         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1531         if (k) {
1532             TRACE("value (at %p): '%s'\n", k->value, k->value);
1533             if (((strlen(k->value) - 2) / 2) == len)
1534             {
1535                 LPSTR end, p;
1536                 BOOL valid = TRUE;
1537                 CHAR c;
1538                 DWORD chksum = 0;
1539
1540                 end  = k->value + strlen(k->value); /* -> '\0' */
1541                 /* check for invalid chars in ASCII coded hex string */
1542                 for (p=k->value; p < end; p++)
1543                 {
1544                     if (!isxdigit(*p))
1545                     {
1546                         WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1547                              *p, filename, section, key);
1548                         valid = FALSE;
1549                         break;
1550                     }
1551                 }
1552                 if (valid)
1553                 {
1554                     BOOL highnibble = TRUE;
1555                     BYTE b = 0, val;
1556                     LPBYTE binbuf = (LPBYTE)buf;
1557                     
1558                     end -= 2; /* don't include checksum in output data */
1559                     /* translate ASCII hex format into binary data */
1560                     for (p=k->value; p < end; p++)
1561                     {
1562                         c = toupper(*p);
1563                         val = (c > '9') ?
1564                                 (c - 'A' + 10) : (c - '0');
1565
1566                         if (highnibble)
1567                             b = val << 4;
1568                         else
1569                         {
1570                             b += val;
1571                             *binbuf++ = b; /* feed binary data into output */
1572                             chksum += b; /* calculate checksum */
1573                         }
1574                         highnibble ^= 1; /* toggle */
1575                     }
1576                     /* retrieve stored checksum value */
1577                     c = toupper(*p++);
1578                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1579                     c = toupper(*p);
1580                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1581                     if (b == (chksum & 0xff)) /* checksums match ? */
1582                         ret = TRUE;
1583                 }
1584             }
1585         }
1586     }
1587     LeaveCriticalSection( &PROFILE_CritSect );
1588
1589     return ret;
1590 }
1591
1592 /***********************************************************************
1593  *           GetPrivateProfileStruct32W (KERNEL32.543)
1594  */
1595 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1596                                       LPVOID buffer, UINT len, LPCWSTR filename)
1597 {
1598     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1599     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1600     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1601     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1602
1603     INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1604                                         len, filenameA );
1605     lstrcpynAtoW( buffer, bufferA, len );
1606     HeapFree( GetProcessHeap(), 0, bufferA);
1607     HeapFree( GetProcessHeap(), 0, sectionA );
1608     HeapFree( GetProcessHeap(), 0, keyA );
1609     HeapFree( GetProcessHeap(), 0, filenameA );
1610
1611     return ret;
1612 }
1613
1614
1615
1616 /***********************************************************************
1617  *           WritePrivateProfileStruct16 (KERNEL.406)
1618  */
1619 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key, 
1620         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1621 {
1622     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1623 }
1624
1625 /***********************************************************************
1626  *           WritePrivateProfileStruct32A (KERNEL32.744)
1627  */
1628 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key, 
1629                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1630 {
1631     BOOL ret = FALSE;
1632     LPBYTE binbuf;
1633     LPSTR outstring, p;
1634     DWORD sum = 0;
1635
1636     if (!section && !key && !buf)  /* flush the cache */
1637         return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1638
1639     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1640     outstring = HeapAlloc( SystemHeap, 0, bufsize*2 + 2 + 1);
1641     p = outstring;
1642     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1643       *p++ = hex[*binbuf >> 4];
1644       *p++ = hex[*binbuf & 0xf];
1645       sum += *binbuf;
1646     }
1647     /* checksum is sum & 0xff */
1648     *p++ = hex[(sum & 0xf0) >> 4];
1649     *p++ = hex[sum & 0xf];
1650     *p++ = '\0';
1651
1652     EnterCriticalSection( &PROFILE_CritSect );
1653
1654     if (PROFILE_Open( filename )) 
1655         ret = PROFILE_SetString( section, key, outstring );
1656
1657     LeaveCriticalSection( &PROFILE_CritSect );
1658
1659     HeapFree( SystemHeap, 0, outstring );
1660
1661     return ret;
1662 }
1663
1664 /***********************************************************************
1665  *           WritePrivateProfileStruct32W (KERNEL32.544)
1666  */
1667 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1668                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1669 {
1670     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1671     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1672     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1673     INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1674                                           filenameA );
1675     HeapFree( GetProcessHeap(), 0, sectionA );
1676     HeapFree( GetProcessHeap(), 0, keyA );
1677     HeapFree( GetProcessHeap(), 0, filenameA );
1678
1679     return ret;
1680 }
1681
1682
1683 /***********************************************************************
1684  *           WriteOutProfiles   (KERNEL.315)
1685  */
1686 void WINAPI WriteOutProfiles16(void)
1687 {
1688     EnterCriticalSection( &PROFILE_CritSect );
1689     PROFILE_FlushFile();
1690     LeaveCriticalSection( &PROFILE_CritSect );
1691 }
1692
1693 /***********************************************************************
1694  *           CloseProfileUserMapping   (KERNEL.138)
1695  */
1696 BOOL WINAPI CloseProfileUserMapping(void) {
1697     FIXME("(), stub!\n");
1698     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1699     return FALSE;
1700 }