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