summaryrefslogtreecommitdiffstats
path: root/libmsi/format.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmsi/format.c')
-rw-r--r--libmsi/format.c1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/libmsi/format.c b/libmsi/format.c
new file mode 100644
index 0000000..8188f13
--- /dev/null
+++ b/libmsi/format.c
@@ -0,0 +1,1045 @@
+/*
+ * 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->db, val);
+
+ 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);
+
+ comp = msi_get_loaded_component(format->package, key);
+ if (!comp)
+ goto done;
+
+ 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 ) );
+
+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 = msi_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;
+
+ 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 )
+{
+ 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;
+}
+
+/* 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;
+}