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