~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/kernel32/profile.c

Version: ~ [ wine-1.5.4 ] ~ [ wine-1.5.3 ] ~ [ wine-1.5.2 ] ~ [ wine-1.5.1 ] ~ [ wine-1.5.0 ] ~ [ wine-1.4 ] ~ [ wine-1.4-rc6 ] ~ [ wine-1.4-rc5 ] ~ [ wine-1.4-rc4 ] ~ [ wine-1.4-rc3 ] ~ [ wine-1.4-rc2 ] ~ [ wine-1.4-rc1 ] ~ [ wine-1.3.37 ] ~ [ wine-1.3.36 ] ~ [ wine-1.3.35 ] ~ [ wine-1.3.34 ] ~ [ wine-1.3.33 ] ~ [ wine-1.3.32 ] ~ [ wine-1.3.31 ] ~ [ wine-1.3.30 ] ~ [ wine-1.3.29 ] ~ [ wine-1.3.28 ] ~ [ wine-1.3.27 ] ~ [ wine-1.3.26 ] ~ [ wine-1.3.25 ] ~ [ wine-1.3.24 ] ~ [ wine-1.3.23 ] ~ [ wine-1.3.22 ] ~ [ wine-1.3.21 ] ~ [ wine-1.3.20 ] ~ [ wine-1.3.19 ] ~ [ wine-1.3.18 ] ~ [ wine-1.2.3 ] ~ [ wine-1.3.17 ] ~ [ wine-1.3.16 ] ~ [ wine-1.3.15 ] ~ [ wine-1.3.14 ] ~ [ wine-1.3.13 ] ~ [ wine-1.3.12 ] ~ [ wine-1.3.11 ] ~ [ wine-1.3.10 ] ~ [ wine-1.3.9 ] ~ [ wine-1.2.2 ] ~ [ wine-1.3.8 ] ~ [ wine-1.3.7 ] ~ [ wine-1.3.6 ] ~ [ wine-1.3.5 ] ~ [ wine-1.2.1 ] ~ [ wine-1.3.4 ] ~ [ wine-1.3.3 ] ~ [ wine-1.3.2 ] ~ [ wine-1.3.1 ] ~ [ wine-1.3.0 ] ~ [ wine-1.2 ] ~ [ wine-1.2-rc7 ] ~ [ wine-1.2-rc6 ] ~ [ wine-1.2-rc5 ] ~ [ wine-1.2-rc4 ] ~ [ wine-1.2-rc3 ] ~ [ wine-1.2-rc2 ] ~ [ wine-1.2-rc1 ] ~ [ wine-1.1.44 ] ~ [ wine-1.1.43 ] ~ [ wine-1.1.42 ] ~ [ wine-1.1.41 ] ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.