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