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