Merged wing.dll into gdi32.dll.
[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_DeleteAllKeys
397  *
398  * Delete all keys from a profile tree.
399  */
400 void PROFILE_DeleteAllKeys( LPCSTR section_name)
401 {
402     PROFILESECTION **section= &CurProfile->section;
403     while (*section)
404     {
405         if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
406         {
407             PROFILEKEY **key = &(*section)->key;
408             while (*key)
409             {
410                 PROFILEKEY *to_del = *key;
411                 *key = to_del->next;
412                 if (to_del->name) HeapFree( GetProcessHeap(), 0, to_del->name );
413                 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
414                 HeapFree( GetProcessHeap(), 0, to_del );
415                 CurProfile->changed =TRUE;
416             }
417         }
418         section = &(*section)->next;
419     }
420 }
421
422
423 /***********************************************************************
424  *           PROFILE_Find
425  *
426  * Find a key in a profile tree, optionally creating it.
427  */
428 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
429                                  const char *section_name,
430                                  const char *key_name, int create )
431 {
432     const char *p;
433     int seclen, keylen;
434
435     while (PROFILE_isspace(*section_name)) section_name++;
436     p = section_name + strlen(section_name) - 1;
437     while ((p > section_name) && PROFILE_isspace(*p)) p--;
438     seclen = p - section_name + 1;
439     
440     while (PROFILE_isspace(*key_name)) key_name++;
441     p = key_name + strlen(key_name) - 1;
442     while ((p > key_name) && PROFILE_isspace(*p)) p--;
443     keylen = p - key_name + 1;
444
445     while (*section)
446     {
447         if ( ((*section)->name)
448           && (!(strncasecmp( (*section)->name, section_name, seclen )))
449           && (((*section)->name)[seclen] == '\0') )
450         {
451             PROFILEKEY **key = &(*section)->key;
452             while (*key)
453             {
454                 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
455                   && (((*key)->name)[keylen] == '\0') )
456                     return *key;
457                 key = &(*key)->next;
458             }
459             if (!create) return NULL;
460             *key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
461             if(*key == NULL) return NULL;
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 = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) );
471     if(*section == NULL) return NULL;
472     (*section)->name = HEAP_strdupA( GetProcessHeap(), 0, section_name );
473     (*section)->next = NULL;
474     (*section)->key  = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
475     if((*section)->key  == NULL)
476     {
477             HeapFree(GetProcessHeap(), 0, *section);
478             return NULL;
479     }
480     (*section)->key->name  = HEAP_strdupA( GetProcessHeap(), 0, key_name );
481     (*section)->key->value = NULL;
482     (*section)->key->next  = NULL;
483     return (*section)->key;
484 }
485
486
487 /***********************************************************************
488  *           PROFILE_FlushFile
489  *
490  * Flush the current profile to disk if changed.
491  */
492 static BOOL PROFILE_FlushFile(void)
493 {
494     char *p, buffer[MAX_PATHNAME_LEN];
495     const char *unix_name;
496     FILE *file = NULL;
497     struct stat buf;
498
499     if(!CurProfile)
500     {
501         WARN("No current profile!\n");
502         return FALSE;
503     }
504
505     if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
506     if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
507     {
508         /* Try to create it in $HOME/.wine */
509         /* FIXME: this will need a more general solution */
510         strcpy( buffer, get_config_dir() );
511         p = buffer + strlen(buffer);
512         *p++ = '/';
513         strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
514         CharLowerA( p );
515         file = fopen( buffer, "w" );
516         unix_name = buffer;
517     }
518     
519     if (!file)
520     {
521         WARN("could not save profile file %s\n", CurProfile->dos_name);
522         return FALSE;
523     }
524
525     TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
526     PROFILE_Save( file, CurProfile->section );
527     fclose( file );
528     CurProfile->changed = FALSE;
529     if(!stat(unix_name,&buf))
530        CurProfile->mtime=buf.st_mtime;
531     return TRUE;
532 }
533
534
535 /***********************************************************************
536  *           PROFILE_ReleaseFile
537  *
538  * Flush the current profile to disk and remove it from the cache.
539  */
540 static void PROFILE_ReleaseFile(void)
541 {
542     PROFILE_FlushFile();
543     PROFILE_Free( CurProfile->section );
544     if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
545     if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
546     if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
547     CurProfile->changed   = FALSE;
548     CurProfile->section   = NULL;
549     CurProfile->dos_name  = NULL;
550     CurProfile->unix_name = NULL;
551     CurProfile->filename  = NULL;
552     CurProfile->mtime     = 0;
553 }
554
555
556 /***********************************************************************
557  *           PROFILE_Open
558  *
559  * Open a profile file, checking the cached file first.
560  */
561 static BOOL PROFILE_Open( LPCSTR filename )
562 {
563     DOS_FULL_NAME full_name;
564     char buffer[MAX_PATHNAME_LEN];
565     char *newdos_name, *p;
566     FILE *file = NULL;
567     int i,j;
568     struct stat buf;
569     PROFILE *tempProfile;
570
571     /* First time around */
572
573     if(!CurProfile)
574        for(i=0;i<N_CACHED_PROFILES;i++)
575          {
576           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
577           if(MRUProfile[i] == NULL) break;
578           MRUProfile[i]->changed=FALSE;
579           MRUProfile[i]->section=NULL;
580           MRUProfile[i]->dos_name=NULL;
581           MRUProfile[i]->unix_name=NULL;
582           MRUProfile[i]->filename=NULL;
583           MRUProfile[i]->mtime=0;
584          }
585
586     /* Check for a match */
587
588     if (strchr( filename, '/' ) || strchr( filename, '\\' ) || 
589         strchr( filename, ':' ))
590     {
591         if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
592     }
593     else
594     {
595         GetWindowsDirectoryA( buffer, sizeof(buffer) );
596         strcat( buffer, "\\" );
597         strcat( buffer, filename );
598         if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
599     }
600
601     for(i=0;i<N_CACHED_PROFILES;i++)
602       {
603        if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
604            (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
605          {
606           if(i)
607             {
608              PROFILE_FlushFile();
609              tempProfile=MRUProfile[i];
610              for(j=i;j>0;j--)
611                 MRUProfile[j]=MRUProfile[j-1];
612              CurProfile=tempProfile;
613             }
614           if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
615              TRACE("(%s): already opened (mru=%d)\n",
616                               filename, i );
617           else
618               TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
619                              filename, i );
620           return TRUE;
621          }
622       }
623
624     /* Flush the old current profile */
625     PROFILE_FlushFile();
626
627     /* Make the oldest profile the current one only in order to get rid of it */
628     if(i==N_CACHED_PROFILES)
629       {
630        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
631        for(i=N_CACHED_PROFILES-1;i>0;i--)
632           MRUProfile[i]=MRUProfile[i-1];
633        CurProfile=tempProfile;
634       }
635     if(CurProfile->filename) PROFILE_ReleaseFile();
636
637     /* OK, now that CurProfile is definitely free we assign it our new file */
638     newdos_name = HEAP_strdupA( GetProcessHeap(), 0, full_name.short_name );
639     CurProfile->dos_name  = newdos_name;
640     CurProfile->filename  = HEAP_strdupA( GetProcessHeap(), 0, filename );
641
642     /* Try to open the profile file, first in $HOME/.wine */
643
644     /* FIXME: this will need a more general solution */
645     strcpy( buffer, get_config_dir() );
646     p = buffer + strlen(buffer);
647     *p++ = '/';
648     strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
649     CharLowerA( p );
650     if ((file = fopen( buffer, "r" )))
651     {
652         TRACE("(%s): found it in %s\n",
653               filename, buffer );
654         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0, buffer );
655     }
656
657     if (!file)
658     {
659         CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0,
660                                              full_name.long_name );
661         if ((file = fopen( full_name.long_name, "r" )))
662             TRACE("(%s): found it in %s\n",
663                              filename, full_name.long_name );
664     }
665
666     if (file)
667     {
668         CurProfile->section = PROFILE_Load( file );
669         fclose( file );
670         if(!stat(CurProfile->unix_name,&buf))
671            CurProfile->mtime=buf.st_mtime;
672     }
673     else
674     {
675         /* Does not exist yet, we will create it in PROFILE_FlushFile */
676         WARN("profile file %s not found\n", newdos_name );
677     }
678     return TRUE;
679 }
680
681
682 /***********************************************************************
683  *           PROFILE_GetSection
684  *
685  * Returns all keys of a section.
686  * If return_values is TRUE, also include the corresponding values.
687  */
688 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
689                                LPSTR buffer, UINT len, BOOL handle_env,
690                                BOOL return_values )
691 {
692     PROFILEKEY *key;
693     while (section)
694     {
695         if (section->name && !strcasecmp( section->name, section_name ))
696         {
697             UINT oldlen = len;
698             for (key = section->key; key; key = key->next)
699             {
700                 if (len <= 2) break;
701                 if (!*key->name) continue;  /* Skip empty lines */
702                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
703                 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
704                 len -= strlen(buffer) + 1;
705                 buffer += strlen(buffer) + 1;
706                 if (return_values && key->value) {
707                         buffer[-1] = '=';
708                         PROFILE_CopyEntry ( buffer,
709                                 key->value, len - 1, handle_env );
710                         len -= strlen(buffer) + 1;
711                         buffer += strlen(buffer) + 1;
712                 }
713             }
714             *buffer = '\0';
715             if (len <= 1)
716                 /*If either lpszSection or lpszKey is NULL and the supplied
717                   destination buffer is too small to hold all the strings, 
718                   the last string is truncated and followed by two null characters.
719                   In this case, the return value is equal to cchReturnBuffer
720                   minus two. */
721             {
722                 buffer[-1] = '\0';
723                 return oldlen - 2;
724             }
725             return oldlen - len;
726         }
727         section = section->next;
728     }
729     buffer[0] = buffer[1] = '\0';
730     return 0;
731 }
732
733
734 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
735 {
736     LPSTR buf = buffer;
737     WORD l, cursize = 0;
738     PROFILESECTION *section;
739
740     for (section = CurProfile->section; section; section = section->next)
741         if (section->name) {
742             l = strlen(section->name);
743             cursize += l+1;
744             if (cursize > len+1)
745                 return len-2;
746
747             strcpy(buf, section->name);
748             buf += l+1;
749         }
750
751     *buf=0;
752     buf++;
753     return buf-buffer;
754 }
755
756
757 /***********************************************************************
758  *           PROFILE_GetString
759  *
760  * Get a profile string.
761  */
762 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
763                               LPCSTR def_val, LPSTR buffer, UINT len )
764 {
765     PROFILEKEY *key = NULL;
766
767     if (!def_val) def_val = "";
768     if (key_name && key_name[0])
769     {
770         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
771         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
772                            len, FALSE );
773         TRACE("('%s','%s','%s'): returning '%s'\n",
774                          section, key_name, def_val, buffer );
775         return strlen( buffer );
776     }
777     if (key_name && !(key_name[0]))
778       /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227*/
779       return 0;
780     if (section && section[0])
781         return PROFILE_GetSection(CurProfile->section, section, buffer, len,
782                                 FALSE, FALSE);
783     /* undocumented; both section and key_name are NULL */
784     return PROFILE_GetSectionNames(buffer, len);
785 }
786
787
788 /***********************************************************************
789  *           PROFILE_SetString
790  *
791  * Set a profile string.
792  */
793 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
794                                LPCSTR value )
795 {
796     if (!key_name)  /* Delete a whole section */
797     {
798         TRACE("('%s')\n", section_name);
799         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
800                                                       section_name );
801         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
802                                 this is not an error on application's level.*/
803     }
804     else if (!value)  /* Delete a key */
805     {
806         TRACE("('%s','%s')\n",
807                          section_name, key_name );
808         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
809                                                   section_name, key_name );
810         return TRUE;          /* same error handling as above */
811     }
812     else  /* Set the key value */
813     {
814         PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
815                                         key_name, TRUE );
816         TRACE("('%s','%s','%s'): \n",
817                          section_name, key_name, value );
818         if (!key) return FALSE;
819         if (key->value)
820         {
821             /* strip the leading spaces. We can safely strip \n\r and 
822              * friends too, they should not happen here anyway. */
823             while (PROFILE_isspace(*value)) value++;
824
825             if (!strcmp( key->value, value ))
826             {
827                 TRACE("  no change needed\n" );
828                 return TRUE;  /* No change needed */
829             }
830             TRACE("  replacing '%s'\n", key->value );
831             HeapFree( GetProcessHeap(), 0, key->value );
832         }
833         else TRACE("  creating key\n" );
834         key->value = HEAP_strdupA( GetProcessHeap(), 0, value );
835         CurProfile->changed = TRUE;
836     }
837     return TRUE;
838 }
839
840
841 /***********************************************************************
842  *           PROFILE_GetWineIniString
843  *
844  * Get a config string from the wine.ini file.
845  */
846 int PROFILE_GetWineIniString( const char *section, const char *key_name,
847                               const char *def, char *buffer, int len )
848 {
849     char tmp[PROFILE_MAX_LINE_LEN];
850     HKEY hkey;
851     DWORD err;
852
853     if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey )))
854     {
855         DWORD type;
856         DWORD count = sizeof(tmp);
857         err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count );
858         RegCloseKey( hkey );
859     }
860     PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE );
861     TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer );
862     return strlen(buffer);
863 }
864
865
866 /***********************************************************************
867  *           PROFILE_EnumWineIniString
868  *
869  * Get a config string from the wine.ini file.
870  */
871 BOOL PROFILE_EnumWineIniString( const char *section, int index,
872                                 char *name, int name_len, char *buffer, int len )
873 {
874     char tmp[PROFILE_MAX_LINE_LEN];
875     HKEY hkey;
876     DWORD err, type;
877     DWORD count = sizeof(tmp);
878
879     if (RegOpenKeyA( wine_profile_key, section, &hkey )) return FALSE;
880     err = RegEnumValueA( hkey, index, name, (DWORD*)&name_len, NULL, &type, tmp, &count );
881     RegCloseKey( hkey );
882     if (!err)
883     {
884         PROFILE_CopyEntry( buffer, tmp, len, TRUE );
885         TRACE( "('%s',%d): returning '%s'='%s'\n", section, index, name, buffer );
886     }
887     return !err;
888 }
889
890
891 /***********************************************************************
892  *           PROFILE_GetWineIniInt
893  *
894  * Get a config integer from the wine.ini file.
895  */
896 int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
897 {
898     char buffer[20];
899     char *p;
900     long result;
901
902     PROFILE_GetWineIniString( section, key_name, "", buffer, sizeof(buffer) );
903     if (!buffer[0]) return def;
904     result = strtol( buffer, &p, 0 );
905     return (p == buffer) ? 0  /* No digits at all */ : (int)result;
906 }
907
908
909 /******************************************************************************
910  *
911  *   int  PROFILE_GetWineIniBool(
912  *      char const  *section,
913  *      char const  *key_name,
914  *      int  def )
915  *
916  *   Reads a boolean value from the wine.ini file.  This function attempts to
917  *   be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
918  *   (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
919  *   true.  Anything else results in the return of the default value.
920  *
921  *   This function uses 1 to indicate true, and 0 for false.  You can check
922  *   for existence by setting def to something other than 0 or 1 and
923  *   examining the return value.
924  */
925 int  PROFILE_GetWineIniBool(
926     char const  *section,
927     char const  *key_name,
928     int  def )
929 {
930     char  key_value[2];
931     int  retval;
932
933     PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
934
935     switch(key_value[0]) {
936     case 'n':
937     case 'N':
938     case 'f':
939     case 'F':
940     case '0':
941         retval = 0;
942         break;
943
944     case 'y':
945     case 'Y':
946     case 't':
947     case 'T':
948     case '1':
949         retval = 1;
950         break;
951
952     default:
953         retval = def;
954     }
955
956     TRACE("(\"%s\", \"%s\", %s), "
957                     "[%c], ret %s.\n", section, key_name,
958                     def ? "TRUE" : "FALSE", key_value[0],
959                     retval ? "TRUE" : "FALSE");
960
961     return retval;
962 }
963
964
965 /***********************************************************************
966  *           PROFILE_LoadWineIni
967  *
968  * Load the wine.ini file.
969  */
970 int PROFILE_LoadWineIni(void)
971 {
972     char buffer[MAX_PATHNAME_LEN];
973     const char *p;
974     FILE *f;
975     HKEY hKeySW;
976
977     /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
978     if (RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine", &hKeySW ))
979     {
980         ERR("Cannot create config registry key\n" );
981         return 0;
982     }
983     RegCloseKey( hKeySW );
984     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", 0, NULL,
985                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &wine_profile_key, NULL ))
986     {
987         ERR("Cannot create config registry key\n" );
988         return 0;
989     }
990
991     if (!CLIENT_IsBootThread()) return 1;  /* already loaded */
992
993     if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) )
994     {
995       /* Open -config specified file */
996       lstrcpynA(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN);
997       goto found;
998     }
999
1000     if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) )
1001     {
1002         lstrcpynA(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN);
1003         goto found;
1004     }
1005     if ((p = getenv( "HOME" )) != NULL)
1006     {
1007         lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
1008         strcat( buffer, PROFILE_WineIniName );
1009         if ((f = fopen( buffer, "r" )) != NULL)
1010         {
1011             lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
1012             goto found;
1013         }
1014     }
1015     else WARN("could not get $HOME value for config file.\n" );
1016
1017     /* Try global file */
1018
1019     if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
1020     {
1021         lstrcpynA(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN);
1022         goto found;
1023     }
1024     MESSAGE( "Can't open configuration file %s or $HOME%s\n",
1025              WINE_INI_GLOBAL, PROFILE_WineIniName );
1026     return 0;
1027
1028  found:
1029     PROFILE_RegistryLoad( wine_profile_key, f );
1030     fclose( f );
1031     return 1;
1032 }
1033
1034
1035 /***********************************************************************
1036  *           PROFILE_UsageWineIni
1037  *
1038  * Explain the wine.ini file to those who don't read documentation.
1039  * Keep below one screenful in length so that error messages above are
1040  * noticed.
1041  */
1042 void PROFILE_UsageWineIni(void)
1043 {
1044     MESSAGE("Perhaps you have not properly edited or created "
1045         "your Wine configuration file.\n");
1046     MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName);
1047     MESSAGE("  or it is determined by the -config option or from\n"
1048         "  the WINE_INI environment variable.\n");
1049     if (*PROFILE_WineIniUsed)
1050         MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed);
1051     /* RTFM, so to say */
1052 }
1053
1054 /***********************************************************************
1055  *           PROFILE_GetStringItem
1056  *
1057  *  Convenience function that turns a string 'xxx, yyy, zzz' into 
1058  *  the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1059  */
1060 char* PROFILE_GetStringItem( char* start )
1061 {
1062     char* lpchX, *lpch;
1063
1064     for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
1065     {
1066         if( *lpchX == ',' )
1067         {
1068             if( lpch ) *lpch = '\0'; else *lpchX = '\0';
1069             while( *(++lpchX) )
1070                 if( !PROFILE_isspace(*lpchX) ) return lpchX;
1071         }
1072         else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
1073              else lpch = NULL;
1074     }
1075     if( lpch ) *lpch = '\0';
1076     return NULL;
1077 }
1078
1079 /********************* API functions **********************************/
1080
1081 /***********************************************************************
1082  *           GetProfileInt16   (KERNEL.57)
1083  */
1084 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1085 {
1086     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1087 }
1088
1089
1090 /***********************************************************************
1091  *           GetProfileIntA   (KERNEL32.264)
1092  */
1093 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1094 {
1095     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1096 }
1097
1098 /***********************************************************************
1099  *           GetProfileIntW   (KERNEL32.264)
1100  */
1101 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1102 {
1103     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1104 }
1105
1106 /***********************************************************************
1107  *           GetProfileString16   (KERNEL.58)
1108  */
1109 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1110                                  LPSTR buffer, UINT16 len )
1111 {
1112     return GetPrivateProfileString16( section, entry, def_val,
1113                                       buffer, len, "win.ini" );
1114 }
1115
1116 /***********************************************************************
1117  *           GetProfileStringA   (KERNEL32.268)
1118  */
1119 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1120                               LPSTR buffer, UINT len )
1121 {
1122     return GetPrivateProfileStringA( section, entry, def_val,
1123                                      buffer, len, "win.ini" );
1124 }
1125
1126 /***********************************************************************
1127  *           GetProfileStringW   (KERNEL32.269)
1128  */
1129 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1130                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1131 {
1132     return GetPrivateProfileStringW( section, entry, def_val,
1133                                      buffer, len, wininiW );
1134 }
1135
1136 /***********************************************************************
1137  *           WriteProfileString16   (KERNEL.59)
1138  */
1139 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1140                                     LPCSTR string )
1141 {
1142     return WritePrivateProfileString16( section, entry, string, "win.ini" );
1143 }
1144
1145 /***********************************************************************
1146  *           WriteProfileStringA   (KERNEL32.587)
1147  */
1148 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1149                                  LPCSTR string )
1150 {
1151     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1152 }
1153
1154 /***********************************************************************
1155  *           WriteProfileStringW   (KERNEL32.588)
1156  */
1157 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1158                                      LPCWSTR string )
1159 {
1160     return WritePrivateProfileStringW( section, entry, string, wininiW );
1161 }
1162
1163
1164 /***********************************************************************
1165  *           GetPrivateProfileInt16   (KERNEL.127)
1166  */
1167 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1168                                       INT16 def_val, LPCSTR filename )
1169 {
1170     long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename);
1171
1172     if (result > 65535) return 65535;
1173     if (result >= 0) return (UINT16)result;
1174     if (result < -32768) return -32768;
1175     return (UINT16)(INT16)result;
1176 }
1177
1178 /***********************************************************************
1179  *           GetPrivateProfileIntA   (KERNEL32.251)
1180  */
1181 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1182                                    INT def_val, LPCSTR filename )
1183 {
1184     char buffer[20];
1185     char *p;
1186     long result;
1187
1188     GetPrivateProfileStringA( section, entry, "",
1189                                 buffer, sizeof(buffer), filename );
1190     if (!buffer[0]) return (UINT)def_val;
1191     result = strtol( buffer, &p, 0 );
1192     if (p == buffer) return 0;  /* No digits at all */
1193     return (UINT)result;
1194 }
1195
1196 /***********************************************************************
1197  *           GetPrivateProfileIntW   (KERNEL32.252)
1198  */
1199 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1200                                    INT def_val, LPCWSTR filename )
1201 {
1202     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1203     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1204     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1205     UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1206     HeapFree( GetProcessHeap(), 0, sectionA );
1207     HeapFree( GetProcessHeap(), 0, filenameA );
1208     HeapFree( GetProcessHeap(), 0, entryA );
1209     return res;
1210 }
1211
1212 /***********************************************************************
1213  *           GetPrivateProfileString16   (KERNEL.128)
1214  */
1215 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1216                                         LPCSTR def_val, LPSTR buffer,
1217                                         UINT16 len, LPCSTR filename )
1218 {
1219     return GetPrivateProfileStringA(section,entry,def_val,buffer,len,filename);
1220 }
1221
1222 /***********************************************************************
1223  *           GetPrivateProfileStringA   (KERNEL32.255)
1224  */
1225 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1226                                      LPCSTR def_val, LPSTR buffer,
1227                                      UINT len, LPCSTR filename )
1228 {
1229     int         ret;
1230
1231     if (!filename) 
1232         filename = "win.ini";
1233
1234     EnterCriticalSection( &PROFILE_CritSect );
1235
1236     if (PROFILE_Open( filename )) {
1237         ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1238     } else {
1239        lstrcpynA( buffer, def_val, len );
1240        ret = strlen( buffer );
1241     }
1242
1243     LeaveCriticalSection( &PROFILE_CritSect );
1244     
1245     return ret;
1246 }
1247
1248 /***********************************************************************
1249  *           GetPrivateProfileStringW   (KERNEL32.256)
1250  */
1251 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1252                                      LPCWSTR def_val, LPWSTR buffer,
1253                                      UINT len, LPCWSTR filename )
1254 {
1255     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1256     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1257     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1258     LPSTR def_valA  = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1259     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1260     INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1261                                             bufferA, len, filenameA );
1262     lstrcpynAtoW( buffer, bufferA, len );
1263     HeapFree( GetProcessHeap(), 0, sectionA );
1264     HeapFree( GetProcessHeap(), 0, entryA );
1265     HeapFree( GetProcessHeap(), 0, filenameA );
1266     HeapFree( GetProcessHeap(), 0, def_valA );
1267     HeapFree( GetProcessHeap(), 0, bufferA);
1268     return ret;
1269 }
1270
1271 /***********************************************************************
1272  *           GetPrivateProfileSection16   (KERNEL.418)
1273  */
1274 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1275                                         UINT16 len, LPCSTR filename )
1276 {
1277     return GetPrivateProfileSectionA( section, buffer, len, filename );
1278 }
1279
1280 /***********************************************************************
1281  *           GetPrivateProfileSectionA   (KERNEL32.255)
1282  */
1283 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1284                                       DWORD len, LPCSTR filename )
1285 {
1286     int         ret = 0;
1287
1288     EnterCriticalSection( &PROFILE_CritSect );
1289
1290     if (PROFILE_Open( filename ))
1291         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1292                                  FALSE, TRUE);
1293     
1294     LeaveCriticalSection( &PROFILE_CritSect );
1295
1296     return ret;
1297 }
1298
1299 /***********************************************************************
1300  *           GetPrivateProfileSectionW   (KERNEL32.256)
1301  */
1302
1303 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1304                                       DWORD len, LPCWSTR filename )
1305
1306 {
1307     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1308     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1309     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1310     INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len, 
1311                                                 filenameA );
1312     MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1313     HeapFree( GetProcessHeap(), 0, sectionA );
1314     HeapFree( GetProcessHeap(), 0, filenameA );
1315     HeapFree( GetProcessHeap(), 0, bufferA);
1316     return ret;
1317 }
1318
1319 /***********************************************************************
1320  *           GetProfileSection16   (KERNEL.419)
1321  */
1322 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1323 {
1324     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1325 }
1326
1327 /***********************************************************************
1328  *           GetProfileSectionA   (KERNEL32.268)
1329  */
1330 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1331 {
1332     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1333 }
1334
1335 /***********************************************************************
1336  *           GetProfileSectionW   (KERNEL32)
1337  */
1338 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1339 {
1340     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1341 }
1342
1343
1344 /***********************************************************************
1345  *           WritePrivateProfileString16   (KERNEL.129)
1346  */
1347 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1348                                            LPCSTR string, LPCSTR filename )
1349 {
1350     return WritePrivateProfileStringA(section,entry,string,filename);
1351 }
1352
1353 /***********************************************************************
1354  *           WritePrivateProfileStringA   (KERNEL32.582)
1355  */
1356 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1357                                         LPCSTR string, LPCSTR filename )
1358 {
1359     BOOL ret = FALSE;
1360
1361     EnterCriticalSection( &PROFILE_CritSect );
1362
1363     if (PROFILE_Open( filename ))
1364     {
1365         if (!section && !entry && !string)
1366             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1367         else
1368             ret = PROFILE_SetString( section, entry, string );
1369     }
1370
1371     LeaveCriticalSection( &PROFILE_CritSect );
1372     return ret;
1373 }
1374
1375 /***********************************************************************
1376  *           WritePrivateProfileStringW   (KERNEL32.583)
1377  */
1378 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1379                                         LPCWSTR string, LPCWSTR filename )
1380 {
1381     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1382     LPSTR entryA    = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1383     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1384     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1385     BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1386                                            stringA, filenameA );
1387     HeapFree( GetProcessHeap(), 0, sectionA );
1388     HeapFree( GetProcessHeap(), 0, entryA );
1389     HeapFree( GetProcessHeap(), 0, stringA );
1390     HeapFree( GetProcessHeap(), 0, filenameA );
1391     return res;
1392 }
1393
1394 /***********************************************************************
1395  *           WritePrivateProfileSection16   (KERNEL.416)
1396  */
1397 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section, 
1398                                             LPCSTR string, LPCSTR filename )
1399 {
1400     return WritePrivateProfileSectionA( section, string, filename );
1401 }
1402
1403 /***********************************************************************
1404  *           WritePrivateProfileSectionA   (KERNEL32)
1405  */
1406 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section, 
1407                                          LPCSTR string, LPCSTR filename )
1408 {
1409     BOOL ret = FALSE;
1410     LPSTR p ;
1411
1412     EnterCriticalSection( &PROFILE_CritSect );
1413
1414     if (PROFILE_Open( filename )) {
1415         if (!section && !string && !filename)
1416             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1417         else if (!string) /* delete the named section*/
1418             ret = PROFILE_SetString(section,NULL,NULL);
1419         else {
1420             PROFILE_DeleteAllKeys(section);
1421             ret = TRUE;
1422             while(*string) {
1423                 LPSTR buf=HEAP_strdupA( GetProcessHeap(), 0, string );
1424                 if((p=strchr( buf, '='))){
1425                     *p='\0';
1426                     ret = PROFILE_SetString( section, buf, p+1 );
1427                     
1428                 }
1429                 HeapFree( GetProcessHeap(), 0, buf );
1430                 string += strlen(string)+1;
1431             }
1432             
1433         }
1434     }
1435
1436     LeaveCriticalSection( &PROFILE_CritSect );
1437     return ret;
1438 }
1439
1440 /***********************************************************************
1441  *           WritePrivateProfileSectionW   (KERNEL32)
1442  */
1443 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1444                                          LPCWSTR string, LPCWSTR filename)
1445
1446 {
1447     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1448     LPSTR stringA   = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1449     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1450     BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1451     HeapFree( GetProcessHeap(), 0, sectionA );
1452     HeapFree( GetProcessHeap(), 0, stringA );
1453     HeapFree( GetProcessHeap(), 0, filenameA );
1454     return res;
1455 }
1456
1457 /***********************************************************************
1458  *           WriteProfileSection16   (KERNEL.417)
1459  */
1460 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1461 {
1462     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1463 }
1464
1465 /***********************************************************************
1466  *           WriteProfileSectionA   (KERNEL32.747)
1467  */
1468 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1469                                      
1470 {
1471     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1472 }
1473
1474 /***********************************************************************
1475  *           WriteProfileSectionW   (KERNEL32.748)
1476  */
1477 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1478 {
1479    return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1480 }
1481
1482 /***********************************************************************
1483  *           GetPrivateProfileSectionNames16   (KERNEL.143)
1484  */
1485 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1486                                              LPCSTR filename )
1487 {
1488     WORD ret = 0;
1489
1490     EnterCriticalSection( &PROFILE_CritSect );
1491
1492     if (PROFILE_Open( filename ))
1493         ret = PROFILE_GetSectionNames(buffer, size);
1494
1495     LeaveCriticalSection( &PROFILE_CritSect );
1496
1497     return ret;
1498 }
1499
1500
1501 /***********************************************************************
1502  *           GetProfileSectionNames16   (KERNEL.142)
1503  */
1504 WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size)
1505
1506 {
1507     return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini"));
1508 }
1509
1510
1511 /***********************************************************************
1512  *           GetPrivateProfileSectionNamesA  (KERNEL32.365)
1513  */
1514 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1515                                              LPCSTR filename)
1516
1517 {
1518  return (GetPrivateProfileSectionNames16 (buffer,size,filename));
1519 }
1520
1521
1522 /***********************************************************************
1523  *           GetPrivateProfileSectionNamesW  (KERNEL32.366)
1524  */
1525 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1526                                              LPCWSTR filename)
1527
1528 {
1529    LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1530    LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, size);
1531
1532    INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
1533    lstrcpynAtoW( buffer, bufferA, size);   
1534    HeapFree( GetProcessHeap(), 0, bufferA);
1535    HeapFree( GetProcessHeap(), 0, filenameA );
1536
1537    return ret;
1538 }
1539
1540 /***********************************************************************
1541  *           GetPrivateProfileStruct16 (KERNEL.407)
1542  */
1543 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key, 
1544                                         LPVOID buf, UINT16 len, LPCSTR filename)
1545 {
1546     return GetPrivateProfileStructA( section, key, buf, len, filename );
1547 }
1548
1549 /***********************************************************************
1550  *           GetPrivateProfileStructA (KERNEL32.370)
1551  *
1552  * Should match Win95's behaviour pretty much
1553  */
1554 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key, 
1555                                       LPVOID buf, UINT len, LPCSTR filename)
1556 {
1557     BOOL        ret = FALSE;
1558
1559     EnterCriticalSection( &PROFILE_CritSect );
1560
1561     if (PROFILE_Open( filename )) {
1562         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1563         if (k) {
1564             TRACE("value (at %p): '%s'\n", k->value, k->value);
1565             if (((strlen(k->value) - 2) / 2) == len)
1566             {
1567                 LPSTR end, p;
1568                 BOOL valid = TRUE;
1569                 CHAR c;
1570                 DWORD chksum = 0;
1571
1572                 end  = k->value + strlen(k->value); /* -> '\0' */
1573                 /* check for invalid chars in ASCII coded hex string */
1574                 for (p=k->value; p < end; p++)
1575                 {
1576                     if (!isxdigit(*p))
1577                     {
1578                         WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1579                              *p, filename, section, key);
1580                         valid = FALSE;
1581                         break;
1582                     }
1583                 }
1584                 if (valid)
1585                 {
1586                     BOOL highnibble = TRUE;
1587                     BYTE b = 0, val;
1588                     LPBYTE binbuf = (LPBYTE)buf;
1589                     
1590                     end -= 2; /* don't include checksum in output data */
1591                     /* translate ASCII hex format into binary data */
1592                     for (p=k->value; p < end; p++)
1593                     {
1594                         c = toupper(*p);
1595                         val = (c > '9') ?
1596                                 (c - 'A' + 10) : (c - '0');
1597
1598                         if (highnibble)
1599                             b = val << 4;
1600                         else
1601                         {
1602                             b += val;
1603                             *binbuf++ = b; /* feed binary data into output */
1604                             chksum += b; /* calculate checksum */
1605                         }
1606                         highnibble ^= 1; /* toggle */
1607                     }
1608                     /* retrieve stored checksum value */
1609                     c = toupper(*p++);
1610                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1611                     c = toupper(*p);
1612                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1613                     if (b == (chksum & 0xff)) /* checksums match ? */
1614                         ret = TRUE;
1615                 }
1616             }
1617         }
1618     }
1619     LeaveCriticalSection( &PROFILE_CritSect );
1620
1621     return ret;
1622 }
1623
1624 /***********************************************************************
1625  *           GetPrivateProfileStructW (KERNEL32.543)
1626  */
1627 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1628                                       LPVOID buffer, UINT len, LPCWSTR filename)
1629 {
1630     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1631     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1632     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1633     LPSTR bufferA   = HeapAlloc( GetProcessHeap(), 0, len );
1634
1635     INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1636                                         len, filenameA );
1637     lstrcpynAtoW( buffer, bufferA, len );
1638     HeapFree( GetProcessHeap(), 0, bufferA);
1639     HeapFree( GetProcessHeap(), 0, sectionA );
1640     HeapFree( GetProcessHeap(), 0, keyA );
1641     HeapFree( GetProcessHeap(), 0, filenameA );
1642
1643     return ret;
1644 }
1645
1646
1647
1648 /***********************************************************************
1649  *           WritePrivateProfileStruct16 (KERNEL.406)
1650  */
1651 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key, 
1652         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1653 {
1654     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1655 }
1656
1657 /***********************************************************************
1658  *           WritePrivateProfileStructA (KERNEL32.744)
1659  */
1660 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key, 
1661                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1662 {
1663     BOOL ret = FALSE;
1664     LPBYTE binbuf;
1665     LPSTR outstring, p;
1666     DWORD sum = 0;
1667
1668     if (!section && !key && !buf)  /* flush the cache */
1669         return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1670
1671     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1672     outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1);
1673     p = outstring;
1674     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1675       *p++ = hex[*binbuf >> 4];
1676       *p++ = hex[*binbuf & 0xf];
1677       sum += *binbuf;
1678     }
1679     /* checksum is sum & 0xff */
1680     *p++ = hex[(sum & 0xf0) >> 4];
1681     *p++ = hex[sum & 0xf];
1682     *p++ = '\0';
1683
1684     EnterCriticalSection( &PROFILE_CritSect );
1685
1686     if (PROFILE_Open( filename )) 
1687         ret = PROFILE_SetString( section, key, outstring );
1688
1689     LeaveCriticalSection( &PROFILE_CritSect );
1690
1691     HeapFree( GetProcessHeap(), 0, outstring );
1692
1693     return ret;
1694 }
1695
1696 /***********************************************************************
1697  *           WritePrivateProfileStructW (KERNEL32.544)
1698  */
1699 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1700                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1701 {
1702     LPSTR sectionA  = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1703     LPSTR keyA      = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1704     LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1705     INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1706                                           filenameA );
1707     HeapFree( GetProcessHeap(), 0, sectionA );
1708     HeapFree( GetProcessHeap(), 0, keyA );
1709     HeapFree( GetProcessHeap(), 0, filenameA );
1710
1711     return ret;
1712 }
1713
1714
1715 /***********************************************************************
1716  *           WriteOutProfiles   (KERNEL.315)
1717  */
1718 void WINAPI WriteOutProfiles16(void)
1719 {
1720     EnterCriticalSection( &PROFILE_CritSect );
1721     PROFILE_FlushFile();
1722     LeaveCriticalSection( &PROFILE_CritSect );
1723 }
1724
1725 /***********************************************************************
1726  *           CloseProfileUserMapping   (KERNEL.138)
1727  */
1728 BOOL WINAPI CloseProfileUserMapping(void) {
1729     FIXME("(), stub!\n");
1730     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1731     return FALSE;
1732 }