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