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