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