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"
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 = msi_alloc((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 FIXME("component key %s\n", debugstr_w(key));
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 = msi_alloc(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 = msi_alloc(sz * sizeof(WCHAR));
154 GetEnvironmentVariableW(key,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 = msi_alloc(sizeof(WCHAR)*2);
173 *chunk = sizeof(WCHAR);
177 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
181 value = msi_alloc(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 = msi_dup_record_field(record,index);
211 *chunk = strlenW(value) * sizeof(WCHAR);
220 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
227 value = msi_dup_property( package, key );
230 *chunk = (strlenW(value)) * sizeof(WCHAR);
236 * Groups cannot be nested. They are just treated as from { to next }
238 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
239 LPWSTR *group, LPCWSTR *mark,
245 *mark = scanW(source,'{',len_remaining);
249 for (i = 1; (*mark - source) + i < len_remaining; i++)
251 if ((*mark)[i] == '}')
260 *mark2 = &(*mark)[i];
263 *group = msi_alloc(i*sizeof(WCHAR));
266 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
269 TRACE("Found group %s\n",debugstr_w(*group));
274 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
275 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
282 *mark = scanW(source,'[',len_remaining);
289 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
291 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
297 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
306 *mark2 = &(*mark)[i-1];
309 *key = msi_alloc(i*sizeof(WCHAR));
310 /* do not have the [] in the key */
312 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
315 TRACE("Found Key %s\n",debugstr_w(*key));
319 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
320 MSIRECORD* record, DWORD* size)
327 static const WCHAR fmt[] = {'{','%','s','}',0};
330 if (!group || group[0] == 0)
335 /* if no [] then group is returned as is */
337 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
339 *size = (len+2)*sizeof(WCHAR);
340 value = msi_alloc(*size);
341 sprintfW(value,fmt,group);
342 /* do not return size of the null at the end */
343 *size = (len+1)*sizeof(WCHAR);
349 sz = deformat_string_internal(package, group, &value, strlenW(group),
353 *size = sz * sizeof(WCHAR);
356 else if (failcount < 0)
360 v2 = msi_alloc((sz+2)*sizeof(WCHAR));
362 memcpy(&v2[1],value,sz*sizeof(WCHAR));
366 *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 = msi_alloc((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,
431 /* formatted string located */
432 else if (!find_next_outermost_key(progress, len - (progress - ptr),
433 &key, &mark, &mark2, &nested))
437 TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
438 size/sizeof(WCHAR)));
439 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
440 TRACE("after chunk is %li + %li\n",size,chunk);
442 nd2 = msi_realloc(newdata,(size+chunk));
444 nd2 = msi_alloc(chunk);
447 memcpy(&newdata[size],progress,chunk);
452 if (mark != progress)
455 DWORD old_size = size;
456 INT cnt = (mark - progress);
457 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
458 size += cnt * sizeof(WCHAR);
460 tgt = msi_alloc(size);
462 tgt = msi_realloc(newdata,size);
464 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
471 TRACE("Nested key... %s\n",debugstr_w(key));
472 deformat_string_internal(package, key, &value, strlenW(key)+1,
479 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
480 size/sizeof(WCHAR)),debugstr_w(key));
484 /* only deformat number indexs */
485 if (key && is_key_number(key))
487 value = deformat_index(record,key,&chunk);
488 if (!chunk && failcount && *failcount >= 0)
497 DWORD keylen = strlenW(key);
498 chunk = (keylen + 2)*sizeof(WCHAR);
499 value = msi_alloc(chunk);
501 memcpy(&value[1],key,keylen*sizeof(WCHAR));
502 value[1+keylen] = ']';
509 if (key) switch (key[0])
512 value = deformat_NULL(&chunk);
515 value = deformat_component(package,&key[1],&chunk);
518 value = deformat_file(package,&key[1], &chunk, FALSE);
520 case '!': /* should be short path */
521 value = deformat_file(package,&key[1], &chunk, TRUE);
524 value = deformat_escape(&key[1],&chunk);
527 value = deformat_environment(package,&key[1],&chunk);
530 /* index keys cannot be nested */
531 if (is_key_number(key))
533 value = deformat_index(record,key,&chunk);
536 static const WCHAR fmt[] = {'[','%','s',']',0};
537 value = msi_alloc(10);
538 sprintfW(value,fmt,key);
539 chunk = strlenW(value)*sizeof(WCHAR);
542 value = deformat_property(package,key,&chunk);
552 TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
555 nd2= msi_realloc(newdata,(size + chunk));
557 nd2= msi_alloc(chunk);
559 memcpy(&newdata[size],value,chunk);
563 else if (failcount && *failcount >=0 )
569 TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
570 size/sizeof(WCHAR)));
572 *data = (LPWSTR)newdata;
573 return size / sizeof(WCHAR);
577 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
583 UINT rc = ERROR_INVALID_PARAMETER;
585 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
587 rec = msi_dup_record_field(record,0);
589 rec = build_default_format(record);
591 TRACE("(%s)\n",debugstr_w(rec));
593 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
600 memcpy(buffer,deformated,len*sizeof(WCHAR));
608 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
609 buffer[(*size)-1] = 0;
611 rc = ERROR_MORE_DATA;
620 msi_free(deformated);
624 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
630 UINT rc = ERROR_INVALID_PARAMETER;
632 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
634 rec = msi_dup_record_field(record,0);
636 rec = build_default_format(record);
638 TRACE("(%s)\n",debugstr_w(rec));
640 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
642 /* If len is zero then WideCharToMultiByte will return 0 indicating
643 * failure, but that will do just as well since we are ignoring
646 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
651 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
659 rc = ERROR_MORE_DATA;
661 buffer[(*size)-1] = 0;
670 msi_free(deformated);
675 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
676 LPWSTR szResult, DWORD *sz )
678 UINT r = ERROR_INVALID_HANDLE;
682 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
684 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
687 return ERROR_INVALID_HANDLE;
690 msiobj_release( &record->hdr );
692 return ERROR_INVALID_PARAMETER;
694 return ERROR_SUCCESS;
697 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
699 r = MSI_FormatRecordW( package, record, szResult, sz );
700 msiobj_release( &record->hdr );
702 msiobj_release( &package->hdr );
706 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
707 LPSTR szResult, DWORD *sz )
709 UINT r = ERROR_INVALID_HANDLE;
710 MSIPACKAGE *package = NULL;
711 MSIRECORD *record = NULL;
713 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
715 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
718 return ERROR_INVALID_HANDLE;
721 msiobj_release( &record->hdr );
723 return ERROR_INVALID_PARAMETER;
725 return ERROR_SUCCESS;
728 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
730 r = MSI_FormatRecordA( package, record, szResult, sz );
731 msiobj_release( &record->hdr );
733 msiobj_release( &package->hdr );