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