blob: 7f30e90f4b4c0889ba488f4490752ab22a66b320 [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/*
10 * dosinst.h: Common code for dosinst.c and uninstal.c
11 */
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 Moolenaar6199d432017-10-14 19:05:44 +020050# ifndef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +000051# define vim_mkdir(x, y) _mkdir((char *)(x))
52# else
53# define vim_mkdir(x, y) mkdir((char *)(x))
54# endif
55#endif
Bram Moolenaarab8205e2010-07-07 15:14:03 +020056
Bram Moolenaar48e330a2016-02-23 14:53:34 +010057#define sleep(n) Sleep((n) * 1000)
Bram Moolenaarab8205e2010-07-07 15:14:03 +020058
Bram Moolenaar071d4272004-06-13 20:20:40 +000059/* ---------------------------------------- */
60
61
Bram Moolenaare4963c52019-02-22 19:41:08 +010062#define BUFSIZE (MAX_PATH*2) /* long enough to hold a file name path */
Bram Moolenaar071d4272004-06-13 20:20:40 +000063#define NUL 0
64
65#define FAIL 0
66#define OK 1
67
68#ifndef FALSE
69# define FALSE 0
70#endif
71#ifndef TRUE
72# define TRUE 1
73#endif
74
Bram Moolenaar760d14a2010-07-31 22:03:44 +020075/*
76 * Modern way of creating registry entries, also works on 64 bit windows when
77 * compiled as a 32 bit program.
78 */
79# ifndef KEY_WOW64_64KEY
80# define KEY_WOW64_64KEY 0x0100
81# endif
Bram Moolenaar6199d432017-10-14 19:05:44 +020082# ifndef KEY_WOW64_32KEY
83# define KEY_WOW64_32KEY 0x0200
84# endif
Bram Moolenaar760d14a2010-07-31 22:03:44 +020085
Bram Moolenaar071d4272004-06-13 20:20:40 +000086#define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT
87
88int interactive; /* non-zero when running interactively */
89
90/*
91 * Call malloc() and exit when out of memory.
92 */
93 static void *
94alloc(int len)
95{
Bram Moolenaare4963c52019-02-22 19:41:08 +010096 void *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +000097
Bram Moolenaare4963c52019-02-22 19:41:08 +010098 p = malloc(len);
99 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100 {
101 printf("ERROR: out of memory\n");
102 exit(1);
103 }
Bram Moolenaare4963c52019-02-22 19:41:08 +0100104 return p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105}
106
107/*
108 * The toupper() in Bcc 5.5 doesn't work, use our own implementation.
109 */
110 static int
111mytoupper(int c)
112{
113 if (c >= 'a' && c <= 'z')
114 return c - 'a' + 'A';
115 return c;
116}
117
118 static void
119myexit(int n)
120{
121 if (!interactive)
122 {
123 /* Present a prompt, otherwise error messages can't be read. */
124 printf("Press Enter to continue\n");
125 rewind(stdin);
126 (void)getchar();
127 }
128 exit(n);
129}
130
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131
Bram Moolenaar6199d432017-10-14 19:05:44 +0200132typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133/*
Bram Moolenaar6199d432017-10-14 19:05:44 +0200134 * Check if this is a 64-bit OS.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135 */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200136 static BOOL
137is_64bit_os(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000138{
Bram Moolenaar6199d432017-10-14 19:05:44 +0200139#ifdef _WIN64
140 return TRUE;
141#else
142 BOOL bIsWow64 = FALSE;
143 LPFN_ISWOW64PROCESS pIsWow64Process;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144
Bram Moolenaar6199d432017-10-14 19:05:44 +0200145 pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
146 GetModuleHandle("kernel32"), "IsWow64Process");
147 if (pIsWow64Process != NULL)
148 pIsWow64Process(GetCurrentProcess(), &bIsWow64);
149 return bIsWow64;
150#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000151}
152
Bram Moolenaar6199d432017-10-14 19:05:44 +0200153#ifdef __BORLANDC__
Bram Moolenaar071d4272004-06-13 20:20:40 +0000154/* Borland defines its own searchpath() in dir.h */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200155# include <dir.h>
156#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 static char *
158searchpath(char *name)
159{
160 static char widename[2 * BUFSIZE];
161 static char location[2 * BUFSIZE + 2];
162
163 /* There appears to be a bug in FindExecutableA() on Windows NT.
164 * Use FindExecutableW() instead... */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200165 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
166 (LPWSTR)widename, BUFSIZE);
167 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
168 (LPWSTR)location) > (HINSTANCE)32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169 {
Bram Moolenaar6199d432017-10-14 19:05:44 +0200170 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
171 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
172 return widename;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173 }
174 return NULL;
175}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176#endif
177
178/*
179 * Call searchpath() and save the result in allocated memory, or return NULL.
180 */
181 static char *
182searchpath_save(char *name)
183{
184 char *p;
185 char *s;
186
187 p = searchpath(name);
188 if (p == NULL)
189 return NULL;
190 s = alloc(strlen(p) + 1);
191 strcpy(s, p);
192 return s;
193}
194
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000195
196#ifndef CSIDL_COMMON_PROGRAMS
197# define CSIDL_COMMON_PROGRAMS 0x0017
198#endif
199#ifndef CSIDL_COMMON_DESKTOPDIRECTORY
200# define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019
201#endif
202
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203/*
204 * Get the path to a requested Windows shell folder.
205 *
206 * Return FAIL on error, OK on success
207 */
208 int
209get_shell_folder_path(
210 char *shell_folder_path,
211 const char *shell_folder_name)
212{
213 /*
214 * The following code was successfully built with make_mvc.mak.
215 * The resulting executable worked on Windows 95, Millennium Edition, and
216 * 2000 Professional. But it was changed after testing...
217 */
218 LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */
219 LPMALLOC pMalloc; /* Pointer to an IMalloc interface */
220 int csidl;
221 int alt_csidl = -1;
222 static int desktop_csidl = -1;
223 static int programs_csidl = -1;
224 int *pcsidl;
225 int r;
226
227 if (strcmp(shell_folder_name, "desktop") == 0)
228 {
229 pcsidl = &desktop_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230 csidl = CSIDL_COMMON_DESKTOPDIRECTORY;
231 alt_csidl = CSIDL_DESKTOP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 }
233 else if (strncmp(shell_folder_name, "Programs", 8) == 0)
234 {
235 pcsidl = &programs_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 csidl = CSIDL_COMMON_PROGRAMS;
237 alt_csidl = CSIDL_PROGRAMS;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238 }
239 else
240 {
241 printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n",
242 shell_folder_name);
243 return FAIL;
244 }
245
246 /* Did this stuff before, use the same ID again. */
247 if (*pcsidl >= 0)
248 {
249 csidl = *pcsidl;
250 alt_csidl = -1;
251 }
252
253retry:
254 /* Initialize pointer to IMalloc interface */
255 if (NOERROR != SHGetMalloc(&pMalloc))
256 {
257 printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n",
258 shell_folder_name);
259 return FAIL;
260 }
261
262 /* Get an ITEMIDLIST corresponding to the folder code */
263 if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl))
264 {
265 if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0,
266 alt_csidl, &pidl))
267 {
268 printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
269 shell_folder_name);
270 return FAIL;
271 }
272 csidl = alt_csidl;
273 alt_csidl = -1;
274 }
275
276 /* Translate that ITEMIDLIST to a string */
277 r = SHGetPathFromIDList(pidl, shell_folder_path);
278
279 /* Free the data associated with pidl */
280 pMalloc->lpVtbl->Free(pMalloc, pidl);
281 /* Release the IMalloc interface */
282 pMalloc->lpVtbl->Release(pMalloc);
283
284 if (!r)
285 {
286 if (alt_csidl >= 0)
287 {
288 /* We probably get here for Windows 95: the "all users"
289 * desktop/start menu entry doesn't exist. */
290 csidl = alt_csidl;
291 alt_csidl = -1;
292 goto retry;
293 }
294 printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
295 shell_folder_name);
296 return FAIL;
297 }
298
299 /* If there is an alternative: verify we can write in this directory.
300 * This should cause a retry when the "all users" directory exists but we
301 * are a normal user and can't write there. */
302 if (alt_csidl >= 0)
303 {
304 char tbuf[BUFSIZE];
305 FILE *fd;
306
307 strcpy(tbuf, shell_folder_path);
308 strcat(tbuf, "\\vim write test");
309 fd = fopen(tbuf, "w");
310 if (fd == NULL)
311 {
312 csidl = alt_csidl;
313 alt_csidl = -1;
314 goto retry;
315 }
316 fclose(fd);
317 unlink(tbuf);
318 }
319
320 /*
321 * Keep the found csidl for next time, so that we don't have to do the
322 * write test every time.
323 */
324 if (*pcsidl < 0)
325 *pcsidl = csidl;
326
327 if (strncmp(shell_folder_name, "Programs\\", 9) == 0)
328 strcat(shell_folder_path, shell_folder_name + 8);
329
330 return OK;
331}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000332
333/*
334 * List of targets. The first one (index zero) is used for the default path
335 * for the batch files.
336 */
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000337#define TARGET_COUNT 9
Bram Moolenaar071d4272004-06-13 20:20:40 +0000338
339struct
340{
341 char *name; /* Vim exe name (without .exe) */
342 char *batname; /* batch file name */
343 char *lnkname; /* shortcut file name */
344 char *exename; /* exe file name */
345 char *exenamearg; /* exe file name when using exearg */
346 char *exearg; /* argument for vim.exe or gvim.exe */
347 char *oldbat; /* path to existing xxx.bat or NULL */
348 char *oldexe; /* path to existing xxx.exe or NULL */
349 char batpath[BUFSIZE]; /* path of batch file to create; not
350 created when it's empty */
351} targets[TARGET_COUNT] =
352{
353 {"all", "batch files"},
354 {"vim", "vim.bat", "Vim.lnk",
355 "vim.exe", "vim.exe", ""},
356 {"gvim", "gvim.bat", "gVim.lnk",
357 "gvim.exe", "gvim.exe", ""},
358 {"evim", "evim.bat", "gVim Easy.lnk",
359 "evim.exe", "gvim.exe", "-y"},
360 {"view", "view.bat", "Vim Read-only.lnk",
361 "view.exe", "vim.exe", "-R"},
362 {"gview", "gview.bat", "gVim Read-only.lnk",
363 "gview.exe", "gvim.exe", "-R"},
364 {"vimdiff", "vimdiff.bat", "Vim Diff.lnk",
365 "vimdiff.exe","vim.exe", "-d"},
366 {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk",
367 "gvimdiff.exe","gvim.exe", "-d"},
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000368 {"vimtutor","vimtutor.bat", "Vim tutor.lnk",
369 "vimtutor.bat", "vimtutor.bat", ""},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370};
371
372#define ICON_COUNT 3
373char *(icon_names[ICON_COUNT]) =
374 {"gVim " VIM_VERSION_SHORT,
375 "gVim Easy " VIM_VERSION_SHORT,
376 "gVim Read only " VIM_VERSION_SHORT};
377char *(icon_link_names[ICON_COUNT]) =
378 {"gVim " VIM_VERSION_SHORT ".lnk",
379 "gVim Easy " VIM_VERSION_SHORT ".lnk",
380 "gVim Read only " VIM_VERSION_SHORT ".lnk"};
381
Bram Moolenaar6199d432017-10-14 19:05:44 +0200382/* This is only used for dosinst.c. */
383#if defined(DOSINST)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384/*
385 * Run an external command and wait for it to finish.
386 */
387 static void
388run_command(char *cmd)
389{
390 char *cmd_path;
Bram Moolenaarbbd854d2019-02-18 22:19:33 +0100391 char cmd_buf[BUFSIZE * 2 + 35];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392 char *p;
393
394 /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100395 * executable (start.exe) like in Win9x. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396 cmd_path = searchpath_save("cmd.exe");
397 if (cmd_path != NULL)
398 {
399 /* There is a cmd.exe, so this might be Windows NT. If it is,
400 * we need to call cmd.exe explicitly. If it is a later OS,
401 * calling cmd.exe won't hurt if it is present.
Bram Moolenaar442b4222010-05-24 21:34:22 +0200402 * Also, "start" on NT expects a window title argument.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 */
404 /* Replace the slashes with backslashes. */
405 while ((p = strchr(cmd_path, '/')) != NULL)
406 *p = '\\';
Bram Moolenaar442b4222010-05-24 21:34:22 +0200407 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 free(cmd_path);
409 }
410 else
411 {
412 /* No cmd.exe, just make the call and let the system handle it. */
413 sprintf(cmd_buf, "start /w %s", cmd);
414 }
415 system(cmd_buf);
416}
417#endif
418
419/*
420 * Append a backslash to "name" if there isn't one yet.
421 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100422 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423add_pathsep(char *name)
424{
425 int len = strlen(name);
426
427 if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/')
428 strcat(name, "\\");
429}
430
431/*
432 * The normal chdir() does not change the default drive. This one does.
433 */
434/*ARGSUSED*/
435 int
436change_drive(int drive)
437{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000438 char temp[3] = "-:";
439 temp[0] = (char)(drive + 'A' - 1);
440 return !SetCurrentDirectory(temp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441}
442
443/*
444 * Change directory to "path".
445 * Return 0 for success, -1 for failure.
446 */
447 int
448mch_chdir(char *path)
449{
450 if (path[0] == NUL) /* just checking... */
451 return 0;
452 if (path[1] == ':') /* has a drive name */
453 {
454 if (change_drive(mytoupper(path[0]) - 'A' + 1))
455 return -1; /* invalid drive name */
456 path += 2;
457 }
458 if (*path == NUL) /* drive name only */
459 return 0;
460 return chdir(path); /* let the normal chdir() do the rest */
461}
462
463/*
464 * Expand the executable name into a full path name.
465 */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200466#if defined(__BORLANDC__)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467
468/* Only Borland C++ has this. */
469# define my_fullpath(b, n, l) _fullpath(b, n, l)
470
471#else
472 static char *
473my_fullpath(char *buf, char *fname, int len)
474{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 /* Only GetModuleFileName() will get the long file name path.
476 * GetFullPathName() may still use the short (FAT) name. */
477 DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len);
478
479 return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480}
481#endif
482
483/*
484 * Remove the tail from a file or directory name.
485 * Puts a NUL on the last '/' or '\'.
486 */
487 static void
488remove_tail(char *path)
489{
490 int i;
491
492 for (i = strlen(path) - 1; i > 0; --i)
493 if (path[i] == '/' || path[i] == '\\')
494 {
495 path[i] = NUL;
496 break;
497 }
498}
499
500
Bram Moolenaarbbd854d2019-02-18 22:19:33 +0100501char installdir[MAX_PATH-9]; /* top of the installation dir, where the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 install.exe is located, E.g.:
503 "c:\vim\vim60" */
504int runtimeidx; /* index in installdir[] where "vim60" starts */
505char *sysdrive; /* system drive or "c:\" */
506
507/*
508 * Setup for using this program.
509 * Sets "installdir[]".
510 */
511 static void
512do_inits(char **argv)
513{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000514 /* Find out the full path of our executable. */
Bram Moolenaare4963c52019-02-22 19:41:08 +0100515 if (my_fullpath(installdir, argv[0], sizeof(installdir)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 {
517 printf("ERROR: Cannot get name of executable\n");
518 myexit(1);
519 }
520 /* remove the tail, the executable name "install.exe" */
521 remove_tail(installdir);
522
523 /* change to the installdir */
524 mch_chdir(installdir);
525
526 /* Find the system drive. Only used for searching the Vim executable, not
527 * very important. */
528 sysdrive = getenv("SYSTEMDRIVE");
529 if (sysdrive == NULL || *sysdrive == NUL)
530 sysdrive = "C:\\";
531}