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