format.c 24.4 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
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];
}

143
static WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len )
144
{
145
    WCHAR *val;
146

147 148 149 150 151 152 153
    if (!str->len) return NULL;
    if ((val = msi_alloc( (str->len + 1) * sizeof(WCHAR) )))
    {
        memcpy( val, get_formstr_data(format, str), str->len * sizeof(WCHAR) );
        val[str->len] = 0;
        *ret_len = str->len;
    }
154 155 156
    return val;
}

157
static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len )
158
{
159 160 161
    WCHAR *val, *ret;
    DWORD len;
    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 166
    field = atoiW( val );
    msi_free( val );
167

168 169
    if (MSI_RecordIsNull( format->record, field ) ||
        MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL;
170

171 172 173 174 175 176 177 178 179
    len++;
    if (!(ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
    ret[0] = 0;
    if (MSI_RecordGetStringW( format->record, field, ret, &len ))
    {
        msi_free( ret );
        return NULL;
    }
    *ret_len = len;
180 181 182
    return ret;
}

183
static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len )
184
{
185 186 187
    WCHAR *prop, *ret;
    DWORD len = 0;
    UINT r;
188

189 190
    if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
    lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 );
191

192 193 194 195 196 197 198 199 200 201 202
    r = msi_get_property( format->package->db, prop, NULL, &len );
    if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
    {
        msi_free( prop );
        return NULL;
    }
    len++;
    if ((ret = msi_alloc( len * sizeof(WCHAR) )))
        msi_get_property( format->package->db, prop, ret, &len );
    msi_free( prop );
    *ret_len = len;
203 204 205
    return ret;
}

206
static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len )
207
{
208
    WCHAR *key, *ret;
209 210
    MSICOMPONENT *comp;

211
    if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
212 213
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

214 215 216 217 218
    if (!(comp = msi_get_loaded_component( format->package, key )))
    {
        msi_free( key );
        return NULL;
    }
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 225
    if (ret) *ret_len = strlenW( ret );
    else *ret_len = 0;
226
    msi_free( key );
227 228 229
    return ret;
}

230
static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len )
231
{
232 233 234
    WCHAR *key, *ret = NULL;
    const MSIFILE *file;
    DWORD len = 0;
235

236
    if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
237 238
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

239
    if (!(file = msi_get_loaded_file( format->package, key ))) goto done;
240 241
    if (!shortname)
    {
242
        if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret );
243 244
        goto done;
    }
245
    if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0)
246
    {
247
        if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret );
248 249
        goto done;
    }
250 251 252
    len++;
    if ((ret = msi_alloc( len * sizeof(WCHAR) )))
        len = GetShortPathNameW( file->TargetPath, ret, len );
253 254

done:
255 256
    msi_free( key );
    *ret_len = len;
257 258 259
    return ret;
}

260
static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len )
261
{
262 263
    WCHAR *key, *ret = NULL;
    DWORD len;
264

265
    if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL;
266 267
    lstrcpynW(key, get_formstr_data(format, str), str->len + 1);

268 269 270 271 272 273 274
    if ((len = GetEnvironmentVariableW( key, NULL, 0 )))
    {
        len++;
        if ((ret = msi_alloc( len * sizeof(WCHAR) )))
            *ret_len = GetEnvironmentVariableW( key, ret, len );
    }
    msi_free( key );
275 276 277
    return ret;
}

278 279
static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound,
                                BOOL *nonprop, int *type, int *len )
