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