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