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
35 #include "wine/debug.h"
39 #include "msvcrt/fcntl.h"
46 #include "wine/unicode.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
54 WCHAR** data, DWORD len, MSIRECORD* record,
58 LPWSTR build_default_format(MSIRECORD* record)
63 static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
66 count = MSI_RecordGetFieldCount(record);
68 rc = HeapAlloc(GetProcessHeap(),0,(11*count)*sizeof(WCHAR));
70 for (i = 1; i <= count; i++)
72 sprintfW(buf,fmt,i,i);
78 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
81 for (i = 0; i < len; i++)
87 /* break out helper functions for deformating */
88 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
97 ERR("POORLY HANDLED DEFORMAT.. [$componentkey] \n");
98 index = get_loaded_component(package,key);
101 value = resolve_folder(package, package->components[index].Directory,
103 *sz = (strlenW(value)) * sizeof(WCHAR);
109 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
120 index = get_loaded_file(package,key);
125 value = strdupW(package->files[index].TargetPath);
126 *sz = (strlenW(value)) * sizeof(WCHAR);
131 size = GetShortPathNameW(package->files[index].TargetPath, NULL, 0);
135 *sz = (size-1) * sizeof (WCHAR);
137 value = HeapAlloc(GetProcessHeap(),0,size * sizeof(WCHAR));
138 GetShortPathNameW(package->files[index].TargetPath, value,
143 ERR("Unable to get ShortPath size (%s)\n",
144 debugstr_w(package->files[index].TargetPath));
154 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
160 sz = GetEnvironmentVariableW(key,NULL,0);
164 value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
165 GetEnvironmentVariableW(&key[1],value,sz);
166 *chunk = (strlenW(value)) * sizeof(WCHAR);
170 ERR("Unknown environment variable %s\n", debugstr_w(key));
178 static LPWSTR deformat_NULL(DWORD* chunk)
182 value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
184 *chunk = sizeof(WCHAR);
188 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
192 value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
194 *chunk = sizeof(WCHAR);
200 static BOOL is_key_number(LPCWSTR key)
206 while (isdigitW(key[index])) index++;
213 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
219 TRACE("record index %i\n",index);
220 value = load_dynamic_stringW(record,index);
222 *chunk = strlenW(value) * sizeof(WCHAR);
231 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
239 value = load_dynamic_property(package,key, &rc);
241 if (rc == ERROR_SUCCESS)
242 *chunk = (strlenW(value)) * sizeof(WCHAR);
248 * Groups cannot be nested. They are just treated as from { to next }
250 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
251 LPWSTR *group, LPCWSTR *mark,
257 *mark = scanW(source,'{',len_remaining);
261 for (i = 1; (*mark - source) + i < len_remaining; i++)
263 if ((*mark)[i] == '}')
272 *mark2 = &(*mark)[i];
275 *group = HeapAlloc(GetProcessHeap(),0,i*sizeof(WCHAR));
278 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
281 TRACE("Found group %s\n",debugstr_w(*group));
286 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
287 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
294 *mark = scanW(source,'[',len_remaining);
301 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
303 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
309 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
318 *mark2 = &(*mark)[i-1];
321 *key = HeapAlloc(GetProcessHeap(),0,i*sizeof(WCHAR));
322 /* do not have the [] in the key */
324 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
327 TRACE("Found Key %s\n",debugstr_w(*key));
331 LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
332 MSIRECORD* record, DWORD* size)
339 static const WCHAR fmt[] = {'{','%','s','}',0};
342 if (!group || group[0] == 0)
347 /* if no [] then group is returned as is */
349 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
351 *size = (len+2)*sizeof(WCHAR);
352 value = HeapAlloc(GetProcessHeap(),0,*size);
353 sprintfW(value,fmt,group);
354 /* do not return size of the null at the end */
355 *size = (len+1)*sizeof(WCHAR);
359 HeapFree(GetProcessHeap(),0,key);
361 sz = deformat_string_internal(package, group, &value, strlenW(group),
365 *size = sz * sizeof(WCHAR);
368 else if (failcount < 0)
372 v2 = HeapAlloc(GetProcessHeap(),0,(sz+2)*sizeof(WCHAR));
374 memcpy(&v2[1],value,sz*sizeof(WCHAR));
376 HeapFree(GetProcessHeap(),0,value);
378 *size = (sz+2)*sizeof(WCHAR);
391 * return is also in WCHARs
393 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
394 WCHAR** data, DWORD len, MSIRECORD* record,
398 LPCWSTR mark2 = NULL;
404 LPBYTE newdata = NULL;
405 const WCHAR* progress = NULL;
410 TRACE("Deformatting NULL string\n");
415 TRACE("Starting with %s\n",debugstr_w(ptr));
417 /* scan for special characters... fast exit */
418 if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
419 (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
422 *data = HeapAlloc(GetProcessHeap(),0,(len*sizeof(WCHAR)));
423 memcpy(*data,ptr,len*sizeof(WCHAR));
424 TRACE("Returning %s\n",debugstr_wn(*data,len));
430 while (progress - ptr < len)
432 /* seek out first group if existing */
433 if (find_next_group(progress, len - (progress - ptr), &key,
436 value = deformat_group(package, key, strlenW(key)+1, record,
441 /* formatted string located */
442 else if (!find_next_outermost_key(progress, len - (progress - ptr),
443 &key, &mark, &mark2, &nested))
447 TRACE("after value %s .. %s\n",debugstr_w((LPWSTR)newdata),
449 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
450 TRACE("after chunk is %li + %li\n",size,chunk);
452 nd2 = HeapReAlloc(GetProcessHeap(),0,newdata,(size+chunk));
454 nd2 = HeapAlloc(GetProcessHeap(),0,chunk);
457 memcpy(&newdata[size],progress,chunk);
462 if (mark != progress)
465 DWORD old_size = size;
466 INT cnt = (mark - progress);
467 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
468 size += cnt * sizeof(WCHAR);
470 tgt = HeapAlloc(GetProcessHeap(),0,size);
472 tgt = HeapReAlloc(GetProcessHeap(),0,newdata,size);
474 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
481 TRACE("Nested key... %s\n",debugstr_w(key));
482 deformat_string_internal(package, key, &value, strlenW(key)+1,
485 HeapFree(GetProcessHeap(),0,key);
489 TRACE("Current %s .. %s\n",debugstr_w((LPWSTR)newdata),debugstr_w(key));
493 /* only deformat number indexs */
494 if (key && is_key_number(key))
496 value = deformat_index(record,key,&chunk);
497 if (!chunk && failcount && *failcount >= 0)
506 DWORD keylen = strlenW(key);
507 chunk = (keylen + 2)*sizeof(WCHAR);
508 value = HeapAlloc(GetProcessHeap(),0,chunk);
510 memcpy(&value[1],key,keylen*sizeof(WCHAR));
511 value[1+keylen] = ']';
518 if (key) switch (key[0])
521 value = deformat_NULL(&chunk);
524 value = deformat_component(package,&key[1],&chunk);
527 value = deformat_file(package,&key[1], &chunk, FALSE);
529 case '!': /* should be short path */
530 value = deformat_file(package,&key[1], &chunk, TRUE);
533 value = deformat_escape(&key[1],&chunk);
536 value = deformat_environment(package,&key[1],&chunk);
539 /* index keys cannot be nested */
540 if (is_key_number(key))
542 value = deformat_index(record,key,&chunk);
545 static const WCHAR fmt[] = {'[','%','s',']',0};
546 value = HeapAlloc(GetProcessHeap(),0,10);
547 sprintfW(value,fmt,key);
548 chunk = strlenW(value)*sizeof(WCHAR);
551 value = deformat_property(package,key,&chunk);
556 HeapFree(GetProcessHeap(),0,key);
561 TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
564 nd2= HeapReAlloc(GetProcessHeap(),0,newdata,(size + chunk));
566 nd2= HeapAlloc(GetProcessHeap(),0,chunk);
568 memcpy(&newdata[size],value,chunk);
570 HeapFree(GetProcessHeap(),0,value);
572 else if (failcount && *failcount >=0 )
578 TRACE("after everything %s\n",debugstr_w((LPWSTR)newdata));
580 *data = (LPWSTR)newdata;
581 return size / sizeof(WCHAR);
585 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
591 UINT rc = ERROR_INVALID_PARAMETER;
593 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
595 rec = load_dynamic_stringW(record,0);
597 rec = build_default_format(record);
599 TRACE("(%s)\n",debugstr_w(rec));
601 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
608 memcpy(buffer,deformated,len*sizeof(WCHAR));
616 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
617 buffer[(*size)-1] = 0;
619 rc = ERROR_MORE_DATA;
627 HeapFree(GetProcessHeap(),0,rec);
628 HeapFree(GetProcessHeap(),0,deformated);
632 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
638 UINT rc = ERROR_INVALID_PARAMETER;
640 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
642 rec = load_dynamic_stringW(record,0);
644 rec = build_default_format(record);
646 TRACE("(%s)\n",debugstr_w(rec));
648 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
650 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
654 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
662 rc = ERROR_MORE_DATA;
663 buffer[(*size)-1] = 0;
671 HeapFree(GetProcessHeap(),0,rec);
672 HeapFree(GetProcessHeap(),0,deformated);
677 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
678 LPWSTR szResult, DWORD *sz )
680 UINT r = ERROR_INVALID_HANDLE;
684 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
686 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
689 return ERROR_INVALID_HANDLE;
692 msiobj_release( &record->hdr );
694 return ERROR_INVALID_PARAMETER;
696 return ERROR_SUCCESS;
699 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
701 r = MSI_FormatRecordW( package, record, szResult, sz );
702 msiobj_release( &record->hdr );
704 msiobj_release( &package->hdr );
708 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
709 LPSTR szResult, DWORD *sz )
711 UINT r = ERROR_INVALID_HANDLE;
712 MSIPACKAGE *package = NULL;
713 MSIRECORD *record = NULL;
715 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
717 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
720 return ERROR_INVALID_HANDLE;
723 msiobj_release( &record->hdr );
725 return ERROR_INVALID_PARAMETER;
727 return ERROR_SUCCESS;
730 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
732 r = MSI_FormatRecordA( package, record, szResult, sz );
733 msiobj_release( &record->hdr );
735 msiobj_release( &package->hdr );