Mercurial > vim
annotate src/dosinst.h @ 2282:a888ed7ba375 vim73
Make updating text for conceal mode simpler. A few compiler warning fixes.
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Fri, 02 Jul 2010 20:20:09 +0200 |
parents | 120502692d82 |
children | 573da4dac306 |
rev | line source |
---|---|
7 | 1 /* 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 */ | |
714 | 12 |
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 | |
7 | 19 #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 | |
714 | 26 # include "vimio.h" |
7 | 27 # 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. */ | |
46 char *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 | |
83 int interactive; /* non-zero when running interactively */ | |
84 | |
85 /* | |
86 * Call malloc() and exit when out of memory. | |
87 */ | |
88 static void * | |
89 alloc(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 | |
106 mytoupper(int c) | |
107 { | |
108 if (c >= 'a' && c <= 'z') | |
109 return c - 'a' + 'A'; | |
110 return c; | |
111 } | |
112 | |
113 static void | |
114 myexit(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 | |
133 static 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 | |
140 PlatformId(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 * | |
161 searchpath(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 * | |
196 searchpath_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 | |
826 | 210 |
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 | |
7 | 218 /* |
219 * Get the path to a requested Windows shell folder. | |
220 * | |
221 * Return FAIL on error, OK on success | |
222 */ | |
223 int | |
224 get_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; | |
245 csidl = CSIDL_COMMON_DESKTOPDIRECTORY; | |
246 alt_csidl = CSIDL_DESKTOP; | |
247 } | |
248 else if (strncmp(shell_folder_name, "Programs", 8) == 0) | |
249 { | |
250 pcsidl = &programs_csidl; | |
251 csidl = CSIDL_COMMON_PROGRAMS; | |
252 alt_csidl = CSIDL_PROGRAMS; | |
253 } | |
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 | |
268 retry: | |
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 */ | |
782 | 353 #define TARGET_COUNT 9 |
7 | 354 |
355 struct | |
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"}, | |
782 | 384 {"vimtutor","vimtutor.bat", "Vim tutor.lnk", |
385 "vimtutor.bat", "vimtutor.bat", ""}, | |
7 | 386 }; |
387 | |
388 #define ICON_COUNT 3 | |
389 char *(icon_names[ICON_COUNT]) = | |
390 {"gVim " VIM_VERSION_SHORT, | |
391 "gVim Easy " VIM_VERSION_SHORT, | |
392 "gVim Read only " VIM_VERSION_SHORT}; | |
393 char *(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 | |
405 run_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. | |
2217
120502692d82
Improve the MS-Windows installer.
Bram Moolenaar <bram@vim.org>
parents:
1222
diff
changeset
|
421 * Also, "start" on NT expects a window title argument. |
7 | 422 */ |
423 /* Replace the slashes with backslashes. */ | |
424 while ((p = strchr(cmd_path, '/')) != NULL) | |
425 *p = '\\'; | |
2217
120502692d82
Improve the MS-Windows installer.
Bram Moolenaar <bram@vim.org>
parents:
1222
diff
changeset
|
426 sprintf(cmd_buf, "%s /c start \"vimcmd\" /wait %s", cmd_path, cmd); |
7 | 427 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 | |
442 add_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 | |
455 change_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(®s, ®s); /* set default drive */ | |
468 regs.h.ah = 0x19; | |
469 intdos(®s, ®s); /* 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 | |
482 mch_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 * | |
507 my_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 | |
1222 | 521 if (strchr(fname, ':') != NULL) /* already expanded */ |
7 | 522 { |
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 | |
603 remove_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 | |
616 char installdir[BUFSIZE]; /* top of the installation dir, where the | |
617 install.exe is located, E.g.: | |
618 "c:\vim\vim60" */ | |
619 int runtimeidx; /* index in installdir[] where "vim60" starts */ | |
620 char *sysdrive; /* system drive or "c:\" */ | |
621 | |
622 /* | |
623 * Setup for using this program. | |
624 * Sets "installdir[]". | |
625 */ | |
626 static void | |
627 do_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 } |