Allow the implementation of the VxDCall entry points to be moved to
[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  *           GetProfileInt   (KERNEL.57)
1025  */
1026 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1027 {
1028     return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1029 }
1030
1031
1032 /***********************************************************************
1033  *           GetProfileIntA   (KERNEL32.@)
1034  */
1035 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1036 {
1037     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1038 }
1039
1040 /***********************************************************************
1041  *           GetProfileIntW   (KERNEL32.@)
1042  */
1043 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1044 {
1045     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1046 }
1047
1048 /*
1049  * if allow_section_name_copy is TRUE, allow the copying :
1050  *   - of Section names if 'section' is NULL
1051  *   - of Keys in a Section if 'entry' is NULL
1052  * (see MSDN doc for GetPrivateProfileString)
1053  */
1054 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1055                                             LPCWSTR def_val, LPWSTR buffer,
1056                                             UINT len, LPCWSTR filename,
1057                                             BOOL allow_section_name_copy )
1058 {
1059     int         ret;
1060     LPWSTR      pDefVal = NULL;
1061
1062     if (!filename)
1063         filename = wininiW;
1064
1065     TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1066           debugstr_w(def_val), buffer, len, debugstr_w(filename));
1067
1068     /* strip any trailing ' ' of def_val. */
1069     if (def_val)
1070     {
1071         LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1072
1073         while (p > def_val)
1074         {
1075             p--;
1076             if ((*p) != ' ')
1077                 break;
1078         }
1079         if (*p == ' ') /* ouch, contained trailing ' ' */
1080         {
1081             int len = (int)(p - def_val);
1082             pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1083             strncpyW(pDefVal, def_val, len);
1084             pDefVal[len] = '\0';
1085         }
1086     }
1087     if (!pDefVal)
1088         pDefVal = (LPWSTR)def_val;
1089
1090     RtlEnterCriticalSection( &PROFILE_CritSect );
1091
1092     if (PROFILE_Open( filename )) {
1093         if ((allow_section_name_copy) && (section == NULL))
1094             ret = PROFILE_GetSectionNames(buffer, len);
1095         else
1096             /* PROFILE_GetString already handles the 'entry == NULL' case */
1097             ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1098     } else {
1099        lstrcpynW( buffer, pDefVal, len );
1100        ret = strlenW( buffer );
1101     }
1102
1103     RtlLeaveCriticalSection( &PROFILE_CritSect );
1104
1105     if (pDefVal != def_val) /* allocated */
1106         HeapFree(GetProcessHeap(), 0, pDefVal);
1107
1108     TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1109
1110     return ret;
1111 }
1112
1113 /***********************************************************************
1114  *           GetPrivateProfileString   (KERNEL.128)
1115  */
1116 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1117                                         LPCSTR def_val, LPSTR buffer,
1118                                         UINT16 len, LPCSTR filename )
1119 {
1120     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1121     LPWSTR bufferW;
1122     INT16 retW, ret = 0;
1123
1124     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1125     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1126     else sectionW.Buffer = NULL;
1127     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1128     else entryW.Buffer = NULL;
1129     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1130     else def_valW.Buffer = NULL;
1131     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1132     else filenameW.Buffer = NULL;
1133
1134     retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1135                                      def_valW.Buffer, bufferW, len,
1136                                      filenameW.Buffer, FALSE );
1137     if (len)
1138     {
1139         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1140         if (!ret)
1141         {
1142             ret = len - 1;
1143             buffer[ret] = 0;
1144         }
1145         else
1146             ret--; /* strip terminating 0 */
1147     }
1148
1149     RtlFreeUnicodeString(&sectionW);
1150     RtlFreeUnicodeString(&entryW);
1151     RtlFreeUnicodeString(&def_valW);
1152     RtlFreeUnicodeString(&filenameW);
1153     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1154     return ret;
1155 }
1156
1157 /***********************************************************************
1158  *           GetPrivateProfileStringA   (KERNEL32.@)
1159  */
1160 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1161                                      LPCSTR def_val, LPSTR buffer,
1162                                      UINT len, LPCSTR filename )
1163 {
1164     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1165     LPWSTR bufferW;
1166     INT retW, ret = 0;
1167
1168     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1169     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1170     else sectionW.Buffer = NULL;
1171     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1172     else entryW.Buffer = NULL;
1173     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1174     else def_valW.Buffer = NULL;
1175     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1176     else filenameW.Buffer = NULL;
1177
1178     retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1179                                      def_valW.Buffer, bufferW, len,
1180                                      filenameW.Buffer);
1181     if (len)
1182     {
1183         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1184         if (!ret)
1185         {
1186             ret = len - 1;
1187             buffer[ret] = 0;
1188         }
1189         else
1190             ret--; /* strip terminating 0 */
1191     }
1192
1193     RtlFreeUnicodeString(&sectionW);
1194     RtlFreeUnicodeString(&entryW);
1195     RtlFreeUnicodeString(&def_valW);
1196     RtlFreeUnicodeString(&filenameW);
1197     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1198     return ret;
1199 }
1200
1201 /***********************************************************************
1202  *           GetPrivateProfileStringW   (KERNEL32.@)
1203  */
1204 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1205                                      LPCWSTR def_val, LPWSTR buffer,
1206                                      UINT len, LPCWSTR filename )
1207 {
1208     TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1209
1210     return PROFILE_GetPrivateProfileString( section, entry, def_val,
1211                                             buffer, len, filename, TRUE );
1212 }
1213
1214 /***********************************************************************
1215  *           GetProfileString   (KERNEL.58)
1216  */
1217 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1218                                  LPSTR buffer, UINT16 len )
1219 {
1220     return GetPrivateProfileString16( section, entry, def_val,
1221                                       buffer, len, "win.ini" );
1222 }
1223
1224 /***********************************************************************
1225  *           GetProfileStringA   (KERNEL32.@)
1226  */
1227 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1228                               LPSTR buffer, UINT len )
1229 {
1230     return GetPrivateProfileStringA( section, entry, def_val,
1231                                      buffer, len, "win.ini" );
1232 }
1233
1234 /***********************************************************************
1235  *           GetProfileStringW   (KERNEL32.@)
1236  */
1237 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1238                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1239 {
1240     return GetPrivateProfileStringW( section, entry, def_val,
1241                                      buffer, len, wininiW );
1242 }
1243
1244 /***********************************************************************
1245  *           WriteProfileString   (KERNEL.59)
1246  */
1247 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1248                                     LPCSTR string )
1249 {
1250     return WritePrivateProfileString16( section, entry, string, "win.ini" );
1251 }
1252
1253 /***********************************************************************
1254  *           WriteProfileStringA   (KERNEL32.@)
1255  */
1256 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1257                                  LPCSTR string )
1258 {
1259     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1260 }
1261
1262 /***********************************************************************
1263  *           WriteProfileStringW   (KERNEL32.@)
1264  */
1265 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1266                                      LPCWSTR string )
1267 {
1268     return WritePrivateProfileStringW( section, entry, string, wininiW );
1269 }
1270
1271
1272 /***********************************************************************
1273  *           GetPrivateProfileInt   (KERNEL.127)
1274  */
1275 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1276                                       INT16 def_val, LPCSTR filename )
1277 {
1278     /* we used to have some elaborate return value limitation (<= -32768 etc.)
1279      * here, but Win98SE doesn't care about this at all, so I deleted it.
1280      * AFAIR versions prior to Win9x had these limits, though. */
1281     return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1282 }
1283
1284 /***********************************************************************
1285  *           GetPrivateProfileIntW   (KERNEL32.@)
1286  */
1287 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1288                                    INT def_val, LPCWSTR filename )
1289 {
1290     WCHAR buffer[30];
1291     UNICODE_STRING bufferW;
1292     INT len;
1293     ULONG result;
1294
1295     if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1296                                           buffer, sizeof(buffer)/sizeof(WCHAR),
1297                                           filename )))
1298         return def_val;
1299
1300     if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1301
1302     /* FIXME: if entry can be found but it's empty, then Win16 is
1303      * supposed to return 0 instead of def_val ! Difficult/problematic
1304      * to implement (every other failure also returns zero buffer),
1305      * thus wait until testing framework avail for making sure nothing
1306      * else gets broken that way. */
1307     if (!buffer[0]) return (UINT)def_val;
1308
1309     RtlInitUnicodeString( &bufferW, buffer );
1310     RtlUnicodeStringToInteger( &bufferW, 10, &result);
1311     return result;
1312 }
1313
1314 /***********************************************************************
1315  *           GetPrivateProfileIntA   (KERNEL32.@)
1316  *
1317  * FIXME: rewrite using unicode
1318  */
1319 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1320                                    INT def_val, LPCSTR filename )
1321 {
1322     UNICODE_STRING entryW, filenameW, sectionW;
1323     UINT res;
1324     if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1325     else entryW.Buffer = NULL;
1326     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1327     else filenameW.Buffer = NULL;
1328     if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1329     else sectionW.Buffer = NULL;
1330     res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1331                                 filenameW.Buffer);
1332     RtlFreeUnicodeString(&sectionW);
1333     RtlFreeUnicodeString(&filenameW);
1334     RtlFreeUnicodeString(&entryW);
1335     return res;
1336 }
1337
1338 /***********************************************************************
1339  *           GetPrivateProfileSection   (KERNEL.418)
1340  */
1341 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1342                                         UINT16 len, LPCSTR filename )
1343 {
1344     return GetPrivateProfileSectionA( section, buffer, len, filename );
1345 }
1346
1347 /***********************************************************************
1348  *           GetPrivateProfileSectionW   (KERNEL32.@)
1349  */
1350 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1351                                       DWORD len, LPCWSTR filename )
1352 {
1353     int         ret = 0;
1354
1355     TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1356
1357     RtlEnterCriticalSection( &PROFILE_CritSect );
1358
1359     if (PROFILE_Open( filename ))
1360         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1361
1362     RtlLeaveCriticalSection( &PROFILE_CritSect );
1363
1364     return ret;
1365 }
1366
1367 /***********************************************************************
1368  *           GetPrivateProfileSectionA   (KERNEL32.@)
1369  */
1370 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1371                                       DWORD len, LPCSTR filename )
1372 {
1373     UNICODE_STRING sectionW, filenameW;
1374     LPWSTR bufferW;
1375     INT retW, ret = 0;
1376
1377     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1378     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1379     else sectionW.Buffer = NULL;
1380     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1381     else filenameW.Buffer = NULL;
1382
1383     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1384     if (len > 2)
1385     {
1386         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1387         if (ret > 2)
1388             ret -= 2;
1389         else
1390         {
1391             ret = 0;
1392             buffer[len-2] = 0;
1393             buffer[len-1] = 0;
1394         }
1395     }
1396     else
1397     {
1398         buffer[0] = 0;
1399         buffer[1] = 0;
1400     }
1401
1402     RtlFreeUnicodeString(&sectionW);
1403     RtlFreeUnicodeString(&filenameW);
1404     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1405     return ret;
1406 }
1407
1408 /***********************************************************************
1409  *           GetProfileSection   (KERNEL.419)
1410  */
1411 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1412 {
1413     return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1414 }
1415
1416 /***********************************************************************
1417  *           GetProfileSectionA   (KERNEL32.@)
1418  */
1419 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1420 {
1421     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1422 }
1423
1424 /***********************************************************************
1425  *           GetProfileSectionW   (KERNEL32.@)
1426  */
1427 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1428 {
1429     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1430 }
1431
1432
1433 /***********************************************************************
1434  *           WritePrivateProfileString   (KERNEL.129)
1435  */
1436 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1437                                            LPCSTR string, LPCSTR filename )
1438 {
1439     return WritePrivateProfileStringA(section,entry,string,filename);
1440 }
1441
1442 /***********************************************************************
1443  *           WritePrivateProfileStringW   (KERNEL32.@)
1444  */
1445 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1446                                         LPCWSTR string, LPCWSTR filename )
1447 {
1448     BOOL ret = FALSE;
1449
1450     RtlEnterCriticalSection( &PROFILE_CritSect );
1451
1452     if (PROFILE_Open( filename ))
1453     {
1454         if (!section && !entry && !string) /* documented "file flush" case */
1455         {
1456             PROFILE_FlushFile();
1457             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1458         }
1459         else {
1460             if (!section) {
1461                 FIXME("(NULL?,%s,%s,%s)?\n",
1462                       debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1463             } else {
1464                 ret = PROFILE_SetString( section, entry, string, FALSE);
1465                 PROFILE_FlushFile();
1466             }
1467         }
1468     }
1469
1470     RtlLeaveCriticalSection( &PROFILE_CritSect );
1471     return ret;
1472 }
1473
1474 /***********************************************************************
1475  *           WritePrivateProfileStringA   (KERNEL32.@)
1476  */
1477 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1478                                         LPCSTR string, LPCSTR filename )
1479 {
1480     UNICODE_STRING sectionW, entryW, stringW, filenameW;
1481     BOOL ret;
1482
1483     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1484     else sectionW.Buffer = NULL;
1485     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1486     else entryW.Buffer = NULL;
1487     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1488     else stringW.Buffer = NULL;
1489     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1490     else filenameW.Buffer = NULL;
1491
1492     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1493                                      stringW.Buffer, filenameW.Buffer);
1494     RtlFreeUnicodeString(&sectionW);
1495     RtlFreeUnicodeString(&entryW);
1496     RtlFreeUnicodeString(&stringW);
1497     RtlFreeUnicodeString(&filenameW);
1498     return ret;
1499 }
1500
1501 /***********************************************************************
1502  *           WritePrivateProfileSection   (KERNEL.416)
1503  */
1504 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1505                                             LPCSTR string, LPCSTR filename )
1506 {
1507     return WritePrivateProfileSectionA( section, string, filename );
1508 }
1509
1510 /***********************************************************************
1511  *           WritePrivateProfileSectionW   (KERNEL32.@)
1512  */
1513 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1514                                          LPCWSTR string, LPCWSTR filename )
1515 {
1516     BOOL ret = FALSE;
1517     LPWSTR p;
1518
1519     RtlEnterCriticalSection( &PROFILE_CritSect );
1520
1521     if (PROFILE_Open( filename )) {
1522         if (!section && !string)
1523             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1524         else if (!string) {/* delete the named section*/
1525             ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1526             PROFILE_FlushFile();
1527         } else {
1528             PROFILE_DeleteAllKeys(section);
1529             ret = TRUE;
1530             while(*string) {
1531                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1532                 strcpyW( buf, string );
1533                 if((p = strchrW( buf, '='))) {
1534                     *p='\0';
1535                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
1536                 }
1537                 HeapFree( GetProcessHeap(), 0, buf );
1538                 string += strlenW(string)+1;
1539             }
1540             PROFILE_FlushFile();
1541         }
1542     }
1543
1544     RtlLeaveCriticalSection( &PROFILE_CritSect );
1545     return ret;
1546 }
1547
1548 /***********************************************************************
1549  *           WritePrivateProfileSectionA   (KERNEL32.@)
1550  */
1551 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1552                                          LPCSTR string, LPCSTR filename)
1553
1554 {
1555     UNICODE_STRING sectionW, filenameW;
1556     LPWSTR stringW;
1557     BOOL ret;
1558
1559     if (string)
1560     {
1561         INT lenA, lenW;
1562         LPCSTR p = string;
1563
1564         while(*p) p += strlen(p) + 1;
1565         lenA = p - string + 1;
1566         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1567         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1568             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1569     }
1570     else stringW = NULL;
1571     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1572     else sectionW.Buffer = NULL;
1573     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1574     else filenameW.Buffer = NULL;
1575
1576     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1577
1578     HeapFree(GetProcessHeap(), 0, stringW);
1579     RtlFreeUnicodeString(&sectionW);
1580     RtlFreeUnicodeString(&filenameW);
1581     return ret;
1582 }
1583
1584 /***********************************************************************
1585  *           WriteProfileSection   (KERNEL.417)
1586  */
1587 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1588 {
1589     return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1590 }
1591
1592 /***********************************************************************
1593  *           WriteProfileSectionA   (KERNEL32.@)
1594  */
1595 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1596
1597 {
1598     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1599 }
1600
1601 /***********************************************************************
1602  *           WriteProfileSectionW   (KERNEL32.@)
1603  */
1604 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1605 {
1606    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1607 }
1608
1609 /***********************************************************************
1610  *           GetPrivateProfileSectionNames   (KERNEL.143)
1611  */
1612 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1613                                              LPCSTR filename )
1614 {
1615     return GetPrivateProfileSectionNamesA(buffer,size,filename);
1616 }
1617
1618
1619 /***********************************************************************
1620  *           GetProfileSectionNames   (KERNEL.142)
1621  */
1622 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1623
1624 {
1625     return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1626 }
1627
1628
1629 /***********************************************************************
1630  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1631  *
1632  * Returns the section names contained in the specified file.
1633  * FIXME: Where do we find this file when the path is relative?
1634  * The section names are returned as a list of strings with an extra
1635  * '\0' to mark the end of the list. Except for that the behavior
1636  * depends on the Windows version.
1637  *
1638  * Win95:
1639  * - if the buffer is 0 or 1 character long then it is as if it was of
1640  *   infinite length.
1641  * - otherwise, if the buffer is to small only the section names that fit
1642  *   are returned.
1643  * - note that this means if the buffer was to small to return even just
1644  *   the first section name then a single '\0' will be returned.
1645  * - the return value is the number of characters written in the buffer,
1646  *   except if the buffer was too smal in which case len-2 is returned
1647  *
1648  * Win2000:
1649  * - if the buffer is 0, 1 or 2 characters long then it is filled with
1650  *   '\0' and the return value is 0
1651  * - otherwise if the buffer is too small then the first section name that
1652  *   does not fit is truncated so that the string list can be terminated
1653  *   correctly (double '\0')
1654  * - the return value is the number of characters written in the buffer
1655  *   except for the trailing '\0'. If the buffer is too small, then the
1656  *   return value is len-2
1657  * - Win2000 has a bug that triggers when the section names and the
1658  *   trailing '\0' fit exactly in the buffer. In that case the trailing
1659  *   '\0' is missing.
1660  *
1661  * Wine implements the observed Win2000 behavior (except for the bug).
1662  *
1663  * Note that when the buffer is big enough then the return value may be any
1664  * value between 1 and len-1 (or len in Win95), including len-2.
1665  */
1666 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1667                                              LPCWSTR filename)
1668 {
1669     DWORD ret = 0;
1670
1671     RtlEnterCriticalSection( &PROFILE_CritSect );
1672
1673     if (PROFILE_Open( filename ))
1674         ret = PROFILE_GetSectionNames(buffer, size);
1675
1676     RtlLeaveCriticalSection( &PROFILE_CritSect );
1677
1678     return ret;
1679 }
1680
1681
1682 /***********************************************************************
1683  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
1684  */
1685 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1686                                              LPCSTR filename)
1687 {
1688     UNICODE_STRING filenameW;
1689     LPWSTR bufferW;
1690     INT retW, ret = 0;
1691
1692     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1693     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1694     else filenameW.Buffer = NULL;
1695
1696     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1697     if (retW && size)
1698     {
1699         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1700         if (!ret)
1701         {
1702             ret = size;
1703             buffer[size-1] = 0;
1704         }
1705     }
1706
1707     RtlFreeUnicodeString(&filenameW);
1708     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1709     return ret;
1710 }
1711
1712 /***********************************************************************
1713  *           GetPrivateProfileStruct (KERNEL.407)
1714  */
1715 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1716                                         LPVOID buf, UINT16 len, LPCSTR filename)
1717 {
1718     return GetPrivateProfileStructA( section, key, buf, len, filename );
1719 }
1720
1721 /***********************************************************************
1722  *           GetPrivateProfileStructW (KERNEL32.@)
1723  *
1724  * Should match Win95's behaviour pretty much
1725  */
1726 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1727                                       LPVOID buf, UINT len, LPCWSTR filename)
1728 {
1729     BOOL        ret = FALSE;
1730
1731     RtlEnterCriticalSection( &PROFILE_CritSect );
1732
1733     if (PROFILE_Open( filename )) {
1734         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1735         if (k) {
1736             TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1737             if (((strlenW(k->value) - 2) / 2) == len)
1738             {
1739                 LPWSTR end, p;
1740                 BOOL valid = TRUE;
1741                 WCHAR c;
1742                 DWORD chksum = 0;
1743
1744                 end  = k->value + strlenW(k->value); /* -> '\0' */
1745                 /* check for invalid chars in ASCII coded hex string */
1746                 for (p=k->value; p < end; p++)
1747                 {
1748                     if (!isxdigitW(*p))
1749                     {
1750                         WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1751                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1752                         valid = FALSE;
1753                         break;
1754                     }
1755                 }
1756                 if (valid)
1757                 {
1758                     BOOL highnibble = TRUE;
1759                     BYTE b = 0, val;
1760                     LPBYTE binbuf = (LPBYTE)buf;
1761
1762                     end -= 2; /* don't include checksum in output data */
1763                     /* translate ASCII hex format into binary data */
1764                     for (p=k->value; p < end; p++)
1765                     {
1766                         c = toupperW(*p);
1767                         val = (c > '9') ?
1768                                 (c - 'A' + 10) : (c - '0');
1769
1770                         if (highnibble)
1771                             b = val << 4;
1772                         else
1773                         {
1774                             b += val;
1775                             *binbuf++ = b; /* feed binary data into output */
1776                             chksum += b; /* calculate checksum */
1777                         }
1778                         highnibble ^= 1; /* toggle */
1779                     }
1780                     /* retrieve stored checksum value */
1781                     c = toupperW(*p++);
1782                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1783                     c = toupperW(*p);
1784                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1785                     if (b == (chksum & 0xff)) /* checksums match ? */
1786                         ret = TRUE;
1787                 }
1788             }
1789         }
1790     }
1791     RtlLeaveCriticalSection( &PROFILE_CritSect );
1792
1793     return ret;
1794 }
1795
1796 /***********************************************************************
1797  *           GetPrivateProfileStructA (KERNEL32.@)
1798  */
1799 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1800                                       LPVOID buffer, UINT len, LPCSTR filename)
1801 {
1802     UNICODE_STRING sectionW, keyW, filenameW;
1803     INT ret;
1804
1805     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1806     else sectionW.Buffer = NULL;
1807     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1808     else keyW.Buffer = NULL;
1809     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1810     else filenameW.Buffer = NULL;
1811
1812     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1813                                    filenameW.Buffer);
1814     /* Do not translate binary data. */
1815
1816     RtlFreeUnicodeString(&sectionW);
1817     RtlFreeUnicodeString(&keyW);
1818     RtlFreeUnicodeString(&filenameW);
1819     return ret;
1820 }
1821
1822
1823
1824 /***********************************************************************
1825  *           WritePrivateProfileStruct (KERNEL.406)
1826  */
1827 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1828         LPVOID buf, UINT16 bufsize, LPCSTR filename)
1829 {
1830     return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1831 }
1832
1833 /***********************************************************************
1834  *           WritePrivateProfileStructW (KERNEL32.@)
1835  */
1836 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1837                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1838 {
1839     BOOL ret = FALSE;
1840     LPBYTE binbuf;
1841     LPWSTR outstring, p;
1842     DWORD sum = 0;
1843
1844     if (!section && !key && !buf)  /* flush the cache */
1845         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1846
1847     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1848     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1849     p = outstring;
1850     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1851       *p++ = hex[*binbuf >> 4];
1852       *p++ = hex[*binbuf & 0xf];
1853       sum += *binbuf;
1854     }
1855     /* checksum is sum & 0xff */
1856     *p++ = hex[(sum & 0xf0) >> 4];
1857     *p++ = hex[sum & 0xf];
1858     *p++ = '\0';
1859
1860     RtlEnterCriticalSection( &PROFILE_CritSect );
1861
1862     if (PROFILE_Open( filename )) {
1863         ret = PROFILE_SetString( section, key, outstring, FALSE);
1864         PROFILE_FlushFile();
1865     }
1866
1867     RtlLeaveCriticalSection( &PROFILE_CritSect );
1868
1869     HeapFree( GetProcessHeap(), 0, outstring );
1870
1871     return ret;
1872 }
1873
1874 /***********************************************************************
1875  *           WritePrivateProfileStructA (KERNEL32.@)
1876  */
1877 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1878                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1879 {
1880     UNICODE_STRING sectionW, keyW, filenameW;
1881     INT ret;
1882
1883     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1884     else sectionW.Buffer = NULL;
1885     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1886     else keyW.Buffer = NULL;
1887     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1888     else filenameW.Buffer = NULL;
1889
1890     /* Do not translate binary data. */
1891     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1892                                      filenameW.Buffer);
1893
1894     RtlFreeUnicodeString(&sectionW);
1895     RtlFreeUnicodeString(&keyW);
1896     RtlFreeUnicodeString(&filenameW);
1897     return ret;
1898 }
1899
1900
1901 /***********************************************************************
1902  *           WriteOutProfiles   (KERNEL.315)
1903  */
1904 void WINAPI WriteOutProfiles16(void)
1905 {
1906     RtlEnterCriticalSection( &PROFILE_CritSect );
1907     PROFILE_FlushFile();
1908     RtlLeaveCriticalSection( &PROFILE_CritSect );
1909 }
1910
1911 /***********************************************************************
1912  *           CloseProfileUserMapping   (KERNEL32.@)
1913  */
1914 BOOL WINAPI CloseProfileUserMapping(void) {
1915     FIXME("(), stub!\n");
1916     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1917     return FALSE;
1918 }