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