push f6c05471e518356cc315a2213a1fa6428675e384
[wine/hacks.git] / dlls / msi / format.c
blob4b5edc4ba20e2a1809b7b85afcfaf7e5dcf3dbcb
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"
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
44 WCHAR** data, DWORD len, MSIRECORD* record,
45 BOOL* in_group);
48 static LPWSTR build_default_format(const MSIRECORD* record)
50 int i;
51 int count;
52 LPWSTR rc, buf;
53 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
54 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
55 static const WCHAR fmt_index[] = {'%','i',0};
56 LPCWSTR str;
57 WCHAR index[10];
58 DWORD size, max_len, len;
60 count = MSI_RecordGetFieldCount(record);
62 max_len = MAX_PATH;
63 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
65 rc = NULL;
66 size = 1;
67 for (i = 1; i <= count; i++)
69 sprintfW(index,fmt_index,i);
70 str = MSI_RecordGetString(record, i);
71 len = (str) ? lstrlenW(str) : 0;
72 len += (sizeof(fmt_null) - 3) + lstrlenW(index);
73 size += len;
75 if (len > max_len)
77 max_len = len;
78 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
79 if (!buf) return NULL;
82 if (str)
83 sprintfW(buf,fmt,i,str);
84 else
85 sprintfW(buf,fmt_null,i);
87 if (!rc)
89 rc = msi_alloc(size * sizeof(WCHAR));
90 lstrcpyW(rc, buf);
92 else
94 rc = msi_realloc(rc, size * sizeof(WCHAR));
95 lstrcatW(rc, buf);
98 msi_free(buf);
99 return rc;
102 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
104 DWORD i;
105 for (i = 0; i < len; i++)
106 if (buf[i] == token)
107 return &buf[i];
108 return NULL;
111 /* break out helper functions for deformating */
112 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
114 LPWSTR value = NULL;
115 MSICOMPONENT *comp;
116 BOOL source;
118 *sz = 0;
119 if (!package)
120 return NULL;
122 comp = get_loaded_component(package,key);
123 if (comp)
125 source = (comp->Action == INSTALLSTATE_SOURCE) ? TRUE : FALSE;
126 value = resolve_folder(package, comp->Directory, source, FALSE, TRUE, 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 value = strdupW( file->TargetPath );
167 *sz = (lstrlenW(value)) * sizeof(WCHAR);
172 return value;
175 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
176 DWORD* chunk)
178 LPWSTR value = NULL;
179 DWORD sz;
181 sz = GetEnvironmentVariableW(key,NULL,0);
182 if (sz > 0)
184 sz++;
185 value = msi_alloc(sz * sizeof(WCHAR));
186 GetEnvironmentVariableW(key,value,sz);
187 *chunk = (strlenW(value)) * sizeof(WCHAR);
189 else
191 ERR("Unknown environment variable %s\n", debugstr_w(key));
192 *chunk = 0;
193 value = NULL;
195 return value;
199 static LPWSTR deformat_NULL(DWORD* chunk)
201 LPWSTR value;
203 value = msi_alloc(sizeof(WCHAR)*2);
204 value[0] = 0;
205 *chunk = sizeof(WCHAR);
206 return value;
209 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
211 LPWSTR value;
213 value = msi_alloc(sizeof(WCHAR)*2);
214 value[0] = key[0];
215 *chunk = sizeof(WCHAR);
217 return value;
221 static BOOL is_key_number(LPCWSTR key)
223 INT index = 0;
224 if (key[0] == 0)
225 return FALSE;
227 while (isdigitW(key[index])) index++;
228 if (key[index] == 0)
229 return TRUE;
230 else
231 return FALSE;
234 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
236 INT index;
237 LPWSTR value;
239 index = atoiW(key);
240 TRACE("record index %i\n",index);
241 value = msi_dup_record_field(record,index);
242 if (value)
243 *chunk = strlenW(value) * sizeof(WCHAR);
244 else
246 value = NULL;
247 *chunk = 0;
249 return value;
252 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
254 LPWSTR value;
256 if (!package)
257 return NULL;
259 value = msi_dup_property( package, key );
261 if (value)
262 *chunk = (strlenW(value)) * sizeof(WCHAR);
264 return value;
268 * Groups cannot be nested. They are just treated as from { to next }
270 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
271 LPWSTR *group, LPCWSTR *mark,
272 LPCWSTR* mark2)
274 int i;
275 BOOL found = FALSE;
277 *mark = scanW(source,'{',len_remaining);
278 if (!*mark)
279 return FALSE;
281 for (i = 1; (*mark - source) + i < len_remaining; i++)
283 if ((*mark)[i] == '}')
285 found = TRUE;
286 break;
289 if (! found)
290 return FALSE;
292 *mark2 = &(*mark)[i];
294 i = *mark2 - *mark;
295 *group = msi_alloc(i*sizeof(WCHAR));
297 i -= 1;
298 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
299 (*group)[i] = 0;
301 TRACE("Found group %s\n",debugstr_w(*group));
302 return TRUE;
306 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
307 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
308 BOOL *nested)
310 INT count = 0;
311 INT total_count = 0;
312 int i;
314 *mark = scanW(source,'[',len_remaining);
315 if (!*mark)
316 return FALSE;
318 count = 1;
319 total_count = 1;
320 *nested = FALSE;
321 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
323 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
325 count ++;
326 total_count ++;
327 *nested = TRUE;
329 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
331 count --;
335 if (count > 0)
336 return FALSE;
338 *mark2 = &(*mark)[i-1];
340 i = *mark2 - *mark;
341 *key = msi_alloc(i*sizeof(WCHAR));
342 /* do not have the [] in the key */
343 i -= 1;
344 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
345 (*key)[i] = 0;
347 TRACE("Found Key %s\n",debugstr_w(*key));
348 return TRUE;
351 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
352 MSIRECORD* record, DWORD* size)
354 LPWSTR value = NULL;
355 LPCWSTR mark, mark2;
356 LPWSTR key;
357 BOOL nested;
358 INT failcount;
359 static const WCHAR fmt[] = {'{','%','s','}',0};
360 UINT sz;
362 if (!group || group[0] == 0)
364 *size = 0;
365 return NULL;
367 /* if no [] then group is returned as is */
369 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
371 *size = (len+2)*sizeof(WCHAR);
372 value = msi_alloc(*size);
373 sprintfW(value,fmt,group);
374 /* do not return size of the null at the end */
375 *size = (len+1)*sizeof(WCHAR);
376 return value;
379 msi_free(key);
380 failcount = 0;
381 sz = deformat_string_internal(package, group, &value, strlenW(group),
382 record, &failcount);
383 if (failcount==0)
385 *size = sz * sizeof(WCHAR);
386 return value;
388 else if (failcount < 0)
390 LPWSTR v2;
392 v2 = msi_alloc((sz+2)*sizeof(WCHAR));
393 v2[0] = '{';
394 memcpy(&v2[1],value,sz*sizeof(WCHAR));
395 v2[sz+1]='}';
396 msi_free(value);
398 *size = (sz+2)*sizeof(WCHAR);
399 return v2;
401 else
403 msi_free(value);
404 *size = 0;
405 return NULL;
411 * len is in WCHARs
412 * return is also in WCHARs
414 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
415 WCHAR** data, DWORD len, MSIRECORD* record,
416 INT* failcount)
418 LPCWSTR mark = NULL;
419 LPCWSTR mark2 = NULL;
420 DWORD size=0;
421 DWORD chunk=0;
422 LPWSTR key;
423 LPWSTR value = NULL;
424 DWORD sz;
425 LPBYTE newdata = NULL;
426 const WCHAR* progress = NULL;
427 BOOL nested;
429 if (ptr==NULL)
431 TRACE("Deformatting NULL string\n");
432 *data = NULL;
433 return 0;
436 TRACE("Starting with %s\n",debugstr_wn(ptr,len));
438 /* scan for special characters... fast exit */
439 if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
440 (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
442 /* not formatted */
443 *data = msi_alloc((len*sizeof(WCHAR)));
444 memcpy(*data,ptr,len*sizeof(WCHAR));
445 TRACE("Returning %s\n",debugstr_wn(*data,len));
446 return len;
449 progress = ptr;
451 while (progress - ptr < len)
453 /* seek out first group if existing */
454 if (find_next_group(progress, len - (progress - ptr), &key,
455 &mark, &mark2))
457 value = deformat_group(package, key, strlenW(key)+1, record,
458 &chunk);
459 msi_free( key );
460 key = NULL;
461 nested = FALSE;
463 /* formatted string located */
464 else if (!find_next_outermost_key(progress, len - (progress - ptr),
465 &key, &mark, &mark2, &nested))
467 LPBYTE nd2;
469 TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
470 size/sizeof(WCHAR)));
471 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
472 TRACE("after chunk is %i + %i\n",size,chunk);
473 if (size)
474 nd2 = msi_realloc(newdata,(size+chunk));
475 else
476 nd2 = msi_alloc(chunk);
478 newdata = nd2;
479 memcpy(&newdata[size],progress,chunk);
480 size+=chunk;
481 break;
484 if (mark != progress)
486 LPBYTE tgt;
487 DWORD old_size = size;
488 INT cnt = (mark - progress);
489 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
490 size += cnt * sizeof(WCHAR);
491 if (!old_size)
492 tgt = msi_alloc(size);
493 else
494 tgt = msi_realloc(newdata,size);
495 newdata = tgt;
496 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
499 progress = mark;
501 if (nested)
503 TRACE("Nested key... %s\n",debugstr_w(key));
504 deformat_string_internal(package, key, &value, strlenW(key)+1,
505 record, failcount);
507 msi_free(key);
508 key = value;
511 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
512 size/sizeof(WCHAR)),debugstr_w(key));
514 if (!package)
516 /* only deformat number indexs */
517 if (key && is_key_number(key))
519 value = deformat_index(record,key,&chunk);
520 if (!chunk && failcount && *failcount >= 0)
521 (*failcount)++;
523 else
525 if (failcount)
526 *failcount = -1;
527 if(key)
529 DWORD keylen = strlenW(key);
530 chunk = (keylen + 2)*sizeof(WCHAR);
531 value = msi_alloc(chunk);
532 value[0] = '[';
533 memcpy(&value[1],key,keylen*sizeof(WCHAR));
534 value[1+keylen] = ']';
538 else
540 sz = 0;
541 if (key) switch (key[0])
543 case '~':
544 value = deformat_NULL(&chunk);
545 break;
546 case '$':
547 value = deformat_component(package,&key[1],&chunk);
548 break;
549 case '#':
550 value = deformat_file(package,&key[1], &chunk, FALSE);
551 break;
552 case '!': /* should be short path */
553 value = deformat_file(package,&key[1], &chunk, TRUE);
554 break;
555 case '\\':
556 value = deformat_escape(&key[1],&chunk);
557 break;
558 case '%':
559 value = deformat_environment(package,&key[1],&chunk);
560 break;
561 default:
562 /* index keys cannot be nested */
563 if (is_key_number(key))
564 if (!nested)
565 value = deformat_index(record,key,&chunk);
566 else
568 static const WCHAR fmt[] = {'[','%','s',']',0};
569 value = msi_alloc(10);
570 sprintfW(value,fmt,key);
571 chunk = strlenW(value)*sizeof(WCHAR);
573 else
574 value = deformat_property(package,key,&chunk);
575 break;
579 msi_free(key);
581 if (value!=NULL)
583 LPBYTE nd2;
584 TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
585 chunk, size);
586 if (size)
587 nd2= msi_realloc(newdata,(size + chunk));
588 else
589 nd2= msi_alloc(chunk);
590 newdata = nd2;
591 memcpy(&newdata[size],value,chunk);
592 size+=chunk;
593 msi_free(value);
595 else if (failcount && *failcount >=0 )
596 (*failcount)++;
598 progress = mark2+1;
601 TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
602 size/sizeof(WCHAR)));
604 *data = (LPWSTR)newdata;
605 return size / sizeof(WCHAR);
609 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
610 DWORD *size )
612 LPWSTR deformated;
613 LPWSTR rec;
614 DWORD len;
615 UINT rc = ERROR_INVALID_PARAMETER;
617 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
619 rec = msi_dup_record_field(record,0);
620 if (!rec)
621 rec = build_default_format(record);
623 TRACE("(%s)\n",debugstr_w(rec));
625 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
626 record, NULL);
628 if (buffer)
630 if (*size>len)
632 memcpy(buffer,deformated,len*sizeof(WCHAR));
633 rc = ERROR_SUCCESS;
634 buffer[len] = 0;
636 else
638 if (*size > 0)
640 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
641 buffer[(*size)-1] = 0;
643 rc = ERROR_MORE_DATA;
646 else
647 rc = ERROR_SUCCESS;
649 *size = len;
651 msi_free(rec);
652 msi_free(deformated);
653 return rc;
656 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
657 DWORD *size )
659 LPWSTR deformated;
660 LPWSTR rec;
661 DWORD len,lenA;
662 UINT rc = ERROR_INVALID_PARAMETER;
664 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
666 rec = msi_dup_record_field(record,0);
667 if (!rec)
668 rec = build_default_format(record);
670 TRACE("(%s)\n",debugstr_w(rec));
672 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
673 record, NULL);
674 /* If len is zero then WideCharToMultiByte will return 0 indicating
675 * failure, but that will do just as well since we are ignoring
676 * possible errors.
678 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
680 if (buffer)
682 /* Ditto above */
683 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
684 if (*size>lenA)
686 rc = ERROR_SUCCESS;
687 buffer[lenA] = 0;
689 else
691 rc = ERROR_MORE_DATA;
692 if (*size)
693 buffer[(*size)-1] = 0;
696 else
697 rc = ERROR_SUCCESS;
699 *size = lenA;
701 msi_free(rec);
702 msi_free(deformated);
703 return rc;
707 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
708 LPWSTR szResult, DWORD *sz )
710 UINT r = ERROR_INVALID_HANDLE;
711 MSIPACKAGE *package;
712 MSIRECORD *record;
714 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
716 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
718 if (!record)
719 return ERROR_INVALID_HANDLE;
720 if (!sz)
722 msiobj_release( &record->hdr );
723 if (szResult)
724 return ERROR_INVALID_PARAMETER;
725 else
726 return ERROR_SUCCESS;
729 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
731 r = MSI_FormatRecordW( package, record, szResult, sz );
732 msiobj_release( &record->hdr );
733 if (package)
734 msiobj_release( &package->hdr );
735 return r;
738 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
739 LPSTR szResult, DWORD *sz )
741 UINT r = ERROR_INVALID_HANDLE;
742 MSIPACKAGE *package = NULL;
743 MSIRECORD *record = NULL;
745 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
747 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
749 if (!record)
750 return ERROR_INVALID_HANDLE;
751 if (!sz)
753 msiobj_release( &record->hdr );
754 if (szResult)
755 return ERROR_INVALID_PARAMETER;
756 else
757 return ERROR_SUCCESS;
760 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
762 r = MSI_FormatRecordA( package, record, szResult, sz );
763 msiobj_release( &record->hdr );
764 if (package)
765 msiobj_release( &package->hdr );
766 return r;