wined3d: D3DDECLTYPE: Consistently use in WINED3D namespace.
[wine.git] / dlls / msi / format.c
blob4cb802e21359a1a1d949b6c344ad5d9f9271ad82
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(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;
117 *sz = 0;
118 if (!package)
119 return NULL;
121 FIXME("component key %s\n", debugstr_w(key));
122 comp = get_loaded_component(package,key);
123 if (comp)
125 value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
126 *sz = (strlenW(value)) * sizeof(WCHAR);
129 return value;
132 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
133 BOOL shortname)
135 LPWSTR value = NULL;
136 MSIFILE *file;
138 *sz = 0;
140 if (!package)
141 return NULL;
143 file = get_loaded_file( package, key );
144 if (file)
146 if (!shortname)
148 value = strdupW( file->TargetPath );
149 *sz = (strlenW(value)) * sizeof(WCHAR);
151 else
153 DWORD size = 0;
154 size = GetShortPathNameW( file->TargetPath, NULL, 0 );
156 if (size > 0)
158 *sz = (size-1) * sizeof (WCHAR);
159 size ++;
160 value = msi_alloc(size * sizeof(WCHAR));
161 GetShortPathNameW( file->TargetPath, value, size );
163 else
165 ERR("Unable to get ShortPath size (%s)\n",
166 debugstr_w( file->TargetPath) );
167 value = strdupW( file->TargetPath );
168 *sz = (lstrlenW(value)) * sizeof(WCHAR);
173 return value;
176 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
177 DWORD* chunk)
179 LPWSTR value = NULL;
180 DWORD sz;
182 sz = GetEnvironmentVariableW(key,NULL,0);
183 if (sz > 0)
185 sz++;
186 value = msi_alloc(sz * sizeof(WCHAR));
187 GetEnvironmentVariableW(key,value,sz);
188 *chunk = (strlenW(value)) * sizeof(WCHAR);
190 else
192 ERR("Unknown environment variable %s\n", debugstr_w(key));
193 *chunk = 0;
194 value = NULL;
196 return value;
200 static LPWSTR deformat_NULL(DWORD* chunk)
202 LPWSTR value;
204 value = msi_alloc(sizeof(WCHAR)*2);
205 value[0] = 0;
206 *chunk = sizeof(WCHAR);
207 return value;
210 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
212 LPWSTR value;
214 value = msi_alloc(sizeof(WCHAR)*2);
215 value[0] = key[0];
216 *chunk = sizeof(WCHAR);
218 return value;
222 static BOOL is_key_number(LPCWSTR key)
224 INT index = 0;
225 if (key[0] == 0)
226 return FALSE;
228 while (isdigitW(key[index])) index++;
229 if (key[index] == 0)
230 return TRUE;
231 else
232 return FALSE;
235 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
237 INT index;
238 LPWSTR value;
240 index = atoiW(key);
241 TRACE("record index %i\n",index);
242 value = msi_dup_record_field(record,index);
243 if (value)
244 *chunk = strlenW(value) * sizeof(WCHAR);
245 else
247 value = NULL;
248 *chunk = 0;
250 return value;
253 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
255 LPWSTR value;
257 if (!package)
258 return NULL;
260 value = msi_dup_property( package, key );
262 if (value)
263 *chunk = (strlenW(value)) * sizeof(WCHAR);
265 return value;
269 * Groups cannot be nested. They are just treated as from { to next }
271 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
272 LPWSTR *group, LPCWSTR *mark,
273 LPCWSTR* mark2)
275 int i;
276 BOOL found = FALSE;
278 *mark = scanW(source,'{',len_remaining);
279 if (!*mark)
280 return FALSE;
282 for (i = 1; (*mark - source) + i < len_remaining; i++)
284 if ((*mark)[i] == '}')
286 found = TRUE;
287 break;
290 if (! found)
291 return FALSE;
293 *mark2 = &(*mark)[i];
295 i = *mark2 - *mark;
296 *group = msi_alloc(i*sizeof(WCHAR));
298 i -= 1;
299 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
300 (*group)[i] = 0;
302 TRACE("Found group %s\n",debugstr_w(*group));
303 return TRUE;
307 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
308 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
309 BOOL *nested)
311 INT count = 0;
312 INT total_count = 0;
313 int i;
315 *mark = scanW(source,'[',len_remaining);
316 if (!*mark)
317 return FALSE;
319 count = 1;
320 total_count = 1;
321 *nested = FALSE;
322 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
324 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
326 count ++;
327 total_count ++;
328 *nested = TRUE;
330 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
332 count --;
336 if (count > 0)
337 return FALSE;
339 *mark2 = &(*mark)[i-1];
341 i = *mark2 - *mark;
342 *key = msi_alloc(i*sizeof(WCHAR));
343 /* do not have the [] in the key */
344 i -= 1;
345 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
346 (*key)[i] = 0;
348 TRACE("Found Key %s\n",debugstr_w(*key));
349 return TRUE;
352 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
353 MSIRECORD* record, DWORD* size)
355 LPWSTR value = NULL;
356 LPCWSTR mark, mark2;
357 LPWSTR key;
358 BOOL nested;
359 INT failcount;
360 static const WCHAR fmt[] = {'{','%','s','}',0};
361 UINT sz;
363 if (!group || group[0] == 0)
365 *size = 0;
366 return NULL;
368 /* if no [] then group is returned as is */
370 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
372 *size = (len+2)*sizeof(WCHAR);
373 value = msi_alloc(*size);
374 sprintfW(value,fmt,group);
375 /* do not return size of the null at the end */
376 *size = (len+1)*sizeof(WCHAR);
377 return value;
380 msi_free(key);
381 failcount = 0;
382 sz = deformat_string_internal(package, group, &value, strlenW(group),
383 record, &failcount);
384 if (failcount==0)
386 *size = sz * sizeof(WCHAR);
387 return value;
389 else if (failcount < 0)
391 LPWSTR v2;
393 v2 = msi_alloc((sz+2)*sizeof(WCHAR));
394 v2[0] = '{';
395 memcpy(&v2[1],value,sz*sizeof(WCHAR));
396 v2[sz+1]='}';
397 msi_free(value);
399 *size = (sz+2)*sizeof(WCHAR);
400 return v2;
402 else
404 msi_free(value);
405 *size = 0;
406 return NULL;
412 * len is in WCHARs
413 * return is also in WCHARs
415 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
416 WCHAR** data, DWORD len, MSIRECORD* record,
417 INT* failcount)
419 LPCWSTR mark = NULL;
420 LPCWSTR mark2 = NULL;
421 DWORD size=0;
422 DWORD chunk=0;
423 LPWSTR key;
424 LPWSTR value = NULL;
425 DWORD sz;
426 LPBYTE newdata = NULL;
427 const WCHAR* progress = NULL;
428 BOOL nested;
430 if (ptr==NULL)
432 TRACE("Deformatting NULL string\n");
433 *data = NULL;
434 return 0;
437 TRACE("Starting with %s\n",debugstr_wn(ptr,len));
439 /* scan for special characters... fast exit */
440 if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
441 (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
443 /* not formatted */
444 *data = msi_alloc((len*sizeof(WCHAR)));
445 memcpy(*data,ptr,len*sizeof(WCHAR));
446 TRACE("Returning %s\n",debugstr_wn(*data,len));
447 return len;
450 progress = ptr;
452 while (progress - ptr < len)
454 /* seek out first group if existing */
455 if (find_next_group(progress, len - (progress - ptr), &key,
456 &mark, &mark2))
458 value = deformat_group(package, key, strlenW(key)+1, record,
459 &chunk);
460 msi_free( key );
461 key = NULL;
462 nested = FALSE;
464 /* formatted string located */
465 else if (!find_next_outermost_key(progress, len - (progress - ptr),
466 &key, &mark, &mark2, &nested))
468 LPBYTE nd2;
470 TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
471 size/sizeof(WCHAR)));
472 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
473 TRACE("after chunk is %i + %i\n",size,chunk);
474 if (size)
475 nd2 = msi_realloc(newdata,(size+chunk));
476 else
477 nd2 = msi_alloc(chunk);
479 newdata = nd2;
480 memcpy(&newdata[size],progress,chunk);
481 size+=chunk;
482 break;
485 if (mark != progress)
487 LPBYTE tgt;
488 DWORD old_size = size;
489 INT cnt = (mark - progress);
490 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
491 size += cnt * sizeof(WCHAR);
492 if (!old_size)
493 tgt = msi_alloc(size);
494 else
495 tgt = msi_realloc(newdata,size);
496 newdata = tgt;
497 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
500 progress = mark;
502 if (nested)
504 TRACE("Nested key... %s\n",debugstr_w(key));
505 deformat_string_internal(package, key, &value, strlenW(key)+1,
506 record, failcount);
508 msi_free(key);
509 key = value;
512 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
513 size/sizeof(WCHAR)),debugstr_w(key));
515 if (!package)
517 /* only deformat number indexs */
518 if (key && is_key_number(key))
520 value = deformat_index(record,key,&chunk);
521 if (!chunk && failcount && *failcount >= 0)
522 (*failcount)++;
524 else
526 if (failcount)
527 *failcount = -1;
528 if(key)
530 DWORD keylen = strlenW(key);
531 chunk = (keylen + 2)*sizeof(WCHAR);
532 value = msi_alloc(chunk);
533 value[0] = '[';
534 memcpy(&value[1],key,keylen*sizeof(WCHAR));
535 value[1+keylen] = ']';
539 else
541 sz = 0;
542 if (key) switch (key[0])
544 case '~':
545 value = deformat_NULL(&chunk);
546 break;
547 case '$':
548 value = deformat_component(package,&key[1],&chunk);
549 break;
550 case '#':
551 value = deformat_file(package,&key[1], &chunk, FALSE);
552 break;
553 case '!': /* should be short path */
554 value = deformat_file(package,&key[1], &chunk, TRUE);
555 break;
556 case '\\':
557 value = deformat_escape(&key[1],&chunk);
558 break;
559 case '%':
560 value = deformat_environment(package,&key[1],&chunk);
561 break;
562 default:
563 /* index keys cannot be nested */
564 if (is_key_number(key))
565 if (!nested)
566 value = deformat_index(record,key,&chunk);
567 else
569 static const WCHAR fmt[] = {'[','%','s',']',0};
570 value = msi_alloc(10);
571 sprintfW(value,fmt,key);
572 chunk = strlenW(value)*sizeof(WCHAR);
574 else
575 value = deformat_property(package,key,&chunk);
576 break;
580 msi_free(key);
582 if (value!=NULL)
584 LPBYTE nd2;
585 TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
586 chunk, size);
587 if (size)
588 nd2= msi_realloc(newdata,(size + chunk));
589 else
590 nd2= msi_alloc(chunk);
591 newdata = nd2;
592 memcpy(&newdata[size],value,chunk);
593 size+=chunk;
594 msi_free(value);
596 else if (failcount && *failcount >=0 )
597 (*failcount)++;
599 progress = mark2+1;
602 TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
603 size/sizeof(WCHAR)));
605 *data = (LPWSTR)newdata;
606 return size / sizeof(WCHAR);
610 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
611 DWORD *size )
613 LPWSTR deformated;
614 LPWSTR rec;
615 DWORD len;
616 UINT rc = ERROR_INVALID_PARAMETER;
618 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
620 rec = msi_dup_record_field(record,0);
621 if (!rec)
622 rec = build_default_format(record);
624 TRACE("(%s)\n",debugstr_w(rec));
626 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
627 record, NULL);
629 if (buffer)
631 if (*size>len)
633 memcpy(buffer,deformated,len*sizeof(WCHAR));
634 rc = ERROR_SUCCESS;
635 buffer[len] = 0;
637 else
639 if (*size > 0)
641 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
642 buffer[(*size)-1] = 0;
644 rc = ERROR_MORE_DATA;
647 else
648 rc = ERROR_SUCCESS;
650 *size = len;
652 msi_free(rec);
653 msi_free(deformated);
654 return rc;
657 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
658 DWORD *size )
660 LPWSTR deformated;
661 LPWSTR rec;
662 DWORD len,lenA;
663 UINT rc = ERROR_INVALID_PARAMETER;
665 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
667 rec = msi_dup_record_field(record,0);
668 if (!rec)
669 rec = build_default_format(record);
671 TRACE("(%s)\n",debugstr_w(rec));
673 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
674 record, NULL);
675 /* If len is zero then WideCharToMultiByte will return 0 indicating
676 * failure, but that will do just as well since we are ignoring
677 * possible errors.
679 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
681 if (buffer)
683 /* Ditto above */
684 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
685 if (*size>lenA)
687 rc = ERROR_SUCCESS;
688 buffer[lenA] = 0;
690 else
692 rc = ERROR_MORE_DATA;
693 if (*size)
694 buffer[(*size)-1] = 0;
697 else
698 rc = ERROR_SUCCESS;
700 *size = lenA;
702 msi_free(rec);
703 msi_free(deformated);
704 return rc;
708 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
709 LPWSTR szResult, DWORD *sz )
711 UINT r = ERROR_INVALID_HANDLE;
712 MSIPACKAGE *package;
713 MSIRECORD *record;
715 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
717 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
719 if (!record)
720 return ERROR_INVALID_HANDLE;
721 if (!sz)
723 msiobj_release( &record->hdr );
724 if (szResult)
725 return ERROR_INVALID_PARAMETER;
726 else
727 return ERROR_SUCCESS;
730 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
732 r = MSI_FormatRecordW( package, record, szResult, sz );
733 msiobj_release( &record->hdr );
734 if (package)
735 msiobj_release( &package->hdr );
736 return r;
739 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
740 LPSTR szResult, DWORD *sz )
742 UINT r = ERROR_INVALID_HANDLE;
743 MSIPACKAGE *package = NULL;
744 MSIRECORD *record = NULL;
746 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
748 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
750 if (!record)
751 return ERROR_INVALID_HANDLE;
752 if (!sz)
754 msiobj_release( &record->hdr );
755 if (szResult)
756 return ERROR_INVALID_PARAMETER;
757 else
758 return ERROR_SUCCESS;
761 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
763 r = MSI_FormatRecordA( package, record, szResult, sz );
764 msiobj_release( &record->hdr );
765 if (package)
766 msiobj_release( &package->hdr );
767 return r;