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