Fix some gcc 4.0 warnings.
[wine] / dlls / msi / helpers.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /*
22  * Here are helper functions formally in action.c that are used by a variaty of
23  * actions and functions.
24  */
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "wine/debug.h"
32 #include "msipriv.h"
33 #include "winuser.h"
34 #include "wine/unicode.h"
35 #include "action.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msi);
38
39 static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
40 static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
41
42 const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
43 const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
44 const WCHAR cszbs[]={'\\',0};
45
46 DWORD build_version_dword(LPCWSTR version_string)
47 {
48     SHORT major,minor;
49     WORD build;
50     DWORD rc = 0x00000000;
51     LPCWSTR ptr1;
52
53     ptr1 = version_string;
54
55     if (!ptr1)
56         return rc;
57     else
58         major = atoiW(ptr1);
59
60
61     if(ptr1)
62         ptr1 = strchrW(ptr1,'.');
63     if (ptr1)
64     {
65         ptr1++;
66         minor = atoiW(ptr1);
67     }
68     else
69         minor = 0;
70
71     if (ptr1)
72         ptr1 = strchrW(ptr1,'.');
73
74     if (ptr1)
75     {
76         ptr1++;
77         build = atoiW(ptr1);
78     }
79     else
80         build = 0;
81
82     rc = MAKELONG(build,MAKEWORD(minor,major));
83     TRACE("%s -> 0x%lx\n",debugstr_w(version_string),rc);
84     return rc;
85 }
86
87 UINT build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name, 
88                             LPWSTR *FilePath)
89 {
90     LPWSTR SystemFolder;
91     LPWSTR dest;
92
93     static const WCHAR szInstaller[] = 
94         {'M','i','c','r','o','s','o','f','t','\\',
95          'I','n','s','t','a','l','l','e','r','\\',0};
96     static const WCHAR szFolder[] =
97         {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
98
99     SystemFolder = load_dynamic_property(package,szFolder,NULL);
100
101     dest = build_directory_name(3, SystemFolder, szInstaller, package->ProductCode);
102
103     create_full_pathW(dest);
104
105     *FilePath = build_directory_name(2, dest, icon_name);
106
107     HeapFree(GetProcessHeap(),0,SystemFolder);
108     HeapFree(GetProcessHeap(),0,dest);
109     return ERROR_SUCCESS;
110 }
111
112 WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
113 {
114     UINT rc;
115     DWORD sz;
116     LPWSTR ret;
117    
118     sz = 0; 
119     if (MSI_RecordIsNull(row,index))
120         return NULL;
121
122     rc = MSI_RecordGetStringW(row,index,NULL,&sz);
123
124     /* having an empty string is different than NULL */
125     if (sz == 0)
126     {
127         ret = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR));
128         ret[0] = 0;
129         return ret;
130     }
131
132     sz ++;
133     ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
134     rc = MSI_RecordGetStringW(row,index,ret,&sz);
135     if (rc!=ERROR_SUCCESS)
136     {
137         ERR("Unable to load dynamic string\n");
138         HeapFree(GetProcessHeap(), 0, ret);
139         ret = NULL;
140     }
141     return ret;
142 }
143
144 LPWSTR load_dynamic_property(MSIPACKAGE *package, LPCWSTR prop, UINT* rc)
145 {
146     DWORD sz = 0;
147     LPWSTR str;
148     UINT r;
149
150     r = MSI_GetPropertyW(package, prop, NULL, &sz);
151     if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
152     {
153         if (rc)
154             *rc = r;
155         return NULL;
156     }
157     sz++;
158     str = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
159     r = MSI_GetPropertyW(package, prop, str, &sz);
160     if (r != ERROR_SUCCESS)
161     {
162         HeapFree(GetProcessHeap(),0,str);
163         str = NULL;
164     }
165     if (rc)
166         *rc = r;
167     return str;
168 }
169
170 MSICOMPONENT* get_loaded_component( MSIPACKAGE* package, LPCWSTR Component )
171 {
172     MSICOMPONENT *comp = NULL;
173
174     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
175     {
176         if (lstrcmpW(Component,comp->Component)==0)
177             return comp;
178     }
179     return NULL;
180 }
181
182 int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
183 {
184     int rc = -1;
185     DWORD i;
186
187     for (i = 0; i < package->loaded_features; i++)
188     {
189         if (strcmpW(Feature,package->features[i].Feature)==0)
190         {
191             rc = i;
192             break;
193         }
194     }
195     return rc;
196 }
197
198 int get_loaded_file(MSIPACKAGE* package, LPCWSTR file)
199 {
200     int rc = -1;
201     DWORD i;
202
203     for (i = 0; i < package->loaded_files; i++)
204     {
205         if (strcmpW(file,package->files[i].File)==0)
206         {
207             rc = i;
208             break;
209         }
210     }
211     return rc;
212 }
213
214 int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
215 {
216     DWORD i;
217     DWORD index;
218
219     if (!package)
220         return -2;
221
222     for (i=0; i < package->loaded_files; i++)
223         if (strcmpW(package->files[i].File,name)==0)
224             return -1;
225
226     index = package->loaded_files;
227     package->loaded_files++;
228     if (package->loaded_files== 1)
229         package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
230     else
231         package->files = HeapReAlloc(GetProcessHeap(),0,
232             package->files , package->loaded_files * sizeof(MSIFILE));
233
234     memset(&package->files[index],0,sizeof(MSIFILE));
235
236     package->files[index].File = strdupW(name);
237     package->files[index].TargetPath = strdupW(path);
238     package->files[index].Temporary = TRUE;
239
240     TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));  
241
242     return 0;
243 }
244
245 LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source, 
246                       BOOL set_prop, MSIFOLDER **folder)
247 {
248     DWORD i;
249     LPWSTR p, path = NULL;
250
251     TRACE("Working to resolve %s\n",debugstr_w(name));
252
253     if (!name)
254         return NULL;
255
256     /* special resolving for Target and Source root dir */
257     if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
258     {
259         if (!source)
260         {
261             LPWSTR check_path;
262             check_path = load_dynamic_property(package,cszTargetDir,NULL);
263             if (!check_path)
264             {
265                 check_path = load_dynamic_property(package,cszRootDrive,NULL);
266                 if (set_prop)
267                     MSI_SetPropertyW(package,cszTargetDir,check_path);
268             }
269
270             /* correct misbuilt target dir */
271             path = build_directory_name(2, check_path, NULL);
272             if (strcmpiW(path,check_path)!=0)
273                 MSI_SetPropertyW(package,cszTargetDir,path);
274
275             if (folder)
276             {
277                 for (i = 0; i < package->loaded_folders; i++)
278                 {
279                     if (strcmpW(package->folders[i].Directory,name)==0)
280                         break;
281                 }
282                 *folder = &(package->folders[i]);
283             }
284             return path;
285         }
286         else
287         {
288             path = load_dynamic_property(package,cszSourceDir,NULL);
289             if (!path)
290             {
291                 path = load_dynamic_property(package,cszDatabase,NULL);
292                 if (path)
293                 {
294                     p = strrchrW(path,'\\');
295                     if (p)
296                         *(p+1) = 0;
297                 }
298             }
299             if (folder)
300             {
301                 for (i = 0; i < package->loaded_folders; i++)
302                 {
303                     if (strcmpW(package->folders[i].Directory,name)==0)
304                         break;
305                 }
306                 *folder = &(package->folders[i]);
307             }
308             return path;
309         }
310     }
311
312     for (i = 0; i < package->loaded_folders; i++)
313     {
314         if (strcmpW(package->folders[i].Directory,name)==0)
315             break;
316     }
317
318     if (i >= package->loaded_folders)
319         return NULL;
320
321     if (folder)
322         *folder = &(package->folders[i]);
323
324     if (!source && package->folders[i].ResolvedTarget)
325     {
326         path = strdupW(package->folders[i].ResolvedTarget);
327         TRACE("   already resolved to %s\n",debugstr_w(path));
328         return path;
329     }
330     else if (source && package->folders[i].ResolvedSource)
331     {
332         path = strdupW(package->folders[i].ResolvedSource);
333         TRACE("   (source)already resolved to %s\n",debugstr_w(path));
334         return path;
335     }
336     else if (!source && package->folders[i].Property)
337     {
338         path = build_directory_name(2, package->folders[i].Property, NULL);
339                     
340         TRACE("   internally set to %s\n",debugstr_w(path));
341         if (set_prop)
342             MSI_SetPropertyW(package,name,path);
343         return path;
344     }
345
346     if (package->folders[i].ParentIndex >= 0)
347     {
348         LPWSTR parent = package->folders[package->folders[i].ParentIndex].Directory;
349
350         TRACE(" ! Parent is %s\n", debugstr_w(parent));
351
352         p = resolve_folder(package, parent, source, set_prop, NULL);
353         if (!source)
354         {
355             TRACE("   TargetDefault = %s\n",
356                     debugstr_w(package->folders[i].TargetDefault));
357
358             path = build_directory_name(3, p, 
359                             package->folders[i].TargetDefault, NULL);
360             package->folders[i].ResolvedTarget = strdupW(path);
361             TRACE("   resolved into %s\n",debugstr_w(path));
362             if (set_prop)
363                 MSI_SetPropertyW(package,name,path);
364         }
365         else 
366         {
367             if (package->folders[i].SourceDefault && 
368                 package->folders[i].SourceDefault[0]!='.')
369                 path = build_directory_name(3, p, package->folders[i].SourceDefault, NULL);
370             else
371                 path = strdupW(p);
372             TRACE("   (source)resolved into %s\n",debugstr_w(path));
373             package->folders[i].ResolvedSource = strdupW(path);
374         }
375         HeapFree(GetProcessHeap(),0,p);
376     }
377     return path;
378 }
379
380 /* wrapper to resist a need for a full rewrite right now */
381 DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
382 {
383     if (ptr)
384     {
385         MSIRECORD *rec = MSI_CreateRecord(1);
386         DWORD size = 0;
387
388         MSI_RecordSetStringW(rec,0,ptr);
389         MSI_FormatRecordW(package,rec,NULL,&size);
390         if (size >= 0)
391         {
392             size++;
393             *data = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
394             if (size > 1)
395                 MSI_FormatRecordW(package,rec,*data,&size);
396             else
397                 *data[0] = 0;
398             msiobj_release( &rec->hdr );
399             return sizeof(WCHAR)*size;
400         }
401         msiobj_release( &rec->hdr );
402     }
403
404     *data = NULL;
405     return 0;
406 }
407
408 UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action)
409 {
410     UINT count;
411     LPWSTR *newbuf = NULL;
412     if (script >= TOTAL_SCRIPTS)
413     {
414         FIXME("Unknown script requested %i\n",script);
415         return ERROR_FUNCTION_FAILED;
416     }
417     TRACE("Scheduling Action %s in script %i\n",debugstr_w(action), script);
418     
419     count = package->script->ActionCount[script];
420     package->script->ActionCount[script]++;
421     if (count != 0)
422         newbuf = HeapReAlloc(GetProcessHeap(),0,
423                         package->script->Actions[script],
424                         package->script->ActionCount[script]* sizeof(LPWSTR));
425     else
426         newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
427
428     newbuf[count] = strdupW(action);
429     package->script->Actions[script] = newbuf;
430
431    return ERROR_SUCCESS;
432 }
433
434 static void remove_tracked_tempfiles(MSIPACKAGE* package)
435 {
436     DWORD i;
437
438     if (!package)
439         return;
440
441     for (i = 0; i < package->loaded_files; i++)
442     {
443         if (package->files[i].Temporary)
444         {
445             TRACE("Cleaning up %s\n",debugstr_w(package->files[i].TargetPath));
446             DeleteFileW(package->files[i].TargetPath);
447         }
448
449     }
450 }
451
452 /* Called when the package is being closed */
453 void ACTION_free_package_structures( MSIPACKAGE* package)
454 {
455     INT i;
456     struct list *item, *cursor;
457     
458     TRACE("Freeing package action data\n");
459
460     remove_tracked_tempfiles(package);
461
462     if (package->features)
463     {
464         LIST_FOR_EACH_SAFE( item, cursor, package->features->Components )
465         {
466             ComponentList *cl = LIST_ENTRY( item, ComponentList, entry );
467             list_remove( &cl->entry );
468             HeapFree(GetProcessHeap(), 0, cl);
469         }
470         HeapFree(GetProcessHeap(),0,package->features->Components);
471         HeapFree(GetProcessHeap(),0,package->features);
472     }
473
474     for (i = 0; i < package->loaded_folders; i++)
475     {
476         HeapFree(GetProcessHeap(),0,package->folders[i].Directory);
477         HeapFree(GetProcessHeap(),0,package->folders[i].TargetDefault);
478         HeapFree(GetProcessHeap(),0,package->folders[i].SourceDefault);
479         HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedTarget);
480         HeapFree(GetProcessHeap(),0,package->folders[i].ResolvedSource);
481         HeapFree(GetProcessHeap(),0,package->folders[i].Property);
482     }
483     if (package->folders && package->loaded_folders > 0)
484         HeapFree(GetProcessHeap(),0,package->folders);
485
486     LIST_FOR_EACH_SAFE( item, cursor, &package->components )
487     {
488         MSICOMPONENT *comp = LIST_ENTRY( item, MSICOMPONENT, entry );
489         
490         list_remove( &comp->entry );
491         HeapFree( GetProcessHeap(), 0, comp->FullKeypath );
492         HeapFree( GetProcessHeap(), 0, comp );
493     }
494
495     for (i = 0; i < package->loaded_files; i++)
496     {
497         HeapFree(GetProcessHeap(),0,package->files[i].File);
498         HeapFree(GetProcessHeap(),0,package->files[i].FileName);
499         HeapFree(GetProcessHeap(),0,package->files[i].ShortName);
500         HeapFree(GetProcessHeap(),0,package->files[i].Version);
501         HeapFree(GetProcessHeap(),0,package->files[i].Language);
502         HeapFree(GetProcessHeap(),0,package->files[i].SourcePath);
503         HeapFree(GetProcessHeap(),0,package->files[i].TargetPath);
504     }
505
506     if (package->files && package->loaded_files > 0)
507         HeapFree(GetProcessHeap(),0,package->files);
508
509     /* clean up extension, progid, class and verb structures */
510     for (i = 0; i < package->loaded_classes; i++)
511     {
512         HeapFree(GetProcessHeap(),0,package->classes[i].Description);
513         HeapFree(GetProcessHeap(),0,package->classes[i].FileTypeMask);
514         HeapFree(GetProcessHeap(),0,package->classes[i].IconPath);
515         HeapFree(GetProcessHeap(),0,package->classes[i].DefInprocHandler);
516         HeapFree(GetProcessHeap(),0,package->classes[i].DefInprocHandler32);
517         HeapFree(GetProcessHeap(),0,package->classes[i].Argument);
518         HeapFree(GetProcessHeap(),0,package->classes[i].ProgIDText);
519     }
520
521     if (package->classes && package->loaded_classes > 0)
522         HeapFree(GetProcessHeap(),0,package->classes);
523
524     for (i = 0; i < package->loaded_extensions; i++)
525     {
526         HeapFree(GetProcessHeap(),0,package->extensions[i].ProgIDText);
527     }
528
529     if (package->extensions && package->loaded_extensions > 0)
530         HeapFree(GetProcessHeap(),0,package->extensions);
531
532     for (i = 0; i < package->loaded_progids; i++)
533     {
534         HeapFree(GetProcessHeap(),0,package->progids[i].ProgID);
535         HeapFree(GetProcessHeap(),0,package->progids[i].Description);
536         HeapFree(GetProcessHeap(),0,package->progids[i].IconPath);
537     }
538
539     if (package->progids && package->loaded_progids > 0)
540         HeapFree(GetProcessHeap(),0,package->progids);
541
542     for (i = 0; i < package->loaded_verbs; i++)
543     {
544         HeapFree(GetProcessHeap(),0,package->verbs[i].Verb);
545         HeapFree(GetProcessHeap(),0,package->verbs[i].Command);
546         HeapFree(GetProcessHeap(),0,package->verbs[i].Argument);
547     }
548
549     if (package->verbs && package->loaded_verbs > 0)
550         HeapFree(GetProcessHeap(),0,package->verbs);
551
552     for (i = 0; i < package->loaded_mimes; i++)
553         HeapFree(GetProcessHeap(),0,package->mimes[i].ContentType);
554
555     if (package->mimes && package->loaded_mimes > 0)
556         HeapFree(GetProcessHeap(),0,package->mimes);
557
558     for (i = 0; i < package->loaded_appids; i++)
559     {
560         HeapFree(GetProcessHeap(),0,package->appids[i].RemoteServerName);
561         HeapFree(GetProcessHeap(),0,package->appids[i].LocalServer);
562         HeapFree(GetProcessHeap(),0,package->appids[i].ServiceParameters);
563         HeapFree(GetProcessHeap(),0,package->appids[i].DllSurrogate);
564     }
565
566     if (package->appids && package->loaded_appids > 0)
567         HeapFree(GetProcessHeap(),0,package->appids);
568
569     if (package->script)
570     {
571         for (i = 0; i < TOTAL_SCRIPTS; i++)
572         {
573             int j;
574             for (j = 0; j < package->script->ActionCount[i]; j++)
575                 HeapFree(GetProcessHeap(),0,package->script->Actions[i][j]);
576         
577             HeapFree(GetProcessHeap(),0,package->script->Actions[i]);
578         }
579
580         for (i = 0; i < package->script->UniqueActionsCount; i++)
581             HeapFree(GetProcessHeap(),0,package->script->UniqueActions[i]);
582
583         HeapFree(GetProcessHeap(),0,package->script->UniqueActions);
584         HeapFree(GetProcessHeap(),0,package->script);
585     }
586
587     HeapFree(GetProcessHeap(),0,package->PackagePath);
588     HeapFree(GetProcessHeap(),0,package->msiFilePath);
589     HeapFree(GetProcessHeap(),0,package->ProductCode);
590
591     /* cleanup control event subscriptions */
592     ControlEvent_CleanupSubscriptions(package);
593 }
594
595 /*
596  *  build_directory_name()
597  *
598  *  This function is to save messing round with directory names
599  *  It handles adding backslashes between path segments, 
600  *   and can add \ at the end of the directory name if told to.
601  *
602  *  It takes a variable number of arguments.
603  *  It always allocates a new string for the result, so make sure
604  *   to free the return value when finished with it.
605  *
606  *  The first arg is the number of path segments that follow.
607  *  The arguments following count are a list of path segments.
608  *  A path segment may be NULL.
609  *
610  *  Path segments will be added with a \ separating them.
611  *  A \ will not be added after the last segment, however if the
612  *    last segment is NULL, then the last character will be a \
613  * 
614  */
615 LPWSTR build_directory_name(DWORD count, ...)
616 {
617     DWORD sz = 1, i;
618     LPWSTR dir;
619     va_list va;
620
621     va_start(va,count);
622     for(i=0; i<count; i++)
623     {
624         LPCWSTR str = va_arg(va,LPCWSTR);
625         if (str)
626             sz += strlenW(str) + 1;
627     }
628     va_end(va);
629
630     dir = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
631     dir[0]=0;
632
633     va_start(va,count);
634     for(i=0; i<count; i++)
635     {
636         LPCWSTR str = va_arg(va,LPCWSTR);
637         if (!str)
638             continue;
639         strcatW(dir, str);
640         if( ((i+1)!=count) && dir[strlenW(dir)-1]!='\\')
641             strcatW(dir, cszbs);
642     }
643     return dir;
644 }
645
646 /***********************************************************************
647  *            create_full_pathW
648  *
649  * Recursively create all directories in the path.
650  *
651  * shamelessly stolen from setupapi/queue.c
652  */
653 BOOL create_full_pathW(const WCHAR *path)
654 {
655     BOOL ret = TRUE;
656     int len;
657     WCHAR *new_path;
658
659     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
660                                               sizeof(WCHAR));
661
662     strcpyW(new_path, path);
663
664     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
665     new_path[len - 1] = 0;
666
667     while(!CreateDirectoryW(new_path, NULL))
668     {
669         WCHAR *slash;
670         DWORD last_error = GetLastError();
671         if(last_error == ERROR_ALREADY_EXISTS)
672             break;
673
674         if(last_error != ERROR_PATH_NOT_FOUND)
675         {
676             ret = FALSE;
677             break;
678         }
679
680         if(!(slash = strrchrW(new_path, '\\')))
681         {
682             ret = FALSE;
683             break;
684         }
685
686         len = slash - new_path;
687         new_path[len] = 0;
688         if(!create_full_pathW(new_path))
689         {
690             ret = FALSE;
691             break;
692         }
693         new_path[len] = '\\';
694     }
695
696     HeapFree(GetProcessHeap(), 0, new_path);
697     return ret;
698 }
699
700 void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
701 {
702     MSIRECORD * row;
703
704     row = MSI_CreateRecord(4);
705     MSI_RecordSetInteger(row,1,a);
706     MSI_RecordSetInteger(row,2,b);
707     MSI_RecordSetInteger(row,3,c);
708     MSI_RecordSetInteger(row,4,d);
709     MSI_ProcessMessage(package, INSTALLMESSAGE_PROGRESS, row);
710     msiobj_release(&row->hdr);
711
712     msi_dialog_check_messages(NULL);
713 }
714
715 void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
716 {
717     static const WCHAR Query_t[] = 
718         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
719          '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
720          'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=', 
721          ' ','\'','%','s','\'',0};
722     WCHAR message[1024];
723     MSIRECORD * row = 0;
724     DWORD size;
725     static const WCHAR szActionData[] = 
726         {'A','c','t','i','o','n','D','a','t','a',0};
727
728     if (!package->LastAction || strcmpW(package->LastAction,action))
729     {
730         row = MSI_QueryGetRecord(package->db, Query_t, action);
731         if (!row)
732             return;
733
734         if (MSI_RecordIsNull(row,3))
735         {
736             msiobj_release(&row->hdr);
737             return;
738         }
739
740         /* update the cached actionformat */
741         HeapFree(GetProcessHeap(),0,package->ActionFormat);
742         package->ActionFormat = load_dynamic_stringW(row,3);
743
744         HeapFree(GetProcessHeap(),0,package->LastAction);
745         package->LastAction = strdupW(action);
746
747         msiobj_release(&row->hdr);
748     }
749
750     MSI_RecordSetStringW(record,0,package->ActionFormat);
751     size = 1024;
752     MSI_FormatRecordW(package,record,message,&size);
753
754     row = MSI_CreateRecord(1);
755     MSI_RecordSetStringW(row,1,message);
756  
757     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
758
759     ControlEvent_FireSubscribedEvent(package,szActionData, row);
760
761     msiobj_release(&row->hdr);
762 }
763
764 BOOL ACTION_VerifyComponentForAction(MSIPACKAGE* package, MSICOMPONENT* comp,
765                                             INSTALLSTATE check )
766 {
767     if (comp->Installed == check)
768         return FALSE;
769
770     if (comp->ActionRequest == check)
771         return TRUE;
772     else
773         return FALSE;
774 }
775
776 BOOL ACTION_VerifyFeatureForAction(MSIPACKAGE* package, INT index, 
777                                             INSTALLSTATE check )
778 {
779     if (package->features[index].Installed == check)
780         return FALSE;
781
782     if (package->features[index].ActionRequest == check)
783         return TRUE;
784     else
785         return FALSE;
786 }
787
788 void reduce_to_longfilename(WCHAR* filename)
789 {
790     LPWSTR p = strchrW(filename,'|');
791     if (p)
792         memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
793 }
794
795 void reduce_to_shortfilename(WCHAR* filename)
796 {
797     LPWSTR p = strchrW(filename,'|');
798     if (p)
799         *p = 0;
800 }
801
802 LPWSTR create_component_advertise_string(MSIPACKAGE* package, 
803                 MSICOMPONENT* component, LPCWSTR feature)
804 {
805     GUID clsid;
806     WCHAR productid_85[21];
807     WCHAR component_85[21];
808     /*
809      * I have a fair bit of confusion as to when a < is used and when a > is
810      * used. I do not think i have it right...
811      *
812      * Ok it appears that the > is used if there is a guid for the compoenent
813      * and the < is used if not.
814      */
815     static WCHAR fmt1[] = {'%','s','%','s','<',0,0};
816     static WCHAR fmt2[] = {'%','s','%','s','>','%','s',0,0};
817     LPWSTR output = NULL;
818     DWORD sz = 0;
819
820     memset(productid_85,0,sizeof(productid_85));
821     memset(component_85,0,sizeof(component_85));
822
823     CLSIDFromString(package->ProductCode, &clsid);
824     
825     encode_base85_guid(&clsid,productid_85);
826
827     CLSIDFromString(component->ComponentId, &clsid);
828     encode_base85_guid(&clsid,component_85);
829
830     TRACE("Doing something with this... %s %s %s\n", 
831             debugstr_w(productid_85), debugstr_w(feature),
832             debugstr_w(component_85));
833  
834     sz = lstrlenW(productid_85) + lstrlenW(feature);
835     if (component)
836         sz += lstrlenW(component_85);
837
838     sz+=3;
839     sz *= sizeof(WCHAR);
840            
841     output = HeapAlloc(GetProcessHeap(),0,sz);
842     memset(output,0,sz);
843
844     if (component)
845         sprintfW(output,fmt2,productid_85,feature,component_85);
846     else
847         sprintfW(output,fmt1,productid_85,feature);
848     
849     return output;
850 }
851
852 /* update compoennt state based on a feature change */
853 void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature)
854 {
855     int i;
856     INSTALLSTATE newstate;
857     MSIFEATURE *feature;
858     ComponentList *cl;
859
860     i = get_loaded_feature(package,szFeature);
861     if (i < 0)
862         return;
863
864     feature = &package->features[i];
865     newstate = feature->ActionRequest;
866
867     LIST_FOR_EACH_ENTRY( cl, feature->Components, ComponentList, entry )
868     {
869         MSICOMPONENT* component = cl->component;
870     
871         TRACE("MODIFYING(%i): Component %s (Installed %i, Action %i, Request %i)\n",
872             newstate, debugstr_w(component->Component), component->Installed, 
873             component->Action, component->ActionRequest);
874         
875         if (!component->Enabled)
876             continue;
877         else
878         {
879             if (newstate == INSTALLSTATE_LOCAL)
880             {
881                 component->ActionRequest = INSTALLSTATE_LOCAL;
882                 component->Action = INSTALLSTATE_LOCAL;
883             }
884             else 
885             {
886                 int j;
887                 ComponentList *clist;
888
889                 component->ActionRequest = newstate;
890                 component->Action = newstate;
891
892                 /*if any other feature wants is local we need to set it local*/
893                 for (j = 0; 
894                      j < package->loaded_features &&
895                      component->ActionRequest != INSTALLSTATE_LOCAL; 
896                      j++)
897                 {
898                     LIST_FOR_EACH_ENTRY( clist, package->features[j].Components,
899                                          ComponentList, entry )
900                     {
901                         if ( clist->component == cl->component )
902                         {
903                             if (package->features[j].ActionRequest == 
904                                 INSTALLSTATE_LOCAL)
905                             {
906                                 TRACE("Saved by %s\n", debugstr_w(package->features[j].Feature));
907                                 component->ActionRequest = INSTALLSTATE_LOCAL;
908                                 component->Action = INSTALLSTATE_LOCAL;
909                             }
910                             break;
911                         }
912                     }
913                 }
914             }
915         }
916         TRACE("Result (%i): Component %s (Installed %i, Action %i, Request %i)\n",
917             newstate, debugstr_w(component->Component), component->Installed, 
918             component->Action, component->ActionRequest);
919     } 
920 }
921
922 UINT register_unique_action(MSIPACKAGE *package, LPCWSTR action)
923 {
924     UINT count;
925     LPWSTR *newbuf = NULL;
926
927     if (!package || !package->script)
928         return FALSE;
929
930     TRACE("Registering Action %s as having fun\n",debugstr_w(action));
931     
932     count = package->script->UniqueActionsCount;
933     package->script->UniqueActionsCount++;
934     if (count != 0)
935         newbuf = HeapReAlloc(GetProcessHeap(),0,
936                         package->script->UniqueActions,
937                         package->script->UniqueActionsCount* sizeof(LPWSTR));
938     else
939         newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
940
941     newbuf[count] = strdupW(action);
942     package->script->UniqueActions = newbuf;
943
944    return ERROR_SUCCESS;
945 }
946
947 BOOL check_unique_action(MSIPACKAGE *package, LPCWSTR action)
948 {
949     INT i;
950
951     if (!package || !package->script)
952         return FALSE;
953
954     for (i = 0; i < package->script->UniqueActionsCount; i++)
955         if (!strcmpW(package->script->UniqueActions[i],action))
956             return TRUE;
957
958     return FALSE;
959 }
960
961 WCHAR* generate_error_string(MSIPACKAGE *package, UINT error, DWORD count, ... )
962 {
963     static const WCHAR query[] = {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ','F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ','%','i',0};
964
965     MSIRECORD *rec;
966     MSIRECORD *row;
967     DWORD size = 0;
968     DWORD i;
969     va_list va;
970     LPCWSTR str;
971     LPWSTR data;
972
973     row = MSI_QueryGetRecord(package->db, query, error);
974     if (!row)
975         return 0;
976
977     rec = MSI_CreateRecord(count+2);
978
979     str = MSI_RecordGetString(row,1);
980     MSI_RecordSetStringW(rec,0,str);
981     msiobj_release( &row->hdr );
982     MSI_RecordSetInteger(rec,1,error);
983
984     va_start(va,count);
985     for (i = 0; i < count; i++)
986     {
987         str = va_arg(va,LPCWSTR);
988         MSI_RecordSetStringW(rec,(i+2),str);
989     }
990     va_end(va);
991
992     MSI_FormatRecordW(package,rec,NULL,&size);
993     if (size >= 0)
994     {
995         size++;
996         data = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
997         if (size > 1)
998             MSI_FormatRecordW(package,rec,data,&size);
999         else
1000             data[0] = 0;
1001         msiobj_release( &rec->hdr );
1002         return data;
1003     }
1004
1005     msiobj_release( &rec->hdr );
1006     data = NULL;
1007     return data;
1008 }