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