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