1/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved	by Bram Moolenaar
4 *
5 * Do ":help uganda"  in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * uninstal.c:	Minimalistic uninstall program for Vim on MS-Windows
12 *		Removes:
13 *		- the "Edit with Vim" popup menu entry
14 *		- the Vim "Open With..." popup menu entry
15 *		- any Vim Batch files in the path
16 *		- icons for Vim on the Desktop
17 *		- the Vim entry in the Start Menu
18 */
19
20/* Include common code for dosinst.c and uninstal.c. */
21#include "dosinst.h"
22
23/*
24 * Return TRUE if the user types a 'y' or 'Y', FALSE otherwise.
25 */
26    static int
27confirm(void)
28{
29    char	answer[10];
30
31    fflush(stdout);
32    return (scanf(" %c", answer) == 1 && toupper(answer[0]) == 'Y');
33}
34
35#ifdef WIN3264
36
37    static int
38reg_delete_key(HKEY hRootKey, const char *key)
39{
40    static int did_load = FALSE;
41    static HANDLE advapi_lib = NULL;
42    static LONG (WINAPI *delete_key_ex)(HKEY, LPCTSTR, REGSAM, DWORD) = NULL;
43
44    if (!did_load)
45    {
46	/* The RegDeleteKeyEx() function is only available on new systems.  It
47	 * is required for 64-bit registry access.  For other systems fall
48	 * back to RegDeleteKey(). */
49	did_load = TRUE;
50	advapi_lib = LoadLibrary("ADVAPI32.DLL");
51	if (advapi_lib != NULL)
52	    delete_key_ex = (LONG (WINAPI *)(HKEY, LPCTSTR, REGSAM, DWORD))GetProcAddress(advapi_lib, "RegDeleteKeyExA");
53    }
54    if (delete_key_ex != NULL) {
55	return (*delete_key_ex)(hRootKey, key, KEY_WOW64_64KEY, 0);
56    }
57    return RegDeleteKey(hRootKey, key);
58}
59
60/*
61 * Check if the popup menu entry exists and what gvim it refers to.
62 * Returns non-zero when it's found.
63 */
64    static int
65popup_gvim_path(char *buf)
66{
67    HKEY	key_handle;
68    DWORD	value_type;
69    DWORD	bufsize = BUFSIZE;
70    int		r;
71
72    /* Open the key where the path to gvim.exe is stored. */
73    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
74		    KEY_WOW64_64KEY | KEY_READ, &key_handle) != ERROR_SUCCESS)
75	return 0;
76
77    /* get the DisplayName out of it to show the user */
78    r = RegQueryValueEx(key_handle, "path", 0,
79					  &value_type, (LPBYTE)buf, &bufsize);
80    RegCloseKey(key_handle);
81
82    return (r == ERROR_SUCCESS);
83}
84
85/*
86 * Check if the "Open With..." menu entry exists and what gvim it refers to.
87 * Returns non-zero when it's found.
88 */
89    static int
90openwith_gvim_path(char *buf)
91{
92    HKEY	key_handle;
93    DWORD	value_type;
94    DWORD	bufsize = BUFSIZE;
95    int		r;
96
97    /* Open the key where the path to gvim.exe is stored. */
98    if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
99		"Applications\\gvim.exe\\shell\\edit\\command", 0,
100		    KEY_WOW64_64KEY | KEY_READ, &key_handle) != ERROR_SUCCESS)
101	return 0;
102
103    /* get the DisplayName out of it to show the user */
104    r = RegQueryValueEx(key_handle, "", 0, &value_type, (LPBYTE)buf, &bufsize);
105    RegCloseKey(key_handle);
106
107    return (r == ERROR_SUCCESS);
108}
109
110    static void
111remove_popup(void)
112{
113    int		fail = 0;
114    HKEY	kh;
115
116    if (reg_delete_key(HKEY_CLASSES_ROOT, "CLSID\\{51EEE242-AD87-11d3-9C1E-0090278BBD99}\\InProcServer32") != ERROR_SUCCESS)
117	++fail;
118    if (reg_delete_key(HKEY_CLASSES_ROOT, "CLSID\\{51EEE242-AD87-11d3-9C1E-0090278BBD99}") != ERROR_SUCCESS)
119	++fail;
120    if (reg_delete_key(HKEY_CLASSES_ROOT, "*\\shellex\\ContextMenuHandlers\\gvim") != ERROR_SUCCESS)
121	++fail;
122    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 0,
123		      KEY_WOW64_64KEY | KEY_ALL_ACCESS, &kh) != ERROR_SUCCESS)
124	++fail;
125    else
126    {
127	if (RegDeleteValue(kh, "{51EEE242-AD87-11d3-9C1E-0090278BBD99}") != ERROR_SUCCESS)
128	    ++fail;
129	RegCloseKey(kh);
130    }
131    if (reg_delete_key(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim") != ERROR_SUCCESS)
132	++fail;
133    if (reg_delete_key(HKEY_LOCAL_MACHINE, "Software\\Vim") != ERROR_SUCCESS)
134	++fail;
135
136    if (fail == 6)
137	printf("No Vim popup registry entries could be removed\n");
138    else if (fail > 0)
139	printf("Some Vim popup registry entries could not be removed\n");
140    else
141	printf("The Vim popup registry entries have been removed\n");
142}
143
144    static void
145remove_openwith(void)
146{
147    int		fail = 0;
148
149    if (reg_delete_key(HKEY_CLASSES_ROOT, "Applications\\gvim.exe\\shell\\edit\\command") != ERROR_SUCCESS)
150	++fail;
151    if (reg_delete_key(HKEY_CLASSES_ROOT, "Applications\\gvim.exe\\shell\\edit") != ERROR_SUCCESS)
152	++fail;
153    if (reg_delete_key(HKEY_CLASSES_ROOT, "Applications\\gvim.exe\\shell") != ERROR_SUCCESS)
154	++fail;
155    if (reg_delete_key(HKEY_CLASSES_ROOT, "Applications\\gvim.exe") != ERROR_SUCCESS)
156	++fail;
157    if (reg_delete_key(HKEY_CLASSES_ROOT, ".htm\\OpenWithList\\gvim.exe") != ERROR_SUCCESS)
158	++fail;
159    if (reg_delete_key(HKEY_CLASSES_ROOT, ".vim\\OpenWithList\\gvim.exe") != ERROR_SUCCESS)
160	++fail;
161    if (reg_delete_key(HKEY_CLASSES_ROOT, "*\\OpenWithList\\gvim.exe") != ERROR_SUCCESS)
162	++fail;
163
164    if (fail == 7)
165	printf("No Vim open-with registry entries could be removed\n");
166    else if (fail > 0)
167	printf("Some Vim open-with registry entries could not be removed\n");
168    else
169	printf("The Vim open-with registry entries have been removed\n");
170}
171#endif
172
173/*
174 * Check if a batch file is really for the current version.  Don't delete a
175 * batch file that was written for another (possibly newer) version.
176 */
177    static int
178batfile_thisversion(char *path)
179{
180    FILE	*fd;
181    char	line[BUFSIZE];
182    char	*p;
183    int		ver_len = strlen(VIM_VERSION_NODOT);
184    int		found = FALSE;
185
186    fd = fopen(path, "r");
187    if (fd != NULL)
188    {
189	while (fgets(line, BUFSIZE, fd) != NULL)
190	{
191	    for (p = line; *p != 0; ++p)
192		/* don't accept "vim60an" when looking for "vim60". */
193		if (strnicmp(p, VIM_VERSION_NODOT, ver_len) == 0
194			&& !isdigit(p[ver_len])
195			&& !isalpha(p[ver_len]))
196		{
197		    found = TRUE;
198		    break;
199		}
200	    if (found)
201		break;
202	}
203	fclose(fd);
204    }
205    return found;
206}
207
208    static int
209remove_batfiles(int doit)
210{
211    char *batfile_path;
212    int	 i;
213    int	 found = 0;
214
215    for (i = 1; i < TARGET_COUNT; ++i)
216    {
217	batfile_path = searchpath_save(targets[i].batname);
218	if (batfile_path != NULL && batfile_thisversion(batfile_path))
219	{
220	    ++found;
221	    if (doit)
222	    {
223		printf("removing %s\n", batfile_path);
224		remove(batfile_path);
225	    }
226	    else
227		printf(" - the batch file %s\n", batfile_path);
228	    free(batfile_path);
229	}
230    }
231    return found;
232}
233
234#ifdef WIN3264
235    static void
236remove_if_exists(char *path, char *filename)
237{
238    char buf[BUFSIZE];
239    FILE *fd;
240
241    sprintf(buf, "%s\\%s", path, filename);
242
243    fd = fopen(buf, "r");
244    if (fd != NULL)
245    {
246	fclose(fd);
247	printf("removing %s\n", buf);
248	remove(buf);
249    }
250}
251
252    static void
253remove_icons(void)
254{
255    char	path[BUFSIZE];
256    int		i;
257
258    if (get_shell_folder_path(path, "desktop"))
259	for (i = 0; i < ICON_COUNT; ++i)
260	    remove_if_exists(path, icon_link_names[i]);
261}
262
263    static void
264remove_start_menu(void)
265{
266    char	path[BUFSIZE];
267    int		i;
268    struct stat st;
269
270    if (get_shell_folder_path(path, VIM_STARTMENU))
271    {
272	for (i = 1; i < TARGET_COUNT; ++i)
273	    remove_if_exists(path, targets[i].lnkname);
274	remove_if_exists(path, "uninstall.lnk");
275	remove_if_exists(path, "Help.lnk");
276	/* Win95 uses .pif, WinNT uses .lnk */
277	remove_if_exists(path, "Vim tutor.pif");
278	remove_if_exists(path, "Vim tutor.lnk");
279	remove_if_exists(path, "Vim online.url");
280	if (stat(path, &st) == 0)
281	{
282	    printf("removing %s\n", path);
283	    rmdir(path);
284	}
285    }
286}
287#endif
288
289    static void
290delete_uninstall_key(void)
291{
292#ifdef WIN3264
293    reg_delete_key(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT);
294#else
295    FILE	*fd;
296    char	buf[BUFSIZE];
297
298    /*
299     * On DJGPP we delete registry entries by creating a .inf file and
300     * installing it.
301     */
302    fd = fopen("vim.inf", "w");
303    if (fd != NULL)
304    {
305	fprintf(fd, "[version]\n");
306	fprintf(fd, "signature=\"$CHICAGO$\"\n\n");
307	fprintf(fd, "[DefaultInstall]\n");
308	fprintf(fd, "DelReg=DeleteMe\n\n");
309	fprintf(fd, "[DeleteMe]\n");
310	fprintf(fd, "HKLM,\"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT "\"\n");
311	fclose(fd);
312
313	/* Don't know how to detect Win NT with DJGPP.  Hack: Just try the Win
314	 * 95/98/ME method, since the DJGPP version can't use long filenames
315	 * on Win NT anyway. */
316	sprintf(buf, "rundll setupx.dll,InstallHinfSection DefaultInstall 132 %s\\vim.inf", installdir);
317	run_command(buf);
318#if 0
319	/* Windows NT method (untested). */
320	sprintf(buf, "rundll32 syssetup,SetupInfObjectInstallAction DefaultInstall 128 %s\\vim.inf", installdir);
321	run_command(buf);
322#endif
323
324	remove("vim.inf");
325    }
326#endif
327}
328
329    int
330main(int argc, char *argv[])
331{
332    int		found = 0;
333    FILE	*fd;
334#ifdef WIN3264
335    int		i;
336    struct stat st;
337    char	icon[BUFSIZE];
338    char	path[BUFSIZE];
339    char	popup_path[BUFSIZE];
340
341    /* The nsis uninstaller calls us with a "-nsis" argument. */
342    if (argc == 2 && stricmp(argv[1], "-nsis") == 0)
343	interactive = FALSE;
344    else
345#endif
346	interactive = TRUE;
347
348    /* Initialize this program. */
349    do_inits(argv);
350
351    printf("This program will remove the following items:\n");
352
353#ifdef WIN3264
354    if (popup_gvim_path(popup_path))
355    {
356	printf(" - the \"Edit with Vim\" entry in the popup menu\n");
357	printf("   which uses \"%s\"\n", popup_path);
358	if (interactive)
359	    printf("\nRemove it (y/n)? ");
360	if (!interactive || confirm())
361	{
362	    remove_popup();
363	    /* Assume the "Open With" entry can be removed as well, don't
364	     * bother the user with asking him again. */
365	    remove_openwith();
366	}
367    }
368    else if (openwith_gvim_path(popup_path))
369    {
370	printf(" - the Vim \"Open With...\" entry in the popup menu\n");
371	printf("   which uses \"%s\"\n", popup_path);
372	printf("\nRemove it (y/n)? ");
373	if (confirm())
374	    remove_openwith();
375    }
376
377    if (get_shell_folder_path(path, "desktop"))
378    {
379	printf("\n");
380	for (i = 0; i < ICON_COUNT; ++i)
381	{
382	    sprintf(icon, "%s\\%s", path, icon_link_names[i]);
383	    if (stat(icon, &st) == 0)
384	    {
385		printf(" - the \"%s\" icon on the desktop\n", icon_names[i]);
386		++found;
387	    }
388	}
389	if (found > 0)
390	{
391	    if (interactive)
392		printf("\nRemove %s (y/n)? ", found > 1 ? "them" : "it");
393	    if (!interactive || confirm())
394		remove_icons();
395	}
396    }
397
398    if (get_shell_folder_path(path, VIM_STARTMENU)
399	    && stat(path, &st) == 0)
400    {
401	printf("\n - the \"%s\" entry in the Start Menu\n", VIM_STARTMENU);
402	if (interactive)
403	    printf("\nRemove it (y/n)? ");
404	if (!interactive || confirm())
405	    remove_start_menu();
406    }
407#endif
408
409    printf("\n");
410    found = remove_batfiles(0);
411    if (found > 0)
412    {
413	if (interactive)
414	    printf("\nRemove %s (y/n)? ", found > 1 ? "them" : "it");
415	if (!interactive || confirm())
416	    remove_batfiles(1);
417    }
418
419    fd = fopen("gvim.exe", "r");
420    if (fd != NULL)
421    {
422	fclose(fd);
423	printf("gvim.exe detected.  Attempting to unregister gvim with OLE\n");
424	system("gvim.exe -silent -unregister");
425    }
426
427    delete_uninstall_key();
428
429    if (interactive)
430    {
431	printf("\nYou may now want to delete the Vim executables and runtime files.\n");
432	printf("(They are still where you unpacked them.)\n");
433    }
434
435    if (interactive)
436    {
437	rewind(stdin);
438	printf("\nPress Enter to exit...");
439	(void)getchar();
440    }
441    else
442	sleep(3);
443
444    return 0;
445}
446