1    | /*	SCCS Id: @(#)files.c	3.3	2000/04/27	*/
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    | #include <ctype.h>
9    | 
10   | #if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
11   | #include <fcntl.h>
12   | #endif
13   | #if defined(UNIX) || defined(VMS)
14   | #include <errno.h>
15   | # ifndef SKIP_ERRNO
16   | extern int errno;
17   | # endif
18   | #include <signal.h>
19   | #endif
20   | 
21   | #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
22   | # ifndef GNUDOS
23   | #include <sys\stat.h>
24   | # else
25   | #include <sys/stat.h>
26   | # endif
27   | #endif
28   | #ifndef O_BINARY	/* used for micros, no-op for others */
29   | # define O_BINARY 0
30   | #endif
31   | 
32   | #ifdef PREFIXES_IN_USE
33   | #define FQN_NUMBUF 2
34   | static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
35   | #endif
36   | 
37   | #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
38   | char bones[] = "bonesnn.xxx";
39   | char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */
40   | #else
41   | # if defined(MFLOPPY)
42   | char bones[FILENAME];		/* pathname of bones files */
43   | char lock[FILENAME];		/* pathname of level files */
44   | # endif
45   | # if defined(VMS)
46   | char bones[] = "bonesnn.xxx;1";
47   | char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */
48   | # endif
49   | # if defined(WIN32)
50   | char bones[] = "bonesnn.xxx";
51   | char lock[PL_NSIZ+25];		/* long enough for username+-+name+.99 */
52   | # endif
53   | #endif
54   | 
55   | #if defined(UNIX) || defined(__BEOS__)
56   | #define SAVESIZE	(PL_NSIZ + 13)	/* save/99999player.e */
57   | #else
58   | # ifdef VMS
59   | #define SAVESIZE	(PL_NSIZ + 22)	/* [.save]<uid>player.e;1 */
60   | # else
61   | #  if defined(WIN32)
62   | #define SAVESIZE	(PL_NSIZ + 40)	/* username-player.NetHack-saved-game */
63   | #  else
64   | #define SAVESIZE	FILENAME	/* from macconf.h or pcconf.h */
65   | #  endif
66   | # endif
67   | #endif
68   | 
69   | char SAVEF[SAVESIZE];	/* holds relative path of save file from playground */
70   | #ifdef MICRO
71   | char SAVEP[SAVESIZE];	/* holds path of directory for save file */
72   | #endif
73   | 
74   | #ifdef AMIGA
75   | extern char PATH[];	/* see sys/amiga/amidos.c */
76   | extern char bbs_id[];
77   | static int lockptr;
78   | # ifdef __SASC_60
79   | #include <proto/dos.h>
80   | # endif
81   | 
82   | #include <libraries/dos.h>
83   | #endif
84   | 
85   | #if defined(WIN32) || defined(MSDOS)
86   | static int lockptr;
87   | # ifdef MSDOS
88   | #define Delay(a) msleep(a)
89   | # endif
90   | #define Close close
91   | #define DeleteFile unlink
92   | #endif
93   | 
94   | extern int n_dgns;		/* from dungeon.c */
95   | 
96   | STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*));
97   | STATIC_DCL char *NDECL(set_bonestemp_name);
98   | #ifdef COMPRESS
99   | STATIC_DCL void FDECL(redirect, (char *,char *,FILE *,BOOLEAN_P));
100  | STATIC_DCL void FDECL(docompress_file, (char *,BOOLEAN_P));
101  | #endif
102  | STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
103  | STATIC_DCL FILE *FDECL(fopen_config_file, (const char *));
104  | STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,int,const char *));
105  | int FDECL(parse_config_line, (FILE *,char *,char *,char *));
106  | #ifdef NOCWD_ASSUMPTIONS
107  | STATIC_DCL void FDECL(adjust_prefix, (char *, int));
108  | #endif
109  | 
110  | #ifndef PREFIXES_IN_USE
111  | /*ARGSUSED*/
112  | #endif
113  | const char *
114  | fqname(basename, whichprefix, buffnum)
115  | const char *basename;
116  | int whichprefix, buffnum;
117  | {
118  | #ifndef PREFIXES_IN_USE
119  | 	return basename;
120  | #else
121  | 	if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
122  | 		return basename;
123  | 	if (!fqn_prefix[whichprefix])
124  | 		return basename;
125  | 	if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
126  | 		impossible("Invalid fqn_filename_buffer specified: %d",
127  | 								buffnum);
128  | 		buffnum = 0;
129  | 	}
130  | 	if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >=
131  | 						    FQN_MAX_FILENAME) {
132  | 		impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
133  | 						basename);
134  | 		return basename;	/* XXX */
135  | 	}
136  | 	Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
137  | 	return strcat(fqn_filename_buffer[buffnum], basename);
138  | #endif
139  | }
140  | 
141  | 
142  | /* fopen a file, with OS-dependent bells and whistles */
143  | /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
144  | FILE *
145  | fopen_datafile(filename, mode, use_scoreprefix)
146  | const char *filename, *mode;
147  | boolean use_scoreprefix;
148  | {
149  | 	FILE *fp;
150  | 
151  | #ifdef AMIGA
152  | 	fp = fopenp(filename, mode);
153  | #else
154  | 	filename = fqname(filename,
155  | 				use_scoreprefix ? SCOREPREFIX : DATAPREFIX, 0);
156  | # ifdef VMS	/* essential to have punctuation, to avoid logical names */
157  |     {
158  | 	char tmp[BUFSIZ];
159  | 
160  | 	if (!index(filename, '.') && !index(filename, ';'))
161  | 		filename = strcat(strcpy(tmp, filename), ";0");
162  | 	fp = fopen(filename, mode, "mbc=16");
163  |     }
164  | # else
165  | 	fp = fopen(filename, mode);
166  | # endif
167  | #endif
168  | 	return fp;
169  | }
170  | 
171  | /* ----------  BEGIN LEVEL FILE HANDLING ----------- */
172  | 
173  | #ifdef MFLOPPY
174  | /* Set names for bones[] and lock[] */
175  | void
176  | set_lock_and_bones()
177  | {
178  | 	if (!ramdisk) {
179  | 		Strcpy(levels, permbones);
180  | 		Strcpy(bones, permbones);
181  | 	}
182  | 	append_slash(permbones);
183  | 	append_slash(levels);
184  | #ifdef AMIGA
185  | 	strncat(levels, bbs_id, PATHLEN);
186  | #endif
187  | 	append_slash(bones);
188  | #ifndef AMIGA /* We'll want bones & levels in the user specified directory -jhsa */
189  | 	Strcat(bones, "bonesnn.*");
190  | #endif
191  | 	Strcpy(lock, levels);
192  | #ifndef AMIGA
193  | 	Strcat(lock, alllevels);
194  | #endif
195  | 	return;
196  | }
197  | #endif /* MFLOPPY */
198  | 
199  | 
200  | /* Construct a file name for a level-type file, which is of the form
201  |  * something.level (with any old level stripped off).
202  |  * This assumes there is space on the end of 'file' to append
203  |  * a two digit number.  This is true for 'level'
204  |  * but be careful if you use it for other things -dgk
205  |  */
206  | void
207  | set_levelfile_name(file, lev)
208  | char *file;
209  | int lev;
210  | {
211  | 	char *tf;
212  | 
213  | 	tf = rindex(file, '.');
214  | 	if (!tf) tf = eos(file);
215  | 	Sprintf(tf, ".%d", lev);
216  | #ifdef VMS
217  | 	Strcat(tf, ";1");
218  | #endif
219  | 	return;
220  | }
221  | 
222  | int
223  | create_levelfile(lev)
224  | int lev;
225  | {
226  | 	int fd;
227  | 	const char *fq_lock;
228  | 
229  | 	set_levelfile_name(lock, lev);
230  | 	fq_lock = fqname(lock, LEVELPREFIX, 0);
231  | 
232  | #if defined(MICRO)
233  | 	/* Use O_TRUNC to force the file to be shortened if it already
234  | 	 * exists and is currently longer.
235  | 	 */
236  | 	fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
237  | #else
238  | # ifdef MAC
239  | 	fd = maccreat(fq_lock, LEVL_TYPE);
240  | # else
241  | 	fd = creat(fq_lock, FCMASK);
242  | # endif
243  | #endif /* MICRO */
244  | 
245  | 	if (fd >= 0)
246  | 	    level_info[lev].flags |= LFILE_EXISTS;
247  | 
248  | 	return fd;
249  | }
250  | 
251  | 
252  | int
253  | open_levelfile(lev)
254  | int lev;
255  | {
256  | 	int fd;
257  | 	const char *fq_lock;
258  | 
259  | 	set_levelfile_name(lock, lev);
260  | 	fq_lock = fqname(lock, LEVELPREFIX, 0);
261  | #ifdef MFLOPPY
262  | 	/* If not currently accessible, swap it in. */
263  | 	if (level_info[lev].where != ACTIVE)
264  | 		swapin_file(lev);
265  | #endif
266  | #ifdef MAC
267  | 	fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
268  | #else
269  | 	fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
270  | #endif
271  | 	return fd;
272  | }
273  | 
274  | 
275  | void
276  | delete_levelfile(lev)
277  | int lev;
278  | {
279  | 	/*
280  | 	 * Level 0 might be created by port specific code that doesn't
281  | 	 * call create_levfile(), so always assume that it exists.
282  | 	 */
283  | 	if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
284  | 		set_levelfile_name(lock, lev);
285  | 		(void) unlink(fqname(lock, LEVELPREFIX, 0));
286  | 		level_info[lev].flags &= ~LFILE_EXISTS;
287  | 	}
288  | }
289  | 
290  | 
291  | void
292  | clearlocks()
293  | {
294  | #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
295  | 	eraseall(levels, alllevels);
296  | 	if (ramdisk)
297  | 		eraseall(permbones, alllevels);
298  | #else
299  | 	register int x;
300  | 
301  | # if defined(UNIX) || defined(VMS)
302  | 	(void) signal(SIGHUP, SIG_IGN);
303  | # endif
304  | 	/* can't access maxledgerno() before dungeons are created -dlc */
305  | 	for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
306  | 		delete_levelfile(x);	/* not all levels need be present */
307  | #endif
308  | }
309  | 
310  | /* ----------  END LEVEL FILE HANDLING ----------- */
311  | 
312  | 
313  | /* ----------  BEGIN BONES FILE HANDLING ----------- */
314  | 
315  | /* set up "file" to be file name for retrieving bones, and return a
316  |  * bonesid to be read/written in the bones file.
317  |  */
318  | STATIC_OVL char *
319  | set_bonesfile_name(file, lev)
320  | char *file;
321  | d_level *lev;
322  | {
323  | 	s_level *sptr;
324  | 	char *dptr;
325  | #ifdef AMIGA
326  | 	char bonetmp[16];
327  | #endif
328  | 
329  | #ifdef AMIGA /* We'll want the bones go the user defined Levels directory -jhsa */
330  | 	/* permbones should be subsumed by fqn_prefix[BONESPREFIX] */
331  | 	Sprintf(bonetmp, "bon%c%s", dungeons[lev->dnum].boneid,
332  | 			In_quest(lev) ? urole.filecode : "0");
333  | 	Strcpy(file, permbones);
334  | 	Strcat(file, bonetmp);
335  | #else
336  | 	Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
337  | 			In_quest(lev) ? urole.filecode : "0");
338  | #endif
339  | 	dptr = eos(file);
340  | 	if ((sptr = Is_special(lev)) != 0)
341  | 	    Sprintf(dptr, ".%c", sptr->boneid);
342  | 	else
343  | 	    Sprintf(dptr, ".%d", lev->dlevel);
344  | #ifdef VMS
345  | 	Strcat(dptr, ";1");
346  | #endif
347  | 	return(dptr-2);
348  | }
349  | 
350  | /* set up temporary file name for writing bones, to avoid another game's
351  |  * trying to read from an uncompleted bones file.  we want an uncontentious
352  |  * name, so use one in the namespace reserved for this game's level files.
353  |  * (we are not reading or writing level files while writing bones files, so
354  |  * the same array may be used instead of copying.)
355  |  */
356  | STATIC_OVL char *
357  | set_bonestemp_name()
358  | {
359  | 	char *tf;
360  | 
361  | 	tf = rindex(lock, '.');
362  | 	if (!tf) tf = eos(lock);
363  | 	Sprintf(tf, ".bn");
364  | #ifdef VMS
365  | 	Strcat(tf, ";1");
366  | #endif
367  | 	return lock;
368  | }
369  | 
370  | int
371  | create_bonesfile(lev, bonesid)
372  | d_level *lev;
373  | char **bonesid;
374  | {
375  | 	const char *file;
376  | 	int fd;
377  | 
378  | 	*bonesid = set_bonesfile_name(bones, lev);
379  | 	file = set_bonestemp_name();
380  | 	file = fqname(file, BONESPREFIX, 0);
381  | 
382  | #ifdef MICRO
383  | 	/* Use O_TRUNC to force the file to be shortened if it already
384  | 	 * exists and is currently longer.
385  | 	 */
386  | 	fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
387  | #else
388  | # ifdef MAC
389  | 	fd = maccreat(file, BONE_TYPE);
390  | # else
391  | 	fd = creat(file, FCMASK);
392  | # endif
393  | # if defined(VMS) && !defined(SECURE)
394  | 	/*
395  | 	   Re-protect bones file with world:read+write+execute+delete access.
396  | 	   umask() doesn't seem very reliable; also, vaxcrtl won't let us set
397  | 	   delete access without write access, which is what's really wanted.
398  | 	   Can't simply create it with the desired protection because creat
399  | 	   ANDs the mask with the user's default protection, which usually
400  | 	   denies some or all access to world.
401  | 	 */
402  | 	(void) chmod(file, FCMASK | 007);  /* allow other users full access */
403  | # endif /* VMS && !SECURE */
404  | #endif /* MICRO */
405  | 
406  | 	return fd;
407  | }
408  | 
409  | #ifdef MFLOPPY
410  | /* remove partial bonesfile in process of creation */
411  | void
412  | cancel_bonesfile()
413  | {
414  | 	const char *tempname;
415  | 
416  | 	tempname = set_bonestemp_name();
417  | 	tempname = fqname(tempname, BONESPREFIX, 0);
418  | 	(void) unlink(tempname);
419  | }
420  | #endif /* MFLOPPY */
421  | 
422  | /* move completed bones file to proper name */
423  | void
424  | commit_bonesfile(lev)
425  | d_level *lev;
426  | {
427  | 	const char *fq_bones, *tempname;
428  | 	int ret;
429  | 
430  | 	(void) set_bonesfile_name(bones, lev);
431  | 	fq_bones = fqname(bones, BONESPREFIX, 0);
432  | 	tempname = set_bonestemp_name();
433  | 	tempname = fqname(tempname, BONESPREFIX, 1);
434  | 
435  | #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
436  | 	/* old SYSVs don't have rename.  Some SVR3's may, but since they
437  | 	 * also have link/unlink, it doesn't matter. :-)
438  | 	 */
439  | 	(void) unlink(fq_bones);
440  | 	ret = link(tempname, fq_bones);
441  | 	ret += unlink(tempname);
442  | #else
443  | 	ret = rename(tempname, fq_bones);
444  | #endif
445  | #ifdef WIZARD
446  | 	if (wizard && ret != 0)
447  | 		pline("couldn't rename %s to %s", tempname, fq_bones);
448  | #endif
449  | }
450  | 
451  | 
452  | int
453  | open_bonesfile(lev, bonesid)
454  | d_level *lev;
455  | char **bonesid;
456  | {
457  | 	const char *fq_bones;
458  | 	int fd;
459  | 
460  | 	*bonesid = set_bonesfile_name(bones, lev);
461  | 	fq_bones = fqname(bones, BONESPREFIX, 0);
462  | 	uncompress(fq_bones);	/* no effect if nonexistent */
463  | #ifdef MAC
464  | 	fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
465  | #else
466  | 	fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
467  | #endif
468  | 	return fd;
469  | }
470  | 
471  | 
472  | int
473  | delete_bonesfile(lev)
474  | d_level *lev;
475  | {
476  | 	(void) set_bonesfile_name(bones, lev);
477  | 	return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
478  | }
479  | 
480  | 
481  | /* assume we're compressing the recently read or created bonesfile, so the
482  |  * file name is already set properly */
483  | void
484  | compress_bonesfile()
485  | {
486  | 	compress(fqname(bones, BONESPREFIX, 0));
487  | }
488  | 
489  | /* ----------  END BONES FILE HANDLING ----------- */
490  | 
491  | 
492  | /* ----------  BEGIN SAVE FILE HANDLING ----------- */
493  | 
494  | /* set savefile name in OS-dependent manner from pre-existing plname,
495  |  * avoiding troublesome characters */
496  | void
497  | set_savefile_name()
498  | {
499  | #ifdef VMS
500  | 	Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
501  | 	regularize(SAVEF+7);
502  | 	Strcat(SAVEF, ";1");
503  | #else
504  | # if defined(MICRO) && !defined(WIN32)
505  | 	Strcpy(SAVEF, SAVEP);
506  | #  ifdef AMIGA
507  | 	strncat(SAVEF, bbs_id, PATHLEN);
508  | #  endif
509  | 	{
510  | 		int i = strlen(SAVEP);
511  | #  ifdef AMIGA
512  | 		/* plname has to share space with SAVEP and ".sav" */
513  | 		(void)strncat(SAVEF, plname, FILENAME - i - 4);
514  | #  else
515  | 		(void)strncat(SAVEF, plname, 8);
516  | #  endif
517  | 		regularize(SAVEF+i);
518  | 	}
519  | 	Strcat(SAVEF, ".sav");
520  | # else
521  | #  if defined(WIN32)
522  | 	Sprintf(SAVEF,"%s-%s.NetHack-saved-game",get_username(0), plname);
523  | #  else
524  | 	Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname);
525  | 	regularize(SAVEF+5);	/* avoid . or / in name */
526  | #  endif /* WIN32 */
527  | # endif	/* MICRO */
528  | #endif /* VMS   */
529  | }
530  | 
531  | #ifdef INSURANCE
532  | void
533  | save_savefile_name(fd)
534  | int fd;
535  | {
536  | 	(void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
537  | }
538  | #endif
539  | 
540  | 
541  | #if defined(WIZARD) && !defined(MICRO)
542  | /* change pre-existing savefile name to indicate an error savefile */
543  | void
544  | set_error_savefile()
545  | {
546  | # ifdef VMS
547  |       {
548  | 	char *semi_colon = rindex(SAVEF, ';');
549  | 	if (semi_colon) *semi_colon = '\0';
550  |       }
551  | 	Strcat(SAVEF, ".e;1");
552  | # else
553  | #  ifdef MAC
554  | 	Strcat(SAVEF, "-e");
555  | #  else
556  | 	Strcat(SAVEF, ".e");
557  | #  endif
558  | # endif
559  | }
560  | #endif
561  | 
562  | 
563  | /* create save file, overwriting one if it already exists */
564  | int
565  | create_savefile()
566  | {
567  | 	const char *fq_save;
568  | 	int fd;
569  | 
570  | #ifdef AMIGA
571  | 	fd = ami_wbench_getsave(O_WRONLY | O_CREAT | O_TRUNC);
572  | #else
573  | 	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
574  | # ifdef MICRO
575  | 	fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
576  | # else
577  | #  ifdef MAC
578  | 	fd = maccreat(fq_save, SAVE_TYPE);
579  | #  else
580  | 	fd = creat(fq_save, FCMASK);
581  | #  endif
582  | #  if defined(VMS) && !defined(SECURE)
583  | 	/*
584  | 	   Make sure the save file is owned by the current process.  That's
585  | 	   the default for non-privileged users, but for priv'd users the
586  | 	   file will be owned by the directory's owner instead of the user.
587  | 	 */
588  | #   ifdef getuid	/*(see vmsunix.c)*/
589  | #    undef getuid
590  | #   endif
591  | 	(void) chown(fq_save, getuid(), getgid());
592  | #  endif /* VMS && !SECURE */
593  | # endif	/* MICRO */
594  | #endif	/* AMIGA */
595  | 
596  | 	return fd;
597  | }
598  | 
599  | 
600  | /* open savefile for reading */
601  | int
602  | open_savefile()
603  | {
604  | 	const char *fq_save;
605  | 	int fd;
606  | 
607  | #ifdef AMIGA
608  | 	fd = ami_wbench_getsave(O_RDONLY);
609  | #else
610  | 	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
611  | # ifdef MAC
612  | 	fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
613  | # else
614  | 	fd = open(fq_save, O_RDONLY | O_BINARY, 0);
615  | # endif
616  | #endif /* AMIGA */
617  | 	return fd;
618  | }
619  | 
620  | 
621  | /* delete savefile */
622  | int
623  | delete_savefile()
624  | {
625  | #ifdef AMIGA
626  | 	ami_wbench_unlink(SAVEF);
627  | #endif
628  | 	(void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
629  | 	return 0;	/* for restore_saved_game() (ex-xxxmain.c) test */
630  | }
631  | 
632  | 
633  | /* try to open up a save file and prepare to restore it */
634  | int
635  | restore_saved_game()
636  | {
637  | 	const char *fq_save;
638  | 	int fd;
639  | 
640  | 	set_savefile_name();
641  | #ifdef MFLOPPY
642  | 	if (
643  | # ifdef AMIGA
644  | 	    !(FromWBench || saveDiskPrompt(1))
645  | # else
646  | 	    !saveDiskPrompt(1)
647  | # endif
648  | 	  ) return -1;
649  | #endif /* MFLOPPY */
650  | 	fq_save = fqname(SAVEF, SAVEPREFIX, 0);
651  | 
652  | 	uncompress(fq_save);
653  | 	if ((fd = open_savefile()) < 0) return fd;
654  | 
655  | 	if (!uptodate(fd, fq_save)) {
656  | 	    (void) close(fd),  fd = -1;
657  | 	    (void) delete_savefile();
658  | 	}
659  | 	return fd;
660  | }
661  | 
662  | /* ----------  END SAVE FILE HANDLING ----------- */
663  | 
664  | 
665  | /* ----------  BEGIN FILE COMPRESSION HANDLING ----------- */
666  | 
667  | #ifdef COMPRESS
668  | 
669  | STATIC_OVL void
670  | redirect(filename, mode, stream, uncomp)
671  | char *filename, *mode;
672  | FILE *stream;
673  | boolean uncomp;
674  | {
675  | 	if (freopen(filename, mode, stream) == (FILE *)0) {
676  | 		(void) fprintf(stderr, "freopen of %s for %scompress failed\n",
677  | 			filename, uncomp ? "un" : "");
678  | 		terminate(EXIT_FAILURE);
679  | 	}
680  | }
681  | 
682  | /*
683  |  * using system() is simpler, but opens up security holes and causes
684  |  * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
685  |  * setuid is renounced by /bin/sh, so the files cannot be accessed.
686  |  *
687  |  * cf. child() in unixunix.c.
688  |  */
689  | STATIC_OVL void
690  | docompress_file(filename, uncomp)
691  | char *filename;
692  | boolean uncomp;
693  | {
694  | 	char cfn[80];
695  | 	FILE *cf;
696  | 	const char *args[10];
697  | # ifdef COMPRESS_OPTIONS
698  | 	char opts[80];
699  | # endif
700  | 	int i = 0;
701  | 	int f;
702  | 
703  | 	Strcpy(cfn, filename);
704  | # ifdef COMPRESS_EXTENSION
705  | 	Strcat(cfn, COMPRESS_EXTENSION);
706  | # endif
707  | 	/* when compressing, we know the file exists */
708  | 	if (uncomp) {
709  | 	    if ((cf = fopen(cfn, RDBMODE)) == (FILE *)0)
710  | 		    return;
711  | 	    (void) fclose(cf);
712  | 	}
713  | 
714  | 	args[0] = COMPRESS;
715  | 	if (uncomp) args[++i] = "-d";	/* uncompress */
716  | # ifdef COMPRESS_OPTIONS
717  | 	{
718  | 	    /* we can't guarantee there's only one additional option, sigh */
719  | 	    char *opt;
720  | 	    boolean inword = FALSE;
721  | 
722  | 	    Strcpy(opts, COMPRESS_OPTIONS);
723  | 	    opt = opts;
724  | 	    while (*opt) {
725  | 		if ((*opt == ' ') || (*opt == '\t')) {
726  | 		    if (inword) {
727  | 			*opt = '\0';
728  | 			inword = FALSE;
729  | 		    }
730  | 		} else if (!inword) {
731  | 		    args[++i] = opt;
732  | 		    inword = TRUE;
733  | 		}
734  | 		opt++;
735  | 	    }
736  | 	}
737  | # endif
738  | 	args[++i] = (char *)0;
739  | 
740  | 	f = fork();
741  | 	if (f == 0) {	/* child */
742  | 		/* run compressor without privileges, in case other programs
743  | 		 * have surprises along the line of gzip once taking filenames
744  | 		 * in GZIP.
745  | 		 */
746  | 		/* assume all compressors will compress stdin to stdout
747  | 		 * without explicit filenames.  this is true of at least
748  | 		 * compress and gzip, those mentioned in config.h.
749  | 		 */
750  | 		if (uncomp) {
751  | 			redirect(cfn, RDBMODE, stdin, uncomp);
752  | 			redirect(filename, WRBMODE, stdout, uncomp);
753  | 		} else {
754  | 			redirect(filename, RDBMODE, stdin, uncomp);
755  | 			redirect(cfn, WRBMODE, stdout, uncomp);
756  | 		}
757  | 		(void) setgid(getgid());
758  | 		(void) setuid(getuid());
759  | 		(void) execv(args[0], (char *const *) args);
760  | 		perror((char *)0);
761  | 		(void) fprintf(stderr, "Exec to %scompress %s failed.\n",
762  | 			uncomp ? "un" : "", filename);
763  | 		terminate(EXIT_FAILURE);
764  | 	} else if (f == -1) {
765  | 		perror((char *)0);
766  | 		pline("Fork to %scompress %s failed.",
767  | 			uncomp ? "un" : "", filename);
768  | 		return;
769  | 	}
770  | 	(void) signal(SIGINT, SIG_IGN);
771  | 	(void) signal(SIGQUIT, SIG_IGN);
772  | 	(void) wait((int *)&i);
773  | 	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
774  | # ifdef WIZARD
775  | 	if (wizard) (void) signal(SIGQUIT, SIG_DFL);
776  | # endif
777  | 	if (i == 0) {
778  | 	    /* (un)compress succeeded: remove file left behind */
779  | 	    if (uncomp)
780  | 		(void) unlink(cfn);
781  | 	    else
782  | 		(void) unlink(filename);
783  | 	} else {
784  | 	    /* (un)compress failed; remove the new, bad file */
785  | 	    if (uncomp) {
786  | 		raw_printf("Unable to uncompress %s", filename);
787  | 		(void) unlink(filename);
788  | 	    } else {
789  | 		/* no message needed for compress case; life will go on */
790  | 		(void) unlink(cfn);
791  | 	    }
792  | 	}
793  | }
794  | #endif	/* COMPRESS */
795  | 
796  | /* compress file */
797  | void
798  | compress(filename)
799  | const char *filename;
800  | {
801  | #ifndef COMPRESS
802  | #if defined(applec) || defined(__MWERKS__)
803  | # pragma unused(filename)
804  | #endif
805  | #else
806  | 	docompress_file(filename, FALSE);
807  | #endif
808  | }
809  | 
810  | 
811  | /* uncompress file if it exists */
812  | void
813  | uncompress(filename)
814  | const char *filename;
815  | {
816  | #ifndef COMPRESS
817  | #if defined(applec) || defined(__MWERKS__)
818  | # pragma unused(filename)
819  | #endif
820  | #else
821  | 	docompress_file(filename, TRUE);
822  | #endif
823  | }
824  | 
825  | /* ----------  END FILE COMPRESSION HANDLING ----------- */
826  | 
827  | 
828  | /* ----------  BEGIN FILE LOCKING HANDLING ----------- */
829  | 
830  | static int nesting = 0;
831  | 
832  | #ifdef NO_FILE_LINKS	/* implies UNIX */
833  | static int lockfd;	/* for lock_file() to pass to unlock_file() */
834  | #endif
835  | 
836  | #define HUP	if (!program_state.done_hup)
837  | 
838  | STATIC_OVL char *
839  | make_lockname(filename, lockname)
840  | const char *filename;
841  | char *lockname;
842  | {
843  | #if defined(applec) || defined(__MWERKS__)
844  | # pragma unused(filename,lockname)
845  | 	return (char*)0;
846  | #else
847  | # if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS)
848  | #  ifdef NO_FILE_LINKS
849  | 	Strcpy(lockname, LOCKDIR);
850  | 	Strcat(lockname, "/");
851  | 	Strcat(lockname, filename);
852  | #  else
853  | 	Strcpy(lockname, filename);
854  | #  endif
855  | #  ifdef VMS
856  |       {
857  | 	char *semi_colon = rindex(lockname, ';');
858  | 	if (semi_colon) *semi_colon = '\0';
859  |       }
860  | 	Strcat(lockname, ".lock;1");
861  | #  else
862  | 	Strcat(lockname, "_lock");
863  | #  endif
864  | 	return lockname;
865  | # else
866  | 	lockname[0] = '\0';
867  | 	return (char*)0;
868  | # endif  /* UNIX || VMS || AMIGA || WIN32 || MSDOS */
869  | #endif
870  | }
871  | 
872  | 
873  | /* lock a file */
874  | boolean
875  | lock_file(filename, whichprefix, retryct)
876  | const char *filename;
877  | int whichprefix;
878  | int retryct;
879  | {
880  | #if defined(applec) || defined(__MWERKS__)
881  | # pragma unused(filename, retryct)
882  | #endif
883  | 	char locknambuf[BUFSZ];
884  | 	const char *lockname;
885  | 
886  | 	nesting++;
887  | 	if (nesting > 1) {
888  | 	    impossible("TRIED TO NEST LOCKS");
889  | 	    return TRUE;
890  | 	}
891  | 
892  | 	lockname = make_lockname(filename, locknambuf);
893  | 	filename = fqname(filename, whichprefix, 0);
894  | #ifndef NO_FILE_LINKS	/* LOCKDIR should be subsumed by LOCKPREFIX */
895  | 	lockname = fqname(lockname, LOCKPREFIX, 1);
896  | #endif
897  | 
898  | #if defined(UNIX) || defined(VMS)
899  | # ifdef NO_FILE_LINKS
900  | 	while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) {
901  | # else
902  | 	while (link(filename, lockname) == -1) {
903  | # endif
904  | 	    register int errnosv = errno;
905  | 
906  | 	    switch (errnosv) {	/* George Barbanis */
907  | 	    case EEXIST:
908  | 		if (retryct--) {
909  | 		    HUP raw_printf(
910  | 			    "Waiting for access to %s.  (%d retries left).",
911  | 			    filename, retryct);
912  | # if defined(SYSV) || defined(ULTRIX) || defined(VMS)
913  | 		    (void)
914  | # endif
915  | 			sleep(1);
916  | 		} else {
917  | 		    HUP (void) raw_print("I give up.  Sorry.");
918  | 		    HUP raw_printf("Perhaps there is an old %s around?",
919  | 					lockname);
920  | 		    nesting--;
921  | 		    return FALSE;
922  | 		}
923  | 
924  | 		break;
925  | 	    case ENOENT:
926  | 		HUP raw_printf("Can't find file %s to lock!", filename);
927  | 		nesting--;
928  | 		return FALSE;
929  | 	    case EACCES:
930  | 		HUP raw_printf("No write permission to lock %s!", filename);
931  | 		nesting--;
932  | 		return FALSE;
933  | # ifdef VMS			/* c__translate(vmsfiles.c) */
934  | 	    case EPERM:
935  | 		/* could be misleading, but usually right */
936  | 		HUP raw_printf("Can't lock %s due to directory protection.",
937  | 			       filename);
938  | 		nesting--;
939  | 		return FALSE;
940  | # endif
941  | 	    default:
942  | 		HUP perror(lockname);
943  | 		HUP raw_printf(
944  | 			     "Cannot lock %s for unknown reason (%d).",
945  | 			       filename, errnosv);
946  | 		nesting--;
947  | 		return FALSE;
948  | 	    }
949  | 
950  | 	}
951  | #endif  /* UNIX || VMS */
952  | 
953  | #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
954  |     lockptr = 0;
955  |     while (retryct-- && !lockptr) {
956  | # ifdef AMIGA
957  | 	(void)DeleteFile(lockname); /* in case dead process was here first */
958  | 	lockptr = Open(lockname,MODE_NEWFILE);
959  | # else
960  | 	lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE);
961  | # endif
962  | 	if (!lockptr) {
963  | 	    raw_printf("Waiting for access to %s.  (%d retries left).",
964  | 			filename, retryct);
965  | 	    Delay(50);
966  | 	}
967  |     }
968  |     if (!retryct) {
969  | 	raw_printf("I give up.  Sorry.");
970  | 	nesting--;
971  | 	return FALSE;
972  |     }
973  | #endif /* AMIGA || WIN32 || MSDOS */
974  | 	return TRUE;
975  | }
976  | 
977  | 
978  | #ifdef VMS	/* for unlock_file, use the unlink() routine in vmsunix.c */
979  | # ifdef unlink
980  | #  undef unlink
981  | # endif
982  | # define unlink(foo) vms_unlink(foo)
983  | #endif
984  | 
985  | /* unlock file, which must be currently locked by lock_file */
986  | void
987  | unlock_file(filename)
988  | const char *filename;
989  | #if defined(applec)
990  | # pragma unused(filename)
991  | #endif
992  | {
993  | 	char locknambuf[BUFSZ];
994  | 	const char *lockname;
995  | 
996  | 	if (nesting == 1) {
997  | 		lockname = make_lockname(filename, locknambuf);
998  | #ifndef NO_FILE_LINKS	/* LOCKDIR should be subsumed by LOCKPREFIX */
999  | 		lockname = fqname(lockname, LOCKPREFIX, 1);
1000 | #endif
1001 | 
1002 | #if defined(UNIX) || defined(VMS)
1003 | 		if (unlink(lockname) < 0)
1004 | 			HUP raw_printf("Can't unlink %s.", lockname);
1005 | # ifdef NO_FILE_LINKS
1006 | 		(void) close(lockfd);
1007 | # endif
1008 | 
1009 | #endif  /* UNIX || VMS */
1010 | 
1011 | #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1012 | 		if (lockptr) Close(lockptr);
1013 | 		DeleteFile(lockname);
1014 | 		lockptr = 0;
1015 | #endif /* AMIGA || WIN32 || MSDOS */
1016 | 	}
1017 | 
1018 | 	nesting--;
1019 | }
1020 | 
1021 | /* ----------  END FILE LOCKING HANDLING ----------- */
1022 | 
1023 | 
1024 | /* ----------  BEGIN CONFIG FILE HANDLING ----------- */
1025 | 
1026 | const char *configfile =
1027 | #ifdef UNIX
1028 | 			".nethackrc";
1029 | #else
1030 | # if defined(MAC) || defined(__BEOS__)
1031 | 			"NetHack Defaults";
1032 | # else
1033 | #  if defined(MSDOS) || defined(WIN32)
1034 | 			"defaults.nh";
1035 | #  else
1036 | 			"NetHack.cnf";
1037 | #  endif
1038 | # endif
1039 | #endif
1040 | 
1041 | 
1042 | #ifdef MSDOS
1043 | /* conflict with speed-dial under windows
1044 |  * for XXX.cnf file so support of NetHack.cnf
1045 |  * is for backward compatibility only.
1046 |  * Preferred name (and first tried) is now defaults.nh but
1047 |  * the game will try the old name if there
1048 |  * is no defaults.nh.
1049 |  */
1050 | const char *backward_compat_configfile = "nethack.cnf"; 
1051 | #endif
1052 | 
1053 | #ifndef MFLOPPY
1054 | #define fopenp fopen
1055 | #endif
1056 | 
1057 | STATIC_OVL FILE *
1058 | fopen_config_file(filename)
1059 | const char *filename;
1060 | {
1061 | 	FILE *fp;
1062 | #if defined(UNIX) || defined(VMS)
1063 | 	char	tmp_config[BUFSZ];
1064 | 	char *envp;
1065 | #endif
1066 | 
1067 | 	/* "filename" is an environment variable, so it should hang around */
1068 | 	/* if set, it is expected to be a full path name (if relevant) */
1069 | 	if (filename) {
1070 | #ifdef UNIX
1071 | 		if (access(filename, 4) == -1) {
1072 | 			/* 4 is R_OK on newer systems */
1073 | 			/* nasty sneaky attempt to read file through
1074 | 			 * NetHack's setuid permissions -- this is the only
1075 | 			 * place a file name may be wholly under the player's
1076 | 			 * control
1077 | 			 */
1078 | 			raw_printf("Access to %s denied (%d).",
1079 | 					filename, errno);
1080 | 			wait_synch();
1081 | 			/* fall through to standard names */
1082 | 		} else
1083 | #endif
1084 | 		if ((fp = fopenp(filename, "r")) != (FILE *)0) {
1085 | 		    configfile = filename;
1086 | 		    return(fp);
1087 | #if defined(UNIX) || defined(VMS)
1088 | 		} else {
1089 | 		    /* access() above probably caught most problems for UNIX */
1090 | 		    raw_printf("Couldn't open requested config file %s (%d).",
1091 | 					filename, errno);
1092 | 		    wait_synch();
1093 | 		    /* fall through to standard names */
1094 | #endif
1095 | 		}
1096 | 	}
1097 | 
1098 | #if defined(MICRO) || defined(MAC) || defined(__BEOS__)
1099 | 	if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r"))
1100 | 								!= (FILE *)0)
1101 | 		return(fp);
1102 | # ifdef MSDOS
1103 | 	else if ((fp = fopenp(fqname(backward_compat_configfile,
1104 | 					CONFIGPREFIX, 0), "r")) != (FILE *)0)
1105 | 		return(fp);
1106 | # endif
1107 | #else
1108 | 	/* constructed full path names don't need fqname() */
1109 | # ifdef VMS
1110 | 	if ((fp = fopenp(fqname("nethackini", CONFIGPREFIX, 0), "r"))
1111 | 								!= (FILE *)0) {
1112 | 		configfile = "nethackini";
1113 | 		return(fp);
1114 | 	}
1115 | 	if ((fp = fopenp("sys$login:nethack.ini", "r")) != (FILE *)0) {
1116 | 		configfile = "nethack.ini";
1117 | 		return(fp);
1118 | 	}
1119 | 
1120 | 	envp = nh_getenv("HOME");
1121 | 	if (!envp)
1122 | 		Strcpy(tmp_config, "NetHack.cnf");
1123 | 	else
1124 | 		Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
1125 | 	if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1126 | 		return(fp);
1127 | # else	/* should be only UNIX left */
1128 | 	envp = nh_getenv("HOME");
1129 | 	if (!envp)
1130 | 		Strcpy(tmp_config, ".nethackrc");
1131 | 	else
1132 | 		Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
1133 | 	if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
1134 | 		return(fp);
1135 | 	else if (errno != ENOENT) {
1136 | 		/* e.g., problems when setuid NetHack can't search home
1137 | 		 * directory restricted to user */
1138 | 		raw_printf("Couldn't open default config file %s (%d).",
1139 | 					tmp_config, errno);
1140 | 		wait_synch();
1141 | 	}
1142 | # endif
1143 | #endif
1144 | 	return (FILE *)0;
1145 | 
1146 | }
1147 | 
1148 | 
1149 | /*
1150 |  * Retrieve a list of integers from a file into a uchar array.
1151 |  *
1152 |  * NOTE:  This routine is unable to read a value of 0.
1153 |  */
1154 | STATIC_OVL int
1155 | get_uchars(fp, buf, bufp, list, size, name)
1156 |     FILE *fp;		/* input file pointer */
1157 |     char *buf;		/* read buffer, must be of size BUFSZ */
1158 |     char *bufp;		/* current pointer */
1159 |     uchar *list;	/* return list */
1160 |     int  size;		/* return list size */
1161 |     const char *name;		/* name of option for error message */
1162 | {
1163 |     unsigned int num = 0;
1164 |     int count = 0;
1165 | 
1166 |     while (1) {
1167 | 	switch(*bufp) {
1168 | 	    case ' ':  case '\0':
1169 | 	    case '\t': case '\n':
1170 | 		if (num) {
1171 | 		    list[count++] =  num;
1172 | 		    num = 0;
1173 | 		}
1174 | 		if (count == size || !*bufp) return count;
1175 | 		bufp++;
1176 | 		break;
1177 | 
1178 | 	    case '0': case '1': case '2': case '3':
1179 | 	    case '4': case '5': case '6': case '7':
1180 | 	    case '8': case '9':
1181 | 		num = num*10 + (*bufp-'0');
1182 | 		bufp++;
1183 | 		break;
1184 | 
1185 | 	    case '\\':
1186 | 		if (fp == (FILE *)0)
1187 | 		    goto gi_error;
1188 | 		do  {
1189 | 		    if (!fgets(buf, BUFSZ, fp)) goto gi_error;
1190 | 		} while (buf[0] == '#');
1191 | 		bufp = buf;
1192 | 		break;
1193 | 
1194 | 	    default:
1195 | gi_error:
1196 | 		raw_printf("Syntax error in %s", name);
1197 | 		wait_synch();
1198 | 		return count;
1199 | 	}
1200 |     }
1201 |     /*NOTREACHED*/
1202 | }
1203 | 
1204 | #ifdef NOCWD_ASSUMPTIONS
1205 | STATIC_OVL void
1206 | adjust_prefix(bufp, prefixid)
1207 | char *bufp;
1208 | int prefixid;
1209 | {
1210 | 	char *ptr;
1211 | 
1212 | 	if (!bufp) return;
1213 | 	/* Backward compatibility, ignore trailing ;n */ 
1214 | 	if ((ptr = index(bufp, ';')) != 0) *ptr = '\0';
1215 | 	if (strlen(bufp) > 0) {
1216 | 		fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2);
1217 | 		Strcpy(fqn_prefix[prefixid], bufp);
1218 | 		append_slash(fqn_prefix[prefixid]);
1219 | 	}
1220 | }
1221 | #endif
1222 | 
1223 | #define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE)
1224 | 
1225 | /*ARGSUSED*/
1226 | int
1227 | parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)
1228 | FILE		*fp;
1229 | char		*buf;
1230 | char		*tmp_ramdisk;
1231 | char		*tmp_levels;
1232 | {
1233 | #if defined(applec) || defined(__MWERKS__)
1234 | # pragma unused(tmp_ramdisk,tmp_levels)
1235 | #endif
1236 | 	char		*bufp, *altp;
1237 | 	uchar   translate[MAXPCHARS];
1238 | 	int   len;
1239 | 
1240 | 	if (*buf == '#')
1241 | 		return 1;
1242 | 
1243 | 	/* remove trailing whitespace */
1244 | 	bufp = eos(buf);
1245 | 	while (--bufp > buf && isspace(*bufp))
1246 | 		continue;
1247 | 
1248 | 	if (bufp <= buf)
1249 | 		return 1;		/* skip all-blank lines */
1250 | 	else
1251 | 		*(bufp + 1) = '\0';	/* terminate line */
1252 | 
1253 | 	/* find the '=' or ':' */
1254 | 	bufp = index(buf, '=');
1255 | 	altp = index(buf, ':');
1256 | 	if (!bufp || (altp && altp < bufp)) bufp = altp;
1257 | 	if (!bufp) return 0;
1258 | 
1259 | 	/* skip  whitespace between '=' and value */
1260 | 	do { ++bufp; } while (isspace(*bufp));
1261 | 
1262 | 	/* Go through possible variables */
1263 | 	/* some of these (at least LEVELS and SAVE) should now set the
1264 | 	 * appropriate fqn_prefix[] rather than specialized variables
1265 | 	 */
1266 | 	if (match_varname(buf, "OPTIONS", 4)) {
1267 | 		parseoptions(bufp, TRUE, TRUE);
1268 | 		if (plname[0])		/* If a name was given */
1269 | 			plnamesuffix();	/* set the character class */
1270 | #ifdef NOCWD_ASSUMPTIONS
1271 | 	} else if (match_varname(buf, "HACKDIR", 4)) {
1272 | 		adjust_prefix(bufp, HACKPREFIX);
1273 | 	} else if (match_varname(buf, "LEVELDIR", 4) ||
1274 | 		   match_varname(buf, "LEVELS", 4)) {
1275 | 		adjust_prefix(bufp, LEVELPREFIX);
1276 | 	} else if (match_varname(buf, "SAVE", 4)) {
1277 | 		adjust_prefix(bufp, SAVEPREFIX);
1278 | 	} else if (match_varname(buf, "BONESDIR", 5)) {
1279 | 		adjust_prefix(bufp, BONESPREFIX);
1280 | 	} else if (match_varname(buf, "DATADIR", 4)) {
1281 | 		adjust_prefix(bufp, DATAPREFIX);
1282 | 	} else if (match_varname(buf, "SCOREDIR", 4)) {
1283 | 		adjust_prefix(bufp, SCOREPREFIX);
1284 | 	} else if (match_varname(buf, "LOCKDIR", 4)) {
1285 | 		adjust_prefix(bufp, LOCKPREFIX);
1286 | 	} else if (match_varname(buf, "CONFIGDIR", 4)) {
1287 | 		adjust_prefix(bufp, CONFIGPREFIX);
1288 | #else /*NOCWD_ASSUMPTIONS*/
1289 | # ifdef MICRO
1290 | 	} else if (match_varname(buf, "HACKDIR", 4)) {
1291 | 		(void) strncpy(hackdir, bufp, PATHLEN);
1292 | #  ifdef MFLOPPY
1293 | 	} else if (match_varname(buf, "RAMDISK", 3)) {
1294 | 				/* The following ifdef is NOT in the wrong
1295 | 				 * place.  For now, we accept and silently
1296 | 				 * ignore RAMDISK */
1297 | #   ifndef AMIGA
1298 | 		(void) strncpy(tmp_ramdisk, bufp, PATHLEN);
1299 | #   endif
1300 | #  endif
1301 | 	} else if (match_varname(buf, "LEVELS", 4)) {
1302 | 		(void) strncpy(tmp_levels, bufp, PATHLEN);
1303 | 
1304 | 	} else if (match_varname(buf, "SAVE", 4)) {
1305 | #  ifdef MFLOPPY
1306 | 		extern	int saveprompt;
1307 | #  endif
1308 | 		char *ptr;
1309 | 		if ((ptr = index(bufp, ';')) != 0) {
1310 | 			*ptr = '\0';
1311 | #  ifdef MFLOPPY
1312 | 			if (*(ptr+1) == 'n' || *(ptr+1) == 'N') {
1313 | 				saveprompt = FALSE;
1314 | 			}
1315 | #  endif
1316 | 		}
1317 | # ifdef	MFLOPPY
1318 | 		else
1319 | 		    saveprompt = flags.asksavedisk;
1320 | # endif
1321 | 
1322 | 		(void) strncpy(SAVEP, bufp, PATHLEN);
1323 | 		append_slash(SAVEP);
1324 | # endif /* MICRO */
1325 | #endif /*NOCWD_ASSUMPTIONS*/
1326 | 
1327 | 	} else if (match_varname(buf, "NAME", 4)) {
1328 | 	    (void) strncpy(plname, bufp, PL_NSIZ-1);
1329 | 	    plnamesuffix();
1330 | 	} else if (match_varname(buf, "ROLE", 4) ||
1331 | 		   match_varname(buf, "CHARACTER", 4)) {
1332 | 	    if ((len = str2role(bufp)) >= 0)
1333 | 	    	flags.initrole = len;
1334 | 	} else if (match_varname(buf, "DOGNAME", 3)) {
1335 | 	    (void) strncpy(dogname, bufp, PL_PSIZ-1);
1336 | 	} else if (match_varname(buf, "CATNAME", 3)) {
1337 | 	    (void) strncpy(catname, bufp, PL_PSIZ-1);
1338 | 
1339 | 	} else if (match_varname(buf, "GRAPHICS", 4)) {
1340 | 	    len = get_uchars(fp, buf, bufp, translate, MAXPCHARS, "GRAPHICS");
1341 | 	    assign_graphics(translate, len, MAXPCHARS, 0);
1342 | 	} else if (match_varname(buf, "DUNGEON", 4)) {
1343 | 	    len = get_uchars(fp, buf, bufp, translate, MAXDCHARS, "DUNGEON");
1344 | 	    assign_graphics(translate, len, MAXDCHARS, 0);
1345 | 	} else if (match_varname(buf, "TRAPS", 4)) {
1346 | 	    len = get_uchars(fp, buf, bufp, translate, MAXTCHARS, "TRAPS");
1347 | 	    assign_graphics(translate, len, MAXTCHARS, MAXDCHARS);
1348 | 	} else if (match_varname(buf, "EFFECTS", 4)) {
1349 | 	    len = get_uchars(fp, buf, bufp, translate, MAXECHARS, "EFFECTS");
1350 | 	    assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS);
1351 | 
1352 | 	} else if (match_varname(buf, "OBJECTS", 3)) {
1353 | 	    /* oc_syms[0] is the RANDOM object, unused */
1354 | 	    (void) get_uchars(fp, buf, bufp, &(oc_syms[1]),
1355 | 					MAXOCLASSES-1, "OBJECTS");
1356 | 	} else if (match_varname(buf, "MONSTERS", 3)) {
1357 | 	    /* monsyms[0] is unused */
1358 | 	    (void) get_uchars(fp, buf, bufp, &(monsyms[1]),
1359 | 					MAXMCLASSES-1, "MONSTERS");
1360 | 	} else if (match_varname(buf, "WARNINGS", 5)) {
1361 | 	    (void) get_uchars(fp, buf, bufp, translate,
1362 | 					WARNCOUNT, "WARNINGS");
1363 | 	    assign_warnings(translate);
1364 | #ifdef AMIGA
1365 | 	} else if (match_varname(buf, "FONT", 4)) {
1366 | 		char *t;
1367 | 		extern void amii_set_text_font( char *, int );
1368 | 
1369 | 		if( t = strchr( buf+5, ':' ) )
1370 | 		{
1371 | 		    *t = 0;
1372 | 		    amii_set_text_font( buf+5, atoi( t + 1 ) );
1373 | 		    *t = ':';
1374 | 		}
1375 | 	} else if (match_varname(buf, "PATH", 4)) {
1376 | 		(void) strncpy(PATH, bufp, PATHLEN);
1377 | #endif
1378 | #ifdef AMIGA
1379 | 	} else if (match_varname(buf, "DEPTH", 5)) {
1380 | 		extern int amii_numcolors;
1381 | 		int val = atoi( bufp );
1382 | 		amii_numcolors = 1L << min( DEPTH, val );
1383 | 	} else if (match_varname(buf, "DRIPENS", 7)) {
1384 | 		int i, val;
1385 | 		char *t;
1386 | 		for (i = 0, t = strtok(bufp, ",/"); t != (char *)0;
1387 | 				i < 20 && (t = strtok((char*)0, ",/")), ++i) {
1388 | 			sscanf(t, "%d", &val );
1389 | 			flags.amii_dripens[i] = val;
1390 | 		}
1391 | 	} else if (match_varname(buf, "SCREENMODE", 10 )) {
1392 | 		extern long amii_scrnmode;
1393 | 		if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
1394 | 			amii_scrnmode = 0;
1395 | 	} else if (match_varname(buf, "MSGPENS", 7)) {
1396 | 		extern int amii_msgAPen, amii_msgBPen;
1397 | 		char *t = strtok(bufp, ",/");
1398 | 		if( t )
1399 | 		{
1400 | 		    sscanf(t, "%d", &amii_msgAPen);
1401 | 		    if( t = strtok((char*)0, ",/") )
1402 | 				sscanf(t, "%d", &amii_msgBPen);
1403 | 		}
1404 | 	} else if (match_varname(buf, "TEXTPENS", 8)) {
1405 | 		extern int amii_textAPen, amii_textBPen;
1406 | 		char *t = strtok(bufp, ",/");
1407 | 		if( t )
1408 | 		{
1409 | 		    sscanf(t, "%d", &amii_textAPen);
1410 | 		    if( t = strtok((char*)0, ",/") )
1411 | 				sscanf(t, "%d", &amii_textBPen);
1412 | 		}
1413 | 	} else if (match_varname(buf, "MENUPENS", 8)) {
1414 | 		extern int amii_menuAPen, amii_menuBPen;
1415 | 		char *t = strtok(bufp, ",/");
1416 | 		if( t )
1417 | 		{
1418 | 		    sscanf(t, "%d", &amii_menuAPen);
1419 | 		    if( t = strtok((char*)0, ",/") )
1420 | 				sscanf(t, "%d", &amii_menuBPen);
1421 | 		}
1422 | 	} else if (match_varname(buf, "STATUSPENS", 10)) {
1423 | 		extern int amii_statAPen, amii_statBPen;
1424 | 		char *t = strtok(bufp, ",/");
1425 | 		if( t )
1426 | 		{
1427 | 		    sscanf(t, "%d", &amii_statAPen);
1428 | 		    if( t = strtok((char*)0, ",/") )
1429 | 				sscanf(t, "%d", &amii_statBPen);
1430 | 		}
1431 | 	} else if (match_varname(buf, "OTHERPENS", 9)) {
1432 | 		extern int amii_otherAPen, amii_otherBPen;
1433 | 		char *t = strtok(bufp, ",/");
1434 | 		if( t )
1435 | 		{
1436 | 		    sscanf(t, "%d", &amii_otherAPen);
1437 | 		    if( t = strtok((char*)0, ",/") )
1438 | 				sscanf(t, "%d", &amii_otherBPen);
1439 | 		}
1440 | 	} else if (match_varname(buf, "PENS", 4)) {
1441 | 		extern unsigned short amii_init_map[ AMII_MAXCOLORS ];
1442 | 		int i;
1443 | 		char *t;
1444 | 
1445 | 		for (i = 0, t = strtok(bufp, ",/");
1446 | 			i < AMII_MAXCOLORS && t != (char *)0;
1447 | 			t = strtok((char *)0, ",/"), ++i)
1448 | 		{
1449 | 			sscanf(t, "%hx", &amii_init_map[i]);
1450 | 		}
1451 | 		amii_setpens( amii_numcolors = i );
1452 | #endif
1453 | #ifdef QT_GRAPHICS
1454 | 	} else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
1455 | 		extern char *qt_tilewidth;
1456 | 		if (qt_tilewidth == NULL)	
1457 | 			qt_tilewidth=(char *)strdup(bufp);
1458 | 	} else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
1459 | 		extern char *qt_tileheight;
1460 | 		if (qt_tileheight == NULL)	
1461 | 			qt_tileheight=(char *)strdup(bufp);
1462 | 	} else if (match_varname(buf, "QT_FONTSIZE", 11)) {
1463 | 		extern char *qt_fontsize;
1464 | 		if (qt_fontsize == NULL)
1465 | 			qt_fontsize=(char *)strdup(bufp);
1466 | #endif
1467 | 	} else
1468 | 		return 0;
1469 | 	return 1;
1470 | }
1471 | 
1472 | void
1473 | read_config_file(filename)
1474 | const char *filename;
1475 | {
1476 | #define tmp_levels	(char *)0
1477 | #define tmp_ramdisk	(char *)0
1478 | 
1479 | #ifdef MICRO
1480 | #undef tmp_levels
1481 | 	char	tmp_levels[PATHLEN];
1482 | # ifdef MFLOPPY
1483 | #  ifndef AMIGA
1484 | #undef tmp_ramdisk
1485 | 	char	tmp_ramdisk[PATHLEN];
1486 | #  endif
1487 | # endif
1488 | #endif
1489 | 	char	buf[4*BUFSZ];
1490 | 	FILE	*fp;
1491 | 
1492 | 	if (!(fp = fopen_config_file(filename))) return;
1493 | 
1494 | #ifdef MICRO
1495 | # ifdef MFLOPPY
1496 | #  ifndef AMIGA
1497 | 	tmp_ramdisk[0] = 0;
1498 | #  endif
1499 | # endif
1500 | 	tmp_levels[0] = 0;
1501 | #endif
1502 | 
1503 | 	while (fgets(buf, 4*BUFSZ, fp)) {
1504 | 		if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
1505 | 			raw_printf("Bad option line:  \"%s\"", buf);
1506 | 			wait_synch();
1507 | 		}
1508 | 	}
1509 | 	(void) fclose(fp);
1510 | 
1511 | #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
1512 | 	/* should be superseded by fqn_prefix[] */
1513 | # ifdef MFLOPPY
1514 | 	Strcpy(permbones, tmp_levels);
1515 | #  ifndef AMIGA
1516 | 	if (tmp_ramdisk[0]) {
1517 | 		Strcpy(levels, tmp_ramdisk);
1518 | 		if (strcmp(permbones, levels))		/* if not identical */
1519 | 			ramdisk = TRUE;
1520 | 	} else
1521 | #  endif /* AMIGA */
1522 | 		Strcpy(levels, tmp_levels);
1523 | 
1524 | 	Strcpy(bones, levels);
1525 | # endif /* MFLOPPY */
1526 | #endif /* MICRO */
1527 | 	return;
1528 | }
1529 | 
1530 | /* ----------  END CONFIG FILE HANDLING ----------- */
1531 | 
1532 | /* ----------  BEGIN SCOREBOARD CREATION ----------- */
1533 | 
1534 | /* verify that we can write to the scoreboard file; if not, try to create one */
1535 | void
1536 | check_recordfile(dir)
1537 | const char *dir;
1538 | {
1539 | #if defined(applec) || defined(__MWERKS__)
1540 | # pragma unused(dir)
1541 | #endif
1542 | 	const char *fq_record;
1543 | 	int fd;
1544 | 
1545 | #if defined(UNIX) || defined(VMS)
1546 | 	fq_record = fqname(RECORD, SCOREPREFIX, 0);
1547 | 	fd = open(fq_record, O_RDWR, 0);
1548 | 	if (fd >= 0) {
1549 | # ifdef VMS	/* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
1550 | 		if (!file_is_stmlf(fd)) {
1551 | 		    raw_printf(
1552 | 		  "Warning: scoreboard file %s is not in stream_lf format",
1553 | 				fq_record);
1554 | 		    wait_synch();
1555 | 		}
1556 | # endif
1557 | 	    (void) close(fd);	/* RECORD is accessible */
1558 | 	} else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) {
1559 | 	    (void) close(fd);	/* RECORD newly created */
1560 | # if defined(VMS) && !defined(SECURE)
1561 | 	    /* Re-protect RECORD with world:read+write+execute+delete access. */
1562 | 	    (void) chmod(fq_record, FCMASK | 007);
1563 | # endif /* VMS && !SECURE */
1564 | 	} else {
1565 | 	    raw_printf("Warning: cannot write scoreboard file %s", fq_record);
1566 | 	    wait_synch();
1567 | 	}
1568 | #endif  /* !UNIX && !VMS */
1569 | #ifdef MICRO
1570 | 	char tmp[PATHLEN];
1571 | 
1572 | # ifdef OS2_CODEVIEW   /* explicit path on opening for OS/2 */
1573 | 	/* how does this work when there isn't an explicit path or fopenp
1574 | 	 * for later access to the file via fopen_datafile? ? */
1575 | 	(void) strncpy(tmp, dir, PATHLEN - 1);
1576 | 	tmp[PATHLEN-1] = '\0';
1577 | 	if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
1578 | 		append_slash(tmp);
1579 | 		Strcat(tmp, RECORD);
1580 | 	}
1581 | 	fq_record = tmp;
1582 | # else
1583 | 	Strcpy(tmp, RECORD);
1584 | 	fq_record = fqname(RECORD, SCOREPREFIX, 0);
1585 | # endif
1586 | 
1587 | 	if ((fd = open(fq_record, O_RDWR)) < 0) {
1588 | 	    /* try to create empty record */
1589 | # if defined(AZTEC_C) || defined(_DCC)
1590 | 	    /* Aztec doesn't use the third argument */
1591 | 	    /* DICE doesn't like it */
1592 | 	    if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) {
1593 | # else
1594 | 	    if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) {
1595 | # endif
1596 | 	raw_printf("Warning: cannot write record %s", tmp);
1597 | 		wait_synch();
1598 | 	    } else
1599 | 		(void) close(fd);
1600 | 	} else		/* open succeeded */
1601 | 	    (void) close(fd);
1602 | #else /* MICRO */
1603 | 
1604 | # ifdef MAC
1605 | 	/* Create the "record" file, if necessary */
1606 | 	fq_record = fqname(RECORD, SCOREPREFIX, 0);
1607 | 	fd = macopen (fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
1608 | 	if (fd != -1) macclose (fd);
1609 | # endif /* MAC */
1610 | 
1611 | #endif /* MICRO */
1612 | }
1613 | 
1614 | /* ----------  END SCOREBOARD CREATION ----------- */
1615 | 
1616 | /*files.c*/