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