format.c 23.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
 * Copyright 2005 Mike McCormack for CodeWeavers
 * Copyright 2005 Aric Stewart for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 23 24 25 26 27 28 29 30 31 32
 */

#include <stdarg.h>
#include <stdio.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "msi.h"
#include "winnls.h"
33 34 35 36 37
#include "objbase.h"
#include "oleauto.h"

#include "msipriv.h"
#include "msiserver.h"
38 39 40 41
#include "wine/unicode.h"

WINE_DEFAULT_DEBUG_CHANNEL(msi);

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
/* types arranged by precedence */
#define FORMAT_NULL         0x0001
#define FORMAT_LITERAL      0x0002
#define FORMAT_NUMBER       0x0004
#define FORMAT_LBRACK       0x0010
#define FORMAT_LBRACE       0x0020
#define FORMAT_RBRACK       0x0011
#define FORMAT_RBRACE       0x0021
#define FORMAT_ESCAPE       0x0040
#define FORMAT_PROPNULL     0x0080
#define FORMAT_ERROR        0x1000
#define FORMAT_FAIL         0x2000

#define left_type(x) (x & 0xF0)

typedef struct _tagFORMAT
{
    MSIPACKAGE *package;
    MSIRECORD *record;
    LPWSTR deformatted;
    int len;
    int n;
    BOOL propfailed;
    BOOL groupfailed;
    int groups;
} FORMAT;

typedef struct _tagFORMSTR
{
    struct list entry;
    int n;
    int len;
    int type;
    BOOL propfound;
    BOOL nonprop;
} FORMSTR;

typedef struct _tagSTACK
{
    struct list items;
} STACK;
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static STACK *create_stack(void)
{
    STACK *stack = msi_alloc(sizeof(STACK));
    list_init(&stack->items);
    return stack;
}

static void free_stack(STACK *stack)
{
    while (!list_empty(&stack->items))
    {
        FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
        list_remove(&str->entry);
        msi_free(str);
    }

    msi_free(stack);
}

static void stack_push(STACK *stack, FORMSTR *str)
{
    list_add_head(&stack->items, &str->entry);
}

static FORMSTR *stack_pop(STACK *stack)
{
    FORMSTR *ret;

    if (list_empty(&stack->items))
        return NULL;

    ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
    list_remove(&ret->entry);
    return ret;
}

static FORMSTR *stack_find(STACK *stack, int type)
{
    FORMSTR *str;

    LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry)
    {
        if (str->type == type)
            return str;
    }

    return NULL;
}

static FORMSTR *stack_peek(STACK *stack)
{
    return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
}

static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str)
{
    return &format->deformatted[str->n];
}

static LPWSTR dup_formstr(FORMAT *format, FORMSTR *str)
{
    LPWSTR val;
    LPCWSTR data;

    if (str->len == 0)
        return NULL;

    val = msi_alloc((str->len + 1) * sizeof(WCHAR));
    data = get_formstr_data(format, str);
    lstrcpynW(val, data, str->len + 1);

    return val;
}

static LPWSTR deformat_index(FORMAT *format, FORMSTR *str)
{
    LPWSTR val, ret;

    val = msi_alloc((str->len + 1) * sizeof(WCHAR));
    lstrcpynW(val, get_formstr_data(format, str), str->len + 1);

    ret = msi_dup_record_field(format->record, atoiW(val));

    msi_free(val);
    return ret;
}

static LPWSTR deformat_property(FORMAT *format, FORMSTR *str)
{
    LPWSTR val, ret;

    val = msi_alloc((str->len + 1) * sizeof(WCHAR));
    lstrcpynW(val, get_formstr_data(format, str), str->len + 1);

178
    ret = msi_dup_property(format->package->db, val);
179 180 181 182 183 184 185 186 187 188 189 190 191

    msi_free(val);
    return ret;
}

static LPWSTR deformat_component(FORMAT *format, FORMSTR *str)
{
    LPWSTR key, ret = NULL;
    MSICOMPONENT *comp;

    key = msi_alloc((str->len + 1) * sizeof(WCHAR));
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

192
    comp = msi_get_loaded_component(format->package, key);
193 194 195
    if (!comp)
        goto done;

196
    if (comp->Action == INSTALLSTATE_SOURCE)
197
        ret = msi_resolve_source_folder( format->package, comp->Directory, NULL );
198
    else
199
        ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) );
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

