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