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

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

Version: ~ [ 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 windirW[MAX_PATH];
738     WCHAR buffer[MAX_PATH];
739     HANDLE hFile = INVALID_HANDLE_VALUE;
740     FILETIME LastWriteTime;
741     int i,j;
742     PROFILE *tempProfile;
743     
744     ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
745 
746     /* First time around */
747 
748     if(!CurProfile)
749        for(i=0;i<N_CACHED_PROFILES;i++)
750        {
751           MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
752           if(MRUProfile[i] == NULL) break;
753           MRUProfile[i]->changed=FALSE;
754           MRUProfile[i]->section=NULL;
755           MRUProfile[i]->filename=NULL;
756           MRUProfile[i]->encoding=ENCODING_ANSI;
757           ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
758        }
759 
760     GetWindowsDirectoryW( windirW, MAX_PATH );
761 
762     if (!filename)
763         filename = wininiW;
764 
765     if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
766         !strchrW(filename, '\\') && !strchrW(filename, '/'))
767     {
768         static const WCHAR wszSeparator[] = {'\\', 0};
769         strcpyW(buffer, windirW);
770         strcatW(buffer, wszSeparator);
771         strcatW(buffer, filename);
772     }
773     else
774     {
775         LPWSTR dummy;
776         GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
777     }
778         
779     TRACE("path: %s\n", debugstr_w(buffer));
780 
781     hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
782                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
783                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
784 
785     if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
786     {
787         WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
788         return FALSE;
789     }
790 
791     for(i=0;i<N_CACHED_PROFILES;i++)
792     {
793         if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
794         {
795             TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
796             if(i)
797             {
798                 PROFILE_FlushFile();
799                 tempProfile=MRUProfile[i];
800                 for(j=i;j>0;j--)
801                     MRUProfile[j]=MRUProfile[j-1];
802                 CurProfile=tempProfile;
803             }
804 
805             if (hFile != INVALID_HANDLE_VALUE)
806             {
807                 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
808                 if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
809                     is_not_current(&LastWriteTime))
810                     TRACE("(%s): already opened (mru=%d)\n",
811                           debugstr_w(buffer), i);
812                 else
813                 {
814                     TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
815                           debugstr_w(buffer), i);
816                     PROFILE_Free(CurProfile->section);
817                     CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
818                     CurProfile->LastWriteTime = LastWriteTime;
819                 }
820                 CloseHandle(hFile);
821             }
822             else TRACE("(%s): already opened, not yet created (mru=%d)\n",
823                        debugstr_w(buffer), i);
824             return TRUE;
825         }
826     }
827 
828     /* Flush the old current profile */
829     PROFILE_FlushFile();
830 
831     /* Make the oldest profile the current one only in order to get rid of it */
832     if(i==N_CACHED_PROFILES)
833       {
834        tempProfile=MRUProfile[N_CACHED_PROFILES-1];
835        for(i=N_CACHED_PROFILES-1;i>0;i--)
836           MRUProfile[i]=MRUProfile[i-1];
837        CurProfile=tempProfile;
838       }
839     if(CurProfile->filename) PROFILE_ReleaseFile();
840 
841     /* OK, now that CurProfile is definitely free we assign it our new file */
842     CurProfile->filename  = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
843     strcpyW( CurProfile->filename, buffer );
844 
845     if (hFile != INVALID_HANDLE_VALUE)
846     {
847         CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
848         GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
849         CloseHandle(hFile);
850     }
851     else
852     {
853         /* Does not exist yet, we will create it in PROFILE_FlushFile */
854         WARN("profile file %s not found\n", debugstr_w(buffer) );
855     }
856     return TRUE;
857 }
858 
859 
860 /***********************************************************************
861  *           PROFILE_GetSection
862  *
863  * Returns all keys of a section.
864  * If return_values is TRUE, also include the corresponding values.
865  */
866 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
867                                LPWSTR buffer, UINT len, BOOL return_values )
868 {
869     PROFILEKEY *key;
870 
871     if(!buffer) return 0;
872 
873     TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
874 
875     while (section)
876     {
877         if (section->name[0] && !strcmpiW( section->name, section_name ))
878         {
879             UINT oldlen = len;
880             for (key = section->key; key; key = key->next)
881             {
882                 if (len <= 2) break;
883                 if (!*key->name) continue;  /* Skip empty lines */
884                 if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
885                 if (!return_values && !key->value) continue;  /* Skip lines w.o. '=' */
886                 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
887                 len -= strlenW(buffer) + 1;
888                 buffer += strlenW(buffer) + 1;
889                 if (len < 2)
890                     break;
891                 if (return_values && key->value) {
892                         buffer[-1] = '=';
893                         PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
894                         len -= strlenW(buffer) + 1;
895                         buffer += strlenW(buffer) + 1;
896                 }
897             }
898             *buffer = '\0';
899             if (len <= 1)
900                 /*If either lpszSection or lpszKey is NULL and the supplied
901                   destination buffer is too small to hold all the strings,
902                   the last string is truncated and followed by two null characters.
903                   In this case, the return value is equal to cchReturnBuffer
904                   minus two. */
905             {
906                 buffer[-1] = '\0';
907                 return oldlen - 2;
908             }
909             return oldlen - len;
910         }
911         section = section->next;
912     }
913     buffer[0] = buffer[1] = '\0';
914     return 0;
915 }
916 
917 /* See GetPrivateProfileSectionNamesA for documentation */
918 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
919 {
920     LPWSTR buf;
921     UINT buflen,tmplen;
922     PROFILESECTION *section;
923 
924     TRACE("(%p, %d)\n", buffer, len);
925 
926     if (!buffer || !len)
927         return 0;
928     if (len==1) {
929         *buffer='\0';
930         return 0;
931     }
932 
933     buflen=len-1;
934     buf=buffer;
935     section = CurProfile->section;
936     while ((section!=NULL)) {
937         if (section->name[0]) {
938             tmplen = strlenW(section->name)+1;
939             if (tmplen >= buflen) {
940                 if (buflen > 0) {
941                     memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
942                     buf += buflen-1;
943                     *buf++='\0';
944                 }
945                 *buf='\0';
946                 return len-2;
947             }
948             memcpy(buf, section->name, tmplen * sizeof(WCHAR));
949             buf += tmplen;
950             buflen -= tmplen;
951         }
952         section = section->next;
953     }
954     *buf='\0';
955     return buf-buffer;
956 }
957 
958 
959 /***********************************************************************
960  *           PROFILE_GetString
961  *
962  * Get a profile string.
963  *
964  * Tests with GetPrivateProfileString16, W95a,
965  * with filled buffer ("****...") and section "set1" and key_name "1" valid:
966  * section      key_name        def_val         res     buffer
967  * "set1"       "1"             "x"             43      [data]
968  * "set1"       "1   "          "x"             43      [data]          (!)
969  * "set1"       "  1  "'        "x"             43      [data]          (!)
970  * "set1"       ""              "x"             1       "x"
971  * "set1"       ""              "x   "          1       "x"             (!)
972  * "set1"       ""              "  x   "        3       "  x"           (!)
973  * "set1"       NULL            "x"             6       "1\02\03\0\0"
974  * "set1"       ""              "x"             1       "x"
975  * NULL         "1"             "x"             0       ""              (!)
976  * ""           "1"             "x"             1       "x"
977  * NULL         NULL            ""              0       ""
978  *
979  *
980  */
981 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
982                               LPCWSTR def_val, LPWSTR buffer, UINT len )
983 {
984     PROFILEKEY *key = NULL;
985     static const WCHAR empty_strW[] = { 0 };
986 
987     if(!buffer || !len) return 0;
988 
989     if (!def_val) def_val = empty_strW;
990     if (key_name)
991     {
992         if (!key_name[0])
993         {
994             PROFILE_CopyEntry(buffer, def_val, len, TRUE);
995             return strlenW(buffer);
996         }
997         key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
998         PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
999                            len, TRUE );
1000         TRACE("(%s,%s,%s): returning %s\n",
1001               debugstr_w(section), debugstr_w(key_name),
1002               debugstr_w(def_val), debugstr_w(buffer) );
1003         return strlenW( buffer );
1004     }
1005     /* no "else" here ! */
1006     if (section && section[0])
1007     {
1008         INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
1009         if (!buffer[0]) /* no luck -> def_val */
1010         {
1011             PROFILE_CopyEntry(buffer, def_val, len, TRUE);
1012             ret = strlenW(buffer);
1013         }
1014         return ret;
1015     }
1016     buffer[0] = '\0';
1017     return 0;
1018 }
1019 
1020 
1021 /***********************************************************************
1022  *           PROFILE_SetString
1023  *
1024  * Set a profile string.
1025  */
1026 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
1027                                LPCWSTR value, BOOL create_always )
1028 {
1029     if (!key_name)  /* Delete a whole section */
1030     {
1031         TRACE("(%s)\n", debugstr_w(section_name));
1032         CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1033                                                       section_name );
1034         return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
1035                                 this is not an error on application's level.*/
1036     }
1037     else if (!value)  /* Delete a key */
1038     {
1039         TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1040         CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1041                                                   section_name, key_name );
1042         return TRUE;          /* same error handling as above */
1043     }
1044     else  /* Set the key value */
1045     {
1046         PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1047                                         key_name, TRUE, create_always );
1048         TRACE("(%s,%s,%s):\n",
1049               debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1050         if (!key) return FALSE;
1051 
1052         /* strip the leading spaces. We can safely strip \n\r and
1053          * friends too, they should not happen here anyway. */
1054         while (PROFILE_isspaceW(*value)) value++;
1055 
1056         if (key->value)
1057         {
1058             if (!strcmpW( key->value, value ))
1059             {
1060                 TRACE("  no change needed\n" );
1061                 return TRUE;  /* No change needed */
1062             }
1063             TRACE("  replacing %s\n", debugstr_w(key->value) );
1064             HeapFree( GetProcessHeap(), 0, key->value );
1065         }
1066         else TRACE("  creating key\n" );
1067         key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1068         strcpyW( key->value, value );
1069         CurProfile->changed = TRUE;
1070     }
1071     return TRUE;
1072 }
1073 
1074 
1075 /********************* API functions **********************************/
1076 
1077 
1078 /***********************************************************************
1079  *           GetProfileIntA   (KERNEL32.@)
1080  */
1081 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1082 {
1083     return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1084 }
1085 
1086 /***********************************************************************
1087  *           GetProfileIntW   (KERNEL32.@)
1088  */
1089 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1090 {
1091     return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1092 }
1093 
1094 /***********************************************************************
1095  *           GetPrivateProfileStringW   (KERNEL32.@)
1096  */
1097 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1098                                      LPCWSTR def_val, LPWSTR buffer,
1099                                      UINT len, LPCWSTR filename )
1100 {
1101     int         ret;
1102     LPWSTR      defval_tmp = NULL;
1103 
1104     TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1105           debugstr_w(def_val), buffer, len, debugstr_w(filename));
1106 
1107     /* strip any trailing ' ' of def_val. */
1108     if (def_val)
1109     {
1110         LPCWSTR p = def_val + strlenW(def_val) - 1;
1111 
1112         while (p > def_val && *p == ' ')
1113             p--;
1114 
1115         if (p >= def_val)
1116         {
1117             int len = (int)(p - def_val) + 1;
1118 
1119             defval_tmp = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1120             memcpy(defval_tmp, def_val, len * sizeof(WCHAR));
1121             defval_tmp[len] = '\0';
1122             def_val = defval_tmp;
1123         }
1124     }
1125 
1126     RtlEnterCriticalSection( &PROFILE_CritSect );
1127 
1128     if (PROFILE_Open( filename, FALSE )) {
1129         if (section == NULL)
1130             ret = PROFILE_GetSectionNames(buffer, len);
1131         else 
1132             /* PROFILE_GetString can handle the 'entry == NULL' case */
1133             ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1134     } else if (buffer && def_val) {
1135        lstrcpynW( buffer, def_val, len );
1136        ret = strlenW( buffer );
1137     }
1138     else
1139        ret = 0;
1140 
1141     RtlLeaveCriticalSection( &PROFILE_CritSect );
1142 
1143     HeapFree(GetProcessHeap(), 0, defval_tmp);
1144 
1145     TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1146 
1147     return ret;
1148 }
1149 
1150 /***********************************************************************
1151  *           GetPrivateProfileStringA   (KERNEL32.@)
1152  */
1153 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1154                                      LPCSTR def_val, LPSTR buffer,
1155                                      UINT len, LPCSTR filename )
1156 {
1157     UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1158     LPWSTR bufferW;
1159     INT retW, ret = 0;
1160 
1161     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1162     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1163     else sectionW.Buffer = NULL;
1164     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1165     else entryW.Buffer = NULL;
1166     if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1167     else def_valW.Buffer = NULL;
1168     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1169     else filenameW.Buffer = NULL;
1170 
1171     retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1172                                      def_valW.Buffer, bufferW, len,
1173                                      filenameW.Buffer);
1174     if (len)
1175     {
1176         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1177         if (!ret)
1178         {
1179             ret = len - 1;
1180             buffer[ret] = 0;
1181         }
1182         else
1183             ret--; /* strip terminating 0 */
1184     }
1185 
1186     RtlFreeUnicodeString(&sectionW);
1187     RtlFreeUnicodeString(&entryW);
1188     RtlFreeUnicodeString(&def_valW);
1189     RtlFreeUnicodeString(&filenameW);
1190     HeapFree(GetProcessHeap(), 0, bufferW);
1191     return ret;
1192 }
1193 
1194 /***********************************************************************
1195  *           GetProfileStringA   (KERNEL32.@)
1196  */
1197 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1198                               LPSTR buffer, UINT len )
1199 {
1200     return GetPrivateProfileStringA( section, entry, def_val,
1201                                      buffer, len, "win.ini" );
1202 }
1203 
1204 /***********************************************************************
1205  *           GetProfileStringW   (KERNEL32.@)
1206  */
1207 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1208                               LPCWSTR def_val, LPWSTR buffer, UINT len )
1209 {
1210     return GetPrivateProfileStringW( section, entry, def_val,
1211                                      buffer, len, wininiW );
1212 }
1213 
1214 /***********************************************************************
1215  *           WriteProfileStringA   (KERNEL32.@)
1216  */
1217 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1218                                  LPCSTR string )
1219 {
1220     return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1221 }
1222 
1223 /***********************************************************************
1224  *           WriteProfileStringW   (KERNEL32.@)
1225  */
1226 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1227                                      LPCWSTR string )
1228 {
1229     return WritePrivateProfileStringW( section, entry, string, wininiW );
1230 }
1231 
1232 
1233 /***********************************************************************
1234  *           GetPrivateProfileIntW   (KERNEL32.@)
1235  */
1236 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1237                                    INT def_val, LPCWSTR filename )
1238 {
1239     WCHAR buffer[30];
1240     UNICODE_STRING bufferW;
1241     INT len;
1242     ULONG result;
1243 
1244     if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1245                                           buffer, sizeof(buffer)/sizeof(WCHAR),
1246                                           filename )))
1247         return def_val;
1248 
1249     /* FIXME: if entry can be found but it's empty, then Win16 is
1250      * supposed to return 0 instead of def_val ! Difficult/problematic
1251      * to implement (every other failure also returns zero buffer),
1252      * thus wait until testing framework avail for making sure nothing
1253      * else gets broken that way. */
1254     if (!buffer[0]) return (UINT)def_val;
1255 
1256     RtlInitUnicodeString( &bufferW, buffer );
1257     RtlUnicodeStringToInteger( &bufferW, 0, &result);
1258     return result;
1259 }
1260 
1261 /***********************************************************************
1262  *           GetPrivateProfileIntA   (KERNEL32.@)
1263  *
1264  * FIXME: rewrite using unicode
1265  */
1266 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1267                                    INT def_val, LPCSTR filename )
1268 {
1269     UNICODE_STRING entryW, filenameW, sectionW;
1270     UINT res;
1271     if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1272     else entryW.Buffer = NULL;
1273     if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1274     else filenameW.Buffer = NULL;
1275     if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1276     else sectionW.Buffer = NULL;
1277     res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1278                                 filenameW.Buffer);
1279     RtlFreeUnicodeString(&sectionW);
1280     RtlFreeUnicodeString(&filenameW);
1281     RtlFreeUnicodeString(&entryW);
1282     return res;
1283 }
1284 
1285 /***********************************************************************
1286  *           GetPrivateProfileSectionW   (KERNEL32.@)
1287  */
1288 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1289                                       DWORD len, LPCWSTR filename )
1290 {
1291     int ret = 0;
1292 
1293     if (!section || !buffer)
1294     {
1295         SetLastError(ERROR_INVALID_PARAMETER);
1296         return 0;
1297     }
1298 
1299     TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1300 
1301     RtlEnterCriticalSection( &PROFILE_CritSect );
1302 
1303     if (PROFILE_Open( filename, FALSE ))
1304         ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1305 
1306     RtlLeaveCriticalSection( &PROFILE_CritSect );
1307 
1308     return ret;
1309 }
1310 
1311 /***********************************************************************
1312  *           GetPrivateProfileSectionA   (KERNEL32.@)
1313  */
1314 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1315                                       DWORD len, LPCSTR filename )
1316 {
1317     UNICODE_STRING sectionW, filenameW;
1318     LPWSTR bufferW;
1319     INT retW, ret = 0;
1320 
1321     if (!section || !buffer)
1322     {
1323         SetLastError(ERROR_INVALID_PARAMETER);
1324         return 0;
1325     }
1326 
1327     bufferW = HeapAlloc(GetProcessHeap(), 0, len * 2 * sizeof(WCHAR));
1328     RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1329     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1330     else filenameW.Buffer = NULL;
1331 
1332     retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len * 2, filenameW.Buffer);
1333     if (retW)
1334     {
1335         if (retW == len * 2 - 2) retW++;  /* overflow */
1336         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1337         if (!ret || ret == len)  /* overflow */
1338         {
1339             ret = len - 2;
1340             buffer[len-2] = 0;
1341             buffer[len-1] = 0;
1342         }
1343         else ret--;
1344     }
1345     else
1346     {
1347         buffer[0] = 0;
1348         buffer[1] = 0;
1349     }
1350 
1351     RtlFreeUnicodeString(&sectionW);
1352     RtlFreeUnicodeString(&filenameW);
1353     HeapFree(GetProcessHeap(), 0, bufferW);
1354     return ret;
1355 }
1356 
1357 /***********************************************************************
1358  *           GetProfileSectionA   (KERNEL32.@)
1359  */
1360 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1361 {
1362     return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1363 }
1364 
1365 /***********************************************************************
1366  *           GetProfileSectionW   (KERNEL32.@)
1367  */
1368 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1369 {
1370     return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1371 }
1372 
1373 
1374 /***********************************************************************
1375  *           WritePrivateProfileStringW   (KERNEL32.@)
1376  */
1377 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1378                                         LPCWSTR string, LPCWSTR filename )
1379 {
1380     BOOL ret = FALSE;
1381 
1382     RtlEnterCriticalSection( &PROFILE_CritSect );
1383 
1384     if (!section && !entry && !string) /* documented "file flush" case */
1385     {
1386         if (!filename || PROFILE_Open( filename, TRUE ))
1387         {
1388             if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
1389         }
1390     }
1391     else if (PROFILE_Open( filename, TRUE ))
1392     {
1393         if (!section) {
1394             SetLastError(ERROR_FILE_NOT_FOUND);
1395         } else {
1396             ret = PROFILE_SetString( section, entry, string, FALSE);
1397             PROFILE_FlushFile();
1398         }
1399     }
1400 
1401     RtlLeaveCriticalSection( &PROFILE_CritSect );
1402     return ret;
1403 }
1404 
1405 /***********************************************************************
1406  *           WritePrivateProfileStringA   (KERNEL32.@)
1407  */
1408 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1409                                         LPCSTR string, LPCSTR filename )
1410 {
1411     UNICODE_STRING sectionW, entryW, stringW, filenameW;
1412     BOOL ret;
1413 
1414     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1415     else sectionW.Buffer = NULL;
1416     if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1417     else entryW.Buffer = NULL;
1418     if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1419     else stringW.Buffer = NULL;
1420     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1421     else filenameW.Buffer = NULL;
1422 
1423     ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1424                                      stringW.Buffer, filenameW.Buffer);
1425     RtlFreeUnicodeString(&sectionW);
1426     RtlFreeUnicodeString(&entryW);
1427     RtlFreeUnicodeString(&stringW);
1428     RtlFreeUnicodeString(&filenameW);
1429     return ret;
1430 }
1431 
1432 /***********************************************************************
1433  *           WritePrivateProfileSectionW   (KERNEL32.@)
1434  */
1435 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1436                                          LPCWSTR string, LPCWSTR filename )
1437 {
1438     BOOL ret = FALSE;
1439     LPWSTR p;
1440 
1441     RtlEnterCriticalSection( &PROFILE_CritSect );
1442 
1443     if (!section && !string)
1444     {
1445         if (!filename || PROFILE_Open( filename, TRUE ))
1446         {
1447             if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
1448         }
1449     }
1450     else if (PROFILE_Open( filename, TRUE )) {
1451         if (!string) {/* delete the named section*/
1452             ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1453             PROFILE_FlushFile();
1454         } else {
1455             PROFILE_DeleteAllKeys(section);
1456             ret = TRUE;
1457             while(*string) {
1458                 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1459                 strcpyW( buf, string );
1460                 if((p = strchrW( buf, '='))) {
1461                     *p='\0';
1462                     ret = PROFILE_SetString( section, buf, p+1, TRUE);
1463                 }
1464                 HeapFree( GetProcessHeap(), 0, buf );
1465                 string += strlenW(string)+1;
1466             }
1467             PROFILE_FlushFile();
1468         }
1469     }
1470 
1471     RtlLeaveCriticalSection( &PROFILE_CritSect );
1472     return ret;
1473 }
1474 
1475 /***********************************************************************
1476  *           WritePrivateProfileSectionA   (KERNEL32.@)
1477  */
1478 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1479                                          LPCSTR string, LPCSTR filename)
1480 
1481 {
1482     UNICODE_STRING sectionW, filenameW;
1483     LPWSTR stringW;
1484     BOOL ret;
1485 
1486     if (string)
1487     {
1488         INT lenA, lenW;
1489         LPCSTR p = string;
1490 
1491         while(*p) p += strlen(p) + 1;
1492         lenA = p - string + 1;
1493         lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1494         if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1495             MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1496     }
1497     else stringW = NULL;
1498     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1499     else sectionW.Buffer = NULL;
1500     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1501     else filenameW.Buffer = NULL;
1502 
1503     ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1504 
1505     HeapFree(GetProcessHeap(), 0, stringW);
1506     RtlFreeUnicodeString(&sectionW);
1507     RtlFreeUnicodeString(&filenameW);
1508     return ret;
1509 }
1510 
1511 /***********************************************************************
1512  *           WriteProfileSectionA   (KERNEL32.@)
1513  */
1514 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1515 
1516 {
1517     return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1518 }
1519 
1520 /***********************************************************************
1521  *           WriteProfileSectionW   (KERNEL32.@)
1522  */
1523 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1524 {
1525    return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1526 }
1527 
1528 
1529 /***********************************************************************
1530  *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1531  *
1532  * Returns the section names contained in the specified file.
1533  * FIXME: Where do we find this file when the path is relative?
1534  * The section names are returned as a list of strings with an extra
1535  * '\0' to mark the end of the list. Except for that the behavior
1536  * depends on the Windows version.
1537  *
1538  * Win95:
1539  * - if the buffer is 0 or 1 character long then it is as if it was of
1540  *   infinite length.
1541  * - otherwise, if the buffer is too small only the section names that fit
1542  *   are returned.
1543  * - note that this means if the buffer was too small to return even just
1544  *   the first section name then a single '\0' will be returned.
1545  * - the return value is the number of characters written in the buffer,
1546  *   except if the buffer was too small in which case len-2 is returned
1547  *
1548  * Win2000:
1549  * - if the buffer is 0, 1 or 2 characters long then it is filled with
1550  *   '\0' and the return value is 0
1551  * - otherwise if the buffer is too small then the first section name that
1552  *   does not fit is truncated so that the string list can be terminated
1553  *   correctly (double '\0')
1554  * - the return value is the number of characters written in the buffer
1555  *   except for the trailing '\0'. If the buffer is too small, then the
1556  *   return value is len-2
1557  * - Win2000 has a bug that triggers when the section names and the
1558  *   trailing '\0' fit exactly in the buffer. In that case the trailing
1559  *   '\0' is missing.
1560  *
1561  * Wine implements the observed Win2000 behavior (except for the bug).
1562  *
1563  * Note that when the buffer is big enough then the return value may be any
1564  * value between 1 and len-1 (or len in Win95), including len-2.
1565  */
1566 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1567                                              LPCWSTR filename)
1568 {
1569     DWORD ret = 0;
1570 
1571     RtlEnterCriticalSection( &PROFILE_CritSect );
1572 
1573     if (PROFILE_Open( filename, FALSE ))
1574         ret = PROFILE_GetSectionNames(buffer, size);
1575 
1576     RtlLeaveCriticalSection( &PROFILE_CritSect );
1577 
1578     return ret;
1579 }
1580 
1581 
1582 /***********************************************************************
1583  *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
1584  */
1585 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1586                                              LPCSTR filename)
1587 {
1588     UNICODE_STRING filenameW;
1589     LPWSTR bufferW;
1590     INT retW, ret = 0;
1591 
1592     bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1593     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1594     else filenameW.Buffer = NULL;
1595 
1596     retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1597     if (retW && size)
1598     {
1599         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1600         if (!ret)
1601         {
1602             ret = size-2;
1603             buffer[size-1] = 0;
1604         }
1605         else
1606           ret = ret-1;
1607     }
1608     else if(size)
1609         buffer[0] = '\0';
1610 
1611     RtlFreeUnicodeString(&filenameW);
1612     HeapFree(GetProcessHeap(), 0, bufferW);
1613     return ret;
1614 }
1615 
1616 /***********************************************************************
1617  *           GetPrivateProfileStructW (KERNEL32.@)
1618  *
1619  * Should match Win95's behaviour pretty much
1620  */
1621 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1622                                       LPVOID buf, UINT len, LPCWSTR filename)
1623 {
1624     BOOL        ret = FALSE;
1625 
1626     RtlEnterCriticalSection( &PROFILE_CritSect );
1627 
1628     if (PROFILE_Open( filename, FALSE )) {
1629         PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1630         if (k) {
1631             TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1632             if (((strlenW(k->value) - 2) / 2) == len)
1633             {
1634                 LPWSTR end, p;
1635                 BOOL valid = TRUE;
1636                 WCHAR c;
1637                 DWORD chksum = 0;
1638 
1639                 end  = k->value + strlenW(k->value); /* -> '\0' */
1640                 /* check for invalid chars in ASCII coded hex string */
1641                 for (p=k->value; p < end; p++)
1642                 {
1643                     if (!isxdigitW(*p))
1644                     {
1645                         WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1646                              *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1647                         valid = FALSE;
1648                         break;
1649                     }
1650                 }
1651                 if (valid)
1652                 {
1653                     BOOL highnibble = TRUE;
1654                     BYTE b = 0, val;
1655                     LPBYTE binbuf = buf;
1656 
1657                     end -= 2; /* don't include checksum in output data */
1658                     /* translate ASCII hex format into binary data */
1659                     for (p=k->value; p < end; p++)
1660                     {
1661                         c = toupperW(*p);
1662                         val = (c > '9') ?
1663                                 (c - 'A' + 10) : (c - '');
1664 
1665                         if (highnibble)
1666                             b = val << 4;
1667                         else
1668                         {
1669                             b += val;
1670                             *binbuf++ = b; /* feed binary data into output */
1671                             chksum += b; /* calculate checksum */
1672                         }
1673                         highnibble ^= 1; /* toggle */
1674                     }
1675                     /* retrieve stored checksum value */
1676                     c = toupperW(*p++);
1677                     b = ( (c > '9') ? (c - 'A' + 10) : (c - '') ) << 4;
1678                     c = toupperW(*p);
1679                     b +=  (c > '9') ? (c - 'A' + 10) : (c - '');
1680                     if (b == (chksum & 0xff)) /* checksums match ? */
1681                         ret = TRUE;
1682                 }
1683             }
1684         }
1685     }
1686     RtlLeaveCriticalSection( &PROFILE_CritSect );
1687 
1688     return ret;
1689 }
1690 
1691 /***********************************************************************
1692  *           GetPrivateProfileStructA (KERNEL32.@)
1693  */
1694 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1695                                       LPVOID buffer, UINT len, LPCSTR filename)
1696 {
1697     UNICODE_STRING sectionW, keyW, filenameW;
1698     INT ret;
1699 
1700     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1701     else sectionW.Buffer = NULL;
1702     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1703     else keyW.Buffer = NULL;
1704     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1705     else filenameW.Buffer = NULL;
1706 
1707     ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1708                                    filenameW.Buffer);
1709     /* Do not translate binary data. */
1710 
1711     RtlFreeUnicodeString(&sectionW);
1712     RtlFreeUnicodeString(&keyW);
1713     RtlFreeUnicodeString(&filenameW);
1714     return ret;
1715 }
1716 
1717 
1718 
1719 /***********************************************************************
1720  *           WritePrivateProfileStructW (KERNEL32.@)
1721  */
1722 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1723                                         LPVOID buf, UINT bufsize, LPCWSTR filename)
1724 {
1725     BOOL ret = FALSE;
1726     LPBYTE binbuf;
1727     LPWSTR outstring, p;
1728     DWORD sum = 0;
1729 
1730     if (!section && !key && !buf)  /* flush the cache */
1731         return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1732 
1733     /* allocate string buffer for hex chars + checksum hex char + '\0' */
1734     outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1735     p = outstring;
1736     for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1737       *p++ = hex[*binbuf >> 4];
1738       *p++ = hex[*binbuf & 0xf];
1739       sum += *binbuf;
1740     }
1741     /* checksum is sum & 0xff */
1742     *p++ = hex[(sum & 0xf0) >> 4];
1743     *p++ = hex[sum & 0xf];
1744     *p++ = '\0';
1745 
1746     RtlEnterCriticalSection( &PROFILE_CritSect );
1747 
1748     if (PROFILE_Open( filename, TRUE )) {
1749         ret = PROFILE_SetString( section, key, outstring, FALSE);
1750         PROFILE_FlushFile();
1751     }
1752 
1753     RtlLeaveCriticalSection( &PROFILE_CritSect );
1754 
1755     HeapFree( GetProcessHeap(), 0, outstring );
1756 
1757     return ret;
1758 }
1759 
1760 /***********************************************************************
1761  *           WritePrivateProfileStructA (KERNEL32.@)
1762  */
1763 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1764                                         LPVOID buf, UINT bufsize, LPCSTR filename)
1765 {
1766     UNICODE_STRING sectionW, keyW, filenameW;
1767     INT ret;
1768 
1769     if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1770     else sectionW.Buffer = NULL;
1771     if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1772     else keyW.Buffer = NULL;
1773     if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1774     else filenameW.Buffer = NULL;
1775 
1776     /* Do not translate binary data. */
1777     ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1778                                      filenameW.Buffer);
1779 
1780     RtlFreeUnicodeString(&sectionW);
1781     RtlFreeUnicodeString(&keyW);
1782     RtlFreeUnicodeString(&filenameW);
1783     return ret;
1784 }
1785 
1786 
1787 /***********************************************************************
1788  *           OpenProfileUserMapping   (KERNEL32.@)
1789  */
1790 BOOL WINAPI OpenProfileUserMapping(void) {
1791     FIXME("(), stub!\n");
1792     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1793     return FALSE;
1794 }
1795 
1796 /***********************************************************************
1797  *           CloseProfileUserMapping   (KERNEL32.@)
1798  */
1799 BOOL WINAPI CloseProfileUserMapping(void) {
1800     FIXME("(), stub!\n");
1801     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1802     return FALSE;
1803 }
1804 

~ [ 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.