/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * dosinst.h: Common code for dosinst.c and uninstal.c */ /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ #if _MSC_VER >= 1400 # define _CRT_SECURE_NO_DEPRECATE # define _CRT_NONSTDC_NO_DEPRECATE #endif #include #include #include #include #include #ifndef UNIX_LINT # include "vimio.h" # include # ifndef __CYGWIN__ # include # endif # if defined(_WIN64) || defined(WIN32) # define WIN3264 # include # include # else # include # include # include # endif #endif #ifdef UNIX_LINT /* Running lint on Unix: Some things are missing. */ char *searchpath(char *name); #endif #if defined(DJGPP) || defined(UNIX_LINT) # include # include #endif #include "version.h" #if defined(DJGPP) || defined(UNIX_LINT) # define vim_mkdir(x, y) mkdir((char *)(x), y) #else # if defined(WIN3264) && !defined(__BORLANDC__) # define vim_mkdir(x, y) _mkdir((char *)(x)) # else # define vim_mkdir(x, y) mkdir((char *)(x)) # endif #endif #ifndef DJGPP # define sleep(n) Sleep((n) * 1000) #endif /* ---------------------------------------- */ #define BUFSIZE 512 /* long enough to hold a file name path */ #define NUL 0 #define FAIL 0 #define OK 1 #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT int interactive; /* non-zero when running interactively */ /* * Call malloc() and exit when out of memory. */ static void * alloc(int len) { char *s; s = malloc(len); if (s == NULL) { printf("ERROR: out of memory\n"); exit(1); } return (void *)s; } /* * The toupper() in Bcc 5.5 doesn't work, use our own implementation. */ static int mytoupper(int c) { if (c >= 'a' && c <= 'z') return c - 'a' + 'A'; return c; } static void myexit(int n) { if (!interactive) { /* Present a prompt, otherwise error messages can't be read. */ printf("Press Enter to continue\n"); rewind(stdin); (void)getchar(); } exit(n); } #ifdef WIN3264 /* This symbol is not defined in older versions of the SDK or Visual C++ */ #ifndef VER_PLATFORM_WIN32_WINDOWS # define VER_PLATFORM_WIN32_WINDOWS 1 #endif static DWORD g_PlatformId; /* * Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or * VER_PLATFORM_WIN32_WINDOWS (Win95). */ static void PlatformId(void) { static int done = FALSE; if (!done) { OSVERSIONINFO ovi; ovi.dwOSVersionInfoSize = sizeof(ovi); GetVersionEx(&ovi); g_PlatformId = ovi.dwPlatformId; done = TRUE; } } # ifdef __BORLANDC__ /* Borland defines its own searchpath() in dir.h */ # include # else static char * searchpath(char *name) { static char widename[2 * BUFSIZE]; static char location[2 * BUFSIZE + 2]; /* There appears to be a bug in FindExecutableA() on Windows NT. * Use FindExecutableW() instead... */ PlatformId(); if (g_PlatformId == VER_PLATFORM_WIN32_NT) { MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, (LPWSTR)widename, BUFSIZE); if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", (LPWSTR)location) > (HINSTANCE)32) { WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); return widename; } } else { if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"", (LPTSTR)location) > (HINSTANCE)32) return location; } return NULL; } # endif #endif /* * Call searchpath() and save the result in allocated memory, or return NULL. */ static char * searchpath_save(char *name) { char *p; char *s; p = searchpath(name); if (p == NULL) return NULL; s = alloc(strlen(p) + 1); strcpy(s, p); return s; } #ifdef WIN3264 #ifndef CSIDL_COMMON_PROGRAMS # define CSIDL_COMMON_PROGRAMS 0x0017 #endif #ifndef CSIDL_COMMON_DESKTOPDIRECTORY # define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019 #endif /* * Get the path to a requested Windows shell folder. * * Return FAIL on error, OK on success */ int get_shell_folder_path( char *shell_folder_path, const char *shell_folder_name) { /* * The following code was successfully built with make_mvc.mak. * The resulting executable worked on Windows 95, Millennium Edition, and * 2000 Professional. But it was changed after testing... */ LPITEMIDLIST pidl = 0; /* Pointer to an Item ID list allocated below */ LPMALLOC pMalloc; /* Pointer to an IMalloc interface */ int csidl; int alt_csidl = -1; static int desktop_csidl = -1; static int programs_csidl = -1; int *pcsidl; int r; if (strcmp(shell_folder_name, "desktop") == 0) { pcsidl = &desktop_csidl; csidl = CSIDL_COMMON_DESKTOPDIRECTORY; alt_csidl = CSIDL_DESKTOP; } else if (strncmp(shell_folder_name, "Programs", 8) == 0) { pcsidl = &programs_csidl; csidl = CSIDL_COMMON_PROGRAMS; alt_csidl = CSIDL_PROGRAMS; } else { printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n", shell_folder_name); return FAIL; } /* Did this stuff before, use the same ID again. */ if (*pcsidl >= 0) { csidl = *pcsidl; alt_csidl = -1; } retry: /* Initialize pointer to IMalloc interface */ if (NOERROR != SHGetMalloc(&pMalloc)) { printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n", shell_folder_name); return FAIL; } /* Get an ITEMIDLIST corresponding to the folder code */ if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl)) { if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0, alt_csidl, &pidl)) { printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n", shell_folder_name); return FAIL; } csidl = alt_csidl; alt_csidl = -1; } /* Translate that ITEMIDLIST to a string */ r = SHGetPathFromIDList(pidl, shell_folder_path); /* Free the data associated with pidl */ pMalloc->lpVtbl->Free(pMalloc, pidl); /* Release the IMalloc interface */ pMalloc->lpVtbl->Release(pMalloc); if (!r) { if (alt_csidl >= 0) { /* We probably get here for Windows 95: the "all users" * desktop/start menu entry doesn't exist. */ csidl = alt_csidl; alt_csidl = -1; goto retry; } printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n", shell_folder_name); return FAIL; } /* If there is an alternative: verify we can write in this directory. * This should cause a retry when the "all users" directory exists but we * are a normal user and can't write there. */ if (alt_csidl >= 0) { char tbuf[BUFSIZE]; FILE *fd; strcpy(tbuf, shell_folder_path); strcat(tbuf, "\\vim write test"); fd = fopen(tbuf, "w"); if (fd == NULL) { csidl = alt_csidl; alt_csidl = -1; goto retry; } fclose(fd); unlink(tbuf); } /* * Keep the found csidl for next time, so that we don't have to do the * write test every time. */ if (*pcsidl < 0) *pcsidl = csidl; if (strncmp(shell_folder_name, "Programs\\", 9) == 0) strcat(shell_folder_path, shell_folder_name + 8); return OK; } #endif /* * List of targets. The first one (index zero) is used for the default path * for the batch files. */ #define TARGET_COUNT 9 struct { char *name; /* Vim exe name (without .exe) */ char *batname; /* batch file name */ char *lnkname; /* shortcut file name */ char *exename; /* exe file name */ char *exenamearg; /* exe file name when using exearg */ char *exearg; /* argument for vim.exe or gvim.exe */ char *oldbat; /* path to existing xxx.bat or NULL */ char *oldexe; /* path to existing xxx.exe or NULL */ char batpath[BUFSIZE]; /* path of batch file to create; not created when it's empty */ } targets[TARGET_COUNT] = { {"all", "batch files"}, {"vim", "vim.bat", "Vim.lnk", "vim.exe", "vim.exe", ""}, {"gvim", "gvim.bat", "gVim.lnk", "gvim.exe", "gvim.exe", ""}, {"evim", "evim.bat", "gVim Easy.lnk", "evim.exe", "gvim.exe", "-y"}, {"view", "view.bat", "Vim Read-only.lnk", "view.exe", "vim.exe", "-R"}, {"gview", "gview.bat", "gVim Read-only.lnk", "gview.exe", "gvim.exe", "-R"}, {"vimdiff", "vimdiff.bat", "Vim Diff.lnk", "vimdiff.exe","vim.exe", "-d"}, {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk", "gvimdiff.exe","gvim.exe", "-d"}, {"vimtutor","vimtutor.bat", "Vim tutor.lnk", "vimtutor.bat", "vimtutor.bat", ""}, }; #define ICON_COUNT 3 char *(icon_names[ICON_COUNT]) = {"gVim " VIM_VERSION_SHORT, "gVim Easy " VIM_VERSION_SHORT, "gVim Read only " VIM_VERSION_SHORT}; char *(icon_link_names[ICON_COUNT]) = {"gVim " VIM_VERSION_SHORT ".lnk", "gVim Easy " VIM_VERSION_SHORT ".lnk", "gVim Read only " VIM_VERSION_SHORT ".lnk"}; /* This is only used for dosinst.c when WIN3264 is defined and for uninstal.c * when not being able to directly access registry entries. */ #if (defined(DOSINST) && defined(WIN3264)) \ || (!defined(DOSINST) && !defined(WIN3264)) /* * Run an external command and wait for it to finish. */ static void run_command(char *cmd) { char *cmd_path; char cmd_buf[BUFSIZE]; char *p; /* On WinNT, 'start' is a shell built-in for cmd.exe rather than an * executable (start.exe) like in Win9x. DJGPP, being a DOS program, * is given the COMSPEC command.com by WinNT, so we have to find * cmd.exe manually and use it. */ cmd_path = searchpath_save("cmd.exe"); if (cmd_path != NULL) { /* There is a cmd.exe, so this might be Windows NT. If it is, * we need to call cmd.exe explicitly. If it is a later OS, * calling cmd.exe won't hurt if it is present. * Also, "start" on NT expects a window title argument. */ /* Replace the slashes with backslashes. */ while ((p = strchr(cmd_path, '/')) != NULL) *p = '\\'; sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd); free(cmd_path); } else { /* No cmd.exe, just make the call and let the system handle it. */ sprintf(cmd_buf, "start /w %s", cmd); } system(cmd_buf); } #endif /* * Append a backslash to "name" if there isn't one yet. */ static void add_pathsep(char *name) { int len = strlen(name); if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/') strcat(name, "\\"); } /* * The normal chdir() does not change the default drive. This one does. */ /*ARGSUSED*/ int change_drive(int drive) { #ifdef WIN3264 char temp[3] = "-:"; temp[0] = (char)(drive + 'A' - 1); return !SetCurrentDirectory(temp); #else # ifndef UNIX_LINT union REGS regs; regs.h.ah = 0x0e; regs.h.dl = drive - 1; intdos(®s, ®s); /* set default drive */ regs.h.ah = 0x19; intdos(®s, ®s); /* get default drive */ if (regs.h.al == drive - 1) return 0; # endif return -1; #endif } /* * Change directory to "path". * Return 0 for success, -1 for failure. */ int mch_chdir(char *path) { if (path[0] == NUL) /* just checking... */ return 0; if (path[1] == ':') /* has a drive name */ { if (change_drive(mytoupper(path[0]) - 'A' + 1)) return -1; /* invalid drive name */ path += 2; } if (*path == NUL) /* drive name only */ return 0; return chdir(path); /* let the normal chdir() do the rest */ } /* * Expand the executable name into a full path name. */ #if defined(__BORLANDC__) && !defined(WIN3264) /* Only Borland C++ has this. */ # define my_fullpath(b, n, l) _fullpath(b, n, l) #else static char * my_fullpath(char *buf, char *fname, int len) { # ifdef WIN3264 /* Only GetModuleFileName() will get the long file name path. * GetFullPathName() may still use the short (FAT) name. */ DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len); return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL; # else char olddir[BUFSIZE]; char *p, *q; int c; char *retval = buf; if (strchr(fname, ':') != NULL) /* already expanded */ { strncpy(buf, fname, len); } else { *buf = NUL; /* * change to the directory for a moment, * and then do the getwd() (and get back to where we were). * This will get the correct path name with "../" things. */ p = strrchr(fname, '/'); q = strrchr(fname, '\\'); if (q != NULL && (p == NULL || q > p)) p = q; q = strrchr(fname, ':'); if (q != NULL && (p == NULL || q > p)) p = q; if (p != NULL) { if (getcwd(olddir, BUFSIZE) == NULL) { p = NULL; /* can't get current dir: don't chdir */ retval = NULL; } else { if (p == fname) /* /fname */ q = p + 1; /* -> / */ else if (q + 1 == p) /* ... c:\foo */ q = p + 1; /* -> c:\ */ else /* but c:\foo\bar */ q = p; /* -> c:\foo */ c = *q; /* truncate at start of fname */ *q = NUL; if (mch_chdir(fname)) /* change to the directory */ retval = NULL; else { fname = q; if (c == '\\') /* if we cut the name at a */ fname++; /* '\', don't add it again */ } *q = c; } } if (getcwd(buf, len) == NULL) { retval = NULL; *buf = NUL; } /* * Concatenate the file name to the path. */ if (strlen(buf) + strlen(fname) >= len - 1) { printf("ERROR: File name too long!\n"); myexit(1); } add_pathsep(buf); strcat(buf, fname); if (p) mch_chdir(olddir); } /* Replace forward slashes with backslashes, required for the path to a * command. */ while ((p = strchr(buf, '/')) != NULL) *p = '\\'; return retval; # endif } #endif /* * Remove the tail from a file or directory name. * Puts a NUL on the last '/' or '\'. */ static void remove_tail(char *path) { int i; for (i = strlen(path) - 1; i > 0; --i) if (path[i] == '/' || path[i] == '\\') { path[i] = NUL; break; } } char installdir[BUFSIZE]; /* top of the installation dir, where the install.exe is located, E.g.: "c:\vim\vim60" */ int runtimeidx; /* index in installdir[] where "vim60" starts */ char *sysdrive; /* system drive or "c:\" */ /* * Setup for using this program. * Sets "installdir[]". */ static void do_inits(char **argv) { #ifdef DJGPP /* * Use Long File Names by default, if $LFN not set. */ if (getenv("LFN") == NULL) putenv("LFN=y"); #endif /* Find out the full path of our executable. */ if (my_fullpath(installdir, argv[0], BUFSIZE) == NULL) { printf("ERROR: Cannot get name of executable\n"); myexit(1); } /* remove the tail, the executable name "install.exe" */ remove_tail(installdir); /* change to the installdir */ mch_chdir(installdir); /* Find the system drive. Only used for searching the Vim executable, not * very important. */ sysdrive = getenv("SYSTEMDRIVE"); if (sysdrive == NULL || *sysdrive == NUL) sysdrive = "C:\\"; }