Implemented file locking functions (partly based on my old Corel
[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     UINT result = 0;
1304     char *p = buffer;
1305     int negative = 0;
1306
1307     if (!GetPrivateProfileStringA( section, entry, "",
1308                                    buffer, sizeof(buffer), filename ))
1309         return def_val;
1310     /* FIXME: if entry can be found but it's empty, then Win16 is
1311      * supposed to return 0 instead of def_val ! Difficult/problematic
1312      * to implement (every other failure also returns zero buffer),
1313      * thus wait until testing framework avail for making sure nothing
1314      * else gets broken that way. */
1315     if (!buffer[0]) return (UINT)def_val;
1316
1317     /* do the conversion by hand to make sure
1318      * overflow is *not* handled properly ;-) */
1319     while (*p && isspace(*p)) p++;
1320     if (*p == '-')
1321     {
1322         negative = 1;
1323         p++;
1324     }
1325     else if (*p == '+') p++;
1326
1327     while (*p && isdigit(*p))
1328     {
1329         result = result * 10 + *p - '0';
1330         p++;
1331     }
1332     return negative ? (UINT)-result : result;
1333 }
1334
1335 /***********************************************************************
1336  *           GetPrivateProfileIntW   (KERNEL32.@)
1337  *
1338  * FIXME: rewrite using unicode
1339  */
1340 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1341                                    INT def_val, LPCWSTR filename )
1342 {
1343     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1344     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1345     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1346     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1347     HeapFree( GetProcessHeap(), 0, sectionA );
1348     HeapFree( GetProcessHeap(), 0, filenameA );
1349     HeapFree( GetProcessHeap(), 0, entryA );
1350     return res;
1351 }
1352
1353 /***********************************************************************
1354  *           GetPrivateProfileSection   (KERNEL.418)
1355  */
1356 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1357                                         UINT16 len, LPCSTR filename )
1358 {
1359     return GetPrivateProfileSectionA( section, buffer, len, filename );
1360 }
1361
1362 /***********************************************************************
1363  *           GetPrivateProfileSectionW   (KERNEL32.@)
1364  */
1365 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1366                                       DWORD len, LPCWSTR filename )
1367 {
1368     int         ret = 0;
1369
1370     EnterCriticalSection( &PROFILE_CritSect );
1371
1372     if (PROFILE_Open( filename ))
1373         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1374                                  FALSE, TRUE);
1375
1376     LeaveCriticalSection( &PROFILE_CritSect );
1377
1378     return ret;
1379 }
1380
1381 /***********************************************************************
1382  *           GetPrivateProfileSectionA   (KERNEL32.@)
1383  */
1384 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1385                                       DWORD len, LPCSTR filename )
1386 {
1387     UNICODE_STRING sectionW, filenameW;
1388     LPWSTR bufferW;
1389     INT retW, ret = 0;
1390
1391     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1392     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1393     else sectionW.Buffer = NULL;
1394     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1395     else filenameW.Buffer = NULL;
1396
1397     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1398     if (len > 2)
1399     {
1400         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1401         if (ret > 2)
1402             ret -= 2;
1403         else
1404         {
1405             ret = 0;
1406             buffer[len-2] = 0;
1407             buffer[len-1] = 0;
1408         }
1409     }
1410     else
1411     {
1412         buffer[0] = 0;
1413         buffer[1] = 0;
1414     }
1415
1416     RtlFreeUnicodeString(&sectionW);
1417     RtlFreeUnicodeString(&filenameW);
1418     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1419     return ret;
1420 }
1421
1422 /***********************************************************************
1423  *           GetProfileSection   (KERNEL.419)
1424  */
1425 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1426 {
1427     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1428 }
1429
1430 /***********************************************************************
1431  *           GetProfileSectionA   (KERNEL32.@)
1432  */
1433 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1434 {
1435     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1436 }
1437
1438 /***********************************************************************
1439  *           GetProfileSectionW   (KERNEL32.@)
1440  */
1441 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1442 {
1443     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1444 }
1445
1446
1447 /***********************************************************************
1448  *           WritePrivateProfileString   (KERNEL.129)
1449  */
1450 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1451                                            LPCSTR string, LPCSTR filename )
1452 {
1453     return WritePrivateProfileStringA(section,entry,string,filename);
1454 }
1455
1456 /***********************************************************************
1457  *           WritePrivateProfileStringW   (KERNEL32.@)
1458  */
1459 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1460                                         LPCWSTR string, LPCWSTR filename )
1461 {
1462     BOOL ret = FALSE;
1463
1464     EnterCriticalSection( &PROFILE_CritSect );
1465
1466     if (PROFILE_Open( filename ))
1467     {
1468         if (!section && !entry && !string) /* documented "file flush" case */
1469         {
1470             PROFILE_FlushFile();
1471             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1472         }
1473         else {
1474             if (!section) {
1475                 FIXME("(NULL?,%s,%s,%s)?\n",
1476                       debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1477             } else {
1478                 ret = PROFILE_SetString( section, entry, string, FALSE);
1479                 PROFILE_FlushFile();
1480             }
1481         }
1482     }
1483
1484     LeaveCriticalSection( &PROFILE_CritSect );
1485     return ret;
1486 }
1487
1488 /***********************************************************************
1489  *           WritePrivateProfileStringA   (KERNEL32.@)
1490  */
1491 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1492                                         LPCSTR string, LPCSTR filename )
1493 {
1494     UNICODE_STRING sectionW, entryW, stringW, filenameW;
1495     BOOL ret;
1496
1497     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1498     else sectionW.Buffer = NULL;
1499     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1500     else entryW.Buffer = NULL;
1501     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1502     else stringW.Buffer = NULL;
1503     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1504     else filenameW.Buffer = NULL;
1505
1506     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1507                                      stringW.Buffer, filenameW.Buffer);
1508     RtlFreeUnicodeString(&sectionW);
1509     RtlFreeUnicodeString(&entryW);
1510     RtlFreeUnicodeString(&stringW);
1511     RtlFreeUnicodeString(&filenameW);
1512     return ret;
1513 }
1514
1515 /***********************************************************************
1516  *           WritePrivateProfileSection   (KERNEL.416)
1517  */
1518 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1519                                             LPCSTR string, LPCSTR filename )
1520 {
1521     return WritePrivateProfileSectionA( section, string, filename );
1522 }
1523
1524 /***********************************************************************
1525  *           WritePrivateProfileSectionW   (KERNEL32.@)
1526  */
1527 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1528                                          LPCWSTR string, LPCWSTR filename )
1529 {
1530     BOOL ret = FALSE;
1531     LPWSTR p;
1532
1533     EnterCriticalSection( &PROFILE_CritSect );
1534
1535     if (PROFILE_Open( filename )) {
1536         if (!section && !string)
1537             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1538         else if (!string) {/* delete the named section*/
1539             ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1540             PROFILE_FlushFile();
1541         } else {
1542             PROFILE_DeleteAllKeys(section);
1543             ret = TRUE;
1544             while(*string) {
1545                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1546                 strcpyW( buf, string );
1547                 if((p = strchrW( buf, '='))) {
1548                     *p='\0';
1549                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
1550                 }
1551                 HeapFree( GetProcessHeap(), 0, buf );
1552                 string += strlenW(string)+1;
1553             }
1554             PROFILE_FlushFile();
1555         }
1556     }
1557
1558     LeaveCriticalSection( &PROFILE_CritSect );
1559     return ret;
1560 }
1561
1562 /***********************************************************************
1563  *           WritePrivateProfileSectionA   (KERNEL32.@)
1564  */
1565 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1566                                          LPCSTR string, LPCSTR filename)
1567
1568 {
1569     UNICODE_STRING sectionW, filenameW;
1570     LPWSTR stringW;
1571     BOOL ret;
1572
1573     if (string)
1574     {
1575         INT lenA, lenW;
1576         LPCSTR p = string;
1577
1578         while(*p) p += strlen(p) + 1;
1579         lenA = p - string + 1;
1580         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1581         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1582             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1583     }
1584     else stringW = NULL;
1585     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1586     else sectionW.Buffer = NULL;
1587     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1588     else filenameW.Buffer = NULL;
1589
1590     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1591
1592     HeapFree(GetProcessHeap(), 0, stringW);
1593     RtlFreeUnicodeString(&sectionW);
1594     RtlFreeUnicodeString(&filenameW);
1595     return ret;
1596 }
1597
1598 /***********************************************************************
1599  *           WriteProfileSection   (KERNEL.417)
1600  */
1601 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1602 {
1603     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1604 }
1605
1606 /***********************************************************************
1607  *           WriteProfileSectionA   (KERNEL32.@)
1608  */
1609 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1610
1611 {
1612     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1613 }
1614
1615 /***********************************************************************
1616  *           WriteProfileSectionW   (KERNEL32.@)
1617  */
1618 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1619 {
1620    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1621 }
1622
1623 /***********************************************************************
1624  *           GetPrivateProfileSectionNames   (KERNEL.143)
1625  */
1626 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1627                                              LPCSTR filename )
1628 {
1629     return GetPrivateProfileSectionNamesA(buffer,size,filename);
1630 }
1631
1632
1633 /***********************************************************************
1634  *           GetProfileSectionNames   (KERNEL.142)
1635  */
1636 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1637
1638 {
1639     return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1640 }
1641
1642
1643 /***********************************************************************
1644  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1645  *
1646  * Returns the section names contained in the specified file.
1647  * FIXME: Where do we find this file when the path is relative?
1648  * The section names are returned as a list of strings with an extra
1649  * '\0' to mark the end of the list. Except for that the behavior
1650  * depends on the Windows version.
1651  *
1652  * Win95:
1653  * - if the buffer is 0 or 1 character long then it is as if it was of
1654  *   infinite length.
1655  * - otherwise, if the buffer is to small only the section names that fit
1656  *   are returned.
1657  * - note that this means if the buffer was to small to return even just
1658  *   the first section name then a single '\0' will be returned.
1659  * - the return value is the number of characters written in the buffer,
1660  *   except if the buffer was too smal in which case len-2 is returned
1661  *
1662  * Win2000:
1663  * - if the buffer is 0, 1 or 2 characters long then it is filled with
1664  *   '\0' and the return value is 0
1665  * - otherwise if the buffer is too small then the first section name that
1666  *   does not fit is truncated so that the string list can be terminated
1667  *   correctly (double '\0')
1668  * - the return value is the number of characters written in the buffer
1669  *   except for the trailing '\0'. If the buffer is too small, then the
1670  *   return value is len-2
1671  * - Win2000 has a bug that triggers when the section names and the
1672  *   trailing '\0' fit exactly in the buffer. In that case the trailing
1673  *   '\0' is missing.
1674  *
1675  * Wine implements the observed Win2000 behavior (except for the bug).
1676  *
1677  * Note that when the buffer is big enough then the return value may be any
1678  * value between 1 and len-1 (or len in Win95), including len-2.
1679  */
1680 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1681                                              LPCWSTR filename)
1682 {
1683     DWORD ret = 0;
1684
1685     EnterCriticalSection( &PROFILE_CritSect );
1686
1687     if (PROFILE_Open( filename ))
1688         ret = PROFILE_GetSectionNames(buffer, size);
1689
1690     LeaveCriticalSection( &PROFILE_CritSect );
1691
1692     return ret;
1693 }
1694
1695
1696 /***********************************************************************
1697  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
1698  */
1699 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1700                                              LPCSTR filename)
1701 {
1702     UNICODE_STRING filenameW;
1703     LPWSTR bufferW;
1704     INT retW, ret = 0;
1705
1706     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1707     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1708     else filenameW.Buffer = NULL;
1709
1710     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1711     if (retW && size)
1712     {
1713         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1714         if (!ret)
1715         {
1716             ret = size;
1717             buffer[size-1] = 0;
1718         }
1719     }
1720
1721     RtlFreeUnicodeString(&filenameW);
1722     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1723     return ret;
1724 }
1725
1726 /***********************************************************************
1727  *           GetPrivateProfileStruct (KERNEL.407)
1728  */
1729 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1730                                         LPVOID buf, UINT16 len, LPCSTR filename)
1731 {
1732     return GetPrivateProfileStructA( section, key, buf, len, filename );
1733 }
1734
1735 /***********************************************************************
1736  *           GetPrivateProfileStructW (KERNEL32.@)
1737  *
1738  * Should match Win95's behaviour pretty much
1739  */
1740 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1741                                       LPVOID buf, UINT len, LPCWSTR filename)
1742 {
1743     BOOL        ret = FALSE;
1744
1745     EnterCriticalSection( &PROFILE_CritSect );
1746
1747     if (PROFILE_Open( filename )) {
1748         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1749         if (k) {
1750             TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1751             if (((strlenW(k->value) - 2) / 2) == len)
1752             {
1753                 LPWSTR end, p;
1754                 BOOL valid = TRUE;
1755                 WCHAR c;
1756                 DWORD chksum = 0;
1757
1758                 end  = k->value + strlenW(k->value); /* -> '\0' */
1759                 /* check for invalid chars in ASCII coded hex string */
1760                 for (p=k->value; p < end; p++)
1761                 {
1762                     if (!isxdigitW(*p))
1763                     {
1764                         WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1765                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1766                         valid = FALSE;
1767                         break;
1768                     }
1769                 }
1770                 if (valid)
1771                 {
1772                     BOOL highnibble = TRUE;
1773                     BYTE b = 0, val;
1774                     LPBYTE binbuf = (LPBYTE)buf;
1775
1776                     end -= 2; /* don't include checksum in output data */
1777                     /* translate ASCII hex format into binary data */
1778                     for (p=k->value; p < end; p++)
1779                     {
1780                         c = toupperW(*p);
1781                         val = (c > '9') ?
1782                                 (c - 'A' + 10) : (c - '0');
1783
1784                         if (highnibble)
1785                             b = val << 4;
1786                         else
1787                         {
1788                             b += val;
1789                             *binbuf++ = b; /* feed binary data into output */
1790                             chksum += b; /* calculate checksum */
1791                         }
1792                         highnibble ^= 1; /* toggle */
1793                     }
1794                     /* retrieve stored checksum value */
1795                     c = toupperW(*p++);
1796                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1797                     c = toupperW(*p);
1798                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1799                     if (b == (chksum & 0xff)) /* checksums match ? */
1800                         ret = TRUE;
1801                 }
1802             }
1803         }
1804     }
1805     LeaveCriticalSection( &PROFILE_CritSect );
1806
1807     return ret;
1808 }
1809
1810 /***********************************************************************
1811  *           GetPrivateProfileStructA (KERNEL32.@)
1812  */
1813 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1814                                       LPVOID buffer, UINT len, LPCSTR filename)
1815 {
1816     UNICODE_STRING sectionW, keyW, filenameW;
1817     INT ret;
1818
1819     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1820     else sectionW.Buffer = NULL;
1821     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1822     else keyW.Buffer = NULL;
1823     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1824     else filenameW.Buffer = NULL;
1825
1826     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1827                                    filenameW.Buffer);
1828     /* Do not translate binary data. */
1829
1830     RtlFreeUnicodeString(&sectionW);
1831     RtlFreeUnicodeString(&keyW);
1832     RtlFreeUnicodeString(&filenameW);
1833     return ret;
1834 }
1835
1836
1837
1838 /***********************************************************************
1839  *           WritePrivateProfileStruct (KERNEL.406)
1840  */
1841 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1842         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1843 {
1844     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1845 }
1846
1847 /***********************************************************************
1848  *           WritePrivateProfileStructW (KERNEL32.@)
1849  */
1850 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1851                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1852 {
1853     BOOL ret = FALSE;
1854     LPBYTE binbuf;
1855     LPWSTR outstring, p;
1856     DWORD sum = 0;
1857
1858     if (!section && !key && !buf)  /* flush the cache */
1859         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1860
1861     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1862     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1863     p = outstring;
1864     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1865       *p++ = hex[*binbuf >> 4];
1866       *p++ = hex[*binbuf & 0xf];
1867       sum += *binbuf;
1868     }
1869     /* checksum is sum & 0xff */
1870     *p++ = hex[(sum & 0xf0) >> 4];
1871     *p++ = hex[sum & 0xf];
1872     *p++ = '\0';
1873
1874     EnterCriticalSection( &PROFILE_CritSect );
1875
1876     if (PROFILE_Open( filename )) {
1877         ret = PROFILE_SetString( section, key, outstring, FALSE);
1878         PROFILE_FlushFile();
1879     }
1880
1881     LeaveCriticalSection( &PROFILE_CritSect );
1882
1883     HeapFree( GetProcessHeap(), 0, outstring );
1884
1885     return ret;
1886 }
1887
1888 /***********************************************************************
1889  *           WritePrivateProfileStructA (KERNEL32.@)
1890  */
1891 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1892                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1893 {
1894     UNICODE_STRING sectionW, keyW, filenameW;
1895     INT ret;
1896
1897     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1898     else sectionW.Buffer = NULL;
1899     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1900     else keyW.Buffer = NULL;
1901     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1902     else filenameW.Buffer = NULL;
1903
1904     /* Do not translate binary data. */
1905     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1906                                      filenameW.Buffer);
1907
1908     RtlFreeUnicodeString(&sectionW);
1909     RtlFreeUnicodeString(&keyW);
1910     RtlFreeUnicodeString(&filenameW);
1911     return ret;
1912 }
1913
1914
1915 /***********************************************************************
1916  *           WriteOutProfiles   (KERNEL.315)
1917  */
1918 void WINAPI WriteOutProfiles16(void)
1919 {
1920     EnterCriticalSection( &PROFILE_CritSect );
1921     PROFILE_FlushFile();
1922     LeaveCriticalSection( &PROFILE_CritSect );
1923 }
1924
1925 /***********************************************************************
1926  *           CloseProfileUserMapping   (KERNEL32.@)
1927  */
1928 BOOL WINAPI CloseProfileUserMapping(void) {
1929     FIXME("(), stub!\n");
1930     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1931     return FALSE;
1932 }