xref: /reactos/dll/win32/msi/format.c (revision 527f2f90)
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) 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, WCHAR *replace, int len )
494 {
495     FORMSTR *ret;
496     LPWSTR str, ptr;
497     DWORD size = 0;
498     int n;
499 
500     if (replace)
501     {
502         if (!len)
503             size = 1;
504         else
505             size = len;
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 (!len) str[n++] = 0;
530         else
531         {
532             memcpy( str + n, replace, len * sizeof(WCHAR) );
533             n += len;
534             str[n] = 0;
535         }
536     }
537 
538     ptr = &format->deformatted[format->n + oldsize];
539     memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
540 
541     msi_free(format->deformatted);
542     format->deformatted = str;
543     format->len = size - 1;
544 
545     /* don't reformat the NULL */
546     if (replace && !len)
547         format->n++;
548 
549     if (!replace)
550         return NULL;
551 
552     ret = msi_alloc_zero(sizeof(FORMSTR));
553     if (!ret)
554         return NULL;
555 
556     ret->len = len;
557     ret->type = type;
558     ret->n = format->n;
559     ret->propfound = propfound;
560     ret->nonprop = nonprop;
561 
562     return ret;
563 }
564 
565 static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
566                                    BOOL *propfound, BOOL *nonprop,
567                                    int *oldsize, int *type, int *len )
568 {
569     WCHAR *replaced;
570     FORMSTR *content, *node;
571     int n;
572 
573     *nonprop = FALSE;
574     *propfound = FALSE;
575 
576     node = stack_pop(values);
577     n = node->n;
578     *oldsize = node->len;
579     msi_free(node);
580 
581     while ((node = stack_pop(values)))
582     {
583         *oldsize += node->len;
584 
585         if (node->nonprop)
586             *nonprop = TRUE;
587 
588         if (node->propfound)
589             *propfound = TRUE;
590 
591         msi_free(node);
592     }
593 
594     content = msi_alloc_zero(sizeof(FORMSTR));
595     content->n = n;
596     content->len = *oldsize;
597     content->type = FORMAT_LITERAL;
598 
599     if (!format->groupfailed && (*oldsize == 2 ||
600         (format->propfailed && !*nonprop)))
601     {
602         msi_free(content);
603         return NULL;
604     }
605     else if (format->deformatted[content->n + 1] == '{' &&
606              format->deformatted[content->n + content->len - 2] == '}')
607     {
608         format->groupfailed = FALSE;
609         content->len = 0;
610     }
611     else if (*propfound && !*nonprop &&
612              !format->groupfailed && format->groups == 0)
613     {
614         content->n++;
615         content->len -= 2;
616     }
617     else
618     {
619         if (format->groups != 0)
620             format->groupfailed = TRUE;
621 
622         *nonprop = TRUE;
623     }
624 
625     replaced = dup_formstr( format, content, len );
626     *type = content->type;
627     msi_free(content);
628 
629     if (format->groups == 0)
630         format->propfailed = FALSE;
631 
632     return replaced;
633 }
634 
635 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
636                                   BOOL *propfound, BOOL *nonprop,
637                                   int *oldsize, int *type, int *len )
638 {
639     WCHAR *replaced;
640     FORMSTR *content, *node;
641     int n;
642 
643     *propfound = FALSE;
644     *nonprop = FALSE;
645 
646     node = stack_pop(values);
647     n = node->n;
648     *oldsize = node->len;
649     *type = stack_peek(values)->type;
650     msi_free(node);
651 
652     while ((node = stack_pop(values)))
653     {
654         *oldsize += node->len;
655 
656         if (*type != FORMAT_ESCAPE &&
657             stack_peek(values) && node->type != *type)
658             *type = FORMAT_LITERAL;
659 
660         msi_free(node);
661     }
662 
663     content = msi_alloc_zero(sizeof(FORMSTR));
664     content->n = n + 1;
665     content->len = *oldsize - 2;
666     content->type = *type;
667 
668     if (*type == FORMAT_NUMBER)
669     {
670         replaced = deformat_index( format, content, len );
671         if (replaced)
672             *propfound = TRUE;
673         else
674             format->propfailed = TRUE;
675 
676         if (replaced)
677             *type = format_str_is_number(replaced) ?
678                 FORMAT_NUMBER : FORMAT_LITERAL;
679     }
680     else if (format->package)
681     {
682         replaced = deformat_literal( format, content, propfound, nonprop, type, len );
683     }
684     else
685     {
686         *nonprop = TRUE;
687         content->n--;
688         content->len += 2;
689         replaced = dup_formstr( format, content, len );
690     }
691     msi_free(content);
692     return replaced;
693 }
694 
695 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
696 {
697     WCHAR *replaced = NULL;
698     FORMSTR *beg, *top, *node;
699     BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
700     int type, n, len = 0, oldsize = 0;
701 
702     node = stack_peek(values);
703     type = node->type;
704     n = node->n;
705 
706     if (type == FORMAT_LBRACK)
707         replaced = replace_stack_prop( format, values, &propfound,
708                                        &nonprop, &oldsize, &type, &len );
709     else if (type == FORMAT_LBRACE)
710     {
711         replaced = replace_stack_group( format, values, &propfound,
712                                         &nonprop, &oldsize, &type, &len );
713         group = TRUE;
714     }
715 
716     format->n = n;
717     beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
718     if (!beg)
719         return ERROR_SUCCESS;
720 
721     msi_free(replaced);
722     format->n = beg->n + beg->len;
723 
724     top = stack_peek(stack);
725     if (top)
726     {
727         type = top->type;
728 
729         if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
730             type == beg->type)
731         {
732             top->len += beg->len;
733 
734             if (group)
735                 top->nonprop = FALSE;
736 
737             if (type == FORMAT_LITERAL)
738                 top->nonprop = beg->nonprop;
739 
740             if (beg->propfound)
741                 top->propfound = TRUE;
742 
743             msi_free(beg);
744             return ERROR_SUCCESS;
745         }
746     }
747 
748     stack_push(stack, beg);
749     return ERROR_SUCCESS;
750 }
751 
752 static BOOL verify_format(LPWSTR data)
753 {
754     int count = 0;
755 
756     while (*data)
757     {
758         if (*data == '[' && *(data - 1) != '\\')
759             count++;
760         else if (*data == ']')
761             count--;
762 
763         data++;
764     }
765 
766     if (count > 0)
767         return FALSE;
768 
769     return TRUE;
770 }
771 
772 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
773                                       WCHAR** data, DWORD *len,
774                                       MSIRECORD* record, INT* failcount)
775 {
776     FORMAT format;
777     FORMSTR *str = NULL;
778     STACK *stack, *temp;
779     FORMSTR *node;
780     int type;
781 
782     if (!ptr)
783     {
784         *data = NULL;
785         *len = 0;
786         return ERROR_SUCCESS;
787     }
788 
789     *data = strdupW(ptr);
790     *len = lstrlenW(ptr);
791 
792     ZeroMemory(&format, sizeof(FORMAT));
793     format.package = package;
794     format.record = record;
795     format.deformatted = *data;
796     format.len = *len;
797 
798     if (!verify_format(*data))
799         return ERROR_SUCCESS;
800 
801     stack = create_stack();
802     temp = create_stack();
803 
804     while ((type = format_lex(&format, &str)) != FORMAT_NULL)
805     {
806         if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
807             type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
808             type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
809         {
810             if (type == FORMAT_LBRACE)
811             {
812                 format.propfailed = FALSE;
813                 format.groups++;
814             }
815             else if (type == FORMAT_ESCAPE &&
816                      !stack_find(stack, FORMAT_LBRACK))
817             {
818                 format.n -= str->len - 1;
819                 str->len = 1;
820             }
821 
822             stack_push(stack, str);
823         }
824         else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
825         {
826             if (type == FORMAT_RBRACE)
827                 format.groups--;
828 
829             stack_push(stack, str);
830 
831             if (stack_find(stack, left_type(type)))
832             {
833                 do
834                 {
835                     node = stack_pop(stack);
836                     stack_push(temp, node);
837                 } while (node->type != left_type(type));
838 
839                 replace_stack(&format, stack, temp);
840             }
841         }
842     }
843 
844     *data = format.deformatted;
845     *len = format.len;
846 
847     msi_free(str);
848     free_stack(stack);
849     free_stack(temp);
850 
851     return ERROR_SUCCESS;
852 }
853 
854 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
855                         LPDWORD size )
856 {
857     WCHAR *format, *deformated;
858     UINT rc = ERROR_INVALID_PARAMETER;
859     DWORD len;
860 
861     TRACE("%p %p %p %p\n", package, record, buffer, size);
862 
863     if (!(format = msi_dup_record_field( record, 0 )))
864         format = build_default_format( record );
865 
866     TRACE("%s\n", debugstr_w(format));
867 
868     deformat_string_internal( package, format, &deformated, &len, record, NULL );
869     if (buffer)
870     {
871         if (*size>len)
872         {
873             memcpy(buffer,deformated,len*sizeof(WCHAR));
874             rc = ERROR_SUCCESS;
875             buffer[len] = 0;
876         }
877         else
878         {
879             if (*size > 0)
880             {
881                 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
882                 buffer[(*size)-1] = 0;
883             }
884             rc = ERROR_MORE_DATA;
885         }
886     }
887     else rc = ERROR_SUCCESS;
888 
889     *size = len;
890     msi_free( format );
891     msi_free( deformated );
892     return rc;
893 }
894 
895 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
896                               LPWSTR szResult, LPDWORD sz )
897 {
898     UINT r = ERROR_INVALID_HANDLE;
899     MSIPACKAGE *package;
900     MSIRECORD *record;
901 
902     TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
903 
904     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
905     if (!package)
906     {
907         HRESULT hr;
908         IWineMsiRemotePackage *remote_package;
909         BSTR value = NULL;
910         awstring wstr;
911 
912         remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
913         if (remote_package)
914         {
915             hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
916                                                      &value );
917             if (FAILED(hr))
918                 goto done;
919 
920             wstr.unicode = TRUE;
921             wstr.str.w = szResult;
922             r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz );
923 
924 done:
925             IWineMsiRemotePackage_Release( remote_package );
926             SysFreeString( value );
927 
928             if (FAILED(hr))
929             {
930                 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
931                     return HRESULT_CODE(hr);
932 
933                 return ERROR_FUNCTION_FAILED;
934             }
935 
936             return r;
937         }
938     }
939 
940     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
941 
942     if (!record)
943         return ERROR_INVALID_HANDLE;
944     if (!sz)
945     {
946         msiobj_release( &record->hdr );
947         if (szResult)
948             return ERROR_INVALID_PARAMETER;
949         else
950             return ERROR_SUCCESS;
951     }
952 
953     r = MSI_FormatRecordW( package, record, szResult, sz );
954     msiobj_release( &record->hdr );
955     if (package)
956         msiobj_release( &package->hdr );
957     return r;
958 }
959 
960 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
961                               LPSTR szResult, LPDWORD sz )
962 {
963     UINT r;
964     DWORD len, save;
965     LPWSTR value;
966 
967     TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
968 
969     if (!hRecord)
970         return ERROR_INVALID_HANDLE;
971 
972     if (!sz)
973     {
974         if (szResult)
975             return ERROR_INVALID_PARAMETER;
976         else
977             return ERROR_SUCCESS;
978     }
979 
980     r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
981     if (r != ERROR_SUCCESS)
982         return r;
983 
984     value = msi_alloc(++len * sizeof(WCHAR));
985     if (!value)
986         return ERROR_OUTOFMEMORY;
987 
988     r = MsiFormatRecordW( hInstall, hRecord, value, &len );
989     if (r != ERROR_SUCCESS)
990         goto done;
991 
992     save = len + 1;
993     len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
994     WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
995 
996     if (szResult && len > *sz)
997     {
998         if (*sz) szResult[*sz - 1] = '\0';
999         r = ERROR_MORE_DATA;
1000     }
1001 
1002     *sz = save - 1;
1003 
1004 done:
1005     msi_free(value);
1006     return r;
1007 }
1008 
1009 /* wrapper to resist a need for a full rewrite right now */
1010 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data )
1011 {
1012     DWORD len;
1013     MSIRECORD *rec;
1014 
1015     *data = NULL;
1016     if (!fmt) return 0;
1017     if (!(rec = MSI_CreateRecord( 1 ))) return 0;
1018 
1019     MSI_RecordSetStringW( rec, 0, fmt );
1020     MSI_FormatRecordW( package, rec, NULL, &len );
1021     if (!(*data = msi_alloc( ++len * sizeof(WCHAR) )))
1022     {
1023         msiobj_release( &rec->hdr );
1024         return 0;
1025     }
1026     MSI_FormatRecordW( package, rec, *data, &len );
1027     msiobj_release( &rec->hdr );
1028     return len;
1029 }
1030