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