done:
    msi_free(key);
    return ret;
}

static LPWSTR deformat_file(FORMAT *format, FORMSTR *str, BOOL shortname)
{
    LPWSTR key, ret = NULL;
    MSIFILE *file;
    DWORD size;

    key = msi_alloc((str->len + 1) * sizeof(WCHAR));
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

215
    file = msi_get_loaded_file(format->package, key);
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    if (!file)
        goto done;

    if (!shortname)
    {
        ret = strdupW(file->TargetPath);
        goto done;
    }

    size = GetShortPathNameW(file->TargetPath, NULL, 0);
    if (size <= 0)
    {
        ret = strdupW(file->TargetPath);
        goto done;
    }

    size++;
    ret = msi_alloc(size * sizeof(WCHAR));
    GetShortPathNameW(file->TargetPath, ret, size);

done:
    msi_free(key);
    return ret;
}

static LPWSTR deformat_environment(FORMAT *format, FORMSTR *str)
{
    LPWSTR key, ret = NULL;
    DWORD sz;

    key = msi_alloc((str->len + 1) * sizeof(WCHAR));
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

    sz  = GetEnvironmentVariableW(key, NULL ,0);
    if (sz <= 0)
        goto done;
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
    sz++;
    ret = msi_alloc(sz * sizeof(WCHAR));
    GetEnvironmentVariableW(key, ret, sz);

done:
    msi_free(key);
    return ret;
}

static LPWSTR deformat_literal(FORMAT *format, FORMSTR *str, BOOL *propfound,
                               BOOL *nonprop, int *type)
{
    LPCWSTR data = get_formstr_data(format, str);
    LPWSTR replaced = NULL;
    char ch = data[0];

    if (ch == '\\')
    {
        str->n++;
        if (str->len == 1)
        {
            str->len = 0;
            replaced = NULL;
        }
        else
        {
            str->len = 1;
            replaced = dup_formstr(format, str);
        }
    }
    else if (ch == '~')
    {
        if (str->len != 1)
            replaced = NULL;
        else
        {
            replaced = msi_alloc(sizeof(WCHAR));
            *replaced = '\0';
        }
    }
    else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
    {
        str->n++;
        str->len--;

        switch (ch)
        {
        case '%':
            replaced = deformat_environment(format, str); break;
        case '#':
            replaced = deformat_file(format, str, FALSE); break;
        case '!':
            replaced = deformat_file(format, str, TRUE); break;
        case '$':
            replaced = deformat_component(format, str); break;
        }

        *type = FORMAT_LITERAL;
    }
    else
    {
        replaced = deformat_property(format, str);
        *type = FORMAT_LITERAL;

        if (replaced)
            *propfound = TRUE;
        else
            format->propfailed = TRUE;
    }

    return replaced;
}
325

326
static LPWSTR build_default_format(const MSIRECORD* record)
327 328 329
{
    int i;  
    int count;
330 331 332 333 334 335 336
    LPWSTR rc, buf;
    static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
    static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
    static const WCHAR fmt_index[] = {'%','i',0};
    LPCWSTR str;
    WCHAR index[10];
    DWORD size, max_len, len;
337 338 339

    count = MSI_RecordGetFieldCount(record);

340 341 342 343 344
    max_len = MAX_PATH;
    buf = msi_alloc((max_len + 1) * sizeof(WCHAR));

    rc = NULL;
    size = 1;
345 346
    for (i = 1; i <= count; i++)
    {
347
        sprintfW(index, fmt_index, i);
348 349
        str = MSI_RecordGetString(record, i);
        len = (str) ? lstrlenW(str) : 0;
350
        len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index);
351 352 353 354 355 356 357 358 359 360
        size += len;

        if (len > max_len)
        {
            max_len = len;
            buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
            if (!buf) return NULL;
        }

        if (str)
361
            sprintfW(buf, fmt, i, str);
362
        else
363
            sprintfW(buf, fmt_null, i);
364 365 366 367 368 369 370 371 372 373 374

        if (!rc)
        {
            rc = msi_alloc(size * sizeof(WCHAR));
            lstrcpyW(rc, buf);
        }
        else
        {
            rc = msi_realloc(rc, size * sizeof(WCHAR));
            lstrcatW(rc, buf);
        }
375
    }
