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