msi: Don't reinstall components shared with other products.
[wine] / dlls / 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 = msi_get_loaded_component(format->package, key);
193     if (!comp)
194         goto done;
195
196     if (comp->Action == INSTALLSTATE_SOURCE)
197         ret = msi_resolve_source_folder( format->package, comp->Directory, NULL );
198     else
199         ret = strdupW( msi_get_target_folder( format->package, comp->Directory ) );
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 = msi_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 }
1023
1024 /* wrapper to resist a need for a full rewrite right now */
1025 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *ptr, WCHAR **data )
1026 {
1027     if (ptr)
1028     {
1029         DWORD size = 0;
1030         MSIRECORD *rec = MSI_CreateRecord( 1 );
1031
1032         MSI_RecordSetStringW( rec, 0, ptr );
1033         MSI_FormatRecordW( package, rec, NULL, &size );
1034
1035         size++;
1036         *data = msi_alloc( size * sizeof(WCHAR) );
1037         if (size > 1) MSI_FormatRecordW( package, rec, *data, &size );
1038         else *data[0] = 0;
1039
1040         msiobj_release( &rec->hdr );
1041         return size * sizeof(WCHAR);
1042     }
1043     *data = NULL;
1044     return 0;
1045 }