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