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