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