376

377
    msi_free(buf);
378 379 380
    return rc;
}

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 391 392
    for (ptr = str; *ptr; ptr++)
        if (!format_is_number(*ptr))
            return FALSE;
393

394 395
    return TRUE;
}
396

397
static BOOL format_is_alpha(WCHAR x)
398 399 400
{
    return (!format_is_number(x) && x != '\0' &&
            x != '[' && x != ']' && x != '{' && x != '}');
401 402
}

403
static BOOL format_is_literal(WCHAR x)
404
{
405 406
    return (format_is_alpha(x) || format_is_number(x));
}
407

408 409 410 411 412 413
static int format_lex(FORMAT *format, FORMSTR **out)
{
    int type, len = 1;
    FORMSTR *str;
    LPCWSTR data;
    WCHAR ch;
414

415
    *out = NULL;
416

417 418 419 420 421 422 423 424 425 426 427 428 429 430
    if (!format->deformatted)
        return FORMAT_NULL;

    *out = msi_alloc_zero(sizeof(FORMSTR));
    if (!*out)
        return FORMAT_FAIL;

    str = *out;
    str->n = format->n;
    str->len = 1;
    data = get_formstr_data(format, str);

    ch = data[0];
    switch (ch)
431
    {
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
        case '{': type = FORMAT_LBRACE; break;
        case '}': type = FORMAT_RBRACE; break;
        case '[': type = FORMAT_LBRACK; break;
        case ']': type = FORMAT_RBRACK; break;
        case '~': type = FORMAT_PROPNULL; break;
        case '\0': type = FORMAT_NULL; break;

        default:
            type = 0;
    }

    if (type)
    {
        str->type = type;
        format->n++;
        return type;
    }

    if (ch == '\\')
    {
        while (data[len] && data[len] != ']')
            len++;

        type = FORMAT_ESCAPE;
    }
    else if (format_is_alpha(ch))
    {
        while (format_is_literal(data[len]))
            len++;

        type = FORMAT_LITERAL;
    }
    else if (format_is_number(ch))
    {
        while (format_is_number(data[len]))
            len++;

        type = FORMAT_NUMBER;

        if (data[len] != ']')
472
        {
473 474
            while (format_is_literal(data[len]))
                len++;
475

476
            type = FORMAT_LITERAL;
477
        }
478
    }
479 480 481 482 483 484 485 486 487
    else
    {
        ERR("Got unknown character %c(%x)\n", ch, ch);
        return FORMAT_ERROR;
    }

    format->n += len;
    str->len = len;
    str->type = type;
488

489
    return type;
490 491
}

492 493
static FORMSTR *format_replace(FORMAT *format, BOOL propfound, BOOL nonprop,
                               int oldsize, int type, LPWSTR replace)
494
{
495 496 497 498
    FORMSTR *ret;
    LPWSTR str, ptr;
    DWORD size = 0;
    int n;
499

500
    if (replace)
501
    {
502 503 504 505
        if (!*replace)
            size = 1;
        else
            size = lstrlenW(replace);
506
    }
507 508 509 510 511

    size -= oldsize;
    size = format->len + size + 1;

    if (size <= 1)
512
    {
513 514 515 516
        msi_free(format->deformatted);
        format->deformatted = NULL;
        format->len = 0;
        return NULL;
517 518
    }

519 520 521
    str = msi_alloc(size * sizeof(WCHAR));
    if (!str)
        return NULL;
522

523 524 525
    str[0] = '\0';
    memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
    n = format->n;
526

527 528 529 530 531 532 533 534 535 536 537 538 539
    if (replace)
    {
        if (!*replace)
        {
            str[n] = '\0';
            n++;
        }
        else
        {
            lstrcpyW(&str[n], replace);
            n += lstrlenW(replace);
        }
    }
540

541 542
    ptr = &format->deformatted[format->n + oldsize];
    memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
543

544 545 546
    msi_free(format->deformatted);
    format->deformatted = str;
    format->len = size - 1;
547

548 549 550 551
    /* don't reformat the NULL */
    if (replace && !*replace)
        format->n++;

552 553
    if (!replace)
        return NULL;
554

555 556 557
    ret = msi_alloc_zero(sizeof(FORMSTR));
    if (!ret)
        return NULL;
558

559 560 561 562 563 564 565
    ret->len = lstrlenW(replace);
    ret->type = type;
    ret->n = format->n;
    ret->propfound = propfound;
    ret->nonprop = nonprop;

    return ret;
566 567
}

