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