2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Aric Stewart for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/debug.h"
37 #include "msiserver.h"
38 #include "wine/unicode.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42 /* types arranged by precedence */
43 #define FORMAT_NULL 0x0001
44 #define FORMAT_LITERAL 0x0002
45 #define FORMAT_NUMBER 0x0004
46 #define FORMAT_LBRACK 0x0010
47 #define FORMAT_LBRACE 0x0020
48 #define FORMAT_RBRACK 0x0011
49 #define FORMAT_RBRACE 0x0021
50 #define FORMAT_ESCAPE 0x0040
51 #define FORMAT_PROPNULL 0x0080
52 #define FORMAT_ERROR 0x1000
53 #define FORMAT_FAIL 0x2000
55 #define left_type(x) (x & 0xF0)
57 typedef struct _tagFORMAT
69 typedef struct _tagFORMSTR
79 typedef struct _tagSTACK
84 static STACK *create_stack(void)
86 STACK *stack = msi_alloc(sizeof(STACK));
87 list_init(&stack->items);
91 static void free_stack(STACK *stack)
93 while (!list_empty(&stack->items))
95 FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
96 list_remove(&str->entry);
103 static void stack_push(STACK *stack, FORMSTR *str)
105 list_add_head(&stack->items, &str->entry);
108 static FORMSTR *stack_pop(STACK *stack)
112 if (list_empty(&stack->items))
115 ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
116 list_remove(&ret->entry);
120 static FORMSTR *stack_find(STACK *stack, int type)
124 LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry)
126 if (str->type == type)
133 static FORMSTR *stack_peek(STACK *stack)
135 return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
138 static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str)
140 return &format->deformatted[str->n];
143 static WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len )
147 if (!str->len) return NULL;
148 if ((val = msi_alloc( (str->len + 1) * sizeof(WCHAR) )))
150 memcpy( val, get_formstr_data(format, str), str->len * sizeof(WCHAR) );
157 static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len )
163 if (!(val = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
164 lstrcpynW(val, get_formstr_data(format, str), str->len + 1);
165 field = atoiW( val );
168 if (MSI_RecordIsNull( format->record, field ) ||
169 MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL;
172 if (!(ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
174 if (MSI_RecordGetStringW( format->record, field, ret, &len ))
183 static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len )
189 if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
190 lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 );
192 r = msi_get_property( format->package->db, prop, NULL, &len );
193 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
199 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
200 msi_get_property( format->package->db, prop, ret, &len );
206 static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len )
211 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
212 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
214 if (!(comp = msi_get_loaded_component( format->package, key )))
219 if (comp->Action == INSTALLSTATE_SOURCE)
220 ret = msi_resolve_source_folder( format->package, comp->Directory, NULL );
222 ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) );
224 *ret_len = strlenW( ret );
229 static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len )
231 WCHAR *key, *ret = NULL;
235 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
236 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
238 if (!(file = msi_get_loaded_file( format->package, key ))) goto done;
241 ret = strdupW( file->TargetPath );
242 len = strlenW( ret );
245 if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0)
247 ret = strdupW( file->TargetPath );
248 len = strlenW( ret );
252 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
253 len = GetShortPathNameW( file->TargetPath, ret, len );
261 static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len )
263 WCHAR *key, *ret = NULL;
266 if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL;
267 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
269 if ((len = GetEnvironmentVariableW( key, NULL, 0 )))
272 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
273 *ret_len = GetEnvironmentVariableW( key, ret, len );
279 static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound,
280 BOOL *nonprop, int *type, int *len )
282 LPCWSTR data = get_formstr_data(format, str);
283 WCHAR *replaced = NULL;
297 replaced = dup_formstr( format, str, len );
304 else if ((replaced = msi_alloc( sizeof(WCHAR) )))
310 else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
318 replaced = deformat_environment( format, str, len ); break;
320 replaced = deformat_file( format, str, FALSE, len ); break;
322 replaced = deformat_file( format, str, TRUE, len ); break;
324 replaced = deformat_component( format, str, len ); break;
327 *type = FORMAT_LITERAL;
331 replaced = deformat_property( format, str, len );
332 *type = FORMAT_LITERAL;
337 format->propfailed = TRUE;
343 static LPWSTR build_default_format(const MSIRECORD* record)
348 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
349 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
350 static const WCHAR fmt_index[] = {'%','i',0};
353 DWORD size, max_len, len;
355 count = MSI_RecordGetFieldCount(record);
358 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
362 for (i = 1; i <= count; i++)
364 sprintfW(index, fmt_index, i);
365 str = MSI_RecordGetString(record, i);
366 len = (str) ? lstrlenW(str) : 0;
367 len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index);
373 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
374 if (!buf) return NULL;
378 sprintfW(buf, fmt, i, str);
380 sprintfW(buf, fmt_null, i);
384 rc = msi_alloc(size * sizeof(WCHAR));
389 rc = msi_realloc(rc, size * sizeof(WCHAR));
398 static BOOL format_is_number(WCHAR x)
400 return ((x >= '0') && (x <= '9'));
403 static BOOL format_str_is_number(LPWSTR str)
407 for (ptr = str; *ptr; ptr++)
408 if (!format_is_number(*ptr))
414 static BOOL format_is_alpha(WCHAR x)
416 return (!format_is_number(x) && x != '\0' &&
417 x != '[' && x != ']' && x != '{' && x != '}');
420 static BOOL format_is_literal(WCHAR x)
422 return (format_is_alpha(x) || format_is_number(x));
425 static int format_lex(FORMAT *format, FORMSTR **out)
434 if (!format->deformatted)
437 *out = msi_alloc_zero(sizeof(FORMSTR));
444 data = get_formstr_data(format, str);
449 case '{': type = FORMAT_LBRACE; break;
450 case '}': type = FORMAT_RBRACE; break;
451 case '[': type = FORMAT_LBRACK; break;
452 case ']': type = FORMAT_RBRACK; break;
453 case '~': type = FORMAT_PROPNULL; break;
454 case '\0': type = FORMAT_NULL; break;
469 while (data[len] && data[len] != ']')
472 type = FORMAT_ESCAPE;
474 else if (format_is_alpha(ch))
476 while (format_is_literal(data[len]))
479 type = FORMAT_LITERAL;
481 else if (format_is_number(ch))
483 while (format_is_number(data[len]))
486 type = FORMAT_NUMBER;
488 if (data[len] != ']')
490 while (format_is_literal(data[len]))
493 type = FORMAT_LITERAL;
498 ERR("Got unknown character %c(%x)\n", ch, ch);
509 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop,
510 int oldsize, int type, WCHAR *replace, int len )
526 size = format->len + size + 1;
530 msi_free(format->deformatted);
531 format->deformatted = NULL;
536 str = msi_alloc(size * sizeof(WCHAR));
541 memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
546 if (!len) str[n++] = 0;
549 memcpy( str + n, replace, len * sizeof(WCHAR) );
555 ptr = &format->deformatted[format->n + oldsize];
556 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
558 msi_free(format->deformatted);
559 format->deformatted = str;
560 format->len = size - 1;
562 /* don't reformat the NULL */
569 ret = msi_alloc_zero(sizeof(FORMSTR));
576 ret->propfound = propfound;
577 ret->nonprop = nonprop;
582 static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
583 BOOL *propfound, BOOL *nonprop,
584 int *oldsize, int *type, int *len )
587 FORMSTR *content, *node;
593 node = stack_pop(values);
595 *oldsize = node->len;
598 while ((node = stack_pop(values)))
600 *oldsize += node->len;
611 content = msi_alloc_zero(sizeof(FORMSTR));
613 content->len = *oldsize;
614 content->type = FORMAT_LITERAL;
616 if (!format->groupfailed && (*oldsize == 2 ||
617 (format->propfailed && !*nonprop)))
622 else if (format->deformatted[content->n + 1] == '{' &&
623 format->deformatted[content->n + content->len - 2] == '}')
625 format->groupfailed = FALSE;
628 else if (*propfound && !*nonprop &&
629 !format->groupfailed && format->groups == 0)
636 if (format->groups != 0)
637 format->groupfailed = TRUE;
642 replaced = dup_formstr( format, content, len );
643 *type = content->type;
646 if (format->groups == 0)
647 format->propfailed = FALSE;
652 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
653 BOOL *propfound, BOOL *nonprop,
654 int *oldsize, int *type, int *len )
657 FORMSTR *content, *node;
663 node = stack_pop(values);
665 *oldsize = node->len;
666 *type = stack_peek(values)->type;
669 while ((node = stack_pop(values)))
671 *oldsize += node->len;
673 if (*type != FORMAT_ESCAPE &&
674 stack_peek(values) && node->type != *type)
675 *type = FORMAT_LITERAL;
680 content = msi_alloc_zero(sizeof(FORMSTR));
682 content->len = *oldsize - 2;
683 content->type = *type;
685 if (*type == FORMAT_NUMBER)
687 replaced = deformat_index( format, content, len );
691 format->propfailed = TRUE;
694 *type = format_str_is_number(replaced) ?
695 FORMAT_NUMBER : FORMAT_LITERAL;
697 else if (format->package)
699 replaced = deformat_literal( format, content, propfound, nonprop, type, len );
706 replaced = dup_formstr( format, content, len );
712 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
714 WCHAR *replaced = NULL;
715 FORMSTR *beg, *top, *node;
716 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
717 int type, n, len = 0, oldsize = 0;
719 node = stack_peek(values);
723 if (type == FORMAT_LBRACK)
724 replaced = replace_stack_prop( format, values, &propfound,
725 &nonprop, &oldsize, &type, &len );
726 else if (type == FORMAT_LBRACE)
728 replaced = replace_stack_group( format, values, &propfound,
729 &nonprop, &oldsize, &type, &len );
734 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
736 return ERROR_SUCCESS;
739 format->n = beg->n + beg->len;
741 top = stack_peek(stack);
746 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
749 top->len += beg->len;
752 top->nonprop = FALSE;
754 if (type == FORMAT_LITERAL)
755 top->nonprop = beg->nonprop;
758 top->propfound = TRUE;
761 return ERROR_SUCCESS;
765 stack_push(stack, beg);
766 return ERROR_SUCCESS;
769 static BOOL verify_format(LPWSTR data)
775 if (*data == '[' && *(data - 1) != '\\')
777 else if (*data == ']')
789 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
790 WCHAR** data, DWORD *len,
791 MSIRECORD* record, INT* failcount)
803 return ERROR_SUCCESS;
806 *data = strdupW(ptr);
807 *len = lstrlenW(ptr);
809 ZeroMemory(&format, sizeof(FORMAT));
810 format.package = package;
811 format.record = record;
812 format.deformatted = *data;
815 if (!verify_format(*data))
816 return ERROR_SUCCESS;
818 stack = create_stack();
819 temp = create_stack();
821 while ((type = format_lex(&format, &str)) != FORMAT_NULL)
823 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
824 type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
825 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
827 if (type == FORMAT_LBRACE)
829 format.propfailed = FALSE;
832 else if (type == FORMAT_ESCAPE &&
833 !stack_find(stack, FORMAT_LBRACK))
835 format.n -= str->len - 1;
839 stack_push(stack, str);
841 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
843 if (type == FORMAT_RBRACE)
846 stack_push(stack, str);
848 if (stack_find(stack, left_type(type)))
852 node = stack_pop(stack);
853 stack_push(temp, node);
854 } while (node->type != left_type(type));
856 replace_stack(&format, stack, temp);
861 *data = format.deformatted;
868 return ERROR_SUCCESS;
871 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
874 WCHAR *format, *deformated;
875 UINT rc = ERROR_INVALID_PARAMETER;
878 TRACE("%p %p %p %p\n", package, record, buffer, size);
880 if (!(format = msi_dup_record_field( record, 0 )))
881 format = build_default_format( record );
883 TRACE("%s\n", debugstr_w(format));
885 deformat_string_internal( package, format, &deformated, &len, record, NULL );
890 memcpy(buffer,deformated,len*sizeof(WCHAR));
898 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
899 buffer[(*size)-1] = 0;
901 rc = ERROR_MORE_DATA;
904 else rc = ERROR_SUCCESS;
908 msi_free( deformated );
912 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
913 LPWSTR szResult, LPDWORD sz )
915 UINT r = ERROR_INVALID_HANDLE;
919 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
921 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
925 IWineMsiRemotePackage *remote_package;
929 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
932 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
938 wstr.str.w = szResult;
939 r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz );
942 IWineMsiRemotePackage_Release( remote_package );
943 SysFreeString( value );
947 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
948 return HRESULT_CODE(hr);
950 return ERROR_FUNCTION_FAILED;
957 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
960 return ERROR_INVALID_HANDLE;
963 msiobj_release( &record->hdr );
965 return ERROR_INVALID_PARAMETER;
967 return ERROR_SUCCESS;
970 r = MSI_FormatRecordW( package, record, szResult, sz );
971 msiobj_release( &record->hdr );
973 msiobj_release( &package->hdr );
977 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
978 LPSTR szResult, LPDWORD sz )
984 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
987 return ERROR_INVALID_HANDLE;
992 return ERROR_INVALID_PARAMETER;
994 return ERROR_SUCCESS;
997 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
998 if (r != ERROR_SUCCESS)
1001 value = msi_alloc(++len * sizeof(WCHAR));
1003 return ERROR_OUTOFMEMORY;
1005 r = MsiFormatRecordW( hInstall, hRecord, value, &len );
1006 if (r != ERROR_SUCCESS)
1010 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
1011 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1013 if (szResult && len > *sz)
1015 if (*sz) szResult[*sz - 1] = '\0';
1016 r = ERROR_MORE_DATA;
1026 /* wrapper to resist a need for a full rewrite right now */
1027 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *ptr, WCHAR **data )
1032 MSIRECORD *rec = MSI_CreateRecord( 1 );
1034 MSI_RecordSetStringW( rec, 0, ptr );
1035 MSI_FormatRecordW( package, rec, NULL, &len );
1038 *data = msi_alloc( len * sizeof(WCHAR) );
1039 if (len > 1) MSI_FormatRecordW( package, rec, *data, &len );
1042 msiobj_release( &rec->hdr );