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