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