Source:NetHack 2.3e/msdos.c

From NetHackWiki
Revision as of 23:07, 3 July 2024 by Furey (talk | contribs) (Delete link-to-particular-source instructions. Source code covers that now. "the latest release" -> "newer releases".)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Below is the full text to msdos.c from the source code of NetHack 2.3e.

Warning! This is the source code from an old release. For newer releases, see Source code

Screenshots and source code from Hack are used under the CWI license.

1.    /*	SCCS Id: @(#)msdos.c	2.3	87/12/16
2.    /* An assortment of MSDOS functions.
3.     */
4.    
5.    #include <stdio.h>
6.    #include "hack.h"
7.    
8.    #ifdef MSDOS
9.    # include <dos.h>
10.   
11.   void
12.   flushout()
13.   {
14.   	(void) fflush(stdout);
15.   }
16.   
17.   getuid() {
18.   	return 1;
19.   }
20.   
21.   char *
22.   getlogin() {
23.   	return ((char *) NULL);
24.   }
25.   # ifdef REDO
26.   tgetch() {
27.   	char ch, popch();
28.   	static char DOSgetch(), BIOSgetch();
29.   
30.   	if (!(ch = popch())) {
31.   #  ifdef DGK
32.   		/* BIOSgetch can use the numeric key pad on IBM compatibles. */
33.   		if (flags.IBMBIOS)
34.   			ch = BIOSgetch();
35.   		else
36.   #  endif
37.   			ch = DOSgetch();
38.   	}
39.   	return ((ch == '\r') ? '\n' : ch);
40.   }
41.   # else /* REDO /**/
42.   tgetch() {
43.   	char ch;
44.   	static char DOSgetch(), BIOSgetch();
45.   
46.   #  ifdef DGK
47.   	/* BIOSgetch can use the numeric key pad on IBM compatibles. */
48.   	if (flags.IBMBIOS)
49.   		ch = BIOSgetch();
50.   	else
51.   #  endif
52.   		ch = DOSgetch();
53.   	return ((ch == '\r') ? '\n' : ch);
54.   }
55.   # endif /* REDO /**/
56.   
57.   # define DIRECT_INPUT	0x7
58.   static char
59.   DOSgetch() {
60.   	union REGS regs;
61.   
62.   	regs.h.ah = DIRECT_INPUT;
63.   	intdos(&regs, &regs);
64.   	if (!regs.h.al) {	/* an extended code -- not yet supported */
65.   		regs.h.ah = DIRECT_INPUT;
66.   		intdos(&regs, &regs);	/* eat the next character */
67.   		regs.h.al = 0;		/* and return a 0 */
68.   	}
69.   	return (regs.h.al);
70.   }
71.   
72.   
73.   # ifdef DGK
74.   #  include <ctype.h>
75.   #  include <fcntl.h>
76.   
77.   #  define Sprintf (void) sprintf
78.   #  define WARN 1
79.   #  define NOWARN 0
80.   
81.   static char *
82.   getcomspec(warn) {
83.   	return getenv("COMSPEC");
84.   }
85.   
86.   #  ifdef SHELL
87.   #   include <process.h>
88.   dosh() {
89.   	extern char orgdir[];
90.   	char *comspec;
91.   
92.   	if (comspec = getcomspec()) {
93.   		settty("To return to NetHack, type \"exit\" at the DOS prompt.\n");
94.   		chdirx(orgdir, 0);
95.   		if (spawnl(P_WAIT, comspec, comspec, NULL) < 0) {
96.   			printf("\nCan't spawn %s !\n", comspec);
97.   			flags.toplin = 0;
98.   			more();
99.   		}
100.  		chdirx(hackdir, 0);
101.  		start_screen();
102.  		docrt();
103.  	} else
104.  		pline("No COMSPEC !?  Can't exec COMMAND.COM");
105.  	return(0);
106.  }
107.  #  endif /* SHELL */
108.  
109.  /* Normal characters are output when the shift key is not pushed.
110.   * Shift characters are output when either shift key is pushed.
111.   */
112.  #  define KEYPADHI	83
113.  #  define KEYPADLOW	71
114.  #  define iskeypad(x)	(KEYPADLOW <= (x) && (x) <= KEYPADHI)
115.  static struct {
116.  	char normal, shift;
117.  	} keypad[KEYPADHI - KEYPADLOW + 1] = {
118.  			{'y', 'Y'},		/* 7 */
119.  			{'k', 'K'},		/* 8 */
120.  			{'u', 'U'},		/* 9 */
121.  			{'m', CTRL('P')},	/* - */
122.  			{'h', 'H'},		/* 4 */
123.  			{'g', 'g'},		/* 5 */
124.  			{'l', 'L'},		/* 6 */
125.  			{'p', 'P'},		/* + */
126.  			{'b', 'B'},		/* 1 */
127.  			{'j', 'J'},		/* 2 */
128.  			{'n', 'N'},		/* 3 */
129.  			{'i', 'I'},		/* Ins */
130.  			{'.', ':'}		/* Del */
131.  };
132.  
133.  /* BIOSgetch gets keys directly with a BIOS call.
134.   */
135.  #  define SHIFT		(0x1 | 0x2)
136.  #  define KEYBRD_BIOS	0x16
137.  
138.  static char
139.  BIOSgetch() {
140.  	unsigned char scan, shift, ch;
141.  	union REGS regs;
142.  
143.  	/* Get scan code.
144.  	 */
145.  	regs.h.ah = 0;
146.  	int86(KEYBRD_BIOS, &regs, &regs);
147.  	ch = regs.h.al;
148.  	scan = regs.h.ah;
149.  
150.  	/* Get shift status.
151.  	 */
152.  	regs.h.ah = 2;
153.  	int86(KEYBRD_BIOS, &regs, &regs);
154.  	shift = regs.h.al;
155.  
156.  	/* If scan code is for the keypad, translate it.
157.  	 */
158.  	if (iskeypad(scan)) {
159.  		if (shift & SHIFT)
160.  			ch = keypad[scan - KEYPADLOW].shift;
161.  		else
162.  			ch = keypad[scan - KEYPADLOW].normal;
163.  	}
164.  	return ch;
165.  }
166.  
167.  /* construct the string  file.level */
168.  void
169.  name_file(file, level)
170.  char *file;
171.  int level;
172.  {
173.  	char *tf;
174.  	
175.  	if (tf = rindex(file, '.'))
176.  		Sprintf(tf+1, "%d", level);
177.  }
178.  
179.  
180.  #  define FINDFIRST	0x4E00
181.  #  define FINDNEXT	0x4F00
182.  #  define GETDTA	0x2F00
183.  #  define SETFILETIME	0x5701
184.  #  define GETSWITCHAR	0x3700
185.  #  define FREESPACE	0x36
186.  
187.  static char
188.  switchar()
189.  {
190.  	union REGS regs;
191.  
192.  	regs.x.ax = GETSWITCHAR;
193.  	intdos(&regs, &regs);
194.  	return regs.h.dl;
195.  }
196.  
197.  long
198.  freediskspace(path)
199.  char *path;
200.  {
201.  	union REGS regs;
202.  
203.  	regs.h.ah = FREESPACE;
204.  	if (path[0] && path[1] == ':')
205.  		regs.h.dl = (toupper(path[0]) - 'A') + 1;
206.  	else
207.  		regs.h.dl = 0;
208.  	intdos(&regs, &regs);
209.  	if (regs.x.ax == 0xFFFF)
210.  		return -1L;		/* bad drive number */
211.  	else
212.  		return ((long) regs.x.bx * regs.x.cx * regs.x.ax);
213.  }
214.  
215.  /* Functions to get filenames using wildcards
216.   */
217.  static
218.  findfirst(path)
219.  char *path;
220.  {
221.  	union REGS regs;
222.  	struct SREGS sregs;
223.  
224.  	regs.x.ax = FINDFIRST;
225.  	regs.x.cx = 0;		/* normal files */
226.  	regs.x.dx = FP_OFF(path);
227.  	sregs.ds = FP_SEG(path);
228.  	intdosx(&regs, &regs, &sregs);
229.  	return !regs.x.cflag;
230.  }
231.  
232.  static
233.  findnext() {
234.  	union REGS regs;
235.  
236.  	regs.x.ax = FINDNEXT;
237.  	intdos(&regs, &regs);
238.  	return !regs.x.cflag;
239.  }
240.  
241.  #ifndef __TURBOC__
242.  /* Get disk transfer area, Turbo C already has getdta */
243.  static char *
244.  getdta() {
245.  	union REGS regs;
246.  	struct SREGS sregs;
247.  	char *ret;
248.  
249.  	regs.x.ax = GETDTA;
250.  	intdosx(&regs, &regs, &sregs);
251.  	FP_OFF(ret) = regs.x.bx;
252.  	FP_SEG(ret) = sregs.es;
253.  	return ret;
254.  }
255.  #endif
256.  
257.  long
258.  filesize(file)
259.  char *file;
260.  {
261.  	char *dta;
262.  
263.  	if (findfirst(file)) {
264.  		dta = getdta();
265.  		return  (* (long *) (dta + 26));
266.  	} else
267.  		return -1L;
268.  }
269.  
270.  void
271.  eraseall(path, files)
272.  char *path, *files;
273.  {
274.  	char	*dta, buf[PATHLEN];
275.  
276.  	dta = getdta();
277.  	Sprintf(buf, "%s%s", path, files);
278.  	if (findfirst(buf))
279.  		do {
280.  			Sprintf(buf, "%s%s", path, dta + 30);
281.  			(void) unlink(buf);
282.  		} while (findnext());
283.  }
284.  
285.  /* Rewritten for version 3.3 to be faster
286.   */
287.  void
288.  copybones(mode) {
289.  	char from[PATHLEN], to[PATHLEN], last[13], copy[8];
290.  	char *frompath, *topath, *dta, *comspec;
291.  	int status;
292.  	long fs;
293.  	extern saveprompt;
294.  
295.  	if (!ramdisk)
296.  		return;
297.  
298.  	/* Find the name of the last file to be transferred
299.  	 */
300.  	frompath = (mode != TOPERM) ? permbones : levels;
301.  	dta = getdta();
302.  	last[0] = '\0';
303.  	Sprintf(from, "%s%s", frompath, allbones);
304.  	if (findfirst(from))
305.  		do {
306.  			strcpy(last, dta + 30);
307.  		} while (findnext());
308.  
309.  	topath = (mode == TOPERM) ? permbones : levels;
310.  	if (last[0]) {
311.  		Sprintf(copy, "%cC copy", switchar());
312.  
313.  		/* Remove any bones files in `to' directory.
314.  		 */
315.  		eraseall(topath, allbones);
316.  
317.  		/* Copy `from' to `to' */
318.  		Sprintf(to, "%s%s", topath, allbones);
319.  		comspec = getcomspec();
320.  		status =spawnl(P_WAIT, comspec, comspec, copy, from,
321.  			to, "> nul", NULL);
322.  	} else
323.  		return;
324.  
325.  	/* See if the last file got there.  If so, remove the ramdisk bones
326.  	 * files.
327.  	 */
328.  	Sprintf(to, "%s%s", topath, last);
329.  	if (findfirst(to)) {
330.  		if (mode == TOPERM)
331.  			eraseall(frompath, allbones);
332.  		return;
333.  	}
334.  
335.  	/* Last file didn't get there.
336.  	 */
337.  	Sprintf(to, "%s%s", topath, allbones);
338.  	msmsg("Cannot copy `%s' to `%s' -- %s\n", from, to,
339.  		(status < 0) ? "can't spawn COMSPEC !" :
340.  		(freediskspace(topath) < filesize(from)) ?
341.  			"insufficient disk space." : "bad path(s)?");
342.  	if (mode == TOPERM) {
343.  		msmsg("Bones will be left in `%s'\n",
344.  			*levels ? levels : hackdir);
345.  		return;
346.  	} else {
347.  		/* Remove all bones files on the RAMdisk */
348.  		eraseall(levels, allbones);
349.  		playwoRAMdisk();
350.  	}
351.  }
352.  
353.  playwoRAMdisk() {
354.  	msmsg("Do you wish to play without a RAMdisk (y/n) ? ");
355.  
356.  	/* Set ramdisk false *before* exit'ing (because msexit calls
357.  	 * copybones)
358.  	 */
359.  	ramdisk = FALSE;
360.  	if (getchar() != 'y') {
361.  		settty("Be seeing you ...\n");
362.  		exit(0);
363.  	}
364.  	set_lock_and_bones();
365.  	return;
366.  }
367.  
368.  saveDiskPrompt(start) {
369.  	extern saveprompt;
370.  	char buf[BUFSIZ], *bp;
371.  	int fd;
372.  
373.  	if (saveprompt) {
374.  		/* Don't prompt if you can find the save file */
375.  		if ((fd = open(SAVEF, 0)) >= 0) {
376.  			(void) close(fd);
377.  			return 1;
378.  		}
379.  		remember_topl();
380.  		home();
381.  		cl_end();
382.  		msmsg("If save file is on a SAVE disk, put that disk in now.\n");
383.  		cl_end();
384.  		msmsg("File name (default `%s'%s) ? ", SAVEF,
385.  			start ? "" : ", <Esc> cancels save");
386.  		getlin(buf);
387.  		home();
388.  		cl_end();
389.  		curs(1, 2);
390.  		cl_end();
391.  		if (!start && *buf == '\033')
392.  			return 0;
393.  
394.  		/* Strip any whitespace. Also, if nothing was entered except
395.  		 * whitespace, do not change the value of SAVEF.
396.  		 */
397.  		for (bp = buf; *bp; bp++)
398.  			if (!isspace(*bp)) {
399.  				strncpy(SAVEF, bp, PATHLEN);
400.  				break;
401.  			}
402.  	}
403.  	return 1;
404.  }
405.  
406.  /* Return 1 if the record file was found */
407.  static
408.  record_exists() {
409.  	int fd;
410.  
411.  	if ((fd = open(RECORD, 0)) >= 0) {
412.  		close(fd);
413.  		return TRUE;
414.  	}
415.  	return FALSE;
416.  }
417.  
418.  /* Return 1 if the comspec was found */
419.  static
420.  comspec_exists() {
421.  	int fd;
422.  	char *comspec;
423.  
424.  	if (comspec = getcomspec())
425.  		if ((fd = open(comspec, 0)) >= 0) {
426.  			close(fd);
427.  			return TRUE;
428.  		}
429.  	return FALSE;
430.  }
431.  
432.  /* Prompt for game disk, then check for record file.
433.   */
434.  void
435.  gameDiskPrompt() {
436.  	extern saveprompt;
437.  
438.  	if (saveprompt) {
439.  		if (record_exists() && comspec_exists())
440.  			return;
441.  		(void) putchar('\n');
442.  		getreturn("when the GAME disk has been put in");
443.  	}
444.  	if (comspec_exists() && record_exists())
445.  		return;
446.  
447.  	if (!comspec_exists())
448.  		msmsg("\n\nWARNING: can't find comspec `%s'!\n", getcomspec());
449.  	if (!record_exists())
450.  		msmsg("\n\nWARNING: can't find record file `%s'!\n", RECORD);
451.  	msmsg("If the GAME disk is not in, put it in now.\n");
452.  	getreturn("to continue");
453.  }
454.  
455.  /* Read configuration */
456.  void
457.  read_config_file() {
458.  	char	tmp_ramdisk[PATHLEN], tmp_levels[PATHLEN];
459.  	char	buf[BUFSZ], *bufp;
460.  	FILE	*fp, *fopenp();
461.  	extern	char plname[];
462.  	extern	int saveprompt;
463.  
464.  	tmp_ramdisk[0] = 0;
465.  	tmp_levels[0] = 0;
466.  	if ((fp = fopenp(configfile, "r")) == NULL) {
467.  		msmsg("Warning: no configuration file!\n");
468.  		getreturn("to continue");
469.  		return;
470.  	}
471.  	while (fgets(buf, BUFSZ, fp)) {
472.  		if (*buf == '#')
473.  			continue;
474.  
475.  		/* remove trailing whitespace
476.  		 */
477.  		bufp = index(buf, '\n');
478.  		while (bufp > buf && isspace(*bufp))
479.  			bufp--;
480.  		if (bufp == buf)
481.  			continue;		/* skip all-blank lines */
482.  		else
483.  			*(bufp + 1) = 0;	/* 0 terminate line */
484.  
485.  		/* find the '=' */
486.  		if (!(bufp = strchr(buf, '='))) {
487.  			msmsg("Bad option line: '%s'\n", buf);
488.  			getreturn("to continue");
489.  			continue;
490.  		}
491.  		
492.  		/* skip  whitespace between '=' and value */
493.  		while (isspace(*++bufp))
494.  			;
495.  
496.  		/* Go through possible variables */
497.  		if (!strncmp(buf, "HACKDIR", 4)) {
498.  			strncpy(hackdir, bufp, PATHLEN);
499.  		
500.  		} else if (!strncmp(buf, "RAMDISK", 3)) {
501.  			strncpy(tmp_ramdisk, bufp, PATHLEN);
502.  
503.  		} else if (!strncmp(buf, "LEVELS", 4)) {
504.  			strncpy(tmp_levels, bufp, PATHLEN);
505.  
506.  		} else if (!strncmp(buf, "OPTIONS", 4)) {
507.  			parseoptions(bufp, TRUE);
508.  			if (plname[0])		/* If a name was given */
509.  				plnamesuffix();	/* set the character class */
510.  
511.  		} else if (!strncmp(buf, "SAVE", 4)) {
512.  			char *ptr;
513.  			if (ptr = index(bufp, ';')) {
514.  				*ptr = '\0';
515.  				if (*(ptr+1) == 'n' || *(ptr+1) == 'N')
516.  					saveprompt = FALSE;
517.  			}
518.  			(void) strncpy(SAVEF, bufp, PATHLEN);
519.  			append_slash(SAVEF);
520.  #ifdef GRAPHICS
521.  		} else if (!strncmp(buf, "GRAPHICS", 4)) {
522.  			char translate[17];
523.  			short i;
524.  
525.  		     if ((i = sscanf(bufp, "%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u",
526.  				&translate[0], &translate[1], &translate[2],
527.  				&translate[3], &translate[4], &translate[5],
528.  				&translate[6], &translate[7], &translate[8],
529.  				&translate[9], &translate[10], &translate[11],
530.  				&translate[12], &translate[13], &translate[14],
531.  				&translate[15], &translate[16])) < 0) {
532.  					msmsg ("Syntax error in GRAPHICS\n");
533.  					getreturn("to continue");
534.  			}
535.  			translate[i] = '\0';
536.  #endif /* GRAPHICS /**/
537.  /*
538.   * You could have problems here if you configure FOUNTAINS, SPIDERS or NEWCLASS
539.   * in or out and forget to change the tail entries in your graphics string.
540.   */
541.  #define SETPCHAR(f, n)	showsyms.f = (strlen(translate) > n) ? translate[n] : defsyms.f
542.  			SETPCHAR(stone, 0);
543.  			SETPCHAR(vwall, 1);
544.  			SETPCHAR(hwall, 2);
545.  			SETPCHAR(tlcorn, 3);
546.  			SETPCHAR(trcorn, 4);
547.  			SETPCHAR(blcorn, 5);
548.  			SETPCHAR(brcorn, 6);
549.  			SETPCHAR(door, 7);
550.  			SETPCHAR(room, 8);
551.  			SETPCHAR(corr, 9);
552.  			SETPCHAR(upstair, 10);
553.  			SETPCHAR(dnstair, 11);
554.  			SETPCHAR(trap, 12);
555.  #ifdef FOUNTAINS
556.  			SETPCHAR(pool, 13);
557.  			SETPCHAR(fountain, 14);
558.  #endif
559.  #ifdef NEWCLASS
560.  			SETPCHAR(throne, 15);
561.  #endif
562.  #ifdef SPIDERS
563.  			SETPCHAR(web, 16);
564.  #endif
565.  #undef SETPCHAR
566.  		} else {
567.  			msmsg("Bad option line: '%s'\n", buf);
568.  			getreturn("to continue");
569.  		}
570.  	}
571.  	fclose(fp);
572.  
573.  	strcpy(permbones, tmp_levels);
574.  	if (tmp_ramdisk[0]) {
575.  		strcpy(levels, tmp_ramdisk);
576.  		if (strcmpi(permbones, levels))		/* if not identical */
577.  			ramdisk = TRUE;
578.  	} else
579.  		strcpy(levels, tmp_levels);
580.  	strcpy(bones, levels);
581.  }
582.  
583.  /* Set names for bones[] and lock[]
584.   */
585.  void
586.  set_lock_and_bones() {
587.  	if (!ramdisk) {
588.  		strcpy(levels, permbones);
589.  		strcpy(bones, permbones);
590.  	}
591.  	append_slash(permbones);
592.  	append_slash(levels);
593.  	append_slash(bones);
594.  	strcat(bones, allbones);
595.  	strcpy(lock, levels);
596.  	strcat(lock, alllevels);
597.  }
598.  
599.  /* Add a backslash to any name not ending in /, \ or :   There must
600.   * be room for the \
601.   */
602.  void
603.  append_slash(name)
604.  char *name;
605.  {
606.  	char *ptr;
607.  
608.  	if (!*name)
609.  		return;
610.  	ptr = name + (strlen(name) - 1);
611.  	if (*ptr != '\\' && *ptr != '/' && *ptr != ':') {
612.  		*++ptr = '\\';
613.  		*++ptr = '\0';
614.  	}
615.  }
616.  
617.  
618.  void
619.  getreturn(str)
620.  char *str;
621.  {
622.  	int ch;
623.  
624.  	msmsg("Hit <RETURN> %s.", str);
625.  	while ((ch = getchar()) != '\n')
626.  		;
627.  }
628.  
629.  void
630.  msmsg(fmt, a1, a2, a3)
631.  char *fmt;
632.  long a1, a2, a3;
633.  {
634.  	printf(fmt, a1, a2, a3);
635.  	flushout();
636.  }
637.  
638.  /* Chdrive() changes the default drive.
639.   */
640.  #define SELECTDISK	0x0E
641.  void
642.  chdrive(str)
643.  char *str;
644.  {
645.  	char *ptr;
646.  	union REGS inregs;
647.  	char drive;
648.  
649.  	if ((ptr = index(str, ':')) != NULL) {
650.  		drive = toupper(*(ptr - 1));
651.  		inregs.h.ah = SELECTDISK;
652.  		inregs.h.dl = drive - 'A';
653.  		intdos(&inregs, &inregs);
654.  	}
655.  }
656.  
657.  /* Use the IOCTL DOS function call to change stdin and stdout to raw
658.   * mode.  For stdin, this prevents MSDOS from trapping ^P, thus
659.   * freeing us of ^P toggling 'echo to printer'.
660.   * Thanks to Mark Zbikowski (markz@microsoft.UUCP).
661.   */
662.  
663.  #  define DEVICE	0x80
664.  #  define RAW		0x20
665.  #  define IOCTL		0x44
666.  #  define STDIN		fileno(stdin)
667.  #  define STDOUT	fileno(stdout)
668.  #  define GETBITS	0
669.  #  define SETBITS	1
670.  
671.  static unsigned	old_stdin, old_stdout, ioctl();
672.  
673.  disable_ctrlP() {
674.  	if (!flags.rawio)
675.  		return;
676.  	old_stdin = ioctl(STDIN, GETBITS, 0);
677.  	old_stdout = ioctl(STDOUT, GETBITS, 0);
678.  	if (old_stdin & DEVICE)
679.  		ioctl(STDIN, SETBITS, old_stdin | RAW);
680.  	if (old_stdout & DEVICE)
681.  		ioctl(STDOUT, SETBITS, old_stdout | RAW);
682.  }
683.  
684.  enable_ctrlP() {
685.  	if (!flags.rawio)
686.  		return;
687.  	if (old_stdin)
688.  		(void) ioctl(STDIN, SETBITS, old_stdin);
689.  	if (old_stdout)
690.  		(void) ioctl(STDOUT, SETBITS, old_stdout);
691.  }
692.  
693.  static unsigned
694.  ioctl(handle, mode, setvalue)
695.  unsigned setvalue;
696.  {
697.  	union REGS regs;
698.  
699.  	regs.h.ah = IOCTL;
700.  	regs.h.al = mode;
701.  	regs.x.bx = handle;
702.  	regs.h.dl = setvalue;
703.  	regs.h.dh = 0;			/* Zero out dh */
704.  	intdos(&regs, &regs);
705.  	return (regs.x.dx);
706.  }
707.  
708.  /* Follow the PATH, trying to fopen the file.
709.   */
710.  #define PATHSEP	';'
711.  
712.  FILE *
713.  fopenp(name, mode)
714.  char *name, *mode;
715.  {
716.  	char buf[BUFSIZ], *bp, *pp, *getenv(), lastch;
717.  	FILE *fp;
718.  
719.  	/* Try the default directory first.  Then look along PATH.
720.  	 */
721.  	strcpy(buf, name);
722.  	if (fp = fopen(buf, mode))
723.  		return fp;
724.  	else {
725.  		pp = getenv("PATH");
726.  		while (pp && *pp) {
727.  			bp = buf;
728.  			while (*pp && *pp != PATHSEP)
729.  				lastch = *bp++ = *pp++;
730.  			if (lastch != '\\' && lastch != '/')
731.  				*bp++ = '\\';
732.  			strcpy(bp, name);
733.  			if (fp = fopen(buf, mode))
734.  				return fp;
735.  			if (*pp)
736.  				pp++;
737.  		}
738.  	}
739.  	return NULL;
740.  }
741.  # endif /* DGK */
742.  
743.  /* Chdir back to original directory
744.   */
745.  # undef exit
746.  void
747.  msexit(code)
748.  {
749.  # ifdef CHDIR
750.  	extern char orgdir[];
751.  # endif
752.  
753.  # ifdef DGK
754.  	flushout();
755.  	enable_ctrlP();		/* in case this wasn't done */
756.  	if (ramdisk)
757.  		copybones(TOPERM);
758.  # endif
759.  # ifdef CHDIR
760.  	chdir(orgdir);		/* chdir, not chdirx */
761.  #  ifdef DGK
762.  	chdrive(orgdir);
763.  #  endif
764.  # endif
765.  	exit(code);
766.  }
767.  #endif /* MSDOS */