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