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 * 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 # ifndef __CYGWIN__ 30 # include <direct.h> 31 # endif 32 33 # if defined(_WIN64) || defined(WIN32) 34 # define WIN3264 35 # include <windows.h> 36 # include <shlobj.h> 37 # else 38 # include <dir.h> 39 # include <bios.h> 40 # include <dos.h> 41 # endif 42 #endif 43 44 #ifdef UNIX_LINT 45 /* Running lint on Unix: Some things are missing. */ 46 char *searchpath(char *name); 47 #endif 48 49 #if defined(DJGPP) || defined(UNIX_LINT) 50 # include <unistd.h> 51 # include <errno.h> 52 #endif 53 54 #include "version.h" 55 56 #if defined(DJGPP) || defined(UNIX_LINT) 57 # define vim_mkdir(x, y) mkdir((char *)(x), y) 58 #else 59 # if defined(WIN3264) && !defined(__BORLANDC__) 60 # define vim_mkdir(x, y) _mkdir((char *)(x)) 61 # else 62 # define vim_mkdir(x, y) mkdir((char *)(x)) 63 # endif 64 #endif 65 66 #ifndef DJGPP 67 # define sleep(n) Sleep((n) * 1000) 68 #endif 69 70 /* ---------------------------------------- */ 71 72 73 #define BUFSIZE 512 /* long enough to hold a file name path */ 74 #define NUL 0 75 76 #define FAIL 0 77 #define OK 1 78 79 #ifndef FALSE 80 # define FALSE 0 81 #endif 82 #ifndef TRUE 83 # define TRUE 1 84 #endif 85 86 /* 87 * Modern way of creating registry entries, also works on 64 bit windows when 88 * compiled as a 32 bit program. 89 */ 90 # ifndef KEY_WOW64_64KEY 91 # define KEY_WOW64_64KEY 0x0100 92 # endif 93 94 #define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT 95 96 int interactive; /* non-zero when running interactively */ 97 98 /* 99 * Call malloc() and exit when out of memory. 100 */ 101 static void * 102 alloc(int len) 103 { 104 char *s; 105 106 s = malloc(len); 107 if (s == NULL) 108 { 109 printf("ERROR: out of memory\n"); 110 exit(1); 111 } 112 return (void *)s; 113 } 114 115 /* 116 * The toupper() in Bcc 5.5 doesn't work, use our own implementation. 117 */ 118 static int 119 mytoupper(int c) 120 { 121 if (c >= 'a' && c <= 'z') 122 return c - 'a' + 'A'; 123 return c; 124 } 125 126 static void 127 myexit(int n) 128 { 129 if (!interactive) 130 { 131 /* Present a prompt, otherwise error messages can't be read. */ 132 printf("Press Enter to continue\n"); 133 rewind(stdin); 134 (void)getchar(); 135 } 136 exit(n); 137 } 138 139 #ifdef WIN3264 140 /* This symbol is not defined in older versions of the SDK or Visual C++ */ 141 142 #ifndef VER_PLATFORM_WIN32_WINDOWS 143 # define VER_PLATFORM_WIN32_WINDOWS 1 144 #endif 145 146 static DWORD g_PlatformId; 147 148 /* 149 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or 150 * VER_PLATFORM_WIN32_WINDOWS (Win95). 151 */ 152 static void 153 PlatformId(void) 154 { 155 static int done = FALSE; 156 157 if (!done) 158 { 159 OSVERSIONINFO ovi; 160 161 ovi.dwOSVersionInfoSize = sizeof(ovi); 162 GetVersionEx(&ovi); 163 164 g_PlatformId = ovi.dwPlatformId; 165 done = TRUE; 166 } 167 } 168 169 # ifdef __BORLANDC__ 170 /* Borland defines its own searchpath() in dir.h */ 171 # include <dir.h> 172 # else 173 static char * 174 searchpath(char *name) 175 { 176 static char widename[2 * BUFSIZE]; 177 static char location[2 * BUFSIZE + 2]; 178 179 /* There appears to be a bug in FindExecutableA() on Windows NT. 180 * Use FindExecutableW() instead... */ 181 PlatformId(); 182 if (g_PlatformId == VER_PLATFORM_WIN32_NT) 183 { 184 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, 185 (LPWSTR)widename, BUFSIZE); 186 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", 187 (LPWSTR)location) > (HINSTANCE)32) 188 { 189 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, 190 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); 191 return widename; 192 } 193 } 194 else 195 { 196 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"", 197 (LPTSTR)location) > (HINSTANCE)32) 198 return location; 199 } 200 return NULL; 201 } 202 # endif 203 #endif 204 205 /* 206 * Call searchpath() and save the result in allocated memory, or return NULL. 207 */ 208 static char * 209 searchpath_save(char *name) 210 { 211 char *p; 212 char *s; 213 214 p = searchpath(name); 215 if (p == NULL) 216 return NULL; 217 s = alloc(strlen(p) + 1); 218 strcpy(s, p); 219 return s; 220 } 221 222 #ifdef WIN3264 223 224 #ifndef CSIDL_COMMON_PROGRAMS 225 # define CSIDL_COMMON_PROGRAMS 0x0017 226 #endif 227 #ifndef CSIDL_COMMON_DESKTOPDIRECTORY 228 # define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019 229 #endif 230 231 /* 232 * Get the path to a requested Windows shell folder. 233 * 234 * Return FAIL on error, OK on success 235 */ 236 int 237 get_shell_folder_path( 238 char *shell_folder_path, 239 const char *shell_folder_name) 240 { 241 /* 242 * The following code was successfully built with make_mvc.mak. 243 * The resulting executable worked on Windows 95, Millennium Edition, and 244 * 2000 Professional. But it was changed after testing... 245 */ 246 LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */ 247 LPMALLOC pMalloc; /* Pointer to an IMalloc interface */ 248 int csidl; 249 int alt_csidl = -1; 250 static int desktop_csidl = -1; 251 static int programs_csidl = -1; 252 int *pcsidl; 253 int r; 254 255 if (strcmp(shell_folder_name, "desktop") == 0) 256 { 257 pcsidl = &desktop_csidl; 258 csidl = CSIDL_COMMON_DESKTOPDIRECTORY; 259 alt_csidl = CSIDL_DESKTOP; 260 } 261 else if (strncmp(shell_folder_name, "Programs", 8) == 0) 262 { 263 pcsidl = &programs_csidl; 264 csidl = CSIDL_COMMON_PROGRAMS; 265 alt_csidl = CSIDL_PROGRAMS; 266 } 267 else 268 { 269 printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n", 270 shell_folder_name); 271 return FAIL; 272 } 273 274 /* Did this stuff before, use the same ID again. */ 275 if (*pcsidl >= 0) 276 { 277 csidl = *pcsidl; 278 alt_csidl = -1; 279 } 280 281 retry: 282 /* Initialize pointer to IMalloc interface */ 283 if (NOERROR != SHGetMalloc(&pMalloc)) 284 { 285 printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n", 286 shell_folder_name); 287 return FAIL; 288 } 289 290 /* Get an ITEMIDLIST corresponding to the folder code */ 291 if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl)) 292 { 293 if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0, 294 alt_csidl, &pidl)) 295 { 296 printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n", 297 shell_folder_name); 298 return FAIL; 299 } 300 csidl = alt_csidl; 301 alt_csidl = -1; 302 } 303 304 /* Translate that ITEMIDLIST to a string */ 305 r = SHGetPathFromIDList(pidl, shell_folder_path); 306 307 /* Free the data associated with pidl */ 308 pMalloc->lpVtbl->Free(pMalloc, pidl); 309 /* Release the IMalloc interface */ 310 pMalloc->lpVtbl->Release(pMalloc); 311 312 if (!r) 313 { 314 if (alt_csidl >= 0) 315 { 316 /* We probably get here for Windows 95: the "all users" 317 * desktop/start menu entry doesn't exist. */ 318 csidl = alt_csidl; 319 alt_csidl = -1; 320 goto retry; 321 } 322 printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n", 323 shell_folder_name); 324 return FAIL; 325 } 326 327 /* If there is an alternative: verify we can write in this directory. 328 * This should cause a retry when the "all users" directory exists but we 329 * are a normal user and can't write there. */ 330 if (alt_csidl >= 0) 331 { 332 char tbuf[BUFSIZE]; 333 FILE *fd; 334 335 strcpy(tbuf, shell_folder_path); 336 strcat(tbuf, "\\vim write test"); 337 fd = fopen(tbuf, "w"); 338 if (fd == NULL) 339 { 340 csidl = alt_csidl; 341 alt_csidl = -1; 342 goto retry; 343 } 344 fclose(fd); 345 unlink(tbuf); 346 } 347 348 /* 349 * Keep the found csidl for next time, so that we don't have to do the 350 * write test every time. 351 */ 352 if (*pcsidl < 0) 353 *pcsidl = csidl; 354 355 if (strncmp(shell_folder_name, "Programs\\", 9) == 0) 356 strcat(shell_folder_path, shell_folder_name + 8); 357 358 return OK; 359 } 360 #endif 361 362 /* 363 * List of targets. The first one (index zero) is used for the default path 364 * for the batch files. 365 */ 366 #define TARGET_COUNT 9 367 368 struct 369 { 370 char *name; /* Vim exe name (without .exe) */ 371 char *batname; /* batch file name */ 372 char *lnkname; /* shortcut file name */ 373 char *exename; /* exe file name */ 374 char *exenamearg; /* exe file name when using exearg */ 375 char *exearg; /* argument for vim.exe or gvim.exe */ 376 char *oldbat; /* path to existing xxx.bat or NULL */ 377 char *oldexe; /* path to existing xxx.exe or NULL */ 378 char batpath[BUFSIZE]; /* path of batch file to create; not 379 created when it's empty */ 380 } targets[TARGET_COUNT] = 381 { 382 {"all", "batch files"}, 383 {"vim", "vim.bat", "Vim.lnk", 384 "vim.exe", "vim.exe", ""}, 385 {"gvim", "gvim.bat", "gVim.lnk", 386 "gvim.exe", "gvim.exe", ""}, 387 {"evim", "evim.bat", "gVim Easy.lnk", 388 "evim.exe", "gvim.exe", "-y"}, 389 {"view", "view.bat", "Vim Read-only.lnk", 390 "view.exe", "vim.exe", "-R"}, 391 {"gview", "gview.bat", "gVim Read-only.lnk", 392 "gview.exe", "gvim.exe", "-R"}, 393 {"vimdiff", "vimdiff.bat", "Vim Diff.lnk", 394 "vimdiff.exe","vim.exe", "-d"}, 395 {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk", 396 "gvimdiff.exe","gvim.exe", "-d"}, 397 {"vimtutor","vimtutor.bat", "Vim tutor.lnk", 398 "vimtutor.bat", "vimtutor.bat", ""}, 399 }; 400 401 #define ICON_COUNT 3 402 char *(icon_names[ICON_COUNT]) = 403 {"gVim " VIM_VERSION_SHORT, 404 "gVim Easy " VIM_VERSION_SHORT, 405 "gVim Read only " VIM_VERSION_SHORT}; 406 char *(icon_link_names[ICON_COUNT]) = 407 {"gVim " VIM_VERSION_SHORT ".lnk", 408 "gVim Easy " VIM_VERSION_SHORT ".lnk", 409 "gVim Read only " VIM_VERSION_SHORT ".lnk"}; 410 411 /* This is only used for dosinst.c when WIN3264 is defined and for uninstal.c 412 * when not being able to directly access registry entries. */ 413 #if (defined(DOSINST) && defined(WIN3264)) \ 414 || (!defined(DOSINST) && !defined(WIN3264)) 415 /* 416 * Run an external command and wait for it to finish. 417 */ 418 static void 419 run_command(char *cmd) 420 { 421 char *cmd_path; 422 char cmd_buf[BUFSIZE]; 423 char *p; 424 425 /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an 426 * executable (start.exe) like in Win9x. DJGPP, being a DOS program, 427 * is given the COMSPEC command.com by WinNT, so we have to find 428 * cmd.exe manually and use it. */ 429 cmd_path = searchpath_save("cmd.exe"); 430 if (cmd_path != NULL) 431 { 432 /* There is a cmd.exe, so this might be Windows NT. If it is, 433 * we need to call cmd.exe explicitly. If it is a later OS, 434 * calling cmd.exe won't hurt if it is present. 435 * Also, "start" on NT expects a window title argument. 436 */ 437 /* Replace the slashes with backslashes. */ 438 while ((p = strchr(cmd_path, '/')) != NULL) 439 *p = '\\'; 440 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd); 441 free(cmd_path); 442 } 443 else 444 { 445 /* No cmd.exe, just make the call and let the system handle it. */ 446 sprintf(cmd_buf, "start /w %s", cmd); 447 } 448 system(cmd_buf); 449 } 450 #endif 451 452 /* 453 * Append a backslash to "name" if there isn't one yet. 454 */ 455 static void 456 add_pathsep(char *name) 457 { 458 int len = strlen(name); 459 460 if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/') 461 strcat(name, "\\"); 462 } 463 464 /* 465 * The normal chdir() does not change the default drive. This one does. 466 */ 467 /*ARGSUSED*/ 468 int 469 change_drive(int drive) 470 { 471 #ifdef WIN3264 472 char temp[3] = "-:"; 473 temp[0] = (char)(drive + 'A' - 1); 474 return !SetCurrentDirectory(temp); 475 #else 476 # ifndef UNIX_LINT 477 union REGS regs; 478 479 regs.h.ah = 0x0e; 480 regs.h.dl = drive - 1; 481 intdos(®s, ®s); /* set default drive */ 482 regs.h.ah = 0x19; 483 intdos(®s, ®s); /* get default drive */ 484 if (regs.h.al == drive - 1) 485 return 0; 486 # endif 487 return -1; 488 #endif 489 } 490 491 /* 492 * Change directory to "path". 493 * Return 0 for success, -1 for failure. 494 */ 495 int 496 mch_chdir(char *path) 497 { 498 if (path[0] == NUL) /* just checking... */ 499 return 0; 500 if (path[1] == ':') /* has a drive name */ 501 { 502 if (change_drive(mytoupper(path[0]) - 'A' + 1)) 503 return -1; /* invalid drive name */ 504 path += 2; 505 } 506 if (*path == NUL) /* drive name only */ 507 return 0; 508 return chdir(path); /* let the normal chdir() do the rest */ 509 } 510 511 /* 512 * Expand the executable name into a full path name. 513 */ 514 #if defined(__BORLANDC__) && !defined(WIN3264) 515 516 /* Only Borland C++ has this. */ 517 # define my_fullpath(b, n, l) _fullpath(b, n, l) 518 519 #else 520 static char * 521 my_fullpath(char *buf, char *fname, int len) 522 { 523 # ifdef WIN3264 524 /* Only GetModuleFileName() will get the long file name path. 525 * GetFullPathName() may still use the short (FAT) name. */ 526 DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len); 527 528 return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL; 529 # else 530 char olddir[BUFSIZE]; 531 char *p, *q; 532 int c; 533 char *retval = buf; 534 535 if (strchr(fname, ':') != NULL) /* already expanded */ 536 { 537 strncpy(buf, fname, len); 538 } 539 else 540 { 541 *buf = NUL; 542 /* 543 * change to the directory for a moment, 544 * and then do the getwd() (and get back to where we were). 545 * This will get the correct path name with "../" things. 546 */ 547 p = strrchr(fname, '/'); 548 q = strrchr(fname, '\\'); 549 if (q != NULL && (p == NULL || q > p)) 550 p = q; 551 q = strrchr(fname, ':'); 552 if (q != NULL && (p == NULL || q > p)) 553 p = q; 554 if (p != NULL) 555 { 556 if (getcwd(olddir, BUFSIZE) == NULL) 557 { 558 p = NULL; /* can't get current dir: don't chdir */ 559 retval = NULL; 560 } 561 else 562 { 563 if (p == fname) /* /fname */ 564 q = p + 1; /* -> / */ 565 else if (q + 1 == p) /* ... c:\foo */ 566 q = p + 1; /* -> c:\ */ 567 else /* but c:\foo\bar */ 568 q = p; /* -> c:\foo */ 569 570 c = *q; /* truncate at start of fname */ 571 *q = NUL; 572 if (mch_chdir(fname)) /* change to the directory */ 573 retval = NULL; 574 else 575 { 576 fname = q; 577 if (c == '\\') /* if we cut the name at a */ 578 fname++; /* '\', don't add it again */ 579 } 580 *q = c; 581 } 582 } 583 if (getcwd(buf, len) == NULL) 584 { 585 retval = NULL; 586 *buf = NUL; 587 } 588 /* 589 * Concatenate the file name to the path. 590 */ 591 if (strlen(buf) + strlen(fname) >= len - 1) 592 { 593 printf("ERROR: File name too long!\n"); 594 myexit(1); 595 } 596 add_pathsep(buf); 597 strcat(buf, fname); 598 if (p) 599 mch_chdir(olddir); 600 } 601 602 /* Replace forward slashes with backslashes, required for the path to a 603 * command. */ 604 while ((p = strchr(buf, '/')) != NULL) 605 *p = '\\'; 606 607 return retval; 608 # endif 609 } 610 #endif 611 612 /* 613 * Remove the tail from a file or directory name. 614 * Puts a NUL on the last '/' or '\'. 615 */ 616 static void 617 remove_tail(char *path) 618 { 619 int i; 620 621 for (i = strlen(path) - 1; i > 0; --i) 622 if (path[i] == '/' || path[i] == '\\') 623 { 624 path[i] = NUL; 625 break; 626 } 627 } 628 629 630 char installdir[BUFSIZE]; /* top of the installation dir, where the 631 install.exe is located, E.g.: 632 "c:\vim\vim60" */ 633 int runtimeidx; /* index in installdir[] where "vim60" starts */ 634 char *sysdrive; /* system drive or "c:\" */ 635 636 /* 637 * Setup for using this program. 638 * Sets "installdir[]". 639 */ 640 static void 641 do_inits(char **argv) 642 { 643 #ifdef DJGPP 644 /* 645 * Use Long File Names by default, if $LFN not set. 646 */ 647 if (getenv("LFN") == NULL) 648 putenv("LFN=y"); 649 #endif 650 651 /* Find out the full path of our executable. */ 652 if (my_fullpath(installdir, argv[0], BUFSIZE) == NULL) 653 { 654 printf("ERROR: Cannot get name of executable\n"); 655 myexit(1); 656 } 657 /* remove the tail, the executable name "install.exe" */ 658 remove_tail(installdir); 659 660 /* change to the installdir */ 661 mch_chdir(installdir); 662 663 /* Find the system drive. Only used for searching the Vim executable, not 664 * very important. */ 665 sysdrive = getenv("SYSTEMDRIVE"); 666 if (sysdrive == NULL || *sysdrive == NUL) 667 sysdrive = "C:\\"; 668 } 669