- Merge from trunk
[reactos.git] / 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 BOOL source;
189
190 key = msi_alloc((str->len + 1) * sizeof(WCHAR));
191 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
192
193 comp = get_loaded_component(format->package, key);
194 if (!comp)
195 goto done;
196
197 source = (comp->Action == INSTALLSTATE_SOURCE) ? TRUE : FALSE;
198 ret = resolve_folder(format->package, comp->Directory, source, FALSE, TRUE, NULL);
199
200 done:
201 msi_free(key);
202 return ret;
203 }
204
205 static LPWSTR deformat_file(FORMAT *format, FORMSTR *str, BOOL shortname)
206 {
207 LPWSTR key, ret = NULL;
208 MSIFILE *file;
209 DWORD size;
210
211 key = msi_alloc((str->len + 1) * sizeof(WCHAR));
212 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
213
214 file = get_loaded_file(format->package, key);
215 if (!file)
216 goto done;
217
218 if (!shortname)
219 {
220 ret = strdupW(file->TargetPath);
221 goto done;
222 }
223
224 size = GetShortPathNameW(file->TargetPath, NULL, 0);
225 if (size <= 0)
226 {
227 ret = strdupW(file->TargetPath);
228 goto done;
229 }
230
231 size++;
232 ret = msi_alloc(size * sizeof(WCHAR));
233 GetShortPathNameW(file->TargetPath, ret, size);
234
235 done:
236 msi_free(key);
237 return ret;
238 }
239
240 static LPWSTR deformat_environment(FORMAT *format, FORMSTR *str)
241 {
242 LPWSTR key, ret = NULL;
243 DWORD sz;
244
245 key = msi_alloc((str->len + 1) * sizeof(WCHAR));
246 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
247
248 sz = GetEnvironmentVariableW(key, NULL ,0);
249 if (sz <= 0)
250 goto done;
251
252 sz++;
253 ret = msi_alloc(sz * sizeof(WCHAR));
254 GetEnvironmentVariableW(key, ret, sz);
255
256 done:
257 msi_free(key);
258 return ret;
259 }
260
261 static LPWSTR deformat_literal(FORMAT *format, FORMSTR *str, BOOL *propfound,
262 BOOL *nonprop, int *type)
263 {
264 LPCWSTR data = get_formstr_data(format, str);
265 LPWSTR replaced = NULL;
266 char ch = data[0];
267
268 if (ch == '\\')
269 {
270 str->n++;
271 if (str->len == 1)
272 {
273 str->len = 0;
274 replaced = NULL;
275 }
276 else
277 {
278 str->len = 1;
279 replaced = dup_formstr(format, str);
280 }
281 }
282 else if (ch == '~')
283 {
284 if (str->len != 1)
285 replaced = NULL;
286 else
287 {
288 replaced = msi_alloc(sizeof(WCHAR));
289 *replaced = '\0';
290 }
291 }
292 else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
293 {
294 str->n++;
295 str->len--;
296
297 switch (ch)
298 {
299 case '%':
300 replaced = deformat_environment(format, str); break;
301 case '#':
302 replaced = deformat_file(format, str, FALSE); break;
303 case '!':
304 replaced = deformat_file(format, str, TRUE); break;
305 case '$':
306 replaced = deformat_component(format, str); break;
307 }
308
309 *type = FORMAT_LITERAL;
310 }
311 else
312 {
313 replaced = deformat_property(format, str);
314 *type = FORMAT_LITERAL;
315
316 if (replaced)
317 *propfound = TRUE;
318 else
319 format->propfailed = TRUE;
320 }
321
322 return replaced;
323 }
324
325 static LPWSTR build_default_format(const MSIRECORD* record)
326 {
327 int i;
328 int count;
329 LPWSTR rc, buf;
330 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
331 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
332 static const WCHAR fmt_index[] = {'%','i',0};
333 LPCWSTR str;
334 WCHAR index[10];
335 DWORD size, max_len, len;
336
337 count = MSI_RecordGetFieldCount(record);
338
339 max_len = MAX_PATH;
340 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
341
342 rc = NULL;
343 size = 1;
344 for (i = 1; i <= count; i++)
345 {
346 sprintfW(index, fmt_index, i);
347 str = MSI_RecordGetString(record, i);
348 len = (str) ? lstrlenW(str) : 0;
349 len += (sizeof(fmt_null)/sizeof(fmt_null[0]) - 3) + lstrlenW(index);
350 size += len;
351
352 if (len > max_len)
353 {
354 max_len = len;
355 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
356 if (!buf) return NULL;
357 }
358
359 if (str)
360 sprintfW(buf, fmt, i, str);
361 else
362 sprintfW(buf, fmt_null, i);
363
364 if (!rc)
365 {
366 rc = msi_alloc(size * sizeof(WCHAR));
367 lstrcpyW(rc, buf);
368 }
369 else
370 {
371 rc = msi_realloc(rc, size * sizeof(WCHAR));
372 lstrcatW(rc, buf);
373 }
374 }
375
376 msi_free(buf);
377 return rc;
378 }
379
380 static BOOL format_is_number(WCHAR x)
381 {
382 return ((x >= '0') && (x <= '9'));
383 }
384
385 static BOOL format_str_is_number(LPWSTR str)
386 {
387 LPWSTR ptr;
388
389 for (ptr = str; *ptr; ptr++)
390 if (!format_is_number(*ptr))
391 return FALSE;
392
393 return TRUE;
394 }
395
396 static BOOL format_is_alpha(WCHAR x)
397 {
398 return (!format_is_number(x) && x != '\0' &&
399 x != '[' && x != ']' && x != '{' && x != '}');
400 }
401
402 static BOOL format_is_literal(WCHAR x)
403 {
404 return (format_is_alpha(x) || format_is_number(x));
405 }
406
407 static int format_lex(FORMAT *format, FORMSTR **out)
408 {
409 int type, len = 1;
410 FORMSTR *str;
411 LPCWSTR data;
412 WCHAR ch;
413
414 *out = NULL;
415
416 if (!format->deformatted)
417 return FORMAT_NULL;
418
419 *out = msi_alloc_zero(sizeof(FORMSTR));
420 if (!*out)
421 return FORMAT_FAIL;
422
423 str = *out;
424 str->n = format->n;
425 str->len = 1;
426 data = get_formstr_data(format, str);
427
428 ch = data[0];
429 switch (ch)
430 {
431 case '{': type = FORMAT_LBRACE; break;
432 case '}': type = FORMAT_RBRACE; break;
433 case '[': type = FORMAT_LBRACK; break;
434 case ']': type = FORMAT_RBRACK; break;
435 case '~': type = FORMAT_PROPNULL; break;
436 case '\0': type = FORMAT_NULL; break;
437
438 default:
439 type = 0;
440 }
441
442 if (type)
443 {
444 str->type = type;
445 format->n++;
446 return type;
447 }
448
449 if (ch == '\\')
450 {
451 while (data[len] && data[len] != ']')
452 len++;
453
454 type = FORMAT_ESCAPE;
455 }
456 else if (format_is_alpha(ch))
457 {
458 while (format_is_literal(data[len]))
459 len++;
460
461 type = FORMAT_LITERAL;
462 }
463 else if (format_is_number(ch))
464 {
465 while (format_is_number(data[len]))
466 len++;
467
468 type = FORMAT_NUMBER;
469
470 if (data[len] != ']')
471 {
472 while (format_is_literal(data[len]))
473 len++;
474
475 type = FORMAT_LITERAL;
476 }
477 }
478 else
479 {
480 ERR("Got unknown character %c(%x)\n", ch, ch);
481 return FORMAT_ERROR;
482 }
483
484 format->n += len;
485 str->len = len;
486 str->type = type;
487
488 return type;
489 }
490
491 static FORMSTR *format_replace(FORMAT *format, BOOL propfound, BOOL nonprop,
492 int oldsize, int type, LPWSTR replace)
493 {
494 FORMSTR *ret;
495 LPWSTR str, ptr;
496 DWORD size = 0;
497 int n;
498
499 if (replace)
500 {
501 if (!*replace)
502 size = 1;
503 else
504 size = lstrlenW(replace);
505 }
506
507 size -= oldsize;
508 size = format->len + size + 1;
509
510 if (size <= 1)
511 {
512 msi_free(format->deformatted);
513 format->deformatted = NULL;
514 format->len = 0;
515 return NULL;
516 }
517
518 str = msi_alloc(size * sizeof(WCHAR));
519 if (!str)
520 return NULL;
521
522 str[0] = '\0';
523 memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
524 n = format->n;
525
526 if (replace)
527 {
528 if (!*replace)
529 {
530 str[n] = '\0';
531 n++;
532 }
533 else
534 {
535 lstrcpyW(&str[n], replace);
536 n += lstrlenW(replace);
537 }
538 }
539
540 ptr = &format->deformatted[format->n + oldsize];
541 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
542
543 msi_free(format->deformatted);
544 format->deformatted = str;
545 format->len = size - 1;
546
547 /* don't reformat the NULL */
548 if (replace && !*replace)
549 format->n++;
550
551 if (!replace)
552 return NULL;
553
554 ret = msi_alloc_zero(sizeof(FORMSTR));
555 if (!ret)
556 return NULL;
557
558 ret->len = lstrlenW(replace);
559 ret->type = type;
560 ret->n = format->n;
561 ret->propfound = propfound;
562 ret->nonprop = nonprop;
563
564 return ret;
565 }
566
567 static LPWSTR replace_stack_group(FORMAT *format, STACK *values,
568 BOOL *propfound, BOOL *nonprop,
569 int *oldsize, int *type)
570 {
571 LPWSTR replaced = NULL;
572 FORMSTR *content;
573 FORMSTR *node;
574 int n;
575
576 *nonprop = FALSE;
577 *propfound = FALSE;
578
579 node = stack_pop(values);
580 n = node->n;
581 *oldsize = node->len;
582 msi_free(node);
583
584 while ((node = stack_pop(values)))
585 {
586 *oldsize += node->len;
587
588 if (node->nonprop)
589 *nonprop = TRUE;
590
591 if (node->propfound)
592 *propfound = TRUE;
593
594 msi_free(node);
595 }
596
597 content = msi_alloc_zero(sizeof(FORMSTR));
598 content->n = n;
599 content->len = *oldsize;
600 content->type = FORMAT_LITERAL;
601
602 if (!format->groupfailed && (*oldsize == 2 ||
603 (format->propfailed && !*nonprop)))
604 {
605 msi_free(content);
606 return NULL;
607 }
608 else if (format->deformatted[content->n + 1] == '{' &&
609 format->deformatted[content->n + content->len - 2] == '}')
610 {
611 format->groupfailed = FALSE;
612 content->len = 0;
613 }
614 else if (*propfound && !*nonprop &&
615 !format->groupfailed && format->groups == 0)
616 {
617 content->n++;
618 content->len -= 2;
619 }
620 else
621 {
622 if (format->groups != 0)
623 format->groupfailed = TRUE;
624
625 *nonprop = TRUE;
626 }
627
628 replaced = dup_formstr(format, content);
629 *type = content->type;
630 msi_free(content);
631
632 if (format->groups == 0)
633 format->propfailed = FALSE;
634
635 return replaced;
636 }
637
638 static LPWSTR replace_stack_prop(FORMAT *format, STACK *values,
639 BOOL *propfound, BOOL *nonprop,
640 int *oldsize, int *type)
641 {
642 LPWSTR replaced = NULL;
643 FORMSTR *content;
644 FORMSTR *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);
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);
687 }
688 else
689 {
690 *nonprop = TRUE;
691 content->n--;
692 content->len += 2;
693 replaced = dup_formstr(format, content);
694 }
695
696 msi_free(content);
697 return replaced;
698 }
699
700 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
701 {
702 LPWSTR replaced = NULL;
703 FORMSTR *beg;
704 FORMSTR *top;
705 FORMSTR *node;
706 BOOL propfound = FALSE;
707 BOOL nonprop = FALSE;
708 BOOL group = FALSE;
709 int oldsize = 0;
710 int type, n;
711
712 node = stack_peek(values);
713 type = node->type;
714 n = node->n;
715
716 if (type == FORMAT_LBRACK)
717 replaced = replace_stack_prop(format, values, &propfound,
718 &nonprop, &oldsize, &type);
719 else if (type == FORMAT_LBRACE)
720 {
721 replaced = replace_stack_group(format, values, &propfound,
722 &nonprop, &oldsize, &type);
723 group = TRUE;
724 }
725
726 format->n = n;
727 beg = format_replace(format, propfound, nonprop, oldsize, type, replaced);
728 if (!beg)
729 return ERROR_SUCCESS;
730
731 msi_free(replaced);
732 format->n = beg->n + beg->len;
733
734 top = stack_peek(stack);
735 if (top)
736 {
737 type = top->type;
738
739 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
740 type == beg->type)
741 {
742 top->len += beg->len;
743
744 if (group)
745 top->nonprop = FALSE;
746
747 if (type == FORMAT_LITERAL)
748 top->nonprop = beg->nonprop;
749
750 if (beg->propfound)
751 top->propfound = TRUE;
752
753 msi_free(beg);
754 return ERROR_SUCCESS;
755 }
756 }
757
758 stack_push(stack, beg);
759 return ERROR_SUCCESS;
760 }
761
762 static BOOL verify_format(LPWSTR data)
763 {
764 int count = 0;
765
766 while (*data)
767 {
768 if (*data == '[' && *(data - 1) != '\\')
769 count++;
770 else if (*data == ']')
771 count--;
772
773 data++;
774 }
775
776 if (count > 0)
777 return FALSE;
778
779 return TRUE;
780 }
781
782 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
783 WCHAR** data, DWORD *len,
784 MSIRECORD* record, INT* failcount)
785 {
786 FORMAT format;
787 FORMSTR *str = NULL;
788 STACK *stack, *temp;
789 FORMSTR *node;
790 int type;
791
792 if (!ptr)
793 {
794 *data = NULL;
795 *len = 0;
796 return ERROR_SUCCESS;
797 }
798
799 *data = strdupW(ptr);
800 *len = lstrlenW(ptr);
801
802 ZeroMemory(&format, sizeof(FORMAT));
803 format.package = package;
804 format.record = record;
805 format.deformatted = *data;
806 format.len = *len;
807
808 if (!verify_format(*data))
809 return ERROR_SUCCESS;
810
811 stack = create_stack();
812 temp = create_stack();
813
814 while ((type = format_lex(&format, &str)) != FORMAT_NULL)
815 {
816 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
817 type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
818 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
819 {
820 if (type == FORMAT_LBRACE)
821 {
822 format.propfailed = FALSE;
823 format.groups++;
824 }
825 else if (type == FORMAT_ESCAPE &&
826 !stack_find(stack, FORMAT_LBRACK))
827 {
828 format.n -= str->len - 1;
829 str->len = 1;
830 }
831
832 stack_push(stack, str);
833 }
834 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
835 {
836 if (type == FORMAT_RBRACE)
837 format.groups--;
838
839 stack_push(stack, str);
840
841 if (stack_find(stack, left_type(type)))
842 {
843 do
844 {
845 node = stack_pop(stack);
846 stack_push(temp, node);
847 } while (node->type != left_type(type));
848
849 replace_stack(&format, stack, temp);
850 }
851 }
852 }
853
854 *data = format.deformatted;
855 *len = format.len;
856
857 msi_free(str);
858 free_stack(stack);
859 free_stack(temp);
860
861 return ERROR_SUCCESS;
862 }
863
864 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
865 LPDWORD size )
866 {
867 LPWSTR deformated;
868 LPWSTR rec;
869 DWORD len;
870 UINT rc = ERROR_INVALID_PARAMETER;
871
872 TRACE("%p %p %p %p\n", package, record, buffer, size);
873
874 rec = msi_dup_record_field(record,0);
875 if (!rec)
876 rec = build_default_format(record);
877
878 TRACE("(%s)\n",debugstr_w(rec));
879
880 deformat_string_internal(package, rec, &deformated, &len, record, NULL);
881 if (buffer)
882 {
883 if (*size>len)
884 {
885 memcpy(buffer,deformated,len*sizeof(WCHAR));
886 rc = ERROR_SUCCESS;
887 buffer[len] = 0;
888 }
889 else
890 {
891 if (*size > 0)
892 {
893 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
894 buffer[(*size)-1] = 0;
895 }
896 rc = ERROR_MORE_DATA;
897 }
898 }
899 else
900 rc = ERROR_SUCCESS;
901
902 *size = len;
903
904 msi_free(rec);
905 msi_free(deformated);
906 return rc;
907 }
908
909 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
910 LPWSTR szResult, LPDWORD sz )
911 {
912 UINT r = ERROR_INVALID_HANDLE;
913 MSIPACKAGE *package;
914 MSIRECORD *record;
915
916 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
917
918 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
919 if (!package)
920 {
921 HRESULT hr;
922 IWineMsiRemotePackage *remote_package;
923 BSTR value = NULL;
924 awstring wstr;
925
926 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
927 if (remote_package)
928 {
929 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
930 &value );
931 if (FAILED(hr))
932 goto done;
933
934 wstr.unicode = TRUE;
935 wstr.str.w = szResult;
936 r = msi_strcpy_to_awstring( value, &wstr, sz );
937
938 done:
939 IWineMsiRemotePackage_Release( remote_package );
940 SysFreeString( value );
941
942 if (FAILED(hr))
943 {
944 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
945 return HRESULT_CODE(hr);
946
947 return ERROR_FUNCTION_FAILED;
948 }
949
950 return r;
951 }
952 }
953
954 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
955
956 if (!record)
957 return ERROR_INVALID_HANDLE;
958 if (!sz)
959 {
960 msiobj_release( &record->hdr );
961 if (szResult)
962 return ERROR_INVALID_PARAMETER;
963 else
964 return ERROR_SUCCESS;
965 }
966
967 r = MSI_FormatRecordW( package, record, szResult, sz );
968 msiobj_release( &record->hdr );
969 if (package)
970 msiobj_release( &package->hdr );
971 return r;
972 }
973
974 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
975 LPSTR szResult, LPDWORD sz )
976 {
977 UINT r;
978 DWORD len, save;
979 LPWSTR value;
980
981 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
982
983 if (!hRecord)
984 return ERROR_INVALID_HANDLE;
985
986 if (!sz)
987 {
988 if (szResult)
989 return ERROR_INVALID_PARAMETER;
990 else
991 return ERROR_SUCCESS;
992 }
993
994 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
995 if (r != ERROR_SUCCESS)
996 return r;
997
998 value = msi_alloc(++len * sizeof(WCHAR));
999 if (!value)
1000 return ERROR_OUTOFMEMORY;
1001
1002 r = MsiFormatRecordW( hInstall, hRecord, value, &len );
1003 if (r != ERROR_SUCCESS)
1004 goto done;
1005
1006 save = len + 1;
1007 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
1008 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1009
1010 if (szResult && len > *sz)
1011 {
1012 if (*sz) szResult[*sz - 1] = '\0';
1013 r = ERROR_MORE_DATA;
1014 }
1015
1016 *sz = save - 1;
1017
1018 done:
1019 msi_free(value);
1020 return r;
1021 }