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