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
24 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
26 /* types arranged by precedence */
27 #define FORMAT_NULL 0x0001
28 #define FORMAT_LITERAL 0x0002
29 #define FORMAT_NUMBER 0x0004
30 #define FORMAT_LBRACK 0x0010
31 #define FORMAT_LBRACE 0x0020
32 #define FORMAT_RBRACK 0x0011
33 #define FORMAT_RBRACE 0x0021
34 #define FORMAT_ESCAPE 0x0040
35 #define FORMAT_PROPNULL 0x0080
36 #define FORMAT_ERROR 0x1000
37 #define FORMAT_FAIL 0x2000
39 #define left_type(x) (x & 0xF0)
41 typedef struct _tagFORMAT
53 typedef struct _tagFORMSTR
63 typedef struct _tagSTACK
68 static STACK
*create_stack(void)
70 STACK
*stack
= msi_alloc(sizeof(STACK
));
71 list_init(&stack
->items
);
75 static void free_stack(STACK
*stack
)
77 while (!list_empty(&stack
->items
))
79 FORMSTR
*str
= LIST_ENTRY(list_head(&stack
->items
), FORMSTR
, entry
);
80 list_remove(&str
->entry
);
87 static void stack_push(STACK
*stack
, FORMSTR
*str
)
89 list_add_head(&stack
->items
, &str
->entry
);
92 static FORMSTR
*stack_pop(STACK
*stack
)
96 if (list_empty(&stack
->items
))
99 ret
= LIST_ENTRY(list_head(&stack
->items
), FORMSTR
, entry
);
100 list_remove(&ret
->entry
);
104 static FORMSTR
*stack_find(STACK
*stack
, int type
)
108 LIST_FOR_EACH_ENTRY(str
, &stack
->items
, FORMSTR
, entry
)
110 if (str
->type
== type
)
117 static FORMSTR
*stack_peek(STACK
*stack
)
119 return LIST_ENTRY(list_head(&stack
->items
), FORMSTR
, entry
);
122 static LPCWSTR
get_formstr_data(FORMAT
*format
, FORMSTR
*str
)
124 return &format
->deformatted
[str
->n
];
127 static WCHAR
*dup_formstr( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
131 if (!str
->len
) return NULL
;
132 if ((val
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) )))
134 memcpy( val
, get_formstr_data(format
, str
), str
->len
* sizeof(WCHAR
) );
141 static WCHAR
*deformat_index( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
147 if (!(val
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
148 lstrcpynW(val
, get_formstr_data(format
, str
), str
->len
+ 1);
149 field
= atoiW( val
);
152 if (MSI_RecordIsNull( format
->record
, field
) ||
153 MSI_RecordGetStringW( format
->record
, field
, NULL
, &len
)) return NULL
;
156 if (!(ret
= msi_alloc( len
* sizeof(WCHAR
) ))) return NULL
;
158 if (MSI_RecordGetStringW( format
->record
, field
, ret
, &len
))
167 static WCHAR
*deformat_property( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
173 if (!(prop
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
174 lstrcpynW( prop
, get_formstr_data(format
, str
), str
->len
+ 1 );
176 r
= msi_get_property( format
->package
->db
, prop
, NULL
, &len
);
177 if (r
!= ERROR_SUCCESS
&& r
!= ERROR_MORE_DATA
)
183 if ((ret
= msi_alloc( len
* sizeof(WCHAR
) )))
184 msi_get_property( format
->package
->db
, prop
, ret
, &len
);
190 static WCHAR
*deformat_component( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
195 if (!(key
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
196 lstrcpynW(key
, get_formstr_data(format
, str
), str
->len
+ 1);
198 if (!(comp
= msi_get_loaded_component( format
->package
, key
)))
203 if (comp
->Action
== INSTALLSTATE_SOURCE
)
204 ret
= msi_resolve_source_folder( format
->package
, comp
->Directory
, NULL
);
206 ret
= strdupW( msi_get_target_folder( format
->package
, comp
->Directory
) );
208 if (ret
) *ret_len
= strlenW( ret
);
214 static WCHAR
*deformat_file( FORMAT
*format
, FORMSTR
*str
, BOOL shortname
, int *ret_len
)
216 WCHAR
*key
, *ret
= NULL
;
220 if (!(key
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
221 lstrcpynW(key
, get_formstr_data(format
, str
), str
->len
+ 1);
223 if (!(file
= msi_get_loaded_file( format
->package
, key
))) goto done
;
226 if ((ret
= strdupW( file
->TargetPath
))) len
= strlenW( ret
);
229 if ((len
= GetShortPathNameW(file
->TargetPath
, NULL
, 0)) <= 0)
231 if ((ret
= strdupW( file
->TargetPath
))) len
= strlenW( ret
);
235 if ((ret
= msi_alloc( len
* sizeof(WCHAR
) )))
236 len
= GetShortPathNameW( file
->TargetPath
, ret
, len
);
244 static WCHAR
*deformat_environment( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
246 WCHAR
*key
, *ret
= NULL
;
249 if (!(key
= msi_alloc((str
->len
+ 1) * sizeof(WCHAR
)))) return NULL
;
250 lstrcpynW(key
, get_formstr_data(format
, str
), str
->len
+ 1);
252 if ((len
= GetEnvironmentVariableW( key
, NULL
, 0 )))
255 if ((ret
= msi_alloc( len
* sizeof(WCHAR
) )))
256 *ret_len
= GetEnvironmentVariableW( key
, ret
, len
);
262 static WCHAR
*deformat_literal( FORMAT
*format
, FORMSTR
*str
, BOOL
*propfound
,
263 BOOL
*nonprop
, int *type
, int *len
)
265 LPCWSTR data
= get_formstr_data(format
, str
);
266 WCHAR
*replaced
= NULL
;
280 replaced
= dup_formstr( format
, str
, len
);
287 else if ((replaced
= msi_alloc( sizeof(WCHAR
) )))
293 else if (ch
== '%' || ch
== '#' || ch
== '!' || ch
== '$')
301 replaced
= deformat_environment( format
, str
, len
); break;
303 replaced
= deformat_file( format
, str
, FALSE
, len
); break;
305 replaced
= deformat_file( format
, str
, TRUE
, len
); break;
307 replaced
= deformat_component( format
, str
, len
); break;
310 *type
= FORMAT_LITERAL
;
314 replaced
= deformat_property( format
, str
, len
);
315 *type
= FORMAT_LITERAL
;
320 format
->propfailed
= TRUE
;
326 static LPWSTR
build_default_format(const MSIRECORD
* record
)
331 static const WCHAR fmt
[] = {'%','i',':',' ','%','s',' ',0};
332 static const WCHAR fmt_null
[] = {'%','i',':',' ',' ',0};
333 static const WCHAR fmt_index
[] = {'%','i',0};
336 DWORD size
, max_len
, len
;
338 count
= MSI_RecordGetFieldCount(record
);
341 buf
= msi_alloc((max_len
+ 1) * sizeof(WCHAR
));
345 for (i
= 1; i
<= count
; i
++)
347 sprintfW(index
, fmt_index
, i
);
348 str
= MSI_RecordGetString(record
, i
);
349 len
= (str
) ? lstrlenW(str
) : 0;
350 len
+= (sizeof(fmt_null
)/sizeof(fmt_null
[0]) - 3) + lstrlenW(index
);
356 buf
= msi_realloc(buf
, (max_len
+ 1) * sizeof(WCHAR
));
357 if (!buf
) return NULL
;
361 sprintfW(buf
, fmt
, i
, str
);
363 sprintfW(buf
, fmt_null
, i
);
367 rc
= msi_alloc(size
* sizeof(WCHAR
));
372 rc
= msi_realloc(rc
, size
* sizeof(WCHAR
));
381 static BOOL
format_is_number(WCHAR x
)
383 return ((x
>= '0') && (x
<= '9'));
386 static BOOL
format_str_is_number(LPWSTR str
)
390 for (ptr
= str
; *ptr
; ptr
++)
391 if (!format_is_number(*ptr
))
397 static BOOL
format_is_alpha(WCHAR x
)
399 return (!format_is_number(x
) && x
!= '\0' &&
400 x
!= '[' && x
!= ']' && x
!= '{' && x
!= '}');
403 static BOOL
format_is_literal(WCHAR x
)
405 return (format_is_alpha(x
) || format_is_number(x
));
408 static int format_lex(FORMAT
*format
, FORMSTR
**out
)
417 if (!format
->deformatted
)
420 *out
= msi_alloc_zero(sizeof(FORMSTR
));
427 data
= get_formstr_data(format
, str
);
432 case '{': type
= FORMAT_LBRACE
; break;
433 case '}': type
= FORMAT_RBRACE
; break;
434 case '[': type
= FORMAT_LBRACK
; break;
435 case ']': type
= FORMAT_RBRACK
; break;
436 case '~': type
= FORMAT_PROPNULL
; break;
437 case '\0': type
= FORMAT_NULL
; break;
452 while (data
[len
] && data
[len
] != ']')
455 type
= FORMAT_ESCAPE
;
457 else if (format_is_alpha(ch
))
459 while (format_is_literal(data
[len
]))
462 type
= FORMAT_LITERAL
;
464 else if (format_is_number(ch
))
466 while (format_is_number(data
[len
]))
469 type
= FORMAT_NUMBER
;
471 if (data
[len
] != ']')
473 while (format_is_literal(data
[len
]))
476 type
= FORMAT_LITERAL
;
481 ERR("Got unknown character %c(%x)\n", ch
, ch
);
492 static FORMSTR
*format_replace( FORMAT
*format
, BOOL propfound
, BOOL nonprop
,
493 int oldsize
, int type
, WCHAR
*replace
, int len
)
509 size
= format
->len
+ size
+ 1;
513 msi_free(format
->deformatted
);
514 format
->deformatted
= NULL
;
519 str
= msi_alloc(size
* sizeof(WCHAR
));
524 memcpy(str
, format
->deformatted
, format
->n
* sizeof(WCHAR
));
529 if (!len
) str
[n
++] = 0;
532 memcpy( str
+ n
, replace
, len
* sizeof(WCHAR
) );
538 ptr
= &format
->deformatted
[format
->n
+ oldsize
];
539 memcpy(&str
[n
], ptr
, (lstrlenW(ptr
) + 1) * sizeof(WCHAR
));
541 msi_free(format
->deformatted
);
542 format
->deformatted
= str
;
543 format
->len
= size
- 1;
545 /* don't reformat the NULL */
552 ret
= msi_alloc_zero(sizeof(FORMSTR
));
559 ret
->propfound
= propfound
;
560 ret
->nonprop
= nonprop
;
565 static WCHAR
*replace_stack_group( FORMAT
*format
, STACK
*values
,
566 BOOL
*propfound
, BOOL
*nonprop
,
567 int *oldsize
, int *type
, int *len
)
570 FORMSTR
*content
, *node
;
576 node
= stack_pop(values
);
578 *oldsize
= node
->len
;
581 while ((node
= stack_pop(values
)))
583 *oldsize
+= node
->len
;
594 content
= msi_alloc_zero(sizeof(FORMSTR
));
596 content
->len
= *oldsize
;
597 content
->type
= FORMAT_LITERAL
;
599 if (!format
->groupfailed
&& (*oldsize
== 2 ||
600 (format
->propfailed
&& !*nonprop
)))
605 else if (format
->deformatted
[content
->n
+ 1] == '{' &&
606 format
->deformatted
[content
->n
+ content
->len
- 2] == '}')
608 format
->groupfailed
= FALSE
;
611 else if (*propfound
&& !*nonprop
&&
612 !format
->groupfailed
&& format
->groups
== 0)
619 if (format
->groups
!= 0)
620 format
->groupfailed
= TRUE
;
625 replaced
= dup_formstr( format
, content
, len
);
626 *type
= content
->type
;
629 if (format
->groups
== 0)
630 format
->propfailed
= FALSE
;
635 static WCHAR
*replace_stack_prop( FORMAT
*format
, STACK
*values
,
636 BOOL
*propfound
, BOOL
*nonprop
,
637 int *oldsize
, int *type
, int *len
)
640 FORMSTR
*content
, *node
;
646 node
= stack_pop(values
);
648 *oldsize
= node
->len
;
649 *type
= stack_peek(values
)->type
;
652 while ((node
= stack_pop(values
)))
654 *oldsize
+= node
->len
;
656 if (*type
!= FORMAT_ESCAPE
&&
657 stack_peek(values
) && node
->type
!= *type
)
658 *type
= FORMAT_LITERAL
;
663 content
= msi_alloc_zero(sizeof(FORMSTR
));
665 content
->len
= *oldsize
- 2;
666 content
->type
= *type
;
668 if (*type
== FORMAT_NUMBER
)
670 replaced
= deformat_index( format
, content
, len
);
674 format
->propfailed
= TRUE
;
677 *type
= format_str_is_number(replaced
) ?
678 FORMAT_NUMBER
: FORMAT_LITERAL
;
680 else if (format
->package
)
682 replaced
= deformat_literal( format
, content
, propfound
, nonprop
, type
, len
);
689 replaced
= dup_formstr( format
, content
, len
);
695 static UINT
replace_stack(FORMAT
*format
, STACK
*stack
, STACK
*values
)
697 WCHAR
*replaced
= NULL
;
698 FORMSTR
*beg
, *top
, *node
;
699 BOOL propfound
= FALSE
, nonprop
= FALSE
, group
= FALSE
;
700 int type
, n
, len
= 0, oldsize
= 0;
702 node
= stack_peek(values
);
706 if (type
== FORMAT_LBRACK
)
707 replaced
= replace_stack_prop( format
, values
, &propfound
,
708 &nonprop
, &oldsize
, &type
, &len
);
709 else if (type
== FORMAT_LBRACE
)
711 replaced
= replace_stack_group( format
, values
, &propfound
,
712 &nonprop
, &oldsize
, &type
, &len
);
717 beg
= format_replace( format
, propfound
, nonprop
, oldsize
, type
, replaced
, len
);
719 return ERROR_SUCCESS
;
722 format
->n
= beg
->n
+ beg
->len
;
724 top
= stack_peek(stack
);
729 if ((type
== FORMAT_LITERAL
|| type
== FORMAT_NUMBER
) &&
732 top
->len
+= beg
->len
;
735 top
->nonprop
= FALSE
;
737 if (type
== FORMAT_LITERAL
)
738 top
->nonprop
= beg
->nonprop
;
741 top
->propfound
= TRUE
;
744 return ERROR_SUCCESS
;
748 stack_push(stack
, beg
);
749 return ERROR_SUCCESS
;
752 static BOOL
verify_format(LPWSTR data
)
758 if (*data
== '[' && *(data
- 1) != '\\')
760 else if (*data
== ']')
772 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
773 WCHAR
** data
, DWORD
*len
,
774 MSIRECORD
* record
, INT
* failcount
)
786 return ERROR_SUCCESS
;
789 *data
= strdupW(ptr
);
790 *len
= lstrlenW(ptr
);
792 ZeroMemory(&format
, sizeof(FORMAT
));
793 format
.package
= package
;
794 format
.record
= record
;
795 format
.deformatted
= *data
;
798 if (!verify_format(*data
))
799 return ERROR_SUCCESS
;
801 stack
= create_stack();
802 temp
= create_stack();
804 while ((type
= format_lex(&format
, &str
)) != FORMAT_NULL
)
806 if (type
== FORMAT_LBRACK
|| type
== FORMAT_LBRACE
||
807 type
== FORMAT_LITERAL
|| type
== FORMAT_NUMBER
||
808 type
== FORMAT_ESCAPE
|| type
== FORMAT_PROPNULL
)
810 if (type
== FORMAT_LBRACE
)
812 format
.propfailed
= FALSE
;
815 else if (type
== FORMAT_ESCAPE
&&
816 !stack_find(stack
, FORMAT_LBRACK
))
818 format
.n
-= str
->len
- 1;
822 stack_push(stack
, str
);
824 else if (type
== FORMAT_RBRACK
|| type
== FORMAT_RBRACE
)
826 if (type
== FORMAT_RBRACE
)
829 stack_push(stack
, str
);
831 if (stack_find(stack
, left_type(type
)))
835 node
= stack_pop(stack
);
836 stack_push(temp
, node
);
837 } while (node
->type
!= left_type(type
));
839 replace_stack(&format
, stack
, temp
);
844 *data
= format
.deformatted
;
851 return ERROR_SUCCESS
;
854 UINT
MSI_FormatRecordW( MSIPACKAGE
* package
, MSIRECORD
* record
, LPWSTR buffer
,
857 WCHAR
*format
, *deformated
;
858 UINT rc
= ERROR_INVALID_PARAMETER
;
861 TRACE("%p %p %p %p\n", package
, record
, buffer
, size
);
863 if (!(format
= msi_dup_record_field( record
, 0 )))
864 format
= build_default_format( record
);
866 TRACE("%s\n", debugstr_w(format
));
868 deformat_string_internal( package
, format
, &deformated
, &len
, record
, NULL
);
873 memcpy(buffer
,deformated
,len
*sizeof(WCHAR
));
881 memcpy(buffer
,deformated
,(*size
)*sizeof(WCHAR
));
882 buffer
[(*size
)-1] = 0;
884 rc
= ERROR_MORE_DATA
;
887 else rc
= ERROR_SUCCESS
;
891 msi_free( deformated
);
895 UINT WINAPI
MsiFormatRecordW( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
896 LPWSTR szResult
, LPDWORD sz
)
898 UINT r
= ERROR_INVALID_HANDLE
;
902 TRACE("%d %d %p %p\n", hInstall
, hRecord
, szResult
, sz
);
904 package
= msihandle2msiinfo( hInstall
, MSIHANDLETYPE_PACKAGE
);
908 IWineMsiRemotePackage
*remote_package
;
912 remote_package
= (IWineMsiRemotePackage
*)msi_get_remote( hInstall
);
915 hr
= IWineMsiRemotePackage_FormatRecord( remote_package
, hRecord
,
921 wstr
.str
.w
= szResult
;
922 r
= msi_strcpy_to_awstring( value
, SysStringLen(value
), &wstr
, sz
);
925 IWineMsiRemotePackage_Release( remote_package
);
926 SysFreeString( value
);
930 if (HRESULT_FACILITY(hr
) == FACILITY_WIN32
)
931 return HRESULT_CODE(hr
);
933 return ERROR_FUNCTION_FAILED
;
940 record
= msihandle2msiinfo( hRecord
, MSIHANDLETYPE_RECORD
);
943 return ERROR_INVALID_HANDLE
;
946 msiobj_release( &record
->hdr
);
948 return ERROR_INVALID_PARAMETER
;
950 return ERROR_SUCCESS
;
953 r
= MSI_FormatRecordW( package
, record
, szResult
, sz
);
954 msiobj_release( &record
->hdr
);
956 msiobj_release( &package
->hdr
);
960 UINT WINAPI
MsiFormatRecordA( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
961 LPSTR szResult
, LPDWORD sz
)
967 TRACE("%d %d %p %p\n", hInstall
, hRecord
, szResult
, sz
);
970 return ERROR_INVALID_HANDLE
;
975 return ERROR_INVALID_PARAMETER
;
977 return ERROR_SUCCESS
;
980 r
= MsiFormatRecordW( hInstall
, hRecord
, NULL
, &len
);
981 if (r
!= ERROR_SUCCESS
)
984 value
= msi_alloc(++len
* sizeof(WCHAR
));
986 return ERROR_OUTOFMEMORY
;
988 r
= MsiFormatRecordW( hInstall
, hRecord
, value
, &len
);
989 if (r
!= ERROR_SUCCESS
)
993 len
= WideCharToMultiByte(CP_ACP
, 0, value
, len
+ 1, NULL
, 0, NULL
, NULL
);
994 WideCharToMultiByte(CP_ACP
, 0, value
, len
, szResult
, *sz
, NULL
, NULL
);
996 if (szResult
&& len
> *sz
)
998 if (*sz
) szResult
[*sz
- 1] = '\0';
1009 /* wrapper to resist a need for a full rewrite right now */
1010 DWORD
deformat_string( MSIPACKAGE
*package
, const WCHAR
*fmt
, WCHAR
**data
)
1017 if (!(rec
= MSI_CreateRecord( 1 ))) return 0;
1019 MSI_RecordSetStringW( rec
, 0, fmt
);
1020 MSI_FormatRecordW( package
, rec
, NULL
, &len
);
1021 if (!(*data
= msi_alloc( ++len
* sizeof(WCHAR
) )))
1023 msiobj_release( &rec
->hdr
);
1026 MSI_FormatRecordW( package
, rec
, *data
, &len
);
1027 msiobj_release( &rec
->hdr
);