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