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