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