568 569 570
static LPWSTR replace_stack_group(FORMAT *format, STACK *values,
                                  BOOL *propfound, BOOL *nonprop,
                                  int *oldsize, int *type)
571
{
572 573 574 575
    LPWSTR replaced = NULL;
    FORMSTR *content;
    FORMSTR *node;
    int n;
576

577 578 579 580 581 582 583 584 585
    *nonprop = FALSE;
    *propfound = FALSE;

    node = stack_pop(values);
    n = node->n;
    *oldsize = node->len;
    msi_free(node);

    while ((node = stack_pop(values)))
586
    {
587 588 589 590 591 592 593 594 595
        *oldsize += node->len;

        if (node->nonprop)
            *nonprop = TRUE;

        if (node->propfound)
            *propfound = TRUE;

        msi_free(node);
596 597
    }

598 599 600 601
    content = msi_alloc_zero(sizeof(FORMSTR));
    content->n = n;
    content->len = *oldsize;
    content->type = FORMAT_LITERAL;
602

603 604 605 606
    if (!format->groupfailed && (*oldsize == 2 ||
        (format->propfailed && !*nonprop)))
    {
        msi_free(content);
607
        return NULL;
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
    }
    else if (format->deformatted[content->n + 1] == '{' &&
             format->deformatted[content->n + content->len - 2] == '}')
    {
        format->groupfailed = FALSE;
        content->len = 0;
    }
    else if (*propfound && !*nonprop &&
             !format->groupfailed && format->groups == 0)
    {
        content->n++;
        content->len -= 2;
    }
    else
    {
        if (format->groups != 0)
            format->groupfailed = TRUE;
625

626 627
        *nonprop = TRUE;
    }
628

629 630 631
    replaced = dup_formstr(format, content);
    *type = content->type;
    msi_free(content);
632

633 634 635 636
    if (format->groups == 0)
        format->propfailed = FALSE;

    return replaced;
637 638
}

639 640 641
static LPWSTR replace_stack_prop(FORMAT *format, STACK *values,
                                 BOOL *propfound, BOOL *nonprop,
                                 int *oldsize, int *type)
642
{
643 644 645 646
    LPWSTR replaced = NULL;
    FORMSTR *content;
    FORMSTR *node;
    int n;
647

648 649
    *propfound = FALSE;
    *nonprop = FALSE;
650

651 652 653 654 655 656 657
    node = stack_pop(values);
    n = node->n;
    *oldsize = node->len;
    *type = stack_peek(values)->type;
    msi_free(node);

    while ((node = stack_pop(values)))
658
    {
659 660 661 662 663 664 665
        *oldsize += node->len;

        if (*type != FORMAT_ESCAPE &&
            stack_peek(values) && node->type != *type)
            *type = FORMAT_LITERAL;

        msi_free(node);
666 667
    }

668 669 670 671
    content = msi_alloc_zero(sizeof(FORMSTR));
    content->n = n + 1;
    content->len = *oldsize - 2;
    content->type = *type;
672

673 674 675 676 677 678 679
    if (*type == FORMAT_NUMBER)
    {
        replaced = deformat_index(format, content);
        if (replaced)
            *propfound = TRUE;
        else
            format->propfailed = TRUE;
680

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
        if (replaced)
            *type = format_str_is_number(replaced) ?
                FORMAT_NUMBER : FORMAT_LITERAL;
    }
    else if (format->package)
    {
        replaced = deformat_literal(format, content, propfound, nonprop, type);
    }
    else
    {
        *nonprop = TRUE;
        content->n--;
        content->len += 2;
        replaced = dup_formstr(format, content);
    }
696

697 698
    msi_free(content);
    return replaced;
699 700
}

