msi: Only ignore the StringPool and StringData in a transform.
[wine/testsucceed.git] / dlls / msi / format.c
blob7cbcacc77b3ea2125fb443a8832e38f617a4be4a
1 /*
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
26 #include <stdarg.h>
27 #include <stdio.h>
29 #define COBJMACROS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34 #include "wine/debug.h"
35 #include "msi.h"
36 #include "msipriv.h"
37 #include "winnls.h"
38 #include "wine/unicode.h"
39 #include "action.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
45 WCHAR** data, DWORD len, MSIRECORD* record,
46 BOOL* in_group);
49 static LPWSTR build_default_format(MSIRECORD* record)
51 int i;
52 int count;
53 LPWSTR rc, buf;
54 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
55 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
56 static const WCHAR fmt_index[] = {'%','i',0};
57 LPCWSTR str;
58 WCHAR index[10];
59 DWORD size, max_len, len;
61 count = MSI_RecordGetFieldCount(record);
63 max_len = MAX_PATH;
64 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
66 rc = NULL;
67 size = 1;
68 for (i = 1; i <= count; i++)
70 sprintfW(index,fmt_index,i);
71 str = MSI_RecordGetString(record, i);
72 len = (str) ? lstrlenW(str) : 0;
73 len += (sizeof(fmt_null) - 3) + lstrlenW(index);
74 size += len;
76 if (len > max_len)
78 max_len = len;
79 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
80 if (!buf) return NULL;
83 if (str)
84 sprintfW(buf,fmt,i,str);
85 else
86 sprintfW(buf,fmt_null,i);
88 if (!rc)
90 rc = msi_alloc(size * sizeof(WCHAR));
91 lstrcpyW(rc, buf);
93 else
95 rc = msi_realloc(rc, size * sizeof(WCHAR));
96 lstrcatW(rc, buf);
99 msi_free(buf);
100 return rc;
103 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
105 DWORD i;
106 for (i = 0; i < len; i++)
107 if (buf[i] == token)
108 return &buf[i];
109 return NULL;
112 /* break out helper functions for deformating */
113 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
115 LPWSTR value = NULL;
116 MSICOMPONENT *comp;
118 *sz = 0;
119 if (!package)
120 return NULL;
122 FIXME("component key %s\n", debugstr_w(key));
123 comp = get_loaded_component(package,key);
124 if (comp)
126 value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
127 *sz = (strlenW(value)) * sizeof(WCHAR);
130 return value;
133 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
134 BOOL shortname)
136 LPWSTR value = NULL;
137 MSIFILE *file;
139 *sz = 0;
141 if (!package)
142 return NULL;
144 file = get_loaded_file( package, key );
145 if (file)
147 if (!shortname)
149 value = strdupW( file->TargetPath );
150 *sz = (strlenW(value)) * sizeof(WCHAR);
152 else
154 DWORD size = 0;
155 size = GetShortPathNameW( file->TargetPath, NULL, 0 );
157 if (size > 0)
159 *sz = (size-1) * sizeof (WCHAR);
160 size ++;
161 value = msi_alloc(size * sizeof(WCHAR));
162 GetShortPathNameW( file->TargetPath, value, size );
164 else
166 ERR("Unable to get ShortPath size (%s)\n",
167 debugstr_w( file->TargetPath) );
168 value = strdupW( file->TargetPath );
169 *sz = (lstrlenW(value)) * sizeof(WCHAR);
174 return value;
177 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
178 DWORD* chunk)
180 LPWSTR value = NULL;
181 DWORD sz;
183 sz = GetEnvironmentVariableW(key,NULL,0);
184 if (sz > 0)
186 sz++;
187 value = msi_alloc(sz * sizeof(WCHAR));
188 GetEnvironmentVariableW(key,value,sz);
189 *chunk = (strlenW(value)) * sizeof(WCHAR);
191 else
193 ERR("Unknown environment variable %s\n", debugstr_w(key));
194 *chunk = 0;
195 value = NULL;
197 return value;
201 static LPWSTR deformat_NULL(DWORD* chunk)
203 LPWSTR value;
205 value = msi_alloc(sizeof(WCHAR)*2);
206 value[0] = 0;
207 *chunk = sizeof(WCHAR);
208 return value;
211 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
213 LPWSTR value;
215 value = msi_alloc(sizeof(WCHAR)*2);
216 value[0] = key[0];
217 *chunk = sizeof(WCHAR);
219 return value;
223 static BOOL is_key_number(LPCWSTR key)
225 INT index = 0;
226 if (key[0] == 0)
227 return FALSE;
229 while (isdigitW(key[index])) index++;
230 if (key[index] == 0)
231 return TRUE;
232 else
233 return FALSE;
236 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
238 INT index;
239 LPWSTR value;
241 index = atoiW(key);
242 TRACE("record index %i\n",index);
243 value = msi_dup_record_field(record,index);
244 if (value)
245 *chunk = strlenW(value) * sizeof(WCHAR);
246 else
248 value = NULL;
249 *chunk = 0;
251 return value;
254 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
256 LPWSTR value;
258 if (!package)
259 return NULL;
261 value = msi_dup_property( package, key );
263 if (value)
264 *chunk = (strlenW(value)) * sizeof(WCHAR);
266 return value;
270 * Groups cannot be nested. They are just treated as from { to next }
272 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
273 LPWSTR *group, LPCWSTR *mark,
274 LPCWSTR* mark2)
276 int i;
277 BOOL found = FALSE;
279 *mark = scanW(source,'{',len_remaining);
280 if (!*mark)
281 return FALSE;
283 for (i = 1; (*mark - source) + i < len_remaining; i++)
285 if ((*mark)[i] == '}')
287 found = TRUE;
288 break;
291 if (! found)
292 return FALSE;
294 *mark2 = &(*mark)[i];
296 i = *mark2 - *mark;
297 *group = msi_alloc(i*sizeof(WCHAR));
299 i -= 1;
300 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
301 (*group)[i] = 0;
303 TRACE("Found group %s\n",debugstr_w(*group));
304 return TRUE;
308 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
309 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
310 BOOL *nested)
312 INT count = 0;
313 INT total_count = 0;
314 int i;
316 *mark = scanW(source,'[',len_remaining);
317 if (!*mark)
318 return FALSE;
320 count = 1;
321 total_count = 1;
322 *nested = FALSE;
323 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
325 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
327 count ++;
328 total_count ++;
329 *nested = TRUE;
331 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
333 count --;
337 if (count > 0)
338 return FALSE;
340 *mark2 = &(*mark)[i-1];
342 i = *mark2 - *mark;
343 *key = msi_alloc(i*sizeof(WCHAR));
344 /* do not have the [] in the key */
345 i -= 1;
346 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
347 (*key)[i] = 0;
349 TRACE("Found Key %s\n",debugstr_w(*key));
350 return TRUE;
353 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
354 MSIRECORD* record, DWORD* size)
356 LPWSTR value = NULL;
357 LPCWSTR mark, mark2;
358 LPWSTR key;
359 BOOL nested;
360 INT failcount;
361 static const WCHAR fmt[] = {'{','%','s','}',0};
362 UINT sz;
364 if (!group || group[0] == 0)
366 *size = 0;
367 return NULL;
369 /* if no [] then group is returned as is */
371 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
373 *size = (len+2)*sizeof(WCHAR);
374 value = msi_alloc(*size);
375 sprintfW(value,fmt,group);
376 /* do not return size of the null at the end */
377 *size = (len+1)*sizeof(WCHAR);
378 return value;
381 msi_free(key);
382 failcount = 0;
383 sz = deformat_string_internal(package, group, &value, strlenW(group),
384 record, &failcount);
385 if (failcount==0)
387 *size = sz * sizeof(WCHAR);
388 return value;
390 else if (failcount < 0)
392 LPWSTR v2;
394 v2 = msi_alloc((sz+2)*sizeof(WCHAR));
395 v2[0] = '{';
396 memcpy(&v2[1],value,sz*sizeof(WCHAR));
397 v2[sz+1]='}';
398 msi_free(value);
400 *size = (sz+2)*sizeof(WCHAR);
401 return v2;
403 else
405 msi_free(value);
406 *size = 0;
407 return NULL;
413 * len is in WCHARs
414 * return is also in WCHARs
416 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
417 WCHAR** data, DWORD len, MSIRECORD* record,
418 INT* failcount)
420 LPCWSTR mark = NULL;
421 LPCWSTR mark2 = NULL;
422 DWORD size=0;
423 DWORD chunk=0;
424 LPWSTR key;
425 LPWSTR value = NULL;
426 DWORD sz;
427 LPBYTE newdata = NULL;
428 const WCHAR* progress = NULL;
429 BOOL nested;
431 if (ptr==NULL)
433 TRACE("Deformatting NULL string\n");
434 *data = NULL;
435 return 0;
438 TRACE("Starting with %s\n",debugstr_wn(ptr,len));
440 /* scan for special characters... fast exit */
441 if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
442 (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
444 /* not formatted */
445 *data = msi_alloc((len*sizeof(WCHAR)));
446 memcpy(*data,ptr,len*sizeof(WCHAR));
447 TRACE("Returning %s\n",debugstr_wn(*data,len));
448 return len;
451 progress = ptr;
453 while (progress - ptr < len)
455 /* seek out first group if existing */
456 if (find_next_group(progress, len - (progress - ptr), &key,
457 &mark, &mark2))
459 value = deformat_group(package, key, strlenW(key)+1, record,
460 &chunk);
461 msi_free( key );
462 key = NULL;
463 nested = FALSE;
465 /* formatted string located */
466 else if (!find_next_outermost_key(progress, len - (progress - ptr),
467 &key, &mark, &mark2, &nested))
469 LPBYTE nd2;
471 TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
472 size/sizeof(WCHAR)));
473 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
474 TRACE("after chunk is %i + %i\n",size,chunk);
475 if (size)
476 nd2 = msi_realloc(newdata,(size+chunk));
477 else
478 nd2 = msi_alloc(chunk);
480 newdata = nd2;
481 memcpy(&newdata[size],progress,chunk);
482 size+=chunk;
483 break;
486 if (mark != progress)
488 LPBYTE tgt;
489 DWORD old_size = size;
490 INT cnt = (mark - progress);
491 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
492 size += cnt * sizeof(WCHAR);
493 if (!old_size)
494 tgt = msi_alloc(size);
495 else
496 tgt = msi_realloc(newdata,size);
497 newdata = tgt;
498 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
501 progress = mark;
503 if (nested)
505 TRACE("Nested key... %s\n",debugstr_w(key));
506 deformat_string_internal(package, key, &value, strlenW(key)+1,
507 record, failcount);
509 msi_free(key);
510 key = value;
513 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
514 size/sizeof(WCHAR)),debugstr_w(key));
516 if (!package)
518 /* only deformat number indexs */
519 if (key && is_key_number(key))
521 value = deformat_index(record,key,&chunk);
522 if (!chunk && failcount && *failcount >= 0)
523 (*failcount)++;
525 else
527 if (failcount)
528 *failcount = -1;
529 if(key)
531 DWORD keylen = strlenW(key);
532 chunk = (keylen + 2)*sizeof(WCHAR);
533 value = msi_alloc(chunk);
534 value[0] = '[';
535 memcpy(&value[1],key,keylen*sizeof(WCHAR));
536 value[1+keylen] = ']';
540 else
542 sz = 0;
543 if (key) switch (key[0])
545 case '~':
546 value = deformat_NULL(&chunk);
547 break;
548 case '$':
549 value = deformat_component(package,&key[1],&chunk);
550 break;
551 case '#':
552 value = deformat_file(package,&key[1], &chunk, FALSE);
553 break;
554 case '!': /* should be short path */
555 value = deformat_file(package,&key[1], &chunk, TRUE);
556 break;
557 case '\\':
558 value = deformat_escape(&key[1],&chunk);
559 break;
560 case '%':
561 value = deformat_environment(package,&key[1],&chunk);
562 break;
563 default:
564 /* index keys cannot be nested */
565 if (is_key_number(key))
566 if (!nested)
567 value = deformat_index(record,key,&chunk);
568 else
570 static const WCHAR fmt[] = {'[','%','s',']',0};
571 value = msi_alloc(10);
572 sprintfW(value,fmt,key);
573 chunk = strlenW(value)*sizeof(WCHAR);
575 else
576 value = deformat_property(package,key,&chunk);
577 break;
581 msi_free(key);
583 if (value!=NULL)
585 LPBYTE nd2;
586 TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
587 chunk, size);
588 if (size)
589 nd2= msi_realloc(newdata,(size + chunk));
590 else
591 nd2= msi_alloc(chunk);
592 newdata = nd2;
593 memcpy(&newdata[size],value,chunk);
594 size+=chunk;
595 msi_free(value);
597 else if (failcount && *failcount >=0 )
598 (*failcount)++;
600 progress = mark2+1;
603 TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
604 size/sizeof(WCHAR)));
606 *data = (LPWSTR)newdata;
607 return size / sizeof(WCHAR);
611 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
612 DWORD *size )
614 LPWSTR deformated;
615 LPWSTR rec;
616 DWORD len;
617 UINT rc = ERROR_INVALID_PARAMETER;
619 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
621 rec = msi_dup_record_field(record,0);
622 if (!rec)
623 rec = build_default_format(record);
625 TRACE("(%s)\n",debugstr_w(rec));
627 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
628 record, NULL);
630 if (buffer)
632 if (*size>len)
634 memcpy(buffer,deformated,len*sizeof(WCHAR));
635 rc = ERROR_SUCCESS;
636 buffer[len] = 0;
638 else
640 if (*size > 0)
642 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
643 buffer[(*size)-1] = 0;
645 rc = ERROR_MORE_DATA;
648 else
649 rc = ERROR_SUCCESS;
651 *size = len;
653 msi_free(rec);
654 msi_free(deformated);
655 return rc;
658 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
659 DWORD *size )
661 LPWSTR deformated;
662 LPWSTR rec;
663 DWORD len,lenA;
664 UINT rc = ERROR_INVALID_PARAMETER;
666 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
668 rec = msi_dup_record_field(record,0);
669 if (!rec)
670 rec = build_default_format(record);
672 TRACE("(%s)\n",debugstr_w(rec));
674 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
675 record, NULL);
676 /* If len is zero then WideCharToMultiByte will return 0 indicating
677 * failure, but that will do just as well since we are ignoring
678 * possible errors.
680 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
682 if (buffer)
684 /* Ditto above */
685 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
686 if (*size>lenA)
688 rc = ERROR_SUCCESS;
689 buffer[lenA] = 0;
691 else
693 rc = ERROR_MORE_DATA;
694 if (*size)
695 buffer[(*size)-1] = 0;
698 else
699 rc = ERROR_SUCCESS;
701 *size = lenA;
703 msi_free(rec);
704 msi_free(deformated);
705 return rc;
709 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
710 LPWSTR szResult, DWORD *sz )
712 UINT r = ERROR_INVALID_HANDLE;
713 MSIPACKAGE *package;
714 MSIRECORD *record;
716 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
718 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
720 if (!record)
721 return ERROR_INVALID_HANDLE;
722 if (!sz)
724 msiobj_release( &record->hdr );
725 if (szResult)
726 return ERROR_INVALID_PARAMETER;
727 else
728 return ERROR_SUCCESS;
731 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
733 r = MSI_FormatRecordW( package, record, szResult, sz );
734 msiobj_release( &record->hdr );
735 if (package)
736 msiobj_release( &package->hdr );
737 return r;
740 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
741 LPSTR szResult, DWORD *sz )
743 UINT r = ERROR_INVALID_HANDLE;
744 MSIPACKAGE *package = NULL;
745 MSIRECORD *record = NULL;
747 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
749 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
751 if (!record)
752 return ERROR_INVALID_HANDLE;
753 if (!sz)
755 msiobj_release( &record->hdr );
756 if (szResult)
757 return ERROR_INVALID_PARAMETER;
758 else
759 return ERROR_SUCCESS;
762 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
764 r = MSI_FormatRecordA( package, record, szResult, sz );
765 msiobj_release( &record->hdr );
766 if (package)
767 msiobj_release( &package->hdr );
768 return r;