xref: /vim-8.2.3635/src/dosinst.c (revision 0894e0d8)
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)ARRAYSIZE(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
get_choice(char ** table,int entries)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
check_unpack(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
pathcmp(char * p,int plen,char * q,int qlen)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
findoldfile(char ** destination)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
find_bat_exe(int check_bat_only)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
get_vim_env(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
window_cb(HWND hwnd,LPARAM lparam)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
run_silent_uninstall(char * uninst_exe)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
uninstall_check(int skip_question)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
inspect_system(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
add_dummy_choice(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
install_bat_choice(int idx)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
alloc_text(int idx,char * fmt,char * arg)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
toggle_bat_choice(int idx)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
set_bat_text(int idx,char * batpath,char * name)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
change_bat_choice(int idx)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
change_main_bat_choice(int idx)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
init_bat_choice(int target)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
init_bat_choices(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
install_vimrc(int idx)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
change_vimrc_choice(int idx)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
change_run_choice(int idx)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
change_remap_choice(int idx)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
change_mouse_choice(int idx)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
init_vimrc_choices(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
reg_create_key(HKEY root,const char * subkey,PHKEY phKey,DWORD flag)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
reg_set_string_value(HKEY hKey,const char * value_name,const char * data)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
reg_create_key_and_value(HKEY hRootKey,const char * subkey,const char * value_name,const char * data,DWORD flag)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
register_inproc_server(HKEY hRootKey,const char * clsid,const char * extname,const char * module,const char * threading_model,DWORD flag)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
register_shellex(HKEY hRootKey,const char * clsid,const char * name,const char * exe_path,DWORD flag)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
register_openwith(HKEY hRootKey,const char * exe_path,DWORD flag)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 && i < ARRAYSIZE(openwith); i++)
1531 	    lRet = reg_create_key_and_value(hRootKey, openwith[i], NULL, "", flag);
1532     }
1533 
1534     return lRet;
1535 }
1536 
1537     static LONG
register_uninstall(HKEY hRootKey,const char * appname,const char * display_name,const char * uninstall_string,const char * display_icon,const char * display_version,const char * publisher)1538 register_uninstall(
1539     HKEY hRootKey,
1540     const char *appname,
1541     const char *display_name,
1542     const char *uninstall_string,
1543     const char *display_icon,
1544     const char *display_version,
1545     const char *publisher)
1546 {
1547     LONG lRet = reg_create_key_and_value(hRootKey, appname,
1548 			     "DisplayName", display_name, KEY_WOW64_64KEY);
1549 
1550     if (ERROR_SUCCESS == lRet)
1551 	lRet = reg_create_key_and_value(hRootKey, appname,
1552 		     "UninstallString", uninstall_string, KEY_WOW64_64KEY);
1553     if (ERROR_SUCCESS == lRet)
1554 	lRet = reg_create_key_and_value(hRootKey, appname,
1555 		     "DisplayIcon", display_icon, KEY_WOW64_64KEY);
1556     if (ERROR_SUCCESS == lRet)
1557 	lRet = reg_create_key_and_value(hRootKey, appname,
1558 		     "DisplayVersion", display_version, KEY_WOW64_64KEY);
1559     if (ERROR_SUCCESS == lRet)
1560 	lRet = reg_create_key_and_value(hRootKey, appname,
1561 		     "Publisher", publisher, KEY_WOW64_64KEY);
1562     return lRet;
1563 }
1564 
1565 /*
1566  * Add some entries to the registry:
1567  * - to add "Edit with Vim" to the context * menu
1568  * - to add Vim to the "Open with..." list
1569  * - to uninstall Vim
1570  */
1571 //ARGSUSED
1572     static int
install_registry(void)1573 install_registry(void)
1574 {
1575     LONG	lRet = ERROR_SUCCESS;
1576     const char	*vim_ext_ThreadingModel = "Apartment";
1577     const char	*vim_ext_name = "Vim Shell Extension";
1578     const char	*vim_ext_clsid = "{51EEE242-AD87-11d3-9C1E-0090278BBD99}";
1579     char	vim_exe_path[MAX_PATH];
1580     char	display_name[BUFSIZE];
1581     char	uninstall_string[BUFSIZE];
1582     char	icon_string[BUFSIZE];
1583     char	version_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     sprintf(version_string, VIM_VERSION_SHORT "." VIM_VERSION_PATCHLEVEL_STR);
1657 
1658     lRet = register_uninstall(
1659 	HKEY_LOCAL_MACHINE,
1660 	"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT,
1661 	display_name,
1662 	uninstall_string,
1663 	icon_string,
1664 	version_string,
1665 	"Bram Moolenaar et al.");
1666     if (ERROR_SUCCESS != lRet)
1667 	return FAIL;
1668 
1669     return OK;
1670 }
1671 
1672     static void
change_popup_choice(int idx)1673 change_popup_choice(int idx)
1674 {
1675     if (install_popup == 0)
1676     {
1677 	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";
1678 	install_popup = 1;
1679     }
1680     else
1681     {
1682 	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";
1683 	install_popup = 0;
1684     }
1685 }
1686 
1687 /*
1688  * Only add the choice for the popup menu entry when gvim.exe was found and
1689  * both gvimext.dll and regedit.exe exist.
1690  */
1691     static void
init_popup_choice(void)1692 init_popup_choice(void)
1693 {
1694     struct stat	st;
1695 
1696     if (has_gvim
1697 	    && (stat(GVIMEXT32_PATH, &st) >= 0
1698 		|| stat(GVIMEXT64_PATH, &st) >= 0))
1699     {
1700 	choices[choice_count].changefunc = change_popup_choice;
1701 	choices[choice_count].installfunc = NULL;
1702 	choices[choice_count].active = 1;
1703 	change_popup_choice(choice_count);  // set the text
1704 	++choice_count;
1705     }
1706     else
1707 	add_dummy_choice();
1708 }
1709 
1710     static void
change_openwith_choice(int idx)1711 change_openwith_choice(int idx)
1712 {
1713     if (install_openwith == 0)
1714     {
1715 	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";
1716 	install_openwith = 1;
1717     }
1718     else
1719     {
1720 	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";
1721 	install_openwith = 0;
1722     }
1723 }
1724 
1725 /*
1726  * Only add the choice for the open-with menu entry when gvim.exe was found
1727  * and regedit.exe exist.
1728  */
1729     static void
init_openwith_choice(void)1730 init_openwith_choice(void)
1731 {
1732     if (has_gvim)
1733     {
1734 	choices[choice_count].changefunc = change_openwith_choice;
1735 	choices[choice_count].installfunc = NULL;
1736 	choices[choice_count].active = 1;
1737 	change_openwith_choice(choice_count);  // set the text
1738 	++choice_count;
1739     }
1740     else
1741 	add_dummy_choice();
1742 }
1743 
1744 /*
1745  * Create a shell link.
1746  *
1747  * returns 0 on failure, non-zero on successful completion.
1748  *
1749  * NOTE:  Currently untested with mingw.
1750  */
1751     int
create_shortcut(const char * shortcut_name,const char * iconfile_path,int iconindex,const char * shortcut_target,const char * shortcut_args,const char * workingdir)1752 create_shortcut(
1753 	const char *shortcut_name,
1754 	const char *iconfile_path,
1755 	int	    iconindex,
1756 	const char *shortcut_target,
1757 	const char *shortcut_args,
1758 	const char *workingdir
1759 	)
1760 {
1761     IShellLink	    *shelllink_ptr;
1762     HRESULT	    hres;
1763     IPersistFile	    *persistfile_ptr;
1764 
1765     // Initialize COM library
1766     hres = CoInitialize(NULL);
1767     if (!SUCCEEDED(hres))
1768     {
1769 	printf("Error:  Could not open the COM library.  Not creating shortcut.\n");
1770 	return FAIL;
1771     }
1772 
1773     // Instantiate a COM object for the ShellLink, store a pointer to it
1774     // in shelllink_ptr.
1775     hres = CoCreateInstance(&CLSID_ShellLink,
1776 			   NULL,
1777 			   CLSCTX_INPROC_SERVER,
1778 			   &IID_IShellLink,
1779 			   (void **) &shelllink_ptr);
1780 
1781     if (SUCCEEDED(hres)) // If the instantiation was successful...
1782     {
1783 	// ...Then build a PersistFile interface for the ShellLink so we can
1784 	// save it as a file after we build it.
1785 	hres = shelllink_ptr->lpVtbl->QueryInterface(shelllink_ptr,
1786 		&IID_IPersistFile, (void **) &persistfile_ptr);
1787 
1788 	if (SUCCEEDED(hres))
1789 	{
1790 	    wchar_t wsz[BUFSIZE];
1791 
1792 	    // translate the (possibly) multibyte shortcut filename to windows
1793 	    // Unicode so it can be used as a file name.
1794 	    MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, wsz, sizeof(wsz)/sizeof(wsz[0]));
1795 
1796 	    // set the attributes
1797 	    shelllink_ptr->lpVtbl->SetPath(shelllink_ptr, shortcut_target);
1798 	    shelllink_ptr->lpVtbl->SetWorkingDirectory(shelllink_ptr,
1799 								  workingdir);
1800 	    shelllink_ptr->lpVtbl->SetIconLocation(shelllink_ptr,
1801 						    iconfile_path, iconindex);
1802 	    shelllink_ptr->lpVtbl->SetArguments(shelllink_ptr, shortcut_args);
1803 
1804 	    // save the shortcut to a file and return the PersistFile object
1805 	    persistfile_ptr->lpVtbl->Save(persistfile_ptr, wsz, 1);
1806 	    persistfile_ptr->lpVtbl->Release(persistfile_ptr);
1807 	}
1808 	else
1809 	{
1810 	    printf("QueryInterface Error\n");
1811 	    return FAIL;
1812 	}
1813 
1814 	// Return the ShellLink object
1815 	shelllink_ptr->lpVtbl->Release(shelllink_ptr);
1816     }
1817     else
1818     {
1819 	printf("CoCreateInstance Error - hres = %08x\n", (int)hres);
1820 	return FAIL;
1821     }
1822 
1823     return OK;
1824 }
1825 
1826 /*
1827  * Build a path to where we will put a specified link.
1828  *
1829  * Return 0 on error, non-zero on success
1830  */
1831    int
build_link_name(char * link_path,const char * link_name,const char * shell_folder_name)1832 build_link_name(
1833 	char	   *link_path,
1834 	const char *link_name,
1835 	const char *shell_folder_name)
1836 {
1837     char	shell_folder_path[MAX_PATH];
1838 
1839     if (get_shell_folder_path(shell_folder_path, shell_folder_name) == FAIL)
1840     {
1841 	printf("An error occurred while attempting to find the path to %s.\n",
1842 							   shell_folder_name);
1843 	return FAIL;
1844     }
1845 
1846     // Make sure the directory exists (create Start Menu\Programs\Vim).
1847     // Ignore errors if it already exists.
1848     vim_mkdir(shell_folder_path, 0755);
1849 
1850     // build the path to the shortcut and the path to gvim.exe
1851     sprintf(link_path, "%s\\%s.lnk", shell_folder_path, link_name);
1852 
1853     return OK;
1854 }
1855 
1856     static int
build_shortcut(const char * name,const char * exename,const char * args,const char * shell_folder,const char * workingdir)1857 build_shortcut(
1858 	const char *name,	// Name of the shortcut
1859 	const char *exename,	// Name of the executable (e.g., vim.exe)
1860 	const char *args,
1861 	const char *shell_folder,
1862 	const char *workingdir)
1863 {
1864     char	executable_path[BUFSIZE];
1865     char	link_name[BUFSIZE];
1866 
1867     sprintf(executable_path, "%s\\%s", installdir, exename);
1868 
1869     if (build_link_name(link_name, name, shell_folder) == FAIL)
1870     {
1871 	printf("An error has occurred.  A shortcut to %s will not be created %s.\n",
1872 		name,
1873 		*shell_folder == 'd' ? "on the desktop" : "in the Start menu");
1874 	return FAIL;
1875     }
1876 
1877     // Create the shortcut:
1878     return create_shortcut(link_name, executable_path, 0,
1879 					   executable_path, args, workingdir);
1880 }
1881 
1882 /*
1883  * We used to use "homedir" as the working directory, but that is a bad choice
1884  * on multi-user systems.  However, not specifying a directory results in the
1885  * current directory to be c:\Windows\system32 on Windows 7. Use environment
1886  * variables instead.
1887  */
1888 #define WORKDIR "%HOMEDRIVE%%HOMEPATH%"
1889 
1890 /*
1891  * Create shortcut(s) in the Start Menu\Programs\Vim folder.
1892  */
1893     static void
install_start_menu(int idx)1894 install_start_menu(int idx)
1895 {
1896     need_uninstall_entry = 1;
1897     printf("Creating start menu\n");
1898     if (has_vim)
1899     {
1900 	if (build_shortcut("Vim", "vim.exe", "",
1901 					      VIM_STARTMENU, WORKDIR) == FAIL)
1902 	    return;
1903 	if (build_shortcut("Vim Read-only", "vim.exe", "-R",
1904 					      VIM_STARTMENU, WORKDIR) == FAIL)
1905 	    return;
1906 	if (build_shortcut("Vim Diff", "vim.exe", "-d",
1907 					      VIM_STARTMENU, WORKDIR) == FAIL)
1908 	    return;
1909     }
1910     if (has_gvim)
1911     {
1912 	if (build_shortcut("gVim", "gvim.exe", "",
1913 					      VIM_STARTMENU, WORKDIR) == FAIL)
1914 	    return;
1915 	if (build_shortcut("gVim Easy", "gvim.exe", "-y",
1916 					      VIM_STARTMENU, WORKDIR) == FAIL)
1917 	    return;
1918 	if (build_shortcut("gVim Read-only", "gvim.exe", "-R",
1919 					      VIM_STARTMENU, WORKDIR) == FAIL)
1920 	    return;
1921 	if (build_shortcut("gVim Diff", "gvim.exe", "-d",
1922 					      VIM_STARTMENU, WORKDIR) == FAIL)
1923 	    return;
1924     }
1925     if (build_shortcut("Uninstall",
1926 		interactive ? "uninstall.exe" : "uninstall-gui.exe", "",
1927 					   VIM_STARTMENU, installdir) == FAIL)
1928 	return;
1929     // For Windows NT the working dir of the vimtutor.bat must be right,
1930     // otherwise gvim.exe won't be found and using gvimbat doesn't work.
1931     if (build_shortcut("Vim tutor", "vimtutor.bat", "",
1932 					   VIM_STARTMENU, installdir) == FAIL)
1933 	return;
1934     if (build_shortcut("Help", has_gvim ? "gvim.exe" : "vim.exe", "-c h",
1935 					      VIM_STARTMENU, WORKDIR) == FAIL)
1936 	return;
1937     {
1938 	char	shell_folder_path[BUFSIZE];
1939 
1940 	// Creating the URL shortcut works a bit differently...
1941 	if (get_shell_folder_path(shell_folder_path, VIM_STARTMENU) == FAIL)
1942 	{
1943 	    printf("Finding the path of the Start menu failed\n");
1944 	    return ;
1945 	}
1946 	add_pathsep(shell_folder_path);
1947 	strcat(shell_folder_path, "Vim Online.url");
1948 	if (!WritePrivateProfileString("InternetShortcut", "URL",
1949 				    "https://www.vim.org/", shell_folder_path))
1950 	{
1951 	    printf("Creating the Vim online URL failed\n");
1952 	    return;
1953 	}
1954     }
1955 }
1956 
1957     static void
toggle_startmenu_choice(int idx)1958 toggle_startmenu_choice(int idx)
1959 {
1960     if (choices[idx].installfunc == NULL)
1961     {
1962 	choices[idx].installfunc = install_start_menu;
1963 	choices[idx].text = "Add Vim to the Start menu";
1964     }
1965     else
1966     {
1967 	choices[idx].installfunc = NULL;
1968 	choices[idx].text = "Do NOT add Vim to the Start menu";
1969     }
1970 }
1971 
1972 /*
1973  * Function to actually create the shortcuts
1974  *
1975  * Currently I am supplying no working directory to the shortcut.  This
1976  *    means that the initial working dir will be:
1977  *    - the location of the shortcut if no file is supplied
1978  *    - the location of the file being edited if a file is supplied (ie via
1979  *      drag and drop onto the shortcut).
1980  */
1981     void
install_shortcut_gvim(int idx)1982 install_shortcut_gvim(int idx)
1983 {
1984     // Create shortcut(s) on the desktop
1985     if (choices[idx].arg)
1986     {
1987 	(void)build_shortcut(icon_names[0], "gvim.exe",
1988 						      "", "desktop", WORKDIR);
1989 	need_uninstall_entry = 1;
1990     }
1991 }
1992 
1993     void
install_shortcut_evim(int idx)1994 install_shortcut_evim(int idx)
1995 {
1996     if (choices[idx].arg)
1997     {
1998 	(void)build_shortcut(icon_names[1], "gvim.exe",
1999 						    "-y", "desktop", WORKDIR);
2000 	need_uninstall_entry = 1;
2001     }
2002 }
2003 
2004     void
install_shortcut_gview(int idx)2005 install_shortcut_gview(int idx)
2006 {
2007     if (choices[idx].arg)
2008     {
2009 	(void)build_shortcut(icon_names[2], "gvim.exe",
2010 						    "-R", "desktop", WORKDIR);
2011 	need_uninstall_entry = 1;
2012     }
2013 }
2014 
2015     void
toggle_shortcut_choice(int idx)2016 toggle_shortcut_choice(int idx)
2017 {
2018     char	*arg;
2019 
2020     if (choices[idx].installfunc == install_shortcut_gvim)
2021 	arg = "gVim";
2022     else if (choices[idx].installfunc == install_shortcut_evim)
2023 	arg = "gVim Easy";
2024     else
2025 	arg = "gVim Read-only";
2026     if (choices[idx].arg)
2027     {
2028 	choices[idx].arg = 0;
2029 	alloc_text(idx, "Do NOT create a desktop icon for %s", arg);
2030     }
2031     else
2032     {
2033 	choices[idx].arg = 1;
2034 	alloc_text(idx, "Create a desktop icon for %s", arg);
2035     }
2036 }
2037 
2038     static void
init_startmenu_choice(void)2039 init_startmenu_choice(void)
2040 {
2041     // Start menu
2042     choices[choice_count].changefunc = toggle_startmenu_choice;
2043     choices[choice_count].installfunc = NULL;
2044     choices[choice_count].active = 1;
2045     toggle_startmenu_choice(choice_count);	// set the text
2046     ++choice_count;
2047 }
2048 
2049 /*
2050  * Add the choice for the desktop shortcuts.
2051  */
2052     static void
init_shortcut_choices(void)2053 init_shortcut_choices(void)
2054 {
2055     // Shortcut to gvim
2056     choices[choice_count].text = NULL;
2057     choices[choice_count].arg = 0;
2058     choices[choice_count].active = has_gvim;
2059     choices[choice_count].changefunc = toggle_shortcut_choice;
2060     choices[choice_count].installfunc = install_shortcut_gvim;
2061     toggle_shortcut_choice(choice_count);
2062     ++choice_count;
2063 
2064     // Shortcut to evim
2065     choices[choice_count].text = NULL;
2066     choices[choice_count].arg = 0;
2067     choices[choice_count].active = has_gvim;
2068     choices[choice_count].changefunc = toggle_shortcut_choice;
2069     choices[choice_count].installfunc = install_shortcut_evim;
2070     toggle_shortcut_choice(choice_count);
2071     ++choice_count;
2072 
2073     // Shortcut to gview
2074     choices[choice_count].text = NULL;
2075     choices[choice_count].arg = 0;
2076     choices[choice_count].active = has_gvim;
2077     choices[choice_count].changefunc = toggle_shortcut_choice;
2078     choices[choice_count].installfunc = install_shortcut_gview;
2079     toggle_shortcut_choice(choice_count);
2080     ++choice_count;
2081 }
2082 
2083 /*
2084  * Attempt to register OLE for Vim.
2085  */
2086    static void
install_OLE_register(void)2087 install_OLE_register(void)
2088 {
2089     char register_command_string[BUFSIZE + 30];
2090 
2091     printf("\n--- Attempting to register Vim with OLE ---\n");
2092     printf("(There is no message whether this works or not.)\n");
2093 
2094     sprintf(register_command_string, "\"%s\\gvim.exe\" -silent -register", installdir);
2095     system(register_command_string);
2096 }
2097 
2098 /*
2099  * Remove the last part of directory "path[]" to get its parent, and put the
2100  * result in "to[]".
2101  */
2102     static void
dir_remove_last(const char * path,char to[MAX_PATH])2103 dir_remove_last(const char *path, char to[MAX_PATH])
2104 {
2105     char c;
2106     long last_char_to_copy;
2107     long path_length = strlen(path);
2108 
2109     // skip the last character just in case it is a '\\'
2110     last_char_to_copy = path_length - 2;
2111     c = path[last_char_to_copy];
2112 
2113     while (c != '\\')
2114     {
2115 	last_char_to_copy--;
2116 	c = path[last_char_to_copy];
2117     }
2118 
2119     strncpy(to, path, (size_t)last_char_to_copy);
2120     to[last_char_to_copy] = NUL;
2121 }
2122 
2123     static void
set_directories_text(int idx)2124 set_directories_text(int idx)
2125 {
2126     int vimfiles_dir_choice = choices[idx].arg;
2127 
2128     if (vimfiles_dir_choice == (int)vimfiles_dir_none)
2129 	alloc_text(idx, "Do NOT create plugin directories%s", "");
2130     else
2131 	alloc_text(idx, "Create plugin directories: %s",
2132 				   vimfiles_dir_choices[vimfiles_dir_choice]);
2133 }
2134 
2135 /*
2136  * To get the "real" home directory:
2137  * - get value of $HOME
2138  * - if not found, get value of $HOMEDRIVE$HOMEPATH
2139  * - if not found, get value of $USERPROFILE
2140  *
2141  * This code is based on init_homedir() in misc1.c, keep in sync!
2142  */
2143 static char *homedir = NULL;
2144 
2145     void
init_homedir(void)2146 init_homedir(void)
2147 {
2148     char    *var;
2149     char    buf[MAX_PATH];
2150 
2151     if (homedir != NULL)
2152     {
2153 	free(homedir);
2154 	homedir = NULL;
2155     }
2156 
2157     var = getenv("HOME");
2158 
2159     /*
2160      * Typically, $HOME is not defined on Windows, unless the user has
2161      * specifically defined it for Vim's sake.  However, on Windows NT
2162      * platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
2163      * each user.  Try constructing $HOME from these.
2164      */
2165     if (var == NULL || *var == NUL)
2166     {
2167 	char	*homedrive, *homepath;
2168 
2169 	homedrive = getenv("HOMEDRIVE");
2170 	homepath = getenv("HOMEPATH");
2171 	if (homepath == NULL || *homepath == NUL)
2172 	    homepath = "\\";
2173 	if (homedrive != NULL
2174 		   && strlen(homedrive) + strlen(homepath) < sizeof(buf))
2175 	{
2176 	    sprintf(buf, "%s%s", homedrive, homepath);
2177 	    if (buf[0] != NUL)
2178 		var = buf;
2179 	}
2180     }
2181 
2182     if (var == NULL)
2183 	var = getenv("USERPROFILE");
2184 
2185     /*
2186      * Weird but true: $HOME may contain an indirect reference to another
2187      * variable, esp. "%USERPROFILE%".  Happens when $USERPROFILE isn't set
2188      * when $HOME is being set.
2189      */
2190     if (var != NULL && *var == '%')
2191     {
2192 	char	*p;
2193 	char	*exp;
2194 
2195 	p = strchr(var + 1, '%');
2196 	if (p != NULL)
2197 	{
2198 	    strncpy(buf, var + 1, p - (var + 1));
2199 	    buf[p - (var + 1)] = NUL;
2200 	    exp = getenv(buf);
2201 	    if (exp != NULL && *exp != NUL
2202 				&& strlen(exp) + strlen(p) < sizeof(buf))
2203 	    {
2204 		sprintf(buf, "%s%s", exp, p + 1);
2205 		var = buf;
2206 	    }
2207 	}
2208     }
2209 
2210     if (var != NULL && *var == NUL)	// empty is same as not set
2211 	var = NULL;
2212 
2213     if (var == NULL)
2214 	homedir = NULL;
2215     else
2216 	homedir = _strdup(var);
2217 }
2218 
2219 /*
2220  * Change the directory that the vim plugin directories will be created in:
2221  * $HOME, $VIM or nowhere.
2222  */
2223     static void
change_directories_choice(int idx)2224 change_directories_choice(int idx)
2225 {
2226     int	    choice_count = TABLE_SIZE(vimfiles_dir_choices);
2227 
2228     // Don't offer the $HOME choice if $HOME isn't set.
2229     if (homedir == NULL)
2230 	--choice_count;
2231     choices[idx].arg = get_choice(vimfiles_dir_choices, choice_count);
2232     set_directories_text(idx);
2233 }
2234 
2235 /*
2236  * Create the plugin directories...
2237  */
2238 //ARGSUSED
2239     static void
install_vimfilesdir(int idx)2240 install_vimfilesdir(int idx)
2241 {
2242     int i;
2243     int vimfiles_dir_choice = choices[idx].arg;
2244     char *p;
2245     char vimdir_path[MAX_PATH];
2246     char vimfiles_path[MAX_PATH + 9];
2247     char tmp_dirname[BUFSIZE];
2248 
2249     // switch on the location that the user wants the plugin directories
2250     // built in
2251     switch (vimfiles_dir_choice)
2252     {
2253 	case vimfiles_dir_vim:
2254 	{
2255 	    // Go to the %VIM% directory - check env first, then go one dir
2256 	    //	   below installdir if there is no %VIM% environment variable.
2257 	    //	   The accuracy of $VIM is checked in inspect_system(), so we
2258 	    //	   can be sure it is ok to use here.
2259 	    p = getenv("VIM");
2260 	    if (p == NULL) // No $VIM in path
2261 		dir_remove_last(installdir, vimdir_path);
2262 	    else
2263 		strcpy(vimdir_path, p);
2264 	    break;
2265 	}
2266 	case vimfiles_dir_home:
2267 	{
2268 	    // Find the $HOME directory.  Its existence was already checked.
2269 	    p = homedir;
2270 	    if (p == NULL)
2271 	    {
2272 		printf("Internal error: $HOME is NULL\n");
2273 		p = "c:\\";
2274 	    }
2275 	    strcpy(vimdir_path, p);
2276 	    break;
2277 	}
2278 	case vimfiles_dir_none:
2279 	{
2280 	    // Do not create vim plugin directory.
2281 	    return;
2282 	}
2283     }
2284 
2285     // Now, just create the directory.	If it already exists, it will fail
2286     // silently.
2287     sprintf(vimfiles_path, "%s\\vimfiles", vimdir_path);
2288     vim_mkdir(vimfiles_path, 0755);
2289 
2290     printf("Creating the following directories in \"%s\":\n", vimfiles_path);
2291     for (i = 0; i < TABLE_SIZE(vimfiles_subdirs); i++)
2292     {
2293 	sprintf(tmp_dirname, "%s\\%s", vimfiles_path, vimfiles_subdirs[i]);
2294 	printf("  %s", vimfiles_subdirs[i]);
2295 	vim_mkdir(tmp_dirname, 0755);
2296     }
2297     printf("\n");
2298 }
2299 
2300 /*
2301  * Add the creation of runtime files to the setup sequence.
2302  */
2303     static void
init_directories_choice(void)2304 init_directories_choice(void)
2305 {
2306     struct stat	st;
2307     char	tmp_dirname[BUFSIZE];
2308     char	*p;
2309     int		vimfiles_dir_choice;
2310 
2311     choices[choice_count].text = alloc(150);
2312     choices[choice_count].changefunc = change_directories_choice;
2313     choices[choice_count].installfunc = install_vimfilesdir;
2314     choices[choice_count].active = 1;
2315 
2316     // Check if the "compiler" directory already exists.  That's a good
2317     // indication that the plugin directories were already created.
2318     p = getenv("HOME");
2319     if (p != NULL)
2320     {
2321 	vimfiles_dir_choice = (int)vimfiles_dir_home;
2322 	sprintf(tmp_dirname, "%s\\vimfiles\\compiler", p);
2323 	if (stat(tmp_dirname, &st) == 0)
2324 	    vimfiles_dir_choice = (int)vimfiles_dir_none;
2325     }
2326     else
2327     {
2328 	vimfiles_dir_choice = (int)vimfiles_dir_vim;
2329 	p = getenv("VIM");
2330 	if (p == NULL)  // No $VIM in path, use the install dir.
2331 	    dir_remove_last(installdir, tmp_dirname);
2332 	else
2333 	    strcpy(tmp_dirname, p);
2334 	strcat(tmp_dirname, "\\vimfiles\\compiler");
2335 	if (stat(tmp_dirname, &st) == 0)
2336 	    vimfiles_dir_choice = (int)vimfiles_dir_none;
2337     }
2338 
2339     choices[choice_count].arg = vimfiles_dir_choice;
2340     set_directories_text(choice_count);
2341     ++choice_count;
2342 }
2343 
2344 /*
2345  * Setup the choices and the default values.
2346  */
2347     static void
setup_choices(void)2348 setup_choices(void)
2349 {
2350     // install the batch files
2351     init_bat_choices();
2352 
2353     // (over) write _vimrc file
2354     init_vimrc_choices();
2355 
2356     // Whether to add Vim to the popup menu
2357     init_popup_choice();
2358 
2359     // Whether to add Vim to the "Open With..." menu
2360     init_openwith_choice();
2361 
2362     // Whether to add Vim to the Start Menu.
2363     init_startmenu_choice();
2364 
2365     // Whether to add shortcuts to the Desktop.
2366     init_shortcut_choices();
2367 
2368     // Whether to create the runtime directories.
2369     init_directories_choice();
2370 }
2371 
2372     static void
print_cmd_line_help(void)2373 print_cmd_line_help(void)
2374 {
2375     printf("Vim installer non-interactive command line arguments:\n");
2376     printf("\n");
2377     printf("-create-batfiles  [vim gvim evim view gview vimdiff gvimdiff]\n");
2378     printf("    Create .bat files for Vim variants in the Windows directory.\n");
2379     printf("-create-vimrc\n");
2380     printf("    Create a default _vimrc file if one does not already exist.\n");
2381     printf("-vimrc-remap [no|win]\n");
2382     printf("    Remap keys when creating a default _vimrc file.\n");
2383     printf("-vimrc-behave [unix|mswin|default]\n");
2384     printf("    Set mouse behavior when creating a default _vimrc file.\n");
2385     printf("-vimrc-compat [vi|vim|defaults|all]\n");
2386     printf("    Set Vi compatibility when creating a default _vimrc file.\n");
2387     printf("-install-popup\n");
2388     printf("    Install the Edit-with-Vim context menu entry\n");
2389     printf("-install-openwith\n");
2390     printf("    Add Vim to the \"Open With...\" context menu list\n");
2391     printf("-add-start-menu");
2392     printf("    Add Vim to the start menu\n");
2393     printf("-install-icons");
2394     printf("    Create icons for gVim executables on the desktop\n");
2395     printf("-create-directories [vim|home]\n");
2396     printf("    Create runtime directories to drop plugins into; in the $VIM\n");
2397     printf("    or $HOME directory\n");
2398     printf("-register-OLE");
2399     printf("    Ignored\n");
2400     printf("\n");
2401 }
2402 
2403 /*
2404  * Setup installation choices based on command line switches
2405  */
2406     static void
command_line_setup_choices(int argc,char ** argv)2407 command_line_setup_choices(int argc, char **argv)
2408 {
2409     int i, j;
2410 
2411     for (i = 1; i < argc; i++)
2412     {
2413 	if (strcmp(argv[i], "-create-batfiles") == 0)
2414 	{
2415 	    if (i + 1 == argc)
2416 		continue;
2417 	    while (argv[i + 1][0] != '-' && i < argc)
2418 	    {
2419 		i++;
2420 		for (j = 1; j < TARGET_COUNT; ++j)
2421 		    if ((targets[j].exenamearg[0] == 'g' ? has_gvim : has_vim)
2422 			    && strcmp(argv[i], targets[j].name) == 0)
2423 		    {
2424 			init_bat_choice(j);
2425 			break;
2426 		    }
2427 		if (j == TARGET_COUNT)
2428 		    printf("%s is not a valid choice for -create-batfiles\n",
2429 								     argv[i]);
2430 
2431 		if (i + 1 == argc)
2432 		    break;
2433 	    }
2434 	}
2435 	else if (strcmp(argv[i], "-create-vimrc") == 0)
2436 	{
2437 	    // Setup default vimrc choices.  If there is already a _vimrc file,
2438 	    // it will NOT be overwritten.
2439 	    init_vimrc_choices();
2440 	}
2441 	else if (strcmp(argv[i], "-vimrc-remap") == 0)
2442 	{
2443 	    if (i + 1 == argc)
2444 		break;
2445 	    i++;
2446 	    if (strcmp(argv[i], "no") == 0)
2447 		remap_choice = remap_no;
2448 	    else if (strcmp(argv[i], "win") == 0)
2449 		remap_choice = remap_win;
2450 	}
2451 	else if (strcmp(argv[i], "-vimrc-behave") == 0)
2452 	{
2453 	    if (i + 1 == argc)
2454 		break;
2455 	    i++;
2456 	    if (strcmp(argv[i], "unix") == 0)
2457 		mouse_choice = mouse_xterm;
2458 	    else if (strcmp(argv[i], "mswin") == 0)
2459 		mouse_choice = mouse_mswin;
2460 	    else if (strcmp(argv[i], "default") == 0)
2461 		mouse_choice = mouse_default;
2462 	}
2463 	else if (strcmp(argv[i], "-vimrc-compat") == 0)
2464 	{
2465 	    if (i + 1 == argc)
2466 		break;
2467 	    i++;
2468 	    if (strcmp(argv[i], "vi") == 0)
2469 		compat_choice = compat_vi;
2470 	    else if (strcmp(argv[i], "vim") == 0)
2471 		compat_choice = compat_vim;
2472 	    else if (strcmp(argv[i], "defaults") == 0)
2473 		compat_choice = compat_some_enhancements;
2474 	    else if (strcmp(argv[i], "all") == 0)
2475 		compat_choice = compat_all_enhancements;
2476 	}
2477 	else if (strcmp(argv[i], "-install-popup") == 0)
2478 	{
2479 	    init_popup_choice();
2480 	}
2481 	else if (strcmp(argv[i], "-install-openwith") == 0)
2482 	{
2483 	    init_openwith_choice();
2484 	}
2485 	else if (strcmp(argv[i], "-add-start-menu") == 0)
2486 	{
2487 	    init_startmenu_choice();
2488 	}
2489 	else if (strcmp(argv[i], "-install-icons") == 0)
2490 	{
2491 	    init_shortcut_choices();
2492 	}
2493 	else if (strcmp(argv[i], "-create-directories") == 0)
2494 	{
2495 	    int vimfiles_dir_choice = (int)vimfiles_dir_none;
2496 
2497 	    init_directories_choice();
2498 	    if (argv[i + 1][0] != '-')
2499 	    {
2500 		i++;
2501 		if (strcmp(argv[i], "vim") == 0)
2502 		    vimfiles_dir_choice = (int)vimfiles_dir_vim;
2503 		else if (strcmp(argv[i], "home") == 0)
2504 		{
2505 		    if (homedir == NULL)  // No $HOME in environment
2506 			vimfiles_dir_choice = (int)vimfiles_dir_none;
2507 		    else
2508 			vimfiles_dir_choice = (int)vimfiles_dir_home;
2509 		}
2510 		else
2511 		{
2512 		    printf("Unknown argument for -create-directories: %s\n",
2513 								     argv[i]);
2514 		    print_cmd_line_help();
2515 		}
2516 	    }
2517 	    else // No choice specified, default to vim directory
2518 		vimfiles_dir_choice = (int)vimfiles_dir_vim;
2519 	    choices[choice_count - 1].arg = vimfiles_dir_choice;
2520 	}
2521 	else if (strcmp(argv[i], "-register-OLE") == 0)
2522 	{
2523 	    // This is always done when gvim is found
2524 	}
2525 	else // Unknown switch
2526 	{
2527 	    printf("Got unknown argument argv[%d] = %s\n", i, argv[i]);
2528 	    print_cmd_line_help();
2529 	}
2530     }
2531 }
2532 
2533 
2534 /*
2535  * Show a few screens full of helpful information.
2536  */
2537     static void
show_help(void)2538 show_help(void)
2539 {
2540     static char *(items[]) =
2541     {
2542 "Installing .bat files\n"
2543 "---------------------\n"
2544 "The vim.bat file is written in one of the directories in $PATH.\n"
2545 "This makes it possible to start Vim from the command line.\n"
2546 "If vim.exe can be found in $PATH, the choice for vim.bat will not be\n"
2547 "present.  It is assumed you will use the existing vim.exe.\n"
2548 "If vim.bat can already be found in $PATH this is probably for an old\n"
2549 "version of Vim (but this is not checked!).  You can overwrite it.\n"
2550 "If no vim.bat already exists, you can select one of the directories in\n"
2551 "$PATH for creating the batch file, or disable creating a vim.bat file.\n"
2552 "\n"
2553 "If you choose not to create the vim.bat file, Vim can still be executed\n"
2554 "in other ways, but not from the command line.\n"
2555 "\n"
2556 "The same applies to choices for gvim, evim, (g)view, and (g)vimdiff.\n"
2557 "The first item can be used to change the path for all of them.\n"
2558 ,
2559 "Creating a _vimrc file\n"
2560 "----------------------\n"
2561 "The _vimrc file is used to set options for how Vim behaves.\n"
2562 "The install program can create a _vimrc file with a few basic choices.\n"
2563 "You can edit this file later to tune your preferences.\n"
2564 "If you already have a _vimrc or .vimrc file it can be overwritten.\n"
2565 "Don't do that if you have made changes to it.\n"
2566 ,
2567 "Vim features\n"
2568 "------------\n"
2569 "(this choice is only available when creating a _vimrc file)\n"
2570 "1. Vim can run in Vi-compatible mode.  Many nice Vim features are then\n"
2571 "   disabled.  Only choose Vi-compatible if you really need full Vi\n"
2572 "   compatibility.\n"
2573 "2. Vim runs in not-Vi-compatible mode.  Vim is still mostly Vi compatible,\n"
2574 "   but adds nice features like multi-level undo.\n"
2575 "3. Running Vim with some enhancements is useful when you want some of\n"
2576 "   the nice Vim features, but have a slow computer and want to keep it\n"
2577 "   really fast.\n"
2578 "4. Syntax highlighting shows many files in color.  Not only does this look\n"
2579 "   nice, it also makes it easier to spot errors and you can work faster.\n"
2580 "   The other features include editing compressed files.\n"
2581 ,
2582 "Windows key mapping\n"
2583 "-------------------\n"
2584 "(this choice is only available when creating a _vimrc file)\n"
2585 "Under MS-Windows the CTRL-C key copies text to the clipboard and CTRL-V\n"
2586 "pastes text from the clipboard.  There are a few more keys like these.\n"
2587 "Unfortunately, in Vim these keys normally have another meaning.\n"
2588 "1. Choose to have the keys like they normally are in Vim (useful if you\n"
2589 "   also use Vim on other systems).\n"
2590 "2. Choose to have the keys work like they are used on MS-Windows (useful\n"
2591 "   if you mostly work on MS-Windows).\n"
2592 ,
2593 "Mouse use\n"
2594 "---------\n"
2595 "(this choice is only available when creating a _vimrc file)\n"
2596 "The right mouse button can be used in two ways:\n"
2597 "1. The Unix way is to extend an existing selection.  The popup menu is\n"
2598 "   not available.\n"
2599 "2. The MS-Windows way is to show a popup menu, which allows you to\n"
2600 "   copy/paste text, undo/redo, etc.  Extending the selection can still be\n"
2601 "   done by keeping SHIFT pressed while using the left mouse button\n"
2602 ,
2603 "Edit-with-Vim context menu entry\n"
2604 "--------------------------------\n"
2605 "(this choice is only available when gvim.exe and gvimext.dll are present)\n"
2606 "You can associate different file types with Vim, so that you can (double)\n"
2607 "click on a file to edit it with Vim.  This means you have to individually\n"
2608 "select each file type.\n"
2609 "An alternative is the option offered here: Install an \"Edit with Vim\"\n"
2610 "entry in the popup menu for the right mouse button.  This means you can\n"
2611 "edit any file with Vim.\n"
2612 ,
2613 "\"Open With...\" context menu entry\n"
2614 "--------------------------------\n"
2615 "(this choice is only available when gvim.exe is present)\n"
2616 "This option adds Vim to the \"Open With...\" entry in the popup menu for\n"
2617 "the right mouse button.  This also makes it possible to edit HTML files\n"
2618 "directly from Internet Explorer.\n"
2619 ,
2620 "Add Vim to the Start menu\n"
2621 "-------------------------\n"
2622 "In Windows 95 and later, Vim can be added to the Start menu.  This will\n"
2623 "create a submenu with an entry for vim, gvim, evim, vimdiff, etc..\n"
2624 ,
2625 "Icons on the desktop\n"
2626 "--------------------\n"
2627 "(these choices are only available when installing gvim)\n"
2628 "In Windows 95 and later, shortcuts (icons) can be created on the Desktop.\n"
2629 ,
2630 "Create plugin directories\n"
2631 "-------------------------\n"
2632 "Plugin directories allow extending Vim by dropping a file into a directory.\n"
2633 "This choice allows creating them in $HOME (if you have a home directory) or\n"
2634 "$VIM (used for everybody on the system).\n"
2635 ,
2636 NULL
2637     };
2638     int		i;
2639     int		c;
2640 
2641     rewind(stdin);
2642     printf("\n");
2643     for (i = 0; items[i] != NULL; ++i)
2644     {
2645 	puts(items[i]);
2646 	printf("Hit Enter to continue, b (back) or q (quit help): ");
2647 	c = getchar();
2648 	rewind(stdin);
2649 	if (c == 'b' || c == 'B')
2650 	{
2651 	    if (i == 0)
2652 		--i;
2653 	    else
2654 		i -= 2;
2655 	}
2656 	if (c == 'q' || c == 'Q')
2657 	    break;
2658 	printf("\n");
2659     }
2660 }
2661 
2662 /*
2663  * Install the choices.
2664  */
2665     static void
install(void)2666 install(void)
2667 {
2668     int		i;
2669 
2670     // Install the selected choices.
2671     for (i = 0; i < choice_count; ++i)
2672 	if (choices[i].installfunc != NULL && choices[i].active)
2673 	    (choices[i].installfunc)(i);
2674 
2675     // Add some entries to the registry, if needed.
2676     if (install_popup
2677 	    || install_openwith
2678 	    || (need_uninstall_entry && interactive)
2679 	    || !interactive)
2680 	install_registry();
2681 
2682     // Register gvim with OLE.
2683     if (has_gvim)
2684 	install_OLE_register();
2685 }
2686 
2687 /*
2688  * request_choice
2689  */
2690     static void
request_choice(void)2691 request_choice(void)
2692 {
2693     int		      i;
2694 
2695     printf("\n\nInstall will do for you:\n");
2696     for (i = 0; i < choice_count; ++i)
2697       if (choices[i].active)
2698 	  printf("%2d  %s\n", i + 1, choices[i].text);
2699     printf("To change an item, enter its number\n\n");
2700     printf("Enter item number, h (help), d (do it) or q (quit): ");
2701 }
2702 
2703     int
main(int argc,char ** argv)2704 main(int argc, char **argv)
2705 {
2706     int		i;
2707     char	buf[BUFSIZE];
2708 
2709     /*
2710      * Run interactively if there are no command line arguments.
2711      */
2712     if (argc > 1)
2713 	interactive = 0;
2714     else
2715 	interactive = 1;
2716 
2717     // Initialize this program.
2718     do_inits(argv);
2719     init_homedir();
2720 
2721     if (argc > 1 && strcmp(argv[1], "-uninstall-check") == 0)
2722     {
2723 	// Only check for already installed Vims.  Used by NSIS installer.
2724 	i = uninstall_check(1);
2725 
2726 	// Find the value of $VIM, because NSIS isn't able to do this by
2727 	// itself.
2728 	get_vim_env();
2729 
2730 	// When nothing found exit quietly.  If something found wait for
2731 	// a little while, so that the user can read the messages.
2732 	if (i && _isatty(1))
2733 	    sleep(3);
2734 	exit(0);
2735     }
2736 
2737     printf("This program sets up the installation of Vim "
2738 						   VIM_VERSION_MEDIUM "\n\n");
2739 
2740     // Check if the user unpacked the archives properly.
2741     check_unpack();
2742 
2743     // Check for already installed Vims.
2744     if (interactive)
2745 	uninstall_check(0);
2746 
2747     // Find out information about the system.
2748     inspect_system();
2749 
2750     if (interactive)
2751     {
2752 	// Setup all the choices.
2753 	setup_choices();
2754 
2755 	// Let the user change choices and finally install (or quit).
2756 	for (;;)
2757 	{
2758 	    request_choice();
2759 	    rewind(stdin);
2760 	    if (scanf("%99s", buf) == 1)
2761 	    {
2762 		if (isdigit(buf[0]))
2763 		{
2764 		    // Change a choice.
2765 		    i = atoi(buf);
2766 		    if (i > 0 && i <= choice_count && choices[i - 1].active)
2767 			(choices[i - 1].changefunc)(i - 1);
2768 		    else
2769 			printf("\nIllegal choice\n");
2770 		}
2771 		else if (buf[0] == 'h' || buf[0] == 'H')
2772 		{
2773 		    // Help
2774 		    show_help();
2775 		}
2776 		else if (buf[0] == 'd' || buf[0] == 'D')
2777 		{
2778 		    // Install!
2779 		    install();
2780 		    printf("\nThat finishes the installation.  Happy Vimming!\n");
2781 		    break;
2782 		}
2783 		else if (buf[0] == 'q' || buf[0] == 'Q')
2784 		{
2785 		    // Quit
2786 		    printf("\nExiting without anything done\n");
2787 		    break;
2788 		}
2789 		else
2790 		    printf("\nIllegal choice\n");
2791 	    }
2792 	}
2793 	printf("\n");
2794 	myexit(0);
2795     }
2796     else
2797     {
2798 	/*
2799 	 * Run non-interactive - setup according to the command line switches
2800 	 */
2801 	command_line_setup_choices(argc, argv);
2802 	install();
2803 
2804 	// Avoid that the user has to hit Enter, just wait a little bit to
2805 	// allow reading the messages.
2806 	sleep(2);
2807     }
2808 
2809     return 0;
2810 }
2811