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