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"
38 #include "wine/exception.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
= wcstol( val
, NULL
, 10 );
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 if (ret
) *ret_len
= lstrlenW( ret
);
230 static WCHAR
*deformat_file( FORMAT
*format
, FORMSTR
*str
, BOOL shortname
, int *ret_len
)
232 WCHAR
*key
, *ret
= NULL
;
236 if (!(key
= msi_alloc( (str
->len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
237 lstrcpynW(key
, get_formstr_data(format
, str
), str
->len
+ 1);
239 if (!(file
= msi_get_loaded_file( format
->package
, key
))) goto done
;
242 if ((ret
= strdupW( file
->TargetPath
))) len
= lstrlenW( ret
);
245 if (!(len
= GetShortPathNameW(file
->TargetPath
, NULL
, 0)))
247 if ((ret
= strdupW( file
->TargetPath
))) len
= lstrlenW( ret
);
251 if ((ret
= msi_alloc( len
* sizeof(WCHAR
) )))
252 len
= GetShortPathNameW( file
->TargetPath
, ret
, len
);
260 static WCHAR
*deformat_environment( FORMAT
*format
, FORMSTR
*str
, int *ret_len
)
262 WCHAR
*key
, *ret
= NULL
;
265 if (!(key
= msi_alloc((str
->len
+ 1) * sizeof(WCHAR
)))) return NULL
;
266 lstrcpynW(key
, get_formstr_data(format
, str
), str
->len
+ 1);
268 if ((len
= GetEnvironmentVariableW( key
, NULL
, 0 )))
271 if ((ret
= msi_alloc( len
* sizeof(WCHAR
) )))
272 *ret_len
= GetEnvironmentVariableW( key
, ret
, len
);
278 static WCHAR
*deformat_literal( FORMAT
*format
, FORMSTR
*str
, BOOL
*propfound
,
279 int *type
, int *len
)
281 LPCWSTR data
= get_formstr_data(format
, str
);
282 WCHAR
*replaced
= NULL
;
296 replaced
= dup_formstr( format
, str
, len
);
303 else if ((replaced
= msi_alloc( sizeof(WCHAR
) )))
309 else if (ch
== '%' || ch
== '#' || ch
== '!' || ch
== '$')
317 replaced
= deformat_environment( format
, str
, len
); break;
319 replaced
= deformat_file( format
, str
, FALSE
, len
); break;
321 replaced
= deformat_file( format
, str
, TRUE
, len
); break;
323 replaced
= deformat_component( format
, str
, len
); break;
326 *type
= FORMAT_LITERAL
;
330 replaced
= deformat_property( format
, str
, len
);
331 *type
= FORMAT_LITERAL
;
336 format
->propfailed
= TRUE
;
342 static WCHAR
*build_default_format( const MSIRECORD
*record
)
344 int i
, count
= MSI_RecordGetFieldCount( record
);
345 WCHAR
*ret
, *tmp
, buf
[26];
348 if (!(ret
= msi_alloc( sizeof(*ret
) ))) return NULL
;
351 for (i
= 1; i
<= count
; i
++)
353 size
+= swprintf( buf
, ARRAY_SIZE(buf
), L
"%d: [%d] ", i
, i
);
354 if (!(tmp
= msi_realloc( ret
, size
* sizeof(*ret
) )))
360 lstrcatW( ret
, buf
);
365 static BOOL
format_is_number(WCHAR x
)
367 return ((x
>= '0') && (x
<= '9'));
370 static BOOL
format_str_is_number(LPWSTR str
)
374 for (ptr
= str
; *ptr
; ptr
++)
375 if (!format_is_number(*ptr
))
381 static BOOL
format_is_alpha(WCHAR x
)
383 return (!format_is_number(x
) && x
!= '\0' &&
384 x
!= '[' && x
!= ']' && x
!= '{' && x
!= '}');
387 static BOOL
format_is_literal(WCHAR x
)
389 return (format_is_alpha(x
) || format_is_number(x
));
392 static int format_lex(FORMAT
*format
, FORMSTR
**out
)
401 if (!format
->deformatted
)
404 *out
= msi_alloc_zero(sizeof(FORMSTR
));
411 data
= get_formstr_data(format
, str
);
416 case '{': type
= FORMAT_LBRACE
; break;
417 case '}': type
= FORMAT_RBRACE
; break;
418 case '[': type
= FORMAT_LBRACK
; break;
419 case ']': type
= FORMAT_RBRACK
; break;
420 case '~': type
= FORMAT_PROPNULL
; break;
421 case '\0': type
= FORMAT_NULL
; break;
436 while (data
[len
] && data
[len
] != ']')
439 type
= FORMAT_ESCAPE
;
441 else if (format_is_alpha(ch
))
443 while (format_is_literal(data
[len
]))
446 type
= FORMAT_LITERAL
;
448 else if (format_is_number(ch
))
450 while (format_is_number(data
[len
]))
453 type
= FORMAT_NUMBER
;
455 if (data
[len
] != ']')
457 while (format_is_literal(data
[len
]))
460 type
= FORMAT_LITERAL
;
465 ERR("Got unknown character %c(%x)\n", ch
, ch
);
476 static FORMSTR
*format_replace( FORMAT
*format
, BOOL propfound
, BOOL nonprop
,
477 int oldsize
, int type
, WCHAR
*replace
, int len
)
493 size
= format
->len
+ size
+ 1;
497 msi_free(format
->deformatted
);
498 format
->deformatted
= NULL
;
503 str
= msi_alloc(size
* sizeof(WCHAR
));
508 memcpy(str
, format
->deformatted
, format
->n
* sizeof(WCHAR
));
513 if (!len
) str
[n
++] = 0;
516 memcpy( str
+ n
, replace
, len
* sizeof(WCHAR
) );
522 ptr
= &format
->deformatted
[format
->n
+ oldsize
];
523 memcpy(&str
[n
], ptr
, (lstrlenW(ptr
) + 1) * sizeof(WCHAR
));
525 msi_free(format
->deformatted
);
526 format
->deformatted
= str
;
527 format
->len
= size
- 1;
529 /* don't reformat the NULL */
536 ret
= msi_alloc_zero(sizeof(FORMSTR
));
543 ret
->propfound
= propfound
;
544 ret
->nonprop
= nonprop
;
549 static WCHAR
*replace_stack_group( FORMAT
*format
, STACK
*values
,
550 BOOL
*propfound
, BOOL
*nonprop
,
551 int *oldsize
, int *type
, int *len
)
554 FORMSTR
*content
, *node
;
560 node
= stack_pop(values
);
562 *oldsize
= node
->len
;
565 while ((node
= stack_pop(values
)))
567 *oldsize
+= node
->len
;
578 content
= msi_alloc_zero(sizeof(FORMSTR
));
580 content
->len
= *oldsize
;
581 content
->type
= FORMAT_LITERAL
;
583 if (!format
->groupfailed
&& (*oldsize
== 2 ||
584 (format
->propfailed
&& !*nonprop
)))
589 else if (format
->deformatted
[content
->n
+ 1] == '{' &&
590 format
->deformatted
[content
->n
+ content
->len
- 2] == '}')
592 format
->groupfailed
= FALSE
;
595 else if (*propfound
&& !*nonprop
&&
596 !format
->groupfailed
&& format
->groups
== 0)
603 if (format
->groups
!= 0)
604 format
->groupfailed
= TRUE
;
609 replaced
= dup_formstr( format
, content
, len
);
610 *type
= content
->type
;
613 if (format
->groups
== 0)
614 format
->propfailed
= FALSE
;
619 static WCHAR
*replace_stack_prop( FORMAT
*format
, STACK
*values
,
620 BOOL
*propfound
, BOOL
*nonprop
,
621 int *oldsize
, int *type
, int *len
)
624 FORMSTR
*content
, *node
;
630 node
= stack_pop(values
);
632 *oldsize
= node
->len
;
633 *type
= stack_peek(values
)->type
;
636 while ((node
= stack_pop(values
)))
638 *oldsize
+= node
->len
;
640 if (*type
!= FORMAT_ESCAPE
&&
641 stack_peek(values
) && node
->type
!= *type
)
642 *type
= FORMAT_LITERAL
;
647 content
= msi_alloc_zero(sizeof(FORMSTR
));
649 content
->len
= *oldsize
- 2;
650 content
->type
= *type
;
652 if (*type
== FORMAT_NUMBER
&& format
->record
)
654 replaced
= deformat_index( format
, content
, len
);
658 format
->propfailed
= TRUE
;
661 *type
= format_str_is_number(replaced
) ?
662 FORMAT_NUMBER
: FORMAT_LITERAL
;
664 else if (format
->package
)
666 replaced
= deformat_literal( format
, content
, propfound
, type
, len
);
673 replaced
= dup_formstr( format
, content
, len
);
679 static UINT
replace_stack(FORMAT
*format
, STACK
*stack
, STACK
*values
)
681 WCHAR
*replaced
= NULL
;
682 FORMSTR
*beg
, *top
, *node
;
683 BOOL propfound
= FALSE
, nonprop
= FALSE
, group
= FALSE
;
684 int type
, n
, len
= 0, oldsize
= 0;
686 node
= stack_peek(values
);
690 if (type
== FORMAT_LBRACK
)
691 replaced
= replace_stack_prop( format
, values
, &propfound
,
692 &nonprop
, &oldsize
, &type
, &len
);
693 else if (type
== FORMAT_LBRACE
)
695 replaced
= replace_stack_group( format
, values
, &propfound
,
696 &nonprop
, &oldsize
, &type
, &len
);
701 beg
= format_replace( format
, propfound
, nonprop
, oldsize
, type
, replaced
, len
);
704 return ERROR_SUCCESS
;
706 format
->n
= beg
->n
+ beg
->len
;
708 top
= stack_peek(stack
);
713 if ((type
== FORMAT_LITERAL
|| type
== FORMAT_NUMBER
) &&
716 top
->len
+= beg
->len
;
719 top
->nonprop
= FALSE
;
721 if (type
== FORMAT_LITERAL
)
722 top
->nonprop
= beg
->nonprop
;
725 top
->propfound
= TRUE
;
728 return ERROR_SUCCESS
;
732 stack_push(stack
, beg
);
733 return ERROR_SUCCESS
;
736 static BOOL
verify_format(LPWSTR data
)
742 if (*data
== '[' && *(data
- 1) != '\\')
744 else if (*data
== ']')
756 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
757 WCHAR
** data
, DWORD
*len
,
770 return ERROR_SUCCESS
;
773 *data
= strdupW(ptr
);
774 *len
= lstrlenW(ptr
);
776 ZeroMemory(&format
, sizeof(FORMAT
));
777 format
.package
= package
;
778 format
.record
= record
;
779 format
.deformatted
= *data
;
782 if (!verify_format(*data
))
783 return ERROR_SUCCESS
;
785 stack
= create_stack();
786 temp
= create_stack();
788 while ((type
= format_lex(&format
, &str
)) != FORMAT_NULL
)
790 if (type
== FORMAT_LBRACK
|| type
== FORMAT_LBRACE
||
791 type
== FORMAT_LITERAL
|| type
== FORMAT_NUMBER
||
792 type
== FORMAT_ESCAPE
|| type
== FORMAT_PROPNULL
)
794 if (type
== FORMAT_LBRACE
)
796 format
.propfailed
= FALSE
;
799 else if (type
== FORMAT_ESCAPE
&&
800 !stack_find(stack
, FORMAT_LBRACK
))
802 format
.n
-= str
->len
- 1;
806 stack_push(stack
, str
);
808 else if (type
== FORMAT_RBRACK
|| type
== FORMAT_RBRACE
)
810 if (type
== FORMAT_RBRACE
)
813 stack_push(stack
, str
);
815 if (stack_find(stack
, left_type(type
)))
819 node
= stack_pop(stack
);
820 stack_push(temp
, node
);
821 } while (node
->type
!= left_type(type
));
823 replace_stack(&format
, stack
, temp
);
828 *data
= format
.deformatted
;
835 return ERROR_SUCCESS
;
838 UINT
MSI_FormatRecordW( MSIPACKAGE
* package
, MSIRECORD
* record
, LPWSTR buffer
,
841 WCHAR
*format
, *deformated
= NULL
;
842 UINT rc
= ERROR_INVALID_PARAMETER
;
844 MSIRECORD
*record_deformated
;
847 TRACE("%p %p %p %p\n", package
, record
, buffer
, size
);
850 if (!(format
= msi_dup_record_field( record
, 0 )))
851 format
= build_default_format( record
);
853 field_count
= MSI_RecordGetFieldCount(record
);
854 record_deformated
= MSI_CloneRecord(record
);
855 if (!record_deformated
)
857 rc
= ERROR_OUTOFMEMORY
;
860 MSI_RecordSetStringW(record_deformated
, 0, format
);
861 for (i
= 1; i
<= field_count
; i
++)
863 if (MSI_RecordGetString(record
, i
))
865 deformat_string_internal(package
, MSI_RecordGetString(record
, i
), &deformated
, &len
, NULL
);
866 MSI_RecordSetStringW(record_deformated
, i
, deformated
);
867 msi_free(deformated
);
871 deformat_string_internal(package
, format
, &deformated
, &len
, record_deformated
);
876 memcpy(buffer
,deformated
,len
*sizeof(WCHAR
));
884 memcpy(buffer
,deformated
,(*size
)*sizeof(WCHAR
));
885 buffer
[(*size
)-1] = 0;
887 rc
= ERROR_MORE_DATA
;
890 else rc
= ERROR_SUCCESS
;
893 msiobj_release(&record_deformated
->hdr
);
896 msi_free( deformated
);
900 UINT WINAPI
MsiFormatRecordW( MSIHANDLE hInstall
, MSIHANDLE hRecord
, WCHAR
*szResult
, DWORD
*sz
)
902 UINT r
= ERROR_INVALID_HANDLE
;
906 TRACE( "%lu, %lu, %p, %p\n", hInstall
, hRecord
, szResult
, sz
);
908 record
= msihandle2msiinfo(hRecord
, MSIHANDLETYPE_RECORD
);
910 return ERROR_INVALID_HANDLE
;
912 package
= msihandle2msiinfo( hInstall
, MSIHANDLETYPE_PACKAGE
);
918 if ((remote
= msi_get_remote(hInstall
)))
922 r
= remote_FormatRecord(remote
, (struct wire_record
*)&record
->count
, &value
);
926 r
= GetExceptionCode();
931 r
= msi_strncpyW(value
, -1, szResult
, sz
);
933 midl_user_free(value
);
934 msiobj_release(&record
->hdr
);
941 msiobj_release( &record
->hdr
);
943 return ERROR_INVALID_PARAMETER
;
945 return ERROR_SUCCESS
;
948 r
= MSI_FormatRecordW( package
, record
, szResult
, sz
);
949 msiobj_release( &record
->hdr
);
951 msiobj_release( &package
->hdr
);
955 UINT WINAPI
MsiFormatRecordA(MSIHANDLE hinst
, MSIHANDLE hrec
, char *buf
, DWORD
*sz
)
963 TRACE( "%lu, %lu, %p, %p\n", hinst
, hrec
, buf
, sz
);
965 rec
= msihandle2msiinfo(hrec
, MSIHANDLETYPE_RECORD
);
967 return ERROR_INVALID_HANDLE
;
969 package
= msihandle2msiinfo(hinst
, MSIHANDLETYPE_PACKAGE
);
975 if ((remote
= msi_get_remote(hinst
)))
979 r
= remote_FormatRecord(remote
, (struct wire_record
*)&rec
->count
, &value
);
983 r
= GetExceptionCode();
988 r
= msi_strncpyWtoA(value
, -1, buf
, sz
, TRUE
);
990 midl_user_free(value
);
991 msiobj_release(&rec
->hdr
);
996 r
= MSI_FormatRecordW(package
, rec
, NULL
, &len
);
997 if (r
!= ERROR_SUCCESS
)
1000 value
= msi_alloc(++len
* sizeof(WCHAR
));
1004 r
= MSI_FormatRecordW(package
, rec
, value
, &len
);
1006 r
= msi_strncpyWtoA(value
, len
, buf
, sz
, FALSE
);
1010 msiobj_release(&rec
->hdr
);
1011 if (package
) msiobj_release(&package
->hdr
);
1015 /* wrapper to resist a need for a full rewrite right now */
1016 DWORD
deformat_string( MSIPACKAGE
*package
, const WCHAR
*fmt
, WCHAR
**data
)
1023 if (!(rec
= MSI_CreateRecord( 1 ))) return 0;
1025 MSI_RecordSetStringW( rec
, 0, fmt
);
1026 MSI_FormatRecordW( package
, rec
, NULL
, &len
);
1027 if (!(*data
= msi_alloc( ++len
* sizeof(WCHAR
) )))
1029 msiobj_release( &rec
->hdr
);
1032 MSI_FormatRecordW( package
, rec
, *data
, &len
);
1033 msiobj_release( &rec
->hdr
);