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