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';