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