Source:SLASH'EM 0.0.7E7F2/files.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to files.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[Source:SLASH'EM 0.0.7E7F2/files.c#line123]], for example.

Source code for vanilla NetHack is at Source code.


The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

1.    /*	SCCS Id: @(#)files.c	3.4	2003/11/14	*/
2.    /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3.    /* NetHack may be freely redistributed.  See license for details. */
4.    
5.    #include "hack.h"
6.    #include "dlb.h"
7.    
8.    /* WAC for config file */
9.    #include "filename.h"
10.   /* needs to be after hack.h. Caused .slashemrc to never be read on UNIX */
11.   
12.   #ifdef TTY_GRAPHICS
13.   #include "wintty.h" /* more() */
14.   #endif
15.   
16.   #if defined(GL_GRAPHICS) || defined(SDL_GRAPHICS)
17.   #include "winGL.h"  /* Sdlgl_parse_options */
18.   #endif
19.   
20.   #ifdef PROXY_GRAPHICS
21.   #include "winproxy.h" /* proxy_config_open() */
22.   #endif
23.   
24.   #include <ctype.h>
25.   
26.   #if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
27.   #include <fcntl.h>
28.   #endif
29.   
30.   #include <errno.h>
31.   #ifdef _MSC_VER	/* MSC 6.0 defines errno quite differently */
32.   # if (_MSC_VER >= 600)
33.   #  define SKIP_ERRNO
34.   # endif
35.   #else
36.   # ifdef NHSTDC
37.   #  define SKIP_ERRNO
38.   # endif
39.   #endif
40.   #ifndef SKIP_ERRNO
41.   # ifdef _DCC
42.   const
43.   # endif
44.   extern int errno;
45.   #endif
46.   
47.   #if defined(UNIX) && defined(QT_GRAPHICS)
48.   #include <dirent.h>
49.   #endif
50.   
51.   #if defined(UNIX) || defined(VMS)
52.   #include <signal.h>
53.   #endif
54.   
55.   #ifndef NO_SIGNAL
56.   #include <signal.h>
57.   #endif
58.   
59.   /* WAC moved to below
60.   #include <sys\stat.h>
61.   */
62.   
63.   /* ALI: For compatibility */
64.   #ifndef FILE_AREAS
65.   #define compress(file)		compress_area(NULL, file)
66.   #define uncompress(file)	uncompress_area(NULL, file)
67.   #endif
68.   
69.   #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
70.   # ifndef GNUDOS
71.   #include <sys\stat.h>
72.   # else
73.   #include <sys/stat.h>
74.   # endif
75.   #endif
76.   #ifndef O_BINARY	/* used for micros, no-op for others */
77.   # define O_BINARY 0
78.   #endif
79.   
80.   #ifdef PREFIXES_IN_USE
81.   #define FQN_NUMBUF 4
82.   static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
83.   #endif
84.   
85.   #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
86.   char bones[] = "bonesnn.xxx";
87.   char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */
88.   #else
89.   # if defined(MFLOPPY)
90.   char bones[FILENAMELEN];           /* pathname of bones files */
91.   char lock[FILENAMELEN];            /* pathname of level files */
92.   # endif
93.   # if defined(VMS)
94.   char bones[] = "bonesnn.xxx;1";
95.   char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */
96.   # endif
97.   # if defined(WIN32)
98.   char bones[] = "bonesnn.xxx";
99.   char lock[PL_NSIZ+25];		/* long enough for username+-+name+.99 */
100.  # endif
101.  #endif
102.  
103.  #if defined(UNIX) || defined(__BEOS__)
104.  #define SAVESIZE	(PL_NSIZ + 13)	/* save/99999player.e */
105.  #else
106.  # ifdef VMS
107.  #define SAVESIZE	(PL_NSIZ + 22)	/* [.save]<uid>player.e;1 */
108.  # else
109.  #  if defined(WIN32)
110.  #define SAVESIZE	(PL_NSIZ + 40)	/* username-player.NetHack-saved-game */
111.  #  else
112.  #define SAVESIZE        FILENAMELEN        /* from macconf.h or pcconf.h */
113.  #  endif
114.  # endif
115.  #endif
116.  
117.  char SAVEF[SAVESIZE];	/* holds relative path of save file from playground */
118.  #ifdef MICRO
119.  char SAVEP[SAVESIZE];	/* holds path of directory for save file */
120.  #endif
121.  
122.  #ifdef HOLD_LOCKFILE_OPEN
123.  struct level_ftrack {
124.  int init;
125.  int fd;					/* file descriptor for level file     */
126.  int oflag;				/* open flags                         */
127.  boolean nethack_thinks_it_is_open;	/* Does NetHack think it's open?       */
128.  } lftrack;
129.  # if defined(WIN32)
130.  #include <share.h>
131.  # endif
132.  #endif /*HOLD_LOCKFILE_OPEN*/
133.  
134.  #ifdef WIZARD
135.  #define WIZKIT_MAX 128
136.  static char wizkit[WIZKIT_MAX];
137.  STATIC_DCL FILE *NDECL(fopen_wizkit_file);
138.  #endif
139.  
140.  #ifdef AMIGA
141.  extern char PATH[];	/* see sys/amiga/amidos.c */
142.  extern char bbs_id[];
143.  static int lockptr;
144.  # ifdef __SASC_60
145.  #include <proto/dos.h>
146.  # endif
147.  
148.  #include <libraries/dos.h>
149.  extern void FDECL(amii_set_text_font, ( char *, int ));
150.  #endif
151.  
152.  #if defined(WIN32) || defined(MSDOS)
153.  static int lockptr;
154.  # ifdef MSDOS
155.  #define Delay(a) msleep(a)
156.  # endif
157.  #define Close close
158.  #ifndef WIN_CE
159.  #define DeleteFile unlink
160.  #endif
161.  #endif
162.  
163.  #ifdef MAC
164.  # define unlink macunlink
165.  #endif
166.  
167.  #ifdef USER_SOUNDS
168.  extern char *sounddir;
169.  #endif
170.  
171.  extern int n_dgns;		/* from dungeon.c */
172.  
173.  STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*));
174.  STATIC_DCL char *NDECL(set_bonestemp_name);
175.  #ifdef COMPRESS
176.  STATIC_DCL void FDECL(redirect, (const char *,const char *,const char *,
177.  	FILE *,BOOLEAN_P));
178.  STATIC_DCL void FDECL(docompress_file, (const char *,const char *,BOOLEAN_P));
179.  #endif
180.  #ifndef FILE_AREAS
181.  STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
182.  #endif
183.  STATIC_DCL FILE *FDECL(fopen_config_file, (const char *));
184.  STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,BOOLEAN_P,int,const char *));
185.  int FDECL(parse_config_line, (FILE *,char *,char *,char *));
186.  #ifdef NOCWD_ASSUMPTIONS
187.  STATIC_DCL void FDECL(adjust_prefix, (char *, int));
188.  #endif
189.  #ifdef SELF_RECOVER
190.  STATIC_DCL boolean FDECL(copy_bytes, (int, int));
191.  #endif
192.  #ifdef HOLD_LOCKFILE_OPEN
193.  STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int));
194.  #endif
195.  
196.  /*
197.   * fname_encode()
198.   *
199.   *   Args:
200.   *	legal		zero-terminated list of acceptable file name characters
201.   *	quotechar	lead-in character used to quote illegal characters as hex digits
202.   *	s		string to encode
203.   *	callerbuf	buffer to house result
204.   *	bufsz		size of callerbuf
205.   *
206.   *   Notes:
207.   *	The hex digits 0-9 and A-F are always part of the legal set due to
208.   *	their use in the encoding scheme, even if not explicitly included in 'legal'.
209.   *
210.   *   Sample:
211.   *	The following call:
212.   *	    (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
213.   *				'%', "This is a % test!", buf, 512);
214.   *	results in this encoding:
215.   *	    "This%20is%20a%20%25%20test%21"
216.   */
217.  char *
218.  fname_encode(legal, quotechar, s, callerbuf, bufsz)
219.  const char *legal;
220.  char quotechar;
221.  char *s, *callerbuf;
222.  int bufsz;
223.  {
224.  	char *sp, *op;
225.  	int cnt = 0;
226.  	static char hexdigits[] = "0123456789ABCDEF";
227.  
228.  	sp = s;
229.  	op = callerbuf;
230.  	*op = '\0';
231.  	
232.  	while (*sp) {
233.  		/* Do we have room for one more character or encoding? */
234.  		if ((bufsz - cnt) <= 4) return callerbuf;
235.  
236.  		if (*sp == quotechar) {
237.  			(void)sprintf(op, "%c%02X", quotechar, *sp);
238.  			 op += 3;
239.  			 cnt += 3;
240.  		} else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) {
241.  			*op++ = *sp;
242.  			*op = '\0';
243.  			cnt++;
244.  		} else {
245.  			(void)sprintf(op,"%c%02X", quotechar, *sp);
246.  			op += 3;
247.  			cnt += 3;
248.  		}
249.  		sp++;
250.  	}
251.  	return callerbuf;
252.  }
253.  
254.  /*
255.   * fname_decode()
256.   *
257.   *   Args:
258.   *	quotechar	lead-in character used to quote illegal characters as hex digits
259.   *	s		string to decode
260.   *	callerbuf	buffer to house result
261.   *	bufsz		size of callerbuf
262.   */
263.  char *
264.  fname_decode(quotechar, s, callerbuf, bufsz)
265.  char quotechar;
266.  char *s, *callerbuf;
267.  int bufsz;
268.  {
269.  	char *sp, *op;
270.  	int k,calc,cnt = 0;
271.  	static char hexdigits[] = "0123456789ABCDEF";
272.  
273.  	sp = s;
274.  	op = callerbuf;
275.  	*op = '\0';
276.  	calc = 0;
277.  
278.  	while (*sp) {
279.  		/* Do we have room for one more character? */
280.  		if ((bufsz - cnt) <= 2) return callerbuf;
281.  		if (*sp == quotechar) {
282.  			sp++;
283.  			for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break;
284.  			if (k >= 16) return callerbuf;	/* impossible, so bail */
285.  			calc = k << 4; 
286.  			sp++;
287.  			for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break;
288.  			if (k >= 16) return callerbuf;	/* impossible, so bail */
289.  			calc += k; 
290.  			sp++;
291.  			*op++ = calc;
292.  			*op = '\0';
293.  		} else {
294.  			*op++ = *sp++;
295.  			*op = '\0';
296.  		}
297.  		cnt++;
298.  	}
299.  	return callerbuf;
300.  }
301.  
302.  #ifndef PREFIXES_IN_USE
303.  /*ARGSUSED*/
304.  #endif
305.  const char *
306.  fqname(basename, whichprefix, buffnum)
307.  const char *basename;
308.  int whichprefix, buffnum;
309.  {
310.  #ifndef PREFIXES_IN_USE
311.  	return basename;
312.  #else
313.  	if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
314.  		return basename;
315.  	if (!fqn_prefix[whichprefix])
316.  		return basename;
317.  	if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
318.  		impossible("Invalid fqn_filename_buffer specified: %d",
319.  								buffnum);
320.  		buffnum = 0;
321.  	}
322.  	if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >=
323.  						    FQN_MAX_FILENAME) {
324.  		impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
325.  						basename);
326.  		return basename;	/* XXX */
327.  	}
328.  	Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
329.  	return strcat(fqn_filename_buffer[buffnum], basename);
330.  #endif
331.  }
332.  
333.  /* reasonbuf must be at least BUFSZ, supplied by caller */
334.  /*ARGSUSED*/
335.  int
336.  validate_prefix_locations(reasonbuf)
337.  char *reasonbuf;
338.  {
339.  #if defined(NOCWD_ASSUMPTIONS)
340.  	FILE *fp;
341.  	const char *filename;
342.  	int prefcnt, failcount = 0;
343.  	char panicbuf1[BUFSZ], panicbuf2[BUFSZ], *details;
344.  
345.  	if (reasonbuf) reasonbuf[0] = '\0';
346.  	for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) {
347.  		/* don't test writing to configdir or datadir; they're readonly */
348.  		if (prefcnt == CONFIGPREFIX || prefcnt == DATAPREFIX) continue;
349.  		filename = fqname("validate", prefcnt, 3);
350.  		if ((fp = fopen(filename, "w"))) {
351.  			fclose(fp);
352.  			(void) unlink(filename);
353.  		} else {
354.  			if (reasonbuf) {
355.  				if (failcount) Strcat(reasonbuf,", ");
356.  				Strcat(reasonbuf, fqn_prefix_names[prefcnt]);
357.  			}
358.  			/* the paniclog entry gets the value of errno as well */
359.  			Sprintf(panicbuf1,"Invalid %s", fqn_prefix_names[prefcnt]);
360.  #if defined (NHSTDC) && !defined(NOTSTDC)
361.  			if (!(details = strerror(errno)))
362.  #endif
363.  			details = "";
364.  			Sprintf(panicbuf2,"\"%s\", (%d) %s",
365.  				fqn_prefix[prefcnt], errno, details);
366.  			paniclog(panicbuf1, panicbuf2);
367.  			failcount++;
368.  		}	
369.  	}
370.  	if (failcount)
371.  		return 0;
372.  	else
373.  #endif
374.  	return 1;
375.  }
376.  
377.  /*
378.   * When file areas are in use, fopen_datafile_area is used instead.
379.   */
380.  
381.  #ifndef FILE_AREA
382.  /* fopen a file, with OS-dependent bells and whistles */
383.  /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
384.  FILE *
385.  fopen_datafile(filename, mode, prefix)
386.  const char *filename, *mode;
387.  int prefix;
388.  {
389.  	FILE *fp;
390.  
391.  	filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0);
392.  #ifdef VMS	/* essential to have punctuation, to avoid logical names */
393.      {
394.  	char tmp[BUFSIZ];
395.  
396.  	if (!index(filename, '.') && !index(filename, ';'))
397.  		filename = strcat(strcpy(tmp, filename), ";0");
398.  	fp = fopen(filename, mode, "mbc=16");
399.      }
400.  #else
401.  	fp = fopen(filename, mode);
402.  #endif
403.  	return fp;
404.  }
405.  #endif	/* FILE_AREA */
406.  
407.  /* ----------  BEGIN LEVEL FILE HANDLING ----------- */
408.  
409.  #ifdef MFLOPPY
410.  /* Set names for bones[] and lock[] */
411.  void
412.  set_lock_and_bones()
413.  {
414.  	if (!ramdisk) {
415.  		Strcpy(levels, permbones);
416.  		Strcpy(bones, permbones);
417.  	}
418.  	append_slash(permbones);
419.  	append_slash(levels);
420.  #ifdef AMIGA
421.  	strncat(levels, bbs_id, PATHLEN);
422.  #endif
423.  	append_slash(bones);
424.  	Strcat(bones, "bonesnn.*");
425.  	Strcpy(lock, levels);
426.  #ifndef AMIGA
427.  	Strcat(lock, alllevels);
428.  #endif
429.  	return;
430.  }
431.  #endif /* MFLOPPY */
432.  
433.  
434.  /* Construct a file name for a level-type file, which is of the form
435.   * something.level (with any old level stripped off).
436.   * This assumes there is space on the end of 'file' to append
437.   * a two digit number.  This is true for 'level'
438.   * but be careful if you use it for other things -dgk
439.   */
440.  void
441.  set_levelfile_name(file, lev)
442.  char *file;
443.  int lev;
444.  {
445.  	char *tf;
446.  
447.  	tf = rindex(file, '.');
448.  	if (!tf) tf = eos(file);
449.  	Sprintf(tf, ".%d", lev);
450.  #ifdef VMS
451.  	Strcat(tf, ";1");
452.  #endif
453.  	return;
454.  }
455.  
456.  int
457.  create_levelfile(lev, errbuf)
458.  int lev;
459.  char errbuf[];
460.  {
461.  	int fd;
462.  #ifndef FILE_AREAS
463.  	const char *fq_lock;
464.  #endif
465.  
466.  	if (errbuf) *errbuf = '\0';
467.  	set_levelfile_name(lock, lev);
468.  #ifndef FILE_AREAS
469.  	fq_lock = fqname(lock, LEVELPREFIX, 0);
470.  #endif
471.  
472.  #if defined(MICRO) || defined(WIN32)
473.  	/* Use O_TRUNC to force the file to be shortened if it already
474.  	 * exists and is currently longer.
475.  	 */
476.  # ifdef FILE_AREAS
477.  	fd = open_area(FILE_AREA_LEVL, lock,
478.  	  O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
479.  # else
480.  #  ifdef HOLD_LOCKFILE_OPEN
481.  	if (lev == 0)
482.  	    fd = open_levelfile_exclusively(fq_lock, lev,
483.  			    O_WRONLY |O_CREAT | O_TRUNC | O_BINARY);
484.  	else
485.  #  endif
486.  	fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
487.  # endif
488.  #else	/* MICRO */
489.  # ifdef FILE_AREAS
490.  	fd = creat_area(FILE_AREA_LEVL, lock, FCMASK);
491.  # else
492.  # ifdef MAC
493.  	fd = maccreat(fq_lock, LEVL_TYPE);
494.  # else
495.  	fd = creat(fq_lock, FCMASK);
496.  # endif
497.  # endif	/* FILE_AREAS */
498.  #endif /* MICRO || WIN32 */
499.  
500.  	if (fd >= 0)
501.  	    level_info[lev].flags |= LFILE_EXISTS;
502.  	else if (errbuf)        /* failure explanation */
503.  	    Sprintf(errbuf,
504.  		    "Cannot create file \"%s\" for level %d (errno %d).",
505.  		    lock, lev, errno);
506.  
507.  	return fd;
508.  }
509.  
510.  
511.  int
512.  open_levelfile(lev, errbuf)
513.  int lev;
514.  char errbuf[];
515.  {
516.  	int fd;
517.  #ifndef FILE_AREAS
518.  	const char *fq_lock;
519.  #endif
520.  
521.  	if (errbuf) *errbuf = '\0';
522.  	set_levelfile_name(lock, lev);
523.  #ifndef FILE_AREAS
524.  	fq_lock = fqname(lock, LEVELPREFIX, 0);
525.  #endif
526.  #ifdef MFLOPPY
527.  	/* If not currently accessible, swap it in. */
528.  	if (level_info[lev].where != ACTIVE)
529.  		swapin_file(lev);
530.  #endif
531.  #ifdef FILE_AREAS
532.  	fd = open_area(FILE_AREA_LEVL, lock, O_RDONLY | O_BINARY, 0);
533.  #else
534.  # ifdef MAC
535.  	fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
536.  # else
537.  #  ifdef HOLD_LOCKFILE_OPEN
538.  	if (lev == 0)
539.  		fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY );
540.  	else
541.  #  endif
542.  	fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
543.  # endif
544.  #endif	/* FILE_AREAS */
545.  
546.  	/* for failure, return an explanation that our caller can use;
547.  	   settle for `lock' instead of `fq_lock' because the latter
548.  	   might end up being too big for nethack's BUFSZ */
549.  	if (fd < 0 && errbuf)
550.  	    Sprintf(errbuf,
551.  		    "Cannot open file \"%s\" for level %d (errno %d).",
552.  		    lock, lev, errno);
553.  
554.  	return fd;
555.  }
556.  
557.  
558.  void
559.  delete_levelfile(lev)
560.  int lev;
561.  {
562.  	/*
563.  	 * Level 0 might be created by port specific code that doesn't
564.  	 * call create_levfile(), so always assume that it exists.
565.  	 */
566.  	if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
567.  		set_levelfile_name(lock, lev);
568.  #ifdef FILE_AREAS
569.  		(void) remove_area(FILE_AREA_LEVL, lock, 0);
570.  #else
571.  # ifdef HOLD_LOCKFILE_OPEN
572.  		if (lev == 0) really_close();
573.  # endif
574.  		(void) unlink(fqname(lock, LEVELPREFIX, 0));
575.  #endif
576.  		level_info[lev].flags &= ~LFILE_EXISTS;
577.  	}
578.  }
579.  
580.  
581.  void
582.  clearlocks()
583.  {
584.  /* [Tom] Watcom.....
585.  #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
586.  	eraseall(levels, alllevels);
587.  	if (ramdisk)
588.  		eraseall(permbones, alllevels);
589.  #else
590.  	register int x;
591.  
592.  # if defined(UNIX) || defined(VMS)
593.  	(void) signal(SIGHUP, SIG_IGN);
594.  # endif */
595.  	/* can't access maxledgerno() before dungeons are created -dlc */
596.  	int x;
597.  	for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
598.  		delete_levelfile(x);	/* not all levels need be present */
599.  /* #endif*/
600.  }
601.  
602.  #ifdef HOLD_LOCKFILE_OPEN
603.  STATIC_OVL int
604.  open_levelfile_exclusively(name, lev, oflag)
605.  const char *name;
606.  int lev, oflag;
607.  {
608.  	int reslt, fd;
609.  	if (!lftrack.init) {
610.  		lftrack.init = 1;
611.  		lftrack.fd = -1;
612.  	}
613.  	if (lftrack.fd >= 0) {
614.  		/* check for compatible access */
615.  		if (lftrack.oflag == oflag) {
616.  			fd = lftrack.fd;
617.  			reslt = lseek(fd, 0L, SEEK_SET);
618.  			if (reslt == -1L)
619.  			    panic("open_levelfile_exclusively: lseek failed %d", errno);
620.  			lftrack.nethack_thinks_it_is_open = TRUE;
621.  		} else {
622.  			really_close();
623.  			fd = sopen(name, oflag,SH_DENYRW, FCMASK);
624.  			lftrack.fd = fd;
625.  			lftrack.oflag = oflag;
626.  			lftrack.nethack_thinks_it_is_open = TRUE;
627.  		}
628.  	} else {
629.  			fd = sopen(name, oflag,SH_DENYRW, FCMASK);
630.  			lftrack.fd = fd;
631.  			lftrack.oflag = oflag;
632.  			if (fd >= 0)
633.  			    lftrack.nethack_thinks_it_is_open = TRUE;
634.  	}
635.  	return fd;
636.  }
637.  
638.  void
639.  really_close()
640.  {
641.  	int fd = lftrack.fd;
642.  	lftrack.nethack_thinks_it_is_open = FALSE;
643.  	lftrack.fd = -1;
644.  	lftrack.oflag = 0;
645.  	(void)_close(fd);
646.  	return;
647.  }
648.  
649.  int
650.  close(fd)
651.  int fd;
652.  {
653.   	if (lftrack.fd == fd) {
654.  		really_close();	/* close it, but reopen it to hold it */
655.  		fd = open_levelfile(0, (char *)0);
656.  		lftrack.nethack_thinks_it_is_open = FALSE;
657.  		return 0;
658.  	}
659.  	return _close(fd);
660.  }
661.  #endif
662.  	
663.  /* ----------  END LEVEL FILE HANDLING ----------- */
664.  
665.  
666.  /* ----------  BEGIN BONES FILE HANDLING ----------- */
667.  
668.  /* set up "file" to be file name for retrieving bones, and return a
669.   * bonesid to be read/written in the bones file.
670.   */
671.  STATIC_OVL char *
672.  set_bonesfile_name(file, lev)
673.  char *file;
674.  d_level *lev;
675.  {
676.  	s_level *sptr;
677.  	char *dptr;
678.  
679.  	Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
680.  			In_quest(lev) ? urole.filecode : "0");
681.  	dptr = eos(file);
682.  	if ((sptr = Is_special(lev)) != 0)
683.  	    Sprintf(dptr, ".%c", sptr->boneid);
684.  	else
685.  	    Sprintf(dptr, ".%d", lev->dlevel);
686.  #ifdef VMS
687.  	Strcat(dptr, ";1");
688.  #endif
689.  	return(dptr-2);
690.  }
691.  
692.  /* set up temporary file name for writing bones, to avoid another game's
693.   * trying to read from an uncompleted bones file.  we want an uncontentious
694.   * name, so use one in the namespace reserved for this game's level files.
695.   * (we are not reading or writing level files while writing bones files, so
696.   * the same array may be used instead of copying.)
697.   */
698.  STATIC_OVL char *
699.  set_bonestemp_name()
700.  {
701.  	char *tf;
702.  
703.  	tf = rindex(lock, '.');
704.  	if (!tf) tf = eos(lock);
705.  	Sprintf(tf, ".bn");
706.  #ifdef VMS
707.  	Strcat(tf, ";1");
708.  #endif
709.  	return lock;
710.  }
711.  
712.  int
713.  create_bonesfile(lev, bonesid, errbuf)
714.  d_level *lev;
715.  char **bonesid;
716.  char errbuf[];
717.  {
718.  	const char *file;
719.  	int fd;
720.  
721.  	if (errbuf) *errbuf = '\0';
722.  	*bonesid = set_bonesfile_name(bones, lev);
723.  	file = set_bonestemp_name();
724.  #ifndef FILE_AREAS
725.  	file = fqname(file, BONESPREFIX, 0);
726.  #endif
727.  
728.  #if defined(MICRO) || defined(WIN32)
729.  	/* Use O_TRUNC to force the file to be shortened if it already
730.  	 * exists and is currently longer.
731.  	 */
732.  # ifdef FILE_AREAS
733.  	fd = open_area(FILE_AREA_BONES, file,
734.  	  O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
735.  # else
736.  	fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
737.  # endif
738.  #else
739.  # ifdef FILE_AREAS
740.  	fd = creat_area(FILE_AREA_BONES, file, FCMASK);
741.  # else
742.  # ifdef MAC
743.  	fd = maccreat(file, BONE_TYPE);
744.  # else
745.  	fd = creat(file, FCMASK);
746.  # endif
747.  # endif	/* FILE_AREAS */
748.  #endif
749.  	if (fd < 0 && errbuf) /* failure explanation */
750.  	    Sprintf(errbuf,
751.  		    "Cannot create bones \"%s\", id %s (errno %d).",
752.  		    lock, *bonesid, errno);
753.  
754.  # if defined(VMS) && !defined(SECURE)
755.  	/*
756.  	   Re-protect bones file with world:read+write+execute+delete access.
757.  	   umask() doesn't seem very reliable; also, vaxcrtl won't let us set
758.  	   delete access without write access, which is what's really wanted.
759.  	   Can't simply create it with the desired protection because creat
760.  	   ANDs the mask with the user's default protection, which usually
761.  	   denies some or all access to world.
762.  	 */
763.  #  ifndef FILE_AREAS
764.  	(void) chmod(file, FCMASK | 007);  /* allow other users full access */
765.  #  else
766.  	(void) chmod_area(FILE_AREA_BONES, file, FCMASK | 007);
767.  #  endif
768.  # endif /* VMS && !SECURE */
769.  
770.  	return fd;
771.  }
772.  
773.  #ifdef MFLOPPY
774.  /* remove partial bonesfile in process of creation */
775.  void
776.  cancel_bonesfile()
777.  {
778.  	const char *tempname;
779.  
780.  	tempname = set_bonestemp_name();
781.  # ifdef FILE_AREAS
782.  	(void) remove_area(FILE_AREA_BONES, tempname);
783.  # else
784.  	tempname = fqname(tempname, BONESPREFIX, 0);
785.  	(void) unlink(tempname);
786.  # endif
787.  }
788.  #endif /* MFLOPPY */
789.  
790.  /* move completed bones file to proper name */
791.  void
792.  commit_bonesfile(lev)
793.  d_level *lev;
794.  {
795.  	const char *fq_bones, *tempname;
796.  	int ret;
797.  
798.  	(void) set_bonesfile_name(bones, lev);
799.  #ifndef FILE_AREAS
800.  	fq_bones = fqname(bones, BONESPREFIX, 0);
801.  #endif
802.  	tempname = set_bonestemp_name();
803.  #ifndef FILE_AREAS
804.  	tempname = fqname(tempname, BONESPREFIX, 1);
805.  #endif
806.  
807.  #ifdef FILE_AREAS
808.  	ret = rename_area(FILE_AREA_BONES, tempname, bones);
809.  #else
810.  # if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
811.  	/* old SYSVs don't have rename.  Some SVR3's may, but since they
812.  	 * also have link/unlink, it doesn't matter. :-)
813.  	 */
814.  	(void) unlink(fq_bones);
815.  	ret = link(tempname, fq_bones);
816.  	ret += unlink(tempname);
817.  # else
818.  	ret = rename(tempname, fq_bones);
819.  # endif
820.  #endif	/* FILE_AREAS */
821.  #ifdef WIZARD
822.  	if (wizard && ret != 0)
823.  #ifdef FILE_AREAS
824.  		pline("couldn't rename %s to %s.", tempname, bones);
825.  #else
826.  		pline("couldn't rename %s to %s.", tempname, fq_bones);
827.  #endif
828.  #endif
829.  }
830.  
831.  
832.  int
833.  open_bonesfile(lev, bonesid)
834.  d_level *lev;
835.  char **bonesid;
836.  {
837.  	const char *fq_bones;
838.  	int fd;
839.  
840.  	*bonesid = set_bonesfile_name(bones, lev);
841.  #ifdef FILE_AREAS
842.  	uncompress_area(FILE_AREA_BONES, bones);  /* no effect if nonexistent */
843.  	fd = open_area(FILE_AREA_BONES, bones, O_RDONLY | O_BINARY, 0);
844.  #else
845.  	fq_bones = fqname(bones, BONESPREFIX, 0);
846.  	uncompress(fq_bones);	/* no effect if nonexistent */
847.  # ifdef MAC
848.  	fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
849.  # else
850.  	fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
851.  # endif
852.  #endif	/* FILE_AREAS */
853.  	return fd;
854.  }
855.  
856.  
857.  int
858.  delete_bonesfile(lev)
859.  d_level *lev;
860.  {
861.  	(void) set_bonesfile_name(bones, lev);
862.  #ifdef FILE_AREAS
863.  	return !(remove_area(FILE_AREA_BONES, bones) < 0);
864.  #else
865.  	return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
866.  #endif
867.  }
868.  
869.  
870.  /* assume we're compressing the recently read or created bonesfile, so the
871.   * file name is already set properly */
872.  void
873.  compress_bonesfile()
874.  {
875.  #ifdef FILE_AREAS
876.  	compress_area(FILE_AREA_BONES, bones);
877.  #else
878.  	compress(fqname(bones, BONESPREFIX, 0));
879.  #endif
880.  }
881.  
882.  /* ----------  END BONES FILE HANDLING ----------- */
883.  
884.  
885.  /* ----------  BEGIN SAVE FILE HANDLING ----------- */
886.  
887.  /* set savefile name in OS-dependent manner from pre-existing plname,
888.   * avoiding troublesome characters */
889.  void
890.  set_savefile_name()
891.  {
892.  #if defined(WIN32)
893.  	char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
894.  #endif
895.  #ifdef VMS
896.  #ifndef FILE_AREAS
897.  	Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
898.  	regularize(SAVEF+7);
899.  #else
900.  	Sprintf(SAVEF, "%d%s", getuid(), plname);
901.  	regularize(SAVEF);
902.  #endif
903.  	Strcat(SAVEF, ";1");
904.  #else
905.  # if defined(MICRO)
906.  	Strcpy(SAVEF, SAVEP);
907.  #  ifdef AMIGA
908.  	strncat(SAVEF, bbs_id, PATHLEN);
909.  #  endif
910.  	{
911.  		int i = strlen(SAVEP);
912.  #  ifdef AMIGA
913.  		/* plname has to share space with SAVEP and ".sav" */
914.  		(void)strncat(SAVEF, plname, FILENAME - i - 4);
915.  #  else
916.  		(void)strncat(SAVEF, plname, 8);
917.  #  endif
918.  		regularize(SAVEF+i);
919.  	}
920.  	Strcat(SAVEF, ".sav");
921.  # else
922.  #  ifndef FILE_AREAS
923.  #  if defined(WIN32)
924.  	/* Obtain the name of the logged on user and incorporate
925.  	 * it into the name. */
926.  	Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
927.  	(void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.",
928.  				'%', fnamebuf, encodedfnamebuf, BUFSZ);
929.  	Sprintf(SAVEF, "%s.NetHack-saved-game", encodedfnamebuf);
930.  #  else
931.  	Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname);
932.  	regularize(SAVEF+5);	/* avoid . or / in name */
933.  #  endif /* WIN32 */
934.  #  else
935.  	Sprintf(SAVEF, "%d%s", (int)getuid(), plname);
936.  	regularize(SAVEF);      /* avoid . or / in name */
937.  #  endif
938.  # endif	/* MICRO */
939.  #endif /* VMS   */
940.  }
941.  
942.  #ifdef INSURANCE
943.  void
944.  save_savefile_name(fd)
945.  int fd;
946.  {
947.  	(void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
948.  }
949.  #endif
950.  
951.  
952.  #if defined(WIZARD) && !defined(MICRO)
953.  /* change pre-existing savefile name to indicate an error savefile */
954.  void
955.  set_error_savefile()
956.  {
957.  # ifdef VMS
958.        {
959.  	char *semi_colon = rindex(SAVEF, ';');
960.  	if (semi_colon) *semi_colon = '\0';
961.        }
962.  	Strcat(SAVEF, ".e;1");
963.  # else
964.  #  ifdef MAC
965.  	Strcat(SAVEF, "-e");
966.  #  else
967.  	Strcat(SAVEF, ".e");
968.  #  endif
969.  # endif
970.  }
971.  #endif
972.  
973.  
974.  /* create save file, overwriting one if it already exists */
975.  int
976.  create_savefile()
977.  {
978.  #ifndef FILE_AREAS
979.  	const char *fq_save;
980.  #endif
981.  	int fd;
982.  
983.  #ifdef FILE_AREAS
984.  # ifdef MICRO
985.  	fd = open_area(FILE_AREA_SAVE, SAVEF,
986.  	  O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
987.  # else
988.  	fd = creat_area(FILE_AREA_SAVE, SAVEF, FCMASK);
989.  # endif
990.  #else	/* FILE_AREAS */
991.  	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
992.  # if defined(MICRO) || defined(WIN32)
993.  	fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
994.  # else
995.  #  ifdef MAC
996.  	fd = maccreat(fq_save, SAVE_TYPE);
997.  #  else
998.  	fd = creat(fq_save, FCMASK);
999.  #  endif
1000. # endif /* MICRO */
1001. #endif  /* FILE_AREAS */
1002. 
1003. #if defined(VMS) && !defined(SECURE)
1004. 	/*
1005. 	   Make sure the save file is owned by the current process.  That's
1006. 	   the default for non-privileged users, but for priv'd users the
1007. 	   file will be owned by the directory's owner instead of the user.
1008. 	 */
1009. #  ifdef getuid	/*(see vmsunix.c)*/
1010. #   undef getuid
1011. #  endif
1012. # ifdef FILE_AREAS
1013. 	(void) chown_area(FILE_AREA_SAVE, SAVEF, getuid(), getgid());
1014. # else
1015. 	(void) chown(fq_save, getuid(), getgid());
1016. # endif
1017. #endif /* VMS && !SECURE */
1018. 
1019. 	return fd;
1020. }
1021. 
1022. 
1023. /* open savefile for reading */
1024. int
1025. open_savefile()
1026. {
1027. 	int fd;
1028. 
1029. #ifdef FILE_AREAS
1030. 	fd = open_area(FILE_AREA_SAVE, SAVEF, O_RDONLY | O_BINARY, 0);
1031. #else
1032. 	const char *fq_save;
1033. 	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1034. # ifdef MAC
1035. 	fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
1036. # else
1037. 	fd = open(fq_save, O_RDONLY | O_BINARY, 0);
1038. # endif
1039. #endif	/* FILE_AREAS */
1040. 	return fd;
1041. }
1042. 
1043. 
1044. /* delete savefile */
1045. int
1046. delete_savefile()
1047. {
1048. /*WAC OK...this is probably a contreversial addition.  It's an option tho*/
1049. #ifdef KEEP_SAVE
1050. /* Wizard mode already has prompt*/
1051. 	if (flags.keep_savefile && !wizard) {
1052. # ifdef AMIGA /*WAC If Amiga is ever supported*/
1053. 		preserve_icon();
1054. # endif
1055. 		return 1; /*Should this return 0?*/
1056. 	}
1057. #endif
1058. 
1059. #ifdef FILE_AREAS
1060. 	(void) remove_area(FILE_AREA_SAVE, SAVEF);
1061. #else
1062. 	(void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
1063. #endif
1064. 	return 0;	/* for restore_saved_game() (ex-xxxmain.c) test */
1065. }
1066. 
1067. 
1068. /* try to open up a save file and prepare to restore it */
1069. int
1070. restore_saved_game()
1071. {
1072. #ifndef FILE_AREAS
1073. 	const char *fq_save;
1074. #endif
1075. 	int fd;
1076. 
1077. 	set_savefile_name();
1078. #ifdef MFLOPPY
1079. 	if (!saveDiskPrompt(1))
1080. 	    return -1;
1081. #endif /* MFLOPPY */
1082. #ifndef FILE_AREAS
1083. 	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1084. 
1085. 	uncompress(fq_save);
1086. #else
1087. 	uncompress_area(FILE_AREA_SAVE, SAVEF);
1088. #endif
1089. 	if ((fd = open_savefile()) < 0) return fd;
1090. 
1091. #ifndef FILE_AREAS
1092. 	if (!uptodate(fd, fq_save)) {
1093. #else
1094. 	if (!uptodate(fd, SAVEF)) {
1095. #endif
1096. 	    (void) close(fd),  fd = -1;
1097. 	    (void) delete_savefile();
1098. 	}
1099. 	return fd;
1100. }
1101. 
1102. #if defined(UNIX) && defined(QT_GRAPHICS)
1103. /*ARGSUSED*/
1104. static char*
1105. plname_from_file(filename)
1106. const char* filename;
1107. {
1108. #ifdef STORE_PLNAME_IN_FILE
1109.     int fd;
1110.     char* result = 0;
1111. 
1112.     Strcpy(SAVEF,filename);
1113. #ifdef COMPRESS_EXTENSION
1114.     SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0';
1115. #endif
1116.     uncompress(SAVEF);
1117.     if ((fd = open_savefile()) >= 0) {
1118. 	if (uptodate(fd, filename)) {
1119. 	    char tplname[PL_NSIZ];
1120. 	    mread(fd, (genericptr_t) tplname, PL_NSIZ);
1121. 	    result = strdup(tplname);
1122. 	}
1123. 	(void) close(fd);
1124.     }
1125.     compress(SAVEF);
1126. 
1127.     return result;
1128. #else
1129. # if defined(UNIX) && defined(QT_GRAPHICS)
1130.     /* Name not stored in save file, so we have to extract it from
1131.        the filename, which loses information
1132.        (eg. "/", "_", and "." characters are lost. */
1133.     int k;
1134.     int uid;
1135.     char name[64]; /* more than PL_NSIZ */
1136. #ifdef COMPRESS_EXTENSION
1137. #define EXTSTR COMPRESS_EXTENSION
1138. #else
1139. #define EXTSTR ""
1140. #endif
1141.     if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) {
1142. #undef EXTSTR
1143.     /* "_" most likely means " ", which certainly looks nicer */
1144. 	for (k=0; name[k]; k++)
1145. 	    if ( name[k]=='_' )
1146. 		name[k]=' ';
1147. 	return strdup(name);
1148.     } else
1149. # endif
1150.     {
1151. 	return 0;
1152.     }
1153. #endif
1154. }
1155. #endif /* defined(UNIX) && defined(QT_GRAPHICS) */
1156. 
1157. char**
1158. get_saved_games()
1159. {
1160. #if defined(UNIX) && defined(QT_GRAPHICS)
1161.     int myuid=getuid();
1162.     struct dirent **namelist;
1163.     int n = scandir("save", &namelist, 0, alphasort);;
1164.     if ( n > 0 ) {
1165. 	int i,j=0;
1166. 	char** result = (char**)alloc((n+1)*sizeof(char*)); /* at most */
1167. 	for (i=0; i<n; i++) {
1168. 	    int uid;
1169. 	    char name[64]; /* more than PL_NSIZ */
1170. 	    if ( sscanf( namelist[i]->d_name, "%d%63s", &uid, name ) == 2 ) {
1171. 		if ( uid == myuid ) {
1172. 		    char filename[BUFSZ];
1173. 		    char* r;
1174. 		    Sprintf(filename,"save/%d%s",uid,name);
1175. 		    r = plname_from_file(filename);
1176. 		    if ( r )
1177. 			result[j++] = r;
1178. 		}
1179. 	    }
1180. 	}
1181. 	result[j++] = 0;
1182. 	return result;
1183.     } else
1184. #endif
1185.     {
1186. 	return 0;
1187.     }
1188. }
1189. 
1190. void
1191. free_saved_games(saved)
1192. char** saved;
1193. {
1194.     if ( saved ) {
1195. 	int i=0;
1196. 	while (saved[i]) free((genericptr_t)saved[i++]);
1197. 	free((genericptr_t)saved);
1198.     }
1199. }
1200. 
1201. 
1202. /* ----------  END SAVE FILE HANDLING ----------- */
1203. 
1204. 
1205. /* ----------  BEGIN FILE COMPRESSION HANDLING ----------- */
1206. 
1207. #ifdef COMPRESS
1208. 
1209. STATIC_OVL void
1210. redirect(filearea, filename, mode, stream, uncomp)
1211. const char *filearea, *filename, *mode;
1212. FILE *stream;
1213. boolean uncomp;
1214. {
1215. #ifndef FILE_AREAS
1216. 	if (freopen(filename, mode, stream) == (FILE *)0) {
1217. #else
1218. 	if (freopen_area(filearea, filename, mode, stream) == (FILE *)0) {
1219. #endif
1220. 		(void) fprintf(stderr, "redirect of %s for %scompress failed\n",
1221. 			filename, uncomp ? "un" : "");
1222. 		terminate(EXIT_FAILURE);
1223. 	}
1224. }
1225. 
1226. /*
1227.  * using system() is simpler, but opens up security holes and causes
1228.  * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1229.  * setuid is renounced by /bin/sh, so the files cannot be accessed.
1230.  *
1231.  * cf. child() in unixunix.c.
1232.  */
1233. STATIC_OVL void
1234. docompress_file(filearea, filename, uncomp)
1235. const char *filearea, *filename;
1236. boolean uncomp;
1237. {
1238. 	char cfn[80];
1239. 	FILE *cf;
1240. 	const char *args[10];
1241. # ifdef COMPRESS_OPTIONS
1242. 	char opts[80];
1243. # endif
1244. 	int i = 0;
1245. 	int f;
1246. # ifdef TTY_GRAPHICS
1247. 	boolean istty = !strncmpi(windowprocs.name, "tty", 3);
1248. # endif
1249. 
1250. 	Strcpy(cfn, filename);
1251. # ifdef COMPRESS_EXTENSION
1252. 	Strcat(cfn, COMPRESS_EXTENSION);
1253. # endif
1254. 	/* when compressing, we know the file exists */
1255. 	if (uncomp) {
1256. 	    if ((cf = fopen_datafile_area(filearea, cfn, RDBMODE, FALSE)) ==
1257. 	      (FILE *)0)
1258. 		    return;
1259. 	    (void) fclose(cf);
1260. 	}
1261. 
1262. 	args[0] = COMPRESS;
1263. 	if (uncomp) args[++i] = "-d";	/* uncompress */
1264. # ifdef COMPRESS_OPTIONS
1265. 	{
1266. 	    /* we can't guarantee there's only one additional option, sigh */
1267. 	    char *opt;
1268. 	    boolean inword = FALSE;
1269. 
1270. 	    Strcpy(opts, COMPRESS_OPTIONS);
1271. 	    opt = opts;
1272. 	    while (*opt) {
1273. 		if ((*opt == ' ') || (*opt == '\t')) {
1274. 		    if (inword) {
1275. 			*opt = '\0';
1276. 			inword = FALSE;
1277. 		    }
1278. 		} else if (!inword) {
1279. 		    args[++i] = opt;
1280. 		    inword = TRUE;
1281. 		}
1282. 		opt++;
1283. 	    }
1284. 	}
1285. # endif
1286. 	args[++i] = (char *)0;
1287. 
1288. # ifdef TTY_GRAPHICS
1289. 	/* If we don't do this and we are right after a y/n question *and*
1290. 	 * there is an error message from the compression, the 'y' or 'n' can
1291. 	 * end up being displayed after the error message.
1292. 	 */
1293. 	if (istty)
1294. 	    mark_synch();
1295. # endif
1296. 	f = fork();
1297. 	if (f == 0) {	/* child */
1298. # ifdef TTY_GRAPHICS
1299. 		/* any error messages from the compression must come out after
1300. 		 * the first line, because the more() to let the user read
1301. 		 * them will have to clear the first line.  This should be
1302. 		 * invisible if there are no error messages.
1303. 		 */
1304. 		if (istty)
1305. 		    raw_print("");
1306. # endif
1307. 		/* run compressor without privileges, in case other programs
1308. 		 * have surprises along the line of gzip once taking filenames
1309. 		 * in GZIP.
1310. 		 */
1311. 		/* assume all compressors will compress stdin to stdout
1312. 		 * without explicit filenames.  this is true of at least
1313. 		 * compress and gzip, those mentioned in config.h.
1314. 		 */
1315. 		if (uncomp) {
1316. 			redirect(filearea, cfn, RDBMODE, stdin, uncomp);
1317. 			redirect(filearea, filename, WRBMODE, stdout, uncomp);
1318. 		} else {
1319. 			redirect(filearea, filename, RDBMODE, stdin, uncomp);
1320. 			redirect(filearea, cfn, WRBMODE, stdout, uncomp);
1321. 		}
1322. 		(void) setgid(getgid());
1323. 		(void) setuid(getuid());
1324. 		(void) execv(args[0], (char *const *) args);
1325. 		perror((char *)0);
1326. 		(void) fprintf(stderr, "Exec to %scompress %s failed.\n",
1327. 			uncomp ? "un" : "", filename);
1328. 		terminate(EXIT_FAILURE);
1329. 	} else if (f == -1) {
1330. 		perror((char *)0);
1331. 		pline("Fork to %scompress %s failed.",
1332. 			uncomp ? "un" : "", filename);
1333. 		return;
1334. 	}
1335. # ifndef NO_SIGNAL
1336. 	(void) signal(SIGINT, SIG_IGN);
1337. 	(void) signal(SIGQUIT, SIG_IGN);
1338. # endif
1339. 	(void) wait((int *)&i);
1340. # ifndef NO_SIGNAL
1341. 	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
1342. # endif
1343. # ifdef WIZARD
1344. 	if (wizard) (void) signal(SIGQUIT, SIG_DFL);
1345. # endif
1346. 	if (i == 0) {
1347. 	    /* (un)compress succeeded: remove file left behind */
1348. 	    if (uncomp)
1349. #ifndef FILE_AREAS
1350. 		(void) unlink(cfn);
1351. #else
1352. 		(void) remove_area(filearea, cfn);
1353. #endif
1354. 	    else
1355. #ifndef FILE_AREAS
1356. 		(void) unlink(filename);
1357. #else
1358. 		(void) remove_area(filearea, filename);
1359. #endif
1360. 	} else {
1361. 	    /* (un)compress failed; remove the new, bad file */
1362. 	    if (uncomp) {
1363. 		raw_printf("Unable to uncompress %s", filename);
1364. 		(void) unlink(filename);
1365. 	    } else {
1366. 		/* no message needed for compress case; life will go on */
1367. 		(void) unlink(cfn);
1368. 	    }
1369. #ifdef TTY_GRAPHICS
1370. 	    /* Give them a chance to read any error messages from the
1371. 	     * compression--these would go to stdout or stderr and would get
1372. 	     * overwritten only in tty mode.  It's still ugly, since the
1373. 	     * messages are being written on top of the screen, but at least
1374. 	     * the user can read them.
1375. 	     */
1376. 	    if (istty)
1377. 	    {
1378. 		clear_nhwindow(WIN_MESSAGE);
1379. 		more();
1380. 		/* No way to know if this is feasible */
1381. 		/* doredraw(); */
1382. 	    }
1383. #endif
1384. 	}
1385. }
1386. #endif	/* COMPRESS */
1387. 
1388. /* compress file */
1389. void
1390. compress_area(filearea, filename)
1391. const char *filearea, *filename;
1392. {
1393. #ifndef COMPRESS
1394. #if defined(MAC_MPW) || defined(__MWERKS__)
1395. # pragma unused(filename)
1396. #endif
1397. #else
1398. 	docompress_file(filearea, filename, FALSE);
1399. #endif
1400. }
1401. 
1402. 
1403. /* uncompress file if it exists */
1404. void
1405. uncompress_area(filearea, filename)
1406. const char *filearea, *filename;
1407. {
1408. #ifndef COMPRESS
1409. #if defined(MAC_MPW) || defined(__MWERKS__)
1410. # pragma unused(filename)
1411. #endif
1412. #else
1413. 	docompress_file(filearea, filename, TRUE);
1414. #endif
1415. }
1416. 
1417. /* ----------  END FILE COMPRESSION HANDLING ----------- */
1418. 
1419. 
1420. /*
1421.  * When file areas are in use, (un)lock_file_area are used instead.
1422.  */
1423. 
1424. #ifndef FILE_AREAS
1425. 
1426. /* ----------  BEGIN FILE LOCKING HANDLING ----------- */
1427. 
1428. static int nesting = 0;
1429. 
1430. #ifdef NO_FILE_LINKS	/* implies UNIX */
1431. static int lockfd;	/* for lock_file() to pass to unlock_file() */
1432. #endif
1433. 
1434. #define HUP	if (!program_state.done_hup)
1435. 
1436. STATIC_OVL char *
1437. make_lockname(filename, lockname)
1438. const char *filename;
1439. char *lockname;
1440. {
1441. #if defined(MAC_MPW) || defined(__MWERKS__)
1442. # pragma unused(filename,lockname)
1443. 	return (char*)0;
1444. #else
1445. # if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1446. #  ifdef NO_FILE_LINKS
1447. 	Strcpy(lockname, LOCKDIR);
1448. 	Strcat(lockname, "/");
1449. 	Strcat(lockname, filename);
1450. #  else
1451. 	Strcpy(lockname, filename);
1452. #  endif
1453. #  ifdef VMS
1454.       {
1455. 	char *semi_colon = rindex(lockname, ';');
1456. 	if (semi_colon) *semi_colon = '\0';
1457.       }
1458. 	Strcat(lockname, ".lock;1");
1459. #  else
1460. 	Strcat(lockname, "_lock");
1461. #  endif
1462. 	return lockname;
1463. # else
1464. 	lockname[0] = '\0';
1465. 	return (char*)0;
1466. # endif  /* UNIX || VMS || AMIGA || WIN32 || MSDOS */
1467. #endif
1468. }
1469. 
1470. 
1471. /* lock a file */
1472. boolean
1473. lock_file(filename, whichprefix, retryct)
1474. const char *filename;
1475. int whichprefix;
1476. int retryct;
1477. {
1478. #if defined(MAC_MPW) || defined(__MWERKS__)
1479. # pragma unused(filename, retryct)
1480. #endif
1481. 	char locknambuf[BUFSZ];
1482. 	const char *lockname;
1483. 
1484. 	nesting++;
1485. 	if (nesting > 1) {
1486. 	    impossible("TRIED TO NEST LOCKS");
1487. 	    return TRUE;
1488. 	}
1489. 
1490. 	lockname = make_lockname(filename, locknambuf);
1491. 	filename = fqname(filename, whichprefix, 0);
1492. #ifndef NO_FILE_LINKS	/* LOCKDIR should be subsumed by LOCKPREFIX */
1493. 	lockname = fqname(lockname, LOCKPREFIX, 2);
1494. #endif
1495. 
1496. #if defined(UNIX) || defined(VMS)
1497. # ifdef NO_FILE_LINKS
1498. 	while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) {
1499. # else
1500. 	while (link(filename, lockname) == -1) {
1501. # endif
1502. 	    register int errnosv = errno;
1503. 
1504. 	    switch (errnosv) {	/* George Barbanis */
1505. 	    case EEXIST:
1506. 		if (retryct--) {
1507. 		    HUP raw_printf(
1508. 			    "Waiting for access to %s.  (%d retries left).",
1509. 			    filename, retryct);
1510. # if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1511. 		    (void)
1512. # endif
1513. 			sleep(1);
1514. 		} else {
1515. 		    HUP (void) raw_print("I give up.  Sorry.");
1516. 		    HUP raw_printf("Perhaps there is an old %s around?",
1517. 					lockname);
1518. 		    nesting--;
1519. 		    return FALSE;
1520. 		}
1521. 
1522. 		break;
1523. 	    case ENOENT:
1524. 		HUP raw_printf("Can't find file %s to lock!", filename);
1525. 		nesting--;
1526. 		return FALSE;
1527. 	    case EACCES:
1528. 		HUP raw_printf("No write permission to lock %s!", filename);
1529. 		nesting--;
1530. 		return FALSE;
1531. # ifdef VMS			/* c__translate(vmsfiles.c) */
1532. 	    case EPERM:
1533. 		/* could be misleading, but usually right */
1534. 		HUP raw_printf("Can't lock %s due to directory protection.",
1535. 			       filename);
1536. 		nesting--;
1537. 		return FALSE;
1538. # endif
1539. 	    default:
1540. 		HUP perror(lockname);
1541. 		HUP raw_printf(
1542. 			     "Cannot lock %s for unknown reason (%d).",
1543. 			       filename, errnosv);
1544. 		nesting--;
1545. 		return FALSE;
1546. 	    }
1547. 
1548. 	}
1549. #endif  /* UNIX || VMS */
1550. 
1551. #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1552. # ifdef AMIGA
1553. #define OPENFAILURE(fd) (!fd)
1554.     lockptr = 0;
1555. # else
1556. #define OPENFAILURE(fd) (fd < 0)
1557.     lockptr = -1;
1558. # endif
1559.     while (--retryct && OPENFAILURE(lockptr)) {
1560. # if defined(WIN32) && !defined(WIN_CE)
1561. 	lockptr = sopen(lockname, O_RDWR|O_CREAT, SH_DENYRW, S_IWRITE);
1562. # else
1563. 	(void)DeleteFile(lockname); /* in case dead process was here first */
1564. #  ifdef AMIGA
1565. 	lockptr = Open(lockname,MODE_NEWFILE);
1566. #  else
1567. 	lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE);
1568. #  endif
1569. # endif
1570. 	if (OPENFAILURE(lockptr)) {
1571. 	    raw_printf("Waiting for access to %s.  (%d retries left).",
1572. 			filename, retryct);
1573. 	    Delay(50);
1574. 	}
1575.     }
1576.     if (!retryct) {
1577. 	raw_printf("I give up.  Sorry.");
1578. 	nesting--;
1579. 	return FALSE;
1580.     }
1581. #endif /* AMIGA || WIN32 || MSDOS */
1582. 	return TRUE;
1583. }
1584. 
1585. 
1586. #ifdef VMS	/* for unlock_file, use the unlink() routine in vmsunix.c */
1587. # ifdef unlink
1588. #  undef unlink
1589. # endif
1590. # define unlink(foo) vms_unlink(foo)
1591. #endif
1592. 
1593. /* unlock file, which must be currently locked by lock_file */
1594. void
1595. unlock_file(filename)
1596. const char *filename;
1597. {
1598. 	char locknambuf[BUFSZ];
1599. 	const char *lockname;
1600. 
1601. 	if (nesting == 1) {
1602. 		lockname = make_lockname(filename, locknambuf);
1603. #ifndef NO_FILE_LINKS	/* LOCKDIR should be subsumed by LOCKPREFIX */
1604. 		lockname = fqname(lockname, LOCKPREFIX, 2);
1605. #endif
1606. 
1607. #if defined(UNIX) || defined(VMS)
1608. 		if (unlink(lockname) < 0)
1609. 			HUP raw_printf("Can't unlink %s.", lockname);
1610. # ifdef NO_FILE_LINKS
1611. 		(void) close(lockfd);
1612. # endif
1613. 
1614. #endif  /* UNIX || VMS */
1615. 
1616. #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1617. 		if (lockptr) Close(lockptr);
1618. 		DeleteFile(lockname);
1619. 		lockptr = 0;
1620. #endif /* AMIGA || WIN32 || MSDOS */
1621. 	}
1622. 
1623. 	nesting--;
1624. }
1625. 
1626. /* ----------  END FILE LOCKING HANDLING ----------- */
1627. 
1628. #endif	/* FILE_AREAS */
1629. 
1630. /* ----------  BEGIN CONFIG FILE HANDLING ----------- */
1631. 
1632. const char *configfile =
1633.                         NH_CONFIG_FILE;
1634. /* WAC This stuff is in filename.h now
1635. #ifdef UNIX
1636. 			".nethackrc";
1637. #else
1638. # if defined(MAC) || defined(__BEOS__)
1639. 			"NetHack Defaults";
1640. #  if defined(MSDOS) || defined(WIN32)
1641. 			"defaults.nh";
1642. #  else
1643. 			"NetHack.cnf";
1644. #  endif
1645. # endif
1646. #endif
1647. */
1648. 
1649. 
1650. #ifdef MSDOS
1651. /* conflict with speed-dial under windows
1652.  * for XXX.cnf file so support of NetHack.cnf
1653.  * is for backward compatibility only.
1654.  * Preferred name (and first tried) is now defaults.nh but
1655.  * the game will try the old name if there
1656.  * is no defaults.nh.
1657.  */
1658. const char *backward_compat_configfile = "nethack.cnf"; 
1659. #endif
1660. 
1661. #ifndef MFLOPPY
1662. #define fopenp fopen
1663. #endif
1664. 
1665. STATIC_OVL FILE *
1666. fopen_config_file(filename)
1667. const char *filename;
1668. {
1669. 	FILE *fp;
1670. #if defined(UNIX) || defined(VMS)
1671. 	char	tmp_config[BUFSZ];
1672. 	char *envp;
1673. #endif
1674. 
1675. 	/* "filename" is an environment variable, so it should hang around */
1676. 	/* if set, it is expected to be a full path name (if relevant) */
1677. 	if (filename) {
1678. #ifdef UNIX
1679. 		if (access(filename, 4) == -1) {
1680. 			/* 4 is R_OK on newer systems */
1681. 			/* nasty sneaky attempt to read file through
1682. 			 * NetHack's setuid permissions -- this is the only
1683. 			 * place a file name may be wholly under the player's
1684. 			 * control
1685. 			 */
1686. 			raw_printf("Access to %s denied (%d).",
1687. 					filename, errno);
1688. 			wait_synch();
1689. 			/* fall through to standard names */
1690. 		} else
1691. #endif
1692. 		if ((fp = fopenp(filename, "r")) != (FILE *)0) {
1693. 		    configfile = filename;
1694. 		    return(fp);
1695. #if defined(UNIX) || defined(VMS)
1696. 		} else {
1697. 		    /* access() above probably caught most problems for UNIX */
1698. 		    raw_printf("Couldn't open requested config file %s (%d).",
1699. 					filename, errno);
1700. 		    wait_synch();
1701. 		    /* fall through to standard names */
1702. #endif
1703. 		}
1704. 	}
1705. 
1706. #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1707. 	if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r"))
1708. 								!= (FILE *)0)
1709. 		return(fp);
1710. # ifdef MSDOS
1711. 	else if ((fp = fopenp(fqname(backward_compat_configfile,
1712. 					CONFIGPREFIX, 0), "r")) != (FILE *)0)
1713. 		return(fp);
1714. # endif
1715. #else
1716. 	/* constructed full path names don't need fqname() */
1717. # ifdef VMS
1718. 	if ((fp = fopenp(fqname(NH_CONFIG_FILE, CONFIGPREFIX, 0), "r"))
1719. 								!= (FILE *)0) {
1720. 		configfile = NH_CONFIG_FILE;
1721. 		return(fp);
1722. 	}
1723. 	if ((fp = fopenp(NH_CONFIG_FILE2, "r")) != (FILE *)0) {
1724. 		configfile = index(NH_CONFIG_FILE2, ':');
1725. 		if (configfile)
1726. 			configfile++;
1727. 		else
1728. 			configfile = NH_CONFIG_FILE2;
1729. 		return(fp);
1730. 	}
1731. 
1732. 	envp = nh_getenv("HOME");
1733. 	if (!envp)
1734. 		Strcpy(tmp_config, NH_CONFIG_FILE3);
1735. 	else
1736. 		Sprintf(tmp_config, "%s%s", envp, NH_CONFIG_FILE3);
1737. 	if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1738. 		return(fp);
1739. # else	/* should be only UNIX left */
1740. 	envp = nh_getenv("HOME");
1741. 	if (!envp)
1742. 		Strcpy(tmp_config, configfile);
1743. 	else
1744. 		Sprintf(tmp_config, "%s/%s", envp, configfile);
1745. 	if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1746. 		return(fp);
1747. # if defined(__APPLE__)
1748. 	/* try an alternative */
1749. 	if (envp) {
1750. 		Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults");
1751. 		if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1752. 			return(fp);
1753. 		Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults.txt");
1754. 		if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1755. 			return(fp);
1756. 	}
1757. # endif
1758. 	if (errno != ENOENT) {
1759. 	    char *details;
1760. 
1761. 	    /* e.g., problems when setuid NetHack can't search home
1762. 	     * directory restricted to user */
1763. 
1764. #if defined (NHSTDC) && !defined(NOTSTDC)
1765. 	    if ((details = strerror(errno)) == 0)
1766. #endif
1767. 		details = "";
1768. 	    raw_printf("Couldn't open default config file %s %s(%d).",
1769. 			tmp_config, details, errno);
1770. 	    wait_synch();
1771. 	}
1772. 	else if (!strncmp(windowprocs.name, "proxy/", 6)) {
1773. 	    fp = fopenp("/etc/slashem/proxy.slashemrc", "r");
1774. 	    if (fp != (FILE *)0)
1775. 		return(fp);
1776. 	    else if (errno != ENOENT) {
1777. 		raw_printf("Couldn't open /etc/slashem/proxy.slashemrc (%d).",
1778. 				errno);
1779. 		wait_synch();
1780. 	    }
1781. 	}
1782. # endif
1783. #endif
1784. 	return (FILE *)0;
1785. 
1786. }
1787. 
1788. 
1789. /*
1790.  * Retrieve a list of integers from a file into a uchar array.
1791.  *
1792.  * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
1793.  *  location is unchanged.  Callers must handle zeros if modlist is FALSE.
1794.  */
1795. STATIC_OVL int
1796. get_uchars(fp, buf, bufp, list, modlist, size, name)
1797.     FILE *fp;		/* input file pointer */
1798.     char *buf;		/* read buffer, must be of size BUFSZ */
1799.     char *bufp;		/* current pointer */
1800.     uchar *list;	/* return list */
1801.     boolean modlist;	/* TRUE: list is being modified in place */
1802.     int  size;		/* return list size */
1803.     const char *name;		/* name of option for error message */
1804. {
1805.     unsigned int num = 0;
1806.     int count = 0;
1807.     boolean havenum = FALSE;
1808. 
1809.     while (1) {
1810. 	switch(*bufp) {
1811. 	    case ' ':  case '\0':
1812. 	    case '\t': case '\n':
1813. 		if (havenum) {
1814. 		    /* if modifying in place, don't insert zeros */
1815. 		    if (num || !modlist) list[count] = num;
1816. 		    count++;
1817. 		    num = 0;
1818. 		    havenum = FALSE;
1819. 		}
1820. 		if (count == size || !*bufp) return count;
1821. 		bufp++;
1822. 		break;
1823. 
1824. 	    case '0': case '1': case '2': case '3':
1825. 	    case '4': case '5': case '6': case '7':
1826. 	    case '8': case '9':
1827. 		havenum = TRUE;
1828. 		num = num*10 + (*bufp-'0');
1829. 		bufp++;
1830. 		break;
1831. 
1832. 	    case '\\':
1833. 		if (fp == (FILE *)0)
1834. 		    goto gi_error;
1835. 		do  {
1836. 		    if (!fgets(buf, BUFSZ, fp)) goto gi_error;
1837. 		} while (buf[0] == '#');
1838. 		bufp = buf;
1839. 		break;
1840. 
1841. 	    default:
1842. gi_error:
1843. 		raw_printf("Syntax error in %s", name);
1844. 		wait_synch();
1845. 		return count;
1846. 	}
1847.     }
1848.     /*NOTREACHED*/
1849. }
1850. 
1851. #ifdef NOCWD_ASSUMPTIONS
1852. STATIC_OVL void
1853. adjust_prefix(bufp, prefixid)
1854. char *bufp;
1855. int prefixid;
1856. {
1857. 	char *ptr;
1858. 
1859. 	if (!bufp) return;
1860. 	/* Backward compatibility, ignore trailing ;n */ 
1861. 	if ((ptr = index(bufp, ';')) != 0) *ptr = '\0';
1862. 	if (strlen(bufp) > 0) {
1863. 		fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2);
1864. 		Strcpy(fqn_prefix[prefixid], bufp);
1865. 		append_slash(fqn_prefix[prefixid]);
1866. 	}
1867. }
1868. #endif
1869. 
1870. #define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE)
1871. 
1872. /*ARGSUSED*/
1873. int
1874. parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)
1875. FILE		*fp;
1876. char		*buf;
1877. char		*tmp_ramdisk;
1878. char		*tmp_levels;
1879. {
1880. #if defined(MAC_MPW) || defined(__MWERKS__)
1881. # pragma unused(tmp_ramdisk,tmp_levels)
1882. #endif
1883. 	char		*bufp, *altp;
1884. 	uchar   translate[MAXPCHARS];
1885. 	int   len;
1886. 
1887. 	if (*buf == '#')
1888. 		return 1;
1889. 
1890. 	/* remove trailing whitespace */
1891. 	bufp = eos(buf);
1892. 	while (--bufp > buf && isspace((int)*bufp))
1893. 		continue;
1894. 
1895. 	if (bufp <= buf)
1896. 		return 1;		/* skip all-blank lines */
1897. 	else
1898. 		*(bufp + 1) = '\0';	/* terminate line */
1899. 
1900. 	/* find the '=' or ':' */
1901. 	bufp = index(buf, '=');
1902. 	altp = index(buf, ':');
1903. 	if (!bufp || (altp && altp < bufp)) bufp = altp;
1904. 	if (!bufp) return 0;
1905. 
1906. 	/* skip  whitespace between '=' and value */
1907. 	do { ++bufp; } while (isspace((int)*bufp));
1908. 
1909. 	/* Go through possible variables */
1910. 	/* some of these (at least LEVELS and SAVE) should now set the
1911. 	 * appropriate fqn_prefix[] rather than specialized variables
1912. 	 */
1913. 	if (match_varname(buf, "OPTIONS", 4)) {
1914. 		parseoptions(bufp, TRUE, TRUE);
1915. 		if (plname[0])		/* If a name was given */
1916. 			plnamesuffix();	/* set the character class */
1917. 	} else if (match_varname(buf, "TILESETS", 7)) {
1918. 		parsetileset(bufp);
1919. #ifdef AUTOPICKUP_EXCEPTIONS
1920. 	} else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
1921. 		add_autopickup_exception(bufp);
1922. #endif
1923. #ifdef NOCWD_ASSUMPTIONS
1924. 	} else if (match_varname(buf, "HACKDIR", 4)) {
1925. 		adjust_prefix(bufp, HACKPREFIX);
1926. 	} else if (match_varname(buf, "LEVELDIR", 4) ||
1927. 		   match_varname(buf, "LEVELS", 4)) {
1928. 		adjust_prefix(bufp, LEVELPREFIX);
1929. 	} else if (match_varname(buf, "SAVEDIR", 4)) {
1930. 		adjust_prefix(bufp, SAVEPREFIX);
1931. 	} else if (match_varname(buf, "BONESDIR", 5)) {
1932. 		adjust_prefix(bufp, BONESPREFIX);
1933. 	} else if (match_varname(buf, "DATADIR", 4)) {
1934. 		adjust_prefix(bufp, DATAPREFIX);
1935. 	} else if (match_varname(buf, "SCOREDIR", 4)) {
1936. 		adjust_prefix(bufp, SCOREPREFIX);
1937. 	} else if (match_varname(buf, "LOCKDIR", 4)) {
1938. 		adjust_prefix(bufp, LOCKPREFIX);
1939. 	} else if (match_varname(buf, "CONFIGDIR", 4)) {
1940. 		adjust_prefix(bufp, CONFIGPREFIX);
1941. 	} else if (match_varname(buf, "TROUBLEDIR", 4)) {
1942. 		adjust_prefix(bufp, TROUBLEPREFIX);
1943. #else /*NOCWD_ASSUMPTIONS*/
1944. # ifdef MICRO
1945. 	} else if (match_varname(buf, "HACKDIR", 4)) {
1946. 		(void) strncpy(hackdir, bufp, PATHLEN-1);
1947. #  ifdef MFLOPPY
1948. 	} else if (match_varname(buf, "RAMDISK", 3)) {
1949. 				/* The following ifdef is NOT in the wrong
1950. 				 * place.  For now, we accept and silently
1951. 				 * ignore RAMDISK */
1952. #   ifndef AMIGA
1953. 		(void) strncpy(tmp_ramdisk, bufp, PATHLEN-1);
1954. #   endif
1955. #  endif
1956. 	} else if (match_varname(buf, "LEVELS", 4)) {
1957. 		(void) strncpy(tmp_levels, bufp, PATHLEN-1);
1958. 
1959. 	} else if (match_varname(buf, "SAVE", 4)) {
1960. #  ifdef MFLOPPY
1961. 		extern	int saveprompt;
1962. #  endif
1963. 		char *ptr;
1964. 		if ((ptr = index(bufp, ';')) != 0) {
1965. 			*ptr = '\0';
1966. #  ifdef MFLOPPY
1967. 			if (*(ptr+1) == 'n' || *(ptr+1) == 'N') {
1968. 				saveprompt = FALSE;
1969. 			}
1970. #  endif
1971. 		}
1972. # ifdef	MFLOPPY
1973. 		else
1974. 		    saveprompt = flags.asksavedisk;
1975. # endif
1976. 
1977. 		(void) strncpy(SAVEP, bufp, SAVESIZE-1);
1978. 		append_slash(SAVEP);
1979. # endif /* MICRO */
1980. #endif /*NOCWD_ASSUMPTIONS*/
1981. 
1982. 	} else if (match_varname(buf, "NAME", 4)) {
1983. 	    (void) strncpy(plname, bufp, PL_NSIZ-1);
1984. 	    plnamesuffix();
1985. 	} else if (match_varname(buf, "ROLE", 4) ||
1986. 		   match_varname(buf, "CHARACTER", 4)) {
1987. 	    if ((len = str2role(bufp)) >= 0)
1988. 	    	flags.initrole = len;
1989. 	} else if (match_varname(buf, "DOGNAME", 3)) {
1990. 	    (void) strncpy(dogname, bufp, PL_PSIZ-1);
1991. 	} else if (match_varname(buf, "CATNAME", 3)) {
1992. 	    (void) strncpy(catname, bufp, PL_PSIZ-1);
1993. 	} else if (match_varname(buf, "WOLFNAME", 3)) {
1994.             (void) strncpy(wolfname, bufp, PL_PSIZ-1);
1995. 	} else if (match_varname(buf, "GHOULNAME", 3)) {
1996.             (void) strncpy(ghoulname, bufp, PL_PSIZ-1);
1997. #if 0
1998. 	} else if (match_varname(buf, "BATNAME", 3)) {
1999.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2000. 	} else if (match_varname(buf, "SNAKENAME", 3)) {
2001.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2002. 	} else if (match_varname(buf, "RATNAME", 3)) {
2003.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2004. 	} else if (match_varname(buf, "BADGERNAME", 3)) {
2005.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2006. 	} else if (match_varname(buf, "REDDRAGONNAME", 3)) {
2007.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2008. 	} else if (match_varname(buf, "WHITEDRAGONNAME", 3)) {
2009.             (void) strncpy(batname, bufp, PL_PSIZ-1);
2010. #endif
2011. 
2012. 	} else if (match_varname(buf, "BOULDER", 3)) {
2013. 	    (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE,
2014. 			      1, "BOULDER");
2015. 	} else if (match_varname(buf, "MENUCOLOR", 9)) {
2016. #ifdef MENU_COLOR
2017. 	   add_menu_coloring(bufp);
2018. #endif
2019. 	} else if (match_varname(buf, "GRAPHICS", 4)) {
2020. 	    len = get_uchars(fp, buf, bufp, translate, FALSE,
2021. 			     MAXPCHARS, "GRAPHICS");
2022. 	    assign_graphics(translate, len, MAXPCHARS, 0);
2023. 	} else if (match_varname(buf, "DUNGEON", 4)) {
2024. 	    len = get_uchars(fp, buf, bufp, translate, FALSE,
2025. 			     MAXDCHARS, "DUNGEON");
2026. 	    assign_graphics(translate, len, MAXDCHARS, 0);
2027. 	} else if (match_varname(buf, "TRAPS", 4)) {
2028. 	    len = get_uchars(fp, buf, bufp, translate, FALSE,
2029. 			     MAXTCHARS, "TRAPS");
2030. 	    assign_graphics(translate, len, MAXTCHARS, MAXDCHARS);
2031. 	} else if (match_varname(buf, "EFFECTS", 4)) {
2032. 	    len = get_uchars(fp, buf, bufp, translate, FALSE,
2033. 			     MAXECHARS, "EFFECTS");
2034. 	    assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS);
2035. 
2036. 	} else if (match_varname(buf, "OBJECTS", 3)) {
2037. 	    /* oc_syms[0] is the RANDOM object, unused */
2038. 	    (void) get_uchars(fp, buf, bufp, &(oc_syms[1]), TRUE,
2039. 					MAXOCLASSES-1, "OBJECTS");
2040. 	} else if (match_varname(buf, "MONSTERS", 3)) {
2041. 	    /* monsyms[0] is unused */
2042. 	    (void) get_uchars(fp, buf, bufp, &(monsyms[1]), TRUE,
2043. 					MAXMCLASSES-1, "MONSTERS");
2044. 	} else if (match_varname(buf, "WARNINGS", 5)) {
2045. 	    (void) get_uchars(fp, buf, bufp, translate, TRUE,
2046. 					WARNCOUNT, "WARNINGS");
2047. 	    assign_warnings(translate);
2048. #ifdef WIZARD
2049. 	} else if (match_varname(buf, "WIZKIT", 6)) {
2050. 	    (void) strncpy(wizkit, bufp, WIZKIT_MAX-1);
2051. #endif
2052. #ifdef AMIGA
2053. 	} else if (match_varname(buf, "FONT", 4)) {
2054. 		char *t;
2055. 
2056. 		if( t = strchr( buf+5, ':' ) )
2057. 		{
2058. 		    *t = 0;
2059. 		    amii_set_text_font( buf+5, atoi( t + 1 ) );
2060. 		    *t = ':';
2061. 		}
2062. 	} else if (match_varname(buf, "PATH", 4)) {
2063. 		(void) strncpy(PATH, bufp, PATHLEN-1);
2064. 	} else if (match_varname(buf, "DEPTH", 5)) {
2065. 		extern int amii_numcolors;
2066. 		int val = atoi( bufp );
2067. 		amii_numcolors = 1L << min( DEPTH, val );
2068. 	} else if (match_varname(buf, "DRIPENS", 7)) {
2069. 		int i, val;
2070. 		char *t;
2071. 		for (i = 0, t = strtok(bufp, ",/"); t != (char *)0;
2072. 				i < 20 && (t = strtok((char*)0, ",/")), ++i) {
2073. 			sscanf(t, "%d", &val );
2074. 			flags.amii_dripens[i] = val;
2075. 		}
2076. 	} else if (match_varname(buf, "SCREENMODE", 10 )) {
2077. 		extern long amii_scrnmode;
2078. 		if (!stricmp(bufp,"req"))
2079. 		    amii_scrnmode = 0xffffffff; /* Requester */
2080. 		else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
2081. 		    amii_scrnmode = 0;
2082. 	} else if (match_varname(buf, "MSGPENS", 7)) {
2083. 		extern int amii_msgAPen, amii_msgBPen;
2084. 		char *t = strtok(bufp, ",/");
2085. 		if( t )
2086. 		{
2087. 		    sscanf(t, "%d", &amii_msgAPen);
2088. 		    if( t = strtok((char*)0, ",/") )
2089. 				sscanf(t, "%d", &amii_msgBPen);
2090. 		}
2091. 	} else if (match_varname(buf, "TEXTPENS", 8)) {
2092. 		extern int amii_textAPen, amii_textBPen;
2093. 		char *t = strtok(bufp, ",/");
2094. 		if( t )
2095. 		{
2096. 		    sscanf(t, "%d", &amii_textAPen);
2097. 		    if( t = strtok((char*)0, ",/") )
2098. 				sscanf(t, "%d", &amii_textBPen);
2099. 		}
2100. 	} else if (match_varname(buf, "MENUPENS", 8)) {
2101. 		extern int amii_menuAPen, amii_menuBPen;
2102. 		char *t = strtok(bufp, ",/");
2103. 		if( t )
2104. 		{
2105. 		    sscanf(t, "%d", &amii_menuAPen);
2106. 		    if( t = strtok((char*)0, ",/") )
2107. 				sscanf(t, "%d", &amii_menuBPen);
2108. 		}
2109. 	} else if (match_varname(buf, "STATUSPENS", 10)) {
2110. 		extern int amii_statAPen, amii_statBPen;
2111. 		char *t = strtok(bufp, ",/");
2112. 		if( t )
2113. 		{
2114. 		    sscanf(t, "%d", &amii_statAPen);
2115. 		    if( t = strtok((char*)0, ",/") )
2116. 				sscanf(t, "%d", &amii_statBPen);
2117. 		}
2118. 	} else if (match_varname(buf, "OTHERPENS", 9)) {
2119. 		extern int amii_otherAPen, amii_otherBPen;
2120. 		char *t = strtok(bufp, ",/");
2121. 		if( t )
2122. 		{
2123. 		    sscanf(t, "%d", &amii_otherAPen);
2124. 		    if( t = strtok((char*)0, ",/") )
2125. 				sscanf(t, "%d", &amii_otherBPen);
2126. 		}
2127. 	} else if (match_varname(buf, "PENS", 4)) {
2128. 		extern unsigned short amii_init_map[ AMII_MAXCOLORS ];
2129. 		int i;
2130. 		char *t;
2131. 
2132. 		for (i = 0, t = strtok(bufp, ",/");
2133. 			i < AMII_MAXCOLORS && t != (char *)0;
2134. 			t = strtok((char *)0, ",/"), ++i)
2135. 		{
2136. 			sscanf(t, "%hx", &amii_init_map[i]);
2137. 		}
2138. 		amii_setpens( amii_numcolors = i );
2139. 	} else if (match_varname(buf, "FGPENS", 6)) {
2140. 		extern int foreg[ AMII_MAXCOLORS ];
2141. 		int i;
2142. 		char *t;
2143. 
2144. 		for (i = 0, t = strtok(bufp, ",/");
2145. 			i < AMII_MAXCOLORS && t != (char *)0;
2146. 			t = strtok((char *)0, ",/"), ++i)
2147. 		{
2148. 			sscanf(t, "%d", &foreg[i]);
2149. 		}
2150. 	} else if (match_varname(buf, "BGPENS", 6)) {
2151. 		extern int backg[ AMII_MAXCOLORS ];
2152. 		int i;
2153. 		char *t;
2154. 
2155. 		for (i = 0, t = strtok(bufp, ",/");
2156. 			i < AMII_MAXCOLORS && t != (char *)0;
2157. 			t = strtok((char *)0, ",/"), ++i)
2158. 		{
2159. 			sscanf(t, "%d", &backg[i]);
2160. 		}
2161. #endif
2162. #ifdef USER_SOUNDS
2163. 	} else if (match_varname(buf, "SOUNDDIR", 8)) {
2164. 		sounddir = (char *)strdup(bufp);
2165. 	} else if (match_varname(buf, "SOUND", 5)) {
2166. 		add_sound_mapping(bufp);
2167. #endif
2168. #ifdef QT_GRAPHICS
2169. 	/* These should move to wc_ options */
2170. 	} else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
2171. 		extern char *qt_tilewidth;
2172. 		if (qt_tilewidth == NULL)	
2173. 			qt_tilewidth=(char *)strdup(bufp);
2174. 	} else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
2175. 		extern char *qt_tileheight;
2176. 		if (qt_tileheight == NULL)	
2177. 			qt_tileheight=(char *)strdup(bufp);
2178. 	} else if (match_varname(buf, "QT_FONTSIZE", 11)) {
2179. 		extern char *qt_fontsize;
2180. 		if (qt_fontsize == NULL)
2181. 			qt_fontsize=(char *)strdup(bufp);
2182. 	} else if (match_varname(buf, "QT_COMPACT", 10)) {
2183. 		extern int qt_compact_mode;
2184. 		qt_compact_mode = atoi(bufp);
2185. #endif
2186. #if defined(GL_GRAPHICS) || defined(SDL_GRAPHICS)
2187. 	} else if (match_varname(buf, "GL_OPTIONS", 10)) {
2188. 		Sdlgl_parse_options(bufp, TRUE, TRUE);
2189. #endif
2190. 	} else
2191. 		return 0;
2192. 	return 1;
2193. }
2194. 
2195. #ifdef USER_SOUNDS
2196. boolean
2197. can_read_file(filename)
2198. const char *filename;
2199. {
2200. 	return (access(filename, 4) == 0);
2201. }
2202. #endif /* USER_SOUNDS */
2203. 
2204. void
2205. read_config_file(filename)
2206. const char *filename;
2207. {
2208. #define tmp_levels	(char *)0
2209. #define tmp_ramdisk	(char *)0
2210. 
2211. #if defined(MICRO) || defined(WIN32)
2212. #undef tmp_levels
2213. 	char	tmp_levels[PATHLEN];
2214. # ifdef MFLOPPY
2215. #  ifndef AMIGA
2216. #undef tmp_ramdisk
2217. 	char	tmp_ramdisk[PATHLEN];
2218. #  endif
2219. # endif
2220. #endif
2221. 	char	buf[4*BUFSZ];
2222. 	FILE	*fp;
2223. 	int     i;
2224. #ifdef PROXY_GRAPHICS
2225. 	int	found = FALSE;
2226. 
2227. 	if (!(fp = fopen_config_file(filename)))
2228. 	    goto clnt_process;
2229. 	else
2230. 	    found = TRUE;
2231. #else
2232. 	if (!(fp = fopen_config_file(filename))) goto post_process;
2233. #endif
2234. 
2235. #if defined(MICRO) || defined(WIN32)
2236. # ifdef MFLOPPY
2237. #  ifndef AMIGA
2238. 	tmp_ramdisk[0] = 0;
2239. #  endif
2240. # endif
2241. 	tmp_levels[0] = 0;
2242. #endif
2243. 	/* begin detection of duplicate configfile options */
2244. 	set_duplicate_opt_detection(1);
2245. 
2246. 	while (fgets(buf, 4*BUFSZ, fp)) {
2247. 		if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
2248. 			raw_printf("Bad option line:  \"%.50s\"", buf);
2249. 			wait_synch();
2250. 		}
2251. 	}
2252. 	(void) fclose(fp);
2253. 	
2254. 	/* turn off detection of duplicate configfile options */
2255. 	set_duplicate_opt_detection(0);
2256. 
2257. #ifdef PROXY_GRAPHICS
2258. clnt_process:
2259. 	/*
2260. 	 * When acting as a proxy server, allow the client to provide
2261. 	 * its own config file which overrides values in our config file.
2262. 	 * Note: We don't want to warn of values being present in both
2263. 	 * files, but we do want to warn of duplicates within each file.
2264. 	 */
2265. 	if (!strncmp(windowprocs.name, "proxy/", 6) &&
2266. 		(fp = proxy_config_file_open())) {
2267. 	    found = TRUE;
2268. 	    set_duplicate_opt_detection(1);
2269. 	    while (fgets(buf, 4*BUFSZ, fp)) {
2270. 		if (match_varname(buf, "TILESETS", 7) ||
2271. 		   match_varname(buf, "HACKDIR", 4) ||
2272. 		   match_varname(buf, "LEVELDIR", 4) ||
2273. 		   match_varname(buf, "LEVELS", 4) ||
2274. 		   match_varname(buf, "SAVEDIR", 4) ||
2275. 		   match_varname(buf, "BONESDIR", 5) ||
2276. 		   match_varname(buf, "DATADIR", 4) ||
2277. 		   match_varname(buf, "SCOREDIR", 4) ||
2278. 		   match_varname(buf, "LOCKDIR", 4) ||
2279. 		   match_varname(buf, "CONFIGDIR", 4) ||
2280. 		   match_varname(buf, "TROUBLEDIR", 4) ||
2281. 		   match_varname(buf, "SOUNDDIR", 8) ||
2282. 		   match_varname(buf, "SOUND", 5)) {
2283. 		    /* Quietly ignore many commands. There's no sense in
2284. 		     * the client configuring these and some introduce
2285. 		     * potential security breachs.
2286. 		     */
2287. 		    continue;
2288. 		}
2289. 		if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
2290. 		    pline("Bad option line:  \"%.50s\"", buf);
2291. 		    wait_synch();
2292. 		}
2293. 	    }
2294. 	    proxy_config_file_close(fp);
2295. 	    set_duplicate_opt_detection(0);
2296. 	}
2297. 
2298. 	if (!found) goto post_process;
2299. #endif
2300. 
2301. #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2302. 	/* should be superseded by fqn_prefix[] */
2303. # ifdef MFLOPPY
2304. 	Strcpy(permbones, tmp_levels);
2305. #  ifndef AMIGA
2306. 	if (tmp_ramdisk[0]) {
2307. 		Strcpy(levels, tmp_ramdisk);
2308. 		if (strcmp(permbones, levels))		/* if not identical */
2309. 			ramdisk = TRUE;
2310. 	} else
2311. #  endif /* AMIGA */
2312. 		Strcpy(levels, tmp_levels);
2313. 
2314. 	Strcpy(bones, levels);
2315. # endif /* MFLOPPY */
2316. #endif /* MICRO */
2317. post_process:
2318. 	if (!no_tilesets) {
2319. 		for(i = 0; strlen(def_tilesets[i].name); i++) {
2320. 			strcpy(tilesets[i].name, def_tilesets[i].name);
2321. 			strcpy(tilesets[i].file, def_tilesets[i].file);
2322. 			tilesets[i].flags = def_tilesets[i].flags;
2323. 		}
2324. 		no_tilesets = i;
2325. 	}
2326. 	if (tileset[0] != '\0') {
2327. 		unsigned int len = strlen(tileset);
2328. 		for(i = 0; i < no_tilesets; i++)
2329. 			if (len == strlen(tilesets[i].name) &&
2330. 			    !strncmpi(tilesets[i].name, tileset, len))
2331. 				break;
2332. 		if (i == no_tilesets) {
2333. 			pline("Tileset %s not defined.", tileset);
2334. 			tileset[0] = '\0';
2335. 		}
2336. 		else
2337. 			strcpy(tileset, tilesets[i].name);
2338. 	}
2339. 	return;
2340. }
2341. 
2342. #ifdef WIZARD
2343. STATIC_OVL FILE *
2344. fopen_wizkit_file()
2345. {
2346. 	FILE *fp;
2347. #if defined(VMS) || defined(UNIX)
2348. 	char	tmp_wizkit[BUFSZ];
2349. #endif
2350. 	char *envp;
2351. 
2352. 	envp = nh_getenv("WIZKIT");
2353. 	if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
2354. 	if (!wizkit[0]) return (FILE *)0;
2355. 
2356. #ifdef UNIX
2357. 	if (access(wizkit, 4) == -1) {
2358. 		/* 4 is R_OK on newer systems */
2359. 		/* nasty sneaky attempt to read file through
2360. 		 * NetHack's setuid permissions -- this is a
2361. 		 * place a file name may be wholly under the player's
2362. 		 * control
2363. 		 */
2364. 		raw_printf("Access to %s denied (%d).",
2365. 				wizkit, errno);
2366. 		wait_synch();
2367. 		/* fall through to standard names */
2368. 	} else
2369. #endif
2370. 	if ((fp = fopenp(wizkit, "r")) != (FILE *)0) {
2371. 	    return(fp);
2372. #if defined(UNIX) || defined(VMS)
2373. 	} else {
2374. 	    /* access() above probably caught most problems for UNIX */
2375. 	    raw_printf("Couldn't open requested config file %s (%d).",
2376. 				wizkit, errno);
2377. 	    wait_synch();
2378. #endif
2379. 	}
2380. 
2381. #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2382. 	if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r"))
2383. 								!= (FILE *)0)
2384. 		return(fp);
2385. #else
2386. # ifdef VMS
2387. 	envp = nh_getenv("HOME");
2388. 	if (envp)
2389. 		Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
2390. 	else
2391. 		Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
2392. 	if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
2393. 		return(fp);
2394. # else	/* should be only UNIX left */
2395. 	envp = nh_getenv("HOME");
2396. 	if (envp)
2397. 		Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
2398. 	else 	Strcpy(tmp_wizkit, wizkit);
2399. 	if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
2400. 		return(fp);
2401. 	else if (errno != ENOENT) {
2402. 		/* e.g., problems when setuid NetHack can't search home
2403. 		 * directory restricted to user */
2404. 		raw_printf("Couldn't open default wizkit file %s (%d).",
2405. 					tmp_wizkit, errno);
2406. 		wait_synch();
2407. 	}
2408. # endif
2409. #endif
2410. 	return (FILE *)0;
2411. }
2412. 
2413. void
2414. read_wizkit()
2415. {
2416. 	FILE *fp;
2417. 	char *ep, buf[BUFSZ];
2418. 	struct obj *otmp;
2419. 	boolean bad_items = FALSE, skip = FALSE;
2420. 
2421. 	if (!wizard || !(fp = fopen_wizkit_file())) return;
2422. 
2423. 	while (fgets(buf, (int)(sizeof buf), fp)) {
2424. 	    ep = index(buf, '\n');
2425. 	    if (skip) {	/* in case previous line was too long */
2426. 		if (ep) skip = FALSE; /* found newline; next line is normal */
2427. 	    } else {
2428. 		if (!ep) skip = TRUE; /* newline missing; discard next fgets */
2429. 		else *ep = '\0';		/* remove newline */
2430. 
2431. 		if (buf[0]) {
2432. 			otmp = readobjnam(buf, (struct obj *)0, FALSE);
2433. 			if (otmp) {
2434. 			    if (otmp != &zeroobj)
2435. 				otmp = addinv(otmp);
2436. 			} else {
2437. 			    /* .60 limits output line width to 79 chars */
2438. 			    raw_printf("Bad wizkit item: \"%.60s\"", buf);
2439. 			    bad_items = TRUE;
2440. 			}
2441. 		}
2442. 	    }
2443. 	}
2444. 	if (bad_items)
2445. 	    wait_synch();
2446. 	(void) fclose(fp);
2447. 	return;
2448. }
2449. 
2450. #endif /*WIZARD*/
2451. 
2452. /* ----------  END CONFIG FILE HANDLING ----------- */
2453. 
2454. /* ----------  BEGIN SCOREBOARD CREATION ----------- */
2455. 
2456. /* verify that we can write to the scoreboard file; if not, try to create one */
2457. void
2458. check_recordfile(dir)
2459. const char *dir;
2460. {
2461. #if defined(MAC_MPW) || defined(__MWERKS__)
2462. # pragma unused(dir)
2463. #endif
2464. 	int fd;
2465. #ifndef FILE_AREAS
2466. 	const char *fq_record;
2467. #endif
2468. 
2469. #if defined(UNIX) || defined(VMS)
2470. # ifdef FILE_AREAS
2471. 	fd = open_area(NH_RECORD_AREA, NH_RECORD, O_RDWR, 0);
2472. # else
2473. 	fq_record = fqname(NH_RECORD, SCOREPREFIX, 0);
2474. 	fd = open(fq_record, O_RDWR, 0);
2475. # endif
2476. 
2477. 	if (fd >= 0) {
2478. # ifdef VMS	/* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
2479. 		if (!file_is_stmlf(fd)) {
2480. 		    raw_printf(
2481. 		  "Warning: scoreboard file %s is not in stream_lf format",
2482. 				fq_record);
2483. 		    wait_synch();
2484. 		}
2485. # endif
2486. 	    (void) close(fd);   /* NH_RECORD is accessible */
2487. #  ifdef FILE_AREAS
2488. 	} else if ((fd = open_area(NH_RECORD_AREA, NH_RECORD, O_CREAT|O_RDWR, FCMASK)) >= 0) {
2489. #  else
2490. 	} else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) {
2491. #  endif
2492. 	    (void) close(fd);   /* NH_RECORD newly created */
2493. # if defined(VMS) && !defined(SECURE)
2494. 	    /* Re-protect NH_RECORD with world:read+write+execute+delete access. */
2495. #  ifdef FILE_AREAS
2496. 	    (void) chmod_area(NH_RECORD_AREA, NH_RECORD, FCMASK | 007);
2497. #  else
2498. 	    (void) chmod(fq_record, FCMASK | 007);
2499. #  endif
2500. # endif /* VMS && !SECURE */
2501. 	} else {
2502. # ifdef FILE_AREAS
2503. 	    raw_printf("Warning: cannot write scoreboard file %s", NH_RECORD);
2504. # else
2505. 	    raw_printf("Warning: cannot write scoreboard file %s", fq_record);
2506. # endif
2507. 	    wait_synch();
2508. 	}
2509. #endif  /* !UNIX && !VMS */
2510. #if defined(MICRO) || defined(WIN32)
2511. 	char tmp[PATHLEN];
2512. 
2513. # ifdef OS2_CODEVIEW   /* explicit path on opening for OS/2 */
2514. 	/* how does this work when there isn't an explicit path or fopenp
2515. 	 * for later access to the file via fopen_datafile? ? */
2516. 	(void) strncpy(tmp, dir, PATHLEN - 1);
2517. 	tmp[PATHLEN-1] = '\0';
2518. 	if ((strlen(tmp) + 1 + strlen(NH_RECORD)) < (PATHLEN - 1)) {
2519. 		append_slash(tmp);
2520. 		Strcat(tmp, NH_RECORD);
2521. 	}
2522. #  ifndef FILE_AREAS
2523. 	fq_record = tmp;
2524. #  endif
2525. # else
2526. 	Strcpy(tmp, NH_RECORD);
2527. #  ifndef FILE_AREAS
2528. 	fq_record = fqname(NH_RECORD, SCOREPREFIX, 0);
2529. #  endif
2530. # endif
2531. 
2532. # ifdef FILE_AREAS
2533. 	if ((fd = open_area(NH_RECORD_AREA, tmp, O_RDWR)) < 0) {
2534. # else
2535. 	if ((fd = open(fq_record, O_RDWR)) < 0) {
2536. # endif
2537. 	    /* try to create empty record */
2538. # if defined(FILE_AREAS)
2539. 	    if ((fd = open_area(NH_RECORD_AREA, tmp, O_CREAT|O_RDWR,
2540. 	      S_IREAD|S_IWRITE)) < 0) {
2541. # elif defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__))
2542. 	    /* Aztec doesn't use the third argument */
2543. 	    /* DICE doesn't like it */
2544. 	    if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) {
2545. # else
2546. 	    if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) {
2547. # endif
2548. 	raw_printf("Warning: cannot write record %s", tmp);
2549. 		wait_synch();
2550. 		} else          /* create succeeded */
2551. 		(void) close(fd);
2552. 	} else		/* open succeeded */
2553. 	    (void) close(fd);
2554. #else /* MICRO || WIN32*/
2555. 
2556. # ifdef MAC
2557. 	/* Create the record file, if necessary */
2558. 	fq_record = fqname(NH_RECORD, SCOREPREFIX, 0);
2559. 	fd = macopen (fq_record, O_RDWR | O_CREAT, LOGF_TYPE);
2560. 	if (fd != -1) macclose (fd);
2561. 
2562. 	/* Create the logfile, if necessary */
2563. 	fq_record = fqname(LOGFILE, SCOREPREFIX, 0);
2564. 	fd = macopen (fq_record, O_RDWR | O_CREAT, LOGF_TYPE);
2565. 	if (fd != -1) macclose (fd);
2566. # endif /* MAC */
2567. 
2568. #endif /* MICRO || WIN32*/
2569. }
2570. 
2571. /* ----------  END SCOREBOARD CREATION ----------- */
2572. 
2573. /* ----------  BEGIN PANIC/IMPOSSIBLE LOG ----------- */
2574. 
2575. /*ARGSUSED*/
2576. void
2577. paniclog(type, reason)
2578. const char *type;	/* panic, impossible, trickery */
2579. const char *reason;	/* explanation */
2580. {
2581. #ifdef PANICLOG
2582. 	FILE *lfile;
2583. 	char buf[BUFSZ];
2584. 
2585. 	if (!program_state.in_paniclog) {
2586. 		program_state.in_paniclog = 1;
2587. 		lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX);
2588. 		if (lfile) {
2589. 		    (void) fprintf(lfile, "%s %08ld: %s %s\n",
2590. 				   version_string(buf), yyyymmdd((time_t)0L),
2591. 				   type, reason);
2592. 		    (void) fclose(lfile);
2593. 		}
2594. 		program_state.in_paniclog = 0;
2595. 	}
2596. #endif /* PANICLOG */
2597. 	return;
2598. }
2599. 
2600. /* ----------  END PANIC/IMPOSSIBLE LOG ----------- */
2601. 
2602. #ifdef SELF_RECOVER
2603. 
2604. /* ----------  BEGIN INTERNAL RECOVER ----------- */
2605. boolean
2606. recover_savefile()
2607. {
2608. 	int gfd, lfd, sfd;
2609. 	int lev, savelev, hpid;
2610. 	xchar levc;
2611. 	struct version_info version_data;
2612. 	int processed[256];
2613. 	char savename[SAVESIZE], errbuf[BUFSZ];
2614. 
2615. 	for (lev = 0; lev < 256; lev++)
2616. 		processed[lev] = 0;
2617. 
2618. 	/* level 0 file contains:
2619. 	 *	pid of creating process (ignored here)
2620. 	 *	level number for current level of save file
2621. 	 *	name of save file nethack would have created
2622. 	 *	and game state
2623. 	 */
2624. 	gfd = open_levelfile(0, errbuf);
2625. 	if (gfd < 0) {
2626. 	    raw_printf("%s\n", errbuf);
2627. 	    return FALSE;
2628. 	}
2629. 	if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
2630. 	    raw_printf(
2631. "\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible.");
2632. 	    (void)close(gfd);
2633. 	    return FALSE;
2634. 	}
2635. 	if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
2636. 							!= sizeof(savelev)) {
2637. 	    raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n",
2638. 			lock);
2639. 	    (void)close(gfd);
2640. 	    return FALSE;
2641. 	}
2642. 	if ((read(gfd, (genericptr_t) savename, sizeof savename)
2643. 		!= sizeof savename) ||
2644. 	    (read(gfd, (genericptr_t) &version_data, sizeof version_data)
2645. 		!= sizeof version_data)) {
2646. 	    raw_printf("\nError reading %s -- can't recover.\n", lock);
2647. 	    (void)close(gfd);
2648. 	    return FALSE;
2649. 	}
2650. 
2651. 	/* save file should contain:
2652. 	 *	version info
2653. 	 *	current level (including pets)
2654. 	 *	(non-level-based) game state
2655. 	 *	other levels
2656. 	 */
2657. 	set_savefile_name();
2658. 	sfd = create_savefile();
2659. 	if (sfd < 0) {
2660. 	    raw_printf("\nCannot recover savefile %s.\n", SAVEF);
2661. 	    (void)close(gfd);
2662. 	    return FALSE;
2663. 	}
2664. 
2665. 	lfd = open_levelfile(savelev, errbuf);
2666. 	if (lfd < 0) {
2667. 	    raw_printf("\n%s\n", errbuf);
2668. 	    (void)close(gfd);
2669. 	    (void)close(sfd);
2670. 	    delete_savefile();
2671. 	    return FALSE;
2672. 	}
2673. 
2674. 	if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
2675. 		!= sizeof version_data) {
2676. 	    raw_printf("\nError writing %s; recovery failed.", SAVEF);
2677. 	    (void)close(gfd);
2678. 	    (void)close(sfd);
2679. 	    delete_savefile();
2680. 	    return FALSE;
2681. 	}
2682. 
2683. 	if (!copy_bytes(lfd, sfd)) {
2684. 		(void) close(lfd);
2685. 		(void) close(sfd);
2686. 		delete_savefile();
2687. 		return FALSE;
2688. 	}
2689. 	(void)close(lfd);
2690. 	processed[savelev] = 1;
2691. 
2692. 	if (!copy_bytes(gfd, sfd)) {
2693. 		(void) close(lfd);
2694. 		(void) close(sfd);
2695. 		delete_savefile();
2696. 		return FALSE;
2697. 	}
2698. 	(void)close(gfd);
2699. 	processed[0] = 1;
2700. 
2701. 	for (lev = 1; lev < 256; lev++) {
2702. 		/* level numbers are kept in xchars in save.c, so the
2703. 		 * maximum level number (for the endlevel) must be < 256
2704. 		 */
2705. 		if (lev != savelev) {
2706. 			lfd = open_levelfile(lev, (char *)0);
2707. 			if (lfd >= 0) {
2708. 				/* any or all of these may not exist */
2709. 				levc = (xchar) lev;
2710. 				write(sfd, (genericptr_t) &levc, sizeof(levc));
2711. 				if (!copy_bytes(lfd, sfd)) {
2712. 					(void) close(lfd);
2713. 					(void) close(sfd);
2714. 					delete_savefile();
2715. 					return FALSE;
2716. 				}
2717. 				(void)close(lfd);
2718. 				processed[lev] = 1;
2719. 			}
2720. 		}
2721. 	}
2722. 	(void)close(sfd);
2723. 
2724. #ifdef HOLD_LOCKFILE_OPEN
2725. 	really_close();
2726. #endif
2727. 	/*
2728. 	 * We have a successful savefile!
2729. 	 * Only now do we erase the level files.
2730. 	 */
2731. 	for (lev = 0; lev < 256; lev++) {
2732. 		if (processed[lev]) {
2733. 			const char *fq_lock;
2734. 			set_levelfile_name(lock, lev);
2735. 			fq_lock = fqname(lock, LEVELPREFIX, 3);
2736. 			(void) unlink(fq_lock);
2737. 		}
2738. 	}
2739. 	return TRUE;
2740. }
2741. 
2742. boolean
2743. copy_bytes(ifd, ofd)
2744. int ifd, ofd;
2745. {
2746. 	char buf[BUFSIZ];
2747. 	int nfrom, nto;
2748. 
2749. 	do {
2750. 		nfrom = read(ifd, buf, BUFSIZ);
2751. 		nto = write(ofd, buf, nfrom);
2752. 		if (nto != nfrom) return FALSE;
2753. 	} while (nfrom == BUFSIZ);
2754. 	return TRUE;
2755. }
2756. 
2757. /* ----------  END INTERNAL RECOVER ----------- */
2758. #endif /*SELF_RECOVER*/
2759. 
2760. /*files.c*/