701
static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
702
{
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
    LPWSTR replaced = NULL;
    FORMSTR *beg;
    FORMSTR *top;
    FORMSTR *node;
    BOOL propfound = FALSE;
    BOOL nonprop = FALSE;
    BOOL group = FALSE;
    int oldsize = 0;
    int type, n;

    node = stack_peek(values);
    type = node->type;
    n = node->n;

    if (type == FORMAT_LBRACK)
        replaced = replace_stack_prop(format, values, &propfound,
                                      &nonprop, &oldsize, &type);
    else if (type == FORMAT_LBRACE)
    {
        replaced = replace_stack_group(format, values, &propfound,
                                       &nonprop, &oldsize, &type);
        group = TRUE;
    }
726

727 728 729 730 731 732 733 734 735 736
    format->n = n;
    beg = format_replace(format, propfound, nonprop, oldsize, type, replaced);
    if (!beg)
        return ERROR_SUCCESS;

    msi_free(replaced);
    format->n = beg->n + beg->len;

    top = stack_peek(stack);
    if (top)
737
    {
738 739 740 741
        type = top->type;

        if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
            type == beg->type)
742
        {
743
            top->len += beg->len;
744

745 746
            if (group)
                top->nonprop = FALSE;
747

748 749
            if (type == FORMAT_LITERAL)
                top->nonprop = beg->nonprop;
750

751 752
            if (beg->propfound)
                top->propfound = TRUE;
753

754 755 756 757 758 759 760
            msi_free(beg);
            return ERROR_SUCCESS;
        }
    }

    stack_push(stack, beg);
    return ERROR_SUCCESS;
761 762
}

763
static BOOL verify_format(LPWSTR data)
764
{
765
    int count = 0;
766

767
    while (*data)
768
    {
769 770 771 772 773 774
        if (*data == '[' && *(data - 1) != '\\')
            count++;
        else if (*data == ']')
            count--;

        data++;
775 776
    }

777 778 779 780 781
    if (count > 0)
        return FALSE;

    return TRUE;
}
782

783
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
784 785 786 787 788 789 790 791
                                      WCHAR** data, DWORD *len,
                                      MSIRECORD* record, INT* failcount)
{
    FORMAT format;
    FORMSTR *str = NULL;
    STACK *stack, *temp;
    FORMSTR *node;
    int type;
792

793
    if (!ptr)
794 795
    {
        *data = NULL;
796 797
        *len = 0;
        return ERROR_SUCCESS;
798 799
    }

800 801
    *data = strdupW(ptr);
    *len = lstrlenW(ptr);
802

803 804 805 806 807
    ZeroMemory(&format, sizeof(FORMAT));
    format.package = package;
    format.record = record;
    format.deformatted = *data;
    format.len = *len;
808

809 810
    if (!verify_format(*data))
        return ERROR_SUCCESS;
811

812 813 814
    stack = create_stack();
    temp = create_stack();

815 816 817 818 819
    while ((type = format_lex(&format, &str)) != FORMAT_NULL)
    {
        if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
            type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
            type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
820
        {
821
            if (type == FORMAT_LBRACE)
822
            {
823 824
                format.propfailed = FALSE;
                format.groups++;
825
            }
826 827
            else if (type == FORMAT_ESCAPE &&
                     !stack_find(stack, FORMAT_LBRACK))
828
            {
829 830
                format.n -= str->len - 1;
                str->len = 1;
831
            }
832 833

            stack_push(stack, str);
834
        }
835
        else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
836
        {
837 838
            if (type == FORMAT_RBRACE)
                format.groups--;
839

840
            stack_push(stack, str);
841

842 843 844 845 846 847 848
            if (stack_find(stack, left_type(type)))
            {
                do
                {
                    node = stack_pop(stack);
                    stack_push(temp, node);
                } while (node->type != left_type(type));
849

850 851 852
                replace_stack(&format, stack, temp);
            }
        }
853 854
    }

855 856
    *data = format.deformatted;
    *len = format.len;
857

858 859 860
    msi_free(str);
    free_stack(stack);
    free_stack(temp);
861

862 863
    return ERROR_SUCCESS;
}
864

865
UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
866
                        LPDWORD size )
