ntdll: Make wine_build a hidden symbol.
[wine.git] / dlls / msi / format.c
blob9e97c23aaadf9e0d38e22733bd5e22975882de13
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Mike McCormack for CodeWeavers
5 * Copyright 2005 Aric Stewart for CodeWeavers
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.
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.
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
22 #include <stdarg.h>
23 #include <stdio.h>
25 #define COBJMACROS
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"
36 #include "msipriv.h"
37 #include "winemsi.h"
38 #include "wine/exception.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
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
55 #define left_type(x) (x & 0xF0)
57 typedef struct _tagFORMAT
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;
69 typedef struct _tagFORMSTR
71 struct list entry;
72 int n;
73 int len;
74 int type;
75 BOOL propfound;
76 BOOL nonprop;
77 } FORMSTR;
79 typedef struct _tagSTACK
81 struct list items;
82 } STACK;
84 static STACK *create_stack(void)
86 STACK *stack = msi_alloc(sizeof(STACK));
87 list_init(&stack->items);
88 return stack;
91 static void free_stack(STACK *stack)
93 while (!list_empty(&stack->items))
95 FORMSTR *str = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
96 list_remove(&str->entry);
97 msi_free(str);
100 msi_free(stack);
103 static void stack_push(STACK *stack, FORMSTR *str)
105 list_add_head(&stack->items, &str->entry);
108 static FORMSTR *stack_pop(STACK *stack)
110 FORMSTR *ret;
112 if (list_empty(&stack->items))
113 return NULL;
115 ret = LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
116 list_remove(&ret->entry);
117 return ret;
120 static FORMSTR *stack_find(STACK *stack, int type)
122 FORMSTR *str;
124 LIST_FOR_EACH_ENTRY(str, &stack->items, FORMSTR, entry)
126 if (str->type == type)
127 return str;
130 return NULL;
133 static FORMSTR *stack_peek(STACK *stack)
135 return LIST_ENTRY(list_head(&stack->items), FORMSTR, entry);
138 static LPCWSTR get_formstr_data(FORMAT *format, FORMSTR *str)
140 return &format->deformatted[str->n];
143 static WCHAR *dup_formstr( FORMAT *format, FORMSTR *str, int *ret_len )
145 WCHAR *val;
147 if (!str->len) return NULL;
148 if ((val = msi_alloc( (str->len + 1) * sizeof(WCHAR) )))
150 memcpy( val, get_formstr_data(format, str), str->len * sizeof(WCHAR) );
151 val[str->len] = 0;
152 *ret_len = str->len;
154 return val;
157 static WCHAR *deformat_index( FORMAT *format, FORMSTR *str, int *ret_len )
159 WCHAR *val, *ret;
160 DWORD len;
161 int field;
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 = wcstol( val, NULL, 10 );
166 msi_free( val );
168 if (MSI_RecordIsNull( format->record, field ) ||
169 MSI_RecordGetStringW( format->record, field, NULL, &len )) return NULL;
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 ))
176 msi_free( ret );
177 return NULL;
179 *ret_len = len;
180 return ret;
183 static WCHAR *deformat_property( FORMAT *format, FORMSTR *str, int *ret_len )
185 WCHAR *prop, *ret;
186 DWORD len = 0;
187 UINT r;
189 if (!(prop = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
190 lstrcpynW( prop, get_formstr_data(format, str), str->len + 1 );
192 r = msi_get_property( format->package->db, prop, NULL, &len );
193 if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
195 msi_free( prop );
196 return NULL;
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;
206 static WCHAR *deformat_component( FORMAT *format, FORMSTR *str, int *ret_len )
208 WCHAR *key, *ret;
209 MSICOMPONENT *comp;
211 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
212 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
214 if (!(comp = msi_get_loaded_component( format->package, key )))
216 msi_free( key );
217 return NULL;
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 ) );
224 if (ret) *ret_len = lstrlenW( ret );
225 else *ret_len = 0;
226 msi_free( key );
227 return ret;
230 static WCHAR *deformat_file( FORMAT *format, FORMSTR *str, BOOL shortname, int *ret_len )
232 WCHAR *key, *ret = NULL;
233 const MSIFILE *file;
234 DWORD len = 0;
236 if (!(key = msi_alloc( (str->len + 1) * sizeof(WCHAR) ))) return NULL;
237 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
239 if (!(file = msi_get_loaded_file( format->package, key ))) goto done;
240 if (!shortname)
242 if ((ret = strdupW( file->TargetPath ))) len = lstrlenW( ret );
243 goto done;
245 if (!(len = GetShortPathNameW(file->TargetPath, NULL, 0)))
247 if ((ret = strdupW( file->TargetPath ))) len = lstrlenW( ret );
248 goto done;
250 len++;
251 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
252 len = GetShortPathNameW( file->TargetPath, ret, len );
254 done:
255 msi_free( key );
256 *ret_len = len;
257 return ret;
260 static WCHAR *deformat_environment( FORMAT *format, FORMSTR *str, int *ret_len )
262 WCHAR *key, *ret = NULL;
263 DWORD len;
265 if (!(key = msi_alloc((str->len + 1) * sizeof(WCHAR)))) return NULL;
266 lstrcpynW(key, get_formstr_data(format, str), str->len + 1);
268 if ((len = GetEnvironmentVariableW( key, NULL, 0 )))
270 len++;
271 if ((ret = msi_alloc( len * sizeof(WCHAR) )))
272 *ret_len = GetEnvironmentVariableW( key, ret, len );
274 msi_free( key );
275 return ret;
278 static WCHAR *deformat_literal( FORMAT *format, FORMSTR *str, BOOL *propfound,
279 int *type, int *len )
281 LPCWSTR data = get_formstr_data(format, str);
282 WCHAR *replaced = NULL;
283 char ch = data[0];
285 if (ch == '\\')
287 str->n++;
288 if (str->len == 1)
290 str->len = 0;
291 replaced = NULL;
293 else
295 str->len = 1;
296 replaced = dup_formstr( format, str, len );
299 else if (ch == '~')
301 if (str->len != 1)
302 replaced = NULL;
303 else if ((replaced = msi_alloc( sizeof(WCHAR) )))
305 *replaced = 0;
306 *len = 0;
309 else if (ch == '%' || ch == '#' || ch == '!' || ch == '$')
311 str->n++;
312 str->len--;
314 switch (ch)
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;
326 *type = FORMAT_LITERAL;
328 else
330 replaced = deformat_property( format, str, len );
331 *type = FORMAT_LITERAL;
333 if (replaced)
334 *propfound = TRUE;
335 else
336 format->propfailed = TRUE;
339 return replaced;
342 static WCHAR *build_default_format( const MSIRECORD *record )
344 int i, count = MSI_RecordGetFieldCount( record );
345 WCHAR *ret, *tmp, buf[26];
346 DWORD size = 1;
348 if (!(ret = msi_alloc( sizeof(*ret) ))) return NULL;
349 ret[0] = 0;
351 for (i = 1; i <= count; i++)
353 size += swprintf( buf, ARRAY_SIZE(buf), L"%d: [%d] ", i, i );
354 if (!(tmp = msi_realloc( ret, size * sizeof(*ret) )))
356 msi_free( ret );
357 return NULL;
359 ret = tmp;
360 lstrcatW( ret, buf );
362 return ret;
365 static BOOL format_is_number(WCHAR x)
367 return ((x >= '0') && (x <= '9'));
370 static BOOL format_str_is_number(LPWSTR str)
372 LPWSTR ptr;
374 for (ptr = str; *ptr; ptr++)
375 if (!format_is_number(*ptr))
376 return FALSE;
378 return TRUE;
381 static BOOL format_is_alpha(WCHAR x)
383 return (!format_is_number(x) && x != '\0' &&
384 x != '[' && x != ']' && x != '{' && x != '}');
387 static BOOL format_is_literal(WCHAR x)
389 return (format_is_alpha(x) || format_is_number(x));
392 static int format_lex(FORMAT *format, FORMSTR **out)
394 int type, len = 1;
395 FORMSTR *str;
396 LPCWSTR data;
397 WCHAR ch;
399 *out = NULL;
401 if (!format->deformatted)
402 return FORMAT_NULL;
404 *out = msi_alloc_zero(sizeof(FORMSTR));
405 if (!*out)
406 return FORMAT_FAIL;
408 str = *out;
409 str->n = format->n;
410 str->len = 1;
411 data = get_formstr_data(format, str);
413 ch = data[0];
414 switch (ch)
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;
423 default:
424 type = 0;
427 if (type)
429 str->type = type;
430 format->n++;
431 return type;
434 if (ch == '\\')
436 while (data[len] && data[len] != ']')
437 len++;
439 type = FORMAT_ESCAPE;
441 else if (format_is_alpha(ch))
443 while (format_is_literal(data[len]))
444 len++;
446 type = FORMAT_LITERAL;
448 else if (format_is_number(ch))
450 while (format_is_number(data[len]))
451 len++;
453 type = FORMAT_NUMBER;
455 if (data[len] != ']')
457 while (format_is_literal(data[len]))
458 len++;
460 type = FORMAT_LITERAL;
463 else
465 ERR("Got unknown character %c(%x)\n", ch, ch);
466 return FORMAT_ERROR;
469 format->n += len;
470 str->len = len;
471 str->type = type;
473 return type;
476 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop,
477 int oldsize, int type, WCHAR *replace, int len )
479 FORMSTR *ret;
480 LPWSTR str, ptr;
481 DWORD size = 0;
482 int n;
484 if (replace)
486 if (!len)
487 size = 1;
488 else
489 size = len;
492 size -= oldsize;
493 size = format->len + size + 1;
495 if (size <= 1)
497 msi_free(format->deformatted);
498 format->deformatted = NULL;
499 format->len = 0;
500 return NULL;
503 str = msi_alloc(size * sizeof(WCHAR));
504 if (!str)
505 return NULL;
507 str[0] = '\0';
508 memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
509 n = format->n;
511 if (replace)
513 if (!len) str[n++] = 0;
514 else
516 memcpy( str + n, replace, len * sizeof(WCHAR) );
517 n += len;
518 str[n] = 0;
522 ptr = &format->deformatted[format->n + oldsize];
523 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
525 msi_free(format->deformatted);
526 format->deformatted = str;
527 format->len = size - 1;
529 /* don't reformat the NULL */
530 if (replace && !len)
531 format->n++;
533 if (!replace)
534 return NULL;
536 ret = msi_alloc_zero(sizeof(FORMSTR));
537 if (!ret)
538 return NULL;
540 ret->len = len;
541 ret->type = type;
542 ret->n = format->n;
543 ret->propfound = propfound;
544 ret->nonprop = nonprop;
546 return ret;
549 static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
550 BOOL *propfound, BOOL *nonprop,
551 int *oldsize, int *type, int *len )
553 WCHAR *replaced;
554 FORMSTR *content, *node;
555 int n;
557 *nonprop = FALSE;
558 *propfound = FALSE;
560 node = stack_pop(values);
561 n = node->n;
562 *oldsize = node->len;
563 msi_free(node);
565 while ((node = stack_pop(values)))
567 *oldsize += node->len;
569 if (node->nonprop)
570 *nonprop = TRUE;
572 if (node->propfound)
573 *propfound = TRUE;
575 msi_free(node);
578 content = msi_alloc_zero(sizeof(FORMSTR));
579 content->n = n;
580 content->len = *oldsize;
581 content->type = FORMAT_LITERAL;
583 if (!format->groupfailed && (*oldsize == 2 ||
584 (format->propfailed && !*nonprop)))
586 msi_free(content);
587 return NULL;
589 else if (format->deformatted[content->n + 1] == '{' &&
590 format->deformatted[content->n + content->len - 2] == '}')
592 format->groupfailed = FALSE;
593 content->len = 0;
595 else if (*propfound && !*nonprop &&
596 !format->groupfailed && format->groups == 0)
598 content->n++;
599 content->len -= 2;
601 else
603 if (format->groups != 0)
604 format->groupfailed = TRUE;
606 *nonprop = TRUE;
609 replaced = dup_formstr( format, content, len );
610 *type = content->type;
611 msi_free(content);
613 if (format->groups == 0)
614 format->propfailed = FALSE;
616 return replaced;
619 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
620 BOOL *propfound, BOOL *nonprop,
621 int *oldsize, int *type, int *len )
623 WCHAR *replaced;
624 FORMSTR *content, *node;
625 int n;
627 *propfound = FALSE;
628 *nonprop = FALSE;
630 node = stack_pop(values);
631 n = node->n;
632 *oldsize = node->len;
633 *type = stack_peek(values)->type;
634 msi_free(node);
636 while ((node = stack_pop(values)))
638 *oldsize += node->len;
640 if (*type != FORMAT_ESCAPE &&
641 stack_peek(values) && node->type != *type)
642 *type = FORMAT_LITERAL;
644 msi_free(node);
647 content = msi_alloc_zero(sizeof(FORMSTR));
648 content->n = n + 1;
649 content->len = *oldsize - 2;
650 content->type = *type;
652 if (*type == FORMAT_NUMBER && format->record)
654 replaced = deformat_index( format, content, len );
655 if (replaced)
656 *propfound = TRUE;
657 else
658 format->propfailed = TRUE;
660 if (replaced)
661 *type = format_str_is_number(replaced) ?
662 FORMAT_NUMBER : FORMAT_LITERAL;
664 else if (format->package)
666 replaced = deformat_literal( format, content, propfound, type, len );
668 else
670 *nonprop = TRUE;
671 content->n--;
672 content->len += 2;
673 replaced = dup_formstr( format, content, len );
675 msi_free(content);
676 return replaced;
679 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
681 WCHAR *replaced = NULL;
682 FORMSTR *beg, *top, *node;
683 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
684 int type, n, len = 0, oldsize = 0;
686 node = stack_peek(values);
687 type = node->type;
688 n = node->n;
690 if (type == FORMAT_LBRACK)
691 replaced = replace_stack_prop( format, values, &propfound,
692 &nonprop, &oldsize, &type, &len );
693 else if (type == FORMAT_LBRACE)
695 replaced = replace_stack_group( format, values, &propfound,
696 &nonprop, &oldsize, &type, &len );
697 group = TRUE;
700 format->n = n;
701 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
702 msi_free(replaced);
703 if (!beg)
704 return ERROR_SUCCESS;
706 format->n = beg->n + beg->len;
708 top = stack_peek(stack);
709 if (top)
711 type = top->type;
713 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
714 type == beg->type)
716 top->len += beg->len;
718 if (group)
719 top->nonprop = FALSE;
721 if (type == FORMAT_LITERAL)
722 top->nonprop = beg->nonprop;
724 if (beg->propfound)
725 top->propfound = TRUE;
727 msi_free(beg);
728 return ERROR_SUCCESS;
732 stack_push(stack, beg);
733 return ERROR_SUCCESS;
736 static BOOL verify_format(LPWSTR data)
738 int count = 0;
740 while (*data)
742 if (*data == '[' && *(data - 1) != '\\')
743 count++;
744 else if (*data == ']')
745 count--;
747 data++;
750 if (count > 0)
751 return FALSE;
753 return TRUE;
756 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
757 WCHAR** data, DWORD *len,
758 MSIRECORD* record)
760 FORMAT format;
761 FORMSTR *str = NULL;
762 STACK *stack, *temp;
763 FORMSTR *node;
764 int type;
766 if (!ptr)
768 *data = NULL;
769 *len = 0;
770 return ERROR_SUCCESS;
773 *data = strdupW(ptr);
774 *len = lstrlenW(ptr);
776 ZeroMemory(&format, sizeof(FORMAT));
777 format.package = package;
778 format.record = record;
779 format.deformatted = *data;
780 format.len = *len;
782 if (!verify_format(*data))
783 return ERROR_SUCCESS;
785 stack = create_stack();
786 temp = create_stack();
788 while ((type = format_lex(&format, &str)) != FORMAT_NULL)
790 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
791 type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
792 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
794 if (type == FORMAT_LBRACE)
796 format.propfailed = FALSE;
797 format.groups++;
799 else if (type == FORMAT_ESCAPE &&
800 !stack_find(stack, FORMAT_LBRACK))
802 format.n -= str->len - 1;
803 str->len = 1;
806 stack_push(stack, str);
808 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
810 if (type == FORMAT_RBRACE)
811 format.groups--;
813 stack_push(stack, str);
815 if (stack_find(stack, left_type(type)))
819 node = stack_pop(stack);
820 stack_push(temp, node);
821 } while (node->type != left_type(type));
823 replace_stack(&format, stack, temp);
828 *data = format.deformatted;
829 *len = format.len;
831 msi_free(str);
832 free_stack(stack);
833 free_stack(temp);
835 return ERROR_SUCCESS;
838 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
839 LPDWORD size )
841 WCHAR *format, *deformated = NULL;
842 UINT rc = ERROR_INVALID_PARAMETER;
843 DWORD len;
844 MSIRECORD *record_deformated;
845 int field_count, i;
847 TRACE("%p %p %p %p\n", package, record, buffer, size);
848 dump_record(record);
850 if (!(format = msi_dup_record_field( record, 0 )))
851 format = build_default_format( record );
853 field_count = MSI_RecordGetFieldCount(record);
854 record_deformated = MSI_CloneRecord(record);
855 if (!record_deformated)
857 rc = ERROR_OUTOFMEMORY;
858 goto end;
860 MSI_RecordSetStringW(record_deformated, 0, format);
861 for (i = 1; i <= field_count; i++)
863 if (MSI_RecordGetString(record, i))
865 deformat_string_internal(package, MSI_RecordGetString(record, i), &deformated, &len, NULL);
866 MSI_RecordSetStringW(record_deformated, i, deformated);
867 msi_free(deformated);
871 deformat_string_internal(package, format, &deformated, &len, record_deformated);
872 if (buffer)
874 if (*size>len)
876 memcpy(buffer,deformated,len*sizeof(WCHAR));
877 rc = ERROR_SUCCESS;
878 buffer[len] = 0;
880 else
882 if (*size > 0)
884 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
885 buffer[(*size)-1] = 0;
887 rc = ERROR_MORE_DATA;
890 else rc = ERROR_SUCCESS;
892 *size = len;
893 msiobj_release(&record_deformated->hdr);
894 end:
895 msi_free( format );
896 msi_free( deformated );
897 return rc;
900 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
901 LPWSTR szResult, LPDWORD sz )
903 UINT r = ERROR_INVALID_HANDLE;
904 MSIPACKAGE *package;
905 MSIRECORD *record;
907 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
909 record = msihandle2msiinfo(hRecord, MSIHANDLETYPE_RECORD);
910 if (!record)
911 return ERROR_INVALID_HANDLE;
913 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
914 if (!package)
916 LPWSTR value = NULL;
917 MSIHANDLE remote;
919 if ((remote = msi_get_remote(hInstall)))
921 __TRY
923 r = remote_FormatRecord(remote, (struct wire_record *)&record->count, &value);
925 __EXCEPT(rpc_filter)
927 r = GetExceptionCode();
929 __ENDTRY
931 if (!r)
932 r = msi_strncpyW(value, -1, szResult, sz);
934 midl_user_free(value);
935 msiobj_release(&record->hdr);
936 return r;
940 if (!sz)
942 msiobj_release( &record->hdr );
943 if (szResult)
944 return ERROR_INVALID_PARAMETER;
945 else
946 return ERROR_SUCCESS;
949 r = MSI_FormatRecordW( package, record, szResult, sz );
950 msiobj_release( &record->hdr );
951 if (package)
952 msiobj_release( &package->hdr );
953 return r;
956 UINT WINAPI MsiFormatRecordA(MSIHANDLE hinst, MSIHANDLE hrec, char *buf, DWORD *sz)
958 MSIPACKAGE *package;
959 MSIRECORD *rec;
960 LPWSTR value;
961 DWORD len;
962 UINT r;
964 TRACE("%d %d %p %p\n", hinst, hrec, buf, sz);
966 rec = msihandle2msiinfo(hrec, MSIHANDLETYPE_RECORD);
967 if (!rec)
968 return ERROR_INVALID_HANDLE;
970 package = msihandle2msiinfo(hinst, MSIHANDLETYPE_PACKAGE);
971 if (!package)
973 LPWSTR value = NULL;
974 MSIHANDLE remote;
976 if ((remote = msi_get_remote(hinst)))
978 __TRY
980 r = remote_FormatRecord(remote, (struct wire_record *)&rec->count, &value);
982 __EXCEPT(rpc_filter)
984 r = GetExceptionCode();
986 __ENDTRY
988 if (!r)
989 r = msi_strncpyWtoA(value, -1, buf, sz, TRUE);
991 midl_user_free(value);
992 msiobj_release(&rec->hdr);
993 return r;
997 r = MSI_FormatRecordW(package, rec, NULL, &len);
998 if (r != ERROR_SUCCESS)
999 return r;
1001 value = msi_alloc(++len * sizeof(WCHAR));
1002 if (!value)
1003 goto done;
1005 r = MSI_FormatRecordW(package, rec, value, &len);
1006 if (!r)
1007 r = msi_strncpyWtoA(value, len, buf, sz, FALSE);
1009 msi_free(value);
1010 done:
1011 msiobj_release(&rec->hdr);
1012 if (package) msiobj_release(&package->hdr);
1013 return r;
1016 /* wrapper to resist a need for a full rewrite right now */
1017 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data )
1019 DWORD len;
1020 MSIRECORD *rec;
1022 *data = NULL;
1023 if (!fmt) return 0;
1024 if (!(rec = MSI_CreateRecord( 1 ))) return 0;
1026 MSI_RecordSetStringW( rec, 0, fmt );
1027 MSI_FormatRecordW( package, rec, NULL, &len );
1028 if (!(*data = msi_alloc( ++len * sizeof(WCHAR) )))
1030 msiobj_release( &rec->hdr );
1031 return 0;
1033 MSI_FormatRecordW( package, rec, *data, &len );
1034 msiobj_release( &rec->hdr );
1035 return len;