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