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