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