blob: a83bb0839ff19d8dd595dcdaace4e79f1990b769 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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/*
Bram Moolenaar30e8e732019-09-27 13:08:36 +020010 * dosinst.h: Common code for dosinst.c and uninstall.c
Bram Moolenaar071d4272004-06-13 20:20:40 +000011 */
Bram Moolenaar362e1a32006-03-06 23:29:24 +000012
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
Bram Moolenaar071d4272004-06-13 20:20:40 +000019#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
Bram Moolenaar362e1a32006-03-06 23:29:24 +000026# include "vimio.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +000027# include <ctype.h>
28
Bram Moolenaar6199d432017-10-14 19:05:44 +020029# include <direct.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030
Bram Moolenaar6199d432017-10-14 19:05:44 +020031# include <windows.h>
32# include <shlobj.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000033#endif
34
35#ifdef UNIX_LINT
36/* Running lint on Unix: Some things are missing. */
37char *searchpath(char *name);
38#endif
39
Bram Moolenaar48e330a2016-02-23 14:53:34 +010040#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000041# include <unistd.h>
42# include <errno.h>
43#endif
44
45#include "version.h"
46
Bram Moolenaar48e330a2016-02-23 14:53:34 +010047#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000048# define vim_mkdir(x, y) mkdir((char *)(x), y)
49#else
Bram Moolenaareae1b912019-05-09 15:12:55 +020050# define vim_mkdir(x, y) _mkdir((char *)(x))
Bram Moolenaar071d4272004-06-13 20:20:40 +000051#endif
Bram Moolenaarab8205e2010-07-07 15:14:03 +020052
Bram Moolenaar48e330a2016-02-23 14:53:34 +010053#define sleep(n) Sleep((n) * 1000)
Bram Moolenaarab8205e2010-07-07 15:14:03 +020054
Bram Moolenaar071d4272004-06-13 20:20:40 +000055/* ---------------------------------------- */
56
57
Bram Moolenaare4963c52019-02-22 19:41:08 +010058#define BUFSIZE (MAX_PATH*2) /* long enough to hold a file name path */
Bram Moolenaar071d4272004-06-13 20:20:40 +000059#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
Bram Moolenaar760d14a2010-07-31 22:03:44 +020071/*
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
Bram Moolenaar6199d432017-10-14 19:05:44 +020078# ifndef KEY_WOW64_32KEY
79# define KEY_WOW64_32KEY 0x0200
80# endif
Bram Moolenaar760d14a2010-07-31 22:03:44 +020081
Bram Moolenaar071d4272004-06-13 20:20:40 +000082#define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT
83
84int interactive; /* non-zero when running interactively */
85
86/*
87 * Call malloc() and exit when out of memory.
88 */
89 static void *
90alloc(int len)
91{
Bram Moolenaare4963c52019-02-22 19:41:08 +010092 void *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
Bram Moolenaare4963c52019-02-22 19:41:08 +010094 p = malloc(len);
95 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000096 {
97 printf("ERROR: out of memory\n");
98 exit(1);
99 }
Bram Moolenaare4963c52019-02-22 19:41:08 +0100100 return p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101}
102
103/*
104 * The toupper() in Bcc 5.5 doesn't work, use our own implementation.
105 */
106 static int
107mytoupper(int c)
108{
109 if (c >= 'a' && c <= 'z')
110 return c - 'a' + 'A';
111 return c;
112}
113
114 static void
115myexit(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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127
Bram Moolenaar6199d432017-10-14 19:05:44 +0200128typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129/*
Bram Moolenaar6199d432017-10-14 19:05:44 +0200130 * Check if this is a 64-bit OS.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131 */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200132 static BOOL
133is_64bit_os(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134{
Bram Moolenaar6199d432017-10-14 19:05:44 +0200135#ifdef _WIN64
136 return TRUE;
137#else
138 BOOL bIsWow64 = FALSE;
139 LPFN_ISWOW64PROCESS pIsWow64Process;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000140
Bram Moolenaar6199d432017-10-14 19:05:44 +0200141 pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
142 GetModuleHandle("kernel32"), "IsWow64Process");
143 if (pIsWow64Process != NULL)
144 pIsWow64Process(GetCurrentProcess(), &bIsWow64);
145 return bIsWow64;
146#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147}
148
Bram Moolenaar071d4272004-06-13 20:20:40 +0000149 static char *
150searchpath(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... */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200157 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
158 (LPWSTR)widename, BUFSIZE);
159 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
160 (LPWSTR)location) > (HINSTANCE)32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000161 {
Bram Moolenaar6199d432017-10-14 19:05:44 +0200162 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
163 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
164 return widename;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165 }
166 return NULL;
167}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168
169/*
170 * Call searchpath() and save the result in allocated memory, or return NULL.
171 */
172 static char *
173searchpath_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
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000186
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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194/*
195 * Get the path to a requested Windows shell folder.
196 *
197 * Return FAIL on error, OK on success
198 */
199 int
200get_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;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221 csidl = CSIDL_COMMON_DESKTOPDIRECTORY;
222 alt_csidl = CSIDL_DESKTOP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 }
224 else if (strncmp(shell_folder_name, "Programs", 8) == 0)
225 {
226 pcsidl = &programs_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 csidl = CSIDL_COMMON_PROGRAMS;
228 alt_csidl = CSIDL_PROGRAMS;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 }
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
244retry:
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}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323
324/*
325 * List of targets. The first one (index zero) is used for the default path
326 * for the batch files.
327 */
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000328#define TARGET_COUNT 9
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329
330struct
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"},
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000359 {"vimtutor","vimtutor.bat", "Vim tutor.lnk",
360 "vimtutor.bat", "vimtutor.bat", ""},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000361};
362
363#define ICON_COUNT 3
364char *(icon_names[ICON_COUNT]) =
365 {"gVim " VIM_VERSION_SHORT,
366 "gVim Easy " VIM_VERSION_SHORT,
367 "gVim Read only " VIM_VERSION_SHORT};
368char *(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
Bram Moolenaar6199d432017-10-14 19:05:44 +0200373/* This is only used for dosinst.c. */
374#if defined(DOSINST)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375/*
376 * Run an external command and wait for it to finish.
377 */
378 static void
379run_command(char *cmd)
380{
381 char *cmd_path;
Bram Moolenaarbbd854d2019-02-18 22:19:33 +0100382 char cmd_buf[BUFSIZE * 2 + 35];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383 char *p;
384
385 /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100386 * executable (start.exe) like in Win9x. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 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.
Bram Moolenaar442b4222010-05-24 21:34:22 +0200393 * Also, "start" on NT expects a window title argument.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 */
395 /* Replace the slashes with backslashes. */
396 while ((p = strchr(cmd_path, '/')) != NULL)
397 *p = '\\';
Bram Moolenaar442b4222010-05-24 21:34:22 +0200398 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399 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 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100413 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414add_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
427change_drive(int drive)
428{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429 char temp[3] = "-:";
430 temp[0] = (char)(drive + 'A' - 1);
431 return !SetCurrentDirectory(temp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000432}
433
434/*
435 * Change directory to "path".
436 * Return 0 for success, -1 for failure.
437 */
438 int
439mch_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 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457 static char *
458my_fullpath(char *buf, char *fname, int len)
459{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460 /* 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;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000466
467/*
468 * Remove the tail from a file or directory name.
469 * Puts a NUL on the last '/' or '\'.
470 */
471 static void
472remove_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
Bram Moolenaarbbd854d2019-02-18 22:19:33 +0100485char installdir[MAX_PATH-9]; /* top of the installation dir, where the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486 install.exe is located, E.g.:
487 "c:\vim\vim60" */
488int runtimeidx; /* index in installdir[] where "vim60" starts */
489char *sysdrive; /* system drive or "c:\" */
490
491/*
492 * Setup for using this program.
493 * Sets "installdir[]".
494 */
495 static void
496do_inits(char **argv)
497{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 /* Find out the full path of our executable. */
Bram Moolenaare4963c52019-02-22 19:41:08 +0100499 if (my_fullpath(installdir, argv[0], sizeof(installdir)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 {
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}