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