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