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