| /* | |
| * 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 WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len ) | |
| { | |
| WCHAR *val; | |
| 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; | |
| } | |
| return val; | |
| } | |
| static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len ) | |
| { | |
| WCHAR *val, *ret; | |
| DWORD len; | |
| int field; | |
| if (!(val = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; | |
| lstrcpynW(val, get_formstr_data(format, str), str->len + 1); | |
| field = atoiW( val ); | |
| msi_free( val ); | |
| if (MSI_RecordIsNull( format->record, field ) || | |
| MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL; | |
| 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; | |
| return ret; | |
| } | |
| static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len ) | |
| { | |
| WCHAR *prop, *ret; | |
| DWORD len = 0; | |
| UINT r; | |
| if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; | |
| lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 ); | |
| 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; | |
| return ret; | |
| } | |
| static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len ) | |
| { | |
| WCHAR *key, *ret; | |
| MSICOMPONENT *comp; | |
| if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; | |
| lstrcpynW(key, get_formstr_data(format, str), str->len + 1); | |
| if (!(comp = msi_get_loaded_component( format->package, key ))) | |
| { | |
| msi_free( key ); | |
| return NULL; | |
| } | |
| if (comp->Action == INSTALLSTATE_SOURCE) | |
| ret = msi_resolve_source_folder( format->package, comp->Directory, NULL ); | |
| else | |
| ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) ); | |
| if (ret) *ret_len = strlenW( ret ); | |
| else *ret_len = 0; | |
| msi_free( key ); | |
| return ret; | |
| } | |
| static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len ) | |
| { | |
| WCHAR *key, *ret = NULL; | |
| const MSIFILE *file; | |
| DWORD len = 0; | |
| if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL; | |
| lstrcpynW(key, get_formstr_data(format, str), str->len + 1); | |
| if (!(file = msi_get_loaded_file( format->package, key ))) goto done; | |
| if (!shortname) | |
| { | |
| if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret ); | |
| goto done; | |
| } | |
| if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0) | |
| { | |
| if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret ); | |
| goto done; | |
| } | |
| len++; | |
| if ((ret = msi_alloc( len * sizeof(WCHAR) ))) | |
| len = GetShortPathNameW( file->TargetPath, ret, len ); | |
| done: | |
| msi_free( key ); | |
| *ret_len = len; | |
| return ret; | |
| } | |
| static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len ) | |
| { | |
| WCHAR *key, *ret = NULL; | |
| DWORD len; | |
| if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL; | |
| lstrcpynW(key, get_formstr_data(format, str), str->len + 1); | |
| if ((len = GetEnvironmentVariableW( key, NULL, 0 ))) | |
| { | |
| len++; | |
| if ((ret = msi_alloc( len * sizeof(WCHAR) ))) | |
| *ret_len = GetEnvironmentVariableW( key, ret, len ); | |
| } | |
| msi_free( key ); | |
| return ret; | |
| } | |
| static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound, | |
| int *type, int *len ) | |
| { | |
| LPCWSTR data = get_formstr_data(format, str); | |
| WCHAR *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, len ); | |
| } | |
| } | |
| else if (ch == '~') | |
| { | |
| if (str->len != 1) | |
| replaced = NULL; | |
| else if ((replaced = msi_alloc( sizeof(WCHAR) ))) | |
| { | |
| *replaced = 0; | |
| *len = 0; | |
| } | |
| } | |
| else if (ch == '%' || ch == '#' || ch == '!' || ch == '$') | |
| { | |
| str->n++; | |
| str->len--; | |
| switch (ch) | |
| { | |
| case '%': | |
| replaced = deformat_environment( format, str, len ); break; | |
| case '#': | |
| replaced = deformat_file( format, str, FALSE, len ); break; | |
| case '!': | |
| replaced = deformat_file( format, str, TRUE, len ); break; | |
| case '$': | |
| replaced = deformat_component( format, str, len ); break; | |
| } | |
| *type = FORMAT_LITERAL; | |
| } | |
| else | |
| { | |
| replaced = deformat_property( format, str, len ); | |
| *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; | |
| WCHAR *rc, buf[26]; | |
| static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0}; | |
| DWORD size; | |
| count = MSI_RecordGetFieldCount(record); | |
| rc = msi_alloc(1); | |
| rc[0] = 0; | |
| size = 1; | |
| for (i = 1; i <= count; i++) | |
| { | |
| sprintfW(buf, fmt, i, i); | |
| size += lstrlenW(buf); | |
| rc = msi_realloc(rc, size * sizeof(WCHAR)); | |
| lstrcatW(rc, 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, WCHAR *replace, int len ) | |
| { | |
| FORMSTR *ret; | |
| LPWSTR str, ptr; | |
| DWORD size = 0; | |
| int n; | |
| if (replace) | |
| { | |
| if (!len) | |
| size = 1; | |
| else | |
| size = len; | |
| } | |
| 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 (!len) str[n++] = 0; | |
| else | |
| { | |
| memcpy( str + n, replace, len * sizeof(WCHAR) ); | |
| n += len; | |
| str[n] = 0; | |
| } | |
| } | |
| 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 && !len) | |
| format->n++; | |
| if (!replace) | |
| return NULL; | |
| ret = msi_alloc_zero(sizeof(FORMSTR)); | |
| if (!ret) | |
| return NULL; | |
| ret->len = len; | |
| ret->type = type; | |
| ret->n = format->n; | |
| ret->propfound = propfound; | |
| ret->nonprop = nonprop; | |
| return ret; | |
| } | |
| static WCHAR *replace_stack_group( FORMAT *format, STACK *values, | |
| BOOL *propfound, BOOL *nonprop, | |
| int *oldsize, int *type, int *len ) | |
| { | |
| WCHAR *replaced; | |
| FORMSTR *content, *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, len ); | |
| *type = content->type; | |
| msi_free(content); | |
| if (format->groups == 0) | |
| format->propfailed = FALSE; | |
| return replaced; | |
| } | |
| static WCHAR *replace_stack_prop( FORMAT *format, STACK *values, | |
| BOOL *propfound, BOOL *nonprop, | |
| int *oldsize, int *type, int *len ) | |
| { | |
| WCHAR *replaced; | |
| FORMSTR *content, *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 && format->record) | |
| { | |
| replaced = deformat_index( format, content, len ); | |
| 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, type, len ); | |
| } | |
| else | |
| { | |
| *nonprop = TRUE; | |
| content->n--; | |
| content->len += 2; | |
| replaced = dup_formstr( format, content, len ); | |
| } | |
| msi_free(content); | |
| return replaced; | |
| } | |
| static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values) | |
| { | |
| WCHAR *replaced = NULL; | |
| FORMSTR *beg, *top, *node; | |
| BOOL propfound = FALSE, nonprop = FALSE, group = FALSE; | |
| int type, n, len = 0, oldsize = 0; | |
| node = stack_peek(values); | |
| type = node->type; | |
| n = node->n; | |
| if (type == FORMAT_LBRACK) | |
| replaced = replace_stack_prop( format, values, &propfound, | |
| &nonprop, &oldsize, &type, &len ); | |
| else if (type == FORMAT_LBRACE) | |
| { | |
| replaced = replace_stack_group( format, values, &propfound, | |
| &nonprop, &oldsize, &type, &len ); | |
| group = TRUE; | |
| } | |
| format->n = n; | |
| beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len ); | |
| msi_free(replaced); | |
| if (!beg) | |
| return ERROR_SUCCESS; | |
| 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) | |
| { | |
| 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; | |
| if (!verify_format(*data)) | |
| return ERROR_SUCCESS; | |
| stack = create_stack(); | |
| temp = create_stack(); | |
| 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 ) | |
| { | |
| WCHAR *format, *deformated = NULL; | |
| UINT rc = ERROR_INVALID_PARAMETER; | |
| DWORD len; | |
| MSIRECORD *record_deformated; | |
| int field_count, i; | |
| TRACE("%p %p %p %p\n", package, record, buffer, size); | |
| dump_record(record); | |
| if (!(format = msi_dup_record_field( record, 0 ))) | |
| format = build_default_format( record ); | |
| field_count = MSI_RecordGetFieldCount(record); | |
| record_deformated = MSI_CloneRecord(record); | |
| if (!record_deformated) | |
| { | |
| rc = ERROR_OUTOFMEMORY; | |
| goto end; | |
| } | |
| MSI_RecordSetStringW(record_deformated, 0, format); | |
| for (i = 1; i <= field_count; i++) | |
| { | |
| if (MSI_RecordGetString(record, i)) | |
| { | |
| deformat_string_internal(package, MSI_RecordGetString(record, i), &deformated, &len, NULL); | |
| MSI_RecordSetStringW(record_deformated, i, deformated); | |
| msi_free(deformated); | |
| } | |
| } | |
| deformat_string_internal(package, format, &deformated, &len, record_deformated); | |
| 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; | |
| msiobj_release(&record_deformated->hdr); | |
| end: | |
| msi_free( format ); | |
| 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, SysStringLen(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; | |
| } | |
| /* wrapper to resist a need for a full rewrite right now */ | |
| DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data ) | |
| { | |
| DWORD len; | |
| MSIRECORD *rec; | |
| *data = NULL; | |
| if (!fmt) return 0; | |
| if (!(rec = MSI_CreateRecord( 1 ))) return 0; | |
| MSI_RecordSetStringW( rec, 0, fmt ); | |
| MSI_FormatRecordW( package, rec, NULL, &len ); | |
| if (!(*data = msi_alloc( ++len * sizeof(WCHAR) ))) | |
| { | |
| msiobj_release( &rec->hdr ); | |
| return 0; | |
| } | |
| MSI_FormatRecordW( package, rec, *data, &len ); | |
| msiobj_release( &rec->hdr ); | |
| return len; | |
| } |