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 "config.h"
23 #include "wine/port.h"
24
25 #include <string.h>
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnls.h"
31 #include "winerror.h"
32 #include "winternl.h"
33 #include "wine/winbase16.h"
34 #include "wine/unicode.h"
35 #include "wine/library.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(profile);
39
40 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
41
42 typedef enum
43 {
44 ENCODING_ANSI = 1,
45 ENCODING_UTF8,
46 ENCODING_UTF16LE,
47 ENCODING_UTF16BE
48 } ENCODING;
49
50 typedef struct tagPROFILEKEY
51 {
52 WCHAR *value;
53 struct tagPROFILEKEY *next;
54 WCHAR name[1];
55 } PROFILEKEY;
56
57 typedef struct tagPROFILESECTION
58 {
59 struct tagPROFILEKEY *key;
60 struct tagPROFILESECTION *next;
61 WCHAR name[1];
62 } PROFILESECTION;
63
64
65 typedef struct
66 {
67 BOOL changed;
68 PROFILESECTION *section;
69 WCHAR *filename;
70 FILETIME LastWriteTime;
71 ENCODING encoding;
72 } PROFILE;
73
74
75 #define N_CACHED_PROFILES 10
76
77 /* Cached profile files */
78 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
79
80 #define CurProfile (MRUProfile[0])
81
82 /* Check for comments in profile */
83 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
84
85 static const WCHAR emptystringW[] = {0};
86 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
87
88 static CRITICAL_SECTION PROFILE_CritSect;
89 static CRITICAL_SECTION_DEBUG critsect_debug =
90 {
91 0, 0, &PROFILE_CritSect,
92 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
93 0, 0, { (DWORD_PTR)(__FILE__ ": PROFILE_CritSect") }
94 };
95 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
96
97 static const char hex[16] = "0123456789ABCDEF";
98
99 /***********************************************************************
100 * PROFILE_CopyEntry
101 *
102 * Copy the content of an entry into a buffer, removing quotes, and possibly
103 * translating environment variables.
104 */
105 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
106 BOOL strip_quote )
107 {
108 WCHAR quote = '\0';
109
110 if(!buffer) return;
111
112 if (strip_quote && ((*value == '\'') || (*value == '\"')))
113 {
114 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
115 }
116
117 lstrcpynW( buffer, value, len );
118 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
119 }
120
121 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
122 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
123 {
124 int i;
125 USHORT * shortbuffer = (USHORT *)buffer;
126 for (i = 0; i < len; i++)
127 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
128 }
129
130 /* writes any necessary encoding marker to the file */
131 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
132 {
133 DWORD dwBytesWritten;
134 WCHAR bom;
135 switch (encoding)
136 {
137 case ENCODING_ANSI:
138 break;
139 case ENCODING_UTF8:
140 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
141 break;
142 case ENCODING_UTF16LE:
143 bom = 0xFEFF;
144 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
145 break;
146 case ENCODING_UTF16BE:
147 bom = 0xFFFE;
148 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
149 break;
150 }
151 }
152
153 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
154 {
155 char * write_buffer;
156 int write_buffer_len;
157 DWORD dwBytesWritten;
158
159 TRACE("writing: %s\n", debugstr_wn(szLine, len));
160
161 switch (encoding)
162 {
163 case ENCODING_ANSI:
164 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
165 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
166 if (!write_buffer) return;
167 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
168 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
169 HeapFree(GetProcessHeap(), 0, write_buffer);
170 break;
171 case ENCODING_UTF8:
172 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
173 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
174 if (!write_buffer) return;
175 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
176 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
177 HeapFree(GetProcessHeap(), 0, write_buffer);
178 break;
179 case ENCODING_UTF16LE:
180 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
181 break;
182 case ENCODING_UTF16BE:
183 PROFILE_ByteSwapShortBuffer(szLine, len);
184 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
185 break;
186 default:
187 FIXME("encoding type %d not implemented\n", encoding);
188 }
189 }
190
191 /***********************************************************************
192 * PROFILE_Save
193 *
194 * Save a profile tree to a file.
195 */
196 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
197 {
198 PROFILEKEY *key;
199 WCHAR *buffer, *p;
200
201 PROFILE_WriteMarker(hFile, encoding);
202
203 for ( ; section; section = section->next)
204 {
205 int len = 0;
206
207 if (section->name[0]) len += strlenW(section->name) + 6;
208
209 for (key = section->key; key; key = key->next)
210 {
211 len += strlenW(key->name) + 2;
212 if (key->value) len += strlenW(key->value) + 1;
213 }
214
215 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
216 if (!buffer) return;
217
218 p = buffer;
219 if (section->name[0])
220 {
221 *p++ = '\r';
222 *p++ = '\n';
223 *p++ = '[';
224 strcpyW( p, section->name );
225 p += strlenW(p);
226 *p++ = ']';
227 *p++ = '\r';
228 *p++ = '\n';
229 }
230 for (key = section->key; key; key = key->next)
231 {
232 strcpyW( p, key->name );
233 p += strlenW(p);
234 if (key->value)
235 {
236 *p++ = '=';
237 strcpyW( p, key->value );
238 p += strlenW(p);
239 }
240 *p++ = '\r';
241 *p++ = '\n';
242 }
243 PROFILE_WriteLine( hFile, buffer, len, encoding );
244 HeapFree(GetProcessHeap(), 0, buffer);
245 }
246 }
247
248
249 /***********************************************************************
250 * PROFILE_Free
251 *
252 * Free a profile tree.
253 */
254 static void PROFILE_Free( PROFILESECTION *section )
255 {
256 PROFILESECTION *next_section;
257 PROFILEKEY *key, *next_key;
258
259 for ( ; section; section = next_section)
260 {
261 for (key = section->key; key; key = next_key)
262 {
263 next_key = key->next;
264 HeapFree( GetProcessHeap(), 0, key->value );
265 HeapFree( GetProcessHeap(), 0, key );
266 }
267 next_section = section->next;
268 HeapFree( GetProcessHeap(), 0, section );
269 }
270 }
271
272 /* returns 1 if a character white space else 0 */
273 static inline int PROFILE_isspaceW(WCHAR c)
274 {
275 if (isspaceW(c)) return 1;
276 if (c=='\r' || c==0x1a) return 1;
277 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
278 return 0;
279 }
280
281 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
282 {
283 int flags = IS_TEXT_UNICODE_SIGNATURE |
284 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
285 IS_TEXT_UNICODE_ODD_LENGTH;
286 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
287 {
288 *len = sizeof(bom_utf8);
289 return ENCODING_UTF8;
290 }
291 RtlIsTextUnicode(buffer, *len, &flags);
292 if (flags & IS_TEXT_UNICODE_SIGNATURE)
293 {
294 *len = sizeof(WCHAR);
295 return ENCODING_UTF16LE;
296 }
297 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
298 {
299 *len = sizeof(WCHAR);
300 return ENCODING_UTF16BE;
301 }
302 *len = 0;
303 return ENCODING_ANSI;
304 }
305
306
307 /***********************************************************************
308 * PROFILE_Load
309 *
310 * Load a profile tree from a file.
311 */
312 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
313 {
314 void *buffer_base, *pBuffer;
315 WCHAR * szFile;
316 const WCHAR *szLineStart, *szLineEnd;
317 const WCHAR *szValueStart, *szEnd, *next_line;
318 int line = 0, len;
319 PROFILESECTION *section, *first_section;
320 PROFILESECTION **next_section;
321 PROFILEKEY *key, *prev_key, **next_key;
322 DWORD dwFileSize;
323
324 TRACE("%p\n", hFile);
325
326 dwFileSize = GetFileSize(hFile, NULL);
327 if (dwFileSize == INVALID_FILE_SIZE)
328 return NULL;
329
330 buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
331 if (!buffer_base) return NULL;
332
333 if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
334 {
335 HeapFree(GetProcessHeap(), 0, buffer_base);
336 WARN("Error %d reading file\n", GetLastError());
337 return NULL;
338 }
339 len = dwFileSize;
340 *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
341 /* len is set to the number of bytes in the character marker.
342 * we want to skip these bytes */
343 pBuffer = (char *)buffer_base + len;
344 dwFileSize -= len;
345 switch (*pEncoding)
346 {
347 case ENCODING_ANSI:
348 TRACE("ANSI encoding\n");
349
350 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
351 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
352 if (!szFile)
353 {
354 HeapFree(GetProcessHeap(), 0, buffer_base);
355 return NULL;
356 }
357 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
358 szEnd = szFile + len;
359 break;
360 case ENCODING_UTF8:
361 TRACE("UTF8 encoding\n");
362
363 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
364 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
365 if (!szFile)
366 {
367 HeapFree(GetProcessHeap(), 0, buffer_base);
368 return NULL;
369 }
370 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
371 szEnd = szFile + len;
372 break;
373 case ENCODING_UTF16LE:
374 TRACE("UTF16 Little Endian encoding\n");
375 szFile = (WCHAR *)pBuffer;
376 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
377 break;
378 case ENCODING_UTF16BE:
379 TRACE("UTF16 Big Endian encoding\n");
380 szFile = (WCHAR *)pBuffer;
381 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
382 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
383 break;
384 default:
385 FIXME("encoding type %d not implemented\n", *pEncoding);
386 HeapFree(GetProcessHeap(), 0, buffer_base);
387 return NULL;
388 }
389
390 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
391 if(first_section == NULL)
392 {
393 if (szFile != pBuffer)
394 HeapFree(GetProcessHeap(), 0, szFile);
395 HeapFree(GetProcessHeap(), 0, buffer_base);
396 return NULL;
397 }
398 first_section->name[0] = 0;
399 first_section->key = NULL;
400 first_section->next = NULL;
401 next_section = &first_section->next;
402 next_key = &first_section->key;
403 prev_key = NULL;
404 next_line = szFile;
405
406 while (next_line < szEnd)
407 {
408 szLineStart = next_line;
409 next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
410 if (!next_line) next_line = szEnd;
411 else next_line++;
412 szLineEnd = next_line;
413
414 line++;
415
416 /* get rid of white space */
417 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
418 while ((szLineEnd > szLineStart) && ((szLineEnd[-1] == '\n') || PROFILE_isspaceW(szLineEnd[-1]))) szLineEnd--;
419
420 if (szLineStart >= szLineEnd) continue;
421
422 if (*szLineStart == '[') /* section start */
423 {
424 const WCHAR * szSectionEnd;
425 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
426 {
427 WARN("Invalid section header at line %d: %s\n",
428 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
429 }
430 else
431 {
432 szLineStart++;
433 len = (int)(szSectionEnd - szLineStart);
434 /* no need to allocate +1 for NULL terminating character as
435 * already included in structure */
436 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
437 break;
438 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
439 section->name[len] = '\0';
440 section->key = NULL;
441 section->next = NULL;
442 *next_section = section;
443 next_section = §ion->next;
444 next_key = §ion->key;
445 prev_key = NULL;
446
447 TRACE("New section: %s\n", debugstr_w(section->name));
448
449 continue;
450 }
451 }
452
453 /* get rid of white space after the name and before the start
454 * of the value */
455 len = szLineEnd - szLineStart;
456 if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
457 {
458 const WCHAR *szNameEnd = szValueStart;
459 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
460 len = szNameEnd - szLineStart;
461 szValueStart++;
462 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
463 }
464
465 if (len || !prev_key || *prev_key->name)
466 {
467 /* no need to allocate +1 for NULL terminating character as
468 * already included in structure */
469 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
470 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
471 key->name[len] = '\0';
472 if (szValueStart)
473 {
474 len = (int)(szLineEnd - szValueStart);
475 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
476 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
477 key->value[len] = '\0';
478 }
479 else key->value = NULL;
480
481 key->next = NULL;
482 *next_key = key;
483 next_key = &key->next;
484 prev_key = key;
485
486 TRACE("New key: name=%s, value=%s\n",
487 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
488 }
489 }
490 if (szFile != pBuffer)
491 HeapFree(GetProcessHeap(), 0, szFile);
492 HeapFree(GetProcessHeap(), 0, buffer_base);
493 return first_section;
494 }
495
496
497 /***********************************************************************
498 * PROFILE_DeleteSection
499 *
500 * Delete a section from a profile tree.
501 */
502 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
503 {
504 while (*section)
505 {
506 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
507 {
508 PROFILESECTION *to_del = *section;
509 *section = to_del->next;
510 to_del->next = NULL;
511 PROFILE_Free( to_del );
512 return TRUE;
513 }
514 section = &(*section)->next;
515 }
516 return FALSE;
517 }
518
519
520 /***********************************************************************
521 * PROFILE_DeleteKey
522 *
523 * Delete a key from a profile tree.
524 */
525 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
526 LPCWSTR section_name, LPCWSTR key_name )
527 {
528 while (*section)
529 {
530 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
531 {
532 PROFILEKEY **key = &(*section)->key;
533 while (*key)
534 {
535 if (!strcmpiW( (*key)->name, key_name ))
536 {
537 PROFILEKEY *to_del = *key;
538 *key = to_del->next;
539 HeapFree( GetProcessHeap(), 0, to_del->value);
540 HeapFree( GetProcessHeap(), 0, to_del );
541 return TRUE;
542 }
543 key = &(*key)->next;
544 }
545 }
546 section = &(*section)->next;
547 }
548 return FALSE;
549 }
550
551
552 /***********************************************************************
553 * PROFILE_DeleteAllKeys
554 *
555 * Delete all keys from a profile tree.
556 */
557 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
558 {
559 PROFILESECTION **section= &CurProfile->section;
560 while (*section)
561 {
562 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
563 {
564 PROFILEKEY **key = &(*section)->key;
565 while (*key)
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 CurProfile->changed =TRUE;
572 }
573 }
574 section = &(*section)->next;
575 }
576 }
577
578
579 /***********************************************************************
580 * PROFILE_Find
581 *
582 * Find a key in a profile tree, optionally creating it.
583 */
584 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
585 LPCWSTR key_name, BOOL create, BOOL create_always )
586 {
587 LPCWSTR p;
588 int seclen, keylen;
589
590 while (PROFILE_isspaceW(*section_name)) section_name++;
591 p = section_name + strlenW(section_name) - 1;
592 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
593 seclen = p - section_name + 1;
594
595 while (PROFILE_isspaceW(*key_name)) key_name++;
596 p = key_name + strlenW(key_name) - 1;
597 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
598 keylen = p - key_name + 1;
599
600 while (*section)
601 {
602 if ( ((*section)->name[0])
603 && (!(strncmpiW( (*section)->name, section_name, seclen )))
604 && (((*section)->name)[seclen] == '\0') )
605 {
606 PROFILEKEY **key = &(*section)->key;
607
608 while (*key)
609 {
610 /* If create_always is FALSE then we check if the keyname
611 * already exists. Otherwise we add it regardless of its
612 * existence, to allow keys to be added more than once in
613 * some cases.
614 */
615 if(!create_always)
616 {
617 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
618 && (((*key)->name)[keylen] == '\0') )
619 return *key;
620 }
621 key = &(*key)->next;
622 }
623 if (!create) return NULL;
624 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
625 return NULL;
626 strcpyW( (*key)->name, key_name );
627 (*key)->value = NULL;
628 (*key)->next = NULL;
629 return *key;
630 }
631 section = &(*section)->next;
632 }
633 if (!create) return NULL;
634 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
635 if(*section == NULL) return NULL;
636 strcpyW( (*section)->name, section_name );
637 (*section)->next = NULL;
638 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
639 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
640 {
641 HeapFree(GetProcessHeap(), 0, *section);
642 return NULL;
643 }
644 strcpyW( (*section)->key->name, key_name );
645 (*section)->key->value = NULL;
646 (*section)->key->next = NULL;
647 return (*section)->key;
648 }
649
650
651 /***********************************************************************
652 * PROFILE_FlushFile
653 *
654 * Flush the current profile to disk if changed.
655 */
656 static BOOL PROFILE_FlushFile(void)
657 {
658 HANDLE hFile = NULL;
659 FILETIME LastWriteTime;
660
661 if(!CurProfile)
662 {
663 WARN("No current profile!\n");
664 return FALSE;
665 }
666
667 if (!CurProfile->changed) return TRUE;
668
669 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
670
671 if (hFile == INVALID_HANDLE_VALUE)
672 {
673 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile->filename), GetLastError());
674 return FALSE;
675 }
676
677 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
678 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
679 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
680 CurProfile->LastWriteTime=LastWriteTime;
681 CloseHandle( hFile );
682 CurProfile->changed = FALSE;
683 return TRUE;
684 }
685
686
687 /***********************************************************************
688 * PROFILE_ReleaseFile
689 *
690 * Flush the current profile to disk and remove it from the cache.
691 */
692 static void PROFILE_ReleaseFile(void)
693 {
694 PROFILE_FlushFile();
695 PROFILE_Free( CurProfile->section );
696 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
697 CurProfile->changed = FALSE;
698 CurProfile->section = NULL;
699 CurProfile->filename = NULL;
700 CurProfile->encoding = ENCODING_ANSI;
701 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
702 }
703
704
705 /***********************************************************************
706 * PROFILE_Open
707 *
708 * Open a profile file, checking the cached file first.
709 */
710 static BOOL PROFILE_Open( LPCWSTR filename )
711 {
712 WCHAR windirW[MAX_PATH];
713 WCHAR buffer[MAX_PATH];
714 HANDLE hFile = INVALID_HANDLE_VALUE;
715 FILETIME LastWriteTime;
716 int i,j;
717 PROFILE *tempProfile;
718
719 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
720
721 /* First time around */
722
723 if(!CurProfile)
724 for(i=0;i<N_CACHED_PROFILES;i++)
725 {
726 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
727 if(MRUProfile[i] == NULL) break;
728 MRUProfile[i]->changed=FALSE;
729 MRUProfile[i]->section=NULL;
730 MRUProfile[i]->filename=NULL;
731 MRUProfile[i]->encoding=ENCODING_ANSI;
732 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
733 }
734
735 GetWindowsDirectoryW( windirW, MAX_PATH );
736
737 if (!filename)
738 filename = wininiW;
739
740 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
741 !strchrW(filename, '\\') && !strchrW(filename, '/'))
742 {
743 static const WCHAR wszSeparator[] = {'\\', 0};
744 strcpyW(buffer, windirW);
745 strcatW(buffer, wszSeparator);
746 strcatW(buffer, filename);
747 }
748 else
749 {
750 LPWSTR dummy;
751 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
752 }
753
754 TRACE("path: %s\n", debugstr_w(buffer));
755
756 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
757
758 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
759 {
760 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
761 return FALSE;
762 }
763
764 for(i=0;i<N_CACHED_PROFILES;i++)
765 {
766 if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
767 {
768 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
769 if(i)
770 {
771 PROFILE_FlushFile();
772 tempProfile=MRUProfile[i];
773 for(j=i;j>0;j--)
774 MRUProfile[j]=MRUProfile[j-1];
775 CurProfile=tempProfile;
776 }
777
778 if (hFile != INVALID_HANDLE_VALUE)
779 {
780 if (TRACE_ON(profile))
781 {
782 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
783 if (memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
784 TRACE("(%s): already opened (mru=%d)\n",
785 debugstr_w(buffer), i);
786 else
787 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
788 debugstr_w(buffer), i);
789 }
790 CloseHandle(hFile);
791 }
792 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
793 debugstr_w(buffer), i);
794 return TRUE;
795 }
796 }
797
798 /* Flush the old current profile */
799 PROFILE_FlushFile();
800
801 /* Make the oldest profile the current one only in order to get rid of it */
802 if(i==N_CACHED_PROFILES)
803 {
804 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
805 for(i=N_CACHED_PROFILES-1;i>0;i--)
806 MRUProfile[i]=MRUProfile[i-1];
807 CurProfile=tempProfile;
808 }
809 if(CurProfile->filename) PROFILE_ReleaseFile();
810
811 /* OK, now that CurProfile is definitely free we assign it our new file */
812 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
813 strcpyW( CurProfile->filename, buffer );
814
815 if (hFile != INVALID_HANDLE_VALUE)
816 {
817 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
818 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
819 CloseHandle(hFile);
820 }
821 else
822 {
823 /* Does not exist yet, we will create it in PROFILE_FlushFile */
824 WARN("profile file %s not found\n", debugstr_w(buffer) );
825 }
826 return TRUE;
827 }
828
829
830 /***********************************************************************
831 * PROFILE_GetSection
832 *
833 * Returns all keys of a section.
834 * If return_values is TRUE, also include the corresponding values.
835 */
836 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
837 LPWSTR buffer, UINT len, BOOL return_values, BOOL return_noequalkeys )
838 {
839 PROFILEKEY *key;
840
841 if(!buffer) return 0;
842
843 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
844
845 while (section)
846 {
847 if (section->name[0] && !strcmpiW( section->name, section_name ))
848 {
849 UINT oldlen = len;
850 for (key = section->key; key; key = key->next)
851 {
852 if (len <= 2) break;
853 if (!*key->name) continue; /* Skip empty lines */
854 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
855 if (!return_noequalkeys && !return_values && !key->value) continue; /* Skip lines w.o. '=' */
856 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
857 len -= strlenW(buffer) + 1;
858 buffer += strlenW(buffer) + 1;
859 if (len < 2)
860 break;
861 if (return_values && key->value) {
862 buffer[-1] = '=';
863 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
864 len -= strlenW(buffer) + 1;
865 buffer += strlenW(buffer) + 1;
866 }
867 }
868 *buffer = '\0';
869 if (len <= 1)
870 /*If either lpszSection or lpszKey is NULL and the supplied
871 destination buffer is too small to hold all the strings,
872 the last string is truncated and followed by two null characters.
873 In this case, the return value is equal to cchReturnBuffer
874 minus two. */
875 {