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) 358 { 359 msi_free(rc); 360 return NULL; 361 } 362 } 363 364 if (str) 365 sprintfW(buf, fmt, i, str); 366 else 367 sprintfW(buf, fmt_null, i); 368 369 if (!rc) 370 { 371 rc = msi_alloc(size * sizeof(WCHAR)); 372 lstrcpyW(rc, buf); 373 } 374 else 375 { 376 rc = msi_realloc(rc, size * sizeof(WCHAR)); 377 lstrcatW(rc, buf); 378 } 379 } 380 381 msi_free(buf); 382 return rc; 383 } 384 385 static BOOL format_is_number(WCHAR x) 386 { 387 return ((x >= '0') && (x <= '9')); 388 } 389 390 static BOOL format_str_is_number(LPWSTR str) 391 { 392 LPWSTR ptr; 393 394 for (ptr = str; *ptr; ptr++) 395 if (!format_is_number(*ptr)) 396 return FALSE; 397 398 return TRUE; 399 } 400 401 static BOOL format_is_alpha(WCHAR x) 402 { 403 return (!format_is_number(x) && x != '\0' && 404 x != '[' && x != ']' && x != '{' && x != '}'); 405 } 406 407 static BOOL format_is_literal(WCHAR x) 408 { 409 return (format_is_alpha(x) || format_is_number(x)); 410 } 411 412 static int format_lex(FORMAT *format, FORMSTR **out) 413 { 414 int type, len = 1; 415 FORMSTR *str; 416 LPCWSTR data; 417 WCHAR ch; 418 419 *out = NULL; 420 421 if (!format->deformatted) 422 return FORMAT_NULL; 423 424 *out = msi_alloc_zero(sizeof(FORMSTR)); 425 if (!*out) 426 return FORMAT_FAIL; 427 428 str = *out; 429 str->n = format->n; 430 str->len = 1; 431 data = get_formstr_data(format, str); 432 433 ch = data[0]; 434 switch (ch) 435 { 436 case '{': type = FORMAT_LBRACE; break; 437 case '}': type = FORMAT_RBRACE; break; 438 case '[': type = FORMAT_LBRACK; break; 439 case ']': type = FORMAT_RBRACK; break; 440 case '~': type = FORMAT_PROPNULL; break; 441 case '\0': type = FORMAT_NULL; break; 442 443 default: 444 type = 0; 445 } 446 447 if (type) 448 { 449 str->type = type; 450 format->n++; 451 return type; 452 } 453 454 if (ch == '\\') 455 { 456 while (data[len] && data[len] != ']') 457 len++; 458 459 type = FORMAT_ESCAPE; 460 } 461 else if (format_is_alpha(ch)) 462 { 463 while (format_is_literal(data[len])) 464 len++; 465 466 type = FORMAT_LITERAL; 467 } 468 else if (format_is_number(ch)) 469 { 470 while (format_is_number(data[len])) 471 len++; 472 473 type = FORMAT_NUMBER; 474 475 if (data[len] != ']') 476 { 477 while (format_is_literal(data[len])) 478 len++; 479 480 type = FORMAT_LITERAL; 481 } 482 } 483 else 484 { 485 ERR("Got unknown character %c(%x)\n", ch, ch); 486 return FORMAT_ERROR; 487 } 488 489 format->n += len; 490 str->len = len; 491 str->type = type; 492 493 return type; 494 } 495 496 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop, 497 int oldsize, int type, WCHAR *replace, int len ) 498 { 499 FORMSTR *ret; 500 LPWSTR str, ptr; 501 DWORD size = 0; 502 int n; 503 504 if (replace) 505 { 506 if (!len) 507 size = 1; 508 else 509 size = len; 510 } 511 512 size -= oldsize; 513 size = format->len + size + 1; 514 515 if (size <= 1) 516 { 517 msi_free(format->deformatted); 518 format->deformatted = NULL; 519 format->len = 0; 520 return NULL; 521 } 522 523 str = msi_alloc(size * sizeof(WCHAR)); 524 if (!str) 525 return NULL; 526 527 str[0] = '\0'; 528 memcpy(str, format->deformatted, format->n * sizeof(WCHAR)); 529 n = format->n; 530 531 if (replace) 532 { 533 if (!len) str[n++] = 0; 534 else 535 { 536 memcpy( str + n, replace, len * sizeof(WCHAR) ); 537 n += len; 538 str[n] = 0; 539 } 540 } 541 542 ptr = &format->deformatted[format->n + oldsize]; 543 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR)); 544 545 msi_free(format->deformatted); 546 format->deformatted = str; 547 format->len = size - 1; 548 549 /* don't reformat the NULL */ 550 if (replace && !len) 551 format->n++; 552 553 if (!replace) 554 return NULL; 555 556 ret = msi_alloc_zero(sizeof(FORMSTR)); 557 if (!ret) 558 return NULL; 559 560 ret->len = len; 561 ret->type = type; 562 ret->n = format->n; 563 ret->propfound = propfound; 564 ret->nonprop = nonprop; 565 566 return ret; 567 } 568 569 static WCHAR *replace_stack_group( FORMAT *format, STACK *values, 570 BOOL *propfound, BOOL *nonprop, 571 int *oldsize, int *type, int *len ) 572 { 573 WCHAR *replaced; 574 FORMSTR *content, *node; 575 int n; 576 577 *nonprop = FALSE; 578 *propfound = FALSE; 579 580 node = stack_pop(values); 581 n = node->n; 582 *oldsize = node->len; 583 msi_free(node); 584 585 while ((node = stack_pop(values))) 586 { 587 *oldsize += node->len; 588 589 if (node->nonprop) 590 *nonprop = TRUE; 591 592 if (node->propfound) 593 *propfound = TRUE; 594 595 msi_free(node); 596 } 597 598 content = msi_alloc_zero(sizeof(FORMSTR)); 599 content->n = n; 600 content->len = *oldsize; 601 content->type = FORMAT_LITERAL; 602 603 if (!format->groupfailed && (*oldsize == 2 || 604 (format->propfailed && !*nonprop))) 605 { 606 msi_free(content); 607 return NULL; 608 } 609 else if (format->deformatted[content->n + 1] == '{' && 610 format->deformatted[content->n + content->len - 2] == '}') 611 { 612 format->groupfailed = FALSE; 613 content->len = 0; 614 } 615 else if (*propfound && !*nonprop && 616 !format->groupfailed && format->groups == 0) 617 { 618 content->n++; 619 content->len -= 2; 620 } 621 else 622 { 623 if (format->groups != 0) 624 format->groupfailed = TRUE; 625 626 *nonprop = TRUE; 627 } 628 629 replaced = dup_formstr( format, content, len ); 630 *type = content->type; 631 msi_free(content); 632 633 if (format->groups == 0) 634 format->propfailed = FALSE; 635 636 return replaced; 637 } 638 639 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values, 640 BOOL *propfound, BOOL *nonprop, 641 int *oldsize, int *type, int *len ) 642 { 643 WCHAR *replaced; 644 FORMSTR *content, *node; 645 int n; 646 647 *propfound = FALSE; 648 *nonprop = FALSE; 649 650 node = stack_pop(values); 651 n = node->n; 652 *oldsize = node->len; 653 *type = stack_peek(values)->type; 654 msi_free(node); 655 656 while ((node = stack_pop(values))) 657 { 658 *oldsize += node->len; 659 660 if (*type != FORMAT_ESCAPE && 661 stack_peek(values) && node->type != *type) 662 *type = FORMAT_LITERAL; 663 664 msi_free(node); 665 } 666 667 content = msi_alloc_zero(sizeof(FORMSTR)); 668 content->n = n + 1; 669 content->len = *oldsize - 2; 670 content->type = *type; 671 672 if (*type == FORMAT_NUMBER) 673 { 674 replaced = deformat_index( format, content, len ); 675 if (replaced) 676 *propfound = TRUE; 677 else 678 format->propfailed = TRUE; 679 680 if (replaced) 681 *type = format_str_is_number(replaced) ? 682 FORMAT_NUMBER : FORMAT_LITERAL; 683 } 684 else if (format->package) 685 { 686 replaced = deformat_literal( format, content, propfound, nonprop, type, len ); 687 } 688 else 689 { 690 *nonprop = TRUE; 691 content->n--; 692 content->len += 2; 693 replaced = dup_formstr( format, content, len ); 694 } 695 msi_free(content); 696 return replaced; 697 } 698 699 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values) 700 { 701 WCHAR *replaced = NULL; 702 FORMSTR *beg, *top, *node; 703 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE; 704 int type, n, len = 0, oldsize = 0; 705 706 node = stack_peek(values); 707 type = node->type; 708 n = node->n; 709 710 if (type == FORMAT_LBRACK) 711 replaced = replace_stack_prop( format, values, &propfound, 712 &nonprop, &oldsize, &type, &len ); 713 else if (type == FORMAT_LBRACE) 714 { 715 replaced = replace_stack_group( format, values, &propfound, 716 &nonprop, &oldsize, &type, &len ); 717 group = TRUE; 718 } 719 720 format->n = n; 721 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len ); 722 msi_free(replaced); 723 if (!beg) 724 return ERROR_SUCCESS; 725 726 format->n = beg->n + beg->len; 727 728 top = stack_peek(stack); 729 if (top) 730 { 731 type = top->type; 732 733 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) && 734 type == beg->type) 735 { 736 top->len += beg->len; 737 738 if (group) 739 top->nonprop = FALSE; 740 741 if (type == FORMAT_LITERAL) 742 top->nonprop = beg->nonprop; 743 744 if (beg->propfound) 745 top->propfound = TRUE; 746 747 msi_free(beg); 748 return ERROR_SUCCESS; 749 } 750 } 751 752 stack_push(stack, beg); 753 return ERROR_SUCCESS; 754 } 755 756 static BOOL verify_format(LPWSTR data) 757 { 758 int count = 0; 759 760 while (*data) 761 { 762 if (*data == '[' && *(data - 1) != '\\') 763 count++; 764 else if (*data == ']') 765 count--; 766 767 data++; 768 } 769 770 if (count > 0) 771 return FALSE; 772 773 return TRUE; 774 } 775 776 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 777 WCHAR** data, DWORD *len, 778 MSIRECORD* record, INT* failcount) 779 { 780 FORMAT format; 781 FORMSTR *str = NULL; 782 STACK *stack, *temp; 783 FORMSTR *node; 784 int type; 785 786 if (!ptr) 787 { 788 *data = NULL; 789 *len = 0; 790 return ERROR_SUCCESS; 791 } 792 793 *data = strdupW(ptr); 794 *len = lstrlenW(ptr); 795 796 ZeroMemory(&format, sizeof(FORMAT)); 797 format.package = package; 798 format.record = record; 799 format.deformatted = *data; 800 format.len = *len; 801 802 if (!verify_format(*data)) 803 return ERROR_SUCCESS; 804 805 stack = create_stack(); 806 temp = create_stack(); 807 808 while ((type = format_lex(&format, &str)) != FORMAT_NULL) 809 { 810 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE || 811 type == FORMAT_LITERAL || type == FORMAT_NUMBER || 812 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL) 813 { 814 if (type == FORMAT_LBRACE) 815 { 816 format.propfailed = FALSE; 817 format.groups++; 818 } 819 else if (type == FORMAT_ESCAPE && 820 !stack_find(stack, FORMAT_LBRACK)) 821 { 822 format.n -= str->len - 1; 823 str->len = 1; 824 } 825 826 stack_push(stack, str); 827 } 828 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE) 829 { 830 if (type == FORMAT_RBRACE) 831 format.groups--; 832 833 stack_push(stack, str); 834 835 if (stack_find(stack, left_type(type))) 836 { 837 do 838 { 839 node = stack_pop(stack); 840 stack_push(temp, node); 841 } while (node->type != left_type(type)); 842 843 replace_stack(&format, stack, temp); 844 } 845 } 846 } 847 848 *data = format.deformatted; 849 *len = format.len; 850 851 msi_free(str); 852 free_stack(stack); 853 free_stack(temp); 854 855 return ERROR_SUCCESS; 856 } 857 858 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer, 859 LPDWORD size ) 860 { 861 WCHAR *format, *deformated; 862 UINT rc = ERROR_INVALID_PARAMETER; 863 DWORD len; 864 865 TRACE("%p %p %p %p\n", package, record, buffer, size); 866 867 if (!(format = msi_dup_record_field( record, 0 ))) 868 format = build_default_format( record ); 869 870 TRACE("%s\n", debugstr_w(format)); 871 872 deformat_string_internal( package, format, &deformated, &len, record, NULL ); 873 if (buffer) 874 { 875 if (*size>len) 876 { 877 memcpy(buffer,deformated,len*sizeof(WCHAR)); 878 rc = ERROR_SUCCESS; 879 buffer[len] = 0; 880 } 881 else 882 { 883 if (*size > 0) 884 { 885 memcpy(buffer,deformated,(*size)*sizeof(WCHAR)); 886 buffer[(*size)-1] = 0; 887 } 888 rc = ERROR_MORE_DATA; 889 } 890 } 891 else rc = ERROR_SUCCESS; 892 893 *size = len; 894 msi_free( format ); 895 msi_free( deformated ); 896 return rc; 897 } 898 899 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 900 LPWSTR szResult, LPDWORD sz ) 901 { 902 UINT r = ERROR_INVALID_HANDLE; 903 MSIPACKAGE *package; 904 MSIRECORD *record; 905 906 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); 907 908 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); 909 if (!package) 910 { 911 HRESULT hr; 912 IWineMsiRemotePackage *remote_package; 913 BSTR value = NULL; 914 awstring wstr; 915 916 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall ); 917 if (remote_package) 918 { 919 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord, 920 &value ); 921 if (FAILED(hr)) 922 goto done; 923 924 wstr.unicode = TRUE; 925 wstr.str.w = szResult; 926 r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz ); 927 928 done: 929 IWineMsiRemotePackage_Release( remote_package ); 930 SysFreeString( value ); 931 932 if (FAILED(hr)) 933 { 934 if (HRESULT_FACILITY(hr) == FACILITY_WIN32) 935 return HRESULT_CODE(hr); 936 937 return ERROR_FUNCTION_FAILED; 938 } 939 940 return r; 941 } 942 } 943 944 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); 945 946 if (!record) 947 return ERROR_INVALID_HANDLE; 948 if (!sz) 949 { 950 msiobj_release( &record->hdr ); 951 if (szResult) 952 return ERROR_INVALID_PARAMETER; 953 else 954 return ERROR_SUCCESS; 955 } 956 957 r = MSI_FormatRecordW( package, record, szResult, sz ); 958 msiobj_release( &record->hdr ); 959 if (package) 960 msiobj_release( &package->hdr ); 961 return r; 962 } 963 964 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord, 965 LPSTR szResult, LPDWORD sz ) 966 { 967 UINT r; 968 DWORD len, save; 969 LPWSTR value; 970 971 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); 972 973 if (!hRecord) 974 return ERROR_INVALID_HANDLE; 975 976 if (!sz) 977 { 978 if (szResult) 979 return ERROR_INVALID_PARAMETER; 980 else 981 return ERROR_SUCCESS; 982 } 983 984 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len ); 985 if (r != ERROR_SUCCESS) 986 return r; 987 988 value = msi_alloc(++len * sizeof(WCHAR)); 989 if (!value) 990 return ERROR_OUTOFMEMORY; 991 992 r = MsiFormatRecordW( hInstall, hRecord, value, &len ); 993 if (r != ERROR_SUCCESS) 994 goto done; 995 996 save = len + 1; 997 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL); 998 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL); 999 1000 if (szResult && len > *sz) 1001 { 1002 if (*sz) szResult[*sz - 1] = '\0'; 1003 r = ERROR_MORE_DATA; 1004 } 1005 1006 *sz = save - 1; 1007 1008 done: 1009 msi_free(value); 1010 return r; 1011 } 1012 1013 /* wrapper to resist a need for a full rewrite right now */ 1014 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data ) 1015 { 1016 DWORD len; 1017 MSIRECORD *rec; 1018 1019 *data = NULL; 1020 if (!fmt) return 0; 1021 if (!(rec = MSI_CreateRecord( 1 ))) return 0; 1022 1023 MSI_RecordSetStringW( rec, 0, fmt ); 1024 MSI_FormatRecordW( package, rec, NULL, &len ); 1025 if (!(*data = msi_alloc( ++len * sizeof(WCHAR) ))) 1026 { 1027 msiobj_release( &rec->hdr ); 1028 return 0; 1029 } 1030 MSI_FormatRecordW( package, rec, *data, &len ); 1031 msiobj_release( &rec->hdr ); 1032 return len; 1033 } 1034