Removed trailing whitespace.
[wine] / programs / uninstaller / main.c
1 /*
2  * Q&D Uninstaller (main.c)
3  *
4  * Copyright 2000 Andreas Mohr <andi@lisas.de>
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  * ToDo:
21  * - add search box for locating entries quickly
22  */
23
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29 #include <windows.h>
30 #include "main.h"
31 #include "regstr.h"
32
33 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
34 #ifdef WINE_STRICT
35 #define NULL_HANDLE NULL
36 #else
37 #define NULL_HANDLE 0
38 #endif
39
40 #undef DUMB_DEBUG
41 #ifdef DUMB_DEBUG
42 #include <stdio.h>
43 #define DEBUG(x...) fprintf(stderr,x)
44 #else
45 #define DEBUG(x...)
46 #endif
47
48 /* use multi-select listbox */
49 #undef USE_MULTIPLESEL
50
51 /* Delete uninstall registry key after execution.
52  * This is probably a bad idea, because it's the
53  * uninstall program that is supposed to do that.
54  */
55 #undef DEL_REG_KEY
56
57 char appname[18];
58
59 static char about_string[] =
60     "Windows program uninstaller (C) 2000 by Andreas Mohr <andi@lisas.de>";
61 static char program_description[] =
62         "Welcome to the Wine uninstaller !\n\nThe purpose of this program is to let you get rid of all those fantastic programs that somehow manage to always take way too much space on your HDD :-)";
63
64 typedef struct {
65     char *key;
66     char *descr;
67     char *command;
68     int active;
69 } uninst_entry;
70
71 uninst_entry *entries = NULL;
72
73 int numentries = 0;
74 int list_need_update = 1;
75 int oldsel = -1;
76
77 struct {
78     DWORD style;
79     LPCSTR text;
80     HWND hwnd;
81 } button[] =
82 {
83     { BS_PUSHBUTTON, "Add/Remove", 0 },
84     { BS_PUSHBUTTON, "About", 0 },
85     { BS_PUSHBUTTON, "Exit", 0 }
86 };
87
88 #define NUM (sizeof button/sizeof button[0])
89
90 int FetchUninstallInformation(void);
91 void UninstallProgram(void);
92
93 void ListUninstallPrograms(void)
94 {
95     int i;
96
97     if (! FetchUninstallInformation())
98         return;
99
100     for (i=0; i < numentries; i++)
101         printf("%s|||%s\n", entries[i].key, entries[i].descr);
102 }
103
104
105 void RemoveSpecificProgram(char *name)
106 {
107     int i;
108
109     if (! FetchUninstallInformation())
110         return;
111
112     for (i=0; i < numentries; i++)
113     {
114         if (strcmp(entries[i].key, name) == 0)
115         {
116             entries[i].active++;
117             break;
118         }
119     }
120
121     if (i < numentries)
122         UninstallProgram();
123     else
124     {
125         fprintf(stderr, "Error: could not match program [%s]\n", name);
126     }
127 }
128
129
130 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
131 {
132     MSG msg;
133     WNDCLASS wc;
134     HWND hWnd;
135
136     /*------------------------------------------------------------------------
137     ** Handle requests just to list the programs
138     **----------------------------------------------------------------------*/
139     if (cmdline && strlen(cmdline) >= 6 && memcmp(cmdline, "--list", 6) == 0)
140     {
141         ListUninstallPrograms();
142         return(0);
143     }
144
145     /*------------------------------------------------------------------------
146     ** Handle requests to remove one program
147     **----------------------------------------------------------------------*/
148     if (cmdline && strlen(cmdline) > 9 && memcmp(cmdline, "--remove ", 9) == 0)
149     {
150         RemoveSpecificProgram(cmdline + 9);
151         return(0);
152     }
153
154
155
156     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
157
158     wc.style = 0;
159     wc.lpfnWndProc = MainProc;
160     wc.cbClsExtra = 0;
161     wc.cbWndExtra = 0;
162     wc.hInstance = hInst;
163     wc.hIcon = LoadIcon( hInst, appname );
164     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
165     wc.hbrBackground = (HBRUSH) GetStockObject( LTGRAY_BRUSH );
166     wc.lpszMenuName = NULL;
167     wc.lpszClassName = appname;
168
169     if (!RegisterClass(&wc)) exit(1);
170     hWnd = CreateWindow( appname, appname,
171         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
172         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
173         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
174
175     if (!hWnd) exit(1);
176
177     ShowWindow( hWnd, cmdshow );
178     UpdateWindow( hWnd );
179
180     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
181         TranslateMessage( &msg );
182         DispatchMessage( &msg );
183     }
184     return msg.wParam;
185 }
186
187 int cmp_by_name(const void *a, const void *b)
188 {
189     return strcasecmp(((uninst_entry *)a)->descr, ((uninst_entry *)b)->descr);
190 }
191
192 int FetchUninstallInformation(void)
193 {
194     HKEY hkeyUninst, hkeyApp;
195     int i;
196     DWORD sizeOfSubKeyName=255, displen, uninstlen;
197     char   subKeyName[256];
198     char key_app[1024];
199     char *p;
200
201
202     numentries = 0;
203     oldsel = -1;
204     if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
205                             0, KEY_READ, &hkeyUninst) != ERROR_SUCCESS )
206     {
207         MessageBox(0, "Uninstall registry key not available (yet), nothing to do !", appname, MB_OK);
208         return 0;
209     }
210
211     strcpy(key_app, REGSTR_PATH_UNINSTALL);
212     strcat(key_app, "\\");
213     p = key_app+strlen(REGSTR_PATH_UNINSTALL)+1;
214     for ( i=0;
215           RegEnumKeyExA( hkeyUninst, i, subKeyName, &sizeOfSubKeyName,
216                          NULL, NULL, NULL, NULL ) != ERROR_NO_MORE_ITEMS;
217           ++i, sizeOfSubKeyName=255 )
218     {
219         strcpy(p, subKeyName);
220         RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_app, 0, KEY_READ, &hkeyApp);
221
222         if ( (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME,
223                 0, 0, NULL, &displen) == ERROR_SUCCESS)
224         &&   (RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE,
225                 0, 0, NULL, &uninstlen) == ERROR_SUCCESS) )
226         {
227             numentries++;
228             entries = HeapReAlloc(GetProcessHeap(), 0, entries, numentries*sizeof(uninst_entry));
229             entries[numentries-1].key =
230                     HeapAlloc(GetProcessHeap(), 0, strlen(subKeyName)+1);
231             strcpy(entries[numentries-1].key, subKeyName);
232             entries[numentries-1].descr =
233                     HeapAlloc(GetProcessHeap(), 0, displen);
234             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_DISPLAYNAME, 0, 0,
235                             entries[numentries-1].descr, &displen);
236             entries[numentries-1].command =
237                     HeapAlloc(GetProcessHeap(), 0, uninstlen);
238             entries[numentries-1].active = 0;
239             RegQueryValueEx(hkeyApp, REGSTR_VAL_UNINSTALLER_COMMANDLINE, 0, 0,
240                             entries[numentries-1].command, &uninstlen);
241             DEBUG("allocated entry #%d: '%s' ('%s'), '%s'\n", numentries, entries[numentries-1].key, entries[numentries-1].descr, entries[numentries-1].command);
242         }
243         RegCloseKey(hkeyApp);
244     }
245     qsort(entries, numentries, sizeof(uninst_entry), cmp_by_name);
246     RegCloseKey(hkeyUninst);
247     return 1;
248 }
249
250 void UninstallProgram(void)
251 {
252     int i;
253     char errormsg[1024];
254     BOOL res;
255     STARTUPINFO si;
256     PROCESS_INFORMATION info;
257     DWORD exit_code;
258 #ifdef DEL_REG_KEY
259     HKEY hkey;
260 #endif
261
262     for (i=0; i < numentries; i++)
263     {
264         if (!(entries[i].active)) /* don't uninstall this one */
265             continue;
266         DEBUG("uninstalling '%s'\n", entries[i].descr);
267         memset(&si, 0, sizeof(STARTUPINFO));
268         si.cb = sizeof(STARTUPINFO);
269         si.wShowWindow = SW_NORMAL;
270         res = CreateProcess(NULL, entries[i].command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info);
271         if (res == TRUE)
272         {   /* wait for the process to exit */
273             WaitForSingleObject(info.hProcess, INFINITE);
274             res = GetExitCodeProcess(info.hProcess, &exit_code);
275             DEBUG("%d: %08lx\n", res, exit_code);
276 #ifdef DEL_REG_KEY
277             /* delete the program's uninstall entry */
278             if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_UNINSTALL,
279                 0, KEY_READ, &hkey) == ERROR_SUCCESS)
280             {
281                 RegDeleteKey(hkey, entries[i].key);
282                 RegCloseKey(hkey);
283             }
284 #endif
285         }
286         else
287         {
288             sprintf(errormsg, "Execution of uninstall command '%s' failed, perhaps due to missing executable.", entries[i].command);
289             MessageBox(0, errormsg, appname, MB_OK);
290         }
291     }
292     DEBUG("finished uninstall phase.\n");
293     list_need_update = 1;
294 }
295
296 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
297 {
298     HDC hdc;
299     PAINTSTRUCT ps;
300     TEXTMETRIC tm;
301     int cxChar, cyChar, i, y, bx, by, maxx, maxy, wx, wy;
302     static HWND hwndList = 0, hwndEdit = 0;
303     DWORD style;
304     RECT rect;
305
306     switch( msg ) {
307     case WM_CREATE:
308         {
309         hdc = GetDC(hWnd);
310         GetTextMetrics(hdc, &tm);
311         cxChar = tm.tmAveCharWidth;
312         cyChar = tm.tmHeight + tm.tmExternalLeading;
313         ReleaseDC(hWnd, hdc);
314         /* FIXME: implement sorting and use LBS_SORT here ! */
315         style = (WS_CHILD|WS_VISIBLE|LBS_STANDARD) & ~LBS_SORT;
316 #ifdef USE_MULTIPLESEL
317         style |= LBS_MULTIPLESEL;
318 #endif
319         bx = maxx = cxChar * 5;
320         by = maxy = cyChar * 3;
321         hwndList = CreateWindow("listbox", NULL,
322                 style,
323                 maxx, maxy,
324                 cxChar * 50 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 20,
325                 hWnd, (HMENU) 1,
326                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
327
328         GetWindowRect(hwndList, &rect);
329         y = by;
330         maxx += (rect.right - rect.left)*1.1;
331         maxy += (rect.bottom - rect.top)*1.1;
332         wx = 20*cxChar;
333         wy = 7*cyChar/4;
334         for (i=0; i < NUM; i++)
335         {
336             button[i].hwnd = CreateWindow("button", button[i].text,
337                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
338                 maxx, y,
339                 wx, wy,
340                 hWnd, (HMENU)i,
341                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
342             if (!button[i].hwnd)
343                     PostQuitMessage(0);
344             y += 2*cyChar;
345         }
346         CreateWindow("static", program_description,
347                 WS_CHILD|WS_VISIBLE|SS_LEFT,
348                 bx, maxy,
349                 cxChar * 50, wy,
350                 hWnd, (HMENU)1,
351                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
352         maxx += wx + cxChar * 5; /* button + right border */
353         maxy += cyChar * 5 + cyChar * 2; /* static text + distance */
354         CreateWindow("static", "command line to be executed:",
355                 WS_CHILD|WS_VISIBLE|SS_LEFT,
356                 bx, maxy,
357                 cxChar * 50, cyChar,
358                 hWnd, (HMENU)1,
359                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
360         maxy += cyChar;
361         hwndEdit = CreateWindow("edit", NULL,
362                 WS_CHILD|WS_VISIBLE|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_READONLY,
363                 bx, maxy, maxx-(2*bx), (cyChar*6)+4,
364                 hWnd, (HMENU)1,
365                 ((LPCREATESTRUCT)lParam)->hInstance, NULL);
366         maxy += (cyChar*6)+4 + cyChar * 3; /* edit ctrl + bottom border */
367         SetWindowPos(   hWnd, 0,
368                         0, 0, maxx, maxy,
369                         SWP_NOMOVE);
370         return 0;
371         }
372
373     case WM_PAINT:
374       {
375         if (list_need_update)
376         {
377             int prevsel;
378             prevsel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
379             if (!(FetchUninstallInformation()))
380             {
381                 PostQuitMessage(0);
382                 return 0;
383             }
384             SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
385             SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
386             for (i=0; i < numentries; i++)
387             {
388                 DEBUG("adding '%s'\n", entries[i].descr);
389                 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)entries[i].descr);
390             }
391             DEBUG("setting prevsel %d\n", prevsel);
392             if (prevsel != -1)
393                 SendMessage(hwndList, LB_SETCURSEL, prevsel, 0 );
394             SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);
395             list_need_update = 0;
396         }
397         hdc = BeginPaint( hWnd, &ps );
398         EndPaint( hWnd, &ps );
399         return 0;
400       }
401
402     case WM_DESTROY:
403         PostQuitMessage( 0 );
404         return 0;
405
406     case WM_COMMAND:
407         if ((HWND)lParam == hwndList)
408         {
409             if (HIWORD(wParam) == LBN_SELCHANGE)
410             {
411                 int sel = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
412
413 #ifndef USE_MULTIPLESEL
414                 if (oldsel != -1)
415                 {
416                     entries[oldsel].active ^= 1; /* toggle */
417                     DEBUG("toggling %d old '%s'\n", entries[oldsel].active, entries[oldsel].descr);
418                 }
419 #endif
420                 entries[sel].active ^= 1; /* toggle */
421                 DEBUG("toggling %d '%s'\n", entries[sel].active, entries[sel].descr);
422                 SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)entries[sel].command);
423                 oldsel = sel;
424             }
425         }
426         else
427         if ((HWND)lParam == button[0].hwnd) /* Uninstall button */
428         {
429             UninstallProgram();
430
431             InvalidateRect(hWnd, NULL, TRUE);
432             UpdateWindow(hWnd);
433
434         }
435         else
436         if ((HWND)lParam == button[1].hwnd) /* About button */
437             MessageBox(0, about_string, "About", MB_OK);
438         else
439         if ((HWND)lParam == button[2].hwnd) /* Exit button */
440             PostQuitMessage(0);
441         return 0;
442     }
443
444     return( DefWindowProc( hWnd, msg, wParam, lParam ));
445 }