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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
34 #include "wine/debug.h"
38 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
44 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
45 WCHAR
** data
, DWORD len
, MSIRECORD
* record
,
49 static LPWSTR
build_default_format(MSIRECORD
* record
)
54 static const WCHAR fmt
[] = {'%','i',':',' ','[','%','i',']',' ',0};
57 count
= MSI_RecordGetFieldCount(record
);
59 rc
= HeapAlloc(GetProcessHeap(),0,(11*count
)*sizeof(WCHAR
));
61 for (i
= 1; i
<= count
; i
++)
63 sprintfW(buf
,fmt
,i
,i
);
69 static const WCHAR
* scanW(LPCWSTR buf
, WCHAR token
, DWORD len
)
72 for (i
= 0; i
< len
; i
++)
78 /* break out helper functions for deformating */
79 static LPWSTR
deformat_component(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* sz
)
88 ERR("POORLY HANDLED DEFORMAT.. [$componentkey] \n");
89 comp
= get_loaded_component(package
,key
);
92 value
= resolve_folder(package
, comp
->Directory
, FALSE
, FALSE
, NULL
);
93 *sz
= (strlenW(value
)) * sizeof(WCHAR
);
99 static LPWSTR
deformat_file(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* sz
,
110 file
= get_loaded_file( package
, key
);
115 value
= strdupW( file
->TargetPath
);
116 *sz
= (strlenW(value
)) * sizeof(WCHAR
);
121 size
= GetShortPathNameW( file
->TargetPath
, NULL
, 0 );
125 *sz
= (size
-1) * sizeof (WCHAR
);
127 value
= HeapAlloc(GetProcessHeap(),0,size
* sizeof(WCHAR
));
128 GetShortPathNameW( file
->TargetPath
, value
, size
);
132 ERR("Unable to get ShortPath size (%s)\n",
133 debugstr_w( file
->TargetPath
) );
143 static LPWSTR
deformat_environment(MSIPACKAGE
* package
, LPCWSTR key
,
149 sz
= GetEnvironmentVariableW(key
,NULL
,0);
153 value
= HeapAlloc(GetProcessHeap(),0,sz
* sizeof(WCHAR
));
154 GetEnvironmentVariableW(&key
[1],value
,sz
);
155 *chunk
= (strlenW(value
)) * sizeof(WCHAR
);
159 ERR("Unknown environment variable %s\n", debugstr_w(key
));
167 static LPWSTR
deformat_NULL(DWORD
* chunk
)
171 value
= HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR
)*2);
173 *chunk
= sizeof(WCHAR
);
177 static LPWSTR
deformat_escape(LPCWSTR key
, DWORD
* chunk
)
181 value
= HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR
)*2);
183 *chunk
= sizeof(WCHAR
);
189 static BOOL
is_key_number(LPCWSTR key
)
195 while (isdigitW(key
[index
])) index
++;
202 static LPWSTR
deformat_index(MSIRECORD
* record
, LPCWSTR key
, DWORD
* chunk
)
208 TRACE("record index %i\n",index
);
209 value
= load_dynamic_stringW(record
,index
);
211 *chunk
= strlenW(value
) * sizeof(WCHAR
);
220 static LPWSTR
deformat_property(MSIPACKAGE
* package
, LPCWSTR key
, DWORD
* chunk
)
228 value
= load_dynamic_property(package
,key
, &rc
);
230 if (rc
== ERROR_SUCCESS
)
231 *chunk
= (strlenW(value
)) * sizeof(WCHAR
);
237 * Groups cannot be nested. They are just treated as from { to next }
239 static BOOL
find_next_group(LPCWSTR source
, DWORD len_remaining
,
240 LPWSTR
*group
, LPCWSTR
*mark
,
246 *mark
= scanW(source
,'{',len_remaining
);
250 for (i
= 1; (*mark
- source
) + i
< len_remaining
; i
++)
252 if ((*mark
)[i
] == '}')
261 *mark2
= &(*mark
)[i
];
264 *group
= HeapAlloc(GetProcessHeap(),0,i
*sizeof(WCHAR
));
267 memcpy(*group
,&(*mark
)[1],i
*sizeof(WCHAR
));
270 TRACE("Found group %s\n",debugstr_w(*group
));
275 static BOOL
find_next_outermost_key(LPCWSTR source
, DWORD len_remaining
,
276 LPWSTR
*key
, LPCWSTR
*mark
, LPCWSTR
* mark2
,
283 *mark
= scanW(source
,'[',len_remaining
);
290 for (i
= 1; (*mark
- source
) + i
< len_remaining
&& count
> 0; i
++)
292 if ((*mark
)[i
] == '[' && (*mark
)[i
-1] != '\\')
298 else if ((*mark
)[i
] == ']' && (*mark
)[i
-1] != '\\')
307 *mark2
= &(*mark
)[i
-1];
310 *key
= HeapAlloc(GetProcessHeap(),0,i
*sizeof(WCHAR
));
311 /* do not have the [] in the key */
313 memcpy(*key
,&(*mark
)[1],i
*sizeof(WCHAR
));
316 TRACE("Found Key %s\n",debugstr_w(*key
));
320 static LPWSTR
deformat_group(MSIPACKAGE
* package
, LPWSTR group
, DWORD len
,
321 MSIRECORD
* record
, DWORD
* size
)
328 static const WCHAR fmt
[] = {'{','%','s','}',0};
331 if (!group
|| group
[0] == 0)
336 /* if no [] then group is returned as is */
338 if (!find_next_outermost_key(group
, len
, &key
, &mark
, &mark2
, &nested
))
340 *size
= (len
+2)*sizeof(WCHAR
);
341 value
= HeapAlloc(GetProcessHeap(),0,*size
);
342 sprintfW(value
,fmt
,group
);
343 /* do not return size of the null at the end */
344 *size
= (len
+1)*sizeof(WCHAR
);
348 HeapFree(GetProcessHeap(),0,key
);
350 sz
= deformat_string_internal(package
, group
, &value
, strlenW(group
),
354 *size
= sz
* sizeof(WCHAR
);
357 else if (failcount
< 0)
361 v2
= HeapAlloc(GetProcessHeap(),0,(sz
+2)*sizeof(WCHAR
));
363 memcpy(&v2
[1],value
,sz
*sizeof(WCHAR
));
365 HeapFree(GetProcessHeap(),0,value
);
367 *size
= (sz
+2)*sizeof(WCHAR
);
380 * return is also in WCHARs
382 static DWORD
deformat_string_internal(MSIPACKAGE
*package
, LPCWSTR ptr
,
383 WCHAR
** data
, DWORD len
, MSIRECORD
* record
,
387 LPCWSTR mark2
= NULL
;
393 LPBYTE newdata
= NULL
;
394 const WCHAR
* progress
= NULL
;
399 TRACE("Deformatting NULL string\n");
404 TRACE("Starting with %s\n",debugstr_wn(ptr
,len
));
406 /* scan for special characters... fast exit */
407 if ((!scanW(ptr
,'[',len
) || (scanW(ptr
,'[',len
) && !scanW(ptr
,']',len
))) &&
408 (scanW(ptr
,'{',len
) && !scanW(ptr
,'}',len
)))
411 *data
= HeapAlloc(GetProcessHeap(),0,(len
*sizeof(WCHAR
)));
412 memcpy(*data
,ptr
,len
*sizeof(WCHAR
));
413 TRACE("Returning %s\n",debugstr_wn(*data
,len
));
419 while (progress
- ptr
< len
)
421 /* seek out first group if existing */
422 if (find_next_group(progress
, len
- (progress
- ptr
), &key
,
425 value
= deformat_group(package
, key
, strlenW(key
)+1, record
,
430 /* formatted string located */
431 else if (!find_next_outermost_key(progress
, len
- (progress
- ptr
),
432 &key
, &mark
, &mark2
, &nested
))
436 TRACE("after value %s \n",debugstr_wn((LPWSTR
)newdata
,
437 size
/sizeof(WCHAR
)));
438 chunk
= (len
- (progress
- ptr
)) * sizeof(WCHAR
);
439 TRACE("after chunk is %li + %li\n",size
,chunk
);
441 nd2
= HeapReAlloc(GetProcessHeap(),0,newdata
,(size
+chunk
));
443 nd2
= HeapAlloc(GetProcessHeap(),0,chunk
);
446 memcpy(&newdata
[size
],progress
,chunk
);
451 if (mark
!= progress
)
454 DWORD old_size
= size
;
455 INT cnt
= (mark
- progress
);
456 TRACE("%i (%i) characters before marker\n",cnt
,(mark
-progress
));
457 size
+= cnt
* sizeof(WCHAR
);
459 tgt
= HeapAlloc(GetProcessHeap(),0,size
);
461 tgt
= HeapReAlloc(GetProcessHeap(),0,newdata
,size
);
463 memcpy(&newdata
[old_size
],progress
,(cnt
* sizeof(WCHAR
)));
470 TRACE("Nested key... %s\n",debugstr_w(key
));
471 deformat_string_internal(package
, key
, &value
, strlenW(key
)+1,
474 HeapFree(GetProcessHeap(),0,key
);
478 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR
)newdata
,
479 size
/sizeof(WCHAR
)),debugstr_w(key
));
483 /* only deformat number indexs */
484 if (key
&& is_key_number(key
))
486 value
= deformat_index(record
,key
,&chunk
);
487 if (!chunk
&& failcount
&& *failcount
>= 0)
496 DWORD keylen
= strlenW(key
);
497 chunk
= (keylen
+ 2)*sizeof(WCHAR
);
498 value
= HeapAlloc(GetProcessHeap(),0,chunk
);
500 memcpy(&value
[1],key
,keylen
*sizeof(WCHAR
));
501 value
[1+keylen
] = ']';
508 if (key
) switch (key
[0])
511 value
= deformat_NULL(&chunk
);
514 value
= deformat_component(package
,&key
[1],&chunk
);
517 value
= deformat_file(package
,&key
[1], &chunk
, FALSE
);
519 case '!': /* should be short path */
520 value
= deformat_file(package
,&key
[1], &chunk
, TRUE
);
523 value
= deformat_escape(&key
[1],&chunk
);
526 value
= deformat_environment(package
,&key
[1],&chunk
);
529 /* index keys cannot be nested */
530 if (is_key_number(key
))
532 value
= deformat_index(record
,key
,&chunk
);
535 static const WCHAR fmt
[] = {'[','%','s',']',0};
536 value
= HeapAlloc(GetProcessHeap(),0,10);
537 sprintfW(value
,fmt
,key
);
538 chunk
= strlenW(value
)*sizeof(WCHAR
);
541 value
= deformat_property(package
,key
,&chunk
);
546 HeapFree(GetProcessHeap(),0,key
);
551 TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR
)value
),
554 nd2
= HeapReAlloc(GetProcessHeap(),0,newdata
,(size
+ chunk
));
556 nd2
= HeapAlloc(GetProcessHeap(),0,chunk
);
558 memcpy(&newdata
[size
],value
,chunk
);
560 HeapFree(GetProcessHeap(),0,value
);
562 else if (failcount
&& *failcount
>=0 )
568 TRACE("after everything %s\n",debugstr_wn((LPWSTR
)newdata
,
569 size
/sizeof(WCHAR
)));
571 *data
= (LPWSTR
)newdata
;
572 return size
/ sizeof(WCHAR
);
576 UINT
MSI_FormatRecordW( MSIPACKAGE
* package
, MSIRECORD
* record
, LPWSTR buffer
,
582 UINT rc
= ERROR_INVALID_PARAMETER
;
584 TRACE("%p %p %p %li\n",package
, record
,buffer
, *size
);
586 rec
= load_dynamic_stringW(record
,0);
588 rec
= build_default_format(record
);
590 TRACE("(%s)\n",debugstr_w(rec
));
592 len
= deformat_string_internal(package
,rec
,&deformated
,strlenW(rec
),
599 memcpy(buffer
,deformated
,len
*sizeof(WCHAR
));
607 memcpy(buffer
,deformated
,(*size
)*sizeof(WCHAR
));
608 buffer
[(*size
)-1] = 0;
610 rc
= ERROR_MORE_DATA
;
618 HeapFree(GetProcessHeap(),0,rec
);
619 HeapFree(GetProcessHeap(),0,deformated
);
623 UINT
MSI_FormatRecordA( MSIPACKAGE
* package
, MSIRECORD
* record
, LPSTR buffer
,
629 UINT rc
= ERROR_INVALID_PARAMETER
;
631 TRACE("%p %p %p %li\n",package
, record
,buffer
, *size
);
633 rec
= load_dynamic_stringW(record
,0);
635 rec
= build_default_format(record
);
637 TRACE("(%s)\n",debugstr_w(rec
));
639 len
= deformat_string_internal(package
,rec
,&deformated
,strlenW(rec
),
641 lenA
= WideCharToMultiByte(CP_ACP
,0,deformated
,len
,NULL
,0,NULL
,NULL
);
645 WideCharToMultiByte(CP_ACP
,0,deformated
,len
,buffer
,*size
,NULL
, NULL
);
653 rc
= ERROR_MORE_DATA
;
654 buffer
[(*size
)-1] = 0;
662 HeapFree(GetProcessHeap(),0,rec
);
663 HeapFree(GetProcessHeap(),0,deformated
);
668 UINT WINAPI
MsiFormatRecordW( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
669 LPWSTR szResult
, DWORD
*sz
)
671 UINT r
= ERROR_INVALID_HANDLE
;
675 TRACE("%ld %ld %p %p\n", hInstall
, hRecord
, szResult
, sz
);
677 record
= msihandle2msiinfo( hRecord
, MSIHANDLETYPE_RECORD
);
680 return ERROR_INVALID_HANDLE
;
683 msiobj_release( &record
->hdr
);
685 return ERROR_INVALID_PARAMETER
;
687 return ERROR_SUCCESS
;
690 package
= msihandle2msiinfo( hInstall
, MSIHANDLETYPE_PACKAGE
);
692 r
= MSI_FormatRecordW( package
, record
, szResult
, sz
);
693 msiobj_release( &record
->hdr
);
695 msiobj_release( &package
->hdr
);
699 UINT WINAPI
MsiFormatRecordA( MSIHANDLE hInstall
, MSIHANDLE hRecord
,
700 LPSTR szResult
, DWORD
*sz
)
702 UINT r
= ERROR_INVALID_HANDLE
;
703 MSIPACKAGE
*package
= NULL
;
704 MSIRECORD
*record
= NULL
;
706 TRACE("%ld %ld %p %p\n", hInstall
, hRecord
, szResult
, sz
);
708 record
= msihandle2msiinfo( hRecord
, MSIHANDLETYPE_RECORD
);
711 return ERROR_INVALID_HANDLE
;
714 msiobj_release( &record
->hdr
);
716 return ERROR_INVALID_PARAMETER
;
718 return ERROR_SUCCESS
;
721 package
= msihandle2msiinfo( hInstall
, MSIHANDLETYPE_PACKAGE
);
723 r
= MSI_FormatRecordA( package
, record
, szResult
, sz
);
724 msiobj_release( &record
->hdr
);
726 msiobj_release( &package
->hdr
);