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