xref: /vim-8.2.3635/src/dosinst.h (revision 12ee7ff0)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 /*
10  * dosinst.h: Common code for dosinst.c and uninstal.c
11  */
12 
13 /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */
14 #if _MSC_VER >= 1400
15 # define _CRT_SECURE_NO_DEPRECATE
16 # define _CRT_NONSTDC_NO_DEPRECATE
17 #endif
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 
25 #ifndef UNIX_LINT
26 # include "vimio.h"
27 # include <ctype.h>
28 
29 # include <direct.h>
30 
31 # include <windows.h>
32 # include <shlobj.h>
33 #endif
34 
35 #ifdef UNIX_LINT
36 /* Running lint on Unix: Some things are missing. */
37 char *searchpath(char *name);
38 #endif
39 
40 #if defined(UNIX_LINT)
41 # include <unistd.h>
42 # include <errno.h>
43 #endif
44 
45 #include "version.h"
46 
47 #if defined(UNIX_LINT)
48 # define vim_mkdir(x, y) mkdir((char *)(x), y)
49 #else
50 # define vim_mkdir(x, y) _mkdir((char *)(x))
51 #endif
52 
53 #define sleep(n) Sleep((n) * 1000)
54 
55 /* ---------------------------------------- */
56 
57 
58 #define BUFSIZE (MAX_PATH*2)		/* long enough to hold a file name path */
59 #define NUL 0
60 
61 #define FAIL 0
62 #define OK 1
63 
64 #ifndef FALSE
65 # define FALSE 0
66 #endif
67 #ifndef TRUE
68 # define TRUE 1
69 #endif
70 
71 /*
72  * Modern way of creating registry entries, also works on 64 bit windows when
73  * compiled as a 32 bit program.
74  */
75 # ifndef KEY_WOW64_64KEY
76 #  define KEY_WOW64_64KEY 0x0100
77 # endif
78 # ifndef KEY_WOW64_32KEY
79 #  define KEY_WOW64_32KEY 0x0200
80 # endif
81 
82 #define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT
83 
84 int	interactive;		/* non-zero when running interactively */
85 
86 /*
87  * Call malloc() and exit when out of memory.
88  */
89     static void *
90 alloc(int len)
91 {
92     void *p;
93 
94     p = malloc(len);
95     if (p == NULL)
96     {
97 	printf("ERROR: out of memory\n");
98 	exit(1);
99     }
100     return p;
101 }
102 
103 /*
104  * The toupper() in Bcc 5.5 doesn't work, use our own implementation.
105  */
106     static int
107 mytoupper(int c)
108 {
109     if (c >= 'a' && c <= 'z')
110 	return c - 'a' + 'A';
111     return c;
112 }
113 
114     static void
115 myexit(int n)
116 {
117     if (!interactive)
118     {
119 	/* Present a prompt, otherwise error messages can't be read. */
120 	printf("Press Enter to continue\n");
121 	rewind(stdin);
122 	(void)getchar();
123     }
124     exit(n);
125 }
126 
127 
128 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
129 /*
130  * Check if this is a 64-bit OS.
131  */
132     static BOOL
133 is_64bit_os(void)
134 {
135 #ifdef _WIN64
136     return TRUE;
137 #else
138     BOOL bIsWow64 = FALSE;
139     LPFN_ISWOW64PROCESS pIsWow64Process;
140 
141     pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
142 	    GetModuleHandle("kernel32"), "IsWow64Process");
143     if (pIsWow64Process != NULL)
144 	pIsWow64Process(GetCurrentProcess(), &bIsWow64);
145     return bIsWow64;
146 #endif
147 }
148 
149     static char *
150 searchpath(char *name)
151 {
152     static char widename[2 * BUFSIZE];
153     static char location[2 * BUFSIZE + 2];
154 
155     /* There appears to be a bug in FindExecutableA() on Windows NT.
156      * Use FindExecutableW() instead... */
157     MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
158 	    (LPWSTR)widename, BUFSIZE);
159     if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
160 		(LPWSTR)location) > (HINSTANCE)32)
161     {
162 	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
163 		(LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
164 	return widename;
165     }
166     return NULL;
167 }
168 
169 /*
170  * Call searchpath() and save the result in allocated memory, or return NULL.
171  */
172     static char *
173 searchpath_save(char *name)
174 {
175     char	*p;
176     char	*s;
177 
178     p = searchpath(name);
179     if (p == NULL)
180 	return NULL;
181     s = alloc(strlen(p) + 1);
182     strcpy(s, p);
183     return s;
184 }
185 
186 
187 #ifndef CSIDL_COMMON_PROGRAMS
188 # define CSIDL_COMMON_PROGRAMS 0x0017
189 #endif
190 #ifndef CSIDL_COMMON_DESKTOPDIRECTORY
191 # define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019
192 #endif
193 
194 /*
195  * Get the path to a requested Windows shell folder.
196  *
197  * Return FAIL on error, OK on success
198  */
199     int
200 get_shell_folder_path(
201 	char *shell_folder_path,
202 	const char *shell_folder_name)
203 {
204     /*
205      * The following code was successfully built with make_mvc.mak.
206      * The resulting executable worked on Windows 95, Millennium Edition, and
207      * 2000 Professional.  But it was changed after testing...
208      */
209     LPITEMIDLIST    pidl = 0; /* Pointer to an Item ID list allocated below */
210     LPMALLOC	    pMalloc;  /* Pointer to an IMalloc interface */
211     int		    csidl;
212     int		    alt_csidl = -1;
213     static int	    desktop_csidl = -1;
214     static int	    programs_csidl = -1;
215     int		    *pcsidl;
216     int		    r;
217 
218     if (strcmp(shell_folder_name, "desktop") == 0)
219     {
220 	pcsidl = &desktop_csidl;
221 	csidl = CSIDL_COMMON_DESKTOPDIRECTORY;
222 	alt_csidl = CSIDL_DESKTOP;
223     }
224     else if (strncmp(shell_folder_name, "Programs", 8) == 0)
225     {
226 	pcsidl = &programs_csidl;
227 	csidl = CSIDL_COMMON_PROGRAMS;
228 	alt_csidl = CSIDL_PROGRAMS;
229     }
230     else
231     {
232 	printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n",
233 							   shell_folder_name);
234 	return FAIL;
235     }
236 
237     /* Did this stuff before, use the same ID again. */
238     if (*pcsidl >= 0)
239     {
240 	csidl = *pcsidl;
241 	alt_csidl = -1;
242     }
243 
244 retry:
245     /* Initialize pointer to IMalloc interface */
246     if (NOERROR != SHGetMalloc(&pMalloc))
247     {
248 	printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n",
249 							   shell_folder_name);
250 	return FAIL;
251     }
252 
253     /* Get an ITEMIDLIST corresponding to the folder code */
254     if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl))
255     {
256 	if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0,
257 							    alt_csidl, &pidl))
258 	{
259 	    printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
260 							   shell_folder_name);
261 	    return FAIL;
262 	}
263 	csidl = alt_csidl;
264 	alt_csidl = -1;
265     }
266 
267     /* Translate that ITEMIDLIST to a string */
268     r = SHGetPathFromIDList(pidl, shell_folder_path);
269 
270     /* Free the data associated with pidl */
271     pMalloc->lpVtbl->Free(pMalloc, pidl);
272     /* Release the IMalloc interface */
273     pMalloc->lpVtbl->Release(pMalloc);
274 
275     if (!r)
276     {
277 	if (alt_csidl >= 0)
278 	{
279 	    /* We probably get here for Windows 95: the "all users"
280 	     * desktop/start menu entry doesn't exist. */
281 	    csidl = alt_csidl;
282 	    alt_csidl = -1;
283 	    goto retry;
284 	}
285 	printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
286 							   shell_folder_name);
287 	return FAIL;
288     }
289 
290     /* If there is an alternative: verify we can write in this directory.
291      * This should cause a retry when the "all users" directory exists but we
292      * are a normal user and can't write there. */
293     if (alt_csidl >= 0)
294     {
295 	char tbuf[BUFSIZE];
296 	FILE *fd;
297 
298 	strcpy(tbuf, shell_folder_path);
299 	strcat(tbuf, "\\vim write test");
300 	fd = fopen(tbuf, "w");
301 	if (fd == NULL)
302 	{
303 	    csidl = alt_csidl;
304 	    alt_csidl = -1;
305 	    goto retry;
306 	}
307 	fclose(fd);
308 	unlink(tbuf);
309     }
310 
311     /*
312      * Keep the found csidl for next time, so that we don't have to do the
313      * write test every time.
314      */
315     if (*pcsidl < 0)
316 	*pcsidl = csidl;
317 
318     if (strncmp(shell_folder_name, "Programs\\", 9) == 0)
319 	strcat(shell_folder_path, shell_folder_name + 8);
320 
321     return OK;
322 }
323 
324 /*
325  * List of targets.  The first one (index zero) is used for the default path
326  * for the batch files.
327  */
328 #define TARGET_COUNT  9
329 
330 struct
331 {
332     char	*name;		/* Vim exe name (without .exe) */
333     char	*batname;	/* batch file name */
334     char	*lnkname;	/* shortcut file name */
335     char	*exename;	/* exe file name */
336     char	*exenamearg;	/* exe file name when using exearg */
337     char	*exearg;	/* argument for vim.exe or gvim.exe */
338     char	*oldbat;	/* path to existing xxx.bat or NULL */
339     char	*oldexe;	/* path to existing xxx.exe or NULL */
340     char	batpath[BUFSIZE];  /* path of batch file to create; not
341 				      created when it's empty */
342 } targets[TARGET_COUNT] =
343 {
344     {"all",	"batch files"},
345     {"vim",	"vim.bat",	"Vim.lnk",
346 					"vim.exe",    "vim.exe",  ""},
347     {"gvim",	"gvim.bat",	"gVim.lnk",
348 					"gvim.exe",   "gvim.exe", ""},
349     {"evim",	"evim.bat",	"gVim Easy.lnk",
350 					"evim.exe",   "gvim.exe", "-y"},
351     {"view",	"view.bat",	"Vim Read-only.lnk",
352 					"view.exe",   "vim.exe",  "-R"},
353     {"gview",	"gview.bat",	"gVim Read-only.lnk",
354 					"gview.exe",  "gvim.exe", "-R"},
355     {"vimdiff", "vimdiff.bat",	"Vim Diff.lnk",
356 					"vimdiff.exe","vim.exe",  "-d"},
357     {"gvimdiff","gvimdiff.bat",	"gVim Diff.lnk",
358 					"gvimdiff.exe","gvim.exe", "-d"},
359     {"vimtutor","vimtutor.bat", "Vim tutor.lnk",
360 					"vimtutor.bat",  "vimtutor.bat", ""},
361 };
362 
363 #define ICON_COUNT 3
364 char *(icon_names[ICON_COUNT]) =
365 	{"gVim " VIM_VERSION_SHORT,
366 	 "gVim Easy " VIM_VERSION_SHORT,
367 	 "gVim Read only " VIM_VERSION_SHORT};
368 char *(icon_link_names[ICON_COUNT]) =
369 	{"gVim " VIM_VERSION_SHORT ".lnk",
370 	 "gVim Easy " VIM_VERSION_SHORT ".lnk",
371 	 "gVim Read only " VIM_VERSION_SHORT ".lnk"};
372 
373 /* This is only used for dosinst.c. */
374 #if defined(DOSINST)
375 /*
376  * Run an external command and wait for it to finish.
377  */
378     static void
379 run_command(char *cmd)
380 {
381     char	*cmd_path;
382     char	cmd_buf[BUFSIZE * 2 + 35];
383     char	*p;
384 
385     /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an
386      * executable (start.exe) like in Win9x. */
387     cmd_path = searchpath_save("cmd.exe");
388     if (cmd_path != NULL)
389     {
390 	/* There is a cmd.exe, so this might be Windows NT.  If it is,
391 	 * we need to call cmd.exe explicitly.  If it is a later OS,
392 	 * calling cmd.exe won't hurt if it is present.
393 	 * Also, "start" on NT expects a window title argument.
394 	 */
395 	/* Replace the slashes with backslashes. */
396 	while ((p = strchr(cmd_path, '/')) != NULL)
397 	    *p = '\\';
398 	sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd);
399 	free(cmd_path);
400     }
401     else
402     {
403 	/* No cmd.exe, just make the call and let the system handle it. */
404 	sprintf(cmd_buf, "start /w %s", cmd);
405     }
406     system(cmd_buf);
407 }
408 #endif
409 
410 /*
411  * Append a backslash to "name" if there isn't one yet.
412  */
413     void
414 add_pathsep(char *name)
415 {
416     int		len = strlen(name);
417 
418     if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/')
419 	strcat(name, "\\");
420 }
421 
422 /*
423  * The normal chdir() does not change the default drive.  This one does.
424  */
425 /*ARGSUSED*/
426     int
427 change_drive(int drive)
428 {
429     char temp[3] = "-:";
430     temp[0] = (char)(drive + 'A' - 1);
431     return !SetCurrentDirectory(temp);
432 }
433 
434 /*
435  * Change directory to "path".
436  * Return 0 for success, -1 for failure.
437  */
438     int
439 mch_chdir(char *path)
440 {
441     if (path[0] == NUL)		/* just checking... */
442 	return 0;
443     if (path[1] == ':')		/* has a drive name */
444     {
445 	if (change_drive(mytoupper(path[0]) - 'A' + 1))
446 	    return -1;		/* invalid drive name */
447 	path += 2;
448     }
449     if (*path == NUL)		/* drive name only */
450 	return 0;
451     return chdir(path);		/* let the normal chdir() do the rest */
452 }
453 
454 /*
455  * Expand the executable name into a full path name.
456  */
457     static char *
458 my_fullpath(char *buf, char *fname, int len)
459 {
460     /* Only GetModuleFileName() will get the long file name path.
461      * GetFullPathName() may still use the short (FAT) name. */
462     DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len);
463 
464     return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL;
465 }
466 
467 /*
468  * Remove the tail from a file or directory name.
469  * Puts a NUL on the last '/' or '\'.
470  */
471     static void
472 remove_tail(char *path)
473 {
474     int		i;
475 
476     for (i = strlen(path) - 1; i > 0; --i)
477 	if (path[i] == '/' || path[i] == '\\')
478 	{
479 	    path[i] = NUL;
480 	    break;
481 	}
482 }
483 
484 
485 char	installdir[MAX_PATH-9];	/* top of the installation dir, where the
486 				   install.exe is located, E.g.:
487 				   "c:\vim\vim60" */
488 int	runtimeidx;		/* index in installdir[] where "vim60" starts */
489 char	*sysdrive;		/* system drive or "c:\" */
490 
491 /*
492  * Setup for using this program.
493  * Sets "installdir[]".
494  */
495     static void
496 do_inits(char **argv)
497 {
498     /* Find out the full path of our executable. */
499     if (my_fullpath(installdir, argv[0], sizeof(installdir)) == NULL)
500     {
501 	printf("ERROR: Cannot get name of executable\n");
502 	myexit(1);
503     }
504     /* remove the tail, the executable name "install.exe" */
505     remove_tail(installdir);
506 
507     /* change to the installdir */
508     mch_chdir(installdir);
509 
510     /* Find the system drive.  Only used for searching the Vim executable, not
511      * very important. */
512     sysdrive = getenv("SYSTEMDRIVE");
513     if (sysdrive == NULL || *sysdrive == NUL)
514 	sysdrive = "C:\\";
515 }
516