xref: /vim-8.2.3635/src/dosinst.c (revision 1a928c20)
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, "\n");
845 	    fprintf(fd, "setlocal\n");
846 
847 	    /*
848 	     * Don't use double quotes for the "set" argument, also when it
849 	     * contains a space.  The quotes would be included in the value
850 	     * for MSDOS and NT.
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 exist \"%%VIM_EXE_DIR%%\\%s\" goto havevim\n",
864 								     exename);
865 	    fprintf(fd, "echo \"%%VIM_EXE_DIR%%\\%s\" not found\n", exename);
866 	    fprintf(fd, "goto eof\n");
867 	    fprintf(fd, "\n");
868 	    fprintf(fd, ":havevim\n");
869 
870 	    fprintf(fd, "rem collect the arguments in VIMARGS for Win95\n");
871 	    fprintf(fd, "set VIMARGS=\n");
872 	    if (*exename == 'g')
873 		fprintf(fd, "set VIMNOFORK=\n");
874 	    fprintf(fd, ":loopstart\n");
875 	    fprintf(fd, "if .%%1==. goto loopend\n");
876 	    if (*exename == 'g')
877 	    {
878 		fprintf(fd, "if NOT .%%1==.--nofork goto noforklongarg\n");
879 		fprintf(fd, "set VIMNOFORK=1\n");
880 		fprintf(fd, ":noforklongarg\n");
881 		fprintf(fd, "if NOT .%%1==.-f goto noforkarg\n");
882 		fprintf(fd, "set VIMNOFORK=1\n");
883 		fprintf(fd, ":noforkarg\n");
884 	    }
885 	    fprintf(fd, "set VIMARGS=%%VIMARGS%% %%1\n");
886 	    fprintf(fd, "shift\n");
887 	    fprintf(fd, "goto loopstart\n");
888 	    fprintf(fd, ":loopend\n");
889 	    fprintf(fd, "\n");
890 
891 	    fprintf(fd, "if .%%OS%%==.Windows_NT goto ntaction\n");
892 	    fprintf(fd, "\n");
893 
894 	    // For gvim.exe use "start" to avoid that the console window stays
895 	    // open.
896 	    if (*exename == 'g')
897 	    {
898 		fprintf(fd, "if .%%VIMNOFORK%%==.1 goto nofork\n");
899 		fprintf(fd, "start ");
900 	    }
901 
902 	    // Always use quotes, $VIM or $VIMRUNTIME might have a space.
903 	    fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%VIMARGS%%\n",
904 							     exename, vimarg);
905 	    fprintf(fd, "goto eof\n");
906 	    fprintf(fd, "\n");
907 
908 	    if (*exename == 'g')
909 	    {
910 		fprintf(fd, ":nofork\n");
911 		fprintf(fd, "start /w ");
912 		// Always use quotes, $VIM or $VIMRUNTIME might have a space.
913 		fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%VIMARGS%%\n",
914 							     exename, vimarg);
915 		fprintf(fd, "goto eof\n");
916 		fprintf(fd, "\n");
917 	    }
918 
919 	    fprintf(fd, ":ntaction\n");
920 	    fprintf(fd, "rem for WinNT we can use %%*\n");
921 
922 	    // For gvim.exe use "start /b" to avoid that the console window
923 	    // stays open.
924 	    if (*exename == 'g')
925 	    {
926 		fprintf(fd, "if .%%VIMNOFORK%%==.1 goto noforknt\n");
927 		fprintf(fd, "start \"dummy\" /b ");
928 	    }
929 
930 	    // Always use quotes, $VIM or $VIMRUNTIME might have a space.
931 	    fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n", exename, vimarg);
932 	    fprintf(fd, "goto eof\n");
933 	    fprintf(fd, "\n");
934 
935 	    if (*exename == 'g')
936 	    {
937 		fprintf(fd, ":noforknt\n");
938 		fprintf(fd, "start \"dummy\" /b /wait ");
939 		// Always use quotes, $VIM or $VIMRUNTIME might have a space.
940 		fprintf(fd, "\"%%VIM_EXE_DIR%%\\%s\" %s %%*\n",
941 							     exename, vimarg);
942 	    }
943 
944 	    fprintf(fd, "\n:eof\n");
945 	    fprintf(fd, "set VIMARGS=\n");
946 	    if (*exename == 'g')
947 		fprintf(fd, "set VIMNOFORK=\n");
948 
949 	    fclose(fd);
950 	    printf("%s has been %s\n", batpath,
951 				 oldname == NULL ? "created" : "overwritten");
952 	}
953     }
954 }
955 
956 /*
957  * Make the text string for choice "idx".
958  * The format "fmt" is must have one %s item, which "arg" is used for.
959  */
960     static void
961 alloc_text(int idx, char *fmt, char *arg)
962 {
963     if (choices[idx].text != NULL)
964 	free(choices[idx].text);
965 
966     choices[idx].text = alloc(strlen(fmt) + strlen(arg) - 1);
967     sprintf(choices[idx].text, fmt, arg);
968 }
969 
970 /*
971  * Toggle the "Overwrite .../vim.bat" to "Don't overwrite".
972  */
973     static void
974 toggle_bat_choice(int idx)
975 {
976     char	*batname = targets[choices[idx].arg].batpath;
977     char	*oldname = targets[choices[idx].arg].oldbat;
978 
979     if (*batname == NUL)
980     {
981 	alloc_text(idx, "    Overwrite %s", oldname);
982 	strcpy(batname, oldname);
983     }
984     else
985     {
986 	alloc_text(idx, "    Do NOT overwrite %s", oldname);
987 	*batname = NUL;
988     }
989 }
990 
991 /*
992  * Do some work for a batch file entry: Append the batch file name to the path
993  * and set the text for the choice.
994  */
995     static void
996 set_bat_text(int idx, char *batpath, char *name)
997 {
998     strcat(batpath, name);
999 
1000     alloc_text(idx, "    Create %s", batpath);
1001 }
1002 
1003 /*
1004  * Select a directory to write the batch file line.
1005  */
1006     static void
1007 change_bat_choice(int idx)
1008 {
1009     char	*path;
1010     char	*batpath;
1011     char	*name;
1012     int		n;
1013     char	*s;
1014     char	*p;
1015     int		count;
1016     char	**names = NULL;
1017     int		i;
1018     int		target = choices[idx].arg;
1019 
1020     name = targets[target].batname;
1021     batpath = targets[target].batpath;
1022 
1023     path = getenv("PATH");
1024     if (path == NULL)
1025     {
1026 	printf("\nERROR: The variable $PATH is not set\n");
1027 	return;
1028     }
1029 
1030     /*
1031      * first round: count number of names in path;
1032      * second round: save names to names[].
1033      */
1034     for (;;)
1035     {
1036 	count = 1;
1037 	for (p = path; *p; )
1038 	{
1039 	    s = strchr(p, ';');
1040 	    if (s == NULL)
1041 		s = p + strlen(p);
1042 	    if (names != NULL)
1043 	    {
1044 		names[count] = alloc(s - p + 1);
1045 		strncpy(names[count], p, s - p);
1046 		names[count][s - p] = NUL;
1047 	    }
1048 	    ++count;
1049 	    p = s;
1050 	    if (*p != NUL)
1051 		++p;
1052 	}
1053 	if (names != NULL)
1054 	    break;
1055 	names = alloc((count + 1) * sizeof(char *));
1056     }
1057     names[0] = alloc(50);
1058     sprintf(names[0], "Select directory to create %s in:", name);
1059     names[count] = alloc(50);
1060     if (choices[idx].arg == 0)
1061 	sprintf(names[count], "Do not create any .bat file.");
1062     else
1063 	sprintf(names[count], "Do not create a %s file.", name);
1064     n = get_choice(names, count + 1);
1065 
1066     if (n == count)
1067     {
1068 	// Selected last item, don't create bat file.
1069 	*batpath = NUL;
1070 	if (choices[idx].arg != 0)
1071 	    alloc_text(idx, "    Do NOT create %s", name);
1072     }
1073     else
1074     {
1075 	// Selected one of the paths.  For the first item only keep the path,
1076 	// for the others append the batch file name.
1077 	strcpy(batpath, names[n]);
1078 	add_pathsep(batpath);
1079 	if (choices[idx].arg != 0)
1080 	    set_bat_text(idx, batpath, name);
1081     }
1082 
1083     for (i = 0; i <= count; ++i)
1084 	free(names[i]);
1085     free(names);
1086 }
1087 
1088 char *bat_text_yes = "Install .bat files to use Vim at the command line:";
1089 char *bat_text_no = "do NOT install .bat files to use Vim at the command line";
1090 
1091     static void
1092 change_main_bat_choice(int idx)
1093 {
1094     int		i;
1095 
1096     // let the user select a default directory or NONE
1097     change_bat_choice(idx);
1098 
1099     if (targets[0].batpath[0] != NUL)
1100 	choices[idx].text = bat_text_yes;
1101     else
1102 	choices[idx].text = bat_text_no;
1103 
1104     // update the individual batch file selections
1105     for (i = 1; i < TARGET_COUNT; ++i)
1106     {
1107 	// Only make it active when the first item has a path and the vim.exe
1108 	// or gvim.exe exists (there is a changefunc then).
1109 	if (targets[0].batpath[0] != NUL
1110 		&& choices[idx + i].changefunc != NULL)
1111 	{
1112 	    choices[idx + i].active = 1;
1113 	    if (choices[idx + i].changefunc == change_bat_choice
1114 		    && targets[i].batpath[0] != NUL)
1115 	    {
1116 		strcpy(targets[i].batpath, targets[0].batpath);
1117 		set_bat_text(idx + i, targets[i].batpath, targets[i].batname);
1118 	    }
1119 	}
1120 	else
1121 	    choices[idx + i].active = 0;
1122     }
1123 }
1124 
1125 /*
1126  * Initialize a choice for creating a batch file.
1127  */
1128     static void
1129 init_bat_choice(int target)
1130 {
1131     char	*batpath = targets[target].batpath;
1132     char	*oldbat = targets[target].oldbat;
1133     char	*p;
1134     int		i;
1135 
1136     choices[choice_count].arg = target;
1137     choices[choice_count].installfunc = install_bat_choice;
1138     choices[choice_count].active = 1;
1139     choices[choice_count].text = NULL;	// will be set below
1140     if (oldbat != NULL)
1141     {
1142 	// A [g]vim.bat exists: Only choice is to overwrite it or not.
1143 	choices[choice_count].changefunc = toggle_bat_choice;
1144 	*batpath = NUL;
1145 	toggle_bat_choice(choice_count);
1146     }
1147     else
1148     {
1149 	if (default_bat_dir != NULL)
1150 	    // Prefer using the same path as an existing .bat file.
1151 	    strcpy(batpath, default_bat_dir);
1152 	else
1153 	{
1154 	    // No [g]vim.bat exists: Write it to a directory in $PATH.  Use
1155 	    // $WINDIR by default, if it's empty the first item in $PATH.
1156 	    p = getenv("WINDIR");
1157 	    if (p != NULL && *p != NUL)
1158 		strcpy(batpath, p);
1159 	    else
1160 	    {
1161 		p = getenv("PATH");
1162 		if (p == NULL || *p == NUL)		// "cannot happen"
1163 		    strcpy(batpath, "C:/Windows");
1164 		else
1165 		{
1166 		    i = 0;
1167 		    while (*p != NUL && *p != ';')
1168 			batpath[i++] = *p++;
1169 		    batpath[i] = NUL;
1170 		}
1171 	    }
1172 	}
1173 	add_pathsep(batpath);
1174 	set_bat_text(choice_count, batpath, targets[target].batname);
1175 
1176 	choices[choice_count].changefunc = change_bat_choice;
1177     }
1178     ++choice_count;
1179 }
1180 
1181 /*
1182  * Set up the choices for installing .bat files.
1183  * For these items "arg" is the index in targets[].
1184  */
1185     static void
1186 init_bat_choices(void)
1187 {
1188     int		i;
1189 
1190     // The first item is used to switch installing batch files on/off and
1191     // setting the default path.
1192     choices[choice_count].text = bat_text_yes;
1193     choices[choice_count].changefunc = change_main_bat_choice;
1194     choices[choice_count].installfunc = NULL;
1195     choices[choice_count].active = 1;
1196     choices[choice_count].arg = 0;
1197     ++choice_count;
1198 
1199     // Add items for each batch file target.  Only used when not disabled by
1200     // the first item.  When a .exe exists, don't offer to create a .bat.
1201     for (i = 1; i < TARGET_COUNT; ++i)
1202 	if (targets[i].oldexe == NULL
1203 		&& (targets[i].exenamearg[0] == 'g' ? has_gvim : has_vim))
1204 	    init_bat_choice(i);
1205 	else
1206 	    add_dummy_choice();
1207 }
1208 
1209 /*
1210  * Install the vimrc file.
1211  */
1212     static void
1213 install_vimrc(int idx)
1214 {
1215     FILE	*fd, *tfd;
1216     char	*fname;
1217 
1218     // If an old vimrc file exists, overwrite it.
1219     // Otherwise create a new one.
1220     if (*oldvimrc != NUL)
1221 	fname = oldvimrc;
1222     else
1223 	fname = vimrc;
1224 
1225     fd = fopen(fname, "w");
1226     if (fd == NULL)
1227     {
1228 	printf("\nERROR: Cannot open \"%s\" for writing.\n", fname);
1229 	return;
1230     }
1231     switch (compat_choice)
1232     {
1233 	case compat_vi:
1234 		    fprintf(fd, "\" Vi compatible\n");
1235 		    fprintf(fd, "set compatible\n");
1236 		    break;
1237 	case compat_vim:
1238 		    fprintf(fd, "\" Vim's default behavior\n");
1239 		    fprintf(fd, "if &compatible\n");
1240 		    fprintf(fd, "  set nocompatible\n");
1241 		    fprintf(fd, "endif\n");
1242 		    break;
1243 	case compat_some_enhancements:
1244 		    fprintf(fd, "\" Vim with some enhancements\n");
1245 		    fprintf(fd, "source $VIMRUNTIME/defaults.vim\n");
1246 		    break;
1247 	case compat_all_enhancements:
1248 		    fprintf(fd, "\" Vim with all enhancements\n");
1249 		    fprintf(fd, "source $VIMRUNTIME/vimrc_example.vim\n");
1250 		    break;
1251     }
1252     switch (remap_choice)
1253     {
1254 	case remap_no:
1255 		    break;
1256 	case remap_win:
1257 		    fprintf(fd, "\n");
1258 		    fprintf(fd, "\" Remap a few keys for Windows behavior\n");
1259 		    fprintf(fd, "source $VIMRUNTIME/mswin.vim\n");
1260 		    break;
1261     }
1262     switch (mouse_choice)
1263     {
1264 	case mouse_xterm:
1265 		    fprintf(fd, "\n");
1266 		    fprintf(fd, "\" Mouse behavior (the Unix way)\n");
1267 		    fprintf(fd, "behave xterm\n");
1268 		    break;
1269 	case mouse_mswin:
1270 		    fprintf(fd, "\n");
1271 		    fprintf(fd, "\" Mouse behavior (the Windows way)\n");
1272 		    fprintf(fd, "behave mswin\n");
1273 		    break;
1274 	case mouse_default:
1275 		    break;
1276     }
1277     if ((tfd = fopen("diff.exe", "r")) != NULL)
1278     {
1279 	// Use the diff.exe that comes with the self-extracting gvim.exe.
1280 	fclose(tfd);
1281 	fprintf(fd, "\n");
1282 	fprintf(fd, "\" Use the internal diff if available.\n");
1283 	fprintf(fd, "\" Otherwise use the special 'diffexpr' for Windows.\n");
1284 	fprintf(fd, "if &diffopt !~# 'internal'\n");
1285 	fprintf(fd, "  set diffexpr=MyDiff()\n");
1286 	fprintf(fd, "endif\n");
1287 	fprintf(fd, "function MyDiff()\n");
1288 	fprintf(fd, "  let opt = '-a --binary '\n");
1289 	fprintf(fd, "  if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif\n");
1290 	fprintf(fd, "  if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif\n");
1291 	// Use quotes only when needed, they may cause trouble.
1292 	// Always escape "!".
1293 	fprintf(fd, "  let arg1 = v:fname_in\n");
1294 	fprintf(fd, "  if arg1 =~ ' ' | let arg1 = '\"' . arg1 . '\"' | endif\n");
1295 	fprintf(fd, "  let arg1 = substitute(arg1, '!', '\\!', 'g')\n");
1296 	fprintf(fd, "  let arg2 = v:fname_new\n");
1297 	fprintf(fd, "  if arg2 =~ ' ' | let arg2 = '\"' . arg2 . '\"' | endif\n");
1298 	fprintf(fd, "  let arg2 = substitute(arg2, '!', '\\!', 'g')\n");
1299 	fprintf(fd, "  let arg3 = v:fname_out\n");
1300 	fprintf(fd, "  if arg3 =~ ' ' | let arg3 = '\"' . arg3 . '\"' | endif\n");
1301 	fprintf(fd, "  let arg3 = substitute(arg3, '!', '\\!', 'g')\n");
1302 
1303 	// If the path has a space:  When using cmd.exe (Win NT/2000/XP) put
1304 	// quotes around the diff command and rely on the default value of
1305 	// shellxquote to solve the quoting problem for the whole command.
1306 	//
1307 	// Otherwise put a double quote just before the space and at the
1308 	// end of the command.  Putting quotes around the whole thing
1309 	// doesn't work on Win 95/98/ME.  This is mostly guessed!
1310 	fprintf(fd, "  if $VIMRUNTIME =~ ' '\n");
1311 	fprintf(fd, "    if &sh =~ '\\<cmd'\n");
1312 	fprintf(fd, "      if empty(&shellxquote)\n");
1313 	fprintf(fd, "        let l:shxq_sav = ''\n");
1314 	fprintf(fd, "        set shellxquote&\n");
1315 	fprintf(fd, "      endif\n");
1316 	fprintf(fd, "      let cmd = '\"' . $VIMRUNTIME . '\\diff\"'\n");
1317 	fprintf(fd, "    else\n");
1318 	fprintf(fd, "      let cmd = substitute($VIMRUNTIME, ' ', '\" ', '') . '\\diff\"'\n");
1319 	fprintf(fd, "    endif\n");
1320 	fprintf(fd, "  else\n");
1321 	fprintf(fd, "    let cmd = $VIMRUNTIME . '\\diff'\n");
1322 	fprintf(fd, "  endif\n");
1323 	fprintf(fd, "  let cmd = substitute(cmd, '!', '\\!', 'g')\n");
1324 	fprintf(fd, "  silent execute '!' . cmd . ' ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3\n");
1325 	fprintf(fd, "  if exists('l:shxq_sav')\n");
1326 	fprintf(fd, "    let &shellxquote=l:shxq_sav\n");
1327 	fprintf(fd, "  endif\n");
1328 	fprintf(fd, "endfunction\n");
1329 	fprintf(fd, "\n");
1330     }
1331     fclose(fd);
1332     printf("%s has been written\n", fname);
1333 }
1334 
1335     static void
1336 change_vimrc_choice(int idx)
1337 {
1338     if (choices[idx].installfunc != NULL)
1339     {
1340 	// Switch to NOT change or create a vimrc file.
1341 	if (*oldvimrc != NUL)
1342 	    alloc_text(idx, "Do NOT change startup file %s", oldvimrc);
1343 	else
1344 	    alloc_text(idx, "Do NOT create startup file %s", vimrc);
1345 	choices[idx].installfunc = NULL;
1346 	choices[idx + 1].active = 0;
1347 	choices[idx + 2].active = 0;
1348 	choices[idx + 3].active = 0;
1349     }
1350     else
1351     {
1352 	// Switch to change or create a vimrc file.
1353 	if (*oldvimrc != NUL)
1354 	    alloc_text(idx, "Overwrite startup file %s with:", oldvimrc);
1355 	else
1356 	    alloc_text(idx, "Create startup file %s with:", vimrc);
1357 	choices[idx].installfunc = install_vimrc;
1358 	choices[idx + 1].active = 1;
1359 	choices[idx + 2].active = 1;
1360 	choices[idx + 3].active = 1;
1361     }
1362 }
1363 
1364 /*
1365  * Change the choice how to run Vim.
1366  */
1367     static void
1368 change_run_choice(int idx)
1369 {
1370     compat_choice = get_choice(compat_choices, TABLE_SIZE(compat_choices));
1371     alloc_text(idx, compat_text, compat_choices[compat_choice]);
1372 }
1373 
1374 /*
1375  * Change the choice if keys are to be remapped.
1376  */
1377     static void
1378 change_remap_choice(int idx)
1379 {
1380     remap_choice = get_choice(remap_choices, TABLE_SIZE(remap_choices));
1381     alloc_text(idx, remap_text, remap_choices[remap_choice]);
1382 }
1383 
1384 /*
1385  * Change the choice how to select text.
1386  */
1387     static void
1388 change_mouse_choice(int idx)
1389 {
1390     mouse_choice = get_choice(mouse_choices, TABLE_SIZE(mouse_choices));
1391     alloc_text(idx, mouse_text, mouse_choices[mouse_choice]);
1392 }
1393 
1394     static void
1395 init_vimrc_choices(void)
1396 {
1397     // set path for a new _vimrc file (also when not used)
1398     strcpy(vimrc, installdir);
1399     strcpy(vimrc + runtimeidx, "_vimrc");
1400 
1401     // Set opposite value and then toggle it by calling change_vimrc_choice()
1402     if (*oldvimrc == NUL)
1403 	choices[choice_count].installfunc = NULL;
1404     else
1405 	choices[choice_count].installfunc = install_vimrc;
1406     choices[choice_count].text = NULL;
1407     change_vimrc_choice(choice_count);
1408     choices[choice_count].changefunc = change_vimrc_choice;
1409     choices[choice_count].active = 1;
1410     ++choice_count;
1411 
1412     // default way to run Vim
1413     alloc_text(choice_count, compat_text, compat_choices[compat_choice]);
1414     choices[choice_count].changefunc = change_run_choice;
1415     choices[choice_count].installfunc = NULL;
1416     choices[choice_count].active = (*oldvimrc == NUL);
1417     ++choice_count;
1418 
1419     // Whether to remap keys
1420     alloc_text(choice_count, remap_text , remap_choices[remap_choice]);
1421     choices[choice_count].changefunc = change_remap_choice;
1422     choices[choice_count].installfunc = NULL;
1423     choices[choice_count].active = (*oldvimrc == NUL);
1424     ++choice_count;
1425 
1426     // default way to use the mouse
1427     alloc_text(choice_count, mouse_text, mouse_choices[mouse_choice]);
1428     choices[choice_count].changefunc = change_mouse_choice;
1429     choices[choice_count].installfunc = NULL;
1430     choices[choice_count].active = (*oldvimrc == NUL);
1431     ++choice_count;
1432 }
1433 
1434     static LONG
1435 reg_create_key(
1436     HKEY root,
1437     const char *subkey,
1438     PHKEY phKey,
1439     DWORD flag)
1440 {
1441     DWORD disp;
1442 
1443     *phKey = NULL;
1444     return RegCreateKeyEx(
1445 		root, subkey,
1446 		0, NULL, REG_OPTION_NON_VOLATILE,
1447 		flag | KEY_WRITE,
1448 		NULL, phKey, &disp);
1449 }
1450 
1451     static LONG
1452 reg_set_string_value(
1453     HKEY hKey,
1454     const char *value_name,
1455     const char *data)
1456 {
1457     return RegSetValueEx(hKey, value_name, 0, REG_SZ,
1458 				     (LPBYTE)data, (DWORD)(1 + strlen(data)));
1459 }
1460 
1461     static LONG
1462 reg_create_key_and_value(
1463     HKEY hRootKey,
1464     const char *subkey,
1465     const char *value_name,
1466     const char *data,
1467     DWORD flag)
1468 {
1469     HKEY hKey;
1470     LONG lRet = reg_create_key(hRootKey, subkey, &hKey, flag);
1471 
1472     if (ERROR_SUCCESS == lRet)
1473     {
1474 	lRet = reg_set_string_value(hKey, value_name, data);
1475 	RegCloseKey(hKey);
1476     }
1477     return lRet;
1478 }
1479 
1480     static LONG
1481 register_inproc_server(
1482     HKEY hRootKey,
1483     const char *clsid,
1484     const char *extname,
1485     const char *module,
1486     const char *threading_model,
1487     DWORD flag)
1488 {
1489     CHAR subkey[BUFSIZE];
1490     LONG lRet;
1491 
1492     sprintf(subkey, "CLSID\\%s", clsid);
1493     lRet = reg_create_key_and_value(hRootKey, subkey, NULL, extname, flag);
1494     if (ERROR_SUCCESS == lRet)
1495     {
1496 	sprintf(subkey, "CLSID\\%s\\InProcServer32", clsid);
1497 	lRet = reg_create_key_and_value(hRootKey, subkey, NULL, module, flag);
1498 	if (ERROR_SUCCESS == lRet)
1499 	{
1500 	    lRet = reg_create_key_and_value(hRootKey, subkey,
1501 					   "ThreadingModel", threading_model, flag);
1502 	}
1503     }
1504     return lRet;
1505 }
1506 
1507     static LONG
1508 register_shellex(
1509     HKEY hRootKey,
1510     const char *clsid,
1511     const char *name,
1512     const char *exe_path,
1513     DWORD flag)
1514 {
1515     LONG lRet = reg_create_key_and_value(
1516 	    hRootKey,
1517 	    "*\\shellex\\ContextMenuHandlers\\gvim",
1518 	    NULL,
1519 	    clsid,
1520 	    flag);
1521 
1522     if (ERROR_SUCCESS == lRet)
1523     {
1524 	lRet = reg_create_key_and_value(
1525 		HKEY_LOCAL_MACHINE,
1526 		"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
1527 		clsid,
1528 		name,
1529 		flag);
1530 
1531 	if (ERROR_SUCCESS == lRet)
1532 	{
1533 	    lRet = reg_create_key_and_value(
1534 		    HKEY_LOCAL_MACHINE,
1535 		    "Software\\Vim\\Gvim",
1536 		    "path",
1537 		    exe_path,
1538 		    flag);
1539 	}
1540     }
1541     return lRet;
1542 }
1543 
1544     static LONG
1545 register_openwith(
1546     HKEY hRootKey,
1547     const char *exe_path,
1548     DWORD flag)
1549 {
1550     char	exe_cmd[BUFSIZE];
1551     LONG	lRet;
1552 
1553     sprintf(exe_cmd, "\"%s\" \"%%1\"", exe_path);
1554     lRet = reg_create_key_and_value(
1555 	    hRootKey,
1556 	    "Applications\\gvim.exe\\shell\\edit\\command",
1557 	    NULL,
1558 	    exe_cmd,
1559 	    flag);
1560 
1561     if (ERROR_SUCCESS == lRet)
1562     {
1563 	int i;
1564 	static const char *openwith[] = {
1565 		".htm\\OpenWithList\\gvim.exe",
1566 		".vim\\OpenWithList\\gvim.exe",
1567 		"*\\OpenWithList\\gvim.exe",
1568 	};
1569 
1570 	for (i = 0; ERROR_SUCCESS == lRet
1571 			   && i < sizeof(openwith) / sizeof(openwith[0]); i++)
1572 	    lRet = reg_create_key_and_value(hRootKey, openwith[i], NULL, "", flag);
1573     }
1574 
1575     return lRet;
1576 }
1577 
1578     static LONG
1579 register_uninstall(
1580     HKEY hRootKey,
1581     const char *appname,
1582     const char *display_name,
1583     const char *uninstall_string,
1584     const char *display_icon,
1585     const char *display_version,
1586     const char *publisher)
1587 {
1588     LONG lRet = reg_create_key_and_value(hRootKey, appname,
1589 			     "DisplayName", display_name, KEY_WOW64_64KEY);
1590 
1591     if (ERROR_SUCCESS == lRet)
1592 	lRet = reg_create_key_and_value(hRootKey, appname,
1593 		     "UninstallString", uninstall_string, KEY_WOW64_64KEY);
1594     if (ERROR_SUCCESS == lRet)
1595 	lRet = reg_create_key_and_value(hRootKey, appname,
1596 		     "DisplayIcon", display_icon, KEY_WOW64_64KEY);
1597     if (ERROR_SUCCESS == lRet)
1598 	lRet = reg_create_key_and_value(hRootKey, appname,
1599 		     "DisplayVersion", display_version, KEY_WOW64_64KEY);
1600     if (ERROR_SUCCESS == lRet)
1601 	lRet = reg_create_key_and_value(hRootKey, appname,
1602 		     "Publisher", publisher, KEY_WOW64_64KEY);
1603     return lRet;
1604 }
1605 
1606 /*
1607  * Add some entries to the registry:
1608  * - to add "Edit with Vim" to the context * menu
1609  * - to add Vim to the "Open with..." list
1610  * - to uninstall Vim
1611  */
1612 //ARGSUSED
1613     static int
1614 install_registry(void)
1615 {
1616     LONG	lRet = ERROR_SUCCESS;
1617     const char	*vim_ext_ThreadingModel = "Apartment";
1618     const char	*vim_ext_name = "Vim Shell Extension";
1619     const char	*vim_ext_clsid = "{51EEE242-AD87-11d3-9C1E-0090278BBD99}";
1620     char	vim_exe_path[MAX_PATH];
1621     char	display_name[BUFSIZE];
1622     char	uninstall_string[BUFSIZE];
1623     char	icon_string[BUFSIZE];
1624     int		i;
1625     int		loop_count = is_64bit_os() ? 2 : 1;
1626     DWORD	flag;
1627 
1628     sprintf(vim_exe_path, "%s\\gvim.exe", installdir);
1629 
1630     if (install_popup)
1631     {
1632 	char	    bufg[BUFSIZE];
1633 
1634 	printf("Creating \"Edit with Vim\" popup menu entry\n");
1635 
1636 	for (i = 0; i < loop_count; i++)
1637 	{
1638 	    if (i == 0)
1639 	    {
1640 		sprintf(bufg, "%s\\" GVIMEXT32_PATH, installdir);
1641 		flag = KEY_WOW64_32KEY;
1642 	    }
1643 	    else
1644 	    {
1645 		sprintf(bufg, "%s\\" GVIMEXT64_PATH, installdir);
1646 		flag = KEY_WOW64_64KEY;
1647 	    }
1648 
1649 	    lRet = register_inproc_server(
1650 		    HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name,
1651 		    bufg, vim_ext_ThreadingModel, flag);
1652 	    if (ERROR_SUCCESS != lRet)
1653 		return FAIL;
1654 	    lRet = register_shellex(
1655 		    HKEY_CLASSES_ROOT, vim_ext_clsid, vim_ext_name,
1656 		    vim_exe_path, flag);
1657 	    if (ERROR_SUCCESS != lRet)
1658 		return FAIL;
1659 	}
1660     }
1661 
1662     if (install_openwith)
1663     {
1664 	printf("Creating \"Open with ...\" list entry\n");
1665 
1666 	for (i = 0; i < loop_count; i++)
1667 	{
1668 	    if (i == 0)
1669 		flag = KEY_WOW64_32KEY;
1670 	    else
1671 		flag = KEY_WOW64_64KEY;
1672 
1673 	    lRet = register_openwith(HKEY_CLASSES_ROOT, vim_exe_path, flag);
1674 	    if (ERROR_SUCCESS != lRet)
1675 		return FAIL;
1676 	}
1677     }
1678 
1679     printf("Creating an uninstall entry\n");
1680     sprintf(display_name, "Vim " VIM_VERSION_SHORT
1681 #ifdef _M_ARM64
1682 	    " (arm64)"
1683 #elif _M_X64
1684 	    " (x64)"
1685 #endif
1686 	    );
1687 
1688     // For the NSIS installer use the generated uninstaller.
1689     if (interactive)
1690 	sprintf(uninstall_string, "%s\\uninstall.exe", installdir);
1691     else
1692 	sprintf(uninstall_string, "%s\\uninstall-gui.exe", installdir);
1693 
1694     sprintf(icon_string, "%s\\gvim.exe,0", installdir);
1695 
1696     lRet = register_uninstall(
1697 	HKEY_LOCAL_MACHINE,
1698 	"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vim " VIM_VERSION_SHORT,
1699 	display_name,
1700 	uninstall_string,
1701 	icon_string,
1702 	VIM_VERSION_SHORT,
1703 	"Bram Moolenaar et al.");
1704     if (ERROR_SUCCESS != lRet)
1705 	return FAIL;
1706 
1707     return OK;
1708 }
1709 
1710     static void
1711 change_popup_choice(int idx)
1712 {
1713     if (install_popup == 0)
1714     {
1715 	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";
1716 	install_popup = 1;
1717     }
1718     else
1719     {
1720 	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";
1721 	install_popup = 0;
1722     }
1723 }
1724 
1725 /*
1726  * Only add the choice for the popup menu entry when gvim.exe was found and
1727  * both gvimext.dll and regedit.exe exist.
1728  */
1729     static void
1730 init_popup_choice(void)
1731 {
1732     struct stat	st;
1733 
1734     if (has_gvim
1735 	    && (stat(GVIMEXT32_PATH, &st) >= 0
1736 		|| stat(GVIMEXT64_PATH, &st) >= 0))
1737     {
1738 	choices[choice_count].changefunc = change_popup_choice;
1739 	choices[choice_count].installfunc = NULL;
1740 	choices[choice_count].active = 1;
1741 	change_popup_choice(choice_count);  // set the text
1742 	++choice_count;
1743     }
1744     else
1745 	add_dummy_choice();
1746 }
1747 
1748     static void
1749 change_openwith_choice(int idx)
1750 {
1751     if (install_openwith == 0)
1752     {
1753 	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";
1754 	install_openwith = 1;
1755     }
1756     else
1757     {
1758 	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";
1759 	install_openwith = 0;
1760     }
1761 }
1762 
1763 /*
1764  * Only add the choice for the open-with menu entry when gvim.exe was found
1765  * and regedit.exe exist.
1766  */
1767     static void
1768 init_openwith_choice(void)
1769 {
1770     if (has_gvim)
1771     {
1772 	choices[choice_count].changefunc = change_openwith_choice;
1773 	choices[choice_count].installfunc = NULL;
1774 	choices[choice_count].active = 1;
1775 	change_openwith_choice(choice_count);  // set the text
1776 	++choice_count;
1777     }
1778     else
1779 	add_dummy_choice();
1780 }
1781 
1782 /*
1783  * Create a shell link.
1784  *
1785  * returns 0 on failure, non-zero on successful completion.
1786  *
1787  * NOTE:  Currently untested with mingw.
1788  */
1789     int
1790 create_shortcut(
1791 	const char *shortcut_name,
1792 	const char *iconfile_path,
1793 	int	    iconindex,
1794 	const char *shortcut_target,
1795 	const char *shortcut_args,
1796 	const char *workingdir
1797 	)
1798 {
1799     IShellLink	    *shelllink_ptr;
1800     HRESULT	    hres;
1801     IPersistFile	    *persistfile_ptr;
1802 
1803     // Initialize COM library
1804     hres = CoInitialize(NULL);
1805     if (!SUCCEEDED(hres))
1806     {
1807 	printf("Error:  Could not open the COM library.  Not creating shortcut.\n");
1808 	return FAIL;
1809     }
1810 
1811     // Instantiate a COM object for the ShellLink, store a pointer to it
1812     // in shelllink_ptr.
1813     hres = CoCreateInstance(&CLSID_ShellLink,
1814 			   NULL,
1815 			   CLSCTX_INPROC_SERVER,
1816 			   &IID_IShellLink,
1817 			   (void **) &shelllink_ptr);
1818 
1819     if (SUCCEEDED(hres)) // If the instantiation was successful...
1820     {
1821 	// ...Then build a PersistFile interface for the ShellLink so we can
1822 	// save it as a file after we build it.
1823 	hres = shelllink_ptr->lpVtbl->QueryInterface(shelllink_ptr,
1824 		&IID_IPersistFile, (void **) &persistfile_ptr);
1825 
1826 	if (SUCCEEDED(hres))
1827 	{
1828 	    wchar_t wsz[BUFSIZE];
1829 
1830 	    // translate the (possibly) multibyte shortcut filename to windows
1831 	    // Unicode so it can be used as a file name.
1832 	    MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, wsz, sizeof(wsz)/sizeof(wsz[0]));
1833 
1834 	    // set the attributes
1835 	    shelllink_ptr->lpVtbl->SetPath(shelllink_ptr, shortcut_target);
1836 	    shelllink_ptr->lpVtbl->SetWorkingDirectory(shelllink_ptr,
1837 								  workingdir);
1838 	    shelllink_ptr->lpVtbl->SetIconLocation(shelllink_ptr,
1839 						    iconfile_path, iconindex);
1840 	    shelllink_ptr->lpVtbl->SetArguments(shelllink_ptr, shortcut_args);
1841 
1842 	    // save the shortcut to a file and return the PersistFile object
1843 	    persistfile_ptr->lpVtbl->Save(persistfile_ptr, wsz, 1);
1844 	    persistfile_ptr->lpVtbl->Release(persistfile_ptr);
1845 	}
1846 	else
1847 	{
1848 	    printf("QueryInterface Error\n");
1849 	    return FAIL;
1850 	}
1851 
1852 	// Return the ShellLink object
1853 	shelllink_ptr->lpVtbl->Release(shelllink_ptr);
1854     }
1855     else
1856     {
1857 	printf("CoCreateInstance Error - hres = %08x\n", (int)hres);
1858 	return FAIL;
1859     }
1860 
1861     return OK;
1862 }
1863 
1864 /*
1865  * Build a path to where we will put a specified link.
1866  *
1867  * Return 0 on error, non-zero on success
1868  */
1869    int
1870 build_link_name(
1871 	char	   *link_path,
1872 	const char *link_name,
1873 	const char *shell_folder_name)
1874 {
1875     char	shell_folder_path[MAX_PATH];
1876 
1877     if (get_shell_folder_path(shell_folder_path, shell_folder_name) == FAIL)
1878     {
1879 	printf("An error occurred while attempting to find the path to %s.\n",
1880 							   shell_folder_name);
1881 	return FAIL;
1882     }
1883 
1884     // Make sure the directory exists (create Start Menu\Programs\Vim).
1885     // Ignore errors if it already exists.
1886     vim_mkdir(shell_folder_path, 0755);
1887 
1888     // build the path to the shortcut and the path to gvim.exe
1889     sprintf(link_path, "%s\\%s.lnk", shell_folder_path, link_name);
1890 
1891     return OK;
1892 }
1893 
1894     static int
1895 build_shortcut(
1896 	const char *name,	// Name of the shortcut
1897 	const char *exename,	// Name of the executable (e.g., vim.exe)
1898 	const char *args,
1899 	const char *shell_folder,
1900 	const char *workingdir)
1901 {
1902     char	executable_path[BUFSIZE];
1903     char	link_name[BUFSIZE];
1904 
1905     sprintf(executable_path, "%s\\%s", installdir, exename);
1906 
1907     if (build_link_name(link_name, name, shell_folder) == FAIL)
1908     {
1909 	printf("An error has occurred.  A shortcut to %s will not be created %s.\n",
1910 		name,
1911 		*shell_folder == 'd' ? "on the desktop" : "in the Start menu");
1912 	return FAIL;
1913     }
1914 
1915     // Create the shortcut:
1916     return create_shortcut(link_name, executable_path, 0,
1917 					   executable_path, args, workingdir);
1918 }
1919 
1920 /*
1921  * We used to use "homedir" as the working directory, but that is a bad choice
1922  * on multi-user systems.  However, not specifying a directory results in the
1923  * current directory to be c:\Windows\system32 on Windows 7. Use environment
1924  * variables instead.
1925  */
1926 #define WORKDIR "%HOMEDRIVE%%HOMEPATH%"
1927 
1928 /*
1929  * Create shortcut(s) in the Start Menu\Programs\Vim folder.
1930  */
1931     static void
1932 install_start_menu(int idx)
1933 {
1934     need_uninstall_entry = 1;
1935     printf("Creating start menu\n");
1936     if (has_vim)
1937     {
1938 	if (build_shortcut("Vim", "vim.exe", "",
1939 					      VIM_STARTMENU, WORKDIR) == FAIL)
1940 	    return;
1941 	if (build_shortcut("Vim Read-only", "vim.exe", "-R",
1942 					      VIM_STARTMENU, WORKDIR) == FAIL)
1943 	    return;
1944 	if (build_shortcut("Vim Diff", "vim.exe", "-d",
1945 					      VIM_STARTMENU, WORKDIR) == FAIL)
1946 	    return;
1947     }
1948     if (has_gvim)
1949     {
1950 	if (build_shortcut("gVim", "gvim.exe", "",
1951 					      VIM_STARTMENU, WORKDIR) == FAIL)
1952 	    return;
1953 	if (build_shortcut("gVim Easy", "gvim.exe", "-y",
1954 					      VIM_STARTMENU, WORKDIR) == FAIL)
1955 	    return;
1956 	if (build_shortcut("gVim Read-only", "gvim.exe", "-R",
1957 					      VIM_STARTMENU, WORKDIR) == FAIL)
1958 	    return;
1959 	if (build_shortcut("gVim Diff", "gvim.exe", "-d",
1960 					      VIM_STARTMENU, WORKDIR) == FAIL)
1961 	    return;
1962     }
1963     if (build_shortcut("Uninstall",
1964 		interactive ? "uninstall.exe" : "uninstall-gui.exe", "",
1965 					   VIM_STARTMENU, installdir) == FAIL)
1966 	return;
1967     // For Windows NT the working dir of the vimtutor.bat must be right,
1968     // otherwise gvim.exe won't be found and using gvimbat doesn't work.
1969     if (build_shortcut("Vim tutor", "vimtutor.bat", "",
1970 					   VIM_STARTMENU, installdir) == FAIL)
1971 	return;
1972     if (build_shortcut("Help", has_gvim ? "gvim.exe" : "vim.exe", "-c h",
1973 					      VIM_STARTMENU, WORKDIR) == FAIL)
1974 	return;
1975     {
1976 	char	shell_folder_path[BUFSIZE];
1977 
1978 	// Creating the URL shortcut works a bit differently...
1979 	if (get_shell_folder_path(shell_folder_path, VIM_STARTMENU) == FAIL)
1980 	{
1981 	    printf("Finding the path of the Start menu failed\n");
1982 	    return ;
1983 	}
1984 	add_pathsep(shell_folder_path);
1985 	strcat(shell_folder_path, "Vim Online.url");
1986 	if (!WritePrivateProfileString("InternetShortcut", "URL",
1987 				    "https://www.vim.org/", shell_folder_path))
1988 	{
1989 	    printf("Creating the Vim online URL failed\n");
1990 	    return;
1991 	}
1992     }
1993 }
1994 
1995     static void
1996 toggle_startmenu_choice(int idx)
1997 {
1998     if (choices[idx].installfunc == NULL)
1999     {
2000 	choices[idx].installfunc = install_start_menu;
2001 	choices[idx].text = "Add Vim to the Start menu";
2002     }
2003     else
2004     {
2005 	choices[idx].installfunc = NULL;
2006 	choices[idx].text = "Do NOT add Vim to the Start menu";
2007     }
2008 }
2009 
2010 /*
2011  * Function to actually create the shortcuts
2012  *
2013  * Currently I am supplying no working directory to the shortcut.  This
2014  *    means that the initial working dir will be:
2015  *    - the location of the shortcut if no file is supplied
2016  *    - the location of the file being edited if a file is supplied (ie via
2017  *      drag and drop onto the shortcut).
2018  */
2019     void
2020 install_shortcut_gvim(int idx)
2021 {
2022     // Create shortcut(s) on the desktop
2023     if (choices[idx].arg)
2024     {
2025 	(void)build_shortcut(icon_names[0], "gvim.exe",
2026 						      "", "desktop", WORKDIR);
2027 	need_uninstall_entry = 1;
2028     }
2029 }
2030 
2031     void
2032 install_shortcut_evim(int idx)
2033 {
2034     if (choices[idx].arg)
2035     {
2036 	(void)build_shortcut(icon_names[1], "gvim.exe",
2037 						    "-y", "desktop", WORKDIR);
2038 	need_uninstall_entry = 1;
2039     }
2040 }
2041 
2042     void
2043 install_shortcut_gview(int idx)
2044 {
2045     if (choices[idx].arg)
2046     {
2047 	(void)build_shortcut(icon_names[2], "gvim.exe",
2048 						    "-R", "desktop", WORKDIR);
2049 	need_uninstall_entry = 1;
2050     }
2051 }
2052 
2053     void
2054 toggle_shortcut_choice(int idx)
2055 {
2056     char	*arg;
2057 
2058     if (choices[idx].installfunc == install_shortcut_gvim)
2059 	arg = "gVim";
2060     else if (choices[idx].installfunc == install_shortcut_evim)
2061 	arg = "gVim Easy";
2062     else
2063 	arg = "gVim Read-only";
2064     if (choices[idx].arg)
2065     {
2066 	choices[idx].arg = 0;
2067 	alloc_text(idx, "Do NOT create a desktop icon for %s", arg);
2068     }
2069     else
2070     {
2071 	choices[idx].arg = 1;
2072 	alloc_text(idx, "Create a desktop icon for %s", arg);
2073     }
2074 }
2075 
2076     static void
2077 init_startmenu_choice(void)
2078 {
2079     // Start menu
2080     choices[choice_count].changefunc = toggle_startmenu_choice;
2081     choices[choice_count].installfunc = NULL;
2082     choices[choice_count].active = 1;
2083     toggle_startmenu_choice(choice_count);	// set the text
2084     ++choice_count;
2085 }
2086 
2087 /*
2088  * Add the choice for the desktop shortcuts.
2089  */
2090     static void
2091 init_shortcut_choices(void)
2092 {
2093     // Shortcut to gvim
2094     choices[choice_count].text = NULL;
2095     choices[choice_count].arg = 0;
2096     choices[choice_count].active = has_gvim;
2097     choices[choice_count].changefunc = toggle_shortcut_choice;
2098     choices[choice_count].installfunc = install_shortcut_gvim;
2099     toggle_shortcut_choice(choice_count);
2100     ++choice_count;
2101 
2102     // Shortcut to evim
2103     choices[choice_count].text = NULL;
2104     choices[choice_count].arg = 0;
2105     choices[choice_count].active = has_gvim;
2106     choices[choice_count].changefunc = toggle_shortcut_choice;
2107     choices[choice_count].installfunc = install_shortcut_evim;
2108     toggle_shortcut_choice(choice_count);
2109     ++choice_count;
2110 
2111     // Shortcut to gview
2112     choices[choice_count].text = NULL;
2113     choices[choice_count].arg = 0;
2114     choices[choice_count].active = has_gvim;
2115     choices[choice_count].changefunc = toggle_shortcut_choice;
2116     choices[choice_count].installfunc = install_shortcut_gview;
2117     toggle_shortcut_choice(choice_count);
2118     ++choice_count;
2119 }
2120 
2121 /*
2122  * Attempt to register OLE for Vim.
2123  */
2124    static void
2125 install_OLE_register(void)
2126 {
2127     char register_command_string[BUFSIZE + 30];
2128 
2129     printf("\n--- Attempting to register Vim with OLE ---\n");
2130     printf("(There is no message whether this works or not.)\n");
2131 
2132     sprintf(register_command_string, "\"%s\\gvim.exe\" -silent -register", installdir);
2133     system(register_command_string);
2134 }
2135 
2136 /*
2137  * Remove the last part of directory "path[]" to get its parent, and put the
2138  * result in "to[]".
2139  */
2140     static void
2141 dir_remove_last(const char *path, char to[MAX_PATH])
2142 {
2143     char c;
2144     long last_char_to_copy;
2145     long path_length = strlen(path);
2146 
2147     // skip the last character just in case it is a '\\'
2148     last_char_to_copy = path_length - 2;
2149     c = path[last_char_to_copy];
2150 
2151     while (c != '\\')
2152     {
2153 	last_char_to_copy--;
2154 	c = path[last_char_to_copy];
2155     }
2156 
2157     strncpy(to, path, (size_t)last_char_to_copy);
2158     to[last_char_to_copy] = NUL;
2159 }
2160 
2161     static void
2162 set_directories_text(int idx)
2163 {
2164     int vimfiles_dir_choice = choices[idx].arg;
2165 
2166     if (vimfiles_dir_choice == (int)vimfiles_dir_none)
2167 	alloc_text(idx, "Do NOT create plugin directories%s", "");
2168     else
2169 	alloc_text(idx, "Create plugin directories: %s",
2170 				   vimfiles_dir_choices[vimfiles_dir_choice]);
2171 }
2172 
2173 /*
2174  * To get the "real" home directory:
2175  * - get value of $HOME
2176  * - if not found, get value of $HOMEDRIVE$HOMEPATH
2177  * - if not found, get value of $USERPROFILE
2178  *
2179  * This code is based on init_homedir() in misc1.c, keep in sync!
2180  */
2181 static char *homedir = NULL;
2182 
2183     void
2184 init_homedir(void)
2185 {
2186     char    *var;
2187     char    buf[MAX_PATH];
2188 
2189     if (homedir != NULL)
2190     {
2191 	free(homedir);
2192 	homedir = NULL;
2193     }
2194 
2195     var = getenv("HOME");
2196 
2197     /*
2198      * Typically, $HOME is not defined on Windows, unless the user has
2199      * specifically defined it for Vim's sake.  However, on Windows NT
2200      * platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
2201      * each user.  Try constructing $HOME from these.
2202      */
2203     if (var == NULL || *var == NUL)
2204     {
2205 	char	*homedrive, *homepath;
2206 
2207 	homedrive = getenv("HOMEDRIVE");
2208 	homepath = getenv("HOMEPATH");
2209 	if (homepath == NULL || *homepath == NUL)
2210 	    homepath = "\\";
2211 	if (homedrive != NULL
2212 		   && strlen(homedrive) + strlen(homepath) < sizeof(buf))
2213 	{
2214 	    sprintf(buf, "%s%s", homedrive, homepath);
2215 	    if (buf[0] != NUL)
2216 		var = buf;
2217 	}
2218     }
2219 
2220     if (var == NULL)
2221 	var = getenv("USERPROFILE");
2222 
2223     /*
2224      * Weird but true: $HOME may contain an indirect reference to another
2225      * variable, esp. "%USERPROFILE%".  Happens when $USERPROFILE isn't set
2226      * when $HOME is being set.
2227      */
2228     if (var != NULL && *var == '%')
2229     {
2230 	char	*p;
2231 	char	*exp;
2232 
2233 	p = strchr(var + 1, '%');
2234 	if (p != NULL)
2235 	{
2236 	    strncpy(buf, var + 1, p - (var + 1));
2237 	    buf[p - (var + 1)] = NUL;
2238 	    exp = getenv(buf);
2239 	    if (exp != NULL && *exp != NUL
2240 				&& strlen(exp) + strlen(p) < sizeof(buf))
2241 	    {
2242 		sprintf(buf, "%s%s", exp, p + 1);
2243 		var = buf;
2244 	    }
2245 	}
2246     }
2247 
2248     if (var != NULL && *var == NUL)	// empty is same as not set
2249 	var = NULL;
2250 
2251     if (var == NULL)
2252 	homedir = NULL;
2253     else
2254 	homedir = _strdup(var);
2255 }
2256 
2257 /*
2258  * Change the directory that the vim plugin directories will be created in:
2259  * $HOME, $VIM or nowhere.
2260  */
2261     static void
2262 change_directories_choice(int idx)
2263 {
2264     int	    choice_count = TABLE_SIZE(vimfiles_dir_choices);
2265 
2266     // Don't offer the $HOME choice if $HOME isn't set.
2267     if (homedir == NULL)
2268 	--choice_count;
2269     choices[idx].arg = get_choice(vimfiles_dir_choices, choice_count);
2270     set_directories_text(idx);
2271 }
2272 
2273 /*
2274  * Create the plugin directories...
2275  */
2276 //ARGSUSED
2277     static void
2278 install_vimfilesdir(int idx)
2279 {
2280     int i;
2281     int vimfiles_dir_choice = choices[idx].arg;
2282     char *p;
2283     char vimdir_path[MAX_PATH];
2284     char vimfiles_path[MAX_PATH + 9];
2285     char tmp_dirname[BUFSIZE];
2286 
2287     // switch on the location that the user wants the plugin directories
2288     // built in
2289     switch (vimfiles_dir_choice)
2290     {
2291 	case vimfiles_dir_vim:
2292 	{
2293 	    // Go to the %VIM% directory - check env first, then go one dir
2294 	    //	   below installdir if there is no %VIM% environment variable.
2295 	    //	   The accuracy of $VIM is checked in inspect_system(), so we
2296 	    //	   can be sure it is ok to use here.
2297 	    p = getenv("VIM");
2298 	    if (p == NULL) // No $VIM in path
2299 		dir_remove_last(installdir, vimdir_path);
2300 	    else
2301 		strcpy(vimdir_path, p);
2302 	    break;
2303 	}
2304 	case vimfiles_dir_home:
2305 	{
2306 	    // Find the $HOME directory.  Its existence was already checked.
2307 	    p = homedir;
2308 	    if (p == NULL)
2309 	    {
2310 		printf("Internal error: $HOME is NULL\n");
2311 		p = "c:\\";
2312 	    }
2313 	    strcpy(vimdir_path, p);
2314 	    break;
2315 	}
2316 	case vimfiles_dir_none:
2317 	{
2318 	    // Do not create vim plugin directory.
2319 	    return;
2320 	}
2321     }
2322 
2323     // Now, just create the directory.	If it already exists, it will fail
2324     // silently.
2325     sprintf(vimfiles_path, "%s\\vimfiles", vimdir_path);
2326     vim_mkdir(vimfiles_path, 0755);
2327 
2328     printf("Creating the following directories in \"%s\":\n", vimfiles_path);
2329     for (i = 0; i < TABLE_SIZE(vimfiles_subdirs); i++)
2330     {
2331 	sprintf(tmp_dirname, "%s\\%s", vimfiles_path, vimfiles_subdirs[i]);
2332 	printf("  %s", vimfiles_subdirs[i]);
2333 	vim_mkdir(tmp_dirname, 0755);
2334     }
2335     printf("\n");
2336 }
2337 
2338 /*
2339  * Add the creation of runtime files to the setup sequence.
2340  */
2341     static void
2342 init_directories_choice(void)
2343 {
2344     struct stat	st;
2345     char	tmp_dirname[BUFSIZE];
2346     char	*p;
2347     int		vimfiles_dir_choice;
2348 
2349     choices[choice_count].text = alloc(150);
2350     choices[choice_count].changefunc = change_directories_choice;
2351     choices[choice_count].installfunc = install_vimfilesdir;
2352     choices[choice_count].active = 1;
2353 
2354     // Check if the "compiler" directory already exists.  That's a good
2355     // indication that the plugin directories were already created.
2356     p = getenv("HOME");
2357     if (p != NULL)
2358     {
2359 	vimfiles_dir_choice = (int)vimfiles_dir_home;
2360 	sprintf(tmp_dirname, "%s\\vimfiles\\compiler", p);
2361 	if (stat(tmp_dirname, &st) == 0)
2362 	    vimfiles_dir_choice = (int)vimfiles_dir_none;
2363     }
2364     else
2365     {
2366 	vimfiles_dir_choice = (int)vimfiles_dir_vim;
2367 	p = getenv("VIM");
2368 	if (p == NULL)  // No $VIM in path, use the install dir.
2369 	    dir_remove_last(installdir, tmp_dirname);
2370 	else
2371 	    strcpy(tmp_dirname, p);
2372 	strcat(tmp_dirname, "\\vimfiles\\compiler");
2373 	if (stat(tmp_dirname, &st) == 0)
2374 	    vimfiles_dir_choice = (int)vimfiles_dir_none;
2375     }
2376 
2377     choices[choice_count].arg = vimfiles_dir_choice;
2378     set_directories_text(choice_count);
2379     ++choice_count;
2380 }
2381 
2382 /*
2383  * Setup the choices and the default values.
2384  */
2385     static void
2386 setup_choices(void)
2387 {
2388     // install the batch files
2389     init_bat_choices();
2390 
2391     // (over) write _vimrc file
2392     init_vimrc_choices();
2393 
2394     // Whether to add Vim to the popup menu
2395     init_popup_choice();
2396 
2397     // Whether to add Vim to the "Open With..." menu
2398     init_openwith_choice();
2399 
2400     // Whether to add Vim to the Start Menu.
2401     init_startmenu_choice();
2402 
2403     // Whether to add shortcuts to the Desktop.
2404     init_shortcut_choices();
2405 
2406     // Whether to create the runtime directories.
2407     init_directories_choice();
2408 }
2409 
2410     static void
2411 print_cmd_line_help(void)
2412 {
2413     printf("Vim installer non-interactive command line arguments:\n");
2414     printf("\n");
2415     printf("-create-batfiles  [vim gvim evim view gview vimdiff gvimdiff]\n");
2416     printf("    Create .bat files for Vim variants in the Windows directory.\n");
2417     printf("-create-vimrc\n");
2418     printf("    Create a default _vimrc file if one does not already exist.\n");
2419     printf("-vimrc-remap [no|win]\n");
2420     printf("    Remap keys when creating a default _vimrc file.\n");
2421     printf("-vimrc-behave [unix|mswin|default]\n");
2422     printf("    Set mouse behavior when creating a default _vimrc file.\n");
2423     printf("-vimrc-compat [vi|vim|defaults|all]\n");
2424     printf("    Set Vi compatibility when creating a default _vimrc file.\n");
2425     printf("-install-popup\n");
2426     printf("    Install the Edit-with-Vim context menu entry\n");
2427     printf("-install-openwith\n");
2428     printf("    Add Vim to the \"Open With...\" context menu list\n");
2429     printf("-add-start-menu");
2430     printf("    Add Vim to the start menu\n");
2431     printf("-install-icons");
2432     printf("    Create icons for gVim executables on the desktop\n");
2433     printf("-create-directories [vim|home]\n");
2434     printf("    Create runtime directories to drop plugins into; in the $VIM\n");
2435     printf("    or $HOME directory\n");
2436     printf("-register-OLE");
2437     printf("    Ignored\n");
2438     printf("\n");
2439 }
2440 
2441 /*
2442  * Setup installation choices based on command line switches
2443  */
2444     static void
2445 command_line_setup_choices(int argc, char **argv)
2446 {
2447     int i, j;
2448 
2449     for (i = 1; i < argc; i++)
2450     {
2451 	if (strcmp(argv[i], "-create-batfiles") == 0)
2452 	{
2453 	    if (i + 1 == argc)
2454 		continue;
2455 	    while (argv[i + 1][0] != '-' && i < argc)
2456 	    {
2457 		i++;
2458 		for (j = 1; j < TARGET_COUNT; ++j)
2459 		    if ((targets[j].exenamearg[0] == 'g' ? has_gvim : has_vim)
2460 			    && strcmp(argv[i], targets[j].name) == 0)
2461 		    {
2462 			init_bat_choice(j);
2463 			break;
2464 		    }
2465 		if (j == TARGET_COUNT)
2466 		    printf("%s is not a valid choice for -create-batfiles\n",
2467 								     argv[i]);
2468 
2469 		if (i + 1 == argc)
2470 		    break;
2471 	    }
2472 	}
2473 	else if (strcmp(argv[i], "-create-vimrc") == 0)
2474 	{
2475 	    // Setup default vimrc choices.  If there is already a _vimrc file,
2476 	    // it will NOT be overwritten.
2477 	    init_vimrc_choices();
2478 	}
2479 	else if (strcmp(argv[i], "-vimrc-remap") == 0)
2480 	{
2481 	    if (i + 1 == argc)
2482 		break;
2483 	    i++;
2484 	    if (strcmp(argv[i], "no") == 0)
2485 		remap_choice = remap_no;
2486 	    else if (strcmp(argv[i], "win") == 0)
2487 		remap_choice = remap_win;
2488 	}
2489 	else if (strcmp(argv[i], "-vimrc-behave") == 0)
2490 	{
2491 	    if (i + 1 == argc)
2492 		break;
2493 	    i++;
2494 	    if (strcmp(argv[i], "unix") == 0)
2495 		mouse_choice = mouse_xterm;
2496 	    else if (strcmp(argv[i], "mswin") == 0)
2497 		mouse_choice = mouse_mswin;
2498 	    else if (strcmp(argv[i], "default") == 0)
2499 		mouse_choice = mouse_default;
2500 	}
2501 	else if (strcmp(argv[i], "-vimrc-compat") == 0)
2502 	{
2503 	    if (i + 1 == argc)
2504 		break;
2505 	    i++;
2506 	    if (strcmp(argv[i], "vi") == 0)
2507 		compat_choice = compat_vi;
2508 	    else if (strcmp(argv[i], "vim") == 0)
2509 		compat_choice = compat_vim;
2510 	    else if (strcmp(argv[i], "defaults") == 0)
2511 		compat_choice = compat_some_enhancements;
2512 	    else if (strcmp(argv[i], "all") == 0)
2513 		compat_choice = compat_all_enhancements;
2514 	}
2515 	else if (strcmp(argv[i], "-install-popup") == 0)
2516 	{
2517 	    init_popup_choice();
2518 	}
2519 	else if (strcmp(argv[i], "-install-openwith") == 0)
2520 	{
2521 	    init_openwith_choice();
2522 	}
2523 	else if (strcmp(argv[i], "-add-start-menu") == 0)
2524 	{
2525 	    init_startmenu_choice();
2526 	}
2527 	else if (strcmp(argv[i], "-install-icons") == 0)
2528 	{
2529 	    init_shortcut_choices();
2530 	}
2531 	else if (strcmp(argv[i], "-create-directories") == 0)
2532 	{
2533 	    int vimfiles_dir_choice = (int)vimfiles_dir_none;
2534 
2535 	    init_directories_choice();
2536 	    if (argv[i + 1][0] != '-')
2537 	    {
2538 		i++;
2539 		if (strcmp(argv[i], "vim") == 0)
2540 		    vimfiles_dir_choice = (int)vimfiles_dir_vim;
2541 		else if (strcmp(argv[i], "home") == 0)
2542 		{
2543 		    if (homedir == NULL)  // No $HOME in environment
2544 			vimfiles_dir_choice = (int)vimfiles_dir_none;
2545 		    else
2546 			vimfiles_dir_choice = (int)vimfiles_dir_home;
2547 		}
2548 		else
2549 		{
2550 		    printf("Unknown argument for -create-directories: %s\n",
2551 								     argv[i]);
2552 		    print_cmd_line_help();
2553 		}
2554 	    }
2555 	    else // No choice specified, default to vim directory
2556 		vimfiles_dir_choice = (int)vimfiles_dir_vim;
2557 	    choices[choice_count - 1].arg = vimfiles_dir_choice;
2558 	}
2559 	else if (strcmp(argv[i], "-register-OLE") == 0)
2560 	{
2561 	    // This is always done when gvim is found
2562 	}
2563 	else // Unknown switch
2564 	{
2565 	    printf("Got unknown argument argv[%d] = %s\n", i, argv[i]);
2566 	    print_cmd_line_help();
2567 	}
2568     }
2569 }
2570 
2571 
2572 /*
2573  * Show a few screens full of helpful information.
2574  */
2575     static void
2576 show_help(void)
2577 {
2578     static char *(items[]) =
2579     {
2580 "Installing .bat files\n"
2581 "---------------------\n"
2582 "The vim.bat file is written in one of the directories in $PATH.\n"
2583 "This makes it possible to start Vim from the command line.\n"
2584 "If vim.exe can be found in $PATH, the choice for vim.bat will not be\n"
2585 "present.  It is assumed you will use the existing vim.exe.\n"
2586 "If vim.bat can already be found in $PATH this is probably for an old\n"
2587 "version of Vim (but this is not checked!).  You can overwrite it.\n"
2588 "If no vim.bat already exists, you can select one of the directories in\n"
2589 "$PATH for creating the batch file, or disable creating a vim.bat file.\n"
2590 "\n"
2591 "If you choose not to create the vim.bat file, Vim can still be executed\n"
2592 "in other ways, but not from the command line.\n"
2593 "\n"
2594 "The same applies to choices for gvim, evim, (g)view, and (g)vimdiff.\n"
2595 "The first item can be used to change the path for all of them.\n"
2596 ,
2597 "Creating a _vimrc file\n"
2598 "----------------------\n"
2599 "The _vimrc file is used to set options for how Vim behaves.\n"
2600 "The install program can create a _vimrc file with a few basic choices.\n"
2601 "You can edit this file later to tune your preferences.\n"
2602 "If you already have a _vimrc or .vimrc file it can be overwritten.\n"
2603 "Don't do that if you have made changes to it.\n"
2604 ,
2605 "Vim features\n"
2606 "------------\n"
2607 "(this choice is only available when creating a _vimrc file)\n"
2608 "1. Vim can run in Vi-compatible mode.  Many nice Vim features are then\n"
2609 "   disabled.  Only choose Vi-compatible if you really need full Vi\n"
2610 "   compatibility.\n"
2611 "2. Vim runs in not-Vi-compatible mode.  Vim is still mostly Vi compatible,\n"
2612 "   but adds nice features like multi-level undo.\n"
2613 "3. Running Vim with some enhancements is useful when you want some of\n"
2614 "   the nice Vim features, but have a slow computer and want to keep it\n"
2615 "   really fast.\n"
2616 "4. Syntax highlighting shows many files in color.  Not only does this look\n"
2617 "   nice, it also makes it easier to spot errors and you can work faster.\n"
2618 "   The other features include editing compressed files.\n"
2619 ,
2620 "Windows key mapping\n"
2621 "-------------------\n"
2622 "(this choice is only available when creating a _vimrc file)\n"
2623 "Under MS-Windows the CTRL-C key copies text to the clipboard and CTRL-V\n"
2624 "pastes text from the clipboard.  There are a few more keys like these.\n"
2625 "Unfortunately, in Vim these keys normally have another meaning.\n"
2626 "1. Choose to have the keys like they normally are in Vim (useful if you\n"
2627 "   also use Vim on other systems).\n"
2628 "2. Choose to have the keys work like they are used on MS-Windows (useful\n"
2629 "   if you mostly work on MS-Windows).\n"
2630 ,
2631 "Mouse use\n"
2632 "---------\n"
2633 "(this choice is only available when creating a _vimrc file)\n"
2634 "The right mouse button can be used in two ways:\n"
2635 "1. The Unix way is to extend an existing selection.  The popup menu is\n"
2636 "   not available.\n"
2637 "2. The MS-Windows way is to show a popup menu, which allows you to\n"
2638 "   copy/paste text, undo/redo, etc.  Extending the selection can still be\n"
2639 "   done by keeping SHIFT pressed while using the left mouse button\n"
2640 ,
2641 "Edit-with-Vim context menu entry\n"
2642 "--------------------------------\n"
2643 "(this choice is only available when gvim.exe and gvimext.dll are present)\n"
2644 "You can associate different file types with Vim, so that you can (double)\n"
2645 "click on a file to edit it with Vim.  This means you have to individually\n"
2646 "select each file type.\n"
2647 "An alternative is the option offered here: Install an \"Edit with Vim\"\n"
2648 "entry in the popup menu for the right mouse button.  This means you can\n"
2649 "edit any file with Vim.\n"
2650 ,
2651 "\"Open With...\" context menu entry\n"
2652 "--------------------------------\n"
2653 "(this choice is only available when gvim.exe is present)\n"
2654 "This option adds Vim to the \"Open With...\" entry in the popup menu for\n"
2655 "the right mouse button.  This also makes it possible to edit HTML files\n"
2656 "directly from Internet Explorer.\n"
2657 ,
2658 "Add Vim to the Start menu\n"
2659 "-------------------------\n"
2660 "In Windows 95 and later, Vim can be added to the Start menu.  This will\n"
2661 "create a submenu with an entry for vim, gvim, evim, vimdiff, etc..\n"
2662 ,
2663 "Icons on the desktop\n"
2664 "--------------------\n"
2665 "(these choices are only available when installing gvim)\n"
2666 "In Windows 95 and later, shortcuts (icons) can be created on the Desktop.\n"
2667 ,
2668 "Create plugin directories\n"
2669 "-------------------------\n"
2670 "Plugin directories allow extending Vim by dropping a file into a directory.\n"
2671 "This choice allows creating them in $HOME (if you have a home directory) or\n"
2672 "$VIM (used for everybody on the system).\n"
2673 ,
2674 NULL
2675     };
2676     int		i;
2677     int		c;
2678 
2679     rewind(stdin);
2680     printf("\n");
2681     for (i = 0; items[i] != NULL; ++i)
2682     {
2683 	puts(items[i]);
2684 	printf("Hit Enter to continue, b (back) or q (quit help): ");
2685 	c = getchar();
2686 	rewind(stdin);
2687 	if (c == 'b' || c == 'B')
2688 	{
2689 	    if (i == 0)
2690 		--i;
2691 	    else
2692 		i -= 2;
2693 	}
2694 	if (c == 'q' || c == 'Q')
2695 	    break;
2696 	printf("\n");
2697     }
2698 }
2699 
2700 /*
2701  * Install the choices.
2702  */
2703     static void
2704 install(void)
2705 {
2706     int		i;
2707 
2708     // Install the selected choices.
2709     for (i = 0; i < choice_count; ++i)
2710 	if (choices[i].installfunc != NULL && choices[i].active)
2711 	    (choices[i].installfunc)(i);
2712 
2713     // Add some entries to the registry, if needed.
2714     if (install_popup
2715 	    || install_openwith
2716 	    || (need_uninstall_entry && interactive)
2717 	    || !interactive)
2718 	install_registry();
2719 
2720     // Register gvim with OLE.
2721     if (has_gvim)
2722 	install_OLE_register();
2723 }
2724 
2725 /*
2726  * request_choice
2727  */
2728     static void
2729 request_choice(void)
2730 {
2731     int		      i;
2732 
2733     printf("\n\nInstall will do for you:\n");
2734     for (i = 0; i < choice_count; ++i)
2735       if (choices[i].active)
2736 	  printf("%2d  %s\n", i + 1, choices[i].text);
2737     printf("To change an item, enter its number\n\n");
2738     printf("Enter item number, h (help), d (do it) or q (quit): ");
2739 }
2740 
2741     int
2742 main(int argc, char **argv)
2743 {
2744     int		i;
2745     char	buf[BUFSIZE];
2746 
2747     /*
2748      * Run interactively if there are no command line arguments.
2749      */
2750     if (argc > 1)
2751 	interactive = 0;
2752     else
2753 	interactive = 1;
2754 
2755     // Initialize this program.
2756     do_inits(argv);
2757     init_homedir();
2758 
2759     if (argc > 1 && strcmp(argv[1], "-uninstall-check") == 0)
2760     {
2761 	// Only check for already installed Vims.  Used by NSIS installer.
2762 	i = uninstall_check(1);
2763 
2764 	// Find the value of $VIM, because NSIS isn't able to do this by
2765 	// itself.
2766 	get_vim_env();
2767 
2768 	// When nothing found exit quietly.  If something found wait for
2769 	// a little while, so that the user can read the messages.
2770 	if (i && _isatty(1))
2771 	    sleep(3);
2772 	exit(0);
2773     }
2774 
2775     printf("This program sets up the installation of Vim "
2776 						   VIM_VERSION_MEDIUM "\n\n");
2777 
2778     // Check if the user unpacked the archives properly.
2779     check_unpack();
2780 
2781     // Check for already installed Vims.
2782     if (interactive)
2783 	uninstall_check(0);
2784 
2785     // Find out information about the system.
2786     inspect_system();
2787 
2788     if (interactive)
2789     {
2790 	// Setup all the choices.
2791 	setup_choices();
2792 
2793 	// Let the user change choices and finally install (or quit).
2794 	for (;;)
2795 	{
2796 	    request_choice();
2797 	    rewind(stdin);
2798 	    if (scanf("%99s", buf) == 1)
2799 	    {
2800 		if (isdigit(buf[0]))
2801 		{
2802 		    // Change a choice.
2803 		    i = atoi(buf);
2804 		    if (i > 0 && i <= choice_count && choices[i - 1].active)
2805 			(choices[i - 1].changefunc)(i - 1);
2806 		    else
2807 			printf("\nIllegal choice\n");
2808 		}
2809 		else if (buf[0] == 'h' || buf[0] == 'H')
2810 		{
2811 		    // Help
2812 		    show_help();
2813 		}
2814 		else if (buf[0] == 'd' || buf[0] == 'D')
2815 		{
2816 		    // Install!
2817 		    install();
2818 		    printf("\nThat finishes the installation.  Happy Vimming!\n");
2819 		    break;
2820 		}
2821 		else if (buf[0] == 'q' || buf[0] == 'Q')
2822 		{
2823 		    // Quit
2824 		    printf("\nExiting without anything done\n");
2825 		    break;
2826 		}
2827 		else
2828 		    printf("\nIllegal choice\n");
2829 	    }
2830 	}
2831 	printf("\n");
2832 	myexit(0);
2833     }
2834     else
2835     {
2836 	/*
2837 	 * Run non-interactive - setup according to the command line switches
2838 	 */
2839 	command_line_setup_choices(argc, argv);
2840 	install();
2841 
2842 	// Avoid that the user has to hit Enter, just wait a little bit to
2843 	// allow reading the messages.
2844 	sleep(2);
2845     }
2846 
2847     return 0;
2848 }
2849