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 * dosinst.h: Common code for dosinst.c and uninstal.c 11 */ 12 13 /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ 14 #if _MSC_VER >= 1400 15 # define _CRT_SECURE_NO_DEPRECATE 16 # define _CRT_NONSTDC_NO_DEPRECATE 17 #endif 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <sys/stat.h> 23 #include <fcntl.h> 24 25 #ifndef UNIX_LINT 26 # include "vimio.h" 27 # include <ctype.h> 28 29 # include <direct.h> 30 31 # include <windows.h> 32 # include <shlobj.h> 33 #endif 34 35 #ifdef UNIX_LINT 36 /* Running lint on Unix: Some things are missing. */ 37 char *searchpath(char *name); 38 #endif 39 40 #if defined(UNIX_LINT) 41 # include <unistd.h> 42 # include <errno.h> 43 #endif 44 45 #include "version.h" 46 47 #if defined(UNIX_LINT) 48 # define vim_mkdir(x, y) mkdir((char *)(x), y) 49 #else 50 # define vim_mkdir(x, y) _mkdir((char *)(x)) 51 #endif 52 53 #define sleep(n) Sleep((n) * 1000) 54 55 /* ---------------------------------------- */ 56 57 58 #define BUFSIZE (MAX_PATH*2) /* long enough to hold a file name path */ 59 #define NUL 0 60 61 #define FAIL 0 62 #define OK 1 63 64 #ifndef FALSE 65 # define FALSE 0 66 #endif 67 #ifndef TRUE 68 # define TRUE 1 69 #endif 70 71 /* 72 * Modern way of creating registry entries, also works on 64 bit windows when 73 * compiled as a 32 bit program. 74 */ 75 # ifndef KEY_WOW64_64KEY 76 # define KEY_WOW64_64KEY 0x0100 77 # endif 78 # ifndef KEY_WOW64_32KEY 79 # define KEY_WOW64_32KEY 0x0200 80 # endif 81 82 #define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT 83 84 int interactive; /* non-zero when running interactively */ 85 86 /* 87 * Call malloc() and exit when out of memory. 88 */ 89 static void * 90 alloc(int len) 91 { 92 void *p; 93 94 p = malloc(len); 95 if (p == NULL) 96 { 97 printf("ERROR: out of memory\n"); 98 exit(1); 99 } 100 return p; 101 } 102 103 /* 104 * The toupper() in Bcc 5.5 doesn't work, use our own implementation. 105 */ 106 static int 107 mytoupper(int c) 108 { 109 if (c >= 'a' && c <= 'z') 110 return c - 'a' + 'A'; 111 return c; 112 } 113 114 static void 115 myexit(int n) 116 { 117 if (!interactive) 118 { 119 /* Present a prompt, otherwise error messages can't be read. */ 120 printf("Press Enter to continue\n"); 121 rewind(stdin); 122 (void)getchar(); 123 } 124 exit(n); 125 } 126 127 128 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); 129 /* 130 * Check if this is a 64-bit OS. 131 */ 132 static BOOL 133 is_64bit_os(void) 134 { 135 #ifdef _WIN64 136 return TRUE; 137 #else 138 BOOL bIsWow64 = FALSE; 139 LPFN_ISWOW64PROCESS pIsWow64Process; 140 141 pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( 142 GetModuleHandle("kernel32"), "IsWow64Process"); 143 if (pIsWow64Process != NULL) 144 pIsWow64Process(GetCurrentProcess(), &bIsWow64); 145 return bIsWow64; 146 #endif 147 } 148 149 static char * 150 searchpath(char *name) 151 { 152 static char widename[2 * BUFSIZE]; 153 static char location[2 * BUFSIZE + 2]; 154 155 /* There appears to be a bug in FindExecutableA() on Windows NT. 156 * Use FindExecutableW() instead... */ 157 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, 158 (LPWSTR)widename, BUFSIZE); 159 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", 160 (LPWSTR)location) > (HINSTANCE)32) 161 { 162 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, 163 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); 164 return widename; 165 } 166 return NULL; 167 } 168 169 /* 170 * Call searchpath() and save the result in allocated memory, or return NULL. 171 */ 172 static char * 173 searchpath_save(char *name) 174 { 175 char *p; 176 char *s; 177 178 p = searchpath(name); 179 if (p == NULL) 180 return NULL; 181 s = alloc(strlen(p) + 1); 182 strcpy(s, p); 183 return s; 184 } 185 186 187 #ifndef CSIDL_COMMON_PROGRAMS 188 # define CSIDL_COMMON_PROGRAMS 0x0017 189 #endif 190 #ifndef CSIDL_COMMON_DESKTOPDIRECTORY 191 # define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019 192 #endif 193 194 /* 195 * Get the path to a requested Windows shell folder. 196 * 197 * Return FAIL on error, OK on success 198 */ 199 int 200 get_shell_folder_path( 201 char *shell_folder_path, 202 const char *shell_folder_name) 203 { 204 /* 205 * The following code was successfully built with make_mvc.mak. 206 * The resulting executable worked on Windows 95, Millennium Edition, and 207 * 2000 Professional. But it was changed after testing... 208 */ 209 LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */ 210 LPMALLOC pMalloc; /* Pointer to an IMalloc interface */ 211 int csidl; 212 int alt_csidl = -1; 213 static int desktop_csidl = -1; 214 static int programs_csidl = -1; 215 int *pcsidl; 216 int r; 217 218 if (strcmp(shell_folder_name, "desktop") == 0) 219 { 220 pcsidl = &desktop_csidl; 221 csidl = CSIDL_COMMON_DESKTOPDIRECTORY; 222 alt_csidl = CSIDL_DESKTOP; 223 } 224 else if (strncmp(shell_folder_name, "Programs", 8) == 0) 225 { 226 pcsidl = &programs_csidl; 227 csidl = CSIDL_COMMON_PROGRAMS; 228 alt_csidl = CSIDL_PROGRAMS; 229 } 230 else 231 { 232 printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n", 233 shell_folder_name); 234 return FAIL; 235 } 236 237 /* Did this stuff before, use the same ID again. */ 238 if (*pcsidl >= 0) 239 { 240 csidl = *pcsidl; 241 alt_csidl = -1; 242 } 243 244 retry: 245 /* Initialize pointer to IMalloc interface */ 246 if (NOERROR != SHGetMalloc(&pMalloc)) 247 { 248 printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n", 249 shell_folder_name); 250 return FAIL; 251 } 252 253 /* Get an ITEMIDLIST corresponding to the folder code */ 254 if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl)) 255 { 256 if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0, 257 alt_csidl, &pidl)) 258 { 259 printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n", 260 shell_folder_name); 261 return FAIL; 262 } 263 csidl = alt_csidl; 264 alt_csidl = -1; 265 } 266 267 /* Translate that ITEMIDLIST to a string */ 268 r = SHGetPathFromIDList(pidl, shell_folder_path); 269 270 /* Free the data associated with pidl */ 271 pMalloc->lpVtbl->Free(pMalloc, pidl); 272 /* Release the IMalloc interface */ 273 pMalloc->lpVtbl->Release(pMalloc); 274 275 if (!r) 276 { 277 if (alt_csidl >= 0) 278 { 279 /* We probably get here for Windows 95: the "all users" 280 * desktop/start menu entry doesn't exist. */ 281 csidl = alt_csidl; 282 alt_csidl = -1; 283 goto retry; 284 } 285 printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n", 286 shell_folder_name); 287 return FAIL; 288 } 289 290 /* If there is an alternative: verify we can write in this directory. 291 * This should cause a retry when the "all users" directory exists but we 292 * are a normal user and can't write there. */ 293 if (alt_csidl >= 0) 294 { 295 char tbuf[BUFSIZE]; 296 FILE *fd; 297 298 strcpy(tbuf, shell_folder_path); 299 strcat(tbuf, "\\vim write test"); 300 fd = fopen(tbuf, "w"); 301 if (fd == NULL) 302 { 303 csidl = alt_csidl; 304 alt_csidl = -1; 305 goto retry; 306 } 307 fclose(fd); 308 unlink(tbuf); 309 } 310 311 /* 312 * Keep the found csidl for next time, so that we don't have to do the 313 * write test every time. 314 */ 315 if (*pcsidl < 0) 316 *pcsidl = csidl; 317 318 if (strncmp(shell_folder_name, "Programs\\", 9) == 0) 319 strcat(shell_folder_path, shell_folder_name + 8); 320 321 return OK; 322 } 323 324 /* 325 * List of targets. The first one (index zero) is used for the default path 326 * for the batch files. 327 */ 328 #define TARGET_COUNT 9 329 330 struct 331 { 332 char *name; /* Vim exe name (without .exe) */ 333 char *batname; /* batch file name */ 334 char *lnkname; /* shortcut file name */ 335 char *exename; /* exe file name */ 336 char *exenamearg; /* exe file name when using exearg */ 337 char *exearg; /* argument for vim.exe or gvim.exe */ 338 char *oldbat; /* path to existing xxx.bat or NULL */ 339 char *oldexe; /* path to existing xxx.exe or NULL */ 340 char batpath[BUFSIZE]; /* path of batch file to create; not 341 created when it's empty */ 342 } targets[TARGET_COUNT] = 343 { 344 {"all", "batch files"}, 345 {"vim", "vim.bat", "Vim.lnk", 346 "vim.exe", "vim.exe", ""}, 347 {"gvim", "gvim.bat", "gVim.lnk", 348 "gvim.exe", "gvim.exe", ""}, 349 {"evim", "evim.bat", "gVim Easy.lnk", 350 "evim.exe", "gvim.exe", "-y"}, 351 {"view", "view.bat", "Vim Read-only.lnk", 352 "view.exe", "vim.exe", "-R"}, 353 {"gview", "gview.bat", "gVim Read-only.lnk", 354 "gview.exe", "gvim.exe", "-R"}, 355 {"vimdiff", "vimdiff.bat", "Vim Diff.lnk", 356 "vimdiff.exe","vim.exe", "-d"}, 357 {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk", 358 "gvimdiff.exe","gvim.exe", "-d"}, 359 {"vimtutor","vimtutor.bat", "Vim tutor.lnk", 360 "vimtutor.bat", "vimtutor.bat", ""}, 361 }; 362 363 #define ICON_COUNT 3 364 char *(icon_names[ICON_COUNT]) = 365 {"gVim " VIM_VERSION_SHORT, 366 "gVim Easy " VIM_VERSION_SHORT, 367 "gVim Read only " VIM_VERSION_SHORT}; 368 char *(icon_link_names[ICON_COUNT]) = 369 {"gVim " VIM_VERSION_SHORT ".lnk", 370 "gVim Easy " VIM_VERSION_SHORT ".lnk", 371 "gVim Read only " VIM_VERSION_SHORT ".lnk"}; 372 373 /* This is only used for dosinst.c. */ 374 #if defined(DOSINST) 375 /* 376 * Run an external command and wait for it to finish. 377 */ 378 static void 379 run_command(char *cmd) 380 { 381 char *cmd_path; 382 char cmd_buf[BUFSIZE * 2 + 35]; 383 char *p; 384 385 /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an 386 * executable (start.exe) like in Win9x. */ 387 cmd_path = searchpath_save("cmd.exe"); 388 if (cmd_path != NULL) 389 { 390 /* There is a cmd.exe, so this might be Windows NT. If it is, 391 * we need to call cmd.exe explicitly. If it is a later OS, 392 * calling cmd.exe won't hurt if it is present. 393 * Also, "start" on NT expects a window title argument. 394 */ 395 /* Replace the slashes with backslashes. */ 396 while ((p = strchr(cmd_path, '/')) != NULL) 397 *p = '\\'; 398 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd); 399 free(cmd_path); 400 } 401 else 402 { 403 /* No cmd.exe, just make the call and let the system handle it. */ 404 sprintf(cmd_buf, "start /w %s", cmd); 405 } 406 system(cmd_buf); 407 } 408 #endif 409 410 /* 411 * Append a backslash to "name" if there isn't one yet. 412 */ 413 void 414 add_pathsep(char *name) 415 { 416 int len = strlen(name); 417 418 if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/') 419 strcat(name, "\\"); 420 } 421 422 /* 423 * The normal chdir() does not change the default drive. This one does. 424 */ 425 /*ARGSUSED*/ 426 int 427 change_drive(int drive) 428 { 429 char temp[3] = "-:"; 430 temp[0] = (char)(drive + 'A' - 1); 431 return !SetCurrentDirectory(temp); 432 } 433 434 /* 435 * Change directory to "path". 436 * Return 0 for success, -1 for failure. 437 */ 438 int 439 mch_chdir(char *path) 440 { 441 if (path[0] == NUL) /* just checking... */ 442 return 0; 443 if (path[1] == ':') /* has a drive name */ 444 { 445 if (change_drive(mytoupper(path[0]) - 'A' + 1)) 446 return -1; /* invalid drive name */ 447 path += 2; 448 } 449 if (*path == NUL) /* drive name only */ 450 return 0; 451 return chdir(path); /* let the normal chdir() do the rest */ 452 } 453 454 /* 455 * Expand the executable name into a full path name. 456 */ 457 static char * 458 my_fullpath(char *buf, char *fname, int len) 459 { 460 /* Only GetModuleFileName() will get the long file name path. 461 * GetFullPathName() may still use the short (FAT) name. */ 462 DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len); 463 464 return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL; 465 } 466 467 /* 468 * Remove the tail from a file or directory name. 469 * Puts a NUL on the last '/' or '\'. 470 */ 471 static void 472 remove_tail(char *path) 473 { 474 int i; 475 476 for (i = strlen(path) - 1; i > 0; --i) 477 if (path[i] == '/' || path[i] == '\\') 478 { 479 path[i] = NUL; 480 break; 481 } 482 } 483 484 485 char installdir[MAX_PATH-9]; /* top of the installation dir, where the 486 install.exe is located, E.g.: 487 "c:\vim\vim60" */ 488 int runtimeidx; /* index in installdir[] where "vim60" starts */ 489 char *sysdrive; /* system drive or "c:\" */ 490 491 /* 492 * Setup for using this program. 493 * Sets "installdir[]". 494 */ 495 static void 496 do_inits(char **argv) 497 { 498 /* Find out the full path of our executable. */ 499 if (my_fullpath(installdir, argv[0], sizeof(installdir)) == NULL) 500 { 501 printf("ERROR: Cannot get name of executable\n"); 502 myexit(1); 503 } 504 /* remove the tail, the executable name "install.exe" */ 505 remove_tail(installdir); 506 507 /* change to the installdir */ 508 mch_chdir(installdir); 509 510 /* Find the system drive. Only used for searching the Vim executable, not 511 * very important. */ 512 sysdrive = getenv("SYSTEMDRIVE"); 513 if (sysdrive == NULL || *sysdrive == NUL) 514 sysdrive = "C:\\"; 515 } 516