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