280 281
{
    LPCWSTR data = get_formstr_data(format, str);
282
    WCHAR *replaced = NULL;
283 284 285 286 287 288 289 290 291 292 293 294 295
    char ch = data[0];

    if (ch == '\\')
    {
        str->n++;
        if (str->len == 1)
        {
            str->len = 0;
            replaced = NULL;
        }
        else
        {
            str->len = 1;
296
            replaced = dup_formstr( format, str, len );
297 298 299 300 301 302
        }
    }
    else if (ch == '~')
    {
        if (str->len != 1)
            replaced = NULL;
303
        else if ((replaced = msi_alloc( sizeof(WCHAR) )))
304
        {
305 306
            *replaced = 0;
            *len = 0;
307 308 309 310 311 312 313 314 315 316
        }
    }
    else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
    {
        str->n++;
        str->len--;

        switch (ch)
        {
        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 327 328 329
        }

        *type = FORMAT_LITERAL;
    }
    else
    {
330
        replaced = deformat_property( format, str, len );
331 332 333 334 335 336 337 338 339 340
        *type = FORMAT_LITERAL;

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

    return replaced;
}
341

342
static LPWSTR build_default_format(const MSIRECORD* record)
343 344 345
{
    int i;  
    int count;
346 347 348 349 350 351 352
    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;
353 354 355

    count = MSI_RecordGetFieldCount(record);

356 357 358 359 360
    max_len = MAX_PATH;
    buf = msi_alloc((max_len + 1) * sizeof(WCHAR));

    rc = NULL;
    size = 1;
361 362
    for (i = 1; i <= count; i++)
    {
363
        sprintfW(index, fmt_index, i);
364 365
        str = MSI_RecordGetString(record, i);
        len = (str) ? lstrlenW(str) : 0;
366
        len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index);
367 368 369 370 371 372
        size += len;

        if (len > max_len)
        {
            max_len = len;
            buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
373 374 375 376 377
            if (!buf)
            {
                msi_free(rc);
                return NULL;
            }
378 379 380
        }

        if (str)
381
            sprintfW(buf, fmt, i, str);
382
        else
383
            sprintfW(buf, fmt_null, i);
384 385 386 387 388 389 390 391 392 393 394

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

397
    msi_free(buf);
398 399 400
    return rc;
}

401
static BOOL format_is_number(WCHAR x)
402
{
403
    return ((x >= '0') && (x <= '9'));
404 405
}

406
static BOOL format_str_is_number(LPWSTR str)
407
{
408
    LPWSTR ptr;
409

410 411 412
    for (ptr = str; *ptr; ptr++)
        if (!format_is_number(*ptr))
            return FALSE;
413

414 415
    return TRUE;
}
416

417
static BOOL format_is_alpha(WCHAR x)
418 419 420
{
    return (!format_is_number(x) && x != '\0' &&
            x != '[' && x != ']' && x != '{' && x != '}');
421 422
}

423
static BOOL format_is_literal(WCHAR x)
424
{
425 426
    return (format_is_alpha(x) || format_is_number(x));
}
427

428 429 430 431 432 433
static int format_lex(FORMAT *format, FORMSTR **out)
{
    int type, len = 1;
    FORMSTR *str;
    LPCWSTR data;
    WCHAR ch;
434

435
    *out = NULL;
436

437 438 439 440 441 442 443 444 445 446 447 448 449 450
    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)
451
    {
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
        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] != ']')
492
        {
493 494
            while (format_is_literal(data[len]))
                len++;
495

496
            type = FORMAT_LITERAL;
497
        }
498
    }
499 500 501 502 503 504 505 506 507
    else
    {
        ERR("Got unknown character %c(%x)\n", ch, ch);
        return FORMAT_ERROR;
    }

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

509
    return type;
510 511
}

512 513
static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop,
                                int oldsize, int type, WCHAR *replace, int len )
514
{
515 516 517 518
    FORMSTR *ret;
    LPWSTR str, ptr;
    DWORD size = 0;
    int n;
519

520
    if (replace)
521
    {
522
        if (!len)
523 524
            size = 1;
        else
525
            size = len;
526
    }
527 528 529 530 531

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

    if (size <= 1)
532
    {
533 534 535 536
        msi_free(format->deformatted);
        format->deformatted = NULL;
        format->len = 0;
        return NULL;
537 538
    }

539 540 541
    str = msi_alloc(size * sizeof(WCHAR));
    if (!str)
        return NULL;
542

543 544 545
    str[0] = '\0';
    memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
    n = format->n;
546

547 548
    if (replace)
    {
549
        if (!len) str[n++] = 0;
550 551
        else
        {
552 553 554
            memcpy( str + n, replace, len * sizeof(WCHAR) );
            n += len;
            str[n] = 0;
555 556
        }
    }
557

558 559
    ptr = &format->deformatted[format->n + oldsize];
    memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
560

561 562 563
    msi_free(format->deformatted);
    format->deformatted = str;
    format->len = size - 1;
564

565
    /* don't reformat the NULL */
566
    if (replace && !len)
567 568
        format->n++;

569 570
    if (!replace)
        return NULL;
571

572 573 574
    ret = msi_alloc_zero(sizeof(FORMSTR));
    if (!ret)
        return NULL;
575

576
    ret->len = len;
577 578 579 580 581 582
    ret->type = type;
    ret->n = format->n;
    ret->propfound = propfound;
    ret->nonprop = nonprop;

    return ret;
583 584
}

585 586 587
static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
                                   BOOL *propfound, BOOL *nonprop,
                                   int *oldsize, int *type, int *len )
588
{
589 590
    WCHAR *replaced;
    FORMSTR *content, *node;
591
    int n;
592

593 594 595 596 597 598 599 600 601
    *nonprop = FALSE;
    *propfound = FALSE;

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

    while ((node = stack_pop(values)))
602
    {
603 604 605 606 607 608 609 610 611
        *oldsize += node->len;

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

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

        msi_free(node);
612 613
    }

614 615 616 617
    content = msi_alloc_zero(sizeof(FORMSTR));
    content->n = n;
    content->len = *oldsize;
    content->type = FORMAT_LITERAL;
618

619 620 621 622
    if (!format->groupfailed && (*oldsize == 2 ||
        (format->propfailed && !*nonprop)))
    {
        msi_free(content);
623
        return NULL;
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    }
    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;
641

642 643
        *nonprop = TRUE;
    }
