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