1046 lines
24 KiB
C
1046 lines
24 KiB
C
/*
|
|
* 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,
|
|
BOOL *nonprop, 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;
|
|
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, 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)
|
|
{
|
|
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, nonprop, 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 );
|
|
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 )
|
|
{
|
|
WCHAR *format, *deformated;
|
|
UINT rc = ERROR_INVALID_PARAMETER;
|
|
DWORD len;
|
|
|
|
TRACE("%p %p %p %p\n", package, record, buffer, size);
|
|
|
|
if (!(format = msi_dup_record_field( record, 0 )))
|
|
format = build_default_format( record );
|
|
|
|
TRACE("%s\n", debugstr_w(format));
|
|
|
|
deformat_string_internal( package, format, &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( 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;
|
|
}
|