Fix some declaration and write string warnings.
[wine] / programs / msiexec / msiexec.c
1 /*
2  * msiexec.exe implementation
3  *
4  * Copyright 2004 Vincent Béron
5  * Copyright 2005 Mike McCormack
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 #include <windows.h>
23 #include <msi.h>
24 #include <objbase.h>
25 #include <stdio.h>
26
27 #include "msiexec.h"
28
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
33
34 struct string_list
35 {
36         struct string_list *next;
37         WCHAR str[1];
38 };
39
40 static const char UsageStr[] =
41 "Usage:\n"
42 "  Install a product:\n"
43 "    msiexec {package|productcode} [property]\n"
44 "    msiexec /i {package|productcode} [property]\n"
45 "    msiexec /a package [property]\n"
46 "  Repair an installation:\n"
47 "    msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
48 "  Uninstall a product:\n"
49 "    msiexec /x {package|productcode} [property]\n"
50 "  Advertise a product:\n"
51 "    msiexec /j[u|m] package [/t transform] [/g languageid]\n"
52 "    msiexec {u|m} package [/t transform] [/g languageid]\n"
53 "  Apply a patch:\n"
54 "    msiexec /p patchpackage [property]\n"
55 "    msiexec /p patchpackage /a package [property]\n"
56 "  Modifiers for above operations:\n"
57 "    msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
58 "    msiexec /q{|n|b|r|f|n+|b+|b-}\n"
59 "  Register a module:\n"
60 "    msiexec /y module\n"
61 "  Unregister a module:\n"
62 "    msiexec /z module\n"
63 "  Display usage and copyright:\n"
64 "    msiexec {/h|/?}\n"
65 "NOTE: Product code on commandline unimplemented as of yet\n"
66 "\n"
67 "Copyright 2004 Vincent Béron\n";
68
69 static const WCHAR ActionAdmin[] = {
70    'A','C','T','I','O','N','=','A','D','M','I','N',0 };
71 static const WCHAR RemoveAll[] = {
72    'R','E','M','O','V','E','=','A','L','L',0 };
73
74 static const WCHAR InstallRunOnce[] = {
75    'S','o','f','t','w','a','r','e','\\',
76    'M','i','c','r','o','s','o','f','t','\\',
77    'W','i','n','d','o','w','s','\\',
78    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
79    'I','n','s','t','a','l','l','e','r','\\',
80    'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
81
82 static void ShowUsage(int ExitCode)
83 {
84         printf(UsageStr);
85         ExitProcess(ExitCode);
86 }
87
88 static BOOL IsProductCode(LPWSTR str)
89 {
90         GUID ProductCode;
91
92         if(lstrlenW(str) != 38)
93                 return FALSE;
94         return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
95
96 }
97
98 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
99 {
100         struct string_list *entry;
101         DWORD size;
102
103         size = sizeof *entry + lstrlenW(str) * sizeof (WCHAR);
104         entry = HeapAlloc(GetProcessHeap(), 0, size);
105         if(!entry)
106         {
107                 WINE_ERR("Out of memory!\n");
108                 ExitProcess(1);
109         }
110         lstrcpyW(entry->str, str);
111         entry->next = NULL;
112
113         /*
114          * Ignoring o(n^2) time complexity to add n strings for simplicity,
115          *  add the string to the end of the list to preserve the order.
116          */
117         while( *list )
118                 list = &(*list)->next;
119         *list = entry;
120 }
121
122 static LPWSTR build_properties(struct string_list *property_list)
123 {
124         struct string_list *list;
125         LPWSTR ret, p, value;
126         DWORD len;
127         BOOL needs_quote;
128
129         if(!property_list)
130                 return NULL;
131
132         /* count the space we need */
133         len = 1;
134         for(list = property_list; list; list = list->next)
135                 len += lstrlenW(list->str) + 3;
136
137         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
138
139         /* add a space before each string, and quote the value */
140         p = ret;
141         for(list = property_list; list; list = list->next)
142         {
143                 value = strchrW(list->str,'=');
144                 if(!value)
145                         continue;
146                 len = value - list->str;
147                 *p++ = ' ';
148                 memcpy(p, list->str, len * sizeof(WCHAR));
149                 p += len;
150                 *p++ = '=';
151
152                 /* check if the value contains spaces and maybe quote it */
153                 value++;
154                 needs_quote = strchrW(value,' ') ? 1 : 0;
155                 if(needs_quote)
156                         *p++ = '"';
157                 len = lstrlenW(value);
158                 memcpy(p, value, len * sizeof(WCHAR));
159                 p += len;
160                 if(needs_quote)
161                         *p++ = '"';
162         }
163         *p = 0;
164
165         WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
166
167         return ret;
168 }
169
170 static LPWSTR build_transforms(struct string_list *transform_list)
171 {
172         struct string_list *list;
173         LPWSTR ret, p;
174         DWORD len;
175
176         /* count the space we need */
177         len = 1;
178         for(list = transform_list; list; list = list->next)
179                 len += lstrlenW(list->str) + 1;
180
181         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
182
183         /* add all the transforms with a semicolon between each one */
184         p = ret;
185         for(list = transform_list; list; list = list->next)
186         {
187                 len = lstrlenW(list->str);
188                 lstrcpynW(p, list->str, len );
189                 p += len;
190                 if(list->next)
191                         *p++ = ';';
192         }
193         *p = 0;
194
195         return ret;
196 }
197
198 static DWORD msi_atou(LPCWSTR str)
199 {
200         DWORD ret = 0;
201         while(*str >= '0' && *str <= '9')
202         {
203                 ret *= 10;
204                 ret += (*str - '0');
205                 str++;
206         }
207         return 0;
208 }
209
210 static LPWSTR msi_strdup(LPCWSTR str)
211 {
212         DWORD len = lstrlenW(str)+1;
213         LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
214         lstrcpyW(ret, str);
215         return ret;
216 }
217
218 /* str1 is the same as str2, ignoring case */
219 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
220 {
221         DWORD len, ret;
222         LPWSTR strW;
223
224         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
225         if( !len )
226                 return TRUE;
227         if( lstrlenW(str1) != (len-1) )
228                 return TRUE;
229         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
230         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
231         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
232         HeapFree(GetProcessHeap(), 0, strW);
233         return (ret != CSTR_EQUAL);
234 }
235
236 /* str2 is at the beginning of str1, ignoring case */
237 static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
238 {
239         DWORD len, ret;
240         LPWSTR strW;
241
242         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
243         if( !len )
244                 return TRUE;
245         if( lstrlenW(str1) < (len-1) )
246                 return TRUE;
247         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
248         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
249         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
250         HeapFree(GetProcessHeap(), 0, strW);
251         return (ret != CSTR_EQUAL);
252 }
253
254 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
255 {
256         VOID* (*proc)(void);
257
258         *DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
259         if(!*DllHandle)
260         {
261                 fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
262                 ExitProcess(1);
263         }
264         proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
265         if(!proc)
266         {
267                 fprintf(stderr, "Dll %s does not implement function %s\n",
268                         wine_dbgstr_w(DllName), ProcName);
269                 FreeLibrary(*DllHandle);
270                 ExitProcess(1);
271         }
272
273         return proc;
274 }
275
276 static DWORD DoDllRegisterServer(LPCWSTR DllName)
277 {
278         HRESULT hr;
279         DLLREGISTERSERVER pfDllRegisterServer = NULL;
280         HMODULE DllHandle = NULL;
281
282         pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
283
284         hr = pfDllRegisterServer();
285         if(FAILED(hr))
286         {
287                 fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
288                 return 1;
289         }
290         printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
291         if(DllHandle)
292                 FreeLibrary(DllHandle);
293         return 0;
294 }
295
296 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
297 {
298         HRESULT hr;
299         DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
300         HMODULE DllHandle = NULL;
301
302         pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
303
304         hr = pfDllUnregisterServer();
305         if(FAILED(hr))
306         {
307                 fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
308                 return 1;
309         }
310         printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
311         if(DllHandle)
312                 FreeLibrary(DllHandle);
313         return 0;
314 }
315
316 /*
317  * state machine to break up the command line properly
318  */
319
320 enum chomp_state
321 {
322         cs_whitespace,
323         cs_token,
324         cs_quote
325 };
326
327 static int chomp( WCHAR *str )
328 {
329         enum chomp_state state = cs_whitespace;
330         WCHAR *p, *out;
331         int count = 0, ignore;
332
333         for( p = str, out = str; *p; p++ )
334         {
335                 ignore = 1;
336                 switch( state )
337                 {
338                 case cs_whitespace:
339                         switch( *p )
340                         {
341                         case ' ':
342                                 break;
343                         case '"':
344                                 state = cs_quote;
345                                 count++;
346                                 break;
347                         default:
348                                 count++;
349                                 ignore = 0;
350                                 state = cs_token;
351                         }
352                         break;
353
354                 case cs_token:
355                         switch( *p )
356                         {
357                         case '"':
358                                 state = cs_quote;
359                                 break;
360                         case ' ':
361                                 state = cs_whitespace;
362                                 *out++ = 0;
363                                 break;
364                         default:
365                                 ignore = 0;
366                         }
367                         break;
368
369                 case cs_quote:
370                         switch( *p )
371                         {
372                         case '"':
373                                 state = cs_token;
374                                 break;
375                         default:
376                                 ignore = 0;
377                         }
378                         break;
379                 }
380                 if( !ignore )
381                         *out++ = *p;
382         }
383
384         *out = 0;
385
386         return count;
387 }
388
389 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
390 {
391         WCHAR **argv, *p = msi_strdup(cmdline);
392         int i, n;
393
394         n = chomp( p );
395         argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
396         for( i=0; i<n; i++ )
397         {
398                 argv[i] = p;
399                 p += lstrlenW(p) + 1;
400         }
401         argv[i] = NULL;
402
403         *pargc = n;
404         *pargv = argv;
405 }
406
407 static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
408 {
409         LONG r;
410         HKEY hkey = 0, hkeyArgs = 0;
411         DWORD sz = 0, type = 0;
412         LPWSTR buf = NULL;
413         BOOL ret = FALSE;
414
415         r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
416         if(r != ERROR_SUCCESS)
417                 return FALSE;
418         r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
419         if(r == ERROR_SUCCESS && type == REG_SZ)
420         {
421                 buf = HeapAlloc(GetProcessHeap(), 0, sz);
422                 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)buf, &sz);
423                 if( r == ERROR_SUCCESS )
424                 {
425                         process_args(buf, pargc, pargv);
426                         ret = TRUE;
427                 }
428         }
429         RegCloseKey(hkeyArgs);
430         return ret;
431 }
432
433 int main(int argc, char **argv)
434 {
435         int i;
436         BOOL FunctionInstall = FALSE;
437         BOOL FunctionInstallAdmin = FALSE;
438         BOOL FunctionRepair = FALSE;
439         BOOL FunctionAdvertise = FALSE;
440         BOOL FunctionPatch = FALSE;
441         BOOL FunctionDllRegisterServer = FALSE;
442         BOOL FunctionDllUnregisterServer = FALSE;
443         BOOL FunctionRegServer = FALSE;
444         BOOL FunctionUnregServer = FALSE;
445         BOOL FunctionUnknown = FALSE;
446
447         LPWSTR PackageName = NULL;
448         LPWSTR Properties = NULL;
449         struct string_list *property_list = NULL;
450
451         DWORD RepairMode = 0;
452
453         DWORD AdvertiseMode = 0;
454         LPWSTR Transforms = NULL;
455         struct string_list *transform_list = NULL;
456         LANGID Language = 0;
457
458         DWORD LogMode = 0;
459         LPWSTR LogFileName = NULL;
460         DWORD LogAttributes = 0;
461
462         LPWSTR PatchFileName = NULL;
463         INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
464
465         INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
466
467         LPWSTR DllName = NULL;
468         DWORD ReturnCode;
469         LPWSTR *argvW = NULL;
470
471         /* overwrite the command line */
472         process_args( GetCommandLineW(), &argc, &argvW );
473
474         /*
475          * If the args begin with /@ IDENT then we need to load the real
476          * command line out of the RunOnceEntries key in the registry.
477          *  We do that before starting to process the real commandline,
478          * then overwrite the commandline again.
479          */
480         if(!msi_strequal(argvW[1], "/@"))
481         {
482                 if(!process_args_from_reg( argvW[2], &argc, &argvW ))
483                         return 1;
484         }
485
486         for(i = 1; i < argc; i++)
487         {
488                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
489
490                 if (!msi_strequal(argvW[i], "/regserver"))
491                 {
492                         FunctionRegServer = TRUE;
493                 }
494                 else if (!msi_strequal(argvW[i], "/unregserver") || !msi_strequal(argvW[i], "/unregister"))
495                 {
496                         FunctionUnregServer = TRUE;
497                 }
498                 else if(!msi_strprefix(argvW[i], "/i"))
499                 {
500                         LPWSTR argvWi = argvW[i];
501                         FunctionInstall = TRUE;
502                         if(lstrlenW(argvWi) > 2)
503                                 argvWi += 2;
504                         else
505                         {
506                                 i++;
507                                 if(i >= argc)
508                                         ShowUsage(1);
509                                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
510                                 argvWi = argvW[i];
511                         }
512                         PackageName = argvWi;
513                 }
514                 else if(!msi_strequal(argvW[i], "/a"))
515                 {
516                         FunctionInstall = TRUE;
517                         FunctionInstallAdmin = TRUE;
518                         InstallType = INSTALLTYPE_NETWORK_IMAGE;
519                         i++;
520                         if(i >= argc)
521                                 ShowUsage(1);
522                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
523                         PackageName = argvW[i];
524                         StringListAppend(&property_list, ActionAdmin);
525                 }
526                 else if(!msi_strprefix(argvW[i], "/f"))
527                 {
528                         int j;
529                         int len = lstrlenW(argvW[i]);
530                         FunctionRepair = TRUE;
531                         for(j = 2; j < len; j++)
532                         {
533                                 switch(argvW[i][j])
534                                 {
535                                         case 'P':
536                                         case 'p':
537                                                 RepairMode |= REINSTALLMODE_FILEMISSING;
538                                                 break;
539                                         case 'O':
540                                         case 'o':
541                                                 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
542                                                 break;
543                                         case 'E':
544                                         case 'e':
545                                                 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
546                                                 break;
547                                         case 'D':
548                                         case 'd':
549                                                 RepairMode |= REINSTALLMODE_FILEEXACT;
550                                                 break;
551                                         case 'C':
552                                         case 'c':
553                                                 RepairMode |= REINSTALLMODE_FILEVERIFY;
554                                                 break;
555                                         case 'A':
556                                         case 'a':
557                                                 RepairMode |= REINSTALLMODE_FILEREPLACE;
558                                                 break;
559                                         case 'U':
560                                         case 'u':
561                                                 RepairMode |= REINSTALLMODE_USERDATA;
562                                                 break;
563                                         case 'M':
564                                         case 'm':
565                                                 RepairMode |= REINSTALLMODE_MACHINEDATA;
566                                                 break;
567                                         case 'S':
568                                         case 's':
569                                                 RepairMode |= REINSTALLMODE_SHORTCUT;
570                                                 break;
571                                         case 'V':
572                                         case 'v':
573                                                 RepairMode |= REINSTALLMODE_PACKAGE;
574                                                 break;
575                                         default:
576                                                 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
577                                                 break;
578                                 }
579                         }
580                         if(len == 2)
581                         {
582                                 RepairMode = REINSTALLMODE_FILEMISSING |
583                                         REINSTALLMODE_FILEEQUALVERSION |
584                                         REINSTALLMODE_FILEVERIFY |
585                                         REINSTALLMODE_MACHINEDATA |
586                                         REINSTALLMODE_SHORTCUT;
587                         }
588                         i++;
589                         if(i >= argc)
590                                 ShowUsage(1);
591                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
592                         PackageName = argvW[i];
593                 }
594                 else if(!msi_strequal(argvW[i], "/x"))
595                 {
596                         FunctionInstall = TRUE;
597                         i++;
598                         if(i >= argc)
599                                 ShowUsage(1);
600                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
601                         PackageName = argvW[i];
602                         StringListAppend(&property_list, RemoveAll);
603                 }
604                 else if(!msi_strprefix(argvW[i], "/j"))
605                 {
606                         int j;
607                         int len = lstrlenW(argvW[i]);
608                         FunctionAdvertise = TRUE;
609                         for(j = 2; j < len; j++)
610                         {
611                                 switch(argvW[i][j])
612                                 {
613                                         case 'U':
614                                         case 'u':
615                                                 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
616                                                 break;
617                                         case 'M':
618                                         case 'm':
619                                                 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
620                                                 break;
621                                         default:
622                                                 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
623                                                 break;
624                                 }
625                         }
626                         i++;
627                         if(i >= argc)
628                                 ShowUsage(1);
629                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
630                         PackageName = argvW[i];
631                 }
632                 else if(!msi_strequal(argvW[i], "u"))
633                 {
634                         FunctionAdvertise = TRUE;
635                         AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
636                         i++;
637                         if(i >= argc)
638                                 ShowUsage(1);
639                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
640                         PackageName = argvW[i];
641                 }
642                 else if(!msi_strequal(argvW[i], "m"))
643                 {
644                         FunctionAdvertise = TRUE;
645                         AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
646                         i++;
647                         if(i >= argc)
648                                 ShowUsage(1);
649                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
650                         PackageName = argvW[i];
651                 }
652                 else if(!msi_strequal(argvW[i], "/t"))
653                 {
654                         i++;
655                         if(i >= argc)
656                                 ShowUsage(1);
657                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
658                         StringListAppend(&transform_list, argvW[i]);
659                 }
660                 else if(!msi_strprefix(argvW[i], "TRANSFORMS="))
661                 {
662                         StringListAppend(&transform_list, argvW[i]+11);
663                 }
664                 else if(!msi_strequal(argvW[i], "/g"))
665                 {
666                         i++;
667                         if(i >= argc)
668                                 ShowUsage(1);
669                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
670                         Language = msi_atou(argvW[i]);
671                 }
672                 else if(!msi_strprefix(argvW[i], "/l"))
673                 {
674                         int j;
675                         int len = lstrlenW(argvW[i]);
676                         for(j = 2; j < len; j++)
677                         {
678                                 switch(argvW[i][j])
679                                 {
680                                         case 'I':
681                                         case 'i':
682                                                 LogMode |= INSTALLLOGMODE_INFO;
683                                                 break;
684                                         case 'W':
685                                         case 'w':
686                                                 LogMode |= INSTALLLOGMODE_WARNING;
687                                                 break;
688                                         case 'E':
689                                         case 'e':
690                                                 LogMode |= INSTALLLOGMODE_ERROR;
691                                                 break;
692                                         case 'A':
693                                         case 'a':
694                                                 LogMode |= INSTALLLOGMODE_ACTIONSTART;
695                                                 break;
696                                         case 'R':
697                                         case 'r':
698                                                 LogMode |= INSTALLLOGMODE_ACTIONDATA;
699                                                 break;
700                                         case 'U':
701                                         case 'u':
702                                                 LogMode |= INSTALLLOGMODE_USER;
703                                                 break;
704                                         case 'C':
705                                         case 'c':
706                                                 LogMode |= INSTALLLOGMODE_COMMONDATA;
707                                                 break;
708                                         case 'M':
709                                         case 'm':
710                                                 LogMode |= INSTALLLOGMODE_FATALEXIT;
711                                                 break;
712                                         case 'O':
713                                         case 'o':
714                                                 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
715                                                 break;
716                                         case 'P':
717                                         case 'p':
718                                                 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
719                                                 break;
720                                         case 'V':
721                                         case 'v':
722                                                 LogMode |= INSTALLLOGMODE_VERBOSE;
723                                                 break;
724                                         case '*':
725                                                 LogMode = INSTALLLOGMODE_FATALEXIT |
726                                                         INSTALLLOGMODE_ERROR |
727                                                         INSTALLLOGMODE_WARNING |
728                                                         INSTALLLOGMODE_USER |
729                                                         INSTALLLOGMODE_INFO |
730                                                         INSTALLLOGMODE_RESOLVESOURCE |
731                                                         INSTALLLOGMODE_OUTOFDISKSPACE |
732                                                         INSTALLLOGMODE_ACTIONSTART |
733                                                         INSTALLLOGMODE_ACTIONDATA |
734                                                         INSTALLLOGMODE_COMMONDATA |
735                                                         INSTALLLOGMODE_PROPERTYDUMP |
736                                                         INSTALLLOGMODE_PROGRESS |
737                                                         INSTALLLOGMODE_INITIALIZE |
738                                                         INSTALLLOGMODE_TERMINATE |
739                                                         INSTALLLOGMODE_SHOWDIALOG;
740                                                 break;
741                                         case '+':
742                                                 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
743                                                 break;
744                                         case '!':
745                                                 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
746                                                 break;
747                                         default:
748                                                 break;
749                                 }
750                         }
751                         i++;
752                         if(i >= argc)
753                                 ShowUsage(1);
754                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
755                         LogFileName = argvW[i];
756                         if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
757                         {
758                                 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n",
759                                          wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
760                                 ExitProcess(1);
761                         }
762                 }
763                 else if(!msi_strequal(argvW[i], "/p"))
764                 {
765                         FunctionPatch = TRUE;
766                         i++;
767                         if(i >= argc)
768                                 ShowUsage(1);
769                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
770                         PatchFileName = argvW[i];
771                 }
772                 else if(!msi_strprefix(argvW[i], "/q"))
773                 {
774                         if(lstrlenW(argvW[i]) == 2 || !msi_strequal(argvW[i]+2, "n"))
775                         {
776                                 InstallUILevel = INSTALLUILEVEL_NONE;
777                         }
778                         else if(!msi_strequal(argvW[i]+2, "b"))
779                         {
780                                 InstallUILevel = INSTALLUILEVEL_BASIC;
781                         }
782                         else if(!msi_strequal(argvW[i]+2, "r"))
783                         {
784                                 InstallUILevel = INSTALLUILEVEL_REDUCED;
785                         }
786                         else if(!msi_strequal(argvW[i]+2, "f"))
787                         {
788                                 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
789                         }
790                         else if(!msi_strequal(argvW[i]+2, "n+"))
791                         {
792                                 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
793                         }
794                         else if(!msi_strequal(argvW[i]+2, "b+"))
795                         {
796                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
797                         }
798                         else if(!msi_strequal(argvW[i]+2, "b-"))
799                         {
800                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
801                         }
802                         else if(!msi_strequal(argvW[i]+2, "b+!"))
803                         {
804                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
805                                 WINE_FIXME("Unknown modifier: !\n");
806                         }
807                         else
808                         {
809                                 fprintf(stderr, "Unknown option \"%s\" for UI level\n",
810                                          wine_dbgstr_w(argvW[i]+2));
811                         }
812                 }
813                 else if(!msi_strequal(argvW[i], "/y"))
814                 {
815                         FunctionDllRegisterServer = TRUE;
816                         i++;
817                         if(i >= argc)
818                                 ShowUsage(1);
819                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
820                         DllName = argvW[i];
821                 }
822                 else if(!msi_strequal(argvW[i], "/z"))
823                 {
824                         FunctionDllUnregisterServer = TRUE;
825                         i++;
826                         if(i >= argc)
827                                 ShowUsage(1);
828                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
829                         DllName = argvW[i];
830                 }
831                 else if(!msi_strequal(argvW[i], "/h") || !msi_strequal(argvW[i], "/?"))
832                 {
833                         ShowUsage(0);
834                 }
835                 else if(!msi_strequal(argvW[i], "/m"))
836                 {
837                         FunctionUnknown = TRUE;
838                         WINE_FIXME("Unknown parameter /m\n");
839                 }
840                 else if(!msi_strequal(argvW[i], "/D"))
841                 {
842                         FunctionUnknown = TRUE;
843                         WINE_FIXME("Unknown parameter /D\n");
844                 }
845                 else if(strchrW(argvW[i], '='))
846                 {
847                         StringListAppend(&property_list, argvW[i]);
848                 }
849                 else
850                 {
851                         FunctionInstall = TRUE;
852                         PackageName = argvW[i];
853                 }
854         }
855
856         /* start the GUI */
857         MsiSetInternalUI(InstallUILevel, NULL);
858
859         Properties = build_properties( property_list );
860         Transforms = build_transforms( transform_list );
861
862         if(FunctionInstallAdmin && FunctionPatch)
863                 FunctionInstall = FALSE;
864
865         ReturnCode = 1;
866         if(FunctionInstall)
867         {
868                 if(IsProductCode(PackageName))
869                         ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
870                 else
871                         ReturnCode = MsiInstallProductW(PackageName, Properties);
872         }
873         else if(FunctionRepair)
874         {
875                 if(IsProductCode(PackageName))
876                         WINE_FIXME("Product code treatment not implemented yet\n");
877                 else
878                         ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
879         }
880         else if(FunctionAdvertise)
881         {
882                 ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
883         }
884         else if(FunctionPatch)
885         {
886                 ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
887         }
888         else if(FunctionDllRegisterServer)
889         {
890                 ReturnCode = DoDllRegisterServer(DllName);
891         }
892         else if(FunctionDllUnregisterServer)
893         {
894                 ReturnCode = DoDllUnregisterServer(DllName);
895         }
896         else if (FunctionRegServer)
897         {
898                 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
899         }
900         else if (FunctionUnregServer)
901         {
902                 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
903         }
904         else if (FunctionUnknown)
905         {
906                 WINE_FIXME( "Unknown function, ignoring\n" );
907         }
908         else
909                 ShowUsage(1);
910
911         return ReturnCode;
912 }