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