867 868 869 870 871 872
{
    LPWSTR deformated;
    LPWSTR rec;
    DWORD len;
    UINT rc = ERROR_INVALID_PARAMETER;

873
    TRACE("%p %p %p %p\n", package, record, buffer, size);
874

875
    rec = msi_dup_record_field(record,0);
876
    if (!rec)
877
        rec = build_default_format(record);
878 879 880

    TRACE("(%s)\n",debugstr_w(rec));

881
    deformat_string_internal(package, rec, &deformated, &len, record, NULL);
882
    if (buffer)
883
    {
884 885 886 887 888 889 890 891
        if (*size>len)
        {
            memcpy(buffer,deformated,len*sizeof(WCHAR));
            rc = ERROR_SUCCESS;
            buffer[len] = 0;
        }
        else
        {
892 893 894 895 896
            if (*size > 0)
            {
                memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
                buffer[(*size)-1] = 0;
            }
897 898
            rc = ERROR_MORE_DATA;
        }
899 900
    }
    else
901 902 903
        rc = ERROR_SUCCESS;

    *size = len;
904

905 906
    msi_free(rec);
    msi_free(deformated);
907 908 909
    return rc;
}

910
UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 
911
                              LPWSTR szResult, LPDWORD sz )
912 913 914 915 916
{
    UINT r = ERROR_INVALID_HANDLE;
    MSIPACKAGE *package;
    MSIRECORD *record;

917
    TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
918

919 920 921 922 923 924 925 926 927 928 929 930
    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
    if (!package)
    {
        HRESULT hr;
        IWineMsiRemotePackage *remote_package;
        BSTR value = NULL;
        awstring wstr;

        remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
        if (remote_package)
        {
            hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
931
                                                     &value );
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
            if (FAILED(hr))
                goto done;

            wstr.unicode = TRUE;
            wstr.str.w = szResult;
            r = msi_strcpy_to_awstring( value, &wstr, sz );

done:
            IWineMsiRemotePackage_Release( remote_package );
            SysFreeString( value );

            if (FAILED(hr))
            {
                if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
                    return HRESULT_CODE(hr);

                return ERROR_FUNCTION_FAILED;
            }

            return r;
        }
    }

955 956 957 958 959 960 961 962 963 964 965 966 967
    record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );

    if (!record)
        return ERROR_INVALID_HANDLE;
    if (!sz)
    {
        msiobj_release( &record->hdr );
        if (szResult)
            return ERROR_INVALID_PARAMETER;
        else
            return ERROR_SUCCESS;
    }

968 969
    r = MSI_FormatRecordW( package, record, szResult, sz );
    msiobj_release( &record->hdr );
970 971 972
    if (package)
        msiobj_release( &package->hdr );
    return r;
973 974
}

975
UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
976
                              LPSTR szResult, LPDWORD sz )
977
{
978 979 980
    UINT r;
    DWORD len, save;
    LPWSTR value;
981

982
    TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
983

984
    if (!hRecord)
985
        return ERROR_INVALID_HANDLE;
986

987 988 989 990 991 992 993
    if (!sz)
    {
        if (szResult)
            return ERROR_INVALID_PARAMETER;
        else
            return ERROR_SUCCESS;
    }
994

995 996 997
    r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
    if (r != ERROR_SUCCESS)
        return r;
998

999 1000 1001 1002 1003 1004 1005 1006 1007
    value = msi_alloc(++len * sizeof(WCHAR));
    if (!value)
        return ERROR_OUTOFMEMORY;

    r = MsiFormatRecordW( hInstall, hRecord, value, &len );
    if (r != ERROR_SUCCESS)
        goto done;

    save = len + 1;
1008 1009
    len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
    WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

    if (szResult && len > *sz)
    {
        if (*sz) szResult[*sz - 1] = '\0';
        r = ERROR_MORE_DATA;
    }

    *sz = save - 1;

done:
    msi_free(value);
1021
    return r;
1022
}
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

/* wrapper to resist a need for a full rewrite right now */
DWORD deformat_string( MSIPACKAGE *package, const WCHAR *ptr, WCHAR **data )
{
    if (ptr)
    {
        DWORD size = 0;
        MSIRECORD *rec = MSI_CreateRecord( 1 );

        MSI_RecordSetStringW( rec, 0, ptr );
        MSI_FormatRecordW( package, rec, NULL, &size );

        size++;
        *data = msi_alloc( size * sizeof(WCHAR) );
        if (size > 1) MSI_FormatRecordW( package, rec, *data, &size );
        else *data[0] = 0;

        msiobj_release( &rec->hdr );
        return size * sizeof(WCHAR);
    }
    *data = NULL;
    return 0;
}