blob: a849dad9073e563fbf07c64f98d8276d83ca49c7 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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/*
Bram Moolenaar30e8e732019-09-27 13:08:36 +020010 * dosinst.h: Common code for dosinst.c and uninstall.c
Bram Moolenaar071d4272004-06-13 20:20:40 +000011 */
Bram Moolenaar362e1a32006-03-06 23:29:24 +000012
Bram Moolenaar071d4272004-06-13 20:20:40 +000013#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/stat.h>
17#include <fcntl.h>
18
19#ifndef UNIX_LINT
Bram Moolenaar2f189752020-02-12 21:15:43 +010020# include <io.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000021# include <ctype.h>
22
Bram Moolenaar6199d432017-10-14 19:05:44 +020023# include <direct.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000024
Bram Moolenaar6199d432017-10-14 19:05:44 +020025# include <windows.h>
26# include <shlobj.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000027#endif
28
29#ifdef UNIX_LINT
Bram Moolenaar9bf703d2019-11-30 19:44:38 +010030// Running lint on Unix: Some things are missing.
Bram Moolenaar071d4272004-06-13 20:20:40 +000031char *searchpath(char *name);
32#endif
33
Bram Moolenaar48e330a2016-02-23 14:53:34 +010034#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000035# include <unistd.h>
36# include <errno.h>
37#endif
38
39#include "version.h"
40
Bram Moolenaar48e330a2016-02-23 14:53:34 +010041#if defined(UNIX_LINT)
Bram Moolenaar071d4272004-06-13 20:20:40 +000042# define vim_mkdir(x, y) mkdir((char *)(x), y)
43#else
Bram Moolenaareae1b912019-05-09 15:12:55 +020044# define vim_mkdir(x, y) _mkdir((char *)(x))
Bram Moolenaar071d4272004-06-13 20:20:40 +000045#endif
Bram Moolenaarab8205e2010-07-07 15:14:03 +020046
Bram Moolenaar48e330a2016-02-23 14:53:34 +010047#define sleep(n) Sleep((n) * 1000)
Bram Moolenaarab8205e2010-07-07 15:14:03 +020048
Bram Moolenaar9bf703d2019-11-30 19:44:38 +010049// ----------------------------------------
Bram Moolenaar071d4272004-06-13 20:20:40 +000050
51
Bram Moolenaar9bf703d2019-11-30 19:44:38 +010052#define BUFSIZE (MAX_PATH*2) // long enough to hold a file name path
Bram Moolenaar071d4272004-06-13 20:20:40 +000053#define NUL 0
54
55#define FAIL 0
56#define OK 1
57
58#ifndef FALSE
59# define FALSE 0
60#endif
61#ifndef TRUE
62# define TRUE 1
63#endif
64
Bram Moolenaar760d14a2010-07-31 22:03:44 +020065/*
66 * Modern way of creating registry entries, also works on 64 bit windows when
67 * compiled as a 32 bit program.
68 */
69# ifndef KEY_WOW64_64KEY
70# define KEY_WOW64_64KEY 0x0100
71# endif
Bram Moolenaar6199d432017-10-14 19:05:44 +020072# ifndef KEY_WOW64_32KEY
73# define KEY_WOW64_32KEY 0x0200
74# endif
Bram Moolenaar760d14a2010-07-31 22:03:44 +020075
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#define VIM_STARTMENU "Programs\\Vim " VIM_VERSION_SHORT
77
Bram Moolenaar9bf703d2019-11-30 19:44:38 +010078int interactive; // non-zero when running interactively
Bram Moolenaar071d4272004-06-13 20:20:40 +000079
80/*
81 * Call malloc() and exit when out of memory.
82 */
83 static void *
84alloc(int len)
85{
Bram Moolenaare4963c52019-02-22 19:41:08 +010086 void *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
Bram Moolenaare4963c52019-02-22 19:41:08 +010088 p = malloc(len);
89 if (p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000090 {
91 printf("ERROR: out of memory\n");
92 exit(1);
93 }
Bram Moolenaare4963c52019-02-22 19:41:08 +010094 return p;
Bram Moolenaar071d4272004-06-13 20:20:40 +000095}
96
97/*
98 * The toupper() in Bcc 5.5 doesn't work, use our own implementation.
99 */
100 static int
101mytoupper(int c)
102{
103 if (c >= 'a' && c <= 'z')
104 return c - 'a' + 'A';
105 return c;
106}
107
108 static void
109myexit(int n)
110{
111 if (!interactive)
112 {
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100113 // Present a prompt, otherwise error messages can't be read.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114 printf("Press Enter to continue\n");
115 rewind(stdin);
116 (void)getchar();
117 }
118 exit(n);
119}
120
Bram Moolenaar071d4272004-06-13 20:20:40 +0000121
Bram Moolenaar6199d432017-10-14 19:05:44 +0200122typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123/*
Bram Moolenaar6199d432017-10-14 19:05:44 +0200124 * Check if this is a 64-bit OS.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125 */
Bram Moolenaar6199d432017-10-14 19:05:44 +0200126 static BOOL
127is_64bit_os(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000128{
Bram Moolenaar6199d432017-10-14 19:05:44 +0200129#ifdef _WIN64
130 return TRUE;
131#else
132 BOOL bIsWow64 = FALSE;
133 LPFN_ISWOW64PROCESS pIsWow64Process;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134
Bram Moolenaar6199d432017-10-14 19:05:44 +0200135 pIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(
136 GetModuleHandle("kernel32"), "IsWow64Process");
137 if (pIsWow64Process != NULL)
138 pIsWow64Process(GetCurrentProcess(), &bIsWow64);
139 return bIsWow64;
140#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141}
142
Bram Moolenaar071d4272004-06-13 20:20:40 +0000143 static char *
144searchpath(char *name)
145{
146 static char widename[2 * BUFSIZE];
147 static char location[2 * BUFSIZE + 2];
148
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100149 // There appears to be a bug in FindExecutableA() on Windows NT.
150 // Use FindExecutableW() instead...
Bram Moolenaar6199d432017-10-14 19:05:44 +0200151 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1,
152 (LPWSTR)widename, BUFSIZE);
153 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"",
154 (LPWSTR)location) > (HINSTANCE)32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000155 {
Bram Moolenaar6199d432017-10-14 19:05:44 +0200156 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1,
157 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL);
158 return widename;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 }
160 return NULL;
161}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162
163/*
164 * Call searchpath() and save the result in allocated memory, or return NULL.
165 */
166 static char *
167searchpath_save(char *name)
168{
169 char *p;
170 char *s;
171
172 p = searchpath(name);
173 if (p == NULL)
174 return NULL;
175 s = alloc(strlen(p) + 1);
176 strcpy(s, p);
177 return s;
178}
179
Bram Moolenaar4c3f5362006-04-11 21:38:50 +0000180
181#ifndef CSIDL_COMMON_PROGRAMS
182# define CSIDL_COMMON_PROGRAMS 0x0017
183#endif
184#ifndef CSIDL_COMMON_DESKTOPDIRECTORY
185# define CSIDL_COMMON_DESKTOPDIRECTORY 0x0019
186#endif
187
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188/*
189 * Get the path to a requested Windows shell folder.
190 *
191 * Return FAIL on error, OK on success
192 */
193 int
194get_shell_folder_path(
195 char *shell_folder_path,
196 const char *shell_folder_name)
197{
198 /*
199 * The following code was successfully built with make_mvc.mak.
200 * The resulting executable worked on Windows 95, Millennium Edition, and
201 * 2000 Professional. But it was changed after testing...
202 */
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100203 LPITEMIDLIST pidl = 0; // Pointer to an Item ID list allocated below
204 LPMALLOC pMalloc; // Pointer to an IMalloc interface
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205 int csidl;
206 int alt_csidl = -1;
207 static int desktop_csidl = -1;
208 static int programs_csidl = -1;
209 int *pcsidl;
210 int r;
211
212 if (strcmp(shell_folder_name, "desktop") == 0)
213 {
214 pcsidl = &desktop_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215 csidl = CSIDL_COMMON_DESKTOPDIRECTORY;
216 alt_csidl = CSIDL_DESKTOP;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 }
218 else if (strncmp(shell_folder_name, "Programs", 8) == 0)
219 {
220 pcsidl = &programs_csidl;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221 csidl = CSIDL_COMMON_PROGRAMS;
222 alt_csidl = CSIDL_PROGRAMS;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 }
224 else
225 {
226 printf("\nERROR (internal) unrecognised shell_folder_name: \"%s\"\n\n",
227 shell_folder_name);
228 return FAIL;
229 }
230
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100231 // Did this stuff before, use the same ID again.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 if (*pcsidl >= 0)
233 {
234 csidl = *pcsidl;
235 alt_csidl = -1;
236 }
237
238retry:
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100239 // Initialize pointer to IMalloc interface
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240 if (NOERROR != SHGetMalloc(&pMalloc))
241 {
242 printf("\nERROR getting interface for shell_folder_name: \"%s\"\n\n",
243 shell_folder_name);
244 return FAIL;
245 }
246
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100247 // Get an ITEMIDLIST corresponding to the folder code
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 if (NOERROR != SHGetSpecialFolderLocation(0, csidl, &pidl))
249 {
250 if (alt_csidl < 0 || NOERROR != SHGetSpecialFolderLocation(0,
251 alt_csidl, &pidl))
252 {
253 printf("\nERROR getting ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
254 shell_folder_name);
255 return FAIL;
256 }
257 csidl = alt_csidl;
258 alt_csidl = -1;
259 }
260
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100261 // Translate that ITEMIDLIST to a string
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262 r = SHGetPathFromIDList(pidl, shell_folder_path);
263
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100264 // Free the data associated with pidl
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 pMalloc->lpVtbl->Free(pMalloc, pidl);
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100266 // Release the IMalloc interface
Bram Moolenaar071d4272004-06-13 20:20:40 +0000267 pMalloc->lpVtbl->Release(pMalloc);
268
269 if (!r)
270 {
271 if (alt_csidl >= 0)
272 {
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100273 // We probably get here for Windows 95: the "all users"
274 // desktop/start menu entry doesn't exist.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 csidl = alt_csidl;
276 alt_csidl = -1;
277 goto retry;
278 }
279 printf("\nERROR translating ITEMIDLIST for shell_folder_name: \"%s\"\n\n",
280 shell_folder_name);
281 return FAIL;
282 }
283
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100284 // If there is an alternative: verify we can write in this directory.
285 // This should cause a retry when the "all users" directory exists but we
286 // are a normal user and can't write there.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000287 if (alt_csidl >= 0)
288 {
289 char tbuf[BUFSIZE];
290 FILE *fd;
291
292 strcpy(tbuf, shell_folder_path);
293 strcat(tbuf, "\\vim write test");
294 fd = fopen(tbuf, "w");
295 if (fd == NULL)
296 {
297 csidl = alt_csidl;
298 alt_csidl = -1;
299 goto retry;
300 }
301 fclose(fd);
302 unlink(tbuf);
303 }
304
305 /*
306 * Keep the found csidl for next time, so that we don't have to do the
307 * write test every time.
308 */
309 if (*pcsidl < 0)
310 *pcsidl = csidl;
311
312 if (strncmp(shell_folder_name, "Programs\\", 9) == 0)
313 strcat(shell_folder_path, shell_folder_name + 8);
314
315 return OK;
316}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317
318/*
319 * List of targets. The first one (index zero) is used for the default path
320 * for the batch files.
321 */
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000322#define TARGET_COUNT 9
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323
324struct
325{
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100326 char *name; // Vim exe name (without .exe)
327 char *batname; // batch file name
328 char *lnkname; // shortcut file name
329 char *exename; // exe file name
330 char *exenamearg; // exe file name when using exearg
331 char *exearg; // argument for vim.exe or gvim.exe
332 char *oldbat; // path to existing xxx.bat or NULL
333 char *oldexe; // path to existing xxx.exe or NULL
334 char batpath[BUFSIZE]; // path of batch file to create; not
335 // created when it's empty
Bram Moolenaar071d4272004-06-13 20:20:40 +0000336} targets[TARGET_COUNT] =
337{
338 {"all", "batch files"},
339 {"vim", "vim.bat", "Vim.lnk",
340 "vim.exe", "vim.exe", ""},
341 {"gvim", "gvim.bat", "gVim.lnk",
342 "gvim.exe", "gvim.exe", ""},
343 {"evim", "evim.bat", "gVim Easy.lnk",
344 "evim.exe", "gvim.exe", "-y"},
345 {"view", "view.bat", "Vim Read-only.lnk",
346 "view.exe", "vim.exe", "-R"},
347 {"gview", "gview.bat", "gVim Read-only.lnk",
348 "gview.exe", "gvim.exe", "-R"},
349 {"vimdiff", "vimdiff.bat", "Vim Diff.lnk",
350 "vimdiff.exe","vim.exe", "-d"},
351 {"gvimdiff","gvimdiff.bat", "gVim Diff.lnk",
352 "gvimdiff.exe","gvim.exe", "-d"},
Bram Moolenaar97b2ad32006-03-18 21:40:56 +0000353 {"vimtutor","vimtutor.bat", "Vim tutor.lnk",
354 "vimtutor.bat", "vimtutor.bat", ""},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355};
356
357#define ICON_COUNT 3
358char *(icon_names[ICON_COUNT]) =
359 {"gVim " VIM_VERSION_SHORT,
360 "gVim Easy " VIM_VERSION_SHORT,
361 "gVim Read only " VIM_VERSION_SHORT};
362char *(icon_link_names[ICON_COUNT]) =
363 {"gVim " VIM_VERSION_SHORT ".lnk",
364 "gVim Easy " VIM_VERSION_SHORT ".lnk",
365 "gVim Read only " VIM_VERSION_SHORT ".lnk"};
366
Bram Moolenaar6199d432017-10-14 19:05:44 +0200367/* This is only used for dosinst.c. */
368#if defined(DOSINST)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369/*
370 * Run an external command and wait for it to finish.
371 */
372 static void
373run_command(char *cmd)
374{
375 char *cmd_path;
Bram Moolenaarbbd854d2019-02-18 22:19:33 +0100376 char cmd_buf[BUFSIZE * 2 + 35];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 char *p;
378
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100379 // On WinNT, 'start' is a shell built-in for cmd.exe rather than an
380 // executable (start.exe) like in Win9x.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 cmd_path = searchpath_save("cmd.exe");
382 if (cmd_path != NULL)
383 {
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100384 // There is a cmd.exe, so this might be Windows NT. If it is,
385 // we need to call cmd.exe explicitly. If it is a later OS,
386 // calling cmd.exe won't hurt if it is present.
387 // Also, "start" on NT expects a window title argument.
388 // Replace the slashes with backslashes.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389 while ((p = strchr(cmd_path, '/')) != NULL)
390 *p = '\\';
Bram Moolenaar442b4222010-05-24 21:34:22 +0200391 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392 free(cmd_path);
393 }
394 else
395 {
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100396 // No cmd.exe, just make the call and let the system handle it.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397 sprintf(cmd_buf, "start /w %s", cmd);
398 }
399 system(cmd_buf);
400}
401#endif
402
403/*
404 * Append a backslash to "name" if there isn't one yet.
405 */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100406 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000407add_pathsep(char *name)
408{
409 int len = strlen(name);
410
411 if (len > 0 && name[len - 1] != '\\' && name[len - 1] != '/')
412 strcat(name, "\\");
413}
414
415/*
416 * The normal chdir() does not change the default drive. This one does.
417 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418 int
419change_drive(int drive)
420{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421 char temp[3] = "-:";
422 temp[0] = (char)(drive + 'A' - 1);
423 return !SetCurrentDirectory(temp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424}
425
426/*
427 * Change directory to "path".
428 * Return 0 for success, -1 for failure.
429 */
430 int
431mch_chdir(char *path)
432{
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100433 if (path[0] == NUL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434 return 0;
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100435 if (path[1] == ':') // has a drive name
Bram Moolenaar071d4272004-06-13 20:20:40 +0000436 {
437 if (change_drive(mytoupper(path[0]) - 'A' + 1))
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100438 return -1; // invalid drive name
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439 path += 2;
440 }
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100441 if (*path == NUL) // drive name only
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442 return 0;
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100443 return chdir(path); // let the normal chdir() do the rest
Bram Moolenaar071d4272004-06-13 20:20:40 +0000444}
445
446/*
447 * Expand the executable name into a full path name.
448 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 static char *
450my_fullpath(char *buf, char *fname, int len)
451{
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100452 // Only GetModuleFileName() will get the long file name path.
453 // GetFullPathName() may still use the short (FAT) name.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000454 DWORD len_read = GetModuleFileName(NULL, buf, (size_t)len);
455
456 return (len_read > 0 && len_read < (DWORD)len) ? buf : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458
459/*
460 * Remove the tail from a file or directory name.
461 * Puts a NUL on the last '/' or '\'.
462 */
463 static void
464remove_tail(char *path)
465{
466 int i;
467
468 for (i = strlen(path) - 1; i > 0; --i)
469 if (path[i] == '/' || path[i] == '\\')
470 {
471 path[i] = NUL;
472 break;
473 }
474}
475
476
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100477char installdir[MAX_PATH-9]; // top of the installation dir, where the
478 // install.exe is located, E.g.:
479 // "c:\vim\vim60"
480int runtimeidx; // index in installdir[] where "vim60" starts
481char *sysdrive; // system drive or "c:\"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000482
483/*
484 * Setup for using this program.
485 * Sets "installdir[]".
486 */
487 static void
488do_inits(char **argv)
489{
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100490 // Find out the full path of our executable.
Bram Moolenaare4963c52019-02-22 19:41:08 +0100491 if (my_fullpath(installdir, argv[0], sizeof(installdir)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 {
493 printf("ERROR: Cannot get name of executable\n");
494 myexit(1);
495 }
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100496 // remove the tail, the executable name "install.exe"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 remove_tail(installdir);
498
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100499 // change to the installdir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 mch_chdir(installdir);
501
Bram Moolenaar9bf703d2019-11-30 19:44:38 +0100502 // Find the system drive. Only used for searching the Vim executable, not
503 // very important.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504 sysdrive = getenv("SYSTEMDRIVE");
505 if (sysdrive == NULL || *sysdrive == NUL)
506 sysdrive = "C:\\";
507}