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