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 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 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 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 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 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 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 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 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 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 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 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 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 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