Fixed some quirks (range is 32 bit, page up/dn mismatch on HZ trackbars).
[wine] / files / profile.c
1 /*
2  * Profile functions
3  *
4  * Copyright 1993 Miguel de Icaza
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <pwd.h>
15 #include <unistd.h>
16
17 #include "winbase.h"
18 #include "winerror.h"
19 #include "wine/winbase16.h"
20 #include "windef.h"
21 #include "wingdi.h"
22 #include "winuser.h"
23 #include "winnls.h"
24 #include "winreg.h"
25 #include "file.h"
26 #include "heap.h"
27 #include "debugtools.h"
28 #include "options.h"
29 #include "server.h"
30
31 DEFAULT_DEBUG_CHANNEL(profile);
32
33 typedef struct tagPROFILEKEY
34 {
35     char                  *name;
36     char                  *value;
37     struct tagPROFILEKEY  *next;
38 } PROFILEKEY;
39
40 typedef struct tagPROFILESECTION
41 {
42     char                       *name;
43     struct tagPROFILEKEY       *key;
44     struct tagPROFILESECTION   *next;
45 } PROFILESECTION; 
46
47
48 typedef struct
49 {
50     BOOL             changed;
51     PROFILESECTION  *section;
52     char            *dos_name;
53     char            *unix_name;
54     char            *filename;
55     time_t           mtime;
56 } PROFILE;
57
58
59 #define N_CACHED_PROFILES 10
60
61 /* Cached profile files */
62 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
63
64 #define CurProfile (MRUProfile[0])
65
66 /* wine.ini config file registry root */
67 static HKEY wine_profile_key;
68
69 #define PROFILE_MAX_LINE_LEN   1024
70
71 /* Wine profile name in $HOME directory; must begin with slash */
72 static const char PROFILE_WineIniName[] = "/.winerc";
73
74 /* Wine profile: the profile file being used */
75 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
76
77 /* Check for comments in profile */
78 #define IS_ENTRY_COMMENT(str)  ((str)[0] == ';')
79
80 #define WINE_INI_GLOBAL ETCDIR "/wine.conf"
81
82 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
83
84 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT;
85
86 static const char hex[16] = "0123456789ABCDEF";
87
88 /***********************************************************************
89  *           PROFILE_CopyEntry
90  *
91  * Copy the content of an entry into a buffer, removing quotes, and possibly
92  * translating environment variables.
93  */
94 static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
95                                int handle_env )
96 {
97     char quote = '\0';
98     const char *p;
99
100     if ((*value == '\'') || (*value == '\"'))
101     {
102         if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
103     }
104
105     if (!handle_env)
106     {
107         lstrcpynA( buffer, value, len );
108         if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
109         return;
110     }
111
112     for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
113     {
114         if ((*p == '$') && (p[1] == '{'))
115         {
116             char env_val[1024];
117             const char *env_p;
118             const char *p2 = strchr( p, '}' );
119             if (!p2) continue;  /* ignore it */
120             lstrcpynA(env_val, p + 2, min( sizeof(env_val), (int)(p2-p)-1 ));
121             if ((env_p = getenv( env_val )) != NULL)
122             {
123                 lstrcpynA( buffer, env_p, len );
124                 buffer += strlen( buffer );
125                 len -= strlen( buffer );
126             }
127             p = p2 + 1;
128         }
129     }
130     if (quote && (len > 1)) buffer--;
131     *buffer = '\0';
132 }
133
134
135 /***********************************************************************
136  *           PROFILE_Save
137  *
138  * Save a profile tree to a file.
139  */
140 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
141 {
142     PROFILEKEY *key;
143
144     for ( ; section; section = section->next)
145     {
146         if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name );
147         for (key = section->key; key; key = key->next)
148         {
149             fprintf( file, "%s", key->name );
150             if (key->value) fprintf( file, "=%s", key->value );
151             fprintf( file, "\r\n" );
152         }
153     }
154 }
155
156
157 /***********************************************************************
158  *           PROFILE_Free
159  *
160  * Free a profile tree.
161  */
162 static void PROFILE_Free( PROFILESECTION *section )
163 {
164     PROFILESECTION *next_section;
165     PROFILEKEY *key, *next_key;
166
167     for ( ; section; section = next_section)
168     {
169         if (section->name) HeapFree( GetProcessHeap(), 0, section->name );
170         for (key = section->key; key; key = next_key)
171         {
172             next_key = key->next;
173             if (key->name) HeapFree( GetProcessHeap(), 0, key->name );
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;
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 = NULL;
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                 section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
231                 if(section == NULL) break;
232                 section->name = HEAP_strdupA( GetProcessHeap(), 0, p );
233                 section->key  = NULL;
234                 section->next = NULL;
235                 *next_section = section;
236                 next_section  = &section->next;
237                 next_key      = &section->key;
238                 prev_key      = NULL;
239
240                 TRACE("New section: '%s'\n",section->name);
241
242                 continue;
243             }
244         }
245
246         p2=p+strlen(p) - 1;
247         while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
248
249         if ((p2 = strchr( p, '=' )) != NULL)
250         {
251             char *p3 = p2 - 1;
252             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
253             *p2++ = '\0';
254             while (*p2 && PROFILE_isspace(*p2)) p2++;
255         }
256
257         if(*p || !prev_key || *prev_key->name)
258         {
259            key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) );
260            if(key == NULL) break;
261            key->name  = HEAP_strdupA( GetProcessHeap(), 0, p );
262            key->value = p2 ? HEAP_strdupA( GetProcessHeap(), 0, p2 ) : NULL;
263            key->next  = NULL;
264            *next_key  = key;
265            next_key   = &key->next;
266            prev_key   = key;
267
268            TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)");
269         }
270     }
271     return first_section;
272 }
273
274
275 /***********************************************************************
276  *           PROFILE_RegistryLoad
277  *
278  * Load a profile tree from a file into a registry key.
279  */
280 static DWORD PROFILE_RegistryLoad( HKEY root, FILE *file )
281 {
282     HKEY hkey = 0;
283     DWORD err = 0;
284     char buffer[PROFILE_MAX_LINE_LEN];
285     char *p, *p2;
286     int line = 0;
287
288     while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
289     {
290         line++;
291         p = buffer;
292         while (*p && PROFILE_isspace(*p)) p++;
293         if (*p == '[')  /* section start */
294         {
295             if (!(p2 = strrchr( p, ']' )))
296             {
297                 WARN("Invalid section header at line %d: '%s'\n",
298                      line, p );
299             }
300             else
301             {
302                 *p2 = '\0';
303                 p++;
304                 if (hkey) RegCloseKey( hkey );
305                 if ((err = RegCreateKeyExA( root, p, 0, NULL, REG_OPTION_VOLATILE,
306                                             KEY_ALL_ACCESS, NULL, &hkey, NULL ))) return err;
307                 TRACE("New section: '%s'\n",p);
308                 continue;
309             }
310         }
311
312         p2=p+strlen(p) - 1;
313         while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
314
315         if ((p2 = strchr( p, '=' )) != NULL)
316         {
317             char *p3 = p2 - 1;
318             while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
319             *p2++ = '\0';
320             while (*p2 && PROFILE_isspace(*p2)) p2++;
321         }
322
323         if (*p && hkey && !IS_ENTRY_COMMENT(p))
324         {
325             if (!p2) p2 = "";
326             if ((err = RegSetValueExA( hkey, p, 0, REG_SZ, p2, strlen(p2)+1 )))
327             {
328                 RegCloseKey( hkey );
329                 return err;
330             }
331             TRACE("New key: name='%s', value='%s'\n",p,p2);
332         }
333     }
334     if (hkey) RegCloseKey( hkey );
335     return 0;
336 }
337
338
339 /***********************************************************************
340  *           PROFILE_DeleteSection
341  *
342  * Delete a section from a profile tree.
343  */
344 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
345 {
346     while (*section)
347     {
348         if ((*section)->name && !strcasecmp( (*section)->name, name ))
349         {
350             PROFILESECTION *to_del = *section;
351             *section = to_del->next;
352             to_del->next = NULL;
353             PROFILE_Free( to_del );
354             return TRUE;
355         }
356         section = &(*section)->next;
357     }
358     return FALSE;
359 }
360
361
362 /***********************************************************************
363  *           PROFILE_DeleteKey
364  *
365  * Delete a key from a profile tree.
366  */
367 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
368                                LPCSTR section_name, LPCSTR key_name )
369 {
370     while (*section)
371     {
372         if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
373         {
374             PROFILEKEY **key = &(*section)->key;
375             while (*key)
376             {
377                 if (!strcasecmp( (*key)->name, key_name ))
378                 {
379                     PROFILEKEY *to_del = *key;
380                     *key = to_del->next;
381                     if (to_del->name) HeapFree( GetProcessHeap(), 0, to_del->name );
382                     if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
383                     HeapFree( GetProcessHeap(), 0, to_del );
384                     return TRUE;
385                 }
386                 key = &(*key)->next;
387             }
388         }
389         section = &(*section)->next;
390     }
391     return FALSE;
392 }
393
394
395 /***********************************************************************
396  *           PROFILE_Find
397  *
398  * Find a key in a profile tree, optionally creating it.
399  */
400 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
401                                  const char *section_name,
402                                  const char *key_name, int create )
403 {
404     const char *p;
405     int seclen, keylen;
406
407     while (PROFILE_isspace(*section_name)) section_name++;
408     p = section_name + strlen(section_name) - 1;
409     while ((p > section_name) && PROFILE_isspace(*p)) p--;
410     seclen = p - section_name + 1;
411     
412     while (PROFILE_isspace(*key_name)) key_name++;
413     p = key_name + strlen(key_name) - 1;
414     while ((p > key_name) && PROFILE_isspace(*p)) p--;
415     keylen = p - key_name + 1;
416
417     while (*section)
418     {
419         if ( ((*section)->name)
420           && (!(strncasecmp( (*section)->name, section_name, seclen )))
421           && (((*section)->name)[seclen] == '\0') )
422         {
423             PROFILEKEY **key = &(*section)->key;
424             while (*key)
425             {
426                 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
427                   && (((*key)->name)[keylen] == '\0') )
428                     return *key;
429                 key = &(*key)->next;
430             }
431             if (!create) return NULL;
432             *key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
433             if(*key == NULL) return NULL;
434             (*key)->name  = HEAP_strdupA( GetProcessHeap(), 0, key_name );
435             (*key)->value = NULL;
436             (*key)->next  = NULL;
437             return *key;
438         }
439         section = &(*section)->next;
440     }
441     if (!create) return NULL;
442     *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) );
443     if(*section == NULL) return NULL;
444     (*section)->name = HEAP_strdupA( GetProcessHeap(), 0, section_name );
445     (*section)->next = NULL;
446     (*section)->key  = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
447     if((*section)->key  == NULL)
448     {
449             HeapFree(GetProcessHeap(), 0, *section);
450             return NULL;
451     }
452     (*section)->key->name  = HEAP_strdupA( GetProcessHeap(), 0, key_name );
453     (*section)->key->value = NULL;
454     (*section)->key->next  = NULL;
455     return (*section)->key;
456 }
457
458
459 /***********************************************************************
460  *           PROFILE_FlushFile
461  *
462  * Flush the current profile to disk if changed.
463  */
464 static BOOL PROFILE_FlushFile(void)
465 {
466     char *p, buffer[MAX_PATHNAME_LEN];
467     const char *unix_name;
468     FILE *file = NULL;
469     struct stat buf;
470
471     if(!CurProfile)
472     {
473         WARN("No current profile!\n");
474         return FALSE;
475     }
476
477     if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
478     if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
479     {
480         /* Try to create it in $HOME/.wine */
481         /* FIXME: this will need a more general solution */
482         strcpy( buffer, get_config_dir() );
483         p = buffer + strlen(buffer);
484         *p++ = '/';
485         strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
486         CharLowerA( p );
487         file = fopen( buffer, "w" );
488         unix_name = buffer;
489     }
490     
491     if (!file)
492     {
493         WARN("could not save profile file %s\n", CurProfile->dos_name);
494         return FALSE;
495     }
496
497     TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
498     PROFILE_Save( file, CurProfile->section );
499     fclose( file );
500     CurProfile->changed = FALSE;
501     if(!stat(unix_name,&buf))
502        CurProfile->mtime=buf.st_mtime;
503     return TRUE;
504 }
505
506
507 /***********************************************************************
508  *           PROFILE_ReleaseFile
509  *
510  * Flush the current profile to disk and remove it from the cache.
511  */
512 static void PROFILE_ReleaseFile(void)
513 {
514     PROFILE_FlushFile();
515     PROFILE_Free( CurProfile->section );
516     if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
517     if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
518     if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
519     CurProfile->changed   = FALSE;
520     CurProfile->section   = NULL;
521     CurProfile->dos_name  = NULL;
522     CurProfile->unix_name = NULL;
523     CurProfile->filename  = NULL;
524     CurProfile->mtime     = 0;
525 }
526
527
528 /***********************************************************************
529  *           PROFILE_Open
530  *
531  * Open a profile file, checking the cached file first.
532  */
533 static BOOL PROFILE_Open( LPCSTR filename )
534 {
535     DOS_FULL_NAME full_name;
536     char buffer[MAX_PATHNAME_LEN];
537     char *newdos_name, *p;
538     FILE *file = NULL;
539     int i,j;
540     struct stat buf;
541     PROFILE *tempProfile;
542
543     /* First time around */
544
545     if(!CurProfile)
546        for(i=0;i<N_CACHED_PROFILES;i++)
547          {
548           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
549           if(MRUProfile[i] == NULL) break;
550           MRUProfile[i]->changed=FALSE;
551           MRUProfile[i]->section=NULL;
552           MRUProfile[i]->dos_name=NULL;
553           MRUProfile[i]->unix_name=NULL;
554           MRUProfile[i]->filename=NULL;
555           MRUProfile[i]->mtime=0;
556          }
557
558     /* Check for a match */
559
560     if (strchr( filename, '/' ) || strchr( filename, '\\' ) || 
561         strchr( filename, ':' ))
562     {
563         if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
564     }
565     else
566     {
567         GetWindowsDirectoryA( buffer, sizeof(buffer) );
568         strcat( buffer, "\\" );
569         strcat( buffer, filename );
570         if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
571     }
572
573     for(i=0;i<N_CACHED_PROFILES;i++)
574       {
575        if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
576            (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
577          {
578           if(i)
579             {
580              PROFILE_FlushFile();
581              tempProfile=MRUProfile[i];
582              for(j=i;j>0;j--)
583                 MRUProfile[j]=MRUProfile[j-1];
584              CurProfile=tempProfile;
585             }
586           if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
587              TRACE("(%s): already opened (mru=%d)\n",
588                               filename, i );
589           else
590               TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
591                              filename, i );
592           return TRUE;
593          }
594       }
595
596     /* Flush the old current profile */
597     PROFILE_FlushFile();
598
599     /* Make the oldest profile the current one only in order to get rid of it */
600     if(i==N_CACHED_PROFILES)
601       {
602        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
603        for(i=N_CACHED_PROFILES-1;i>0;i--)
604           MRUProfile[i]=MRUProfile[i-1];
605        CurProfile=tempProfile;
606       }
607     if(CurProfile->filename) PROFILE_ReleaseFile();
608
609     /* OK, now that CurProfile is definitely free we assign it our new file */
610     newdos_name = HEAP_strdupA( GetProcessHeap(), 0, full_name.short_name );
611     CurProfile->dos_name  = newdos_name;
612     CurProfile->filename  = HEAP_strdupA( GetProcessHeap(), 0, filename );
613
614     /* Try to open the profile file, first in $HOME/.wine */
615
616     /* FIXME: this will need a more general solution */
617     strcpy( buffer, get_config_dir() );
618     p = buffer + strlen(buffer);
619     *p++ = '/';
620     strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
621     CharLowerA( p );
622     if ((file = fopen( buffer, "r" )))
623     {
624         TRACE("(%s): found it in %s\n",
625               filename, buffer );
626         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0, buffer );
627     }
628
629     if (!file)
630     {
631         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0,
632                                              full_name.long_name );
633         if ((file = fopen( full_name.long_name, "r" )))
634             TRACE("(%s): found it in %s\n",
635                              filename, full_name.long_name );
636     }
637
638     if (file)
639     {
640         CurProfile->section = PROFILE_Load( file );
641         fclose( file );
642         if(!stat(CurProfile->unix_name,&buf))
643            CurProfile->mtime=buf.st_mtime;
644     }
645     else
646     {
647         /* Does not exist yet, we will create it in PROFILE_FlushFile */
648         WARN("profile file %s not found\n", newdos_name );
649     }
650     return TRUE;
651 }
652
653
654 /***********************************************************************
655  *           PROFILE_GetSection
656  *
657  * Returns all keys of a section.
658  * If return_values is TRUE, also include the corresponding values.
659  */
660 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
661                                LPSTR buffer, UINT len, BOOL handle_env,
662                                BOOL return_values )
663 {
664     PROFILEKEY *key;
665     while (section)
666     {
667         if (section->name && !strcasecmp( section->name, section_name ))
668         {
669             UINT oldlen = len;
670             for (key = section->key; key; key = key->next)
671             {
672                 if (len <= 2) break;
673                 if (!*key->name) continue;  /* Skip empty lines */
674                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
675                 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
676                 len -= strlen(buffer) + 1;
677                 buffer += strlen(buffer) + 1;
678                 if (return_values && key->value) {
679                         buffer[-1] = '=';
680                         PROFILE_CopyEntry ( buffer,
681                                 key->value, len - 1, handle_env );
682                         len -= strlen(buffer) + 1;
683                         buffer += strlen(buffer) + 1;
684                 }
685             }
686             *buffer = '\0';
687             if (len <= 1)
688                 /*If either lpszSection or lpszKey is NULL and the supplied
689                   destination buffer is too small to hold all the strings, 
690                   the last string is truncated and followed by two null characters.
691                   In this case, the return value is equal to cchReturnBuffer
692                   minus two. */
693             {
694                 buffer[-1] = '\0';
695                 return oldlen - 2;
696             }
697             return oldlen - len;
698         }
699         section = section->next;
700     }
701     buffer[0] = buffer[1] = '\0';
702     return 0;
703 }
704
705
706 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
707 {
708     LPSTR buf = buffer;
709     WORD l, cursize = 0;
710     PROFILESECTION *section;
711
712     for (section = CurProfile->section; section; section = section->next)
713         if (section->name) {
714             l = strlen(section->name);
715             cursize += l+1;
716             if (cursize > len+1)
717                 return len-2;
718
719             strcpy(buf, section->name);
720             buf += l+1;
721         }
722
723     *buf=0;
724     buf++;
725     return buf-buffer;
726 }
727
728
729 /***********************************************************************
730  *           PROFILE_GetString
731  *
732  * Get a profile string.
733  */
734 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
735                               LPCSTR def_val, LPSTR buffer, UINT len )
736 {
737     PROFILEKEY *key = NULL;
738
739     if (!def_val) def_val = "";
740     if (key_name && key_name[0])
741     {
742         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
743         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
744                            len, FALSE );
745         TRACE("('%s','%s','%s'): returning '%s'\n",
746                          section, key_name, def_val, buffer );
747         return strlen( buffer );
748     }
749     if (key_name && !(key_name[0]))
750       /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227*/
751       return 0;
752     if (section && section[0])
753         return PROFILE_GetSection(CurProfile->section, section, buffer, len,
754                                 FALSE, FALSE);
755     /* undocumented; both section and key_name are NULL */
756     return PROFILE_GetSectionNames(buffer, len);
757 }
758
759
760 /***********************************************************************
761  *           PROFILE_SetString
762  *
763  * Set a profile string.
764  */
765 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
766                                LPCSTR value )
767 {
768     if (!key_name)  /* Delete a whole section */
769     {
770         TRACE("('%s')\n", section_name);
771         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
772                                                       section_name );
773         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
774                                 this is not an error on application's level.*/
775     }
776     else if (!value)  /* Delete a key */
777     {
778         TRACE("('%s','%s')\n",
779                          section_name, key_name );
780         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
781                                                   section_name, key_name );
782         return TRUE;          /* same error handling as above */
783     }
784     else  /* Set the key value */
785     {
786         PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
787                                         key_name, TRUE );
788         TRACE("('%s','%s','%s'): \n",
789                          section_name, key_name, value );
790         if (!key) return FALSE;
791         if (key->value)
792         {
793             if (!strcmp( key->value, value ))
794             {
795                 TRACE("  no change needed\n" );
796                 return TRUE;  /* No change needed */
797             }
798             TRACE("  replacing '%s'\n", key->value );
799             HeapFree( GetProcessHeap(), 0, key->value );
800         }
801         else TRACE("  creating key\n" );
802         key->value = HEAP_strdupA( GetProcessHeap(), 0, value );
803         CurProfile->changed = TRUE;
804     }
805     return TRUE;
806 }
807
808
809 /***********************************************************************
810  *           PROFILE_GetWineIniString
811  *
812  * Get a config string from the wine.ini file.
813  */
814 int PROFILE_GetWineIniString( const char *section, const char *key_name,
815                               const char *def, char *buffer, int len )
816 {
817     char tmp[PROFILE_MAX_LINE_LEN];
818     HKEY hkey;
819     DWORD err;
820
821     if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey )))
822     {
823         DWORD type;
824         DWORD count = sizeof(tmp);
825         err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count );
826         RegCloseKey( hkey );
827     }
828     PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE );
829     TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer );
830     return strlen(buffer);
831 }
832
833
834 /***********************************************************************
835  *           PROFILE_EnumWineIniString
836  *
837  * Get a config string from the wine.ini file.
838  */
839 BOOL PROFILE_EnumWineIniString( const char *section, int index,
840                                 char *name, int name_len, char *buffer, int len )
841 {
842     char tmp[PROFILE_MAX_LINE_LEN];
843     HKEY hkey;
844     DWORD err, type;
845     DWORD count = sizeof(tmp);
846
847     if (RegOpenKeyA( wine_profile_key, section, &hkey )) return FALSE;
848     err = RegEnumValueA( hkey, index, name, (DWORD*)&name_len, NULL, &type, tmp, &count );
849     RegCloseKey( hkey );
850     if (!err)
851     {
852         PROFILE_CopyEntry( buffer, tmp, len, TRUE );
853         TRACE( "('%s',%d): returning '%s'='%s'\n", section, index, name, buffer );
854     }
855     return !err;
856 }
857
858
859 /***********************************************************************
860  *           PROFILE_GetWineIniInt
861  *
862  * Get a config integer from the wine.ini file.
863  */
864 int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
865 {
866     char buffer[20];
867     char *p;
868     long result;
869
870     PROFILE_GetWineIniString( section, key_name, "", buffer, sizeof(buffer) );
871     if (!buffer[0]) return def;
872     result = strtol( buffer, &p, 0 );
873     return (p == buffer) ? 0  /* No digits at all */ : (int)result;
874 }
875
876
877 /******************************************************************************
878  *
879  *   int  PROFILE_GetWineIniBool(
880  *      char const  *section,
881  *      char const  *key_name,
882  *      int  def )
883  *
884  *   Reads a boolean value from the wine.ini file.  This function attempts to
885  *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
886  *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
887  *   true.  Anything else results in the return of the default value.
888  *
889  *   This function uses 1 to indicate true, and 0 for false.  You can check
890  *   for existence by setting def to something other than 0 or 1 and
891  *   examining the return value.
892  */
893 int  PROFILE_GetWineIniBool(
894     char const  *section,
895     char const  *key_name,
896     int  def )
897 {
898     char  key_value[2];
899     int  retval;
900
901     PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
902
903     switch(key_value[0]) {
904     case 'n':
905     case 'N':
906     case 'f':
907     case 'F':
908     case '0':
909         retval = 0;
910         break;
911
912     case 'y':
913     case 'Y':
914     case 't':
915     case 'T':
916     case '1':
917         retval = 1;
918         break;
919
920     default:
921         retval = def;
922     }
923
924     TRACE("(\"%s\", \"%s\", %s), "
925                     "[%c], ret %s.\n", section, key_name,
926                     def ? "TRUE" : "FALSE", key_value[0],
927                     retval ? "TRUE" : "FALSE");
928
929     return retval;
930 }
931
932
933 /***********************************************************************
934  *           PROFILE_LoadWineIni
935  *
936  * Load the wine.ini file.
937  */
938 int PROFILE_LoadWineIni(void)
939 {
940     char buffer[MAX_PATHNAME_LEN];
941     const char *p;
942     FILE *f;
943     HKEY hKeySW;
944
945     /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
946     if (RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine", &hKeySW ))
947     {
948         ERR("Cannot create config registry key\n" );
949         return 0;
950     }
951     RegCloseKey( hKeySW );
952     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", 0, NULL,
953                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &wine_profile_key, NULL ))
954     {
955         ERR("Cannot create config registry key\n" );
956         return 0;
957     }
958
959     if (!CLIENT_IsBootThread()) return 1;  /* already loaded */
960
961     if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) )
962     {
963       /* Open -config specified file */
964       lstrcpynA(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN);
965       goto found;
966     }
967
968     if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) )
969     {
970         lstrcpynA(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN);
971         goto found;
972     }
973     if ((p = getenv( "HOME" )) != NULL)
974     {
975         lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
976         strcat( buffer, PROFILE_WineIniName );
977         if ((f = fopen( buffer, "r" )) != NULL)
978         {
979             lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
980             goto found;
981         }
982     }
983     else WARN("could not get $HOME value for config file.\n" );
984
985     /* Try global file */
986
987     if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
988     {
989         lstrcpynA(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN);
990         goto found;
991     }
992     MESSAGE( "Can't open configuration file %s or $HOME%s\n",
993              WINE_INI_GLOBAL, PROFILE_WineIniName );
994     return 0;
995
996  found:
997     PROFILE_RegistryLoad( wine_profile_key, f );
998     fclose( f );
999     return 1;
1000 }
1001
1002
1003 /***********************************************************************
1004  *           PROFILE_UsageWineIni
1005  *
1006  * Explain the wine.ini file to those who don't read documentation.
1007  * Keep below one screenful in length so that error messages above are
1008  * noticed.
1009  */
1010 void PROFILE_UsageWineIni(void)
1011 {
1012     MESSAGE("Perhaps you have not properly edited or created "
1013         "your Wine configuration file.\n");
1014     MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName);
1015     MESSAGE("  or it is determined by the -config option or from\n"
1016         "  the WINE_INI environment variable.\n");
1017     if (*PROFILE_WineIniUsed)
1018         MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed);
1019     /* RTFM, so to say */
1020 }
1021
1022 /***********************************************************************
1023  *           PROFILE_GetStringItem
1024  *
1025  *  Convenience function that turns a string 'xxx, yyy, zzz' into 
1026  *  the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1027  */
1028 char* PROFILE_GetStringItem( char* start )
1029 {
1030     char* lpchX, *lpch;
1031
1032     for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
1033     {
1034         if( *lpchX == ',' )
1035         {
1036             if( lpch ) *lpch = '\0'; else *lpchX = '\0';
1037             while( *(++lpchX) )
1038                 if( !PROFILE_isspace(*lpchX) ) return lpchX;
1039         }
1040         else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
1041              else lpch = NULL;
1042     }
1043     if( lpch ) *lpch = '\0';
1044     return NULL;
1045 }
1046
1047 /********************* API functions **********************************/
1048
1049 /***********************************************************************
1050  *           GetProfileInt16   (KERNEL.57)
1051  */
1052 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1053 {
1054     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1055 }
1056
1057
1058 /***********************************************************************
1059  *           GetProfileIntA   (KERNEL32.264)
1060  */
1061 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1062 {
1063     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1064 }
1065
1066 /***********************************************************************
1067  *           GetProfileIntW   (KERNEL32.264)
1068  */
1069 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1070 {
1071     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1072 }
1073
1074 /***********************************************************************
1075  *           GetProfileString16   (KERNEL.58)
1076  */
1077 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1078                                  LPSTR buffer, UINT16 len )
1079 {
1080     return GetPrivateProfileString16( section, entry, def_val,
1081                                       buffer, len, "win.ini" );
1082 }
1083
1084 /***********************************************************************
1085  *           GetProfileStringA   (KERNEL32.268)
1086  */
1087 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1088                               LPSTR buffer, UINT len )
1089 {
1090     return GetPrivateProfileStringA( section, entry, def_val,
1091                                      buffer, len, "win.ini" );
1092 }
1093
1094 /***********************************************************************
1095  *           GetProfileStringW   (KERNEL32.269)
1096  */
1097 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1098                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1099 {
1100     return GetPrivateProfileStringW( section, entry, def_val,
1101                                      buffer, len, wininiW );
1102 }
1103
1104 /***********************************************************************
1105  *           WriteProfileString16   (KERNEL.59)
1106  */
1107 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1108                                     LPCSTR string )
1109 {
1110     return WritePrivateProfileString16( section, entry, string, "win.ini" );
1111 }
1112
1113 /***********************************************************************
1114  *           WriteProfileStringA   (KERNEL32.587)
1115  */
1116 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1117                                  LPCSTR string )
1118 {
1119     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1120 }
1121
1122 /***********************************************************************
1123  *           WriteProfileStringW   (KERNEL32.588)
1124  */
1125 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1126                                      LPCWSTR string )
1127 {
1128     return WritePrivateProfileStringW( section, entry, string, wininiW );
1129 }
1130
1131
1132 /***********************************************************************
1133  *           GetPrivateProfileInt16   (KERNEL.127)
1134  */
1135 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1136                                       INT16 def_val, LPCSTR filename )
1137 {
1138     long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename);
1139
1140     if (result > 65535) return 65535;
1141     if (result >= 0) return (UINT16)result;
1142     if (result < -32768) return -32768;
1143     return (UINT16)(INT16)result;
1144 }
1145
1146 /***********************************************************************
1147  *           GetPrivateProfileIntA   (KERNEL32.251)
1148  */
1149 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1150                                    INT def_val, LPCSTR filename )
1151 {
1152     char buffer[20];
1153     char *p;
1154     long result;
1155
1156     GetPrivateProfileStringA( section, entry, "",
1157                                 buffer, sizeof(buffer), filename );
1158     if (!buffer[0]) return (UINT)def_val;
1159     result = strtol( buffer, &p, 0 );
1160     if (p == buffer) return 0;  /* No digits at all */
1161     return (UINT)result;
1162 }
1163
1164 /***********************************************************************
1165  *           GetPrivateProfileIntW   (KERNEL32.252)
1166  */
1167 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1168                                    INT def_val, LPCWSTR filename )
1169 {
1170     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1171     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1172     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1173     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1174     HeapFree( GetProcessHeap(), 0, sectionA );
1175     HeapFree( GetProcessHeap(), 0, filenameA );
1176     HeapFree( GetProcessHeap(), 0, entryA );
1177     return res;
1178 }
1179
1180 /***********************************************************************
1181  *           GetPrivateProfileString16   (KERNEL.128)
1182  */
1183 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1184                                         LPCSTR def_val, LPSTR buffer,
1185                                         UINT16 len, LPCSTR filename )
1186 {
1187     return GetPrivateProfileStringA(section,entry,def_val,buffer,len,filename);
1188 }
1189
1190 /***********************************************************************
1191  *           GetPrivateProfileStringA   (KERNEL32.255)
1192  */
1193 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1194                                      LPCSTR def_val, LPSTR buffer,
1195                                      UINT len, LPCSTR filename )
1196 {
1197     int         ret;
1198
1199     if (!filename) 
1200         filename = "win.ini";
1201
1202     EnterCriticalSection( &PROFILE_CritSect );
1203
1204     if (PROFILE_Open( filename )) {
1205         ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1206     } else {
1207        lstrcpynA( buffer, def_val, len );
1208        ret = strlen( buffer );
1209     }
1210
1211     LeaveCriticalSection( &PROFILE_CritSect );
1212     
1213     return ret;
1214 }
1215
1216 /***********************************************************************
1217  *           GetPrivateProfileStringW   (KERNEL32.256)
1218  */
1219 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1220                                      LPCWSTR def_val, LPWSTR buffer,
1221                                      UINT len, LPCWSTR filename )
1222 {
1223     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1224     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1225     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1226     LPSTR def_valA  = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1227     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1228     INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1229                                             bufferA, len, filenameA );
1230     lstrcpynAtoW( buffer, bufferA, len );
1231     HeapFree( GetProcessHeap(), 0, sectionA );
1232     HeapFree( GetProcessHeap(), 0, entryA );
1233     HeapFree( GetProcessHeap(), 0, filenameA );
1234     HeapFree( GetProcessHeap(), 0, def_valA );
1235     HeapFree( GetProcessHeap(), 0, bufferA);
1236     return ret;
1237 }
1238
1239 /***********************************************************************
1240  *           GetPrivateProfileSection16   (KERNEL.418)
1241  */
1242 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1243                                         UINT16 len, LPCSTR filename )
1244 {
1245     return GetPrivateProfileSectionA( section, buffer, len, filename );
1246 }
1247
1248 /***********************************************************************
1249  *           GetPrivateProfileSectionA   (KERNEL32.255)
1250  */
1251 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1252                                       DWORD len, LPCSTR filename )
1253 {
1254     int         ret = 0;
1255
1256     EnterCriticalSection( &PROFILE_CritSect );
1257
1258     if (PROFILE_Open( filename ))
1259         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1260                                  FALSE, TRUE);
1261     
1262     LeaveCriticalSection( &PROFILE_CritSect );
1263
1264     return ret;
1265 }
1266
1267 /***********************************************************************
1268  *           GetPrivateProfileSectionW   (KERNEL32.256)
1269  */
1270
1271 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1272                                       DWORD len, LPCWSTR filename )
1273
1274 {
1275     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1276     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1277     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1278     INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len, 
1279                                                 filenameA );
1280     MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1281     HeapFree( GetProcessHeap(), 0, sectionA );
1282     HeapFree( GetProcessHeap(), 0, filenameA );
1283     HeapFree( GetProcessHeap(), 0, bufferA);
1284     return ret;
1285 }
1286
1287 /***********************************************************************
1288  *           GetProfileSection16   (KERNEL.419)
1289  */
1290 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1291 {
1292     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1293 }
1294
1295 /***********************************************************************
1296  *           GetProfileSectionA   (KERNEL32.268)
1297  */
1298 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1299 {
1300     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1301 }
1302
1303 /***********************************************************************
1304  *           GetProfileSectionW   (KERNEL32)
1305  */
1306 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1307 {
1308     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1309 }
1310
1311
1312 /***********************************************************************
1313  *           WritePrivateProfileString16   (KERNEL.129)
1314  */
1315 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1316                                            LPCSTR string, LPCSTR filename )
1317 {
1318     return WritePrivateProfileStringA(section,entry,string,filename);
1319 }
1320
1321 /***********************************************************************
1322  *           WritePrivateProfileStringA   (KERNEL32.582)
1323  */
1324 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1325                                         LPCSTR string, LPCSTR filename )
1326 {
1327     BOOL ret = FALSE;
1328
1329     EnterCriticalSection( &PROFILE_CritSect );
1330
1331     if (PROFILE_Open( filename ))
1332     {
1333         if (!section && !entry && !string)
1334             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1335         else
1336             ret = PROFILE_SetString( section, entry, string );
1337     }
1338
1339     LeaveCriticalSection( &PROFILE_CritSect );
1340     return ret;
1341 }
1342
1343 /***********************************************************************
1344  *           WritePrivateProfileStringW   (KERNEL32.583)
1345  */
1346 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1347                                         LPCWSTR string, LPCWSTR filename )
1348 {
1349     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1350     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1351     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1352     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1353     BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1354                                            stringA, filenameA );
1355     HeapFree( GetProcessHeap(), 0, sectionA );
1356     HeapFree( GetProcessHeap(), 0, entryA );
1357     HeapFree( GetProcessHeap(), 0, stringA );
1358     HeapFree( GetProcessHeap(), 0, filenameA );
1359     return res;
1360 }
1361
1362 /***********************************************************************
1363  *           WritePrivateProfileSection16   (KERNEL.416)
1364  */
1365 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section, 
1366                                             LPCSTR string, LPCSTR filename )
1367 {
1368     return WritePrivateProfileSectionA( section, string, filename );
1369 }
1370
1371 /***********************************************************************
1372  *           WritePrivateProfileSectionA   (KERNEL32)
1373  */
1374 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section, 
1375                                          LPCSTR string, LPCSTR filename )
1376 {
1377     BOOL ret = FALSE;
1378     LPSTR p ;
1379
1380     EnterCriticalSection( &PROFILE_CritSect );
1381
1382     if (PROFILE_Open( filename )) {
1383         if (!section && !string && !filename)
1384             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1385         else {
1386             while(*string){
1387                 LPSTR buf=HEAP_strdupA( GetProcessHeap(), 0, string );
1388                 if((p=strchr( buf, '='))){
1389                     *p='\0';
1390                     ret = PROFILE_SetString( section, buf, p+1 );
1391                     
1392                 }
1393                 HeapFree( GetProcessHeap(), 0, buf );
1394                 string += strlen(string)+1;
1395             }
1396             
1397         }
1398     }
1399
1400     LeaveCriticalSection( &PROFILE_CritSect );
1401     return ret;
1402 }
1403
1404 /***********************************************************************
1405  *           WritePrivateProfileSectionW   (KERNEL32)
1406  */
1407 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1408                                          LPCWSTR string, LPCWSTR filename)
1409
1410 {
1411     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1412     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1413     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1414     BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1415     HeapFree( GetProcessHeap(), 0, sectionA );
1416     HeapFree( GetProcessHeap(), 0, stringA );
1417     HeapFree( GetProcessHeap(), 0, filenameA );
1418     return res;
1419 }
1420
1421 /***********************************************************************
1422  *           WriteProfileSection16   (KERNEL.417)
1423  */
1424 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1425 {
1426     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1427 }
1428
1429 /***********************************************************************
1430  *           WriteProfileSectionA   (KERNEL32.747)
1431  */
1432 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1433                                      
1434 {
1435     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1436 }
1437
1438 /***********************************************************************
1439  *           WriteProfileSectionW   (KERNEL32.748)
1440  */
1441 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1442 {
1443    return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1444 }
1445
1446 /***********************************************************************
1447  *           GetPrivateProfileSectionNames16   (KERNEL.143)
1448  */
1449 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1450                                              LPCSTR filename )
1451 {
1452     WORD ret = 0;
1453
1454     EnterCriticalSection( &PROFILE_CritSect );
1455
1456     if (PROFILE_Open( filename ))
1457         ret = PROFILE_GetSectionNames(buffer, size);
1458
1459     LeaveCriticalSection( &PROFILE_CritSect );
1460
1461     return ret;
1462 }
1463
1464
1465 /***********************************************************************
1466  *           GetProfileSectionNames16   (KERNEL.142)
1467  */
1468 WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size)
1469
1470 {
1471     return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini"));
1472 }
1473
1474
1475 /***********************************************************************
1476  *           GetPrivateProfileSectionNamesA  (KERNEL32.365)
1477  */
1478 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1479                                              LPCSTR filename)
1480
1481 {
1482  return (GetPrivateProfileSectionNames16 (buffer,size,filename));
1483 }
1484
1485
1486 /***********************************************************************
1487  *           GetPrivateProfileSectionNamesW  (KERNEL32.366)
1488  */
1489 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1490                                              LPCWSTR filename)
1491
1492 {
1493    LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1494    LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size);
1495
1496    INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
1497    lstrcpynAtoW( buffer, bufferA, size);   
1498    HeapFree( GetProcessHeap(), 0, bufferA);
1499    HeapFree( GetProcessHeap(), 0, filenameA );
1500
1501    return ret;
1502 }
1503
1504 /***********************************************************************
1505  *           GetPrivateProfileStruct16 (KERNEL.407)
1506  */
1507 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key, 
1508                                         LPVOID buf, UINT16 len, LPCSTR filename)
1509 {
1510     return GetPrivateProfileStructA( section, key, buf, len, filename );
1511 }
1512
1513 /***********************************************************************
1514  *           GetPrivateProfileStructA (KERNEL32.370)
1515  *
1516  * Should match Win95's behaviour pretty much
1517  */
1518 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key, 
1519                                       LPVOID buf, UINT len, LPCSTR filename)
1520 {
1521     BOOL        ret = FALSE;
1522
1523     EnterCriticalSection( &PROFILE_CritSect );
1524
1525     if (PROFILE_Open( filename )) {
1526         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1527         if (k) {
1528             TRACE("value (at %p): '%s'\n", k->value, k->value);
1529             if (((strlen(k->value) - 2) / 2) == len)
1530             {
1531                 LPSTR end, p;
1532                 BOOL valid = TRUE;
1533                 CHAR c;
1534                 DWORD chksum = 0;
1535
1536                 end  = k->value + strlen(k->value); /* -> '\0' */
1537                 /* check for invalid chars in ASCII coded hex string */
1538                 for (p=k->value; p < end; p++)
1539                 {
1540                     if (!isxdigit(*p))
1541                     {
1542                         WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1543                              *p, filename, section, key);
1544                         valid = FALSE;
1545                         break;
1546                     }
1547                 }
1548                 if (valid)
1549                 {
1550                     BOOL highnibble = TRUE;
1551                     BYTE b = 0, val;
1552                     LPBYTE binbuf = (LPBYTE)buf;
1553                     
1554                     end -= 2; /* don't include checksum in output data */
1555                     /* translate ASCII hex format into binary data */
1556                     for (p=k->value; p < end; p++)
1557                     {
1558                         c = toupper(*p);
1559                         val = (c > '9') ?
1560                                 (c - 'A' + 10) : (c - '0');
1561
1562                         if (highnibble)
1563                             b = val << 4;
1564                         else
1565                         {
1566                             b += val;
1567                             *binbuf++ = b; /* feed binary data into output */
1568                             chksum += b; /* calculate checksum */
1569                         }
1570                         highnibble ^= 1; /* toggle */
1571                     }
1572                     /* retrieve stored checksum value */
1573                     c = toupper(*p++);
1574                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1575                     c = toupper(*p);
1576                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1577                     if (b == (chksum & 0xff)) /* checksums match ? */
1578                         ret = TRUE;
1579                 }
1580             }
1581         }
1582     }
1583     LeaveCriticalSection( &PROFILE_CritSect );
1584
1585     return ret;
1586 }
1587
1588 /***********************************************************************
1589  *           GetPrivateProfileStructW (KERNEL32.543)
1590  */
1591 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1592                                       LPVOID buffer, UINT len, LPCWSTR filename)
1593 {
1594     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1595     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1596     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1597     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1598
1599     INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1600                                         len, filenameA );
1601     lstrcpynAtoW( buffer, bufferA, len );
1602     HeapFree( GetProcessHeap(), 0, bufferA);
1603     HeapFree( GetProcessHeap(), 0, sectionA );
1604     HeapFree( GetProcessHeap(), 0, keyA );
1605     HeapFree( GetProcessHeap(), 0, filenameA );
1606
1607     return ret;
1608 }
1609
1610
1611
1612 /***********************************************************************
1613  *           WritePrivateProfileStruct16 (KERNEL.406)
1614  */
1615 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key, 
1616         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1617 {
1618     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1619 }
1620
1621 /***********************************************************************
1622  *           WritePrivateProfileStructA (KERNEL32.744)
1623  */
1624 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key, 
1625                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1626 {
1627     BOOL ret = FALSE;
1628     LPBYTE binbuf;
1629     LPSTR outstring, p;
1630     DWORD sum = 0;
1631
1632     if (!section && !key && !buf)  /* flush the cache */
1633         return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1634
1635     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1636     outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1);
1637     p = outstring;
1638     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1639       *p++ = hex[*binbuf >> 4];
1640       *p++ = hex[*binbuf & 0xf];
1641       sum += *binbuf;
1642     }
1643     /* checksum is sum & 0xff */
1644     *p++ = hex[(sum & 0xf0) >> 4];
1645     *p++ = hex[sum & 0xf];
1646     *p++ = '\0';
1647
1648     EnterCriticalSection( &PROFILE_CritSect );
1649
1650     if (PROFILE_Open( filename )) 
1651         ret = PROFILE_SetString( section, key, outstring );
1652
1653     LeaveCriticalSection( &PROFILE_CritSect );
1654
1655     HeapFree( GetProcessHeap(), 0, outstring );
1656
1657     return ret;
1658 }
1659
1660 /***********************************************************************
1661  *           WritePrivateProfileStructW (KERNEL32.544)
1662  */
1663 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1664                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1665 {
1666     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1667     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1668     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1669     INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1670                                           filenameA );
1671     HeapFree( GetProcessHeap(), 0, sectionA );
1672     HeapFree( GetProcessHeap(), 0, keyA );
1673     HeapFree( GetProcessHeap(), 0, filenameA );
1674
1675     return ret;
1676 }
1677
1678
1679 /***********************************************************************
1680  *           WriteOutProfiles   (KERNEL.315)
1681  */
1682 void WINAPI WriteOutProfiles16(void)
1683 {
1684     EnterCriticalSection( &PROFILE_CritSect );
1685     PROFILE_FlushFile();
1686     LeaveCriticalSection( &PROFILE_CritSect );
1687 }
1688
1689 /***********************************************************************
1690  *           CloseProfileUserMapping   (KERNEL.138)
1691  */
1692 BOOL WINAPI CloseProfileUserMapping(void) {
1693     FIXME("(), stub!\n");
1694     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1695     return FALSE;
1696 }