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