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