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