644

645
    replaced = dup_formstr( format, content, len );
646 647
    *type = content->type;
    msi_free(content);
648

649 650 651 652
    if (format->groups == 0)
        format->propfailed = FALSE;

    return replaced;
653 654
}

655 656 657
static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
                                  BOOL *propfound, BOOL *nonprop,
                                  int *oldsize, int *type, int *len )
658
{
659 660
    WCHAR *replaced;
    FORMSTR *content, *node;
661
    int n;
662

663 664
    *propfound = FALSE;
    *nonprop = FALSE;
665

666 667 668 669 670 671 672
    node = stack_pop(values);
    n = node->n;
    *oldsize = node->len;
    *type = stack_peek(values)->type;
    msi_free(node);

    while ((node = stack_pop(values)))
673
    {
674 675 676 677 678 679 680
        *oldsize += node->len;

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

        msi_free(node);
681 682
    }

683 684 685 686
    content = msi_alloc_zero(sizeof(FORMSTR));
    content->n = n + 1;
    content->len = *oldsize - 2;
    content->type = *type;
687

688 689
    if (*type == FORMAT_NUMBER)
    {
690
        replaced = deformat_index( format, content, len );
691 692 693 694
        if (replaced)
            *propfound = TRUE;
        else
            format->propfailed = TRUE;
695

696 697 698 699 700 701
        if (replaced)
            *type = format_str_is_number(replaced) ?
                FORMAT_NUMBER : FORMAT_LITERAL;
    }
    else if (format->package)
    {
702
        replaced = deformat_literal( format, content, propfound, nonprop, type, len );
703 704 705 706 707 708
    }
    else
    {
        *nonprop = TRUE;
        content->n--;
        content->len += 2;
709
        replaced = dup_formstr( format, content, len );
710 711 712
    }
    msi_free(content);
    return replaced;
713 714
}

715
static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
716
{
717 718 719 720
    WCHAR *replaced = NULL;
    FORMSTR *beg, *top, *node;
    BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
    int type, n, len = 0, oldsize = 0;
721 722 723 724 725 726

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

    if (type == FORMAT_LBRACK)
727 728
        replaced = replace_stack_prop( format, values, &propfound,
                                       &nonprop, &oldsize, &type, &len );
729 730
    else if (type == FORMAT_LBRACE)
    {
731 732
        replaced = replace_stack_group( format, values, &propfound,
                                        &nonprop, &oldsize, &type, &len );
733 734
        group = TRUE;
    }
735

736
    format->n = n;
737
    beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
738
    msi_free(replaced);
739 740 741 742 743 744 745
    if (!beg)
        return ERROR_SUCCESS;

    format->n = beg->n + beg->len;

    top = stack_peek(stack);
    if (top)
746
    {
747 748 749 750
        type = top->type;

        if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
            type == beg->type)
751
        {
752
            top->len += beg->len;
753

754 755
            if (group)
                top->nonprop = FALSE;
756

757 758
            if (type == FORMAT_LITERAL)
                top->nonprop = beg->nonprop;
759

760 761
            if (beg->propfound)
                top->propfound = TRUE;
762

763 764 765 766 767 768 769
            msi_free(beg);
            return ERROR_SUCCESS;
        }
    }

    stack_push(stack, beg);
    return ERROR_SUCCESS;
770 771
}

772
static BOOL verify_format(LPWSTR data)
773
{
774
    int count = 0;
775

776
    while (*data)
777
    {
778 779 780 781 782 783
        if (*data == '[' && *(data - 1) != '\\')
            count++;
        else if (*data == ']')
            count--;

        data++;
784 785
    }

786 787 788 789 790
    if (count > 0)
        return FALSE;

    return TRUE;
}
791

792
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
793 794 795 796 797 798 799 800
                                      WCHAR** data, DWORD *len,
                                      MSIRECORD* record, INT* failcount)
{
    FORMAT format;
    FORMSTR *str = NULL;
    STACK *stack, *temp;
    FORMSTR *node;
    int type;
801

802
    if (!ptr)
803 804
    {
        *data = NULL;
805 806
        *len = 0;
        return ERROR_SUCCESS;
807 808
    }

809 810
    *data = strdupW(ptr);
    *len = lstrlenW(ptr);
811

812 813 814 815 816
    ZeroMemory(&format, sizeof(FORMAT));
    format.package = package;
    format.record = record;
    format.deformatted = *data;
    format.len = *len;
817

818 819
    if (!verify_format(*data))
        return ERROR_SUCCESS;
820

821 822 823
    stack = create_stack();
    temp = create_stack();

824 825 826 827 828
    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)
