| /* | |
| * 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 | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #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" | |
| #include "objbase.h" | |
| #include "oleauto.h" | |
| #include "msipriv.h" | |
| #include "msiserver.h" | |
| #include "wine/unicode.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); | |
| /* 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; | |
| 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); | |
| ret = msi_dup_property(format->package, val); | |
| msi_free(val); | |
| return ret; | |
| } | |
| static LPWSTR deformat_component(FORMAT *format, FORMSTR *str) | |
| { | |
| LPWSTR key, ret = NULL; | |
| MSICOMPONENT *comp; | |
| BOOL source; | |
| key = msi_alloc((str->len + 1) * sizeof(WCHAR)); | |
| lstrcpynW(key, get_formstr_data(format, str), str->len + 1); | |
| comp = get_loaded_component(format->package, key); | |
| if (!comp) | |
| goto done; | |
| source = (comp->Action == INSTALLSTATE_SOURCE) ? TRUE : FALSE; | |
| ret = resolve_folder(format->package, comp->Directory, source, FALSE, TRUE, NULL); | |
| 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); | |
| file = get_loaded_file(format->package, key); | |
| 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; | |
| 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; | |
| } | |
| static LPWSTR build_default_format(const MSIRECORD* record) | |
| { | |
| int i; | |
| int count; | |
| 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; | |
| count = MSI_RecordGetFieldCount(record); | |
| max_len = MAX_PATH; | |
| buf = msi_alloc((max_len + 1) * sizeof(WCHAR)); | |
| rc = NULL; | |
| size = 1; | |
| for (i = 1; i <= count; i++) | |
| { | |
| sprintfW(index, fmt_index, i); | |
| str = MSI_RecordGetString(record, i); | |
| len = (str) ? lstrlenW(str) : 0; | |
| len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index); | |
| size += len; | |
| if (len > max_len) | |
| { | |
| max_len = len; | |
| buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR)); | |
| if (!buf) return NULL; | |
| } | |
| if (str) | |
| sprintfW(buf, fmt, i, str); | |
| else | |
| sprintfW(buf, fmt_null, i); | |
| if (!rc) | |
| { | |
| rc = msi_alloc(size * sizeof(WCHAR)); | |
| lstrcpyW(rc, buf); | |
| } | |
| else | |
| { | |
| rc = msi_realloc(rc, size * sizeof(WCHAR)); | |
| lstrcatW(rc, buf); | |
| } | |
| } | |
| msi_free(buf); | |
| return rc; | |
| } | |
| static BOOL format_is_number(WCHAR x) | |
| { | |
| return ((x >= '0') && (x <= '9')); | |
| } | |
| static BOOL format_str_is_number(LPWSTR str) | |
| { | |
| LPWSTR ptr; | |
| for (ptr = str; *ptr; ptr++) | |
| if (!format_is_number(*ptr)) | |
| return FALSE; | |
| return TRUE; | |
| } | |
| static BOOL format_is_alpha(WCHAR x) | |
| { | |
| return (!format_is_number(x) && x != '\0' && | |
| x != '[' && x != ']' && x != '{' && x != '}'); | |
| } | |
| static BOOL format_is_literal(WCHAR x) | |
| { | |
| return (format_is_alpha(x) || format_is_number(x)); | |
| } | |
| static int format_lex(FORMAT *format, FORMSTR **out) | |
| { | |
| int type, len = 1; | |
| FORMSTR *str; | |
| LPCWSTR data; | |
| WCHAR ch; | |
| *out = NULL; | |
| 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) | |
| { | |
| 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] != ']') | |
| { | |
| while (format_is_literal(data[len])) | |
| len++; | |
| type = FORMAT_LITERAL; | |
| } | |
| } | |
| else | |
| { | |
| ERR("Got unknown character %c(%x)\n", ch, ch); | |
| return FORMAT_ERROR; | |
| } | |
| format->n += len; | |
| str->len = len; | |
| str->type = type; | |
| return type; | |
| } | |
| static FORMSTR *format_replace(FORMAT *format, BOOL propfound, BOOL nonprop, | |
| int oldsize, int type, LPWSTR replace) | |
| { | |
| FORMSTR *ret; | |
| LPWSTR str, ptr; | |
| DWORD size = 0; | |
| int n; | |
| if (replace) | |
| { | |
| if (!*replace) | |
| size = 1; | |
| else | |
| size = lstrlenW(replace); | |
| } | |
| size -= oldsize; | |
| size = format->len + size + 1; | |
| if (size <= 1) | |
| { | |
| msi_free(format->deformatted); | |
| format->deformatted = NULL; | |
| format->len = 0; | |
| return NULL; | |
| } | |
| str = msi_alloc(size * sizeof(WCHAR)); | |
| if (!str) | |
| return NULL; | |
| str[0] = '\0'; | |
| memcpy(str, format->deformatted, format->n * sizeof(WCHAR)); | |
| n = format->n; | |
| if (replace) | |
| { | |
| if (!*replace) | |
| { | |
| str[n] = '\0'; | |
| n++; | |
| } | |
| else | |
| { | |
| lstrcpyW(&str[n], replace); | |
| n += lstrlenW(replace); | |
| } | |
| } | |
| ptr = &format->deformatted[format->n + oldsize]; | |
| memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR)); | |
| msi_free(format->deformatted); | |
| format->deformatted = str; | |
| format->len = size - 1; | |
| /* don't reformat the NULL */ | |
| if (replace && !*replace) | |
| format->n++; | |
| if (!replace) | |
| return NULL; | |
| ret = msi_alloc_zero(sizeof(FORMSTR)); | |
| if (!ret) | |
| return NULL; | |
| ret->len = lstrlenW(replace); | |
| ret->type = type; | |
| ret->n = format->n; | |
| ret->propfound = propfound; | |
| ret->nonprop = nonprop; | |
| return ret; | |
| } | |
| static LPWSTR replace_stack_group(FORMAT *format, STACK *values, | |
| BOOL *propfound, BOOL *nonprop, | |
| int *oldsize, int *type) | |
| { | |
| LPWSTR replaced = NULL; | |
| FORMSTR *content; | |
| FORMSTR *node; | |
| int n; | |
| *nonprop = FALSE; | |
| *propfound = FALSE; | |
| node = stack_pop(values); | |
| n = node->n; | |
| *oldsize = node->len; | |
| msi_free(node); | |
| while ((node = stack_pop(values))) | |
| { | |
| *oldsize += node->len; | |
| if (node->nonprop) | |
| *nonprop = TRUE; | |
| if (node->propfound) | |
| *propfound = TRUE; | |
| msi_free(node); | |
| } | |
| content = msi_alloc_zero(sizeof(FORMSTR)); | |
| content->n = n; | |
| content->len = *oldsize; | |
| content->type = FORMAT_LITERAL; | |
| if (!format->groupfailed && (*oldsize == 2 || | |
| (format->propfailed && !*nonprop))) | |
| { | |
| msi_free(content); | |
| return NULL; | |
| } | |
| 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; | |
| *nonprop = TRUE; | |
| } | |
| replaced = dup_formstr(format, content); | |
| *type = content->type; | |
| msi_free(content); | |
| if (format->groups == 0) | |
| format->propfailed = FALSE; | |
| return replaced; | |
| } | |
| static LPWSTR replace_stack_prop(FORMAT *format, STACK *values, | |
| BOOL *propfound, BOOL *nonprop, | |
| int *oldsize, int *type) | |
| { | |
| LPWSTR replaced = NULL; | |
| FORMSTR *content; | |
| FORMSTR *node; | |
| int n; | |
| *propfound = FALSE; | |
| *nonprop = FALSE; | |
| node = stack_pop(values); | |
| n = node->n; | |
| *oldsize = node->len; | |
| *type = stack_peek(values)->type; | |
| msi_free(node); | |
| while ((node = stack_pop(values))) | |
| { | |
| *oldsize += node->len; | |
| if (*type != FORMAT_ESCAPE && | |
| stack_peek(values) && node->type != *type) | |
| *type = FORMAT_LITERAL; | |
| msi_free(node); | |
| } | |
| content = msi_alloc_zero(sizeof(FORMSTR)); | |
| content->n = n + 1; | |
| content->len = *oldsize - 2; | |
| content->type = *type; | |
| if (*type == FORMAT_NUMBER) | |
| { | |
| replaced = deformat_index(format, content); | |
| if (replaced) | |
| *propfound = TRUE; | |
| else | |
| format->propfailed = TRUE; | |
| 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); | |
| } | |
| msi_free(content); | |
| return replaced; | |
| } | |
| static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values) | |
| { | |
| 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; | |
| } | |
| 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) | |
| { | |
| type = top->type; | |
| if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) && | |
| type == beg->type) | |
| { | |
| top->len += beg->len; | |
| if (group) | |
| top->nonprop = FALSE; | |
| if (type == FORMAT_LITERAL) | |
| top->nonprop = beg->nonprop; | |
| if (beg->propfound) | |
| top->propfound = TRUE; | |
| msi_free(beg); | |
| return ERROR_SUCCESS; | |
| } | |
| } | |
| stack_push(stack, beg); | |
| return ERROR_SUCCESS; | |
| } | |
| static BOOL verify_format(LPWSTR data) | |
| { | |
| int count = 0; | |
| while (*data) | |
| { | |
| if (*data == '[' && *(data - 1) != '\\') | |
| count++; | |
| else if (*data == ']') | |
| count--; | |
| data++; | |
| } | |
| if (count > 0) | |
| return FALSE; | |
| return TRUE; | |
| } | |
| static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, | |
| WCHAR** data, DWORD *len, | |
| MSIRECORD* record, INT* failcount) | |
| { | |
| FORMAT format; | |
| FORMSTR *str = NULL; | |
| STACK *stack, *temp; | |
| FORMSTR *node; | |
| int type; | |
| if (!ptr) | |
| { | |
| *data = NULL; | |
| *len = 0; | |
| return ERROR_SUCCESS; | |
| } | |
| *data = strdupW(ptr); | |
| *len = lstrlenW(ptr); | |
| ZeroMemory(&format, sizeof(FORMAT)); | |
| format.package = package; | |
| format.record = record; | |
| format.deformatted = *data; | |
| format.len = *len; | |
| stack = create_stack(); | |
| temp = create_stack(); | |
| if (!verify_format(*data)) | |
| return ERROR_SUCCESS; | |
| 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) | |
| { | |
| if (type == FORMAT_LBRACE) | |
| { | |
| format.propfailed = FALSE; | |
| format.groups++; | |
| } | |
| else if (type == FORMAT_ESCAPE && | |
| !stack_find(stack, FORMAT_LBRACK)) | |
| { | |
| format.n -= str->len - 1; | |
| str->len = 1; | |
| } | |
| stack_push(stack, str); | |
| } | |
| else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE) | |
| { | |
| if (type == FORMAT_RBRACE) | |
| format.groups--; | |
| stack_push(stack, str); | |
| if (stack_find(stack, left_type(type))) | |
| { | |
| do | |
| { | |
| node = stack_pop(stack); | |
| stack_push(temp, node); | |
| } while (node->type != left_type(type)); | |
| replace_stack(&format, stack, temp); | |
| } | |
| } | |
| } | |
| *data = format.deformatted; | |
| *len = format.len; | |
| msi_free(str); | |
| free_stack(stack); | |
| free_stack(temp); | |
| return ERROR_SUCCESS; | |
| } | |
| UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer, | |
| LPDWORD size ) | |
| { | |
| LPWSTR deformated; | |
| LPWSTR rec; | |
| DWORD len; | |
| UINT rc = ERROR_INVALID_PARAMETER; | |
| TRACE("%p %p %p %p\n", package, record, buffer, size); | |
| rec = msi_dup_record_field(record,0); | |
| if (!rec) | |
| rec = build_default_format(record); | |
| TRACE("(%s)\n",debugstr_w(rec)); | |
| deformat_string_internal(package, rec, &deformated, &len, record, NULL); | |
| if (buffer) | |
| { | |
| if (*size>len) | |
| { | |
| memcpy(buffer,deformated,len*sizeof(WCHAR)); | |
| rc = ERROR_SUCCESS; | |
| buffer[len] = 0; | |
| } | |
| else | |
| { | |
| if (*size > 0) | |
| { | |
| memcpy(buffer,deformated,(*size)*sizeof(WCHAR)); | |
| buffer[(*size)-1] = 0; | |
| } | |
| rc = ERROR_MORE_DATA; | |
| } | |
| } | |
| else | |
| rc = ERROR_SUCCESS; | |
| *size = len; | |
| msi_free(rec); | |
| msi_free(deformated); | |
| return rc; | |
| } | |
| UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, | |
| LPWSTR szResult, LPDWORD sz ) | |
| { | |
| UINT r = ERROR_INVALID_HANDLE; | |
| MSIPACKAGE *package; | |
| MSIRECORD *record; | |
| TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); | |
| 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, | |
| &value ); | |
| 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; | |
| } | |
| } | |
| 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; | |
| } | |
| r = MSI_FormatRecordW( package, record, szResult, sz ); | |
| msiobj_release( &record->hdr ); | |
| if (package) | |
| msiobj_release( &package->hdr ); | |
| return r; | |
| } | |
| UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord, | |
| LPSTR szResult, LPDWORD sz ) | |
| { | |
| UINT r; | |
| DWORD len, save; | |
| LPWSTR value; | |
| TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz); | |
| if (!hRecord) | |
| return ERROR_INVALID_HANDLE; | |
| if (!sz) | |
| { | |
| if (szResult) | |
| return ERROR_INVALID_PARAMETER; | |
| else | |
| return ERROR_SUCCESS; | |
| } | |
| r = MsiFormatRecordW( hInstall, hRecord, NULL, &len ); | |
| if (r != ERROR_SUCCESS) | |
| return r; | |
| 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; | |
| len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL); | |
| WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL); | |
| if (szResult && len > *sz) | |
| { | |
| if (*sz) szResult[*sz - 1] = '\0'; | |
| r = ERROR_MORE_DATA; | |
| } | |
| *sz = save - 1; | |
| done: | |
| msi_free(value); | |
| return r; | |
| } |