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