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