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