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