xref: /reactos/dll/win32/msi/format.c (revision b819608e)
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 }
1034