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