blob: 268a91ba44d42f201323b774444f438558d47107 [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
29# ifndef __CYGWIN__
30# include <direct.h>
31# endif
32
33# if defined(_WIN64) || defined(WIN32)
34# define WIN3264
35# include <windows.h>
36# include <shlobj.h>
37# else
38# include <dir.h>
39# include <bios.h>
40# include <dos.h>
41# endif
42#endif
43
44#ifdef UNIX_LINT
45/* Running lint on Unix: Some things are missing. */
46char *searchpath(char *name);
47#endif
48
Bram Moolenaar48e330a2016-02-23 14:53:34 +010049#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000050# include <unistd.h>
51# include <errno.h>
52#endif
53
54#include "version.h"
55
Bram Moolenaar48e330a2016-02-23 14:53:34 +010056#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000057# define vim_mkdir(x, y) mkdir((char *)(x), y)
58#else
59# if defined(WIN3264) && !defined(__BORLANDC__)
60# define vim_mkdir(x, y) _mkdir((char *)(x))
61# else
62# define vim_mkdir(x, y) mkdir((char *)(x))
63# endif
64#endif
Bram Moolenaarab8205e2010-07-07 15:14:03 +020065
Bram Moolenaar48e330a2016-02-23 14:53:34 +010066#define sleep(n) Sleep((n) * 1000)
Bram Moolenaarab8205e2010-07-07 15:14:03 +020067
Bram Moolenaar071d4272004-06-13 20:20:40 +000068/* ---------------------------------------- */
69
70
71#define BUFSIZE 512 /* long enough to hold a file name path */
72#define NUL 0
73
74#define FAIL 0
75#define OK 1
76
77#ifndef FALSE
78# define FALSE 0
79#endif
80#ifndef TRUE
81# define TRUE 1
82#endif
83
Bram Moolenaar760d14a2010-07-31 22:03:44 +020084/*
85 * Modern way of creating registry entries, also works on 64 bit windows when
86 * compiled as a 32 bit program.
87 */
88# ifndef KEY_WOW64_64KEY
89# define KEY_WOW64_64KEY 0x0100
90# endif
91
Bram Moolenaar071d4272004-06-13 20:20:40 +000092#define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT
93
94int interactive; /* non-zero when running interactively */
95
96/*
97 * Call malloc() and exit when out of memory.
98 */
99 static void *
100alloc(int len)
101{
102 char *s;
103
104 s = malloc(len);
105 if (s == NULL)
106 {
107 printf("ERROR: out of memory\n");
108 exit(1);
109 }
110 return (void *)s;
111}
112
113/*
114 * The toupper() in Bcc 5.5 doesn't work, use our own implementation.
115 */
116 static int
117mytoupper(int c)
118{
119 if (c >= 'a' && c <= 'z')
120 return c - 'a' + 'A';
121 return c;
122}
123
124 static void
125myexit(int n)
126{
127 if (!interactive)
128 {
129 /* Present a prompt, otherwise error messages can't be read. */
130 printf("Press Enter to continue\n");
131 rewind(stdin);
132 (void)getchar();
133 }
134 exit(n);
135}
136
137#ifdef WIN3264
138/* This symbol is not defined in older versions of the SDK or Visual C++ */
139
140#ifndef VER_PLATFORM_WIN32_WINDOWS
141# define VER_PLATFORM_WIN32_WINDOWS 1
142#endif
143
144static DWORD g_PlatformId;
145
146/*
147 * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
148 * VER_PLATFORM_WIN32_WINDOWS (Win95).
149 */
150 static void
151PlatformId(void)
152{
153 static int done = FALSE;
154
155 if (!done)
156 {
157 OSVERSIONINFO ovi;
158
159 ovi.dwOSVersionInfoSize = sizeof(ovi);
160 GetVersionEx(&ovi);
161
162 g_PlatformId = ovi.dwPlatformId;
163 done = TRUE;
164 }
165}
166
167# ifdef __BORLANDC__
168/* Borland defines its own searchpath() in dir.h */
169# include <dir.h>
170# else
171 static char *
172searchpath(char *name)
173{
174 static char widename[2 * BUFSIZE];
175 static char location[2 * BUFSIZE + 2];
176
177 /* There appears to be a bug in FindExecutableA() on Windows NT.
178 * Use FindExecutableW() instead... */
179 PlatformId();
180 if (g_PlatformId == VER_PLATFORM_WIN32_NT)
181 {
182 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
183 (LPWSTR)widename, BUFSIZE);
184 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
185 (LPWSTR)location) > (HINSTANCE)32)
186 {
187 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
188 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
189 return widename;
190 }
191 }
192 else
193 {
194 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"",
195 (LPTSTR)location) > (HINSTANCE)32)
196 return location;
197 }
198 return NULL;
199}
200# endif
201#endif
202
203/*
204 * Call searchpath() and save the result in allocated memory, or return NULL.
205 */
206 static char *
207searchpath_save(char *name)
208{
209 char *p;
210 char *s;
211
212 p = searchpath(name);
213 if (p == NULL)
214 return NULL;
215 s = alloc(strlen(p) + 1);
216 strcpy(s, p);
217 return s;
218}
219
220#ifdef WIN3264
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000221
222#ifndef CSIDL_COMMON_PROGRAMS
223# define CSIDL_COMMON_PROGRAMS 0x0017
224#endif
225#ifndef CSIDL_COMMON_DESKTOPDIRECTORY
226# define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019
227#endif
228
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229/*
230 * Get the path to a requested Windows shell folder.
231 *
232 * Return FAIL on error, OK on success
233 */
234 int
235get_shell_folder_path(
236 char *shell_folder_path,
237 const char *shell_folder_name)
238{
239 /*
240 * The following code was successfully built with make_mvc.mak.
241 * The resulting executable worked on Windows 95, Millennium Edition, and
242 * 2000 Professional. But it was changed after testing...
243 */
244 LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */
245 LPMALLOC pMalloc; /* Pointer to an IMalloc interface */
246 int csidl;
247 int alt_csidl = -1;
248 static int desktop_csidl = -1;
249 static int programs_csidl = -1;
250 int *pcsidl;
251 int r;
252
253 if (strcmp(shell_folder_name, "desktop") == 0)
254 {
255 pcsidl = &desktop_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 csidl = CSIDL_COMMON_DESKTOPDIRECTORY;
257 alt_csidl = CSIDL_DESKTOP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258 }
259 else if (strncmp(shell_folder_name, "Programs", 8) == 0)
260 {
261 pcsidl = &programs_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262 csidl = CSIDL_COMMON_PROGRAMS;
263 alt_csidl = CSIDL_PROGRAMS;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 }
265 else
266 {
267 printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n",
268 shell_folder_name);
269 return FAIL;
270 }
271
272 /* Did this stuff before, use the same ID again. */
273 if (*pcsidl >= 0)
274 {
275 csidl = *pcsidl;
276 alt_csidl = -1;
277 }
278
279retry:
280 /* Initialize pointer to IMalloc interface */
281 if (NOERROR != SHGetMalloc(&pMalloc))
282 {
283 printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n",
284 shell_folder_name);
285 return FAIL;
286 }
287
288 /* Get an ITEMIDLIST corresponding to the folder code */
289 if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl))
290 {
291 if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0,
292 alt_csidl, &pidl))
293 {
294 printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
295 shell_folder_name);
296 return FAIL;
297 }
298 csidl = alt_csidl;
299 alt_csidl = -1;
300 }
301
302 /* Translate that ITEMIDLIST to a string */
303 r = SHGetPathFromIDList(pidl, shell_folder_path);
304
305 /* Free the data associated with pidl */
306 pMalloc->lpVtbl->Free(pMalloc, pidl);
307 /* Release the IMalloc interface */
308 pMalloc->lpVtbl->Release(pMalloc);
309
310 if (!r)
311 {
312 if (alt_csidl >= 0)
313 {
314 /* We probably get here for Windows 95: the "all users"
315 * desktop/start menu entry doesn't exist. */
316 csidl = alt_csidl;
317 alt_csidl = -1;
318 goto retry;
319 }
320 printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
321 shell_folder_name);
322 return FAIL;
323 }
324
325 /* If there is an alternative: verify we can write in this directory.
326 * This should cause a retry when the "all users" directory exists but we
327 * are a normal user and can't write there. */
328 if (alt_csidl >= 0)
329 {
330 char tbuf[BUFSIZE];
331 FILE *fd;
332
333 strcpy(tbuf, shell_folder_path);
334 strcat(tbuf, "\\vim write test");
335 fd = fopen(tbuf, "w");
336 if (fd == NULL)
337 {
338 csidl = alt_csidl;
339 alt_csidl = -1;
340 goto retry;
341 }
342 fclose(fd);
343 unlink(tbuf);
344 }
345
346 /*
347 * Keep the found csidl for next time, so that we don't have to do the
348 * write test every time.
349 */
350 if (*pcsidl < 0)
351 *pcsidl = csidl;
352
353 if (strncmp(shell_folder_name, "Programs\\", 9) == 0)
354 strcat(shell_folder_path, shell_folder_name + 8);
355
356 return OK;
357}
358#endif
359
360/*
361 * List of targets. The first one (index zero) is used for the default path
362 * for the batch files.
363 */
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000364#define TARGET_COUNT 9
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365
366struct
367{
368 char *name; /* Vim exe name (without .exe) */
369 char *batname; /* batch file name */
370 char *lnkname; /* shortcut file name */
371 char *exename; /* exe file name */
372 char *exenamearg; /* exe file name when using exearg */
373 char *exearg; /* argument for vim.exe or gvim.exe */
374 char *oldbat; /* path to existing xxx.bat or NULL */
375 char *oldexe; /* path to existing xxx.exe or NULL */
376 char batpath[BUFSIZE]; /* path of batch file to create; not
377 created when it's empty */
378} targets[TARGET_COUNT] =
379{
380 {"all", "batch files"},
381 {"vim", "vim.bat", "Vim.lnk",
382 "vim.exe", "vim.exe", ""},
383 {"gvim", "gvim.bat", "gVim.lnk",
384 "gvim.exe", "gvim.exe", ""},
385 {"evim", "evim.bat", "gVim Easy.lnk",
386 "evim.exe", "gvim.exe", "-y"},
387 {"view", "view.bat", "Vim Read-only.lnk",
388 "view.exe", "vim.exe", "-R"},
389 {"gview", "gview.bat", "gVim Read-only.lnk",
390 "gview.exe", "gvim.exe", "-R"},
391 {"vimdiff", "vimdiff.bat", "Vim Diff.lnk",
392 "vimdiff.exe","vim.exe", "-d"},
393 {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk",
394 "gvimdiff.exe","gvim.exe", "-d"},
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000395 {"vimtutor","vimtutor.bat", "Vim tutor.lnk",
396 "vimtutor.bat", "vimtutor.bat", ""},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397};
398
399#define ICON_COUNT 3
400char *(icon_names[ICON_COUNT]) =
401 {"gVim " VIM_VERSION_SHORT,
402 "gVim Easy " VIM_VERSION_SHORT,
403 "gVim Read only " VIM_VERSION_SHORT};
404char *(icon_link_names[ICON_COUNT]) =
405 {"gVim " VIM_VERSION_SHORT ".lnk",
406 "gVim Easy " VIM_VERSION_SHORT ".lnk",
407 "gVim Read only " VIM_VERSION_SHORT ".lnk"};
408
Bram Moolenaarab8205e2010-07-07 15:14:03 +0200409/* This is only used for dosinst.c when WIN3264 is defined and for uninstal.c
410 * when not being able to directly access registry entries. */
411#if (defined(DOSINST) && defined(WIN3264)) \
412 || (!defined(DOSINST) && !defined(WIN3264))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413/*
414 * Run an external command and wait for it to finish.
415 */
416 static void
417run_command(char *cmd)
418{
419 char *cmd_path;
420 char cmd_buf[BUFSIZE];
421 char *p;
422
423 /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100424 * executable (start.exe) like in Win9x. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425 cmd_path = searchpath_save("cmd.exe");
426 if (cmd_path != NULL)
427 {
428 /* There is a cmd.exe, so this might be Windows NT. If it is,
429 * we need to call cmd.exe explicitly. If it is a later OS,
430 * calling cmd.exe won't hurt if it is present.
Bram Moolenaar442b4222010-05-24 21:34:22 +0200431 * Also, "start" on NT expects a window title argument.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000432 */
433 /* Replace the slashes with backslashes. */
434 while ((p = strchr(cmd_path, '/')) != NULL)
435 *p = '\\';
Bram Moolenaar442b4222010-05-24 21:34:22 +0200436 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437 free(cmd_path);
438 }
439 else
440 {
441 /* No cmd.exe, just make the call and let the system handle it. */
442 sprintf(cmd_buf, "start /w %s", cmd);
443 }
444 system(cmd_buf);
445}
446#endif
447
448/*
449 * Append a backslash to "name" if there isn't one yet.
450 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100451 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452add_pathsep(char *name)
453{
454 int len = strlen(name);
455
456 if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/')
457 strcat(name, "\\");
458}
459
460/*
461 * The normal chdir() does not change the default drive. This one does.
462 */
463/*ARGSUSED*/
464 int
465change_drive(int drive)
466{
467#ifdef WIN3264
468 char temp[3] = "-:";
469 temp[0] = (char)(drive + 'A' - 1);
470 return !SetCurrentDirectory(temp);
471#else
472# ifndef UNIX_LINT
473 union REGS regs;
474
475 regs.h.ah = 0x0e;
476 regs.h.dl = drive - 1;
477 intdos(&regs, &regs); /* set default drive */
478 regs.h.ah = 0x19;
479 intdos(&regs, &regs); /* get default drive */
480 if (regs.h.al == drive - 1)
481 return 0;
482# endif
483 return -1;
484#endif
485}
486
487/*
488 * Change directory to "path".
489 * Return 0 for success, -1 for failure.
490 */
491 int
492mch_chdir(char *path)
493{
494 if (path[0] == NUL) /* just checking... */
495 return 0;
496 if (path[1] == ':') /* has a drive name */
497 {
498 if (change_drive(mytoupper(path[0]) - 'A' + 1))
499 return -1; /* invalid drive name */
500 path += 2;
501 }
502 if (*path == NUL) /* drive name only */
503 return 0;
504 return chdir(path); /* let the normal chdir() do the rest */
505}
506
507/*
508 * Expand the executable name into a full path name.
509 */
510#if defined(__BORLANDC__) && !defined(WIN3264)
511
512/* Only Borland C++ has this. */
513# define my_fullpath(b, n, l) _fullpath(b, n, l)
514
515#else
516 static char *
517my_fullpath(char *buf, char *fname, int len)
518{
519# ifdef WIN3264
520 /* Only GetModuleFileName() will get the long file name path.
521 * GetFullPathName() may still use the short (FAT) name. */
522 DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len);
523
524 return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL;
525# else
526 char olddir[BUFSIZE];
527 char *p, *q;
528 int c;
529 char *retval = buf;
530
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000531 if (strchr(fname, ':') != NULL) /* already expanded */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 {
533 strncpy(buf, fname, len);
534 }
535 else
536 {
537 *buf = NUL;
538 /*
539 * change to the directory for a moment,
540 * and then do the getwd() (and get back to where we were).
541 * This will get the correct path name with "../" things.
542 */
543 p = strrchr(fname, '/');
544 q = strrchr(fname, '\\');
545 if (q != NULL && (p == NULL || q > p))
546 p = q;
547 q = strrchr(fname, ':');
548 if (q != NULL && (p == NULL || q > p))
549 p = q;
550 if (p != NULL)
551 {
552 if (getcwd(olddir, BUFSIZE) == NULL)
553 {
554 p = NULL; /* can't get current dir: don't chdir */
555 retval = NULL;
556 }
557 else
558 {
559 if (p == fname) /* /fname */
560 q = p + 1; /* -> / */
561 else if (q + 1 == p) /* ... c:\foo */
562 q = p + 1; /* -> c:\ */
563 else /* but c:\foo\bar */
564 q = p; /* -> c:\foo */
565
566 c = *q; /* truncate at start of fname */
567 *q = NUL;
568 if (mch_chdir(fname)) /* change to the directory */
569 retval = NULL;
570 else
571 {
572 fname = q;
573 if (c == '\\') /* if we cut the name at a */
574 fname++; /* '\', don't add it again */
575 }
576 *q = c;
577 }
578 }
579 if (getcwd(buf, len) == NULL)
580 {
581 retval = NULL;
582 *buf = NUL;
583 }
584 /*
585 * Concatenate the file name to the path.
586 */
587 if (strlen(buf) + strlen(fname) >= len - 1)
588 {
589 printf("ERROR: File name too long!\n");
590 myexit(1);
591 }
592 add_pathsep(buf);
593 strcat(buf, fname);
594 if (p)
595 mch_chdir(olddir);
596 }
597
598 /* Replace forward slashes with backslashes, required for the path to a
599 * command. */
600 while ((p = strchr(buf, '/')) != NULL)
601 *p = '\\';
602
603 return retval;
604# endif
605}
606#endif
607
608/*
609 * Remove the tail from a file or directory name.
610 * Puts a NUL on the last '/' or '\'.
611 */
612 static void
613remove_tail(char *path)
614{
615 int i;
616
617 for (i = strlen(path) - 1; i > 0; --i)
618 if (path[i] == '/' || path[i] == '\\')
619 {
620 path[i] = NUL;
621 break;
622 }
623}
624
625
626char installdir[BUFSIZE]; /* top of the installation dir, where the
627 install.exe is located, E.g.:
628 "c:\vim\vim60" */
629int runtimeidx; /* index in installdir[] where "vim60" starts */
630char *sysdrive; /* system drive or "c:\" */
631
632/*
633 * Setup for using this program.
634 * Sets "installdir[]".
635 */
636 static void
637do_inits(char **argv)
638{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 /* Find out the full path of our executable. */
640 if (my_fullpath(installdir, argv[0], BUFSIZE) == NULL)
641 {
642 printf("ERROR: Cannot get name of executable\n");
643 myexit(1);
644 }
645 /* remove the tail, the executable name "install.exe" */
646 remove_tail(installdir);
647
648 /* change to the installdir */
649 mch_chdir(installdir);
650
651 /* Find the system drive. Only used for searching the Vim executable, not
652 * very important. */
653 sysdrive = getenv("SYSTEMDRIVE");
654 if (sysdrive == NULL || *sysdrive == NUL)
655 sysdrive = "C:\\";
656}