Maintain lower cased file names in the Wine config directory.
[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, *name_lwr;
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
577         /* create a lower cased version of the name */
578         name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
579         strcpyW(name_lwr, name);
580         strlwrW(name_lwr);
581         WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
582                             p, sizeof(buffer) - strlen(buffer), NULL, NULL);
583         HeapFree(GetProcessHeap(), 0, name_lwr);
584
585         file = fopen( buffer, "w" );
586         unix_name = buffer;
587     }
588
589     if (!file)
590     {
591         WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
592         return FALSE;
593     }
594
595     TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
596     PROFILE_Save( file, CurProfile->section );
597     fclose( file );
598     CurProfile->changed = FALSE;
599     if(!stat(unix_name,&buf))
600        CurProfile->mtime=buf.st_mtime;
601     return TRUE;
602 }
603
604
605 /***********************************************************************
606  *           PROFILE_ReleaseFile
607  *
608  * Flush the current profile to disk and remove it from the cache.
609  */
610 static void PROFILE_ReleaseFile(void)
611 {
612     PROFILE_FlushFile();
613     PROFILE_Free( CurProfile->section );
614     if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
615     if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
616     if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
617     CurProfile->changed   = FALSE;
618     CurProfile->section   = NULL;
619     CurProfile->dos_name  = NULL;
620     CurProfile->unix_name = NULL;
621     CurProfile->filename  = NULL;
622     CurProfile->mtime     = 0;
623 }
624
625
626 /***********************************************************************
627  *           PROFILE_Open
628  *
629  * Open a profile file, checking the cached file first.
630  */
631 static BOOL PROFILE_Open( LPCWSTR filename )
632 {
633     DOS_FULL_NAME full_name;
634     char buffer[MAX_PATHNAME_LEN];
635     WCHAR *newdos_name;
636     WCHAR *name, *name_lwr;
637     char *p;
638     FILE *file = NULL;
639     int i,j;
640     struct stat buf;
641     PROFILE *tempProfile;
642
643     /* First time around */
644
645     if(!CurProfile)
646        for(i=0;i<N_CACHED_PROFILES;i++)
647          {
648           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
649           if(MRUProfile[i] == NULL) break;
650           MRUProfile[i]->changed=FALSE;
651           MRUProfile[i]->section=NULL;
652           MRUProfile[i]->dos_name=NULL;
653           MRUProfile[i]->unix_name=NULL;
654           MRUProfile[i]->filename=NULL;
655           MRUProfile[i]->mtime=0;
656          }
657
658     /* Check for a match */
659
660     if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
661         strchrW( filename, ':' ))
662     {
663         if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
664     }
665     else
666     {
667         static const WCHAR bkslashW[] = {'\\',0};
668         WCHAR windirW[MAX_PATH];
669
670         GetWindowsDirectoryW( windirW, MAX_PATH );
671         strcatW( windirW, bkslashW );
672         strcatW( windirW, filename );
673         if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
674     }
675
676     for(i=0;i<N_CACHED_PROFILES;i++)
677       {
678        if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
679            (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
680          {
681           if(i)
682             {
683              PROFILE_FlushFile();
684              tempProfile=MRUProfile[i];
685              for(j=i;j>0;j--)
686                 MRUProfile[j]=MRUProfile[j-1];
687              CurProfile=tempProfile;
688             }
689           if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
690              TRACE("(%s): already opened (mru=%d)\n",
691                              debugstr_w(filename), i );
692           else
693               TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
694                              debugstr_w(filename), i );
695           return TRUE;
696          }
697       }
698
699     /* Flush the old current profile */
700     PROFILE_FlushFile();
701
702     /* Make the oldest profile the current one only in order to get rid of it */
703     if(i==N_CACHED_PROFILES)
704       {
705        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
706        for(i=N_CACHED_PROFILES-1;i>0;i--)
707           MRUProfile[i]=MRUProfile[i-1];
708        CurProfile=tempProfile;
709       }
710     if(CurProfile->filename) PROFILE_ReleaseFile();
711
712     /* OK, now that CurProfile is definitely free we assign it our new file */
713     newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
714     strcpyW( newdos_name, full_name.short_name );
715     CurProfile->dos_name  = newdos_name;
716     CurProfile->filename  = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
717     strcpyW( CurProfile->filename, filename );
718
719     /* Try to open the profile file, first in $HOME/.wine */
720
721     /* FIXME: this will need a more general solution */
722     strcpy( buffer, wine_get_config_dir() );
723     p = buffer + strlen(buffer);
724     *p++ = '/';
725     *p = 0; /* make strlen() below happy */
726     name = strrchrW( newdos_name, '\\' ) + 1;
727
728     /* create a lower cased version of the name */
729     name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
730     strcpyW(name_lwr, name);
731     strlwrW(name_lwr);
732     WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
733                         p, sizeof(buffer) - strlen(buffer), NULL, NULL);
734     HeapFree(GetProcessHeap(), 0, name_lwr);
735
736     if ((file = fopen( buffer, "r" )))
737     {
738         TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
739         CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
740         strcpy( CurProfile->unix_name, buffer );
741     }
742     else
743     {
744         CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
745         strcpy( CurProfile->unix_name, full_name.long_name );
746         if ((file = fopen( full_name.long_name, "r" )))
747             TRACE("(%s): found it in %s\n",
748                              debugstr_w(filename), full_name.long_name );
749     }
750
751     if (file)
752     {
753         CurProfile->section = PROFILE_Load( file );
754         fclose( file );
755         if(!stat(CurProfile->unix_name,&buf))
756            CurProfile->mtime=buf.st_mtime;
757     }
758     else
759     {
760         /* Does not exist yet, we will create it in PROFILE_FlushFile */
761         WARN("profile file %s not found\n", debugstr_w(newdos_name) );
762     }
763     return TRUE;
764 }
765
766
767 /***********************************************************************
768  *           PROFILE_GetSection
769  *
770  * Returns all keys of a section.
771  * If return_values is TRUE, also include the corresponding values.
772  */
773 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
774                                LPWSTR buffer, UINT len, BOOL handle_env,
775                                BOOL return_values )
776 {
777     PROFILEKEY *key;
778
779     if(!buffer) return 0;
780
781     TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
782
783     while (section)
784     {
785         if (section->name[0] && !strcmpiW( section->name, section_name ))
786         {
787             UINT oldlen = len;
788             for (key = section->key; key; key = key->next)
789             {
790                 if (len <= 2) break;
791                 if (!*key->name) continue;  /* Skip empty lines */
792                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
793                 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
794                 len -= strlenW(buffer) + 1;
795                 buffer += strlenW(buffer) + 1;
796                 if (len < 2)
797                     break;
798                 if (return_values && key->value) {
799                         buffer[-1] = '=';
800                         PROFILE_CopyEntry ( buffer,
801                                 key->value, len - 1, handle_env, 0 );
802                         len -= strlenW(buffer) + 1;
803                         buffer += strlenW(buffer) + 1;
804                 }
805             }
806             *buffer = '\0';
807             if (len <= 1)
808                 /*If either lpszSection or lpszKey is NULL and the supplied
809                   destination buffer is too small to hold all the strings,
810                   the last string is truncated and followed by two null characters.
811                   In this case, the return value is equal to cchReturnBuffer
812                   minus two. */
813             {
814                 buffer[-1] = '\0';
815                 return oldlen - 2;
816             }
817             return oldlen - len;
818         }
819         section = section->next;
820     }
821     buffer[0] = buffer[1] = '\0';
822     return 0;
823 }
824
825 /* See GetPrivateProfileSectionNamesA for documentation */
826 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
827 {
828     LPWSTR buf;
829     UINT f,l;
830     PROFILESECTION *section;
831
832     if (!buffer || !len)
833         return 0;
834     if (len==1) {
835         *buffer='\0';
836         return 0;
837     }
838
839     f=len-1;
840     buf=buffer;
841     section = CurProfile->section;
842     while ((section!=NULL)) {
843         if (section->name[0]) {
844             l = strlenW(section->name)+1;
845             if (l > f) {
846                 if (f>0) {
847                     strncpyW(buf, section->name, f-1);
848                     buf += f-1;
849                     *buf++='\0';
850                 }
851                 *buf='\0';
852                 return len-2;
853             }
854             strcpyW(buf, section->name);
855             buf += l;
856             f -= l;
857         }
858         section = section->next;
859     }
860     *buf='\0';
861     return buf-buffer;
862 }
863
864
865 /***********************************************************************
866  *           PROFILE_GetString
867  *
868  * Get a profile string.
869  *
870  * Tests with GetPrivateProfileString16, W95a,
871  * with filled buffer ("****...") and section "set1" and key_name "1" valid:
872  * section      key_name        def_val         res     buffer
873  * "set1"       "1"             "x"             43      [data]
874  * "set1"       "1   "          "x"             43      [data]          (!)
875  * "set1"       "  1  "'        "x"             43      [data]          (!)
876  * "set1"       ""              "x"             1       "x"
877  * "set1"       ""              "x   "          1       "x"             (!)
878  * "set1"       ""              "  x   "        3       "  x"           (!)
879  * "set1"       NULL            "x"             6       "1\02\03\0\0"
880  * "set1"       ""              "x"             1       "x"
881  * NULL         "1"             "x"             0       ""              (!)
882  * ""           "1"             "x"             1       "x"
883  * NULL         NULL            ""              0       ""
884  *
885  *
886  */
887 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
888                               LPCWSTR def_val, LPWSTR buffer, UINT len )
889 {
890     PROFILEKEY *key = NULL;
891     static const WCHAR empty_strW[] = { 0 };
892
893     if(!buffer) return 0;
894
895     if (!def_val) def_val = empty_strW;
896     if (key_name)
897     {
898         if (!key_name[0])
899         {
900             /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
901             return 0;
902         }
903         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
904         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
905                            len, FALSE, TRUE );
906         TRACE("(%s,%s,%s): returning %s\n",
907               debugstr_w(section), debugstr_w(key_name),
908               debugstr_w(def_val), debugstr_w(buffer) );
909         return strlenW( buffer );
910     }
911     /* no "else" here ! */
912     if (section && section[0])
913     {
914         INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
915         if (!buffer[0]) /* no luck -> def_val */
916         {
917             PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
918             ret = strlenW(buffer);
919         }
920         return ret;
921     }
922     buffer[0] = '\0';
923     return 0;
924 }
925
926
927 /***********************************************************************
928  *           PROFILE_SetString
929  *
930  * Set a profile string.
931  */
932 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
933                                LPCWSTR value, BOOL create_always )
934 {
935     if (!key_name)  /* Delete a whole section */
936     {
937         TRACE("(%s)\n", debugstr_w(section_name));
938         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
939                                                       section_name );
940         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
941                                 this is not an error on application's level.*/
942     }
943     else if (!value)  /* Delete a key */
944     {
945         TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
946         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
947                                                   section_name, key_name );
948         return TRUE;          /* same error handling as above */
949     }
950     else  /* Set the key value */
951     {
952         PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
953                                         key_name, TRUE, create_always );
954         TRACE("(%s,%s,%s):\n",
955               debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
956         if (!key) return FALSE;
957         if (key->value)
958         {
959             /* strip the leading spaces. We can safely strip \n\r and
960              * friends too, they should not happen here anyway. */
961             while (PROFILE_isspace(*value)) value++;
962
963             if (!strcmpW( key->value, value ))
964             {
965                 TRACE("  no change needed\n" );
966                 return TRUE;  /* No change needed */
967             }
968             TRACE("  replacing %s\n", debugstr_w(key->value) );
969             HeapFree( GetProcessHeap(), 0, key->value );
970         }
971         else TRACE("  creating key\n" );
972         key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
973         strcpyW( key->value, value );
974         CurProfile->changed = TRUE;
975     }
976     return TRUE;
977 }
978
979
980 /***********************************************************************
981  *           PROFILE_GetWineIniString
982  *
983  * Get a config string from the wine.ini file.
984  */
985 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
986                               LPCWSTR def, LPWSTR buffer, int len )
987 {
988     HKEY hkey;
989     NTSTATUS err;
990     OBJECT_ATTRIBUTES attr;
991     UNICODE_STRING nameW;
992
993     attr.Length = sizeof(attr);
994     attr.RootDirectory = wine_profile_key;
995     attr.ObjectName = &nameW;
996     attr.Attributes = 0;
997     attr.SecurityDescriptor = NULL;
998     attr.SecurityQualityOfService = NULL;
999     RtlInitUnicodeString( &nameW, section );
1000     if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
1001     {
1002         char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
1003         DWORD count;
1004
1005         RtlInitUnicodeString( &nameW, key_name );
1006         if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
1007                                      tmp, sizeof(tmp), &count )))
1008         {
1009             WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
1010             PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
1011         }
1012         NtClose( hkey );
1013     }
1014
1015     if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
1016     TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
1017            debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
1018     return strlenW(buffer);
1019 }
1020
1021
1022 /******************************************************************************
1023  *
1024  *           PROFILE_GetWineIniBool
1025  *
1026  *   Reads a boolean value from the wine.ini file.  This function attempts to
1027  *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
1028  *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
1029  *   true.  Anything else results in the return of the default value.
1030  *
1031  *   This function uses 1 to indicate true, and 0 for false.  You can check
1032  *   for existence by setting def to something other than 0 or 1 and
1033  *   examining the return value.
1034  */
1035 int  PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
1036 {
1037     static const WCHAR def_valueW[] = {'~',0};
1038     WCHAR key_value[2];
1039     int  retval;
1040
1041     PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
1042
1043     switch(key_value[0]) {
1044     case 'n':
1045     case 'N':
1046     case 'f':
1047     case 'F':
1048     case '0':
1049         retval = 0;
1050         break;
1051
1052     case 'y':
1053     case 'Y':
1054     case 't':
1055     case 'T':
1056     case '1':
1057         retval = 1;
1058         break;
1059
1060     default:
1061         retval = def;
1062     }
1063
1064     TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1065                     def ? "TRUE" : "FALSE", key_value[0],
1066                     retval ? "TRUE" : "FALSE");
1067
1068     return retval;
1069 }
1070
1071
1072 /***********************************************************************
1073  *           PROFILE_LoadWineIni
1074  *
1075  * Load the old .winerc file.
1076  */
1077 int PROFILE_LoadWineIni(void)
1078 {
1079     OBJECT_ATTRIBUTES attr;
1080     UNICODE_STRING nameW;
1081     char buffer[MAX_PATHNAME_LEN];
1082     const char *p;
1083     FILE *f;
1084     HKEY hKeySW;
1085     DWORD disp;
1086
1087     attr.Length = sizeof(attr);
1088     attr.RootDirectory = 0;
1089     attr.ObjectName = &nameW;
1090     attr.Attributes = 0;
1091     attr.SecurityDescriptor = NULL;
1092     attr.SecurityQualityOfService = NULL;
1093
1094     /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1095     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine" ) ||
1096         NtCreateKey( &hKeySW, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp ))
1097     {
1098         ERR("Cannot create config registry key\n" );
1099         ExitProcess( 1 );
1100     }
1101     RtlFreeUnicodeString( &nameW );
1102     NtClose( hKeySW );
1103
1104     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1105         NtCreateKey( &wine_profile_key, KEY_ALL_ACCESS, &attr, 0,
1106                      NULL, REG_OPTION_VOLATILE, &disp ))
1107     {
1108         ERR("Cannot create config registry key\n" );
1109         ExitProcess( 1 );
1110     }
1111     RtlFreeUnicodeString( &nameW );
1112
1113     if (disp == REG_OPENED_EXISTING_KEY) return 1;  /* loaded by the server */
1114
1115     if ((p = getenv( "HOME" )) != NULL)
1116     {
1117         lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
1118         strcat( buffer, PROFILE_WineIniName );
1119         if ((f = fopen( buffer, "r" )) != NULL)
1120         {
1121             lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
1122
1123             /* convert to the new format */
1124             sprintf( buffer, "%s/config", wine_get_config_dir() );
1125             convert_config( f, buffer );
1126             fclose( f );
1127
1128             MESSAGE( "The '%s' configuration file has been converted\n"
1129                      "to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
1130             MESSAGE( "You should verify that the contents of the new file are correct,\n"
1131                      "and then remove the old one and restart Wine.\n" );
1132             ExitProcess(0);
1133         }
1134     }
1135     else WARN("could not get $HOME value for config file.\n" );
1136
1137     MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
1138     return 0;
1139 }
1140
1141
1142 /***********************************************************************
1143  *           PROFILE_UsageWineIni
1144  *
1145  * Explain the wine.ini file to those who don't read documentation.
1146  * Keep below one screenful in length so that error messages above are
1147  * noticed.
1148  */
1149 void PROFILE_UsageWineIni(void)
1150 {
1151     MESSAGE("Perhaps you have not properly edited or created "
1152         "your Wine configuration file.\n");
1153     MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1154     /* RTFM, so to say */
1155 }
1156
1157
1158 /********************* API functions **********************************/
1159
1160 /***********************************************************************
1161  *           GetProfileInt   (KERNEL.57)
1162  */
1163 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1164 {
1165     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1166 }
1167
1168
1169 /***********************************************************************
1170  *           GetProfileIntA   (KERNEL32.@)
1171  */
1172 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1173 {
1174     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1175 }
1176
1177 /***********************************************************************
1178  *           GetProfileIntW   (KERNEL32.@)
1179  */
1180 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1181 {
1182     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1183 }
1184
1185 /*
1186  * if allow_section_name_copy is TRUE, allow the copying :
1187  *   - of Section names if 'section' is NULL
1188  *   - of Keys in a Section if 'entry' is NULL
1189  * (see MSDN doc for GetPrivateProfileString)
1190  */
1191 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1192                                             LPCWSTR def_val, LPWSTR buffer,
1193                                             UINT len, LPCWSTR filename,
1194                                             BOOL allow_section_name_copy )
1195 {
1196     int         ret;
1197     LPWSTR      pDefVal = NULL;
1198
1199     if (!filename)
1200         filename = wininiW;
1201
1202     TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1203           debugstr_w(def_val), buffer, len, debugstr_w(filename));
1204
1205     /* strip any trailing ' ' of def_val. */
1206     if (def_val)
1207     {
1208         LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1209
1210         while (p > def_val)
1211         {
1212             p--;
1213             if ((*p) != ' ')
1214                 break;
1215         }
1216         if (*p == ' ') /* ouch, contained trailing ' ' */
1217         {
1218             int len = (int)(p - def_val);
1219             pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1220             strncpyW(pDefVal, def_val, len);
1221             pDefVal[len] = '\0';
1222         }
1223     }
1224     if (!pDefVal)
1225         pDefVal = (LPWSTR)def_val;
1226
1227     EnterCriticalSection( &PROFILE_CritSect );
1228
1229     if (PROFILE_Open( filename )) {
1230         if ((allow_section_name_copy) && (section == NULL))
1231             ret = PROFILE_GetSectionNames(buffer, len);
1232         else
1233             /* PROFILE_GetString already handles the 'entry == NULL' case */
1234             ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1235     } else {
1236        lstrcpynW( buffer, pDefVal, len );
1237        ret = strlenW( buffer );
1238     }
1239
1240     LeaveCriticalSection( &PROFILE_CritSect );
1241
1242     if (pDefVal != def_val) /* allocated */
1243         HeapFree(GetProcessHeap(), 0, pDefVal);
1244
1245     TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1246
1247     return ret;
1248 }
1249
1250 /***********************************************************************
1251  *           GetPrivateProfileString   (KERNEL.128)
1252  */
1253 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1254                                         LPCSTR def_val, LPSTR buffer,
1255                                         UINT16 len, LPCSTR filename )
1256 {
1257     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1258     LPWSTR bufferW;
1259     INT16 retW, ret = 0;
1260
1261     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1262     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1263     else sectionW.Buffer = NULL;
1264     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1265     else entryW.Buffer = NULL;
1266     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1267     else def_valW.Buffer = NULL;
1268     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1269     else filenameW.Buffer = NULL;
1270
1271     retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1272                                      def_valW.Buffer, bufferW, len,
1273                                      filenameW.Buffer, FALSE );
1274     if (len)
1275     {
1276         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1277         if (!ret)
1278         {
1279             ret = len - 1;
1280             buffer[ret] = 0;
1281         }
1282         else
1283             ret--; /* strip terminating 0 */
1284     }
1285
1286     RtlFreeUnicodeString(&sectionW);
1287     RtlFreeUnicodeString(&entryW);
1288     RtlFreeUnicodeString(&def_valW);
1289     RtlFreeUnicodeString(&filenameW);
1290     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1291     return ret;
1292 }
1293
1294 /***********************************************************************
1295  *           GetPrivateProfileStringA   (KERNEL32.@)
1296  */
1297 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1298                                      LPCSTR def_val, LPSTR buffer,
1299                                      UINT len, LPCSTR filename )
1300 {
1301     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1302     LPWSTR bufferW;
1303     INT retW, ret = 0;
1304
1305     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1306     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1307     else sectionW.Buffer = NULL;
1308     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1309     else entryW.Buffer = NULL;
1310     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1311     else def_valW.Buffer = NULL;
1312     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1313     else filenameW.Buffer = NULL;
1314
1315     retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1316                                      def_valW.Buffer, bufferW, len,
1317                                      filenameW.Buffer);
1318     if (len)
1319     {
1320         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1321         if (!ret)
1322         {
1323             ret = len - 1;
1324             buffer[ret] = 0;
1325         }
1326         else
1327             ret--; /* strip terminating 0 */
1328     }
1329
1330     RtlFreeUnicodeString(&sectionW);
1331     RtlFreeUnicodeString(&entryW);
1332     RtlFreeUnicodeString(&def_valW);
1333     RtlFreeUnicodeString(&filenameW);
1334     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1335     return ret;
1336 }
1337
1338 /***********************************************************************
1339  *           GetPrivateProfileStringW   (KERNEL32.@)
1340  */
1341 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1342                                      LPCWSTR def_val, LPWSTR buffer,
1343                                      UINT len, LPCWSTR filename )
1344 {
1345     return PROFILE_GetPrivateProfileString( section, entry, def_val,
1346                                             buffer, len, filename, TRUE );
1347 }
1348
1349 /***********************************************************************
1350  *           GetProfileString   (KERNEL.58)
1351  */
1352 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1353                                  LPSTR buffer, UINT16 len )
1354 {
1355     return GetPrivateProfileString16( section, entry, def_val,
1356                                       buffer, len, "win.ini" );
1357 }
1358
1359 /***********************************************************************
1360  *           GetProfileStringA   (KERNEL32.@)
1361  */
1362 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1363                               LPSTR buffer, UINT len )
1364 {
1365     return GetPrivateProfileStringA( section, entry, def_val,
1366                                      buffer, len, "win.ini" );
1367 }
1368
1369 /***********************************************************************
1370  *           GetProfileStringW   (KERNEL32.@)
1371  */
1372 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1373                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1374 {
1375     return GetPrivateProfileStringW( section, entry, def_val,
1376                                      buffer, len, wininiW );
1377 }
1378
1379 /***********************************************************************
1380  *           WriteProfileString   (KERNEL.59)
1381  */
1382 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1383                                     LPCSTR string )
1384 {
1385     return WritePrivateProfileString16( section, entry, string, "win.ini" );
1386 }
1387
1388 /***********************************************************************
1389  *           WriteProfileStringA   (KERNEL32.@)
1390  */
1391 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1392                                  LPCSTR string )
1393 {
1394     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1395 }
1396
1397 /***********************************************************************
1398  *           WriteProfileStringW   (KERNEL32.@)
1399  */
1400 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1401                                      LPCWSTR string )
1402 {
1403     return WritePrivateProfileStringW( section, entry, string, wininiW );
1404 }
1405
1406
1407 /***********************************************************************
1408  *           GetPrivateProfileInt   (KERNEL.127)
1409  */
1410 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1411                                       INT16 def_val, LPCSTR filename )
1412 {
1413     /* we used to have some elaborate return value limitation (<= -32768 etc.)
1414      * here, but Win98SE doesn't care about this at all, so I deleted it.
1415      * AFAIR versions prior to Win9x had these limits, though. */
1416     return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1417 }
1418
1419 /***********************************************************************
1420  *           GetPrivateProfileIntA   (KERNEL32.@)
1421  */
1422 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1423                                    INT def_val, LPCSTR filename )
1424 {
1425     char buffer[20];
1426     long result;
1427
1428     if (!GetPrivateProfileStringA( section, entry, "",
1429                                    buffer, sizeof(buffer), filename ))
1430         return def_val;
1431     /* FIXME: if entry can be found but it's empty, then Win16 is
1432      * supposed to return 0 instead of def_val ! Difficult/problematic
1433      * to implement (every other failure also returns zero buffer),
1434      * thus wait until testing framework avail for making sure nothing
1435      * else gets broken that way. */
1436     if (!buffer[0]) return (UINT)def_val;
1437
1438     /* Don't use strtol() here !
1439      * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1440      YES, scan for unsigned format ! (otherwise compatibility error) */
1441     if (!sscanf(buffer, "%lu", &result)) return 0;
1442     return (UINT)result;
1443 }
1444
1445 /***********************************************************************
1446  *           GetPrivateProfileIntW   (KERNEL32.@)
1447  *
1448  * FIXME: rewrite using unicode
1449  */
1450 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1451                                    INT def_val, LPCWSTR filename )
1452 {
1453     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1454     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1455     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1456     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1457     HeapFree( GetProcessHeap(), 0, sectionA );
1458     HeapFree( GetProcessHeap(), 0, filenameA );
1459     HeapFree( GetProcessHeap(), 0, entryA );
1460     return res;
1461 }
1462
1463 /***********************************************************************
1464  *           GetPrivateProfileSection   (KERNEL.418)
1465  */
1466 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1467                                         UINT16 len, LPCSTR filename )
1468 {
1469     return GetPrivateProfileSectionA( section, buffer, len, filename );
1470 }
1471
1472 /***********************************************************************
1473  *           GetPrivateProfileSectionW   (KERNEL32.@)
1474  */
1475 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1476                                       DWORD len, LPCWSTR filename )
1477 {
1478     int         ret = 0;
1479
1480     EnterCriticalSection( &PROFILE_CritSect );
1481
1482     if (PROFILE_Open( filename ))
1483         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1484                                  FALSE, TRUE);
1485
1486     LeaveCriticalSection( &PROFILE_CritSect );
1487
1488     return ret;
1489 }
1490
1491 /***********************************************************************
1492  *           GetPrivateProfileSectionA   (KERNEL32.@)
1493  */
1494 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1495                                       DWORD len, LPCSTR filename )
1496 {
1497     UNICODE_STRING sectionW, filenameW;
1498     LPWSTR bufferW;
1499     INT retW, ret = 0;
1500
1501     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1502     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1503     else sectionW.Buffer = NULL;
1504     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1505     else filenameW.Buffer = NULL;
1506
1507     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1508     if (len > 2)
1509     {
1510         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1511         if (ret > 2)
1512             ret -= 2;
1513         else
1514         {
1515             ret = 0;
1516             buffer[len-2] = 0;
1517             buffer[len-1] = 0;
1518         }
1519     }
1520     else
1521     {
1522         buffer[0] = 0;
1523         buffer[1] = 0;
1524     }
1525
1526     RtlFreeUnicodeString(&sectionW);
1527     RtlFreeUnicodeString(&filenameW);
1528     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1529     return ret;
1530 }
1531
1532 /***********************************************************************
1533  *           GetProfileSection   (KERNEL.419)
1534  */
1535 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1536 {
1537     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1538 }
1539
1540 /***********************************************************************
1541  *           GetProfileSectionA   (KERNEL32.@)
1542  */
1543 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1544 {
1545     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1546 }
1547
1548 /***********************************************************************
1549  *           GetProfileSectionW   (KERNEL32.@)
1550  */
1551 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1552 {
1553     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1554 }
1555
1556
1557 /***********************************************************************
1558  *           WritePrivateProfileString   (KERNEL.129)
1559  */
1560 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1561                                            LPCSTR string, LPCSTR filename )
1562 {
1563     return WritePrivateProfileStringA(section,entry,string,filename);
1564 }
1565
1566 /***********************************************************************
1567  *           WritePrivateProfileStringW   (KERNEL32.@)
1568  */
1569 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1570                                         LPCWSTR string, LPCWSTR filename )
1571 {
1572     BOOL ret = FALSE;
1573
1574     EnterCriticalSection( &PROFILE_CritSect );
1575
1576     if (PROFILE_Open( filename ))
1577     {
1578         if (!section && !entry && !string) /* documented "file flush" case */
1579         {
1580             PROFILE_FlushFile();
1581             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1582         }
1583         else {
1584             if (!section) {
1585                 FIXME("(NULL?,%s,%s,%s)?\n",
1586                       debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1587             } else {
1588                 ret = PROFILE_SetString( section, entry, string, FALSE);
1589                 PROFILE_FlushFile();
1590             }
1591         }
1592     }
1593
1594     LeaveCriticalSection( &PROFILE_CritSect );
1595     return ret;
1596 }
1597
1598 /***********************************************************************
1599  *           WritePrivateProfileStringA   (KERNEL32.@)
1600  */
1601 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1602                                         LPCSTR string, LPCSTR filename )
1603 {
1604     UNICODE_STRING sectionW, entryW, stringW, filenameW;
1605     BOOL ret;
1606
1607     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1608     else sectionW.Buffer = NULL;
1609     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1610     else entryW.Buffer = NULL;
1611     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1612     else stringW.Buffer = NULL;
1613     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1614     else filenameW.Buffer = NULL;
1615
1616     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1617                                      stringW.Buffer, filenameW.Buffer);
1618     RtlFreeUnicodeString(&sectionW);
1619     RtlFreeUnicodeString(&entryW);
1620     RtlFreeUnicodeString(&stringW);
1621     RtlFreeUnicodeString(&filenameW);
1622     return ret;
1623 }
1624
1625 /***********************************************************************
1626  *           WritePrivateProfileSection   (KERNEL.416)
1627  */
1628 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1629                                             LPCSTR string, LPCSTR filename )
1630 {
1631     return WritePrivateProfileSectionA( section, string, filename );
1632 }
1633
1634 /***********************************************************************
1635  *           WritePrivateProfileSectionW   (KERNEL32.@)
1636  */
1637 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1638                                          LPCWSTR string, LPCWSTR filename )
1639 {
1640     BOOL ret = FALSE;
1641     LPWSTR p;
1642
1643     EnterCriticalSection( &PROFILE_CritSect );
1644
1645     if (PROFILE_Open( filename )) {
1646         if (!section && !string)
1647             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1648         else if (!string) {/* delete the named section*/
1649             ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1650             PROFILE_FlushFile();
1651         } else {
1652             PROFILE_DeleteAllKeys(section);
1653             ret = TRUE;
1654             while(*string) {
1655                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1656                 strcpyW( buf, string );
1657                 if((p = strchrW( buf, '='))) {
1658                     *p='\0';
1659                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
1660                 }
1661                 HeapFree( GetProcessHeap(), 0, buf );
1662                 string += strlenW(string)+1;
1663             }
1664             PROFILE_FlushFile();
1665         }
1666     }
1667
1668     LeaveCriticalSection( &PROFILE_CritSect );
1669     return ret;
1670 }
1671
1672 /***********************************************************************
1673  *           WritePrivateProfileSectionA   (KERNEL32.@)
1674  */
1675 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1676                                          LPCSTR string, LPCSTR filename)
1677
1678 {
1679     UNICODE_STRING sectionW, filenameW;
1680     LPWSTR stringW;
1681     BOOL ret;
1682
1683     if (string)
1684     {
1685         INT lenA, lenW;
1686         LPCSTR p = string;
1687
1688         while(*p) p += strlen(p) + 1;
1689         lenA = p - string + 1;
1690         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1691         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1692             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1693     }
1694     else stringW = NULL;
1695     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1696     else sectionW.Buffer = NULL;
1697     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1698     else filenameW.Buffer = NULL;
1699
1700     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1701
1702     HeapFree(GetProcessHeap(), 0, stringW);
1703     RtlFreeUnicodeString(&sectionW);
1704     RtlFreeUnicodeString(&filenameW);
1705     return ret;
1706 }
1707
1708 /***********************************************************************
1709  *           WriteProfileSection   (KERNEL.417)
1710  */
1711 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1712 {
1713     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1714 }
1715
1716 /***********************************************************************
1717  *           WriteProfileSectionA   (KERNEL32.@)
1718  */
1719 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1720
1721 {
1722     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1723 }
1724
1725 /***********************************************************************
1726  *           WriteProfileSectionW   (KERNEL32.@)
1727  */
1728 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1729 {
1730    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1731 }
1732
1733 /***********************************************************************
1734  *           GetPrivateProfileSectionNames   (KERNEL.143)
1735  */
1736 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1737                                              LPCSTR filename )
1738 {
1739     return GetPrivateProfileSectionNamesA(buffer,size,filename);
1740 }
1741
1742
1743 /***********************************************************************
1744  *           GetProfileSectionNames   (KERNEL.142)
1745  */
1746 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1747
1748 {
1749     return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1750 }
1751
1752
1753 /***********************************************************************
1754  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1755  *
1756  * Returns the section names contained in the specified file.
1757  * FIXME: Where do we find this file when the path is relative?
1758  * The section names are returned as a list of strings with an extra
1759  * '\0' to mark the end of the list. Except for that the behavior
1760  * depends on the Windows version.
1761  *
1762  * Win95:
1763  * - if the buffer is 0 or 1 character long then it is as if it was of
1764  *   infinite length.
1765  * - otherwise, if the buffer is to small only the section names that fit
1766  *   are returned.
1767  * - note that this means if the buffer was to small to return even just
1768  *   the first section name then a single '\0' will be returned.
1769  * - the return value is the number of characters written in the buffer,
1770  *   except if the buffer was too smal in which case len-2 is returned
1771  *
1772  * Win2000:
1773  * - if the buffer is 0, 1 or 2 characters long then it is filled with
1774  *   '\0' and the return value is 0
1775  * - otherwise if the buffer is too small then the first section name that
1776  *   does not fit is truncated so that the string list can be terminated
1777  *   correctly (double '\0')
1778  * - the return value is the number of characters written in the buffer
1779  *   except for the trailing '\0'. If the buffer is too small, then the
1780  *   return value is len-2
1781  * - Win2000 has a bug that triggers when the section names and the
1782  *   trailing '\0' fit exactly in the buffer. In that case the trailing
1783  *   '\0' is missing.
1784  *
1785  * Wine implements the observed Win2000 behavior (except for the bug).
1786  *
1787  * Note that when the buffer is big enough then the return value may be any
1788  * value between 1 and len-1 (or len in Win95), including len-2.
1789  */
1790 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1791                                              LPCWSTR filename)
1792 {
1793     DWORD ret = 0;
1794
1795     EnterCriticalSection( &PROFILE_CritSect );
1796
1797     if (PROFILE_Open( filename ))
1798         ret = PROFILE_GetSectionNames(buffer, size);
1799
1800     LeaveCriticalSection( &PROFILE_CritSect );
1801
1802     return ret;
1803 }
1804
1805
1806 /***********************************************************************
1807  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
1808  */
1809 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1810                                              LPCSTR filename)
1811 {
1812     UNICODE_STRING filenameW;
1813     LPWSTR bufferW;
1814     INT retW, ret = 0;
1815
1816     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1817     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1818     else filenameW.Buffer = NULL;
1819
1820     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1821     if (retW && size)
1822     {
1823         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1824         if (!ret)
1825         {
1826             ret = size;
1827             buffer[size-1] = 0;
1828         }
1829     }
1830
1831     RtlFreeUnicodeString(&filenameW);
1832     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1833     return ret;
1834 }
1835
1836 /***********************************************************************
1837  *           GetPrivateProfileStruct (KERNEL.407)
1838  */
1839 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1840                                         LPVOID buf, UINT16 len, LPCSTR filename)
1841 {
1842     return GetPrivateProfileStructA( section, key, buf, len, filename );
1843 }
1844
1845 /***********************************************************************
1846  *           GetPrivateProfileStructW (KERNEL32.@)
1847  *
1848  * Should match Win95's behaviour pretty much
1849  */
1850 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1851                                       LPVOID buf, UINT len, LPCWSTR filename)
1852 {
1853     BOOL        ret = FALSE;
1854
1855     EnterCriticalSection( &PROFILE_CritSect );
1856
1857     if (PROFILE_Open( filename )) {
1858         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1859         if (k) {
1860             TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1861             if (((strlenW(k->value) - 2) / 2) == len)
1862             {
1863                 LPWSTR end, p;
1864                 BOOL valid = TRUE;
1865                 WCHAR c;
1866                 DWORD chksum = 0;
1867
1868                 end  = k->value + strlenW(k->value); /* -> '\0' */
1869                 /* check for invalid chars in ASCII coded hex string */
1870                 for (p=k->value; p < end; p++)
1871                 {
1872                     if (!isxdigitW(*p))
1873                     {
1874                         WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1875                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1876                         valid = FALSE;
1877                         break;
1878                     }
1879                 }
1880                 if (valid)
1881                 {
1882                     BOOL highnibble = TRUE;
1883                     BYTE b = 0, val;
1884                     LPBYTE binbuf = (LPBYTE)buf;
1885
1886                     end -= 2; /* don't include checksum in output data */
1887                     /* translate ASCII hex format into binary data */
1888                     for (p=k->value; p < end; p++)
1889                     {
1890                         c = toupperW(*p);
1891                         val = (c > '9') ?
1892                                 (c - 'A' + 10) : (c - '0');
1893
1894                         if (highnibble)
1895                             b = val << 4;
1896                         else
1897                         {
1898                             b += val;
1899                             *binbuf++ = b; /* feed binary data into output */
1900                             chksum += b; /* calculate checksum */
1901                         }
1902                         highnibble ^= 1; /* toggle */
1903                     }
1904                     /* retrieve stored checksum value */
1905                     c = toupperW(*p++);
1906                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1907                     c = toupperW(*p);
1908                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1909                     if (b == (chksum & 0xff)) /* checksums match ? */
1910                         ret = TRUE;
1911                 }
1912             }
1913         }
1914     }
1915     LeaveCriticalSection( &PROFILE_CritSect );
1916
1917     return ret;
1918 }
1919
1920 /***********************************************************************
1921  *           GetPrivateProfileStructA (KERNEL32.@)
1922  */
1923 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1924                                       LPVOID buffer, UINT len, LPCSTR filename)
1925 {
1926     UNICODE_STRING sectionW, keyW, filenameW;
1927     INT ret;
1928
1929     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1930     else sectionW.Buffer = NULL;
1931     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1932     else keyW.Buffer = NULL;
1933     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1934     else filenameW.Buffer = NULL;
1935
1936     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1937                                    filenameW.Buffer);
1938     /* Do not translate binary data. */
1939
1940     RtlFreeUnicodeString(&sectionW);
1941     RtlFreeUnicodeString(&keyW);
1942     RtlFreeUnicodeString(&filenameW);
1943     return ret;
1944 }
1945
1946
1947
1948 /***********************************************************************
1949  *           WritePrivateProfileStruct (KERNEL.406)
1950  */
1951 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1952         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1953 {
1954     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1955 }
1956
1957 /***********************************************************************
1958  *           WritePrivateProfileStructW (KERNEL32.@)
1959  */
1960 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1961                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1962 {
1963     BOOL ret = FALSE;
1964     LPBYTE binbuf;
1965     LPWSTR outstring, p;
1966     DWORD sum = 0;
1967
1968     if (!section && !key && !buf)  /* flush the cache */
1969         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1970
1971     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1972     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1973     p = outstring;
1974     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1975       *p++ = hex[*binbuf >> 4];
1976       *p++ = hex[*binbuf & 0xf];
1977       sum += *binbuf;
1978     }
1979     /* checksum is sum & 0xff */
1980     *p++ = hex[(sum & 0xf0) >> 4];
1981     *p++ = hex[sum & 0xf];
1982     *p++ = '\0';
1983
1984     EnterCriticalSection( &PROFILE_CritSect );
1985
1986     if (PROFILE_Open( filename )) {
1987         ret = PROFILE_SetString( section, key, outstring, FALSE);
1988         PROFILE_FlushFile();
1989     }
1990
1991     LeaveCriticalSection( &PROFILE_CritSect );
1992
1993     HeapFree( GetProcessHeap(), 0, outstring );
1994
1995     return ret;
1996 }
1997
1998 /***********************************************************************
1999  *           WritePrivateProfileStructA (KERNEL32.@)
2000  */
2001 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
2002                                         LPVOID buf, UINT bufsize, LPCSTR filename)
2003 {
2004     UNICODE_STRING sectionW, keyW, filenameW;
2005     INT ret;
2006
2007     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
2008     else sectionW.Buffer = NULL;
2009     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
2010     else keyW.Buffer = NULL;
2011     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
2012     else filenameW.Buffer = NULL;
2013
2014     /* Do not translate binary data. */
2015     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
2016                                      filenameW.Buffer);
2017
2018     RtlFreeUnicodeString(&sectionW);
2019     RtlFreeUnicodeString(&keyW);
2020     RtlFreeUnicodeString(&filenameW);
2021     return ret;
2022 }
2023
2024
2025 /***********************************************************************
2026  *           WriteOutProfiles   (KERNEL.315)
2027  */
2028 void WINAPI WriteOutProfiles16(void)
2029 {
2030     EnterCriticalSection( &PROFILE_CritSect );
2031     PROFILE_FlushFile();
2032     LeaveCriticalSection( &PROFILE_CritSect );
2033 }
2034
2035 /***********************************************************************
2036  *           CloseProfileUserMapping   (KERNEL32.@)
2037  */
2038 BOOL WINAPI CloseProfileUserMapping(void) {
2039     FIXME("(), stub!\n");
2040     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2041     return FALSE;
2042 }