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