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