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