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 * dosinst.c: Install program for Vim on MS-DOS and MS-Windows 12 * 13 * Compile with Make_mvc.mak, Make_cyg.mak or Make_ming.mak. 14 */ 15 16 /* 17 * Include common code for dosinst.c and uninstall.c. 18 */ 19 #define DOSINST 20 #include "dosinst.h" 21 #include <io.h> 22 23 #define GVIMEXT64_PATH "GvimExt64\\gvimext.dll" 24 #define GVIMEXT32_PATH "GvimExt32\\gvimext.dll" 25 26 // Macro to do an error check I was typing over and over 27 #define CHECK_REG_ERROR(code) \ 28 do { \ 29 if (code != ERROR_SUCCESS) \ 30 { \ 31 printf("%ld error number: %ld\n", (long)__LINE__, (long)code); \ 32 return 1; \ 33 } \ 34 } while (0) 35 36 int has_vim = 0; // installable vim.exe exists 37 int has_gvim = 0; // installable gvim.exe exists 38 39 char oldvimrc[BUFSIZE]; // name of existing vimrc file 40 char vimrc[BUFSIZE]; // name of vimrc file to create 41 42 char *default_bat_dir = NULL; // when not NULL, use this as the default 43 // directory to write .bat files in 44 char *default_vim_dir = NULL; // when not NULL, use this as the default 45 // install dir for NSIS 46 47 /* 48 * Structure used for each choice the user can make. 49 */ 50 struct choice 51 { 52 int active; // non-zero when choice is active 53 char *text; // text displayed for this choice 54 void (*changefunc)(int idx); // function to change this choice 55 int arg; // argument for function 56 void (*installfunc)(int idx); // function to install this choice 57 }; 58 59 struct choice choices[30]; // choices the user can make 60 int choice_count = 0; // number of choices available 61 62 #define TABLE_SIZE(s) (int)(sizeof(s) / sizeof(*s)) 63 64 enum 65 { 66 compat_vi = 1, 67 compat_vim, 68 compat_some_enhancements, 69 compat_all_enhancements 70 }; 71 char *(compat_choices[]) = 72 { 73 "\nChoose the default way to run Vim:", 74 "Vi compatible", 75 "Vim default", 76 "with some Vim enhancements", 77 "with syntax highlighting and other features switched on", 78 }; 79 int compat_choice = (int)compat_all_enhancements; 80 char *compat_text = "- run Vim %s"; 81 82 enum 83 { 84 remap_no = 1, 85 remap_win 86 }; 87 char *(remap_choices[]) = 88 { 89 "\nChoose:", 90 "Do not remap keys for Windows behavior", 91 "Remap a few keys for Windows behavior (CTRL-V, CTRL-C, CTRL-F, etc)", 92 }; 93 int remap_choice = (int)remap_no; 94 char *remap_text = "- %s"; 95 96 enum 97 { 98 mouse_xterm = 1, 99 mouse_mswin, 100 mouse_default 101 }; 102 char *(mouse_choices[]) = 103 { 104 "\nChoose the way how Vim uses the mouse:", 105 "right button extends selection (the Unix way)", 106 "right button has a popup menu, left button starts select mode (the Windows way)", 107 "right button has a popup menu, left button starts visual mode", 108 }; 109 int mouse_choice = (int)mouse_default; 110 char *mouse_text = "- The mouse %s"; 111 112 enum 113 { 114 vimfiles_dir_none = 1, 115 vimfiles_dir_vim, 116 vimfiles_dir_home 117 }; 118 static char *(vimfiles_dir_choices[]) = 119 { 120 "\nCreate plugin directories:", 121 "No", 122 "In the VIM directory", 123 "In your HOME directory", 124 }; 125 126 // non-zero when selected to install the popup menu entry. 127 static int install_popup = 0; 128 129 // non-zero when selected to install the "Open with" entry. 130 static int install_openwith = 0; 131 132 // non-zero when need to add an uninstall entry in the registry 133 static int need_uninstall_entry = 0; 134 135 /* 136 * Definitions of the directory name (under $VIM) of the vimfiles directory 137 * and its subdirectories: 138 */ 139 static char *(vimfiles_subdirs[]) = 140 { 141 "colors", 142 "compiler", 143 "doc", 144 "ftdetect", 145 "ftplugin", 146 "indent", 147 "keymap", 148 "plugin", 149 "syntax", 150 }; 151 152 /* 153 * Obtain a choice from a table. 154 * First entry is a question, others are choices. 155 */ 156 static int 157 get_choice(char **table, int entries) 158 { 159 int answer; 160 int idx; 161 char dummy[100]; 162 163 do 164 { 165 for (idx = 0; idx < entries; ++idx) 166 { 167 if (idx) 168 printf("%2d ", idx); 169 puts(table[idx]); 170 } 171 printf("Choice: "); 172 if (scanf("%d", &answer) != 1) 173 { 174 scanf("%99s", dummy); 175 answer = 0; 176 } 177 } 178 while (answer < 1 || answer >= entries); 179 180 return answer; 181 } 182 183 /* 184 * Check if the user unpacked the archives properly. 185 * Sets "runtimeidx". 186 */ 187 static void 188 check_unpack(void) 189 { 190 char buf[BUFSIZE]; 191 FILE *fd; 192 struct stat st; 193 194 // check for presence of the correct version number in installdir[] 195 runtimeidx = strlen(installdir) - strlen(VIM_VERSION_NODOT); 196 if (runtimeidx <= 0 197 || stricmp(installdir + runtimeidx, VIM_VERSION_NODOT) != 0 198 || (installdir[runtimeidx - 1] != '/' 199 && installdir[runtimeidx - 1] != '\\')) 200 { 201 printf("ERROR: Install program not in directory \"%s\"\n", 202 VIM_VERSION_NODOT); 203 printf("This program can only work when it is located in its original directory\n"); 204 myexit(1); 205 } 206 207 // check if filetype.vim is present, which means the runtime archive has 208 // been unpacked 209 sprintf(buf, "%s\\filetype.vim", installdir); 210 if (stat(buf, &st) < 0) 211 { 212 printf("ERROR: Cannot find filetype.vim in \"%s\"\n", installdir); 213 printf("It looks like you did not unpack the runtime archive.\n"); 214 printf("You must unpack the runtime archive \"vim%srt.zip\" before installing.\n", 215 VIM_VERSION_NODOT + 3); 216 myexit(1); 217 } 218 219 // Check if vim.exe or gvim.exe is in the current directory. 220 if ((fd = fopen("gvim.exe", "r")) != NULL) 221 { 222 fclose(fd); 223 has_gvim = 1; 224 } 225 if ((fd = fopen("vim.exe", "r")) != NULL) 226 { 227 fclose(fd); 228 has_vim = 1; 229 } 230 if (!has_gvim && !has_vim) 231 { 232 printf("ERROR: Cannot find any Vim executables in \"%s\"\n\n", 233 installdir); 234 myexit(1); 235 } 236 } 237 238 /* 239 * Compare paths "p[plen]" to "q[qlen]". Return 0 if they match. 240 * Ignores case and differences between '/' and '\'. 241 * "plen" and "qlen" can be negative, strlen() is used then. 242 */ 243 static int 244 pathcmp(char *p, int plen, char *q, int qlen) 245 { 246 int i; 247 248 if (plen < 0) 249 plen = strlen(p); 250 if (qlen < 0) 251 qlen = strlen(q); 252 for (i = 0; ; ++i) 253 { 254 // End of "p": check if "q" also ends or just has a slash. 255 if (i == plen) 256 { 257 if (i == qlen) // match 258 return 0; 259 if (i == qlen - 1 && (q[i] == '\\' || q[i] == '/')) 260 return 0; // match with trailing slash 261 return 1; // no match 262 } 263 264 // End of "q": check if "p" also ends or just has a slash. 265 if (i == qlen) 266 { 267 if (i == plen) // match 268 return 0; 269 if (i == plen - 1 && (p[i] == '\\' || p[i] == '/')) 270 return 0; // match with trailing slash 271 return 1; // no match 272 } 273 274 if (!(mytoupper(p[i]) == mytoupper(q[i]) 275 || ((p[i] == '/' || p[i] == '\\') 276 && (q[i] == '/' || q[i] == '\\')))) 277 return 1; // no match 278 } 279 //NOTREACHED 280 } 281 282 /* 283 * If the executable "**destination" is in the install directory, find another 284 * one in $PATH. 285 * On input "**destination" is the path of an executable in allocated memory 286 * (or NULL). 287 * "*destination" is set to NULL or the location of the file. 288 */ 289 static void 290 findoldfile(char **destination) 291 { 292 char *bp = *destination; 293 size_t indir_l = strlen(installdir); 294 char *cp; 295 char *tmpname; 296 char *farname; 297 298 /* 299 * No action needed if exe not found or not in this directory. 300 */ 301 if (bp == NULL || strnicmp(bp, installdir, indir_l) != 0) 302 return; 303 cp = bp + indir_l; 304 if (strchr("/\\", *cp++) == NULL 305 || strchr(cp, '\\') != NULL 306 || strchr(cp, '/') != NULL) 307 return; 308 309 tmpname = alloc(strlen(cp) + 1); 310 strcpy(tmpname, cp); 311 tmpname[strlen(tmpname) - 1] = 'x'; // .exe -> .exx 312 313 if (access(tmpname, 0) == 0) 314 { 315 printf("\nERROR: %s and %s clash. Remove or rename %s.\n", 316 tmpname, cp, tmpname); 317 myexit(1); 318 } 319 320 if (rename(cp, tmpname) != 0) 321 { 322 printf("\nERROR: failed to rename %s to %s: %s\n", 323 cp, tmpname, strerror(0)); 324 myexit(1); 325 } 326 327 farname = searchpath_save(cp); 328 329 if (rename(tmpname, cp) != 0) 330 { 331 printf("\nERROR: failed to rename %s back to %s: %s\n", 332 tmpname, cp, strerror(0)); 333 myexit(1); 334 } 335 336 free(*destination); 337 free(tmpname); 338 *destination = farname; 339 } 340 341 /* 342 * Check if there is a vim.[exe|bat|, gvim.[exe|bat|, etc. in the path. 343 * When "check_bat_only" is TRUE, only find "default_bat_dir". 344 */ 345 static void 346 find_bat_exe(int check_bat_only) 347 { 348 int i; 349 350 // avoid looking in the "installdir" by chdir to system root 351 mch_chdir(sysdrive); 352 mch_chdir("\\"); 353 354 for (i = 1; i < TARGET_COUNT; ++i) 355 { 356 targets[i].oldbat = searchpath_save(targets[i].batname); 357 if (!check_bat_only) 358 targets[i].oldexe = searchpath_save(targets[i].exename); 359 360 if (default_bat_dir == NULL && targets[i].oldbat != NULL) 361 { 362 default_bat_dir = alloc(strlen(targets[i].oldbat) + 1); 363 strcpy(default_bat_dir, targets[i].oldbat); 364 remove_tail(default_bat_dir); 365 } 366 if (check_bat_only && targets[i].oldbat != NULL) 367 { 368 free(targets[i].oldbat); 369 targets[i].oldbat = NULL; 370 } 371 } 372 373 mch_chdir(installdir); 374 } 375 376 /* 377 * Get the value of $VIMRUNTIME or $VIM and write it in $TEMP/vimini.ini, so 378 * that NSIS can read it. 379 * When not set, use the directory of a previously installed Vim. 380 */ 381 static void 382 get_vim_env(void) 383 { 384 char *vim; 385 char buf[BUFSIZE]; 386 FILE *fd; 387 char fname[BUFSIZE]; 388 389 // First get $VIMRUNTIME. If it's set, remove the tail. 390 vim = getenv("VIMRUNTIME"); 391 if (vim != NULL && *vim != 0 && strlen(vim) < sizeof(buf)) 392 { 393 strcpy(buf, vim); 394 remove_tail(buf); 395 vim = buf; 396 } 397 else 398 { 399 vim = getenv("VIM"); 400 if (vim == NULL || *vim == 0) 401 { 402 // Use the directory from an old uninstall entry. 403 if (default_vim_dir != NULL) 404 vim = default_vim_dir; 405 else 406 // Let NSIS know there is no default, it should use 407 // $PROGRAMFILES. 408 vim = ""; 409 } 410 } 411 412 // NSIS also uses GetTempPath(), thus we should get the same directory 413 // name as where NSIS will look for vimini.ini. 414 GetTempPath(sizeof(fname) - 12, fname); 415 add_pathsep(fname); 416 strcat(fname, "vimini.ini"); 417 418 fd = fopen(fname, "w"); 419 if (fd != NULL) 420 { 421 // Make it look like an .ini file, so that NSIS can read it with a 422 // ReadINIStr command. 423 fprintf(fd, "[vimini]\n"); 424 fprintf(fd, "dir=\"%s\"\n", vim); 425 fclose(fd); 426 } 427 else 428 { 429 printf("Failed to open %s\n", fname); 430 sleep(2); 431 } 432 } 433 434 static int num_windows; 435 436 /* 437 * Callback used for EnumWindows(): 438 * Count the window if the title looks like it is for the uninstaller. 439 */ 440 //ARGSUSED 441 static BOOL CALLBACK 442 window_cb(HWND hwnd, LPARAM lparam) 443 { 444 char title[256]; 445 446 title[0] = 0; 447 GetWindowText(hwnd, title, 256); 448 if (strstr(title, "Vim ") != NULL && strstr(title, " Uninstall") != NULL) 449 ++num_windows; 450 return TRUE; 451 } 452 453 /* 454 * Run the uninstaller silently. 455 */ 456 static int 457 run_silent_uninstall(char *uninst_exe) 458 { 459 char vimrt_dir[BUFSIZE]; 460 char temp_uninst[BUFSIZE]; 461 char temp_dir[MAX_PATH]; 462 char buf[BUFSIZE * 2 + 10]; 463 int i; 464 DWORD tick; 465 466 strcpy(vimrt_dir, uninst_exe); 467 remove_tail(vimrt_dir); 468 469 if (!GetTempPath(sizeof(temp_dir), temp_dir)) 470 return FAIL; 471 472 // Copy the uninstaller to a temporary exe. 473 tick = GetTickCount(); 474 for (i = 0; ; i++) 475 { 476 sprintf(temp_uninst, "%s\\vimun%04X.exe", temp_dir, 477 (unsigned int)((i + tick) & 0xFFFF)); 478 if (CopyFile(uninst_exe, temp_uninst, TRUE)) 479 break; 480 if (GetLastError() != ERROR_FILE_EXISTS) 481 return FAIL; 482 if (i == 65535) 483 return FAIL; 484 } 485 486 // Run the copied uninstaller silently. 487 if (strchr(temp_uninst, ' ') != NULL) 488 sprintf(buf, "\"%s\" /S _?=%s", temp_uninst, vimrt_dir); 489 else 490 sprintf(buf, "%s /S _?=%s", temp_uninst, vimrt_dir); 491 run_command(buf); 492 493 DeleteFile(temp_uninst); 494 return OK; 495 } 496 497 /* 498 * Check for already installed Vims. 499 * Return non-zero when found one. 500 */ 501 static int 502 uninstall_check(int skip_question) 503 { 504 HKEY key_handle; 505 HKEY uninstall_key_handle; 506 char *uninstall_key = "software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; 507 char subkey_name_buff[BUFSIZE]; 508 char temp_string_buffer[BUFSIZE-2]; 509 DWORD local_bufsize; 510 FILETIME temp_pfiletime; 511 DWORD key_index; 512 char input; 513 long code; 514 DWORD value_type; 515 DWORD orig_num_keys; 516 DWORD new_num_keys; 517 DWORD allow_silent; 518 int foundone = 0; 519 520 code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, uninstall_key, 0, 521 KEY_WOW64_64KEY | KEY_READ, &key_handle); 522 CHECK_REG_ERROR(code); 523 524 key_index = 0; 525 while (TRUE) 526 { 527 local_bufsize = sizeof(subkey_name_buff); 528 if (RegEnumKeyEx(key_handle, key_index, subkey_name_buff, &local_bufsize, 529 NULL, NULL, NULL, &temp_pfiletime) == ERROR_NO_MORE_ITEMS) 530 break; 531 532 if (strncmp("Vim", subkey_name_buff, 3) == 0) 533 { 534 // Open the key named Vim* 535 code = RegOpenKeyEx(key_handle, subkey_name_buff, 0, 536 KEY_WOW64_64KEY | KEY_READ, &uninstall_key_handle); 537 CHECK_REG_ERROR(code); 538 539 // get the DisplayName out of it to show the user 540 local_bufsize = sizeof(temp_string_buffer); 541 code = RegQueryValueEx(uninstall_key_handle, "displayname", 0, 542 &value_type, (LPBYTE)temp_string_buffer, 543 &local_bufsize); 544 CHECK_REG_ERROR(code); 545 546 allow_silent = 0; 547 if (skip_question) 548 { 549 DWORD varsize = sizeof(DWORD); 550 551 RegQueryValueEx(uninstall_key_handle, "AllowSilent", 0, 552 &value_type, (LPBYTE)&allow_silent, 553 &varsize); 554 } 555 556 foundone = 1; 557 printf("\n*********************************************************\n"); 558 printf("Vim Install found what looks like an existing Vim version.\n"); 559 printf("The name of the entry is:\n"); 560 printf("\n \"%s\"\n\n", temp_string_buffer); 561 562 printf("Installing the new version will disable part of the existing version.\n"); 563 printf("(The batch files used in a console and the \"Edit with Vim\" entry in\n"); 564 printf("the popup menu will use the new version)\n"); 565 566 if (skip_question) 567 printf("\nRunning uninstall program for \"%s\"\n", temp_string_buffer); 568 else 569 printf("\nDo you want to uninstall \"%s\" now?\n(y)es/(n)o) ", temp_string_buffer); 570 fflush(stdout); 571 572 // get the UninstallString 573 local_bufsize = sizeof(temp_string_buffer); 574 code = RegQueryValueEx(uninstall_key_handle, "uninstallstring", 0, 575 &value_type, (LPBYTE)temp_string_buffer, &local_bufsize); 576 CHECK_REG_ERROR(code); 577 578 // Remember the directory, it is used as the default for NSIS. 579 default_vim_dir = alloc(strlen(temp_string_buffer) + 1); 580 strcpy(default_vim_dir, temp_string_buffer); 581 remove_tail(default_vim_dir); 582 remove_tail(default_vim_dir); 583 584 input = 'n'; 585 do 586 { 587 if (input != 'n') 588 printf("%c is an invalid reply. Please enter either 'y' or 'n'\n", input); 589 590 if (skip_question) 591 input = 'y'; 592 else 593 { 594 rewind(stdin); 595 scanf("%c", &input); 596 } 597 switch (input) 598 { 599 case 'y': 600 case 'Y': 601 // save the number of uninstall keys so we can know if 602 // it changed 603 RegQueryInfoKey(key_handle, NULL, NULL, NULL, 604 &orig_num_keys, NULL, NULL, NULL, 605 NULL, NULL, NULL, NULL); 606 607 // Find existing .bat files before deleting them. 608 find_bat_exe(TRUE); 609 610 if (allow_silent) 611 { 612 if (run_silent_uninstall(temp_string_buffer) 613 == FAIL) 614 allow_silent = 0; // Retry with non silent. 615 } 616 if (!allow_silent) 617 { 618 // Execute the uninstall program. Put it in double 619 // quotes if there is an embedded space. 620 { 621 char buf[BUFSIZE]; 622 623 if (strchr(temp_string_buffer, ' ') != NULL) 624 sprintf(buf, "\"%s\"", temp_string_buffer); 625 else 626 strcpy(buf, temp_string_buffer); 627 run_command(buf); 628 } 629 630 // Count the number of windows with a title that 631 // match the installer, so that we can check when 632 // it's done. The uninstaller copies itself, 633 // executes the copy and exits, thus we can't wait 634 // for the process to finish. 635 sleep(1); // wait for uninstaller to start up 636 num_windows = 0; 637 EnumWindows(window_cb, 0); 638 if (num_windows == 0) 639 { 640 // Did not find the uninstaller, ask user to 641 // press Enter when done. Just in case. 642 printf("Press Enter when the uninstaller is finished\n"); 643 rewind(stdin); 644 (void)getchar(); 645 } 646 else 647 { 648 printf("Waiting for the uninstaller to finish (press CTRL-C to abort)."); 649 do 650 { 651 printf("."); 652 fflush(stdout); 653 sleep(1); // wait for the uninstaller to 654 // finish 655 num_windows = 0; 656 EnumWindows(window_cb, 0); 657 } while (num_windows > 0); 658 } 659 } 660 printf("\nDone!\n"); 661 662 // Check if an uninstall reg key was deleted. 663 // if it was, we want to decrement key_index. 664 // if we don't do this, we will skip the key 665 // immediately after any key that we delete. 666 RegQueryInfoKey(key_handle, NULL, NULL, NULL, 667 &new_num_keys, NULL, NULL, NULL, 668 NULL, NULL, NULL, NULL); 669 if (new_num_keys < orig_num_keys) 670 key_index--; 671 672 input = 'y'; 673 break; 674 675 case 'n': 676 case 'N': 677 // Do not uninstall 678 input = 'n'; 679 break; 680 681 default: // just drop through and redo the loop 682 break; 683 } 684 685 } while (input != 'n' && input != 'y'); 686 687 RegCloseKey(uninstall_key_handle); 688 } 689 690 key_index++; 691 } 692 RegCloseKey(key_handle); 693 694 return foundone; 695 } 696 697 /* 698 * Find out information about the system. 699 */ 700 static void 701 inspect_system(void) 702 { 703 char *p; 704 char buf[BUFSIZE]; 705 FILE *fd; 706 int i; 707 int foundone; 708 709 // This may take a little while, let the user know what we're doing. 710 printf("Inspecting system...\n"); 711 712 /* 713 * If $VIM is set, check that it's pointing to our directory. 714 */ 715 p = getenv("VIM"); 716 if (p != NULL && pathcmp(p, -1, installdir, runtimeidx - 1) != 0) 717 { 718 printf("------------------------------------------------------\n"); 719 printf("$VIM is set to \"%s\".\n", p); 720 printf("This is different from where this version of Vim is:\n"); 721 strcpy(buf, installdir); 722 *(buf + runtimeidx - 1) = NUL; 723 printf("\"%s\"\n", buf); 724 printf("You must adjust or remove the setting of $VIM,\n"); 725 if (interactive) 726 { 727 printf("to be able to use this install program.\n"); 728 myexit(1); 729 } 730 printf("otherwise Vim WILL NOT WORK properly!\n"); 731 printf("------------------------------------------------------\n"); 732 } 733 734 /* 735 * If $VIMRUNTIME is set, check that it's pointing to our runtime directory. 736 */ 737 p = getenv("VIMRUNTIME"); 738 if (p != NULL && pathcmp(p, -1, installdir, -1) != 0) 739 { 740 printf("------------------------------------------------------\n"); 741 printf("$VIMRUNTIME is set to \"%s\".\n", p); 742 printf("This is different from where this version of Vim is:\n"); 743 printf("\"%s\"\n", installdir); 744 printf("You must adjust or remove the setting of $VIMRUNTIME,\n"); 745 if (interactive) 746 { 747 printf("to be able to use this install program.\n"); 748 myexit(1); 749 } 750 printf("otherwise Vim WILL NOT WORK properly!\n"); 751 printf("------------------------------------------------------\n"); 752 } 753 754 /* 755 * Check if there is a vim.[exe|bat|, gvim.[exe|bat|, etc. in the path. 756 */ 757 find_bat_exe(FALSE); 758 759 /* 760 * A .exe in the install directory may be found anyway on Windows 2000. 761 * Check for this situation and find another executable if necessary. 762 * [email protected] 2001-01-20 763 */ 764 foundone = 0; 765 for (i = 1; i < TARGET_COUNT; ++i) 766 { 767 findoldfile(&(targets[i].oldexe)); 768 if (targets[i].oldexe != NULL) 769 foundone = 1; 770 } 771 772 if (foundone) 773 { 774 printf("Warning: Found Vim executable(s) in your $PATH:\n"); 775 for (i = 1; i < TARGET_COUNT; ++i) 776 if (targets[i].oldexe != NULL) 777 printf("%s\n", targets[i].oldexe); 778 printf("It will be used instead of the version you are installing.\n"); 779 printf("Please delete or rename it, or adjust your $PATH setting.\n"); 780 } 781 782 /* 783 * Check if there is an existing ../_vimrc or ../.vimrc file. 784 */ 785 strcpy(oldvimrc, installdir); 786 strcpy(oldvimrc + runtimeidx, "_vimrc"); 787 if ((fd = fopen(oldvimrc, "r")) == NULL) 788 { 789 strcpy(oldvimrc + runtimeidx, "vimrc~1"); // short version of .vimrc 790 if ((fd = fopen(oldvimrc, "r")) == NULL) 791 { 792 strcpy(oldvimrc + runtimeidx, ".vimrc"); 793 fd = fopen(oldvimrc, "r"); 794 } 795 } 796 if (fd != NULL) 797 fclose(fd); 798 else 799 *oldvimrc = NUL; 800 } 801 802 /* 803 * Add a dummy choice to avoid that the numbering changes depending on items 804 * in the environment. The user may type a number he remembered without 805 * looking. 806 */ 807 static void 808 add_dummy_choice(void) 809 { 810 choices[choice_count].installfunc = NULL; 811 choices[choice_count].active = 0; 812 choices[choice_count].changefunc = NULL; 813 choices[choice_count].text = NULL; 814 choices[choice_count].arg = 0; 815 ++choice_count; 816 } 817 818 //////////////////////////////////////////////// 819 // stuff for creating the batch files. 820 821 /* 822 * Install the vim.bat, gvim.bat, etc. files. 823 */ 824 static void 825 install_bat_choice(int idx) 826 { 827 char *batpath = targets[choices[idx].arg].batpath; 828 char *oldname = targets[choices[idx].arg].oldbat; 829 char *exename = targets[choices[idx].arg].exenamearg; 830 char *vimarg = targets[choices[idx].arg].exearg; 831 FILE *fd; 832 833 if (*batpath != NUL) 834 { 835 fd = fopen(batpath, "w"); 836 if (fd == NULL) 837 printf("\nERROR: Cannot open \"%s\" for writing.\n", batpath); 838 else 839 { 840 need_uninstall_entry = 1; 841 842 fprintf(fd, "@echo off\n"); 843 fprintf(fd, "rem -- Run Vim --\n"); 844 fprintf(fd, VIMBAT_UNINSTKEY "\n"); 845 fprintf(fd, "\n"); 846 fprintf(fd, "setlocal\n"); 847 848 /* 849 * Don't use double quotes for the "set" argument, also when it 850 * contains a space. The quotes would be included in the value. 851 * The order of preference is: 852 * 1. $VIMRUNTIME/vim.exe (user preference) 853 * 2. $VIM/vim81/vim.exe (hard coded version) 854 * 3. installdir/vim.exe (hard coded install directory) 855 */ 856 fprintf(fd, "set VIM_EXE_DIR=%s\n", installdir); 857 fprintf(fd, "if exist \"%%VIM%%\\%s\\%s\" set VIM_EXE_DIR=%%VIM%%\\%s\n", 858 VIM_VERSION_NODOT, exename, VIM_VERSION_NODOT); 859 fprintf(fd, "if exist \"%%VIMRUNTIME%%\\%s\" set VIM_EXE_DIR=%%VIMRUNTIME%%\n", exename); 860 fprintf(fd, "\n"); 861 862 // Give an error message when the executable could not be found. 863 fprintf(fd, "if not exist \"%%VIM_EXE_DIR%%\\%s\" (\n", exename); 864 fprintf(fd, " echo \"%%VIM_EXE_DIR%%\\%s\" not found\n", exename); 865 fprintf(fd, " goto :eof\n"); 866 fprintf(fd, ")\n"); 867 fprintf(fd, "\n"); 868 869 if (*exename == 'g') 870 { 871 fprintf(fd, "rem check --nofork argument\n"); 872 fprintf(fd, "set VIMNOFORK=\n"); 873 fprintf(fd, ":loopstart\n"); 874 fprintf(fd, "if .%%1==. goto loopend\n"); 875 fprintf(fd, "if .%%1==.--nofork (\n"); 876 fprintf(fd, " set VIMNOFORK=1\n"); 877 fprintf(fd, ") else if .%%1==.-f (\n"); 878 fprintf(fd, " set VIMNOFORK=1\n"); 879 fprintf(fd, ")\n"); 880 fprintf(fd, "shift\n"); 881 fprintf(fd, "goto loopstart\n"); 882 fprintf(fd, ":loopend\n"); 883 fprintf(fd, "\n"); 884 } 885 886 if (*exename == 'g') 887 { 888 // For gvim.exe use "start /b" to avoid that the console window 889 // stays open. 890 fprintf(fd, "if .%%VIMNOFORK%%==.1 (\n"); 891 fprintf(fd, " start \"dummy\" /b /wait "); 892 // Always use quotes, $VIM or $VIMRUNTIME might have a space. 893 fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", 894 exename, vimarg); 895 fprintf(fd, ") else (\n"); 896 fprintf(fd, " start \"dummy\" /b "); 897 // Always use quotes, $VIM or $VIMRUNTIME might have a space. 898 fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", 899 exename, vimarg); 900 fprintf(fd, ")\n"); 901 } 902 else 903 { 904 // Always use quotes, $VIM or $VIMRUNTIME might have a space. 905 fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", 906 exename, vimarg); 907 } 908 909 fclose(fd); 910 printf("%s has been %s\n", batpath, 911 oldname == NULL ? "created" : "overwritten"); 912 } 913 } 914 } 915 916 /* 917 * Make the text string for choice "idx". 918 * The format "fmt" is must have one %s item, which "arg" is used for. 919 */ 920 static void 921 alloc_text(int idx, char *fmt, char *arg) 922 { 923 if (choices[idx].text != NULL) 924 free(choices[idx].text); 925 926 choices[idx].text = alloc(strlen(fmt) + strlen(arg) - 1); 927 sprintf(choices[idx].text, fmt, arg); 928 } 929 930 /* 931 * Toggle the "Overwrite .../vim.bat" to "Don't overwrite". 932 */ 933 static void 934 toggle_bat_choice(int idx) 935 { 936 char *batname = targets[choices[idx].arg].batpath; 937 char *oldname = targets[choices[idx].arg].oldbat; 938 939 if (*batname == NUL) 940 { 941 alloc_text(idx, " Overwrite %s", oldname); 942 strcpy(batname, oldname); 943 } 944 else 945 { 946 alloc_text(idx, " Do NOT overwrite %s", oldname); 947 *batname = NUL; 948 } 949 } 950 951 /* 952 * Do some work for a batch file entry: Append the batch file name to the path 953 * and set the text for the choice. 954 */ 955 static void 956 set_bat_text(int idx, char *batpath, char *name) 957 { 958 strcat(batpath, name); 959 960 alloc_text(idx, " Create %s", batpath); 961 } 962 963 /* 964 * Select a directory to write the batch file line. 965 */ 966 static void 967 change_bat_choice(int idx) 968 { 969 char *path; 970 char *batpath; 971 char *name; 972 int n; 973 char *s; 974 char *p; 975 int count; 976 char **names = NULL; 977 int i; 978 int target = choices[idx].arg; 979 980 name = targets[target].batname; 981 batpath = targets[target].batpath; 982 983 path = getenv("PATH"); 984 if (path == NULL) 985 { 986 printf("\nERROR: The variable $PATH is not set\n"); 987 return; 988 } 989 990 /* 991 * first round: count number of names in path; 992 * second round: save names to names[]. 993 */ 994 for (;;) 995 { 996 count = 1; 997 for (p = path; *p; ) 998 { 999 s = strchr(p, ';'); 1000 if (s == NULL) 1001 s = p + strlen(p); 1002 if (names != NULL) 1003 { 1004 names[count] = alloc(s - p + 1); 1005 strncpy(names[count], p, s - p); 1006 names[count][s - p] = NUL; 1007 } 1008 ++count; 1009 p = s; 1010 if (*p != NUL) 1011 ++p; 1012 } 1013 if (names != NULL) 1014 break; 1015 names = alloc((count + 1) * sizeof(char *)); 1016 } 1017 names[0] = alloc(50); 1018 sprintf(names[0], "Select directory to create %s in:", name); 1019 names[count] = alloc(50); 1020 if (choices[idx].arg == 0) 1021 sprintf(names[count], "Do not create any .bat file."); 1022 else 1023 sprintf(names[count], "Do not create a %s file.", name); 1024 n = get_choice(names, count + 1); 1025 1026 if (n == count) 1027 { 1028 // Selected last item, don't create bat file. 1029 *batpath = NUL; 1030 if (choices[idx].arg != 0) 1031 alloc_text(idx, " Do NOT create %s", name); 1032 } 1033 else 1034 { 1035 // Selected one of the paths. For the first item only keep the path, 1036 // for the others append the batch file name. 1037 strcpy(batpath, names[n]); 1038 add_pathsep(batpath); 1039 if (choices[idx].arg != 0) 1040 set_bat_text(idx, batpath, name); 1041 } 1042 1043 for (i = 0; i <= count; ++i) 1044 free(names[i]); 1045 free(names); 1046 } 1047 1048 char *bat_text_yes = "Install .bat files to use Vim at the command line:"; 1049 char *bat_text_no = "do NOT install .bat files to use Vim at the command line"; 1050 1051 static void 1052 change_main_bat_choice(int idx) 1053 { 1054 int i; 1055 1056 // let the user select a default directory or NONE 1057 change_bat_choice(idx); 1058 1059 if (targets[0].batpath[0] != NUL) 1060 choices[idx].text = bat_text_yes; 1061 else 1062 choices[idx].text = bat_text_no; 1063 1064 // update the individual batch file selections 1065 for (i = 1; i < TARGET_COUNT; ++i) 1066 { 1067 // Only make it active when the first item has a path and the vim.exe 1068 // or gvim.exe exists (there is a changefunc then). 1069 if (targets[0].batpath[0] != NUL 1070 && choices[idx + i].changefunc != NULL) 1071 { 1072 choices[idx + i].active = 1; 1073 if (choices[idx + i].changefunc == change_bat_choice 1074 && targets[i].batpath[0] != NUL) 1075 { 1076 strcpy(targets[i].batpath, targets[0].batpath); 1077 set_bat_text(idx + i, targets[i].batpath, targets[i].batname); 1078 } 1079 } 1080 else 1081 choices[idx + i].active = 0; 1082 } 1083 } 1084 1085 /* 1086 * Initialize a choice for creating a batch file. 1087 */ 1088 static void 1089 init_bat_choice(int target) 1090 { 1091 char *batpath = targets[target].batpath; 1092 char *oldbat = targets[target].oldbat; 1093 char *p; 1094 int i; 1095 1096 choices[choice_count].arg = target; 1097 choices[choice_count].installfunc = install_bat_choice; 1098 choices[choice_count].active = 1; 1099 choices[choice_count].text = NULL; // will be set below 1100 if (oldbat != NULL) 1101 { 1102 // A [g]vim.bat exists: Only choice is to overwrite it or not. 1103 choices[choice_count].changefunc = toggle_bat_choice; 1104 *batpath = NUL; 1105 toggle_bat_choice(choice_count); 1106 } 1107 else 1108 { 1109 if (default_bat_dir != NULL) 1110 // Prefer using the same path as an existing .bat file. 1111 strcpy(batpath, default_bat_dir); 1112 else 1113 { 1114 // No [g]vim.bat exists: Write it to a directory in $PATH. Use 1115 // $WINDIR by default, if it's empty the first item in $PATH. 1116 p = getenv("WINDIR"); 1117 if (p != NULL && *p != NUL) 1118 strcpy(batpath, p); 1119 else 1120 { 1121 p = getenv("PATH"); 1122 if (p == NULL || *p == NUL) // "cannot happen" 1123 strcpy(batpath, "C:/Windows"); 1124 else 1125 { 1126 i = 0; 1127 while (*p != NUL && *p != ';') 1128 batpath[i++] = *p++; 1129 batpath[i] = NUL; 1130 } 1131 } 1132 } 1133 add_pathsep(batpath); 1134 set_bat_text(choice_count, batpath, targets[target].batname); 1135 1136 choices[choice_count].changefunc = change_bat_choice; 1137 } 1138 ++choice_count; 1139 } 1140 1141 /* 1142 * Set up the choices for installing .bat files. 1143 * For these items "arg" is the index in targets[]. 1144 */ 1145 static void 1146 init_bat_choices(void) 1147 { 1148 int i; 1149 1150 // The first item is used to switch installing batch files on/off and 1151 // setting the default path. 1152 choices[choice_count].text = bat_text_yes; 1153 choices[choice_count].changefunc = change_main_bat_choice; 1154 choices[choice_count].installfunc = NULL; 1155 choices[choice_count].active = 1; 1156 choices[choice_count].arg = 0; 1157 ++choice_count; 1158 1159 // Add items for each batch file target. Only used when not disabled by 1160 // the first item. When a .exe exists, don't offer to create a .bat. 1161 for (i = 1; i < TARGET_COUNT; ++i) 1162 if (targets[i].oldexe == NULL 1163 && (targets[i].exenamearg[0] == 'g' ? has_gvim : has_vim)) 1164 init_bat_choice(i); 1165 else 1166 add_dummy_choice(); 1167 } 1168 1169 /* 1170 * Install the vimrc file. 1171 */ 1172 static void 1173 install_vimrc(int idx) 1174 { 1175 FILE *fd, *tfd; 1176 char *fname; 1177 1178 // If an old vimrc file exists, overwrite it. 1179 // Otherwise create a new one. 1180 if (*oldvimrc != NUL) 1181 fname = oldvimrc; 1182 else 1183 fname = vimrc; 1184 1185 fd = fopen(fname, "w"); 1186 if (fd == NULL) 1187 { 1188 printf("\nERROR: Cannot open \"%s\" for writing.\n", fname); 1189 return; 1190 } 1191 switch (compat_choice) 1192 { 1193 case compat_vi: 1194 fprintf(fd, "\" Vi compatible\n"); 1195 fprintf(fd, "set compatible\n"); 1196 break; 1197 case compat_vim: 1198 fprintf(fd, "\" Vim's default behavior\n"); 1199 fprintf(fd, "if &compatible\n"); 1200 fprintf(fd, " set nocompatible\n"); 1201 fprintf(fd, "endif\n"); 1202 break; 1203 case compat_some_enhancements: 1204 fprintf(fd, "\" Vim with some enhancements\n"); 1205 fprintf(fd, "source $VIMRUNTIME/defaults.vim\n"); 1206 break; 1207 case compat_all_enhancements: 1208 fprintf(fd, "\" Vim with all enhancements\n"); 1209 fprintf(fd, "source $VIMRUNTIME/vimrc_example.vim\n"); 1210 break; 1211 } 1212 switch (remap_choice) 1213 { 1214 case remap_no: 1215 break; 1216 case remap_win: 1217 fprintf(fd, "\n"); 1218 fprintf(fd, "\" Remap a few keys for Windows behavior\n"); 1219 fprintf(fd, "source $VIMRUNTIME/mswin.vim\n"); 1220 break; 1221 } 1222 switch (mouse_choice) 1223 { 1224 case mouse_xterm: 1225 fprintf(fd, "\n"); 1226 fprintf(fd, "\" Mouse behavior (the Unix way)\n"); 1227 fprintf(fd, "behave xterm\n"); 1228 break; 1229 case mouse_mswin: 1230 fprintf(fd, "\n"); 1231 fprintf(fd, "\" Mouse behavior (the Windows way)\n"); 1232 fprintf(fd, "behave mswin\n"); 1233 break; 1234 case mouse_default: 1235 break; 1236 } 1237 if ((tfd = fopen("diff.exe", "r")) != NULL) 1238 { 1239 // Use the diff.exe that comes with the self-extracting gvim.exe. 1240 fclose(tfd); 1241 fprintf(fd, "\n"); 1242 fprintf(fd, "\" Use the internal diff if available.\n"); 1243 fprintf(fd, "\" Otherwise use the special 'diffexpr' for Windows.\n"); 1244 fprintf(fd, "if &diffopt !~# 'internal'\n"); 1245 fprintf(fd, " set diffexpr=MyDiff()\n"); 1246 fprintf(fd, "endif\n"); 1247 fprintf(fd, "function MyDiff()\n"); 1248 fprintf(fd, " let opt = '-a --binary '\n"); 1249 fprintf(fd, " if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif\n"); 1250 fprintf(fd, " if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif\n"); 1251 // Use quotes only when needed, they may cause trouble. 1252 // Always escape "!". 1253 fprintf(fd, " let arg1 = v:fname_in\n"); 1254 fprintf(fd, " if arg1 =~ ' ' | let arg1 = '\"' . arg1 . '\"' | endif\n"); 1255 fprintf(fd, " let arg1 = substitute(arg1, '!', '\\!', 'g')\n"); 1256 fprintf(fd, " let arg2 = v:fname_new\n"); 1257 fprintf(fd, " if arg2 =~ ' ' | let arg2 = '\"' . arg2 . '\"' | endif\n"); 1258 fprintf(fd, " let arg2 = substitute(arg2, '!', '\\!', 'g')\n"); 1259 fprintf(fd, " let arg3 = v:fname_out\n"); 1260 fprintf(fd, " if arg3 =~ ' ' | let arg3 = '\"' . arg3 . '\"' | endif\n"); 1261 fprintf(fd, " let arg3 = substitute(arg3, '!', '\\!', 'g')\n"); 1262 1263 // If the path has a space: When using cmd.exe (Win NT/2000/XP) put 1264 // quotes around the diff command and rely on the default value of 1265 // shellxquote to solve the quoting problem for the whole command. 1266 // 1267 // Otherwise put a double quote just before the space and at the 1268 // end of the command. Putting quotes around the whole thing 1269 // doesn't work on Win 95/98/ME. This is mostly guessed! 1270 fprintf(fd, " if $VIMRUNTIME =~ ' '\n"); 1271 fprintf(fd, " if &sh =~ '\\<cmd'\n"); 1272 fprintf(fd, " if empty(&shellxquote)\n"); 1273 fprintf(fd, " let l:shxq_sav = ''\n"); 1274 fprintf(fd, " set shellxquote&\n"); 1275 fprintf(fd, " endif\n"); 1276 fprintf(fd, " let cmd = '\"' . $VIMRUNTIME . '\\diff\"'\n"); 1277 fprintf(fd, " else\n"); 1278 fprintf(fd, " let cmd = substitute($VIMRUNTIME, ' ', '\" ', '') . '\\diff\"'\n"); 1279 fprintf(fd, " endif\n"); 1280 fprintf(fd, " else\n"); 1281 fprintf(fd, " let cmd = $VIMRUNTIME . '\\diff'\n"); 1282 fprintf(fd, " endif\n"); 1283 fprintf(fd, " let cmd = substitute(cmd, '!', '\\!', 'g')\n"); 1284 fprintf(fd, " silent execute '!' . cmd . ' ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3\n"); 1285 fprintf(fd, " if exists('l:shxq_sav')\n"); 1286 fprintf(fd, " let &shellxquote=l:shxq_sav\n"); 1287 fprintf(fd, " endif\n"); 1288 fprintf(fd, "endfunction\n"); 1289 fprintf(fd, "\n"); 1290 } 1291 fclose(fd); 1292 printf("%s has been written\n", fname); 1293 } 1294 1295 static void 1296 change_vimrc_choice(int idx) 1297 { 1298 if (choices[idx].installfunc != NULL) 1299 { 1300 // Switch to NOT change or create a vimrc file. 1301 if (*oldvimrc != NUL) 1302 alloc_text(idx, "Do NOT change startup file %s", oldvimrc); 1303 else 1304 alloc_text(idx, "Do NOT create startup file %s", vimrc); 1305 choices[idx].installfunc = NULL; 1306 choices[idx + 1].active = 0; 1307 choices[idx + 2].active = 0; 1308 choices[idx + 3].active = 0; 1309 } 1310 else 1311 { 1312 // Switch to change or create a vimrc file. 1313 if (*oldvimrc != NUL) 1314 alloc_text(idx, "Overwrite startup file %s with:", oldvimrc); 1315 else 1316 alloc_text(idx, "Create startup file %s with:", vimrc); 1317 choices[idx].installfunc = install_vimrc; 1318 choices[idx + 1].active = 1; 1319 choices[idx + 2].active = 1; 1320 choices[idx + 3].active = 1; 1321 } 1322 } 1323 1324 /* 1325 * Change the choice how to run Vim. 1326 */ 1327 static void 1328 change_run_choice(int idx) 1329 { 1330 compat_choice = get_choice(compat_choices, TABLE_SIZE(compat_choices)); 1331 alloc_text(idx, compat_text, compat_choices[compat_choice]); 1332 } 1333 1334 /* 1335 * Change the choice if keys are to be remapped. 1336 */ 1337 static void 1338 change_remap_choice(int idx) 1339 { 1340 remap_choice = get_choice(remap_choices, TABLE_SIZE(remap_choices)); 1341 alloc_text(idx, remap_text, remap_choices[remap_choice]); 1342 } 1343 1344 /* 1345 * Change the choice how to select text. 1346 */ 1347 static void 1348 change_mouse_choice(int idx) 1349 { 1350 mouse_choice = get_choice(mouse_choices, TABLE_SIZE(mouse_choices)); 1351 alloc_text(idx, mouse_text, mouse_choices[mouse_choice]); 1352 } 1353 1354 static void 1355 init_vimrc_choices(void) 1356 { 1357 // set path for a new _vimrc file (also when not used) 1358 strcpy(vimrc, installdir); 1359 strcpy(vimrc + runtimeidx, "_vimrc"); 1360 1361 // Set opposite value and then toggle it by calling change_vimrc_choice() 1362 if (*oldvimrc == NUL) 1363 choices[choice_count].installfunc = NULL; 1364 else 1365 choices[choice_count].installfunc = install_vimrc; 1366 choices[choice_count].text = NULL; 1367 change_vimrc_choice(choice_count); 1368 choices[choice_count].changefunc = change_vimrc_choice; 1369 choices[choice_count].active = 1; 1370 ++choice_count; 1371 1372 // default way to run Vim 1373 alloc_text(choice_count, compat_text, compat_choices[compat_choice]); 1374 choices[choice_count].changefunc = change_run_choice; 1375 choices[choice_count].installfunc = NULL; 1376 choices[choice_count].active = (*oldvimrc == NUL); 1377 ++choice_count; 1378 1379 // Whether to remap keys 1380 alloc_text(choice_count, remap_text , remap_choices[remap_choice]); 1381 choices[choice_count].changefunc = change_remap_choice; 1382 choices[choice_count].installfunc = NULL; 1383 choices[choice_count].active = (*oldvimrc == NUL); 1384 ++choice_count; 1385 1386 // default way to use the mouse 1387 alloc_text(choice_count, mouse_text, mouse_choices[mouse_choice]); 1388 choices[choice_count].changefunc = change_mouse_choice; 1389 choices[choice_count].installfunc = NULL; 1390 choices[choice_count].active = (*oldvimrc == NUL); 1391 ++choice_count; 1392 } 1393 1394 static LONG 1395 reg_create_key( 1396 HKEY root, 1397 const char *subkey, 1398 PHKEY phKey, 1399 DWORD flag) 1400 { 1401 DWORD disp; 1402 1403 *phKey = NULL; 1404 return RegCreateKeyEx( 1405 root, subkey, 1406 0, NULL, REG_OPTION_NON_VOLATILE, 1407 flag | KEY_WRITE, 1408 NULL, phKey, &disp); 1409 } 1410 1411 static LONG 1412 reg_set_string_value( 1413 HKEY hKey, 1414 const char *value_name, 1415 const char *data) 1416 { 1417 return RegSetValueEx(hKey, value_name, 0, REG_SZ, 1418 (LPBYTE)data, (DWORD)(1 + strlen(data))); 1419 } 1420 1421 static LONG 1422 reg_create_key_and_value( 1423 HKEY hRootKey, 1424 const char *subkey, 1425 const char *value_name, 1426 const char *data, 1427 DWORD flag) 1428 { 1429 HKEY hKey; 1430 LONG lRet = reg_create_key(hRootKey, subkey, &hKey, flag); 1431 1432 if (ERROR_SUCCESS == lRet) 1433 { 1434 lRet = reg_set_string_value(hKey, value_name, data); 1435 RegCloseKey(hKey); 1436 } 1437 return lRet; 1438 } 1439 1440 static LONG 1441 register_inproc_server( 1442 HKEY hRootKey, 1443 const char *clsid, 1444 const char *extname, 1445 const char *module, 1446 const char *threading_model, 1447 DWORD flag) 1448 { 1449 CHAR subkey[BUFSIZE]; 1450 LONG lRet; 1451 1452 sprintf(subkey, "CLSID\\%s", clsid); 1453 lRet = reg_create_key_and_value(hRootKey, subkey, NULL, extname, flag); 1454 if (ERROR_SUCCESS == lRet) 1455 { 1456 sprintf(subkey, "CLSID\\%s\\InProcServer32", clsid); 1457 lRet = reg_create_key_and_value(hRootKey, subkey, NULL, module, flag); 1458 if (ERROR_SUCCESS == lRet) 1459 { 1460 lRet = reg_create_key_and_value(hRootKey, subkey, 1461 "ThreadingModel", threading_model, flag); 1462 } 1463 } 1464 return lRet; 1465 } 1466 1467 static LONG 1468 register_shellex( 1469 HKEY hRootKey, 1470 const char *clsid, 1471 const char *name, 1472 const char *exe_path, 1473 DWORD flag) 1474 { 1475 LONG lRet = reg_create_key_and_value( 1476 hRootKey, 1477 "*\\shellex\\ContextMenuHandlers\\gvim", 1478 NULL, 1479 clsid, 1480 flag); 1481 1482 if (ERROR_SUCCESS == lRet) 1483 { 1484 lRet = reg_create_key_and_value( 1485 HKEY_LOCAL_MACHINE, 1486 "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 1487 clsid, 1488 name, 1489 flag); 1490 1491 if (ERROR_SUCCESS == lRet) 1492 { 1493 lRet = reg_create_key_and_value( 1494 HKEY_LOCAL_MACHINE, 1495 "Software\\Vim\\Gvim", 1496 "path", 1497 exe_path, 1498 flag); 1499 } 1500 } 1501 return lRet; 1502 } 1503 1504 static LONG 1505 register_openwith( 1506 HKEY hRootKey, 1507 const char *exe_path, 1508 DWORD flag) 1509 { 1510 char exe_cmd[BUFSIZE]; 1511 LONG lRet; 1512 1513 sprintf(exe_cmd, "\"%s\" \"%%1\"", exe_path); 1514 lRet = reg_create_key_and_value( 1515 hRootKey, 1516 "Applications\\gvim.exe\\shell\\edit\\command", 1517 NULL, 1518 exe_cmd, 1519 flag); 1520 1521 if (ERROR_SUCCESS == lRet) 1522 { 1523 int i; 1524 static const char *openwith[] = { 1525 ".htm\\OpenWithList\\gvim.exe", 1526 ".vim\\OpenWithList\\gvim.exe", 1527 "*\\OpenWithList\\gvim.exe", 1528 }; 1529 1530 for (i = 0; ERROR_SUCCESS == lRet 1531 && i < sizeof(openwith) / sizeof(openwith[0]); i++) 1532 lRet = reg_create_key_and_value(hRootKey, openwith[i], NULL, "", flag); 1533 } 1534 1535 return lRet; 1536 } 1537 1538 static LONG 1539 register_uninstall( 1540 HKEY hRootKey, 1541 const char *appname, 1542 const char *display_name, 1543 const char *uninstall_string, 1544 const char *display_icon, 1545 const char *display_version, 1546 const char *publisher) 1547 { 1548 LONG lRet = reg_create_key_and_value(hRootKey, appname, 1549 "DisplayName", display_name, KEY_WOW64_64KEY); 1550 1551 if (ERROR_SUCCESS == lRet) 1552 lRet = reg_create_key_and_value(hRootKey, appname, 1553 "UninstallString", uninstall_string, KEY_WOW64_64KEY); 1554 if (ERROR_SUCCESS == lRet) 1555 lRet = reg_create_key_and_value(hRootKey, appname, 1556 "DisplayIcon", display_icon, KEY_WOW64_64KEY); 1557 if (ERROR_SUCCESS == lRet) 1558 lRet = reg_create_key_and_value(hRootKey, appname, 1559 "DisplayVersion", display_version, KEY_WOW64_64KEY); 1560 if (ERROR_SUCCESS == lRet) 1561 lRet = reg_create_key_and_value(hRootKey, appname, 1562 "Publisher", publisher, KEY_WOW64_64KEY); 1563 return lRet; 1564 } 1565 1566 /* 1567 * Add some entries to the registry: 1568 * - to add "Edit with Vim" to the context * menu 1569 * - to add Vim to the "Open with..." list 1570 * - to uninstall Vim 1571 */ 1572 //ARGSUSED 1573 static int 1574 install_registry(void) 1575 { 1576 LONG lRet = ERROR_SUCCESS; 1577 const char *vim_ext_ThreadingModel = "Apartment"; 1578 const char *vim_ext_name = "Vim Shell Extension"; 1579 const char *vim_ext_clsid = "{51EEE242-AD87-11d3-9C1E-0090278BBD99}"; 1580 char vim_exe_path[MAX_PATH]; 1581 char display_name[BUFSIZE]; 1582 char uninstall_string[BUFSIZE]; 1583 char icon_string[BUFSIZE]; 1584 int i; 1585 int loop_count = is_64bit_os() ? 2 : 1; 1586 DWORD flag; 1587 1588 sprintf(vim_exe_path, "%s\\gvim.exe", installdir); 1589 1590 if (install_popup) 1591 { 1592 char bufg[BUFSIZE]; 1593 1594 printf("Creating \"Edit with Vim\" popup menu entry\n"); 1595 1596 for (i = 0; i < loop_count; i++) 1597 { 1598 if (i == 0) 1599 { 1600 sprintf(bufg, "%s\\" GVIMEXT32_PATH, installdir); 1601 flag = KEY_WOW64_32KEY; 1602 } 1603 else 1604 { 1605 sprintf(bufg, "%s\\" GVIMEXT64_PATH, installdir); 1606 flag = KEY_WOW64_64KEY; 1607 } 1608 1609 lRet = register_inproc_server( 1610 HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name, 1611 bufg, vim_ext_ThreadingModel, flag); 1612 if (ERROR_SUCCESS != lRet) 1613 return FAIL; 1614 lRet = register_shellex( 1615 HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name, 1616 vim_exe_path, flag); 1617 if (ERROR_SUCCESS != lRet) 1618 return FAIL; 1619 } 1620 } 1621 1622 if (install_openwith) 1623 { 1624 printf("Creating \"Open with ...\" list entry\n"); 1625 1626 for (i = 0; i < loop_count; i++) 1627 { 1628 if (i == 0) 1629 flag = KEY_WOW64_32KEY; 1630 else 1631 flag = KEY_WOW64_64KEY; 1632 1633 lRet = register_openwith(HKEY_CLASSES_ROOT, vim_exe_path, flag); 1634 if (ERROR_SUCCESS != lRet) 1635 return FAIL; 1636 } 1637 } 1638 1639 printf("Creating an uninstall entry\n"); 1640 sprintf(display_name, "Vim " VIM_VERSION_SHORT 1641 #ifdef _M_ARM64 1642 " (arm64)" 1643 #elif _M_X64 1644 " (x64)" 1645 #endif 1646 ); 1647 1648 // For the NSIS installer use the generated uninstaller. 1649 if (interactive) 1650 sprintf(uninstall_string, "%s\\uninstall.exe", installdir); 1651 else 1652 sprintf(uninstall_string, "%s\\uninstall-gui.exe", installdir); 1653 1654 sprintf(icon_string, "%s\\gvim.exe,0", installdir); 1655 1656 lRet = register_uninstall( 1657 HKEY_LOCAL_MACHINE, 1658 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT, 1659 display_name, 1660 uninstall_string, 1661 icon_string, 1662 VIM_VERSION_SHORT, 1663 "Bram Moolenaar et al."); 1664 if (ERROR_SUCCESS != lRet) 1665 return FAIL; 1666 1667 return OK; 1668 } 1669 1670 static void 1671 change_popup_choice(int idx) 1672 { 1673 if (install_popup == 0) 1674 { 1675 choices[idx].text = "Install an entry for Vim in the popup menu for the right\n mouse button so that you can edit any file with Vim"; 1676 install_popup = 1; 1677 } 1678 else 1679 { 1680 choices[idx].text = "Do NOT install an entry for Vim in the popup menu for the\n right mouse button to edit any file with Vim"; 1681 install_popup = 0; 1682 } 1683 } 1684 1685 /* 1686 * Only add the choice for the popup menu entry when gvim.exe was found and 1687 * both gvimext.dll and regedit.exe exist. 1688 */ 1689 static void 1690 init_popup_choice(void) 1691 { 1692 struct stat st; 1693 1694 if (has_gvim 1695 && (stat(GVIMEXT32_PATH, &st) >= 0 1696 || stat(GVIMEXT64_PATH, &st) >= 0)) 1697 { 1698 choices[choice_count].changefunc = change_popup_choice; 1699 choices[choice_count].installfunc = NULL; 1700 choices[choice_count].active = 1; 1701 change_popup_choice(choice_count); // set the text 1702 ++choice_count; 1703 } 1704 else 1705 add_dummy_choice(); 1706 } 1707 1708 static void 1709 change_openwith_choice(int idx) 1710 { 1711 if (install_openwith == 0) 1712 { 1713 choices[idx].text = "Add Vim to the \"Open With...\" list in the popup menu for the right\n mouse button so that you can edit any file with Vim"; 1714 install_openwith = 1; 1715 } 1716 else 1717 { 1718 choices[idx].text = "Do NOT add Vim to the \"Open With...\" list in the popup menu for the\n right mouse button to edit any file with Vim"; 1719 install_openwith = 0; 1720 } 1721 } 1722 1723 /* 1724 * Only add the choice for the open-with menu entry when gvim.exe was found 1725 * and regedit.exe exist. 1726 */ 1727 static void 1728 init_openwith_choice(void) 1729 { 1730 if (has_gvim) 1731 { 1732 choices[choice_count].changefunc = change_openwith_choice; 1733 choices[choice_count].installfunc = NULL; 1734 choices[choice_count].active = 1; 1735 change_openwith_choice(choice_count); // set the text 1736 ++choice_count; 1737 } 1738 else 1739 add_dummy_choice(); 1740 } 1741 1742 /* 1743 * Create a shell link. 1744 * 1745 * returns 0 on failure, non-zero on successful completion. 1746 * 1747 * NOTE: Currently untested with mingw. 1748 */ 1749 int 1750 create_shortcut( 1751 const char *shortcut_name, 1752 const char *iconfile_path, 1753 int iconindex, 1754 const char *shortcut_target, 1755 const char *shortcut_args, 1756 const char *workingdir 1757 ) 1758 { 1759 IShellLink *shelllink_ptr; 1760 HRESULT hres; 1761 IPersistFile *persistfile_ptr; 1762 1763 // Initialize COM library 1764 hres = CoInitialize(NULL); 1765 if (!SUCCEEDED(hres)) 1766 { 1767 printf("Error: Could not open the COM library. Not creating shortcut.\n"); 1768 return FAIL; 1769 } 1770 1771 // Instantiate a COM object for the ShellLink, store a pointer to it 1772 // in shelllink_ptr. 1773 hres = CoCreateInstance(&CLSID_ShellLink, 1774 NULL, 1775 CLSCTX_INPROC_SERVER, 1776 &IID_IShellLink, 1777 (void **) &shelllink_ptr); 1778 1779 if (SUCCEEDED(hres)) // If the instantiation was successful... 1780 { 1781 // ...Then build a PersistFile interface for the ShellLink so we can 1782 // save it as a file after we build it. 1783 hres = shelllink_ptr->lpVtbl->QueryInterface(shelllink_ptr, 1784 &IID_IPersistFile, (void **) &persistfile_ptr); 1785 1786 if (SUCCEEDED(hres)) 1787 { 1788 wchar_t wsz[BUFSIZE]; 1789 1790 // translate the (possibly) multibyte shortcut filename to windows 1791 // Unicode so it can be used as a file name. 1792 MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, wsz, sizeof(wsz)/sizeof(wsz[0])); 1793 1794 // set the attributes 1795 shelllink_ptr->lpVtbl->SetPath(shelllink_ptr, shortcut_target); 1796 shelllink_ptr->lpVtbl->SetWorkingDirectory(shelllink_ptr, 1797 workingdir); 1798 shelllink_ptr->lpVtbl->SetIconLocation(shelllink_ptr, 1799 iconfile_path, iconindex); 1800 shelllink_ptr->lpVtbl->SetArguments(shelllink_ptr, shortcut_args); 1801 1802 // save the shortcut to a file and return the PersistFile object 1803 persistfile_ptr->lpVtbl->Save(persistfile_ptr, wsz, 1); 1804 persistfile_ptr->lpVtbl->Release(persistfile_ptr); 1805 } 1806 else 1807 { 1808 printf("QueryInterface Error\n"); 1809 return FAIL; 1810 } 1811 1812 // Return the ShellLink object 1813 shelllink_ptr->lpVtbl->Release(shelllink_ptr); 1814 } 1815 else 1816 { 1817 printf("CoCreateInstance Error - hres = %08x\n", (int)hres); 1818 return FAIL; 1819 } 1820 1821 return OK; 1822 } 1823 1824 /* 1825 * Build a path to where we will put a specified link. 1826 * 1827 * Return 0 on error, non-zero on success 1828 */ 1829 int 1830 build_link_name( 1831 char *link_path, 1832 const char *link_name, 1833 const char *shell_folder_name) 1834 { 1835 char shell_folder_path[MAX_PATH]; 1836 1837 if (get_shell_folder_path(shell_folder_path, shell_folder_name) == FAIL) 1838 { 1839 printf("An error occurred while attempting to find the path to %s.\n", 1840 shell_folder_name); 1841 return FAIL; 1842 } 1843 1844 // Make sure the directory exists (create Start Menu\Programs\Vim). 1845 // Ignore errors if it already exists. 1846 vim_mkdir(shell_folder_path, 0755); 1847 1848 // build the path to the shortcut and the path to gvim.exe 1849 sprintf(link_path, "%s\\%s.lnk", shell_folder_path, link_name); 1850 1851 return OK; 1852 } 1853 1854 static int 1855 build_shortcut( 1856 const char *name, // Name of the shortcut 1857 const char *exename, // Name of the executable (e.g., vim.exe) 1858 const char *args, 1859 const char *shell_folder, 1860 const char *workingdir) 1861 { 1862 char executable_path[BUFSIZE]; 1863 char link_name[BUFSIZE]; 1864 1865 sprintf(executable_path, "%s\\%s", installdir, exename); 1866 1867 if (build_link_name(link_name, name, shell_folder) == FAIL) 1868 { 1869 printf("An error has occurred. A shortcut to %s will not be created %s.\n", 1870 name, 1871 *shell_folder == 'd' ? "on the desktop" : "in the Start menu"); 1872 return FAIL; 1873 } 1874 1875 // Create the shortcut: 1876 return create_shortcut(link_name, executable_path, 0, 1877 executable_path, args, workingdir); 1878 } 1879 1880 /* 1881 * We used to use "homedir" as the working directory, but that is a bad choice 1882 * on multi-user systems. However, not specifying a directory results in the 1883 * current directory to be c:\Windows\system32 on Windows 7. Use environment 1884 * variables instead. 1885 */ 1886 #define WORKDIR "%HOMEDRIVE%%HOMEPATH%" 1887 1888 /* 1889 * Create shortcut(s) in the Start Menu\Programs\Vim folder. 1890 */ 1891 static void 1892 install_start_menu(int idx) 1893 { 1894 need_uninstall_entry = 1; 1895 printf("Creating start menu\n"); 1896 if (has_vim) 1897 { 1898 if (build_shortcut("Vim", "vim.exe", "", 1899 VIM_STARTMENU, WORKDIR) == FAIL) 1900 return; 1901 if (build_shortcut("Vim Read-only", "vim.exe", "-R", 1902 VIM_STARTMENU, WORKDIR) == FAIL) 1903 return; 1904 if (build_shortcut("Vim Diff", "vim.exe", "-d", 1905 VIM_STARTMENU, WORKDIR) == FAIL) 1906 return; 1907 } 1908 if (has_gvim) 1909 { 1910 if (build_shortcut("gVim", "gvim.exe", "", 1911 VIM_STARTMENU, WORKDIR) == FAIL) 1912 return; 1913 if (build_shortcut("gVim Easy", "gvim.exe", "-y", 1914 VIM_STARTMENU, WORKDIR) == FAIL) 1915 return; 1916 if (build_shortcut("gVim Read-only", "gvim.exe", "-R", 1917 VIM_STARTMENU, WORKDIR) == FAIL) 1918 return; 1919 if (build_shortcut("gVim Diff", "gvim.exe", "-d", 1920 VIM_STARTMENU, WORKDIR) == FAIL) 1921 return; 1922 } 1923 if (build_shortcut("Uninstall", 1924 interactive ? "uninstall.exe" : "uninstall-gui.exe", "", 1925 VIM_STARTMENU, installdir) == FAIL) 1926 return; 1927 // For Windows NT the working dir of the vimtutor.bat must be right, 1928 // otherwise gvim.exe won't be found and using gvimbat doesn't work. 1929 if (build_shortcut("Vim tutor", "vimtutor.bat", "", 1930 VIM_STARTMENU, installdir) == FAIL) 1931 return; 1932 if (build_shortcut("Help", has_gvim ? "gvim.exe" : "vim.exe", "-c h", 1933 VIM_STARTMENU, WORKDIR) == FAIL) 1934 return; 1935 { 1936 char shell_folder_path[BUFSIZE]; 1937 1938 // Creating the URL shortcut works a bit differently... 1939 if (get_shell_folder_path(shell_folder_path, VIM_STARTMENU) == FAIL) 1940 { 1941 printf("Finding the path of the Start menu failed\n"); 1942 return ; 1943 } 1944 add_pathsep(shell_folder_path); 1945 strcat(shell_folder_path, "Vim Online.url"); 1946 if (!WritePrivateProfileString("InternetShortcut", "URL", 1947 "https://www.vim.org/", shell_folder_path)) 1948 { 1949 printf("Creating the Vim online URL failed\n"); 1950 return; 1951 } 1952 } 1953 } 1954 1955 static void 1956 toggle_startmenu_choice(int idx) 1957 { 1958 if (choices[idx].installfunc == NULL) 1959 { 1960 choices[idx].installfunc = install_start_menu; 1961 choices[idx].text = "Add Vim to the Start menu"; 1962 } 1963 else 1964 { 1965 choices[idx].installfunc = NULL; 1966 choices[idx].text = "Do NOT add Vim to the Start menu"; 1967 } 1968 } 1969 1970 /* 1971 * Function to actually create the shortcuts 1972 * 1973 * Currently I am supplying no working directory to the shortcut. This 1974 * means that the initial working dir will be: 1975 * - the location of the shortcut if no file is supplied 1976 * - the location of the file being edited if a file is supplied (ie via 1977 * drag and drop onto the shortcut). 1978 */ 1979 void 1980 install_shortcut_gvim(int idx) 1981 { 1982 // Create shortcut(s) on the desktop 1983 if (choices[idx].arg) 1984 { 1985 (void)build_shortcut(icon_names[0], "gvim.exe", 1986 "", "desktop", WORKDIR); 1987 need_uninstall_entry = 1; 1988 } 1989 } 1990 1991 void 1992 install_shortcut_evim(int idx) 1993 { 1994 if (choices[idx].arg) 1995 { 1996 (void)build_shortcut(icon_names[1], "gvim.exe", 1997 "-y", "desktop", WORKDIR); 1998 need_uninstall_entry = 1; 1999 } 2000 } 2001 2002 void 2003 install_shortcut_gview(int idx) 2004 { 2005 if (choices[idx].arg) 2006 { 2007 (void)build_shortcut(icon_names[2], "gvim.exe", 2008 "-R", "desktop", WORKDIR); 2009 need_uninstall_entry = 1; 2010 } 2011 } 2012 2013 void 2014 toggle_shortcut_choice(int idx) 2015 { 2016 char *arg; 2017 2018 if (choices[idx].installfunc == install_shortcut_gvim) 2019 arg = "gVim"; 2020 else if (choices[idx].installfunc == install_shortcut_evim) 2021 arg = "gVim Easy"; 2022 else 2023 arg = "gVim Read-only"; 2024 if (choices[idx].arg) 2025 { 2026 choices[idx].arg = 0; 2027 alloc_text(idx, "Do NOT create a desktop icon for %s", arg); 2028 } 2029 else 2030 { 2031 choices[idx].arg = 1; 2032 alloc_text(idx, "Create a desktop icon for %s", arg); 2033 } 2034 } 2035 2036 static void 2037 init_startmenu_choice(void) 2038 { 2039 // Start menu 2040 choices[choice_count].changefunc = toggle_startmenu_choice; 2041 choices[choice_count].installfunc = NULL; 2042 choices[choice_count].active = 1; 2043 toggle_startmenu_choice(choice_count); // set the text 2044 ++choice_count; 2045 } 2046 2047 /* 2048 * Add the choice for the desktop shortcuts. 2049 */ 2050 static void 2051 init_shortcut_choices(void) 2052 { 2053 // Shortcut to gvim 2054 choices[choice_count].text = NULL; 2055 choices[choice_count].arg = 0; 2056 choices[choice_count].active = has_gvim; 2057 choices[choice_count].changefunc = toggle_shortcut_choice; 2058 choices[choice_count].installfunc = install_shortcut_gvim; 2059 toggle_shortcut_choice(choice_count); 2060 ++choice_count; 2061 2062 // Shortcut to evim 2063 choices[choice_count].text = NULL; 2064 choices[choice_count].arg = 0; 2065 choices[choice_count].active = has_gvim; 2066 choices[choice_count].changefunc = toggle_shortcut_choice; 2067 choices[choice_count].installfunc = install_shortcut_evim; 2068 toggle_shortcut_choice(choice_count); 2069 ++choice_count; 2070 2071 // Shortcut to gview 2072 choices[choice_count].text = NULL; 2073 choices[choice_count].arg = 0; 2074 choices[choice_count].active = has_gvim; 2075 choices[choice_count].changefunc = toggle_shortcut_choice; 2076 choices[choice_count].installfunc = install_shortcut_gview; 2077 toggle_shortcut_choice(choice_count); 2078 ++choice_count; 2079 } 2080 2081 /* 2082 * Attempt to register OLE for Vim. 2083 */ 2084 static void 2085 install_OLE_register(void) 2086 { 2087 char register_command_string[BUFSIZE + 30]; 2088 2089 printf("\n--- Attempting to register Vim with OLE ---\n"); 2090 printf("(There is no message whether this works or not.)\n"); 2091 2092 sprintf(register_command_string, "\"%s\\gvim.exe\" -silent -register", installdir); 2093 system(register_command_string); 2094 } 2095 2096 /* 2097 * Remove the last part of directory "path[]" to get its parent, and put the 2098 * result in "to[]". 2099 */ 2100 static void 2101 dir_remove_last(const char *path, char to[MAX_PATH]) 2102 { 2103 char c; 2104 long last_char_to_copy; 2105 long path_length = strlen(path); 2106 2107 // skip the last character just in case it is a '\\' 2108 last_char_to_copy = path_length - 2; 2109 c = path[last_char_to_copy]; 2110 2111 while (c != '\\') 2112 { 2113 last_char_to_copy--; 2114 c = path[last_char_to_copy]; 2115 } 2116 2117 strncpy(to, path, (size_t)last_char_to_copy); 2118 to[last_char_to_copy] = NUL; 2119 } 2120 2121 static void 2122 set_directories_text(int idx) 2123 { 2124 int vimfiles_dir_choice = choices[idx].arg; 2125 2126 if (vimfiles_dir_choice == (int)vimfiles_dir_none) 2127 alloc_text(idx, "Do NOT create plugin directories%s", ""); 2128 else 2129 alloc_text(idx, "Create plugin directories: %s", 2130 vimfiles_dir_choices[vimfiles_dir_choice]); 2131 } 2132 2133 /* 2134 * To get the "real" home directory: 2135 * - get value of $HOME 2136 * - if not found, get value of $HOMEDRIVE$HOMEPATH 2137 * - if not found, get value of $USERPROFILE 2138 * 2139 * This code is based on init_homedir() in misc1.c, keep in sync! 2140 */ 2141 static char *homedir = NULL; 2142 2143 void 2144 init_homedir(void) 2145 { 2146 char *var; 2147 char buf[MAX_PATH]; 2148 2149 if (homedir != NULL) 2150 { 2151 free(homedir); 2152 homedir = NULL; 2153 } 2154 2155 var = getenv("HOME"); 2156 2157 /* 2158 * Typically, $HOME is not defined on Windows, unless the user has 2159 * specifically defined it for Vim's sake. However, on Windows NT 2160 * platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for 2161 * each user. Try constructing $HOME from these. 2162 */ 2163 if (var == NULL || *var == NUL) 2164 { 2165 char *homedrive, *homepath; 2166 2167 homedrive = getenv("HOMEDRIVE"); 2168 homepath = getenv("HOMEPATH"); 2169 if (homepath == NULL || *homepath == NUL) 2170 homepath = "\\"; 2171 if (homedrive != NULL 2172 && strlen(homedrive) + strlen(homepath) < sizeof(buf)) 2173 { 2174 sprintf(buf, "%s%s", homedrive, homepath); 2175 if (buf[0] != NUL) 2176 var = buf; 2177 } 2178 } 2179 2180 if (var == NULL) 2181 var = getenv("USERPROFILE"); 2182 2183 /* 2184 * Weird but true: $HOME may contain an indirect reference to another 2185 * variable, esp. "%USERPROFILE%". Happens when $USERPROFILE isn't set 2186 * when $HOME is being set. 2187 */ 2188 if (var != NULL && *var == '%') 2189 { 2190 char *p; 2191 char *exp; 2192 2193 p = strchr(var + 1, '%'); 2194 if (p != NULL) 2195 { 2196 strncpy(buf, var + 1, p - (var + 1)); 2197 buf[p - (var + 1)] = NUL; 2198 exp = getenv(buf); 2199 if (exp != NULL && *exp != NUL 2200 && strlen(exp) + strlen(p) < sizeof(buf)) 2201 { 2202 sprintf(buf, "%s%s", exp, p + 1); 2203 var = buf; 2204 } 2205 } 2206 } 2207 2208 if (var != NULL && *var == NUL) // empty is same as not set 2209 var = NULL; 2210 2211 if (var == NULL) 2212 homedir = NULL; 2213 else 2214 homedir = _strdup(var); 2215 } 2216 2217 /* 2218 * Change the directory that the vim plugin directories will be created in: 2219 * $HOME, $VIM or nowhere. 2220 */ 2221 static void 2222 change_directories_choice(int idx) 2223 { 2224 int choice_count = TABLE_SIZE(vimfiles_dir_choices); 2225 2226 // Don't offer the $HOME choice if $HOME isn't set. 2227 if (homedir == NULL) 2228 --choice_count; 2229 choices[idx].arg = get_choice(vimfiles_dir_choices, choice_count); 2230 set_directories_text(idx); 2231 } 2232 2233 /* 2234 * Create the plugin directories... 2235 */ 2236 //ARGSUSED 2237 static void 2238 install_vimfilesdir(int idx) 2239 { 2240 int i; 2241 int vimfiles_dir_choice = choices[idx].arg; 2242 char *p; 2243 char vimdir_path[MAX_PATH]; 2244 char vimfiles_path[MAX_PATH + 9]; 2245 char tmp_dirname[BUFSIZE]; 2246 2247 // switch on the location that the user wants the plugin directories 2248 // built in 2249 switch (vimfiles_dir_choice) 2250 { 2251 case vimfiles_dir_vim: 2252 { 2253 // Go to the %VIM% directory - check env first, then go one dir 2254 // below installdir if there is no %VIM% environment variable. 2255 // The accuracy of $VIM is checked in inspect_system(), so we 2256 // can be sure it is ok to use here. 2257 p = getenv("VIM"); 2258 if (p == NULL) // No $VIM in path 2259 dir_remove_last(installdir, vimdir_path); 2260 else 2261 strcpy(vimdir_path, p); 2262 break; 2263 } 2264 case vimfiles_dir_home: 2265 { 2266 // Find the $HOME directory. Its existence was already checked. 2267 p = homedir; 2268 if (p == NULL) 2269 { 2270 printf("Internal error: $HOME is NULL\n"); 2271 p = "c:\\"; 2272 } 2273 strcpy(vimdir_path, p); 2274 break; 2275 } 2276 case vimfiles_dir_none: 2277 { 2278 // Do not create vim plugin directory. 2279 return; 2280 } 2281 } 2282 2283 // Now, just create the directory. If it already exists, it will fail 2284 // silently. 2285 sprintf(vimfiles_path, "%s\\vimfiles", vimdir_path); 2286 vim_mkdir(vimfiles_path, 0755); 2287 2288 printf("Creating the following directories in \"%s\":\n", vimfiles_path); 2289 for (i = 0; i < TABLE_SIZE(vimfiles_subdirs); i++) 2290 { 2291 sprintf(tmp_dirname, "%s\\%s", vimfiles_path, vimfiles_subdirs[i]); 2292 printf(" %s", vimfiles_subdirs[i]); 2293 vim_mkdir(tmp_dirname, 0755); 2294 } 2295 printf("\n"); 2296 } 2297 2298 /* 2299 * Add the creation of runtime files to the setup sequence. 2300 */ 2301 static void 2302 init_directories_choice(void) 2303 { 2304 struct stat st; 2305 char tmp_dirname[BUFSIZE]; 2306 char *p; 2307 int vimfiles_dir_choice; 2308 2309 choices[choice_count].text = alloc(150); 2310 choices[choice_count].changefunc = change_directories_choice; 2311 choices[choice_count].installfunc = install_vimfilesdir; 2312 choices[choice_count].active = 1; 2313 2314 // Check if the "compiler" directory already exists. That's a good 2315 // indication that the plugin directories were already created. 2316 p = getenv("HOME"); 2317 if (p != NULL) 2318 { 2319 vimfiles_dir_choice = (int)vimfiles_dir_home; 2320 sprintf(tmp_dirname, "%s\\vimfiles\\compiler", p); 2321 if (stat(tmp_dirname, &st) == 0) 2322 vimfiles_dir_choice = (int)vimfiles_dir_none; 2323 } 2324 else 2325 { 2326 vimfiles_dir_choice = (int)vimfiles_dir_vim; 2327 p = getenv("VIM"); 2328 if (p == NULL) // No $VIM in path, use the install dir. 2329 dir_remove_last(installdir, tmp_dirname); 2330 else 2331 strcpy(tmp_dirname, p); 2332 strcat(tmp_dirname, "\\vimfiles\\compiler"); 2333 if (stat(tmp_dirname, &st) == 0) 2334 vimfiles_dir_choice = (int)vimfiles_dir_none; 2335 } 2336 2337 choices[choice_count].arg = vimfiles_dir_choice; 2338 set_directories_text(choice_count); 2339 ++choice_count; 2340 } 2341 2342 /* 2343 * Setup the choices and the default values. 2344 */ 2345 static void 2346 setup_choices(void) 2347 { 2348 // install the batch files 2349 init_bat_choices(); 2350 2351 // (over) write _vimrc file 2352 init_vimrc_choices(); 2353 2354 // Whether to add Vim to the popup menu 2355 init_popup_choice(); 2356 2357 // Whether to add Vim to the "Open With..." menu 2358 init_openwith_choice(); 2359 2360 // Whether to add Vim to the Start Menu. 2361 init_startmenu_choice(); 2362 2363 // Whether to add shortcuts to the Desktop. 2364 init_shortcut_choices(); 2365 2366 // Whether to create the runtime directories. 2367 init_directories_choice(); 2368 } 2369 2370 static void 2371 print_cmd_line_help(void) 2372 { 2373 printf("Vim installer non-interactive command line arguments:\n"); 2374 printf("\n"); 2375 printf("-create-batfiles [vim gvim evim view gview vimdiff gvimdiff]\n"); 2376 printf(" Create .bat files for Vim variants in the Windows directory.\n"); 2377 printf("-create-vimrc\n"); 2378 printf(" Create a default _vimrc file if one does not already exist.\n"); 2379 printf("-vimrc-remap [no|win]\n"); 2380 printf(" Remap keys when creating a default _vimrc file.\n"); 2381 printf("-vimrc-behave [unix|mswin|default]\n"); 2382 printf(" Set mouse behavior when creating a default _vimrc file.\n"); 2383 printf("-vimrc-compat [vi|vim|defaults|all]\n"); 2384 printf(" Set Vi compatibility when creating a default _vimrc file.\n"); 2385 printf("-install-popup\n"); 2386 printf(" Install the Edit-with-Vim context menu entry\n"); 2387 printf("-install-openwith\n"); 2388 printf(" Add Vim to the \"Open With...\" context menu list\n"); 2389 printf("-add-start-menu"); 2390 printf(" Add Vim to the start menu\n"); 2391 printf("-install-icons"); 2392 printf(" Create icons for gVim executables on the desktop\n"); 2393 printf("-create-directories [vim|home]\n"); 2394 printf(" Create runtime directories to drop plugins into; in the $VIM\n"); 2395 printf(" or $HOME directory\n"); 2396 printf("-register-OLE"); 2397 printf(" Ignored\n"); 2398 printf("\n"); 2399 } 2400 2401 /* 2402 * Setup installation choices based on command line switches 2403 */ 2404 static void 2405 command_line_setup_choices(int argc, char **argv) 2406 { 2407 int i, j; 2408 2409 for (i = 1; i < argc; i++) 2410 { 2411 if (strcmp(argv[i], "-create-batfiles") == 0) 2412 { 2413 if (i + 1 == argc) 2414 continue; 2415 while (argv[i + 1][0] != '-' && i < argc) 2416 { 2417 i++; 2418 for (j = 1; j < TARGET_COUNT; ++j) 2419 if ((targets[j].exenamearg[0] == 'g' ? has_gvim : has_vim) 2420 && strcmp(argv[i], targets[j].name) == 0) 2421 { 2422 init_bat_choice(j); 2423 break; 2424 } 2425 if (j == TARGET_COUNT) 2426 printf("%s is not a valid choice for -create-batfiles\n", 2427 argv[i]); 2428 2429 if (i + 1 == argc) 2430 break; 2431 } 2432 } 2433 else if (strcmp(argv[i], "-create-vimrc") == 0) 2434 { 2435 // Setup default vimrc choices. If there is already a _vimrc file, 2436 // it will NOT be overwritten. 2437 init_vimrc_choices(); 2438 } 2439 else if (strcmp(argv[i], "-vimrc-remap") == 0) 2440 { 2441 if (i + 1 == argc) 2442 break; 2443 i++; 2444 if (strcmp(argv[i], "no") == 0) 2445 remap_choice = remap_no; 2446 else if (strcmp(argv[i], "win") == 0) 2447 remap_choice = remap_win; 2448 } 2449 else if (strcmp(argv[i], "-vimrc-behave") == 0) 2450 { 2451 if (i + 1 == argc) 2452 break; 2453 i++; 2454 if (strcmp(argv[i], "unix") == 0) 2455 mouse_choice = mouse_xterm; 2456 else if (strcmp(argv[i], "mswin") == 0) 2457 mouse_choice = mouse_mswin; 2458 else if (strcmp(argv[i], "default") == 0) 2459 mouse_choice = mouse_default; 2460 } 2461 else if (strcmp(argv[i], "-vimrc-compat") == 0) 2462 { 2463 if (i + 1 == argc) 2464 break; 2465 i++; 2466 if (strcmp(argv[i], "vi") == 0) 2467 compat_choice = compat_vi; 2468 else if (strcmp(argv[i], "vim") == 0) 2469 compat_choice = compat_vim; 2470 else if (strcmp(argv[i], "defaults") == 0) 2471 compat_choice = compat_some_enhancements; 2472 else if (strcmp(argv[i], "all") == 0) 2473 compat_choice = compat_all_enhancements; 2474 } 2475 else if (strcmp(argv[i], "-install-popup") == 0) 2476 { 2477 init_popup_choice(); 2478 } 2479 else if (strcmp(argv[i], "-install-openwith") == 0) 2480 { 2481 init_openwith_choice(); 2482 } 2483 else if (strcmp(argv[i], "-add-start-menu") == 0) 2484 { 2485 init_startmenu_choice(); 2486 } 2487 else if (strcmp(argv[i], "-install-icons") == 0) 2488 { 2489 init_shortcut_choices(); 2490 } 2491 else if (strcmp(argv[i], "-create-directories") == 0) 2492 { 2493 int vimfiles_dir_choice = (int)vimfiles_dir_none; 2494 2495 init_directories_choice(); 2496 if (argv[i + 1][0] != '-') 2497 { 2498 i++; 2499 if (strcmp(argv[i], "vim") == 0) 2500 vimfiles_dir_choice = (int)vimfiles_dir_vim; 2501 else if (strcmp(argv[i], "home") == 0) 2502 { 2503 if (homedir == NULL) // No $HOME in environment 2504 vimfiles_dir_choice = (int)vimfiles_dir_none; 2505 else 2506 vimfiles_dir_choice = (int)vimfiles_dir_home; 2507 } 2508 else 2509 { 2510 printf("Unknown argument for -create-directories: %s\n", 2511 argv[i]); 2512 print_cmd_line_help(); 2513 } 2514 } 2515 else // No choice specified, default to vim directory 2516 vimfiles_dir_choice = (int)vimfiles_dir_vim; 2517 choices[choice_count - 1].arg = vimfiles_dir_choice; 2518 } 2519 else if (strcmp(argv[i], "-register-OLE") == 0) 2520 { 2521 // This is always done when gvim is found 2522 } 2523 else // Unknown switch 2524 { 2525 printf("Got unknown argument argv[%d] = %s\n", i, argv[i]); 2526 print_cmd_line_help(); 2527 } 2528 } 2529 } 2530 2531 2532 /* 2533 * Show a few screens full of helpful information. 2534 */ 2535 static void 2536 show_help(void) 2537 { 2538 static char *(items[]) = 2539 { 2540 "Installing .bat files\n" 2541 "---------------------\n" 2542 "The vim.bat file is written in one of the directories in $PATH.\n" 2543 "This makes it possible to start Vim from the command line.\n" 2544 "If vim.exe can be found in $PATH, the choice for vim.bat will not be\n" 2545 "present. It is assumed you will use the existing vim.exe.\n" 2546 "If vim.bat can already be found in $PATH this is probably for an old\n" 2547 "version of Vim (but this is not checked!). You can overwrite it.\n" 2548 "If no vim.bat already exists, you can select one of the directories in\n" 2549 "$PATH for creating the batch file, or disable creating a vim.bat file.\n" 2550 "\n" 2551 "If you choose not to create the vim.bat file, Vim can still be executed\n" 2552 "in other ways, but not from the command line.\n" 2553 "\n" 2554 "The same applies to choices for gvim, evim, (g)view, and (g)vimdiff.\n" 2555 "The first item can be used to change the path for all of them.\n" 2556 , 2557 "Creating a _vimrc file\n" 2558 "----------------------\n" 2559 "The _vimrc file is used to set options for how Vim behaves.\n" 2560 "The install program can create a _vimrc file with a few basic choices.\n" 2561 "You can edit this file later to tune your preferences.\n" 2562 "If you already have a _vimrc or .vimrc file it can be overwritten.\n" 2563 "Don't do that if you have made changes to it.\n" 2564 , 2565 "Vim features\n" 2566 "------------\n" 2567 "(this choice is only available when creating a _vimrc file)\n" 2568 "1. Vim can run in Vi-compatible mode. Many nice Vim features are then\n" 2569 " disabled. Only choose Vi-compatible if you really need full Vi\n" 2570 " compatibility.\n" 2571 "2. Vim runs in not-Vi-compatible mode. Vim is still mostly Vi compatible,\n" 2572 " but adds nice features like multi-level undo.\n" 2573 "3. Running Vim with some enhancements is useful when you want some of\n" 2574 " the nice Vim features, but have a slow computer and want to keep it\n" 2575 " really fast.\n" 2576 "4. Syntax highlighting shows many files in color. Not only does this look\n" 2577 " nice, it also makes it easier to spot errors and you can work faster.\n" 2578 " The other features include editing compressed files.\n" 2579 , 2580 "Windows key mapping\n" 2581 "-------------------\n" 2582 "(this choice is only available when creating a _vimrc file)\n" 2583 "Under MS-Windows the CTRL-C key copies text to the clipboard and CTRL-V\n" 2584 "pastes text from the clipboard. There are a few more keys like these.\n" 2585 "Unfortunately, in Vim these keys normally have another meaning.\n" 2586 "1. Choose to have the keys like they normally are in Vim (useful if you\n" 2587 " also use Vim on other systems).\n" 2588 "2. Choose to have the keys work like they are used on MS-Windows (useful\n" 2589 " if you mostly work on MS-Windows).\n" 2590 , 2591 "Mouse use\n" 2592 "---------\n" 2593 "(this choice is only available when creating a _vimrc file)\n" 2594 "The right mouse button can be used in two ways:\n" 2595 "1. The Unix way is to extend an existing selection. The popup menu is\n" 2596 " not available.\n" 2597 "2. The MS-Windows way is to show a popup menu, which allows you to\n" 2598 " copy/paste text, undo/redo, etc. Extending the selection can still be\n" 2599 " done by keeping SHIFT pressed while using the left mouse button\n" 2600 , 2601 "Edit-with-Vim context menu entry\n" 2602 "--------------------------------\n" 2603 "(this choice is only available when gvim.exe and gvimext.dll are present)\n" 2604 "You can associate different file types with Vim, so that you can (double)\n" 2605 "click on a file to edit it with Vim. This means you have to individually\n" 2606 "select each file type.\n" 2607 "An alternative is the option offered here: Install an \"Edit with Vim\"\n" 2608 "entry in the popup menu for the right mouse button. This means you can\n" 2609 "edit any file with Vim.\n" 2610 , 2611 "\"Open With...\" context menu entry\n" 2612 "--------------------------------\n" 2613 "(this choice is only available when gvim.exe is present)\n" 2614 "This option adds Vim to the \"Open With...\" entry in the popup menu for\n" 2615 "the right mouse button. This also makes it possible to edit HTML files\n" 2616 "directly from Internet Explorer.\n" 2617 , 2618 "Add Vim to the Start menu\n" 2619 "-------------------------\n" 2620 "In Windows 95 and later, Vim can be added to the Start menu. This will\n" 2621 "create a submenu with an entry for vim, gvim, evim, vimdiff, etc..\n" 2622 , 2623 "Icons on the desktop\n" 2624 "--------------------\n" 2625 "(these choices are only available when installing gvim)\n" 2626 "In Windows 95 and later, shortcuts (icons) can be created on the Desktop.\n" 2627 , 2628 "Create plugin directories\n" 2629 "-------------------------\n" 2630 "Plugin directories allow extending Vim by dropping a file into a directory.\n" 2631 "This choice allows creating them in $HOME (if you have a home directory) or\n" 2632 "$VIM (used for everybody on the system).\n" 2633 , 2634 NULL 2635 }; 2636 int i; 2637 int c; 2638 2639 rewind(stdin); 2640 printf("\n"); 2641 for (i = 0; items[i] != NULL; ++i) 2642 { 2643 puts(items[i]); 2644 printf("Hit Enter to continue, b (back) or q (quit help): "); 2645 c = getchar(); 2646 rewind(stdin); 2647 if (c == 'b' || c == 'B') 2648 { 2649 if (i == 0) 2650 --i; 2651 else 2652 i -= 2; 2653 } 2654 if (c == 'q' || c == 'Q') 2655 break; 2656 printf("\n"); 2657 } 2658 } 2659 2660 /* 2661 * Install the choices. 2662 */ 2663 static void 2664 install(void) 2665 { 2666 int i; 2667 2668 // Install the selected choices. 2669 for (i = 0; i < choice_count; ++i) 2670 if (choices[i].installfunc != NULL && choices[i].active) 2671 (choices[i].installfunc)(i); 2672 2673 // Add some entries to the registry, if needed. 2674 if (install_popup 2675 || install_openwith 2676 || (need_uninstall_entry && interactive) 2677 || !interactive) 2678 install_registry(); 2679 2680 // Register gvim with OLE. 2681 if (has_gvim) 2682 install_OLE_register(); 2683 } 2684 2685 /* 2686 * request_choice 2687 */ 2688 static void 2689 request_choice(void) 2690 { 2691 int i; 2692 2693 printf("\n\nInstall will do for you:\n"); 2694 for (i = 0; i < choice_count; ++i) 2695 if (choices[i].active) 2696 printf("%2d %s\n", i + 1, choices[i].text); 2697 printf("To change an item, enter its number\n\n"); 2698 printf("Enter item number, h (help), d (do it) or q (quit): "); 2699 } 2700 2701 int 2702 main(int argc, char **argv) 2703 { 2704 int i; 2705 char buf[BUFSIZE]; 2706 2707 /* 2708 * Run interactively if there are no command line arguments. 2709 */ 2710 if (argc > 1) 2711 interactive = 0; 2712 else 2713 interactive = 1; 2714 2715 // Initialize this program. 2716 do_inits(argv); 2717 init_homedir(); 2718 2719 if (argc > 1 && strcmp(argv[1], "-uninstall-check") == 0) 2720 { 2721 // Only check for already installed Vims. Used by NSIS installer. 2722 i = uninstall_check(1); 2723 2724 // Find the value of $VIM, because NSIS isn't able to do this by 2725 // itself. 2726 get_vim_env(); 2727 2728 // When nothing found exit quietly. If something found wait for 2729 // a little while, so that the user can read the messages. 2730 if (i && _isatty(1)) 2731 sleep(3); 2732 exit(0); 2733 } 2734 2735 printf("This program sets up the installation of Vim " 2736 VIM_VERSION_MEDIUM "\n\n"); 2737 2738 // Check if the user unpacked the archives properly. 2739 check_unpack(); 2740 2741 // Check for already installed Vims. 2742 if (interactive) 2743 uninstall_check(0); 2744 2745 // Find out information about the system. 2746 inspect_system(); 2747 2748 if (interactive) 2749 { 2750 // Setup all the choices. 2751 setup_choices(); 2752 2753 // Let the user change choices and finally install (or quit). 2754 for (;;) 2755 { 2756 request_choice(); 2757 rewind(stdin); 2758 if (scanf("%99s", buf) == 1) 2759 { 2760 if (isdigit(buf[0])) 2761 { 2762 // Change a choice. 2763 i = atoi(buf); 2764 if (i > 0 && i <= choice_count && choices[i - 1].active) 2765 (choices[i - 1].changefunc)(i - 1); 2766 else 2767 printf("\nIllegal choice\n"); 2768 } 2769 else if (buf[0] == 'h' || buf[0] == 'H') 2770 { 2771 // Help 2772 show_help(); 2773 } 2774 else if (buf[0] == 'd' || buf[0] == 'D') 2775 { 2776 // Install! 2777 install(); 2778 printf("\nThat finishes the installation. Happy Vimming!\n"); 2779 break; 2780 } 2781 else if (buf[0] == 'q' || buf[0] == 'Q') 2782 { 2783 // Quit 2784 printf("\nExiting without anything done\n"); 2785 break; 2786 } 2787 else 2788 printf("\nIllegal choice\n"); 2789 } 2790 } 2791 printf("\n"); 2792 myexit(0); 2793 } 2794 else 2795 { 2796 /* 2797 * Run non-interactive - setup according to the command line switches 2798 */ 2799 command_line_setup_choices(argc, argv); 2800 install(); 2801 2802 // Avoid that the user has to hit Enter, just wait a little bit to 2803 // allow reading the messages. 2804 sleep(2); 2805 } 2806 2807 return 0; 2808 } 2809