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