ntdll: Add some I/O completion tests.
[wine/wine64.git] / dlls / msi / format.c
blob41d4a473edda2e8c0232db4dbc5b23b276c620b0
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
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
26 #include <stdarg.h>
27 #include <stdio.h>
29 #define COBJMACROS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34 #include "wine/debug.h"
35 #include "msi.h"
36 #include "winnls.h"
37 #include "objbase.h"
38 #include "oleauto.h"
40 #include "msipriv.h"
41 #include "msiserver.h"
42 #include "wine/unicode.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
48 WCHAR** data, DWORD len, MSIRECORD* record,
49 BOOL* in_group);
52 static LPWSTR build_default_format(const MSIRECORD* record)
54 int i;
55 int count;
56 LPWSTR rc, buf;
57 static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
58 static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
59 static const WCHAR fmt_index[] = {'%','i',0};
60 LPCWSTR str;
61 WCHAR index[10];
62 DWORD size, max_len, len;
64 count = MSI_RecordGetFieldCount(record);
66 max_len = MAX_PATH;
67 buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
69 rc = NULL;
70 size = 1;
71 for (i = 1; i <= count; i++)
73 sprintfW(index,fmt_index,i);
74 str = MSI_RecordGetString(record, i);
75 len = (str) ? lstrlenW(str) : 0;
76 len += (sizeof(fmt_null) - 3) + lstrlenW(index);
77 size += len;
79 if (len > max_len)
81 max_len = len;
82 buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
83 if (!buf) return NULL;
86 if (str)
87 sprintfW(buf,fmt,i,str);
88 else
89 sprintfW(buf,fmt_null,i);
91 if (!rc)
93 rc = msi_alloc(size * sizeof(WCHAR));
94 lstrcpyW(rc, buf);
96 else
98 rc = msi_realloc(rc, size * sizeof(WCHAR));
99 lstrcatW(rc, buf);
102 msi_free(buf);
103 return rc;
106 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
108 DWORD i;
109 for (i = 0; i < len; i++)
110 if (buf[i] == token)
111 return &buf[i];
112 return NULL;
115 /* break out helper functions for deformating */
116 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
118 LPWSTR value = NULL;
119 MSICOMPONENT *comp;
120 BOOL source;
122 *sz = 0;
123 if (!package)
124 return NULL;
126 comp = get_loaded_component(package,key);
127 if (comp)
129 source = (comp->Action == INSTALLSTATE_SOURCE) ? TRUE : FALSE;
130 value = resolve_folder(package, comp->Directory, source, FALSE, TRUE, NULL);
131 *sz = (strlenW(value)) * sizeof(WCHAR);
134 return value;
137 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
138 BOOL shortname)
140 LPWSTR value = NULL;
141 MSIFILE *file;
143 *sz = 0;
145 if (!package)
146 return NULL;
148 file = get_loaded_file( package, key );
149 if (file)
151 if (!shortname)
153 value = strdupW( file->TargetPath );
154 *sz = (strlenW(value)) * sizeof(WCHAR);
156 else
158 DWORD size = 0;
159 size = GetShortPathNameW( file->TargetPath, NULL, 0 );
161 if (size > 0)
163 *sz = (size-1) * sizeof (WCHAR);
164 size ++;
165 value = msi_alloc(size * sizeof(WCHAR));
166 GetShortPathNameW( file->TargetPath, value, size );
168 else
170 value = strdupW( file->TargetPath );
171 *sz = (lstrlenW(value)) * sizeof(WCHAR);
176 return value;
179 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
180 DWORD* chunk)
182 LPWSTR value = NULL;
183 DWORD sz;
185 sz = GetEnvironmentVariableW(key,NULL,0);
186 if (sz > 0)
188 sz++;
189 value = msi_alloc(sz * sizeof(WCHAR));
190 GetEnvironmentVariableW(key,value,sz);
191 *chunk = (strlenW(value)) * sizeof(WCHAR);
193 else
195 ERR("Unknown environment variable %s\n", debugstr_w(key));
196 *chunk = 0;
197 value = NULL;
199 return value;
203 static LPWSTR deformat_NULL(DWORD* chunk)
205 LPWSTR value;
207 value = msi_alloc(sizeof(WCHAR)*2);
208 value[0] = 0;
209 *chunk = sizeof(WCHAR);
210 return value;
213 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
215 LPWSTR value;
217 value = msi_alloc(sizeof(WCHAR)*2);
218 value[0] = key[0];
219 *chunk = sizeof(WCHAR);
221 return value;
225 static BOOL is_key_number(LPCWSTR key)
227 INT index = 0;
228 if (key[0] == 0)
229 return FALSE;
231 while (isdigitW(key[index])) index++;
232 if (key[index] == 0)
233 return TRUE;
234 else
235 return FALSE;
238 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
240 INT index;
241 LPWSTR value;
243 index = atoiW(key);
244 TRACE("record index %i\n",index);
245 value = msi_dup_record_field(record,index);
246 if (value)
247 *chunk = strlenW(value) * sizeof(WCHAR);
248 else
250 value = NULL;
251 *chunk = 0;
253 return value;
256 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
258 LPWSTR value;
260 if (!package)
261 return NULL;
263 value = msi_dup_property( package, key );
265 if (value)
266 *chunk = (strlenW(value)) * sizeof(WCHAR);
268 return value;
272 * Groups cannot be nested. They are just treated as from { to next }
274 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
275 LPWSTR *group, LPCWSTR *mark,
276 LPCWSTR* mark2)
278 int i;
279 BOOL found = FALSE;
281 *mark = scanW(source,'{',len_remaining);
282 if (!*mark)
283 return FALSE;
285 for (i = 1; (*mark - source) + i < len_remaining; i++)
287 if ((*mark)[i] == '}')
289 found = TRUE;
290 break;
293 if (! found)
294 return FALSE;
296 *mark2 = &(*mark)[i];
298 i = *mark2 - *mark;
299 *group = msi_alloc(i*sizeof(WCHAR));
301 i -= 1;
302 memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
303 (*group)[i] = 0;
305 TRACE("Found group %s\n",debugstr_w(*group));
306 return TRUE;
310 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
311 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
312 BOOL *nested)
314 INT count = 0;
315 INT total_count = 0;
316 int i;
318 *nested = FALSE;
319 *mark = scanW(source,'[',len_remaining);
320 if (!*mark)
321 return FALSE;
323 count = 1;
324 total_count = 1;
325 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
327 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
329 count ++;
330 total_count ++;
331 *nested = TRUE;
333 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
335 count --;
339 if (count > 0)
340 return FALSE;
342 *mark2 = &(*mark)[i-1];
344 i = *mark2 - *mark;
345 *key = msi_alloc(i*sizeof(WCHAR));
346 /* do not have the [] in the key */
347 i -= 1;
348 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
349 (*key)[i] = 0;
351 TRACE("Found Key %s\n",debugstr_w(*key));
352 return TRUE;
355 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
356 MSIRECORD* record, DWORD* size)
358 LPWSTR value = NULL;
359 LPCWSTR mark, mark2;
360 LPWSTR key;
361 BOOL nested;
362 INT failcount;
363 static const WCHAR fmt[] = {'{','%','s','}',0};
364 UINT sz;
366 if (!group || group[0] == 0)
368 *size = 0;
369 return NULL;
371 /* if no [] then group is returned as is */
373 if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
375 *size = (len+2)*sizeof(WCHAR);
376 value = msi_alloc(*size);
377 sprintfW(value,fmt,group);
378 /* do not return size of the null at the end */
379 *size = (len+1)*sizeof(WCHAR);
380 return value;
383 msi_free(key);
384 failcount = 0;
385 sz = deformat_string_internal(package, group, &value, strlenW(group),
386 record, &failcount);
387 if (failcount==0)
389 *size = sz * sizeof(WCHAR);
390 return value;
392 else if (failcount < 0)
394 LPWSTR v2;
396 v2 = msi_alloc((sz+2)*sizeof(WCHAR));
397 v2[0] = '{';
398 memcpy(&v2[1],value,sz*sizeof(WCHAR));
399 v2[sz+1]='}';
400 msi_free(value);
402 *size = (sz+2)*sizeof(WCHAR);
403 return v2;
405 else
407 msi_free(value);
408 *size = 0;
409 return NULL;
415 * len is in WCHARs
416 * return is also in WCHARs
418 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
419 WCHAR** data, DWORD len, MSIRECORD* record,
420 INT* failcount)
422 LPCWSTR mark = NULL;
423 LPCWSTR mark2 = NULL;
424 DWORD size=0;
425 DWORD chunk=0;
426 LPWSTR key;
427 LPWSTR value = NULL;
428 DWORD sz;
429 LPBYTE newdata = NULL;
430 const WCHAR* progress = NULL;
431 BOOL nested = FALSE;
433 if (ptr==NULL)
435 TRACE("Deformatting NULL string\n");
436 *data = NULL;
437 return 0;
440 TRACE("Starting with %s\n",debugstr_wn(ptr,len));
442 /* scan for special characters... fast exit */
443 if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
444 (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
446 /* not formatted */
447 *data = msi_alloc((len*sizeof(WCHAR)));
448 memcpy(*data,ptr,len*sizeof(WCHAR));
449 TRACE("Returning %s\n",debugstr_wn(*data,len));
450 return len;
453 progress = ptr;
455 while (progress - ptr < len)
457 /* seek out first group if existing */
458 if (find_next_group(progress, len - (progress - ptr), &key,
459 &mark, &mark2))
461 value = deformat_group(package, key, strlenW(key)+1, record,
462 &chunk);
463 msi_free( key );
464 key = NULL;
465 nested = FALSE;
467 /* formatted string located */
468 else if (!find_next_outermost_key(progress, len - (progress - ptr),
469 &key, &mark, &mark2, &nested))
471 LPBYTE nd2;
473 TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
474 size/sizeof(WCHAR)));
475 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
476 TRACE("after chunk is %i + %i\n",size,chunk);
477 if (size)
478 nd2 = msi_realloc(newdata,(size+chunk));
479 else
480 nd2 = msi_alloc(chunk);
482 newdata = nd2;
483 memcpy(&newdata[size],progress,chunk);
484 size+=chunk;
485 break;
488 if (mark != progress)
490 LPBYTE tgt;
491 DWORD old_size = size;
492 INT cnt = (mark - progress);
493 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
494 size += cnt * sizeof(WCHAR);
495 if (!old_size)
496 tgt = msi_alloc(size);
497 else
498 tgt = msi_realloc(newdata,size);
499 newdata = tgt;
500 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
503 progress = mark;
505 if (nested)
507 TRACE("Nested key... %s\n",debugstr_w(key));
508 deformat_string_internal(package, key, &value, strlenW(key)+1,
509 record, failcount);
511 msi_free(key);
512 key = value;
515 TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
516 size/sizeof(WCHAR)),debugstr_w(key));
518 if (!package)
520 /* only deformat number indexs */
521 if (key && is_key_number(key))
523 value = deformat_index(record,key,&chunk);
524 if (!chunk && failcount && *failcount >= 0)
525 (*failcount)++;
527 else
529 if (failcount)
530 *failcount = -1;
531 if(key)
533 DWORD keylen = strlenW(key);
534 chunk = (keylen + 2)*sizeof(WCHAR);
535 value = msi_alloc(chunk);
536 value[0] = '[';
537 memcpy(&value[1],key,keylen*sizeof(WCHAR));
538 value[1+keylen] = ']';
542 else
544 sz = 0;
545 if (key) switch (key[0])
547 case '~':
548 value = deformat_NULL(&chunk);
549 break;
550 case '$':
551 value = deformat_component(package,&key[1],&chunk);
552 break;
553 case '#':
554 value = deformat_file(package,&key[1], &chunk, FALSE);
555 break;
556 case '!': /* should be short path */
557 value = deformat_file(package,&key[1], &chunk, TRUE);
558 break;
559 case '\\':
560 value = deformat_escape(&key[1],&chunk);
561 break;
562 case '%':
563 value = deformat_environment(package,&key[1],&chunk);
564 break;
565 default:
566 /* index keys cannot be nested */
567 if (is_key_number(key))
568 if (!nested)
569 value = deformat_index(record,key,&chunk);
570 else
572 static const WCHAR fmt[] = {'[','%','s',']',0};
573 value = msi_alloc(10);
574 sprintfW(value,fmt,key);
575 chunk = strlenW(value)*sizeof(WCHAR);
577 else
578 value = deformat_property(package,key,&chunk);
579 break;
583 msi_free(key);
585 if (value!=NULL)
587 LPBYTE nd2;
588 TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
589 chunk, size);
590 if (size)
591 nd2= msi_realloc(newdata,(size + chunk));
592 else
593 nd2= msi_alloc(chunk);
594 newdata = nd2;
595 memcpy(&newdata[size],value,chunk);
596 size+=chunk;
597 msi_free(value);
599 else if (failcount && *failcount >=0 )
600 (*failcount)++;
602 progress = mark2+1;
605 TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
606 size/sizeof(WCHAR)));
608 *data = (LPWSTR)newdata;
609 return size / sizeof(WCHAR);
613 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
614 LPDWORD size )
616 LPWSTR deformated;
617 LPWSTR rec;
618 DWORD len;
619 UINT rc = ERROR_INVALID_PARAMETER;
621 TRACE("%p %p %p %i\n", package, record ,buffer, *size);
623 rec = msi_dup_record_field(record,0);
624 if (!rec)
625 rec = build_default_format(record);
627 TRACE("(%s)\n",debugstr_w(rec));
629 len = deformat_string_internal(package,rec,&deformated,
630 rec ? strlenW(rec) : 0, record, NULL);
632 if (buffer)
634 if (*size>len)
636 memcpy(buffer,deformated,len*sizeof(WCHAR));
637 rc = ERROR_SUCCESS;
638 buffer[len] = 0;
640 else
642 if (*size > 0)
644 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
645 buffer[(*size)-1] = 0;
647 rc = ERROR_MORE_DATA;
650 else
651 rc = ERROR_SUCCESS;
653 *size = len;
655 msi_free(rec);
656 msi_free(deformated);
657 return rc;
660 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
661 LPWSTR szResult, LPDWORD sz )
663 UINT r = ERROR_INVALID_HANDLE;
664 MSIPACKAGE *package;
665 MSIRECORD *record;
667 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
669 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
670 if (!package)
672 HRESULT hr;
673 IWineMsiRemotePackage *remote_package;
674 BSTR value = NULL;
675 DWORD len;
676 awstring wstr;
678 remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
679 if (remote_package)
681 len = 0;
682 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
683 NULL, &len );
684 if (FAILED(hr))
685 goto done;
687 len++;
688 value = SysAllocStringLen( NULL, len );
689 if (!value)
691 r = ERROR_OUTOFMEMORY;
692 goto done;
695 hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
696 value, &len );
697 if (FAILED(hr))
698 goto done;
700 wstr.unicode = TRUE;
701 wstr.str.w = szResult;
702 r = msi_strcpy_to_awstring( value, &wstr, sz );
704 done:
705 IWineMsiRemotePackage_Release( remote_package );
706 SysFreeString( value );
708 if (FAILED(hr))
710 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
711 return HRESULT_CODE(hr);
713 return ERROR_FUNCTION_FAILED;
716 return r;
720 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
722 if (!record)
723 return ERROR_INVALID_HANDLE;
724 if (!sz)
726 msiobj_release( &record->hdr );
727 if (szResult)
728 return ERROR_INVALID_PARAMETER;
729 else
730 return ERROR_SUCCESS;
733 r = MSI_FormatRecordW( package, record, szResult, sz );
734 msiobj_release( &record->hdr );
735 if (package)
736 msiobj_release( &package->hdr );
737 return r;
740 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
741 LPSTR szResult, LPDWORD sz )
743 UINT r;
744 DWORD len, save;
745 LPWSTR value;
747 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
749 if (!hRecord)
750 return ERROR_INVALID_HANDLE;
752 if (!sz)
754 if (szResult)
755 return ERROR_INVALID_PARAMETER;
756 else
757 return ERROR_SUCCESS;
760 r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
761 if (r != ERROR_SUCCESS)
762 return r;
764 value = msi_alloc(++len * sizeof(WCHAR));
765 if (!value)
766 return ERROR_OUTOFMEMORY;
768 r = MsiFormatRecordW( hInstall, hRecord, value, &len );
769 if (r != ERROR_SUCCESS)
770 goto done;
772 save = len + 1;
773 len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
774 WideCharToMultiByte(CP_ACP, 0, value, -1, szResult, *sz, NULL, NULL);
776 if (szResult && len > *sz)
778 if (*sz) szResult[*sz - 1] = '\0';
779 r = ERROR_MORE_DATA;
782 *sz = save - 1;
784 done:
785 msi_free(value);
786 return r;