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
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
34 #include "wine/debug.h"
41 #include "msiserver.h"
42 #include "wine/unicode.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
47 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
48 WCHAR
** data
, DWORD len
, MSIRECORD
* record
,
52 static LPWSTR
build_default_format(const MSIRECORD
* record
)
57 static const WCHAR fmt
[] = {'%','i',':',' ','%','s',' ',0};
58 static const WCHAR fmt_null
[] = {'%','i',':',' ',' ',0};
59 static const WCHAR fmt_index
[] = {'%','i',0};
62 DWORD size
, max_len
, len
;
64 count
= MSI_RecordGetFieldCount(record
);
67 buf
= msi_alloc((max_len
+ 1) * sizeof(WCHAR
));
71 for (i
= 1; i
<= count
; i
++)
73 sprintfW(index
,fmt_index
,i
);
74 str
= MSI_RecordGetString(record
, i
);
75 len
= (str
) ? lstrlenW(str
) : 0;
76 len
+= (sizeof(fmt_null
) - 3) + lstrlenW(index
);
82 buf
= msi_realloc(buf
, (max_len
+ 1) * sizeof(WCHAR
));
83 if (!buf
) return NULL
;
87 sprintfW(buf
,fmt
,i
,str
);
89 sprintfW(buf
,fmt_null
,i
);
93 rc
= msi_alloc(size
* sizeof(WCHAR
));
98 rc
= msi_realloc(rc
, size
* sizeof(WCHAR
));
106 static const WCHAR
* scanW(LPCWSTR buf
, WCHAR token
, DWORD len
)
109 for (i
= 0; i
< len
; i
++)
115 /* break out helper functions for deformating */
116 static LPWSTR
deformat_component(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* sz
)
126 comp
= get_loaded_component(package
,key
);
129 source
= (comp
->Action
== INSTALLSTATE_SOURCE
) ? TRUE
: FALSE
;
130 value
= resolve_folder(package
, comp
->Directory
, source
, FALSE
, TRUE
, NULL
);
131 *sz
= (strlenW(value
)) * sizeof(WCHAR
);
137 static LPWSTR
deformat_file(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* sz
,
148 file
= get_loaded_file( package
, key
);
153 value
= strdupW( file
->TargetPath
);
154 *sz
= (strlenW(value
)) * sizeof(WCHAR
);
159 size
= GetShortPathNameW( file
->TargetPath
, NULL
, 0 );
163 *sz
= (size
-1) * sizeof (WCHAR
);
165 value
= msi_alloc(size
* sizeof(WCHAR
));
166 GetShortPathNameW( file
->TargetPath
, value
, size
);
170 value
= strdupW( file
->TargetPath
);
171 *sz
= (lstrlenW(value
)) * sizeof(WCHAR
);
179 static LPWSTR
deformat_environment(MSIPACKAGE
* package
, LPCWSTR key
,
185 sz
= GetEnvironmentVariableW(key
,NULL
,0);
189 value
= msi_alloc(sz
* sizeof(WCHAR
));
190 GetEnvironmentVariableW(key
,value
,sz
);
191 *chunk
= (strlenW(value
)) * sizeof(WCHAR
);
195 ERR("Unknown environment variable %s\n", debugstr_w(key
));
203 static LPWSTR
deformat_NULL(DWORD
* chunk
)
207 value
= msi_alloc(sizeof(WCHAR
)*2);
209 *chunk
= sizeof(WCHAR
);
213 static LPWSTR
deformat_escape(LPCWSTR key
, DWORD
* chunk
)
217 value
= msi_alloc(sizeof(WCHAR
)*2);
219 *chunk
= sizeof(WCHAR
);
225 static BOOL
is_key_number(LPCWSTR key
)
231 while (isdigitW(key
[index
])) index
++;
238 static LPWSTR
deformat_index(MSIRECORD
* record
, LPCWSTR key
, DWORD
* chunk
)
244 TRACE("record index %i\n",index
);
245 value
= msi_dup_record_field(record
,index
);
247 *chunk
= strlenW(value
) * sizeof(WCHAR
);
256 static LPWSTR
deformat_property(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* chunk
)
263 value
= msi_dup_property( package
, key
);
266 *chunk
= (strlenW(value
)) * sizeof(WCHAR
);
272 * Groups cannot be nested. They are just treated as from { to next }
274 static BOOL
find_next_group(LPCWSTR source
, DWORD len_remaining
,
275 LPWSTR
*group
, LPCWSTR
*mark
,
281 *mark
= scanW(source
,'{',len_remaining
);
285 for (i
= 1; (*mark
- source
) + i
< len_remaining
; i
++)
287 if ((*mark
)[i
] == '}')
296 *mark2
= &(*mark
)[i
];
299 *group
= msi_alloc(i
*sizeof(WCHAR
));
302 memcpy(*group
,&(*mark
)[1],i
*sizeof(WCHAR
));
305 TRACE("Found group %s\n",debugstr_w(*group
));
310 static BOOL
find_next_outermost_key(LPCWSTR source
, DWORD len_remaining
,
311 LPWSTR
*key
, LPCWSTR
*mark
, LPCWSTR
* mark2
,
319 *mark
= scanW(source
,'[',len_remaining
);
325 for (i
= 1; (*mark
- source
) + i
< len_remaining
&& count
> 0; i
++)
327 if ((*mark
)[i
] == '[' && (*mark
)[i
-1] != '\\')
333 else if ((*mark
)[i
] == ']' && (*mark
)[i
-1] != '\\')
342 *mark2
= &(*mark
)[i
-1];
345 *key
= msi_alloc(i
*sizeof(WCHAR
));
346 /* do not have the [] in the key */
348 memcpy(*key
,&(*mark
)[1],i
*sizeof(WCHAR
));
351 TRACE("Found Key %s\n",debugstr_w(*key
));
355 static LPWSTR
deformat_group(MSIPACKAGE
* package
, LPWSTR group
, DWORD len
,
356 MSIRECORD
* record
, DWORD
* size
)
363 static const WCHAR fmt
[] = {'{','%','s','}',0};
366 if (!group
|| group
[0] == 0)
371 /* if no [] then group is returned as is */
373 if (!find_next_outermost_key(group
, len
, &key
, &mark
, &mark2
, &nested
))
375 *size
= (len
+2)*sizeof(WCHAR
);
376 value
= msi_alloc(*size
);
377 sprintfW(value
,fmt
,group
);
378 /* do not return size of the null at the end */
379 *size
= (len
+1)*sizeof(WCHAR
);
385 sz
= deformat_string_internal(package
, group
, &value
, strlenW(group
),
389 *size
= sz
* sizeof(WCHAR
);
392 else if (failcount
< 0)
396 v2
= msi_alloc((sz
+2)*sizeof(WCHAR
));
398 memcpy(&v2
[1],value
,sz
*sizeof(WCHAR
));
402 *size
= (sz
+2)*sizeof(WCHAR
);
416 * return is also in WCHARs
418 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
419 WCHAR
** data
, DWORD len
, MSIRECORD
* record
,
423 LPCWSTR mark2
= NULL
;
429 LPBYTE newdata
= NULL
;
430 const WCHAR
* progress
= NULL
;
435 TRACE("Deformatting NULL string\n");
440 TRACE("Starting with %s\n",debugstr_wn(ptr
,len
));
442 /* scan for special characters... fast exit */
443 if ((!scanW(ptr
,'[',len
) || (scanW(ptr
,'[',len
) && !scanW(ptr
,']',len
))) &&
444 (scanW(ptr
,'{',len
) && !scanW(ptr
,'}',len
)))
447 *data
= msi_alloc((len
*sizeof(WCHAR
)));
448 memcpy(*data
,ptr
,len
*sizeof(WCHAR
));
449 TRACE("Returning %s\n",debugstr_wn(*data
,len
));
455 while (progress
- ptr
< len
)
457 /* seek out first group if existing */
458 if (find_next_group(progress
, len
- (progress
- ptr
), &key
,
461 value
= deformat_group(package
, key
, strlenW(key
)+1, record
,
467 /* formatted string located */
468 else if (!find_next_outermost_key(progress
, len
- (progress
- ptr
),
469 &key
, &mark
, &mark2
, &nested
))
473 TRACE("after value %s\n", debugstr_wn((LPWSTR
)newdata
,
474 size
/sizeof(WCHAR
)));
475 chunk
= (len
- (progress
- ptr
)) * sizeof(WCHAR
);
476 TRACE("after chunk is %i + %i\n",size
,chunk
);
478 nd2
= msi_realloc(newdata
,(size
+chunk
));
480 nd2
= msi_alloc(chunk
);
483 memcpy(&newdata
[size
],progress
,chunk
);
488 if (mark
!= progress
)
491 DWORD old_size
= size
;
492 INT cnt
= (mark
- progress
);
493 TRACE("%i (%i) characters before marker\n",cnt
,(mark
-progress
));
494 size
+= cnt
* sizeof(WCHAR
);
496 tgt
= msi_alloc(size
);
498 tgt
= msi_realloc(newdata
,size
);
500 memcpy(&newdata
[old_size
],progress
,(cnt
* sizeof(WCHAR
)));
507 TRACE("Nested key... %s\n",debugstr_w(key
));
508 deformat_string_internal(package
, key
, &value
, strlenW(key
)+1,
515 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR
)newdata
,
516 size
/sizeof(WCHAR
)),debugstr_w(key
));
520 /* only deformat number indexs */
521 if (key
&& is_key_number(key
))
523 value
= deformat_index(record
,key
,&chunk
);
524 if (!chunk
&& failcount
&& *failcount
>= 0)
533 DWORD keylen
= strlenW(key
);
534 chunk
= (keylen
+ 2)*sizeof(WCHAR
);
535 value
= msi_alloc(chunk
);
537 memcpy(&value
[1],key
,keylen
*sizeof(WCHAR
));
538 value
[1+keylen
] = ']';
545 if (key
) switch (key
[0])
548 value
= deformat_NULL(&chunk
);
551 value
= deformat_component(package
,&key
[1],&chunk
);
554 value
= deformat_file(package
,&key
[1], &chunk
, FALSE
);
556 case '!': /* should be short path */
557 value
= deformat_file(package
,&key
[1], &chunk
, TRUE
);
560 value
= deformat_escape(&key
[1],&chunk
);
563 value
= deformat_environment(package
,&key
[1],&chunk
);
566 /* index keys cannot be nested */
567 if (is_key_number(key
))
569 value
= deformat_index(record
,key
,&chunk
);
572 static const WCHAR fmt
[] = {'[','%','s',']',0};
573 value
= msi_alloc(10);
574 sprintfW(value
,fmt
,key
);
575 chunk
= strlenW(value
)*sizeof(WCHAR
);
578 value
= deformat_property(package
,key
,&chunk
);
588 TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR
)value
),
591 nd2
= msi_realloc(newdata
,(size
+ chunk
));
593 nd2
= msi_alloc(chunk
);
595 memcpy(&newdata
[size
],value
,chunk
);
599 else if (failcount
&& *failcount
>=0 )
605 TRACE("after everything %s\n",debugstr_wn((LPWSTR
)newdata
,
606 size
/sizeof(WCHAR
)));
608 *data
= (LPWSTR
)newdata
;
609 return size
/ sizeof(WCHAR
);
613 UINT
MSI_FormatRecordW( MSIPACKAGE
* package
, MSIRECORD
* record
, LPWSTR buffer
,
619 UINT rc
= ERROR_INVALID_PARAMETER
;
621 TRACE("%p %p %p %i\n", package
, record
,buffer
, *size
);
623 rec
= msi_dup_record_field(record
,0);
625 rec
= build_default_format(record
);
627 TRACE("(%s)\n",debugstr_w(rec
));
629 len
= deformat_string_internal(package
,rec
,&deformated
,
630 rec
? strlenW(rec
) : 0, record
, NULL
);
636 memcpy(buffer
,deformated
,len
*sizeof(WCHAR
));
644 memcpy(buffer
,deformated
,(*size
)*sizeof(WCHAR
));
645 buffer
[(*size
)-1] = 0;
647 rc
= ERROR_MORE_DATA
;
656 msi_free(deformated
);
660 UINT WINAPI
MsiFormatRecordW( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
661 LPWSTR szResult
, LPDWORD sz
)
663 UINT r
= ERROR_INVALID_HANDLE
;
667 TRACE("%ld %ld %p %p\n", hInstall
, hRecord
, szResult
, sz
);
669 package
= msihandle2msiinfo( hInstall
, MSIHANDLETYPE_PACKAGE
);
673 IWineMsiRemotePackage
*remote_package
;
678 remote_package
= (IWineMsiRemotePackage
*)msi_get_remote( hInstall
);
682 hr
= IWineMsiRemotePackage_FormatRecord( remote_package
, hRecord
,
688 value
= SysAllocStringLen( NULL
, len
);
691 r
= ERROR_OUTOFMEMORY
;
695 hr
= IWineMsiRemotePackage_FormatRecord( remote_package
, hRecord
,
701 wstr
.str
.w
= szResult
;
702 r
= msi_strcpy_to_awstring( value
, &wstr
, sz
);
705 IWineMsiRemotePackage_Release( remote_package
);
706 SysFreeString( value
);
710 if (HRESULT_FACILITY(hr
) == FACILITY_WIN32
)
711 return HRESULT_CODE(hr
);
713 return ERROR_FUNCTION_FAILED
;
720 record
= msihandle2msiinfo( hRecord
, MSIHANDLETYPE_RECORD
);
723 return ERROR_INVALID_HANDLE
;
726 msiobj_release( &record
->hdr
);
728 return ERROR_INVALID_PARAMETER
;
730 return ERROR_SUCCESS
;
733 r
= MSI_FormatRecordW( package
, record
, szResult
, sz
);
734 msiobj_release( &record
->hdr
);
736 msiobj_release( &package
->hdr
);
740 UINT WINAPI
MsiFormatRecordA( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
741 LPSTR szResult
, LPDWORD sz
)
747 TRACE("%ld %ld %p %p\n", hInstall
, hRecord
, szResult
, sz
);
750 return ERROR_INVALID_HANDLE
;
755 return ERROR_INVALID_PARAMETER
;
757 return ERROR_SUCCESS
;
760 r
= MsiFormatRecordW( hInstall
, hRecord
, NULL
, &len
);
761 if (r
!= ERROR_SUCCESS
)
764 value
= msi_alloc(++len
* sizeof(WCHAR
));
766 return ERROR_OUTOFMEMORY
;
768 r
= MsiFormatRecordW( hInstall
, hRecord
, value
, &len
);
769 if (r
!= ERROR_SUCCESS
)
773 len
= WideCharToMultiByte(CP_ACP
, 0, value
, -1, NULL
, 0, NULL
, NULL
);
774 WideCharToMultiByte(CP_ACP
, 0, value
, -1, szResult
, *sz
, NULL
, NULL
);
776 if (szResult
&& len
> *sz
)
778 if (*sz
) szResult
[*sz
- 1] = '\0';