1 /* SCCS Id: @(#)pcsys.c 3.3 1999/12/10
2 /* NetHack may be freely redistributed. See license for details. */
3
4 /*
5 * System related functions for MSDOS, OS/2, TOS, and Windows NT
6 */
7
8 #define NEED_VARARGS
9 #include "hack.h"
10 #include "wintty.h"
11
12 #include <ctype.h>
13 #include <fcntl.h>
14 #ifndef MSDOS /* already done */
15 #include <process.h>
16 #endif
17 #ifdef __GO32__
18 #define P_WAIT 0
19 #define P_NOWAIT 1
20 #endif
21 #ifdef TOS
22 #include <osbind.h>
23 #endif
24 #if defined(MSDOS) && !defined(__GO32__)
25 #define findfirst findfirst_file
26 #define findnext findnext_file
27 #define filesize filesize_nh
28 #endif
29
30
31 #if defined(MICRO) || defined(WIN32) || defined(OS2)
32 void FDECL(nethack_exit,(int));
33 #else
34 #define nethack_exit exit
35 #endif
36 static void NDECL(msexit);
37
38
39 #ifdef MOVERLAY
40 extern void __far __cdecl _movepause( void );
41 extern void __far __cdecl _moveresume( void );
42 extern unsigned short __far __cdecl _movefpause;
43 extern unsigned short __far __cdecl _movefpaused;
44 #define __MOVE_PAUSE_DISK 2 /* Represents the executable file */
45 #define __MOVE_PAUSE_CACHE 4 /* Represents the cache memory */
46 #endif /* MOVERLAY */
47
48 STATIC_DCL boolean NDECL(record_exists);
49 #ifndef TOS
50 STATIC_DCL boolean NDECL(comspec_exists);
51 #endif
52
53 #ifdef WIN32CON
54 extern int GUILaunched; /* from nttty.c */
55 #endif
56
57 #ifdef MICRO
58
59 void
flushout()60 flushout()
61 {
62 (void) fflush(stdout);
63 return;
64 }
65
66 static const char *COMSPEC =
67 # ifdef TOS
68 "SHELL";
69 # else
70 "COMSPEC";
71 # endif
72
73 #define getcomspec() nh_getenv(COMSPEC)
74
75 # ifdef SHELL
76 int
dosh()77 dosh()
78 {
79 extern char orgdir[];
80 char *comspec;
81 int spawnstat;
82 #if defined(MSDOS) && defined(NO_TERMS)
83 int grmode;
84 #endif
85 if (comspec = getcomspec()) {
86 # ifndef TOS /* TOS has a variety of shells */
87 suspend_nhwindows("To return to NetHack, enter \"exit\" at the system prompt.\n");
88 # else
89 # if defined(MSDOS) && defined(NO_TERMS)
90 grmode = iflags.grmode;
91 # endif
92 suspend_nhwindows((char *)0);
93 # endif /* TOS */
94 chdirx(orgdir, 0);
95 # ifdef __GO32__
96 if (system(comspec) < 0) { /* wsu@eecs.umich.edu */
97 # else
98 # ifdef MOVERLAY
99 /* Free the cache memory used by overlays, close .exe */
100 _movefpause |= __MOVE_PAUSE_DISK;
101 _movefpause |= __MOVE_PAUSE_CACHE;
102 _movepause();
103 # endif
104 spawnstat = spawnl(P_WAIT, comspec, comspec, (char *)0);
105 # ifdef MOVERLAY
106 _moveresume();
107 # endif
108
109 if ( spawnstat < 0) {
110 # endif
111 raw_printf("Can't spawn \"%s\"!", comspec);
112 getreturn("to continue");
113 }
114 # ifdef TOS
115 /* Some shells (e.g. Gulam) turn the cursor off when they exit */
116 if (iflags.BIOS)
117 (void)Cursconf(1, -1);
118 # endif
119 chdirx(hackdir, 0);
120 get_scr_size(); /* maybe the screen mode changed (TH) */
121 # if defined(MSDOS) && defined(NO_TERMS)
122 if (grmode) gr_init();
123 # endif
124 resume_nhwindows();
125 } else
126 pline("Can't find %s.",COMSPEC);
127 return 0;
128 }
129 # endif /* SHELL */
130
131 # ifdef MFLOPPY
132
133 void
134 eraseall(path, files)
135 const char *path, *files;
136 {
137 char buf[PATHLEN];
138 char *foundfile;
139
140 foundfile = foundfile_buffer();
141 Sprintf(buf, "%s%s", path, files);
142 if (findfirst(buf))
143 do {
144 Sprintf(buf, "%s%s", path, foundfile);
145 (void) unlink(buf);
146 } while (findnext());
147 return;
148 }
149
150 /*
151 * Rewritten for version 3.3 to be faster
152 */
153 void
154 copybones(mode)
155 int mode;
156 {
157 char from[PATHLEN], to[PATHLEN], last[13];
158 char *frompath, *topath;
159 char *foundfile;
160 # ifndef TOS
161 int status;
162 char copy[8], *comspec;
163 # endif
164
165 if (!ramdisk)
166 return;
167
168 /* Find the name of the last file to be transferred
169 */
170 frompath = (mode != TOPERM) ? permbones : levels;
171 foundfile = foundfile_buffer();
172 last[0] = '\0';
173 Sprintf(from, "%s%s", frompath, allbones);
174 topath = (mode == TOPERM) ? permbones : levels;
175 # ifdef TOS
176 eraseall(topath, allbones);
177 # endif
178 if (findfirst(from))
179 do {
180 # ifdef TOS
181 Sprintf(from, "%s%s", frompath, foundfile);
182 Sprintf(to, "%s%s", topath, foundfile);
183 if (_copyfile(from, to))
184 goto error_copying;
185 # endif
186 Strcpy(last, foundfile);
187 } while (findnext());
188 # ifdef TOS
189 else
190 return;
191 # else
192 if (last[0]) {
193 Sprintf(copy, "%cC copy",switchar());
194
195 /* Remove any bones files in `to' directory.
196 */
197 eraseall(topath, allbones);
198
199 /* Copy `from' to `to' */
200 Sprintf(to, "%s%s", topath, allbones);
201 comspec = getcomspec();
202 status =spawnl(P_WAIT, comspec, comspec, copy, from,
203 to, "> nul", (char *)0);
204 } else
205 return;
206 # endif /* TOS */
207
208 /* See if the last file got there. If so, remove the ramdisk bones
209 * files.
210 */
211 Sprintf(to, "%s%s", topath, last);
212 if (findfirst(to)) {
213 if (mode == TOPERM)
214 eraseall(frompath, allbones);
215 return;
216 }
217
218 # ifdef TOS
219 error_copying:
220 # endif
221 /* Last file didn't get there.
222 */
223 Sprintf(to, "%s%s", topath, allbones);
224 msmsg("Can't copy \"%s\" to \"%s\" -- ", from, to);
225 # ifndef TOS
226 if (status < 0)
227 msmsg("can't spawn \"%s\"!", comspec);
228 else
229 # endif
230 msmsg((freediskspace(topath) < filesize(from)) ?
231 "insufficient disk space." : "bad path(s)?");
232 if (mode == TOPERM) {
233 msmsg("Bones will be left in \"%s\"\n",
234 *levels ? levels : hackdir);
235 } else {
236 /* Remove all bones files on the RAMdisk */
237 eraseall(levels, allbones);
238 playwoRAMdisk();
239 }
240 return;
241 }
242
243 void
244 playwoRAMdisk()
245 {
246 int c;
247
248 msmsg("Do you wish to play without a RAMdisk? [yn] (n)");
249
250 /* Set ramdisk false *before* exit-ing (because msexit calls
251 * copybones)
252 */
253 ramdisk = FALSE;
254 c = tgetch(); if (c == 'Y') c = 'y';
255 if (c != 'y') {
256 settty("Be seeing you...\n");
257 nethack_exit(EXIT_SUCCESS);
258 }
259 set_lock_and_bones();
260 return;
261 }
262
263 int
264 saveDiskPrompt(start)
265 int start;
266 {
267 char buf[BUFSIZ], *bp;
268 char qbuf[QBUFSZ];
269
270 int fd;
271
272 if (flags.asksavedisk) {
273 /* Don't prompt if you can find the save file */
274 if ((fd = open_savefile()) >= 0) {
275 (void) close(fd);
276 return 1;
277 }
278 clear_nhwindow(WIN_MESSAGE);
279 pline("If save file is on a save disk, insert that disk now.");
280 mark_synch();
281 Sprintf(qbuf,"File name (default \"%s\"%s) ?", SAVEF,
282 start ? "" : ", <Esc> cancels save");
283 getlin(qbuf, buf);
284 clear_nhwindow(WIN_MESSAGE);
285 if (!start && *buf == '\033')
286 return 0;
287
288 /* Strip any whitespace. Also, if nothing was entered except
289 * whitespace, do not change the value of SAVEF.
290 */
291 for (bp = buf; *bp; bp++)
292 if (!isspace(*bp)) {
293 strncpy(SAVEF, bp, PATHLEN);
294 break;
295 }
296 }
297 return 1;
298 }
299
300 # endif /* MFLOPPY */
301
302 /* Return 1 if the record file was found */
303 STATIC_OVL boolean
304 record_exists()
305 {
306 FILE *fp;
307
308 fp = fopen_datafile(RECORD, "r", TRUE);
309 if (fp) {
310 fclose(fp);
311 return TRUE;
312 }
313 return FALSE;
314 }
315
316 # ifdef TOS
317 #define comspec_exists() 1
318 # else
319 /* Return 1 if the comspec was found */
320 STATIC_OVL boolean
321 comspec_exists()
322 {
323 int fd;
324 char *comspec;
325
326 if (comspec = getcomspec())
327 if ((fd = open(comspec, O_RDONLY)) >= 0) {
328 (void) close(fd);
329 return TRUE;
330 }
331 return FALSE;
332 }
333 # endif
334
335 # ifdef MFLOPPY
336 /* Prompt for game disk, then check for record file.
337 */
338 void
339 gameDiskPrompt()
340 {
341 if (flags.asksavedisk) {
342 if (record_exists() && comspec_exists())
343 return;
344 (void) putchar('\n');
345 getreturn("when the game disk has been inserted");
346 }
347 if (comspec_exists() && record_exists())
348 return;
349
350 if (!comspec_exists())
351 msmsg("\n\nWARNING: can't find command processor \"%s\"!\n", getcomspec());
352 if (!record_exists())
353 msmsg("\n\nWARNING: can't find record file \"%s\"!\n", RECORD);
354 msmsg("If the game disk is not in, insert it now.\n");
355 getreturn("to continue");
356 return;
357 }
358 # endif /* MFLOPPY */
359 #endif /* MICRO */
360
361 /*
362 * Add a backslash to any name not ending in /, \ or : There must
363 * be room for the \
364 */
365 void
366 append_slash(name)
367 char *name;
368 {
369 char *ptr;
370
371 if (!*name)
372 return;
373 ptr = name + (strlen(name) - 1);
374 if (*ptr != '\\' && *ptr != '/' && *ptr != ':') {
375 *++ptr = '\\';
376 *++ptr = '\0';
377 }
378 return;
379 }
380
381 void
382 getreturn(str)
383 const char *str;
384 {
385 #ifdef TOS
386 msmsg("Hit <Return> %s.", str);
387 #else
388 msmsg("Hit <Enter> %s.", str);
389 #endif
390 while (Getchar() != '\n') ;
391 return;
392 }
393
394 void
395 msmsg VA_DECL(const char *, fmt)
396 VA_START(fmt);
397 VA_INIT(fmt, const char *);
398 # if defined(MSDOS)
399 if (iflags.grmode)
400 gr_finish();
401 # endif
402 Vprintf(fmt, VA_ARGS);
403 flushout();
404 VA_END();
405 return;
406 }
407
408 /*
409 * Follow the PATH, trying to fopen the file.
410 */
411 #ifdef TOS
412 # ifdef __MINT__
413 #define PATHSEP ':'
414 # else
415 #define PATHSEP ','
416 # endif
417 #else
418 #define PATHSEP ';'
419 #endif
420
421 FILE *
fopenp(name,mode)422 fopenp(name, mode)
423 const char *name, *mode;
424 {
425 char buf[BUFSIZ], *bp, *pp, lastch = 0;
426 FILE *fp;
427
428 /* Try the default directory first. Then look along PATH.
429 */
430 (void) strncpy(buf, name, BUFSIZ - 1);
431 buf[BUFSIZ-1] = '\0';
432 if (fp = fopen(buf, mode))
433 return fp;
434 else {
435 int ccnt = 0;
436 pp = getenv("PATH");
437 while (pp && *pp) {
438 bp = buf;
439 while (*pp && *pp != PATHSEP) {
440 lastch = *bp++ = *pp++;
441 ccnt++;
442 }
443 if (lastch != '\\' && lastch != '/') {
444 *bp++ = '\\';
445 ccnt++;
446 }
447 (void) strncpy(bp, name, (BUFSIZ - ccnt) - 2);
448 bp[BUFSIZ-1] = '\0';
449 if (fp = fopen(buf, mode))
450 return fp;
451 if (*pp)
452 pp++;
453 }
454 }
455 #ifdef OS2_CODEVIEW /* one more try for hackdir */
456 (void) strncpy(buf, hackdir, BUFSZ);
457 buf[BUFSZ-1] = '\0';
458 if ((strlen(name) + 1 + strlen(buf)) < BUFSZ - 1) {
459 append_slash(buf);
460 Strcat(buf,name);
461 } else
462 impossible("fopenp() buffer too small for complete filename!");
463 if(fp = fopen(buf,mode))
464 return fp;
465 #endif
466 return (FILE *)0;
467 }
468
469 #if defined(MICRO) || defined(WIN32) || defined(OS2)
nethack_exit(code)470 void nethack_exit(code)
471 int code;
472 {
473 msexit();
474 #ifdef MTHREAD_VIEW
475 if (iflags.mthreaded) nh_thread_exit(code); /* no return from this */
476 else
477 #endif
478 exit(code);
479 }
480
481 /* Chdir back to original directory
482 */
483 #ifdef TOS
484 extern boolean run_from_desktop; /* set in pcmain.c */
485 #endif
486
msexit()487 static void msexit()
488 {
489 #ifdef CHDIR
490 extern char orgdir[];
491 #endif
492
493 flushout();
494 #ifndef TOS
495 # ifndef WIN32
496 enable_ctrlP(); /* in case this wasn't done */
497 # endif
498 #endif
499 #ifdef MFLOPPY
500 if (ramdisk) copybones(TOPERM);
501 #endif
502 #ifdef CHDIR
503 chdir(orgdir); /* chdir, not chdirx */
504 chdrive(orgdir);
505 #endif
506 #ifdef TOS
507 if (run_from_desktop)
508 getreturn("to continue"); /* so the user can read the score list */
509 # ifdef TEXTCOLOR
510 if (colors_changed)
511 restore_colors();
512 # endif
513 #endif
514 #ifdef WIN32CON
515 /* Only if we started from the GUI, not the command prompt,
516 * we need to get one last return, so the score board does
517 * not vanish instantly after being created.
518 * GUILaunched is defined and set in nttty.c.
519 */
520
521 if (GUILaunched) getreturn("to end");
522 #endif
523 return;
524 }
525 #ifdef WIN32
526 /*
527 * This is a kludge. Just before the release of 3.3.0 the latest
528 * version of a popular MAPI mail product was found to exhibit
529 * a strange result where the current directory was changed out
530 * from under NetHack resulting in a failure of all subsequent
531 * file operations in NetHack. This routine is called prior
532 * to all file open/renames/deletes in file.c.
533 *
534 * A more elegant solution will be sought after 3.3.0 is released.
535 */
dircheck()536 void dircheck()
537 {
538 char dirbuf[BUFSZ];
539 dirbuf[0] = '\0';
540 if (getcwd(dirbuf, sizeof dirbuf) != (char *)0)
541 /* pline("%s,%s",dirbuf,hackdir); */
542 if (strcmp(dirbuf,hackdir) != 0)
543 chdir(hackdir); /* chdir, not chdirx */
544 }
545 #endif
546 #endif /* MICRO || WIN32 || OS2 */
547