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