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