1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2005 Mike McCormack for CodeWeavers 5 * Copyright 2005 Aric Stewart for CodeWeavers 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 "msipriv.h" 23 24 WINE_DEFAULT_DEBUG_CHANNEL(msi); 25 26 /* types arranged by precedence */ 27 #define FORMAT_NULL 0x0001 28 #define FORMAT_LITERAL 0x0002 29 #define FORMAT_NUMBER 0x0004 30 #define FORMAT_LBRACK 0x0010 31 #define FORMAT_LBRACE 0x0020 32 #define FORMAT_RBRACK 0x0011 33 #define FORMAT_RBRACE 0x0021 34 #define FORMAT_ESCAPE 0x0040 35 #define FORMAT_PROPNULL 0x0080 36 #define FORMAT_ERROR 0x1000 37 #define FORMAT_FAIL 0x2000 38 39 #define left_type(x) (x & 0xF0) 40 41 typedef struct _tagFORMAT 42 { 43 MSIPACKAGE *package; 44 MSIRECORD *record; 45 LPWSTR deformatted; 46 int len; 47 int n; 48 BOOL propfailed; 49 BOOL groupfailed; 50 int groups; 51 } FORMAT; 52 53 typedef struct _tagFORMSTR 54 { 55 struct list entry; 56 int n; 57 int len; 58 int type; 59 BOOL propfound; 60 BOOL nonprop; 61 } FORMSTR; 62 63 typedef struct _tagSTACK 64 { 65 struct list items; 66 } STACK; 67 68 static STACK *create_stack(void) 69 { 70 STACK *stack = msi_alloc(sizeof(STACK)); 71 list_init(&stack->items); 72 return stack; 73 } 74 75 static void free_stack(STACK *stack) 76 { 77 while (!list_empty(&stack->items)) 78 { 79 FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); 80 list_remove(&str->entry); 81 msi_free(str); 82 } 83 84 msi_free(stack); 85 } 86 87 static void stack_push(STACK *stack, FORMSTR *str) 88 { 89 list_add_head(&stack->items, &str->entry); 90 } 91 92 static FORMSTR *stack_pop(STACK *stack) 93 { 94 FORMSTR *ret; 95 96 if (list_empty(&stack->items)) 97 return NULL; 98 99 ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); 100 list_remove(&ret->entry); 101 return ret; 102 } 103 104 static FORMSTR *stack_find(STACK *stack, int type) 105 { 106 FORMSTR *str; 107 108 LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry) 109 { 110 if (str->type == type) 111 return str; 112 } 113 114 return NULL; 115 } 116 117 static FORMSTR *stack_peek(STACK *stack) 118 { 119 return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry); 120 } 121 122 static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str) 123 { 124 return &format->deformatted[str->n]; 125 } 126 127 static WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len ) 128 { 129 WCHAR *val; 130 131 if (!str->len) return NULL; 132 if ((val = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) 133 { 134 memcpy( val, get_formstr_data(format, str), str->len * sizeof(WCHAR) ); 135 val[str->len] = 0; 136 *ret_len = str->len; 137 } 138 return val; 139 } 140 141 static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len ) 142 { 143 WCHAR *val, *ret; 144 DWORD len; 145 int field; 146 147 if (!(val = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; 148 lstrcpynW(val, get_formstr_data(format, str), str->len + 1); 149 field = atoiW( val ); 150 msi_free( val ); 151 152 if (MSI_RecordIsNull( format->record, field ) || 153 MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL; 154 155 len++; 156 if (!(ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL; 157 ret[0] = 0; 158 if (MSI_RecordGetStringW( format->record, field, ret, &len )) 159 { 160 msi_free( ret ); 161 return NULL; 162 } 163 *ret_len = len; 164 return ret; 165 } 166 167 static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len ) 168 { 169 WCHAR *prop, *ret; 170 DWORD len = 0; 171 UINT r; 172 173 if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; 174 lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 ); 175 176 r = msi_get_property( format->package->db, prop, NULL, &len ); 177 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) 178 { 179 msi_free( prop ); 180 return NULL; 181 } 182 len++; 183 if ((ret = msi_alloc( len * sizeof(WCHAR) ))) 184 msi_get_property( format->package->db, prop, ret, &len ); 185 msi_free( prop ); 186 *ret_len = len; 187 return ret; 188 } 189 190 static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len ) 191 { 192 WCHAR *key, *ret; 193 MSICOMPONENT *comp; 194 195 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; 196 lstrcpynW(key, get_formstr_data(format, str), str->len + 1); 197 198 if (!(comp = msi_get_loaded_component( format->package, key ))) 199 { 200 msi_free( key ); 201 return NULL; 202 } 203 if (comp->Action == INSTALLSTATE_SOURCE) 204 ret = msi_resolve_source_folder( format->package, comp->Directory, NULL ); 205 else 206 ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) ); 207 208 if (ret) *ret_len = strlenW( ret ); 209 else *ret_len = 0; 210 msi_free( key ); 211 return ret; 212 } 213 214 static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len ) 215 { 216 WCHAR *key, *ret = NULL; 217 const MSIFILE *file; 218 DWORD len = 0; 219 220 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; 221 lstrcpynW(key, get_formstr_data(format, str), str->len + 1); 222 223 if (!(file = msi_get_loaded_file( format->package, key ))) goto done; 224 if (!shortname) 225 { 226 if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret ); 227 goto done; 228 } 229 if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0) 230 { 231 if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret ); 232 goto done; 233 } 234 len++; 235 if ((ret = msi_alloc( len * sizeof(WCHAR) ))) 236 len = GetShortPathNameW( file->TargetPath, ret, len ); 237 238 done: 239 msi_free( key ); 240 *ret_len = len; 241 return ret; 242 } 243 244 static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len ) 245 { 246 WCHAR *key, *ret = NULL; 247 DWORD len; 248 249 if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL; 250 lstrcpynW(key, get_formstr_data(format, str), str->len + 1); 251 252 if ((len = GetEnvironmentVariableW( key, NULL, 0 ))) 253 { 254 len++; 255 if ((ret = msi_alloc( len * sizeof(WCHAR) ))) 256 *ret_len = GetEnvironmentVariableW( key, ret, len ); 257 } 258 msi_free( key ); 259 return ret; 260 } 261 262 static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound, 263 BOOL *nonprop, int *type, int *len ) 264 { 265 LPCWSTR data = get_formstr_data(format, str); 266 WCHAR *replaced = NULL; 267 char ch = data[0]; 268 269 if (ch == '\\') 270 { 271 str->n++; 272 if (str->len == 1) 273 { 274 str->len = 0; 275 replaced = NULL; 276 } 277 else 278 { 279 str->len = 1; 280 replaced = dup_formstr( format, str, len ); 281 } 282 } 283 else if (ch == '~') 284 { 285 if (str->len != 1) 286 replaced = NULL; 287 else if ((replaced = msi_alloc( sizeof(WCHAR) ))) 288 { 289 *replaced = 0; 290 *len = 0; 291 } 292 } 293 else if (ch == '%' || ch == '#' || ch == '!' || ch == '$') 294 { 295 str->n++; 296 str->len--; 297 298 switch (ch) 299 { 300 case '%': 301 replaced = deformat_environment( format, str, len ); break; 302 case '#': 303 replaced = deformat_file( format, str, FALSE, len ); break; 304 case '!': 305 replaced = deformat_file( format, str, TRUE, len ); break; 306 case '$': 307 replaced = deformat_component( format, str, len ); break; 308 } 309 310 *type = FORMAT_LITERAL; 311 } 312 else 313 { 314 replaced = deformat_property( format, str, len ); 315 *type = FORMAT_LITERAL; 316 317 if (replaced) 318 *propfound = TRUE; 319 else 320 format->propfailed = TRUE; 321 } 322 323 return replaced; 324 } 325 326 static LPWSTR build_default_format(const MSIRECORD* record) 327 { 328 int i; 329 int count; 330 LPWSTR rc, buf; 331 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0}; 332 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0}; 333 static const WCHAR fmt_index[] = {'%','i',0}; 334 LPCWSTR str; 335 WCHAR index[10]; 336 DWORD size, max_len, len; 337 338 count = MSI_RecordGetFieldCount(record); 339 340 max_len = MAX_PATH; 341 buf = msi_alloc((max_len + 1) * sizeof(WCHAR)); 342 343 rc = NULL; 344 size = 1; 345 for (i = 1; i <= count; i++) 346 { 347 sprintfW(index, fmt_index, i); 348 str = MSI_RecordGetString(record, i); 349 len = (str) ? lstrlenW(str) : 0; 350 len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index); 351 size += len; 352 353 if (len > max_len) 354 { 355 max_len = len; 356 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR)); 357 if (!buf) return NULL; 358 } 359 360 if (str) 361 sprintfW(buf, fmt, i, str); 362 else 363 sprintfW(buf, fmt_null, i); 364 365 if (!rc) 366 { 367 rc = msi_alloc(size * sizeof(WCHAR)); 368 lstrcpyW(rc, buf); 369 } 370 else 371 { 372 rc = msi_realloc(rc, size * sizeof(WCHAR)); 373 lstrcatW(rc, buf); 374 } 375 } 376 377 msi_free(buf); 378 return rc; 379 } 380 381 static BOOL format_is_number(WCHAR x) 382 { 383 return ((x >= '0') && (x <= '9')); 384 } 385 386 static BOOL format_str_is_number(LPWSTR str) 387 { 388 LPWSTR ptr; 389 390 for (ptr = str; *ptr; ptr++) 391 if (!format_is_number(*ptr)) 392 return FALSE; 393 394 return TRUE; 395 } 396 397 static BOOL format_is_alpha(WCHAR x) 398 { 399 return (!format_is_number(x) && x != '\0' && 400 x != '[' && x != ']' && x != '{' && x != '}'); 401 } 402 403 static BOOL format_is_literal(WCHAR x) 404 { 405 return (format_is_alpha(x) || format_is_number(x)); 406 } 407 408 static int format_lex(FORMAT *format, FORMSTR **out) 409 { 410 int type, len = 1; 411 FORMSTR *str; 412 LPCWSTR data; 413 WCHAR ch; 414 415 *out = NULL; 416 417 if (!format->deformatted) 418 return FORMAT_NULL; 419 420 *out = msi_alloc_zero(sizeof(FORMSTR)); 421 if (!*out) 422 return FORMAT_FAIL; 423 424 str = *out; 425 str->n = format->n; 426 str->len = 1; 427 data = get_formstr_data(format, str); 428 429 ch = data[0]; 430 switch (ch) 431 { 432 case '{': type = FORMAT_LBRACE; break; 433 case '}': type = FORMAT_RBRACE; break; 434 case '[': type = FORMAT_LBRACK; break; 435 case ']': type = FORMAT_RBRACK; break; 436 case '~': type = FORMAT_PROPNULL; break; 437 case '\0': type = FORMAT_NULL; break; 438 439 default: 440 type = 0; 441 } 442 443 if (type) 444 { 445 str->type = type; 446 format->n++; 447 return type; 448 } 449 450 if (ch == '\\') 451 { 452 while (data[len] && data[len] != ']') 453 len++; 454 455 type = FORMAT_ESCAPE; 456 } 457 else if (format_is_alpha(ch)) 458 { 459 while (format_is_literal(data[len])) 460 len++; 461 462 type = FORMAT_LITERAL; 463 } 464 else if (format_is_number(ch)) 465 { 466 while (format_is_number(data[len])) 467 len++; 468 469 type = FORMAT_NUMBER; 470 471 if (data[len] != ']') 472 { 473 while (format_is_literal(data[len])) 474 len++; 475 476 type = FORMAT_LITERAL; 477 } 478 } 479 else 480 { 481 ERR("Got unknown character %c(%x)\n", ch, ch); 482 return FORMAT_ERROR; 483 } 484 485 format->n += len; 486 str->len = len; 487 str->type = type; 488 489 return type; 490 } 491 492 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop, 493 int oldsize, int type, WCHAR *replace, int len ) 494 { 495 FORMSTR *ret; 496 LPWSTR str, ptr; 497 DWORD size = 0; 498 int n; 499 500 if (replace) 501 { 502 if (!len) 503 size = 1; 504 else 505 size = len; 506 } 507 508 size -= oldsize; 509 size = format->len + size + 1; 510 511 if (size <= 1) 512 { 513 msi_free(format->deformatted); 514 format->deformatted = NULL; 515 format->len = 0; 516 return NULL; 517 } 518 519 str = msi_alloc(size * sizeof(WCHAR)); 520 if (!str) 521 return NULL; 522 523 str[0] = '\0'; 524 memcpy(str, format->deformatted, format->n * sizeof(WCHAR)); 525 n = format->n; 526 527 if (replace) 528 { 529 if (!len) str[n++] = 0; 530 else 531 { 532 memcpy( str + n, replace, len * sizeof(WCHAR) ); 533 n += len; 534 str[n] = 0; 535 } 536 } 537 538 ptr = &format->deformatted[format->n + oldsize]; 539 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR)); 540 541 msi_free(format->deformatted); 542 format->deformatted = str; 543 format->len = size - 1; 544 545 /* don't reformat the NULL */ 546 if (replace && !len) 547 format->n++; 548 549 if (!replace) 550 return NULL; 551 552 ret = msi_alloc_zero(sizeof(FORMSTR)); 553 if (!ret) 554 return NULL; 555 556 ret->len = len; 557 ret->type = type; 558 ret->n = format->n; 559 ret->propfound = propfound; 560 ret->nonprop = nonprop; 561 562 return ret; 563 } 564 565 static WCHAR *replace_stack_group( FORMAT *format, STACK *values, 566 BOOL *propfound, BOOL *nonprop, 567 int *oldsize, int *type, int *len ) 568 { 569 WCHAR *replaced; 570 FORMSTR *content, *node; 571 int n; 572 573 *nonprop = FALSE; 574 *propfound = FALSE; 575 576 node = stack_pop(values); 577 n = node->n; 578 *oldsize = node->len; 579 msi_free(node); 580 581 while ((node = stack_pop(values))) 582 { 583 *oldsize += node->len; 584 585 if (node->nonprop) 586 *nonprop = TRUE; 587 588 if (node->propfound) 589 *propfound = TRUE; 590 591 msi_free(node); 592 } 593 594 content = msi_alloc_zero(sizeof(FORMSTR)); 595 content->n = n; 596 content->len = *oldsize; 597 content->type = FORMAT_LITERAL; 598 599 if (!format->groupfailed && (*oldsize == 2 || 600 (format->propfailed && !*nonprop))) 601 { 602 msi_free(content); 603 return NULL; 604 } 605 else if (format->deformatted[content->n + 1] == '{' && 606 format->deformatted[content->n + content->len - 2] == '}') 607 { 608 format->groupfailed = FALSE; 609 content->len = 0; 610 } 611 else if (*propfound && !*nonprop && 612 !format->groupfailed && format->groups == 0) 613 { 614 content->n++; 615 content->len -= 2; 616 } 617 else 618 { 619 if (format->groups != 0) 620 format->groupfailed = TRUE; 621 622 *nonprop = TRUE; 623 } 624 625 replaced = dup_formstr( format, content, len ); 626 *type = content->type; 627 msi_free(content); 628 629 if (format->groups == 0) 630 format->propfailed = FALSE; 631 632 return replaced; 633 } 634 635 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values, 636 BOOL *propfound, BOOL *nonprop, 637 int *oldsize, int *type, int *len ) 638 { 639 WCHAR *replaced; 640 FORMSTR *content, *node; 641 int n; 642 643 *propfound = FALSE; 644 *nonprop = FALSE; 645 646 node = stack_pop(values); 647 n = node->n; 648 *oldsize = node->len; 649 *type = stack_peek(values)->type; 650 msi_free(node); 651 652 while ((node = stack_pop(values))) 653 { 654 *oldsize += node->len; 655 656 if (*type != FORMAT_ESCAPE && 657 stack_peek(values) && node->type != *type) 658 *type = FORMAT_LITERAL; 659 660 msi_free(node); 661 } 662 663 content = msi_alloc_zero(sizeof(FORMSTR)); 664 content->n = n + 1; 665 content->len = *oldsize - 2; 666 content->type = *type; 667 668 if (*type == FORMAT_NUMBER) 669 { 670 replaced = deformat_index( format, content, len ); 671 if (replaced) 672 *propfound = TRUE; 673 else 674 format->propfailed = TRUE; 675 676 if (replaced) 677 *type = format_str_is_number(replaced) ? 678 FORMAT_NUMBER : FORMAT_LITERAL; 679 } 680 else if (format->package) 681 { 682 replaced = deformat_literal( format, content, propfound, nonprop, type, len ); 683 } 684 else 685 { 686 *nonprop = TRUE; 687 content->n--; 688 content->len += 2; 689 replaced = dup_formstr( format, content, len ); 690 } 691 msi_free(content); 692 return replaced; 693 } 694 695 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values) 696 { 697 WCHAR *replaced = NULL; 698 FORMSTR *beg, *top, *node; 699 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE; 700 int type, n, len = 0, oldsize = 0; 701 702 node = stack_peek(values); 703 type = node->type; 704 n = node->n; 705 706 if (type == FORMAT_LBRACK) 707 replaced = replace_stack_prop( format, values, &propfound, 708 &nonprop, &oldsize, &type, &len ); 709 else if (type == FORMAT_LBRACE) 710 { 711 replaced = replace_stack_group( format, values, &propfound, 712 &nonprop, &oldsize, &type, &len ); 713 group = TRUE; 714 } 715 716 format->n = n; 717 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len ); 718 if (!beg) 719 return ERROR_SUCCESS; 720 721 msi_free(replaced); 722 format->n = beg->n + beg->len; 723 724 top = stack_peek(stack); 725 if (top) 726 { 727 type = top->type; 728 729 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) && 730 type == beg->type) 731 { 732 top->len += beg->len; 733 734 if (group) 735 top->nonprop = FALSE; 736 737 if (type == FORMAT_LITERAL) 738 top->nonprop = beg->nonprop; 739 740 if (beg->propfound) 741 top->propfound = TRUE; 742 743 msi_free(beg); 744 return ERROR_SUCCESS; 745 } 746 } 747 748 stack_push(stack, beg); 749 return ERROR_SUCCESS; 750 } 751 752 static BOOL verify_format(LPWSTR data) 753 { 754 int count = 0; 755 756 while (*data) 757 { 758 if (*data == '[' && *(data - 1) != '\\') 759 count++; 760 else if (*data == ']') 761 count--; 762 763 data++; 764 } 765 766 if (count > 0) 767 return FALSE; 768 769 return TRUE; 770 } 771 772 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 773 WCHAR** data, DWORD *len, 774 MSIRECORD* record, INT* failcount) 775 { 776 FORMAT format; 777 FORMSTR *str = NULL; 778 STACK *stack, *temp; 779 FORMSTR *node; 780 int type; 781 782 if (!ptr) 783 { 784 *data = NULL; 785 *len = 0; 786 return ERROR_SUCCESS; 787 } 788 789 *data = strdupW(ptr); 790 *len = lstrlenW(ptr); 791 792 ZeroMemory(&format, sizeof(FORMAT)); 793 format.package = package; 794 format.record = record; 795 format.deformatted = *data; 796 format.len = *len; 797 798 if (!verify_format(*data)) 799 return ERROR_SUCCESS; 800 801 stack = create_stack(); 802 temp = create_stack(); 803 804 while ((type = format_lex(&format, &str)) != FORMAT_NULL) 805 { 806 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE || 807 type == FORMAT_LITERAL || type == FORMAT_NUMBER || 808 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL) 809 { 810 if (type == FORMAT_LBRACE) 811 { 812 format.propfailed = FALSE; 813 format.groups++; 814 } 815 else if (type == FORMAT_ESCAPE && 816 !stack_find(stack, FORMAT_LBRACK)) 817 { 818 format.n -= str->len - 1; 819 str->len = 1; 820 } 821 822 stack_push(stack, str); 823 } 824 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE) 825 { 826 if (type == FORMAT_RBRACE) 827 format.groups--; 828 829 stack_push(stack, str); 830 831 if (stack_find(stack, left_type(type))) 832 { 833 do 834 { 835 node = stack_pop(stack); 836 stack_push(temp, node); 837 } while (node->type != left_type(type)); 838 839 replace_stack(&format, stack, temp); 840 } 841 } 842 } 843 844 *data = format.deformatted; 845 *len = format.len; 846 847 msi_free(str); 848 free_stack(stack); 849 free_stack(temp); 850 851 return ERROR_SUCCESS; 852 } 853 854 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer, 855 LPDWORD size ) 856 { 857 WCHAR *format, *deformated; 858 UINT rc = ERROR_INVALID_PARAMETER; 859 DWORD len; 860 861 TRACE("%p %p %p %p\n", package, record, buffer, size); 862 863 if (!(format = msi_dup_record_field( record, 0 ))) 864 format = build_default_format( record ); 865 866 TRACE("%s\n", debugstr_w(format)); 867 868 deformat_string_internal( package, format, &deformated, &len, record, NULL ); 869 if (buffer) 870 { 871 if (*size>len) 872 { 873 memcpy(buffer,deformated,len*sizeof(WCHAR)); 874 rc = ERROR_SUCCESS; 875 buffer[len] = 0; 876 } 877 else 878 { 879 if (*size > 0) 880 { 881 memcpy(buffer,deformated,(*size)*sizeof(WCHAR)); 882 buffer[(*size)-1] = 0; 883 } 884 rc = ERROR_MORE_DATA; 885 } 886 } 887 else rc = ERROR_SUCCESS; 888 889 *size = len; 890 msi_free( format ); 891 msi_free( deformated ); 892 return rc; 893 } 894 895 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 896 LPWSTR szResult, LPDWORD sz ) 897 { 898 UINT r = ERROR_INVALID_HANDLE; 899 MSIPACKAGE *package; 900 MSIRECORD *record; 901 902 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); 903 904 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); 905 if (!package) 906 { 907 HRESULT hr; 908 IWineMsiRemotePackage *remote_package; 909 BSTR value = NULL; 910 awstring wstr; 911 912 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); 913 if (remote_package) 914 { 915 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord, 916 &value ); 917 if (FAILED(hr)) 918 goto done; 919 920 wstr.unicode = TRUE; 921 wstr.str.w = szResult; 922 r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz ); 923 924 done: 925 IWineMsiRemotePackage_Release( remote_package ); 926 SysFreeString( value ); 927 928 if (FAILED(hr)) 929 { 930 if (HRESULT_FACILITY(hr) == FACILITY_WIN32) 931 return HRESULT_CODE(hr); 932 933 return ERROR_FUNCTION_FAILED; 934 } 935 936 return r; 937 } 938 } 939 940 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); 941 942 if (!record) 943 return ERROR_INVALID_HANDLE; 944 if (!sz) 945 { 946 msiobj_release( &record->hdr ); 947 if (szResult) 948 return ERROR_INVALID_PARAMETER; 949 else 950 return ERROR_SUCCESS; 951 } 952 953 r = MSI_FormatRecordW( package, record, szResult, sz ); 954 msiobj_release( &record->hdr ); 955 if (package) 956 msiobj_release( &package->hdr ); 957 return r; 958 } 959 960 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord, 961 LPSTR szResult, LPDWORD sz ) 962 { 963 UINT r; 964 DWORD len, save; 965 LPWSTR value; 966 967 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); 968 969 if (!hRecord) 970 return ERROR_INVALID_HANDLE; 971 972 if (!sz) 973 { 974 if (szResult) 975 return ERROR_INVALID_PARAMETER; 976 else 977 return ERROR_SUCCESS; 978 } 979 980 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len ); 981 if (r != ERROR_SUCCESS) 982 return r; 983 984 value = msi_alloc(++len * sizeof(WCHAR)); 985 if (!value) 986 return ERROR_OUTOFMEMORY; 987 988 r = MsiFormatRecordW( hInstall, hRecord, value, &len ); 989 if (r != ERROR_SUCCESS) 990 goto done; 991 992 save = len + 1; 993 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL); 994 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL); 995 996 if (szResult && len > *sz) 997 { 998 if (*sz) szResult[*sz - 1] = '\0'; 999 r = ERROR_MORE_DATA; 1000 } 1001 1002 *sz = save - 1; 1003 1004 done: 1005 msi_free(value); 1006 return r; 1007 } 1008 1009 /* wrapper to resist a need for a full rewrite right now */ 1010 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data ) 1011 { 1012 DWORD len; 1013 MSIRECORD *rec; 1014 1015 *data = NULL; 1016 if (!fmt) return 0; 1017 if (!(rec = MSI_CreateRecord( 1 ))) return 0; 1018 1019 MSI_RecordSetStringW( rec, 0, fmt ); 1020 MSI_FormatRecordW( package, rec, NULL, &len ); 1021 if (!(*data = msi_alloc( ++len * sizeof(WCHAR) ))) 1022 { 1023 msiobj_release( &rec->hdr ); 1024 return 0; 1025 } 1026 MSI_FormatRecordW( package, rec, *data, &len ); 1027 msiobj_release( &rec->hdr ); 1028 return len; 1029 } 1030