Merge 14551:14980 from trunk
[reactos.git] / reactos / lib / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /*
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
24 */
25
26 #include <stdarg.h>
27 #include <stdio.h>
28
29 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34 #include "winreg.h"
35 #include "wine/debug.h"
36 #include "fdi.h"
37 #include "msi.h"
38 #include "msiquery.h"
39 #include "fcntl.h"
40 #include "objbase.h"
41 #include "objidl.h"
42 #include "msipriv.h"
43 #include "winnls.h"
44 #include "winuser.h"
45 #include "shlobj.h"
46 #include "wine/unicode.h"
47 #include "winver.h"
48 #include "action.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52
53 LPWSTR build_default_format(MSIRECORD* record)
54 {
55 int i;
56 int count;
57 LPWSTR rc;
58 static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
59 WCHAR buf[11];
60
61 count = MSI_RecordGetFieldCount(record);
62
63 rc = HeapAlloc(GetProcessHeap(),0,(11*count)*sizeof(WCHAR));
64 rc[0] = 0;
65 for (i = 1; i <= count; i++)
66 {
67 sprintfW(buf,fmt,i,i);
68 strcatW(rc,buf);
69 }
70 return rc;
71 }
72
73 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
74 {
75 DWORD i;
76 for (i = 0; i < len; i++)
77 if (buf[i] == token)
78 return &buf[i];
79 return NULL;
80 }
81
82 /* break out helper functions for deformating */
83 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
84 {
85 LPWSTR value = NULL;
86 INT index;
87
88 *sz = 0;
89 if (!package)
90 return NULL;
91
92 ERR("POORLY HANDLED DEFORMAT.. [$componentkey] \n");
93 index = get_loaded_component(package,key);
94 if (index >= 0)
95 {
96 value = resolve_folder(package, package->components[index].Directory,
97 FALSE, FALSE, NULL);
98 *sz = (strlenW(value)) * sizeof(WCHAR);
99 }
100
101 return value;
102 }
103
104 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
105 {
106 LPWSTR value = NULL;
107 INT index;
108
109 *sz = 0;
110
111 if (!package)
112 return NULL;
113
114 index = get_loaded_file(package,key);
115 if (index >=0)
116 {
117 value = strdupW(package->files[index].TargetPath);
118 *sz = (strlenW(value)) * sizeof(WCHAR);
119 }
120
121 return value;
122 }
123
124 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
125 DWORD* chunk)
126 {
127 LPWSTR value = NULL;
128 DWORD sz;
129
130 sz = GetEnvironmentVariableW(key,NULL,0);
131 if (sz > 0)
132 {
133 sz++;
134 value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
135 GetEnvironmentVariableW(&key[1],value,sz);
136 *chunk = (strlenW(value)) * sizeof(WCHAR);
137 }
138 else
139 {
140 ERR("Unknown environment variable %s\n", debugstr_w(key));
141 *chunk = 0;
142 value = NULL;
143 }
144 return value;
145 }
146
147
148 static LPWSTR deformat_NULL(DWORD* chunk)
149 {
150 LPWSTR value;
151
152 value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
153 value[0] = 0;
154 *chunk = sizeof(WCHAR);
155 return value;
156 }
157
158 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
159 {
160 LPWSTR value;
161
162 value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
163 value[0] = key[0];
164 *chunk = sizeof(WCHAR);
165
166 return value;
167 }
168
169
170 static BOOL is_key_number(LPCWSTR key)
171 {
172 INT index = 0;
173 if (key[0] == 0)
174 return FALSE;
175
176 while (isdigitW(key[index])) index++;
177 if (key[index] == 0)
178 return TRUE;
179 else
180 return FALSE;
181 }
182
183 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
184 {
185 INT index;
186 LPWSTR value;
187
188 index = atoiW(key);
189 TRACE("record index %i\n",index);
190 value = load_dynamic_stringW(record,index);
191 if (value)
192 *chunk = strlenW(value) * sizeof(WCHAR);
193 else
194 {
195 value = NULL;
196 *chunk = 0;
197 }
198 return value;
199 }
200
201 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
202 {
203 UINT rc;
204 LPWSTR value;
205
206 if (!package)
207 return NULL;
208
209 value = load_dynamic_property(package,key, &rc);
210
211 if (rc == ERROR_SUCCESS)
212 *chunk = (strlenW(value)) * sizeof(WCHAR);
213
214 return value;
215 }
216
217 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
218 LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
219 BOOL *nested)
220 {
221 INT count = 0;
222 INT total_count = 0;
223 int i;
224
225 *mark = scanW(source,'[',len_remaining);
226 if (!*mark)
227 return FALSE;
228
229 count = 1;
230 total_count = 1;
231 *nested = FALSE;
232 for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
233 {
234 if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
235 {
236 count ++;
237 total_count ++;
238 *nested = TRUE;
239 }
240 else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
241 {
242 count --;
243 }
244 }
245
246 if (count > 0)
247 return FALSE;
248
249 *mark2 = &(*mark)[i-1];
250
251 i = *mark2 - *mark;
252 *key = HeapAlloc(GetProcessHeap(),0,i*sizeof(WCHAR));
253 /* do not have the [] in the key */
254 i -= 1;
255 memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
256 (*key)[i] = 0;
257
258 TRACE("Found Key %s\n",debugstr_w(*key));
259 return TRUE;
260 }
261
262
263 /*
264 * len is in WCHARs
265 * return is also in WCHARs
266 */
267 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
268 WCHAR** data, DWORD len, MSIRECORD* record)
269 {
270 LPCWSTR mark = NULL;
271 LPCWSTR mark2 = NULL;
272 DWORD size=0;
273 DWORD chunk=0;
274 LPWSTR key;
275 LPWSTR value = NULL;
276 DWORD sz;
277 LPBYTE newdata = NULL;
278 const WCHAR* progress = NULL;
279 BOOL nested;
280
281 if (ptr==NULL)
282 {
283 TRACE("Deformatting NULL string\n");
284 *data = NULL;
285 return 0;
286 }
287
288 TRACE("Starting with %s\n",debugstr_w(ptr));
289
290 /* scan for special characters... fast exit */
291 if (!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len)))
292 {
293 /* not formatted */
294 *data = HeapAlloc(GetProcessHeap(),0,(len*sizeof(WCHAR)));
295 memcpy(*data,ptr,len*sizeof(WCHAR));
296 TRACE("Returning %s\n",debugstr_w(*data));
297 return len;
298 }
299
300 progress = ptr;
301
302 while (progress - ptr < len)
303 {
304 /* formatted string located */
305 if (!find_next_outermost_key(progress, len - (progress - ptr), &key,
306 &mark, &mark2, &nested))
307 {
308 LPBYTE nd2;
309
310 TRACE("after value %s .. %s\n",debugstr_w((LPWSTR)newdata),
311 debugstr_w(mark));
312 chunk = (len - (progress - ptr)) * sizeof(WCHAR);
313 TRACE("after chunk is %li + %li\n",size,chunk);
314 if (size)
315 nd2 = HeapReAlloc(GetProcessHeap(),0,newdata,(size+chunk));
316 else
317 nd2 = HeapAlloc(GetProcessHeap(),0,chunk);
318
319 newdata = nd2;
320 memcpy(&newdata[size],progress,chunk);
321 size+=chunk;
322 break;
323 }
324
325 if (mark != progress)
326 {
327 LPBYTE tgt;
328 DWORD old_size = size;
329 INT cnt = (mark - progress);
330 TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
331 size += cnt * sizeof(WCHAR);
332 if (!old_size)
333 tgt = HeapAlloc(GetProcessHeap(),0,size);
334 else
335 tgt = HeapReAlloc(GetProcessHeap(),0,newdata,size);
336 newdata = tgt;
337 memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
338 }
339
340 progress = mark;
341
342 if (nested)
343 {
344 TRACE("Nested key... %s\n",debugstr_w(key));
345 deformat_string_internal(package, key, &value, strlenW(key)+1,
346 record);
347
348 HeapFree(GetProcessHeap(),0,key);
349 key = value;
350 }
351
352 TRACE("Current %s .. %s\n",debugstr_w((LPWSTR)newdata),debugstr_w(key));
353
354 if (!package)
355 {
356 /* only deformat number indexs */
357 if (is_key_number(key))
358 value = deformat_index(record,key,&chunk);
359 else
360 {
361 DWORD keylen = strlenW(key);
362 chunk = (keylen + 2)*sizeof(WCHAR);
363 value = HeapAlloc(GetProcessHeap(),0,chunk);
364 value[0] = '[';
365 memcpy(&value[1],key,keylen*sizeof(WCHAR));
366 value[1+keylen] = ']';
367 }
368 }
369 else
370 {
371 sz = 0;
372 switch (key[0])
373 {
374 case '~':
375 value = deformat_NULL(&chunk);
376 break;
377 case '$':
378 value = deformat_component(package,&key[1],&chunk);
379 break;
380 case '#':
381 case '!': /* should be short path */
382 value = deformat_file(package,&key[1], &chunk);
383 break;
384 case '\\':
385 value = deformat_escape(&key[1],&chunk);
386 break;
387 case '%':
388 value = deformat_environment(package,&key[1],&chunk);
389 break;
390 default:
391 if (is_key_number(key))
392 value = deformat_index(record,key,&chunk);
393 else
394 value = deformat_property(package,key,&chunk);
395 break;
396 }
397 }
398
399 HeapFree(GetProcessHeap(),0,key);
400
401 if (value!=NULL)
402 {
403 LPBYTE nd2;
404 TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
405 chunk, size);
406 if (size)
407 nd2= HeapReAlloc(GetProcessHeap(),0,newdata,(size + chunk));
408 else
409 nd2= HeapAlloc(GetProcessHeap(),0,chunk);
410 newdata = nd2;
411 memcpy(&newdata[size],value,chunk);
412 size+=chunk;
413 HeapFree(GetProcessHeap(),0,value);
414 }
415
416 progress = mark2+1;
417 }
418
419 TRACE("after everything %s\n",debugstr_w((LPWSTR)newdata));
420
421 *data = (LPWSTR)newdata;
422 return size / sizeof(WCHAR);
423 }
424
425
426 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
427 DWORD *size )
428 {
429 LPWSTR deformated;
430 LPWSTR rec;
431 DWORD len;
432 UINT rc = ERROR_INVALID_PARAMETER;
433
434 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
435
436 rec = load_dynamic_stringW(record,0);
437 if (!rec)
438 rec = build_default_format(record);
439
440 TRACE("(%s)\n",debugstr_w(rec));
441
442 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
443 record);
444
445 if (buffer)
446 {
447 if (*size>len)
448 {
449 memcpy(buffer,deformated,len*sizeof(WCHAR));
450 rc = ERROR_SUCCESS;
451 buffer[len] = 0;
452 }
453 else
454 {
455 if (*size > 0)
456 {
457 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
458 buffer[(*size)-1] = 0;
459 }
460 rc = ERROR_MORE_DATA;
461 }
462 }
463 else
464 rc = ERROR_SUCCESS;
465
466 *size = len;
467
468 HeapFree(GetProcessHeap(),0,rec);
469 HeapFree(GetProcessHeap(),0,deformated);
470 return rc;
471 }
472
473 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
474 DWORD *size )
475 {
476 LPWSTR deformated;
477 LPWSTR rec;
478 DWORD len,lenA;
479 UINT rc = ERROR_INVALID_PARAMETER;
480
481 TRACE("%p %p %p %li\n",package, record ,buffer, *size);
482
483 rec = load_dynamic_stringW(record,0);
484 if (!rec)
485 rec = build_default_format(record);
486
487 TRACE("(%s)\n",debugstr_w(rec));
488
489 len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
490 record);
491 lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
492
493 if (buffer)
494 {
495 WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
496 if (*size>lenA)
497 {
498 rc = ERROR_SUCCESS;
499 buffer[lenA] = 0;
500 }
501 else
502 {
503 rc = ERROR_MORE_DATA;
504 buffer[(*size)-1] = 0;
505 }
506 }
507 else
508 rc = ERROR_SUCCESS;
509
510 *size = lenA;
511
512 HeapFree(GetProcessHeap(),0,rec);
513 HeapFree(GetProcessHeap(),0,deformated);
514 return rc;
515 }
516
517
518 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
519 LPWSTR szResult, DWORD *sz )
520 {
521 UINT r = ERROR_INVALID_HANDLE;
522 MSIPACKAGE *package;
523 MSIRECORD *record;
524
525 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
526
527 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
528
529 if (!record)
530 return ERROR_INVALID_HANDLE;
531 if (!sz)
532 {
533 msiobj_release( &record->hdr );
534 if (szResult)
535 return ERROR_INVALID_PARAMETER;
536 else
537 return ERROR_SUCCESS;
538 }
539
540 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
541
542 r = MSI_FormatRecordW( package, record, szResult, sz );
543 msiobj_release( &record->hdr );
544 if (package)
545 msiobj_release( &package->hdr );
546 return r;
547 }
548
549 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
550 LPSTR szResult, DWORD *sz )
551 {
552 UINT r = ERROR_INVALID_HANDLE;
553 MSIPACKAGE *package = NULL;
554 MSIRECORD *record = NULL;
555
556 TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
557
558 record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
559
560 if (!record)
561 return ERROR_INVALID_HANDLE;
562 if (!sz)
563 {
564 msiobj_release( &record->hdr );
565 if (szResult)
566 return ERROR_INVALID_PARAMETER;
567 else
568 return ERROR_SUCCESS;
569 }
570
571 package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
572
573 r = MSI_FormatRecordA( package, record, szResult, sz );
574 msiobj_release( &record->hdr );
575 if (package)
576 msiobj_release( &package->hdr );
577 return r;
578 }