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