Added a few more info classes in NtQueryInformationFile.
[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)
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     for(i=0;i<N_CACHED_PROFILES;i++)
751     {
752        if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
753        {
754           TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
755           if(i)
756           {
757              PROFILE_FlushFile();
758              tempProfile=MRUProfile[i];
759              for(j=i;j>0;j--)
760                 MRUProfile[j]=MRUProfile[j-1];
761              CurProfile=tempProfile;
762           }
763           GetFileTime(hFile, NULL, NULL, &LastWriteTime);
764           if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
765              TRACE("(%s): already opened (mru=%d)\n",
766                              debugstr_w(buffer), i );
767           else
768               TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
769                              debugstr_w(buffer), i );
770           CloseHandle(hFile);
771           return TRUE;
772         }
773     }
774
775     /* Flush the old current profile */
776     PROFILE_FlushFile();
777
778     /* Make the oldest profile the current one only in order to get rid of it */
779     if(i==N_CACHED_PROFILES)
780       {
781        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
782        for(i=N_CACHED_PROFILES-1;i>0;i--)
783           MRUProfile[i]=MRUProfile[i-1];
784        CurProfile=tempProfile;
785       }
786     if(CurProfile->filename) PROFILE_ReleaseFile();
787
788     /* OK, now that CurProfile is definitely free we assign it our new file */
789     CurProfile->filename  = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
790     strcpyW( CurProfile->filename, buffer );
791
792     if (hFile != INVALID_HANDLE_VALUE)
793     {
794         CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
795         GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
796         CloseHandle(hFile);
797     }
798     else
799     {
800         /* Does not exist yet, we will create it in PROFILE_FlushFile */
801         WARN("profile file %s not found\n", debugstr_w(buffer) );
802     }
803     return TRUE;
804 }
805
806
807 /***********************************************************************
808  *           PROFILE_GetSection
809  *
810  * Returns all keys of a section.
811  * If return_values is TRUE, also include the corresponding values.
812  */
813 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
814                                LPWSTR buffer, UINT len, BOOL return_values )
815 {
816     PROFILEKEY *key;
817
818     if(!buffer) return 0;
819
820     TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
821
822     while (section)
823     {
824         if (section->name[0] && !strcmpiW( section->name, section_name ))
825         {
826             UINT oldlen = len;
827             for (key = section->key; key; key = key->next)
828             {
829                 if (len <= 2) break;
830                 if (!*key->name) continue;  /* Skip empty lines */
831                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
832                 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
833                 len -= strlenW(buffer) + 1;
834                 buffer += strlenW(buffer) + 1;
835                 if (len < 2)
836                     break;
837                 if (return_values && key->value) {
838                         buffer[-1] = '=';
839                         PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
840                         len -= strlenW(buffer) + 1;
841                         buffer += strlenW(buffer) + 1;
842                 }
843             }
844             *buffer = '\0';
845             if (len <= 1)
846                 /*If either lpszSection or lpszKey is NULL and the supplied
847                   destination buffer is too small to hold all the strings,
848                   the last string is truncated and followed by two null characters.
849                   In this case, the return value is equal to cchReturnBuffer
850                   minus two. */
851             {
852                 buffer[-1] = '\0';
853                 return oldlen - 2;
854             }
855             return oldlen - len;
856         }
857         section = section->next;
858     }
859     buffer[0] = buffer[1] = '\0';
860     return 0;
861 }
862
863 /* See GetPrivateProfileSectionNamesA for documentation */
864 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
865 {
866     LPWSTR buf;
867     UINT f,l;
868     PROFILESECTION *section;
869
870     TRACE("(%p, %d)\n", buffer, len);
871
872     if (!buffer || !len)
873         return 0;
874     if (len==1) {
875         *buffer='\0';
876         return 0;
877     }
878
879     f=len-1;
880     buf=buffer;
881     section = CurProfile->section;
882     while ((section!=NULL)) {
883         if (section->name[0]) {
884             l = strlenW(section->name)+1;
885             if (l > f) {
886                 if (f>0) {
887                     strncpyW(buf, section->name, f-1);
888                     buf += f-1;
889                     *buf++='\0';
890                 }
891                 *buf='\0';
892                 return len-2;
893             }
894             strcpyW(buf, section->name);
895             buf += l;
896             f -= l;
897         }
898         section = section->next;
899     }
900     *buf='\0';
901     return buf-buffer;
902 }
903
904
905 /***********************************************************************
906  *           PROFILE_GetString
907  *
908  * Get a profile string.
909  *
910  * Tests with GetPrivateProfileString16, W95a,
911  * with filled buffer ("****...") and section "set1" and key_name "1" valid:
912  * section      key_name        def_val         res     buffer
913  * "set1"       "1"             "x"             43      [data]
914  * "set1"       "1   "          "x"             43      [data]          (!)
915  * "set1"       "  1  "'        "x"             43      [data]          (!)
916  * "set1"       ""              "x"             1       "x"
917  * "set1"       ""              "x   "          1       "x"             (!)
918  * "set1"       ""              "  x   "        3       "  x"           (!)
919  * "set1"       NULL            "x"             6       "1\02\03\0\0"
920  * "set1"       ""              "x"             1       "x"
921  * NULL         "1"             "x"             0       ""              (!)
922  * ""           "1"             "x"             1       "x"
923  * NULL         NULL            ""              0       ""
924  *
925  *
926  */
927 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
928                               LPCWSTR def_val, LPWSTR buffer, UINT len )
929 {
930     PROFILEKEY *key = NULL;
931     static const WCHAR empty_strW[] = { 0 };
932
933     if(!buffer) return 0;
934
935     if (!def_val) def_val = empty_strW;
936     if (key_name)
937     {
938         if (!key_name[0])
939         {
940             /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
941             return 0;
942         }
943         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
944         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
945                            len, TRUE );
946         TRACE("(%s,%s,%s): returning %s\n",
947               debugstr_w(section), debugstr_w(key_name),
948               debugstr_w(def_val), debugstr_w(buffer) );
949         return strlenW( buffer );
950     }
951     /* no "else" here ! */
952     if (section && section[0])
953     {
954         INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
955         if (!buffer[0]) /* no luck -> def_val */
956         {
957             PROFILE_CopyEntry(buffer, def_val, len, TRUE);
958             ret = strlenW(buffer);
959         }
960         return ret;
961     }
962     buffer[0] = '\0';
963     return 0;
964 }
965
966
967 /***********************************************************************
968  *           PROFILE_SetString
969  *
970  * Set a profile string.
971  */
972 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
973                                LPCWSTR value, BOOL create_always )
974 {
975     if (!key_name)  /* Delete a whole section */
976     {
977         TRACE("(%s)\n", debugstr_w(section_name));
978         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
979                                                       section_name );
980         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
981                                 this is not an error on application's level.*/
982     }
983     else if (!value)  /* Delete a key */
984     {
985         TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
986         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
987                                                   section_name, key_name );
988         return TRUE;          /* same error handling as above */
989     }
990     else  /* Set the key value */
991     {
992         PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
993                                         key_name, TRUE, create_always );
994         TRACE("(%s,%s,%s):\n",
995               debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
996         if (!key) return FALSE;
997         if (key->value)
998         {
999             /* strip the leading spaces. We can safely strip \n\r and
1000              * friends too, they should not happen here anyway. */
1001             while (PROFILE_isspaceW(*value)) value++;
1002
1003             if (!strcmpW( key->value, value ))
1004             {
1005                 TRACE("  no change needed\n" );
1006                 return TRUE;  /* No change needed */
1007             }
1008             TRACE("  replacing %s\n", debugstr_w(key->value) );
1009             HeapFree( GetProcessHeap(), 0, key->value );
1010         }
1011         else TRACE("  creating key\n" );
1012         key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1013         strcpyW( key->value, value );
1014         CurProfile->changed = TRUE;
1015     }
1016     return TRUE;
1017 }
1018
1019
1020 /********************* API functions **********************************/
1021
1022
1023 /***********************************************************************
1024  *           GetProfileIntA   (KERNEL32.@)
1025  */
1026 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1027 {
1028     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1029 }
1030
1031 /***********************************************************************
1032  *           GetProfileIntW   (KERNEL32.@)
1033  */
1034 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1035 {
1036     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1037 }
1038
1039 /*
1040  * if allow_section_name_copy is TRUE, allow the copying :
1041  *   - of Section names if 'section' is NULL
1042  *   - of Keys in a Section if 'entry' is NULL
1043  * (see MSDN doc for GetPrivateProfileString)
1044  */
1045 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1046                                             LPCWSTR def_val, LPWSTR buffer,
1047                                             UINT len, LPCWSTR filename,
1048                                             BOOL allow_section_name_copy )
1049 {
1050     int         ret;
1051     LPWSTR      pDefVal = NULL;
1052
1053     if (!filename)
1054         filename = wininiW;
1055
1056     TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1057           debugstr_w(def_val), buffer, len, debugstr_w(filename));
1058
1059     /* strip any trailing ' ' of def_val. */
1060     if (def_val)
1061     {
1062         LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1063
1064         while (p > def_val)
1065         {
1066             p--;
1067             if ((*p) != ' ')
1068                 break;
1069         }
1070         if (*p == ' ') /* ouch, contained trailing ' ' */
1071         {
1072             int len = (int)(p - def_val);
1073             pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1074             strncpyW(pDefVal, def_val, len);
1075             pDefVal[len] = '\0';
1076         }
1077     }
1078     if (!pDefVal)
1079         pDefVal = (LPWSTR)def_val;
1080
1081     RtlEnterCriticalSection( &PROFILE_CritSect );
1082
1083     if (PROFILE_Open( filename )) {
1084         if ((allow_section_name_copy) && (section == NULL))
1085             ret = PROFILE_GetSectionNames(buffer, len);
1086         else
1087             /* PROFILE_GetString already handles the 'entry == NULL' case */
1088             ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1089     } else {
1090        lstrcpynW( buffer, pDefVal, len );
1091        ret = strlenW( buffer );
1092     }
1093
1094     RtlLeaveCriticalSection( &PROFILE_CritSect );
1095
1096     if (pDefVal != def_val) /* allocated */
1097         HeapFree(GetProcessHeap(), 0, pDefVal);
1098
1099     TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1100
1101     return ret;
1102 }
1103
1104 /***********************************************************************
1105  *           GetPrivateProfileString   (KERNEL.128)
1106  */
1107 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1108                                         LPCSTR def_val, LPSTR buffer,
1109                                         UINT16 len, LPCSTR filename )
1110 {
1111     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1112     LPWSTR bufferW;
1113     INT16 retW, ret = 0;
1114
1115     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1116     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1117     else sectionW.Buffer = NULL;
1118     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1119     else entryW.Buffer = NULL;
1120     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1121     else def_valW.Buffer = NULL;
1122     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1123     else filenameW.Buffer = NULL;
1124
1125     retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1126                                      def_valW.Buffer, bufferW, len,
1127                                      filenameW.Buffer, FALSE );
1128     if (len)
1129     {
1130         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1131         if (!ret)
1132         {
1133             ret = len - 1;
1134             buffer[ret] = 0;
1135         }
1136         else
1137             ret--; /* strip terminating 0 */
1138     }
1139
1140     RtlFreeUnicodeString(&sectionW);
1141     RtlFreeUnicodeString(&entryW);
1142     RtlFreeUnicodeString(&def_valW);
1143     RtlFreeUnicodeString(&filenameW);
1144     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1145     return ret;
1146 }
1147
1148 /***********************************************************************
1149  *           GetPrivateProfileStringA   (KERNEL32.@)
1150  */
1151 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1152                                      LPCSTR def_val, LPSTR buffer,
1153                                      UINT len, LPCSTR filename )
1154 {
1155     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1156     LPWSTR bufferW;
1157     INT retW, ret = 0;
1158
1159     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1160     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1161     else sectionW.Buffer = NULL;
1162     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1163     else entryW.Buffer = NULL;
1164     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1165     else def_valW.Buffer = NULL;
1166     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1167     else filenameW.Buffer = NULL;
1168
1169     retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1170                                      def_valW.Buffer, bufferW, len,
1171                                      filenameW.Buffer);
1172     if (len)
1173     {
1174         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1175         if (!ret)
1176         {
1177             ret = len - 1;
1178             buffer[ret] = 0;
1179         }
1180         else
1181             ret--; /* strip terminating 0 */
1182     }
1183
1184     RtlFreeUnicodeString(&sectionW);
1185     RtlFreeUnicodeString(&entryW);
1186     RtlFreeUnicodeString(&def_valW);
1187     RtlFreeUnicodeString(&filenameW);
1188     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1189     return ret;
1190 }
1191
1192 /***********************************************************************
1193  *           GetPrivateProfileStringW   (KERNEL32.@)
1194  */
1195 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1196                                      LPCWSTR def_val, LPWSTR buffer,
1197                                      UINT len, LPCWSTR filename )
1198 {
1199     TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1200
1201     return PROFILE_GetPrivateProfileString( section, entry, def_val,
1202                                             buffer, len, filename, TRUE );
1203 }
1204
1205 /***********************************************************************
1206  *           GetProfileStringA   (KERNEL32.@)
1207  */
1208 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1209                               LPSTR buffer, UINT len )
1210 {
1211     return GetPrivateProfileStringA( section, entry, def_val,
1212                                      buffer, len, "win.ini" );
1213 }
1214
1215 /***********************************************************************
1216  *           GetProfileStringW   (KERNEL32.@)
1217  */
1218 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1219                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1220 {
1221     return GetPrivateProfileStringW( section, entry, def_val,
1222                                      buffer, len, wininiW );
1223 }
1224
1225 /***********************************************************************
1226  *           WriteProfileStringA   (KERNEL32.@)
1227  */
1228 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1229                                  LPCSTR string )
1230 {
1231     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1232 }
1233
1234 /***********************************************************************
1235  *           WriteProfileStringW   (KERNEL32.@)
1236  */
1237 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1238                                      LPCWSTR string )
1239 {
1240     return WritePrivateProfileStringW( section, entry, string, wininiW );
1241 }
1242
1243
1244 /***********************************************************************
1245  *           GetPrivateProfileIntW   (KERNEL32.@)
1246  */
1247 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1248                                    INT def_val, LPCWSTR filename )
1249 {
1250     WCHAR buffer[30];
1251     UNICODE_STRING bufferW;
1252     INT len;
1253     ULONG result;
1254
1255     if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1256                                           buffer, sizeof(buffer)/sizeof(WCHAR),
1257                                           filename )))
1258         return def_val;
1259
1260     if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1261
1262     /* FIXME: if entry can be found but it's empty, then Win16 is
1263      * supposed to return 0 instead of def_val ! Difficult/problematic
1264      * to implement (every other failure also returns zero buffer),
1265      * thus wait until testing framework avail for making sure nothing
1266      * else gets broken that way. */
1267     if (!buffer[0]) return (UINT)def_val;
1268
1269     RtlInitUnicodeString( &bufferW, buffer );
1270     RtlUnicodeStringToInteger( &bufferW, 10, &result);
1271     return result;
1272 }
1273
1274 /***********************************************************************
1275  *           GetPrivateProfileIntA   (KERNEL32.@)
1276  *
1277  * FIXME: rewrite using unicode
1278  */
1279 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1280                                    INT def_val, LPCSTR filename )
1281 {
1282     UNICODE_STRING entryW, filenameW, sectionW;
1283     UINT res;
1284     if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1285     else entryW.Buffer = NULL;
1286     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1287     else filenameW.Buffer = NULL;
1288     if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1289     else sectionW.Buffer = NULL;
1290     res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1291                                 filenameW.Buffer);
1292     RtlFreeUnicodeString(&sectionW);
1293     RtlFreeUnicodeString(&filenameW);
1294     RtlFreeUnicodeString(&entryW);
1295     return res;
1296 }
1297
1298 /***********************************************************************
1299  *           GetPrivateProfileSectionW   (KERNEL32.@)
1300  */
1301 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1302                                       DWORD len, LPCWSTR filename )
1303 {
1304     int         ret = 0;
1305
1306     TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1307
1308     RtlEnterCriticalSection( &PROFILE_CritSect );
1309
1310     if (PROFILE_Open( filename ))
1311         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1312
1313     RtlLeaveCriticalSection( &PROFILE_CritSect );
1314
1315     return ret;
1316 }
1317
1318 /***********************************************************************
1319  *           GetPrivateProfileSectionA   (KERNEL32.@)
1320  */
1321 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1322                                       DWORD len, LPCSTR filename )
1323 {
1324     UNICODE_STRING sectionW, filenameW;
1325     LPWSTR bufferW;
1326     INT retW, ret = 0;
1327
1328     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1329     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1330     else sectionW.Buffer = NULL;
1331     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1332     else filenameW.Buffer = NULL;
1333
1334     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1335     if (len > 2)
1336     {
1337         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1338         if (ret > 2)
1339             ret -= 2;
1340         else
1341         {
1342             ret = 0;
1343             buffer[len-2] = 0;
1344             buffer[len-1] = 0;
1345         }
1346     }
1347     else
1348     {
1349         buffer[0] = 0;
1350         buffer[1] = 0;
1351     }
1352
1353     RtlFreeUnicodeString(&sectionW);
1354     RtlFreeUnicodeString(&filenameW);
1355     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1356     return ret;
1357 }
1358
1359 /***********************************************************************
1360  *           GetProfileSectionA   (KERNEL32.@)
1361  */
1362 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1363 {
1364     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1365 }
1366
1367 /***********************************************************************
1368  *           GetProfileSectionW   (KERNEL32.@)
1369  */
1370 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1371 {
1372     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1373 }
1374
1375
1376 /***********************************************************************
1377  *           WritePrivateProfileStringW   (KERNEL32.@)
1378  */
1379 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1380                                         LPCWSTR string, LPCWSTR filename )
1381 {
1382     BOOL ret = FALSE;
1383
1384     RtlEnterCriticalSection( &PROFILE_CritSect );
1385
1386     if (PROFILE_Open( filename ))
1387     {
1388         if (!section && !entry && !string) /* documented "file flush" case */
1389         {
1390             PROFILE_FlushFile();
1391             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1392         }
1393         else {
1394             if (!section) {
1395                 FIXME("(NULL?,%s,%s,%s)?\n",
1396                       debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1397             } else {
1398                 ret = PROFILE_SetString( section, entry, string, FALSE);
1399                 PROFILE_FlushFile();
1400             }
1401         }
1402     }
1403
1404     RtlLeaveCriticalSection( &PROFILE_CritSect );
1405     return ret;
1406 }
1407
1408 /***********************************************************************
1409  *           WritePrivateProfileStringA   (KERNEL32.@)
1410  */
1411 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1412                                         LPCSTR string, LPCSTR filename )
1413 {
1414     UNICODE_STRING sectionW, entryW, stringW, filenameW;
1415     BOOL ret;
1416
1417     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1418     else sectionW.Buffer = NULL;
1419     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1420     else entryW.Buffer = NULL;
1421     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1422     else stringW.Buffer = NULL;
1423     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1424     else filenameW.Buffer = NULL;
1425
1426     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1427                                      stringW.Buffer, filenameW.Buffer);
1428     RtlFreeUnicodeString(&sectionW);
1429     RtlFreeUnicodeString(&entryW);
1430     RtlFreeUnicodeString(&stringW);
1431     RtlFreeUnicodeString(&filenameW);
1432     return ret;
1433 }
1434
1435 /***********************************************************************
1436  *           WritePrivateProfileSectionW   (KERNEL32.@)
1437  */
1438 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1439                                          LPCWSTR string, LPCWSTR filename )
1440 {
1441     BOOL ret = FALSE;
1442     LPWSTR p;
1443
1444     RtlEnterCriticalSection( &PROFILE_CritSect );
1445
1446     if (PROFILE_Open( filename )) {
1447         if (!section && !string)
1448             PROFILE_ReleaseFile();  /* always return FALSE in this case */
1449         else if (!string) {/* delete the named section*/
1450             ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1451             PROFILE_FlushFile();
1452         } else {
1453             PROFILE_DeleteAllKeys(section);
1454             ret = TRUE;
1455             while(*string) {
1456                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1457                 strcpyW( buf, string );
1458                 if((p = strchrW( buf, '='))) {
1459                     *p='\0';
1460                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
1461                 }
1462                 HeapFree( GetProcessHeap(), 0, buf );
1463                 string += strlenW(string)+1;
1464             }
1465             PROFILE_FlushFile();
1466         }
1467     }
1468
1469     RtlLeaveCriticalSection( &PROFILE_CritSect );
1470     return ret;
1471 }
1472
1473 /***********************************************************************
1474  *           WritePrivateProfileSectionA   (KERNEL32.@)
1475  */
1476 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1477                                          LPCSTR string, LPCSTR filename)
1478
1479 {
1480     UNICODE_STRING sectionW, filenameW;
1481     LPWSTR stringW;
1482     BOOL ret;
1483
1484     if (string)
1485     {
1486         INT lenA, lenW;
1487         LPCSTR p = string;
1488
1489         while(*p) p += strlen(p) + 1;
1490         lenA = p - string + 1;
1491         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1492         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1493             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1494     }
1495     else stringW = NULL;
1496     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1497     else sectionW.Buffer = NULL;
1498     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1499     else filenameW.Buffer = NULL;
1500
1501     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1502
1503     HeapFree(GetProcessHeap(), 0, stringW);
1504     RtlFreeUnicodeString(&sectionW);
1505     RtlFreeUnicodeString(&filenameW);
1506     return ret;
1507 }
1508
1509 /***********************************************************************
1510  *           WriteProfileSectionA   (KERNEL32.@)
1511  */
1512 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1513
1514 {
1515     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1516 }
1517
1518 /***********************************************************************
1519  *           WriteProfileSectionW   (KERNEL32.@)
1520  */
1521 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1522 {
1523    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1524 }
1525
1526
1527 /***********************************************************************
1528  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1529  *
1530  * Returns the section names contained in the specified file.
1531  * FIXME: Where do we find this file when the path is relative?
1532  * The section names are returned as a list of strings with an extra
1533  * '\0' to mark the end of the list. Except for that the behavior
1534  * depends on the Windows version.
1535  *
1536  * Win95:
1537  * - if the buffer is 0 or 1 character long then it is as if it was of
1538  *   infinite length.
1539  * - otherwise, if the buffer is to small only the section names that fit
1540  *   are returned.
1541  * - note that this means if the buffer was to small to return even just
1542  *   the first section name then a single '\0' will be returned.
1543  * - the return value is the number of characters written in the buffer,
1544  *   except if the buffer was too smal in which case len-2 is returned
1545  *
1546  * Win2000:
1547  * - if the buffer is 0, 1 or 2 characters long then it is filled with
1548  *   '\0' and the return value is 0
1549  * - otherwise if the buffer is too small then the first section name that
1550  *   does not fit is truncated so that the string list can be terminated
1551  *   correctly (double '\0')
1552  * - the return value is the number of characters written in the buffer
1553  *   except for the trailing '\0'. If the buffer is too small, then the
1554  *   return value is len-2
1555  * - Win2000 has a bug that triggers when the section names and the
1556  *   trailing '\0' fit exactly in the buffer. In that case the trailing
1557  *   '\0' is missing.
1558  *
1559  * Wine implements the observed Win2000 behavior (except for the bug).
1560  *
1561  * Note that when the buffer is big enough then the return value may be any
1562  * value between 1 and len-1 (or len in Win95), including len-2.
1563  */
1564 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1565                                              LPCWSTR filename)
1566 {
1567     DWORD ret = 0;
1568
1569     RtlEnterCriticalSection( &PROFILE_CritSect );
1570
1571     if (PROFILE_Open( filename ))
1572         ret = PROFILE_GetSectionNames(buffer, size);
1573
1574     RtlLeaveCriticalSection( &PROFILE_CritSect );
1575
1576     return ret;
1577 }
1578
1579
1580 /***********************************************************************
1581  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
1582  */
1583 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1584                                              LPCSTR filename)
1585 {
1586     UNICODE_STRING filenameW;
1587     LPWSTR bufferW;
1588     INT retW, ret = 0;
1589
1590     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1591     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1592     else filenameW.Buffer = NULL;
1593
1594     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1595     if (retW && size)
1596     {
1597         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1598         if (!ret)
1599         {
1600             ret = size;
1601             buffer[size-1] = 0;
1602         }
1603     }
1604
1605     RtlFreeUnicodeString(&filenameW);
1606     if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1607     return ret;
1608 }
1609
1610 /***********************************************************************
1611  *           GetPrivateProfileStructW (KERNEL32.@)
1612  *
1613  * Should match Win95's behaviour pretty much
1614  */
1615 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1616                                       LPVOID buf, UINT len, LPCWSTR filename)
1617 {
1618     BOOL        ret = FALSE;
1619
1620     RtlEnterCriticalSection( &PROFILE_CritSect );
1621
1622     if (PROFILE_Open( filename )) {
1623         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1624         if (k) {
1625             TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1626             if (((strlenW(k->value) - 2) / 2) == len)
1627             {
1628                 LPWSTR end, p;
1629                 BOOL valid = TRUE;
1630                 WCHAR c;
1631                 DWORD chksum = 0;
1632
1633                 end  = k->value + strlenW(k->value); /* -> '\0' */
1634                 /* check for invalid chars in ASCII coded hex string */
1635                 for (p=k->value; p < end; p++)
1636                 {
1637                     if (!isxdigitW(*p))
1638                     {
1639                         WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1640                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1641                         valid = FALSE;
1642                         break;
1643                     }
1644                 }
1645                 if (valid)
1646                 {
1647                     BOOL highnibble = TRUE;
1648                     BYTE b = 0, val;
1649                     LPBYTE binbuf = (LPBYTE)buf;
1650
1651                     end -= 2; /* don't include checksum in output data */
1652                     /* translate ASCII hex format into binary data */
1653                     for (p=k->value; p < end; p++)
1654                     {
1655                         c = toupperW(*p);
1656                         val = (c > '9') ?
1657                                 (c - 'A' + 10) : (c - '0');
1658
1659                         if (highnibble)
1660                             b = val << 4;
1661                         else
1662                         {
1663                             b += val;
1664                             *binbuf++ = b; /* feed binary data into output */
1665                             chksum += b; /* calculate checksum */
1666                         }
1667                         highnibble ^= 1; /* toggle */
1668                     }
1669                     /* retrieve stored checksum value */
1670                     c = toupperW(*p++);
1671                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1672                     c = toupperW(*p);
1673                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
1674                     if (b == (chksum & 0xff)) /* checksums match ? */
1675                         ret = TRUE;
1676                 }
1677             }
1678         }
1679     }
1680     RtlLeaveCriticalSection( &PROFILE_CritSect );
1681
1682     return ret;
1683 }
1684
1685 /***********************************************************************
1686  *           GetPrivateProfileStructA (KERNEL32.@)
1687  */
1688 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1689                                       LPVOID buffer, UINT len, LPCSTR filename)
1690 {
1691     UNICODE_STRING sectionW, keyW, filenameW;
1692     INT ret;
1693
1694     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1695     else sectionW.Buffer = NULL;
1696     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1697     else keyW.Buffer = NULL;
1698     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1699     else filenameW.Buffer = NULL;
1700
1701     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1702                                    filenameW.Buffer);
1703     /* Do not translate binary data. */
1704
1705     RtlFreeUnicodeString(&sectionW);
1706     RtlFreeUnicodeString(&keyW);
1707     RtlFreeUnicodeString(&filenameW);
1708     return ret;
1709 }
1710
1711
1712
1713 /***********************************************************************
1714  *           WritePrivateProfileStructW (KERNEL32.@)
1715  */
1716 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1717                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1718 {
1719     BOOL ret = FALSE;
1720     LPBYTE binbuf;
1721     LPWSTR outstring, p;
1722     DWORD sum = 0;
1723
1724     if (!section && !key && !buf)  /* flush the cache */
1725         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1726
1727     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1728     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1729     p = outstring;
1730     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1731       *p++ = hex[*binbuf >> 4];
1732       *p++ = hex[*binbuf & 0xf];
1733       sum += *binbuf;
1734     }
1735     /* checksum is sum & 0xff */
1736     *p++ = hex[(sum & 0xf0) >> 4];
1737     *p++ = hex[sum & 0xf];
1738     *p++ = '\0';
1739
1740     RtlEnterCriticalSection( &PROFILE_CritSect );
1741
1742     if (PROFILE_Open( filename )) {
1743         ret = PROFILE_SetString( section, key, outstring, FALSE);
1744         PROFILE_FlushFile();
1745     }
1746
1747     RtlLeaveCriticalSection( &PROFILE_CritSect );
1748
1749     HeapFree( GetProcessHeap(), 0, outstring );
1750
1751     return ret;
1752 }
1753
1754 /***********************************************************************
1755  *           WritePrivateProfileStructA (KERNEL32.@)
1756  */
1757 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1758                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1759 {
1760     UNICODE_STRING sectionW, keyW, filenameW;
1761     INT ret;
1762
1763     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1764     else sectionW.Buffer = NULL;
1765     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1766     else keyW.Buffer = NULL;
1767     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1768     else filenameW.Buffer = NULL;
1769
1770     /* Do not translate binary data. */
1771     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1772                                      filenameW.Buffer);
1773
1774     RtlFreeUnicodeString(&sectionW);
1775     RtlFreeUnicodeString(&keyW);
1776     RtlFreeUnicodeString(&filenameW);
1777     return ret;
1778 }
1779
1780
1781 /***********************************************************************
1782  *           WriteOutProfiles   (KERNEL.315)
1783  */
1784 void WINAPI WriteOutProfiles16(void)
1785 {
1786     RtlEnterCriticalSection( &PROFILE_CritSect );
1787     PROFILE_FlushFile();
1788     RtlLeaveCriticalSection( &PROFILE_CritSect );
1789 }
1790
1791 /***********************************************************************
1792  *           CloseProfileUserMapping   (KERNEL32.@)
1793  */
1794 BOOL WINAPI CloseProfileUserMapping(void) {
1795     FIXME("(), stub!\n");
1796     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1797     return FALSE;
1798 }