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. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
14 #include "wintty.h" /* more() */
17 #if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) \
23 #ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */
39 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
41 #ifndef COMPRESS_EXTENSION
42 #define COMPRESS_EXTENSION ".gz"
46 #if defined(UNIX) && defined(QT_GRAPHICS)
47 #include <sys/types.h>
52 #if defined(UNIX) || defined(VMS) || !defined(NO_SIGNAL)
56 #if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
63 #ifndef O_BINARY /* used for micros, no-op for others */
67 #ifdef PREFIXES_IN_USE
69 static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
72 #if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
73 char bones[] = "bonesnn.xxx";
74 char lock[PL_NSIZ + 14] = "1lock"; /* long enough for uid+name+.99 */
77 char bones[FILENAME]; /* pathname of bones files */
78 char lock[FILENAME]; /* pathname of level files */
81 char bones[] = "bonesnn.xxx;1";
82 char lock[PL_NSIZ + 17] = "1lock"; /* long enough for _uid+name+.99;1 */
85 char bones[] = "bonesnn.xxx";
86 char lock[PL_NSIZ + 25]; /* long enough for username+-+name+.99 */
90 #if defined(UNIX) || defined(__BEOS__)
91 #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
94 #define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
97 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
99 #define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
104 #if !defined(SAVE_EXTENSION)
106 #define SAVE_EXTENSION ".sav"
109 #define SAVE_EXTENSION ".NetHack-saved-game"
113 char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */
115 char SAVEP[SAVESIZE]; /* holds path of directory for save file */
118 #ifdef HOLD_LOCKFILE_OPEN
119 struct level_ftrack {
121 int fd; /* file descriptor for level file */
122 int oflag; /* open flags */
123 boolean nethack_thinks_it_is_open; /* Does NetHack think it's open? */
128 #endif /*HOLD_LOCKFILE_OPEN*/
130 #define WIZKIT_MAX 128
131 static char wizkit[WIZKIT_MAX];
132 STATIC_DCL FILE *NDECL(fopen_wizkit_file);
133 STATIC_DCL void FDECL(wizkit_addinv, (struct obj *));
136 extern char PATH[]; /* see sys/amiga/amidos.c */
137 extern char bbs_id[];
140 #include <proto/dos.h>
143 #include <libraries/dos.h>
144 extern void FDECL(amii_set_text_font, (char *, int));
147 #if defined(WIN32) || defined(MSDOS)
150 #define Delay(a) msleep(a)
154 #define DeleteFile unlink
160 #define unlink macunlink
163 #if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) \
164 || defined(__MWERKS__)
165 #define PRAGMA_UNUSED
169 extern char *sounddir;
172 extern int n_dgns; /* from dungeon.c */
174 #if defined(UNIX) && defined(QT_GRAPHICS)
179 STATIC_PTR int FDECL(CFDECLSPEC strcmp_wrap, (const void *, const void *));
181 STATIC_DCL char *FDECL(set_bonesfile_name, (char *, d_level *));
182 STATIC_DCL char *NDECL(set_bonestemp_name);
184 STATIC_DCL void FDECL(redirect, (const char *, const char *, FILE *,
187 #if defined(COMPRESS) || defined(ZLIB_COMP)
188 STATIC_DCL void FDECL(docompress_file, (const char *, BOOLEAN_P));
190 #if defined(ZLIB_COMP)
191 STATIC_DCL boolean FDECL(make_compressed_name, (const char *, char *));
194 STATIC_DCL char *FDECL(make_lockname, (const char *, char *));
196 STATIC_DCL FILE *FDECL(fopen_config_file, (const char *, int));
197 STATIC_DCL int FDECL(get_uchars, (FILE *, char *, char *, uchar *, BOOLEAN_P,
199 int FDECL(parse_config_line, (FILE *, char *, int));
200 STATIC_DCL FILE *NDECL(fopen_sym_file);
201 STATIC_DCL void FDECL(set_symhandling, (char *, int));
202 #ifdef NOCWD_ASSUMPTIONS
203 STATIC_DCL void FDECL(adjust_prefix, (char *, int));
206 STATIC_DCL boolean FDECL(copy_bytes, (int, int));
208 #ifdef HOLD_LOCKFILE_OPEN
209 STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int));
216 * legal zero-terminated list of acceptable file name characters
217 * quotechar lead-in character used to quote illegal characters as
220 * callerbuf buffer to house result
221 * bufsz size of callerbuf
224 * The hex digits 0-9 and A-F are always part of the legal set due to
225 * their use in the encoding scheme, even if not explicitly included in
229 * The following call:
230 * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
231 * '%', "This is a % test!", buf, 512);
232 * results in this encoding:
233 * "This%20is%20a%20%25%20test%21"
236 fname_encode(legal, quotechar, s, callerbuf, bufsz)
244 static char hexdigits[] = "0123456789ABCDEF";
251 /* Do we have room for one more character or encoding? */
252 if ((bufsz - cnt) <= 4)
255 if (*sp == quotechar) {
256 (void) sprintf(op, "%c%02X", quotechar, *sp);
259 } else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) {
264 } else if (is_kanji1(s, sp - s)) {
271 (void) sprintf(op, "%c%02X", quotechar, *sp);
284 * quotechar lead-in character used to quote illegal characters as
287 * callerbuf buffer to house result
288 * bufsz size of callerbuf
291 fname_decode(quotechar, s, callerbuf, bufsz)
297 int k, calc, cnt = 0;
298 static char hexdigits[] = "0123456789ABCDEF";
306 /* Do we have room for one more character? */
307 if ((bufsz - cnt) <= 2)
309 if (*sp == quotechar) {
311 for (k = 0; k < 16; ++k)
312 if (*sp == hexdigits[k])
315 return callerbuf; /* impossible, so bail */
318 for (k = 0; k < 16; ++k)
319 if (*sp == hexdigits[k])
322 return callerbuf; /* impossible, so bail */
336 #ifdef PREFIXES_IN_USE
337 #define UNUSED_if_not_PREFIXES_IN_USE /*empty*/
339 #define UNUSED_if_not_PREFIXES_IN_USE UNUSED
344 fqname(basenam, whichprefix, buffnum)
346 int whichprefix UNUSED_if_not_PREFIXES_IN_USE;
347 int buffnum UNUSED_if_not_PREFIXES_IN_USE;
349 #ifndef PREFIXES_IN_USE
352 if (!basenam || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
354 if (!fqn_prefix[whichprefix])
356 if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
357 impossible("Invalid fqn_filename_buffer specified: %d", buffnum);
360 if (strlen(fqn_prefix[whichprefix]) + strlen(basenam)
361 >= FQN_MAX_FILENAME) {
362 impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
364 return basenam; /* XXX */
366 Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
367 return strcat(fqn_filename_buffer[buffnum], basenam);
372 validate_prefix_locations(reasonbuf)
373 char *reasonbuf; /* reasonbuf must be at least BUFSZ, supplied by caller */
375 #if defined(NOCWD_ASSUMPTIONS)
377 const char *filename;
378 int prefcnt, failcount = 0;
379 char panicbuf1[BUFSZ], panicbuf2[BUFSZ];
385 #if defined(NOCWD_ASSUMPTIONS)
386 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) {
387 /* don't test writing to configdir or datadir; they're readonly */
388 if (prefcnt == SYSCONFPREFIX || prefcnt == CONFIGPREFIX
389 || prefcnt == DATAPREFIX)
391 filename = fqname("validate", prefcnt, 3);
392 if ((fp = fopen(filename, "w"))) {
394 (void) unlink(filename);
398 Strcat(reasonbuf, ", ");
399 Strcat(reasonbuf, fqn_prefix_names[prefcnt]);
401 /* the paniclog entry gets the value of errno as well */
402 Sprintf(panicbuf1, "Invalid %s", fqn_prefix_names[prefcnt]);
403 #if defined(NHSTDC) && !defined(NOTSTDC)
404 if (!(details = strerror(errno)))
407 Sprintf(panicbuf2, "\"%s\", (%d) %s", fqn_prefix[prefcnt], errno,
409 paniclog(panicbuf1, panicbuf2);
420 /* fopen a file, with OS-dependent bells and whistles */
421 /* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
423 fopen_datafile(filename, mode, prefix)
424 const char *filename, *mode;
429 filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0);
430 fp = fopen(filename, mode);
434 /* ---------- BEGIN LEVEL FILE HANDLING ----------- */
437 /* Set names for bones[] and lock[] */
442 Strcpy(levels, permbones);
443 Strcpy(bones, permbones);
445 append_slash(permbones);
446 append_slash(levels);
448 strncat(levels, bbs_id, PATHLEN);
451 Strcat(bones, "bonesnn.*");
452 Strcpy(lock, levels);
454 Strcat(lock, alllevels);
460 /* Construct a file name for a level-type file, which is of the form
461 * something.level (with any old level stripped off).
462 * This assumes there is space on the end of 'file' to append
463 * a two digit number. This is true for 'level'
464 * but be careful if you use it for other things -dgk
467 set_levelfile_name(file, lev)
473 tf = rindex(file, '.');
476 Sprintf(tf, ".%d", lev);
484 create_levelfile(lev, errbuf)
493 set_levelfile_name(lock, lev);
494 fq_lock = fqname(lock, LEVELPREFIX, 0);
496 #if defined(MICRO) || defined(WIN32)
497 /* Use O_TRUNC to force the file to be shortened if it already
498 * exists and is currently longer.
500 #ifdef HOLD_LOCKFILE_OPEN
502 fd = open_levelfile_exclusively(
503 fq_lock, lev, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
506 fd = open(fq_lock, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
509 fd = maccreat(fq_lock, LEVL_TYPE);
511 fd = creat(fq_lock, FCMASK);
513 #endif /* MICRO || WIN32 */
516 level_info[lev].flags |= LFILE_EXISTS;
517 else if (errbuf) /* failure explanation */
518 Sprintf(errbuf, "Cannot create file \"%s\" for level %d (errno %d).",
525 open_levelfile(lev, errbuf)
534 set_levelfile_name(lock, lev);
535 fq_lock = fqname(lock, LEVELPREFIX, 0);
537 /* If not currently accessible, swap it in. */
538 if (level_info[lev].where != ACTIVE)
542 fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
544 #ifdef HOLD_LOCKFILE_OPEN
546 fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY);
549 fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
552 /* for failure, return an explanation that our caller can use;
553 settle for `lock' instead of `fq_lock' because the latter
554 might end up being too big for nethack's BUFSZ */
555 if (fd < 0 && errbuf)
557 Sprintf(errbuf, "Cannot open file \"%s\" for level %d (errno %d).",
560 Sprintf(errbuf, "
\92n
\89º%d
\8aK
\82Ì
\83t
\83@
\83C
\83\8b\"%s\"
\82ð
\8aJ
\82¯
\82È
\82¢(errno %d)
\81D",
568 delete_levelfile(lev)
572 * Level 0 might be created by port specific code that doesn't
573 * call create_levfile(), so always assume that it exists.
575 if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
576 set_levelfile_name(lock, lev);
577 #ifdef HOLD_LOCKFILE_OPEN
581 (void) unlink(fqname(lock, LEVELPREFIX, 0));
582 level_info[lev].flags &= ~LFILE_EXISTS;
589 #ifdef HANGUPHANDLING
590 if (program_state.preserve_locks)
593 #if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
594 eraseall(levels, alllevels);
596 eraseall(permbones, alllevels);
602 (void) signal(SIGINT, SIG_IGN);
604 #if defined(UNIX) || defined(VMS)
605 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
607 /* can't access maxledgerno() before dungeons are created -dlc */
608 for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
609 delete_levelfile(x); /* not all levels need be present */
611 #endif /* ?PC_LOCKING,&c */
614 #if defined(SELECTSAVED)
615 /* qsort comparison routine */
616 STATIC_OVL int CFDECLSPEC
621 #if defined(UNIX) && defined(QT_GRAPHICS)
622 return strncasecmp(*(char **) p, *(char **) q, 16);
624 return strncmpi(*(char **) p, *(char **) q, 16);
629 #ifdef HOLD_LOCKFILE_OPEN
631 open_levelfile_exclusively(name, lev, oflag)
640 if (lftrack.fd >= 0) {
641 /* check for compatible access */
642 if (lftrack.oflag == oflag) {
644 reslt = lseek(fd, 0L, SEEK_SET);
646 panic("open_levelfile_exclusively: lseek failed %d", errno);
647 lftrack.nethack_thinks_it_is_open = TRUE;
650 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
652 lftrack.oflag = oflag;
653 lftrack.nethack_thinks_it_is_open = TRUE;
656 fd = sopen(name, oflag, SH_DENYRW, FCMASK);
658 lftrack.oflag = oflag;
660 lftrack.nethack_thinks_it_is_open = TRUE;
670 lftrack.nethack_thinks_it_is_open = FALSE;
682 if (lftrack.fd == fd) {
683 really_close(); /* close it, but reopen it to hold it */
684 fd = open_levelfile(0, (char *) 0);
685 lftrack.nethack_thinks_it_is_open = FALSE;
700 /* ---------- END LEVEL FILE HANDLING ----------- */
702 /* ---------- BEGIN BONES FILE HANDLING ----------- */
704 /* set up "file" to be file name for retrieving bones, and return a
705 * bonesid to be read/written in the bones file.
708 set_bonesfile_name(file, lev)
715 Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
716 In_quest(lev) ? urole.filecode : "0");
718 if ((sptr = Is_special(lev)) != 0)
719 Sprintf(dptr, ".%c", sptr->boneid);
721 Sprintf(dptr, ".%d", lev->dlevel);
728 /* set up temporary file name for writing bones, to avoid another game's
729 * trying to read from an uncompleted bones file. we want an uncontentious
730 * name, so use one in the namespace reserved for this game's level files.
731 * (we are not reading or writing level files while writing bones files, so
732 * the same array may be used instead of copying.)
739 tf = rindex(lock, '.');
750 create_bonesfile(lev, bonesid, errbuf)
760 *bonesid = set_bonesfile_name(bones, lev);
761 file = set_bonestemp_name();
762 file = fqname(file, BONESPREFIX, 0);
764 #if defined(MICRO) || defined(WIN32)
765 /* Use O_TRUNC to force the file to be shortened if it already
766 * exists and is currently longer.
768 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, FCMASK);
771 fd = maccreat(file, BONE_TYPE);
773 fd = creat(file, FCMASK);
776 if (fd < 0 && errbuf) /* failure explanation */
777 Sprintf(errbuf, "Cannot create bones \"%s\", id %s (errno %d).", lock,
780 #if defined(VMS) && !defined(SECURE)
782 Re-protect bones file with world:read+write+execute+delete access.
783 umask() doesn't seem very reliable; also, vaxcrtl won't let us set
784 delete access without write access, which is what's really wanted.
785 Can't simply create it with the desired protection because creat
786 ANDs the mask with the user's default protection, which usually
787 denies some or all access to world.
789 (void) chmod(file, FCMASK | 007); /* allow other users full access */
790 #endif /* VMS && !SECURE */
796 /* remove partial bonesfile in process of creation */
800 const char *tempname;
802 tempname = set_bonestemp_name();
803 tempname = fqname(tempname, BONESPREFIX, 0);
804 (void) unlink(tempname);
808 /* move completed bones file to proper name */
810 commit_bonesfile(lev)
813 const char *fq_bones, *tempname;
816 (void) set_bonesfile_name(bones, lev);
817 fq_bones = fqname(bones, BONESPREFIX, 0);
818 tempname = set_bonestemp_name();
819 tempname = fqname(tempname, BONESPREFIX, 1);
821 #if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
822 /* old SYSVs don't have rename. Some SVR3's may, but since they
823 * also have link/unlink, it doesn't matter. :-)
825 (void) unlink(fq_bones);
826 ret = link(tempname, fq_bones);
827 ret += unlink(tempname);
829 ret = rename(tempname, fq_bones);
831 if (wizard && ret != 0)
832 pline("couldn't rename %s to %s.", tempname, fq_bones);
836 open_bonesfile(lev, bonesid)
840 const char *fq_bones;
843 *bonesid = set_bonesfile_name(bones, lev);
844 fq_bones = fqname(bones, BONESPREFIX, 0);
845 nh_uncompress(fq_bones); /* no effect if nonexistent */
847 fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
849 fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
855 delete_bonesfile(lev)
858 (void) set_bonesfile_name(bones, lev);
859 return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
862 /* assume we're compressing the recently read or created bonesfile, so the
863 * file name is already set properly */
867 nh_compress(fqname(bones, BONESPREFIX, 0));
870 /* ---------- END BONES FILE HANDLING ----------- */
872 /* ---------- BEGIN SAVE FILE HANDLING ----------- */
874 /* set savefile name in OS-dependent manner from pre-existing plname,
875 * avoiding troublesome characters */
877 set_savefile_name(regularize_it)
878 boolean regularize_it;
881 Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
883 regularize(SAVEF + 7);
887 Strcpy(SAVEF, SAVEP);
889 strncat(SAVEF, bbs_id, PATHLEN);
892 int i = strlen(SAVEP);
894 /* plname has to share space with SAVEP and ".sav" */
895 (void) strncat(SAVEF, plname, FILENAME - i - 4);
897 (void) strncat(SAVEF, plname, 8);
900 regularize(SAVEF + i);
902 Strcat(SAVEF, SAVE_EXTENSION);
906 static const char okchars[] =
907 "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
908 const char *legal = okchars;
909 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
911 /* Obtain the name of the logged on user and incorporate
912 * it into the name. */
913 Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
915 ++legal; /* skip '*' wildcard character */
916 (void) fname_encode(legal, '%', fnamebuf, encodedfnamebuf, BUFSZ);
917 Sprintf(SAVEF, "%s%s", encodedfnamebuf, SAVE_EXTENSION);
919 #else /* not VMS or MICRO or WIN32 */
920 Sprintf(SAVEF, "save/%d%s", (int) getuid(), plname);
922 regularize(SAVEF + 5); /* avoid . or / in name */
930 save_savefile_name(fd)
933 (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
938 /* change pre-existing savefile name to indicate an error savefile */
944 char *semi_colon = rindex(SAVEF, ';');
949 Strcat(SAVEF, ".e;1");
960 /* create save file, overwriting one if it already exists */
967 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
968 #if defined(MICRO) || defined(WIN32)
969 fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
972 fd = maccreat(fq_save, SAVE_TYPE);
974 fd = creat(fq_save, FCMASK);
976 #if defined(VMS) && !defined(SECURE)
978 Make sure the save file is owned by the current process. That's
979 the default for non-privileged users, but for priv'd users the
980 file will be owned by the directory's owner instead of the user.
983 (void) chown(fq_save, getuid(), getgid());
984 #define getuid() vms_getuid()
985 #endif /* VMS && !SECURE */
991 /* open savefile for reading */
998 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1000 fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
1002 fd = open(fq_save, O_RDONLY | O_BINARY, 0);
1007 /* delete savefile */
1011 (void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
1012 return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
1015 /* try to open up a save file and prepare to restore it */
1017 restore_saved_game()
1019 const char *fq_save;
1023 set_savefile_name(TRUE);
1025 if (!saveDiskPrompt(1))
1027 #endif /* MFLOPPY */
1028 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1030 nh_uncompress(fq_save);
1031 if ((fd = open_savefile()) < 0)
1034 if (validate(fd, fq_save) != 0) {
1035 (void) nhclose(fd), fd = -1;
1036 (void) delete_savefile();
1041 #if defined(SELECTSAVED)
1043 plname_from_file(filename)
1044 const char *filename;
1049 Strcpy(SAVEF, filename);
1050 #ifdef COMPRESS_EXTENSION
1051 SAVEF[strlen(SAVEF) - strlen(COMPRESS_EXTENSION)] = '\0';
1053 nh_uncompress(SAVEF);
1054 if ((fd = open_savefile()) >= 0) {
1055 if (validate(fd, filename) == 0) {
1056 char tplname[PL_NSIZ];
1057 get_plname_from_file(fd, tplname);
1058 result = dupstr(tplname);
1066 /* --------- obsolete - used to be ifndef STORE_PLNAME_IN_FILE ----*/
1067 #if defined(UNIX) && defined(QT_GRAPHICS)
1068 /* Name not stored in save file, so we have to extract it from
1069 the filename, which loses information
1070 (eg. "/", "_", and "." characters are lost. */
1073 char name[64]; /* more than PL_NSIZ */
1074 #ifdef COMPRESS_EXTENSION
1075 #define EXTSTR COMPRESS_EXTENSION
1080 if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) {
1082 /* "_" most likely means " ", which certainly looks nicer */
1083 for (k=0; name[k]; k++)
1084 if ( name[k] == '_' )
1086 return dupstr(name);
1088 #endif /* UNIX && QT_GRAPHICS */
1092 /* --------- end of obsolete code ----*/
1093 #endif /* 0 - WAS STORE_PLNAME_IN_FILE*/
1095 #endif /* defined(SELECTSAVED) */
1100 #if defined(SELECTSAVED)
1106 const char *fq_save;
1108 Strcpy(plname, "*");
1109 set_savefile_name(FALSE);
1110 #if defined(ZLIB_COMP)
1111 Strcat(SAVEF, COMPRESS_EXTENSION);
1113 fq_save = fqname(SAVEF, SAVEPREFIX, 0);
1116 foundfile = foundfile_buffer();
1117 if (findfirst((char *) fq_save)) {
1120 } while (findnext());
1123 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1124 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1125 if (findfirst((char *) fq_save)) {
1129 r = plname_from_file(foundfile);
1133 } while (findnext());
1138 #if defined(UNIX) && defined(QT_GRAPHICS)
1139 /* posixly correct version */
1140 int myuid = getuid();
1143 if ((dir = opendir(fqname("save", SAVEPREFIX, 0)))) {
1144 for (n = 0; readdir(dir); n++)
1150 if (!(dir = opendir(fqname("save", SAVEPREFIX, 0))))
1152 result = (char **) alloc((n + 1) * sizeof(char *)); /* at most */
1153 (void) memset((genericptr_t) result, 0, (n + 1) * sizeof(char *));
1154 for (i = 0, j = 0; i < n; i++) {
1156 char name[64]; /* more than PL_NSIZ */
1157 struct dirent *entry = readdir(dir);
1161 if (sscanf(entry->d_name, "%d%63s", &uid, name) == 2) {
1163 char filename[BUFSZ];
1166 Sprintf(filename, "save/%d%s", uid, name);
1167 r = plname_from_file(filename);
1178 Strcpy(plname, "*");
1179 set_savefile_name(FALSE);
1180 j = vms_get_saved_games(SAVEF, &result);
1185 qsort(result, j, sizeof (char *), strcmp_wrap);
1188 } else if (result) { /* could happen if save files are obsolete */
1189 free_saved_games(result);
1191 #endif /* SELECTSAVED */
1196 free_saved_games(saved)
1203 free((genericptr_t) saved[i++]);
1204 free((genericptr_t) saved);
1208 /* ---------- END SAVE FILE HANDLING ----------- */
1210 /* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
1215 redirect(filename, mode, stream, uncomp)
1216 const char *filename, *mode;
1220 if (freopen(filename, mode, stream) == (FILE *) 0) {
1221 (void) fprintf(stderr, "freopen of %s for %scompress failed\n",
1222 filename, uncomp ? "un" : "");
1223 terminate(EXIT_FAILURE);
1228 * using system() is simpler, but opens up security holes and causes
1229 * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
1230 * setuid is renounced by /bin/sh, so the files cannot be accessed.
1232 * cf. child() in unixunix.c.
1235 docompress_file(filename, uncomp)
1236 const char *filename;
1241 const char *args[10];
1242 #ifdef COMPRESS_OPTIONS
1248 boolean istty = !strncmpi(windowprocs.name, "tty", 3);
1251 Strcpy(cfn, filename);
1252 #ifdef COMPRESS_EXTENSION
1253 Strcat(cfn, COMPRESS_EXTENSION);
1255 /* when compressing, we know the file exists */
1257 if ((cf = fopen(cfn, RDBMODE)) == (FILE *) 0)
1264 args[++i] = "-d"; /* uncompress */
1265 #ifdef COMPRESS_OPTIONS
1267 /* we can't guarantee there's only one additional option, sigh */
1269 boolean inword = FALSE;
1271 Strcpy(opts, COMPRESS_OPTIONS);
1274 if ((*opt == ' ') || (*opt == '\t')) {
1279 } else if (!inword) {
1287 args[++i] = (char *) 0;
1290 /* If we don't do this and we are right after a y/n question *and*
1291 * there is an error message from the compression, the 'y' or 'n' can
1292 * end up being displayed after the error message.
1298 if (f == 0) { /* child */
1300 /* any error messages from the compression must come out after
1301 * the first line, because the more() to let the user read
1302 * them will have to clear the first line. This should be
1303 * invisible if there are no error messages.
1308 /* run compressor without privileges, in case other programs
1309 * have surprises along the line of gzip once taking filenames
1312 /* assume all compressors will compress stdin to stdout
1313 * without explicit filenames. this is true of at least
1314 * compress and gzip, those mentioned in config.h.
1317 redirect(cfn, RDBMODE, stdin, uncomp);
1318 redirect(filename, WRBMODE, stdout, uncomp);
1320 redirect(filename, RDBMODE, stdin, uncomp);
1321 redirect(cfn, WRBMODE, stdout, uncomp);
1323 (void) setgid(getgid());
1324 (void) setuid(getuid());
1325 (void) execv(args[0], (char *const *) args);
1327 (void) fprintf(stderr, "Exec to %scompress %s failed.\n",
1328 uncomp ? "un" : "", filename);
1329 terminate(EXIT_FAILURE);
1330 } else if (f == -1) {
1332 pline("Fork to %scompress %s failed.", uncomp ? "un" : "", filename);
1336 (void) signal(SIGINT, SIG_IGN);
1337 (void) signal(SIGQUIT, SIG_IGN);
1338 (void) wait((int *) &i);
1339 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
1341 (void) signal(SIGQUIT, SIG_DFL);
1343 /* I don't think we can really cope with external compression
1344 * without signals, so we'll declare that compress failed and
1345 * go on. (We could do a better job by forcing off external
1346 * compression if there are no signals, but we want this for
1347 * testing with FailSafeC
1352 /* (un)compress succeeded: remove file left behind */
1356 (void) unlink(filename);
1358 /* (un)compress failed; remove the new, bad file */
1360 raw_printf("Unable to uncompress %s", filename);
1361 (void) unlink(filename);
1363 /* no message needed for compress case; life will go on */
1367 /* Give them a chance to read any error messages from the
1368 * compression--these would go to stdout or stderr and would get
1369 * overwritten only in tty mode. It's still ugly, since the
1370 * messages are being written on top of the screen, but at least
1371 * the user can read them.
1373 if (istty && iflags.window_inited) {
1374 clear_nhwindow(WIN_MESSAGE);
1376 /* No way to know if this is feasible */
1382 #endif /* COMPRESS */
1384 #if defined(COMPRESS) || defined(ZLIB_COMP)
1385 #define UNUSED_if_not_COMPRESS /*empty*/
1387 #define UNUSED_if_not_COMPRESS UNUSED
1392 nh_compress(filename)
1393 const char *filename UNUSED_if_not_COMPRESS;
1395 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1396 #ifdef PRAGMA_UNUSED
1397 #pragma unused(filename)
1400 docompress_file(filename, FALSE);
1404 /* uncompress file if it exists */
1406 nh_uncompress(filename)
1407 const char *filename UNUSED_if_not_COMPRESS;
1409 #if !defined(COMPRESS) && !defined(ZLIB_COMP)
1410 #ifdef PRAGMA_UNUSED
1411 #pragma unused(filename)
1414 docompress_file(filename, TRUE);
1418 #ifdef ZLIB_COMP /* RLC 09 Mar 1999: Support internal ZLIB */
1420 make_compressed_name(filename, cfn)
1421 const char *filename;
1424 #ifndef SHORT_FILENAMES
1425 /* Assume free-form filename with no 8.3 restrictions */
1426 strcpy(cfn, filename);
1427 strcat(cfn, COMPRESS_EXTENSION);
1430 #ifdef SAVE_EXTENSION
1431 char *bp = (char *) 0;
1433 strcpy(cfn, filename);
1434 if ((bp = strstri(cfn, SAVE_EXTENSION))) {
1435 strsubst(bp, SAVE_EXTENSION, ".saz");
1438 /* find last occurrence of bon */
1440 while (bp-- > cfn) {
1441 if (strstri(bp, "bon")) {
1442 strsubst(bp, "bon", "boz");
1447 #endif /* SAVE_EXTENSION */
1449 #endif /* SHORT_FILENAMES */
1453 docompress_file(filename, uncomp)
1454 const char *filename;
1457 gzFile compressedfile;
1458 FILE *uncompressedfile;
1463 if (!make_compressed_name(filename, cfn))
1467 /* Open the input and output files */
1468 /* Note that gzopen takes "wb" as its mode, even on systems where
1469 fopen takes "r" and "w" */
1471 uncompressedfile = fopen(filename, RDBMODE);
1472 if (!uncompressedfile) {
1473 pline("Error in zlib docompress_file %s", filename);
1476 compressedfile = gzopen(cfn, "wb");
1477 if (compressedfile == NULL) {
1479 pline("zlib failed to allocate memory");
1481 panic("Error in docompress_file %d", errno);
1483 fclose(uncompressedfile);
1487 /* Copy from the uncompressed to the compressed file */
1490 len = fread(buf, 1, sizeof(buf), uncompressedfile);
1491 if (ferror(uncompressedfile)) {
1492 pline("Failure reading uncompressed file");
1493 pline("Can't compress %s.", filename);
1494 fclose(uncompressedfile);
1495 gzclose(compressedfile);
1500 break; /* End of file */
1502 len2 = gzwrite(compressedfile, buf, len);
1504 pline("Failure writing compressed file");
1505 pline("Can't compress %s.", filename);
1506 fclose(uncompressedfile);
1507 gzclose(compressedfile);
1513 fclose(uncompressedfile);
1514 gzclose(compressedfile);
1516 /* Delete the file left behind */
1518 (void) unlink(filename);
1520 } else { /* uncomp */
1522 /* Open the input and output files */
1523 /* Note that gzopen takes "rb" as its mode, even on systems where
1524 fopen takes "r" and "w" */
1526 compressedfile = gzopen(cfn, "rb");
1527 if (compressedfile == NULL) {
1529 pline("zlib failed to allocate memory");
1530 } else if (errno != ENOENT) {
1531 panic("Error in zlib docompress_file %s, %d", filename,
1536 uncompressedfile = fopen(filename, WRBMODE);
1537 if (!uncompressedfile) {
1538 pline("Error in zlib docompress file uncompress %s", filename);
1539 gzclose(compressedfile);
1543 /* Copy from the compressed to the uncompressed file */
1546 len = gzread(compressedfile, buf, sizeof(buf));
1547 if (len == (unsigned) -1) {
1548 pline("Failure reading compressed file");
1549 pline("Can't uncompress %s.", filename);
1550 fclose(uncompressedfile);
1551 gzclose(compressedfile);
1552 (void) unlink(filename);
1556 break; /* End of file */
1558 fwrite(buf, 1, len, uncompressedfile);
1559 if (ferror(uncompressedfile)) {
1560 pline("Failure writing uncompressed file");
1561 pline("Can't uncompress %s.", filename);
1562 fclose(uncompressedfile);
1563 gzclose(compressedfile);
1564 (void) unlink(filename);
1569 fclose(uncompressedfile);
1570 gzclose(compressedfile);
1572 /* Delete the file left behind */
1576 #endif /* RLC 09 Mar 1999: End ZLIB patch */
1578 /* ---------- END FILE COMPRESSION HANDLING ----------- */
1580 /* ---------- BEGIN FILE LOCKING HANDLING ----------- */
1582 static int nesting = 0;
1584 #if defined(NO_FILE_LINKS) || defined(USE_FCNTL) /* implies UNIX */
1585 static int lockfd; /* for lock_file() to pass to unlock_file() */
1588 struct flock sflock; /* for unlocking, same as above */
1591 #define HUP if (!program_state.done_hup)
1595 make_lockname(filename, lockname)
1596 const char *filename;
1599 #if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) \
1601 #ifdef NO_FILE_LINKS
1602 Strcpy(lockname, LOCKDIR);
1603 Strcat(lockname, "/");
1604 Strcat(lockname, filename);
1606 Strcpy(lockname, filename);
1610 char *semi_colon = rindex(lockname, ';');
1614 Strcat(lockname, ".lock;1");
1616 Strcat(lockname, "_lock");
1619 #else /* !(UNIX || VMS || AMIGA || WIN32 || MSDOS) */
1620 #ifdef PRAGMA_UNUSED
1621 #pragma unused(filename)
1627 #endif /* !USE_FCNTL */
1631 lock_file(filename, whichprefix, retryct)
1632 const char *filename;
1636 #if defined(PRAGMA_UNUSED) && !(defined(UNIX) || defined(VMS)) \
1637 && !(defined(AMIGA) || defined(WIN32) || defined(MSDOS))
1638 #pragma unused(retryct)
1641 char locknambuf[BUFSZ];
1642 const char *lockname;
1647 impossible("TRIED TO NEST LOCKS");
1652 lockname = make_lockname(filename, locknambuf);
1653 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1654 lockname = fqname(lockname, LOCKPREFIX, 2);
1657 filename = fqname(filename, whichprefix, 0);
1659 lockfd = open(filename, O_RDWR);
1661 HUP raw_printf("Cannot open file %s. This is a program bug.",
1664 sflock.l_type = F_WRLCK;
1665 sflock.l_whence = SEEK_SET;
1670 #if defined(UNIX) || defined(VMS)
1672 while (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1674 #ifdef NO_FILE_LINKS
1675 while ((lockfd = open(lockname, O_RDWR | O_CREAT | O_EXCL, 0666)) == -1) {
1677 while (link(filename, lockname) == -1) {
1684 "Waiting for release of fcntl lock on %s. (%d retries left).",
1688 HUP(void) raw_print("I give up. Sorry.");
1689 HUP raw_printf("Some other process has an unnatural grip on %s.",
1695 register int errnosv = errno;
1697 switch (errnosv) { /* George Barbanis */
1701 "Waiting for access to %s. (%d retries left).", filename,
1703 #if defined(SYSV) || defined(ULTRIX) || defined(VMS)
1708 HUP(void) raw_print("I give up. Sorry.");
1709 HUP raw_printf("Perhaps there is an old %s around?",
1717 HUP raw_printf("Can't find file %s to lock!", filename);
1721 HUP raw_printf("No write permission to lock %s!", filename);
1724 #ifdef VMS /* c__translate(vmsfiles.c) */
1726 /* could be misleading, but usually right */
1727 HUP raw_printf("Can't lock %s due to directory protection.",
1733 /* take a wild guess at the underlying cause */
1734 HUP perror(lockname);
1735 HUP raw_printf("Cannot lock %s.", filename);
1737 "(Perhaps you are running NetHack from inside the distribution package?).");
1741 HUP perror(lockname);
1742 HUP raw_printf("Cannot lock %s for unknown reason (%d).",
1747 #endif /* USE_FCNTL */
1749 #endif /* UNIX || VMS */
1751 #if (defined(AMIGA) || defined(WIN32) || defined(MSDOS)) \
1752 && !defined(USE_FCNTL)
1754 #define OPENFAILURE(fd) (!fd)
1757 #define OPENFAILURE(fd) (fd < 0)
1760 while (--retryct && OPENFAILURE(lockptr)) {
1761 #if defined(WIN32) && !defined(WIN_CE)
1762 lockptr = sopen(lockname, O_RDWR | O_CREAT, SH_DENYRW, S_IWRITE);
1764 (void) DeleteFile(lockname); /* in case dead process was here first */
1766 lockptr = Open(lockname, MODE_NEWFILE);
1768 lockptr = open(lockname, O_RDWR | O_CREAT | O_EXCL, S_IWRITE);
1771 if (OPENFAILURE(lockptr)) {
1772 raw_printf("Waiting for access to %s. (%d retries left).",
1778 raw_printf("I give up. Sorry.");
1782 #endif /* AMIGA || WIN32 || MSDOS */
1786 #ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
1790 #define unlink(foo) vms_unlink(foo)
1793 /* unlock file, which must be currently locked by lock_file */
1795 unlock_file(filename)
1796 const char *filename;
1799 char locknambuf[BUFSZ];
1800 const char *lockname;
1805 sflock.l_type = F_UNLCK;
1806 if (fcntl(lockfd, F_SETLK, &sflock) == -1) {
1807 HUP raw_printf("Can't remove fcntl lock on %s.", filename);
1808 (void) close(lockfd);
1811 lockname = make_lockname(filename, locknambuf);
1812 #ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
1813 lockname = fqname(lockname, LOCKPREFIX, 2);
1816 #if defined(UNIX) || defined(VMS)
1817 if (unlink(lockname) < 0)
1818 HUP raw_printf("Can't unlink %s.", lockname);
1819 #ifdef NO_FILE_LINKS
1820 (void) nhclose(lockfd);
1823 #endif /* UNIX || VMS */
1825 #if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
1828 DeleteFile(lockname);
1830 #endif /* AMIGA || WIN32 || MSDOS */
1831 #endif /* USE_FCNTL */
1837 /* ---------- END FILE LOCKING HANDLING ----------- */
1839 /* ---------- BEGIN CONFIG FILE HANDLING ----------- */
1841 const char *configfile =
1845 #if defined(MAC) || defined(__BEOS__)
1848 #if defined(MSDOS) || defined(WIN32)
1856 /* used for messaging */
1857 char lastconfigfile[BUFSZ];
1860 /* conflict with speed-dial under windows
1861 * for XXX.cnf file so support of NetHack.cnf
1862 * is for backward compatibility only.
1863 * Preferred name (and first tried) is now defaults.nh but
1864 * the game will try the old name if there
1865 * is no defaults.nh.
1867 const char *backward_compat_configfile = "nethack.cnf";
1871 #define fopenp fopen
1875 fopen_config_file(filename, src)
1876 const char *filename;
1880 #if defined(UNIX) || defined(VMS)
1881 char tmp_config[BUFSZ];
1885 /* If src != SET_IN_SYS, "filename" is an environment variable, so it
1886 * should hang around. If set, it is expected to be a full path name
1890 if ((src != SET_IN_SYS) && access(filename, 4) == -1) {
1891 /* 4 is R_OK on newer systems */
1892 /* nasty sneaky attempt to read file through
1893 * NetHack's setuid permissions -- this is the only
1894 * place a file name may be wholly under the player's
1895 * control (but SYSCF_FILE is not under the player's
1896 * control so it's OK).
1898 raw_printf("Access to %s denied (%d).", filename, errno);
1900 /* fall through to standard names */
1903 #ifdef PREFIXES_IN_USE
1904 if (src == SET_IN_SYS) {
1905 (void) strncpy(lastconfigfile, fqname(filename, SYSCONFPREFIX, 0),
1909 /* always honor sysconf first before anything else */
1910 (void) strncpy(lastconfigfile, filename, BUFSZ - 1);
1911 lastconfigfile[BUFSZ - 1] = '\0';
1912 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1914 if ((fp = fopenp(filename, "r")) != (FILE *) 0) {
1916 #if defined(UNIX) || defined(VMS)
1918 /* access() above probably caught most problems for UNIX */
1919 raw_printf("Couldn't open requested config file %s (%d).",
1922 /* fall through to standard names */
1927 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
1928 if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
1930 if ((fp = fopenp(configfile, "r")) != (FILE *) 0)
1933 if ((fp = fopenp(fqname(backward_compat_configfile, CONFIGPREFIX, 0),
1934 "r")) != (FILE *) 0)
1936 if ((fp = fopenp(backward_compat_configfile, "r")) != (FILE *) 0)
1940 /* constructed full path names don't need fqname() */
1942 (void) strncpy(lastconfigfile, fqname("nethackini", CONFIGPREFIX, 0),
1944 lastconfigfile[BUFSZ - 1] = '\0';
1945 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1948 (void) strncpy(lastconfigfile, "sys$login:nethack.ini", BUFSZ - 1);
1949 lastconfigfile[BUFSZ - 1] = '\0';
1950 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0) {
1954 envp = nh_getenv("HOME");
1956 Strcpy(tmp_config, "NetHack.cnf");
1958 Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
1960 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1961 lastconfigfile[BUFSZ - 1] = '\0';
1962 if ((fp = fopenp(tmp_config, "r")) != (FILE *) 0)
1964 #else /* should be only UNIX left */
1965 envp = nh_getenv("HOME");
1966 #if 1 /*JP*//*".jnethackrc"
\82ð
\97D
\90æ
\82µ
\82Ä
\93Ç
\82Ý
\8d\9e\82Ý*/
1968 Strcpy(tmp_config, ".jnethackrc");
1970 Sprintf(tmp_config, "%s/%s", envp, ".jnethackrc");
1972 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1973 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1977 Strcpy(tmp_config, ".nethackrc");
1979 Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
1981 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1982 lastconfigfile[BUFSZ - 1] = '\0';
1983 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1985 #if defined(__APPLE__)
1986 /* try an alternative */
1988 Sprintf(tmp_config, "%s/%s", envp,
1989 "Library/Preferences/NetHack Defaults");
1990 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1991 lastconfigfile[BUFSZ - 1] = '\0';
1992 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
1994 Sprintf(tmp_config, "%s/%s", envp,
1995 "Library/Preferences/NetHack Defaults.txt");
1996 (void) strncpy(lastconfigfile, tmp_config, BUFSZ - 1);
1997 lastconfigfile[BUFSZ - 1] = '\0';
1998 if ((fp = fopenp(lastconfigfile, "r")) != (FILE *) 0)
2002 if (errno != ENOENT) {
2003 const char *details;
2005 /* e.g., problems when setuid NetHack can't search home
2006 * directory restricted to user */
2008 #if defined(NHSTDC) && !defined(NOTSTDC)
2009 if ((details = strerror(errno)) == 0)
2012 raw_printf("Couldn't open default config file %s %s(%d).",
2013 lastconfigfile, details, errno);
2022 * Retrieve a list of integers from a file into a uchar array.
2024 * NOTE: zeros are inserted unless modlist is TRUE, in which case the list
2025 * location is unchanged. Callers must handle zeros if modlist is FALSE.
2028 get_uchars(fp, buf, bufp, list, modlist, size, name)
2029 FILE *fp; /* input file pointer */
2030 char *buf; /* read buffer, must be of size BUFSZ */
2031 char *bufp; /* current pointer */
2032 uchar *list; /* return list */
2033 boolean modlist; /* TRUE: list is being modified in place */
2034 int size; /* return list size */
2035 const char *name; /* name of option for error message */
2037 unsigned int num = 0;
2039 boolean havenum = FALSE;
2048 /* if modifying in place, don't insert zeros */
2049 if (num || !modlist)
2055 if (count == size || !*bufp)
2071 num = num * 10 + (*bufp - '0');
2076 if (fp == (FILE *) 0)
2079 if (!fgets(buf, BUFSZ, fp))
2081 } while (buf[0] == '#');
2087 raw_printf("Syntax error in %s", name);
2095 #ifdef NOCWD_ASSUMPTIONS
2097 adjust_prefix(bufp, prefixid)
2105 /* Backward compatibility, ignore trailing ;n */
2106 if ((ptr = index(bufp, ';')) != 0)
2108 if (strlen(bufp) > 0) {
2109 fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2);
2110 Strcpy(fqn_prefix[prefixid], bufp);
2111 append_slash(fqn_prefix[prefixid]);
2116 #define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
2119 parse_config_line(fp, origbuf, src)
2124 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
2125 static boolean ramdisk_specified = FALSE;
2130 char *bufp, *altp, buf[BUFSZ];
2131 uchar translate[MAXPCHARS];
2134 /* convert any tab to space, condense consecutive spaces into one,
2135 remove leading and trailing spaces (exception: if there is nothing
2136 but spaces, one of them will be kept even though it leads/trails) */
2137 mungspaces(strcpy(buf, origbuf));
2138 /* lines beginning with '#' are comments; accept empty lines too */
2139 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2142 /* find the '=' or ':' */
2143 bufp = index(buf, '=');
2144 altp = index(buf, ':');
2145 if (!bufp || (altp && altp < bufp))
2149 /* skip past '=', then space between it and value, if any */
2154 /* Go through possible variables */
2155 /* some of these (at least LEVELS and SAVE) should now set the
2156 * appropriate fqn_prefix[] rather than specialized variables
2158 if (match_varname(buf, "OPTIONS", 4)) {
2159 /* hack: un-mungspaces to allow consecutive spaces in
2160 general options until we verify that this is unnecessary;
2161 '=' or ':' is guaranteed to be present */
2162 bufp = index(origbuf, '=');
2163 altp = index(origbuf, ':');
2164 if (!bufp || (altp && altp < bufp))
2166 ++bufp; /* skip '='; parseoptions() handles spaces */
2168 parseoptions(bufp, TRUE, TRUE);
2169 if (plname[0]) /* If a name was given */
2170 plnamesuffix(); /* set the character class */
2171 } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
2172 add_autopickup_exception(bufp);
2173 } else if (match_varname(buf, "MSGTYPE", 7)) {
2174 (void) msgtype_parse_add(bufp);
2175 #ifdef NOCWD_ASSUMPTIONS
2176 } else if (match_varname(buf, "HACKDIR", 4)) {
2177 adjust_prefix(bufp, HACKPREFIX);
2178 } else if (match_varname(buf, "LEVELDIR", 4)
2179 || match_varname(buf, "LEVELS", 4)) {
2180 adjust_prefix(bufp, LEVELPREFIX);
2181 } else if (match_varname(buf, "SAVEDIR", 4)) {
2182 adjust_prefix(bufp, SAVEPREFIX);
2183 } else if (match_varname(buf, "BONESDIR", 5)) {
2184 adjust_prefix(bufp, BONESPREFIX);
2185 } else if (match_varname(buf, "DATADIR", 4)) {
2186 adjust_prefix(bufp, DATAPREFIX);
2187 } else if (match_varname(buf, "SCOREDIR", 4)) {
2188 adjust_prefix(bufp, SCOREPREFIX);
2189 } else if (match_varname(buf, "LOCKDIR", 4)) {
2190 adjust_prefix(bufp, LOCKPREFIX);
2191 } else if (match_varname(buf, "CONFIGDIR", 4)) {
2192 adjust_prefix(bufp, CONFIGPREFIX);
2193 } else if (match_varname(buf, "TROUBLEDIR", 4)) {
2194 adjust_prefix(bufp, TROUBLEPREFIX);
2195 #else /*NOCWD_ASSUMPTIONS*/
2197 } else if (match_varname(buf, "HACKDIR", 4)) {
2198 (void) strncpy(hackdir, bufp, PATHLEN - 1);
2200 } else if (match_varname(buf, "RAMDISK", 3)) {
2201 /* The following ifdef is NOT in the wrong
2202 * place. For now, we accept and silently
2205 if (strlen(bufp) >= PATHLEN)
2206 bufp[PATHLEN - 1] = '\0';
2207 Strcpy(levels, bufp);
2208 ramdisk = (strcmp(permbones, levels) != 0);
2209 ramdisk_specified = TRUE;
2212 } else if (match_varname(buf, "LEVELS", 4)) {
2213 if (strlen(bufp) >= PATHLEN)
2214 bufp[PATHLEN - 1] = '\0';
2215 Strcpy(permbones, bufp);
2216 if (!ramdisk_specified || !*levels)
2217 Strcpy(levels, bufp);
2218 ramdisk = (strcmp(permbones, levels) != 0);
2219 } else if (match_varname(buf, "SAVE", 4)) {
2221 extern int saveprompt;
2225 if ((ptr = index(bufp, ';')) != 0) {
2228 if (*(ptr + 1) == 'n' || *(ptr + 1) == 'N') {
2233 #if defined(SYSFLAGS) && defined(MFLOPPY)
2235 saveprompt = sysflags.asksavedisk;
2238 (void) strncpy(SAVEP, bufp, SAVESIZE - 1);
2239 append_slash(SAVEP);
2241 #endif /*NOCWD_ASSUMPTIONS*/
2243 } else if (match_varname(buf, "NAME", 4)) {
2244 (void) strncpy(plname, bufp, PL_NSIZ - 1);
2246 } else if (match_varname(buf, "ROLE", 4)
2247 || match_varname(buf, "CHARACTER", 4)) {
2248 if ((len = str2role(bufp)) >= 0)
2249 flags.initrole = len;
2250 } else if (match_varname(buf, "DOGNAME", 3)) {
2251 (void) strncpy(dogname, bufp, PL_PSIZ - 1);
2252 } else if (match_varname(buf, "CATNAME", 3)) {
2253 (void) strncpy(catname, bufp, PL_PSIZ - 1);
2256 } else if (src == SET_IN_SYS && match_varname(buf, "WIZARDS", 7)) {
2258 free((genericptr_t) sysopt.wizards);
2259 sysopt.wizards = dupstr(bufp);
2260 if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) {
2261 /* pre-format WIZARDS list now; it's displayed during a panic
2262 and since that panic might be due to running out of memory,
2263 we don't want to risk attempting to allocate any memory then */
2264 if (sysopt.fmtd_wizard_list)
2265 free((genericptr_t) sysopt.fmtd_wizard_list);
2266 sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards);
2268 } else if (src == SET_IN_SYS && match_varname(buf, "SHELLERS", 8)) {
2269 if (sysopt.shellers)
2270 free((genericptr_t) sysopt.shellers);
2271 sysopt.shellers = dupstr(bufp);
2272 } else if (src == SET_IN_SYS && match_varname(buf, "EXPLORERS", 7)) {
2273 if (sysopt.explorers)
2274 free((genericptr_t) sysopt.explorers);
2275 sysopt.explorers = dupstr(bufp);
2276 } else if (src == SET_IN_SYS && match_varname(buf, "DEBUGFILES", 5)) {
2277 /* if showdebug() has already been called (perhaps we've added
2278 some debugpline() calls to option processing) and has found
2279 a value for getenv("DEBUGFILES"), don't override that */
2280 if (sysopt.env_dbgfl <= 0) {
2281 if (sysopt.debugfiles)
2282 free((genericptr_t) sysopt.debugfiles);
2283 sysopt.debugfiles = dupstr(bufp);
2285 } else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
2287 free((genericptr_t) sysopt.support);
2288 sysopt.support = dupstr(bufp);
2289 } else if (src == SET_IN_SYS && match_varname(buf, "RECOVER", 7)) {
2291 free((genericptr_t) sysopt.recover);
2292 sysopt.recover = dupstr(bufp);
2293 } else if (src == SET_IN_SYS
2294 && match_varname(buf, "CHECK_SAVE_UID", 14)) {
2296 sysopt.check_save_uid = n;
2297 } else if (match_varname(buf, "SEDUCE", 6)) {
2298 n = !!atoi(bufp); /* XXX this could be tighter */
2299 /* allow anyone to turn it off, but only sysconf to turn it on*/
2300 if (src != SET_IN_SYS && n != 0) {
2301 raw_printf("Illegal value in SEDUCE");
2305 sysopt_seduce_set(sysopt.seduce);
2306 } else if (src == SET_IN_SYS && match_varname(buf, "MAXPLAYERS", 10)) {
2308 /* XXX to get more than 25, need to rewrite all lock code */
2309 if (n < 1 || n > 25) {
2310 raw_printf("Illegal value in MAXPLAYERS (maximum is 25).");
2313 sysopt.maxplayers = n;
2314 } else if (src == SET_IN_SYS && match_varname(buf, "PERSMAX", 7)) {
2317 raw_printf("Illegal value in PERSMAX (minimum is 1).");
2321 } else if (src == SET_IN_SYS && match_varname(buf, "PERS_IS_UID", 11)) {
2323 if (n != 0 && n != 1) {
2324 raw_printf("Illegal value in PERS_IS_UID (must be 0 or 1).");
2327 sysopt.pers_is_uid = n;
2328 } else if (src == SET_IN_SYS && match_varname(buf, "ENTRYMAX", 8)) {
2331 raw_printf("Illegal value in ENTRYMAX (minimum is 10).");
2334 sysopt.entrymax = n;
2335 } else if ((src == SET_IN_SYS) && match_varname(buf, "POINTSMIN", 9)) {
2338 raw_printf("Illegal value in POINTSMIN (minimum is 1).");
2341 sysopt.pointsmin = n;
2342 } else if (src == SET_IN_SYS
2343 && match_varname(buf, "MAX_STATUENAME_RANK", 10)) {
2347 "Illegal value in MAX_STATUENAME_RANK (minimum is 1).");
2350 sysopt.tt_oname_maxrank = n;
2352 /* SYSCF PANICTRACE options */
2353 } else if (src == SET_IN_SYS
2354 && match_varname(buf, "PANICTRACE_LIBC", 15)) {
2356 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
2357 if (n < 0 || n > 2) {
2358 raw_printf("Illegal value in PANICTRACE_LIBC (not 0,1,2).");
2362 sysopt.panictrace_libc = n;
2363 } else if (src == SET_IN_SYS
2364 && match_varname(buf, "PANICTRACE_GDB", 14)) {
2366 #if defined(PANICTRACE)
2367 if (n < 0 || n > 2) {
2368 raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
2372 sysopt.panictrace_gdb = n;
2373 } else if (src == SET_IN_SYS && match_varname(buf, "GDBPATH", 7)) {
2374 #if defined(PANICTRACE) && !defined(VMS)
2375 if (!file_exists(bufp)) {
2376 raw_printf("File specified in GDBPATH does not exist.");
2381 free((genericptr_t) sysopt.gdbpath);
2382 sysopt.gdbpath = dupstr(bufp);
2383 } else if (src == SET_IN_SYS && match_varname(buf, "GREPPATH", 7)) {
2384 #if defined(PANICTRACE) && !defined(VMS)
2385 if (!file_exists(bufp)) {
2386 raw_printf("File specified in GREPPATH does not exist.");
2390 if (sysopt.greppath)
2391 free((genericptr_t) sysopt.greppath);
2392 sysopt.greppath = dupstr(bufp);
2395 } else if (match_varname(buf, "BOULDER", 3)) {
2396 (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, 1,
2398 } else if (match_varname(buf, "MENUCOLOR", 9)) {
2399 (void) add_menu_coloring(bufp);
2400 } else if (match_varname(buf, "WARNINGS", 5)) {
2401 (void) get_uchars(fp, buf, bufp, translate, FALSE, WARNCOUNT,
2403 assign_warnings(translate);
2404 } else if (match_varname(buf, "SYMBOLS", 4)) {
2405 char *op, symbuf[BUFSZ];
2409 /* check for line continuation (trailing '\') */
2411 morelines = (--op >= bufp && *op == '\\');
2414 /* strip trailing space now that '\' is gone */
2415 if (--op >= bufp && *op == ' ')
2423 if (!fgets(symbuf, BUFSZ, fp)) {
2429 } while (*bufp == '#');
2431 } while (morelines);
2432 switch_symbols(TRUE);
2433 } else if (match_varname(buf, "WIZKIT", 6)) {
2434 (void) strncpy(wizkit, bufp, WIZKIT_MAX - 1);
2436 } else if (match_varname(buf, "FONT", 4)) {
2439 if (t = strchr(buf + 5, ':')) {
2441 amii_set_text_font(buf + 5, atoi(t + 1));
2444 } else if (match_varname(buf, "PATH", 4)) {
2445 (void) strncpy(PATH, bufp, PATHLEN - 1);
2446 } else if (match_varname(buf, "DEPTH", 5)) {
2447 extern int amii_numcolors;
2448 int val = atoi(bufp);
2450 amii_numcolors = 1L << min(DEPTH, val);
2452 } else if (match_varname(buf, "DRIPENS", 7)) {
2456 for (i = 0, t = strtok(bufp, ",/"); t != (char *) 0;
2457 i < 20 && (t = strtok((char *) 0, ",/")), ++i) {
2458 sscanf(t, "%d", &val);
2459 sysflags.amii_dripens[i] = val;
2462 } else if (match_varname(buf, "SCREENMODE", 10)) {
2463 extern long amii_scrnmode;
2465 if (!stricmp(bufp, "req"))
2466 amii_scrnmode = 0xffffffff; /* Requester */
2467 else if (sscanf(bufp, "%x", &amii_scrnmode) != 1)
2469 } else if (match_varname(buf, "MSGPENS", 7)) {
2470 extern int amii_msgAPen, amii_msgBPen;
2471 char *t = strtok(bufp, ",/");
2474 sscanf(t, "%d", &amii_msgAPen);
2475 if (t = strtok((char *) 0, ",/"))
2476 sscanf(t, "%d", &amii_msgBPen);
2478 } else if (match_varname(buf, "TEXTPENS", 8)) {
2479 extern int amii_textAPen, amii_textBPen;
2480 char *t = strtok(bufp, ",/");
2483 sscanf(t, "%d", &amii_textAPen);
2484 if (t = strtok((char *) 0, ",/"))
2485 sscanf(t, "%d", &amii_textBPen);
2487 } else if (match_varname(buf, "MENUPENS", 8)) {
2488 extern int amii_menuAPen, amii_menuBPen;
2489 char *t = strtok(bufp, ",/");
2492 sscanf(t, "%d", &amii_menuAPen);
2493 if (t = strtok((char *) 0, ",/"))
2494 sscanf(t, "%d", &amii_menuBPen);
2496 } else if (match_varname(buf, "STATUSPENS", 10)) {
2497 extern int amii_statAPen, amii_statBPen;
2498 char *t = strtok(bufp, ",/");
2501 sscanf(t, "%d", &amii_statAPen);
2502 if (t = strtok((char *) 0, ",/"))
2503 sscanf(t, "%d", &amii_statBPen);
2505 } else if (match_varname(buf, "OTHERPENS", 9)) {
2506 extern int amii_otherAPen, amii_otherBPen;
2507 char *t = strtok(bufp, ",/");
2510 sscanf(t, "%d", &amii_otherAPen);
2511 if (t = strtok((char *) 0, ",/"))
2512 sscanf(t, "%d", &amii_otherBPen);
2514 } else if (match_varname(buf, "PENS", 4)) {
2515 extern unsigned short amii_init_map[AMII_MAXCOLORS];
2519 for (i = 0, t = strtok(bufp, ",/");
2520 i < AMII_MAXCOLORS && t != (char *) 0;
2521 t = strtok((char *) 0, ",/"), ++i) {
2522 sscanf(t, "%hx", &amii_init_map[i]);
2524 amii_setpens(amii_numcolors = i);
2525 } else if (match_varname(buf, "FGPENS", 6)) {
2526 extern int foreg[AMII_MAXCOLORS];
2530 for (i = 0, t = strtok(bufp, ",/");
2531 i < AMII_MAXCOLORS && t != (char *) 0;
2532 t = strtok((char *) 0, ",/"), ++i) {
2533 sscanf(t, "%d", &foreg[i]);
2535 } else if (match_varname(buf, "BGPENS", 6)) {
2536 extern int backg[AMII_MAXCOLORS];
2540 for (i = 0, t = strtok(bufp, ",/");
2541 i < AMII_MAXCOLORS && t != (char *) 0;
2542 t = strtok((char *) 0, ",/"), ++i) {
2543 sscanf(t, "%d", &backg[i]);
2547 } else if (match_varname(buf, "SOUNDDIR", 8)) {
2548 sounddir = dupstr(bufp);
2549 } else if (match_varname(buf, "SOUND", 5)) {
2550 add_sound_mapping(bufp);
2553 /* These should move to wc_ options */
2554 } else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
2555 extern char *qt_tilewidth;
2557 if (qt_tilewidth == NULL)
2558 qt_tilewidth = dupstr(bufp);
2559 } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
2560 extern char *qt_tileheight;
2562 if (qt_tileheight == NULL)
2563 qt_tileheight = dupstr(bufp);
2564 } else if (match_varname(buf, "QT_FONTSIZE", 11)) {
2565 extern char *qt_fontsize;
2567 if (qt_fontsize == NULL)
2568 qt_fontsize = dupstr(bufp);
2569 } else if (match_varname(buf, "QT_COMPACT", 10)) {
2570 extern int qt_compact_mode;
2572 qt_compact_mode = atoi(bufp);
2581 can_read_file(filename)
2582 const char *filename;
2584 return (boolean) (access(filename, 4) == 0);
2586 #endif /* USER_SOUNDS */
2589 read_config_file(filename, src)
2590 const char *filename;
2593 char buf[4 * BUFSZ], *p;
2595 boolean rv = TRUE; /* assume successful parse */
2597 if (!(fp = fopen_config_file(filename, src)))
2600 /* begin detection of duplicate configfile options */
2601 set_duplicate_opt_detection(1);
2603 while (fgets(buf, sizeof buf, fp)) {
2606 XXX Don't call read() in parse_config_line, read as callback or reassemble
2608 OR: Forbid multiline stuff for alternate config sources.
2611 if ((p = index(buf, '\n')) != 0)
2613 if (!parse_config_line(fp, buf, src)) {
2614 static const char badoptionline[] = "Bad option line: \"%s\"";
2616 /* truncate buffer if it's long; this is actually conservative */
2617 if (strlen(buf) > BUFSZ - sizeof badoptionline)
2618 buf[BUFSZ - sizeof badoptionline] = '\0';
2620 raw_printf(badoptionline, buf);
2627 /* turn off detection of duplicate configfile options */
2628 set_duplicate_opt_detection(0);
2636 #if defined(VMS) || defined(UNIX)
2637 char tmp_wizkit[BUFSZ];
2641 envp = nh_getenv("WIZKIT");
2643 (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
2648 if (access(wizkit, 4) == -1) {
2649 /* 4 is R_OK on newer systems */
2650 /* nasty sneaky attempt to read file through
2651 * NetHack's setuid permissions -- this is a
2652 * place a file name may be wholly under the player's
2655 raw_printf("Access to %s denied (%d).", wizkit, errno);
2657 /* fall through to standard names */
2660 if ((fp = fopenp(wizkit, "r")) != (FILE *) 0) {
2662 #if defined(UNIX) || defined(VMS)
2664 /* access() above probably caught most problems for UNIX */
2665 raw_printf("Couldn't open requested config file %s (%d).", wizkit,
2671 #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
2672 if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r")) != (FILE *) 0)
2676 envp = nh_getenv("HOME");
2678 Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
2680 Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
2681 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2683 #else /* should be only UNIX left */
2684 envp = nh_getenv("HOME");
2686 Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
2688 Strcpy(tmp_wizkit, wizkit);
2689 if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *) 0)
2691 else if (errno != ENOENT) {
2692 /* e.g., problems when setuid NetHack can't search home
2693 * directory restricted to user */
2694 raw_printf("Couldn't open default wizkit file %s (%d).", tmp_wizkit,
2703 /* add to hero's inventory if there's room, otherwise put item on floor */
2708 if (!obj || obj == &zeroobj)
2711 /* subset of starting inventory pre-ID */
2713 if (Role_if(PM_PRIEST))
2715 /* same criteria as lift_object()'s check for available inventory slot */
2716 if (obj->oclass != COIN_CLASS && inv_cnt(FALSE) >= 52
2717 && !merge_choice(invent, obj)) {
2718 /* inventory overflow; can't just place & stack object since
2719 hero isn't in position yet, so schedule for arrival later */
2720 add_to_migration(obj);
2721 obj->ox = 0; /* index of main dungeon */
2722 obj->oy = 1; /* starting level number */
2724 (long) (MIGR_WITH_HERO | MIGR_NOBREAK | MIGR_NOSCATTER);
2734 char *ep, buf[BUFSZ];
2736 boolean bad_items = FALSE, skip = FALSE;
2738 if (!wizard || !(fp = fopen_wizkit_file()))
2741 program_state.wizkit_wishing = 1;
2742 while (fgets(buf, (int) (sizeof buf), fp)) {
2743 ep = index(buf, '\n');
2744 if (skip) { /* in case previous line was too long */
2746 skip = FALSE; /* found newline; next line is normal */
2749 skip = TRUE; /* newline missing; discard next fgets */
2751 *ep = '\0'; /* remove newline */
2754 otmp = readobjnam(buf, (struct obj *) 0);
2756 if (otmp != &zeroobj)
2757 wizkit_addinv(otmp);
2759 /* .60 limits output line width to 79 chars */
2760 raw_printf("Bad wizkit item: \"%.60s\"", buf);
2766 program_state.wizkit_wishing = 0;
2773 extern struct symsetentry *symset_list; /* options.c */
2774 extern struct symparse loadsyms[]; /* drawing.c */
2775 extern const char *known_handling[]; /* drawing.c */
2776 extern const char *known_restrictions[]; /* drawing.c */
2777 static int symset_count = 0; /* for pick-list building only */
2778 static boolean chosen_symset_start = FALSE, chosen_symset_end = FALSE;
2786 fp = fopen_datafile(SYMBOLS, "r", HACKPREFIX);
2791 * Returns 1 if the chose symset was found and loaded.
2792 * 0 if it wasn't found in the sym file or other problem.
2795 read_sym_file(which_set)
2798 char buf[4 * BUFSZ];
2801 if (!(fp = fopen_sym_file()))
2805 chosen_symset_start = chosen_symset_end = FALSE;
2806 while (fgets(buf, 4 * BUFSZ, fp)) {
2807 if (!parse_sym_line(buf, which_set)) {
2808 raw_printf("Bad symbol line: \"%.50s\"", buf);
2813 if (!chosen_symset_end && !chosen_symset_start)
2814 return (symset[which_set].name == 0) ? 1 : 0;
2815 if (!chosen_symset_end) {
2816 raw_printf("Missing finish for symset \"%s\"",
2817 symset[which_set].name ? symset[which_set].name
2824 /* returns 0 on error */
2826 parse_sym_line(buf, which_set)
2831 struct symparse *symp = (struct symparse *) 0;
2832 char *bufp, *commentp, *altp;
2834 /* convert each instance of whitespace (tabs, consecutive spaces)
2835 into a single space; leading and trailing spaces are stripped */
2837 if (!*buf || *buf == '#' || !strcmp(buf, " "))
2839 /* remove trailing comment, if any */
2840 if ((commentp = rindex(buf, '#')) != 0) {
2842 /* remove space preceding the stripped comment, if any;
2843 we know 'commentp > buf' because *buf=='#' was caught above */
2844 if (commentp[-1] == ' ')
2848 /* find the '=' or ':' */
2849 bufp = index(buf, '=');
2850 altp = index(buf, ':');
2851 if (!bufp || (altp && altp < bufp))
2854 if (strncmpi(buf, "finish", 6) == 0) {
2855 /* end current graphics set */
2856 if (chosen_symset_start)
2857 chosen_symset_end = TRUE;
2858 chosen_symset_start = FALSE;
2863 /* skip '=' and space which follows, if any */
2868 symp = match_sym(buf);
2872 if (!symset[which_set].name) {
2873 /* A null symset name indicates that we're just
2874 building a pick-list of possible symset
2875 values from the file, so only do that */
2876 if (symp->range == SYM_CONTROL) {
2877 struct symsetentry *tmpsp;
2879 switch (symp->idx) {
2882 (struct symsetentry *) alloc(sizeof (struct symsetentry));
2883 tmpsp->next = (struct symsetentry *) 0;
2885 symset_list = tmpsp;
2889 tmpsp->next = symset_list;
2890 symset_list = tmpsp;
2892 tmpsp->idx = symset_count;
2893 tmpsp->name = dupstr(bufp);
2894 tmpsp->desc = (char *) 0;
2896 /* initialize restriction bits */
2901 /* handler type identified */
2902 tmpsp = symset_list; /* most recent symset */
2903 tmpsp->handling = H_UNK;
2905 while (known_handling[i]) {
2906 if (!strcmpi(known_handling[i], bufp)) {
2907 tmpsp->handling = i;
2908 break; /* while loop */
2913 case 3: /* description:something */
2914 tmpsp = symset_list; /* most recent symset */
2915 if (tmpsp && !tmpsp->desc)
2916 tmpsp->desc = dupstr(bufp);
2919 /* restrictions: xxxx*/
2920 tmpsp = symset_list; /* most recent symset */
2921 for (i = 0; known_restrictions[i]; ++i) {
2922 if (!strcmpi(known_restrictions[i], bufp)) {
2931 break; /* while loop */
2940 if (symp->range == SYM_CONTROL) {
2941 switch (symp->idx) {
2943 /* start of symset */
2944 if (!strcmpi(bufp, symset[which_set].name)) {
2945 /* matches desired one */
2946 chosen_symset_start = TRUE;
2947 /* these init_*() functions clear symset fields too */
2948 if (which_set == ROGUESET)
2950 else if (which_set == PRIMARY)
2956 if (chosen_symset_start)
2957 chosen_symset_end = TRUE;
2958 chosen_symset_start = FALSE;
2961 /* handler type identified */
2962 if (chosen_symset_start)
2963 set_symhandling(bufp, which_set);
2965 /* case 3: (description) is ignored here */
2966 case 4: /* color:off */
2967 if (chosen_symset_start) {
2969 if (!strcmpi(bufp, "true") || !strcmpi(bufp, "yes")
2970 || !strcmpi(bufp, "on"))
2971 symset[which_set].nocolor = 0;
2972 else if (!strcmpi(bufp, "false")
2973 || !strcmpi(bufp, "no")
2974 || !strcmpi(bufp, "off"))
2975 symset[which_set].nocolor = 1;
2979 case 5: /* restrictions: xxxx*/
2980 if (chosen_symset_start) {
2983 while (known_restrictions[n]) {
2984 if (!strcmpi(known_restrictions[n], bufp)) {
2987 symset[which_set].primary = 1;
2990 symset[which_set].rogue = 1;
2993 break; /* while loop */
3000 } else { /* !SYM_CONTROL */
3001 val = sym_val(bufp);
3002 if (chosen_symset_start) {
3003 if (which_set == PRIMARY) {
3004 update_l_symset(symp, val);
3005 } else if (which_set == ROGUESET) {
3006 update_r_symset(symp, val);
3015 set_symhandling(handling, which_set)
3021 symset[which_set].handling = H_UNK;
3022 while (known_handling[i]) {
3023 if (!strcmpi(known_handling[i], handling)) {
3024 symset[which_set].handling = i;
3031 /* ---------- END CONFIG FILE HANDLING ----------- */
3033 /* ---------- BEGIN SCOREBOARD CREATION ----------- */
3036 #define UNUSED_if_not_OS2_CODEVIEW /*empty*/
3038 #define UNUSED_if_not_OS2_CODEVIEW UNUSED
3041 /* verify that we can write to scoreboard file; if not, try to create one */
3044 check_recordfile(dir)
3045 const char *dir UNUSED_if_not_OS2_CODEVIEW;
3047 #if defined(PRAGMA_UNUSED) && !defined(OS2_CODEVIEW)
3050 const char *fq_record;
3053 #if defined(UNIX) || defined(VMS)
3054 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3055 fd = open(fq_record, O_RDWR, 0);
3057 #ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
3058 if (!file_is_stmlf(fd)) {
3060 "Warning: scoreboard file %s is not in stream_lf format",
3065 (void) nhclose(fd); /* RECORD is accessible */
3066 } else if ((fd = open(fq_record, O_CREAT | O_RDWR, FCMASK)) >= 0) {
3067 (void) nhclose(fd); /* RECORD newly created */
3068 #if defined(VMS) && !defined(SECURE)
3069 /* Re-protect RECORD with world:read+write+execute+delete access. */
3070 (void) chmod(fq_record, FCMASK | 007);
3071 #endif /* VMS && !SECURE */
3073 raw_printf("Warning: cannot write scoreboard file %s", fq_record);
3076 #endif /* !UNIX && !VMS */
3077 #if defined(MICRO) || defined(WIN32)
3080 #ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
3081 /* how does this work when there isn't an explicit path or fopenp
3082 * for later access to the file via fopen_datafile? ? */
3083 (void) strncpy(tmp, dir, PATHLEN - 1);
3084 tmp[PATHLEN - 1] = '\0';
3085 if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
3087 Strcat(tmp, RECORD);
3091 Strcpy(tmp, RECORD);
3092 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3095 if ((fd = open(fq_record, O_RDWR)) < 0) {
3096 /* try to create empty record */
3097 #if defined(AZTEC_C) || defined(_DCC) \
3098 || (defined(__GNUC__) && defined(__AMIGA__))
3099 /* Aztec doesn't use the third argument */
3100 /* DICE doesn't like it */
3101 if ((fd = open(fq_record, O_CREAT | O_RDWR)) < 0) {
3103 if ((fd = open(fq_record, O_CREAT | O_RDWR, S_IREAD | S_IWRITE))
3106 raw_printf("Warning: cannot write record %s", tmp);
3110 } else /* open succeeded */
3112 #else /* MICRO || WIN32*/
3115 /* Create the "record" file, if necessary */
3116 fq_record = fqname(RECORD, SCOREPREFIX, 0);
3117 fd = macopen(fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
3122 #endif /* MICRO || WIN32*/
3125 /* ---------- END SCOREBOARD CREATION ----------- */
3127 /* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
3131 paniclog(type, reason)
3132 const char *type; /* panic, impossible, trickery */
3133 const char *reason; /* explanation */
3139 if (!program_state.in_paniclog) {
3140 program_state.in_paniclog = 1;
3141 lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX);
3143 time_t now = getnow();
3145 char playmode = wizard ? 'D' : discover ? 'X' : '-';
3147 (void) fprintf(lfile, "%s %08ld %06ld %d %c: %s %s\n",
3148 version_string(buf), yyyymmdd(now), hhmmss(now),
3149 uid, playmode, type, reason);
3150 (void) fclose(lfile);
3152 program_state.in_paniclog = 0;
3154 #endif /* PANICLOG */
3158 /* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
3162 /* ---------- BEGIN INTERNAL RECOVER ----------- */
3167 int lev, savelev, hpid, pltmpsiz;
3169 struct version_info version_data;
3171 char savename[SAVESIZE], errbuf[BUFSZ];
3172 struct savefile_info sfi;
3173 char tmpplbuf[PL_NSIZ];
3175 for (lev = 0; lev < 256; lev++)
3178 /* level 0 file contains:
3179 * pid of creating process (ignored here)
3180 * level number for current level of save file
3181 * name of save file nethack would have created
3186 gfd = open_levelfile(0, errbuf);
3188 raw_printf("%s\n", errbuf);
3191 if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
3192 raw_printf("\n%s\n%s\n",
3193 "Checkpoint data incompletely written or subsequently clobbered.",
3194 "Recovery impossible.");
3195 (void) nhclose(gfd);
3198 if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
3199 != sizeof(savelev)) {
3201 "\nCheckpointing was not in effect for %s -- recovery impossible.\n",
3203 (void) nhclose(gfd);
3206 if ((read(gfd, (genericptr_t) savename, sizeof savename)
3208 || (read(gfd, (genericptr_t) &version_data, sizeof version_data)
3209 != sizeof version_data)
3210 || (read(gfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi)
3211 || (read(gfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3212 != sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ)
3213 || (read(gfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz)) {
3214 raw_printf("\nError reading %s -- can't recover.\n", lock);
3215 (void) nhclose(gfd);
3219 /* save file should contain:
3223 * current level (including pets)
3224 * (non-level-based) game state
3227 set_savefile_name(TRUE);
3228 sfd = create_savefile();
3230 raw_printf("\nCannot recover savefile %s.\n", SAVEF);
3231 (void) nhclose(gfd);
3235 lfd = open_levelfile(savelev, errbuf);
3237 raw_printf("\n%s\n", errbuf);
3238 (void) nhclose(gfd);
3239 (void) nhclose(sfd);
3244 if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
3245 != sizeof version_data) {
3246 raw_printf("\nError writing %s; recovery failed.", SAVEF);
3247 (void) nhclose(gfd);
3248 (void) nhclose(sfd);
3253 if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) {
3254 raw_printf("\nError writing %s; recovery failed (savefile_info).\n",
3256 (void) nhclose(gfd);
3257 (void) nhclose(sfd);
3262 if (write(sfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
3263 != sizeof pltmpsiz) {
3264 raw_printf("Error writing %s; recovery failed (player name size).\n",
3266 (void) nhclose(gfd);
3267 (void) nhclose(sfd);
3272 if (write(sfd, (genericptr_t) &tmpplbuf, pltmpsiz) != pltmpsiz) {
3273 raw_printf("Error writing %s; recovery failed (player name).\n",
3275 (void) nhclose(gfd);
3276 (void) nhclose(sfd);
3281 if (!copy_bytes(lfd, sfd)) {
3282 (void) nhclose(lfd);
3283 (void) nhclose(sfd);
3287 (void) nhclose(lfd);
3288 processed[savelev] = 1;
3290 if (!copy_bytes(gfd, sfd)) {
3291 (void) nhclose(lfd);
3292 (void) nhclose(sfd);
3296 (void) nhclose(gfd);
3299 for (lev = 1; lev < 256; lev++) {
3300 /* level numbers are kept in xchars in save.c, so the
3301 * maximum level number (for the endlevel) must be < 256
3303 if (lev != savelev) {
3304 lfd = open_levelfile(lev, (char *) 0);
3306 /* any or all of these may not exist */
3308 write(sfd, (genericptr_t) &levc, sizeof(levc));
3309 if (!copy_bytes(lfd, sfd)) {
3310 (void) nhclose(lfd);
3311 (void) nhclose(sfd);
3315 (void) nhclose(lfd);
3320 (void) nhclose(sfd);
3322 #ifdef HOLD_LOCKFILE_OPEN
3326 * We have a successful savefile!
3327 * Only now do we erase the level files.
3329 for (lev = 0; lev < 256; lev++) {
3330 if (processed[lev]) {
3331 const char *fq_lock;
3332 set_levelfile_name(lock, lev);
3333 fq_lock = fqname(lock, LEVELPREFIX, 3);
3334 (void) unlink(fq_lock);
3341 copy_bytes(ifd, ofd)
3348 nfrom = read(ifd, buf, BUFSIZ);
3349 nto = write(ofd, buf, nfrom);
3352 } while (nfrom == BUFSIZ);
3356 /* ---------- END INTERNAL RECOVER ----------- */
3357 #endif /*SELF_RECOVER*/
3359 /* ---------- OTHER ----------- */
3369 * All we really care about is the end result - can we read the file?
3370 * So just check that directly.
3372 * Not tested on most of the old platforms (which don't attempt
3373 * to implement SYSCF).
3374 * Some ports don't like open()'s optional third argument;
3375 * VMS overrides open() usage with a macro which requires it.
3378 fd = open(SYSCF_FILE, O_RDONLY);
3380 fd = open(SYSCF_FILE, O_RDONLY, 0);
3387 raw_printf("Unable to open SYSCF_FILE.\n");
3391 #endif /* SYSCF_FILE */
3395 /* used by debugpline() to decide whether to issue a message
3396 * from a particular source file; caller passes __FILE__ and we check
3397 * whether it is in the source file list supplied by SYSCF's DEBUGFILES
3399 * pass FALSE to override wildcard matching; useful for files
3400 * like dungeon.c and questpgr.c, which generate a ridiculous amount of
3401 * output if DEBUG is defined and effectively block the use of a wildcard */
3403 debugcore(filename, wildcards)
3404 const char *filename;
3407 const char *debugfiles, *p;
3409 if (!filename || !*filename)
3410 return FALSE; /* sanity precaution */
3412 if (sysopt.env_dbgfl == 0) {
3413 /* check once for DEBUGFILES in the environment;
3414 if found, it supersedes the sysconf value
3415 [note: getenv() rather than nh_getenv() since a long value
3416 is valid and doesn't pose any sort of overflow risk here] */
3417 if ((p = getenv("DEBUGFILES")) != 0) {
3418 if (sysopt.debugfiles)
3419 free((genericptr_t) sysopt.debugfiles);
3420 sysopt.debugfiles = dupstr(p);
3421 sysopt.env_dbgfl = 1;
3423 sysopt.env_dbgfl = -1;
3426 debugfiles = sysopt.debugfiles;
3427 /* usual case: sysopt.debugfiles will be empty */
3428 if (!debugfiles || !*debugfiles)
3431 /* strip filename's path if present */
3433 if ((p = rindex(filename, '/')) != 0)
3437 filename = vms_basename(filename);
3438 /* vms_basename strips off 'type' suffix as well as path and version;
3439 we want to put suffix back (".c" assumed); since it always returns
3440 a pointer to a static buffer, we can safely modify its result */
3441 Strcat((char *) filename, ".c");
3445 * Wildcard match will only work if there's a single pattern (which
3446 * might be a single file name without any wildcarding) rather than
3447 * a space-separated list.
3448 * [to NOT do: We could step through the space-separated list and
3449 * attempt a wildcard match against each element, but that would be
3450 * overkill for the intended usage.]
3452 if (wildcards && pmatch(debugfiles, filename))
3455 /* check whether filename is an element of the list */
3456 if ((p = strstr(debugfiles, filename)) != 0) {
3457 int l = (int) strlen(filename);
3459 if ((p == debugfiles || p[-1] == ' ' || p[-1] == '/')
3460 && (p[l] == ' ' || p[l] == '\0'))
3468 /* ---------- BEGIN TRIBUTE ----------- */
3473 #define SECTIONSCOPE 1
3474 #define TITLESCOPE 2
3475 #define PASSAGESCOPE 3
3477 #define MAXPASSAGES SIZE(context.novel.pasg) /* 30 */
3479 static int FDECL(choose_passage, (int, unsigned));
3481 /* choose a random passage that hasn't been chosen yet; once all have
3482 been chosen, reset the tracking to make all passages available again */
3484 choose_passage(passagecnt, oid)
3485 int passagecnt; /* total of available passages, 1..MAXPASSAGES */
3486 unsigned oid; /* book.o_id, used to determine whether re-reading same book */
3492 if (passagecnt > MAXPASSAGES)
3493 passagecnt = MAXPASSAGES;
3495 /* if a different book or we've used up all the passages already,
3496 reset in order to have all 'passagecnt' passages available */
3497 if (oid != context.novel.id || context.novel.count == 0) {
3498 context.novel.id = oid;
3499 context.novel.count = passagecnt;
3500 for (idx = 0; idx < MAXPASSAGES; idx++)
3501 context.novel.pasg[idx] = (xchar) ((idx < passagecnt) ? idx + 1
3505 idx = rn2(context.novel.count);
3506 res = (int) context.novel.pasg[idx];
3507 /* move the last slot's passage index into the slot just used
3508 and reduce the number of passages available */
3509 context.novel.pasg[idx] = context.novel.pasg[--context.novel.count];
3513 /* Returns True if you were able to read something. */
3515 read_tribute(tribsection, tribtitle, tribpassage, nowin_buf, bufsz, oid)
3516 const char *tribsection, *tribtitle;
3517 int tribpassage, bufsz;
3519 unsigned oid; /* book identifier */
3523 char line[BUFSZ], lastline[BUFSZ];
3526 int linect = 0, passagecnt = 0, targetpassage = 0;
3527 const char *badtranslation = "an incomprehensible foreign translation";
3528 boolean matchedsection = FALSE, matchedtitle = FALSE;
3529 winid tribwin = WIN_ERR;
3530 boolean grasped = FALSE;
3531 boolean foundpassage = FALSE;
3533 /* check for mandatories */
3534 if (!tribsection || !tribtitle) {
3536 pline("It's %s of \"%s\"!", badtranslation, tribtitle);
3540 debugpline3("read_tribute %s, %s, %d.", tribsection, tribtitle,
3543 fp = dlb_fopen(TRIBUTEFILE, "r");
3545 /* this is actually an error - cannot open tribute file! */
3547 pline("You feel too overwhelmed to continue!");
3552 * Syntax (not case-sensitive):
3555 * In the books section:
3556 * %title booktitle (n)
3557 * where booktitle=book title without quotes
3558 * (n)= total number of passages present for this title
3560 * where k=sequential passage number
3562 * %e ends the passage/book/section
3563 * If in a passage, it marks the end of that passage.
3564 * If in a book, it marks the end of that book.
3565 * If in a section, it marks the end of that section.
3570 *line = *lastline = '\0';
3571 while (dlb_fgets(line, sizeof line, fp) != 0) {
3573 if ((endp = index(line, '\n')) != 0)
3577 if (!strncmpi(&line[1], "section ", sizeof("section ") - 1)) {
3578 char *st = &line[9]; /* 9 from "%section " */
3580 scope = SECTIONSCOPE;
3581 if (!strcmpi(st, tribsection))
3582 matchedsection = TRUE;
3584 matchedsection = FALSE;
3585 } else if (!strncmpi(&line[1], "title ", sizeof("title ") - 1)) {
3586 char *st = &line[7]; /* 7 from "%title " */
3589 if ((p1 = index(st, '(')) != 0) {
3591 (void) mungspaces(st);
3592 if ((p2 = index(p1, ')')) != 0) {
3594 passagecnt = atoi(p1);
3595 if (passagecnt > MAXPASSAGES)
3596 passagecnt = MAXPASSAGES;
3598 if (matchedsection && !strcmpi(st, tribtitle)) {
3599 matchedtitle = TRUE;
3600 targetpassage = !tribpassage
3601 ? choose_passage(passagecnt, oid)
3602 : (tribpassage <= passagecnt)
3605 matchedtitle = FALSE;
3609 } else if (!strncmpi(&line[1], "passage ",
3610 sizeof("passage ") - 1)) {
3612 char *st = &line[9]; /* 9 from "%passage " */
3614 while (*st == ' ' || *st == '\t')
3616 if (*st && digit(*st) && (strlen(st) < 3))
3617 passagenum = atoi(st);
3618 if (passagenum && (passagenum <= passagecnt)) {
3619 scope = PASSAGESCOPE;
3620 if (matchedtitle && (passagenum == targetpassage)) {
3622 tribwin = create_nhwindow(NHW_MENU);
3624 foundpassage = TRUE;
3627 } else if (!strncmpi(&line[1], "e ", sizeof("e ") - 1)) {
3628 if (matchedtitle && scope == PASSAGESCOPE
3629 && ((!nowin_buf && tribwin != WIN_ERR)
3630 || (nowin_buf && foundpassage)))
3632 if (scope == TITLESCOPE)
3633 matchedtitle = FALSE;
3634 if (scope == SECTIONSCOPE)
3635 matchedsection = FALSE;
3639 debugpline1("tribute file error: bad %% command, line %d.",
3644 /* comment only, next! */
3647 if (matchedtitle && scope == PASSAGESCOPE) {
3648 if (!nowin_buf && tribwin != WIN_ERR) {
3649 putstr(tribwin, 0, line);
3650 Strcpy(lastline, line);
3651 } else if (nowin_buf) {
3652 if ((int) strlen(line) < bufsz - 1)
3653 Strcpy(nowin_buf, line);
3660 (void) dlb_fclose(fp);
3661 if (!nowin_buf && tribwin != WIN_ERR) {
3662 if (matchedtitle && scope == PASSAGESCOPE) {
3663 display_nhwindow(tribwin, FALSE);
3664 /* put the final attribution line into message history,
3665 analogous to the summary line from long quest messages */
3666 if (index(lastline, '['))
3667 mungspaces(lastline); /* to remove leading spaces */
3668 else /* construct one if necessary */
3669 Sprintf(lastline, "[%s, by Terry Pratchett]", tribtitle);
3670 putmsghistory(lastline, FALSE);
3672 destroy_nhwindow(tribwin);
3677 pline("It seems to be %s of \"%s\"!", badtranslation, tribtitle);
3686 Death_quote(buf, bufsz)
3690 unsigned death_oid = 1; /* chance of oid #1 being a novel is negligible */
3692 return read_tribute("Death", "Death Quotes", 0, buf, bufsz, death_oid);
3695 /* ---------- END TRIBUTE ----------- */