[MSI]
[reactos.git] / reactos / dll / win32 / msi / format.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Aric Stewart for CodeWeavers
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #define WIN32_NO_STATUS
23 #define _INC_WINDOWS
24 #define COM_NO_WINDOWS_H
25
26 #include <stdarg.h>
27 //#include <stdio.h>
28
29 #define COBJMACROS
30
31 #include <windef.h>
32 #include <winbase.h>
33 //#include "winerror.h"
34 #include <wine/debug.h>
35 //#include "msi.h"
36 //#include "winnls.h"
37 #include <objbase.h>
38 #include <oleauto.h>
39
40 #include "msipriv.h"
41 #include <msiserver.h>
42 #include <wine/unicode.h>
43
44 WINE_DEFAULT_DEBUG_CHANNEL(msi);
45
46 /* types arranged by precedence */
47 #define FORMAT_NULL 0x0001
48 #define FORMAT_LITERAL 0x0002
49 #define FORMAT_NUMBER 0x0004
50 #define FORMAT_LBRACK 0x0010
51 #define FORMAT_LBRACE 0x0020
52 #define FORMAT_RBRACK 0x0011
53 #define FORMAT_RBRACE 0x0021
54 #define FORMAT_ESCAPE 0x0040
55 #define FORMAT_PROPNULL 0x0080
56 #define FORMAT_ERROR 0x1000
57 #define FORMAT_FAIL 0x2000
58
59 #define left_type(x) (x & 0xF0)
60
61 typedef struct _tagFORMAT
62 {
63 MSIPACKAGE *package;
64 MSIRECORD *record;
65 LPWSTR deformatted;
66 int len;
67 int n;
68 BOOL propfailed;
69 BOOL groupfailed;
70 int groups;
71 } FORMAT;
72
73 typedef struct _tagFORMSTR
74 {
75 struct list entry;
76 int n;
77 int len;
78 int type;
79 BOOL propfound;
80 BOOL nonprop;
81 } FORMSTR;
82
83 typedef struct _tagSTACK
84 {
85 struct list items;
86 } STACK;
87
88 static STACK *create_stack(void)
89 {
90 STACK *stack = msi_alloc(sizeof(STACK));
91 list_init(&stack->items);
92 return stack;
93 }
94
95 static void free_stack(STACK *stack)
96 {
97 while (!list_empty(&stack->items))
98 {
99 FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
100 list_remove(&str->entry);
101 msi_free(str);
102 }
103
104 msi_free(stack);
105 }
106
107 static void stack_push(STACK *stack, FORMSTR *str)
108 {
109 list_add_head(&stack->items, &str->entry);
110 }
111
112 static FORMSTR *stack_pop(STACK *stack)
113 {
114 FORMSTR *ret;
115
116 if (list_empty(&stack->items))
117 return NULL;
118
119 ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
120 list_remove(&ret->entry);
121 return ret;
122 }
123
124 static FORMSTR *stack_find(STACK *stack, int type)
125 {
126 FORMSTR *str;
127
128 LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry)
129 {
130 if (str->type == type)
131 return str;
132 }
133
134 return NULL;
135 }
136
137 static FORMSTR *stack_peek(STACK *stack)
138 {
139 return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
140 }
141
142 static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str)
143 {
144 return &format->deformatted[str->n];
145 }
146
147 static WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len )
148 {
149 WCHAR *val;
150
151 if (!str->len) return NULL;
152 if ((val = msi_alloc( (str->len + 1) * sizeof(WCHAR) )))
153 {
154 memcpy( val, get_formstr_data(format, str), str->len * sizeof(WCHAR) );
155 val[str->len] = 0;
156 *ret_len = str->len;
157 }
158 return val;
159 }
160
161 static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len )
162 {
163 WCHAR *val, *ret;
164 DWORD len;
165 int field;
166
167 if (!(val = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
168 lstrcpynW(val, get_formstr_data(format, str), str->len + 1);
169 field = atoiW( val );
170 msi_free( val );
171
172 if (MSI_RecordIsNull( format->record, field ) ||
173 MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL;
174
175 len++;
176 if (!(ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
177 ret[0] = 0;
178 if (MSI_RecordGetStringW( format->record, field, ret, &len ))
179 {
180 msi_free( ret );
181 return NULL;
182 }
183 *ret_len = len;
184 return ret;
185 }
186
187 static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len )
188 {
189 WCHAR *prop, *ret;
190 DWORD len = 0;
191 UINT r;
192
193 if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
194 lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 );
195
196 r = msi_get_property( format->package->db, prop, NULL, &len );
197 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
198 {
199 msi_free( prop );
200 return NULL;
201 }
202 len++;
203 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
204 msi_get_property( format->package->db, prop, ret, &len );
205 msi_free( prop );
206 *ret_len = len;
207 return ret;
208 }
209
210 static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len )
211 {
212 WCHAR *key, *ret;
213 MSICOMPONENT *comp;
214
215 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
216 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
217
218 if (!(comp = msi_get_loaded_component( format->package, key )))
219 {
220 msi_free( key );
221 return NULL;
222 }
223 if (comp->Action == INSTALLSTATE_SOURCE)
224 ret = msi_resolve_source_folder( format->package, comp->Directory, NULL );
225 else
226 ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) );
227
228 if (ret) *ret_len = strlenW( ret );
229 else *ret_len = 0;
230 msi_free( key );
231 return ret;
232 }
233
234 static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len )
235 {
236 WCHAR *key, *ret = NULL;
237 const MSIFILE *file;
238 DWORD len = 0;
239
240 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
241 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
242
243 if (!(file = msi_get_loaded_file( format->package, key ))) goto done;
244 if (!shortname)
245 {
246 if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret );
247 goto done;
248 }
249 if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0)
250 {
251 if ((ret = strdupW( file->TargetPath ))) len = strlenW( ret );
252 goto done;
253 }
254 len++;
255 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
256 len = GetShortPathNameW( file->TargetPath, ret, len );
257
258 done:
259 msi_free( key );
260 *ret_len = len;
261 return ret;
262 }
263
264 static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len )
265 {
266 WCHAR *key, *ret = NULL;
267 DWORD len;
268
269 if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL;
270 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
271
272 if ((len = GetEnvironmentVariableW( key, NULL, 0 )))
273 {
274 len++;
275 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
276 *ret_len = GetEnvironmentVariableW( key, ret, len );
277 }
278 msi_free( key );
279 return ret;
280 }
281
282 static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound,
283 BOOL *nonprop, int *type, int *len )
284 {
285 LPCWSTR data = get_formstr_data(format, str);
286 WCHAR *replaced = NULL;
287 char ch = data[0];
288
289 if (ch == '\\')
290 {
291 str->n++;
292 if (str->len == 1)
293 {
294 str->len = 0;
295 replaced = NULL;
296 }
297 else
298 {
299 str->len = 1;
300 replaced = dup_formstr( format, str, len );
301 }
302 }
303 else if (ch == '~')
304 {
305 if (str->len != 1)
306 replaced = NULL;
307 else if ((replaced = msi_alloc( sizeof(WCHAR) )))
308 {
309 *replaced = 0;
310 *len = 0;
311 }
312 }
313 else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
314 {
315 str->n++;
316 str->len--;
317
318 switch (ch)
319 {
320 case '%':
321 replaced = deformat_environment( format, str, len ); break;
322 case '#':
323 replaced = deformat_file( format, str, FALSE, len ); break;
324 case '!':
325 replaced = deformat_file( format, str, TRUE, len ); break;
326 case '$':
327 replaced = deformat_component( format, str, len ); break;
328 }
329
330 *type = FORMAT_LITERAL;
331 }
332 else
333 {
334 replaced = deformat_property( format, str, len );
335 *type = FORMAT_LITERAL;
336
337 if (replaced)
338 *propfound = TRUE;
339 else
340 format->propfailed = TRUE;
341 }
342
343 return replaced;
344 }
345
346 static LPWSTR build_default_format(const MSIRECORD* record)
347 {
348 int i;
349 int count;
350 LPWSTR rc, buf;
351 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
352 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
353 static const WCHAR fmt_index[] = {'%','i',0};
354 LPCWSTR str;
355 WCHAR index[10];
356 DWORD size, max_len, len;
357
358 count = MSI_RecordGetFieldCount(record);
359
360 max_len = MAX_PATH;
361 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
362
363 rc = NULL;
364 size = 1;
365 for (i = 1; i <= count; i++)
366 {
367 sprintfW(index, fmt_index, i);
368 str = MSI_RecordGetString(record, i);
369 len = (str) ? lstrlenW(str) : 0;
370 len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index);
371 size += len;
372
373 if (len > max_len)
374 {
375 max_len = len;
376 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
377 if (!buf) return NULL;
378 }
379
380 if (str)
381 sprintfW(buf, fmt, i, str);
382 else
383 sprintfW(buf, fmt_null, i);
384
385 if (!rc)
386 {
387 rc = msi_alloc(size * sizeof(WCHAR));
388 lstrcpyW(rc, buf);
389 }
390 else
391 {
392 rc = msi_realloc(rc, size * sizeof(WCHAR));
393 lstrcatW(rc, buf);
394 }
395 }
396
397 msi_free(buf);
398 return rc;
399 }
400
401 static BOOL format_is_number(WCHAR x)
402 {
403 return ((x >= '0') && (x <= '9'));
404 }
405
406 static BOOL format_str_is_number(LPWSTR str)
407 {
408 LPWSTR ptr;
409
410 for (ptr = str; *ptr; ptr++)
411 if (!format_is_number(*ptr))
412 return FALSE;
413
414 return TRUE;
415 }
416
417 static BOOL format_is_alpha(WCHAR x)
418 {
419 return (!format_is_number(x) && x != '\0' &&
420 x != '[' && x != ']' && x != '{' && x != '}');
421 }
422
423 static BOOL format_is_literal(WCHAR x)
424 {
425 return (format_is_alpha(x) || format_is_number(x));
426 }
427
428 static int format_lex(FORMAT *format, FORMSTR **out)
429 {
430 int type, len = 1;
431 FORMSTR *str;
432 LPCWSTR data;
433 WCHAR ch;
434
435 *out = NULL;
436
437 if (!format->deformatted)
438 return FORMAT_NULL;
439
440 *out = msi_alloc_zero(sizeof(FORMSTR));
441 if (!*out)
442 return FORMAT_FAIL;
443
444 str = *out;
445 str->n = format->n;
446 str->len = 1;
447 data = get_formstr_data(format, str);
448
449 ch = data[0];
450 switch (ch)
451 {
452 case '{': type = FORMAT_LBRACE; break;
453 case '}': type = FORMAT_RBRACE; break;
454 case '[': type = FORMAT_LBRACK; break;
455 case ']': type = FORMAT_RBRACK; break;
456 case '~': type = FORMAT_PROPNULL; break;
457 case '\0': type = FORMAT_NULL; break;
458
459 default:
460 type = 0;
461 }
462
463 if (type)
464 {
465 str->type = type;
466 format->n++;
467 return type;
468 }
469
470 if (ch == '\\')
471 {
472 while (data[len] && data[len] != ']')
473 len++;
474
475 type = FORMAT_ESCAPE;
476 }
477 else if (format_is_alpha(ch))
478 {
479 while (format_is_literal(data[len]))
480 len++;
481
482 type = FORMAT_LITERAL;
483 }
484 else if (format_is_number(ch))
485 {
486 while (format_is_number(data[len]))
487 len++;
488
489 type = FORMAT_NUMBER;
490
491 if (data[len] != ']')
492 {
493 while (format_is_literal(data[len]))
494 len++;
495
496 type = FORMAT_LITERAL;
497 }
498 }
499 else
500 {
501 ERR("Got unknown character %c(%x)\n", ch, ch);
502 return FORMAT_ERROR;
503 }
504
505 format->n += len;
506 str->len = len;
507 str->type = type;
508
509 return type;
510 }
511
512 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop,
513 int oldsize, int type, WCHAR *replace, int len )
514 {
515 FORMSTR *ret;
516 LPWSTR str, ptr;
517 DWORD size = 0;
518 int n;
519
520 if (replace)
521 {
522 if (!len)
523 size = 1;
524 else
525 size = len;
526 }
527
528 size -= oldsize;
529 size = format->len + size + 1;
530
531 if (size <= 1)
532 {
533 msi_free(format->deformatted);
534 format->deformatted = NULL;
535 format->len = 0;
536 return NULL;
537 }
538
539 str = msi_alloc(size * sizeof(WCHAR));
540 if (!str)
541 return NULL;
542
543 str[0] = '\0';
544 memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
545 n = format->n;
546
547 if (replace)
548 {
549 if (!len) str[n++] = 0;
550 else
551 {
552 memcpy( str + n, replace, len * sizeof(WCHAR) );
553 n += len;
554 str[n] = 0;
555 }
556 }
557
558 ptr = &format->deformatted[format->n + oldsize];
559 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
560
561 msi_free(format->deformatted);
562 format->deformatted = str;
563 format->len = size - 1;
564
565 /* don't reformat the NULL */
566 if (replace && !len)
567 format->n++;
568
569 if (!replace)
570 return NULL;
571
572 ret = msi_alloc_zero(sizeof(FORMSTR));
573 if (!ret)
574 return NULL;
575
576 ret->len = len;
577 ret->type = type;
578 ret->n = format->n;
579 ret->propfound = propfound;
580 ret->nonprop = nonprop;
581
582 return ret;
583 }
584
585 static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
586 BOOL *propfound, BOOL *nonprop,
587 int *oldsize, int *type, int *len )
588 {
589 WCHAR *replaced;
590 FORMSTR *content, *node;
591 int n;
592
593 *nonprop = FALSE;
594 *propfound = FALSE;
595
596 node = stack_pop(values);
597 n = node->n;
598 *oldsize = node->len;
599 msi_free(node);
600
601 while ((node = stack_pop(values)))
602 {
603 *oldsize += node->len;
604
605 if (node->nonprop)
606 *nonprop = TRUE;
607
608 if (node->propfound)
609 *propfound = TRUE;
610
611 msi_free(node);
612 }
613
614 content = msi_alloc_zero(sizeof(FORMSTR));
615 content->n = n;
616 content->len = *oldsize;
617 content->type = FORMAT_LITERAL;
618
619 if (!format->groupfailed && (*oldsize == 2 ||
620 (format->propfailed && !*nonprop)))
621 {
622 msi_free(content);
623 return NULL;
624 }
625 else if (format->deformatted[content->n + 1] == '{' &&
626 format->deformatted[content->n + content->len - 2] == '}')
627 {
628 format->groupfailed = FALSE;
629 content->len = 0;
630 }
631 else if (*propfound && !*nonprop &&
632 !format->groupfailed && format->groups == 0)
633 {
634 content->n++;
635 content->len -= 2;
636 }
637 else
638 {
639 if (format->groups != 0)
640 format->groupfailed = TRUE;
641
642 *nonprop = TRUE;
643 }
644
645 replaced = dup_formstr( format, content, len );
646 *type = content->type;
647 msi_free(content);
648
649 if (format->groups == 0)
650 format->propfailed = FALSE;
651
652 return replaced;
653 }
654
655 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
656 BOOL *propfound, BOOL *nonprop,
657 int *oldsize, int *type, int *len )
658 {
659 WCHAR *replaced;
660 FORMSTR *content, *node;
661 int n;
662
663 *propfound = FALSE;
664 *nonprop = FALSE;
665
666 node = stack_pop(values);
667 n = node->n;
668 *oldsize = node->len;
669 *type = stack_peek(values)->type;
670 msi_free(node);
671
672 while ((node = stack_pop(values)))
673 {
674 *oldsize += node->len;
675
676 if (*type != FORMAT_ESCAPE &&
677 stack_peek(values) && node->type != *type)
678 *type = FORMAT_LITERAL;
679
680 msi_free(node);
681 }
682
683 content = msi_alloc_zero(sizeof(FORMSTR));
684 content->n = n + 1;
685 content->len = *oldsize - 2;
686 content->type = *type;
687
688 if (*type == FORMAT_NUMBER)
689 {
690 replaced = deformat_index( format, content, len );
691 if (replaced)
692 *propfound = TRUE;
693 else
694 format->propfailed = TRUE;
695
696 if (replaced)
697 *type = format_str_is_number(replaced) ?
698 FORMAT_NUMBER : FORMAT_LITERAL;
699 }
700 else if (format->package)
701 {
702 replaced = deformat_literal( format, content, propfound, nonprop, type, len );
703 }
704 else
705 {
706 *nonprop = TRUE;
707 content->n--;
708 content->len += 2;
709 replaced = dup_formstr( format, content, len );
710 }
711 msi_free(content);
712 return replaced;
713 }
714
715 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
716 {
717 WCHAR *replaced = NULL;
718 FORMSTR *beg, *top, *node;
719 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
720 int type, n, len = 0, oldsize = 0;
721
722 node = stack_peek(values);
723 type = node->type;
724 n = node->n;
725
726 if (type == FORMAT_LBRACK)
727 replaced = replace_stack_prop( format, values, &propfound,
728 &nonprop, &oldsize, &type, &len );
729 else if (type == FORMAT_LBRACE)
730 {
731 replaced = replace_stack_group( format, values, &propfound,
732 &nonprop, &oldsize, &type, &len );
733 group = TRUE;
734 }
735
736 format->n = n;
737 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
738 if (!beg)
739 return ERROR_SUCCESS;
740
741 msi_free(replaced);
742 format->n = beg->n + beg->len;
743
744 top = stack_peek(stack);
745 if (top)
746 {
747 type = top->type;
748
749 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
750 type == beg->type)
751 {
752 top->len += beg->len;
753
754 if (group)
755 top->nonprop = FALSE;
756
757 if (type == FORMAT_LITERAL)
758 top->nonprop = beg->nonprop;
759
760 if (beg->propfound)
761 top->propfound = TRUE;
762
763 msi_free(beg);
764 return ERROR_SUCCESS;
765 }
766 }
767
768 stack_push(stack, beg);
769 return ERROR_SUCCESS;
770 }
771
772 static BOOL verify_format(LPWSTR data)
773 {
774 int count = 0;
775
776 while (*data)
777 {
778 if (*data == '[' && *(data - 1) != '\\')
779 count++;
780 else if (*data == ']')
781 count--;
782
783 data++;
784 }
785
786 if (count > 0)
787 return FALSE;
788
789 return TRUE;
790 }
791
792 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
793 WCHAR** data, DWORD *len,
794 MSIRECORD* record, INT* failcount)
795 {
796 FORMAT format;
797 FORMSTR *str = NULL;
798 STACK *stack, *temp;
799 FORMSTR *node;
800 int type;
801
802 if (!ptr)
803 {
804 *data = NULL;
805 *len = 0;
806 return ERROR_SUCCESS;
807 }
808
809 *data = strdupW(ptr);
810 *len = lstrlenW(ptr);
811
812 ZeroMemory(&format, sizeof(FORMAT));
813 format.package = package;
814 format.record = record;
815 format.deformatted = *data;
816 format.len = *len;
817
818 if (!verify_format(*data))
819 return ERROR_SUCCESS;
820
821 stack = create_stack();
822 temp = create_stack();
823
824 while ((type = format_lex(&format, &str)) != FORMAT_NULL)
825 {
826 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
827 type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
828 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
829 {
830 if (type == FORMAT_LBRACE)
831 {
832 format.propfailed = FALSE;
833 format.groups++;
834 }
835 else if (type == FORMAT_ESCAPE &&
836 !stack_find(stack, FORMAT_LBRACK))
837 {
838 format.n -= str->len - 1;
839 str->len = 1;
840 }
841
842 stack_push(stack, str);
843 }
844 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
845 {
846 if (type == FORMAT_RBRACE)
847 format.groups--;
848
849 stack_push(stack, str);
850
851 if (stack_find(stack, left_type(type)))
852 {
853 do
854 {
855 node = stack_pop(stack);
856 stack_push(temp, node);
857 } while (node->type != left_type(type));
858
859 replace_stack(&format, stack, temp);
860 }
861 }
862 }
863
864 *data = format.deformatted;
865 *len = format.len;
866
867 msi_free(str);
868 free_stack(stack);
869 free_stack(temp);
870
871 return ERROR_SUCCESS;
872 }
873
874 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
875 LPDWORD size )
876 {
877 WCHAR *format, *deformated;
878 UINT rc = ERROR_INVALID_PARAMETER;
879 DWORD len;
880
881 TRACE("%p %p %p %p\n", package, record, buffer, size);
882
883 if (!(format = msi_dup_record_field( record, 0 )))
884 format = build_default_format( record );
885
886 TRACE("%s\n", debugstr_w(format));
887
888 deformat_string_internal( package, format, &deformated, &len, record, NULL );
889 if (buffer)
890 {
891 if (*size>len)
892 {
893 memcpy(buffer,deformated,len*sizeof(WCHAR));
894 rc = ERROR_SUCCESS;
895 buffer[len] = 0;
896 }
897 else
898 {
899 if (*size > 0)
900 {
901 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
902 buffer[(*size)-1] = 0;
903 }
904 rc = ERROR_MORE_DATA;
905 }
906 }
907 else rc = ERROR_SUCCESS;
908
909 *size = len;
910 msi_free( format );
911 msi_free( deformated );
912 return rc;
913 }
914
915 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
916 LPWSTR szResult, LPDWORD sz )
917 {
918 UINT r = ERROR_INVALID_HANDLE;
919 MSIPACKAGE *package;
920 MSIRECORD *record;
921
922 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
923
924 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
925 if (!package)
926 {
927 HRESULT hr;
928 IWineMsiRemotePackage *remote_package;
929 BSTR value = NULL;
930 awstring wstr;
931
932 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
933 if (remote_package)
934 {
935 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
936 &value );
937 if (FAILED(hr))
938 goto done;
939
940 wstr.unicode = TRUE;
941 wstr.str.w = szResult;
942 r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz );
943
944 done:
945 IWineMsiRemotePackage_Release( remote_package );
946 SysFreeString( value );
947
948 if (FAILED(hr))
949 {
950 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
951 return HRESULT_CODE(hr);
952
953 return ERROR_FUNCTION_FAILED;
954 }
955
956 return r;
957 }
958 }
959
960 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
961
962 if (!record)
963 return ERROR_INVALID_HANDLE;
964 if (!sz)
965 {
966 msiobj_release( &record->hdr );
967 if (szResult)
968 return ERROR_INVALID_PARAMETER;
969 else
970 return ERROR_SUCCESS;
971 }
972
973 r = MSI_FormatRecordW( package, record, szResult, sz );
974 msiobj_release( &record->hdr );
975 if (package)
976 msiobj_release( &package->hdr );
977 return r;
978 }
979
980 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
981 LPSTR szResult, LPDWORD sz )
982 {
983 UINT r;
984 DWORD len, save;
985 LPWSTR value;
986
987 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
988
989 if (!hRecord)
990 return ERROR_INVALID_HANDLE;
991
992 if (!sz)
993 {
994 if (szResult)
995 return ERROR_INVALID_PARAMETER;
996 else
997 return ERROR_SUCCESS;
998 }
999
1000 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
1001 if (r != ERROR_SUCCESS)
1002 return r;
1003
1004 value = msi_alloc(++len * sizeof(WCHAR));
1005 if (!value)
1006 return ERROR_OUTOFMEMORY;
1007
1008 r = MsiFormatRecordW( hInstall, hRecord, value, &len );
1009 if (r != ERROR_SUCCESS)
1010 goto done;
1011
1012 save = len + 1;
1013 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
1014 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1015
1016 if (szResult && len > *sz)
1017 {
1018 if (*sz) szResult[*sz - 1] = '\0';
1019 r = ERROR_MORE_DATA;
1020 }
1021
1022 *sz = save - 1;
1023
1024 done:
1025 msi_free(value);
1026 return r;
1027 }
1028
1029 /* wrapper to resist a need for a full rewrite right now */
1030 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data )
1031 {
1032 DWORD len;
1033 MSIRECORD *rec;
1034
1035 *data = NULL;
1036 if (!fmt) return 0;
1037 if (!(rec = MSI_CreateRecord( 1 ))) return 0;
1038
1039 MSI_RecordSetStringW( rec, 0, fmt );
1040 MSI_FormatRecordW( package, rec, NULL, &len );
1041 if (!(*data = msi_alloc( ++len * sizeof(WCHAR) )))
1042 {
1043 msiobj_release( &rec->hdr );
1044 return 0;
1045 }
1046 MSI_FormatRecordW( package, rec, *data, &len );
1047 msiobj_release( &rec->hdr );
1048 return len;
1049 }