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