bcrypt: Require macOS version 10.7 or newer for encryption support.
[wine.git] / dlls / msi / format.c
blob3bcd0b84ee6183111b324380a2b371d3c91eb56a
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 "msiserver.h"
38 #include "wine/unicode.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 = atoiW( val );
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 = strlenW( 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 = strlenW( ret );
243 goto done;
245 if ((len = GetShortPathNameW(file->TargetPath, NULL, 0)) <= 0)
247 if ((ret = strdupW( file->TargetPath ))) len = strlenW( 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 static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
345 int i, count = MSI_RecordGetFieldCount( record );
346 WCHAR *ret, *tmp, buf[26];
347 DWORD size = 1;
349 if (!(ret = msi_alloc( sizeof(*ret) ))) return NULL;
350 ret[0] = 0;
352 for (i = 1; i <= count; i++)
354 size += sprintfW( buf, fmt, i, i );
355 if (!(tmp = msi_realloc( ret, size * sizeof(*ret) )))
357 msi_free( ret );
358 return NULL;
360 ret = tmp;
361 strcatW( ret, buf );
363 return ret;
366 static BOOL format_is_number(WCHAR x)
368 return ((x >= '0') && (x <= '9'));
371 static BOOL format_str_is_number(LPWSTR str)
373 LPWSTR ptr;
375 for (ptr = str; *ptr; ptr++)
376 if (!format_is_number(*ptr))
377 return FALSE;
379 return TRUE;
382 static BOOL format_is_alpha(WCHAR x)
384 return (!format_is_number(x) && x != '\0' &&
385 x != '[' && x != ']' && x != '{' && x != '}');
388 static BOOL format_is_literal(WCHAR x)
390 return (format_is_alpha(x) || format_is_number(x));
393 static int format_lex(FORMAT *format, FORMSTR **out)
395 int type, len = 1;
396 FORMSTR *str;
397 LPCWSTR data;
398 WCHAR ch;
400 *out = NULL;
402 if (!format->deformatted)
403 return FORMAT_NULL;
405 *out = msi_alloc_zero(sizeof(FORMSTR));
406 if (!*out)
407 return FORMAT_FAIL;
409 str = *out;
410 str->n = format->n;
411 str->len = 1;
412 data = get_formstr_data(format, str);
414 ch = data[0];
415 switch (ch)
417 case '{': type = FORMAT_LBRACE; break;
418 case '}': type = FORMAT_RBRACE; break;
419 case '[': type = FORMAT_LBRACK; break;
420 case ']': type = FORMAT_RBRACK; break;
421 case '~': type = FORMAT_PROPNULL; break;
422 case '\0': type = FORMAT_NULL; break;
424 default:
425 type = 0;
428 if (type)
430 str->type = type;
431 format->n++;
432 return type;
435 if (ch == '\\')
437 while (data[len] && data[len] != ']')
438 len++;
440 type = FORMAT_ESCAPE;
442 else if (format_is_alpha(ch))
444 while (format_is_literal(data[len]))
445 len++;
447 type = FORMAT_LITERAL;
449 else if (format_is_number(ch))
451 while (format_is_number(data[len]))
452 len++;
454 type = FORMAT_NUMBER;
456 if (data[len] != ']')
458 while (format_is_literal(data[len]))
459 len++;
461 type = FORMAT_LITERAL;
464 else
466 ERR("Got unknown character %c(%x)\n", ch, ch);
467 return FORMAT_ERROR;
470 format->n += len;
471 str->len = len;
472 str->type = type;
474 return type;
477 static FORMSTR *format_replace( FORMAT *format, BOOL propfound, BOOL nonprop,
478 int oldsize, int type, WCHAR *replace, int len )
480 FORMSTR *ret;
481 LPWSTR str, ptr;
482 DWORD size = 0;
483 int n;
485 if (replace)
487 if (!len)
488 size = 1;
489 else
490 size = len;
493 size -= oldsize;
494 size = format->len + size + 1;
496 if (size <= 1)
498 msi_free(format->deformatted);
499 format->deformatted = NULL;
500 format->len = 0;
501 return NULL;
504 str = msi_alloc(size * sizeof(WCHAR));
505 if (!str)
506 return NULL;
508 str[0] = '\0';
509 memcpy(str, format->deformatted, format->n * sizeof(WCHAR));
510 n = format->n;
512 if (replace)
514 if (!len) str[n++] = 0;
515 else
517 memcpy( str + n, replace, len * sizeof(WCHAR) );
518 n += len;
519 str[n] = 0;
523 ptr = &format->deformatted[format->n + oldsize];
524 memcpy(&str[n], ptr, (lstrlenW(ptr) + 1) * sizeof(WCHAR));
526 msi_free(format->deformatted);
527 format->deformatted = str;
528 format->len = size - 1;
530 /* don't reformat the NULL */
531 if (replace && !len)
532 format->n++;
534 if (!replace)
535 return NULL;
537 ret = msi_alloc_zero(sizeof(FORMSTR));
538 if (!ret)
539 return NULL;
541 ret->len = len;
542 ret->type = type;
543 ret->n = format->n;
544 ret->propfound = propfound;
545 ret->nonprop = nonprop;
547 return ret;
550 static WCHAR *replace_stack_group( FORMAT *format, STACK *values,
551 BOOL *propfound, BOOL *nonprop,
552 int *oldsize, int *type, int *len )
554 WCHAR *replaced;
555 FORMSTR *content, *node;
556 int n;
558 *nonprop = FALSE;
559 *propfound = FALSE;
561 node = stack_pop(values);
562 n = node->n;
563 *oldsize = node->len;
564 msi_free(node);
566 while ((node = stack_pop(values)))
568 *oldsize += node->len;
570 if (node->nonprop)
571 *nonprop = TRUE;
573 if (node->propfound)
574 *propfound = TRUE;
576 msi_free(node);
579 content = msi_alloc_zero(sizeof(FORMSTR));
580 content->n = n;
581 content->len = *oldsize;
582 content->type = FORMAT_LITERAL;
584 if (!format->groupfailed && (*oldsize == 2 ||
585 (format->propfailed && !*nonprop)))
587 msi_free(content);
588 return NULL;
590 else if (format->deformatted[content->n + 1] == '{' &&
591 format->deformatted[content->n + content->len - 2] == '}')
593 format->groupfailed = FALSE;
594 content->len = 0;
596 else if (*propfound && !*nonprop &&
597 !format->groupfailed && format->groups == 0)
599 content->n++;
600 content->len -= 2;
602 else
604 if (format->groups != 0)
605 format->groupfailed = TRUE;
607 *nonprop = TRUE;
610 replaced = dup_formstr( format, content, len );
611 *type = content->type;
612 msi_free(content);
614 if (format->groups == 0)
615 format->propfailed = FALSE;
617 return replaced;
620 static WCHAR *replace_stack_prop( FORMAT *format, STACK *values,
621 BOOL *propfound, BOOL *nonprop,
622 int *oldsize, int *type, int *len )
624 WCHAR *replaced;
625 FORMSTR *content, *node;
626 int n;
628 *propfound = FALSE;
629 *nonprop = FALSE;
631 node = stack_pop(values);
632 n = node->n;
633 *oldsize = node->len;
634 *type = stack_peek(values)->type;
635 msi_free(node);
637 while ((node = stack_pop(values)))
639 *oldsize += node->len;
641 if (*type != FORMAT_ESCAPE &&
642 stack_peek(values) && node->type != *type)
643 *type = FORMAT_LITERAL;
645 msi_free(node);
648 content = msi_alloc_zero(sizeof(FORMSTR));
649 content->n = n + 1;
650 content->len = *oldsize - 2;
651 content->type = *type;
653 if (*type == FORMAT_NUMBER && format->record)
655 replaced = deformat_index( format, content, len );
656 if (replaced)
657 *propfound = TRUE;
658 else
659 format->propfailed = TRUE;
661 if (replaced)
662 *type = format_str_is_number(replaced) ?
663 FORMAT_NUMBER : FORMAT_LITERAL;
665 else if (format->package)
667 replaced = deformat_literal( format, content, propfound, type, len );
669 else
671 *nonprop = TRUE;
672 content->n--;
673 content->len += 2;
674 replaced = dup_formstr( format, content, len );
676 msi_free(content);
677 return replaced;
680 static UINT replace_stack(FORMAT *format, STACK *stack, STACK *values)
682 WCHAR *replaced = NULL;
683 FORMSTR *beg, *top, *node;
684 BOOL propfound = FALSE, nonprop = FALSE, group = FALSE;
685 int type, n, len = 0, oldsize = 0;
687 node = stack_peek(values);
688 type = node->type;
689 n = node->n;
691 if (type == FORMAT_LBRACK)
692 replaced = replace_stack_prop( format, values, &propfound,
693 &nonprop, &oldsize, &type, &len );
694 else if (type == FORMAT_LBRACE)
696 replaced = replace_stack_group( format, values, &propfound,
697 &nonprop, &oldsize, &type, &len );
698 group = TRUE;
701 format->n = n;
702 beg = format_replace( format, propfound, nonprop, oldsize, type, replaced, len );
703 msi_free(replaced);
704 if (!beg)
705 return ERROR_SUCCESS;
707 format->n = beg->n + beg->len;
709 top = stack_peek(stack);
710 if (top)
712 type = top->type;
714 if ((type == FORMAT_LITERAL || type == FORMAT_NUMBER) &&
715 type == beg->type)
717 top->len += beg->len;
719 if (group)
720 top->nonprop = FALSE;
722 if (type == FORMAT_LITERAL)
723 top->nonprop = beg->nonprop;
725 if (beg->propfound)
726 top->propfound = TRUE;
728 msi_free(beg);
729 return ERROR_SUCCESS;
733 stack_push(stack, beg);
734 return ERROR_SUCCESS;
737 static BOOL verify_format(LPWSTR data)
739 int count = 0;
741 while (*data)
743 if (*data == '[' && *(data - 1) != '\\')
744 count++;
745 else if (*data == ']')
746 count--;
748 data++;
751 if (count > 0)
752 return FALSE;
754 return TRUE;
757 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
758 WCHAR** data, DWORD *len,
759 MSIRECORD* record)
761 FORMAT format;
762 FORMSTR *str = NULL;
763 STACK *stack, *temp;
764 FORMSTR *node;
765 int type;
767 if (!ptr)
769 *data = NULL;
770 *len = 0;
771 return ERROR_SUCCESS;
774 *data = strdupW(ptr);
775 *len = lstrlenW(ptr);
777 ZeroMemory(&format, sizeof(FORMAT));
778 format.package = package;
779 format.record = record;
780 format.deformatted = *data;
781 format.len = *len;
783 if (!verify_format(*data))
784 return ERROR_SUCCESS;
786 stack = create_stack();
787 temp = create_stack();
789 while ((type = format_lex(&format, &str)) != FORMAT_NULL)
791 if (type == FORMAT_LBRACK || type == FORMAT_LBRACE ||
792 type == FORMAT_LITERAL || type == FORMAT_NUMBER ||
793 type == FORMAT_ESCAPE || type == FORMAT_PROPNULL)
795 if (type == FORMAT_LBRACE)
797 format.propfailed = FALSE;
798 format.groups++;
800 else if (type == FORMAT_ESCAPE &&
801 !stack_find(stack, FORMAT_LBRACK))
803 format.n -= str->len - 1;
804 str->len = 1;
807 stack_push(stack, str);
809 else if (type == FORMAT_RBRACK || type == FORMAT_RBRACE)
811 if (type == FORMAT_RBRACE)
812 format.groups--;
814 stack_push(stack, str);
816 if (stack_find(stack, left_type(type)))
820 node = stack_pop(stack);
821 stack_push(temp, node);
822 } while (node->type != left_type(type));
824 replace_stack(&format, stack, temp);
829 *data = format.deformatted;
830 *len = format.len;
832 msi_free(str);
833 free_stack(stack);
834 free_stack(temp);
836 return ERROR_SUCCESS;
839 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
840 LPDWORD size )
842 WCHAR *format, *deformated = NULL;
843 UINT rc = ERROR_INVALID_PARAMETER;
844 DWORD len;
845 MSIRECORD *record_deformated;
846 int field_count, i;
848 TRACE("%p %p %p %p\n", package, record, buffer, size);
849 dump_record(record);
851 if (!(format = msi_dup_record_field( record, 0 )))
852 format = build_default_format( record );
854 field_count = MSI_RecordGetFieldCount(record);
855 record_deformated = MSI_CloneRecord(record);
856 if (!record_deformated)
858 rc = ERROR_OUTOFMEMORY;
859 goto end;
861 MSI_RecordSetStringW(record_deformated, 0, format);
862 for (i = 1; i <= field_count; i++)
864 if (MSI_RecordGetString(record, i))
866 deformat_string_internal(package, MSI_RecordGetString(record, i), &deformated, &len, NULL);
867 MSI_RecordSetStringW(record_deformated, i, deformated);
868 msi_free(deformated);
872 deformat_string_internal(package, format, &deformated, &len, record_deformated);
873 if (buffer)
875 if (*size>len)
877 memcpy(buffer,deformated,len*sizeof(WCHAR));
878 rc = ERROR_SUCCESS;
879 buffer[len] = 0;
881 else
883 if (*size > 0)
885 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
886 buffer[(*size)-1] = 0;
888 rc = ERROR_MORE_DATA;
891 else rc = ERROR_SUCCESS;
893 *size = len;
894 msiobj_release(&record_deformated->hdr);
895 end:
896 msi_free( format );
897 msi_free( deformated );
898 return rc;
901 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
902 LPWSTR szResult, LPDWORD sz )
904 UINT r = ERROR_INVALID_HANDLE;
905 MSIPACKAGE *package;
906 MSIRECORD *record;
908 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
910 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
911 if (!package)
913 HRESULT hr;
914 IWineMsiRemotePackage *remote_package;
915 BSTR value = NULL;
916 awstring wstr;
918 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
919 if (remote_package)
921 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
922 &value );
923 if (FAILED(hr))
924 goto done;
926 wstr.unicode = TRUE;
927 wstr.str.w = szResult;
928 r = msi_strcpy_to_awstring( value, SysStringLen(value), &wstr, sz );
930 done:
931 IWineMsiRemotePackage_Release( remote_package );
932 SysFreeString( value );
934 if (FAILED(hr))
936 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
937 return HRESULT_CODE(hr);
939 return ERROR_FUNCTION_FAILED;
942 return r;
946 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
948 if (!record)
949 return ERROR_INVALID_HANDLE;
950 if (!sz)
952 msiobj_release( &record->hdr );
953 if (szResult)
954 return ERROR_INVALID_PARAMETER;
955 else
956 return ERROR_SUCCESS;
959 r = MSI_FormatRecordW( package, record, szResult, sz );
960 msiobj_release( &record->hdr );
961 if (package)
962 msiobj_release( &package->hdr );
963 return r;
966 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
967 LPSTR szResult, LPDWORD sz )
969 UINT r;
970 DWORD len, save;
971 LPWSTR value;
973 TRACE("%d %d %p %p\n", hInstall, hRecord, szResult, sz);
975 if (!hRecord)
976 return ERROR_INVALID_HANDLE;
978 if (!sz)
980 if (szResult)
981 return ERROR_INVALID_PARAMETER;
982 else
983 return ERROR_SUCCESS;
986 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
987 if (r != ERROR_SUCCESS)
988 return r;
990 value = msi_alloc(++len * sizeof(WCHAR));
991 if (!value)
992 return ERROR_OUTOFMEMORY;
994 r = MsiFormatRecordW( hInstall, hRecord, value, &len );
995 if (r != ERROR_SUCCESS)
996 goto done;
998 save = len + 1;
999 len = WideCharToMultiByte(CP_ACP, 0, value, len + 1, NULL, 0, NULL, NULL);
1000 WideCharToMultiByte(CP_ACP, 0, value, len, szResult, *sz, NULL, NULL);
1002 if (szResult && len > *sz)
1004 if (*sz) szResult[*sz - 1] = '\0';
1005 r = ERROR_MORE_DATA;
1008 *sz = save - 1;
1010 done:
1011 msi_free(value);
1012 return r;
1015 /* wrapper to resist a need for a full rewrite right now */
1016 DWORD deformat_string( MSIPACKAGE *package, const WCHAR *fmt, WCHAR **data )
1018 DWORD len;
1019 MSIRECORD *rec;
1021 *data = NULL;
1022 if (!fmt) return 0;
1023 if (!(rec = MSI_CreateRecord( 1 ))) return 0;
1025 MSI_RecordSetStringW( rec, 0, fmt );
1026 MSI_FormatRecordW( package, rec, NULL, &len );
1027 if (!(*data = msi_alloc( ++len * sizeof(WCHAR) )))
1029 msiobj_release( &rec->hdr );
1030 return 0;
1032 MSI_FormatRecordW( package, rec, *data, &len );
1033 msiobj_release( &rec->hdr );
1034 return len;