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