829
        {
830
            if (type == FORMAT_LBRACE)
831
            {
832 833
                format.propfailed = FALSE;
                format.groups++;
834
            }
835 836
            else if (type == FORMAT_ESCAPE &&
                     !stack_find(stack, FORMAT_LBRACK))
837
            {
838 839
                format.n -= str->len - 1;
                str->len = 1;
840
            }
841 842

            stack_push(stack, str);
843
        }
844
        else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
845
        {
846 847
            if (type == FORMAT_RBRACE)
                format.groups--;
848

849
            stack_push(stack, str);
850

851 852 853 854 855 856 857
            if (stack_find(stack, left_type(type)))
            {
                do
                {
                    node = stack_pop(stack);
                    stack_push(temp, node);
                } while (node->type != left_type(type));
858

859 860 861
                replace_stack(&format, stack, temp);
            }
        }
862 863
    }

864 865
    *data = format.deformatted;
    *len = format.len;
866

867 868 869
    msi_free(str);
    free_stack(stack);
    free_stack(temp);
870

871 872
    return ERROR_SUCCESS;
}
873

874
UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
875
                        LPDWORD size )
876
{
877
    WCHAR *format, *deformated;
878
    UINT rc = ERROR_INVALID_PARAMETER;
879
    DWORD len;
880

881
    TRACE("%p %p %p %p\n", package, record, buffer, size);
882

883 884
    if (!(format = msi_dup_record_field( record, 0 )))
        format = build_default_format( record );
885

886
    TRACE("%s\n", debugstr_w(format));
887

888
    deformat_string_internal( package, format, &deformated, &len, record, NULL );
889
    if (buffer)
890
    {
891 892 893 894 895 896 897 898
        if (*size>len)
        {
            memcpy(buffer,deformated,len*sizeof(WCHAR));
            rc = ERROR_SUCCESS;
            buffer[len] = 0;
        }
        else
        {
899 900 901 902 903
            if (*size > 0)
            {
                memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
                buffer[(*size)-1] = 0;
            }
904 905
            rc = ERROR_MORE_DATA;
        }
906
    }
907
    else rc = ERROR_SUCCESS;
908 909

    *size = len;
910 911
    msi_free( format );
    msi_free( deformated );
912 913 914
    return rc;
}

915
UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 
916
                              LPWSTR szResult, LPDWORD sz )
917 918 919 920 921
{
    UINT r = ERROR_INVALID_HANDLE;
    MSIPACKAGE *package;
    MSIRECORD *record;

922
    TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
923

924 925 926 927 928 929 930 931 932 933 934 935
    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,
936
                                                     &value );
937 938 939 940 941
            if (FAILED(hr))
                goto done;

            wstr.unicode = TRUE;
            wstr.str.w = szResult;
942
            r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz );
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959

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;
        }
    }

960 961 962 963 964 965 966 967 968 969 970 971 972
    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;
    }

973 974
    r = MSI_FormatRecordW( package, record, szResult, sz );
    msiobj_release( &record->hdr );
975 976 977
    if (package)
        msiobj_release( &package->hdr );
    return r;
978 979
}

980
UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
981
                              LPSTR szResult, LPDWORD sz )
982
{
983 984 985
    UINT r;
    DWORD len, save;
    LPWSTR value;
986

987
    TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
988

989
    if (!hRecord)
990
        return ERROR_INVALID_HANDLE;
991

992 993 994 995 996 997 998
    if (!sz)
    {
        if (szResult)
            return ERROR_INVALID_PARAMETER;
        else
            return ERROR_SUCCESS;
    }
999

1000 1001 1002
    r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
    if (r != ERROR_SUCCESS)
        return r;
1003

1004 1005 1006 1007 1008 1009 1010 1011 1012
    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;
1013 1014
    len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
    WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025

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

    *sz = save - 1;

done:
    msi_free(value);
1026
    return r;
1027
}
1028 1029

/* wrapper to resist a need for a full rewrite right now */
1030
DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data )
1031
{
1032 1033
    DWORD len;
    MSIRECORD *rec;
1034

1035 1036 1037
    *data = NULL;
    if (!fmt) return 0;
    if (!(rec = MSI_CreateRecord( 1 ))) return 0;
1038

1039 1040 1041 1042
    MSI_RecordSetStringW( rec, 0, fmt );
    MSI_FormatRecordW( package, rec, NULL, &len );
    if (!(*data = msi_alloc( ++len * sizeof(WCHAR) )))
    {
1043
        msiobj_release( &rec->hdr );
1044
        return 0;
1045
    }
1046 1047 1048
    MSI_FormatRecordW( package, rec, *data, &len );
    msiobj_release( &rec->hdr );
    return len;
1049
}