1 /* NetHack 3.6 files.c $NHDT-Date: 1449296293 2015/12/05 06:18:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.192 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
9 #include "wintty.h" /* more() */
12 #if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) \
18 #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */
34 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
36 #ifndef COMPRESS_EXTENSION
37 #define COMPRESS_EXTENSION ".gz"
41 #if defined(UNIX) && defined(QT_GRAPHICS)
42 #include <sys/types.h>
47 #if defined(UNIX) || defined(VMS) || !defined(NO_SIGNAL)
51 #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
58 #ifndef O_BINARY /* used for micros, no-op for others */
62 #ifdef PREFIXES_IN_USE
64 static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
67 #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
68 char bones[] = "bonesnn.xxx";
69 char lock[PL_NSIZ + 14] = "1lock"; /* long enough for uid+name+.99 */
72 char bones[FILENAME]; /* pathname of bones files */
73 char lock[FILENAME]; /* pathname of level files */
76 char bones[] = "bonesnn.xxx;1";
77 char lock[PL_NSIZ + 17] = "1lock"; /* long enough for _uid+name+.99;1 */
80 char bones[] = "bonesnn.xxx";
81 char lock[PL_NSIZ + 25]; /* long enough for username+-+name+.99 */
85 #if defined(UNIX) || defined(__BEOS__)
86 #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
89 #define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
92 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
94 #define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
99 #if !defined(SAVE_EXTENSION)
101 #define SAVE_EXTENSION ".sav"
104 #define SAVE_EXTENSION ".NetHack-saved-game"
108 char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */
110 char SAVEP[SAVESIZE]; /* holds path of directory for save file */
113 #ifdef HOLD_LOCKFILE_OPEN
114 struct level_ftrack {
116 int fd; /* file descriptor for level file */
117 int oflag; /* open flags */
118 boolean nethack_thinks_it_is_open; /* Does NetHack think it's open? */
123 #endif /*HOLD_LOCKFILE_OPEN*/
125 #define WIZKIT_MAX 128
126 static char wizkit[WIZKIT_MAX];
127 STATIC_DCL FILE *NDECL(fopen_wizkit_file);
128 STATIC_DCL void FDECL(wizkit_addinv, (struct obj *));
131 extern char PATH[]; /* see sys/amiga/amidos.c */
132 extern char bbs_id[];
135 #include <proto/dos.h>
138 #include <libraries/dos.h>
139 extern void FDECL(amii_set_text_font, (char *, int));
142 #if defined(WIN32) || defined(MSDOS)
145 #define Delay(a) msleep(a)
149 #define DeleteFile unlink
155 #define unlink macunlink
158 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) \
159 || defined(__MWERKS__)
160 #define PRAGMA_UNUSED
164 extern char *sounddir;
167 extern int n_dgns; /* from dungeon.c */
169 #if defined(UNIX) && defined(QT_GRAPHICS)
174 STATIC_PTR int FDECL(CFDECLSPEC strcmp_wrap, (const void *, const void *));
176 STATIC_DCL char *FDECL(set_bonesfile_name, (char *, d_level *));
177 STATIC_DCL char *NDECL(set_bonestemp_name);
179 STATIC_DCL void FDECL(redirect, (const char *, const char *, FILE *,
182 #if defined(COMPRESS) || defined(ZLIB_COMP)
183 STATIC_DCL void FDECL(docompress_file, (const char *, BOOLEAN_P));
185 #if defined(ZLIB_COMP)
186 STATIC_DCL boolean FDECL(make_compressed_name, (const char *, char *));
189 STATIC_DCL char *FDECL(make_lockname, (const char *, char *));
191 STATIC_DCL FILE *FDECL(fopen_config_file, (const char *, int));
192 STATIC_DCL int FDECL(get_uchars, (FILE *, char *, char *, uchar *, BOOLEAN_P,
194 int FDECL(parse_config_line, (FILE *, char *, int));
195 STATIC_DCL FILE *NDECL(fopen_sym_file);
196 STATIC_DCL void FDECL(set_symhandling, (char *, int));
197 #ifdef NOCWD_ASSUMPTIONS
198 STATIC_DCL void FDECL(adjust_prefix, (char *, int));
201 STATIC_DCL boolean FDECL(copy_bytes, (int, int));
203 #ifdef HOLD_LOCKFILE_OPEN
204 STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int));
211 * legal zero-terminated list of acceptable file name characters
212 * quotechar lead-in character used to quote illegal characters as
215 * callerbuf buffer to house result
216 * bufsz size of callerbuf
219 * The hex digits 0-9 and A-F are always part of the legal set due to
220 * their use in the encoding scheme, even if not explicitly included in
224 * The following call:
225 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
226 * '%', "This is a % test!", buf, 512);
227 * results in this encoding:
228 * "This%20is%20a%20%25%20test%21"
231 fname_encode(legal, quotechar, s, callerbuf, bufsz)
239 static char hexdigits[] = "0123456789ABCDEF";
246 /* Do we have room for one more character or encoding? */
247 if ((bufsz - cnt) <= 4)
250 if (*sp == quotechar) {
251 (void) sprintf(op, "%c%02X", quotechar, *sp);
254 } else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) {
259 (void) sprintf(op, "%c%02X", quotechar, *sp);
272 * quotechar lead-in character used to quote illegal characters as
275 * callerbuf buffer to house result
276 * bufsz size of callerbuf
279 fname_decode(quotechar, s, callerbuf, bufsz)
285 int k, calc, cnt = 0;
286 static char hexdigits[] = "0123456789ABCDEF";
294 /* Do we have room for one more character? */
295 if ((bufsz - cnt) <= 2)
297 if (*sp == quotechar) {
299 for (k = 0; k < 16; ++k)
300 if (*sp == hexdigits[k])
303 return callerbuf; /* impossible, so bail */
306 for (k = 0; k < 16; ++k)
307 if (*sp == hexdigits[k])
310 return callerbuf; /* impossible, so bail */
324 #ifdef PREFIXES_IN_USE
325 #define UNUSED_if_not_PREFIXES_IN_USE /*empty*/
327 #define UNUSED_if_not_PREFIXES_IN_USE UNUSED
332 fqname(basenam, whichprefix, buffnum)
334 int whichprefix UNUSED_if_not_PREFIXES_IN_USE;
335 int buffnum UNUSED_if_not_PREFIXES_IN_USE;
337 #ifndef PREFIXES_IN_USE
340 if (!basenam || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
342 if (!fqn_prefix[whichprefix])
344 if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
345 impossible("Invalid fqn_filename_buffer specified: %d", buffnum);
348 if (strlen(fqn_prefix[whichprefix]) + strlen(basenam)
349 >= FQN_MAX_FILENAME) {
350 impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
352 return basenam; /* XXX */
354 Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
355 return strcat(fqn_filename_buffer[buffnum], basenam);
360 validate_prefix_locations(reasonbuf)
361 char *reasonbuf; /* reasonbuf must be at least BUFSZ, supplied by caller */
363 #if defined(NOCWD_ASSUMPTIONS)
365 const char *filename;
366 int prefcnt, failcount = 0;
367 char panicbuf1[BUFSZ], panicbuf2[BUFSZ];
373 #if defined(NOCWD_ASSUMPTIONS)
374 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) {
375 /* don't test writing to configdir or datadir; they're readonly */
376 if (prefcnt == SYSCONFPREFIX || prefcnt == CONFIGPREFIX
377 || prefcnt == DATAPREFIX)
379 filename = fqname("validate", prefcnt, 3);
380 if ((fp = fopen(filename, "w"))) {
382 (void) unlink(filename);
386 Strcat(reasonbuf, ", ");
387 Strcat(reasonbuf, fqn_prefix_names[prefcnt]);
389 /* the paniclog entry gets the value of errno as well */
390 Sprintf(panicbuf1, "Invalid %s", fqn_prefix_names[prefcnt]);
391 #if defined(NHSTDC) && !defined(NOTSTDC)
392 if (!(details = strerror(errno)))
395 Sprintf(panicbuf2, "\"%s\", (%d) %s", fqn_prefix[prefcnt], errno,
397 paniclog(panicbuf1, panicbuf2);
408 /* fopen a file, with OS-dependent bells and whistles */
409 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
411 fopen_datafile(filename, mode, prefix)
412 const char *filename, *mode;
417 filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0);
418 fp = fopen(filename, mode);
422 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
425 /* Set names for bones[] and lock[] */
430 Strcpy(levels, permbones);
431 Strcpy(bones, permbones);
433 append_slash(permbones);
434 append_slash(levels);
436 strncat(levels, bbs_id, PATHLEN);
439 Strcat(bones, "bonesnn.*");
440 Strcpy(lock, levels);
442 Strcat(lock, alllevels);
448 /* Construct a file name for a level-type file, which is of the form
449 * something.level (with any old level stripped off).
450 * This assumes there is space on the end of 'file' to append
451 * a two digit number. This is true for 'level'
452 * but be careful if you use it for other things -dgk
455 set_levelfile_name(file, lev)
461 tf = rindex(file, '.');
464 Sprintf(tf, ".%d", lev);
472 create_levelfile(lev, errbuf)
481 set_levelfile_name(lock, lev);
482 fq_lock = fqname(lock, LEVELPREFIX, 0);
484 #if defined(MICRO) || defined(WIN32)
485 /* Use O_TRUNC to force the file to be shortened if it already
486 * exists and is currently longer.
488 #ifdef HOLD_LOCKFILE_OPEN
490 fd = open_levelfile_exclusively(
491 fq_lock, lev, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
494 fd = open(fq_lock, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
497 fd = maccreat(fq_lock, LEVL_TYPE);
499 fd = creat(fq_lock, FCMASK);
501 #endif /* MICRO || WIN32 */
504 level_info[lev].flags |= LFILE_EXISTS;
505 else if (errbuf) /* failure explanation */
506 Sprintf(errbuf, "Cannot create file \"%s\" for level %d (errno %d).",
513 open_levelfile(lev, errbuf)
522 set_levelfile_name(lock, lev);
523 fq_lock = fqname(lock, LEVELPREFIX, 0);
525 /* If not currently accessible, swap it in. */
526 if (level_info[lev].where != ACTIVE)
530 fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
532 #ifdef HOLD_LOCKFILE_OPEN
534 fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY);
537 fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
540 /* for failure, return an explanation that our caller can use;
541 settle for `lock' instead of `fq_lock' because the latter
542 might end up being too big for nethack's BUFSZ */
543 if (fd < 0 && errbuf)
544 Sprintf(errbuf, "Cannot open file \"%s\" for level %d (errno %d).",
551 delete_levelfile(lev)
555 * Level 0 might be created by port specific code that doesn't
556 * call create_levfile(), so always assume that it exists.
558 if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
559 set_levelfile_name(lock, lev);
560 #ifdef HOLD_LOCKFILE_OPEN
564 (void) unlink(fqname(lock, LEVELPREFIX, 0));
565 level_info[lev].flags &= ~LFILE_EXISTS;
572 #ifdef HANGUPHANDLING
573 if (program_state.preserve_locks)
576 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
577 eraseall(levels, alllevels);
579 eraseall(permbones, alllevels);
585 (void) signal(SIGINT, SIG_IGN);
587 #if defined(UNIX) || defined(VMS)
588 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
590 /* can't access maxledgerno() before dungeons are created -dlc */
591 for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
592 delete_levelfile(x); /* not all levels need be present */
594 #endif /* ?PC_LOCKING,&c */
597 #if defined(SELECTSAVED)
598 /* qsort comparison routine */
599 STATIC_OVL int CFDECLSPEC
604 #if defined(UNIX) && defined(QT_GRAPHICS)
605 return strncasecmp(*(char **) p, *(char **) q, 16);
607 return strncmpi(*(char **) p, *(char **) q, 16);
612 #ifdef HOLD_LOCKFILE_OPEN
614 open_levelfile_exclusively(name, lev, oflag)
623 if (lftrack.fd >= 0) {
624 /* check for compatible access */
625 if (lftrack.oflag == oflag) {
627 reslt = lseek(fd, 0L, SEEK_SET);
629 panic("open_levelfile_exclusively: lseek failed %d", errno);
630 lftrack.nethack_thinks_it_is_open = TRUE;
633 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
635 lftrack.oflag = oflag;
636 lftrack.nethack_thinks_it_is_open = TRUE;
639 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
641 lftrack.oflag = oflag;
643 lftrack.nethack_thinks_it_is_open = TRUE;
653 lftrack.nethack_thinks_it_is_open = FALSE;
665 if (lftrack.fd == fd) {
666 really_close(); /* close it, but reopen it to hold it */
667 fd = open_levelfile(0, (char *) 0);
668 lftrack.nethack_thinks_it_is_open = FALSE;
683 /* ---------- END LEVEL FILE HANDLING ----------- */
685 /* ---------- BEGIN BONES FILE HANDLING ----------- */
687 /* set up "file" to be file name for retrieving bones, and return a
688 * bonesid to be read/written in the bones file.
691 set_bonesfile_name(file, lev)
698 Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
699 In_quest(lev) ? urole.filecode : "0");
701 if ((sptr = Is_special(lev)) != 0)
702 Sprintf(dptr, ".%c", sptr->boneid);
704 Sprintf(dptr, ".%d", lev->dlevel);
711 /* set up temporary file name for writing bones, to avoid another game's
712 * trying to read from an uncompleted bones file. we want an uncontentious
713 * name, so use one in the namespace reserved for this game's level files.
714 * (we are not reading or writing level files while writing bones files, so
715 * the same array may be used instead of copying.)
722 tf = rindex(lock, '.');
733 create_bonesfile(lev, bonesid, errbuf)
743 *bonesid = set_bonesfile_name(bones, lev);
744 file = set_bonestemp_name();
745 file = fqname(file, BONESPREFIX, 0);
747 #if defined(MICRO) || defined(WIN32)
748 /* Use O_TRUNC to force the file to be shortened if it already
749 * exists and is currently longer.
751 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
754 fd = maccreat(file, BONE_TYPE);
756 fd = creat(file, FCMASK);
759 if (fd < 0 && errbuf) /* failure explanation */
760 Sprintf(errbuf, "Cannot create bones \"%s\", id %s (errno %d).", lock,
763 #if defined(VMS) && !defined(SECURE)
765 Re-protect bones file with world:read+write+execute+delete access.
766 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
767 delete access without write access, which is what's really wanted.
768 Can't simply create it with the desired protection because creat
769 ANDs the mask with the user's default protection, which usually
770 denies some or all access to world.
772 (void) chmod(file, FCMASK | 007); /* allow other users full access */
773 #endif /* VMS && !SECURE */
779 /* remove partial bonesfile in process of creation */
783 const char *tempname;
785 tempname = set_bonestemp_name();
786 tempname = fqname(tempname, BONESPREFIX, 0);
787 (void) unlink(tempname);
791 /* move completed bones file to proper name */
793 commit_bonesfile(lev)
796 const char *fq_bones, *tempname;
799 (void) set_bonesfile_name(bones, lev);
800 fq_bones = fqname(bones, BONESPREFIX, 0);
801 tempname = set_bonestemp_name();
802 tempname = fqname(tempname, BONESPREFIX, 1);
804 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
805 /* old SYSVs don't have rename. Some SVR3's may, but since they
806 * also have link/unlink, it doesn't matter. :-)
808 (void) unlink(fq_bones);
809 ret = link(tempname, fq_bones);
810 ret += unlink(tempname);
812 ret = rename(tempname, fq_bones);
814 if (wizard && ret != 0)
815 pline("couldn't rename %s to %s.", tempname, fq_bones);
819 open_bonesfile(lev, bonesid)
823 const char *fq_bones;
826 *bonesid = set_bonesfile_name(bones, lev);
827 fq_bones = fqname(bones, BONESPREFIX, 0);
828 nh_uncompress(fq_bones); /* no effect if nonexistent */
830 fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
832 fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
838 delete_bonesfile(lev)
841 (void) set_bonesfile_name(bones, lev);
842 return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
845 /* assume we're compressing the recently read or created bonesfile, so the
846 * file name is already set properly */
850 nh_compress(fqname(bones, BONESPREFIX, 0));
853 /* ---------- END BONES FILE HANDLING ----------- */
855 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
857 /* set savefile name in OS-dependent manner from pre-existing plname,
858 * avoiding troublesome characters */
860 set_savefile_name(regularize_it)
861 boolean regularize_it;
864 Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
866 regularize(SAVEF + 7);
870 Strcpy(SAVEF, SAVEP);
872 strncat(SAVEF, bbs_id, PATHLEN);
875 int i = strlen(SAVEP);
877 /* plname has to share space with SAVEP and ".sav" */
878 (void) strncat(SAVEF, plname, FILENAME - i - 4);
880 (void) strncat(SAVEF, plname, 8);
883 regularize(SAVEF + i);
885 Strcat(SAVEF, SAVE_EXTENSION);
889 static const char okchars[] =
890 "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
891 const char *legal = okchars;
892 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
894 /* Obtain the name of the logged on user and incorporate
895 * it into the name. */
896 Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
898 ++legal; /* skip '*' wildcard character */
899 (void) fname_encode(legal, '%', fnamebuf, encodedfnamebuf, BUFSZ);
900 Sprintf(SAVEF, "%s%s", encodedfnamebuf, SAVE_EXTENSION);
902 #else /* not VMS or MICRO or WIN32 */
903 Sprintf(SAVEF, "save/%d%s", (int) getuid(), plname);
905 regularize(SAVEF + 5); /* avoid . or / in name */
913 save_savefile_name(fd)
916 (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
921 /* change pre-existing savefile name to indicate an error savefile */
927 char *semi_colon = rindex(SAVEF, ';');
932 Strcat(SAVEF, ".e;1");
943 /* create save file, overwriting one if it already exists */
950 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
951 #if defined(MICRO) || defined(WIN32)
952 fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
955 fd = maccreat(fq_save, SAVE_TYPE);
957 fd = creat(fq_save, FCMASK);
959 #if defined(VMS) && !defined(SECURE)
961 Make sure the save file is owned by the current process. That's
962 the default for non-privileged users, but for priv'd users the
963 file will be owned by the directory's owner instead of the user.
966 (void) chown(fq_save, getuid(), getgid());
967 #define getuid() vms_getuid()
968 #endif /* VMS && !SECURE */
974 /* open savefile for reading */
981 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
983 fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
985 fd = open(fq_save, O_RDONLY | O_BINARY, 0);
990 /* delete savefile */
994 (void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
995 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
998 /* try to open up a save file and prepare to restore it */
1000 restore_saved_game()
1002 const char *fq_save;
1006 set_savefile_name(TRUE);
1008 if (!saveDiskPrompt(1))
1010 #endif /* MFLOPPY */
1011 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1013 nh_uncompress(fq_save);
1014 if ((fd = open_savefile()) < 0)
1017 if (validate(fd, fq_save) != 0) {
1018 (void) nhclose(fd), fd = -1;
1019 (void) delete_savefile();
1024 #if defined(SELECTSAVED)
1026 plname_from_file(filename)
1027 const char *filename;
1032 Strcpy(SAVEF, filename);
1033 #ifdef COMPRESS_EXTENSION
1034 SAVEF[strlen(SAVEF) - strlen(COMPRESS_EXTENSION)] = '\0';
1036 nh_uncompress(SAVEF);
1037 if ((fd = open_savefile()) >= 0) {
1038 if (validate(fd, filename) == 0) {
1039 char tplname[PL_NSIZ];
1040 get_plname_from_file(fd, tplname);
1041 result = dupstr(tplname);
1049 /* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/
1050 #if defined(UNIX) && defined(QT_GRAPHICS)
1051 /* Name not stored in save file, so we have to extract it from
1052 the filename, which loses information
1053 (eg. "/", "_", and "." characters are lost. */
1056 char name[64]; /* more than PL_NSIZ */
1057 #ifdef COMPRESS_EXTENSION
1058 #define EXTSTR COMPRESS_EXTENSION
1063 if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) {
1065 /* "_" most likely means " ", which certainly looks nicer */
1066 for (k=0; name[k]; k++)
1067 if ( name[k] == '_' )
1069 return dupstr(name);
1071 #endif /* UNIX && QT_GRAPHICS */
1075 /* --------- end of obsolete code ----*/
1076 #endif /* 0 - WAS STORE_PLNAME_IN_FILE*/
1078 #endif /* defined(SELECTSAVED) */
1083 #if defined(SELECTSAVED)
1089 const char *fq_save;
1091 Strcpy(plname, "*");
1092 set_savefile_name(FALSE);
1093 #if defined(ZLIB_COMP)
1094 Strcat(SAVEF, COMPRESS_EXTENSION);
1096 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1099 foundfile = foundfile_buffer();
1100 if (findfirst((char *) fq_save)) {
1103 } while (findnext());
1106 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1107 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1108 if (findfirst((char *) fq_save)) {
1112 r = plname_from_file(foundfile);
1116 } while (findnext());
1121 #if defined(UNIX) && defined(QT_GRAPHICS)
1122 /* posixly correct version */
1123 int myuid = getuid();
1126 if ((dir = opendir(fqname("save", SAVEPREFIX, 0)))) {
1127 for (n = 0; readdir(dir); n++)
1133 if (!(dir = opendir(fqname("save", SAVEPREFIX, 0))))
1135 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1136 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1137 for (i = 0, j = 0; i < n; i++) {
1139 char name[64]; /* more than PL_NSIZ */
1140 struct dirent *entry = readdir(dir);
1144 if (sscanf(entry->d_name, "%d%63s", &uid, name) == 2) {
1146 char filename[BUFSZ];
1149 Sprintf(filename, "save/%d%s", uid, name);
1150 r = plname_from_file(filename);
1161 Strcpy(plname, "*");
1162 set_savefile_name(FALSE);
1163 j = vms_get_saved_games(SAVEF, &result);
1168 qsort(result, j, sizeof (char *), strcmp_wrap);
1171 } else if (result) { /* could happen if save files are obsolete */
1172 free_saved_games(result);
1174 #endif /* SELECTSAVED */
1179 free_saved_games(saved)
1186 free((genericptr_t) saved[i++]);
1187 free((genericptr_t) saved);
1191 /* ---------- END SAVE FILE HANDLING ----------- */
1193 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1198 redirect(filename, mode, stream, uncomp)
1199 const char *filename, *mode;
1203 if (freopen(filename, mode, stream) == (FILE *) 0) {
1204 (void) fprintf(stderr, "freopen of %s for %scompress failed\n",
1205 filename, uncomp ? "un" : "");
1206 terminate(EXIT_FAILURE);
1211 * using system() is simpler, but opens up security holes and causes
1212 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1213 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1215 * cf. child() in unixunix.c.
1218 docompress_file(filename, uncomp)
1219 const char *filename;
1224 const char *args[10];
1225 #ifdef COMPRESS_OPTIONS
1231 boolean istty = !strncmpi(windowprocs.name, "tty", 3);
1234 Strcpy(cfn, filename);
1235 #ifdef COMPRESS_EXTENSION
1236 Strcat(cfn, COMPRESS_EXTENSION);
1238 /* when compressing, we know the file exists */
1240 if ((cf = fopen(cfn, RDBMODE)) == (FILE *) 0)
1247 args[++i] = "-d"; /* uncompress */
1248 #ifdef COMPRESS_OPTIONS
1250 /* we can't guarantee there's only one additional option, sigh */
1252 boolean inword = FALSE;
1254 Strcpy(opts, COMPRESS_OPTIONS);
1257 if ((*opt == ' ') || (*opt == '\t')) {
1262 } else if (!inword) {
1270 args[++i] = (char *) 0;
1273 /* If we don't do this and we are right after a y/n question *and*
1274 * there is an error message from the compression, the 'y' or 'n' can
1275 * end up being displayed after the error message.
1281 if (f == 0) { /* child */
1283 /* any error messages from the compression must come out after
1284 * the first line, because the more() to let the user read
1285 * them will have to clear the first line. This should be
1286 * invisible if there are no error messages.
1291 /* run compressor without privileges, in case other programs
1292 * have surprises along the line of gzip once taking filenames
1295 /* assume all compressors will compress stdin to stdout
1296 * without explicit filenames. this is true of at least
1297 * compress and gzip, those mentioned in config.h.
1300 redirect(cfn, RDBMODE, stdin, uncomp);
1301 redirect(filename, WRBMODE, stdout, uncomp);
1303 redirect(filename, RDBMODE, stdin, uncomp);
1304 redirect(cfn, WRBMODE, stdout, uncomp);
1306 (void) setgid(getgid());
1307 (void) setuid(getuid());
1308 (void) execv(args[0], (char *const *) args);
1310 (void) fprintf(stderr, "Exec to %scompress %s failed.\n",
1311 uncomp ? "un" : "", filename);
1312 terminate(EXIT_FAILURE);
1313 } else if (f == -1) {
1315 pline("Fork to %scompress %s failed.", uncomp ? "un" : "", filename);
1319 (void) signal(SIGINT, SIG_IGN);
1320 (void) signal(SIGQUIT, SIG_IGN);
1321 (void) wait((int *) &i);
1322 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
1324 (void) signal(SIGQUIT, SIG_DFL);
1326 /* I don't think we can really cope with external compression
1327 * without signals, so we'll declare that compress failed and
1328 * go on. (We could do a better job by forcing off external
1329 * compression if there are no signals, but we want this for
1330 * testing with FailSafeC
1335 /* (un)compress succeeded: remove file left behind */
1339 (void) unlink(filename);
1341 /* (un)compress failed; remove the new, bad file */
1343 raw_printf("Unable to uncompress %s", filename);
1344 (void) unlink(filename);
1346 /* no message needed for compress case; life will go on */
1350 /* Give them a chance to read any error messages from the
1351 * compression--these would go to stdout or stderr and would get
1352 * overwritten only in tty mode. It's still ugly, since the
1353 * messages are being written on top of the screen, but at least
1354 * the user can read them.
1356 if (istty && iflags.window_inited) {
1357 clear_nhwindow(WIN_MESSAGE);
1359 /* No way to know if this is feasible */
1365 #endif /* COMPRESS */
1367 #if defined(COMPRESS) || defined(ZLIB_COMP)
1368 #define UNUSED_if_not_COMPRESS /*empty*/
1370 #define UNUSED_if_not_COMPRESS UNUSED
1375 nh_compress(filename)
1376 const char *filename UNUSED_if_not_COMPRESS;
1378 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1379 #ifdef PRAGMA_UNUSED
1380 #pragma unused(filename)
1383 docompress_file(filename, FALSE);
1387 /* uncompress file if it exists */
1389 nh_uncompress(filename)
1390 const char *filename UNUSED_if_not_COMPRESS;
1392 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1393 #ifdef PRAGMA_UNUSED
1394 #pragma unused(filename)
1397 docompress_file(filename, TRUE);
1401 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
1403 make_compressed_name(filename, cfn)
1404 const char *filename;
1407 #ifndef SHORT_FILENAMES
1408 /* Assume free-form filename with no 8.3 restrictions */
1409 strcpy(cfn, filename);
1410 strcat(cfn, COMPRESS_EXTENSION);
1413 #ifdef SAVE_EXTENSION
1414 char *bp = (char *) 0;
1416 strcpy(cfn, filename);
1417 if ((bp = strstri(cfn, SAVE_EXTENSION))) {
1418 strsubst(bp, SAVE_EXTENSION, ".saz");
1421 /* find last occurrence of bon */
1423 while (bp-- > cfn) {
1424 if (strstri(bp, "bon")) {
1425 strsubst(bp, "bon", "boz");
1430 #endif /* SAVE_EXTENSION */
1432 #endif /* SHORT_FILENAMES */
1436 docompress_file(filename, uncomp)
1437 const char *filename;
1440 gzFile compressedfile;
1441 FILE *uncompressedfile;
1446 if (!make_compressed_name(filename, cfn))
1450 /* Open the input and output files */
1451 /* Note that gzopen takes "wb" as its mode, even on systems where
1452 fopen takes "r" and "w" */
1454 uncompressedfile = fopen(filename, RDBMODE);
1455 if (!uncompressedfile) {
1456 pline("Error in zlib docompress_file %s", filename);
1459 compressedfile = gzopen(cfn, "wb");
1460 if (compressedfile == NULL) {
1462 pline("zlib failed to allocate memory");
1464 panic("Error in docompress_file %d", errno);
1466 fclose(uncompressedfile);
1470 /* Copy from the uncompressed to the compressed file */
1473 len = fread(buf, 1, sizeof(buf), uncompressedfile);
1474 if (ferror(uncompressedfile)) {
1475 pline("Failure reading uncompressed file");
1476 pline("Can't compress %s.", filename);
1477 fclose(uncompressedfile);
1478 gzclose(compressedfile);
1483 break; /* End of file */
1485 len2 = gzwrite(compressedfile, buf, len);
1487 pline("Failure writing compressed file");
1488 pline("Can't compress %s.", filename);
1489 fclose(uncompressedfile);
1490 gzclose(compressedfile);
1496 fclose(uncompressedfile);
1497 gzclose(compressedfile);
1499 /* Delete the file left behind */
1501 (void) unlink(filename);
1503 } else { /* uncomp */
1505 /* Open the input and output files */
1506 /* Note that gzopen takes "rb" as its mode, even on systems where
1507 fopen takes "r" and "w" */
1509 compressedfile = gzopen(cfn, "rb");
1510 if (compressedfile == NULL) {
1512 pline("zlib failed to allocate memory");
1513 } else if (errno != ENOENT) {
1514 panic("Error in zlib docompress_file %s, %d", filename,
1519 uncompressedfile = fopen(filename, WRBMODE);
1520 if (!uncompressedfile) {
1521 pline("Error in zlib docompress file uncompress %s", filename);
1522 gzclose(compressedfile);
1526 /* Copy from the compressed to the uncompressed file */
1529 len = gzread(compressedfile, buf, sizeof(buf));
1530 if (len == (unsigned) -1) {
1531 pline("Failure reading compressed file");
1532 pline("Can't uncompress %s.", filename);
1533 fclose(uncompressedfile);
1534 gzclose(compressedfile);
1535 (void) unlink(filename);
1539 break; /* End of file */
1541 fwrite(buf, 1, len, uncompressedfile);
1542 if (ferror(uncompressedfile)) {
1543 pline("Failure writing uncompressed file");
1544 pline("Can't uncompress %s.", filename);
1545 fclose(uncompressedfile);
1546 gzclose(compressedfile);
1547 (void) unlink(filename);
1552 fclose(uncompressedfile);
1553 gzclose(compressedfile);
1555 /* Delete the file left behind */
1559 #endif /* RLC 09 Mar 1999: End ZLIB patch */
1561 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1563 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1565 static int nesting = 0;
1567 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1568 static int lockfd; /* for lock_file() to pass to unlock_file() */
1571 struct flock sflock; /* for unlocking, same as above */
1574 #define HUP if (!program_state.done_hup)
1578 make_lockname(filename, lockname)
1579 const char *filename;
1582 #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \
1584 #ifdef NO_FILE_LINKS
1585 Strcpy(lockname, LOCKDIR);
1586 Strcat(lockname, "/");
1587 Strcat(lockname, filename);
1589 Strcpy(lockname, filename);
1593 char *semi_colon = rindex(lockname, ';');
1597 Strcat(lockname, ".lock;1");
1599 Strcat(lockname, "_lock");
1602 #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */
1603 #ifdef PRAGMA_UNUSED
1604 #pragma unused(filename)
1610 #endif /* !USE_FCNTL */
1614 lock_file(filename, whichprefix, retryct)
1615 const char *filename;
1619 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \
1620 && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
1621 #pragma unused(retryct)
1624 char locknambuf[BUFSZ];
1625 const char *lockname;
1630 impossible("TRIED TO NEST LOCKS");
1635 lockname = make_lockname(filename, locknambuf);
1636 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1637 lockname = fqname(lockname, LOCKPREFIX, 2);
1640 filename = fqname(filename, whichprefix, 0);
1642 lockfd = open(filename, O_RDWR);
1644 HUP raw_printf("Cannot open file %s. This is a program bug.",
1647 sflock.l_type = F_WRLCK;
1648 sflock.l_whence = SEEK_SET;
1653 #if defined(UNIX) || defined(VMS)
1655 while (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1657 #ifdef NO_FILE_LINKS
1658 while ((lockfd = open(lockname, O_RDWR | O_CREAT | O_EXCL, 0666)) == -1) {
1660 while (link(filename, lockname) == -1) {
1667 "Waiting for release of fcntl lock on %s. (%d retries left).",
1671 HUP(void) raw_print("I give up. Sorry.");
1672 HUP raw_printf("Some other process has an unnatural grip on %s.",
1678 register int errnosv = errno;
1680 switch (errnosv) { /* George Barbanis */
1684 "Waiting for access to %s. (%d retries left).", filename,
1686 #if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1691 HUP(void) raw_print("I give up. Sorry.");
1692 HUP raw_printf("Perhaps there is an old %s around?",
1700 HUP raw_printf("Can't find file %s to lock!", filename);
1704 HUP raw_printf("No write permission to lock %s!", filename);
1707 #ifdef VMS /* c__translate(vmsfiles.c) */
1709 /* could be misleading, but usually right */
1710 HUP raw_printf("Can't lock %s due to directory protection.",
1716 /* take a wild guess at the underlying cause */
1717 HUP perror(lockname);
1718 HUP raw_printf("Cannot lock %s.", filename);
1720 "(Perhaps you are running NetHack from inside the distribution package?).");
1724 HUP perror(lockname);
1725 HUP raw_printf("Cannot lock %s for unknown reason (%d).",
1730 #endif /* USE_FCNTL */
1732 #endif /* UNIX || VMS */
1734 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) \
1735 && !defined(USE_FCNTL)
1737 #define OPENFAILURE(fd) (!fd)
1740 #define OPENFAILURE(fd) (fd < 0)
1743 while (--retryct && OPENFAILURE(lockptr)) {
1744 #if defined(WIN32) && !defined(WIN_CE)
1745 lockptr = sopen(lockname, O_RDWR | O_CREAT, SH_DENYRW, S_IWRITE);
1747 (void) DeleteFile(lockname); /* in case dead process was here first */
1749 lockptr = Open(lockname, MODE_NEWFILE);
1751 lockptr = open(lockname, O_RDWR | O_CREAT | O_EXCL, S_IWRITE);
1754 if (OPENFAILURE(lockptr)) {
1755 raw_printf("Waiting for access to %s. (%d retries left).",
1761 raw_printf("I give up. Sorry.");
1765 #endif /* AMIGA || WIN32 || MSDOS */
1769 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1773 #define unlink(foo) vms_unlink(foo)
1776 /* unlock file, which must be currently locked by lock_file */
1778 unlock_file(filename)
1779 const char *filename;
1782 char locknambuf[BUFSZ];
1783 const char *lockname;
1788 sflock.l_type = F_UNLCK;
1789 if (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1790 HUP raw_printf("Can't remove fcntl lock on %s.", filename);
1791 (void) close(lockfd);
1794 lockname = make_lockname(filename, locknambuf);
1795 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1796 lockname = fqname(lockname, LOCKPREFIX, 2);
1799 #if defined(UNIX) || defined(VMS)
1800 if (unlink(lockname) < 0)
1801 HUP raw_printf("Can't unlink %s.", lockname);
1802 #ifdef NO_FILE_LINKS
1803 (void) nhclose(lockfd);
1806 #endif /* UNIX || VMS */
1808 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1811 DeleteFile(lockname);
1813 #endif /* AMIGA || WIN32 || MSDOS */
1814 #endif /* USE_FCNTL */
1820 /* ---------- END FILE LOCKING HANDLING ----------- */
1822 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1824 const char *configfile =
1828 #if defined(MAC) || defined(__BEOS__)
1831 #if defined(MSDOS) || defined(WIN32)
1839 /* used for messaging */
1840 char lastconfigfile[BUFSZ];
1843 /* conflict with speed-dial under windows
1844 * for XXX.cnf file so support of NetHack.cnf
1845 * is for backward compatibility only.
1846 * Preferred name (and first tried) is now defaults.nh but
1847 * the game will try the old name if there
1848 * is no defaults.nh.
1850 const char *backward_compat_configfile = "nethack.cnf";
1854 #define fopenp fopen
1858 fopen_config_file(filename, src)
1859 const char *filename;
1863 #if defined(UNIX) || defined(VMS)
1864 char tmp_config[BUFSZ];
1868 /* If src != SET_IN_SYS, "filename" is an environment variable, so it
1869 * should hang around. If set, it is expected to be a full path name
1873 if ((src != SET_IN_SYS) && access(filename, 4) == -1) {
1874 /* 4 is R_OK on newer systems */
1875 /* nasty sneaky attempt to read file through
1876 * NetHack's setuid permissions -- this is the only
1877 * place a file name may be wholly under the player's
1878 * control (but SYSCF_FILE is not under the player's
1879 * control so it's OK).
1881 raw_printf("Access to %s denied (%d).", filename, errno);
1883 /* fall through to standard names */
1886 #ifdef PREFIXES_IN_USE
1887 if (src == SET_IN_SYS) {
1888 (void) strncpy(lastconfigfile, fqname(filename, SYSCONFPREFIX, 0),
1892 /* always honor sysconf first before anything else */
1893 (void) strncpy(lastconfigfile, filename, BUFSZ - 1);
1894 lastconfigfile[BUFSZ - 1] = '\0';
1895 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1897 if ((fp = fopenp(filename, "r")) != (FILE *) 0) {
1899 #if defined(UNIX) || defined(VMS)
1901 /* access() above probably caught most problems for UNIX */
1902 raw_printf("Couldn't open requested config file %s (%d).",
1905 /* fall through to standard names */
1910 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1911 if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
1913 if ((fp = fopenp(configfile, "r")) != (FILE *) 0)
1916 if ((fp = fopenp(fqname(backward_compat_configfile, CONFIGPREFIX, 0),
1917 "r")) != (FILE *) 0)
1919 if ((fp = fopenp(backward_compat_configfile, "r")) != (FILE *) 0)
1923 /* constructed full path names don't need fqname() */
1925 (void) strncpy(lastconfigfile, fqname("nethackini", CONFIGPREFIX, 0),
1927 lastconfigfile[BUFSZ - 1] = '\0';
1928 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1931 (void) strncpy(lastconfigfile, "sys$login:nethack.ini", BUFSZ - 1);
1932 lastconfigfile[BUFSZ - 1] = '\0';
1933 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1937 envp = nh_getenv("HOME");
1939 Strcpy(tmp_config, "NetHack.cnf");
1941 Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
1943 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1944 lastconfigfile[BUFSZ - 1] = '\0';
1945 if ((fp = fopenp(tmp_config, "r")) != (FILE *) 0)
1947 #else /* should be only UNIX left */
1948 envp = nh_getenv("HOME");
1950 Strcpy(tmp_config, ".nethackrc");
1952 Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
1954 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1955 lastconfigfile[BUFSZ - 1] = '\0';
1956 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1958 #if defined(__APPLE__)
1959 /* try an alternative */
1961 Sprintf(tmp_config, "%s/%s", envp,
1962 "Library/Preferences/NetHack Defaults");
1963 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1964 lastconfigfile[BUFSZ - 1] = '\0';
1965 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1967 Sprintf(tmp_config, "%s/%s", envp,
1968 "Library/Preferences/NetHack Defaults.txt");
1969 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1970 lastconfigfile[BUFSZ - 1] = '\0';
1971 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1975 if (errno != ENOENT) {
1976 const char *details;
1978 /* e.g., problems when setuid NetHack can't search home
1979 * directory restricted to user */
1981 #if defined(NHSTDC) && !defined(NOTSTDC)
1982 if ((details = strerror(errno)) == 0)
1985 raw_printf("Couldn't open default config file %s %s(%d).",
1986 lastconfigfile, details, errno);
1995 * Retrieve a list of integers from a file into a uchar array.
1997 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
1998 * location is unchanged. Callers must handle zeros if modlist is FALSE.
2001 get_uchars(fp, buf, bufp, list, modlist, size, name)
2002 FILE *fp; /* input file pointer */
2003 char *buf; /* read buffer, must be of size BUFSZ */
2004 char *bufp; /* current pointer */
2005 uchar *list; /* return list */
2006 boolean modlist; /* TRUE: list is being modified in place */
2007 int size; /* return list size */
2008 const char *name; /* name of option for error message */
2010 unsigned int num = 0;
2012 boolean havenum = FALSE;
2021 /* if modifying in place, don't insert zeros */
2022 if (num || !modlist)
2028 if (count == size || !*bufp)
2044 num = num * 10 + (*bufp - '0');
2049 if (fp == (FILE *) 0)
2052 if (!fgets(buf, BUFSZ, fp))
2054 } while (buf[0] == '#');
2060 raw_printf("Syntax error in %s", name);
2068 #ifdef NOCWD_ASSUMPTIONS
2070 adjust_prefix(bufp, prefixid)
2078 /* Backward compatibility, ignore trailing ;n */
2079 if ((ptr = index(bufp, ';')) != 0)
2081 if (strlen(bufp) > 0) {
2082 fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2);
2083 Strcpy(fqn_prefix[prefixid], bufp);
2084 append_slash(fqn_prefix[prefixid]);
2089 #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
2092 parse_config_line(fp, origbuf, src)
2097 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2098 static boolean ramdisk_specified = FALSE;
2103 char *bufp, *altp, buf[BUFSZ];
2104 uchar translate[MAXPCHARS];
2107 /* convert any tab to space, condense consecutive spaces into one,
2108 remove leading and trailing spaces (exception: if there is nothing
2109 but spaces, one of them will be kept even though it leads/trails) */
2110 mungspaces(strcpy(buf, origbuf));
2111 /* lines beginning with '#' are comments; accept empty lines too */
2112 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2115 /* find the '=' or ':' */
2116 bufp = index(buf, '=');
2117 altp = index(buf, ':');
2118 if (!bufp || (altp && altp < bufp))
2122 /* skip past '=', then space between it and value, if any */
2127 /* Go through possible variables */
2128 /* some of these (at least LEVELS and SAVE) should now set the
2129 * appropriate fqn_prefix[] rather than specialized variables
2131 if (match_varname(buf, "OPTIONS", 4)) {
2132 /* hack: un-mungspaces to allow consecutive spaces in
2133 general options until we verify that this is unnecessary;
2134 '=' or ':' is guaranteed to be present */
2135 bufp = index(origbuf, '=');
2136 altp = index(origbuf, ':');
2137 if (!bufp || (altp && altp < bufp))
2139 ++bufp; /* skip '='; parseoptions() handles spaces */
2141 parseoptions(bufp, TRUE, TRUE);
2142 if (plname[0]) /* If a name was given */
2143 plnamesuffix(); /* set the character class */
2144 } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
2145 add_autopickup_exception(bufp);
2146 } else if (match_varname(buf, "MSGTYPE", 7)) {
2147 (void) msgtype_parse_add(bufp);
2148 #ifdef NOCWD_ASSUMPTIONS
2149 } else if (match_varname(buf, "HACKDIR", 4)) {
2150 adjust_prefix(bufp, HACKPREFIX);
2151 } else if (match_varname(buf, "LEVELDIR", 4)
2152 || match_varname(buf, "LEVELS", 4)) {
2153 adjust_prefix(bufp, LEVELPREFIX);
2154 } else if (match_varname(buf, "SAVEDIR", 4)) {
2155 adjust_prefix(bufp, SAVEPREFIX);
2156 } else if (match_varname(buf, "BONESDIR", 5)) {
2157 adjust_prefix(bufp, BONESPREFIX);
2158 } else if (match_varname(buf, "DATADIR", 4)) {
2159 adjust_prefix(bufp, DATAPREFIX);
2160 } else if (match_varname(buf, "SCOREDIR", 4)) {
2161 adjust_prefix(bufp, SCOREPREFIX);
2162 } else if (match_varname(buf, "LOCKDIR", 4)) {
2163 adjust_prefix(bufp, LOCKPREFIX);
2164 } else if (match_varname(buf, "CONFIGDIR", 4)) {
2165 adjust_prefix(bufp, CONFIGPREFIX);
2166 } else if (match_varname(buf, "TROUBLEDIR", 4)) {
2167 adjust_prefix(bufp, TROUBLEPREFIX);
2168 #else /*NOCWD_ASSUMPTIONS*/
2170 } else if (match_varname(buf, "HACKDIR", 4)) {
2171 (void) strncpy(hackdir, bufp, PATHLEN - 1);
2173 } else if (match_varname(buf, "RAMDISK", 3)) {
2174 /* The following ifdef is NOT in the wrong
2175 * place. For now, we accept and silently
2178 if (strlen(bufp) >= PATHLEN)
2179 bufp[PATHLEN - 1] = '\0';
2180 Strcpy(levels, bufp);
2181 ramdisk = (strcmp(permbones, levels) != 0);
2182 ramdisk_specified = TRUE;
2185 } else if (match_varname(buf, "LEVELS", 4)) {
2186 if (strlen(bufp) >= PATHLEN)
2187 bufp[PATHLEN - 1] = '\0';
2188 Strcpy(permbones, bufp);
2189 if (!ramdisk_specified || !*levels)
2190 Strcpy(levels, bufp);
2191 ramdisk = (strcmp(permbones, levels) != 0);
2192 } else if (match_varname(buf, "SAVE", 4)) {
2194 extern int saveprompt;
2198 if ((ptr = index(bufp, ';')) != 0) {
2201 if (*(ptr + 1) == 'n' || *(ptr + 1) == 'N') {
2206 #if defined(SYSFLAGS) && defined(MFLOPPY)
2208 saveprompt = sysflags.asksavedisk;
2211 (void) strncpy(SAVEP, bufp, SAVESIZE - 1);
2212 append_slash(SAVEP);
2214 #endif /*NOCWD_ASSUMPTIONS*/
2216 } else if (match_varname(buf, "NAME", 4)) {
2217 (void) strncpy(plname, bufp, PL_NSIZ - 1);
2219 } else if (match_varname(buf, "ROLE", 4)
2220 || match_varname(buf, "CHARACTER", 4)) {
2221 if ((len = str2role(bufp)) >= 0)
2222 flags.initrole = len;
2223 } else if (match_varname(buf, "DOGNAME", 3)) {
2224 (void) strncpy(dogname, bufp, PL_PSIZ - 1);
2225 } else if (match_varname(buf, "CATNAME", 3)) {
2226 (void) strncpy(catname, bufp, PL_PSIZ - 1);
2229 } else if (src == SET_IN_SYS && match_varname(buf, "WIZARDS", 7)) {
2231 free((genericptr_t) sysopt.wizards);
2232 sysopt.wizards = dupstr(bufp);
2233 if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) {
2234 /* pre-format WIZARDS list now; it's displayed during a panic
2235 and since that panic might be due to running out of memory,
2236 we don't want to risk attempting to allocate any memory then */
2237 if (sysopt.fmtd_wizard_list)
2238 free((genericptr_t) sysopt.fmtd_wizard_list);
2239 sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards);
2241 } else if (src == SET_IN_SYS && match_varname(buf, "SHELLERS", 8)) {
2242 if (sysopt.shellers)
2243 free((genericptr_t) sysopt.shellers);
2244 sysopt.shellers = dupstr(bufp);
2245 } else if (src == SET_IN_SYS && match_varname(buf, "EXPLORERS", 7)) {
2246 if (sysopt.explorers)
2247 free((genericptr_t) sysopt.explorers);
2248 sysopt.explorers = dupstr(bufp);
2249 } else if (src == SET_IN_SYS && match_varname(buf, "DEBUGFILES", 5)) {
2250 /* if showdebug() has already been called (perhaps we've added
2251 some debugpline() calls to option processing) and has found
2252 a value for getenv("DEBUGFILES"), don't override that */
2253 if (sysopt.env_dbgfl <= 0) {
2254 if (sysopt.debugfiles)
2255 free((genericptr_t) sysopt.debugfiles);
2256 sysopt.debugfiles = dupstr(bufp);
2258 } else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
2260 free((genericptr_t) sysopt.support);
2261 sysopt.support = dupstr(bufp);
2262 } else if (src == SET_IN_SYS && match_varname(buf, "RECOVER", 7)) {
2264 free((genericptr_t) sysopt.recover);
2265 sysopt.recover = dupstr(bufp);
2266 } else if (src == SET_IN_SYS
2267 && match_varname(buf, "CHECK_SAVE_UID", 14)) {
2269 sysopt.check_save_uid = n;
2270 } else if (match_varname(buf, "SEDUCE", 6)) {
2271 n = !!atoi(bufp); /* XXX this could be tighter */
2272 /* allow anyone to turn it off, but only sysconf to turn it on*/
2273 if (src != SET_IN_SYS && n != 0) {
2274 raw_printf("Illegal value in SEDUCE");
2278 sysopt_seduce_set(sysopt.seduce);
2279 } else if (src == SET_IN_SYS && match_varname(buf, "MAXPLAYERS", 10)) {
2281 /* XXX to get more than 25, need to rewrite all lock code */
2282 if (n < 1 || n > 25) {
2283 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2286 sysopt.maxplayers = n;
2287 } else if (src == SET_IN_SYS && match_varname(buf, "PERSMAX", 7)) {
2290 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2294 } else if (src == SET_IN_SYS && match_varname(buf, "PERS_IS_UID", 11)) {
2296 if (n != 0 && n != 1) {
2297 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2300 sysopt.pers_is_uid = n;
2301 } else if (src == SET_IN_SYS && match_varname(buf, "ENTRYMAX", 8)) {
2304 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2307 sysopt.entrymax = n;
2308 } else if ((src == SET_IN_SYS) && match_varname(buf, "POINTSMIN", 9)) {
2311 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2314 sysopt.pointsmin = n;
2315 } else if (src == SET_IN_SYS
2316 && match_varname(buf, "MAX_STATUENAME_RANK", 10)) {
2320 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2323 sysopt.tt_oname_maxrank = n;
2325 /* SYSCF PANICTRACE options */
2326 } else if (src == SET_IN_SYS
2327 && match_varname(buf, "PANICTRACE_LIBC", 15)) {
2329 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2330 if (n < 0 || n > 2) {
2331 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2335 sysopt.panictrace_libc = n;
2336 } else if (src == SET_IN_SYS
2337 && match_varname(buf, "PANICTRACE_GDB", 14)) {
2339 #if defined(PANICTRACE)
2340 if (n < 0 || n > 2) {
2341 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2345 sysopt.panictrace_gdb = n;
2346 } else if (src == SET_IN_SYS && match_varname(buf, "GDBPATH", 7)) {
2347 #if defined(PANICTRACE) && !defined(VMS)
2348 if (!file_exists(bufp)) {
2349 raw_printf("File specified in GDBPATH does not exist.");
2354 free((genericptr_t) sysopt.gdbpath);
2355 sysopt.gdbpath = dupstr(bufp);
2356 } else if (src == SET_IN_SYS && match_varname(buf, "GREPPATH", 7)) {
2357 #if defined(PANICTRACE) && !defined(VMS)
2358 if (!file_exists(bufp)) {
2359 raw_printf("File specified in GREPPATH does not exist.");
2363 if (sysopt.greppath)
2364 free((genericptr_t) sysopt.greppath);
2365 sysopt.greppath = dupstr(bufp);
2368 } else if (match_varname(buf, "BOULDER", 3)) {
2369 (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1,
2371 } else if (match_varname(buf, "MENUCOLOR", 9)) {
2372 (void) add_menu_coloring(bufp);
2373 } else if (match_varname(buf, "WARNINGS", 5)) {
2374 (void) get_uchars(fp, buf, bufp, translate, FALSE, WARNCOUNT,
2376 assign_warnings(translate);
2377 } else if (match_varname(buf, "SYMBOLS", 4)) {
2378 char *op, symbuf[BUFSZ];
2382 /* check for line continuation (trailing '\') */
2384 morelines = (--op >= bufp && *op == '\\');
2387 /* strip trailing space now that '\' is gone */
2388 if (--op >= bufp && *op == ' ')
2396 if (!fgets(symbuf, BUFSZ, fp)) {
2402 } while (*bufp == '#');
2404 } while (morelines);
2405 switch_symbols(TRUE);
2406 } else if (match_varname(buf, "WIZKIT", 6)) {
2407 (void) strncpy(wizkit, bufp, WIZKIT_MAX - 1);
2409 } else if (match_varname(buf, "FONT", 4)) {
2412 if (t = strchr(buf + 5, ':')) {
2414 amii_set_text_font(buf + 5, atoi(t + 1));
2417 } else if (match_varname(buf, "PATH", 4)) {
2418 (void) strncpy(PATH, bufp, PATHLEN - 1);
2419 } else if (match_varname(buf, "DEPTH", 5)) {
2420 extern int amii_numcolors;
2421 int val = atoi(bufp);
2423 amii_numcolors = 1L << min(DEPTH, val);
2425 } else if (match_varname(buf, "DRIPENS", 7)) {
2429 for (i = 0, t = strtok(bufp, ",/"); t != (char *) 0;
2430 i < 20 && (t = strtok((char *) 0, ",/")), ++i) {
2431 sscanf(t, "%d", &val);
2432 sysflags.amii_dripens[i] = val;
2435 } else if (match_varname(buf, "SCREENMODE", 10)) {
2436 extern long amii_scrnmode;
2438 if (!stricmp(bufp, "req"))
2439 amii_scrnmode = 0xffffffff; /* Requester */
2440 else if (sscanf(bufp, "%x", &amii_scrnmode) != 1)
2442 } else if (match_varname(buf, "MSGPENS", 7)) {
2443 extern int amii_msgAPen, amii_msgBPen;
2444 char *t = strtok(bufp, ",/");
2447 sscanf(t, "%d", &amii_msgAPen);
2448 if (t = strtok((char *) 0, ",/"))
2449 sscanf(t, "%d", &amii_msgBPen);
2451 } else if (match_varname(buf, "TEXTPENS", 8)) {
2452 extern int amii_textAPen, amii_textBPen;
2453 char *t = strtok(bufp, ",/");
2456 sscanf(t, "%d", &amii_textAPen);
2457 if (t = strtok((char *) 0, ",/"))
2458 sscanf(t, "%d", &amii_textBPen);
2460 } else if (match_varname(buf, "MENUPENS", 8)) {
2461 extern int amii_menuAPen, amii_menuBPen;
2462 char *t = strtok(bufp, ",/");
2465 sscanf(t, "%d", &amii_menuAPen);
2466 if (t = strtok((char *) 0, ",/"))
2467 sscanf(t, "%d", &amii_menuBPen);
2469 } else if (match_varname(buf, "STATUSPENS", 10)) {
2470 extern int amii_statAPen, amii_statBPen;
2471 char *t = strtok(bufp, ",/");
2474 sscanf(t, "%d", &amii_statAPen);
2475 if (t = strtok((char *) 0, ",/"))
2476 sscanf(t, "%d", &amii_statBPen);
2478 } else if (match_varname(buf, "OTHERPENS", 9)) {
2479 extern int amii_otherAPen, amii_otherBPen;
2480 char *t = strtok(bufp, ",/");
2483 sscanf(t, "%d", &amii_otherAPen);
2484 if (t = strtok((char *) 0, ",/"))
2485 sscanf(t, "%d", &amii_otherBPen);
2487 } else if (match_varname(buf, "PENS", 4)) {
2488 extern unsigned short amii_init_map[AMII_MAXCOLORS];
2492 for (i = 0, t = strtok(bufp, ",/");
2493 i < AMII_MAXCOLORS && t != (char *) 0;
2494 t = strtok((char *) 0, ",/"), ++i) {
2495 sscanf(t, "%hx", &amii_init_map[i]);
2497 amii_setpens(amii_numcolors = i);
2498 } else if (match_varname(buf, "FGPENS", 6)) {
2499 extern int foreg[AMII_MAXCOLORS];
2503 for (i = 0, t = strtok(bufp, ",/");
2504 i < AMII_MAXCOLORS && t != (char *) 0;
2505 t = strtok((char *) 0, ",/"), ++i) {
2506 sscanf(t, "%d", &foreg[i]);
2508 } else if (match_varname(buf, "BGPENS", 6)) {
2509 extern int backg[AMII_MAXCOLORS];
2513 for (i = 0, t = strtok(bufp, ",/");
2514 i < AMII_MAXCOLORS && t != (char *) 0;
2515 t = strtok((char *) 0, ",/"), ++i) {
2516 sscanf(t, "%d", &backg[i]);
2520 } else if (match_varname(buf, "SOUNDDIR", 8)) {
2521 sounddir = dupstr(bufp);
2522 } else if (match_varname(buf, "SOUND", 5)) {
2523 add_sound_mapping(bufp);
2526 /* These should move to wc_ options */
2527 } else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
2528 extern char *qt_tilewidth;
2530 if (qt_tilewidth == NULL)
2531 qt_tilewidth = dupstr(bufp);
2532 } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
2533 extern char *qt_tileheight;
2535 if (qt_tileheight == NULL)
2536 qt_tileheight = dupstr(bufp);
2537 } else if (match_varname(buf, "QT_FONTSIZE", 11)) {
2538 extern char *qt_fontsize;
2540 if (qt_fontsize == NULL)
2541 qt_fontsize = dupstr(bufp);
2542 } else if (match_varname(buf, "QT_COMPACT", 10)) {
2543 extern int qt_compact_mode;
2545 qt_compact_mode = atoi(bufp);
2554 can_read_file(filename)
2555 const char *filename;
2557 return (boolean) (access(filename, 4) == 0);
2559 #endif /* USER_SOUNDS */
2562 read_config_file(filename, src)
2563 const char *filename;
2566 char buf[4 * BUFSZ], *p;
2568 boolean rv = TRUE; /* assume successful parse */
2570 if (!(fp = fopen_config_file(filename, src)))
2573 /* begin detection of duplicate configfile options */
2574 set_duplicate_opt_detection(1);
2576 while (fgets(buf, sizeof buf, fp)) {
2579 XXX Don't call read() in parse_config_line, read as callback or reassemble
2581 OR: Forbid multiline stuff for alternate config sources.
2584 if ((p = index(buf, '\n')) != 0)
2586 if (!parse_config_line(fp, buf, src)) {
2587 static const char badoptionline[] = "Bad option line: \"%s\"";
2589 /* truncate buffer if it's long; this is actually conservative */
2590 if (strlen(buf) > BUFSZ - sizeof badoptionline)
2591 buf[BUFSZ - sizeof badoptionline] = '\0';
2593 raw_printf(badoptionline, buf);
2600 /* turn off detection of duplicate configfile options */
2601 set_duplicate_opt_detection(0);
2609 #if defined(VMS) || defined(UNIX)
2610 char tmp_wizkit[BUFSZ];
2614 envp = nh_getenv("WIZKIT");
2616 (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
2621 if (access(wizkit, 4) == -1) {
2622 /* 4 is R_OK on newer systems */
2623 /* nasty sneaky attempt to read file through
2624 * NetHack's setuid permissions -- this is a
2625 * place a file name may be wholly under the player's
2628 raw_printf("Access to %s denied (%d).", wizkit, errno);
2630 /* fall through to standard names */
2633 if ((fp = fopenp(wizkit, "r")) != (FILE *) 0) {
2635 #if defined(UNIX) || defined(VMS)
2637 /* access() above probably caught most problems for UNIX */
2638 raw_printf("Couldn't open requested config file %s (%d).", wizkit,
2644 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2645 if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
2649 envp = nh_getenv("HOME");
2651 Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
2653 Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
2654 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2656 #else /* should be only UNIX left */
2657 envp = nh_getenv("HOME");
2659 Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
2661 Strcpy(tmp_wizkit, wizkit);
2662 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2664 else if (errno != ENOENT) {
2665 /* e.g., problems when setuid NetHack can't search home
2666 * directory restricted to user */
2667 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit,
2676 /* add to hero's inventory if there's room, otherwise put item on floor */
2681 if (!obj || obj == &zeroobj)
2684 /* subset of starting inventory pre-ID */
2686 if (Role_if(PM_PRIEST))
2688 /* same criteria as lift_object()'s check for available inventory slot */
2689 if (obj->oclass != COIN_CLASS && inv_cnt(FALSE) >= 52
2690 && !merge_choice(invent, obj)) {
2691 /* inventory overflow; can't just place & stack object since
2692 hero isn't in position yet, so schedule for arrival later */
2693 add_to_migration(obj);
2694 obj->ox = 0; /* index of main dungeon */
2695 obj->oy = 1; /* starting level number */
2697 (long) (MIGR_WITH_HERO | MIGR_NOBREAK | MIGR_NOSCATTER);
2707 char *ep, buf[BUFSZ];
2709 boolean bad_items = FALSE, skip = FALSE;
2711 if (!wizard || !(fp = fopen_wizkit_file()))
2714 program_state.wizkit_wishing = 1;
2715 while (fgets(buf, (int) (sizeof buf), fp)) {
2716 ep = index(buf, '\n');
2717 if (skip) { /* in case previous line was too long */
2719 skip = FALSE; /* found newline; next line is normal */
2722 skip = TRUE; /* newline missing; discard next fgets */
2724 *ep = '\0'; /* remove newline */
2727 otmp = readobjnam(buf, (struct obj *) 0);
2729 if (otmp != &zeroobj)
2730 wizkit_addinv(otmp);
2732 /* .60 limits output line width to 79 chars */
2733 raw_printf("Bad wizkit item: \"%.60s\"", buf);
2739 program_state.wizkit_wishing = 0;
2746 extern struct symsetentry *symset_list; /* options.c */
2747 extern struct symparse loadsyms[]; /* drawing.c */
2748 extern const char *known_handling[]; /* drawing.c */
2749 extern const char *known_restrictions[]; /* drawing.c */
2750 static int symset_count = 0; /* for pick-list building only */
2751 static boolean chosen_symset_start = FALSE, chosen_symset_end = FALSE;
2759 fp = fopen_datafile(SYMBOLS, "r", HACKPREFIX);
2764 * Returns 1 if the chose symset was found and loaded.
2765 * 0 if it wasn't found in the sym file or other problem.
2768 read_sym_file(which_set)
2771 char buf[4 * BUFSZ];
2774 if (!(fp = fopen_sym_file()))
2778 chosen_symset_start = chosen_symset_end = FALSE;
2779 while (fgets(buf, 4 * BUFSZ, fp)) {
2780 if (!parse_sym_line(buf, which_set)) {
2781 raw_printf("Bad symbol line: \"%.50s\"", buf);
2786 if (!chosen_symset_end && !chosen_symset_start)
2787 return (symset[which_set].name == 0) ? 1 : 0;
2788 if (!chosen_symset_end) {
2789 raw_printf("Missing finish for symset \"%s\"",
2790 symset[which_set].name ? symset[which_set].name
2797 /* returns 0 on error */
2799 parse_sym_line(buf, which_set)
2804 struct symparse *symp = (struct symparse *) 0;
2805 char *bufp, *commentp, *altp;
2807 /* convert each instance of whitespace (tabs, consecutive spaces)
2808 into a single space; leading and trailing spaces are stripped */
2810 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2812 /* remove trailing comment, if any */
2813 if ((commentp = rindex(buf, '#')) != 0) {
2815 /* remove space preceding the stripped comment, if any;
2816 we know 'commentp > buf' because *buf=='#' was caught above */
2817 if (commentp[-1] == ' ')
2821 /* find the '=' or ':' */
2822 bufp = index(buf, '=');
2823 altp = index(buf, ':');
2824 if (!bufp || (altp && altp < bufp))
2827 if (strncmpi(buf, "finish", 6) == 0) {
2828 /* end current graphics set */
2829 if (chosen_symset_start)
2830 chosen_symset_end = TRUE;
2831 chosen_symset_start = FALSE;
2836 /* skip '=' and space which follows, if any */
2841 symp = match_sym(buf);
2845 if (!symset[which_set].name) {
2846 /* A null symset name indicates that we're just
2847 building a pick-list of possible symset
2848 values from the file, so only do that */
2849 if (symp->range == SYM_CONTROL) {
2850 struct symsetentry *tmpsp;
2852 switch (symp->idx) {
2855 (struct symsetentry *) alloc(sizeof (struct symsetentry));
2856 tmpsp->next = (struct symsetentry *) 0;
2858 symset_list = tmpsp;
2862 tmpsp->next = symset_list;
2863 symset_list = tmpsp;
2865 tmpsp->idx = symset_count;
2866 tmpsp->name = dupstr(bufp);
2867 tmpsp->desc = (char *) 0;
2869 /* initialize restriction bits */
2874 /* handler type identified */
2875 tmpsp = symset_list; /* most recent symset */
2876 tmpsp->handling = H_UNK;
2878 while (known_handling[i]) {
2879 if (!strcmpi(known_handling[i], bufp)) {
2880 tmpsp->handling = i;
2881 break; /* while loop */
2886 case 3: /* description:something */
2887 tmpsp = symset_list; /* most recent symset */
2888 if (tmpsp && !tmpsp->desc)
2889 tmpsp->desc = dupstr(bufp);
2892 /* restrictions: xxxx*/
2893 tmpsp = symset_list; /* most recent symset */
2894 for (i = 0; known_restrictions[i]; ++i) {
2895 if (!strcmpi(known_restrictions[i], bufp)) {
2904 break; /* while loop */
2913 if (symp->range == SYM_CONTROL) {
2914 switch (symp->idx) {
2916 /* start of symset */
2917 if (!strcmpi(bufp, symset[which_set].name)) {
2918 /* matches desired one */
2919 chosen_symset_start = TRUE;
2920 /* these init_*() functions clear symset fields too */
2921 if (which_set == ROGUESET)
2923 else if (which_set == PRIMARY)
2929 if (chosen_symset_start)
2930 chosen_symset_end = TRUE;
2931 chosen_symset_start = FALSE;
2934 /* handler type identified */
2935 if (chosen_symset_start)
2936 set_symhandling(bufp, which_set);
2938 /* case 3: (description) is ignored here */
2939 case 4: /* color:off */
2940 if (chosen_symset_start) {
2942 if (!strcmpi(bufp, "true") || !strcmpi(bufp, "yes")
2943 || !strcmpi(bufp, "on"))
2944 symset[which_set].nocolor = 0;
2945 else if (!strcmpi(bufp, "false")
2946 || !strcmpi(bufp, "no")
2947 || !strcmpi(bufp, "off"))
2948 symset[which_set].nocolor = 1;
2952 case 5: /* restrictions: xxxx*/
2953 if (chosen_symset_start) {
2956 while (known_restrictions[n]) {
2957 if (!strcmpi(known_restrictions[n], bufp)) {
2960 symset[which_set].primary = 1;
2963 symset[which_set].rogue = 1;
2966 break; /* while loop */
2973 } else { /* !SYM_CONTROL */
2974 val = sym_val(bufp);
2975 if (chosen_symset_start) {
2976 if (which_set == PRIMARY) {
2977 update_l_symset(symp, val);
2978 } else if (which_set == ROGUESET) {
2979 update_r_symset(symp, val);
2988 set_symhandling(handling, which_set)
2994 symset[which_set].handling = H_UNK;
2995 while (known_handling[i]) {
2996 if (!strcmpi(known_handling[i], handling)) {
2997 symset[which_set].handling = i;
3004 /* ---------- END CONFIG FILE HANDLING ----------- */
3006 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3009 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3011 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3014 /* verify that we can write to scoreboard file; if not, try to create one */
3017 check_recordfile(dir)
3018 const char *dir UNUSED_if_not_OS2_CODEVIEW;
3020 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3023 const char *fq_record;
3026 #if defined(UNIX) || defined(VMS)
3027 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3028 fd = open(fq_record, O_RDWR, 0);
3030 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3031 if (!file_is_stmlf(fd)) {
3033 "Warning: scoreboard file %s is not in stream_lf format",
3038 (void) nhclose(fd); /* RECORD is accessible */
3039 } else if ((fd = open(fq_record, O_CREAT | O_RDWR, FCMASK)) >= 0) {
3040 (void) nhclose(fd); /* RECORD newly created */
3041 #if defined(VMS) && !defined(SECURE)
3042 /* Re-protect RECORD with world:read+write+execute+delete access. */
3043 (void) chmod(fq_record, FCMASK | 007);
3044 #endif /* VMS && !SECURE */
3046 raw_printf("Warning: cannot write scoreboard file %s", fq_record);
3049 #endif /* !UNIX && !VMS */
3050 #if defined(MICRO) || defined(WIN32)
3053 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3054 /* how does this work when there isn't an explicit path or fopenp
3055 * for later access to the file via fopen_datafile? ? */
3056 (void) strncpy(tmp, dir, PATHLEN - 1);
3057 tmp[PATHLEN - 1] = '\0';
3058 if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
3060 Strcat(tmp, RECORD);
3064 Strcpy(tmp, RECORD);
3065 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3068 if ((fd = open(fq_record, O_RDWR)) < 0) {
3069 /* try to create empty record */
3070 #if defined(AZTEC_C) || defined(_DCC) \
3071 || (defined(__GNUC__) && defined(__AMIGA__))
3072 /* Aztec doesn't use the third argument */
3073 /* DICE doesn't like it */
3074 if ((fd = open(fq_record, O_CREAT | O_RDWR)) < 0) {
3076 if ((fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE))
3079 raw_printf("Warning: cannot write record %s", tmp);
3083 } else /* open succeeded */
3085 #else /* MICRO || WIN32*/
3088 /* Create the "record" file, if necessary */
3089 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3090 fd = macopen(fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
3095 #endif /* MICRO || WIN32*/
3098 /* ---------- END SCOREBOARD CREATION ----------- */
3100 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3104 paniclog(type, reason)
3105 const char *type; /* panic, impossible, trickery */
3106 const char *reason; /* explanation */
3112 if (!program_state.in_paniclog) {
3113 program_state.in_paniclog = 1;
3114 lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX);
3116 time_t now = getnow();
3118 char playmode = wizard ? 'D' : discover ? 'X' : '-';
3120 (void) fprintf(lfile, "%s %08ld %06ld %d %c: %s %s\n",
3121 version_string(buf), yyyymmdd(now), hhmmss(now),
3122 uid, playmode, type, reason);
3123 (void) fclose(lfile);
3125 program_state.in_paniclog = 0;
3127 #endif /* PANICLOG */
3131 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3135 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3140 int lev, savelev, hpid, pltmpsiz;
3142 struct version_info version_data;
3144 char savename[SAVESIZE], errbuf[BUFSZ];
3145 struct savefile_info sfi;
3146 char tmpplbuf[PL_NSIZ];
3148 for (lev = 0; lev < 256; lev++)
3151 /* level 0 file contains:
3152 * pid of creating process (ignored here)
3153 * level number for current level of save file
3154 * name of save file nethack would have created
3159 gfd = open_levelfile(0, errbuf);
3161 raw_printf("%s\n", errbuf);
3164 if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
3165 raw_printf("\n%s\n%s\n",
3166 "Checkpoint data incompletely written or subsequently clobbered.",
3167 "Recovery impossible.");
3168 (void) nhclose(gfd);
3171 if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
3172 != sizeof(savelev)) {
3174 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3176 (void) nhclose(gfd);
3179 if ((read(gfd, (genericptr_t) savename, sizeof savename)
3181 || (read(gfd, (genericptr_t) &version_data, sizeof version_data)
3182 != sizeof version_data)
3183 || (read(gfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi)
3184 || (read(gfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3185 != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ)
3186 || (read(gfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) {
3187 raw_printf("\nError reading %s -- can't recover.\n", lock);
3188 (void) nhclose(gfd);
3192 /* save file should contain:
3196 * current level (including pets)
3197 * (non-level-based) game state
3200 set_savefile_name(TRUE);
3201 sfd = create_savefile();
3203 raw_printf("\nCannot recover savefile %s.\n", SAVEF);
3204 (void) nhclose(gfd);
3208 lfd = open_levelfile(savelev, errbuf);
3210 raw_printf("\n%s\n", errbuf);
3211 (void) nhclose(gfd);
3212 (void) nhclose(sfd);
3217 if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
3218 != sizeof version_data) {
3219 raw_printf("\nError writing %s; recovery failed.", SAVEF);
3220 (void) nhclose(gfd);
3221 (void) nhclose(sfd);
3226 if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) {
3227 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3229 (void) nhclose(gfd);
3230 (void) nhclose(sfd);
3235 if (write(sfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3236 != sizeof pltmpsiz) {
3237 raw_printf("Error writing %s; recovery failed (player name size).\n",
3239 (void) nhclose(gfd);
3240 (void) nhclose(sfd);
3245 if (write(sfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz) {
3246 raw_printf("Error writing %s; recovery failed (player name).\n",
3248 (void) nhclose(gfd);
3249 (void) nhclose(sfd);
3254 if (!copy_bytes(lfd, sfd)) {
3255 (void) nhclose(lfd);
3256 (void) nhclose(sfd);
3260 (void) nhclose(lfd);
3261 processed[savelev] = 1;
3263 if (!copy_bytes(gfd, sfd)) {
3264 (void) nhclose(lfd);
3265 (void) nhclose(sfd);
3269 (void) nhclose(gfd);
3272 for (lev = 1; lev < 256; lev++) {
3273 /* level numbers are kept in xchars in save.c, so the
3274 * maximum level number (for the endlevel) must be < 256
3276 if (lev != savelev) {
3277 lfd = open_levelfile(lev, (char *) 0);
3279 /* any or all of these may not exist */
3281 write(sfd, (genericptr_t) &levc, sizeof(levc));
3282 if (!copy_bytes(lfd, sfd)) {
3283 (void) nhclose(lfd);
3284 (void) nhclose(sfd);
3288 (void) nhclose(lfd);
3293 (void) nhclose(sfd);
3295 #ifdef HOLD_LOCKFILE_OPEN
3299 * We have a successful savefile!
3300 * Only now do we erase the level files.
3302 for (lev = 0; lev < 256; lev++) {
3303 if (processed[lev]) {
3304 const char *fq_lock;
3305 set_levelfile_name(lock, lev);
3306 fq_lock = fqname(lock, LEVELPREFIX, 3);
3307 (void) unlink(fq_lock);
3314 copy_bytes(ifd, ofd)
3321 nfrom = read(ifd, buf, BUFSIZ);
3322 nto = write(ofd, buf, nfrom);
3325 } while (nfrom == BUFSIZ);
3329 /* ---------- END INTERNAL RECOVER ----------- */
3330 #endif /*SELF_RECOVER*/
3332 /* ---------- OTHER ----------- */
3342 * All we really care about is the end result - can we read the file?
3343 * So just check that directly.
3345 * Not tested on most of the old platforms (which don't attempt
3346 * to implement SYSCF).
3347 * Some ports don't like open()'s optional third argument;
3348 * VMS overrides open() usage with a macro which requires it.
3351 fd = open(SYSCF_FILE, O_RDONLY);
3353 fd = open(SYSCF_FILE, O_RDONLY, 0);
3360 raw_printf("Unable to open SYSCF_FILE.\n");
3364 #endif /* SYSCF_FILE */
3368 /* used by debugpline() to decide whether to issue a message
3369 * from a particular source file; caller passes __FILE__ and we check
3370 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3372 * pass FALSE to override wildcard matching; useful for files
3373 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3374 * output if DEBUG is defined and effectively block the use of a wildcard */
3376 debugcore(filename, wildcards)
3377 const char *filename;
3380 const char *debugfiles, *p;
3382 if (!filename || !*filename)
3383 return FALSE; /* sanity precaution */
3385 if (sysopt.env_dbgfl == 0) {
3386 /* check once for DEBUGFILES in the environment;
3387 if found, it supersedes the sysconf value
3388 [note: getenv() rather than nh_getenv() since a long value
3389 is valid and doesn't pose any sort of overflow risk here] */
3390 if ((p = getenv("DEBUGFILES")) != 0) {
3391 if (sysopt.debugfiles)
3392 free((genericptr_t) sysopt.debugfiles);
3393 sysopt.debugfiles = dupstr(p);
3394 sysopt.env_dbgfl = 1;
3396 sysopt.env_dbgfl = -1;
3399 debugfiles = sysopt.debugfiles;
3400 /* usual case: sysopt.debugfiles will be empty */
3401 if (!debugfiles || !*debugfiles)
3404 /* strip filename's path if present */
3406 if ((p = rindex(filename, '/')) != 0)
3410 filename = vms_basename(filename);
3411 /* vms_basename strips off 'type' suffix as well as path and version;
3412 we want to put suffix back (".c" assumed); since it always returns
3413 a pointer to a static buffer, we can safely modify its result */
3414 Strcat((char *) filename, ".c");
3418 * Wildcard match will only work if there's a single pattern (which
3419 * might be a single file name without any wildcarding) rather than
3420 * a space-separated list.
3421 * [to NOT do: We could step through the space-separated list and
3422 * attempt a wildcard match against each element, but that would be
3423 * overkill for the intended usage.]
3425 if (wildcards && pmatch(debugfiles, filename))
3428 /* check whether filename is an element of the list */
3429 if ((p = strstr(debugfiles, filename)) != 0) {
3430 int l = (int) strlen(filename);
3432 if ((p == debugfiles || p[-1] == ' ' || p[-1] == '/')
3433 && (p[l] == ' ' || p[l] == '\0'))
3441 /* ---------- BEGIN TRIBUTE ----------- */
3446 #define SECTIONSCOPE 1
3447 #define TITLESCOPE 2
3448 #define PASSAGESCOPE 3
3450 #define MAXPASSAGES SIZE(context.novel.pasg) /* 30 */
3452 static int FDECL(choose_passage, (int, unsigned));
3454 /* choose a random passage that hasn't been chosen yet; once all have
3455 been chosen, reset the tracking to make all passages available again */
3457 choose_passage(passagecnt, oid)
3458 int passagecnt; /* total of available passages, 1..MAXPASSAGES */
3459 unsigned oid; /* book.o_id, used to determine whether re-reading same book */
3465 if (passagecnt > MAXPASSAGES)
3466 passagecnt = MAXPASSAGES;
3468 /* if a different book or we've used up all the passages already,
3469 reset in order to have all 'passagecnt' passages available */
3470 if (oid != context.novel.id || context.novel.count == 0) {
3471 context.novel.id = oid;
3472 context.novel.count = passagecnt;
3473 for (idx = 0; idx < MAXPASSAGES; idx++)
3474 context.novel.pasg[idx] = (xchar) ((idx < passagecnt) ? idx + 1
3478 idx = rn2(context.novel.count);
3479 res = (int) context.novel.pasg[idx];
3480 /* move the last slot's passage index into the slot just used
3481 and reduce the number of passages available */
3482 context.novel.pasg[idx] = context.novel.pasg[--context.novel.count];
3486 /* Returns True if you were able to read something. */
3488 read_tribute(tribsection, tribtitle, tribpassage, nowin_buf, bufsz, oid)
3489 const char *tribsection, *tribtitle;
3490 int tribpassage, bufsz;
3492 unsigned oid; /* book identifier */
3496 char line[BUFSZ], lastline[BUFSZ];
3499 int linect = 0, passagecnt = 0, targetpassage = 0;
3500 const char *badtranslation = "an incomprehensible foreign translation";
3501 boolean matchedsection = FALSE, matchedtitle = FALSE;
3502 winid tribwin = WIN_ERR;
3503 boolean grasped = FALSE;
3504 boolean foundpassage = FALSE;
3506 /* check for mandatories */
3507 if (!tribsection || !tribtitle) {
3509 pline("It's %s of \"%s\"!", badtranslation, tribtitle);
3513 debugpline3("read_tribute %s, %s, %d.", tribsection, tribtitle,
3516 fp = dlb_fopen(TRIBUTEFILE, "r");
3518 /* this is actually an error - cannot open tribute file! */
3520 pline("You feel too overwhelmed to continue!");
3525 * Syntax (not case-sensitive):
3528 * In the books section:
3529 * %title booktitle (n)
3530 * where booktitle=book title without quotes
3531 * (n)= total number of passages present for this title
3533 * where k=sequential passage number
3535 * %e ends the passage/book/section
3536 * If in a passage, it marks the end of that passage.
3537 * If in a book, it marks the end of that book.
3538 * If in a section, it marks the end of that section.
3543 *line = *lastline = '\0';
3544 while (dlb_fgets(line, sizeof line, fp) != 0) {
3546 if ((endp = index(line, '\n')) != 0)
3550 if (!strncmpi(&line[1], "section ", sizeof("section ") - 1)) {
3551 char *st = &line[9]; /* 9 from "%section " */
3553 scope = SECTIONSCOPE;
3554 if (!strcmpi(st, tribsection))
3555 matchedsection = TRUE;
3557 matchedsection = FALSE;
3558 } else if (!strncmpi(&line[1], "title ", sizeof("title ") - 1)) {
3559 char *st = &line[7]; /* 7 from "%title " */
3562 if ((p1 = index(st, '(')) != 0) {
3564 (void) mungspaces(st);
3565 if ((p2 = index(p1, ')')) != 0) {
3567 passagecnt = atoi(p1);
3568 if (passagecnt > MAXPASSAGES)
3569 passagecnt = MAXPASSAGES;
3571 if (matchedsection && !strcmpi(st, tribtitle)) {
3572 matchedtitle = TRUE;
3573 targetpassage = !tribpassage
3574 ? choose_passage(passagecnt, oid)
3575 : (tribpassage <= passagecnt)
3578 matchedtitle = FALSE;
3582 } else if (!strncmpi(&line[1], "passage ",
3583 sizeof("passage ") - 1)) {
3585 char *st = &line[9]; /* 9 from "%passage " */
3587 while (*st == ' ' || *st == '\t')
3589 if (*st && digit(*st) && (strlen(st) < 3))
3590 passagenum = atoi(st);
3591 if (passagenum && (passagenum <= passagecnt)) {
3592 scope = PASSAGESCOPE;
3593 if (matchedtitle && (passagenum == targetpassage)) {
3595 tribwin = create_nhwindow(NHW_MENU);
3597 foundpassage = TRUE;
3600 } else if (!strncmpi(&line[1], "e ", sizeof("e ") - 1)) {
3601 if (matchedtitle && scope == PASSAGESCOPE
3602 && ((!nowin_buf && tribwin != WIN_ERR)
3603 || (nowin_buf && foundpassage)))
3605 if (scope == TITLESCOPE)
3606 matchedtitle = FALSE;
3607 if (scope == SECTIONSCOPE)
3608 matchedsection = FALSE;
3612 debugpline1("tribute file error: bad %% command, line %d.",
3617 /* comment only, next! */
3620 if (matchedtitle && scope == PASSAGESCOPE) {
3621 if (!nowin_buf && tribwin != WIN_ERR) {
3622 putstr(tribwin, 0, line);
3623 Strcpy(lastline, line);
3624 } else if (nowin_buf) {
3625 if ((int) strlen(line) < bufsz - 1)
3626 Strcpy(nowin_buf, line);
3633 (void) dlb_fclose(fp);
3634 if (!nowin_buf && tribwin != WIN_ERR) {
3635 if (matchedtitle && scope == PASSAGESCOPE) {
3636 display_nhwindow(tribwin, FALSE);
3637 /* put the final attribution line into message history,
3638 analogous to the summary line from long quest messages */
3639 if (index(lastline, '['))
3640 mungspaces(lastline); /* to remove leading spaces */
3641 else /* construct one if necessary */
3642 Sprintf(lastline, "[%s, by Terry Pratchett]", tribtitle);
3643 putmsghistory(lastline, FALSE);
3645 destroy_nhwindow(tribwin);
3650 pline("It seems to be %s of \"%s\"!", badtranslation, tribtitle);
3659 Death_quote(buf, bufsz)
3663 unsigned death_oid = 1; /* chance of oid #1 being a novel is negligible */
3665 return read_tribute("Death", "Death Quotes", 0, buf, bufsz, death_oid);
3668 /* ---------- END TRIBUTE ----------- */